diff --git a/src/cmd/metroidbuildinfo.rs b/src/cmd/metroidbuildinfo.rs new file mode 100644 index 00000000..0491b104 --- /dev/null +++ b/src/cmd/metroidbuildinfo.rs @@ -0,0 +1,48 @@ +use std::path::PathBuf; + +use anyhow::{Context, Result, bail, ensure}; +use argp::FromArgs; +use memchr::memmem; +use memmap2::MmapOptions; + +#[derive(FromArgs, PartialEq, Eq, Debug)] +/// Sets the MetroidBuildInfo tag value in a given binary. +#[argp(subcommand, name = "metroidbuildinfo")] +pub struct Args { + #[argp(positional)] + /// path to source binary + binary: PathBuf, + #[argp(positional)] + /// path to build info string + build_info: PathBuf, +} + +const BUILD_STRING_MAX: usize = 35; +const BUILD_STRING_TAG: &str = "!#$MetroidBuildInfo!#$"; + +pub fn run(args: Args) -> Result<()> { + let build_string = std::fs::read_to_string(&args.build_info).with_context(|| { + format!("Failed to read build info string from '{}'", args.build_info.display()) + })?; + let build_string_trim = build_string.trim_end(); + let build_string_bytes = build_string_trim.as_bytes(); + ensure!( + build_string_bytes.len() <= BUILD_STRING_MAX, + "Build string '{build_string_trim}' is greater than maximum size of {BUILD_STRING_MAX}" + ); + + let binary_file = + std::fs::File::options().read(true).write(true).open(&args.binary).with_context(|| { + format!("Failed to open binary for writing: '{}'", args.binary.display()) + })?; + let mut map = unsafe { MmapOptions::new().map_mut(&binary_file) } + .with_context(|| format!("Failed to mmap binary: '{}'", args.binary.display()))?; + let start = match memmem::find(&map, BUILD_STRING_TAG.as_bytes()) { + Some(idx) => idx + BUILD_STRING_TAG.len(), + None => bail!("Failed to find build string tag in binary"), + }; + let end = start + build_string_bytes.len(); + map[start..end].copy_from_slice(build_string_bytes); + map[end] = 0; + Ok(()) +} diff --git a/src/cmd/mod.rs b/src/cmd/mod.rs index 2162501b..284c4f18 100644 --- a/src/cmd/mod.rs +++ b/src/cmd/mod.rs @@ -8,6 +8,7 @@ pub mod elf; pub mod elf2dol; pub mod extab; pub mod map; +pub mod metroidbuildinfo; pub mod nlzss; pub mod rarc; pub mod rel; diff --git a/src/main.rs b/src/main.rs index 5f6e6b5a..4333df7c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -98,6 +98,7 @@ enum SubCommand { Elf2Dol(cmd::elf2dol::Args), Extab(cmd::extab::Args), Map(cmd::map::Args), + MetroidBuildInfo(cmd::metroidbuildinfo::Args), Nlzss(cmd::nlzss::Args), Rarc(cmd::rarc::Args), Rel(cmd::rel::Args), @@ -176,6 +177,7 @@ fn main() { SubCommand::Elf2Dol(c_args) => cmd::elf2dol::run(c_args), SubCommand::Extab(c_args) => cmd::extab::run(c_args), SubCommand::Map(c_args) => cmd::map::run(c_args), + SubCommand::MetroidBuildInfo(c_args) => cmd::metroidbuildinfo::run(c_args), SubCommand::Nlzss(c_args) => cmd::nlzss::run(c_args), SubCommand::Rarc(c_args) => cmd::rarc::run(c_args), SubCommand::Rel(c_args) => cmd::rel::run(c_args),