mirror of
https://github.com/GrahamKracker/UnityExplorer.git
synced 2025-07-03 03:52:28 +08:00
Compare commits
6 Commits
Author | SHA1 | Date | |
---|---|---|---|
e7208d0c9d | |||
2f3b779199 | |||
916bdea59b | |||
d8688193d5 | |||
30b48b1f1f | |||
0fd382c1f6 |
@ -1,4 +1,4 @@
|
|||||||
# CppExplorer []()
|
# CppExplorer []()
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<img align="center" src="https://sinai-dev.github.io/images/thumbs/02.png">
|
<img align="center" src="https://sinai-dev.github.io/images/thumbs/02.png">
|
||||||
@ -66,7 +66,7 @@ CppExplorer has two main inspector modes: <b>GameObject Inspector</b>, and <b>Re
|
|||||||
* Allows you to inspect Properties, Fields and basic Methods, as well as set primitive values and evaluate primitive methods.
|
* Allows you to inspect Properties, Fields and basic Methods, as well as set primitive values and evaluate primitive methods.
|
||||||
* Can search and filter members for the ones you are interested in.
|
* Can search and filter members for the ones you are interested in.
|
||||||
|
|
||||||
[](https://i.imgur.com/eFVTQdh.png)
|
[](https://i.imgur.com/iq92m0l.png)
|
||||||
|
|
||||||
### Object Search
|
### Object Search
|
||||||
|
|
||||||
|
@ -3,6 +3,7 @@ using System.Collections.Generic;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using System.Reflection;
|
||||||
using MelonLoader;
|
using MelonLoader;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
|
||||||
@ -15,8 +16,23 @@ namespace Explorer
|
|||||||
|
|
||||||
public override void Init()
|
public override void Init()
|
||||||
{
|
{
|
||||||
EnumType = Value.GetType();
|
try
|
||||||
EnumNames = Enum.GetNames(EnumType);
|
{
|
||||||
|
EnumType = Value.GetType();
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
EnumType = (MemInfo as FieldInfo)?.FieldType ?? (MemInfo as PropertyInfo).PropertyType;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (EnumType != null)
|
||||||
|
{
|
||||||
|
EnumNames = Enum.GetNames(EnumType);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ReflectionException = "Unknown, could not get Enum names.";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void DrawValue(Rect window, float width)
|
public override void DrawValue(Rect window, float width)
|
||||||
|
@ -3,11 +3,12 @@ using System.Collections;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
using MelonLoader;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
|
||||||
namespace Explorer
|
namespace Explorer
|
||||||
{
|
{
|
||||||
public partial class CacheList : CacheObjectBase
|
public class CacheList : CacheObjectBase
|
||||||
{
|
{
|
||||||
public bool IsExpanded { get; set; }
|
public bool IsExpanded { get; set; }
|
||||||
public int ArrayOffset { get; set; }
|
public int ArrayOffset { get; set; }
|
||||||
@ -16,74 +17,246 @@ namespace Explorer
|
|||||||
public float WhiteSpace = 215f;
|
public float WhiteSpace = 215f;
|
||||||
public float ButtonWidthOffset = 290f;
|
public float ButtonWidthOffset = 290f;
|
||||||
|
|
||||||
|
private CacheObjectBase[] m_cachedEntries;
|
||||||
|
|
||||||
|
// Type of Entries in the Array
|
||||||
public Type EntryType
|
public Type EntryType
|
||||||
{
|
{
|
||||||
get
|
get => GetEntryType();
|
||||||
{
|
set => m_entryType = value;
|
||||||
if (m_entryType == null)
|
|
||||||
{
|
|
||||||
if (this.MemberInfo != null)
|
|
||||||
{
|
|
||||||
switch (this.MemberInfo.MemberType)
|
|
||||||
{
|
|
||||||
case MemberTypes.Field:
|
|
||||||
m_entryType = (MemberInfo as FieldInfo).FieldType.GetGenericArguments()[0];
|
|
||||||
break;
|
|
||||||
case MemberTypes.Property:
|
|
||||||
m_entryType = (MemberInfo as PropertyInfo).PropertyType.GetGenericArguments()[0];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (Value != null)
|
|
||||||
{
|
|
||||||
m_entryType = Value.GetType().GetGenericArguments()[0];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return m_entryType;
|
|
||||||
}
|
|
||||||
set
|
|
||||||
{
|
|
||||||
m_entryType = value;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
private Type m_entryType;
|
private Type m_entryType;
|
||||||
|
|
||||||
|
// Cached IEnumerable object
|
||||||
public IEnumerable Enumerable
|
public IEnumerable Enumerable
|
||||||
{
|
{
|
||||||
get
|
get => GetEnumerable();
|
||||||
{
|
|
||||||
if (m_enumerable == null && Value != null)
|
|
||||||
{
|
|
||||||
m_enumerable = Value as IEnumerable ?? CastValueFromList();
|
|
||||||
}
|
|
||||||
return m_enumerable;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private IEnumerable m_enumerable;
|
private IEnumerable m_enumerable;
|
||||||
private CacheObjectBase[] m_cachedEntries;
|
|
||||||
|
|
||||||
|
// Generic Type Definition for Lists
|
||||||
|
public Type GenericTypeDef
|
||||||
|
{
|
||||||
|
get => GetGenericTypeDef();
|
||||||
|
}
|
||||||
|
private Type m_genericTypeDef;
|
||||||
|
|
||||||
|
// Cached ToArray method for Lists
|
||||||
public MethodInfo GenericToArrayMethod
|
public MethodInfo GenericToArrayMethod
|
||||||
{
|
{
|
||||||
get
|
get => GetGenericToArrayMethod();
|
||||||
{
|
|
||||||
if (EntryType == null) return null;
|
|
||||||
|
|
||||||
return m_genericToArray ??
|
|
||||||
(m_genericToArray = typeof(Il2CppSystem.Collections.Generic.List<>)
|
|
||||||
.MakeGenericType(new Type[] { this.EntryType })
|
|
||||||
.GetMethod("ToArray"));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
private MethodInfo m_genericToArray;
|
private MethodInfo m_genericToArray;
|
||||||
|
|
||||||
private IEnumerable CastValueFromList()
|
// Cached Item Property for ILists
|
||||||
|
public PropertyInfo ItemProperty
|
||||||
{
|
{
|
||||||
return (Value == null) ? null : (IEnumerable)GenericToArrayMethod?.Invoke(Value, new object[0]);
|
get => GetItemProperty();
|
||||||
}
|
}
|
||||||
|
private PropertyInfo m_itemProperty;
|
||||||
|
|
||||||
|
// ========== Methods ==========
|
||||||
|
|
||||||
|
private IEnumerable GetEnumerable()
|
||||||
|
{
|
||||||
|
if (m_enumerable == null && Value != null)
|
||||||
|
{
|
||||||
|
m_enumerable = Value as IEnumerable ?? GetEnumerableFromIl2CppList();
|
||||||
|
}
|
||||||
|
return m_enumerable;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Type GetGenericTypeDef()
|
||||||
|
{
|
||||||
|
if (m_genericTypeDef == null && Value != null)
|
||||||
|
{
|
||||||
|
var type = Value.GetType();
|
||||||
|
if (type.IsGenericType)
|
||||||
|
{
|
||||||
|
m_genericTypeDef = type.GetGenericTypeDefinition();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return m_genericTypeDef;
|
||||||
|
}
|
||||||
|
|
||||||
|
private MethodInfo GetGenericToArrayMethod()
|
||||||
|
{
|
||||||
|
if (GenericTypeDef == null) return null;
|
||||||
|
|
||||||
|
if (m_genericToArray == null)
|
||||||
|
{
|
||||||
|
m_genericToArray = GenericTypeDef
|
||||||
|
.MakeGenericType(new Type[] { this.EntryType })
|
||||||
|
.GetMethod("ToArray");
|
||||||
|
}
|
||||||
|
return m_genericToArray;
|
||||||
|
}
|
||||||
|
|
||||||
|
private PropertyInfo GetItemProperty()
|
||||||
|
{
|
||||||
|
if (m_itemProperty == null)
|
||||||
|
{
|
||||||
|
m_itemProperty = Value?.GetType().GetProperty("Item");
|
||||||
|
}
|
||||||
|
return m_itemProperty;
|
||||||
|
}
|
||||||
|
|
||||||
|
private IEnumerable GetEnumerableFromIl2CppList()
|
||||||
|
{
|
||||||
|
if (Value == null) return null;
|
||||||
|
|
||||||
|
if (GenericTypeDef == typeof(Il2CppSystem.Collections.Generic.List<>))
|
||||||
|
{
|
||||||
|
return (IEnumerable)GenericToArrayMethod?.Invoke(Value, new object[0]);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return ConvertIListToMono();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private IList ConvertIListToMono()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var genericType = typeof(List<>).MakeGenericType(new Type[] { this.EntryType });
|
||||||
|
var list = (IList)Activator.CreateInstance(genericType);
|
||||||
|
|
||||||
|
for (int i = 0; ; i++)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var itm = ItemProperty.GetValue(Value, new object[] { i });
|
||||||
|
list.Add(itm);
|
||||||
|
}
|
||||||
|
catch { break; }
|
||||||
|
}
|
||||||
|
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
MelonLogger.Log("Exception converting Il2Cpp IList to Mono IList: " + e.GetType() + ", " + e.Message);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Type GetEntryType()
|
||||||
|
{
|
||||||
|
if (m_entryType == null)
|
||||||
|
{
|
||||||
|
if (this.MemInfo != null)
|
||||||
|
{
|
||||||
|
Type memberType = null;
|
||||||
|
switch (this.MemInfo.MemberType)
|
||||||
|
{
|
||||||
|
case MemberTypes.Field:
|
||||||
|
memberType = (MemInfo as FieldInfo).FieldType;
|
||||||
|
break;
|
||||||
|
case MemberTypes.Property:
|
||||||
|
memberType = (MemInfo as PropertyInfo).PropertyType;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (memberType != null && memberType.IsGenericType)
|
||||||
|
{
|
||||||
|
m_entryType = memberType.GetGenericArguments()[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (Value != null)
|
||||||
|
{
|
||||||
|
var type = Value.GetType();
|
||||||
|
if (type.IsGenericType)
|
||||||
|
{
|
||||||
|
m_entryType = type.GetGenericArguments()[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// IList probably won't be able to get any EntryType.
|
||||||
|
if (m_entryType == null)
|
||||||
|
{
|
||||||
|
m_entryType = typeof(object);
|
||||||
|
}
|
||||||
|
|
||||||
|
return m_entryType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void UpdateValue()
|
||||||
|
{
|
||||||
|
base.UpdateValue();
|
||||||
|
|
||||||
|
if (Value == null || Enumerable == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var enumerator = Enumerable.GetEnumerator();
|
||||||
|
if (enumerator == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var list = new List<CacheObjectBase>();
|
||||||
|
while (enumerator.MoveNext())
|
||||||
|
{
|
||||||
|
var obj = enumerator.Current;
|
||||||
|
|
||||||
|
if (obj != null && ReflectionHelpers.GetActualType(obj) is Type t)
|
||||||
|
{
|
||||||
|
if (obj is Il2CppSystem.Object iObj)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var cast = iObj.Il2CppCast(t);
|
||||||
|
if (cast != null)
|
||||||
|
{
|
||||||
|
obj = cast;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch { }
|
||||||
|
}
|
||||||
|
|
||||||
|
if (GetCacheObject(obj, t) is CacheObjectBase cached)
|
||||||
|
{
|
||||||
|
list.Add(cached);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
list.Add(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
list.Add(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
//var type = ReflectionHelpers.GetActualType(obj);
|
||||||
|
|
||||||
|
//if (obj is Il2CppSystem.Object iObj)
|
||||||
|
//{
|
||||||
|
// obj = iObj.Il2CppCast(type);
|
||||||
|
//}
|
||||||
|
|
||||||
|
//var cached = GetCacheObject(obj, null, null, type);
|
||||||
|
//cached.UpdateValue();
|
||||||
|
|
||||||
|
//list.Add(cached);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_cachedEntries = list.ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============= GUI Draw =============
|
||||||
|
|
||||||
public override void DrawValue(Rect window, float width)
|
public override void DrawValue(Rect window, float width)
|
||||||
{
|
{
|
||||||
|
if (m_cachedEntries == null)
|
||||||
|
{
|
||||||
|
GUILayout.Label("m_cachedEntries is null!", null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
int count = m_cachedEntries.Length;
|
int count = m_cachedEntries.Length;
|
||||||
|
|
||||||
if (!IsExpanded)
|
if (!IsExpanded)
|
||||||
@ -167,42 +340,22 @@ namespace Explorer
|
|||||||
|
|
||||||
GUILayout.Space(whitespace);
|
GUILayout.Space(whitespace);
|
||||||
|
|
||||||
if (entry.Value == null)
|
if (entry == null || entry.Value == null)
|
||||||
{
|
{
|
||||||
GUILayout.Label(i + "<i><color=grey> (null)</color></i>", null);
|
GUILayout.Label($"[{i}] <i><color=grey>(null)</color></i>", null);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
GUI.skin.label.alignment = TextAnchor.MiddleCenter;
|
GUI.skin.label.alignment = TextAnchor.MiddleCenter;
|
||||||
GUILayout.Label($"[{i}]", new GUILayoutOption[] { GUILayout.Width(30) });
|
GUILayout.Label($"[{i}]", new GUILayoutOption[] { GUILayout.Width(30) });
|
||||||
entry.DrawValue(window, window.width - (whitespace + 85));
|
|
||||||
|
entry.DrawValue(window, window.width - (whitespace + 85));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
GUI.skin.label.alignment = TextAnchor.UpperLeft;
|
GUI.skin.label.alignment = TextAnchor.UpperLeft;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Called only when the user presses the "Update" button, or if AutoUpdate is on.
|
|
||||||
/// </summary>
|
|
||||||
public override void UpdateValue()
|
|
||||||
{
|
|
||||||
base.UpdateValue();
|
|
||||||
|
|
||||||
if (Value == null) return;
|
|
||||||
|
|
||||||
var enumerator = Enumerable?.GetEnumerator();
|
|
||||||
|
|
||||||
if (enumerator == null) return;
|
|
||||||
|
|
||||||
var list = new List<CacheObjectBase>();
|
|
||||||
while (enumerator.MoveNext())
|
|
||||||
{
|
|
||||||
list.Add(GetCacheObject(enumerator.Current, null, null, this.EntryType));
|
|
||||||
}
|
|
||||||
|
|
||||||
m_cachedEntries = list.ToArray();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -24,7 +24,7 @@ namespace Explorer
|
|||||||
{
|
{
|
||||||
if (m_hasParams == null)
|
if (m_hasParams == null)
|
||||||
{
|
{
|
||||||
m_hasParams = (MemberInfo as MethodInfo).GetParameters().Length > 0;
|
m_hasParams = (MemInfo as MethodInfo).GetParameters().Length > 0;
|
||||||
}
|
}
|
||||||
return (bool)m_hasParams;
|
return (bool)m_hasParams;
|
||||||
}
|
}
|
||||||
@ -55,7 +55,7 @@ namespace Explorer
|
|||||||
{
|
{
|
||||||
base.Init();
|
base.Init();
|
||||||
|
|
||||||
var mi = MemberInfo as MethodInfo;
|
var mi = MemInfo as MethodInfo;
|
||||||
|
|
||||||
m_arguments = mi.GetParameters();
|
m_arguments = mi.GetParameters();
|
||||||
m_argumentInput = new string[m_arguments.Length];
|
m_argumentInput = new string[m_arguments.Length];
|
||||||
@ -63,7 +63,7 @@ namespace Explorer
|
|||||||
|
|
||||||
public override void UpdateValue()
|
public override void UpdateValue()
|
||||||
{
|
{
|
||||||
base.UpdateValue();
|
//base.UpdateValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void DrawValue(Rect window, float width)
|
public override void DrawValue(Rect window, float width)
|
||||||
@ -136,12 +136,12 @@ namespace Explorer
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
GUILayout.Label($"null (<color=yellow>{ValueType}</color>)", null);
|
GUILayout.Label($"null (<color=yellow>{ValueTypeName}</color>)", null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
GUILayout.Label($"<color=grey><i>Not yet evaluated</i></color> (<color=yellow>{ValueType}</color>)", null);
|
GUILayout.Label($"<color=grey><i>Not yet evaluated</i></color> (<color=yellow>{ValueTypeName}</color>)", null);
|
||||||
}
|
}
|
||||||
GUILayout.EndHorizontal();
|
GUILayout.EndHorizontal();
|
||||||
|
|
||||||
@ -150,7 +150,7 @@ namespace Explorer
|
|||||||
|
|
||||||
private void Evaluate()
|
private void Evaluate()
|
||||||
{
|
{
|
||||||
var mi = MemberInfo as MethodInfo;
|
var mi = MemInfo as MethodInfo;
|
||||||
|
|
||||||
object ret = null;
|
object ret = null;
|
||||||
|
|
||||||
|
@ -13,54 +13,70 @@ namespace Explorer
|
|||||||
public abstract class CacheObjectBase
|
public abstract class CacheObjectBase
|
||||||
{
|
{
|
||||||
public object Value;
|
public object Value;
|
||||||
public string ValueType;
|
public string ValueTypeName;
|
||||||
|
|
||||||
// Reflection window only
|
// Reflection Inspector only
|
||||||
public MemberInfo MemberInfo { get; set; }
|
public MemberInfo MemInfo { get; set; }
|
||||||
public Type DeclaringType { get; set; }
|
public Type DeclaringType { get; set; }
|
||||||
public object DeclaringInstance { get; set; }
|
public object DeclaringInstance { get; set; }
|
||||||
public string FullName => $"{MemberInfo.DeclaringType.Name}.{MemberInfo.Name}";
|
public string ReflectionException { get; set; }
|
||||||
public string ReflectionException;
|
|
||||||
|
public int PropertyIndex { get; private set; }
|
||||||
|
private string m_propertyIndexInput = "0";
|
||||||
|
|
||||||
|
public string RichTextName => m_richTextName ?? GetRichTextName();
|
||||||
|
private string m_richTextName;
|
||||||
|
|
||||||
public bool CanWrite
|
public bool CanWrite
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
if (MemberInfo is FieldInfo fi)
|
if (MemInfo is FieldInfo fi)
|
||||||
{
|
|
||||||
return !(fi.IsLiteral && !fi.IsInitOnly);
|
return !(fi.IsLiteral && !fi.IsInitOnly);
|
||||||
}
|
else if (MemInfo is PropertyInfo pi)
|
||||||
else if (MemberInfo is PropertyInfo pi)
|
|
||||||
{
|
|
||||||
return pi.CanWrite;
|
return pi.CanWrite;
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// methods
|
// ===== Abstract/Virtual Methods ===== //
|
||||||
|
|
||||||
public virtual void Init() { }
|
public virtual void Init() { }
|
||||||
|
|
||||||
public abstract void DrawValue(Rect window, float width);
|
public abstract void DrawValue(Rect window, float width);
|
||||||
|
|
||||||
|
// ===== Static Methods ===== //
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get CacheObject from only an object instance
|
||||||
|
/// Calls GetCacheObject(obj, memberInfo, declaringInstance) with (obj, null, null)</summary>
|
||||||
public static CacheObjectBase GetCacheObject(object obj)
|
public static CacheObjectBase GetCacheObject(object obj)
|
||||||
{
|
{
|
||||||
return GetCacheObject(obj, null, null);
|
return GetCacheObject(obj, null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the CacheObject subclass for an object or MemberInfo
|
/// Get CacheObject from an object instance and provide the value type
|
||||||
/// </summary>
|
/// Calls GetCacheObjectImpl directly</summary>
|
||||||
/// <param name="obj">The current value (can be null if memberInfo is not null)</param>
|
public static CacheObjectBase GetCacheObject(object obj, Type valueType)
|
||||||
/// <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>
|
return GetCacheObjectImpl(obj, null, null, valueType);
|
||||||
/// <returns></returns>
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get CacheObject from only a MemberInfo and declaring instance
|
||||||
|
/// Calls GetCacheObject(obj, memberInfo, declaringInstance) with (null, memberInfo, declaringInstance)</summary>
|
||||||
|
public static CacheObjectBase GetCacheObject(MemberInfo memberInfo, object declaringInstance)
|
||||||
|
{
|
||||||
|
return GetCacheObject(null, memberInfo, declaringInstance);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get CacheObject from either an object or MemberInfo, and don't provide the type.
|
||||||
|
/// This gets the type and then calls GetCacheObjectImpl</summary>
|
||||||
public static CacheObjectBase GetCacheObject(object obj, MemberInfo memberInfo, object declaringInstance)
|
public static CacheObjectBase GetCacheObject(object obj, MemberInfo memberInfo, object declaringInstance)
|
||||||
{
|
{
|
||||||
//var type = ReflectionHelpers.GetActualType(obj) ?? (memberInfo as FieldInfo)?.FieldType ?? (memberInfo as PropertyInfo)?.PropertyType;
|
|
||||||
|
|
||||||
Type type = null;
|
Type type = null;
|
||||||
|
|
||||||
if (obj != null)
|
if (obj != null)
|
||||||
@ -85,26 +101,16 @@ namespace Explorer
|
|||||||
|
|
||||||
if (type == null)
|
if (type == null)
|
||||||
{
|
{
|
||||||
MelonLogger.Log("Could not get type for object or memberinfo!");
|
|
||||||
if (memberInfo is MethodInfo)
|
|
||||||
{
|
|
||||||
MelonLogger.Log("is it void?");
|
|
||||||
}
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return GetCacheObject(obj, memberInfo, declaringInstance, type);
|
return GetCacheObjectImpl(obj, memberInfo, declaringInstance, type);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the CacheObject subclass for an object or MemberInfo
|
/// Actual GetCacheObject implementation (private)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="obj">The current value (can be null if memberInfo is not null)</param>
|
private static CacheObjectBase GetCacheObjectImpl(object obj, MemberInfo memberInfo, object declaringInstance, Type valueType)
|
||||||
/// <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>
|
|
||||||
/// <param name="valueType">The type of the object or MemberInfo value.</param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public static CacheObjectBase GetCacheObject(object obj, MemberInfo memberInfo, object declaringInstance, Type valueType)
|
|
||||||
{
|
{
|
||||||
CacheObjectBase holder;
|
CacheObjectBase holder;
|
||||||
|
|
||||||
@ -145,11 +151,11 @@ namespace Explorer
|
|||||||
}
|
}
|
||||||
|
|
||||||
holder.Value = obj;
|
holder.Value = obj;
|
||||||
holder.ValueType = valueType.FullName;
|
holder.ValueTypeName = valueType.FullName;
|
||||||
|
|
||||||
if (memberInfo != null)
|
if (memberInfo != null)
|
||||||
{
|
{
|
||||||
holder.MemberInfo = memberInfo;
|
holder.MemInfo = memberInfo;
|
||||||
holder.DeclaringType = memberInfo.DeclaringType;
|
holder.DeclaringType = memberInfo.DeclaringType;
|
||||||
holder.DeclaringInstance = declaringInstance;
|
holder.DeclaringInstance = declaringInstance;
|
||||||
|
|
||||||
@ -161,6 +167,79 @@ namespace Explorer
|
|||||||
return holder;
|
return holder;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ======== Instance Methods =========
|
||||||
|
|
||||||
|
public virtual void UpdateValue()
|
||||||
|
{
|
||||||
|
if (MemInfo == null || !string.IsNullOrEmpty(ReflectionException))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (MemInfo.MemberType == MemberTypes.Field)
|
||||||
|
{
|
||||||
|
var fi = MemInfo as FieldInfo;
|
||||||
|
Value = fi.GetValue(fi.IsStatic ? null : DeclaringInstance);
|
||||||
|
}
|
||||||
|
else if (MemInfo.MemberType == MemberTypes.Property)
|
||||||
|
{
|
||||||
|
var pi = MemInfo as PropertyInfo;
|
||||||
|
bool isStatic = pi.GetAccessors()[0].IsStatic;
|
||||||
|
var target = isStatic ? null : DeclaringInstance;
|
||||||
|
|
||||||
|
if (pi.GetIndexParameters().Length > 0)
|
||||||
|
{
|
||||||
|
var indexes = new object[] { PropertyIndex };
|
||||||
|
Value = pi.GetValue(target, indexes);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Value = pi.GetValue(target, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ReflectionException = null;
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
ReflectionException = ReflectionHelpers.ExceptionToString(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetValue()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (MemInfo.MemberType == MemberTypes.Field)
|
||||||
|
{
|
||||||
|
var fi = MemInfo as FieldInfo;
|
||||||
|
fi.SetValue(fi.IsStatic ? null : DeclaringInstance, Value);
|
||||||
|
}
|
||||||
|
else if (MemInfo.MemberType == MemberTypes.Property)
|
||||||
|
{
|
||||||
|
var pi = MemInfo as PropertyInfo;
|
||||||
|
|
||||||
|
if (pi.GetIndexParameters().Length > 0)
|
||||||
|
{
|
||||||
|
var indexes = new object[] { PropertyIndex };
|
||||||
|
pi.SetValue(pi.GetAccessors()[0].IsStatic ? null : DeclaringInstance, Value, indexes);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
pi.SetValue(pi.GetAccessors()[0].IsStatic ? null : DeclaringInstance, Value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
MelonLogger.LogWarning($"Error setting value: {e.GetType()}, {e.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========= Instance Gui Draw ==========
|
||||||
|
|
||||||
public const float MAX_LABEL_WIDTH = 400f;
|
public const float MAX_LABEL_WIDTH = 400f;
|
||||||
|
|
||||||
public static void ClampLabelWidth(Rect window, ref float labelWidth)
|
public static void ClampLabelWidth(Rect window, ref float labelWidth)
|
||||||
@ -178,9 +257,15 @@ namespace Explorer
|
|||||||
ClampLabelWidth(window, ref labelWidth);
|
ClampLabelWidth(window, ref labelWidth);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (MemberInfo != null)
|
if (MemInfo != null)
|
||||||
{
|
{
|
||||||
GUILayout.Label("<color=cyan>" + FullName + "</color>", new GUILayoutOption[] { GUILayout.Width(labelWidth) });
|
var name = RichTextName;
|
||||||
|
if (MemInfo is PropertyInfo pi && pi.GetIndexParameters().Length > 0)
|
||||||
|
{
|
||||||
|
name += $"[{PropertyIndex}]";
|
||||||
|
}
|
||||||
|
|
||||||
|
GUILayout.Label(name, new GUILayoutOption[] { GUILayout.Width(labelWidth) });
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -191,64 +276,70 @@ namespace Explorer
|
|||||||
{
|
{
|
||||||
GUILayout.Label("<color=red>Reflection failed!</color> (" + ReflectionException + ")", null);
|
GUILayout.Label("<color=red>Reflection failed!</color> (" + ReflectionException + ")", null);
|
||||||
}
|
}
|
||||||
else if (Value == null && MemberInfo?.MemberType != MemberTypes.Method)
|
else if (Value == null && MemInfo?.MemberType != MemberTypes.Method)
|
||||||
{
|
{
|
||||||
GUILayout.Label("<i>null (" + ValueType + ")</i>", null);
|
GUILayout.Label("<i>null (" + ValueTypeName + ")</i>", null);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
if (MemInfo is PropertyInfo pi && pi.GetIndexParameters().Length > 0)
|
||||||
|
{
|
||||||
|
GUILayout.Label("index:", new GUILayoutOption[] { GUILayout.Width(50) });
|
||||||
|
|
||||||
|
m_propertyIndexInput = GUILayout.TextField(m_propertyIndexInput, new GUILayoutOption[] { GUILayout.Width(100) });
|
||||||
|
if (GUILayout.Button("Set", new GUILayoutOption[] { GUILayout.Width(60) }))
|
||||||
|
{
|
||||||
|
if (int.TryParse(m_propertyIndexInput, out int i))
|
||||||
|
{
|
||||||
|
PropertyIndex = i;
|
||||||
|
UpdateValue();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
MelonLogger.Log($"Could not parse '{m_propertyIndexInput}' to an int!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// new line and space
|
||||||
|
GUILayout.EndHorizontal();
|
||||||
|
GUILayout.BeginHorizontal(null);
|
||||||
|
GUILayout.Space(labelWidth);
|
||||||
|
}
|
||||||
|
|
||||||
DrawValue(window, window.width - labelWidth - 90);
|
DrawValue(window, window.width - labelWidth - 90);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual void UpdateValue()
|
private string GetRichTextName()
|
||||||
{
|
{
|
||||||
if (MemberInfo == null || !string.IsNullOrEmpty(ReflectionException))
|
string memberColor = "";
|
||||||
|
switch (MemInfo.MemberType)
|
||||||
{
|
{
|
||||||
return;
|
case MemberTypes.Field:
|
||||||
|
memberColor = "#c266ff"; break;
|
||||||
|
case MemberTypes.Property:
|
||||||
|
memberColor = "#72a6a6"; break;
|
||||||
|
case MemberTypes.Method:
|
||||||
|
memberColor = "#ff8000"; break;
|
||||||
|
};
|
||||||
|
|
||||||
|
m_richTextName = $"<color=#2df7b2>{MemInfo.DeclaringType.Name}</color>.<color={memberColor}>{MemInfo.Name}</color>";
|
||||||
|
|
||||||
|
if (MemInfo 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 += ")";
|
||||||
}
|
}
|
||||||
|
|
||||||
try
|
return m_richTextName;
|
||||||
{
|
|
||||||
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;
|
|
||||||
bool isStatic = pi.GetAccessors()[0].IsStatic;
|
|
||||||
var target = isStatic ? null : DeclaringInstance;
|
|
||||||
Value = pi.GetValue(target, null);
|
|
||||||
}
|
|
||||||
//ReflectionException = null;
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
ReflectionException = ReflectionHelpers.ExceptionToString(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SetValue()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (MemberInfo.MemberType == MemberTypes.Field)
|
|
||||||
{
|
|
||||||
var fi = MemberInfo as FieldInfo;
|
|
||||||
fi.SetValue(fi.IsStatic ? null : DeclaringInstance, Value);
|
|
||||||
}
|
|
||||||
else if (MemberInfo.MemberType == MemberTypes.Property)
|
|
||||||
{
|
|
||||||
var pi = MemberInfo as PropertyInfo;
|
|
||||||
pi.SetValue(pi.GetAccessors()[0].IsStatic ? null : DeclaringInstance, Value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
MelonLogger.LogWarning($"Error setting value: {e.GetType()}, {e.Message}");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,33 +12,25 @@ namespace Explorer
|
|||||||
public class CacheOther : CacheObjectBase
|
public class CacheOther : CacheObjectBase
|
||||||
{
|
{
|
||||||
private MethodInfo m_toStringMethod;
|
private MethodInfo m_toStringMethod;
|
||||||
private bool m_triedToGetMethod;
|
|
||||||
|
|
||||||
public MethodInfo ToStringMethod
|
public MethodInfo ToStringMethod
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
if (m_toStringMethod == null && !m_triedToGetMethod)
|
if (m_toStringMethod == null)
|
||||||
{
|
{
|
||||||
if (Value == null) return null;
|
|
||||||
|
|
||||||
m_triedToGetMethod = true;
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var methods = ReflectionHelpers.GetActualType(Value)
|
m_toStringMethod = ReflectionHelpers.GetActualType(Value).GetMethod("ToString", new Type[0])
|
||||||
.GetMethods(ReflectionHelpers.CommonFlags)
|
?? typeof(object).GetMethod("ToString", new Type[0]);
|
||||||
.Where(x => x.Name == "ToString")
|
|
||||||
.GetEnumerator();
|
|
||||||
|
|
||||||
while (methods.MoveNext())
|
// test invoke
|
||||||
{
|
m_toStringMethod.Invoke(Value, null);
|
||||||
// just get the first (top-most level) method, then break.
|
}
|
||||||
m_toStringMethod = methods.Current;
|
catch
|
||||||
break;
|
{
|
||||||
}
|
m_toStringMethod = typeof(object).GetMethod("ToString", new Type[0]);
|
||||||
}
|
}
|
||||||
catch { }
|
|
||||||
}
|
}
|
||||||
return m_toStringMethod;
|
return m_toStringMethod;
|
||||||
}
|
}
|
||||||
@ -48,9 +40,9 @@ namespace Explorer
|
|||||||
{
|
{
|
||||||
string label = (string)ToStringMethod?.Invoke(Value, null) ?? Value.ToString();
|
string label = (string)ToStringMethod?.Invoke(Value, null) ?? Value.ToString();
|
||||||
|
|
||||||
if (!label.Contains(ValueType))
|
if (!label.Contains(ValueTypeName))
|
||||||
{
|
{
|
||||||
label += $" ({ValueType})";
|
label += $" ({ValueTypeName})";
|
||||||
}
|
}
|
||||||
if (Value is UnityEngine.Object unityObj && !label.Contains(unityObj.name))
|
if (Value is UnityEngine.Object unityObj && !label.Contains(unityObj.name))
|
||||||
{
|
{
|
||||||
@ -58,7 +50,7 @@ namespace Explorer
|
|||||||
}
|
}
|
||||||
|
|
||||||
GUI.skin.button.alignment = TextAnchor.MiddleLeft;
|
GUI.skin.button.alignment = TextAnchor.MiddleLeft;
|
||||||
if (GUILayout.Button("<color=yellow>" + label + "</color>", new GUILayoutOption[] { GUILayout.MaxWidth(width + 40) }))
|
if (GUILayout.Button("<color=yellow>" + label + "</color>", new GUILayoutOption[] { GUILayout.Width(width) }))
|
||||||
{
|
{
|
||||||
WindowManager.InspectObject(Value, out bool _);
|
WindowManager.InspectObject(Value, out bool _);
|
||||||
}
|
}
|
||||||
|
@ -28,21 +28,7 @@ namespace Explorer
|
|||||||
{
|
{
|
||||||
if (m_parseMethod == null)
|
if (m_parseMethod == null)
|
||||||
{
|
{
|
||||||
Type t = null;
|
m_parseMethod = Value.GetType().GetMethod("Parse", new Type[] { typeof(string) });
|
||||||
switch (PrimitiveType)
|
|
||||||
{
|
|
||||||
case PrimitiveTypes.Bool:
|
|
||||||
t = typeof(bool); break;
|
|
||||||
case PrimitiveTypes.Double:
|
|
||||||
t = typeof(double); break;
|
|
||||||
case PrimitiveTypes.Float:
|
|
||||||
t = typeof(float); break;
|
|
||||||
case PrimitiveTypes.Int:
|
|
||||||
t = typeof(int); break;
|
|
||||||
case PrimitiveTypes.Char:
|
|
||||||
t = typeof(char); break;
|
|
||||||
}
|
|
||||||
m_parseMethod = t?.GetMethod("Parse", new Type[] { typeof(string) });
|
|
||||||
}
|
}
|
||||||
return m_parseMethod;
|
return m_parseMethod;
|
||||||
}
|
}
|
||||||
@ -127,7 +113,7 @@ namespace Explorer
|
|||||||
b = GUILayout.Toggle(b, label, null);
|
b = GUILayout.Toggle(b, label, null);
|
||||||
if (b != (bool)Value)
|
if (b != (bool)Value)
|
||||||
{
|
{
|
||||||
SetValue(m_valueToString);
|
SetValueFromInput(b.ToString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -156,7 +142,7 @@ namespace Explorer
|
|||||||
{
|
{
|
||||||
if (GUILayout.Button("<color=#00FF00>Apply</color>", new GUILayoutOption[] { GUILayout.Width(60) }))
|
if (GUILayout.Button("<color=#00FF00>Apply</color>", new GUILayoutOption[] { GUILayout.Width(60) }))
|
||||||
{
|
{
|
||||||
SetValue(m_valueToString);
|
SetValueFromInput(m_valueToString);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -164,9 +150,9 @@ namespace Explorer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetValue(string value)
|
public void SetValueFromInput(string value)
|
||||||
{
|
{
|
||||||
if (MemberInfo == null)
|
if (MemInfo == null)
|
||||||
{
|
{
|
||||||
MelonLogger.Log("Trying to SetValue but the MemberInfo is null!");
|
MelonLogger.Log("Trying to SetValue but the MemberInfo is null!");
|
||||||
return;
|
return;
|
||||||
|
@ -12,7 +12,7 @@ namespace Explorer
|
|||||||
public class CppExplorer : MelonMod
|
public class CppExplorer : MelonMod
|
||||||
{
|
{
|
||||||
public const string GUID = "com.sinai.cppexplorer";
|
public const string GUID = "com.sinai.cppexplorer";
|
||||||
public const string VERSION = "1.4.5";
|
public const string VERSION = "1.5.1";
|
||||||
public const string AUTHOR = "Sinai";
|
public const string AUTHOR = "Sinai";
|
||||||
|
|
||||||
public const string NAME = "CppExplorer"
|
public const string NAME = "CppExplorer"
|
||||||
@ -133,7 +133,7 @@ namespace Explorer
|
|||||||
// value that we set back to when we close the menu or disable force-unlock.
|
// value that we set back to when we close the menu or disable force-unlock.
|
||||||
|
|
||||||
[HarmonyPatch(typeof(Cursor), nameof(Cursor.lockState), MethodType.Setter)]
|
[HarmonyPatch(typeof(Cursor), nameof(Cursor.lockState), MethodType.Setter)]
|
||||||
public class Cursor_lockState
|
public class Cursor_set_lockState
|
||||||
{
|
{
|
||||||
[HarmonyPrefix]
|
[HarmonyPrefix]
|
||||||
public static void Prefix(ref CursorLockMode value)
|
public static void Prefix(ref CursorLockMode value)
|
||||||
@ -170,19 +170,6 @@ namespace Explorer
|
|||||||
|
|
||||||
// Make it appear as though UnlockMouse is disabled to the rest of the application.
|
// Make it appear as though UnlockMouse is disabled to the rest of the application.
|
||||||
|
|
||||||
[HarmonyPatch(typeof(Cursor), nameof(Cursor.visible), MethodType.Getter)]
|
|
||||||
public class Cursor_get_visible
|
|
||||||
{
|
|
||||||
[HarmonyPostfix]
|
|
||||||
public static void Postfix(ref bool __result)
|
|
||||||
{
|
|
||||||
if (ShouldForceMouse)
|
|
||||||
{
|
|
||||||
__result = m_lastVisibleState;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[HarmonyPatch(typeof(Cursor), nameof(Cursor.lockState), MethodType.Getter)]
|
[HarmonyPatch(typeof(Cursor), nameof(Cursor.lockState), MethodType.Getter)]
|
||||||
public class Cursor_get_lockState
|
public class Cursor_get_lockState
|
||||||
{
|
{
|
||||||
@ -195,5 +182,18 @@ namespace Explorer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[HarmonyPatch(typeof(Cursor), nameof(Cursor.visible), MethodType.Getter)]
|
||||||
|
public class Cursor_get_visible
|
||||||
|
{
|
||||||
|
[HarmonyPostfix]
|
||||||
|
public static void Postfix(ref bool __result)
|
||||||
|
{
|
||||||
|
if (ShouldForceMouse)
|
||||||
|
{
|
||||||
|
__result = m_lastVisibleState;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -60,7 +60,9 @@
|
|||||||
<HintPath>..\..\..\..\..\Steam\steamapps\common\Hellpoint\MelonLoader\Managed\UnhollowerBaseLib.dll</HintPath>
|
<HintPath>..\..\..\..\..\Steam\steamapps\common\Hellpoint\MelonLoader\Managed\UnhollowerBaseLib.dll</HintPath>
|
||||||
<Private>False</Private>
|
<Private>False</Private>
|
||||||
</Reference>
|
</Reference>
|
||||||
|
|
||||||
<!-- Unity 2019 build (InputLegacyModule.dll) -->
|
<!-- Unity 2019 build (InputLegacyModule.dll) -->
|
||||||
|
|
||||||
<Reference Include="UnityEngine" Condition="'$(Configuration)'=='Debug'">
|
<Reference Include="UnityEngine" Condition="'$(Configuration)'=='Debug'">
|
||||||
<HintPath>..\..\..\Steam\steamapps\common\Hellpoint\MelonLoader\Managed\UnityEngine.dll</HintPath>
|
<HintPath>..\..\..\Steam\steamapps\common\Hellpoint\MelonLoader\Managed\UnityEngine.dll</HintPath>
|
||||||
<Private>False</Private>
|
<Private>False</Private>
|
||||||
@ -89,7 +91,9 @@
|
|||||||
<HintPath>..\..\..\Steam\steamapps\common\Hellpoint\MelonLoader\Managed\UnityEngine.UI.dll</HintPath>
|
<HintPath>..\..\..\Steam\steamapps\common\Hellpoint\MelonLoader\Managed\UnityEngine.UI.dll</HintPath>
|
||||||
<Private>False</Private>
|
<Private>False</Private>
|
||||||
</Reference>
|
</Reference>
|
||||||
|
|
||||||
<!-- Unity 2018 build (InputModule.dll) -->
|
<!-- Unity 2018 build (InputModule.dll) -->
|
||||||
|
|
||||||
<Reference Include="UnityEngine" Condition="'$(Configuration)'=='Release_Unity2018'">
|
<Reference Include="UnityEngine" Condition="'$(Configuration)'=='Release_Unity2018'">
|
||||||
<HintPath>..\..\..\Steam\steamapps\common\VRChat\MelonLoader\Managed\UnityEngine.dll</HintPath>
|
<HintPath>..\..\..\Steam\steamapps\common\VRChat\MelonLoader\Managed\UnityEngine.dll</HintPath>
|
||||||
<Private>False</Private>
|
<Private>False</Private>
|
||||||
@ -118,6 +122,7 @@
|
|||||||
<HintPath>..\..\..\Steam\steamapps\common\VRChat\MelonLoader\Managed\UnityEngine.UI.dll</HintPath>
|
<HintPath>..\..\..\Steam\steamapps\common\VRChat\MelonLoader\Managed\UnityEngine.UI.dll</HintPath>
|
||||||
<Private>False</Private>
|
<Private>False</Private>
|
||||||
</Reference>
|
</Reference>
|
||||||
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Include="CachedObjects\CacheDictionary.cs" />
|
<Compile Include="CachedObjects\CacheDictionary.cs" />
|
||||||
|
@ -8,6 +8,7 @@ using UnhollowerBaseLib;
|
|||||||
using UnhollowerRuntimeLib;
|
using UnhollowerRuntimeLib;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using BF = System.Reflection.BindingFlags;
|
using BF = System.Reflection.BindingFlags;
|
||||||
|
using MelonLoader;
|
||||||
|
|
||||||
namespace Explorer
|
namespace Explorer
|
||||||
{
|
{
|
||||||
@ -27,8 +28,9 @@ namespace Explorer
|
|||||||
{
|
{
|
||||||
if (!typeof(Il2CppSystem.Object).IsAssignableFrom(castTo)) return obj;
|
if (!typeof(Il2CppSystem.Object).IsAssignableFrom(castTo)) return obj;
|
||||||
|
|
||||||
var generic = m_tryCastMethodInfo.MakeGenericMethod(castTo);
|
return m_tryCastMethodInfo
|
||||||
return generic.Invoke(obj, null);
|
.MakeGenericMethod(castTo)
|
||||||
|
.Invoke(obj, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static string ExceptionToString(Exception e)
|
public static string ExceptionToString(Exception e)
|
||||||
@ -79,9 +81,17 @@ namespace Explorer
|
|||||||
|
|
||||||
public static bool IsList(Type t)
|
public static bool IsList(Type t)
|
||||||
{
|
{
|
||||||
return t.IsGenericType
|
if (t.IsGenericType)
|
||||||
&& t.GetGenericTypeDefinition() is Type typeDef
|
{
|
||||||
&& (typeDef.IsAssignableFrom(typeof(List<>)) || typeDef.IsAssignableFrom(typeof(Il2CppSystem.Collections.Generic.List<>)));
|
var generic = t.GetGenericTypeDefinition();
|
||||||
|
|
||||||
|
return generic.IsAssignableFrom(typeof(Il2CppSystem.Collections.Generic.List<>))
|
||||||
|
|| generic.IsAssignableFrom(typeof(Il2CppSystem.Collections.Generic.IList<>));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return t.IsAssignableFrom(typeof(Il2CppSystem.Collections.IList));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool IsDictionary(Type t)
|
public static bool IsDictionary(Type t)
|
||||||
@ -108,51 +118,36 @@ namespace Explorer
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Type GetActualType(object m_object)
|
public static Type GetActualType(object obj)
|
||||||
{
|
{
|
||||||
if (m_object == null) return null;
|
if (obj == null) return null;
|
||||||
|
|
||||||
if (m_object is Il2CppSystem.Object ilObject)
|
if (obj is Il2CppSystem.Object ilObject)
|
||||||
{
|
{
|
||||||
var iltype = ilObject.GetIl2CppType();
|
var ilTypeName = ilObject.GetIl2CppType().AssemblyQualifiedName;
|
||||||
return Type.GetType(iltype.AssemblyQualifiedName);
|
|
||||||
}
|
if (Type.GetType(ilTypeName) is Type t && !t.FullName.Contains("System.RuntimeType"))
|
||||||
else
|
{
|
||||||
{
|
return t;
|
||||||
return m_object.GetType();
|
}
|
||||||
|
|
||||||
|
return ilObject.GetType();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return obj.GetType();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Type[] GetAllBaseTypes(object m_object)
|
public static Type[] GetAllBaseTypes(object obj)
|
||||||
{
|
{
|
||||||
var list = new List<Type>();
|
var list = new List<Type>();
|
||||||
|
|
||||||
if (m_object is Il2CppSystem.Object ilObject)
|
var type = GetActualType(obj);
|
||||||
{
|
list.Add(type);
|
||||||
var ilType = ilObject.GetIl2CppType();
|
|
||||||
if (Type.GetType(ilType.AssemblyQualifiedName) is Type ilTypeToManaged)
|
|
||||||
{
|
|
||||||
list.Add(ilTypeToManaged);
|
|
||||||
|
|
||||||
while (ilType.BaseType != null)
|
while (type.BaseType != null)
|
||||||
{
|
|
||||||
ilType = ilType.BaseType;
|
|
||||||
if (Type.GetType(ilType.AssemblyQualifiedName) is Type ilBaseTypeToManaged)
|
|
||||||
{
|
|
||||||
list.Add(ilBaseTypeToManaged);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
var type = m_object.GetType();
|
type = type.BaseType;
|
||||||
list.Add(type);
|
list.Add(type);
|
||||||
while (type.BaseType != null)
|
|
||||||
{
|
|
||||||
type = type.BaseType;
|
|
||||||
list.Add(type);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return list.ToArray();
|
return list.ToArray();
|
||||||
|
@ -33,12 +33,14 @@ namespace Explorer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void HorizontalLine(Color color)
|
public static void HorizontalLine(Color _color, bool small = false)
|
||||||
{
|
{
|
||||||
var c = GUI.color;
|
var orig = GUI.color;
|
||||||
GUI.color = color;
|
|
||||||
GUILayout.Box(GUIContent.none, HorizontalBar, null);
|
GUI.color = _color;
|
||||||
GUI.color = c;
|
GUILayout.Box(GUIContent.none, !small ? HorizontalBar : HorizontalBarSmall, null);
|
||||||
|
|
||||||
|
GUI.color = orig;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static GUISkin _customSkin;
|
private static GUISkin _customSkin;
|
||||||
@ -46,8 +48,6 @@ namespace Explorer
|
|||||||
public static Texture2D m_nofocusTex;
|
public static Texture2D m_nofocusTex;
|
||||||
public static Texture2D m_focusTex;
|
public static Texture2D m_focusTex;
|
||||||
|
|
||||||
private static GUIStyle _horizBarStyle;
|
|
||||||
|
|
||||||
private static GUIStyle HorizontalBar
|
private static GUIStyle HorizontalBar
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
@ -63,6 +63,24 @@ namespace Explorer
|
|||||||
return _horizBarStyle;
|
return _horizBarStyle;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
private static GUIStyle _horizBarStyle;
|
||||||
|
|
||||||
|
private static GUIStyle HorizontalBarSmall
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (_horizBarSmallStyle == null)
|
||||||
|
{
|
||||||
|
_horizBarSmallStyle = new GUIStyle();
|
||||||
|
_horizBarSmallStyle.normal.background = Texture2D.whiteTexture;
|
||||||
|
_horizBarSmallStyle.margin = new RectOffset(0, 0, 2, 2);
|
||||||
|
_horizBarSmallStyle.fixedHeight = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return _horizBarSmallStyle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private static GUIStyle _horizBarSmallStyle;
|
||||||
|
|
||||||
private static GUISkin CreateWindowSkin()
|
private static GUISkin CreateWindowSkin()
|
||||||
{
|
{
|
||||||
|
@ -350,11 +350,24 @@ namespace Explorer
|
|||||||
m_pageOffset = 0;
|
m_pageOffset = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = offset; i < offset + m_limit && offset < m_searchResults.Count; i++)
|
for (int i = offset; i < offset + m_limit && i < m_searchResults.Count; i++)
|
||||||
{
|
{
|
||||||
var obj = m_searchResults[i];
|
var obj = m_searchResults[i];
|
||||||
|
|
||||||
UIHelpers.FastGameobjButton(obj.RefGameObject, obj.EnabledColor, obj.Label, obj.RefGameObject.activeSelf, SetTransformTarget, true, MainMenu.MainRect.width - 170);
|
if (obj.RefGameObject)
|
||||||
|
{
|
||||||
|
UIHelpers.FastGameobjButton(obj.RefGameObject,
|
||||||
|
obj.EnabledColor,
|
||||||
|
obj.Label,
|
||||||
|
obj.RefGameObject.activeSelf,
|
||||||
|
SetTransformTarget,
|
||||||
|
true,
|
||||||
|
MainMenu.MainRect.width - 170);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
GUILayout.Label("<i><color=red>Null or destroyed!</color></i>", null);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -13,10 +13,10 @@ namespace Explorer
|
|||||||
public class ReflectionWindow : UIWindow
|
public class ReflectionWindow : UIWindow
|
||||||
{
|
{
|
||||||
public override string Title => WindowManager.TabView
|
public override string Title => WindowManager.TabView
|
||||||
? $"<color=cyan>[R]</color> {ObjectType.Name}"
|
? $"<color=cyan>[R]</color> {TargetType.Name}"
|
||||||
: $"Reflection Inspector ({ObjectType.Name})";
|
: $"Reflection Inspector ({TargetType.Name})";
|
||||||
|
|
||||||
public Type ObjectType;
|
public Type TargetType;
|
||||||
|
|
||||||
private CacheObjectBase[] m_allCachedMembers;
|
private CacheObjectBase[] m_allCachedMembers;
|
||||||
private CacheObjectBase[] m_cachedMembersFiltered;
|
private CacheObjectBase[] m_cachedMembersFiltered;
|
||||||
@ -35,16 +35,11 @@ namespace Explorer
|
|||||||
public override void Init()
|
public override void Init()
|
||||||
{
|
{
|
||||||
var type = ReflectionHelpers.GetActualType(Target);
|
var type = ReflectionHelpers.GetActualType(Target);
|
||||||
if (type == null)
|
|
||||||
{
|
|
||||||
MelonLogger.Log($"Could not get underlying type for object..? Type: {Target?.GetType().Name}, ToString: {Target?.ToString()}");
|
|
||||||
DestroyWindow();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ObjectType = type;
|
TargetType = type;
|
||||||
|
|
||||||
var types = ReflectionHelpers.GetAllBaseTypes(Target);
|
var types = ReflectionHelpers.GetAllBaseTypes(Target);
|
||||||
|
|
||||||
CacheMembers(types);
|
CacheMembers(types);
|
||||||
|
|
||||||
if (Target is Il2CppSystem.Object ilObject)
|
if (Target is Il2CppSystem.Object ilObject)
|
||||||
@ -90,15 +85,15 @@ namespace Explorer
|
|||||||
|
|
||||||
private bool ShouldProcessMember(CacheObjectBase holder)
|
private bool ShouldProcessMember(CacheObjectBase holder)
|
||||||
{
|
{
|
||||||
if (m_filter != MemberTypes.All && m_filter != holder.MemberInfo?.MemberType) return false;
|
if (m_filter != MemberTypes.All && m_filter != holder.MemInfo?.MemberType) return false;
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(holder.ReflectionException) && m_hideFailedReflection) return false;
|
if (!string.IsNullOrEmpty(holder.ReflectionException) && m_hideFailedReflection) return false;
|
||||||
|
|
||||||
if (m_search == "" || holder.MemberInfo == null) return true;
|
if (m_search == "" || holder.MemInfo == null) return true;
|
||||||
|
|
||||||
return holder.FullName
|
var name = holder.MemInfo.DeclaringType.Name + "." + holder.MemInfo.Name;
|
||||||
.ToLower()
|
|
||||||
.Contains(m_search.ToLower());
|
return name.ToLower().Contains(m_search.ToLower());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CacheMembers(Type[] types)
|
private void CacheMembers(Type[] types)
|
||||||
@ -118,7 +113,7 @@ namespace Explorer
|
|||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
MelonLogger.Log("Exception getting members for type: " + declaringType.Name);
|
MelonLogger.Log($"Exception getting members for type: {declaringType.FullName}");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -140,25 +135,41 @@ namespace Explorer
|
|||||||
{
|
{
|
||||||
if (member.MemberType == MemberTypes.Field || member.MemberType == MemberTypes.Property || member.MemberType == MemberTypes.Method)
|
if (member.MemberType == MemberTypes.Field || member.MemberType == MemberTypes.Property || member.MemberType == MemberTypes.Method)
|
||||||
{
|
{
|
||||||
if (member.Name.Contains("Il2CppType") || member.Name.StartsWith("get_") || member.Name.StartsWith("set_"))
|
var name = $"{member.DeclaringType.Name}.{member.Name}";
|
||||||
|
|
||||||
|
// blacklist (should probably make a proper implementation)
|
||||||
|
if (name == "Type.DeclaringMethod" || member.Name.Contains("Il2CppType") || member.Name.StartsWith("get_") || member.Name.StartsWith("set_"))
|
||||||
|
{
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (member is MethodInfo mi)
|
||||||
|
{
|
||||||
|
name += " (";
|
||||||
|
foreach (var param in mi.GetParameters())
|
||||||
|
{
|
||||||
|
name += param.ParameterType.Name + ", ";
|
||||||
|
}
|
||||||
|
name += ")";
|
||||||
|
}
|
||||||
|
if (names.Contains(name))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var name = member.DeclaringType.Name + "." + member.Name;
|
var cached = CacheObjectBase.GetCacheObject(member, target);
|
||||||
if (names.Contains(name)) continue;
|
|
||||||
names.Add(name);
|
|
||||||
|
|
||||||
var cached = CacheObjectBase.GetCacheObject(null, member, target);
|
|
||||||
if (cached != null)
|
if (cached != null)
|
||||||
{
|
{
|
||||||
|
names.Add(name);
|
||||||
list.Add(cached);
|
list.Add(cached);
|
||||||
cached.ReflectionException = exception;
|
cached.ReflectionException = exception;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
MelonLogger.LogWarning($"Exception caching member {declaringType.Name}.{member.Name}!");
|
MelonLogger.LogWarning($"Exception caching member {name}!");
|
||||||
MelonLogger.Log(e.ToString());
|
MelonLogger.Log(e.ToString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -185,7 +196,7 @@ namespace Explorer
|
|||||||
}
|
}
|
||||||
|
|
||||||
GUILayout.BeginHorizontal(null);
|
GUILayout.BeginHorizontal(null);
|
||||||
GUILayout.Label("<b>Type:</b> <color=cyan>" + ObjectType.FullName + "</color>", new GUILayoutOption[] { GUILayout.Width(245f) });
|
GUILayout.Label("<b>Type:</b> <color=cyan>" + TargetType.FullName + "</color>", new GUILayoutOption[] { GUILayout.Width(245f) });
|
||||||
if (m_uObj)
|
if (m_uObj)
|
||||||
{
|
{
|
||||||
GUILayout.Label("Name: " + m_uObj.name, null);
|
GUILayout.Label("Name: " + m_uObj.name, null);
|
||||||
@ -285,11 +296,10 @@ namespace Explorer
|
|||||||
|
|
||||||
GUILayout.BeginVertical(GUI.skin.box, null);
|
GUILayout.BeginVertical(GUI.skin.box, null);
|
||||||
|
|
||||||
int index = 0;
|
|
||||||
var members = this.m_cachedMembersFiltered;
|
var members = this.m_cachedMembersFiltered;
|
||||||
int offsetIndex = (m_pageOffset * m_limitPerPage) + index;
|
int start = m_pageOffset * m_limitPerPage;
|
||||||
|
|
||||||
if (offsetIndex >= count)
|
if (start >= count)
|
||||||
{
|
{
|
||||||
int maxOffset = (int)Mathf.Ceil((float)(m_cachedMembersFiltered.Length / (decimal)m_limitPerPage)) - 1;
|
int maxOffset = (int)Mathf.Ceil((float)(m_cachedMembersFiltered.Length / (decimal)m_limitPerPage)) - 1;
|
||||||
if (m_pageOffset > maxOffset)
|
if (m_pageOffset > maxOffset)
|
||||||
@ -298,21 +308,25 @@ namespace Explorer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int j = offsetIndex; (j < offsetIndex + m_limitPerPage && j < members.Length); j++)
|
for (int j = start; (j < start + m_limitPerPage && j < members.Length); j++)
|
||||||
{
|
{
|
||||||
var holder = members[j];
|
var holder = members[j];
|
||||||
|
|
||||||
GUILayout.BeginHorizontal(new GUILayoutOption[] { GUILayout.Height(25) });
|
GUILayout.BeginHorizontal(new GUILayoutOption[] { GUILayout.Height(25) });
|
||||||
|
try
|
||||||
try
|
{
|
||||||
{
|
|
||||||
holder.Draw(rect, 180f);
|
holder.Draw(rect, 180f);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
GUILayout.EndHorizontal();
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
catch { }
|
|
||||||
|
|
||||||
GUILayout.EndHorizontal();
|
GUILayout.EndHorizontal();
|
||||||
|
|
||||||
index++;
|
// if not last element
|
||||||
|
if (!(j == (start + m_limitPerPage - 1) || j == (members.Length - 1)))
|
||||||
|
UIStyles.HorizontalLine(new Color(0.07f, 0.07f, 0.07f), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
GUILayout.EndVertical();
|
GUILayout.EndVertical();
|
||||||
|
@ -86,61 +86,6 @@ namespace Explorer
|
|||||||
|
|
||||||
// ========= Public Helpers =========
|
// ========= Public Helpers =========
|
||||||
|
|
||||||
public static bool IsMouseInWindow
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
if (!CppExplorer.ShowMenu)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var window in Windows)
|
|
||||||
{
|
|
||||||
if (RectContainsMouse(window.m_rect))
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return RectContainsMouse(MainMenu.MainRect);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static bool RectContainsMouse(Rect rect)
|
|
||||||
{
|
|
||||||
return rect.Contains(new Vector2(Input.mousePosition.x, Screen.height - Input.mousePosition.y));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static int NextWindowID()
|
|
||||||
{
|
|
||||||
return CurrentWindowID++;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Rect GetNewWindowRect()
|
|
||||||
{
|
|
||||||
return GetNewWindowRect(ref m_lastWindowRect);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Rect GetNewWindowRect(ref Rect lastRect)
|
|
||||||
{
|
|
||||||
Rect rect = new Rect(0, 0, 550, 700);
|
|
||||||
|
|
||||||
var mainrect = MainMenu.MainRect;
|
|
||||||
if (mainrect.x <= (Screen.width - mainrect.width - 100))
|
|
||||||
{
|
|
||||||
rect = new Rect(mainrect.x + mainrect.width + 20, mainrect.y, rect.width, rect.height);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (lastRect.x == rect.x)
|
|
||||||
{
|
|
||||||
rect = new Rect(rect.x + 25, rect.y + 25, rect.width, rect.height);
|
|
||||||
}
|
|
||||||
|
|
||||||
lastRect = rect;
|
|
||||||
|
|
||||||
return rect;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static UIWindow InspectObject(object obj, out bool createdNew)
|
public static UIWindow InspectObject(object obj, out bool createdNew)
|
||||||
{
|
{
|
||||||
createdNew = false;
|
createdNew = false;
|
||||||
@ -206,5 +151,62 @@ namespace Explorer
|
|||||||
|
|
||||||
return new_window;
|
return new_window;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// === Misc Helpers ===
|
||||||
|
|
||||||
|
public static bool IsMouseInWindow
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (!CppExplorer.ShowMenu)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var window in Windows)
|
||||||
|
{
|
||||||
|
if (RectContainsMouse(window.m_rect))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return RectContainsMouse(MainMenu.MainRect);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool RectContainsMouse(Rect rect)
|
||||||
|
{
|
||||||
|
return rect.Contains(new Vector2(Input.mousePosition.x, Screen.height - Input.mousePosition.y));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int NextWindowID()
|
||||||
|
{
|
||||||
|
return CurrentWindowID++;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Rect GetNewWindowRect()
|
||||||
|
{
|
||||||
|
return GetNewWindowRect(ref m_lastWindowRect);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Rect GetNewWindowRect(ref Rect lastRect)
|
||||||
|
{
|
||||||
|
Rect rect = new Rect(0, 0, 550, 700);
|
||||||
|
|
||||||
|
var mainrect = MainMenu.MainRect;
|
||||||
|
if (mainrect.x <= (Screen.width - mainrect.width - 100))
|
||||||
|
{
|
||||||
|
rect = new Rect(mainrect.x + mainrect.width + 20, mainrect.y, rect.width, rect.height);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lastRect.x == rect.x)
|
||||||
|
{
|
||||||
|
rect = new Rect(rect.x + 25, rect.y + 25, rect.width, rect.height);
|
||||||
|
}
|
||||||
|
|
||||||
|
lastRect = rect;
|
||||||
|
|
||||||
|
return rect;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user