diff --git a/Cargo.lock b/Cargo.lock index b784fde..c44a130 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -25,6 +25,12 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "cfg-if" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9" + [[package]] name = "clap" version = "3.2.25" @@ -51,6 +57,12 @@ dependencies = [ "os_str_bytes", ] +[[package]] +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" + [[package]] name = "errno" version = "0.3.9" @@ -134,17 +146,27 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" [[package]] -name = "rgbds-obj" -version = "0.4.0" +name = "patharg" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ced192eee5b2317a1b91405464f4c559b2dae660d9d5fd722373f704d05ec03d" +checksum = "4d67efed937875267f39986682045e3430c4da4055785825506837ddb037c771" +dependencies = [ + "cfg-if", + "either", +] [[package]] -name = "rgbobj" +name = "rgbds-obj" version = "0.5.0" + +[[package]] +name = "rgbobj" +version = "1.0.0" dependencies = [ "clap", + "either", "paste", + "patharg", "rgbds-obj", "sigpipe", "termcolor", diff --git a/Cargo.toml b/Cargo.toml index 68531eb..5b492dd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "rgbobj" -version = "0.5.0" # Remember to sync html_root_url in `main.rs` +version = "1.0.0" # Remember to sync html_root_url in `main.rs` authors = ["ISSOtm ", "Rangi42"] edition = "2018" description = "A command-line program to print out RGBDS object files." @@ -13,8 +13,10 @@ categories = ["command-line-utilities", "development-tools::debugging", "game-de # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +either = "1.15.0" paste = "1.0" -rgbds-obj = "0.4.0" +patharg = "0.4.1" +rgbds-obj = { path = "../rgbds-obj" } sigpipe = "0.1.3" termcolor = "1.1.2" diff --git a/rgbobj.1 b/rgbobj.1 index 5e555a0..d4939d4 100644 --- a/rgbobj.1 +++ b/rgbobj.1 @@ -3,7 +3,7 @@ .\" .\" SPDX-License-Identifier: MIT .\" -.Dd May 2, 2021 +.Dd Aug 17, 2025 .Dt RGBOBJ 1 .Os .Sh NAME @@ -24,6 +24,12 @@ .Nm allows examining RGBDS object files in a human-friendly manner. .Pp +The input +.Ar path +can be a path to an object file, or +.Cm \- +to read from standard input. +.Pp The options are as follows: .Bl -tag -width Ds .It Fl A , Fl Fl all @@ -214,7 +220,7 @@ It may also report 0 bytes, e.g. if the input file is a FIFO. The output format may be flakey if not requesting at least the name when it's present. .Pp Please report other bugs on -.Lk https://github.com/ISSOtm/rgbobj/issues GitHub . +.Lk https://github.com/gbdev/rgbobj/issues GitHub . .Sh SEE ALSO .Xr rgbasm 1 , .Xr rgblink 1 , diff --git a/src/args.rs b/src/args.rs index ca1d098..7346163 100644 --- a/src/args.rs +++ b/src/args.rs @@ -2,7 +2,7 @@ use clap::builder::PossibleValue; use clap::{arg, command, value_parser, Command, ValueEnum}; -use std::ffi::OsString; +use patharg::InputArg; use std::fmt::{self, Display, Formatter}; use std::stringify; use termcolor::ColorChoice; @@ -302,7 +302,7 @@ fn get_cmd() -> Command<'static> { .arg(arg!(-s --section "Keyword list of what to display about sections").value_parser(value_parser!(SectionFeatures)).default_value(SectionFeatures::DEFAULT).required(false)) .arg(arg!(-p --patch "Keyword list of what to display about patches").value_parser(value_parser!(PatchFeatures)).default_value(PatchFeatures::DEFAULT).required(false)) .arg(arg!(-a --assertion "Keyword list of what to display about assertions").value_parser(value_parser!(AssertionFeatures)).default_value(AssertionFeatures::DEFAULT).required(false)) - .arg(arg!( "Path to the object file to inspect")) + .arg(arg!( "Path to the object file to inspect (use '-' for standard input)")) } /// Parse command-line arguments. @@ -356,7 +356,7 @@ pub fn parse() -> Args { patch, assertion, - path: matches.value_of("file").unwrap().into(), + path: InputArg::from(matches.value_of("file").unwrap()), color_out, color_err, @@ -374,7 +374,7 @@ pub struct Args { pub patch: PatchFeatures, pub assertion: AssertionFeatures, - pub path: OsString, + pub path: InputArg, pub color_out: ColorChoice, pub color_err: ColorChoice, diff --git a/src/main.rs b/src/main.rs index c563ec4..05fcfd6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,12 +1,11 @@ -#![doc(html_root_url = "https://docs.rs/rgbobj/0.5.0")] +#![doc(html_root_url = "https://docs.rs/rgbobj/1.0.0")] -use rgbds_obj::{Node, NodeType, NodeWalkError, Object}; +use either::Either; +use rgbds_obj::{Node, Object}; use std::convert::{Infallible, TryFrom}; use std::error::Error; use std::fmt::{self, Display, Formatter}; -use std::fs::File; -use std::io::{self, BufReader, Write}; -use std::path::Path; +use std::io::{self, Write}; use std::process; use termcolor::{ColorChoice, ColorSpec, StandardStream, WriteColor}; @@ -50,6 +49,20 @@ macro_rules! plural { }; } +fn rept_parent_non_rept<'a>(object: &'a Object, node: &'a Node) -> Option<&'a Node> { + let mut node = node; + if !node.is_rept() { + return None; + } + loop { + let (parent_id, _) = node.parent()?; + node = object.node(parent_id)?; + if !node.is_rept() { + return Some(node) + } + } +} + fn work(args: &Args) -> Result<(), MainError> { macro_rules! error { ($($args:tt)+) => { @@ -90,7 +103,7 @@ fn work(args: &Args) -> Result<(), MainError> { }; } - let mut file = BufReader::new(File::open(&args.path)?); + let mut file = args.path.open()?; let object = Object::read_from(&mut file)?; let mut stdout = StandardStream::stdout(args.color_out); @@ -129,34 +142,25 @@ fn work(args: &Args) -> Result<(), MainError> { ($source:expr) => {{ let (id, line_no) = $source; // TODO: wrap the node stack nicely - object - .walk_nodes::(id, &mut |node: &Node| { - if let Some((id, line)) = node.parent() { - print!("({line}) -> "); - // REPT nodes are prefixed by their parent's name - if matches!(node.type_data(), NodeType::Rept(..)) { - print!( - "{}", - object - .node(id) - .ok_or_else(|| NodeWalkError::bad_id(id, &object))? - .type_data() - ); - } - } else { - // The root node should not be a REPT one - if matches!(node.type_data(), NodeType::Rept(..)) { - print!(""); - error!("REPT-type root file stack node"); - } - } - print!("{}", node.type_data()); - Ok(()) - }) - .and_then(|()| -> Result<_, NodeWalkError> { - print!("({line_no})"); - Ok(()) - }) + object.walk_nodes::(id, line_no, &mut |node: &Node, line_no: u32| { + if node.parent().is_some() { + print!(" -> "); + } else if node.is_rept() { + // The root node should not be a REPT one + error!("REPT-type root file stack node"); + } + + // REPT nodes are prefixed by their parent's name + if let Some(non_rept) = rept_parent_non_rept(&object, node) { + print!("{}", non_rept.type_data()); + } + + print!("{}({})", node.type_data(), line_no); + if *node.is_quiet() { + print!("?"); + } + Ok(()) + }) }}; } @@ -211,16 +215,23 @@ fn work(args: &Args) -> Result<(), MainError> { // Print header - setup!(bold); - print!( - "{}", - Path::new(&args.path).file_name().unwrap().to_string_lossy() - ); - reset!(); - if args.header.get(HeaderFeatures::SIZE) { - let len = file.get_ref().metadata().unwrap().len(); - print!(" [{len} byte{}]", plural!(len, "s")); + match file { + Either::Left(_) => { + setup!(bold); + print!(""); + reset!(); + } + Either::Right(reader) => { + setup!(bold); + print!("{}", args.path); + reset!(); + if args.header.get(HeaderFeatures::SIZE) { + let len = reader.get_ref().metadata().unwrap().len(); + print!(" [{len} byte{}]", plural!(len, "s")); + } + } } + println!( ": RGBDS object v{} revision {}", object.version() as char,