Skip to content

Commit 7b8c066

Browse files
committed
Add support for unknown IPL3 variants
Currently, CIC autodetection features works by calculating a CRC32 of the IPL3 boot code (header bytes 0x40..0x1000) and checking it against a known database to know which CIC is expected. This approach cannot detect unknown IPL3s and is going to be insufficient in the short future when libdragon releases its own open source IPL3. A more robust approach is to use the same custom checksum function used by IPL2, trying to simulate boot with different CICs: that is, try with different seeds and see if the respective checksum matches. This basically means that we now have a DB of CIC secrets rather than IPL3 CRCs: it seems a better compromise if several new IPL3 spawn from libdragon's.
1 parent 3d4fa61 commit 7b8c066

File tree

3 files changed

+290
-18
lines changed

3 files changed

+290
-18
lines changed

drive64/const.go

+44-10
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ package drive64
33
import (
44
"errors"
55
"fmt"
6-
"hash/crc32"
76
)
87

98
//go:generate stringer -type=Cmd,Bank,CIC,SaveType,UpgradeStatus -output=const_string.go
@@ -86,6 +85,10 @@ const (
8685
CICX105 CIC = 5
8786
CICX106 CIC = 6
8887
CIC5101 CIC = 7
88+
CIC8303 CIC = 8
89+
CIC8401 CIC = 9
90+
CIC5167 CIC = 10
91+
CICDDUS CIC = 11
8992
)
9093

9194
// Save emulation types supported by 64drive
@@ -122,28 +125,59 @@ func NewCICFromString(name string) (CIC, error) {
122125
return CICX106, nil
123126
case "5101":
124127
return CIC5101, nil
128+
case "8303":
129+
return CIC8303, nil
130+
case "8401":
131+
return CIC8401, nil
132+
case "5167":
133+
return CIC5167, nil
134+
case "DDUE":
135+
return CICDDUS, nil
125136
default:
126137
return 0, errors.New("invalid CIC variant")
127138
}
128139
}
129140

130141
// NewCICFromHeader detects a CIC variant from a ROM header
131142
func NewCICFromHeader(header []uint8) (CIC, error) {
132-
crc := crc32.ChecksumIEEE(header[0x40:0x1000])
133-
switch crc {
134-
case 0x6170A4A1:
143+
header = header[0x40:0x1000]
144+
145+
switch IPL2Checksum(header, 0x3F) {
146+
case 0x45cc73ee317a:
135147
return CIC6101, nil
136-
case 0x90BB6CB5:
148+
case 0xa536c0f1d859:
137149
return CIC6102, nil
138-
case 0x0B050EE0:
150+
case 0x44160ec5d9af:
151+
return CIC7102, nil
152+
}
153+
154+
switch IPL2Checksum(header, 0x78) {
155+
case 0x586fd4709867:
139156
return CICX103, nil
140-
case 0x98BC2C86:
157+
}
158+
159+
switch IPL2Checksum(header, 0x91) {
160+
case 0x8618a45bc2d3:
141161
return CICX105, nil
142-
case 0xACC8580A:
162+
}
163+
164+
switch IPL2Checksum(header, 0x85) {
165+
case 0x2bbad4e6eb74:
143166
return CICX106, nil
144-
default:
145-
return 0, fmt.Errorf("cannot detect CIC from ROM header (%08x)", crc)
146167
}
168+
169+
switch IPL2Checksum(header, 0xDD) {
170+
case 0x32b294e2ab90:
171+
return CIC8303, nil
172+
case 0x6ee8d9e84970:
173+
return CIC8401, nil
174+
case 0x083c6c77e0b1:
175+
return CIC5167, nil
176+
case 0x05ba2ef0a5f1:
177+
return CICDDUS, nil
178+
}
179+
180+
return 0, errors.New("cannot detect CIC from ROM header")
147181
}
148182

149183
func NewSaveTypeFromString(name string) (SaveType, error) {

drive64/const_string.go

+16-8
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

drive64/ipl2checksum.go

+230
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,230 @@
1+
package drive64
2+
3+
import (
4+
"encoding/binary"
5+
)
6+
7+
const MAGIC_NUMBER = 0x6c078965
8+
9+
type ReadCallback func(uint32) uint32
10+
11+
type CheckSumInfo struct {
12+
Input []byte
13+
Buffer [16]uint32
14+
ChecksumLow uint32
15+
ChecksumHigh uint32
16+
}
17+
18+
func checksumFunction(a0, a1, a2 uint32) uint32 {
19+
var prod uint64
20+
var hi, lo, diff, res uint32
21+
22+
if a1 == 0 {
23+
a1 = a2
24+
}
25+
26+
prod = uint64(a0) * uint64(a1)
27+
hi = uint32(prod >> 32)
28+
lo = uint32(prod)
29+
diff = hi - lo
30+
if diff == 0 {
31+
res = a0
32+
} else {
33+
res = diff
34+
}
35+
return res
36+
}
37+
38+
func initializeChecksum(seed uint8, input []byte) *CheckSumInfo {
39+
var init, data uint32
40+
var info CheckSumInfo
41+
42+
info.Input = input
43+
44+
init = MAGIC_NUMBER*uint32(seed) + 1
45+
data = binary.BigEndian.Uint32(input[0:4])
46+
init ^= data
47+
48+
for loop := 0; loop < 16; loop++ {
49+
info.Buffer[loop] = init
50+
}
51+
52+
return &info
53+
}
54+
55+
func calculateChecksum(info *CheckSumInfo) {
56+
var sum, dataIndex, loop, shift, dataShiftedRight, dataShiftedLeft, s2Tmp, s5Tmp, a3Tmp uint32
57+
var data, dataNext, dataLast uint32
58+
59+
if info == nil {
60+
return
61+
}
62+
63+
dataIndex = 0
64+
loop = 0
65+
data = binary.BigEndian.Uint32(info.Input[0:4])
66+
for {
67+
loop++
68+
dataLast = data
69+
data = binary.BigEndian.Uint32(info.Input[dataIndex : dataIndex+4])
70+
71+
sum = checksumFunction(1007-loop, data, loop)
72+
info.Buffer[0] += sum
73+
74+
sum = checksumFunction(info.Buffer[1], data, loop)
75+
info.Buffer[1] = sum
76+
info.Buffer[2] ^= data
77+
78+
sum = checksumFunction(data+5, MAGIC_NUMBER, loop)
79+
info.Buffer[3] += sum
80+
81+
if dataLast < data {
82+
sum = checksumFunction(info.Buffer[9], data, loop)
83+
info.Buffer[9] = sum
84+
} else {
85+
info.Buffer[9] += data
86+
}
87+
88+
shift = dataLast & 0x1f
89+
dataShiftedRight = data >> shift
90+
dataShiftedLeft = data << (32 - shift)
91+
s5Tmp = dataShiftedRight | dataShiftedLeft
92+
info.Buffer[4] += s5Tmp
93+
94+
dataShiftedLeft = data << shift
95+
dataShiftedRight = data >> (32 - shift)
96+
97+
sum = checksumFunction(info.Buffer[7], dataShiftedLeft|dataShiftedRight, loop)
98+
info.Buffer[7] = sum
99+
100+
if data < info.Buffer[6] {
101+
info.Buffer[6] = (info.Buffer[3] + info.Buffer[6]) ^ (data + loop)
102+
} else {
103+
info.Buffer[6] = (info.Buffer[4] + data) ^ info.Buffer[6]
104+
}
105+
106+
shift = dataLast >> 27
107+
dataShiftedRight = data >> (32 - shift)
108+
dataShiftedLeft = data << shift
109+
s2Tmp = dataShiftedLeft | dataShiftedRight
110+
info.Buffer[5] += s2Tmp
111+
112+
dataShiftedLeft = data << (32 - shift)
113+
dataShiftedRight = data >> shift
114+
115+
sum = checksumFunction(info.Buffer[8], dataShiftedRight|dataShiftedLeft, loop)
116+
info.Buffer[8] = sum
117+
118+
if loop == 1008 {
119+
break
120+
}
121+
122+
dataIndex += 4
123+
dataNext = binary.BigEndian.Uint32(info.Input[dataIndex : dataIndex+4])
124+
125+
sum = checksumFunction(info.Buffer[15], s2Tmp, loop)
126+
127+
shift = data >> 27
128+
dataShiftedLeft = dataNext << shift
129+
dataShiftedRight = dataNext >> (32 - shift)
130+
131+
sum = checksumFunction(sum, dataShiftedLeft|dataShiftedRight, loop)
132+
info.Buffer[15] = sum
133+
134+
sum = checksumFunction(info.Buffer[14], s5Tmp, loop)
135+
136+
shift = data & 0x1f
137+
s2Tmp = shift
138+
dataShiftedLeft = dataNext << (32 - shift)
139+
dataShiftedRight = dataNext >> shift
140+
141+
sum = checksumFunction(sum, dataShiftedRight|dataShiftedLeft, loop)
142+
info.Buffer[14] = sum
143+
144+
dataShiftedRight = data >> s2Tmp
145+
dataShiftedLeft = data << (32 - s2Tmp)
146+
a3Tmp = dataShiftedRight | dataShiftedLeft
147+
148+
shift = dataNext & 0x1f
149+
dataShiftedRight = dataNext >> shift
150+
dataShiftedLeft = dataNext << (32 - shift)
151+
152+
info.Buffer[13] += a3Tmp + (dataShiftedRight | dataShiftedLeft)
153+
154+
sum = checksumFunction(info.Buffer[10]+data, dataNext, loop)
155+
info.Buffer[10] = sum
156+
157+
sum = checksumFunction(info.Buffer[11]^data, dataNext, loop)
158+
info.Buffer[11] = sum
159+
160+
info.Buffer[12] += info.Buffer[8] ^ data
161+
}
162+
}
163+
164+
func finalizeChecksum(info *CheckSumInfo) {
165+
var buf [4]uint32
166+
var checksum uint64
167+
var sum, loop, tmp, s2Tmp, shift, dataShiftedRight, dataShiftedLeft uint32
168+
169+
if info == nil {
170+
return
171+
}
172+
173+
data := info.Buffer[0]
174+
buf[0] = data
175+
buf[1] = data
176+
buf[2] = data
177+
buf[3] = data
178+
179+
for loop = 0; loop < 16; loop++ {
180+
data = info.Buffer[loop]
181+
182+
shift = data & 0x1f
183+
dataShiftedLeft = data << (32 - shift)
184+
dataShiftedRight = data >> shift
185+
tmp = buf[0] + (dataShiftedRight | dataShiftedLeft)
186+
buf[0] = tmp
187+
188+
if data < tmp {
189+
buf[1] += data
190+
} else {
191+
sum = checksumFunction(buf[1], data, loop)
192+
buf[1] = sum
193+
}
194+
195+
tmp = (data & 0x02) >> 1
196+
s2Tmp = data & 0x01
197+
198+
if tmp == s2Tmp {
199+
buf[2] += data
200+
} else {
201+
sum = checksumFunction(buf[2], data, loop)
202+
buf[2] = sum
203+
}
204+
205+
if s2Tmp == 1 {
206+
buf[3] ^= data
207+
} else {
208+
sum = checksumFunction(buf[3], data, loop)
209+
buf[3] = sum
210+
}
211+
}
212+
213+
// 0xa4001510
214+
sum = checksumFunction(buf[0], buf[1], 16)
215+
tmp = buf[3] ^ buf[2]
216+
217+
checksum = uint64(sum) << 32
218+
checksum |= uint64(tmp)
219+
checksum &= 0xffffffffffff
220+
221+
info.ChecksumLow = uint32(checksum)
222+
info.ChecksumHigh = uint32(checksum >> 32)
223+
}
224+
225+
func IPL2Checksum(input []byte, seed uint8) uint64 {
226+
info := initializeChecksum(seed, input)
227+
calculateChecksum(info)
228+
finalizeChecksum(info)
229+
return uint64(info.ChecksumHigh)<<32 | uint64(info.ChecksumLow)
230+
}

0 commit comments

Comments
 (0)