cge_nes 0.1.0

Cycle-accurate NES (Nintendo Entertainment System) emulator library: CPU, PPU, cartridge, input, and iNES ROM loading.
Documentation
//! ROM loader for NES cartridge emulation.
//!
//! This crate provides functionality for loading and parsing NES ROM files in the iNES format.
//! It handles the details of reading ROM data and creating appropriate cartridge implementations
//! based on the mapper type specified in the ROM header.
//!
//! # Features
//!
//! * Supports iNES format ROM files
//! * Parses ROM headers to determine cartridge configuration
//! * Implements multiple mapper types (0, 1, 2, 3)
//! * Provides error handling for invalid ROM files
//!
//! # Example
//!
//! ```no_run
//! # use std::path::Path;
//! # use std::fs;
//! use cge_nes::rom_loader::load_rom;
//!
//! let mut rom_file = fs::File::open(Path::new("super_mario.nes")).unwrap();
//! match load_rom(&mut rom_file) {
//!     Ok(cartridge) => println!("ROM loaded successfully!"),
//!     Err(e) => println!("Failed to load ROM: {}", e),
//! }
//! ```
//!
//! # Supported Mappers
//!
//! * Mapper 0: NROM - Nintendo's original mapper
//! * Mapper 1: MMC1 - Memory Management Controller 1
//! * Mapper 2: UxROM
//! * Mapper 3: CNROM
//! * Mapper 4: MMC3 - Memory Management Controller 3
//!
//! # Error Handling
//!
//! The crate uses a custom [`RomError`] type to handle both I/O errors and
//! format validation errors that may occur during ROM loading.

#![deny(missing_docs)]

pub mod error;
pub mod ines;

pub use error::RomError;

use crate::cartridge::Cartridge;
use ines::mappers::*;
use std::io::Read;

/// Result type for ROM loading operations, returns either a boxed Cartridge trait object or a RomError
pub type LoadRomResult = Result<Box<dyn Cartridge>, RomError>;

/// Loads and parses an NES ROM file, creating an appropriate cartridge implementation.
///
/// # Arguments
///
/// * `rom_reader` - A reader providing access to the ROM data
///
/// # Returns
///
/// Returns a `LoadRomResult` which is either:
/// * `Ok(Box<dyn Cartridge>)` - A boxed cartridge implementation for the loaded ROM
/// * `Err(RomError)` - An error describing what went wrong during loading
///
/// # Errors
///
/// This function will return an error if:
/// * The ROM data cannot be read
/// * The data doesn't start with the NES magic constant
/// * The data is too small to contain the declared PRG and CHR ROM data
/// * The mapper number is not implemented (see
///   [`RomError::UnsupportedMapper`])
///
/// # Example
///
/// ```no_run
/// # use std::path::Path;
/// # use std::fs;
/// use cge_nes::rom_loader::load_rom;
///
/// let mut rom_file = fs::File::open(Path::new("super_mario.nes")).unwrap();
/// match load_rom(&mut rom_file) {
///     Ok(cartridge) => println!("ROM loaded successfully!"),
///     Err(e) => println!("Failed to load ROM: {}", e),
/// }
/// ```
pub fn load_rom(rom_reader: &mut impl Read) -> LoadRomResult {
    let mut header = [0u8; 16];
    rom_reader.read_exact(&mut header)?;

    if &header[0..4] != b"NES\x1A".as_slice() {
        return Err(RomError::RomFormat(
            r#"File does not start with magic constant"#.into(),
        ));
    }

    let header_data = ines::HeaderData::new(&header);

    #[cfg(feature = "mapper_debug_log")]
    println!("Mapper: {}", header_data.mapper);

    match header_data.mapper {
        0 => basic_mapper::load(&header_data, rom_reader),
        1 => mapper001::load(&header_data, rom_reader),
        2 => basic_mapper::load(&header_data, rom_reader),
        3 => basic_mapper::load(&header_data, rom_reader),
        4 => mapper004::load(&header_data, rom_reader),
        _ => Err(RomError::UnsupportedMapper(header_data.mapper)),
    }
}

#[cfg(test)]
mod tests;