155 lines
4.4 KiB
Rust
155 lines
4.4 KiB
Rust
// hide console in release mode
|
|
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
|
|
|
|
use std::{process, time::SystemTime};
|
|
|
|
use anyhow::{Result, bail};
|
|
use app::Application;
|
|
use clap::Parser;
|
|
use emulator::EmulatorBuilder;
|
|
use thread_priority::{ThreadBuilder, ThreadPriority};
|
|
use tracing::error;
|
|
use tracing_subscriber::{EnvFilter, Layer, layer::SubscriberExt, util::SubscriberInitExt};
|
|
use winit::event_loop::{ControlFlow, EventLoop};
|
|
|
|
use crate::{
|
|
config::{AppConfig, CliArgs, SimConfig},
|
|
emulator::SimId,
|
|
persistence::Persistence,
|
|
};
|
|
|
|
mod app;
|
|
mod audio;
|
|
mod config;
|
|
mod controller;
|
|
mod emulator;
|
|
mod filepicker;
|
|
mod gdbserver;
|
|
mod graphics;
|
|
mod images;
|
|
mod input;
|
|
mod memory;
|
|
mod persistence;
|
|
mod profiler;
|
|
mod window;
|
|
|
|
fn init_logger() {
|
|
let directives = std::env::var("RUST_LOG").unwrap_or("error,lemur=info".into());
|
|
let filter = EnvFilter::builder().parse_lossy(directives);
|
|
let layer = tracing_subscriber::fmt::layer().with_filter(filter);
|
|
tracing_subscriber::registry().with(layer).init();
|
|
}
|
|
|
|
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!("{msg}\n");
|
|
} else if let Some(msg) = info.payload().downcast_ref::<String>() {
|
|
message += &format!("{msg}\n");
|
|
}
|
|
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{backtrace:#}\n");
|
|
|
|
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-{timestamp}.txt");
|
|
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<()> {
|
|
init_logger();
|
|
|
|
set_panic_handler();
|
|
|
|
#[cfg(windows)]
|
|
set_process_priority_to_high()?;
|
|
|
|
let args = CliArgs::parse();
|
|
|
|
let persistence = Persistence::new();
|
|
|
|
let (mut builder, client) = EmulatorBuilder::new();
|
|
if let Some(path) = &args.rom {
|
|
builder = builder.with_rom(path);
|
|
}
|
|
if args.debug_port.is_some() {
|
|
if args.rom.is_none() {
|
|
bail!("to start debugging, please select a game.");
|
|
}
|
|
builder = builder.start_paused(true);
|
|
}
|
|
if args.profile {
|
|
builder = builder.start_paused(true)
|
|
}
|
|
let mut config = AppConfig::load(&persistence);
|
|
if config.update(&args) {
|
|
let _ = config.save(&persistence);
|
|
}
|
|
let p1 = SimConfig::load(&persistence, SimId::Player1);
|
|
let p2 = SimConfig::load(&persistence, SimId::Player2);
|
|
|
|
let watch = args.watch;
|
|
|
|
builder = builder
|
|
.with_audio_on(p1.audio_enabled, p2.audio_enabled)
|
|
.with_watch_rom(watch, watch);
|
|
|
|
ThreadBuilder::default()
|
|
.name("Emulator".to_owned())
|
|
.priority(ThreadPriority::Max)
|
|
.spawn_careless(move || {
|
|
let mut emulator = match builder.build() {
|
|
Ok(e) => e,
|
|
Err(error) => {
|
|
error!(%error, "Error initializing emulator");
|
|
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();
|
|
let handle = event_loop.owned_display_handle();
|
|
event_loop.run_app(&mut Application::new(
|
|
client,
|
|
proxy,
|
|
handle,
|
|
persistence,
|
|
args,
|
|
config,
|
|
))?;
|
|
Ok(())
|
|
}
|