mirror of
https://github.com/Zenithsiz/dcb.git
synced 2026-02-07 18:31:14 +00:00
Executable size and start positions are now dynamic.
Function labels are no longer printed at the start.
This commit is contained in:
parent
c443402400
commit
d9bc90882b
@ -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());
|
||||
|
||||
@ -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),
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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),
|
||||
|
||||
@ -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));
|
||||
}
|
||||
|
||||
@ -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> {
|
||||
|
||||
@ -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,
|
||||
),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -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")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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) {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user