Executable size and start positions are now dynamic.

Function labels are no longer printed at the start.
This commit is contained in:
Filipe Rodrigues 2021-01-12 03:20:13 +00:00
parent c443402400
commit d9bc90882b
9 changed files with 41 additions and 69 deletions

View File

@ -22,7 +22,10 @@ pub use pos::Pos;
// Imports
use dcb_bytes::{ByteArray, Bytes};
use dcb_io::GameFile;
use std::io::{Read, Seek, Write};
use std::{
convert::TryFrom,
io::{Read, Seek, Write},
};
/// The game executable
#[derive(PartialEq, Eq, Clone, Debug)]
@ -41,22 +44,10 @@ pub struct Exe {
}
impl Exe {
/// Size of the executable section.
pub const SIZE: u32 = 0x68000;
/// Start address of the executable in the game file.
pub const START_ADDRESS: dcb_io::Data = dcb_io::Data::from_u64(0x58b9000);
}
// 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 {
/// Returns this executable's header
#[must_use]
@ -91,7 +82,7 @@ impl Exe {
/// 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)
inst::ParseIter::new(&*self.bytes, self.header.start_pos)
}
}
@ -107,14 +98,8 @@ impl Exe {
file.read_exact(&mut header_bytes).map_err(DeserializeError::ReadHeader)?;
let header = Header::from_bytes(&header_bytes).map_err(DeserializeError::ParseHeader)?;
// Make sure the header size is the one we expect
if header.size != Self::SIZE {
return Err(DeserializeError::WrongDataSize { header: Box::new(header) });
}
// Read all of the bytes
#[allow(clippy::as_conversions)] // `SIZE` always fits
let mut bytes = Box::new([0u8; Self::SIZE as usize]);
let mut bytes = vec![0u8; usize::try_from(header.size).expect("Len didn't fit into `usize`")].into_boxed_slice();
file.read_exact(bytes.as_mut()).map_err(DeserializeError::ReadData)?;
// Read the known data and func table
@ -122,7 +107,7 @@ impl Exe {
let known_func_table = FuncTable::get_known().map_err(DeserializeError::KnownFuncTable)?;
// Parse all instructions
let insts = inst::ParseIter::new(&*bytes, Self::MEM_START_ADDRESS);
let insts = inst::ParseIter::new(&*bytes, header.start_pos);
// Then parse all heuristic tables
let heuristics_data_table = DataTable::search_instructions(insts.clone());

View File

@ -1,7 +1,7 @@
//! Errors
// Imports
use super::{data, func, header, Exe, Header};
use super::{data, func, header};
/// Error type for [`Table::deserialize`]
#[derive(Debug, thiserror::Error)]
@ -18,13 +18,6 @@ pub enum DeserializeError {
#[error("Unable to parse header")]
ParseHeader(#[source] header::FromBytesError),
/// Data had wrong size
#[error("Wrong data size, expected {}, found {}", Exe::SIZE, header.size)]
WrongDataSize {
/// The read header
header: Box<Header>,
},
/// Unable to read data
#[error("Unable to read data")]
ReadData(#[source] std::io::Error),

View File

@ -146,7 +146,7 @@ impl FuncTable {
Inst::Basic(basic::Inst::Cond(inst)) => Some(inst.target(pos)),
_ => None,
})
.filter(|target| Inst::CODE_RANGE.contains(target))
//.filter(|target| Inst::CODE_RANGE.contains(target))
.collect();
// Now check every `Jal` and `Dw` for possible function entrances
@ -163,7 +163,7 @@ impl FuncTable {
Inst::Directive(Directive::Dw(address)) => Some(Pos(address)),
_ => None,
})
.filter(|target| Inst::CODE_RANGE.contains(target))
//.filter(|target| Inst::CODE_RANGE.contains(target))
.collect();
#[allow(clippy::cognitive_complexity)] // TODO: Fix

View File

@ -12,6 +12,8 @@ use dcb_bytes::Bytes;
use dcb_util::{array_split, null_ascii_string::NullAsciiString, AsciiStrArr};
use std::fmt;
use crate::Pos;
/// The header of the executable.
#[derive(PartialEq, Eq, Clone, Copy, Hash, Debug)]
#[derive(serde::Serialize, serde::Deserialize)]
@ -22,8 +24,8 @@ pub struct Header {
/// Initial global pointer
pub gp0: u32,
/// Destination in memory for the executable
pub dest: u32,
/// Starting position, in memory, of the executable.
pub start_pos: Pos,
/// Size of the executable
pub size: u32,
@ -55,7 +57,7 @@ impl fmt::Display for Header {
let Self {
ref pc0,
ref gp0,
ref dest,
ref start_pos,
ref size,
ref memfill_start,
ref memfill_size,
@ -67,7 +69,7 @@ impl fmt::Display for Header {
writeln!(f, "PC: {pc0:#x}")?;
writeln!(f, "GP: {gp0:#x}")?;
writeln!(f, "Destination: {dest:#x} / size: {size:#x}")?;
writeln!(f, "Memory position: {start_pos} / size: {size:#x}")?;
writeln!(f, "Memfill: {memfill_start:#X} / size: {memfill_size:#x}")?;
writeln!(f, "SP: {initial_sp_base:#x} / offset: {initial_sp_offset:#x}")?;
writeln!(f, "Marker: {marker:?}")
@ -112,7 +114,7 @@ impl Bytes for Header {
Ok(Self {
pc0: LittleEndian::read_u32(bytes.pc0),
gp0: LittleEndian::read_u32(bytes.gp0),
dest: LittleEndian::read_u32(bytes.dest),
start_pos: Pos(LittleEndian::read_u32(bytes.dest)),
size: LittleEndian::read_u32(bytes.size),
unknown20: LittleEndian::read_u32(bytes.unknown20),
unknown24: LittleEndian::read_u32(bytes.unknown24),

View File

@ -54,20 +54,13 @@ pub enum Inst<'a> {
Directive(Directive<'a>),
}
impl<'a> Inst<'a> {
/// End of the code itself in the executable.
pub const CODE_END: Pos = Pos(0x8006dd3c);
/// Code range
pub const CODE_RANGE: std::ops::Range<Pos> = Self::CODE_START..Self::CODE_END;
/// Start of the code itself in the executable.
pub const CODE_START: Pos = Pos(0x80013e4c);
}
impl<'a> Inst<'a> {}
impl<'a> Inst<'a> {
/// Decodes an instruction from bytes and it's position.
pub fn decode(pos: Pos, bytes: &'a [u8]) -> Option<Self> {
// If we're outside of code range, or not aligned to a word, decode a directive
if !Self::CODE_RANGE.contains(&pos) || !pos.is_word_aligned() {
// If we're not aligned to a word, decode a directive
if !pos.is_word_aligned() {
let directive = Directive::decode(pos, bytes)?;
return Some(Self::Directive(directive));
}

View File

@ -1,8 +1,7 @@
//! Directives
// Imports
//use super::{FromRawIter, Instruction, Raw};
use super::{Inst, InstFmt, InstSize};
use super::{InstFmt, InstSize};
use crate::exe::Pos;
use ascii::AsciiStr;
use dcb_util::NextFromBytes;
@ -72,6 +71,7 @@ pub enum ForceDecodeKind {
}
impl<'a> Directive<'a> {
/*
/// Positions that should be force decoded using a specific variant.
// TODO: Get this at run-time via a file.
pub const FORCE_DECODE_RANGES: &'static [ForceDecodeRange] = &[
@ -91,6 +91,7 @@ impl<'a> Directive<'a> {
kind: ForceDecodeKind::Word,
},
];
*/
}
impl<'a> Directive<'a> {

View File

@ -19,7 +19,7 @@ impl<'a> Iter<'a> {
pub(crate) const fn new(exe: &'a Exe) -> Self {
Self {
exe,
cur_pos: Exe::MEM_START_ADDRESS,
cur_pos: exe.header.start_pos,
}
}
}
@ -58,7 +58,7 @@ impl<'a> Iterator for Iter<'a> {
fn next(&mut self) -> Option<Self::Item> {
// If we're at the end, return `None`
let cur_pos = self.cur_pos;
if cur_pos >= Exe::MEM_END_ADDRESS {
if cur_pos >= self.exe.header.start_pos + self.exe.header.size {
return None;
}
@ -76,7 +76,10 @@ impl<'a> Iterator for Iter<'a> {
return Some(ExeItem::Data {
data,
insts: ParseIter::new(&self.exe.bytes[cur_pos.as_mem_idx()..end_pos.as_mem_idx()], cur_pos),
insts: ParseIter::new(
&self.exe.bytes[cur_pos.as_mem_idx(self.exe.header.start_pos)..end_pos.as_mem_idx(self.exe.header.start_pos)],
cur_pos,
),
});
}
@ -85,7 +88,10 @@ impl<'a> Iterator for Iter<'a> {
self.cur_pos = func.end_pos;
return Some(ExeItem::Func {
func,
insts: ParseIter::new(&self.exe.bytes[cur_pos.as_mem_idx()..func.end_pos.as_mem_idx()], cur_pos),
insts: ParseIter::new(
&self.exe.bytes[cur_pos.as_mem_idx(self.exe.header.start_pos)..func.end_pos.as_mem_idx(self.exe.header.start_pos)],
cur_pos,
),
});
}
@ -100,16 +106,19 @@ impl<'a> Iterator for Iter<'a> {
},
(Some(next_data), None) => next_data.pos,
(None, Some(next_func)) => next_func.start_pos,
(None, None) => Exe::MEM_END_ADDRESS,
(None, None) => self.exe.header.start_pos + self.exe.header.size,
};
// Make sure to limit the end position
let end_pos = end_pos.min(Exe::MEM_END_ADDRESS);
let end_pos = end_pos.min(self.exe.header.start_pos + self.exe.header.size);
self.cur_pos = end_pos;
Some(ExeItem::Unknown {
insts: ParseIter::new(&self.exe.bytes[cur_pos.as_mem_idx()..end_pos.as_mem_idx()], cur_pos),
insts: ParseIter::new(
&self.exe.bytes[cur_pos.as_mem_idx(self.exe.header.start_pos)..end_pos.as_mem_idx(self.exe.header.start_pos)],
cur_pos,
),
})
}
}

View File

@ -1,7 +1,6 @@
//! Instruction position
// Imports
use crate::Exe;
use int_conv::{SignExtended, Signed};
use std::{convert::TryFrom, fmt, ops};
@ -14,17 +13,10 @@ use std::{convert::TryFrom, fmt, ops};
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::MEM_START_ADDRESS).expect("Failed to compute index")
pub fn as_mem_idx(self, start_pos: Self) -> usize {
usize::try_from(self - start_pos).expect("Failed to compute index")
}
}

View File

@ -120,9 +120,6 @@ fn main() -> Result<(), anyhow::Error> {
for description in func.desc.lines() {
println!("# {description}");
}
for (pos, label) in &func.labels {
println!("# {pos}: .{label}");
}
for (pos, inst) in insts {
// If there's a label, print it
if let Some(label) = func.labels.get(&pos) {