package cart import ( "bytes" "errors" "fmt" "os" ) type Cartridge struct { PRG []uint8 CHR []uint8 CHRIsRAM bool MapperID uint8 Mirroring Mirroring Battery bool Mapper Mapper } func Load(path string) (*Cartridge, error) { data, err := os.ReadFile(path) if err != nil { return nil, err } return Parse(data) } func Parse(data []byte) (*Cartridge, error) { if len(data) < 16 || !bytes.Equal(data[:4], []byte("NES\x1A")) { return nil, errors.New("cart: not an iNES file") } prgSize := int(data[4]) * 16 * 1024 chrSize := int(data[5]) * 8 * 1024 flags6, flags7 := data[6], data[7] isNES2 := flags7&0x0C == 0x08 mapperID := flags6 >> 4 if isNES2 || bytes.Equal(data[12:16], []byte{0, 0, 0, 0}) { mapperID |= flags7 & 0xF0 } c := &Cartridge{ MapperID: mapperID, Battery: flags6&0x02 != 0, } switch { case flags6&0x08 != 0: c.Mirroring = FourScreen case flags6&0x01 != 0: c.Mirroring = Vertical default: c.Mirroring = Horizontal } offset := 16 if flags6&0x04 != 0 { offset += 512 } if len(data) < offset+prgSize+chrSize { return nil, fmt.Errorf("cart: file truncated: need %d bytes, have %d", offset+prgSize+chrSize, len(data)) } c.PRG = data[offset : offset+prgSize] offset += prgSize if chrSize == 0 { c.CHR = make([]uint8, 8*1024) c.CHRIsRAM = true } else { c.CHR = data[offset : offset+chrSize] } m, err := newMapper(c) if err != nil { return nil, err } c.Mapper = m return c, nil }