Skip to content

Commit

Permalink
Self CR fixes:
Browse files Browse the repository at this point in the history
Remove the mutex from the libretro logger implementation and fix log filter

Add docs for the Libretro frontend
  • Loading branch information
alloncm committed Jun 21, 2024
1 parent f97e8ac commit 3b7c223
Show file tree
Hide file tree
Showing 5 changed files with 86 additions and 11 deletions.
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,10 @@ rustup component add llvm-tools-preview

3. Builds the image

### Libretro

See - [LibretroDocs](docs/Libretro.md)

## Running

### Desktop
Expand Down
66 changes: 66 additions & 0 deletions docs/Libretro.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
# Libretro

Since libretro offers cross platform support out of the box (mainly using [RetroArch](https://github.com/libretro/RetroArch))
I imlemented a libretro core for MagenBoy mainly to play MagenBoy on android.

## How to build

The command is `cargo make libretro`, which you'll need to have `cargo-make` to run (as the readme tells).

This command will build both the native desktop target and the `aarch64-linux-android` target and output a `.info` file
along with a dynamic library at the same directory.

In order to build android you'll also need to install the android SDK and enable the NDK package.

Make sure to have an environment variable named `ANDROID_NDK_HOME` and set to the NDK install path,
for example - `export ANDROID_NDK_HOME=/home/alloncm/Android/Sdk/ndk`.

## How to install

I'll explain how to install on RetroArch, if you are using another frontend - good luck!

First of course youll need to download RetroArch for your platform.

### Desktop

You can always run the rom from the command line using the generated dynamic library:

```sh
retroarch -v -L target/release/libmagenboy_libretro.so path_to_rom
```

But in order to install it as a core youll first need to suplly RetroArch with the metadata for the core (otherwise it will install in incorrectly).

1. Choose: `Settings -> Directory -> Core Info`, this is the path where RetroArch searches for the mathcing `.info` files for the cores, you need to copy the `.info` file to this directory.

2. Install the core, choose: `Load Core -> Install or Restore Core` and then navigate to the `.so` file and select it.

3. Verify installation by choosing: `Information -> Core Information` and check that the metadata is correct.

If there is no `Core Information` option there was an error in the installation process, manually delete the `.so` from the default directory configured in `Settings -> Directory -> Core` and start again.

Now you should be able to load roms and use the whole other options of RetroArch along with MagenBoy!

### Android

This instalation is a bit more complicated then the above since I assume you dont have root on the device (like I dont have) so will need to do some workarounds.

The installation process is kind of the same as the desktop except that without root user you can't execute the first step because you dont have access the internal directories the app uses by default.

In order to workaround this limitation we will change the default `.info` directory from an internal one to an external one in order to gain access

Choose: `Settings -> Directory -> Core Info` and set the path to another folder which you have a write access to (using a file explorer of your choice)
for example `/storage/emulated/0/RetroArch/info`.

Copy the `.info` to this folder, and continue as described in the desktop installation.

> Note: If you have root access just copy the `.info` to the configured directory.
> Warning: Do not attempt this workaround with the Core section (the `.so` files), since on android only certain directories can contain executable files.
## Input mapping

Each GameBoy button is mapped to the corresponding button in Libretro's joypad except for the A and B buttons,
A is mapped to both A and X and B is mapped to both B and Y.

I do that in order to make pressing them easier, especially on mobile.
6 changes: 3 additions & 3 deletions libretro/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ pub unsafe extern "C" fn retro_get_system_info(system_info: *mut SystemInfo){
};
const VALID_EXTENSIONS:*const c_char = b"gb|gbc\0".as_ptr() as _;

std::ptr::write_bytes(system_info, 0, 1);
std::ptr::write_bytes(system_info, 0, 1); //memset
(*system_info).library_name = NAME;
(*system_info).library_version = VERSION.as_ptr() as *const c_char;
(*system_info).need_fullpath = false;
Expand All @@ -48,7 +48,7 @@ pub unsafe extern "C" fn retro_get_system_info(system_info: *mut SystemInfo){

#[no_mangle]
pub unsafe extern "C" fn retro_get_system_av_info(av_info: *mut SystemAvInfo){
std::ptr::write_bytes(av_info, 0, 1);
std::ptr::write_bytes(av_info, 0, 1); //memset
(*av_info).timing.fps = 60.0;
(*av_info).timing.sample_rate = RetroAudioDevice::OUTPUT_FREQUENCY as f64;
(*av_info).geometry.base_width = SCREEN_WIDTH as c_uint;
Expand Down Expand Up @@ -90,7 +90,7 @@ pub unsafe extern "C" fn retro_load_game(game_info: *const GameInfo)->bool{

let mut pixel_format = PixelFormat::RGB565.to_uint();
if !(RETRO_CORE_CTX.environment_cb.unwrap())(ENVIRONMENT_SET_PIXEL_FORMAT, &mut pixel_format as *mut u32 as *mut c_void){
log::error!("RGB565 is not supported, cant initialize MagenBoy");
log::error!("RGB565 is not supported, can't initialize MagenBoy");
return false;
}

Expand Down
20 changes: 12 additions & 8 deletions libretro/src/logging.rs
Original file line number Diff line number Diff line change
@@ -1,31 +1,31 @@
use std::{ffi::CString ,sync::{Mutex, OnceLock}};
use std::{ffi::{c_char, CString} ,sync::OnceLock};

use libretro_sys::*;
use log::Log;

pub struct RetroLogger{
retro_logger: Option<Mutex<LogCallback>>
retro_logger: LogCallback
}

impl RetroLogger{
pub fn init(max_log_level:log::LevelFilter, log_cb: Option<LogCallback>){
static LOGGER: OnceLock<RetroLogger> = 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 }),
Some(cb) => LOGGER.get_or_init(||Self{ retro_logger: cb}),
None => LOGGER.get_or_init(||Self { retro_logger: LogCallback{log: logcb_fallback} }),
};
log::set_logger(logger).unwrap();
log::set_logger(logger).expect("Failed to set logger");
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();
return metadata.level() <= log::max_level();
}

fn log(&self, record: &log::Record) {
let logger = self.retro_logger.as_ref().unwrap().lock().unwrap();
if !self.enabled(record.metadata()) {return}
let level = match record.level(){
log::Level::Error => LogLevel::Error,
log::Level::Warn => LogLevel::Warn,
Expand All @@ -34,8 +34,12 @@ impl Log for RetroLogger{
log::Level::Trace => LogLevel::Debug,
};
let message = CString::new(format!("{}\n", record.args())).unwrap();
unsafe{(logger.log)(level, message.as_ptr())};
unsafe{(self.retro_logger.log)(level, message.as_ptr())};
}

fn flush(&self) {}
}

// Empty callback in case there is no logbc on the platform
// According to the docs we can also write to the stderr instead
unsafe extern "C" fn logcb_fallback(_: LogLevel, _: *const c_char){}
1 change: 1 addition & 0 deletions rpi/src/bin/baremetal/logging.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ impl Write for UartDevice{
}
}

// TODO: Fix this logger according to the libretro one
pub struct UartLogger{
uart_mutex:Mutex<UartDevice>
}
Expand Down

0 comments on commit 3b7c223

Please sign in to comment.