using System; using System.Collections.Generic; using System.Text; using System.Text.RegularExpressions; using HarmonyLib; namespace NitroxPatcher.PatternMatching; internal static class ILExtensions { private static readonly Regex spaceRegex = new(Regex.Escape(" ")); /// /// Makes a string of an indexed list of instructions, line by line, formatted to have all opcodes and operand aligned in columns. /// public static string ToPrettyString(this IEnumerable instructions) { List instructionList = [.. instructions]; if (instructionList.Count == 0) { return "No instructions"; } int tenPower = 0; int count = instructionList.Count; while (count > 10) { count /= 10; tenPower++; } // if tenPower is 1 (number between 10 and 99), there are 2 numbers to show so we always add 1 to tenPower string format = $"D{tenPower + 1}"; // We need to find the max length of the opcodes to have all of them take the same amount of space int opcodeMaxLength = 0; foreach (CodeInstruction instruction in instructionList) { int length = instruction.opcode.ToString().Length; if (length > opcodeMaxLength) { opcodeMaxLength = length; } } StringBuilder builder = new(); for (int i = 0; i < instructionList.Count; i++) { CodeInstruction instruction = instructionList[i]; // We add 2 so the text is more readable int spacesRequired = 2 + Math.Max(0, opcodeMaxLength - instruction.opcode.ToString().Length); string instructionToString = spaceRegex.Replace(instruction.ToString(), new string(' ', spacesRequired), 1); builder.AppendLine($"{i.ToString(format)} {instructionToString}"); } return builder.ToString(); } /// /// Iterates the instructions, searching for the given pattern. When the pattern matches, the transform function is /// called. If the pattern does not match the expected match count , /// an exception is thrown. /// public static IEnumerable Transform(this IEnumerable instructions, InstructionsPattern pattern, Func> transform) { return pattern.ApplyTransform(instructions, transform); } /// public static IEnumerable Transform(this IEnumerable instructions, InstructionsPattern pattern, Action transform) { return pattern.ApplyTransform(instructions, (label, instruction) => { transform(label, instruction); return null; }); } /// /// Inserts the new instructions on every occurence of the marker, as defined by the pattern. /// /// Code with the additions. public static IEnumerable InsertAfterMarker(this IEnumerable instructions, InstructionsPattern pattern, string marker, CodeInstruction[] newInstructions) { return pattern.ApplyTransform(instructions, (m, _) => { if (m.Equals(marker, StringComparison.Ordinal)) { return newInstructions; } return null; }); } /// /// Calls the action on each instruction matching the given marker, as defined by the /// pattern. /// public static IEnumerable ChangeAtMarker(this IEnumerable instructions, InstructionsPattern pattern, string marker, Action instructionChange) { return pattern.ApplyTransform(instructions, (m, instruction) => { if (m.Equals(marker, StringComparison.Ordinal)) { instructionChange(instruction); } return null; }); } }