mirror of
https://github.com/GrahamKracker/UnityExplorer.git
synced 2025-07-14 23:56:36 +08:00
Compare commits
3 Commits
Author | SHA1 | Date | |
---|---|---|---|
9efb9581f5 | |||
f10a462b00 | |||
9072b16c5a |
@ -3,6 +3,7 @@ using System.IO;
|
|||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using IniParser;
|
using IniParser;
|
||||||
using IniParser.Parser;
|
using IniParser.Parser;
|
||||||
|
using UnityExplorer.UI;
|
||||||
|
|
||||||
namespace UnityExplorer.Config
|
namespace UnityExplorer.Config
|
||||||
{
|
{
|
||||||
@ -16,16 +17,20 @@ namespace UnityExplorer.Config
|
|||||||
static ExplorerConfig()
|
static ExplorerConfig()
|
||||||
{
|
{
|
||||||
_parser.Configuration.CommentString = "#";
|
_parser.Configuration.CommentString = "#";
|
||||||
|
|
||||||
|
PanelDragger.OnFinishResize += PanelDragger_OnFinishResize;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Actual configs
|
// Actual configs
|
||||||
public KeyCode Main_Menu_Toggle = KeyCode.F7;
|
public KeyCode Main_Menu_Toggle = KeyCode.F7;
|
||||||
public bool Force_Unlock_Mouse = true;
|
public bool Force_Unlock_Mouse = true;
|
||||||
public int Default_Page_Limit = 25;
|
public int Default_Page_Limit = 25;
|
||||||
public string Default_Output_Path = ExplorerCore.ExplorerFolder + @"\Output";
|
public string Default_Output_Path = Path.Combine(ExplorerCore.ExplorerFolder, "Output");
|
||||||
public bool Log_Unity_Debug = false;
|
public bool Log_Unity_Debug = false;
|
||||||
public bool Hide_On_Startup = false;
|
public bool Hide_On_Startup = false;
|
||||||
//public bool Save_Logs_To_Disk = true;
|
public string Window_Anchors = DEFAULT_WINDOW_ANCHORS;
|
||||||
|
|
||||||
|
private const string DEFAULT_WINDOW_ANCHORS = "0.25,0.1,0.78,0.95";
|
||||||
|
|
||||||
public static event Action OnConfigChanged;
|
public static event Action OnConfigChanged;
|
||||||
|
|
||||||
@ -75,9 +80,9 @@ namespace UnityExplorer.Config
|
|||||||
case nameof(Hide_On_Startup):
|
case nameof(Hide_On_Startup):
|
||||||
Instance.Hide_On_Startup = bool.Parse(config.Value);
|
Instance.Hide_On_Startup = bool.Parse(config.Value);
|
||||||
break;
|
break;
|
||||||
//case nameof(Save_Logs_To_Disk):
|
case nameof(Window_Anchors):
|
||||||
// Instance.Save_Logs_To_Disk = bool.Parse(config.Value);
|
Instance.Window_Anchors = config.Value;
|
||||||
// break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -97,12 +102,52 @@ namespace UnityExplorer.Config
|
|||||||
sec.AddKey(nameof(Log_Unity_Debug), Instance.Log_Unity_Debug.ToString());
|
sec.AddKey(nameof(Log_Unity_Debug), Instance.Log_Unity_Debug.ToString());
|
||||||
sec.AddKey(nameof(Default_Output_Path), Instance.Default_Output_Path);
|
sec.AddKey(nameof(Default_Output_Path), Instance.Default_Output_Path);
|
||||||
sec.AddKey(nameof(Hide_On_Startup), Instance.Hide_On_Startup.ToString());
|
sec.AddKey(nameof(Hide_On_Startup), Instance.Hide_On_Startup.ToString());
|
||||||
//sec.AddKey("Save_Logs_To_Disk", Instance.Save_Logs_To_Disk.ToString());
|
sec.AddKey(nameof(Window_Anchors), GetWindowAnchorsString());
|
||||||
|
|
||||||
if (!Directory.Exists(ExplorerCore.Loader.ConfigFolder))
|
if (!Directory.Exists(ExplorerCore.Loader.ConfigFolder))
|
||||||
Directory.CreateDirectory(ExplorerCore.Loader.ConfigFolder);
|
Directory.CreateDirectory(ExplorerCore.Loader.ConfigFolder);
|
||||||
|
|
||||||
File.WriteAllText(INI_PATH, data.ToString());
|
File.WriteAllText(INI_PATH, data.ToString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ============ Window Anchors specific stuff ============== //
|
||||||
|
|
||||||
|
private static void PanelDragger_OnFinishResize()
|
||||||
|
{
|
||||||
|
Instance.Window_Anchors = GetWindowAnchorsString();
|
||||||
|
SaveSettings();
|
||||||
|
}
|
||||||
|
|
||||||
|
internal Vector4 GetWindowAnchorsVector()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var split = Window_Anchors.Split(',');
|
||||||
|
Vector4 ret = Vector4.zero;
|
||||||
|
ret.x = float.Parse(split[0]);
|
||||||
|
ret.y = float.Parse(split[1]);
|
||||||
|
ret.z = float.Parse(split[2]);
|
||||||
|
ret.w = float.Parse(split[3]);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
Window_Anchors = DEFAULT_WINDOW_ANCHORS;
|
||||||
|
return GetWindowAnchorsVector();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static string GetWindowAnchorsString()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var rect = PanelDragger.Instance.Panel;
|
||||||
|
return $"{rect.anchorMin.x},{rect.anchorMin.y},{rect.anchorMax.x},{rect.anchorMax.y}";
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return DEFAULT_WINDOW_ANCHORS;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,7 @@ namespace UnityExplorer
|
|||||||
public class ExplorerCore
|
public class ExplorerCore
|
||||||
{
|
{
|
||||||
public const string NAME = "UnityExplorer";
|
public const string NAME = "UnityExplorer";
|
||||||
public const string VERSION = "3.2.1";
|
public const string VERSION = "3.2.2";
|
||||||
public const string AUTHOR = "Sinai";
|
public const string AUTHOR = "Sinai";
|
||||||
public const string GUID = "com.sinai.unityexplorer";
|
public const string GUID = "com.sinai.unityexplorer";
|
||||||
|
|
||||||
|
@ -11,20 +11,27 @@ namespace UnityExplorer.Helpers
|
|||||||
{
|
{
|
||||||
private static readonly Dictionary<string, Delegate> iCallCache = new Dictionary<string, Delegate>();
|
private static readonly Dictionary<string, Delegate> iCallCache = new Dictionary<string, Delegate>();
|
||||||
|
|
||||||
public static T GetICall<T>(string iCallName) where T : Delegate
|
/// <summary>
|
||||||
|
/// Helper to get and cache an iCall by providing the signature (eg. "UnityEngine.Resources::FindObjectsOfTypeAll").
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">The Type of Delegate to provide for the iCall.</typeparam>
|
||||||
|
/// <param name="signature">The signature of the iCall you want to get.</param>
|
||||||
|
/// <returns>The <typeparamref name="T"/> delegate if successful.</returns>
|
||||||
|
/// <exception cref="MissingMethodException">If the iCall could not be found.</exception>
|
||||||
|
public static T GetICall<T>(string signature) where T : Delegate
|
||||||
{
|
{
|
||||||
if (iCallCache.ContainsKey(iCallName))
|
if (iCallCache.ContainsKey(signature))
|
||||||
return (T)iCallCache[iCallName];
|
return (T)iCallCache[signature];
|
||||||
|
|
||||||
IntPtr ptr = il2cpp_resolve_icall(iCallName);
|
IntPtr ptr = il2cpp_resolve_icall(signature);
|
||||||
|
|
||||||
if (ptr == IntPtr.Zero)
|
if (ptr == IntPtr.Zero)
|
||||||
{
|
{
|
||||||
throw new MissingMethodException($"Could not resolve internal call by name '{iCallName}'!");
|
throw new MissingMethodException($"Could not resolve internal call by name '{signature}'!");
|
||||||
}
|
}
|
||||||
|
|
||||||
Delegate iCall = Marshal.GetDelegateForFunctionPointer(ptr, typeof(T));
|
Delegate iCall = Marshal.GetDelegateForFunctionPointer(ptr, typeof(T));
|
||||||
iCallCache.Add(iCallName, iCall);
|
iCallCache.Add(signature, iCall);
|
||||||
|
|
||||||
return (T)iCall;
|
return (T)iCall;
|
||||||
}
|
}
|
||||||
|
@ -21,26 +21,61 @@ namespace UnityExplorer.Helpers
|
|||||||
{
|
{
|
||||||
public static BF CommonFlags = BF.Public | BF.Instance | BF.NonPublic | BF.Static;
|
public static BF CommonFlags = BF.Public | BF.Instance | BF.NonPublic | BF.Static;
|
||||||
|
|
||||||
|
// cache for GetTypeByName
|
||||||
|
internal static readonly Dictionary<string, Type> s_typesByName = new Dictionary<string, Type>();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Find a <see cref="Type"/> in the current AppDomain whose <see cref="Type.FullName"/> matches the provided <paramref name="fullName"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="fullName">The <see cref="Type.FullName"/> you want to search for - case sensitive and full matches only.</param>
|
||||||
|
/// <returns>The Type if found, otherwise null.</returns>
|
||||||
public static Type GetTypeByName(string fullName)
|
public static Type GetTypeByName(string fullName)
|
||||||
{
|
{
|
||||||
foreach (var asm in AppDomain.CurrentDomain.GetAssemblies())
|
s_typesByName.TryGetValue(fullName, out Type ret);
|
||||||
{
|
|
||||||
foreach (var type in asm.TryGetTypes())
|
if (ret != null)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
foreach (var type in from asm in AppDomain.CurrentDomain.GetAssemblies()
|
||||||
|
from type in asm.TryGetTypes()
|
||||||
|
select type)
|
||||||
{
|
{
|
||||||
if (type.FullName == fullName)
|
if (type.FullName == fullName)
|
||||||
{
|
{
|
||||||
return type;
|
ret = type;
|
||||||
}
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
if (s_typesByName.ContainsKey(fullName))
|
||||||
|
s_typesByName[fullName] = ret;
|
||||||
|
else
|
||||||
|
s_typesByName.Add(fullName, ret);
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// cache for GetBaseTypes
|
||||||
|
internal static readonly Dictionary<string, Type[]> s_cachedTypeInheritance = new Dictionary<string, Type[]>();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get all base types of the provided Type, including itself.
|
||||||
|
/// </summary>
|
||||||
public static Type[] GetAllBaseTypes(object obj) => GetAllBaseTypes(GetActualType(obj));
|
public static Type[] GetAllBaseTypes(object obj) => GetAllBaseTypes(GetActualType(obj));
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get all base types of the provided Type, including itself.
|
||||||
|
/// </summary>
|
||||||
public static Type[] GetAllBaseTypes(Type type)
|
public static Type[] GetAllBaseTypes(Type type)
|
||||||
{
|
{
|
||||||
|
if (type == null)
|
||||||
|
throw new ArgumentNullException("type");
|
||||||
|
|
||||||
|
var name = type.AssemblyQualifiedName;
|
||||||
|
|
||||||
|
if (s_cachedTypeInheritance.TryGetValue(name, out Type[] ret))
|
||||||
|
return ret;
|
||||||
|
|
||||||
List<Type> list = new List<Type>();
|
List<Type> list = new List<Type>();
|
||||||
|
|
||||||
while (type != null)
|
while (type != null)
|
||||||
@ -49,9 +84,18 @@ namespace UnityExplorer.Helpers
|
|||||||
type = type.BaseType;
|
type = type.BaseType;
|
||||||
}
|
}
|
||||||
|
|
||||||
return list.ToArray();
|
ret = list.ToArray();
|
||||||
|
|
||||||
|
s_cachedTypeInheritance.Add(name, ret);
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Helper for IL2CPP to get the underlying true Type (Unhollowed) of the object.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="obj">The object to get the true Type for.</param>
|
||||||
|
/// <returns>The most accurate Type of the object which could be identified.</returns>
|
||||||
public static Type GetActualType(this object obj)
|
public static Type GetActualType(this object obj)
|
||||||
{
|
{
|
||||||
if (obj == null)
|
if (obj == null)
|
||||||
@ -59,9 +103,9 @@ namespace UnityExplorer.Helpers
|
|||||||
|
|
||||||
var type = obj.GetType();
|
var type = obj.GetType();
|
||||||
#if CPP
|
#if CPP
|
||||||
if (obj is Il2CppSystem.Object ilObject)
|
if (obj is Il2CppSystem.Object cppObject)
|
||||||
{
|
{
|
||||||
if (ilObject is CppType)
|
if (cppObject is CppType)
|
||||||
return typeof(CppType);
|
return typeof(CppType);
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(type.Namespace))
|
if (!string.IsNullOrEmpty(type.Namespace))
|
||||||
@ -69,22 +113,22 @@ namespace UnityExplorer.Helpers
|
|||||||
// Il2CppSystem-namespace objects should just return GetType,
|
// Il2CppSystem-namespace objects should just return GetType,
|
||||||
// because using GetIl2CppType returns the System namespace type instead.
|
// because using GetIl2CppType returns the System namespace type instead.
|
||||||
if (type.Namespace.StartsWith("System.") || type.Namespace.StartsWith("Il2CppSystem."))
|
if (type.Namespace.StartsWith("System.") || type.Namespace.StartsWith("Il2CppSystem."))
|
||||||
return ilObject.GetType();
|
return cppObject.GetType();
|
||||||
}
|
}
|
||||||
|
|
||||||
var il2cppType = ilObject.GetIl2CppType();
|
var cppType = cppObject.GetIl2CppType();
|
||||||
|
|
||||||
// check if type is injected
|
// check if type is injected
|
||||||
IntPtr classPtr = il2cpp_object_get_class(ilObject.Pointer);
|
IntPtr classPtr = il2cpp_object_get_class(cppObject.Pointer);
|
||||||
if (RuntimeSpecificsStore.IsInjected(classPtr))
|
if (RuntimeSpecificsStore.IsInjected(classPtr))
|
||||||
{
|
{
|
||||||
var typeByName = GetTypeByName(il2cppType.FullName);
|
var typeByName = GetTypeByName(cppType.FullName);
|
||||||
if (typeByName != null)
|
if (typeByName != null)
|
||||||
return typeByName;
|
return typeByName;
|
||||||
}
|
}
|
||||||
|
|
||||||
// this should be fine for all other il2cpp objects
|
// this should be fine for all other il2cpp objects
|
||||||
var getType = GetMonoType(il2cppType);
|
var getType = GetMonoType(cppType);
|
||||||
if (getType != null)
|
if (getType != null)
|
||||||
return getType;
|
return getType;
|
||||||
}
|
}
|
||||||
@ -93,41 +137,55 @@ namespace UnityExplorer.Helpers
|
|||||||
}
|
}
|
||||||
|
|
||||||
#if CPP
|
#if CPP
|
||||||
|
// caching for GetMonoType
|
||||||
private static readonly Dictionary<string, Type> Il2CppToMonoType = new Dictionary<string, Type>();
|
private static readonly Dictionary<string, Type> Il2CppToMonoType = new Dictionary<string, Type>();
|
||||||
|
|
||||||
|
// keep unobfuscated type name cache, used to display proper name.
|
||||||
|
internal static Dictionary<string, string> UnobfuscatedTypeNames = new Dictionary<string, string>();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Try to get the Mono (Unhollowed) Type representation of the provided <see cref="Il2CppSystem.Type"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="cppType">The Cpp Type you want to convert to Mono.</param>
|
||||||
|
/// <returns>The Mono Type if found, otherwise null.</returns>
|
||||||
public static Type GetMonoType(CppType cppType)
|
public static Type GetMonoType(CppType cppType)
|
||||||
{
|
{
|
||||||
if (Il2CppToMonoType.ContainsKey(cppType.AssemblyQualifiedName))
|
string name = cppType.AssemblyQualifiedName;
|
||||||
return Il2CppToMonoType[cppType.AssemblyQualifiedName];
|
|
||||||
|
|
||||||
var getType = Type.GetType(cppType.AssemblyQualifiedName);
|
if (Il2CppToMonoType.ContainsKey(name))
|
||||||
|
return Il2CppToMonoType[name];
|
||||||
|
|
||||||
if (getType != null)
|
Type ret = Type.GetType(name);
|
||||||
{
|
|
||||||
Il2CppToMonoType.Add(cppType.AssemblyQualifiedName, getType);
|
if (ret == null)
|
||||||
return getType;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
string baseName = cppType.FullName;
|
string baseName = cppType.FullName;
|
||||||
string baseAssembly = cppType.Assembly.GetName().name;
|
string baseAssembly = cppType.Assembly.GetName().name;
|
||||||
|
|
||||||
Type unhollowedType = AppDomain.CurrentDomain
|
ret = AppDomain.CurrentDomain
|
||||||
.GetAssemblies()
|
.GetAssemblies()
|
||||||
.FirstOrDefault(a => a.GetName().Name == baseAssembly)?
|
.FirstOrDefault(a
|
||||||
|
=> a.GetName().Name == baseAssembly)?
|
||||||
.TryGetTypes()
|
.TryGetTypes()
|
||||||
.FirstOrDefault(t =>
|
.FirstOrDefault(t
|
||||||
t.CustomAttributes.Any(ca
|
=> t.CustomAttributes.Any(ca
|
||||||
=> ca.AttributeType.Name == "ObfuscatedNameAttribute"
|
=> ca.AttributeType.Name == "ObfuscatedNameAttribute"
|
||||||
&& (string)ca.ConstructorArguments[0].Value == baseName));
|
&& (string)ca.ConstructorArguments[0].Value == baseName));
|
||||||
|
|
||||||
Il2CppToMonoType.Add(cppType.AssemblyQualifiedName, unhollowedType);
|
if (ret != null)
|
||||||
|
{
|
||||||
return unhollowedType;
|
// unobfuscated type was found, add to cache.
|
||||||
|
UnobfuscatedTypeNames.Add(cppType.FullName, ret.FullName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static readonly Dictionary<Type, IntPtr> CppClassPointers = new Dictionary<Type, IntPtr>();
|
Il2CppToMonoType.Add(name, ret);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
// cached class pointers for Il2CppCast
|
||||||
|
private static readonly Dictionary<string, IntPtr> CppClassPointers = new Dictionary<string, IntPtr>();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Attempt to cast the object to its underlying type.
|
/// Attempt to cast the object to its underlying type.
|
||||||
@ -161,7 +219,43 @@ namespace UnityExplorer.Helpers
|
|||||||
return Activator.CreateInstance(castTo, ilObj.Pointer);
|
return Activator.CreateInstance(castTo, ilObj.Pointer);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static readonly Dictionary<Type, MethodInfo> s_unboxMethods = new Dictionary<Type, MethodInfo>();
|
/// <summary>
|
||||||
|
/// Get the Il2Cpp Class Pointer for the provided Mono (Unhollowed) Type.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="type">The Mono/Unhollowed Type you want the Il2Cpp Class Pointer for.</param>
|
||||||
|
/// <returns>True if successful, false if not.</returns>
|
||||||
|
public static bool Il2CppTypeNotNull(Type type) => Il2CppTypeNotNull(type, out _);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get the Il2Cpp Class Pointer for the provided Mono (Unhollowed) Type.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="type">The Mono/Unhollowed Type you want the Il2Cpp Class Pointer for.</param>
|
||||||
|
/// <param name="il2cppPtr">The IntPtr for the Il2Cpp class, or IntPtr.Zero if not found.</param>
|
||||||
|
/// <returns>True if successful, false if not.</returns>
|
||||||
|
public static bool Il2CppTypeNotNull(Type type, out IntPtr il2cppPtr)
|
||||||
|
{
|
||||||
|
if (CppClassPointers.TryGetValue(type.AssemblyQualifiedName, out il2cppPtr))
|
||||||
|
return il2cppPtr != IntPtr.Zero;
|
||||||
|
|
||||||
|
il2cppPtr = (IntPtr)typeof(Il2CppClassPointerStore<>)
|
||||||
|
.MakeGenericType(new Type[] { type })
|
||||||
|
.GetField("NativeClassPtr", BF.Public | BF.Static)
|
||||||
|
.GetValue(null);
|
||||||
|
|
||||||
|
CppClassPointers.Add(type.AssemblyQualifiedName, il2cppPtr);
|
||||||
|
|
||||||
|
return il2cppPtr != IntPtr.Zero;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extern C++ methods
|
||||||
|
[DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
|
||||||
|
public static extern bool il2cpp_class_is_assignable_from(IntPtr klass, IntPtr oklass);
|
||||||
|
|
||||||
|
[DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
|
||||||
|
public static extern IntPtr il2cpp_object_get_class(IntPtr obj);
|
||||||
|
|
||||||
|
// cached il2cpp unbox methods
|
||||||
|
internal static readonly Dictionary<string, MethodInfo> s_unboxMethods = new Dictionary<string, MethodInfo>();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Attempt to unbox the object to the underlying struct type.
|
/// Attempt to unbox the object to the underlying struct type.
|
||||||
@ -184,41 +278,18 @@ namespace UnityExplorer.Helpers
|
|||||||
if (!(obj is Il2CppSystem.Object))
|
if (!(obj is Il2CppSystem.Object))
|
||||||
return obj;
|
return obj;
|
||||||
|
|
||||||
if (!s_unboxMethods.ContainsKey(type))
|
var name = type.AssemblyQualifiedName;
|
||||||
|
|
||||||
|
if (!s_unboxMethods.ContainsKey(name))
|
||||||
{
|
{
|
||||||
s_unboxMethods.Add(type, typeof(Il2CppObjectBase)
|
s_unboxMethods.Add(name, typeof(Il2CppObjectBase)
|
||||||
.GetMethod("Unbox")
|
.GetMethod("Unbox")
|
||||||
.MakeGenericMethod(type));
|
.MakeGenericMethod(type));
|
||||||
}
|
}
|
||||||
|
|
||||||
return s_unboxMethods[type].Invoke(obj, new object[0]);
|
return s_unboxMethods[name].Invoke(obj, new object[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool Il2CppTypeNotNull(Type type) => Il2CppTypeNotNull(type, out _);
|
|
||||||
|
|
||||||
public static bool Il2CppTypeNotNull(Type type, out IntPtr il2cppPtr)
|
|
||||||
{
|
|
||||||
if (!CppClassPointers.ContainsKey(type))
|
|
||||||
{
|
|
||||||
il2cppPtr = (IntPtr)typeof(Il2CppClassPointerStore<>)
|
|
||||||
.MakeGenericType(new Type[] { type })
|
|
||||||
.GetField("NativeClassPtr", BF.Public | BF.Static)
|
|
||||||
.GetValue(null);
|
|
||||||
|
|
||||||
CppClassPointers.Add(type, il2cppPtr);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
il2cppPtr = CppClassPointers[type];
|
|
||||||
|
|
||||||
return il2cppPtr != IntPtr.Zero;
|
|
||||||
}
|
|
||||||
|
|
||||||
[DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
|
|
||||||
public static extern bool il2cpp_class_is_assignable_from(IntPtr klass, IntPtr oklass);
|
|
||||||
|
|
||||||
[DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
|
|
||||||
public static extern IntPtr il2cpp_object_get_class(IntPtr obj);
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
public static IEnumerable<Type> TryGetTypes(this Assembly asm)
|
public static IEnumerable<Type> TryGetTypes(this Assembly asm)
|
||||||
@ -244,49 +315,17 @@ namespace UnityExplorer.Helpers
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#if CPP
|
// Helper for IL2CPP to check if a Type is assignable to IEnumerable
|
||||||
internal static void TryLoadGameModules()
|
|
||||||
{
|
|
||||||
LoadModule("Assembly-CSharp");
|
|
||||||
LoadModule("Assembly-CSharp-firstpass");
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool LoadModule(string module)
|
|
||||||
{
|
|
||||||
#if ML
|
|
||||||
var path = $@"MelonLoader\Managed\{module}.dll";
|
|
||||||
#else
|
|
||||||
var path = $@"BepInEx\unhollowed\{module}.dll";
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return LoadModuleInternal(path);
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static bool LoadModuleInternal(string fullPath)
|
|
||||||
{
|
|
||||||
if (!File.Exists(fullPath))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
Assembly.Load(File.ReadAllBytes(fullPath));
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
Console.WriteLine(e.GetType() + ", " + e.Message);
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
public static bool LoadModule(string module) => true;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if CPP
|
#if CPP
|
||||||
internal static IntPtr s_cppEnumerableClassPtr;
|
internal static IntPtr s_cppEnumerableClassPtr;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Check if the provided Type is assignable to IEnumerable.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="t">The Type to check</param>
|
||||||
|
/// <returns>True if the Type is assignable to IEnumerable, otherwise false.</returns>
|
||||||
public static bool IsEnumerable(Type t)
|
public static bool IsEnumerable(Type t)
|
||||||
{
|
{
|
||||||
if (typeof(IEnumerable).IsAssignableFrom(t))
|
if (typeof(IEnumerable).IsAssignableFrom(t))
|
||||||
@ -309,10 +348,17 @@ namespace UnityExplorer.Helpers
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Helper for IL2CPP to check if a Type is assignable to IDictionary
|
||||||
|
|
||||||
#if CPP
|
#if CPP
|
||||||
internal static IntPtr s_cppDictionaryClassPtr;
|
internal static IntPtr s_cppDictionaryClassPtr;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Check if the provided Type is assignable to IDictionary.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="t">The Type to check</param>
|
||||||
|
/// <returns>True if the Type is assignable to IDictionary, otherwise false.</returns>
|
||||||
public static bool IsDictionary(Type t)
|
public static bool IsDictionary(Type t)
|
||||||
{
|
{
|
||||||
if (typeof(IDictionary).IsAssignableFrom(t))
|
if (typeof(IDictionary).IsAssignableFrom(t))
|
||||||
@ -335,18 +381,68 @@ namespace UnityExplorer.Helpers
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Helper for IL2CPP to try to make sure the Unhollowed game assemblies are actually loaded.
|
||||||
|
|
||||||
|
#if CPP
|
||||||
|
internal static void TryLoadGameModules()
|
||||||
|
{
|
||||||
|
LoadModule("Assembly-CSharp");
|
||||||
|
LoadModule("Assembly-CSharp-firstpass");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool LoadModule(string module)
|
||||||
|
{
|
||||||
|
#if ML
|
||||||
|
var path = Path.Combine("MelonLoader", "Managed", $"{module}.dll");
|
||||||
|
#else
|
||||||
|
var path = Path.Combine("BepInEx", "unhollowed", $"{module}.dll");
|
||||||
|
#endif
|
||||||
|
return LoadModuleInternal(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static bool LoadModuleInternal(string fullPath)
|
||||||
|
{
|
||||||
|
if (!File.Exists(fullPath))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Assembly.Load(File.ReadAllBytes(fullPath));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Console.WriteLine(e.GetType() + ", " + e.Message);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
// For Mono, just return true and do nothing, Mono will sort it out itself.
|
||||||
|
public static bool LoadModule(string module) => true;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Helper to display a simple "{ExceptionType}: {Message}" of the exception, and optionally use the inner-most exception.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="e">The Exception to convert to string.</param>
|
||||||
|
/// <param name="innerMost">Should the inner-most Exception of the stack be used? If false, the Exception you provided will be used directly.</param>
|
||||||
|
/// <returns>The exception to string.</returns>
|
||||||
public static string ExceptionToString(Exception e, bool innerMost = false)
|
public static string ExceptionToString(Exception e, bool innerMost = false)
|
||||||
{
|
{
|
||||||
while (innerMost && e.InnerException != null)
|
if (innerMost)
|
||||||
|
{
|
||||||
|
while (e.InnerException != null)
|
||||||
{
|
{
|
||||||
#if CPP
|
#if CPP
|
||||||
if (e.InnerException is System.Runtime.CompilerServices.RuntimeWrappedException runtimeEx)
|
if (e.InnerException is System.Runtime.CompilerServices.RuntimeWrappedException)
|
||||||
break;
|
break;
|
||||||
#endif
|
#endif
|
||||||
e = e.InnerException;
|
e = e.InnerException;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return e.GetType() + ", " + e.Message;
|
return $"{e.GetType()}: {e.Message}";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -381,7 +381,7 @@ namespace UnityExplorer.Inspectors.Reflection
|
|||||||
argLabelLayout.minHeight = 25;
|
argLabelLayout.minHeight = 25;
|
||||||
var argText = argLabelObj.GetComponent<Text>();
|
var argText = argLabelObj.GetComponent<Text>();
|
||||||
var argTypeTxt = UISyntaxHighlight.ParseFullSyntax(arg.ParameterType, false);
|
var argTypeTxt = UISyntaxHighlight.ParseFullSyntax(arg.ParameterType, false);
|
||||||
argText.text = $"{argTypeTxt} <color={UISyntaxHighlight.Local}>{arg.Name}</color>";
|
argText.text = $"{argTypeTxt} <color={UISyntaxHighlight.LOCAL_ARG}>{arg.Name}</color>";
|
||||||
|
|
||||||
var argInputObj = UIFactory.CreateInputField(rowObj, 14, (int)TextAnchor.MiddleLeft, 1);
|
var argInputObj = UIFactory.CreateInputField(rowObj, 14, (int)TextAnchor.MiddleLeft, 1);
|
||||||
var argInputLayout = argInputObj.AddComponent<LayoutElement>();
|
var argInputLayout = argInputObj.AddComponent<LayoutElement>();
|
||||||
|
@ -168,7 +168,7 @@ namespace UnityExplorer.Inspectors.Reflection
|
|||||||
//var argLayout = argLabelObj.AddComponent<LayoutElement>();
|
//var argLayout = argLabelObj.AddComponent<LayoutElement>();
|
||||||
//argLayout.minWidth = 20;
|
//argLayout.minWidth = 20;
|
||||||
var argText = argLabelObj.GetComponent<Text>();
|
var argText = argLabelObj.GetComponent<Text>();
|
||||||
argText.text = $"{constrainTxt} <color={UISyntaxHighlight.Enum}>{arg.Name}</color>";
|
argText.text = $"{constrainTxt} <color={UISyntaxHighlight.CONST_VAR}>{arg.Name}</color>";
|
||||||
|
|
||||||
var argInputObj = UIFactory.CreateInputField(rowObj, 14, (int)TextAnchor.MiddleLeft, 1);
|
var argInputObj = UIFactory.CreateInputField(rowObj, 14, (int)TextAnchor.MiddleLeft, 1);
|
||||||
var argInputLayout = argInputObj.AddComponent<LayoutElement>();
|
var argInputLayout = argInputObj.AddComponent<LayoutElement>();
|
||||||
|
@ -194,6 +194,7 @@ namespace UnityExplorer.Inspectors.Reflection
|
|||||||
|
|
||||||
string label;
|
string label;
|
||||||
|
|
||||||
|
// Two dirty fixes for TextAsset and EventSystem, which can have very long ToString results.
|
||||||
if (Value is TextAsset textAsset)
|
if (Value is TextAsset textAsset)
|
||||||
{
|
{
|
||||||
label = textAsset.text;
|
label = textAsset.text;
|
||||||
@ -207,7 +208,7 @@ namespace UnityExplorer.Inspectors.Reflection
|
|||||||
{
|
{
|
||||||
label = m_richValueType;
|
label = m_richValueType;
|
||||||
}
|
}
|
||||||
else
|
else // For everything else...
|
||||||
{
|
{
|
||||||
if (!m_gotToStringMethods)
|
if (!m_gotToStringMethods)
|
||||||
{
|
{
|
||||||
@ -233,17 +234,24 @@ namespace UnityExplorer.Inspectors.Reflection
|
|||||||
else
|
else
|
||||||
toString = (string)m_toStringMethod.Invoke(Value, new object[0]);
|
toString = (string)m_toStringMethod.Invoke(Value, new object[0]);
|
||||||
|
|
||||||
var fullnametemp = valueType.ToString();
|
string typeName = valueType.FullName;
|
||||||
if (fullnametemp.StartsWith("Il2CppSystem"))
|
if (typeName.StartsWith("Il2CppSystem."))
|
||||||
fullnametemp = fullnametemp.Substring(6, fullnametemp.Length - 6);
|
typeName = typeName.Substring(6, typeName.Length - 6);
|
||||||
|
#if CPP
|
||||||
|
var cppType = UnhollowerRuntimeLib.Il2CppType.From(valueType);
|
||||||
|
if (cppType != null && ReflectionHelpers.UnobfuscatedTypeNames.ContainsKey(cppType.FullName))
|
||||||
|
{
|
||||||
|
typeName = ReflectionHelpers.UnobfuscatedTypeNames[cppType.FullName];
|
||||||
|
toString = toString.Replace(cppType.FullName, typeName);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
var temp = toString.Replace(fullnametemp, "").Trim();
|
// If the ToString is just the type name, use our syntax highlighted type name instead.
|
||||||
|
if (toString == typeName)
|
||||||
if (string.IsNullOrEmpty(temp))
|
|
||||||
{
|
{
|
||||||
label = m_richValueType;
|
label = m_richValueType;
|
||||||
}
|
}
|
||||||
else
|
else // Otherwise, parse the result and put our highlighted name in.
|
||||||
{
|
{
|
||||||
if (toString.Length > 200)
|
if (toString.Length > 200)
|
||||||
toString = toString.Substring(0, 200) + "...";
|
toString = toString.Substring(0, 200) + "...";
|
||||||
|
@ -155,8 +155,11 @@ namespace UnityExplorer.UI
|
|||||||
MainPanel = UIFactory.CreatePanel(UIManager.CanvasRoot, "MainMenu", out GameObject content);
|
MainPanel = UIFactory.CreatePanel(UIManager.CanvasRoot, "MainMenu", out GameObject content);
|
||||||
|
|
||||||
RectTransform panelRect = MainPanel.GetComponent<RectTransform>();
|
RectTransform panelRect = MainPanel.GetComponent<RectTransform>();
|
||||||
panelRect.anchorMin = new Vector2(0.25f, 0.1f);
|
//panelRect.anchorMin = new Vector2(0.25f, 0.1f);
|
||||||
panelRect.anchorMax = new Vector2(0.78f, 0.95f);
|
//panelRect.anchorMax = new Vector2(0.78f, 0.95f);
|
||||||
|
var anchors = ExplorerConfig.Instance.GetWindowAnchorsVector();
|
||||||
|
panelRect.anchorMin = new Vector2(anchors.x, anchors.y);
|
||||||
|
panelRect.anchorMax = new Vector2(anchors.z, anchors.w);
|
||||||
|
|
||||||
MainPanel.AddComponent<Mask>();
|
MainPanel.AddComponent<Mask>();
|
||||||
|
|
||||||
|
@ -8,36 +8,36 @@ namespace UnityExplorer.UI
|
|||||||
{
|
{
|
||||||
public class UISyntaxHighlight
|
public class UISyntaxHighlight
|
||||||
{
|
{
|
||||||
public const string Field_Static = "#8d8dc6";
|
public const string FIELD_STATIC = "#8d8dc6";
|
||||||
public const string Field_Instance = "#c266ff";
|
public const string FIELD_INSTANCE = "#c266ff";
|
||||||
|
|
||||||
public const string Method_Static = "#b55b02";
|
public const string METHOD_STATIC = "#b55b02";
|
||||||
public const string Method_Instance = "#ff8000";
|
public const string METHOD_INSTANCE = "#ff8000";
|
||||||
|
|
||||||
public const string Prop_Static = "#588075";
|
public const string PROP_STATIC = "#588075";
|
||||||
public const string Prop_Instance = "#55a38e";
|
public const string PROP_INSTANCE = "#55a38e";
|
||||||
|
|
||||||
public const string Class_Static = "#3a8d71";
|
public const string CLASS_STATIC = "#3a8d71";
|
||||||
public const string Class_Instance = "#2df7b2";
|
public const string CLASS_INSTANCE = "#2df7b2";
|
||||||
|
|
||||||
public const string Local = "#a6e9e9";
|
public const string CLASS_STRUCT = "#0fba3a";
|
||||||
|
|
||||||
public const string StructGreen = "#0fba3a";
|
public const string LOCAL_ARG = "#a6e9e9";
|
||||||
|
|
||||||
public static string Enum = "#92c470";
|
public static string CONST_VAR = "#92c470";
|
||||||
|
|
||||||
internal static readonly Color s_silver = new Color(0.66f, 0.66f, 0.66f);
|
internal static readonly Color s_silver = new Color(0.66f, 0.66f, 0.66f);
|
||||||
|
|
||||||
internal static string GetClassColor(Type type)
|
internal static string GetClassColor(Type type)
|
||||||
{
|
{
|
||||||
if (type.IsAbstract && type.IsSealed)
|
if (type.IsAbstract && type.IsSealed)
|
||||||
return Class_Static;
|
return CLASS_STATIC;
|
||||||
else if (type.IsEnum || type.IsGenericParameter)
|
else if (type.IsEnum || type.IsGenericParameter)
|
||||||
return Enum;
|
return CONST_VAR;
|
||||||
else if (type.IsValueType)
|
else if (type.IsValueType)
|
||||||
return StructGreen;
|
return CLASS_STRUCT;
|
||||||
else
|
else
|
||||||
return Class_Instance;
|
return CLASS_INSTANCE;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static string ParseFullSyntax(Type type, bool includeNamespace, MemberInfo memberInfo = null)
|
public static string ParseFullSyntax(Type type, bool includeNamespace, MemberInfo memberInfo = null)
|
||||||
@ -49,7 +49,7 @@ namespace UnityExplorer.UI
|
|||||||
|
|
||||||
if (type.IsGenericParameter || (type.HasElementType && type.GetElementType().IsGenericParameter))
|
if (type.IsGenericParameter || (type.HasElementType && type.GetElementType().IsGenericParameter))
|
||||||
{
|
{
|
||||||
ret = $"<color={Enum}>{type.Name}</color>";
|
ret = $"<color={CONST_VAR}>{type.Name}</color>";
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -134,7 +134,7 @@ namespace UnityExplorer.UI
|
|||||||
|
|
||||||
if (allGeneric)
|
if (allGeneric)
|
||||||
{
|
{
|
||||||
args += $"<color={Enum}>{arg.Name}</color>";
|
args += $"<color={CONST_VAR}>{arg.Name}</color>";
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -153,30 +153,30 @@ namespace UnityExplorer.UI
|
|||||||
if (fi.IsStatic)
|
if (fi.IsStatic)
|
||||||
{
|
{
|
||||||
isStatic = true;
|
isStatic = true;
|
||||||
memberColor = Field_Static;
|
memberColor = FIELD_STATIC;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
memberColor = Field_Instance;
|
memberColor = FIELD_INSTANCE;
|
||||||
}
|
}
|
||||||
else if (memberInfo is MethodInfo mi)
|
else if (memberInfo is MethodInfo mi)
|
||||||
{
|
{
|
||||||
if (mi.IsStatic)
|
if (mi.IsStatic)
|
||||||
{
|
{
|
||||||
isStatic = true;
|
isStatic = true;
|
||||||
memberColor = Method_Static;
|
memberColor = METHOD_STATIC;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
memberColor = Method_Instance;
|
memberColor = METHOD_INSTANCE;
|
||||||
}
|
}
|
||||||
else if (memberInfo is PropertyInfo pi)
|
else if (memberInfo is PropertyInfo pi)
|
||||||
{
|
{
|
||||||
if (pi.GetAccessors(true)[0].IsStatic)
|
if (pi.GetAccessors(true)[0].IsStatic)
|
||||||
{
|
{
|
||||||
isStatic = true;
|
isStatic = true;
|
||||||
memberColor = Prop_Static;
|
memberColor = PROP_STATIC;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
memberColor = Prop_Instance;
|
memberColor = PROP_INSTANCE;
|
||||||
}
|
}
|
||||||
return memberColor;
|
return memberColor;
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user