diff --git a/rustboyadvance-core/Cargo.toml b/rustboyadvance-core/Cargo.toml index 7813483..9b7c68c 100644 --- a/rustboyadvance-core/Cargo.toml +++ b/rustboyadvance-core/Cargo.toml @@ -38,6 +38,8 @@ ringbuf = "0.2.1" goblin = { version = "0.2", optional = true } fuzzy-matcher = { version = "0.3.4", optional = true } bit_reverse = "0.1.8" +yaml-rust = "0.4" +lazy_static = "1.4.0" [target.'cfg(target_arch="wasm32")'.dependencies] instant = { version = "0.1.2", features = ["wasm-bindgen"] } diff --git a/rustboyadvance-core/overrides.yaml b/rustboyadvance-core/overrides.yaml new file mode 100644 index 0000000..d0d0ef9 --- /dev/null +++ b/rustboyadvance-core/overrides.yaml @@ -0,0 +1,13 @@ +# Game specific overrides +# TODO - complete the list + +- code: ALFP + name: Dragon Ball Z - The Legacy of Goku II (Europe)(En,Fr,De,Es,It) + save_type: eeprom + +- code: AZJE + name: Dragon Ball Z - Supersonic Warriors (USA) + +- code: BPEE + name: Pokemon - Emerald Version (USA, Europe) + rtc: true \ No newline at end of file diff --git a/rustboyadvance-core/src/cartridge/builder.rs b/rustboyadvance-core/src/cartridge/builder.rs index 1a37f41..b203512 100644 --- a/rustboyadvance-core/src/cartridge/builder.rs +++ b/rustboyadvance-core/src/cartridge/builder.rs @@ -3,6 +3,7 @@ use std::path::{Path, PathBuf}; use memmem::{Searcher, TwoWaySearcher}; use num::FromPrimitive; +use super::super::overrides; use super::super::{GBAError, GBAResult}; use super::backup::eeprom::*; use super::backup::flash::*; @@ -131,24 +132,53 @@ impl GamepakBuilder { } } - if self.save_type == BackupType::AutoDetect { + let mut save_type = self.save_type; + let mut gpio_device = self.gpio_device; + + if let Some(overrides) = overrides::get_game_overrides(&header.game_code) { + info!("Found game overrides for {}: {:#?}", header.game_code, overrides); + if let Some(override_save_type) = overrides.save_type() { + if override_save_type != save_type && save_type != BackupType::AutoDetect { + warn!( + "Forced save type {:?} takes priority of {:?}", + save_type, override_save_type + ); + } + save_type = override_save_type; + } + + if overrides.force_rtc() { + match gpio_device { + GpioDeviceType::None => gpio_device = GpioDeviceType::Rtc, + GpioDeviceType::Rtc => {}, + _ => { + warn!( + "Can't use RTC due to forced gpio device type {:?}", + gpio_device + ); + } + } + } + } + + if save_type == BackupType::AutoDetect { if let Some(detected) = detect_backup_type(&bytes) { info!("Detected Backup: {:?}", detected); - self.save_type = detected; + save_type = detected; } else { warn!("could not detect backup save type"); } } - let backup = create_backup(self.save_type, self.save_path); + let backup = create_backup(save_type, self.save_path); - let gpio = match self.gpio_device { + let gpio = match gpio_device { GpioDeviceType::None => Gpio::new_none(), GpioDeviceType::Rtc => { info!("Emulating RTC!"); Gpio::new_rtc() } - _ => unimplemented!("Gpio device {:?} not implemented", self.gpio_device), + _ => unimplemented!("Gpio device {:?} not implemented", gpio_device), }; let size = bytes.len(); diff --git a/rustboyadvance-core/src/lib.rs b/rustboyadvance-core/src/lib.rs index 0012a04..0d2f5bb 100644 --- a/rustboyadvance-core/src/lib.rs +++ b/rustboyadvance-core/src/lib.rs @@ -1,6 +1,9 @@ #[macro_use] extern crate serde; +#[macro_use] +extern crate lazy_static; + #[macro_use] extern crate debug_stub_derive; @@ -48,6 +51,7 @@ pub mod dma; pub mod keypad; pub mod timer; pub use bus::*; +pub(crate) mod overrides; #[cfg(feature = "gdb")] pub mod gdb; diff --git a/rustboyadvance-core/src/overrides.rs b/rustboyadvance-core/src/overrides.rs new file mode 100644 index 0000000..b02a0fa --- /dev/null +++ b/rustboyadvance-core/src/overrides.rs @@ -0,0 +1,58 @@ +use std::collections::HashMap; +use std::convert::TryFrom; + +use yaml_rust::YamlLoader; + +use super::cartridge::BackupType; + +#[derive(Debug)] +pub struct GameOverride { + force_rtc: bool, + save_type: Option, +} + +impl GameOverride { + pub fn force_rtc(&self) -> bool { + self.force_rtc + } + pub fn save_type(&self) -> Option { + self.save_type + } +} + +lazy_static! { + static ref GAME_OVERRIDES: HashMap = { + let mut m = HashMap::new(); + + let docs = YamlLoader::load_from_str(include_str!("../overrides.yaml")) + .expect("failed to load overrides file"); + + let doc = &docs[0]; + let games = doc.as_vec().unwrap(); + + for game in games { + let game_code = String::from(game["code"].as_str().unwrap()); + let force_rtc = game["rtc"].as_bool().unwrap_or(false); + let save_type = if let Some(save_type) = game["save_type"].as_str() { + match BackupType::try_from(save_type) { + Ok(x) => Some(x), + _ => panic!("{}: invalid save type {:#}", game_code, save_type), + } + } else { + None + }; + + let game_overrride = GameOverride { + force_rtc, + save_type, + }; + m.insert(game_code, game_overrride); + } + + m + }; +} + +pub fn get_game_overrides(game_code: &str) -> Option<&GameOverride> { + GAME_OVERRIDES.get(game_code) +}