using System; using System.Linq; using System.Reflection; using System.Reflection.Emit; using HarmonyLib; using NitroxModel.Helper; namespace NitroxPatcher.PatternMatching; public readonly struct InstructionPattern { public bool Equals(InstructionPattern other) => OpCode.Equals(other.OpCode) && Operand.Equals(other.Operand) && Label == other.Label; public override bool Equals(object obj) => obj is InstructionPattern other && Equals(other); public override int GetHashCode() { unchecked { int hashCode = OpCode.GetHashCode(); hashCode = (hashCode * 397) ^ Operand.GetHashCode(); hashCode = (hashCode * 397) ^ (Label != null ? Label.GetHashCode() : 0); return hashCode; } } public OpCodePattern OpCode { get; init; } public OperandPattern Operand { get; init; } public string Label { get; init; } public static implicit operator InstructionPattern(OpCode opCode) => new() { OpCode = opCode }; public static implicit operator InstructionPattern(OperandPattern operand) => new() { Operand = operand }; public static implicit operator InstructionPattern(MethodInfo method) => Call(method, true); public static InstructionPattern Call(string className, string methodName) => new() { OpCode = OpCodes.Call, Operand = new(className, methodName) }; public static InstructionPattern Call(MethodInfo method) => Call(method, false); private static InstructionPattern Call(MethodInfo method, bool matchAnyCallOpcode) { Type methodDeclaringType = method.DeclaringType; Validate.NotNull(methodDeclaringType); return new() { OpCode = new OpCodePattern { OpCode = OpCodes.Call, WeakMatch = matchAnyCallOpcode }, Operand = new(methodDeclaringType.FullName, method.Name, method.GetParameters().Select(p => p.ParameterType).ToArray()) }; } public static bool operator ==(InstructionPattern pattern, CodeInstruction instruction) { if (instruction == null) { return false; } return pattern.OpCode == instruction.opcode && pattern.Operand == instruction.operand; } public static bool operator ==(CodeInstruction instruction, InstructionPattern pattern) { return pattern == instruction; } public static bool operator !=(CodeInstruction instruction, InstructionPattern pattern) { return !(instruction == pattern); } public static bool operator !=(InstructionPattern pattern, CodeInstruction instruction) { return !(pattern == instruction); } public override string ToString() => $"{OpCode.OpCode}{(Operand != default ? $" {Operand}" : "")}{(Label != null ? $" '{Label}'" : "")}"; }