mirror of
https://github.com/SabreTools/SabreTools.Serialization.git
synced 2026-07-08 18:06:41 +00:00
Add NES cart reader
This commit is contained in:
@@ -35,7 +35,12 @@ namespace SabreTools.Data.Models.NES
|
||||
/// <summary>
|
||||
/// PlayChoice PROM, if present (16 bytes Data, 16 bytes CounterOut)
|
||||
/// </summary>
|
||||
/// <remarks>This is often missing; see PC10 ROM-Images for details</remarks>
|
||||
/// <remarks>
|
||||
/// This is often missing; see PC10 ROM-Images for details
|
||||
/// 16 bytes RP5H01 PROM Data output (needed to decrypt the INST ROM)
|
||||
/// 16 bytes RP5H01 PROM CounterOut output (needed to decrypt the INST ROM) (usually constant: 00,00,00,00,FF,FF,FF,FF,00,00,00,00,FF,FF,FF,FF)
|
||||
/// </remarks>
|
||||
/// TODO: Split into 2 parts
|
||||
public byte[] PlayChoicePROM { get; set; } = [];
|
||||
|
||||
/// <summary>
|
||||
|
||||
15
SabreTools.Serialization/Models/NES/Constants.cs
Normal file
15
SabreTools.Serialization/Models/NES/Constants.cs
Normal file
@@ -0,0 +1,15 @@
|
||||
namespace SabreTools.Data.Models.NES
|
||||
{
|
||||
public static class Constants
|
||||
{
|
||||
/// <summary>
|
||||
/// NES<EOF>
|
||||
/// </summary>
|
||||
public static readonly byte[] SignatureBytes = [0x4E, 0x45, 0x53, 0x1A];
|
||||
|
||||
/// <summary>
|
||||
/// NES<EOF>
|
||||
/// </summary>
|
||||
public static readonly string SignatureString = "NES" + (char)0x1A;
|
||||
}
|
||||
}
|
||||
@@ -70,7 +70,7 @@ namespace SabreTools.Data.Models.NES
|
||||
/// Number of miscellaneous ROMs present
|
||||
/// </summary>
|
||||
/// <remarks>Only bits 0-1 are used</remarks>
|
||||
public bool MiscellaneousROMs { get; set; }
|
||||
public byte MiscellaneousROMs { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Default Expansion Device
|
||||
|
||||
143
SabreTools.Serialization/Readers/NESCart.cs
Normal file
143
SabreTools.Serialization/Readers/NESCart.cs
Normal file
@@ -0,0 +1,143 @@
|
||||
using System.IO;
|
||||
using SabreTools.Data.Models.NES;
|
||||
using SabreTools.IO.Extensions;
|
||||
using static SabreTools.Data.Models.NES.Constants;
|
||||
|
||||
namespace SabreTools.Serialization.Readers
|
||||
{
|
||||
public class NESCart : 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 NES cart image to fill
|
||||
var cart = new Cart();
|
||||
|
||||
#region Header
|
||||
|
||||
// Try to parse the header
|
||||
var header = ParseHeader(data);
|
||||
if (header == null)
|
||||
return null;
|
||||
|
||||
// Set the header
|
||||
cart.Header = header;
|
||||
|
||||
#endregion
|
||||
|
||||
// Read the trainer data, if necessary
|
||||
#if NET20 || NET35
|
||||
if ((cart.Header.Flag6 & Flag6.TrainerPresent) != 0)
|
||||
#else
|
||||
if (cart.Header.Flag6.HasFlag(Flag6.TrainerPresent))
|
||||
#endif
|
||||
cart.Trainer = data.ReadBytes(512);
|
||||
|
||||
// Read the PRG-ROM data
|
||||
// TODO: Make model for PRG-ROM data blocks
|
||||
int prgRomSize = cart.Header.PRGROMSize * 16384;
|
||||
if (prgRomSize > 0)
|
||||
cart.PRGROMData = data.ReadBytes(prgRomSize);
|
||||
|
||||
// Read the CHR-ROM data, if necessary
|
||||
int chrRomSize = cart.Header.CHRROMSize * 8192;
|
||||
if (chrRomSize > 0)
|
||||
cart.CHRROMData = data.ReadBytes(chrRomSize);
|
||||
|
||||
// Read the PlayChoice INST-ROM and PROM data, if necessary
|
||||
#if NET20 || NET35
|
||||
if ((cart.Header.Flag7 & Flag7.PlayChoice10) != 0
|
||||
&& (cart.Header.Flag7 & Flag7.VSUnisystem) == 0)
|
||||
#else
|
||||
if (cart.Header.Flag7.HasFlag(Flag7.PlayChoice10)
|
||||
&& !cart.Header.Flag7.HasFlag(Flag7.VSUnisystem))
|
||||
#endif
|
||||
{
|
||||
cart.PlayChoiceINSTROM = data.ReadBytes(8192);
|
||||
cart.PlayChoicePROM = data.ReadBytes(32);
|
||||
}
|
||||
|
||||
// Read the cart title, if it exists
|
||||
// TODO: Read cart title
|
||||
|
||||
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)
|
||||
{
|
||||
// Cache data until NES 2.0 flag determined
|
||||
byte[] identificationString = data.ReadBytes(4);
|
||||
if (identificationString.EqualsExactly(SignatureBytes))
|
||||
return null;
|
||||
|
||||
byte prgRomSize = data.ReadByteValue();
|
||||
byte chrRomSize = data.ReadByteValue();
|
||||
Flag6 flag6 = (Flag6)data.ReadByteValue();
|
||||
Flag7 flag7 = (Flag7)data.ReadByteValue();
|
||||
|
||||
// NES 2.0
|
||||
#if NET20 || NET35
|
||||
if ((flag7 & Flag7.NES20) != 0)
|
||||
#else
|
||||
if (flag7.HasFlag(Flag7.NES20))
|
||||
#endif
|
||||
{
|
||||
var obj = new Header2();
|
||||
|
||||
obj.IdentificationString = identificationString;
|
||||
obj.PRGROMSize = prgRomSize;
|
||||
obj.CHRROMSize = chrRomSize;
|
||||
obj.Flag6 = flag6;
|
||||
obj.Flag7 = flag7;
|
||||
obj.MapperMSBSubmapper = data.ReadByteValue();
|
||||
obj.PRGCHRMSB = data.ReadByteValue();
|
||||
obj.PRGRAMEEPROMSize = data.ReadByteValue();
|
||||
obj.CHRRAMSize = data.ReadByteValue();
|
||||
obj.CPUPPUTiming = (CPUPPUTiming)data.ReadByteValue();
|
||||
obj.ExtendedSystemType = (ExtendedSystemType)data.ReadByteValue();
|
||||
obj.MiscellaneousROMs = data.ReadByteValue();
|
||||
obj.DefaultExpansionDevice = (DefaultExpansionDevice)data.ReadByteValue();
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
// NES 1.0
|
||||
else
|
||||
{
|
||||
var obj = new Header1();
|
||||
|
||||
obj.IdentificationString = identificationString;
|
||||
obj.PRGROMSize = prgRomSize;
|
||||
obj.CHRROMSize = chrRomSize;
|
||||
obj.Flag6 = flag6;
|
||||
obj.Flag7 = flag7;
|
||||
obj.PRGRAMSize = data.ReadByteValue();
|
||||
obj.TVSystem = (TVSystem)data.ReadByteValue();
|
||||
obj.Flag10 = (Flag10)data.ReadByteValue();
|
||||
obj.Padding = data.ReadBytes(5);
|
||||
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user