mirror of
https://github.com/sinai-dev/UnityExplorer.git
synced 2025-06-22 08:32:51 +08:00
Compare commits
5 Commits
Author | SHA1 | Date | |
---|---|---|---|
4e8b84b67e | |||
5b94e31a12 | |||
692a37635e | |||
9cb1cea025 | |||
e13f198815 |
@ -1,4 +1,4 @@
|
||||
# CppExplorer []()
|
||||
# CppExplorer [](https://github.com/HerpDerpinstine/MelonLoader)
|
||||
|
||||
<p align="center">
|
||||
<img align="center" src="https://sinai-dev.github.io/images/thumbs/02.png">
|
||||
|
@ -14,6 +14,7 @@ namespace Explorer
|
||||
{
|
||||
public object Value;
|
||||
public string ValueTypeName;
|
||||
public Type ValueType;
|
||||
|
||||
// Reflection Inspector only
|
||||
public MemberInfo MemInfo { get; set; }
|
||||
@ -79,11 +80,7 @@ namespace Explorer
|
||||
{
|
||||
Type type = null;
|
||||
|
||||
if (obj != null)
|
||||
{
|
||||
type = ReflectionHelpers.GetActualType(obj);
|
||||
}
|
||||
else if (memberInfo != null)
|
||||
if (memberInfo != null)
|
||||
{
|
||||
if (memberInfo is FieldInfo fi)
|
||||
{
|
||||
@ -98,6 +95,10 @@ namespace Explorer
|
||||
type = mi.ReturnType;
|
||||
}
|
||||
}
|
||||
else if (obj != null)
|
||||
{
|
||||
type = ReflectionHelpers.GetActualType(obj);
|
||||
}
|
||||
|
||||
if (type == null)
|
||||
{
|
||||
@ -114,6 +115,11 @@ namespace Explorer
|
||||
{
|
||||
CacheObjectBase holder;
|
||||
|
||||
// This is pretty ugly, could probably make a cleaner implementation.
|
||||
// However, the only cleaner ways I can think of are slower and probably not worth it.
|
||||
|
||||
// Note: the order is somewhat important.
|
||||
|
||||
if (memberInfo is MethodInfo mi)
|
||||
{
|
||||
if (CacheMethod.CanEvaluate(mi))
|
||||
@ -153,20 +159,22 @@ namespace Explorer
|
||||
{
|
||||
holder = new CacheRect();
|
||||
}
|
||||
else if (ReflectionHelpers.IsArray(valueType) || ReflectionHelpers.IsList(valueType))
|
||||
{
|
||||
holder = new CacheList();
|
||||
}
|
||||
// must check this before IsEnumerable
|
||||
else if (ReflectionHelpers.IsDictionary(valueType))
|
||||
{
|
||||
holder = new CacheDictionary();
|
||||
}
|
||||
else if (ReflectionHelpers.IsEnumerable(valueType) || ReflectionHelpers.IsCppList(valueType))
|
||||
{
|
||||
holder = new CacheList();
|
||||
}
|
||||
else
|
||||
{
|
||||
holder = new CacheOther();
|
||||
}
|
||||
|
||||
holder.Value = obj;
|
||||
holder.ValueType = valueType;
|
||||
holder.ValueTypeName = valueType.FullName;
|
||||
|
||||
if (memberInfo != null)
|
||||
|
@ -1,34 +1,278 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using MelonLoader;
|
||||
using UnityEngine;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Explorer
|
||||
{
|
||||
public class CacheDictionary : CacheObjectBase
|
||||
{
|
||||
public bool IsExpanded { get; set; }
|
||||
public PageHelper Pages = new PageHelper();
|
||||
|
||||
public float WhiteSpace = 215f;
|
||||
public float ButtonWidthOffset = 290f;
|
||||
|
||||
public override void Init()
|
||||
private CacheObjectBase[] m_cachedKeys;
|
||||
private CacheObjectBase[] m_cachedValues;
|
||||
|
||||
public Type TypeOfKeys
|
||||
{
|
||||
//base.Init();
|
||||
get
|
||||
{
|
||||
if (m_keysType == null) GetGenericArguments();
|
||||
return m_keysType;
|
||||
}
|
||||
}
|
||||
private Type m_keysType;
|
||||
|
||||
Value = "Unsupported";
|
||||
public Type TypeOfValues
|
||||
{
|
||||
get
|
||||
{
|
||||
if (m_valuesType == null) GetGenericArguments();
|
||||
return m_valuesType;
|
||||
}
|
||||
}
|
||||
private Type m_valuesType;
|
||||
|
||||
public IDictionary IDict
|
||||
{
|
||||
get => m_iDictionary ?? (m_iDictionary = Value as IDictionary) ?? Il2CppDictionaryToMono();
|
||||
set => m_iDictionary = value;
|
||||
}
|
||||
private IDictionary m_iDictionary;
|
||||
|
||||
// This is a bit janky due to Il2Cpp Dictionary not implementing IDictionary.
|
||||
private IDictionary Il2CppDictionaryToMono()
|
||||
{
|
||||
// note: "ValueType" is the Dictionary itself, TypeOfValues is the 'Dictionary.Values' type.
|
||||
|
||||
// make generic dictionary from key and value type
|
||||
var dict = (IDictionary)Activator.CreateInstance(typeof(Dictionary<,>)
|
||||
.MakeGenericType(TypeOfKeys, TypeOfValues));
|
||||
|
||||
// get keys and values
|
||||
var keys = ValueType.GetProperty("Keys") .GetValue(Value);
|
||||
var values = ValueType.GetProperty("Values").GetValue(Value);
|
||||
|
||||
// create lists to hold them
|
||||
var keyList = new List<object>();
|
||||
var valueList = new List<object>();
|
||||
|
||||
// get keys enumerator and store keys
|
||||
var keyEnumerator = keys.GetType().GetMethod("GetEnumerator").Invoke(keys, null);
|
||||
var keyCollectionType = keyEnumerator.GetType();
|
||||
var keyMoveNext = keyCollectionType.GetMethod("MoveNext");
|
||||
var keyCurrent = keyCollectionType.GetProperty("Current");
|
||||
while ((bool)keyMoveNext.Invoke(keyEnumerator, null))
|
||||
{
|
||||
keyList.Add(keyCurrent.GetValue(keyEnumerator));
|
||||
}
|
||||
|
||||
// get values enumerator and store values
|
||||
var valueEnumerator = values.GetType().GetMethod("GetEnumerator").Invoke(values, null);
|
||||
var valueCollectionType = valueEnumerator.GetType();
|
||||
var valueMoveNext = valueCollectionType.GetMethod("MoveNext");
|
||||
var valueCurrent = valueCollectionType.GetProperty("Current");
|
||||
while ((bool)valueMoveNext.Invoke(valueEnumerator, null))
|
||||
{
|
||||
valueList.Add(valueCurrent.GetValue(valueEnumerator));
|
||||
}
|
||||
|
||||
// finally iterate into actual dictionary
|
||||
for (int i = 0; i < keyList.Count; i++)
|
||||
{
|
||||
dict.Add(keyList[i], valueList[i]);
|
||||
}
|
||||
|
||||
return dict;
|
||||
}
|
||||
|
||||
// ========== Methods ==========
|
||||
|
||||
private void GetGenericArguments()
|
||||
{
|
||||
if (m_keysType == null || m_valuesType == null)
|
||||
{
|
||||
if (this.MemInfo != null)
|
||||
{
|
||||
Type memberType = null;
|
||||
switch (this.MemInfo.MemberType)
|
||||
{
|
||||
case MemberTypes.Field:
|
||||
memberType = (MemInfo as FieldInfo).FieldType;
|
||||
break;
|
||||
case MemberTypes.Property:
|
||||
memberType = (MemInfo as PropertyInfo).PropertyType;
|
||||
break;
|
||||
}
|
||||
|
||||
if (memberType != null && memberType.IsGenericType)
|
||||
{
|
||||
m_keysType = memberType.GetGenericArguments()[0];
|
||||
m_valuesType = memberType.GetGenericArguments()[1];
|
||||
}
|
||||
}
|
||||
else if (Value != null)
|
||||
{
|
||||
var type = Value.GetType();
|
||||
if (type.IsGenericType)
|
||||
{
|
||||
m_keysType = type.GetGenericArguments()[0];
|
||||
m_valuesType = type.GetGenericArguments()[1];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
public override void UpdateValue()
|
||||
{
|
||||
//base.UpdateValue();
|
||||
base.UpdateValue();
|
||||
|
||||
// reset
|
||||
IDict = null;
|
||||
|
||||
if (Value == null || IDict == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var keys = new List<CacheObjectBase>();
|
||||
foreach (var key in IDict.Keys)
|
||||
{
|
||||
var cache = GetCacheObject(key, TypeOfKeys);
|
||||
cache.UpdateValue();
|
||||
keys.Add(cache);
|
||||
}
|
||||
|
||||
var values = new List<CacheObjectBase>();
|
||||
foreach (var val in IDict.Values)
|
||||
{
|
||||
var cache = GetCacheObject(val, TypeOfValues);
|
||||
cache.UpdateValue();
|
||||
values.Add(cache);
|
||||
}
|
||||
|
||||
m_cachedKeys = keys.ToArray();
|
||||
m_cachedValues = values.ToArray();
|
||||
}
|
||||
|
||||
// ============= GUI Draw =============
|
||||
|
||||
public override void DrawValue(Rect window, float width)
|
||||
{
|
||||
GUILayout.Label("<color=red>Dictionary (unsupported)</color>", null);
|
||||
if (m_cachedKeys == null || m_cachedValues == null)
|
||||
{
|
||||
GUILayout.Label("Cached keys or values is null!", null);
|
||||
return;
|
||||
}
|
||||
|
||||
int count = m_cachedKeys.Length;
|
||||
|
||||
if (!IsExpanded)
|
||||
{
|
||||
if (GUILayout.Button("v", new GUILayoutOption[] { GUILayout.Width(25) }))
|
||||
{
|
||||
IsExpanded = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (GUILayout.Button("^", new GUILayoutOption[] { GUILayout.Width(25) }))
|
||||
{
|
||||
IsExpanded = false;
|
||||
}
|
||||
}
|
||||
|
||||
GUI.skin.button.alignment = TextAnchor.MiddleLeft;
|
||||
string btnLabel = $"<color=yellow>[{count}] Dictionary<{TypeOfKeys.FullName}, {TypeOfValues.FullName}></color>";
|
||||
if (GUILayout.Button(btnLabel, new GUILayoutOption[] { GUILayout.MaxWidth(window.width - ButtonWidthOffset) }))
|
||||
{
|
||||
WindowManager.InspectObject(Value, out bool _);
|
||||
}
|
||||
GUI.skin.button.alignment = TextAnchor.MiddleCenter;
|
||||
|
||||
GUILayout.Space(5);
|
||||
|
||||
if (IsExpanded)
|
||||
{
|
||||
float whitespace = WhiteSpace;
|
||||
if (whitespace > 0)
|
||||
{
|
||||
ClampLabelWidth(window, ref whitespace);
|
||||
}
|
||||
|
||||
Pages.ItemCount = count;
|
||||
|
||||
if (count > Pages.ItemsPerPage)
|
||||
{
|
||||
GUILayout.EndHorizontal();
|
||||
GUILayout.BeginHorizontal(null);
|
||||
|
||||
GUILayout.Space(whitespace);
|
||||
|
||||
Pages.CurrentPageLabel();
|
||||
|
||||
// prev/next page buttons
|
||||
if (GUILayout.Button("< Prev", new GUILayoutOption[] { GUILayout.Width(60) }))
|
||||
{
|
||||
Pages.TurnPage(Turn.Left);
|
||||
}
|
||||
if (GUILayout.Button("Next >", new GUILayoutOption[] { GUILayout.Width(60) }))
|
||||
{
|
||||
Pages.TurnPage(Turn.Right);
|
||||
}
|
||||
|
||||
Pages.DrawLimitInputArea();
|
||||
|
||||
GUILayout.Space(5);
|
||||
}
|
||||
|
||||
int offset = Pages.CalculateOffsetIndex();
|
||||
|
||||
for (int i = offset; i < offset + Pages.ItemsPerPage && i < count; i++)
|
||||
{
|
||||
var key = m_cachedKeys[i];
|
||||
var val = m_cachedValues[i];
|
||||
|
||||
//collapsing the BeginHorizontal called from ReflectionWindow.WindowFunction or previous array entry
|
||||
GUILayout.EndHorizontal();
|
||||
GUILayout.BeginHorizontal(null);
|
||||
|
||||
//GUILayout.Space(whitespace);
|
||||
|
||||
if (key == null || val == null)
|
||||
{
|
||||
GUILayout.Label($"[{i}] <i><color=grey>(null)</color></i>", null);
|
||||
}
|
||||
else
|
||||
{
|
||||
GUI.skin.label.alignment = TextAnchor.MiddleCenter;
|
||||
GUILayout.Label($"[{i}]", new GUILayoutOption[] { GUILayout.Width(30) });
|
||||
|
||||
GUILayout.BeginHorizontal(new GUILayoutOption[] { GUILayout.MinWidth((window.width / 3) - 60f) });
|
||||
GUILayout.Label("Key:", new GUILayoutOption[] { GUILayout.Width(40) });
|
||||
key.DrawValue(window, (window.width / 2) - 30f);
|
||||
GUILayout.EndHorizontal();
|
||||
|
||||
GUILayout.BeginHorizontal(null);
|
||||
GUILayout.Label("Value:", new GUILayoutOption[] { GUILayout.Width(40) });
|
||||
val.DrawValue(window, (window.width / 2) - 30f);
|
||||
GUILayout.EndHorizontal();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
GUI.skin.label.alignment = TextAnchor.UpperLeft;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -218,6 +218,7 @@ namespace Explorer
|
||||
|
||||
if (GetCacheObject(obj, t) is CacheObjectBase cached)
|
||||
{
|
||||
cached.UpdateValue();
|
||||
list.Add(cached);
|
||||
}
|
||||
else
|
||||
@ -262,7 +263,7 @@ namespace Explorer
|
||||
}
|
||||
|
||||
GUI.skin.button.alignment = TextAnchor.MiddleLeft;
|
||||
string btnLabel = "<color=yellow>[" + count + "] " + EntryType + "</color>";
|
||||
string btnLabel = "<color=yellow>[" + count + "] " + EntryType.FullName + "</color>";
|
||||
if (GUILayout.Button(btnLabel, new GUILayoutOption[] { GUILayout.MaxWidth(window.width - ButtonWidthOffset) }))
|
||||
{
|
||||
WindowManager.InspectObject(Value, out bool _);
|
||||
@ -305,12 +306,6 @@ namespace Explorer
|
||||
GUILayout.Space(5);
|
||||
}
|
||||
|
||||
//int offset = ArrayOffset * ArrayLimit;
|
||||
//if (offset >= count)
|
||||
//{
|
||||
// offset = 0;
|
||||
// ArrayOffset = 0;
|
||||
//}
|
||||
int offset = Pages.CalculateOffsetIndex();
|
||||
|
||||
for (int i = offset; i < offset + Pages.ItemsPerPage && i < count; i++)
|
||||
|
@ -212,6 +212,11 @@ namespace Explorer
|
||||
cacheList.WhiteSpace = 0f;
|
||||
cacheList.ButtonWidthOffset += 70f;
|
||||
}
|
||||
else if (m_cachedReturnValue is CacheDictionary cacheDict)
|
||||
{
|
||||
cacheDict.WhiteSpace = 0f;
|
||||
cacheDict.ButtonWidthOffset += 70f;
|
||||
}
|
||||
m_cachedReturnValue.UpdateValue();
|
||||
}
|
||||
else
|
||||
|
@ -51,7 +51,7 @@ namespace Explorer
|
||||
}
|
||||
}
|
||||
|
||||
GUILayout.Label(Value.ToString(), null);// + "<color=yellow><i> (" + ValueType + ")</i></color>", null);
|
||||
GUILayout.Label(Value.ToString() + "<color=yellow><i> (" + ValueType + ")</i></color>", null);
|
||||
}
|
||||
|
||||
public void SetEnum(ref object value, int change)
|
||||
|
@ -8,58 +8,34 @@ namespace Explorer
|
||||
{
|
||||
public class CachePrimitive : CacheObjectBase
|
||||
{
|
||||
public enum Types
|
||||
{
|
||||
Bool,
|
||||
Double,
|
||||
Float,
|
||||
Int,
|
||||
String,
|
||||
Char
|
||||
}
|
||||
private bool m_isBool;
|
||||
private bool m_isString;
|
||||
|
||||
private string m_valueToString;
|
||||
|
||||
public Types PrimitiveType;
|
||||
|
||||
public MethodInfo ParseMethod => m_parseMethod ?? (m_parseMethod = Value.GetType().GetMethod("Parse", new Type[] { typeof(string) }));
|
||||
private MethodInfo m_parseMethod;
|
||||
|
||||
public override void Init()
|
||||
{
|
||||
if (Value == null)
|
||||
if (ValueType == null)
|
||||
{
|
||||
// this must mean it is a string. No other primitive type should be nullable.
|
||||
PrimitiveType = Types.String;
|
||||
return;
|
||||
ValueType = Value?.GetType();
|
||||
|
||||
// has to be a string at this point
|
||||
if (ValueType == null)
|
||||
{
|
||||
ValueType = typeof(string);
|
||||
}
|
||||
}
|
||||
|
||||
m_valueToString = Value.ToString();
|
||||
|
||||
var type = Value.GetType();
|
||||
if (type == typeof(bool))
|
||||
if (ValueType == typeof(string))
|
||||
{
|
||||
PrimitiveType = Types.Bool;
|
||||
m_isString = true;
|
||||
}
|
||||
else if (type == typeof(double))
|
||||
else if (ValueType == typeof(bool))
|
||||
{
|
||||
PrimitiveType = Types.Double;
|
||||
}
|
||||
else if (type == typeof(float))
|
||||
{
|
||||
PrimitiveType = Types.Float;
|
||||
}
|
||||
else if (type == typeof(char))
|
||||
{
|
||||
PrimitiveType = Types.Char;
|
||||
}
|
||||
else if (typeof(int).IsAssignableFrom(type))
|
||||
{
|
||||
PrimitiveType = Types.Int;
|
||||
}
|
||||
else
|
||||
{
|
||||
PrimitiveType = Types.String;
|
||||
m_isBool = true;
|
||||
}
|
||||
}
|
||||
|
||||
@ -72,7 +48,7 @@ namespace Explorer
|
||||
|
||||
public override void DrawValue(Rect window, float width)
|
||||
{
|
||||
if (PrimitiveType == Types.Bool)
|
||||
if (m_isBool)
|
||||
{
|
||||
var b = (bool)Value;
|
||||
var label = $"<color={(b ? "lime" : "red")}>{b}</color>";
|
||||
@ -92,7 +68,8 @@ namespace Explorer
|
||||
}
|
||||
else
|
||||
{
|
||||
GUILayout.Label("<color=yellow><i>" + PrimitiveType + "</i></color>", new GUILayoutOption[] { GUILayout.Width(50) });
|
||||
// using ValueType.Name instead of ValueTypeName, because we only want the short name.
|
||||
GUILayout.Label("<color=yellow><i>" + ValueType.Name + "</i></color>", new GUILayoutOption[] { GUILayout.Width(50) });
|
||||
|
||||
int dynSize = 25 + (m_valueToString.Length * 15);
|
||||
var maxwidth = window.width - 300f;
|
||||
@ -127,7 +104,7 @@ namespace Explorer
|
||||
return;
|
||||
}
|
||||
|
||||
if (PrimitiveType == Types.String)
|
||||
if (m_isString)
|
||||
{
|
||||
Value = valueString;
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
@ -12,7 +13,7 @@ namespace Explorer
|
||||
public class CppExplorer : MelonMod
|
||||
{
|
||||
public const string GUID = "com.sinai.cppexplorer";
|
||||
public const string VERSION = "1.5.7";
|
||||
public const string VERSION = "1.6.0";
|
||||
public const string AUTHOR = "Sinai";
|
||||
|
||||
public const string NAME = "CppExplorer"
|
||||
|
@ -16,7 +16,18 @@ namespace Explorer
|
||||
public class PageHelper
|
||||
{
|
||||
public int PageOffset { get; set; }
|
||||
public int ItemsPerPage { get; set; } = 20;
|
||||
|
||||
public int ItemsPerPage
|
||||
{
|
||||
get => m_itemsPerPage;
|
||||
set
|
||||
{
|
||||
m_itemsPerPage = value;
|
||||
CalculateMaxOffset();
|
||||
}
|
||||
}
|
||||
private int m_itemsPerPage = 20;
|
||||
|
||||
public int ItemCount
|
||||
{
|
||||
get => m_count;
|
||||
|
@ -74,12 +74,13 @@ namespace Explorer
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool IsArray(Type t)
|
||||
public static bool IsEnumerable(Type t)
|
||||
{
|
||||
return typeof(System.Collections.IEnumerable).IsAssignableFrom(t);
|
||||
}
|
||||
|
||||
public static bool IsList(Type t)
|
||||
// Only Il2Cpp List needs this check. C# List is IEnumerable.
|
||||
public static bool IsCppList(Type t)
|
||||
{
|
||||
if (t.IsGenericType)
|
||||
{
|
||||
@ -98,21 +99,21 @@ namespace Explorer
|
||||
{
|
||||
return t.IsGenericType
|
||||
&& t.GetGenericTypeDefinition() is Type typeDef
|
||||
&& typeDef.IsAssignableFrom(typeof(Il2CppSystem.Collections.Generic.Dictionary<,>));
|
||||
&& (typeDef.IsAssignableFrom(typeof(Il2CppSystem.Collections.Generic.Dictionary<,>))
|
||||
|| typeDef.IsAssignableFrom(typeof(Dictionary<,>)));
|
||||
}
|
||||
|
||||
public static Type GetTypeByName(string typeName)
|
||||
{
|
||||
foreach (var asm in AppDomain.CurrentDomain.GetAssemblies())
|
||||
{
|
||||
try
|
||||
foreach (var type in GetTypesSafe(asm))
|
||||
{
|
||||
if (asm.GetType(typeName) is Type type)
|
||||
if (type.FullName == typeName)
|
||||
{
|
||||
return type;
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
return null;
|
||||
@ -137,6 +138,13 @@ namespace Explorer
|
||||
return obj.GetType();
|
||||
}
|
||||
|
||||
public static IEnumerable<Type> GetTypesSafe(Assembly asm)
|
||||
{
|
||||
try { return asm.GetTypes(); }
|
||||
catch (ReflectionTypeLoadException e) { return e.Types.Where(x => x != null); }
|
||||
catch { return Enumerable.Empty<Type>(); }
|
||||
}
|
||||
|
||||
public static Type[] GetAllBaseTypes(object obj)
|
||||
{
|
||||
var list = new List<Type>();
|
||||
|
@ -56,7 +56,10 @@ namespace Explorer
|
||||
{
|
||||
_horizBarStyle = new GUIStyle();
|
||||
_horizBarStyle.normal.background = Texture2D.whiteTexture;
|
||||
_horizBarStyle.margin = new RectOffset(0, 0, 4, 4);
|
||||
var rectOffset = new RectOffset();
|
||||
rectOffset.top = 4;
|
||||
rectOffset.bottom = 4;
|
||||
_horizBarStyle.margin = rectOffset;
|
||||
_horizBarStyle.fixedHeight = 2;
|
||||
}
|
||||
|
||||
@ -73,7 +76,10 @@ namespace Explorer
|
||||
{
|
||||
_horizBarSmallStyle = new GUIStyle();
|
||||
_horizBarSmallStyle.normal.background = Texture2D.whiteTexture;
|
||||
_horizBarSmallStyle.margin = new RectOffset(0, 0, 2, 2);
|
||||
var rectOffset = new RectOffset();
|
||||
rectOffset.top = 2;
|
||||
rectOffset.bottom = 2;
|
||||
_horizBarSmallStyle.margin = rectOffset;
|
||||
_horizBarSmallStyle.fixedHeight = 1;
|
||||
}
|
||||
|
||||
|
@ -21,8 +21,6 @@ namespace Explorer
|
||||
|
||||
private static bool m_getRootObjectsFailed;
|
||||
|
||||
// ----- Holders for GUI elements ----- //
|
||||
|
||||
private static string m_currentScene = "";
|
||||
|
||||
// gameobject list
|
||||
@ -34,8 +32,6 @@ namespace Explorer
|
||||
private string m_searchInput = "";
|
||||
private List<GameObjectCache> m_searchResults = new List<GameObjectCache>();
|
||||
|
||||
// ------------ Init and Update ------------ //
|
||||
|
||||
public override void Init()
|
||||
{
|
||||
Instance = this;
|
||||
@ -126,9 +122,7 @@ namespace Explorer
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!manual && m_getRootObjectsFailed) return;
|
||||
|
||||
if (!manual)
|
||||
if (!m_getRootObjectsFailed)
|
||||
{
|
||||
try
|
||||
{
|
||||
@ -139,12 +133,19 @@ namespace Explorer
|
||||
}
|
||||
catch
|
||||
{
|
||||
MelonLogger.Log("Exception getting root scene objects, falling back to backup method...");
|
||||
|
||||
m_getRootObjectsFailed = true;
|
||||
allTransforms.AddRange(GetRootObjectsManual_Impl());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!manual)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
allTransforms.AddRange(GetRootObjectsManual_Impl());
|
||||
}
|
||||
}
|
||||
|
@ -137,14 +137,11 @@ namespace Explorer
|
||||
|
||||
if (m_searchResults.Count > 0)
|
||||
{
|
||||
//int offset = m_pageOffset * this.m_limit;
|
||||
//if (offset >= count) m_pageOffset = 0;
|
||||
int offset = Pages.CalculateOffsetIndex();
|
||||
|
||||
for (int i = offset; i < offset + Pages.ItemsPerPage && i < count; i++)
|
||||
{
|
||||
m_searchResults[i].Draw(MainMenu.MainRect, 0f);
|
||||
//m_searchResults[i].DrawValue(MainMenu.MainRect);
|
||||
}
|
||||
}
|
||||
else
|
||||
@ -175,16 +172,6 @@ namespace Explorer
|
||||
GUILayout.Label("Name Contains:", new GUILayoutOption[] { GUILayout.Width(100) });
|
||||
m_searchInput = GUILayout.TextField(m_searchInput, new GUILayoutOption[] { GUILayout.Width(200) });
|
||||
|
||||
//GUI.skin.label.alignment = TextAnchor.MiddleRight;
|
||||
//GUILayout.Label("Results per page:", new GUILayoutOption[] { GUILayout.Width(120) });
|
||||
//var resultinput = m_limit.ToString();
|
||||
//resultinput = GUILayout.TextField(resultinput, new GUILayoutOption[] { GUILayout.Width(55) });
|
||||
//if (int.TryParse(resultinput, out int _i) && _i > 0)
|
||||
//{
|
||||
// m_limit = _i;
|
||||
//}
|
||||
//GUI.skin.label.alignment = TextAnchor.UpperLeft;
|
||||
|
||||
GUILayout.EndHorizontal();
|
||||
|
||||
GUILayout.BeginHorizontal(null);
|
||||
@ -266,7 +253,7 @@ namespace Explorer
|
||||
CacheResults(FindAllObjectsOfType(m_searchInput, m_typeInput));
|
||||
}
|
||||
|
||||
private List<object> FindAllObjectsOfType(string _search, string _type)
|
||||
private List<object> FindAllObjectsOfType(string searchQuery, string typeName)
|
||||
{
|
||||
Il2CppSystem.Type searchType = null;
|
||||
|
||||
@ -274,13 +261,18 @@ namespace Explorer
|
||||
{
|
||||
try
|
||||
{
|
||||
var findType = ReflectionHelpers.GetTypeByName(_type);
|
||||
searchType = Il2CppSystem.Type.GetType(findType.AssemblyQualifiedName);
|
||||
//MelonLogger.Log("Search type: " + findType.AssemblyQualifiedName);
|
||||
if (ReflectionHelpers.GetTypeByName(typeName) is Type t)
|
||||
{
|
||||
searchType = Il2CppSystem.Type.GetType(t.AssemblyQualifiedName);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception($"Could not find a Type by the name of '{typeName}'!");
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
MelonLogger.Log("Exception: " + e.GetType() + ", " + e.Message + "\r\n" + e.StackTrace);
|
||||
MelonLogger.Log("Exception getting Search Type: " + e.GetType() + ", " + e.Message);
|
||||
}
|
||||
}
|
||||
else if (TypeMode == TypeFilter.Object)
|
||||
@ -298,7 +290,10 @@ namespace Explorer
|
||||
|
||||
if (!ReflectionHelpers.ObjectType.IsAssignableFrom(searchType))
|
||||
{
|
||||
MelonLogger.LogError("Your Custom Class Type must inherit from UnityEngine.Object!");
|
||||
if (searchType != null)
|
||||
{
|
||||
MelonLogger.LogWarning("Your Custom Class Type must inherit from UnityEngine.Object!");
|
||||
}
|
||||
return new List<object>();
|
||||
}
|
||||
|
||||
@ -313,7 +308,7 @@ namespace Explorer
|
||||
{
|
||||
if (i >= 2000) break;
|
||||
|
||||
if (_search != "" && !obj.name.ToLower().Contains(_search.ToLower()))
|
||||
if (searchQuery != "" && !obj.name.ToLower().Contains(searchQuery.ToLower()))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@ -377,43 +372,70 @@ namespace Explorer
|
||||
|
||||
// ====== other ========
|
||||
|
||||
private static bool FilterName(string name)
|
||||
{
|
||||
// Don't really want these instances.
|
||||
return !name.StartsWith("Mono")
|
||||
&& !name.StartsWith("System")
|
||||
&& !name.StartsWith("Il2CppSystem")
|
||||
&& !name.StartsWith("Iced");
|
||||
}
|
||||
|
||||
// credit: ManlyMarco (RuntimeUnityEditor)
|
||||
public static IEnumerable<object> GetInstanceClassScanner()
|
||||
{
|
||||
var query = AppDomain.CurrentDomain.GetAssemblies()
|
||||
.Where(x => !x.FullName.StartsWith("Mono"))
|
||||
.SelectMany(GetTypesSafe)
|
||||
.SelectMany(ReflectionHelpers.GetTypesSafe)
|
||||
.Where(t => t.IsClass && !t.IsAbstract && !t.ContainsGenericParameters);
|
||||
|
||||
var flags = BindingFlags.Public | BindingFlags.Static;
|
||||
var flatFlags = flags | BindingFlags.FlattenHierarchy;
|
||||
|
||||
foreach (var type in query)
|
||||
{
|
||||
object obj = null;
|
||||
try
|
||||
{
|
||||
obj = type.GetProperty("Instance", BindingFlags.Public | BindingFlags.Static | BindingFlags.FlattenHierarchy)?.GetValue(null, null);
|
||||
}
|
||||
catch
|
||||
{
|
||||
try
|
||||
var pi = type.GetProperty("Instance", flags);
|
||||
|
||||
if (pi == null)
|
||||
{
|
||||
obj = type.GetField("Instance", BindingFlags.Public | BindingFlags.Static | BindingFlags.FlattenHierarchy)?.GetValue(null);
|
||||
pi = type.GetProperty("Instance", flatFlags);
|
||||
}
|
||||
catch
|
||||
|
||||
if (pi != null)
|
||||
{
|
||||
obj = pi.GetValue(null);
|
||||
}
|
||||
else
|
||||
{
|
||||
var fi = type.GetField("Instance", flags);
|
||||
|
||||
if (fi == null)
|
||||
{
|
||||
fi = type.GetField("Instance", flatFlags);
|
||||
}
|
||||
|
||||
if (fi != null)
|
||||
{
|
||||
obj = fi.GetValue(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (obj != null && !obj.ToString().StartsWith("Mono"))
|
||||
catch { }
|
||||
|
||||
if (obj != null)
|
||||
{
|
||||
var t = ReflectionHelpers.GetActualType(obj);
|
||||
|
||||
if (!FilterName(t.FullName) || ReflectionHelpers.IsEnumerable(t) || ReflectionHelpers.IsCppList(t))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
yield return obj;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static IEnumerable<Type> GetTypesSafe(Assembly asm)
|
||||
{
|
||||
try { return asm.GetTypes(); }
|
||||
catch (ReflectionTypeLoadException e) { return e.Types.Where(x => x != null); }
|
||||
catch { return Enumerable.Empty<Type>(); }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -24,13 +24,23 @@ namespace Explorer
|
||||
{
|
||||
get
|
||||
{
|
||||
#if Release_2019
|
||||
return GUI.scrollViewStates;
|
||||
#else
|
||||
return GUI.s_ScrollViewStates;
|
||||
#endif
|
||||
if (m_scrollViewStatesInfo == null)
|
||||
{
|
||||
try
|
||||
{
|
||||
m_scrollViewStatesInfo = typeof(GUI).GetProperty("scrollViewStates");
|
||||
if (m_scrollViewStatesInfo == null) throw new Exception();
|
||||
}
|
||||
catch
|
||||
{
|
||||
m_scrollViewStatesInfo = typeof(GUI).GetProperty("s_scrollViewStates");
|
||||
}
|
||||
}
|
||||
|
||||
return (GenericStack)m_scrollViewStatesInfo?.GetValue(null, null);
|
||||
}
|
||||
}
|
||||
private static PropertyInfo m_scrollViewStatesInfo;
|
||||
|
||||
// ======= public methods ======= //
|
||||
|
||||
@ -49,24 +59,6 @@ namespace Explorer
|
||||
return last;
|
||||
}
|
||||
|
||||
public static float HorizontalScrollbar(Rect position, float value, float size, float leftValue, float rightValue, GUIStyle style)
|
||||
{
|
||||
return Scroller_Impl(position, value, size, leftValue, rightValue, style,
|
||||
GUI.skin.GetStyle(style.name + "thumb"),
|
||||
GUI.skin.GetStyle(style.name + "leftbutton"),
|
||||
GUI.skin.GetStyle(style.name + "rightbutton"),
|
||||
true);
|
||||
}
|
||||
|
||||
public static float VerticalScrollbar(Rect position, float value, float size, float topValue, float bottomValue, GUIStyle style)
|
||||
{
|
||||
return Scroller_Impl(position, value, size, topValue, bottomValue, style,
|
||||
GUI.skin.GetStyle(style.name + "thumb"),
|
||||
GUI.skin.GetStyle(style.name + "upbutton"),
|
||||
GUI.skin.GetStyle(style.name + "downbutton"),
|
||||
false);
|
||||
}
|
||||
|
||||
// Fix for BeginScrollView.
|
||||
|
||||
public static Vector2 BeginScrollView(Vector2 scroll, params GUILayoutOption[] options)
|
||||
@ -221,7 +213,7 @@ namespace Explorer
|
||||
}
|
||||
if (flag2 && horizontalScrollbar != GUIStyle.none)
|
||||
{
|
||||
scrollPosition.x = HorizontalScrollbar(
|
||||
scrollPosition.x = HorizBar_Impl(
|
||||
new Rect(
|
||||
position.x,
|
||||
position.yMax - horizontalScrollbar.fixedHeight,
|
||||
@ -243,7 +235,7 @@ namespace Explorer
|
||||
}
|
||||
if (flag && verticalScrollbar != GUIStyle.none)
|
||||
{
|
||||
scrollPosition.y = VerticalScrollbar(
|
||||
scrollPosition.y = VertBar_Impl(
|
||||
new Rect(
|
||||
screenRect.xMax + (float)verticalScrollbar.margin.left,
|
||||
screenRect.y,
|
||||
@ -279,6 +271,24 @@ namespace Explorer
|
||||
return scrollPosition;
|
||||
}
|
||||
|
||||
public static float HorizBar_Impl(Rect position, float value, float size, float leftValue, float rightValue, GUIStyle style)
|
||||
{
|
||||
return Scroller_Impl(position, value, size, leftValue, rightValue, style,
|
||||
GUI.skin.GetStyle(style.name + "thumb"),
|
||||
GUI.skin.GetStyle(style.name + "leftbutton"),
|
||||
GUI.skin.GetStyle(style.name + "rightbutton"),
|
||||
true);
|
||||
}
|
||||
|
||||
public static float VertBar_Impl(Rect position, float value, float size, float topValue, float bottomValue, GUIStyle style)
|
||||
{
|
||||
return Scroller_Impl(position, value, size, topValue, bottomValue, style,
|
||||
GUI.skin.GetStyle(style.name + "thumb"),
|
||||
GUI.skin.GetStyle(style.name + "upbutton"),
|
||||
GUI.skin.GetStyle(style.name + "downbutton"),
|
||||
false);
|
||||
}
|
||||
|
||||
private static void EndScrollView_Impl(bool handleScrollWheel)
|
||||
{
|
||||
GUIUtility.CheckOnGUI();
|
||||
@ -337,7 +347,7 @@ namespace Explorer
|
||||
rect2 = new Rect(position.x, position.yMax - rightButton.fixedHeight, position.width, rightButton.fixedHeight);
|
||||
}
|
||||
|
||||
value = Slider(position2, value, size, leftValue, rightValue, slider, thumb, horiz, controlID);
|
||||
value = Slider_Impl(position2, value, size, leftValue, rightValue, slider, thumb, horiz, controlID);
|
||||
|
||||
bool flag = Event.current.type == EventType.MouseUp;
|
||||
if (ScrollerRepeatButton_Impl(controlID, rect, leftButton))
|
||||
@ -363,7 +373,7 @@ namespace Explorer
|
||||
return value;
|
||||
}
|
||||
|
||||
public static float Slider(Rect position, float value, float size, float start, float end, GUIStyle slider, GUIStyle thumb, bool horiz, int id)
|
||||
public static float Slider_Impl(Rect position, float value, float size, float start, float end, GUIStyle slider, GUIStyle thumb, bool horiz, int id)
|
||||
{
|
||||
if (id == 0)
|
||||
{
|
||||
|
Reference in New Issue
Block a user