-
Notifications
You must be signed in to change notification settings - Fork 42
Expand file tree
/
Copy pathcartridge.go
More file actions
167 lines (143 loc) · 4.5 KB
/
Copy pathcartridge.go
File metadata and controls
167 lines (143 loc) · 4.5 KB
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
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
package cartridge
import (
"crypto/md5"
"errors"
"fmt"
"io"
"strings"
"github.com/djhworld/gomeboycolor/utils"
)
const (
MBC_0 = 0x00
MBC_1 = 0x01
MBC_1_RAM = 0x02
MBC_1_RAM_BATT = 0x03
MBC_3_RAM_BATT = 0x13
MBC_3_RAM_BATT_RTC = 0x10
MBC_5 = 0x19
MBC_5_RAM = 0x1A
MBC_5_RAM_BATT = 0x1B
MBC_5_RUMBLE = 0x1C
MBC_5_RAM_RUMBLE = 0x1D
MBC_5_RAM_BATT_RUMBLE = 0x1E
)
type CartridgeType struct {
ID byte
Description string
}
var CartridgeTypes map[byte]CartridgeType = map[byte]CartridgeType{
MBC_0: CartridgeType{MBC_0, "ROM ONLY"},
MBC_1: CartridgeType{MBC_1, "ROM+MBC1"},
MBC_1_RAM: CartridgeType{MBC_1_RAM, "ROM+MBC1+RAM"},
MBC_1_RAM_BATT: CartridgeType{MBC_1_RAM_BATT, "ROM+MBC1+RAM+BATT"},
MBC_3_RAM_BATT: CartridgeType{MBC_3_RAM_BATT, "ROM+MBC3+RAM+BATT"},
MBC_3_RAM_BATT_RTC: CartridgeType{MBC_3_RAM_BATT_RTC, "ROM+MBC3+RAM+BATT+RTC"}, //TODO: support RTC in MBC3
MBC_5: CartridgeType{MBC_5, "ROM+MBC5"},
MBC_5_RAM: CartridgeType{MBC_5_RAM, "ROM+MBC5+RAM"},
MBC_5_RAM_BATT: CartridgeType{MBC_5_RAM_BATT, "ROM+MBC5+RAM+BATT"},
MBC_5_RUMBLE: CartridgeType{MBC_5_RUMBLE, "ROM+MBC5+RUMBLE"},
MBC_5_RAM_RUMBLE: CartridgeType{MBC_5_RAM_RUMBLE, "ROM+MBC5+RAM+RUMBLE"},
MBC_5_RAM_BATT_RUMBLE: CartridgeType{MBC_5_RAM_BATT_RUMBLE, "ROM+MBC5+RAM+BATT+RUMBLE"},
}
type Cartridge struct {
Title string
IsColourGB bool
Type CartridgeType
ROMSize int
RAMSize int
IsJapanese bool
Name string
MBC MemoryBankController
ID string
}
func NewCartridge(romName string, romContents []byte) (*Cartridge, error) {
var cart *Cartridge = new(Cartridge)
cart.Name = romName
var err error = cart.Init(romContents)
if err != nil {
return nil, err
}
return cart, nil
}
func (c *Cartridge) Init(rom []byte) error {
if size := len(rom); size < 32768 {
return errors.New(fmt.Sprintf("ROM size %d is too small", size))
}
c.Title = strings.TrimSpace(string(rom[0x0134:0x0142]))
h := md5.New()
io.WriteString(h, c.Title)
c.ID = fmt.Sprintf("%x", h.Sum(nil))
c.IsColourGB = (rom[0x0143] == 0x80) || (rom[0x0143] == 0xC0)
ctype := rom[0x0147]
//validate
if v, ok := CartridgeTypes[ctype]; !ok {
return errors.New(fmt.Sprintf("Unknown cartridge type: %X for ROM", ctype))
} else {
c.Type = v
}
if romSize := rom[0x0148]; romSize > 0x06 {
return errors.New(fmt.Sprintf("Handling for ROM size id: 0x%X is currently unimplemented", romSize))
} else {
c.ROMSize = 0x8000 << romSize
}
switch rom[0x0149] {
case 0x00:
c.RAMSize = 0
case 0x01:
c.RAMSize = 2048
case 0x02:
c.RAMSize = 8192
case 0x03:
c.RAMSize = 32768
case 0x04:
c.RAMSize = 131072
}
c.IsJapanese = (rom[0x014A] == 0x00)
switch c.Type.ID {
case MBC_0:
c.MBC = NewMBC0(rom)
case MBC_1, MBC_1_RAM:
c.MBC = NewMBC1(rom, c.ROMSize, c.RAMSize, false)
case MBC_1_RAM_BATT:
c.MBC = NewMBC1(rom, c.ROMSize, c.RAMSize, true)
case MBC_3_RAM_BATT, MBC_3_RAM_BATT_RTC:
c.MBC = NewMBC3(rom, c.ROMSize, c.RAMSize, true)
case MBC_5, MBC_5_RAM, MBC_5_RUMBLE, MBC_5_RAM_RUMBLE:
c.MBC = NewMBC5(rom, c.ROMSize, c.RAMSize, false)
case MBC_5_RAM_BATT, MBC_5_RAM_BATT_RUMBLE:
c.MBC = NewMBC5(rom, c.ROMSize, c.RAMSize, true)
default:
return errors.New("Error: Cartridge type " + utils.ByteToString(c.Type.ID) + " is currently unsupported")
}
return nil
}
func (c *Cartridge) SaveRam(writer io.Writer) error {
return c.MBC.SaveRam(writer)
}
func (c *Cartridge) LoadRam(reader io.Reader) error {
return c.MBC.LoadRam(reader)
}
func (c *Cartridge) String() string {
startingString := "Gameboy"
if c.IsColourGB {
startingString += " Color"
}
var destinationRegion string
if c.IsJapanese {
destinationRegion = "Japanese"
} else {
destinationRegion = "Non-Japanese"
}
var header []string = []string{
fmt.Sprintf(utils.PadRight("Title:", 19, " ")+"%s", c.Title),
fmt.Sprintf(utils.PadRight("Type:", 19, " ")+"%s %s", c.Type.Description, utils.ByteToString(c.Type.ID)),
fmt.Sprintf(utils.PadRight("Destination code:", 19, " ")+"%s", destinationRegion),
fmt.Sprintf(utils.PadRight("Name:", 19, " ")+"%s", c.Name),
fmt.Sprintf(utils.PadRight("ID:", 19, " ")+"%s", c.ID),
}
return fmt.Sprintln("\n"+startingString, "Cartridge") +
fmt.Sprintln(strings.Repeat("-", 100)) +
fmt.Sprintln(strings.Join(header, "\n")) +
fmt.Sprintln(c.MBC) +
fmt.Sprintln(strings.Repeat("-", 100))
}