mirror of
https://github.com/goatcorp/Dalamud.git
synced 2025-12-15 05:04:15 +01:00
233 lines
7.6 KiB
C#
233 lines
7.6 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Runtime.InteropServices;
|
|
using System.Text;
|
|
using System.Text.RegularExpressions;
|
|
using System.Threading.Tasks;
|
|
using Dalamud.Plugin;
|
|
|
|
namespace Dalamud.Interface.Scratchpad
|
|
{
|
|
class ScratchMacroProcessor
|
|
{
|
|
private const string template = @"
|
|
|
|
public class ScratchPlugin : IDalamudPlugin {
|
|
|
|
public string Name => ""ScratchPlugin"";
|
|
private DalamudPluginInterface pi;
|
|
|
|
{SETUPBODY}
|
|
|
|
public void Initialize(DalamudPluginInterface pluginInterface)
|
|
{
|
|
this.pi = pluginInterface;
|
|
|
|
this.pi.UiBuilder.OnBuildUi += DrawUI;
|
|
|
|
{INITBODY}
|
|
}
|
|
|
|
private void DrawUI()
|
|
{
|
|
{DRAWBODY}
|
|
}
|
|
|
|
{NONEBODY}
|
|
|
|
public void Dispose()
|
|
{
|
|
this.pi.UiBuilder.OnBuildUi -= DrawUI;
|
|
{DISPOSEBODY}
|
|
}
|
|
}
|
|
";
|
|
|
|
private enum ParseContext
|
|
{
|
|
None,
|
|
Init,
|
|
Draw,
|
|
Hook,
|
|
Dispose,
|
|
}
|
|
|
|
private class HookInfo
|
|
{
|
|
public string Body { get; set; }
|
|
|
|
public string Arguments { get; set; }
|
|
|
|
public string Invocation { get; set; }
|
|
|
|
public string RetType { get; set; }
|
|
|
|
public string Sig { get; set; }
|
|
}
|
|
|
|
public string Process(string input)
|
|
{
|
|
var lines = input.Split(new[] {'\r', '\n'});
|
|
|
|
var ctx = ParseContext.None;
|
|
|
|
var setupBody = string.Empty;
|
|
var noneBody = string.Empty;
|
|
var initBody = string.Empty;
|
|
var disposeBody = string.Empty;
|
|
var drawBody = string.Empty;
|
|
var tHook = new HookInfo();
|
|
|
|
var hooks = new List<HookInfo>();
|
|
|
|
for (var i = 0; i < lines.Length; i++)
|
|
{
|
|
var line = lines[i];
|
|
|
|
if (line.StartsWith("INITIALIZE:"))
|
|
{
|
|
ctx = ParseContext.Init;
|
|
continue;
|
|
}
|
|
|
|
if (line.StartsWith("DRAW:"))
|
|
{
|
|
ctx = ParseContext.Draw;
|
|
continue;
|
|
}
|
|
|
|
if (line.StartsWith("DISPOSE:"))
|
|
{
|
|
ctx = ParseContext.Dispose;
|
|
continue;
|
|
}
|
|
|
|
if (line.StartsWith("HOOK("))
|
|
{
|
|
ctx = ParseContext.Hook;
|
|
|
|
var args = Regex.Match(line, "HOOK\\((.+)+\\):").Groups[0].Captures[0].Value
|
|
.Split(new[] { "," }, StringSplitOptions.RemoveEmptyEntries).Select(x => x[0] == ' ' ? x[1..] : x).ToArray();
|
|
|
|
tHook.Sig = args[0].Replace("\"", string.Empty); // Split quotation marks if any
|
|
tHook.Sig = tHook.Sig.Replace("HOOK(", string.Empty);
|
|
tHook.RetType = args[1];
|
|
tHook.Arguments = string.Join(", ", args.Skip(2).ToArray()).Replace("):", string.Empty);
|
|
|
|
var invocationGroups = Regex.Matches(tHook.Arguments, "\\S+ ([a-zA-Z0-9]+),*").Cast<Match>()
|
|
.Select(x => x.Groups[1].Value);
|
|
tHook.Invocation = string.Join(", ", invocationGroups);
|
|
continue;
|
|
}
|
|
|
|
if (line.StartsWith("END;"))
|
|
{
|
|
switch (ctx)
|
|
{
|
|
case ParseContext.None:
|
|
throw new Exception("Not in a macro!!!");
|
|
case ParseContext.Init:
|
|
break;
|
|
case ParseContext.Draw:
|
|
break;
|
|
case ParseContext.Hook:
|
|
hooks.Add(tHook);
|
|
tHook = new HookInfo();
|
|
break;
|
|
case ParseContext.Dispose:
|
|
break;
|
|
default:
|
|
throw new ArgumentOutOfRangeException();
|
|
}
|
|
|
|
ctx = ParseContext.None;
|
|
continue;
|
|
}
|
|
|
|
switch (ctx)
|
|
{
|
|
case ParseContext.None:
|
|
noneBody += line + "\n";
|
|
break;
|
|
case ParseContext.Init:
|
|
initBody += line + "\n";
|
|
break;
|
|
case ParseContext.Draw:
|
|
drawBody += line + "\n";
|
|
break;
|
|
case ParseContext.Hook:
|
|
tHook.Body += line + "\n";
|
|
break;
|
|
case ParseContext.Dispose:
|
|
disposeBody += line + "\n";
|
|
break;
|
|
default:
|
|
throw new ArgumentOutOfRangeException();
|
|
}
|
|
}
|
|
|
|
var hookSetup = string.Empty;
|
|
var hookInit = string.Empty;
|
|
var hookDetour = string.Empty;
|
|
var hookDispose = string.Empty;
|
|
for (var i = 0; i < hooks.Count; i++)
|
|
{
|
|
var hook = hooks[i];
|
|
|
|
hookSetup +=
|
|
$"private delegate {hook.RetType} Hook{i}Delegate({hook.Arguments});\n";
|
|
hookSetup +=
|
|
$"private Hook<Hook{i}Delegate> hook{i}Inst;\n";
|
|
|
|
hookInit += $"var addrH{i} = pi.TargetModuleScanner.ScanText(\"{hook.Sig}\");\n";
|
|
hookInit +=
|
|
$"this.hook{i}Inst = new Hook<Hook{i}Delegate>(addrH{i}, new Hook{i}Delegate(Hook{i}Detour), this);\n";
|
|
hookInit +=
|
|
$"this.hook{i}Inst.Enable();\n";
|
|
|
|
var originalCall = $"this.hook{i}Inst.Original({hook.Invocation});\n";
|
|
if (hook.RetType != "void")
|
|
originalCall = "return " + originalCall;
|
|
|
|
if (hook.Body.Contains($"hook{i}Inst.Original(") || hook.Body.Contains("ORIG("))
|
|
{
|
|
PluginLog.Warning($"Attention! A manual call to Original() in Hook #{i} was detected. Original calls will not be managed for you.");
|
|
originalCall = string.Empty;
|
|
}
|
|
|
|
if (hook.Body.Contains("ORIG("))
|
|
{
|
|
PluginLog.Warning($"Normalizing Original() call in Hook #{i}.");
|
|
hook.Body = hook.Body.Replace("ORIG(", $"this.hook{i}Inst.Original(");
|
|
}
|
|
|
|
hookDetour +=
|
|
$"private {hook.RetType} Hook{i}Detour({hook.Arguments}) {{\n" +
|
|
$"try {{\n" +
|
|
$" {hook.Body}\n" +
|
|
$"}} catch(Exception ex) {{\n" +
|
|
$" PluginLog.Error(ex, \"Exception in Hook{i}Detour!!\");\n" +
|
|
$"}}\n" +
|
|
$"{originalCall}" +
|
|
$"\n}}\n";
|
|
|
|
hookDispose += $"this.hook{i}Inst.Dispose();\n";
|
|
}
|
|
|
|
setupBody += "\n" + hookSetup;
|
|
initBody = hookInit + "\n" + initBody;
|
|
noneBody += "\n" + hookDetour;
|
|
disposeBody += "\n" + hookDispose;
|
|
|
|
var output = template;
|
|
output = output.Replace("{SETUPBODY}", setupBody);
|
|
output = output.Replace("{INITBODY}", initBody);
|
|
output = output.Replace("{DRAWBODY}", drawBody);
|
|
output = output.Replace("{NONEBODY}", noneBody);
|
|
output = output.Replace("{DISPOSEBODY}", disposeBody);
|
|
|
|
return output;
|
|
}
|
|
}
|
|
}
|