mirror of
https://github.com/GrahamKracker/UnityExplorer.git
synced 2025-07-13 23:36:35 +08:00
Compare commits
3 Commits
Author | SHA1 | Date | |
---|---|---|---|
9efb9581f5 | |||
f10a462b00 | |||
9072b16c5a |
@ -3,6 +3,7 @@ using System.IO;
|
||||
using UnityEngine;
|
||||
using IniParser;
|
||||
using IniParser.Parser;
|
||||
using UnityExplorer.UI;
|
||||
|
||||
namespace UnityExplorer.Config
|
||||
{
|
||||
@ -16,16 +17,20 @@ namespace UnityExplorer.Config
|
||||
static ExplorerConfig()
|
||||
{
|
||||
_parser.Configuration.CommentString = "#";
|
||||
|
||||
PanelDragger.OnFinishResize += PanelDragger_OnFinishResize;
|
||||
}
|
||||
|
||||
// Actual configs
|
||||
public KeyCode Main_Menu_Toggle = KeyCode.F7;
|
||||
public bool Force_Unlock_Mouse = true;
|
||||
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 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;
|
||||
|
||||
@ -75,9 +80,9 @@ namespace UnityExplorer.Config
|
||||
case nameof(Hide_On_Startup):
|
||||
Instance.Hide_On_Startup = bool.Parse(config.Value);
|
||||
break;
|
||||
//case nameof(Save_Logs_To_Disk):
|
||||
// Instance.Save_Logs_To_Disk = bool.Parse(config.Value);
|
||||
// break;
|
||||
case nameof(Window_Anchors):
|
||||
Instance.Window_Anchors = config.Value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@ -97,12 +102,52 @@ namespace UnityExplorer.Config
|
||||
sec.AddKey(nameof(Log_Unity_Debug), Instance.Log_Unity_Debug.ToString());
|
||||
sec.AddKey(nameof(Default_Output_Path), Instance.Default_Output_Path);
|
||||
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))
|
||||
Directory.CreateDirectory(ExplorerCore.Loader.ConfigFolder);
|
||||
|
||||
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 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 GUID = "com.sinai.unityexplorer";
|
||||
|
||||
|
@ -11,20 +11,27 @@ namespace UnityExplorer.Helpers
|
||||
{
|
||||
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))
|
||||
return (T)iCallCache[iCallName];
|
||||
if (iCallCache.ContainsKey(signature))
|
||||
return (T)iCallCache[signature];
|
||||
|
||||
IntPtr ptr = il2cpp_resolve_icall(iCallName);
|
||||
IntPtr ptr = il2cpp_resolve_icall(signature);
|
||||
|
||||
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));
|
||||
iCallCache.Add(iCallName, iCall);
|
||||
iCallCache.Add(signature, iCall);
|
||||
|
||||
return (T)iCall;
|
||||
}
|
||||
|
@ -21,26 +21,61 @@ namespace UnityExplorer.Helpers
|
||||
{
|
||||
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)
|
||||
{
|
||||
foreach (var asm in AppDomain.CurrentDomain.GetAssemblies())
|
||||
s_typesByName.TryGetValue(fullName, out Type ret);
|
||||
|
||||
if (ret != null)
|
||||
return ret;
|
||||
|
||||
foreach (var type in from asm in AppDomain.CurrentDomain.GetAssemblies()
|
||||
from type in asm.TryGetTypes()
|
||||
select type)
|
||||
{
|
||||
foreach (var type in asm.TryGetTypes())
|
||||
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));
|
||||
|
||||
/// <summary>
|
||||
/// Get all base types of the provided Type, including itself.
|
||||
/// </summary>
|
||||
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>();
|
||||
|
||||
while (type != null)
|
||||
@ -49,9 +84,18 @@ namespace UnityExplorer.Helpers
|
||||
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)
|
||||
{
|
||||
if (obj == null)
|
||||
@ -59,9 +103,9 @@ namespace UnityExplorer.Helpers
|
||||
|
||||
var type = obj.GetType();
|
||||
#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);
|
||||
|
||||
if (!string.IsNullOrEmpty(type.Namespace))
|
||||
@ -69,22 +113,22 @@ namespace UnityExplorer.Helpers
|
||||
// Il2CppSystem-namespace objects should just return GetType,
|
||||
// because using GetIl2CppType returns the System namespace type instead.
|
||||
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
|
||||
IntPtr classPtr = il2cpp_object_get_class(ilObject.Pointer);
|
||||
IntPtr classPtr = il2cpp_object_get_class(cppObject.Pointer);
|
||||
if (RuntimeSpecificsStore.IsInjected(classPtr))
|
||||
{
|
||||
var typeByName = GetTypeByName(il2cppType.FullName);
|
||||
var typeByName = GetTypeByName(cppType.FullName);
|
||||
if (typeByName != null)
|
||||
return typeByName;
|
||||
}
|
||||
|
||||
// this should be fine for all other il2cpp objects
|
||||
var getType = GetMonoType(il2cppType);
|
||||
var getType = GetMonoType(cppType);
|
||||
if (getType != null)
|
||||
return getType;
|
||||
}
|
||||
@ -93,41 +137,55 @@ namespace UnityExplorer.Helpers
|
||||
}
|
||||
|
||||
#if CPP
|
||||
// caching for GetMonoType
|
||||
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)
|
||||
{
|
||||
if (Il2CppToMonoType.ContainsKey(cppType.AssemblyQualifiedName))
|
||||
return Il2CppToMonoType[cppType.AssemblyQualifiedName];
|
||||
string name = cppType.AssemblyQualifiedName;
|
||||
|
||||
var getType = Type.GetType(cppType.AssemblyQualifiedName);
|
||||
if (Il2CppToMonoType.ContainsKey(name))
|
||||
return Il2CppToMonoType[name];
|
||||
|
||||
Type ret = Type.GetType(name);
|
||||
|
||||
if (getType != null)
|
||||
{
|
||||
Il2CppToMonoType.Add(cppType.AssemblyQualifiedName, getType);
|
||||
return getType;
|
||||
}
|
||||
else
|
||||
if (ret == null)
|
||||
{
|
||||
string baseName = cppType.FullName;
|
||||
string baseAssembly = cppType.Assembly.GetName().name;
|
||||
|
||||
Type unhollowedType = AppDomain.CurrentDomain
|
||||
ret = AppDomain.CurrentDomain
|
||||
.GetAssemblies()
|
||||
.FirstOrDefault(a => a.GetName().Name == baseAssembly)?
|
||||
.FirstOrDefault(a
|
||||
=> a.GetName().Name == baseAssembly)?
|
||||
.TryGetTypes()
|
||||
.FirstOrDefault(t =>
|
||||
t.CustomAttributes.Any(ca
|
||||
.FirstOrDefault(t
|
||||
=> t.CustomAttributes.Any(ca
|
||||
=> ca.AttributeType.Name == "ObfuscatedNameAttribute"
|
||||
&& (string)ca.ConstructorArguments[0].Value == baseName));
|
||||
|
||||
Il2CppToMonoType.Add(cppType.AssemblyQualifiedName, unhollowedType);
|
||||
|
||||
return unhollowedType;
|
||||
if (ret != null)
|
||||
{
|
||||
// unobfuscated type was found, add to cache.
|
||||
UnobfuscatedTypeNames.Add(cppType.FullName, ret.FullName);
|
||||
}
|
||||
}
|
||||
|
||||
Il2CppToMonoType.Add(name, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
private static readonly Dictionary<Type, IntPtr> CppClassPointers = new Dictionary<Type, IntPtr>();
|
||||
// cached class pointers for Il2CppCast
|
||||
private static readonly Dictionary<string, IntPtr> CppClassPointers = new Dictionary<string, IntPtr>();
|
||||
|
||||
/// <summary>
|
||||
/// Attempt to cast the object to its underlying type.
|
||||
@ -161,7 +219,43 @@ namespace UnityExplorer.Helpers
|
||||
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>
|
||||
/// Attempt to unbox the object to the underlying struct type.
|
||||
@ -184,41 +278,18 @@ namespace UnityExplorer.Helpers
|
||||
if (!(obj is Il2CppSystem.Object))
|
||||
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")
|
||||
.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
|
||||
|
||||
public static IEnumerable<Type> TryGetTypes(this Assembly asm)
|
||||
@ -244,49 +315,17 @@ namespace UnityExplorer.Helpers
|
||||
}
|
||||
}
|
||||
|
||||
#if CPP
|
||||
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
|
||||
// Helper for IL2CPP to check if a Type is assignable to IEnumerable
|
||||
|
||||
#if CPP
|
||||
internal static IntPtr s_cppEnumerableClassPtr;
|
||||
#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)
|
||||
{
|
||||
if (typeof(IEnumerable).IsAssignableFrom(t))
|
||||
@ -297,8 +336,8 @@ namespace UnityExplorer.Helpers
|
||||
if (s_cppEnumerableClassPtr == IntPtr.Zero)
|
||||
Il2CppTypeNotNull(typeof(Il2CppSystem.Collections.IEnumerable), out s_cppEnumerableClassPtr);
|
||||
|
||||
if (s_cppEnumerableClassPtr != IntPtr.Zero
|
||||
&& Il2CppTypeNotNull(t, out IntPtr classPtr)
|
||||
if (s_cppEnumerableClassPtr != IntPtr.Zero
|
||||
&& Il2CppTypeNotNull(t, out IntPtr classPtr)
|
||||
&& il2cpp_class_is_assignable_from(s_cppEnumerableClassPtr, classPtr))
|
||||
{
|
||||
return true;
|
||||
@ -309,10 +348,17 @@ namespace UnityExplorer.Helpers
|
||||
return false;
|
||||
}
|
||||
|
||||
// Helper for IL2CPP to check if a Type is assignable to IDictionary
|
||||
|
||||
#if CPP
|
||||
internal static IntPtr s_cppDictionaryClassPtr;
|
||||
#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)
|
||||
{
|
||||
if (typeof(IDictionary).IsAssignableFrom(t))
|
||||
@ -335,18 +381,68 @@ namespace UnityExplorer.Helpers
|
||||
return false;
|
||||
}
|
||||
|
||||
public static string ExceptionToString(Exception e, bool innerMost = false)
|
||||
{
|
||||
while (innerMost && e.InnerException != null)
|
||||
{
|
||||
// Helper for IL2CPP to try to make sure the Unhollowed game assemblies are actually loaded.
|
||||
|
||||
#if CPP
|
||||
if (e.InnerException is System.Runtime.CompilerServices.RuntimeWrappedException runtimeEx)
|
||||
break;
|
||||
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
|
||||
e = e.InnerException;
|
||||
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 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)
|
||||
{
|
||||
if (innerMost)
|
||||
{
|
||||
while (e.InnerException != null)
|
||||
{
|
||||
#if CPP
|
||||
if (e.InnerException is System.Runtime.CompilerServices.RuntimeWrappedException)
|
||||
break;
|
||||
#endif
|
||||
e = e.InnerException;
|
||||
}
|
||||
}
|
||||
|
||||
return $"{e.GetType()}: {e.Message}";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -381,7 +381,7 @@ namespace UnityExplorer.Inspectors.Reflection
|
||||
argLabelLayout.minHeight = 25;
|
||||
var argText = argLabelObj.GetComponent<Text>();
|
||||
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 argInputLayout = argInputObj.AddComponent<LayoutElement>();
|
||||
|
@ -168,7 +168,7 @@ namespace UnityExplorer.Inspectors.Reflection
|
||||
//var argLayout = argLabelObj.AddComponent<LayoutElement>();
|
||||
//argLayout.minWidth = 20;
|
||||
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 argInputLayout = argInputObj.AddComponent<LayoutElement>();
|
||||
|
@ -194,6 +194,7 @@ namespace UnityExplorer.Inspectors.Reflection
|
||||
|
||||
string label;
|
||||
|
||||
// Two dirty fixes for TextAsset and EventSystem, which can have very long ToString results.
|
||||
if (Value is TextAsset textAsset)
|
||||
{
|
||||
label = textAsset.text;
|
||||
@ -207,7 +208,7 @@ namespace UnityExplorer.Inspectors.Reflection
|
||||
{
|
||||
label = m_richValueType;
|
||||
}
|
||||
else
|
||||
else // For everything else...
|
||||
{
|
||||
if (!m_gotToStringMethods)
|
||||
{
|
||||
@ -233,17 +234,24 @@ namespace UnityExplorer.Inspectors.Reflection
|
||||
else
|
||||
toString = (string)m_toStringMethod.Invoke(Value, new object[0]);
|
||||
|
||||
var fullnametemp = valueType.ToString();
|
||||
if (fullnametemp.StartsWith("Il2CppSystem"))
|
||||
fullnametemp = fullnametemp.Substring(6, fullnametemp.Length - 6);
|
||||
string typeName = valueType.FullName;
|
||||
if (typeName.StartsWith("Il2CppSystem."))
|
||||
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 (string.IsNullOrEmpty(temp))
|
||||
// If the ToString is just the type name, use our syntax highlighted type name instead.
|
||||
if (toString == typeName)
|
||||
{
|
||||
label = m_richValueType;
|
||||
}
|
||||
else
|
||||
else // Otherwise, parse the result and put our highlighted name in.
|
||||
{
|
||||
if (toString.Length > 200)
|
||||
toString = toString.Substring(0, 200) + "...";
|
||||
|
@ -155,8 +155,11 @@ namespace UnityExplorer.UI
|
||||
MainPanel = UIFactory.CreatePanel(UIManager.CanvasRoot, "MainMenu", out GameObject content);
|
||||
|
||||
RectTransform panelRect = MainPanel.GetComponent<RectTransform>();
|
||||
panelRect.anchorMin = new Vector2(0.25f, 0.1f);
|
||||
panelRect.anchorMax = new Vector2(0.78f, 0.95f);
|
||||
//panelRect.anchorMin = new Vector2(0.25f, 0.1f);
|
||||
//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>();
|
||||
|
||||
|
@ -8,36 +8,36 @@ namespace UnityExplorer.UI
|
||||
{
|
||||
public class UISyntaxHighlight
|
||||
{
|
||||
public const string Field_Static = "#8d8dc6";
|
||||
public const string Field_Instance = "#c266ff";
|
||||
public const string FIELD_STATIC = "#8d8dc6";
|
||||
public const string FIELD_INSTANCE = "#c266ff";
|
||||
|
||||
public const string Method_Static = "#b55b02";
|
||||
public const string Method_Instance = "#ff8000";
|
||||
public const string METHOD_STATIC = "#b55b02";
|
||||
public const string METHOD_INSTANCE = "#ff8000";
|
||||
|
||||
public const string Prop_Static = "#588075";
|
||||
public const string Prop_Instance = "#55a38e";
|
||||
public const string PROP_STATIC = "#588075";
|
||||
public const string PROP_INSTANCE = "#55a38e";
|
||||
|
||||
public const string Class_Static = "#3a8d71";
|
||||
public const string Class_Instance = "#2df7b2";
|
||||
public const string CLASS_STATIC = "#3a8d71";
|
||||
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 string GetClassColor(Type type)
|
||||
{
|
||||
if (type.IsAbstract && type.IsSealed)
|
||||
return Class_Static;
|
||||
return CLASS_STATIC;
|
||||
else if (type.IsEnum || type.IsGenericParameter)
|
||||
return Enum;
|
||||
return CONST_VAR;
|
||||
else if (type.IsValueType)
|
||||
return StructGreen;
|
||||
return CLASS_STRUCT;
|
||||
else
|
||||
return Class_Instance;
|
||||
return CLASS_INSTANCE;
|
||||
}
|
||||
|
||||
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))
|
||||
{
|
||||
ret = $"<color={Enum}>{type.Name}</color>";
|
||||
ret = $"<color={CONST_VAR}>{type.Name}</color>";
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -134,7 +134,7 @@ namespace UnityExplorer.UI
|
||||
|
||||
if (allGeneric)
|
||||
{
|
||||
args += $"<color={Enum}>{arg.Name}</color>";
|
||||
args += $"<color={CONST_VAR}>{arg.Name}</color>";
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -153,30 +153,30 @@ namespace UnityExplorer.UI
|
||||
if (fi.IsStatic)
|
||||
{
|
||||
isStatic = true;
|
||||
memberColor = Field_Static;
|
||||
memberColor = FIELD_STATIC;
|
||||
}
|
||||
else
|
||||
memberColor = Field_Instance;
|
||||
memberColor = FIELD_INSTANCE;
|
||||
}
|
||||
else if (memberInfo is MethodInfo mi)
|
||||
{
|
||||
if (mi.IsStatic)
|
||||
{
|
||||
isStatic = true;
|
||||
memberColor = Method_Static;
|
||||
memberColor = METHOD_STATIC;
|
||||
}
|
||||
else
|
||||
memberColor = Method_Instance;
|
||||
memberColor = METHOD_INSTANCE;
|
||||
}
|
||||
else if (memberInfo is PropertyInfo pi)
|
||||
{
|
||||
if (pi.GetAccessors(true)[0].IsStatic)
|
||||
{
|
||||
isStatic = true;
|
||||
memberColor = Prop_Static;
|
||||
memberColor = PROP_STATIC;
|
||||
}
|
||||
else
|
||||
memberColor = Prop_Instance;
|
||||
memberColor = PROP_INSTANCE;
|
||||
}
|
||||
return memberColor;
|
||||
}
|
||||
|
Reference in New Issue
Block a user