From eb6f11ef25f7aee269e05719827078f91e46a2b9 Mon Sep 17 00:00:00 2001 From: alloncm Date: Sat, 4 May 2024 16:28:50 +0300 Subject: [PATCH] Extract libretro devices and logging to another modules --- libretro/src/devices.rs | 70 ++++++++++++++++++++ libretro/src/lib.rs | 139 +++++++--------------------------------- libretro/src/logging.rs | 41 ++++++++++++ 3 files changed, 134 insertions(+), 116 deletions(-) create mode 100644 libretro/src/devices.rs create mode 100644 libretro/src/logging.rs diff --git a/libretro/src/devices.rs b/libretro/src/devices.rs new file mode 100644 index 00000000..e68489e4 --- /dev/null +++ b/libretro/src/devices.rs @@ -0,0 +1,70 @@ +use std::{ffi::c_void, mem::size_of}; + +use libretro_sys::*; + +use magenboy_common::audio::*; +use magenboy_core::{apu::audio_device::*, keypad::{button::*, joypad::*, joypad_provider::*}, ppu::{gb_ppu::*, gfx_device::*}, GB_FREQUENCY}; + +use super::GAMEBOY_CORE; + +pub struct RetroGfxDevice; +impl GfxDevice for RetroGfxDevice{ + fn swap_buffer(&mut self, buffer:&[Pixel; SCREEN_HEIGHT * SCREEN_WIDTH]) { + unsafe{(GAMEBOY_CORE.video_cb.unwrap())(buffer.as_ptr() as *const c_void, SCREEN_WIDTH as u32, SCREEN_HEIGHT as u32, SCREEN_WIDTH * size_of::())}; + } +} + +pub struct RetroJoypadProvider; +impl JoypadProvider for RetroJoypadProvider{ + fn provide(&mut self, joypad:&mut Joypad) { + unsafe{ + (GAMEBOY_CORE.input_poll_cb.unwrap())(); + + let input_cb: unsafe extern fn(port:u32, device:u32, index:u32, id:u32) -> i16 = GAMEBOY_CORE.input_cb.unwrap(); + joypad.buttons[Button::A as usize] = input_cb(0, DEVICE_JOYPAD, 0, DEVICE_ID_JOYPAD_A) != 0 || + input_cb(0, DEVICE_JOYPAD, 0, DEVICE_ID_JOYPAD_X) != 0; + joypad.buttons[Button::B as usize] = input_cb(0, DEVICE_JOYPAD, 0, DEVICE_ID_JOYPAD_B) != 0 || + input_cb(0, DEVICE_JOYPAD, 0, DEVICE_ID_JOYPAD_Y) != 0; + joypad.buttons[Button::Start as usize] = input_cb(0, DEVICE_JOYPAD, 0, DEVICE_ID_JOYPAD_START) != 0; + joypad.buttons[Button::Select as usize] = input_cb(0, DEVICE_JOYPAD, 0, DEVICE_ID_JOYPAD_SELECT) != 0; + joypad.buttons[Button::Up as usize] = input_cb(0, DEVICE_JOYPAD, 0, DEVICE_ID_JOYPAD_UP) != 0; + joypad.buttons[Button::Down as usize] = input_cb(0, DEVICE_JOYPAD, 0, DEVICE_ID_JOYPAD_DOWN) != 0; + joypad.buttons[Button::Right as usize] = input_cb(0, DEVICE_JOYPAD, 0, DEVICE_ID_JOYPAD_RIGHT) != 0; + joypad.buttons[Button::Left as usize] = input_cb(0, DEVICE_JOYPAD, 0, DEVICE_ID_JOYPAD_LEFT) != 0; + } + } +} + +pub struct RetroAudioDevice{ + resampler: ManualAudioResampler +} + +impl RetroAudioDevice{ + pub const OUTPUT_FREQUENCY:u32 = 48000; +} + +impl Default for RetroAudioDevice{ + fn default()->Self{ + Self { resampler: ManualAudioResampler::new(GB_FREQUENCY, Self::OUTPUT_FREQUENCY) } + } +} + +impl AudioDevice for RetroAudioDevice{ + fn push_buffer(&mut self, buffer:&[StereoSample; BUFFER_SIZE]) { + let mut resampled = self.resampler.resample(buffer); + unsafe{GAMEBOY_CORE.audio_buffer.append(&mut resampled)}; + } +} + +impl RetroAudioDevice{ + pub unsafe fn push_audio_buffer_to_libretro(){ + let mut remaining_frames = GAMEBOY_CORE.audio_buffer.len(); + let mut buffer_pos_ptr = GAMEBOY_CORE.audio_buffer.as_ptr() as *const Sample; + while remaining_frames > 0 { + let uploaded_frames = (GAMEBOY_CORE.audio_cb.unwrap())(buffer_pos_ptr, remaining_frames); + remaining_frames -= uploaded_frames; + buffer_pos_ptr = buffer_pos_ptr.add(uploaded_frames); + } + GAMEBOY_CORE.audio_buffer.clear(); + } +} \ No newline at end of file diff --git a/libretro/src/lib.rs b/libretro/src/lib.rs index 7fedfa4d..1e69d151 100644 --- a/libretro/src/lib.rs +++ b/libretro/src/lib.rs @@ -1,10 +1,13 @@ -use std::{ffi::{c_char, c_uint, c_void, CString}, mem::{size_of, MaybeUninit}, ptr::null_mut, slice, sync::{Mutex, OnceLock}}; +mod devices; +mod logging; + +use std::{ffi::{c_char, c_uint, c_void}, mem::MaybeUninit, ptr::null_mut, slice}; use libretro_sys::*; -use log::Log; -use magenboy_common::audio::*; -use magenboy_core::{apu::audio_device::*, keypad::{button::Button, joypad::*, joypad_provider::JoypadProvider}, machine::{gameboy::GameBoy, mbc_initializer}, mmu::external_memory_bus::Bootrom, ppu::{gb_ppu::*, gfx_device::*}, GB_FREQUENCY}; +use magenboy_core::{apu::audio_device::*, machine::{gameboy::GameBoy, mbc_initializer}, mmu::external_memory_bus::Bootrom, ppu::gb_ppu::*}; + +use crate::{devices::*, logging::*}; pub struct MagenBoyRetroCore<'a>{ gameboy: Option>, @@ -17,113 +20,26 @@ pub struct MagenBoyRetroCore<'a>{ environment_cb: Option } -struct RetroGfxDevice; -impl GfxDevice for RetroGfxDevice{ - fn swap_buffer(&mut self, buffer:&[Pixel; SCREEN_HEIGHT * SCREEN_WIDTH]) { - unsafe{(GAMEBOY_CORE.video_cb.unwrap())(buffer.as_ptr() as *const c_void, SCREEN_WIDTH as u32, SCREEN_HEIGHT as u32, SCREEN_WIDTH * size_of::())}; - } -} - -struct RetroJoypadProvider; - -impl JoypadProvider for RetroJoypadProvider{ - fn provide(&mut self, joypad:&mut Joypad) { - unsafe{ - (GAMEBOY_CORE.input_poll_cb.unwrap())(); - - let input_cb: unsafe extern fn(port:u32, device:u32, index:u32, id:u32) -> i16 = GAMEBOY_CORE.input_cb.unwrap(); - joypad.buttons[Button::A as usize] = input_cb(0, DEVICE_JOYPAD, 0, DEVICE_ID_JOYPAD_A) != 0 || - input_cb(0, DEVICE_JOYPAD, 0, DEVICE_ID_JOYPAD_X) != 0; - joypad.buttons[Button::B as usize] = input_cb(0, DEVICE_JOYPAD, 0, DEVICE_ID_JOYPAD_B) != 0 || - input_cb(0, DEVICE_JOYPAD, 0, DEVICE_ID_JOYPAD_Y) != 0; - joypad.buttons[Button::Start as usize] = input_cb(0, DEVICE_JOYPAD, 0, DEVICE_ID_JOYPAD_START) != 0; - joypad.buttons[Button::Select as usize] = input_cb(0, DEVICE_JOYPAD, 0, DEVICE_ID_JOYPAD_SELECT) != 0; - joypad.buttons[Button::Up as usize] = input_cb(0, DEVICE_JOYPAD, 0, DEVICE_ID_JOYPAD_UP) != 0; - joypad.buttons[Button::Down as usize] = input_cb(0, DEVICE_JOYPAD, 0, DEVICE_ID_JOYPAD_DOWN) != 0; - joypad.buttons[Button::Right as usize] = input_cb(0, DEVICE_JOYPAD, 0, DEVICE_ID_JOYPAD_RIGHT) != 0; - joypad.buttons[Button::Left as usize] = input_cb(0, DEVICE_JOYPAD, 0, DEVICE_ID_JOYPAD_LEFT) != 0; - } - } -} - -struct RetroAudioDevice{ - resampler: ManualAudioResampler -} - -impl RetroAudioDevice{ - const OUTPUT_FREQUENCY:u32 = 48000; -} - -impl Default for RetroAudioDevice{ - fn default()->Self{ - Self { resampler: ManualAudioResampler::new(GB_FREQUENCY, Self::OUTPUT_FREQUENCY) } - } -} - -impl AudioDevice for RetroAudioDevice{ - fn push_buffer(&mut self, buffer:&[StereoSample; BUFFER_SIZE]) { - let mut resampled = self.resampler.resample(buffer); - unsafe{GAMEBOY_CORE.audio_buffer.append(&mut resampled)}; - } -} - -struct RetroLogger{ - retro_logger: Option> -} - -impl RetroLogger{ - pub fn init(max_log_level:log::LevelFilter, log_cb: Option){ - static LOGGER: OnceLock = OnceLock::new(); - let logger = match log_cb{ - Some(cb) => LOGGER.get_or_init(||Self{ retro_logger: Some(Mutex::new(cb))}), - None => LOGGER.get_or_init(||Self { retro_logger: None }), - }; - log::set_logger(logger).unwrap(); - log::set_max_level(max_log_level); - } -} - -impl Log for RetroLogger{ - fn enabled(&self, metadata: &log::Metadata) -> bool { - return self.retro_logger.is_some() && metadata.level() >= log::max_level(); - } - - fn log(&self, record: &log::Record) { - let logger = self.retro_logger.as_ref().unwrap().lock().unwrap(); - let level = match record.level(){ - log::Level::Error => LogLevel::Error, - log::Level::Warn => LogLevel::Warn, - log::Level::Info => LogLevel::Info, - log::Level::Debug | - log::Level::Trace => LogLevel::Debug, - }; - let message = CString::new(format!("{}\n", record.args())).unwrap(); - unsafe{(logger.log)(level, message.as_ptr())}; - } - - fn flush(&self) {} -} - -static mut GAMEBOY_CORE: MagenBoyRetroCore = MagenBoyRetroCore{ +pub(crate) static mut GAMEBOY_CORE: MagenBoyRetroCore = MagenBoyRetroCore{ gameboy: None, save_data_fat_ptr: None, audio_buffer:Vec::new(), video_cb: None, audio_cb: None, input_poll_cb: None, input_cb: None, environment_cb: None, }; -const NAME:*const c_char = b"MagenBoy\0".as_ptr() as _; -const VERSION_SIZE:usize = magenboy_common::VERSION.len() + 1; -const VERSION:[u8;VERSION_SIZE] = { - let mut result:[u8;VERSION_SIZE] = [0;VERSION_SIZE]; - let mut i = 0; - while i < VERSION_SIZE - 1{ - result[i] = magenboy_common::VERSION.as_bytes()[i]; - i +=1 ; - } - result[VERSION_SIZE - 1] = b'\0'; - result -}; -const VALID_EXTENSIONS:*const c_char = b"gb|gbc\0".as_ptr() as _; - #[no_mangle] pub unsafe extern "C" fn retro_get_system_info(system_info: *mut SystemInfo){ + const NAME:*const c_char = b"MagenBoy\0".as_ptr() as _; + const VERSION_SIZE:usize = magenboy_common::VERSION.len() + 1; + const VERSION:[u8;VERSION_SIZE] = { + let mut result:[u8;VERSION_SIZE] = [0;VERSION_SIZE]; + let mut i = 0; + while i < VERSION_SIZE - 1{ + result[i] = magenboy_common::VERSION.as_bytes()[i]; + i +=1 ; + } + result[VERSION_SIZE - 1] = b'\0'; + result + }; + const VALID_EXTENSIONS:*const c_char = b"gb|gbc\0".as_ptr() as _; + std::ptr::write_bytes(system_info, 0, 1); (*system_info).library_name = NAME; (*system_info).library_version = VERSION.as_ptr() as *const c_char; @@ -209,16 +125,7 @@ pub unsafe extern "C" fn retro_get_memory_size(id:c_uint)->isize{ #[no_mangle] pub unsafe extern "C" fn retro_run(){ GAMEBOY_CORE.gameboy.as_mut().unwrap().cycle_frame(); - - // push audio buffer - let mut remaining_frames = GAMEBOY_CORE.audio_buffer.len(); - let mut buffer_pos_ptr = GAMEBOY_CORE.audio_buffer.as_ptr() as *const Sample; - while remaining_frames > 0 { - let uploaded_frames = (GAMEBOY_CORE.audio_cb.unwrap())(buffer_pos_ptr, remaining_frames); - remaining_frames -= uploaded_frames; - buffer_pos_ptr = buffer_pos_ptr.add(uploaded_frames); - } - GAMEBOY_CORE.audio_buffer.clear(); + RetroAudioDevice::push_audio_buffer_to_libretro(); } #[no_mangle] pub extern "C" fn retro_load_game_special(_:c_uint, _:*const GameInfo, _:isize)->bool{false} diff --git a/libretro/src/logging.rs b/libretro/src/logging.rs new file mode 100644 index 00000000..d83eb2f1 --- /dev/null +++ b/libretro/src/logging.rs @@ -0,0 +1,41 @@ +use std::{ffi::CString ,sync::{Mutex, OnceLock}}; + +use libretro_sys::*; +use log::Log; + +pub struct RetroLogger{ + retro_logger: Option> +} + +impl RetroLogger{ + pub fn init(max_log_level:log::LevelFilter, log_cb: Option){ + static LOGGER: OnceLock = OnceLock::new(); + let logger = match log_cb{ + Some(cb) => LOGGER.get_or_init(||Self{ retro_logger: Some(Mutex::new(cb))}), + None => LOGGER.get_or_init(||Self { retro_logger: None }), + }; + log::set_logger(logger).unwrap(); + log::set_max_level(max_log_level); + } +} + +impl Log for RetroLogger{ + fn enabled(&self, metadata: &log::Metadata) -> bool { + return self.retro_logger.is_some() && metadata.level() >= log::max_level(); + } + + fn log(&self, record: &log::Record) { + let logger = self.retro_logger.as_ref().unwrap().lock().unwrap(); + let level = match record.level(){ + log::Level::Error => LogLevel::Error, + log::Level::Warn => LogLevel::Warn, + log::Level::Info => LogLevel::Info, + log::Level::Debug | + log::Level::Trace => LogLevel::Debug, + }; + let message = CString::new(format!("{}\n", record.args())).unwrap(); + unsafe{(logger.log)(level, message.as_ptr())}; + } + + fn flush(&self) {} +}