2022-04-12 05:20:35 +10:00
|
|
|
|
using HarmonyLib;
|
|
|
|
|
using Mono.CSharp;
|
|
|
|
|
using System;
|
2022-04-22 09:12:38 +10:00
|
|
|
|
using System.Collections.Generic;
|
2021-09-06 23:04:40 +10:00
|
|
|
|
using System.IO;
|
|
|
|
|
using System.Linq;
|
|
|
|
|
using System.Reflection;
|
|
|
|
|
using System.Text;
|
|
|
|
|
using UnityExplorer.CSConsole;
|
2021-12-02 18:35:46 +11:00
|
|
|
|
using UniverseLib;
|
2021-09-06 23:04:40 +10:00
|
|
|
|
|
|
|
|
|
namespace UnityExplorer.Hooks
|
|
|
|
|
{
|
|
|
|
|
public class HookInstance
|
|
|
|
|
{
|
2021-09-07 03:19:40 +10:00
|
|
|
|
// Static
|
|
|
|
|
|
2022-04-22 21:01:47 +10:00
|
|
|
|
//static readonly StringBuilder evalOutput = new();
|
|
|
|
|
static readonly StringBuilder evaluatorOutput;
|
|
|
|
|
static readonly ScriptEvaluator scriptEvaluator = new(new StringWriter(evaluatorOutput = new StringBuilder()));
|
2021-09-06 23:04:40 +10:00
|
|
|
|
|
2021-09-07 19:11:19 +10:00
|
|
|
|
static HookInstance()
|
|
|
|
|
{
|
|
|
|
|
scriptEvaluator.Run("using System;");
|
2022-04-22 09:12:38 +10:00
|
|
|
|
scriptEvaluator.Run("using System.Text;");
|
2021-09-07 19:11:19 +10:00
|
|
|
|
scriptEvaluator.Run("using System.Reflection;");
|
|
|
|
|
scriptEvaluator.Run("using System.Collections;");
|
|
|
|
|
scriptEvaluator.Run("using System.Collections.Generic;");
|
|
|
|
|
}
|
|
|
|
|
|
2021-09-06 23:04:40 +10:00
|
|
|
|
// Instance
|
|
|
|
|
|
|
|
|
|
public bool Enabled;
|
|
|
|
|
public MethodInfo TargetMethod;
|
2021-09-07 17:23:20 +10:00
|
|
|
|
public string PatchSourceCode;
|
2021-09-06 23:04:40 +10:00
|
|
|
|
|
2021-09-07 17:23:20 +10:00
|
|
|
|
private readonly string shortSignature;
|
2021-09-06 23:04:40 +10:00
|
|
|
|
private PatchProcessor patchProcessor;
|
2021-09-07 17:23:20 +10:00
|
|
|
|
|
|
|
|
|
private MethodInfo postfix;
|
|
|
|
|
private MethodInfo prefix;
|
|
|
|
|
private MethodInfo finalizer;
|
|
|
|
|
private MethodInfo transpiler;
|
2021-09-06 23:04:40 +10:00
|
|
|
|
|
|
|
|
|
public HookInstance(MethodInfo targetMethod)
|
|
|
|
|
{
|
|
|
|
|
this.TargetMethod = targetMethod;
|
2022-04-22 09:12:38 +10:00
|
|
|
|
this.shortSignature = TargetMethod.FullDescription();
|
2021-09-07 17:23:20 +10:00
|
|
|
|
|
|
|
|
|
GenerateDefaultPatchSourceCode(targetMethod);
|
|
|
|
|
|
|
|
|
|
if (CompileAndGenerateProcessor(PatchSourceCode))
|
2021-09-07 03:40:09 +10:00
|
|
|
|
Patch();
|
2021-09-06 23:04:40 +10:00
|
|
|
|
}
|
|
|
|
|
|
2021-09-07 03:19:40 +10:00
|
|
|
|
// Evaluator.source_file
|
2022-01-31 21:24:01 +11:00
|
|
|
|
private static readonly FieldInfo fi_sourceFile = AccessTools.Field(typeof(Evaluator), "source_file");
|
2021-09-07 03:19:40 +10:00
|
|
|
|
// TypeDefinition.Definition
|
2022-01-31 21:24:01 +11:00
|
|
|
|
private static readonly PropertyInfo pi_Definition = AccessTools.Property(typeof(TypeDefinition), "Definition");
|
2021-09-07 03:19:40 +10:00
|
|
|
|
|
2021-09-07 17:23:20 +10:00
|
|
|
|
public bool CompileAndGenerateProcessor(string patchSource)
|
2021-09-06 23:04:40 +10:00
|
|
|
|
{
|
2021-09-07 17:23:20 +10:00
|
|
|
|
Unpatch();
|
|
|
|
|
|
2022-04-22 09:12:38 +10:00
|
|
|
|
StringBuilder codeBuilder = new();
|
|
|
|
|
|
2021-09-06 23:04:40 +10:00
|
|
|
|
try
|
|
|
|
|
{
|
2021-11-20 18:57:53 +11:00
|
|
|
|
patchProcessor = ExplorerCore.Harmony.CreateProcessor(TargetMethod);
|
2021-09-06 23:04:40 +10:00
|
|
|
|
|
|
|
|
|
// Dynamically compile the patch method
|
|
|
|
|
|
2022-04-22 21:01:47 +10:00
|
|
|
|
codeBuilder.AppendLine($"static class DynamicPatch_{DateTime.Now.Ticks}");
|
2021-09-07 17:23:20 +10:00
|
|
|
|
codeBuilder.AppendLine("{");
|
|
|
|
|
codeBuilder.AppendLine(patchSource);
|
|
|
|
|
codeBuilder.AppendLine("}");
|
|
|
|
|
|
|
|
|
|
scriptEvaluator.Run(codeBuilder.ToString());
|
2021-09-06 23:04:40 +10:00
|
|
|
|
|
|
|
|
|
if (ScriptEvaluator._reportPrinter.ErrorsCount > 0)
|
2021-09-07 03:40:09 +10:00
|
|
|
|
throw new FormatException($"Unable to compile the generated patch!");
|
2021-09-06 23:04:40 +10:00
|
|
|
|
|
2021-09-07 03:19:40 +10:00
|
|
|
|
// TODO: Publicize MCS to avoid this reflection
|
2021-09-07 17:23:20 +10:00
|
|
|
|
// Get the most recent Patch type in the source file
|
2022-04-12 05:20:35 +10:00
|
|
|
|
TypeContainer typeContainer = ((CompilationSourceFile)fi_sourceFile.GetValue(scriptEvaluator))
|
2021-09-07 17:23:20 +10:00
|
|
|
|
.Containers
|
|
|
|
|
.Last(it => it.MemberName.Name.StartsWith("DynamicPatch_"));
|
|
|
|
|
// Get the TypeSpec from the TypeDefinition, then get its "MetaInfo" (System.Type)
|
2022-04-12 05:20:35 +10:00
|
|
|
|
Type patchClass = ((TypeSpec)pi_Definition.GetValue((Class)typeContainer, null)).GetMetaInfo();
|
2021-09-06 23:04:40 +10:00
|
|
|
|
|
2021-09-07 17:23:20 +10:00
|
|
|
|
// Create the harmony patches as defined
|
|
|
|
|
|
|
|
|
|
postfix = patchClass.GetMethod("Postfix", ReflectionUtility.FLAGS);
|
|
|
|
|
if (postfix != null)
|
|
|
|
|
patchProcessor.AddPostfix(new HarmonyMethod(postfix));
|
|
|
|
|
|
|
|
|
|
prefix = patchClass.GetMethod("Prefix", ReflectionUtility.FLAGS);
|
|
|
|
|
if (prefix != null)
|
|
|
|
|
patchProcessor.AddPrefix(new HarmonyMethod(prefix));
|
|
|
|
|
|
|
|
|
|
finalizer = patchClass.GetMethod("Finalizer", ReflectionUtility.FLAGS);
|
|
|
|
|
if (finalizer != null)
|
|
|
|
|
patchProcessor.AddFinalizer(new HarmonyMethod(finalizer));
|
|
|
|
|
|
|
|
|
|
transpiler = patchClass.GetMethod("Transpiler", ReflectionUtility.FLAGS);
|
|
|
|
|
if (transpiler != null)
|
|
|
|
|
patchProcessor.AddTranspiler(new HarmonyMethod(transpiler));
|
2021-09-07 03:40:09 +10:00
|
|
|
|
|
|
|
|
|
return true;
|
2021-09-06 23:04:40 +10:00
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
2022-04-22 21:01:47 +10:00
|
|
|
|
if (ex is FormatException)
|
|
|
|
|
{
|
|
|
|
|
string output = scriptEvaluator._textWriter.ToString();
|
|
|
|
|
string[] outputSplit = output.Split('\n');
|
|
|
|
|
if (outputSplit.Length >= 2)
|
|
|
|
|
output = outputSplit[outputSplit.Length - 2];
|
|
|
|
|
evaluatorOutput.Clear();
|
|
|
|
|
|
|
|
|
|
if (ScriptEvaluator._reportPrinter.ErrorsCount > 0)
|
|
|
|
|
ExplorerCore.LogWarning($"Unable to compile the code. Evaluator's last output was:\r\n{output}");
|
|
|
|
|
else
|
|
|
|
|
ExplorerCore.LogWarning($"Exception generating patch source code: {ex}");
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
ExplorerCore.LogWarning($"Exception generating patch source code: {ex}");
|
|
|
|
|
|
|
|
|
|
// ExplorerCore.Log(codeBuilder.ToString());
|
2022-04-22 09:12:38 +10:00
|
|
|
|
|
2021-09-07 03:40:09 +10:00
|
|
|
|
return false;
|
2021-09-06 23:04:40 +10:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-04-22 21:01:47 +10:00
|
|
|
|
static string FullDescriptionClean(Type type)
|
|
|
|
|
{
|
|
|
|
|
string description = type.FullDescription().Replace("+", ".");
|
|
|
|
|
if (description.EndsWith("&"))
|
|
|
|
|
description = $"ref {description.Substring(0, description.Length - 1)}";
|
|
|
|
|
return description;
|
|
|
|
|
}
|
|
|
|
|
|
2021-09-07 17:23:20 +10:00
|
|
|
|
private string GenerateDefaultPatchSourceCode(MethodInfo targetMethod)
|
2021-09-06 23:04:40 +10:00
|
|
|
|
{
|
2022-04-12 05:20:35 +10:00
|
|
|
|
StringBuilder codeBuilder = new();
|
2021-09-06 23:04:40 +10:00
|
|
|
|
|
2022-04-22 21:01:47 +10:00
|
|
|
|
codeBuilder.Append("static void Postfix("); // System.Reflection.MethodBase __originalMethod
|
2021-09-06 23:04:40 +10:00
|
|
|
|
|
2022-04-22 09:12:38 +10:00
|
|
|
|
bool isStatic = targetMethod.IsStatic;
|
|
|
|
|
if (!isStatic)
|
2022-04-22 21:01:47 +10:00
|
|
|
|
codeBuilder.Append($"{FullDescriptionClean(targetMethod.DeclaringType)} __instance");
|
2021-09-06 23:04:40 +10:00
|
|
|
|
|
|
|
|
|
if (targetMethod.ReturnType != typeof(void))
|
2022-04-22 09:12:38 +10:00
|
|
|
|
{
|
|
|
|
|
if (!isStatic)
|
|
|
|
|
codeBuilder.Append(", ");
|
2022-04-22 21:01:47 +10:00
|
|
|
|
codeBuilder.Append($"{FullDescriptionClean(targetMethod.ReturnType)} __result");
|
2022-04-22 09:12:38 +10:00
|
|
|
|
}
|
2021-09-06 23:04:40 +10:00
|
|
|
|
|
2022-04-12 05:20:35 +10:00
|
|
|
|
ParameterInfo[] parameters = targetMethod.GetParameters();
|
2021-09-07 19:11:19 +10:00
|
|
|
|
|
|
|
|
|
int paramIdx = 0;
|
2022-04-12 05:20:35 +10:00
|
|
|
|
foreach (ParameterInfo param in parameters)
|
2021-09-06 23:04:40 +10:00
|
|
|
|
{
|
2022-04-22 21:01:47 +10:00
|
|
|
|
codeBuilder.Append($", {FullDescriptionClean(param.ParameterType)} __{paramIdx}");
|
2021-09-06 23:04:40 +10:00
|
|
|
|
paramIdx++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
codeBuilder.Append(")\n");
|
|
|
|
|
|
|
|
|
|
// Patch body
|
|
|
|
|
|
2021-09-07 17:23:20 +10:00
|
|
|
|
codeBuilder.AppendLine("{");
|
|
|
|
|
codeBuilder.AppendLine(" try {");
|
2022-04-22 09:12:38 +10:00
|
|
|
|
codeBuilder.AppendLine(" StringBuilder sb = new StringBuilder();");
|
|
|
|
|
codeBuilder.AppendLine($" sb.AppendLine(\"---- Patched called ----\");");
|
|
|
|
|
codeBuilder.AppendLine($" sb.AppendLine(\"{shortSignature}\");");
|
2021-09-07 17:23:20 +10:00
|
|
|
|
|
2021-09-06 23:04:40 +10:00
|
|
|
|
if (!targetMethod.IsStatic)
|
2022-04-22 09:12:38 +10:00
|
|
|
|
codeBuilder.AppendLine($" sb.Append(\"- __instance: \").AppendLine(__instance.ToString());");
|
2021-09-06 23:04:40 +10:00
|
|
|
|
|
|
|
|
|
paramIdx = 0;
|
2022-04-12 05:20:35 +10:00
|
|
|
|
foreach (ParameterInfo param in parameters)
|
2021-09-06 23:04:40 +10:00
|
|
|
|
{
|
2022-04-22 09:12:38 +10:00
|
|
|
|
codeBuilder.Append($" sb.Append(\"- Parameter {paramIdx} '{param.Name}': \")");
|
|
|
|
|
|
2021-09-07 03:40:09 +10:00
|
|
|
|
Type pType = param.ParameterType;
|
|
|
|
|
if (pType.IsByRef) pType = pType.GetElementType();
|
|
|
|
|
if (pType.IsValueType)
|
2022-04-22 09:12:38 +10:00
|
|
|
|
codeBuilder.AppendLine($".AppendLine(__{paramIdx}.ToString());");
|
2021-09-06 23:04:40 +10:00
|
|
|
|
else
|
2022-04-22 09:12:38 +10:00
|
|
|
|
codeBuilder.AppendLine($".AppendLine(__{paramIdx}?.ToString() ?? \"null\");");
|
|
|
|
|
|
2021-09-06 23:04:40 +10:00
|
|
|
|
paramIdx++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (targetMethod.ReturnType != typeof(void))
|
|
|
|
|
{
|
2022-04-22 09:12:38 +10:00
|
|
|
|
codeBuilder.Append(" sb.Append(\"- Return value: \")");
|
2021-09-06 23:04:40 +10:00
|
|
|
|
if (targetMethod.ReturnType.IsValueType)
|
2022-04-22 09:12:38 +10:00
|
|
|
|
codeBuilder.AppendLine(".AppendLine(__result.ToString());");
|
2021-09-06 23:04:40 +10:00
|
|
|
|
else
|
2022-04-22 09:12:38 +10:00
|
|
|
|
codeBuilder.AppendLine(".AppendLine(__result?.ToString() ?? \"null\");");
|
2021-09-06 23:04:40 +10:00
|
|
|
|
}
|
|
|
|
|
|
2022-04-22 09:12:38 +10:00
|
|
|
|
codeBuilder.AppendLine($" UnityExplorer.ExplorerCore.Log(sb.ToString());");
|
2021-09-07 17:23:20 +10:00
|
|
|
|
codeBuilder.AppendLine(" }");
|
|
|
|
|
codeBuilder.AppendLine(" catch (System.Exception ex) {");
|
|
|
|
|
codeBuilder.AppendLine($" UnityExplorer.ExplorerCore.LogWarning($\"Exception in patch of {shortSignature}:\\n{{ex}}\");");
|
2021-09-06 23:04:40 +10:00
|
|
|
|
codeBuilder.AppendLine(" }");
|
|
|
|
|
|
2021-09-07 17:23:20 +10:00
|
|
|
|
// End patch body
|
2021-09-06 23:04:40 +10:00
|
|
|
|
|
|
|
|
|
codeBuilder.AppendLine("}");
|
|
|
|
|
|
2021-09-07 19:11:19 +10:00
|
|
|
|
//ExplorerCore.Log(codeBuilder.ToString());
|
|
|
|
|
|
2021-09-07 17:23:20 +10:00
|
|
|
|
return PatchSourceCode = codeBuilder.ToString();
|
2021-09-06 23:04:40 +10:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void TogglePatch()
|
|
|
|
|
{
|
2021-09-07 18:06:38 +10:00
|
|
|
|
if (!Enabled)
|
2021-09-06 23:04:40 +10:00
|
|
|
|
Patch();
|
|
|
|
|
else
|
|
|
|
|
Unpatch();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void Patch()
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
patchProcessor.Patch();
|
2021-09-07 18:06:38 +10:00
|
|
|
|
|
2021-09-06 23:04:40 +10:00
|
|
|
|
Enabled = true;
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
|
|
|
|
ExplorerCore.LogWarning($"Exception hooking method!\r\n{ex}");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void Unpatch()
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
2021-09-07 17:23:20 +10:00
|
|
|
|
if (prefix != null)
|
|
|
|
|
patchProcessor.Unpatch(prefix);
|
|
|
|
|
if (postfix != null)
|
|
|
|
|
patchProcessor.Unpatch(postfix);
|
|
|
|
|
if (finalizer != null)
|
|
|
|
|
patchProcessor.Unpatch(finalizer);
|
|
|
|
|
if (transpiler != null)
|
|
|
|
|
patchProcessor.Unpatch(transpiler);
|
2021-09-07 18:06:38 +10:00
|
|
|
|
|
2021-09-06 23:04:40 +10:00
|
|
|
|
Enabled = false;
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
|
|
|
|
ExplorerCore.LogWarning($"Exception unpatching method: {ex}");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|