using System.Reflection; using HarmonyLib; namespace SPTarkov.Reflection.Patching; public abstract class AbstractPatch { private readonly Harmony _harmony; private readonly List _prefixList; private readonly List _postfixList; private readonly List _transpilerList; private readonly List _finalizerList; private readonly List _ilManipulatorList; /// /// Constructor /// /// Name protected AbstractPatch(string? name = null) { _harmony = new Harmony(name ?? GetType().Name); _prefixList = GetPatchMethods(typeof(PatchPrefixAttribute)); _postfixList = GetPatchMethods(typeof(PatchPostfixAttribute)); _transpilerList = GetPatchMethods(typeof(PatchTranspilerAttribute)); _finalizerList = GetPatchMethods(typeof(PatchFinalizerAttribute)); _ilManipulatorList = GetPatchMethods(typeof(PatchIlManipulatorAttribute)); if (_prefixList.Count == 0 && _postfixList.Count == 0 && _transpilerList.Count == 0 && _finalizerList.Count == 0 && _ilManipulatorList.Count == 0) { throw new Exception($"{_harmony.Id}: At least one of the patch methods must be specified"); } } /// /// Get original method /// /// Method protected abstract MethodBase GetTargetMethod(); /// /// Get HarmonyMethod from string /// /// Attribute type /// Method private List GetPatchMethods(Type attributeType) { var T = GetType(); var methods = new List(); foreach (var method in T.GetMethods(BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.DeclaredOnly)) { if (method.GetCustomAttribute(attributeType) != null) { methods.Add(new HarmonyMethod(method)); } } return methods; } /// /// Apply patch to target /// public void Enable() { var target = GetTargetMethod(); if (target == null) { throw new InvalidOperationException($"{_harmony.Id}: TargetMethod is null"); } try { foreach (var prefix in _prefixList) { _harmony.Patch(target, prefix: prefix); } foreach (var postfix in _postfixList) { _harmony.Patch(target, postfix: postfix); } foreach (var transpiler in _transpilerList) { _harmony.Patch(target, transpiler: transpiler); } foreach (var finalizer in _finalizerList) { _harmony.Patch(target, finalizer: finalizer); } foreach (var ilmanipulator in _ilManipulatorList) { _harmony.Patch(target, ilmanipulator: ilmanipulator); } } catch (Exception ex) { throw new Exception($"{_harmony.Id}:", ex); } } /// /// Remove applied patch from target /// public void Disable() { var target = GetTargetMethod(); if (target == null) { throw new InvalidOperationException($"{_harmony.Id}: TargetMethod is null"); } try { _harmony.Unpatch(target, HarmonyPatchType.All, _harmony.Id); } catch (Exception ex) { throw new Exception($"{_harmony.Id}:", ex); } } }