diff --git a/dcb-tools/src/decompiler/main.rs b/dcb-tools/src/decompiler/main.rs index 2a31dd5..800e691 100644 --- a/dcb-tools/src/decompiler/main.rs +++ b/dcb-tools/src/decompiler/main.rs @@ -250,69 +250,56 @@ fn main() -> Result<(), anyhow::Error> { ) | Instruction::Pseudo( PseudoInstruction::B { target } | PseudoInstruction::Beqz { target, .. } | PseudoInstruction::Bnez { target, .. }, - ) => { - if let Some((target, prefix)) = functions - .get(*target) - .map(|func| (&func.name, "")) - .or_else(|| cur_func.and_then(|func| func.labels.get(target).map(|label| (label, ".")))) - { - // TODO: Improve solution, removing the target like this isn't - // a good way of going about it. - let instruction = instruction.to_string(); - #[allow(clippy::indexing_slicing)] // This can't panic, it's index is `..{0..len}`. - let instruction = &instruction[..instruction.rfind(' ').unwrap_or_else(|| instruction.len())]; - print!("{instruction} {prefix}{target}"); - } else { - print!("{instruction}"); - } + ) => match functions + .get(*target) + .map(|func| (&func.name, "")) + .or_else(|| cur_func.and_then(|func| func.labels.get(target).map(|label| (label, ".")))) + { + Some((target, prefix)) => print!("{} {prefix}{target}", strip_last_arg(instruction)), + None => print!("{instruction}"), }, - _ => print!("{instruction}"), - } - // Check if we should have any comments with this instruction - - match instruction { // Comment loading address, loading and writing values of string and data // TODO: Maybe check loads / writes to halfway between // the strings / data. Instruction::Pseudo( - PseudoInstruction::La { target: offset, .. } | - PseudoInstruction::Li32 { imm: offset, .. } | - PseudoInstruction::LbImm { offset, .. } | - PseudoInstruction::LbuImm { offset, .. } | - PseudoInstruction::LhImm { offset, .. } | - PseudoInstruction::LhuImm { offset, .. } | - PseudoInstruction::LwlImm { offset, .. } | - PseudoInstruction::LwImm { offset, .. } | - PseudoInstruction::LwrImm { offset, .. } | - PseudoInstruction::SbImm { offset, .. } | - PseudoInstruction::ShImm { offset, .. } | - PseudoInstruction::SwlImm { offset, .. } | - PseudoInstruction::SwImm { offset, .. } | - PseudoInstruction::SwrImm { offset, .. }, - ) => { - if let Some(string_idx) = strings_pos.get(Pos::ref_cast(offset)) { - print!(" # string_{string_idx}"); - } - if let Some(data_idx) = data_pos.get(Pos::ref_cast(offset)) { - print!(" # data_{data_idx}"); - } + PseudoInstruction::La { target, .. } | + PseudoInstruction::Li32 { imm: target, .. } | + PseudoInstruction::LbImm { offset: target, .. } | + PseudoInstruction::LbuImm { offset: target, .. } | + PseudoInstruction::LhImm { offset: target, .. } | + PseudoInstruction::LhuImm { offset: target, .. } | + PseudoInstruction::LwlImm { offset: target, .. } | + PseudoInstruction::LwImm { offset: target, .. } | + PseudoInstruction::LwrImm { offset: target, .. } | + PseudoInstruction::SbImm { offset: target, .. } | + PseudoInstruction::ShImm { offset: target, .. } | + PseudoInstruction::SwlImm { offset: target, .. } | + PseudoInstruction::SwImm { offset: target, .. } | + PseudoInstruction::SwrImm { offset: target, .. }, + ) => match strings_pos + .get(Pos::ref_cast(target)) + .map(|idx| (idx, "string_")) + .or_else(|| data_pos.get(Pos::ref_cast(target)).map(|idx| (idx, "data_"))) + { + Some((target, prefix)) => print!("{} {prefix}{target}", strip_last_arg(instruction)), + None => print!("{instruction}"), }, - // Comment `dw`s with both function and data - Instruction::Directive(Directive::Dw(offset) | Directive::DwRepeated { value: offset, .. }) => { - if let Some(func) = functions.get(Pos(*offset)) { - print!(" # {}", func.name); - } - if let Some(string_idx) = strings_pos.get(Pos::ref_cast(offset)) { - print!(" # string_{string_idx}"); - } - if let Some(data_idx) = data_pos.get(Pos::ref_cast(offset)) { - print!(" # data_{data_idx}"); - } - }, + _ => print!("{instruction}"), + } - _ => (), + // Comment any `dw` instructions that are function, data or string pointers + if let Instruction::Directive(Directive::Dw(target) | Directive::DwRepeated { value: target, .. }) = instruction { + if let Some(func) = functions.get(Pos(*target)) { + print!(" # {}", func.name); + } + if let Some(string_idx) = strings_pos.get(Pos::ref_cast(target)) { + print!(" # string_{string_idx}"); + } + if let Some(data_idx) = data_pos.get(Pos::ref_cast(target)) { + print!(" # data_{data_idx}"); + } } // Append any comments in this line @@ -336,3 +323,13 @@ fn main() -> Result<(), anyhow::Error> { Ok(()) } + + +/// Helper function to extract the last argument from an instruction +// TODO: Use something better than this +fn strip_last_arg(instruction: &Instruction) -> String { + let mut instruction: String = instruction.to_string(); + // Note: This can't panic + instruction.truncate(instruction.rfind(' ').unwrap_or(0)); + instruction +} diff --git a/dcb/src/game/exe/func.rs b/dcb/src/game/exe/func.rs index 263fafa..3ec6571 100644 --- a/dcb/src/game/exe/func.rs +++ b/dcb/src/game/exe/func.rs @@ -94,7 +94,7 @@ impl Func<&'static str> { Pos(0x80056280) => "zero_loop", }, start_pos: Pos(0x80056270), - end_pos: Pos(0x80056384), + end_pos: Pos(0x80056388), }, ]) } diff --git a/dcb/src/game/exe/instruction/directive.rs b/dcb/src/game/exe/instruction/directive.rs index 464a900..070e77b 100644 --- a/dcb/src/game/exe/instruction/directive.rs +++ b/dcb/src/game/exe/instruction/directive.rs @@ -1,7 +1,7 @@ //! Directives // Imports -use super::{FromRawIter, Raw}; +use super::{FromRawIter, Instruction, Raw}; use crate::game::exe::Pos; use ascii::{AsciiChar, AsciiStr, AsciiString}; use AsciiChar::Null; @@ -29,6 +29,41 @@ pub enum Directive { Ascii(AsciiString), } +impl Directive { + /// Decodes a `dw` instruction + pub fn decode_dw(first_raw: Raw, iter: &mut (impl Iterator + Clone)) -> Self { + let mut times_repeated = 0; + + // Keep getting values until either eof or a different one + loop { + let mut cur_iter = iter.clone(); + match cur_iter.next().map(|next_raw| next_raw.repr == first_raw.repr) { + // If we got a different value, keep fetching values until they're different + Some(true) => { + *iter = cur_iter; + times_repeated += 1; + }, + + // If we didn't get it or we got a different value, exit + // Note: No need t update the iterator, as it either returned `None` or + // a different raw. + None | Some(false) => match times_repeated { + // If the value didn't repeat, use a single `dw` + 0 => break Self::Dw(first_raw.repr), + + // Else return the table + _ => { + break Self::DwRepeated { + value: first_raw.repr, + len: times_repeated + 1, + } + }, + }, + } + } + } +} + /// Helper function to check if a string has null and if everything after the first /// null is also null (or if there were no nulls). @@ -52,6 +87,12 @@ impl FromRawIter for Directive { // Get the first raw let raw = iter.next()?; + // If we're past all the code, there are no more strings, + // so just decode a `dw`. + if raw.pos >= Instruction::CODE_END { + return Some((raw.pos, Self::decode_dw(raw, iter))); + } + // Try to get an ascii string from the raw and check for nulls match AsciiString::from_ascii(raw.repr.to_ne_bytes()).map(check_nulls) { // If we got a string with at least 1 non-null, but @@ -101,37 +142,7 @@ impl FromRawIter for Directive { // Else if it was full null, non-uniformly null or non-ascii, // try to get a dw table - _ => { - let mut times_repeated = 0; - - // Keep getting values until either eof or a different one - loop { - let mut cur_iter = iter.clone(); - match cur_iter.next().map(|next_raw| next_raw.repr == raw.repr) { - // If we got a different value, keep fetching values until they're different - Some(true) => { - *iter = cur_iter; - times_repeated += 1; - }, - - // If we didn't get it or we got a different value, exit - // Note: No need t update the iterator, as it either returned `None` or - // a different raw. - None | Some(false) => match times_repeated { - // If the value didn't repeat, use a single `dw` - 0 => break Some((raw.pos, Self::Dw(raw.repr))), - - // Else return the table - _ => { - break Some((raw.pos, Self::DwRepeated { - value: raw.repr, - len: times_repeated + 1, - })) - }, - }, - } - } - }, + _ => Some((raw.pos, Self::decode_dw(raw, iter))), } } }