mirror of
https://github.com/sinai-dev/UnityExplorer.git
synced 2025-06-22 16:42:38 +08:00
Compare commits
10 Commits
Author | SHA1 | Date | |
---|---|---|---|
3628f3db31 | |||
d39fea69c3 | |||
95e8b3aa58 | |||
b68145385c | |||
2310f2f7ce | |||
2cc403ad17 | |||
c2d9b9b59e | |||
c748be7bcc | |||
09dae6f1d3 | |||
6ca117b070 |
@ -6,7 +6,7 @@
|
||||
An in-game explorer and a suite of debugging tools for <a href="https://docs.unity3d.com/Manual/IL2CPP.html">IL2CPP</a> and <b>Mono</b> Unity games, to aid with modding development.
|
||||
</p>
|
||||
|
||||
## Releases [](../../releases/latest) [](../../releases) [](../../releases/latest)
|
||||
## Releases [](../../releases/latest) [](../../releases) [](../../releases/latest)
|
||||
|
||||
| Mod Loader | IL2CPP | Mono |
|
||||
| ----------- | ------ | ---- |
|
||||
|
@ -20,7 +20,7 @@ namespace UnityExplorer.Core.CSharp
|
||||
|
||||
public static void StartCoroutine(IEnumerator ienumerator)
|
||||
{
|
||||
RuntimeProvider.Instance.StartConsoleCoroutine(ienumerator);
|
||||
RuntimeProvider.Instance.StartCoroutine(ienumerator);
|
||||
}
|
||||
|
||||
public static void AddUsing(string directive)
|
||||
|
@ -17,18 +17,18 @@ namespace UnityExplorer.Core.Config
|
||||
// See the UnityExplorer.Loader namespace for the implementations.
|
||||
public static ConfigHandler Handler { get; private set; }
|
||||
|
||||
public static ConfigElement<KeyCode> Main_Menu_Toggle;
|
||||
public static ConfigElement<bool> Force_Unlock_Mouse;
|
||||
public static ConfigElement<int> Default_Page_Limit;
|
||||
public static ConfigElement<string> Default_Output_Path;
|
||||
public static ConfigElement<bool> Log_Unity_Debug;
|
||||
public static ConfigElement<bool> Hide_On_Startup;
|
||||
public static ConfigElement<KeyCode> Main_Menu_Toggle;
|
||||
public static ConfigElement<bool> Force_Unlock_Mouse;
|
||||
public static ConfigElement<MenuPages> Default_Tab;
|
||||
public static ConfigElement<int> Default_Page_Limit;
|
||||
public static ConfigElement<string> Default_Output_Path;
|
||||
public static ConfigElement<bool> Log_Unity_Debug;
|
||||
public static ConfigElement<bool> Hide_On_Startup;
|
||||
|
||||
public static ConfigElement<string> Last_Window_Anchors;
|
||||
public static ConfigElement<string> Last_Window_Position;
|
||||
public static ConfigElement<int> Last_Active_Tab;
|
||||
public static ConfigElement<bool> Last_DebugConsole_State;
|
||||
public static ConfigElement<bool> Last_SceneExplorer_State;
|
||||
public static ConfigElement<string> Last_Window_Anchors;
|
||||
public static ConfigElement<string> Last_Window_Position;
|
||||
public static ConfigElement<bool> Last_DebugConsole_State;
|
||||
public static ConfigElement<bool> Last_SceneExplorer_State;
|
||||
|
||||
internal static readonly Dictionary<string, IConfigElement> ConfigElements = new Dictionary<string, IConfigElement>();
|
||||
|
||||
@ -44,7 +44,6 @@ namespace UnityExplorer.Core.Config
|
||||
SceneExplorer.OnToggleShow += SceneExplorer_OnToggleShow;
|
||||
PanelDragger.OnFinishResize += PanelDragger_OnFinishResize;
|
||||
PanelDragger.OnFinishDrag += PanelDragger_OnFinishDrag;
|
||||
MainMenu.OnActiveTabChanged += MainMenu_OnActiveTabChanged;
|
||||
DebugConsole.OnToggleShow += DebugConsole_OnToggleShow;
|
||||
|
||||
InitConsoleCallback();
|
||||
@ -66,6 +65,10 @@ namespace UnityExplorer.Core.Config
|
||||
"Should UnityExplorer be hidden on startup?",
|
||||
false);
|
||||
|
||||
Default_Tab = new ConfigElement<MenuPages>("Default Tab",
|
||||
"The default menu page when starting the game.",
|
||||
MenuPages.Home);
|
||||
|
||||
Log_Unity_Debug = new ConfigElement<bool>("Log Unity Debug",
|
||||
"Should UnityEngine.Debug.Log messages be printed to UnityExplorer's log?",
|
||||
false);
|
||||
@ -94,11 +97,6 @@ namespace UnityExplorer.Core.Config
|
||||
DEFAULT_WINDOW_POSITION,
|
||||
true);
|
||||
|
||||
Last_Active_Tab = new ConfigElement<int>("Last_Active_Tab",
|
||||
"For internal use, the last active tab index.",
|
||||
0,
|
||||
true);
|
||||
|
||||
Last_DebugConsole_State = new ConfigElement<bool>("Last_DebugConsole_State",
|
||||
"For internal use, the collapsed state of the Debug Console.",
|
||||
true,
|
||||
@ -115,6 +113,7 @@ namespace UnityExplorer.Core.Config
|
||||
private static void PanelDragger_OnFinishResize(RectTransform rect)
|
||||
{
|
||||
Last_Window_Anchors.Value = rect.RectAnchorsToString();
|
||||
PanelDragger_OnFinishDrag(rect);
|
||||
}
|
||||
|
||||
private static void PanelDragger_OnFinishDrag(RectTransform rect)
|
||||
@ -122,11 +121,6 @@ namespace UnityExplorer.Core.Config
|
||||
Last_Window_Position.Value = rect.RectPositionToString();
|
||||
}
|
||||
|
||||
private static void MainMenu_OnActiveTabChanged(int page)
|
||||
{
|
||||
Last_Active_Tab.Value = page;
|
||||
}
|
||||
|
||||
private static void DebugConsole_OnToggleShow(bool showing)
|
||||
{
|
||||
Last_DebugConsole_State.Value = showing;
|
||||
|
@ -89,23 +89,19 @@ namespace UnityExplorer.Core.Input
|
||||
|
||||
public static void SetEventSystem()
|
||||
{
|
||||
// temp disabled for new InputSystem
|
||||
if (InputManager.CurrentType == InputType.InputSystem)
|
||||
return;
|
||||
|
||||
// Disable current event system object
|
||||
if (m_lastEventSystem || EventSystem.current)
|
||||
if (EventSystem.current && EventSystem.current != UIManager.EventSys)
|
||||
{
|
||||
if (!m_lastEventSystem)
|
||||
m_lastEventSystem = EventSystem.current;
|
||||
|
||||
m_lastEventSystem = EventSystem.current;
|
||||
m_lastEventSystem.enabled = false;
|
||||
}
|
||||
|
||||
// Set to our current system
|
||||
m_settingEventSystem = true;
|
||||
EventSystem.current = UIManager.EventSys;
|
||||
UIManager.EventSys.enabled = true;
|
||||
EventSystem.current = UIManager.EventSys;
|
||||
InputManager.ActivateUIModule();
|
||||
m_settingEventSystem = false;
|
||||
}
|
||||
@ -115,7 +111,7 @@ namespace UnityExplorer.Core.Input
|
||||
if (InputManager.CurrentType == InputType.InputSystem)
|
||||
return;
|
||||
|
||||
if (m_lastEventSystem)
|
||||
if (m_lastEventSystem && m_lastEventSystem.gameObject.activeSelf)
|
||||
{
|
||||
m_lastEventSystem.enabled = true;
|
||||
|
||||
@ -152,13 +148,16 @@ namespace UnityExplorer.Core.Input
|
||||
|
||||
public static void Prefix_EventSystem_set_current(ref EventSystem value)
|
||||
{
|
||||
if (!m_settingEventSystem)
|
||||
if (!m_settingEventSystem && value != UIManager.EventSys)
|
||||
{
|
||||
m_lastEventSystem = value;
|
||||
m_lastInputModule = value?.currentInputModule;
|
||||
|
||||
if (ShouldActuallyUnlock)
|
||||
{
|
||||
value = UIManager.EventSys;
|
||||
value.enabled = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -188,4 +187,4 @@ namespace UnityExplorer.Core.Input
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -15,9 +15,7 @@ namespace UnityExplorer.Core.Input
|
||||
|
||||
BaseInputModule UIModule { get; }
|
||||
|
||||
PointerEventData InputPointerEvent { get; }
|
||||
|
||||
void AddUIInputModule();
|
||||
void ActivateModule();
|
||||
}
|
||||
}
|
||||
}
|
@ -27,7 +27,6 @@ namespace UnityExplorer.Core.Input
|
||||
public static bool GetMouseButton(int btn) => m_inputModule.GetMouseButton(btn);
|
||||
|
||||
public static BaseInputModule UIInput => m_inputModule.UIModule;
|
||||
public static PointerEventData InputPointerEvent => m_inputModule.InputPointerEvent;
|
||||
|
||||
public static void ActivateUIModule() => m_inputModule.ActivateModule();
|
||||
|
||||
@ -52,7 +51,7 @@ namespace UnityExplorer.Core.Input
|
||||
|
||||
if (m_inputModule == null)
|
||||
{
|
||||
ExplorerCore.LogWarning("Could not find any Input module!");
|
||||
ExplorerCore.LogWarning("Could not find any Input Module Type!");
|
||||
m_inputModule = new NoInput();
|
||||
CurrentType = InputType.None;
|
||||
}
|
||||
|
@ -5,6 +5,8 @@ using UnityEngine;
|
||||
using UnityEngine.EventSystems;
|
||||
using UnityExplorer.UI;
|
||||
using System.Collections.Generic;
|
||||
using UnityExplorer.UI.Inspectors;
|
||||
using System.Linq;
|
||||
|
||||
namespace UnityExplorer.Core.Input
|
||||
{
|
||||
@ -83,16 +85,28 @@ namespace UnityExplorer.Core.Input
|
||||
}
|
||||
|
||||
internal static Dictionary<KeyCode, object> ActualKeyDict = new Dictionary<KeyCode, object>();
|
||||
internal static Dictionary<string, string> enumNameFixes = new Dictionary<string, string>
|
||||
{
|
||||
{ "Control", "Ctrl" },
|
||||
{ "Return", "Enter" },
|
||||
{ "Alpha", "Digit" },
|
||||
{ "Keypad", "Numpad" },
|
||||
{ "Numlock", "NumLock" },
|
||||
{ "Print", "PrintScreen" },
|
||||
{ "BackQuote", "Backquote" }
|
||||
};
|
||||
|
||||
internal object GetActualKey(KeyCode key)
|
||||
{
|
||||
if (!ActualKeyDict.ContainsKey(key))
|
||||
{
|
||||
var s = key.ToString();
|
||||
if (s.Contains("Control"))
|
||||
s = s.Replace("Control", "Ctrl");
|
||||
else if (s.Contains("Return"))
|
||||
s = "Enter";
|
||||
try
|
||||
{
|
||||
if (enumNameFixes.First(it => s.Contains(it.Key)) is KeyValuePair<string, string> entry)
|
||||
s = s.Replace(entry.Key, entry.Value);
|
||||
}
|
||||
catch { }
|
||||
|
||||
var parsed = Enum.Parse(TKey, s);
|
||||
var actualKey = m_kbIndexer.GetValue(CurrentKeyboard, new object[] { parsed });
|
||||
@ -131,41 +145,70 @@ namespace UnityExplorer.Core.Input
|
||||
|
||||
// UI Input
|
||||
|
||||
//public Type TInputSystemUIInputModule
|
||||
// => m_tUIInputModule
|
||||
// ?? (m_tUIInputModule = ReflectionHelpers.GetTypeByName("UnityEngine.InputSystem.UI.InputSystemUIInputModule"));
|
||||
//internal Type m_tUIInputModule;
|
||||
public Type TInputSystemUIInputModule
|
||||
=> m_tUIInputModule
|
||||
?? (m_tUIInputModule = ReflectionUtility.GetTypeByName("UnityEngine.InputSystem.UI.InputSystemUIInputModule"));
|
||||
internal Type m_tUIInputModule;
|
||||
|
||||
public BaseInputModule UIModule => null; // m_newInputModule;
|
||||
//internal BaseInputModule m_newInputModule;
|
||||
|
||||
public PointerEventData InputPointerEvent => null;
|
||||
public BaseInputModule UIModule => m_newInputModule;
|
||||
internal BaseInputModule m_newInputModule;
|
||||
|
||||
public void AddUIInputModule()
|
||||
{
|
||||
// if (TInputSystemUIInputModule != null)
|
||||
// {
|
||||
//#if CPP
|
||||
// // m_newInputModule = UIManager.CanvasRoot.AddComponent(Il2CppType.From(TInputSystemUIInputModule)).TryCast<BaseInputModule>();
|
||||
//#else
|
||||
// m_newInputModule = (BaseInputModule)UIManager.CanvasRoot.AddComponent(TInputSystemUIInputModule);
|
||||
//#endif
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// ExplorerCore.LogWarning("New input system: Could not find type by name 'UnityEngine.InputSystem.UI.InputSystemUIInputModule'");
|
||||
// }
|
||||
if (TInputSystemUIInputModule == null)
|
||||
{
|
||||
ExplorerCore.LogWarning("Unable to find UI Input Module Type, Input will not work!");
|
||||
return;
|
||||
}
|
||||
|
||||
var assetType = ReflectionUtility.GetTypeByName("UnityEngine.InputSystem.InputActionAsset");
|
||||
m_newInputModule = RuntimeProvider.Instance.AddComponent<BaseInputModule>(UIManager.CanvasRoot, TInputSystemUIInputModule);
|
||||
var asset = RuntimeProvider.Instance.CreateScriptable(assetType);
|
||||
|
||||
inputExtensions = ReflectionUtility.GetTypeByName("UnityEngine.InputSystem.InputActionSetupExtensions");
|
||||
|
||||
var addMap = inputExtensions.GetMethod("AddActionMap", new Type[] { assetType, typeof(string) });
|
||||
var map = addMap.Invoke(null, new object[] { asset, "UI" });
|
||||
|
||||
CreateAction(map, "point", new[] { "<Mouse>/position" }, "point");
|
||||
CreateAction(map, "click", new[] { "<Mouse>/leftButton" }, "leftClick");
|
||||
CreateAction(map, "rightClick", new[] { "<Mouse>/rightButton" }, "rightClick");
|
||||
CreateAction(map, "scrollWheel", new[] { "<Mouse>/scroll" }, "scrollWheel");
|
||||
|
||||
UI_Enable = map.GetType().GetMethod("Enable");
|
||||
UI_Enable.Invoke(map, new object[0]);
|
||||
UI_ActionMap = map;
|
||||
}
|
||||
|
||||
private Type inputExtensions;
|
||||
private object UI_ActionMap;
|
||||
private MethodInfo UI_Enable;
|
||||
|
||||
private void CreateAction(object map, string actionName, string[] bindings, string propertyName)
|
||||
{
|
||||
var addAction = inputExtensions.GetMethod("AddAction");
|
||||
var pointAction = addAction.Invoke(null, new object[] { map, actionName, default, null, null, null, null, null });
|
||||
|
||||
var inputActionType = pointAction.GetType();
|
||||
var addBinding = inputExtensions.GetMethod("AddBinding",
|
||||
new Type[] { inputActionType, typeof(string), typeof(string), typeof(string), typeof(string) });
|
||||
|
||||
foreach (string binding in bindings)
|
||||
addBinding.Invoke(null, new object[] { pointAction, binding, null, null, null });
|
||||
|
||||
var inputRef = ReflectionUtility.GetTypeByName("UnityEngine.InputSystem.InputActionReference")
|
||||
.GetMethod("Create")
|
||||
.Invoke(null, new object[] { pointAction });
|
||||
|
||||
TInputSystemUIInputModule
|
||||
.GetProperty(propertyName)
|
||||
.SetValue(m_newInputModule, inputRef, null);
|
||||
}
|
||||
|
||||
public void ActivateModule()
|
||||
{
|
||||
//#if CPP
|
||||
// // m_newInputModule.ActivateModule();
|
||||
//#else
|
||||
// m_newInputModule.ActivateModule();
|
||||
//#endif
|
||||
|
||||
|
||||
m_newInputModule.ActivateModule();
|
||||
UI_Enable.Invoke(UI_ActionMap, new object[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -44,13 +44,6 @@ namespace UnityExplorer.Core.Input
|
||||
public BaseInputModule UIModule => m_inputModule;
|
||||
internal StandaloneInputModule m_inputModule;
|
||||
|
||||
public PointerEventData InputPointerEvent =>
|
||||
#if CPP
|
||||
m_inputModule.m_InputPointerEvent;
|
||||
#else
|
||||
null;
|
||||
#endif
|
||||
|
||||
public void AddUIInputModule()
|
||||
{
|
||||
m_inputModule = UIManager.CanvasRoot.gameObject.AddComponent<StandaloneInputModule>();
|
||||
@ -61,4 +54,4 @@ namespace UnityExplorer.Core.Input
|
||||
m_inputModule.ActivateModule();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -16,8 +16,7 @@ namespace UnityExplorer.Core.Input
|
||||
public bool GetMouseButtonDown(int btn) => false;
|
||||
|
||||
public BaseInputModule UIModule => null;
|
||||
public PointerEventData InputPointerEvent => null;
|
||||
public void ActivateModule() { }
|
||||
public void AddUIInputModule() { }
|
||||
}
|
||||
}
|
||||
}
|
@ -43,6 +43,9 @@ namespace UnityExplorer
|
||||
public static object Cast(this object obj, Type castTo)
|
||||
=> ReflectionProvider.Instance.Cast(obj, castTo);
|
||||
|
||||
public static T TryCast<T>(this object obj)
|
||||
=> ReflectionProvider.Instance.TryCast<T>(obj);
|
||||
|
||||
/// <summary>
|
||||
/// Check if the provided Type is assignable to IEnumerable.
|
||||
/// </summary>
|
||||
@ -201,10 +204,9 @@ namespace UnityExplorer
|
||||
{
|
||||
while (e.InnerException != null)
|
||||
{
|
||||
#if CPP
|
||||
if (e.InnerException is System.Runtime.CompilerServices.RuntimeWrappedException)
|
||||
break;
|
||||
#endif
|
||||
|
||||
e = e.InnerException;
|
||||
}
|
||||
}
|
||||
|
@ -50,12 +50,47 @@ namespace UnityExplorer.Core.Runtime.Il2Cpp
|
||||
ExplorerCore.Log(condition, type, true);
|
||||
}
|
||||
|
||||
public override void StartConsoleCoroutine(IEnumerator routine)
|
||||
public override void StartCoroutine(IEnumerator routine)
|
||||
{
|
||||
Il2CppCoroutine.Start(routine);
|
||||
}
|
||||
|
||||
// Unity API Handlers
|
||||
public override void Update()
|
||||
{
|
||||
Il2CppCoroutine.Process();
|
||||
}
|
||||
|
||||
public override T AddComponent<T>(GameObject obj, Type type)
|
||||
{
|
||||
return obj.AddComponent(Il2CppType.From(type)).TryCast<T>();
|
||||
}
|
||||
|
||||
public override ScriptableObject CreateScriptable(Type type)
|
||||
{
|
||||
return ScriptableObject.CreateInstance(Il2CppType.From(type));
|
||||
}
|
||||
|
||||
public override void GraphicRaycast(GraphicRaycaster raycaster, PointerEventData data, List<RaycastResult> list)
|
||||
{
|
||||
var il2cppList = new Il2CppSystem.Collections.Generic.List<RaycastResult>();
|
||||
|
||||
raycaster.Raycast(data, il2cppList);
|
||||
|
||||
if (il2cppList.Count > 0)
|
||||
list.AddRange(il2cppList.ToArray());
|
||||
}
|
||||
|
||||
public override bool IsReferenceEqual(object a, object b)
|
||||
{
|
||||
if (a.TryCast<UnityEngine.Object>() is UnityEngine.Object ua)
|
||||
{
|
||||
var ub = b.TryCast<UnityEngine.Object>();
|
||||
if (ub && ua.m_CachedPtr == ub.m_CachedPtr)
|
||||
return true;
|
||||
}
|
||||
|
||||
return base.IsReferenceEqual(a, b);
|
||||
}
|
||||
|
||||
// LayerMask.LayerToName
|
||||
|
||||
@ -162,6 +197,26 @@ namespace UnityExplorer.Core.Runtime.Il2Cpp
|
||||
|
||||
return colors;
|
||||
}
|
||||
|
||||
public override void FindSingleton(string[] possibleNames, Type type, BF flags, List<object> instances)
|
||||
{
|
||||
PropertyInfo pi;
|
||||
foreach (var name in possibleNames)
|
||||
{
|
||||
pi = type.GetProperty(name, flags);
|
||||
if (pi != null)
|
||||
{
|
||||
var instance = pi.GetValue(null, null);
|
||||
if (instance != null)
|
||||
{
|
||||
instances.Add(instance);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
base.FindSingleton(possibleNames, type, flags, instances);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -32,6 +32,43 @@ namespace UnityExplorer.Core.Runtime.Il2Cpp
|
||||
return Il2CppCast(obj, castTo);
|
||||
}
|
||||
|
||||
public override T TryCast<T>(object obj)
|
||||
{
|
||||
try
|
||||
{
|
||||
return (T)Il2CppCast(obj, typeof(T));
|
||||
}
|
||||
catch
|
||||
{
|
||||
return default;
|
||||
}
|
||||
}
|
||||
|
||||
public override void BoxStringToType(ref object value, Type castTo)
|
||||
{
|
||||
if (castTo == typeof(Il2CppSystem.String))
|
||||
value = (Il2CppSystem.String)(value as string);
|
||||
else
|
||||
value = (Il2CppSystem.Object)(value as string);
|
||||
}
|
||||
|
||||
public override string UnboxString(object value)
|
||||
{
|
||||
if (value is string s)
|
||||
return s;
|
||||
|
||||
s = null;
|
||||
// strings boxed as Il2CppSystem.Objects can behave weirdly.
|
||||
// GetActualType will find they are a string, but if its boxed
|
||||
// then we need to unbox it like this...
|
||||
if (value is Il2CppSystem.Object cppObject)
|
||||
s = cppObject.ToString();
|
||||
else if (value is Il2CppSystem.String cppString)
|
||||
s = cppString;
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
public override string ProcessTypeNameInString(Type type, string theString, ref string typeName)
|
||||
{
|
||||
if (!Il2CppTypeNotNull(type))
|
||||
@ -154,21 +191,24 @@ namespace UnityExplorer.Core.Runtime.Il2Cpp
|
||||
/// <returns>The object, as the type (or a normal C# object) if successful or the input value if not.</returns>
|
||||
public static object Il2CppCast(object obj, Type castTo)
|
||||
{
|
||||
if (!(obj is Il2CppSystem.Object ilObj))
|
||||
if (!(obj is Il2CppSystem.Object cppObj))
|
||||
return obj;
|
||||
|
||||
if (!Il2CppTypeNotNull(castTo, out IntPtr castToPtr))
|
||||
return obj;
|
||||
|
||||
IntPtr castFromPtr = il2cpp_object_get_class(ilObj.Pointer);
|
||||
IntPtr castFromPtr = il2cpp_object_get_class(cppObj.Pointer);
|
||||
|
||||
if (!il2cpp_class_is_assignable_from(castToPtr, castFromPtr))
|
||||
return null;
|
||||
|
||||
if (RuntimeSpecificsStore.IsInjected(castToPtr))
|
||||
return UnhollowerBaseLib.Runtime.ClassInjectorBase.GetMonoObjectFromIl2CppPointer(ilObj.Pointer);
|
||||
return UnhollowerBaseLib.Runtime.ClassInjectorBase.GetMonoObjectFromIl2CppPointer(cppObj.Pointer);
|
||||
|
||||
return Activator.CreateInstance(castTo, ilObj.Pointer);
|
||||
if (castTo == typeof(string))
|
||||
return cppObj.ToString();
|
||||
|
||||
return Activator.CreateInstance(castTo, cppObj.Pointer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -319,6 +359,112 @@ namespace UnityExplorer.Core.Runtime.Il2Cpp
|
||||
return false;
|
||||
}
|
||||
|
||||
internal static readonly Dictionary<Type, MethodInfo> s_getEnumeratorMethods = new Dictionary<Type, MethodInfo>();
|
||||
|
||||
internal static readonly Dictionary<Type, EnumeratorInfo> s_enumeratorInfos = new Dictionary<Type, EnumeratorInfo>();
|
||||
|
||||
internal class EnumeratorInfo
|
||||
{
|
||||
internal MethodInfo moveNext;
|
||||
internal PropertyInfo current;
|
||||
}
|
||||
|
||||
public override IEnumerable EnumerateEnumerable(object value)
|
||||
{
|
||||
if (value == null)
|
||||
return null;
|
||||
|
||||
var cppEnumerable = (value as Il2CppSystem.Object)?.TryCast<Il2CppSystem.Collections.IEnumerable>();
|
||||
if (cppEnumerable != null)
|
||||
{
|
||||
var type = value.GetType();
|
||||
if (!s_getEnumeratorMethods.ContainsKey(type))
|
||||
s_getEnumeratorMethods.Add(type, type.GetMethod("GetEnumerator"));
|
||||
|
||||
var enumerator = s_getEnumeratorMethods[type].Invoke(value, null);
|
||||
var enumeratorType = enumerator.GetType();
|
||||
|
||||
if (!s_enumeratorInfos.ContainsKey(enumeratorType))
|
||||
{
|
||||
s_enumeratorInfos.Add(enumeratorType, new EnumeratorInfo
|
||||
{
|
||||
current = enumeratorType.GetProperty("Current"),
|
||||
moveNext = enumeratorType.GetMethod("MoveNext"),
|
||||
});
|
||||
}
|
||||
var info = s_enumeratorInfos[enumeratorType];
|
||||
|
||||
// iterate
|
||||
var list = new List<object>();
|
||||
while ((bool)info.moveNext.Invoke(enumerator, null))
|
||||
list.Add(info.current.GetValue(enumerator));
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public override IDictionary EnumerateDictionary(object value, Type typeOfKeys, Type typeOfValues)
|
||||
{
|
||||
var valueType = ReflectionUtility.GetActualType(value);
|
||||
|
||||
var keyList = new List<object>();
|
||||
var valueList = new List<object>();
|
||||
|
||||
var hashtable = value.Cast(typeof(Il2CppSystem.Collections.Hashtable)) as Il2CppSystem.Collections.Hashtable;
|
||||
|
||||
if (hashtable != null)
|
||||
{
|
||||
EnumerateCppHashtable(hashtable, keyList, valueList);
|
||||
}
|
||||
else
|
||||
{
|
||||
var keys = valueType.GetProperty("Keys").GetValue(value, null);
|
||||
var values = valueType.GetProperty("Values").GetValue(value, null);
|
||||
|
||||
EnumerateCppIDictionary(keys, keyList);
|
||||
EnumerateCppIDictionary(values, valueList);
|
||||
}
|
||||
|
||||
var dict = Activator.CreateInstance(typeof(Dictionary<,>)
|
||||
.MakeGenericType(typeOfKeys, typeOfValues))
|
||||
as IDictionary;
|
||||
|
||||
for (int i = 0; i < keyList.Count; i++)
|
||||
dict.Add(keyList[i], valueList[i]);
|
||||
|
||||
return dict;
|
||||
}
|
||||
|
||||
private void EnumerateCppIDictionary(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));
|
||||
}
|
||||
}
|
||||
|
||||
private void EnumerateCppHashtable(Il2CppSystem.Collections.Hashtable hashtable, List<object> keys, List<object> values)
|
||||
{
|
||||
for (int i = 0; i < hashtable.buckets.Count; i++)
|
||||
{
|
||||
var bucket = hashtable.buckets[i];
|
||||
if (bucket == null || bucket.key == null)
|
||||
continue;
|
||||
keys.Add(bucket.key);
|
||||
values.Add(bucket.val);
|
||||
}
|
||||
}
|
||||
|
||||
// ~~~~~~~~~~ not used ~~~~~~~~~~~~
|
||||
|
||||
// cached il2cpp unbox methods
|
||||
|
@ -5,7 +5,7 @@ using System.Linq;
|
||||
using System.Text;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityExplorer.Core.CSharp
|
||||
namespace UnityExplorer.Core.Runtime.Mono
|
||||
{
|
||||
public class DummyBehaviour : MonoBehaviour
|
||||
{
|
@ -7,6 +7,7 @@ using System.Reflection;
|
||||
using System.Text;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Events;
|
||||
using UnityEngine.EventSystems;
|
||||
using UnityEngine.SceneManagement;
|
||||
using UnityEngine.UI;
|
||||
using UnityExplorer.Core;
|
||||
@ -20,6 +21,8 @@ namespace UnityExplorer.Core.Runtime.Mono
|
||||
{
|
||||
Reflection = new MonoReflection();
|
||||
TextureUtil = new MonoTextureUtil();
|
||||
|
||||
DummyBehaviour.Setup();
|
||||
}
|
||||
|
||||
public override void SetupEvents()
|
||||
@ -32,11 +35,31 @@ namespace UnityExplorer.Core.Runtime.Mono
|
||||
ExplorerCore.Log(condition, type, true);
|
||||
}
|
||||
|
||||
public override void StartConsoleCoroutine(IEnumerator routine)
|
||||
public override void StartCoroutine(IEnumerator routine)
|
||||
{
|
||||
DummyBehaviour.Instance.StartCoroutine(routine);
|
||||
}
|
||||
|
||||
public override void Update()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public override T AddComponent<T>(GameObject obj, Type type)
|
||||
{
|
||||
return (T)obj.AddComponent(type);
|
||||
}
|
||||
|
||||
public override ScriptableObject CreateScriptable(Type type)
|
||||
{
|
||||
return ScriptableObject.CreateInstance(type);
|
||||
}
|
||||
|
||||
public override void GraphicRaycast(GraphicRaycaster raycaster, PointerEventData data, List<RaycastResult> list)
|
||||
{
|
||||
raycaster.Raycast(data, list);
|
||||
}
|
||||
|
||||
public override string LayerToName(int layer)
|
||||
=> LayerMask.LayerToName(layer);
|
||||
|
||||
|
@ -12,6 +12,18 @@ namespace UnityExplorer.Core.Runtime.Mono
|
||||
public override object Cast(object obj, Type castTo)
|
||||
=> obj;
|
||||
|
||||
public override T TryCast<T>(object obj)
|
||||
{
|
||||
try
|
||||
{
|
||||
return (T)obj;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return default;
|
||||
}
|
||||
}
|
||||
|
||||
// Vanilla GetType is fine for mono
|
||||
public override Type GetActualType(object obj)
|
||||
=> obj.GetType();
|
||||
@ -27,6 +39,9 @@ namespace UnityExplorer.Core.Runtime.Mono
|
||||
|
||||
public override string ProcessTypeNameInString(Type type, string theString, ref string typeName)
|
||||
=> theString;
|
||||
|
||||
// not necessary
|
||||
public override void BoxStringToType(ref object _string, Type castTo) { }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
@ -18,6 +19,8 @@ namespace UnityExplorer.Core.Runtime
|
||||
|
||||
public abstract object Cast(object obj, Type castTo);
|
||||
|
||||
public abstract T TryCast<T>(object obj);
|
||||
|
||||
public abstract bool IsAssignableFrom(Type toAssignTo, Type toAssignFrom);
|
||||
|
||||
public abstract bool IsReflectionSupported(Type type);
|
||||
@ -25,5 +28,15 @@ namespace UnityExplorer.Core.Runtime
|
||||
public abstract string ProcessTypeNameInString(Type type, string theString, ref string typeName);
|
||||
|
||||
public abstract bool LoadModule(string module);
|
||||
|
||||
public abstract void BoxStringToType(ref object _string, Type castTo);
|
||||
|
||||
public virtual string UnboxString(object value) => (string)value;
|
||||
|
||||
public virtual IDictionary EnumerateDictionary(object value, Type typeOfKeys, Type typeOfValues)
|
||||
=> null;
|
||||
|
||||
public virtual IEnumerable EnumerateEnumerable(object value)
|
||||
=> null;
|
||||
}
|
||||
}
|
||||
|
@ -2,16 +2,17 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using UnityEngine;
|
||||
using UnityEngine.EventSystems;
|
||||
using UnityEngine.SceneManagement;
|
||||
using UnityEngine.UI;
|
||||
using UnityExplorer.Core.Runtime;
|
||||
|
||||
namespace UnityExplorer.Core.Runtime
|
||||
// Intentionally project-wide namespace so that its always easily accessible.
|
||||
namespace UnityExplorer
|
||||
{
|
||||
// Work in progress, this will be used to replace all the "if CPP / if MONO"
|
||||
// pre-processor directives all over the codebase.
|
||||
|
||||
public abstract class RuntimeProvider
|
||||
{
|
||||
public static RuntimeProvider Instance;
|
||||
@ -28,24 +29,33 @@ namespace UnityExplorer.Core.Runtime
|
||||
|
||||
public static void Init() =>
|
||||
#if CPP
|
||||
Instance = new Il2Cpp.Il2CppProvider();
|
||||
Instance = new Core.Runtime.Il2Cpp.Il2CppProvider();
|
||||
#else
|
||||
Instance = new Mono.MonoProvider();
|
||||
Instance = new Core.Runtime.Mono.MonoProvider();
|
||||
#endif
|
||||
|
||||
|
||||
public abstract void Initialize();
|
||||
|
||||
public abstract void SetupEvents();
|
||||
|
||||
public abstract void StartConsoleCoroutine(IEnumerator routine);
|
||||
public abstract void StartCoroutine(IEnumerator routine);
|
||||
|
||||
public abstract void Update();
|
||||
|
||||
public virtual bool IsReferenceEqual(object a, object b) => ReferenceEquals(a, b);
|
||||
|
||||
// Unity API handlers
|
||||
|
||||
public abstract T AddComponent<T>(GameObject obj, Type type) where T : Component;
|
||||
|
||||
public abstract ScriptableObject CreateScriptable(Type type);
|
||||
|
||||
public abstract string LayerToName(int layer);
|
||||
|
||||
public abstract UnityEngine.Object[] FindObjectsOfTypeAll(Type type);
|
||||
|
||||
public abstract void GraphicRaycast(GraphicRaycaster raycaster, PointerEventData data, List<RaycastResult> list);
|
||||
|
||||
public abstract int GetSceneHandle(Scene scene);
|
||||
|
||||
public abstract GameObject[] GetRootGameObjects(Scene scene);
|
||||
@ -53,5 +63,24 @@ namespace UnityExplorer.Core.Runtime
|
||||
public abstract int GetRootCount(Scene scene);
|
||||
|
||||
public abstract ColorBlock SetColorBlock(ColorBlock colors, Color? normal = null, Color? highlighted = null, Color? pressed = null);
|
||||
|
||||
public virtual void FindSingleton(string[] s_instanceNames, Type type, BindingFlags flags, List<object> instances)
|
||||
{
|
||||
// Look for a typical Instance backing field.
|
||||
FieldInfo fi;
|
||||
foreach (var name in s_instanceNames)
|
||||
{
|
||||
fi = type.GetField(name, flags);
|
||||
if (fi != null)
|
||||
{
|
||||
var instance = fi.GetValue(null);
|
||||
if (instance != null)
|
||||
{
|
||||
instances.Add(instance);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -67,38 +67,8 @@ namespace UnityExplorer.Core.Search
|
||||
{
|
||||
if (!string.IsNullOrEmpty(nameFilter) && !type.FullName.ToLower().Contains(nameFilter))
|
||||
continue;
|
||||
#if CPP
|
||||
// Only look for Properties in IL2CPP, not for Mono.
|
||||
PropertyInfo pi;
|
||||
foreach (var name in s_instanceNames)
|
||||
{
|
||||
pi = type.GetProperty(name, flags);
|
||||
if (pi != null)
|
||||
{
|
||||
var instance = pi.GetValue(null, null);
|
||||
if (instance != null)
|
||||
{
|
||||
instances.Add(instance);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
// Look for a typical Instance backing field.
|
||||
FieldInfo fi;
|
||||
foreach (var name in s_instanceNames)
|
||||
{
|
||||
fi = type.GetField(name, flags);
|
||||
if (fi != null)
|
||||
{
|
||||
var instance = fi.GetValue(null);
|
||||
if (instance != null)
|
||||
{
|
||||
instances.Add(instance);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RuntimeProvider.Instance.FindSingleton(s_instanceNames, type, flags, instances);
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
@ -175,15 +145,9 @@ namespace UnityExplorer.Core.Search
|
||||
|
||||
if (canGetGameObject)
|
||||
{
|
||||
#if MONO
|
||||
var go = context == SearchContext.GameObject
|
||||
? obj as GameObject
|
||||
: (obj as Component).gameObject;
|
||||
#else
|
||||
var go = context == SearchContext.GameObject
|
||||
? obj.TryCast<GameObject>()
|
||||
: obj.TryCast<Component>().gameObject;
|
||||
#endif
|
||||
|
||||
// scene check
|
||||
if (sceneFilter != SceneFilter.Any)
|
||||
|
@ -5,10 +5,18 @@ using System.Text;
|
||||
|
||||
namespace UnityExplorer
|
||||
{
|
||||
#if CPP
|
||||
public static class TestClass
|
||||
{
|
||||
public static UI.Main.PanelDragger.ResizeTypes flags = UI.Main.PanelDragger.ResizeTypes.NONE;
|
||||
|
||||
#if CPP
|
||||
public static string testStringOne = "Test";
|
||||
public static Il2CppSystem.Object testStringTwo = "string boxed as cpp object";
|
||||
public static Il2CppSystem.String testStringThree = "string boxed as cpp string";
|
||||
public static string nullString = null;
|
||||
|
||||
public static Il2CppSystem.Collections.Hashtable testHashset;
|
||||
public static Il2CppSystem.Collections.Generic.List<Il2CppSystem.Object> testList;
|
||||
|
||||
static TestClass()
|
||||
{
|
||||
@ -16,7 +24,13 @@ namespace UnityExplorer
|
||||
testHashset.Add("key1", "itemOne");
|
||||
testHashset.Add("key2", "itemTwo");
|
||||
testHashset.Add("key3", "itemThree");
|
||||
|
||||
testList = new Il2CppSystem.Collections.Generic.List<Il2CppSystem.Object>(3);
|
||||
testList.Add("One");
|
||||
testList.Add("Two");
|
||||
testList.Add("Three");
|
||||
//testIList = list.TryCast<Il2CppSystem.Collections.IList>();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
@ -5,6 +5,7 @@ using UnityExplorer.Core.Config;
|
||||
using UnityExplorer.Core.Input;
|
||||
using UnityExplorer.Core.Runtime;
|
||||
using UnityExplorer.UI;
|
||||
using UnityExplorer.UI.Inspectors;
|
||||
using UnityExplorer.UI.Main;
|
||||
|
||||
namespace UnityExplorer
|
||||
@ -12,7 +13,7 @@ namespace UnityExplorer
|
||||
public class ExplorerCore
|
||||
{
|
||||
public const string NAME = "UnityExplorer";
|
||||
public const string VERSION = "3.3.5";
|
||||
public const string VERSION = "3.3.10";
|
||||
public const string AUTHOR = "Sinai";
|
||||
public const string GUID = "com.sinai.unityexplorer";
|
||||
|
||||
@ -47,11 +48,13 @@ namespace UnityExplorer
|
||||
|
||||
Log($"{NAME} {VERSION} initialized.");
|
||||
|
||||
// InspectorManager.Instance.Inspect(typeof(TestClass));
|
||||
//InspectorManager.Instance.Inspect(typeof(TestClass));
|
||||
}
|
||||
|
||||
public static void Update()
|
||||
{
|
||||
RuntimeProvider.Instance.Update();
|
||||
|
||||
UIManager.Update();
|
||||
}
|
||||
|
||||
|
@ -13,6 +13,7 @@ using UnityExplorer.Loader.ML;
|
||||
|
||||
[assembly: MelonInfo(typeof(ExplorerMelonMod), ExplorerCore.NAME, ExplorerCore.VERSION, ExplorerCore.AUTHOR)]
|
||||
[assembly: MelonGame(null, null)]
|
||||
//[assembly: MelonPlatformDomain(MelonPlatformDomainAttribute.CompatibleDomains.UNIVERSAL)]
|
||||
|
||||
namespace UnityExplorer
|
||||
{
|
||||
|
@ -23,7 +23,9 @@ namespace UnityExplorer.Loader.ML
|
||||
|
||||
try
|
||||
{
|
||||
// TEMPORARY - JUST REQUIRED UNTIL ML 0.3.1 RELEASED
|
||||
MelonPreferences.Mapper.RegisterMapper(KeycodeReader, KeycodeWriter);
|
||||
MelonPreferences.Mapper.RegisterMapper(MenuPagesReader, MenuPagesWriter);
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
@ -80,6 +82,8 @@ namespace UnityExplorer.Loader.ML
|
||||
MelonPreferences.Save();
|
||||
}
|
||||
|
||||
// TEMPORARY - JUST REQUIRED UNTIL ML 0.3.1 RELEASED
|
||||
|
||||
public static KeyCode KeycodeReader(TomlObject value)
|
||||
{
|
||||
try
|
||||
@ -101,6 +105,28 @@ namespace UnityExplorer.Loader.ML
|
||||
{
|
||||
return MelonPreferences.Mapper.ToToml(value.ToString());
|
||||
}
|
||||
|
||||
public static UI.Main.MenuPages MenuPagesReader(TomlObject value)
|
||||
{
|
||||
try
|
||||
{
|
||||
var kc = (UI.Main.MenuPages)Enum.Parse(typeof(UI.Main.MenuPages), (value as TomlString).Value);
|
||||
|
||||
if (kc == default)
|
||||
throw new Exception();
|
||||
|
||||
return kc;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return UI.Main.MenuPages.Home;
|
||||
}
|
||||
}
|
||||
|
||||
public static TomlObject MenuPagesWriter(UI.Main.MenuPages value)
|
||||
{
|
||||
return MelonPreferences.Mapper.ToToml(value.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -56,8 +56,12 @@ namespace UnityExplorer.UI.CacheObject
|
||||
|
||||
var rowObj = UIFactory.CreateHorizontalGroup(m_mainContent, "PairedGroup", false, false, true, true, 0, new Vector4(0,0,5,2),
|
||||
bgColor);
|
||||
|
||||
var indexLabel = UIFactory.CreateLabel(rowObj, "IndexLabel", $"{this.PairType} {this.Index}:", TextAnchor.MiddleLeft);
|
||||
|
||||
string lbl = $"{this.PairType}";
|
||||
if (this.PairType == PairTypes.Key)
|
||||
lbl = $"[{Index}] {lbl}";
|
||||
|
||||
var indexLabel = UIFactory.CreateLabel(rowObj, "IndexLabel", lbl, TextAnchor.MiddleLeft);
|
||||
UIFactory.SetLayoutElement(indexLabel.gameObject, minWidth: 80, flexibleWidth: 30, minHeight: 25);
|
||||
|
||||
IValue.m_mainContentParent = rowObj;
|
||||
|
@ -78,11 +78,7 @@ namespace UnityExplorer.UI.Inspectors.GameObjects
|
||||
text.text = SignatureHighlighter.ParseFullSyntax(ReflectionUtility.GetActualType(comp), true);
|
||||
|
||||
var toggle = s_compToggles[i];
|
||||
#if CPP
|
||||
if (comp.TryCast<Behaviour>() is Behaviour behaviour)
|
||||
#else
|
||||
if (comp is Behaviour behaviour)
|
||||
#endif
|
||||
{
|
||||
if (!toggle.gameObject.activeSelf)
|
||||
toggle.gameObject.SetActive(true);
|
||||
@ -109,19 +105,13 @@ namespace UnityExplorer.UI.Inspectors.GameObjects
|
||||
internal static void OnCompToggleClicked(int index, bool value)
|
||||
{
|
||||
var comp = s_compShortlist[index];
|
||||
#if CPP
|
||||
comp.TryCast<Behaviour>().enabled = value;
|
||||
#else
|
||||
(comp as Behaviour).enabled = value;
|
||||
#endif
|
||||
}
|
||||
|
||||
internal static void OnCompListObjectClicked(int index)
|
||||
{
|
||||
if (index >= s_compShortlist.Count || !s_compShortlist[index])
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
InspectorManager.Instance.Inspect(s_compShortlist[index]);
|
||||
}
|
||||
|
@ -203,11 +203,6 @@ namespace UnityExplorer.UI.Main.Home
|
||||
position = mousePos
|
||||
};
|
||||
|
||||
#if MONO
|
||||
var list = new List<RaycastResult>();
|
||||
#else
|
||||
var list = new Il2CppSystem.Collections.Generic.List<RaycastResult>();
|
||||
#endif
|
||||
//ExplorerCore.Log("~~~~~~~~~ begin raycast ~~~~~~~~");
|
||||
GameObject hitObject = null;
|
||||
int highestLayer = int.MinValue;
|
||||
@ -215,7 +210,10 @@ namespace UnityExplorer.UI.Main.Home
|
||||
int highestDepth = int.MinValue;
|
||||
foreach (var gr in graphicRaycasters)
|
||||
{
|
||||
gr.Raycast(ped, list);
|
||||
var list = new List<RaycastResult>();
|
||||
RuntimeProvider.Instance.GraphicRaycast(gr, ped, list);
|
||||
|
||||
//gr.Raycast(ped, list);
|
||||
|
||||
if (list.Count > 0)
|
||||
{
|
||||
@ -290,8 +288,6 @@ namespace UnityExplorer.UI.Main.Home
|
||||
_wasDisabledGraphics.Clear();
|
||||
}
|
||||
|
||||
#region UI
|
||||
|
||||
internal static Text s_objNameLabel;
|
||||
internal static Text s_objPathLabel;
|
||||
internal static Text s_mousePosLabel;
|
||||
@ -328,7 +324,5 @@ namespace UnityExplorer.UI.Main.Home
|
||||
|
||||
s_UIContent.SetActive(false);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
@ -54,21 +54,11 @@ namespace UnityExplorer.UI.Inspectors
|
||||
// check if currently inspecting this object
|
||||
foreach (InspectorBase tab in m_currentInspectors)
|
||||
{
|
||||
if (ReferenceEquals(obj, tab.Target))
|
||||
if (RuntimeProvider.Instance.IsReferenceEqual(obj, tab.Target))
|
||||
{
|
||||
SetInspectorTab(tab);
|
||||
return;
|
||||
}
|
||||
#if CPP
|
||||
else if (unityObj && tab.Target is UnityEngine.Object uTabObj)
|
||||
{
|
||||
if (unityObj.m_CachedPtr == uTabObj.m_CachedPtr)
|
||||
{
|
||||
SetInspectorTab(tab);
|
||||
return;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
InspectorBase inspector;
|
||||
|
@ -13,7 +13,9 @@ using UnityExplorer.UI.CacheObject;
|
||||
using UnityExplorer.Core;
|
||||
using UnityExplorer.UI.Utility;
|
||||
#if CPP
|
||||
using CppDictionary = Il2CppSystem.Collections.IDictionary;
|
||||
using AltIDictionary = Il2CppSystem.Collections.IDictionary;
|
||||
#else
|
||||
using AltIDictionary = System.Collections.IDictionary;
|
||||
#endif
|
||||
|
||||
namespace UnityExplorer.UI.InteractiveValues
|
||||
@ -48,11 +50,7 @@ namespace UnityExplorer.UI.InteractiveValues
|
||||
}
|
||||
|
||||
internal IDictionary RefIDictionary;
|
||||
#if CPP
|
||||
internal CppDictionary RefCppDictionary;
|
||||
#else
|
||||
internal IDictionary RefCppDictionary = null;
|
||||
#endif
|
||||
internal AltIDictionary RefAltIDictionary;
|
||||
internal Type m_typeOfKeys;
|
||||
internal Type m_typeofValues;
|
||||
|
||||
@ -73,10 +71,11 @@ namespace UnityExplorer.UI.InteractiveValues
|
||||
{
|
||||
RefIDictionary = Value as IDictionary;
|
||||
|
||||
#if CPP
|
||||
try { RefCppDictionary = (Value as Il2CppSystem.Object).TryCast<CppDictionary>(); }
|
||||
catch { }
|
||||
#endif
|
||||
if (RefIDictionary == null)
|
||||
{
|
||||
try { RefAltIDictionary = Value.TryCast<AltIDictionary>(); }
|
||||
catch { }
|
||||
}
|
||||
|
||||
if (m_subContentParent.activeSelf)
|
||||
{
|
||||
@ -129,10 +128,8 @@ namespace UnityExplorer.UI.InteractiveValues
|
||||
m_entries.Clear();
|
||||
}
|
||||
|
||||
#if CPP
|
||||
if (RefIDictionary == null && Value != null)
|
||||
RefIDictionary = EnumerateWithReflection();
|
||||
#endif
|
||||
RefIDictionary = RuntimeProvider.Instance.Reflection.EnumerateDictionary(Value, m_typeOfKeys, m_typeofValues);
|
||||
|
||||
if (RefIDictionary != null)
|
||||
{
|
||||
@ -212,82 +209,11 @@ namespace UnityExplorer.UI.InteractiveValues
|
||||
RefreshDisplay();
|
||||
}
|
||||
|
||||
#region CPP fixes
|
||||
#if CPP
|
||||
// temp fix for Il2Cpp IDictionary until interfaces are fixed
|
||||
|
||||
private IDictionary EnumerateWithReflection()
|
||||
{
|
||||
var valueType = ReflectionUtility.GetActualType(Value);
|
||||
|
||||
var keyList = new List<object>();
|
||||
var valueList = new List<object>();
|
||||
|
||||
var hashtable = Value.Cast(typeof(Il2CppSystem.Collections.Hashtable)) as Il2CppSystem.Collections.Hashtable;
|
||||
|
||||
if (hashtable != null)
|
||||
{
|
||||
EnumerateCppHashtable(hashtable, keyList, valueList);
|
||||
}
|
||||
else
|
||||
{
|
||||
var keys = valueType.GetProperty("Keys").GetValue(Value, null);
|
||||
var values = valueType.GetProperty("Values").GetValue(Value, null);
|
||||
|
||||
EnumerateCppIDictionary(keys, keyList);
|
||||
EnumerateCppIDictionary(values, valueList);
|
||||
}
|
||||
|
||||
var dict = Activator.CreateInstance(typeof(Dictionary<,>)
|
||||
.MakeGenericType(m_typeOfKeys, m_typeofValues))
|
||||
as IDictionary;
|
||||
|
||||
for (int i = 0; i < keyList.Count; i++)
|
||||
dict.Add(keyList[i], valueList[i]);
|
||||
|
||||
return dict;
|
||||
}
|
||||
|
||||
private void EnumerateCppIDictionary(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));
|
||||
}
|
||||
}
|
||||
|
||||
private void EnumerateCppHashtable(Il2CppSystem.Collections.Hashtable hashtable, List<object> keys, List<object> values)
|
||||
{
|
||||
for (int i = 0; i < hashtable.buckets.Count; i++)
|
||||
{
|
||||
var bucket = hashtable.buckets[i];
|
||||
if (bucket == null || bucket.key == null)
|
||||
continue;
|
||||
keys.Add(bucket.key);
|
||||
values.Add(bucket.val);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#endregion
|
||||
|
||||
#region UI CONSTRUCTION
|
||||
|
||||
internal GameObject m_listContent;
|
||||
internal LayoutElement m_listLayout;
|
||||
|
||||
internal PageHandler m_pageHandler;
|
||||
|
||||
//internal List<GameObject> m_rowHolders = new List<GameObject>();
|
||||
|
||||
public override void ConstructUI(GameObject parent, GameObject subGroup)
|
||||
{
|
||||
base.ConstructUI(parent, subGroup);
|
||||
@ -317,7 +243,5 @@ namespace UnityExplorer.UI.InteractiveValues
|
||||
contentFitter.horizontalFit = ContentSizeFitter.FitMode.Unconstrained;
|
||||
contentFitter.verticalFit = ContentSizeFitter.FitMode.PreferredSize;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
@ -94,7 +94,7 @@ namespace UnityExplorer.UI.InteractiveValues
|
||||
{
|
||||
base.RefreshUIForValue();
|
||||
|
||||
if (m_subContentConstructed)
|
||||
if (m_subContentConstructed && !(this is InteractiveFlags))
|
||||
{
|
||||
m_dropdownText.text = Value?.ToString() ?? "<no value set>";
|
||||
}
|
||||
|
@ -39,11 +39,6 @@ namespace UnityExplorer.UI.InteractiveValues
|
||||
|
||||
internal IEnumerable RefIEnumerable;
|
||||
internal IList RefIList;
|
||||
//#if CPP
|
||||
// internal object CppICollection;
|
||||
//#else
|
||||
// internal object CppICollection = null;
|
||||
//#endif
|
||||
|
||||
internal readonly Type m_baseEntryType;
|
||||
|
||||
@ -56,18 +51,6 @@ namespace UnityExplorer.UI.InteractiveValues
|
||||
RefIEnumerable = Value as IEnumerable;
|
||||
RefIList = Value as IList;
|
||||
|
||||
//#if CPP
|
||||
// if (Value != null && RefIList == null)
|
||||
// {
|
||||
// try
|
||||
// {
|
||||
// var type = typeof(Il2CppSystem.Collections.ICollection).MakeGenericType(this.m_baseEntryType);
|
||||
// CppICollection = (Value as Il2CppSystem.Object).Cast(type);
|
||||
// }
|
||||
// catch { }
|
||||
// }
|
||||
//#endif
|
||||
|
||||
if (m_subContentParent.activeSelf)
|
||||
{
|
||||
GetCacheEntries();
|
||||
@ -96,8 +79,8 @@ namespace UnityExplorer.UI.InteractiveValues
|
||||
if (Value != null)
|
||||
{
|
||||
string count = "?";
|
||||
if (m_recacheWanted && RefIList != null)// || CppICollection != null))
|
||||
count = RefIList.Count.ToString();// ?? CppICollection.Count.ToString();
|
||||
if (m_recacheWanted && RefIList != null)
|
||||
count = RefIList.Count.ToString();
|
||||
else if (!m_recacheWanted)
|
||||
count = m_entries.Count.ToString();
|
||||
|
||||
@ -121,10 +104,8 @@ namespace UnityExplorer.UI.InteractiveValues
|
||||
m_entries.Clear();
|
||||
}
|
||||
|
||||
#if CPP
|
||||
if (RefIEnumerable == null && Value != null)
|
||||
RefIEnumerable = EnumerateWithReflection();
|
||||
#endif
|
||||
RefIEnumerable = RuntimeProvider.Instance.Reflection.EnumerateEnumerable(Value);
|
||||
|
||||
if (RefIEnumerable != null)
|
||||
{
|
||||
@ -188,62 +169,6 @@ namespace UnityExplorer.UI.InteractiveValues
|
||||
RefreshDisplay();
|
||||
}
|
||||
|
||||
#region CPP Helpers
|
||||
|
||||
#if CPP
|
||||
// some temp fixes for Il2Cpp IEnumerables until interfaces are fixed
|
||||
|
||||
internal static readonly Dictionary<Type, MethodInfo> s_getEnumeratorMethods = new Dictionary<Type, MethodInfo>();
|
||||
|
||||
internal static readonly Dictionary<Type, EnumeratorInfo> s_enumeratorInfos = new Dictionary<Type, EnumeratorInfo>();
|
||||
|
||||
internal class EnumeratorInfo
|
||||
{
|
||||
internal MethodInfo moveNext;
|
||||
internal PropertyInfo current;
|
||||
}
|
||||
|
||||
private IEnumerable EnumerateWithReflection()
|
||||
{
|
||||
if (Value == null)
|
||||
return null;
|
||||
|
||||
// new test
|
||||
var CppEnumerable = (Value as Il2CppSystem.Object)?.TryCast<Il2CppSystem.Collections.IEnumerable>();
|
||||
if (CppEnumerable != null)
|
||||
{
|
||||
var type = Value.GetType();
|
||||
if (!s_getEnumeratorMethods.ContainsKey(type))
|
||||
s_getEnumeratorMethods.Add(type, type.GetMethod("GetEnumerator"));
|
||||
|
||||
var enumerator = s_getEnumeratorMethods[type].Invoke(Value, null);
|
||||
var enumeratorType = enumerator.GetType();
|
||||
|
||||
if (!s_enumeratorInfos.ContainsKey(enumeratorType))
|
||||
{
|
||||
s_enumeratorInfos.Add(enumeratorType, new EnumeratorInfo
|
||||
{
|
||||
current = enumeratorType.GetProperty("Current"),
|
||||
moveNext = enumeratorType.GetMethod("MoveNext"),
|
||||
});
|
||||
}
|
||||
var info = s_enumeratorInfos[enumeratorType];
|
||||
|
||||
// iterate
|
||||
var list = new List<object>();
|
||||
while ((bool)info.moveNext.Invoke(enumerator, null))
|
||||
list.Add(info.current.GetValue(enumerator));
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
#endif
|
||||
|
||||
#endregion
|
||||
|
||||
#region UI CONSTRUCTION
|
||||
|
||||
internal GameObject m_listContent;
|
||||
internal LayoutElement m_listLayout;
|
||||
@ -278,7 +203,5 @@ namespace UnityExplorer.UI.InteractiveValues
|
||||
contentFitter.horizontalFit = ContentSizeFitter.FitMode.Unconstrained;
|
||||
contentFitter.verticalFit = ContentSizeFitter.FitMode.PreferredSize;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
@ -26,8 +26,6 @@ namespace UnityExplorer.UI.InteractiveValues
|
||||
|
||||
public override void OnValueUpdated()
|
||||
{
|
||||
base.OnValueUpdated();
|
||||
|
||||
if (Owner.CanWrite)
|
||||
{
|
||||
var enabledNames = new List<string>();
|
||||
@ -37,10 +35,10 @@ namespace UnityExplorer.UI.InteractiveValues
|
||||
enabledNames.AddRange(enabled);
|
||||
|
||||
for (int i = 0; i < m_values.Length; i++)
|
||||
{
|
||||
m_enabledFlags[i] = enabledNames.Contains(m_values[i].Value);
|
||||
}
|
||||
}
|
||||
|
||||
base.OnValueUpdated();
|
||||
}
|
||||
|
||||
public override void RefreshUIForValue()
|
||||
@ -48,6 +46,8 @@ namespace UnityExplorer.UI.InteractiveValues
|
||||
GetDefaultLabel();
|
||||
m_baseLabel.text = DefaultLabel;
|
||||
|
||||
base.RefreshUIForValue();
|
||||
|
||||
if (m_subContentConstructed)
|
||||
{
|
||||
for (int i = 0; i < m_values.Length; i++)
|
||||
|
201
src/UI/InteractiveValues/InteractiveFloatStruct.cs
Normal file
201
src/UI/InteractiveValues/InteractiveFloatStruct.cs
Normal file
@ -0,0 +1,201 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using System.Reflection;
|
||||
|
||||
namespace UnityExplorer.UI.InteractiveValues
|
||||
{
|
||||
// Class for supporting any "float struct" (ie Vector, Rect, etc).
|
||||
// Supports any struct where all the public instance fields are floats (or types assignable to float)
|
||||
|
||||
public class StructInfo
|
||||
{
|
||||
public string[] FieldNames { get; }
|
||||
private readonly FieldInfo[] m_fields;
|
||||
|
||||
public StructInfo(Type type)
|
||||
{
|
||||
m_fields = type.GetFields(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public)
|
||||
.Where(it => !it.IsLiteral)
|
||||
.ToArray();
|
||||
|
||||
FieldNames = m_fields.Select(it => it.Name)
|
||||
.ToArray();
|
||||
}
|
||||
|
||||
public object SetValue(ref object instance, int fieldIndex, float val)
|
||||
{
|
||||
m_fields[fieldIndex].SetValue(instance, val);
|
||||
return instance;
|
||||
}
|
||||
|
||||
public float GetValue(object instance, int fieldIndex)
|
||||
=> (float)m_fields[fieldIndex].GetValue(instance);
|
||||
|
||||
public void RefreshUI(InputField[] inputs, object instance)
|
||||
{
|
||||
try
|
||||
{
|
||||
for (int i = 0; i < m_fields.Length; i++)
|
||||
{
|
||||
var field = m_fields[i];
|
||||
float val = (float)field.GetValue(instance);
|
||||
inputs[i].text = val.ToString();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ExplorerCore.Log(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class InteractiveFloatStruct : InteractiveValue
|
||||
{
|
||||
private static readonly Dictionary<string, bool> _typeSupportCache = new Dictionary<string, bool>();
|
||||
public static bool IsTypeSupported(Type type)
|
||||
{
|
||||
if (!type.IsValueType)
|
||||
return false;
|
||||
|
||||
if (string.IsNullOrEmpty(type.AssemblyQualifiedName))
|
||||
return false;
|
||||
|
||||
if (_typeSupportCache.TryGetValue(type.AssemblyQualifiedName, out bool ret))
|
||||
return ret;
|
||||
|
||||
ret = true;
|
||||
var fields = type.GetFields(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
|
||||
foreach (var field in fields)
|
||||
{
|
||||
if (field.IsLiteral)
|
||||
continue;
|
||||
|
||||
if (!typeof(float).IsAssignableFrom(field.FieldType))
|
||||
{
|
||||
ret = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
_typeSupportCache.Add(type.AssemblyQualifiedName, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
//~~~~~~~~~ Instance ~~~~~~~~~~
|
||||
|
||||
public InteractiveFloatStruct(object value, Type valueType) : base(value, valueType) { }
|
||||
|
||||
public override bool HasSubContent => true;
|
||||
public override bool SubContentWanted => true;
|
||||
|
||||
public StructInfo StructInfo;
|
||||
|
||||
public override void RefreshUIForValue()
|
||||
{
|
||||
InitializeStructInfo();
|
||||
|
||||
base.RefreshUIForValue();
|
||||
|
||||
if (m_subContentConstructed)
|
||||
StructInfo.RefreshUI(m_inputs, this.Value);
|
||||
}
|
||||
|
||||
internal override void OnToggleSubcontent(bool toggle)
|
||||
{
|
||||
InitializeStructInfo();
|
||||
|
||||
base.OnToggleSubcontent(toggle);
|
||||
|
||||
StructInfo.RefreshUI(m_inputs, this.Value);
|
||||
}
|
||||
|
||||
internal Type m_lastStructType;
|
||||
|
||||
internal void InitializeStructInfo()
|
||||
{
|
||||
var type = Value?.GetType() ?? FallbackType;
|
||||
|
||||
if (StructInfo != null && type == m_lastStructType)
|
||||
return;
|
||||
|
||||
if (StructInfo != null && m_subContentConstructed)
|
||||
DestroySubContent();
|
||||
|
||||
m_lastStructType = type;
|
||||
|
||||
StructInfo = new StructInfo(type);
|
||||
|
||||
if (m_subContentParent.activeSelf)
|
||||
ConstructSubcontent();
|
||||
}
|
||||
|
||||
#region UI CONSTRUCTION
|
||||
|
||||
internal InputField[] m_inputs;
|
||||
|
||||
public override void ConstructUI(GameObject parent, GameObject subGroup)
|
||||
{
|
||||
base.ConstructUI(parent, subGroup);
|
||||
}
|
||||
|
||||
public override void ConstructSubcontent()
|
||||
{
|
||||
base.ConstructSubcontent();
|
||||
|
||||
if (StructInfo == null)
|
||||
{
|
||||
ExplorerCore.LogWarning("Setting up subcontent but structinfo is null");
|
||||
return;
|
||||
}
|
||||
|
||||
var editorContainer = UIFactory.CreateVerticalGroup(m_subContentParent, "EditorContent", false, true, true, true, 2, new Vector4(4, 4, 4, 4),
|
||||
new Color(0.08f, 0.08f, 0.08f));
|
||||
|
||||
m_inputs = new InputField[StructInfo.FieldNames.Length];
|
||||
|
||||
for (int i = 0; i < StructInfo.FieldNames.Length; i++)
|
||||
AddEditorRow(i, editorContainer);
|
||||
|
||||
RefreshUIForValue();
|
||||
}
|
||||
|
||||
internal void AddEditorRow(int index, GameObject groupObj)
|
||||
{
|
||||
try
|
||||
{
|
||||
var row = UIFactory.CreateHorizontalGroup(groupObj, "EditorRow", false, true, true, true, 5, default, new Color(1, 1, 1, 0));
|
||||
|
||||
string name = StructInfo.FieldNames[index];
|
||||
|
||||
var label = UIFactory.CreateLabel(row, "RowLabel", $"{name}:", TextAnchor.MiddleRight, Color.cyan);
|
||||
UIFactory.SetLayoutElement(label.gameObject, minWidth: 30, flexibleWidth: 0, minHeight: 25);
|
||||
|
||||
var inputFieldObj = UIFactory.CreateInputField(row, "InputField", "...", 14, 3, 1);
|
||||
UIFactory.SetLayoutElement(inputFieldObj, minWidth: 120, minHeight: 25, flexibleWidth: 0);
|
||||
|
||||
var inputField = inputFieldObj.GetComponent<InputField>();
|
||||
m_inputs[index] = inputField;
|
||||
|
||||
inputField.onValueChanged.AddListener((string val) =>
|
||||
{
|
||||
try
|
||||
{
|
||||
float f = float.Parse(val);
|
||||
Value = StructInfo.SetValue(ref this.Value, index, f);
|
||||
Owner.SetValue();
|
||||
}
|
||||
catch { }
|
||||
});
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ExplorerCore.Log(ex);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
@ -9,6 +9,7 @@ using UnityExplorer.Core.Unity;
|
||||
using UnityExplorer.UI;
|
||||
using UnityExplorer.UI.Utility;
|
||||
using UnityExplorer.UI.CacheObject;
|
||||
using UnityExplorer.Core.Runtime;
|
||||
|
||||
namespace UnityExplorer.UI.InteractiveValues
|
||||
{
|
||||
@ -23,13 +24,8 @@ namespace UnityExplorer.UI.InteractiveValues
|
||||
|
||||
public override void OnValueUpdated()
|
||||
{
|
||||
#if CPP
|
||||
// strings boxed as Il2CppSystem.Objects can behave weirdly.
|
||||
// GetActualType will find they are a string, but if its boxed
|
||||
// then we need to unbox it like this...
|
||||
if (!(Value is string))
|
||||
Value = ((Il2CppSystem.Object)Value).ToString();
|
||||
#endif
|
||||
if (!(Value is string) && Value != null)
|
||||
Value = RuntimeProvider.Instance.Reflection.UnboxString(Value);
|
||||
|
||||
base.OnValueUpdated();
|
||||
}
|
||||
@ -96,10 +92,18 @@ namespace UnityExplorer.UI.InteractiveValues
|
||||
m_labelLayout.flexibleWidth = 0;
|
||||
}
|
||||
|
||||
internal void OnApplyClicked()
|
||||
internal void SetValueFromInput()
|
||||
{
|
||||
Value = m_valueInput.text;
|
||||
|
||||
if (!typeof(string).IsAssignableFrom(Owner.FallbackType))
|
||||
ReflectionProvider.Instance.BoxStringToType(ref Value, Owner.FallbackType);
|
||||
|
||||
Owner.SetValue();
|
||||
|
||||
// revert back to string now
|
||||
OnValueUpdated();
|
||||
|
||||
RefreshUIForValue();
|
||||
}
|
||||
|
||||
@ -170,7 +174,7 @@ namespace UnityExplorer.UI.InteractiveValues
|
||||
|
||||
if (Owner.CanWrite)
|
||||
{
|
||||
var apply = UIFactory.CreateButton(groupObj, "ApplyButton", "Apply", OnApplyClicked, new Color(0.2f, 0.2f, 0.2f));
|
||||
var apply = UIFactory.CreateButton(groupObj, "ApplyButton", "Apply", SetValueFromInput, new Color(0.2f, 0.2f, 0.2f));
|
||||
UIFactory.SetLayoutElement(apply.gameObject, minWidth: 50, minHeight: 25, flexibleWidth: 0);
|
||||
}
|
||||
else
|
||||
|
@ -1,298 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using UnityExplorer.Core.Unity;
|
||||
using UnityExplorer.UI;
|
||||
|
||||
namespace UnityExplorer.UI.InteractiveValues
|
||||
{
|
||||
#region IStructInfo helper
|
||||
|
||||
public interface IStructInfo
|
||||
{
|
||||
string[] FieldNames { get; }
|
||||
object SetValue(ref object value, int fieldIndex, float val);
|
||||
void RefreshUI(InputField[] inputs, object value);
|
||||
}
|
||||
|
||||
public class StructInfo<T> : IStructInfo where T : struct
|
||||
{
|
||||
public string[] FieldNames { get; set; }
|
||||
|
||||
public delegate void SetMethod(ref T value, int fieldIndex, float val);
|
||||
public SetMethod SetValueMethod;
|
||||
|
||||
public delegate void UpdateMethod(InputField[] inputs, object value);
|
||||
public UpdateMethod UpdateUIMethod;
|
||||
|
||||
public object SetValue(ref object value, int fieldIndex, float val)
|
||||
{
|
||||
var box = (T)value;
|
||||
SetValueMethod.Invoke(ref box, fieldIndex, val);
|
||||
return box;
|
||||
}
|
||||
|
||||
public void RefreshUI(InputField[] inputs, object value)
|
||||
{
|
||||
UpdateUIMethod.Invoke(inputs, value);
|
||||
}
|
||||
}
|
||||
|
||||
// This part is a bit ugly, but everything else is generalized above.
|
||||
// I could generalize it more with reflection, but it would be different for
|
||||
// mono/il2cpp and also slower.
|
||||
public static class StructInfoFactory
|
||||
{
|
||||
public static IStructInfo Create(Type type)
|
||||
{
|
||||
if (type == typeof(Vector2))
|
||||
{
|
||||
return new StructInfo<Vector2>()
|
||||
{
|
||||
FieldNames = new[] { "x", "y", },
|
||||
SetValueMethod = (ref Vector2 vec, int fieldIndex, float val) =>
|
||||
{
|
||||
switch (fieldIndex)
|
||||
{
|
||||
case 0: vec.x = val; break;
|
||||
case 1: vec.y = val; break;
|
||||
}
|
||||
},
|
||||
UpdateUIMethod = (InputField[] inputs, object value) =>
|
||||
{
|
||||
Vector2 vec = (Vector2)value;
|
||||
inputs[0].text = vec.x.ToString();
|
||||
inputs[1].text = vec.y.ToString();
|
||||
}
|
||||
};
|
||||
}
|
||||
else if (type == typeof(Vector3))
|
||||
{
|
||||
return new StructInfo<Vector3>()
|
||||
{
|
||||
FieldNames = new[] { "x", "y", "z" },
|
||||
SetValueMethod = (ref Vector3 vec, int fieldIndex, float val) =>
|
||||
{
|
||||
switch (fieldIndex)
|
||||
{
|
||||
case 0: vec.x = val; break;
|
||||
case 1: vec.y = val; break;
|
||||
case 2: vec.z = val; break;
|
||||
}
|
||||
},
|
||||
UpdateUIMethod = (InputField[] inputs, object value) =>
|
||||
{
|
||||
Vector3 vec = (Vector3)value;
|
||||
inputs[0].text = vec.x.ToString();
|
||||
inputs[1].text = vec.y.ToString();
|
||||
inputs[2].text = vec.z.ToString();
|
||||
}
|
||||
};
|
||||
}
|
||||
else if (type == typeof(Vector4))
|
||||
{
|
||||
return new StructInfo<Vector4>()
|
||||
{
|
||||
FieldNames = new[] { "x", "y", "z", "w" },
|
||||
SetValueMethod = (ref Vector4 vec, int fieldIndex, float val) =>
|
||||
{
|
||||
switch (fieldIndex)
|
||||
{
|
||||
case 0: vec.x = val; break;
|
||||
case 1: vec.y = val; break;
|
||||
case 2: vec.z = val; break;
|
||||
case 3: vec.w = val; break;
|
||||
}
|
||||
},
|
||||
UpdateUIMethod = (InputField[] inputs, object value) =>
|
||||
{
|
||||
Vector4 vec = (Vector4)value;
|
||||
inputs[0].text = vec.x.ToString();
|
||||
inputs[1].text = vec.y.ToString();
|
||||
inputs[2].text = vec.z.ToString();
|
||||
inputs[3].text = vec.w.ToString();
|
||||
}
|
||||
};
|
||||
}
|
||||
else if (type == typeof(Rect))
|
||||
{
|
||||
return new StructInfo<Rect>()
|
||||
{
|
||||
FieldNames = new[] { "x", "y", "width", "height" },
|
||||
SetValueMethod = (ref Rect vec, int fieldIndex, float val) =>
|
||||
{
|
||||
switch (fieldIndex)
|
||||
{
|
||||
case 0: vec.x = val; break;
|
||||
case 1: vec.y = val; break;
|
||||
case 2: vec.width = val; break;
|
||||
case 3: vec.height = val; break;
|
||||
}
|
||||
},
|
||||
UpdateUIMethod = (InputField[] inputs, object value) =>
|
||||
{
|
||||
Rect vec = (Rect)value;
|
||||
inputs[0].text = vec.x.ToString();
|
||||
inputs[1].text = vec.y.ToString();
|
||||
inputs[2].text = vec.width.ToString();
|
||||
inputs[3].text = vec.height.ToString();
|
||||
}
|
||||
};
|
||||
}
|
||||
else if (type == typeof(Color))
|
||||
{
|
||||
return new StructInfo<Color>()
|
||||
{
|
||||
FieldNames = new[] { "r", "g", "b", "a" },
|
||||
SetValueMethod = (ref Color vec, int fieldIndex, float val) =>
|
||||
{
|
||||
switch (fieldIndex)
|
||||
{
|
||||
case 0: vec.r = val; break;
|
||||
case 1: vec.g = val; break;
|
||||
case 2: vec.b = val; break;
|
||||
case 3: vec.a = val; break;
|
||||
}
|
||||
},
|
||||
UpdateUIMethod = (InputField[] inputs, object value) =>
|
||||
{
|
||||
Color vec = (Color)value;
|
||||
inputs[0].text = vec.r.ToString();
|
||||
inputs[1].text = vec.g.ToString();
|
||||
inputs[2].text = vec.b.ToString();
|
||||
inputs[3].text = vec.a.ToString();
|
||||
}
|
||||
};
|
||||
}
|
||||
else
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public class InteractiveUnityStruct : InteractiveValue
|
||||
{
|
||||
public static bool SupportsType(Type type) => s_supportedTypes.Contains(type);
|
||||
private static readonly HashSet<Type> s_supportedTypes = new HashSet<Type>
|
||||
{
|
||||
typeof(Vector2),
|
||||
typeof(Vector3),
|
||||
typeof(Vector4),
|
||||
typeof(Rect),
|
||||
//typeof(Color) // todo might make a special editor for colors
|
||||
};
|
||||
|
||||
//~~~~~~~~~ Instance ~~~~~~~~~~
|
||||
|
||||
public InteractiveUnityStruct(object value, Type valueType) : base(value, valueType) { }
|
||||
|
||||
public override bool HasSubContent => true;
|
||||
public override bool SubContentWanted => true;
|
||||
public override bool WantInspectBtn => true;
|
||||
|
||||
public IStructInfo StructInfo;
|
||||
|
||||
public override void RefreshUIForValue()
|
||||
{
|
||||
InitializeStructInfo();
|
||||
|
||||
base.RefreshUIForValue();
|
||||
|
||||
if (m_subContentConstructed)
|
||||
StructInfo.RefreshUI(m_inputs, this.Value);
|
||||
}
|
||||
|
||||
internal override void OnToggleSubcontent(bool toggle)
|
||||
{
|
||||
InitializeStructInfo();
|
||||
|
||||
base.OnToggleSubcontent(toggle);
|
||||
|
||||
StructInfo.RefreshUI(m_inputs, this.Value);
|
||||
}
|
||||
|
||||
internal Type m_lastStructType;
|
||||
|
||||
internal void InitializeStructInfo()
|
||||
{
|
||||
var type = Value?.GetType() ?? FallbackType;
|
||||
|
||||
if (StructInfo != null && type == m_lastStructType)
|
||||
return;
|
||||
|
||||
if (StructInfo != null)
|
||||
DestroySubContent();
|
||||
|
||||
m_lastStructType = type;
|
||||
|
||||
StructInfo = StructInfoFactory.Create(type);
|
||||
|
||||
if (m_subContentParent.activeSelf)
|
||||
{
|
||||
ConstructSubcontent();
|
||||
}
|
||||
}
|
||||
|
||||
#region UI CONSTRUCTION
|
||||
|
||||
internal InputField[] m_inputs;
|
||||
|
||||
public override void ConstructUI(GameObject parent, GameObject subGroup)
|
||||
{
|
||||
base.ConstructUI(parent, subGroup);
|
||||
}
|
||||
|
||||
public override void ConstructSubcontent()
|
||||
{
|
||||
base.ConstructSubcontent();
|
||||
|
||||
if (StructInfo == null)
|
||||
{
|
||||
ExplorerCore.LogWarning("Setting up subcontent but structinfo is null");
|
||||
return;
|
||||
}
|
||||
|
||||
var editorContainer = UIFactory.CreateVerticalGroup(m_subContentParent, "EditorContent", false, true, true, true, 2, new Vector4(4,4,4,4),
|
||||
new Color(0.08f, 0.08f, 0.08f));
|
||||
|
||||
m_inputs = new InputField[StructInfo.FieldNames.Length];
|
||||
|
||||
for (int i = 0; i < StructInfo.FieldNames.Length; i++)
|
||||
AddEditorRow(i, editorContainer);
|
||||
|
||||
if (Owner.CanWrite)
|
||||
{
|
||||
var applyBtn = UIFactory.CreateButton(editorContainer, "ApplyButton", "Apply", OnSetValue, new Color(0.2f, 0.2f, 0.2f));
|
||||
UIFactory.SetLayoutElement(applyBtn.gameObject, minWidth: 175, minHeight: 25, flexibleWidth: 0);
|
||||
|
||||
void OnSetValue()
|
||||
{
|
||||
Owner.SetValue();
|
||||
RefreshUIForValue();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal void AddEditorRow(int index, GameObject groupObj)
|
||||
{
|
||||
var rowObj = UIFactory.CreateHorizontalGroup(groupObj, "EditorRow", false, true, true, true, 5, default, new Color(1, 1, 1, 0));
|
||||
|
||||
var label = UIFactory.CreateLabel(rowObj, "RowLabel", $"{StructInfo.FieldNames[index]}:", TextAnchor.MiddleRight, Color.cyan);
|
||||
UIFactory.SetLayoutElement(label.gameObject, minWidth: 50, flexibleWidth: 0, minHeight: 25);
|
||||
|
||||
var inputFieldObj = UIFactory.CreateInputField(rowObj, "InputField", "...", 14, 3, 1);
|
||||
UIFactory.SetLayoutElement(inputFieldObj, minWidth: 120, minHeight: 25, flexibleWidth: 0);
|
||||
|
||||
var inputField = inputFieldObj.GetComponent<InputField>();
|
||||
m_inputs[index] = inputField;
|
||||
|
||||
inputField.onValueChanged.AddListener((string val) => { Value = StructInfo.SetValue(ref this.Value, index, float.Parse(val)); });
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
@ -31,8 +31,8 @@ namespace UnityExplorer.UI.InteractiveValues
|
||||
// arbitrarily check some types, fastest methods first.
|
||||
if (type == typeof(bool))
|
||||
return typeof(InteractiveBool);
|
||||
// if type is primitive then it must be a number if its not a bool
|
||||
else if (type.IsPrimitive)
|
||||
// if type is primitive then it must be a number if its not a bool. Also check for decimal.
|
||||
else if (type.IsPrimitive || type == typeof(decimal))
|
||||
return typeof(InteractiveNumber);
|
||||
// check for strings
|
||||
else if (type == typeof(string))
|
||||
@ -49,8 +49,8 @@ namespace UnityExplorer.UI.InteractiveValues
|
||||
// check for unity struct types
|
||||
else if (typeof(Color).IsAssignableFrom(type))
|
||||
return typeof(InteractiveColor);
|
||||
else if (InteractiveUnityStruct.SupportsType(type))
|
||||
return typeof(InteractiveUnityStruct);
|
||||
else if (InteractiveFloatStruct.IsTypeSupported(type))
|
||||
return typeof(InteractiveFloatStruct);
|
||||
// check Transform, force InteractiveValue so they dont become InteractiveEnumerables.
|
||||
else if (typeof(Transform).IsAssignableFrom(type))
|
||||
return typeof(InteractiveValue);
|
||||
|
@ -7,9 +7,19 @@ using UnityEngine.UI;
|
||||
|
||||
namespace UnityExplorer.UI.Main
|
||||
{
|
||||
public enum MenuPages
|
||||
{
|
||||
Home,
|
||||
Search,
|
||||
CSConsole,
|
||||
Options
|
||||
}
|
||||
|
||||
public abstract class BaseMenuPage
|
||||
{
|
||||
public abstract string Name { get; }
|
||||
public abstract MenuPages Type { get; }
|
||||
public bool WasDisabled { get; internal set; }
|
||||
|
||||
public GameObject Content;
|
||||
public Button RefNavbarButton { get; set; }
|
||||
@ -20,7 +30,6 @@ namespace UnityExplorer.UI.Main
|
||||
set => Content?.SetActive(true);
|
||||
}
|
||||
|
||||
|
||||
public abstract bool Init();
|
||||
public abstract void Update();
|
||||
}
|
||||
|
@ -13,15 +13,13 @@ using UnityExplorer.UI.Main.CSConsole;
|
||||
using UnityExplorer.Core;
|
||||
using UnityExplorer.Core.Unity;
|
||||
using UnityExplorer.UI.Utility;
|
||||
#if CPP
|
||||
using UnityExplorer.Core.Runtime.Il2Cpp;
|
||||
#endif
|
||||
|
||||
namespace UnityExplorer.UI.Main.CSConsole
|
||||
{
|
||||
public class CSharpConsole : BaseMenuPage
|
||||
{
|
||||
public override string Name => "C# Console";
|
||||
public override MenuPages Type => MenuPages.CSConsole;
|
||||
|
||||
public static CSharpConsole Instance { get; private set; }
|
||||
|
||||
@ -53,13 +51,10 @@ namespace UnityExplorer.UI.Main.CSConsole
|
||||
InitConsole();
|
||||
|
||||
AutoCompleter.Init();
|
||||
#if MONO
|
||||
DummyBehaviour.Setup();
|
||||
#endif
|
||||
|
||||
ResetConsole(false);
|
||||
// Make sure compiler is supported on this platform
|
||||
Evaluator.Compile("");
|
||||
Evaluator.Compile("new object();");
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -73,6 +68,8 @@ namespace UnityExplorer.UI.Main.CSConsole
|
||||
|
||||
ExplorerCore.LogWarning(info);
|
||||
|
||||
this.RefNavbarButton.GetComponentInChildren<Text>().text += " (disabled)";
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -100,9 +97,6 @@ namespace UnityExplorer.UI.Main.CSConsole
|
||||
UpdateConsole();
|
||||
|
||||
AutoCompleter.Update();
|
||||
#if CPP
|
||||
Il2CppCoroutine.Process();
|
||||
#endif
|
||||
}
|
||||
|
||||
public void AddUsing(string asm)
|
||||
|
@ -13,6 +13,8 @@ namespace UnityExplorer.UI.Main.Home
|
||||
|
||||
public static HomePage Instance { get; internal set; }
|
||||
|
||||
public override MenuPages Type => MenuPages.Home;
|
||||
|
||||
public override bool Init()
|
||||
{
|
||||
Instance = this;
|
||||
|
@ -26,7 +26,7 @@ namespace UnityExplorer.UI.Main
|
||||
public readonly List<BaseMenuPage> Pages = new List<BaseMenuPage>();
|
||||
private BaseMenuPage m_activePage;
|
||||
|
||||
public static Action<int> OnActiveTabChanged;
|
||||
public static Action<MenuPages> OnActiveTabChanged;
|
||||
|
||||
// Navbar buttons
|
||||
private Button m_lastNavButtonPressed;
|
||||
@ -38,8 +38,6 @@ namespace UnityExplorer.UI.Main
|
||||
internal bool pageLayoutInit;
|
||||
internal int layoutInitIndex;
|
||||
|
||||
private int origDesiredPage = -1;
|
||||
|
||||
public static void Create()
|
||||
{
|
||||
if (Instance != null)
|
||||
@ -67,6 +65,7 @@ namespace UnityExplorer.UI.Main
|
||||
|
||||
if (!page.Init())
|
||||
{
|
||||
page.WasDisabled = true;
|
||||
// page init failed.
|
||||
Pages.RemoveAt(i);
|
||||
i--;
|
||||
@ -88,9 +87,6 @@ namespace UnityExplorer.UI.Main
|
||||
{
|
||||
if (!pageLayoutInit)
|
||||
{
|
||||
if (origDesiredPage == -1)
|
||||
origDesiredPage = ConfigManager.Last_Active_Tab?.Value ?? 0;
|
||||
|
||||
if (layoutInitIndex < Pages.Count)
|
||||
{
|
||||
SetPage(Pages[layoutInitIndex]);
|
||||
@ -100,7 +96,8 @@ namespace UnityExplorer.UI.Main
|
||||
{
|
||||
pageLayoutInit = true;
|
||||
MainPanel.transform.position = initPos;
|
||||
SetPage(Pages[origDesiredPage]);
|
||||
|
||||
SetPage(ConfigManager.Default_Tab.Value);
|
||||
}
|
||||
return;
|
||||
}
|
||||
@ -108,9 +105,17 @@ namespace UnityExplorer.UI.Main
|
||||
m_activePage?.Update();
|
||||
}
|
||||
|
||||
public void SetPage(MenuPages page)
|
||||
{
|
||||
var pageObj = Pages.Find(it => it.Type == page);
|
||||
if (pageObj == null || pageObj.WasDisabled)
|
||||
return;
|
||||
SetPage(pageObj);
|
||||
}
|
||||
|
||||
public void SetPage(BaseMenuPage page)
|
||||
{
|
||||
if (page == null || m_activePage == page)
|
||||
if (page == null || m_activePage == page || page.WasDisabled)
|
||||
return;
|
||||
|
||||
m_activePage?.Content?.SetActive(false);
|
||||
@ -131,7 +136,7 @@ namespace UnityExplorer.UI.Main
|
||||
|
||||
m_lastNavButtonPressed = button;
|
||||
|
||||
OnActiveTabChanged?.Invoke(Pages.IndexOf(m_activePage));
|
||||
OnActiveTabChanged?.Invoke(m_activePage.Type);
|
||||
}
|
||||
|
||||
internal void SetButtonActiveColors(Button button)
|
||||
|
@ -12,6 +12,7 @@ namespace UnityExplorer.UI.Main.Options
|
||||
public class OptionsPage : BaseMenuPage
|
||||
{
|
||||
public override string Name => "Options";
|
||||
public override MenuPages Type => MenuPages.Options;
|
||||
|
||||
internal static readonly List<CacheConfigEntry> _cachedConfigEntries = new List<CacheConfigEntry>();
|
||||
|
||||
|
@ -17,6 +17,7 @@ namespace UnityExplorer.UI.Main.Search
|
||||
public class SearchPage : BaseMenuPage
|
||||
{
|
||||
public override string Name => "Search";
|
||||
public override MenuPages Type => MenuPages.Search;
|
||||
|
||||
public static SearchPage Instance;
|
||||
|
||||
|
@ -140,6 +140,8 @@ namespace UnityExplorer.UI
|
||||
ExplorerCore.Log("This game does not ship with the 'UI/Default' shader, using manual Default Shader...");
|
||||
Graphic.defaultGraphicMaterial.shader = BackupShader;
|
||||
}
|
||||
else
|
||||
BackupShader = Graphic.defaultGraphicMaterial.shader;
|
||||
|
||||
ConsoleFont = bundle.LoadAsset<Font>("CONSOLA");
|
||||
|
||||
@ -148,9 +150,11 @@ namespace UnityExplorer.UI
|
||||
|
||||
private static AssetBundle LoadExplorerUi(string id)
|
||||
{
|
||||
var data = ReadFully(typeof(ExplorerCore)
|
||||
var stream = typeof(ExplorerCore)
|
||||
.Assembly
|
||||
.GetManifestResourceStream($"UnityExplorer.Resources.explorerui.{id}.bundle"));
|
||||
.GetManifestResourceStream($"UnityExplorer.Resources.explorerui.{id}.bundle");
|
||||
|
||||
var data = ReadFully(stream);
|
||||
|
||||
return AssetBundle.LoadFromMemory(data);
|
||||
}
|
||||
|
@ -219,7 +219,7 @@
|
||||
<Compile Include="Core\Config\ConfigManager.cs" />
|
||||
<Compile Include="Core\Config\IConfigElement.cs" />
|
||||
<Compile Include="Core\Config\ConfigHandler.cs" />
|
||||
<Compile Include="Core\CSharp\DummyBehaviour.cs" />
|
||||
<Compile Include="Core\Runtime\Mono\DummyBehaviour.cs" />
|
||||
<Compile Include="Core\CSharp\ScriptEvaluator.cs" />
|
||||
<Compile Include="Core\CSharp\ScriptInteraction.cs" />
|
||||
<Compile Include="Core\CSharp\Suggestion.cs" />
|
||||
@ -273,7 +273,7 @@
|
||||
<Compile Include="UI\InteractiveValues\InteractiveFlags.cs" />
|
||||
<Compile Include="UI\InteractiveValues\InteractiveNumber.cs" />
|
||||
<Compile Include="UI\InteractiveValues\InteractiveString.cs" />
|
||||
<Compile Include="UI\InteractiveValues\InteractiveUnityStruct.cs" />
|
||||
<Compile Include="UI\InteractiveValues\InteractiveFloatStruct.cs" />
|
||||
<Compile Include="UI\InteractiveValues\InteractiveValue.cs" />
|
||||
<Compile Include="UI\Main\BaseMenuPage.cs" />
|
||||
<Compile Include="UI\Main\CSConsole\AutoCompleter.cs" />
|
||||
|
Reference in New Issue
Block a user