diff --git a/Cargo.toml b/Cargo.toml index 6c1dd65..425955b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,6 +8,7 @@ edition = "2018" members = [ "rustboyadvance-sdl2", "rustboyadvance-minifb", + "rustboyadvance-jni", ] [dependencies] @@ -24,7 +25,7 @@ hexdump = "0.1.0" time = "0.2.6" bitfield = "0.13.1" bitflags = "1.2.1" -zip = "0.5.4" +zip = {version = "0.5.4", default-features = false, features = ["deflate", "time"]} ctrlc = "3.1.3" bit-set = "0.5.1" debug_stub_derive = "0.3.0" diff --git a/rustboyadvance-jni/Cargo.toml b/rustboyadvance-jni/Cargo.toml new file mode 100644 index 0000000..9671000 --- /dev/null +++ b/rustboyadvance-jni/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "rustboyadvance-jni" +version = "0.1.0" +authors = ["Michel Heily "] +edition = "2018" +description = "JNI bindings for rustboyadvance core" +publish = false + +[lib] +crate-type = ["staticlib", "cdylib"] + +[dependencies] +rustboyadvance-ng = {path = "../"} +jni = { version = "0.14", default-features = false } +log = "0.4.8" +env_logger = "0.7.1" diff --git a/rustboyadvance-jni/src/lib.rs b/rustboyadvance-jni/src/lib.rs new file mode 100644 index 0000000..7ee397d --- /dev/null +++ b/rustboyadvance-jni/src/lib.rs @@ -0,0 +1,159 @@ +/// JNI Bindings to rustboyadvance +/// For use with the following java class +/// +/// package com.mrmichel.rustboyadvance; +//// +/// public class EmulatorInterface { +/// +/// public static native int loadRom(String romPath); +/// +/// public static native int openEmulator(String biosPath); +/// +/// public static native void closeEmulator(); +/// +/// public static native int runFrame(int[] frame_buffer); +/// +/// static { +/// System.loadLibrary("rustboyadvance_jni"); +/// } +/// } +use std::cell::RefCell; +use std::os::raw::c_void; +use std::rc::Rc; + +#[macro_use] +extern crate log; + +use env_logger; + +use rustboyadvance_ng::prelude::*; + +struct Hardware { + frame_buffer: [u32; DISPLAY_WIDTH * DISPLAY_HEIGHT], +} + +impl Hardware { + fn new() -> Hardware { + Hardware { + frame_buffer: [0; DISPLAY_WIDTH * DISPLAY_HEIGHT], + } + } +} + +impl VideoInterface for Hardware { + fn render(&mut self, buffer: &[u32]) { + self.frame_buffer[..].clone_from_slice(buffer); + } +} + +impl AudioInterface for Hardware {} +impl InputInterface for Hardware {} + +struct Emulator { + hwif: Rc>, + gba: GameBoyAdvance, +} + +#[allow(non_snake_case)] +pub mod android { + use super::*; + + use std::path::Path; + + use jni; + + use jni::objects::{JClass, JString}; + use jni::sys::{jint, jintArray, JNI_VERSION_1_6}; + use jni::{JNIEnv, JavaVM}; + + static mut EMULATOR: Option = None; + static mut ROM: Option = None; + + #[no_mangle] + pub unsafe extern "C" fn JNI_OnLoad(_vm: *mut JavaVM, _reserved: *mut c_void) -> jint { + env_logger::init(); + debug!("library loaded!"); + + JNI_VERSION_1_6 + } + + #[no_mangle] + pub unsafe extern "C" fn Java_com_mrmichel_rustboyadvance_EmulatorInterface_loadRom( + env: JNIEnv, + _: JClass, + rom_path: JString, + ) -> jint { + if EMULATOR.is_some() { + error!("can't load rom while emulator is running"); + return -1; + } + + let rom_path: String = env + .get_string(rom_path) + .expect("invalid rom path object") + .into(); + let gamepak = GamepakBuilder::new() + .file(&Path::new(&rom_path)) + .build() + .expect("failed to load rom"); + + info!("Loaded ROM file {:?}", gamepak.header); + ROM = Some(gamepak); + + 0 + } + + #[no_mangle] + pub unsafe extern "C" fn Java_com_mrmichel_rustboyadvance_EmulatorInterface_openEmulator( + env: JNIEnv, + _: JClass, + bios_path: JString, + ) -> jint { + if let Some(cartridge) = ROM.clone() { + let bios_path: String = env + .get_string(bios_path) + .expect("invalid bios path object") + .into(); + let hw = Rc::new(RefCell::new(Hardware::new())); + + let bios_rom = read_bin_file(&Path::new(&bios_path)).expect("failed to load bios file"); + + EMULATOR = Some(Emulator { + hwif: hw.clone(), + gba: GameBoyAdvance::new(bios_rom, cartridge, hw.clone(), hw.clone(), hw.clone()), + }); + + return 0; + } else { + error!("please call loadRom first"); + return -1; + } + } + + #[no_mangle] + pub unsafe extern "C" fn Java_com_mrmichel_rustboyadvance_EmulatorInterface_closeEmulator( + env: JNIEnv, + _: JClass, + ) { + EMULATOR = None; + } + + #[no_mangle] + pub unsafe extern "C" fn Java_com_mrmichel_rustboyadvance_EmulatorInterface_runFrame( + env: JNIEnv, + _: JClass, + frame_buffer: jintArray, + ) -> jint { + if let Some(emu) = &mut EMULATOR { + emu.gba.frame(); + let our_buffer = + std::mem::transmute::<&[u32], &[i32]>(&emu.hwif.borrow().frame_buffer as &[u32]); + env.set_int_array_region(frame_buffer, 0, our_buffer) + .expect("failed to copy frame buffer to java"); + + return 0; + } + error!("emulator is not initalized"); + return -1; + } +} diff --git a/src/lib.rs b/src/lib.rs index 95a887f..1f9a235 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -58,7 +58,8 @@ pub trait InputInterface { pub mod prelude { pub use super::core::arm7tdmi; - pub use super::core::cartridge::GamepakBuilder; + pub use super::core::cartridge::{Cartridge, GamepakBuilder}; + pub use super::core::gpu::{DISPLAY_HEIGHT, DISPLAY_WIDTH}; pub use super::core::Bus; pub use super::core::{GBAError, GBAResult, GameBoyAdvance}; #[cfg(feature = "debugger")]