|
|
|
@ -0,0 +1,96 @@
|
|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.IO.Compression;
|
|
|
|
|
using System.Linq;
|
|
|
|
|
using System.Reflection;
|
|
|
|
|
using System.Reflection.Emit;
|
|
|
|
|
using HarmonyLib;
|
|
|
|
|
|
|
|
|
|
namespace NzbDrone.RuntimePatches.Mono
|
|
|
|
|
{
|
|
|
|
|
// Mono 6.0 - 6.x bug 16122
|
|
|
|
|
// Unimplemented method used in GzipStream initiated via the http stack, the method existed as far back as 5.10
|
|
|
|
|
public class DeflateStreamAsyncPatch : MonoRuntimePatchBase
|
|
|
|
|
{
|
|
|
|
|
private static DeflateStreamAsyncPatch Instance;
|
|
|
|
|
|
|
|
|
|
public override Version MonoMinVersion => new Version(6, 0);
|
|
|
|
|
public override Version MonoMaxVersion => new Version(6, 0, 0, 334);
|
|
|
|
|
|
|
|
|
|
protected override void Patch()
|
|
|
|
|
{
|
|
|
|
|
Instance = this;
|
|
|
|
|
|
|
|
|
|
TryPatchMethod(typeof(DeflateStream), "ReadAsyncMemory", "Memory<Byte>", "CancellationToken");
|
|
|
|
|
TryPatchMethod(typeof(DeflateStream), "WriteAsyncMemory", "ReadOnlyMemory<Byte>", "CancellationToken");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// We need a Transpiler coz these methods are for net4.7.2 so we cannot access the types directly
|
|
|
|
|
|
|
|
|
|
// internal ValueTask<int> ReadAsyncMemory(Memory<byte> destination, CancellationToken cancellationToken)
|
|
|
|
|
// {
|
|
|
|
|
// - throw new NotImplementedException();
|
|
|
|
|
// + return base.ReadAsync(destination, cancellationToken);
|
|
|
|
|
// }
|
|
|
|
|
static IEnumerable<CodeInstruction> Transpiler_ReadAsyncMemory(IEnumerable<CodeInstruction> instructions, MethodBase method)
|
|
|
|
|
{
|
|
|
|
|
var codes = instructions.ToList();
|
|
|
|
|
|
|
|
|
|
var patchable = codes.Matches(OpCodes.Newobj, OpCodes.Throw);
|
|
|
|
|
|
|
|
|
|
var readAsync = method.DeclaringType.BaseType.GetMethod("ReadAsync", method.GetParameterTypes());
|
|
|
|
|
|
|
|
|
|
if (patchable && readAsync != null)
|
|
|
|
|
{
|
|
|
|
|
codes.Clear();
|
|
|
|
|
|
|
|
|
|
codes.Add(new CodeInstruction(OpCodes.Ldarg_0));
|
|
|
|
|
codes.Add(new CodeInstruction(OpCodes.Ldarg_1));
|
|
|
|
|
codes.Add(new CodeInstruction(OpCodes.Ldarg_2));
|
|
|
|
|
codes.Add(new CodeInstruction(OpCodes.Call, readAsync));
|
|
|
|
|
codes.Add(new CodeInstruction(OpCodes.Ret));
|
|
|
|
|
|
|
|
|
|
Instance.Debug($"Patch applied to method {method.GetSimplifiedName()}");
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
Instance.Error($"Skipped patching method {method.GetSimplifiedName()}: Method construct different than expected");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return codes;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// internal ValueTask WriteAsyncMemory(ReadOnlyMemory<byte> source, CancellationToken cancellationToken)
|
|
|
|
|
// {
|
|
|
|
|
// - throw new NotImplementedException();
|
|
|
|
|
// + return base.WriteAsync(source, cancellationToken);
|
|
|
|
|
// }
|
|
|
|
|
static IEnumerable<CodeInstruction> Transpiler_WriteAsyncMemory(IEnumerable<CodeInstruction> instructions, MethodBase method)
|
|
|
|
|
{
|
|
|
|
|
var codes = instructions.ToList();
|
|
|
|
|
|
|
|
|
|
var patchable = codes.Matches(OpCodes.Newobj, OpCodes.Throw);
|
|
|
|
|
|
|
|
|
|
var writeAsync = method.DeclaringType.BaseType.GetMethod("WriteAsync", method.GetParameterTypes());
|
|
|
|
|
|
|
|
|
|
if (patchable && writeAsync != null)
|
|
|
|
|
{
|
|
|
|
|
codes.Clear();
|
|
|
|
|
|
|
|
|
|
codes.Add(new CodeInstruction(OpCodes.Ldarg_0));
|
|
|
|
|
codes.Add(new CodeInstruction(OpCodes.Ldarg_1));
|
|
|
|
|
codes.Add(new CodeInstruction(OpCodes.Ldarg_2));
|
|
|
|
|
codes.Add(new CodeInstruction(OpCodes.Call, writeAsync));
|
|
|
|
|
codes.Add(new CodeInstruction(OpCodes.Ret));
|
|
|
|
|
|
|
|
|
|
Instance.Debug($"Patch applied to method {method.GetSimplifiedName()}");
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
Instance.Error($"Skipped patching method {method.GetSimplifiedName()}: Method construct different than expected");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return codes;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|