Skip to main content

hash_frame/
hash_frame.rs

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