pkmn-rs/savestate/src/lib.rs
Maxime “pep” Buquet fd93736793
Initial save state party data parsing
Signed-off-by: Maxime “pep” Buquet <pep@bouah.net>
2020-06-28 22:00:21 +02:00

100 lines
2.7 KiB
Rust

// Copyright (C) 2020 "Maxime “pep” Buquet <pep@bouah.net>"
//
// 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 <https://www.gnu.org/licenses/>.
#![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>) -> u8 {
iter.fold(255, |acc, x| acc.wrapping_sub(*x))
}
pub fn verify_checksum(iter: Iter<u8>, checksum: u8) -> bool {
make_checksum(iter) == checksum
}
/// Party Data
const PARTY_DATA_RANGE: Range<usize> = 0x2F2C..0x30bf;
/// Main Data Checksum
const MAIN_DATA_CHECKSUM: usize = 0x3523;
/// Area validated by the Main Data Checksum
const MAIN_DATA_CHECKSUM_RANGE: Range<usize> = 0x2598..0x3523;
/// A Save State
#[derive(PartialEq)]
pub struct SaveState {
_data: Vec<u8>,
/// 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<Vec<u8>> for SaveState {
type Error = Error;
fn try_from(data: Vec<u8>) -> Result<SaveState, Error> {
// 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<u8>) -> Result<SaveState, Error> {
SaveState::try_from(data)
}
pub fn into_vec(self) -> Vec<u8> {
self._data
}
}