diff --git a/Cargo.lock b/Cargo.lock index 45cc61e..8062b91 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -367,7 +367,7 @@ checksum = "c2e06f9bce634a3c898eb1e5cb949ff63133cbb218af93cc9b38b31d6f3ea285" [[package]] name = "decomp-toolkit" -version = "0.9.3" +version = "0.9.4" dependencies = [ "anyhow", "ar", diff --git a/Cargo.toml b/Cargo.toml index c4b4655..dcfd8e8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,7 @@ name = "decomp-toolkit" description = "Yet another GameCube/Wii decompilation toolkit." authors = ["Luke Street "] license = "MIT OR Apache-2.0" -version = "0.9.3" +version = "0.9.4" edition = "2021" publish = false repository = "https://github.com/encounter/decomp-toolkit" diff --git a/src/obj/mod.rs b/src/obj/mod.rs index ad2349f..2013e16 100644 --- a/src/obj/mod.rs +++ b/src/obj/mod.rs @@ -47,6 +47,8 @@ pub struct ObjUnit { pub autogenerated: bool, /// MW `.comment` section version. pub comment_version: Option, + /// Influences the order of this unit relative to other ordered units. + pub order: Option, } #[derive(Debug, Clone)] diff --git a/src/util/config.rs b/src/util/config.rs index d3e78f4..3ea38ef 100644 --- a/src/util/config.rs +++ b/src/util/config.rs @@ -34,6 +34,16 @@ pub fn parse_u32(s: &str) -> Result { } } +pub fn parse_i32(s: &str) -> Result { + if let Some(s) = s.strip_prefix("-0x").or_else(|| s.strip_prefix("-0X")) { + i32::from_str_radix(s, 16).map(|v| -v) + } else if let Some(s) = s.strip_prefix("0x").or_else(|| s.strip_prefix("0X")) { + i32::from_str_radix(s, 16) + } else { + s.parse::() + } +} + pub fn apply_symbols_file

(path: P, obj: &mut ObjInfo) -> Result> where P: AsRef { Ok(if path.as_ref().is_file() { @@ -428,6 +438,9 @@ where W: Write + ?Sized { if let Some(comment_version) = unit.comment_version { write!(w, " comment:{}", comment_version)?; } + if let Some(order) = unit.order { + write!(w, " order:{}", order)?; + } writeln!(w)?; let mut split_iter = obj.sections.all_splits().peekable(); while let Some((_section_index, section, addr, split)) = split_iter.next() { @@ -475,6 +488,8 @@ struct SplitUnit { name: String, /// MW `.comment` section version comment_version: Option, + /// Influences the order of this unit relative to other ordered units. + order: Option, } pub struct SectionDef { @@ -515,12 +530,13 @@ fn parse_unit_line(captures: Captures) -> Result { if name == "Sections" { return Ok(SplitLine::SectionsStart); } - let mut unit = SplitUnit { name: name.to_string(), comment_version: None }; + let mut unit = SplitUnit { name: name.to_string(), comment_version: None, order: None }; for attr in captures["attrs"].split(' ').filter(|&s| !s.is_empty()) { if let Some((attr, value)) = attr.split_once(':') { match attr { "comment" => unit.comment_version = Some(u8::from_str(value)?), + "order" => unit.order = Some(parse_i32(value)?), _ => bail!("Unknown unit attribute '{}'", attr), } } else { @@ -631,12 +647,13 @@ where R: BufRead + ?Sized { match (&mut state, split_line) { ( SplitState::None | SplitState::Unit(_) | SplitState::Sections(_), - SplitLine::Unit(SplitUnit { name, comment_version }), + SplitLine::Unit(SplitUnit { name, comment_version, order }), ) => { obj.link_order.push(ObjUnit { name: name.clone(), autogenerated: false, comment_version, + order, }); state = SplitState::Unit(name); } diff --git a/src/util/elf.rs b/src/util/elf.rs index 62d470e..1ef2cd3 100644 --- a/src/util/elf.rs +++ b/src/util/elf.rs @@ -316,6 +316,7 @@ where P: AsRef { name: file_name.clone(), autogenerated: false, comment_version: None, + order: None, }); } diff --git a/src/util/map.rs b/src/util/map.rs index 9f26916..a2c2630 100644 --- a/src/util/map.rs +++ b/src/util/map.rs @@ -796,6 +796,7 @@ pub fn apply_map(result: &MapInfo, obj: &mut ObjInfo) -> Result<()> { name: unit.clone(), autogenerated: false, comment_version: Some(0), + order: None, }); } diff --git a/src/util/split.rs b/src/util/split.rs index c07ec47..3fca13b 100644 --- a/src/util/split.rs +++ b/src/util/split.rs @@ -1,6 +1,6 @@ use std::{ cmp::{max, min, Ordering}, - collections::{BTreeMap, HashMap, HashSet}, + collections::{btree_map, BTreeMap, HashMap, HashSet}, }; use anyhow::{anyhow, bail, ensure, Context, Result}; @@ -816,8 +816,8 @@ fn resolve_link_order(obj: &ObjInfo) -> Result> { #[allow(dead_code)] #[derive(Debug, Copy, Clone)] struct SplitEdge { - from: u32, - to: u32, + from: i64, + to: i64, } let mut graph = Graph::::new(); @@ -852,11 +852,36 @@ fn resolve_link_order(obj: &ObjInfo) -> Result> { ); let a_index = *unit_to_index_map.get(&a.unit).unwrap(); let b_index = *unit_to_index_map.get(&b.unit).unwrap(); - graph.add_edge(a_index, b_index, SplitEdge { from: a_addr, to: b_addr }); + graph.add_edge(a_index, b_index, SplitEdge { + from: a_addr as i64, + to: b_addr as i64, + }); } } } + // Apply link order constraints provided by the user + let mut ordered_units = BTreeMap::::new(); + for unit in &obj.link_order { + if let Some(order) = unit.order { + match ordered_units.entry(order) { + btree_map::Entry::Vacant(entry) => { + entry.insert(unit.name.clone()); + } + btree_map::Entry::Occupied(entry) => { + bail!("Duplicate order {} for units {} and {}", order, entry.get(), unit.name); + } + } + } + } + let mut iter = ordered_units + .into_iter() + .filter_map(|(order, unit)| unit_to_index_map.get(&unit).map(|&index| (order, index))) + .peekable(); + while let (Some((a_order, a_index)), Some((b_order, b_index))) = (iter.next(), iter.peek()) { + graph.add_edge(a_index, *b_index, SplitEdge { from: a_order as i64, to: *b_order as i64 }); + } + // use petgraph::{ // dot::{Config, Dot}, // graph::EdgeReference, @@ -886,6 +911,7 @@ fn resolve_link_order(obj: &ObjInfo) -> Result> { name: name.clone(), autogenerated: obj.is_unit_autogenerated(name), comment_version: None, + order: None, } } })