-
Notifications
You must be signed in to change notification settings - Fork 0
Method Body Reader
BlazingTwist edited this page Dec 19, 2022
·
1 revision
Lets you read the instructions of Methods without needing a Transpiler.
Suppose there is a method with a complicated section that you want to copy to a different method.
public class ExampleClass {
public static void ExampleMethod(int input) {
Console.WriteLine("msg0");
int importantValue = (int) Math.Max(input * 12.2f, input + 10);
Console.WriteLine("msg1 - " + importantValue);
}
}
You don't want to call ExampleMethod, because it contains a lot more code than you need.
Ideally you could make a copy of just the code you care about, like this:
public class MyClass {
public static int ImportantValueCalculator(int input) {
return (int) Math.Max(input * 12.2f, input + 10);
}
}
Also, you know that several Modders are patching that section, because it's really that important.
Let's write a transpiler that copies the target section, including changes from other Mods, to our own Method.
public class MyClass {
public static int ImportantValueCalculator(int input) {
throw new InvalidProgramException("Harmony failed to generate the method body.");
}
}
[HarmonyPatch(declaringType: typeof(MyClass))]
public class Example_Patch {
private static readonly ManualLogSource logger = Logger.CreateLogSource(nameof(Example_Patch));
[HarmonyPatch(methodName: nameof(MyClass.ImportantValueCalculator))]
[HarmonyTranspiler]
private static IEnumerable<CodeInstruction> ImportantValueCalculator_Transpiler(IEnumerable<CodeInstruction> originalInstructions) {
MethodBodyReader reader = new MethodBodyReader(AccessTools.DeclaredMethod(typeof(ExampleClass), nameof(ExampleClass.ExampleMethod)));
List<CodeInstruction> exampleMethodInstructions = reader.ReadInstructions();
List<InstructionMask> prefixSequence = new List<InstructionMask> {
InstructionMask.MatchInstruction(OpCodes.Ldstr, "msg0"),
InstructionMask.MatchOpCode(OpCodes.Call),
};
List<InstructionMask> postfixSequence = new List<InstructionMask> {
InstructionMask.MatchOpCode(OpCodes.Stloc),
InstructionMask.MatchInstruction(OpCodes.Ldstr, "msg1 - "),
};
List<int> prefixSequenceIndices = InstructionUtils.FindInstructionSequence(exampleMethodInstructions, prefixSequence);
if (prefixSequenceIndices.Count != 1) {
logger.LogError($"ImportantValueCalculator_Transpiler - expected 1 match for prefix sequence, but found: {prefixSequenceIndices.Count}");
return originalInstructions;
}
List<int> postfixSequenceIndices = InstructionUtils.FindInstructionSequence(exampleMethodInstructions, postfixSequence);
if (postfixSequenceIndices.Count != 1) {
logger.LogError($"ImportantValueCalculator_Transpiler - expected 1 match for postfix sequence, but found: {postfixSequenceIndices.Count}");
return originalInstructions;
}
List<CodeInstruction> resultInstructions = new List<CodeInstruction>();
resultInstructions.AddRange(exampleMethodInstructions.GetRange(
prefixSequenceIndices[0] + prefixSequence.Count,
postfixSequenceIndices[0] - (prefixSequenceIndices[0] + prefixSequence.Count)
));
resultInstructions.Add(new CodeInstruction(OpCodes.Ret));
return resultInstructions;
}
}
- Instruction Mask - recommended for matching CodeInstructions
Made with ❤️ by BlazingTwist
If you like my work and wish to support me, a
goes a long way