using System; using System.Collections.Generic; using System.Reflection; using HarmonyLib; namespace NzbDrone.RuntimePatches { public abstract class RuntimePatchBase { private Harmony _harmony; internal static bool IsDebug; public virtual bool ShouldPatch() => true; protected abstract void Patch(); public void Patch(Harmony harmony) { _harmony = harmony; if (ShouldPatch()) { Patch(); } } protected const BindingFlags DefaultBindingFlags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static; protected static MethodInfo FindMethod(Type type, string methodName, params string[] paramTypes) { foreach (var methodInfo in type.GetMethods(DefaultBindingFlags)) { if (methodInfo.Name != methodName) { continue; } var parameters = methodInfo.GetParameters(); if (parameters.Length != paramTypes.Length) { continue; } var parametersMatch = true; for (var i = 0; i < parameters.Length; i++) { if (parameters[i].ParameterType.Name != paramTypes[i] && parameters[i].ParameterType.FullName != paramTypes[i] && parameters[i].ParameterType.GetSimplifiedName() != paramTypes[i] && parameters[i].ParameterType.GetSimplifiedName(true) != paramTypes[i]) { parametersMatch = false; break; } } if (!parametersMatch) { continue; } return methodInfo; } return null; } protected void PatchMethod(MethodInfo methodInfo) { var prefix = GetPatchMethod("Prefix_" + methodInfo.Name); var postfix = GetPatchMethod("Postfix_" + methodInfo.Name); var transpiler = GetPatchMethod("Transpiler_" + methodInfo.Name); _harmony.Patch(methodInfo, prefix, postfix, transpiler); } protected void TryPatchMethod(string typeName, string methodName, params string[] paramTypes) { var type = Type.GetType(typeName); if (type != null) { TryPatchMethod(type, "GetSslServer"); } else { Debug($"Skipped patching method {typeName}.{methodName}: Type not found"); } } protected void TryPatchMethod(Type type, string methodName, params string[] paramTypes) { var methodInfo = FindMethod(type, methodName, paramTypes); if (methodInfo != null) { PatchMethod(methodInfo); } else { Debug($"Skipped patching method {type.GetSimplifiedName()}.{methodName}: Method not found"); } } private HarmonyMethod GetPatchMethod(string name) { var patch = GetType().GetMethod(name, DefaultBindingFlags); if (patch != null) { return new HarmonyMethod(patch); } return null; } protected void DebugOpcodes(string prefix, List codes) { if (IsDebug) { Log($"Opcodes {prefix}:"); foreach (var code in codes) { Console.WriteLine($" {code}"); } } } protected void Debug(string log) { if (IsDebug) { Log(log); } } protected void Error(string log) { Log(log); } protected virtual void Log(string log) { Console.WriteLine($"RuntimePatch {GetType().Name}: {log}"); } } }