This repository has been archived on 2024-06-01. You can view files and clone it, but cannot push or open issues or pull requests.
rustboyadvance-ng/bindings/rustboyadvance-jni/src/lib.rs
Michel Heily ba2eff82ac platform/android: Big re-write of native interface
Mainly convert mainloop and audio thread into native code for
performance increase. (Calling into JNI every frame was costy)

The code was cleaned up quite a bit, but I may have introduced new bugs
in this process :<


Former-commit-id: fdbc21b5ab39f3d2e36647fd1177dc9a84a16980
Former-commit-id: ac765dbee8c994e1b69cc694846511837c2685b9
2020-09-30 00:27:00 +03:00

294 lines
7.7 KiB
Rust

mod audio;
mod emulator;
/// JNI Bindings for rustboyadvance
///
mod rom_helper;
use emulator::EmulatorContext;
use std::os::raw::c_void;
use jni::objects::*;
use jni::sys::*;
use jni::{JNIEnv, JavaVM};
#[macro_use]
extern crate log;
#[cfg(target_os = "android")]
use android_log;
#[cfg(not(target_os = "android"))]
use env_logger;
use rustboyadvance_core::prelude::*;
static mut DID_LOAD: bool = false;
const NATIVE_EXCEPTION_CLASS: &'static str =
"com/mrmichel/rustboyadvance/EmulatorBindings/NativeBindingException";
fn save_state(env: &JNIEnv, gba: &mut GameBoyAdvance) -> Result<jbyteArray, String> {
let saved_state = gba
.save_state()
.map_err(|e| format!("failed to serielize state, error: {:?}", e))?;
let byte_array = env
.byte_array_from_slice(&saved_state)
.map_err(|e| format!("failed to create byte array, error: {:?}", e))?;
Ok(byte_array)
}
fn load_state(env: &JNIEnv, gba: &mut GameBoyAdvance, state: jbyteArray) -> Result<(), String> {
let state = env
.convert_byte_array(state)
.map_err(|e| format!("failed to convert byte array, error: {:?}", e))?;
gba.restore_state(&state)
.map_err(|e| format!("failed to restore state, error: {:?}", e))
}
#[allow(non_snake_case)]
pub mod bindings {
use super::*;
#[inline(always)]
unsafe fn cast_ctx<'a>(ctx: jlong) -> &'a mut EmulatorContext {
&mut (*(ctx as *mut EmulatorContext))
}
#[no_mangle]
pub unsafe extern "C" fn JNI_OnLoad(vm: *mut JavaVM, _reserved: *mut c_void) -> jint {
if DID_LOAD {
return JNI_VERSION_1_6;
}
#[cfg(target_os = "android")]
android_log::init("EmulatorBindings").unwrap();
#[cfg(not(target_os = "android"))]
env_logger::init();
debug!("library loaded and logger initialized!");
debug!("JVM: {:?}", vm);
DID_LOAD = true;
JNI_VERSION_1_6
}
#[no_mangle]
pub unsafe extern "C" fn Java_com_mrmichel_rustboyadvance_EmulatorBindings_openEmulator(
env: JNIEnv,
_obj: JClass,
bios: jbyteArray,
rom: jbyteArray,
renderer_obj: JObject,
audio_player_obj: JObject,
keypad_obj: JObject,
save_file: JString,
skip_bios: jboolean,
) -> jlong {
match EmulatorContext::native_open_context(
&env,
bios,
rom,
renderer_obj,
audio_player_obj,
keypad_obj,
save_file,
skip_bios,
) {
Ok(ctx) => Box::into_raw(Box::new(ctx)) as jlong,
Err(msg) => {
env.throw_new(NATIVE_EXCEPTION_CLASS, msg).unwrap();
-1
}
}
}
#[no_mangle]
pub unsafe extern "C" fn Java_com_mrmichel_rustboyadvance_EmulatorBindings_openSavedState(
env: JNIEnv,
_obj: JClass,
state: jbyteArray,
renderer_obj: JObject,
audio_player_obj: JObject,
keypad_obj: JObject,
) -> jlong {
match EmulatorContext::native_open_saved_state(
&env,
state,
renderer_obj,
audio_player_obj,
keypad_obj,
) {
Ok(ctx) => Box::into_raw(Box::new(ctx)) as jlong,
Err(msg) => {
env.throw_new(NATIVE_EXCEPTION_CLASS, msg).unwrap();
-1
}
}
}
#[no_mangle]
pub unsafe extern "C" fn Java_com_mrmichel_rustboyadvance_EmulatorBindings_closeEmulator(
_env: JNIEnv,
_obj: JClass,
ctx: jlong,
) {
info!("waiting for emulation thread to stop");
{
let ctx = cast_ctx(ctx);
ctx.request_stop();
while !ctx.is_stopped() {}
}
info!("destroying context {:#x}", ctx);
// consume the wrapped content
let _ = Box::from_raw(ctx as *mut EmulatorContext);
}
#[no_mangle]
pub unsafe extern "C" fn Java_com_mrmichel_rustboyadvance_EmulatorBindings_runMainLoop(
env: JNIEnv,
_obj: JClass,
ctx: jlong,
) {
let ctx = cast_ctx(ctx);
match ctx.native_run(&env) {
Ok(_) => {}
Err(err) => {
env.throw_new(NATIVE_EXCEPTION_CLASS, format!("Error: {:?}", err))
.unwrap();
}
}
}
#[no_mangle]
pub unsafe extern "C" fn Java_com_mrmichel_rustboyadvance_EmulatorBindings_pause(
_env: JNIEnv,
_obj: JClass,
ctx: jlong,
) {
let ctx = cast_ctx(ctx);
ctx.pause();
}
#[no_mangle]
pub unsafe extern "C" fn Java_com_mrmichel_rustboyadvance_EmulatorBindings_resume(
_env: JNIEnv,
_obj: JClass,
ctx: jlong,
) {
let ctx = cast_ctx(ctx);
ctx.resume();
}
#[no_mangle]
pub unsafe extern "C" fn Java_com_mrmichel_rustboyadvance_EmulatorBindings_setTurbo(
_env: JNIEnv,
_obj: JClass,
ctx: jlong,
turbo: jboolean,
) {
info!("setTurbo called!");
let ctx = cast_ctx(ctx);
ctx.set_turbo(turbo != 0);
}
#[no_mangle]
pub unsafe extern "C" fn Java_com_mrmichel_rustboyadvance_EmulatorBindings_stop(
_env: JNIEnv,
_obj: JClass,
ctx: jlong,
) {
let ctx = cast_ctx(ctx);
ctx.request_stop();
while !ctx.is_stopped() {}
}
#[no_mangle]
pub unsafe extern "C" fn Java_com_mrmichel_rustboyadvance_EmulatorBindings_getFrameBuffer(
env: JNIEnv,
_obj: JClass,
ctx: jlong,
) -> jintArray {
let ctx = cast_ctx(ctx);
ctx.native_get_framebuffer(&env)
}
#[no_mangle]
pub unsafe extern "C" fn Java_com_mrmichel_rustboyadvance_EmulatorBindings_saveState(
env: JNIEnv,
_obj: JClass,
ctx: jlong,
) -> jbyteArray {
let ctx = cast_ctx(ctx);
ctx.pause();
let (_lock, gba) = ctx.lock_and_get_gba();
match save_state(&env, gba) {
Ok(result) => {
drop(_lock);
drop(gba);
ctx.resume();
return result;
}
Err(msg) => {
env.throw_new(NATIVE_EXCEPTION_CLASS, msg).unwrap();
return JObject::null().into_inner();
}
}
}
#[no_mangle]
pub unsafe extern "C" fn Java_com_mrmichel_rustboyadvance_EmulatorBindings_loadState(
env: JNIEnv,
_obj: JClass,
ctx: jlong,
state: jbyteArray,
) {
let ctx = cast_ctx(ctx);
ctx.pause();
let (_lock, gba) = ctx.lock_and_get_gba();
match load_state(&env, gba, state) {
Ok(_) => {
drop(_lock);
drop(gba);
ctx.resume();
}
Err(msg) => env.throw_new(NATIVE_EXCEPTION_CLASS, msg).unwrap(),
}
}
#[no_mangle]
pub unsafe extern "C" fn Java_com_mrmichel_rustboyadvance_EmulatorBindings_getGameTitle(
env: JNIEnv,
_obj: JClass,
ctx: jlong,
) -> jstring {
let ctx = cast_ctx(ctx);
env.new_string(ctx.gba.get_game_title())
.unwrap()
.into_inner()
}
#[no_mangle]
pub unsafe extern "C" fn Java_com_mrmichel_rustboyadvance_EmulatorBindings_getGameCode(
env: JNIEnv,
_obj: JClass,
ctx: jlong,
) -> jstring {
let ctx = cast_ctx(ctx);
env.new_string(ctx.gba.get_game_code())
.unwrap()
.into_inner()
}
#[no_mangle]
pub unsafe extern "C" fn Java_com_mrmichel_rustboyadvance_EmulatorBindings_log(
_env: JNIEnv,
_obj: JClass,
ctx: jlong,
) {
let ctx = cast_ctx(ctx);
info!("CPU LOG: {:#x?}", ctx.gba.cpu);
}
}