From 3896b75b3bfb5ec3a815225f8c0717d18daaa165 Mon Sep 17 00:00:00 2001 From: Matt Nadareski Date: Tue, 14 Apr 2026 11:59:12 -0400 Subject: [PATCH] Add A78 cart writer --- .../Atari7800CartTests.cs | 24 +++ .../Atari7800Cart.cs | 165 ++++++++++++++++++ .../Atari7800Cart.Extraction.cs | 7 +- SabreTools.Wrappers/Atari7800Cart.Writing.cs | 144 +-------------- .../SabreTools.Wrappers.csproj | 1 + 5 files changed, 198 insertions(+), 143 deletions(-) create mode 100644 SabreTools.Serialization.Writers.Test/Atari7800CartTests.cs create mode 100644 SabreTools.Serialization.Writers/Atari7800Cart.cs diff --git a/SabreTools.Serialization.Writers.Test/Atari7800CartTests.cs b/SabreTools.Serialization.Writers.Test/Atari7800CartTests.cs new file mode 100644 index 00000000..69614b0e --- /dev/null +++ b/SabreTools.Serialization.Writers.Test/Atari7800CartTests.cs @@ -0,0 +1,24 @@ +using System.IO; +using Xunit; + +namespace SabreTools.Serialization.Writers.Test +{ + public class Atari7800CartTests + { + [Fact] + public void SerializeArray_Null_Null() + { + var serializer = new Atari7800Cart(); + byte[]? actual = serializer.SerializeArray(null); + Assert.Null(actual); + } + + [Fact] + public void SerializeStream_Null_Null() + { + var serializer = new Atari7800Cart(); + Stream? actual = serializer.SerializeStream(null); + Assert.Null(actual); + } + } +} diff --git a/SabreTools.Serialization.Writers/Atari7800Cart.cs b/SabreTools.Serialization.Writers/Atari7800Cart.cs new file mode 100644 index 00000000..32e2c6a2 --- /dev/null +++ b/SabreTools.Serialization.Writers/Atari7800Cart.cs @@ -0,0 +1,165 @@ +using System; +using System.IO; +using SabreTools.Data.Models.Atari7800; +using SabreTools.IO.Extensions; +using SabreTools.Numerics.Extensions; + +namespace SabreTools.Serialization.Writers +{ + public class Atari7800Cart : BaseBinaryWriter + { + /// + public override Stream? SerializeStream(Cart? obj) + { + // If the metadata file is null + if (obj is null) + return null; + + // Setup the writer and output + var stream = new MemoryStream(); + + // Write header + if (!WriteHeader(obj.Header, stream)) + return null; + + // Write data + if (!WriteRom(obj.Data, stream)) + return null; + + // Return the stream + stream.SeekIfPossible(0, SeekOrigin.Begin); + return stream; + } + + /// + /// Write header data to the stream + /// + /// Stream to write to + /// True if the writing was successful, false otherwise + public bool WriteHeader(Header? obj, Stream stream) + { + if (obj is null) + { + if (Debug) Console.WriteLine("Header was invalid!"); + return false; + } + + // Try to write the data + try + { + if (Debug) Console.WriteLine("Attempting to write header data"); + + // Byte 0 + stream.Write(obj.HeaderVersion); + stream.Flush(); + + // Bytes 1-16 + stream.Write(obj.MagicText); + stream.Flush(); + + // Bytes 17-48 + stream.Write(obj.CartTitle); + stream.Flush(); + + // Bytes 49-52 + stream.Write(obj.RomSizeWithoutHeader); + stream.Flush(); + + // Bytes 53-54 + stream.Write((ushort)obj.CartType); + stream.Flush(); + + // Bytes 55-56 + stream.Write((byte)obj.Controller1Type); + stream.Write((byte)obj.Controller2Type); + stream.Flush(); + + // Byte 57 + stream.Write((byte)obj.TVType); + stream.Flush(); + + // Byte 58 + stream.Write((byte)obj.SaveDevice); + stream.Flush(); + + // Bytes 59-62 + stream.Write(obj.Reserved); + stream.Flush(); + + // Byte 63 + stream.Write((byte)obj.SlotPassthroughDevice); + stream.Flush(); + + if (obj.HeaderVersion >= 4) + { + // Byte 64 + stream.Write((byte)obj.Mapper); + stream.Flush(); + + // Byte 65 + stream.Write((byte)obj.MapperOptions); + stream.Flush(); + + // Byte 66-67 + stream.Write((ushort)obj.AudioDevice); + stream.Flush(); + + // Byte 68-69 + stream.Write((ushort)obj.Interrupt); + stream.Flush(); + + // Bytes 70-99 + stream.Write(obj.Padding); + stream.Flush(); + } + else + { + // Bytes 64-99 + stream.Write(obj.Padding); + stream.Flush(); + } + + // Bytes 100-127 + stream.Write(obj.EndMagicText); + stream.Flush(); + + // Header extracted + return true; + } + catch (Exception ex) + { + if (Debug) Console.Error.WriteLine(ex); + return false; + } + } + + /// + /// Write rom data to the stream + /// + /// Stream to write to + /// True if the writing was successful, false otherwise + public bool WriteRom(byte[]? obj, Stream stream) + { + if (obj is null || obj.Length == 0) + { + if (Debug) Console.WriteLine("ROM data was invalid!"); + return false; + } + + // Try to write the data + try + { + if (Debug) Console.WriteLine("Attempting to write ROM data"); + + stream.Write(obj, 0, obj.Length); + stream.Flush(); + return true; + } + catch (Exception ex) + { + if (Debug) Console.Error.WriteLine(ex); + return false; + } + } + } +} diff --git a/SabreTools.Wrappers/Atari7800Cart.Extraction.cs b/SabreTools.Wrappers/Atari7800Cart.Extraction.cs index bd85a0f1..d20bea15 100644 --- a/SabreTools.Wrappers/Atari7800Cart.Extraction.cs +++ b/SabreTools.Wrappers/Atari7800Cart.Extraction.cs @@ -14,6 +14,9 @@ namespace SabreTools.Wrappers : Path.GetFileNameWithoutExtension(Filename); string basePath = Path.Combine(outputDirectory, baseFilename); + // Create the writer + var writer = new Serialization.Writers.Atari7800Cart { Debug = includeDebug }; + // Check if any data was extracted successfully bool success = false; @@ -27,7 +30,7 @@ namespace SabreTools.Wrappers try { using var fs = File.Open(headerPath, FileMode.Create, FileAccess.Write, FileShare.None); - success |= WriteHeader(fs, includeDebug); + success |= writer.WriteHeader(Header, fs); } catch (Exception ex) { @@ -45,7 +48,7 @@ namespace SabreTools.Wrappers try { using var fs = File.Open(romPath, FileMode.Create, FileAccess.Write, FileShare.None); - success |= WriteRom(fs, includeDebug); + success |= writer.WriteRom(Model.Data, fs); } catch (Exception ex) { diff --git a/SabreTools.Wrappers/Atari7800Cart.Writing.cs b/SabreTools.Wrappers/Atari7800Cart.Writing.cs index 05efb918..44f94463 100644 --- a/SabreTools.Wrappers/Atari7800Cart.Writing.cs +++ b/SabreTools.Wrappers/Atari7800Cart.Writing.cs @@ -1,7 +1,5 @@ using System; using System.IO; -using SabreTools.Data.Models.Atari7800; -using SabreTools.Numerics.Extensions; namespace SabreTools.Wrappers { @@ -26,145 +24,9 @@ namespace SabreTools.Wrappers return false; } - // Open the output file for writing - using var fs = File.Open(outputPath, FileMode.Create, FileAccess.Write, FileShare.None); - - // Header data - if (!WriteHeader(fs, includeDebug)) - return false; - - // ROM data - if (!WriteRom(fs, includeDebug)) - return false; - - return true; - } - - /// - /// Write header data to the stream - /// - /// Stream to write to - /// True to include debug data, false otherwise - /// True if the writing was successful, false otherwise - private bool WriteHeader(Stream stream, bool includeDebug) - { - if (includeDebug) Console.WriteLine("Attempting to write header data"); - - if (Header is null) - { - if (includeDebug) Console.WriteLine("Header was invalid!"); - return false; - } - - // Try to write the data - try - { - // Byte 0 - stream.Write(Header.HeaderVersion); - stream.Flush(); - - // Bytes 1-16 - stream.Write(Header.MagicText); - stream.Flush(); - - // Bytes 17-48 - stream.Write(Header.CartTitle); - stream.Flush(); - - // Bytes 49-52 - stream.Write(Header.RomSizeWithoutHeader); - stream.Flush(); - - // Bytes 53-54 - stream.Write((ushort)Header.CartType); - stream.Flush(); - - // Bytes 55-56 - stream.Write((byte)Header.Controller1Type); - stream.Write((byte)Header.Controller2Type); - stream.Flush(); - - // Byte 57 - stream.Write((byte)Header.TVType); - stream.Flush(); - - // Byte 58 - stream.Write((byte)Header.SaveDevice); - stream.Flush(); - - // Bytes 59-62 - stream.Write(Header.Reserved); - stream.Flush(); - - // Byte 63 - stream.Write((byte)Header.SlotPassthroughDevice); - stream.Flush(); - - if (HeaderVersion >= 4) - { - // Byte 64 - stream.Write((byte)Header.Mapper); - stream.Flush(); - - // Byte 65 - stream.Write((byte)Header.MapperOptions); - stream.Flush(); - - // Byte 66-67 - stream.Write((ushort)Header.AudioDevice); - stream.Flush(); - - // Byte 68-69 - stream.Write((ushort)Header.Interrupt); - stream.Flush(); - - // Bytes 70-99 - stream.Write(Header.Padding); - stream.Flush(); - } - else - { - // Bytes 64-99 - stream.Write(Header.Padding); - stream.Flush(); - } - - // Bytes 100-127 - stream.Write(Header.EndMagicText); - stream.Flush(); - - // Header extracted - return true; - } - catch (Exception ex) - { - if (includeDebug) Console.Error.WriteLine(ex); - return false; - } - } - - /// - /// Write rom data to the stream - /// - /// Stream to write to - /// True to include debug data, false otherwise - /// True if the writing was successful, false otherwise - private bool WriteRom(Stream stream, bool includeDebug) - { - if (includeDebug) Console.WriteLine("Attempting to write ROM data"); - - // Try to write the data - try - { - stream.Write(Model.Data, 0, Model.Data.Length); - stream.Flush(); - return true; - } - catch (Exception ex) - { - if (includeDebug) Console.Error.WriteLine(ex); - return false; - } + // Create and use the writer + var writer = new Serialization.Writers.Atari7800Cart { Debug = includeDebug }; + return writer.SerializeFile(Model, outputPath); } } } diff --git a/SabreTools.Wrappers/SabreTools.Wrappers.csproj b/SabreTools.Wrappers/SabreTools.Wrappers.csproj index a788393a..9722af0a 100644 --- a/SabreTools.Wrappers/SabreTools.Wrappers.csproj +++ b/SabreTools.Wrappers/SabreTools.Wrappers.csproj @@ -51,6 +51,7 @@ + \ No newline at end of file