diff --git a/src/CachedObjects/CacheEnum.cs b/src/CachedObjects/CacheEnum.cs new file mode 100644 index 0000000..009d0c2 --- /dev/null +++ b/src/CachedObjects/CacheEnum.cs @@ -0,0 +1,69 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using MelonLoader; +using UnityEngine; + +namespace Explorer +{ + public class CacheEnum : CacheObject + { + private readonly Type m_enumType; + private readonly string[] m_names; + + public CacheEnum(object obj) + { + m_enumType = obj.GetType(); + m_names = Enum.GetNames(obj.GetType()); + } + + public override void DrawValue(Rect window, float width) + { + if (MemberInfo != null) + { + if (GUILayout.Button("<", new GUILayoutOption[] { GUILayout.Width(25) })) + { + SetEnum(ref Value, -1); + SetValue(); + } + if (GUILayout.Button(">", new GUILayoutOption[] { GUILayout.Width(25) })) + { + SetEnum(ref Value, 1); + SetValue(); + } + } + + GUILayout.Label(Value.ToString(), null); + } + + public override void SetValue() + { + if (MemberInfo == null) + { + MelonLogger.Log("Trying to SetValue but the MemberInfo is null!"); + return; + } + + if (Enum.Parse(m_enumType, Value.ToString()) is object enumValue && enumValue != null) + { + Value = enumValue; + } + + SetValue(Value, MemberInfo, DeclaringInstance); + } + + public void SetEnum(ref object value, int change) + { + var names = m_names.ToList(); + + int newindex = names.IndexOf(value.ToString()) + change; + + if ((change < 0 && newindex >= 0) || (change > 0 && newindex < names.Count)) + { + value = Enum.Parse(m_enumType, names[newindex]); + } + } + } +} diff --git a/src/CachedObjects/CacheGameObject.cs b/src/CachedObjects/CacheGameObject.cs new file mode 100644 index 0000000..1fded23 --- /dev/null +++ b/src/CachedObjects/CacheGameObject.cs @@ -0,0 +1,53 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using MelonLoader; +using UnityEngine; + +namespace Explorer +{ + public class CacheGameObject : CacheObject + { + private GameObject m_gameObject; + + public CacheGameObject(object obj) + { + if (obj != null) + m_gameObject = GetGameObject(obj); + } + + private GameObject GetGameObject(object obj) + { + if (obj is Il2CppSystem.Object ilObj) + { + var ilType = ilObj.GetIl2CppType(); + + if (ilType == ReflectionHelpers.GameObjectType || ilType == ReflectionHelpers.TransformType) + { + return ilObj.TryCast() ?? ilObj.TryCast()?.gameObject; + } + } + + return null; + } + + public override void DrawValue(Rect window, float width) + { + UIHelpers.GameobjButton(m_gameObject, null, false, width); + } + + public override void SetValue() + { + throw new NotImplementedException("TODO"); + } + + public override void UpdateValue(object obj) + { + base.UpdateValue(obj); + + m_gameObject = GetGameObject(Value); + } + } +} diff --git a/src/CachedObjects/CacheIl2CppObject.cs b/src/CachedObjects/CacheIl2CppObject.cs new file mode 100644 index 0000000..87b9e3e --- /dev/null +++ b/src/CachedObjects/CacheIl2CppObject.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UnityEngine; + +namespace Explorer +{ + public class CacheIl2CppObject : CacheObject + { + public override void DrawValue(Rect window, float width) + { + var label = ValueType ?? Value.ToString(); + if (!label.Contains(ValueType)) + { + label += $" ({ValueType})"; + } + if (Value is UnityEngine.Object unityObj) + { + label = unityObj.name + " | " + label; + } + + GUI.skin.button.alignment = TextAnchor.MiddleLeft; + if (GUILayout.Button("" + label + "", new GUILayoutOption[] { GUILayout.MaxWidth(width) })) + { + WindowManager.InspectObject(Value, out bool _); + } + GUI.skin.button.alignment = TextAnchor.MiddleCenter; + } + + public override void SetValue() + { + throw new NotImplementedException("TODO"); + } + } +} diff --git a/src/CachedObjects/CacheList.cs b/src/CachedObjects/CacheList.cs new file mode 100644 index 0000000..1af6eba --- /dev/null +++ b/src/CachedObjects/CacheList.cs @@ -0,0 +1,153 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Threading.Tasks; +using UnityEngine; + +namespace Explorer +{ + public partial class CacheList : CacheObject + { + public bool IsExpanded { get; set; } + public int ArrayOffset { get; set; } + public Type EntryType { get; set; } + + private IEnumerable m_enumerable; + private CacheObject[] m_cachedEntries; + + public CacheList(object obj) + { + GetEnumerable(obj); + EntryType = m_enumerable.GetType().GetGenericArguments()[0]; + } + + private void GetEnumerable(object obj) + { + if (obj is IEnumerable isEnumerable) + { + m_enumerable = isEnumerable; + } + else + { + var listValueType = obj.GetType().GetGenericArguments()[0]; + var listType = typeof(Il2CppSystem.Collections.Generic.List<>).MakeGenericType(new Type[] { listValueType }); + var method = listType.GetMethod("ToArray"); + m_enumerable = (IEnumerable)method.Invoke(obj, new object[0]); + } + } + + public override void DrawValue(Rect window, float width) + { + int count = m_cachedEntries.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 = "[" + count + "] " + EntryType + ""; + if (GUILayout.Button(btnLabel, new GUILayoutOption[] { GUILayout.MaxWidth(window.width - 260) })) + { + WindowManager.InspectObject(Value, out bool _); + } + GUI.skin.button.alignment = TextAnchor.MiddleCenter; + + if (IsExpanded) + { + if (count > CppExplorer.ArrayLimit) + { + GUILayout.EndHorizontal(); + GUILayout.BeginHorizontal(null); + GUILayout.Space(190); + int maxOffset = (int)Mathf.Ceil(count / CppExplorer.ArrayLimit); + GUILayout.Label($"Page {ArrayOffset + 1}/{maxOffset + 1}", new GUILayoutOption[] { GUILayout.Width(80) }); + // prev/next page buttons + if (GUILayout.Button("< Prev", null)) + { + if (ArrayOffset > 0) ArrayOffset--; + } + if (GUILayout.Button("Next >", null)) + { + if (ArrayOffset < maxOffset) ArrayOffset++; + } + } + + int offset = ArrayOffset * CppExplorer.ArrayLimit; + + if (offset >= count) offset = 0; + + for (int i = offset; i < offset + CppExplorer.ArrayLimit && i < count; i++) + { + var entry = m_cachedEntries[i]; + + //collapsing the BeginHorizontal called from ReflectionWindow.WindowFunction or previous array entry + GUILayout.EndHorizontal(); + GUILayout.BeginHorizontal(null); + GUILayout.Space(190); + + if (entry == null) + { + GUILayout.Label("null", null); + } + else + { + GUILayout.Label(i.ToString(), new GUILayoutOption[] { GUILayout.Width(30) }); + + entry.DrawValue(window, window.width - 250); + + //var lbl = i + ": " + obj.Value.ToString() + ""; + + //if (EntryType.IsPrimitive || typeof(string).IsAssignableFrom(EntryType)) + //{ + // GUILayout.Label(lbl, null); + //} + //else + //{ + // GUI.skin.button.alignment = TextAnchor.MiddleLeft; + // if (GUILayout.Button(lbl, null)) + // { + // WindowManager.InspectObject(obj, out _); + // } + // GUI.skin.button.alignment = TextAnchor.MiddleCenter; + //} + } + } + } + } + + public override void SetValue() + { + throw new NotImplementedException("TODO"); + } + + public override void UpdateValue(object obj) + { + GetEnumerable(Value); + + var list = new List(); + + var enumerator = m_enumerable.GetEnumerator(); + + while (enumerator.MoveNext()) + { + list.Add(GetCacheObject(enumerator.Current)); + } + + m_cachedEntries = list.ToArray(); + } + } +} diff --git a/src/CachedObjects/CacheObject.cs b/src/CachedObjects/CacheObject.cs new file mode 100644 index 0000000..57b75c3 --- /dev/null +++ b/src/CachedObjects/CacheObject.cs @@ -0,0 +1,194 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Threading.Tasks; +using MelonLoader; +using UnhollowerBaseLib; +using UnityEngine; + +namespace Explorer +{ + public abstract class CacheObject + { + public object Value; + public string ValueType; + + // Reflection window only + public MemberInfo MemberInfo { get; set; } + public ReflectionWindow.MemberInfoType MemberInfoType { get; set; } + public Type DeclaringType { get; set; } + public object DeclaringInstance { get; set; } + + public abstract void DrawValue(Rect window, float width); + public abstract void SetValue(); + + public static CacheObject GetCacheObject(object obj) + { + return GetCacheObject(obj, null, null); + } + + public static CacheObject GetCacheObject(object obj, MemberInfo memberInfo, object declaringInstance) + { + CacheObject holder; + + var type = ReflectionHelpers.GetActualType(obj) ?? (memberInfo as FieldInfo)?.FieldType ?? (memberInfo as PropertyInfo)?.PropertyType; + + if (obj is Il2CppSystem.Object || typeof(Il2CppSystem.Object).IsAssignableFrom(type)) + { + var name = type.FullName; + if (name == "UnityEngine.GameObject" || name == "UnityEngine.Transform") + { + holder = new CacheGameObject(obj); + } + else + { + holder = new CacheIl2CppObject(); + } + } + else + { + if (type.IsPrimitive || type == typeof(string)) + { + holder = new CachePrimitive(obj); + } + else if (type.IsEnum) + { + holder = new CacheEnum(obj); + } + else if (typeof(System.Collections.IEnumerable).IsAssignableFrom(type) || ReflectionHelpers.IsList(type)) + { + holder = new CacheList(obj); + } + else if (type.IsValueType) + { + holder = new CacheStruct(obj); + } + else + { + holder = new CacheOther(); + } + } + + if (holder == null) + { + return null; + } + + if (memberInfo != null) + { + holder.MemberInfo = memberInfo; + holder.DeclaringType = memberInfo.DeclaringType; + + if (declaringInstance is Il2CppSystem.Object ilInstance && ilInstance.GetType() != memberInfo.DeclaringType) + { + try + { + holder.DeclaringInstance = ilInstance.Il2CppCast(holder.DeclaringType); + } + catch + { + holder.DeclaringInstance = declaringInstance; + } + } + else + { + holder.DeclaringInstance = declaringInstance; + } + + if (memberInfo.MemberType == MemberTypes.Field) + { + holder.MemberInfoType = ReflectionWindow.MemberInfoType.Field; + } + else if (memberInfo.MemberType == MemberTypes.Property) + { + holder.MemberInfoType = ReflectionWindow.MemberInfoType.Property; + } + else if (memberInfo.MemberType == MemberTypes.Method) + { + holder.MemberInfoType = ReflectionWindow.MemberInfoType.Method; + } + } + + holder.Value = obj; + holder.ValueType = type.FullName; + + return holder; + } + + public void Draw(Rect window, float labelWidth = 180f) + { + if (MemberInfo != null) + { + GUILayout.Label("" + MemberInfo.Name + ":", new GUILayoutOption[] { GUILayout.Width(labelWidth) }); + } + else + { + GUILayout.Space(labelWidth); + } + + if (Value == null) + { + GUILayout.Label("null (" + this.ValueType + ")", null); + } + else + { + DrawValue(window, window.width - labelWidth - 90); + } + } + + public virtual void UpdateValue(object obj) + { + if (MemberInfo == null) + { + return; + } + + try + { + if (MemberInfo.MemberType == MemberTypes.Field) + { + var fi = MemberInfo as FieldInfo; + Value = fi.GetValue(fi.IsStatic ? null : DeclaringInstance); + } + else if (MemberInfo.MemberType == MemberTypes.Property) + { + var pi = MemberInfo as PropertyInfo; + Value = pi.GetValue(pi.GetAccessors()[0].IsStatic ? null : DeclaringInstance, null); + } + } + catch //(Exception e) + { + //MelonLogger.Log($"Error updating MemberInfo value | {e.GetType()}: {e.Message}\r\n{e.StackTrace}"); + } + } + + public void SetValue(object value, MemberInfo memberInfo, object declaringInstance) + { + try + { + if (memberInfo.MemberType == MemberTypes.Field) + { + var fi = memberInfo as FieldInfo; + if (!(fi.IsLiteral && !fi.IsInitOnly)) + { + fi.SetValue(fi.IsStatic ? null : declaringInstance, value); + } + } + else if (memberInfo.MemberType == MemberTypes.Property) + { + var pi = memberInfo as PropertyInfo; + if (pi.CanWrite) + { + pi.SetValue(pi.GetAccessors()[0].IsStatic ? null : declaringInstance, value); + } + } + } + catch (Exception e) + { + MelonLogger.LogWarning($"Error setting value: {e.GetType()}, {e.Message}"); + } + } + } +} diff --git a/src/CachedObjects/CacheOther.cs b/src/CachedObjects/CacheOther.cs new file mode 100644 index 0000000..41e98b9 --- /dev/null +++ b/src/CachedObjects/CacheOther.cs @@ -0,0 +1,49 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UnityEngine; + +namespace Explorer +{ + public class CacheOther : CacheObject + { + public override void DrawValue(Rect window, float width) + { + string label; + if (Value is UnityEngine.Object uObj) + { + label = uObj.name; + } + else + { + label = Value.ToString(); + } + + string typeLabel = Value.GetType().FullName; + + if (!label.Contains(typeLabel)) + { + label += $" ({typeLabel})"; + } + + GUI.skin.button.alignment = TextAnchor.MiddleLeft; + if (GUILayout.Button("" + label + "", new GUILayoutOption[] { GUILayout.MaxWidth(window.width - 230) })) + { + WindowManager.InspectObject(Value, out bool _); + } + GUI.skin.button.alignment = TextAnchor.MiddleCenter; + } + + public override void SetValue() + { + + } + + public override void UpdateValue(object obj) + { + + } + } +} diff --git a/src/CachedObjects/CachePrimitive.cs b/src/CachedObjects/CachePrimitive.cs new file mode 100644 index 0000000..46122c1 --- /dev/null +++ b/src/CachedObjects/CachePrimitive.cs @@ -0,0 +1,105 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using MelonLoader; +using UnityEngine; + +namespace Explorer +{ + public class CachePrimitive : CacheObject + { + public enum PrimitiveType + { + Bool, + Double, + Float, + Int, + String + } + + private readonly PrimitiveType m_primitiveType; + + public CachePrimitive(object obj) + { + if (obj is bool) + { + m_primitiveType = PrimitiveType.Bool; + } + else if (obj is double) + { + m_primitiveType = PrimitiveType.Double; + } + else if (obj is float) + { + m_primitiveType = PrimitiveType.Float; + } + else if (obj is int) + { + m_primitiveType = PrimitiveType.Int; + } + else if (obj is string) + { + m_primitiveType = PrimitiveType.String; + } + } + + public override void DrawValue(Rect window, float width) + { + if (m_primitiveType == PrimitiveType.Bool && Value is bool b) + { + var color = "" : "red>"); + Value = GUILayout.Toggle((bool)Value, color + Value.ToString() + "", null); + + if (b != (bool)Value) + { + SetValue(); + } + } + else + { + var toString = Value.ToString(); + if (toString.Length > 37) + { + Value = GUILayout.TextArea(toString, new GUILayoutOption[] { GUILayout.MaxWidth(window.width - 260) }); + } + else + { + Value = GUILayout.TextField(toString, new GUILayoutOption[] { GUILayout.MaxWidth(window.width - 260) }); + } + + if (MemberInfo != null) + { + if (GUILayout.Button("Apply", new GUILayoutOption[] { GUILayout.Width(60) })) + { + SetValue(); + } + } + } + } + + public override void SetValue() + { + if (MemberInfo == null) + { + MelonLogger.Log("Trying to SetValue but the MemberInfo is null!"); + return; + } + + switch (m_primitiveType) + { + case PrimitiveType.Bool: + SetValue(bool.Parse(Value.ToString()), MemberInfo, DeclaringInstance); return; + case PrimitiveType.Double: + SetValue(double.Parse(Value.ToString()), MemberInfo, DeclaringInstance); return; + case PrimitiveType.Float: + SetValue(float.Parse(Value.ToString()), MemberInfo, DeclaringInstance); return; + case PrimitiveType.Int: + SetValue(int.Parse(Value.ToString()), MemberInfo, DeclaringInstance); return; + case PrimitiveType.String: + SetValue(Value.ToString(), MemberInfo, DeclaringInstance); return; + } + } + } +} diff --git a/src/CachedObjects/CacheStruct.cs b/src/CachedObjects/CacheStruct.cs new file mode 100644 index 0000000..945cb5f --- /dev/null +++ b/src/CachedObjects/CacheStruct.cs @@ -0,0 +1,71 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Reflection; +using UnityEngine; + +namespace Explorer +{ + public class CacheStruct : CacheObject + { + public MethodInfo ToStringMethod { get; private set; } + private static readonly MethodInfo m_defaultToString = typeof(object).GetMethod("ToString"); + + public CacheStruct(object obj) + { + try + { + var methods = obj.GetType().GetMethods(ReflectionHelpers.CommonFlags).Where(x => x.Name == "ToString"); + var enumerator = methods.GetEnumerator(); + while (enumerator.MoveNext()) + { + ToStringMethod = enumerator.Current; + break; + } + } + catch + { + ToStringMethod = m_defaultToString; + } + } + + public override void DrawValue(Rect window, float width) + { + string label; + try + { + label = (string)ToStringMethod.Invoke(Value, null); + } + catch + { + label = Value.ToString(); + } + string typeLabel = Value.GetType().FullName; + + if (!label.Contains(typeLabel)) + { + label += $" ({typeLabel})"; + } + + GUI.skin.button.alignment = TextAnchor.MiddleLeft; + if (GUILayout.Button("" + label + "", new GUILayoutOption[] { GUILayout.MaxWidth(window.width - 230) })) + { + WindowManager.InspectObject(Value, out bool _); + } + GUI.skin.button.alignment = TextAnchor.MiddleCenter; + } + + public override void SetValue() + { + throw new NotImplementedException("TODO"); + } + + //public override void UpdateValue(object obj) + //{ + + //} + } +} diff --git a/src/CppExplorer.cs b/src/CppExplorer.cs index 649b810..ee0d90a 100644 --- a/src/CppExplorer.cs +++ b/src/CppExplorer.cs @@ -12,10 +12,10 @@ namespace Explorer // consts public const string ID = "com.sinai.cppexplorer"; - public const string VERSION = "1.3.5"; + public const string VERSION = "1.4.0"; public const string AUTHOR = "Sinai"; - public const string NAME = "IL2CPP Runtime Explorer" + public const string NAME = "CppExplorer" #if Release_Unity2018 + " (Unity 2018)" #endif diff --git a/src/CppExplorer.csproj b/src/CppExplorer.csproj index 54284cd..9f352ba 100644 --- a/src/CppExplorer.csproj +++ b/src/CppExplorer.csproj @@ -136,6 +136,13 @@ + + + + + + + @@ -143,9 +150,7 @@ - - - + diff --git a/src/Helpers/ReflectionHelpers.cs b/src/Helpers/ReflectionHelpers.cs index dc81c70..65f9284 100644 --- a/src/Helpers/ReflectionHelpers.cs +++ b/src/Helpers/ReflectionHelpers.cs @@ -56,6 +56,8 @@ namespace Explorer public static Type GetActualType(object m_object) { + if (m_object == null) return null; + if (m_object is Il2CppSystem.Object ilObject) { var iltype = ilObject.GetIl2CppType(); diff --git a/src/Helpers/UIHelpers.cs b/src/Helpers/UIHelpers.cs index 75438ac..d72d479 100644 --- a/src/Helpers/UIHelpers.cs +++ b/src/Helpers/UIHelpers.cs @@ -5,7 +5,6 @@ using System.Text; using System.Threading.Tasks; using UnityEngine; using Object = UnityEngine.Object; -using System.Reflection; namespace Explorer { @@ -103,311 +102,5 @@ namespace Explorer GUILayout.EndHorizontal(); } - - public static void DrawMember(ref object value, ref bool isExpanded, ref int arrayOffset, MemberInfo memberInfo, Rect rect, object setTarget = null, Action setAction = null, float labelWidth = 180, bool autoSet = false) - { - GUILayout.Label("" + memberInfo.Name + ":", new GUILayoutOption[] { GUILayout.Width(labelWidth) }); - - string valueType = ""; - bool canWrite = true; - if (memberInfo is FieldInfo fi) - { - valueType = fi.FieldType.Name; - canWrite = !(fi.IsLiteral && !fi.IsInitOnly); - } - else if (memberInfo is PropertyInfo pi) - { - valueType = pi.PropertyType.Name; - canWrite = pi.CanWrite; - } - - DrawValue(ref value, ref isExpanded, ref arrayOffset, rect, valueType, (canWrite ? setTarget : null), (canWrite ? setAction : null), autoSet); - } - - public static void DrawValue(ref object value, ref bool isExpanded, ref int arrayOffset, Rect rect, string nullValueType = null, object setTarget = null, Action setAction = null, bool autoSet = false) - { - if (value == null) - { - GUILayout.Label("null (" + nullValueType + ")", null); - return; - } - - var valueType = value.GetType(); - - Il2CppSystem.Type ilType = null; - if (value is Il2CppSystem.Object ilObject) - { - ilType = ilObject.GetIl2CppType(); - } - - if (valueType.IsPrimitive || value.GetType() == typeof(string)) - { - DrawPrimitive(ref value, rect, setTarget, setAction); - } - else if (ilType != null && ilType == ReflectionHelpers.GameObjectType || ReflectionHelpers.TransformType.IsAssignableFrom(ilType)) - { - GameObject go; - var ilObj = value as Il2CppSystem.Object; - if (ilType == ReflectionHelpers.GameObjectType) - { - go = ilObj.TryCast(); - } - else - { - go = ilObj.TryCast().gameObject; - } - - GameobjButton(go, null, false, rect.width - 250); - } - else if (valueType.IsEnum) - { - if (setAction != null) - { - if (GUILayout.Button("<", new GUILayoutOption[] { GUILayout.Width(25) })) - { - SetEnum(ref value, -1); - setAction.Invoke(setTarget); - } - if (GUILayout.Button(">", new GUILayoutOption[] { GUILayout.Width(25) })) - { - SetEnum(ref value, 1); - setAction.Invoke(setTarget); - } - } - - GUILayout.Label(value.ToString(), null); - } - else if (value is System.Collections.IEnumerable || ReflectionHelpers.IsList(valueType)) - { - System.Collections.IEnumerable enumerable; - - if (value is System.Collections.IEnumerable isEnumerable) - { - enumerable = isEnumerable; - } - else - { - var listValueType = value.GetType().GetGenericArguments()[0]; - var listType = typeof(Il2CppSystem.Collections.Generic.List<>).MakeGenericType(new Type[] { listValueType }); - var method = listType.GetMethod("ToArray"); - enumerable = (System.Collections.IEnumerable)method.Invoke(value, new object[0]); - } - - int count = enumerable.Cast().Count(); - - 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 = "[" + count + "] " + valueType + ""; - if (GUILayout.Button(btnLabel, new GUILayoutOption[] { GUILayout.MaxWidth(rect.width - 260) })) - { - WindowManager.InspectObject(value, out bool _); - } - GUI.skin.button.alignment = TextAnchor.MiddleCenter; - - if (isExpanded) - { - if (count > CppExplorer.ArrayLimit) - { - GUILayout.EndHorizontal(); - GUILayout.BeginHorizontal(null); - GUILayout.Space(190); - int maxOffset = (int)Mathf.Ceil(count / CppExplorer.ArrayLimit); - GUILayout.Label($"Page {arrayOffset + 1}/{maxOffset + 1}", new GUILayoutOption[] { GUILayout.Width(80) }); - // prev/next page buttons - if (GUILayout.Button("< Prev", null)) - { - if (arrayOffset > 0) arrayOffset--; - } - if (GUILayout.Button("Next >", null)) - { - if (arrayOffset < maxOffset) arrayOffset++; - } - } - - int offset = arrayOffset * CppExplorer.ArrayLimit; - - if (offset >= count) offset = 0; - - var enumerator = enumerable.GetEnumerator(); - if (enumerator != null) - { - int i = 0; - int preiterated = 0; - while (enumerator.MoveNext()) - { - if (offset > 0 && preiterated < offset) - { - preiterated++; - continue; - } - - var obj = enumerator.Current; - - //collapsing the BeginHorizontal called from ReflectionWindow.WindowFunction or previous array entry - GUILayout.EndHorizontal(); - GUILayout.BeginHorizontal(null); - GUILayout.Space(190); - - if (i > CppExplorer.ArrayLimit - 1) - { - //GUILayout.Label($"{count - CppExplorer.ArrayLimit} results omitted, array is too long!", null); - break; - } - - if (obj == null) - { - GUILayout.Label("null", null); - } - else - { - var type = obj.GetType(); - - var lbl = (i + offset) + ": " + obj.ToString() + ""; - - if (type.IsPrimitive || typeof(string).IsAssignableFrom(type)) - { - GUILayout.Label(lbl, null); - } - else - { - GUI.skin.button.alignment = TextAnchor.MiddleLeft; - if (GUILayout.Button(lbl, null)) - { - WindowManager.InspectObject(obj, out _); - } - GUI.skin.button.alignment = TextAnchor.MiddleCenter; - } - //var type = obj.GetType(); - //DrawMember(ref obj, type.ToString(), i.ToString(), rect, setTarget, setAction, 25, true); - } - - i++; - } - } - } - } - else - { - var label = value.ToString(); - - if (valueType == typeof(Object)) - { - label = (value as Object).name; - } - else if (value is Vector4 vec4) - { - label = vec4.ToString(); - } - else if (value is Vector3 vec3) - { - label = vec3.ToString(); - } - else if (value is Vector2 vec2) - { - label = vec2.ToString(); - } - else if (value is Rect rec) - { - label = rec.ToString(); - } - else if (value is Matrix4x4 matrix) - { - label = matrix.ToString(); - } - else if (value is Color col) - { - label = col.ToString(); - } - - string typeLabel; - if (ilType != null) - { - typeLabel = ilType.FullName; - } - else - { - typeLabel = value.GetType().FullName; - } - if (!label.Contains(typeLabel)) - { - label += $" ({typeLabel})"; - } - - GUI.skin.button.alignment = TextAnchor.MiddleLeft; - if (GUILayout.Button("" + label + "", new GUILayoutOption[] { GUILayout.MaxWidth(rect.width - 230) })) - { - WindowManager.InspectObject(value, out bool _); - } - GUI.skin.button.alignment = TextAnchor.MiddleCenter; - } - } - - // Helper for drawing primitive values (with Apply button) - - public static void DrawPrimitive(ref object value, Rect m_rect, object setTarget = null, Action setAction = null, bool autoSet = false) - { - bool allowSet = setAction != null; - - if (value.GetType() == typeof(bool)) - { - bool b = (bool)value; - var color = "" : "red>"); - - if (allowSet) - { - value = GUILayout.Toggle((bool)value, color + value.ToString() + "", null); - - if (b != (bool)value) - { - setAction.Invoke(setTarget); - } - } - } - else - { - if (value.ToString().Length > 37) - { - value = GUILayout.TextArea(value.ToString(), new GUILayoutOption[] { GUILayout.MaxWidth(m_rect.width - 260) }); - } - else - { - value = GUILayout.TextField(value.ToString(), new GUILayoutOption[] { GUILayout.MaxWidth(m_rect.width - 260) }); - } - - if (autoSet || (allowSet && GUILayout.Button("Apply", new GUILayoutOption[] { GUILayout.Width(60) }))) - { - setAction.Invoke(setTarget); - } - } - } - - // Helper for setting an enum - - public static void SetEnum(ref object value, int change) - { - var type = value.GetType(); - var names = Enum.GetNames(type).ToList(); - - int newindex = names.IndexOf(value.ToString()) + change; - - if ((change < 0 && newindex >= 0) || (change > 0 && newindex < names.Count)) - { - value = Enum.Parse(type, names[newindex]); - } - } } } diff --git a/src/MainMenu/Pages/SearchPage.cs b/src/MainMenu/Pages/SearchPage.cs index 9f9963d..9081cbd 100644 --- a/src/MainMenu/Pages/SearchPage.cs +++ b/src/MainMenu/Pages/SearchPage.cs @@ -1,4 +1,5 @@ using System; +using System.Collections; using System.Collections.Generic; using System.Linq; using System.Text; @@ -22,9 +23,11 @@ namespace Explorer private string m_typeInput = ""; private int m_limit = 20; private int m_pageOffset = 0; - private List m_searchResults = new List(); + //private List m_searchResults = new List(); private Vector2 resultsScroll = Vector2.zero; + private List m_searchResults = new List(); + public SceneFilter SceneMode = SceneFilter.Any; public TypeFilter TypeMode = TypeFilter.Object; @@ -59,6 +62,24 @@ namespace Explorer { } + private void CacheResults(IEnumerable results) + { + m_searchResults = new List(); + + foreach (var obj in results) + { + var toCache = obj; + + if (toCache is Il2CppSystem.Object ilObject) + { + toCache = ilObject.TryCast() ?? ilObject.TryCast()?.gameObject ?? ilObject; + } + + var cache = CacheObject.GetCacheObject(toCache); + m_searchResults.Add(cache); + } + } + public override void DrawWindow() { try @@ -68,7 +89,8 @@ namespace Explorer GUILayout.Label("Helpers", new GUILayoutOption[] { GUILayout.Width(70) }); if (GUILayout.Button("Find Static Instances", new GUILayoutOption[] { GUILayout.Width(180) })) { - m_searchResults = GetInstanceClassScanner().ToList(); + //m_searchResults = GetInstanceClassScanner().ToList(); + CacheResults(GetInstanceClassScanner()); m_pageOffset = 0; } GUILayout.EndHorizontal(); @@ -111,28 +133,12 @@ namespace Explorer if (m_searchResults.Count > 0) { int offset = m_pageOffset * this.m_limit; - int preiterated = 0; - if (offset >= count) m_pageOffset = 0; - for (int i = 0; i < m_searchResults.Count; i++) + for (int i = offset; i < offset + CppExplorer.ArrayLimit && i < count; i++) { - if (offset > 0 && preiterated < offset) - { - preiterated++; - continue; - } - - if (i - offset > this.m_limit - 1) - { - break; - } - - var obj = m_searchResults[i]; - - bool _ = false; - int __ = 0; - UIHelpers.DrawValue(ref obj, ref _, ref __, _temprect); + m_searchResults[i].Draw(MainMenu.MainRect, 0f); + //m_searchResults[i].DrawValue(MainMenu.MainRect); } } else @@ -141,7 +147,6 @@ namespace Explorer } GUILayout.EndScrollView(); - GUILayout.EndVertical(); } catch @@ -252,7 +257,7 @@ namespace Explorer private void Search() { m_pageOffset = 0; - m_searchResults = FindAllObjectsOfType(m_searchInput, m_typeInput); + CacheResults(FindAllObjectsOfType(m_searchInput, m_typeInput)); } private List FindAllObjectsOfType(string _search, string _type) diff --git a/src/Windows/Reflection/FieldInfoHolder.cs b/src/Windows/Reflection/FieldInfoHolder.cs deleted file mode 100644 index 5c4ca86..0000000 --- a/src/Windows/Reflection/FieldInfoHolder.cs +++ /dev/null @@ -1,122 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using System.Text; -using System.Threading.Tasks; -using MelonLoader; -using UnhollowerBaseLib; - -namespace Explorer -{ - public class FieldInfoHolder : MemberInfoHolder - { - public FieldInfo fieldInfo; - public object m_value; - - public FieldInfoHolder(Type _type, FieldInfo _fieldInfo) - { - classType = _type; - fieldInfo = _fieldInfo; - } - - public override void UpdateValue(object obj) - { - try - { - if (obj is Il2CppSystem.Object ilObject) - { - var declaringType = this.fieldInfo.DeclaringType; - - //var cast = ReflectionHelpers.Il2CppCast(obj, declaringType); - var cast = obj.Il2CppCast(declaringType); - m_value = this.fieldInfo.GetValue(fieldInfo.IsStatic ? null : cast); - } - else - { - m_value = this.fieldInfo.GetValue(fieldInfo.IsStatic ? null : obj); - } - } - catch (Exception e) - { - MelonLogger.Log($"Error updating FieldInfoHolder | {e.GetType()}: {e.Message}\r\n{e.StackTrace}"); - } - } - - public override void Draw(ReflectionWindow window) - { - UIHelpers.DrawMember(ref m_value, ref this.IsExpanded, ref this.arrayOffset, this.fieldInfo, window.m_rect, window.Target, SetValue); - } - - public override void SetValue(object obj) - { - try - { - if (fieldInfo.FieldType.IsEnum) - { - if (Enum.Parse(fieldInfo.FieldType, m_value.ToString()) is object enumValue && enumValue != null) - { - m_value = enumValue; - } - } - else if (fieldInfo.FieldType.IsPrimitive) - { - if (fieldInfo.FieldType == typeof(float)) - { - if (float.TryParse(m_value.ToString(), out float f)) - { - m_value = f; - } - else - { - MelonLogger.LogWarning("Cannot parse " + m_value.ToString() + " to a float!"); - } - } - else if (fieldInfo.FieldType == typeof(double)) - { - if (double.TryParse(m_value.ToString(), out double d)) - { - m_value = d; - } - else - { - MelonLogger.LogWarning("Cannot parse " + m_value.ToString() + " to a double!"); - } - } - else if (fieldInfo.FieldType != typeof(bool)) - { - if (int.TryParse(m_value.ToString(), out int i)) - { - m_value = i; - } - else - { - MelonLogger.LogWarning("Cannot parse " + m_value.ToString() + " to an integer! type: " + fieldInfo.FieldType); - } - } - else - { - MelonLogger.Log("Unsupported primitive field type: " + fieldInfo.FieldType.FullName); - } - } - - if (obj is Il2CppSystem.Object ilObject) - { - var declaringType = this.fieldInfo.DeclaringType; - - //var cast = ReflectionHelpers.Il2CppCast(obj, declaringType); - var cast = obj.Il2CppCast(declaringType); - fieldInfo.SetValue(fieldInfo.IsStatic ? null : cast, m_value); - } - else - { - fieldInfo.SetValue(fieldInfo.IsStatic ? null : obj, m_value); - } - } - catch (Exception e) - { - MelonLogger.Log($"Error setting FieldInfoHolder | {e.GetType()}: {e.Message}\r\n{e.StackTrace}"); - } - } - } -} diff --git a/src/Windows/Reflection/MemberInfoHolder.cs b/src/Windows/Reflection/MemberInfoHolder.cs deleted file mode 100644 index 8df7f83..0000000 --- a/src/Windows/Reflection/MemberInfoHolder.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Explorer -{ - public abstract class MemberInfoHolder - { - public Type classType; - public bool IsExpanded = false; - public int arrayOffset = 0; - - public abstract void Draw(ReflectionWindow window); - public abstract void UpdateValue(object obj); - public abstract void SetValue(object obj); - } -} diff --git a/src/Windows/Reflection/PropertyInfoHolder.cs b/src/Windows/Reflection/PropertyInfoHolder.cs deleted file mode 100644 index 498e651..0000000 --- a/src/Windows/Reflection/PropertyInfoHolder.cs +++ /dev/null @@ -1,126 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using System.Text; -using System.Threading.Tasks; -using MelonLoader; -using UnhollowerBaseLib; - -namespace Explorer -{ - public class PropertyInfoHolder : MemberInfoHolder - { - public PropertyInfo propInfo; - public object m_value; - - public PropertyInfoHolder(Type _type, PropertyInfo _propInfo) - { - classType = _type; - propInfo = _propInfo; - } - - public override void Draw(ReflectionWindow window) - { - UIHelpers.DrawMember(ref m_value, ref this.IsExpanded, ref this.arrayOffset, this.propInfo, window.m_rect, window.Target, SetValue); - } - - public override void UpdateValue(object obj) - { - try - { - if (obj is Il2CppSystem.Object ilObject) - { - var declaringType = this.propInfo.DeclaringType; - - if (declaringType == typeof(Il2CppObjectBase)) - { - m_value = ilObject.Pointer; - } - else - { - //var cast = ReflectionHelpers.Il2CppCast(obj, declaringType); - var cast = obj.Il2CppCast(declaringType); - m_value = this.propInfo.GetValue(this.propInfo.GetAccessors()[0].IsStatic ? null : cast, null); - } - } - else - { - m_value = this.propInfo.GetValue(obj, null); - } - } - catch //(Exception e) - { - //MelonLogger.Log("Exception on PropertyInfoHolder.UpdateValue, Name: " + this.propInfo.Name); - //MelonLogger.Log(e.GetType() + ", " + e.Message); - - //var inner = e.InnerException; - //while (inner != null) - //{ - // MelonLogger.Log("inner: " + inner.GetType() + ", " + inner.Message); - // inner = inner.InnerException; - //} - - //m_value = null; - } - } - - public override void SetValue(object obj) - { - try - { - if (propInfo.PropertyType.IsEnum) - { - if (Enum.Parse(propInfo.PropertyType, m_value.ToString()) is object enumValue && enumValue != null) - { - m_value = enumValue; - } - } - else if (propInfo.PropertyType.IsPrimitive) - { - if (propInfo.PropertyType == typeof(float)) - { - if (float.TryParse(m_value.ToString(), out float f)) - { - m_value = f; - } - else - { - MelonLogger.LogWarning("Cannot parse " + m_value.ToString() + " to a float!"); - } - } - else if (propInfo.PropertyType == typeof(double)) - { - if (double.TryParse(m_value.ToString(), out double d)) - { - m_value = d; - } - else - { - MelonLogger.LogWarning("Cannot parse " + m_value.ToString() + " to a double!"); - } - } - else if (propInfo.PropertyType != typeof(bool)) - { - if (int.TryParse(m_value.ToString(), out int i)) - { - m_value = i; - } - else - { - MelonLogger.LogWarning("Cannot parse " + m_value.ToString() + " to an integer! type: " + propInfo.PropertyType); - } - } - } - - var cast = obj.Il2CppCast(propInfo.DeclaringType); - propInfo.SetValue(propInfo.GetAccessors()[0].IsStatic ? null : cast, m_value, null); - } - catch - { - //MelonLogger.Log("Exception trying to set property " + this.propInfo.Name); - } - } - } -} diff --git a/src/Windows/ReflectionWindow.cs b/src/Windows/ReflectionWindow.cs index edc7e88..b1a245f 100644 --- a/src/Windows/ReflectionWindow.cs +++ b/src/Windows/ReflectionWindow.cs @@ -16,20 +16,21 @@ namespace Explorer public override string Name { get => "Object Reflection"; set => Name = value; } public Type ObjectType; - //public object Target; - private FieldInfoHolder[] m_FieldInfos; - private PropertyInfoHolder[] m_PropertyInfos; + private CacheObject[] m_cachedMembers; + private CacheObject[] m_cachedMemberFiltered; + private int m_pageOffset; private bool m_autoUpdate = false; private string m_search = ""; - public MemberFilter m_filter = MemberFilter.Property; + public MemberInfoType m_filter = MemberInfoType.Property; - public enum MemberFilter + public enum MemberInfoType { - Both, + Field, Property, - Field + Method, + All } public override void Init() @@ -44,119 +45,114 @@ namespace Explorer ObjectType = type; var types = ReflectionHelpers.GetAllBaseTypes(Target); + CacheMembers(types); - CacheFields(types); - CacheProperties(types); - - UpdateValues(true); + m_filter = MemberInfoType.All; + m_cachedMemberFiltered = m_cachedMembers.Where(x => ShouldProcessMember(x)).ToArray(); + UpdateValues(); + m_filter = MemberInfoType.Property; } public override void Update() { + m_cachedMemberFiltered = m_cachedMembers.Where(x => ShouldProcessMember(x)).ToArray(); + if (m_autoUpdate) { UpdateValues(); } } - private void UpdateValues(bool forceAll = false) + private void UpdateValues() { - UpdateMemberList(forceAll, this.m_FieldInfos, MemberFilter.Field); - UpdateMemberList(forceAll, this.m_PropertyInfos, MemberFilter.Property); + UpdateMembers(); } - private void UpdateMemberList(bool forceAll, MemberInfoHolder[] list, MemberFilter filter) + private void UpdateMembers() { - if (forceAll || m_filter == MemberFilter.Both || m_filter == filter) + foreach (var member in m_cachedMemberFiltered) { - foreach (var holder in list) - { - if (forceAll || ShouldUpdateMemberInfo(holder)) - { - holder.UpdateValue(Target); - } - } + member.UpdateValue(Target); } } - private bool ShouldUpdateMemberInfo(MemberInfoHolder holder) + private bool ShouldProcessMember(CacheObject holder) { - var memberName = holder is FieldInfoHolder ? - (holder as FieldInfoHolder).fieldInfo.Name : - (holder as PropertyInfoHolder).propInfo.Name; + if (m_filter != MemberInfoType.All && m_filter != holder.MemberInfoType) return false; - return m_search == "" || memberName.ToLower().Contains(m_search.ToLower()); + if (m_search == "" || holder.MemberInfo == null) return true; + + return holder.MemberInfo.Name + .ToLower() + .Contains(m_search.ToLower()); } - private void CacheProperties(Type[] types, List names = null) + private void CacheMembers(Type[] types, List names = null) { if (names == null) { names = new List(); } - var list = new List(); + var list = new List(); foreach (var type in types) { - PropertyInfo[] propInfos = new PropertyInfo[0]; + MemberInfo[] infos; try { - propInfos = type.GetProperties(ReflectionHelpers.CommonFlags); + infos = type.GetMembers(ReflectionHelpers.CommonFlags); } - catch (TypeLoadException) + catch { - MelonLogger.Log($"Couldn't get Properties for Type '{type.Name}', it may not support Il2Cpp Reflection at the moment."); + MelonLogger.Log("Exception getting members for type: " + type.Name); + continue; } - foreach (var pi in propInfos) + foreach (var member in infos) { - // this member causes a crash when inspected, so just skipping it for now. - if (pi.Name == "Il2CppType") + try { - continue; - } + if (member.MemberType == MemberTypes.Field || member.MemberType == MemberTypes.Property) + { + if (member.Name == "Il2CppType") continue; - if (names.Contains(pi.Name)) + if (names.Contains(member.Name)) continue; + names.Add(member.Name); + + object value = null; + object target = Target; + + if (target is Il2CppSystem.Object ilObject) + { + if (member.DeclaringType == typeof(Il2CppObjectBase)) continue; + + target = ilObject.Il2CppCast(member.DeclaringType); + } + + if (member is FieldInfo) + { + value = (member as FieldInfo).GetValue(target); + } + else if (member is PropertyInfo) + { + value = (member as PropertyInfo).GetValue(target); + } + + list.Add(CacheObject.GetCacheObject(value, member, Target)); + } + } + catch (Exception e) { - continue; + MelonLogger.Log("Exception caching member " + member.Name + "!"); + MelonLogger.Log(e.GetType() + ", " + e.Message); + MelonLogger.Log(e.StackTrace); } - names.Add(pi.Name); - - var piHolder = new PropertyInfoHolder(type, pi); - list.Add(piHolder); } } - m_PropertyInfos = list.ToArray(); - } - - private void CacheFields(Type[] types, List names = null) - { - if (names == null) - { - names = new List(); - } - - var list = new List(); - - foreach (var type in types) - { - foreach (var fi in type.GetFields(ReflectionHelpers.CommonFlags)) - { - if (names.Contains(fi.Name)) - { - continue; - } - names.Add(fi.Name); - - var fiHolder = new FieldInfoHolder(type, fi); - list.Add(fiHolder); - } - } - - m_FieldInfos = list.ToArray(); + m_cachedMembers = list.ToArray(); } // =========== GUI DRAW =========== // @@ -211,9 +207,9 @@ namespace Explorer GUILayout.BeginHorizontal(null); GUILayout.Label("Filter:", new GUILayoutOption[] { GUILayout.Width(75) }); - FilterToggle(MemberFilter.Both, "Both"); - FilterToggle(MemberFilter.Property, "Properties"); - FilterToggle(MemberFilter.Field, "Fields"); + FilterToggle(MemberInfoType.All, "All"); + FilterToggle(MemberInfoType.Property, "Properties"); + FilterToggle(MemberInfoType.Field, "Fields"); GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(null); @@ -229,19 +225,32 @@ namespace Explorer GUILayout.Space(10); + int count = m_cachedMemberFiltered.Length; + + if (count > 20) + { + // prev/next page buttons + GUILayout.BeginHorizontal(null); + int maxOffset = (int)Mathf.Ceil(count / 20); + if (GUILayout.Button("< Prev", null)) + { + if (m_pageOffset > 0) m_pageOffset--; + } + + GUILayout.Label($"Page {m_pageOffset + 1}/{maxOffset + 1}", new GUILayoutOption[] { GUILayout.Width(80) }); + + if (GUILayout.Button("Next >", null)) + { + if (m_pageOffset < maxOffset) m_pageOffset++; + } + GUILayout.EndHorizontal(); + } + scroll = GUILayout.BeginScrollView(scroll, GUI.skin.scrollView); GUILayout.Space(10); - if (m_filter == MemberFilter.Both || m_filter == MemberFilter.Field) - { - DrawMembers(this.m_FieldInfos, "Fields"); - } - - if (m_filter == MemberFilter.Both || m_filter == MemberFilter.Property) - { - DrawMembers(this.m_PropertyInfos, "Properties"); - } + DrawMembers(this.m_cachedMemberFiltered); GUILayout.EndScrollView(); @@ -257,28 +266,59 @@ namespace Explorer } } - private void DrawMembers(MemberInfoHolder[] members, string title) + private void DrawMembers(CacheObject[] members) { + // todo pre-cache list based on current search, otherwise this doesnt work. + + int i = 0; + DrawMembersInternal("Properties", MemberInfoType.Property, members, ref i); + DrawMembersInternal("Fields", MemberInfoType.Field, members, ref i); + } + + private void DrawMembersInternal(string title, MemberInfoType filter, CacheObject[] members, ref int index) + { + if (m_filter != filter && m_filter != MemberInfoType.All) + { + return; + } + UIStyles.HorizontalLine(Color.grey); GUILayout.Label($"{title}", null); - foreach (var holder in members) - { - var memberName = (holder as FieldInfoHolder)?.fieldInfo.Name ?? (holder as PropertyInfoHolder)?.propInfo.Name; + int offset = (m_pageOffset * 20) + index; - if (m_search != "" && !memberName.ToLower().Contains(m_search.ToLower())) - { - continue; - } + if (offset >= m_cachedMemberFiltered.Length) + { + m_pageOffset = 0; + offset = 0; + } + + for (int j = offset; j < offset + 20 && j < members.Length; j++) + { + var holder = members[j]; + + if (holder.MemberInfoType != filter || !ShouldProcessMember(holder)) continue; GUILayout.BeginHorizontal(new GUILayoutOption[] { GUILayout.Height(25) }); - holder.Draw(this); + try + { + holder.Draw(this.m_rect, 180f); + } + catch // (Exception e) + { + //MelonLogger.Log("Exception drawing member " + holder.MemberInfo.Name); + //MelonLogger.Log(e.GetType() + ", " + e.Message); + //MelonLogger.Log(e.StackTrace); + } GUILayout.EndHorizontal(); + + index++; + if (index >= 20) break; } } - private void FilterToggle(MemberFilter mode, string label) + private void FilterToggle(MemberInfoType mode, string label) { if (m_filter == mode) { diff --git a/src/Windows/UIWindow.cs b/src/Windows/UIWindow.cs index 7831d8f..117e35b 100644 --- a/src/Windows/UIWindow.cs +++ b/src/Windows/UIWindow.cs @@ -23,9 +23,12 @@ namespace Explorer public Vector2 scroll = Vector2.zero; + public abstract void Init(); + public abstract void WindowFunction(int windowID); + public abstract void Update(); + public static UIWindow CreateWindow(object target) where T : UIWindow { - //var component = (UIWindow)AddToGameObject(Instance.gameObject); var window = Activator.CreateInstance(); window.Target = target; @@ -50,13 +53,8 @@ namespace Explorer MelonLogger.Log("Exception removing Window from WindowManager.Windows list!"); MelonLogger.Log($"{e.GetType()} : {e.Message}\r\n{e.StackTrace}"); } - //Destroy(this); } - public abstract void Init(); - public abstract void WindowFunction(int windowID); - public abstract void Update(); - public void OnGUI() { if (CppExplorer.ShowMenu)