Initial xenia-rs
This commit is contained in:
13
xenia-rs/crates/xenia-xex/Cargo.toml
Normal file
13
xenia-rs/crates/xenia-xex/Cargo.toml
Normal file
@@ -0,0 +1,13 @@
|
||||
[package]
|
||||
name = "xenia-xex"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
|
||||
[dependencies]
|
||||
xenia-types = { workspace = true }
|
||||
xenia-memory = { workspace = true }
|
||||
tracing = { workspace = true }
|
||||
byteorder = { workspace = true }
|
||||
thiserror = { workspace = true }
|
||||
anyhow = { workspace = true }
|
||||
55
xenia-rs/crates/xenia-xex/src/header.rs
Normal file
55
xenia-rs/crates/xenia-xex/src/header.rs
Normal file
@@ -0,0 +1,55 @@
|
||||
/// XEX2 file header. Parsed from the beginning of an Xbox 360 executable.
|
||||
#[derive(Debug)]
|
||||
pub struct Xex2Header {
|
||||
pub magic: u32,
|
||||
pub module_flags: u32,
|
||||
pub header_size: u32,
|
||||
pub security_offset: u32,
|
||||
pub header_count: u32,
|
||||
pub optional_headers: Vec<Xex2OptionalHeader>,
|
||||
pub security_info: Option<Xex2SecurityInfo>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Xex2OptionalHeader {
|
||||
pub key: u32,
|
||||
pub value: u32,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Xex2SecurityInfo {
|
||||
pub image_size: u32,
|
||||
pub load_address: u32,
|
||||
pub export_table_address: u32,
|
||||
pub image_flags: u32,
|
||||
pub page_descriptors: Vec<Xex2PageDescriptor>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct Xex2PageDescriptor {
|
||||
pub size_and_info: u32,
|
||||
}
|
||||
|
||||
impl Xex2PageDescriptor {
|
||||
pub fn page_count(&self) -> u32 {
|
||||
self.size_and_info >> 4
|
||||
}
|
||||
|
||||
pub fn info(&self) -> u32 {
|
||||
self.size_and_info & 0xF
|
||||
}
|
||||
}
|
||||
|
||||
/// XEX2 magic: "XEX2"
|
||||
pub const XEX2_MAGIC: u32 = 0x58455832;
|
||||
|
||||
/// Optional header keys
|
||||
pub mod header_keys {
|
||||
pub const ENTRY_POINT: u32 = 0x00010100;
|
||||
pub const IMAGE_BASE_ADDRESS: u32 = 0x00010201;
|
||||
pub const IMPORT_LIBRARIES: u32 = 0x000103FF;
|
||||
pub const TLS_INFO: u32 = 0x00020200;
|
||||
pub const EXECUTION_INFO: u32 = 0x00040006;
|
||||
pub const DEFAULT_STACK_SIZE: u32 = 0x00020200;
|
||||
pub const ORIGINAL_PE_NAME: u32 = 0x000183FF;
|
||||
}
|
||||
4
xenia-rs/crates/xenia-xex/src/lib.rs
Normal file
4
xenia-rs/crates/xenia-xex/src/lib.rs
Normal file
@@ -0,0 +1,4 @@
|
||||
pub mod header;
|
||||
pub mod loader;
|
||||
|
||||
pub use header::Xex2Header;
|
||||
98
xenia-rs/crates/xenia-xex/src/loader.rs
Normal file
98
xenia-rs/crates/xenia-xex/src/loader.rs
Normal file
@@ -0,0 +1,98 @@
|
||||
use crate::header::*;
|
||||
use byteorder::{BigEndian, ReadBytesExt};
|
||||
use std::io::{self, Cursor, Read, Seek, SeekFrom};
|
||||
|
||||
/// Parse a XEX2 header from raw file data.
|
||||
pub fn parse_xex2_header(data: &[u8]) -> io::Result<Xex2Header> {
|
||||
let mut cursor = Cursor::new(data);
|
||||
|
||||
let magic = cursor.read_u32::<BigEndian>()?;
|
||||
if magic != XEX2_MAGIC {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::InvalidData,
|
||||
format!("Invalid XEX2 magic: {:#010x} (expected {:#010x})", magic, XEX2_MAGIC),
|
||||
));
|
||||
}
|
||||
|
||||
let module_flags = cursor.read_u32::<BigEndian>()?;
|
||||
let header_size = cursor.read_u32::<BigEndian>()?;
|
||||
let _reserved = cursor.read_u32::<BigEndian>()?;
|
||||
let security_offset = cursor.read_u32::<BigEndian>()?;
|
||||
let header_count = cursor.read_u32::<BigEndian>()?;
|
||||
|
||||
let mut optional_headers = Vec::new();
|
||||
for _ in 0..header_count {
|
||||
let key = cursor.read_u32::<BigEndian>()?;
|
||||
let value = cursor.read_u32::<BigEndian>()?;
|
||||
optional_headers.push(Xex2OptionalHeader { key, value });
|
||||
}
|
||||
|
||||
// Parse security info
|
||||
let security_info = if (security_offset as usize) < data.len() {
|
||||
cursor.seek(SeekFrom::Start(security_offset as u64))?;
|
||||
Some(parse_security_info(&mut cursor)?)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
Ok(Xex2Header {
|
||||
magic,
|
||||
module_flags,
|
||||
header_size,
|
||||
security_offset,
|
||||
header_count,
|
||||
optional_headers,
|
||||
security_info,
|
||||
})
|
||||
}
|
||||
|
||||
fn parse_security_info(cursor: &mut Cursor<&[u8]>) -> io::Result<Xex2SecurityInfo> {
|
||||
let _header_size = cursor.read_u32::<BigEndian>()?;
|
||||
let image_size = cursor.read_u32::<BigEndian>()?;
|
||||
|
||||
// Skip RSA signature (256 bytes) and other security fields
|
||||
let mut skip_buf = [0u8; 256];
|
||||
cursor.read_exact(&mut skip_buf)?;
|
||||
|
||||
// Skip image info hash (20 bytes) and import table hash (20 bytes)
|
||||
cursor.read_exact(&mut [0u8; 20])?;
|
||||
cursor.read_exact(&mut [0u8; 20])?;
|
||||
|
||||
let load_address = cursor.read_u32::<BigEndian>()?;
|
||||
let _load_size = cursor.read_u32::<BigEndian>()?;
|
||||
let export_table_address = cursor.read_u32::<BigEndian>()?;
|
||||
let image_flags = cursor.read_u32::<BigEndian>()?;
|
||||
|
||||
// Read page descriptor count
|
||||
let page_descriptor_count = cursor.read_u32::<BigEndian>()?;
|
||||
let mut page_descriptors = Vec::new();
|
||||
for _ in 0..page_descriptor_count {
|
||||
let size_and_info = cursor.read_u32::<BigEndian>()?;
|
||||
page_descriptors.push(Xex2PageDescriptor { size_and_info });
|
||||
}
|
||||
|
||||
Ok(Xex2SecurityInfo {
|
||||
image_size,
|
||||
load_address,
|
||||
export_table_address,
|
||||
image_flags,
|
||||
page_descriptors,
|
||||
})
|
||||
}
|
||||
|
||||
/// Get an optional header value by key.
|
||||
pub fn get_opt_header(header: &Xex2Header, key: u32) -> Option<u32> {
|
||||
header.optional_headers.iter()
|
||||
.find(|h| h.key == key)
|
||||
.map(|h| h.value)
|
||||
}
|
||||
|
||||
/// Get the entry point address from the XEX2 header.
|
||||
pub fn get_entry_point(header: &Xex2Header) -> Option<u32> {
|
||||
get_opt_header(header, header_keys::ENTRY_POINT)
|
||||
}
|
||||
|
||||
/// Get the image base address.
|
||||
pub fn get_image_base(header: &Xex2Header) -> Option<u32> {
|
||||
get_opt_header(header, header_keys::IMAGE_BASE_ADDRESS)
|
||||
}
|
||||
Reference in New Issue
Block a user