2020-08-22 00:16:05 +10:00
|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.Linq;
|
|
|
|
|
using System.Reflection;
|
|
|
|
|
using System.Text;
|
|
|
|
|
using System.Threading.Tasks;
|
|
|
|
|
using MelonLoader;
|
|
|
|
|
using UnhollowerBaseLib;
|
|
|
|
|
using UnityEngine;
|
|
|
|
|
|
|
|
|
|
namespace Explorer
|
|
|
|
|
{
|
2020-08-29 21:15:54 +10:00
|
|
|
|
public abstract class CacheObjectBase
|
2020-08-22 00:16:05 +10:00
|
|
|
|
{
|
|
|
|
|
public object Value;
|
|
|
|
|
public string ValueType;
|
|
|
|
|
|
|
|
|
|
// Reflection window only
|
|
|
|
|
public MemberInfo MemberInfo { get; set; }
|
|
|
|
|
public Type DeclaringType { get; set; }
|
|
|
|
|
public object DeclaringInstance { get; set; }
|
2020-08-31 23:28:44 +10:00
|
|
|
|
|
|
|
|
|
public string RichTextName
|
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
if (m_richTextName == null)
|
|
|
|
|
{
|
|
|
|
|
GetRichTextName();
|
|
|
|
|
}
|
|
|
|
|
return m_richTextName;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
private string m_richTextName;
|
|
|
|
|
|
2020-08-22 17:17:11 +10:00
|
|
|
|
public string ReflectionException;
|
2020-08-22 00:16:05 +10:00
|
|
|
|
|
2020-08-24 01:42:19 +10:00
|
|
|
|
public bool CanWrite
|
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
if (MemberInfo is FieldInfo fi)
|
|
|
|
|
{
|
|
|
|
|
return !(fi.IsLiteral && !fi.IsInitOnly);
|
|
|
|
|
}
|
|
|
|
|
else if (MemberInfo is PropertyInfo pi)
|
|
|
|
|
{
|
|
|
|
|
return pi.CanWrite;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-08-22 17:17:11 +10:00
|
|
|
|
// methods
|
2020-08-24 01:42:19 +10:00
|
|
|
|
public virtual void Init() { }
|
2020-08-22 00:16:05 +10:00
|
|
|
|
public abstract void DrawValue(Rect window, float width);
|
|
|
|
|
|
2020-08-29 21:15:54 +10:00
|
|
|
|
public static CacheObjectBase GetCacheObject(object obj)
|
2020-08-22 00:16:05 +10:00
|
|
|
|
{
|
|
|
|
|
return GetCacheObject(obj, null, null);
|
|
|
|
|
}
|
|
|
|
|
|
2020-08-22 17:17:11 +10:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Gets the CacheObject subclass for an object or MemberInfo
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="obj">The current value (can be null if memberInfo is not null)</param>
|
|
|
|
|
/// <param name="memberInfo">The MemberInfo (can be null if obj is not null)</param>
|
|
|
|
|
/// <param name="declaringInstance">If MemberInfo is not null, the declaring class instance. Can be null if static.</param>
|
|
|
|
|
/// <returns></returns>
|
2020-08-29 21:15:54 +10:00
|
|
|
|
public static CacheObjectBase GetCacheObject(object obj, MemberInfo memberInfo, object declaringInstance)
|
2020-08-22 00:16:05 +10:00
|
|
|
|
{
|
2020-08-29 21:15:54 +10:00
|
|
|
|
//var type = ReflectionHelpers.GetActualType(obj) ?? (memberInfo as FieldInfo)?.FieldType ?? (memberInfo as PropertyInfo)?.PropertyType;
|
|
|
|
|
|
|
|
|
|
Type type = null;
|
|
|
|
|
|
|
|
|
|
if (obj != null)
|
|
|
|
|
{
|
|
|
|
|
type = ReflectionHelpers.GetActualType(obj);
|
|
|
|
|
}
|
|
|
|
|
else if (memberInfo != null)
|
|
|
|
|
{
|
|
|
|
|
if (memberInfo is FieldInfo fi)
|
|
|
|
|
{
|
|
|
|
|
type = fi.FieldType;
|
|
|
|
|
}
|
|
|
|
|
else if (memberInfo is PropertyInfo pi)
|
|
|
|
|
{
|
|
|
|
|
type = pi.PropertyType;
|
|
|
|
|
}
|
|
|
|
|
else if (memberInfo is MethodInfo mi)
|
|
|
|
|
{
|
|
|
|
|
type = mi.ReturnType;
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-08-22 00:16:05 +10:00
|
|
|
|
|
2020-08-24 01:42:19 +10:00
|
|
|
|
if (type == null)
|
|
|
|
|
{
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return GetCacheObject(obj, memberInfo, declaringInstance, type);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Gets the CacheObject subclass for an object or MemberInfo
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="obj">The current value (can be null if memberInfo is not null)</param>
|
|
|
|
|
/// <param name="memberInfo">The MemberInfo (can be null if obj is not null)</param>
|
|
|
|
|
/// <param name="declaringInstance">If MemberInfo is not null, the declaring class instance. Can be null if static.</param>
|
2020-08-28 00:45:34 +10:00
|
|
|
|
/// <param name="valueType">The type of the object or MemberInfo value.</param>
|
2020-08-24 01:42:19 +10:00
|
|
|
|
/// <returns></returns>
|
2020-08-29 21:15:54 +10:00
|
|
|
|
public static CacheObjectBase GetCacheObject(object obj, MemberInfo memberInfo, object declaringInstance, Type valueType)
|
2020-08-24 01:42:19 +10:00
|
|
|
|
{
|
2020-08-29 21:15:54 +10:00
|
|
|
|
CacheObjectBase holder;
|
2020-08-24 01:42:19 +10:00
|
|
|
|
|
2020-08-29 21:15:54 +10:00
|
|
|
|
if (memberInfo is MethodInfo mi)
|
|
|
|
|
{
|
|
|
|
|
if (CacheMethod.CanEvaluate(mi))
|
|
|
|
|
{
|
|
|
|
|
holder = new CacheMethod();
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (valueType == typeof(GameObject) || valueType == typeof(Transform))
|
2020-08-22 00:16:05 +10:00
|
|
|
|
{
|
2020-08-24 01:42:19 +10:00
|
|
|
|
holder = new CacheGameObject();
|
2020-08-22 00:16:05 +10:00
|
|
|
|
}
|
2020-08-28 00:45:34 +10:00
|
|
|
|
else if (valueType.IsPrimitive || valueType == typeof(string))
|
2020-08-22 00:16:05 +10:00
|
|
|
|
{
|
2020-08-24 01:42:19 +10:00
|
|
|
|
holder = new CachePrimitive();
|
2020-08-22 00:16:05 +10:00
|
|
|
|
}
|
2020-08-28 00:45:34 +10:00
|
|
|
|
else if (valueType.IsEnum)
|
2020-08-22 17:17:11 +10:00
|
|
|
|
{
|
2020-08-24 01:42:19 +10:00
|
|
|
|
holder = new CacheEnum();
|
2020-08-22 17:17:11 +10:00
|
|
|
|
}
|
2020-08-29 21:15:54 +10:00
|
|
|
|
else if (ReflectionHelpers.IsArray(valueType) || ReflectionHelpers.IsList(valueType))
|
2020-08-22 17:17:11 +10:00
|
|
|
|
{
|
2020-08-24 01:42:19 +10:00
|
|
|
|
holder = new CacheList();
|
2020-08-22 17:17:11 +10:00
|
|
|
|
}
|
2020-08-29 21:15:54 +10:00
|
|
|
|
else if (ReflectionHelpers.IsDictionary(valueType))
|
|
|
|
|
{
|
|
|
|
|
holder = new CacheDictionary();
|
|
|
|
|
}
|
2020-08-22 17:17:11 +10:00
|
|
|
|
else
|
2020-08-22 00:16:05 +10:00
|
|
|
|
{
|
2020-08-22 17:17:11 +10:00
|
|
|
|
holder = new CacheOther();
|
2020-08-22 00:16:05 +10:00
|
|
|
|
}
|
|
|
|
|
|
2020-08-24 01:42:19 +10:00
|
|
|
|
holder.Value = obj;
|
2020-08-28 00:45:34 +10:00
|
|
|
|
holder.ValueType = valueType.FullName;
|
2020-08-24 01:42:19 +10:00
|
|
|
|
|
2020-08-22 00:16:05 +10:00
|
|
|
|
if (memberInfo != null)
|
|
|
|
|
{
|
2020-08-24 01:42:19 +10:00
|
|
|
|
holder.MemberInfo = memberInfo;
|
2020-08-22 00:16:05 +10:00
|
|
|
|
holder.DeclaringType = memberInfo.DeclaringType;
|
2020-08-22 17:17:11 +10:00
|
|
|
|
holder.DeclaringInstance = declaringInstance;
|
2020-08-28 00:45:34 +10:00
|
|
|
|
|
|
|
|
|
holder.UpdateValue();
|
2020-08-22 00:16:05 +10:00
|
|
|
|
}
|
|
|
|
|
|
2020-08-24 01:42:19 +10:00
|
|
|
|
holder.Init();
|
2020-08-22 00:16:05 +10:00
|
|
|
|
|
|
|
|
|
return holder;
|
|
|
|
|
}
|
|
|
|
|
|
2020-08-31 23:28:44 +10:00
|
|
|
|
// ======== Updating and Setting Value (memberinfo only) =========
|
2020-08-22 00:16:05 +10:00
|
|
|
|
|
2020-08-22 17:17:11 +10:00
|
|
|
|
public virtual void UpdateValue()
|
2020-08-22 00:16:05 +10:00
|
|
|
|
{
|
2020-08-22 17:17:11 +10:00
|
|
|
|
if (MemberInfo == null || !string.IsNullOrEmpty(ReflectionException))
|
2020-08-22 00:16:05 +10:00
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
if (MemberInfo.MemberType == MemberTypes.Field)
|
|
|
|
|
{
|
|
|
|
|
var fi = MemberInfo as FieldInfo;
|
|
|
|
|
Value = fi.GetValue(fi.IsStatic ? null : DeclaringInstance);
|
|
|
|
|
}
|
|
|
|
|
else if (MemberInfo.MemberType == MemberTypes.Property)
|
|
|
|
|
{
|
|
|
|
|
var pi = MemberInfo as PropertyInfo;
|
2020-08-22 17:17:11 +10:00
|
|
|
|
bool isStatic = pi.GetAccessors()[0].IsStatic;
|
|
|
|
|
var target = isStatic ? null : DeclaringInstance;
|
|
|
|
|
Value = pi.GetValue(target, null);
|
2020-08-22 00:16:05 +10:00
|
|
|
|
}
|
2020-08-22 17:17:11 +10:00
|
|
|
|
//ReflectionException = null;
|
2020-08-22 00:16:05 +10:00
|
|
|
|
}
|
2020-08-22 17:17:11 +10:00
|
|
|
|
catch (Exception e)
|
2020-08-22 00:16:05 +10:00
|
|
|
|
{
|
2020-08-22 17:17:11 +10:00
|
|
|
|
ReflectionException = ReflectionHelpers.ExceptionToString(e);
|
2020-08-22 00:16:05 +10:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-08-24 01:42:19 +10:00
|
|
|
|
public void SetValue()
|
2020-08-22 00:16:05 +10:00
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
2020-08-24 01:42:19 +10:00
|
|
|
|
if (MemberInfo.MemberType == MemberTypes.Field)
|
2020-08-22 00:16:05 +10:00
|
|
|
|
{
|
2020-08-24 01:42:19 +10:00
|
|
|
|
var fi = MemberInfo as FieldInfo;
|
|
|
|
|
fi.SetValue(fi.IsStatic ? null : DeclaringInstance, Value);
|
2020-08-22 00:16:05 +10:00
|
|
|
|
}
|
2020-08-24 01:42:19 +10:00
|
|
|
|
else if (MemberInfo.MemberType == MemberTypes.Property)
|
2020-08-22 00:16:05 +10:00
|
|
|
|
{
|
2020-08-24 01:42:19 +10:00
|
|
|
|
var pi = MemberInfo as PropertyInfo;
|
|
|
|
|
pi.SetValue(pi.GetAccessors()[0].IsStatic ? null : DeclaringInstance, Value);
|
2020-08-22 00:16:05 +10:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
catch (Exception e)
|
|
|
|
|
{
|
|
|
|
|
MelonLogger.LogWarning($"Error setting value: {e.GetType()}, {e.Message}");
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-08-31 23:28:44 +10:00
|
|
|
|
|
|
|
|
|
// ========= Gui Draw ==========
|
|
|
|
|
|
|
|
|
|
public const float MAX_LABEL_WIDTH = 400f;
|
|
|
|
|
|
|
|
|
|
public static void ClampLabelWidth(Rect window, ref float labelWidth)
|
|
|
|
|
{
|
|
|
|
|
float min = window.width * 0.37f;
|
|
|
|
|
if (min > MAX_LABEL_WIDTH) min = MAX_LABEL_WIDTH;
|
|
|
|
|
|
|
|
|
|
labelWidth = Mathf.Clamp(labelWidth, min, MAX_LABEL_WIDTH);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void Draw(Rect window, float labelWidth = 215f)
|
|
|
|
|
{
|
|
|
|
|
if (labelWidth > 0)
|
|
|
|
|
{
|
|
|
|
|
ClampLabelWidth(window, ref labelWidth);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (MemberInfo != null)
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
GUILayout.Label(RichTextName, new GUILayoutOption[] { GUILayout.Width(labelWidth) });
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
GUILayout.Space(labelWidth);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!string.IsNullOrEmpty(ReflectionException))
|
|
|
|
|
{
|
|
|
|
|
GUILayout.Label("<color=red>Reflection failed!</color> (" + ReflectionException + ")", null);
|
|
|
|
|
}
|
|
|
|
|
else if (Value == null && MemberInfo?.MemberType != MemberTypes.Method)
|
|
|
|
|
{
|
|
|
|
|
GUILayout.Label("<i>null (" + ValueType + ")</i>", null);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
DrawValue(window, window.width - labelWidth - 90);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void GetRichTextName()
|
|
|
|
|
{
|
|
|
|
|
string memberColor = "";
|
|
|
|
|
switch (MemberInfo.MemberType)
|
|
|
|
|
{
|
|
|
|
|
case MemberTypes.Field:
|
|
|
|
|
memberColor = "#c266ff"; break;
|
|
|
|
|
case MemberTypes.Property:
|
|
|
|
|
memberColor = "#72a6a6"; break;
|
|
|
|
|
case MemberTypes.Method:
|
|
|
|
|
memberColor = "#ff8000"; break;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
m_richTextName = $"<color=#2df7b2>{MemberInfo.DeclaringType.Name}</color>.<color={memberColor}>{MemberInfo.Name}</color>";
|
|
|
|
|
|
|
|
|
|
if (MemberInfo is MethodInfo mi)
|
|
|
|
|
{
|
|
|
|
|
m_richTextName += "(";
|
|
|
|
|
var _params = "";
|
|
|
|
|
foreach (var param in mi.GetParameters())
|
|
|
|
|
{
|
|
|
|
|
if (_params != "") _params += ", ";
|
|
|
|
|
|
|
|
|
|
_params += $"<color=#a6e9e9>{param.Name}</color>";
|
|
|
|
|
}
|
|
|
|
|
m_richTextName += _params;
|
|
|
|
|
m_richTextName += ")";
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-08-22 00:16:05 +10:00
|
|
|
|
}
|
|
|
|
|
}
|