use nds_cart::Cart;
use std::fs::File;
use std::io::BufWriter;
use std::path::PathBuf;
use png::BitDepth;
use png::ColorType;
use png::Encoder;
use clap::Parser;
#[derive(Parser)]
struct Cli {
#[arg(short, long)]
fetch_icon: bool,
files: Vec<PathBuf>,
}
pub fn main() {
let args = Cli::parse();
if args.files.is_empty() {
return;
}
println!("[");
for (i, path) in args.files.iter().enumerate() {
let f = File::open(path).unwrap();
let cart = Cart::try_from(f);
if let Err(ref err) = cart {
eprintln!("Skipping {}, error: {err}", path.to_string_lossy());
continue;
}
let mut cart = cart.unwrap();
let header_checksum = cart.header_checksum().unwrap();
let header = &cart.metadata;
println!("\t{{");
println!("\t\t\"title\": \"{}\",", header.title);
println!("\t\t\"game code\": \"{}\",", header.game_code);
println!("\t\t\"manufacturer\": \"{}\",", header.manufacturer);
println!("\t\t\"unit code\": \"{}\",", header.unit_code);
println!(
"\t\t\"encryption seed select\": {},",
header.encryption_seed_select
);
println!(
"\t\t\"device capacity\": \"{} KiB\",",
header.device_capacity
);
println!("\t\t\"language\": \"{}\",", header.language);
println!("\t\t\"region\": \"{}\",", header.region);
println!("\t\t\"version\": {},", header.version);
println!("\t\t\"autostart\": {},", header.autostart);
println!("\t\t\"ARM9 rom offset\": {},", header.arm9_rom_offset);
println!(
"\t\t\"ARM9 entry address\": \"0x{:08X}\",",
header.arm9_entry_address
);
println!(
"\t\t\"ARM9 RAM address\": \"0x{:08X}\",",
header.arm9_ram_address
);
println!("\t\t\"ARM9 size\": {},", header.arm9_size);
println!("\t\t\"ARM7 rom offset\": {},", header.arm7_rom_offset);
println!(
"\t\t\"ARM7 entry address\": \"0x{:08X}\",",
header.arm7_entry_address
);
println!(
"\t\t\"ARM7 RAM address\": \"0x{:08X}\",",
header.arm7_ram_address
);
println!("\t\t\"ARM7 size\": {},", header.arm7_size);
println!(
"\t\t\"file name table offset\": {},",
header.file_name_table_offset
);
println!(
"\t\t\"file name table size\": {},",
header.file_name_table_size
);
println!(
"\t\t\"file allocation table offset\": {},",
header.file_allocation_table_offset
);
println!(
"\t\t\"file allocation table size\": {},",
header.file_allocation_table_size
);
println!(
"\t\t\"ARM9 overlay offset\": {},",
header.arm9_overlay_offset
);
println!("\t\t\"ARM9 overlay size\": {},", header.arm9_overlay_size);
println!(
"\t\t\"ARM7 overlay offset\": {},",
header.arm7_overlay_offset
);
println!("\t\t\"ARM7 overlay size\": {},", header.arm7_overlay_size);
println!(
"\t\t\"ROMCTL setting normal\": \"0x{:08X}\",",
header.romctl_setting_normal
);
println!(
"\t\t\"ROMCTL setting key1\": \"0x{:08X}\",",
header.romctl_setting_key1
);
println!("\t\t\"icon offset\": {},", header.icon_offset);
println!(
"\t\t\"secure area checksum\": \"0x{:04X}\",",
header.secure_area_checksum
);
println!(
"\t\t\"secure area delay\": \"{} ms\",",
u32::from(header.secure_area_delay) / 131
);
println!("\t\t\"ARM9 autoload\": \"0x{:08X}\",", header.arm9_autoload);
println!("\t\t\"ARM7 autoload\": \"0x{:08X}\",", header.arm7_autoload);
println!(
"\t\t\"secure area disable\": \"0x{:016X}\",",
header.secure_area_disable
);
println!("\t\t\"used ROM size\": {},", header.used_rom_size);
println!("\t\t\"ROM header size\": {},", header.rom_header_size);
println!(
"\t\t\"NAND end of ROM\": \"0x{:08X}\",",
u32::from(header.nand_end_of_rom) * 0x20000
);
println!(
"\t\t\"NAND start of RW\": \"0x{:08X}\",",
u32::from(header.nand_start_of_rw) * 0x20000
);
println!(
"\t\t\"Nintendo logo checksum\": \"0x{:04X}\",",
header.nintendo_logo_checksum
);
println!(
"\t\t\"header checksum\": \"0x{:04X}\",",
header.header_checksum
);
println!("\t\t\"computed header checksum\": \"0x{header_checksum:04X}\",");
println!(
"\t\t\"debug ROM offset\": \"0x{:08X}\",",
header.debug_rom_offset
);
println!("\t\t\"debug size\": \"0x{:08X}\",", header.debug_size);
println!(
"\t\t\"debug RAM address\": \"0x{:08X}\"",
header.debug_ram_address
);
if i == (args.files.len() - 1) {
println!("\t}}");
} else {
println!("\t}},");
}
if args.fetch_icon {
let icon = cart.extract_rgba_icon().unwrap();
let f = File::create(cart.metadata.game_code + ".png").unwrap();
let w = BufWriter::new(f);
let mut encoder = Encoder::new(w, 32, 32);
encoder.set_color(ColorType::Rgba);
encoder.set_depth(BitDepth::Eight);
let mut writer = encoder.write_header().unwrap();
writer.write_image_data(&icon).unwrap();
}
}
println!("]");
}