Compare commits

...

3 Commits
3.2.1 ... 3.2.2

9 changed files with 316 additions and 157 deletions

View File

@ -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;
}
}
}
}

View File

@ -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";

View File

@ -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;
}

View File

@ -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}";
}
}
}

View File

@ -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>();

View File

@ -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>();

View File

@ -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) + "...";

View File

@ -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>();

View File

@ -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;
}