diff --git a/src/CachedObjects/CacheObjectBase.cs b/src/CachedObjects/CacheObjectBase.cs index 61f6118..fd41eda 100644 --- a/src/CachedObjects/CacheObjectBase.cs +++ b/src/CachedObjects/CacheObjectBase.cs @@ -44,12 +44,9 @@ namespace Explorer } } - // ===== Abstract/Virtual Methods ===== // - public virtual void Init() { } - public abstract void DrawValue(Rect window, float width); - // ===== Static Methods ===== // + public abstract void DrawValue(Rect window, float width); /// /// Get CacheObject from only an object instance @@ -205,11 +202,8 @@ namespace Explorer } holder.m_argumentInput = new string[holder.m_arguments.Length]; - - if (!holder.HasParameters) - { - holder.UpdateValue(); - } + + holder.UpdateValue(); holder.Init(); @@ -229,7 +223,18 @@ namespace Explorer return true; } - // ======== Instance Methods ========= + public float CalcWhitespace(Rect window) + { + if (!(this is IExpandHeight)) return 0f; + + float whitespace = (this as IExpandHeight).WhiteSpace; + if (whitespace > 0) + { + ClampLabelWidth(window, ref whitespace); + } + + return whitespace; + } public object[] ParseArguments() { @@ -247,16 +252,20 @@ namespace Explorer { try { - parsedArgs.Add(type.GetMethod("Parse", new Type[] { typeof(string) }).Invoke(null, new object[] { input })); + parsedArgs.Add(type.GetMethod("Parse", new Type[] { typeof(string) }) + .Invoke(null, new object[] { input })); } catch { - //MelonLogger.Log($"Unable to parse '{input}' to type '{type.Name}'"); - - // try add a null arg i guess - parsedArgs.Add(null); - - //break; + if (m_arguments[i].HasDefaultValue) + { + parsedArgs.Add(m_arguments[i].DefaultValue); + } + else + { + // Try add a null arg I guess + parsedArgs.Add(null); + } } } } @@ -271,6 +280,12 @@ namespace Explorer return; } + if (HasParameters && !m_isEvaluating) + { + // Need to enter parameters first + return; + } + try { if (MemInfo.MemberType == MemberTypes.Field) @@ -332,7 +347,7 @@ namespace Explorer } } - // ========= Instance Gui Draw ========== + // ========= Gui Draw ========== public const float MAX_LABEL_WIDTH = 400f; public const string EVALUATE_LABEL = "Evaluate"; @@ -375,10 +390,18 @@ namespace Explorer var input = m_argumentInput[i]; var type = m_arguments[i].ParameterType.Name; + var label = "" + type + " " + name + ""; + if (m_arguments[i].HasDefaultValue) + { + label = $"[{label} = {m_arguments[i].DefaultValue}]"; + } + GUILayout.BeginHorizontal(null); - GUILayout.Label(i.ToString(), new GUILayoutOption[] { GUILayout.Width(30) }); + + GUILayout.Label(i.ToString(), new GUILayoutOption[] { GUILayout.Width(20) }); m_argumentInput[i] = GUILayout.TextField(input, new GUILayoutOption[] { GUILayout.Width(150) }); - GUILayout.Label("" + type + " " + name + "", null); + GUILayout.Label(label, null); + GUILayout.EndHorizontal(); } @@ -438,7 +461,11 @@ namespace Explorer { GUILayout.Label("Reflection failed! (" + ReflectionException + ")", null); } - else if (Value == null && MemInfo?.MemberType != MemberTypes.Method) + else if ((HasParameters || this is CacheMethod) && !m_evaluated) + { + GUILayout.Label($"Not yet evaluated ({ValueTypeName})", null); + } + else if (Value == null && !(this is CacheMethod)) { GUILayout.Label("null (" + ValueTypeName + ")", null); } @@ -463,7 +490,7 @@ namespace Explorer m_richTextName = $"{MemInfo.DeclaringType.Name}.{MemInfo.Name}"; - if (m_arguments.Length > 0) + if (m_arguments.Length > 0 || this is CacheMethod) { m_richTextName += "("; var _params = ""; diff --git a/src/Helpers/IExpandHeight.cs b/src/CachedObjects/IExpandHeight.cs similarity index 71% rename from src/Helpers/IExpandHeight.cs rename to src/CachedObjects/IExpandHeight.cs index 20b8e72..4a3fb68 100644 --- a/src/Helpers/IExpandHeight.cs +++ b/src/CachedObjects/IExpandHeight.cs @@ -9,8 +9,6 @@ namespace Explorer interface IExpandHeight { bool IsExpanded { get; set; } - - float WhiteSpace { get; set; } - float ButtonWidthOffset { get; set; } + float WhiteSpace { get; set; } } } diff --git a/src/CachedObjects/Object/CacheDictionary.cs b/src/CachedObjects/Object/CacheDictionary.cs index e6c9bce..af7530b 100644 --- a/src/CachedObjects/Object/CacheDictionary.cs +++ b/src/CachedObjects/Object/CacheDictionary.cs @@ -15,7 +15,6 @@ namespace Explorer { public bool IsExpanded { get; set; } public float WhiteSpace { get; set; } = 215f; - public float ButtonWidthOffset { get; set; } = 350f; public PageHelper Pages = new PageHelper(); @@ -126,6 +125,10 @@ namespace Explorer m_keysType = type.GetGenericArguments()[0]; m_valuesType = type.GetGenericArguments()[1]; } + else + { + MelonLogger.Log("TODO? Dictionary is of type: " + Value.GetType().FullName); + } } } @@ -203,11 +206,7 @@ namespace Explorer return; } - float whitespace = WhiteSpace; - if (whitespace > 0) - { - ClampLabelWidth(window, ref whitespace); - } + var whitespace = CalcWhitespace(window); int count = m_cachedKeys.Length; diff --git a/src/CachedObjects/Object/CacheList.cs b/src/CachedObjects/Object/CacheList.cs index bc5b963..6713d45 100644 --- a/src/CachedObjects/Object/CacheList.cs +++ b/src/CachedObjects/Object/CacheList.cs @@ -12,7 +12,6 @@ namespace Explorer { public bool IsExpanded { get; set; } public float WhiteSpace { get; set; } = 215f; - public float ButtonWidthOffset { get; set; } = 290f; public PageHelper Pages = new PageHelper(); @@ -245,11 +244,7 @@ namespace Explorer return; } - float whitespace = WhiteSpace; - if (whitespace > 0) - { - ClampLabelWidth(window, ref whitespace); - } + var whitespace = CalcWhitespace(window); int count = m_cachedEntries.Length; diff --git a/src/CachedObjects/Other/CacheMethod.cs b/src/CachedObjects/Other/CacheMethod.cs index a182c66..6ce890e 100644 --- a/src/CachedObjects/Other/CacheMethod.cs +++ b/src/CachedObjects/Other/CacheMethod.cs @@ -44,11 +44,9 @@ namespace Explorer } else { - var parsedArgs = ParseArguments(); - try { - ret = mi.Invoke(mi.IsStatic ? null : DeclaringInstance, parsedArgs.ToArray()); + ret = mi.Invoke(mi.IsStatic ? null : DeclaringInstance, ParseArguments()); m_evaluated = true; } catch (Exception e) diff --git a/src/CachedObjects/Struct/CacheColor.cs b/src/CachedObjects/Struct/CacheColor.cs index ca56db3..c05bc59 100644 --- a/src/CachedObjects/Struct/CacheColor.cs +++ b/src/CachedObjects/Struct/CacheColor.cs @@ -16,7 +16,6 @@ namespace Explorer public bool IsExpanded { get; set; } public float WhiteSpace { get; set; } = 215f; - public float ButtonWidthOffset { get; set; } = 290f; public override void UpdateValue() { @@ -59,11 +58,7 @@ namespace Explorer { GUILayout.EndHorizontal(); - float whitespace = WhiteSpace; - if (whitespace > 0) - { - ClampLabelWidth(window, ref whitespace); - } + var whitespace = CalcWhitespace(window); GUILayout.BeginHorizontal(null); GUILayout.Space(whitespace); diff --git a/src/CachedObjects/Struct/CacheQuaternion.cs b/src/CachedObjects/Struct/CacheQuaternion.cs index b739a61..b15e178 100644 --- a/src/CachedObjects/Struct/CacheQuaternion.cs +++ b/src/CachedObjects/Struct/CacheQuaternion.cs @@ -15,7 +15,6 @@ namespace Explorer public bool IsExpanded { get; set; } public float WhiteSpace { get; set; } = 215f; - public float ButtonWidthOffset { get; set; } = 290f; public override void UpdateValue() { @@ -54,11 +53,7 @@ namespace Explorer { GUILayout.EndHorizontal(); - float whitespace = WhiteSpace; - if (whitespace > 0) - { - ClampLabelWidth(window, ref whitespace); - } + var whitespace = CalcWhitespace(window); GUILayout.BeginHorizontal(null); GUILayout.Space(whitespace); diff --git a/src/CachedObjects/Struct/CacheRect.cs b/src/CachedObjects/Struct/CacheRect.cs index a89bc53..038c23f 100644 --- a/src/CachedObjects/Struct/CacheRect.cs +++ b/src/CachedObjects/Struct/CacheRect.cs @@ -16,7 +16,6 @@ namespace Explorer public bool IsExpanded { get; set; } public float WhiteSpace { get; set; } = 215f; - public float ButtonWidthOffset { get; set; } = 290f; public override void UpdateValue() { @@ -56,11 +55,7 @@ namespace Explorer { GUILayout.EndHorizontal(); - float whitespace = WhiteSpace; - if (whitespace > 0) - { - ClampLabelWidth(window, ref whitespace); - } + var whitespace = CalcWhitespace(window); GUILayout.BeginHorizontal(null); GUILayout.Space(whitespace); diff --git a/src/CachedObjects/Struct/CacheVector.cs b/src/CachedObjects/Struct/CacheVector.cs index 083f55b..1a6da12 100644 --- a/src/CachedObjects/Struct/CacheVector.cs +++ b/src/CachedObjects/Struct/CacheVector.cs @@ -21,7 +21,6 @@ namespace Explorer public bool IsExpanded { get; set; } public float WhiteSpace { get; set; } = 215f; - public float ButtonWidthOffset { get; set; } = 290f; public override void Init() { @@ -90,11 +89,8 @@ namespace Explorer if (CanWrite && IsExpanded) { GUILayout.EndHorizontal(); - float whitespace = WhiteSpace; - if (whitespace > 0) - { - ClampLabelWidth(window, ref whitespace); - } + + var whitespace = CalcWhitespace(window); // always draw x and y GUILayout.BeginHorizontal(null); diff --git a/src/CppExplorer.cs b/src/CppExplorer.cs index 02e7654..555962e 100644 --- a/src/CppExplorer.cs +++ b/src/CppExplorer.cs @@ -13,7 +13,7 @@ namespace Explorer public class CppExplorer : MelonMod { public const string NAME = "CppExplorer"; - public const string VERSION = "1.6.5"; + public const string VERSION = "1.6.7"; public const string AUTHOR = "Sinai"; public const string GUID = "com.sinai.cppexplorer"; @@ -44,7 +44,11 @@ namespace Explorer UpdateCursorControl(); } - // ========== MonoBehaviour methods ========== + private static void SetForceUnlock(bool unlock) + { + m_forceUnlock = unlock; + UpdateCursorControl(); + } public override void OnApplicationStart() { @@ -60,9 +64,9 @@ namespace Explorer m_lastVisibleState = Cursor.visible; // Enable ShowMenu and ForceUnlockMouse - // (set m_showMenu to not call UpdateCursorState twice) + // (set m_showMenu directly to not call UpdateCursorState twice) m_showMenu = true; - SetForceUnlock(true); + ForceUnlockMouse = true; MelonLogger.Log($"CppExplorer {VERSION} initialized."); } @@ -104,14 +108,6 @@ namespace Explorer InspectUnderMouse.OnGUI(); } - // =========== Cursor control =========== - - private static void SetForceUnlock(bool unlock) - { - m_forceUnlock = unlock; - UpdateCursorControl(); - } - private static void UpdateCursorControl() { m_currentlySettingCursor = true; diff --git a/src/CppExplorer.csproj b/src/CppExplorer.csproj index 8e0cba1..5971b5b 100644 --- a/src/CppExplorer.csproj +++ b/src/CppExplorer.csproj @@ -73,7 +73,7 @@ - + diff --git a/src/Tests/TestClass.cs b/src/Tests/TestClass.cs index 0f9dc0f..d763d55 100644 --- a/src/Tests/TestClass.cs +++ b/src/Tests/TestClass.cs @@ -14,21 +14,7 @@ namespace Explorer.Tests public static TestClass Instance => m_instance ?? (m_instance = new TestClass()); private static TestClass m_instance; - public string this[int index] - { - get - { - return $"int indexer: {index}"; - } - } - - public string this[string stringIndex] - { - get - { - return $"string indexer: {stringIndex}"; - } - } + // Test indexed parameter public string this[int arg0, string arg1] { @@ -38,6 +24,8 @@ namespace Explorer.Tests } } + // Test basic list + public static List TestList = new List { "1", @@ -46,29 +34,48 @@ namespace Explorer.Tests "etc..." }; - public static Dictionary> NestedDictionary = new Dictionary> + // Test a nested dictionary + + public static Dictionary> NestedDictionary = new Dictionary> { { - 123, - new List + 1, + new Dictionary { - "One", - "Two" + { + "Sub 1", 123 + }, + { + "Sub 2", 456 + }, } }, { - 567, - new List + 2, + new Dictionary { - "One", - "Two" + { + "Sub 3", 789 + }, + { + "Sub 4", 000 + }, } }, }; + // Test a basic method + public static Color TestMethod(float r, float g, float b, float a) { return new Color(r, g, b, a); } + + // A method with default arguments + + public static Vector3 TestDefaultArgs(float arg0, float arg1, float arg2 = 5.0f) + { + return new Vector3(arg0, arg1, arg2); + } } } diff --git a/src/Windows/ReflectionWindow.cs b/src/Windows/ReflectionWindow.cs index e616fad..da5d2e7 100644 --- a/src/Windows/ReflectionWindow.cs +++ b/src/Windows/ReflectionWindow.cs @@ -28,20 +28,26 @@ namespace Explorer public MemberTypes m_filter = MemberTypes.Property; private bool m_hideFailedReflection = false; - // some extra caching + // some extra cast-caching private UnityEngine.Object m_uObj; private Component m_component; + private static readonly HashSet _memberBlacklist = new HashSet + { + // Causes a crash + "Type.DeclaringMethod", + // Pointless (handled by Properties) + "get_", + "set_" + }; + public override void Init() { - var type = ReflectionHelpers.GetActualType(Target); + TargetType = ReflectionHelpers.GetActualType(Target); - TargetType = type; - - var types = ReflectionHelpers.GetAllBaseTypes(Target); - - CacheMembers(types); + CacheMembers(ReflectionHelpers.GetAllBaseTypes(Target)); + // cache the extra cast-caching if (Target is Il2CppSystem.Object ilObject) { var unityObj = ilObject.TryCast(); @@ -56,13 +62,6 @@ namespace Explorer } } } - - m_filter = MemberTypes.All; - m_autoUpdate = true; - Update(); - - m_autoUpdate = false; - m_filter = MemberTypes.Property; } public override void Update() @@ -99,28 +98,32 @@ namespace Explorer private bool ShouldProcessMember(CacheObjectBase holder) { - if (m_filter != MemberTypes.All && m_filter != holder.MemInfo?.MemberType) return false; + // check MemberTypes filter + if (m_filter != MemberTypes.All && m_filter != holder.MemInfo?.MemberType) + return false; - if (!string.IsNullOrEmpty(holder.ReflectionException) && m_hideFailedReflection) return false; + // hide failed reflection + if (!string.IsNullOrEmpty(holder.ReflectionException) && m_hideFailedReflection) + return false; - if (m_search == "" || holder.MemInfo == null) return true; + // see if we should do name search + if (m_search == "" || holder.MemInfo == null) + return true; - var name = holder.MemInfo.DeclaringType.Name + "." + holder.MemInfo.Name; - - return name.ToLower().Contains(m_search.ToLower()); + // ok do name search + return (holder.MemInfo.DeclaringType.Name + "." + holder.MemInfo.Name) + .ToLower() + .Contains(m_search.ToLower()); } private void CacheMembers(Type[] types) { var list = new List(); - - var names = new List(); + var cachedSigs = new List(); foreach (var declaringType in types) { MemberInfo[] infos; - string exception = null; - try { infos = declaringType.GetMembers(ReflectionHelpers.CommonFlags); @@ -132,6 +135,7 @@ namespace Explorer } object target = Target; + string exception = null; if (target is Il2CppSystem.Object ilObject) { @@ -147,56 +151,56 @@ namespace Explorer foreach (var member in infos) { - if (member.MemberType == MemberTypes.Field || member.MemberType == MemberTypes.Property || member.MemberType == MemberTypes.Method) + // make sure member type is Field, Method of Property (4 / 8 / 16) + int m = (int)member.MemberType; + if (m < 4 || m > 16) + continue; + + // check blacklisted members + if (_memberBlacklist.Any(it => member.Name.StartsWith(it))) + continue; + + // compare signature to already cached members + var signature = $"{member.DeclaringType.Name}.{member.Name}"; + if (member is MethodInfo mi) { - var name = $"{member.DeclaringType.Name}.{member.Name}"; + AppendParams(mi.GetParameters()); + } + else if (member is PropertyInfo pi) + { + AppendParams(pi.GetIndexParameters()); + } - // blacklist (should probably make a proper implementation) - if (name == "Type.DeclaringMethod" || member.Name.StartsWith("get_") || member.Name.StartsWith("set_")) //|| member.Name.Contains("Il2CppType") + void AppendParams(ParameterInfo[] _args) + { + signature += " ("; + foreach (var param in _args) { - continue; + signature += $"{param.ParameterType.Name} {param.Name}, "; } + signature += ")"; + } - if (member is MethodInfo mi) - { - name += " ("; - foreach (var param in mi.GetParameters()) - { - name += $"{param.ParameterType.Name} {param.Name}, "; - } - name += ")"; - } - else if (member is PropertyInfo pi) - { - name += " ("; - foreach (var param in pi.GetIndexParameters()) - { - name += $"{param.ParameterType.Name} {param.Name}, "; - } - name += ")"; - } + if (cachedSigs.Contains(signature)) + { + continue; + } - if (names.Contains(name)) + try + { + var cached = CacheObjectBase.GetCacheObject(member, target); + if (cached != null) { - continue; - } - - try - { - var cached = CacheObjectBase.GetCacheObject(member, target); - if (cached != null) - { - names.Add(name); - list.Add(cached); - cached.ReflectionException = exception; - } - } - catch (Exception e) - { - MelonLogger.LogWarning($"Exception caching member {name}!"); - MelonLogger.Log(e.ToString()); + cachedSigs.Add(signature); + list.Add(cached); + cached.ReflectionException = exception; } } + catch (Exception e) + { + MelonLogger.LogWarning($"Exception caching member {signature}!"); + MelonLogger.Log(e.ToString()); + } } }