Compare commits

..

3 Commits
1.5.7 ... 1.5.9

Author SHA1 Message Date
692a37635e 1.5.9
* Added beta support for Dictionaries. Should work fine for simple dictionaries, may be janky or broken for more complex ones (eg. Dicts nested inside a Dict).
* Fixed a bug with Lists of primitive values.
2020-09-06 21:33:09 +10:00
9cb1cea025 Update README.md 2020-09-06 17:48:40 +10:00
e13f198815 1.5.8
* Fixed a bug where the Page Helper would not update the total page count after changing the limit per page
* Cleaned up the "Find Instances" helper, it will now filter out all types in the `System`, `Mono`, `Il2CppSystem` and `Iced` namespaces.
* Improved the Find Instances helper so that it will avoid exceptions and get more results.
* Enums now display their value type name
* Changed the Scroll View unstrip so that it is less hard-coded for different unity versions and more dynamic.
2020-09-06 16:55:39 +10:00
12 changed files with 420 additions and 143 deletions

View File

@ -1,4 +1,4 @@
# CppExplorer [![Version](https://img.shields.io/badge/MelonLoader-0.2.7.1-green.svg)]() # CppExplorer [![Version](https://img.shields.io/badge/MelonLoader-0.2.7.1-green.svg)](https://github.com/HerpDerpinstine/MelonLoader)
<p align="center"> <p align="center">
<img align="center" src="https://sinai-dev.github.io/images/thumbs/02.png"> <img align="center" src="https://sinai-dev.github.io/images/thumbs/02.png">

View File

@ -14,6 +14,7 @@ namespace Explorer
{ {
public object Value; public object Value;
public string ValueTypeName; public string ValueTypeName;
public Type ValueType;
// Reflection Inspector only // Reflection Inspector only
public MemberInfo MemInfo { get; set; } public MemberInfo MemInfo { get; set; }
@ -79,11 +80,7 @@ namespace Explorer
{ {
Type type = null; Type type = null;
if (obj != null) if (memberInfo != null)
{
type = ReflectionHelpers.GetActualType(obj);
}
else if (memberInfo != null)
{ {
if (memberInfo is FieldInfo fi) if (memberInfo is FieldInfo fi)
{ {
@ -98,6 +95,10 @@ namespace Explorer
type = mi.ReturnType; type = mi.ReturnType;
} }
} }
else if (obj != null)
{
type = ReflectionHelpers.GetActualType(obj);
}
if (type == null) if (type == null)
{ {
@ -114,6 +115,11 @@ namespace Explorer
{ {
CacheObjectBase holder; 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 (memberInfo is MethodInfo mi)
{ {
if (CacheMethod.CanEvaluate(mi)) if (CacheMethod.CanEvaluate(mi))
@ -153,20 +159,22 @@ namespace Explorer
{ {
holder = new CacheRect(); holder = new CacheRect();
} }
else if (ReflectionHelpers.IsArray(valueType) || ReflectionHelpers.IsList(valueType)) // must check this before IsEnumerable
{
holder = new CacheList();
}
else if (ReflectionHelpers.IsDictionary(valueType)) else if (ReflectionHelpers.IsDictionary(valueType))
{ {
holder = new CacheDictionary(); holder = new CacheDictionary();
} }
else if (ReflectionHelpers.IsEnumerable(valueType) || ReflectionHelpers.IsCppList(valueType))
{
holder = new CacheList();
}
else else
{ {
holder = new CacheOther(); holder = new CacheOther();
} }
holder.Value = obj; holder.Value = obj;
holder.ValueType = valueType;
holder.ValueTypeName = valueType.FullName; holder.ValueTypeName = valueType.FullName;
if (memberInfo != null) if (memberInfo != null)

View File

@ -1,34 +1,278 @@
using System; using System;
using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using MelonLoader; using MelonLoader;
using UnityEngine; using UnityEngine;
using System.Reflection;
namespace Explorer namespace Explorer
{ {
public class CacheDictionary : CacheObjectBase 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 a list 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() 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) 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;
}
} }
} }
} }

View File

@ -218,6 +218,7 @@ namespace Explorer
if (GetCacheObject(obj, t) is CacheObjectBase cached) if (GetCacheObject(obj, t) is CacheObjectBase cached)
{ {
cached.UpdateValue();
list.Add(cached); list.Add(cached);
} }
else else
@ -262,7 +263,7 @@ namespace Explorer
} }
GUI.skin.button.alignment = TextAnchor.MiddleLeft; 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) })) if (GUILayout.Button(btnLabel, new GUILayoutOption[] { GUILayout.MaxWidth(window.width - ButtonWidthOffset) }))
{ {
WindowManager.InspectObject(Value, out bool _); WindowManager.InspectObject(Value, out bool _);
@ -305,12 +306,6 @@ namespace Explorer
GUILayout.Space(5); GUILayout.Space(5);
} }
//int offset = ArrayOffset * ArrayLimit;
//if (offset >= count)
//{
// offset = 0;
// ArrayOffset = 0;
//}
int offset = Pages.CalculateOffsetIndex(); int offset = Pages.CalculateOffsetIndex();
for (int i = offset; i < offset + Pages.ItemsPerPage && i < count; i++) for (int i = offset; i < offset + Pages.ItemsPerPage && i < count; i++)

View File

@ -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) public void SetEnum(ref object value, int change)

View File

@ -8,58 +8,34 @@ namespace Explorer
{ {
public class CachePrimitive : CacheObjectBase public class CachePrimitive : CacheObjectBase
{ {
public enum Types private bool m_isBool;
{ private bool m_isString;
Bool,
Double,
Float,
Int,
String,
Char
}
private string m_valueToString; private string m_valueToString;
public Types PrimitiveType;
public MethodInfo ParseMethod => m_parseMethod ?? (m_parseMethod = Value.GetType().GetMethod("Parse", new Type[] { typeof(string) })); public MethodInfo ParseMethod => m_parseMethod ?? (m_parseMethod = Value.GetType().GetMethod("Parse", new Type[] { typeof(string) }));
private MethodInfo m_parseMethod; private MethodInfo m_parseMethod;
public override void Init() public override void Init()
{ {
if (Value == null) if (ValueType == null)
{ {
// this must mean it is a string. No other primitive type should be nullable. ValueType = Value?.GetType();
PrimitiveType = Types.String;
return; // has to be a string at this point
if (ValueType == null)
{
ValueType = typeof(string);
}
} }
m_valueToString = Value.ToString(); if (ValueType == typeof(string))
var type = Value.GetType();
if (type == typeof(bool))
{ {
PrimitiveType = Types.Bool; m_isString = true;
} }
else if (type == typeof(double)) else if (ValueType == typeof(bool))
{ {
PrimitiveType = Types.Double; m_isBool = true;
}
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;
} }
} }
@ -72,7 +48,7 @@ namespace Explorer
public override void DrawValue(Rect window, float width) public override void DrawValue(Rect window, float width)
{ {
if (PrimitiveType == Types.Bool) if (m_isBool)
{ {
var b = (bool)Value; var b = (bool)Value;
var label = $"<color={(b ? "lime" : "red")}>{b}</color>"; var label = $"<color={(b ? "lime" : "red")}>{b}</color>";
@ -92,7 +68,8 @@ namespace Explorer
} }
else 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); int dynSize = 25 + (m_valueToString.Length * 15);
var maxwidth = window.width - 300f; var maxwidth = window.width - 300f;
@ -127,7 +104,7 @@ namespace Explorer
return; return;
} }
if (PrimitiveType == Types.String) if (m_isString)
{ {
Value = valueString; Value = valueString;
} }

View File

@ -1,4 +1,5 @@
using System; using System;
using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
@ -12,7 +13,7 @@ namespace Explorer
public class CppExplorer : MelonMod public class CppExplorer : MelonMod
{ {
public const string GUID = "com.sinai.cppexplorer"; public const string GUID = "com.sinai.cppexplorer";
public const string VERSION = "1.5.7"; public const string VERSION = "1.5.9";
public const string AUTHOR = "Sinai"; public const string AUTHOR = "Sinai";
public const string NAME = "CppExplorer" public const string NAME = "CppExplorer"

View File

@ -16,7 +16,18 @@ namespace Explorer
public class PageHelper public class PageHelper
{ {
public int PageOffset { get; set; } 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 public int ItemCount
{ {
get => m_count; get => m_count;

View File

@ -74,12 +74,13 @@ namespace Explorer
return false; return false;
} }
public static bool IsArray(Type t) public static bool IsEnumerable(Type t)
{ {
return typeof(System.Collections.IEnumerable).IsAssignableFrom(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) if (t.IsGenericType)
{ {
@ -98,21 +99,21 @@ namespace Explorer
{ {
return t.IsGenericType return t.IsGenericType
&& t.GetGenericTypeDefinition() is Type typeDef && 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) public static Type GetTypeByName(string typeName)
{ {
foreach (var asm in AppDomain.CurrentDomain.GetAssemblies()) 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; return type;
} }
} }
catch { }
} }
return null; return null;
@ -137,6 +138,13 @@ namespace Explorer
return obj.GetType(); 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) public static Type[] GetAllBaseTypes(object obj)
{ {
var list = new List<Type>(); var list = new List<Type>();

View File

@ -21,8 +21,6 @@ namespace Explorer
private static bool m_getRootObjectsFailed; private static bool m_getRootObjectsFailed;
// ----- Holders for GUI elements ----- //
private static string m_currentScene = ""; private static string m_currentScene = "";
// gameobject list // gameobject list
@ -34,8 +32,6 @@ namespace Explorer
private string m_searchInput = ""; private string m_searchInput = "";
private List<GameObjectCache> m_searchResults = new List<GameObjectCache>(); private List<GameObjectCache> m_searchResults = new List<GameObjectCache>();
// ------------ Init and Update ------------ //
public override void Init() public override void Init()
{ {
Instance = this; Instance = this;
@ -126,9 +122,7 @@ namespace Explorer
} }
else else
{ {
if (!manual && m_getRootObjectsFailed) return; if (!m_getRootObjectsFailed)
if (!manual)
{ {
try try
{ {
@ -139,12 +133,19 @@ namespace Explorer
} }
catch catch
{ {
MelonLogger.Log("Exception getting root scene objects, falling back to backup method...");
m_getRootObjectsFailed = true; m_getRootObjectsFailed = true;
allTransforms.AddRange(GetRootObjectsManual_Impl()); allTransforms.AddRange(GetRootObjectsManual_Impl());
} }
} }
else else
{ {
if (!manual)
{
return;
}
allTransforms.AddRange(GetRootObjectsManual_Impl()); allTransforms.AddRange(GetRootObjectsManual_Impl());
} }
} }

View File

@ -137,14 +137,11 @@ namespace Explorer
if (m_searchResults.Count > 0) if (m_searchResults.Count > 0)
{ {
//int offset = m_pageOffset * this.m_limit;
//if (offset >= count) m_pageOffset = 0;
int offset = Pages.CalculateOffsetIndex(); int offset = Pages.CalculateOffsetIndex();
for (int i = offset; i < offset + Pages.ItemsPerPage && i < count; i++) for (int i = offset; i < offset + Pages.ItemsPerPage && i < count; i++)
{ {
m_searchResults[i].Draw(MainMenu.MainRect, 0f); m_searchResults[i].Draw(MainMenu.MainRect, 0f);
//m_searchResults[i].DrawValue(MainMenu.MainRect);
} }
} }
else else
@ -175,16 +172,6 @@ namespace Explorer
GUILayout.Label("Name Contains:", new GUILayoutOption[] { GUILayout.Width(100) }); GUILayout.Label("Name Contains:", new GUILayoutOption[] { GUILayout.Width(100) });
m_searchInput = GUILayout.TextField(m_searchInput, new GUILayoutOption[] { GUILayout.Width(200) }); m_searchInput = GUILayout.TextField(m_searchInput, new GUILayoutOption[] { GUILayout.Width(200) });
//GUI.skin.label.alignment = TextAnchor.MiddleRight;
//GUILayout.Label("Results per page:", new GUILayoutOption[] { GUILayout.Width(120) });
//var resultinput = m_limit.ToString();
//resultinput = GUILayout.TextField(resultinput, new GUILayoutOption[] { GUILayout.Width(55) });
//if (int.TryParse(resultinput, out int _i) && _i > 0)
//{
// m_limit = _i;
//}
//GUI.skin.label.alignment = TextAnchor.UpperLeft;
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
GUILayout.BeginHorizontal(null); GUILayout.BeginHorizontal(null);
@ -266,7 +253,7 @@ namespace Explorer
CacheResults(FindAllObjectsOfType(m_searchInput, m_typeInput)); CacheResults(FindAllObjectsOfType(m_searchInput, m_typeInput));
} }
private List<object> FindAllObjectsOfType(string _search, string _type) private List<object> FindAllObjectsOfType(string searchQuery, string typeName)
{ {
Il2CppSystem.Type searchType = null; Il2CppSystem.Type searchType = null;
@ -274,13 +261,18 @@ namespace Explorer
{ {
try try
{ {
var findType = ReflectionHelpers.GetTypeByName(_type); if (ReflectionHelpers.GetTypeByName(typeName) is Type t)
searchType = Il2CppSystem.Type.GetType(findType.AssemblyQualifiedName); {
//MelonLogger.Log("Search type: " + findType.AssemblyQualifiedName); searchType = Il2CppSystem.Type.GetType(t.AssemblyQualifiedName);
}
else
{
throw new Exception($"Could not find a Type by the name of '{typeName}'!");
}
} }
catch (Exception e) catch (Exception e)
{ {
MelonLogger.Log("Exception: " + e.GetType() + ", " + e.Message + "\r\n" + e.StackTrace); MelonLogger.Log("Exception getting Search Type: " + e.GetType() + ", " + e.Message);
} }
} }
else if (TypeMode == TypeFilter.Object) else if (TypeMode == TypeFilter.Object)
@ -298,7 +290,10 @@ namespace Explorer
if (!ReflectionHelpers.ObjectType.IsAssignableFrom(searchType)) if (!ReflectionHelpers.ObjectType.IsAssignableFrom(searchType))
{ {
MelonLogger.LogError("Your Custom Class Type must inherit from UnityEngine.Object!"); if (searchType != null)
{
MelonLogger.LogWarning("Your Custom Class Type must inherit from UnityEngine.Object!");
}
return new List<object>(); return new List<object>();
} }
@ -313,7 +308,7 @@ namespace Explorer
{ {
if (i >= 2000) break; if (i >= 2000) break;
if (_search != "" && !obj.name.ToLower().Contains(_search.ToLower())) if (searchQuery != "" && !obj.name.ToLower().Contains(searchQuery.ToLower()))
{ {
continue; continue;
} }
@ -377,43 +372,70 @@ namespace Explorer
// ====== other ======== // ====== 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) // credit: ManlyMarco (RuntimeUnityEditor)
public static IEnumerable<object> GetInstanceClassScanner() public static IEnumerable<object> GetInstanceClassScanner()
{ {
var query = AppDomain.CurrentDomain.GetAssemblies() var query = AppDomain.CurrentDomain.GetAssemblies()
.Where(x => !x.FullName.StartsWith("Mono")) .SelectMany(ReflectionHelpers.GetTypesSafe)
.SelectMany(GetTypesSafe)
.Where(t => t.IsClass && !t.IsAbstract && !t.ContainsGenericParameters); .Where(t => t.IsClass && !t.IsAbstract && !t.ContainsGenericParameters);
var flags = BindingFlags.Public | BindingFlags.Static;
var flatFlags = flags | BindingFlags.FlattenHierarchy;
foreach (var type in query) foreach (var type in query)
{ {
object obj = null; object obj = null;
try try
{ {
obj = type.GetProperty("Instance", BindingFlags.Public | BindingFlags.Static | BindingFlags.FlattenHierarchy)?.GetValue(null, null); var pi = type.GetProperty("Instance", flags);
}
catch if (pi == null)
{
try
{ {
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; 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>(); }
}
} }
} }

View File

@ -24,13 +24,23 @@ namespace Explorer
{ {
get get
{ {
#if Release_2019 if (m_scrollViewStatesInfo == null)
return GUI.scrollViewStates; {
#else try
return GUI.s_ScrollViewStates; {
#endif 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 ======= // // ======= public methods ======= //
@ -49,24 +59,6 @@ namespace Explorer
return last; 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. // Fix for BeginScrollView.
public static Vector2 BeginScrollView(Vector2 scroll, params GUILayoutOption[] options) public static Vector2 BeginScrollView(Vector2 scroll, params GUILayoutOption[] options)
@ -221,7 +213,7 @@ namespace Explorer
} }
if (flag2 && horizontalScrollbar != GUIStyle.none) if (flag2 && horizontalScrollbar != GUIStyle.none)
{ {
scrollPosition.x = HorizontalScrollbar( scrollPosition.x = HorizBar_Impl(
new Rect( new Rect(
position.x, position.x,
position.yMax - horizontalScrollbar.fixedHeight, position.yMax - horizontalScrollbar.fixedHeight,
@ -243,7 +235,7 @@ namespace Explorer
} }
if (flag && verticalScrollbar != GUIStyle.none) if (flag && verticalScrollbar != GUIStyle.none)
{ {
scrollPosition.y = VerticalScrollbar( scrollPosition.y = VertBar_Impl(
new Rect( new Rect(
screenRect.xMax + (float)verticalScrollbar.margin.left, screenRect.xMax + (float)verticalScrollbar.margin.left,
screenRect.y, screenRect.y,
@ -279,6 +271,24 @@ namespace Explorer
return scrollPosition; 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) private static void EndScrollView_Impl(bool handleScrollWheel)
{ {
GUIUtility.CheckOnGUI(); GUIUtility.CheckOnGUI();
@ -337,7 +347,7 @@ namespace Explorer
rect2 = new Rect(position.x, position.yMax - rightButton.fixedHeight, position.width, rightButton.fixedHeight); 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; bool flag = Event.current.type == EventType.MouseUp;
if (ScrollerRepeatButton_Impl(controlID, rect, leftButton)) if (ScrollerRepeatButton_Impl(controlID, rect, leftButton))
@ -363,7 +373,7 @@ namespace Explorer
return value; 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) if (id == 0)
{ {