Skip to content

Commit b6a29fa

Browse files
committed
Add split order attribute for manual reordering
Example in splits.txt: ``` file1.cpp: order:0 ... file2.cpp: order:1 ... file3.cpp: order:2 ... ``` This ensures that file2.cpp is always anchored in between 1 and 3 when resolving the final link order.
1 parent da6a514 commit b6a29fa

File tree

7 files changed

+55
-8
lines changed

7 files changed

+55
-8
lines changed

Cargo.lock

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ name = "decomp-toolkit"
33
description = "Yet another GameCube/Wii decompilation toolkit."
44
authors = ["Luke Street <[email protected]>"]
55
license = "MIT OR Apache-2.0"
6-
version = "0.9.3"
6+
version = "0.9.4"
77
edition = "2021"
88
publish = false
99
repository = "https://github.com/encounter/decomp-toolkit"

src/obj/mod.rs

+2
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@ pub struct ObjUnit {
4747
pub autogenerated: bool,
4848
/// MW `.comment` section version.
4949
pub comment_version: Option<u8>,
50+
/// Influences the order of this unit relative to other ordered units.
51+
pub order: Option<i32>,
5052
}
5153

5254
#[derive(Debug, Clone)]

src/util/config.rs

+19-2
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,16 @@ pub fn parse_u32(s: &str) -> Result<u32, ParseIntError> {
3434
}
3535
}
3636

37+
pub fn parse_i32(s: &str) -> Result<i32, ParseIntError> {
38+
if let Some(s) = s.strip_prefix("-0x").or_else(|| s.strip_prefix("-0X")) {
39+
i32::from_str_radix(s, 16).map(|v| -v)
40+
} else if let Some(s) = s.strip_prefix("0x").or_else(|| s.strip_prefix("0X")) {
41+
i32::from_str_radix(s, 16)
42+
} else {
43+
s.parse::<i32>()
44+
}
45+
}
46+
3747
pub fn apply_symbols_file<P>(path: P, obj: &mut ObjInfo) -> Result<Option<FileReadInfo>>
3848
where P: AsRef<Path> {
3949
Ok(if path.as_ref().is_file() {
@@ -428,6 +438,9 @@ where W: Write + ?Sized {
428438
if let Some(comment_version) = unit.comment_version {
429439
write!(w, " comment:{}", comment_version)?;
430440
}
441+
if let Some(order) = unit.order {
442+
write!(w, " order:{}", order)?;
443+
}
431444
writeln!(w)?;
432445
let mut split_iter = obj.sections.all_splits().peekable();
433446
while let Some((_section_index, section, addr, split)) = split_iter.next() {
@@ -475,6 +488,8 @@ struct SplitUnit {
475488
name: String,
476489
/// MW `.comment` section version
477490
comment_version: Option<u8>,
491+
/// Influences the order of this unit relative to other ordered units.
492+
order: Option<i32>,
478493
}
479494

480495
pub struct SectionDef {
@@ -515,12 +530,13 @@ fn parse_unit_line(captures: Captures) -> Result<SplitLine> {
515530
if name == "Sections" {
516531
return Ok(SplitLine::SectionsStart);
517532
}
518-
let mut unit = SplitUnit { name: name.to_string(), comment_version: None };
533+
let mut unit = SplitUnit { name: name.to_string(), comment_version: None, order: None };
519534

520535
for attr in captures["attrs"].split(' ').filter(|&s| !s.is_empty()) {
521536
if let Some((attr, value)) = attr.split_once(':') {
522537
match attr {
523538
"comment" => unit.comment_version = Some(u8::from_str(value)?),
539+
"order" => unit.order = Some(parse_i32(value)?),
524540
_ => bail!("Unknown unit attribute '{}'", attr),
525541
}
526542
} else {
@@ -631,12 +647,13 @@ where R: BufRead + ?Sized {
631647
match (&mut state, split_line) {
632648
(
633649
SplitState::None | SplitState::Unit(_) | SplitState::Sections(_),
634-
SplitLine::Unit(SplitUnit { name, comment_version }),
650+
SplitLine::Unit(SplitUnit { name, comment_version, order }),
635651
) => {
636652
obj.link_order.push(ObjUnit {
637653
name: name.clone(),
638654
autogenerated: false,
639655
comment_version,
656+
order,
640657
});
641658
state = SplitState::Unit(name);
642659
}

src/util/elf.rs

+1
Original file line numberDiff line numberDiff line change
@@ -316,6 +316,7 @@ where P: AsRef<Path> {
316316
name: file_name.clone(),
317317
autogenerated: false,
318318
comment_version: None,
319+
order: None,
319320
});
320321
}
321322

src/util/map.rs

+1
Original file line numberDiff line numberDiff line change
@@ -796,6 +796,7 @@ pub fn apply_map(result: &MapInfo, obj: &mut ObjInfo) -> Result<()> {
796796
name: unit.clone(),
797797
autogenerated: false,
798798
comment_version: Some(0),
799+
order: None,
799800
});
800801
}
801802

src/util/split.rs

+30-4
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use std::{
22
cmp::{max, min, Ordering},
3-
collections::{BTreeMap, HashMap, HashSet},
3+
collections::{btree_map, BTreeMap, HashMap, HashSet},
44
};
55

66
use anyhow::{anyhow, bail, ensure, Context, Result};
@@ -816,8 +816,8 @@ fn resolve_link_order(obj: &ObjInfo) -> Result<Vec<ObjUnit>> {
816816
#[allow(dead_code)]
817817
#[derive(Debug, Copy, Clone)]
818818
struct SplitEdge {
819-
from: u32,
820-
to: u32,
819+
from: i64,
820+
to: i64,
821821
}
822822

823823
let mut graph = Graph::<String, SplitEdge>::new();
@@ -852,11 +852,36 @@ fn resolve_link_order(obj: &ObjInfo) -> Result<Vec<ObjUnit>> {
852852
);
853853
let a_index = *unit_to_index_map.get(&a.unit).unwrap();
854854
let b_index = *unit_to_index_map.get(&b.unit).unwrap();
855-
graph.add_edge(a_index, b_index, SplitEdge { from: a_addr, to: b_addr });
855+
graph.add_edge(a_index, b_index, SplitEdge {
856+
from: a_addr as i64,
857+
to: b_addr as i64,
858+
});
856859
}
857860
}
858861
}
859862

863+
// Apply link order constraints provided by the user
864+
let mut ordered_units = BTreeMap::<i32, String>::new();
865+
for unit in &obj.link_order {
866+
if let Some(order) = unit.order {
867+
match ordered_units.entry(order) {
868+
btree_map::Entry::Vacant(entry) => {
869+
entry.insert(unit.name.clone());
870+
}
871+
btree_map::Entry::Occupied(entry) => {
872+
bail!("Duplicate order {} for units {} and {}", order, entry.get(), unit.name);
873+
}
874+
}
875+
}
876+
}
877+
let mut iter = ordered_units
878+
.into_iter()
879+
.filter_map(|(order, unit)| unit_to_index_map.get(&unit).map(|&index| (order, index)))
880+
.peekable();
881+
while let (Some((a_order, a_index)), Some((b_order, b_index))) = (iter.next(), iter.peek()) {
882+
graph.add_edge(a_index, *b_index, SplitEdge { from: a_order as i64, to: *b_order as i64 });
883+
}
884+
860885
// use petgraph::{
861886
// dot::{Config, Dot},
862887
// graph::EdgeReference,
@@ -886,6 +911,7 @@ fn resolve_link_order(obj: &ObjInfo) -> Result<Vec<ObjUnit>> {
886911
name: name.clone(),
887912
autogenerated: obj.is_unit_autogenerated(name),
888913
comment_version: None,
914+
order: None,
889915
}
890916
}
891917
})

0 commit comments

Comments
 (0)