lots, see release description
This commit is contained in:
sinaioutlander 2020-10-08 06:15:42 +11:00
parent b012e2305c
commit f1c3771c24
63 changed files with 2558 additions and 2031 deletions

View File

@ -12,16 +12,11 @@
<img src="https://img.shields.io/github/downloads/sinai-dev/Explorer/total.svg" /> <img src="https://img.shields.io/github/downloads/sinai-dev/Explorer/total.svg" />
</p> </p>
<p align="center">
<img src="https://raw.githubusercontent.com/sinai-dev/Explorer/master/overview.png">
</p>
- [Releases](#releases) - [Releases](#releases)
- [How to install](#how-to-install)
- [How to use](#how-to-use)
- [Mod Config](#mod-config)
- [Features](#features) - [Features](#features)
- [Mouse Control](#mouse-control) - [How to install](#how-to-install)
- [Mod Config](#mod-config)
- [Mouse Control](#mouse-control)
- [Building](#building) - [Building](#building)
- [Credits](#credits) - [Credits](#credits)
@ -33,10 +28,23 @@
| [BepInEx](https://github.com/BepInEx/BepInEx) | ❔ [link](https://github.com/sinai-dev/Explorer/releases/latest/download/Explorer.BepInEx.Il2Cpp.zip) | ✔️ [link](https://github.com/sinai-dev/Explorer/releases/latest/download/Explorer.BepInEx.Mono.zip) | | [BepInEx](https://github.com/BepInEx/BepInEx) | ❔ [link](https://github.com/sinai-dev/Explorer/releases/latest/download/Explorer.BepInEx.Il2Cpp.zip) | ✔️ [link](https://github.com/sinai-dev/Explorer/releases/latest/download/Explorer.BepInEx.Mono.zip) |
<b>Il2Cpp Issues:</b> <b>Il2Cpp Issues:</b>
* Some methods may still fail with a `MissingMethodException`, please let me know if you experience this (with full MelonLoader log please). * Some methods may still fail with a `MissingMethodException`, please let me know if you experience this (with full debug log please).
* Reflection may fail with certain types, see [here](https://github.com/knah/Il2CppAssemblyUnhollower#known-issues) for more details. * Reflection may fail with certain types, see [here](https://github.com/knah/Il2CppAssemblyUnhollower#known-issues) for more details.
* Scrolling with mouse wheel in the Explorer menu may not work on all games at the moment. * Scrolling with mouse wheel in the Explorer menu may not work on all games at the moment.
## Features
<p align="center">
<img src="https://raw.githubusercontent.com/sinai-dev/Explorer/master/overview.png">
</p>
* <b>Scene Explorer</b>: Simple menu to traverse the Transform heirarchy of the scene.
* <b>GameObject Inspector</b>: Various helpful tools to see and manipulate the GameObject, similar to what you can do in the Editor.
* <b>Reflection Inspector</b>: Inspect Properties and Fields. Can also set primitive values and evaluate primitive methods.
* <b>Search</b>: Search for UnityEngine.Objects with various filters, or use the helpers for static Instances and Classes.
* <b>C# Console</b>: Interactive console for evaluating C# methods on the fly, with some basic helpers.
* <b>Inspect-under-mouse</b>: Hover over an object with a collider and inspect it by clicking on it.
## How to install ## How to install
### MelonLoader ### MelonLoader
@ -53,73 +61,26 @@ Requires [BepInEx](https://github.com/BepInEx/BepInEx) to be installed for your
2. Unzip the file into the `BepInEx\plugins\` folder in your game's installation directory, created by BepInEx. 2. Unzip the file into the `BepInEx\plugins\` folder in your game's installation directory, created by BepInEx.
3. Make sure it's not in a sub-folder, `Explorer.dll` should be directly in the `plugins\` folder. 3. Make sure it's not in a sub-folder, `Explorer.dll` should be directly in the `plugins\` folder.
## How to use ## Mod Config
* Press F7 to show or hide the menu.
* Use the Scene Explorer or the Object Search to start Exploring, or the C# Console to test some code.
* See below for more specific details.
### Mod Config
There is a simple Mod Config for the Explorer. You can access the settings via the "Options" page of the main menu. There is a simple Mod Config for the Explorer. You can access the settings via the "Options" page of the main menu.
`Main Menu Toggle` (KeyCode) `Main Menu Toggle` (KeyCode) | Default: `F7`
* Sets the keybinding for the Main Menu toggle (show/hide all Explorer windows)
* See [this article](https://docs.unity3d.com/ScriptReference/KeyCode.html) for a full list of all accepted KeyCodes. * See [this article](https://docs.unity3d.com/ScriptReference/KeyCode.html) for a full list of all accepted KeyCodes.
* Default: `F7`
`Default Window Size` (Vector2) `Default Window Size` (Vector2) | Default: `x: 550, y: 700`
* Sets the default width and height for all Explorer windows when created. * Sets the default width and height for all Explorer windows when created.
* `x` is width, `y` is height.
* Default: `<x>550</x> <y>700</y>`
`Default Items per Page` (Int) `Default Items per Page` (int) | Default: `20`
* Sets the default items per page when viewing lists or search results. * Sets the default items per page when viewing lists or search results.
* Default: `20`
## Features `Enable Bitwise Editing` (bool) | Default: `false`
* Whether or not to show the Bitwise Editing helper when inspecting integers
### Scene Explorer `Enable Tab View` (bool) | Default: `true`
* Whether or not all inspector windows a grouped into a single window with tabs.
* A simple menu which allows you to traverse the Transform heirarchy of the scene. ## Mouse Control
* Click on a GameObject to set it as the current path, or <b>Inspect</b> it to send it to an Inspector Window.
### Inspectors
Explorer has two main inspector modes: <b>GameObject Inspector</b>, and <b>Reflection Inspector</b>.
<b>Tips:</b>
* When in Tab View, GameObjects are denoted by a [G] prefix, and Reflection objects are denoted by a [R] prefix.
* Hold <b>Left Shift</b> when you click the Inspect button to force Reflection mode for GameObjects and Transforms.
### GameObject Inspector
* Allows you to see the children and components on a GameObject.
* Can use some basic GameObject Controls such as translating and rotating the object, destroy it, clone it, etc.
### Reflection Inspector
* The Reflection Inspector is used for all other supported objects.
* Allows you to inspect Properties, Fields and basic Methods, as well as set primitive values and evaluate primitive methods.
* Can search and filter members for the ones you are interested in.
### Object Search
* You can search for an `UnityEngine.Object` with the Object Search feature.
* Filter by name, type, etc.
* For GameObjects and Transforms you can filter which scene they are found in too.
### C# console
* A simple C# console, allows you to execute a method body on the fly.
### Inspect-under-mouse
* Press Shift+RMB (Right Mouse Button) while the Explorer menu is open to begin Inspect-Under-Mouse.
* Hover over your desired object, if you see the name appear then you can click on it to inspect it.
* Only objects with Colliders are supported.
### Mouse Control
Explorer can force the mouse to be visible and unlocked when the menu is open, if you have enabled "Force Unlock Mouse" (Left-Alt toggle). However, you may also want to prevent the mouse clicking-through onto the game behind Explorer, this is possible but it requires specific patches for that game. Explorer can force the mouse to be visible and unlocked when the menu is open, if you have enabled "Force Unlock Mouse" (Left-Alt toggle). However, you may also want to prevent the mouse clicking-through onto the game behind Explorer, this is possible but it requires specific patches for that game.

View File

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

View File

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

View File

@ -0,0 +1,45 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
namespace Explorer.CacheObject
{
public class CacheField : CacheMember
{
public override bool IsStatic => (MemInfo as FieldInfo).IsStatic;
public override void InitMember(MemberInfo member, object declaringInstance)
{
base.InitMember(member, declaringInstance);
base.Init(null, (member as FieldInfo).FieldType);
UpdateValue();
}
public override void UpdateValue()
{
try
{
var fi = MemInfo as FieldInfo;
IValue.Value = fi.GetValue(fi.IsStatic ? null : DeclaringInstance);
base.UpdateValue();
}
catch (Exception e)
{
ReflectionException = ReflectionHelpers.ExceptionToString(e);
}
}
public override void SetValue()
{
var fi = MemInfo as FieldInfo;
fi.SetValue(fi.IsStatic ? null : DeclaringInstance, IValue.Value);
base.SetValue();
}
}
}

View File

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

View File

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

View File

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

View File

@ -0,0 +1,60 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
namespace Explorer.CacheObject
{
public class CacheProperty : CacheMember
{
public override bool IsStatic => (MemInfo as PropertyInfo).GetAccessors()[0].IsStatic;
public override void InitMember(MemberInfo member, object declaringInstance)
{
base.InitMember(member, declaringInstance);
var pi = member as PropertyInfo;
this.m_arguments = pi.GetIndexParameters();
this.m_argumentInput = new string[m_arguments.Length];
base.Init(null, pi.PropertyType);
UpdateValue();
}
public override void UpdateValue()
{
if (HasParameters && !m_isEvaluating)
{
// Need to enter parameters first.
return;
}
try
{
var pi = MemInfo as PropertyInfo;
var target = pi.GetAccessors()[0].IsStatic ? null : DeclaringInstance;
IValue.Value = pi.GetValue(target, ParseArguments());
base.UpdateValue();
}
catch (Exception e)
{
ReflectionException = ReflectionHelpers.ExceptionToString(e);
}
}
public override void SetValue()
{
var pi = MemInfo as PropertyInfo;
var target = pi.GetAccessors()[0].IsStatic ? null : DeclaringInstance;
pi.SetValue(target, IValue.Value, ParseArguments());
base.SetValue();
}
}
}

View File

@ -1,169 +0,0 @@
using System;
using System.Reflection;
using UnityEngine;
namespace Explorer
{
public static class CacheFactory
{
public static CacheObjectBase GetTypeAndCacheObject(object obj)
=> GetTypeAndCacheObject(obj, null, null);
public static CacheObjectBase GetTypeAndCacheObject(MemberInfo memberInfo, object declarer)
=> GetTypeAndCacheObject(null, memberInfo, declarer);
public static CacheObjectBase GetTypeAndCacheObject(object obj, MemberInfo memberInfo, object declarer)
{
Type type = null;
if (memberInfo != null)
{
if (memberInfo is FieldInfo fi)
{
type = fi.FieldType;
}
else if (memberInfo is PropertyInfo pi)
{
type = pi.PropertyType;
}
else if (memberInfo is MethodInfo mi)
{
type = mi.ReturnType;
}
}
else if (obj != null)
{
type = ReflectionHelpers.GetActualType(obj);
}
if (type == null)
{
return null;
}
return GetCacheObject(obj, memberInfo, declarer, type);
}
public static CacheObjectBase GetCacheObject(object obj, Type valueType)
=> GetCacheObject(obj, null, null, valueType);
private static CacheObjectBase GetCacheObject(object obj, MemberInfo memberInfo, object declaringInstance, Type valueType)
{
CacheObjectBase cached;
var pi = memberInfo as PropertyInfo;
var mi = memberInfo as MethodInfo;
// Check if can process args
if ((pi != null && !CanProcessArgs(pi.GetIndexParameters()))
|| (mi != null && !CanProcessArgs(mi.GetParameters())))
{
return null;
}
if (mi != null)
{
cached = new CacheMethod();
}
else if (valueType == typeof(GameObject) || valueType == typeof(Transform))
{
cached = new CacheGameObject();
}
else if (valueType.IsPrimitive || valueType == typeof(string))
{
cached = new CachePrimitive();
}
else if (valueType.IsEnum)
{
if (valueType.GetCustomAttributes(typeof(FlagsAttribute), true) is object[] attributes && attributes.Length > 0)
{
cached = new CacheEnumFlags();
}
else
{
cached = new CacheEnum();
}
}
else if (valueType == typeof(Vector2) || valueType == typeof(Vector3) || valueType == typeof(Vector4))
{
cached = new CacheVector();
}
else if (valueType == typeof(Quaternion))
{
cached = new CacheQuaternion();
}
else if (valueType == typeof(Color))
{
cached = new CacheColor();
}
else if (valueType == typeof(Rect))
{
cached = new CacheRect();
}
// must check this before IsEnumerable
else if (ReflectionHelpers.IsDictionary(valueType))
{
cached = new CacheDictionary();
}
else if (ReflectionHelpers.IsEnumerable(valueType))
{
cached = new CacheList();
}
else
{
cached = new CacheOther();
}
cached.Value = obj;
cached.ValueType = valueType;
if (memberInfo != null)
{
cached.MemInfo = memberInfo;
cached.DeclaringType = memberInfo.DeclaringType;
cached.DeclaringInstance = declaringInstance;
}
if (pi != null)
{
cached.m_arguments = pi.GetIndexParameters();
}
else if (mi != null)
{
cached.m_arguments = mi.GetParameters();
}
cached.m_argumentInput = new string[cached.m_arguments.Length];
cached.UpdateValue();
cached.Init();
return cached;
}
public static bool CanProcessArgs(ParameterInfo[] parameters)
{
foreach (var param in parameters)
{
var pType = param.ParameterType;
if (pType.IsByRef && pType.HasElementType)
{
pType = pType.GetElementType();
}
if (pType.IsPrimitive || pType == typeof(string))
{
continue;
}
else
{
return false;
}
}
return true;
}
}
}

View File

@ -1,430 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using UnityEngine;
namespace Explorer
{
public abstract class CacheObjectBase
{
public object Value;
public Type ValueType;
public MemberInfo MemInfo { get; set; }
public Type DeclaringType { get; set; }
public object DeclaringInstance { get; set; }
public virtual bool HasParameters => m_arguments != null && m_arguments.Length > 0;
public bool m_evaluated = false;
public bool m_isEvaluating;
public ParameterInfo[] m_arguments = new ParameterInfo[0];
public string[] m_argumentInput = new string[0];
public string ReflectionException { get; set; }
public string RichTextName => m_richTextName ?? GetRichTextName();
private string m_richTextName;
public bool CanWrite => m_canWrite ?? GetCanWrite();
private bool? m_canWrite;
public virtual void Init() { }
public abstract void DrawValue(Rect window, float width);
public virtual void UpdateValue()
{
if (MemInfo == null)
{
return;
}
if (HasParameters && !m_isEvaluating)
{
// Need to enter parameters first
return;
}
try
{
if (MemInfo.MemberType == MemberTypes.Field)
{
var fi = MemInfo as FieldInfo;
Value = fi.GetValue(fi.IsStatic ? null : DeclaringInstance);
}
else if (MemInfo.MemberType == MemberTypes.Property)
{
var pi = MemInfo as PropertyInfo;
var target = pi.GetAccessors()[0].IsStatic ? null : DeclaringInstance;
Value = pi.GetValue(target, ParseArguments());
}
ReflectionException = null;
m_evaluated = true;
m_isEvaluating = false;
}
catch (Exception e)
{
ReflectionException = ReflectionHelpers.ExceptionToString(e);
}
}
public void SetValue()
{
try
{
if (MemInfo.MemberType == MemberTypes.Field)
{
var fi = MemInfo as FieldInfo;
fi.SetValue(fi.IsStatic ? null : DeclaringInstance, Value);
}
else if (MemInfo.MemberType == MemberTypes.Property)
{
var pi = MemInfo as PropertyInfo;
if (HasParameters)
{
pi.SetValue(pi.GetAccessors()[0].IsStatic ? null : DeclaringInstance, Value, ParseArguments());
}
else
{
pi.SetValue(pi.GetAccessors()[0].IsStatic ? null : DeclaringInstance, Value, null);
}
}
}
catch (Exception e)
{
ExplorerCore.LogWarning($"Error setting value: {e.GetType()}, {e.Message}");
}
}
public object[] ParseArguments()
{
var parsedArgs = new List<object>();
for (int i = 0; i < m_arguments.Length; i++)
{
var input = m_argumentInput[i];
var type = m_arguments[i].ParameterType;
if (type.IsByRef)
{
type = type.GetElementType();
}
if (!string.IsNullOrEmpty(input))
{
if (type == typeof(string))
{
parsedArgs.Add(input);
continue;
}
else
{
try
{
var arg = type.GetMethod("Parse", new Type[] { typeof(string) })
.Invoke(null, new object[] { input });
parsedArgs.Add(arg);
continue;
}
catch
{
ExplorerCore.Log($"Argument #{i} '{m_arguments[i].Name}' ({type.Name}), could not parse input '{input}'.");
}
}
}
// No input, see if there is a default value.
if (HasDefaultValue(m_arguments[i]))
{
parsedArgs.Add(m_arguments[i].DefaultValue);
continue;
}
// Try add a null arg I guess
parsedArgs.Add(null);
}
return parsedArgs.ToArray();
}
public static bool HasDefaultValue(ParameterInfo arg) => arg.DefaultValue != DBNull.Value;
// ========= Gui Draw ==========
public const float MAX_LABEL_WIDTH = 400f;
public const string EVALUATE_LABEL = "<color=lime>Evaluate</color>";
public float CalcWhitespace(Rect window)
{
if (!(this is IExpandHeight)) return 0f;
float whitespace = (this as IExpandHeight).WhiteSpace;
if (whitespace > 0)
{
ClampLabelWidth(window, ref whitespace);
}
return whitespace;
}
public static void ClampLabelWidth(Rect window, ref float labelWidth)
{
float min = window.width * 0.37f;
if (min > MAX_LABEL_WIDTH) min = MAX_LABEL_WIDTH;
labelWidth = Mathf.Clamp(labelWidth, min, MAX_LABEL_WIDTH);
}
public void Draw(Rect window, float labelWidth = 215f)
{
if (labelWidth > 0)
{
ClampLabelWidth(window, ref labelWidth);
}
if (MemInfo != null)
{
GUILayout.Label(RichTextName, new GUILayoutOption[] { GUILayout.Width(labelWidth) });
}
else
{
GUIUnstrip.Space(labelWidth);
}
var cm = this as CacheMethod;
if (HasParameters)
{
GUILayout.BeginVertical(new GUILayoutOption[0]);
if (m_isEvaluating)
{
if (cm != null && cm.GenericArgs.Length > 0)
{
GUILayout.Label($"<b><color=orange>Generic Arguments:</color></b>", new GUILayoutOption[0]);
for (int i = 0; i < cm.GenericArgs.Length; i++)
{
string types = "";
if (cm.GenericConstraints[i].Length > 0)
{
foreach (var constraint in cm.GenericConstraints[i])
{
if (types != "") types += ", ";
string type;
if (constraint == null)
type = "Any";
else
type = constraint.ToString();
types += $"<color={UIStyles.Syntax.Class_Instance}>{type}</color>";
}
}
else
{
types = $"<color={UIStyles.Syntax.Class_Instance}>Any</color>";
}
var input = cm.GenericArgInput[i];
GUILayout.BeginHorizontal(new GUILayoutOption[0]);
GUI.skin.label.alignment = TextAnchor.MiddleCenter;
GUILayout.Label(
$"<color={UIStyles.Syntax.StructGreen}>{cm.GenericArgs[i].Name}</color>",
new GUILayoutOption[] { GUILayout.Width(15) }
);
cm.GenericArgInput[i] = GUIUnstrip.TextField(input, new GUILayoutOption[] { GUILayout.Width(150) });
GUI.skin.label.alignment = TextAnchor.MiddleLeft;
GUILayout.Label(types, new GUILayoutOption[0]);
GUILayout.EndHorizontal();
}
}
if (m_arguments.Length > 0)
{
GUILayout.Label($"<b><color=orange>Arguments:</color></b>", new GUILayoutOption[0]);
for (int i = 0; i < m_arguments.Length; i++)
{
var name = m_arguments[i].Name;
var input = m_argumentInput[i];
var type = m_arguments[i].ParameterType.Name;
var label = $"<color={UIStyles.Syntax.Class_Instance}>{type}</color> ";
label += $"<color={UIStyles.Syntax.Local}>{name}</color>";
if (HasDefaultValue(m_arguments[i]))
{
label = $"<i>[{label} = {m_arguments[i].DefaultValue ?? "null"}]</i>";
}
GUILayout.BeginHorizontal(new GUILayoutOption[0]);
GUI.skin.label.alignment = TextAnchor.MiddleCenter;
GUILayout.Label(i.ToString(), new GUILayoutOption[] { GUILayout.Width(15) });
m_argumentInput[i] = GUIUnstrip.TextField(input, new GUILayoutOption[] { GUILayout.Width(150) });
GUI.skin.label.alignment = TextAnchor.MiddleLeft;
GUILayout.Label(label, new GUILayoutOption[0]);
GUILayout.EndHorizontal();
}
}
GUILayout.BeginHorizontal(new GUILayoutOption[0]);
if (GUILayout.Button(EVALUATE_LABEL, new GUILayoutOption[] { GUILayout.Width(70) }))
{
if (cm != null)
cm.Evaluate();
else
UpdateValue();
}
if (GUILayout.Button("Cancel", new GUILayoutOption[] { GUILayout.Width(70) }))
{
m_isEvaluating = false;
}
GUILayout.EndHorizontal();
}
else
{
var lbl = $"Evaluate (";
int len = m_arguments.Length;
if (cm != null) len += cm.GenericArgs.Length;
lbl += len + " params)";
if (GUILayout.Button(lbl, new GUILayoutOption[] { GUILayout.Width(150) }))
{
m_isEvaluating = true;
}
}
GUILayout.EndVertical();
GUILayout.EndHorizontal();
GUILayout.BeginHorizontal(new GUILayoutOption[0]);
GUIUnstrip.Space(labelWidth);
}
else if (cm != null)
{
if (GUILayout.Button(EVALUATE_LABEL, new GUILayoutOption[] { GUILayout.Width(70) }))
{
cm.Evaluate();
}
GUILayout.EndHorizontal();
GUILayout.BeginHorizontal(new GUILayoutOption[0]);
GUIUnstrip.Space(labelWidth);
}
string typeName = $"<color={UIStyles.Syntax.Class_Instance}>{ValueType.FullName}</color>";
if (!string.IsNullOrEmpty(ReflectionException))
{
GUILayout.Label("<color=red>Reflection failed!</color> (" + ReflectionException + ")", new GUILayoutOption[0]);
}
else if ((HasParameters || this is CacheMethod) && !m_evaluated)
{
GUILayout.Label($"<color=grey><i>Not yet evaluated</i></color> ({typeName})", new GUILayoutOption[0]);
}
else if (Value == null && !(this is CacheMethod))
{
GUILayout.Label($"<i>null ({typeName})</i>", new GUILayoutOption[0]);
}
else
{
DrawValue(window, window.width - labelWidth - 90);
}
}
private bool GetCanWrite()
{
if (MemInfo is FieldInfo fi)
m_canWrite = !(fi.IsLiteral && !fi.IsInitOnly);
else if (MemInfo is PropertyInfo pi)
m_canWrite = pi.CanWrite;
else
m_canWrite = false;
return (bool)m_canWrite;
}
private string GetRichTextName()
{
string memberColor = "";
bool isStatic = false;
if (MemInfo is FieldInfo fi)
{
if (fi.IsStatic)
{
isStatic = true;
memberColor = UIStyles.Syntax.Field_Static;
}
else
memberColor = UIStyles.Syntax.Field_Instance;
}
else if (MemInfo is MethodInfo mi)
{
if (mi.IsStatic)
{
isStatic = true;
memberColor = UIStyles.Syntax.Method_Static;
}
else
memberColor = UIStyles.Syntax.Method_Instance;
}
else if (MemInfo is PropertyInfo pi)
{
if (pi.GetAccessors()[0].IsStatic)
{
isStatic = true;
memberColor = UIStyles.Syntax.Prop_Static;
}
else
memberColor = UIStyles.Syntax.Prop_Instance;
}
string classColor;
if (MemInfo.DeclaringType.IsValueType)
{
classColor = UIStyles.Syntax.StructGreen;
}
else if (MemInfo.DeclaringType.IsAbstract && MemInfo.DeclaringType.IsSealed)
{
classColor = UIStyles.Syntax.Class_Static;
}
else
{
classColor = UIStyles.Syntax.Class_Instance;
}
m_richTextName = $"<color={classColor}>{MemInfo.DeclaringType.Name}</color>.";
if (isStatic) m_richTextName += "<i>";
m_richTextName += $"<color={memberColor}>{MemInfo.Name}</color>";
if (isStatic) m_richTextName += "</i>";
// generic method args
if (this is CacheMethod cm && cm.GenericArgs.Length > 0)
{
m_richTextName += "<";
var args = "";
for (int i = 0; i < cm.GenericArgs.Length; i++)
{
if (args != "") args += ", ";
args += $"<color={UIStyles.Syntax.StructGreen}>{cm.GenericArgs[i].Name}</color>";
}
m_richTextName += args;
m_richTextName += ">";
}
return m_richTextName;
}
}
}

View File

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

View File

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

View File

@ -2,7 +2,7 @@
using System.Xml.Serialization; using System.Xml.Serialization;
using UnityEngine; using UnityEngine;
namespace Explorer namespace Explorer.Config
{ {
public class ModConfig public class ModConfig
{ {
@ -13,9 +13,13 @@ namespace Explorer
[XmlIgnore] public static ModConfig Instance; [XmlIgnore] public static ModConfig Instance;
// Actual configs
public KeyCode Main_Menu_Toggle = KeyCode.F7; public KeyCode Main_Menu_Toggle = KeyCode.F7;
public Vector2 Default_Window_Size = new Vector2(550, 700); public Vector2 Default_Window_Size = new Vector2(550, 700);
public int Default_Page_Limit = 20; public int Default_Page_Limit = 20;
public bool Bitwise_Support = false;
public bool Tab_View = true;
//public bool Main_Toggle_Global = true;
public static void OnLoad() public static void OnLoad()
{ {

View File

@ -197,58 +197,67 @@
</Reference> </Reference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="CachedObjects\CacheFactory.cs" /> <Compile Include="CacheObject\CacheEnumerated.cs" />
<Compile Include="CachedObjects\IExpandHeight.cs" /> <Compile Include="CacheObject\CacheFactory.cs" />
<Compile Include="CachedObjects\Struct\CacheColor.cs" /> <Compile Include="CacheObject\CacheField.cs" />
<Compile Include="CachedObjects\Object\CacheDictionary.cs" /> <Compile Include="CacheObject\CacheMember.cs" />
<Compile Include="CachedObjects\Struct\CacheEnum.cs" /> <Compile Include="CacheObject\CacheMethod.cs" />
<Compile Include="CachedObjects\Object\CacheGameObject.cs" /> <Compile Include="CacheObject\CacheProperty.cs" />
<Compile Include="CachedObjects\Object\CacheList.cs" /> <Compile Include="CacheObject\CacheObjectBase.cs" />
<Compile Include="CachedObjects\Struct\CacheEnumFlags.cs" /> <Compile Include="UI\InteractiveValue\InteractiveValue.cs" />
<Compile Include="CachedObjects\Struct\CachePrimitive.cs" /> <Compile Include="UI\InteractiveValue\Object\InteractiveDictionary.cs" />
<Compile Include="CachedObjects\Other\CacheOther.cs" /> <Compile Include="UI\InteractiveValue\Object\InteractiveEnumerable.cs" />
<Compile Include="CachedObjects\Other\CacheMethod.cs" /> <Compile Include="UI\InteractiveValue\Object\InteractiveGameObject.cs" />
<Compile Include="CachedObjects\Struct\CacheQuaternion.cs" /> <Compile Include="UI\InteractiveValue\Struct\InteractiveQuaternion.cs" />
<Compile Include="CachedObjects\Struct\CacheVector.cs" /> <Compile Include="UI\InteractiveValue\Struct\InteractiveRect.cs" />
<Compile Include="CachedObjects\Struct\CacheRect.cs" /> <Compile Include="UI\InteractiveValue\Struct\InteractiveVector.cs" />
<Compile Include="UI\InteractiveValue\Struct\InteractiveColor.cs" />
<Compile Include="UI\InteractiveValue\Struct\InteractiveEnum.cs" />
<Compile Include="UI\InteractiveValue\Struct\InteractiveFlags.cs" />
<Compile Include="UI\InteractiveValue\Struct\InteractivePrimitive.cs" />
<Compile Include="Config\ModConfig.cs" /> <Compile Include="Config\ModConfig.cs" />
<Compile Include="ExplorerCore.cs" /> <Compile Include="ExplorerCore.cs" />
<Compile Include="ExplorerBepInPlugin.cs" /> <Compile Include="ExplorerBepInPlugin.cs" />
<Compile Include="ExplorerMelonMod.cs" /> <Compile Include="ExplorerMelonMod.cs" />
<Compile Include="Extensions\ReflectionExtensions.cs" /> <Compile Include="Extensions\ReflectionExtensions.cs" />
<Compile Include="Extensions\UnityExtensions.cs" />
<Compile Include="Helpers\ReflectionHelpers.cs" />
<Compile Include="Helpers\UnityHelpers.cs" />
<Compile Include="Input\AbstractInput.cs" /> <Compile Include="Input\AbstractInput.cs" />
<Compile Include="Tests\TestClass.cs" />
<Compile Include="UI\ForceUnlockCursor.cs" />
<Compile Include="Input\InputManager.cs" /> <Compile Include="Input\InputManager.cs" />
<Compile Include="Input\InputSystem.cs" /> <Compile Include="Input\InputSystem.cs" />
<Compile Include="Input\LegacyInput.cs" /> <Compile Include="Input\LegacyInput.cs" />
<Compile Include="Input\NoInput.cs" /> <Compile Include="Input\NoInput.cs" />
<Compile Include="Menu\CursorControl.cs" />
<Compile Include="Menu\MainMenu\Pages\OptionsPage.cs" />
<Compile Include="Tests\TestClass.cs" /> <Compile Include="Tests\TestClass.cs" />
<Compile Include="UI\Inspectors\InspectUnderMouse.cs" />
<Compile Include="UI\Inspectors\Reflection\InstanceInspector.cs" />
<Compile Include="UI\Inspectors\ReflectionInspector.cs" />
<Compile Include="UI\Inspectors\Reflection\StaticInspector.cs" />
<Compile Include="UI\MainMenu.cs" />
<Compile Include="UI\Main\ConsolePage.cs" />
<Compile Include="UI\Main\Console\AutoComplete.cs" />
<Compile Include="UI\Main\Console\ScriptInteraction.cs" />
<Compile Include="UI\Main\Console\ScriptEvaluator.cs" />
<Compile Include="UI\Main\OptionsPage.cs" />
<Compile Include="UI\Main\ScenePage.cs" />
<Compile Include="UI\Main\SearchPage.cs" />
<Compile Include="UI\Main\BaseMainMenuPage.cs" />
<Compile Include="UI\Shared\Buttons.cs" />
<Compile Include="UI\Shared\IExpandHeight.cs" />
<Compile Include="UI\Shared\PageHelper.cs" />
<Compile Include="UI\Shared\ResizeDrag.cs" />
<Compile Include="UI\Shared\Syntax.cs" />
<Compile Include="UI\Shared\UIStyles.cs" />
<Compile Include="UI\Inspectors\GameObjectInspector.cs" />
<Compile Include="UI\TabViewWindow.cs" />
<Compile Include="UI\WindowBase.cs" />
<Compile Include="UI\WindowManager.cs" />
<Compile Include="UnstripFixes\GUIUnstrip.cs" /> <Compile Include="UnstripFixes\GUIUnstrip.cs" />
<Compile Include="UnstripFixes\Internal_LayoutUtility.cs" /> <Compile Include="UnstripFixes\Internal_LayoutUtility.cs" />
<Compile Include="Extensions\UnityExtensions.cs" />
<Compile Include="Helpers\PageHelper.cs" />
<Compile Include="Helpers\ReflectionHelpers.cs" />
<Compile Include="Menu\UIHelpers.cs" />
<Compile Include="Helpers\UnityHelpers.cs" />
<Compile Include="Menu\InspectUnderMouse.cs" />
<Compile Include="CachedObjects\CacheObjectBase.cs" />
<Compile Include="UnstripFixes\Internal_ScrollViewState.cs" /> <Compile Include="UnstripFixes\Internal_ScrollViewState.cs" />
<Compile Include="UnstripFixes\Internal_SliderHandler.cs" /> <Compile Include="UnstripFixes\Internal_SliderHandler.cs" />
<Compile Include="Menu\ResizeDrag.cs" />
<Compile Include="Menu\Windows\TabViewWindow.cs" />
<Compile Include="Menu\Windows\UIWindow.cs" />
<Compile Include="Menu\MainMenu\Pages\ConsolePage.cs" />
<Compile Include="Menu\MainMenu\Pages\Console\REPL.cs" />
<Compile Include="Menu\MainMenu\Pages\Console\REPLHelper.cs" />
<Compile Include="Menu\MainMenu\Pages\WindowPage.cs" />
<Compile Include="Menu\Windows\WindowManager.cs" />
<Compile Include="Menu\MainMenu\MainMenu.cs" />
<Compile Include="Menu\Windows\GameObjectWindow.cs" />
<Compile Include="Menu\Windows\ReflectionWindow.cs" />
<Compile Include="Menu\MainMenu\Pages\ScenePage.cs" />
<Compile Include="Menu\MainMenu\Pages\SearchPage.cs" />
<Compile Include="Menu\UIStyles.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="UnstripFixes\Internal.cs" /> <Compile Include="UnstripFixes\Internal.cs" />
<Compile Include="UnstripFixes\Internal_SliderState.cs" /> <Compile Include="UnstripFixes\Internal_SliderState.cs" />
@ -257,6 +266,7 @@
<None Include="ILRepack.targets" /> <None Include="ILRepack.targets" />
<None Include="packages.config" /> <None Include="packages.config" />
</ItemGroup> </ItemGroup>
<ItemGroup />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="packages\ILRepack.Lib.MSBuild.Task.2.0.18.1\build\ILRepack.Lib.MSBuild.Task.targets" Condition="Exists('packages\ILRepack.Lib.MSBuild.Task.2.0.18.1\build\ILRepack.Lib.MSBuild.Task.targets')" /> <Import Project="packages\ILRepack.Lib.MSBuild.Task.2.0.18.1\build\ILRepack.Lib.MSBuild.Task.targets" Condition="Exists('packages\ILRepack.Lib.MSBuild.Task.2.0.18.1\build\ILRepack.Lib.MSBuild.Task.targets')" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild"> <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">

View File

@ -1,11 +1,16 @@
using UnityEngine; using Explorer.Config;
using Explorer.UI;
using Explorer.UI.Inspectors;
using Explorer.UI.Main;
using Explorer.UI.Shared;
using UnityEngine;
namespace Explorer namespace Explorer
{ {
public class ExplorerCore public class ExplorerCore
{ {
public const string NAME = "Explorer (" + PLATFORM + ", " + MODLOADER + ")"; public const string NAME = "Explorer " + VERSION + " (" + PLATFORM + ", " + MODLOADER + ")";
public const string VERSION = "1.8.3.1"; public const string VERSION = "2.0.0";
public const string AUTHOR = "Sinai"; public const string AUTHOR = "Sinai";
public const string GUID = "com.sinai.explorer"; public const string GUID = "com.sinai.explorer";
@ -34,9 +39,11 @@ namespace Explorer
new WindowManager(); new WindowManager();
InputManager.Init(); InputManager.Init();
CursorControl.Init(); ForceUnlockCursor.Init();
Log($"{NAME} {VERSION} initialized."); ShowMenu = true;
Log($"{NAME} initialized.");
} }
public static bool ShowMenu public static bool ShowMenu
@ -49,7 +56,7 @@ namespace Explorer
private static void SetShowMenu(bool show) private static void SetShowMenu(bool show)
{ {
m_showMenu = show; m_showMenu = show;
CursorControl.UpdateCursorControl(); ForceUnlockCursor.UpdateCursorControl();
} }
public static void Update() public static void Update()
@ -61,7 +68,7 @@ namespace Explorer
if (ShowMenu) if (ShowMenu)
{ {
CursorControl.Update(); //CursorControl.Update();
InspectUnderMouse.Update(); InspectUnderMouse.Update();
MainMenu.Instance.Update(); MainMenu.Instance.Update();
@ -89,30 +96,30 @@ namespace Explorer
SearchPage.Instance?.OnSceneChange(); SearchPage.Instance?.OnSceneChange();
} }
public static void Log(string message) public static void Log(object message)
{ {
#if ML #if ML
MelonLoader.MelonLogger.Log(message); MelonLoader.MelonLogger.Log(message.ToString());
#else #else
ExplorerBepInPlugin.Logging?.LogMessage(message); ExplorerBepInPlugin.Logging?.LogMessage(message.ToString());
#endif #endif
} }
public static void LogWarning(string message) public static void LogWarning(object message)
{ {
#if ML #if ML
MelonLoader.MelonLogger.LogWarning(message); MelonLoader.MelonLogger.LogWarning(message.ToString());
#else #else
ExplorerBepInPlugin.Logging?.LogWarning(message); ExplorerBepInPlugin.Logging?.LogWarning(message.ToString());
#endif #endif
} }
public static void LogError(string message) public static void LogError(object message)
{ {
#if ML #if ML
MelonLoader.MelonLogger.LogError(message); MelonLoader.MelonLogger.LogError(message.ToString());
#else #else
ExplorerBepInPlugin.Logging?.LogError(message); ExplorerBepInPlugin.Logging?.LogError(message.ToString());
#endif #endif
} }
} }

View File

@ -131,12 +131,12 @@ namespace Explorer
return obj.GetType(); return obj.GetType();
} }
public static Type[] GetAllBaseTypes(object obj) public static Type[] GetAllBaseTypes(object obj) => GetAllBaseTypes(GetActualType(obj));
public static Type[] GetAllBaseTypes(Type type)
{ {
var list = new List<Type>(); var list = new List<Type>();
var type = GetActualType(obj);
while (type != null) while (type != null)
{ {
list.Add(type); list.Add(type);

View File

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

View File

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

View File

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

View File

@ -1,99 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Explorer.Tests;
using UnityEngine;
namespace Explorer
{
public class OptionsPage : WindowPage
{
public override string Name => "Options";
public string toggleKeyInputString = "";
public Vector2 defaultSizeInputVector;
public int defaultPageLimit;
private CacheObjectBase toggleKeyInput;
private CacheObjectBase defaultSizeInput;
private CacheObjectBase defaultPageLimitInput;
public override void Init()
{
toggleKeyInputString = ModConfig.Instance.Main_Menu_Toggle.ToString();
toggleKeyInput = CacheFactory.GetTypeAndCacheObject(typeof(OptionsPage).GetField("toggleKeyInputString"), this);
defaultSizeInputVector = ModConfig.Instance.Default_Window_Size;
defaultSizeInput = CacheFactory.GetTypeAndCacheObject(typeof(OptionsPage).GetField("defaultSizeInputVector"), this);
defaultPageLimit = ModConfig.Instance.Default_Page_Limit;
defaultPageLimitInput = CacheFactory.GetTypeAndCacheObject(typeof(OptionsPage).GetField("defaultPageLimit"), this);
}
public override void Update() { }
public override void DrawWindow()
{
GUI.skin.label.alignment = TextAnchor.MiddleCenter;
GUILayout.Label("<color=orange><size=16><b>Settings</b></size></color>", new GUILayoutOption[0]);
GUI.skin.label.alignment = TextAnchor.MiddleLeft;
GUILayout.BeginVertical(GUIContent.none, GUI.skin.box, new GUILayoutOption[0]);
GUILayout.BeginHorizontal(new GUILayoutOption[0]);
GUILayout.Label($"Menu Toggle Key:", new GUILayoutOption[] { GUILayout.Width(215f) });
toggleKeyInput.DrawValue(MainMenu.MainRect, MainMenu.MainRect.width - 215f);
GUILayout.EndHorizontal();
GUILayout.BeginHorizontal(new GUILayoutOption[0]);
GUILayout.Label($"Default Window Size:", new GUILayoutOption[] { GUILayout.Width(215f) });
defaultSizeInput.DrawValue(MainMenu.MainRect, MainMenu.MainRect.width - 215f);
GUILayout.EndHorizontal();
GUILayout.BeginHorizontal(new GUILayoutOption[0]);
GUILayout.Label($"Default Items per Page:", new GUILayoutOption[] { GUILayout.Width(215f) });
defaultPageLimitInput.DrawValue(MainMenu.MainRect, MainMenu.MainRect.width - 215f);
GUILayout.EndHorizontal();
if (GUILayout.Button("<color=lime><b>Apply and Save</b></color>", new GUILayoutOption[0]))
{
ApplyAndSave();
}
GUILayout.EndVertical();
//GUIUnstrip.Space(10f);
//GUI.skin.label.alignment = TextAnchor.MiddleCenter;
//GUILayout.Label("<color=orange><size=16><b>Other</b></size></color>", new GUILayoutOption[0]);
//GUI.skin.label.alignment = TextAnchor.MiddleLeft;
//GUILayout.BeginVertical(GUIContent.none, GUI.skin.box, new GUILayoutOption[0]);
//if (GUILayout.Button("Inspect Test Class", new GUILayoutOption[0]))
//{
// WindowManager.InspectObject(TestClass.Instance, out bool _);
//}
//GUILayout.EndVertical();
}
private void ApplyAndSave()
{
if (Enum.Parse(typeof(KeyCode), toggleKeyInputString) is KeyCode key)
{
ModConfig.Instance.Main_Menu_Toggle = key;
}
else
{
ExplorerCore.LogWarning($"Could not parse '{toggleKeyInputString}' to KeyCode!");
}
ModConfig.Instance.Default_Window_Size = defaultSizeInputVector;
ModConfig.Instance.Default_Page_Limit = defaultPageLimit;
ModConfig.SaveSettings();
}
}
}

View File

@ -3,46 +3,35 @@ using System.Collections.Generic;
using System; using System;
using UnityEngine; using UnityEngine;
using System.Reflection; using System.Reflection;
using System.Collections.Specialized;
// used to test multiple generic constraints
public class TestGeneric : IComparable<string>
{
public TestGeneric() { }
public int CompareTo(string other) => throw new NotImplementedException();
}
[Flags]
public enum TestFlags
{
Red,
Green,
Blue
}
// test non-flags weird enum
public enum WeirdEnum
{
First = 1,
Second,
Third = 2,
Fourth,
Fifth
}
namespace Explorer.Tests namespace Explorer.Tests
{ {
public static class StaticTestClass
{
public static int StaticProperty => 5;
public static int StaticField = 69;
public static List<string> StaticList = new List<string>
{
"one",
"two",
"three",
};
public static void StaticMethod() { }
}
public class TestClass public 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 static int StaticProperty => 5;
public static int StaticField = 5;
public int NonStaticField;
#if CPP #if CPP
public static Il2CppSystem.Collections.Generic.HashSet<string> ILHashSetTest; public static Il2CppSystem.Collections.Generic.HashSet<string> ILHashSetTest;
#endif #endif
@ -55,17 +44,6 @@ namespace Explorer.Tests
ILHashSetTest.Add("2"); ILHashSetTest.Add("2");
ILHashSetTest.Add("3"); ILHashSetTest.Add("3");
#endif #endif
testBitmask = 1 | 2;
}
public static int StaticProperty => 5;
public static int StaticField = 5;
public int NonStaticField;
public static string TestGeneric<C, T>(string arg0) where C : Component where T : TestGeneric, IComparable<string>
{
return $"C: '{typeof(C).FullName}', T: '{typeof(T).FullName}', arg0: '{arg0}'";
} }
public static string TestRefInOutGeneric<T>(ref string arg0, in int arg1, out string arg2) public static string TestRefInOutGeneric<T>(ref string arg0, in int arg1, out string arg2)
@ -75,12 +53,6 @@ namespace Explorer.Tests
return $"T: '{typeof(T).FullName}', ref arg0: '{arg0}', in arg1: '{arg1}', out arg2: '{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 // test a non-generic dictionary
public Hashtable TestNonGenericDict() public Hashtable TestNonGenericDict()
@ -93,18 +65,6 @@ namespace Explorer.Tests
}; };
} }
// IL2CPP HASHTABLE NOT SUPPORTED! Cannot assign Il2CppSystem.Object from primitive struct / string.
// Technically they are "supported" but if they contain System types they will not work.
//public Il2CppSystem.Collections.Hashtable TestIl2CppNonGenericDict()
//{
// var table = new Il2CppSystem.Collections.Hashtable();
// table.Add("One", 1);
// table.Add("One", 2);
// table.Add("One", 3);
// return table;
//}
// test HashSets // test HashSets
public static HashSet<string> HashSetTest = new HashSet<string> public static HashSet<string> HashSetTest = new HashSet<string>

View File

@ -6,21 +6,22 @@ using Harmony;
using HarmonyLib; using HarmonyLib;
#endif #endif
namespace Explorer namespace Explorer.UI
{ {
public class CursorControl public class ForceUnlockCursor
{ {
public static bool ForceUnlockMouse public static bool Unlock
{ {
get => m_forceUnlock; get => m_forceUnlock;
set => SetForceUnlock(value); set => SetForceUnlock(value);
} }
private static bool m_forceUnlock; private static bool m_forceUnlock;
private static CursorLockMode m_lastLockMode; private static CursorLockMode m_lastLockMode;
private static bool m_lastVisibleState; private static bool m_lastVisibleState;
private static bool m_currentlySettingCursor = false; private static bool m_currentlySettingCursor = false;
public static bool ShouldForceMouse => ExplorerCore.ShowMenu && ForceUnlockMouse; public static bool ShouldForceMouse => ExplorerCore.ShowMenu && Unlock;
private static Type CursorType => m_cursorType ?? (m_cursorType = ReflectionHelpers.GetTypeByName("UnityEngine.Cursor")); private static Type CursorType => m_cursorType ?? (m_cursorType = ReflectionHelpers.GetTypeByName("UnityEngine.Cursor"));
private static Type m_cursorType; private static Type m_cursorType;
@ -49,29 +50,24 @@ namespace Explorer
m_lastVisibleState = Cursor.visible; m_lastVisibleState = Cursor.visible;
// Setup Harmony Patches // Setup Harmony Patches
TryPatch("lockState", new HarmonyMethod(typeof(CursorControl).GetMethod(nameof(Prefix_set_lockState))), true); TryPatch("lockState", new HarmonyMethod(typeof(ForceUnlockCursor).GetMethod(nameof(Prefix_set_lockState))), true);
TryPatch("lockState", new HarmonyMethod(typeof(CursorControl).GetMethod(nameof(Postfix_get_lockState))), false); TryPatch("lockState", new HarmonyMethod(typeof(ForceUnlockCursor).GetMethod(nameof(Postfix_get_lockState))), false);
TryPatch("visible", new HarmonyMethod(typeof(CursorControl).GetMethod(nameof(Prefix_set_visible))), true); TryPatch("visible", new HarmonyMethod(typeof(ForceUnlockCursor).GetMethod(nameof(Prefix_set_visible))), true);
TryPatch("visible", new HarmonyMethod(typeof(CursorControl).GetMethod(nameof(Postfix_get_visible))), false); TryPatch("visible", new HarmonyMethod(typeof(ForceUnlockCursor).GetMethod(nameof(Postfix_get_visible))), false);
} }
catch (Exception e) catch (Exception e)
{ {
ExplorerCore.Log($"Exception on CursorControl.Init! {e.GetType()}, {e.Message}"); ExplorerCore.Log($"Exception on CursorControl.Init! {e.GetType()}, {e.Message}");
} }
// Enable ShowMenu and ForceUnlockMouse Unlock = true;
// (set m_showMenu directly to not call UpdateCursorState twice)
ExplorerCore.m_showMenu = true;
ForceUnlockMouse = true;
} }
private static void TryPatch(string property, HarmonyMethod patch, bool setter) private static void TryPatch(string property, HarmonyMethod patch, bool setter)
{ {
try try
{ {
// var harmony = ExplorerCore.Instance.harmonyInstance;
var harmony = var harmony =
#if ML #if ML
ExplorerMelonMod.Instance.harmonyInstance; ExplorerMelonMod.Instance.harmonyInstance;
@ -85,12 +81,12 @@ namespace Explorer
if (setter) if (setter)
{ {
// setter is prefix // setter is prefix
harmony.Patch(prop.GetSetMethod(), patch); harmony.Patch(prop.GetSetMethod(), prefix: patch);
} }
else else
{ {
// getter is postfix // getter is postfix
harmony.Patch(prop.GetGetMethod(), null, patch); harmony.Patch(prop.GetGetMethod(), postfix: patch);
} }
} }
catch (Exception e) catch (Exception e)
@ -110,7 +106,7 @@ namespace Explorer
// Check Force-Unlock input // Check Force-Unlock input
if (InputManager.GetKeyDown(KeyCode.LeftAlt)) if (InputManager.GetKeyDown(KeyCode.LeftAlt))
{ {
ForceUnlockMouse = !ForceUnlockMouse; Unlock = !Unlock;
} }
} }

View File

@ -1,14 +1,15 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using UnityEngine; using UnityEngine;
using Explorer.UI.Shared;
using Explorer.UI.Main;
#if CPP #if CPP
using UnhollowerRuntimeLib; using UnhollowerRuntimeLib;
#endif #endif
namespace Explorer namespace Explorer.UI.Inspectors
{ {
public class GameObjectWindow : UIWindow public class GameObjectInspector : WindowBase
{ {
public override string Title => WindowManager.TabView public override string Title => WindowManager.TabView
? $"<color=cyan>[G]</color> {TargetGO.name}" ? $"<color=cyan>[G]</color> {TargetGO.name}"
@ -49,7 +50,7 @@ namespace Explorer
public bool GetObjectAsGameObject() public bool GetObjectAsGameObject()
{ {
var targetType = Target.GetType(); var targetType = Target?.GetType();
if (targetType == typeof(GameObject)) if (targetType == typeof(GameObject))
{ {
@ -109,20 +110,12 @@ namespace Explorer
DestroyWindow(); DestroyWindow();
return; return;
} }
if (Target is UnityEngine.Object uObj) if (!TargetGO && !GetObjectAsGameObject())
{
if (!uObj)
{ {
ExplorerCore.Log("Target was destroyed!"); ExplorerCore.Log("Target was destroyed!");
DestroyWindow(); DestroyWindow();
return; return;
} }
}
if (!TargetGO && !GetObjectAsGameObject())
{
throw new Exception("Object is null!");
}
if (m_freeze) if (m_freeze)
{ {
@ -223,7 +216,7 @@ namespace Explorer
scroll = GUIUnstrip.BeginScrollView(scroll); scroll = GUIUnstrip.BeginScrollView(scroll);
GUILayout.BeginHorizontal(new GUILayoutOption[0]); GUIUnstrip.BeginHorizontal(new GUILayoutOption[0]);
GUILayout.Label("Scene: <color=cyan>" + (m_scene == "" ? "n/a" : m_scene) + "</color>", new GUILayoutOption[0]); GUILayout.Label("Scene: <color=cyan>" + (m_scene == "" ? "n/a" : m_scene) + "</color>", new GUILayoutOption[0]);
if (m_scene == UnityHelpers.ActiveSceneName) if (m_scene == UnityHelpers.ActiveSceneName)
{ {
@ -239,7 +232,7 @@ namespace Explorer
} }
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
GUILayout.BeginHorizontal(new GUILayoutOption[0]); GUIUnstrip.BeginHorizontal(new GUILayoutOption[0]);
GUILayout.Label("Path:", new GUILayoutOption[] { GUILayout.Width(50) }); GUILayout.Label("Path:", new GUILayoutOption[] { GUILayout.Width(50) });
string pathlabel = TargetGO.transform.GetGameObjectPath(); string pathlabel = TargetGO.transform.GetGameObjectPath();
if (TargetGO.transform.parent != null) if (TargetGO.transform.parent != null)
@ -252,19 +245,19 @@ namespace Explorer
GUIUnstrip.TextArea(pathlabel, new GUILayoutOption[0]); GUIUnstrip.TextArea(pathlabel, new GUILayoutOption[0]);
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
GUILayout.BeginHorizontal(new GUILayoutOption[0]); GUIUnstrip.BeginHorizontal(new GUILayoutOption[0]);
GUILayout.Label("Name:", new GUILayoutOption[] { GUILayout.Width(50) }); GUILayout.Label("Name:", new GUILayoutOption[] { GUILayout.Width(50) });
GUIUnstrip.TextArea(m_name, new GUILayoutOption[0]); GUIUnstrip.TextArea(m_name, new GUILayoutOption[0]);
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
// --- Horizontal Columns section --- // --- Horizontal Columns section ---
GUILayout.BeginHorizontal(new GUILayoutOption[0]); GUIUnstrip.BeginHorizontal(new GUILayoutOption[0]);
GUILayout.BeginVertical(new GUILayoutOption[] { GUILayout.Width(rect.width / 2 - 17) }); GUIUnstrip.BeginVertical(new GUILayoutOption[] { GUILayout.Width(rect.width / 2 - 17) });
TransformList(rect); TransformList(rect);
GUILayout.EndVertical(); GUILayout.EndVertical();
GUILayout.BeginVertical(new GUILayoutOption[] { GUILayout.Width(rect.width / 2 - 17) }); GUIUnstrip.BeginVertical(new GUILayoutOption[] { GUILayout.Width(rect.width / 2 - 17) });
ComponentList(rect); ComponentList(rect);
GUILayout.EndVertical(); GUILayout.EndVertical();
@ -289,12 +282,12 @@ namespace Explorer
private void TransformList(Rect m_rect) private void TransformList(Rect m_rect)
{ {
GUILayout.BeginVertical(GUIContent.none, GUI.skin.box, null); GUIUnstrip.BeginVertical(GUIContent.none, GUI.skin.box, null);
m_transformScroll = GUIUnstrip.BeginScrollView(m_transformScroll); m_transformScroll = GUIUnstrip.BeginScrollView(m_transformScroll);
GUILayout.Label("<b><size=15>Children</size></b>", new GUILayoutOption[0]); GUILayout.Label("<b><size=15>Children</size></b>", new GUILayoutOption[0]);
GUILayout.BeginHorizontal(new GUILayoutOption[0]); GUIUnstrip.BeginHorizontal(new GUILayoutOption[0]);
ChildPages.DrawLimitInputArea(); ChildPages.DrawLimitInputArea();
if (ChildPages.ItemCount > ChildPages.ItemsPerPage) if (ChildPages.ItemCount > ChildPages.ItemsPerPage)
@ -302,7 +295,7 @@ namespace Explorer
ChildPages.CurrentPageLabel(); ChildPages.CurrentPageLabel();
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
GUILayout.BeginHorizontal(new GUILayoutOption[0]); GUIUnstrip.BeginHorizontal(new GUILayoutOption[0]);
if (GUILayout.Button("< Prev", new GUILayoutOption[] { GUILayout.Width(80) })) if (GUILayout.Button("< Prev", new GUILayoutOption[] { GUILayout.Width(80) }))
{ {
@ -329,7 +322,7 @@ namespace Explorer
continue; continue;
} }
UIHelpers.GOButton(obj.gameObject, InspectGameObject, false, m_rect.width / 2 - 80); Buttons.GameObjectButton(obj.gameObject, InspectGameObject, false, m_rect.width / 2 - 80);
} }
} }
else else
@ -343,11 +336,11 @@ namespace Explorer
private void ComponentList(Rect m_rect) private void ComponentList(Rect m_rect)
{ {
GUILayout.BeginVertical(GUIContent.none, GUI.skin.box, null); GUIUnstrip.BeginVertical(GUIContent.none, GUI.skin.box, null);
m_compScroll = GUIUnstrip.BeginScrollView(m_compScroll); m_compScroll = GUIUnstrip.BeginScrollView(m_compScroll);
GUILayout.Label("<b><size=15>Components</size></b>", new GUILayoutOption[0]); GUILayout.Label("<b><size=15>Components</size></b>", new GUILayoutOption[0]);
GUILayout.BeginHorizontal(new GUILayoutOption[0]); GUIUnstrip.BeginHorizontal(new GUILayoutOption[0]);
CompPages.DrawLimitInputArea(); CompPages.DrawLimitInputArea();
if (CompPages.ItemCount > CompPages.ItemsPerPage) if (CompPages.ItemCount > CompPages.ItemsPerPage)
@ -355,7 +348,7 @@ namespace Explorer
CompPages.CurrentPageLabel(); CompPages.CurrentPageLabel();
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
GUILayout.BeginHorizontal(new GUILayoutOption[0]); GUIUnstrip.BeginHorizontal(new GUILayoutOption[0]);
if (GUILayout.Button("< Prev", new GUILayoutOption[] { GUILayout.Width(80) })) if (GUILayout.Button("< Prev", new GUILayoutOption[] { GUILayout.Width(80) }))
{ {
@ -368,7 +361,7 @@ namespace Explorer
} }
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
GUILayout.BeginHorizontal(new GUILayoutOption[0]); GUIUnstrip.BeginHorizontal(new GUILayoutOption[0]);
var width = m_rect.width / 2 - 135f; var width = m_rect.width / 2 - 135f;
m_addComponentInput = GUIUnstrip.TextField(m_addComponentInput, new GUILayoutOption[] { GUILayout.Width(width) }); m_addComponentInput = GUIUnstrip.TextField(m_addComponentInput, new GUILayoutOption[] { GUILayout.Width(width) });
if (GUILayout.Button("Add Comp", new GUILayoutOption[0])) if (GUILayout.Button("Add Comp", new GUILayoutOption[0]))
@ -417,7 +410,7 @@ namespace Explorer
#else #else
component.GetType(); component.GetType();
#endif #endif
GUILayout.BeginHorizontal(new GUILayoutOption[0]); GUIUnstrip.BeginHorizontal(new GUILayoutOption[0]);
if (ReflectionHelpers.BehaviourType.IsAssignableFrom(type)) if (ReflectionHelpers.BehaviourType.IsAssignableFrom(type))
{ {
#if CPP #if CPP
@ -484,7 +477,7 @@ namespace Explorer
{ {
if (m_hideControls) if (m_hideControls)
{ {
GUILayout.BeginHorizontal(new GUILayoutOption[0]); GUIUnstrip.BeginHorizontal(new GUILayoutOption[0]);
GUILayout.Label("<b><size=15>GameObject Controls</size></b>", new GUILayoutOption[] { GUILayout.Width(200) }); GUILayout.Label("<b><size=15>GameObject Controls</size></b>", new GUILayoutOption[] { GUILayout.Width(200) });
if (GUILayout.Button("^ Show ^", new GUILayoutOption[] { GUILayout.Width(75) })) if (GUILayout.Button("^ Show ^", new GUILayoutOption[] { GUILayout.Width(75) }))
{ {
@ -495,9 +488,9 @@ namespace Explorer
return; return;
} }
GUILayout.BeginVertical(GUIContent.none, GUI.skin.box, new GUILayoutOption[] { GUILayout.Width(520) }); GUIUnstrip.BeginVertical(GUIContent.none, GUI.skin.box, new GUILayoutOption[] { GUILayout.Width(520) });
GUILayout.BeginHorizontal(new GUILayoutOption[0]); GUIUnstrip.BeginHorizontal(new GUILayoutOption[0]);
GUILayout.Label("<b><size=15>GameObject Controls</size></b>", new GUILayoutOption[] { GUILayout.Width(200) }); 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) })) if (GUILayout.Button("v Hide v", new GUILayoutOption[] { GUILayout.Width(75) }))
{ {
@ -505,13 +498,13 @@ namespace Explorer
} }
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
GUILayout.BeginHorizontal(new GUILayoutOption[0]); GUIUnstrip.BeginHorizontal(new GUILayoutOption[0]);
bool m_active = TargetGO.activeSelf; 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 (TargetGO.activeSelf != m_active) { TargetGO.SetActive(m_active); } if (TargetGO.activeSelf != m_active) { TargetGO.SetActive(m_active); }
UIHelpers.InstantiateButton(TargetGO, 100); Buttons.InstantiateButton(TargetGO, 100);
if (GUILayout.Button("Set DontDestroyOnLoad", new GUILayoutOption[] { GUILayout.Width(170) })) if (GUILayout.Button("Set DontDestroyOnLoad", new GUILayoutOption[] { GUILayout.Width(170) }))
{ {
@ -530,7 +523,7 @@ namespace Explorer
} }
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
GUILayout.BeginHorizontal(new GUILayoutOption[0]); GUIUnstrip.BeginHorizontal(new GUILayoutOption[0]);
m_setParentInput = GUIUnstrip.TextField(m_setParentInput, new GUILayoutOption[0]); m_setParentInput = GUIUnstrip.TextField(m_setParentInput, new GUILayoutOption[0]);
if (GUILayout.Button("Set Parent", new GUILayoutOption[] { GUILayout.Width(80) })) if (GUILayout.Button("Set Parent", new GUILayoutOption[] { GUILayout.Width(80) }))
@ -551,13 +544,13 @@ namespace Explorer
} }
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
GUILayout.BeginVertical(GUIContent.none, GUI.skin.box, null); GUIUnstrip.BeginVertical(GUIContent.none, GUI.skin.box, null);
m_cachedInput[0] = TranslateControl(TranslateType.Position, ref m_translateAmount, false); m_cachedInput[0] = TranslateControl(TranslateType.Position, ref m_translateAmount, false);
m_cachedInput[1] = TranslateControl(TranslateType.Rotation, ref m_rotateAmount, true); m_cachedInput[1] = TranslateControl(TranslateType.Rotation, ref m_rotateAmount, true);
m_cachedInput[2] = TranslateControl(TranslateType.Scale, ref m_scaleAmount, false); m_cachedInput[2] = TranslateControl(TranslateType.Scale, ref m_scaleAmount, false);
GUILayout.BeginHorizontal(new GUILayoutOption[0]); GUIUnstrip.BeginHorizontal(new GUILayoutOption[0]);
if (GUILayout.Button("<color=lime>Apply to Transform</color>", new GUILayoutOption[0]) || m_autoApplyTransform) if (GUILayout.Button("<color=lime>Apply to Transform</color>", new GUILayoutOption[0]) || m_autoApplyTransform)
{ {
if (m_localContext) if (m_localContext)
@ -583,7 +576,7 @@ namespace Explorer
} }
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
GUILayout.BeginHorizontal(new GUILayoutOption[0]); GUIUnstrip.BeginHorizontal(new GUILayoutOption[0]);
BoolToggle(ref m_autoApplyTransform, "Auto-apply to Transform?"); BoolToggle(ref m_autoApplyTransform, "Auto-apply to Transform?");
BoolToggle(ref m_autoUpdateTransform, "Auto-update from transform?"); BoolToggle(ref m_autoUpdateTransform, "Auto-update from transform?");
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
@ -645,7 +638,7 @@ namespace Explorer
private Vector3 TranslateControl(TranslateType mode, ref float amount, bool multByTime) private Vector3 TranslateControl(TranslateType mode, ref float amount, bool multByTime)
{ {
GUILayout.BeginHorizontal(new GUILayoutOption[0]); GUIUnstrip.BeginHorizontal(new GUILayoutOption[0]);
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) });
@ -668,7 +661,7 @@ namespace Explorer
Vector3 input = m_cachedInput[(int)mode]; Vector3 input = m_cachedInput[(int)mode];
GUILayout.BeginHorizontal(new GUILayoutOption[0]); GUIUnstrip.BeginHorizontal(new GUILayoutOption[0]);
GUI.skin.label.alignment = TextAnchor.MiddleRight; GUI.skin.label.alignment = TextAnchor.MiddleRight;
GUILayout.Label("<color=cyan>X:</color>", new GUILayoutOption[] { GUILayout.Width(20) }); GUILayout.Label("<color=cyan>X:</color>", new GUILayoutOption[] { GUILayout.Width(20) });

View File

@ -1,6 +1,6 @@
using UnityEngine; using UnityEngine;
namespace Explorer namespace Explorer.UI.Inspectors
{ {
public class InspectUnderMouse public class InspectUnderMouse
{ {

View File

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

View File

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

View File

@ -3,33 +3,43 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using UnityEngine; using UnityEngine;
using Explorer.UI;
using Explorer.UI.Shared;
using Explorer.CacheObject;
using Explorer.UI.Inspectors;
#if CPP #if CPP
using UnhollowerBaseLib; using UnhollowerBaseLib;
#endif #endif
namespace Explorer namespace Explorer.UI.Inspectors
{ {
public class ReflectionWindow : UIWindow public abstract class ReflectionInspector : WindowBase
{ {
public override string Title => WindowManager.TabView public override string Title => WindowManager.TabView
? $"<color=cyan>[R]</color> {TargetType.Name}" ? $"<color=cyan>[R]</color> {TargetType.Name}"
: $"Reflection Inspector ({TargetType.Name})"; : $"Reflection Inspector ({TargetType.Name})";
public virtual bool IsStaticInspector { get; }
public Type TargetType; public Type TargetType;
private CacheObjectBase[] m_allCachedMembers; private CacheMember[] m_allCachedMembers;
private CacheObjectBase[] m_cachedMembersFiltered; private CacheMember[] m_cachedMembersFiltered;
public PageHelper Pages = new PageHelper(); public PageHelper Pages = new PageHelper();
private bool m_autoUpdate = false; private bool m_autoUpdate = false;
private string m_search = ""; private string m_search = "";
public MemberTypes m_filter = MemberTypes.Property; public MemberTypes m_typeFilter = MemberTypes.Property;
private bool m_hideFailedReflection = false; private bool m_hideFailedReflection = false;
// some extra cast-caching private MemberScopes m_scopeFilter;
private UnityEngine.Object m_uObj; private enum MemberScopes
private Component m_component; {
Both,
Instance,
Static
}
private static readonly HashSet<string> _typeAndMemberBlacklist = new HashSet<string> private static readonly HashSet<string> _typeAndMemberBlacklist = new HashSet<string>
{ {
@ -47,50 +57,24 @@ namespace Explorer
}; };
public override void Init() public override void Init()
{
if (!IsStaticInspector)
{ {
TargetType = ReflectionHelpers.GetActualType(Target); TargetType = ReflectionHelpers.GetActualType(Target);
CacheMembers(ReflectionHelpers.GetAllBaseTypes(Target)); CacheMembers(ReflectionHelpers.GetAllBaseTypes(Target));
// cache the extra cast-caching
#if CPP
if (Target is Il2CppSystem.Object ilObject)
{
var unityObj = ilObject.TryCast<UnityEngine.Object>();
if (unityObj)
{
m_uObj = unityObj;
var component = ilObject.TryCast<Component>();
if (component)
{
m_component = component;
} }
else
{
CacheMembers(new Type[] { TargetType });
} }
} }
#else
m_uObj = Target as UnityEngine.Object;
m_component = Target as Component;
#endif
}
public override void Update() public override void Update()
{ {
if (Target == null) if (m_allCachedMembers == null)
{ {
ExplorerCore.Log("Target is null!");
DestroyWindow();
return; return;
} }
if (Target is UnityEngine.Object uObj)
{
if (!uObj)
{
ExplorerCore.Log("Target was destroyed!");
DestroyWindow();
return;
}
}
m_cachedMembersFiltered = m_allCachedMembers.Where(x => ShouldProcessMember(x)).ToArray(); m_cachedMembersFiltered = m_allCachedMembers.Where(x => ShouldProcessMember(x)).ToArray();
@ -108,12 +92,22 @@ namespace Explorer
} }
} }
private bool ShouldProcessMember(CacheObjectBase holder) public virtual bool ShouldProcessMember(CacheMember holder)
{ {
// check MemberTypes filter // check MemberTypes filter
if (m_filter != MemberTypes.All && m_filter != holder.MemInfo?.MemberType) if (m_typeFilter != MemberTypes.All && m_typeFilter != holder.MemInfo?.MemberType)
return false; return false;
// check scope filter
if (m_scopeFilter == MemberScopes.Instance)
{
return !holder.IsStatic;
}
else if (m_scopeFilter == MemberScopes.Static)
{
return holder.IsStatic;
}
// hide failed reflection // hide failed reflection
if (!string.IsNullOrEmpty(holder.ReflectionException) && m_hideFailedReflection) if (!string.IsNullOrEmpty(holder.ReflectionException) && m_hideFailedReflection)
return false; return false;
@ -130,7 +124,7 @@ namespace Explorer
private void CacheMembers(Type[] types) private void CacheMembers(Type[] types)
{ {
var list = new List<CacheObjectBase>(); var list = new List<CacheMember>();
var cachedSigs = new List<string>(); var cachedSigs = new List<string>();
foreach (var declaringType in types) foreach (var declaringType in types)
@ -150,7 +144,7 @@ namespace Explorer
string exception = null; string exception = null;
#if CPP #if CPP
if (target is Il2CppSystem.Object ilObject) if (!IsStaticInspector && target is Il2CppSystem.Object ilObject)
{ {
try try
{ {
@ -162,14 +156,26 @@ namespace Explorer
} }
} }
#endif #endif
foreach (var member in infos) foreach (var member in infos)
{
try
{ {
// make sure member type is Field, Method of Property (4 / 8 / 16) // make sure member type is Field, Method of Property (4 / 8 / 16)
int m = (int)member.MemberType; int m = (int)member.MemberType;
if (m < 4 || m > 16) if (m < 4 || m > 16)
continue; continue;
var fi = member as FieldInfo;
var pi = member as PropertyInfo;
var mi = member as MethodInfo;
if (IsStaticInspector)
{
if (fi != null && !fi.IsStatic) continue;
else if (pi != null && !pi.GetAccessors()[0].IsStatic) continue;
else if (mi != null && !mi.IsStatic) continue;
}
// check blacklisted members // check blacklisted members
var sig = $"{member.DeclaringType.Name}.{member.Name}"; var sig = $"{member.DeclaringType.Name}.{member.Name}";
if (_typeAndMemberBlacklist.Any(it => it == sig)) if (_typeAndMemberBlacklist.Any(it => it == sig))
@ -178,11 +184,11 @@ namespace Explorer
if (_methodStartsWithBlacklist.Any(it => member.Name.StartsWith(it))) if (_methodStartsWithBlacklist.Any(it => member.Name.StartsWith(it)))
continue; continue;
if (member is MethodInfo mi) if (mi != null)
{ {
AppendParams(mi.GetParameters()); AppendParams(mi.GetParameters());
} }
else if (member is PropertyInfo pi) else if (pi != null)
{ {
AppendParams(pi.GetIndexParameters()); AppendParams(pi.GetIndexParameters());
} }
@ -206,7 +212,7 @@ namespace Explorer
try try
{ {
var cached = CacheFactory.GetTypeAndCacheObject(member, target); var cached = CacheFactory.GetCacheObject(member, target);
if (cached != null) if (cached != null)
{ {
cachedSigs.Add(sig); cachedSigs.Add(sig);
@ -220,6 +226,12 @@ namespace Explorer
ExplorerCore.Log(e.ToString()); ExplorerCore.Log(e.ToString());
} }
} }
catch (Exception e)
{
ExplorerCore.LogWarning($"Exception caching member {member.DeclaringType.FullName}.{member.Name}!");
ExplorerCore.Log(e.ToString());
}
}
} }
m_allCachedMembers = list.ToArray(); m_allCachedMembers = list.ToArray();
@ -241,51 +253,49 @@ namespace Explorer
GUIUnstrip.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(new GUILayoutOption[0]); var asInstance = this as InstanceInspector;
GUILayout.Label("<b>Type:</b> <color=cyan>" + TargetType.FullName + "</color>", new GUILayoutOption[] { GUILayout.Width(245f) });
if (m_uObj)
{
GUILayout.Label("Name: " + m_uObj.name, new GUILayoutOption[0]);
}
GUILayout.EndHorizontal();
if (m_uObj) GUIUnstrip.BeginHorizontal(new GUILayoutOption[0]);
var labelWidth = (asInstance != null && asInstance.m_uObj)
? new GUILayoutOption[] { GUILayout.Width(245f) }
: new GUILayoutOption[0];
GUILayout.Label("<b>Type:</b> <color=cyan>" + TargetType.FullName + "</color>", labelWidth);
if (asInstance != null)
{ {
GUILayout.BeginHorizontal(new GUILayoutOption[0]); asInstance.DrawInstanceControls(rect);
GUILayout.Label("<b>Tools:</b>", new GUILayoutOption[] { GUILayout.Width(80) });
UIHelpers.InstantiateButton(m_uObj);
if (m_component && m_component.gameObject is GameObject obj)
{
GUI.skin.label.alignment = TextAnchor.MiddleRight;
GUILayout.Label("GameObject:", new GUILayoutOption[] { GUILayout.Width(135) });
var charWidth = obj.name.Length * 15;
var maxWidth = rect.width - 350;
var labelWidth = charWidth < maxWidth ? charWidth : maxWidth;
if (GUILayout.Button("<color=#00FF00>" + obj.name + "</color>", new GUILayoutOption[] { GUILayout.Width(labelWidth) }))
{
WindowManager.InspectObject(obj, out bool _);
}
GUI.skin.label.alignment = TextAnchor.UpperLeft;
} }
else
{
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
} }
UIStyles.HorizontalLine(Color.grey); UIStyles.HorizontalLine(Color.grey);
GUILayout.BeginHorizontal(new GUILayoutOption[0]); GUIUnstrip.BeginHorizontal(new GUILayoutOption[0]);
GUILayout.Label("<b>Search:</b>", new GUILayoutOption[] { GUILayout.Width(75) }); GUILayout.Label("<b>Search:</b>", new GUILayoutOption[] { GUILayout.Width(75) });
m_search = GUIUnstrip.TextField(m_search, new GUILayoutOption[0]); m_search = GUIUnstrip.TextField(m_search, new GUILayoutOption[0]);
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
GUILayout.BeginHorizontal(new GUILayoutOption[0]); GUIUnstrip.BeginHorizontal(new GUILayoutOption[0]);
GUILayout.Label("<b>Filter:</b>", new GUILayoutOption[] { GUILayout.Width(75) }); GUILayout.Label("<b>Filter:</b>", new GUILayoutOption[] { GUILayout.Width(75) });
FilterToggle(MemberTypes.All, "All"); FilterTypeToggle(MemberTypes.All, "All");
FilterToggle(MemberTypes.Property, "Properties"); FilterTypeToggle(MemberTypes.Property, "Properties");
FilterToggle(MemberTypes.Field, "Fields"); FilterTypeToggle(MemberTypes.Field, "Fields");
FilterToggle(MemberTypes.Method, "Methods"); FilterTypeToggle(MemberTypes.Method, "Methods");
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
GUILayout.BeginHorizontal(new GUILayoutOption[0]); if (this is InstanceInspector)
{
GUIUnstrip.BeginHorizontal(new GUILayoutOption[0]);
GUILayout.Label("<b>Scope:</b>", new GUILayoutOption[] { GUILayout.Width(75) });
FilterScopeToggle(MemberScopes.Both, "Both");
FilterScopeToggle(MemberScopes.Instance, "Instance");
FilterScopeToggle(MemberScopes.Static, "Static");
GUILayout.EndHorizontal();
}
GUIUnstrip.BeginHorizontal(new GUILayoutOption[0]);
GUILayout.Label("<b>Values:</b>", new GUILayoutOption[] { GUILayout.Width(75) }); GUILayout.Label("<b>Values:</b>", new GUILayoutOption[] { GUILayout.Width(75) });
if (GUILayout.Button("Update", new GUILayoutOption[] { GUILayout.Width(100) })) if (GUILayout.Button("Update", new GUILayoutOption[] { GUILayout.Width(100) }))
{ {
@ -303,7 +313,7 @@ namespace Explorer
Pages.ItemCount = m_cachedMembersFiltered.Length; Pages.ItemCount = m_cachedMembersFiltered.Length;
// prev/next page buttons // prev/next page buttons
GUILayout.BeginHorizontal(new GUILayoutOption[0]); GUIUnstrip.BeginHorizontal(new GUILayoutOption[0]);
Pages.DrawLimitInputArea(); Pages.DrawLimitInputArea();
@ -331,7 +341,7 @@ namespace Explorer
UIStyles.HorizontalLine(Color.grey); UIStyles.HorizontalLine(Color.grey);
GUILayout.BeginVertical(GUIContent.none, GUI.skin.box, null); GUIUnstrip.BeginVertical(GUIContent.none, GUI.skin.box, null);
var members = this.m_cachedMembersFiltered; var members = this.m_cachedMembersFiltered;
int start = Pages.CalculateOffsetIndex(); int start = Pages.CalculateOffsetIndex();
@ -340,7 +350,7 @@ namespace Explorer
{ {
var holder = members[j]; var holder = members[j];
GUILayout.BeginHorizontal(new GUILayoutOption[] { GUILayout.Height(25) }); GUIUnstrip.BeginHorizontal(new GUILayoutOption[] { GUILayout.Height(25) });
try try
{ {
holder.Draw(rect, 180f); holder.Draw(rect, 180f);
@ -379,9 +389,9 @@ namespace Explorer
} }
} }
private void FilterToggle(MemberTypes mode, string label) private void FilterTypeToggle(MemberTypes mode, string label)
{ {
if (m_filter == mode) if (m_typeFilter == mode)
{ {
GUI.color = Color.green; GUI.color = Color.green;
} }
@ -391,7 +401,26 @@ namespace Explorer
} }
if (GUILayout.Button(label, new GUILayoutOption[] { GUILayout.Width(100) })) if (GUILayout.Button(label, new GUILayoutOption[] { GUILayout.Width(100) }))
{ {
m_filter = mode; m_typeFilter = mode;
Pages.PageOffset = 0;
scroll = Vector2.zero;
}
GUI.color = Color.white;
}
private void FilterScopeToggle(MemberScopes mode, string label)
{
if (m_scopeFilter == mode)
{
GUI.color = Color.green;
}
else
{
GUI.color = Color.white;
}
if (GUILayout.Button(label, new GUILayoutOption[] { GUILayout.Width(100) }))
{
m_scopeFilter = mode;
Pages.PageOffset = 0; Pages.PageOffset = 0;
scroll = Vector2.zero; scroll = Vector2.zero;
} }

View File

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

View File

@ -1,15 +1,16 @@
using System; using System;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading;
using UnityEngine; using UnityEngine;
#if CPP #if CPP
using UnhollowerBaseLib; using UnhollowerBaseLib;
#endif #endif
using Explorer.UI.Shared;
using Explorer.CacheObject;
namespace Explorer namespace Explorer.UI
{ {
public class CacheDictionary : CacheObjectBase, IExpandHeight public class InteractiveDictionary : InteractiveValue, IExpandHeight
{ {
public bool IsExpanded { get; set; } public bool IsExpanded { get; set; }
public float WhiteSpace { get; set; } = 215f; public float WhiteSpace { get; set; } = 215f;
@ -54,7 +55,7 @@ namespace Explorer
// note: "ValueType" is the Dictionary itself, TypeOfValues is the 'Dictionary.Values' type. // note: "ValueType" is the Dictionary itself, TypeOfValues is the 'Dictionary.Values' type.
// get keys and values // get keys and values
var keys = ValueType.GetProperty("Keys") .GetValue(Value, null); var keys = ValueType.GetProperty("Keys").GetValue(Value, null);
var values = ValueType.GetProperty("Values").GetValue(Value, null); var values = ValueType.GetProperty("Values").GetValue(Value, null);
// create lists to hold them // create lists to hold them
@ -98,8 +99,9 @@ namespace Explorer
{ {
if (ValueType.IsGenericType) if (ValueType.IsGenericType)
{ {
m_keysType = ValueType.GetGenericArguments()[0]; var generics = ValueType.GetGenericArguments();
m_valuesType = ValueType.GetGenericArguments()[1]; m_keysType = generics[0];
m_valuesType = generics[1];
} }
else else
{ {
@ -114,7 +116,10 @@ namespace Explorer
// first make sure we won't run into a TypeInitializationException. // first make sure we won't run into a TypeInitializationException.
if (!EnsureDictionaryIsSupported()) if (!EnsureDictionaryIsSupported())
{ {
ReflectionException = "Dictionary Type not supported with Reflection!"; if (OwnerCacheObject is CacheMember cacheMember)
{
cacheMember.ReflectionException = "Dictionary Type not supported with Reflection!";
}
return; return;
} }
@ -232,7 +237,7 @@ namespace Explorer
if (count > Pages.ItemsPerPage) if (count > Pages.ItemsPerPage)
{ {
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
GUILayout.BeginHorizontal(new GUILayoutOption[0]); GUIUnstrip.BeginHorizontal(new GUILayoutOption[0]);
GUIUnstrip.Space(whitespace); GUIUnstrip.Space(whitespace);
@ -262,7 +267,7 @@ namespace Explorer
//collapsing the BeginHorizontal called from ReflectionWindow.WindowFunction or previous array entry //collapsing the BeginHorizontal called from ReflectionWindow.WindowFunction or previous array entry
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
GUILayout.BeginHorizontal(new GUILayoutOption[0]); GUIUnstrip.BeginHorizontal(new GUILayoutOption[0]);
//GUIUnstrip.Space(whitespace); //GUIUnstrip.Space(whitespace);
@ -278,13 +283,13 @@ namespace Explorer
GUI.skin.label.alignment = TextAnchor.MiddleLeft; GUI.skin.label.alignment = TextAnchor.MiddleLeft;
GUILayout.Label("Key:", new GUILayoutOption[] { GUILayout.Width(40) }); GUILayout.Label("Key:", new GUILayoutOption[] { GUILayout.Width(40) });
if (key != null) if (key != null)
key.DrawValue(window, (window.width / 2) - 80f); key.IValue.DrawValue(window, (window.width / 2) - 80f);
else else
GUILayout.Label("<i>null</i>", new GUILayoutOption[0]); GUILayout.Label("<i>null</i>", new GUILayoutOption[0]);
GUILayout.Label("Value:", new GUILayoutOption[] { GUILayout.Width(40) }); GUILayout.Label("Value:", new GUILayoutOption[] { GUILayout.Width(40) });
if (Value != null) if (val != null)
val.DrawValue(window, (window.width / 2) - 80f); val.IValue.DrawValue(window, (window.width / 2) - 80f);
else else
GUILayout.Label("<i>null</i>", new GUILayoutOption[0]); GUILayout.Label("<i>null</i>", new GUILayoutOption[0]);
} }

View File

@ -4,17 +4,19 @@ using System.Collections.Generic;
using System.Threading; using System.Threading;
using System.Reflection; using System.Reflection;
using UnityEngine; using UnityEngine;
using Explorer.UI.Shared;
using Explorer.CacheObject;
namespace Explorer namespace Explorer.UI
{ {
public class CacheList : CacheObjectBase, IExpandHeight public class InteractiveEnumerable : InteractiveValue, IExpandHeight
{ {
public bool IsExpanded { get; set; } public bool IsExpanded { get; set; }
public float WhiteSpace { get; set; } = 215f; public float WhiteSpace { get; set; } = 215f;
public PageHelper Pages = new PageHelper(); public PageHelper Pages = new PageHelper();
private CacheObjectBase[] m_cachedEntries = new CacheObjectBase[0]; private CacheEnumerated[] m_cachedEntries = new CacheEnumerated[0];
// Type of Entries in the Array // Type of Entries in the Array
public Type EntryType public Type EntryType
@ -173,16 +175,16 @@ namespace Explorer
{ {
if (m_entryType == null) if (m_entryType == null)
{ {
if (this.MemInfo != null) if (OwnerCacheObject is CacheMember cacheMember && cacheMember.MemInfo != null)
{ {
Type memberType = null; Type memberType = null;
switch (this.MemInfo.MemberType) switch (cacheMember.MemInfo.MemberType)
{ {
case MemberTypes.Field: case MemberTypes.Field:
memberType = (MemInfo as FieldInfo).FieldType; memberType = (cacheMember.MemInfo as FieldInfo).FieldType;
break; break;
case MemberTypes.Property: case MemberTypes.Property:
memberType = (MemInfo as PropertyInfo).PropertyType; memberType = (cacheMember.MemInfo as PropertyInfo).PropertyType;
break; break;
} }
@ -201,7 +203,7 @@ namespace Explorer
} }
} }
// IList probably won't be able to get any EntryType. // use System.Object for non-generic.
if (m_entryType == null) if (m_entryType == null)
{ {
m_entryType = typeof(object); m_entryType = typeof(object);
@ -230,7 +232,8 @@ namespace Explorer
return; return;
} }
var list = new List<CacheObjectBase>(); var list = new List<CacheEnumerated>();
int index = 0;
while (enumerator.MoveNext()) while (enumerator.MoveNext())
{ {
var obj = enumerator.Current; var obj = enumerator.Current;
@ -252,19 +255,25 @@ namespace Explorer
} }
#endif #endif
if (CacheFactory.GetCacheObject(obj, t) is CacheObjectBase cached) var cached = new CacheEnumerated() { Index = index, RefIList = Value as IList, ParentEnumeration = this };
{ cached.Init(obj, EntryType);
list.Add(cached); list.Add(cached);
//if (CacheFactory.GetCacheObject(obj, t) is CacheObjectBase cached)
//{
// list.Add(cached);
//}
//else
//{
// list.Add(null);
//}
} }
else else
{ {
list.Add(null); list.Add(null);
} }
}
else index++;
{
list.Add(null);
}
} }
m_cachedEntries = list.ToArray(); m_cachedEntries = list.ToArray();
@ -318,7 +327,7 @@ namespace Explorer
if (count > Pages.ItemsPerPage) if (count > Pages.ItemsPerPage)
{ {
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
GUILayout.BeginHorizontal(new GUILayoutOption[0]); GUIUnstrip.BeginHorizontal(new GUILayoutOption[0]);
GUIUnstrip.Space(whitespace); GUIUnstrip.Space(whitespace);
@ -347,11 +356,11 @@ namespace Explorer
//collapsing the BeginHorizontal called from ReflectionWindow.WindowFunction or previous array entry //collapsing the BeginHorizontal called from ReflectionWindow.WindowFunction or previous array entry
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
GUILayout.BeginHorizontal(new GUILayoutOption[0]); GUIUnstrip.BeginHorizontal(new GUILayoutOption[0]);
GUIUnstrip.Space(whitespace); GUIUnstrip.Space(whitespace);
if (entry == null || entry.Value == null) if (entry == null || entry.IValue == null)
{ {
GUILayout.Label($"[{i}] <i><color=grey>(null)</color></i>", new GUILayoutOption[0]); GUILayout.Label($"[{i}] <i><color=grey>(null)</color></i>", new GUILayoutOption[0]);
} }
@ -361,7 +370,7 @@ namespace Explorer
GUILayout.Label($"[{i}]", new GUILayoutOption[] { GUILayout.Width(30) }); GUILayout.Label($"[{i}]", new GUILayoutOption[] { GUILayout.Width(30) });
GUI.skin.label.alignment = TextAnchor.MiddleLeft; GUI.skin.label.alignment = TextAnchor.MiddleLeft;
entry.DrawValue(window, window.width - (whitespace + 85)); entry.IValue.DrawValue(window, window.width - (whitespace + 85));
} }
} }

View File

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

View File

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

View File

@ -4,10 +4,12 @@ using System.Linq;
using System.Text; using System.Text;
using System.Reflection; using System.Reflection;
using UnityEngine; using UnityEngine;
using Explorer.UI.Shared;
using Explorer.CacheObject;
namespace Explorer namespace Explorer.UI
{ {
public class CacheEnum : CacheObjectBase public class InteractiveEnum : InteractiveValue
{ {
internal static Dictionary<Type, string[]> EnumNamesInternalCache = new Dictionary<Type, string[]>(); internal static Dictionary<Type, string[]> EnumNamesInternalCache = new Dictionary<Type, string[]>();
@ -27,7 +29,10 @@ namespace Explorer
} }
else else
{ {
ReflectionException = "Unknown, could not get Enum names."; if (OwnerCacheObject is CacheMember cacheMember)
{
cacheMember.ReflectionException = "Unknown, could not get Enum names.";
}
} }
} }
@ -54,21 +59,21 @@ namespace Explorer
public override void DrawValue(Rect window, float width) public override void DrawValue(Rect window, float width)
{ {
if (CanWrite) if (OwnerCacheObject.CanWrite)
{ {
if (GUILayout.Button("<", new GUILayoutOption[] { GUILayout.Width(25) })) if (GUILayout.Button("<", new GUILayoutOption[] { GUILayout.Width(25) }))
{ {
SetEnum(-1); SetEnum(-1);
SetValue(); OwnerCacheObject.SetValue();
} }
if (GUILayout.Button(">", new GUILayoutOption[] { GUILayout.Width(25) })) if (GUILayout.Button(">", new GUILayoutOption[] { GUILayout.Width(25) }))
{ {
SetEnum(1); SetEnum(1);
SetValue(); OwnerCacheObject.SetValue();
} }
} }
GUILayout.Label(Value.ToString() + $"<color={UIStyles.Syntax.StructGreen}><i> ({ValueType})</i></color>", new GUILayoutOption[0]); GUILayout.Label(Value.ToString() + $"<color={Syntax.StructGreen}><i> ({ValueType})</i></color>", new GUILayoutOption[0]);
} }
public void SetEnum(int change) public void SetEnum(int change)

View File

@ -3,10 +3,12 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using UnityEngine; using UnityEngine;
using Explorer.UI.Shared;
using Explorer.CacheObject;
namespace Explorer namespace Explorer.UI
{ {
public class CacheEnumFlags : CacheEnum, IExpandHeight public class InteractiveFlags : InteractiveEnum, IExpandHeight
{ {
public bool[] m_enabledFlags = new bool[0]; public bool[] m_enabledFlags = new bool[0];
@ -45,7 +47,7 @@ namespace Explorer
public override void DrawValue(Rect window, float width) public override void DrawValue(Rect window, float width)
{ {
if (CanWrite) if (OwnerCacheObject.CanWrite)
{ {
if (!IsExpanded) if (!IsExpanded)
{ {
@ -73,7 +75,7 @@ namespace Explorer
for (int i = 0; i < EnumNames.Length; i++) for (int i = 0; i < EnumNames.Length; i++)
{ {
GUILayout.BeginHorizontal(new GUILayoutOption[0]); GUIUnstrip.BeginHorizontal(new GUILayoutOption[0]);
GUIUnstrip.Space(whitespace); GUIUnstrip.Space(whitespace);
m_enabledFlags[i] = GUILayout.Toggle(m_enabledFlags[i], EnumNames[i], new GUILayoutOption[0]); m_enabledFlags[i] = GUILayout.Toggle(m_enabledFlags[i], EnumNames[i], new GUILayoutOption[0]);
@ -81,7 +83,7 @@ namespace Explorer
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
} }
GUILayout.BeginHorizontal(new GUILayoutOption[0]); GUIUnstrip.BeginHorizontal(new GUILayoutOption[0]);
GUIUnstrip.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) }))
{ {
@ -89,7 +91,7 @@ namespace Explorer
} }
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
GUILayout.BeginHorizontal(new GUILayoutOption[0]); GUIUnstrip.BeginHorizontal(new GUILayoutOption[0]);
} }
} }
@ -105,7 +107,7 @@ namespace Explorer
} }
} }
Value = Enum.Parse(ValueType, val); Value = Enum.Parse(ValueType, val);
SetValue(); OwnerCacheObject.SetValue();
} }
} }
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

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

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

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

View File

@ -3,10 +3,12 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using UnityEngine; using UnityEngine;
using UnityEngine.SceneManagement; using UnityEngine.SceneManagement;
using Explorer.UI.Shared;
using Explorer.CacheObject;
namespace Explorer namespace Explorer.UI.Main
{ {
public class ScenePage : WindowPage public class ScenePage : BaseMainMenuPage
{ {
public static ScenePage Instance; public static ScenePage Instance;
@ -23,12 +25,12 @@ namespace Explorer
// gameobject list // gameobject list
private Transform m_currentTransform; private Transform m_currentTransform;
private readonly List<GameObjectCache> m_objectList = new List<GameObjectCache>(); private readonly List<CacheObjectBase> m_objectList = new List<CacheObjectBase>();
// search bar // search bar
private bool m_searching = false; private bool m_searching = false;
private string m_searchInput = ""; private string m_searchInput = "";
private List<GameObjectCache> m_searchResults = new List<GameObjectCache>(); private List<CacheObjectBase> m_searchResults = new List<CacheObjectBase>();
public override void Init() public override void Init()
{ {
@ -80,9 +82,9 @@ namespace Explorer
} }
} }
public List<GameObjectCache> SearchSceneObjects(string _search) public List<CacheObjectBase> SearchSceneObjects(string _search)
{ {
var matches = new List<GameObjectCache>(); var matches = new List<CacheObjectBase>();
foreach (var obj in Resources.FindObjectsOfTypeAll(ReflectionHelpers.GameObjectType)) foreach (var obj in Resources.FindObjectsOfTypeAll(ReflectionHelpers.GameObjectType))
{ {
@ -93,7 +95,7 @@ namespace Explorer
#endif #endif
if (go.name.ToLower().Contains(_search.ToLower()) && go.scene.name == m_currentScene) if (go.name.ToLower().Contains(_search.ToLower()) && go.scene.name == m_currentScene)
{ {
matches.Add(new GameObjectCache(go)); matches.Add(CacheFactory.GetCacheObject(go));
} }
} }
@ -172,7 +174,7 @@ namespace Explorer
for (int i = offset; i < offset + Pages.ItemsPerPage && i < Pages.ItemCount; i++) for (int i = offset; i < offset + Pages.ItemsPerPage && i < Pages.ItemCount; i++)
{ {
var child = allTransforms[i]; var child = allTransforms[i];
m_objectList.Add(new GameObjectCache(child.gameObject)); m_objectList.Add(CacheFactory.GetCacheObject(child));
} }
} }
@ -214,7 +216,7 @@ namespace Explorer
{ {
DrawHeaderArea(); DrawHeaderArea();
GUILayout.BeginVertical(GUIContent.none, GUI.skin.box, null); GUIUnstrip.BeginVertical(GUIContent.none, GUI.skin.box, null);
DrawPageButtons(); DrawPageButtons();
@ -240,7 +242,7 @@ namespace Explorer
private void DrawHeaderArea() private void DrawHeaderArea()
{ {
GUILayout.BeginHorizontal(new GUILayoutOption[0]); GUIUnstrip.BeginHorizontal(new GUILayoutOption[0]);
// Current Scene label // Current Scene label
GUILayout.Label("Current Scene:", new GUILayoutOption[] { GUILayout.Width(120) }); GUILayout.Label("Current Scene:", new GUILayoutOption[] { GUILayout.Width(120) });
@ -250,7 +252,7 @@ namespace Explorer
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
// ----- GameObject Search ----- // ----- GameObject Search -----
GUILayout.BeginHorizontal(GUIContent.none, GUI.skin.box, null); GUIUnstrip.BeginHorizontal(GUIContent.none, GUI.skin.box, null);
GUILayout.Label("<b>Search Scene:</b>", new GUILayoutOption[] { GUILayout.Width(100) }); GUILayout.Label("<b>Search Scene:</b>", new GUILayoutOption[] { GUILayout.Width(100) });
m_searchInput = GUIUnstrip.TextField(m_searchInput, new GUILayoutOption[0]); m_searchInput = GUIUnstrip.TextField(m_searchInput, new GUILayoutOption[0]);
@ -305,7 +307,7 @@ namespace Explorer
private void DrawPageButtons() private void DrawPageButtons()
{ {
GUILayout.BeginHorizontal(new GUILayoutOption[0]); GUIUnstrip.BeginHorizontal(new GUILayoutOption[0]);
Pages.DrawLimitInputArea(); Pages.DrawLimitInputArea();
@ -336,7 +338,7 @@ namespace Explorer
{ {
if (m_currentTransform != null) if (m_currentTransform != null)
{ {
GUILayout.BeginHorizontal(new GUILayoutOption[0]); GUIUnstrip.BeginHorizontal(new GUILayoutOption[0]);
if (GUILayout.Button("<-", new GUILayoutOption[] { GUILayout.Width(35) })) if (GUILayout.Button("<-", new GUILayoutOption[] { GUILayout.Width(35) }))
{ {
TraverseUp(); TraverseUp();
@ -347,7 +349,7 @@ namespace Explorer
new GUILayoutOption[] { GUILayout.Width(MainMenu.MainRect.width - 187f) }); new GUILayoutOption[] { GUILayout.Width(MainMenu.MainRect.width - 187f) });
} }
UIHelpers.SmallInspectButton(m_currentTransform); Buttons.InspectButton(m_currentTransform);
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
} }
@ -372,11 +374,15 @@ namespace Explorer
if (obj == null) continue; if (obj == null) continue;
if (!obj.RefGameObject) try
{
var go = obj.IValue.Value as GameObject ?? (obj.IValue.Value as Transform)?.gameObject;
if (!go)
{ {
string label = "<color=red><i>null"; string label = "<color=red><i>null";
if (obj.RefGameObject != null) if (go != null)
{ {
label += " (Destroyed)"; label += " (Destroyed)";
} }
@ -386,15 +392,11 @@ namespace Explorer
} }
else else
{ {
UIHelpers.GOButton_Impl(obj.RefGameObject, Buttons.GameObjectButton(go, SetTransformTarget, true, MainMenu.MainRect.width - 170f);
obj.EnabledColor,
obj.Label,
obj.RefGameObject.activeSelf,
SetTransformTarget,
true,
MainMenu.MainRect.width - 170);
} }
} }
catch { }
}
} }
} }
@ -413,17 +415,11 @@ namespace Explorer
for (int i = offset; i < offset + Pages.ItemsPerPage && i < m_searchResults.Count; i++) for (int i = offset; i < offset + Pages.ItemsPerPage && i < m_searchResults.Count; i++)
{ {
var obj = m_searchResults[i]; var obj = m_searchResults[i].IValue.Value as GameObject;
if (obj.RefGameObject) if (obj)
{ {
UIHelpers.GOButton_Impl(obj.RefGameObject, Buttons.GameObjectButton(obj, SetTransformTarget, true, MainMenu.MainRect.width - 170);
obj.EnabledColor,
obj.Label,
obj.RefGameObject.activeSelf,
SetTransformTarget,
true,
MainMenu.MainRect.width - 170);
} }
else else
{ {
@ -436,42 +432,5 @@ namespace Explorer
GUILayout.Label("<color=red><i>No results found!</i></color>", new GUILayoutOption[0]); GUILayout.Label("<color=red><i>No results found!</i></color>", new GUILayoutOption[0]);
} }
} }
// -------- Mini GameObjectCache class ---------- //
public class GameObjectCache
{
public GameObject RefGameObject;
public string Label;
public Color EnabledColor;
public int ChildCount;
public GameObjectCache(GameObject obj)
{
RefGameObject = obj;
ChildCount = obj.transform.childCount;
Label = (ChildCount > 0) ? "[" + obj.transform.childCount + " children] " : "";
Label += obj.name;
bool enabled = obj.activeSelf;
int childCount = obj.transform.childCount;
if (enabled)
{
if (childCount > 0)
{
EnabledColor = Color.green;
}
else
{
EnabledColor = UIStyles.LightGreen;
}
}
else
{
EnabledColor = Color.red;
}
}
}
} }
} }

View File

@ -4,14 +4,16 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using UnityEngine; using UnityEngine;
using Explorer.UI.Shared;
using Explorer.CacheObject;
namespace Explorer namespace Explorer.UI.Main
{ {
public class SearchPage : WindowPage public class SearchPage : BaseMainMenuPage
{ {
public static SearchPage Instance; public static SearchPage Instance;
public override string Name { get => "Object Search"; } public override string Name { get => "Search"; }
private string m_searchInput = ""; private string m_searchInput = "";
private string m_typeInput = ""; private string m_typeInput = "";
@ -52,11 +54,9 @@ namespace Explorer
Pages.PageOffset = 0; Pages.PageOffset = 0;
} }
public override void Update() public override void Update() { }
{
}
private void CacheResults(IEnumerable results) private void CacheResults(IEnumerable results, bool isStaticClasses = false)
{ {
m_searchResults = new List<CacheObjectBase>(); m_searchResults = new List<CacheObjectBase>();
@ -76,7 +76,8 @@ namespace Explorer
} }
#endif #endif
var cache = CacheFactory.GetTypeAndCacheObject(toCache); var cache = CacheFactory.GetCacheObject(toCache);
cache.IsStaticClassSearchResult = isStaticClasses;
m_searchResults.Add(cache); m_searchResults.Add(cache);
} }
@ -89,12 +90,15 @@ namespace Explorer
try try
{ {
// helpers // helpers
GUILayout.BeginHorizontal(GUIContent.none, GUI.skin.box, null); GUIUnstrip.BeginHorizontal(GUIContent.none, GUI.skin.box, null);
GUILayout.Label("<b><color=orange>Helpers</color></b>", new GUILayoutOption[] { GUILayout.Width(70) }); GUILayout.Label("<b><color=orange>Helpers</color></b>", new GUILayoutOption[] { GUILayout.Width(70) });
if (GUILayout.Button("Find Static Instances", new GUILayoutOption[] { GUILayout.Width(180) })) if (GUILayout.Button("Find Static Instances", new GUILayoutOption[] { GUILayout.Width(180) }))
{ {
//m_searchResults = GetInstanceClassScanner().ToList(); CacheResults(GetStaticInstances());
CacheResults(GetInstanceClassScanner()); }
if (GUILayout.Button("Find Static Classes", new GUILayoutOption[] { GUILayout.Width(180) }))
{
CacheResults(GetStaticClasses(), true);
} }
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
@ -102,7 +106,7 @@ namespace Explorer
SearchBox(); SearchBox();
// results // results
GUILayout.BeginVertical(GUIContent.none, GUI.skin.box, null); GUIUnstrip.BeginVertical(GUIContent.none, GUI.skin.box, null);
GUI.skin.label.alignment = TextAnchor.MiddleCenter; GUI.skin.label.alignment = TextAnchor.MiddleCenter;
GUILayout.Label("<b><color=orange>Results </color></b>" + " (" + m_searchResults.Count + ")", new GUILayoutOption[0]); GUILayout.Label("<b><color=orange>Results </color></b>" + " (" + m_searchResults.Count + ")", new GUILayoutOption[0]);
@ -110,7 +114,7 @@ namespace Explorer
int count = m_searchResults.Count; int count = m_searchResults.Count;
GUILayout.BeginHorizontal(new GUILayoutOption[0]); GUIUnstrip.BeginHorizontal(new GUILayoutOption[0]);
Pages.DrawLimitInputArea(); Pages.DrawLimitInputArea();
@ -165,21 +169,21 @@ namespace Explorer
private void SearchBox() private void SearchBox()
{ {
GUILayout.BeginVertical(GUIContent.none, GUI.skin.box, null); GUIUnstrip.BeginVertical(GUIContent.none, GUI.skin.box, null);
// ----- GameObject Search ----- // ----- GameObject Search -----
GUI.skin.label.alignment = TextAnchor.MiddleCenter; GUI.skin.label.alignment = TextAnchor.MiddleCenter;
GUILayout.Label("<b><color=orange>Search</color></b>", new GUILayoutOption[0]); GUILayout.Label("<b><color=orange>Search</color></b>", new GUILayoutOption[0]);
GUI.skin.label.alignment = TextAnchor.UpperLeft; GUI.skin.label.alignment = TextAnchor.UpperLeft;
GUILayout.BeginHorizontal(new GUILayoutOption[0]); GUIUnstrip.BeginHorizontal(new GUILayoutOption[0]);
GUILayout.Label("Name Contains:", new GUILayoutOption[] { GUILayout.Width(100) }); GUILayout.Label("Name Contains:", new GUILayoutOption[] { GUILayout.Width(100) });
m_searchInput = GUIUnstrip.TextField(m_searchInput, new GUILayoutOption[] { GUILayout.Width(200) }); m_searchInput = GUIUnstrip.TextField(m_searchInput, new GUILayoutOption[] { GUILayout.Width(200) });
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
GUILayout.BeginHorizontal(new GUILayoutOption[0]); GUIUnstrip.BeginHorizontal(new GUILayoutOption[0]);
GUILayout.Label("Class Filter:", new GUILayoutOption[] { GUILayout.Width(100) }); GUILayout.Label("Class Filter:", new GUILayoutOption[] { GUILayout.Width(100) });
ClassFilterToggle(TypeFilter.Object, "Object"); ClassFilterToggle(TypeFilter.Object, "Object");
@ -189,7 +193,7 @@ namespace Explorer
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
if (TypeMode == TypeFilter.Custom) if (TypeMode == TypeFilter.Custom)
{ {
GUILayout.BeginHorizontal(new GUILayoutOption[0]); GUIUnstrip.BeginHorizontal(new GUILayoutOption[0]);
GUI.skin.label.alignment = TextAnchor.MiddleRight; GUI.skin.label.alignment = TextAnchor.MiddleRight;
GUILayout.Label("Custom Class:", new GUILayoutOption[] { GUILayout.Width(250) }); GUILayout.Label("Custom Class:", new GUILayoutOption[] { GUILayout.Width(250) });
GUI.skin.label.alignment = TextAnchor.UpperLeft; GUI.skin.label.alignment = TextAnchor.UpperLeft;
@ -197,7 +201,7 @@ namespace Explorer
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
} }
GUILayout.BeginHorizontal(new GUILayoutOption[0]); GUIUnstrip.BeginHorizontal(new GUILayoutOption[0]);
GUILayout.Label("Scene Filter:", new GUILayoutOption[] { GUILayout.Width(100) }); GUILayout.Label("Scene Filter:", new GUILayoutOption[] { GUILayout.Width(100) });
SceneFilterToggle(SceneFilter.Any, "Any", 60); SceneFilterToggle(SceneFilter.Any, "Any", 60);
SceneFilterToggle(SceneFilter.This, "This Scene", 100); SceneFilterToggle(SceneFilter.This, "This Scene", 100);
@ -402,7 +406,7 @@ namespace Explorer
} }
// credit: ManlyMarco (RuntimeUnityEditor) // credit: ManlyMarco (RuntimeUnityEditor)
public static IEnumerable<object> GetInstanceClassScanner() public static IEnumerable<object> GetStaticInstances()
{ {
var query = AppDomain.CurrentDomain.GetAssemblies() var query = AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(t => t.TryGetTypes()) .SelectMany(t => t.TryGetTypes())
@ -457,5 +461,41 @@ namespace Explorer
} }
} }
} }
private IEnumerable GetStaticClasses()
{
var list = new List<Type>();
var input = m_searchInput?.ToLower() ?? "";
foreach (var asm in AppDomain.CurrentDomain.GetAssemblies())
{
try
{
foreach (var type in asm.TryGetTypes())
{
if (!type.IsAbstract || !type.IsSealed)
{
continue;
}
if (!string.IsNullOrEmpty(input))
{
var typename = type.FullName.ToLower();
if (!typename.Contains(input))
{
continue;
}
}
list.Add(type);
}
}
catch { }
}
return list;
}
} }
} }

View File

@ -3,8 +3,12 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using UnityEngine; using UnityEngine;
using Explorer.Config;
using Explorer.UI.Main;
using Explorer.UI.Shared;
using Explorer.UI.Inspectors;
namespace Explorer namespace Explorer.UI
{ {
public class MainMenu public class MainMenu
{ {
@ -32,7 +36,7 @@ namespace Explorer
public const int MainWindowID = 5000; public const int MainWindowID = 5000;
public static Rect MainRect = new Rect(5, 5, ModConfig.Instance.Default_Window_Size.x, ModConfig.Instance.Default_Window_Size.y); public static Rect MainRect = new Rect(5, 5, ModConfig.Instance.Default_Window_Size.x, ModConfig.Instance.Default_Window_Size.y);
public static readonly List<WindowPage> Pages = new List<WindowPage>(); public static readonly List<BaseMainMenuPage> Pages = new List<BaseMainMenuPage>();
private static int m_currentPage = 0; private static int m_currentPage = 0;
public static void SetCurrentPage(int index) public static void SetCurrentPage(int index)
@ -86,7 +90,7 @@ namespace Explorer
private void MainHeader() private void MainHeader()
{ {
GUILayout.BeginHorizontal(new GUILayoutOption[0]); GUIUnstrip.BeginHorizontal(new GUILayoutOption[0]);
for (int i = 0; i < Pages.Count; i++) for (int i = 0; i < Pages.Count; i++)
{ {
if (m_currentPage == i) if (m_currentPage == i)
@ -101,15 +105,15 @@ namespace Explorer
} }
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
GUILayout.BeginHorizontal(new GUILayoutOption[0]); GUIUnstrip.BeginHorizontal(new GUILayoutOption[0]);
GUI.color = Color.white; GUI.color = Color.white;
InspectUnderMouse.EnableInspect = GUILayout.Toggle(InspectUnderMouse.EnableInspect, "Inspect Under Mouse (Shift + RMB)", new GUILayoutOption[0]); InspectUnderMouse.EnableInspect = GUILayout.Toggle(InspectUnderMouse.EnableInspect, "Inspect Under Mouse (Shift + RMB)", new GUILayoutOption[0]);
bool mouseState = CursorControl.ForceUnlockMouse; bool mouseState = ForceUnlockCursor.Unlock;
bool setMouse = GUILayout.Toggle(mouseState, "Force Unlock Mouse (Left Alt)", new GUILayoutOption[0]); bool setMouse = GUILayout.Toggle(mouseState, "Force Unlock Mouse (Left Alt)", new GUILayoutOption[0]);
if (setMouse != mouseState) CursorControl.ForceUnlockMouse = setMouse; if (setMouse != mouseState) ForceUnlockCursor.Unlock = setMouse;
WindowManager.TabView = GUILayout.Toggle(WindowManager.TabView, "Tab View", new GUILayoutOption[0]); //WindowManager.TabView = GUILayout.Toggle(WindowManager.TabView, "Tab View", new GUILayoutOption[0]);
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
//GUIUnstrip.Space(10); //GUIUnstrip.Space(10);

View File

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

View File

@ -1,6 +1,6 @@
using UnityEngine; using UnityEngine;
namespace Explorer namespace Explorer.UI.Shared
{ {
public enum Turn public enum Turn
{ {
@ -21,7 +21,7 @@ namespace Explorer
CalculateMaxOffset(); CalculateMaxOffset();
} }
} }
private int m_itemsPerPage = ModConfig.Instance.Default_Page_Limit; private int m_itemsPerPage = Config.ModConfig.Instance.Default_Page_Limit;
public int ItemCount public int ItemCount
{ {

View File

@ -4,7 +4,7 @@ using UnhollowerBaseLib;
#endif #endif
using UnityEngine; using UnityEngine;
namespace Explorer namespace Explorer.UI.Shared
{ {
public class ResizeDrag public class ResizeDrag
{ {
@ -26,7 +26,7 @@ namespace Explorer
try try
{ {
GUILayout.BeginHorizontal(GUIContent.none, GUI.skin.box, null); GUIUnstrip.BeginHorizontal(GUIContent.none, GUI.skin.box, null);
GUI.skin.label.alignment = TextAnchor.MiddleCenter; GUI.skin.label.alignment = TextAnchor.MiddleCenter;
#if ML #if ML
@ -35,8 +35,7 @@ namespace Explorer
GUILayout.Button("<-- Drag to resize -->", new GUILayoutOption[] { GUILayout.Height(15) }); GUILayout.Button("<-- Drag to resize -->", new GUILayoutOption[] { GUILayout.Height(15) });
#endif #endif
//var r = GUILayoutUtility.GetLastRect(); var r = GUIUnstrip.GetLastRect();
var r = Internal_LayoutUtility.GetLastRect();
var mousePos = InputManager.MousePosition; var mousePos = InputManager.MousePosition;
@ -87,7 +86,7 @@ namespace Explorer
} }
else else
{ {
GUILayout.BeginHorizontal(new GUILayoutOption[0]); GUIUnstrip.BeginHorizontal(new GUILayoutOption[0]);
GUILayout.Label("Resize window:", new GUILayoutOption[] { GUILayout.Width(100) }); GUILayout.Label("Resize window:", new GUILayoutOption[] { GUILayout.Width(100) });
@ -117,7 +116,7 @@ namespace Explorer
#else // mono #else // mono
GUILayout.BeginHorizontal(GUIContent.none, GUI.skin.box, null); GUIUnstrip.BeginHorizontal(GUIContent.none, GUI.skin.box, null);
GUI.skin.label.alignment = TextAnchor.MiddleCenter; GUI.skin.label.alignment = TextAnchor.MiddleCenter;
GUILayout.Button(gcDrag, GUI.skin.label, new GUILayoutOption[] { GUILayout.Height(15) }); GUILayout.Button(gcDrag, GUI.skin.label, new GUILayoutOption[] { GUILayout.Height(15) });

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

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

View File

@ -1,29 +1,10 @@
using UnityEngine; using UnityEngine;
using Object = UnityEngine.Object; using Object = UnityEngine.Object;
namespace Explorer namespace Explorer.UI.Shared
{ {
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 = "#92c470";
}
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,15 +1,17 @@
using UnityEngine; using System;
using UnityEngine;
using Explorer.UI.Shared;
namespace Explorer namespace Explorer.UI
{ {
public class TabViewWindow : UIWindow public class TabViewWindow : WindowBase
{ {
public override string Title => $"Tabs ({WindowManager.Windows.Count})"; public override string Title => $"Tabs ({WindowManager.Windows.Count})";
public static TabViewWindow Instance => m_instance ?? (m_instance = new TabViewWindow()); public static TabViewWindow Instance => m_instance ?? (m_instance = new TabViewWindow());
private static TabViewWindow m_instance; private static TabViewWindow m_instance;
private UIWindow m_targetWindow; private WindowBase m_targetWindow;
public int TargetTabID = 0; public int TargetTabID = 0;
public override bool IsTabViewWindow => true; public override bool IsTabViewWindow => true;
@ -61,8 +63,8 @@ namespace Explorer
GUIUnstrip.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(GUIContent.none, GUI.skin.box, null); GUIUnstrip.BeginVertical(GUIContent.none, GUI.skin.box, null);
GUILayout.BeginHorizontal(new GUILayoutOption[0]); GUIUnstrip.BeginHorizontal(new GUILayoutOption[0]);
GUI.skin.button.alignment = TextAnchor.MiddleLeft; GUI.skin.button.alignment = TextAnchor.MiddleLeft;
int tabPerRow = Mathf.FloorToInt((float)((decimal)m_rect.width / 238)); int tabPerRow = Mathf.FloorToInt((float)((decimal)m_rect.width / 238));
int rowCount = 0; int rowCount = 0;
@ -72,7 +74,7 @@ namespace Explorer
{ {
rowCount = 0; rowCount = 0;
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
GUILayout.BeginHorizontal(new GUILayoutOption[0]); GUIUnstrip.BeginHorizontal(new GUILayoutOption[0]);
} }
rowCount++; rowCount++;
@ -97,15 +99,18 @@ namespace Explorer
m_targetWindow.WindowFunction(m_targetWindow.windowID); m_targetWindow.WindowFunction(m_targetWindow.windowID);
try
{
m_rect = ResizeDrag.ResizeWindow(m_rect, windowID); m_rect = ResizeDrag.ResizeWindow(m_rect, windowID);
}
catch { }
GUIUnstrip.EndArea(); GUIUnstrip.EndArea();
} }
catch { } catch (Exception e)
{
if (!e.Message.Contains("in a group with only"))
{
ExplorerCore.Log("Exception drawing Tab View window: " + e.GetType() + ", " + e.Message);
ExplorerCore.Log(e.StackTrace);
}
}
} }
} }
} }

View File

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

View File

@ -1,90 +1,40 @@
using System.Collections.Generic; using System;
using System.Collections.Generic;
using UnityEngine; using UnityEngine;
using Explorer.UI.Inspectors;
namespace Explorer namespace Explorer.UI
{ {
public class WindowManager public class WindowManager
{ {
public static WindowManager Instance; public static WindowManager Instance;
public static bool TabView = true; public static bool TabView = Config.ModConfig.Instance.Tab_View;
public static List<UIWindow> Windows = new List<UIWindow>(); public static List<WindowBase> Windows = new List<WindowBase>();
public static int CurrentWindowID { get; set; } = 500000; public static int CurrentWindowID { get; set; } = 500000;
private static Rect m_lastWindowRect; private static Rect m_lastWindowRect;
private static readonly List<UIWindow> m_windowsToDestroy = new List<UIWindow>(); private static readonly List<WindowBase> m_windowsToDestroy = new List<WindowBase>();
public WindowManager() public WindowManager()
{ {
Instance = this; Instance = this;
} }
public static void DestroyWindow(UIWindow window) public static void DestroyWindow(WindowBase window)
{ {
m_windowsToDestroy.Add(window); m_windowsToDestroy.Add(window);
} }
public void Update() public static WindowBase InspectObject(object obj, out bool createdNew, bool forceReflection = false)
{
if (m_windowsToDestroy.Count > 0)
{
foreach (var window in m_windowsToDestroy)
{
if (Windows.Contains(window))
{
Windows.Remove(window);
}
}
m_windowsToDestroy.Clear();
}
if (TabView)
{
TabViewWindow.Instance.Update();
}
else
{
for (int i = 0; i < Windows.Count; i++)
{
var window = Windows[i];
if (window != null)
{
window.Update();
}
}
}
}
public void OnGUI()
{
if (TabView)
{
if (Windows.Count > 0)
{
TabViewWindow.Instance.OnGUI();
}
}
else
{
foreach (var window in Windows)
{
window.OnGUI();
}
}
}
// ========= Public Helpers =========
public static UIWindow InspectObject(object obj, out bool createdNew, bool forceReflection = false)
{ {
createdNew = false; createdNew = false;
if (InputManager.GetKey(KeyCode.LeftShift)) //if (InputManager.GetKey(KeyCode.LeftShift))
{ //{
forceReflection = true; // forceReflection = true;
} //}
#if CPP #if CPP
Il2CppSystem.Object iObj = null; Il2CppSystem.Object iObj = null;
@ -136,7 +86,7 @@ namespace Explorer
} }
} }
private static void FocusWindow(UIWindow window) private static void FocusWindow(WindowBase window)
{ {
if (!TabView) if (!TabView)
{ {
@ -149,23 +99,29 @@ namespace Explorer
} }
} }
private static UIWindow InspectGameObject(GameObject obj) private static WindowBase InspectGameObject(GameObject obj)
{ {
var new_window = UIWindow.CreateWindow<GameObjectWindow>(obj); var new_window = WindowBase.CreateWindow<GameObjectInspector>(obj);
FocusWindow(new_window); FocusWindow(new_window);
return new_window; return new_window;
} }
private static UIWindow InspectReflection(object obj) private static WindowBase InspectReflection(object obj)
{ {
var new_window = UIWindow.CreateWindow<ReflectionWindow>(obj); var new_window = WindowBase.CreateWindow<InstanceInspector>(obj);
FocusWindow(new_window); FocusWindow(new_window);
return new_window; return new_window;
} }
// === Misc Helpers === public static StaticInspector InspectStaticReflection(Type type)
{
var new_window = WindowBase.CreateWindowStatic(type);
FocusWindow(new_window);
return new_window;
}
public static bool IsMouseInWindow public static bool IsMouseInWindow
{ {
@ -222,5 +178,57 @@ namespace Explorer
return rect; return rect;
} }
// ============= instance methods ===============
public void Update()
{
if (m_windowsToDestroy.Count > 0)
{
foreach (var window in m_windowsToDestroy)
{
if (Windows.Contains(window))
{
Windows.Remove(window);
}
}
m_windowsToDestroy.Clear();
}
if (TabView)
{
TabViewWindow.Instance.Update();
}
else
{
for (int i = 0; i < Windows.Count; i++)
{
var window = Windows[i];
if (window != null)
{
window.Update();
}
}
}
}
public void OnGUI()
{
if (TabView)
{
if (Windows.Count > 0)
{
TabViewWindow.Instance.OnGUI();
}
}
else
{
foreach (var window in Windows)
{
window.OnGUI();
}
}
}
} }
} }

View File

@ -4,14 +4,52 @@ using System.Reflection;
using UnityEngine; using UnityEngine;
#if CPP #if CPP
using Explorer.UnstripInternals; using Explorer.UnstripInternals;
using UnityEngineInternal;
using UnhollowerRuntimeLib;
#endif #endif
namespace Explorer namespace Explorer
{ {
public class GUIUnstrip public class GUIUnstrip
{ {
public static void BeginHorizontal(params GUILayoutOption[] options)
=> BeginHorizontal(GUIContent.none, GUIStyle.none, options);
public static void BeginHorizontal(GUIStyle style, params GUILayoutOption[] options)
=> BeginHorizontal(GUIContent.none, style, options);
public static void BeginHorizontal(GUIContent content, GUIStyle style, params GUILayoutOption[] options)
{
#if CPP
Internal.BeginLayoutDirection(false, content, style, options);
#else
GUILayout.BeginHorizontal(content, style, options);
#endif
}
public static void BeginVertical(params GUILayoutOption[] options)
=> BeginVertical(GUIContent.none, GUIStyle.none, options);
public static void BeginVertical(GUIStyle style, params GUILayoutOption[] options)
=> BeginVertical(GUIContent.none, style, options);
public static void BeginVertical(GUIContent content, GUIStyle style, params GUILayoutOption[] options)
{
#if CPP
Internal.BeginLayoutDirection(true, content, style, options);
#else
GUILayout.BeginVertical(content, style, options);
#endif
}
public static Rect GetLastRect()
{
#if CPP
return Internal_LayoutUtility.GetLastRect();
#else
return GUILayoutUtility.GetLastRect();
#endif
}
public static string TextField(string text, GUILayoutOption[] options) public static string TextField(string text, GUILayoutOption[] options)
{ {
#if CPP #if CPP

View File

@ -107,6 +107,14 @@ namespace Explorer.UnstripInternals
#region GUILayout Methods #region GUILayout Methods
public static void BeginLayoutDirection(bool vertical, GUIContent content, GUIStyle style, GUILayoutOption[] options)
{
var g = GUILayoutUtility.BeginLayoutGroup(style, options, Il2CppType.Of<GUILayoutGroup>());
g.isVertical = vertical;
if (style != GUIStyle.none || content != GUIContent.none)
GUI.Box(g.rect, content, style);
}
public static string TextField(string text, GUILayoutOption[] options) public static string TextField(string text, GUILayoutOption[] options)
{ {
text = text ?? ""; text = text ?? "";

View File

@ -1,8 +1,7 @@
#if CPP #if CPP
using UnityEngine; using UnityEngine;
using Explorer.UnstripInternals;
namespace Explorer namespace Explorer.UnstripInternals
{ {
public class Internal_LayoutUtility public class Internal_LayoutUtility
{ {

View File

@ -3,7 +3,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using UnityEngine; using UnityEngine;
namespace Explorer namespace Explorer.UnstripInternals
{ {
public class Internal_ScrollViewState public class Internal_ScrollViewState
{ {

View File

@ -5,7 +5,7 @@ using UnityEngine;
using Explorer.UnstripInternals; using Explorer.UnstripInternals;
using Il2CppSystem.Reflection; using Il2CppSystem.Reflection;
namespace Explorer namespace Explorer.UnstripInternals
{ {
public struct Internal_SliderHandler public struct Internal_SliderHandler
{ {

View File

@ -3,7 +3,7 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
namespace Explorer namespace Explorer.UnstripInternals
{ {
public class Internal_SliderState public class Internal_SliderState
{ {