Added new deserializer for the game executable.

This commit is contained in:
2020-10-23 03:00:37 +01:00
parent 147a44e4cd
commit 06c88e506d
6 changed files with 216 additions and 0 deletions

View File

@@ -17,9 +17,11 @@
// Modules
pub mod card;
pub mod deck;
pub mod exe;
pub mod validation;
// Exports
pub use card::{Digimon, Digivolve, Item, Table as CardTable};
pub use deck::{Deck, Table as DeckTable};
pub use exe::{Exe, Header as ExeHeader};
pub use validation::{Validatable, Validation};

58
dcb/src/game/exe.rs Normal file
View File

@@ -0,0 +1,58 @@
//! Executable
//!
//! This module contains the executable portion of the game,
//! as well as tools to decompile and recompile it.
// Modules
pub mod error;
pub mod header;
// Exports
pub use error::DeserializeError;
pub use header::Header;
// Imports
use crate::{io::address::Data, GameFile};
use dcb_bytes::{ByteArray, Bytes};
use std::{
convert::TryFrom,
io::{Read, Seek, Write},
};
/// The game executable
///
/// This type holds all of the executable code
/// of the game.
#[derive(PartialEq, Eq, Clone, Hash, Debug)]
#[derive(serde::Serialize, serde::Deserialize)]
pub struct Exe {
/// The executable header
pub header: Header,
/// All data
pub data: Vec<u8>,
}
impl Exe {
/// Start address of the executable
const START_ADDRESS: Data = Data::from_u64(0x58b9000);
}
impl Exe {
/// Deserializes the card table 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()))
.map_err(DeserializeError::Seek)?;
// Read header
let mut header_bytes = [0u8; <<Header as Bytes>::ByteArray as ByteArray>::SIZE];
file.read_exact(&mut header_bytes).map_err(DeserializeError::ReadHeader)?;
let header = Header::from_bytes(&header_bytes).map_err(DeserializeError::ParseHeader)?;
let mut data = vec![0u8; usize::try_from(header.size).expect("Header size didn't fit into a `usize`")];
file.read_exact(data.as_mut()).map_err(DeserializeError::ReadData)?;
Ok(Self { header, data })
}
}

24
dcb/src/game/exe/error.rs Normal file
View File

@@ -0,0 +1,24 @@
//! Errors
// Imports
use super::header;
/// Error type for [`Table::deserialize`]
#[derive(Debug, thiserror::Error)]
pub enum DeserializeError {
/// Unable to seek game file
#[error("Unable to seek game file to executable")]
Seek(#[source] std::io::Error),
/// Unable to read header
#[error("Unable to read header")]
ReadHeader(#[source] std::io::Error),
/// Unable to parse header
#[error("Unable to parse header")]
ParseHeader(#[source] header::FromBytesError),
/// Unable to read data
#[error("Unable to read data")]
ReadData(#[source] std::io::Error),
}

108
dcb/src/game/exe/header.rs Normal file
View File

@@ -0,0 +1,108 @@
//! Executable header
// Modules
pub mod error;
// Exports
pub use error::{FromBytesError, ToBytesError};
// Import
use crate::{
util::{array_split, null_ascii_string::NullAsciiString},
AsciiStrArr,
};
use byteorder::{ByteOrder, LittleEndian};
use dcb_bytes::Bytes;
/// The header of the executable.
#[derive(PartialEq, Eq, Clone, Copy, Hash, Debug)]
#[derive(serde::Serialize, serde::Deserialize)]
pub struct Header {
/// Initial program counter
pub initial_pc: u32,
/// Initial global pointer
pub initial_gp: u32,
/// Destination in memory for the executable
pub dest: u32,
/// Size of the executable
pub size: u32,
/// Unknown at `0x20`
pub unknown20: u32,
/// Unknown at `0x24`
pub unknown24: u32,
/// Where to start mem filling
pub memfill_start: u32,
/// Size to mem fill
pub memfill_size: u32,
/// Initial stack pointer
pub initial_sp_base: u32,
/// Offset from initial stack pointer
pub initial_sp_offset: u32,
/// Executable region marker
pub marker: AsciiStrArr<0x7b4>,
}
impl Header {
/// Magic
pub const MAGIC: &'static [u8; 8] = b"PS-X EXE";
}
impl Bytes for Header {
type ByteArray = [u8; 0x800];
type FromError = FromBytesError;
type ToError = ToBytesError;
fn from_bytes(bytes: &Self::ByteArray) -> Result<Self, Self::FromError> {
let bytes = array_split!(bytes,
magic : [0x8],
_zero : [0x8],
initial_pc : [0x4],
initial_gp : [0x4],
dest : [0x4],
size : [0x4],
unknown20 : [0x4],
unknown24 : [0x4],
memfill_start : [0x4],
memfill_size : [0x4],
initial_sp_base : [0x4],
initial_sp_offset: [0x4],
_zero2 : [0x13],
marker : [0x7b5],
);
// If the magic is wrong, return Err
if bytes.magic != Self::MAGIC {
return Err(FromBytesError::Magic { magic: *bytes.magic });
}
// TODO: Maybe check if `zero` and `zero2` are actually zero?
Ok(Self {
initial_pc: LittleEndian::read_u32(bytes.initial_pc),
initial_gp: LittleEndian::read_u32(bytes.initial_gp),
dest: LittleEndian::read_u32(bytes.dest),
size: LittleEndian::read_u32(bytes.size),
unknown20: LittleEndian::read_u32(bytes.unknown20),
unknown24: LittleEndian::read_u32(bytes.unknown24),
memfill_start: LittleEndian::read_u32(bytes.memfill_start),
memfill_size: LittleEndian::read_u32(bytes.memfill_size),
initial_sp_base: LittleEndian::read_u32(bytes.initial_sp_base),
initial_sp_offset: LittleEndian::read_u32(bytes.initial_sp_offset),
marker: NullAsciiString::read_string(bytes.marker).map_err(FromBytesError::Name)?,
})
}
fn to_bytes(&self, _bytes: &mut Self::ByteArray) -> Result<(), Self::ToError> {
todo!()
}
}

View File

@@ -0,0 +1,23 @@
//! Errors
// Imports
use super::Header;
use crate::util::null_ascii_string;
/// Error type for [`Bytes::from_bytes`](dcb_bytes::Bytes::from_bytes)
#[derive(PartialEq, Eq, Clone, Copy, Debug, thiserror::Error)]
pub enum FromBytesError {
/// The magic of the table was wrong
#[error("Found wrong header magic (expected {:?}, found {:?})", Header::MAGIC, magic)]
Magic {
/// Magic we found
magic: [u8; 8],
},
/// Unable to read region marker
#[error("Unable to read the region marker")]
Name(#[source] null_ascii_string::ReadError),
}
/// Error type for [`Bytes::to_bytes`](dcb_bytes::Bytes::to_bytes)
pub type ToBytesError = !;

View File

@@ -58,4 +58,5 @@ impl_null_ascii_string!(
10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
30, 31, 32,
1972
);