diff --git a/src/RioJoy.Core/Hid/RioHidReport.cs b/src/RioJoy.Core/Hid/RioHidReport.cs
index b0df9fe..1794208 100644
--- a/src/RioJoy.Core/Hid/RioHidReport.cs
+++ b/src/RioJoy.Core/Hid/RioHidReport.cs
@@ -43,10 +43,7 @@ public sealed class RioHidReport
SetHat(RioHat.Centered);
}
- /// The current report bytes (length ).
- public ReadOnlySpan Bytes => _buffer;
-
- /// Copy of the current report bytes.
+ /// Copy of the current report bytes (length ).
public byte[] ToArray() => (byte[])_buffer.Clone();
/// Set an axis value (clamped to 0..), little-endian.
diff --git a/src/RioJoy.Core/Overlay/OverlayTemplateStore.cs b/src/RioJoy.Core/Overlay/OverlayTemplateStore.cs
index 720e0ab..1558b5a 100644
--- a/src/RioJoy.Core/Overlay/OverlayTemplateStore.cs
+++ b/src/RioJoy.Core/Overlay/OverlayTemplateStore.cs
@@ -1,5 +1,5 @@
-using System.Text.Json;
-using System.Text.Json.Serialization;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Converters;
namespace RioJoy.Core.Overlay;
@@ -11,24 +11,24 @@ namespace RioJoy.Core.Overlay;
///
public static class OverlayTemplateStore
{
- private static readonly JsonSerializerOptions Options = new()
+ private static readonly JsonSerializerSettings Options = new()
{
- WriteIndented = true,
- DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
- Converters = { new JsonStringEnumConverter() },
+ Formatting = Formatting.Indented,
+ NullValueHandling = NullValueHandling.Ignore,
+ Converters = { new StringEnumConverter() },
};
public static string Serialize(OverlayTemplate template)
{
if (template is null) throw new ArgumentNullException(nameof(template));
- return JsonSerializer.Serialize(template, Options);
+ return JsonConvert.SerializeObject(template, Options);
}
public static OverlayTemplate Deserialize(string json)
{
if (string.IsNullOrWhiteSpace(json)) throw new ArgumentException("Value cannot be null or whitespace.", nameof(json));
- return JsonSerializer.Deserialize(json, Options)
- ?? throw new JsonException("Overlay template JSON deserialized to null.");
+ return JsonConvert.DeserializeObject(json, Options)
+ ?? throw new JsonSerializationException("Overlay template JSON deserialized to null.");
}
public static void Save(OverlayTemplate template, string path)
diff --git a/src/RioJoy.Core/Plasma/PlasmaDisplay.cs b/src/RioJoy.Core/Plasma/PlasmaDisplay.cs
index 3e8fe69..99eed11 100644
--- a/src/RioJoy.Core/Plasma/PlasmaDisplay.cs
+++ b/src/RioJoy.Core/Plasma/PlasmaDisplay.cs
@@ -48,5 +48,5 @@ public sealed class PlasmaDisplay
}
private Task WriteAsync(byte[] data, CancellationToken ct) =>
- _transport.WriteAsync(data, ct).AsTask();
+ _transport.WriteAsync(data, ct);
}
diff --git a/src/RioJoy.Core/Profiles/ConfigStore.cs b/src/RioJoy.Core/Profiles/ConfigStore.cs
index a3f903b..54cabb7 100644
--- a/src/RioJoy.Core/Profiles/ConfigStore.cs
+++ b/src/RioJoy.Core/Profiles/ConfigStore.cs
@@ -1,32 +1,36 @@
-using System.Text.Json;
-using System.Text.Json.Serialization;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Converters;
namespace RioJoy.Core.Profiles;
///
/// Loads and saves as JSON. Replaces the legacy
/// SimpleIni single-file config with a profile library (see docs/PLAN.md).
+/// Newtonsoft rather than System.Text.Json so the net40 (Windows XP) flavor
+/// shares one serializer — and one on-disk format — with net48 (PLAN.md §8A).
+/// Conventions match the original STJ settings: indented, PascalCase names,
+/// enums as strings, nulls skipped — existing config.json files parse as-is.
///
public static class ConfigStore
{
- private static readonly JsonSerializerOptions Options = new()
+ private static readonly JsonSerializerSettings Options = new()
{
- WriteIndented = true,
- DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
- Converters = { new JsonStringEnumConverter() },
+ Formatting = Formatting.Indented,
+ NullValueHandling = NullValueHandling.Ignore,
+ Converters = { new StringEnumConverter() },
};
public static string Serialize(AppConfig config)
{
if (config is null) throw new ArgumentNullException(nameof(config));
- return JsonSerializer.Serialize(config, Options);
+ return JsonConvert.SerializeObject(config, Options);
}
public static AppConfig Deserialize(string json)
{
if (string.IsNullOrWhiteSpace(json)) throw new ArgumentException("Value cannot be null or whitespace.", nameof(json));
- return JsonSerializer.Deserialize(json, Options)
- ?? throw new JsonException("Config JSON deserialized to null.");
+ return JsonConvert.DeserializeObject(json, Options)
+ ?? throw new JsonSerializationException("Config JSON deserialized to null.");
}
/// Save the config to (creating directories).
diff --git a/src/RioJoy.Core/Protocol/AnalogReport.cs b/src/RioJoy.Core/Protocol/AnalogReport.cs
index 9013957..77934d3 100644
--- a/src/RioJoy.Core/Protocol/AnalogReport.cs
+++ b/src/RioJoy.Core/Protocol/AnalogReport.cs
@@ -44,8 +44,9 @@ public readonly struct AnalogReport
/// 0xFE (), which the RIO uses as an
/// "invalid sample" sentinel — the legacy code ignores such replies.
///
- public static bool TryParse(ReadOnlySpan payload, out AnalogReport report)
+ public static bool TryParse(byte[] payload, out AnalogReport report)
{
+ if (payload is null) throw new ArgumentNullException(nameof(payload));
if (payload.Length != RioCommandTable.PayloadLength(RioCommand.AnalogReply))
throw new ArgumentException(
$"AnalogReply payload must be {RioCommandTable.PayloadLength(RioCommand.AnalogReply)} bytes.",
diff --git a/src/RioJoy.Core/Protocol/PacketBuilder.cs b/src/RioJoy.Core/Protocol/PacketBuilder.cs
index e8d915b..902e093 100644
--- a/src/RioJoy.Core/Protocol/PacketBuilder.cs
+++ b/src/RioJoy.Core/Protocol/PacketBuilder.cs
@@ -13,8 +13,9 @@ public static class PacketBuilder
/// . The payload length must match the command's
/// entry in the length table.
///
- public static byte[] Build(RioCommand command, ReadOnlySpan payload)
+ public static byte[] Build(RioCommand command, byte[] payload)
{
+ if (payload is null) throw new ArgumentNullException(nameof(payload));
int expected = RioCommandTable.PayloadLength(command);
if (payload.Length != expected)
throw new ArgumentException(
@@ -23,13 +24,15 @@ public static class PacketBuilder
var packet = new byte[1 + expected + 1];
packet[0] = (byte)command;
- payload.CopyTo(packet.AsSpan(1));
- packet[^1] = RioChecksum.Compute(packet.AsSpan(0, 1 + expected));
+ Array.Copy(payload, 0, packet, 1, expected);
+ packet[packet.Length - 1] = RioChecksum.Compute(packet, 0, 1 + expected);
return packet;
}
/// Build a zero-payload command packet (CheckRequest etc.).
- public static byte[] Build(RioCommand command) => Build(command, ReadOnlySpan.Empty);
+ public static byte[] Build(RioCommand command) => Build(command, EmptyPayload);
+
+ private static readonly byte[] EmptyPayload = new byte[0];
// --- Convenience builders for the PC → RIO commands ---------------------
@@ -40,12 +43,12 @@ public static class PacketBuilder
public static byte[] AnalogRequest() => Build(RioCommand.AnalogRequest);
public static byte[] ResetRequest(RioResetTarget target) =>
- Build(RioCommand.ResetRequest, stackalloc byte[] { (byte)target });
+ Build(RioCommand.ResetRequest, new[] { (byte)target });
///
/// LampRequest: payload is [lamp#, state]. The state byte is a
/// 7-bit lamp-state value (see ).
///
public static byte[] LampRequest(byte lampNumber, byte state) =>
- Build(RioCommand.LampRequest, stackalloc byte[] { lampNumber, state });
+ Build(RioCommand.LampRequest, new[] { lampNumber, state });
}
diff --git a/src/RioJoy.Core/Protocol/PacketParser.cs b/src/RioJoy.Core/Protocol/PacketParser.cs
index 9dbc7ed..10ea04c 100644
--- a/src/RioJoy.Core/Protocol/PacketParser.cs
+++ b/src/RioJoy.Core/Protocol/PacketParser.cs
@@ -92,15 +92,17 @@ public sealed class PacketParser
}
///
- /// Feed a chunk of received bytes, invoking for
- /// each event produced, in order.
+ /// Feed the first received bytes of
+ /// , invoking for each
+ /// event produced, in order.
///
- public void Feed(ReadOnlySpan data, Action onEvent)
+ public void Feed(byte[] data, int count, Action onEvent)
{
+ if (data is null) throw new ArgumentNullException(nameof(data));
if (onEvent is null) throw new ArgumentNullException(nameof(onEvent));
- foreach (byte ch in data)
+ for (int i = 0; i < count; i++)
{
- if (Feed(ch, out RioRxEvent ev))
+ if (Feed(data[i], out RioRxEvent ev))
onEvent(ev);
}
}
@@ -110,11 +112,12 @@ public sealed class PacketParser
// _buffer = [command][payload…][checksum]; _count includes all of them.
int bodyLen = _count - 1; // command + payload (exclude checksum)
byte receivedChecksum = _buffer[bodyLen];
- byte computed = RioChecksum.Compute(_buffer.AsSpan(0, bodyLen));
+ byte computed = RioChecksum.Compute(_buffer, 0, bodyLen);
bool checksumValid = computed == receivedChecksum;
var command = (RioCommand)_buffer[0];
- var payload = _buffer.AsSpan(1, bodyLen - 1).ToArray(); // copy out; buffer is reused
+ var payload = new byte[bodyLen - 1]; // copy out; buffer is reused
+ Array.Copy(_buffer, 1, payload, 0, payload.Length);
var packet = new RioPacket(command, payload);
Reset();
diff --git a/src/RioJoy.Core/Protocol/RioChecksum.cs b/src/RioJoy.Core/Protocol/RioChecksum.cs
index 2664cf4..8c46f0a 100644
--- a/src/RioJoy.Core/Protocol/RioChecksum.cs
+++ b/src/RioJoy.Core/Protocol/RioChecksum.cs
@@ -9,15 +9,22 @@ namespace RioJoy.Core.Protocol;
public static class RioChecksum
{
///
- /// Compute the 7-bit checksum over
- /// (the command byte followed by its payload bytes — not the checksum byte).
+ /// Compute the 7-bit checksum over the command byte followed by its payload
+ /// bytes (not the checksum byte): bytes of
+ /// starting at .
///
- public static byte Compute(ReadOnlySpan commandAndPayload)
+ public static byte Compute(byte[] commandAndPayload, int offset, int count)
{
+ if (commandAndPayload is null) throw new ArgumentNullException(nameof(commandAndPayload));
+
byte sum = 0;
- foreach (byte b in commandAndPayload)
- sum += (byte)(b & 0x7F);
+ for (int i = offset; i < offset + count; i++)
+ sum += (byte)(commandAndPayload[i] & 0x7F);
return (byte)(sum & 0x7F);
}
+
+ /// Compute the checksum over all of .
+ public static byte Compute(byte[] commandAndPayload) =>
+ Compute(commandAndPayload, 0, commandAndPayload?.Length ?? 0);
}
diff --git a/src/RioJoy.Core/Protocol/RioPacket.cs b/src/RioJoy.Core/Protocol/RioPacket.cs
index 8be56fd..c211ed9 100644
--- a/src/RioJoy.Core/Protocol/RioPacket.cs
+++ b/src/RioJoy.Core/Protocol/RioPacket.cs
@@ -11,13 +11,14 @@ public readonly struct RioPacket
public RioCommand Command { get; }
///
- /// The payload bytes (high bit always clear). Length matches
- /// .
+ /// The payload bytes (high bit always clear; treat as read-only). Length
+ /// matches .
///
- public ReadOnlyMemory Payload { get; }
+ public byte[] Payload { get; }
- public RioPacket(RioCommand command, ReadOnlyMemory payload)
+ public RioPacket(RioCommand command, byte[] payload)
{
+ if (payload is null) throw new ArgumentNullException(nameof(payload));
int expected = RioCommandTable.PayloadLength(command);
if (payload.Length != expected)
throw new ArgumentException(
@@ -30,7 +31,7 @@ public readonly struct RioPacket
public override string ToString()
{
- var hex = BitConverter.ToString(Payload.ToArray()).Replace("-", string.Empty);
- return Payload.IsEmpty ? Command.ToString() : $"{Command} [{hex}]";
+ var hex = BitConverter.ToString(Payload).Replace("-", string.Empty);
+ return Payload.Length == 0 ? Command.ToString() : $"{Command} [{hex}]";
}
}
diff --git a/src/RioJoy.Core/Protocol/RioReplies.cs b/src/RioJoy.Core/Protocol/RioReplies.cs
index 2589f65..5a6224a 100644
--- a/src/RioJoy.Core/Protocol/RioReplies.cs
+++ b/src/RioJoy.Core/Protocol/RioReplies.cs
@@ -16,7 +16,7 @@ public readonly struct VersionInfo
}
/// Decode a payload (2 bytes).
- public static VersionInfo Parse(ReadOnlySpan payload)
+ public static VersionInfo Parse(byte[] payload)
{
Require(payload, RioCommand.VersionReply);
return new VersionInfo(payload[0], payload[1]);
@@ -24,8 +24,9 @@ public readonly struct VersionInfo
public override string ToString() => $"{Major}.{Minor}";
- internal static void Require(ReadOnlySpan payload, RioCommand command)
+ internal static void Require(byte[] payload, RioCommand command)
{
+ if (payload is null) throw new ArgumentNullException(nameof(payload));
int expected = RioCommandTable.PayloadLength(command);
if (payload.Length != expected)
throw new ArgumentException(
@@ -51,7 +52,7 @@ public readonly struct CheckStatus
}
/// Decode a payload (2 bytes).
- public static CheckStatus Parse(ReadOnlySpan payload)
+ public static CheckStatus Parse(byte[] payload)
{
VersionInfo.Require(payload, RioCommand.CheckReply);
return new CheckStatus((RioStatusType)payload[0], payload[1]);
diff --git a/src/RioJoy.Core/RioJoy.Core.csproj b/src/RioJoy.Core/RioJoy.Core.csproj
index ffb09f8..b050b9b 100644
--- a/src/RioJoy.Core/RioJoy.Core.csproj
+++ b/src/RioJoy.Core/RioJoy.Core.csproj
@@ -15,8 +15,7 @@
-
-
+
all
diff --git a/src/RioJoy.Core/RioRuntime.cs b/src/RioJoy.Core/RioRuntime.cs
index d57b5d3..9323fc8 100644
--- a/src/RioJoy.Core/RioRuntime.cs
+++ b/src/RioJoy.Core/RioRuntime.cs
@@ -144,7 +144,7 @@ public sealed class RioRuntime : IRioCommandSink, IDisposable
private void OnPacket(RioPacket packet)
{
EnsureLampsInitialized();
- ReadOnlySpan p = packet.Payload.Span;
+ byte[] p = packet.Payload;
switch (packet.Command)
{
case RioCommand.ButtonPressed when p[0] < RioAddress.ButtonCount:
@@ -185,7 +185,7 @@ public sealed class RioRuntime : IRioCommandSink, IDisposable
ButtonActivity?.Invoke(address, pressed);
}
- private static bool IsKeypad(ReadOnlySpan p) => p[0] is 0 or 1 && p[1] <= 0x0F;
+ private static bool IsKeypad(byte[] p) => p[0] is 0 or 1 && p[1] <= 0x0F;
// IRioCommandSink: RIO commands routed from a button (RIOcmd port).
void IRioCommandSink.Execute(RioCommandCode command)
diff --git a/src/RioJoy.Core/Serial/IRioTransport.cs b/src/RioJoy.Core/Serial/IRioTransport.cs
index 9692281..bb8e113 100644
--- a/src/RioJoy.Core/Serial/IRioTransport.cs
+++ b/src/RioJoy.Core/Serial/IRioTransport.cs
@@ -15,8 +15,13 @@ public interface IRioTransport : IDisposable
/// Read available bytes into . Returns the number of
/// bytes read; a return of 0 indicates the transport has closed.
///
- ValueTask ReadAsync(Memory buffer, CancellationToken cancellationToken);
+ ///
+ /// Plain byte[]/ rather than Memory/ValueTask: the
+ /// link runs at 9600 baud, and the net40 (Windows XP) flavor has neither
+ /// System.Memory nor ValueTask (see docs/PLAN.md §Phase 8).
+ ///
+ Task ReadAsync(byte[] buffer, CancellationToken cancellationToken);
/// Write all of to the transport.
- ValueTask WriteAsync(ReadOnlyMemory data, CancellationToken cancellationToken);
+ Task WriteAsync(byte[] data, CancellationToken cancellationToken);
}
diff --git a/src/RioJoy.Core/Serial/RioSerialLink.cs b/src/RioJoy.Core/Serial/RioSerialLink.cs
index 1104e73..4ad4b73 100644
--- a/src/RioJoy.Core/Serial/RioSerialLink.cs
+++ b/src/RioJoy.Core/Serial/RioSerialLink.cs
@@ -79,7 +79,7 @@ public sealed class RioSerialLink
}
/// Send a pre-built packet (see ) to the RIO.
- public async Task SendAsync(ReadOnlyMemory packet, CancellationToken cancellationToken = default)
+ public async Task SendAsync(byte[] packet, CancellationToken cancellationToken = default)
{
await _writeLock.WaitAsync(cancellationToken).ConfigureAwait(false);
try
@@ -155,7 +155,7 @@ public sealed class RioSerialLink
switch (packet.Command)
{
case RioCommand.AnalogReply:
- if (AnalogReport.TryParse(packet.Payload.Span, out AnalogReport report))
+ if (AnalogReport.TryParse(packet.Payload, out AnalogReport report))
{
_sinceAnalog.Restart();
AnalogReceived?.Invoke(report);
@@ -163,11 +163,11 @@ public sealed class RioSerialLink
break;
case RioCommand.VersionReply:
- VersionReceived?.Invoke(VersionInfo.Parse(packet.Payload.Span));
+ VersionReceived?.Invoke(VersionInfo.Parse(packet.Payload));
break;
case RioCommand.CheckReply:
- CheckReceived?.Invoke(CheckStatus.Parse(packet.Payload.Span));
+ CheckReceived?.Invoke(CheckStatus.Parse(packet.Payload));
break;
}
}
diff --git a/src/RioJoy.Core/Serial/SerialPortTransport.cs b/src/RioJoy.Core/Serial/SerialPortTransport.cs
index 20f8dc2..cce6bd9 100644
--- a/src/RioJoy.Core/Serial/SerialPortTransport.cs
+++ b/src/RioJoy.Core/Serial/SerialPortTransport.cs
@@ -47,21 +47,11 @@ public sealed class SerialPortTransport : IRioTransport
public string Description => $"{_port.PortName} @ {BaudRate} 8N1";
- // net48's Stream has no Memory-based ReadAsync/WriteAsync overloads, so bridge
- // through a pooled array and copy into/out of the caller's Memory.
- public async ValueTask ReadAsync(Memory buffer, CancellationToken cancellationToken)
- {
- byte[] tmp = new byte[buffer.Length];
- int read = await _stream.ReadAsync(tmp, 0, tmp.Length, cancellationToken).ConfigureAwait(false);
- new ReadOnlySpan(tmp, 0, read).CopyTo(buffer.Span);
- return read;
- }
+ public Task ReadAsync(byte[] buffer, CancellationToken cancellationToken) =>
+ _stream.ReadAsync(buffer, 0, buffer.Length, cancellationToken);
- public async ValueTask WriteAsync(ReadOnlyMemory data, CancellationToken cancellationToken)
- {
- byte[] tmp = data.ToArray();
- await _stream.WriteAsync(tmp, 0, tmp.Length, cancellationToken).ConfigureAwait(false);
- }
+ public Task WriteAsync(byte[] data, CancellationToken cancellationToken) =>
+ _stream.WriteAsync(data, 0, data.Length, cancellationToken);
public void Dispose()
{
diff --git a/tests/RioJoy.Core.Tests/Hid/RioHidReportTests.cs b/tests/RioJoy.Core.Tests/Hid/RioHidReportTests.cs
index 9caf07b..2e4aec3 100644
--- a/tests/RioJoy.Core.Tests/Hid/RioHidReportTests.cs
+++ b/tests/RioJoy.Core.Tests/Hid/RioHidReportTests.cs
@@ -29,12 +29,12 @@ public class RioHidReportTests
{
var report = new RioHidReport();
report.SetAxis(JoyAxis.Z, 0x1234);
- Assert.Equal(0x34, report.Bytes[4]);
- Assert.Equal(0x12, report.Bytes[5]);
+ Assert.Equal(0x34, report.ToArray()[4]);
+ Assert.Equal(0x12, report.ToArray()[5]);
report.SetAxis(JoyAxis.Z, 70000); // over max → clamped to 32767
- Assert.Equal(0xFF, report.Bytes[4]);
- Assert.Equal(0x7F, report.Bytes[5]);
+ Assert.Equal(0xFF, report.ToArray()[4]);
+ Assert.Equal(0x7F, report.ToArray()[5]);
}
[Theory]
@@ -47,7 +47,7 @@ public class RioHidReportTests
{
var report = new RioHidReport();
report.SetHat(hat);
- Assert.Equal(expected, report.Bytes[12] & 0x0F);
+ Assert.Equal(expected, report.ToArray()[12] & 0x0F);
}
[Theory]
@@ -59,10 +59,10 @@ public class RioHidReportTests
{
var report = new RioHidReport();
report.SetButton(button, pressed: true);
- Assert.Equal(mask, report.Bytes[byteIndex]);
+ Assert.Equal(mask, report.ToArray()[byteIndex]);
report.SetButton(button, pressed: false);
- Assert.Equal(0, report.Bytes[byteIndex]);
+ Assert.Equal(0, report.ToArray()[byteIndex]);
}
[Fact]
@@ -80,6 +80,6 @@ public class RioHidReportTests
report.SetButton(1, true);
report.SetButton(2, true);
report.SetButton(1, false);
- Assert.Equal(0x02, report.Bytes[13]); // only button 2 remains
+ Assert.Equal(0x02, report.ToArray()[13]); // only button 2 remains
}
}
diff --git a/tests/RioJoy.Core.Tests/Protocol/PacketBuilderTests.cs b/tests/RioJoy.Core.Tests/Protocol/PacketBuilderTests.cs
index 709a583..b42755e 100644
--- a/tests/RioJoy.Core.Tests/Protocol/PacketBuilderTests.cs
+++ b/tests/RioJoy.Core.Tests/Protocol/PacketBuilderTests.cs
@@ -41,7 +41,7 @@ public class PacketBuilderTests
byte[] packet = PacketBuilder.Build(RioCommand.AnalogReply, payload);
Assert.Equal((byte)RioCommand.AnalogReply, packet[0]);
- Assert.Equal(payload, packet.AsSpan(1, packet.Length - 2).ToArray());
- Assert.Equal(RioChecksum.Compute(packet.AsSpan(0, packet.Length - 1)), packet[^1]);
+ Assert.Equal(payload, packet.Skip(1).Take(packet.Length - 2).ToArray());
+ Assert.Equal(RioChecksum.Compute(packet, 0, packet.Length - 1), packet[packet.Length - 1]);
}
}
diff --git a/tests/RioJoy.Core.Tests/Protocol/PacketParserTests.cs b/tests/RioJoy.Core.Tests/Protocol/PacketParserTests.cs
index eaa57be..9c585a4 100644
--- a/tests/RioJoy.Core.Tests/Protocol/PacketParserTests.cs
+++ b/tests/RioJoy.Core.Tests/Protocol/PacketParserTests.cs
@@ -5,7 +5,7 @@ namespace RioJoy.Core.Tests.Protocol;
public class PacketParserTests
{
- private static List FeedAll(PacketParser parser, ReadOnlySpan data)
+ private static List FeedAll(PacketParser parser, byte[] data)
{
var events = new List();
foreach (byte b in data)
diff --git a/tests/RioJoy.Core.Tests/Protocol/RioChecksumTests.cs b/tests/RioJoy.Core.Tests/Protocol/RioChecksumTests.cs
index cb4691a..0a3ba96 100644
--- a/tests/RioJoy.Core.Tests/Protocol/RioChecksumTests.cs
+++ b/tests/RioJoy.Core.Tests/Protocol/RioChecksumTests.cs
@@ -8,7 +8,7 @@ public class RioChecksumTests
[Fact]
public void Empty_IsZero()
{
- Assert.Equal(0, RioChecksum.Compute(ReadOnlySpan.Empty));
+ Assert.Equal(0, RioChecksum.Compute(new byte[0]));
}
[Fact]
diff --git a/tests/RioJoy.Core.Tests/Serial/FakeTransport.cs b/tests/RioJoy.Core.Tests/Serial/FakeTransport.cs
index c86865e..fdd915a 100644
--- a/tests/RioJoy.Core.Tests/Serial/FakeTransport.cs
+++ b/tests/RioJoy.Core.Tests/Serial/FakeTransport.cs
@@ -23,13 +23,13 @@ internal sealed class FakeTransport : IRioTransport
/// Signal that no more inbound data will arrive (transport closed).
public void CompleteIncoming() => _incoming.Writer.TryComplete();
- public async ValueTask ReadAsync(Memory buffer, CancellationToken cancellationToken)
+ public async Task ReadAsync(byte[] buffer, CancellationToken cancellationToken)
{
while (await _incoming.Reader.WaitToReadAsync(cancellationToken).ConfigureAwait(false))
{
if (_incoming.Reader.TryRead(out byte[]? chunk))
{
- chunk.AsSpan().CopyTo(buffer.Span);
+ Array.Copy(chunk, buffer, chunk.Length);
return chunk.Length;
}
}
@@ -37,10 +37,10 @@ internal sealed class FakeTransport : IRioTransport
return 0; // completed
}
- public ValueTask WriteAsync(ReadOnlyMemory data, CancellationToken cancellationToken)
+ public Task WriteAsync(byte[] data, CancellationToken cancellationToken)
{
- _writes.Writer.TryWrite(data.ToArray());
- return default; // net48 has no ValueTask.CompletedTask; default(ValueTask) is the completed task
+ _writes.Writer.TryWrite((byte[])data.Clone());
+ return Task.CompletedTask;
}
/// Read the next outbound write, failing if none arrives in time.
diff --git a/tools/RioSerialMonitor/Program.cs b/tools/RioSerialMonitor/Program.cs
index 41c3200..287a5df 100644
--- a/tools/RioSerialMonitor/Program.cs
+++ b/tools/RioSerialMonitor/Program.cs
@@ -68,7 +68,7 @@ bool Changed(AnalogReport a, AnalogReport b) =>
link.PacketReceived += p =>
{
packets++;
- ReadOnlySpan pl = p.Payload.Span;
+ byte[] pl = p.Payload;
switch (p.Command)
{
case RioCommand.ButtonPressed when pl[0] < RioAddress.ButtonCount: