Add LNX cart writer

This commit is contained in:
Matt Nadareski
2026-04-14 12:03:19 -04:00
parent 3896b75b3b
commit fead0d781c
4 changed files with 155 additions and 101 deletions

View File

@@ -0,0 +1,24 @@
using System.IO;
using Xunit;
namespace SabreTools.Serialization.Writers.Test
{
public class AtariLynxCartTests
{
[Fact]
public void SerializeArray_Null_Null()
{
var serializer = new AtariLynxCart();
byte[]? actual = serializer.SerializeArray(null);
Assert.Null(actual);
}
[Fact]
public void SerializeStream_Null_Null()
{
var serializer = new AtariLynxCart();
Stream? actual = serializer.SerializeStream(null);
Assert.Null(actual);
}
}
}

View File

@@ -0,0 +1,123 @@
using System;
using System.IO;
using SabreTools.Data.Models.AtariLynx;
using SabreTools.IO.Extensions;
using SabreTools.Numerics.Extensions;
namespace SabreTools.Serialization.Writers
{
public class AtariLynxCart : BaseBinaryWriter<Cart>
{
/// <inheritdoc/>
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;
}
/// <summary>
/// Write header data to the stream
/// </summary>
/// <param name="stream">Stream to write to</param>
/// <returns>True if the writing was successful, false otherwise</returns>
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");
// Bytes 0-3
stream.Write(obj.Magic);
stream.Flush();
// Bytes 4-5
stream.Write(obj.Bank0PageSize);
stream.Flush();
// Bytes 6-7
stream.Write(obj.Bank0PageSize);
stream.Flush();
// Bytes 8-9
stream.Write(obj.Version);
stream.Flush();
// Bytes 10-41
stream.Write(obj.CartName);
stream.Flush();
// Bytes 42-57
stream.Write(obj.Manufacturer);
stream.Flush();
// Byte 58
stream.Write((byte)obj.Rotation);
stream.Flush();
// Bytes 59-63
stream.Write(obj.Spare);
stream.Flush();
// Header extracted
return true;
}
catch (Exception ex)
{
if (Debug) Console.Error.WriteLine(ex);
return false;
}
}
/// <summary>
/// Write rom data to the stream
/// </summary>
/// <param name="stream">Stream to write to</param>
/// <returns>True if the writing was successful, false otherwise</returns>
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;
}
}
}
}

View File

@@ -14,6 +14,9 @@ namespace SabreTools.Wrappers
: Path.GetFileNameWithoutExtension(Filename);
string basePath = Path.Combine(outputDirectory, baseFilename);
// Create the writer
var writer = new Serialization.Writers.AtariLynxCart { 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)
{

View File

@@ -1,7 +1,5 @@
using System;
using System.IO;
using SabreTools.Data.Models.AtariLynx;
using SabreTools.Numerics.Extensions;
namespace SabreTools.Wrappers
{
@@ -26,103 +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;
}
/// <summary>
/// Write header data to the stream
/// </summary>
/// <param name="stream">Stream to write to</param>
/// <param name="includeDebug">True to include debug data, false otherwise</param>
/// <returns>True if the writing was successful, false otherwise</returns>
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
{
// Bytes 0-3
stream.Write(Header.Magic);
stream.Flush();
// Bytes 4-5
stream.Write(Header.Bank0PageSize);
stream.Flush();
// Bytes 6-7
stream.Write(Header.Bank0PageSize);
stream.Flush();
// Bytes 8-9
stream.Write(Header.Version);
stream.Flush();
// Bytes 10-41
stream.Write(Header.CartName);
stream.Flush();
// Bytes 42-57
stream.Write(Header.Manufacturer);
stream.Flush();
// Byte 58
stream.Write((byte)Header.Rotation);
stream.Flush();
// Bytes 59-63
stream.Write(Header.Spare);
stream.Flush();
// Header extracted
return true;
}
catch (Exception ex)
{
if (includeDebug) Console.Error.WriteLine(ex);
return false;
}
}
/// <summary>
/// Write rom data to the stream
/// </summary>
/// <param name="stream">Stream to write to</param>
/// <param name="includeDebug">True to include debug data, false otherwise</param>
/// <returns>True if the writing was successful, false otherwise</returns>
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.AtariLynxCart { Debug = includeDebug };
return writer.SerializeFile(Model, outputPath);
}
}
}