diff --git a/SabreTools.Wrappers/NESCart.Extraction.cs b/SabreTools.Wrappers/NESCart.Extraction.cs
index ad76939c..fa043c37 100644
--- a/SabreTools.Wrappers/NESCart.Extraction.cs
+++ b/SabreTools.Wrappers/NESCart.Extraction.cs
@@ -1,7 +1,5 @@
using System;
using System.IO;
-using SabreTools.Data.Models.NES;
-using SabreTools.Numerics.Extensions;
namespace SabreTools.Wrappers
{
@@ -28,141 +26,8 @@ namespace SabreTools.Wrappers
// 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.IdentificationString);
- fs.Flush();
-
- // Byte 4
- fs.Write(Header.PrgRomSize);
- fs.Flush();
-
- // Byte 5
- fs.Write(Header.ChrRomSize);
- fs.Flush();
-
- // Byte 6
- byte byte6 = 0;
- byte6 |= (byte)NametableArrangement;
- if (BatteryBackedPrgRam)
- byte6 |= 0b00000010;
- if (TrainerPresent)
- byte6 |= 0b00000100;
- if (AlternativeNametableLayout)
- byte6 |= 0b00001000;
- byte6 |= (byte)(Header.MapperLowerNibble << 4);
- fs.Write(byte6);
- fs.Flush();
-
- // Byte 7
- byte byte7 = 0;
- byte7 |= (byte)ConsoleType;
- if (NES20)
- byte7 |= 0b00001000;
- byte7 |= (byte)(Header.MapperUpperNibble << 4);
- fs.Write(byte7);
- fs.Flush();
-
- if (Header is CartHeader1 header1)
- {
- // Byte 8
- fs.Write(header1.PrgRamSize);
- fs.Flush();
-
- // Byte 9
- fs.Write((byte)header1.TVSystem);
- fs.Flush();
-
- // Byte 10
- byte byte10 = 0;
- byte10 |= (byte)TVSystemExtended;
- byte10 |= (byte)(header1.Byte10ReservedBits23 << 2);
- if (PrgRamPresent)
- byte10 |= 0b00010000;
- if (HasBusConflicts)
- byte10 |= 0b00100000;
- byte10 |= (byte)(header1.Byte10ReservedBits67 << 6);
- fs.Write(byte10);
- fs.Flush();
-
- // Bytes 11-15
- fs.Write(header1.Padding);
- fs.Flush();
- }
- else if (Header is CartHeader2 header2)
- {
- // Byte 8
- byte byte8 = 0;
- byte8 |= header2.MapperMSB;
- byte8 |= (byte)(header2.Submapper << 4);
- fs.Write(byte8);
- fs.Flush();
-
- // Byte 9
- byte byte9 = 0;
- byte9 |= header2.PrgRomSizeMSB;
- byte9 |= (byte)(header2.ChrRomSizeMSB << 4);
- fs.Write(byte9);
- fs.Flush();
-
- // Byte 10
- byte byte10 = 0;
- byte10 |= header2.PrgRamShiftCount;
- byte10 |= (byte)(header2.PrgNvramEepromShiftCount << 4);
- fs.Write(byte10);
- fs.Flush();
-
- // Byte 11
- byte byte11 = 0;
- byte11 |= header2.ChrRamShiftCount;
- byte11 |= (byte)(header2.ChrNvramShiftCount << 4);
- fs.Write(byte11);
- fs.Flush();
-
- // Byte 12
- fs.Write((byte)header2.CPUPPUTiming);
- fs.Flush();
-
- // Byte 13
- if (ConsoleType == ConsoleType.VSUnisystem)
- {
- byte byte13 = 0;
- byte13 |= (byte)header2.VsSystemType;
- byte13 |= (byte)((byte)header2.VsHardwareType << 4);
- fs.Write(byte13);
- fs.Flush();
- }
- else if (ConsoleType == ConsoleType.ExtendedConsoleType)
- {
- byte byte13 = 0;
- byte13 |= (byte)header2.ExtendedConsoleType;
- byte13 |= (byte)(header2.Byte13ReservedBits47 << 4);
- fs.Write(byte13);
- fs.Flush();
- }
- else
- {
- fs.Write(header2.Reserved13);
- fs.Flush();
- }
-
- // Byte 14
- fs.Write(header2.MiscellaneousROMs);
- fs.Flush();
-
- // Byte 15
- fs.Write((byte)DefaultExpansionDevice);
- fs.Flush();
- }
- else
- {
- if (includeDebug) Console.Error.WriteLine("Unknown header type, incomplete header data output");
- }
-
- // Header extracted
- success = true;
+ success |= WriteHeader(fs, includeDebug);
}
catch (Exception ex)
{
@@ -179,13 +44,8 @@ namespace SabreTools.Wrappers
// Try to write the data
try
{
- // Open the output file for writing
using var fs = File.Open(trainerPath, FileMode.Create, FileAccess.Write, FileShare.None);
- fs.Write(Trainer, 0, Trainer.Length);
- fs.Flush();
-
- // Trainer extracted
- success = true;
+ success |= WriteTrainer(fs, includeDebug);
}
catch (Exception ex)
{
@@ -204,11 +64,7 @@ namespace SabreTools.Wrappers
{
// Open the output file for writing
using var fs = File.Open(prgRomPath, FileMode.Create, FileAccess.Write, FileShare.None);
- fs.Write(PrgRomData, 0, PrgRomData.Length);
- fs.Flush();
-
- // PRG-ROM extracted
- success = true;
+ success |= WritePrgRom(fs, includeDebug);
}
catch (Exception ex)
{
@@ -227,11 +83,7 @@ namespace SabreTools.Wrappers
{
// Open the output file for writing
using var fs = File.Open(chrRomPath, FileMode.Create, FileAccess.Write, FileShare.None);
- fs.Write(ChrRomData, 0, ChrRomData.Length);
- fs.Flush();
-
- // CHR-ROM extracted
- success = true;
+ success |= WriteChrRom(fs, includeDebug);
}
catch (Exception ex)
{
@@ -250,10 +102,8 @@ namespace SabreTools.Wrappers
{
// Open the output file for writing
using var fs = File.Open(unheaderedPath, FileMode.Create, FileAccess.Write, FileShare.None);
- fs.Write(PrgRomData, 0, PrgRomData.Length);
- fs.Flush();
- fs.Write(ChrRomData, 0, ChrRomData.Length);
- fs.Flush();
+ success |= WritePrgRom(fs, includeDebug);
+ success |= WriteChrRom(fs, includeDebug);
// Unheadered ROM extracted
success = true;
@@ -275,11 +125,7 @@ namespace SabreTools.Wrappers
{
// Open the output file for writing
using var fs = File.Open(instRomPath, FileMode.Create, FileAccess.Write, FileShare.None);
- fs.Write(PlayChoiceInstRom, 0, PlayChoiceInstRom.Length);
- fs.Flush();
-
- // PC10 INST-ROM extracted
- success = true;
+ success |= WritePlayChoiceInstRom(fs, includeDebug);
}
catch (Exception ex)
{
diff --git a/SabreTools.Wrappers/NESCart.Writing.cs b/SabreTools.Wrappers/NESCart.Writing.cs
new file mode 100644
index 00000000..65fbed57
--- /dev/null
+++ b/SabreTools.Wrappers/NESCart.Writing.cs
@@ -0,0 +1,312 @@
+using System;
+using System.IO;
+using SabreTools.Data.Models.NES;
+using SabreTools.Numerics.Extensions;
+
+namespace SabreTools.Wrappers
+{
+ public partial class NESCart : IWritable
+ {
+ ///
+ public bool Write(string outputPath, bool includeDebug)
+ {
+ // Ensure an output path
+ if (string.IsNullOrEmpty(outputPath))
+ {
+ string outputFilename = Filename is null
+ ? (Guid.NewGuid().ToString() + ".nes")
+ : (Filename + ".new");
+ outputPath = Path.GetFullPath(outputFilename);
+ }
+
+ // Check for invalid data
+ if (Header is null || PrgRomData is null || PrgRomData.Length == 0)
+ {
+ if (includeDebug) Console.WriteLine("Model was invalid, cannot write!");
+ 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;
+
+ // Trainer data
+ if (Trainer is not null && Trainer.Length > 0)
+ WriteTrainer(fs, includeDebug);
+
+ // PRG-ROM data
+ if (!WritePrgRom(fs, includeDebug))
+ return false;
+
+ // CHR-ROM data
+ if (ChrRomData is not null && ChrRomData.Length > 0)
+ WriteChrRom(fs, includeDebug);
+
+ // INST-ROM data
+ if (PlayChoiceInstRom is not null && PlayChoiceInstRom.Length > 0)
+ WritePlayChoiceInstRom(fs, includeDebug);
+
+ // TODO: Add PlayChoice-10 PROM data writing
+
+ return true;
+ }
+
+ ///
+ /// Write CHR-ROM data to the stream
+ ///
+ /// Stream to write to
+ /// True to include debug data, false otherwise
+ /// True if the writing was successful, false otherwise
+ private bool WriteChrRom(Stream stream, bool includeDebug)
+ {
+ if (includeDebug) Console.WriteLine("Attempting to write CHR-ROM data");
+
+ // Try to write the data
+ try
+ {
+ stream.Write(ChrRomData, 0, ChrRomData.Length);
+ stream.Flush();
+ return true;
+ }
+ catch (Exception ex)
+ {
+ if (includeDebug) Console.Error.WriteLine(ex);
+ return false;
+ }
+ }
+
+ ///
+ /// Write header data to the stream
+ ///
+ /// Stream to write to
+ /// True to include debug data, false otherwise
+ /// True if the writing was successful, false otherwise
+ 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.IdentificationString);
+ stream.Flush();
+
+ // Byte 4
+ stream.Write(Header.PrgRomSize);
+ stream.Flush();
+
+ // Byte 5
+ stream.Write(Header.ChrRomSize);
+ stream.Flush();
+
+ // Byte 6
+ byte byte6 = 0;
+ byte6 |= (byte)NametableArrangement;
+ if (BatteryBackedPrgRam)
+ byte6 |= 0b00000010;
+ if (TrainerPresent)
+ byte6 |= 0b00000100;
+ if (AlternativeNametableLayout)
+ byte6 |= 0b00001000;
+ byte6 |= (byte)(Header.MapperLowerNibble << 4);
+ stream.Write(byte6);
+ stream.Flush();
+
+ // Byte 7
+ byte byte7 = 0;
+ byte7 |= (byte)ConsoleType;
+ if (NES20)
+ byte7 |= 0b00001000;
+ byte7 |= (byte)(Header.MapperUpperNibble << 4);
+ stream.Write(byte7);
+ stream.Flush();
+
+ if (Header 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)TVSystemExtended;
+ byte10 |= (byte)(header1.Byte10ReservedBits23 << 2);
+ if (PrgRamPresent)
+ byte10 |= 0b00010000;
+ if (HasBusConflicts)
+ byte10 |= 0b00100000;
+ byte10 |= (byte)(header1.Byte10ReservedBits67 << 6);
+ stream.Write(byte10);
+ stream.Flush();
+
+ // Bytes 11-15
+ stream.Write(header1.Padding);
+ stream.Flush();
+ }
+ else if (Header 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 (ConsoleType == ConsoleType.VSUnisystem)
+ {
+ byte byte13 = 0;
+ byte13 |= (byte)header2.VsSystemType;
+ byte13 |= (byte)((byte)header2.VsHardwareType << 4);
+ stream.Write(byte13);
+ stream.Flush();
+ }
+ else if (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)DefaultExpansionDevice);
+ stream.Flush();
+ }
+ else
+ {
+ if (includeDebug) Console.Error.WriteLine("Unknown header type, incomplete header data output");
+ }
+
+ // Header extracted
+ return true;
+ }
+ catch (Exception ex)
+ {
+ if (includeDebug) Console.Error.WriteLine(ex);
+ return false;
+ }
+ }
+
+ ///
+ /// Write INST-ROM data to the stream
+ ///
+ /// Stream to write to
+ /// True to include debug data, false otherwise
+ /// True if the writing was successful, false otherwise
+ private bool WritePlayChoiceInstRom(Stream stream, bool includeDebug)
+ {
+ if (includeDebug) Console.WriteLine("Attempting to write INST-ROM data");
+
+ // Try to write the data
+ try
+ {
+ stream.Write(PlayChoiceInstRom, 0, PlayChoiceInstRom.Length);
+ stream.Flush();
+ return true;
+ }
+ catch (Exception ex)
+ {
+ if (includeDebug) Console.Error.WriteLine(ex);
+ return false;
+ }
+ }
+
+ ///
+ /// Write PRG-ROM data to the stream
+ ///
+ /// Stream to write to
+ /// True to include debug data, false otherwise
+ /// True if the writing was successful, false otherwise
+ private bool WritePrgRom(Stream stream, bool includeDebug)
+ {
+ if (includeDebug) Console.WriteLine("Attempting to write PRG-ROM data");
+
+ // Try to write the data
+ try
+ {
+ stream.Write(PrgRomData, 0, PrgRomData.Length);
+ stream.Flush();
+ return true;
+ }
+ catch (Exception ex)
+ {
+ if (includeDebug) Console.Error.WriteLine(ex);
+ return false;
+ }
+ }
+
+ ///
+ /// Write trainer data to the stream
+ ///
+ /// Stream to write to
+ /// True to include debug data, false otherwise
+ /// True if the writing was successful, false otherwise
+ private bool WriteTrainer(Stream stream, bool includeDebug)
+ {
+ if (includeDebug) Console.WriteLine("Attempting to write trainer data");
+
+ // Try to write the data
+ try
+ {
+ stream.Write(Trainer, 0, Trainer.Length);
+ stream.Flush();
+ return true;
+ }
+ catch (Exception ex)
+ {
+ if (includeDebug) Console.Error.WriteLine(ex);
+ return false;
+ }
+ }
+ }
+}