Add NES cart printing

This commit is contained in:
Matt Nadareski
2026-03-06 12:47:59 -05:00
parent b77e866be4
commit 9f2765edd0
6 changed files with 514 additions and 50 deletions

View File

@@ -0,0 +1,185 @@
using SabreTools.Data.Models.NES;
namespace SabreTools.Data.Extensions
{
public static class NESCart
{
/// <summary>
/// Convert a <see cref="CPUPPUTiming"/> value to string
/// </summary>
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}",
};
}
/// <summary>
/// Convert a <see cref="DefaultExpansionDevice"/> value to string
/// </summary>
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}",
};
}
/// <summary>
/// Convert a <see cref="ExtendedConsoleType"/> value to string
/// </summary>
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}",
};
}
/// <summary>
/// Convert a <see cref="VsHardwareType"/> value to string
/// </summary>
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}",
};
}
/// <summary>
/// Convert a <see cref="VsSystemType"/> value to string
/// </summary>
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}",
};
}
}
}

View File

@@ -164,42 +164,35 @@ namespace SabreTools.Data.Models.NES
/// <summary>
/// CPU/PPU Timing
/// </summary>
[Flags]
public enum CPUPPUTiming : byte
{
#region Bits 0-1
/// <summary>
/// NTSC NES
/// </summary>
RP2C02 = 0b00000000,
RP2C02 = 0x00,
/// <summary>
/// Licensed PAL NES
/// </summary>
RP2C07 = 0b00000001,
RP2C07 = 0x01,
/// <summary>
/// Multiple-region
/// </summary>
MultipleRegion = 0b00000010,
MultipleRegion = 0x02,
/// <summary>
/// Dendy
/// </summary>
UA6538 = 0b00000011,
#endregion
UA6538 = 0x03,
}
/// <summary>
/// Vs. System Type and Extended Console Type
/// Vs. System Type
/// </summary>
[Flags]
public enum ExtendedSystemType : byte
/// <remarks>Actually only 4 bits</remarks>
public enum VsSystemType : byte
{
#region Bits 0-3 (Vs. System Type)
/// <summary>
/// Any RP2C03/RC2C03 variant
/// </summary>
@@ -208,7 +201,7 @@ namespace SabreTools.Data.Models.NES
/// <summary>
/// Reserved
/// </summary>
VsReserved1 = 0x01,
Reserved1 = 0x01,
/// <summary>
/// RP2C04-0001
@@ -238,7 +231,7 @@ namespace SabreTools.Data.Models.NES
/// <summary>
/// Reserved
/// </summary>
VsReserved7 = 0x07,
Reserved7 = 0x07,
/// <summary>
/// RC2C05-01 (signature unknown)
@@ -263,27 +256,30 @@ namespace SabreTools.Data.Models.NES
/// <summary>
/// Reserved
/// </summary>
VsReservedC = 0x0C,
ReservedC = 0x0C,
/// <summary>
/// Reserved
/// </summary>
VsReservedD = 0x0D,
ReservedD = 0x0D,
/// <summary>
/// Reserved
/// </summary>
VsReservedE = 0x0E,
ReservedE = 0x0E,
/// <summary>
/// Reserved
/// </summary>
VsReservedF = 0x0F,
#endregion
#region Bits 4-7 (Vs. System Type)
ReservedF = 0x0F,
}
/// <summary>
/// Vs. Hardware Type
/// </summary>
/// <remarks>Actually only 4 bits</remarks>
public enum VsHardwareType : byte
{
/// <summary>
/// Vs. Unisystem (normal)
/// </summary>
@@ -292,37 +288,39 @@ namespace SabreTools.Data.Models.NES
/// <summary>
/// Vs. Unisystem (RBI Baseball protection)
/// </summary>
VsUnisystemRBIBaseballProtection = 0x10,
VsUnisystemRBIBaseballProtection = 0x01,
/// <summary>
/// Vs. Unisystem (TKO Boxing protection)
/// </summary>
VsUnisystemTKOBoxingProtection = 0x20,
VsUnisystemTKOBoxingProtection = 0x02,
/// <summary>
/// Vs. Unisystem (Super Xevious protection)
/// </summary>
VsUnisystemSuperXeviousProtection = 0x30,
VsUnisystemSuperXeviousProtection = 0x03,
/// <summary>
/// Vs. Unisystem (Vs. Ice Climber Japan protection)
/// </summary>
VsUnisystemVsIceClimberJapanProtection = 0x40,
VsUnisystemVsIceClimberJapanProtection = 0x04,
/// <summary>
/// Vs. Dual System (normal)
/// </summary>
VsDualSystem = 0x50,
VsDualSystem = 0x05,
/// <summary>
/// Vs. Dual System (Raid on Bungeling Bay protection)
/// </summary>
VsDualSystemRaidOnBungelingBayProtection = 0x60,
#endregion
#region Bits 0-3 (Extended Console Type)
VsDualSystemRaidOnBungelingBayProtection = 0x06,
}
/// <summary>
/// Extended Console Type
/// </summary>
public enum ExtendedConsoleType : byte
{
/// <summary>
/// Regular NES/Famicom/Dendy
/// </summary>
@@ -391,19 +389,17 @@ namespace SabreTools.Data.Models.NES
/// <summary>
/// Reserved
/// </summary>
ExtendedConsoleReservedD = 0x0D,
ReservedD = 0x0D,
/// <summary>
/// Reserved
/// </summary>
ExtendedConsoleReservedE = 0x0E,
ReservedE = 0x0E,
/// <summary>
/// Reserved
/// </summary>
ExtendedConsoleReservedF = 0x0F,
#endregion
ReservedF = 0x0F,
}
/// <summary>
@@ -500,7 +496,7 @@ namespace SabreTools.Data.Models.NES
/// <summary>
/// Two Vaus Controllers plus Famicom Data Recorder
/// </summary>
TwoVausControllers = 0x11,
TwoVausControllersPlusFamicomDataRecorder = 0x11,
/// <summary>
/// Konami Hyper Shot Controller
@@ -590,7 +586,7 @@ namespace SabreTools.Data.Models.NES
/// <summary>
/// Family BASIC Keyboard plus Famicom Data Recorder
/// </summary>
FamilyBASICKeyboard = 0x23,
FamilyBASICKeyboardPlusFamicomDataRecorder = 0x23,
/// <summary>
/// 东达 (Dōngdá) PEC Keyboard
@@ -740,7 +736,7 @@ namespace SabreTools.Data.Models.NES
/// <summary>
/// PS/2 Keyboard in UM6578 PS/2 port, PS/2 Mouse via $4017
/// </summary>
PS2KeyboardInUM6578PS2Port = 0x41,
PS2KeyboardInUM6578PS2PortPS2Mouse4017 = 0x41,
/// <summary>
/// PS/2 Mouse in UM6578 PS/2 port
@@ -750,13 +746,13 @@ namespace SabreTools.Data.Models.NES
/// <summary>
/// 裕兴 (Yùxìng) Mouse via $4016
/// </summary>
YuxingMouse = 0x43,
YuxingMouse4016 = 0x43,
/// <summary>
/// 小霸王 (Xiǎobàwáng, a.k.a. Subor) Keyboard plus 裕兴 (Yùxìng)
/// Mouse mouse in $4016
/// </summary>
SuborKeyboardPlusYuxingMouse = 0x44,
SuborKeyboardPlusYuxingMouse4016 = 0x44,
/// <summary>
/// Gigggle TV Pump
@@ -766,7 +762,7 @@ namespace SabreTools.Data.Models.NES
/// <summary>
/// 步步高 (Bùbùgāo, a.k.a. BBK) Keyboard plus R90-rotated PS/2 mouse in $4017
/// </summary>
BBKKeyboardPlusR90RotatedPS2Mouse = 0x46,
BBKKeyboardPlusR90RotatedPS2Mouse4017 = 0x46,
/// <summary>
/// Magical Cooking
@@ -801,7 +797,7 @@ namespace SabreTools.Data.Models.NES
/// <summary>
/// 小霸王 (Xiǎobàwáng, a.k.a. Subor) Keyboard plus X-inverted PS/2 mouse in $4017
/// </summary>
SuborKeyboardPlusXInvertedPS2Mouse = 0x4D,
SuborKeyboardPlusXInvertedPS2Mouse4017 = 0x4D,
/// <summary>
/// IBM PC/XT Keyboard

View File

@@ -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
/// </remarks>
public ExtendedSystemType ExtendedSystemType { get; set; }
public byte ExtendedSystemType { get; set; }
/// <summary>
/// Number of miscellaneous ROMs present

View File

@@ -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();

View File

@@ -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
/// <inheritdoc/>
public string ExportJSON() => System.Text.Json.JsonSerializer.Serialize(Model, _jsonSerializerOptions);
#endif
/// <inheritdoc/>
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();
}
}
}

View File

@@ -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<Cart>
public partial class NESCart : WrapperBase<Cart>
{
#region Descriptive Properties