diff --git a/SabreTools.Serialization/Extensions/NESCart.cs b/SabreTools.Serialization/Extensions/NESCart.cs
new file mode 100644
index 00000000..22f72d8e
--- /dev/null
+++ b/SabreTools.Serialization/Extensions/NESCart.cs
@@ -0,0 +1,185 @@
+using SabreTools.Data.Models.NES;
+
+namespace SabreTools.Data.Extensions
+{
+ public static class NESCart
+ {
+ ///
+ /// Convert a value to string
+ ///
+ public static string FromCPUPPUTiming(this CPUPPUTiming timing)
+ {
+ return timing switch
+ {
+ CPUPPUTiming.RP2C02 => "RP2C02 (NTSC NES)",
+ CPUPPUTiming.RP2C07 => "RP2C07 (Licensed PAL NES)",
+ CPUPPUTiming.MultipleRegion => "Multiple-region",
+ CPUPPUTiming.UA6538 => "UA6538 (Dendy)",
+ _ => $"Unknown {(byte)timing}",
+ };
+ }
+
+ ///
+ /// Convert a value to string
+ ///
+ public static string FromDefaultExpansionDevice(this DefaultExpansionDevice device)
+ {
+ return device switch
+ {
+ DefaultExpansionDevice.Unspecified => "Unspecified",
+ DefaultExpansionDevice.StandardControllers => "Standard NES/Famicom controllers",
+ DefaultExpansionDevice.NESFourScore => "NES Four Score/Satellite with two additional standard controllers",
+ DefaultExpansionDevice.FamicomFourPlayersAdapter => "Famicom Four Players Adapter with two additional standard controllers using the 'simple' protocol",
+ DefaultExpansionDevice.VsSystem4016 => "Vs. System (1P via $4016)",
+ DefaultExpansionDevice.VsSystem4017 => "Vs. System (1P via $4017)",
+ DefaultExpansionDevice.Reserved06 => "Reserved (0x06)",
+ DefaultExpansionDevice.VsZapper => "Vs. Zapper",
+ DefaultExpansionDevice.Zapper4017 => "Zapper ($4017)",
+ DefaultExpansionDevice.TwoZappers => "Two Zappers",
+ DefaultExpansionDevice.BandaiHyperShotLightgun => "Bandai Hyper Shot Lightgun",
+ DefaultExpansionDevice.PowerPadSideA => "Power Pad Side A",
+ DefaultExpansionDevice.PowerPadSideB => "Power Pad Side B",
+ DefaultExpansionDevice.FamilyTrainerSideA => "Family Trainer Side A",
+ DefaultExpansionDevice.FamilyTrainerSideB => "Family Trainer Side B",
+ DefaultExpansionDevice.ArkanoidVausControllerNES => "Arkanoid Vaus Controller (NES)",
+ DefaultExpansionDevice.ArkanoidVausControllerFamicom => "Arkanoid Vaus Controller (Famicom)",
+ DefaultExpansionDevice.TwoVausControllersPlusFamicomDataRecorder => "Two Vaus Controllers plus Famicom Data Recorder",
+ DefaultExpansionDevice.KonamiHyperShotController => "Konami Hyper Shot Controller",
+ DefaultExpansionDevice.CoconutsPachinkoController => "Coconuts Pachinko Controller",
+ DefaultExpansionDevice.ExcitingBoxingPunchingBag => "Exciting Boxing Punching Bag (Blowup Doll)",
+ DefaultExpansionDevice.JissenMahjongController => "Jissen Mahjong Controller",
+ DefaultExpansionDevice.YonezawaPartyTap => "米澤 (Yonezawa) Party Tap",
+ DefaultExpansionDevice.OekaKidsTablet => "Oeka Kids Tablet",
+ DefaultExpansionDevice.SunsoftBarcodeBattler => "Sunsoft Barcode Battler",
+ DefaultExpansionDevice.MiraclePianoKeyboard => "Miracle Piano Keyboard",
+ DefaultExpansionDevice.PokkunMoguraaTapTapMat => "Pokkun Moguraa Tap-tap Mat (Whack-a-Mole Mat and Mallet)",
+ DefaultExpansionDevice.TopRider => "Top Rider (Inflatable Bicycle)",
+ DefaultExpansionDevice.DoubleFisted => "Double-Fisted (Requires or allows use of two controllers by one player)",
+ DefaultExpansionDevice.Famicom3DSystem => "Famicom 3D System",
+ DefaultExpansionDevice.DoremikkoKeyboard => "Doremikko Keyboard",
+ DefaultExpansionDevice.ROBGyromite => "R.O.B. Gyromite",
+ DefaultExpansionDevice.FamicomDataRecorder => "Famicom Data Recorder ('silent' keyboard)",
+ DefaultExpansionDevice.ASCIITurboFile => "ASCII Turbo File",
+ DefaultExpansionDevice.IGSStorageBattleBox => "IGS Storage Battle Box",
+ DefaultExpansionDevice.FamilyBASICKeyboardPlusFamicomDataRecorder => "Family BASIC Keyboard plus Famicom Data Recorder",
+ DefaultExpansionDevice.DongdaPECKeyboard => "东达 (Dōngdá) PEC Keyboard",
+ DefaultExpansionDevice.BitCorpBit79Keyboard => "普澤 (Pǔzé, a.k.a. Bit Corp.) Bit-79 Keyboard",
+ DefaultExpansionDevice.SuborKeyboard => "小霸王 (Xiǎobàwáng, a.k.a. Subor) Keyboard",
+ DefaultExpansionDevice.SuborKeyboardPlusMacroWinnersMouse => "小霸王 (Xiǎobàwáng, a.k.a. Subor) Keyboard plus Macro Winners Mouse",
+ DefaultExpansionDevice.SuborKeyboardPlusSuborMouse4016 => "小霸王 (Xiǎobàwáng, a.k.a. Subor) Keyboard plus Subor Mouse via $4016",
+ DefaultExpansionDevice.SNESMouse4016 => "SNES Mouse ($4016)",
+ DefaultExpansionDevice.Multicart => "Multicart",
+ DefaultExpansionDevice.TwoSNESControllers => "Two SNES controllers replacing the two standard NES controllers",
+ DefaultExpansionDevice.RacerMateBicycle => "RacerMate Bicycle",
+ DefaultExpansionDevice.UForce => "U-Force",
+ DefaultExpansionDevice.ROBStackUp => "R.O.B. Stack-Up",
+ DefaultExpansionDevice.CityPatrolmanLightgun => "City Patrolman Lightgun",
+ DefaultExpansionDevice.SharpC1CassetteInterface => "Sharp C1 Cassette Interface",
+ DefaultExpansionDevice.StandardControllerWithSwappedInputs => "Standard Controller with swapped Left-Right/Up-Down/B-A",
+ DefaultExpansionDevice.ExcaliburSudokuPad => "Excalibur Sudoku Pad",
+ DefaultExpansionDevice.ABLPinball => "ABL Pinball",
+ DefaultExpansionDevice.GoldenNuggetCasinoExtraButtons => "Golden Nugget Casino extra buttons",
+ DefaultExpansionDevice.KedaKeyboard => "科达 (Kēdá) Keyboard",
+ DefaultExpansionDevice.SuborKeyboardPlusSuborMouse4017 => "小霸王 (Xiǎobàwáng, a.k.a. Subor) Keyboard plus Subor Mouse via $4017",
+ DefaultExpansionDevice.PortTestController => "Port test controller",
+ DefaultExpansionDevice.BandaiMultiGamePlayerGamepad => "Bandai Multi Game Player Gamepad buttons",
+ DefaultExpansionDevice.VenomTVDanceMat => "Venom TV Dance Mat",
+ DefaultExpansionDevice.LGTVRemoteControl => "LG TV Remote Control",
+ DefaultExpansionDevice.FamicomNetworkController => "Famicom Network Controller",
+ DefaultExpansionDevice.KingFishingController => "King Fishing Controller",
+ DefaultExpansionDevice.CroakyKaraokeController => "Croaky Karaoke Controller",
+ DefaultExpansionDevice.KingwonKeyboard => "科王 (Kēwáng, a.k.a. Kingwon) Keyboard",
+ DefaultExpansionDevice.ZechengKeyboard => "泽诚 (Zéchéng) Keyboard",
+ DefaultExpansionDevice.SuborKeyboardPlusL90RotatedPS2Mouse4017 => "小霸王 (Xiǎobàwáng, a.k.a. Subor) Keyboard plus L90-rotated PS/2 mouse in $4017",
+ DefaultExpansionDevice.PS2KeyboardInUM6578PS2PortPS2Mouse4017 => "PS/2 Keyboard in UM6578 PS/2 port, PS/2 Mouse via $4017",
+ DefaultExpansionDevice.PS2MouseInUM6578PS2Port => "PS/2 Mouse in UM6578 PS/2 port",
+ DefaultExpansionDevice.YuxingMouse4016 => "裕兴 (Yùxìng) Mouse via $4016",
+ DefaultExpansionDevice.SuborKeyboardPlusYuxingMouse4016 => "小霸王 (Xiǎobàwáng, a.k.a. Subor) Keyboard plus 裕兴 (Yùxìng) Mouse mouse in $4016",
+ DefaultExpansionDevice.GigggleTVPump => "Gigggle TV Pump",
+ DefaultExpansionDevice.BBKKeyboardPlusR90RotatedPS2Mouse4017 => "步步高 (Bùbùgāo, a.k.a. BBK) Keyboard plus R90-rotated PS/2 mouse in $4017",
+ DefaultExpansionDevice.MagicalCooking => "Magical Cooking",
+ DefaultExpansionDevice.SNESMouse4017 => "SNES Mouse ($4017)",
+ DefaultExpansionDevice.Zapper4016 => "Zapper ($4016)",
+ DefaultExpansionDevice.ArkanoidVausControllerPrototype => "Arkanoid Vaus Controller (Prototype)",
+ DefaultExpansionDevice.TVMahjongGameController => "TV 麻雀 Game (TV Mahjong Game) Controller",
+ DefaultExpansionDevice.MahjongGekitouDensetsuController => "麻雀激闘伝説 (Mahjong Gekitou Densetsu) Controller",
+ DefaultExpansionDevice.SuborKeyboardPlusXInvertedPS2Mouse4017 => "小霸王 (Xiǎobàwáng, a.k.a. Subor) Keyboard plus X-inverted PS/2 mouse in $4017",
+ DefaultExpansionDevice.IBMPCXTKeyboard => "IBM PC/XT Keyboard",
+ DefaultExpansionDevice.SuborKeyboardPlusMegaBookMouse => "小霸王 (Xiǎobàwáng, a.k.a. Subor) Keyboard plus Mega Book Mouse",
+ _ => $"Unknown {(byte)device}",
+ };
+ }
+
+ ///
+ /// Convert a value to string
+ ///
+ public static string FromExtendedConsoleType(this ExtendedConsoleType type)
+ {
+ return type switch
+ {
+ ExtendedConsoleType.RegularSystem => "Regular NES/Famicom/Dendy",
+ ExtendedConsoleType.NintendoVsSystem => "Nintendo Vs. System",
+ ExtendedConsoleType.Playchoice10 => "Playchoice 10",
+ ExtendedConsoleType.RegularFamicloneDecimalMode => "Regular Famiclone, but with CPU that supports Decimal Mode",
+ ExtendedConsoleType.RegularNESWithEPSM => "Regular NES/Famicom with EPSM module or plug-through cartridge",
+ ExtendedConsoleType.VRTechnologyVT01 => "V.R. Technology VT01 with red/cyan STN palette",
+ ExtendedConsoleType.VRTechnologyVT02 => "V.R. Technology VT02",
+ ExtendedConsoleType.VRTechnologyVT03 => "V.R. Technology VT03",
+ ExtendedConsoleType.VRTechnologyVT09 => "V.R. Technology VT09",
+ ExtendedConsoleType.VRTechnologyVT32 => "V.R. Technology VT32",
+ ExtendedConsoleType.VRTechnologyVT369 => "V.R. Technology VT369",
+ ExtendedConsoleType.UMCUM6578 => "UMC UM6578",
+ ExtendedConsoleType.FamicomNetworkSystem => "Famicom Network System",
+ ExtendedConsoleType.ReservedD => "Reserved (0x0D)",
+ ExtendedConsoleType.ReservedE => "Reserved (0x0E)",
+ ExtendedConsoleType.ReservedF => "Reserved (0x0F)",
+ _ => $"Unknown {(byte)type}",
+ };
+ }
+
+ ///
+ /// Convert a value to string
+ ///
+ public static string FromVsHardwareType(this VsHardwareType type)
+ {
+ return type switch
+ {
+ VsHardwareType.VsUnisystem => "Vs. Unisystem (normal)",
+ VsHardwareType.VsUnisystemRBIBaseballProtection => "Vs. Unisystem (RBI Baseball protection)",
+ VsHardwareType.VsUnisystemTKOBoxingProtection => "Vs. Unisystem (TKO Boxing protection)",
+ VsHardwareType.VsUnisystemSuperXeviousProtection => "Vs. Unisystem (Super Xevious protection)",
+ VsHardwareType.VsUnisystemVsIceClimberJapanProtection => "Vs. Unisystem (Vs. Ice Climber Japan protection)",
+ VsHardwareType.VsDualSystem => "Vs. Dual System (normal)",
+ VsHardwareType.VsDualSystemRaidOnBungelingBayProtection => "Vs. Dual System (Raid on Bungeling Bay protection)",
+ _ => $"Unknown {(byte)type}",
+ };
+ }
+
+ ///
+ /// Convert a value to string
+ ///
+ public static string FromVsSystemType(this VsSystemType type)
+ {
+ return type switch
+ {
+ VsSystemType.AnyRP2C03RC2C03Variant => "Any RP2C03/RC2C03 variant",
+ VsSystemType.Reserved1 => "Reserved (0x01)",
+ VsSystemType.RP2C040001 => "RP2C04-0001",
+ VsSystemType.RP2C040002 => "RP2C04-0002",
+ VsSystemType.RP2C040003 => "RP2C04-0003",
+ VsSystemType.RP2C040004 => "RP2C04-0004",
+ VsSystemType.Reserved6 => "Reserved (0x06)",
+ VsSystemType.Reserved7 => "Reserved (0x07)",
+ VsSystemType.RC2C0501 => "RC2C05-01 (signature unknown)",
+ VsSystemType.RC2C0502 => "RC2C05-02 ($2002 AND $3F =$3D)",
+ VsSystemType.RC2C0503 => "RC2C05-03 ($2002 AND $1F =$1C)",
+ VsSystemType.RC2C0504 => "RC2C05-04 ($2002 AND $1F =$1B)",
+ VsSystemType.ReservedC => "Reserved (0x0C)",
+ VsSystemType.ReservedD => "Reserved (0x0D)",
+ VsSystemType.ReservedE => "Reserved (0x0E)",
+ VsSystemType.ReservedF => "Reserved (0x0F)",
+ _ => $"Unknown {(byte)type}",
+ };
+ }
+ }
+}
diff --git a/SabreTools.Serialization/Models/NES/Enums.cs b/SabreTools.Serialization/Models/NES/Enums.cs
index b66b9c16..3113a26c 100644
--- a/SabreTools.Serialization/Models/NES/Enums.cs
+++ b/SabreTools.Serialization/Models/NES/Enums.cs
@@ -164,42 +164,35 @@ namespace SabreTools.Data.Models.NES
///
/// CPU/PPU Timing
///
- [Flags]
public enum CPUPPUTiming : byte
{
- #region Bits 0-1
-
///
/// NTSC NES
///
- RP2C02 = 0b00000000,
+ RP2C02 = 0x00,
///
/// Licensed PAL NES
///
- RP2C07 = 0b00000001,
+ RP2C07 = 0x01,
///
/// Multiple-region
///
- MultipleRegion = 0b00000010,
+ MultipleRegion = 0x02,
///
/// Dendy
///
- UA6538 = 0b00000011,
-
- #endregion
+ UA6538 = 0x03,
}
///
- /// Vs. System Type and Extended Console Type
+ /// Vs. System Type
///
- [Flags]
- public enum ExtendedSystemType : byte
+ /// Actually only 4 bits
+ public enum VsSystemType : byte
{
- #region Bits 0-3 (Vs. System Type)
-
///
/// Any RP2C03/RC2C03 variant
///
@@ -208,7 +201,7 @@ namespace SabreTools.Data.Models.NES
///
/// Reserved
///
- VsReserved1 = 0x01,
+ Reserved1 = 0x01,
///
/// RP2C04-0001
@@ -238,7 +231,7 @@ namespace SabreTools.Data.Models.NES
///
/// Reserved
///
- VsReserved7 = 0x07,
+ Reserved7 = 0x07,
///
/// RC2C05-01 (signature unknown)
@@ -263,27 +256,30 @@ namespace SabreTools.Data.Models.NES
///
/// Reserved
///
- VsReservedC = 0x0C,
+ ReservedC = 0x0C,
///
/// Reserved
///
- VsReservedD = 0x0D,
+ ReservedD = 0x0D,
///
/// Reserved
///
- VsReservedE = 0x0E,
+ ReservedE = 0x0E,
///
/// Reserved
///
- VsReservedF = 0x0F,
-
- #endregion
-
- #region Bits 4-7 (Vs. System Type)
+ ReservedF = 0x0F,
+ }
+ ///
+ /// Vs. Hardware Type
+ ///
+ /// Actually only 4 bits
+ public enum VsHardwareType : byte
+ {
///
/// Vs. Unisystem (normal)
///
@@ -292,37 +288,39 @@ namespace SabreTools.Data.Models.NES
///
/// Vs. Unisystem (RBI Baseball protection)
///
- VsUnisystemRBIBaseballProtection = 0x10,
+ VsUnisystemRBIBaseballProtection = 0x01,
///
/// Vs. Unisystem (TKO Boxing protection)
///
- VsUnisystemTKOBoxingProtection = 0x20,
+ VsUnisystemTKOBoxingProtection = 0x02,
///
/// Vs. Unisystem (Super Xevious protection)
///
- VsUnisystemSuperXeviousProtection = 0x30,
+ VsUnisystemSuperXeviousProtection = 0x03,
///
/// Vs. Unisystem (Vs. Ice Climber Japan protection)
///
- VsUnisystemVsIceClimberJapanProtection = 0x40,
+ VsUnisystemVsIceClimberJapanProtection = 0x04,
///
/// Vs. Dual System (normal)
///
- VsDualSystem = 0x50,
+ VsDualSystem = 0x05,
///
/// Vs. Dual System (Raid on Bungeling Bay protection)
///
- VsDualSystemRaidOnBungelingBayProtection = 0x60,
-
- #endregion
-
- #region Bits 0-3 (Extended Console Type)
+ VsDualSystemRaidOnBungelingBayProtection = 0x06,
+ }
+ ///
+ /// Extended Console Type
+ ///
+ public enum ExtendedConsoleType : byte
+ {
///
/// Regular NES/Famicom/Dendy
///
@@ -391,19 +389,17 @@ namespace SabreTools.Data.Models.NES
///
/// Reserved
///
- ExtendedConsoleReservedD = 0x0D,
+ ReservedD = 0x0D,
///
/// Reserved
///
- ExtendedConsoleReservedE = 0x0E,
+ ReservedE = 0x0E,
///
/// Reserved
///
- ExtendedConsoleReservedF = 0x0F,
-
- #endregion
+ ReservedF = 0x0F,
}
///
@@ -500,7 +496,7 @@ namespace SabreTools.Data.Models.NES
///
/// Two Vaus Controllers plus Famicom Data Recorder
///
- TwoVausControllers = 0x11,
+ TwoVausControllersPlusFamicomDataRecorder = 0x11,
///
/// Konami Hyper Shot Controller
@@ -590,7 +586,7 @@ namespace SabreTools.Data.Models.NES
///
/// Family BASIC Keyboard plus Famicom Data Recorder
///
- FamilyBASICKeyboard = 0x23,
+ FamilyBASICKeyboardPlusFamicomDataRecorder = 0x23,
///
/// 东达 (Dōngdá) PEC Keyboard
@@ -740,7 +736,7 @@ namespace SabreTools.Data.Models.NES
///
/// PS/2 Keyboard in UM6578 PS/2 port, PS/2 Mouse via $4017
///
- PS2KeyboardInUM6578PS2Port = 0x41,
+ PS2KeyboardInUM6578PS2PortPS2Mouse4017 = 0x41,
///
/// PS/2 Mouse in UM6578 PS/2 port
@@ -750,13 +746,13 @@ namespace SabreTools.Data.Models.NES
///
/// 裕兴 (Yùxìng) Mouse via $4016
///
- YuxingMouse = 0x43,
+ YuxingMouse4016 = 0x43,
///
/// 小霸王 (Xiǎobàwáng, a.k.a. Subor) Keyboard plus 裕兴 (Yùxìng)
/// Mouse mouse in $4016
///
- SuborKeyboardPlusYuxingMouse = 0x44,
+ SuborKeyboardPlusYuxingMouse4016 = 0x44,
///
/// Gigggle TV Pump
@@ -766,7 +762,7 @@ namespace SabreTools.Data.Models.NES
///
/// 步步高 (Bùbùgāo, a.k.a. BBK) Keyboard plus R90-rotated PS/2 mouse in $4017
///
- BBKKeyboardPlusR90RotatedPS2Mouse = 0x46,
+ BBKKeyboardPlusR90RotatedPS2Mouse4017 = 0x46,
///
/// Magical Cooking
@@ -801,7 +797,7 @@ namespace SabreTools.Data.Models.NES
///
/// 小霸王 (Xiǎobàwáng, a.k.a. Subor) Keyboard plus X-inverted PS/2 mouse in $4017
///
- SuborKeyboardPlusXInvertedPS2Mouse = 0x4D,
+ SuborKeyboardPlusXInvertedPS2Mouse4017 = 0x4D,
///
/// IBM PC/XT Keyboard
diff --git a/SabreTools.Serialization/Models/NES/Header2.cs b/SabreTools.Serialization/Models/NES/Header2.cs
index 2f31a166..782e2401 100644
--- a/SabreTools.Serialization/Models/NES/Header2.cs
+++ b/SabreTools.Serialization/Models/NES/Header2.cs
@@ -64,7 +64,7 @@ namespace SabreTools.Data.Models.NES
/// When Byte 7 AND 3 =1: Vs. System Type
/// When Byte 7 AND 3 =3: Extended Console Type
///
- public ExtendedSystemType ExtendedSystemType { get; set; }
+ public byte ExtendedSystemType { get; set; }
///
/// Number of miscellaneous ROMs present
diff --git a/SabreTools.Serialization/Readers/NESCart.cs b/SabreTools.Serialization/Readers/NESCart.cs
index addf780d..a0e34888 100644
--- a/SabreTools.Serialization/Readers/NESCart.cs
+++ b/SabreTools.Serialization/Readers/NESCart.cs
@@ -114,7 +114,7 @@ namespace SabreTools.Serialization.Readers
obj.PRGRAMEEPROMSize = data.ReadByteValue();
obj.CHRRAMSize = data.ReadByteValue();
obj.CPUPPUTiming = (CPUPPUTiming)data.ReadByteValue();
- obj.ExtendedSystemType = (ExtendedSystemType)data.ReadByteValue();
+ obj.ExtendedSystemType = data.ReadByteValue();
obj.MiscellaneousROMs = data.ReadByteValue();
obj.DefaultExpansionDevice = (DefaultExpansionDevice)data.ReadByteValue();
diff --git a/SabreTools.Serialization/Wrappers/NESCart.Printing.cs b/SabreTools.Serialization/Wrappers/NESCart.Printing.cs
new file mode 100644
index 00000000..2ed63241
--- /dev/null
+++ b/SabreTools.Serialization/Wrappers/NESCart.Printing.cs
@@ -0,0 +1,284 @@
+using System.Text;
+using SabreTools.Data.Extensions;
+using SabreTools.Data.Models.NES;
+
+namespace SabreTools.Serialization.Wrappers
+{
+ public partial class NESCart : IPrintable
+ {
+#if NETCOREAPP
+ ///
+ public string ExportJSON() => System.Text.Json.JsonSerializer.Serialize(Model, _jsonSerializerOptions);
+#endif
+
+ ///
+ public void PrintInformation(StringBuilder builder)
+ {
+ builder.AppendLine("Nintendo Entertainment System Cart Information:");
+ builder.AppendLine("-------------------------");
+ builder.AppendLine();
+
+ Print(builder, Model.Header);
+
+ builder.AppendLine(Model.Trainer, "Trainer Data");
+ builder.AppendLine(Model.PRGROMData, "PRG-ROM Data");
+ builder.AppendLine(Model.CHRROMData, "CHR-ROM Data");
+ builder.AppendLine(Model.PlayChoiceINSTROM, "PlayChoice INST-ROM Data");
+ builder.AppendLine(Model.PlayChoicePROM, "PlayChoice PROM Data");
+ builder.AppendLine(Model.Title, "Title:");
+ }
+
+ 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.IdentificationString, " Identification string");
+ builder.AppendLine(header.PRGROMSize, " PRG-ROM size in 16KiB units");
+ builder.AppendLine(header.CHRROMSize, " CHR-ROM size in 8KiB units");
+
+ #region Flag 6
+
+ builder.AppendLine(" Flag 6:");
+
+ // Bit 0
+#if NET20 || NET35
+ if ((header.Flag6 & Flag6.NametableArrangementHorizontal) != 0)
+#else
+ if (header.Flag6.HasFlag(Flag6.NametableArrangementHorizontal))
+#endif
+ builder.AppendLine(" Nametable Arrangement: Horizontal");
+ else
+ builder.AppendLine(" Nametable Arrangement: Vertical");
+
+ // Bit 1
+#if NET20 || NET35
+ if ((header.Flag6 & Flag6.BatteryBackedPRGRAMPresent) != 0)
+#else
+ if (header.Flag6.HasFlag(Flag6.BatteryBackedPRGRAMPresent))
+#endif
+ builder.AppendLine(" Battery-Backed PRG RAM: Present");
+ else
+ builder.AppendLine(" Battery-Backed PRG RAM: Not present");
+
+ // Bit 2
+#if NET20 || NET35
+ if ((header.Flag6 & Flag6.TrainerPresent) != 0)
+#else
+ if (header.Flag6.HasFlag(Flag6.TrainerPresent))
+#endif
+ builder.AppendLine(" Trainer: Present");
+ else
+ builder.AppendLine(" Trainer: Not present");
+
+ // Bit 3
+#if NET20 || NET35
+ if ((header.Flag6 & Flag6.AlternativeNametableLayout) != 0)
+#else
+ if (header.Flag6.HasFlag(Flag6.AlternativeNametableLayout))
+#endif
+ builder.AppendLine(" Alternative Nametable Layout: True");
+ else
+ builder.AppendLine(" Alternative Nametable Layout: False");
+
+ #endregion
+
+ #region Flag 7
+
+ builder.AppendLine(" Flag 7:");
+
+ // Bits 0-1
+#if NET20 || NET35
+ if ((header.Flag7 & Flag7.ExtendedConsoleType) != 0)
+#else
+ if (header.Flag7.HasFlag(Flag7.ExtendedConsoleType))
+#endif
+ builder.AppendLine(" System Type: Extended Console Type");
+#if NET20 || NET35
+ else if ((header.Flag7 & Flag7.PlayChoice10) != 0)
+#else
+ else if (header.Flag7.HasFlag(Flag7.PlayChoice10))
+#endif
+ builder.AppendLine(" System Type: PlayChoice-10");
+#if NET20 || NET35
+ else if ((header.Flag7 & Flag7.VSUnisystem) != 0)
+#else
+ else if (header.Flag7.HasFlag(Flag7.VSUnisystem))
+#endif
+ builder.AppendLine(" System Type: Vs. Unisystem");
+ else
+ builder.AppendLine(" System Type: Nintendo Entertainment System/Family Computer");
+
+ // Bits 2-3
+#if NET20 || NET35
+ if ((header.Flag7 & Flag7.NES20) != 0)
+#else
+ if (header.Flag7.HasFlag(Flag7.NES20))
+#endif
+ builder.AppendLine(" NES 2.0: True");
+ else
+ builder.AppendLine(" NES 2.0: False");
+
+ #endregion
+
+ byte mapperNumber = (byte)((((byte)header.Flag7 >> 4) << 4) | (byte)((byte)header.Flag6 >> 4));
+ builder.AppendLine(mapperNumber, " Mapper number");
+
+ if (header is Header1 header1)
+ {
+ // Byte 8
+ builder.AppendLine(header1.PRGRAMSize, prefixString: " PRG-RAM size in 8KiB units");
+
+ // Byte 9
+ builder.AppendLine($" TV system: {header1.TVSystem} (0x{header1.TVSystem:X})");
+
+ // Byte 10
+ #region Flag 10
+
+ builder.AppendLine(" Flag 10:");
+
+ // Bits 0-1
+#if NET20 || NET35
+ if ((header1.Flag10 & Flag10.DualCompatible2) != 0)
+#else
+ if (header1.Flag10.HasFlag(Flag10.DualCompatible2))
+#endif
+ builder.AppendLine(" TV System: Dual-compatible");
+#if NET20 || NET35
+ else if ((header1.Flag10 & Flag10.PAL) != 0)
+#else
+ else if (header1.Flag10.HasFlag(Flag10.PAL))
+#endif
+ builder.AppendLine(" TV System: PAL");
+#if NET20 || NET35
+ else if ((header1.Flag10 & Flag10.DualCompatible1) != 0)
+#else
+ else if (header1.Flag10.HasFlag(Flag10.DualCompatible1))
+#endif
+ builder.AppendLine(" TV System: Dual-compatible");
+ else
+ builder.AppendLine(" TV System: NTSC");
+
+ // Bit 4
+#if NET20 || NET35
+ if ((header1.Flag10 & Flag10.PRGRAMNotPresent) != 0)
+#else
+ if (header1.Flag10.HasFlag(Flag10.PRGRAMNotPresent))
+#endif
+ builder.AppendLine(" PRG-RAM: Not present");
+ else
+ builder.AppendLine(" PRG-RAM: Present");
+
+ // Bit 5
+#if NET20 || NET35
+ if ((header1.Flag10 & Flag10.BoardHasBusConflicts) != 0)
+#else
+ if (header1.Flag10.HasFlag(Flag10.BoardHasBusConflicts))
+#endif
+ builder.AppendLine(" Has Bus Conflicts: True");
+ else
+ builder.AppendLine(" Has Bus Conflicts: False");
+
+ #endregion
+
+ // Bytes 11-15
+ builder.AppendLine(header1.Padding, " Padding");
+ }
+ else if (header is Header2 header2)
+ {
+ // Byte 8
+ byte mapperMsb = (byte)(header2.MapperMSBSubmapper & 0x0F);
+ ushort extendedMapperNumber = (ushort)((mapperMsb << 8)
+ | (byte)((((byte)header.Flag7 >> 4) << 4)
+ | (byte)((byte)header.Flag6 >> 4)));
+ byte submapperNumber = (byte)(header2.MapperMSBSubmapper >> 4);
+
+ builder.AppendLine(mapperMsb, " Mapper MSB");
+ builder.AppendLine(extendedMapperNumber, " Extended mapper number");
+ builder.AppendLine(submapperNumber, " Submapper number");
+
+ // Byte 9
+ byte prgRomMsb = (byte)(header2.PRGCHRMSB & 0x0F);
+ ushort extendedPrgRomSize = (ushort)((prgRomMsb << 8) | header.PRGROMSize);
+ byte chrRomMsb = (byte)(header2.PRGCHRMSB >> 4);
+ ushort extendedChrRomSize = (ushort)((chrRomMsb << 8) | header.CHRROMSize);
+
+ builder.AppendLine(prgRomMsb, " PRG-ROM size MSB");
+ builder.AppendLine(extendedPrgRomSize, " Extended PRG-ROM size");
+ builder.AppendLine(chrRomMsb, " CHR-ROM size MSB");
+ builder.AppendLine(extendedChrRomSize, " Extended CHR-ROM size");
+
+ // Byte 10
+ byte prgRamShiftCount = (byte)(header2.PRGRAMEEPROMSize & 0x0F);
+ int prgRamSize = prgRamShiftCount > 0 ? 64 << prgRamShiftCount : 0;
+ byte eepromShiftCount = (byte)(header2.PRGRAMEEPROMSize >> 4);
+ int eepromSize = eepromShiftCount > 0 ? 64 << eepromShiftCount : 0;
+
+ builder.AppendLine(prgRamShiftCount, " PRG-RAM shift count");
+ builder.AppendLine(prgRamSize, " PRG-RAM size");
+ builder.AppendLine(eepromShiftCount, " PRG-NVRAM/EEPROM shift count");
+ builder.AppendLine(eepromSize, " PRG-NVRAM/EEPROM size");
+
+ // Byte 11
+ byte chrRamShiftCount = (byte)(header2.CHRRAMSize & 0x0F);
+ int chrRamSize = chrRamShiftCount > 0 ? 64 << chrRamShiftCount : 0;
+ byte chrNvramShiftCount = (byte)(header2.CHRRAMSize >> 4);
+ int chrNvramSize = eepromShiftCount > 0 ? 64 << chrNvramShiftCount : 0;
+
+ builder.AppendLine(chrRamShiftCount, " CHR-RAM shift count");
+ builder.AppendLine(chrRamSize, " CHR-RAM size");
+ builder.AppendLine(chrNvramShiftCount, " CHR-NVRAM shift count");
+ builder.AppendLine(chrNvramSize, " CHR-NVRAM size");
+
+ // Byte 12
+ string cpuTiming = header2.CPUPPUTiming.FromCPUPPUTiming();
+ builder.AppendLine(cpuTiming, " CPU timing");
+
+ // Byte 13
+#if NET20 || NET35
+ if ((header.Flag7 & Flag7.ExtendedConsoleType) != 0)
+#else
+ if (header.Flag7.HasFlag(Flag7.ExtendedConsoleType))
+#endif
+ {
+ ExtendedConsoleType extendedConsoleType = (ExtendedConsoleType)(header2.ExtendedSystemType & 0x0F);
+ string extendedConsoleTypeString = extendedConsoleType.FromExtendedConsoleType();
+ builder.AppendLine(extendedConsoleTypeString, " Extended console type");
+ }
+#if NET20 || NET35
+ else if ((header.Flag7 & Flag7.VSUnisystem) != 0)
+#else
+ else if (header.Flag7.HasFlag(Flag7.VSUnisystem))
+#endif
+ {
+ VsSystemType vsSystemType = (VsSystemType)(header2.ExtendedSystemType & 0x0F);
+ string vsSystemTypeString = vsSystemType.FromVsSystemType();
+ builder.AppendLine(vsSystemTypeString, " Vs. system type");
+
+ VsHardwareType vsHardwareType = (VsHardwareType)(header2.ExtendedSystemType >> 4);
+ string vsHardwareTypeString = vsHardwareType.FromVsHardwareType();
+ builder.AppendLine(vsHardwareTypeString, " Vs. hardware type");
+ }
+
+ // Byte 14
+ builder.AppendLine(header2.MiscellaneousROMs, " Number of miscellaneous ROMs");
+
+ // Byte 15
+ string defaultExpansionDevice = header2.DefaultExpansionDevice.FromDefaultExpansionDevice();
+ builder.AppendLine(defaultExpansionDevice, " Default expansion device");
+ }
+ else
+ {
+ builder.AppendLine(" Unrecognized header type!");
+ }
+
+ builder.AppendLine();
+ }
+ }
+}
diff --git a/SabreTools.Serialization/Wrappers/NESCart.cs b/SabreTools.Serialization/Wrappers/NESCart.cs
index e57d8d75..3e1e3001 100644
--- a/SabreTools.Serialization/Wrappers/NESCart.cs
+++ b/SabreTools.Serialization/Wrappers/NESCart.cs
@@ -3,9 +3,8 @@ using SabreTools.Data.Models.NES;
namespace SabreTools.Serialization.Wrappers
{
- // TODO: Add printer class
// TODO: Add extension properties
- public class NESCart : WrapperBase
+ public partial class NESCart : WrapperBase
{
#region Descriptive Properties