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;
});
}
}