almost done, just interactive unity structs and a few minor things to finish off.

This commit is contained in:
sinaioutlander
2020-11-16 21:21:04 +11:00
parent 4a1125cf1d
commit 91d5fc284f
26 changed files with 1020 additions and 232 deletions

BIN
lib/UnityEngine.UI.dll Normal file

Binary file not shown.

BIN
lib/UnityEngine.dll Normal file

Binary file not shown.

View File

@ -34,7 +34,7 @@ namespace UnityExplorer.CSConsole
public static char indentOpen = '{'; public static char indentOpen = '{';
public static char indentClose = '}'; public static char indentClose = '}';
private static readonly StringBuilder indentBuilder = new StringBuilder(); private static StringBuilder indentBuilder = new StringBuilder();
public static char[] delimiters = new[] public static char[] delimiters = new[]
{ {
@ -153,7 +153,7 @@ namespace UnityExplorer.CSConsole
public static string GetIndentForInput(string input, int indent, out int caretPosition) public static string GetIndentForInput(string input, int indent, out int caretPosition)
{ {
indentBuilder.Clear(); indentBuilder = new StringBuilder();
indent += 1; indent += 1;

View File

@ -61,11 +61,9 @@ namespace UnityExplorer
var obj = new GameObject( var obj = new GameObject(
"ExplorerBehaviour", "ExplorerBehaviour",
new Il2CppSystem.Type[] new Il2CppSystem.Type[] { Il2CppType.Of<ExplorerBehaviour>() }
{
Il2CppType.Of<ExplorerBehaviour>()
}
); );
obj.hideFlags = HideFlags.HideAndDontSave;
GameObject.DontDestroyOnLoad(obj); GameObject.DontDestroyOnLoad(obj);
new ExplorerCore(); new ExplorerCore();

View File

@ -77,7 +77,11 @@ namespace UnityExplorer.Helpers
// check if type is injected // check if type is injected
IntPtr classPtr = il2cpp_object_get_class(ilObject.Pointer); IntPtr classPtr = il2cpp_object_get_class(ilObject.Pointer);
if (RuntimeSpecificsStore.IsInjected(classPtr)) if (RuntimeSpecificsStore.IsInjected(classPtr))
return GetTypeByName(il2cppType.FullName); {
var typeByName = GetTypeByName(il2cppType.FullName);
if (typeByName != null)
return typeByName;
}
// this should be fine for all other il2cpp objects // this should be fine for all other il2cpp objects
var getType = GetMonoType(il2cppType); var getType = GetMonoType(il2cppType);
@ -252,7 +256,13 @@ namespace UnityExplorer.Helpers
public static string ExceptionToString(Exception e, bool innerMost = false) public static string ExceptionToString(Exception e, bool innerMost = false)
{ {
while (innerMost && e.InnerException != null) while (innerMost && e.InnerException != null)
{
#if CPP
if (e.InnerException is System.Runtime.CompilerServices.RuntimeWrappedException runtimeEx)
break;
#endif
e = e.InnerException; e = e.InnerException;
}
return e.GetType() + ", " + e.Message; return e.GetType() + ", " + e.Message;
} }

View File

@ -12,7 +12,7 @@ namespace UnityExplorer.Inspectors.Reflection
public class CacheEnumerated : CacheObjectBase public class CacheEnumerated : CacheObjectBase
{ {
public override Type FallbackType => ParentEnumeration.m_baseEntryType; public override Type FallbackType => ParentEnumeration.m_baseEntryType;
public override bool CanWrite => RefIList != null && ParentEnumeration.OwnerCacheObject.CanWrite; public override bool CanWrite => RefIList != null && ParentEnumeration.Owner.CanWrite;
public int Index { get; set; } public int Index { get; set; }
public IList RefIList { get; set; } public IList RefIList { get; set; }
@ -29,7 +29,7 @@ namespace UnityExplorer.Inspectors.Reflection
public override void CreateIValue(object value, Type fallbackType) public override void CreateIValue(object value, Type fallbackType)
{ {
IValue = InteractiveValue.Create(value, fallbackType); IValue = InteractiveValue.Create(value, fallbackType);
IValue.OwnerCacheObject = this; IValue.Owner = this;
} }
public override void SetValue() public override void SetValue()
@ -37,7 +37,7 @@ namespace UnityExplorer.Inspectors.Reflection
RefIList[Index] = IValue.Value; RefIList[Index] = IValue.Value;
ParentEnumeration.Value = RefIList; ParentEnumeration.Value = RefIList;
ParentEnumeration.OwnerCacheObject.SetValue(); ParentEnumeration.Owner.SetValue();
} }
internal override void ConstructUI() internal override void ConstructUI()

View File

@ -48,6 +48,10 @@ namespace UnityExplorer.Inspectors.Reflection
MemInfo = memberInfo; MemInfo = memberInfo;
DeclaringType = memberInfo.DeclaringType; DeclaringType = memberInfo.DeclaringType;
DeclaringInstance = declaringInstance; DeclaringInstance = declaringInstance;
#if CPP
if (DeclaringInstance != null)
DeclaringInstance = DeclaringInstance.Il2CppCast(DeclaringType);
#endif
} }
public static bool CanProcessArgs(ParameterInfo[] parameters) public static bool CanProcessArgs(ParameterInfo[] parameters)
@ -70,7 +74,7 @@ namespace UnityExplorer.Inspectors.Reflection
public override void CreateIValue(object value, Type fallbackType) public override void CreateIValue(object value, Type fallbackType)
{ {
IValue = InteractiveValue.Create(value, fallbackType); IValue = InteractiveValue.Create(value, fallbackType);
IValue.OwnerCacheObject = this; IValue.Owner = this;
IValue.m_mainContentParent = this.m_rightGroup; IValue.m_mainContentParent = this.m_rightGroup;
IValue.m_subContentParent = this.m_subContent; IValue.m_subContentParent = this.m_subContent;
} }
@ -384,7 +388,7 @@ namespace UnityExplorer.Inspectors.Reflection
argInputLayout.minWidth = 20; argInputLayout.minWidth = 20;
argInputLayout.minHeight = 25; argInputLayout.minHeight = 25;
argInputLayout.flexibleHeight = 0; argInputLayout.flexibleHeight = 0;
argInputLayout.layoutPriority = 2; //argInputLayout.layoutPriority = 2;
var argInput = argInputObj.GetComponent<InputField>(); var argInput = argInputObj.GetComponent<InputField>();
argInput.onValueChanged.AddListener((string val) => { m_argumentInput[i] = val; }); argInput.onValueChanged.AddListener((string val) => { m_argumentInput[i] = val; });

View File

@ -37,7 +37,8 @@ namespace UnityExplorer.Inspectors.Reflection
public virtual void Disable() public virtual void Disable()
{ {
m_mainContent?.SetActive(false); if (m_mainContent)
m_mainContent.SetActive(false);
} }
public void Destroy() public void Destroy()

View File

@ -41,7 +41,7 @@ namespace UnityExplorer.Inspectors.Reflection
public override void CreateIValue(object value, Type fallbackType) public override void CreateIValue(object value, Type fallbackType)
{ {
IValue = InteractiveValue.Create(value, fallbackType); IValue = InteractiveValue.Create(value, fallbackType);
IValue.OwnerCacheObject = this; IValue.Owner = this;
} }
#region UI CONSTRUCTION #region UI CONSTRUCTION

View File

@ -43,7 +43,16 @@ namespace UnityExplorer.Inspectors.Reflection
} }
else else
{ {
// todo write-only properties? if (FallbackType == typeof(string))
{
IValue.Value = "";
}
else if (FallbackType.IsPrimitive)
{
IValue.Value = Activator.CreateInstance(FallbackType);
}
m_evaluated = true;
ReflectionException = null;
} }
} }

View File

@ -18,61 +18,96 @@ namespace UnityExplorer.Inspectors.Reflection
public override bool WantInspectBtn => false; public override bool WantInspectBtn => false;
internal Toggle m_toggle; internal Toggle m_toggle;
internal Button m_applyBtn;
public override void OnValueUpdated() public override void OnValueUpdated()
{ {
base.OnValueUpdated(); base.OnValueUpdated();
}
if (!Value.IsNullOrDestroyed()) public override void RefreshUIForValue()
{
GetDefaultLabel();
if (Owner.HasEvaluated)
{ {
if (OwnerCacheObject.CanWrite) var val = (bool)Value;
if (Owner.CanWrite)
{ {
if (!m_toggle.gameObject.activeSelf) if (!m_toggle.gameObject.activeSelf)
m_toggle.gameObject.SetActive(true); m_toggle.gameObject.SetActive(true);
var val = (bool)Value; if (!m_applyBtn.gameObject.activeSelf)
if (m_toggle.isOn != val) m_applyBtn.gameObject.SetActive(true);
if (val != m_toggle.isOn)
m_toggle.isOn = val; m_toggle.isOn = val;
} }
RefreshUIElements(); var color = val
? "6bc981" // on
: "c96b6b"; // off
m_baseLabel.text = $"<color=#{color}>{val}</color>";
}
else
{
m_baseLabel.text = DefaultLabel;
} }
} }
internal void RefreshUIElements() public override void OnException(CacheMember member)
{ {
if (m_baseLabel) base.OnException(member);
{
var val = (bool)Value;
var color = val
? "00FF00" // on
: "FF0000"; // off
m_baseLabel.text = $"<color=#{color}>{val}</color> ({m_richValueType})"; if (Owner.CanWrite)
{
if (m_toggle.gameObject.activeSelf)
m_toggle.gameObject.SetActive(false);
if (m_applyBtn.gameObject.activeSelf)
m_applyBtn.gameObject.SetActive(false);
} }
} }
internal void OnToggleValueChanged(bool val) internal void OnToggleValueChanged(bool val)
{ {
Value = val; Value = val;
OwnerCacheObject.SetValue(); RefreshUIForValue();
RefreshUIElements();
} }
public override void ConstructUI(GameObject parent, GameObject subGroup) public override void ConstructUI(GameObject parent, GameObject subGroup)
{ {
base.ConstructUI(parent, subGroup); base.ConstructUI(parent, subGroup);
if (OwnerCacheObject.CanWrite) var baseLayout = m_baseLabel.gameObject.GetComponent<LayoutElement>();
baseLayout.flexibleWidth = 0;
baseLayout.minWidth = 50;
if (Owner.CanWrite)
{ {
var toggleObj = UIFactory.CreateToggle(m_valueContent, out m_toggle, out _, new Color(0.1f, 0.1f, 0.1f)); var toggleObj = UIFactory.CreateToggle(m_valueContent, out m_toggle, out _, new Color(0.1f, 0.1f, 0.1f));
toggleObj.SetActive(false);
var toggleLayout = toggleObj.AddComponent<LayoutElement>(); var toggleLayout = toggleObj.AddComponent<LayoutElement>();
toggleLayout.minWidth = 24; toggleLayout.minWidth = 24;
m_toggle.onValueChanged.AddListener(OnToggleValueChanged); m_toggle.onValueChanged.AddListener(OnToggleValueChanged);
m_baseLabel.transform.SetAsLastSibling(); m_baseLabel.transform.SetAsLastSibling();
var applyBtnObj = UIFactory.CreateButton(m_valueContent, new Color(0.2f, 0.2f, 0.2f));
var applyLayout = applyBtnObj.AddComponent<LayoutElement>();
applyLayout.minWidth = 50;
applyLayout.minHeight = 25;
applyLayout.flexibleWidth = 0;
m_applyBtn = applyBtnObj.GetComponent<Button>();
m_applyBtn.onClick.AddListener(() => { Owner.SetValue(); });
var applyText = applyBtnObj.GetComponentInChildren<Text>();
applyText.text = "Apply";
toggleObj.SetActive(false);
applyBtnObj.SetActive(false);
} }
} }
} }

View File

@ -9,6 +9,7 @@ using UnityExplorer.Config;
using UnityExplorer.Helpers; using UnityExplorer.Helpers;
using UnityExplorer.UI; using UnityExplorer.UI;
using UnityExplorer.UI.Shared; using UnityExplorer.UI.Shared;
using System.Reflection;
namespace UnityExplorer.Inspectors.Reflection namespace UnityExplorer.Inspectors.Reflection
{ {
@ -62,26 +63,22 @@ namespace UnityExplorer.Inspectors.Reflection
public override void OnValueUpdated() public override void OnValueUpdated()
{ {
RefIDictionary = Value as IDictionary;
if (Value != null)
{
if (m_subContentParent.activeSelf)
{
GetCacheEntries();
RefreshDisplay();
}
else
m_recacheWanted = true;
}
else
m_entries.Clear();
base.OnValueUpdated(); base.OnValueUpdated();
if (!Value.IsNullOrDestroyed())
{
RefIDictionary = Value as IDictionary;
UpdateLabel();
}
else
{
m_baseLabel.text = base.GetLabelForValue();
RefIDictionary = null;
}
if (m_subContentParent.activeSelf)
{
GetCacheEntries();
RefreshDisplay();
}
else
m_recacheWanted = true;
} }
internal void OnPageTurned() internal void OnPageTurned()
@ -89,15 +86,24 @@ namespace UnityExplorer.Inspectors.Reflection
RefreshDisplay(); RefreshDisplay();
} }
internal void UpdateLabel() public override void RefreshUIForValue()
{ {
string count = "?"; GetDefaultLabel();
if (m_recacheWanted && RefIDictionary != null)
count = RefIDictionary.Count.ToString();
else if (!m_recacheWanted)
count = m_entries.Count.ToString();
m_baseLabel.text = $"[{count}] {m_richValueType}"; if (Value != null)
{
string count = "?";
if (m_recacheWanted && RefIDictionary != null)
count = RefIDictionary.Count.ToString();
else if (!m_recacheWanted)
count = m_entries.Count.ToString();
m_baseLabel.text = $"[{count}] {m_richValueType}";
}
else
{
m_baseLabel.text = DefaultLabel;
}
} }
public void GetCacheEntries() public void GetCacheEntries()
@ -115,6 +121,11 @@ namespace UnityExplorer.Inspectors.Reflection
m_entries.Clear(); m_entries.Clear();
} }
#if CPP
if (RefIDictionary == null && !Value.IsNullOrDestroyed())
RefIDictionary = EnumerateWithReflection();
#endif
if (RefIDictionary != null) if (RefIDictionary != null)
{ {
int index = 0; int index = 0;
@ -194,12 +205,61 @@ namespace UnityExplorer.Inspectors.Reflection
{ {
m_recacheWanted = false; m_recacheWanted = false;
GetCacheEntries(); GetCacheEntries();
UpdateLabel(); RefreshUIForValue();
} }
RefreshDisplay(); RefreshDisplay();
} }
#region CPP fixes
#if CPP
// temp fix for Il2Cpp IDictionary until interfaces are fixed
private IDictionary EnumerateWithReflection()
{
var valueType = Value?.GetType() ?? FallbackType;
// get keys and values
var keys = valueType.GetProperty("Keys").GetValue(Value, null);
var values = valueType.GetProperty("Values").GetValue(Value, null);
// create lists to hold them
var keyList = new List<object>();
var valueList = new List<object>();
// store entries with reflection
EnumerateWithReflection(keys, keyList);
EnumerateWithReflection(values, valueList);
// make actual mono dictionary
var dict = (IDictionary)Activator.CreateInstance(typeof(Dictionary<,>)
.MakeGenericType(m_typeOfKeys, m_typeofValues));
// finally iterate into mono dictionary
for (int i = 0; i < keyList.Count; i++)
dict.Add(keyList[i], valueList[i]);
return dict;
}
private void EnumerateWithReflection(object collection, List<object> list)
{
// invoke GetEnumerator
var enumerator = collection.GetType().GetMethod("GetEnumerator").Invoke(collection, null);
// get the type of it
var enumeratorType = enumerator.GetType();
// reflect MoveNext and Current
var moveNext = enumeratorType.GetMethod("MoveNext");
var current = enumeratorType.GetProperty("Current");
// iterate
while ((bool)moveNext.Invoke(enumerator, null))
{
list.Add(current.GetValue(enumerator, null));
}
}
#endif
#endregion
#region UI CONSTRUCTION #region UI CONSTRUCTION
internal GameObject m_listContent; internal GameObject m_listContent;
@ -228,10 +288,10 @@ namespace UnityExplorer.Inspectors.Reflection
var scrollRect = scrollObj.GetComponent<RectTransform>(); var scrollRect = scrollObj.GetComponent<RectTransform>();
scrollRect.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, 0); scrollRect.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, 0);
m_listLayout = OwnerCacheObject.m_mainContent.GetComponent<LayoutElement>(); m_listLayout = Owner.m_mainContent.GetComponent<LayoutElement>();
m_listLayout.minHeight = 25; m_listLayout.minHeight = 25;
m_listLayout.flexibleHeight = 0; m_listLayout.flexibleHeight = 0;
OwnerCacheObject.m_mainRect.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, 25); Owner.m_mainRect.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, 25);
var scrollGroup = m_listContent.GetComponent<VerticalLayoutGroup>(); var scrollGroup = m_listContent.GetComponent<VerticalLayoutGroup>();
scrollGroup.childForceExpandHeight = true; scrollGroup.childForceExpandHeight = true;

View File

@ -0,0 +1,140 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
using UnityEngine.UI;
using UnityExplorer.Helpers;
using UnityExplorer.UI;
namespace UnityExplorer.Inspectors.Reflection
{
public class InteractiveEnum : InteractiveValue
{
internal static Dictionary<Type, KeyValuePair<int,string>[]> s_enumNamesCache = new Dictionary<Type, KeyValuePair<int, string>[]>();
public InteractiveEnum(object value, Type valueType) : base(value, valueType)
{
GetNames();
}
public override bool HasSubContent => true;
public override bool SubContentWanted => Owner.CanWrite;
public override bool WantInspectBtn => false;
internal KeyValuePair<int,string>[] m_values = new KeyValuePair<int, string>[0];
internal void GetNames()
{
var type = Value?.GetType() ?? FallbackType;
if (!s_enumNamesCache.ContainsKey(type))
{
// using GetValues not GetNames, to catch instances of weird enums (eg CameraClearFlags)
var values = Enum.GetValues(type);
var list = new List<KeyValuePair<int, string>>();
var set = new HashSet<string>();
foreach (var value in values)
{
var name = value.ToString();
if (set.Contains(name))
continue;
set.Add(name);
list.Add(new KeyValuePair<int, string>((int)value, name));
}
s_enumNamesCache.Add(type, list.ToArray());
}
m_values = s_enumNamesCache[type];
}
public override void OnValueUpdated()
{
base.OnValueUpdated();
}
public override void RefreshUIForValue()
{
base.RefreshUIForValue();
if (m_subContentConstructed)
{
m_dropdownText.text = Value?.ToString() ?? "<no value set>";
}
}
internal override void OnToggleSubcontent(bool toggle)
{
base.OnToggleSubcontent(toggle);
RefreshUIForValue();
}
private void SetValueFromDropdown()
{
var type = Value?.GetType() ?? FallbackType;
var value = Enum.Parse(type, m_dropdownText.text);
if (value != null)
{
Value = value;
Owner.SetValue();
RefreshUIForValue();
}
}
internal Dropdown m_dropdown;
internal Text m_dropdownText;
public override void ConstructUI(GameObject parent, GameObject subGroup)
{
base.ConstructUI(parent, subGroup);
}
public override void ConstructSubcontent()
{
base.ConstructSubcontent();
if (Owner.CanWrite)
{
var groupObj = UIFactory.CreateHorizontalGroup(m_subContentParent, new Color(1, 1, 1, 0));
var group = groupObj.GetComponent<HorizontalLayoutGroup>();
group.padding.top = 3;
group.padding.left = 3;
group.padding.right = 3;
group.padding.bottom = 3;
group.spacing = 5;
// apply button
var applyObj = UIFactory.CreateButton(groupObj, new Color(0.3f, 0.3f, 0.3f));
var applyLayout = applyObj.AddComponent<LayoutElement>();
applyLayout.minHeight = 25;
applyLayout.minWidth = 50;
var applyText = applyObj.GetComponentInChildren<Text>();
applyText.text = "Apply";
var applyBtn = applyObj.GetComponent<Button>();
applyBtn.onClick.AddListener(SetValueFromDropdown);
// dropdown
var dropdownObj = UIFactory.CreateDropdown(groupObj, out m_dropdown);
var dropLayout = dropdownObj.AddComponent<LayoutElement>();
dropLayout.minWidth = 150;
dropLayout.minHeight = 25;
dropLayout.flexibleWidth = 120;
foreach (var kvp in m_values)
{
m_dropdown.options.Add(new Dropdown.OptionData
{
text = $"{kvp.Key}: {kvp.Value}"
});
}
m_dropdownText = m_dropdown.transform.Find("Label").GetComponent<Text>();
}
}
}
}

View File

@ -2,6 +2,7 @@
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Reflection;
using System.Text; using System.Text;
using UnityEngine; using UnityEngine;
using UnityEngine.UI; using UnityEngine.UI;
@ -45,30 +46,28 @@ namespace UnityExplorer.Inspectors.Reflection
public override void OnValueUpdated() public override void OnValueUpdated()
{ {
RefIEnumerable = Value as IEnumerable;
RefIList = Value as IList;
if (Value != null)
{
if (m_subContentParent.activeSelf)
{
GetCacheEntries();
RefreshDisplay();
}
else
m_recacheWanted = true;
}
else
m_entries.Clear();
base.OnValueUpdated(); base.OnValueUpdated();
}
if (!Value.IsNullOrDestroyed()) public override void OnException(CacheMember member)
{ {
RefIEnumerable = Value as IEnumerable; // todo il2cpp base.OnException(member);
RefIList = Value as IList;
UpdateLabel();
}
else
{
m_baseLabel.text = base.GetLabelForValue();
RefIEnumerable = null;
RefIList = null;
}
if (m_subContentParent.activeSelf)
{
GetCacheEntries();
RefreshDisplay();
}
else
m_recacheWanted = true;
} }
private void OnPageTurned() private void OnPageTurned()
@ -76,15 +75,24 @@ namespace UnityExplorer.Inspectors.Reflection
RefreshDisplay(); RefreshDisplay();
} }
internal void UpdateLabel() public override void RefreshUIForValue()
{ {
string count = "?"; GetDefaultLabel();
if (m_recacheWanted && RefIList != null)
count = RefIList.Count.ToString();
else if (!m_recacheWanted)
count = m_entries.Count.ToString();
m_baseLabel.text = $"[{count}] {m_richValueType}"; if (Value != null)
{
string count = "?";
if (m_recacheWanted && RefIList != null)
count = RefIList.Count.ToString();
else if (!m_recacheWanted)
count = m_entries.Count.ToString();
m_baseLabel.text = $"[{count}] {m_richValueType}";
}
else
{
m_baseLabel.text = DefaultLabel;
}
} }
public void GetCacheEntries() public void GetCacheEntries()
@ -99,6 +107,11 @@ namespace UnityExplorer.Inspectors.Reflection
m_entries.Clear(); m_entries.Clear();
} }
#if CPP
if (RefIEnumerable == null && !Value.IsNullOrDestroyed())
RefIEnumerable = EnumerateWithReflection();
#endif
if (RefIEnumerable != null) if (RefIEnumerable != null)
{ {
int index = 0; int index = 0;
@ -155,12 +168,94 @@ namespace UnityExplorer.Inspectors.Reflection
{ {
m_recacheWanted = false; m_recacheWanted = false;
GetCacheEntries(); GetCacheEntries();
UpdateLabel(); RefreshUIForValue();
} }
RefreshDisplay(); RefreshDisplay();
} }
#region CPP Helpers
#if CPP
// some temp fixes for Il2Cpp IEnumerables until interfaces are fixed
private IEnumerable EnumerateWithReflection()
{
if (Value.IsNullOrDestroyed())
return null;
var genericDef = Value.GetType().GetGenericTypeDefinition();
if (genericDef == typeof(Il2CppSystem.Collections.Generic.List<>))
return CppListToMono(genericDef);
else if (genericDef == typeof(Il2CppSystem.Collections.Generic.HashSet<>))
return CppHashSetToMono();
else
return CppIListToMono();
}
// List<T>.ToArray()
private IEnumerable CppListToMono(Type genericTypeDef)
{
if (genericTypeDef == null) return null;
return genericTypeDef
.MakeGenericType(new Type[] { this.m_baseEntryType })
.GetMethod("ToArray")
.Invoke(Value, new object[0]) as IEnumerable;
}
// HashSet.GetEnumerator
private IEnumerable CppHashSetToMono()
{
var set = new HashSet<object>();
// invoke GetEnumerator
var enumerator = Value.GetType().GetMethod("GetEnumerator").Invoke(Value, null);
// get the type of it
var enumeratorType = enumerator.GetType();
// reflect MoveNext and Current
var moveNext = enumeratorType.GetMethod("MoveNext");
var current = enumeratorType.GetProperty("Current");
// iterate
while ((bool)moveNext.Invoke(enumerator, null))
set.Add(current.GetValue(enumerator));
return set;
}
// IList.Item
private IList CppIListToMono()
{
try
{
var genericType = typeof(List<>).MakeGenericType(new Type[] { this.m_baseEntryType });
var list = (IList)Activator.CreateInstance(genericType);
for (int i = 0; ; i++)
{
try
{
var itm = Value?.GetType()
.GetProperty("Item")
.GetValue(Value, new object[] { i });
list.Add(itm);
}
catch { break; }
}
return list;
}
catch (Exception e)
{
ExplorerCore.Log("Exception converting Il2Cpp IList to Mono IList: " + e.GetType() + ", " + e.Message);
return null;
}
}
#endif
#endregion
#region UI CONSTRUCTION #region UI CONSTRUCTION
internal GameObject m_listContent; internal GameObject m_listContent;
@ -187,10 +282,10 @@ namespace UnityExplorer.Inspectors.Reflection
var scrollRect = scrollObj.GetComponent<RectTransform>(); var scrollRect = scrollObj.GetComponent<RectTransform>();
scrollRect.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, 0); scrollRect.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, 0);
m_listLayout = OwnerCacheObject.m_mainContent.GetComponent<LayoutElement>(); m_listLayout = Owner.m_mainContent.GetComponent<LayoutElement>();
m_listLayout.minHeight = 25; m_listLayout.minHeight = 25;
m_listLayout.flexibleHeight = 0; m_listLayout.flexibleHeight = 0;
OwnerCacheObject.m_mainRect.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, 25); Owner.m_mainRect.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, 25);
var scrollGroup = m_listContent.GetComponent<VerticalLayoutGroup>(); var scrollGroup = m_listContent.GetComponent<VerticalLayoutGroup>();
scrollGroup.childForceExpandHeight = true; scrollGroup.childForceExpandHeight = true;

View File

@ -0,0 +1,148 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
using UnityEngine.UI;
using UnityExplorer.Helpers;
using UnityExplorer.UI;
namespace UnityExplorer.Inspectors.Reflection
{
public class InteractiveFlags : InteractiveEnum
{
public InteractiveFlags(object value, Type valueType) : base(value, valueType)
{
m_toggles = new Toggle[m_values.Length];
m_enabledFlags = new bool[m_values.Length];
}
public override bool HasSubContent => true;
public override bool SubContentWanted => Owner.CanWrite;
public override bool WantInspectBtn => false;
internal bool[] m_enabledFlags;
internal Toggle[] m_toggles;
public override void OnValueUpdated()
{
base.OnValueUpdated();
if (Owner.CanWrite)
{
var enabledNames = new List<string>();
var enabled = Value?.ToString().Split(',').Select(it => it.Trim());
if (enabled != null)
enabledNames.AddRange(enabled);
for (int i = 0; i < m_values.Length; i++)
{
m_enabledFlags[i] = enabledNames.Contains(m_values[i].Value);
}
}
}
public override void RefreshUIForValue()
{
//base.RefreshUIForValue();
GetDefaultLabel();
m_baseLabel.text = DefaultLabel;
if (m_subContentConstructed)
{
for (int i = 0; i < m_values.Length; i++)
{
var toggle = m_toggles[i];
if (toggle.isOn != m_enabledFlags[i])
toggle.isOn = m_enabledFlags[i];
}
}
}
private void SetValueFromToggles()
{
string val = "";
for (int i = 0; i < m_values.Length; i++)
{
if (m_enabledFlags[i])
{
if (val != "") val += ", ";
val += m_values[i].Value;
}
}
var type = Value?.GetType() ?? FallbackType;
Value = Enum.Parse(type, val);
RefreshUIForValue();
Owner.SetValue();
}
internal override void OnToggleSubcontent(bool toggle)
{
base.OnToggleSubcontent(toggle);
RefreshUIForValue();
}
public override void ConstructUI(GameObject parent, GameObject subGroup)
{
base.ConstructUI(parent, subGroup);
}
public override void ConstructSubcontent()
{
m_subContentConstructed = true;
if (Owner.CanWrite)
{
var groupObj = UIFactory.CreateVerticalGroup(m_subContentParent, new Color(1, 1, 1, 0));
var group = groupObj.GetComponent<VerticalLayoutGroup>();
group.childForceExpandHeight = true;
group.childForceExpandWidth = false;
group.childControlHeight = true;
group.childControlWidth = true;
group.padding.top = 3;
group.padding.left = 3;
group.padding.right = 3;
group.padding.bottom = 3;
group.spacing = 5;
// apply button
var applyObj = UIFactory.CreateButton(groupObj, new Color(0.3f, 0.3f, 0.3f));
var applyLayout = applyObj.AddComponent<LayoutElement>();
applyLayout.minHeight = 25;
applyLayout.minWidth = 50;
var applyText = applyObj.GetComponentInChildren<Text>();
applyText.text = "Apply";
var applyBtn = applyObj.GetComponent<Button>();
applyBtn.onClick.AddListener(SetValueFromToggles);
// toggles
for (int i = 0; i < m_values.Length; i++)
{
AddToggle(i, groupObj);
}
}
}
internal void AddToggle(int index, GameObject groupObj)
{
var value = m_values[index];
var toggleObj = UIFactory.CreateToggle(groupObj, out Toggle toggle, out Text text, new Color(0.1f, 0.1f, 0.1f));
var toggleLayout = toggleObj.AddComponent<LayoutElement>();
toggleLayout.minWidth = 100;
toggleLayout.flexibleWidth = 2000;
toggleLayout.minHeight = 25;
m_toggles[index] = toggle;
toggle.onValueChanged.AddListener((bool val) => { m_enabledFlags[index] = val; });
text.text = $"{value.Key}: {value.Value}";
}
}
}

View File

@ -2,7 +2,11 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Reflection;
using UnityEngine; using UnityEngine;
using UnityEngine.UI;
using UnityExplorer.Helpers;
using UnityExplorer.UI;
namespace UnityExplorer.Inspectors.Reflection namespace UnityExplorer.Inspectors.Reflection
{ {
@ -14,16 +18,108 @@ namespace UnityExplorer.Inspectors.Reflection
public override bool SubContentWanted => false; public override bool SubContentWanted => false;
public override bool WantInspectBtn => false; public override bool WantInspectBtn => false;
public override void ConstructUI(GameObject parent, GameObject subGroup)
{
base.ConstructUI(parent, subGroup);
}
public override void OnValueUpdated() public override void OnValueUpdated()
{ {
base.OnValueUpdated(); base.OnValueUpdated();
} }
public override void OnException(CacheMember member)
{
base.OnException(member);
if (m_valueInput.gameObject.activeSelf)
m_valueInput.gameObject.SetActive(false);
if (Owner.CanWrite)
{
if (m_applyBtn.gameObject.activeSelf)
m_applyBtn.gameObject.SetActive(false);
}
}
public override void RefreshUIForValue()
{
if (!Owner.HasEvaluated)
{
GetDefaultLabel();
m_baseLabel.text = DefaultLabel;
return;
}
m_baseLabel.text = UISyntaxHighlight.ParseFullSyntax(FallbackType, false);
m_valueInput.text = Value.ToString();
if (Owner.CanWrite)
{
if (!m_applyBtn.gameObject.activeSelf)
m_applyBtn.gameObject.SetActive(true);
}
if (!m_valueInput.gameObject.activeSelf)
m_valueInput.gameObject.SetActive(true);
}
public MethodInfo ParseMethod => m_parseMethod ?? (m_parseMethod = Value.GetType().GetMethod("Parse", new Type[] { typeof(string) }));
private MethodInfo m_parseMethod;
internal void OnApplyClicked()
{
try
{
Value = ParseMethod.Invoke(null, new object[] { m_valueInput.text });
Owner.SetValue();
RefreshUIForValue();
}
catch (Exception e)
{
ExplorerCore.LogWarning("Could not parse input! " + ReflectionHelpers.ExceptionToString(e, true));
}
}
internal InputField m_valueInput;
internal Button m_applyBtn;
public override void ConstructUI(GameObject parent, GameObject subGroup)
{
base.ConstructUI(parent, subGroup);
var labelLayout = m_baseLabel.gameObject.GetComponent<LayoutElement>();
labelLayout.minWidth = 50;
labelLayout.flexibleWidth = 0;
var inputObj = UIFactory.CreateInputField(m_valueContent);
var inputLayout = inputObj.AddComponent<LayoutElement>();
inputLayout.minWidth = 120;
inputLayout.minHeight = 25;
inputLayout.flexibleWidth = 0;
m_valueInput = inputObj.GetComponent<InputField>();
m_valueInput.gameObject.SetActive(false);
if (this.FallbackType == typeof(float)
|| this.FallbackType == typeof(double)
|| this.FallbackType == typeof(decimal))
{
m_valueInput.characterValidation = InputField.CharacterValidation.Decimal;
}
else
{
m_valueInput.characterValidation = InputField.CharacterValidation.Integer;
}
if (Owner.CanWrite)
{
var applyBtnObj = UIFactory.CreateButton(m_valueContent, new Color(0.2f, 0.2f, 0.2f));
var applyLayout = applyBtnObj.AddComponent<LayoutElement>();
applyLayout.minWidth = 50;
applyLayout.minHeight = 25;
applyLayout.flexibleWidth = 0;
m_applyBtn = applyBtnObj.GetComponent<Button>();
m_applyBtn.onClick.AddListener(OnApplyClicked);
var applyText = applyBtnObj.GetComponentInChildren<Text>();
applyText.text = "Apply";
}
}
} }
} }

View File

@ -2,7 +2,11 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Reflection;
using UnityEngine; using UnityEngine;
using UnityEngine.UI;
using UnityExplorer.Helpers;
using UnityExplorer.UI;
namespace UnityExplorer.Inspectors.Reflection namespace UnityExplorer.Inspectors.Reflection
{ {
@ -14,16 +18,132 @@ namespace UnityExplorer.Inspectors.Reflection
public override bool SubContentWanted => false; public override bool SubContentWanted => false;
public override bool WantInspectBtn => false; public override bool WantInspectBtn => false;
public override void ConstructUI(GameObject parent, GameObject subGroup)
{
base.ConstructUI(parent, subGroup);
}
public override void OnValueUpdated() public override void OnValueUpdated()
{ {
base.OnValueUpdated(); base.OnValueUpdated();
} }
public override void OnException(CacheMember member)
{
base.OnException(member);
if (m_hiddenObj.gameObject.activeSelf)
m_hiddenObj.gameObject.SetActive(false);
// m_baseLabel.text = DefaultLabel;
m_labelLayout.minWidth = 200;
m_labelLayout.flexibleWidth = 5000;
}
public override void RefreshUIForValue()
{
GetDefaultLabel(false);
if (!Owner.HasEvaluated)
{
m_baseLabel.text = DefaultLabel;
return;
}
if (!m_hiddenObj.gameObject.activeSelf)
m_hiddenObj.gameObject.SetActive(true);
m_baseLabel.text = m_richValueType;
if (Value != null)
{
var toString = Value.ToString();
if (toString.Length > 15000)
toString = toString.Substring(0, 15000);
m_valueInput.text = toString;
m_placeholderText.text = toString;
}
else
{
m_valueInput.text = "";
m_placeholderText.text = "null";
}
m_labelLayout.minWidth = 50;
m_labelLayout.flexibleWidth = 0;
}
internal void OnApplyClicked()
{
Value = m_valueInput.text;
Owner.SetValue();
}
internal InputField m_valueInput;
internal LayoutElement m_labelLayout;
internal GameObject m_hiddenObj;
internal Text m_placeholderText;
public override void ConstructUI(GameObject parent, GameObject subGroup)
{
base.ConstructUI(parent, subGroup);
GetDefaultLabel(false);
m_richValueType = UISyntaxHighlight.ParseFullSyntax(FallbackType, false);
m_labelLayout = m_baseLabel.gameObject.GetComponent<LayoutElement>();
m_hiddenObj = UIFactory.CreateLabel(m_valueContent, TextAnchor.MiddleLeft);
m_hiddenObj.SetActive(false);
var hiddenText = m_hiddenObj.GetComponent<Text>();
hiddenText.color = Color.clear;
hiddenText.fontSize = 14;
hiddenText.raycastTarget = false;
var hiddenFitter = m_hiddenObj.AddComponent<ContentSizeFitter>();
hiddenFitter.verticalFit = ContentSizeFitter.FitMode.PreferredSize;
var hiddenLayout = m_hiddenObj.AddComponent<LayoutElement>();
hiddenLayout.minHeight = 25;
hiddenLayout.flexibleHeight = 500;
hiddenLayout.minWidth = 250;
hiddenLayout.flexibleWidth = 9000;
var hiddenGroup = m_hiddenObj.AddComponent<HorizontalLayoutGroup>();
hiddenGroup.childForceExpandWidth = true;
hiddenGroup.childControlWidth = true;
hiddenGroup.childForceExpandHeight = true;
hiddenGroup.childControlHeight = true;
var inputObj = UIFactory.CreateInputField(m_hiddenObj, 14, 3);
var inputLayout = inputObj.AddComponent<LayoutElement>();
inputLayout.minWidth = 120;
inputLayout.minHeight = 25;
inputLayout.flexibleWidth = 5000;
inputLayout.flexibleHeight = 5000;
m_valueInput = inputObj.GetComponent<InputField>();
m_valueInput.lineType = InputField.LineType.MultiLineNewline;
m_placeholderText = m_valueInput.placeholder.GetComponent<Text>();
m_valueInput.onValueChanged.AddListener((string val) =>
{
hiddenText.text = val;
LayoutRebuilder.ForceRebuildLayoutImmediate(Owner.m_mainRect);
});
if (Owner.CanWrite)
{
var applyBtnObj = UIFactory.CreateButton(m_valueContent, new Color(0.2f, 0.2f, 0.2f));
var applyLayout = applyBtnObj.AddComponent<LayoutElement>();
applyLayout.minWidth = 50;
applyLayout.minHeight = 25;
applyLayout.flexibleWidth = 0;
var applyBtn = applyBtnObj.GetComponent<Button>();
applyBtn.onClick.AddListener(OnApplyClicked);
var applyText = applyBtnObj.GetComponentInChildren<Text>();
applyText.text = "Apply";
}
else
{
m_valueInput.readOnly = true;
}
}
} }
} }

View File

@ -21,6 +21,13 @@ namespace UnityExplorer.Inspectors.Reflection
return typeof(InteractiveString); return typeof(InteractiveString);
else if (type.IsPrimitive) else if (type.IsPrimitive)
return typeof(InteractiveNumber); return typeof(InteractiveNumber);
else if (typeof(Enum).IsAssignableFrom(type))
{
if (type.GetCustomAttributes(typeof(FlagsAttribute), true) is object[] attributes && attributes.Length > 0)
return typeof(InteractiveFlags);
else
return typeof(InteractiveEnum);
}
else if (typeof(Transform).IsAssignableFrom(type)) else if (typeof(Transform).IsAssignableFrom(type))
return typeof(InteractiveValue); return typeof(InteractiveValue);
else if (ReflectionHelpers.IsDictionary(type)) else if (ReflectionHelpers.IsDictionary(type))
@ -47,7 +54,7 @@ namespace UnityExplorer.Inspectors.Reflection
this.FallbackType = valueType; this.FallbackType = valueType;
} }
public CacheObjectBase OwnerCacheObject; public CacheObjectBase Owner;
public object Value { get; set; } public object Value { get; set; }
public readonly Type FallbackType; public readonly Type FallbackType;
@ -56,8 +63,8 @@ namespace UnityExplorer.Inspectors.Reflection
public virtual bool SubContentWanted => false; public virtual bool SubContentWanted => false;
public virtual bool WantInspectBtn => true; public virtual bool WantInspectBtn => true;
public string RichTextValue => m_richValue ?? GetLabelForValue(); public string DefaultLabel => m_defaultLabel ?? GetDefaultLabel();
internal string m_richValue; internal string m_defaultLabel;
internal string m_richValueType; internal string m_richValueType;
public MethodInfo ToStringMethod => m_toStringMethod ?? GetToStringMethod(); public MethodInfo ToStringMethod => m_toStringMethod ?? GetToStringMethod();
@ -73,6 +80,15 @@ namespace UnityExplorer.Inspectors.Reflection
m_valueContent.SetActive(false); m_valueContent.SetActive(false);
GameObject.Destroy(this.m_valueContent.gameObject); GameObject.Destroy(this.m_valueContent.gameObject);
} }
if (this.m_subContentParent && SubContentWanted)
{
for (int i = 0; i < this.m_subContentParent.transform.childCount; i++)
{
var child = m_subContentParent.transform.GetChild(i);
if (child)
GameObject.Destroy(child.gameObject);
}
}
} }
public virtual void OnValueUpdated() public virtual void OnValueUpdated()
@ -80,18 +96,28 @@ namespace UnityExplorer.Inspectors.Reflection
if (!m_UIConstructed) if (!m_UIConstructed)
ConstructUI(m_mainContentParent, m_subContentParent); ConstructUI(m_mainContentParent, m_subContentParent);
if (OwnerCacheObject is CacheMember ownerMember && !string.IsNullOrEmpty(ownerMember.ReflectionException)) if (Owner is CacheMember ownerMember && !string.IsNullOrEmpty(ownerMember.ReflectionException))
{ {
m_baseLabel.text = "<color=red>" + ownerMember.ReflectionException + "</color>"; OnException(ownerMember);
Value = null;
} }
else else
{ {
GetLabelForValue(); RefreshUIForValue();
m_baseLabel.text = RichTextValue;
} }
} }
public virtual void OnException(CacheMember member)
{
m_baseLabel.text = "<color=red>" + member.ReflectionException + "</color>";
Value = null;
}
public virtual void RefreshUIForValue()
{
GetDefaultLabel();
m_baseLabel.text = DefaultLabel;
}
public void RefreshElementsAfterUpdate() public void RefreshElementsAfterUpdate()
{ {
if (WantInspectBtn) if (WantInspectBtn)
@ -103,7 +129,7 @@ namespace UnityExplorer.Inspectors.Reflection
} }
bool subContentWanted = SubContentWanted; bool subContentWanted = SubContentWanted;
if (OwnerCacheObject is CacheMember cm && !cm.HasEvaluated) if (Owner is CacheMember cm && (!cm.HasEvaluated || !string.IsNullOrEmpty(cm.ReflectionException)))
subContentWanted = false; subContentWanted = false;
if (HasSubContent) if (HasSubContent)
@ -116,7 +142,7 @@ namespace UnityExplorer.Inspectors.Reflection
} }
} }
public virtual void ConstructSubcontent() public virtual void ConstructSubcontent()
{ {
m_subContentConstructed = true; m_subContentConstructed = true;
} }
@ -146,23 +172,21 @@ namespace UnityExplorer.Inspectors.Reflection
ConstructSubcontent(); ConstructSubcontent();
} }
public string GetLabelForValue() public string GetDefaultLabel(bool updateType = true)
{ {
var valueType = Value?.GetType() ?? this.FallbackType; var valueType = Value?.GetType() ?? this.FallbackType;
if (updateType)
m_richValueType = UISyntaxHighlight.ParseFullSyntax(valueType, true);
m_richValueType = UISyntaxHighlight.ParseFullSyntax(valueType, true); if (!Owner.HasEvaluated)
return m_defaultLabel = $"<i><color=grey>Not yet evaluated</color> ({m_richValueType})</i>";
if (OwnerCacheObject is CacheMember cm && !cm.HasEvaluated)
return $"<i><color=grey>Not yet evaluated</color> ({m_richValueType})</i>";
if (Value.IsNullOrDestroyed()) if (Value.IsNullOrDestroyed())
{ return m_defaultLabel = $"<color=grey>null</color> ({m_richValueType})";
return $"<color=grey>null</color> ({m_richValueType})";
}
string label; string label;
if (valueType == typeof(TextAsset) && Value is TextAsset textAsset) if (Value is TextAsset textAsset)
{ {
label = textAsset.text; label = textAsset.text;
@ -171,7 +195,7 @@ namespace UnityExplorer.Inspectors.Reflection
label = $"\"{label}\" {textAsset.name} ({m_richValueType})"; label = $"\"{label}\" {textAsset.name} ({m_richValueType})";
} }
else if (valueType == typeof(EventSystem)) else if (Value is EventSystem)
{ {
label = m_richValueType; label = m_richValueType;
} }
@ -204,7 +228,7 @@ namespace UnityExplorer.Inspectors.Reflection
} }
} }
return m_richValue = label; return m_defaultLabel = label;
} }
private MethodInfo GetToStringMethod() private MethodInfo GetToStringMethod()

View File

@ -617,7 +617,7 @@ namespace UnityExplorer.Inspectors
internal void ConstructMemberList() internal void ConstructMemberList()
{ {
var scrollobj = UIFactory.CreateScrollView(Content, out m_scrollContent, out m_sliderScroller, new Color(0.12f, 0.12f, 0.12f)); var scrollobj = UIFactory.CreateScrollView(Content, out m_scrollContent, out m_sliderScroller, new Color(0.08f, 0.08f, 0.08f));
m_scrollContentRect = m_scrollContent.GetComponent<RectTransform>(); m_scrollContentRect = m_scrollContent.GetComponent<RectTransform>();

View File

@ -29,8 +29,12 @@ namespace UnityExplorer.Inspectors
private const float UPDATE_INTERVAL = 1f; private const float UPDATE_INTERVAL = 1f;
private float m_timeOfLastSceneUpdate; private float m_timeOfLastSceneUpdate;
// private int m_currentSceneHandle = -1;
public static Scene DontDestroyScene => DontDestroyObject.scene;
internal Scene m_currentScene;
internal Scene[] m_currentScenes = new Scene[0];
private GameObject m_selectedSceneObject; private GameObject m_selectedSceneObject;
private int m_currentSceneHandle = -1;
private int m_lastCount; private int m_lastCount;
private Dropdown m_sceneDropdown; private Dropdown m_sceneDropdown;
@ -46,7 +50,6 @@ namespace UnityExplorer.Inspectors
private readonly List<Text> m_shortListTexts = new List<Text>(); private readonly List<Text> m_shortListTexts = new List<Text>();
private readonly List<Toggle> m_shortListToggles = new List<Toggle>(); private readonly List<Toggle> m_shortListToggles = new List<Toggle>();
public static int DontDestroyHandle => DontDestroyObject.scene.handle;
internal static GameObject DontDestroyObject internal static GameObject DontDestroyObject
{ {
@ -79,9 +82,13 @@ namespace UnityExplorer.Inspectors
if (!m_selectedSceneObject) if (!m_selectedSceneObject)
{ {
if (m_currentSceneHandle != -1) if (m_currentScene != default)
{ {
SetSceneObjectList(SceneUnstrip.GetRootGameObjects(m_currentSceneHandle)); #if CPP
SetSceneObjectList(SceneUnstrip.GetRootGameObjects(m_currentScene.handle));
#else
SetSceneObjectList(m_currentScene.GetRootGameObjects());
#endif
} }
} }
else else
@ -90,19 +97,21 @@ namespace UnityExplorer.Inspectors
} }
} }
public int GetSceneHandle(string sceneName) //#if CPP
{ // public int GetSceneHandle(string sceneName)
if (sceneName == "DontDestroyOnLoad") // {
return DontDestroyHandle; // if (sceneName == "DontDestroyOnLoad")
// return DontDestroyScene;
for (int i = 0; i < SceneManager.sceneCount; i++) // for (int i = 0; i < SceneManager.sceneCount; i++)
{ // {
var scene = SceneManager.GetSceneAt(i); // var scene = SceneManager.GetSceneAt(i);
if (scene.name == sceneName) // if (scene.name == sceneName)
return scene.handle; // return scene.handle;
} // }
return -1; // return -1;
} // }
//#endif
internal void OnSceneChange() internal void OnSceneChange()
{ {
@ -113,23 +122,21 @@ namespace UnityExplorer.Inspectors
private void RefreshSceneSelector() private void RefreshSceneSelector()
{ {
var names = new List<string>(); var names = new List<string>();
var handles = new List<int>(); var scenes = new List<Scene>();
for (int i = 0; i < SceneManager.sceneCount; i++) for (int i = 0; i < SceneManager.sceneCount; i++)
{ {
Scene scene = SceneManager.GetSceneAt(i); Scene scene = SceneManager.GetSceneAt(i);
int handle = scene.handle; if (scene == default)
if (scene == null || handle == -1 || string.IsNullOrEmpty(scene.name))
continue; continue;
handles.Add(handle); scenes.Add(scene);
names.Add(scene.name); names.Add(scene.name);
} }
names.Add("DontDestroyOnLoad"); names.Add("DontDestroyOnLoad");
handles.Add(DontDestroyHandle); scenes.Add(DontDestroyScene);
m_sceneDropdown.options.Clear(); m_sceneDropdown.options.Clear();
@ -141,20 +148,27 @@ namespace UnityExplorer.Inspectors
if (!names.Contains(m_sceneDropdownText.text)) if (!names.Contains(m_sceneDropdownText.text))
{ {
m_sceneDropdownText.text = names[0]; m_sceneDropdownText.text = names[0];
SetTargetScene(handles[0]); SetTargetScene(scenes[0]);
} }
m_currentScenes = scenes.ToArray();
} }
public void SetTargetScene(string name) => SetTargetScene(GetSceneHandle(name)); //#if CPP
// public void SetTargetScene(string name) => SetTargetScene(scene.handle);
//#endif
public void SetTargetScene(int handle) public void SetTargetScene(Scene scene)
{ {
if (handle == -1) if (scene == default)
return; return;
m_currentSceneHandle = handle; m_currentScene = scene;
#if CPP
GameObject[] rootObjs = SceneUnstrip.GetRootGameObjects(handle); GameObject[] rootObjs = SceneUnstrip.GetRootGameObjects(scene.handle);
#else
GameObject[] rootObjs = SceneUnstrip.GetRootGameObjects(scene);
#endif
SetSceneObjectList(rootObjs); SetSceneObjectList(rootObjs);
m_selectedSceneObject = null; m_selectedSceneObject = null;
@ -301,7 +315,7 @@ namespace UnityExplorer.Inspectors
m_lastCount = newCount; m_lastCount = newCount;
} }
#region UI CONSTRUCTION #region UI CONSTRUCTION
public void ConstructScenePane() public void ConstructScenePane()
{ {
@ -341,8 +355,8 @@ namespace UnityExplorer.Inspectors
void SetSceneFromDropdown(int val) void SetSceneFromDropdown(int val)
{ {
string scene = m_sceneDropdown.options[val].text; //string scene = m_sceneDropdown.options[val].text;
SetTargetScene(scene); SetTargetScene(m_currentScenes[val]);
} }
GameObject scenePathGroupObj = UIFactory.CreateHorizontalGroup(leftPane, new Color(1, 1, 1, 0f)); GameObject scenePathGroupObj = UIFactory.CreateHorizontalGroup(leftPane, new Color(1, 1, 1, 0f));
@ -375,7 +389,7 @@ namespace UnityExplorer.Inspectors
if (!m_selectedSceneObject || !m_selectedSceneObject.transform.parent?.gameObject) if (!m_selectedSceneObject || !m_selectedSceneObject.transform.parent?.gameObject)
{ {
m_selectedSceneObject = null; m_selectedSceneObject = null;
SetTargetScene(m_currentSceneHandle); SetTargetScene(m_currentScene);
} }
else else
{ {
@ -540,6 +554,6 @@ namespace UnityExplorer.Inspectors
inspectBtn.onClick.AddListener(() => { InspectorManager.Instance.Inspect(m_shortList[thisIndex]); }); inspectBtn.onClick.AddListener(() => { InspectorManager.Instance.Inspect(m_shortList[thisIndex]); });
} }
#endregion #endregion
} }
} }

View File

@ -2,6 +2,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using UnityExplorer.UI; using UnityExplorer.UI;
using UnityEngine; using UnityEngine;
using System;
#if CPP #if CPP
#endif #endif
@ -23,21 +24,54 @@ namespace UnityExplorer.Tests
public class TestClass public class TestClass
{ {
public bool ATestBoolMethod() => false;
public bool this[int index]
{
get => index % 2 == 0;
set => m_thisBool = value;
}
internal bool m_thisBool;
static int testInt;
public static List<string> ExceptionList
{
get
{
testInt++;
if (testInt % 2 == 0)
throw new Exception("its even");
else
return new List<string> { "one" };
}
}
static bool abool;
public static bool ATestExceptionBool
{
get
{
abool = !abool;
if (!abool)
throw new Exception("false");
else
return true;
}
}
public static string ExceptionString => throw new NotImplementedException();
public static string ANullString = null;
public static float ATestFloat = 420.69f;
public static int ATestInt = -1;
public static string ATestString = "hello world";
public static uint ATestUInt = 1u;
public static byte ATestByte = 255;
public static ulong AReadonlyUlong = 82934UL;
public static TestClass Instance => m_instance ?? (m_instance = new TestClass()); public static TestClass Instance => m_instance ?? (m_instance = new TestClass());
private static TestClass m_instance; private static TestClass m_instance;
public Dictionary<string, List<string>> AComboTest = new Dictionary<string, List<string>>
{
{
"key",
new List<string>
{
"1",
"2"
}
}
};
public object AmbigObject; public object AmbigObject;
public List<List<List<string>>> ANestedNestedList = new List<List<List<string>>> public List<List<List<string>>> ANestedNestedList = new List<List<List<string>>>
@ -76,7 +110,9 @@ namespace UnityExplorer.Tests
public static Sprite TestSprite; public static Sprite TestSprite;
#if CPP #if CPP
public static Il2CppSystem.Collections.Generic.HashSet<string> ILHashSetTest; public static Il2CppSystem.Collections.Generic.HashSet<string> CppHashSetTest;
public static Il2CppSystem.Collections.Generic.List<string> CppStringTest;
public static Il2CppSystem.Collections.IList CppIList;
#endif #endif
public TestClass() public TestClass()
@ -107,10 +143,14 @@ namespace UnityExplorer.Tests
//var dataToLoad = System.IO.File.ReadAllBytes(@"Mods\UnityExplorer\Tex_Nemundis_Nebula.png"); //var dataToLoad = System.IO.File.ReadAllBytes(@"Mods\UnityExplorer\Tex_Nemundis_Nebula.png");
//ExplorerCore.Log($"Tex load success: {TestTexture.LoadImage(dataToLoad, false)}"); //ExplorerCore.Log($"Tex load success: {TestTexture.LoadImage(dataToLoad, false)}");
ILHashSetTest = new Il2CppSystem.Collections.Generic.HashSet<string>(); CppHashSetTest = new Il2CppSystem.Collections.Generic.HashSet<string>();
ILHashSetTest.Add("1"); CppHashSetTest.Add("1");
ILHashSetTest.Add("2"); CppHashSetTest.Add("2");
ILHashSetTest.Add("3"); CppHashSetTest.Add("3");
CppStringTest = new Il2CppSystem.Collections.Generic.List<string>();
CppStringTest.Add("1");
CppStringTest.Add("2");
#endif #endif
} }

View File

@ -1,7 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using UnityExplorer.Unstrip; using UnityExplorer.Unstrip;
//using TMPro;
using UnityEngine; using UnityEngine;
using UnityEngine.UI; using UnityEngine.UI;
using UnityExplorer.Config; using UnityExplorer.Config;

View File

@ -295,8 +295,8 @@ namespace UnityExplorer.UI
else if (m_currentResizeType.HasFlag(ResizeTypes.Bottom)) else if (m_currentResizeType.HasFlag(ResizeTypes.Bottom))
anchorMin.y -= diffY; anchorMin.y -= diffY;
//Panel.anchorMin = new Vector2(anchorMin.x, anchorMin.y); Panel.anchorMin = new Vector2(anchorMin.x, anchorMin.y);
//Panel.anchorMax = new Vector2(anchorMax.x, anchorMax.y); Panel.anchorMax = new Vector2(anchorMax.x, anchorMax.y);
var newWidth = (anchorMax.x - anchorMin.x) * Screen.width; var newWidth = (anchorMax.x - anchorMin.x) * Screen.width;
var newHeight = (anchorMax.y - anchorMin.y) * Screen.height; var newHeight = (anchorMax.y - anchorMin.y) * Screen.height;
@ -346,4 +346,14 @@ namespace UnityExplorer.UI
#endregion #endregion
} }
// Just to allow Enum to do .HasFlag() in NET 3.5
public static class Net35FlagsEx
{
public static bool HasFlag(this Enum flags, Enum value)
{
ulong num = Convert.ToUInt64(value);
return (Convert.ToUInt64(flags) & num) == num;
}
}
} }

View File

@ -44,6 +44,9 @@ namespace UnityExplorer.UI
{ {
string ret = ""; string ret = "";
if (type == null)
return "????????????";
if (type.IsGenericParameter || (type.HasElementType && type.GetElementType().IsGenericParameter)) if (type.IsGenericParameter || (type.HasElementType && type.GetElementType().IsGenericParameter))
{ {
ret = $"<color={Enum}>{type.Name}</color>"; ret = $"<color={Enum}>{type.Name}</color>";

View File

@ -6,7 +6,7 @@
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{B21DBDE3-5D6F-4726-93AB-CC3CC68BAE7D}</ProjectGuid> <ProjectGuid>{B21DBDE3-5D6F-4726-93AB-CC3CC68BAE7D}</ProjectGuid>
<OutputType>Library</OutputType> <OutputType>Library</OutputType>
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion> <!--<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>-->
<AppDesignerFolder>Properties</AppDesignerFolder> <AppDesignerFolder>Properties</AppDesignerFolder>
<FileAlignment>512</FileAlignment> <FileAlignment>512</FileAlignment>
<Deterministic>true</Deterministic> <Deterministic>true</Deterministic>
@ -40,7 +40,7 @@
</NuGetPackageImportStamp> </NuGetPackageImportStamp>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release_ML_Cpp|AnyCPU' "> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release_ML_Cpp|AnyCPU' ">
<!-- <TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion> --> <TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
<OutputPath>..\Release\UnityExplorer.MelonLoader.Il2Cpp\</OutputPath> <OutputPath>..\Release\UnityExplorer.MelonLoader.Il2Cpp\</OutputPath>
<DefineConstants>CPP,ML</DefineConstants> <DefineConstants>CPP,ML</DefineConstants>
<AssemblyName>UnityExplorer.ML.IL2CPP</AssemblyName> <AssemblyName>UnityExplorer.ML.IL2CPP</AssemblyName>
@ -49,7 +49,7 @@
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release_ML_Mono|AnyCPU' "> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release_ML_Mono|AnyCPU' ">
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion> <TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
<OutputPath>..\Release\UnityExplorer.MelonLoader.Mono\</OutputPath> <OutputPath>..\Release\UnityExplorer.MelonLoader.Mono\</OutputPath>
<DefineConstants>MONO,ML</DefineConstants> <DefineConstants>MONO,ML</DefineConstants>
<AssemblyName>UnityExplorer.ML.Mono</AssemblyName> <AssemblyName>UnityExplorer.ML.Mono</AssemblyName>
@ -59,15 +59,16 @@
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release_BIE_Cpp|AnyCPU' "> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release_BIE_Cpp|AnyCPU' ">
<!-- <TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion> --> <TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
<OutputPath>..\Release\UnityExplorer.BepInEx.Il2Cpp\</OutputPath> <OutputPath>..\Release\UnityExplorer.BepInEx.Il2Cpp\</OutputPath>
<DefineConstants>CPP,BIE</DefineConstants> <DefineConstants>CPP,BIE</DefineConstants>
<AssemblyName>UnityExplorer.BIE.IL2CPP</AssemblyName>
<IsCpp>true</IsCpp> <IsCpp>true</IsCpp>
<IsMelonLoader>false</IsMelonLoader> <IsMelonLoader>false</IsMelonLoader>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release_BIE_Mono|AnyCPU' "> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release_BIE_Mono|AnyCPU' ">
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion> <TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
<OutputPath>..\Release\UnityExplorer.BepInEx.Mono\</OutputPath> <OutputPath>..\Release\UnityExplorer.BepInEx.Mono\</OutputPath>
<DefineConstants>MONO,BIE</DefineConstants> <DefineConstants>MONO,BIE</DefineConstants>
<AssemblyName>UnityExplorer.BIE.Mono</AssemblyName> <AssemblyName>UnityExplorer.BIE.Mono</AssemblyName>
@ -89,7 +90,6 @@
</Reference> </Reference>
</ItemGroup> </ItemGroup>
<!-- Universal Mono UnityEngine.dll ref (v5.3) --> <!-- Universal Mono UnityEngine.dll ref (v5.3) -->
<!--
<ItemGroup Condition="'$(IsCpp)'=='false'"> <ItemGroup Condition="'$(IsCpp)'=='false'">
<Reference Include="UnityEngine"> <Reference Include="UnityEngine">
<HintPath>..\lib\UnityEngine.dll</HintPath> <HintPath>..\lib\UnityEngine.dll</HintPath>
@ -99,26 +99,14 @@
<HintPath>..\lib\UnityEngine.UI.dll</HintPath> <HintPath>..\lib\UnityEngine.UI.dll</HintPath>
<Private>False</Private> <Private>False</Private>
</Reference> </Reference>
<Reference Include="Unity.TextMeshPro"> </ItemGroup>
<HintPath>..\lib\Unity.TextMeshPro.dll</HintPath>
<Private>False</Private>
</Reference>
</ItemGroup>-->
<!-- MelonLoader Mono refs --> <!-- MelonLoader Mono refs -->
<ItemGroup Condition="'$(IsMelonLoader)|$(IsCpp)'=='true|false'"> <ItemGroup Condition="'$(IsMelonLoader)|$(IsCpp)'=='true|false'">
<Reference Include="MelonLoader.ModHandler"> <Reference Include="MelonLoader.ModHandler">
<HintPath>$(MLMonoGameFolder)\MelonLoader\MelonLoader.ModHandler.dll</HintPath> <HintPath>$(MLMonoGameFolder)\MelonLoader\MelonLoader.ModHandler.dll</HintPath>
<Private>False</Private> <Private>False</Private>
</Reference> </Reference>
<!-- <Reference Include="Unity.TextMeshPro"> <!--<Reference Include="UnityEngine">
<HintPath>$(MLMonoManagedFolder)\Unity.TextMeshPro.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="UnityEngine.TextCoreModule">
<HintPath>$(MLMonoManagedFolder)\UnityEngine.TextCoreModule.dll</HintPath>
<Private>False</Private>
</Reference> -->
<Reference Include="UnityEngine">
<HintPath>$(MLMonoManagedFolder)\UnityEngine.dll</HintPath> <HintPath>$(MLMonoManagedFolder)\UnityEngine.dll</HintPath>
<Private>False</Private> <Private>False</Private>
</Reference> </Reference>
@ -149,7 +137,7 @@
<Reference Include="UnityEngine.ImageConversionModule"> <Reference Include="UnityEngine.ImageConversionModule">
<HintPath>$(MLMonoManagedFolder)\UnityEngine.ImageConversionModule.dll</HintPath> <HintPath>$(MLMonoManagedFolder)\UnityEngine.ImageConversionModule.dll</HintPath>
<Private>False</Private> <Private>False</Private>
</Reference> </Reference>-->
</ItemGroup> </ItemGroup>
<!-- BepInEx Mono refs --> <!-- BepInEx Mono refs -->
<ItemGroup Condition="'$(IsMelonLoader)|$(IsCpp)'=='false|false'"> <ItemGroup Condition="'$(IsMelonLoader)|$(IsCpp)'=='false|false'">
@ -161,15 +149,7 @@
<HintPath>$(BIEMonoGameFolder)\BepInEx\core\0Harmony.dll</HintPath> <HintPath>$(BIEMonoGameFolder)\BepInEx\core\0Harmony.dll</HintPath>
<Private>False</Private> <Private>False</Private>
</Reference> </Reference>
<!-- <Reference Include="Unity.TextMeshPro"> <!--<Reference Include="UnityEngine.AssetBundleModule">
<HintPath>$(BIEMonoManagedFolder)\Unity.TextMeshPro.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="UnityEngine.TextCoreModule">
<HintPath>$(BIEMonoManagedFolder)\UnityEngine.TextCoreModule.dll</HintPath>
<Private>False</Private>
</Reference> -->
<Reference Include="UnityEngine.AssetBundleModule">
<HintPath>$(BIEMonoManagedFolder)\UnityEngine.AssetBundleModule.dll</HintPath> <HintPath>$(BIEMonoManagedFolder)\UnityEngine.AssetBundleModule.dll</HintPath>
<Private>False</Private> <Private>False</Private>
</Reference> </Reference>
@ -204,7 +184,7 @@
<Reference Include="UnityEngine.ImageConversionModule"> <Reference Include="UnityEngine.ImageConversionModule">
<HintPath>$(BIEMonoManagedFolder)\UnityEngine.ImageConversionModule.dll</HintPath> <HintPath>$(BIEMonoManagedFolder)\UnityEngine.ImageConversionModule.dll</HintPath>
<Private>False</Private> <Private>False</Private>
</Reference> </Reference>-->
</ItemGroup> </ItemGroup>
<!-- MelonLoader Il2Cpp refs --> <!-- MelonLoader Il2Cpp refs -->
<ItemGroup Condition="'$(IsMelonLoader)|$(IsCpp)'=='true|true'"> <ItemGroup Condition="'$(IsMelonLoader)|$(IsCpp)'=='true|true'">
@ -346,7 +326,9 @@
<Compile Include="Inspectors\GameObjects\GameObjectControls.cs" /> <Compile Include="Inspectors\GameObjects\GameObjectControls.cs" />
<Compile Include="Inspectors\Reflection\InteractiveValue\InteractiveBool.cs" /> <Compile Include="Inspectors\Reflection\InteractiveValue\InteractiveBool.cs" />
<Compile Include="Inspectors\Reflection\InteractiveValue\InteractiveDictionary.cs" /> <Compile Include="Inspectors\Reflection\InteractiveValue\InteractiveDictionary.cs" />
<Compile Include="Inspectors\Reflection\InteractiveValue\InteractiveEnum.cs" />
<Compile Include="Inspectors\Reflection\InteractiveValue\InteractiveEnumerable.cs" /> <Compile Include="Inspectors\Reflection\InteractiveValue\InteractiveEnumerable.cs" />
<Compile Include="Inspectors\Reflection\InteractiveValue\InteractiveFlags.cs" />
<Compile Include="Inspectors\Reflection\InteractiveValue\InteractiveNumber.cs" /> <Compile Include="Inspectors\Reflection\InteractiveValue\InteractiveNumber.cs" />
<Compile Include="Inspectors\Reflection\InteractiveValue\InteractiveString.cs" /> <Compile Include="Inspectors\Reflection\InteractiveValue\InteractiveString.cs" />
<Compile Include="UI\ForceUnlockCursor.cs" /> <Compile Include="UI\ForceUnlockCursor.cs" />

View File

@ -11,26 +11,26 @@ namespace UnityExplorer.Unstrip
#if MONO #if MONO
public static GameObject[] GetRootGameObjects(Scene scene) => scene.GetRootGameObjects(); public static GameObject[] GetRootGameObjects(Scene scene) => scene.GetRootGameObjects();
public static GameObject[] GetRootGameObjects(int handle) //public static GameObject[] GetRootGameObjects(int handle)
{ //{
Scene scene = default; // Scene scene = default;
if (handle == SceneExplorer.DontDestroyHandle) // if (handle == SceneExplorer.DontDestroyHandle)
scene = SceneExplorer.DontDestroyObject.scene; // scene = SceneExplorer.DontDestroyObject.scene;
else // else
{ // {
for (int i = 0; i < SceneManager.sceneCount; i++) // for (int i = 0; i < SceneManager.sceneCount; i++)
{ // {
var iscene = SceneManager.GetSceneAt(i); // var iscene = SceneManager.GetSceneAt(i);
if (iscene.handle == handle) // if (iscene.handle == handle)
scene = iscene; // scene = iscene;
} // }
} // }
if (scene != default && scene.handle != -1) // if (scene != default && scene.handle != -1)
return scene.GetRootGameObjects(); // return scene.GetRootGameObjects();
return new GameObject[0]; // return new GameObject[0];
} //}
#endif #endif
#if CPP #if CPP