nes-sim 0.1.4

A NES (Famicom) emulator core library written in pure Rust.
Documentation
use std::error::Error;
use std::fmt::{Display, Formatter};

const SAVE_STATE_MAGIC: &[u8; 8] = b"NESSTAT\0";
const SAVE_STATE_VERSION: u32 = 1;

#[derive(Debug, PartialEq, Eq)]
pub enum SaveStateError {
    InvalidMagic,
    UnsupportedVersion(u32),
    UnexpectedEof,
    TrailingData,
    NoCartridge,
    MapperMismatch { expected: u16, actual: u16 },
    InvalidData(&'static str),
}

impl Display for SaveStateError {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        match self {
            Self::InvalidMagic => f.write_str("save state header is invalid"),
            Self::UnsupportedVersion(version) => {
                write!(f, "save state version {} is not supported", version)
            }
            Self::UnexpectedEof => f.write_str("save state ended unexpectedly"),
            Self::TrailingData => f.write_str("save state contains trailing bytes"),
            Self::NoCartridge => f.write_str("save state requires an inserted cartridge"),
            Self::MapperMismatch { expected, actual } => write!(
                f,
                "save state mapper mismatch: expected mapper {}, got mapper {}",
                expected, actual
            ),
            Self::InvalidData(message) => f.write_str(message),
        }
    }
}

impl Error for SaveStateError {}

pub(crate) struct StateWriter {
    bytes: Vec<u8>,
}

impl StateWriter {
    pub(crate) fn new() -> Self {
        let mut writer = Self { bytes: Vec::new() };
        writer.write_bytes(SAVE_STATE_MAGIC);
        writer.write_u32(SAVE_STATE_VERSION);
        writer
    }

    pub(crate) fn finish(self) -> Vec<u8> {
        self.bytes
    }

    pub(crate) fn write_u8(&mut self, value: u8) {
        self.bytes.push(value);
    }

    pub(crate) fn write_bool(&mut self, value: bool) {
        self.write_u8(u8::from(value));
    }

    pub(crate) fn write_u16(&mut self, value: u16) {
        self.bytes.extend_from_slice(&value.to_le_bytes());
    }

    pub(crate) fn write_u32(&mut self, value: u32) {
        self.bytes.extend_from_slice(&value.to_le_bytes());
    }

    pub(crate) fn write_u64(&mut self, value: u64) {
        self.bytes.extend_from_slice(&value.to_le_bytes());
    }

    pub(crate) fn write_i16(&mut self, value: i16) {
        self.bytes.extend_from_slice(&value.to_le_bytes());
    }

    pub(crate) fn write_bytes(&mut self, bytes: &[u8]) {
        self.bytes.extend_from_slice(bytes);
    }
}

pub(crate) struct StateReader<'a> {
    bytes: &'a [u8],
    offset: usize,
}

impl<'a> StateReader<'a> {
    pub(crate) fn new(bytes: &'a [u8]) -> Result<Self, SaveStateError> {
        let mut reader = Self { bytes, offset: 0 };
        let mut magic = [0; SAVE_STATE_MAGIC.len()];
        reader.read_exact(&mut magic)?;
        if &magic != SAVE_STATE_MAGIC {
            return Err(SaveStateError::InvalidMagic);
        }

        let version = reader.read_u32()?;
        if version != SAVE_STATE_VERSION {
            return Err(SaveStateError::UnsupportedVersion(version));
        }

        Ok(reader)
    }

    pub(crate) fn finish(self) -> Result<(), SaveStateError> {
        if self.offset == self.bytes.len() {
            Ok(())
        } else {
            Err(SaveStateError::TrailingData)
        }
    }

    pub(crate) fn read_u8(&mut self) -> Result<u8, SaveStateError> {
        let mut buf = [0];
        self.read_exact(&mut buf)?;
        Ok(buf[0])
    }

    pub(crate) fn read_bool(&mut self) -> Result<bool, SaveStateError> {
        match self.read_u8()? {
            0 => Ok(false),
            1 => Ok(true),
            _ => Err(SaveStateError::InvalidData("boolean field must be 0 or 1")),
        }
    }

    pub(crate) fn read_u16(&mut self) -> Result<u16, SaveStateError> {
        let mut buf = [0; 2];
        self.read_exact(&mut buf)?;
        Ok(u16::from_le_bytes(buf))
    }

    pub(crate) fn read_u32(&mut self) -> Result<u32, SaveStateError> {
        let mut buf = [0; 4];
        self.read_exact(&mut buf)?;
        Ok(u32::from_le_bytes(buf))
    }

    pub(crate) fn read_u64(&mut self) -> Result<u64, SaveStateError> {
        let mut buf = [0; 8];
        self.read_exact(&mut buf)?;
        Ok(u64::from_le_bytes(buf))
    }

    pub(crate) fn read_i16(&mut self) -> Result<i16, SaveStateError> {
        let mut buf = [0; 2];
        self.read_exact(&mut buf)?;
        Ok(i16::from_le_bytes(buf))
    }

    pub(crate) fn read_bytes_into(&mut self, bytes: &mut [u8]) -> Result<(), SaveStateError> {
        self.read_exact(bytes)
    }

    fn read_exact(&mut self, buf: &mut [u8]) -> Result<(), SaveStateError> {
        let end = self.offset.saturating_add(buf.len());
        if end > self.bytes.len() {
            return Err(SaveStateError::UnexpectedEof);
        }
        buf.copy_from_slice(&self.bytes[self.offset..end]);
        self.offset = end;
        Ok(())
    }
}