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
|
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
}
|