1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
const HEADER_BYTES: usize = 16;
const K: usize = 1024;
pub const PRG_ROM_BLOCK_BYTES: usize = 16 * K;
pub const CHR_ROM_BLOCK_BYTES: usize = 8 * K;
const HEADER_CHECKSUM: [u8; 4] = [78, 69, 83, 26];

#[derive(Debug, Clone, Copy)]
pub enum Mirroring {
    Horizontal,
    Vertical,
}

#[derive(Debug, Clone, Copy)]
pub enum Mapper {
    Nrom,
    Mmc1,
}

#[derive(Debug, Clone, Copy)]
pub enum Error {
    UnimplementedMapper { code: u8 },
}

impl Mapper {
    fn encode(self) -> u8 {
        match self {
            Mapper::Nrom => 0,
            Mapper::Mmc1 => 1,
        }
    }
    fn decode(code: u8) -> Result<Self, Error> {
        match code {
            0 => Ok(Mapper::Nrom),
            1 => Ok(Mapper::Mmc1),
            other => Err(Error::UnimplementedMapper { code: other }),
        }
    }
}

#[derive(Debug)]
pub struct Header {
    pub num_prg_rom_blocks: u8,
    pub num_chr_rom_blocks: u8,
    pub mapper: Mapper,
    pub mirroring: Mirroring,
    pub four_screen_vram: bool,
}

impl Header {
    fn parse(buffer: &[u8]) -> Result<Self, Error> {
        let checksum = &buffer[0..HEADER_CHECKSUM.len()];
        if checksum != &HEADER_CHECKSUM {
            panic!("Invalid checksum");
        }
        let four_screen_vram = buffer[6] & (1 << 3) != 0;
        let mirroring = if buffer[6] & (1 << 0) != 0 {
            Mirroring::Vertical
        } else {
            Mirroring::Horizontal
        };
        let num_prg_rom_blocks = buffer[4];
        let num_chr_rom_blocks = buffer[5];
        let mapper_number = (buffer[7] & 0xF0) | (buffer[6] >> 4);
        let mapper = Mapper::decode(mapper_number)?;
        Ok(Self {
            num_prg_rom_blocks,
            num_chr_rom_blocks,
            mapper,
            mirroring,
            four_screen_vram,
        })
    }
    fn prg_rom_bytes(&self) -> usize {
        self.num_prg_rom_blocks as usize * PRG_ROM_BLOCK_BYTES
    }
    fn chr_rom_bytes(&self) -> usize {
        self.num_chr_rom_blocks as usize * CHR_ROM_BLOCK_BYTES
    }
    fn encode(&self, buffer: &mut Vec<u8>) {
        (&mut buffer[0..HEADER_CHECKSUM.len()]).copy_from_slice(&HEADER_CHECKSUM);
        buffer[4] = self.num_prg_rom_blocks;
        buffer[5] = self.num_chr_rom_blocks;
        let mapper_number = self.mapper.encode();
        buffer[6] = mapper_number << 4;
        buffer[7] = mapper_number & 0xF0;
        match self.mirroring {
            Mirroring::Horizontal => {
                buffer[6] |= 1 << 0;
            }
            Mirroring::Vertical => {
                buffer[6] &= !(1 << 0);
            }
        }
        if self.four_screen_vram {
            buffer[6] |= 1 << 3;
        } else {
            buffer[6] &= !(1 << 3);
        }
    }
}

pub struct Ines {
    pub header: Header,
    pub prg_rom: Vec<u8>,
    pub chr_rom: Vec<u8>,
}

impl Ines {
    pub fn parse(buffer: &[u8]) -> Result<Self, Error> {
        let header_raw = &buffer[0..HEADER_BYTES];
        let data = &buffer[HEADER_BYTES..];
        let header = Header::parse(header_raw)?;
        let prg_rom_bytes = header.prg_rom_bytes();
        let chr_rom_bytes = header.chr_rom_bytes();
        let prg_rom = data[0..prg_rom_bytes].to_vec();
        let chr_rom = data[prg_rom_bytes..(prg_rom_bytes + chr_rom_bytes)].to_vec();
        Ok(Self {
            header,
            prg_rom,
            chr_rom,
        })
    }
    pub fn encode(&self, buffer: &mut Vec<u8>) {
        buffer.resize(
            HEADER_BYTES + self.header.prg_rom_bytes() + self.header.chr_rom_bytes(),
            0,
        );
        self.header.encode(buffer);
        let prg_start = HEADER_BYTES;
        let chr_start = prg_start + self.header.prg_rom_bytes();
        (&mut buffer[prg_start..(prg_start + self.prg_rom.len())]).copy_from_slice(&self.prg_rom);
        (&mut buffer[chr_start..(chr_start + self.chr_rom.len())]).copy_from_slice(&self.chr_rom);
    }
}