mirror of
https://github.com/sinai-dev/UnityExplorer.git
synced 2025-06-24 01:12:41 +08:00
Compare commits
22 Commits
Author | SHA1 | Date | |
---|---|---|---|
a927b5ed21 | |||
642c97812c | |||
e43d3579de | |||
6ea435deee | |||
94f749342d | |||
0769b7ef23 | |||
5086dcc82b | |||
56abd38e92 | |||
a7e6ae87df | |||
b5c584bb02 | |||
c8a3aecdf4 | |||
33c2378f41 | |||
38aafa7e5b | |||
4bb0811b2c | |||
4aefe1c5a3 | |||
c228d29707 | |||
56d1507aff | |||
72d31eaa64 | |||
4e8b84b67e | |||
5b94e31a12 | |||
692a37635e | |||
9cb1cea025 |
1
.gitignore
vendored
1
.gitignore
vendored
@ -9,6 +9,7 @@
|
|||||||
*.user
|
*.user
|
||||||
*.userosscache
|
*.userosscache
|
||||||
*.sln.docstates
|
*.sln.docstates
|
||||||
|
*/mcs-unity*
|
||||||
|
|
||||||
# User-specific files (MonoDevelop/Xamarin Studio)
|
# User-specific files (MonoDevelop/Xamarin Studio)
|
||||||
*.userprefs
|
*.userprefs
|
||||||
|
46
README.md
46
README.md
@ -1,4 +1,4 @@
|
|||||||
# CppExplorer []()
|
# CppExplorer [](https://github.com/HerpDerpinstine/MelonLoader)
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<img align="center" src="https://sinai-dev.github.io/images/thumbs/02.png">
|
<img align="center" src="https://sinai-dev.github.io/images/thumbs/02.png">
|
||||||
@ -16,16 +16,9 @@
|
|||||||
|
|
||||||
### Known issues
|
### Known issues
|
||||||
* CppExplorer may experience a `MissingMethodException` when trying to use certain UnityEngine methods. If you experience this, please open an issue and I will do my best to fix it.
|
* CppExplorer may experience a `MissingMethodException` when trying to use certain UnityEngine methods. If you experience this, please open an issue and I will do my best to fix it.
|
||||||
|
* Reflection may fail with certain types (eg. `Nullable<T>`, some Dictionary types, etc). Please see [Il2CppAssemblyUnhollower](https://github.com/knah/Il2CppAssemblyUnhollower#known-issues) for more details.
|
||||||
* Scrolling with mouse wheel in the CppExplorer menu may not work on all games at the moment.
|
* Scrolling with mouse wheel in the CppExplorer menu may not work on all games at the moment.
|
||||||
|
|
||||||
## Features
|
|
||||||
* Scene hierarchy explorer
|
|
||||||
* Search loaded assets with filters
|
|
||||||
* Traverse and manipulate GameObjects
|
|
||||||
* Generic Reflection inspector
|
|
||||||
* C# REPL Console
|
|
||||||
* Inspect-under-mouse
|
|
||||||
|
|
||||||
## How to install
|
## How to install
|
||||||
|
|
||||||
Requires [MelonLoader](https://github.com/HerpDerpinstine/MelonLoader) to be installed for your game.
|
Requires [MelonLoader](https://github.com/HerpDerpinstine/MelonLoader) to be installed for your game.
|
||||||
@ -39,13 +32,23 @@ Requires [MelonLoader](https://github.com/HerpDerpinstine/MelonLoader) to be ins
|
|||||||
* Press F7 to show or hide the menu.
|
* Press F7 to show or hide the menu.
|
||||||
* Simply browse through the scene, search for objects, etc, most of it is pretty self-explanatory.
|
* Simply browse through the scene, search for objects, etc, most of it is pretty self-explanatory.
|
||||||
|
|
||||||
|
### Features
|
||||||
|
[](overview.png)
|
||||||
|
|
||||||
|
<i>An overview of the different CppExplorer menus.</i>
|
||||||
|
|
||||||
|
* Scene hierarchy explorer
|
||||||
|
* Search loaded assets with filters
|
||||||
|
* Traverse and manipulate GameObjects
|
||||||
|
* Generic Reflection inspector
|
||||||
|
* C# REPL Console
|
||||||
|
* Inspect-under-mouse
|
||||||
|
|
||||||
### Scene Explorer
|
### Scene Explorer
|
||||||
|
|
||||||
* A simple menu which allows you to traverse the Transform heirarchy of the scene.
|
* A simple menu which allows you to traverse the Transform heirarchy of the scene.
|
||||||
* Click on a GameObject to set it as the current path, or <b>Inspect</b> it to send it to an Inspector Window.
|
* Click on a GameObject to set it as the current path, or <b>Inspect</b> it to send it to an Inspector Window.
|
||||||
|
|
||||||
[](https://i.imgur.com/BzTOCvp.png)
|
|
||||||
|
|
||||||
### Inspectors
|
### Inspectors
|
||||||
|
|
||||||
CppExplorer has two main inspector modes: <b>GameObject Inspector</b>, and <b>Reflection Inspector</b>.
|
CppExplorer has two main inspector modes: <b>GameObject Inspector</b>, and <b>Reflection Inspector</b>.
|
||||||
@ -59,30 +62,22 @@ CppExplorer has two main inspector modes: <b>GameObject Inspector</b>, and <b>Re
|
|||||||
* Allows you to see the children and components on a GameObject.
|
* 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.
|
* Can use some basic GameObject Controls such as translating and rotating the object, destroy it, clone it, etc.
|
||||||
|
|
||||||
[](https://i.imgur.com/DiDvl0Q.png)
|
|
||||||
|
|
||||||
### Reflection Inspector
|
### Reflection Inspector
|
||||||
|
|
||||||
* The Reflection Inspector is used for all other supported objects.
|
* 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.
|
* 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.
|
* Can search and filter members for the ones you are interested in.
|
||||||
|
|
||||||
[](https://i.imgur.com/Pq127XD.png)
|
|
||||||
|
|
||||||
### Object Search
|
### Object Search
|
||||||
|
|
||||||
* You can search for an `UnityEngine.Object` with the Object Search feature.
|
* You can search for an `UnityEngine.Object` with the Object Search feature.
|
||||||
* Filter by name, type, etc.
|
* Filter by name, type, etc.
|
||||||
* For GameObjects and Transforms you can filter which scene they are found in too.
|
* For GameObjects and Transforms you can filter which scene they are found in too.
|
||||||
|
|
||||||
[](https://i.imgur.com/lK2RthM.png)
|
|
||||||
|
|
||||||
### C# REPL console
|
### C# REPL console
|
||||||
|
|
||||||
* A simple C# REPL console, allows you to execute a method body on the fly.
|
* A simple C# REPL console, allows you to execute a method body on the fly.
|
||||||
|
|
||||||
[](https://i.imgur.com/5U4D1a8.png)
|
|
||||||
|
|
||||||
### Inspect-under-mouse
|
### Inspect-under-mouse
|
||||||
|
|
||||||
* Press Shift+RMB (Right Mouse Button) while the CppExplorer menu is open to begin Inspect-Under-Mouse.
|
* Press Shift+RMB (Right Mouse Button) while the CppExplorer menu is open to begin Inspect-Under-Mouse.
|
||||||
@ -97,10 +92,21 @@ CppExplorer can force the mouse to be visible and unlocked when the menu is open
|
|||||||
* For Hellpoint, use [HPExplorerMouseControl](https://github.com/sinai-dev/Hellpoint-Mods/tree/master/HPExplorerMouseControl/HPExplorerMouseControl)
|
* For Hellpoint, use [HPExplorerMouseControl](https://github.com/sinai-dev/Hellpoint-Mods/tree/master/HPExplorerMouseControl/HPExplorerMouseControl)
|
||||||
* You can create your own plugin using one of the two plugins above as an example. Usually only a few simple Harmony patches are needed to fix the problem.
|
* You can create your own plugin using one of the two plugins above as an example. Usually only a few simple Harmony patches are needed to fix the problem.
|
||||||
|
|
||||||
|
## Building
|
||||||
|
|
||||||
|
If you'd like to build this yourself, everything you need (other than MelonLoader) is included with this repository, there is no need for recursive cloning etc.
|
||||||
|
|
||||||
|
1. Install MelonLoader for your game.
|
||||||
|
2. Open the `src\CppExplorer.csproj` file in a text editor.
|
||||||
|
3. Scroll down until you see the `<ItemGroup>` containing the References.
|
||||||
|
4. Fix all of the paths in the `..\Steam\` directory for your game (use the full path if you need to).
|
||||||
|
5. Open the `src\CppExplorer.sln` project and build it.
|
||||||
|
6. The dll is built to the `Release\` folder in the root of the repository.
|
||||||
|
|
||||||
## Credits
|
## Credits
|
||||||
|
|
||||||
Written by Sinai.
|
Written by Sinai.
|
||||||
|
|
||||||
Thanks to:
|
Thanks to:
|
||||||
* [ManlyMarco](https://github.com/ManlyMarco) for their [Runtime Unity Editor](https://github.com/ManlyMarco/RuntimeUnityEditor), which I used for the REPL Console and the "Find instances" snippet, and the UI style.
|
* [ManlyMarco](https://github.com/ManlyMarco) for their [Runtime Unity Editor](https://github.com/ManlyMarco/RuntimeUnityEditor), which I used for the REPL Console and the "Find instances" snippet, and the UI style.
|
||||||
* [denikson](https://github.com/denikson) for [mcs-unity](https://github.com/denikson/mcs-unity). I commented out the `SkipVisibilityExt` constructor in `mcs.dll` since it was causing an exception with the Hook it attempted.
|
* [denikson](https://github.com/denikson) for [mcs-unity](https://github.com/denikson/mcs-unity). I commented out the `SkipVisibilityExt` constructor since it was causing an exception with the Hook it attempted.
|
||||||
|
BIN
overview.png
Normal file
BIN
overview.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 523 KiB |
@ -16,14 +16,17 @@ namespace Explorer
|
|||||||
public string ValueTypeName;
|
public string ValueTypeName;
|
||||||
public Type ValueType;
|
public Type ValueType;
|
||||||
|
|
||||||
// Reflection Inspector only
|
|
||||||
public MemberInfo MemInfo { get; set; }
|
public MemberInfo MemInfo { get; set; }
|
||||||
public Type DeclaringType { get; set; }
|
public Type DeclaringType { get; set; }
|
||||||
public object DeclaringInstance { get; set; }
|
public object DeclaringInstance { get; set; }
|
||||||
public string ReflectionException { get; set; }
|
|
||||||
|
|
||||||
public int PropertyIndex { get; private set; }
|
public bool HasParameters => m_arguments != null && m_arguments.Length > 0;
|
||||||
private string m_propertyIndexInput = "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();
|
public string RichTextName => m_richTextName ?? GetRichTextName();
|
||||||
private string m_richTextName;
|
private string m_richTextName;
|
||||||
@ -41,14 +44,10 @@ namespace Explorer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ===== Abstract/Virtual Methods ===== //
|
|
||||||
|
|
||||||
public virtual void Init() { }
|
public virtual void Init() { }
|
||||||
|
|
||||||
public abstract void DrawValue(Rect window, float width);
|
public abstract void DrawValue(Rect window, float width);
|
||||||
|
|
||||||
// ===== Static Methods ===== //
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Get CacheObject from only an object instance
|
/// Get CacheObject from only an object instance
|
||||||
/// Calls GetCacheObject(obj, memberInfo, declaringInstance) with (obj, null, null)</summary>
|
/// Calls GetCacheObject(obj, memberInfo, declaringInstance) with (obj, null, null)</summary>
|
||||||
@ -115,7 +114,21 @@ namespace Explorer
|
|||||||
{
|
{
|
||||||
CacheObjectBase holder;
|
CacheObjectBase holder;
|
||||||
|
|
||||||
if (memberInfo is MethodInfo mi)
|
var pi = memberInfo as PropertyInfo;
|
||||||
|
var mi = memberInfo as MethodInfo;
|
||||||
|
|
||||||
|
// if PropertyInfo, check if can process args
|
||||||
|
if (pi != null && !CanProcessArgs(pi.GetIndexParameters()))
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is pretty ugly, could probably make a cleaner implementation.
|
||||||
|
// However, the only cleaner ways I can think of are slower and probably not worth it.
|
||||||
|
|
||||||
|
// Note: the order is somewhat important.
|
||||||
|
|
||||||
|
if (mi != null)
|
||||||
{
|
{
|
||||||
if (CacheMethod.CanEvaluate(mi))
|
if (CacheMethod.CanEvaluate(mi))
|
||||||
{
|
{
|
||||||
@ -154,14 +167,15 @@ namespace Explorer
|
|||||||
{
|
{
|
||||||
holder = new CacheRect();
|
holder = new CacheRect();
|
||||||
}
|
}
|
||||||
else if (ReflectionHelpers.IsArray(valueType) || ReflectionHelpers.IsList(valueType))
|
// must check this before IsEnumerable
|
||||||
{
|
|
||||||
holder = new CacheList();
|
|
||||||
}
|
|
||||||
else if (ReflectionHelpers.IsDictionary(valueType))
|
else if (ReflectionHelpers.IsDictionary(valueType))
|
||||||
{
|
{
|
||||||
holder = new CacheDictionary();
|
holder = new CacheDictionary();
|
||||||
}
|
}
|
||||||
|
else if (ReflectionHelpers.IsEnumerable(valueType) || ReflectionHelpers.IsCppList(valueType))
|
||||||
|
{
|
||||||
|
holder = new CacheList();
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
holder = new CacheOther();
|
holder = new CacheOther();
|
||||||
@ -176,24 +190,102 @@ namespace Explorer
|
|||||||
holder.MemInfo = memberInfo;
|
holder.MemInfo = memberInfo;
|
||||||
holder.DeclaringType = memberInfo.DeclaringType;
|
holder.DeclaringType = memberInfo.DeclaringType;
|
||||||
holder.DeclaringInstance = declaringInstance;
|
holder.DeclaringInstance = declaringInstance;
|
||||||
|
|
||||||
holder.UpdateValue();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (pi != null)
|
||||||
|
{
|
||||||
|
holder.m_arguments = pi.GetIndexParameters();
|
||||||
|
}
|
||||||
|
else if (mi != null)
|
||||||
|
{
|
||||||
|
holder.m_arguments = mi.GetParameters();
|
||||||
|
}
|
||||||
|
|
||||||
|
holder.m_argumentInput = new string[holder.m_arguments.Length];
|
||||||
|
|
||||||
|
holder.UpdateValue();
|
||||||
|
|
||||||
holder.Init();
|
holder.Init();
|
||||||
|
|
||||||
return holder;
|
return holder;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ======== Instance Methods =========
|
public static bool CanProcessArgs(ParameterInfo[] parameters)
|
||||||
|
{
|
||||||
|
foreach (var param in parameters)
|
||||||
|
{
|
||||||
|
if (!param.ParameterType.IsPrimitive && param.ParameterType != typeof(string))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float CalcWhitespace(Rect window)
|
||||||
|
{
|
||||||
|
if (!(this is IExpandHeight)) return 0f;
|
||||||
|
|
||||||
|
float whitespace = (this as IExpandHeight).WhiteSpace;
|
||||||
|
if (whitespace > 0)
|
||||||
|
{
|
||||||
|
ClampLabelWidth(window, ref whitespace);
|
||||||
|
}
|
||||||
|
|
||||||
|
return whitespace;
|
||||||
|
}
|
||||||
|
|
||||||
|
public object[] ParseArguments()
|
||||||
|
{
|
||||||
|
var parsedArgs = new List<object>();
|
||||||
|
for (int i = 0; i < m_arguments.Length; i++)
|
||||||
|
{
|
||||||
|
var input = m_argumentInput[i];
|
||||||
|
var type = m_arguments[i].ParameterType;
|
||||||
|
|
||||||
|
if (type == typeof(string))
|
||||||
|
{
|
||||||
|
parsedArgs.Add(input);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
parsedArgs.Add(type.GetMethod("Parse", new Type[] { typeof(string) })
|
||||||
|
.Invoke(null, new object[] { input }));
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
if (m_arguments[i].HasDefaultValue)
|
||||||
|
{
|
||||||
|
parsedArgs.Add(m_arguments[i].DefaultValue);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Try add a null arg I guess
|
||||||
|
parsedArgs.Add(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return parsedArgs.ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
public virtual void UpdateValue()
|
public virtual void UpdateValue()
|
||||||
{
|
{
|
||||||
if (MemInfo == null || !string.IsNullOrEmpty(ReflectionException))
|
if (MemInfo == null)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (HasParameters && !m_isEvaluating)
|
||||||
|
{
|
||||||
|
// Need to enter parameters first
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (MemInfo.MemberType == MemberTypes.Field)
|
if (MemInfo.MemberType == MemberTypes.Field)
|
||||||
@ -204,13 +296,11 @@ namespace Explorer
|
|||||||
else if (MemInfo.MemberType == MemberTypes.Property)
|
else if (MemInfo.MemberType == MemberTypes.Property)
|
||||||
{
|
{
|
||||||
var pi = MemInfo as PropertyInfo;
|
var pi = MemInfo as PropertyInfo;
|
||||||
bool isStatic = pi.GetAccessors()[0].IsStatic;
|
var target = pi.GetAccessors()[0].IsStatic ? null : DeclaringInstance;
|
||||||
var target = isStatic ? null : DeclaringInstance;
|
|
||||||
|
|
||||||
if (pi.GetIndexParameters().Length > 0)
|
if (HasParameters)
|
||||||
{
|
{
|
||||||
var indexes = new object[] { PropertyIndex };
|
Value = pi.GetValue(target, ParseArguments());
|
||||||
Value = pi.GetValue(target, indexes);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -219,6 +309,8 @@ namespace Explorer
|
|||||||
}
|
}
|
||||||
|
|
||||||
ReflectionException = null;
|
ReflectionException = null;
|
||||||
|
m_evaluated = true;
|
||||||
|
m_isEvaluating = false;
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
@ -239,10 +331,9 @@ namespace Explorer
|
|||||||
{
|
{
|
||||||
var pi = MemInfo as PropertyInfo;
|
var pi = MemInfo as PropertyInfo;
|
||||||
|
|
||||||
if (pi.GetIndexParameters().Length > 0)
|
if (HasParameters)
|
||||||
{
|
{
|
||||||
var indexes = new object[] { PropertyIndex };
|
pi.SetValue(pi.GetAccessors()[0].IsStatic ? null : DeclaringInstance, Value, ParseArguments());
|
||||||
pi.SetValue(pi.GetAccessors()[0].IsStatic ? null : DeclaringInstance, Value, indexes);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -256,9 +347,10 @@ namespace Explorer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ========= Instance Gui Draw ==========
|
// ========= Gui Draw ==========
|
||||||
|
|
||||||
public const float MAX_LABEL_WIDTH = 400f;
|
public const float MAX_LABEL_WIDTH = 400f;
|
||||||
|
public const string EVALUATE_LABEL = "<color=lime>Evaluate</color>";
|
||||||
|
|
||||||
public static void ClampLabelWidth(Rect window, ref float labelWidth)
|
public static void ClampLabelWidth(Rect window, ref float labelWidth)
|
||||||
{
|
{
|
||||||
@ -277,53 +369,108 @@ namespace Explorer
|
|||||||
|
|
||||||
if (MemInfo != null)
|
if (MemInfo != null)
|
||||||
{
|
{
|
||||||
var name = RichTextName;
|
GUILayout.Label(RichTextName, new GUILayoutOption[] { GUILayout.Width(labelWidth) });
|
||||||
if (MemInfo is PropertyInfo pi && pi.GetIndexParameters().Length > 0)
|
|
||||||
{
|
|
||||||
name += $"[{PropertyIndex}]";
|
|
||||||
}
|
|
||||||
|
|
||||||
GUILayout.Label(name, new GUILayoutOption[] { GUILayout.Width(labelWidth) });
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
GUILayout.Space(labelWidth);
|
GUILayout.Space(labelWidth);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var cm = this as CacheMethod;
|
||||||
|
|
||||||
|
if (HasParameters)
|
||||||
|
{
|
||||||
|
GUILayout.BeginVertical(null);
|
||||||
|
|
||||||
|
if (m_isEvaluating)
|
||||||
|
{
|
||||||
|
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=#2df7b2>" + type + "</color> <color=#a6e9e9>" + name + "</color>";
|
||||||
|
if (m_arguments[i].HasDefaultValue)
|
||||||
|
{
|
||||||
|
label = $"<i>[{label} = {m_arguments[i].DefaultValue}]</i>";
|
||||||
|
}
|
||||||
|
|
||||||
|
GUILayout.BeginHorizontal(null);
|
||||||
|
|
||||||
|
GUILayout.Label(i.ToString(), new GUILayoutOption[] { GUILayout.Width(20) });
|
||||||
|
m_argumentInput[i] = GUILayout.TextField(input, new GUILayoutOption[] { GUILayout.Width(150) });
|
||||||
|
GUILayout.Label(label, null);
|
||||||
|
|
||||||
|
GUILayout.EndHorizontal();
|
||||||
|
}
|
||||||
|
|
||||||
|
GUILayout.BeginHorizontal(null);
|
||||||
|
if (cm != null)
|
||||||
|
{
|
||||||
|
if (GUILayout.Button(EVALUATE_LABEL, new GUILayoutOption[] { GUILayout.Width(70) }))
|
||||||
|
{
|
||||||
|
cm.Evaluate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (GUILayout.Button(EVALUATE_LABEL, new GUILayoutOption[] { GUILayout.Width(70) }))
|
||||||
|
{
|
||||||
|
UpdateValue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (GUILayout.Button("Cancel", new GUILayoutOption[] { GUILayout.Width(70) }))
|
||||||
|
{
|
||||||
|
m_isEvaluating = false;
|
||||||
|
}
|
||||||
|
GUILayout.EndHorizontal();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (GUILayout.Button($"Evaluate ({m_arguments.Length} params)", new GUILayoutOption[] { GUILayout.Width(150) }))
|
||||||
|
{
|
||||||
|
m_isEvaluating = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
GUILayout.EndVertical();
|
||||||
|
|
||||||
|
// new line and space
|
||||||
|
GUILayout.EndHorizontal();
|
||||||
|
GUILayout.BeginHorizontal(null);
|
||||||
|
GUILayout.Space(labelWidth);
|
||||||
|
}
|
||||||
|
else if (cm != null)
|
||||||
|
{
|
||||||
|
//GUILayout.BeginHorizontal(null);
|
||||||
|
|
||||||
|
if (GUILayout.Button(EVALUATE_LABEL, new GUILayoutOption[] { GUILayout.Width(70) }))
|
||||||
|
{
|
||||||
|
cm.Evaluate();
|
||||||
|
}
|
||||||
|
|
||||||
|
// new line and space
|
||||||
|
GUILayout.EndHorizontal();
|
||||||
|
GUILayout.BeginHorizontal(null);
|
||||||
|
GUILayout.Space(labelWidth);
|
||||||
|
}
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(ReflectionException))
|
if (!string.IsNullOrEmpty(ReflectionException))
|
||||||
{
|
{
|
||||||
GUILayout.Label("<color=red>Reflection failed!</color> (" + ReflectionException + ")", null);
|
GUILayout.Label("<color=red>Reflection failed!</color> (" + ReflectionException + ")", null);
|
||||||
}
|
}
|
||||||
else if (Value == null && MemInfo?.MemberType != MemberTypes.Method)
|
else if ((HasParameters || this is CacheMethod) && !m_evaluated)
|
||||||
|
{
|
||||||
|
GUILayout.Label($"<color=grey><i>Not yet evaluated</i></color> (<color=#2df7b2>{ValueTypeName}</color>)", null);
|
||||||
|
}
|
||||||
|
else if (Value == null && !(this is CacheMethod))
|
||||||
{
|
{
|
||||||
GUILayout.Label("<i>null (" + ValueTypeName + ")</i>", null);
|
GUILayout.Label("<i>null (" + ValueTypeName + ")</i>", null);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (MemInfo is PropertyInfo pi && pi.GetIndexParameters().Length > 0)
|
|
||||||
{
|
|
||||||
GUILayout.Label("index:", new GUILayoutOption[] { GUILayout.Width(50) });
|
|
||||||
|
|
||||||
m_propertyIndexInput = GUILayout.TextField(m_propertyIndexInput, new GUILayoutOption[] { GUILayout.Width(100) });
|
|
||||||
if (GUILayout.Button("Set", new GUILayoutOption[] { GUILayout.Width(60) }))
|
|
||||||
{
|
|
||||||
if (int.TryParse(m_propertyIndexInput, out int i))
|
|
||||||
{
|
|
||||||
PropertyIndex = i;
|
|
||||||
UpdateValue();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
MelonLogger.Log($"Could not parse '{m_propertyIndexInput}' to an int!");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// new line and space
|
|
||||||
GUILayout.EndHorizontal();
|
|
||||||
GUILayout.BeginHorizontal(null);
|
|
||||||
GUILayout.Space(labelWidth);
|
|
||||||
}
|
|
||||||
|
|
||||||
DrawValue(window, window.width - labelWidth - 90);
|
DrawValue(window, window.width - labelWidth - 90);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -343,15 +490,15 @@ namespace Explorer
|
|||||||
|
|
||||||
m_richTextName = $"<color=#2df7b2>{MemInfo.DeclaringType.Name}</color>.<color={memberColor}>{MemInfo.Name}</color>";
|
m_richTextName = $"<color=#2df7b2>{MemInfo.DeclaringType.Name}</color>.<color={memberColor}>{MemInfo.Name}</color>";
|
||||||
|
|
||||||
if (MemInfo is MethodInfo mi)
|
if (m_arguments.Length > 0 || this is CacheMethod)
|
||||||
{
|
{
|
||||||
m_richTextName += "(";
|
m_richTextName += "(";
|
||||||
var _params = "";
|
var _params = "";
|
||||||
foreach (var param in mi.GetParameters())
|
foreach (var param in m_arguments)
|
||||||
{
|
{
|
||||||
if (_params != "") _params += ", ";
|
if (_params != "") _params += ", ";
|
||||||
|
|
||||||
_params += $"<color=#a6e9e9>{param.Name}</color>";
|
_params += $"<color=#2df7b2>{param.ParameterType.Name}</color> <color=#a6e9e9>{param.Name}</color>";
|
||||||
}
|
}
|
||||||
m_richTextName += _params;
|
m_richTextName += _params;
|
||||||
m_richTextName += ")";
|
m_richTextName += ")";
|
||||||
|
14
src/CachedObjects/IExpandHeight.cs
Normal file
14
src/CachedObjects/IExpandHeight.cs
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Explorer
|
||||||
|
{
|
||||||
|
interface IExpandHeight
|
||||||
|
{
|
||||||
|
bool IsExpanded { get; set; }
|
||||||
|
float WhiteSpace { get; set; }
|
||||||
|
}
|
||||||
|
}
|
@ -1,34 +1,303 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Collections;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using MelonLoader;
|
using MelonLoader;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
using System.Reflection;
|
||||||
|
using UnhollowerBaseLib;
|
||||||
|
|
||||||
namespace Explorer
|
namespace Explorer
|
||||||
{
|
{
|
||||||
public class CacheDictionary : CacheObjectBase
|
public class CacheDictionary : CacheObjectBase, IExpandHeight
|
||||||
{
|
{
|
||||||
|
public bool IsExpanded { get; set; }
|
||||||
|
public float WhiteSpace { get; set; } = 215f;
|
||||||
|
|
||||||
|
public PageHelper Pages = new PageHelper();
|
||||||
|
|
||||||
public override void Init()
|
private CacheObjectBase[] m_cachedKeys;
|
||||||
|
private CacheObjectBase[] m_cachedValues;
|
||||||
|
|
||||||
|
public Type TypeOfKeys
|
||||||
{
|
{
|
||||||
//base.Init();
|
get
|
||||||
|
{
|
||||||
|
if (m_keysType == null) GetGenericArguments();
|
||||||
|
return m_keysType;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private Type m_keysType;
|
||||||
|
|
||||||
Value = "Unsupported";
|
public Type TypeOfValues
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (m_valuesType == null) GetGenericArguments();
|
||||||
|
return m_valuesType;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private Type m_valuesType;
|
||||||
|
|
||||||
|
public IDictionary IDict
|
||||||
|
{
|
||||||
|
get => m_iDictionary ?? (m_iDictionary = Value as IDictionary) ?? Il2CppDictionaryToMono();
|
||||||
|
set => m_iDictionary = value;
|
||||||
|
}
|
||||||
|
private IDictionary m_iDictionary;
|
||||||
|
|
||||||
|
// ========== Methods ==========
|
||||||
|
|
||||||
|
// This is a bit janky due to Il2Cpp Dictionary not implementing IDictionary.
|
||||||
|
private IDictionary Il2CppDictionaryToMono()
|
||||||
|
{
|
||||||
|
// note: "ValueType" is the Dictionary itself, TypeOfValues is the 'Dictionary.Values' type.
|
||||||
|
|
||||||
|
// get keys and values
|
||||||
|
var keys = ValueType.GetProperty("Keys") .GetValue(Value);
|
||||||
|
var values = ValueType.GetProperty("Values").GetValue(Value);
|
||||||
|
|
||||||
|
// create lists to hold them
|
||||||
|
var keyList = new List<object>();
|
||||||
|
var valueList = new List<object>();
|
||||||
|
|
||||||
|
// store entries with reflection
|
||||||
|
EnumerateWithReflection(keys, keyList);
|
||||||
|
EnumerateWithReflection(values, valueList);
|
||||||
|
|
||||||
|
// make actual mono dictionary
|
||||||
|
var dict = (IDictionary)Activator.CreateInstance(typeof(Dictionary<,>)
|
||||||
|
.MakeGenericType(TypeOfKeys, TypeOfValues));
|
||||||
|
|
||||||
|
// finally iterate into dictionary
|
||||||
|
for (int i = 0; i < keyList.Count; i++)
|
||||||
|
{
|
||||||
|
dict.Add(keyList[i], valueList[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return dict;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void EnumerateWithReflection(object collection, List<object> list)
|
||||||
|
{
|
||||||
|
// invoke GetEnumerator
|
||||||
|
var enumerator = collection.GetType().GetMethod("GetEnumerator").Invoke(collection, null);
|
||||||
|
// get the type of it
|
||||||
|
var enumeratorType = enumerator.GetType();
|
||||||
|
// reflect MoveNext and Current
|
||||||
|
var moveNext = enumeratorType.GetMethod("MoveNext");
|
||||||
|
var current = enumeratorType.GetProperty("Current");
|
||||||
|
// iterate
|
||||||
|
while ((bool)moveNext.Invoke(enumerator, null))
|
||||||
|
{
|
||||||
|
list.Add(current.GetValue(enumerator));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void GetGenericArguments()
|
||||||
|
{
|
||||||
|
if (this.MemInfo != null)
|
||||||
|
{
|
||||||
|
Type memberType = null;
|
||||||
|
switch (this.MemInfo.MemberType)
|
||||||
|
{
|
||||||
|
case MemberTypes.Field:
|
||||||
|
memberType = (MemInfo as FieldInfo).FieldType;
|
||||||
|
break;
|
||||||
|
case MemberTypes.Property:
|
||||||
|
memberType = (MemInfo as PropertyInfo).PropertyType;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (memberType != null && memberType.IsGenericType)
|
||||||
|
{
|
||||||
|
m_keysType = memberType.GetGenericArguments()[0];
|
||||||
|
m_valuesType = memberType.GetGenericArguments()[1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (Value != null)
|
||||||
|
{
|
||||||
|
var type = Value.GetType();
|
||||||
|
if (type.IsGenericType)
|
||||||
|
{
|
||||||
|
m_keysType = type.GetGenericArguments()[0];
|
||||||
|
m_valuesType = type.GetGenericArguments()[1];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
MelonLogger.Log("TODO? Dictionary is of type: " + Value.GetType().FullName);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void UpdateValue()
|
public override void UpdateValue()
|
||||||
{
|
{
|
||||||
//base.UpdateValue();
|
// first make sure we won't run into a TypeInitializationException.
|
||||||
|
if (!EnsureDictionaryIsSupported())
|
||||||
|
{
|
||||||
|
ReflectionException = "Dictionary Type not supported with Reflection!";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
base.UpdateValue();
|
||||||
|
|
||||||
|
// reset
|
||||||
|
IDict = null;
|
||||||
|
|
||||||
|
if (Value == null || IDict == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var keys = new List<CacheObjectBase>();
|
||||||
|
foreach (var key in IDict.Keys)
|
||||||
|
{
|
||||||
|
var cache = GetCacheObject(key, TypeOfKeys);
|
||||||
|
keys.Add(cache);
|
||||||
|
}
|
||||||
|
|
||||||
|
var values = new List<CacheObjectBase>();
|
||||||
|
foreach (var val in IDict.Values)
|
||||||
|
{
|
||||||
|
var cache = GetCacheObject(val, TypeOfValues);
|
||||||
|
values.Add(cache);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_cachedKeys = keys.ToArray();
|
||||||
|
m_cachedValues = values.ToArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private bool EnsureDictionaryIsSupported()
|
||||||
|
{
|
||||||
|
if (typeof(IDictionary).IsAssignableFrom(ValueType))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return Check(TypeOfKeys) && Check(TypeOfValues);
|
||||||
|
|
||||||
|
bool Check(Type type)
|
||||||
|
{
|
||||||
|
var ptr = (IntPtr)typeof(Il2CppClassPointerStore<>)
|
||||||
|
.MakeGenericType(type)
|
||||||
|
.GetField("NativeClassPtr")
|
||||||
|
.GetValue(null);
|
||||||
|
|
||||||
|
return Il2CppSystem.Type.internal_from_handle(IL2CPP.il2cpp_class_get_type(ptr)) is Il2CppSystem.Type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============= GUI Draw =============
|
||||||
|
|
||||||
public override void DrawValue(Rect window, float width)
|
public override void DrawValue(Rect window, float width)
|
||||||
{
|
{
|
||||||
GUILayout.Label("<color=red>Dictionary (unsupported)</color>", null);
|
if (m_cachedKeys == null || m_cachedValues == null)
|
||||||
|
{
|
||||||
|
GUILayout.Label("Cached keys or values is null!", null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var whitespace = CalcWhitespace(window);
|
||||||
|
|
||||||
|
int count = m_cachedKeys.Length;
|
||||||
|
|
||||||
|
if (!IsExpanded)
|
||||||
|
{
|
||||||
|
if (GUILayout.Button("v", new GUILayoutOption[] { GUILayout.Width(25) }))
|
||||||
|
{
|
||||||
|
IsExpanded = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (GUILayout.Button("^", new GUILayoutOption[] { GUILayout.Width(25) }))
|
||||||
|
{
|
||||||
|
IsExpanded = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var negativeWhitespace = window.width - (whitespace + 100f);
|
||||||
|
|
||||||
|
GUI.skin.button.alignment = TextAnchor.MiddleLeft;
|
||||||
|
string btnLabel = $"<color=#2df7b2>[{count}] Dictionary<{TypeOfKeys.FullName}, {TypeOfValues.FullName}></color>";
|
||||||
|
if (GUILayout.Button(btnLabel, new GUILayoutOption[] { GUILayout.Width(negativeWhitespace) }))
|
||||||
|
{
|
||||||
|
WindowManager.InspectObject(Value, out bool _);
|
||||||
|
}
|
||||||
|
GUI.skin.button.alignment = TextAnchor.MiddleCenter;
|
||||||
|
|
||||||
|
GUILayout.Space(5);
|
||||||
|
|
||||||
|
if (IsExpanded)
|
||||||
|
{
|
||||||
|
Pages.ItemCount = count;
|
||||||
|
|
||||||
|
if (count > Pages.ItemsPerPage)
|
||||||
|
{
|
||||||
|
GUILayout.EndHorizontal();
|
||||||
|
GUILayout.BeginHorizontal(null);
|
||||||
|
|
||||||
|
GUILayout.Space(whitespace);
|
||||||
|
|
||||||
|
Pages.CurrentPageLabel();
|
||||||
|
|
||||||
|
// prev/next page buttons
|
||||||
|
if (GUILayout.Button("< Prev", new GUILayoutOption[] { GUILayout.Width(60) }))
|
||||||
|
{
|
||||||
|
Pages.TurnPage(Turn.Left);
|
||||||
|
}
|
||||||
|
if (GUILayout.Button("Next >", new GUILayoutOption[] { GUILayout.Width(60) }))
|
||||||
|
{
|
||||||
|
Pages.TurnPage(Turn.Right);
|
||||||
|
}
|
||||||
|
|
||||||
|
Pages.DrawLimitInputArea();
|
||||||
|
|
||||||
|
GUILayout.Space(5);
|
||||||
|
}
|
||||||
|
|
||||||
|
int offset = Pages.CalculateOffsetIndex();
|
||||||
|
|
||||||
|
for (int i = offset; i < offset + Pages.ItemsPerPage && i < count; i++)
|
||||||
|
{
|
||||||
|
var key = m_cachedKeys[i];
|
||||||
|
var val = m_cachedValues[i];
|
||||||
|
|
||||||
|
//collapsing the BeginHorizontal called from ReflectionWindow.WindowFunction or previous array entry
|
||||||
|
GUILayout.EndHorizontal();
|
||||||
|
GUILayout.BeginHorizontal(null);
|
||||||
|
|
||||||
|
//GUILayout.Space(whitespace);
|
||||||
|
|
||||||
|
if (key == null || val == null)
|
||||||
|
{
|
||||||
|
GUILayout.Label($"[{i}] <i><color=grey>(null)</color></i>", null);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
GUI.skin.label.alignment = TextAnchor.MiddleCenter;
|
||||||
|
GUILayout.Label($"[{i}]", new GUILayoutOption[] { GUILayout.Width(30) });
|
||||||
|
|
||||||
|
GUILayout.Label("Key:", new GUILayoutOption[] { GUILayout.Width(40) });
|
||||||
|
key.DrawValue(window, (window.width / 2) - 30f);
|
||||||
|
|
||||||
|
GUILayout.Label("Value:", new GUILayoutOption[] { GUILayout.Width(40) });
|
||||||
|
val.DrawValue(window, (window.width / 2) - 30f);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
GUI.skin.label.alignment = TextAnchor.UpperLeft;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,13 +8,12 @@ using UnityEngine;
|
|||||||
|
|
||||||
namespace Explorer
|
namespace Explorer
|
||||||
{
|
{
|
||||||
public class CacheList : CacheObjectBase
|
public class CacheList : CacheObjectBase, IExpandHeight
|
||||||
{
|
{
|
||||||
public bool IsExpanded { get; set; }
|
public bool IsExpanded { get; set; }
|
||||||
public PageHelper Pages = new PageHelper();
|
public float WhiteSpace { get; set; } = 215f;
|
||||||
|
|
||||||
public float WhiteSpace = 215f;
|
public PageHelper Pages = new PageHelper();
|
||||||
public float ButtonWidthOffset = 290f;
|
|
||||||
|
|
||||||
private CacheObjectBase[] m_cachedEntries;
|
private CacheObjectBase[] m_cachedEntries;
|
||||||
|
|
||||||
@ -52,6 +51,7 @@ namespace Explorer
|
|||||||
{
|
{
|
||||||
get => GetItemProperty();
|
get => GetItemProperty();
|
||||||
}
|
}
|
||||||
|
|
||||||
private PropertyInfo m_itemProperty;
|
private PropertyInfo m_itemProperty;
|
||||||
|
|
||||||
// ========== Methods ==========
|
// ========== Methods ==========
|
||||||
@ -244,6 +244,8 @@ namespace Explorer
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var whitespace = CalcWhitespace(window);
|
||||||
|
|
||||||
int count = m_cachedEntries.Length;
|
int count = m_cachedEntries.Length;
|
||||||
|
|
||||||
if (!IsExpanded)
|
if (!IsExpanded)
|
||||||
@ -261,9 +263,11 @@ namespace Explorer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var negativeWhitespace = window.width - (whitespace + 100f);
|
||||||
|
|
||||||
GUI.skin.button.alignment = TextAnchor.MiddleLeft;
|
GUI.skin.button.alignment = TextAnchor.MiddleLeft;
|
||||||
string btnLabel = "<color=yellow>[" + count + "] " + EntryType + "</color>";
|
string btnLabel = $"[{count}] <color=#2df7b2>{EntryType.FullName}</color>";
|
||||||
if (GUILayout.Button(btnLabel, new GUILayoutOption[] { GUILayout.MaxWidth(window.width - ButtonWidthOffset) }))
|
if (GUILayout.Button(btnLabel, new GUILayoutOption[] { GUILayout.MaxWidth(negativeWhitespace) }))
|
||||||
{
|
{
|
||||||
WindowManager.InspectObject(Value, out bool _);
|
WindowManager.InspectObject(Value, out bool _);
|
||||||
}
|
}
|
||||||
@ -273,12 +277,6 @@ namespace Explorer
|
|||||||
|
|
||||||
if (IsExpanded)
|
if (IsExpanded)
|
||||||
{
|
{
|
||||||
float whitespace = WhiteSpace;
|
|
||||||
if (whitespace > 0)
|
|
||||||
{
|
|
||||||
ClampLabelWidth(window, ref whitespace);
|
|
||||||
}
|
|
||||||
|
|
||||||
Pages.ItemCount = count;
|
Pages.ItemCount = count;
|
||||||
|
|
||||||
if (count > Pages.ItemsPerPage)
|
if (count > Pages.ItemsPerPage)
|
||||||
@ -305,12 +303,6 @@ namespace Explorer
|
|||||||
GUILayout.Space(5);
|
GUILayout.Space(5);
|
||||||
}
|
}
|
||||||
|
|
||||||
//int offset = ArrayOffset * ArrayLimit;
|
|
||||||
//if (offset >= count)
|
|
||||||
//{
|
|
||||||
// offset = 0;
|
|
||||||
// ArrayOffset = 0;
|
|
||||||
//}
|
|
||||||
int offset = Pages.CalculateOffsetIndex();
|
int offset = Pages.CalculateOffsetIndex();
|
||||||
|
|
||||||
for (int i = offset; i < offset + Pages.ItemsPerPage && i < count; i++)
|
for (int i = offset; i < offset + Pages.ItemsPerPage && i < count; i++)
|
||||||
|
@ -11,147 +11,30 @@ namespace Explorer
|
|||||||
{
|
{
|
||||||
public class CacheMethod : CacheObjectBase
|
public class CacheMethod : CacheObjectBase
|
||||||
{
|
{
|
||||||
private bool m_evaluated = false;
|
|
||||||
private CacheObjectBase m_cachedReturnValue;
|
private CacheObjectBase m_cachedReturnValue;
|
||||||
|
|
||||||
private bool m_isEvaluating;
|
|
||||||
private ParameterInfo[] m_arguments;
|
|
||||||
private string[] m_argumentInput;
|
|
||||||
|
|
||||||
public bool HasParameters
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
if (m_hasParams == null)
|
|
||||||
{
|
|
||||||
m_hasParams = (MemInfo as MethodInfo).GetParameters().Length > 0;
|
|
||||||
}
|
|
||||||
return (bool)m_hasParams;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
private bool? m_hasParams;
|
|
||||||
|
|
||||||
public static bool CanEvaluate(MethodInfo mi)
|
public static bool CanEvaluate(MethodInfo mi)
|
||||||
{
|
{
|
||||||
// generic type args not supported yet
|
// TODO generic args
|
||||||
if (mi.GetGenericArguments().Length > 0)
|
if (mi.GetGenericArguments().Length > 0)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// only primitive and string args supported
|
// primitive and string args supported
|
||||||
foreach (var param in mi.GetParameters())
|
return CanProcessArgs(mi.GetParameters());
|
||||||
{
|
|
||||||
if (!param.ParameterType.IsPrimitive && param.ParameterType != typeof(string))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Init()
|
|
||||||
{
|
|
||||||
base.Init();
|
|
||||||
|
|
||||||
var mi = MemInfo as MethodInfo;
|
|
||||||
|
|
||||||
m_arguments = mi.GetParameters();
|
|
||||||
m_argumentInput = new string[m_arguments.Length];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void UpdateValue()
|
public override void UpdateValue()
|
||||||
{
|
{
|
||||||
//base.UpdateValue();
|
//base.UpdateValue();
|
||||||
}
|
|
||||||
|
|
||||||
public override void DrawValue(Rect window, float width)
|
|
||||||
{
|
|
||||||
GUILayout.BeginVertical(null);
|
|
||||||
|
|
||||||
string evaluateLabel = "<color=lime>Evaluate</color>";
|
|
||||||
if (HasParameters)
|
|
||||||
{
|
|
||||||
if (m_isEvaluating)
|
|
||||||
{
|
|
||||||
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;
|
|
||||||
|
|
||||||
GUILayout.BeginHorizontal(null);
|
|
||||||
m_argumentInput[i] = GUILayout.TextField(input, new GUILayoutOption[] { GUILayout.Width(150) });
|
|
||||||
GUILayout.Label(i + ": <color=cyan>" + name + "</color> <color=yellow>(" + type + ")</color>", null);
|
|
||||||
|
|
||||||
GUILayout.EndHorizontal();
|
|
||||||
}
|
|
||||||
|
|
||||||
GUILayout.BeginHorizontal(null);
|
|
||||||
if (GUILayout.Button(evaluateLabel, new GUILayoutOption[] { GUILayout.Width(70) }))
|
|
||||||
{
|
|
||||||
Evaluate();
|
|
||||||
m_isEvaluating = false;
|
|
||||||
}
|
|
||||||
if (GUILayout.Button("Cancel", new GUILayoutOption[] { GUILayout.Width(70) }))
|
|
||||||
{
|
|
||||||
m_isEvaluating = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
GUILayout.BeginHorizontal(null);
|
|
||||||
if (GUILayout.Button($"Evaluate ({m_arguments.Length} params)", new GUILayoutOption[] { GUILayout.Width(150) }))
|
|
||||||
{
|
|
||||||
m_isEvaluating = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
GUILayout.BeginHorizontal(null);
|
|
||||||
if (GUILayout.Button(evaluateLabel, new GUILayoutOption[] { GUILayout.Width(70) }))
|
|
||||||
{
|
|
||||||
Evaluate();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
GUILayout.EndHorizontal();
|
|
||||||
|
|
||||||
GUILayout.BeginHorizontal(null);
|
|
||||||
if (m_evaluated)
|
|
||||||
{
|
|
||||||
if (m_cachedReturnValue != null)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
m_cachedReturnValue.DrawValue(window, width);
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
MelonLogger.Log("Exception drawing m_cachedReturnValue!");
|
|
||||||
MelonLogger.Log(e.ToString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
GUILayout.Label($"null (<color=yellow>{ValueTypeName}</color>)", null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
GUILayout.Label($"<color=grey><i>Not yet evaluated</i></color> (<color=yellow>{ValueTypeName}</color>)", null);
|
|
||||||
}
|
|
||||||
GUILayout.EndHorizontal();
|
|
||||||
|
|
||||||
GUILayout.EndVertical();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Evaluate()
|
public void Evaluate()
|
||||||
{
|
{
|
||||||
var mi = MemInfo as MethodInfo;
|
m_isEvaluating = false;
|
||||||
|
|
||||||
|
var mi = MemInfo as MethodInfo;
|
||||||
object ret = null;
|
object ret = null;
|
||||||
|
|
||||||
if (!HasParameters)
|
if (!HasParameters)
|
||||||
@ -161,57 +44,20 @@ namespace Explorer
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var arguments = new List<object>();
|
try
|
||||||
for (int i = 0; i < m_arguments.Length; i++)
|
|
||||||
{
|
{
|
||||||
var input = m_argumentInput[i];
|
ret = mi.Invoke(mi.IsStatic ? null : DeclaringInstance, ParseArguments());
|
||||||
var type = m_arguments[i].ParameterType;
|
|
||||||
|
|
||||||
if (type == typeof(string))
|
|
||||||
{
|
|
||||||
arguments.Add(input);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (type.GetMethod("Parse", new Type[] { typeof(string) }).Invoke(null, new object[] { input }) is object parsed)
|
|
||||||
{
|
|
||||||
arguments.Add(parsed);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw new Exception();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
MelonLogger.Log($"Unable to parse '{input}' to type '{type.Name}'");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (arguments.Count == m_arguments.Length)
|
|
||||||
{
|
|
||||||
ret = mi.Invoke(mi.IsStatic ? null : DeclaringInstance, arguments.ToArray());
|
|
||||||
m_evaluated = true;
|
m_evaluated = true;
|
||||||
}
|
}
|
||||||
else
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
MelonLogger.Log($"Did not invoke because {m_arguments.Length - arguments.Count} arguments could not be parsed!");
|
MelonLogger.Log($"Exception evaluating: {e.GetType()}, {e.Message}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ret != null)
|
if (ret != null)
|
||||||
{
|
{
|
||||||
m_cachedReturnValue = GetCacheObject(ret);
|
m_cachedReturnValue = GetCacheObject(ret);
|
||||||
if (m_cachedReturnValue is CacheList cacheList)
|
|
||||||
{
|
|
||||||
cacheList.WhiteSpace = 0f;
|
|
||||||
cacheList.ButtonWidthOffset += 70f;
|
|
||||||
}
|
|
||||||
m_cachedReturnValue.UpdateValue();
|
m_cachedReturnValue.UpdateValue();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -219,5 +65,26 @@ namespace Explorer
|
|||||||
m_cachedReturnValue = null;
|
m_cachedReturnValue = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ==== GUI DRAW ====
|
||||||
|
|
||||||
|
public override void DrawValue(Rect window, float width)
|
||||||
|
{
|
||||||
|
if (m_evaluated)
|
||||||
|
{
|
||||||
|
if (m_cachedReturnValue != null)
|
||||||
|
{
|
||||||
|
m_cachedReturnValue.DrawValue(window, width);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
GUILayout.Label($"null (<color=#2df7b2>{ValueTypeName}</color>)", null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
GUILayout.Label($"<color=grey><i>Not yet evaluated</i></color> (<color=#2df7b2>{ValueTypeName}</color>)", null);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -42,15 +42,20 @@ namespace Explorer
|
|||||||
|
|
||||||
if (!label.Contains(ValueTypeName))
|
if (!label.Contains(ValueTypeName))
|
||||||
{
|
{
|
||||||
label += $" ({ValueTypeName})";
|
label += $" (<color=#2df7b2>{ValueTypeName}</color>)";
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
label = label.Replace(ValueTypeName, $"<color=#2df7b2>{ValueTypeName}</color>");
|
||||||
|
}
|
||||||
|
|
||||||
if (Value is UnityEngine.Object unityObj && !label.Contains(unityObj.name))
|
if (Value is UnityEngine.Object unityObj && !label.Contains(unityObj.name))
|
||||||
{
|
{
|
||||||
label = unityObj.name + " | " + label;
|
label = unityObj.name + " | " + label;
|
||||||
}
|
}
|
||||||
|
|
||||||
GUI.skin.button.alignment = TextAnchor.MiddleLeft;
|
GUI.skin.button.alignment = TextAnchor.MiddleLeft;
|
||||||
if (GUILayout.Button("<color=yellow>" + label + "</color>", new GUILayoutOption[] { GUILayout.Width(width) }))
|
if (GUILayout.Button(label, new GUILayoutOption[] { GUILayout.Width(width - 15) }))
|
||||||
{
|
{
|
||||||
WindowManager.InspectObject(Value, out bool _);
|
WindowManager.InspectObject(Value, out bool _);
|
||||||
}
|
}
|
||||||
|
@ -7,13 +7,16 @@ using UnityEngine;
|
|||||||
|
|
||||||
namespace Explorer
|
namespace Explorer
|
||||||
{
|
{
|
||||||
public class CacheColor : CacheObjectBase
|
public class CacheColor : CacheObjectBase, IExpandHeight
|
||||||
{
|
{
|
||||||
private string r = "0";
|
private string r = "0";
|
||||||
private string g = "0";
|
private string g = "0";
|
||||||
private string b = "0";
|
private string b = "0";
|
||||||
private string a = "0";
|
private string a = "0";
|
||||||
|
|
||||||
|
public bool IsExpanded { get; set; }
|
||||||
|
public float WhiteSpace { get; set; } = 215f;
|
||||||
|
|
||||||
public override void UpdateValue()
|
public override void UpdateValue()
|
||||||
{
|
{
|
||||||
base.UpdateValue();
|
base.UpdateValue();
|
||||||
@ -28,12 +31,34 @@ namespace Explorer
|
|||||||
|
|
||||||
public override void DrawValue(Rect window, float width)
|
public override void DrawValue(Rect window, float width)
|
||||||
{
|
{
|
||||||
GUILayout.Label($"<color=yellow>Color</color>: {((Color)Value).ToString()}", null);
|
|
||||||
|
|
||||||
if (CanWrite)
|
if (CanWrite)
|
||||||
|
{
|
||||||
|
if (!IsExpanded)
|
||||||
|
{
|
||||||
|
if (GUILayout.Button("v", new GUILayoutOption[] { GUILayout.Width(25) }))
|
||||||
|
{
|
||||||
|
IsExpanded = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (GUILayout.Button("^", new GUILayoutOption[] { GUILayout.Width(25) }))
|
||||||
|
{
|
||||||
|
IsExpanded = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var c = (Color)Value;
|
||||||
|
GUI.color = c;
|
||||||
|
GUILayout.Label($"<color=#2df7b2>Color:</color> {c.ToString()}", null);
|
||||||
|
GUI.color = Color.white;
|
||||||
|
|
||||||
|
if (CanWrite && IsExpanded)
|
||||||
{
|
{
|
||||||
GUILayout.EndHorizontal();
|
GUILayout.EndHorizontal();
|
||||||
var whitespace = window.width - width - 90;
|
|
||||||
|
var whitespace = CalcWhitespace(window);
|
||||||
|
|
||||||
GUILayout.BeginHorizontal(null);
|
GUILayout.BeginHorizontal(null);
|
||||||
GUILayout.Space(whitespace);
|
GUILayout.Space(whitespace);
|
||||||
|
@ -51,7 +51,7 @@ namespace Explorer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
GUILayout.Label(Value.ToString() + "<color=yellow><i> (" + ValueType + ")</i></color>", null);
|
GUILayout.Label(Value.ToString() + "<color=#2df7b2><i> (" + ValueType + ")</i></color>", null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetEnum(ref object value, int change)
|
public void SetEnum(ref object value, int change)
|
||||||
|
@ -69,10 +69,10 @@ namespace Explorer
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
// using ValueType.Name instead of ValueTypeName, because we only want the short name.
|
// using ValueType.Name instead of ValueTypeName, because we only want the short name.
|
||||||
GUILayout.Label("<color=yellow><i>" + ValueType.Name + "</i></color>", new GUILayoutOption[] { GUILayout.Width(50) });
|
GUILayout.Label("<color=#2df7b2><i>" + ValueType.Name + "</i></color>", new GUILayoutOption[] { GUILayout.Width(50) });
|
||||||
|
|
||||||
int dynSize = 25 + (m_valueToString.Length * 15);
|
int dynSize = 25 + (m_valueToString.Length * 15);
|
||||||
var maxwidth = window.width - 300f;
|
var maxwidth = window.width - 310f;
|
||||||
if (CanWrite) maxwidth -= 60;
|
if (CanWrite) maxwidth -= 60;
|
||||||
|
|
||||||
if (dynSize > maxwidth)
|
if (dynSize > maxwidth)
|
||||||
@ -92,7 +92,7 @@ namespace Explorer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
GUILayout.Space(5);
|
GUILayout.Space(10);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,12 +7,15 @@ using UnityEngine;
|
|||||||
|
|
||||||
namespace Explorer
|
namespace Explorer
|
||||||
{
|
{
|
||||||
public class CacheQuaternion : CacheObjectBase
|
public class CacheQuaternion : CacheObjectBase, IExpandHeight
|
||||||
{
|
{
|
||||||
private string x = "0";
|
private string x = "0";
|
||||||
private string y = "0";
|
private string y = "0";
|
||||||
private string z = "0";
|
private string z = "0";
|
||||||
|
|
||||||
|
public bool IsExpanded { get; set; }
|
||||||
|
public float WhiteSpace { get; set; } = 215f;
|
||||||
|
|
||||||
public override void UpdateValue()
|
public override void UpdateValue()
|
||||||
{
|
{
|
||||||
base.UpdateValue();
|
base.UpdateValue();
|
||||||
@ -26,12 +29,31 @@ namespace Explorer
|
|||||||
|
|
||||||
public override void DrawValue(Rect window, float width)
|
public override void DrawValue(Rect window, float width)
|
||||||
{
|
{
|
||||||
GUILayout.Label($"<color=yellow>Quaternion</color>: {((Quaternion)Value).eulerAngles.ToString()}", null);
|
|
||||||
|
|
||||||
if (CanWrite)
|
if (CanWrite)
|
||||||
|
{
|
||||||
|
if (!IsExpanded)
|
||||||
|
{
|
||||||
|
if (GUILayout.Button("v", new GUILayoutOption[] { GUILayout.Width(25) }))
|
||||||
|
{
|
||||||
|
IsExpanded = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (GUILayout.Button("^", new GUILayoutOption[] { GUILayout.Width(25) }))
|
||||||
|
{
|
||||||
|
IsExpanded = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
GUILayout.Label($"<color=#2df7b2>Quaternion</color>: {((Quaternion)Value).eulerAngles.ToString()}", null);
|
||||||
|
|
||||||
|
if (CanWrite && IsExpanded)
|
||||||
{
|
{
|
||||||
GUILayout.EndHorizontal();
|
GUILayout.EndHorizontal();
|
||||||
var whitespace = window.width - width - 90;
|
|
||||||
|
var whitespace = CalcWhitespace(window);
|
||||||
|
|
||||||
GUILayout.BeginHorizontal(null);
|
GUILayout.BeginHorizontal(null);
|
||||||
GUILayout.Space(whitespace);
|
GUILayout.Space(whitespace);
|
||||||
|
@ -7,13 +7,16 @@ using UnityEngine;
|
|||||||
|
|
||||||
namespace Explorer
|
namespace Explorer
|
||||||
{
|
{
|
||||||
public class CacheRect : CacheObjectBase
|
public class CacheRect : CacheObjectBase, IExpandHeight
|
||||||
{
|
{
|
||||||
private string x = "0";
|
private string x = "0";
|
||||||
private string y = "0";
|
private string y = "0";
|
||||||
private string w = "0";
|
private string w = "0";
|
||||||
private string h = "0";
|
private string h = "0";
|
||||||
|
|
||||||
|
public bool IsExpanded { get; set; }
|
||||||
|
public float WhiteSpace { get; set; } = 215f;
|
||||||
|
|
||||||
public override void UpdateValue()
|
public override void UpdateValue()
|
||||||
{
|
{
|
||||||
base.UpdateValue();
|
base.UpdateValue();
|
||||||
@ -28,12 +31,31 @@ namespace Explorer
|
|||||||
|
|
||||||
public override void DrawValue(Rect window, float width)
|
public override void DrawValue(Rect window, float width)
|
||||||
{
|
{
|
||||||
GUILayout.Label($"<color=yellow>Rect</color>: {((Rect)Value).ToString()}", null);
|
|
||||||
|
|
||||||
if (CanWrite)
|
if (CanWrite)
|
||||||
|
{
|
||||||
|
if (!IsExpanded)
|
||||||
|
{
|
||||||
|
if (GUILayout.Button("v", new GUILayoutOption[] { GUILayout.Width(25) }))
|
||||||
|
{
|
||||||
|
IsExpanded = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (GUILayout.Button("^", new GUILayoutOption[] { GUILayout.Width(25) }))
|
||||||
|
{
|
||||||
|
IsExpanded = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
GUILayout.Label($"<color=#2df7b2>Rect</color>: {((Rect)Value).ToString()}", null);
|
||||||
|
|
||||||
|
if (CanWrite && IsExpanded)
|
||||||
{
|
{
|
||||||
GUILayout.EndHorizontal();
|
GUILayout.EndHorizontal();
|
||||||
var whitespace = window.width - width - 90;
|
|
||||||
|
var whitespace = CalcWhitespace(window);
|
||||||
|
|
||||||
GUILayout.BeginHorizontal(null);
|
GUILayout.BeginHorizontal(null);
|
||||||
GUILayout.Space(whitespace);
|
GUILayout.Space(whitespace);
|
||||||
|
@ -8,7 +8,7 @@ using UnityEngine;
|
|||||||
|
|
||||||
namespace Explorer
|
namespace Explorer
|
||||||
{
|
{
|
||||||
public class CacheVector : CacheObjectBase
|
public class CacheVector : CacheObjectBase, IExpandHeight
|
||||||
{
|
{
|
||||||
public int VectorSize = 2;
|
public int VectorSize = 2;
|
||||||
|
|
||||||
@ -19,6 +19,9 @@ namespace Explorer
|
|||||||
|
|
||||||
private MethodInfo m_toStringMethod;
|
private MethodInfo m_toStringMethod;
|
||||||
|
|
||||||
|
public bool IsExpanded { get; set; }
|
||||||
|
public float WhiteSpace { get; set; } = 215f;
|
||||||
|
|
||||||
public override void Init()
|
public override void Init()
|
||||||
{
|
{
|
||||||
if (Value is Vector2)
|
if (Value is Vector2)
|
||||||
@ -63,12 +66,31 @@ namespace Explorer
|
|||||||
|
|
||||||
public override void DrawValue(Rect window, float width)
|
public override void DrawValue(Rect window, float width)
|
||||||
{
|
{
|
||||||
GUILayout.Label($"<color=yellow>Vector{VectorSize}</color>: {(string)m_toStringMethod.Invoke(Value, new object[0])}", null);
|
|
||||||
|
|
||||||
if (CanWrite)
|
if (CanWrite)
|
||||||
|
{
|
||||||
|
if (!IsExpanded)
|
||||||
|
{
|
||||||
|
if (GUILayout.Button("v", new GUILayoutOption[] { GUILayout.Width(25) }))
|
||||||
|
{
|
||||||
|
IsExpanded = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (GUILayout.Button("^", new GUILayoutOption[] { GUILayout.Width(25) }))
|
||||||
|
{
|
||||||
|
IsExpanded = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
GUILayout.Label($"<color=#2df7b2>Vector{VectorSize}</color>: {(string)m_toStringMethod.Invoke(Value, new object[0])}", null);
|
||||||
|
|
||||||
|
if (CanWrite && IsExpanded)
|
||||||
{
|
{
|
||||||
GUILayout.EndHorizontal();
|
GUILayout.EndHorizontal();
|
||||||
var whitespace = window.width - width - 90;
|
|
||||||
|
var whitespace = CalcWhitespace(window);
|
||||||
|
|
||||||
// always draw x and y
|
// always draw x and y
|
||||||
GUILayout.BeginHorizontal(null);
|
GUILayout.BeginHorizontal(null);
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Collections;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
@ -11,15 +12,10 @@ namespace Explorer
|
|||||||
{
|
{
|
||||||
public class CppExplorer : MelonMod
|
public class CppExplorer : MelonMod
|
||||||
{
|
{
|
||||||
public const string GUID = "com.sinai.cppexplorer";
|
public const string NAME = "CppExplorer";
|
||||||
public const string VERSION = "1.5.8";
|
public const string VERSION = "1.6.7";
|
||||||
public const string AUTHOR = "Sinai";
|
public const string AUTHOR = "Sinai";
|
||||||
|
public const string GUID = "com.sinai.cppexplorer";
|
||||||
public const string NAME = "CppExplorer"
|
|
||||||
#if Release_Unity2018
|
|
||||||
+ " (Unity 2018)"
|
|
||||||
#endif
|
|
||||||
;
|
|
||||||
|
|
||||||
public static CppExplorer Instance { get; private set; }
|
public static CppExplorer Instance { get; private set; }
|
||||||
|
|
||||||
@ -48,12 +44,18 @@ namespace Explorer
|
|||||||
UpdateCursorControl();
|
UpdateCursorControl();
|
||||||
}
|
}
|
||||||
|
|
||||||
// ========== MonoBehaviour methods ==========
|
private static void SetForceUnlock(bool unlock)
|
||||||
|
{
|
||||||
|
m_forceUnlock = unlock;
|
||||||
|
UpdateCursorControl();
|
||||||
|
}
|
||||||
|
|
||||||
public override void OnApplicationStart()
|
public override void OnApplicationStart()
|
||||||
{
|
{
|
||||||
Instance = this;
|
Instance = this;
|
||||||
|
|
||||||
|
InputHelper.Init();
|
||||||
|
|
||||||
new MainMenu();
|
new MainMenu();
|
||||||
new WindowManager();
|
new WindowManager();
|
||||||
|
|
||||||
@ -62,9 +64,9 @@ namespace Explorer
|
|||||||
m_lastVisibleState = Cursor.visible;
|
m_lastVisibleState = Cursor.visible;
|
||||||
|
|
||||||
// Enable ShowMenu and ForceUnlockMouse
|
// Enable ShowMenu and ForceUnlockMouse
|
||||||
// (set m_showMenu to not call UpdateCursorState twice)
|
// (set m_showMenu directly to not call UpdateCursorState twice)
|
||||||
m_showMenu = true;
|
m_showMenu = true;
|
||||||
SetForceUnlock(true);
|
ForceUnlockMouse = true;
|
||||||
|
|
||||||
MelonLogger.Log($"CppExplorer {VERSION} initialized.");
|
MelonLogger.Log($"CppExplorer {VERSION} initialized.");
|
||||||
}
|
}
|
||||||
@ -78,7 +80,7 @@ namespace Explorer
|
|||||||
public override void OnUpdate()
|
public override void OnUpdate()
|
||||||
{
|
{
|
||||||
// Check main toggle key input
|
// Check main toggle key input
|
||||||
if (Input.GetKeyDown(KeyCode.F7))
|
if (InputHelper.GetKeyDown(KeyCode.F7))
|
||||||
{
|
{
|
||||||
ShowMenu = !ShowMenu;
|
ShowMenu = !ShowMenu;
|
||||||
}
|
}
|
||||||
@ -86,7 +88,7 @@ namespace Explorer
|
|||||||
if (ShowMenu)
|
if (ShowMenu)
|
||||||
{
|
{
|
||||||
// Check Force-Unlock input
|
// Check Force-Unlock input
|
||||||
if (Input.GetKeyDown(KeyCode.LeftAlt))
|
if (InputHelper.GetKeyDown(KeyCode.LeftAlt))
|
||||||
{
|
{
|
||||||
ForceUnlockMouse = !ForceUnlockMouse;
|
ForceUnlockMouse = !ForceUnlockMouse;
|
||||||
}
|
}
|
||||||
@ -106,14 +108,6 @@ namespace Explorer
|
|||||||
InspectUnderMouse.OnGUI();
|
InspectUnderMouse.OnGUI();
|
||||||
}
|
}
|
||||||
|
|
||||||
// =========== Cursor control ===========
|
|
||||||
|
|
||||||
private static void SetForceUnlock(bool unlock)
|
|
||||||
{
|
|
||||||
m_forceUnlock = unlock;
|
|
||||||
UpdateCursorControl();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void UpdateCursorControl()
|
private static void UpdateCursorControl()
|
||||||
{
|
{
|
||||||
m_currentlySettingCursor = true;
|
m_currentlySettingCursor = true;
|
||||||
|
@ -12,32 +12,19 @@
|
|||||||
<FileAlignment>512</FileAlignment>
|
<FileAlignment>512</FileAlignment>
|
||||||
<Deterministic>true</Deterministic>
|
<Deterministic>true</Deterministic>
|
||||||
<TargetFrameworkProfile />
|
<TargetFrameworkProfile />
|
||||||
</PropertyGroup>
|
|
||||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
|
||||||
<AssemblyName>CppExplorer</AssemblyName>
|
<AssemblyName>CppExplorer</AssemblyName>
|
||||||
<DebugSymbols>false</DebugSymbols>
|
<DebugSymbols>false</DebugSymbols>
|
||||||
<DebugType>none</DebugType>
|
<DebugType>none</DebugType>
|
||||||
<Optimize>false</Optimize>
|
<Optimize>false</Optimize>
|
||||||
<OutputPath>..\Release\2019\</OutputPath>
|
<OutputPath>..\Release\</OutputPath>
|
||||||
<DefineConstants>Release_2019</DefineConstants>
|
<DefineConstants />
|
||||||
<ErrorReport>prompt</ErrorReport>
|
|
||||||
<WarningLevel>4</WarningLevel>
|
|
||||||
<PlatformTarget>x64</PlatformTarget>
|
|
||||||
<Prefer32Bit>false</Prefer32Bit>
|
|
||||||
</PropertyGroup>
|
|
||||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release_Unity2018|AnyCPU' ">
|
|
||||||
<AssemblyName>CppExplorer_Unity2018</AssemblyName>
|
|
||||||
<DebugSymbols>false</DebugSymbols>
|
|
||||||
<DebugType>none</DebugType>
|
|
||||||
<Optimize>false</Optimize>
|
|
||||||
<OutputPath>..\Release\2018\</OutputPath>
|
|
||||||
<DefineConstants>Release_Unity2018</DefineConstants>
|
|
||||||
<ErrorReport>prompt</ErrorReport>
|
<ErrorReport>prompt</ErrorReport>
|
||||||
<WarningLevel>4</WarningLevel>
|
<WarningLevel>4</WarningLevel>
|
||||||
<PlatformTarget>x64</PlatformTarget>
|
<PlatformTarget>x64</PlatformTarget>
|
||||||
<Prefer32Bit>false</Prefer32Bit>
|
<Prefer32Bit>false</Prefer32Bit>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<!-- Replace the '..\Steam\..` references with ones from your game (make sure to use the 'MelonLoader\' folder) -->
|
||||||
<Reference Include="Il2Cppmscorlib">
|
<Reference Include="Il2Cppmscorlib">
|
||||||
<HintPath>..\..\..\..\..\Steam\steamapps\common\Hellpoint\MelonLoader\Managed\Il2Cppmscorlib.dll</HintPath>
|
<HintPath>..\..\..\..\..\Steam\steamapps\common\Hellpoint\MelonLoader\Managed\Il2Cppmscorlib.dll</HintPath>
|
||||||
<Private>False</Private>
|
<Private>False</Private>
|
||||||
@ -60,66 +47,33 @@
|
|||||||
<HintPath>..\..\..\..\..\Steam\steamapps\common\Hellpoint\MelonLoader\Managed\UnhollowerBaseLib.dll</HintPath>
|
<HintPath>..\..\..\..\..\Steam\steamapps\common\Hellpoint\MelonLoader\Managed\UnhollowerBaseLib.dll</HintPath>
|
||||||
<Private>False</Private>
|
<Private>False</Private>
|
||||||
</Reference>
|
</Reference>
|
||||||
<!-- Unity 2019 build (InputLegacyModule.dll) -->
|
<Reference Include="UnityEngine">
|
||||||
<Reference Include="UnityEngine" Condition="'$(Configuration)'=='Debug'">
|
|
||||||
<HintPath>..\..\..\Steam\steamapps\common\Hellpoint\MelonLoader\Managed\UnityEngine.dll</HintPath>
|
<HintPath>..\..\..\Steam\steamapps\common\Hellpoint\MelonLoader\Managed\UnityEngine.dll</HintPath>
|
||||||
<Private>False</Private>
|
<Private>False</Private>
|
||||||
</Reference>
|
</Reference>
|
||||||
<Reference Include="UnityEngine.CoreModule" Condition="'$(Configuration)'=='Debug'">
|
<Reference Include="UnityEngine.CoreModule">
|
||||||
<HintPath>..\..\..\Steam\steamapps\common\Hellpoint\MelonLoader\Managed\UnityEngine.CoreModule.dll</HintPath>
|
<HintPath>..\..\..\Steam\steamapps\common\Hellpoint\MelonLoader\Managed\UnityEngine.CoreModule.dll</HintPath>
|
||||||
<Private>False</Private>
|
<Private>False</Private>
|
||||||
</Reference>
|
</Reference>
|
||||||
<Reference Include="UnityEngine.IMGUIModule" Condition="'$(Configuration)'=='Debug'">
|
<Reference Include="UnityEngine.IMGUIModule">
|
||||||
<HintPath>..\..\..\Steam\steamapps\common\Hellpoint\MelonLoader\Managed\UnityEngine.IMGUIModule.dll</HintPath>
|
<HintPath>..\..\..\Steam\steamapps\common\Hellpoint\MelonLoader\Managed\UnityEngine.IMGUIModule.dll</HintPath>
|
||||||
<Private>False</Private>
|
<Private>False</Private>
|
||||||
</Reference>
|
</Reference>
|
||||||
<Reference Include="UnityEngine.InputModule" Condition="'$(Configuration)'=='Debug'">
|
<Reference Include="UnityEngine.PhysicsModule">
|
||||||
<HintPath>..\..\..\Steam\steamapps\common\Hellpoint\MelonLoader\Managed\UnityEngine.InputLegacyModule.dll</HintPath>
|
|
||||||
<Private>False</Private>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="UnityEngine.PhysicsModule" Condition="'$(Configuration)'=='Debug'">
|
|
||||||
<HintPath>..\..\..\Steam\steamapps\common\Hellpoint\MelonLoader\Managed\UnityEngine.PhysicsModule.dll</HintPath>
|
<HintPath>..\..\..\Steam\steamapps\common\Hellpoint\MelonLoader\Managed\UnityEngine.PhysicsModule.dll</HintPath>
|
||||||
<Private>False</Private>
|
<Private>False</Private>
|
||||||
</Reference>
|
</Reference>
|
||||||
<Reference Include="UnityEngine.TextRenderingModule" Condition="'$(Configuration)'=='Debug'">
|
<Reference Include="UnityEngine.TextRenderingModule">
|
||||||
<HintPath>..\..\..\Steam\steamapps\common\Hellpoint\MelonLoader\Managed\UnityEngine.TextRenderingModule.dll</HintPath>
|
<HintPath>..\..\..\Steam\steamapps\common\Hellpoint\MelonLoader\Managed\UnityEngine.TextRenderingModule.dll</HintPath>
|
||||||
<Private>False</Private>
|
<Private>False</Private>
|
||||||
</Reference>
|
</Reference>
|
||||||
<Reference Include="UnityEngine.UI" Condition="'$(Configuration)'=='Debug'">
|
<Reference Include="UnityEngine.UI">
|
||||||
<HintPath>..\..\..\Steam\steamapps\common\Hellpoint\MelonLoader\Managed\UnityEngine.UI.dll</HintPath>
|
<HintPath>..\..\..\Steam\steamapps\common\Hellpoint\MelonLoader\Managed\UnityEngine.UI.dll</HintPath>
|
||||||
<Private>False</Private>
|
<Private>False</Private>
|
||||||
</Reference>
|
</Reference>
|
||||||
<!-- Unity 2018 build (InputModule.dll) -->
|
|
||||||
<Reference Include="UnityEngine" Condition="'$(Configuration)'=='Release_Unity2018'">
|
|
||||||
<HintPath>..\..\..\Steam\steamapps\common\VRChat\MelonLoader\Managed\UnityEngine.dll</HintPath>
|
|
||||||
<Private>False</Private>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="UnityEngine.CoreModule" Condition="'$(Configuration)'=='Release_Unity2018'">
|
|
||||||
<HintPath>..\..\..\Steam\steamapps\common\VRChat\MelonLoader\Managed\UnityEngine.CoreModule.dll</HintPath>
|
|
||||||
<Private>False</Private>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="UnityEngine.IMGUIModule" Condition="'$(Configuration)'=='Release_Unity2018'">
|
|
||||||
<HintPath>..\..\..\Steam\steamapps\common\VRChat\MelonLoader\Managed\UnityEngine.IMGUIModule.dll</HintPath>
|
|
||||||
<Private>False</Private>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="UnityEngine.InputModule" Condition="'$(Configuration)'=='Release_Unity2018'">
|
|
||||||
<HintPath>..\..\..\Steam\steamapps\common\VRChat\MelonLoader\Managed\UnityEngine.InputModule.dll</HintPath>
|
|
||||||
<Private>False</Private>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="UnityEngine.PhysicsModule" Condition="'$(Configuration)'=='Release_Unity2018'">
|
|
||||||
<HintPath>..\..\..\Steam\steamapps\common\VRChat\MelonLoader\Managed\UnityEngine.PhysicsModule.dll</HintPath>
|
|
||||||
<Private>False</Private>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="UnityEngine.TextRenderingModule" Condition="'$(Configuration)'=='Release_Unity2018'">
|
|
||||||
<HintPath>..\..\..\Steam\steamapps\common\VRChat\MelonLoader\Managed\UnityEngine.TextRenderingModule.dll</HintPath>
|
|
||||||
<Private>False</Private>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="UnityEngine.UI" Condition="'$(Configuration)'=='Release_Unity2018'">
|
|
||||||
<HintPath>..\..\..\Steam\steamapps\common\VRChat\MelonLoader\Managed\UnityEngine.UI.dll</HintPath>
|
|
||||||
<Private>False</Private>
|
|
||||||
</Reference>
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<Compile Include="CachedObjects\IExpandHeight.cs" />
|
||||||
<Compile Include="CachedObjects\Struct\CacheColor.cs" />
|
<Compile Include="CachedObjects\Struct\CacheColor.cs" />
|
||||||
<Compile Include="CachedObjects\Object\CacheDictionary.cs" />
|
<Compile Include="CachedObjects\Object\CacheDictionary.cs" />
|
||||||
<Compile Include="CachedObjects\Struct\CacheEnum.cs" />
|
<Compile Include="CachedObjects\Struct\CacheEnum.cs" />
|
||||||
@ -133,6 +87,8 @@
|
|||||||
<Compile Include="CachedObjects\Struct\CacheRect.cs" />
|
<Compile Include="CachedObjects\Struct\CacheRect.cs" />
|
||||||
<Compile Include="CppExplorer.cs" />
|
<Compile Include="CppExplorer.cs" />
|
||||||
<Compile Include="Extensions\ReflectionExtensions.cs" />
|
<Compile Include="Extensions\ReflectionExtensions.cs" />
|
||||||
|
<Compile Include="Helpers\InputHelper.cs" />
|
||||||
|
<Compile Include="Tests\TestClass.cs" />
|
||||||
<Compile Include="UnstripFixes\GUIUnstrip.cs" />
|
<Compile Include="UnstripFixes\GUIUnstrip.cs" />
|
||||||
<Compile Include="UnstripFixes\ScrollViewStateUnstrip.cs" />
|
<Compile Include="UnstripFixes\ScrollViewStateUnstrip.cs" />
|
||||||
<Compile Include="Extensions\UnityExtensions.cs" />
|
<Compile Include="Extensions\UnityExtensions.cs" />
|
||||||
|
@ -7,14 +7,11 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CppExplorer", "CppExplorer.
|
|||||||
EndProject
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Release|Any CPU = Release|Any CPU
|
||||||
Release_Unity2018|Any CPU = Release_Unity2018|Any CPU
|
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||||
{B21DBDE3-5D6F-4726-93AB-CC3CC68BAE7D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
{B21DBDE3-5D6F-4726-93AB-CC3CC68BAE7D}.Release|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
{B21DBDE3-5D6F-4726-93AB-CC3CC68BAE7D}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{B21DBDE3-5D6F-4726-93AB-CC3CC68BAE7D}.Release|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{B21DBDE3-5D6F-4726-93AB-CC3CC68BAE7D}.Release_Unity2018|Any CPU.ActiveCfg = Release_Unity2018|Any CPU
|
|
||||||
{B21DBDE3-5D6F-4726-93AB-CC3CC68BAE7D}.Release_Unity2018|Any CPU.Build.0 = Release_Unity2018|Any CPU
|
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
|
@ -3,14 +3,38 @@ using System.Collections.Generic;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using System.Reflection;
|
||||||
|
|
||||||
namespace Explorer
|
namespace Explorer
|
||||||
{
|
{
|
||||||
public static class ReflectionExtensions
|
public static class ReflectionExtensions
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Extension to allow for easy, non-generic Il2Cpp casting.
|
||||||
|
/// The extension is on System.Object, but only Il2Cpp objects would be a valid target.
|
||||||
|
/// </summary>
|
||||||
public static object Il2CppCast(this object obj, Type castTo)
|
public static object Il2CppCast(this object obj, Type castTo)
|
||||||
{
|
{
|
||||||
return ReflectionHelpers.Il2CppCast(obj, castTo);
|
return ReflectionHelpers.Il2CppCast(obj, castTo);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Extension to safely try to get all Types from an Assembly, with a fallback for ReflectionTypeLoadException.
|
||||||
|
/// </summary>
|
||||||
|
public static IEnumerable<Type> TryGetTypes(this Assembly asm)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return asm.GetTypes();
|
||||||
|
}
|
||||||
|
catch (ReflectionTypeLoadException e)
|
||||||
|
{
|
||||||
|
return e.Types.Where(t => t != null);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return Enumerable.Empty<Type>();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
120
src/Helpers/InputHelper.cs
Normal file
120
src/Helpers/InputHelper.cs
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Reflection;
|
||||||
|
using UnityEngine;
|
||||||
|
using MelonLoader;
|
||||||
|
|
||||||
|
namespace Explorer
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Version-agnostic UnityEngine Input module using Reflection.
|
||||||
|
/// </summary>
|
||||||
|
public static class InputHelper
|
||||||
|
{
|
||||||
|
// If Input module failed to load at all
|
||||||
|
public static bool NO_INPUT;
|
||||||
|
|
||||||
|
// Base UnityEngine.Input class
|
||||||
|
private static Type Input => _input ?? (_input = ReflectionHelpers.GetTypeByName("UnityEngine.Input"));
|
||||||
|
private static Type _input;
|
||||||
|
|
||||||
|
// Cached member infos
|
||||||
|
private static PropertyInfo _mousePosition;
|
||||||
|
private static MethodInfo _getKey;
|
||||||
|
private static MethodInfo _getKeyDown;
|
||||||
|
private static MethodInfo _getMouseButton;
|
||||||
|
private static MethodInfo _getMouseButtonDown;
|
||||||
|
|
||||||
|
public static void Init()
|
||||||
|
{
|
||||||
|
if (Input == null && !TryManuallyLoadInput())
|
||||||
|
{
|
||||||
|
NO_INPUT = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cache reflection now that we know Input is loaded
|
||||||
|
|
||||||
|
_mousePosition = Input.GetProperty("mousePosition");
|
||||||
|
|
||||||
|
_getKey = Input.GetMethod("GetKey", new Type[] { typeof(KeyCode) });
|
||||||
|
_getKeyDown = Input.GetMethod("GetKeyDown", new Type[] { typeof(KeyCode) });
|
||||||
|
_getMouseButton = Input.GetMethod("GetMouseButton", new Type[] { typeof(int) });
|
||||||
|
_getMouseButtonDown = Input.GetMethod("GetMouseButtonDown", new Type[] { typeof(int) });
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma warning disable IDE1006 // Camel-case property (Unity style)
|
||||||
|
public static Vector3 mousePosition
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (NO_INPUT) return Vector3.zero;
|
||||||
|
return (Vector3)_mousePosition.GetValue(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#pragma warning restore IDE1006
|
||||||
|
|
||||||
|
public static bool GetKeyDown(KeyCode key)
|
||||||
|
{
|
||||||
|
if (NO_INPUT) return false;
|
||||||
|
return (bool)_getKeyDown.Invoke(null, new object[] { key });
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool GetKey(KeyCode key)
|
||||||
|
{
|
||||||
|
if (NO_INPUT) return false;
|
||||||
|
return (bool)_getKey.Invoke(null, new object[] { key });
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <param name="btn">1 = left, 2 = middle, 3 = right, etc</param>
|
||||||
|
public static bool GetMouseButtonDown(int btn)
|
||||||
|
{
|
||||||
|
if (NO_INPUT) return false;
|
||||||
|
return (bool)_getMouseButtonDown.Invoke(null, new object[] { btn });
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <param name="btn">1 = left, 2 = middle, 3 = right, etc</param>
|
||||||
|
public static bool GetMouseButton(int btn)
|
||||||
|
{
|
||||||
|
if (NO_INPUT) return false;
|
||||||
|
return (bool)_getMouseButton.Invoke(null, new object[] { btn });
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool TryManuallyLoadInput()
|
||||||
|
{
|
||||||
|
MelonLogger.Log("UnityEngine.Input is null, trying to load manually....");
|
||||||
|
|
||||||
|
if ((TryLoad("UnityEngine.InputLegacyModule.dll") || TryLoad("UnityEngine.CoreModule.dll")) && Input != null)
|
||||||
|
{
|
||||||
|
MelonLogger.Log("Ok!");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
MelonLogger.Log("Could not load Input module!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TryLoad(string module)
|
||||||
|
{
|
||||||
|
var path = $@"MelonLoader\Managed\{module}";
|
||||||
|
if (!File.Exists(path)) return false;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Assembly.Load(File.ReadAllBytes(path));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
MelonLogger.Log(e.GetType() + ", " + e.Message);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,14 +1,13 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Collections;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using UnhollowerBaseLib;
|
using UnhollowerBaseLib;
|
||||||
using UnhollowerRuntimeLib;
|
using UnhollowerRuntimeLib;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using BF = System.Reflection.BindingFlags;
|
using BF = System.Reflection.BindingFlags;
|
||||||
using MelonLoader;
|
using ILType = Il2CppSystem.Type;
|
||||||
|
|
||||||
namespace Explorer
|
namespace Explorer
|
||||||
{
|
{
|
||||||
@ -16,11 +15,11 @@ namespace Explorer
|
|||||||
{
|
{
|
||||||
public static BF CommonFlags = BF.Public | BF.Instance | BF.NonPublic | BF.Static;
|
public static BF CommonFlags = BF.Public | BF.Instance | BF.NonPublic | BF.Static;
|
||||||
|
|
||||||
public static Il2CppSystem.Type GameObjectType => Il2CppType.Of<GameObject>();
|
public static ILType GameObjectType => Il2CppType.Of<GameObject>();
|
||||||
public static Il2CppSystem.Type TransformType => Il2CppType.Of<Transform>();
|
public static ILType TransformType => Il2CppType.Of<Transform>();
|
||||||
public static Il2CppSystem.Type ObjectType => Il2CppType.Of<UnityEngine.Object>();
|
public static ILType ObjectType => Il2CppType.Of<UnityEngine.Object>();
|
||||||
public static Il2CppSystem.Type ComponentType => Il2CppType.Of<Component>();
|
public static ILType ComponentType => Il2CppType.Of<Component>();
|
||||||
public static Il2CppSystem.Type BehaviourType => Il2CppType.Of<Behaviour>();
|
public static ILType BehaviourType => Il2CppType.Of<Behaviour>();
|
||||||
|
|
||||||
private static readonly MethodInfo m_tryCastMethodInfo = typeof(Il2CppObjectBase).GetMethod("TryCast");
|
private static readonly MethodInfo m_tryCastMethodInfo = typeof(Il2CppObjectBase).GetMethod("TryCast");
|
||||||
|
|
||||||
@ -33,6 +32,94 @@ namespace Explorer
|
|||||||
.Invoke(obj, null);
|
.Invoke(obj, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static bool IsEnumerable(Type t)
|
||||||
|
{
|
||||||
|
return typeof(IEnumerable).IsAssignableFrom(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only Il2Cpp List needs this check. C# List is IEnumerable.
|
||||||
|
public static bool IsCppList(Type t)
|
||||||
|
{
|
||||||
|
if (t.IsGenericType && t.GetGenericTypeDefinition() is Type g)
|
||||||
|
{
|
||||||
|
return typeof(Il2CppSystem.Collections.Generic.List<>).IsAssignableFrom(g)
|
||||||
|
|| typeof(Il2CppSystem.Collections.Generic.IList<>).IsAssignableFrom(g);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return typeof(Il2CppSystem.Collections.IList).IsAssignableFrom(t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool IsDictionary(Type t)
|
||||||
|
{
|
||||||
|
if (typeof(IDictionary).IsAssignableFrom(t))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (t.IsGenericType && t.GetGenericTypeDefinition() is Type g)
|
||||||
|
{
|
||||||
|
return typeof(Il2CppSystem.Collections.Generic.Dictionary<,>).IsAssignableFrom(g)
|
||||||
|
|| typeof(Il2CppSystem.Collections.Generic.IDictionary<,>).IsAssignableFrom(g);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return typeof(Il2CppSystem.Collections.IDictionary).IsAssignableFrom(t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Type GetTypeByName(string fullName)
|
||||||
|
{
|
||||||
|
foreach (var asm in AppDomain.CurrentDomain.GetAssemblies())
|
||||||
|
{
|
||||||
|
foreach (var type in asm.TryGetTypes())
|
||||||
|
{
|
||||||
|
if (type.FullName == fullName)
|
||||||
|
{
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Type GetActualType(object obj)
|
||||||
|
{
|
||||||
|
if (obj == null) return null;
|
||||||
|
|
||||||
|
if (obj is Il2CppSystem.Object ilObject)
|
||||||
|
{
|
||||||
|
var ilTypeName = ilObject.GetIl2CppType().AssemblyQualifiedName;
|
||||||
|
|
||||||
|
if (Type.GetType(ilTypeName) is Type t && !t.FullName.Contains("System.RuntimeType"))
|
||||||
|
{
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ilObject.GetType();
|
||||||
|
}
|
||||||
|
|
||||||
|
return obj.GetType();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Type[] GetAllBaseTypes(object obj)
|
||||||
|
{
|
||||||
|
var list = new List<Type>();
|
||||||
|
|
||||||
|
var type = GetActualType(obj);
|
||||||
|
list.Add(type);
|
||||||
|
|
||||||
|
while (type.BaseType != null)
|
||||||
|
{
|
||||||
|
type = type.BaseType;
|
||||||
|
list.Add(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
return list.ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
public static string ExceptionToString(Exception e)
|
public static string ExceptionToString(Exception e)
|
||||||
{
|
{
|
||||||
if (IsFailedGeneric(e))
|
if (IsFailedGeneric(e))
|
||||||
@ -73,84 +160,5 @@ namespace Explorer
|
|||||||
else
|
else
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool IsArray(Type t)
|
|
||||||
{
|
|
||||||
return typeof(System.Collections.IEnumerable).IsAssignableFrom(t);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool IsList(Type t)
|
|
||||||
{
|
|
||||||
if (t.IsGenericType)
|
|
||||||
{
|
|
||||||
var generic = t.GetGenericTypeDefinition();
|
|
||||||
|
|
||||||
return generic.IsAssignableFrom(typeof(Il2CppSystem.Collections.Generic.List<>))
|
|
||||||
|| generic.IsAssignableFrom(typeof(Il2CppSystem.Collections.Generic.IList<>));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return t.IsAssignableFrom(typeof(Il2CppSystem.Collections.IList));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool IsDictionary(Type t)
|
|
||||||
{
|
|
||||||
return t.IsGenericType
|
|
||||||
&& t.GetGenericTypeDefinition() is Type typeDef
|
|
||||||
&& typeDef.IsAssignableFrom(typeof(Il2CppSystem.Collections.Generic.Dictionary<,>));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Type GetTypeByName(string typeName)
|
|
||||||
{
|
|
||||||
foreach (var asm in AppDomain.CurrentDomain.GetAssemblies())
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (asm.GetType(typeName) is Type type)
|
|
||||||
{
|
|
||||||
return type;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch { }
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Type GetActualType(object obj)
|
|
||||||
{
|
|
||||||
if (obj == null) return null;
|
|
||||||
|
|
||||||
if (obj is Il2CppSystem.Object ilObject)
|
|
||||||
{
|
|
||||||
var ilTypeName = ilObject.GetIl2CppType().AssemblyQualifiedName;
|
|
||||||
|
|
||||||
if (Type.GetType(ilTypeName) is Type t && !t.FullName.Contains("System.RuntimeType"))
|
|
||||||
{
|
|
||||||
return t;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ilObject.GetType();
|
|
||||||
}
|
|
||||||
|
|
||||||
return obj.GetType();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Type[] GetAllBaseTypes(object obj)
|
|
||||||
{
|
|
||||||
var list = new List<Type>();
|
|
||||||
|
|
||||||
var type = GetActualType(obj);
|
|
||||||
list.Add(type);
|
|
||||||
|
|
||||||
while (type.BaseType != null)
|
|
||||||
{
|
|
||||||
type = type.BaseType;
|
|
||||||
list.Add(type);
|
|
||||||
}
|
|
||||||
|
|
||||||
return list.ToArray();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -56,7 +56,10 @@ namespace Explorer
|
|||||||
{
|
{
|
||||||
_horizBarStyle = new GUIStyle();
|
_horizBarStyle = new GUIStyle();
|
||||||
_horizBarStyle.normal.background = Texture2D.whiteTexture;
|
_horizBarStyle.normal.background = Texture2D.whiteTexture;
|
||||||
_horizBarStyle.margin = new RectOffset(0, 0, 4, 4);
|
var rectOffset = new RectOffset();
|
||||||
|
rectOffset.top = 4;
|
||||||
|
rectOffset.bottom = 4;
|
||||||
|
_horizBarStyle.margin = rectOffset;
|
||||||
_horizBarStyle.fixedHeight = 2;
|
_horizBarStyle.fixedHeight = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -73,7 +76,10 @@ namespace Explorer
|
|||||||
{
|
{
|
||||||
_horizBarSmallStyle = new GUIStyle();
|
_horizBarSmallStyle = new GUIStyle();
|
||||||
_horizBarSmallStyle.normal.background = Texture2D.whiteTexture;
|
_horizBarSmallStyle.normal.background = Texture2D.whiteTexture;
|
||||||
_horizBarSmallStyle.margin = new RectOffset(0, 0, 2, 2);
|
var rectOffset = new RectOffset();
|
||||||
|
rectOffset.top = 2;
|
||||||
|
rectOffset.bottom = 2;
|
||||||
|
_horizBarSmallStyle.margin = rectOffset;
|
||||||
_horizBarSmallStyle.fixedHeight = 1;
|
_horizBarSmallStyle.fixedHeight = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,7 +15,7 @@ namespace Explorer
|
|||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
if (m_mainCamera == null)
|
if (!m_mainCamera)
|
||||||
{
|
{
|
||||||
m_mainCamera = Camera.main;
|
m_mainCamera = Camera.main;
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,7 @@ namespace Explorer
|
|||||||
{
|
{
|
||||||
if (CppExplorer.ShowMenu)
|
if (CppExplorer.ShowMenu)
|
||||||
{
|
{
|
||||||
if (Input.GetKey(KeyCode.LeftShift) && Input.GetMouseButtonDown(1))
|
if (InputHelper.GetKey(KeyCode.LeftShift) && InputHelper.GetMouseButtonDown(1))
|
||||||
{
|
{
|
||||||
EnableInspect = !EnableInspect;
|
EnableInspect = !EnableInspect;
|
||||||
}
|
}
|
||||||
@ -35,7 +35,10 @@ namespace Explorer
|
|||||||
|
|
||||||
public static void InspectRaycast()
|
public static void InspectRaycast()
|
||||||
{
|
{
|
||||||
Ray ray = UnityHelpers.MainCamera.ScreenPointToRay(Input.mousePosition);
|
if (!UnityHelpers.MainCamera)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var ray = UnityHelpers.MainCamera.ScreenPointToRay(InputHelper.mousePosition);
|
||||||
|
|
||||||
if (Physics.Raycast(ray, out RaycastHit hit, 1000f))
|
if (Physics.Raycast(ray, out RaycastHit hit, 1000f))
|
||||||
{
|
{
|
||||||
@ -43,7 +46,7 @@ namespace Explorer
|
|||||||
|
|
||||||
m_objUnderMouseName = obj.transform.GetGameObjectPath();
|
m_objUnderMouseName = obj.transform.GetGameObjectPath();
|
||||||
|
|
||||||
if (Input.GetMouseButtonDown(0))
|
if (InputHelper.GetMouseButtonDown(0))
|
||||||
{
|
{
|
||||||
EnableInspect = false;
|
EnableInspect = false;
|
||||||
m_objUnderMouseName = "";
|
m_objUnderMouseName = "";
|
||||||
@ -63,7 +66,7 @@ namespace Explorer
|
|||||||
{
|
{
|
||||||
if (m_objUnderMouseName != "")
|
if (m_objUnderMouseName != "")
|
||||||
{
|
{
|
||||||
var pos = Input.mousePosition;
|
var pos = InputHelper.mousePosition;
|
||||||
var rect = new Rect(
|
var rect = new Rect(
|
||||||
pos.x - (Screen.width / 2), // x
|
pos.x - (Screen.width / 2), // x
|
||||||
Screen.height - pos.y - 50, // y
|
Screen.height - pos.y - 50, // y
|
||||||
|
@ -19,6 +19,8 @@ namespace Explorer
|
|||||||
private ScriptEvaluator _evaluator;
|
private ScriptEvaluator _evaluator;
|
||||||
private readonly StringBuilder _sb = new StringBuilder();
|
private readonly StringBuilder _sb = new StringBuilder();
|
||||||
|
|
||||||
|
private Vector2 inputAreaScroll;
|
||||||
|
|
||||||
private string MethodInput = "";
|
private string MethodInput = "";
|
||||||
private string UsingInput = "";
|
private string UsingInput = "";
|
||||||
|
|
||||||
@ -124,7 +126,12 @@ MelonLogger.Log(""hello world"");";
|
|||||||
GUI.skin.label.alignment = TextAnchor.UpperLeft;
|
GUI.skin.label.alignment = TextAnchor.UpperLeft;
|
||||||
|
|
||||||
GUILayout.Label("Enter code here as though it is a method body:", null);
|
GUILayout.Label("Enter code here as though it is a method body:", null);
|
||||||
MethodInput = GUILayout.TextArea(MethodInput, new GUILayoutOption[] { GUILayout.Height(250) });
|
|
||||||
|
inputAreaScroll = GUIUnstrip.BeginScrollView(inputAreaScroll, new GUILayoutOption[] { GUILayout.Height(250) });
|
||||||
|
|
||||||
|
MethodInput = GUILayout.TextArea(MethodInput, new GUILayoutOption[] { GUILayout.ExpandHeight(true) });
|
||||||
|
|
||||||
|
GUIUnstrip.EndScrollView();
|
||||||
|
|
||||||
if (GUILayout.Button("<color=cyan><b>Execute</b></color>", null))
|
if (GUILayout.Button("<color=cyan><b>Execute</b></color>", null))
|
||||||
{
|
{
|
||||||
|
@ -192,7 +192,7 @@ namespace Explorer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// --------- GUI Draw Function --------- //
|
// --------- GUI Draw Function --------- //
|
||||||
|
|
||||||
public override void DrawWindow()
|
public override void DrawWindow()
|
||||||
{
|
{
|
||||||
@ -215,11 +215,9 @@ namespace Explorer
|
|||||||
|
|
||||||
GUILayout.EndVertical();
|
GUILayout.EndVertical();
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch
|
||||||
{
|
{
|
||||||
MelonLogger.Log("Exception drawing ScenePage! " + e.GetType() + ", " + e.Message);
|
// supress
|
||||||
MelonLogger.Log(e.StackTrace);
|
|
||||||
m_currentTransform = null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -229,39 +227,7 @@ namespace Explorer
|
|||||||
|
|
||||||
// Current Scene label
|
// Current Scene label
|
||||||
GUILayout.Label("Current Scene:", new GUILayoutOption[] { GUILayout.Width(120) });
|
GUILayout.Label("Current Scene:", new GUILayoutOption[] { GUILayout.Width(120) });
|
||||||
try
|
SceneChangeButtons();
|
||||||
{
|
|
||||||
// Need to do 'ToList()' so the object isn't cleaned up by Il2Cpp GC.
|
|
||||||
var scenes = SceneManager.GetAllScenes().ToList();
|
|
||||||
|
|
||||||
if (scenes.Count > 1)
|
|
||||||
{
|
|
||||||
int changeWanted = 0;
|
|
||||||
if (GUILayout.Button("<", new GUILayoutOption[] { GUILayout.Width(30) }))
|
|
||||||
{
|
|
||||||
changeWanted = -1;
|
|
||||||
}
|
|
||||||
if (GUILayout.Button(">", new GUILayoutOption[] { GUILayout.Width(30) }))
|
|
||||||
{
|
|
||||||
changeWanted = 1;
|
|
||||||
}
|
|
||||||
if (changeWanted != 0)
|
|
||||||
{
|
|
||||||
int index = scenes.IndexOf(SceneManager.GetSceneByName(m_currentScene));
|
|
||||||
index += changeWanted;
|
|
||||||
if (index > scenes.Count - 1)
|
|
||||||
{
|
|
||||||
index = 0;
|
|
||||||
}
|
|
||||||
else if (index < 0)
|
|
||||||
{
|
|
||||||
index = scenes.Count - 1;
|
|
||||||
}
|
|
||||||
m_currentScene = scenes[index].name;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch { }
|
|
||||||
GUILayout.Label("<color=cyan>" + m_currentScene + "</color>", null); //new GUILayoutOption[] { GUILayout.Width(250) });
|
GUILayout.Label("<color=cyan>" + m_currentScene + "</color>", null); //new GUILayoutOption[] { GUILayout.Width(250) });
|
||||||
|
|
||||||
GUILayout.EndHorizontal();
|
GUILayout.EndHorizontal();
|
||||||
@ -269,7 +235,9 @@ namespace Explorer
|
|||||||
// ----- GameObject Search -----
|
// ----- GameObject Search -----
|
||||||
GUILayout.BeginHorizontal(GUI.skin.box, null);
|
GUILayout.BeginHorizontal(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 = GUILayout.TextField(m_searchInput, null);
|
m_searchInput = GUILayout.TextField(m_searchInput, null);
|
||||||
|
|
||||||
if (GUILayout.Button("Search", new GUILayoutOption[] { GUILayout.Width(80) }))
|
if (GUILayout.Button("Search", new GUILayoutOption[] { GUILayout.Width(80) }))
|
||||||
{
|
{
|
||||||
Search();
|
Search();
|
||||||
@ -279,6 +247,39 @@ namespace Explorer
|
|||||||
GUILayout.Space(5);
|
GUILayout.Space(5);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void SceneChangeButtons()
|
||||||
|
{
|
||||||
|
// Need to do 'ToList()' so the object isn't cleaned up by Il2Cpp GC.
|
||||||
|
var scenes = SceneManager.GetAllScenes().ToList();
|
||||||
|
|
||||||
|
if (scenes.Count > 1)
|
||||||
|
{
|
||||||
|
int changeWanted = 0;
|
||||||
|
if (GUILayout.Button("<", new GUILayoutOption[] { GUILayout.Width(30) }))
|
||||||
|
{
|
||||||
|
changeWanted = -1;
|
||||||
|
}
|
||||||
|
if (GUILayout.Button(">", new GUILayoutOption[] { GUILayout.Width(30) }))
|
||||||
|
{
|
||||||
|
changeWanted = 1;
|
||||||
|
}
|
||||||
|
if (changeWanted != 0)
|
||||||
|
{
|
||||||
|
int index = scenes.IndexOf(SceneManager.GetSceneByName(m_currentScene));
|
||||||
|
index += changeWanted;
|
||||||
|
if (index > scenes.Count - 1)
|
||||||
|
{
|
||||||
|
index = 0;
|
||||||
|
}
|
||||||
|
else if (index < 0)
|
||||||
|
{
|
||||||
|
index = scenes.Count - 1;
|
||||||
|
}
|
||||||
|
m_currentScene = scenes[index].name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void DrawPageButtons()
|
private void DrawPageButtons()
|
||||||
{
|
{
|
||||||
GUILayout.BeginHorizontal(null);
|
GUILayout.BeginHorizontal(null);
|
||||||
|
@ -172,16 +172,6 @@ namespace Explorer
|
|||||||
GUILayout.Label("Name Contains:", new GUILayoutOption[] { GUILayout.Width(100) });
|
GUILayout.Label("Name Contains:", new GUILayoutOption[] { GUILayout.Width(100) });
|
||||||
m_searchInput = GUILayout.TextField(m_searchInput, new GUILayoutOption[] { GUILayout.Width(200) });
|
m_searchInput = GUILayout.TextField(m_searchInput, new GUILayoutOption[] { GUILayout.Width(200) });
|
||||||
|
|
||||||
//GUI.skin.label.alignment = TextAnchor.MiddleRight;
|
|
||||||
//GUILayout.Label("Results per page:", new GUILayoutOption[] { GUILayout.Width(120) });
|
|
||||||
//var resultinput = m_limit.ToString();
|
|
||||||
//resultinput = GUILayout.TextField(resultinput, new GUILayoutOption[] { GUILayout.Width(55) });
|
|
||||||
//if (int.TryParse(resultinput, out int _i) && _i > 0)
|
|
||||||
//{
|
|
||||||
// m_limit = _i;
|
|
||||||
//}
|
|
||||||
//GUI.skin.label.alignment = TextAnchor.UpperLeft;
|
|
||||||
|
|
||||||
GUILayout.EndHorizontal();
|
GUILayout.EndHorizontal();
|
||||||
|
|
||||||
GUILayout.BeginHorizontal(null);
|
GUILayout.BeginHorizontal(null);
|
||||||
@ -263,7 +253,7 @@ namespace Explorer
|
|||||||
CacheResults(FindAllObjectsOfType(m_searchInput, m_typeInput));
|
CacheResults(FindAllObjectsOfType(m_searchInput, m_typeInput));
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<object> FindAllObjectsOfType(string _search, string _type)
|
private List<object> FindAllObjectsOfType(string searchQuery, string typeName)
|
||||||
{
|
{
|
||||||
Il2CppSystem.Type searchType = null;
|
Il2CppSystem.Type searchType = null;
|
||||||
|
|
||||||
@ -271,13 +261,18 @@ namespace Explorer
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var findType = ReflectionHelpers.GetTypeByName(_type);
|
if (ReflectionHelpers.GetTypeByName(typeName) is Type t)
|
||||||
searchType = Il2CppSystem.Type.GetType(findType.AssemblyQualifiedName);
|
{
|
||||||
//MelonLogger.Log("Search type: " + findType.AssemblyQualifiedName);
|
searchType = Il2CppSystem.Type.GetType(t.AssemblyQualifiedName);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new Exception($"Could not find a Type by the name of '{typeName}'!");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
MelonLogger.Log("Exception: " + e.GetType() + ", " + e.Message + "\r\n" + e.StackTrace);
|
MelonLogger.Log("Exception getting Search Type: " + e.GetType() + ", " + e.Message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (TypeMode == TypeFilter.Object)
|
else if (TypeMode == TypeFilter.Object)
|
||||||
@ -295,7 +290,10 @@ namespace Explorer
|
|||||||
|
|
||||||
if (!ReflectionHelpers.ObjectType.IsAssignableFrom(searchType))
|
if (!ReflectionHelpers.ObjectType.IsAssignableFrom(searchType))
|
||||||
{
|
{
|
||||||
MelonLogger.LogError("Your Custom Class Type must inherit from UnityEngine.Object!");
|
if (searchType != null)
|
||||||
|
{
|
||||||
|
MelonLogger.LogWarning("Your Custom Class Type must inherit from UnityEngine.Object!");
|
||||||
|
}
|
||||||
return new List<object>();
|
return new List<object>();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -310,7 +308,7 @@ namespace Explorer
|
|||||||
{
|
{
|
||||||
if (i >= 2000) break;
|
if (i >= 2000) break;
|
||||||
|
|
||||||
if (_search != "" && !obj.name.ToLower().Contains(_search.ToLower()))
|
if (searchQuery != "" && !obj.name.ToLower().Contains(searchQuery.ToLower()))
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -387,8 +385,8 @@ namespace Explorer
|
|||||||
public static IEnumerable<object> GetInstanceClassScanner()
|
public static IEnumerable<object> GetInstanceClassScanner()
|
||||||
{
|
{
|
||||||
var query = AppDomain.CurrentDomain.GetAssemblies()
|
var query = AppDomain.CurrentDomain.GetAssemblies()
|
||||||
.SelectMany(GetTypesSafe)
|
.SelectMany(t => t.TryGetTypes())
|
||||||
.Where(t => t.IsClass && !t.IsAbstract && !t.ContainsGenericParameters);
|
.Where(t => t.IsClass && !t.IsAbstract && !t.ContainsGenericParameters);
|
||||||
|
|
||||||
var flags = BindingFlags.Public | BindingFlags.Static;
|
var flags = BindingFlags.Public | BindingFlags.Static;
|
||||||
var flatFlags = flags | BindingFlags.FlattenHierarchy;
|
var flatFlags = flags | BindingFlags.FlattenHierarchy;
|
||||||
@ -430,7 +428,7 @@ namespace Explorer
|
|||||||
{
|
{
|
||||||
var t = ReflectionHelpers.GetActualType(obj);
|
var t = ReflectionHelpers.GetActualType(obj);
|
||||||
|
|
||||||
if (!FilterName(t.FullName) || ReflectionHelpers.IsArray(t) || ReflectionHelpers.IsList(t))
|
if (!FilterName(t.FullName) || ReflectionHelpers.IsEnumerable(t) || ReflectionHelpers.IsCppList(t))
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -439,12 +437,5 @@ namespace Explorer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IEnumerable<Type> GetTypesSafe(Assembly asm)
|
|
||||||
{
|
|
||||||
try { return asm.GetTypes(); }
|
|
||||||
catch (ReflectionTypeLoadException e) { return e.Types.Where(x => x != null); }
|
|
||||||
catch { return Enumerable.Empty<Type>(); }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
81
src/Tests/TestClass.cs
Normal file
81
src/Tests/TestClass.cs
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using MelonLoader;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace Explorer.Tests
|
||||||
|
{
|
||||||
|
public class TestClass
|
||||||
|
{
|
||||||
|
public static TestClass Instance => m_instance ?? (m_instance = new TestClass());
|
||||||
|
private static TestClass m_instance;
|
||||||
|
|
||||||
|
// Test indexed parameter
|
||||||
|
|
||||||
|
public string this[int arg0, string arg1]
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return $"arg0: {arg0}, arg1: {arg1}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test basic list
|
||||||
|
|
||||||
|
public static List<string> TestList = new List<string>
|
||||||
|
{
|
||||||
|
"1",
|
||||||
|
"2",
|
||||||
|
"3",
|
||||||
|
"etc..."
|
||||||
|
};
|
||||||
|
|
||||||
|
// Test a nested dictionary
|
||||||
|
|
||||||
|
public static Dictionary<int, Dictionary<string, int>> NestedDictionary = new Dictionary<int, Dictionary<string, int>>
|
||||||
|
{
|
||||||
|
{
|
||||||
|
1,
|
||||||
|
new Dictionary<string, int>
|
||||||
|
{
|
||||||
|
{
|
||||||
|
"Sub 1", 123
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Sub 2", 456
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
2,
|
||||||
|
new Dictionary<string, int>
|
||||||
|
{
|
||||||
|
{
|
||||||
|
"Sub 3", 789
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Sub 4", 000
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
// Test a basic method
|
||||||
|
|
||||||
|
public static Color TestMethod(float r, float g, float b, float a)
|
||||||
|
{
|
||||||
|
return new Color(r, g, b, a);
|
||||||
|
}
|
||||||
|
|
||||||
|
// A method with default arguments
|
||||||
|
|
||||||
|
public static Vector3 TestDefaultArgs(float arg0, float arg1, float arg2 = 5.0f)
|
||||||
|
{
|
||||||
|
return new Vector3(arg0, arg1, arg2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -41,8 +41,7 @@ namespace Explorer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
private static PropertyInfo m_scrollViewStatesInfo;
|
private static PropertyInfo m_scrollViewStatesInfo;
|
||||||
|
|
||||||
// ======= public methods ======= //
|
|
||||||
|
|
||||||
public static Rect GetLastRect()
|
public static Rect GetLastRect()
|
||||||
{
|
{
|
||||||
@ -73,7 +72,6 @@ namespace Explorer
|
|||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
ScrollFailed = true;
|
ScrollFailed = true;
|
||||||
return scroll;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -86,10 +84,8 @@ namespace Explorer
|
|||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
MelonLogger.Log("Exception on GUIUnstrip.BeginScrollView_ImplLayout: " + e.GetType() + ", " + e.Message + "\r\n" + e.StackTrace);
|
MelonLogger.Log("Exception on manual BeginScrollView: " + e.GetType() + ", " + e.Message + "\r\n" + e.StackTrace);
|
||||||
|
|
||||||
ManualUnstripFailed = true;
|
ManualUnstripFailed = true;
|
||||||
return scroll;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -113,8 +109,6 @@ namespace Explorer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ======= private methods ======= //
|
|
||||||
|
|
||||||
private static Vector2 BeginScrollView_ImplLayout(Vector2 scrollPosition, bool alwaysShowHorizontal, bool alwaysShowVertical,
|
private static Vector2 BeginScrollView_ImplLayout(Vector2 scrollPosition, bool alwaysShowHorizontal, bool alwaysShowVertical,
|
||||||
GUIStyle horizontalScrollbar, GUIStyle verticalScrollbar, GUIStyle background, params GUILayoutOption[] options)
|
GUIStyle horizontalScrollbar, GUIStyle verticalScrollbar, GUIStyle background, params GUILayoutOption[] options)
|
||||||
{
|
{
|
||||||
|
@ -48,12 +48,6 @@ namespace Explorer
|
|||||||
|
|
||||||
public bool GetObjectAsGameObject()
|
public bool GetObjectAsGameObject()
|
||||||
{
|
{
|
||||||
if (Target == null)
|
|
||||||
{
|
|
||||||
MelonLogger.Log("Target is null!");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
var targetType = Target.GetType();
|
var targetType = Target.GetType();
|
||||||
|
|
||||||
if (targetType == typeof(GameObject))
|
if (targetType == typeof(GameObject))
|
||||||
@ -108,6 +102,22 @@ namespace Explorer
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
if (Target == null)
|
||||||
|
{
|
||||||
|
MelonLogger.Log("Target is null!");
|
||||||
|
DestroyWindow();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if (Target is UnityEngine.Object uObj)
|
||||||
|
{
|
||||||
|
if (!uObj)
|
||||||
|
{
|
||||||
|
MelonLogger.Log("Target was destroyed!");
|
||||||
|
DestroyWindow();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!m_object && !GetObjectAsGameObject())
|
if (!m_object && !GetObjectAsGameObject())
|
||||||
{
|
{
|
||||||
throw new Exception("Object is null!");
|
throw new Exception("Object is null!");
|
||||||
|
@ -22,28 +22,32 @@ namespace Explorer
|
|||||||
private CacheObjectBase[] m_cachedMembersFiltered;
|
private CacheObjectBase[] m_cachedMembersFiltered;
|
||||||
|
|
||||||
public PageHelper Pages = new PageHelper();
|
public PageHelper Pages = new PageHelper();
|
||||||
//private int m_pageOffset;
|
|
||||||
//private int m_limitPerPage = 20;
|
|
||||||
|
|
||||||
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_filter = MemberTypes.Property;
|
||||||
private bool m_hideFailedReflection = false;
|
private bool m_hideFailedReflection = false;
|
||||||
|
|
||||||
// some extra caching
|
// some extra cast-caching
|
||||||
private UnityEngine.Object m_uObj;
|
private UnityEngine.Object m_uObj;
|
||||||
private Component m_component;
|
private Component m_component;
|
||||||
|
|
||||||
|
private static readonly HashSet<string> _memberBlacklist = new HashSet<string>
|
||||||
|
{
|
||||||
|
// Causes a crash
|
||||||
|
"Type.DeclaringMethod",
|
||||||
|
// Pointless (handled by Properties)
|
||||||
|
"get_",
|
||||||
|
"set_"
|
||||||
|
};
|
||||||
|
|
||||||
public override void Init()
|
public override void Init()
|
||||||
{
|
{
|
||||||
var type = ReflectionHelpers.GetActualType(Target);
|
TargetType = ReflectionHelpers.GetActualType(Target);
|
||||||
|
|
||||||
TargetType = type;
|
CacheMembers(ReflectionHelpers.GetAllBaseTypes(Target));
|
||||||
|
|
||||||
var types = ReflectionHelpers.GetAllBaseTypes(Target);
|
|
||||||
|
|
||||||
CacheMembers(types);
|
|
||||||
|
|
||||||
|
// cache the extra cast-caching
|
||||||
if (Target is Il2CppSystem.Object ilObject)
|
if (Target is Il2CppSystem.Object ilObject)
|
||||||
{
|
{
|
||||||
var unityObj = ilObject.TryCast<UnityEngine.Object>();
|
var unityObj = ilObject.TryCast<UnityEngine.Object>();
|
||||||
@ -58,17 +62,24 @@ namespace Explorer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
m_filter = MemberTypes.All;
|
|
||||||
m_autoUpdate = true;
|
|
||||||
Update();
|
|
||||||
|
|
||||||
m_autoUpdate = false;
|
|
||||||
m_filter = MemberTypes.Property;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Update()
|
public override void Update()
|
||||||
{
|
{
|
||||||
|
if (Target == null)
|
||||||
|
{
|
||||||
|
DestroyWindow();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if (Target is UnityEngine.Object uObj)
|
||||||
|
{
|
||||||
|
if (!uObj)
|
||||||
|
{
|
||||||
|
DestroyWindow();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
m_cachedMembersFiltered = m_allCachedMembers.Where(x => ShouldProcessMember(x)).ToArray();
|
m_cachedMembersFiltered = m_allCachedMembers.Where(x => ShouldProcessMember(x)).ToArray();
|
||||||
|
|
||||||
if (m_autoUpdate)
|
if (m_autoUpdate)
|
||||||
@ -87,28 +98,32 @@ namespace Explorer
|
|||||||
|
|
||||||
private bool ShouldProcessMember(CacheObjectBase holder)
|
private bool ShouldProcessMember(CacheObjectBase holder)
|
||||||
{
|
{
|
||||||
if (m_filter != MemberTypes.All && m_filter != holder.MemInfo?.MemberType) return false;
|
// check MemberTypes filter
|
||||||
|
if (m_filter != MemberTypes.All && m_filter != holder.MemInfo?.MemberType)
|
||||||
|
return false;
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(holder.ReflectionException) && m_hideFailedReflection) return false;
|
// hide failed reflection
|
||||||
|
if (!string.IsNullOrEmpty(holder.ReflectionException) && m_hideFailedReflection)
|
||||||
|
return false;
|
||||||
|
|
||||||
if (m_search == "" || holder.MemInfo == null) return true;
|
// see if we should do name search
|
||||||
|
if (m_search == "" || holder.MemInfo == null)
|
||||||
|
return true;
|
||||||
|
|
||||||
var name = holder.MemInfo.DeclaringType.Name + "." + holder.MemInfo.Name;
|
// ok do name search
|
||||||
|
return (holder.MemInfo.DeclaringType.Name + "." + holder.MemInfo.Name)
|
||||||
return name.ToLower().Contains(m_search.ToLower());
|
.ToLower()
|
||||||
|
.Contains(m_search.ToLower());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CacheMembers(Type[] types)
|
private void CacheMembers(Type[] types)
|
||||||
{
|
{
|
||||||
var list = new List<CacheObjectBase>();
|
var list = new List<CacheObjectBase>();
|
||||||
|
var cachedSigs = new List<string>();
|
||||||
var names = new List<string>();
|
|
||||||
|
|
||||||
foreach (var declaringType in types)
|
foreach (var declaringType in types)
|
||||||
{
|
{
|
||||||
MemberInfo[] infos;
|
MemberInfo[] infos;
|
||||||
string exception = null;
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
infos = declaringType.GetMembers(ReflectionHelpers.CommonFlags);
|
infos = declaringType.GetMembers(ReflectionHelpers.CommonFlags);
|
||||||
@ -120,6 +135,7 @@ namespace Explorer
|
|||||||
}
|
}
|
||||||
|
|
||||||
object target = Target;
|
object target = Target;
|
||||||
|
string exception = null;
|
||||||
|
|
||||||
if (target is Il2CppSystem.Object ilObject)
|
if (target is Il2CppSystem.Object ilObject)
|
||||||
{
|
{
|
||||||
@ -135,46 +151,56 @@ namespace Explorer
|
|||||||
|
|
||||||
foreach (var member in infos)
|
foreach (var member in infos)
|
||||||
{
|
{
|
||||||
if (member.MemberType == MemberTypes.Field || member.MemberType == MemberTypes.Property || member.MemberType == MemberTypes.Method)
|
// make sure member type is Field, Method of Property (4 / 8 / 16)
|
||||||
|
int m = (int)member.MemberType;
|
||||||
|
if (m < 4 || m > 16)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// check blacklisted members
|
||||||
|
if (_memberBlacklist.Any(it => member.Name.StartsWith(it)))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// compare signature to already cached members
|
||||||
|
var signature = $"{member.DeclaringType.Name}.{member.Name}";
|
||||||
|
if (member is MethodInfo mi)
|
||||||
{
|
{
|
||||||
var name = $"{member.DeclaringType.Name}.{member.Name}";
|
AppendParams(mi.GetParameters());
|
||||||
|
}
|
||||||
|
else if (member is PropertyInfo pi)
|
||||||
|
{
|
||||||
|
AppendParams(pi.GetIndexParameters());
|
||||||
|
}
|
||||||
|
|
||||||
// blacklist (should probably make a proper implementation)
|
void AppendParams(ParameterInfo[] _args)
|
||||||
if (name == "Type.DeclaringMethod" || member.Name.StartsWith("get_") || member.Name.StartsWith("set_")) //|| member.Name.Contains("Il2CppType")
|
{
|
||||||
|
signature += " (";
|
||||||
|
foreach (var param in _args)
|
||||||
{
|
{
|
||||||
continue;
|
signature += $"{param.ParameterType.Name} {param.Name}, ";
|
||||||
}
|
}
|
||||||
|
signature += ")";
|
||||||
|
}
|
||||||
|
|
||||||
if (member is MethodInfo mi)
|
if (cachedSigs.Contains(signature))
|
||||||
{
|
{
|
||||||
name += " (";
|
continue;
|
||||||
foreach (var param in mi.GetParameters())
|
}
|
||||||
{
|
|
||||||
name += param.ParameterType.Name + ", ";
|
|
||||||
}
|
|
||||||
name += ")";
|
|
||||||
}
|
|
||||||
if (names.Contains(name))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
try
|
||||||
|
{
|
||||||
|
var cached = CacheObjectBase.GetCacheObject(member, target);
|
||||||
|
if (cached != null)
|
||||||
{
|
{
|
||||||
var cached = CacheObjectBase.GetCacheObject(member, target);
|
cachedSigs.Add(signature);
|
||||||
if (cached != null)
|
list.Add(cached);
|
||||||
{
|
cached.ReflectionException = exception;
|
||||||
names.Add(name);
|
|
||||||
list.Add(cached);
|
|
||||||
cached.ReflectionException = exception;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
MelonLogger.LogWarning($"Exception caching member {name}!");
|
|
||||||
MelonLogger.Log(e.ToString());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
MelonLogger.LogWarning($"Exception caching member {signature}!");
|
||||||
|
MelonLogger.Log(e.ToString());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,15 +34,17 @@ namespace Explorer
|
|||||||
//var r = GUILayoutUtility.GetLastRect();
|
//var r = GUILayoutUtility.GetLastRect();
|
||||||
var r = GUIUnstrip.GetLastRect();
|
var r = GUIUnstrip.GetLastRect();
|
||||||
|
|
||||||
Vector2 mouse = GUIUtility.ScreenToGUIPoint(new Vector2(Input.mousePosition.x, Screen.height - Input.mousePosition.y));
|
var mousePos = InputHelper.mousePosition;
|
||||||
|
|
||||||
if (r.Contains(mouse) && Input.GetMouseButtonDown(0))
|
Vector2 mouse = GUIUtility.ScreenToGUIPoint(new Vector2(mousePos.x, Screen.height - mousePos.y));
|
||||||
|
|
||||||
|
if (r.Contains(mouse) && InputHelper.GetMouseButtonDown(0))
|
||||||
{
|
{
|
||||||
isResizing = true;
|
isResizing = true;
|
||||||
m_currentWindow = ID;
|
m_currentWindow = ID;
|
||||||
m_currentResize = new Rect(mouse.x, mouse.y, _rect.width, _rect.height);
|
m_currentResize = new Rect(mouse.x, mouse.y, _rect.width, _rect.height);
|
||||||
}
|
}
|
||||||
else if (!Input.GetMouseButton(0))
|
else if (!InputHelper.GetMouseButton(0))
|
||||||
{
|
{
|
||||||
isResizing = false;
|
isResizing = false;
|
||||||
}
|
}
|
||||||
|
@ -90,7 +90,7 @@ namespace Explorer
|
|||||||
{
|
{
|
||||||
createdNew = false;
|
createdNew = false;
|
||||||
|
|
||||||
if (Input.GetKey(KeyCode.LeftShift))
|
if (InputHelper.GetKey(KeyCode.LeftShift))
|
||||||
{
|
{
|
||||||
forceReflection = true;
|
forceReflection = true;
|
||||||
}
|
}
|
||||||
@ -192,7 +192,8 @@ namespace Explorer
|
|||||||
|
|
||||||
private static bool RectContainsMouse(Rect rect)
|
private static bool RectContainsMouse(Rect rect)
|
||||||
{
|
{
|
||||||
return rect.Contains(new Vector2(Input.mousePosition.x, Screen.height - Input.mousePosition.y));
|
var mousePos = InputHelper.mousePosition;
|
||||||
|
return rect.Contains(new Vector2(mousePos.x, Screen.height - mousePos.y));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static int NextWindowID()
|
public static int NextWindowID()
|
||||||
|
Reference in New Issue
Block a user