use crate::firmware::firmware_state::{ AppCrashesStep, FirmwareState, GameRunningStep, HubHomeStep, ShellRunningStep, }; use crate::firmware::prometeu_context::PrometeuContext; use crate::firmware::resident_game_termination; use prometeu_hal::app_mode::AppMode; use prometeu_hal::cartridge::Cartridge; use prometeu_hal::color::Color; use prometeu_hal::log::{LogLevel, LogSource}; use prometeu_hal::primitives::Rect; use prometeu_system::CrashReport; use prometeu_system::windows::WindowOwner; #[derive(Debug, Clone)] pub struct LoadCartridgeStep { pub cartridge: Cartridge, init_error: Option, loaded_task_id: Option, loaded_app_mode: Option, failure_recovery: LoadCartridgeFailureRecovery, } #[derive(Debug, Clone, Copy, PartialEq, Eq)] enum LoadCartridgeFailureRecovery { AppCrash, ReturnToHub, } impl LoadCartridgeStep { pub fn new(cartridge: Cartridge) -> Self { Self { cartridge, init_error: None, loaded_task_id: None, loaded_app_mode: None, failure_recovery: LoadCartridgeFailureRecovery::AppCrash, } } pub fn new_game_switch(cartridge: Cartridge) -> Self { Self { failure_recovery: LoadCartridgeFailureRecovery::ReturnToHub, ..Self::new(cartridge) } } pub fn on_enter(&mut self, ctx: &mut PrometeuContext) { ctx.os.log( LogLevel::Info, LogSource::Pos, 0, format!("Loading cartridge: {}", self.cartridge.title), ); ctx.platform.game2d_frame_composer().unbind_scene(); // Initialize Asset Manager ctx.platform.assets_mut().initialize_for_cartridge( self.cartridge.asset_table.clone(), self.cartridge.preload.clone(), self.cartridge.assets.clone(), ); match ctx.os.sessions().load_vm_cartridge(&self.cartridge) { Ok(loaded) => { self.loaded_task_id = Some(loaded.task_id); self.loaded_app_mode = Some(loaded.app_mode); } Err(report) => { self.init_error = Some(report.into_crash_report("load VM cartridge into session")); } } } pub fn on_update(&mut self, ctx: &mut PrometeuContext) -> Option { if let Some(report) = self.init_error.take() { if self.failure_recovery == LoadCartridgeFailureRecovery::ReturnToHub { if self.cartridge.app_mode == AppMode::Game && let Some(task_id) = ctx.os.lifecycle().resident_game_task() { let is_partial_target = ctx .os .sessions() .vm_session_for_task(task_id) .map(|session| session.app_id == self.cartridge.app_id) .unwrap_or(false); if is_partial_target { let _ = resident_game_termination::terminate_resident_game( ctx.os, ctx.platform, false, ); } } ctx.os.log( LogLevel::Error, LogSource::Hub, 0, format!( "Failed to switch to game {} app_id {}; returning to Home: {:?}", self.cartridge.title, self.cartridge.app_id, report ), ); return Some(FirmwareState::HubHome(HubHomeStep)); } return Some(FirmwareState::AppCrashes(AppCrashesStep { report })); } let Some(task_id) = self.loaded_task_id.take() else { let report = CrashReport::VmPanic { message: format!( "cartridge {} was not loaded into a VM session", self.cartridge.title ), pc: None, }; return Some(FirmwareState::AppCrashes(AppCrashesStep { report })); }; if self.loaded_app_mode == Some(AppMode::Shell) { let id = ctx.os.windows().add_window( self.cartridge.title.clone(), WindowOwner::Task(task_id), Rect { x: 40, y: 20, w: 240, h: 140 }, Color::WHITE, ); ctx.os.windows().set_focus(id); return Some(FirmwareState::ShellRunning(ShellRunningStep::new(task_id))); } Some(FirmwareState::GameRunning(GameRunningStep::new(task_id))) } pub fn on_exit(&mut self, _ctx: &mut PrometeuContext) {} }