diff --git a/src/Hooks/HookCreator.cs b/src/Hooks/HookCreator.cs index 17abccd5..ffbb3456 100644 --- a/src/Hooks/HookCreator.cs +++ b/src/Hooks/HookCreator.cs @@ -1,4 +1,6 @@ -using HarmonyLib; +using System.Linq; +using System.Xml.Serialization; +using HarmonyLib; using UnityExplorer.CSConsole; using UnityExplorer.Runtime; using UnityExplorer.UI.Panels; @@ -225,12 +227,96 @@ internal static void EditorInputSave() CurrentEditedHook.Patch(); CurrentEditedHook.PatchSourceCode = input; + SaveHooks(CurrentEditedHook); CurrentEditedHook = null; HookManagerPanel.Instance.SetPage(HookManagerPanel.Pages.ClassMethodSelector); } HookList.HooksScrollPool.Refresh(true, false); } + // Persistent hooks + public struct HookData + { + public string Description; + public string SourceCode; + public string ReflectedType; + } + + public static void SaveHooks(HookInstance hook) + { + HookData data = new HookData(); + data.Description = hook.TargetMethod.FullDescription(); + data.ReflectedType = hook.TargetMethod.ReflectedType.ToString(); + data.SourceCode = hook.PatchSourceCode; + string filename = data.Description.GetHashCode().ToString("X8") + ".txt"; + string folderpath = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "UnityExplorerHarmonyHooks"); + Directory.CreateDirectory(folderpath); + string fpath = Path.Combine(folderpath, filename); + XmlSerializer xs = new XmlSerializer(typeof(HookData)); + TextWriter tw = new StreamWriter(fpath); + xs.Serialize(tw, data); + tw.Close(); + ExplorerCore.Log("Save hook: " + data.Description + " to: " + filename); + } + + public void LoadSavedHooks() + { + string folderpath = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "UnityExplorerHarmonyHooks"); + Directory.CreateDirectory(folderpath); + ExplorerCore.Log("Hooks path: " + folderpath); + XmlSerializer xs = new XmlSerializer(typeof(HookData)); + xs.UnknownNode += new XmlNodeEventHandler(xs_UnknownNode); + xs.UnknownAttribute += new XmlAttributeEventHandler(xs_UnknownAttribute); + DirectoryInfo di = new DirectoryInfo(folderpath); + FileInfo[] fs = di.GetFiles("*.txt"); + foreach (FileInfo fi in fs) + { + ExplorerCore.Log("Load: " + fi.Name); + try + { + FileStream fileStream = new FileStream(fi.FullName, FileMode.Open, FileAccess.Read); + HookData hookData; + hookData = (HookData)xs.Deserialize(fileStream); + ExplorerCore.Log("+> Hook: " + hookData.Description); + Type type = ReflectionUtility.GetTypeByName(hookData.ReflectedType); + IEnumerable ms = type.GetMethods().Where( + mi => mi.FullDescription() == hookData.Description + ); + + MethodInfo method = ms.First(); + HookManagerPanel.Instance.SetPage(HookManagerPanel.Pages.ClassMethodSelector); + + if (HookList.hookedSignatures.Contains(hookData.Description)) + { + ExplorerCore.LogWarning($"Method is already hooked!"); + return; + } + + HookInstance hook = new(method, hookData.SourceCode); + HookList.hookedSignatures.Add(hookData.Description); + HookList.currentHooks.Add(hookData.Description, hook); + + AddHooksScrollPool.Refresh(true, false); + HookList.HooksScrollPool.Refresh(true, false); + } + catch (Exception ex) + { + ExplorerCore.LogError("Exception when load: " + fi.Name + " ---------\n" + ex.Message); + } + } + } + + + private void xs_UnknownNode(object sender, XmlNodeEventArgs e) + { + ExplorerCore.LogWarning("Unknown Node in hooks:" + e.Name + "\t" + e.Text); + } + + private void xs_UnknownAttribute(object sender, XmlAttributeEventArgs e) + { + System.Xml.XmlAttribute attr = e.Attr; + ExplorerCore.LogWarning("Unknown attribute in hooks:" + attr.Name + "='" + attr.Value + "'"); + } // UI Construction diff --git a/src/Hooks/HookInstance.cs b/src/Hooks/HookInstance.cs index d0c771ea..31a19e7b 100644 --- a/src/Hooks/HookInstance.cs +++ b/src/Hooks/HookInstance.cs @@ -46,6 +46,19 @@ public HookInstance(MethodInfo targetMethod) if (CompileAndGenerateProcessor(PatchSourceCode)) Patch(); } + + public HookInstance(MethodInfo targetMethod, string code) + { + this.TargetMethod = targetMethod; + this.signature = TargetMethod.FullDescription(); + + PatchSourceCode = code; + + if (CompileAndGenerateProcessor(PatchSourceCode)) + { + Patch(); + } + } // Evaluator.source_file private static readonly FieldInfo fi_sourceFile = AccessTools.Field(typeof(Evaluator), "source_file"); diff --git a/src/UI/Panels/HookManagerPanel.cs b/src/UI/Panels/HookManagerPanel.cs index f4f60300..7f0f6ba5 100644 --- a/src/UI/Panels/HookManagerPanel.cs +++ b/src/UI/Panels/HookManagerPanel.cs @@ -97,6 +97,8 @@ protected override void ConstructPanelContent() genericArgsHandler.ConstructUI(ContentRoot); genericArgsHandler.UIRoot.SetActive(false); + + hookCreator.LoadSavedHooks(); } } }