#![doc(html_root_url = "https://docs.rs/rgbds-obj/0.5.0")]
use std::convert::TryInto;
use std::error::Error;
use std::fmt::{self, Display, Formatter};
use std::io::{self, Read};
mod assertion;
pub use assertion::*;
mod fstack;
pub use fstack::*;
mod patch;
pub use patch::*;
mod rpn;
pub use rpn::*;
mod section;
pub use section::*;
mod symbol;
pub use symbol::*;
mod util;
use util::*;
#[derive(Debug)]
pub struct Object {
version: u8,
revision: u32,
fstack_nodes: Vec<Node>,
symbols: Vec<Symbol>,
sections: Vec<Section>,
assertions: Vec<Assertion>,
}
impl Object {
pub fn read_from(mut input: impl Read) -> Result<Self, io::Error> {
let mut magic = [0; 4];
input.read_exact(&mut magic)?;
if &magic[0..3] != b"RGB" || !magic[3].is_ascii_digit() {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
"This does not appear to be a valid RGBDS object",
));
}
let version = magic[3];
if version != b'9' {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
format!(
"Object file version {} is not supported (must be 9)",
version as char
),
));
}
let revision = read_u32le(&mut input)?;
if !(6..=13).contains(&revision) {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
format!(
"Object file {} revision {revision} is not supported (must be between 6 and 13)",
version as char
),
));
}
let sections_have_srcs = version > b'9' || (version == b'9' && revision >= 11);
let nb_symbols = read_u32le(&mut input)?.try_into().unwrap();
let nb_sections = read_u32le(&mut input)?.try_into().unwrap();
let nb_fstack_nodes = read_u32le(&mut input)?.try_into().unwrap();
let mut obj = Self {
version,
revision,
fstack_nodes: Vec::with_capacity(nb_fstack_nodes),
symbols: Vec::with_capacity(nb_symbols),
sections: Vec::with_capacity(nb_sections),
assertions: Vec::new(), };
for _ in 0..nb_fstack_nodes {
obj.fstack_nodes.push(Node::read_from(&mut input)?);
}
for _ in 0..nb_symbols {
obj.symbols.push(Symbol::read_from(&mut input)?);
}
for _ in 0..nb_sections {
obj.sections
.push(Section::read_from(&mut input, sections_have_srcs)?);
}
let nb_assertions = read_u32le(&mut input)?.try_into().unwrap();
obj.assertions.reserve_exact(nb_assertions);
for _ in 0..nb_assertions {
obj.assertions.push(Assertion::read_from(&mut input)?);
}
Ok(obj)
}
pub fn version(&self) -> u8 {
self.version
}
pub fn revision(&self) -> u32 {
self.revision
}
pub fn node(&self, id: u32) -> Option<&Node> {
let id: usize = id.try_into().unwrap();
if id < self.fstack_nodes.len() {
Some(&self.fstack_nodes[self.fstack_nodes.len() - 1 - id])
} else {
None
}
}
pub fn walk_nodes<E, F>(
&self,
id: u32,
line_no: u32,
callback: &mut F,
) -> Result<(), NodeWalkError<E>>
where
F: FnMut(&Node, u32) -> Result<(), NodeWalkError<E>>,
{
let node = self
.node(id)
.ok_or_else(|| NodeWalkError::bad_id(id, self))?;
if let Some((parent_id, parent_line_no)) = node.parent() {
self.walk_nodes(parent_id, parent_line_no, callback)?;
}
callback(node, line_no)
}
pub fn symbols(&self) -> &[Symbol] {
&self.symbols
}
pub fn sections(&self) -> &[Section] {
&self.sections
}
pub fn assertions(&self) -> &[Assertion] {
&self.assertions
}
}
#[derive(Debug)]
pub enum NodeWalkError<E> {
BadId(u32, usize),
Custom(E),
}
impl<E> NodeWalkError<E> {
pub fn bad_id(id: u32, object: &Object) -> Self {
Self::BadId(id, object.fstack_nodes.len())
}
}
impl<E: Display> Display for NodeWalkError<E> {
fn fmt(&self, fmt: &mut Formatter) -> Result<(), fmt::Error> {
use NodeWalkError::*;
match self {
BadId(id, len) => write!(fmt, "Requested node #{id} of {len}"),
Custom(err) => err.fmt(fmt),
}
}
}
impl<E: Error + 'static> Error for NodeWalkError<E> {
fn source(&self) -> Option<&(dyn Error + 'static)> {
use NodeWalkError::*;
if let Custom(err) = self {
Some(err)
} else {
None
}
}
}
impl<E> From<E> for NodeWalkError<E> {
fn from(inner: E) -> Self {
Self::Custom(inner)
}
}