Handle multiple games by reading their metadata

This commit is contained in:
Vivian Lim 2020-03-10 00:15:33 -07:00
parent ccc3f16491
commit 6889711f5b
7 changed files with 108 additions and 22 deletions

7
Cargo.lock generated
View File

@ -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"

View File

@ -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]

View File

@ -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<GameInfo>,
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<Path>) {
pub fn load_game(&mut self, rom: impl AsRef<Path>) {
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();

View File

@ -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<GameInfo, failure::Error> {
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::<Vec<&str>>()
.join(" "), // remove duplicate spaces
};
Ok(GameInfo {
platform,
core_name: String::from(core_name),
game_name,
})
}

View File

@ -1,2 +1,3 @@
pub mod emulator;
pub mod libretro_memory_map;
pub mod libretro_memory_map;
pub mod metadata_reader;

View File

@ -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::<ferretro_synced::sync::pokemon_rb::Message>();
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.

View File

@ -17,8 +17,11 @@ pub enum KnownGames {
PokemonRedBlue,
}
pub fn read_game_name(name: String) -> Option<KnownGames> {
Some(KnownGames::PokemonRedBlue) // todo: actually read the name of the game
pub fn read_game_name(name: &str) -> Option<KnownGames> {
match name {
"POKEMON BLUE" => Some(KnownGames::PokemonRedBlue),
_ => None
}
}