diff --git a/Cargo.lock b/Cargo.lock index ebf655e..fb27344 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8,6 +8,11 @@ dependencies = [ "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "ascii" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "atty" version = "0.2.13" @@ -204,6 +209,7 @@ dependencies = [ name = "ferretro-synced" version = "0.1.0" dependencies = [ + "ascii 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "cc 1.0.48 (registry+https://github.com/rust-lang/crates.io-index)", "crossbeam-channel 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1353,6 +1359,7 @@ dependencies = [ [metadata] "checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" +"checksum ascii 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bbf56136a5198c7b01a49e3afcbef6cf84597273d298f54432926024107b0109" "checksum atty 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)" = "1803c647a3ec87095e7ae7acfca019e98de5ec9a7d01343f611cf3152ed71a90" "checksum autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "1d49d90015b3c36167a20fe2810c5cd875ad504b39cff3d4eae7977e6b7c1cb2" "checksum backtrace 0.3.40 (registry+https://github.com/rust-lang/crates.io-index)" = "924c76597f0d9ca25d762c25a4d369d51267536465dc5064bdf0eb073ed477ea" diff --git a/Cargo.toml b/Cargo.toml index ccf638a..4e49823 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,5 +21,6 @@ serde = { version = "1.0.104", features = ["derive"] } serde_bytes = "0.11" serde_json = "1.0.44" websocket = "0.24.0" +ascii = "1.0.0" [dev-dependencies] \ No newline at end of file diff --git a/src/emulator/emulator.rs b/src/emulator/emulator.rs index e3df8b7..cdd999c 100644 --- a/src/emulator/emulator.rs +++ b/src/emulator/emulator.rs @@ -6,6 +6,7 @@ use ferretro::retro::wrapper::LibretroWrapper; use crate::sync::game::{SyncableGame, KnownGames, read_game_name, create_game}; use crate::emulator::libretro_memory_map::LibRetroMemoryMap; +use crate::emulator::metadata_reader::{GameInfo, get_game_info}; use std::ffi::CStr; use std::io::Read; @@ -30,6 +31,7 @@ pub struct MyEmulator { sys_info: SystemInfo, av_info: SystemAvInfo, + pub game_info: Option, sdl_context: sdl2::Sdl, @@ -129,6 +131,7 @@ impl MyEmulator { preferred_pad: None, av_info, sys_info, + game_info: None, sdl_context, canvas, pixel_format, @@ -208,17 +211,31 @@ impl MyEmulator { } } - pub fn load_game(&self, rom: impl AsRef) { + pub fn load_game(&mut self, rom: impl AsRef) { let path = rom.as_ref(); let mut data = None; let mut v = Vec::new(); - if !self.sys_info.need_fullpath { - if let Ok(mut f) = std::fs::File::open(path) { - if f.read_to_end(&mut v).is_ok() { - data = Some(v.as_ref()); - } + + if let Ok(mut f) = std::fs::File::open(path) { + if f.read_to_end(&mut v).is_ok() { + data = Some(v.as_ref()); + + let library_name = unsafe { CStr::from_ptr(self.sys_info.library_name) }.to_string_lossy(); + + self.game_info = match get_game_info(library_name.as_ref(), v.as_ref()) { + Ok(game_info) => Some(game_info), + Err(e) => { + println!("Couldn't get game metadata: {:?}", e); + None + } + }; } } + + if self.sys_info.need_fullpath { // this core will load the rom itself, so don't pass it data + data = None + } + self.retro .load_game(Some(path), data, None) .unwrap(); diff --git a/src/emulator/metadata_reader.rs b/src/emulator/metadata_reader.rs new file mode 100644 index 0000000..627241a --- /dev/null +++ b/src/emulator/metadata_reader.rs @@ -0,0 +1,47 @@ +use ascii::AsAsciiStr; + +pub enum GamePlatform { + GameBoyColor, + SuperNintendo, + Genesis +} + +pub struct GameInfo { + pub platform: GamePlatform, + pub core_name: String, + pub game_name: String, +} + +fn core_to_platform(core_name: &str) -> GamePlatform { + match core_name.to_lowercase().as_str() { + "gambatte" => GamePlatform::GameBoyColor, + "bsnes" => GamePlatform::SuperNintendo, + "genesis plus gx" => GamePlatform::Genesis, + "higan (super famicom accuracy)" => GamePlatform::SuperNintendo, + _ => panic!(format!("unknown core {}", core_name)) + } +} + +pub fn get_game_info(core_name: &str, data: &[u8]) -> Result { + let platform = core_to_platform(&core_name); + + let game_name = match &platform { + GamePlatform::GameBoyColor => data.slice_ascii(0x134..0x143)?.to_string() + .trim_end_matches("\0").to_string(), // remove nulls at end + + GamePlatform::SuperNintendo => data.slice_ascii(0x7fc0..0x7fd5)?.to_string() + .trim_end_matches("\0").to_string(), // remove nulls at end + + GamePlatform::Genesis => data.slice_ascii(0x120..0x150)?.to_string() + .split(" ") + .filter(|w| w.len() > 0) + .collect::>() + .join(" "), // remove duplicate spaces + }; + + Ok(GameInfo { + platform, + core_name: String::from(core_name), + game_name, + }) +} \ No newline at end of file diff --git a/src/emulator/mod.rs b/src/emulator/mod.rs index 552b575..f25abfe 100644 --- a/src/emulator/mod.rs +++ b/src/emulator/mod.rs @@ -1,2 +1,3 @@ pub mod emulator; -pub mod libretro_memory_map; \ No newline at end of file +pub mod libretro_memory_map; +pub mod metadata_reader; \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 0fbb0e9..d5dba4f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,8 +1,10 @@ +#[macro_use] extern crate failure; extern crate crossbeam_channel; extern crate ferretro; extern crate sdl2; extern crate serde; extern crate serde_bytes; +extern crate ascii; use structopt::StructOpt; use std::path::{Path, PathBuf}; @@ -18,37 +20,45 @@ pub fn main() -> failure::Fallible<()> { emu.load_game(&opt.rom); // memory map should be ready after init & loading game. - let memory_map = emu.memory_map.as_ref().expect("Memory map was not set by emulator core, cannot continue."); + //let memory_map = emu.memory_map.as_ref().expect("Memory map was not set by emulator core, cannot continue."); match (&opt.state) { Some(s) => emu.load_state(s), None => () } - let which_game = read_game_name("assume you can get it from emu".to_string()); - let comms_settings = CommunicationSettings { connection: "ws://127.0.0.1:8765".to_string() }; - match which_game { - Some(which_game) => { - let synced_game = create_game(which_game, comms_settings, &memory_map); - - emu.begin_sync(synced_game); - }, - None => { - println!("Game is unknown, not syncing.") - } + if let Err(e) = attempt_to_start_sync(&mut emu, comms_settings) { + println!("Error trying to start sync: {:?}", e); } - //let mut comms = Communication::new::(); emu.run(); Ok(()) } +fn attempt_to_start_sync(emu: &mut MyEmulator, comms_settings: CommunicationSettings) -> Result<(), failure::Error> { + match &emu.game_info { + Some(game_info) => match read_game_name(&game_info.game_name) { + Some(which_game) => match &emu.memory_map { + Some(memory_map) => { + let synced_game = create_game(which_game, comms_settings, memory_map); + emu.begin_sync(synced_game); + Ok(()) + }, + None => Err(format_err!("Memory map has not been set")) + }, + None => Err(format_err!("Unrecognized game {}", game_info.game_name)) + }, + None => Err(format_err!("Couldn't get game metadata")) + } + +} + #[derive(StructOpt)] struct Opt { /// Core module to use. diff --git a/src/sync/game.rs b/src/sync/game.rs index 279dfe0..3f5bd0b 100644 --- a/src/sync/game.rs +++ b/src/sync/game.rs @@ -17,8 +17,11 @@ pub enum KnownGames { PokemonRedBlue, } -pub fn read_game_name(name: String) -> Option { - Some(KnownGames::PokemonRedBlue) // todo: actually read the name of the game +pub fn read_game_name(name: &str) -> Option { + match name { + "POKEMON BLUE" => Some(KnownGames::PokemonRedBlue), + _ => None + } }