1mod formats;
10
11use std::error::Error;
12use std::fmt::{Debug, Display, Formatter};
13
14use num_enum::{FromPrimitive, IntoPrimitive};
15use serde::{Deserialize, Serialize};
16use sha2::{Digest, Sha256};
17
18use crate::emulation::mem::nametable_memory::{NametableArrangement, NametableMemory};
19use crate::emulation::mem::{Memory, MemoryDevice, Ram, Rom};
20use crate::emulation::rom::formats::archaic_ines::ArchaicInes;
21use crate::emulation::rom::formats::ines::Ines;
22use crate::emulation::rom::formats::ines_07::Ines07;
23use crate::emulation::rom::formats::ines2::Ines2;
24
25#[derive(Debug, Clone)]
27pub enum ParseError {
28 SizeBiggerThanFile,
30 InvalidHeader,
32 UnsupportedFormat,
34}
35
36impl Display for ParseError {
37 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
38 match self {
39 ParseError::SizeBiggerThanFile => {
40 write!(
41 f,
42 "Rom sizes specified in header are larger than total rom size"
43 )
44 }
45 ParseError::InvalidHeader => {
46 write!(f, "Rom data is too short to contain a valid header")
47 }
48 ParseError::UnsupportedFormat => {
49 write!(f, "Rom format is not recognized")
50 }
51 }
52 }
53}
54
55impl Error for ParseError {}
56
57#[doc(hidden)]
63pub trait RomParser: Debug {
64 fn parse(&self, rom: &[u8], name: Option<String>) -> Result<RomFile, ParseError>;
65}
66
67#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
88pub struct RomFile {
89 pub name: Option<String>,
91 pub prg_memory: PrgMemory,
93 pub chr_memory: ChrMemory,
95 pub mapper: RomMapper,
97 pub default_expansion_device: ExpansionDevice,
99 pub misc_rom_count: u8,
101 pub extended_console_type: Option<ExtendedConsoleType>,
103 pub vs_system_hardware_type: Option<VsHardwareType>,
105 pub vs_system_ppu_type: Option<VsSystemPpuType>,
107 pub timing_region: RomTimingRegion,
109 pub console_type: ConsoleType,
112 pub hardwired_nametable_layout: bool,
115 pub is_battery_backed: bool,
117 pub trainer_present: bool,
119 pub alternative_nametables: bool,
121 pub submapper_number: u8,
123 pub data_checksum: [u8; 32],
125 #[serde(skip)]
128 pub data: Vec<u8>,
129}
130
131#[derive(
132 Debug,
133 Copy,
134 Clone,
135 Eq,
136 PartialEq,
137 Ord,
138 PartialOrd,
139 Hash,
140 FromPrimitive,
141 IntoPrimitive,
142 Serialize,
143 Deserialize,
144)]
145#[repr(u8)]
146#[serde(into = "u8", from = "u8")]
147pub enum ExpansionDevice {
148 Unspecified = 0,
149 StandardController = 1,
150 FourScore = 2,
151 FourScoreSimple = 3,
152 VsSystem1P4016 = 4,
153 VsSystem1P4017 = 5,
154 VsZapper = 7,
155 Zapper4017 = 8,
156 TwoZappers = 9,
157 BandaiHyperShotLightgun = 10,
158 PowerPadSideA = 11,
159 PowerPadSideB = 12,
160 FamilyTrainerSideA = 13,
161 FamilyTrainerSideB = 14,
162 ArkanoidVausNes = 15,
163 ArkanoidVausFamicom = 16,
164 TwoVausPlusDataRecorder = 17,
165 KonamiHyperShot = 18,
166 CoconutsPachinko = 19,
167 ExcitingBoxingPunchingBag = 20,
168 JissenMahjong = 21,
169 PartyTap = 22,
170 OekaKidsTablet = 23,
171 SunsoftBarcodeBattler = 24,
172 MiraclePianoKeyboard = 25,
173 PokkunMoguraaTapTapMat = 26,
174 TopRider = 27,
175 DoubleFisted = 28,
176 Famicom3dSystem = 29,
177 DoremikkoKeyboard = 30,
178 RobGyromite = 31,
179 FamicomDataRecorder = 32,
180 AsciiTurboFile = 33,
181 IgsStorageBattleBox = 34,
182 FamilyBasicKeyboardPlusDataRecorder = 35,
183 PecKeyboard = 36,
184 Bit79Keyboard = 37,
185 SuborKeyboard = 38,
186 SuborKeyboardPlusMacroWinnersMouse = 39,
187 SuborKeyboardPlusSuborMouse4016 = 40,
188 SnesMouse4016 = 41,
189 Multicart = 42,
190 TwoSnesControllers = 43,
191 RacerMateBicycle = 44,
192 UForce = 45,
193 RobStackUp = 46,
194 CityPatrolmanLightgun = 47,
195 SharpC1CassetteInterface = 48,
196 StandardControllerSwappedLayout = 49,
197 ExcaliburSudokuPad = 50,
198 AblPinball = 51,
199 GoldenNuggetCasinoExtraButtons = 52,
200 KedaKeyboard = 53,
201 SuborKeyboardPlusSuborMouse4017 = 54,
202 PortTestController = 55,
203 BandaiMultiGamePlayerGamepadButtons = 56,
204 VenomTvDanceMat = 57,
205 LgTvRemoteControl = 58,
206 FamicomNetworkController = 59,
207 KingFishingController = 60,
208 CroakyKaraokeController = 61,
209 KingwonKeyboard = 62,
210 ZechengKeyboard = 63,
211 SuborKeyboardPlusL90RotatedPs2Mouse4017 = 64,
212 Ps2KeyboardUM6578PlusPs2Mouse4017 = 65,
213 Ps2MouseUM6578 = 66,
214 YuxingMouse4016 = 67,
215 SuborKeyboardPlusYuxingMouse4016 = 68,
216 GiggleTvPump = 69,
217 BBKKeyboardPlusR90RotatedPs2Mouse4017 = 70,
218 MagicalCooking = 71,
219 SnesMouse4017 = 72,
220 Zapper4016 = 73,
221 ArkanoidVausControllerPrototype = 74,
222 TvMahjongGameController = 75,
223 MahjongGekitouDensetsuController = 76,
224 SuborKeyboardPlusXInvertedPs2Mouse4017 = 77,
225 IbmPcXtKeyboard = 78,
226 SuborKeyboardPlusMegaBookMouse = 79,
227 #[num_enum(catch_all)]
228 Unknown(u8),
229}
230
231impl Display for ExpansionDevice {
232 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
233 let str: &str = match self {
234 ExpansionDevice::Unspecified => "Unspecified",
235 ExpansionDevice::StandardController => "Standard NES/Famicom Controllers",
236 ExpansionDevice::FourScore => {
237 "NES Four Score/Satellite w/ two additional standard controllers"
238 }
239 ExpansionDevice::FourScoreSimple => {
240 "Famicom Four Player Adapter w/ two additional standard controllers using the \
241 \"simple\" protocol"
242 }
243 ExpansionDevice::VsSystem1P4016 => "Vs. System (One Player via Port 1)",
244 ExpansionDevice::VsSystem1P4017 => "Vs. System (One Player via Port 2)",
245 ExpansionDevice::VsZapper => "Vs. Zapper",
246 ExpansionDevice::Zapper4017 => "Zapper (via Port 2)",
247 ExpansionDevice::TwoZappers => "Two Zappers",
248 ExpansionDevice::BandaiHyperShotLightgun => "Bandai Hyper Shot Lightgun",
249 ExpansionDevice::PowerPadSideA => "Power Pad Side A",
250 ExpansionDevice::PowerPadSideB => "Power Pad Side B",
251 ExpansionDevice::FamilyTrainerSideA => "Family Trainer Side A",
252 ExpansionDevice::FamilyTrainerSideB => "Family Trainer Side B",
253 ExpansionDevice::ArkanoidVausNes => "Arkanoid Vaus Controller (NES)",
254 ExpansionDevice::ArkanoidVausFamicom => "Arkanoid Vaus Controller (Famicom)",
255 ExpansionDevice::TwoVausPlusDataRecorder => {
256 "Two Arkanoid Vaus Controllers + Famicom Data Recorder"
257 }
258 ExpansionDevice::KonamiHyperShot => "Konami Hyper Shot Controller",
259 ExpansionDevice::CoconutsPachinko => "Coconuts Pachinko Controller",
260 ExpansionDevice::ExcitingBoxingPunchingBag => "Exciting Boxing Punching Bag",
261 ExpansionDevice::JissenMahjong => "Jissen Mahjong Controller",
262 ExpansionDevice::PartyTap => "米澤 (Yonezawa) Party Tap",
263 ExpansionDevice::OekaKidsTablet => "Oeka Kids Tablet",
264 ExpansionDevice::SunsoftBarcodeBattler => "Sunsoft Barcode Battler",
265 ExpansionDevice::MiraclePianoKeyboard => "Miracle Piano Keyboard",
266 ExpansionDevice::PokkunMoguraaTapTapMat => "Pokkun Moguraa Tap-tap Mat1",
267 ExpansionDevice::TopRider => "Top Rider",
268 ExpansionDevice::DoubleFisted => "Double Fisted",
269 ExpansionDevice::Famicom3dSystem => "Famicom 3D System",
270 ExpansionDevice::DoremikkoKeyboard => "Doremikko Keyboard",
271 ExpansionDevice::RobGyromite => "R.O.B Gyromite",
272 ExpansionDevice::FamicomDataRecorder => "Famicom Data Recorder",
273 ExpansionDevice::AsciiTurboFile => "ASCII Turbo File",
274 ExpansionDevice::IgsStorageBattleBox => "IGS Storage Battle Box",
275 ExpansionDevice::FamilyBasicKeyboardPlusDataRecorder => {
276 "Family Basic Keyboard + Famicom Data Recorder"
277 }
278 ExpansionDevice::PecKeyboard => "东达 (Dōngdá) PEC Keyboard",
279 ExpansionDevice::Bit79Keyboard => "普澤 (Pǔzé, a.k.a. Bit Corp.) Bit-79 Keyboard",
280 ExpansionDevice::SuborKeyboard => "小霸王 (Xiǎobàwáng, a.k.a. Subor) Keyboard",
281 ExpansionDevice::SuborKeyboardPlusMacroWinnersMouse => {
282 "小霸王 (Xiǎobàwáng, a.k.a. Subor) Keyboard + Macro Winners Mouse"
283 }
284 ExpansionDevice::SuborKeyboardPlusSuborMouse4016 => {
285 "小霸王 (Xiǎobàwáng, a.k.a. Subor) Keyboard + Subor Mouse (via Port 1)"
286 }
287 ExpansionDevice::SnesMouse4016 => "SNES Mouse (via Port 1)",
288 ExpansionDevice::Multicart => "Multicart",
289 ExpansionDevice::TwoSnesControllers => "Two SNES Controllers",
290 ExpansionDevice::RacerMateBicycle => "RacerMate Bicycle",
291 ExpansionDevice::UForce => "U-Force",
292 ExpansionDevice::RobStackUp => "R.O.B Stack-Up",
293 ExpansionDevice::CityPatrolmanLightgun => "City Patrolman Lightgun",
294 ExpansionDevice::SharpC1CassetteInterface => "Sharp C1 Cassette Interface",
295 ExpansionDevice::StandardControllerSwappedLayout => {
296 "Standard Controller with swapped Left-Right/Up-Down/B-A"
297 }
298 ExpansionDevice::ExcaliburSudokuPad => "Excalibur Sudoku Pad",
299 ExpansionDevice::AblPinball => "ABL Pinball",
300 ExpansionDevice::GoldenNuggetCasinoExtraButtons => "Golden Nugget Casino Controller",
301 ExpansionDevice::KedaKeyboard => "科达 (Kēdá) Keyboard",
302 ExpansionDevice::SuborKeyboardPlusSuborMouse4017 => {
303 "小霸王 (Xiǎobàwáng, a.k.a. Subor) Keyboard + Subor Mouse (via Port 2)"
304 }
305 ExpansionDevice::PortTestController => "Port test controller",
306 ExpansionDevice::BandaiMultiGamePlayerGamepadButtons => {
307 "Bandai Multi Game Player Gamepad"
308 }
309 ExpansionDevice::VenomTvDanceMat => "Venom TV Dance Mat",
310 ExpansionDevice::LgTvRemoteControl => "LG TV Remote Control",
311 ExpansionDevice::FamicomNetworkController => "Famicom Network Controller",
312 ExpansionDevice::KingFishingController => "King Fishing Controller",
313 ExpansionDevice::CroakyKaraokeController => "Croaky Karaoke Controller",
314 ExpansionDevice::KingwonKeyboard => "科王 (Kēwáng, a.k.a. Kingwon) Keyboard",
315 ExpansionDevice::ZechengKeyboard => "泽诚 (Zéchéng) Keyboard",
316 ExpansionDevice::SuborKeyboardPlusL90RotatedPs2Mouse4017 => {
317 "小霸王 (Xiǎobàwáng, a.k.a. Subor) Keyboard + PS/2 mouse rotated left (via Port 2)"
318 }
319 ExpansionDevice::Ps2KeyboardUM6578PlusPs2Mouse4017 => {
320 "PS/2 Keyboard in UM6578 PS/2 port + PS/2 Mouse (via Port 2)"
321 }
322 ExpansionDevice::Ps2MouseUM6578 => "PS/2 Mouse in UM6578 PS/2 port",
323 ExpansionDevice::YuxingMouse4016 => "裕兴 (Yùxìng) Mouse (via Port 1)",
324 ExpansionDevice::SuborKeyboardPlusYuxingMouse4016 => {
325 "小霸王 (Xiǎobàwáng, a.k.a. Subor )Keyboard + 裕兴 (Yùxìng) Mouse (via Port 1)"
326 }
327 ExpansionDevice::GiggleTvPump => "Giggle TV Pump",
328 ExpansionDevice::BBKKeyboardPlusR90RotatedPs2Mouse4017 => {
329 "步步高 (Bùbùgāo, a.k.a. BBK) Keyboard + PS/2 mouse rotated right (via Port 2)"
330 }
331 ExpansionDevice::MagicalCooking => "Magical Cooking",
332 ExpansionDevice::SnesMouse4017 => "SNES Mouse (via Port 2)",
333 ExpansionDevice::Zapper4016 => "Zapper (via Port 1)",
334 ExpansionDevice::ArkanoidVausControllerPrototype => {
335 "Arkanoid Vaus Controller (Prototype)"
336 }
337 ExpansionDevice::TvMahjongGameController => "TV 麻雀 Game (TV Mahjong Game) Controller",
338 ExpansionDevice::MahjongGekitouDensetsuController => {
339 "麻雀激闘伝説 (Mahjong Gekitou Densetsu) Controller"
340 }
341 ExpansionDevice::SuborKeyboardPlusXInvertedPs2Mouse4017 => {
342 "小霸王 (Xiǎobàwáng, a.k.a. Subor) Keyboard + X-inverted PS/2 mouse via (Port 2)"
343 }
344 ExpansionDevice::IbmPcXtKeyboard => "IBM PC/XT Keyboard",
345 ExpansionDevice::SuborKeyboardPlusMegaBookMouse => {
346 "小霸王 (Xiǎobàwáng, a.k.a. Subor) Keyboard + Mega Book Mouse"
347 }
348 ExpansionDevice::Unknown(_) => "Unknown Expansion Device",
349 };
350
351 let id: u8 = (*self).into();
352
353 write!(f, "{str} (Header: {})", id)
354 }
355}
356
357#[derive(
358 Debug,
359 Copy,
360 Clone,
361 Eq,
362 PartialEq,
363 Ord,
364 PartialOrd,
365 Hash,
366 FromPrimitive,
367 IntoPrimitive,
368 Serialize,
369 Deserialize,
370)]
371#[repr(u8)]
372#[serde(into = "u8", from = "u8")]
373pub enum ExtendedConsoleType {
374 NesFamicom = 0,
375 VsSystem = 1,
376 Playchoice10 = 2,
377 DecimalModeFamiclone = 3,
378 EPSMFamicom = 4,
379 VT01 = 5,
380 VT02 = 6,
381 VT03 = 7,
382 VT09 = 8,
383 VT32 = 9,
384 VT369 = 0xA,
385 UM6578 = 0xB,
386 FamicomNetworkSystem = 0xC,
387 #[num_enum(catch_all)]
388 Unknown(u8),
389}
390
391impl Display for ExtendedConsoleType {
392 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
393 let str: &str = match self {
394 ExtendedConsoleType::NesFamicom => "Nes/Famicom/Dendy",
395 ExtendedConsoleType::VsSystem => "Nintendo Vs. System",
396 ExtendedConsoleType::Playchoice10 => "Nintendo Playchoice 10",
397 ExtendedConsoleType::DecimalModeFamiclone => "Famiclone w/ Decimal Mode CPU",
398 ExtendedConsoleType::EPSMFamicom => {
399 "Nes/Famicom/Dendy w/ EPSM module or plug-through Cartridge"
400 }
401 ExtendedConsoleType::VT01 => "V.R. Technology VT01 with red/cyan STN palette",
402 ExtendedConsoleType::VT02 => "V.R Technology VT02",
403 ExtendedConsoleType::VT03 => "V.R Technology VT03",
404 ExtendedConsoleType::VT09 => "V.R Technology VT09",
405 ExtendedConsoleType::VT32 => "V.R Technology VT32",
406 ExtendedConsoleType::VT369 => "V.R Technology VT369",
407 ExtendedConsoleType::UM6578 => "UMC UM6578",
408 ExtendedConsoleType::FamicomNetworkSystem => "Famicom Network System",
409 ExtendedConsoleType::Unknown(_) => "Unknown Extended Console Type",
410 };
411
412 let id: u8 = (*self).into();
413
414 write!(f, "{str} (Header: {})", id)
415 }
416}
417
418#[derive(
419 Debug,
420 Copy,
421 Clone,
422 Eq,
423 PartialEq,
424 Ord,
425 PartialOrd,
426 Hash,
427 FromPrimitive,
428 IntoPrimitive,
429 Serialize,
430 Deserialize,
431)]
432#[repr(u8)]
433#[serde(into = "u8", from = "u8")]
434pub enum VsHardwareType {
435 UniSystem = 0,
436 UnisystemRbiBaseball = 1,
437 UnisystemTkoBoxing = 2,
438 UnisystemSuperXevious = 3,
439 UnisystemVcIceClimberJapan = 4,
440 DualSystem = 5,
441 DualSystemRaidOnBungelingBay = 6,
442 #[num_enum(catch_all)]
443 Unknown(u8),
444}
445
446impl Display for VsHardwareType {
447 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
448 let str: &str = match self {
449 VsHardwareType::UniSystem => "Vs. Unisystem (normal)",
450 VsHardwareType::UnisystemRbiBaseball => "Vs. Unisystem (RBI Baseball protection)",
451 VsHardwareType::UnisystemTkoBoxing => "Vs. Unisystem (TKO Boxing protection)",
452 VsHardwareType::UnisystemSuperXevious => "Vs. Unisystem (Super Xevious protection)",
453 VsHardwareType::UnisystemVcIceClimberJapan => {
454 "Vs. Unisystem (Vs. Ice Climber Japan protection)"
455 }
456 VsHardwareType::DualSystem => "Vs. Dual System (normal)",
457 VsHardwareType::DualSystemRaidOnBungelingBay => {
458 "Vs. Dual System (Raid on Bungeling Bay protection)"
459 }
460 VsHardwareType::Unknown(_) => "Unknown Vs. System hardware type",
461 };
462
463 let id: u8 = (*self).into();
464
465 write!(f, "{str} (Header: {})", id)
466 }
467}
468
469#[derive(
470 Debug,
471 Copy,
472 Clone,
473 Eq,
474 PartialEq,
475 Ord,
476 PartialOrd,
477 Hash,
478 FromPrimitive,
479 IntoPrimitive,
480 Serialize,
481 Deserialize,
482)]
483#[repr(u8)]
484#[serde(into = "u8", from = "u8")]
485pub enum VsSystemPpuType {
486 RP2C03 = 0,
487 RP2C04_0001 = 2,
488 RP2C04_0002 = 3,
489 RP2C04_0003 = 4,
490 RP2C04_0004 = 5,
491 RC2C05_01 = 8,
492 RC2C05_02 = 9,
493 RC2C05_03 = 0xA,
494 RC2C05_04 = 0xB,
495 #[num_enum(catch_all)]
496 Unknown(u8),
497}
498
499impl Display for VsSystemPpuType {
500 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
501 let str: &str = match self {
502 VsSystemPpuType::RP2C03 => "Any RP2C03/RC2C03 Variant",
503 VsSystemPpuType::RP2C04_0001 => "RP2C04-0001",
504 VsSystemPpuType::RP2C04_0002 => "RP2C04-0002",
505 VsSystemPpuType::RP2C04_0003 => "RP2C04-0003",
506 VsSystemPpuType::RP2C04_0004 => "RP2C04-0004",
507 VsSystemPpuType::RC2C05_01 => "RC2C05-01",
508 VsSystemPpuType::RC2C05_02 => "RC2C05-02",
509 VsSystemPpuType::RC2C05_03 => "RC2C05-03",
510 VsSystemPpuType::RC2C05_04 => "RC2C05-04",
511 VsSystemPpuType::Unknown(_) => "Unknown Vs. System PPU",
512 };
513
514 let id: u8 = (*self).into();
515
516 write!(f, "{str} (Header: {})", id)
517 }
518}
519
520#[derive(
521 Debug,
522 Copy,
523 Clone,
524 Eq,
525 PartialEq,
526 Ord,
527 PartialOrd,
528 Hash,
529 FromPrimitive,
530 IntoPrimitive,
531 Serialize,
532 Deserialize,
533)]
534#[repr(u8)]
535#[serde(into = "u8", from = "u8")]
536pub enum ConsoleType {
537 NesFamicom = 0,
538 VsSystem = 1,
539 Playchoice10 = 2,
540 Extended = 3,
541 #[num_enum(catch_all)]
542 Unknown(u8),
543}
544
545impl Display for ConsoleType {
546 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
547 let str: &str = match self {
548 ConsoleType::NesFamicom => "Nes/Famicom/Dendy",
549 ConsoleType::VsSystem => "Nintendo Vs. System",
550 ConsoleType::Playchoice10 => "Nintendo Playchoice 10",
551 ConsoleType::Extended => "Extended Console Type",
552 ConsoleType::Unknown(_) => "Unknown Console Type",
553 };
554
555 let id: u8 = (*self).into();
556
557 write!(f, "{str} (Header: {})", id)
558 }
559}
560
561#[derive(
562 Debug,
563 Copy,
564 Clone,
565 Eq,
566 PartialEq,
567 Ord,
568 PartialOrd,
569 Hash,
570 FromPrimitive,
571 IntoPrimitive,
572 Serialize,
573 Deserialize,
574)]
575#[repr(u8)]
576#[serde(into = "u8", from = "u8")]
577pub enum RomTimingRegion {
578 RP2C02 = 0,
579 RP2C07 = 1,
580 Multi = 2,
581 UA6538 = 3,
582 #[num_enum(catch_all)]
583 Unknown(u8),
584}
585
586impl Display for RomTimingRegion {
587 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
588 let str: &str = match self {
589 RomTimingRegion::RP2C02 => "NTSC/RP2C02",
590 RomTimingRegion::RP2C07 => "Licensed PAL/RP2C07",
591 RomTimingRegion::Multi => "Multiple Regions",
592 RomTimingRegion::UA6538 => "Dendy/UA6538",
593 RomTimingRegion::Unknown(_) => "Unknown Region",
594 };
595
596 let id: u8 = (*self).into();
597
598 write!(f, "{str} (Header: {})", id)
599 }
600}
601
602#[derive(
603 Debug,
604 Copy,
605 Clone,
606 Eq,
607 PartialEq,
608 Ord,
609 PartialOrd,
610 Hash,
611 FromPrimitive,
612 IntoPrimitive,
613 Serialize,
614 Deserialize,
615)]
616#[repr(u16)]
617#[serde(into = "u16", from = "u16")]
618pub enum RomMapper {
619 NRom = 0,
620 MMC1 = 1,
621 #[num_enum(catch_all)]
622 Unknown(u16),
623}
624
625impl Display for RomMapper {
626 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
627 let str: &str = match self {
628 RomMapper::NRom => "NROM",
629 RomMapper::MMC1 => "MMC1",
630 RomMapper::Unknown(_) => "Unknown Mapper",
631 };
632
633 let mapper_num: u16 = (*self).into();
634
635 write!(f, "{str} (INes Mapper {})", mapper_num)
636 }
637}
638
639#[derive(Debug, Copy, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
641pub struct PrgMemory {
642 pub prg_rom_size: u32,
644 pub prg_ram_size: u32,
646 pub prg_nvram_size: u32,
648}
649
650impl PrgMemory {
651 fn new(prg_rom_size: u32, prg_ram_size: u32, prg_nvram_size: u32) -> PrgMemory {
652 Self {
653 prg_rom_size,
654 prg_nvram_size,
655 prg_ram_size,
656 }
657 }
658}
659
660#[derive(Debug, Copy, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
662pub struct ChrMemory {
663 pub chr_rom_size: u32,
665 pub chr_ram_size: u32,
667 pub chr_nvram_size: u32,
669}
670
671impl ChrMemory {
672 fn new(chr_rom_size: u32, chr_ram_size: u32, chr_nvram_size: u32) -> ChrMemory {
673 Self {
674 chr_rom_size,
675 chr_nvram_size,
676 chr_ram_size,
677 }
678 }
679}
680
681impl RomFile {
682 fn range_all_zeros(arr: &[u8], start: usize, end: usize) -> bool {
683 if start > end || end > arr.len() {
684 return false;
685 }
686 arr[start..end].iter().all(|&x| x == 0)
687 }
688
689 fn get_rom_type(rom: &[u8]) -> Result<Box<dyn RomParser>, ParseError> {
690 if rom.len() < 16 {
692 return Err(ParseError::InvalidHeader);
693 }
694
695 if rom.starts_with(&[0x4E, 0x45, 0x53, 0x1A]) {
696 let prg_rom_size_lsb = rom[4] as u16;
697 let prg_rom_size_msb = (rom[9] & 0xF) as u16;
698
699 let prg_rom_size = Ines2::get_prg_rom_size(prg_rom_size_lsb, prg_rom_size_msb);
700
701 let chr_rom_size_lsb = rom[5] as u16;
702 let chr_rom_size_msb = (rom[9] & 0xF0) as u16;
703
704 let chr_rom_size = Ines2::get_chr_rom_size(chr_rom_size_lsb, chr_rom_size_msb);
705
706 if rom[7] & 0b00001100 == 8
707 && (prg_rom_size as usize + chr_rom_size as usize) < rom.len()
708 {
709 return Ok(Box::new(Ines2));
710 }
711
712 if rom[7] & 0b00001100 == 4 {
713 return Ok(Box::new(ArchaicInes));
714 }
715
716 if rom[7] & 0b00001100 == 0 && Self::range_all_zeros(rom, 12, 16) {
717 return Ok(Box::new(Ines));
718 }
719
720 return Ok(Box::new(Ines07));
721 }
722
723 Err(ParseError::UnsupportedFormat)
724 }
725
726 pub fn load(data: &[u8], name: Option<String>) -> Result<RomFile, ParseError> {
746 let mut hasher = Sha256::new();
747 hasher.update(data);
748 let hash: [u8; 32] = hasher.finalize().into();
749
750 let rom_type = RomFile::get_rom_type(data)?;
751 let mut rom_file = rom_type.parse(data, name)?;
752 rom_file.data = data.to_vec();
753 rom_file.data_checksum = hash;
754 Ok(rom_file)
755 }
756
757 #[doc(hidden)]
762 pub fn get_prg_rom(&self) -> Memory {
763 let mut rom = Rom::new(self.prg_memory.prg_rom_size as usize);
764
765 let mut start = 16usize;
766
767 if self.trainer_present {
768 start += 512;
769 }
770
771 rom.load(
772 self.data[start..start + self.prg_memory.prg_rom_size as usize]
773 .to_vec()
774 .into_boxed_slice(),
775 );
776 Memory::Rom(rom)
777 }
778
779 #[doc(hidden)]
785 pub fn get_chr_rom(&self) -> Option<Memory> {
786 if self.chr_memory.chr_rom_size == 0 {
787 return None;
788 }
789
790 let mut rom = Rom::new(self.chr_memory.chr_rom_size as usize);
791
792 let mut start = 16usize;
793
794 if self.trainer_present {
795 start += 512;
796 }
797
798 rom.load(
799 self.data[start + self.prg_memory.prg_rom_size as usize
800 ..start
801 + self.prg_memory.prg_rom_size as usize
802 + self.chr_memory.chr_rom_size as usize]
803 .to_vec()
804 .into_boxed_slice(),
805 );
806 Some(Memory::Rom(rom))
807 }
808
809 #[doc(hidden)]
814 pub fn get_prg_ram(&self) -> Memory {
815 let mut ram = Ram::new(self.prg_memory.prg_ram_size as usize);
816
817 let mut start = 16usize;
818
819 if self.trainer_present {
820 start += 512;
821 }
822
823 ram.load(
824 self.data[start..start + self.prg_memory.prg_rom_size as usize]
825 .to_vec()
826 .into_boxed_slice(),
827 );
828
829 Memory::Ram(ram)
830 }
831
832 #[doc(hidden)]
838 pub fn get_nametable_memory(&self) -> Memory {
839 let mirroring = match self.hardwired_nametable_layout {
840 true => NametableArrangement::Vertical,
841 false => NametableArrangement::Horizontal,
842 };
843 Memory::NametableMemory(NametableMemory::new(mirroring))
844 }
845}
846
847impl From<&RomFile> for RomFile {
848 fn from(rom: &RomFile) -> Self { rom.clone() }
849}
850
851impl From<&[u8]> for RomFile {
852 fn from(data: &[u8]) -> Self { RomFile::load(data, None).expect("Failed to parse ROM data") }
853}
854
855impl From<&(&[u8], String)> for RomFile {
856 fn from((data, name): &(&[u8], String)) -> Self {
857 RomFile::load(data, Some(name.clone())).expect("Failed to parse ROM data")
858 }
859}
860
861#[cfg(not(target_arch = "wasm32"))]
862impl From<&String> for RomFile {
863 fn from(path: &String) -> Self {
864 use std::fs::File;
865 use std::io::Read;
866
867 let mut data = Vec::new();
868 File::open(path)
869 .unwrap_or_else(|e| panic!("Failed to open ROM file '{}': {}", path, e))
870 .read_to_end(&mut data)
871 .unwrap_or_else(|e| panic!("Failed to read ROM file '{}': {}", path, e));
872
873 RomFile::load(&data, Some(path.clone())).expect("Failed to parse ROM data")
874 }
875}
876
877pub struct RomBuilder {
897 name: Option<String>,
898 prg_rom_size: u32,
899 chr_rom_size: u32,
900 mapper_number: u16,
901 default_expansion_device: u8,
902 misc_rom_count: u8,
903 extended_console_type: Option<u8>,
904 vs_system_hardware_type: Option<u8>,
905 vs_system_ppu_type: Option<u8>,
906 rom_timing_region: u8,
907 chr_nvram_size: u32,
908 chr_ram_size: u32,
909 prg_nvram_size: u32,
910 prg_ram_size: u32,
911 console_type: u8,
912 hardwired_nametable_layout: bool,
913 is_battery_backed: bool,
914 trainer_present: bool,
915 alternative_nametables: bool,
916 submapper_number: u8,
917}
918
919impl Default for RomBuilder {
920 fn default() -> Self {
921 Self {
922 name: None,
923 prg_rom_size: 0,
924 chr_rom_size: 0,
925 mapper_number: 0,
926 default_expansion_device: 0,
927 misc_rom_count: 0,
928 extended_console_type: None,
929 vs_system_hardware_type: None,
930 vs_system_ppu_type: None,
931 rom_timing_region: 0,
932 chr_nvram_size: 0,
933 chr_ram_size: 0,
934 prg_nvram_size: 0,
935 prg_ram_size: 8 * 1024,
936 console_type: 0,
937 hardwired_nametable_layout: false,
938 is_battery_backed: false,
939 trainer_present: false,
940 alternative_nametables: false,
941 submapper_number: 0,
942 }
943 }
944}
945
946impl RomBuilder {
947 pub fn new() -> Self { Self::default() }
950
951 pub fn prg_rom_size(mut self, size: u32) -> Self {
953 self.prg_rom_size = size;
954 self
955 }
956
957 pub fn chr_rom_size(mut self, size: u32) -> Self {
959 self.chr_rom_size = size;
960 self
961 }
962
963 pub fn mapper_number(mut self, number: u16) -> Self {
965 self.mapper_number = number;
966 self
967 }
968
969 pub fn default_expansion_device(mut self, device: u8) -> Self {
971 self.default_expansion_device = device;
972 self
973 }
974
975 pub fn misc_rom_count(mut self, count: u8) -> Self {
977 self.misc_rom_count = count;
978 self
979 }
980
981 pub fn extended_console_type(mut self, console_type: Option<u8>) -> Self {
983 self.extended_console_type = console_type;
984 self
985 }
986
987 pub fn vs_system_hardware_type(mut self, hardware_type: Option<u8>) -> Self {
989 self.vs_system_hardware_type = hardware_type;
990 self
991 }
992
993 pub fn vs_system_ppu_type(mut self, ppu_type: Option<u8>) -> Self {
995 self.vs_system_ppu_type = ppu_type;
996 self
997 }
998
999 pub fn cpu_ppu_timing(mut self, timing: u8) -> Self {
1001 self.rom_timing_region = timing;
1002 self
1003 }
1004
1005 pub fn chr_nvram_size(mut self, size: u32) -> Self {
1007 self.chr_nvram_size = size;
1008 self
1009 }
1010
1011 pub fn chr_ram_size(mut self, size: u32) -> Self {
1013 self.chr_ram_size = size;
1014 self
1015 }
1016
1017 pub fn prg_nvram_size(mut self, size: u32) -> Self {
1019 self.prg_nvram_size = size;
1020 self
1021 }
1022
1023 pub fn prg_ram_size(mut self, size: u32) -> Self {
1025 self.prg_ram_size = size;
1026 self
1027 }
1028
1029 pub fn console_type(mut self, console_type: u8) -> Self {
1032 self.console_type = console_type;
1033 self
1034 }
1035
1036 pub fn hardwired_nametable_layout(mut self, value: bool) -> Self {
1038 self.hardwired_nametable_layout = value;
1039 self
1040 }
1041
1042 pub fn battery_backed(mut self, value: bool) -> Self {
1044 self.is_battery_backed = value;
1045 self
1046 }
1047
1048 pub fn trainer_present(mut self, value: bool) -> Self {
1050 self.trainer_present = value;
1051 self
1052 }
1053
1054 pub fn alternative_nametables(mut self, value: bool) -> Self {
1056 self.alternative_nametables = value;
1057 self
1058 }
1059
1060 pub fn submapper_number(mut self, number: u8) -> Self {
1062 self.submapper_number = number;
1063 self
1064 }
1065
1066 pub fn name(mut self, value: Option<String>) -> Self {
1068 self.name = value;
1069 self
1070 }
1071
1072 pub fn build(self) -> RomFile {
1076 RomFile {
1077 name: self.name,
1078 prg_memory: PrgMemory::new(self.prg_rom_size, self.prg_ram_size, self.prg_nvram_size),
1079 chr_memory: ChrMemory::new(self.chr_rom_size, self.chr_ram_size, self.chr_nvram_size),
1080 mapper: RomMapper::from(self.mapper_number),
1081 default_expansion_device: ExpansionDevice::from(self.default_expansion_device),
1082 misc_rom_count: self.misc_rom_count,
1083 extended_console_type: self.extended_console_type.map(ExtendedConsoleType::from),
1084 vs_system_hardware_type: self.vs_system_hardware_type.map(VsHardwareType::from),
1085 vs_system_ppu_type: self.vs_system_ppu_type.map(VsSystemPpuType::from),
1086 timing_region: RomTimingRegion::from(self.rom_timing_region),
1087 console_type: ConsoleType::from(self.console_type),
1088 hardwired_nametable_layout: self.hardwired_nametable_layout,
1089 is_battery_backed: self.is_battery_backed,
1090 trainer_present: self.trainer_present,
1091 alternative_nametables: self.alternative_nametables,
1092 submapper_number: self.submapper_number,
1093 data_checksum: [0; 32],
1094 data: Vec::new(),
1095 }
1096 }
1097}
1098
1099#[cfg(test)]
1100mod tests {
1101 use super::*;
1102
1103 #[test]
1104 fn repr_enums_serialize_as_numbers() {
1105 let serialized = serde_json::to_string(&ExpansionDevice::StandardController)
1106 .expect("failed to serialize expansion device");
1107 assert_eq!(serialized, "1");
1108
1109 let serialized =
1110 serde_json::to_string(&RomMapper::MMC1).expect("failed to serialize rom mapper");
1111 assert_eq!(serialized, "1");
1112 }
1113
1114 #[test]
1115 fn repr_enums_deserialize_unknown_values() {
1116 let expansion: ExpansionDevice =
1117 serde_json::from_str("200").expect("failed to deserialize expansion device");
1118 assert_eq!(expansion, ExpansionDevice::Unknown(200));
1119
1120 let mapper: RomMapper = serde_json::from_str("9999").expect("failed to deserialize mapper");
1121 assert_eq!(mapper, RomMapper::Unknown(9999));
1122 }
1123
1124 #[test]
1125 fn rom_file_enum_fields_use_repr_values_in_json() {
1126 let rom = RomBuilder::new()
1127 .mapper_number(1)
1128 .default_expansion_device(1)
1129 .console_type(1)
1130 .cpu_ppu_timing(2)
1131 .vs_system_hardware_type(Some(5))
1132 .vs_system_ppu_type(Some(3))
1133 .extended_console_type(Some(0xC))
1134 .build();
1135
1136 let json = serde_json::to_string(&rom).expect("failed to serialize rom");
1137 assert!(json.contains("\"mapper\":1"));
1138 assert!(json.contains("\"default_expansion_device\":1"));
1139 assert!(json.contains("\"console_type\":1"));
1140 assert!(json.contains("\"timing_region\":2"));
1141 assert!(json.contains("\"vs_system_hardware_type\":5"));
1142 assert!(json.contains("\"vs_system_ppu_type\":3"));
1143 assert!(json.contains("\"extended_console_type\":12"));
1144 }
1145}