// hide console in release mode #![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] use std::{path::PathBuf, process, time::SystemTime}; use anyhow::Result; use app::Application; use clap::Parser; use emulator::EmulatorBuilder; use thread_priority::{ThreadBuilder, ThreadPriority}; use winit::event_loop::{ControlFlow, EventLoop}; mod app; mod audio; mod controller; mod emulator; mod gdbserver; mod graphics; mod input; mod persistence; mod window; #[derive(Parser)] struct Args { /// The path to a virtual boy ROM to run. rom: Option, /// Start a GDB/LLDB debug server on this port. #[arg(short, long)] debug_port: Option, } fn set_panic_handler() { std::panic::set_hook(Box::new(|info| { let mut message = String::new(); if let Some(msg) = info.payload().downcast_ref::<&str>() { message += &format!("{}\n", msg); } else if let Some(msg) = info.payload().downcast_ref::() { message += &format!("{}\n", msg); } if let Some(location) = info.location() { message += &format!( " in file '{}' at line {}\n", location.file(), location.line() ); } let backtrace = std::backtrace::Backtrace::force_capture(); message += &format!("stack trace:\n{:#}\n", backtrace); eprint!("{}", message); let Some(project_dirs) = directories::ProjectDirs::from("com", "virtual-boy", "Lemur") else { return; }; let data_dir = project_dirs.data_dir(); if std::fs::create_dir_all(data_dir).is_err() { return; } let timestamp = SystemTime::now() .duration_since(SystemTime::UNIX_EPOCH) .unwrap() .as_millis(); let logfile_name = format!("crash-{}.txt", timestamp); let _ = std::fs::write(data_dir.join(logfile_name), message); })); } #[cfg(windows)] fn set_process_priority_to_high() -> Result<()> { use windows::Win32::{Foundation, System::Threading}; let process = unsafe { Threading::GetCurrentProcess() }; unsafe { Threading::SetPriorityClass(process, Threading::HIGH_PRIORITY_CLASS)? }; unsafe { Foundation::CloseHandle(process)? }; Ok(()) } fn main() -> Result<()> { set_panic_handler(); #[cfg(windows)] set_process_priority_to_high()?; let args = Args::parse(); let (mut builder, client) = EmulatorBuilder::new(); if let Some(path) = args.rom { builder = builder.with_rom(&path); } ThreadBuilder::default() .name("Emulator".to_owned()) .priority(ThreadPriority::Max) .spawn_careless(move || { let mut emulator = match builder.build() { Ok(e) => e, Err(err) => { eprintln!("Error initializing emulator: {err}"); process::exit(1); } }; emulator.run(); })?; let event_loop = EventLoop::with_user_event().build().unwrap(); event_loop.set_control_flow(ControlFlow::Poll); let proxy = event_loop.create_proxy(); event_loop.run_app(&mut Application::new(client, proxy, args.debug_port))?; Ok(()) }