mirror of
https://github.com/SabreTools/SabreTools.Serialization.git
synced 2026-07-08 18:06:41 +00:00
323 lines
10 KiB
C#
323 lines
10 KiB
C#
using System;
|
|
using System.IO;
|
|
using SabreTools.Data.Models.NES;
|
|
using SabreTools.IO.Extensions;
|
|
using SabreTools.Numerics.Extensions;
|
|
|
|
namespace SabreTools.Serialization.Writers
|
|
{
|
|
public class NESCart : 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;
|
|
|
|
// Trainer data
|
|
if (obj.Trainer is not null && obj.Trainer.Length > 0 && !WriteTrainer(obj.Trainer, stream))
|
|
return null;
|
|
|
|
// PRG-ROM data
|
|
if (!WritePrgRom(obj.PrgRomData, stream))
|
|
return null;
|
|
|
|
// CHR-ROM data
|
|
if (obj.ChrRomData is not null && obj.ChrRomData.Length > 0 && !WriteChrRom(obj.ChrRomData, stream))
|
|
return null;
|
|
|
|
// INST-ROM data
|
|
if (obj.PlayChoiceInstRom is not null && obj.PlayChoiceInstRom.Length > 0 && !WritePlayChoiceInstRom(obj.PlayChoiceInstRom, stream))
|
|
return null;
|
|
|
|
// TODO: Add PlayChoice-10 PROM data writing
|
|
|
|
// Return the stream
|
|
stream.SeekIfPossible(0, SeekOrigin.Begin);
|
|
return stream;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Write CHR-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 WriteChrRom(byte[]? obj, Stream stream)
|
|
{
|
|
if (obj is null || obj.Length == 0)
|
|
{
|
|
if (Debug) Console.WriteLine("CHR-ROM data was invalid!");
|
|
return false;
|
|
}
|
|
|
|
// Try to write the data
|
|
try
|
|
{
|
|
if (Debug) Console.WriteLine("Attempting to write CHR-ROM data");
|
|
|
|
stream.Write(obj);
|
|
stream.Flush();
|
|
return true;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
if (Debug) Console.Error.WriteLine(ex);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/// <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(CartHeader? 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.IdentificationString);
|
|
stream.Flush();
|
|
|
|
// Byte 4
|
|
stream.Write(obj.PrgRomSize);
|
|
stream.Flush();
|
|
|
|
// Byte 5
|
|
stream.Write(obj.ChrRomSize);
|
|
stream.Flush();
|
|
|
|
// Byte 6
|
|
byte byte6 = 0;
|
|
byte6 |= (byte)obj.NametableArrangement;
|
|
if (obj.BatteryBackedPrgRam)
|
|
byte6 |= 0b00000010;
|
|
if (obj.TrainerPresent)
|
|
byte6 |= 0b00000100;
|
|
if (obj.AlternativeNametableLayout)
|
|
byte6 |= 0b00001000;
|
|
byte6 |= (byte)(obj.MapperLowerNibble << 4);
|
|
stream.Write(byte6);
|
|
stream.Flush();
|
|
|
|
// Byte 7
|
|
byte byte7 = 0;
|
|
byte7 |= (byte)obj.ConsoleType;
|
|
if (obj.NES20)
|
|
byte7 |= 0b00001000;
|
|
byte7 |= (byte)(obj.MapperUpperNibble << 4);
|
|
stream.Write(byte7);
|
|
stream.Flush();
|
|
|
|
if (obj is CartHeader1 header1)
|
|
{
|
|
// Byte 8
|
|
stream.Write(header1.PrgRamSize);
|
|
stream.Flush();
|
|
|
|
// Byte 9
|
|
stream.Write((byte)header1.TVSystem);
|
|
stream.Flush();
|
|
|
|
// Byte 10
|
|
byte byte10 = 0;
|
|
byte10 |= (byte)header1.TVSystemExtended;
|
|
byte10 |= (byte)(header1.Byte10ReservedBits23 << 2);
|
|
if (header1.PrgRamPresent)
|
|
byte10 |= 0b00010000;
|
|
if (header1.HasBusConflicts)
|
|
byte10 |= 0b00100000;
|
|
byte10 |= (byte)(header1.Byte10ReservedBits67 << 6);
|
|
stream.Write(byte10);
|
|
stream.Flush();
|
|
|
|
// Bytes 11-15
|
|
stream.Write(header1.Padding);
|
|
stream.Flush();
|
|
}
|
|
else if (obj is CartHeader2 header2)
|
|
{
|
|
// Byte 8
|
|
byte byte8 = 0;
|
|
byte8 |= header2.MapperMSB;
|
|
byte8 |= (byte)(header2.Submapper << 4);
|
|
stream.Write(byte8);
|
|
stream.Flush();
|
|
|
|
// Byte 9
|
|
byte byte9 = 0;
|
|
byte9 |= header2.PrgRomSizeMSB;
|
|
byte9 |= (byte)(header2.ChrRomSizeMSB << 4);
|
|
stream.Write(byte9);
|
|
stream.Flush();
|
|
|
|
// Byte 10
|
|
byte byte10 = 0;
|
|
byte10 |= header2.PrgRamShiftCount;
|
|
byte10 |= (byte)(header2.PrgNvramEepromShiftCount << 4);
|
|
stream.Write(byte10);
|
|
stream.Flush();
|
|
|
|
// Byte 11
|
|
byte byte11 = 0;
|
|
byte11 |= header2.ChrRamShiftCount;
|
|
byte11 |= (byte)(header2.ChrNvramShiftCount << 4);
|
|
stream.Write(byte11);
|
|
stream.Flush();
|
|
|
|
// Byte 12
|
|
stream.Write((byte)header2.CPUPPUTiming);
|
|
stream.Flush();
|
|
|
|
// Byte 13
|
|
if (obj.ConsoleType == ConsoleType.VSUnisystem)
|
|
{
|
|
byte byte13 = 0;
|
|
byte13 |= (byte)header2.VsSystemType;
|
|
byte13 |= (byte)((byte)header2.VsHardwareType << 4);
|
|
stream.Write(byte13);
|
|
stream.Flush();
|
|
}
|
|
else if (obj.ConsoleType == ConsoleType.ExtendedConsoleType)
|
|
{
|
|
byte byte13 = 0;
|
|
byte13 |= (byte)header2.ExtendedConsoleType;
|
|
byte13 |= (byte)(header2.Byte13ReservedBits47 << 4);
|
|
stream.Write(byte13);
|
|
stream.Flush();
|
|
}
|
|
else
|
|
{
|
|
stream.Write(header2.Reserved13);
|
|
stream.Flush();
|
|
}
|
|
|
|
// Byte 14
|
|
stream.Write(header2.MiscellaneousROMs);
|
|
stream.Flush();
|
|
|
|
// Byte 15
|
|
stream.Write((byte)header2.DefaultExpansionDevice);
|
|
stream.Flush();
|
|
}
|
|
else
|
|
{
|
|
if (Debug) Console.Error.WriteLine("Unknown header type, incomplete header data output");
|
|
}
|
|
|
|
// Header extracted
|
|
return true;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
if (Debug) Console.Error.WriteLine(ex);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Write INST-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 WritePlayChoiceInstRom(byte[]? obj, Stream stream)
|
|
{
|
|
if (obj is null || obj.Length == 0)
|
|
{
|
|
if (Debug) Console.WriteLine("INST-ROM data was invalid!");
|
|
return false;
|
|
}
|
|
|
|
// Try to write the data
|
|
try
|
|
{
|
|
if (Debug) Console.WriteLine("Attempting to write INST-ROM data");
|
|
|
|
stream.Write(obj);
|
|
stream.Flush();
|
|
return true;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
if (Debug) Console.Error.WriteLine(ex);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Write PRG-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 WritePrgRom(byte[]? obj, Stream stream)
|
|
{
|
|
if (obj is null || obj.Length == 0)
|
|
{
|
|
if (Debug) Console.WriteLine("PRG-ROM data was invalid!");
|
|
return false;
|
|
}
|
|
|
|
// Try to write the data
|
|
try
|
|
{
|
|
if (Debug) Console.WriteLine("Attempting to write PRG-ROM data");
|
|
|
|
stream.Write(obj);
|
|
stream.Flush();
|
|
return true;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
if (Debug) Console.Error.WriteLine(ex);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Write trainer 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 WriteTrainer(byte[]? obj, Stream stream)
|
|
{
|
|
if (obj is null || obj.Length == 0)
|
|
{
|
|
if (Debug) Console.WriteLine("Trainer data was invalid!");
|
|
return false;
|
|
}
|
|
|
|
// Try to write the data
|
|
try
|
|
{
|
|
if (Debug) Console.WriteLine("Attempting to write trainer data");
|
|
|
|
stream.Write(obj);
|
|
stream.Flush();
|
|
return true;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
if (Debug) Console.Error.WriteLine(ex);
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
}
|