mirror of
https://github.com/SabreTools/SabreTools.Serialization.git
synced 2026-09-21 22:35:06 +00:00
Add Atari Lynx cart support
This commit is contained in:
@@ -150,11 +150,16 @@ namespace ExtractionTool.Features
|
||||
sz.Extract(OutputPath, Debug);
|
||||
break;
|
||||
|
||||
// Atari 78800 Cart
|
||||
// Atari 7800 Cart
|
||||
case Atari7800Cart a7800:
|
||||
a7800.Extract(OutputPath, Debug);
|
||||
break;
|
||||
|
||||
// Atari Lynx Cart
|
||||
case AtariLynxCart lynx:
|
||||
lynx.Extract(OutputPath, Debug);
|
||||
break;
|
||||
|
||||
// BFPK archive
|
||||
case BFPK bfpk:
|
||||
bfpk.Extract(OutputPath, Debug);
|
||||
|
||||
@@ -54,6 +54,7 @@ Options:
|
||||
| --- | --- |
|
||||
| 7-zip archive | .NET Framework 4.6.2 and greater |
|
||||
| Atari 7800 (A78) Cart Image | Header and ROM data |
|
||||
| Atari Lynx (LNX) Cart Image | Header and ROM data |
|
||||
| BFPK custom archive format | |
|
||||
| bzip2 archive | .NET Framework 4.6.2 and greater |
|
||||
| Compound File Binary (CFB) | Only CFB common pieces extractable |
|
||||
|
||||
73
SabreTools.Serialization.Test/Readers/AtariLynxCartTests.cs
Normal file
73
SabreTools.Serialization.Test/Readers/AtariLynxCartTests.cs
Normal file
@@ -0,0 +1,73 @@
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using SabreTools.Serialization.Readers;
|
||||
using Xunit;
|
||||
|
||||
namespace SabreTools.Serialization.Test.Readers
|
||||
{
|
||||
public class AtariLynxCartTests
|
||||
{
|
||||
[Fact]
|
||||
public void NullArray_Null()
|
||||
{
|
||||
byte[]? data = null;
|
||||
int offset = 0;
|
||||
var deserializer = new AtariLynxCart();
|
||||
|
||||
var actual = deserializer.Deserialize(data, offset);
|
||||
Assert.Null(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void EmptyArray_Null()
|
||||
{
|
||||
byte[]? data = [];
|
||||
int offset = 0;
|
||||
var deserializer = new AtariLynxCart();
|
||||
|
||||
var actual = deserializer.Deserialize(data, offset);
|
||||
Assert.Null(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void InvalidArray_Null()
|
||||
{
|
||||
byte[]? data = [.. Enumerable.Repeat<byte>(0xFF, 1024)];
|
||||
int offset = 0;
|
||||
var deserializer = new AtariLynxCart();
|
||||
|
||||
var actual = deserializer.Deserialize(data, offset);
|
||||
Assert.Null(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void NullStream_Null()
|
||||
{
|
||||
Stream? data = null;
|
||||
var deserializer = new AtariLynxCart();
|
||||
|
||||
var actual = deserializer.Deserialize(data);
|
||||
Assert.Null(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void EmptyStream_Null()
|
||||
{
|
||||
Stream? data = new MemoryStream([]);
|
||||
var deserializer = new AtariLynxCart();
|
||||
|
||||
var actual = deserializer.Deserialize(data);
|
||||
Assert.Null(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void InvalidStream_Null()
|
||||
{
|
||||
Stream? data = new MemoryStream([.. Enumerable.Repeat<byte>(0xFF, 1024)]);
|
||||
var deserializer = new AtariLynxCart();
|
||||
|
||||
var actual = deserializer.Deserialize(data);
|
||||
Assert.Null(actual);
|
||||
}
|
||||
}
|
||||
}
|
||||
61
SabreTools.Serialization.Test/Wrappers/AtariLynxCartTests.cs
Normal file
61
SabreTools.Serialization.Test/Wrappers/AtariLynxCartTests.cs
Normal file
@@ -0,0 +1,61 @@
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using SabreTools.Serialization.Wrappers;
|
||||
using Xunit;
|
||||
|
||||
namespace SabreTools.Serialization.Test.Wrappers
|
||||
{
|
||||
public class AtariLynxCartTests
|
||||
{
|
||||
[Fact]
|
||||
public void NullArray_Null()
|
||||
{
|
||||
byte[]? data = null;
|
||||
int offset = 0;
|
||||
var actual = AtariLynxCart.Create(data, offset);
|
||||
Assert.Null(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void EmptyArray_Null()
|
||||
{
|
||||
byte[]? data = [];
|
||||
int offset = 0;
|
||||
var actual = AtariLynxCart.Create(data, offset);
|
||||
Assert.Null(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void InvalidArray_Null()
|
||||
{
|
||||
byte[]? data = [.. Enumerable.Repeat<byte>(0xFF, 1024)];
|
||||
int offset = 0;
|
||||
var actual = AtariLynxCart.Create(data, offset);
|
||||
Assert.Null(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void NullStream_Null()
|
||||
{
|
||||
Stream? data = null;
|
||||
var actual = AtariLynxCart.Create(data);
|
||||
Assert.Null(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void EmptyStream_Null()
|
||||
{
|
||||
Stream? data = new MemoryStream([]);
|
||||
var actual = AtariLynxCart.Create(data);
|
||||
Assert.Null(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void InvalidStream_Null()
|
||||
{
|
||||
Stream? data = new MemoryStream([.. Enumerable.Repeat<byte>(0xFF, 1024)]);
|
||||
var actual = AtariLynxCart.Create(data);
|
||||
Assert.Null(actual);
|
||||
}
|
||||
}
|
||||
}
|
||||
21
SabreTools.Serialization/Extensions/AtariLynxCart.cs
Normal file
21
SabreTools.Serialization/Extensions/AtariLynxCart.cs
Normal file
@@ -0,0 +1,21 @@
|
||||
using SabreTools.Data.Models.AtariLynx;
|
||||
|
||||
namespace SabreTools.Data.Extensions
|
||||
{
|
||||
public static class AtariLynxCart
|
||||
{
|
||||
/// <summary>
|
||||
/// Convert a <see cref="Rotation"/> value to string
|
||||
/// </summary>
|
||||
public static string FromRotation(this Rotation rotation)
|
||||
{
|
||||
return rotation switch
|
||||
{
|
||||
Rotation.NoRotation => "No rotation (horizontal, buttons right)",
|
||||
Rotation.RotateLeft => "Rotate left (vertical, buttons down)",
|
||||
Rotation.RotateRight => "Rotate right (vertical, buttons up)",
|
||||
_ => $"Unknown {(byte)rotation}",
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
20
SabreTools.Serialization/Models/AtariLynx/Cart.cs
Normal file
20
SabreTools.Serialization/Models/AtariLynx/Cart.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
namespace SabreTools.Data.Models.AtariLynx
|
||||
{
|
||||
/// <summary>
|
||||
/// Atari Lynx headered cart (LNX)
|
||||
/// </summary>
|
||||
/// <see href="https://github.com/mozzwald/handy-sdl/blob/master/src/handy-0.95/cart.h"/>
|
||||
public class Cart
|
||||
{
|
||||
/// <summary>
|
||||
/// LNX header
|
||||
/// </summary>
|
||||
/// <remarks>If omitted, format is technically LYX</remarks>
|
||||
public Header? Header { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Cartridge data
|
||||
/// </summary>
|
||||
public byte[] Data { get; set; } = [];
|
||||
}
|
||||
}
|
||||
15
SabreTools.Serialization/Models/AtariLynx/Constants.cs
Normal file
15
SabreTools.Serialization/Models/AtariLynx/Constants.cs
Normal file
@@ -0,0 +1,15 @@
|
||||
namespace SabreTools.Data.Models.AtariLynx
|
||||
{
|
||||
public static class Constants
|
||||
{
|
||||
/// <summary>
|
||||
/// LNX magic string
|
||||
/// </summary>
|
||||
public static readonly byte[] MagicBytes = [0x4C, 0x59, 0x4E, 0x58];
|
||||
|
||||
/// <summary>
|
||||
/// LNX magic string
|
||||
/// </summary>
|
||||
public const string MagicString = "LYNX";
|
||||
}
|
||||
}
|
||||
24
SabreTools.Serialization/Models/AtariLynx/Enums.cs
Normal file
24
SabreTools.Serialization/Models/AtariLynx/Enums.cs
Normal file
@@ -0,0 +1,24 @@
|
||||
namespace SabreTools.Data.Models.AtariLynx
|
||||
{
|
||||
/// <summary>
|
||||
/// Screen rotation
|
||||
/// </summary>
|
||||
/// <see href="https://github.com/mozzwald/handy-sdl/blob/master/src/handy-0.95/cart.h"/>
|
||||
public enum Rotation : byte
|
||||
{
|
||||
/// <summary>
|
||||
/// No rotation (horizontal, buttons right)
|
||||
/// </summary>
|
||||
NoRotation = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Rotate left (vertical, buttons down)
|
||||
/// </summary>
|
||||
RotateLeft = 1,
|
||||
|
||||
/// <summary>
|
||||
/// Rotate right (vertical, buttons up)
|
||||
/// </summary>
|
||||
RotateRight = 2,
|
||||
}
|
||||
}
|
||||
54
SabreTools.Serialization/Models/AtariLynx/Header.cs
Normal file
54
SabreTools.Serialization/Models/AtariLynx/Header.cs
Normal file
@@ -0,0 +1,54 @@
|
||||
namespace SabreTools.Data.Models.AtariLynx
|
||||
{
|
||||
/// <summary>
|
||||
/// Atari Lynx emulator header
|
||||
/// </summary>
|
||||
/// <see href="https://github.com/mozzwald/handy-sdl/blob/master/src/handy-0.95/cart.h"/>
|
||||
public class Header
|
||||
{
|
||||
/// <summary>
|
||||
/// "LYNX"
|
||||
/// </summary>
|
||||
/// <remarks>4 bytes</remarks>
|
||||
public byte[] Magic { get; set; } = new byte[4];
|
||||
|
||||
/// <summary>
|
||||
/// Page size for bank 0
|
||||
/// </summary>
|
||||
public ushort Bank0PageSize { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Page size for bank 1
|
||||
/// </summary>
|
||||
public ushort Bank1PageSize { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Header version
|
||||
/// </summary>
|
||||
/// <remarks>Must be 0x0001</remarks>
|
||||
public ushort Version { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Game title (Null-padded ASCII)
|
||||
/// </summary>
|
||||
/// <remarks>32 bytes</remarks>
|
||||
public byte[] CartName { get; set; } = new byte[32];
|
||||
|
||||
/// <summary>
|
||||
/// Publisher name
|
||||
/// </summary>
|
||||
/// <remarks>16 bytes</remarks>
|
||||
public byte[] Manufacturer { get; set; } = new byte[16];
|
||||
|
||||
/// <summary>
|
||||
/// Screen rotation
|
||||
/// </summary>
|
||||
public Rotation Rotation { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Padding
|
||||
/// </summary>
|
||||
/// <remarks>5 bytes</remarks>
|
||||
public byte[] Spare { get; set; } = new byte[5];
|
||||
}
|
||||
}
|
||||
@@ -24,6 +24,7 @@ Not all of this information was able to be gathered directly from the files in q
|
||||
| [faydoc.tripod.com](https://faydoc.tripod.com/formats/) | LinearExecutable |
|
||||
| [GuitarGame_ChartFormats](https://github.com/TheNathannator/GuitarGame_ChartFormats/) | Charts |
|
||||
| [HandWiki](https://handwiki.org/wiki/Start) | Quantum |
|
||||
| [Handy SDL](https://github.com/mozzwald/handy-sdl) | AtariLynxCart |
|
||||
| [HLLib](https://github.com/RavuAlHemio/hllib/) | BSP, GCF, NCF, PAK, SGA, VPK, WAD3, XZP |
|
||||
| [IBM Documentation](https://www.ibm.com/docs/en) | TAR |
|
||||
| [IETF](https://www.ietf.org/) | GZIP |
|
||||
|
||||
72
SabreTools.Serialization/Readers/AtariLynxCart.cs
Normal file
72
SabreTools.Serialization/Readers/AtariLynxCart.cs
Normal file
@@ -0,0 +1,72 @@
|
||||
using System.IO;
|
||||
using SabreTools.Data.Models.AtariLynx;
|
||||
using SabreTools.IO.Extensions;
|
||||
using static SabreTools.Data.Models.AtariLynx.Constants;
|
||||
|
||||
namespace SabreTools.Serialization.Readers
|
||||
{
|
||||
public class AtariLynxCart : BaseBinaryReader<Cart>
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public override Cart? Deserialize(Stream? data)
|
||||
{
|
||||
// If the data is invalid
|
||||
if (data is null || !data.CanRead)
|
||||
return null;
|
||||
|
||||
try
|
||||
{
|
||||
// Cache the current offset
|
||||
long initialOffset = data.Position;
|
||||
|
||||
// Create a new A78 cart image to fill
|
||||
var cart = new Cart();
|
||||
|
||||
#region Header
|
||||
|
||||
// Try to parse the header
|
||||
var header = ParseHeader(data);
|
||||
if (!header.Magic.EqualsExactly(MagicBytes))
|
||||
return null;
|
||||
else if (header.Version != 0x0001)
|
||||
return null;
|
||||
|
||||
// Set the header
|
||||
cart.Header = header;
|
||||
|
||||
#endregion
|
||||
|
||||
// Read the cart data
|
||||
cart.Data = data.ReadBytes((int)(data.Length - data.Position));
|
||||
|
||||
return cart;
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Ignore the actual error
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a Header
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled Header on success, null on error</returns>
|
||||
public static Header ParseHeader(Stream data)
|
||||
{
|
||||
var obj = new Header();
|
||||
|
||||
obj.Magic = data.ReadBytes(4);
|
||||
obj.Bank0PageSize = data.ReadUInt16LittleEndian();
|
||||
obj.Bank1PageSize = data.ReadUInt16LittleEndian();
|
||||
obj.Version = data.ReadUInt16LittleEndian();
|
||||
obj.CartName = data.ReadBytes(32);
|
||||
obj.Manufacturer = data.ReadBytes(16);
|
||||
obj.Rotation = (Rotation)data.ReadByteValue();
|
||||
obj.Spare = data.ReadBytes(5);
|
||||
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,7 @@ namespace SabreTools.Serialization
|
||||
{
|
||||
WrapperType.AACSMediaKeyBlock => AACSMediaKeyBlock.Create(data),
|
||||
WrapperType.Atari7800Cart => Atari7800Cart.Create(data),
|
||||
WrapperType.AtariLynxCart => AtariLynxCart.Create(data),
|
||||
WrapperType.BDPlusSVM => BDPlusSVM.Create(data),
|
||||
WrapperType.BFPK => BFPK.Create(data),
|
||||
WrapperType.BSP => BSP.Create(data),
|
||||
@@ -193,6 +194,17 @@ namespace SabreTools.Serialization
|
||||
|
||||
#endregion
|
||||
|
||||
#region AtariLynxCart
|
||||
|
||||
// LNX header
|
||||
if (magic.StartsWith(Data.Models.AtariLynx.Constants.MagicBytes))
|
||||
return WrapperType.AtariLynxCart;
|
||||
|
||||
if (extension.Equals("lnx", StringComparison.OrdinalIgnoreCase))
|
||||
return WrapperType.AtariLynxCart;
|
||||
|
||||
#endregion
|
||||
|
||||
#region BDPlusSVM
|
||||
|
||||
if (magic.StartsWith(Data.Models.BDPlus.Constants.SignatureBytes))
|
||||
|
||||
@@ -20,7 +20,7 @@ namespace SabreTools.Serialization.Wrappers
|
||||
|
||||
Print(builder, Model.Header);
|
||||
|
||||
//builder.AppendLine(Model.Trainer, "Trainer Data");
|
||||
//builder.AppendLine(Model.Data, "ROM Data");
|
||||
builder.AppendLine(Model.Data.Length, "ROM Data Length");
|
||||
}
|
||||
|
||||
|
||||
101
SabreTools.Serialization/Wrappers/AtariLynxCart.Extraction.cs
Normal file
101
SabreTools.Serialization/Wrappers/AtariLynxCart.Extraction.cs
Normal file
@@ -0,0 +1,101 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using SabreTools.Data.Models.AtariLynx;
|
||||
using SabreTools.IO.Extensions;
|
||||
|
||||
namespace SabreTools.Serialization.Wrappers
|
||||
{
|
||||
public partial class AtariLynxCart : IExtractable
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public bool Extract(string outputDirectory, bool includeDebug)
|
||||
{
|
||||
// Get the base path
|
||||
string baseFilename = Filename is null
|
||||
? Guid.NewGuid().ToString()
|
||||
: Path.GetFileNameWithoutExtension(Filename);
|
||||
string basePath = Path.Combine(outputDirectory, baseFilename);
|
||||
|
||||
// Check if any data was extracted successfully
|
||||
bool success = false;
|
||||
|
||||
// Header data
|
||||
if (Header is not null)
|
||||
{
|
||||
string headerPath = $"{basePath}.hdr";
|
||||
if (includeDebug) Console.WriteLine($"Attempting to extract header data to {headerPath}");
|
||||
|
||||
// Try to write the data
|
||||
try
|
||||
{
|
||||
// Open the output file for writing
|
||||
using var fs = File.Open(headerPath, FileMode.Create, FileAccess.Write, FileShare.None);
|
||||
|
||||
// Bytes 0-3
|
||||
fs.Write(Header.Magic);
|
||||
fs.Flush();
|
||||
|
||||
// Bytes 4-5
|
||||
fs.Write(Header.Bank0PageSize);
|
||||
fs.Flush();
|
||||
|
||||
// Bytes 6-7
|
||||
fs.Write(Header.Bank0PageSize);
|
||||
fs.Flush();
|
||||
|
||||
// Bytes 8-9
|
||||
fs.Write(Header.Version);
|
||||
fs.Flush();
|
||||
|
||||
// Bytes 10-41
|
||||
fs.Write(Header.CartName);
|
||||
fs.Flush();
|
||||
|
||||
// Bytes 42-57
|
||||
fs.Write(Header.Manufacturer);
|
||||
fs.Flush();
|
||||
|
||||
// Byte 58
|
||||
fs.Write((byte)Header.Rotation);
|
||||
fs.Flush();
|
||||
|
||||
// Bytes 59-63
|
||||
fs.Write(Header.Spare);
|
||||
fs.Flush();
|
||||
|
||||
// Header extracted
|
||||
success = true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (includeDebug) Console.Error.WriteLine(ex);
|
||||
}
|
||||
}
|
||||
|
||||
// ROM data
|
||||
if (Model.Data.Length > 0)
|
||||
{
|
||||
string romPath = $"{basePath}.bin";
|
||||
if (includeDebug) Console.WriteLine($"Attempting to extract ROM data to {romPath}");
|
||||
|
||||
// Try to write the data
|
||||
try
|
||||
{
|
||||
// Open the output file for writing
|
||||
using var fs = File.Open(romPath, FileMode.Create, FileAccess.Write, FileShare.None);
|
||||
fs.Write(Model.Data, 0, Model.Data.Length);
|
||||
fs.Flush();
|
||||
|
||||
// ROM extracted
|
||||
success = true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (includeDebug) Console.Error.WriteLine(ex);
|
||||
}
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
}
|
||||
}
|
||||
55
SabreTools.Serialization/Wrappers/AtariLynxCart.Printing.cs
Normal file
55
SabreTools.Serialization/Wrappers/AtariLynxCart.Printing.cs
Normal file
@@ -0,0 +1,55 @@
|
||||
using System.Text;
|
||||
using SabreTools.Data.Extensions;
|
||||
using SabreTools.Data.Models.AtariLynx;
|
||||
|
||||
namespace SabreTools.Serialization.Wrappers
|
||||
{
|
||||
public partial class AtariLynxCart : IPrintable
|
||||
{
|
||||
#if NETCOREAPP
|
||||
/// <inheritdoc/>
|
||||
public string ExportJSON() => System.Text.Json.JsonSerializer.Serialize(Model, _jsonSerializerOptions);
|
||||
#endif
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void PrintInformation(StringBuilder builder)
|
||||
{
|
||||
builder.AppendLine("Atari Lynx Cart Information:");
|
||||
builder.AppendLine("-------------------------");
|
||||
builder.AppendLine();
|
||||
|
||||
Print(builder, Model.Header);
|
||||
|
||||
//builder.AppendLine(Model.Data, "ROM Data");
|
||||
builder.AppendLine(Model.Data.Length, "ROM Data Length");
|
||||
}
|
||||
|
||||
private static void Print(StringBuilder builder, Header? header)
|
||||
{
|
||||
builder.AppendLine(" Header Information:");
|
||||
builder.AppendLine(" -------------------------");
|
||||
if (header is null)
|
||||
{
|
||||
builder.AppendLine(" No header present");
|
||||
builder.AppendLine();
|
||||
return;
|
||||
}
|
||||
|
||||
builder.AppendLine(header.Magic, " Magic");
|
||||
builder.AppendLine(Encoding.ASCII.GetString(header.Magic).TrimEnd('\0'), " Magic (ASCII)");
|
||||
builder.AppendLine(header.Bank0PageSize, " Bank 0 page size");
|
||||
builder.AppendLine(header.Bank1PageSize, " Bank 1 page size");
|
||||
builder.AppendLine(header.Version, " Version");
|
||||
builder.AppendLine(header.CartName, " Cart name");
|
||||
builder.AppendLine(Encoding.ASCII.GetString(header.CartName).TrimEnd('\0'), " Cart name (ASCII)");
|
||||
builder.AppendLine(header.Manufacturer, " Manufacturer");
|
||||
builder.AppendLine(Encoding.ASCII.GetString(header.Manufacturer).TrimEnd('\0'), " Manufacturer (ASCII)");
|
||||
|
||||
string rotation = header.Rotation.FromRotation();
|
||||
builder.AppendLine(rotation, " Rotation");
|
||||
|
||||
builder.AppendLine(header.Spare, " Padding");
|
||||
builder.AppendLine();
|
||||
}
|
||||
}
|
||||
}
|
||||
120
SabreTools.Serialization/Wrappers/AtariLynxCart.cs
Normal file
120
SabreTools.Serialization/Wrappers/AtariLynxCart.cs
Normal file
@@ -0,0 +1,120 @@
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using SabreTools.Data.Models.AtariLynx;
|
||||
|
||||
namespace SabreTools.Serialization.Wrappers
|
||||
{
|
||||
public partial class AtariLynxCart : WrapperBase<Cart>
|
||||
{
|
||||
#region Descriptive Properties
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string DescriptionString => "Atari Lynx Cart Image";
|
||||
|
||||
#endregion
|
||||
|
||||
#region Extension Properties
|
||||
|
||||
/// <inheritdoc cref="Cart.Header"/>
|
||||
public Header? Header => Model.Header;
|
||||
|
||||
/// <inheritdoc cref="Header.Bank0PageSize"/>
|
||||
public ushort Bank0PageSize => Header?.Bank0PageSize ?? 0;
|
||||
|
||||
/// <inheritdoc cref="Header.Bank1PageSize"/>
|
||||
public ushort Bank1PageSize => Header?.Bank1PageSize ?? 0;
|
||||
|
||||
/// <inheritdoc cref="Header.CartName"/>
|
||||
public string? CartName => Header is null
|
||||
? null
|
||||
: Encoding.ASCII.GetString(Header.CartName).TrimEnd('\0');
|
||||
|
||||
/// <inheritdoc cref="Header.Manufacturer"/>
|
||||
public string? Manufacturer => Header is null
|
||||
? null
|
||||
: Encoding.ASCII.GetString(Header.Manufacturer).TrimEnd('\0');
|
||||
|
||||
/// <inheritdoc cref="Header.Rotation"/>
|
||||
public Rotation Rotation => Header?.Rotation ?? 0;
|
||||
|
||||
/// <inheritdoc cref="Header.Version"/>
|
||||
public ushort Version => Header?.Version ?? 0;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
/// <inheritdoc/>
|
||||
public AtariLynxCart(Cart model, byte[] data) : base(model, data) { }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public AtariLynxCart(Cart model, byte[] data, int offset) : base(model, data, offset) { }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public AtariLynxCart(Cart model, byte[] data, int offset, int length) : base(model, data, offset, length) { }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public AtariLynxCart(Cart model, Stream data) : base(model, data) { }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public AtariLynxCart(Cart model, Stream data, long offset) : base(model, data, offset) { }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public AtariLynxCart(Cart model, Stream data, long offset, long length) : base(model, data, offset, length) { }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Static Constructors
|
||||
|
||||
/// <summary>
|
||||
/// Create an Atari Lynx cart image from a byte array and offset
|
||||
/// </summary>
|
||||
/// <param name="data">Byte array representing the archive</param>
|
||||
/// <param name="offset">Offset within the array to parse</param>
|
||||
/// <returns>An Atari Lynx cart image wrapper on success, null on failure</returns>
|
||||
public static AtariLynxCart? Create(byte[]? data, int offset)
|
||||
{
|
||||
// If the data is invalid
|
||||
if (data is null || data.Length == 0)
|
||||
return null;
|
||||
|
||||
// If the offset is out of bounds
|
||||
if (offset < 0 || offset >= data.Length)
|
||||
return null;
|
||||
|
||||
// Create a memory stream and use that
|
||||
var dataStream = new MemoryStream(data, offset, data.Length - offset);
|
||||
return Create(dataStream);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create an Atari Lynx cart image from a Stream
|
||||
/// </summary>
|
||||
/// <param name="data">Stream representing the archive</param>
|
||||
/// <returns>An Atari Lynx cart image wrapper on success, null on failure</returns>
|
||||
public static AtariLynxCart? Create(Stream? data)
|
||||
{
|
||||
// If the data is invalid
|
||||
if (data is null || !data.CanRead)
|
||||
return null;
|
||||
|
||||
try
|
||||
{
|
||||
// Cache the current offset
|
||||
long currentOffset = data.Position;
|
||||
|
||||
var model = new Readers.AtariLynxCart().Deserialize(data);
|
||||
if (model is null)
|
||||
return null;
|
||||
|
||||
return new AtariLynxCart(model, data, currentOffset);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -16,10 +16,15 @@ namespace SabreTools.Serialization.Wrappers
|
||||
AACSMediaKeyBlock,
|
||||
|
||||
/// <summary>
|
||||
/// Atari7800 cart image
|
||||
/// Atari 7800 cart image
|
||||
/// </summary>
|
||||
Atari7800Cart,
|
||||
|
||||
/// <summary>
|
||||
/// Atari Lynx cart image
|
||||
/// </summary>
|
||||
AtariLynxCart,
|
||||
|
||||
/// <summary>
|
||||
/// BD+ SVM
|
||||
/// </summary>
|
||||
|
||||
Reference in New Issue
Block a user