aboutsummaryrefslogtreecommitdiff
path: root/cart/cart.go
diff options
context:
space:
mode:
Diffstat (limited to 'cart/cart.go')
-rw-r--r--cart/cart.go83
1 files changed, 83 insertions, 0 deletions
diff --git a/cart/cart.go b/cart/cart.go
new file mode 100644
index 0000000..2438c1b
--- /dev/null
+++ b/cart/cart.go
@@ -0,0 +1,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
+}