Initial commit: xenia-rs workspace for Xbox 360 RE
Rust reimplementation of the xenia Xbox 360 emulator targeting reverse- engineering and preservation, initially scoped to Project Sylpheed. Includes: - XEX2 loader (LZX decompression, AES decryption, PE parsing) - XISO / XGD2 disc image VFS - PPC interpreter with 200+ opcodes and VMX128 decoding - Static analyzer: functions, cross-references, labels, asm + SQLite output - HLE kernel covering the xboxkrnl/xam subset used by Sylpheed init - Debugger with in-memory and SQLite-backed execution tracing - `xenia-rs` CLI with extract/dis/exec commands that produce cumulative, superset SQLite databases and opt-in instruction/import/branch traces Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
253
crates/xenia-kernel/src/xam.rs
Normal file
253
crates/xenia-kernel/src/xam.rs
Normal file
@@ -0,0 +1,253 @@
|
||||
//! HLE kernel export implementations (xam.xex).
|
||||
|
||||
use crate::state::{KernelState, ModuleId};
|
||||
use xenia_cpu::PpcContext;
|
||||
use xenia_memory::{GuestMemory, MemoryAccess};
|
||||
|
||||
pub fn register_exports(state: &mut KernelState) {
|
||||
use ModuleId::Xam;
|
||||
|
||||
// Net
|
||||
state.register_export(Xam, 0x01, "NetDll_WSAStartup", stub_success);
|
||||
state.register_export(Xam, 0x02, "NetDll_WSACleanup", stub_success);
|
||||
|
||||
// Input
|
||||
state.register_export(Xam, 0x0190, "XamInputGetCapabilities", xam_input_not_connected);
|
||||
state.register_export(Xam, 0x0191, "XamInputGetState", xam_input_not_connected);
|
||||
state.register_export(Xam, 0x0192, "XamInputSetState", xam_input_not_connected);
|
||||
state.register_export(Xam, 0x0198, "XamInputGetKeystrokeEx", xam_input_not_connected);
|
||||
|
||||
// Inactivity
|
||||
state.register_export(Xam, 0x01A0, "XamEnableInactivityProcessing", stub_success);
|
||||
state.register_export(Xam, 0x01A1, "XamResetInactivity", stub_success);
|
||||
|
||||
// Loader
|
||||
state.register_export(Xam, 0x01A4, "XamLoaderLaunchTitle", xam_loader_launch_title);
|
||||
state.register_export(Xam, 0x01A9, "XamLoaderTerminateTitle", xam_loader_terminate_title);
|
||||
|
||||
// Task
|
||||
state.register_export(Xam, 0x01AF, "XamTaskSchedule", xam_task_schedule);
|
||||
state.register_export(Xam, 0x01B1, "XamTaskCloseHandle", stub_success);
|
||||
state.register_export(Xam, 0x01B3, "XamTaskShouldExit", stub_return_zero);
|
||||
|
||||
// Alloc
|
||||
state.register_export(Xam, 0x01EA, "XamAlloc", xam_alloc);
|
||||
state.register_export(Xam, 0x01EC, "XamFree", stub_success);
|
||||
|
||||
// Msg
|
||||
state.register_export(Xam, 0x01F4, "XMsgInProcessCall", stub_success);
|
||||
state.register_export(Xam, 0x01F7, "XMsgStartIORequest", stub_success);
|
||||
state.register_export(Xam, 0x01FC, "XMsgStartIORequestEx", stub_success);
|
||||
|
||||
// User
|
||||
state.register_export(Xam, 0x020A, "XamUserGetXUID", xam_user_get_xuid);
|
||||
state.register_export(Xam, 0x020E, "XamUserGetName", xam_user_get_name);
|
||||
state.register_export(Xam, 0x0210, "XamUserGetSigninState", stub_return_zero);
|
||||
state.register_export(Xam, 0x0219, "XamUserReadProfileSettings", xam_user_read_profile_settings);
|
||||
state.register_export(Xam, 0x021A, "XamUserWriteProfileSettings", stub_success);
|
||||
|
||||
// Enum
|
||||
state.register_export(Xam, 0x0250, "XamEnumerate", stub_error_no_more_files);
|
||||
|
||||
// Content
|
||||
state.register_export(Xam, 0x0258, "XamContentCreate", stub_success);
|
||||
state.register_export(Xam, 0x025A, "XamContentClose", stub_success);
|
||||
state.register_export(Xam, 0x025B, "XamContentDelete", stub_success);
|
||||
state.register_export(Xam, 0x025C, "XamContentCreateEnumerator", stub_success);
|
||||
state.register_export(Xam, 0x025E, "XamContentGetDeviceData", stub_success);
|
||||
state.register_export(Xam, 0x025F, "XamContentGetDeviceName", stub_success);
|
||||
state.register_export(Xam, 0x0260, "XamContentSetThumbnail", stub_success);
|
||||
state.register_export(Xam, 0x0262, "XamContentGetCreator", stub_success);
|
||||
state.register_export(Xam, 0x0265, "XamContentGetDeviceState", stub_success);
|
||||
|
||||
// System
|
||||
state.register_export(Xam, 0x0280, "XamGetExecutionId", xam_get_execution_id);
|
||||
state.register_export(Xam, 0x0282, "XamGetSystemVersion", xam_get_system_version);
|
||||
|
||||
// Notify
|
||||
state.register_export(Xam, 0x028A, "XamNotifyCreateListener", xam_notify_create_listener);
|
||||
state.register_export(Xam, 0x028B, "XNotifyGetNext", xnotify_get_next);
|
||||
state.register_export(Xam, 0x028C, "XNotifyPositionUI", stub_success);
|
||||
|
||||
// Achievements/Stats
|
||||
state.register_export(Xam, 0x02EE, "XamUserCreateAchievementEnumerator", stub_success);
|
||||
state.register_export(Xam, 0x02F7, "XamUserCreateStatsEnumerator", stub_success);
|
||||
|
||||
// UI
|
||||
state.register_export(Xam, 0x02BC, "XamShowSigninUI", stub_success);
|
||||
state.register_export(Xam, 0x02C1, "XamShowKeyboardUI", stub_success);
|
||||
state.register_export(Xam, 0x02CB, "XamShowDeviceSelectorUI", stub_success);
|
||||
state.register_export(Xam, 0x02D5, "XamShowGamerCardUIForXUID", stub_success);
|
||||
state.register_export(Xam, 0x02D9, "XamShowDirtyDiscErrorUI", stub_success);
|
||||
state.register_export(Xam, 0x02DC, "XamShowMessageBoxUIEx", stub_success);
|
||||
|
||||
// Session
|
||||
state.register_export(Xam, 0x0316, "XamSessionCreateHandle", xam_session_create_handle);
|
||||
state.register_export(Xam, 0x0317, "XamSessionRefObjByHandle", stub_success);
|
||||
|
||||
// Locale
|
||||
state.register_export(Xam, 0x03CB, "XGetAVPack", xget_avpack);
|
||||
state.register_export(Xam, 0x03CC, "XGetGameRegion", xget_game_region);
|
||||
state.register_export(Xam, 0x03CD, "XGetLanguage", xget_language);
|
||||
state.register_export(Xam, 0x03D1, "XGetVideoMode", xget_video_mode);
|
||||
}
|
||||
|
||||
// ===== Generic stubs =====
|
||||
|
||||
fn stub_success(ctx: &mut PpcContext, _mem: &mut GuestMemory, _state: &mut KernelState) {
|
||||
ctx.gpr[3] = 0;
|
||||
}
|
||||
|
||||
fn stub_return_zero(ctx: &mut PpcContext, _mem: &mut GuestMemory, _state: &mut KernelState) {
|
||||
ctx.gpr[3] = 0;
|
||||
}
|
||||
|
||||
fn stub_error_no_more_files(ctx: &mut PpcContext, _mem: &mut GuestMemory, _state: &mut KernelState) {
|
||||
ctx.gpr[3] = 0x12; // ERROR_NO_MORE_FILES
|
||||
}
|
||||
|
||||
// ===== Input =====
|
||||
|
||||
fn xam_input_not_connected(ctx: &mut PpcContext, _mem: &mut GuestMemory, _state: &mut KernelState) {
|
||||
ctx.gpr[3] = 0x48F; // ERROR_DEVICE_NOT_CONNECTED
|
||||
}
|
||||
|
||||
// ===== Loader =====
|
||||
|
||||
fn xam_loader_launch_title(ctx: &mut PpcContext, _mem: &mut GuestMemory, _state: &mut KernelState) {
|
||||
tracing::warn!("XamLoaderLaunchTitle called");
|
||||
ctx.gpr[3] = 0;
|
||||
}
|
||||
|
||||
fn xam_loader_terminate_title(ctx: &mut PpcContext, _mem: &mut GuestMemory, _state: &mut KernelState) {
|
||||
tracing::warn!("XamLoaderTerminateTitle called");
|
||||
ctx.gpr[3] = 0;
|
||||
}
|
||||
|
||||
// ===== Task =====
|
||||
|
||||
fn xam_task_schedule(ctx: &mut PpcContext, _mem: &mut GuestMemory, state: &mut KernelState) {
|
||||
let handle = state.alloc_handle();
|
||||
tracing::info!("XamTaskSchedule: handle={:#x}", handle);
|
||||
ctx.gpr[3] = 0;
|
||||
}
|
||||
|
||||
// ===== Alloc =====
|
||||
|
||||
fn xam_alloc(ctx: &mut PpcContext, mem: &mut GuestMemory, state: &mut KernelState) {
|
||||
// r3 = flags, r4 = size, r5 = out_ptr_ptr
|
||||
let size = ctx.gpr[4] as u32;
|
||||
let out_ptr = ctx.gpr[5] as u32;
|
||||
|
||||
match state.heap_alloc(size, mem) {
|
||||
Some(addr) => {
|
||||
if out_ptr != 0 {
|
||||
mem.write_u32(out_ptr, addr);
|
||||
}
|
||||
ctx.gpr[3] = 0; // SUCCESS
|
||||
}
|
||||
None => {
|
||||
ctx.gpr[3] = 0x8007_000E; // E_OUTOFMEMORY
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ===== User =====
|
||||
|
||||
fn xam_user_get_xuid(ctx: &mut PpcContext, mem: &mut GuestMemory, _state: &mut KernelState) {
|
||||
// r3 = user_index, r4 = xuid_ptr
|
||||
let xuid_ptr = ctx.gpr[4] as u32;
|
||||
if xuid_ptr != 0 {
|
||||
mem.write_u64(xuid_ptr, 0); // No XUID
|
||||
}
|
||||
ctx.gpr[3] = 0;
|
||||
}
|
||||
|
||||
fn xam_user_get_name(ctx: &mut PpcContext, mem: &mut GuestMemory, _state: &mut KernelState) {
|
||||
// r3 = user_index, r4 = buffer, r5 = buffer_size
|
||||
let buffer = ctx.gpr[4] as u32;
|
||||
if buffer != 0 {
|
||||
mem.write_u8(buffer, 0); // Empty string
|
||||
}
|
||||
ctx.gpr[3] = 0;
|
||||
}
|
||||
|
||||
fn xam_user_read_profile_settings(ctx: &mut PpcContext, _mem: &mut GuestMemory, _state: &mut KernelState) {
|
||||
// Return error — no profile
|
||||
ctx.gpr[3] = 0x0000_048B; // ERROR_NOT_FOUND
|
||||
}
|
||||
|
||||
// ===== System =====
|
||||
|
||||
fn xam_get_execution_id(ctx: &mut PpcContext, mem: &mut GuestMemory, state: &mut KernelState) {
|
||||
// r3 = execution_id_ptr_ptr — write pointer to execution info
|
||||
let ptr_ptr = ctx.gpr[3] as u32;
|
||||
if ptr_ptr != 0 {
|
||||
// Allocate and fill a fake XEX_EXECUTION_ID structure
|
||||
if let Some(exec_id_addr) = state.heap_alloc(0x18, mem) {
|
||||
mem.write_u32(exec_id_addr, 0x535107D4); // title_id (Project Sylpheed)
|
||||
mem.write_u32(exec_id_addr + 4, 0x2D2E2EEB); // media_id
|
||||
mem.write_u16(exec_id_addr + 8, 0); // version
|
||||
mem.write_u16(exec_id_addr + 10, 0); // base_version
|
||||
mem.write_u16(exec_id_addr + 12, 1); // disc_number
|
||||
mem.write_u16(exec_id_addr + 14, 1); // disc_count
|
||||
mem.write_u32(ptr_ptr, exec_id_addr);
|
||||
}
|
||||
}
|
||||
ctx.gpr[3] = 0;
|
||||
}
|
||||
|
||||
fn xam_get_system_version(ctx: &mut PpcContext, _mem: &mut GuestMemory, _state: &mut KernelState) {
|
||||
ctx.gpr[3] = 0x2000_0000; // System version
|
||||
}
|
||||
|
||||
// ===== Notify =====
|
||||
|
||||
fn xam_notify_create_listener(ctx: &mut PpcContext, _mem: &mut GuestMemory, state: &mut KernelState) {
|
||||
let handle = state.alloc_handle();
|
||||
ctx.gpr[3] = handle as u64;
|
||||
}
|
||||
|
||||
fn xnotify_get_next(ctx: &mut PpcContext, _mem: &mut GuestMemory, _state: &mut KernelState) {
|
||||
// r3 = handle, r4 = id_ptr, r5 = param_ptr
|
||||
ctx.gpr[3] = 0; // FALSE (no notifications)
|
||||
}
|
||||
|
||||
// ===== Session =====
|
||||
|
||||
fn xam_session_create_handle(ctx: &mut PpcContext, mem: &mut GuestMemory, state: &mut KernelState) {
|
||||
// r3 = handle_ptr
|
||||
let handle_ptr = ctx.gpr[3] as u32;
|
||||
let handle = state.alloc_handle();
|
||||
if handle_ptr != 0 {
|
||||
mem.write_u32(handle_ptr, handle);
|
||||
}
|
||||
ctx.gpr[3] = 0;
|
||||
}
|
||||
|
||||
// ===== Locale =====
|
||||
|
||||
fn xget_avpack(ctx: &mut PpcContext, _mem: &mut GuestMemory, _state: &mut KernelState) {
|
||||
ctx.gpr[3] = 0x16; // HDMI
|
||||
}
|
||||
|
||||
fn xget_game_region(ctx: &mut PpcContext, _mem: &mut GuestMemory, _state: &mut KernelState) {
|
||||
ctx.gpr[3] = 0xFF; // All regions
|
||||
}
|
||||
|
||||
fn xget_language(ctx: &mut PpcContext, _mem: &mut GuestMemory, _state: &mut KernelState) {
|
||||
ctx.gpr[3] = 1; // English
|
||||
}
|
||||
|
||||
fn xget_video_mode(ctx: &mut PpcContext, mem: &mut GuestMemory, _state: &mut KernelState) {
|
||||
// r3 = video_mode_ptr
|
||||
let ptr = ctx.gpr[3] as u32;
|
||||
if ptr != 0 {
|
||||
mem.write_u32(ptr, 1280); // width
|
||||
mem.write_u32(ptr + 4, 720); // height
|
||||
mem.write_u32(ptr + 8, 0); // is_interlaced
|
||||
mem.write_u32(ptr + 12, 1); // is_widescreen
|
||||
mem.write_u32(ptr + 16, 60); // refresh_rate
|
||||
}
|
||||
ctx.gpr[3] = 0;
|
||||
}
|
||||
Reference in New Issue
Block a user