2020-05-12 08:29:49 +01:00
|
|
|
use sdl2::controller::Button;
|
2022-09-14 22:34:41 +01:00
|
|
|
use sdl2::event::Event;
|
2020-05-03 21:03:57 +01:00
|
|
|
use sdl2::keyboard::Scancode;
|
2020-02-14 12:01:48 +00:00
|
|
|
|
2022-09-12 23:50:50 +01:00
|
|
|
use structopt::StructOpt;
|
2019-12-04 23:15:49 +00:00
|
|
|
|
2020-01-31 00:12:38 +00:00
|
|
|
use std::fs;
|
2020-05-20 23:23:11 +01:00
|
|
|
use std::io::Cursor;
|
2022-09-12 23:50:50 +01:00
|
|
|
use std::path::Path;
|
2020-01-31 00:12:38 +00:00
|
|
|
use std::time;
|
2020-01-11 15:28:25 +00:00
|
|
|
|
2020-01-30 23:47:52 +00:00
|
|
|
#[macro_use]
|
|
|
|
extern crate log;
|
|
|
|
use flexi_logger::*;
|
|
|
|
|
2019-12-04 23:15:49 +00:00
|
|
|
mod audio;
|
2019-12-20 16:11:14 +00:00
|
|
|
mod input;
|
2022-09-12 23:50:50 +01:00
|
|
|
mod options;
|
2019-12-04 23:15:49 +00:00
|
|
|
mod video;
|
|
|
|
|
2020-04-11 14:06:34 +01:00
|
|
|
use rustboyadvance_core::prelude::*;
|
2022-09-04 21:54:44 +01:00
|
|
|
|
|
|
|
use rustboyadvance_utils::FpsCounter;
|
2019-12-04 23:15:49 +00:00
|
|
|
|
2020-01-30 23:47:52 +00:00
|
|
|
const LOG_DIR: &str = ".logs";
|
|
|
|
|
2020-05-20 23:23:11 +01:00
|
|
|
fn ask_download_bios() {
|
2024-03-22 19:50:34 +00:00
|
|
|
const OPEN_SOURCE_BIOS_URL: &str =
|
2020-05-01 15:56:26 +01:00
|
|
|
"https://github.com/Nebuleon/ReGBA/raw/master/bios/gba_bios.bin";
|
2020-05-20 23:23:11 +01:00
|
|
|
println!("Missing BIOS file. If you don't have the original GBA BIOS, you can download an open-source bios from {}", OPEN_SOURCE_BIOS_URL);
|
2022-09-12 23:50:50 +01:00
|
|
|
std::process::exit(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
fn load_bios(bios_path: &Path) -> Box<[u8]> {
|
|
|
|
match read_bin_file(bios_path) {
|
|
|
|
Ok(bios) => bios.into_boxed_slice(),
|
|
|
|
_ => {
|
|
|
|
ask_download_bios();
|
|
|
|
unreachable!()
|
|
|
|
}
|
|
|
|
}
|
2020-01-11 15:28:25 +00:00
|
|
|
}
|
|
|
|
|
2020-01-16 18:07:14 +00:00
|
|
|
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
2024-03-22 21:12:34 +00:00
|
|
|
fs::create_dir_all(LOG_DIR)
|
|
|
|
.unwrap_or_else(|_| panic!("could not create log directory ({})", LOG_DIR));
|
2020-01-31 00:12:38 +00:00
|
|
|
flexi_logger::Logger::with_env_or_str("info")
|
2020-01-30 23:47:52 +00:00
|
|
|
.log_to_file()
|
|
|
|
.directory(LOG_DIR)
|
|
|
|
.duplicate_to_stderr(Duplicate::Debug)
|
|
|
|
.format_for_files(default_format)
|
|
|
|
.format_for_stderr(colored_default_format)
|
|
|
|
.start()
|
|
|
|
.unwrap();
|
|
|
|
|
2022-09-12 23:50:50 +01:00
|
|
|
let opts = options::Options::from_args();
|
2019-12-04 23:15:49 +00:00
|
|
|
|
2020-01-30 23:47:52 +00:00
|
|
|
info!("Initializing SDL2 context");
|
2020-01-11 23:29:36 +00:00
|
|
|
let sdl_context = sdl2::init().expect("failed to initialize sdl2");
|
2020-05-01 15:56:26 +01:00
|
|
|
|
2020-05-09 22:51:40 +01:00
|
|
|
let controller_subsystem = sdl_context.game_controller()?;
|
2020-05-11 18:18:42 +01:00
|
|
|
let controller_mappings =
|
2024-03-22 23:05:46 +00:00
|
|
|
include_str!("../../../external/gamecontrollerdb.txt");
|
2020-05-09 22:51:40 +01:00
|
|
|
controller_subsystem.load_mappings_from_read(&mut Cursor::new(controller_mappings))?;
|
|
|
|
|
|
|
|
let available_controllers = (0..controller_subsystem.num_joysticks()?)
|
|
|
|
.filter(|&id| controller_subsystem.is_game_controller(id))
|
|
|
|
.collect::<Vec<u32>>();
|
|
|
|
|
2020-05-23 09:45:25 +01:00
|
|
|
let mut active_controller = match available_controllers.first() {
|
2020-05-09 22:51:40 +01:00
|
|
|
Some(&id) => {
|
|
|
|
let controller = controller_subsystem.open(id)?;
|
|
|
|
info!("Found game controller: {}", controller.name());
|
|
|
|
Some(controller)
|
2020-05-11 18:18:42 +01:00
|
|
|
}
|
2020-05-09 22:51:40 +01:00
|
|
|
_ => {
|
|
|
|
info!("No game controllers were found");
|
|
|
|
None
|
2020-05-11 18:18:42 +01:00
|
|
|
}
|
2020-05-09 22:51:40 +01:00
|
|
|
};
|
|
|
|
|
2022-09-14 22:34:41 +01:00
|
|
|
let mut renderer = video::init(&sdl_context)?;
|
2022-09-12 23:50:50 +01:00
|
|
|
let (audio_interface, mut _sdl_audio_device) = audio::create_audio_player(&sdl_context)?;
|
2024-03-22 19:50:34 +00:00
|
|
|
let rom_name = opts.rom_name();
|
2020-01-16 18:07:14 +00:00
|
|
|
|
2022-09-12 23:50:50 +01:00
|
|
|
let bios_bin = load_bios(&opts.bios);
|
2020-01-31 12:47:27 +00:00
|
|
|
|
2022-09-14 22:34:41 +01:00
|
|
|
let mut gba = Box::new(GameBoyAdvance::new(
|
2022-09-12 23:50:50 +01:00
|
|
|
bios_bin.clone(),
|
|
|
|
opts.cartridge_from_opts()?,
|
|
|
|
audio_interface,
|
2022-09-14 22:34:41 +01:00
|
|
|
));
|
2020-05-17 18:12:31 +01:00
|
|
|
|
2022-09-18 22:10:55 +01:00
|
|
|
// let gba_raw_ptr = Box::into_raw(gba) as usize;
|
|
|
|
// static mut gba_raw: usize = 0;
|
|
|
|
// unsafe { gba_raw = gba_raw_ptr };
|
|
|
|
// let mut gba = unsafe {Box::from_raw(gba_raw_ptr as *mut GameBoyAdvance) };
|
|
|
|
|
|
|
|
// std::panic::set_hook(Box::new(|panic_info| {
|
|
|
|
// let gba = unsafe {Box::from_raw(gba_raw as *mut GameBoyAdvance) };
|
|
|
|
// println!("System crashed Oh No!!! {:?}", gba.cpu);
|
|
|
|
// let normal_panic = std::panic::take_hook();
|
|
|
|
// normal_panic(panic_info);
|
|
|
|
// }));
|
|
|
|
|
2022-09-12 23:50:50 +01:00
|
|
|
if opts.skip_bios {
|
2022-09-18 22:10:55 +01:00
|
|
|
println!("Skipping bios animation..");
|
2020-01-16 17:47:05 +00:00
|
|
|
gba.skip_bios();
|
|
|
|
}
|
|
|
|
|
2022-09-12 23:50:50 +01:00
|
|
|
if opts.gdbserver {
|
2022-10-04 20:45:06 +01:00
|
|
|
gba.start_gdbserver(opts.gdbserver_port);
|
2022-09-04 22:31:38 +01:00
|
|
|
}
|
2020-02-21 12:04:39 +00:00
|
|
|
|
2022-09-12 23:50:50 +01:00
|
|
|
let mut vsync = true;
|
2020-01-11 15:28:25 +00:00
|
|
|
let mut fps_counter = FpsCounter::default();
|
2022-09-14 22:34:41 +01:00
|
|
|
const FRAME_TIME: time::Duration = time::Duration::new(0, 1_000_000_000u32 / 60);
|
|
|
|
let mut event_pump = sdl_context.event_pump()?;
|
2020-01-11 15:28:25 +00:00
|
|
|
'running: loop {
|
2019-12-29 21:37:23 +00:00
|
|
|
let start_time = time::Instant::now();
|
|
|
|
for event in event_pump.poll_iter() {
|
|
|
|
match event {
|
|
|
|
Event::KeyDown {
|
2020-05-03 21:03:57 +01:00
|
|
|
scancode: Some(scancode),
|
2019-12-29 21:37:23 +00:00
|
|
|
..
|
2020-05-03 21:03:57 +01:00
|
|
|
} => match scancode {
|
2022-09-12 23:50:50 +01:00
|
|
|
Scancode::Space => vsync = false,
|
2022-09-17 00:19:46 +01:00
|
|
|
k => input::on_keyboard_key_down(gba.get_key_state_mut(), k),
|
2020-05-01 16:36:41 +01:00
|
|
|
},
|
2019-12-29 21:37:23 +00:00
|
|
|
Event::KeyUp {
|
2020-05-03 21:03:57 +01:00
|
|
|
scancode: Some(scancode),
|
2019-12-29 21:37:23 +00:00
|
|
|
..
|
2020-05-03 21:03:57 +01:00
|
|
|
} => match scancode {
|
2020-05-01 16:36:41 +01:00
|
|
|
#[cfg(feature = "debugger")]
|
2020-05-03 21:03:57 +01:00
|
|
|
Scancode::F1 => {
|
2021-06-08 23:24:31 +01:00
|
|
|
let mut debugger = Debugger::new();
|
2020-05-01 16:36:41 +01:00
|
|
|
info!("starting debugger...");
|
2021-06-08 23:24:31 +01:00
|
|
|
debugger
|
|
|
|
.repl(&mut gba, matches.value_of("script_file"))
|
|
|
|
.unwrap();
|
2020-05-01 16:36:41 +01:00
|
|
|
info!("ending debugger...")
|
|
|
|
}
|
2022-10-04 20:45:06 +01:00
|
|
|
Scancode::F2 => gba.start_gdbserver(opts.gdbserver_port),
|
2020-05-03 21:03:57 +01:00
|
|
|
Scancode::F5 => {
|
2020-05-01 16:36:41 +01:00
|
|
|
info!("Saving state ...");
|
|
|
|
let save = gba.save_state()?;
|
2022-09-12 23:50:50 +01:00
|
|
|
write_bin_file(&opts.savestate_path(), &save)?;
|
2020-05-01 16:36:41 +01:00
|
|
|
info!(
|
|
|
|
"Saved to {:?} ({})",
|
2022-09-12 23:50:50 +01:00
|
|
|
opts.savestate_path(),
|
2020-05-01 16:36:41 +01:00
|
|
|
bytesize::ByteSize::b(save.len() as u64)
|
|
|
|
);
|
|
|
|
}
|
2020-05-03 21:03:57 +01:00
|
|
|
Scancode::F9 => {
|
2022-09-12 23:50:50 +01:00
|
|
|
if opts.savestate_path().is_file() {
|
|
|
|
let save = read_bin_file(&opts.savestate_path())?;
|
|
|
|
info!("Restoring state from {:?}...", opts.savestate_path());
|
2022-10-04 20:45:06 +01:00
|
|
|
let (audio_interface, _sdl_audio_device_new) =
|
|
|
|
audio::create_audio_player(&sdl_context)?;
|
2022-09-18 22:10:55 +01:00
|
|
|
_sdl_audio_device = _sdl_audio_device_new;
|
|
|
|
let rom = opts.read_rom()?.into_boxed_slice();
|
2022-10-04 20:45:06 +01:00
|
|
|
gba = Box::new(GameBoyAdvance::from_saved_state(
|
|
|
|
&save,
|
|
|
|
bios_bin.clone(),
|
|
|
|
rom,
|
|
|
|
audio_interface,
|
|
|
|
)?);
|
2020-05-01 16:36:41 +01:00
|
|
|
info!("Restored!");
|
|
|
|
} else {
|
|
|
|
info!("Savestate not created, please create one by pressing F5");
|
|
|
|
}
|
|
|
|
}
|
2022-09-12 23:50:50 +01:00
|
|
|
Scancode::Space => vsync = true,
|
2022-09-17 00:19:46 +01:00
|
|
|
k => input::on_keyboard_key_up(gba.get_key_state_mut(), k),
|
2020-05-01 16:36:41 +01:00
|
|
|
},
|
2020-05-12 06:07:37 +01:00
|
|
|
Event::ControllerButtonDown { button, .. } => match button {
|
2022-09-12 23:50:50 +01:00
|
|
|
Button::RightStick => vsync = !vsync,
|
2022-09-17 00:19:46 +01:00
|
|
|
b => input::on_controller_button_down(gba.get_key_state_mut(), b),
|
2020-05-12 06:07:37 +01:00
|
|
|
},
|
2020-05-09 22:51:40 +01:00
|
|
|
Event::ControllerButtonUp { button, .. } => {
|
2022-09-17 00:19:46 +01:00
|
|
|
input::on_controller_button_up(gba.get_key_state_mut(), button);
|
2020-05-11 18:18:42 +01:00
|
|
|
}
|
|
|
|
Event::ControllerAxisMotion { axis, value, .. } => {
|
2022-09-17 00:19:46 +01:00
|
|
|
input::on_axis_motion(gba.get_key_state_mut(), axis, value);
|
2020-05-11 18:18:42 +01:00
|
|
|
}
|
2020-05-23 09:45:25 +01:00
|
|
|
Event::ControllerDeviceRemoved { which, .. } => {
|
|
|
|
let removed = if let Some(active_controller) = &active_controller {
|
|
|
|
active_controller.instance_id() == (which as i32)
|
|
|
|
} else {
|
|
|
|
false
|
|
|
|
};
|
|
|
|
if removed {
|
|
|
|
let name = active_controller
|
2024-03-22 19:50:34 +00:00
|
|
|
.map(|controller| Some(controller.name()))
|
2020-05-23 09:45:25 +01:00
|
|
|
.unwrap();
|
2024-03-22 19:50:34 +00:00
|
|
|
info!("Removing game controller: {:?}", name);
|
2020-05-23 09:45:25 +01:00
|
|
|
active_controller = None;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Event::ControllerDeviceAdded { which, .. } => {
|
|
|
|
if active_controller.is_none() {
|
|
|
|
let controller = controller_subsystem.open(which)?;
|
|
|
|
info!("Adding game controller: {}", controller.name());
|
|
|
|
active_controller = Some(controller);
|
|
|
|
}
|
|
|
|
}
|
2020-01-11 15:28:25 +00:00
|
|
|
Event::Quit { .. } => break 'running,
|
2024-03-22 21:12:34 +00:00
|
|
|
Event::DropFile { .. } => {
|
2022-09-12 23:50:50 +01:00
|
|
|
todo!("impl DropFile again")
|
2020-01-11 15:28:25 +00:00
|
|
|
}
|
2019-12-29 21:37:23 +00:00
|
|
|
_ => {}
|
2019-12-20 16:11:14 +00:00
|
|
|
}
|
2019-12-29 21:37:23 +00:00
|
|
|
}
|
2019-12-20 16:11:14 +00:00
|
|
|
|
2022-09-17 00:19:46 +01:00
|
|
|
if gba.is_debugger_attached() {
|
|
|
|
gba.debugger_run()
|
|
|
|
} else {
|
|
|
|
gba.frame();
|
|
|
|
}
|
2022-09-11 23:13:01 +01:00
|
|
|
renderer.render(gba.get_frame_buffer());
|
2019-12-09 21:37:46 +00:00
|
|
|
|
2019-12-29 21:37:23 +00:00
|
|
|
if let Some(fps) = fps_counter.tick() {
|
|
|
|
let title = format!("{} ({} fps)", rom_name, fps);
|
2022-09-11 23:13:01 +01:00
|
|
|
renderer.set_window_title(&title);
|
2019-12-29 21:37:23 +00:00
|
|
|
}
|
2019-12-04 23:15:49 +00:00
|
|
|
|
2022-09-12 23:50:50 +01:00
|
|
|
if vsync {
|
2019-12-29 21:37:23 +00:00
|
|
|
let time_passed = start_time.elapsed();
|
2022-09-14 22:34:41 +01:00
|
|
|
let delay = FRAME_TIME.checked_sub(time_passed);
|
2019-12-29 21:37:23 +00:00
|
|
|
match delay {
|
|
|
|
None => {}
|
|
|
|
Some(delay) => {
|
|
|
|
spin_sleep::sleep(delay);
|
|
|
|
}
|
|
|
|
};
|
2019-12-04 23:15:49 +00:00
|
|
|
}
|
|
|
|
}
|
2020-01-16 18:07:14 +00:00
|
|
|
|
|
|
|
Ok(())
|
2019-12-04 23:15:49 +00:00
|
|
|
}
|