mirror of
https://github.com/sinai-dev/UnityExplorer.git
synced 2025-06-16 22:27:45 +08:00
Hooks: Add support for generic classes and methods
This commit is contained in:
parent
97cb14d6fc
commit
6e91f2a792
@ -16,14 +16,13 @@ namespace UnityExplorer.Hooks
|
|||||||
public float DefaultHeight => 30;
|
public float DefaultHeight => 30;
|
||||||
|
|
||||||
public Text MethodNameLabel;
|
public Text MethodNameLabel;
|
||||||
public Text HookedLabel;
|
|
||||||
public ButtonRef HookButton;
|
public ButtonRef HookButton;
|
||||||
|
|
||||||
public int CurrentDisplayedIndex;
|
public int CurrentDisplayedIndex;
|
||||||
|
|
||||||
private void OnHookClicked()
|
private void OnHookClicked()
|
||||||
{
|
{
|
||||||
HookManager.Instance.AddHookClicked(CurrentDisplayedIndex);
|
HookCreator.AddHookClicked(CurrentDisplayedIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Enable()
|
public void Enable()
|
||||||
@ -44,9 +43,6 @@ namespace UnityExplorer.Hooks
|
|||||||
UIFactory.SetLayoutElement(UIRoot, minWidth: 100, flexibleWidth: 9999, minHeight: 30, flexibleHeight: 600);
|
UIFactory.SetLayoutElement(UIRoot, minWidth: 100, flexibleWidth: 9999, minHeight: 30, flexibleHeight: 600);
|
||||||
UIRoot.AddComponent<ContentSizeFitter>().verticalFit = ContentSizeFitter.FitMode.PreferredSize;
|
UIRoot.AddComponent<ContentSizeFitter>().verticalFit = ContentSizeFitter.FitMode.PreferredSize;
|
||||||
|
|
||||||
HookedLabel = UIFactory.CreateLabel(UIRoot, "HookedLabel", "✓", TextAnchor.MiddleCenter, Color.green);
|
|
||||||
UIFactory.SetLayoutElement(HookedLabel.gameObject, minHeight: 25, minWidth: 100);
|
|
||||||
|
|
||||||
HookButton = UIFactory.CreateButton(UIRoot, "HookButton", "Hook", new Color(0.2f, 0.25f, 0.2f));
|
HookButton = UIFactory.CreateButton(UIRoot, "HookButton", "Hook", new Color(0.2f, 0.25f, 0.2f));
|
||||||
UIFactory.SetLayoutElement(HookButton.Component.gameObject, minHeight: 25, minWidth: 100);
|
UIFactory.SetLayoutElement(HookButton.Component.gameObject, minHeight: 25, minWidth: 100);
|
||||||
HookButton.OnClick += OnHookClicked;
|
HookButton.OnClick += OnHookClicked;
|
||||||
|
131
src/Hooks/GenericHookHandler.cs
Normal file
131
src/Hooks/GenericHookHandler.cs
Normal file
@ -0,0 +1,131 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Text;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEngine.UI;
|
||||||
|
using UnityExplorer.UI.Panels;
|
||||||
|
using UnityExplorer.UI.Widgets;
|
||||||
|
using UniverseLib.UI;
|
||||||
|
using UniverseLib.UI.Models;
|
||||||
|
using UniverseLib.UI.ObjectPool;
|
||||||
|
using UniverseLib.Utility;
|
||||||
|
|
||||||
|
namespace UnityExplorer.Hooks
|
||||||
|
{
|
||||||
|
public class GenericHookHandler
|
||||||
|
{
|
||||||
|
static GenericArgumentHandler[] handlers;
|
||||||
|
|
||||||
|
static Type[] currentGenericParameters;
|
||||||
|
static Action<Type[]> currentOnSubmit;
|
||||||
|
static Action currentOnCancel;
|
||||||
|
|
||||||
|
static Text Title;
|
||||||
|
static GameObject ArgsHolder;
|
||||||
|
|
||||||
|
// UI
|
||||||
|
internal static GameObject UIRoot;
|
||||||
|
|
||||||
|
public static void Show(Action<Type[]> onSubmit, Action onCancel, Type genericTypeDefinition)
|
||||||
|
{
|
||||||
|
Title.text = $"Setting generic arguments for {SignatureHighlighter.Parse(genericTypeDefinition, false)}...";
|
||||||
|
|
||||||
|
OnShow(onSubmit, onCancel, genericTypeDefinition.GetGenericArguments());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Show(Action<Type[]> onSubmit, Action onCancel, MethodInfo genericMethodDefinition)
|
||||||
|
{
|
||||||
|
Title.text = $"Setting generic arguments for {SignatureHighlighter.HighlightMethod(genericMethodDefinition)}...";
|
||||||
|
|
||||||
|
OnShow(onSubmit, onCancel, genericMethodDefinition.GetGenericArguments());
|
||||||
|
}
|
||||||
|
|
||||||
|
static void OnShow(Action<Type[]> onSubmit, Action onCancel, Type[] genericParameters)
|
||||||
|
{
|
||||||
|
HookManagerPanel.Instance.SetPage(HookManagerPanel.Pages.GenericArgsSelector);
|
||||||
|
|
||||||
|
currentOnSubmit = onSubmit;
|
||||||
|
currentOnCancel = onCancel;
|
||||||
|
|
||||||
|
SetGenericParameters(genericParameters);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void SetGenericParameters(Type[] genericParameters)
|
||||||
|
{
|
||||||
|
currentGenericParameters = genericParameters;
|
||||||
|
|
||||||
|
handlers = new GenericArgumentHandler[genericParameters.Length];
|
||||||
|
for (int i = 0; i < genericParameters.Length; i++)
|
||||||
|
{
|
||||||
|
Type type = genericParameters[i];
|
||||||
|
|
||||||
|
GenericArgumentHandler holder = handlers[i] = Pool<GenericArgumentHandler>.Borrow();
|
||||||
|
holder.UIRoot.transform.SetParent(ArgsHolder.transform, false);
|
||||||
|
holder.OnBorrowed(type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void TrySubmit()
|
||||||
|
{
|
||||||
|
Type[] args = new Type[currentGenericParameters.Length];
|
||||||
|
|
||||||
|
for (int i = 0; i < args.Length; i++)
|
||||||
|
{
|
||||||
|
GenericArgumentHandler handler = handlers[i];
|
||||||
|
Type arg = handler.Evaluate();
|
||||||
|
if (arg == null)
|
||||||
|
{
|
||||||
|
ExplorerCore.LogWarning($"Generic argument '{handler.inputField.Text}' is not a valid type.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
args[i] = arg;
|
||||||
|
}
|
||||||
|
|
||||||
|
OnClose();
|
||||||
|
currentOnSubmit(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Cancel()
|
||||||
|
{
|
||||||
|
OnClose();
|
||||||
|
|
||||||
|
currentOnCancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void OnClose()
|
||||||
|
{
|
||||||
|
foreach (GenericArgumentHandler widget in handlers)
|
||||||
|
{
|
||||||
|
widget.OnReturned();
|
||||||
|
Pool<GenericArgumentHandler>.Return(widget);
|
||||||
|
}
|
||||||
|
handlers = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// UI Construction
|
||||||
|
|
||||||
|
internal void ConstructUI(GameObject parent)
|
||||||
|
{
|
||||||
|
UIRoot = UIFactory.CreateVerticalGroup(parent, "GenericArgsHandler", false, false, true, true, 5, new Vector4(5, 5, 5, 5),
|
||||||
|
childAlignment: TextAnchor.MiddleCenter);
|
||||||
|
UIFactory.SetLayoutElement(UIRoot, flexibleWidth: 9999, flexibleHeight: 9999);
|
||||||
|
|
||||||
|
ButtonRef submitButton = UIFactory.CreateButton(UIRoot, "SubmitButton", "Submit", new Color(0.2f, 0.3f, 0.2f));
|
||||||
|
UIFactory.SetLayoutElement(submitButton.GameObject, minHeight: 25, minWidth: 200);
|
||||||
|
submitButton.OnClick += TrySubmit;
|
||||||
|
|
||||||
|
ButtonRef cancelButton = UIFactory.CreateButton(UIRoot, "CancelButton", "Cancel", new Color(0.3f, 0.2f, 0.2f));
|
||||||
|
UIFactory.SetLayoutElement(cancelButton.GameObject, minHeight: 25, minWidth: 200);
|
||||||
|
cancelButton.OnClick += Cancel;
|
||||||
|
|
||||||
|
Title = UIFactory.CreateLabel(UIRoot, "Title", "Generic Arguments", TextAnchor.MiddleCenter);
|
||||||
|
UIFactory.SetLayoutElement(Title.gameObject, minHeight: 25, flexibleWidth: 9999);
|
||||||
|
|
||||||
|
GameObject scrollview = UIFactory.CreateScrollView(UIRoot, "GenericArgsScrollView", out ArgsHolder, out _, new(0.1f, 0.1f, 0.1f));
|
||||||
|
UIFactory.SetLayoutElement(scrollview, flexibleWidth: 9999, flexibleHeight: 9999);
|
||||||
|
UIFactory.SetLayoutGroup<VerticalLayoutGroup>(ArgsHolder, padTop: 5, padLeft: 5, padBottom: 5, padRight: 5);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,6 @@
|
|||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using UnityEngine.UI;
|
using UnityEngine.UI;
|
||||||
|
using UnityExplorer.UI.Panels;
|
||||||
using UniverseLib.UI;
|
using UniverseLib.UI;
|
||||||
using UniverseLib.UI.Models;
|
using UniverseLib.UI.Models;
|
||||||
using UniverseLib.UI.Widgets.ScrollView;
|
using UniverseLib.UI.Widgets.ScrollView;
|
||||||
@ -24,17 +25,18 @@ namespace UnityExplorer.Hooks
|
|||||||
|
|
||||||
private void OnToggleActiveClicked()
|
private void OnToggleActiveClicked()
|
||||||
{
|
{
|
||||||
HookManager.Instance.EnableOrDisableHookClicked(CurrentDisplayedIndex);
|
HookList.EnableOrDisableHookClicked(CurrentDisplayedIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnDeleteClicked()
|
private void OnDeleteClicked()
|
||||||
{
|
{
|
||||||
HookManager.Instance.DeleteHookClicked(CurrentDisplayedIndex);
|
HookList.DeleteHookClicked(CurrentDisplayedIndex);
|
||||||
|
HookCreator.AddHooksScrollPool.Refresh(true, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnEditPatchClicked()
|
private void OnEditPatchClicked()
|
||||||
{
|
{
|
||||||
HookManager.Instance.EditPatchClicked(CurrentDisplayedIndex);
|
HookList.EditPatchClicked(CurrentDisplayedIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
public GameObject CreateContent(GameObject parent)
|
public GameObject CreateContent(GameObject parent)
|
||||||
@ -48,18 +50,18 @@ namespace UnityExplorer.Hooks
|
|||||||
MethodNameLabel = UIFactory.CreateLabel(UIRoot, "MethodName", "NOT SET", TextAnchor.MiddleLeft);
|
MethodNameLabel = UIFactory.CreateLabel(UIRoot, "MethodName", "NOT SET", TextAnchor.MiddleLeft);
|
||||||
UIFactory.SetLayoutElement(MethodNameLabel.gameObject, minHeight: 25, flexibleWidth: 9999);
|
UIFactory.SetLayoutElement(MethodNameLabel.gameObject, minHeight: 25, flexibleWidth: 9999);
|
||||||
|
|
||||||
ToggleActiveButton = UIFactory.CreateButton(UIRoot, "ToggleActiveBtn", "Enabled", new Color(0.15f, 0.2f, 0.15f));
|
ToggleActiveButton = UIFactory.CreateButton(UIRoot, "ToggleActiveBtn", "On", new Color(0.15f, 0.2f, 0.15f));
|
||||||
UIFactory.SetLayoutElement(ToggleActiveButton.Component.gameObject, minHeight: 25, minWidth: 100);
|
UIFactory.SetLayoutElement(ToggleActiveButton.Component.gameObject, minHeight: 25, minWidth: 35);
|
||||||
ToggleActiveButton.OnClick += OnToggleActiveClicked;
|
ToggleActiveButton.OnClick += OnToggleActiveClicked;
|
||||||
|
|
||||||
DeleteButton = UIFactory.CreateButton(UIRoot, "DeleteButton", "Delete", new Color(0.2f, 0.15f, 0.15f));
|
EditPatchButton = UIFactory.CreateButton(UIRoot, "EditButton", "Edit", new Color(0.15f, 0.15f, 0.15f));
|
||||||
UIFactory.SetLayoutElement(DeleteButton.Component.gameObject, minHeight: 25, minWidth: 100);
|
UIFactory.SetLayoutElement(EditPatchButton.Component.gameObject, minHeight: 25, minWidth: 35);
|
||||||
DeleteButton.OnClick += OnDeleteClicked;
|
|
||||||
|
|
||||||
EditPatchButton = UIFactory.CreateButton(UIRoot, "EditButton", "Edit Hook Source", new Color(0.15f, 0.15f, 0.15f));
|
|
||||||
UIFactory.SetLayoutElement(EditPatchButton.Component.gameObject, minHeight: 25, minWidth: 150);
|
|
||||||
EditPatchButton.OnClick += OnEditPatchClicked;
|
EditPatchButton.OnClick += OnEditPatchClicked;
|
||||||
|
|
||||||
|
DeleteButton = UIFactory.CreateButton(UIRoot, "DeleteButton", "X", new Color(0.2f, 0.15f, 0.15f));
|
||||||
|
UIFactory.SetLayoutElement(DeleteButton.Component.gameObject, minHeight: 25, minWidth: 35);
|
||||||
|
DeleteButton.OnClick += OnDeleteClicked;
|
||||||
|
|
||||||
return UIRoot;
|
return UIRoot;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
331
src/Hooks/HookCreator.cs
Normal file
331
src/Hooks/HookCreator.cs
Normal file
@ -0,0 +1,331 @@
|
|||||||
|
using HarmonyLib;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Reflection;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEngine.UI;
|
||||||
|
using UnityExplorer.CSConsole;
|
||||||
|
using UnityExplorer.Runtime;
|
||||||
|
using UnityExplorer.UI.Panels;
|
||||||
|
using UnityExplorer.UI.Widgets.AutoComplete;
|
||||||
|
using UniverseLib;
|
||||||
|
using UniverseLib.UI;
|
||||||
|
using UniverseLib.UI.Models;
|
||||||
|
using UniverseLib.UI.Widgets;
|
||||||
|
using UniverseLib.UI.Widgets.ScrollView;
|
||||||
|
using UniverseLib.Utility;
|
||||||
|
|
||||||
|
namespace UnityExplorer.Hooks
|
||||||
|
{
|
||||||
|
public class HookCreator : ICellPoolDataSource<AddHookCell>
|
||||||
|
{
|
||||||
|
public int ItemCount => filteredEligableMethods.Count;
|
||||||
|
|
||||||
|
static readonly List<MethodInfo> currentAddEligableMethods = new();
|
||||||
|
static readonly List<MethodInfo> filteredEligableMethods = new();
|
||||||
|
|
||||||
|
// hook editor
|
||||||
|
static readonly LexerBuilder Lexer = new();
|
||||||
|
internal static HookInstance CurrentEditedHook;
|
||||||
|
|
||||||
|
// Add Hooks UI
|
||||||
|
internal static GameObject AddHooksRoot;
|
||||||
|
internal static ScrollPool<AddHookCell> AddHooksScrollPool;
|
||||||
|
internal static Text AddHooksLabel;
|
||||||
|
internal static InputFieldRef AddHooksMethodFilterInput;
|
||||||
|
internal static InputFieldRef ClassSelectorInputField;
|
||||||
|
internal static Type pendingGenericDefinition;
|
||||||
|
internal static MethodInfo pendingGenericMethod;
|
||||||
|
|
||||||
|
// Hook Source Editor UI
|
||||||
|
public static GameObject EditorRoot { get; private set; }
|
||||||
|
public static Text EditingHookLabel { get; private set; }
|
||||||
|
public static InputFieldScroller EditorInputScroller { get; private set; }
|
||||||
|
public static InputFieldRef EditorInput => EditorInputScroller.InputField;
|
||||||
|
public static Text EditorInputText { get; private set; }
|
||||||
|
public static Text EditorHighlightText { get; private set; }
|
||||||
|
|
||||||
|
// ~~~~~~ New hook method selector ~~~~~~~
|
||||||
|
|
||||||
|
public void OnClassSelectedForHooks(string typeFullName)
|
||||||
|
{
|
||||||
|
Type type = ReflectionUtility.GetTypeByName(typeFullName);
|
||||||
|
if (type == null)
|
||||||
|
{
|
||||||
|
ExplorerCore.LogWarning($"Could not find any type by name {typeFullName}!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (type.IsGenericType)
|
||||||
|
{
|
||||||
|
pendingGenericDefinition = type;
|
||||||
|
GenericHookHandler.Show(OnGenericClassChosen, OnGenericClassCancel, type);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ShowMethodsForType(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShowMethodsForType(Type type)
|
||||||
|
{
|
||||||
|
SetAddHooksLabelType(SignatureHighlighter.Parse(type, true));
|
||||||
|
|
||||||
|
AddHooksMethodFilterInput.Text = string.Empty;
|
||||||
|
|
||||||
|
filteredEligableMethods.Clear();
|
||||||
|
currentAddEligableMethods.Clear();
|
||||||
|
foreach (MethodInfo method in type.GetMethods(ReflectionUtility.FLAGS))
|
||||||
|
{
|
||||||
|
if (UERuntimeHelper.IsBlacklisted(method))
|
||||||
|
continue;
|
||||||
|
currentAddEligableMethods.Add(method);
|
||||||
|
filteredEligableMethods.Add(method);
|
||||||
|
}
|
||||||
|
|
||||||
|
AddHooksScrollPool.Refresh(true, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void OnGenericClassChosen(Type[] genericArgs)
|
||||||
|
{
|
||||||
|
Type generic = pendingGenericDefinition.MakeGenericType(genericArgs);
|
||||||
|
ShowMethodsForType(generic);
|
||||||
|
HookManagerPanel.Instance.SetPage(HookManagerPanel.Pages.ClassMethodSelector);
|
||||||
|
}
|
||||||
|
|
||||||
|
void OnGenericClassCancel()
|
||||||
|
{
|
||||||
|
pendingGenericDefinition = null;
|
||||||
|
HookManagerPanel.Instance.SetPage(HookManagerPanel.Pages.ClassMethodSelector);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetAddHooksLabelType(string typeText)
|
||||||
|
{
|
||||||
|
AddHooksLabel.text = $"Adding hooks to: {typeText}";
|
||||||
|
|
||||||
|
AddHooksMethodFilterInput.GameObject.SetActive(true);
|
||||||
|
AddHooksScrollPool.UIRoot.SetActive(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void AddHookClicked(int index)
|
||||||
|
{
|
||||||
|
if (index >= filteredEligableMethods.Count)
|
||||||
|
return;
|
||||||
|
|
||||||
|
MethodInfo method = filteredEligableMethods[index];
|
||||||
|
if (!method.IsGenericMethod && HookList.hookedSignatures.Contains(method.FullDescription()))
|
||||||
|
{
|
||||||
|
ExplorerCore.Log($"Non-generic methods can only be hooked once.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if (method.IsGenericMethod)
|
||||||
|
{
|
||||||
|
pendingGenericMethod = method;
|
||||||
|
GenericHookHandler.Show(OnGenericMethodChosen, OnGenericMethodCancel, method);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
AddHook(filteredEligableMethods[index]);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void OnGenericMethodChosen(Type[] arguments)
|
||||||
|
{
|
||||||
|
MethodInfo generic = pendingGenericMethod.MakeGenericMethod(arguments);
|
||||||
|
AddHook(generic);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void OnGenericMethodCancel()
|
||||||
|
{
|
||||||
|
pendingGenericMethod = null;
|
||||||
|
HookManagerPanel.Instance.SetPage(HookManagerPanel.Pages.ClassMethodSelector);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void AddHook(MethodInfo method)
|
||||||
|
{
|
||||||
|
HookManagerPanel.Instance.SetPage(HookManagerPanel.Pages.ClassMethodSelector);
|
||||||
|
|
||||||
|
string sig = method.FullDescription();
|
||||||
|
if (HookList.hookedSignatures.Contains(sig))
|
||||||
|
{
|
||||||
|
ExplorerCore.LogWarning($"Method is already hooked!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
HookInstance hook = new(method);
|
||||||
|
if (hook.Enabled)
|
||||||
|
{
|
||||||
|
HookList.hookedSignatures.Add(sig);
|
||||||
|
HookList.currentHooks.Add(sig, hook);
|
||||||
|
}
|
||||||
|
|
||||||
|
AddHooksScrollPool.Refresh(true, false);
|
||||||
|
HookList.HooksScrollPool.Refresh(true, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnAddHookFilterInputChanged(string input)
|
||||||
|
{
|
||||||
|
filteredEligableMethods.Clear();
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(input))
|
||||||
|
filteredEligableMethods.AddRange(currentAddEligableMethods);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
foreach (MethodInfo method in currentAddEligableMethods)
|
||||||
|
{
|
||||||
|
if (method.Name.ContainsIgnoreCase(input))
|
||||||
|
filteredEligableMethods.Add(method);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AddHooksScrollPool.Refresh(true, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set eligable method cell
|
||||||
|
|
||||||
|
public void OnCellBorrowed(AddHookCell cell) { }
|
||||||
|
|
||||||
|
public void SetCell(AddHookCell cell, int index)
|
||||||
|
{
|
||||||
|
if (index >= filteredEligableMethods.Count)
|
||||||
|
{
|
||||||
|
cell.Disable();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
cell.CurrentDisplayedIndex = index;
|
||||||
|
MethodInfo method = filteredEligableMethods[index];
|
||||||
|
|
||||||
|
cell.MethodNameLabel.text = SignatureHighlighter.HighlightMethod(method);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ~~~~~~~~ Hook source editor ~~~~~~~~
|
||||||
|
|
||||||
|
internal static void SetEditedHook(HookInstance hook)
|
||||||
|
{
|
||||||
|
CurrentEditedHook = hook;
|
||||||
|
EditingHookLabel.text = $"Editing: {SignatureHighlighter.Parse(hook.TargetMethod.DeclaringType, false, hook.TargetMethod)}";
|
||||||
|
EditorInput.Text = hook.PatchSourceCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static void OnEditorInputChanged(string value)
|
||||||
|
{
|
||||||
|
EditorHighlightText.text = Lexer.BuildHighlightedString(value, 0, value.Length - 1, 0, EditorInput.Component.caretPosition, out _);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static void EditorInputCancel()
|
||||||
|
{
|
||||||
|
CurrentEditedHook = null;
|
||||||
|
HookManagerPanel.Instance.SetPage(HookManagerPanel.Pages.ClassMethodSelector);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static void EditorInputSave()
|
||||||
|
{
|
||||||
|
string input = EditorInput.Text;
|
||||||
|
bool wasEnabled = CurrentEditedHook.Enabled;
|
||||||
|
if (CurrentEditedHook.CompileAndGenerateProcessor(input))
|
||||||
|
{
|
||||||
|
if (wasEnabled)
|
||||||
|
CurrentEditedHook.Patch();
|
||||||
|
|
||||||
|
CurrentEditedHook.PatchSourceCode = input;
|
||||||
|
CurrentEditedHook = null;
|
||||||
|
HookManagerPanel.Instance.SetPage(HookManagerPanel.Pages.ClassMethodSelector);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// UI Construction
|
||||||
|
|
||||||
|
internal void ConstructAddHooksView(GameObject rightGroup)
|
||||||
|
{
|
||||||
|
AddHooksRoot = UIFactory.CreateUIObject("AddHooksPanel", rightGroup);
|
||||||
|
UIFactory.SetLayoutElement(AddHooksRoot, flexibleHeight: 9999, flexibleWidth: 9999);
|
||||||
|
UIFactory.SetLayoutGroup<VerticalLayoutGroup>(AddHooksRoot, false, false, true, true);
|
||||||
|
|
||||||
|
GameObject addRow = UIFactory.CreateHorizontalGroup(AddHooksRoot, "AddRow", false, true, true, true, 4,
|
||||||
|
new Vector4(2, 2, 2, 2), new Color(0.2f, 0.2f, 0.2f));
|
||||||
|
UIFactory.SetLayoutElement(addRow, minHeight: 25, flexibleHeight: 0, flexibleWidth: 9999);
|
||||||
|
|
||||||
|
ClassSelectorInputField = UIFactory.CreateInputField(addRow, "ClassInput", "Enter a class to add hooks to...");
|
||||||
|
UIFactory.SetLayoutElement(ClassSelectorInputField.Component.gameObject, flexibleWidth: 9999, minHeight: 25, flexibleHeight: 0);
|
||||||
|
new TypeCompleter(typeof(object), ClassSelectorInputField, true, false, Universe.Context != UniverseLib.Runtime.RuntimeContext.IL2CPP);
|
||||||
|
|
||||||
|
ButtonRef addButton = UIFactory.CreateButton(addRow, "AddButton", "View Methods");
|
||||||
|
UIFactory.SetLayoutElement(addButton.Component.gameObject, minWidth: 110, minHeight: 25);
|
||||||
|
addButton.OnClick += () => { OnClassSelectedForHooks(ClassSelectorInputField.Text); };
|
||||||
|
|
||||||
|
AddHooksLabel = UIFactory.CreateLabel(AddHooksRoot, "AddLabel", "Choose a class to begin...", TextAnchor.MiddleCenter);
|
||||||
|
UIFactory.SetLayoutElement(AddHooksLabel.gameObject, minHeight: 30, minWidth: 100, flexibleWidth: 9999);
|
||||||
|
|
||||||
|
AddHooksMethodFilterInput = UIFactory.CreateInputField(AddHooksRoot, "FilterInputField", "Filter method names...");
|
||||||
|
UIFactory.SetLayoutElement(AddHooksMethodFilterInput.Component.gameObject, minHeight: 30, flexibleWidth: 9999);
|
||||||
|
AddHooksMethodFilterInput.OnValueChanged += OnAddHookFilterInputChanged;
|
||||||
|
|
||||||
|
AddHooksScrollPool = UIFactory.CreateScrollPool<AddHookCell>(AddHooksRoot, "MethodAddScrollPool",
|
||||||
|
out GameObject addScrollRoot, out GameObject addContent);
|
||||||
|
UIFactory.SetLayoutElement(addScrollRoot, flexibleHeight: 9999);
|
||||||
|
AddHooksScrollPool.Initialize(this);
|
||||||
|
|
||||||
|
AddHooksMethodFilterInput.GameObject.SetActive(false);
|
||||||
|
AddHooksScrollPool.UIRoot.SetActive(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ConstructEditor(GameObject parent)
|
||||||
|
{
|
||||||
|
EditorRoot = UIFactory.CreateUIObject("HookSourceEditor", parent);
|
||||||
|
UIFactory.SetLayoutElement(EditorRoot, flexibleHeight: 9999, flexibleWidth: 9999);
|
||||||
|
UIFactory.SetLayoutGroup<VerticalLayoutGroup>(EditorRoot, true, true, true, true, 2, 3, 3, 3, 3);
|
||||||
|
|
||||||
|
EditingHookLabel = UIFactory.CreateLabel(EditorRoot, "EditingHookLabel", "NOT SET", TextAnchor.MiddleCenter);
|
||||||
|
EditingHookLabel.fontStyle = FontStyle.Bold;
|
||||||
|
UIFactory.SetLayoutElement(EditingHookLabel.gameObject, flexibleWidth: 9999, minHeight: 25);
|
||||||
|
|
||||||
|
Text editorLabel = UIFactory.CreateLabel(EditorRoot,
|
||||||
|
"EditorLabel",
|
||||||
|
"* Accepted method names are <b>Prefix</b>, <b>Postfix</b>, <b>Finalizer</b> and <b>Transpiler</b> (can define multiple).\n" +
|
||||||
|
"* Your patch methods must be static.\n" +
|
||||||
|
"* Hooks are temporary! Copy the source into your IDE to avoid losing work if you wish to keep it!",
|
||||||
|
TextAnchor.MiddleLeft);
|
||||||
|
UIFactory.SetLayoutElement(editorLabel.gameObject, minHeight: 25, flexibleWidth: 9999);
|
||||||
|
|
||||||
|
GameObject editorButtonRow = UIFactory.CreateHorizontalGroup(EditorRoot, "ButtonRow", false, false, true, true, 5);
|
||||||
|
UIFactory.SetLayoutElement(editorButtonRow, minHeight: 25, flexibleWidth: 9999);
|
||||||
|
|
||||||
|
ButtonRef editorSaveButton = UIFactory.CreateButton(editorButtonRow, "DoneButton", "Save and Return", new Color(0.2f, 0.3f, 0.2f));
|
||||||
|
UIFactory.SetLayoutElement(editorSaveButton.Component.gameObject, minHeight: 25, flexibleWidth: 9999);
|
||||||
|
editorSaveButton.OnClick += EditorInputSave;
|
||||||
|
|
||||||
|
ButtonRef editorDoneButton = UIFactory.CreateButton(editorButtonRow, "DoneButton", "Cancel and Return", new Color(0.2f, 0.2f, 0.2f));
|
||||||
|
UIFactory.SetLayoutElement(editorDoneButton.Component.gameObject, minHeight: 25, flexibleWidth: 9999);
|
||||||
|
editorDoneButton.OnClick += EditorInputCancel;
|
||||||
|
|
||||||
|
int fontSize = 16;
|
||||||
|
GameObject inputObj = UIFactory.CreateScrollInputField(EditorRoot, "EditorInput", "", out InputFieldScroller inputScroller, fontSize);
|
||||||
|
EditorInputScroller = inputScroller;
|
||||||
|
EditorInput.OnValueChanged += OnEditorInputChanged;
|
||||||
|
|
||||||
|
EditorInputText = EditorInput.Component.textComponent;
|
||||||
|
EditorInputText.supportRichText = false;
|
||||||
|
EditorInputText.color = Color.clear;
|
||||||
|
EditorInput.Component.customCaretColor = true;
|
||||||
|
EditorInput.Component.caretColor = Color.white;
|
||||||
|
EditorInput.PlaceholderText.fontSize = fontSize;
|
||||||
|
|
||||||
|
// Lexer highlight text overlay
|
||||||
|
GameObject highlightTextObj = UIFactory.CreateUIObject("HighlightText", EditorInputText.gameObject);
|
||||||
|
RectTransform highlightTextRect = highlightTextObj.GetComponent<RectTransform>();
|
||||||
|
highlightTextRect.pivot = new Vector2(0, 1);
|
||||||
|
highlightTextRect.anchorMin = Vector2.zero;
|
||||||
|
highlightTextRect.anchorMax = Vector2.one;
|
||||||
|
highlightTextRect.offsetMin = Vector2.zero;
|
||||||
|
highlightTextRect.offsetMax = Vector2.zero;
|
||||||
|
|
||||||
|
EditorHighlightText = highlightTextObj.AddComponent<Text>();
|
||||||
|
EditorHighlightText.color = Color.white;
|
||||||
|
EditorHighlightText.supportRichText = true;
|
||||||
|
EditorHighlightText.fontSize = fontSize;
|
||||||
|
|
||||||
|
// Set fonts
|
||||||
|
EditorInputText.font = UniversalUI.ConsoleFont;
|
||||||
|
EditorInput.PlaceholderText.font = UniversalUI.ConsoleFont;
|
||||||
|
EditorHighlightText.font = UniversalUI.ConsoleFont;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
94
src/Hooks/HookList.cs
Normal file
94
src/Hooks/HookList.cs
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
using HarmonyLib;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Collections.Specialized;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEngine.UI;
|
||||||
|
using UnityExplorer.UI.Panels;
|
||||||
|
using UniverseLib;
|
||||||
|
using UniverseLib.UI;
|
||||||
|
using UniverseLib.UI.Models;
|
||||||
|
using UniverseLib.UI.Widgets.ScrollView;
|
||||||
|
using UniverseLib.Utility;
|
||||||
|
|
||||||
|
namespace UnityExplorer.Hooks
|
||||||
|
{
|
||||||
|
public class HookList : ICellPoolDataSource<HookCell>
|
||||||
|
{
|
||||||
|
public int ItemCount => currentHooks.Count;
|
||||||
|
|
||||||
|
internal static readonly HashSet<string> hookedSignatures = new();
|
||||||
|
internal static readonly OrderedDictionary currentHooks = new();
|
||||||
|
|
||||||
|
internal static GameObject UIRoot;
|
||||||
|
internal static ScrollPool<HookCell> HooksScrollPool;
|
||||||
|
|
||||||
|
public static void EnableOrDisableHookClicked(int index)
|
||||||
|
{
|
||||||
|
HookInstance hook = (HookInstance)currentHooks[index];
|
||||||
|
hook.TogglePatch();
|
||||||
|
|
||||||
|
HooksScrollPool.Refresh(true, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void DeleteHookClicked(int index)
|
||||||
|
{
|
||||||
|
HookInstance hook = (HookInstance)currentHooks[index];
|
||||||
|
|
||||||
|
if (HookCreator.CurrentEditedHook == hook)
|
||||||
|
HookCreator.EditorInputCancel();
|
||||||
|
|
||||||
|
hook.Unpatch();
|
||||||
|
currentHooks.RemoveAt(index);
|
||||||
|
hookedSignatures.Remove(hook.TargetMethod.FullDescription());
|
||||||
|
|
||||||
|
HooksScrollPool.Refresh(true, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void EditPatchClicked(int index)
|
||||||
|
{
|
||||||
|
HookManagerPanel.Instance.SetPage(HookManagerPanel.Pages.HookSourceEditor);
|
||||||
|
HookInstance hook = (HookInstance)currentHooks[index];
|
||||||
|
HookCreator.SetEditedHook(hook);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set current hook cell
|
||||||
|
|
||||||
|
public void OnCellBorrowed(HookCell cell) { }
|
||||||
|
|
||||||
|
public void SetCell(HookCell cell, int index)
|
||||||
|
{
|
||||||
|
if (index >= currentHooks.Count)
|
||||||
|
{
|
||||||
|
cell.Disable();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
cell.CurrentDisplayedIndex = index;
|
||||||
|
HookInstance hook = (HookInstance)currentHooks[index];
|
||||||
|
|
||||||
|
cell.MethodNameLabel.text = SignatureHighlighter.HighlightMethod(hook.TargetMethod);
|
||||||
|
|
||||||
|
cell.ToggleActiveButton.ButtonText.text = hook.Enabled ? "On" : "Off";
|
||||||
|
RuntimeHelper.SetColorBlockAuto(cell.ToggleActiveButton.Component,
|
||||||
|
hook.Enabled ? new Color(0.15f, 0.2f, 0.15f) : new Color(0.2f, 0.2f, 0.15f));
|
||||||
|
}
|
||||||
|
|
||||||
|
// UI
|
||||||
|
|
||||||
|
internal void ConstructUI(GameObject leftGroup)
|
||||||
|
{
|
||||||
|
UIRoot = UIFactory.CreateUIObject("CurrentHooksPanel", leftGroup);
|
||||||
|
UIFactory.SetLayoutElement(UIRoot, flexibleHeight: 9999, flexibleWidth: 9999);
|
||||||
|
UIFactory.SetLayoutGroup<VerticalLayoutGroup>(UIRoot, true, true, true, true);
|
||||||
|
|
||||||
|
Text hooksLabel = UIFactory.CreateLabel(UIRoot, "HooksLabel", "Current Hooks", TextAnchor.MiddleCenter);
|
||||||
|
UIFactory.SetLayoutElement(hooksLabel.gameObject, minHeight: 30, flexibleWidth: 9999);
|
||||||
|
|
||||||
|
HooksScrollPool = UIFactory.CreateScrollPool<HookCell>(UIRoot, "HooksScrollPool",
|
||||||
|
out GameObject hooksScroll, out GameObject hooksContent);
|
||||||
|
UIFactory.SetLayoutElement(hooksScroll, flexibleHeight: 9999);
|
||||||
|
HooksScrollPool.Initialize(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,227 +0,0 @@
|
|||||||
using HarmonyLib;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Collections.Specialized;
|
|
||||||
using System.Reflection;
|
|
||||||
using UnityEngine;
|
|
||||||
using UnityExplorer.CSConsole;
|
|
||||||
using UnityExplorer.Runtime;
|
|
||||||
using UnityExplorer.UI;
|
|
||||||
using UnityExplorer.UI.Panels;
|
|
||||||
using UniverseLib;
|
|
||||||
using UniverseLib.UI.Widgets.ScrollView;
|
|
||||||
using UniverseLib.Utility;
|
|
||||||
|
|
||||||
namespace UnityExplorer.Hooks
|
|
||||||
{
|
|
||||||
public class HookManager : ICellPoolDataSource<HookCell>, ICellPoolDataSource<AddHookCell>
|
|
||||||
{
|
|
||||||
private static HookManager s_instance;
|
|
||||||
public static HookManager Instance => s_instance ?? (s_instance = new HookManager());
|
|
||||||
|
|
||||||
public HookManagerPanel Panel => UIManager.GetPanel<HookManagerPanel>(UIManager.Panels.HookManager);
|
|
||||||
|
|
||||||
// This class acts as the data source for both current hooks and eligable methods when adding hooks.
|
|
||||||
// 'isAddingMethods' keeps track of which pool is currently the displayed one, so our ItemCount reflects the
|
|
||||||
// correct pool cells.
|
|
||||||
private bool isAddingMethods;
|
|
||||||
public int ItemCount => isAddingMethods ? filteredEligableMethods.Count : currentHooks.Count;
|
|
||||||
|
|
||||||
// current hooks
|
|
||||||
private readonly HashSet<string> hookedSignatures = new();
|
|
||||||
private readonly OrderedDictionary currentHooks = new();
|
|
||||||
|
|
||||||
// adding hooks
|
|
||||||
private readonly List<MethodInfo> currentAddEligableMethods = new();
|
|
||||||
private readonly List<MethodInfo> filteredEligableMethods = new();
|
|
||||||
|
|
||||||
// hook editor
|
|
||||||
private readonly LexerBuilder Lexer = new();
|
|
||||||
private HookInstance currentEditedHook;
|
|
||||||
|
|
||||||
// ~~~~~~~~~~~ Main Current Hooks window ~~~~~~~~~~~
|
|
||||||
|
|
||||||
public void EnableOrDisableHookClicked(int index)
|
|
||||||
{
|
|
||||||
HookInstance hook = (HookInstance)currentHooks[index];
|
|
||||||
hook.TogglePatch();
|
|
||||||
|
|
||||||
Panel.HooksScrollPool.Refresh(true, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void DeleteHookClicked(int index)
|
|
||||||
{
|
|
||||||
HookInstance hook = (HookInstance)currentHooks[index];
|
|
||||||
hook.Unpatch();
|
|
||||||
currentHooks.RemoveAt(index);
|
|
||||||
hookedSignatures.Remove(hook.TargetMethod.FullDescription());
|
|
||||||
|
|
||||||
Panel.HooksScrollPool.Refresh(true, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void EditPatchClicked(int index)
|
|
||||||
{
|
|
||||||
Panel.SetPage(HookManagerPanel.Pages.HookSourceEditor);
|
|
||||||
HookInstance hook = (HookInstance)currentHooks[index];
|
|
||||||
currentEditedHook = hook;
|
|
||||||
Panel.EditorInput.Text = hook.PatchSourceCode;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set current hook cell
|
|
||||||
|
|
||||||
public void OnCellBorrowed(HookCell cell) { }
|
|
||||||
|
|
||||||
public void SetCell(HookCell cell, int index)
|
|
||||||
{
|
|
||||||
if (index >= this.currentHooks.Count)
|
|
||||||
{
|
|
||||||
cell.Disable();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
cell.CurrentDisplayedIndex = index;
|
|
||||||
HookInstance hook = (HookInstance)this.currentHooks[index];
|
|
||||||
|
|
||||||
cell.MethodNameLabel.text = SignatureHighlighter.HighlightMethod(hook.TargetMethod);
|
|
||||||
|
|
||||||
cell.ToggleActiveButton.ButtonText.text = hook.Enabled ? "Enabled" : "Disabled";
|
|
||||||
RuntimeHelper.SetColorBlockAuto(cell.ToggleActiveButton.Component,
|
|
||||||
hook.Enabled ? new Color(0.15f, 0.2f, 0.15f) : new Color(0.2f, 0.2f, 0.15f));
|
|
||||||
}
|
|
||||||
|
|
||||||
// ~~~~~~~~~~~ Add Hooks window ~~~~~~~~~~~
|
|
||||||
|
|
||||||
public void OnClassSelectedForHooks(string typeFullName)
|
|
||||||
{
|
|
||||||
Type type = ReflectionUtility.GetTypeByName(typeFullName);
|
|
||||||
if (type == null)
|
|
||||||
{
|
|
||||||
ExplorerCore.LogWarning($"Could not find any type by name {typeFullName}!");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Panel.SetAddHooksLabelType(SignatureHighlighter.Parse(type, true));
|
|
||||||
|
|
||||||
Panel.ResetMethodFilter();
|
|
||||||
filteredEligableMethods.Clear();
|
|
||||||
currentAddEligableMethods.Clear();
|
|
||||||
foreach (MethodInfo method in type.GetMethods(ReflectionUtility.FLAGS))
|
|
||||||
{
|
|
||||||
if (method.IsGenericMethod || UERuntimeHelper.IsBlacklisted(method))
|
|
||||||
continue;
|
|
||||||
currentAddEligableMethods.Add(method);
|
|
||||||
filteredEligableMethods.Add(method);
|
|
||||||
}
|
|
||||||
|
|
||||||
isAddingMethods = true;
|
|
||||||
Panel.SetPage(HookManagerPanel.Pages.ClassMethodSelector);
|
|
||||||
Panel.AddHooksScrollPool.Refresh(true, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void DoneAddingHooks()
|
|
||||||
{
|
|
||||||
isAddingMethods = false;
|
|
||||||
Panel.SetPage(HookManagerPanel.Pages.CurrentHooks);
|
|
||||||
Panel.HooksScrollPool.Refresh(true, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void AddHookClicked(int index)
|
|
||||||
{
|
|
||||||
if (index >= this.filteredEligableMethods.Count)
|
|
||||||
return;
|
|
||||||
|
|
||||||
AddHook(filteredEligableMethods[index]);
|
|
||||||
Panel.AddHooksScrollPool.Refresh(true, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void AddHook(MethodInfo method)
|
|
||||||
{
|
|
||||||
string sig = method.FullDescription();
|
|
||||||
if (hookedSignatures.Contains(sig))
|
|
||||||
return;
|
|
||||||
|
|
||||||
HookInstance hook = new(method);
|
|
||||||
if (hook.Enabled)
|
|
||||||
{
|
|
||||||
hookedSignatures.Add(sig);
|
|
||||||
currentHooks.Add(sig, hook);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void OnAddHookFilterInputChanged(string input)
|
|
||||||
{
|
|
||||||
filteredEligableMethods.Clear();
|
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(input))
|
|
||||||
filteredEligableMethods.AddRange(currentAddEligableMethods);
|
|
||||||
else
|
|
||||||
{
|
|
||||||
foreach (MethodInfo method in currentAddEligableMethods)
|
|
||||||
{
|
|
||||||
if (method.Name.ContainsIgnoreCase(input))
|
|
||||||
filteredEligableMethods.Add(method);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Panel.AddHooksScrollPool.Refresh(true, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set eligable method cell
|
|
||||||
|
|
||||||
public void OnCellBorrowed(AddHookCell cell) { }
|
|
||||||
|
|
||||||
public void SetCell(AddHookCell cell, int index)
|
|
||||||
{
|
|
||||||
if (index >= this.filteredEligableMethods.Count)
|
|
||||||
{
|
|
||||||
cell.Disable();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
cell.CurrentDisplayedIndex = index;
|
|
||||||
MethodInfo method = this.filteredEligableMethods[index];
|
|
||||||
|
|
||||||
cell.MethodNameLabel.text = SignatureHighlighter.HighlightMethod(method);
|
|
||||||
|
|
||||||
string sig = method.FullDescription();
|
|
||||||
if (hookedSignatures.Contains(sig))
|
|
||||||
{
|
|
||||||
cell.HookButton.Component.gameObject.SetActive(false);
|
|
||||||
cell.HookedLabel.gameObject.SetActive(true);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
cell.HookButton.Component.gameObject.SetActive(true);
|
|
||||||
cell.HookedLabel.gameObject.SetActive(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ~~~~~~~~~~~ Hook source editor window ~~~~~~~~~~~
|
|
||||||
|
|
||||||
public void OnEditorInputChanged(string value)
|
|
||||||
{
|
|
||||||
Panel.EditorHighlightText.text = Lexer.BuildHighlightedString(value, 0, value.Length - 1, 0,
|
|
||||||
Panel.EditorInput.Component.caretPosition, out _);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void EditorInputCancel()
|
|
||||||
{
|
|
||||||
currentEditedHook = null;
|
|
||||||
Panel.SetPage(HookManagerPanel.Pages.CurrentHooks);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void EditorInputSave()
|
|
||||||
{
|
|
||||||
string input = Panel.EditorInput.Text;
|
|
||||||
bool wasEnabled = currentEditedHook.Enabled;
|
|
||||||
if (currentEditedHook.CompileAndGenerateProcessor(input))
|
|
||||||
{
|
|
||||||
if (wasEnabled)
|
|
||||||
currentEditedHook.Patch();
|
|
||||||
currentEditedHook.PatchSourceCode = input;
|
|
||||||
currentEditedHook = null;
|
|
||||||
Panel.SetPage(HookManagerPanel.Pages.CurrentHooks);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,7 +1,9 @@
|
|||||||
using UnityEngine;
|
using System;
|
||||||
|
using UnityEngine;
|
||||||
using UnityEngine.UI;
|
using UnityEngine.UI;
|
||||||
using UnityExplorer.Hooks;
|
using UnityExplorer.Hooks;
|
||||||
using UnityExplorer.UI.Widgets.AutoComplete;
|
using UnityExplorer.UI.Widgets.AutoComplete;
|
||||||
|
using UniverseLib;
|
||||||
using UniverseLib.UI;
|
using UniverseLib.UI;
|
||||||
using UniverseLib.UI.Models;
|
using UniverseLib.UI.Models;
|
||||||
using UniverseLib.UI.Widgets;
|
using UniverseLib.UI.Widgets;
|
||||||
@ -11,75 +13,57 @@ namespace UnityExplorer.UI.Panels
|
|||||||
{
|
{
|
||||||
public class HookManagerPanel : UEPanel
|
public class HookManagerPanel : UEPanel
|
||||||
{
|
{
|
||||||
|
public static HookManagerPanel Instance { get; private set; }
|
||||||
|
|
||||||
public enum Pages
|
public enum Pages
|
||||||
{
|
{
|
||||||
CurrentHooks,
|
|
||||||
ClassMethodSelector,
|
ClassMethodSelector,
|
||||||
HookSourceEditor
|
HookSourceEditor,
|
||||||
|
GenericArgsSelector,
|
||||||
}
|
}
|
||||||
|
|
||||||
public override UIManager.Panels PanelType => UIManager.Panels.HookManager;
|
internal static HookCreator hookCreator;
|
||||||
|
internal static HookList hookList;
|
||||||
|
internal static GenericHookHandler genericArgsHandler;
|
||||||
|
|
||||||
|
// Panel
|
||||||
|
public override UIManager.Panels PanelType => UIManager.Panels.HookManager;
|
||||||
public override string Name => "Hooks";
|
public override string Name => "Hooks";
|
||||||
public override bool ShowByDefault => false;
|
public override bool ShowByDefault => false;
|
||||||
|
public override int MinWidth => 750;
|
||||||
public override int MinWidth => 500;
|
public override int MinHeight => 400;
|
||||||
public override int MinHeight => 600;
|
|
||||||
public override Vector2 DefaultAnchorMin => new(0.5f, 0.5f);
|
public override Vector2 DefaultAnchorMin => new(0.5f, 0.5f);
|
||||||
public override Vector2 DefaultAnchorMax => new(0.5f, 0.5f);
|
public override Vector2 DefaultAnchorMax => new(0.5f, 0.5f);
|
||||||
|
|
||||||
public Pages CurrentPage { get; private set; } = Pages.CurrentHooks;
|
public Pages CurrentPage { get; private set; } = Pages.ClassMethodSelector;
|
||||||
|
|
||||||
private GameObject currentHooksPanel;
|
|
||||||
public ScrollPool<HookCell> HooksScrollPool;
|
|
||||||
private InputFieldRef classSelectorInputField;
|
|
||||||
|
|
||||||
private GameObject addHooksPanel;
|
|
||||||
public ScrollPool<AddHookCell> AddHooksScrollPool;
|
|
||||||
private Text addHooksLabel;
|
|
||||||
private InputFieldRef AddHooksMethodFilterInput;
|
|
||||||
|
|
||||||
private GameObject editorPanel;
|
|
||||||
|
|
||||||
public InputFieldScroller EditorInputScroller { get; private set; }
|
|
||||||
public InputFieldRef EditorInput => EditorInputScroller.InputField;
|
|
||||||
public Text EditorInputText { get; private set; }
|
|
||||||
public Text EditorHighlightText { get; private set; }
|
|
||||||
|
|
||||||
public HookManagerPanel(UIBase owner) : base(owner)
|
public HookManagerPanel(UIBase owner) : base(owner)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnClassInputAddClicked()
|
|
||||||
{
|
|
||||||
HookManager.Instance.OnClassSelectedForHooks(this.classSelectorInputField.Text);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SetAddHooksLabelType(string typeText) => addHooksLabel.text = $"Adding hooks to: {typeText}";
|
|
||||||
|
|
||||||
public void SetPage(Pages page)
|
public void SetPage(Pages page)
|
||||||
{
|
{
|
||||||
switch (page)
|
switch (page)
|
||||||
{
|
{
|
||||||
case Pages.CurrentHooks:
|
|
||||||
currentHooksPanel.SetActive(true);
|
|
||||||
addHooksPanel.SetActive(false);
|
|
||||||
editorPanel.SetActive(false);
|
|
||||||
break;
|
|
||||||
case Pages.ClassMethodSelector:
|
case Pages.ClassMethodSelector:
|
||||||
currentHooksPanel.SetActive(false);
|
HookCreator.AddHooksRoot.SetActive(true);
|
||||||
addHooksPanel.SetActive(true);
|
HookCreator.EditorRoot.SetActive(false);
|
||||||
editorPanel.SetActive(false);
|
GenericHookHandler.UIRoot.SetActive(false);
|
||||||
break;
|
break;
|
||||||
case Pages.HookSourceEditor:
|
|
||||||
currentHooksPanel.SetActive(false);
|
|
||||||
addHooksPanel.SetActive(false);
|
|
||||||
editorPanel.SetActive(true);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void ResetMethodFilter() => AddHooksMethodFilterInput.Text = string.Empty;
|
case Pages.HookSourceEditor:
|
||||||
|
HookCreator.AddHooksRoot.SetActive(false);
|
||||||
|
HookCreator.EditorRoot.SetActive(true);
|
||||||
|
GenericHookHandler.UIRoot.SetActive(false);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Pages.GenericArgsSelector:
|
||||||
|
HookCreator.AddHooksRoot.SetActive(false);
|
||||||
|
HookCreator.EditorRoot.SetActive(false);
|
||||||
|
GenericHookHandler.UIRoot.SetActive(true);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public override void SetDefaultSizeAndPosition()
|
public override void SetDefaultSizeAndPosition()
|
||||||
{
|
{
|
||||||
@ -91,115 +75,33 @@ namespace UnityExplorer.UI.Panels
|
|||||||
|
|
||||||
protected override void ConstructPanelContent()
|
protected override void ConstructPanelContent()
|
||||||
{
|
{
|
||||||
// ~~~~~~~~~ Active hooks scroll pool
|
Instance = this;
|
||||||
|
hookList = new();
|
||||||
|
hookCreator = new();
|
||||||
|
genericArgsHandler = new();
|
||||||
|
|
||||||
currentHooksPanel = UIFactory.CreateUIObject("CurrentHooksPanel", this.ContentRoot);
|
GameObject baseHoriGroup = UIFactory.CreateHorizontalGroup(ContentRoot, "HoriGroup", true, true, true, true);
|
||||||
UIFactory.SetLayoutElement(currentHooksPanel, flexibleHeight: 9999, flexibleWidth: 9999);
|
UIFactory.SetLayoutElement(baseHoriGroup, flexibleWidth: 9999, flexibleHeight: 9999);
|
||||||
UIFactory.SetLayoutGroup<VerticalLayoutGroup>(currentHooksPanel, true, true, true, true);
|
|
||||||
|
|
||||||
GameObject addRow = UIFactory.CreateHorizontalGroup(currentHooksPanel, "AddRow", false, true, true, true, 4,
|
// Left Group
|
||||||
new Vector4(2, 2, 2, 2), new Color(0.2f, 0.2f, 0.2f));
|
|
||||||
UIFactory.SetLayoutElement(addRow, minHeight: 30, flexibleWidth: 9999);
|
|
||||||
|
|
||||||
classSelectorInputField = UIFactory.CreateInputField(addRow, "ClassInput", "Enter a class to add hooks to...");
|
GameObject leftGroup = UIFactory.CreateVerticalGroup(baseHoriGroup, "LeftGroup", true, true, true, true);
|
||||||
UIFactory.SetLayoutElement(classSelectorInputField.Component.gameObject, flexibleWidth: 9999);
|
UIFactory.SetLayoutElement(leftGroup.gameObject, minWidth: 300, flexibleWidth: 9999, flexibleHeight: 9999);
|
||||||
new TypeCompleter(typeof(object), classSelectorInputField, true, false);
|
|
||||||
|
|
||||||
ButtonRef addButton = UIFactory.CreateButton(addRow, "AddButton", "Add Hooks");
|
hookList.ConstructUI(leftGroup);
|
||||||
UIFactory.SetLayoutElement(addButton.Component.gameObject, minWidth: 100, minHeight: 25);
|
|
||||||
addButton.OnClick += OnClassInputAddClicked;
|
|
||||||
|
|
||||||
Text hooksLabel = UIFactory.CreateLabel(currentHooksPanel, "HooksLabel", "Current Hooks", TextAnchor.MiddleCenter);
|
// Right Group
|
||||||
UIFactory.SetLayoutElement(hooksLabel.gameObject, minHeight: 30, flexibleWidth: 9999);
|
|
||||||
|
|
||||||
HooksScrollPool = UIFactory.CreateScrollPool<HookCell>(currentHooksPanel, "HooksScrollPool",
|
GameObject rightGroup = UIFactory.CreateVerticalGroup(baseHoriGroup, "RightGroup", true, true, true, true);
|
||||||
out GameObject hooksScroll, out GameObject hooksContent);
|
UIFactory.SetLayoutElement(rightGroup, minWidth: 300, flexibleWidth: 9999, flexibleHeight: 9999);
|
||||||
UIFactory.SetLayoutElement(hooksScroll, flexibleHeight: 9999);
|
|
||||||
HooksScrollPool.Initialize(HookManager.Instance);
|
|
||||||
|
|
||||||
// ~~~~~~~~~ Add hooks panel
|
hookCreator.ConstructAddHooksView(rightGroup);
|
||||||
|
|
||||||
addHooksPanel = UIFactory.CreateUIObject("AddHooksPanel", this.ContentRoot);
|
hookCreator.ConstructEditor(rightGroup);
|
||||||
UIFactory.SetLayoutElement(addHooksPanel, flexibleHeight: 9999, flexibleWidth: 9999);
|
HookCreator.EditorRoot.SetActive(false);
|
||||||
UIFactory.SetLayoutGroup<VerticalLayoutGroup>(addHooksPanel, true, true, true, true);
|
|
||||||
|
|
||||||
addHooksLabel = UIFactory.CreateLabel(addHooksPanel, "AddLabel", "NOT SET", TextAnchor.MiddleCenter);
|
genericArgsHandler.ConstructUI(rightGroup);
|
||||||
UIFactory.SetLayoutElement(addHooksLabel.gameObject, minHeight: 30, minWidth: 100, flexibleWidth: 9999);
|
GenericHookHandler.UIRoot.SetActive(false);
|
||||||
|
|
||||||
GameObject buttonRow = UIFactory.CreateHorizontalGroup(addHooksPanel, "ButtonRow", false, false, true, true, 5);
|
|
||||||
UIFactory.SetLayoutElement(buttonRow, minHeight: 25, flexibleWidth: 9999);
|
|
||||||
|
|
||||||
ButtonRef doneButton = UIFactory.CreateButton(buttonRow, "DoneButton", "Done", new Color(0.2f, 0.3f, 0.2f));
|
|
||||||
UIFactory.SetLayoutElement(doneButton.Component.gameObject, minHeight: 25, flexibleWidth: 9999);
|
|
||||||
doneButton.OnClick += HookManager.Instance.DoneAddingHooks;
|
|
||||||
|
|
||||||
AddHooksMethodFilterInput = UIFactory.CreateInputField(addHooksPanel, "FilterInputField", "Filter method names...");
|
|
||||||
UIFactory.SetLayoutElement(AddHooksMethodFilterInput.Component.gameObject, minHeight: 30, flexibleWidth: 9999);
|
|
||||||
AddHooksMethodFilterInput.OnValueChanged += HookManager.Instance.OnAddHookFilterInputChanged;
|
|
||||||
|
|
||||||
AddHooksScrollPool = UIFactory.CreateScrollPool<AddHookCell>(addHooksPanel, "MethodAddScrollPool",
|
|
||||||
out GameObject addScrollRoot, out GameObject addContent);
|
|
||||||
UIFactory.SetLayoutElement(addScrollRoot, flexibleHeight: 9999);
|
|
||||||
AddHooksScrollPool.Initialize(HookManager.Instance);
|
|
||||||
|
|
||||||
addHooksPanel.gameObject.SetActive(false);
|
|
||||||
|
|
||||||
// ~~~~~~~~~ Hook source editor panel
|
|
||||||
|
|
||||||
editorPanel = UIFactory.CreateUIObject("HookSourceEditor", this.ContentRoot);
|
|
||||||
UIFactory.SetLayoutElement(editorPanel, flexibleHeight: 9999, flexibleWidth: 9999);
|
|
||||||
UIFactory.SetLayoutGroup<VerticalLayoutGroup>(editorPanel, true, true, true, true);
|
|
||||||
|
|
||||||
Text editorLabel = UIFactory.CreateLabel(editorPanel,
|
|
||||||
"EditorLabel",
|
|
||||||
"Edit Harmony patch source as desired. Accepted method names are Prefix, Postfix, Finalizer and Transpiler (can define multiple).\n\n" +
|
|
||||||
"Hooks are temporary! Please copy the source into your IDE to avoid losing work if you wish to keep it!",
|
|
||||||
TextAnchor.MiddleLeft);
|
|
||||||
UIFactory.SetLayoutElement(editorLabel.gameObject, minHeight: 25, flexibleWidth: 9999);
|
|
||||||
|
|
||||||
GameObject editorButtonRow = UIFactory.CreateHorizontalGroup(editorPanel, "ButtonRow", false, false, true, true, 5);
|
|
||||||
UIFactory.SetLayoutElement(editorButtonRow, minHeight: 25, flexibleWidth: 9999);
|
|
||||||
|
|
||||||
ButtonRef editorSaveButton = UIFactory.CreateButton(editorButtonRow, "DoneButton", "Save and Return", new Color(0.2f, 0.3f, 0.2f));
|
|
||||||
UIFactory.SetLayoutElement(editorSaveButton.Component.gameObject, minHeight: 25, flexibleWidth: 9999);
|
|
||||||
editorSaveButton.OnClick += HookManager.Instance.EditorInputSave;
|
|
||||||
|
|
||||||
ButtonRef editorDoneButton = UIFactory.CreateButton(editorButtonRow, "DoneButton", "Cancel and Return", new Color(0.2f, 0.2f, 0.2f));
|
|
||||||
UIFactory.SetLayoutElement(editorDoneButton.Component.gameObject, minHeight: 25, flexibleWidth: 9999);
|
|
||||||
editorDoneButton.OnClick += HookManager.Instance.EditorInputCancel;
|
|
||||||
|
|
||||||
int fontSize = 16;
|
|
||||||
GameObject inputObj = UIFactory.CreateScrollInputField(editorPanel, "EditorInput", "", out InputFieldScroller inputScroller, fontSize);
|
|
||||||
EditorInputScroller = inputScroller;
|
|
||||||
EditorInput.OnValueChanged += HookManager.Instance.OnEditorInputChanged;
|
|
||||||
|
|
||||||
EditorInputText = EditorInput.Component.textComponent;
|
|
||||||
EditorInputText.supportRichText = false;
|
|
||||||
EditorInputText.color = Color.clear;
|
|
||||||
EditorInput.Component.customCaretColor = true;
|
|
||||||
EditorInput.Component.caretColor = Color.white;
|
|
||||||
EditorInput.PlaceholderText.fontSize = fontSize;
|
|
||||||
|
|
||||||
// Lexer highlight text overlay
|
|
||||||
GameObject highlightTextObj = UIFactory.CreateUIObject("HighlightText", EditorInputText.gameObject);
|
|
||||||
RectTransform highlightTextRect = highlightTextObj.GetComponent<RectTransform>();
|
|
||||||
highlightTextRect.pivot = new Vector2(0, 1);
|
|
||||||
highlightTextRect.anchorMin = Vector2.zero;
|
|
||||||
highlightTextRect.anchorMax = Vector2.one;
|
|
||||||
highlightTextRect.offsetMin = Vector2.zero;
|
|
||||||
highlightTextRect.offsetMax = Vector2.zero;
|
|
||||||
|
|
||||||
EditorHighlightText = highlightTextObj.AddComponent<Text>();
|
|
||||||
EditorHighlightText.color = Color.white;
|
|
||||||
EditorHighlightText.supportRichText = true;
|
|
||||||
EditorHighlightText.fontSize = fontSize;
|
|
||||||
|
|
||||||
// Set fonts
|
|
||||||
EditorInputText.font = UniversalUI.ConsoleFont;
|
|
||||||
EditorInput.PlaceholderText.font = UniversalUI.ConsoleFont;
|
|
||||||
EditorHighlightText.font = UniversalUI.ConsoleFont;
|
|
||||||
|
|
||||||
editorPanel.SetActive(false);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user