From 580c9b6ea4963fef2bb7c85567083a1eb13f218a Mon Sep 17 00:00:00 2001 From: Filipe Rodrigues Date: Wed, 28 Oct 2020 17:45:32 +0000 Subject: [PATCH] Moved `Funcs` into it's own module. Moved all known functions into `Func`. --- dcb/src/game/exe/func.rs | 146 ++++----------------------------- dcb/src/game/exe/func/funcs.rs | 130 +++++++++++++++++++++++++++++ 2 files changed, 144 insertions(+), 132 deletions(-) create mode 100644 dcb/src/game/exe/func/funcs.rs diff --git a/dcb/src/game/exe/func.rs b/dcb/src/game/exe/func.rs index bc058d5..0c464c6 100644 --- a/dcb/src/game/exe/func.rs +++ b/dcb/src/game/exe/func.rs @@ -1,25 +1,17 @@ //! Executable functions // Modules +pub mod funcs; pub mod iter; // Exports +pub use funcs::Funcs; pub use iter::WithInstructionsIter; +use maplit::hashmap; // Imports -use crate::{ - game::exe::{ - instruction::{Directive, Register, SimpleInstruction}, - Instruction, Pos, - }, - util::merge_iter::MergeSortedIter, -}; -use maplit::hashmap; -use std::{ - collections::{BTreeSet, HashMap}, - iter::FromIterator, - vec, -}; +use crate::game::exe::Pos; +use std::collections::HashMap; /// A function within the executable #[derive(Clone, Debug)] @@ -66,67 +58,11 @@ impl> Ord for Func { } } -/// A sorted list of functions by their start address. -pub struct Funcs>(Vec>); - -impl> FromIterator> for Funcs { - fn from_iter>>(iter: T) -> Self { - Self(iter.into_iter().collect()) - } -} - -impl> Funcs { - /// Merges two function lists, discarding any duplicates - /// from `other`. - #[must_use] - pub fn merge(self, other: Self) -> MergeSortedIter, vec::IntoIter>, vec::IntoIter>> { - MergeSortedIter::new(self.0.into_iter(), other.0.into_iter()) - } - - /// Adapts an instruction iterator to extract the current function - pub fn with_instructions<'a, I: Iterator>(&'a self, instructions: I) -> WithInstructionsIter<'a, S, I> { - WithInstructionsIter::new(instructions, self) - } - - /// Retrieves a function with start address `pos` - #[must_use] - pub fn get(&self, pos: Pos) -> Option<&Func> { - // Note: As we're sorted, we can binary search - self.0 - .binary_search_by(|func| func.start_pos.cmp(&pos)) - .ok() - .and_then(|idx| self.0.get(idx)) - } -} - -#[allow(clippy::use_self)] // We're not using `Funcs`, but `Funcs` -impl + Into> Funcs { - /// Converts all strings to `String`. - #[must_use] - pub fn into_string(self) -> Funcs { - Funcs( - self.0 - .into_iter() - .map(|func| Func { - name: func.name.into(), - signature: func.signature.into(), - desc: func.desc.into(), - comments: func.comments.into_iter().map(|(pos, comment)| (pos, comment.into())).collect(), - start_pos: func.start_pos, - end_pos: func.end_pos, - }) - .collect(), - ) - } -} - - -impl Funcs<&'static str> { - /// Returns all known functions - #[must_use] - pub fn known() -> Self { - let mut functions = vec![ - Func { +impl Func<&'static str> { + /// Returns an iterator of all known functions + pub fn known() -> impl Iterator { + std::array::IntoIter::new([ + Self { name: "InitHeap", signature: "void(int* addr, unsigned int size)", desc: "Calls A(0x39)", @@ -134,7 +70,7 @@ impl Funcs<&'static str> { start_pos: Pos(0x8006a734), end_pos: Pos(0x8006a744), }, - Func { + Self { name: "start", signature: "void(void)", desc: "Executable start", @@ -150,7 +86,7 @@ impl Funcs<&'static str> { start_pos: Pos(0x80056270), end_pos: Pos(0x80056330), }, - Func { + Self { name: "func_1025", signature: "void(int*)", desc: "", @@ -161,7 +97,7 @@ impl Funcs<&'static str> { start_pos: Pos(0x80013e4c), end_pos: Pos(0x80013f04), }, - Func { + Self { name: "func_446", signature: "int(int)", desc: "", @@ -169,60 +105,6 @@ impl Funcs<&'static str> { start_pos: Pos(0x80069124), end_pos: Pos(0x80069150), }, - ]; - - functions.sort_by(|lhs, rhs| lhs.start_pos.cmp(&rhs.start_pos)); - Self(functions) - } -} - -impl Funcs { - /// Creates a new list of functions from an iterator over instructions - #[must_use] - pub fn from_instructions<'a>(instructions: impl Iterator + Clone) -> Self { - // Get all instruction offsets present, ignoring directives. - let offsets: BTreeSet = instructions - .clone() - .filter_map(|(pos, instruction)| match instruction { - Instruction::Directive(_) => None, - _ => Some(pos), - }) - .collect(); - - // Get all returns - let returns: BTreeSet = instructions - .clone() - .filter_map(|(pos, instruction)| match instruction { - Instruction::Simple(SimpleInstruction::Jr { rs: Register::Ra }) => Some(pos), - _ => None, - }) - .collect(); - - // Now get every function entrance from jumps and `dw`s. - let function_entrances: BTreeSet = instructions - .filter_map(|(_, instruction)| match instruction { - Instruction::Simple(SimpleInstruction::Jal { target }) => Some(*target), - Instruction::Directive(Directive::Dw(target) | Directive::DwRepeated { value: target, .. }) => Some(Pos(*target)), - _ => None, - }) - .filter(|target| (Instruction::CODE_START..Instruction::CODE_END).contains(target) && offsets.contains(target)) - .collect(); - - // Now combine the function entrances and exits. - // Note: functions will be sorted, as - let functions = function_entrances - .iter() - .zip(0..) - .map(|(&target, idx)| Func { - name: format!("func_{idx}"), - signature: "".to_string(), - desc: "".to_string(), - comments: hashmap![], - start_pos: target, - end_pos: returns.range(target..).next().copied().unwrap_or(Pos(0xFFFFFFFF)), - }) - .collect(); - - Self(functions) + ]) } } diff --git a/dcb/src/game/exe/func/funcs.rs b/dcb/src/game/exe/func/funcs.rs new file mode 100644 index 0000000..3dfcc49 --- /dev/null +++ b/dcb/src/game/exe/func/funcs.rs @@ -0,0 +1,130 @@ +//! Function lists + +// Imports +use super::{Func, WithInstructionsIter}; +use crate::{ + game::exe::{ + instruction::{Directive, Register, SimpleInstruction}, + Instruction, Pos, + }, + util::merge_iter::MergeSortedIter, +}; +use maplit::hashmap; +use std::{collections::BTreeSet, iter::FromIterator, vec}; + +/// A sorted list of functions by their start address. +pub struct Funcs>(Vec>); + +impl> FromIterator> for Funcs { + fn from_iter>>(iter: T) -> Self { + Self(iter.into_iter().collect()) + } +} + +impl> Funcs { + /// Merges two function lists, discarding any duplicates + /// from `other`. + #[must_use] + pub fn merge(self, other: Self) -> MergeSortedIter, vec::IntoIter>, vec::IntoIter>> { + MergeSortedIter::new(self.0.into_iter(), other.0.into_iter()) + } + + /// Adapts an instruction iterator to extract the current function + pub fn with_instructions<'a, I: Iterator>(&'a self, instructions: I) -> WithInstructionsIter<'a, S, I> { + WithInstructionsIter::new(instructions, self) + } + + /// Retrieves a function with start address `pos` + #[must_use] + pub fn get(&self, pos: Pos) -> Option<&Func> { + // Note: As we're sorted, we can binary search + self.0 + .binary_search_by(|func| func.start_pos.cmp(&pos)) + .ok() + .and_then(|idx| self.0.get(idx)) + } +} + +#[allow(clippy::use_self)] // We're not using `Funcs`, but `Funcs` +impl + Into> Funcs { + /// Converts all strings to `String`. + #[must_use] + pub fn into_string(self) -> Funcs { + Funcs( + self.0 + .into_iter() + .map(|func| Func { + name: func.name.into(), + signature: func.signature.into(), + desc: func.desc.into(), + comments: func.comments.into_iter().map(|(pos, comment)| (pos, comment.into())).collect(), + start_pos: func.start_pos, + end_pos: func.end_pos, + }) + .collect(), + ) + } +} + + +impl Funcs<&'static str> { + /// Returns all known functions + #[must_use] + pub fn known() -> Self { + let mut functions: Vec<_> = Func::known().collect(); + + functions.sort_by(|lhs, rhs| lhs.start_pos.cmp(&rhs.start_pos)); + Self(functions) + } +} + +impl Funcs { + /// Creates a new list of functions from an iterator over instructions + #[must_use] + pub fn from_instructions<'a>(instructions: impl Iterator + Clone) -> Self { + // Get all instruction offsets present, ignoring directives. + let offsets: BTreeSet = instructions + .clone() + .filter_map(|(pos, instruction)| match instruction { + Instruction::Directive(_) => None, + _ => Some(pos), + }) + .collect(); + + // Get all returns + let returns: BTreeSet = instructions + .clone() + .filter_map(|(pos, instruction)| match instruction { + Instruction::Simple(SimpleInstruction::Jr { rs: Register::Ra }) => Some(pos), + _ => None, + }) + .collect(); + + // Now get every function entrance from jumps and `dw`s. + let function_entrances: BTreeSet = instructions + .filter_map(|(_, instruction)| match instruction { + Instruction::Simple(SimpleInstruction::Jal { target }) => Some(*target), + Instruction::Directive(Directive::Dw(target) | Directive::DwRepeated { value: target, .. }) => Some(Pos(*target)), + _ => None, + }) + .filter(|target| (Instruction::CODE_START..Instruction::CODE_END).contains(target) && offsets.contains(target)) + .collect(); + + // Now combine the function entrances and exits. + // Note: functions will be sorted, as + let functions = function_entrances + .iter() + .zip(0..) + .map(|(&target, idx)| Func { + name: format!("func_{idx}"), + signature: "".to_string(), + desc: "".to_string(), + comments: hashmap![], + start_pos: target, + end_pos: returns.range(target..).next().copied().unwrap_or(Pos(0xFFFFFFFF)), + }) + .collect(); + + Self(functions) + } +}