Skip to content
This repository was archived by the owner on May 9, 2023. It is now read-only.

Commit 371054d

Browse files
committed
Add hook source editor for custom dynamic hooks
1 parent 427f23b commit 371054d

File tree

4 files changed

+291
-142
lines changed

4 files changed

+291
-142
lines changed

src/Hooks/HookCell.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ public GameObject CreateContent(GameObject parent)
5959
UIFactory.SetLayoutElement(DeleteButton.Component.gameObject, minHeight: 25, minWidth: 100);
6060
DeleteButton.OnClick += OnDeleteClicked;
6161

62-
EditPatchButton = UIFactory.CreateButton(UIRoot, "EditButton", "Log Hook Source", new Color(0.15f, 0.15f, 0.15f));
62+
EditPatchButton = UIFactory.CreateButton(UIRoot, "EditButton", "Edit Hook Source", new Color(0.15f, 0.15f, 0.15f));
6363
UIFactory.SetLayoutElement(EditPatchButton.Component.gameObject, minHeight: 25, minWidth: 150);
6464
EditPatchButton.OnClick += OnEditPatchClicked;
6565

src/Hooks/HookInstance.cs

Lines changed: 74 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -21,18 +21,24 @@ public class HookInstance
2121

2222
public bool Enabled;
2323
public MethodInfo TargetMethod;
24-
public string GeneratedSource;
24+
public string PatchSourceCode;
2525

26-
private string shortSignature;
26+
private readonly string shortSignature;
2727
private PatchProcessor patchProcessor;
28-
private HarmonyMethod patchDelegate;
29-
private MethodInfo patchDelegateMethodInfo;
28+
29+
private MethodInfo postfix;
30+
private MethodInfo prefix;
31+
private MethodInfo finalizer;
32+
private MethodInfo transpiler;
3033

3134
public HookInstance(MethodInfo targetMethod)
3235
{
3336
this.TargetMethod = targetMethod;
3437
this.shortSignature = $"{targetMethod.DeclaringType.Name}.{targetMethod.Name}";
35-
if (GenerateProcessorAndDelegate())
38+
39+
GenerateDefaultPatchSourceCode(targetMethod);
40+
41+
if (CompileAndGenerateProcessor(PatchSourceCode))
3642
Patch();
3743
}
3844

@@ -41,30 +47,53 @@ public HookInstance(MethodInfo targetMethod)
4147
// TypeDefinition.Definition
4248
private static readonly PropertyInfo pi_Definition = ReflectionUtility.GetPropertyInfo(typeof(TypeDefinition), "Definition");
4349

44-
private bool GenerateProcessorAndDelegate()
50+
public bool CompileAndGenerateProcessor(string patchSource)
4551
{
52+
Unpatch();
53+
4654
try
4755
{
4856
patchProcessor = ExplorerCore.Harmony.CreateProcessor(TargetMethod);
4957

5058
// Dynamically compile the patch method
5159

52-
scriptEvaluator.Run(GeneratePatchSourceCode(TargetMethod));
60+
var codeBuilder = new StringBuilder();
61+
62+
codeBuilder.AppendLine($"public class DynamicPatch_{DateTime.Now.Ticks}");
63+
codeBuilder.AppendLine("{");
64+
codeBuilder.AppendLine(patchSource);
65+
codeBuilder.AppendLine("}");
66+
67+
scriptEvaluator.Run(codeBuilder.ToString());
5368

5469
if (ScriptEvaluator._reportPrinter.ErrorsCount > 0)
5570
throw new FormatException($"Unable to compile the generated patch!");
5671

5772
// TODO: Publicize MCS to avoid this reflection
58-
// Get the last defined type in the source file
59-
var typeContainer = ((CompilationSourceFile)fi_sourceFile.GetValue(scriptEvaluator)).Containers.Last();
60-
// Get the TypeSpec from the TypeDefinition, then get its "MetaInfo" (System.Type), then get the method called Patch.
61-
this.patchDelegateMethodInfo = ((TypeSpec)pi_Definition.GetValue((Class)typeContainer, null))
62-
.GetMetaInfo()
63-
.GetMethod("Patch", ReflectionUtility.FLAGS);
73+
// Get the most recent Patch type in the source file
74+
var typeContainer = ((CompilationSourceFile)fi_sourceFile.GetValue(scriptEvaluator))
75+
.Containers
76+
.Last(it => it.MemberName.Name.StartsWith("DynamicPatch_"));
77+
// Get the TypeSpec from the TypeDefinition, then get its "MetaInfo" (System.Type)
78+
var patchClass = ((TypeSpec)pi_Definition.GetValue((Class)typeContainer, null)).GetMetaInfo();
6479

65-
// Actually create the harmony patch
66-
this.patchDelegate = new HarmonyMethod(patchDelegateMethodInfo);
67-
patchProcessor.AddPostfix(patchDelegate);
80+
// Create the harmony patches as defined
81+
82+
postfix = patchClass.GetMethod("Postfix", ReflectionUtility.FLAGS);
83+
if (postfix != null)
84+
patchProcessor.AddPostfix(new HarmonyMethod(postfix));
85+
86+
prefix = patchClass.GetMethod("Prefix", ReflectionUtility.FLAGS);
87+
if (prefix != null)
88+
patchProcessor.AddPrefix(new HarmonyMethod(prefix));
89+
90+
finalizer = patchClass.GetMethod("Finalizer", ReflectionUtility.FLAGS);
91+
if (finalizer != null)
92+
patchProcessor.AddFinalizer(new HarmonyMethod(finalizer));
93+
94+
transpiler = patchClass.GetMethod("Transpiler", ReflectionUtility.FLAGS);
95+
if (transpiler != null)
96+
patchProcessor.AddTranspiler(new HarmonyMethod(transpiler));
6897

6998
return true;
7099
}
@@ -75,16 +104,12 @@ private bool GenerateProcessorAndDelegate()
75104
}
76105
}
77106

78-
private string GeneratePatchSourceCode(MethodInfo targetMethod)
107+
private string GenerateDefaultPatchSourceCode(MethodInfo targetMethod)
79108
{
80109
var codeBuilder = new StringBuilder();
81-
82-
codeBuilder.AppendLine($"public class DynamicPatch_{DateTime.Now.Ticks}");
83-
codeBuilder.AppendLine("{");
84-
85110
// Arguments
86111

87-
codeBuilder.Append(" public static void Patch(System.Reflection.MethodBase __originalMethod");
112+
codeBuilder.Append("public static void Postfix(System.Reflection.MethodBase __originalMethod");
88113

89114
if (!targetMethod.IsStatic)
90115
codeBuilder.Append($", {targetMethod.DeclaringType.FullName} __instance");
@@ -106,55 +131,53 @@ private string GeneratePatchSourceCode(MethodInfo targetMethod)
106131

107132
// Patch body
108133

109-
codeBuilder.AppendLine(" {");
134+
codeBuilder.AppendLine("{");
110135

111-
codeBuilder.AppendLine(" try {");
136+
codeBuilder.AppendLine(" try {");
112137

113138
// Log message
114139

115140
var logMessage = new StringBuilder();
116-
logMessage.AppendLine($"$@\"Patch called: {shortSignature}");
117-
141+
logMessage.Append($"Patch called: {shortSignature}\\n");
142+
118143
if (!targetMethod.IsStatic)
119-
logMessage.AppendLine("__instance: {__instance.ToString()}");
144+
logMessage.Append("__instance: {__instance.ToString()}\\n");
120145

121146
paramIdx = 0;
122147
foreach (var param in parameters)
123148
{
149+
logMessage.Append($"Parameter {paramIdx} {param.Name}: ");
124150
Type pType = param.ParameterType;
125151
if (pType.IsByRef) pType = pType.GetElementType();
126152
if (pType.IsValueType)
127-
logMessage.AppendLine($"Parameter {paramIdx} {param.Name}: {{__{paramIdx}.ToString()}}");
153+
logMessage.Append($"{{__{paramIdx}.ToString()}}");
128154
else
129-
logMessage.AppendLine($"Parameter {paramIdx} {param.Name}: {{__{paramIdx}?.ToString() ?? \"null\"}}");
155+
logMessage.Append($"{{__{paramIdx}?.ToString() ?? \"null\"}}");
156+
logMessage.Append("\\n");
130157
paramIdx++;
131158
}
132159

133160
if (targetMethod.ReturnType != typeof(void))
134161
{
162+
logMessage.Append("Return value: ");
135163
if (targetMethod.ReturnType.IsValueType)
136-
logMessage.AppendLine("Return value: {__result.ToString()}");
164+
logMessage.Append("{__result.ToString()}");
137165
else
138-
logMessage.AppendLine("Return value: {__result?.ToString() ?? \"null\"}");
166+
logMessage.Append("{__result?.ToString() ?? \"null\"}");
167+
logMessage.Append("\\n");
139168
}
140169

141-
logMessage.Append('"');
142-
143-
codeBuilder.AppendLine($" UnityExplorer.ExplorerCore.Log({logMessage});");
144-
codeBuilder.AppendLine(" }");
145-
codeBuilder.AppendLine(" catch (System.Exception ex) {");
146-
codeBuilder.AppendLine($" UnityExplorer.ExplorerCore.LogWarning($\"Exception in patch of {shortSignature}:\\n{{ex}}\");");
147-
codeBuilder.AppendLine(" }");
148-
149-
// End patch body
150-
170+
codeBuilder.AppendLine($" UnityExplorer.ExplorerCore.Log($\"{logMessage}\");");
171+
codeBuilder.AppendLine(" }");
172+
codeBuilder.AppendLine(" catch (System.Exception ex) {");
173+
codeBuilder.AppendLine($" UnityExplorer.ExplorerCore.LogWarning($\"Exception in patch of {shortSignature}:\\n{{ex}}\");");
151174
codeBuilder.AppendLine(" }");
152175

153-
// End class
176+
// End patch body
154177

155178
codeBuilder.AppendLine("}");
156179

157-
return GeneratedSource = codeBuilder.ToString();
180+
return PatchSourceCode = codeBuilder.ToString();
158181
}
159182

160183
public void TogglePatch()
@@ -181,12 +204,16 @@ public void Patch()
181204

182205
public void Unpatch()
183206
{
184-
if (!Enabled)
185-
return;
186-
187207
try
188208
{
189-
this.patchProcessor.Unpatch(patchDelegateMethodInfo);
209+
if (prefix != null)
210+
patchProcessor.Unpatch(prefix);
211+
if (postfix != null)
212+
patchProcessor.Unpatch(postfix);
213+
if (finalizer != null)
214+
patchProcessor.Unpatch(finalizer);
215+
if (transpiler != null)
216+
patchProcessor.Unpatch(transpiler);
190217
Enabled = false;
191218
}
192219
catch (Exception ex)

0 commit comments

Comments
 (0)