Compare commits

...

14 Commits
1.5.6 ... 1.6.3

Author SHA1 Message Date
4bb0811b2c 1.6.3
* Merged the two builds into one, there is now only one release. Using Reflection for UnityEngine.Input
* A few small fixes and cleanups
2020-09-08 06:21:45 +10:00
4aefe1c5a3 a few tidy ups 2020-09-08 04:33:27 +10:00
c228d29707 Update CppExplorer.cs 2020-09-07 20:28:43 +10:00
56d1507aff 1.6.2
* Fix for a crash that can occur when inspecting unsupported Dictionaries
* Added a scroll bar to the REPL console input area, fixes the issue of the code just being cut off when it goes too long.
2020-09-07 20:28:33 +10:00
72d31eaa64 1.6.1
* Fix for when inspected object gets destroyed
* Fix for displaying Dictionaries/Lists nested inside a Dictionary
* Cleanups
2020-09-07 17:05:37 +10:00
4e8b84b67e Update CppExplorer.cs 2020-09-07 03:26:10 +10:00
5b94e31a12 1.6.0
* Fix for failed unstrip with RectOffset(int, int, int, int) ctor
* Cleanups
2020-09-07 03:25:43 +10:00
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
9a059c1056 Update ScenePage.cs 2020-09-06 03:19:39 +10:00
ffb6cad8c2 1.5.7
* More fixes for failed unstripping, should fix most issues in Audica and other games
* If `GetRootSceneObjects` fails and we fall back to the manual implementation, the auto-update for root scene objects will be disabled. Instead, there will be a button to press to update the list.
* Transforms are now listed on the Components list in the GameObject inspector
* Various cleanups
2020-09-06 03:19:21 +10:00
d0a4863139 Update ScenePage.cs 2020-09-05 23:18:58 +10:00
bb8837d58c 1.5.6 hotfix
* Fix for setting CacheColor value
* Cleanup
2020-09-05 23:10:50 +10:00
31 changed files with 780 additions and 451 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

@ -4,7 +4,6 @@ using System.Linq;
using System.Reflection; using System.Reflection;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using Explorer.CachedObjects;
using MelonLoader; using MelonLoader;
using UnhollowerBaseLib; using UnhollowerBaseLib;
using UnityEngine; using UnityEngine;
@ -15,16 +14,16 @@ namespace Explorer
{ {
public object Value; public object Value;
public string ValueTypeName; public string ValueTypeName;
public Type ValueType;
// Reflection Inspector only
public MemberInfo MemInfo { get; set; } public MemberInfo MemInfo { get; set; }
public Type DeclaringType { get; set; } public Type DeclaringType { get; set; }
public object DeclaringInstance { get; set; } public object DeclaringInstance { get; set; }
public string ReflectionException { get; set; }
public int PropertyIndex { get; private set; } public int PropertyIndex { get; private set; }
private string m_propertyIndexInput = "0"; private string m_propertyIndexInput = "0";
public string ReflectionException { get; set; }
public string RichTextName => m_richTextName ?? GetRichTextName(); public string RichTextName => m_richTextName ?? GetRichTextName();
private string m_richTextName; private string m_richTextName;
@ -80,11 +79,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)
{ {
@ -99,6 +94,10 @@ namespace Explorer
type = mi.ReturnType; type = mi.ReturnType;
} }
} }
else if (obj != null)
{
type = ReflectionHelpers.GetActualType(obj);
}
if (type == null) if (type == null)
{ {
@ -115,6 +114,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))
@ -154,20 +158,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,299 @@
using System; using System;
using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using MelonLoader; using MelonLoader;
using UnityEngine; using UnityEngine;
using System.Reflection;
using UnhollowerBaseLib;
namespace Explorer namespace Explorer
{ {
public class CacheDictionary : CacheObjectBase public class CacheDictionary : CacheObjectBase, IExpandHeight
{ {
public bool IsExpanded { get; set; }
public float WhiteSpace { get; set; } = 215f;
public float ButtonWidthOffset { get; set; } = 290f;
public PageHelper Pages = new PageHelper();
public override void Init() private CacheObjectBase[] m_cachedKeys;
private CacheObjectBase[] m_cachedValues;
public Type TypeOfKeys
{ {
//base.Init(); get
{
if (m_keysType == null) GetGenericArguments();
return m_keysType;
}
}
private Type m_keysType;
Value = "Unsupported"; public Type TypeOfValues
{
get
{
if (m_valuesType == null) GetGenericArguments();
return m_valuesType;
}
}
private Type m_valuesType;
public IDictionary IDict
{
get => m_iDictionary ?? (m_iDictionary = Value as IDictionary) ?? Il2CppDictionaryToMono();
set => m_iDictionary = value;
}
private IDictionary m_iDictionary;
// ========== Methods ==========
// This is a bit janky due to Il2Cpp Dictionary not implementing IDictionary.
private IDictionary Il2CppDictionaryToMono()
{
// note: "ValueType" is the Dictionary itself, TypeOfValues is the 'Dictionary.Values' type.
// get keys and values
var keys = ValueType.GetProperty("Keys") .GetValue(Value);
var values = ValueType.GetProperty("Values").GetValue(Value);
// create lists to hold them
var keyList = new List<object>();
var valueList = new List<object>();
// store entries with reflection
EnumerateWithReflection(keys, keyList);
EnumerateWithReflection(values, valueList);
// make actual mono dictionary
var dict = (IDictionary)Activator.CreateInstance(typeof(Dictionary<,>)
.MakeGenericType(TypeOfKeys, TypeOfValues));
// finally iterate into dictionary
for (int i = 0; i < keyList.Count; i++)
{
dict.Add(keyList[i], valueList[i]);
}
return dict;
}
private void EnumerateWithReflection(object collection, List<object> list)
{
// invoke GetEnumerator
var enumerator = collection.GetType().GetMethod("GetEnumerator").Invoke(collection, null);
// get the type of it
var enumeratorType = enumerator.GetType();
// reflect MoveNext and Current
var moveNext = enumeratorType.GetMethod("MoveNext");
var current = enumeratorType.GetProperty("Current");
// iterate
while ((bool)moveNext.Invoke(enumerator, null))
{
list.Add(current.GetValue(enumerator));
}
}
private void GetGenericArguments()
{
if (this.MemInfo != null)
{
Type memberType = null;
switch (this.MemInfo.MemberType)
{
case MemberTypes.Field:
memberType = (MemInfo as FieldInfo).FieldType;
break;
case MemberTypes.Property:
memberType = (MemInfo as PropertyInfo).PropertyType;
break;
}
if (memberType != null && memberType.IsGenericType)
{
m_keysType = memberType.GetGenericArguments()[0];
m_valuesType = memberType.GetGenericArguments()[1];
}
}
else if (Value != null)
{
var type = Value.GetType();
if (type.IsGenericType)
{
m_keysType = type.GetGenericArguments()[0];
m_valuesType = type.GetGenericArguments()[1];
}
}
} }
public override void UpdateValue() public override void UpdateValue()
{ {
//base.UpdateValue(); // first make sure we won't run into a TypeInitializationException.
if (!EnsureDictionaryIsSupported())
{
ReflectionException = "Dictionary Type not supported with Reflection!";
return;
}
base.UpdateValue();
// reset
IDict = null;
if (Value == null || IDict == null)
{
return;
}
var keys = new List<CacheObjectBase>();
foreach (var key in IDict.Keys)
{
var cache = GetCacheObject(key, TypeOfKeys);
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();
} }
private bool EnsureDictionaryIsSupported()
{
try
{
return Check(TypeOfKeys) && Check(TypeOfValues);
bool Check(Type type)
{
var ptr = (IntPtr)typeof(Il2CppClassPointerStore<>)
.MakeGenericType(type)
.GetField("NativeClassPtr")
.GetValue(null);
return Il2CppSystem.Type.internal_from_handle(IL2CPP.il2cpp_class_get_type(ptr)) is Il2CppSystem.Type;
}
}
catch
{
return false;
}
}
// ============= GUI Draw =============
public override void DrawValue(Rect window, float width) public override void DrawValue(Rect window, float width)
{ {
GUILayout.Label("<color=red>Dictionary (unsupported)</color>", null); if (m_cachedKeys == null || m_cachedValues == null)
{
GUILayout.Label("Cached keys or values is null!", null);
return;
}
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.Label("Key:", new GUILayoutOption[] { GUILayout.Width(40) });
key.DrawValue(window, (window.width / 2) - 30f);
GUILayout.Label("Value:", new GUILayoutOption[] { GUILayout.Width(40) });
val.DrawValue(window, (window.width / 2) - 30f);
}
}
GUI.skin.label.alignment = TextAnchor.UpperLeft;
}
} }
} }
} }

View File

@ -8,13 +8,13 @@ using UnityEngine;
namespace Explorer namespace Explorer
{ {
public class CacheList : CacheObjectBase public class CacheList : CacheObjectBase, IExpandHeight
{ {
public bool IsExpanded { get; set; } public bool IsExpanded { get; set; }
public PageHelper Pages = new PageHelper(); public float WhiteSpace { get; set; } = 215f;
public float ButtonWidthOffset { get; set; } = 290f;
public float WhiteSpace = 215f; public PageHelper Pages = new PageHelper();
public float ButtonWidthOffset = 290f;
private CacheObjectBase[] m_cachedEntries; private CacheObjectBase[] m_cachedEntries;
@ -52,6 +52,7 @@ namespace Explorer
{ {
get => GetItemProperty(); get => GetItemProperty();
} }
private PropertyInfo m_itemProperty; private PropertyInfo m_itemProperty;
// ========== Methods ========== // ========== Methods ==========
@ -218,6 +219,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 +264,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 +307,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

@ -18,28 +18,17 @@ namespace Explorer
private ParameterInfo[] m_arguments; private ParameterInfo[] m_arguments;
private string[] m_argumentInput; private string[] m_argumentInput;
public bool HasParameters public bool HasParameters => m_arguments != null && m_arguments.Length > 0;
{
get
{
if (m_hasParams == null)
{
m_hasParams = (MemInfo as MethodInfo).GetParameters().Length > 0;
}
return (bool)m_hasParams;
}
}
private bool? m_hasParams;
public static bool CanEvaluate(MethodInfo mi) public static bool CanEvaluate(MethodInfo mi)
{ {
// generic type args not supported yet // TODO generic args
if (mi.GetGenericArguments().Length > 0) if (mi.GetGenericArguments().Length > 0)
{ {
return false; return false;
} }
// only primitive and string args supported // primitive and string args supported
foreach (var param in mi.GetParameters()) foreach (var param in mi.GetParameters())
{ {
if (!param.ParameterType.IsPrimitive && param.ParameterType != typeof(string)) if (!param.ParameterType.IsPrimitive && param.ParameterType != typeof(string))
@ -64,7 +53,84 @@ namespace Explorer
public override void UpdateValue() public override void UpdateValue()
{ {
//base.UpdateValue(); //base.UpdateValue();
} }
private void Evaluate()
{
var mi = MemInfo as MethodInfo;
object ret = null;
if (!HasParameters)
{
ret = mi.Invoke(mi.IsStatic ? null : DeclaringInstance, new object[0]);
m_evaluated = true;
}
else
{
var parsedArgs = new List<object>();
for (int i = 0; i < m_arguments.Length; i++)
{
var input = m_argumentInput[i];
var type = m_arguments[i].ParameterType;
if (type == typeof(string))
{
parsedArgs.Add(input);
}
else
{
try
{
if (type.GetMethod("Parse", new Type[] { typeof(string) }).Invoke(null, new object[] { input }) is object parsed)
{
parsedArgs.Add(parsed);
}
else
{
// try add a null arg i guess
parsedArgs.Add(null);
}
}
catch
{
MelonLogger.Log($"Unable to parse '{input}' to type '{type.Name}'");
break;
}
}
}
try
{
ret = mi.Invoke(mi.IsStatic ? null : DeclaringInstance, parsedArgs.ToArray());
m_evaluated = true;
}
catch (Exception e)
{
MelonLogger.Log($"Exception evaluating: {e.GetType()}, {e.Message}");
}
}
if (ret != null)
{
m_cachedReturnValue = GetCacheObject(ret);
if (m_cachedReturnValue is IExpandHeight expander)
{
expander.WhiteSpace = 0f;
expander.ButtonWidthOffset += 70f;
}
m_cachedReturnValue.UpdateValue();
}
else
{
m_cachedReturnValue = null;
}
}
// ==== GUI DRAW ====
public override void DrawValue(Rect window, float width) public override void DrawValue(Rect window, float width)
{ {
@ -124,15 +190,7 @@ namespace Explorer
{ {
if (m_cachedReturnValue != null) if (m_cachedReturnValue != null)
{ {
try m_cachedReturnValue.DrawValue(window, width);
{
m_cachedReturnValue.DrawValue(window, width);
}
catch (Exception e)
{
MelonLogger.Log("Exception drawing m_cachedReturnValue!");
MelonLogger.Log(e.ToString());
}
} }
else else
{ {
@ -147,77 +205,5 @@ namespace Explorer
GUILayout.EndVertical(); GUILayout.EndVertical();
} }
private void Evaluate()
{
var mi = MemInfo as MethodInfo;
object ret = null;
if (!HasParameters)
{
ret = mi.Invoke(mi.IsStatic ? null : DeclaringInstance, new object[0]);
m_evaluated = true;
}
else
{
var arguments = new List<object>();
for (int i = 0; i < m_arguments.Length; i++)
{
var input = m_argumentInput[i];
var type = m_arguments[i].ParameterType;
if (type == typeof(string))
{
arguments.Add(input);
}
else
{
try
{
if (type.GetMethod("Parse", new Type[] { typeof(string) }).Invoke(null, new object[] { input }) is object parsed)
{
arguments.Add(parsed);
}
else
{
throw new Exception();
}
}
catch
{
MelonLogger.Log($"Unable to parse '{input}' to type '{type.Name}'");
break;
}
}
}
if (arguments.Count == m_arguments.Length)
{
ret = mi.Invoke(mi.IsStatic ? null : DeclaringInstance, arguments.ToArray());
m_evaluated = true;
}
else
{
MelonLogger.Log($"Did not invoke because {m_arguments.Length - arguments.Count} arguments could not be parsed!");
}
}
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;
}
}
} }
} }

View File

@ -50,7 +50,7 @@ namespace Explorer
} }
GUI.skin.button.alignment = TextAnchor.MiddleLeft; GUI.skin.button.alignment = TextAnchor.MiddleLeft;
if (GUILayout.Button("<color=yellow>" + label + "</color>", new GUILayoutOption[] { GUILayout.Width(width) })) if (GUILayout.Button("<color=yellow>" + label + "</color>", new GUILayoutOption[] { GUILayout.Width(width - 15) }))
{ {
WindowManager.InspectObject(Value, out bool _); WindowManager.InspectObject(Value, out bool _);
} }

View File

@ -5,7 +5,7 @@ using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using UnityEngine; using UnityEngine;
namespace Explorer.CachedObjects namespace Explorer
{ {
public class CacheColor : CacheObjectBase public class CacheColor : CacheObjectBase
{ {
@ -79,7 +79,8 @@ namespace Explorer.CachedObjects
&& float.TryParse(b, out float fB) && float.TryParse(b, out float fB)
&& float.TryParse(a, out float fA)) && float.TryParse(a, out float fA))
{ {
Value = new Color(fR, fB, fG, fA); Value = new Color(fR, fB, fG, fA);
SetValue();
} }
} }
} }

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,91 +8,37 @@ namespace Explorer
{ {
public class CachePrimitive : CacheObjectBase public class CachePrimitive : CacheObjectBase
{ {
public enum PrimitiveTypes private bool m_isBool;
{ private bool m_isString;
Bool,
Double,
Float,
Int,
String,
Char
}
private string m_valueToString; private string m_valueToString;
public PrimitiveTypes PrimitiveType; public MethodInfo ParseMethod => m_parseMethod ?? (m_parseMethod = Value.GetType().GetMethod("Parse", new Type[] { typeof(string) }));
public MethodInfo ParseMethod
{
get
{
if (m_parseMethod == null)
{
m_parseMethod = Value.GetType().GetMethod("Parse", new Type[] { typeof(string) });
}
return m_parseMethod;
}
}
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 = PrimitiveTypes.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 = PrimitiveTypes.Bool; m_isString = true;
} }
else if (type == typeof(double)) else if (ValueType == typeof(bool))
{ {
PrimitiveType = PrimitiveTypes.Double; m_isBool = true;
}
else if (type == typeof(float))
{
PrimitiveType = PrimitiveTypes.Float;
}
else if (IsInteger(type))
{
PrimitiveType = PrimitiveTypes.Int;
}
else if (type == typeof(char))
{
PrimitiveType = PrimitiveTypes.Char;
}
else
{
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() public override void UpdateValue()
{ {
base.UpdateValue(); base.UpdateValue();
@ -102,11 +48,10 @@ namespace Explorer
public override void DrawValue(Rect window, float width) public override void DrawValue(Rect window, float width)
{ {
if (PrimitiveType == PrimitiveTypes.Bool) if (m_isBool)
{ {
var b = (bool)Value; var b = (bool)Value;
var color = $"<color={(b ? "lime>" : "red>")}"; var label = $"<color={(b ? "lime" : "red")}>{b}</color>";
var label = $"{color}{b}</color>";
if (CanWrite) if (CanWrite)
{ {
@ -123,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;
@ -150,7 +96,7 @@ namespace Explorer
} }
} }
public void SetValueFromInput(string value) public void SetValueFromInput(string valueString)
{ {
if (MemInfo == null) if (MemInfo == null)
{ {
@ -158,16 +104,15 @@ namespace Explorer
return; return;
} }
if (PrimitiveType == PrimitiveTypes.String) if (m_isString)
{ {
Value = value; Value = valueString;
} }
else else
{ {
try try
{ {
var val = ParseMethod.Invoke(null, new object[] { value }); Value = ParseMethod.Invoke(null, new object[] { valueString });
Value = val;
} }
catch (Exception e) catch (Exception e)
{ {

View File

@ -9,8 +9,6 @@ namespace Explorer
{ {
public class CacheQuaternion : CacheObjectBase public class CacheQuaternion : CacheObjectBase
{ {
private Vector3 EulerAngle = Vector3.zero;
private string x = "0"; private string x = "0";
private string y = "0"; private string y = "0";
private string z = "0"; private string z = "0";
@ -19,11 +17,11 @@ namespace Explorer
{ {
base.UpdateValue(); base.UpdateValue();
EulerAngle = ((Quaternion)Value).eulerAngles; var euler = ((Quaternion)Value).eulerAngles;
x = EulerAngle.x.ToString(); x = euler.x.ToString();
y = EulerAngle.y.ToString(); y = euler.y.ToString();
z = EulerAngle.z.ToString(); z = euler.z.ToString();
} }
public override void DrawValue(Rect window, float width) public override void DrawValue(Rect window, float width)

View File

@ -5,7 +5,7 @@ using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using UnityEngine; using UnityEngine;
namespace Explorer.CachedObjects namespace Explorer
{ {
public class CacheRect : CacheObjectBase public class CacheRect : CacheObjectBase
{ {

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;
@ -11,15 +12,10 @@ namespace Explorer
{ {
public class CppExplorer : MelonMod public class CppExplorer : MelonMod
{ {
public const string GUID = "com.sinai.cppexplorer"; public const string NAME = "CppExplorer";
public const string VERSION = "1.5.6"; public const string VERSION = "1.6.3";
public const string AUTHOR = "Sinai"; public const string AUTHOR = "Sinai";
public const string GUID = "com.sinai.cppexplorer";
public const string NAME = "CppExplorer"
#if Release_Unity2018
+ " (Unity 2018)"
#endif
;
public static CppExplorer Instance { get; private set; } public static CppExplorer Instance { get; private set; }
@ -78,7 +74,7 @@ namespace Explorer
public override void OnUpdate() public override void OnUpdate()
{ {
// Check main toggle key input // Check main toggle key input
if (Input.GetKeyDown(KeyCode.F7)) if (InputHelper.GetKeyDown(KeyCode.F7))
{ {
ShowMenu = !ShowMenu; ShowMenu = !ShowMenu;
} }
@ -86,7 +82,7 @@ namespace Explorer
if (ShowMenu) if (ShowMenu)
{ {
// Check Force-Unlock input // Check Force-Unlock input
if (Input.GetKeyDown(KeyCode.LeftAlt)) if (InputHelper.GetKeyDown(KeyCode.LeftAlt))
{ {
ForceUnlockMouse = !ForceUnlockMouse; ForceUnlockMouse = !ForceUnlockMouse;
} }

View File

@ -2,7 +2,7 @@
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" /> <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup> <PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> <Configuration Condition=" '$(Configuration)' == '' ">Release</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{B21DBDE3-5D6F-4726-93AB-CC3CC68BAE7D}</ProjectGuid> <ProjectGuid>{B21DBDE3-5D6F-4726-93AB-CC3CC68BAE7D}</ProjectGuid>
<OutputType>Library</OutputType> <OutputType>Library</OutputType>
@ -11,32 +11,18 @@
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion> <TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment> <FileAlignment>512</FileAlignment>
<Deterministic>true</Deterministic> <Deterministic>true</Deterministic>
<TargetFrameworkProfile /> <TargetFrameworkProfile />
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<AssemblyName>CppExplorer</AssemblyName> <AssemblyName>CppExplorer</AssemblyName>
<DebugSymbols>false</DebugSymbols> <DebugSymbols>false</DebugSymbols>
<DebugType>none</DebugType> <DebugType>none</DebugType>
<Optimize>false</Optimize> <Optimize>false</Optimize>
<OutputPath>..\Release\2019\</OutputPath> <OutputPath>..\Release\</OutputPath>
<DefineConstants>Release_2019</DefineConstants> <DefineConstants />
<ErrorReport>prompt</ErrorReport> <ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel> <WarningLevel>4</WarningLevel>
<PlatformTarget>x64</PlatformTarget> <PlatformTarget>x64</PlatformTarget>
<Prefer32Bit>false</Prefer32Bit> <Prefer32Bit>false</Prefer32Bit>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release_Unity2018|AnyCPU' ">
<AssemblyName>CppExplorer_Unity2018</AssemblyName>
<DebugSymbols>false</DebugSymbols>
<DebugType>none</DebugType>
<Optimize>false</Optimize>
<OutputPath>..\Release\2018\</OutputPath>
<DefineConstants>Release_Unity2018</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<PlatformTarget>x64</PlatformTarget>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<ItemGroup> <ItemGroup>
<Reference Include="Il2Cppmscorlib"> <Reference Include="Il2Cppmscorlib">
<HintPath>..\..\..\..\..\Steam\steamapps\common\Hellpoint\MelonLoader\Managed\Il2Cppmscorlib.dll</HintPath> <HintPath>..\..\..\..\..\Steam\steamapps\common\Hellpoint\MelonLoader\Managed\Il2Cppmscorlib.dll</HintPath>
@ -60,66 +46,34 @@
<HintPath>..\..\..\..\..\Steam\steamapps\common\Hellpoint\MelonLoader\Managed\UnhollowerBaseLib.dll</HintPath> <HintPath>..\..\..\..\..\Steam\steamapps\common\Hellpoint\MelonLoader\Managed\UnhollowerBaseLib.dll</HintPath>
<Private>False</Private> <Private>False</Private>
</Reference> </Reference>
<!-- Unity 2019 build (InputLegacyModule.dll) --> <!-- Replace these references with ones from your game (..\MelonLoader\ folder) -->
<Reference Include="UnityEngine" Condition="'$(Configuration)'=='Debug'"> <Reference Include="UnityEngine">
<HintPath>..\..\..\Steam\steamapps\common\Hellpoint\MelonLoader\Managed\UnityEngine.dll</HintPath> <HintPath>..\..\..\Steam\steamapps\common\Hellpoint\MelonLoader\Managed\UnityEngine.dll</HintPath>
<Private>False</Private> <Private>False</Private>
</Reference> </Reference>
<Reference Include="UnityEngine.CoreModule" Condition="'$(Configuration)'=='Debug'"> <Reference Include="UnityEngine.CoreModule">
<HintPath>..\..\..\Steam\steamapps\common\Hellpoint\MelonLoader\Managed\UnityEngine.CoreModule.dll</HintPath> <HintPath>..\..\..\Steam\steamapps\common\Hellpoint\MelonLoader\Managed\UnityEngine.CoreModule.dll</HintPath>
<Private>False</Private> <Private>False</Private>
</Reference> </Reference>
<Reference Include="UnityEngine.IMGUIModule" Condition="'$(Configuration)'=='Debug'"> <Reference Include="UnityEngine.IMGUIModule">
<HintPath>..\..\..\Steam\steamapps\common\Hellpoint\MelonLoader\Managed\UnityEngine.IMGUIModule.dll</HintPath> <HintPath>..\..\..\Steam\steamapps\common\Hellpoint\MelonLoader\Managed\UnityEngine.IMGUIModule.dll</HintPath>
<Private>False</Private> <Private>False</Private>
</Reference> </Reference>
<Reference Include="UnityEngine.InputModule" Condition="'$(Configuration)'=='Debug'"> <Reference Include="UnityEngine.PhysicsModule">
<HintPath>..\..\..\Steam\steamapps\common\Hellpoint\MelonLoader\Managed\UnityEngine.InputLegacyModule.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="UnityEngine.PhysicsModule" Condition="'$(Configuration)'=='Debug'">
<HintPath>..\..\..\Steam\steamapps\common\Hellpoint\MelonLoader\Managed\UnityEngine.PhysicsModule.dll</HintPath> <HintPath>..\..\..\Steam\steamapps\common\Hellpoint\MelonLoader\Managed\UnityEngine.PhysicsModule.dll</HintPath>
<Private>False</Private> <Private>False</Private>
</Reference> </Reference>
<Reference Include="UnityEngine.TextRenderingModule" Condition="'$(Configuration)'=='Debug'"> <Reference Include="UnityEngine.TextRenderingModule">
<HintPath>..\..\..\Steam\steamapps\common\Hellpoint\MelonLoader\Managed\UnityEngine.TextRenderingModule.dll</HintPath> <HintPath>..\..\..\Steam\steamapps\common\Hellpoint\MelonLoader\Managed\UnityEngine.TextRenderingModule.dll</HintPath>
<Private>False</Private> <Private>False</Private>
</Reference> </Reference>
<Reference Include="UnityEngine.UI" Condition="'$(Configuration)'=='Debug'"> <Reference Include="UnityEngine.UI">
<HintPath>..\..\..\Steam\steamapps\common\Hellpoint\MelonLoader\Managed\UnityEngine.UI.dll</HintPath> <HintPath>..\..\..\Steam\steamapps\common\Hellpoint\MelonLoader\Managed\UnityEngine.UI.dll</HintPath>
<Private>False</Private> <Private>False</Private>
</Reference> </Reference>
<!-- Unity 2018 build (InputModule.dll) -->
<Reference Include="UnityEngine" Condition="'$(Configuration)'=='Release_Unity2018'">
<HintPath>..\..\..\Steam\steamapps\common\VRChat\MelonLoader\Managed\UnityEngine.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="UnityEngine.CoreModule" Condition="'$(Configuration)'=='Release_Unity2018'">
<HintPath>..\..\..\Steam\steamapps\common\VRChat\MelonLoader\Managed\UnityEngine.CoreModule.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="UnityEngine.IMGUIModule" Condition="'$(Configuration)'=='Release_Unity2018'">
<HintPath>..\..\..\Steam\steamapps\common\VRChat\MelonLoader\Managed\UnityEngine.IMGUIModule.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="UnityEngine.InputModule" Condition="'$(Configuration)'=='Release_Unity2018'">
<HintPath>..\..\..\Steam\steamapps\common\VRChat\MelonLoader\Managed\UnityEngine.InputModule.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="UnityEngine.PhysicsModule" Condition="'$(Configuration)'=='Release_Unity2018'">
<HintPath>..\..\..\Steam\steamapps\common\VRChat\MelonLoader\Managed\UnityEngine.PhysicsModule.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="UnityEngine.TextRenderingModule" Condition="'$(Configuration)'=='Release_Unity2018'">
<HintPath>..\..\..\Steam\steamapps\common\VRChat\MelonLoader\Managed\UnityEngine.TextRenderingModule.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="UnityEngine.UI" Condition="'$(Configuration)'=='Release_Unity2018'">
<HintPath>..\..\..\Steam\steamapps\common\VRChat\MelonLoader\Managed\UnityEngine.UI.dll</HintPath>
<Private>False</Private>
</Reference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="Helpers\IExpandHeight.cs" />
<Compile Include="CachedObjects\Struct\CacheColor.cs" /> <Compile Include="CachedObjects\Struct\CacheColor.cs" />
<Compile Include="CachedObjects\Object\CacheDictionary.cs" /> <Compile Include="CachedObjects\Object\CacheDictionary.cs" />
<Compile Include="CachedObjects\Struct\CacheEnum.cs" /> <Compile Include="CachedObjects\Struct\CacheEnum.cs" />
@ -133,6 +87,7 @@
<Compile Include="CachedObjects\Struct\CacheRect.cs" /> <Compile Include="CachedObjects\Struct\CacheRect.cs" />
<Compile Include="CppExplorer.cs" /> <Compile Include="CppExplorer.cs" />
<Compile Include="Extensions\ReflectionExtensions.cs" /> <Compile Include="Extensions\ReflectionExtensions.cs" />
<Compile Include="Helpers\InputHelper.cs" />
<Compile Include="UnstripFixes\GUIUnstrip.cs" /> <Compile Include="UnstripFixes\GUIUnstrip.cs" />
<Compile Include="UnstripFixes\ScrollViewStateUnstrip.cs" /> <Compile Include="UnstripFixes\ScrollViewStateUnstrip.cs" />
<Compile Include="Extensions\UnityExtensions.cs" /> <Compile Include="Extensions\UnityExtensions.cs" />

View File

@ -7,14 +7,11 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CppExplorer", "CppExplorer.
EndProject EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU Release|Any CPU = Release|Any CPU
Release_Unity2018|Any CPU = Release_Unity2018|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution GlobalSection(ProjectConfigurationPlatforms) = postSolution
{B21DBDE3-5D6F-4726-93AB-CC3CC68BAE7D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {B21DBDE3-5D6F-4726-93AB-CC3CC68BAE7D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B21DBDE3-5D6F-4726-93AB-CC3CC68BAE7D}.Debug|Any CPU.Build.0 = Debug|Any CPU {B21DBDE3-5D6F-4726-93AB-CC3CC68BAE7D}.Release|Any CPU.Build.0 = Release|Any CPU
{B21DBDE3-5D6F-4726-93AB-CC3CC68BAE7D}.Release_Unity2018|Any CPU.ActiveCfg = Release_Unity2018|Any CPU
{B21DBDE3-5D6F-4726-93AB-CC3CC68BAE7D}.Release_Unity2018|Any CPU.Build.0 = Release_Unity2018|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE

View File

@ -0,0 +1,16 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Explorer
{
interface IExpandHeight
{
bool IsExpanded { get; set; }
float WhiteSpace { get; set; }
float ButtonWidthOffset { get; set; }
}
}

View File

@ -0,0 +1,51 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Reflection;
using UnityEngine;
namespace Explorer
{
/// <summary>
/// Version-agnostic UnityEngine.Input module using Reflection
/// </summary>
public class InputHelper
{
private static readonly Type input = ReflectionHelpers.GetTypeByName("UnityEngine.Input");
private static readonly PropertyInfo mousePositionInfo = input.GetProperty("mousePosition");
private static readonly MethodInfo getKey = input.GetMethod("GetKey", new Type[] { typeof(KeyCode) });
private static readonly MethodInfo getKeyDown = input.GetMethod("GetKeyDown", new Type[] { typeof(KeyCode) });
private static readonly MethodInfo getMouseButtonDown = input.GetMethod("GetMouseButtonDown", new Type[] { typeof(int) });
private static readonly MethodInfo getMouseButton = input.GetMethod("GetMouseButton", new Type[] { typeof(int) });
#pragma warning disable IDE1006 // Camel-case property (Unity style)
public static Vector3 mousePosition => (Vector3)mousePositionInfo.GetValue(null);
#pragma warning restore IDE1006
public static bool GetKeyDown(KeyCode key)
{
return (bool)getKeyDown.Invoke(null, new object[] { key });
}
public static bool GetKey(KeyCode key)
{
return (bool)getKey.Invoke(null, new object[] { key });
}
/// <param name="btn">1 = left, 2 = middle, 3 = right, etc</param>
public static bool GetMouseButtonDown(int btn)
{
return (bool)getMouseButtonDown.Invoke(null, new object[] { btn });
}
/// <param name="btn">1 = left, 2 = middle, 3 = right, etc</param>
public static bool GetMouseButton(int btn)
{
return (bool)getMouseButton.Invoke(null, new object[] { btn });
}
}
}

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

@ -9,6 +9,8 @@ using UnhollowerRuntimeLib;
using UnityEngine; using UnityEngine;
using BF = System.Reflection.BindingFlags; using BF = System.Reflection.BindingFlags;
using MelonLoader; using MelonLoader;
using System.Collections;
using Mono.CSharp;
namespace Explorer namespace Explorer
{ {
@ -74,45 +76,54 @@ 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(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 && t.GetGenericTypeDefinition() is Type g)
{ {
var generic = t.GetGenericTypeDefinition(); return typeof(Il2CppSystem.Collections.Generic.List<>).IsAssignableFrom(g)
|| typeof(Il2CppSystem.Collections.Generic.IList<>).IsAssignableFrom(g);
return generic.IsAssignableFrom(typeof(Il2CppSystem.Collections.Generic.List<>))
|| generic.IsAssignableFrom(typeof(Il2CppSystem.Collections.Generic.IList<>));
} }
else else
{ {
return t.IsAssignableFrom(typeof(Il2CppSystem.Collections.IList)); return typeof(Il2CppSystem.Collections.IList).IsAssignableFrom(t);
} }
} }
public static bool IsDictionary(Type t) public static bool IsDictionary(Type t)
{ {
return t.IsGenericType if (typeof(IDictionary).IsAssignableFrom(t))
&& t.GetGenericTypeDefinition() is Type typeDef {
&& typeDef.IsAssignableFrom(typeof(Il2CppSystem.Collections.Generic.Dictionary<,>)); return true;
}
if (t.IsGenericType && t.GetGenericTypeDefinition() is Type g)
{
return typeof(Il2CppSystem.Collections.Generic.Dictionary<,>).IsAssignableFrom(g)
|| typeof(Il2CppSystem.Collections.Generic.IDictionary<,>).IsAssignableFrom(g);
}
else
{
return typeof(Il2CppSystem.Collections.IDictionary).IsAssignableFrom(t);
}
} }
public static Type GetTypeByName(string typeName) public static Type GetTypeByName(string fullName)
{ {
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 == fullName)
{ {
return type; return type;
} }
} }
catch { }
} }
return null; return null;
@ -137,6 +148,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

@ -56,7 +56,10 @@ namespace Explorer
{ {
_horizBarStyle = new GUIStyle(); _horizBarStyle = new GUIStyle();
_horizBarStyle.normal.background = Texture2D.whiteTexture; _horizBarStyle.normal.background = Texture2D.whiteTexture;
_horizBarStyle.margin = new RectOffset(0, 0, 4, 4); var rectOffset = new RectOffset();
rectOffset.top = 4;
rectOffset.bottom = 4;
_horizBarStyle.margin = rectOffset;
_horizBarStyle.fixedHeight = 2; _horizBarStyle.fixedHeight = 2;
} }
@ -73,7 +76,10 @@ namespace Explorer
{ {
_horizBarSmallStyle = new GUIStyle(); _horizBarSmallStyle = new GUIStyle();
_horizBarSmallStyle.normal.background = Texture2D.whiteTexture; _horizBarSmallStyle.normal.background = Texture2D.whiteTexture;
_horizBarSmallStyle.margin = new RectOffset(0, 0, 2, 2); var rectOffset = new RectOffset();
rectOffset.top = 2;
rectOffset.bottom = 2;
_horizBarSmallStyle.margin = rectOffset;
_horizBarSmallStyle.fixedHeight = 1; _horizBarSmallStyle.fixedHeight = 1;
} }

View File

@ -15,7 +15,7 @@ namespace Explorer
{ {
get get
{ {
if (m_mainCamera == null) if (!m_mainCamera)
{ {
m_mainCamera = Camera.main; m_mainCamera = Camera.main;
} }

View File

@ -17,7 +17,7 @@ namespace Explorer
{ {
if (CppExplorer.ShowMenu) if (CppExplorer.ShowMenu)
{ {
if (Input.GetKey(KeyCode.LeftShift) && Input.GetMouseButtonDown(1)) if (InputHelper.GetKey(KeyCode.LeftShift) && InputHelper.GetMouseButtonDown(1))
{ {
EnableInspect = !EnableInspect; EnableInspect = !EnableInspect;
} }
@ -35,7 +35,10 @@ namespace Explorer
public static void InspectRaycast() public static void InspectRaycast()
{ {
Ray ray = UnityHelpers.MainCamera.ScreenPointToRay(Input.mousePosition); if (!UnityHelpers.MainCamera)
return;
var ray = UnityHelpers.MainCamera.ScreenPointToRay(InputHelper.mousePosition);
if (Physics.Raycast(ray, out RaycastHit hit, 1000f)) if (Physics.Raycast(ray, out RaycastHit hit, 1000f))
{ {
@ -43,7 +46,7 @@ namespace Explorer
m_objUnderMouseName = obj.transform.GetGameObjectPath(); m_objUnderMouseName = obj.transform.GetGameObjectPath();
if (Input.GetMouseButtonDown(0)) if (InputHelper.GetMouseButtonDown(0))
{ {
EnableInspect = false; EnableInspect = false;
m_objUnderMouseName = ""; m_objUnderMouseName = "";
@ -63,7 +66,7 @@ namespace Explorer
{ {
if (m_objUnderMouseName != "") if (m_objUnderMouseName != "")
{ {
var pos = Input.mousePosition; var pos = InputHelper.mousePosition;
var rect = new Rect( var rect = new Rect(
pos.x - (Screen.width / 2), // x pos.x - (Screen.width / 2), // x
Screen.height - pos.y - 50, // y Screen.height - pos.y - 50, // y

View File

@ -14,11 +14,13 @@ namespace Explorer
{ {
public class ConsolePage : WindowPage public class ConsolePage : WindowPage
{ {
public override string Name { get => "C# Console"; set => base.Name = value; } public override string Name { get => "C# Console"; }
private ScriptEvaluator _evaluator; private ScriptEvaluator _evaluator;
private readonly StringBuilder _sb = new StringBuilder(); private readonly StringBuilder _sb = new StringBuilder();
private Vector2 inputAreaScroll;
private string MethodInput = ""; private string MethodInput = "";
private string UsingInput = ""; private string UsingInput = "";
@ -124,7 +126,12 @@ MelonLogger.Log(""hello world"");";
GUI.skin.label.alignment = TextAnchor.UpperLeft; GUI.skin.label.alignment = TextAnchor.UpperLeft;
GUILayout.Label("Enter code here as though it is a method body:", null); GUILayout.Label("Enter code here as though it is a method body:", null);
MethodInput = GUILayout.TextArea(MethodInput, new GUILayoutOption[] { GUILayout.Height(250) });
inputAreaScroll = GUIUnstrip.BeginScrollView(inputAreaScroll, new GUILayoutOption[] { GUILayout.Height(250) });
MethodInput = GUILayout.TextArea(MethodInput, new GUILayoutOption[] { GUILayout.ExpandHeight(true) });
GUIUnstrip.EndScrollView();
if (GUILayout.Button("<color=cyan><b>Execute</b></color>", null)) if (GUILayout.Button("<color=cyan><b>Execute</b></color>", null))
{ {

View File

@ -12,16 +12,14 @@ namespace Explorer
{ {
public static ScenePage Instance; public static ScenePage Instance;
public override string Name { get => "Scene Explorer"; set => base.Name = value; } public override string Name { get => "Scene Explorer"; }
public PageHelper Pages = new PageHelper(); public PageHelper Pages = new PageHelper();
private float m_timeOfLastUpdate = -1f; private float m_timeOfLastUpdate = -1f;
private static int PASSIVE_UPDATE_INTERVAL = 1; private const int PASSIVE_UPDATE_INTERVAL = 1;
private static bool m_getRootObjectsFailed = false; private static bool m_getRootObjectsFailed;
// ----- Holders for GUI elements ----- //
private static string m_currentScene = ""; private static string m_currentScene = "";
@ -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;
@ -54,7 +50,7 @@ namespace Explorer
if (m_searching) if (m_searching)
CancelSearch(); CancelSearch();
Update_Impl(); Update_Impl(true);
} }
public void TraverseUp() public void TraverseUp()
@ -90,11 +86,12 @@ namespace Explorer
{ {
var matches = new List<GameObjectCache>(); var matches = new List<GameObjectCache>();
foreach (var obj in Resources.FindObjectsOfTypeAll<GameObject>()) foreach (var obj in Resources.FindObjectsOfTypeAll(ReflectionHelpers.GameObjectType))
{ {
if (obj.name.ToLower().Contains(_search.ToLower()) && obj.scene.name == m_currentScene) var go = obj.TryCast<GameObject>();
if (go.name.ToLower().Contains(_search.ToLower()) && go.scene.name == m_currentScene)
{ {
matches.Add(new GameObjectCache(obj)); matches.Add(new GameObjectCache(go));
} }
} }
@ -111,9 +108,9 @@ namespace Explorer
Update_Impl(); Update_Impl();
} }
private void Update_Impl() private void Update_Impl(bool manual = false)
{ {
var allTransforms = new List<Transform>(); List<Transform> allTransforms = new List<Transform>();
// get current list of all transforms (either scene root or our current transform children) // get current list of all transforms (either scene root or our current transform children)
if (m_currentTransform) if (m_currentTransform)
@ -129,24 +126,27 @@ namespace Explorer
{ {
try try
{ {
var list = SceneManager.GetActiveScene().GetRootGameObjects().ToArray(); var scene = SceneManager.GetSceneByName(m_currentScene);
foreach (var obj in list) allTransforms.AddRange(scene.GetRootGameObjects()
{ .Select(it => it.transform));
allTransforms.Add(obj.transform);
}
} }
catch catch
{ {
m_getRootObjectsFailed = true; MelonLogger.Log("Exception getting root scene objects, falling back to backup method...");
PASSIVE_UPDATE_INTERVAL = 2;
allTransforms = GetRootObjectsManual_Impl(); m_getRootObjectsFailed = true;
allTransforms.AddRange(GetRootObjectsManual_Impl());
} }
} }
else else
{ {
allTransforms = GetRootObjectsManual_Impl(); if (!manual)
{
return;
}
allTransforms.AddRange(GetRootObjectsManual_Impl());
} }
} }
@ -166,13 +166,30 @@ namespace Explorer
} }
} }
private List<Transform> GetRootObjectsManual_Impl() private IEnumerable<Transform> GetRootObjectsManual_Impl()
{ {
var allTransforms = Resources.FindObjectsOfTypeAll<Transform>() try
.Where(x => x.parent == null && x.gameObject.scene.name == m_currentScene) {
.ToList(); var array = Resources.FindObjectsOfTypeAll(ReflectionHelpers.TransformType);
return allTransforms; var list = new List<Transform>();
foreach (var obj in array)
{
var transform = obj.TryCast<Transform>();
if (transform.parent == null && transform.gameObject.scene.name == m_currentScene)
{
list.Add(transform);
}
}
return list;
}
catch (Exception e)
{
MelonLogger.Log("Exception getting root scene objects (manual): "
+ e.GetType() + ", " + e.Message + "\r\n"
+ e.StackTrace);
return new Transform[0];
}
} }
// --------- GUI Draw Function --------- // // --------- GUI Draw Function --------- //
@ -274,7 +291,7 @@ namespace Explorer
{ {
Pages.TurnPage(Turn.Left, ref this.scroll); Pages.TurnPage(Turn.Left, ref this.scroll);
Update_Impl(); Update_Impl(true);
} }
Pages.CurrentPageLabel(); Pages.CurrentPageLabel();
@ -283,7 +300,7 @@ namespace Explorer
{ {
Pages.TurnPage(Turn.Right, ref this.scroll); Pages.TurnPage(Turn.Right, ref this.scroll);
Update_Impl(); Update_Impl(true);
} }
} }
@ -313,6 +330,14 @@ namespace Explorer
else else
{ {
GUILayout.Label("Scene Root GameObjects:", null); GUILayout.Label("Scene Root GameObjects:", null);
if (m_getRootObjectsFailed)
{
if (GUILayout.Button("Update Root Object List (auto-update failed!)", null))
{
Update_Impl(true);
}
}
} }
if (m_objectList.Count > 0) if (m_objectList.Count > 0)

View File

@ -13,7 +13,7 @@ namespace Explorer
{ {
public static SearchPage Instance; public static SearchPage Instance;
public override string Name { get => "Object Search"; set => base.Name = value; } public override string Name { get => "Object Search"; }
private string m_searchInput = ""; private string m_searchInput = "";
private string m_typeInput = ""; private string m_typeInput = "";
@ -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,12 +308,13 @@ 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;
} }
if (searchType == ReflectionHelpers.ComponentType && ReflectionHelpers.TransformType.IsAssignableFrom(obj.GetIl2CppType())) if (searchType.FullName == ReflectionHelpers.ComponentType.FullName
&& ReflectionHelpers.TransformType.IsAssignableFrom(obj.GetIl2CppType()))
{ {
// Transforms shouldn't really be counted as Components, skip them. // Transforms shouldn't really be counted as Components, skip them.
// They're more akin to GameObjects. // They're more akin to GameObjects.
@ -376,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

@ -9,7 +9,7 @@ namespace Explorer
{ {
public abstract class WindowPage public abstract class WindowPage
{ {
public virtual string Name { get; set; } public virtual string Name { get; }
public Vector2 scroll = Vector2.zero; public Vector2 scroll = Vector2.zero;

View File

@ -24,15 +24,24 @@ 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 static Rect GetLastRect() public static Rect GetLastRect()
{ {
@ -49,24 +58,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)
@ -81,7 +72,6 @@ namespace Explorer
catch catch
{ {
ScrollFailed = true; ScrollFailed = true;
return scroll;
} }
} }
@ -94,10 +84,8 @@ namespace Explorer
} }
catch (Exception e) catch (Exception e)
{ {
MelonLogger.Log("Exception on GUIUnstrip.BeginScrollView_ImplLayout: " + e.GetType() + ", " + e.Message + "\r\n" + e.StackTrace); MelonLogger.Log("Exception on manual BeginScrollView: " + e.GetType() + ", " + e.Message + "\r\n" + e.StackTrace);
ManualUnstripFailed = true; ManualUnstripFailed = true;
return scroll;
} }
} }
@ -121,8 +109,6 @@ namespace Explorer
} }
} }
// ======= private methods ======= //
private static Vector2 BeginScrollView_ImplLayout(Vector2 scrollPosition, bool alwaysShowHorizontal, bool alwaysShowVertical, private static Vector2 BeginScrollView_ImplLayout(Vector2 scrollPosition, bool alwaysShowHorizontal, bool alwaysShowVertical,
GUIStyle horizontalScrollbar, GUIStyle verticalScrollbar, GUIStyle background, params GUILayoutOption[] options) GUIStyle horizontalScrollbar, GUIStyle verticalScrollbar, GUIStyle background, params GUILayoutOption[] options)
{ {
@ -221,7 +207,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 +229,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 +265,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 +341,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 +367,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)
{ {

View File

@ -48,12 +48,6 @@ namespace Explorer
public bool GetObjectAsGameObject() public bool GetObjectAsGameObject()
{ {
if (Target == null)
{
MelonLogger.Log("Target is null!");
return false;
}
var targetType = Target.GetType(); var targetType = Target.GetType();
if (targetType == typeof(GameObject)) if (targetType == typeof(GameObject))
@ -108,6 +102,22 @@ namespace Explorer
{ {
try try
{ {
if (Target == null)
{
MelonLogger.Log("Target is null!");
DestroyWindow();
return;
}
else if (Target is UnityEngine.Object uObj)
{
if (!uObj)
{
MelonLogger.Log("Target was destroyed!");
DestroyWindow();
return;
}
}
if (!m_object && !GetObjectAsGameObject()) if (!m_object && !GetObjectAsGameObject())
{ {
throw new Exception("Object is null!"); throw new Exception("Object is null!");
@ -128,28 +138,22 @@ namespace Explorer
m_object.transform.localScale = m_frozenScale; m_object.transform.localScale = m_frozenScale;
} }
var list = new List<Transform>(); // update child objects
var childList = new List<Transform>();
for (int i = 0; i < m_object.transform.childCount; i++) for (int i = 0; i < m_object.transform.childCount; i++)
{ {
list.Add(m_object.transform.GetChild(i)); childList.Add(m_object.transform.GetChild(i));
} }
list.Sort((a, b) => b.childCount.CompareTo(a.childCount)); childList.Sort((a, b) => b.childCount.CompareTo(a.childCount));
m_children = list.ToArray(); m_children = childList.ToArray();
ChildPages.ItemCount = m_children.Length; ChildPages.ItemCount = m_children.Length;
var list2 = new List<Component>(); // update components
foreach (var comp in m_object.GetComponents(ReflectionHelpers.ComponentType)) var compList = new Il2CppSystem.Collections.Generic.List<Component>();
{ m_object.GetComponentsInternal(ReflectionHelpers.ComponentType, true, false, true, false, compList);
var ilType = comp.GetIl2CppType();
if (ilType == ReflectionHelpers.TransformType)
{
continue;
}
list2.Add(comp); m_components = compList.ToArray();
}
m_components = list2.ToArray();
CompPages.ItemCount = m_components.Length; CompPages.ItemCount = m_components.Length;
} }

View File

@ -22,8 +22,6 @@ namespace Explorer
private CacheObjectBase[] m_cachedMembersFiltered; private CacheObjectBase[] m_cachedMembersFiltered;
public PageHelper Pages = new PageHelper(); public PageHelper Pages = new PageHelper();
//private int m_pageOffset;
//private int m_limitPerPage = 20;
private bool m_autoUpdate = false; private bool m_autoUpdate = false;
private string m_search = ""; private string m_search = "";
@ -69,6 +67,20 @@ namespace Explorer
public override void Update() public override void Update()
{ {
if (Target == null)
{
DestroyWindow();
return;
}
else if (Target is UnityEngine.Object uObj)
{
if (!uObj)
{
DestroyWindow();
return;
}
}
m_cachedMembersFiltered = m_allCachedMembers.Where(x => ShouldProcessMember(x)).ToArray(); m_cachedMembersFiltered = m_allCachedMembers.Where(x => ShouldProcessMember(x)).ToArray();
if (m_autoUpdate) if (m_autoUpdate)

View File

@ -34,15 +34,17 @@ namespace Explorer
//var r = GUILayoutUtility.GetLastRect(); //var r = GUILayoutUtility.GetLastRect();
var r = GUIUnstrip.GetLastRect(); var r = GUIUnstrip.GetLastRect();
Vector2 mouse = GUIUtility.ScreenToGUIPoint(new Vector2(Input.mousePosition.x, Screen.height - Input.mousePosition.y)); var mousePos = InputHelper.mousePosition;
if (r.Contains(mouse) && Input.GetMouseButtonDown(0)) Vector2 mouse = GUIUtility.ScreenToGUIPoint(new Vector2(mousePos.x, Screen.height - mousePos.y));
if (r.Contains(mouse) && InputHelper.GetMouseButtonDown(0))
{ {
isResizing = true; isResizing = true;
m_currentWindow = ID; m_currentWindow = ID;
m_currentResize = new Rect(mouse.x, mouse.y, _rect.width, _rect.height); m_currentResize = new Rect(mouse.x, mouse.y, _rect.width, _rect.height);
} }
else if (!Input.GetMouseButton(0)) else if (!InputHelper.GetMouseButton(0))
{ {
isResizing = false; isResizing = false;
} }

View File

@ -26,6 +26,7 @@ namespace Explorer
} }
public override void Init() { } public override void Init() { }
public override void Update() public override void Update()
{ {
while (TargetTabID >= WindowManager.Windows.Count) while (TargetTabID >= WindowManager.Windows.Count)

View File

@ -90,7 +90,7 @@ namespace Explorer
{ {
createdNew = false; createdNew = false;
if (Input.GetKey(KeyCode.LeftShift)) if (InputHelper.GetKey(KeyCode.LeftShift))
{ {
forceReflection = true; forceReflection = true;
} }
@ -109,7 +109,7 @@ namespace Explorer
if (!equals && iObj is Il2CppSystem.Object iCurrent && window.Target is Il2CppSystem.Object iTarget) if (!equals && iObj is Il2CppSystem.Object iCurrent && window.Target is Il2CppSystem.Object iTarget)
{ {
if (iCurrent.GetIl2CppType() != iTarget.GetIl2CppType()) if (iCurrent.GetIl2CppType().FullName != iTarget.GetIl2CppType().FullName)
{ {
if (iCurrent is Transform transform) if (iCurrent is Transform transform)
{ {
@ -192,7 +192,8 @@ namespace Explorer
private static bool RectContainsMouse(Rect rect) private static bool RectContainsMouse(Rect rect)
{ {
return rect.Contains(new Vector2(Input.mousePosition.x, Screen.height - Input.mousePosition.y)); var mousePos = InputHelper.mousePosition;
return rect.Contains(new Vector2(mousePos.x, Screen.height - mousePos.y));
} }
public static int NextWindowID() public static int NextWindowID()