mirror of
https://github.com/Zenithsiz/dcb.git
synced 2026-02-04 00:21:57 +00:00
Now generating a .dot graph of all 'blocks' of the msd.
This commit is contained in:
parent
37ba47e20f
commit
dd24925fd1
@ -20,6 +20,7 @@ anyhow = "1.0.38"
|
||||
|
||||
# Utility
|
||||
byteorder = "1.4.3"
|
||||
dbg_hex = {git = "https://github.com/Zenithsiz/dbg_hex"}
|
||||
itertools = "0.10.0"
|
||||
|
||||
# Strings
|
||||
|
||||
@ -9,7 +9,8 @@
|
||||
exact_size_is_empty,
|
||||
iter_advance_by,
|
||||
try_blocks,
|
||||
cow_is_borrowed
|
||||
cow_is_borrowed,
|
||||
map_first_last
|
||||
)]
|
||||
|
||||
// Modules
|
||||
@ -26,7 +27,9 @@ use std::{
|
||||
collections::{BTreeMap, HashMap},
|
||||
convert::TryInto,
|
||||
fs,
|
||||
io::Write,
|
||||
};
|
||||
use zutil::Void;
|
||||
|
||||
|
||||
fn main() -> Result<(), anyhow::Error> {
|
||||
@ -118,12 +121,99 @@ fn main() -> Result<(), anyhow::Error> {
|
||||
.map(|(idx, addr)| (addr, format!("jump_{idx}")));
|
||||
labels.extend(heuristic_labels);
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
struct Block {
|
||||
start: u32,
|
||||
end: u32,
|
||||
|
||||
calls: BTreeMap<u32, u32>,
|
||||
}
|
||||
|
||||
let blocks = labels
|
||||
.keys()
|
||||
.map(|&pos| {
|
||||
let (end, label_at_end, unconditional_jump) = commands
|
||||
.range(pos..)
|
||||
.tuple_windows()
|
||||
.find_map(|((&cur_pos, cur), (&next_pos, next))| {
|
||||
// If the first instruction is a jump, it's unconditional
|
||||
if pos == cur_pos {
|
||||
if let Command::Jump { addr, .. } = *cur {
|
||||
return Some((next_pos, labels.contains_key(&next_pos), Some((cur_pos, addr))));
|
||||
}
|
||||
}
|
||||
|
||||
// Else check if the next instruction is a label
|
||||
if labels.contains_key(&next_pos) {
|
||||
// Note: Here the unconditional must be `None` or we would have
|
||||
// returned on the previous iteration or above.
|
||||
return Some((next_pos, true, None));
|
||||
}
|
||||
|
||||
// Else check for a non-test + jump combo.
|
||||
match (cur, next) {
|
||||
// Ignore test-jumps
|
||||
(Command::Test { .. }, Command::Jump { .. }) => None,
|
||||
|
||||
// Else a non-test jump should be unconditional
|
||||
// Note: `unconditional_jump` should be false here always I believe, else it's dead code?
|
||||
(_, Command::Jump { .. }) => Some((next_pos + next.size() as u32, false, match *cur {
|
||||
Command::Jump { addr, .. } => Some((cur_pos, addr)),
|
||||
_ => None,
|
||||
})),
|
||||
_ => None,
|
||||
}
|
||||
})
|
||||
.unwrap_or_else(|| (commands.last_key_value().map_or(0, |(&pos, _)| pos), false, None));
|
||||
|
||||
let mut calls = commands
|
||||
.range(pos..end)
|
||||
.filter_map(|(&pos, command)| match *command {
|
||||
Command::Jump { addr, .. } => Some((pos, addr)),
|
||||
_ => None,
|
||||
})
|
||||
.collect::<BTreeMap<_, _>>();
|
||||
|
||||
// Check if we need to add any extra calls
|
||||
match (label_at_end, unconditional_jump) {
|
||||
// If we ended on a label without diverging, add a call the label
|
||||
(true, None) => calls.insert(end, end).void(),
|
||||
|
||||
// If we ended by diverging, insert a call to it.
|
||||
(_, Some((pos, addr))) => calls.insert(pos, addr).void(),
|
||||
|
||||
// Else no extra calls
|
||||
_ => (),
|
||||
}
|
||||
|
||||
(pos, Block { start: pos, end, calls })
|
||||
})
|
||||
.collect::<BTreeMap<u32, Block>>();
|
||||
|
||||
let dot_file_path = format!("{}.dot", cli_data.input_file.display());
|
||||
let mut dot_file = std::fs::File::create(dot_file_path).context("Unable to create dot file")?;
|
||||
|
||||
writeln!(dot_file, "digraph \"G\" {{").context("Unable to write to dot file")?;
|
||||
for block in blocks.values() {
|
||||
let label = labels.get(&block.start).expect("Block had no label");
|
||||
writeln!(dot_file, "\t{label};").context("Unable to write to dot file")?;
|
||||
for call_pos in block.calls.values().unique() {
|
||||
let call_label = match labels.get(call_pos) {
|
||||
Some(label) => label.to_owned(),
|
||||
None => format!("\"{call_pos:#x}\""),
|
||||
};
|
||||
writeln!(dot_file, "\t{label} -> {call_label};").context("Unable to write to dot file")?;
|
||||
}
|
||||
}
|
||||
writeln!(dot_file, "}}").context("Unable to write to dot file")?;
|
||||
|
||||
let mut state = State::Start;
|
||||
for (pos, command) in commands {
|
||||
if let Some(label) = labels.get(&pos) {
|
||||
println!("{label}:");
|
||||
};
|
||||
|
||||
|
||||
print!("{pos:#010x}: ");
|
||||
|
||||
/*
|
||||
|
||||
@ -1,7 +1,9 @@
|
||||
---
|
||||
0x00000100: "intro"
|
||||
0x000002ac: "city_start"
|
||||
0x0000b090: "arena_1_closed"
|
||||
0x0000b090: "arenas_check_open"
|
||||
0x0000b0cc: "arena_1_check_open"
|
||||
0x0000b0e0: "arena_1_closed"
|
||||
0x0000b144: "arena_1_enter_menu"
|
||||
0x0000b1bc: "arena_1_enter_menu_no"
|
||||
0x0000b1c4: "arena_1_enter_menu_yes"
|
||||
@ -28,7 +30,8 @@
|
||||
0x0000cc00: "arena_1_leave_menu"
|
||||
0x0000cc74: "arena_1_leave_menu_no"
|
||||
0x0000cc7c: "arena_1_leave_menu_yes"
|
||||
0x0000cc90: "arena_2_closed"
|
||||
0x0000cc90: "arena_2_check_open"
|
||||
0x0000cca4: "arena_2_closed"
|
||||
0x0000cd08: "arena_2_enter_menu"
|
||||
0x0000cd80: "arena_2_enter_menu_no"
|
||||
0x0000cd88: "arena_2_enter_menu_yes"
|
||||
@ -63,7 +66,8 @@
|
||||
0x0000e35c: "arena_2_leave_menu"
|
||||
0x0000e3d0: "arena_2_leave_menu_no"
|
||||
0x0000e3d8: "arena_2_leave_menu_yes"
|
||||
0x0000e3ec: "arena_3_closed"
|
||||
0x0000e3ec: "arena_3_check_open"
|
||||
0x0000e400: "arena_3_closed"
|
||||
0x0000e464: "arena_3_enter_menu"
|
||||
0x0000e4dc: "arena_3_enter_menu_no"
|
||||
0x0000e4e4: "arena_3_enter_menu_yes"
|
||||
@ -97,6 +101,7 @@
|
||||
0x0000fb60: "arena_3_leave_menu"
|
||||
0x0000fbd4: "arena_3_leave_menu_no"
|
||||
0x0000fbdc: "arena_3_leave_menu_yes"
|
||||
0x0000fc04: "arena_4_check_open"
|
||||
0x0000fbf0: "arena_4_closed"
|
||||
0x0000fc68: "arena_4_enter_menu"
|
||||
0x0000fce0: "arena_4_enter_menu_no"
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
---
|
||||
0x0000a9e8: "arena_1_check_open"
|
||||
0x0000a9fc: "arena_1_closed"
|
||||
0x0000aa60: "arena_1_enter_menu"
|
||||
0x0000aaec: "arena_1_enter_menu_no"
|
||||
@ -35,7 +36,9 @@
|
||||
0x0000cb5c: "arena_1_leave_menu"
|
||||
0x0000cbd0: "arena_1_leave_menu_no"
|
||||
0x0000cbd8: "arena_1_leave_menu_yes"
|
||||
0x0000cbec: "arena_2_closed"
|
||||
0x0000cbec: "arenas_2_3_4_5_check_open"
|
||||
0x0000cc28: "arena_2_check_open"
|
||||
0x0000cc3c: "arena_2_closed"
|
||||
0x0000cca0: "arena_2_enter_menu"
|
||||
0x0000cd18: "arena_2_enter_menu_no"
|
||||
0x0000cd20: "arena_2_enter_menu_yes"
|
||||
|
||||
@ -22,9 +22,9 @@
|
||||
0x0018: "city00_rosemon_favor_dark_city_accepted"
|
||||
0x0019: "city00_rosemon_favor_dark_city_finished"
|
||||
0x001a: "city00_battle_cafe_tai_won"
|
||||
0x001b: "city00_arena_2_unlocked"
|
||||
0x001c: "city00_arena_3_unlocked"
|
||||
0x001d: "city00_arena_4_unlocked"
|
||||
0x001b: "city00_arena_2_open"
|
||||
0x001c: "city00_arena_3_open"
|
||||
0x001d: "city00_arena_4_open"
|
||||
0x001e: "done_intro"
|
||||
0x001f: "city00_battle_cafe_rosemon_won"
|
||||
0x0020: "city01_arena_1_won"
|
||||
@ -42,7 +42,7 @@
|
||||
0x002d: "city01_arena_4_magnamon_introduction_done"
|
||||
0x002e: "city01_arena_5_imperialdramon_introduction_done"
|
||||
0x0033: "city00_talked_to_babamon_early"
|
||||
0x0036: "city00_arena_open"
|
||||
0x0036: "city00_arena_1_open"
|
||||
0x008c: "rosemon_favor_dark_city_battled"
|
||||
0x010c: "arena_won_first"
|
||||
0x010d: "arena_won_second"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user