// Copyright (C) 2020 "Maxime “pep” Buquet " // // This program is free software: you can redistribute it and/or modify it // under the terms of the GNU Affero General Public License as published by the // Free Software Foundation, either version 3 of the License, or (at your // option) any later version. // // This program is distributed in the hope that it will be useful, but WITHOUT // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or // FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License // for more details. // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . #![feature(debug_non_exhaustive)] /// Nintendo Pokemon gen1 save state handling, based on /// https://bulbapedia.bulbagarden.net/wiki/Save_data_structure_in_Generation_I mod error; mod party; mod species; use std::fmt; use std::ops::Range; use std::slice::Iter; use std::convert::TryFrom; use crate::error::Error; use crate::party::PokemonList; /// Compute checksum of a specific region. pub fn make_checksum(iter: Iter) -> u8 { iter.fold(255, |acc, x| acc.wrapping_sub(*x)) } pub fn verify_checksum(iter: Iter, checksum: u8) -> bool { make_checksum(iter) == checksum } /// Party Data const PARTY_DATA_RANGE: Range = 0x2F2C..0x30bf; /// Main Data Checksum const MAIN_DATA_CHECKSUM: usize = 0x3523; /// Area validated by the Main Data Checksum const MAIN_DATA_CHECKSUM_RANGE: Range = 0x2598..0x3523; /// A Save State #[derive(PartialEq)] pub struct SaveState { _data: Vec, /// Player name. offset: 0x2598, size: 0xB pub player_name: String, /// Party Data. offset: 0x2F2C, size: 0x194 pub party: PokemonList, } impl fmt::Debug for SaveState { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("SaveState") .field("party", &self.party) .finish_non_exhaustive() } } impl TryFrom> for SaveState { type Error = Error; fn try_from(data: Vec) -> Result { // Length of a savestate is 32k if data.len() != 32768 { return Err(Error::InvalidSaveLength) } if ! verify_checksum(data[MAIN_DATA_CHECKSUM_RANGE].iter(), data[MAIN_DATA_CHECKSUM]) { return Err(Error::InvalidChecksum) } Ok(SaveState { _data: data.clone(), player_name: String::from("Foo"), party: PokemonList::try_from(&data[PARTY_DATA_RANGE])?, }) } } impl SaveState { pub fn new(data: Vec) -> Result { SaveState::try_from(data) } pub fn into_vec(self) -> Vec { self._data } }