Revised documentation for dcb-exe::exe.

This commit is contained in:
2021-01-09 14:19:44 +00:00
parent 9dd35a19a7
commit 21a0d7a4cb
4 changed files with 66 additions and 51 deletions

View File

@@ -1,7 +1,7 @@
//! Executable
//!
//! This module contains the executable portion of the game,
//! as well as tools to decompile and recompile it.
//! This module defines the `Exe` type, which encompasses the whole
//! executable part of the game file (`SLUS_013`).
// Modules
pub mod data;
@@ -15,7 +15,7 @@ pub mod pos;
// Exports
pub use data::{Data, DataTable, DataType};
pub use error::DeserializeError;
pub use func::Func;
pub use func::{Func, FuncTable};
pub use header::Header;
pub use pos::Pos;
@@ -24,39 +24,73 @@ use dcb_bytes::{ByteArray, Bytes};
use dcb_io::GameFile;
use std::io::{Read, Seek, Write};
use self::func::FuncTable;
/// The game executable
#[derive(PartialEq, Eq, Clone, Debug)]
//#[derive(serde::Serialize, serde::Deserialize)]
#[allow(clippy::as_conversions)] // `SIZE` always fits
pub struct Exe {
/// The executable header
pub header: Header,
header: Header,
/// All bytes within the exe
pub bytes: Box<[u8; Self::SIZE as usize]>,
/// All bytes within the executable
bytes: Box<[u8]>,
/// The data table.
pub data_table: DataTable,
data_table: DataTable,
/// The function table.
pub func_table: FuncTable,
func_table: FuncTable,
}
impl Exe {
/// End memory address of the executable
pub const END_MEM_ADDRESS: Pos = Pos(Self::START_MEM_ADDRESS.0 + Self::SIZE);
/// Size of the executable
/// Size of the executable section.
pub const SIZE: u32 = 0x68000;
/// Start address of the executable
/// Start address of the executable in the game file.
pub const START_ADDRESS: dcb_io::Data = dcb_io::Data::from_u64(0x58b9000);
/// Start memory address of the executable
pub const START_MEM_ADDRESS: Pos = Pos(0x80010000);
}
// Memory address
impl Exe {
/// End memory address of the executable.
pub const MEM_END_ADDRESS: Pos = Self::MEM_START_ADDRESS.add_offset_u32(Self::SIZE);
/// Memory range of the executable
pub const MEM_RANGE: std::ops::Range<Pos> = Self::MEM_START_ADDRESS..Self::MEM_END_ADDRESS;
/// Start memory address of the executable.
pub const MEM_START_ADDRESS: Pos = Pos(0x80010000);
}
impl Exe {
/// Deserializes the card table from a game file
/// Returns this executable's header
#[must_use]
pub const fn header(&self) -> &Header {
&self.header
}
/// Returns this executable's bytes
#[must_use]
pub const fn bytes(&self) -> &[u8] {
&*self.bytes
}
/// Creates an iterator over this executable
#[must_use]
pub const fn iter(&self) -> iter::Iter {
iter::Iter::new(self)
}
/// Returns a parsing iterator for all instructions
#[must_use]
pub const fn parse_iter(&self) -> inst::ParseIter {
inst::ParseIter::new(&*self.bytes, Self::MEM_START_ADDRESS)
}
/// Returns the instruction at `pos`
#[must_use]
pub fn get(&self, pos: Pos) -> Option<inst::Inst> {
inst::ParseIter::new(&*self.bytes, pos).next().map(|(_, inst)| inst)
}
}
impl Exe {
/// Deserializes the executable from a game file
pub fn deserialize<R: Read + Write + Seek>(file: &mut GameFile<R>) -> Result<Self, DeserializeError> {
// Seek to the table
file.seek(std::io::SeekFrom::Start(Self::START_ADDRESS.as_u64()))
@@ -78,7 +112,7 @@ impl Exe {
file.read_exact(bytes.as_mut()).map_err(DeserializeError::ReadData)?;
// Parse all instructions
let insts = inst::ParseIter::new(&*bytes, Self::START_MEM_ADDRESS);
let insts = inst::ParseIter::new(&*bytes, Self::MEM_START_ADDRESS);
// Parse all data and code
let known_data_table = DataTable::get_known().map_err(DeserializeError::KnownDataTable)?;
@@ -96,30 +130,4 @@ impl Exe {
func_table,
})
}
/*
/// Returns a parsing iterator for all instructions
#[must_use]
pub const fn parse_iter(&self) -> inst::ParseIter {
inst::ParseIter::new(&*self.bytes, Self::START_MEM_ADDRESS)
}
/// Returns this executable's bytes
#[must_use]
pub const fn bytes(&self) -> &[u8] {
&*self.bytes
}
*/
/// Creates an iterator over this executable
#[must_use]
pub const fn iter(&self) -> iter::Iter {
iter::Iter::new(self)
}
/// Returns the instruction at `pos`
#[must_use]
pub fn get(&self, pos: Pos) -> Option<inst::Inst> {
inst::ParseIter::new(&*self.bytes, pos).next().map(|(_, inst)| inst)
}
}

View File

@@ -21,7 +21,7 @@ impl<'a> Iter<'a> {
pub(crate) const fn new(exe: &'a Exe) -> Self {
Self {
exe,
cur_pos: Exe::START_MEM_ADDRESS,
cur_pos: Exe::MEM_START_ADDRESS,
}
}
}
@@ -43,7 +43,7 @@ impl<'a> Iterator for Iter<'a> {
fn next(&mut self) -> Option<Self::Item> {
// If we're at the end, return `None`
if self.cur_pos == Exe::END_MEM_ADDRESS {
if self.cur_pos == Exe::MEM_END_ADDRESS {
return None;
}

View File

@@ -16,10 +16,17 @@ use crate::Exe;
pub struct Pos(pub u32);
impl Pos {
/// Adds a `u32` offset to this position
// TODO: Remove once we can `impl const Add`
#[must_use]
pub(crate) const fn add_offset_u32(self, offset: u32) -> Self {
Self(self.0 + offset)
}
/// Returns the memory position of this position
#[must_use]
pub fn as_mem_idx(self) -> usize {
usize::try_from(self - Exe::START_MEM_ADDRESS).expect("Failed to compute index")
usize::try_from(self - Exe::MEM_START_ADDRESS).expect("Failed to compute index")
}
}

View File

@@ -95,7 +95,7 @@ fn main() -> Result<(), anyhow::Error> {
log::debug!("Deserializing executable");
let exe = dcb_exe::Exe::deserialize(&mut game_file).context("Unable to parse game executable")?;
println!("Header:\n{}", exe.header);
println!("Header:\n{}", exe.header());
for item in exe.iter() {
match item {
@@ -159,7 +159,7 @@ fn main() -> Result<(), anyhow::Error> {
// If it's standalone, print it by it's own
ExeItem::Inst(pos, inst) => {
println!("{}: {}", pos, inst.fmt_value(pos, &*exe.bytes));
println!("{}: {}", pos, inst.fmt_value(pos, exe.bytes()));
},
}