export_frame/
export_frame.rs1use nes_sim::NES;
2use nes_sim::headless::write_frame_ppm;
3use std::env;
4use std::path::Path;
5use std::process::ExitCode;
6
7fn usage(program: &str) {
8 eprintln!("Usage: {program} <rom-path> <output-ppm> [frames]");
9 eprintln!(r#"Example: {program} "roms/mmc1/Rockman2(J).nes" "out/rockman2.ppm" 180"#);
10}
11
12fn main() -> ExitCode {
13 let mut args = env::args();
14 let program = args.next().unwrap_or_else(|| "export_frame".to_string());
15
16 let Some(rom_path) = args.next() else {
17 usage(&program);
18 return ExitCode::from(2);
19 };
20 let Some(output_path) = args.next() else {
21 usage(&program);
22 return ExitCode::from(2);
23 };
24 let frames = match args.next() {
25 Some(value) => match value.parse::<usize>() {
26 Ok(frames) => frames,
27 Err(error) => {
28 eprintln!("invalid frame count {value:?}: {error}");
29 return ExitCode::from(2);
30 }
31 },
32 None => 120,
33 };
34
35 let rom = match std::fs::read(&rom_path) {
36 Ok(rom) => rom,
37 Err(error) => {
38 eprintln!("failed to read ROM {rom_path:?}: {error}");
39 return ExitCode::from(1);
40 }
41 };
42
43 let mut nes = NES::new();
44 if let Err(error) = nes.load_cartridge_ines(&rom) {
45 eprintln!("failed to load ROM {rom_path:?}: {error}");
46 return ExitCode::from(1);
47 }
48 nes.reset();
49
50 for _ in 0..frames {
51 nes.run_frame();
52 }
53
54 if let Some(parent) = Path::new(&output_path).parent()
55 && !parent.as_os_str().is_empty()
56 {
57 if let Err(error) = std::fs::create_dir_all(parent) {
58 eprintln!("failed to create output directory {:?}: {}", parent, error);
59 return ExitCode::from(1);
60 }
61 }
62
63 if let Err(error) = write_frame_ppm(&output_path, nes.video_frame()) {
64 eprintln!("failed to write PPM {output_path:?}: {error}");
65 return ExitCode::from(1);
66 }
67
68 println!(
69 "wrote frame {} from {} to {}",
70 nes.frame_number(),
71 rom_path,
72 output_path
73 );
74 ExitCode::SUCCESS
75}