ferretro-synced/src/sync/pokemon_rb.rs

319 lines
8.0 KiB
Rust

use std::default::Default;
use std::fmt::Display;
use crate::emulator::libretro_memory_map::LibRetroMemoryMap;
use serde::{Serialize, Deserialize};
use ferretro::retro::wrapper::LibretroWrapper; // want LibretroWrapper.api : LibretroApi.get_memory()
use crate::num::ToPrimitive;
use crate::sync::comms::Communication;
use crate::sync::comms::CommunicationSettings;
use crate::sync::memory_slice_handle::MemorySliceHandle;
use crate::sync::game::SyncableGame;
pub struct SyncedPokemonRedBlue {
memory_handles: PokemonRedBlueMemoryHandles<'static>,
battle_context: BattleContext,
comms: Communication<PokemonRedBlueMessage>
}
#[derive(Serialize, Deserialize)]
pub struct PokemonRedBlueMessage {
active_pkmn: Option<Vec<u8>>
}
impl From<&mut PokemonRedBlueMemoryHandles<'_>> for PokemonRedBlueMessage {
fn from(src: &mut PokemonRedBlueMemoryHandles) -> Self {
PokemonRedBlueMessage {
active_pkmn: src.active_pokemon.get_copy_if_changed_since_last_read()
}
}
}
struct PokemonRedBlueMemoryHandles<'a> {
active_pokemon: MemorySliceHandle<'a>,
player_and_party: MemorySliceHandle<'a>,
pokemon_out: MemorySliceHandle<'a>,
}
struct BattleContext {
active_pokemon: Pokemon,
}
impl SyncableGame for SyncedPokemonRedBlue {
fn update_state(&mut self) {
let battle_context = BattleContext {
active_pokemon: match &self.memory_handles.active_pokemon.slice {
Some(d) => num::FromPrimitive::from_u8(d[0xB]).unwrap_or(Pokemon::Unknown),
None => Pokemon::Unknown
}
};
self.battle_context = battle_context;
}
fn send_state_messages(&mut self) -> Result<(), failure::Error>{
let message: PokemonRedBlueMessage = (&mut self.memory_handles).into();
if !message.active_pkmn.is_none() {
self.comms.send(message)
}
else {
Ok(())
}
}
fn handle_inbound_messages(&mut self) -> Result<(), failure::Error>{
let in_msg = self.comms.try_recv()?;
self.handle_message(in_msg)
}
}
impl PokemonRedBlueMemoryHandles<'_> {
pub fn create(memory_map: &LibRetroMemoryMap) -> Self {
PokemonRedBlueMemoryHandles {
active_pokemon: MemorySliceHandle::create(0xd009, 0x27, 0, memory_map),
player_and_party: MemorySliceHandle::create(0xd158, 0x19e, 0, memory_map),
pokemon_out: MemorySliceHandle::create(0xcc2f, 0x1, 0, memory_map),
}
}
}
impl Default for BattleContext {
fn default() -> Self {
BattleContext {
active_pokemon: Pokemon::Rhydon
}
}
}
impl Display for SyncedPokemonRedBlue {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "active: pk {:?}", &self.battle_context.active_pokemon)
}
}
impl SyncedPokemonRedBlue {
pub fn create(comms_settings: CommunicationSettings, memory_map: &LibRetroMemoryMap) -> Self {
SyncedPokemonRedBlue {
memory_handles: PokemonRedBlueMemoryHandles::create(memory_map),
battle_context: BattleContext::default(),
comms: Communication::new(comms_settings),
}
}
fn handle_message(&mut self, msg: PokemonRedBlueMessage) -> Result<(), failure::Error> {
match msg.active_pkmn {
Some(src) => {
println!("message contains a new active pokemon. data: {:?}", &src);
let target_slice = self.memory_handles.active_pokemon.slice.as_mut().ok_or(failure::err_msg("can't write message back to None memory slice"))?;
if src.len() != target_slice.len() {
return Err(failure::err_msg("message size and slice size differ."));
}
let mut index: usize = 0;
for value in src.into_iter(){
target_slice[index] = value;
index += 1;
}
Ok(())
}
None => Ok(())
}
}
}
#[derive(FromPrimitive, ToPrimitive, Debug)]
enum Pokemon {
Unknown = 0x0,
Abra = 0x94,
Aerodactyl = 0xAB,
Alakazam = 0x95,
Arbok = 0x2D,
Arcanine = 0x14,
Articuno = 0x4A,
Beedrill = 0x72,
Bellsprout = 0xBC,
Blastoise = 0x1C,
Bulbasaur = 0x99,
Butterfree = 0x7D,
Caterpie = 0x7B,
Chansey = 0x28,
Charizard = 0xB4,
Charmander = 0xB0,
Charmeleon = 0xB2,
Clefable = 0x8E,
Clefairy = 0x04,
Cloyster = 0x8B,
Cubone = 0x11,
Dewgong = 0x78,
Diglett = 0x3B,
Ditto = 0x4C,
Dodrio = 0x74,
Doduo = 0x46,
Dragonair = 0x59,
Dragonite = 0x42,
Dratini = 0x58,
Drowzee = 0x30,
Dugtrio = 0x76,
Eevee = 0x66,
Ekans = 0x6C,
Electabuzz = 0x35,
Electrode = 0x8D,
Exeggcute = 0x0C,
Exeggutor = 0x0A,
Farfetchd = 0x40,
Fearow = 0x23,
Flareon = 0x67,
Gastly = 0x19,
Gengar = 0x0E,
Geodude = 0xA9,
Gloom = 0xBA,
Golbat = 0x82,
Goldeen = 0x9D,
Golduck = 0x80,
Golem = 0x31,
Graveler = 0x27,
Grimer = 0x0D,
Growlithe = 0x21,
Gyarados = 0x16,
Haunter = 0x93,
Hitmonchan = 0x2C,
Hitmonlee = 0x2B,
Horsea = 0x5C,
Hypno = 0x81,
Ivysaur = 0x09,
Jigglypuff = 0x64,
Jolteon = 0x68,
Jynx = 0x48,
Kabuto = 0x5A,
Kabutops = 0x5B,
Kadabra = 0x26,
Kakuna = 0x71,
Kangaskhan = 0x02,
Kingler = 0x8A,
Koffing = 0x37,
Krabby = 0x4E,
Lapras = 0x13,
Lickitung = 0x0B,
Machamp = 0x7E,
Machoke = 0x29,
Machop = 0x6A,
Magikarp = 0x85,
Magmar = 0x33,
Magnemite = 0xAD,
Magneton = 0x36,
Mankey = 0x39,
Marowak = 0x91,
Meowth = 0x4D,
Metapod = 0x7C,
Mew = 0x15,
Mewtwo = 0x83,
Moltres = 0x49,
MrMime = 0x2A,
Muk = 0x88,
Nidoking = 0x07,
Nidoqueen = 0x10,
NidoranF = 0x0F,
NidoranM = 0x03,
Nidorino = 0xA7,
Nidorina = 0xA8,
Ninetales = 0x53,
Oddish = 0xB9,
Omanyte = 0x62,
Omastar = 0x63,
Onix = 0x22,
Paras = 0x6D,
Parasect = 0x2E,
Persian = 0x90,
Pidgeot = 0x97,
Pidgeotto = 0x96,
Pidgey = 0x24,
Pikachu = 0x54,
Pinsir = 0x1D,
Poliwag = 0x47,
Poliwhirl = 0x6E,
Poliwrath = 0x6F,
Ponyta = 0xA3,
Porygon = 0xAA,
Primeape = 0x75,
Psyduck = 0x2F,
Raichu = 0x55,
Rapidash = 0xA4,
Raticate = 0xA6,
Rattata = 0xA5,
Rhydon = 0x01,
Rhyhorn = 0x12,
Sandshrew = 0x60,
Sandslash = 0x61,
Scyther = 0x1A,
Seadra = 0x5D,
Seaking = 0x9E,
Seel = 0x3A,
Shellder = 0x17,
Slowbro = 0x08,
Slowpoke = 0x25,
Snorlax = 0x84,
Spearow = 0x05,
Squirtle = 0xB1,
Starmie = 0x98,
Staryu = 0x1B,
Tangela = 0x1E,
Tauros = 0x3C,
Tentacool = 0x18,
Tentacruel = 0x9B,
Vaporeon = 0x69,
Venomoth = 0x77,
Venonat = 0x41,
Venusaur = 0x9A,
Victreebell = 0xBE,
Vileplume = 0xBB,
Voltorb = 0x06,
Vulpix = 0x52,
Wartortle = 0xB3,
Weedle = 0x70,
Weepinbell = 0xBD,
Weezing = 0x8F,
Wigglytuff = 0x65,
Zapdos = 0x4B,
Zubat = 0x6B,
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json;
fn build_message(em: &MemorySliceHandle) -> PokemonRedBlueMessage {
PokemonRedBlueMessage {
active_pkmn: match &em.slice {
Some(m) => Some(m.to_vec()),
None => None
}
}
}
#[test]
fn serde_mem() -> Result<(), String> {
let mut data: [u8; 5] = [1, 2, 3, 4, 5];
let b: &mut [u8] = &mut data;
let slice = MemorySliceHandle {
offset: 0,
length: 0,
bank_switch: 0,
slice: Some(b),
last_read_value: None
};
let message = build_message(&slice);
let serialized = serde_json::to_string(&message).unwrap();
println!("serialized data: {}", serialized);
let deserialized: PokemonRedBlueMessage = serde_json::from_str(&serialized).unwrap();
assert_eq!(deserialized.active_pkmn.unwrap()[0], data[0]);
Ok(())
}
}