mirror of
https://github.com/sinai-dev/UnityExplorer.git
synced 2025-06-15 22:07:48 +08:00
1.4.5 (pre-release)
* Added MethodInfo support for basic methods with no arguments. * Added support for missing primitive types (char, short, byte) * Added CacheDictionary class (currently unsupported) * Cleaned up some stuff, using System.Reflection.MemberType instead of a custom enum.
This commit is contained in:
parent
6e644b4f50
commit
92fe1dc704
@ -45,7 +45,6 @@ Requires [MelonLoader](https://github.com/HerpDerpinstine/MelonLoader) to be ins
|
||||
CppExplorer can force the mouse to be visible and unlocked when the menu is open, if you have enabled "Force Unlock Mouse" (Left-Alt toggle). However, you may also want to prevent the mouse clicking-through onto the game behind CppExplorer, this is possible but it requires specific patches for that game.
|
||||
|
||||
* For VRChat, use [VRCExplorerMouseControl](https://github.com/sinaioutlander/VRCExplorerMouseControl)
|
||||
* For Hellpoint, use [HPExplorerMouseControl](https://github.com/sinaioutlander/Hellpoint-Mods/tree/master/HPExplorerMouseControl/HPExplorerMouseControl)
|
||||
* You can create your own mini-plugin using one of the two plugins above as an example. Usually only 1 or 2 simple Harmony patches are needed to fix the problem (if you want to submit that here, feel free to make a PR to this Readme).
|
||||
|
||||
## Images
|
||||
|
34
src/CachedObjects/CacheDictionary.cs
Normal file
34
src/CachedObjects/CacheDictionary.cs
Normal file
@ -0,0 +1,34 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using MelonLoader;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Explorer
|
||||
{
|
||||
public class CacheDictionary : CacheObjectBase
|
||||
{
|
||||
|
||||
|
||||
public override void Init()
|
||||
{
|
||||
//base.Init();
|
||||
|
||||
Value = "Unsupported";
|
||||
}
|
||||
|
||||
public override void UpdateValue()
|
||||
{
|
||||
//base.UpdateValue();
|
||||
|
||||
|
||||
}
|
||||
|
||||
public override void DrawValue(Rect window, float width)
|
||||
{
|
||||
GUILayout.Label("<color=red>Dictionary (unsupported)</color>", null);
|
||||
}
|
||||
}
|
||||
}
|
@ -8,7 +8,7 @@ using UnityEngine;
|
||||
|
||||
namespace Explorer
|
||||
{
|
||||
public class CacheEnum : CacheObject
|
||||
public class CacheEnum : CacheObjectBase
|
||||
{
|
||||
public Type EnumType;
|
||||
public string[] EnumNames;
|
||||
|
@ -8,7 +8,7 @@ using UnityEngine;
|
||||
|
||||
namespace Explorer
|
||||
{
|
||||
public class CacheGameObject : CacheObject
|
||||
public class CacheGameObject : CacheObjectBase
|
||||
{
|
||||
private GameObject GameObj
|
||||
{
|
||||
|
@ -3,34 +3,40 @@ using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using MelonLoader;
|
||||
using Mono.CSharp;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Explorer
|
||||
{
|
||||
public partial class CacheList : CacheObject
|
||||
public partial class CacheList : CacheObjectBase
|
||||
{
|
||||
public bool IsExpanded { get; set; }
|
||||
public int ArrayOffset { get; set; }
|
||||
public int ArrayLimit { get; set; } = 20;
|
||||
|
||||
|
||||
public float WhiteSpace = 215f;
|
||||
public float ButtonWidthOffset = 290f;
|
||||
|
||||
public Type EntryType
|
||||
{
|
||||
get
|
||||
{
|
||||
if (m_entryType == null)
|
||||
{
|
||||
switch (this.MemberInfoType)
|
||||
if (this.MemberInfo != null)
|
||||
{
|
||||
case ReflectionWindow.MemberInfoType.Field:
|
||||
m_entryType = (MemberInfo as FieldInfo).FieldType.GetGenericArguments()[0];
|
||||
break;
|
||||
case ReflectionWindow.MemberInfoType.Property:
|
||||
m_entryType = (MemberInfo as PropertyInfo).PropertyType.GetGenericArguments()[0];
|
||||
break;
|
||||
switch (this.MemberInfoType)
|
||||
{
|
||||
case MemberTypes.Field:
|
||||
m_entryType = (MemberInfo as FieldInfo).FieldType.GetGenericArguments()[0];
|
||||
break;
|
||||
case MemberTypes.Property:
|
||||
m_entryType = (MemberInfo as PropertyInfo).PropertyType.GetGenericArguments()[0];
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (Value != null)
|
||||
{
|
||||
m_entryType = Value.GetType().GetGenericArguments()[0];
|
||||
}
|
||||
}
|
||||
return m_entryType;
|
||||
@ -55,7 +61,7 @@ namespace Explorer
|
||||
}
|
||||
|
||||
private IEnumerable m_enumerable;
|
||||
private CacheObject[] m_cachedEntries;
|
||||
private CacheObjectBase[] m_cachedEntries;
|
||||
|
||||
public MethodInfo GenericToArrayMethod
|
||||
{
|
||||
@ -97,7 +103,7 @@ namespace Explorer
|
||||
|
||||
GUI.skin.button.alignment = TextAnchor.MiddleLeft;
|
||||
string btnLabel = "<color=yellow>[" + count + "] " + EntryType + "</color>";
|
||||
if (GUILayout.Button(btnLabel, new GUILayoutOption[] { GUILayout.MaxWidth(window.width - 260) }))
|
||||
if (GUILayout.Button(btnLabel, new GUILayoutOption[] { GUILayout.MaxWidth(window.width - ButtonWidthOffset) }))
|
||||
{
|
||||
WindowManager.InspectObject(Value, out bool _);
|
||||
}
|
||||
@ -107,8 +113,12 @@ namespace Explorer
|
||||
|
||||
if (IsExpanded)
|
||||
{
|
||||
float whitespace = 215;
|
||||
ClampLabelWidth(window, ref whitespace);
|
||||
float whitespace = WhiteSpace;
|
||||
|
||||
if (whitespace > 0)
|
||||
{
|
||||
ClampLabelWidth(window, ref whitespace);
|
||||
}
|
||||
|
||||
if (count > ArrayLimit)
|
||||
{
|
||||
@ -120,11 +130,11 @@ namespace Explorer
|
||||
int maxOffset = (int)Mathf.Ceil((float)(count / (decimal)ArrayLimit)) - 1;
|
||||
GUILayout.Label($"Page {ArrayOffset + 1}/{maxOffset + 1}", new GUILayoutOption[] { GUILayout.Width(80) });
|
||||
// prev/next page buttons
|
||||
if (GUILayout.Button("< Prev", null))
|
||||
if (GUILayout.Button("< Prev", new GUILayoutOption[] { GUILayout.Width(60) }))
|
||||
{
|
||||
if (ArrayOffset > 0) ArrayOffset--;
|
||||
}
|
||||
if (GUILayout.Button("Next >", null))
|
||||
if (GUILayout.Button("Next >", new GUILayoutOption[] { GUILayout.Width(60) }))
|
||||
{
|
||||
if (ArrayOffset < maxOffset) ArrayOffset++;
|
||||
}
|
||||
@ -186,7 +196,7 @@ namespace Explorer
|
||||
|
||||
if (enumerator == null) return;
|
||||
|
||||
var list = new List<CacheObject>();
|
||||
var list = new List<CacheObjectBase>();
|
||||
while (enumerator.MoveNext())
|
||||
{
|
||||
list.Add(GetCacheObject(enumerator.Current, null, null, this.EntryType));
|
||||
|
104
src/CachedObjects/CacheMethod.cs
Normal file
104
src/CachedObjects/CacheMethod.cs
Normal file
@ -0,0 +1,104 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Reflection;
|
||||
using UnityEngine;
|
||||
using MelonLoader;
|
||||
|
||||
namespace Explorer
|
||||
{
|
||||
public class CacheMethod : CacheObjectBase
|
||||
{
|
||||
private bool m_evaluated = false;
|
||||
private CacheObjectBase m_cachedReturnValue;
|
||||
|
||||
public static bool CanEvaluate(MethodInfo mi)
|
||||
{
|
||||
if (mi.GetParameters().Length > 0 || mi.GetGenericArguments().Length > 0)
|
||||
{
|
||||
// Currently methods with arguments are not supported (no good way to input them).
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override void Init()
|
||||
{
|
||||
base.Init();
|
||||
}
|
||||
|
||||
public override void UpdateValue()
|
||||
{
|
||||
base.UpdateValue();
|
||||
}
|
||||
|
||||
private void Evaluate()
|
||||
{
|
||||
m_evaluated = true;
|
||||
|
||||
var mi = MemberInfo as MethodInfo;
|
||||
var ret = mi.Invoke(mi.IsStatic ? null : DeclaringInstance, new object[0]);
|
||||
|
||||
if (ret != null)
|
||||
{
|
||||
m_cachedReturnValue = GetCacheObject(ret);
|
||||
if (m_cachedReturnValue is CacheList cacheList)
|
||||
{
|
||||
cacheList.WhiteSpace = 0f;
|
||||
cacheList.ButtonWidthOffset += 70f;
|
||||
}
|
||||
m_cachedReturnValue.UpdateValue();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_cachedReturnValue = null;
|
||||
}
|
||||
}
|
||||
|
||||
public override void DrawValue(Rect window, float width)
|
||||
{
|
||||
GUILayout.BeginVertical(null);
|
||||
|
||||
GUILayout.BeginHorizontal(null);
|
||||
if (GUILayout.Button("Evaluate", new GUILayoutOption[] { GUILayout.Width(70) }))
|
||||
{
|
||||
Evaluate();
|
||||
}
|
||||
GUI.skin.label.wordWrap = false;
|
||||
GUILayout.Label($"<color=yellow>{ValueType}</color>", null);
|
||||
GUI.skin.label.wordWrap = true;
|
||||
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", null);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
GUILayout.Label($"<color=grey><i>Not yet evaluated</i></color>", null);
|
||||
}
|
||||
GUILayout.EndHorizontal();
|
||||
|
||||
GUILayout.EndVertical();
|
||||
}
|
||||
}
|
||||
}
|
@ -10,14 +10,13 @@ using UnityEngine;
|
||||
|
||||
namespace Explorer
|
||||
{
|
||||
public abstract class CacheObject
|
||||
public abstract class CacheObjectBase
|
||||
{
|
||||
public object Value;
|
||||
public string ValueType;
|
||||
|
||||
// Reflection window only
|
||||
public MemberInfo MemberInfo { get; set; }
|
||||
// public ReflectionWindow.MemberInfoType MemberInfoType { get; set; }
|
||||
public Type DeclaringType { get; set; }
|
||||
public object DeclaringInstance { get; set; }
|
||||
public string FullName => $"{MemberInfo.DeclaringType.Name}.{MemberInfo.Name}";
|
||||
@ -42,14 +41,14 @@ namespace Explorer
|
||||
}
|
||||
}
|
||||
|
||||
public ReflectionWindow.MemberInfoType MemberInfoType
|
||||
public MemberTypes MemberInfoType
|
||||
{
|
||||
get
|
||||
{
|
||||
if (MemberInfo is FieldInfo) return ReflectionWindow.MemberInfoType.Field;
|
||||
if (MemberInfo is PropertyInfo) return ReflectionWindow.MemberInfoType.Property;
|
||||
if (MemberInfo is MethodInfo) return ReflectionWindow.MemberInfoType.Method;
|
||||
return ReflectionWindow.MemberInfoType.All;
|
||||
if (MemberInfo is FieldInfo) return MemberTypes.Field;
|
||||
if (MemberInfo is PropertyInfo) return MemberTypes.Property;
|
||||
if (MemberInfo is MethodInfo) return MemberTypes.Method;
|
||||
return MemberTypes.All;
|
||||
}
|
||||
}
|
||||
|
||||
@ -57,7 +56,7 @@ namespace Explorer
|
||||
public virtual void Init() { }
|
||||
public abstract void DrawValue(Rect window, float width);
|
||||
|
||||
public static CacheObject GetCacheObject(object obj)
|
||||
public static CacheObjectBase GetCacheObject(object obj)
|
||||
{
|
||||
return GetCacheObject(obj, null, null);
|
||||
}
|
||||
@ -69,13 +68,39 @@ namespace Explorer
|
||||
/// <param name="memberInfo">The MemberInfo (can be null if obj is not null)</param>
|
||||
/// <param name="declaringInstance">If MemberInfo is not null, the declaring class instance. Can be null if static.</param>
|
||||
/// <returns></returns>
|
||||
public static CacheObject GetCacheObject(object obj, MemberInfo memberInfo, object declaringInstance)
|
||||
public static CacheObjectBase GetCacheObject(object obj, MemberInfo memberInfo, object declaringInstance)
|
||||
{
|
||||
var type = ReflectionHelpers.GetActualType(obj) ?? (memberInfo as FieldInfo)?.FieldType ?? (memberInfo as PropertyInfo)?.PropertyType;
|
||||
//var type = ReflectionHelpers.GetActualType(obj) ?? (memberInfo as FieldInfo)?.FieldType ?? (memberInfo as PropertyInfo)?.PropertyType;
|
||||
|
||||
Type type = null;
|
||||
|
||||
if (obj != null)
|
||||
{
|
||||
type = ReflectionHelpers.GetActualType(obj);
|
||||
}
|
||||
else if (memberInfo != null)
|
||||
{
|
||||
if (memberInfo is FieldInfo fi)
|
||||
{
|
||||
type = fi.FieldType;
|
||||
}
|
||||
else if (memberInfo is PropertyInfo pi)
|
||||
{
|
||||
type = pi.PropertyType;
|
||||
}
|
||||
else if (memberInfo is MethodInfo mi)
|
||||
{
|
||||
type = mi.ReturnType;
|
||||
}
|
||||
}
|
||||
|
||||
if (type == null)
|
||||
{
|
||||
MelonLogger.Log("Could not get type for object or memberinfo!");
|
||||
if (memberInfo is MethodInfo)
|
||||
{
|
||||
MelonLogger.Log("is it void?");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -90,12 +115,22 @@ namespace Explorer
|
||||
/// <param name="declaringInstance">If MemberInfo is not null, the declaring class instance. Can be null if static.</param>
|
||||
/// <param name="valueType">The type of the object or MemberInfo value.</param>
|
||||
/// <returns></returns>
|
||||
public static CacheObject GetCacheObject(object obj, MemberInfo memberInfo, object declaringInstance, Type valueType)
|
||||
public static CacheObjectBase GetCacheObject(object obj, MemberInfo memberInfo, object declaringInstance, Type valueType)
|
||||
{
|
||||
CacheObject holder;
|
||||
CacheObjectBase holder;
|
||||
|
||||
if ((obj is Il2CppSystem.Object || typeof(Il2CppSystem.Object).IsAssignableFrom(valueType))
|
||||
&& (valueType.FullName.Contains("UnityEngine.GameObject") || valueType.FullName.Contains("UnityEngine.Transform")))
|
||||
if (memberInfo is MethodInfo mi)
|
||||
{
|
||||
if (CacheMethod.CanEvaluate(mi))
|
||||
{
|
||||
holder = new CacheMethod();
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
else if (valueType == typeof(GameObject) || valueType == typeof(Transform))
|
||||
{
|
||||
holder = new CacheGameObject();
|
||||
}
|
||||
@ -107,10 +142,14 @@ namespace Explorer
|
||||
{
|
||||
holder = new CacheEnum();
|
||||
}
|
||||
else if (typeof(System.Collections.IEnumerable).IsAssignableFrom(valueType) || ReflectionHelpers.IsList(valueType))
|
||||
else if (ReflectionHelpers.IsArray(valueType) || ReflectionHelpers.IsList(valueType))
|
||||
{
|
||||
holder = new CacheList();
|
||||
}
|
||||
else if (ReflectionHelpers.IsDictionary(valueType))
|
||||
{
|
||||
holder = new CacheDictionary();
|
||||
}
|
||||
else
|
||||
{
|
||||
holder = new CacheOther();
|
||||
@ -163,7 +202,7 @@ namespace Explorer
|
||||
{
|
||||
GUILayout.Label("<color=red>Reflection failed!</color> (" + ReflectionException + ")", null);
|
||||
}
|
||||
else if (Value == null)
|
||||
else if (Value == null && MemberInfoType != MemberTypes.Method)
|
||||
{
|
||||
GUILayout.Label("<i>null (" + ValueType + ")</i>", null);
|
||||
}
|
@ -9,7 +9,7 @@ using UnityEngine;
|
||||
|
||||
namespace Explorer
|
||||
{
|
||||
public class CacheOther : CacheObject
|
||||
public class CacheOther : CacheObjectBase
|
||||
{
|
||||
private MethodInfo m_toStringMethod;
|
||||
private bool m_triedToGetMethod;
|
||||
|
@ -1,11 +1,12 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using MelonLoader;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Explorer
|
||||
{
|
||||
public class CachePrimitive : CacheObject
|
||||
public class CachePrimitive : CacheObjectBase
|
||||
{
|
||||
public enum PrimitiveTypes
|
||||
{
|
||||
@ -13,7 +14,8 @@ namespace Explorer
|
||||
Double,
|
||||
Float,
|
||||
Int,
|
||||
String
|
||||
String,
|
||||
Char
|
||||
}
|
||||
|
||||
private string m_valueToString;
|
||||
@ -37,10 +39,10 @@ namespace Explorer
|
||||
t = typeof(float); break;
|
||||
case PrimitiveTypes.Int:
|
||||
t = typeof(int); break;
|
||||
case PrimitiveTypes.String:
|
||||
t = typeof(string); break;
|
||||
case PrimitiveTypes.Char:
|
||||
t = typeof(char); break;
|
||||
}
|
||||
m_parseMethod = t.GetMethod("Parse", new Type[] { typeof(string) });
|
||||
m_parseMethod = t?.GetMethod("Parse", new Type[] { typeof(string) });
|
||||
}
|
||||
return m_parseMethod;
|
||||
}
|
||||
@ -52,7 +54,7 @@ namespace Explorer
|
||||
{
|
||||
if (Value == null)
|
||||
{
|
||||
// this must mean it is a string? no other primitive type should be nullable
|
||||
// this must mean it is a string. No other primitive type should be nullable.
|
||||
PrimitiveType = PrimitiveTypes.String;
|
||||
return;
|
||||
}
|
||||
@ -72,21 +74,39 @@ namespace Explorer
|
||||
{
|
||||
PrimitiveType = PrimitiveTypes.Float;
|
||||
}
|
||||
else if (type == typeof(int) || type == typeof(long) || type == typeof(uint) || type == typeof(ulong) || type == typeof(IntPtr))
|
||||
else if (IsInteger(type))
|
||||
{
|
||||
PrimitiveType = PrimitiveTypes.Int;
|
||||
}
|
||||
else if (type == typeof(char))
|
||||
{
|
||||
PrimitiveType = PrimitiveTypes.Char;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (type != typeof(string))
|
||||
{
|
||||
MelonLogger.Log("Unsupported primitive: " + type);
|
||||
}
|
||||
|
||||
PrimitiveType = PrimitiveTypes.String;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool IsInteger(Type type)
|
||||
{
|
||||
// For our purposes, all types of int can be treated the same, including IntPtr.
|
||||
return _integerTypes.Contains(type);
|
||||
}
|
||||
|
||||
private static readonly HashSet<Type> _integerTypes = new HashSet<Type>
|
||||
{
|
||||
typeof(int),
|
||||
typeof(uint),
|
||||
typeof(short),
|
||||
typeof(ushort),
|
||||
typeof(long),
|
||||
typeof(ulong),
|
||||
typeof(byte),
|
||||
typeof(sbyte),
|
||||
typeof(IntPtr)
|
||||
};
|
||||
|
||||
public override void UpdateValue()
|
||||
{
|
||||
base.UpdateValue();
|
||||
@ -99,21 +119,26 @@ namespace Explorer
|
||||
if (PrimitiveType == PrimitiveTypes.Bool)
|
||||
{
|
||||
var b = (bool)Value;
|
||||
var color = "<color=" + (b ? "lime>" : "red>");
|
||||
b = GUILayout.Toggle(b, color + b.ToString() + "</color>", null);
|
||||
var color = $"<color={(b ? "lime>" : "red>")}";
|
||||
var label = $"{color}{b}</color>";
|
||||
|
||||
if (b != (bool)Value)
|
||||
if (CanWrite)
|
||||
{
|
||||
SetValue(m_valueToString);
|
||||
b = GUILayout.Toggle(b, label, null);
|
||||
if (b != (bool)Value)
|
||||
{
|
||||
SetValue(m_valueToString);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
GUILayout.Label(label, null);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
GUILayout.Label("<color=yellow><i>" + PrimitiveType + "</i></color>", new GUILayoutOption[] { GUILayout.Width(50) });
|
||||
|
||||
//var content = new GUIContent(m_valueToString);
|
||||
//var contentSize = GUI.skin.textField.CalcSize(content);
|
||||
|
||||
int dynSize = 25 + (m_valueToString.Length * 15);
|
||||
var maxwidth = window.width - 300f;
|
||||
if (CanWrite) maxwidth -= 60;
|
||||
|
@ -170,32 +170,34 @@ namespace Explorer
|
||||
}
|
||||
}
|
||||
|
||||
// Make it appear as though UnlockMouse is disabled to the rest of the application.
|
||||
// Temporarily disabled this because I don't think it's actually useful, and may in fact cause problems instead
|
||||
|
||||
[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;
|
||||
}
|
||||
}
|
||||
}
|
||||
//// 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;
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
|
||||
//[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;
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
}
|
||||
}
|
||||
|
@ -120,11 +120,13 @@
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="CachedObjects\CacheDictionary.cs" />
|
||||
<Compile Include="CachedObjects\CacheEnum.cs" />
|
||||
<Compile Include="CachedObjects\CacheGameObject.cs" />
|
||||
<Compile Include="CachedObjects\CacheList.cs" />
|
||||
<Compile Include="CachedObjects\CachePrimitive.cs" />
|
||||
<Compile Include="CachedObjects\CacheOther.cs" />
|
||||
<Compile Include="CachedObjects\CacheMethod.cs" />
|
||||
<Compile Include="CppExplorer.cs" />
|
||||
<Compile Include="Extensions\ReflectionExtensions.cs" />
|
||||
<Compile Include="Extensions\UnityExtensions.cs" />
|
||||
@ -132,7 +134,7 @@
|
||||
<Compile Include="Helpers\UIHelpers.cs" />
|
||||
<Compile Include="Helpers\UnityHelpers.cs" />
|
||||
<Compile Include="MainMenu\InspectUnderMouse.cs" />
|
||||
<Compile Include="CachedObjects\CacheObject.cs" />
|
||||
<Compile Include="CachedObjects\CacheObjectBase.cs" />
|
||||
<Compile Include="Windows\ResizeDrag.cs" />
|
||||
<Compile Include="Windows\UIWindow.cs" />
|
||||
<Compile Include="MainMenu\Pages\ConsolePage.cs" />
|
||||
|
@ -72,6 +72,11 @@ namespace Explorer
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool IsArray(Type t)
|
||||
{
|
||||
return typeof(System.Collections.IEnumerable).IsAssignableFrom(t);
|
||||
}
|
||||
|
||||
public static bool IsList(Type t)
|
||||
{
|
||||
return t.IsGenericType
|
||||
@ -79,6 +84,13 @@ namespace Explorer
|
||||
&& (typeDef.IsAssignableFrom(typeof(List<>)) || typeDef.IsAssignableFrom(typeof(Il2CppSystem.Collections.Generic.List<>)));
|
||||
}
|
||||
|
||||
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())
|
||||
|
@ -121,7 +121,9 @@ MelonLogger.Log(""hello world"");";
|
||||
{
|
||||
GUILayout.Label("<b><size=15><color=cyan>C# REPL Console</color></size></b>", null);
|
||||
|
||||
GUILayout.Label("Method:", null);
|
||||
GUI.skin.label.alignment = TextAnchor.UpperLeft;
|
||||
|
||||
GUILayout.Label("Enter code here as though it is a method body:", null);
|
||||
MethodInput = GUILayout.TextArea(MethodInput, new GUILayoutOption[] { GUILayout.Height(250) });
|
||||
|
||||
if (GUILayout.Button("<color=cyan><b>Execute</b></color>", null))
|
||||
@ -147,10 +149,7 @@ MelonLogger.Log(""hello world"");";
|
||||
}
|
||||
|
||||
GUILayout.Label("<b>Using directives:</b>", null);
|
||||
foreach (var asm in UsingDirectives)
|
||||
{
|
||||
GUILayout.Label(AsmToUsing(asm, true), null);
|
||||
}
|
||||
|
||||
GUILayout.BeginHorizontal(null);
|
||||
GUILayout.Label("Add namespace:", new GUILayoutOption[] { GUILayout.Width(105) });
|
||||
UsingInput = GUILayout.TextField(UsingInput, new GUILayoutOption[] { GUILayout.Width(150) });
|
||||
@ -163,6 +162,11 @@ MelonLogger.Log(""hello world"");";
|
||||
ResetConsole();
|
||||
}
|
||||
GUILayout.EndHorizontal();
|
||||
|
||||
foreach (var asm in UsingDirectives)
|
||||
{
|
||||
GUILayout.Label(AsmToUsing(asm, true), null);
|
||||
}
|
||||
}
|
||||
|
||||
public override void Update() { }
|
||||
|
@ -26,7 +26,7 @@ namespace Explorer
|
||||
//private List<object> m_searchResults = new List<object>();
|
||||
private Vector2 resultsScroll = Vector2.zero;
|
||||
|
||||
private List<CacheObject> m_searchResults = new List<CacheObject>();
|
||||
private List<CacheObjectBase> m_searchResults = new List<CacheObjectBase>();
|
||||
|
||||
public SceneFilter SceneMode = SceneFilter.Any;
|
||||
public TypeFilter TypeMode = TypeFilter.Object;
|
||||
@ -64,7 +64,7 @@ namespace Explorer
|
||||
|
||||
private void CacheResults(IEnumerable results)
|
||||
{
|
||||
m_searchResults = new List<CacheObject>();
|
||||
m_searchResults = new List<CacheObjectBase>();
|
||||
|
||||
foreach (var obj in results)
|
||||
{
|
||||
@ -75,7 +75,7 @@ namespace Explorer
|
||||
toCache = ilObject.TryCast<GameObject>() ?? ilObject.TryCast<Transform>()?.gameObject ?? ilObject;
|
||||
}
|
||||
|
||||
var cache = CacheObject.GetCacheObject(toCache);
|
||||
var cache = CacheObjectBase.GetCacheObject(toCache);
|
||||
m_searchResults.Add(cache);
|
||||
}
|
||||
}
|
||||
|
@ -6,7 +6,6 @@ using MelonLoader;
|
||||
using UnhollowerRuntimeLib;
|
||||
using UnityEngine;
|
||||
using UnityEngine.SceneManagement;
|
||||
using ComponentList = Il2CppSystem.Collections.Generic.List<UnityEngine.Component>;
|
||||
|
||||
namespace Explorer
|
||||
{
|
||||
@ -22,7 +21,7 @@ namespace Explorer
|
||||
|
||||
private Vector2 m_transformScroll = Vector2.zero;
|
||||
private Transform[] m_children;
|
||||
private ComponentList m_components;
|
||||
private Component[] m_components;
|
||||
|
||||
private Vector2 m_compScroll = Vector2.zero;
|
||||
|
||||
@ -69,11 +68,9 @@ namespace Explorer
|
||||
}
|
||||
|
||||
m_name = m_object.name;
|
||||
m_scene = m_object.scene == null ? "null" : m_object.scene.name;
|
||||
|
||||
//var listComps = new Il2CppSystem.Collections.Generic.List<Component>();
|
||||
//m_object.GetComponents(listComps);
|
||||
//m_components = listComps.ToArray();
|
||||
m_scene = string.IsNullOrEmpty(m_object.scene.name)
|
||||
? "None"
|
||||
: m_object.scene.name;
|
||||
|
||||
var list = new List<Transform>();
|
||||
for (int i = 0; i < m_object.transform.childCount; i++)
|
||||
@ -92,8 +89,12 @@ namespace Explorer
|
||||
throw new Exception("Object is null!");
|
||||
}
|
||||
|
||||
m_components = new Il2CppSystem.Collections.Generic.List<Component>();
|
||||
m_object.GetComponentsInternal(ReflectionHelpers.ComponentType, false, false, true, false, m_components);
|
||||
var list = new List<Component>();
|
||||
foreach (var comp in m_object.GetComponents(ReflectionHelpers.ComponentType))
|
||||
{
|
||||
list.Add(comp);
|
||||
}
|
||||
m_components = list.ToArray();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@ -255,34 +256,37 @@ namespace Explorer
|
||||
m_cachedDestroyList.Clear();
|
||||
}
|
||||
|
||||
foreach (var component in m_components)
|
||||
if (m_components != null)
|
||||
{
|
||||
if (!component) continue;
|
||||
foreach (var component in m_components)
|
||||
{
|
||||
if (!component) continue;
|
||||
|
||||
var ilType = component.GetIl2CppType();
|
||||
if (ilType == ReflectionHelpers.TransformType)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
var ilType = component.GetIl2CppType();
|
||||
if (ilType == ReflectionHelpers.TransformType)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
GUILayout.BeginHorizontal(null);
|
||||
if (ReflectionHelpers.BehaviourType.IsAssignableFrom(ilType))
|
||||
{
|
||||
BehaviourEnabledBtn(component.TryCast<Behaviour>());
|
||||
GUILayout.BeginHorizontal(null);
|
||||
if (ReflectionHelpers.BehaviourType.IsAssignableFrom(ilType))
|
||||
{
|
||||
BehaviourEnabledBtn(component.TryCast<Behaviour>());
|
||||
}
|
||||
else
|
||||
{
|
||||
GUILayout.Space(26);
|
||||
}
|
||||
if (GUILayout.Button("<color=cyan>" + ilType.Name + "</color>", new GUILayoutOption[] { GUILayout.Width(m_rect.width / 2 - 90) }))
|
||||
{
|
||||
ReflectObject(component);
|
||||
}
|
||||
if (GUILayout.Button("<color=red>-</color>", new GUILayoutOption[] { GUILayout.Width(20) }))
|
||||
{
|
||||
m_cachedDestroyList.Add(component);
|
||||
}
|
||||
GUILayout.EndHorizontal();
|
||||
}
|
||||
else
|
||||
{
|
||||
GUILayout.Space(26);
|
||||
}
|
||||
if (GUILayout.Button("<color=cyan>" + ilType.Name + "</color>", new GUILayoutOption[] { GUILayout.Width(m_rect.width / 2 - 90) }))
|
||||
{
|
||||
ReflectObject(component);
|
||||
}
|
||||
if (GUILayout.Button("<color=red>-</color>", new GUILayoutOption[] { GUILayout.Width(20) }))
|
||||
{
|
||||
m_cachedDestroyList.Add(component);
|
||||
}
|
||||
GUILayout.EndHorizontal();
|
||||
}
|
||||
|
||||
GUI.skin.button.alignment = TextAnchor.MiddleCenter;
|
||||
|
@ -16,30 +16,27 @@ namespace Explorer
|
||||
|
||||
public Type ObjectType;
|
||||
|
||||
private CacheObject[] m_cachedMembers;
|
||||
private CacheObject[] m_cachedMemberFiltered;
|
||||
private CacheObjectBase[] m_cachedMembers;
|
||||
private CacheObjectBase[] m_cachedMemberFiltered;
|
||||
private int m_pageOffset;
|
||||
private int m_limitPerPage = 20;
|
||||
|
||||
private bool m_autoUpdate = false;
|
||||
private string m_search = "";
|
||||
public MemberInfoType m_filter = MemberInfoType.Property;
|
||||
public MemberTypes m_filter = MemberTypes.Property;
|
||||
private bool m_hideFailedReflection = false;
|
||||
|
||||
public enum MemberInfoType
|
||||
{
|
||||
Field,
|
||||
Property,
|
||||
Method,
|
||||
All
|
||||
}
|
||||
// some extra caching
|
||||
private UnityEngine.Object m_uObj;
|
||||
private Component m_component;
|
||||
|
||||
public override void Init()
|
||||
{
|
||||
var type = ReflectionHelpers.GetActualType(Target);
|
||||
if (type == null)
|
||||
{
|
||||
MelonLogger.Log("Could not get underlying type for object. ToString(): " + Target.ToString());
|
||||
MelonLogger.Log($"Could not get underlying type for object..? Type: {Target?.GetType().Name}, ToString: {Target?.ToString()}");
|
||||
DestroyWindow();
|
||||
return;
|
||||
}
|
||||
|
||||
@ -48,10 +45,27 @@ namespace Explorer
|
||||
var types = ReflectionHelpers.GetAllBaseTypes(Target);
|
||||
CacheMembers(types);
|
||||
|
||||
m_filter = MemberInfoType.All;
|
||||
m_cachedMemberFiltered = m_cachedMembers.Where(x => ShouldProcessMember(x)).ToArray();
|
||||
UpdateValues();
|
||||
m_filter = MemberInfoType.Property;
|
||||
if (Target is Il2CppSystem.Object ilObject)
|
||||
{
|
||||
var unityObj = ilObject.TryCast<UnityEngine.Object>();
|
||||
if (unityObj)
|
||||
{
|
||||
m_uObj = unityObj;
|
||||
|
||||
var component = ilObject.TryCast<Component>();
|
||||
if (component)
|
||||
{
|
||||
m_component = component;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_filter = MemberTypes.All;
|
||||
m_autoUpdate = true;
|
||||
Update();
|
||||
|
||||
m_autoUpdate = false;
|
||||
m_filter = MemberTypes.Property;
|
||||
}
|
||||
|
||||
public override void Update()
|
||||
@ -65,11 +79,6 @@ namespace Explorer
|
||||
}
|
||||
|
||||
private void UpdateValues()
|
||||
{
|
||||
UpdateMembers();
|
||||
}
|
||||
|
||||
private void UpdateMembers()
|
||||
{
|
||||
foreach (var member in m_cachedMemberFiltered)
|
||||
{
|
||||
@ -77,9 +86,9 @@ namespace Explorer
|
||||
}
|
||||
}
|
||||
|
||||
private bool ShouldProcessMember(CacheObject holder)
|
||||
private bool ShouldProcessMember(CacheObjectBase holder)
|
||||
{
|
||||
if (m_filter != MemberInfoType.All && m_filter != holder.MemberInfoType) return false;
|
||||
if (m_filter != MemberTypes.All && m_filter != holder.MemberInfoType) return false;
|
||||
|
||||
if (!string.IsNullOrEmpty(holder.ReflectionException) && m_hideFailedReflection) return false;
|
||||
|
||||
@ -92,16 +101,13 @@ namespace Explorer
|
||||
|
||||
private void CacheMembers(Type[] types)
|
||||
{
|
||||
var list = new List<CacheObject>();
|
||||
var list = new List<CacheObjectBase>();
|
||||
|
||||
var names = new List<string>();
|
||||
|
||||
foreach (var declaringType in types)
|
||||
{
|
||||
if (declaringType == typeof(Il2CppObjectBase)) continue;
|
||||
|
||||
MemberInfo[] infos;
|
||||
|
||||
string exception = null;
|
||||
|
||||
try
|
||||
@ -114,8 +120,8 @@ namespace Explorer
|
||||
continue;
|
||||
}
|
||||
|
||||
//object value = null;
|
||||
object target = Target;
|
||||
|
||||
if (target is Il2CppSystem.Object ilObject)
|
||||
{
|
||||
try
|
||||
@ -130,9 +136,10 @@ namespace Explorer
|
||||
|
||||
foreach (var member in infos)
|
||||
{
|
||||
if (member.MemberType == MemberTypes.Field || member.MemberType == MemberTypes.Property)
|
||||
if (member.MemberType == MemberTypes.Field || member.MemberType == MemberTypes.Property || member.MemberType == MemberTypes.Method)
|
||||
{
|
||||
if (member.Name == "Il2CppType") continue;
|
||||
if (member.Name.Contains("Il2CppType") || member.Name.StartsWith("get_"))
|
||||
continue;
|
||||
|
||||
try
|
||||
{
|
||||
@ -140,7 +147,7 @@ namespace Explorer
|
||||
if (names.Contains(name)) continue;
|
||||
names.Add(name);
|
||||
|
||||
var cached = CacheObject.GetCacheObject(null, member, target);
|
||||
var cached = CacheObjectBase.GetCacheObject(null, member, target);
|
||||
if (cached != null)
|
||||
{
|
||||
list.Add(cached);
|
||||
@ -149,9 +156,8 @@ namespace Explorer
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
MelonLogger.Log("Exception caching member!");
|
||||
MelonLogger.Log(e.GetType() + ", " + e.Message);
|
||||
MelonLogger.Log(e.StackTrace);
|
||||
MelonLogger.LogWarning($"Exception caching member {declaringType.Name}.{member.Name}!");
|
||||
MelonLogger.Log(e.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -172,26 +178,18 @@ namespace Explorer
|
||||
|
||||
GUILayout.BeginHorizontal(null);
|
||||
GUILayout.Label("<b>Type:</b> <color=cyan>" + ObjectType.FullName + "</color>", null);
|
||||
|
||||
bool unityObj = Target is UnityEngine.Object;
|
||||
|
||||
if (unityObj)
|
||||
if (m_uObj)
|
||||
{
|
||||
GUILayout.Label("Name: " + (Target as UnityEngine.Object).name, null);
|
||||
GUILayout.Label("Name: " + m_uObj.name, null);
|
||||
}
|
||||
GUILayout.EndHorizontal();
|
||||
|
||||
if (unityObj)
|
||||
if (m_uObj)
|
||||
{
|
||||
GUILayout.BeginHorizontal(null);
|
||||
|
||||
GUILayout.Label("<b>Tools:</b>", new GUILayoutOption[] { GUILayout.Width(80) });
|
||||
|
||||
UIHelpers.InstantiateButton((UnityEngine.Object)Target);
|
||||
|
||||
var comp = (Target as Il2CppSystem.Object).TryCast<Component>();
|
||||
|
||||
if (comp && comp.gameObject is GameObject obj)
|
||||
UIHelpers.InstantiateButton(m_uObj);
|
||||
if (m_component && m_component.gameObject is GameObject obj)
|
||||
{
|
||||
GUI.skin.label.alignment = TextAnchor.MiddleRight;
|
||||
GUILayout.Label("GameObject:", null);
|
||||
@ -201,7 +199,6 @@ namespace Explorer
|
||||
}
|
||||
GUI.skin.label.alignment = TextAnchor.UpperLeft;
|
||||
}
|
||||
|
||||
GUILayout.EndHorizontal();
|
||||
}
|
||||
|
||||
@ -221,9 +218,10 @@ namespace Explorer
|
||||
|
||||
GUILayout.BeginHorizontal(null);
|
||||
GUILayout.Label("<b>Filter:</b>", new GUILayoutOption[] { GUILayout.Width(75) });
|
||||
FilterToggle(MemberInfoType.All, "All");
|
||||
FilterToggle(MemberInfoType.Property, "Properties");
|
||||
FilterToggle(MemberInfoType.Field, "Fields");
|
||||
FilterToggle(MemberTypes.All, "All");
|
||||
FilterToggle(MemberTypes.Property, "Properties");
|
||||
FilterToggle(MemberTypes.Field, "Fields");
|
||||
FilterToggle(MemberTypes.Method, "Methods");
|
||||
GUILayout.EndHorizontal();
|
||||
|
||||
GUILayout.BeginHorizontal(null);
|
||||
@ -284,18 +282,19 @@ namespace Explorer
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawMembers(CacheObject[] members)
|
||||
private void DrawMembers(CacheObjectBase[] members)
|
||||
{
|
||||
// todo pre-cache list based on current search, otherwise this doesnt work.
|
||||
|
||||
int i = 0;
|
||||
DrawMembersInternal("Properties", MemberInfoType.Property, members, ref i);
|
||||
DrawMembersInternal("Fields", MemberInfoType.Field, members, ref i);
|
||||
DrawMembersInternal("Properties", MemberTypes.Property, members, ref i);
|
||||
DrawMembersInternal("Fields", MemberTypes.Field, members, ref i);
|
||||
DrawMembersInternal("Methods", MemberTypes.Method, members, ref i);
|
||||
}
|
||||
|
||||
private void DrawMembersInternal(string title, MemberInfoType filter, CacheObject[] members, ref int index)
|
||||
private void DrawMembersInternal(string title, MemberTypes filter, CacheObjectBase[] members, ref int index)
|
||||
{
|
||||
if (m_filter != filter && m_filter != MemberInfoType.All)
|
||||
if (m_filter != filter && m_filter != MemberTypes.All)
|
||||
{
|
||||
return;
|
||||
}
|
||||
@ -336,7 +335,7 @@ namespace Explorer
|
||||
}
|
||||
}
|
||||
|
||||
private void FilterToggle(MemberInfoType mode, string label)
|
||||
private void FilterToggle(MemberTypes mode, string label)
|
||||
{
|
||||
if (m_filter == mode)
|
||||
{
|
||||
|
@ -59,6 +59,8 @@ namespace Explorer
|
||||
MelonLogger.Log("Exception on GuiResize: " + e.GetType() + ", " + e.Message);
|
||||
}
|
||||
|
||||
GUI.skin.label.alignment = TextAnchor.UpperLeft;
|
||||
|
||||
return _rect;
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user