mirror of
https://github.com/GrahamKracker/UnityExplorer.git
synced 2025-07-03 20:12:33 +08:00
Compare commits
12 Commits
Author | SHA1 | Date | |
---|---|---|---|
8d648fec43 | |||
835a81765e | |||
51ed936e30 | |||
ac16587cdc | |||
1e1eaa6c38 | |||
af17ae82c6 | |||
e23341c2b1 | |||
080bde4a63 | |||
1d739a1936 | |||
a927b5ed21 | |||
642c97812c | |||
e43d3579de |
30
README.md
30
README.md
@ -30,20 +30,30 @@ Requires [MelonLoader](https://github.com/HerpDerpinstine/MelonLoader) to be ins
|
|||||||
## How to use
|
## How to use
|
||||||
|
|
||||||
* Press F7 to show or hide the menu.
|
* Press F7 to show or hide the menu.
|
||||||
* Simply browse through the scene, search for objects, etc, most of it is pretty self-explanatory.
|
* Use the Scene Explorer or the Object Search to start Exploring, or the C# Console to test some code.
|
||||||
|
* See below for more specific details.
|
||||||
|
|
||||||
### Features
|
### Mod Config
|
||||||
[](img.png)
|
|
||||||
|
There is a simple Mod Config for the CppExplorer, which is generated the first time you run it.
|
||||||
|
|
||||||
|
This config is generated to `Mods\CppExplorer\config.xml`. Edit the config while the game is closed if you wish to change it.
|
||||||
|
|
||||||
|
`Main_Menu_Toggle` (KeyCode)
|
||||||
|
* Sets the keybinding for the Main Menu toggle (show/hide all CppExplorer windows)
|
||||||
|
* See [this article](https://docs.unity3d.com/ScriptReference/KeyCode.html) for a full list of all accepted KeyCodes.
|
||||||
|
* Default: `F7`
|
||||||
|
|
||||||
|
`Default_Window_Size` (Vector2)
|
||||||
|
* Sets the default width and height for all CppExplorer windows when created.
|
||||||
|
* `x` is width, `y` is height.
|
||||||
|
* Default: `<x>550</x> <y>700</y>`
|
||||||
|
|
||||||
|
## Features
|
||||||
|
[](overview.png)
|
||||||
|
|
||||||
<i>An overview of the different CppExplorer menus.</i>
|
<i>An overview of the different CppExplorer menus.</i>
|
||||||
|
|
||||||
* Scene hierarchy explorer
|
|
||||||
* Search loaded assets with filters
|
|
||||||
* Traverse and manipulate GameObjects
|
|
||||||
* Generic Reflection inspector
|
|
||||||
* C# REPL Console
|
|
||||||
* Inspect-under-mouse
|
|
||||||
|
|
||||||
### Scene Explorer
|
### Scene Explorer
|
||||||
|
|
||||||
* A simple menu which allows you to traverse the Transform heirarchy of the scene.
|
* A simple menu which allows you to traverse the Transform heirarchy of the scene.
|
||||||
|
Binary file not shown.
Before Width: | Height: | Size: 524 KiB After Width: | Height: | Size: 523 KiB |
@ -44,12 +44,9 @@ namespace Explorer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ===== Abstract/Virtual Methods ===== //
|
|
||||||
|
|
||||||
public virtual void Init() { }
|
public virtual void Init() { }
|
||||||
public abstract void DrawValue(Rect window, float width);
|
|
||||||
|
|
||||||
// ===== Static Methods ===== //
|
public abstract void DrawValue(Rect window, float width);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Get CacheObject from only an object instance
|
/// Get CacheObject from only an object instance
|
||||||
@ -175,7 +172,7 @@ namespace Explorer
|
|||||||
{
|
{
|
||||||
holder = new CacheDictionary();
|
holder = new CacheDictionary();
|
||||||
}
|
}
|
||||||
else if (ReflectionHelpers.IsEnumerable(valueType) || ReflectionHelpers.IsCppList(valueType))
|
else if (ReflectionHelpers.IsEnumerable(valueType) || ReflectionHelpers.IsCppEnumerable(valueType))
|
||||||
{
|
{
|
||||||
holder = new CacheList();
|
holder = new CacheList();
|
||||||
}
|
}
|
||||||
@ -205,11 +202,8 @@ namespace Explorer
|
|||||||
}
|
}
|
||||||
|
|
||||||
holder.m_argumentInput = new string[holder.m_arguments.Length];
|
holder.m_argumentInput = new string[holder.m_arguments.Length];
|
||||||
|
|
||||||
if (!holder.HasParameters)
|
holder.UpdateValue();
|
||||||
{
|
|
||||||
holder.UpdateValue();
|
|
||||||
}
|
|
||||||
|
|
||||||
holder.Init();
|
holder.Init();
|
||||||
|
|
||||||
@ -229,7 +223,18 @@ namespace Explorer
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ======== Instance Methods =========
|
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()
|
public object[] ParseArguments()
|
||||||
{
|
{
|
||||||
@ -239,26 +244,41 @@ namespace Explorer
|
|||||||
var input = m_argumentInput[i];
|
var input = m_argumentInput[i];
|
||||||
var type = m_arguments[i].ParameterType;
|
var type = m_arguments[i].ParameterType;
|
||||||
|
|
||||||
if (type == typeof(string))
|
// First, try parse the input and use that.
|
||||||
|
if (!string.IsNullOrEmpty(input))
|
||||||
{
|
{
|
||||||
parsedArgs.Add(input);
|
// strings can obviously just be used directly
|
||||||
}
|
if (type == typeof(string))
|
||||||
else
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
{
|
||||||
parsedArgs.Add(type.GetMethod("Parse", new Type[] { typeof(string) }).Invoke(null, new object[] { input }));
|
parsedArgs.Add(input);
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
catch
|
else
|
||||||
{
|
{
|
||||||
//MelonLogger.Log($"Unable to parse '{input}' to type '{type.Name}'");
|
// try to invoke the parse method and use that.
|
||||||
|
try
|
||||||
|
{
|
||||||
|
parsedArgs.Add(type.GetMethod("Parse", new Type[] { typeof(string) })
|
||||||
|
.Invoke(null, new object[] { input }));
|
||||||
|
|
||||||
// try add a null arg i guess
|
continue;
|
||||||
parsedArgs.Add(null);
|
}
|
||||||
|
catch
|
||||||
//break;
|
{
|
||||||
|
MelonLogger.Log($"Argument #{i} '{m_arguments[i].Name}' ({type.Name}), could not parse input '{input}'.");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Didn't use input, see if there is a default value.
|
||||||
|
if (m_arguments[i].HasDefaultValue)
|
||||||
|
{
|
||||||
|
parsedArgs.Add(m_arguments[i].DefaultValue);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try add a null arg I guess
|
||||||
|
parsedArgs.Add(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
return parsedArgs.ToArray();
|
return parsedArgs.ToArray();
|
||||||
@ -271,6 +291,12 @@ namespace Explorer
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (HasParameters && !m_isEvaluating)
|
||||||
|
{
|
||||||
|
// Need to enter parameters first
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (MemInfo.MemberType == MemberTypes.Field)
|
if (MemInfo.MemberType == MemberTypes.Field)
|
||||||
@ -332,7 +358,7 @@ 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 const string EVALUATE_LABEL = "<color=lime>Evaluate</color>";
|
||||||
@ -375,10 +401,18 @@ namespace Explorer
|
|||||||
var input = m_argumentInput[i];
|
var input = m_argumentInput[i];
|
||||||
var type = m_arguments[i].ParameterType.Name;
|
var type = m_arguments[i].ParameterType.Name;
|
||||||
|
|
||||||
|
var label = "<color=#2df7b2>" + type + "</color> <color=#a6e9e9>" + name + "</color>";
|
||||||
|
if (m_arguments[i].HasDefaultValue)
|
||||||
|
{
|
||||||
|
label = $"<i>[{label} = {m_arguments[i].DefaultValue}]</i>";
|
||||||
|
}
|
||||||
|
|
||||||
GUILayout.BeginHorizontal(null);
|
GUILayout.BeginHorizontal(null);
|
||||||
GUILayout.Label(i.ToString(), new GUILayoutOption[] { GUILayout.Width(30) });
|
|
||||||
|
GUILayout.Label(i.ToString(), new GUILayoutOption[] { GUILayout.Width(20) });
|
||||||
m_argumentInput[i] = GUILayout.TextField(input, new GUILayoutOption[] { GUILayout.Width(150) });
|
m_argumentInput[i] = GUILayout.TextField(input, new GUILayoutOption[] { GUILayout.Width(150) });
|
||||||
GUILayout.Label("<color=#2df7b2>" + type + "</color> <color=cyan>" + name + "</color>", null);
|
GUILayout.Label(label, null);
|
||||||
|
|
||||||
GUILayout.EndHorizontal();
|
GUILayout.EndHorizontal();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -438,7 +472,11 @@ namespace Explorer
|
|||||||
{
|
{
|
||||||
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);
|
||||||
}
|
}
|
||||||
@ -463,7 +501,7 @@ 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 (m_arguments.Length > 0)
|
if (m_arguments.Length > 0 || this is CacheMethod)
|
||||||
{
|
{
|
||||||
m_richTextName += "(";
|
m_richTextName += "(";
|
||||||
var _params = "";
|
var _params = "";
|
||||||
|
@ -9,8 +9,6 @@ namespace Explorer
|
|||||||
interface IExpandHeight
|
interface IExpandHeight
|
||||||
{
|
{
|
||||||
bool IsExpanded { get; set; }
|
bool IsExpanded { get; set; }
|
||||||
|
float WhiteSpace { get; set; }
|
||||||
float WhiteSpace { get; set; }
|
|
||||||
float ButtonWidthOffset { get; set; }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -15,7 +15,6 @@ namespace Explorer
|
|||||||
{
|
{
|
||||||
public bool IsExpanded { get; set; }
|
public bool IsExpanded { get; set; }
|
||||||
public float WhiteSpace { get; set; } = 215f;
|
public float WhiteSpace { get; set; } = 215f;
|
||||||
public float ButtonWidthOffset { get; set; } = 350f;
|
|
||||||
|
|
||||||
public PageHelper Pages = new PageHelper();
|
public PageHelper Pages = new PageHelper();
|
||||||
|
|
||||||
@ -126,6 +125,10 @@ namespace Explorer
|
|||||||
m_keysType = type.GetGenericArguments()[0];
|
m_keysType = type.GetGenericArguments()[0];
|
||||||
m_valuesType = type.GetGenericArguments()[1];
|
m_valuesType = type.GetGenericArguments()[1];
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
MelonLogger.Log("TODO? Dictionary is of type: " + Value.GetType().FullName);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -203,11 +206,7 @@ namespace Explorer
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
float whitespace = WhiteSpace;
|
var whitespace = CalcWhitespace(window);
|
||||||
if (whitespace > 0)
|
|
||||||
{
|
|
||||||
ClampLabelWidth(window, ref whitespace);
|
|
||||||
}
|
|
||||||
|
|
||||||
int count = m_cachedKeys.Length;
|
int count = m_cachedKeys.Length;
|
||||||
|
|
||||||
@ -229,7 +228,7 @@ namespace Explorer
|
|||||||
var negativeWhitespace = window.width - (whitespace + 100f);
|
var negativeWhitespace = window.width - (whitespace + 100f);
|
||||||
|
|
||||||
GUI.skin.button.alignment = TextAnchor.MiddleLeft;
|
GUI.skin.button.alignment = TextAnchor.MiddleLeft;
|
||||||
string btnLabel = $"<color=#2df7b2>[{count}] Dictionary<{TypeOfKeys.FullName}, {TypeOfValues.FullName}></color>";
|
string btnLabel = $"[{count}] <color=#2df7b2>Dictionary<{TypeOfKeys.FullName}, {TypeOfValues.FullName}></color>";
|
||||||
if (GUILayout.Button(btnLabel, new GUILayoutOption[] { GUILayout.Width(negativeWhitespace) }))
|
if (GUILayout.Button(btnLabel, new GUILayoutOption[] { GUILayout.Width(negativeWhitespace) }))
|
||||||
{
|
{
|
||||||
WindowManager.InspectObject(Value, out bool _);
|
WindowManager.InspectObject(Value, out bool _);
|
||||||
|
@ -12,7 +12,6 @@ namespace Explorer
|
|||||||
{
|
{
|
||||||
public bool IsExpanded { get; set; }
|
public bool IsExpanded { get; set; }
|
||||||
public float WhiteSpace { get; set; } = 215f;
|
public float WhiteSpace { get; set; } = 215f;
|
||||||
public float ButtonWidthOffset { get; set; } = 290f;
|
|
||||||
|
|
||||||
public PageHelper Pages = new PageHelper();
|
public PageHelper Pages = new PageHelper();
|
||||||
|
|
||||||
@ -41,7 +40,7 @@ namespace Explorer
|
|||||||
private Type m_genericTypeDef;
|
private Type m_genericTypeDef;
|
||||||
|
|
||||||
// Cached ToArray method for Lists
|
// Cached ToArray method for Lists
|
||||||
public MethodInfo GenericToArrayMethod
|
public MethodInfo CppListToArrayMethod
|
||||||
{
|
{
|
||||||
get => GetGenericToArrayMethod();
|
get => GetGenericToArrayMethod();
|
||||||
}
|
}
|
||||||
@ -61,7 +60,7 @@ namespace Explorer
|
|||||||
{
|
{
|
||||||
if (m_enumerable == null && Value != null)
|
if (m_enumerable == null && Value != null)
|
||||||
{
|
{
|
||||||
m_enumerable = Value as IEnumerable ?? GetEnumerableFromIl2CppList();
|
m_enumerable = Value as IEnumerable ?? EnumerateWithReflection();
|
||||||
}
|
}
|
||||||
return m_enumerable;
|
return m_enumerable;
|
||||||
}
|
}
|
||||||
@ -101,21 +100,45 @@ namespace Explorer
|
|||||||
return m_itemProperty;
|
return m_itemProperty;
|
||||||
}
|
}
|
||||||
|
|
||||||
private IEnumerable GetEnumerableFromIl2CppList()
|
private IEnumerable EnumerateWithReflection()
|
||||||
{
|
{
|
||||||
if (Value == null) return null;
|
if (Value == null) return null;
|
||||||
|
|
||||||
if (GenericTypeDef == typeof(Il2CppSystem.Collections.Generic.List<>))
|
if (GenericTypeDef == typeof(Il2CppSystem.Collections.Generic.List<>))
|
||||||
{
|
{
|
||||||
return (IEnumerable)GenericToArrayMethod?.Invoke(Value, new object[0]);
|
return (IEnumerable)CppListToArrayMethod?.Invoke(Value, new object[0]);
|
||||||
|
}
|
||||||
|
else if (GenericTypeDef == typeof(Il2CppSystem.Collections.Generic.HashSet<>))
|
||||||
|
{
|
||||||
|
return CppHashSetToMono();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return ConvertIListToMono();
|
return CppIListToMono();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private IList ConvertIListToMono()
|
private IEnumerable CppHashSetToMono()
|
||||||
|
{
|
||||||
|
var set = new HashSet<object>();
|
||||||
|
|
||||||
|
// invoke GetEnumerator
|
||||||
|
var enumerator = Value.GetType().GetMethod("GetEnumerator").Invoke(Value, null);
|
||||||
|
// get the type of it
|
||||||
|
var enumeratorType = enumerator.GetType();
|
||||||
|
// reflect MoveNext and Current
|
||||||
|
var moveNext = enumeratorType.GetMethod("MoveNext");
|
||||||
|
var current = enumeratorType.GetProperty("Current");
|
||||||
|
// iterate
|
||||||
|
while ((bool)moveNext.Invoke(enumerator, null))
|
||||||
|
{
|
||||||
|
set.Add(current.GetValue(enumerator));
|
||||||
|
}
|
||||||
|
|
||||||
|
return set;
|
||||||
|
}
|
||||||
|
|
||||||
|
private IList CppIListToMono()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@ -245,11 +268,7 @@ namespace Explorer
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
float whitespace = WhiteSpace;
|
var whitespace = CalcWhitespace(window);
|
||||||
if (whitespace > 0)
|
|
||||||
{
|
|
||||||
ClampLabelWidth(window, ref whitespace);
|
|
||||||
}
|
|
||||||
|
|
||||||
int count = m_cachedEntries.Length;
|
int count = m_cachedEntries.Length;
|
||||||
|
|
||||||
|
@ -44,11 +44,9 @@ namespace Explorer
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var parsedArgs = ParseArguments();
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
ret = mi.Invoke(mi.IsStatic ? null : DeclaringInstance, parsedArgs.ToArray());
|
ret = mi.Invoke(mi.IsStatic ? null : DeclaringInstance, ParseArguments());
|
||||||
m_evaluated = true;
|
m_evaluated = true;
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
|
@ -16,7 +16,6 @@ namespace Explorer
|
|||||||
|
|
||||||
public bool IsExpanded { get; set; }
|
public bool IsExpanded { get; set; }
|
||||||
public float WhiteSpace { get; set; } = 215f;
|
public float WhiteSpace { get; set; } = 215f;
|
||||||
public float ButtonWidthOffset { get; set; } = 290f;
|
|
||||||
|
|
||||||
public override void UpdateValue()
|
public override void UpdateValue()
|
||||||
{
|
{
|
||||||
@ -50,20 +49,16 @@ namespace Explorer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var c = (Color)Value;
|
//var c = (Color)Value;
|
||||||
GUI.color = c;
|
//GUI.color = c;
|
||||||
GUILayout.Label($"<color=#2df7b2>Color:</color> {c.ToString()}", null);
|
GUILayout.Label($"<color=#2df7b2>Color:</color> {((Color)Value).ToString()}", null);
|
||||||
GUI.color = Color.white;
|
//GUI.color = Color.white;
|
||||||
|
|
||||||
if (CanWrite && IsExpanded)
|
if (CanWrite && IsExpanded)
|
||||||
{
|
{
|
||||||
GUILayout.EndHorizontal();
|
GUILayout.EndHorizontal();
|
||||||
|
|
||||||
float whitespace = WhiteSpace;
|
var whitespace = CalcWhitespace(window);
|
||||||
if (whitespace > 0)
|
|
||||||
{
|
|
||||||
ClampLabelWidth(window, ref whitespace);
|
|
||||||
}
|
|
||||||
|
|
||||||
GUILayout.BeginHorizontal(null);
|
GUILayout.BeginHorizontal(null);
|
||||||
GUILayout.Space(whitespace);
|
GUILayout.Space(whitespace);
|
||||||
|
@ -15,7 +15,6 @@ namespace Explorer
|
|||||||
|
|
||||||
public bool IsExpanded { get; set; }
|
public bool IsExpanded { get; set; }
|
||||||
public float WhiteSpace { get; set; } = 215f;
|
public float WhiteSpace { get; set; } = 215f;
|
||||||
public float ButtonWidthOffset { get; set; } = 290f;
|
|
||||||
|
|
||||||
public override void UpdateValue()
|
public override void UpdateValue()
|
||||||
{
|
{
|
||||||
@ -54,11 +53,7 @@ namespace Explorer
|
|||||||
{
|
{
|
||||||
GUILayout.EndHorizontal();
|
GUILayout.EndHorizontal();
|
||||||
|
|
||||||
float whitespace = WhiteSpace;
|
var whitespace = CalcWhitespace(window);
|
||||||
if (whitespace > 0)
|
|
||||||
{
|
|
||||||
ClampLabelWidth(window, ref whitespace);
|
|
||||||
}
|
|
||||||
|
|
||||||
GUILayout.BeginHorizontal(null);
|
GUILayout.BeginHorizontal(null);
|
||||||
GUILayout.Space(whitespace);
|
GUILayout.Space(whitespace);
|
||||||
|
@ -16,7 +16,6 @@ namespace Explorer
|
|||||||
|
|
||||||
public bool IsExpanded { get; set; }
|
public bool IsExpanded { get; set; }
|
||||||
public float WhiteSpace { get; set; } = 215f;
|
public float WhiteSpace { get; set; } = 215f;
|
||||||
public float ButtonWidthOffset { get; set; } = 290f;
|
|
||||||
|
|
||||||
public override void UpdateValue()
|
public override void UpdateValue()
|
||||||
{
|
{
|
||||||
@ -56,11 +55,7 @@ namespace Explorer
|
|||||||
{
|
{
|
||||||
GUILayout.EndHorizontal();
|
GUILayout.EndHorizontal();
|
||||||
|
|
||||||
float whitespace = WhiteSpace;
|
var whitespace = CalcWhitespace(window);
|
||||||
if (whitespace > 0)
|
|
||||||
{
|
|
||||||
ClampLabelWidth(window, ref whitespace);
|
|
||||||
}
|
|
||||||
|
|
||||||
GUILayout.BeginHorizontal(null);
|
GUILayout.BeginHorizontal(null);
|
||||||
GUILayout.Space(whitespace);
|
GUILayout.Space(whitespace);
|
||||||
|
@ -21,7 +21,6 @@ namespace Explorer
|
|||||||
|
|
||||||
public bool IsExpanded { get; set; }
|
public bool IsExpanded { get; set; }
|
||||||
public float WhiteSpace { get; set; } = 215f;
|
public float WhiteSpace { get; set; } = 215f;
|
||||||
public float ButtonWidthOffset { get; set; } = 290f;
|
|
||||||
|
|
||||||
public override void Init()
|
public override void Init()
|
||||||
{
|
{
|
||||||
@ -90,11 +89,8 @@ namespace Explorer
|
|||||||
if (CanWrite && IsExpanded)
|
if (CanWrite && IsExpanded)
|
||||||
{
|
{
|
||||||
GUILayout.EndHorizontal();
|
GUILayout.EndHorizontal();
|
||||||
float whitespace = WhiteSpace;
|
|
||||||
if (whitespace > 0)
|
var whitespace = CalcWhitespace(window);
|
||||||
{
|
|
||||||
ClampLabelWidth(window, ref whitespace);
|
|
||||||
}
|
|
||||||
|
|
||||||
// always draw x and y
|
// always draw x and y
|
||||||
GUILayout.BeginHorizontal(null);
|
GUILayout.BeginHorizontal(null);
|
||||||
|
69
src/Config/ModConfig.cs
Normal file
69
src/Config/ModConfig.cs
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Xml.Serialization;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace Explorer
|
||||||
|
{
|
||||||
|
public class ModConfig
|
||||||
|
{
|
||||||
|
[XmlIgnore] public static readonly XmlSerializer Serializer = new XmlSerializer(typeof(ModConfig));
|
||||||
|
|
||||||
|
[XmlIgnore] private const string EXPLORER_FOLDER = @"Mods\CppExplorer";
|
||||||
|
[XmlIgnore] private const string SETTINGS_PATH = EXPLORER_FOLDER + @"\config.xml";
|
||||||
|
|
||||||
|
[XmlIgnore] public static ModConfig Instance;
|
||||||
|
|
||||||
|
public KeyCode Main_Menu_Toggle = KeyCode.F7;
|
||||||
|
public Vector2 Default_Window_Size = new Vector2(550, 700);
|
||||||
|
|
||||||
|
public static void OnLoad()
|
||||||
|
{
|
||||||
|
if (!Directory.Exists(EXPLORER_FOLDER))
|
||||||
|
{
|
||||||
|
Directory.CreateDirectory(EXPLORER_FOLDER);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (LoadSettings()) return;
|
||||||
|
|
||||||
|
Instance = new ModConfig();
|
||||||
|
SaveSettings();
|
||||||
|
}
|
||||||
|
|
||||||
|
// returns true if settings successfully loaded
|
||||||
|
public static bool LoadSettings(bool checkExist = true)
|
||||||
|
{
|
||||||
|
if (checkExist && !File.Exists(SETTINGS_PATH))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using (var file = File.OpenRead(SETTINGS_PATH))
|
||||||
|
{
|
||||||
|
Instance = (ModConfig)Serializer.Deserialize(file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Instance != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void SaveSettings(bool checkExist = true)
|
||||||
|
{
|
||||||
|
if (checkExist && File.Exists(SETTINGS_PATH))
|
||||||
|
File.Delete(SETTINGS_PATH);
|
||||||
|
|
||||||
|
using (var file = File.Create(SETTINGS_PATH))
|
||||||
|
{
|
||||||
|
Serializer.Serialize(file, Instance);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -13,7 +13,7 @@ namespace Explorer
|
|||||||
public class CppExplorer : MelonMod
|
public class CppExplorer : MelonMod
|
||||||
{
|
{
|
||||||
public const string NAME = "CppExplorer";
|
public const string NAME = "CppExplorer";
|
||||||
public const string VERSION = "1.6.5";
|
public const string VERSION = "1.6.9";
|
||||||
public const string AUTHOR = "Sinai";
|
public const string AUTHOR = "Sinai";
|
||||||
public const string GUID = "com.sinai.cppexplorer";
|
public const string GUID = "com.sinai.cppexplorer";
|
||||||
|
|
||||||
@ -24,45 +24,30 @@ namespace Explorer
|
|||||||
get => m_showMenu;
|
get => m_showMenu;
|
||||||
set => SetShowMenu(value);
|
set => SetShowMenu(value);
|
||||||
}
|
}
|
||||||
private static bool m_showMenu;
|
public static bool m_showMenu;
|
||||||
|
|
||||||
public static bool ForceUnlockMouse
|
|
||||||
{
|
|
||||||
get => m_forceUnlock;
|
|
||||||
set => SetForceUnlock(value);
|
|
||||||
}
|
|
||||||
private static bool m_forceUnlock;
|
|
||||||
private static CursorLockMode m_lastLockMode;
|
|
||||||
private static bool m_lastVisibleState;
|
|
||||||
private static bool m_currentlySettingCursor = false;
|
|
||||||
|
|
||||||
public static bool ShouldForceMouse => ShowMenu && ForceUnlockMouse;
|
|
||||||
|
|
||||||
private static void SetShowMenu(bool show)
|
private static void SetShowMenu(bool show)
|
||||||
{
|
{
|
||||||
m_showMenu = show;
|
m_showMenu = show;
|
||||||
UpdateCursorControl();
|
CursorControl.UpdateCursorControl();
|
||||||
}
|
}
|
||||||
|
|
||||||
// ========== MonoBehaviour methods ==========
|
|
||||||
|
|
||||||
public override void OnApplicationStart()
|
public override void OnApplicationStart()
|
||||||
{
|
{
|
||||||
Instance = this;
|
Instance = this;
|
||||||
|
|
||||||
|
// First, load config
|
||||||
|
ModConfig.OnLoad();
|
||||||
|
|
||||||
|
// Setup InputHelper class (UnityEngine.Input)
|
||||||
InputHelper.Init();
|
InputHelper.Init();
|
||||||
|
|
||||||
|
// Create CppExplorer modules
|
||||||
new MainMenu();
|
new MainMenu();
|
||||||
new WindowManager();
|
new WindowManager();
|
||||||
|
|
||||||
// Get current cursor state and enable cursor
|
// Init cursor control
|
||||||
m_lastLockMode = Cursor.lockState;
|
CursorControl.Init();
|
||||||
m_lastVisibleState = Cursor.visible;
|
|
||||||
|
|
||||||
// Enable ShowMenu and ForceUnlockMouse
|
|
||||||
// (set m_showMenu to not call UpdateCursorState twice)
|
|
||||||
m_showMenu = true;
|
|
||||||
SetForceUnlock(true);
|
|
||||||
|
|
||||||
MelonLogger.Log($"CppExplorer {VERSION} initialized.");
|
MelonLogger.Log($"CppExplorer {VERSION} initialized.");
|
||||||
}
|
}
|
||||||
@ -76,22 +61,18 @@ namespace Explorer
|
|||||||
public override void OnUpdate()
|
public override void OnUpdate()
|
||||||
{
|
{
|
||||||
// Check main toggle key input
|
// Check main toggle key input
|
||||||
if (InputHelper.GetKeyDown(KeyCode.F7))
|
if (InputHelper.GetKeyDown(ModConfig.Instance.Main_Menu_Toggle))
|
||||||
{
|
{
|
||||||
ShowMenu = !ShowMenu;
|
ShowMenu = !ShowMenu;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ShowMenu)
|
if (ShowMenu)
|
||||||
{
|
{
|
||||||
// Check Force-Unlock input
|
CursorControl.Update();
|
||||||
if (InputHelper.GetKeyDown(KeyCode.LeftAlt))
|
InspectUnderMouse.Update();
|
||||||
{
|
|
||||||
ForceUnlockMouse = !ForceUnlockMouse;
|
|
||||||
}
|
|
||||||
|
|
||||||
MainMenu.Instance.Update();
|
MainMenu.Instance.Update();
|
||||||
WindowManager.Instance.Update();
|
WindowManager.Instance.Update();
|
||||||
InspectUnderMouse.Update();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -99,101 +80,14 @@ namespace Explorer
|
|||||||
{
|
{
|
||||||
if (!ShowMenu) return;
|
if (!ShowMenu) return;
|
||||||
|
|
||||||
|
var origSkin = GUI.skin;
|
||||||
|
GUI.skin = UIStyles.WindowSkin;
|
||||||
|
|
||||||
MainMenu.Instance.OnGUI();
|
MainMenu.Instance.OnGUI();
|
||||||
WindowManager.Instance.OnGUI();
|
WindowManager.Instance.OnGUI();
|
||||||
InspectUnderMouse.OnGUI();
|
InspectUnderMouse.OnGUI();
|
||||||
}
|
|
||||||
|
|
||||||
// =========== Cursor control ===========
|
GUI.skin = origSkin;
|
||||||
|
|
||||||
private static void SetForceUnlock(bool unlock)
|
|
||||||
{
|
|
||||||
m_forceUnlock = unlock;
|
|
||||||
UpdateCursorControl();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void UpdateCursorControl()
|
|
||||||
{
|
|
||||||
m_currentlySettingCursor = true;
|
|
||||||
if (ShouldForceMouse)
|
|
||||||
{
|
|
||||||
Cursor.lockState = CursorLockMode.None;
|
|
||||||
Cursor.visible = true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Cursor.lockState = m_lastLockMode;
|
|
||||||
Cursor.visible = m_lastVisibleState;
|
|
||||||
}
|
|
||||||
m_currentlySettingCursor = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Force mouse to stay unlocked and visible while UnlockMouse and ShowMenu are true.
|
|
||||||
// Also keep track of when anything else tries to set Cursor state, this will be the
|
|
||||||
// value that we set back to when we close the menu or disable force-unlock.
|
|
||||||
|
|
||||||
[HarmonyPatch(typeof(Cursor), nameof(Cursor.lockState), MethodType.Setter)]
|
|
||||||
public class Cursor_set_lockState
|
|
||||||
{
|
|
||||||
[HarmonyPrefix]
|
|
||||||
public static void Prefix(ref CursorLockMode value)
|
|
||||||
{
|
|
||||||
if (!m_currentlySettingCursor)
|
|
||||||
{
|
|
||||||
m_lastLockMode = value;
|
|
||||||
|
|
||||||
if (ShouldForceMouse)
|
|
||||||
{
|
|
||||||
value = CursorLockMode.None;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[HarmonyPatch(typeof(Cursor), nameof(Cursor.visible), MethodType.Setter)]
|
|
||||||
public class Cursor_set_visible
|
|
||||||
{
|
|
||||||
[HarmonyPrefix]
|
|
||||||
public static void Prefix(ref bool value)
|
|
||||||
{
|
|
||||||
if (!m_currentlySettingCursor)
|
|
||||||
{
|
|
||||||
m_lastVisibleState = value;
|
|
||||||
|
|
||||||
if (ShouldForceMouse)
|
|
||||||
{
|
|
||||||
value = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make it appear as though UnlockMouse is disabled to the rest of the application.
|
|
||||||
|
|
||||||
[HarmonyPatch(typeof(Cursor), nameof(Cursor.lockState), MethodType.Getter)]
|
|
||||||
public class Cursor_get_lockState
|
|
||||||
{
|
|
||||||
[HarmonyPostfix]
|
|
||||||
public static void Postfix(ref CursorLockMode __result)
|
|
||||||
{
|
|
||||||
if (ShouldForceMouse)
|
|
||||||
{
|
|
||||||
__result = m_lastLockMode;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[HarmonyPatch(typeof(Cursor), nameof(Cursor.visible), MethodType.Getter)]
|
|
||||||
public class Cursor_get_visible
|
|
||||||
{
|
|
||||||
[HarmonyPostfix]
|
|
||||||
public static void Postfix(ref bool __result)
|
|
||||||
{
|
|
||||||
if (ShouldForceMouse)
|
|
||||||
{
|
|
||||||
__result = m_lastVisibleState;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -29,6 +29,10 @@
|
|||||||
<HintPath>..\..\..\..\..\Steam\steamapps\common\Hellpoint\MelonLoader\Managed\Il2Cppmscorlib.dll</HintPath>
|
<HintPath>..\..\..\..\..\Steam\steamapps\common\Hellpoint\MelonLoader\Managed\Il2Cppmscorlib.dll</HintPath>
|
||||||
<Private>False</Private>
|
<Private>False</Private>
|
||||||
</Reference>
|
</Reference>
|
||||||
|
<Reference Include="Il2CppSystem.Core">
|
||||||
|
<HintPath>..\..\..\Steam\steamapps\common\Hellpoint\MelonLoader\Managed\Il2CppSystem.Core.dll</HintPath>
|
||||||
|
<Private>False</Private>
|
||||||
|
</Reference>
|
||||||
<Reference Include="mcs">
|
<Reference Include="mcs">
|
||||||
<HintPath>..\lib\mcs.dll</HintPath>
|
<HintPath>..\lib\mcs.dll</HintPath>
|
||||||
<Private>True</Private>
|
<Private>True</Private>
|
||||||
@ -73,7 +77,7 @@
|
|||||||
</Reference>
|
</Reference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Include="Helpers\IExpandHeight.cs" />
|
<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" />
|
||||||
@ -85,9 +89,11 @@
|
|||||||
<Compile Include="CachedObjects\Struct\CacheQuaternion.cs" />
|
<Compile Include="CachedObjects\Struct\CacheQuaternion.cs" />
|
||||||
<Compile Include="CachedObjects\Struct\CacheVector.cs" />
|
<Compile Include="CachedObjects\Struct\CacheVector.cs" />
|
||||||
<Compile Include="CachedObjects\Struct\CacheRect.cs" />
|
<Compile Include="CachedObjects\Struct\CacheRect.cs" />
|
||||||
|
<Compile Include="Config\ModConfig.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="Helpers\InputHelper.cs" />
|
||||||
|
<Compile Include="Menu\CursorControl.cs" />
|
||||||
<Compile Include="Tests\TestClass.cs" />
|
<Compile Include="Tests\TestClass.cs" />
|
||||||
<Compile Include="UnstripFixes\GUIUnstrip.cs" />
|
<Compile Include="UnstripFixes\GUIUnstrip.cs" />
|
||||||
<Compile Include="UnstripFixes\ScrollViewStateUnstrip.cs" />
|
<Compile Include="UnstripFixes\ScrollViewStateUnstrip.cs" />
|
||||||
@ -96,24 +102,24 @@
|
|||||||
<Compile Include="Helpers\ReflectionHelpers.cs" />
|
<Compile Include="Helpers\ReflectionHelpers.cs" />
|
||||||
<Compile Include="Helpers\UIHelpers.cs" />
|
<Compile Include="Helpers\UIHelpers.cs" />
|
||||||
<Compile Include="Helpers\UnityHelpers.cs" />
|
<Compile Include="Helpers\UnityHelpers.cs" />
|
||||||
<Compile Include="MainMenu\InspectUnderMouse.cs" />
|
<Compile Include="Menu\MainMenu\InspectUnderMouse.cs" />
|
||||||
<Compile Include="CachedObjects\CacheObjectBase.cs" />
|
<Compile Include="CachedObjects\CacheObjectBase.cs" />
|
||||||
<Compile Include="UnstripFixes\SliderHandlerUnstrip.cs" />
|
<Compile Include="UnstripFixes\SliderHandlerUnstrip.cs" />
|
||||||
<Compile Include="UnstripFixes\UnstripExtensions.cs" />
|
<Compile Include="UnstripFixes\UnstripExtensions.cs" />
|
||||||
<Compile Include="Windows\ResizeDrag.cs" />
|
<Compile Include="Menu\Windows\ResizeDrag.cs" />
|
||||||
<Compile Include="Windows\TabViewWindow.cs" />
|
<Compile Include="Menu\Windows\TabViewWindow.cs" />
|
||||||
<Compile Include="Windows\UIWindow.cs" />
|
<Compile Include="Menu\Windows\UIWindow.cs" />
|
||||||
<Compile Include="MainMenu\Pages\ConsolePage.cs" />
|
<Compile Include="Menu\MainMenu\Pages\ConsolePage.cs" />
|
||||||
<Compile Include="MainMenu\Pages\Console\REPL.cs" />
|
<Compile Include="Menu\MainMenu\Pages\Console\REPL.cs" />
|
||||||
<Compile Include="MainMenu\Pages\Console\REPLHelper.cs" />
|
<Compile Include="Menu\MainMenu\Pages\Console\REPLHelper.cs" />
|
||||||
<Compile Include="MainMenu\Pages\WindowPage.cs" />
|
<Compile Include="Menu\MainMenu\Pages\WindowPage.cs" />
|
||||||
<Compile Include="Windows\WindowManager.cs" />
|
<Compile Include="Menu\Windows\WindowManager.cs" />
|
||||||
<Compile Include="MainMenu\MainMenu.cs" />
|
<Compile Include="Menu\MainMenu\MainMenu.cs" />
|
||||||
<Compile Include="Windows\GameObjectWindow.cs" />
|
<Compile Include="Menu\Windows\GameObjectWindow.cs" />
|
||||||
<Compile Include="Windows\ReflectionWindow.cs" />
|
<Compile Include="Menu\Windows\ReflectionWindow.cs" />
|
||||||
<Compile Include="MainMenu\Pages\ScenePage.cs" />
|
<Compile Include="Menu\MainMenu\Pages\ScenePage.cs" />
|
||||||
<Compile Include="MainMenu\Pages\SearchPage.cs" />
|
<Compile Include="Menu\MainMenu\Pages\SearchPage.cs" />
|
||||||
<Compile Include="Helpers\UIStyles.cs" />
|
<Compile Include="Menu\UIStyles.cs" />
|
||||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||||
|
@ -88,7 +88,8 @@ namespace Explorer
|
|||||||
{
|
{
|
||||||
MelonLogger.Log("UnityEngine.Input is null, trying to load manually....");
|
MelonLogger.Log("UnityEngine.Input is null, trying to load manually....");
|
||||||
|
|
||||||
if ((TryLoad("UnityEngine.InputLegacyModule.dll") || TryLoad("UnityEngine.CoreModule.dll")) && Input != null)
|
if ((ReflectionHelpers.LoadModule("UnityEngine.InputLegacyModule.dll") || ReflectionHelpers.LoadModule("UnityEngine.CoreModule.dll"))
|
||||||
|
&& Input != null)
|
||||||
{
|
{
|
||||||
MelonLogger.Log("Ok!");
|
MelonLogger.Log("Ok!");
|
||||||
return true;
|
return true;
|
||||||
@ -98,23 +99,6 @@ namespace Explorer
|
|||||||
MelonLogger.Log("Could not load Input module!");
|
MelonLogger.Log("Could not load Input module!");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TryLoad(string module)
|
|
||||||
{
|
|
||||||
var path = $@"MelonLoader\Managed\{module}";
|
|
||||||
if (!File.Exists(path)) return false;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
Assembly.Load(File.ReadAllBytes(path));
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
MelonLogger.Log(e.GetType() + ", " + e.Message);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,8 @@ using System.Collections;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
using System.IO;
|
||||||
|
using MelonLoader;
|
||||||
using UnhollowerBaseLib;
|
using UnhollowerBaseLib;
|
||||||
using UnhollowerRuntimeLib;
|
using UnhollowerRuntimeLib;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
@ -37,13 +39,14 @@ namespace Explorer
|
|||||||
return typeof(IEnumerable).IsAssignableFrom(t);
|
return typeof(IEnumerable).IsAssignableFrom(t);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only Il2Cpp List needs this check. C# List is IEnumerable.
|
// Checks for Il2Cpp List or HashSet.
|
||||||
public static bool IsCppList(Type t)
|
public static bool IsCppEnumerable(Type t)
|
||||||
{
|
{
|
||||||
if (t.IsGenericType && t.GetGenericTypeDefinition() is Type g)
|
if (t.IsGenericType && t.GetGenericTypeDefinition() is Type g)
|
||||||
{
|
{
|
||||||
return typeof(Il2CppSystem.Collections.Generic.List<>).IsAssignableFrom(g)
|
return typeof(Il2CppSystem.Collections.Generic.List<>).IsAssignableFrom(g)
|
||||||
|| typeof(Il2CppSystem.Collections.Generic.IList<>).IsAssignableFrom(g);
|
|| typeof(Il2CppSystem.Collections.Generic.IList<>).IsAssignableFrom(g)
|
||||||
|
|| typeof(Il2CppSystem.Collections.Generic.HashSet<>).IsAssignableFrom(g);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -89,18 +92,20 @@ namespace Explorer
|
|||||||
{
|
{
|
||||||
if (obj == null) return null;
|
if (obj == null) return null;
|
||||||
|
|
||||||
|
// Need to use GetIl2CppType for Il2CppSystem Objects
|
||||||
if (obj is Il2CppSystem.Object ilObject)
|
if (obj is Il2CppSystem.Object ilObject)
|
||||||
{
|
{
|
||||||
var ilTypeName = ilObject.GetIl2CppType().AssemblyQualifiedName;
|
// Prevent weird behaviour when inspecting an Il2CppSystem.Type object.
|
||||||
|
if (ilObject is ILType)
|
||||||
if (Type.GetType(ilTypeName) is Type t && !t.FullName.Contains("System.RuntimeType"))
|
|
||||||
{
|
{
|
||||||
return t;
|
return typeof(ILType);
|
||||||
}
|
}
|
||||||
|
|
||||||
return ilObject.GetType();
|
// Get the System.Type using the qualified name, or fallback to GetType.
|
||||||
|
return Type.GetType(ilObject.GetIl2CppType().AssemblyQualifiedName) ?? obj.GetType();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// It's a normal object, this is fine
|
||||||
return obj.GetType();
|
return obj.GetType();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -109,17 +114,33 @@ namespace Explorer
|
|||||||
var list = new List<Type>();
|
var list = new List<Type>();
|
||||||
|
|
||||||
var type = GetActualType(obj);
|
var type = GetActualType(obj);
|
||||||
list.Add(type);
|
|
||||||
|
|
||||||
while (type.BaseType != null)
|
while (type != null)
|
||||||
{
|
{
|
||||||
type = type.BaseType;
|
|
||||||
list.Add(type);
|
list.Add(type);
|
||||||
|
type = type.BaseType;
|
||||||
}
|
}
|
||||||
|
|
||||||
return list.ToArray();
|
return list.ToArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static bool LoadModule(string module)
|
||||||
|
{
|
||||||
|
var path = $@"MelonLoader\Managed\{module}";
|
||||||
|
if (!File.Exists(path)) return false;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Assembly.Load(File.ReadAllBytes(path));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
MelonLogger.Log(e.GetType() + ", " + e.Message);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static string ExceptionToString(Exception e)
|
public static string ExceptionToString(Exception e)
|
||||||
{
|
{
|
||||||
if (IsFailedGeneric(e))
|
if (IsFailedGeneric(e))
|
||||||
|
238
src/Menu/CursorControl.cs
Normal file
238
src/Menu/CursorControl.cs
Normal file
@ -0,0 +1,238 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using UnityEngine;
|
||||||
|
using Harmony;
|
||||||
|
using MelonLoader;
|
||||||
|
using System.Reflection;
|
||||||
|
|
||||||
|
namespace Explorer
|
||||||
|
{
|
||||||
|
public class CursorControl
|
||||||
|
{
|
||||||
|
public static bool ForceUnlockMouse
|
||||||
|
{
|
||||||
|
get => m_forceUnlock;
|
||||||
|
set => SetForceUnlock(value);
|
||||||
|
}
|
||||||
|
private static bool m_forceUnlock;
|
||||||
|
private static CursorLockMode m_lastLockMode;
|
||||||
|
private static bool m_lastVisibleState;
|
||||||
|
private static bool m_currentlySettingCursor = false;
|
||||||
|
|
||||||
|
public static bool ShouldForceMouse => CppExplorer.ShowMenu && ForceUnlockMouse;
|
||||||
|
|
||||||
|
public static void Init()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Check if Cursor class is loaded
|
||||||
|
if (ReflectionHelpers.GetTypeByName("UnityEngine.Cursor") == null)
|
||||||
|
{
|
||||||
|
MelonLogger.Log("Trying to manually load Cursor module...");
|
||||||
|
|
||||||
|
if (ReflectionHelpers.LoadModule("UnityEngine.CoreModule"))
|
||||||
|
{
|
||||||
|
MelonLogger.Log("Ok!");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new Exception("Could not load UnityEngine.Cursor module!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get current cursor state and enable cursor
|
||||||
|
m_lastLockMode = Cursor.lockState;
|
||||||
|
m_lastVisibleState = Cursor.visible;
|
||||||
|
|
||||||
|
TryPatch("lockState", new HarmonyMethod(typeof(CursorControl).GetMethod(nameof(Prefix_set_lockState))), false, false);
|
||||||
|
TryPatch("visible", new HarmonyMethod(typeof(CursorControl).GetMethod(nameof(Prefix_set_visible))), false, false);
|
||||||
|
|
||||||
|
TryPatch("lockState", new HarmonyMethod(typeof(CursorControl).GetMethod(nameof(Postfix_get_lockState))), true, true);
|
||||||
|
TryPatch("visible", new HarmonyMethod(typeof(CursorControl).GetMethod(nameof(Postfix_get_visible))), true, true);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
MelonLogger.Log($"Exception on CursorControl.Init! {e.GetType()}, {e.Message}");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enable ShowMenu and ForceUnlockMouse
|
||||||
|
// (set m_showMenu directly to not call UpdateCursorState twice)
|
||||||
|
CppExplorer.m_showMenu = true;
|
||||||
|
ForceUnlockMouse = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void TryPatch(string property, HarmonyMethod patch, bool getter = true, bool postfix = false)
|
||||||
|
{
|
||||||
|
// Setup Harmony Patches
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var harmony = CppExplorer.Instance.harmonyInstance;
|
||||||
|
|
||||||
|
var prop = typeof(Cursor).GetProperty(property);
|
||||||
|
|
||||||
|
harmony.Patch(getter ? prop.GetGetMethod() : prop.GetSetMethod(),
|
||||||
|
postfix ? null : patch,
|
||||||
|
postfix ? patch : null);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
MelonLogger.Log($"[NON-FATAL] Couldn't patch a method: {e.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void SetForceUnlock(bool unlock)
|
||||||
|
{
|
||||||
|
m_forceUnlock = unlock;
|
||||||
|
UpdateCursorControl();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Update()
|
||||||
|
{
|
||||||
|
// Check Force-Unlock input
|
||||||
|
if (InputHelper.GetKeyDown(KeyCode.LeftAlt))
|
||||||
|
{
|
||||||
|
ForceUnlockMouse = !ForceUnlockMouse;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void UpdateCursorControl()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
m_currentlySettingCursor = true;
|
||||||
|
if (ShouldForceMouse)
|
||||||
|
{
|
||||||
|
Cursor.lockState = CursorLockMode.None;
|
||||||
|
Cursor.visible = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Cursor.lockState = m_lastLockMode;
|
||||||
|
Cursor.visible = m_lastVisibleState;
|
||||||
|
}
|
||||||
|
m_currentlySettingCursor = false;
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
MelonLogger.Log($"Exception setting Cursor state: {e.GetType()}, {e.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Force mouse to stay unlocked and visible while UnlockMouse and ShowMenu are true.
|
||||||
|
// Also keep track of when anything else tries to set Cursor state, this will be the
|
||||||
|
// value that we set back to when we close the menu or disable force-unlock.
|
||||||
|
|
||||||
|
[HarmonyPrefix]
|
||||||
|
public static void Prefix_set_lockState(ref CursorLockMode value)
|
||||||
|
{
|
||||||
|
if (!m_currentlySettingCursor)
|
||||||
|
{
|
||||||
|
m_lastLockMode = value;
|
||||||
|
|
||||||
|
if (ShouldForceMouse)
|
||||||
|
{
|
||||||
|
value = CursorLockMode.None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[HarmonyPrefix]
|
||||||
|
public static void Prefix_set_visible(ref bool value)
|
||||||
|
{
|
||||||
|
if (!m_currentlySettingCursor)
|
||||||
|
{
|
||||||
|
m_lastVisibleState = value;
|
||||||
|
|
||||||
|
if (ShouldForceMouse)
|
||||||
|
{
|
||||||
|
value = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[HarmonyPrefix]
|
||||||
|
public static void Postfix_get_lockState(ref CursorLockMode __result)
|
||||||
|
{
|
||||||
|
if (ShouldForceMouse)
|
||||||
|
{
|
||||||
|
__result = m_lastLockMode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[HarmonyPrefix]
|
||||||
|
public static void Postfix_get_visible(ref bool __result)
|
||||||
|
{
|
||||||
|
if (ShouldForceMouse)
|
||||||
|
{
|
||||||
|
__result = m_lastVisibleState;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//[HarmonyPatch(typeof(Cursor), nameof(Cursor.lockState), MethodType.Setter)]
|
||||||
|
//public class Cursor_set_lockState
|
||||||
|
//{
|
||||||
|
// [HarmonyPrefix]
|
||||||
|
// public static void Prefix(ref CursorLockMode value)
|
||||||
|
// {
|
||||||
|
// if (!m_currentlySettingCursor)
|
||||||
|
// {
|
||||||
|
// m_lastLockMode = value;
|
||||||
|
|
||||||
|
// if (ShouldForceMouse)
|
||||||
|
// {
|
||||||
|
// value = CursorLockMode.None;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
|
||||||
|
//[HarmonyPatch(typeof(Cursor), nameof(Cursor.visible), MethodType.Setter)]
|
||||||
|
//public class Cursor_set_visible
|
||||||
|
//{
|
||||||
|
// [HarmonyPrefix]
|
||||||
|
// public static void Prefix(ref bool value)
|
||||||
|
// {
|
||||||
|
// if (!m_currentlySettingCursor)
|
||||||
|
// {
|
||||||
|
// m_lastVisibleState = value;
|
||||||
|
|
||||||
|
// if (ShouldForceMouse)
|
||||||
|
// {
|
||||||
|
// value = true;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
|
||||||
|
//// Make it appear as though UnlockMouse is disabled to the rest of the application.
|
||||||
|
|
||||||
|
//[HarmonyPatch(typeof(Cursor), nameof(Cursor.lockState), MethodType.Getter)]
|
||||||
|
//public class Cursor_get_lockState
|
||||||
|
//{
|
||||||
|
// [HarmonyPostfix]
|
||||||
|
// public static void Postfix(ref CursorLockMode __result)
|
||||||
|
// {
|
||||||
|
// if (ShouldForceMouse)
|
||||||
|
// {
|
||||||
|
// __result = m_lastLockMode;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
|
||||||
|
//[HarmonyPatch(typeof(Cursor), nameof(Cursor.visible), MethodType.Getter)]
|
||||||
|
//public class Cursor_get_visible
|
||||||
|
//{
|
||||||
|
// [HarmonyPostfix]
|
||||||
|
// public static void Postfix(ref bool __result)
|
||||||
|
// {
|
||||||
|
// if (ShouldForceMouse)
|
||||||
|
// {
|
||||||
|
// __result = m_lastVisibleState;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
}
|
||||||
|
}
|
@ -27,8 +27,9 @@ namespace Explorer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public const int MainWindowID = 10;
|
public const int MainWindowID = 5000;
|
||||||
public static Rect MainRect = new Rect(5, 5, 550, 700);
|
public static Rect MainRect = new Rect(5,5, ModConfig.Instance.Default_Window_Size.x,ModConfig.Instance.Default_Window_Size.y);
|
||||||
|
|
||||||
private static readonly List<WindowPage> Pages = new List<WindowPage>();
|
private static readonly List<WindowPage> Pages = new List<WindowPage>();
|
||||||
private static int m_currentPage = 0;
|
private static int m_currentPage = 0;
|
||||||
|
|
||||||
@ -51,19 +52,14 @@ namespace Explorer
|
|||||||
|
|
||||||
public void OnGUI()
|
public void OnGUI()
|
||||||
{
|
{
|
||||||
var origSkin = GUI.skin;
|
|
||||||
GUI.skin = UIStyles.WindowSkin;
|
|
||||||
|
|
||||||
MainRect = GUI.Window(MainWindowID, MainRect, (GUI.WindowFunction)MainWindow, CppExplorer.NAME);
|
MainRect = GUI.Window(MainWindowID, MainRect, (GUI.WindowFunction)MainWindow, CppExplorer.NAME);
|
||||||
|
|
||||||
GUI.skin = origSkin;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void MainWindow(int id)
|
private void MainWindow(int id)
|
||||||
{
|
{
|
||||||
GUI.DragWindow(new Rect(0, 0, MainRect.width - 90, 20));
|
GUI.DragWindow(new Rect(0, 0, MainRect.width - 90, 20));
|
||||||
|
|
||||||
if (GUI.Button(new Rect(MainRect.width - 90, 2, 80, 20), "Hide (F7)"))
|
if (GUI.Button(new Rect(MainRect.width - 90, 2, 80, 20), $"Hide ({ModConfig.Instance.Main_Menu_Toggle})"))
|
||||||
{
|
{
|
||||||
CppExplorer.ShowMenu = false;
|
CppExplorer.ShowMenu = false;
|
||||||
return;
|
return;
|
||||||
@ -107,9 +103,9 @@ namespace Explorer
|
|||||||
GUI.color = Color.white;
|
GUI.color = Color.white;
|
||||||
InspectUnderMouse.EnableInspect = GUILayout.Toggle(InspectUnderMouse.EnableInspect, "Inspect Under Mouse (Shift + RMB)", null);
|
InspectUnderMouse.EnableInspect = GUILayout.Toggle(InspectUnderMouse.EnableInspect, "Inspect Under Mouse (Shift + RMB)", null);
|
||||||
|
|
||||||
bool mouseState = CppExplorer.ForceUnlockMouse;
|
bool mouseState = CursorControl.ForceUnlockMouse;
|
||||||
bool setMouse = GUILayout.Toggle(mouseState, "Force Unlock Mouse (Left Alt)", null);
|
bool setMouse = GUILayout.Toggle(mouseState, "Force Unlock Mouse (Left Alt)", null);
|
||||||
if (setMouse != mouseState) CppExplorer.ForceUnlockMouse = setMouse;
|
if (setMouse != mouseState) CursorControl.ForceUnlockMouse = setMouse;
|
||||||
|
|
||||||
WindowManager.TabView = GUILayout.Toggle(WindowManager.TabView, "Tab View", null);
|
WindowManager.TabView = GUILayout.Toggle(WindowManager.TabView, "Tab View", null);
|
||||||
GUILayout.EndHorizontal();
|
GUILayout.EndHorizontal();
|
@ -428,7 +428,7 @@ namespace Explorer
|
|||||||
{
|
{
|
||||||
var t = ReflectionHelpers.GetActualType(obj);
|
var t = ReflectionHelpers.GetActualType(obj);
|
||||||
|
|
||||||
if (!FilterName(t.FullName) || ReflectionHelpers.IsEnumerable(t) || ReflectionHelpers.IsCppList(t))
|
if (!FilterName(t.FullName) || ReflectionHelpers.IsEnumerable(t) || ReflectionHelpers.IsCppEnumerable(t))
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
@ -28,20 +28,30 @@ namespace Explorer
|
|||||||
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> _typeAndMemberBlacklist = new HashSet<string>
|
||||||
|
{
|
||||||
|
// Causes a crash
|
||||||
|
"Type.DeclaringMethod",
|
||||||
|
};
|
||||||
|
|
||||||
|
private static readonly HashSet<string> _methodStartsWithBlacklist = new HashSet<string>
|
||||||
|
{
|
||||||
|
// 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>();
|
||||||
@ -56,13 +66,6 @@ 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()
|
||||||
@ -99,28 +102,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);
|
||||||
@ -132,6 +139,7 @@ namespace Explorer
|
|||||||
}
|
}
|
||||||
|
|
||||||
object target = Target;
|
object target = Target;
|
||||||
|
string exception = null;
|
||||||
|
|
||||||
if (target is Il2CppSystem.Object ilObject)
|
if (target is Il2CppSystem.Object ilObject)
|
||||||
{
|
{
|
||||||
@ -147,56 +155,60 @@ 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
|
||||||
|
var name = member.DeclaringType.Name + "." + member.Name;
|
||||||
|
if (_typeAndMemberBlacklist.Any(it => it == name))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (_methodStartsWithBlacklist.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} {param.Name}, ";
|
|
||||||
}
|
|
||||||
name += ")";
|
|
||||||
}
|
|
||||||
else if (member is PropertyInfo pi)
|
|
||||||
{
|
|
||||||
name += " (";
|
|
||||||
foreach (var param in pi.GetIndexParameters())
|
|
||||||
{
|
|
||||||
name += $"{param.ParameterType.Name} {param.Name}, ";
|
|
||||||
}
|
|
||||||
name += ")";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (names.Contains(name))
|
try
|
||||||
|
{
|
||||||
|
var cached = CacheObjectBase.GetCacheObject(member, target);
|
||||||
|
if (cached != null)
|
||||||
{
|
{
|
||||||
continue;
|
cachedSigs.Add(signature);
|
||||||
}
|
list.Add(cached);
|
||||||
|
cached.ReflectionException = exception;
|
||||||
try
|
|
||||||
{
|
|
||||||
var cached = CacheObjectBase.GetCacheObject(member, target);
|
|
||||||
if (cached != null)
|
|
||||||
{
|
|
||||||
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());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -17,7 +17,7 @@ namespace Explorer
|
|||||||
public object Target;
|
public object Target;
|
||||||
|
|
||||||
public int windowID;
|
public int windowID;
|
||||||
public Rect m_rect = new Rect(0, 0, 550, 700);
|
public Rect m_rect = new Rect(0,0, ModConfig.Instance.Default_Window_Size.x,ModConfig.Instance.Default_Window_Size.y);
|
||||||
|
|
||||||
public Vector2 scroll = Vector2.zero;
|
public Vector2 scroll = Vector2.zero;
|
||||||
|
|
||||||
@ -49,15 +49,7 @@ namespace Explorer
|
|||||||
|
|
||||||
public void OnGUI()
|
public void OnGUI()
|
||||||
{
|
{
|
||||||
if (CppExplorer.ShowMenu)
|
m_rect = GUI.Window(windowID, m_rect, (GUI.WindowFunction)WindowFunction, Title);
|
||||||
{
|
|
||||||
var origSkin = GUI.skin;
|
|
||||||
|
|
||||||
GUI.skin = UIStyles.WindowSkin;
|
|
||||||
m_rect = GUI.Window(windowID, m_rect, (GUI.WindowFunction)WindowFunction, Title);
|
|
||||||
|
|
||||||
GUI.skin = origSkin;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Header()
|
public void Header()
|
@ -14,21 +14,26 @@ namespace Explorer.Tests
|
|||||||
public static TestClass Instance => m_instance ?? (m_instance = new TestClass());
|
public static TestClass Instance => m_instance ?? (m_instance = new TestClass());
|
||||||
private static TestClass m_instance;
|
private static TestClass m_instance;
|
||||||
|
|
||||||
public string this[int index]
|
public TestClass()
|
||||||
{
|
{
|
||||||
get
|
ILHashSetTest = new Il2CppSystem.Collections.Generic.HashSet<string>();
|
||||||
{
|
ILHashSetTest.Add("1");
|
||||||
return $"int indexer: {index}";
|
ILHashSetTest.Add("2");
|
||||||
}
|
ILHashSetTest.Add("3");
|
||||||
}
|
}
|
||||||
|
|
||||||
public string this[string stringIndex]
|
// test HashSets
|
||||||
|
|
||||||
|
public static HashSet<string> HashSetTest = new HashSet<string>
|
||||||
{
|
{
|
||||||
get
|
"One",
|
||||||
{
|
"Two",
|
||||||
return $"string indexer: {stringIndex}";
|
"Three"
|
||||||
}
|
};
|
||||||
}
|
|
||||||
|
public static Il2CppSystem.Collections.Generic.HashSet<string> ILHashSetTest;
|
||||||
|
|
||||||
|
// Test indexed parameter
|
||||||
|
|
||||||
public string this[int arg0, string arg1]
|
public string this[int arg0, string arg1]
|
||||||
{
|
{
|
||||||
@ -38,6 +43,8 @@ namespace Explorer.Tests
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Test basic list
|
||||||
|
|
||||||
public static List<string> TestList = new List<string>
|
public static List<string> TestList = new List<string>
|
||||||
{
|
{
|
||||||
"1",
|
"1",
|
||||||
@ -46,29 +53,48 @@ namespace Explorer.Tests
|
|||||||
"etc..."
|
"etc..."
|
||||||
};
|
};
|
||||||
|
|
||||||
public static Dictionary<int, List<string>> NestedDictionary = new Dictionary<int, List<string>>
|
// Test a nested dictionary
|
||||||
|
|
||||||
|
public static Dictionary<int, Dictionary<string, int>> NestedDictionary = new Dictionary<int, Dictionary<string, int>>
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
123,
|
1,
|
||||||
new List<string>
|
new Dictionary<string, int>
|
||||||
{
|
{
|
||||||
"One",
|
{
|
||||||
"Two"
|
"Sub 1", 123
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Sub 2", 456
|
||||||
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
567,
|
2,
|
||||||
new List<string>
|
new Dictionary<string, int>
|
||||||
{
|
{
|
||||||
"One",
|
{
|
||||||
"Two"
|
"Sub 3", 789
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Sub 4", 000
|
||||||
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Test a basic method
|
||||||
|
|
||||||
public static Color TestMethod(float r, float g, float b, float a)
|
public static Color TestMethod(float r, float g, float b, float a)
|
||||||
{
|
{
|
||||||
return new Color(r, g, b, 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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user