use UniverseLib

This commit is contained in:
Sinai
2021-12-02 18:35:46 +11:00
parent 077a2b434a
commit 3334549902
111 changed files with 540 additions and 7819 deletions

View File

@ -6,6 +6,8 @@ using UnityEngine;
using UnityExplorer.CSConsole.Lexers;
using UnityExplorer.UI;
using UnityExplorer.UI.Widgets.AutoComplete;
using UniverseLib;
using UniverseLib.UI;
namespace UnityExplorer.CSConsole
{

View File

@ -9,11 +9,13 @@ using System.Text;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
using UnityExplorer.Core.Input;
using UniverseLib.Input;
using UnityExplorer.CSConsole;
using UnityExplorer.UI;
using UnityExplorer.UI.Panels;
using UnityExplorer.UI.Widgets.AutoComplete;
using UniverseLib.UI;
using UniverseLib;
namespace UnityExplorer.CSConsole
{

View File

@ -6,6 +6,7 @@ using System.Text;
using UnityEngine;
using UnityEngine.UI;
using UnityExplorer.CSConsole.Lexers;
using UniverseLib;
namespace UnityExplorer.CSConsole
{

View File

@ -1,6 +1,7 @@
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UniverseLib;
namespace UnityExplorer.CSConsole.Lexers
{

View File

@ -6,6 +6,7 @@ using System.Linq;
using System.Text;
using UnityEngine;
using UnityExplorer.Core.Runtime;
using UniverseLib;
namespace UnityExplorer.CSConsole
{

View File

@ -4,6 +4,7 @@ using System.Linq;
using System.Text;
using UnityExplorer.CacheObject.IValues;
using UnityExplorer.CacheObject.Views;
using UniverseLib;
namespace UnityExplorer.CacheObject
{

View File

@ -7,8 +7,10 @@ using UnityEngine;
using UnityExplorer.Core.Runtime;
using UnityExplorer.CacheObject.Views;
using UnityExplorer.Inspectors;
using UnityExplorer.UI.Models;
using UniverseLib.UI.Models;
using UnityExplorer.UI;
using UniverseLib;
using UniverseLib.UI;
namespace UnityExplorer.CacheObject
{
@ -230,7 +232,7 @@ namespace UnityExplorer.CacheObject
{
try
{
if (ReflectionUtility.IsBlacklisted(member))
if (RuntimeHelper.IsBlacklisted(member))
return;
var sig = GetSig(member);

View File

@ -4,6 +4,7 @@ using System.Linq;
using System.Reflection;
using System.Text;
using UnityExplorer.Inspectors;
using UniverseLib;
namespace UnityExplorer.CacheObject
{

View File

@ -9,8 +9,10 @@ using UnityEngine.UI;
using UnityExplorer.Core.Runtime;
using UnityExplorer.CacheObject.IValues;
using UnityExplorer.CacheObject.Views;
using UnityExplorer.UI.Models;
using UniverseLib.UI.Models;
using UnityExplorer.UI;
using UniverseLib;
using UniverseLib.UI;
namespace UnityExplorer.CacheObject
{
@ -442,7 +444,7 @@ namespace UnityExplorer.CacheObject
{
inactiveIValueHolder = new GameObject("Temp_IValue_Holder");
GameObject.DontDestroyOnLoad(inactiveIValueHolder);
inactiveIValueHolder.transform.parent = UIManager.PoolHolder.transform;
inactiveIValueHolder.transform.parent = UniversalUI.PoolHolder.transform;
inactiveIValueHolder.SetActive(false);
}
return inactiveIValueHolder;

View File

@ -6,6 +6,7 @@ using UnityEngine;
using UnityEngine.UI;
using UnityExplorer.CacheObject;
using UnityExplorer.UI;
using UniverseLib.UI;
namespace UnityExplorer.CacheObject.IValues
{

View File

@ -11,6 +11,9 @@ using UnityExplorer.Inspectors;
using UnityExplorer.UI;
using UnityExplorer.UI.Panels;
using UnityExplorer.UI.Widgets;
using UniverseLib;
using UniverseLib.UI;
using UniverseLib.UI.Widgets;
namespace UnityExplorer.CacheObject.IValues
{

View File

@ -7,6 +7,7 @@ using UnityEngine;
using UnityEngine.UI;
using UnityExplorer.CacheObject;
using UnityExplorer.UI;
using UniverseLib.UI;
namespace UnityExplorer.CacheObject.IValues
{

View File

@ -11,6 +11,9 @@ using UnityExplorer.Inspectors;
using UnityExplorer.UI;
using UnityExplorer.UI.Panels;
using UnityExplorer.UI.Widgets;
using UniverseLib;
using UniverseLib.UI;
using UniverseLib.UI.Widgets;
namespace UnityExplorer.CacheObject.IValues
{

View File

@ -9,6 +9,8 @@ using UnityExplorer.Core.Config;
using UnityExplorer.CacheObject;
using UnityExplorer.UI.Widgets;
using UnityExplorer.UI;
using UniverseLib.UI;
using UniverseLib;
namespace UnityExplorer.CacheObject.IValues
{
@ -38,7 +40,7 @@ namespace UnityExplorer.CacheObject.IValues
if (s == null)
return false;
return s.Length >= UIManager.MAX_INPUTFIELD_CHARS;
return s.Length >= UniversalUI.MAX_INPUTFIELD_CHARS;
}
public override void SetValue(object value)

View File

@ -6,7 +6,8 @@ using UnityEngine;
using UnityEngine.UI;
using UnityExplorer.CacheObject;
using UnityExplorer.UI;
using UnityExplorer.UI.Models;
using UniverseLib.UI;
using UniverseLib.UI.Models;
namespace UnityExplorer.CacheObject.IValues
{

View File

@ -7,6 +7,8 @@ using UnityEngine;
using UnityEngine.UI;
using UnityExplorer.CacheObject;
using UnityExplorer.UI;
using UniverseLib;
using UniverseLib.UI;
namespace UnityExplorer.CacheObject.IValues
{

View File

@ -5,6 +5,8 @@ using System.Text;
using UnityEngine;
using UnityEngine.UI;
using UnityExplorer.UI;
using UniverseLib;
using UniverseLib.UI;
namespace UnityExplorer.CacheObject.Views
{

View File

@ -8,6 +8,7 @@ using UnityExplorer.CacheObject.IValues;
using UnityExplorer.Inspectors;
using UnityExplorer.UI;
using UnityExplorer.UI.Widgets;
using UniverseLib.UI;
namespace UnityExplorer.CacheObject.Views
{

View File

@ -6,6 +6,7 @@ using UnityEngine;
using UnityEngine.UI;
using UnityExplorer.UI;
using UnityExplorer.UI.Widgets;
using UniverseLib.UI;
namespace UnityExplorer.CacheObject.Views
{

View File

@ -8,6 +8,9 @@ using UnityExplorer.CacheObject.IValues;
using UnityExplorer.Inspectors;
using UnityExplorer.UI;
using UnityExplorer.UI.Widgets;
using UniverseLib;
using UniverseLib.UI;
using UniverseLib.UI.Widgets;
namespace UnityExplorer.CacheObject.Views
{

View File

@ -6,8 +6,10 @@ using System.Text;
using UnityEngine;
using UnityEngine.UI;
using UnityExplorer.UI;
using UnityExplorer.UI.Models;
using UniverseLib.UI.Models;
using UnityExplorer.UI.Widgets.AutoComplete;
using UniverseLib.UI;
using UniverseLib;
namespace UnityExplorer.CacheObject.Views
{

View File

@ -89,18 +89,22 @@ namespace UnityExplorer.Core.Config
Force_Unlock_Mouse = new ConfigElement<bool>("Force Unlock Mouse",
"Force the Cursor to be unlocked (visible) when the UnityExplorer menu is open.",
true);
Force_Unlock_Mouse.OnValueChanged += (bool value) =>
{
UniverseLib.Config.ConfigManager.Force_Unlock_Mouse = value;
};
Force_Unlock_Toggle = new ConfigElement<KeyCode>("Force Unlock Toggle Key",
"The keybind to toggle the 'Force Unlock Mouse' setting. Only usable when UnityExplorer is open.",
KeyCode.None);
Aggressive_Mouse_Unlock = new ConfigElement<bool>("Aggressive Mouse Unlock",
"Use WaitForEndOfFrame to aggressively force the Mouse to be unlocked.\n<b>Requires restart to take effect.</b>",
false);
Disable_EventSystem_Override = new ConfigElement<bool>("Disable EventSystem override",
"If enabled, UnityExplorer will not override the EventSystem from the game.\n<b>May require restart to take effect.</b>",
false);
Disable_EventSystem_Override.OnValueChanged += (bool value) =>
{
UniverseLib.Config.ConfigManager.Disable_EventSystem_Override = value;
};
Log_Unity_Debug = new ConfigElement<bool>("Log Unity Debug",
"Should UnityEngine.Debug.Log messages be printed to UnityExplorer's log?",

View File

@ -32,53 +32,9 @@ namespace UnityExplorer
public ExplorerBehaviour(IntPtr ptr) : base(ptr) { }
#endif
private static bool onPostRenderFailed;
internal void Awake()
{
try
{
#if CPP
Camera.onPostRender = Camera.onPostRender == null
? new Action<Camera>(OnPostRender)
: Il2CppSystem.Delegate.Combine(Camera.onPostRender,
(Camera.CameraCallback)new Action<Camera>(OnPostRender)).Cast<Camera.CameraCallback>();
if (Camera.onPostRender == null || Camera.onPostRender.delegates == null)
{
ExplorerCore.LogWarning("Failed to add Camera.onPostRender listener, falling back to LateUpdate instead!");
onPostRenderFailed = true;
}
#else
Camera.onPostRender += OnPostRender;
#endif
}
catch (Exception ex)
{
ExplorerCore.LogWarning($"Exception adding onPostRender listener: {ex.ReflectionExToString()}\r\nFalling back to LateUpdate!");
onPostRenderFailed = true;
}
}
internal void Update()
{
ExplorerCore.Update();
}
internal void FixedUpdate()
{
ExplorerCore.FixedUpdate();
}
internal void LateUpdate()
{
if (onPostRenderFailed)
OnPostRender(null);
}
internal static void OnPostRender(Camera _)
{
ExplorerCore.OnPostRender();
}
}
}

View File

@ -1,281 +0,0 @@
using HarmonyLib;
using System;
using System.Collections;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityExplorer.Core;
using UnityExplorer.Core.Config;
using UnityExplorer.Core.Input;
using UnityExplorer.UI;
namespace UnityExplorer.Core.Input
{
public class CursorUnlocker
{
public static bool Unlock
{
get => m_forceUnlock;
set
{
m_forceUnlock = value;
UpdateCursorControl();
}
}
private static bool m_forceUnlock;
public static bool ShouldActuallyUnlock => UIManager.ShowMenu && Unlock;
private static CursorLockMode lastLockMode;
private static bool lastVisibleState;
private static bool currentlySettingCursor = false;
public static void Init()
{
lastLockMode = Cursor.lockState;
lastVisibleState = Cursor.visible;
SetupPatches();
UpdateCursorControl();
// Hook up config values
// Force Unlock Mouse
Unlock = ConfigManager.Force_Unlock_Mouse.Value;
ConfigManager.Force_Unlock_Mouse.OnValueChanged += (bool val) => { Unlock = val; };
// Aggressive Mouse Unlock
if (ConfigManager.Aggressive_Mouse_Unlock.Value)
SetupAggressiveUnlock();
}
public static void SetupAggressiveUnlock()
{
try
{
RuntimeProvider.Instance.StartCoroutine(AggressiveUnlockCoroutine());
}
catch (Exception ex)
{
ExplorerCore.LogWarning($"Exception setting up Aggressive Mouse Unlock: {ex}");
}
}
private static WaitForEndOfFrame _waitForEndOfFrame = new WaitForEndOfFrame();
private static IEnumerator AggressiveUnlockCoroutine()
{
while (true)
{
yield return _waitForEndOfFrame ?? (_waitForEndOfFrame = new WaitForEndOfFrame());
if (UIManager.ShowMenu)
UpdateCursorControl();
}
}
public static void UpdateCursorControl()
{
try
{
currentlySettingCursor = true;
if (ShouldActuallyUnlock)
{
Cursor.lockState = CursorLockMode.None;
Cursor.visible = true;
if (!ConfigManager.Disable_EventSystem_Override.Value && UIManager.EventSys)
SetEventSystem();
}
else
{
Cursor.lockState = lastLockMode;
Cursor.visible = lastVisibleState;
if (!ConfigManager.Disable_EventSystem_Override.Value && UIManager.EventSys)
ReleaseEventSystem();
}
currentlySettingCursor = false;
}
catch (Exception e)
{
ExplorerCore.Log($"Exception setting Cursor state: {e.GetType()}, {e.Message}");
}
}
// Event system overrides
private static bool settingEventSystem;
private static EventSystem lastEventSystem;
private static BaseInputModule lastInputModule;
public static void SetEventSystem()
{
if (EventSystem.current && EventSystem.current != UIManager.EventSys)
{
lastEventSystem = EventSystem.current;
lastEventSystem.enabled = false;
}
// Set to our current system
settingEventSystem = true;
UIManager.EventSys.enabled = true;
EventSystem.current = UIManager.EventSys;
InputManager.ActivateUIModule();
settingEventSystem = false;
}
public static void ReleaseEventSystem()
{
if (lastEventSystem && lastEventSystem.gameObject.activeSelf)
{
lastEventSystem.enabled = true;
settingEventSystem = true;
UIManager.EventSys.enabled = false;
EventSystem.current = lastEventSystem;
lastInputModule?.ActivateModule();
settingEventSystem = false;
}
}
// Patches
public static void SetupPatches()
{
try
{
PrefixPropertySetter(typeof(Cursor),
"lockState",
new HarmonyMethod(typeof(CursorUnlocker).GetMethod(nameof(CursorUnlocker.Prefix_set_lockState))));
PrefixPropertySetter(typeof(Cursor),
"visible",
new HarmonyMethod(typeof(CursorUnlocker).GetMethod(nameof(CursorUnlocker.Prefix_set_visible))));
PrefixPropertySetter(typeof(EventSystem),
"current",
new HarmonyMethod(typeof(CursorUnlocker).GetMethod(nameof(CursorUnlocker.Prefix_EventSystem_set_current))));
PrefixMethod(typeof(EventSystem),
"SetSelectedGameObject",
// some games use a modified version of uGUI that includes this extra int argument on this method.
new Type[] { typeof(GameObject), typeof(BaseEventData), typeof(int) },
new HarmonyMethod(typeof(CursorUnlocker).GetMethod(nameof(CursorUnlocker.Prefix_EventSystem_SetSelectedGameObject))),
// most games use these arguments, we'll use them as our "backup".
new Type[] { typeof(GameObject), typeof(BaseEventData) });
//// Not sure if this one is needed.
//PrefixMethod(typeof(PointerInputModule),
// "ClearSelection",
// new Type[0],
// new HarmonyMethod(typeof(CursorUnlocker).GetMethod(nameof(CursorUnlocker.Prefix_PointerInputModule_ClearSelection))));
}
catch (Exception ex)
{
ExplorerCore.Log($"Exception setting up Harmony patches:\r\n{ex.ReflectionExToString()}");
}
}
private static void PrefixMethod(Type type, string method, Type[] arguments, HarmonyMethod prefix, Type[] backupArgs = null)
{
try
{
var methodInfo = type.GetMethod(method, ReflectionUtility.FLAGS, null, arguments, null);
if (methodInfo == null)
{
if (backupArgs != null)
methodInfo = type.GetMethod(method, ReflectionUtility.FLAGS, null, backupArgs, null);
if (methodInfo == null)
throw new MissingMethodException($"Could not find method for patching - '{type.FullName}.{method}'!");
}
var processor = ExplorerCore.Harmony.CreateProcessor(methodInfo);
processor.AddPrefix(prefix);
processor.Patch();
}
catch (Exception e)
{
ExplorerCore.LogWarning($"Unable to patch {type.Name}.{method}: {e.Message}");
}
}
private static void PrefixPropertySetter(Type type, string property, HarmonyMethod prefix)
{
try
{
var processor = ExplorerCore.Harmony.CreateProcessor(type.GetProperty(property, ReflectionUtility.FLAGS).GetSetMethod());
processor.AddPrefix(prefix);
processor.Patch();
}
catch (Exception e)
{
ExplorerCore.Log($"Unable to patch {type.Name}.set_{property}: {e.Message}");
}
}
// Prevent setting non-UnityExplorer objects as selected when menu is open
public static bool Prefix_EventSystem_SetSelectedGameObject(GameObject __0)
{
if (!UIManager.ShowMenu || !UIManager.CanvasRoot)
return true;
return __0 && __0.transform.root.gameObject.GetInstanceID() == UIManager.CanvasRoot.GetInstanceID();
}
//public static bool Prefix_PointerInputModule_ClearSelection()
//{
// return !(UIManager.ShowMenu && UIManager.CanvasRoot);
//}
// Force EventSystem.current to be UnityExplorer's when menu is open
public static void Prefix_EventSystem_set_current(ref EventSystem value)
{
if (!settingEventSystem && value)
{
lastEventSystem = value;
lastInputModule = value.currentInputModule;
}
if (!UIManager.EventSys)
return;
if (!settingEventSystem && ShouldActuallyUnlock && !ConfigManager.Disable_EventSystem_Override.Value)
{
value = UIManager.EventSys;
value.enabled = true;
}
}
// Force mouse to stay unlocked and visible while UnlockMouse and ShowMenu are true.
// Also keep track of when anything else tries to set Cursor state, this will be the
// value that we set back to when we close the menu or disable force-unlock.
public static void Prefix_set_lockState(ref CursorLockMode value)
{
if (!currentlySettingCursor)
{
lastLockMode = value;
if (ShouldActuallyUnlock)
value = CursorLockMode.None;
}
}
public static void Prefix_set_visible(ref bool value)
{
if (!currentlySettingCursor)
{
lastVisibleState = value;
if (ShouldActuallyUnlock)
value = true;
}
}
}
}

View File

@ -1,22 +0,0 @@
using UnityEngine;
using UnityEngine.EventSystems;
namespace UnityExplorer.Core.Input
{
public interface IHandleInput
{
Vector2 MousePosition { get; }
Vector2 MouseScrollDelta { get; }
bool GetKeyDown(KeyCode key);
bool GetKey(KeyCode key);
bool GetMouseButtonDown(int btn);
bool GetMouseButton(int btn);
BaseInputModule UIInputModule { get; }
void AddUIInputModule();
void ActivateModule();
}
}

View File

@ -1,108 +0,0 @@
using System;
using System.Diagnostics.CodeAnalysis;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityExplorer.UI;
namespace UnityExplorer.Core.Input
{
public enum InputType
{
InputSystem,
Legacy,
None
}
public static class InputManager
{
public static InputType CurrentType { get; private set; }
private static IHandleInput m_inputHandler;
public static Vector3 MousePosition => m_inputHandler.MousePosition;
public static bool GetKeyDown(KeyCode key)
{
if (key == KeyCode.None)
return false;
return m_inputHandler.GetKeyDown(key);
}
public static bool GetKey(KeyCode key)
{
if (key == KeyCode.None)
return false;
return m_inputHandler.GetKey(key);
}
public static bool GetMouseButtonDown(int btn) => m_inputHandler.GetMouseButtonDown(btn);
public static bool GetMouseButton(int btn) => m_inputHandler.GetMouseButton(btn);
public static BaseInputModule UIInput => m_inputHandler.UIInputModule;
public static Vector2 MouseScrollDelta => m_inputHandler.MouseScrollDelta;
public static void AddUIModule()
{
m_inputHandler.AddUIInputModule();
ActivateUIModule();
}
public static void ActivateUIModule()
{
UIManager.EventSys.m_CurrentInputModule = UIInput;
m_inputHandler.ActivateModule();
}
public static void Init()
{
InitHandler();
CursorUnlocker.Init();
}
private static void InitHandler()
{
// First, just try to use the legacy input, see if its working.
// The InputSystem package may be present but not actually activated, so we can find out this way.
if (LegacyInput.TInput != null)
{
try
{
m_inputHandler = new LegacyInput();
CurrentType = InputType.Legacy;
// make sure its working
GetKeyDown(KeyCode.F5);
ExplorerCore.Log("Initialized Legacy Input support");
return;
}
catch
{
// It's not working, we'll fall back to InputSystem.
}
}
if (InputSystem.TKeyboard != null)
{
try
{
m_inputHandler = new InputSystem();
CurrentType = InputType.InputSystem;
ExplorerCore.Log("Initialized new InputSystem support.");
return;
}
catch (Exception ex)
{
ExplorerCore.Log(ex);
}
}
ExplorerCore.LogWarning("Could not find any Input Module Type!");
m_inputHandler = new NoInput();
CurrentType = InputType.None;
}
}
}

View File

@ -1,280 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityExplorer.UI;
namespace UnityExplorer.Core.Input
{
public class InputSystem : IHandleInput
{
public InputSystem()
{
SetupSupportedDevices();
m_kbCurrentProp = TKeyboard.GetProperty("current");
m_kbIndexer = TKeyboard.GetProperty("Item", new Type[] { TKey });
var btnControl = ReflectionUtility.GetTypeByName("UnityEngine.InputSystem.Controls.ButtonControl");
m_btnIsPressedProp = btnControl.GetProperty("isPressed");
m_btnWasPressedProp = btnControl.GetProperty("wasPressedThisFrame");
m_mouseCurrentProp = TMouse.GetProperty("current");
m_leftButtonProp = TMouse.GetProperty("leftButton");
m_rightButtonProp = TMouse.GetProperty("rightButton");
m_scrollDeltaProp = TMouse.GetProperty("scroll");
m_positionProp = ReflectionUtility.GetTypeByName("UnityEngine.InputSystem.Pointer")
.GetProperty("position");
ReadV2ControlMethod = ReflectionUtility.GetTypeByName("UnityEngine.InputSystem.InputControl`1")
.MakeGenericType(typeof(Vector2))
.GetMethod("ReadValue");
}
internal static void SetupSupportedDevices()
{
try
{
// typeof(InputSystem)
Type TInputSystem = ReflectionUtility.GetTypeByName("UnityEngine.InputSystem.InputSystem");
// InputSystem.settings
var settings = TInputSystem.GetProperty("settings", BindingFlags.Public | BindingFlags.Static).GetValue(null, null);
// typeof(InputSettings)
Type TSettings = settings.GetActualType();
// InputSettings.supportedDevices
PropertyInfo supportedProp = TSettings.GetProperty("supportedDevices", BindingFlags.Public | BindingFlags.Instance);
var supportedDevices = supportedProp.GetValue(settings, null);
// An empty supportedDevices list means all devices are supported.
#if CPP
// weird hack for il2cpp, use the implicit operator and cast Il2CppStringArray to ReadOnlyArray<string>
var args = new object[] { new UnhollowerBaseLib.Il2CppStringArray(0) };
var method = supportedDevices.GetActualType().GetMethod("op_Implicit", BindingFlags.Static | BindingFlags.Public);
supportedProp.SetValue(settings, method.Invoke(null, args), null);
#else
supportedProp.SetValue(settings, Activator.CreateInstance(supportedDevices.GetActualType(), new object[] { new string[0] }), null);
#endif
}
catch (Exception ex)
{
ExplorerCore.LogWarning($"Exception setting up InputSystem.settings.supportedDevices list!");
ExplorerCore.Log(ex);
}
}
#region reflection cache
public static Type TKeyboard => m_tKeyboard ?? (m_tKeyboard = ReflectionUtility.GetTypeByName("UnityEngine.InputSystem.Keyboard"));
private static Type m_tKeyboard;
public static Type TMouse => m_tMouse ?? (m_tMouse = ReflectionUtility.GetTypeByName("UnityEngine.InputSystem.Mouse"));
private static Type m_tMouse;
public static Type TKey => m_tKey ?? (m_tKey = ReflectionUtility.GetTypeByName("UnityEngine.InputSystem.Key"));
private static Type m_tKey;
private static PropertyInfo m_btnIsPressedProp;
private static PropertyInfo m_btnWasPressedProp;
private static object CurrentKeyboard => m_currentKeyboard ?? (m_currentKeyboard = m_kbCurrentProp.GetValue(null, null));
private static object m_currentKeyboard;
private static PropertyInfo m_kbCurrentProp;
private static PropertyInfo m_kbIndexer;
private static object CurrentMouse => m_currentMouse ?? (m_currentMouse = m_mouseCurrentProp.GetValue(null, null));
private static object m_currentMouse;
private static PropertyInfo m_mouseCurrentProp;
private static object LeftMouseButton => m_lmb ?? (m_lmb = m_leftButtonProp.GetValue(CurrentMouse, null));
private static object m_lmb;
private static PropertyInfo m_leftButtonProp;
private static object RightMouseButton => m_rmb ?? (m_rmb = m_rightButtonProp.GetValue(CurrentMouse, null));
private static object m_rmb;
private static PropertyInfo m_rightButtonProp;
private static MethodInfo ReadV2ControlMethod;
private static object MousePositionInfo => m_pos ?? (m_pos = m_positionProp.GetValue(CurrentMouse, null));
private static object m_pos;
private static PropertyInfo m_positionProp;
private static object MouseScrollInfo => m_scrollInfo ?? (m_scrollInfo = m_scrollDeltaProp.GetValue(CurrentMouse, null));
private static object m_scrollInfo;
private static PropertyInfo m_scrollDeltaProp;
#endregion
public Vector2 MousePosition
{
get
{
try
{
return (Vector2)ReadV2ControlMethod.Invoke(MousePositionInfo, ArgumentUtility.EmptyArgs);
}
catch { return Vector2.zero; }
}
}
public Vector2 MouseScrollDelta
{
get
{
try
{
return (Vector2)ReadV2ControlMethod.Invoke(MouseScrollInfo, ArgumentUtility.EmptyArgs);
}
catch { return Vector2.zero; }
}
}
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();
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 });
ActualKeyDict.Add(key, actualKey);
}
return ActualKeyDict[key];
}
public bool GetKeyDown(KeyCode key) => (bool)m_btnWasPressedProp.GetValue(GetActualKey(key), null);
public bool GetKey(KeyCode key) => (bool)m_btnIsPressedProp.GetValue(GetActualKey(key), null);
public bool GetMouseButtonDown(int btn)
{
if (CurrentMouse == null)
return false;
switch (btn)
{
case 0: return (bool)m_btnWasPressedProp.GetValue(LeftMouseButton, null);
case 1: return (bool)m_btnWasPressedProp.GetValue(RightMouseButton, null);
// case 2: return (bool)_btnWasPressedProp.GetValue(MiddleMouseButton, null);
default: throw new NotImplementedException();
}
}
public bool GetMouseButton(int btn)
{
if (CurrentMouse == null)
return false;
switch (btn)
{
case 0: return (bool)m_btnIsPressedProp.GetValue(LeftMouseButton, null);
case 1: return (bool)m_btnIsPressedProp.GetValue(RightMouseButton, null);
// case 2: return (bool)_btnIsPressedProp.GetValue(MiddleMouseButton, null);
default: throw new NotImplementedException();
}
}
// UI Input
public Type TInputSystemUIInputModule
=> m_tUIInputModule
?? (m_tUIInputModule = ReflectionUtility.GetTypeByName("UnityEngine.InputSystem.UI.InputSystemUIInputModule"));
internal Type m_tUIInputModule;
public BaseInputModule UIInputModule => m_newInputModule;
internal BaseInputModule m_newInputModule;
public void AddUIInputModule()
{
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)
.TryCast(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" })
.TryCast(ReflectionUtility.GetTypeByName("UnityEngine.InputSystem.InputActionMap"));
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, ArgumentUtility.EmptyArgs);
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 disable = map.GetType().GetMethod("Disable");
disable.Invoke(map, ArgumentUtility.EmptyArgs);
var inputActionType = ReflectionUtility.GetTypeByName("UnityEngine.InputSystem.InputAction");
var addAction = inputExtensions.GetMethod("AddAction");
var action = addAction.Invoke(null, new object[] { map, actionName, default, null, null, null, null, null })
.TryCast(inputActionType);
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[] { action.TryCast(inputActionType), binding, null, null, null });
var refType = ReflectionUtility.GetTypeByName("UnityEngine.InputSystem.InputActionReference");
var inputRef = refType.GetMethod("Create")
.Invoke(null, new object[] { action })
.TryCast(refType);
TInputSystemUIInputModule
.GetProperty(propertyName)
.SetValue(m_newInputModule.TryCast(TInputSystemUIInputModule), inputRef, null);
}
public void ActivateModule()
{
try
{
m_newInputModule.m_EventSystem = UIManager.EventSys;
m_newInputModule.ActivateModule();
UI_Enable.Invoke(UI_ActionMap, ArgumentUtility.EmptyArgs);
}
catch (Exception ex)
{
ExplorerCore.LogWarning("Exception enabling InputSystem UI Input Module: " + ex);
}
}
}
}

View File

@ -1,66 +0,0 @@
using System;
using System.Reflection;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityExplorer.UI;
namespace UnityExplorer.Core.Input
{
public class LegacyInput : IHandleInput
{
public LegacyInput()
{
m_mousePositionProp = TInput.GetProperty("mousePosition");
m_mouseDeltaProp = TInput.GetProperty("mouseScrollDelta");
m_getKeyMethod = TInput.GetMethod("GetKey", new Type[] { typeof(KeyCode) });
m_getKeyDownMethod = TInput.GetMethod("GetKeyDown", new Type[] { typeof(KeyCode) });
m_getMouseButtonMethod = TInput.GetMethod("GetMouseButton", new Type[] { typeof(int) });
m_getMouseButtonDownMethod = TInput.GetMethod("GetMouseButtonDown", new Type[] { typeof(int) });
}
public static Type TInput => m_tInput ?? (m_tInput = ReflectionUtility.GetTypeByName("UnityEngine.Input"));
private static Type m_tInput;
private static PropertyInfo m_mousePositionProp;
private static PropertyInfo m_mouseDeltaProp;
private static MethodInfo m_getKeyMethod;
private static MethodInfo m_getKeyDownMethod;
private static MethodInfo m_getMouseButtonMethod;
private static MethodInfo m_getMouseButtonDownMethod;
public Vector2 MousePosition => (Vector3)m_mousePositionProp.GetValue(null, null);
public Vector2 MouseScrollDelta => (Vector2)m_mouseDeltaProp.GetValue(null, null);
public bool GetKey(KeyCode key) => (bool)m_getKeyMethod.Invoke(null, new object[] { key });
public bool GetKeyDown(KeyCode key) => (bool)m_getKeyDownMethod.Invoke(null, new object[] { key });
public bool GetMouseButton(int btn) => (bool)m_getMouseButtonMethod.Invoke(null, new object[] { btn });
public bool GetMouseButtonDown(int btn) => (bool)m_getMouseButtonDownMethod.Invoke(null, new object[] { btn });
// UI Input module
public BaseInputModule UIInputModule => m_inputModule;
internal StandaloneInputModule m_inputModule;
public void AddUIInputModule()
{
m_inputModule = UIManager.CanvasRoot.gameObject.AddComponent<StandaloneInputModule>();
m_inputModule.m_EventSystem = UIManager.EventSys;
}
public void ActivateModule()
{
try
{
m_inputModule.ActivateModule();
}
catch (Exception ex)
{
ExplorerCore.LogWarning($"Exception enabling StandaloneInputModule: {ex}");
}
}
}
}

View File

@ -1,23 +0,0 @@
using UnityEngine;
using UnityEngine.EventSystems;
namespace UnityExplorer.Core.Input
{
// Just a stub for games where no Input module was able to load at all.
public class NoInput : IHandleInput
{
public Vector2 MousePosition => Vector2.zero;
public Vector2 MouseScrollDelta => Vector2.zero;
public bool GetKey(KeyCode key) => false;
public bool GetKeyDown(KeyCode key) => false;
public bool GetMouseButton(int btn) => false;
public bool GetMouseButtonDown(int btn) => false;
public BaseInputModule UIInputModule => null;
public void ActivateModule() { }
public void AddUIInputModule() { }
}
}

View File

@ -1,113 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
namespace UnityExplorer
{
public static class ReflectionExtensions
{
// ReflectionUtility extensions
public static Type GetActualType(this object obj)
=> ReflectionUtility.Instance.Internal_GetActualType(obj);
public static object TryCast(this object obj)
=> ReflectionUtility.Instance.Internal_TryCast(obj, ReflectionUtility.Instance.Internal_GetActualType(obj));
public static object TryCast(this object obj, Type castTo)
=> ReflectionUtility.Instance.Internal_TryCast(obj, castTo);
public static T TryCast<T>(this object obj)
{
try
{
return (T)ReflectionUtility.Instance.Internal_TryCast(obj, typeof(T));
}
catch
{
return default;
}
}
// ------- Misc extensions --------
/// <summary>
/// Safely try to get all Types inside an Assembly.
/// </summary>
public static IEnumerable<Type> TryGetTypes(this Assembly asm)
{
try
{
return asm.GetTypes();
}
catch (ReflectionTypeLoadException e)
{
try
{
return asm.GetExportedTypes();
}
catch
{
return e.Types.Where(t => t != null);
}
}
catch
{
return Enumerable.Empty<Type>();
}
}
/// <summary>
/// Check if the two objects are reference-equal, including checking for UnityEngine.Object-equality and Il2CppSystem.Object-equality.
/// </summary>
public static bool ReferenceEqual(this object objA, object objB)
{
if (object.ReferenceEquals(objA, objB))
return true;
if (objA is UnityEngine.Object unityA && objB is UnityEngine.Object unityB)
{
if (unityA && unityB && unityA.m_CachedPtr == unityB.m_CachedPtr)
return true;
}
#if CPP
if (objA is Il2CppSystem.Object cppA && objB is Il2CppSystem.Object cppB
&& cppA.Pointer == cppB.Pointer)
return true;
#endif
return false;
}
/// <summary>
/// Helper to display a simple "{ExceptionType}: {Message}" of the exception, and optionally use the inner-most exception.
/// </summary>
public static string ReflectionExToString(this Exception e, bool innerMost = true)
{
if (innerMost)
e = e.GetInnerMostException();
return $"{e.GetType()}: {e.Message}";
}
public static Exception GetInnerMostException(this Exception e)
{
while (e != null)
{
if (e.InnerException == null)
break;
#if CPP
if (e.InnerException is System.Runtime.CompilerServices.RuntimeWrappedException)
break;
#endif
e = e.InnerException;
}
return e;
}
}
}

View File

@ -1,956 +0,0 @@
#if CPP
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnhollowerBaseLib;
using UnhollowerRuntimeLib;
using System.Runtime.InteropServices;
using System.Reflection;
using System.Collections;
using System.IO;
using System.Diagnostics.CodeAnalysis;
using UnityExplorer.Core;
using CppType = Il2CppSystem.Type;
using BF = System.Reflection.BindingFlags;
using UnhollowerBaseLib.Attributes;
using UnityEngine;
namespace UnityExplorer
{
public class Il2CppReflection : ReflectionUtility
{
protected override void Initialize()
{
base.Initialize();
float start = Time.realtimeSinceStartup;
TryLoadGameModules();
ExplorerCore.Log($"Loaded Unhollowed modules in {Time.realtimeSinceStartup - start} seconds");
start = Time.realtimeSinceStartup;
BuildDeobfuscationCache();
OnTypeLoaded += TryCacheDeobfuscatedType;
ExplorerCore.Log($"Setup IL2CPP reflection in {Time.realtimeSinceStartup - start} seconds, " +
$"deobfuscated types count: {DeobfuscatedTypes.Count}");
}
#region IL2CPP Extern and pointers
// Extern C++ methods
[DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
public static extern bool il2cpp_class_is_assignable_from(IntPtr klass, IntPtr oklass);
[DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
public static extern IntPtr il2cpp_object_get_class(IntPtr obj);
public static bool Il2CppTypeNotNull(Type type) => Il2CppTypeNotNull(type, out _);
public static bool Il2CppTypeNotNull(Type type, out IntPtr il2cppPtr)
{
if (!cppClassPointers.TryGetValue(type.AssemblyQualifiedName, out il2cppPtr))
{
il2cppPtr = (IntPtr)typeof(Il2CppClassPointerStore<>)
.MakeGenericType(new Type[] { type })
.GetField("NativeClassPtr", BF.Public | BF.Static)
.GetValue(null);
cppClassPointers.Add(type.AssemblyQualifiedName, il2cppPtr);
}
return il2cppPtr != IntPtr.Zero;
}
#endregion
#region Deobfuscation cache
private static readonly Dictionary<string, Type> DeobfuscatedTypes = new Dictionary<string, Type>();
private static readonly Dictionary<string, string> reverseDeobCache = new Dictionary<string, string>();
private static void BuildDeobfuscationCache()
{
foreach (var asm in AppDomain.CurrentDomain.GetAssemblies())
{
foreach (var type in asm.TryGetTypes())
TryCacheDeobfuscatedType(type);
}
}
private static void TryCacheDeobfuscatedType(Type type)
{
try
{
if (!type.CustomAttributes.Any())
return;
foreach (var att in type.CustomAttributes)
{
// Thanks to Slaynash for this
if (att.AttributeType == typeof(ObfuscatedNameAttribute))
{
string obfuscatedName = att.ConstructorArguments[0].Value.ToString();
DeobfuscatedTypes.Add(obfuscatedName, type);
reverseDeobCache.Add(type.FullName, obfuscatedName);
}
}
}
catch { }
}
internal override string Internal_ProcessTypeInString(string theString, Type type)
{
if (reverseDeobCache.TryGetValue(type.FullName, out string obName))
return theString.Replace(obName, type.FullName);
return theString;
}
#endregion
// Get type by name
internal override Type Internal_GetTypeByName(string fullName)
{
if (DeobfuscatedTypes.TryGetValue(fullName, out Type deob))
return deob;
return base.Internal_GetTypeByName(fullName);
}
#region Get actual type
internal override Type Internal_GetActualType(object obj)
{
if (obj == null)
return null;
var type = obj.GetType();
try
{
if (type.IsGenericType)
return type;
if (IsString(obj))
return typeof(string);
if (IsIl2CppPrimitive(type))
return il2cppPrimitivesToMono[type.FullName];
if (obj is Il2CppSystem.Object cppObject)
{
var cppType = cppObject.GetIl2CppType();
// check if type is injected
IntPtr classPtr = il2cpp_object_get_class(cppObject.Pointer);
if (RuntimeSpecificsStore.IsInjected(classPtr))
{
// Note: This will fail on injected subclasses.
// - {Namespace}.{Class}.{Subclass} would be {Namespace}.{Subclass} when injected.
// Not sure on solution yet.
return GetTypeByName(cppType.FullName) ?? type;
}
if (AllTypes.TryGetValue(cppType.FullName, out Type primitive) && primitive.IsPrimitive)
return primitive;
return GetUnhollowedType(cppType) ?? type;
}
}
catch (Exception ex)
{
ExplorerCore.LogWarning("Exception in IL2CPP GetActualType: " + ex);
}
return type;
}
public static Type GetUnhollowedType(CppType cppType)
{
var fullname = cppType.FullName;
if (DeobfuscatedTypes.TryGetValue(fullname, out Type deob))
return deob;
if (fullname.StartsWith("System."))
fullname = $"Il2Cpp{fullname}";
if (!AllTypes.TryGetValue(fullname, out Type monoType))
ExplorerCore.LogWarning($"Failed to get type by name '{fullname}'!");
return monoType;
}
#endregion
#region Casting
private static readonly Dictionary<string, IntPtr> cppClassPointers = new Dictionary<string, IntPtr>();
internal override object Internal_TryCast(object obj, Type castTo)
{
if (obj == null)
return null;
var type = obj.GetType();
if (type == castTo)
return obj;
// from structs
if (type.IsValueType)
{
// from il2cpp primitive to system primitive
if (IsIl2CppPrimitive(type) && castTo.IsPrimitive)
{
return MakeMonoPrimitive(obj);
}
// from system primitive to il2cpp primitive
else if (IsIl2CppPrimitive(castTo))
{
return MakeIl2CppPrimitive(castTo, obj);
}
// from other structs to il2cpp object
else if (typeof(Il2CppSystem.Object).IsAssignableFrom(castTo))
{
return BoxIl2CppObject(obj).TryCast(castTo);
}
else
return obj;
}
// from string to il2cpp.Object / il2cpp.String
if (obj is string && typeof(Il2CppSystem.Object).IsAssignableFrom(castTo))
{
return BoxStringToType(obj, castTo);
}
// from il2cpp objects...
if (!(obj is Il2CppObjectBase cppObj))
return obj;
// from Il2CppSystem.Object to a struct
if (castTo.IsValueType)
return UnboxCppObject(cppObj, castTo);
// or to system string
else if (castTo == typeof(string))
return UnboxString(obj);
if (!Il2CppTypeNotNull(castTo, out IntPtr castToPtr))
return obj;
// Casting from il2cpp object to il2cpp object...
IntPtr castFromPtr = il2cpp_object_get_class(cppObj.Pointer);
if (!il2cpp_class_is_assignable_from(castToPtr, castFromPtr))
return null;
if (RuntimeSpecificsStore.IsInjected(castToPtr))
{
var injectedObj = UnhollowerBaseLib.Runtime.ClassInjectorBase.GetMonoObjectFromIl2CppPointer(cppObj.Pointer);
return injectedObj ?? obj;
}
try
{
return Activator.CreateInstance(castTo, cppObj.Pointer);
}
catch
{
return obj;
}
}
//private static bool IsAssignableFrom(Type thisType, Type fromType)
//{
// if (!Il2CppTypeNotNull(fromType, out IntPtr fromTypePtr)
// || !Il2CppTypeNotNull(thisType, out IntPtr thisTypePtr))
// {
// // one or both of the types are not Il2Cpp types, use normal check
// return thisType.IsAssignableFrom(fromType);
// }
//
// return il2cpp_class_is_assignable_from(thisTypePtr, fromTypePtr);
//}
#endregion
#region Boxing and unboxing ValueTypes
// cached il2cpp unbox methods
internal static readonly Dictionary<string, MethodInfo> unboxMethods = new Dictionary<string, MethodInfo>();
// Unbox an il2cpp object to a struct or System primitive.
public object UnboxCppObject(Il2CppObjectBase cppObj, Type toType)
{
if (!toType.IsValueType)
return null;
try
{
if (toType.IsEnum)
{
// Check for nullable enums
var type = cppObj.GetType();
if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Il2CppSystem.Nullable<>))
{
var nullable = cppObj.TryCast(type);
var nullableHasValueProperty = type.GetProperty("HasValue");
if ((bool)nullableHasValueProperty.GetValue(nullable, null))
{
// nullable has a value.
var nullableValueProperty = type.GetProperty("Value");
return Enum.Parse(toType, nullableValueProperty.GetValue(nullable, null).ToString());
}
// nullable and no current value.
return cppObj;
}
return Enum.Parse(toType, cppObj.ToString());
}
// Not enum, unbox with Il2CppObjectBase.Unbox
var name = toType.AssemblyQualifiedName;
if (!unboxMethods.ContainsKey(name))
{
unboxMethods.Add(name, typeof(Il2CppObjectBase)
.GetMethod("Unbox")
.MakeGenericMethod(toType));
}
return unboxMethods[name].Invoke(cppObj, ArgumentUtility.EmptyArgs);
}
catch (Exception ex)
{
ExplorerCore.LogWarning("Exception Unboxing Il2Cpp object to struct: " + ex);
return null;
}
}
private static Il2CppSystem.Object BoxIl2CppObject(object cppStruct, Type structType)
{
return GetMethodInfo(structType, "BoxIl2CppObject", ArgumentUtility.EmptyTypes)
.Invoke(cppStruct, ArgumentUtility.EmptyArgs)
as Il2CppSystem.Object;
}
public Il2CppSystem.Object BoxIl2CppObject(object value)
{
if (value == null)
return null;
try
{
var type = value.GetType();
if (!type.IsValueType)
return null;
if (type.IsEnum)
return Il2CppSystem.Enum.Parse(Il2CppType.From(type), value.ToString());
if (type.IsPrimitive && AllTypes.TryGetValue($"Il2Cpp{type.FullName}", out Type cppType))
return BoxIl2CppObject(MakeIl2CppPrimitive(cppType, value), cppType);
return BoxIl2CppObject(value, type);
}
catch (Exception ex)
{
ExplorerCore.LogWarning("Exception in BoxIl2CppObject: " + ex);
return null;
}
}
// Helpers for Il2Cpp primitive <-> Mono
internal static readonly Dictionary<string, Type> il2cppPrimitivesToMono = new Dictionary<string, Type>
{
{ "Il2CppSystem.Boolean", typeof(bool) },
{ "Il2CppSystem.Byte", typeof(byte) },
{ "Il2CppSystem.SByte", typeof(sbyte) },
{ "Il2CppSystem.Char", typeof(char) },
{ "Il2CppSystem.Double", typeof(double) },
{ "Il2CppSystem.Single", typeof(float) },
{ "Il2CppSystem.Int32", typeof(int) },
{ "Il2CppSystem.UInt32", typeof(uint) },
{ "Il2CppSystem.Int64", typeof(long) },
{ "Il2CppSystem.UInt64", typeof(ulong) },
{ "Il2CppSystem.Int16", typeof(short) },
{ "Il2CppSystem.UInt16", typeof(ushort) },
{ "Il2CppSystem.IntPtr", typeof(IntPtr) },
{ "Il2CppSystem.UIntPtr", typeof(UIntPtr) }
};
public static bool IsIl2CppPrimitive(object obj) => IsIl2CppPrimitive(obj.GetType());
public static bool IsIl2CppPrimitive(Type type) => il2cppPrimitivesToMono.ContainsKey(type.FullName);
public object MakeMonoPrimitive(object cppPrimitive)
{
return GetFieldInfo(cppPrimitive.GetType(), "m_value").GetValue(cppPrimitive);
}
public object MakeIl2CppPrimitive(Type cppType, object monoValue)
{
var cppStruct = Activator.CreateInstance(cppType);
GetFieldInfo(cppType, "m_value").SetValue(cppStruct, monoValue);
return cppStruct;
}
#endregion
#region String boxing/unboxing
private const string IL2CPP_STRING_FULLNAME = "Il2CppSystem.String";
private const string STRING_FULLNAME = "System.String";
public bool IsString(object obj)
{
if (obj is string || obj is Il2CppSystem.String)
return true;
if (obj is Il2CppSystem.Object cppObj)
{
var type = cppObj.GetIl2CppType();
return type.FullName == IL2CPP_STRING_FULLNAME || type.FullName == STRING_FULLNAME;
}
return false;
}
public object BoxStringToType(object value, Type castTo)
{
if (castTo == typeof(Il2CppSystem.String))
return (Il2CppSystem.String)(value as string);
else
return (Il2CppSystem.Object)(value as string);
}
public string UnboxString(object value)
{
if (value is string s)
return s;
s = null;
if (value is Il2CppSystem.Object cppObject)
s = cppObject.ToString();
else if (value is Il2CppSystem.String cppString)
s = cppString;
return s;
}
#endregion
#region Singleton finder
internal override void Internal_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.Internal_FindSingleton(possibleNames, type, flags, instances);
}
#endregion
#region Force-loading game modules
// Helper for IL2CPP to try to make sure the Unhollowed game assemblies are actually loaded.
// Force loading all il2cpp modules
internal void TryLoadGameModules()
{
var dir = ExplorerCore.Loader.UnhollowedModulesFolder;
if (Directory.Exists(dir))
{
foreach (var filePath in Directory.GetFiles(dir, "*.dll"))
DoLoadModule(filePath);
}
else
ExplorerCore.LogWarning($"Expected Unhollowed folder path does not exist: '{dir}'. " +
$"If you are using the standalone release, you can specify the Unhollowed modules path when you call CreateInstance().");
}
internal bool DoLoadModule(string fullPath)
{
if (string.IsNullOrEmpty(fullPath) || !File.Exists(fullPath))
return false;
try
{
Assembly.LoadFile(fullPath);
return true;
}
catch //(Exception e)
{
//ExplorerCore.LogWarning($"Failed loading module '{Path.GetFileName(fullPath)}'! {e.ReflectionExToString()}");
return false;
}
}
#endregion
#region Il2cpp reflection blacklist
public override string[] DefaultReflectionBlacklist => defaultIl2CppBlacklist.ToArray();
// These methods currently cause a crash in most il2cpp games,
// even from doing "GetParameters()" on the MemberInfo.
// Blacklisting until the issue is fixed in Unhollower.
public static HashSet<string> defaultIl2CppBlacklist = new HashSet<string>
{
// These were deprecated a long time ago, still show up in some IL2CPP games for some reason
"UnityEngine.MonoBehaviour.allowPrefabModeInPlayMode",
"UnityEngine.MonoBehaviour.runInEditMode",
"UnityEngine.Component.animation",
"UnityEngine.Component.audio",
"UnityEngine.Component.camera",
"UnityEngine.Component.collider",
"UnityEngine.Component.collider2D",
"UnityEngine.Component.constantForce",
"UnityEngine.Component.hingeJoint",
"UnityEngine.Component.light",
"UnityEngine.Component.networkView",
"UnityEngine.Component.particleSystem",
"UnityEngine.Component.renderer",
"UnityEngine.Component.rigidbody",
"UnityEngine.Component.rigidbody2D",
"UnityEngine.Light.flare",
// These can cause a crash in IL2CPP
"Il2CppSystem.Type.DeclaringMethod",
"Il2CppSystem.RuntimeType.DeclaringMethod",
"Unity.Jobs.LowLevel.Unsafe.JobsUtility.CreateJobReflectionData",
"Unity.Profiling.ProfilerRecorder.CopyTo",
"Unity.Profiling.ProfilerRecorder.StartNew",
"UnityEngine.Analytics.Analytics.RegisterEvent",
"UnityEngine.Analytics.Analytics.SendEvent",
"UnityEngine.Analytics.ContinuousEvent+ConfigureEventDelegate.Invoke",
"UnityEngine.Analytics.ContinuousEvent.ConfigureEvent",
"UnityEngine.Animations.AnimationLayerMixerPlayable.Create",
"UnityEngine.Animations.AnimationLayerMixerPlayable.CreateHandle",
"UnityEngine.Animations.AnimationMixerPlayable.Create",
"UnityEngine.Animations.AnimationMixerPlayable.CreateHandle",
"UnityEngine.AssetBundle.RecompressAssetBundleAsync",
"UnityEngine.Audio.AudioMixerPlayable.Create",
"UnityEngine.BoxcastCommand.ScheduleBatch",
"UnityEngine.Camera.CalculateProjectionMatrixFromPhysicalProperties",
"UnityEngine.Canvas.renderingDisplaySize",
"UnityEngine.CapsulecastCommand.ScheduleBatch",
"UnityEngine.Collider2D.Cast",
"UnityEngine.Collider2D.Raycast",
"UnityEngine.ComputeBuffer+BeginBufferWriteDelegate.Invoke",
"UnityEngine.ComputeBuffer+EndBufferWriteDelegate.Invoke",
"UnityEngine.ComputeBuffer.BeginBufferWrite",
"UnityEngine.ComputeBuffer.EndBufferWrite",
"UnityEngine.Cubemap+SetPixelDataImplArrayDelegate.Invoke",
"UnityEngine.Cubemap+SetPixelDataImplDelegate.Invoke",
"UnityEngine.Cubemap.SetPixelDataImpl",
"UnityEngine.Cubemap.SetPixelDataImplArray",
"UnityEngine.CubemapArray+SetPixelDataImplArrayDelegate.Invoke",
"UnityEngine.CubemapArray+SetPixelDataImplDelegate.Invoke",
"UnityEngine.CubemapArray.SetPixelDataImpl",
"UnityEngine.CubemapArray.SetPixelDataImplArray",
"UnityEngine.Experimental.Playables.MaterialEffectPlayable.Create",
"UnityEngine.Experimental.Rendering.RayTracingAccelerationStructure+AddInstanceDelegate.Invoke",
"UnityEngine.Experimental.Rendering.RayTracingAccelerationStructure+AddInstance_Procedural_InjectedDelegate.Invoke",
"UnityEngine.Experimental.Rendering.RayTracingAccelerationStructure.AddInstance",
"UnityEngine.Experimental.Rendering.RayTracingAccelerationStructure.AddInstance_Procedural",
"UnityEngine.Experimental.Rendering.RayTracingAccelerationStructure.AddInstance_Procedural_Injected",
"UnityEngine.Experimental.Rendering.RayTracingShader+DispatchDelegate.Invoke",
"UnityEngine.Experimental.Rendering.RayTracingShader.Dispatch",
"UnityEngine.Experimental.Rendering.RenderPassAttachment.Clear",
"UnityEngine.GUI.DoButtonGrid",
"UnityEngine.GUI.Slider",
"UnityEngine.GUI.Toolbar",
"UnityEngine.Graphics.DrawMeshInstancedIndirect",
"UnityEngine.Graphics.DrawMeshInstancedProcedural",
"UnityEngine.Graphics.DrawProcedural",
"UnityEngine.Graphics.DrawProceduralIndirect",
"UnityEngine.Graphics.DrawProceduralIndirectNow",
"UnityEngine.Graphics.DrawProceduralNow",
"UnityEngine.LineRenderer+BakeMeshDelegate.Invoke",
"UnityEngine.LineRenderer.BakeMesh",
"UnityEngine.Mesh.GetIndices",
"UnityEngine.Mesh.GetTriangles",
"UnityEngine.Mesh.SetIndices",
"UnityEngine.Mesh.SetTriangles",
"UnityEngine.Physics2D.BoxCast",
"UnityEngine.Physics2D.CapsuleCast",
"UnityEngine.Physics2D.CircleCast",
"UnityEngine.PhysicsScene.BoxCast",
"UnityEngine.PhysicsScene.CapsuleCast",
"UnityEngine.PhysicsScene.OverlapBox",
"UnityEngine.PhysicsScene.OverlapCapsule",
"UnityEngine.PhysicsScene.SphereCast",
"UnityEngine.PhysicsScene2D.BoxCast",
"UnityEngine.PhysicsScene2D.CapsuleCast",
"UnityEngine.PhysicsScene2D.CircleCast",
"UnityEngine.PhysicsScene2D.GetRayIntersection",
"UnityEngine.PhysicsScene2D.Linecast",
"UnityEngine.PhysicsScene2D.OverlapArea",
"UnityEngine.PhysicsScene2D.OverlapBox",
"UnityEngine.PhysicsScene2D.OverlapCapsule",
"UnityEngine.PhysicsScene2D.OverlapCircle",
"UnityEngine.PhysicsScene2D.OverlapCollider",
"UnityEngine.PhysicsScene2D.OverlapPoint",
"UnityEngine.PhysicsScene2D.Raycast",
"UnityEngine.Playables.Playable.Create",
"UnityEngine.Profiling.CustomSampler.Create",
"UnityEngine.RaycastCommand.ScheduleBatch",
"UnityEngine.RemoteConfigSettings+QueueConfigDelegate.Invoke",
"UnityEngine.RemoteConfigSettings.QueueConfig",
"UnityEngine.RenderTexture.GetTemporaryImpl",
"UnityEngine.Rendering.AsyncGPUReadback.Request",
"UnityEngine.Rendering.AttachmentDescriptor.ConfigureClear",
"UnityEngine.Rendering.BatchRendererGroup+AddBatch_InjectedDelegate.Invoke",
"UnityEngine.Rendering.BatchRendererGroup.AddBatch",
"UnityEngine.Rendering.BatchRendererGroup.AddBatch_Injected",
"UnityEngine.Rendering.CommandBuffer+Internal_DispatchRaysDelegate.Invoke",
"UnityEngine.Rendering.CommandBuffer.DispatchRays",
"UnityEngine.Rendering.CommandBuffer.DrawMeshInstancedProcedural",
"UnityEngine.Rendering.CommandBuffer.Internal_DispatchRays",
"UnityEngine.Rendering.CommandBuffer.ResolveAntiAliasedSurface",
"UnityEngine.Rendering.ScriptableRenderContext.BeginRenderPass",
"UnityEngine.Rendering.ScriptableRenderContext.BeginScopedRenderPass",
"UnityEngine.Rendering.ScriptableRenderContext.BeginScopedSubPass",
"UnityEngine.Rendering.ScriptableRenderContext.BeginSubPass",
"UnityEngine.Rendering.ScriptableRenderContext.SetupCameraProperties",
"UnityEngine.Rigidbody2D.Cast",
"UnityEngine.Scripting.GarbageCollector+CollectIncrementalDelegate.Invoke",
"UnityEngine.Scripting.GarbageCollector.CollectIncremental",
"UnityEngine.SpherecastCommand.ScheduleBatch",
"UnityEngine.Texture.GetPixelDataSize",
"UnityEngine.Texture.GetPixelDataOffset",
"UnityEngine.Texture.GetPixelDataOffset",
"UnityEngine.Texture2D+SetPixelDataImplArrayDelegate.Invoke",
"UnityEngine.Texture2D+SetPixelDataImplDelegate.Invoke",
"UnityEngine.Texture2D.SetPixelDataImpl",
"UnityEngine.Texture2D.SetPixelDataImplArray",
"UnityEngine.Texture2DArray+SetPixelDataImplArrayDelegate.Invoke",
"UnityEngine.Texture2DArray+SetPixelDataImplDelegate.Invoke",
"UnityEngine.Texture2DArray.SetPixelDataImpl",
"UnityEngine.Texture2DArray.SetPixelDataImplArray",
"UnityEngine.Texture3D+SetPixelDataImplArrayDelegate.Invoke",
"UnityEngine.Texture3D+SetPixelDataImplDelegate.Invoke",
"UnityEngine.Texture3D.SetPixelDataImpl",
"UnityEngine.Texture3D.SetPixelDataImplArray",
"UnityEngine.TrailRenderer+BakeMeshDelegate.Invoke",
"UnityEngine.TrailRenderer.BakeMesh",
"UnityEngine.WWW.LoadFromCacheOrDownload",
"UnityEngine.XR.InputDevice.SendHapticImpulse",
};
#endregion
#region IL2CPP IEnumerable and IDictionary
protected override bool Internal_TryGetEntryType(Type enumerableType, out Type type)
{
// Check for system types (not unhollowed)
if (base.Internal_TryGetEntryType(enumerableType, out type))
return true;
// Type is either an IL2CPP enumerable, or its not generic.
if (type.IsGenericType)
{
// Temporary naive solution until IL2CPP interface support improves.
// This will work fine for most cases, but there are edge cases which would not work.
type = type.GetGenericArguments()[0];
return true;
}
// Unable to determine entry type
type = typeof(object);
return false;
}
protected override bool Internal_TryGetEntryTypes(Type type, out Type keys, out Type values)
{
if (base.Internal_TryGetEntryTypes(type, out keys, out values))
return true;
// Type is either an IL2CPP dictionary, or its not generic.
if (type.IsGenericType)
{
// Naive solution until IL2CPP interfaces improve.
var args = type.GetGenericArguments();
if (args.Length == 2)
{
keys = args[0];
values = args[1];
return true;
}
}
keys = typeof(object);
values = typeof(object);
return false;
}
// Temp fix until Unhollower interface support improves
internal static readonly Dictionary<string, MethodInfo> getEnumeratorMethods = new Dictionary<string, MethodInfo>();
internal static readonly Dictionary<string, EnumeratorInfo> enumeratorInfos = new Dictionary<string, EnumeratorInfo>();
internal static readonly HashSet<string> notSupportedTypes = new HashSet<string>();
// IEnumerables
internal static IntPtr cppIEnumerablePointer;
protected override bool Internal_IsEnumerable(Type type)
{
if (base.Internal_IsEnumerable(type))
return true;
try
{
if (cppIEnumerablePointer == IntPtr.Zero)
Il2CppTypeNotNull(typeof(Il2CppSystem.Collections.IEnumerable), out cppIEnumerablePointer);
if (cppIEnumerablePointer != IntPtr.Zero
&& Il2CppTypeNotNull(type, out IntPtr assignFromPtr)
&& il2cpp_class_is_assignable_from(cppIEnumerablePointer, assignFromPtr))
{
return true;
}
}
catch { }
return false;
}
internal class EnumeratorInfo
{
internal MethodInfo moveNext;
internal PropertyInfo current;
}
protected override bool Internal_TryGetEnumerator(object list, out IEnumerator enumerator)
{
if (list is IEnumerable)
return base.Internal_TryGetEnumerator(list, out enumerator);
try
{
PrepareCppEnumerator(list, out object cppEnumerator, out EnumeratorInfo info);
enumerator = EnumerateCppList(info, cppEnumerator);
return true;
}
catch //(Exception ex)
{
//ExplorerCore.LogWarning($"Exception enumerating IEnumerable: {ex.ReflectionExToString()}");
enumerator = null;
return false;
}
}
private static void PrepareCppEnumerator(object list, out object cppEnumerator, out EnumeratorInfo info)
{
info = null;
cppEnumerator = null;
if (list == null)
throw new ArgumentNullException("list");
// Some ugly reflection to use the il2cpp interface for the instance type
var type = list.GetActualType();
var key = type.AssemblyQualifiedName;
if (!getEnumeratorMethods.ContainsKey(key))
{
var method = type.GetMethod("GetEnumerator")
?? type.GetMethod("System_Collections_IEnumerable_GetEnumerator", FLAGS);
getEnumeratorMethods.Add(key, method);
// ensure the enumerator type is supported
try
{
var test = getEnumeratorMethods[key].Invoke(list, null);
test.GetActualType().GetMethod("MoveNext").Invoke(test, null);
}
catch (Exception ex)
{
ExplorerCore.Log($"IEnumerable failed to enumerate: {ex}");
notSupportedTypes.Add(key);
}
}
if (notSupportedTypes.Contains(key))
throw new NotSupportedException($"The IEnumerable type '{type.FullName}' does not support MoveNext.");
cppEnumerator = getEnumeratorMethods[key].Invoke(list, null);
var enumeratorType = cppEnumerator.GetActualType();
var enumInfoKey = enumeratorType.AssemblyQualifiedName;
if (!enumeratorInfos.ContainsKey(enumInfoKey))
{
enumeratorInfos.Add(enumInfoKey, new EnumeratorInfo
{
current = enumeratorType.GetProperty("Current"),
moveNext = enumeratorType.GetMethod("MoveNext"),
});
}
info = enumeratorInfos[enumInfoKey];
}
internal static IEnumerator EnumerateCppList(EnumeratorInfo info, object enumerator)
{
// Yield and return the actual entries
while ((bool)info.moveNext.Invoke(enumerator, null))
yield return info.current.GetValue(enumerator);
}
// IDictionary
internal static IntPtr cppIDictionaryPointer;
protected override bool Internal_IsDictionary(Type type)
{
if (base.Internal_IsDictionary(type))
return true;
try
{
if (cppIDictionaryPointer == IntPtr.Zero)
if (!Il2CppTypeNotNull(typeof(Il2CppSystem.Collections.IDictionary), out cppIDictionaryPointer))
return false;
if (Il2CppTypeNotNull(type, out IntPtr classPtr)
&& il2cpp_class_is_assignable_from(cppIDictionaryPointer, classPtr))
return true;
}
catch { }
return false;
}
protected override bool Internal_TryGetDictEnumerator(object dictionary, out IEnumerator<DictionaryEntry> dictEnumerator)
{
if (dictionary is IDictionary)
return base.Internal_TryGetDictEnumerator(dictionary, out dictEnumerator);
try
{
var type = dictionary.GetActualType();
if (typeof(Il2CppSystem.Collections.Hashtable).IsAssignableFrom(type))
{
dictEnumerator = EnumerateCppHashTable(dictionary.TryCast<Il2CppSystem.Collections.Hashtable>());
return true;
}
var keys = type.GetProperty("Keys").GetValue(dictionary, null);
var keyCollType = keys.GetActualType();
var cacheKey = keyCollType.AssemblyQualifiedName;
if (!getEnumeratorMethods.ContainsKey(cacheKey))
{
var method = keyCollType.GetMethod("GetEnumerator")
?? keyCollType.GetMethod("System_Collections_IDictionary_GetEnumerator", FLAGS);
getEnumeratorMethods.Add(cacheKey, method);
// test support
try
{
var test = getEnumeratorMethods[cacheKey].Invoke(keys, null);
test.GetActualType().GetMethod("MoveNext").Invoke(test, null);
}
catch (Exception ex)
{
ExplorerCore.Log($"IDictionary failed to enumerate: {ex}");
notSupportedTypes.Add(cacheKey);
}
}
if (notSupportedTypes.Contains(cacheKey))
throw new Exception($"The IDictionary type '{type.FullName}' does not support MoveNext.");
var keyEnumerator = getEnumeratorMethods[cacheKey].Invoke(keys, null);
var keyInfo = new EnumeratorInfo
{
current = keyEnumerator.GetActualType().GetProperty("Current"),
moveNext = keyEnumerator.GetActualType().GetMethod("MoveNext"),
};
var values = type.GetProperty("Values").GetValue(dictionary, null);
var valueEnumerator = values.GetActualType().GetMethod("GetEnumerator").Invoke(values, null);
var valueInfo = new EnumeratorInfo
{
current = valueEnumerator.GetActualType().GetProperty("Current"),
moveNext = valueEnumerator.GetActualType().GetMethod("MoveNext"),
};
dictEnumerator = EnumerateCppDict(keyInfo, keyEnumerator, valueInfo, valueEnumerator);
return true;
}
catch //(Exception ex)
{
//ExplorerCore.LogWarning($"Exception enumerating IDictionary: {ex.ReflectionExToString()}");
dictEnumerator = null;
return false;
}
}
internal static IEnumerator<DictionaryEntry> EnumerateCppDict(EnumeratorInfo keyInfo, object keyEnumerator,
EnumeratorInfo valueInfo, object valueEnumerator)
{
while ((bool)keyInfo.moveNext.Invoke(keyEnumerator, null))
{
valueInfo.moveNext.Invoke(valueEnumerator, null);
var key = keyInfo.current.GetValue(keyEnumerator, null);
var value = valueInfo.current.GetValue(valueEnumerator, null);
yield return new DictionaryEntry(key, value);
}
}
internal static IEnumerator<DictionaryEntry> EnumerateCppHashTable(Il2CppSystem.Collections.Hashtable hashtable)
{
for (int i = 0; i < hashtable.buckets.Count; i++)
{
var bucket = hashtable.buckets[i];
if (bucket == null || bucket.key == null)
continue;
yield return new DictionaryEntry(bucket.key, bucket.val);
}
}
#endregion
}
}
#endif

View File

@ -1,57 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using HarmonyLib;
namespace UnityExplorer
{
public static class ReflectionPatches
{
public static void Init()
{
try
{
var method = typeof(Assembly).GetMethod(nameof(Assembly.GetTypes), new Type[0]);
var processor = ExplorerCore.Harmony.CreateProcessor(method);
processor.AddFinalizer(typeof(ReflectionPatches).GetMethod(nameof(Assembly_GetTypes)));
processor.Patch();
}
catch (Exception ex)
{
ExplorerCore.LogWarning($"Exception setting up Reflection patch: {ex}");
}
}
private static readonly Type[] emptyTypes = new Type[0];
public static Exception Assembly_GetTypes(Assembly __instance, Exception __exception, ref Type[] __result)
{
if (__exception != null)
{
try
{
__result = __instance.GetExportedTypes();
}
catch (ReflectionTypeLoadException e)
{
try
{
__result = e.Types.Where(it => it != null).ToArray();
}
catch
{
__result = emptyTypes;
}
}
catch
{
__result = emptyTypes;
}
}
return null;
}
}
}

View File

@ -1,597 +0,0 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using UnityEngine;
using UnityExplorer.Core.Config;
using UnityExplorer.Core.Runtime;
using BF = System.Reflection.BindingFlags;
namespace UnityExplorer
{
public class ReflectionUtility
{
public const BF FLAGS = BF.Public | BF.Instance | BF.NonPublic | BF.Static;
internal static ReflectionUtility Instance;
public static void Init()
{
Instance =
#if CPP
new Il2CppReflection();
#else
new ReflectionUtility();
#endif
Instance.Initialize();
ReflectionPatches.Init();
}
protected virtual void Initialize()
{
SetupTypeCache();
LoadBlacklistString(ConfigManager.Reflection_Signature_Blacklist.Value);
ConfigManager.Reflection_Signature_Blacklist.OnValueChanged += LoadBlacklistString;
}
#region Type cache
public static Action<Type> OnTypeLoaded;
/// <summary>Key: Type.FullName</summary>
protected static readonly SortedDictionary<string, Type> AllTypes = new SortedDictionary<string, Type>(StringComparer.OrdinalIgnoreCase);
public static readonly List<string> AllNamespaces = new List<string>();
private static readonly HashSet<string> uniqueNamespaces = new HashSet<string>();
private static string[] allTypesArray;
public static string[] GetTypeNameArray()
{
if (allTypesArray == null || allTypesArray.Length != AllTypes.Count)
{
allTypesArray = new string[AllTypes.Count];
int i = 0;
foreach (var name in AllTypes.Keys)
{
allTypesArray[i] = name;
i++;
}
}
return allTypesArray;
}
private static void SetupTypeCache()
{
float start = Time.realtimeSinceStartup;
foreach (var asm in AppDomain.CurrentDomain.GetAssemblies())
CacheTypes(asm);
AppDomain.CurrentDomain.AssemblyLoad += AssemblyLoaded;
ExplorerCore.Log($"Cached AppDomain assemblies in {Time.realtimeSinceStartup - start} seconds");
}
private static void AssemblyLoaded(object sender, AssemblyLoadEventArgs args)
{
if (args.LoadedAssembly == null || args.LoadedAssembly.GetName().Name == "completions")
return;
CacheTypes(args.LoadedAssembly);
}
private static void CacheTypes(Assembly asm)
{
foreach (var type in asm.TryGetTypes())
{
// Cache namespace if there is one
if (!string.IsNullOrEmpty(type.Namespace) && !uniqueNamespaces.Contains(type.Namespace))
{
uniqueNamespaces.Add(type.Namespace);
int i = 0;
while (i < AllNamespaces.Count)
{
if (type.Namespace.CompareTo(AllNamespaces[i]) < 0)
break;
i++;
}
AllNamespaces.Insert(i, type.Namespace);
}
// Cache the type. Overwrite type if one exists with the full name
if (AllTypes.ContainsKey(type.FullName))
AllTypes[type.FullName] = type;
else
AllTypes.Add(type.FullName, type);
// Invoke listener
OnTypeLoaded?.Invoke(type);
// Check type inheritance cache, add this to any lists it should be in
foreach (var key in typeInheritance.Keys)
{
try
{
var baseType = AllTypes[key];
if (baseType.IsAssignableFrom(type) && !typeInheritance[key].Contains(type))
typeInheritance[key].Add(type);
}
catch { }
}
}
}
#endregion
/// <summary>
/// Find a <see cref="Type"/> in the current AppDomain whose <see cref="Type.FullName"/> matches the provided <paramref name="fullName"/>.
/// </summary>
/// <param name="fullName">The <see cref="Type.FullName"/> you want to search for - case sensitive and full matches only.</param>
/// <returns>The Type if found, otherwise null.</returns>
public static Type GetTypeByName(string fullName)
=> Instance.Internal_GetTypeByName(fullName);
internal virtual Type Internal_GetTypeByName(string fullName)
{
AllTypes.TryGetValue(fullName, out Type type);
return type;
}
// Getting the actual type of an object
internal virtual Type Internal_GetActualType(object obj)
=> obj?.GetType();
// Force-casting an object to a type
internal virtual object Internal_TryCast(object obj, Type castTo)
=> obj;
// Processing deobfuscated type names in strings
public static string ProcessTypeInString(Type type, string theString)
=> Instance.Internal_ProcessTypeInString(theString, type);
internal virtual string Internal_ProcessTypeInString(string theString, Type type)
=> theString;
// Singleton finder
public static void FindSingleton(string[] possibleNames, Type type, BindingFlags flags, List<object> instances)
=> Instance.Internal_FindSingleton(possibleNames, type, flags, instances);
internal virtual void Internal_FindSingleton(string[] possibleNames, Type type, BindingFlags flags, List<object> instances)
{
// Look for a typical Instance backing field.
FieldInfo fi;
foreach (var name in possibleNames)
{
fi = type.GetField(name, flags);
if (fi != null)
{
var instance = fi.GetValue(null);
if (instance != null)
{
instances.Add(instance);
return;
}
}
}
}
// Universal helpers
#region Type inheritance cache
// cache for GetBaseTypes
internal static readonly Dictionary<string, Type[]> baseTypes = new Dictionary<string, Type[]>();
/// <summary>
/// Get all base types of the provided Type, including itself.
/// </summary>
public static Type[] GetAllBaseTypes(object obj) => GetAllBaseTypes(obj?.GetActualType());
/// <summary>
/// Get all base types of the provided Type, including itself.
/// </summary>
public static Type[] GetAllBaseTypes(Type type)
{
if (type == null)
throw new ArgumentNullException("type");
var name = type.AssemblyQualifiedName;
if (baseTypes.TryGetValue(name, out Type[] ret))
return ret;
List<Type> list = new List<Type>();
while (type != null)
{
list.Add(type);
type = type.BaseType;
}
ret = list.ToArray();
baseTypes.Add(name, ret);
return ret;
}
#endregion
#region Type and Generic Parameter implementation cache
// cache for GetImplementationsOf
internal static readonly Dictionary<string, HashSet<Type>> typeInheritance = new Dictionary<string, HashSet<Type>>();
internal static readonly Dictionary<string, HashSet<Type>> genericParameterInheritance = new Dictionary<string, HashSet<Type>>();
public static string GetImplementationKey(Type type)
{
if (!type.IsGenericParameter)
return type.FullName;
else
{
var sb = new StringBuilder();
sb.Append(type.GenericParameterAttributes)
.Append('|');
foreach (var c in type.GetGenericParameterConstraints())
sb.Append(c.FullName).Append(',');
return sb.ToString();
}
}
/// <summary>
/// Get all non-abstract implementations of the provided type (include itself, if not abstract) in the current AppDomain.
/// Also works for generic parameters by analyzing the constraints.
/// </summary>
/// <param name="baseType">The base type, which can optionally be abstract / interface.</param>
/// <returns>All implementations of the type in the current AppDomain.</returns>
public static HashSet<Type> GetImplementationsOf(Type baseType, bool allowAbstract, bool allowGeneric, bool allowEnum, bool allowRecursive = true)
{
var key = GetImplementationKey(baseType);
int count = AllTypes.Count;
HashSet<Type> ret;
if (!baseType.IsGenericParameter)
ret = GetImplementations(key, baseType, allowAbstract, allowGeneric, allowEnum);
else
ret = GetGenericParameterImplementations(key, baseType, allowAbstract, allowGeneric);
// types were resolved during the parse, do it again if we're not already rebuilding.
if (allowRecursive && AllTypes.Count != count)
ret = GetImplementationsOf(baseType, allowAbstract, allowGeneric, false);
return ret;
}
private static HashSet<Type> GetImplementations(string key, Type baseType, bool allowAbstract, bool allowGeneric, bool allowEnum)
{
if (!typeInheritance.ContainsKey(key))
{
var set = new HashSet<Type>();
var names = GetTypeNameArray();
for (int i = 0; i < names.Length; i++)
{
var name = names[i];
try
{
var type = AllTypes[name];
if (set.Contains(type)
|| (type.IsAbstract && type.IsSealed) // ignore static classes
|| (!allowAbstract && type.IsAbstract)
|| (!allowGeneric && (type.IsGenericType || type.IsGenericTypeDefinition))
|| (!allowEnum && type.IsEnum))
continue;
if (type.FullName.Contains("PrivateImplementationDetails")
|| type.FullName.Contains("DisplayClass")
|| type.FullName.Contains('<'))
continue;
if (baseType.IsAssignableFrom(type) && !set.Contains(type))
set.Add(type);
}
catch { }
}
//set.
typeInheritance.Add(key, set);
}
return typeInheritance[key];
}
private static HashSet<Type> GetGenericParameterImplementations(string key, Type baseType, bool allowAbstract, bool allowGeneric)
{
if (!genericParameterInheritance.ContainsKey(key))
{
var set = new HashSet<Type>();
var names = GetTypeNameArray();
for (int i = 0; i < names.Length; i++)
{
var name = names[i];
try
{
var type = AllTypes[name];
if (set.Contains(type)
|| (type.IsAbstract && type.IsSealed) // ignore static classes
|| (!allowAbstract && type.IsAbstract)
|| (!allowGeneric && (type.IsGenericType || type.IsGenericTypeDefinition)))
continue;
if (type.FullName.Contains("PrivateImplementationDetails")
|| type.FullName.Contains("DisplayClass")
|| type.FullName.Contains('<'))
continue;
if (baseType.GenericParameterAttributes.HasFlag(GenericParameterAttributes.NotNullableValueTypeConstraint)
&& type.IsClass)
continue;
if (baseType.GenericParameterAttributes.HasFlag(GenericParameterAttributes.ReferenceTypeConstraint)
&& type.IsValueType)
continue;
if (baseType.GetGenericParameterConstraints().Any(it => !it.IsAssignableFrom(type)))
continue;
set.Add(type);
}
catch { }
}
genericParameterInheritance.Add(key, set);
}
return genericParameterInheritance[key];
}
#endregion
#region Internal MemberInfo Cache
internal static Dictionary<Type, Dictionary<string, FieldInfo>> fieldInfos = new Dictionary<Type, Dictionary<string, FieldInfo>>();
public static FieldInfo GetFieldInfo(Type type, string fieldName)
{
if (!fieldInfos.ContainsKey(type))
fieldInfos.Add(type, new Dictionary<string, FieldInfo>());
if (!fieldInfos[type].ContainsKey(fieldName))
fieldInfos[type].Add(fieldName, type.GetField(fieldName, FLAGS));
return fieldInfos[type][fieldName];
}
internal static Dictionary<Type, Dictionary<string, PropertyInfo>> propertyInfos = new Dictionary<Type, Dictionary<string, PropertyInfo>>();
public static PropertyInfo GetPropertyInfo(Type type, string propertyName)
{
if (!propertyInfos.ContainsKey(type))
propertyInfos.Add(type, new Dictionary<string, PropertyInfo>());
if (!propertyInfos[type].ContainsKey(propertyName))
propertyInfos[type].Add(propertyName, type.GetProperty(propertyName, FLAGS));
return propertyInfos[type][propertyName];
}
internal static Dictionary<Type, Dictionary<string, MethodInfo>> methodInfos = new Dictionary<Type, Dictionary<string, MethodInfo>>();
public static MethodInfo GetMethodInfo(Type type, string methodName)
=> GetMethodInfo(type, methodName, ArgumentUtility.EmptyTypes, false);
public static MethodInfo GetMethodInfo(Type type, string methodName, Type[] argumentTypes, bool cacheAmbiguous = false)
{
if (!methodInfos.ContainsKey(type))
methodInfos.Add(type, new Dictionary<string, MethodInfo>());
if (cacheAmbiguous)
{
methodName += "|";
foreach (var arg in argumentTypes)
methodName += arg.FullName + ",";
}
try
{
if (!methodInfos[type].ContainsKey(methodName))
{
if (argumentTypes != null)
methodInfos[type].Add(methodName, type.GetMethod(methodName, FLAGS, null, argumentTypes, null));
else
methodInfos[type].Add(methodName, type.GetMethod(methodName, FLAGS));
}
return methodInfos[type][methodName];
}
catch (AmbiguousMatchException)
{
ExplorerCore.LogWarning($"AmbiguousMatchException trying to get method '{methodName}'");
return null;
}
catch (Exception e)
{
ExplorerCore.LogWarning($"{e.GetType()} trying to get method '{methodName}': {e.Message}\r\n{e.StackTrace}");
return null;
}
}
#endregion
#region Reflection Blacklist
public virtual string[] DefaultReflectionBlacklist => new string[0];
public static void LoadBlacklistString(string blacklist)
{
try
{
if (string.IsNullOrEmpty(blacklist) && !Instance.DefaultReflectionBlacklist.Any())
return;
try
{
var sigs = blacklist.Split(';');
foreach (var sig in sigs)
{
var s = sig.Trim();
if (string.IsNullOrEmpty(s))
continue;
if (!currentBlacklist.Contains(s))
currentBlacklist.Add(s);
}
}
catch (Exception ex)
{
ExplorerCore.LogWarning($"Exception parsing blacklist string: {ex.ReflectionExToString()}");
}
foreach (var sig in Instance.DefaultReflectionBlacklist)
{
if (!currentBlacklist.Contains(sig))
currentBlacklist.Add(sig);
}
Mono.CSharp.IL2CPP.Blacklist.SignatureBlacklist = currentBlacklist;
}
catch (Exception ex)
{
ExplorerCore.LogWarning($"Exception setting up reflection blacklist: {ex.ReflectionExToString()}");
}
}
public static bool IsBlacklisted(MemberInfo member)
{
if (string.IsNullOrEmpty(member.DeclaringType?.Namespace))
return false;
var sig = $"{member.DeclaringType.FullName}.{member.Name}";
return currentBlacklist.Contains(sig);
}
private static readonly HashSet<string> currentBlacklist = new HashSet<string>();
#endregion
// Temp fix for IL2CPP until interface support improves
// IsEnumerable
public static bool IsEnumerable(Type type) => Instance.Internal_IsEnumerable(type);
protected virtual bool Internal_IsEnumerable(Type type)
{
return typeof(IEnumerable).IsAssignableFrom(type);
}
// TryGetEnumerator (list)
public static bool TryGetEnumerator(object list, out IEnumerator enumerator)
=> Instance.Internal_TryGetEnumerator(list, out enumerator);
protected virtual bool Internal_TryGetEnumerator(object list, out IEnumerator enumerator)
{
enumerator = (list as IEnumerable).GetEnumerator();
return true;
}
// TryGetEntryType
public static bool TryGetEntryType(Type enumerableType, out Type type)
=> Instance.Internal_TryGetEntryType(enumerableType, out type);
protected virtual bool Internal_TryGetEntryType(Type enumerableType, out Type type)
{
// Check for arrays
if (enumerableType.IsArray)
{
type = enumerableType.GetElementType();
return true;
}
// Check for implementation of IEnumerable<T>, IList<T> or ICollection<T>
foreach (var t in enumerableType.GetInterfaces())
{
if (t.IsGenericType)
{
var typeDef = t.GetGenericTypeDefinition();
if (typeDef == typeof(IEnumerable<>) || typeDef == typeof(IList<>) || typeDef == typeof(ICollection<>))
{
type = t.GetGenericArguments()[0];
return true;
}
}
}
// Unable to determine any generic element type, just use object.
type = typeof(object);
return false;
}
// IsDictionary
public static bool IsDictionary(Type type) => Instance.Internal_IsDictionary(type);
protected virtual bool Internal_IsDictionary(Type type)
{
return typeof(IDictionary).IsAssignableFrom(type);
}
// TryGetEnumerator (dictionary)
public static bool TryGetDictEnumerator(object dictionary, out IEnumerator<DictionaryEntry> dictEnumerator)
=> Instance.Internal_TryGetDictEnumerator(dictionary, out dictEnumerator);
protected virtual bool Internal_TryGetDictEnumerator(object dictionary, out IEnumerator<DictionaryEntry> dictEnumerator)
{
dictEnumerator = EnumerateDictionary((IDictionary)dictionary);
return true;
}
private IEnumerator<DictionaryEntry> EnumerateDictionary(IDictionary dict)
{
var enumerator = dict.GetEnumerator();
while (enumerator.MoveNext())
{
yield return new DictionaryEntry(enumerator.Key, enumerator.Value);
}
}
// TryGetEntryTypes
public static bool TryGetEntryTypes(Type dictionaryType, out Type keys, out Type values)
=> Instance.Internal_TryGetEntryTypes(dictionaryType, out keys, out values);
protected virtual bool Internal_TryGetEntryTypes(Type dictionaryType, out Type keys, out Type values)
{
foreach (var t in dictionaryType.GetInterfaces())
{
if (t.IsGenericType && t.GetGenericTypeDefinition() == typeof(IDictionary<,>))
{
var args = t.GetGenericArguments();
keys = args[0];
values = args[1];
return true;
}
}
keys = typeof(object);
values = typeof(object);
return false;
}
}
}

View File

@ -1,106 +0,0 @@
#if CPP
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnhollowerBaseLib;
using UnhollowerBaseLib.Attributes;
using UnhollowerRuntimeLib;
using UnityEngine;
using UnityExplorer.Core.Runtime.Il2Cpp;
namespace UnityExplorer
{
public class AssetBundle : UnityEngine.Object
{
static AssetBundle()
{
ClassInjector.RegisterTypeInIl2Cpp<AssetBundle>();
}
// ~~~~~~~~~~~~ Static ~~~~~~~~~~~~
// AssetBundle.LoadFromFile(string path)
internal delegate IntPtr d_LoadFromFile(IntPtr path, uint crc, ulong offset);
[HideFromIl2Cpp]
public static AssetBundle LoadFromFile(string path)
{
IntPtr ptr = ICallManager.GetICall<d_LoadFromFile>("UnityEngine.AssetBundle::LoadFromFile_Internal")
.Invoke(IL2CPP.ManagedStringToIl2Cpp(path), 0u, 0UL);
return ptr != IntPtr.Zero ? new AssetBundle(ptr) : null;
}
// AssetBundle.LoadFromMemory(byte[] binary)
private delegate IntPtr d_LoadFromMemory(IntPtr binary, uint crc);
[HideFromIl2Cpp]
public static AssetBundle LoadFromMemory(byte[] binary, uint crc = 0)
{
IntPtr ptr = ICallManager.GetICall<d_LoadFromMemory>("UnityEngine.AssetBundle::LoadFromMemory_Internal")
.Invoke(((Il2CppStructArray<byte>)binary).Pointer, crc);
return ptr != IntPtr.Zero ? new AssetBundle(ptr) : null;
}
// AssetBundle.GetAllLoadedAssetBundles()
public delegate IntPtr d_GetAllLoadedAssetBundles_Native();
[HideFromIl2Cpp]
public static AssetBundle[] GetAllLoadedAssetBundles()
{
IntPtr ptr = ICallManager.GetICall<d_GetAllLoadedAssetBundles_Native>("UnityEngine.AssetBundle::GetAllLoadedAssetBundles_Native")
.Invoke();
return ptr != IntPtr.Zero ? (AssetBundle[])new Il2CppReferenceArray<AssetBundle>(ptr) : null;
}
// ~~~~~~~~~~~~ Instance ~~~~~~~~~~~~
public readonly IntPtr m_bundlePtr = IntPtr.Zero;
public AssetBundle(IntPtr ptr) : base(ptr) { m_bundlePtr = ptr; }
// LoadAllAssets()
internal delegate IntPtr d_LoadAssetWithSubAssets_Internal(IntPtr _this, IntPtr name, IntPtr type);
[HideFromIl2Cpp]
public UnityEngine.Object[] LoadAllAssets()
{
IntPtr ptr = ICallManager.GetICall<d_LoadAssetWithSubAssets_Internal>("UnityEngine.AssetBundle::LoadAssetWithSubAssets_Internal")
.Invoke(m_bundlePtr, IL2CPP.ManagedStringToIl2Cpp(""), UnhollowerRuntimeLib.Il2CppType.Of<UnityEngine.Object>().Pointer);
return ptr != IntPtr.Zero ? (UnityEngine.Object[])new Il2CppReferenceArray<UnityEngine.Object>(ptr) : new UnityEngine.Object[0];
}
// LoadAsset<T>(string name, Type type)
internal delegate IntPtr d_LoadAsset_Internal(IntPtr _this, IntPtr name, IntPtr type);
[HideFromIl2Cpp]
public T LoadAsset<T>(string name) where T : UnityEngine.Object
{
IntPtr ptr = ICallManager.GetICall<d_LoadAsset_Internal>("UnityEngine.AssetBundle::LoadAsset_Internal")
.Invoke(m_bundlePtr, IL2CPP.ManagedStringToIl2Cpp(name), UnhollowerRuntimeLib.Il2CppType.Of<T>().Pointer);
return ptr != IntPtr.Zero ? new UnityEngine.Object(ptr).TryCast<T>() : null;
}
// Unload(bool unloadAllLoadedObjects);
internal delegate void d_Unload(IntPtr _this, bool unloadAllLoadedObjects);
[HideFromIl2Cpp]
public void Unload(bool unloadAllLoadedObjects)
{
ICallManager.GetICall<d_Unload>("UnityEngine.AssetBundle::Unload")
.Invoke(this.m_bundlePtr, unloadAllLoadedObjects);
}
}
}
#endif

View File

@ -1,71 +0,0 @@
#if CPP
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Runtime.InteropServices;
namespace UnityExplorer.Core.Runtime.Il2Cpp
{
public static class ICallManager
{
[DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
public static extern IntPtr il2cpp_resolve_icall([MarshalAs(UnmanagedType.LPStr)] string name);
private static readonly Dictionary<string, Delegate> iCallCache = new Dictionary<string, Delegate>();
/// <summary>
/// Helper to get and cache an iCall by providing the signature (eg. "UnityEngine.Resources::FindObjectsOfTypeAll").
/// </summary>
/// <typeparam name="T">The Type of Delegate to provide for the iCall.</typeparam>
/// <param name="signature">The signature of the iCall you want to get.</param>
/// <returns>The <typeparamref name="T"/> delegate if successful.</returns>
/// <exception cref="MissingMethodException">If the iCall could not be found.</exception>
public static T GetICall<T>(string signature) where T : Delegate
{
if (iCallCache.ContainsKey(signature))
return (T)iCallCache[signature];
IntPtr ptr = il2cpp_resolve_icall(signature);
if (ptr == IntPtr.Zero)
throw new MissingMethodException($"Could not find any iCall with the signature '{signature}'!");
Delegate iCall = Marshal.GetDelegateForFunctionPointer(ptr, typeof(T));
iCallCache.Add(signature, iCall);
return (T)iCall;
}
private static readonly Dictionary<string, Delegate> s_unreliableCache = new Dictionary<string, Delegate>();
/// <summary>
/// Get an iCall which may be one of multiple different signatures (ie, it changed in different Unity versions).
/// Each possible signature must have the same Type pattern, it can only vary by name.
/// </summary>
public static T GetICallUnreliable<T>(IEnumerable<string> possibleSignatures) where T : Delegate
{
// use the first possible signature as the 'key'.
string key = possibleSignatures.First();
if (s_unreliableCache.ContainsKey(key))
return (T)s_unreliableCache[key];
T iCall;
IntPtr ptr;
foreach (var sig in possibleSignatures)
{
ptr = il2cpp_resolve_icall(sig);
if (ptr != IntPtr.Zero)
{
iCall = (T)Marshal.GetDelegateForFunctionPointer(ptr, typeof(T));
s_unreliableCache.Add(key, iCall);
return iCall;
}
}
throw new MissingMethodException($"Could not find any iCall from list of provided signatures starting with '{key}'!");
}
}
}
#endif

View File

@ -1,159 +0,0 @@
#if CPP
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnhollowerBaseLib;
using UnityEngine;
// Credit to HerpDerpenstine and knah
// https://github.com/LavaGang/MelonLoader/blob/master/SM_Il2Cpp/Coroutines.cs
namespace UnityExplorer.Core.Runtime.Il2Cpp
{
public static class Il2CppCoroutine
{
private struct CoroTuple
{
public object WaitCondition;
public IEnumerator Coroutine;
}
private static readonly List<CoroTuple> ourCoroutinesStore = new List<CoroTuple>();
private static readonly List<IEnumerator> ourNextFrameCoroutines = new List<IEnumerator>();
private static readonly List<IEnumerator> ourWaitForFixedUpdateCoroutines = new List<IEnumerator>();
private static readonly List<IEnumerator> ourWaitForEndOfFrameCoroutines = new List<IEnumerator>();
private static readonly List<IEnumerator> tempList = new List<IEnumerator>();
internal static object Start(IEnumerator routine)
{
if (routine != null) ProcessNextOfCoroutine(routine);
return routine;
}
internal static void Stop(IEnumerator enumerator)
{
if (ourNextFrameCoroutines.Contains(enumerator)) // the coroutine is running itself
ourNextFrameCoroutines.Remove(enumerator);
else
{
int coroTupleIndex = ourCoroutinesStore.FindIndex(c => c.Coroutine == enumerator);
if (coroTupleIndex != -1) // the coroutine is waiting for a subroutine
{
object waitCondition = ourCoroutinesStore[coroTupleIndex].WaitCondition;
if (waitCondition is IEnumerator waitEnumerator)
Stop(waitEnumerator);
ourCoroutinesStore.RemoveAt(coroTupleIndex);
}
}
}
private static void ProcessCoroList(List<IEnumerator> target)
{
if (target.Count == 0) return;
// use a temp list to make sure waits made during processing are not handled by same processing invocation
// additionally, a temp list reduces allocations compared to an array
tempList.AddRange(target);
target.Clear();
foreach (var enumerator in tempList) ProcessNextOfCoroutine(enumerator);
tempList.Clear();
}
internal static void Process()
{
for (var i = ourCoroutinesStore.Count - 1; i >= 0; i--)
{
var tuple = ourCoroutinesStore[i];
if (tuple.WaitCondition is WaitForSeconds waitForSeconds)
{
if ((waitForSeconds.m_Seconds -= Time.deltaTime) <= 0)
{
ourCoroutinesStore.RemoveAt(i);
ProcessNextOfCoroutine(tuple.Coroutine);
}
}
}
ProcessCoroList(ourNextFrameCoroutines);
}
internal static void ProcessWaitForFixedUpdate() => ProcessCoroList(ourWaitForFixedUpdateCoroutines);
internal static void ProcessWaitForEndOfFrame() => ProcessCoroList(ourWaitForEndOfFrameCoroutines);
private static void ProcessNextOfCoroutine(IEnumerator enumerator)
{
try
{
if (!enumerator.MoveNext()) // Run the next step of the coroutine. If it's done, restore the parent routine
{
var indices = ourCoroutinesStore.Select((it, idx) => (idx, it)).Where(it => it.it.WaitCondition == enumerator).Select(it => it.idx).ToList();
for (var i = indices.Count - 1; i >= 0; i--)
{
var index = indices[i];
ourNextFrameCoroutines.Add(ourCoroutinesStore[index].Coroutine);
ourCoroutinesStore.RemoveAt(index);
}
return;
}
}
catch (Exception e)
{
ExplorerCore.LogError(e.ToString());
Stop(FindOriginalCoro(enumerator)); // We want the entire coroutine hierachy to stop when an error happen
}
var next = enumerator.Current;
switch (next)
{
case null:
ourNextFrameCoroutines.Add(enumerator);
return;
case WaitForFixedUpdate _:
ourWaitForFixedUpdateCoroutines.Add(enumerator);
return;
case WaitForEndOfFrame _:
ourWaitForEndOfFrameCoroutines.Add(enumerator);
return;
case WaitForSeconds _:
break; // do nothing, this one is supported in Process
case Il2CppObjectBase il2CppObjectBase:
var nextAsEnumerator = il2CppObjectBase.TryCast<Il2CppSystem.Collections.IEnumerator>();
if (nextAsEnumerator != null) // il2cpp IEnumerator also handles CustomYieldInstruction
next = new Il2CppEnumeratorWrapper(nextAsEnumerator);
else
ExplorerCore.LogWarning($"Unknown coroutine yield object of type '{il2CppObjectBase}' for coroutine '{enumerator}'");
return;
default:
ExplorerCore.LogWarning($"Unknown coroutine yield object of type '{next}' for coroutine '{enumerator}'");
return;
}
ourCoroutinesStore.Add(new CoroTuple { WaitCondition = next, Coroutine = enumerator });
if (next is IEnumerator nextCoro)
ProcessNextOfCoroutine(nextCoro);
}
private static IEnumerator FindOriginalCoro(IEnumerator enumerator)
{
int index = ourCoroutinesStore.FindIndex(ct => ct.WaitCondition == enumerator);
if (index == -1)
return enumerator;
return FindOriginalCoro(ourCoroutinesStore[index].Coroutine);
}
private class Il2CppEnumeratorWrapper : IEnumerator
{
private readonly Il2CppSystem.Collections.IEnumerator il2cppEnumerator;
public Il2CppEnumeratorWrapper(Il2CppSystem.Collections.IEnumerator il2CppEnumerator) => il2cppEnumerator = il2CppEnumerator;
public bool MoveNext() => il2cppEnumerator.MoveNext();
public void Reset() => il2cppEnumerator.Reset();
public object Current => il2cppEnumerator.Current;
}
}
}
#endif

View File

@ -1,268 +0,0 @@
#if CPP
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using BF = System.Reflection.BindingFlags;
using System.Text;
using UnhollowerBaseLib;
using UnhollowerRuntimeLib;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.SceneManagement;
using System.Collections;
using UnityEngine.UI;
using UnityExplorer.Core.Input;
using UnityEngine.EventSystems;
namespace UnityExplorer.Core.Runtime.Il2Cpp
{
public class Il2CppProvider : RuntimeProvider
{
public override void Initialize()
{
ExplorerCore.Context = RuntimeContext.IL2CPP;
TextureUtil = new Il2CppTextureUtil();
}
public override void SetupEvents()
{
try
{
Application.add_logMessageReceived(new Action<string, string, LogType>(Application_logMessageReceived));
}
catch (Exception ex)
{
ExplorerCore.LogWarning("Exception setting up Unity log listener, make sure Unity libraries have been unstripped!");
ExplorerCore.Log(ex);
}
}
private void Application_logMessageReceived(string condition, string stackTrace, LogType type)
{
ExplorerCore.LogUnity(condition, type);
}
public override void Update()
{
Il2CppCoroutine.Process();
}
internal override void ProcessOnPostRender()
{
Il2CppCoroutine.ProcessWaitForEndOfFrame();
}
internal override void ProcessFixedUpdate()
{
Il2CppCoroutine.ProcessWaitForFixedUpdate();
}
public override void StartCoroutine(IEnumerator routine)
{
Il2CppCoroutine.Start(routine);
}
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));
}
// Pretty disgusting but couldn't figure out a cleaner way yet unfortunately
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());
}
// LayerMask.LayerToName
internal delegate IntPtr d_LayerToName(int layer);
public override string LayerToName(int layer)
{
var iCall = ICallManager.GetICall<d_LayerToName>("UnityEngine.LayerMask::LayerToName");
return IL2CPP.Il2CppStringToManaged(iCall.Invoke(layer));
}
// Resources.FindObjectsOfTypeAll
internal delegate IntPtr d_FindObjectsOfTypeAll(IntPtr type);
internal static readonly string[] findObjectsOfTypeAllSignatures = new[]
{
"UnityEngine.Resources::FindObjectsOfTypeAll",
"UnityEngine.ResourcesAPIInternal::FindObjectsOfTypeAll" // Unity 2020+ updated to this
};
public override UnityEngine.Object[] FindObjectsOfTypeAll(Type type)
{
return new Il2CppReferenceArray<UnityEngine.Object>(
ICallManager.GetICallUnreliable<d_FindObjectsOfTypeAll>(findObjectsOfTypeAllSignatures)
.Invoke(Il2CppType.From(type).Pointer));
}
// Scene.GetRootGameObjects();
internal delegate void d_GetRootGameObjects(int handle, IntPtr list);
public override GameObject[] GetRootGameObjects(Scene scene)
{
if (!scene.isLoaded)
return new GameObject[0];
if (scene.handle == -1)
return new GameObject[0];
int count = GetRootCount(scene.handle);
if (count < 1)
return new GameObject[0];
var list = new Il2CppSystem.Collections.Generic.List<GameObject>(count);
var iCall = ICallManager.GetICall<d_GetRootGameObjects>("UnityEngine.SceneManagement.Scene::GetRootGameObjectsInternal");
iCall.Invoke(scene.handle, list.Pointer);
return list.ToArray();
}
// Scene.rootCount
internal delegate int d_GetRootCountInternal(int handle);
public override int GetRootCount(Scene scene) => GetRootCount(scene.handle);
public static int GetRootCount(int handle)
{
return ICallManager.GetICall<d_GetRootCountInternal>("UnityEngine.SceneManagement.Scene::GetRootCountInternal")
.Invoke(handle);
}
internal static bool triedToGetColorBlockProps;
internal static PropertyInfo _normalColorProp;
internal static PropertyInfo _highlightColorProp;
internal static PropertyInfo _pressedColorProp;
internal static PropertyInfo _disabledColorProp;
public override void SetColorBlock(Selectable selectable, Color? normal = null, Color? highlighted = null, Color? pressed = null,
Color? disabled = null)
{
var colors = selectable.colors;
colors.colorMultiplier = 1;
object boxed = (object)colors;
if (!triedToGetColorBlockProps)
{
triedToGetColorBlockProps = true;
if (ReflectionUtility.GetPropertyInfo(typeof(ColorBlock), "normalColor") is PropertyInfo norm && norm.CanWrite)
_normalColorProp = norm;
if (ReflectionUtility.GetPropertyInfo(typeof(ColorBlock), "highlightedColor") is PropertyInfo high && high.CanWrite)
_highlightColorProp = high;
if (ReflectionUtility.GetPropertyInfo(typeof(ColorBlock), "pressedColor") is PropertyInfo pres && pres.CanWrite)
_pressedColorProp = pres;
if (ReflectionUtility.GetPropertyInfo(typeof(ColorBlock), "disabledColor") is PropertyInfo disa && disa.CanWrite)
_disabledColorProp = disa;
}
try
{
if (normal != null)
{
if (_normalColorProp != null)
_normalColorProp.SetValue(boxed, (Color)normal);
else if (ReflectionUtility.GetFieldInfo(typeof(ColorBlock), "m_NormalColor") is FieldInfo fi)
fi.SetValue(boxed, (Color)normal);
}
if (highlighted != null)
{
if (_highlightColorProp != null)
_highlightColorProp.SetValue(boxed, (Color)highlighted);
else if (ReflectionUtility.GetFieldInfo(typeof(ColorBlock), "m_HighlightedColor") is FieldInfo fi)
fi.SetValue(boxed, (Color)highlighted);
}
if (pressed != null)
{
if (_pressedColorProp != null)
_pressedColorProp.SetValue(boxed, (Color)pressed);
else if (ReflectionUtility.GetFieldInfo(typeof(ColorBlock), "m_PressedColor") is FieldInfo fi)
fi.SetValue(boxed, (Color)pressed);
}
if (disabled != null)
{
if (_disabledColorProp != null)
_disabledColorProp.SetValue(boxed, (Color)disabled);
else if (ReflectionUtility.GetFieldInfo(typeof(ColorBlock), "m_DisabledColor") is FieldInfo fi)
fi.SetValue(boxed, (Color)disabled);
}
}
catch (Exception ex)
{
ExplorerCore.Log(ex);
}
colors = (ColorBlock)boxed;
SetColorBlock(selectable, colors);
}
public override void SetColorBlock(Selectable selectable, ColorBlock _colorBlock)
{
try
{
selectable = selectable.TryCast<Selectable>();
ReflectionUtility.GetPropertyInfo(typeof(Selectable), "m_Colors")
.SetValue(selectable, _colorBlock, null);
ReflectionUtility.GetMethodInfo(typeof(Selectable), "OnSetProperty")
.Invoke(selectable, ArgumentUtility.EmptyArgs);
}
catch (Exception ex)
{
ExplorerCore.Log(ex);
}
}
}
}
public static class Il2CppExtensions
{
public static void AddListener(this UnityEvent action, Action listener)
{
action.AddListener(listener);
}
public static void AddListener<T>(this UnityEvent<T> action, Action<T> listener)
{
action.AddListener(listener);
}
public static void RemoveListener(this UnityEvent action, Action listener)
{
action.RemoveListener(listener);
}
public static void RemoveListener<T>(this UnityEvent<T> action, Action<T> listener)
{
action.RemoveListener(listener);
}
public static void SetChildControlHeight(this HorizontalOrVerticalLayoutGroup group, bool value) => group.childControlHeight = value;
public static void SetChildControlWidth(this HorizontalOrVerticalLayoutGroup group, bool value) => group.childControlWidth = value;
}
#endif

View File

@ -1,64 +0,0 @@
#if CPP
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnhollowerBaseLib;
using UnityEngine;
namespace UnityExplorer.Core.Runtime.Il2Cpp
{
public class Il2CppTextureUtil : TextureUtilProvider
{
public override Texture2D NewTexture2D(int width, int height)
=> new Texture2D((int)width, (int)height, TextureFormat.RGBA32, Texture.GenerateAllMips, false, IntPtr.Zero);
internal delegate void d_Blit2(IntPtr source, IntPtr dest);
public override void Blit(Texture2D tex, RenderTexture rt)
{
var iCall = ICallManager.GetICall<d_Blit2>("UnityEngine.Graphics::Blit2");
iCall.Invoke(tex.Pointer, rt.Pointer);
}
// byte[] ImageConversion.EncodeToPNG(this Texture2D image);
internal delegate IntPtr d_EncodeToPNG(IntPtr tex);
public override byte[] EncodeToPNG(Texture2D tex)
{
var iCall = ICallManager.GetICall<d_EncodeToPNG>("UnityEngine.ImageConversion::EncodeToPNG");
IntPtr ptr = iCall.Invoke(tex.Pointer);
if (ptr == IntPtr.Zero)
return null;
return new Il2CppStructArray<byte>(ptr);
}
// Sprite Sprite.Create
public override Sprite CreateSprite(Texture2D texture)
{
return CreateSpriteImpl(texture, new Rect(0, 0, texture.width, texture.height), Vector2.zero, 100f, 0u, Vector4.zero);
}
internal delegate IntPtr d_CreateSprite(IntPtr texture, ref Rect rect, ref Vector2 pivot, float pixelsPerUnit,
uint extrude, int meshType, ref Vector4 border, bool generateFallbackPhysicsShape);
public static Sprite CreateSpriteImpl(Texture texture, Rect rect, Vector2 pivot, float pixelsPerUnit, uint extrude, Vector4 border)
{
var iCall = ICallManager.GetICall<d_CreateSprite>("UnityEngine.Sprite::CreateSprite_Injected");
var ptr = iCall.Invoke(texture.Pointer, ref rect, ref pivot, pixelsPerUnit, extrude, 1, ref border, false);
if (ptr == IntPtr.Zero)
return null;
else
return new Sprite(ptr);
}
}
}
#endif

View File

@ -0,0 +1,189 @@
#if CPP
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using BF = System.Reflection.BindingFlags;
using System.Text;
using UnhollowerBaseLib;
using UnhollowerRuntimeLib;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.SceneManagement;
using System.Collections;
using UnityEngine.UI;
using UnityEngine.EventSystems;
namespace UnityExplorer.Core.Runtime
{
public class Il2CppProvider : RuntimeHelper
{
public override void SetupEvents()
{
try
{
Application.add_logMessageReceived(new Action<string, string, LogType>(Application_logMessageReceived));
}
catch (Exception ex)
{
ExplorerCore.LogWarning("Exception setting up Unity log listener, make sure Unity libraries have been unstripped!");
ExplorerCore.Log(ex);
}
}
private void Application_logMessageReceived(string condition, string stackTrace, LogType type)
{
ExplorerCore.LogUnity(condition, type);
}
public override string[] DefaultReflectionBlacklist => defaultIl2CppBlacklist.ToArray();
// These methods currently cause a crash in most il2cpp games,
// even from doing "GetParameters()" on the MemberInfo.
// Blacklisting until the issue is fixed in Unhollower.
public static HashSet<string> defaultIl2CppBlacklist = new HashSet<string>
{
// These were deprecated a long time ago, still show up in some IL2CPP games for some reason
"UnityEngine.MonoBehaviour.allowPrefabModeInPlayMode",
"UnityEngine.MonoBehaviour.runInEditMode",
"UnityEngine.Component.animation",
"UnityEngine.Component.audio",
"UnityEngine.Component.camera",
"UnityEngine.Component.collider",
"UnityEngine.Component.collider2D",
"UnityEngine.Component.constantForce",
"UnityEngine.Component.hingeJoint",
"UnityEngine.Component.light",
"UnityEngine.Component.networkView",
"UnityEngine.Component.particleSystem",
"UnityEngine.Component.renderer",
"UnityEngine.Component.rigidbody",
"UnityEngine.Component.rigidbody2D",
"UnityEngine.Light.flare",
// These can cause a crash in IL2CPP
"Il2CppSystem.Type.DeclaringMethod",
"Il2CppSystem.RuntimeType.DeclaringMethod",
"Unity.Jobs.LowLevel.Unsafe.JobsUtility.CreateJobReflectionData",
"Unity.Profiling.ProfilerRecorder.CopyTo",
"Unity.Profiling.ProfilerRecorder.StartNew",
"UnityEngine.Analytics.Analytics.RegisterEvent",
"UnityEngine.Analytics.Analytics.SendEvent",
"UnityEngine.Analytics.ContinuousEvent+ConfigureEventDelegate.Invoke",
"UnityEngine.Analytics.ContinuousEvent.ConfigureEvent",
"UnityEngine.Animations.AnimationLayerMixerPlayable.Create",
"UnityEngine.Animations.AnimationLayerMixerPlayable.CreateHandle",
"UnityEngine.Animations.AnimationMixerPlayable.Create",
"UnityEngine.Animations.AnimationMixerPlayable.CreateHandle",
"UnityEngine.AssetBundle.RecompressAssetBundleAsync",
"UnityEngine.Audio.AudioMixerPlayable.Create",
"UnityEngine.BoxcastCommand.ScheduleBatch",
"UnityEngine.Camera.CalculateProjectionMatrixFromPhysicalProperties",
"UnityEngine.Canvas.renderingDisplaySize",
"UnityEngine.CapsulecastCommand.ScheduleBatch",
"UnityEngine.Collider2D.Cast",
"UnityEngine.Collider2D.Raycast",
"UnityEngine.ComputeBuffer+BeginBufferWriteDelegate.Invoke",
"UnityEngine.ComputeBuffer+EndBufferWriteDelegate.Invoke",
"UnityEngine.ComputeBuffer.BeginBufferWrite",
"UnityEngine.ComputeBuffer.EndBufferWrite",
"UnityEngine.Cubemap+SetPixelDataImplArrayDelegate.Invoke",
"UnityEngine.Cubemap+SetPixelDataImplDelegate.Invoke",
"UnityEngine.Cubemap.SetPixelDataImpl",
"UnityEngine.Cubemap.SetPixelDataImplArray",
"UnityEngine.CubemapArray+SetPixelDataImplArrayDelegate.Invoke",
"UnityEngine.CubemapArray+SetPixelDataImplDelegate.Invoke",
"UnityEngine.CubemapArray.SetPixelDataImpl",
"UnityEngine.CubemapArray.SetPixelDataImplArray",
"UnityEngine.Experimental.Playables.MaterialEffectPlayable.Create",
"UnityEngine.Experimental.Rendering.RayTracingAccelerationStructure+AddInstanceDelegate.Invoke",
"UnityEngine.Experimental.Rendering.RayTracingAccelerationStructure+AddInstance_Procedural_InjectedDelegate.Invoke",
"UnityEngine.Experimental.Rendering.RayTracingAccelerationStructure.AddInstance",
"UnityEngine.Experimental.Rendering.RayTracingAccelerationStructure.AddInstance_Procedural",
"UnityEngine.Experimental.Rendering.RayTracingAccelerationStructure.AddInstance_Procedural_Injected",
"UnityEngine.Experimental.Rendering.RayTracingShader+DispatchDelegate.Invoke",
"UnityEngine.Experimental.Rendering.RayTracingShader.Dispatch",
"UnityEngine.Experimental.Rendering.RenderPassAttachment.Clear",
"UnityEngine.GUI.DoButtonGrid",
"UnityEngine.GUI.Slider",
"UnityEngine.GUI.Toolbar",
"UnityEngine.Graphics.DrawMeshInstancedIndirect",
"UnityEngine.Graphics.DrawMeshInstancedProcedural",
"UnityEngine.Graphics.DrawProcedural",
"UnityEngine.Graphics.DrawProceduralIndirect",
"UnityEngine.Graphics.DrawProceduralIndirectNow",
"UnityEngine.Graphics.DrawProceduralNow",
"UnityEngine.LineRenderer+BakeMeshDelegate.Invoke",
"UnityEngine.LineRenderer.BakeMesh",
"UnityEngine.Mesh.GetIndices",
"UnityEngine.Mesh.GetTriangles",
"UnityEngine.Mesh.SetIndices",
"UnityEngine.Mesh.SetTriangles",
"UnityEngine.Physics2D.BoxCast",
"UnityEngine.Physics2D.CapsuleCast",
"UnityEngine.Physics2D.CircleCast",
"UnityEngine.PhysicsScene.BoxCast",
"UnityEngine.PhysicsScene.CapsuleCast",
"UnityEngine.PhysicsScene.OverlapBox",
"UnityEngine.PhysicsScene.OverlapCapsule",
"UnityEngine.PhysicsScene.SphereCast",
"UnityEngine.PhysicsScene2D.BoxCast",
"UnityEngine.PhysicsScene2D.CapsuleCast",
"UnityEngine.PhysicsScene2D.CircleCast",
"UnityEngine.PhysicsScene2D.GetRayIntersection",
"UnityEngine.PhysicsScene2D.Linecast",
"UnityEngine.PhysicsScene2D.OverlapArea",
"UnityEngine.PhysicsScene2D.OverlapBox",
"UnityEngine.PhysicsScene2D.OverlapCapsule",
"UnityEngine.PhysicsScene2D.OverlapCircle",
"UnityEngine.PhysicsScene2D.OverlapCollider",
"UnityEngine.PhysicsScene2D.OverlapPoint",
"UnityEngine.PhysicsScene2D.Raycast",
"UnityEngine.Playables.Playable.Create",
"UnityEngine.Profiling.CustomSampler.Create",
"UnityEngine.RaycastCommand.ScheduleBatch",
"UnityEngine.RemoteConfigSettings+QueueConfigDelegate.Invoke",
"UnityEngine.RemoteConfigSettings.QueueConfig",
"UnityEngine.RenderTexture.GetTemporaryImpl",
"UnityEngine.Rendering.AsyncGPUReadback.Request",
"UnityEngine.Rendering.AttachmentDescriptor.ConfigureClear",
"UnityEngine.Rendering.BatchRendererGroup+AddBatch_InjectedDelegate.Invoke",
"UnityEngine.Rendering.BatchRendererGroup.AddBatch",
"UnityEngine.Rendering.BatchRendererGroup.AddBatch_Injected",
"UnityEngine.Rendering.CommandBuffer+Internal_DispatchRaysDelegate.Invoke",
"UnityEngine.Rendering.CommandBuffer.DispatchRays",
"UnityEngine.Rendering.CommandBuffer.DrawMeshInstancedProcedural",
"UnityEngine.Rendering.CommandBuffer.Internal_DispatchRays",
"UnityEngine.Rendering.CommandBuffer.ResolveAntiAliasedSurface",
"UnityEngine.Rendering.ScriptableRenderContext.BeginRenderPass",
"UnityEngine.Rendering.ScriptableRenderContext.BeginScopedRenderPass",
"UnityEngine.Rendering.ScriptableRenderContext.BeginScopedSubPass",
"UnityEngine.Rendering.ScriptableRenderContext.BeginSubPass",
"UnityEngine.Rendering.ScriptableRenderContext.SetupCameraProperties",
"UnityEngine.Rigidbody2D.Cast",
"UnityEngine.Scripting.GarbageCollector+CollectIncrementalDelegate.Invoke",
"UnityEngine.Scripting.GarbageCollector.CollectIncremental",
"UnityEngine.SpherecastCommand.ScheduleBatch",
"UnityEngine.Texture.GetPixelDataSize",
"UnityEngine.Texture.GetPixelDataOffset",
"UnityEngine.Texture.GetPixelDataOffset",
"UnityEngine.Texture2D+SetPixelDataImplArrayDelegate.Invoke",
"UnityEngine.Texture2D+SetPixelDataImplDelegate.Invoke",
"UnityEngine.Texture2D.SetPixelDataImpl",
"UnityEngine.Texture2D.SetPixelDataImplArray",
"UnityEngine.Texture2DArray+SetPixelDataImplArrayDelegate.Invoke",
"UnityEngine.Texture2DArray+SetPixelDataImplDelegate.Invoke",
"UnityEngine.Texture2DArray.SetPixelDataImpl",
"UnityEngine.Texture2DArray.SetPixelDataImplArray",
"UnityEngine.Texture3D+SetPixelDataImplArrayDelegate.Invoke",
"UnityEngine.Texture3D+SetPixelDataImplDelegate.Invoke",
"UnityEngine.Texture3D.SetPixelDataImpl",
"UnityEngine.Texture3D.SetPixelDataImplArray",
"UnityEngine.TrailRenderer+BakeMeshDelegate.Invoke",
"UnityEngine.TrailRenderer.BakeMesh",
"UnityEngine.WWW.LoadFromCacheOrDownload",
"UnityEngine.XR.InputDevice.SendHapticImpulse",
};
}
}
#endif

View File

@ -1,119 +0,0 @@
#if MONO
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.EventSystems;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
using UnityExplorer;
namespace UnityExplorer.Core.Runtime.Mono
{
public class MonoProvider : RuntimeProvider
{
public override void Initialize()
{
ExplorerCore.Context = RuntimeContext.Mono;
TextureUtil = new MonoTextureUtil();
}
public override void SetupEvents()
{
Application.logMessageReceived += Application_logMessageReceived;
}
private void Application_logMessageReceived(string condition, string stackTrace, LogType type)
=> ExplorerCore.LogUnity(condition, type);
public override void StartCoroutine(IEnumerator routine)
=> ExplorerBehaviour.Instance.StartCoroutine(routine);
public override void Update()
{
}
public override T AddComponent<T>(GameObject obj, Type type)
=> (T)obj.AddComponent(type);
public override ScriptableObject CreateScriptable(Type type)
=> 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);
public override UnityEngine.Object[] FindObjectsOfTypeAll(Type type)
=> Resources.FindObjectsOfTypeAll(type);
public override GameObject[] GetRootGameObjects(Scene scene)
=> scene.isLoaded ? scene.GetRootGameObjects() : new GameObject[0];
public override int GetRootCount(Scene scene)
=> scene.rootCount;
public override void SetColorBlock(Selectable selectable, Color? normal = null, Color? highlighted = null, Color? pressed = null,
Color? disabled = null)
{
var colors = selectable.colors;
if (normal != null)
colors.normalColor = (Color)normal;
if (highlighted != null)
colors.highlightedColor = (Color)highlighted;
if (pressed != null)
colors.pressedColor = (Color)pressed;
if (disabled != null)
colors.disabledColor = (Color)disabled;
SetColorBlock(selectable, colors);
}
public override void SetColorBlock(Selectable selectable, ColorBlock colors)
=> selectable.colors = colors;
}
}
public static class MonoExtensions
{
// Helpers to use the same style of AddListener that IL2CPP uses.
public static void AddListener(this UnityEvent _event, Action listener)
=> _event.AddListener(new UnityAction(listener));
public static void AddListener<T>(this UnityEvent<T> _event, Action<T> listener)
=> _event.AddListener(new UnityAction<T>(listener));
public static void RemoveListener(this UnityEvent _event, Action listener)
=> _event.RemoveListener(new UnityAction(listener));
public static void RemoveListener<T>(this UnityEvent<T> _event, Action<T> listener)
=> _event.RemoveListener(new UnityAction<T>(listener));
// Doesn't exist in NET 3.5
public static void Clear(this StringBuilder sb)
=> sb.Remove(0, sb.Length);
// These properties don't exist in some earlier games, so null check before trying to set them.
public static void SetChildControlHeight(this HorizontalOrVerticalLayoutGroup group, bool value)
=> ReflectionUtility.GetPropertyInfo(typeof(HorizontalOrVerticalLayoutGroup), "childControlHeight")
?.SetValue(group, value, null);
public static void SetChildControlWidth(this HorizontalOrVerticalLayoutGroup group, bool value)
=> ReflectionUtility.GetPropertyInfo(typeof(HorizontalOrVerticalLayoutGroup), "childControlWidth")
?.SetValue(group, value, null);
}
#endif

View File

@ -1,53 +0,0 @@
#if MONO
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using UnityEngine;
using UnityExplorer.Core;
namespace UnityExplorer.Core.Runtime.Mono
{
public class MonoTextureUtil : TextureUtilProvider
{
public override void Blit(Texture2D tex, RenderTexture rt)
=> Graphics.Blit(tex, rt);
public override Sprite CreateSprite(Texture2D texture)
=> Sprite.Create(texture, new Rect(0, 0, texture.width, texture.height), Vector2.zero);
//public override bool LoadImage(Texture2D tex, byte[] data, bool markNonReadable)
// => tex.LoadImage(data, markNonReadable);
public override Texture2D NewTexture2D(int width, int height)
=> new Texture2D(width, height);
public override byte[] EncodeToPNG(Texture2D tex)
=> EncodeToPNGSafe(tex);
private static MethodInfo EncodeToPNGMethod => m_encodeToPNGMethod ?? GetEncodeToPNGMethod();
private static MethodInfo m_encodeToPNGMethod;
public static byte[] EncodeToPNGSafe(Texture2D tex)
{
return EncodeToPNGMethod.IsStatic
? (byte[])EncodeToPNGMethod.Invoke(null, new object[] { tex })
: (byte[])EncodeToPNGMethod.Invoke(tex, ArgumentUtility.EmptyArgs);
}
private static MethodInfo GetEncodeToPNGMethod()
{
if (ReflectionUtility.GetTypeByName("UnityEngine.ImageConversion") is Type imageConversion)
return m_encodeToPNGMethod = imageConversion.GetMethod("EncodeToPNG", ReflectionUtility.FLAGS);
var method = typeof(Texture2D).GetMethod("EncodeToPNG", ReflectionUtility.FLAGS);
if (method != null)
return m_encodeToPNGMethod = method;
ExplorerCore.Log("ERROR: Cannot get any EncodeToPNG method!");
return null;
}
}
}
#endif

View File

@ -0,0 +1,29 @@
#if MONO
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.EventSystems;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
using UnityExplorer;
namespace UnityExplorer.Core.Runtime
{
public class MonoProvider : RuntimeHelper
{
public override void SetupEvents()
{
Application.logMessageReceived += Application_logMessageReceived;
}
private void Application_logMessageReceived(string condition, string stackTrace, LogType type)
=> ExplorerCore.LogUnity(condition, type);
}
}
#endif

View File

@ -1,13 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace UnityExplorer.Core.Runtime
{
public enum RuntimeContext
{
Mono,
IL2CPP
}
}

View File

@ -0,0 +1,94 @@
using System;
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.Config;
using UniverseLib;
namespace UnityExplorer.Core.Runtime
{
public abstract class RuntimeHelper
{
public static RuntimeHelper Instance;
public static void Init()
{
#if CPP
Instance = new Il2CppProvider();
#else
Instance = new MonoProvider();
#endif
Instance.SetupEvents();
LoadBlacklistString(ConfigManager.Reflection_Signature_Blacklist.Value);
ConfigManager.Reflection_Signature_Blacklist.OnValueChanged += (string val) =>
{
LoadBlacklistString(val);
};
}
public abstract void SetupEvents();
#region Reflection Blacklist
private static readonly HashSet<string> currentBlacklist = new HashSet<string>();
public virtual string[] DefaultReflectionBlacklist => new string[0];
public static void LoadBlacklistString(string blacklist)
{
try
{
if (string.IsNullOrEmpty(blacklist) && !Instance.DefaultReflectionBlacklist.Any())
return;
try
{
var sigs = blacklist.Split(';');
foreach (var sig in sigs)
{
var s = sig.Trim();
if (string.IsNullOrEmpty(s))
continue;
if (!currentBlacklist.Contains(s))
currentBlacklist.Add(s);
}
}
catch (Exception ex)
{
ExplorerCore.LogWarning($"Exception parsing blacklist string: {ex.ReflectionExToString()}");
}
foreach (var sig in Instance.DefaultReflectionBlacklist)
{
if (!currentBlacklist.Contains(sig))
currentBlacklist.Add(sig);
}
Mono.CSharp.IL2CPP.Blacklist.SignatureBlacklist = currentBlacklist;
}
catch (Exception ex)
{
ExplorerCore.LogWarning($"Exception setting up reflection blacklist: {ex.ReflectionExToString()}");
}
}
public static bool IsBlacklisted(MemberInfo member)
{
if (string.IsNullOrEmpty(member.DeclaringType?.Namespace))
return false;
var sig = $"{member.DeclaringType.FullName}.{member.Name}";
return currentBlacklist.Contains(sig);
}
#endregion
}
}

View File

@ -1,75 +0,0 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
using UnityExplorer.Core.Runtime;
// Intentionally project-wide namespace so that its always easily accessible.
namespace UnityExplorer
{
public abstract class RuntimeProvider
{
public static RuntimeProvider Instance;
public TextureUtilProvider TextureUtil;
public RuntimeProvider()
{
Initialize();
SetupEvents();
}
public static void Init() =>
#if CPP
Instance = new Core.Runtime.Il2Cpp.Il2CppProvider();
#else
Instance = new Core.Runtime.Mono.MonoProvider();
#endif
public abstract void Initialize();
public abstract void SetupEvents();
public abstract void StartCoroutine(IEnumerator routine);
public abstract void Update();
// 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 GameObject[] GetRootGameObjects(Scene scene);
public abstract int GetRootCount(Scene scene);
public void SetColorBlockAuto(Selectable selectable, Color baseColor)
=> SetColorBlock(selectable, baseColor, baseColor * 1.2f, baseColor * 0.8f);
public abstract void SetColorBlock(Selectable selectable, ColorBlock colors);
public abstract void SetColorBlock(Selectable selectable, Color? normal = null, Color? highlighted = null, Color? pressed = null,
Color? disabled = null);
internal virtual void ProcessOnPostRender()
{
}
internal virtual void ProcessFixedUpdate()
{
}
}
}

View File

@ -1,149 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using UnityEngine;
namespace UnityExplorer.Core.Runtime
{
public abstract class TextureUtilProvider
{
public static TextureUtilProvider Instance;
public TextureUtilProvider()
{
Instance = this;
}
public abstract byte[] EncodeToPNG(Texture2D tex);
public abstract Texture2D NewTexture2D(int width, int height);
public abstract void Blit(Texture2D tex, RenderTexture rt);
//public abstract bool LoadImage(Texture2D tex, byte[] data, bool markNonReadable);
public abstract Sprite CreateSprite(Texture2D texture);
public static bool IsReadable(Texture2D tex)
{
try
{
// This will cause an exception if it's not readable.
// Reason for doing it this way is not all Unity versions
// ship with the 'Texture.isReadable' property.
tex.GetPixel(0, 0);
return true;
}
catch
{
return false;
}
}
//public static bool LoadImage(Texture2D tex, string filePath, bool markNonReadable)
//{
// if (!File.Exists(filePath))
// return false;
//
// return Instance.LoadImage(tex, File.ReadAllBytes(filePath), markNonReadable);
//}
public static Texture2D Copy(Texture2D orig, Rect rect)
{
if (!IsReadable(orig))
orig = ForceReadTexture(orig);
Color[] pixels = orig.GetPixels((int)rect.x, (int)rect.y, (int)rect.width, (int)rect.height);
Texture2D newTex = Instance.NewTexture2D((int)rect.width, (int)rect.height);
newTex.SetPixels(pixels);
return newTex;
}
public static Texture2D ForceReadTexture(Texture2D tex)
{
try
{
FilterMode origFilter = tex.filterMode;
tex.filterMode = FilterMode.Point;
var rt = RenderTexture.GetTemporary(tex.width, tex.height, 0, RenderTextureFormat.ARGB32);
rt.filterMode = FilterMode.Point;
RenderTexture.active = rt;
Instance.Blit(tex, rt);
var _newTex = new Texture2D(tex.width, tex.height, TextureFormat.ARGB32, false);
_newTex.ReadPixels(new Rect(0, 0, tex.width, tex.height), 0, 0);
_newTex.Apply(false, false);
RenderTexture.active = null;
tex.filterMode = origFilter;
return _newTex;
}
catch (Exception e)
{
ExplorerCore.Log($"Exception on ForceReadTexture: {e.ToString()}");
return default;
}
}
public static void SaveTextureAsPNG(Texture2D tex, string dir, string name, bool isDTXnmNormal = false)
{
if (!Directory.Exists(dir))
Directory.CreateDirectory(dir);
byte[] data;
string savepath = $@"{dir}\{name}.png";
// Make sure we can EncodeToPNG it.
if (tex.format != TextureFormat.ARGB32 || !IsReadable(tex))
tex = ForceReadTexture(tex);
if (isDTXnmNormal)
{
tex = DTXnmToRGBA(tex);
tex.Apply(false, false);
}
data = Instance.EncodeToPNG(tex);
if (data == null || !data.Any())
ExplorerCore.LogWarning("Couldn't get any data for the texture!");
else
File.WriteAllBytes(savepath, data);
}
// Converts DTXnm-format Normal Map to RGBA-format Normal Map.
public static Texture2D DTXnmToRGBA(Texture2D tex)
{
Color[] colors = tex.GetPixels();
for (int i = 0; i < colors.Length; i++)
{
var c = colors[i];
c.r = c.a * 2 - 1; // red <- alpha
c.g = c.g * 2 - 1; // green is always the same
var rg = new Vector2(c.r, c.g); //this is the red-green vector
c.b = Mathf.Sqrt(1 - Mathf.Clamp01(Vector2.Dot(rg, rg))); //recalculate the blue channel
colors[i] = new Color(
(c.r * 0.5f) + 0.5f,
(c.g * 0.5f) + 0.25f,
(c.b * 0.5f) + 0.5f
);
}
var newtex = new Texture2D(tex.width, tex.height, TextureFormat.ARGB32, false);
newtex.SetPixels(colors);
return newtex;
}
}
}

View File

@ -1,15 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace UnityExplorer
{
public static class ArgumentUtility
{
public static readonly Type[] EmptyTypes = new Type[0];
public static readonly object[] EmptyArgs = new object[0];
public static readonly Type[] ParseArgs = new Type[] { typeof(string) };
}
}

View File

@ -1,30 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
namespace UnityExplorer
{
public static class IOUtility
{
private static readonly char[] invalidDirectoryCharacters = Path.GetInvalidPathChars();
private static readonly char[] invalidFilenameCharacters = Path.GetInvalidFileNameChars();
public static string EnsureValidFilePath(string fullPathWithFile)
{
// Remove invalid path characters
fullPathWithFile = string.Concat(fullPathWithFile.Split(invalidDirectoryCharacters));
// Create directory (does nothing if it exists)
Directory.CreateDirectory(Path.GetDirectoryName(fullPathWithFile));
return fullPathWithFile;
}
public static string EnsureValidFilename(string filename)
{
return string.Concat(filename.Split(invalidFilenameCharacters));
}
}
}

View File

@ -1,28 +0,0 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
namespace UnityExplorer
{
public static class MiscUtility
{
/// <summary>
/// Check if a string contains another string, case-insensitive.
/// </summary>
public static bool ContainsIgnoreCase(this string _this, string s)
{
return CultureInfo.CurrentCulture.CompareInfo.IndexOf(_this, s, CompareOptions.IgnoreCase) >= 0;
}
/// <summary>
/// Just to allow Enum to do .HasFlag() in NET 3.5
/// </summary>
public static bool HasFlag(this Enum flags, Enum value)
{
ulong flag = Convert.ToUInt64(value);
return (Convert.ToUInt64(flags) & flag) == flag;
}
}
}

View File

@ -1,414 +0,0 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Text;
using UnityEngine;
namespace UnityExplorer
{
public static class ParseUtility
{
private static readonly HashSet<Type> nonPrimitiveTypes = new HashSet<Type>
{
typeof(string),
typeof(decimal),
typeof(DateTime),
};
// Helper for formatting float/double/decimal numbers to maximum of 4 decimal points.
// And also for formatting a sequence of those numbers, ie a Vector3, Color etc
public static readonly string NumberFormatString = $"0.####";
private static readonly Dictionary<int, string> numSequenceStrings = new Dictionary<int, string>();
public static string FormatDecimalSequence(params object[] numbers)
{
if (numbers.Length <= 0)
return null;
return string.Format(CultureInfo.CurrentCulture, GetSequenceFormatString(numbers.Length), numbers);
}
public static string GetSequenceFormatString(int count)
{
if (count <= 0)
return null;
if (numSequenceStrings.ContainsKey(count))
return numSequenceStrings[count];
string[] strings = new string[count];
for (int i = 0; i < count; i++)
strings[i] = $"{{{i}:{NumberFormatString}}}";
string ret = string.Join(" ", strings);
numSequenceStrings.Add(count, ret);
return ret;
}
// Main parsing API
public static bool CanParse(Type type)
{
return !string.IsNullOrEmpty(type?.FullName)
&& (type.IsPrimitive || type.IsEnum || nonPrimitiveTypes.Contains(type) || customTypes.ContainsKey(type.FullName));
}
public static bool TryParse(string input, Type type, out object obj, out Exception parseException)
{
obj = null;
parseException = null;
if (type == null)
return false;
if (type == typeof(string))
{
obj = input;
return true;
}
if (type.IsEnum)
{
try
{
obj = Enum.Parse(type, input);
return true;
}
catch (Exception ex)
{
parseException = ex.GetInnerMostException();
return false;
}
}
try
{
if (customTypes.ContainsKey(type.FullName))
{
obj = customTypes[type.FullName].Invoke(input);
}
else
{
obj = ReflectionUtility.GetMethodInfo(type, "Parse", ArgumentUtility.ParseArgs)
.Invoke(null, new object[] { input });
}
return true;
}
catch (Exception ex)
{
ex = ex.GetInnerMostException();
parseException = ex;
}
return false;
}
private static readonly HashSet<Type> formattedTypes = new HashSet<Type>
{
typeof(float),
typeof(double),
typeof(decimal)
};
public static string ToStringForInput(object obj, Type type)
{
if (type == null || obj == null)
return null;
if (type == typeof(string))
return obj as string;
if (type.IsEnum)
{
return Enum.IsDefined(type, obj)
? Enum.GetName(type, obj)
: obj.ToString();
}
try
{
if (customTypes.ContainsKey(type.FullName))
{
return customTypesToString[type.FullName].Invoke(obj);
}
else if (formattedTypes.Contains(type))
{
return ReflectionUtility.GetMethodInfo(type, "ToString", new Type[] { typeof(string), typeof(IFormatProvider) })
.Invoke(obj, new object[] { NumberFormatString, CultureInfo.CurrentCulture })
as string;
}
else
return obj.ToString();
}
catch (Exception ex)
{
ExplorerCore.LogWarning($"Exception formatting object for input: {ex}");
return null;
}
}
private static readonly Dictionary<string, string> typeInputExamples = new Dictionary<string, string>();
public static string GetExampleInput(Type type)
{
if (!typeInputExamples.ContainsKey(type.AssemblyQualifiedName))
{
try
{
if (type.IsEnum)
typeInputExamples.Add(type.AssemblyQualifiedName, Enum.GetNames(type).First());
else
{
var instance = Activator.CreateInstance(type);
typeInputExamples.Add(type.AssemblyQualifiedName, ToStringForInput(instance, type));
}
}
catch (Exception ex)
{
ExplorerCore.LogWarning("Exception generating default instance for example input for '" + type.FullName + "'");
ExplorerCore.Log(ex);
return "";
}
}
return typeInputExamples[type.AssemblyQualifiedName];
}
#region Custom parse methods
internal delegate object ParseMethod(string input);
private static readonly Dictionary<string, ParseMethod> customTypes = new Dictionary<string, ParseMethod>
{
{ typeof(Vector2).FullName, TryParseVector2 },
{ typeof(Vector3).FullName, TryParseVector3 },
{ typeof(Vector4).FullName, TryParseVector4 },
{ typeof(Quaternion).FullName, TryParseQuaternion },
{ typeof(Rect).FullName, TryParseRect },
{ typeof(Color).FullName, TryParseColor },
{ typeof(Color32).FullName, TryParseColor32 },
{ typeof(LayerMask).FullName, TryParseLayerMask },
};
internal delegate string ToStringMethod(object obj);
private static readonly Dictionary<string, ToStringMethod> customTypesToString = new Dictionary<string, ToStringMethod>
{
{ typeof(Vector2).FullName, Vector2ToString },
{ typeof(Vector3).FullName, Vector3ToString },
{ typeof(Vector4).FullName, Vector4ToString },
{ typeof(Quaternion).FullName, QuaternionToString },
{ typeof(Rect).FullName, RectToString },
{ typeof(Color).FullName, ColorToString },
{ typeof(Color32).FullName, Color32ToString },
{ typeof(LayerMask).FullName, LayerMaskToString },
};
// Vector2
public static object TryParseVector2(string input)
{
Vector2 vector = default;
var split = input.Split(' ');
vector.x = float.Parse(split[0].Trim(), CultureInfo.CurrentCulture);
vector.y = float.Parse(split[1].Trim(), CultureInfo.CurrentCulture);
return vector;
}
public static string Vector2ToString(object obj)
{
if (!(obj is Vector2 vector))
return null;
return FormatDecimalSequence(vector.x, vector.y);
}
// Vector3
public static object TryParseVector3(string input)
{
Vector3 vector = default;
var split = input.Split(' ');
vector.x = float.Parse(split[0].Trim(), CultureInfo.CurrentCulture);
vector.y = float.Parse(split[1].Trim(), CultureInfo.CurrentCulture);
vector.z = float.Parse(split[2].Trim(), CultureInfo.CurrentCulture);
return vector;
}
public static string Vector3ToString(object obj)
{
if (!(obj is Vector3 vector))
return null;
return FormatDecimalSequence(vector.x, vector.y, vector.z);
}
// Vector4
public static object TryParseVector4(string input)
{
Vector4 vector = default;
var split = input.Split(' ');
vector.x = float.Parse(split[0].Trim(), CultureInfo.CurrentCulture);
vector.y = float.Parse(split[1].Trim(), CultureInfo.CurrentCulture);
vector.z = float.Parse(split[2].Trim(), CultureInfo.CurrentCulture);
vector.w = float.Parse(split[3].Trim(), CultureInfo.CurrentCulture);
return vector;
}
public static string Vector4ToString(object obj)
{
if (!(obj is Vector4 vector))
return null;
return FormatDecimalSequence(vector.x, vector.y, vector.z, vector.w);
}
// Quaternion
public static object TryParseQuaternion(string input)
{
Vector3 vector = default;
var split = input.Split(' ');
if (split.Length == 4)
{
Quaternion quat = default;
quat.x = float.Parse(split[0].Trim(), CultureInfo.CurrentCulture);
quat.y = float.Parse(split[1].Trim(), CultureInfo.CurrentCulture);
quat.z = float.Parse(split[2].Trim(), CultureInfo.CurrentCulture);
quat.w = float.Parse(split[3].Trim(), CultureInfo.CurrentCulture);
return quat;
}
else
{
vector.x = float.Parse(split[0].Trim(), CultureInfo.CurrentCulture);
vector.y = float.Parse(split[1].Trim(), CultureInfo.CurrentCulture);
vector.z = float.Parse(split[2].Trim(), CultureInfo.CurrentCulture);
return Quaternion.Euler(vector);
}
}
public static string QuaternionToString(object obj)
{
if (!(obj is Quaternion quaternion))
return null;
Vector3 vector = quaternion.eulerAngles;
return FormatDecimalSequence(vector.x, vector.y, vector.z);
}
// Rect
public static object TryParseRect(string input)
{
Rect rect = default;
var split = input.Split(' ');
rect.x = float.Parse(split[0].Trim(), CultureInfo.CurrentCulture);
rect.y = float.Parse(split[1].Trim(), CultureInfo.CurrentCulture);
rect.width = float.Parse(split[2].Trim(), CultureInfo.CurrentCulture);
rect.height = float.Parse(split[3].Trim(), CultureInfo.CurrentCulture);
return rect;
}
public static string RectToString(object obj)
{
if (!(obj is Rect rect))
return null;
return FormatDecimalSequence(rect.x, rect.y, rect.width, rect.height);
}
// Color
public static object TryParseColor(string input)
{
Color color = default;
var split = input.Split(' ');
color.r = float.Parse(split[0].Trim(), CultureInfo.CurrentCulture);
color.g = float.Parse(split[1].Trim(), CultureInfo.CurrentCulture);
color.b = float.Parse(split[2].Trim(), CultureInfo.CurrentCulture);
if (split.Length > 3)
color.a = float.Parse(split[3].Trim(), CultureInfo.CurrentCulture);
else
color.a = 1;
return color;
}
public static string ColorToString(object obj)
{
if (!(obj is Color color))
return null;
return FormatDecimalSequence(color.r, color.g, color.b, color.a);
}
// Color32
public static object TryParseColor32(string input)
{
Color32 color = default;
var split = input.Split(' ');
color.r = byte.Parse(split[0].Trim(), CultureInfo.CurrentCulture);
color.g = byte.Parse(split[1].Trim(), CultureInfo.CurrentCulture);
color.b = byte.Parse(split[2].Trim(), CultureInfo.CurrentCulture);
if (split.Length > 3)
color.a = byte.Parse(split[3].Trim(), CultureInfo.CurrentCulture);
else
color.a = 255;
return color;
}
public static string Color32ToString(object obj)
{
if (!(obj is Color32 color))
return null;
// ints, this is fine
return $"{color.r} {color.g} {color.b} {color.a}";
}
// Layermask (Int32)
public static object TryParseLayerMask(string input)
{
return (LayerMask)int.Parse(input);
}
public static string LayerMaskToString(object obj)
{
if (!(obj is LayerMask mask))
return null;
return mask.value.ToString();
}
#endregion
}
}

View File

@ -1,294 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using UnityEngine;
using UnityExplorer.Core.Runtime;
namespace UnityExplorer
{
/// <summary>
/// Syntax-highlights a member's signature, by either the Type name or a Type and Member together.
/// </summary>
public static class SignatureHighlighter
{
public const string NAMESPACE = "#a8a8a8";
public const string CONST = "#92c470";
public const string CLASS_STATIC = "#3a8d71";
public const string CLASS_INSTANCE = "#2df7b2";
public const string STRUCT = "#0fba3a";
public const string INTERFACE = "#9b9b82";
public const string FIELD_STATIC = "#8d8dc6";
public const string FIELD_INSTANCE = "#c266ff";
public const string METHOD_STATIC = "#b55b02";
public const string METHOD_INSTANCE = "#ff8000";
public const string PROP_STATIC = "#588075";
public const string PROP_INSTANCE = "#55a38e";
public const string LOCAL_ARG = "#a6e9e9";
internal const string ARRAY_TOKEN = "[]";
internal const string OPEN_COLOR = "<color=";
internal const string CLOSE_COLOR = "</color>";
internal const string OPEN_ITALIC = "<i>";
internal const string CLOSE_ITALIC = "</i>";
public static readonly Color StringOrange = new Color(0.83f, 0.61f, 0.52f);
public static readonly Color EnumGreen = new Color(0.57f, 0.76f, 0.43f);
public static readonly Color KeywordBlue = new Color(0.3f, 0.61f, 0.83f);
public static readonly string keywordBlueHex = KeywordBlue.ToHex();
public static readonly Color NumberGreen = new Color(0.71f, 0.8f, 0.65f);
internal static string GetClassColor(Type type)
{
if (type.IsAbstract && type.IsSealed)
return CLASS_STATIC;
else if (type.IsEnum || type.IsGenericParameter)
return CONST;
else if (type.IsValueType)
return STRUCT;
else if (type.IsInterface)
return INTERFACE;
else
return CLASS_INSTANCE;
}
//private static readonly StringBuilder syntaxBuilder = new StringBuilder(2156);
private static bool GetNamespace(Type type, out string ns)
{
var ret = !string.IsNullOrEmpty(ns = type.Namespace?.Trim());
return ret;
}
public static string Parse(Type type, bool includeNamespace, MemberInfo memberInfo = null)
{
if (type == null)
throw new ArgumentNullException("type");
var syntaxBuilder = new StringBuilder();
// Namespace
bool isGeneric = type.IsGenericParameter || (type.HasElementType && type.GetElementType().IsGenericParameter);
if (!isGeneric)
{
if (includeNamespace && GetNamespace(type, out string ns))
syntaxBuilder.Append(OPEN_COLOR).Append(NAMESPACE).Append('>').Append(ns).Append(CLOSE_COLOR).Append('.');
// Declaring type
var declaring = type.DeclaringType;
while (declaring != null)
{
syntaxBuilder.Append(HighlightType(declaring));
syntaxBuilder.Append('.');
declaring = declaring.DeclaringType;
}
}
// Highlight the type name
syntaxBuilder.Append(HighlightType(type));
// If memberInfo, highlight the member info
if (memberInfo != null)
{
syntaxBuilder.Append('.');
int start = syntaxBuilder.Length - 1;
syntaxBuilder.Append(OPEN_COLOR)
.Append(GetMemberInfoColor(memberInfo, out bool isStatic))
.Append('>')
.Append(memberInfo.Name)
.Append(CLOSE_COLOR);
if (isStatic)
{
syntaxBuilder.Insert(start, OPEN_ITALIC);
syntaxBuilder.Append(CLOSE_ITALIC);
}
if (memberInfo is MethodInfo method)
{
var args = method.GetGenericArguments();
if (args.Length > 0)
syntaxBuilder.Append('<').Append(ParseGenericArgs(args, true)).Append('>');
}
}
return syntaxBuilder.ToString();
}
private static readonly Dictionary<string, string> typeToRichType = new Dictionary<string, string>();
private static bool EndsWith(this StringBuilder sb, string _string)
{
int len = _string.Length;
if (sb.Length < len)
return false;
int stringpos = 0;
for (int i = sb.Length - len; i < sb.Length; i++, stringpos++)
{
if (sb[i] != _string[stringpos])
return false;
}
return true;
}
private static string HighlightType(Type type)
{
string key = type.ToString();
if (typeToRichType.ContainsKey(key))
return typeToRichType[key];
var sb = new StringBuilder(type.Name);
bool isArray = false;
if (sb.EndsWith(ARRAY_TOKEN))
{
isArray = true;
sb.Remove(sb.Length - 2, 2);
type = type.GetElementType();
}
if (type.IsGenericParameter || (type.HasElementType && type.GetElementType().IsGenericParameter))
{
sb.Insert(0, $"<color={CONST}>");
sb.Append(CLOSE_COLOR);
}
else
{
var args = type.GetGenericArguments();
if (args.Length > 0)
{
// remove the `N from the end of the type name
// this could actually be >9 in some cases, so get the length of the length string and use that.
// eg, if it was "List`15", we would remove the ending 3 chars
int suffixLen = 1 + args.Length.ToString().Length;
// make sure the typename actually has expected "`N" format.
if (sb[sb.Length - suffixLen] == '`')
sb.Remove(sb.Length - suffixLen, suffixLen);
}
// highlight the base name itself
// do this after removing the `N suffix, so only the name itself is in the color tags.
sb.Insert(0, $"{OPEN_COLOR}{GetClassColor(type)}>");
sb.Append(CLOSE_COLOR);
// parse the generic args, if any
if (args.Length > 0)
{
sb.Append('<').Append(ParseGenericArgs(args)).Append('>');
}
}
if (isArray)
sb.Append('[').Append(']');
var ret = sb.ToString();
typeToRichType.Add(key, ret);
return ret;
}
public static string ParseGenericArgs(Type[] args, bool isGenericParams = false)
{
if (args.Length < 1)
return string.Empty;
var sb = new StringBuilder();
for (int i = 0; i < args.Length; i++)
{
if (i > 0)
sb.Append(',').Append(' ');
if (isGenericParams)
{
sb.Append(OPEN_COLOR).Append(CONST).Append('>').Append(args[i].Name).Append(CLOSE_COLOR);
continue;
}
sb.Append(HighlightType(args[i]));
}
return sb.ToString();
}
public static string GetMemberInfoColor(MemberTypes type)
{
switch (type)
{
case MemberTypes.Method: return METHOD_INSTANCE;
case MemberTypes.Property: return PROP_INSTANCE;
case MemberTypes.Field: return FIELD_INSTANCE;
default: return null;
}
}
public static string GetMemberInfoColor(MemberInfo memberInfo, out bool isStatic)
{
isStatic = false;
if (memberInfo is FieldInfo fi)
{
if (fi.IsStatic)
{
isStatic = true;
return FIELD_STATIC;
}
return FIELD_INSTANCE;
}
else if (memberInfo is MethodInfo mi)
{
if (mi.IsStatic)
{
isStatic = true;
return METHOD_STATIC;
}
return METHOD_INSTANCE;
}
else if (memberInfo is PropertyInfo pi)
{
if (pi.GetAccessors(true)[0].IsStatic)
{
isStatic = true;
return PROP_STATIC;
}
return PROP_INSTANCE;
}
//else if (memberInfo is EventInfo ei)
//{
// if (ei.GetAddMethod().IsStatic)
// {
// isStatic = true;
// return EVENT_STATIC;
// }
// return EVENT_INSTANCE;
//}
throw new NotImplementedException(memberInfo.GetType().Name + " is not supported");
}
}
}

View File

@ -1,170 +0,0 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityExplorer.Core.Runtime;
namespace UnityExplorer
{
public static class ToStringUtility
{
internal static Dictionary<string, MethodInfo> toStringMethods = new Dictionary<string, MethodInfo>();
private const string nullString = "<color=grey>null</color>";
private const string nullUnknown = nullString + " (?)";
private const string destroyedString = "<color=red>Destroyed</color>";
private const string untitledString = "<i><color=grey>untitled</color></i>";
private const string eventSystemNamespace = "UnityEngine.EventSystem";
public static string PruneString(string s, int chars = 200, int lines = 5)
{
if (string.IsNullOrEmpty(s))
return s;
var sb = new StringBuilder(Math.Max(chars, s.Length));
int newlines = 0;
for (int i = 0; i < s.Length; i++)
{
if (newlines >= lines || i >= chars)
{
sb.Append("...");
break;
}
char c = s[i];
if (c == '\r' || c == '\n')
newlines++;
sb.Append(c);
}
return sb.ToString();
}
public static string ToStringWithType(object value, Type fallbackType, bool includeNamespace = true)
{
if (value.IsNullOrDestroyed() && fallbackType == null)
return nullUnknown;
Type type = value?.GetActualType() ?? fallbackType;
string richType = SignatureHighlighter.Parse(type, includeNamespace);
var sb = new StringBuilder();
if (value.IsNullOrDestroyed())
{
if (value == null)
{
sb.Append(nullString);
AppendRichType(sb, richType);
return sb.ToString();
}
else // destroyed unity object
{
sb.Append(destroyedString);
AppendRichType(sb, richType);
return sb.ToString();
}
}
if (value is UnityEngine.Object obj)
{
if (string.IsNullOrEmpty(obj.name))
sb.Append(untitledString);
else
{
sb.Append('"');
sb.Append(PruneString(obj.name, 50, 1));
sb.Append('"');
}
AppendRichType(sb, richType);
}
else if (type.FullName.StartsWith(eventSystemNamespace))
{
// UnityEngine.EventSystem classes can have some obnoxious ToString results with rich text.
sb.Append(richType);
}
else
{
var toString = ToString(value);
if (type.IsGenericType
|| toString == type.FullName
|| toString == $"{type.FullName} {type.FullName}"
|| toString == $"Il2Cpp{type.FullName}" || type.FullName == $"Il2Cpp{toString}")
{
sb.Append(richType);
}
else // the ToString contains some actual implementation, use that value.
{
sb.Append(PruneString(toString, 200, 5));
AppendRichType(sb, richType);
}
}
return sb.ToString();
}
private static void AppendRichType(StringBuilder sb, string richType)
{
sb.Append(' ');
sb.Append('(');
sb.Append(richType);
sb.Append(')');
}
private static string ToString(object value)
{
if (value.IsNullOrDestroyed())
{
if (value == null)
return nullString;
else // destroyed unity object
return destroyedString;
}
var type = value.GetActualType();
// Find and cache the ToString method for this Type, if haven't already.
if (!toStringMethods.ContainsKey(type.AssemblyQualifiedName))
{
var toStringMethod = type.GetMethod("ToString", ArgumentUtility.EmptyTypes);
toStringMethods.Add(type.AssemblyQualifiedName, toStringMethod);
}
// Invoke the ToString method on the object
value = value.TryCast(type);
string toString;
try
{
toString = (string)toStringMethods[type.AssemblyQualifiedName].Invoke(value, ArgumentUtility.EmptyArgs);
}
catch (Exception ex)
{
toString = ex.ReflectionExToString();
}
toString = ReflectionUtility.ProcessTypeInString(type, toString);
#if CPP
if (value is Il2CppSystem.Type cppType)
{
var monoType = Il2CppReflection.GetUnhollowedType(cppType);
if (monoType != null)
toString = ReflectionUtility.ProcessTypeInString(monoType, toString);
}
#endif
return toString;
}
}
}

View File

@ -1,126 +0,0 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Text;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.UI;
using Object = UnityEngine.Object;
namespace UnityExplorer
{
public static class UnityHelpers
{
// Time helpers, can't use Time.time since timeScale will affect it.
// default 10ms (one frame at 100fps)
public static bool OccuredEarlierThanDefault(this float time)
{
return Time.realtimeSinceStartup - 0.01f >= time;
}
public static bool OccuredEarlierThan(this float time, float secondsAgo)
{
return Time.realtimeSinceStartup - secondsAgo >= time;
}
/// <summary>
/// Check if an object is null, and if it's a UnityEngine.Object then also check if it was destroyed.
/// </summary>
public static bool IsNullOrDestroyed(this object obj, bool suppressWarning = true)
{
try
{
if (obj == null)
{
if (!suppressWarning)
ExplorerCore.LogWarning("The target instance is null!");
return true;
}
else if (obj is Object unityObj && !unityObj)
{
if (!suppressWarning)
ExplorerCore.LogWarning("The target UnityEngine.Object was destroyed!");
return true;
}
return false;
}
catch
{
return true;
}
}
/// <summary>
/// Get the full Transform heirarchy path for this provided Transform.
/// </summary>
public static string GetTransformPath(this Transform transform, bool includeSelf = false)
{
var sb = new StringBuilder();
if (includeSelf)
sb.Append(transform.name);
while (transform.parent)
{
transform = transform.parent;
sb.Insert(0, '/');
sb.Insert(0, transform.name);
}
return sb.ToString();
}
/// <summary>
/// Converts Color to 6-digit RGB hex code (without # symbol). Eg, RGBA(1,0,0,1) -> FF0000
/// </summary>
public static string ToHex(this Color color)
{
byte r = (byte)Mathf.Clamp(Mathf.RoundToInt(color.r * 255f), 0, 255);
byte g = (byte)Mathf.Clamp(Mathf.RoundToInt(color.g * 255f), 0, 255);
byte b = (byte)Mathf.Clamp(Mathf.RoundToInt(color.b * 255f), 0, 255);
return $"{r:X2}{g:X2}{b:X2}";
}
/// <summary>
/// Assumes the string is a 6-digit RGB Hex color code (with optional leading #) which it will parse into a UnityEngine.Color.
/// Eg, FF0000 -> RGBA(1,0,0,1)
/// </summary>
public static Color ToColor(this string _string)
{
_string = _string.Replace("#", "");
if (_string.Length != 6)
return Color.magenta;
var r = byte.Parse(_string.Substring(0, 2), NumberStyles.HexNumber);
var g = byte.Parse(_string.Substring(2, 2), NumberStyles.HexNumber);
var b = byte.Parse(_string.Substring(4, 2), NumberStyles.HexNumber);
var color = new Color
{
r = (float)(r / (decimal)255),
g = (float)(g / (decimal)255),
b = (float)(b / (decimal)255),
a = 1
};
return color;
}
private static PropertyInfo onEndEdit;
public static UnityEvent<string> GetOnEndEdit(this InputField _this)
{
if (onEndEdit == null)
onEndEdit = typeof(InputField).GetProperty("onEndEdit")
?? throw new Exception("Could not get InputField.onEndEdit property!");
return onEndEdit.GetValue(_this, null).TryCast<UnityEvent<string>>();
}
}
}

View File

@ -1,19 +1,15 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using UnityEngine;
using UnityEngine.UI;
using UnityExplorer.Core;
using UnityExplorer.Core.Config;
using UnityExplorer.Core.Input;
using UnityExplorer.Core.Runtime;
using UnityExplorer.Tests;
using UnityExplorer.UI;
using UnityExplorer.Inspectors;
using UnityExplorer.ObjectExplorer;
using UnityExplorer.UI.Panels;
using UnityExplorer.Core.Runtime;
using UniverseLib.Input;
namespace UnityExplorer
{
@ -25,7 +21,6 @@ namespace UnityExplorer
public const string GUID = "com.sinai.unityexplorer";
public static IExplorerLoader Loader { get; private set; }
public static RuntimeContext Context { get; internal set; }
public static HarmonyLib.Harmony Harmony { get; } = new HarmonyLib.Harmony(GUID);
@ -43,38 +38,31 @@ namespace UnityExplorer
Log($"{NAME} {VERSION} initializing...");
ExplorerBehaviour.Setup();
if (!Directory.Exists(Loader.ExplorerFolder))
Directory.CreateDirectory(Loader.ExplorerFolder);
ConfigManager.Init(Loader.ConfigHandler);
ReflectionUtility.Init();
RuntimeProvider.Init();
RuntimeHelper.Init();
ExplorerBehaviour.Setup();
UniverseLib.Universe.Init(ConfigManager.Startup_Delay_Time.Value, LateInit, Log, new UniverseLib.Config.UUConfig
{
Disable_EventSystem_Override = ConfigManager.Disable_EventSystem_Override.Value,
Force_Unlock_Mouse = ConfigManager.Force_Unlock_Mouse.Value,
Unhollowed_Modules_Folder = loader.UnhollowedModulesFolder
});
SceneHandler.Init();
InputManager.Init();
RuntimeProvider.Instance.StartCoroutine(SetupCoroutine());
Log($"Finished core setup, waiting for UI setup...");
Log($"Finished core setup, waiting for late setup...");
}
// Do a delayed setup so that objects aren't destroyed instantly.
// This can happen for a multitude of reasons.
// Default delay is 1 second which is usually enough.
private static IEnumerator SetupCoroutine()
private static void LateInit()
{
yield return null;
float prevRealTime = Time.realtimeSinceStartup;
float delay = ConfigManager.Startup_Delay_Time.Value;
while (delay > 0)
{
float diff = Math.Max(Time.deltaTime, Time.realtimeSinceStartup - prevRealTime);
delay -= diff;
prevRealTime = Time.realtimeSinceStartup;
yield return null;
}
Log($"Setting up late core features...");
SceneHandler.Init();
Log($"Creating UI...");
@ -88,19 +76,11 @@ namespace UnityExplorer
/// </summary>
public static void Update()
{
RuntimeProvider.Instance.Update();
UIManager.Update();
}
public static void FixedUpdate()
{
RuntimeProvider.Instance.ProcessFixedUpdate();
}
public static void OnPostRender()
{
RuntimeProvider.Instance.ProcessOnPostRender();
// check master toggle
if (InputManager.GetKeyDown(ConfigManager.Master_Toggle.Value))
UIManager.ShowMenu = !UIManager.ShowMenu;
}
#region LOGGING

View File

@ -6,6 +6,8 @@ using UnityEngine;
using UnityEngine.UI;
using UnityExplorer.UI;
using UnityExplorer.UI.Widgets;
using UniverseLib.UI;
using UniverseLib.UI.Widgets;
namespace UnityExplorer.Hooks
{

View File

@ -4,8 +4,8 @@ using System.Linq;
using System.Text;
using UnityEngine;
using UnityEngine.UI;
using UnityExplorer.UI;
using UnityExplorer.UI.Widgets;
using UniverseLib.UI;
using UniverseLib.UI.Widgets;
namespace UnityExplorer.Hooks
{

View File

@ -7,6 +7,7 @@ using System.Text;
using HarmonyLib;
using Mono.CSharp;
using UnityExplorer.CSConsole;
using UniverseLib;
namespace UnityExplorer.Hooks
{

View File

@ -6,10 +6,13 @@ using System.Reflection;
using System.Text;
using HarmonyLib;
using UnityEngine;
using UnityExplorer.Core.Runtime;
using UnityExplorer.CSConsole;
using UnityExplorer.UI;
using UnityExplorer.UI.Panels;
using UnityExplorer.UI.Widgets;
using UniverseLib;
using UniverseLib.UI.Widgets;
namespace UnityExplorer.Hooks
{
@ -106,7 +109,7 @@ namespace UnityExplorer.Hooks
currentAddEligableMethods.Clear();
foreach (var method in type.GetMethods(ReflectionUtility.FLAGS))
{
if (method.IsGenericMethod /* || method.IsAbstract */ || ReflectionUtility.IsBlacklisted(method))
if (method.IsGenericMethod /* || method.IsAbstract */ || RuntimeHelper.IsBlacklisted(method))
continue;
currentAddEligableMethods.Add(method);
filteredEligableMethods.Add(method);

View File

@ -5,12 +5,15 @@ using System.Linq;
using System.Text;
using UnityEngine;
using UnityEngine.UI;
using UnityExplorer.Core.Input;
using UniverseLib.Input;
using UnityExplorer.UI;
using UnityExplorer.UI.Models;
using UniverseLib.UI.Models;
using UnityExplorer.UI.Panels;
using UnityExplorer.UI.Widgets;
using UnityExplorer.UI.Widgets.AutoComplete;
using UniverseLib.UI.Widgets;
using UniverseLib.UI;
using UniverseLib;
namespace UnityExplorer.Inspectors
{

View File

@ -6,6 +6,8 @@ using UnityEngine;
using UnityEngine.UI;
using UnityExplorer.UI;
using UnityExplorer.UI.Widgets;
using UniverseLib.UI;
using UniverseLib.UI.Widgets;
namespace UnityExplorer.Inspectors
{

View File

@ -4,6 +4,8 @@ using System.Linq;
using System.Text;
using UnityEngine;
using UnityExplorer.UI.Widgets;
using UniverseLib;
using UniverseLib.UI.Widgets;
namespace UnityExplorer.Inspectors
{

View File

@ -4,8 +4,10 @@ using System.Linq;
using System.Text;
using UnityEngine;
using UnityEngine.UI;
using UnityExplorer.Core.Input;
using UniverseLib.Input;
using UnityExplorer.UI;
using UniverseLib.UI;
using UniverseLib;
namespace UnityExplorer.Inspectors
{

View File

@ -6,11 +6,13 @@ using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
using UnityExplorer.Core;
using UnityExplorer.Core.Input;
using UniverseLib.Input;
using UnityExplorer.Core.Runtime;
using UnityExplorer.Inspectors.MouseInspectors;
using UnityExplorer.UI;
using UnityExplorer.UI.Panels;
using UniverseLib;
using UniverseLib.UI;
namespace UnityExplorer.Inspectors
{
@ -163,7 +165,7 @@ namespace UnityExplorer.Inspectors
mousePos.y -= 10;
// calculate and set our UI position
var inversePos = UIManager.CanvasRoot.transform.InverseTransformPoint(mousePos);
var inversePos = UIManager.UIRoot.transform.InverseTransformPoint(mousePos);
UIRoot.transform.localPosition = new Vector3(inversePos.x, inversePos.y, 0);
}
@ -181,7 +183,7 @@ namespace UnityExplorer.Inspectors
{
// hide title bar
this.titleBar.SetActive(false);
this.UIRoot.transform.SetParent(UIManager.CanvasRoot.transform, false);
this.UIRoot.transform.SetParent(UIManager.UIRoot.transform, false);
var inspectContent = UIFactory.CreateVerticalGroup(this.content, "InspectContent", true, true, true, true, 3, new Vector4(2, 2, 2, 2));
UIFactory.SetLayoutElement(inspectContent, flexibleWidth: 9999, flexibleHeight: 9999);

View File

@ -5,8 +5,9 @@ using System.Text;
using UnityEngine;
using UnityEngine.UI;
using UnityExplorer.UI;
using UnityExplorer.UI.Models;
using UniverseLib.UI.Models;
using UnityExplorer.UI.Panels;
using UniverseLib.UI;
namespace UnityExplorer.Inspectors
{

View File

@ -7,8 +7,10 @@ using UnityEngine.UI;
using UnityExplorer.UI;
using UnityExplorer.CacheObject;
using UnityExplorer.Inspectors;
using UnityExplorer.UI.Models;
using UniverseLib.UI.Models;
using UnityExplorer.UI.Panels;
using UniverseLib;
using UniverseLib.UI;
namespace UnityExplorer
{

View File

@ -5,8 +5,10 @@ using System.Text;
using UnityEngine;
using UnityEngine.UI;
using UnityExplorer.UI;
using UnityExplorer.UI.Models;
using UniverseLib.UI.Models;
using UnityExplorer.UI.Widgets;
using UniverseLib.UI;
using UniverseLib;
namespace UnityExplorer.Inspectors
{

View File

@ -7,6 +7,7 @@ using UnityEngine.EventSystems;
using UnityEngine.UI;
using UnityExplorer.UI;
using UnityExplorer.UI.Panels;
using UniverseLib;
namespace UnityExplorer.Inspectors.MouseInspectors
{

View File

@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
using UniverseLib;
namespace UnityExplorer.Inspectors.MouseInspectors
{

View File

@ -15,6 +15,10 @@ using UnityExplorer.CacheObject.Views;
using UnityExplorer.UI.Panels;
using UnityExplorer.UI.Widgets;
using UnityExplorer.UI;
using UniverseLib.UI.Widgets;
using UniverseLib.UI;
using UniverseLib;
using UniverseLib.Runtime;
namespace UnityExplorer.Inspectors
{

View File

@ -11,7 +11,7 @@ using UnityEngine;
using UnityEngine.EventSystems;
using UnityExplorer.Core;
using UnityExplorer.Core.Config;
using UnityExplorer.Core.Input;
using UniverseLib.Input;
using UnityExplorer.Loader.BIE;
#if CPP
using BepInEx.IL2CPP;

View File

@ -7,7 +7,7 @@ using UnityEngine;
using UnityExplorer.Core.Config;
using UnityExplorer.Loader.STANDALONE;
using UnityEngine.EventSystems;
using UnityExplorer.Core.Input;
using UniverseLib.Input;
using UnityExplorer.Core;
#if CPP
using UnhollowerRuntimeLib;

View File

@ -5,10 +5,13 @@ using System.Text;
using UnityEngine;
using UnityEngine.UI;
using UnityExplorer.UI;
using UnityExplorer.UI.Models;
using UniverseLib.UI.Models;
using UnityExplorer.UI.Panels;
using UnityExplorer.UI.Widgets;
using UnityExplorer.UI.Widgets.AutoComplete;
using UniverseLib.UI.Widgets;
using UniverseLib.UI;
using UniverseLib;
namespace UnityExplorer.ObjectExplorer
{

View File

@ -9,9 +9,11 @@ using UnityEngine.SceneManagement;
using UnityEngine.UI;
using UnityExplorer.Core;
using UnityExplorer.UI;
using UnityExplorer.UI.Models;
using UniverseLib.UI.Models;
using UnityExplorer.UI.Panels;
using UnityExplorer.UI.Widgets;
using UniverseLib.UI;
using UniverseLib;
namespace UnityExplorer.ObjectExplorer
{

View File

@ -5,6 +5,7 @@ using System.Linq;
using System.Text;
using UnityEngine;
using UnityEngine.SceneManagement;
using UniverseLib;
namespace UnityExplorer.ObjectExplorer
{

View File

@ -7,6 +7,7 @@ using UnityEngine;
using UnityEngine.SceneManagement;
using UnityExplorer.Core;
using UnityExplorer.Core.Runtime;
using UniverseLib;
namespace UnityExplorer.ObjectExplorer
{
@ -101,7 +102,7 @@ namespace UnityExplorer.ObjectExplorer
if (go)
{
// hide unityexplorer objects
if (go.transform.root.name == "ExplorerCanvas")
if (go.transform.root.name == "UniverseLibCanvas")
continue;
if (shouldFilterGOs)

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -1,27 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine.UI;
using UnityExplorer.UI.Models;
namespace UnityExplorer.UI
{
// A simple helper class to handle a button's OnClick more effectively.
public class ButtonRef
{
public Action OnClick;
public Button Component { get; }
public Text ButtonText { get; }
public ButtonRef(Button button)
{
this.Component = button;
this.ButtonText = button.GetComponentInChildren<Text>();
button.onClick.AddListener(() => { OnClick?.Invoke(); });
}
}
}

View File

@ -1,69 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
using UnityEngine.UI;
using UnityExplorer.UI.Models;
namespace UnityExplorer.UI
{
public class InputFieldRef : UIModel
{
public static readonly HashSet<InputFieldRef> inputsPendingUpdate = new HashSet<InputFieldRef>();
public static void UpdateInstances()
{
if (inputsPendingUpdate.Any())
{
var array = inputsPendingUpdate.ToArray();
for (int i = array.Length - 1; i >= 0; i--)
{
var entry = array[i];
LayoutRebuilder.MarkLayoutForRebuild(entry.Rect);
entry.OnValueChanged?.Invoke(entry.Component.text);
}
inputsPendingUpdate.Clear();
}
}
public InputFieldRef(InputField component)
{
this.Component = component;
Rect = component.GetComponent<RectTransform>();
PlaceholderText = component.placeholder.TryCast<Text>();
component.onValueChanged.AddListener(OnInputChanged);
}
public event Action<string> OnValueChanged;
public InputField Component;
public Text PlaceholderText;
public RectTransform Rect;
public string Text
{
get => Component.text;
set => Component.text = value;
}
public TextGenerator TextGenerator => Component.cachedInputTextGenerator;
public bool ReachedMaxVerts => TextGenerator.vertexCount >= UIManager.MAX_TEXT_VERTS;
private void OnInputChanged(string value)
{
if (!inputsPendingUpdate.Contains(this))
inputsPendingUpdate.Add(this);
}
public override GameObject UIRoot => Component.gameObject;
public override void ConstructUI(GameObject parent)
{
throw new NotImplementedException();
}
}
}

View File

@ -1,58 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
namespace UnityExplorer.UI.Models
{
public abstract class UIBehaviourModel : UIModel
{
private static readonly List<UIBehaviourModel> Instances = new List<UIBehaviourModel>();
public static void UpdateInstances()
{
if (!Instances.Any())
return;
try
{
for (int i = Instances.Count - 1; i >= 0; i--)
{
var instance = Instances[i];
if (instance == null || !instance.UIRoot)
{
Instances.RemoveAt(i);
continue;
}
if (instance.Enabled)
instance.Update();
}
}
catch (Exception ex)
{
ExplorerCore.Log(ex);
}
}
public UIBehaviourModel()
{
Instances.Add(this);
}
/// <summary>
/// Default empty method, override and implement if NeedsUpdateTick is true.
/// </summary>
public virtual void Update()
{
}
public override void Destroy()
{
if (Instances.Contains(this))
Instances.Remove(this);
base.Destroy();
}
}
}

View File

@ -1,42 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
namespace UnityExplorer.UI.Models
{
public abstract class UIModel
{
public abstract GameObject UIRoot { get; }
public bool Enabled
{
get => UIRoot && UIRoot.activeInHierarchy;
set
{
if (!UIRoot || Enabled == value)
return;
UIRoot.SetActive(value);
OnToggleEnabled?.Invoke(value);
}
}
public event Action<bool> OnToggleEnabled;
public abstract void ConstructUI(GameObject parent);
public virtual void Toggle() => SetActive(!Enabled);
public virtual void SetActive(bool active)
{
UIRoot?.SetActive(active);
}
public virtual void Destroy()
{
if (UIRoot)
GameObject.Destroy(UIRoot);
}
}
}

View File

@ -9,6 +9,9 @@ using UnityEngine.UI;
using UnityExplorer.Core.Config;
using UnityExplorer.CSConsole;
using UnityExplorer.UI.Widgets;
using UniverseLib;
using UniverseLib.UI;
using UniverseLib.UI.Widgets;
namespace UnityExplorer.UI.Panels
{
@ -39,8 +42,8 @@ namespace UnityExplorer.UI.Panels
private void InvokeOnValueChanged(string value)
{
if (value.Length == UIManager.MAX_INPUTFIELD_CHARS)
ExplorerCore.LogWarning($"Reached maximum InputField character length! ({UIManager.MAX_INPUTFIELD_CHARS})");
if (value.Length == UniversalUI.MAX_INPUTFIELD_CHARS)
ExplorerCore.LogWarning($"Reached maximum InputField character length! ({UniversalUI.MAX_INPUTFIELD_CHARS})");
OnInputChanged?.Invoke(value);
}
@ -148,7 +151,7 @@ namespace UnityExplorer.UI.Panels
UIFactory.SetLayoutGroup<VerticalLayoutGroup>(linesHolder, true, true, true, true);
LineNumberText = UIFactory.CreateLabel(linesHolder, "LineNumbers", "1", TextAnchor.UpperCenter, Color.grey, fontSize: 16);
LineNumberText.font = UIManager.ConsoleFont;
LineNumberText.font = UniversalUI.ConsoleFont;
// input field
@ -192,9 +195,9 @@ namespace UnityExplorer.UI.Panels
HighlightText.fontSize = fontSize;
// Set fonts
InputText.font = UIManager.ConsoleFont;
Input.PlaceholderText.font = UIManager.ConsoleFont;
HighlightText.font = UIManager.ConsoleFont;
InputText.font = UniversalUI.ConsoleFont;
Input.PlaceholderText.font = UniversalUI.ConsoleFont;
HighlightText.font = UniversalUI.ConsoleFont;
RuntimeProvider.Instance.StartCoroutine(DelayedLayoutSetup());
}

View File

@ -8,6 +8,8 @@ using UnityExplorer.Core.Config;
using UnityExplorer.Hooks;
using UnityExplorer.UI.Widgets;
using UnityExplorer.UI.Widgets.AutoComplete;
using UniverseLib.UI;
using UniverseLib.UI.Widgets;
namespace UnityExplorer.UI.Panels
{
@ -185,9 +187,9 @@ namespace UnityExplorer.UI.Panels
EditorHighlightText.fontSize = fontSize;
// Set fonts
EditorInputText.font = UIManager.ConsoleFont;
EditorInput.PlaceholderText.font = UIManager.ConsoleFont;
EditorHighlightText.font = UIManager.ConsoleFont;
EditorInputText.font = UniversalUI.ConsoleFont;
EditorInput.PlaceholderText.font = UniversalUI.ConsoleFont;
EditorHighlightText.font = UniversalUI.ConsoleFont;
editorPanel.SetActive(false);
}

View File

@ -7,6 +7,7 @@ using UnityEngine;
using UnityEngine.UI;
using UnityExplorer.Core.Config;
using UnityExplorer.Inspectors;
using UniverseLib.UI;
namespace UnityExplorer.UI.Panels
{

View File

@ -8,6 +8,9 @@ using UnityEngine;
using UnityEngine.UI;
using UnityExplorer.Core.Config;
using UnityExplorer.UI.Widgets;
using UniverseLib;
using UniverseLib.UI;
using UniverseLib.UI.Widgets;
namespace UnityExplorer.UI.Panels
{
@ -233,8 +236,8 @@ namespace UnityExplorer.UI.Panels
Input.Component.readOnly = true;
Input.Component.textComponent.supportRichText = true;
Input.Component.lineType = InputField.LineType.MultiLineNewline;
Input.Component.textComponent.font = UIManager.ConsoleFont;
Input.PlaceholderText.font = UIManager.ConsoleFont;
Input.Component.textComponent.font = UniversalUI.ConsoleFont;
Input.PlaceholderText.font = UniversalUI.ConsoleFont;
return UIRoot;
}

View File

@ -10,9 +10,11 @@ using UnityEngine.SceneManagement;
using UnityEngine.UI;
using UnityExplorer.Core;
using UnityExplorer.Core.Config;
using UnityExplorer.UI.Models;
using UniverseLib.UI.Models;
using UnityExplorer.ObjectExplorer;
using UnityExplorer.UI.Widgets;
using UniverseLib.UI;
using UniverseLib;
namespace UnityExplorer.UI.Panels
{
@ -42,7 +44,7 @@ namespace UnityExplorer.UI.Panels
content.SetActive(true);
var button = tabButtons[tabIndex];
RuntimeProvider.Instance.SetColorBlock(button.Component, UIManager.enabledButtonColor, UIManager.enabledButtonColor * 1.2f);
RuntimeProvider.Instance.SetColorBlock(button.Component, UniversalUI.enabledButtonColor, UniversalUI.enabledButtonColor * 1.2f);
SelectedTab = tabIndex;
SaveToConfigManager();
@ -51,7 +53,7 @@ namespace UnityExplorer.UI.Panels
private void DisableTab(int tabIndex)
{
tabPages[tabIndex].SetActive(false);
RuntimeProvider.Instance.SetColorBlock(tabButtons[tabIndex].Component, UIManager.disabledButtonColor, UIManager.disabledButtonColor * 1.2f);
RuntimeProvider.Instance.SetColorBlock(tabButtons[tabIndex].Component, UniversalUI.disabledButtonColor, UniversalUI.disabledButtonColor * 1.2f);
}
public override void Update()

View File

@ -8,6 +8,8 @@ using UnityExplorer.Core.Config;
using UnityExplorer.CacheObject;
using UnityExplorer.CacheObject.Views;
using UnityExplorer.UI.Widgets;
using UniverseLib.UI.Widgets;
using UniverseLib.UI;
namespace UnityExplorer.UI.Panels
{

View File

@ -5,9 +5,11 @@ using System.IO;
using System.Linq;
using UnityEngine;
using UnityEngine.UI;
using UnityExplorer.Core.Input;
using UnityExplorer.UI.Models;
using UniverseLib.Input;
using UniverseLib.UI.Models;
using UnityExplorer.UI.Widgets.AutoComplete;
using UniverseLib.UI;
using UniverseLib;
namespace UnityExplorer.UI.Panels
{
@ -472,7 +474,7 @@ namespace UnityExplorer.UI.Panels
{
try
{
var text = UIFactory.CreateLabel(UIManager.CanvasRoot, "ResizeCursor", "↔", TextAnchor.MiddleCenter, Color.white, true, 35);
var text = UIFactory.CreateLabel(UIManager.UIRoot, "ResizeCursor", "↔", TextAnchor.MiddleCenter, Color.white, true, 35);
s_resizeCursorObj = text.gameObject;
RectTransform rect = s_resizeCursorObj.GetComponent<RectTransform>();

View File

@ -6,9 +6,11 @@ using System.Text;
using UnityEngine;
using UnityEngine.UI;
using UnityExplorer.Core.Config;
using UnityExplorer.Core.Input;
using UnityExplorer.UI.Models;
using UniverseLib.Input;
using UnityExplorer.UI.Widgets;
using UniverseLib.UI.Models;
using UniverseLib.UI;
using UniverseLib;
namespace UnityExplorer.UI.Panels
{
@ -35,6 +37,7 @@ namespace UnityExplorer.UI.Panels
int count = UIManager.PanelHolder.transform.childCount;
var mousePos = InputManager.MousePosition;
bool clickedInAny = false;
for (int i = count - 1; i >= 0; i--)
{
// make sure this is a real recognized panel
@ -106,7 +109,7 @@ namespace UnityExplorer.UI.Panels
public override void SetActive(bool active)
{
if (this.Enabled.Equals(active))
if (this.Enabled == active)
return;
base.SetActive(active);
@ -116,7 +119,7 @@ namespace UnityExplorer.UI.Panels
if (NavButtonWanted)
{
var color = active ? UIManager.enabledButtonColor : UIManager.disabledButtonColor;
var color = active ? UniversalUI.enabledButtonColor : UniversalUI.disabledButtonColor;
RuntimeProvider.Instance.SetColorBlock(NavButton.Component, color, color * 1.2f);
}
@ -232,7 +235,7 @@ namespace UnityExplorer.UI.Panels
UIFactory.SetLayoutGroup<HorizontalLayoutGroup>(navBtn, false, true, true, true, 0, 0, 0, 5, 5, TextAnchor.MiddleCenter);
UIFactory.SetLayoutElement(navBtn, minWidth: 80);
RuntimeProvider.Instance.SetColorBlock(NavButton.Component, UIManager.disabledButtonColor, UIManager.disabledButtonColor * 1.2f);
RuntimeProvider.Instance.SetColorBlock(NavButton.Component, UniversalUI.disabledButtonColor, UniversalUI.disabledButtonColor * 1.2f);
NavButton.OnClick += () => { UIManager.TogglePanel(PanelType); };
var txtObj = navBtn.transform.Find("Text").gameObject;
@ -240,7 +243,7 @@ namespace UnityExplorer.UI.Panels
}
// create core canvas
uiRoot = UIFactory.CreatePanel(Name, out GameObject panelContent);
uiRoot = UIFactory.CreatePanel(Name, UIManager.PanelHolder, out GameObject panelContent);
Rect = this.uiRoot.GetComponent<RectTransform>();
UIFactory.SetLayoutGroup<VerticalLayoutGroup>(this.uiRoot, false, false, true, true, 0, 2, 2, 2, 2, TextAnchor.UpperLeft);

View File

@ -5,6 +5,9 @@ using System.Text;
using UnityEngine;
using UnityExplorer.Inspectors.MouseInspectors;
using UnityExplorer.UI.Widgets;
using UniverseLib;
using UniverseLib.UI;
using UniverseLib.UI.Widgets;
namespace UnityExplorer.UI.Panels
{

View File

@ -1,129 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
namespace UnityExplorer.UI
{
public interface IPooledObject
{
GameObject UIRoot { get; set; }
float DefaultHeight { get; }
GameObject CreateContent(GameObject parent);
}
public abstract class Pool
{
protected static readonly Dictionary<Type, Pool> pools = new Dictionary<Type, Pool>();
public static Pool GetPool(Type type)
{
if (!pools.TryGetValue(type, out Pool pool))
pool = CreatePool(type);
return pool;
}
protected static Pool CreatePool(Type type)
{
Pool pool = (Pool)Activator.CreateInstance(typeof(Pool<>).MakeGenericType(new[] { type }));
pools.Add(type, pool);
return pool;
}
public static IPooledObject Borrow(Type type)
{
return GetPool(type).TryBorrow();
}
public static void Return(Type type, IPooledObject obj)
{
GetPool(type).TryReturn(obj);
}
protected abstract IPooledObject TryBorrow();
protected abstract void TryReturn(IPooledObject obj);
}
public class Pool<T> : Pool where T : IPooledObject
{
public static Pool<T> GetPool() => (Pool<T>)GetPool(typeof(T));
public static T Borrow()
{
return GetPool().BorrowObject();
}
public static void Return(T obj)
{
GetPool().ReturnObject(obj);
}
// Instance
public static Pool<T> Instance
{
get => s_instance ?? (Pool<T>)CreatePool(typeof(T));
}
private static Pool<T> s_instance;
public Pool()
{
s_instance = this;
//ExplorerCore.LogWarning("Creating Pool<" + typeof(T).Name + ">");
InactiveHolder = new GameObject($"PoolHolder_{typeof(T).Name}");
InactiveHolder.transform.parent = UIManager.PoolHolder.transform;
InactiveHolder.hideFlags |= HideFlags.HideAndDontSave;
InactiveHolder.SetActive(false);
// Create an instance (not content) to grab the default height
var obj = (T)Activator.CreateInstance(typeof(T));
DefaultHeight = obj.DefaultHeight;
}
public GameObject InactiveHolder { get; }
public float DefaultHeight { get; }
private readonly HashSet<T> available = new HashSet<T>();
private readonly HashSet<T> borrowed = new HashSet<T>();
public int AvailableCount => available.Count;
private void IncrementPool()
{
var obj = (T)Activator.CreateInstance(typeof(T));
obj.CreateContent(InactiveHolder);
available.Add(obj);
}
public T BorrowObject()
{
if (available.Count <= 0)
IncrementPool();
var obj = available.First();
available.Remove(obj);
borrowed.Add(obj);
return obj;
}
public void ReturnObject(T obj)
{
if (!borrowed.Contains(obj))
ExplorerCore.LogWarning($"Returning an item to object pool ({typeof(T).Name}) but the item didn't exist in the borrowed list?");
else
borrowed.Remove(obj);
available.Add(obj);
obj.UIRoot.transform.SetParent(InactiveHolder.transform, false);
}
protected override IPooledObject TryBorrow() => Borrow();
protected override void TryReturn(IPooledObject obj) => Return((T)obj);
}
}

View File

@ -1,970 +0,0 @@
using System;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.UI;
using UnityExplorer.Core.Config;
using UnityExplorer.Core.Runtime;
using UnityExplorer.UI.Models;
using UnityExplorer.UI.Widgets;
namespace UnityExplorer.UI
{
public static class UIFactory
{
#region Init, Core
internal static Vector2 _largeElementSize = new Vector2(100, 30);
internal static Vector2 _smallElementSize = new Vector2(25, 25);
internal static Color _defaultTextColor = Color.white;
public static GameObject CreateUIObject(string name, GameObject parent, Vector2 size = default)
{
if (!parent)
{
ExplorerCore.LogWarning($"Warning: Creating {name} but parent is null");
ExplorerCore.Log(Environment.StackTrace);
}
var obj = new GameObject(name)
{
layer = 5,
hideFlags = HideFlags.HideAndDontSave,
};
if (parent)
obj.transform.SetParent(parent.transform, false);
RectTransform rect = obj.AddComponent<RectTransform>();
rect.sizeDelta = size;
return obj;
}
internal static void SetDefaultTextValues(Text text)
{
text.color = _defaultTextColor;
text.font = UIManager.DefaultFont;
text.fontSize = 14;
}
internal static void SetDefaultSelectableColors(Selectable selectable)
{
RuntimeProvider.Instance.SetColorBlock(selectable, new Color(0.2f, 0.2f, 0.2f),
new Color(0.3f, 0.3f, 0.3f), new Color(0.15f, 0.15f, 0.15f));
}
#endregion
#region Default Layout Components
/// <summary>
/// Get and/or Add a LayoutElement component to the GameObject, and set any of the values on it.
/// </summary>
public static LayoutElement SetLayoutElement(GameObject gameObject, int? minWidth = null, int? minHeight = null,
int? flexibleWidth = null, int? flexibleHeight = null, int? preferredWidth = null, int? preferredHeight = null,
bool? ignoreLayout = null)
{
var layout = gameObject.GetComponent<LayoutElement>();
if (!layout)
layout = gameObject.AddComponent<LayoutElement>();
if (minWidth != null)
layout.minWidth = (int)minWidth;
if (minHeight != null)
layout.minHeight = (int)minHeight;
if (flexibleWidth != null)
layout.flexibleWidth = (int)flexibleWidth;
if (flexibleHeight != null)
layout.flexibleHeight = (int)flexibleHeight;
if (preferredWidth != null)
layout.preferredWidth = (int)preferredWidth;
if (preferredHeight != null)
layout.preferredHeight = (int)preferredHeight;
if (ignoreLayout != null)
layout.ignoreLayout = (bool)ignoreLayout;
return layout;
}
/// <summary>
/// Get and/or Add a HorizontalOrVerticalLayoutGroup (must pick one) to the GameObject, and set the values on it.
/// </summary>
public static T SetLayoutGroup<T>(GameObject gameObject, bool? forceWidth = null, bool? forceHeight = null,
bool? childControlWidth = null, bool? childControlHeight = null, int? spacing = null, int? padTop = null,
int? padBottom = null, int? padLeft = null, int? padRight = null, TextAnchor? childAlignment = null)
where T : HorizontalOrVerticalLayoutGroup
{
var group = gameObject.GetComponent<T>();
if (!group)
group = gameObject.AddComponent<T>();
return SetLayoutGroup(group, forceWidth, forceHeight, childControlWidth, childControlHeight, spacing, padTop,
padBottom, padLeft, padRight, childAlignment);
}
/// <summary>
/// Set the values on a HorizontalOrVerticalLayoutGroup.
/// </summary>
public static T SetLayoutGroup<T>(T group, bool? forceWidth = null, bool? forceHeight = null,
bool? childControlWidth = null, bool? childControlHeight = null, int? spacing = null, int? padTop = null,
int? padBottom = null, int? padLeft = null, int? padRight = null, TextAnchor? childAlignment = null)
where T : HorizontalOrVerticalLayoutGroup
{
if (forceWidth != null)
group.childForceExpandWidth = (bool)forceWidth;
if (forceHeight != null)
group.childForceExpandHeight = (bool)forceHeight;
if (childControlWidth != null)
group.SetChildControlWidth((bool)childControlWidth);
if (childControlHeight != null)
group.SetChildControlHeight((bool)childControlHeight);
if (spacing != null)
group.spacing = (int)spacing;
if (padTop != null)
group.padding.top = (int)padTop;
if (padBottom != null)
group.padding.bottom = (int)padBottom;
if (padLeft != null)
group.padding.left = (int)padLeft;
if (padRight != null)
group.padding.right = (int)padRight;
if (childAlignment != null)
group.childAlignment = (TextAnchor)childAlignment;
return group;
}
/// <summary>
/// Create a Panel on the UI Canvas.
/// </summary>
public static GameObject CreatePanel(string name, out GameObject contentHolder, Color? bgColor = null)
{
var panelObj = CreateUIObject(name, UIManager.PanelHolder);
SetLayoutGroup<VerticalLayoutGroup>(panelObj, true, true, true, true);
var rect = panelObj.GetComponent<RectTransform>();
rect.anchorMin = Vector2.zero;
rect.anchorMax = Vector2.one;
rect.anchoredPosition = Vector2.zero;
rect.sizeDelta = Vector2.zero;
var maskImg = panelObj.AddComponent<Image>();
maskImg.color = Color.black;
panelObj.AddComponent<Mask>().showMaskGraphic = true;
contentHolder = CreateUIObject("Content", panelObj);
Image bgImage = contentHolder.AddComponent<Image>();
bgImage.type = Image.Type.Filled;
if (bgColor == null)
bgImage.color = new Color(0.07f, 0.07f, 0.07f);
else
bgImage.color = (Color)bgColor;
SetLayoutGroup<VerticalLayoutGroup>(contentHolder, true, true, true, true, 3, 3, 3, 3, 3);
return panelObj;
}
/// <summary>
/// Create a VerticalLayoutGroup object.
/// </summary>
public static GameObject CreateVerticalGroup(GameObject parent, string name, bool forceWidth, bool forceHeight,
bool childControlWidth, bool childControlHeight, int spacing = 0, Vector4 padding = default, Color bgColor = default,
TextAnchor? childAlignment = null)
{
GameObject groupObj = CreateUIObject(name, parent);
SetLayoutGroup<VerticalLayoutGroup>(groupObj, forceWidth, forceHeight, childControlWidth, childControlHeight,
spacing, (int)padding.x, (int)padding.y, (int)padding.z, (int)padding.w, childAlignment);
Image image = groupObj.AddComponent<Image>();
image.color = bgColor == default
? new Color(0.17f, 0.17f, 0.17f)
: bgColor;
return groupObj;
}
/// <summary>
/// Create a HorizontalLayoutGroup object.
/// </summary>
public static GameObject CreateHorizontalGroup(GameObject parent, string name, bool forceExpandWidth, bool forceExpandHeight,
bool childControlWidth, bool childControlHeight, int spacing = 0, Vector4 padding = default, Color bgColor = default,
TextAnchor? childAlignment = null)
{
GameObject groupObj = CreateUIObject(name, parent);
SetLayoutGroup<HorizontalLayoutGroup>(groupObj, forceExpandWidth, forceExpandHeight, childControlWidth, childControlHeight,
spacing, (int)padding.x, (int)padding.y, (int)padding.z, (int)padding.w, childAlignment);
Image image = groupObj.AddComponent<Image>();
image.color = bgColor == default
? new Color(0.17f, 0.17f, 0.17f)
: bgColor;
return groupObj;
}
/// <summary>
/// Create a GridLayoutGroup object.
/// </summary>
public static GameObject CreateGridGroup(GameObject parent, string name, Vector2 cellSize, Vector2 spacing, Color bgColor = default)
{
var groupObj = CreateUIObject(name, parent);
GridLayoutGroup gridGroup = groupObj.AddComponent<GridLayoutGroup>();
gridGroup.childAlignment = TextAnchor.UpperLeft;
gridGroup.cellSize = cellSize;
gridGroup.spacing = spacing;
Image image = groupObj.AddComponent<Image>();
image.color = bgColor == default
? new Color(0.17f, 0.17f, 0.17f)
: bgColor;
return groupObj;
}
#endregion
#region Default Control Elements
/// <summary>
/// Create a Label object.
/// </summary>
public static Text CreateLabel(GameObject parent, string name, string text, TextAnchor alignment,
Color color = default, bool supportRichText = true, int fontSize = 14)
{
var obj = CreateUIObject(name, parent);
var textComp = obj.AddComponent<Text>();
SetDefaultTextValues(textComp);
textComp.text = text;
textComp.color = color == default ? _defaultTextColor : color;
textComp.supportRichText = supportRichText;
textComp.alignment = alignment;
textComp.fontSize = fontSize;
return textComp;
}
public static ButtonRef CreateButton(GameObject parent, string name, string text, Color? normalColor = null)
{
var colors = new ColorBlock();
normalColor = normalColor ?? new Color(0.25f, 0.25f, 0.25f);
var btn = CreateButton(parent, name, text, colors);
RuntimeProvider.Instance.SetColorBlock(btn.Component, normalColor, normalColor * 1.2f, normalColor * 0.7f);
return btn;
}
public static ButtonRef CreateButton(GameObject parent, string name, string text, ColorBlock colors)
{
GameObject buttonObj = CreateUIObject(name, parent, _smallElementSize);
var textObj = CreateUIObject("Text", buttonObj);
Image image = buttonObj.AddComponent<Image>();
image.type = Image.Type.Sliced;
image.color = new Color(1, 1, 1, 1);
var button = buttonObj.AddComponent<Button>();
SetDefaultSelectableColors(button);
colors.colorMultiplier = 1;
RuntimeProvider.Instance.SetColorBlock(button, colors);
Text textComp = textObj.AddComponent<Text>();
textComp.text = text;
SetDefaultTextValues(textComp);
textComp.alignment = TextAnchor.MiddleCenter;
RectTransform rect = textObj.GetComponent<RectTransform>();
rect.anchorMin = Vector2.zero;
rect.anchorMax = Vector2.one;
rect.sizeDelta = Vector2.zero;
SetButtonDeselectListener(button);
return new ButtonRef(button);
}
public static void SetButtonDeselectListener(Button button)
{
button.onClick.AddListener(() =>
{
button.OnDeselect(null);
});
}
/// <summary>
/// Create a Slider control.
/// </summary>
public static GameObject CreateSlider(GameObject parent, string name, out Slider slider)
{
GameObject sliderObj = CreateUIObject(name, parent, _smallElementSize);
GameObject bgObj = CreateUIObject("Background", sliderObj);
GameObject fillAreaObj = CreateUIObject("Fill Area", sliderObj);
GameObject fillObj = CreateUIObject("Fill", fillAreaObj);
GameObject handleSlideAreaObj = CreateUIObject("Handle Slide Area", sliderObj);
GameObject handleObj = CreateUIObject("Handle", handleSlideAreaObj);
Image bgImage = bgObj.AddComponent<Image>();
bgImage.type = Image.Type.Sliced;
bgImage.color = new Color(0.15f, 0.15f, 0.15f, 1.0f);
RectTransform bgRect = bgObj.GetComponent<RectTransform>();
bgRect.anchorMin = new Vector2(0f, 0.25f);
bgRect.anchorMax = new Vector2(1f, 0.75f);
bgRect.sizeDelta = new Vector2(0f, 0f);
RectTransform fillAreaRect = fillAreaObj.GetComponent<RectTransform>();
fillAreaRect.anchorMin = new Vector2(0f, 0.25f);
fillAreaRect.anchorMax = new Vector2(1f, 0.75f);
fillAreaRect.anchoredPosition = new Vector2(-5f, 0f);
fillAreaRect.sizeDelta = new Vector2(-20f, 0f);
Image fillImage = fillObj.AddComponent<Image>();
fillImage.type = Image.Type.Sliced;
fillImage.color = new Color(0.3f, 0.3f, 0.3f, 1.0f);
fillObj.GetComponent<RectTransform>().sizeDelta = new Vector2(10f, 0f);
RectTransform handleSlideRect = handleSlideAreaObj.GetComponent<RectTransform>();
handleSlideRect.sizeDelta = new Vector2(-20f, 0f);
handleSlideRect.anchorMin = new Vector2(0f, 0f);
handleSlideRect.anchorMax = new Vector2(1f, 1f);
Image handleImage = handleObj.AddComponent<Image>();
handleImage.color = new Color(0.5f, 0.5f, 0.5f, 1.0f);
handleObj.GetComponent<RectTransform>().sizeDelta = new Vector2(20f, 0f);
slider = sliderObj.AddComponent<Slider>();
slider.fillRect = fillObj.GetComponent<RectTransform>();
slider.handleRect = handleObj.GetComponent<RectTransform>();
slider.targetGraphic = handleImage;
slider.direction = Slider.Direction.LeftToRight;
RuntimeProvider.Instance.SetColorBlock(slider, new Color(0.4f, 0.4f, 0.4f),
new Color(0.55f, 0.55f, 0.55f), new Color(0.3f, 0.3f, 0.3f));
return sliderObj;
}
/// <summary>
/// Create a Scrollbar control.
/// </summary>
public static GameObject CreateScrollbar(GameObject parent, string name, out Scrollbar scrollbar)
{
GameObject scrollObj = CreateUIObject(name, parent, _smallElementSize);
GameObject slideAreaObj = CreateUIObject("Sliding Area", scrollObj);
GameObject handleObj = CreateUIObject("Handle", slideAreaObj);
Image scrollImage = scrollObj.AddComponent<Image>();
scrollImage.type = Image.Type.Sliced;
scrollImage.color = new Color(0.1f, 0.1f, 0.1f);
Image handleImage = handleObj.AddComponent<Image>();
handleImage.type = Image.Type.Sliced;
handleImage.color = new Color(0.4f, 0.4f, 0.4f);
RectTransform slideAreaRect = slideAreaObj.GetComponent<RectTransform>();
slideAreaRect.sizeDelta = new Vector2(-20f, -20f);
slideAreaRect.anchorMin = Vector2.zero;
slideAreaRect.anchorMax = Vector2.one;
RectTransform handleRect = handleObj.GetComponent<RectTransform>();
handleRect.sizeDelta = new Vector2(20f, 20f);
scrollbar = scrollObj.AddComponent<Scrollbar>();
scrollbar.handleRect = handleRect;
scrollbar.targetGraphic = handleImage;
SetDefaultSelectableColors(scrollbar);
return scrollObj;
}
/// <summary>
/// Create a Toggle control.
/// </summary>
public static GameObject CreateToggle(GameObject parent, string name, out Toggle toggle, out Text text, Color bgColor = default,
int checkWidth = 20, int checkHeight = 20)
{
// Main obj
GameObject toggleObj = CreateUIObject(name, parent, _smallElementSize);
SetLayoutGroup<HorizontalLayoutGroup>(toggleObj, false, false, true, true, 5, 0,0,0,0, childAlignment: TextAnchor.MiddleLeft);
toggle = toggleObj.AddComponent<Toggle>();
toggle.isOn = true;
SetDefaultSelectableColors(toggle);
// need a second reference so we can use it inside the lambda, since 'toggle' is an out var.
Toggle t2 = toggle;
toggle.onValueChanged.AddListener((bool _) => { t2.OnDeselect(null); });
// Check mark background
GameObject checkBgObj = CreateUIObject("Background", toggleObj);
Image bgImage = checkBgObj.AddComponent<Image>();
bgImage.color = bgColor == default ? new Color(0.04f, 0.04f, 0.04f, 0.75f) : bgColor;
SetLayoutGroup<HorizontalLayoutGroup>(checkBgObj, true, true, true, true, 0, 2, 2, 2, 2);
SetLayoutElement(checkBgObj, minWidth: checkWidth, flexibleWidth: 0, minHeight: checkHeight, flexibleHeight: 0);
// Check mark image
GameObject checkMarkObj = CreateUIObject("Checkmark", checkBgObj);
Image checkImage = checkMarkObj.AddComponent<Image>();
checkImage.color = new Color(0.8f, 1, 0.8f, 0.3f);
// Label
GameObject labelObj = CreateUIObject("Label", toggleObj);
text = labelObj.AddComponent<Text>();
text.text = "";
text.alignment = TextAnchor.MiddleLeft;
SetDefaultTextValues(text);
SetLayoutElement(labelObj, minWidth: 0, flexibleWidth: 0, minHeight: checkHeight, flexibleHeight: 0);
// References
toggle.graphic = checkImage;
toggle.targetGraphic = bgImage;
return toggleObj;
}
/// <summary>
/// Create a standard InputField control.
/// </summary>
public static InputFieldRef CreateInputField(GameObject parent, string name, string placeHolderText)
{
GameObject mainObj = CreateUIObject(name, parent);
Image mainImage = mainObj.AddComponent<Image>();
mainImage.type = Image.Type.Sliced;
mainImage.color = new Color(0, 0, 0, 0.5f);
var inputField = mainObj.AddComponent<InputField>();
Navigation nav = inputField.navigation;
nav.mode = Navigation.Mode.None;
inputField.navigation = nav;
inputField.lineType = InputField.LineType.SingleLine;
inputField.interactable = true;
inputField.transition = Selectable.Transition.ColorTint;
inputField.targetGraphic = mainImage;
RuntimeProvider.Instance.SetColorBlock(inputField, new Color(1, 1, 1, 1),
new Color(0.95f, 0.95f, 0.95f, 1.0f), new Color(0.78f, 0.78f, 0.78f, 1.0f));
GameObject textArea = CreateUIObject("TextArea", mainObj);
textArea.AddComponent<RectMask2D>();
RectTransform textAreaRect = textArea.GetComponent<RectTransform>();
textAreaRect.anchorMin = Vector2.zero;
textAreaRect.anchorMax = Vector2.one;
textAreaRect.offsetMin = Vector2.zero;
textAreaRect.offsetMax = Vector2.zero;
GameObject placeHolderObj = CreateUIObject("Placeholder", textArea);
Text placeholderText = placeHolderObj.AddComponent<Text>();
SetDefaultTextValues(placeholderText);
placeholderText.text = placeHolderText ?? "...";
placeholderText.color = new Color(0.5f, 0.5f, 0.5f, 1.0f);
placeholderText.horizontalOverflow = HorizontalWrapMode.Wrap;
placeholderText.alignment = TextAnchor.MiddleLeft;
placeholderText.fontSize = 14;
RectTransform placeHolderRect = placeHolderObj.GetComponent<RectTransform>();
placeHolderRect.anchorMin = Vector2.zero;
placeHolderRect.anchorMax = Vector2.one;
placeHolderRect.offsetMin = Vector2.zero;
placeHolderRect.offsetMax = Vector2.zero;
inputField.placeholder = placeholderText;
GameObject inputTextObj = CreateUIObject("Text", textArea);
Text inputText = inputTextObj.AddComponent<Text>();
SetDefaultTextValues(inputText);
inputText.text = "";
inputText.color = new Color(1f, 1f, 1f, 1f);
inputText.horizontalOverflow = HorizontalWrapMode.Wrap;
inputText.alignment = TextAnchor.MiddleLeft;
inputText.fontSize = 14;
RectTransform inputTextRect = inputTextObj.GetComponent<RectTransform>();
inputTextRect.anchorMin = Vector2.zero;
inputTextRect.anchorMax = Vector2.one;
inputTextRect.offsetMin = Vector2.zero;
inputTextRect.offsetMax = Vector2.zero;
inputField.textComponent = inputText;
inputField.characterLimit = UIManager.MAX_INPUTFIELD_CHARS;
return new InputFieldRef(inputField);
}
/// <summary>
/// Create a DropDown control.
/// </summary>
public static GameObject CreateDropdown(GameObject parent, out Dropdown dropdown, string defaultItemText, int itemFontSize,
Action<int> onValueChanged, string[] defaultOptions = null)
{
GameObject dropdownObj = CreateUIObject("Dropdown", parent, _largeElementSize);
GameObject labelObj = CreateUIObject("Label", dropdownObj);
GameObject arrowObj = CreateUIObject("Arrow", dropdownObj);
GameObject templateObj = CreateUIObject("Template", dropdownObj);
GameObject viewportObj = CreateUIObject("Viewport", templateObj);
GameObject contentObj = CreateUIObject("Content", viewportObj);
GameObject itemObj = CreateUIObject("Item", contentObj);
GameObject itemBgObj = CreateUIObject("Item Background", itemObj);
GameObject itemCheckObj = CreateUIObject("Item Checkmark", itemObj);
GameObject itemLabelObj = CreateUIObject("Item Label", itemObj);
GameObject scrollbarObj = CreateScrollbar(templateObj, "DropdownScroll", out Scrollbar scrollbar);
scrollbar.SetDirection(Scrollbar.Direction.BottomToTop, true);
RuntimeProvider.Instance.SetColorBlock(scrollbar, new Color(0.45f, 0.45f, 0.45f), new Color(0.6f, 0.6f, 0.6f), new Color(0.4f, 0.4f, 0.4f));
RectTransform scrollRectTransform = scrollbarObj.GetComponent<RectTransform>();
scrollRectTransform.anchorMin = Vector2.right;
scrollRectTransform.anchorMax = Vector2.one;
scrollRectTransform.pivot = Vector2.one;
scrollRectTransform.sizeDelta = new Vector2(scrollRectTransform.sizeDelta.x, 0f);
Text itemLabelText = itemLabelObj.AddComponent<Text>();
SetDefaultTextValues(itemLabelText);
itemLabelText.alignment = TextAnchor.MiddleLeft;
itemLabelText.text = defaultItemText;
itemLabelText.fontSize = itemFontSize;
var arrowText = arrowObj.AddComponent<Text>();
SetDefaultTextValues(arrowText);
arrowText.text = "▼";
var arrowRect = arrowObj.GetComponent<RectTransform>();
arrowRect.anchorMin = new Vector2(1f, 0.5f);
arrowRect.anchorMax = new Vector2(1f, 0.5f);
arrowRect.sizeDelta = new Vector2(20f, 20f);
arrowRect.anchoredPosition = new Vector2(-15f, 0f);
Image itemBgImage = itemBgObj.AddComponent<Image>();
itemBgImage.color = new Color(0.25f, 0.35f, 0.25f, 1.0f);
Toggle itemToggle = itemObj.AddComponent<Toggle>();
itemToggle.targetGraphic = itemBgImage;
itemToggle.isOn = true;
RuntimeProvider.Instance.SetColorBlock(itemToggle,
new Color(0.35f, 0.35f, 0.35f, 1.0f), new Color(0.25f, 0.55f, 0.25f, 1.0f));
itemToggle.onValueChanged.AddListener((bool val) => { itemToggle.OnDeselect(null); });
Image templateImage = templateObj.AddComponent<Image>();
templateImage.type = Image.Type.Sliced;
templateImage.color = Color.black;
var scrollRect = templateObj.AddComponent<ScrollRect>();
scrollRect.scrollSensitivity = 35;
scrollRect.content = contentObj.GetComponent<RectTransform>();
scrollRect.viewport = viewportObj.GetComponent<RectTransform>();
scrollRect.horizontal = false;
scrollRect.movementType = ScrollRect.MovementType.Clamped;
scrollRect.verticalScrollbar = scrollbar;
scrollRect.verticalScrollbarVisibility = ScrollRect.ScrollbarVisibility.AutoHideAndExpandViewport;
scrollRect.verticalScrollbarSpacing = -3f;
viewportObj.AddComponent<Mask>().showMaskGraphic = false;
Image viewportImage = viewportObj.AddComponent<Image>();
viewportImage.type = Image.Type.Sliced;
Text labelText = labelObj.AddComponent<Text>();
SetDefaultTextValues(labelText);
labelText.alignment = TextAnchor.MiddleLeft;
Image dropdownImage = dropdownObj.AddComponent<Image>();
dropdownImage.color = new Color(0.04f, 0.04f, 0.04f, 0.75f);
dropdownImage.type = Image.Type.Sliced;
dropdown = dropdownObj.AddComponent<Dropdown>();
dropdown.targetGraphic = dropdownImage;
dropdown.template = templateObj.GetComponent<RectTransform>();
dropdown.captionText = labelText;
dropdown.itemText = itemLabelText;
//itemLabelText.text = "DEFAULT";
dropdown.RefreshShownValue();
RectTransform labelRect = labelObj.GetComponent<RectTransform>();
labelRect.anchorMin = Vector2.zero;
labelRect.anchorMax = Vector2.one;
labelRect.offsetMin = new Vector2(10f, 2f);
labelRect.offsetMax = new Vector2(-28f, -2f);
RectTransform templateRect = templateObj.GetComponent<RectTransform>();
templateRect.anchorMin = new Vector2(0f, 0f);
templateRect.anchorMax = new Vector2(1f, 0f);
templateRect.pivot = new Vector2(0.5f, 1f);
templateRect.anchoredPosition = new Vector2(0f, 2f);
templateRect.sizeDelta = new Vector2(0f, 150f);
RectTransform viewportRect = viewportObj.GetComponent<RectTransform>();
viewportRect.anchorMin = new Vector2(0f, 0f);
viewportRect.anchorMax = new Vector2(1f, 1f);
viewportRect.sizeDelta = new Vector2(-18f, 0f);
viewportRect.pivot = new Vector2(0f, 1f);
RectTransform contentRect = contentObj.GetComponent<RectTransform>();
contentRect.anchorMin = new Vector2(0f, 1f);
contentRect.anchorMax = new Vector2(1f, 1f);
contentRect.pivot = new Vector2(0.5f, 1f);
contentRect.anchoredPosition = new Vector2(0f, 0f);
contentRect.sizeDelta = new Vector2(0f, 28f);
RectTransform itemRect = itemObj.GetComponent<RectTransform>();
itemRect.anchorMin = new Vector2(0f, 0.5f);
itemRect.anchorMax = new Vector2(1f, 0.5f);
itemRect.sizeDelta = new Vector2(0f, 25f);
RectTransform itemBgRect = itemBgObj.GetComponent<RectTransform>();
itemBgRect.anchorMin = Vector2.zero;
itemBgRect.anchorMax = Vector2.one;
itemBgRect.sizeDelta = Vector2.zero;
RectTransform itemLabelRect = itemLabelObj.GetComponent<RectTransform>();
itemLabelRect.anchorMin = Vector2.zero;
itemLabelRect.anchorMax = Vector2.one;
itemLabelRect.offsetMin = new Vector2(20f, 1f);
itemLabelRect.offsetMax = new Vector2(-10f, -2f);
templateObj.SetActive(false);
if (onValueChanged != null)
dropdown.onValueChanged.AddListener(onValueChanged);
if (defaultOptions != null)
{
foreach (var option in defaultOptions)
dropdown.options.Add(new Dropdown.OptionData(option));
}
return dropdownObj;
}
#endregion
#region Custom Scroll Components
/// <summary>
/// Create a ScrollPool for the <typeparamref name="T"/> ICell.
/// </summary>
public static ScrollPool<T> CreateScrollPool<T>(GameObject parent, string name, out GameObject uiRoot,
out GameObject content, Color? bgColor = null) where T : ICell
{
var mainObj = CreateUIObject(name, parent, new Vector2(1, 1));
mainObj.AddComponent<Image>().color = bgColor ?? new Color(0.12f, 0.12f, 0.12f);
SetLayoutGroup<HorizontalLayoutGroup>(mainObj, false, true, true, true);
GameObject viewportObj = CreateUIObject("Viewport", mainObj);
SetLayoutElement(viewportObj, flexibleWidth: 9999, flexibleHeight: 9999);
var viewportRect = viewportObj.GetComponent<RectTransform>();
viewportRect.anchorMin = Vector2.zero;
viewportRect.anchorMax = Vector2.one;
viewportRect.pivot = new Vector2(0.0f, 1.0f);
viewportRect.sizeDelta = new Vector2(0f, 0.0f);
viewportRect.offsetMax = new Vector2(-10.0f, 0.0f);
viewportObj.AddComponent<Image>().color = Color.white;
viewportObj.AddComponent<Mask>().showMaskGraphic = false;
content = CreateUIObject("Content", viewportObj);
var contentRect = content.GetComponent<RectTransform>();
contentRect.anchorMin = Vector2.zero;
contentRect.anchorMax = Vector2.one;
contentRect.pivot = new Vector2(0.5f, 1f);
contentRect.sizeDelta = new Vector2(0f, 0f);
contentRect.offsetMax = new Vector2(0f, 0f);
SetLayoutGroup<VerticalLayoutGroup>(content, true, false, true, true, 0, 2, 2, 2, 2, TextAnchor.UpperCenter);
content.AddComponent<ContentSizeFitter>().verticalFit = ContentSizeFitter.FitMode.PreferredSize;
var scrollRect = mainObj.AddComponent<ScrollRect>();
scrollRect.movementType = ScrollRect.MovementType.Clamped;
//scrollRect.inertia = false;
scrollRect.inertia = true;
scrollRect.elasticity = 0.125f;
scrollRect.scrollSensitivity = 25;
scrollRect.horizontal = false;
scrollRect.vertical = true;
scrollRect.viewport = viewportRect;
scrollRect.content = contentRect;
// Slider
var sliderContainer = CreateVerticalGroup(mainObj, "SliderContainer",
false, false, true, true, 0, default, new Color(0.05f, 0.05f, 0.05f));
SetLayoutElement(sliderContainer, minWidth: 25, flexibleWidth: 0, flexibleHeight: 9999);
sliderContainer.AddComponent<Mask>();
CreateSliderScrollbar(sliderContainer, out Slider slider);
RuntimeProvider.Instance.SetColorBlock(slider, disabled: new Color(0.1f, 0.1f, 0.1f));
// finalize and create ScrollPool
uiRoot = mainObj;
var scrollPool = new ScrollPool<T>(scrollRect);
return scrollPool;
}
/// <summary>
/// Create a SliderScrollbar, using a Slider to mimic a scrollbar.
/// </summary>
public static GameObject CreateSliderScrollbar(GameObject parent, out Slider slider)
{
GameObject mainObj = CreateUIObject("SliderScrollbar", parent, _smallElementSize);
mainObj.AddComponent<Mask>();
mainObj.AddComponent<Image>().color = Color.white;
GameObject bgImageObj = CreateUIObject("Background", mainObj);
GameObject handleSlideAreaObj = CreateUIObject("Handle Slide Area", mainObj);
GameObject handleObj = CreateUIObject("Handle", handleSlideAreaObj);
Image bgImage = bgImageObj.AddComponent<Image>();
bgImage.type = Image.Type.Sliced;
bgImage.color = new Color(0.05f, 0.05f, 0.05f, 1.0f);
bgImageObj.AddComponent<Mask>();
RectTransform bgRect = bgImageObj.GetComponent<RectTransform>();
bgRect.pivot = new Vector2(0, 1);
bgRect.anchorMin = Vector2.zero;
bgRect.anchorMax = Vector2.one;
bgRect.sizeDelta = Vector2.zero;
bgRect.offsetMax = new Vector2(0f, 0f);
RectTransform handleSlideRect = handleSlideAreaObj.GetComponent<RectTransform>();
handleSlideRect.anchorMin = Vector3.zero;
handleSlideRect.anchorMax = Vector3.one;
handleSlideRect.pivot = new Vector3(0.5f, 0.5f);
Image handleImage = handleObj.AddComponent<Image>();
handleImage.color = new Color(0.5f, 0.5f, 0.5f, 1.0f);
var handleRect = handleObj.GetComponent<RectTransform>();
handleRect.pivot = new Vector2(0.5f, 0.5f);
SetLayoutElement(handleObj, minWidth: 21, flexibleWidth: 0);
var sliderBarLayout = mainObj.AddComponent<LayoutElement>();
sliderBarLayout.minWidth = 25;
sliderBarLayout.flexibleWidth = 0;
sliderBarLayout.minHeight = 30;
sliderBarLayout.flexibleHeight = 9999;
slider = mainObj.AddComponent<Slider>();
slider.handleRect = handleObj.GetComponent<RectTransform>();
slider.targetGraphic = handleImage;
slider.direction = Slider.Direction.TopToBottom;
SetLayoutElement(mainObj, minWidth: 25, flexibleWidth: 0, flexibleHeight: 9999);
RuntimeProvider.Instance.SetColorBlock(slider,
new Color(0.4f, 0.4f, 0.4f),
new Color(0.5f, 0.5f, 0.5f),
new Color(0.3f, 0.3f, 0.3f),
new Color(0.5f, 0.5f, 0.5f));
return mainObj;
}
/// <summary>
/// Create a ScrollView and a SliderScrollbar for non-pooled content.
/// </summary>
public static GameObject CreateScrollView(GameObject parent, string name, out GameObject content, out AutoSliderScrollbar autoScrollbar,
Color color = default)
{
GameObject mainObj = CreateUIObject(name, parent);
var mainRect = mainObj.GetComponent<RectTransform>();
mainRect.anchorMin = Vector2.zero;
mainRect.anchorMax = Vector2.one;
Image mainImage = mainObj.AddComponent<Image>();
mainImage.type = Image.Type.Filled;
mainImage.color = (color == default) ? new Color(0.3f, 0.3f, 0.3f, 1f) : color;
GameObject viewportObj = CreateUIObject("Viewport", mainObj);
var viewportRect = viewportObj.GetComponent<RectTransform>();
viewportRect.anchorMin = Vector2.zero;
viewportRect.anchorMax = Vector2.one;
viewportRect.pivot = new Vector2(0.0f, 1.0f);
viewportRect.offsetMax = new Vector2(-28, 0);
viewportObj.AddComponent<Image>().color = Color.white;
viewportObj.AddComponent<Mask>().showMaskGraphic = false;
content = CreateUIObject("Content", viewportObj);
SetLayoutGroup<VerticalLayoutGroup>(content, true, false, true, true, childAlignment: TextAnchor.UpperLeft);
SetLayoutElement(content, flexibleHeight: 9999);
var contentRect = content.GetComponent<RectTransform>();
contentRect.anchorMin = Vector2.zero;
contentRect.anchorMax = Vector2.one;
contentRect.pivot = new Vector2(0.0f, 1.0f);
content.AddComponent<ContentSizeFitter>().verticalFit = ContentSizeFitter.FitMode.PreferredSize;
// Slider
GameObject scrollBarObj = CreateUIObject("AutoSliderScrollbar", mainObj);
var scrollBarRect = scrollBarObj.GetComponent<RectTransform>();
scrollBarRect.anchorMin = new Vector2(1, 0);
scrollBarRect.anchorMax = Vector2.one;
scrollBarRect.offsetMin = new Vector2(-25, 0);
SetLayoutGroup<VerticalLayoutGroup>(scrollBarObj, false, true, true, true);
scrollBarObj.AddComponent<Image>().color = Color.white;
scrollBarObj.AddComponent<Mask>().showMaskGraphic = false;
GameObject hiddenBar = CreateScrollbar(scrollBarObj, "HiddenScrollviewScroller", out var hiddenScrollbar);
hiddenScrollbar.SetDirection(Scrollbar.Direction.BottomToTop, true);
for (int i = 0; i < hiddenBar.transform.childCount; i++)
{
var child = hiddenBar.transform.GetChild(i);
child.gameObject.SetActive(false);
}
CreateSliderScrollbar(scrollBarObj, out Slider scrollSlider);
autoScrollbar = new AutoSliderScrollbar(hiddenScrollbar, scrollSlider, contentRect, viewportRect);
// Set up the ScrollRect component
var scrollRect = mainObj.AddComponent<ScrollRect>();
scrollRect.horizontal = false;
scrollRect.vertical = true;
scrollRect.verticalScrollbar = hiddenScrollbar;
scrollRect.movementType = ScrollRect.MovementType.Clamped;
scrollRect.scrollSensitivity = 35;
scrollRect.horizontalScrollbarVisibility = ScrollRect.ScrollbarVisibility.AutoHideAndExpandViewport;
scrollRect.verticalScrollbarVisibility = ScrollRect.ScrollbarVisibility.Permanent;
scrollRect.viewport = viewportRect;
scrollRect.content = contentRect;
return mainObj;
}
/// <summary>
/// Create a Scrollable Input Field control
/// </summary>
public static GameObject CreateScrollInputField(GameObject parent, string name, string placeHolderText, out InputFieldScroller inputScroll,
int fontSize = 14, Color color = default)
{
if (color == default)
color = new Color(0.12f, 0.12f, 0.12f);
GameObject mainObj = CreateUIObject(name, parent);
SetLayoutElement(mainObj, minWidth: 100, minHeight: 30, flexibleWidth: 5000, flexibleHeight: 5000);
SetLayoutGroup<HorizontalLayoutGroup>(mainObj, false, true, true, true, 2);
Image mainImage = mainObj.AddComponent<Image>();
mainImage.type = Image.Type.Filled;
mainImage.color = (color == default) ? new Color(0.3f, 0.3f, 0.3f, 1f) : color;
GameObject viewportObj = CreateUIObject("Viewport", mainObj);
SetLayoutElement(viewportObj, minWidth: 1, flexibleWidth: 9999, flexibleHeight: 9999);
var viewportRect = viewportObj.GetComponent<RectTransform>();
viewportRect.anchorMin = Vector2.zero;
viewportRect.anchorMax = Vector2.one;
viewportRect.pivot = new Vector2(0.0f, 1.0f);
viewportObj.AddComponent<Image>().color = Color.white;
viewportObj.AddComponent<Mask>().showMaskGraphic = false;
// Input Field
var inputField = CreateInputField(viewportObj, "InputField", placeHolderText);
var content = inputField.UIRoot;
var textComp = inputField.Component.textComponent;
textComp.alignment = TextAnchor.UpperLeft;
textComp.fontSize = fontSize;
textComp.horizontalOverflow = HorizontalWrapMode.Wrap;
inputField.Component.lineType = InputField.LineType.MultiLineNewline;
inputField.Component.targetGraphic.color = color;
inputField.PlaceholderText.alignment = TextAnchor.UpperLeft;
inputField.PlaceholderText.fontSize = fontSize;
inputField.PlaceholderText.horizontalOverflow = HorizontalWrapMode.Wrap;
//var content = CreateInputField(viewportObj, name, placeHolderText ?? "...", out InputField inputField, fontSize, 0);
SetLayoutElement(content, flexibleHeight: 9999, flexibleWidth: 9999);
var contentRect = content.GetComponent<RectTransform>();
contentRect.pivot = new Vector2(0, 1);
contentRect.anchorMin = new Vector2(0, 1);
contentRect.anchorMax = new Vector2(1, 1);
contentRect.offsetMin = new Vector2(2, 0);
contentRect.offsetMax = new Vector2(2, 0);
inputField.Component.lineType = InputField.LineType.MultiLineNewline;
inputField.Component.targetGraphic.color = color;
// Slider
GameObject scrollBarObj = CreateUIObject("AutoSliderScrollbar", mainObj);
SetLayoutGroup<VerticalLayoutGroup>(scrollBarObj, true, true, true, true);
SetLayoutElement(scrollBarObj, minWidth: 25, flexibleWidth: 0, flexibleHeight: 9999);
scrollBarObj.AddComponent<Image>().color = Color.white;
scrollBarObj.AddComponent<Mask>().showMaskGraphic = false;
GameObject hiddenBar = CreateScrollbar(scrollBarObj, "HiddenScrollviewScroller", out var hiddenScrollbar);
hiddenScrollbar.SetDirection(Scrollbar.Direction.BottomToTop, true);
for (int i = 0; i < hiddenBar.transform.childCount; i++)
{
var child = hiddenBar.transform.GetChild(i);
child.gameObject.SetActive(false);
}
CreateSliderScrollbar(scrollBarObj, out Slider scrollSlider);
// Set up the AutoSliderScrollbar module
var autoScroller = new AutoSliderScrollbar(hiddenScrollbar, scrollSlider, contentRect, viewportRect);
var sliderContainer = autoScroller.Slider.m_HandleContainerRect.gameObject;
SetLayoutElement(sliderContainer, minWidth: 25, flexibleWidth: 0, flexibleHeight: 9999);
sliderContainer.AddComponent<Mask>();
// Set up the InputFieldScroller module
inputScroll = new InputFieldScroller(autoScroller, inputField);
inputScroll.ProcessInputText();
// Set up the ScrollRect component
var scrollRect = mainObj.AddComponent<ScrollRect>();
scrollRect.horizontal = false;
scrollRect.vertical = true;
scrollRect.verticalScrollbar = hiddenScrollbar;
scrollRect.movementType = ScrollRect.MovementType.Clamped;
scrollRect.scrollSensitivity = 35;
scrollRect.horizontalScrollbarVisibility = ScrollRect.ScrollbarVisibility.AutoHideAndExpandViewport;
scrollRect.verticalScrollbarVisibility = ScrollRect.ScrollbarVisibility.Permanent;
scrollRect.viewport = viewportRect;
scrollRect.content = contentRect;
return mainObj;
}
#endregion
}
}

View File

@ -10,13 +10,15 @@ using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
using UnityExplorer.Core.Config;
using UnityExplorer.Core.Input;
using UnityExplorer.CSConsole;
using UnityExplorer.Inspectors;
using UnityExplorer.UI.Models;
using UnityExplorer.UI.Panels;
using UnityExplorer.UI.Widgets;
using UnityExplorer.UI.Widgets.AutoComplete;
using UniverseLib;
using UniverseLib.Input;
using UniverseLib.UI;
using UniverseLib.UI.Widgets;
namespace UnityExplorer.UI
{
@ -41,22 +43,16 @@ namespace UnityExplorer.UI
Bottom
}
public static bool Initializing { get; internal set; } = true;
public static VerticalAnchor NavbarAnchor = VerticalAnchor.Top;
public static GameObject CanvasRoot { get; private set; }
public static Canvas Canvas { get; private set; }
public static EventSystem EventSys { get; private set; }
public static bool Initializing { get; internal set; } = true;
private static UIBase uiBase;
public static GameObject UIRoot => uiBase?.RootObject;
internal static GameObject PoolHolder { get; private set; }
internal static GameObject PanelHolder { get; private set; }
private static readonly Dictionary<Panels, UIPanel> UIPanels = new Dictionary<Panels, UIPanel>();
internal static Font ConsoleFont { get; private set; }
internal static Font DefaultFont { get; private set; }
internal static Shader BackupShader { get; private set; }
public static RectTransform NavBarRect;
public static GameObject NavbarTabButtonHolder;
public static Dropdown MouseInspectDropdown;
@ -67,40 +63,25 @@ namespace UnityExplorer.UI
private static bool pauseButtonPausing;
private static float lastTimeScale;
// defaults
internal static readonly Color enabledButtonColor = new Color(0.2f, 0.4f, 0.28f);
internal static readonly Color disabledButtonColor = new Color(0.25f, 0.25f, 0.25f);
public const int MAX_INPUTFIELD_CHARS = 16000;
public const int MAX_TEXT_VERTS = 65000;
public static bool ShowMenu
{
get => s_showMenu;
get => uiBase != null && uiBase.Enabled;
set
{
if (s_showMenu == value || !CanvasRoot)
if (uiBase == null || !UIRoot || uiBase.Enabled == value)
return;
s_showMenu = value;
CanvasRoot.SetActive(value);
CursorUnlocker.UpdateCursorControl();
UniversalUI.SetUIActive(ExplorerCore.GUID, value);
}
}
public static bool s_showMenu = true;
// Initialization
internal static void InitUI()
{
LoadBundle();
uiBase = UniversalUI.RegisterUI(ExplorerCore.GUID, Update);
CreateRootCanvas();
// Global UI Pool Holder
PoolHolder = new GameObject("PoolHolder");
PoolHolder.transform.parent = CanvasRoot.transform;
PoolHolder.SetActive(false);
CreatePanelHolder();
CreateTopNavBar();
@ -125,11 +106,13 @@ namespace UnityExplorer.UI
lastScreenHeight = Screen.height;
// Failsafe fix
foreach (var dropdown in CanvasRoot.GetComponentsInChildren<Dropdown>(true))
foreach (var dropdown in UIRoot.GetComponentsInChildren<Dropdown>(true))
dropdown.RefreshShownValue();
timeInput.Text = string.Empty;
timeInput.Text = Time.timeScale.ToString();
ScrollPool<ICell>.writingLockedListeners.Add(() => !PanelDragger.Resizing);
Initializing = false;
}
@ -140,7 +123,7 @@ namespace UnityExplorer.UI
public static void Update()
{
if (!CanvasRoot || Initializing)
if (!UIRoot)
return;
// if doing Mouse Inspect, update that and return.
@ -150,28 +133,13 @@ namespace UnityExplorer.UI
return;
}
// check master toggle
if (InputManager.GetKeyDown(ConfigManager.Master_Toggle.Value))
ShowMenu = !ShowMenu;
// return if menu closed
if (!ShowMenu)
return;
// Check forceUnlockMouse toggle
if (InputManager.GetKeyDown(ConfigManager.Force_Unlock_Toggle.Value))
CursorUnlocker.Unlock = !CursorUnlocker.Unlock;
// check event system state
if (!ConfigManager.Disable_EventSystem_Override.Value && EventSystem.current != EventSys)
CursorUnlocker.SetEventSystem();
UniverseLib.Config.ConfigManager.Force_Unlock_Mouse = !UniverseLib.Config.ConfigManager.Force_Unlock_Mouse;
// update focused panel
UIPanel.UpdateFocus();
// update UI model instances
PanelDragger.UpdateInstances();
InputFieldRef.UpdateInstances();
UIBehaviourModel.UpdateInstances();
// update the timescale value
if (!timeInput.Component.isFocused && lastTimeScale != Time.timeScale)
@ -311,35 +279,10 @@ namespace UnityExplorer.UI
// UI Construction
private static void CreateRootCanvas()
private static void CreatePanelHolder()
{
CanvasRoot = new GameObject("ExplorerCanvas");
UnityEngine.Object.DontDestroyOnLoad(CanvasRoot);
CanvasRoot.hideFlags |= HideFlags.HideAndDontSave;
CanvasRoot.layer = 5;
CanvasRoot.transform.position = new Vector3(0f, 0f, 1f);
CanvasRoot.SetActive(false);
EventSys = CanvasRoot.AddComponent<EventSystem>();
InputManager.AddUIModule();
EventSys.enabled = false;
CanvasRoot.SetActive(true);
Canvas = CanvasRoot.AddComponent<Canvas>();
Canvas.renderMode = RenderMode.ScreenSpaceCamera;
Canvas.referencePixelsPerUnit = 100;
Canvas.sortingOrder = 999;
CanvasScaler scaler = CanvasRoot.AddComponent<CanvasScaler>();
scaler.referenceResolution = new Vector2(1920, 1080);
scaler.screenMatchMode = CanvasScaler.ScreenMatchMode.Expand;
CanvasRoot.AddComponent<GraphicRaycaster>();
PanelHolder = new GameObject("PanelHolder");
PanelHolder.transform.SetParent(CanvasRoot.transform, false);
PanelHolder.transform.SetParent(UIRoot.transform, false);
PanelHolder.layer = 5;
var rect = PanelHolder.AddComponent<RectTransform>();
rect.sizeDelta = Vector2.zero;
@ -352,7 +295,7 @@ namespace UnityExplorer.UI
private static void CreateTopNavBar()
{
var navbarPanel = UIFactory.CreateUIObject("MainNavbar", CanvasRoot);
var navbarPanel = UIFactory.CreateUIObject("MainNavbar", UIRoot);
UIFactory.SetLayoutGroup<HorizontalLayoutGroup>(navbarPanel, false, false, true, true, 5, 4, 4, 4, 4, TextAnchor.MiddleCenter);
navbarPanel.AddComponent<Image>().color = new Color(0.1f, 0.1f, 0.1f);
NavBarRect = navbarPanel.GetComponent<RectTransform>();
@ -411,141 +354,5 @@ namespace UnityExplorer.UI
ConfigManager.Master_Toggle.OnValueChanged += Master_Toggle_OnValueChanged;
closeBtn.OnClick += OnCloseButtonClicked;
}
// UI AssetBundle
internal static AssetBundle ExplorerBundle;
private static void LoadBundle()
{
SetupAssetBundlePatches();
try
{
// Get the Major and Minor of the Unity version
var split = Application.unityVersion.Split('.');
int major = int.Parse(split[0]);
int minor = int.Parse(split[1]);
// Use appropriate AssetBundle for Unity version
// >= 2017
if (major >= 2017)
ExplorerBundle = LoadBundle("modern");
// 5.6.0 to <2017
else if (major == 5 && minor >= 6)
ExplorerBundle = LoadBundle("legacy.5.6");
// < 5.6.0
else
ExplorerBundle = LoadBundle("legacy");
}
catch
{
ExplorerCore.LogWarning($"Exception parsing Unity version, falling back to old AssetBundle load method...");
ExplorerBundle = LoadBundle("modern") ?? LoadBundle("legacy.5.6") ?? LoadBundle("legacy");
}
AssetBundle LoadBundle(string id)
{
var bundle = AssetBundle.LoadFromMemory(ReadFully(typeof(ExplorerCore)
.Assembly
.GetManifestResourceStream($"UnityExplorer.Resources.{id}.bundle")));
if (bundle)
ExplorerCore.Log($"Loaded {id} bundle for Unity {Application.unityVersion}");
return bundle;
}
if (ExplorerBundle == null)
{
ExplorerCore.LogWarning("Could not load the UnityExplorer UI Bundle!");
DefaultFont = ConsoleFont = Resources.GetBuiltinResource<Font>("Arial.ttf");
return;
}
// Bundle loaded
ConsoleFont = ExplorerBundle.LoadAsset<Font>("CONSOLA");
ConsoleFont.hideFlags = HideFlags.HideAndDontSave;
UnityEngine.Object.DontDestroyOnLoad(ConsoleFont);
DefaultFont = ExplorerBundle.LoadAsset<Font>("arial");
DefaultFont.hideFlags = HideFlags.HideAndDontSave;
UnityEngine.Object.DontDestroyOnLoad(DefaultFont);
BackupShader = ExplorerBundle.LoadAsset<Shader>("DefaultUI");
BackupShader.hideFlags = HideFlags.HideAndDontSave;
UnityEngine.Object.DontDestroyOnLoad(BackupShader);
// Fix for games which don't ship with 'UI/Default' shader.
if (Graphic.defaultGraphicMaterial.shader?.name != "UI/Default")
{
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;
}
private static byte[] ReadFully(Stream input)
{
using (var ms = new MemoryStream())
{
byte[] buffer = new byte[81920];
int read;
while ((read = input.Read(buffer, 0, buffer.Length)) != 0)
ms.Write(buffer, 0, read);
return ms.ToArray();
}
}
// AssetBundle patch
private static Type TypeofAssetBundle => ReflectionUtility.GetTypeByName("UnityEngine.AssetBundle");
private static void SetupAssetBundlePatches()
{
try
{
if (TypeofAssetBundle.GetMethod("UnloadAllAssetBundles", AccessTools.all) is MethodInfo unloadAllBundles)
{
#if CPP
// if IL2CPP, ensure method wasn't stripped
if (UnhollowerBaseLib.UnhollowerUtils.GetIl2CppMethodInfoPointerFieldForGeneratedMethod(unloadAllBundles) == null)
return;
#endif
var processor = ExplorerCore.Harmony.CreateProcessor(unloadAllBundles);
var prefix = new HarmonyMethod(typeof(UIManager).GetMethod(nameof(Prefix_UnloadAllAssetBundles), AccessTools.all));
processor.AddPrefix(prefix);
processor.Patch();
}
}
catch (Exception ex)
{
ExplorerCore.LogWarning($"Exception setting up AssetBundle.UnloadAllAssetBundles patch: {ex}");
}
}
static bool Prefix_UnloadAllAssetBundles(bool unloadAllObjects)
{
try
{
var method = typeof(AssetBundle).GetMethod("GetAllLoadedAssetBundles", AccessTools.all);
if (method == null)
return true;
var bundles = method.Invoke(null, ArgumentUtility.EmptyArgs) as AssetBundle[];
foreach (var obj in bundles)
{
if (obj.m_CachedPtr == ExplorerBundle.m_CachedPtr)
continue;
obj.Unload(unloadAllObjects);
}
}
catch (Exception ex)
{
ExplorerCore.LogWarning($"Exception unloading AssetBundles: {ex}");
}
return false;
}
}
}

View File

@ -4,10 +4,13 @@ using System.Linq;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
using UnityExplorer.Core.Input;
using UniverseLib.Input;
using UnityExplorer.Core.Runtime;
using UnityExplorer.UI;
using UnityExplorer.UI.Panels;
using UniverseLib.UI.Widgets;
using UniverseLib;
using UniverseLib.UI;
namespace UnityExplorer.UI.Widgets.AutoComplete
{

View File

@ -4,6 +4,7 @@ using System.Linq;
using System.Text;
using UnityEngine;
using UnityEngine.UI;
using UniverseLib.UI;
namespace UnityExplorer.UI.Widgets.AutoComplete
{

View File

@ -1,14 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using UnityEngine;
using UnityEngine.UI;
using UnityExplorer.Core.Input;
using UnityExplorer.Core.Runtime;
using UnityExplorer.UI.Models;
using UnityExplorer.UI.Panels;
using UniverseLib;
using UniverseLib.UI;
namespace UnityExplorer.UI.Widgets.AutoComplete
{

View File

@ -1,136 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.UI;
using UnityExplorer;
using UnityExplorer.Core;
using UnityExplorer.UI;
using UnityExplorer.UI.Models;
namespace UnityExplorer.UI.Widgets
{
public class AutoSliderScrollbar : UIBehaviourModel
{
public override GameObject UIRoot
{
get
{
if (Slider)
return Slider.gameObject;
return null;
}
}
//public event Action<float> OnValueChanged;
internal readonly Scrollbar Scrollbar;
internal readonly Slider Slider;
internal RectTransform ContentRect;
internal RectTransform ViewportRect;
//internal InputFieldScroller m_parentInputScroller;
public AutoSliderScrollbar(Scrollbar scrollbar, Slider slider, RectTransform contentRect, RectTransform viewportRect)
{
this.Scrollbar = scrollbar;
this.Slider = slider;
this.ContentRect = contentRect;
this.ViewportRect = viewportRect;
this.Scrollbar.onValueChanged.AddListener(this.OnScrollbarValueChanged);
this.Slider.onValueChanged.AddListener(this.OnSliderValueChanged);
//this.RefreshVisibility();
this.Slider.Set(0f, false);
}
private float lastAnchorPosition;
private float lastContentHeight;
private float lastViewportHeight;
private bool _refreshWanted;
public override void Update()
{
if (!Enabled)
return;
_refreshWanted = false;
if (ContentRect.localPosition.y != lastAnchorPosition)
{
lastAnchorPosition = ContentRect.localPosition.y;
_refreshWanted = true;
}
if (ContentRect.rect.height != lastContentHeight)
{
lastContentHeight = ContentRect.rect.height;
_refreshWanted = true;
}
if (ViewportRect.rect.height != lastViewportHeight)
{
lastViewportHeight = ViewportRect.rect.height;
_refreshWanted = true;
}
if (_refreshWanted)
UpdateSliderHandle();
}
public void UpdateSliderHandle()
{
// calculate handle size based on viewport / total data height
var totalHeight = ContentRect.rect.height;
var viewportHeight = ViewportRect.rect.height;
if (totalHeight <= viewportHeight)
{
Slider.handleRect.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, 0f);
Slider.value = 0f;
Slider.interactable = false;
return;
}
var handleHeight = viewportHeight * Math.Min(1, viewportHeight / totalHeight);
handleHeight = Math.Max(15f, handleHeight);
// resize the handle container area for the size of the handle (bigger handle = smaller container)
var container = Slider.m_HandleContainerRect;
container.offsetMax = new Vector2(container.offsetMax.x, -(handleHeight * 0.5f));
container.offsetMin = new Vector2(container.offsetMin.x, handleHeight * 0.5f);
// set handle size
Slider.handleRect.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, handleHeight);
// if slider is 100% height then make it not interactable
Slider.interactable = !Mathf.Approximately(handleHeight, viewportHeight);
float val = 0f;
if (totalHeight > 0f)
val = (float)((decimal)ContentRect.localPosition.y / (decimal)(totalHeight - ViewportRect.rect.height));
Slider.value = val;
}
public void OnScrollbarValueChanged(float value)
{
value = 1f - value;
if (this.Slider.value != value)
this.Slider.Set(value, false);
//OnValueChanged?.Invoke(value);
}
public void OnSliderValueChanged(float value)
{
value = 1f - value;
this.Scrollbar.value = value;
//OnValueChanged?.Invoke(value);
}
public override void ConstructUI(GameObject parent)
{
throw new NotImplementedException();
}
}
}

View File

@ -1,71 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
using UnityEngine.UI;
namespace UnityExplorer.UI.Widgets
{
public class ButtonCell : ICell
{
public float DefaultHeight => 25f;
public Action<int> OnClick;
public int CurrentDataIndex;
public ButtonRef Button;
#region ICell
public bool Enabled => m_enabled;
private bool m_enabled;
public GameObject UIRoot { get; set; }
public RectTransform Rect { get; set; }
public void Disable()
{
m_enabled = false;
UIRoot.SetActive(false);
}
public void Enable()
{
m_enabled = true;
UIRoot.SetActive(true);
}
#endregion
public virtual GameObject CreateContent(GameObject parent)
{
UIRoot = UIFactory.CreateHorizontalGroup(parent, "ButtonCell", true, false, true, true, 2, default,
new Color(0.11f, 0.11f, 0.11f), TextAnchor.MiddleCenter);
Rect = UIRoot.GetComponent<RectTransform>();
Rect.anchorMin = new Vector2(0, 1);
Rect.anchorMax = new Vector2(0, 1);
Rect.pivot = new Vector2(0.5f, 1);
Rect.sizeDelta = new Vector2(25, 25);
UIFactory.SetLayoutElement(UIRoot, minWidth: 100, flexibleWidth: 9999, minHeight: 25, flexibleHeight: 0);
UIRoot.SetActive(false);
this.Button = UIFactory.CreateButton(UIRoot, "NameButton", "Name");
UIFactory.SetLayoutElement(Button.Component.gameObject, flexibleWidth: 9999, minHeight: 25, flexibleHeight: 0);
var buttonText = Button.Component.GetComponentInChildren<Text>();
buttonText.horizontalOverflow = HorizontalWrapMode.Overflow;
buttonText.alignment = TextAnchor.MiddleLeft;
Color normal = new Color(0.11f, 0.11f, 0.11f);
Color highlight = new Color(0.16f, 0.16f, 0.16f);
Color pressed = new Color(0.05f, 0.05f, 0.05f);
Color disabled = new Color(1, 1, 1, 0);
RuntimeProvider.Instance.SetColorBlock(Button.Component, normal, highlight, pressed, disabled);
Button.OnClick += () => { OnClick?.Invoke(CurrentDataIndex); };
return UIRoot;
}
}
}

View File

@ -1,80 +0,0 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
namespace UnityExplorer.UI.Widgets
{
public class ButtonListHandler<TData, TCell> : ICellPoolDataSource<TCell> where TCell : ButtonCell
{
internal ScrollPool<TCell> ScrollPool;
public int ItemCount => currentEntries.Count;
public readonly List<TData> currentEntries = new List<TData>();
public Func<List<TData>> GetEntries;
public Action<TCell, int> SetICell;
public Func<TData, string, bool> ShouldDisplay;
public Action<int> OnCellClicked;
public string CurrentFilter
{
get => currentFilter;
set => currentFilter = value ?? "";
}
private string currentFilter;
public ButtonListHandler(ScrollPool<TCell> scrollPool, Func<List<TData>> getEntriesMethod,
Action<TCell, int> setICellMethod, Func<TData, string, bool> shouldDisplayMethod,
Action<int> onCellClickedMethod)
{
ScrollPool = scrollPool;
GetEntries = getEntriesMethod;
SetICell = setICellMethod;
ShouldDisplay = shouldDisplayMethod;
OnCellClicked = onCellClickedMethod;
}
public void RefreshData()
{
var allEntries = GetEntries();
currentEntries.Clear();
foreach (var entry in allEntries)
{
if (!string.IsNullOrEmpty(currentFilter))
{
if (!ShouldDisplay(entry, currentFilter))
continue;
currentEntries.Add(entry);
}
else
currentEntries.Add(entry);
}
}
public virtual void OnCellBorrowed(TCell cell)
{
cell.OnClick += OnCellClicked;
}
public virtual void SetCell(TCell cell, int index)
{
if (currentEntries == null)
RefreshData();
if (index < 0 || index >= currentEntries.Count)
cell.Disable();
else
{
cell.Enable();
cell.CurrentDataIndex = index;
SetICell(cell, index);
}
}
}
}

Some files were not shown because too many files have changed in this diff Show More