Skip to content

Commit

Permalink
Handle large writes more elegantly, fix overflow on large write for U…
Browse files Browse the repository at this point in the history
…SB (kwsch#105)

* Fix large write final chunk irregular size; socket r/w chunk for large

* Update SwitchUSB.cs
  • Loading branch information
kwsch authored Jan 18, 2021
1 parent df49677 commit c816a65
Showing 5 changed files with 102 additions and 59 deletions.
2 changes: 1 addition & 1 deletion SysBot.Base/Connection/Switch/USB/SwitchUSB.cs
Original file line number Diff line number Diff line change
@@ -195,7 +195,7 @@ private void WriteLarge(byte[] data, uint offset, Func<ulong, byte[], byte[]> me
{
int byteCount = data.Length;
for (int i = 0; i < byteCount; i += MaximumTransferSize)
Write(data.Slice(i, MaximumTransferSize), offset + (uint)i, method);
Write(data.SliceSafe(i, MaximumTransferSize), offset + (uint)i, method);
}

private byte[] ReadLarge(uint offset, int length, Func<ulong, int, byte[]> method)
2 changes: 2 additions & 0 deletions SysBot.Base/Connection/Switch/Wireless/SwitchSocket.cs
Original file line number Diff line number Diff line change
@@ -28,5 +28,7 @@ protected SwitchSocket(IWirelessConnectionConfig wi, SocketType type = SocketTyp
public abstract void Connect();
public abstract void Reset();
public abstract void Disconnect();

protected const int MaximumTransferSize = 468;
}
}
71 changes: 45 additions & 26 deletions SysBot.Base/Connection/Switch/Wireless/SwitchSocketAsync.cs
Original file line number Diff line number Diff line change
@@ -3,6 +3,7 @@
using System.Net.Sockets;
using System.Threading;
using System.Threading.Tasks;
using static SysBot.Base.SwitchOffsetType;

namespace SysBot.Base
{
@@ -84,17 +85,18 @@ public void DisconnectCallback(IAsyncResult ar)
LogUtil.LogInfo("Disconnected.", Name);
}

public int Read(byte[] buffer)
private int Read(byte[] buffer)
{
int br = Connection.Receive(buffer, 0, 1, SocketFlags.None);
while (buffer[br - 1] != (byte)'\n')
br += Connection.Receive(buffer, br, 1, SocketFlags.None);
return br;
}

/// <summary> Only call this if you are sending small commands. </summary>
public async Task<int> SendAsync(byte[] buffer, CancellationToken token) => await Task.Run(() => Connection.Send(buffer), token).ConfigureAwait(false);

public async Task<byte[]> ReadBytesFromCmdAsync(byte[] cmd, int length, CancellationToken token)
private async Task<byte[]> ReadBytesFromCmdAsync(byte[] cmd, int length, CancellationToken token)
{
await SendAsync(cmd, token).ConfigureAwait(false);

@@ -103,20 +105,13 @@ public async Task<byte[]> ReadBytesFromCmdAsync(byte[] cmd, int length, Cancella
return Decoder.ConvertHexByteStringToBytes(buffer);
}

public async Task<byte[]> ReadBytesAsync(uint offset, int length, CancellationToken token)
{
return await ReadBytesFromCmdAsync(SwitchCommand.Peek(offset, length), length, token).ConfigureAwait(false);
}
public async Task<byte[]> ReadBytesAsync(uint offset, int length, CancellationToken token) => await Read(offset, length, Heap, token).ConfigureAwait(false);
public async Task<byte[]> ReadBytesMainAsync(ulong offset, int length, CancellationToken token) => await Read(offset, length, Main, token).ConfigureAwait(false);
public async Task<byte[]> ReadBytesAbsoluteAsync(ulong offset, int length, CancellationToken token) => await Read(offset, length, Absolute, token).ConfigureAwait(false);

public async Task<byte[]> ReadBytesAbsoluteAsync(ulong offset, int length, CancellationToken token)
{
return await ReadBytesFromCmdAsync(SwitchCommand.PeekAbsolute(offset, length), length, token).ConfigureAwait(false);
}

public async Task<byte[]> ReadBytesMainAsync(ulong offset, int length, CancellationToken token)
{
return await ReadBytesFromCmdAsync(SwitchCommand.PeekMain(offset, length), length, token).ConfigureAwait(false);
}
public async Task WriteBytesAsync(byte[] data, uint offset, CancellationToken token) => await Write(data, offset, Heap, token).ConfigureAwait(false);
public async Task WriteBytesMainAsync(byte[] data, ulong offset, CancellationToken token) => await Write(data, offset, Main, token).ConfigureAwait(false);
public async Task WriteBytesAbsoluteAsync(byte[] data, ulong offset, CancellationToken token) => await Write(data, offset, Absolute, token).ConfigureAwait(false);

public async Task<ulong> GetMainNsoBaseAsync(CancellationToken token)
{
@@ -132,22 +127,46 @@ public async Task<ulong> GetHeapBaseAsync(CancellationToken token)
return BitConverter.ToUInt64(baseBytes, 0);
}

public async Task WriteBytesAsync(byte[] data, uint offset, CancellationToken token)
private async Task<byte[]> Read(ulong offset, int length, SwitchOffsetType type, CancellationToken token)
{
var cmd = SwitchCommand.Poke(offset, data);
await SendAsync(cmd, token).ConfigureAwait(false);
}
var method = type.GetReadMethod();
if (length <= MaximumTransferSize)
{
var cmd = method(offset, length);
return await ReadBytesFromCmdAsync(cmd, length, token).ConfigureAwait(false);
}

public async Task WriteBytesMainAsync(byte[] data, ulong offset, CancellationToken token)
{
var cmd = SwitchCommand.PokeMain(offset, data);
await SendAsync(cmd, token).ConfigureAwait(false);
byte[] result = new byte[length];
for (int i = 0; i < length; i += MaximumTransferSize)
{
int len = MaximumTransferSize;
int delta = length - i;
if (delta < MaximumTransferSize)
len = delta;

var cmd = method(offset + (uint)i, len);
var bytes = await ReadBytesFromCmdAsync(cmd, len, token).ConfigureAwait(false);
bytes.CopyTo(result, i);
}
return result;
}

public async Task WriteBytesAbsoluteAsync(byte[] data, ulong offset, CancellationToken token)
private async Task Write(byte[] data, ulong offset, SwitchOffsetType type, CancellationToken token)
{
var cmd = SwitchCommand.PokeAbsolute(offset, data);
await SendAsync(cmd, token).ConfigureAwait(false);
var method = type.GetWriteMethod();
if (data.Length <= MaximumTransferSize)
{
var cmd = method(offset, data);
await SendAsync(cmd, token).ConfigureAwait(false);
return;
}
int byteCount = data.Length;
for (int i = 0; i < byteCount; i += MaximumTransferSize)
{
var slice = data.SliceSafe(i, MaximumTransferSize);
var cmd = method(offset + (uint)i, slice);
await SendAsync(cmd, token).ConfigureAwait(false);
}
}
}
}
80 changes: 49 additions & 31 deletions SysBot.Base/Connection/Switch/Wireless/SwitchSocketSync.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using System.Threading;
using static SysBot.Base.SwitchOffsetType;

namespace SysBot.Base
{
@@ -13,6 +14,9 @@ public sealed class SwitchSocketSync : SwitchSocket, ISwitchConnectionSync
{
public SwitchSocketSync(IWirelessConnectionConfig cfg) : base(cfg) { }

public int BaseDelay { get; set; } = 64;
public int DelayFactor { get; set; } = 256;

public override void Connect()
{
Log("Connecting to device...");
@@ -35,18 +39,9 @@ public override void Disconnect()
Log("Disconnected!");
}

public int Read(byte[] buffer) => Connection.Receive(buffer);
private int Read(byte[] buffer) => Connection.Receive(buffer);
public int Send(byte[] buffer) => Connection.Send(buffer);

private const int BaseDelay = 64;
private const int DelayFactor = 256;

public byte[] ReadBytes(uint offset, int length)
{
Send(SwitchCommand.Peek(offset, length));
return ReadResponse(length);
}

private byte[] ReadResponse(int length)
{
// give it time to push data back
@@ -56,13 +51,6 @@ private byte[] ReadResponse(int length)
return Decoder.ConvertHexByteStringToBytes(buffer);
}

public void WriteBytes(byte[] data, uint offset)
{
Send(SwitchCommand.Poke(offset, data));
// give it time to push data back
Thread.Sleep((data.Length / DelayFactor) + BaseDelay);
}

public ulong GetMainNsoBase()
{
Send(SwitchCommand.GetMainNsoBase());
@@ -79,26 +67,56 @@ public ulong GetHeapBase()
return BitConverter.ToUInt64(baseBytes, 0);
}

public byte[] ReadBytesMain(ulong offset, int length)
{
Send(SwitchCommand.PeekMain(offset, length));
return ReadResponse(length);
}
public byte[] ReadBytes(uint offset, int length) => Read(offset, length, Heap);
public byte[] ReadBytesMain(ulong offset, int length) => Read(offset, length, Main);
public byte[] ReadBytesAbsolute(ulong offset, int length) => Read(offset, length, Absolute);

public byte[] ReadBytesAbsolute(ulong offset, int length)
{
Send(SwitchCommand.PeekAbsolute(offset, length));
return ReadResponse(length);
}
public void WriteBytes(byte[] data, uint offset) => Write(data, offset, Heap);
public void WriteBytesMain(byte[] data, ulong offset) => Write(data, offset, Main);
public void WriteBytesAbsolute(byte[] data, ulong offset) => Write(data, offset, Absolute);

public void WriteBytesMain(byte[] data, ulong offset)
private byte[] Read(ulong offset, int length, SwitchOffsetType type)
{
Send(SwitchCommand.PokeMain(offset, data));
var method = type.GetReadMethod();
if (length <= MaximumTransferSize)
{
var cmd = method(offset, length);
Send(cmd);
return ReadResponse(length);
}

byte[] result = new byte[length];
for (int i = 0; i < length; i += MaximumTransferSize)
{
int len = MaximumTransferSize;
int delta = length - i;
if (delta < MaximumTransferSize)
len = delta;

var cmd = method(offset + (uint)i, len);
Send(cmd);
var bytes = ReadResponse(length);
bytes.CopyTo(result, i);
}
return result;
}

public void WriteBytesAbsolute(byte[] data, ulong offset)
private void Write(byte[] data, ulong offset, SwitchOffsetType type)
{
Send(SwitchCommand.PokeAbsolute(offset, data));
var method = type.GetWriteMethod();
if (data.Length <= MaximumTransferSize)
{
var cmd = method(offset, data);
Send(cmd);
return;
}
int byteCount = data.Length;
for (int i = 0; i < byteCount; i += MaximumTransferSize)
{
var slice = data.SliceSafe(i, MaximumTransferSize);
var cmd = method(offset + (uint)i, slice);
Send(cmd);
}
}
}
}
6 changes: 5 additions & 1 deletion SysBot.Base/Util/ArrayUtil.cs
Original file line number Diff line number Diff line change
@@ -4,8 +4,12 @@ namespace SysBot.Base
{
internal static class ArrayUtil
{
public static byte[] Slice(this byte[] src, int offset, int length)
public static byte[] SliceSafe(this byte[] src, int offset, int length)
{
var delta = src.Length - offset;
if (delta < length)
length = delta;

byte[] data = new byte[length];
Buffer.BlockCopy(src, offset, data, 0, data.Length);
return data;

0 comments on commit c816a65

Please sign in to comment.