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 { /// 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; } /// /// Write CHR-ROM data to the stream /// /// Stream to write to /// True if the writing was successful, false otherwise 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; } } /// /// Write header data to the stream /// /// Stream to write to /// True if the writing was successful, false otherwise 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; } } /// /// Write INST-ROM data to the stream /// /// Stream to write to /// True if the writing was successful, false otherwise 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; } } /// /// Write PRG-ROM data to the stream /// /// Stream to write to /// True if the writing was successful, false otherwise 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; } } /// /// Write trainer data to the stream /// /// Stream to write to /// True if the writing was successful, false otherwise 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; } } } }