diff --git a/SabreTools.Serialization/Extensions/NESCart.cs b/SabreTools.Serialization/Extensions/NESCart.cs index 4fda5372..3be12544 100644 --- a/SabreTools.Serialization/Extensions/NESCart.cs +++ b/SabreTools.Serialization/Extensions/NESCart.cs @@ -4,6 +4,21 @@ namespace SabreTools.Data.Extensions { public static class NESCart { + /// + /// Convert a value to string + /// + public static string FromConsoleType(this ConsoleType type) + { + return type switch + { + ConsoleType.StandardSystem => "Nintendo Entertainment System/Family Computer", + ConsoleType.VSUnisystem => "VS Unisystem", + ConsoleType.PlayChoice10 => "PlayChoice-10 (8 KB of Hint Screen data stored after CHR data)", + ConsoleType.ExtendedConsoleType => "Extended Console Type", + _ => $"Unknown {(byte)type}", + }; + } + /// /// Convert a value to string /// diff --git a/SabreTools.Serialization/Models/NES/Enums.cs b/SabreTools.Serialization/Models/NES/Enums.cs index c1c7dc86..aa97467a 100644 --- a/SabreTools.Serialization/Models/NES/Enums.cs +++ b/SabreTools.Serialization/Models/NES/Enums.cs @@ -2,47 +2,6 @@ using System; namespace SabreTools.Data.Models.NES { - /// - /// Mapper, VS/Playchoice, NES 2.0 - /// - /// Bits 4-7 are the upper nybble of mapper number - [Flags] - public enum Flag7 : byte - { - #region Bits 0-1 - - /// - /// Nintendo Entertainment System/Family Computer - /// - StandardSystem = 0b0000, - - /// - /// VS Unisystem - /// - VSUnisystem = 0b0001, - - /// - /// PlayChoice-10 (8 KB of Hint Screen data stored after CHR data) - /// - PlayChoice10 = 0b0010, - - /// - /// Extended Console Type - /// - ExtendedConsoleType = 0b0011, - - #endregion - - #region Bits 2-3 - - /// - /// If equal to 2, flags 8-15 are in NES 2.0 format - /// - NES20 = 0b1000, - - #endregion - } - /// /// TV system, PRG-RAM presence (unofficial, rarely used extension) /// diff --git a/SabreTools.Serialization/Models/NES/Header.cs b/SabreTools.Serialization/Models/NES/Header.cs index 41f7327c..36463fd5 100644 --- a/SabreTools.Serialization/Models/NES/Header.cs +++ b/SabreTools.Serialization/Models/NES/Header.cs @@ -23,45 +23,49 @@ namespace SabreTools.Data.Models.NES /// Value 0 means the board uses CHR RAM public byte CHRROMSize { get; set; } - #region Flag 6 + #region Byte 6 /// /// Nametable arrangement /// - /// Flag 6, Bit 0 + /// Byte 6, Bit 0 public NametableArrangement NametableArrangement { get; set; } /// /// Cartridge contains battery-backed PRG RAM ($6000-7FFF) /// or other persistent memory /// - /// Flag 6, Bit 1 + /// Byte 6, Bit 1 public bool BatteryBackedPRGRAM { get; set; } /// /// 512-byte trainer at $7000-$71FF /// - /// Flag 6, Bit 2 + /// Byte 6, Bit 2 public bool TrainerPresent { get; set; } /// /// Alternative nametable layout /// - /// Flag 6, Bit 3 + /// Byte 6, Bit 3 public bool AlternativeNametableLayout { get; set; } /// /// Lower nibble of Mapper number /// - /// Flag 6, Bits 4-7 + /// Byte 6, Bits 4-7 public byte MapperLowerNibble { get; set; } #endregion + #region Byte 7 + /// - /// Mapper, VS/Playchoice, NES 2.0 + /// Nametable arrangement /// /// + /// Byte 7, Bits 0-1 + /// /// The PlayChoice-10 bit is not part of the official specification, /// and most emulators simply ignore the extra 8 KB of data. PlayChoice /// games are designed to look good with the 2C03 RGB PPU, which handles @@ -69,10 +73,26 @@ namespace SabreTools.Data.Models.NES /// /// Vs. games have a coin slot and different palettes. The detection /// of which palette a particular game uses is left unspecified. + /// + public ConsoleType ConsoleType { get; set; } + + /// + /// Indicates NES 2.0 format + /// + /// + /// Byte 7, Bits 2-3 == 0x02 /// /// NES 2.0 is a more recent extension to the format that allows more /// flexibility in ROM and RAM size, among other things. /// - public Flag7 Flag7 { get; set; } + public bool NES20 { get; set; } + + /// + /// Upper nibble of Mapper number + /// + /// Byte 7, Bits 4-7 + public byte MapperUpperNibble { get; set; } + + #endregion } } diff --git a/SabreTools.Serialization/Readers/NESCart.cs b/SabreTools.Serialization/Readers/NESCart.cs index 0edcf05e..4325857a 100644 --- a/SabreTools.Serialization/Readers/NESCart.cs +++ b/SabreTools.Serialization/Readers/NESCart.cs @@ -60,13 +60,7 @@ namespace SabreTools.Serialization.Readers cart.CHRROMData = data.ReadBytes(chrRomSize); // Read the PlayChoice INST-ROM and PROM data, if necessary -#if NET20 || NET35 - if ((cart.Header.Flag7 & Flag7.PlayChoice10) != 0 - && (cart.Header.Flag7 & Flag7.VSUnisystem) == 0) -#else - if (cart.Header.Flag7.HasFlag(Flag7.PlayChoice10) - && !cart.Header.Flag7.HasFlag(Flag7.VSUnisystem)) -#endif + if (cart.Header.ConsoleType == ConsoleType.PlayChoice10) { cart.PlayChoiceINSTROM = data.ReadBytes(8192); cart.PlayChoicePROM = data.ReadBytes(32); @@ -106,14 +100,13 @@ namespace SabreTools.Serialization.Readers bool alternativeNametableLayout = ((flag6 >> 3) & 0x01) != 0; byte mapperLowerNibble = (byte)(flag6 >> 4); - Flag7 flag7 = (Flag7)data.ReadByteValue(); + byte flag7 = data.ReadByteValue(); + ConsoleType consoleType = (ConsoleType)(flag7 & 0x03); + bool nes20 = ((flag7 >> 2) & 0x02) == 0x02; + byte mapperUpperNibble = (byte)(flag7 >> 4); // NES 2.0 -#if NET20 || NET35 - if ((flag7 & Flag7.NES20) != 0) -#else - if (flag7.HasFlag(Flag7.NES20)) -#endif + if (nes20) { var obj = new Header2(); @@ -128,7 +121,11 @@ namespace SabreTools.Serialization.Readers obj.AlternativeNametableLayout = alternativeNametableLayout; obj.MapperLowerNibble = mapperLowerNibble; - obj.Flag7 = flag7; + // Flag 7 + obj.ConsoleType = consoleType; + obj.NES20 = nes20; + obj.MapperUpperNibble = mapperUpperNibble; + obj.MapperMSBSubmapper = data.ReadByteValue(); obj.PRGCHRMSB = data.ReadByteValue(); obj.PRGRAMEEPROMSize = data.ReadByteValue(); @@ -157,7 +154,11 @@ namespace SabreTools.Serialization.Readers obj.AlternativeNametableLayout = alternativeNametableLayout; obj.MapperLowerNibble = mapperLowerNibble; - obj.Flag7 = flag7; + // Flag 7 + obj.ConsoleType = consoleType; + obj.NES20 = nes20; + obj.MapperUpperNibble = mapperUpperNibble; + obj.PRGRAMSize = data.ReadByteValue(); obj.TVSystem = (TVSystem)data.ReadByteValue(); obj.Flag10 = (Flag10)data.ReadByteValue(); diff --git a/SabreTools.Serialization/Wrappers/NESCart.Printing.cs b/SabreTools.Serialization/Wrappers/NESCart.Printing.cs index 9f428df7..d3418cc0 100644 --- a/SabreTools.Serialization/Wrappers/NESCart.Printing.cs +++ b/SabreTools.Serialization/Wrappers/NESCart.Printing.cs @@ -74,40 +74,15 @@ namespace SabreTools.Serialization.Wrappers 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"); + string consoleType = header.ConsoleType.FromConsoleType(); + builder.AppendLine(consoleType, " System Type"); // 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"); + builder.AppendLine(header.NES20, " NES 2.0"); #endregion - byte mapperNumber = (byte)((((byte)header.Flag7 >> 4) << 4) | header.MapperLowerNibble); + byte mapperNumber = (byte)((header.MapperUpperNibble << 4) | header.MapperLowerNibble); builder.AppendLine(mapperNumber, " Mapper number"); if (header is Header1 header1) @@ -175,7 +150,7 @@ namespace SabreTools.Serialization.Wrappers // Byte 8 byte mapperMsb = (byte)(header2.MapperMSBSubmapper & 0x0F); ushort extendedMapperNumber = (ushort)((mapperMsb << 8) - | (byte)((((byte)header.Flag7 >> 4) << 4) + | (byte)((header.MapperUpperNibble << 4) | header.MapperLowerNibble)); byte submapperNumber = (byte)(header2.MapperMSBSubmapper >> 4); @@ -221,21 +196,13 @@ namespace SabreTools.Serialization.Wrappers builder.AppendLine(cpuTiming, " CPU timing"); // Byte 13 -#if NET20 || NET35 - if ((header.Flag7 & Flag7.ExtendedConsoleType) != 0) -#else - if (header.Flag7.HasFlag(Flag7.ExtendedConsoleType)) -#endif + if (header.ConsoleType == ConsoleType.ExtendedConsoleType) { 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 + else if (header.ConsoleType == ConsoleType.VSUnisystem) { VsSystemType vsSystemType = (VsSystemType)(header2.ExtendedSystemType & 0x0F); string vsSystemTypeString = vsSystemType.FromVsSystemType(); diff --git a/SabreTools.Serialization/Wrappers/NESCart.cs b/SabreTools.Serialization/Wrappers/NESCart.cs index d9660af2..f52e1164 100644 --- a/SabreTools.Serialization/Wrappers/NESCart.cs +++ b/SabreTools.Serialization/Wrappers/NESCart.cs @@ -48,107 +48,8 @@ namespace SabreTools.Serialization.Wrappers } } - /// - /// Indicates if the game is meant for an extended console type - /// - /// Possibly only valid for NES 2.0 - public bool IsExtendedConsole - { - get - { - // Missing header - if (Header is null) - return false; - -#if NET20 || NET35 - return (Header.Flag7 & Flag7.ExtendedConsoleType) != 0; -#else - return Header.Flag7.HasFlag(Flag7.ExtendedConsoleType); -#endif - } - } - - /// - /// Indicates if the cart is using an NES 2.0 header - /// - public bool IsNES20 - { - get - { - // Missing header - if (Header is null) - return false; - -#if NET20 || NET35 - return (Header.Flag7 & Flag7.NES20) != 0; -#else - return Header.Flag7.HasFlag(Flag7.NES20); -#endif - } - } - - /// - /// Indicates if the game is meant for PlayChoice-10 - /// - public bool IsPlayChoice10 - { - get - { - // Missing header - if (Header is null) - return false; - -#if NET20 || NET35 - return (Header.Flag7 & Flag7.PlayChoice10) != 0 - && (Header.Flag7 & Flag7.VSUnisystem) == 0; -#else - return Header.Flag7.HasFlag(Flag7.PlayChoice10) - && !Header.Flag7.HasFlag(Flag7.VSUnisystem); -#endif - } - } - - /// - /// Indicates if the game is meant for a standard console - /// - public bool IsStandardConsole - { - get - { - // Missing header - if (Header is null) - return false; - -#if NET20 || NET35 - return (Header.Flag7 & Flag7.PlayChoice10) == 0 - && (Header.Flag7 & Flag7.VSUnisystem) == 0; -#else - return !Header.Flag7.HasFlag(Flag7.PlayChoice10) - && !Header.Flag7.HasFlag(Flag7.VSUnisystem); -#endif - } - } - - /// - /// Indicates if the game is meant for Vs. Unisystem - /// - public bool IsVsUnisystem - { - get - { - // Missing header - if (Header is null) - return false; - -#if NET20 || NET35 - return (Header.Flag7 & Flag7.PlayChoice10) == 0 - && (Header.Flag7 & Flag7.VSUnisystem) != 0; -#else - return !Header.Flag7.HasFlag(Flag7.PlayChoice10) - && Header.Flag7.HasFlag(Flag7.VSUnisystem); -#endif - } - } + /// + public ConsoleType ConsoleType => Header?.ConsoleType ?? ConsoleType.StandardSystem; /// /// Mapper number @@ -162,7 +63,7 @@ namespace SabreTools.Serialization.Wrappers if (Header is null) return 0; - int mapperNumber = (((byte)Header.Flag7 >> 4) << 4) | Header.MapperLowerNibble; + int mapperNumber = (Header.MapperUpperNibble << 4) | Header.MapperLowerNibble; if (Header is Header2 header2) mapperNumber = ((header2.MapperMSBSubmapper & 0x0F) << 8) | mapperNumber; @@ -174,6 +75,9 @@ namespace SabreTools.Serialization.Wrappers public NametableArrangement NametableArrangement => Header?.NametableArrangement ?? NametableArrangement.Vertical; + /// + public bool NES20 => Header?.NES20 ?? false; + /// /// PRG-RAM size in bytes /// @@ -366,15 +270,11 @@ namespace SabreTools.Serialization.Wrappers if (Header is null || Header is not Header2 header2) return ExtendedConsoleType.RegularSystem; -#if NET20 || NET35 - if ((Header.Flag7 & Flag7.ExtendedConsoleType) != 0) -#else - if (Header.Flag7.HasFlag(Flag7.ExtendedConsoleType)) -#endif - return (ExtendedConsoleType)(header2.ExtendedSystemType & 0x0F); + // Invalid console type + if (ConsoleType != ConsoleType.ExtendedConsoleType) + return ExtendedConsoleType.RegularSystem; - // If flag is unset - return ExtendedConsoleType.RegularSystem; + return (ExtendedConsoleType)(header2.ExtendedSystemType & 0x0F); } } @@ -440,15 +340,11 @@ namespace SabreTools.Serialization.Wrappers if (Header is null || Header is not Header2 header2) return VsHardwareType.VsUnisystem; -#if NET20 || NET35 - else if ((Header.Flag7 & Flag7.VSUnisystem) != 0) -#else - else if (Header.Flag7.HasFlag(Flag7.VSUnisystem)) -#endif - return (VsHardwareType)(header2.ExtendedSystemType >> 4); + // Invalid console type + if (ConsoleType != ConsoleType.VSUnisystem) + return VsHardwareType.VsUnisystem; - // If flag is unset - return VsHardwareType.VsUnisystem; + return (VsHardwareType)(header2.ExtendedSystemType >> 4); } } @@ -465,15 +361,11 @@ namespace SabreTools.Serialization.Wrappers if (Header is null || Header is not Header2 header2) return VsSystemType.AnyRP2C03RC2C03Variant; -#if NET20 || NET35 - else if ((Header.Flag7 & Flag7.VSUnisystem) != 0) -#else - else if (Header.Flag7.HasFlag(Flag7.VSUnisystem)) -#endif - return (VsSystemType)(header2.ExtendedSystemType & 0x0F); + // Invalid console type + if (ConsoleType != ConsoleType.VSUnisystem) + return VsSystemType.AnyRP2C03RC2C03Variant; - // If flag is unset - return VsSystemType.AnyRP2C03RC2C03Variant; + return (VsSystemType)(header2.ExtendedSystemType & 0x0F); } }