feat: parse and display all optional headers (M2)

Implement parsing for all 15 optional header types found in XEX2 files:
inline values (entry point, base address, stack size, system flags),
fixed-size structures (execution info, file format, TLS, game ratings,
LAN key, checksum/timestamp), and variable-size structures (static
libraries, import libraries, resource info, original PE name, Xbox 360
logo). Add comprehensive unit and integration tests.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
MechaCat02
2026-03-28 18:59:41 +01:00
parent a2e390a3fe
commit a9436a3a7a
8 changed files with 1582 additions and 11 deletions

View File

@@ -1,5 +1,8 @@
/// Pretty-print formatting for parsed XEX2 structures.
use crate::header::Xex2Header;
use crate::optional::{
format_hex_bytes, format_rating, format_timestamp, HeaderKey, OptionalHeaders,
};
/// Prints the XEX2 main header in a human-readable format.
pub fn display_header(header: &Xex2Header) {
@@ -14,3 +17,186 @@ pub fn display_header(header: &Xex2Header) {
println!("Security Offset: 0x{:08X}", header.security_offset);
println!("Header Count: {}", header.header_count);
}
/// Prints all parsed optional headers in a human-readable format.
pub fn display_optional_headers(headers: &OptionalHeaders) {
println!();
println!("=== Optional Headers ({} entries) ===", headers.entries.len());
// Display inline u32 values first
if let Some(v) = headers.entry_point {
println!();
println!("[ENTRY_POINT] 0x{v:08X}");
}
if let Some(v) = headers.original_base_address {
println!("[ORIGINAL_BASE_ADDRESS] 0x{v:08X}");
}
if let Some(v) = headers.image_base_address {
println!("[IMAGE_BASE_ADDRESS] 0x{v:08X}");
}
if let Some(v) = headers.default_stack_size {
println!("[DEFAULT_STACK_SIZE] 0x{v:08X} ({v} bytes)");
}
if let Some(v) = headers.default_filesystem_cache_size {
println!("[DEFAULT_FILESYSTEM_CACHE_SIZE] 0x{v:08X} ({v} bytes)");
}
if let Some(v) = headers.default_heap_size {
println!("[DEFAULT_HEAP_SIZE] 0x{v:08X} ({v} bytes)");
}
if let Some(v) = headers.title_workspace_size {
println!("[TITLE_WORKSPACE_SIZE] 0x{v:08X} ({v} bytes)");
}
if let Some(v) = headers.additional_title_memory {
println!("[ADDITIONAL_TITLE_MEMORY] 0x{v:08X} ({v} bytes)");
}
if let Some(v) = headers.enabled_for_fastcap {
println!("[ENABLED_FOR_FASTCAP] 0x{v:08X}");
}
// System flags
if let Some(ref flags) = headers.system_flags {
println!("[SYSTEM_FLAGS] {flags}");
}
// Execution info
if let Some(ref exec) = headers.execution_info {
println!();
println!("[EXECUTION_INFO]");
println!(" Media ID: 0x{:08X}", exec.media_id);
println!(" Title ID: 0x{:08X}", exec.title_id);
println!(" Version: {}", exec.version);
println!(" Base Version: {}", exec.base_version);
println!(" Platform: {}", exec.platform);
println!(" Executable Type: {}", exec.executable_type);
println!(" Disc: {}/{}", exec.disc_number, exec.disc_count);
println!(" Savegame ID: 0x{:08X}", exec.savegame_id);
}
// File format info
if let Some(ref fmt) = headers.file_format_info {
println!();
println!("[FILE_FORMAT_INFO]");
println!(" Encryption: {}", fmt.encryption_type);
println!(" Compression: {}", fmt.compression_type);
}
// Checksum + timestamp
if let Some(ref ct) = headers.checksum_timestamp {
println!();
println!("[CHECKSUM_TIMESTAMP]");
println!(" Checksum: 0x{:08X}", ct.checksum);
println!(
" Timestamp: 0x{:08X} ({})",
ct.timestamp,
format_timestamp(ct.timestamp)
);
}
// Original PE name
if let Some(ref name) = headers.original_pe_name {
println!();
println!("[ORIGINAL_PE_NAME] \"{name}\"");
}
// Bounding path
if let Some(ref path) = headers.bounding_path {
println!("[BOUNDING_PATH] \"{path}\"");
}
// TLS info
if let Some(ref tls) = headers.tls_info {
println!();
println!("[TLS_INFO]");
println!(" Slot Count: {}", tls.slot_count);
println!(" Raw Data Address: 0x{:08X}", tls.raw_data_address);
println!(" Data Size: {} bytes", tls.data_size);
println!(" Raw Data Size: {} bytes", tls.raw_data_size);
}
// Static libraries
if let Some(ref libs) = headers.static_libraries {
println!();
println!("[STATIC_LIBRARIES] ({} libraries)", libs.len());
for lib in libs {
println!(" {lib}");
}
}
// Import libraries
if let Some(ref imports) = headers.import_libraries {
println!();
println!("[IMPORT_LIBRARIES] ({} libraries)", imports.libraries.len());
for lib in &imports.libraries {
println!(
" {} v{} (min v{}) - {} imports",
lib.name, lib.version, lib.version_min, lib.record_count
);
}
}
// Resource info
if let Some(ref resources) = headers.resource_info {
println!();
println!("[RESOURCE_INFO] ({} entries)", resources.len());
for res in resources {
println!(
" \"{}\" @ 0x{:08X}, size: {} bytes",
res.name, res.address, res.size
);
}
}
// Game ratings
if let Some(ref ratings) = headers.game_ratings {
println!();
println!("[GAME_RATINGS]");
println!(" ESRB: {} | PEGI: {} | PEGI-FI: {} | PEGI-PT: {}",
format_rating(ratings.esrb), format_rating(ratings.pegi),
format_rating(ratings.pegi_fi), format_rating(ratings.pegi_pt));
println!(" BBFC: {} | CERO: {} | USK: {} | OFLC-AU: {}",
format_rating(ratings.bbfc), format_rating(ratings.cero),
format_rating(ratings.usk), format_rating(ratings.oflc_au));
println!(" OFLC-NZ: {} | KMRB: {} | Brazil: {} | FPB: {}",
format_rating(ratings.oflc_nz), format_rating(ratings.kmrb),
format_rating(ratings.brazil), format_rating(ratings.fpb));
}
// LAN key
if let Some(ref key) = headers.lan_key {
println!();
println!("[LAN_KEY] {}", format_hex_bytes(key));
}
// Callcap imports
if let Some(ref callcap) = headers.enabled_for_callcap {
println!();
println!("[ENABLED_FOR_CALLCAP]");
println!(" Start Thunk: 0x{:08X}", callcap.start_func_thunk_addr);
println!(" End Thunk: 0x{:08X}", callcap.end_func_thunk_addr);
}
// Exports by name
if let Some(ref dir) = headers.exports_by_name {
println!();
println!("[EXPORTS_BY_NAME]");
println!(" Offset: 0x{:08X}", dir.offset);
println!(" Size: {} bytes", dir.size);
}
// Xbox 360 logo
if let Some(size) = headers.xbox360_logo_size {
println!();
println!("[XBOX360_LOGO] {size} bytes");
}
// Unknown headers
for entry in &headers.entries {
if let HeaderKey::Unknown(raw) = entry.key {
println!();
println!(
"[UNKNOWN(0x{raw:08X})] value/offset: 0x{:08X}",
entry.value
);
}
}
}

View File

@@ -9,16 +9,20 @@
pub mod display;
pub mod error;
pub mod header;
pub mod optional;
pub mod util;
use error::Result;
use header::Xex2Header;
use optional::OptionalHeaders;
/// A parsed XEX2 file containing all extracted structures.
#[derive(Debug)]
pub struct Xex2File {
/// The main XEX2 header (magic, flags, sizes, offsets).
pub header: Xex2Header,
/// All parsed optional headers.
pub optional_headers: OptionalHeaders,
}
/// Parses an XEX2 file from a byte slice.
@@ -27,6 +31,10 @@ pub struct Xex2File {
/// Returns a [`Xex2File`] with all successfully parsed structures.
pub fn parse(data: &[u8]) -> Result<Xex2File> {
let header = header::parse_header(data)?;
let optional_headers = optional::parse_optional_headers(data, &header)?;
Ok(Xex2File { header })
Ok(Xex2File {
header,
optional_headers,
})
}

View File

@@ -26,4 +26,5 @@ fn main() {
};
xex2tractor::display::display_header(&xex.header);
xex2tractor::display::display_optional_headers(&xex.optional_headers);
}

1242
src/optional.rs Normal file

File diff suppressed because it is too large Load Diff