Skip to content
Open
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 26 additions & 4 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 4 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -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 <[email protected]>", "Rangi42"]
edition = "2018"
description = "A command-line program to print out RGBDS object files."
Expand All @@ -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"

Expand Down
10 changes: 8 additions & 2 deletions rgbobj.1
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
.\"
.\" SPDX-License-Identifier: MIT
.\"
.Dd May 2, 2021
.Dd Aug 17, 2025
.Dt RGBOBJ 1
.Os
.Sh NAME
Expand All @@ -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
Expand Down Expand Up @@ -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 ,
Expand Down
8 changes: 4 additions & 4 deletions src/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -302,7 +302,7 @@ fn get_cmd() -> Command<'static> {
.arg(arg!(-s --section <features> "Keyword list of what to display about sections").value_parser(value_parser!(SectionFeatures)).default_value(SectionFeatures::DEFAULT).required(false))
.arg(arg!(-p --patch <features> "Keyword list of what to display about patches").value_parser(value_parser!(PatchFeatures)).default_value(PatchFeatures::DEFAULT).required(false))
.arg(arg!(-a --assertion <features> "Keyword list of what to display about assertions").value_parser(value_parser!(AssertionFeatures)).default_value(AssertionFeatures::DEFAULT).required(false))
.arg(arg!(<file> "Path to the object file to inspect"))
.arg(arg!(<file> "Path to the object file to inspect (use '-' for standard input)"))
}

/// Parse command-line arguments.
Expand Down Expand Up @@ -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,
Expand All @@ -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,
Expand Down
103 changes: 60 additions & 43 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -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, NodeType, 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};

Expand Down Expand Up @@ -50,6 +49,26 @@ macro_rules! plural {
};
}

fn is_rept(node: &Node) -> bool {
matches!(node.type_data(), NodeType::Rept(..))
}

fn rept_parent_non_rept<'a>(object: &'a Object, node: &'a Node) -> Option<&'a Node> {
if is_rept(node) {
node.parent().and_then(|(parent_id, _)| {
object.node(parent_id).and_then(|parent| {
if is_rept(parent) {
rept_parent_non_rept(object, parent)
} else {
Some(parent)
}
})
})
} else {
None
}
}

fn work(args: &Args) -> Result<(), MainError> {
macro_rules! error {
($($args:tt)+) => {
Expand Down Expand Up @@ -90,7 +109,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);

Expand Down Expand Up @@ -129,34 +148,25 @@ fn work(args: &Args) -> Result<(), MainError> {
($source:expr) => {{
let (id, line_no) = $source;
// TODO: wrap the node stack nicely
object
.walk_nodes::<Infallible, _>(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>");
error!("REPT-type root file stack node");
}
}
print!("{}", node.type_data());
Ok(())
})
.and_then(|()| -> Result<_, NodeWalkError<Infallible>> {
print!("({line_no})");
Ok(())
})
object.walk_nodes::<Infallible, _>(id, line_no, &mut |node: &Node, line_no: u32| {
if node.parent().is_some() {
print!(" -> ");
} else if is_rept(node) {
// 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(())
})
}};
}

Expand Down Expand Up @@ -211,16 +221,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!("<stdin>");
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,
Expand Down