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