use std::path::PathBuf;
use std::process::Command;
use clap::{Parser, Subcommand};
use gtrom::asm::{build_asm, build_asm_in_container};
use gtrom::audio::do_audio_build;
use gtrom::cargo::{cargo_build, cargo_build_in_container, find_rom_dir, get_crate_name};
use gtrom::container::{ensure_container, is_in_container};
use gtrom::init::do_init;
use gtrom::rom_builder::RomBuilder;
#[derive(Parser)]
#[command(name = "gtrom")]
#[command(version, about = "GameTank ROM build tool", long_about = None)]
struct Cli {
#[command(subcommand)]
command: Commands,
}
#[derive(Subcommand)]
enum Commands {
Build {
#[arg(short, long, default_value_t = true)]
release: bool,
},
Audio {
path: String,
},
Convert {
elf_path: String,
#[arg(short, long)]
output: Option<String>,
},
Init {
#[arg(default_value = ".")]
path: String,
#[arg(long)]
name: Option<String>,
#[arg(long)]
with_audiofw_src: bool,
#[arg(long, default_value = "wavetable-8v")]
audio: String,
},
Run {},
Flash {
#[arg(short, long)]
port: Option<String>,
},
}
fn convert_elf_to_gtr(elf_path: &str, output: &str) -> Result<(), String> {
println!("Converting ELF to GTR: {} -> {}", elf_path, output);
RomBuilder::build(elf_path.to_string(), output.to_string());
Ok(())
}
fn do_build(release: bool) -> Result<PathBuf, String> {
let (working_dir, rom_dir) = find_rom_dir()?;
if is_in_container() {
let rom_dir_str = rom_dir.to_string_lossy().to_string();
build_asm(&rom_dir_str)?;
cargo_build(&rom_dir_str, release)?;
} else {
let workspace_root = ensure_container()?;
build_asm_in_container(&rom_dir, &workspace_root)?;
cargo_build_in_container(&rom_dir, &workspace_root, release)?;
}
let crate_name = get_crate_name(&rom_dir)?;
let profile = if release { "release" } else { "debug" };
let elf_path = rom_dir.join(format!("target/mos-unknown-none/{}/{}", profile, crate_name));
let gtr_path = working_dir.join(format!("{}.gtr", crate_name));
convert_elf_to_gtr(
elf_path.to_str().unwrap(),
gtr_path.to_str().unwrap(),
)?;
println!("Build complete: {}", gtr_path.display());
Ok(gtr_path)
}
fn main() {
let cli = Cli::parse();
let result: Result<(), String> = match cli.command {
Commands::Build { release } => {
do_build(release).map(|_| ())
}
Commands::Audio { path } => {
do_audio_build(&path)
}
Commands::Convert { elf_path, output } => {
let out = output.unwrap_or_else(|| "game.gtr".to_string());
convert_elf_to_gtr(&elf_path, &out)
}
Commands::Init { path, name, with_audiofw_src, audio } => {
do_init(&path, name.as_deref(), with_audiofw_src, &audio)
}
Commands::Run {} => {
do_build(true).and_then(|gtr_path| {
println!("Launching emulator...");
let status = Command::new("gte")
.arg(>r_path)
.status()
.map_err(|e| format!("Failed to launch gte: {}", e))?;
if status.success() {
Ok(())
} else {
Err("Emulator exited with error".to_string())
}
})
}
Commands::Flash { port } => {
do_build(true).and_then(|gtr_path| {
println!("Flashing to cartridge...");
let gtr_str = gtr_path.to_string_lossy().to_string();
let mut args = vec!["load".to_string(), gtr_str];
if let Some(ref p) = port {
args.push("--port".to_string());
args.push(p.clone());
}
let status = Command::new("gtld")
.args(&args)
.status()
.map_err(|e| format!("Failed to run gtld: {}", e))?;
if status.success() {
Ok(())
} else {
Err("Flash failed".to_string())
}
})
}
};
if let Err(e) = result {
eprintln!("Error: {}", e);
std::process::exit(1);
}
}