agb/agb-gbafix/src/main.rs

98 lines
3 KiB
Rust
Raw Normal View History

2023-07-26 18:53:32 +10:00
use anyhow::{anyhow, bail, Result};
2023-04-07 08:59:28 +10:00
use clap::{arg, value_parser};
2023-04-07 08:16:38 +10:00
2023-04-07 06:09:44 +10:00
use std::{
2023-04-07 08:16:38 +10:00
fs,
2023-04-07 06:54:26 +10:00
io::{BufWriter, Write},
2023-04-07 06:09:44 +10:00
path::PathBuf,
};
2023-07-26 19:21:54 +10:00
use agb_gbafix::{write_gba_file, GbaHeader, PaddingBehaviour};
2023-07-26 18:53:32 +10:00
2023-04-07 08:16:38 +10:00
fn main() -> Result<()> {
2023-04-07 08:59:28 +10:00
let matches = clap::Command::new("agb-gbafix")
.about("Convert elf files directly to a valid GBA ROM")
2023-04-07 08:43:57 +10:00
.arg(arg!(<INPUT> "Input elf file").value_parser(value_parser!(PathBuf)))
.arg(arg!(-o --output <OUTPUT> "Set output file, defaults to replacing INPUT's extension to .gba").value_parser(value_parser!(PathBuf)))
2023-04-07 09:03:00 +10:00
.arg(arg!(-t --title <TITLE> "Set the title. At most 12 bytes. Defaults to truncating the input file name"))
2023-04-07 08:56:19 +10:00
.arg(arg!(-c --gamecode <GAME_CODE> "Sets the game code, 4 bytes"))
2023-04-09 06:18:27 +10:00
.arg(arg!(-m --makercode <MAKER_CODE> "Set the maker code, 2 bytes"))
2023-04-07 08:56:19 +10:00
.arg(arg!(-r --gameversion <VERSION> "Set the version of the game, 0-255").value_parser(value_parser!(u8)))
2023-07-26 19:21:54 +10:00
.arg(arg!(-p --padding "Pad the ROM to the next power of 2 in size"))
.arg(arg!(-g --debug "Include debug information directly in the ROM"))
2023-04-07 08:43:57 +10:00
.get_matches();
2023-04-07 08:13:42 +10:00
2023-04-07 08:43:57 +10:00
let input = matches.get_one::<PathBuf>("INPUT").unwrap();
let output = match matches.get_one::<PathBuf>("output") {
Some(output) => output.clone(),
None => input.with_extension("gba"),
};
2023-07-26 19:18:28 +10:00
let mut header = GbaHeader::default();
2023-04-07 08:56:19 +10:00
2023-04-07 09:03:00 +10:00
{
let title = if let Some(title) = matches.get_one::<String>("title") {
title.clone()
} else {
let title = input
.file_stem()
.ok_or_else(|| anyhow!("Invalid filename {}", input.to_string_lossy()))?
.to_string_lossy();
title.into_owned()
};
2023-04-07 08:56:19 +10:00
for (i, &c) in title.as_bytes().iter().enumerate().take(12) {
2023-07-26 19:18:28 +10:00
header.game_title[i] = c;
2023-04-07 08:56:19 +10:00
}
}
2023-04-09 06:18:27 +10:00
if let Some(maker_code) = matches.get_one::<String>("makercode") {
let maker_code = maker_code.as_bytes();
if maker_code.len() > 2 {
bail!(
"Maker code must be at most 2 bytes, got {}",
maker_code.len()
);
}
header.maker_code = [
*maker_code.first().unwrap_or(&0),
*maker_code.get(1).unwrap_or(&0),
];
2023-04-07 08:56:19 +10:00
}
if let Some(game_version) = matches.get_one::<u8>("gameversion") {
2023-07-26 19:18:28 +10:00
header.software_version = *game_version;
2023-04-07 08:56:19 +10:00
}
2023-04-07 08:43:57 +10:00
2023-04-07 08:56:19 +10:00
if let Some(game_code) = matches.get_one::<String>("gamecode") {
for (i, &c) in game_code.as_bytes().iter().enumerate().take(4) {
header.game_code[i] = c;
}
}
let include_debug = matches.get_flag("debug");
2023-07-26 19:21:54 +10:00
let pad = matches.get_flag("padding");
let pad = if pad {
PaddingBehaviour::Pad
} else {
PaddingBehaviour::DoNotPad
};
2023-04-07 08:56:19 +10:00
let mut output = BufWriter::new(fs::File::create(output)?);
2023-04-07 08:43:57 +10:00
let file_data = fs::read(input)?;
2023-04-07 06:09:44 +10:00
write_gba_file(
file_data.as_slice(),
header,
pad,
include_debug,
&mut output,
)?;
2023-04-07 08:13:42 +10:00
output.flush()?;
Ok(())
}