mirror of
https://github.com/Zenithsiz/dcb.git
synced 2026-02-11 04:35:46 +00:00
Added new deserializer for the game executable.
This commit is contained in:
@@ -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
58
dcb/src/game/exe.rs
Normal 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
24
dcb/src/game/exe/error.rs
Normal 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
108
dcb/src/game/exe/header.rs
Normal 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!()
|
||||
}
|
||||
}
|
||||
23
dcb/src/game/exe/header/error.rs
Normal file
23
dcb/src/game/exe/header/error.rs
Normal 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 = !;
|
||||
@@ -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
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user