mirror of
https://github.com/sinai-dev/UnityExplorer.git
synced 2025-06-16 22:27:45 +08:00
Add support for Hashset, add try/catch for loading settings
This commit is contained in:
parent
51ed936e30
commit
835a81765e
@ -172,7 +172,7 @@ namespace Explorer
|
|||||||
{
|
{
|
||||||
holder = new CacheDictionary();
|
holder = new CacheDictionary();
|
||||||
}
|
}
|
||||||
else if (ReflectionHelpers.IsEnumerable(valueType) || ReflectionHelpers.IsCppList(valueType))
|
else if (ReflectionHelpers.IsEnumerable(valueType) || ReflectionHelpers.IsCppEnumerable(valueType))
|
||||||
{
|
{
|
||||||
holder = new CacheList();
|
holder = new CacheList();
|
||||||
}
|
}
|
||||||
|
@ -228,7 +228,7 @@ namespace Explorer
|
|||||||
var negativeWhitespace = window.width - (whitespace + 100f);
|
var negativeWhitespace = window.width - (whitespace + 100f);
|
||||||
|
|
||||||
GUI.skin.button.alignment = TextAnchor.MiddleLeft;
|
GUI.skin.button.alignment = TextAnchor.MiddleLeft;
|
||||||
string btnLabel = $"<color=#2df7b2>[{count}] Dictionary<{TypeOfKeys.FullName}, {TypeOfValues.FullName}></color>";
|
string btnLabel = $"[{count}] <color=#2df7b2>Dictionary<{TypeOfKeys.FullName}, {TypeOfValues.FullName}></color>";
|
||||||
if (GUILayout.Button(btnLabel, new GUILayoutOption[] { GUILayout.Width(negativeWhitespace) }))
|
if (GUILayout.Button(btnLabel, new GUILayoutOption[] { GUILayout.Width(negativeWhitespace) }))
|
||||||
{
|
{
|
||||||
WindowManager.InspectObject(Value, out bool _);
|
WindowManager.InspectObject(Value, out bool _);
|
||||||
|
@ -40,7 +40,7 @@ namespace Explorer
|
|||||||
private Type m_genericTypeDef;
|
private Type m_genericTypeDef;
|
||||||
|
|
||||||
// Cached ToArray method for Lists
|
// Cached ToArray method for Lists
|
||||||
public MethodInfo GenericToArrayMethod
|
public MethodInfo CppListToArrayMethod
|
||||||
{
|
{
|
||||||
get => GetGenericToArrayMethod();
|
get => GetGenericToArrayMethod();
|
||||||
}
|
}
|
||||||
@ -60,7 +60,7 @@ namespace Explorer
|
|||||||
{
|
{
|
||||||
if (m_enumerable == null && Value != null)
|
if (m_enumerable == null && Value != null)
|
||||||
{
|
{
|
||||||
m_enumerable = Value as IEnumerable ?? GetEnumerableFromIl2CppList();
|
m_enumerable = Value as IEnumerable ?? EnumerateWithReflection();
|
||||||
}
|
}
|
||||||
return m_enumerable;
|
return m_enumerable;
|
||||||
}
|
}
|
||||||
@ -100,21 +100,45 @@ namespace Explorer
|
|||||||
return m_itemProperty;
|
return m_itemProperty;
|
||||||
}
|
}
|
||||||
|
|
||||||
private IEnumerable GetEnumerableFromIl2CppList()
|
private IEnumerable EnumerateWithReflection()
|
||||||
{
|
{
|
||||||
if (Value == null) return null;
|
if (Value == null) return null;
|
||||||
|
|
||||||
if (GenericTypeDef == typeof(Il2CppSystem.Collections.Generic.List<>))
|
if (GenericTypeDef == typeof(Il2CppSystem.Collections.Generic.List<>))
|
||||||
{
|
{
|
||||||
return (IEnumerable)GenericToArrayMethod?.Invoke(Value, new object[0]);
|
return (IEnumerable)CppListToArrayMethod?.Invoke(Value, new object[0]);
|
||||||
|
}
|
||||||
|
else if (GenericTypeDef == typeof(Il2CppSystem.Collections.Generic.HashSet<>))
|
||||||
|
{
|
||||||
|
return CppHashSetToMono();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return ConvertIListToMono();
|
return CppIListToMono();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private IList ConvertIListToMono()
|
private IEnumerable CppHashSetToMono()
|
||||||
|
{
|
||||||
|
var set = new HashSet<object>();
|
||||||
|
|
||||||
|
// invoke GetEnumerator
|
||||||
|
var enumerator = Value.GetType().GetMethod("GetEnumerator").Invoke(Value, 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))
|
||||||
|
{
|
||||||
|
set.Add(current.GetValue(enumerator));
|
||||||
|
}
|
||||||
|
|
||||||
|
return set;
|
||||||
|
}
|
||||||
|
|
||||||
|
private IList CppIListToMono()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
@ -49,10 +49,10 @@ namespace Explorer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var c = (Color)Value;
|
//var c = (Color)Value;
|
||||||
GUI.color = c;
|
//GUI.color = c;
|
||||||
GUILayout.Label($"<color=#2df7b2>Color:</color> {c.ToString()}", null);
|
GUILayout.Label($"<color=#2df7b2>Color:</color> {((Color)Value).ToString()}", null);
|
||||||
GUI.color = Color.white;
|
//GUI.color = Color.white;
|
||||||
|
|
||||||
if (CanWrite && IsExpanded)
|
if (CanWrite && IsExpanded)
|
||||||
{
|
{
|
||||||
|
@ -28,25 +28,31 @@ namespace Explorer
|
|||||||
Directory.CreateDirectory(EXPLORER_FOLDER);
|
Directory.CreateDirectory(EXPLORER_FOLDER);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (File.Exists(SETTINGS_PATH))
|
if (LoadSettings()) return;
|
||||||
{
|
|
||||||
LoadSettings(false);
|
Instance = new ModConfig();
|
||||||
}
|
SaveSettings();
|
||||||
else
|
|
||||||
{
|
|
||||||
Instance = new ModConfig();
|
|
||||||
SaveSettings(false);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void LoadSettings(bool checkExist = true)
|
// returns true if settings successfully loaded
|
||||||
|
public static bool LoadSettings(bool checkExist = true)
|
||||||
{
|
{
|
||||||
if (checkExist && !File.Exists(SETTINGS_PATH))
|
if (checkExist && !File.Exists(SETTINGS_PATH))
|
||||||
return;
|
return false;
|
||||||
|
|
||||||
var file = File.OpenRead(SETTINGS_PATH);
|
try
|
||||||
Instance = (ModConfig)Serializer.Deserialize(file);
|
{
|
||||||
file.Close();
|
using (var file = File.OpenRead(SETTINGS_PATH))
|
||||||
|
{
|
||||||
|
Instance = (ModConfig)Serializer.Deserialize(file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Instance != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void SaveSettings(bool checkExist = true)
|
public static void SaveSettings(bool checkExist = true)
|
||||||
@ -54,9 +60,10 @@ namespace Explorer
|
|||||||
if (checkExist && File.Exists(SETTINGS_PATH))
|
if (checkExist && File.Exists(SETTINGS_PATH))
|
||||||
File.Delete(SETTINGS_PATH);
|
File.Delete(SETTINGS_PATH);
|
||||||
|
|
||||||
FileStream file = File.Create(SETTINGS_PATH);
|
using (var file = File.Create(SETTINGS_PATH))
|
||||||
Serializer.Serialize(file, Instance);
|
{
|
||||||
file.Close();
|
Serializer.Serialize(file, Instance);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<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>
|
||||||
@ -29,6 +29,10 @@
|
|||||||
<HintPath>..\..\..\..\..\Steam\steamapps\common\Hellpoint\MelonLoader\Managed\Il2Cppmscorlib.dll</HintPath>
|
<HintPath>..\..\..\..\..\Steam\steamapps\common\Hellpoint\MelonLoader\Managed\Il2Cppmscorlib.dll</HintPath>
|
||||||
<Private>False</Private>
|
<Private>False</Private>
|
||||||
</Reference>
|
</Reference>
|
||||||
|
<Reference Include="Il2CppSystem.Core">
|
||||||
|
<HintPath>..\..\..\Steam\steamapps\common\Hellpoint\MelonLoader\Managed\Il2CppSystem.Core.dll</HintPath>
|
||||||
|
<Private>False</Private>
|
||||||
|
</Reference>
|
||||||
<Reference Include="mcs">
|
<Reference Include="mcs">
|
||||||
<HintPath>..\lib\mcs.dll</HintPath>
|
<HintPath>..\lib\mcs.dll</HintPath>
|
||||||
<Private>True</Private>
|
<Private>True</Private>
|
||||||
|
@ -37,13 +37,14 @@ namespace Explorer
|
|||||||
return typeof(IEnumerable).IsAssignableFrom(t);
|
return typeof(IEnumerable).IsAssignableFrom(t);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only Il2Cpp List needs this check. C# List is IEnumerable.
|
// Checks for Il2Cpp List or HashSet.
|
||||||
public static bool IsCppList(Type t)
|
public static bool IsCppEnumerable(Type t)
|
||||||
{
|
{
|
||||||
if (t.IsGenericType && t.GetGenericTypeDefinition() is Type g)
|
if (t.IsGenericType && t.GetGenericTypeDefinition() is Type g)
|
||||||
{
|
{
|
||||||
return typeof(Il2CppSystem.Collections.Generic.List<>).IsAssignableFrom(g)
|
return typeof(Il2CppSystem.Collections.Generic.List<>).IsAssignableFrom(g)
|
||||||
|| typeof(Il2CppSystem.Collections.Generic.IList<>).IsAssignableFrom(g);
|
|| typeof(Il2CppSystem.Collections.Generic.IList<>).IsAssignableFrom(g)
|
||||||
|
|| typeof(Il2CppSystem.Collections.Generic.HashSet<>).IsAssignableFrom(g);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -89,18 +90,20 @@ namespace Explorer
|
|||||||
{
|
{
|
||||||
if (obj == null) return null;
|
if (obj == null) return null;
|
||||||
|
|
||||||
|
// Need to use GetIl2CppType for Il2CppSystem Objects
|
||||||
if (obj is Il2CppSystem.Object ilObject)
|
if (obj is Il2CppSystem.Object ilObject)
|
||||||
{
|
{
|
||||||
var ilTypeName = ilObject.GetIl2CppType().AssemblyQualifiedName;
|
// Prevent weird behaviour when inspecting an Il2CppSystem.Type object.
|
||||||
|
if (ilObject is ILType)
|
||||||
if (Type.GetType(ilTypeName) is Type t && !t.FullName.Contains("System.RuntimeType"))
|
|
||||||
{
|
{
|
||||||
return t;
|
return typeof(ILType);
|
||||||
}
|
}
|
||||||
|
|
||||||
return ilObject.GetType();
|
// Get the System.Type using the qualified name, or fallback to GetType.
|
||||||
|
return Type.GetType(ilObject.GetIl2CppType().AssemblyQualifiedName) ?? obj.GetType();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// It's a normal object, this is fine
|
||||||
return obj.GetType();
|
return obj.GetType();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -109,12 +112,11 @@ namespace Explorer
|
|||||||
var list = new List<Type>();
|
var list = new List<Type>();
|
||||||
|
|
||||||
var type = GetActualType(obj);
|
var type = GetActualType(obj);
|
||||||
list.Add(type);
|
|
||||||
|
|
||||||
while (type.BaseType != null)
|
while (type != null)
|
||||||
{
|
{
|
||||||
type = type.BaseType;
|
|
||||||
list.Add(type);
|
list.Add(type);
|
||||||
|
type = type.BaseType;
|
||||||
}
|
}
|
||||||
|
|
||||||
return list.ToArray();
|
return list.ToArray();
|
||||||
|
@ -428,7 +428,7 @@ namespace Explorer
|
|||||||
{
|
{
|
||||||
var t = ReflectionHelpers.GetActualType(obj);
|
var t = ReflectionHelpers.GetActualType(obj);
|
||||||
|
|
||||||
if (!FilterName(t.FullName) || ReflectionHelpers.IsEnumerable(t) || ReflectionHelpers.IsCppList(t))
|
if (!FilterName(t.FullName) || ReflectionHelpers.IsEnumerable(t) || ReflectionHelpers.IsCppEnumerable(t))
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -14,6 +14,25 @@ namespace Explorer.Tests
|
|||||||
public static TestClass Instance => m_instance ?? (m_instance = new TestClass());
|
public static TestClass Instance => m_instance ?? (m_instance = new TestClass());
|
||||||
private static TestClass m_instance;
|
private static TestClass m_instance;
|
||||||
|
|
||||||
|
public TestClass()
|
||||||
|
{
|
||||||
|
ILHashSetTest = new Il2CppSystem.Collections.Generic.HashSet<string>();
|
||||||
|
ILHashSetTest.Add("1");
|
||||||
|
ILHashSetTest.Add("2");
|
||||||
|
ILHashSetTest.Add("3");
|
||||||
|
}
|
||||||
|
|
||||||
|
// test HashSets
|
||||||
|
|
||||||
|
public static HashSet<string> HashSetTest = new HashSet<string>
|
||||||
|
{
|
||||||
|
"One",
|
||||||
|
"Two",
|
||||||
|
"Three"
|
||||||
|
};
|
||||||
|
|
||||||
|
public static Il2CppSystem.Collections.Generic.HashSet<string> ILHashSetTest;
|
||||||
|
|
||||||
// Test indexed parameter
|
// Test indexed parameter
|
||||||
|
|
||||||
public string this[int arg0, string arg1]
|
public string this[int arg0, string arg1]
|
||||||
|
@ -32,10 +32,14 @@ namespace Explorer
|
|||||||
private UnityEngine.Object m_uObj;
|
private UnityEngine.Object m_uObj;
|
||||||
private Component m_component;
|
private Component m_component;
|
||||||
|
|
||||||
private static readonly HashSet<string> _memberBlacklist = new HashSet<string>
|
private static readonly HashSet<string> _typeAndMemberBlacklist = new HashSet<string>
|
||||||
{
|
{
|
||||||
// Causes a crash
|
// Causes a crash
|
||||||
"Type.DeclaringMethod",
|
"Type.DeclaringMethod",
|
||||||
|
};
|
||||||
|
|
||||||
|
private static readonly HashSet<string> _methodStartsWithBlacklist = new HashSet<string>
|
||||||
|
{
|
||||||
// Pointless (handled by Properties)
|
// Pointless (handled by Properties)
|
||||||
"get_",
|
"get_",
|
||||||
"set_"
|
"set_"
|
||||||
@ -157,7 +161,11 @@ namespace Explorer
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
// check blacklisted members
|
// check blacklisted members
|
||||||
if (_memberBlacklist.Any(it => member.Name.StartsWith(it)))
|
var name = member.DeclaringType.Name + "." + member.Name;
|
||||||
|
if (_typeAndMemberBlacklist.Any(it => it == name))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (_methodStartsWithBlacklist.Any(it => member.Name.StartsWith(it)))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// compare signature to already cached members
|
// compare signature to already cached members
|
||||||
|
Loading…
x
Reference in New Issue
Block a user