about summary refs log tree commit diff stats
path: root/source
diff options
context:
space:
mode:
authorKelly Rauchenberger <fefferburbia@gmail.com>2017-07-13 20:38:04 -0400
committerKelly Rauchenberger <fefferburbia@gmail.com>2017-07-13 20:38:04 -0400
commit2190722ac1f0732cf35e7b63572afa698a47789d (patch)
tree004d2302ece1d9f8feb4d3d435d0393739ebd1bd /source
parent252e2911383a5267673f00c08419e2afeac35a31 (diff)
downloadgen3uploader-2190722ac1f0732cf35e7b63572afa698a47789d.tar.gz
gen3uploader-2190722ac1f0732cf35e7b63572afa698a47789d.tar.bz2
gen3uploader-2190722ac1f0732cf35e7b63572afa698a47789d.zip
Organized code more
Now the link-specific stuff is abstracted into its own file, and the
code for negotiating the "different" multiboot protocol is in its own
file. Also, removed support for compiling for GC because eventually we
will be using Wii-only features. Also put the main extractor code into a
thread so that we can monitor for the user pressing the start button to
exit.
Diffstat (limited to 'source')
-rw-r--r--source/link.c157
-rw-r--r--source/link.h14
-rw-r--r--source/main.c596
-rw-r--r--source/multiboot.c246
-rw-r--r--source/multiboot.h8
5 files changed, 550 insertions, 471 deletions
diff --git a/source/link.c b/source/link.c index 4178293..1091576 100644 --- a/source/link.c +++ b/source/link.c
@@ -5,18 +5,159 @@
5 * of the MIT license. See the LICENSE file for details. 5 * of the MIT license. See the LICENSE file for details.
6 */ 6 */
7#include "link.h" 7#include "link.h"
8#include <unistd.h>
9#include <stdio.h>
10#include <string.h>
11#include <malloc.h>
8 12
9u32 waitForButtons(u32 mask) 13//from my tests 50us seems to be the lowest
14//safe si transfer delay in between calls
15#define SI_TRANS_DELAY 50
16
17static u8* resbuf;
18static u8* cmdbuf;
19
20void initLink()
21{
22 cmdbuf = memalign(32,32);
23 resbuf = memalign(32,32);
24}
25
26static volatile u32 transval = 0;
27void transcb(s32 chan, u32 ret)
28{
29 transval = 1;
30}
31
32static volatile u32 resval = 0;
33void acb(s32 res, u32 val)
34{
35 resval = val;
36}
37
38void doreset()
39{
40 cmdbuf[0] = 0xFF; //reset
41 transval = 0;
42 SI_Transfer(1, cmdbuf, 1, resbuf, 3, transcb, SI_TRANS_DELAY);
43
44 while (transval == 0);
45}
46
47void getstatus()
10{ 48{
11 for (;;) 49 cmdbuf[0] = 0; //status
50 transval = 0;
51 SI_Transfer(1, cmdbuf, 1, resbuf, 3, transcb, SI_TRANS_DELAY);
52
53 while (transval == 0);
54}
55
56u32 recv()
57{
58 memset(resbuf,0,32);
59 cmdbuf[0]=0x14; //read
60 transval = 0;
61 SI_Transfer(1, cmdbuf, 1, resbuf, 5, transcb, SI_TRANS_DELAY);
62
63 while (transval == 0);
64 printf("%08lx\n", *(vu32*)resbuf);
65 return *(vu32*)resbuf;
66}
67
68void send(u32 msg)
69{
70 cmdbuf[0] = 0x15;
71 cmdbuf[1] = (msg >> 0) & 0xFF;
72 cmdbuf[2] = (msg >> 8) & 0xFF;
73 cmdbuf[3] = (msg >> 16) & 0xFF;
74 cmdbuf[4] = (msg >> 24) & 0xFF;
75
76 transval = 0;
77 resbuf[0] = 0;
78 SI_Transfer(1, cmdbuf, 5, resbuf, 1, transcb, SI_TRANS_DELAY);
79
80 while (transval == 0);
81}
82
83u32 getMsg()
84{
85 u32 val = 0;
86 while (val == 0)
12 { 87 {
13 PAD_ScanPads(); 88 val = __builtin_bswap32(recv());
14 VIDEO_WaitVSync(); 89 sleep(1);
90 }
15 91
16 u32 btns = PAD_ButtonsDown(0); 92 send(0);
17 if (btns & mask) 93 while (recv()!=0) {sleep(1);}
94 send(0);
95
96 return val;
97}
98
99void getMsgArr(u32* arr, int len)
100{
101 for (int i=0; i<len; i++)
102 {
103 *(vu32*)(arr+i) = __builtin_bswap32(recv());
104 usleep(500000);
105 }
106}
107
108void sendMsg(u32 msg)
109{
110 while (recv()==0) {sleep(1);}
111 send(msg);
112 while (recv()!=0) {sleep(1);}
113 send(0);
114}
115
116void waitForGBA()
117{
118 resval = 0;
119
120 do
121 {
122 SI_GetTypeAsync(1, acb);
123
124 for (;;)
18 { 125 {
19 return btns; 126 if (resval)
127 {
128 if (resval == 0x80 || resval & 8)
129 {
130 resval = 0;
131 SI_GetTypeAsync(1, acb);
132 } else if (resval)
133 {
134 break;
135 }
136 }
20 } 137 }
21 } 138 } while (!(resval & SI_GBA));
139}
140
141void waitForBIOS()
142{
143 resbuf[2]=0;
144
145 do
146 {
147 doreset();
148 } while (!(resbuf[1] > 4));
149}
150
151void waitForGame()
152{
153 do
154 {
155 doreset();
156 } while ((resbuf[0] != 0) || !(resbuf[2] & 0x10));
157}
158
159void waitForAck()
160{
161 while (recv() != 0) {sleep(1);};
162 send(0);
22} 163}
diff --git a/source/link.h b/source/link.h index 24a60b5..15eff62 100644 --- a/source/link.h +++ b/source/link.h
@@ -9,6 +9,18 @@
9 9
10#include <gccore.h> 10#include <gccore.h>
11 11
12u32 waitForButtons(u32 mask); 12void initLink();
13
14u32 recv();
15void send(u32 msg);
16
17u32 getMsg();
18void getMsgArr(u32* arr, int len);
19void sendMsg(u32 msg);
20
21void waitForGBA();
22void waitForBIOS();
23void waitForGame();
24void waitForAck();
13 25
14#endif 26#endif
diff --git a/source/main.c b/source/main.c index dd252b5..a3b7525 100644 --- a/source/main.c +++ b/source/main.c
@@ -10,17 +10,9 @@
10#include <stdio.h> 10#include <stdio.h>
11#include <stdlib.h> 11#include <stdlib.h>
12#include <unistd.h> 12#include <unistd.h>
13#include <string.h>
14#include <malloc.h>
15#include "link.h" 13#include "link.h"
16#include "encoding.h" 14#include "encoding.h"
17 15#include "multiboot.h"
18//from my tests 50us seems to be the lowest
19//safe si transfer delay in between calls
20#define SI_TRANS_DELAY 50
21
22extern u8 gba_mb_gba[];
23extern u32 gba_mb_gba_size;
24 16
25void printmain() 17void printmain()
26{ 18{
@@ -31,91 +23,27 @@ void printmain()
31 printf("Based on GBA Link Cable Dumper v1.6 by FIX94\n"); 23 printf("Based on GBA Link Cable Dumper v1.6 by FIX94\n");
32} 24}
33 25
34u8 *resbuf,*cmdbuf;
35
36volatile u32 transval = 0;
37void transcb(s32 chan, u32 ret)
38{
39 transval = 1;
40}
41
42volatile u32 resval = 0;
43void acb(s32 res, u32 val)
44{
45 resval = val;
46}
47
48unsigned int docrc(u32 crc,u32 val)
49{
50 u32 result;
51
52 result = val ^ crc;
53 for (int i = 0; i < 0x20; i++)
54 {
55 if (result & 1)
56 {
57 result >>= 1;
58 result ^= 0xA1C1;
59 } else {
60 result >>= 1;
61 }
62 }
63
64 return result;
65}
66
67void doreset()
68{
69 cmdbuf[0] = 0xFF; //reset
70 transval = 0;
71 SI_Transfer(1, cmdbuf, 1, resbuf, 3, transcb, SI_TRANS_DELAY);
72
73 while (transval == 0);
74}
75
76void getstatus()
77{
78 cmdbuf[0] = 0; //status
79 transval = 0;
80 SI_Transfer(1, cmdbuf, 1, resbuf, 3, transcb, SI_TRANS_DELAY);
81
82 while (transval == 0);
83}
84
85void endproc() 26void endproc()
86{ 27{
87 doreset();
88 printf("Start pressed, exit\n"); 28 printf("Start pressed, exit\n");
89 VIDEO_WaitVSync(); 29 VIDEO_WaitVSync();
90 VIDEO_WaitVSync(); 30 VIDEO_WaitVSync();
91 exit(0); 31 exit(0);
92} 32}
93 33
94u32 recv() 34u32 waitForButtons(u32 mask)
95{
96 memset(resbuf,0,32);
97 cmdbuf[0]=0x14; //read
98 transval = 0;
99 SI_Transfer(1, cmdbuf, 1, resbuf, 5, transcb, SI_TRANS_DELAY);
100
101 while (transval == 0);
102 printf("%08lx\n", *(vu32*)resbuf);
103 return *(vu32*)resbuf;
104}
105
106void send(u32 msg)
107{ 35{
108 cmdbuf[0] = 0x15; 36 for (;;)
109 cmdbuf[1] = (msg >> 0) & 0xFF; 37 {
110 cmdbuf[2] = (msg >> 8) & 0xFF; 38 PAD_ScanPads();
111 cmdbuf[3] = (msg >> 16) & 0xFF; 39 VIDEO_WaitVSync();
112 cmdbuf[4] = (msg >> 24) & 0xFF;
113
114 transval = 0;
115 resbuf[0] = 0;
116 SI_Transfer(1, cmdbuf, 5, resbuf, 1, transcb, SI_TRANS_DELAY);
117 40
118 while (transval == 0); 41 u32 btns = PAD_ButtonsDown(0);
42 if (btns & mask)
43 {
44 return btns;
45 }
46 }
119} 47}
120 48
121void warnError(char *msg) 49void warnError(char *msg)
@@ -135,132 +63,148 @@ void fatalError(char *msg)
135 exit(0); 63 exit(0);
136} 64}
137 65
138u32 genKeyA() 66void* extractor(void* userdata)
139{ 67{
140 u32 retries = 0;
141
142 for (;;) 68 for (;;)
143 { 69 {
144 u32 key = 0; 70 printmain();
145 71
146 if (retries > 32) 72 printf("Press A when GBA is plugged in and turned off.\n");
147 { 73 waitForButtons(PAD_BUTTON_A);
148 key = 0xDD654321; 74
149 } else { 75 printf("Turn on your GBA.\n");
150 key = (rand() & 0x00ffffff) | 0xDD000000; 76 printf("Waiting for a GBA in port 2...\n");
151 } 77 waitForGBA();
152 78
153 u32 unk = (key % 2 != 0); 79 printf("GBA Found! Sending multiboot image...\n");
154 u32 v12 = key; 80 if (!sendMultibootImage())
155 for (u32 v13 = 1; v13 < 32; v13++)
156 { 81 {
157 v12 >>= 1; 82 warnError("Failed sending multiboot image.\n");
158 unk += (v12 % 2 != 0); 83
84 continue;
159 } 85 }
160 86
161 if ((unk >= 10 && unk <= 24)) 87 printf("Waiting for GBA...\n");
88 waitForAck();
89
90 VIDEO_WaitVSync();
91
92 // Get game
93 // -1 - unsupported game
94 // 1 - Ruby
95 // 2 - Sapphire
96 // 3 - FireRed
97 // 4 - LeafGreen
98 // 5 - Emerald
99 u32 gameId = getMsg();
100 if (gameId == -1)
162 { 101 {
163 if (retries > 4) 102 warnError("ERROR: Unsupported GBA game inserted!\n");
164 {
165 printf("KeyA retries = %ld", retries);
166 }
167 103
168 printf("KeyA = 0x%08lx\n", key); 104 continue;
105 }
169 106
170 return key; 107 printf("\nPokemon ");
108 switch (gameId)
109 {
110 case 1: printf("Ruby"); break;
111 case 2: printf("Sapphire"); break;
112 case 3: printf("FireRed"); break;
113 case 4: printf("LeafGreen"); break;
114 case 5: printf("Emerald"); break;
171 } 115 }
172 116
173 retries++; 117 printf("\n");
174 } 118 VIDEO_WaitVSync();
175}
176 119
177u32 checkKeyB(u32 KeyBRaw) 120 u32 isValid = getMsg();
178{ 121 if (isValid == -1)
179 if ((KeyBRaw & 0xFF) != 0xEE) 122 {
180 { 123 warnError("ERROR: Unsupported game version inserted!\n");
181 printf("Invalid KeyB - lowest 8 bits should be 0xEE, actually 0x%02x\n",
182 ((u8)(KeyBRaw)));
183 124
184 return 0; 125 continue;
185 } 126 }
186 127
187 u32 KeyB = KeyBRaw & 0xffffff00; 128 // Get trainer name
188 u32 val = KeyB; 129 u8 trainerName[8];
189 u32 unk = (val < 0);
190 for (u32 i = 1; i < 24; i++)
191 {
192 val <<= 1;
193 unk += (val < 0);
194 }
195 130
196 if (unk > 14) 131 u32 tnd = getMsg();
197 { 132 trainerName[0] = (tnd & 0xFF000000) >> 24;
198 printf("Invalid KeyB - high 24 bits bad: 0x%08lx\n", KeyB); 133 trainerName[1] = (tnd & 0x00FF0000) >> 16;
134 trainerName[2] = (tnd & 0x0000FF00) >> 8;
135 trainerName[3] = (tnd & 0x000000FF);
199 136
200 return 0; 137 tnd = getMsg();
201 } 138 trainerName[4] = (tnd & 0xFF000000) >> 24;
139 trainerName[5] = (tnd & 0x00FF0000) >> 16;
140 trainerName[6] = (tnd & 0x0000FF00) >> 8;
141 trainerName[7] = (tnd & 0x000000FF);
202 142
203 printf("Valid KeyB: 0x%08lx\n", KeyB); 143 // Get trainer ID
144 u32 trainerId = getMsg();
204 145
205 return KeyB; 146 printf("Trainer: ");
206}
207 147
208u32 deriveKeyC(u32 keyCderive, u32 kcrc) 148 for (int i = 0; i < 8; i++)
209{ 149 {
210 u32 keyc = 0; 150 if (trainerName[i] == 0xFF)
211 u32 keyCi = 0; 151 {
152 break;
153 } else {
154 printf("%c", debugGen3Decode(trainerName[i]));
155 }
156 }
212 157
213 do 158 printf(" (%ld)\n", trainerId);
214 { 159
215 u32 v5 = 0x1000000 * keyCi - 1; 160 // Wait for confirmation.
216 u32 keyCattempt = docrc(kcrc,v5); 161 printf("Press A to import the data from this game.\n");
162 printf("Press B to cancel.\n");
163 VIDEO_WaitVSync();
217 164
218 if (keyCderive == keyCattempt) 165 if (waitForButtons(PAD_BUTTON_A | PAD_BUTTON_B) & PAD_BUTTON_B)
219 { 166 {
220 keyc = v5; 167 printf("Cancelling...\n");
168 VIDEO_WaitVSync();
221 169
222 printf("Found keyC: %08lx\n",keyc); 170 sendMsg(0);
223 171
224 return keyc; 172 continue;
225 } 173 }
226 174
227 keyCi++; 175 printf("Importing...\n");
228 } while (keyCi < 256); 176 VIDEO_WaitVSync();
229 177
230 return keyc; 178 sendMsg(1);
231}
232 179
233u32 getMsg() 180 // Get Pokédex data
234{ 181 u32 pokedexSeen[13];
235 u32 val = 0; 182 u32 pokedexCaught[13];
236 while (val == 0)
237 {
238 val = __builtin_bswap32(recv());
239 sleep(1);
240 }
241 183
242 send(0); 184 getMsgArr(pokedexSeen, 13);
243 while (recv()!=0) {sleep(1);} 185 getMsgArr(pokedexCaught, 13);
244 send(0); 186 int numCaught = 0;
187 int numSeen = 0;
188 for (int i=0; i<(13*32); i++)
189 {
190 if (pokedexCaught[i >> 5] >> (i & 31) & 1)
191 {
192 //printf("Caught #%d\n", i);
193 numCaught++;
194 numSeen++;
195 } else if (pokedexSeen[i >> 5] >> (i & 31) & 1)
196 {
197 //printf("Saw #%d\n", i);
198 numSeen++;
199 }
200 }
245 201
246 return val; 202 printf("Caught: %d\nSeen: %d\n", numCaught, numSeen);
247}
248 203
249void getMsgArr(u32* arr, int len) 204 waitForButtons(PAD_BUTTON_START);
250{
251 for (int i=0; i<len; i++)
252 {
253 *(vu32*)(arr+i) = __builtin_bswap32(recv());
254 usleep(500000);
255 } 205 }
256}
257 206
258void sendMsg(u32 msg) 207 return NULL;
259{
260 while (recv()==0) {sleep(1);}
261 send(msg);
262 while (recv()!=0) {sleep(1);}
263 send(0);
264} 208}
265 209
266int main(int argc, char *argv[]) 210int main(int argc, char *argv[])
@@ -282,299 +226,27 @@ int main(int argc, char *argv[])
282 CON_InitEx(rmode, x, y, w, h); 226 CON_InitEx(rmode, x, y, w, h);
283 VIDEO_ClearFrameBuffer(rmode, xfb, COLOR_BLACK); 227 VIDEO_ClearFrameBuffer(rmode, xfb, COLOR_BLACK);
284 PAD_Init(); 228 PAD_Init();
285 cmdbuf = memalign(32,32);
286 resbuf = memalign(32,32);
287 229
288 for (;;) 230 initLink();
289 {
290 printmain();
291 231
292 printf("Press A to begin, press Start to quit.\n"); 232 lwp_t extractorHandle = (lwp_t)NULL;
293 if (waitForButtons(PAD_BUTTON_A | PAD_BUTTON_START) & PAD_BUTTON_START)
294 {
295 endproc();
296 }
297 233
298 printf("Waiting for a GBA in port 2...\n"); 234 LWP_CreateThread(
299 resval = 0; 235 &extractorHandle, // thread handle
236 extractor, // code
237 NULL, // userdata
238 NULL, // stack base
239 16*1024, // stack size
240 LWP_PRIO_HIGHEST); // thread priority
300 241
301 SI_GetTypeAsync(1,acb); 242 for (;;)
302 while(1) 243 {
303 { 244 VIDEO_WaitVSync();
304 if (resval) 245 PAD_ScanPads();
305 {
306 if (resval == 0x80 || resval & 8)
307 {
308 resval = 0;
309 SI_GetTypeAsync(1,acb);
310 } else if (resval)
311 {
312 break;
313 }
314 }
315
316 PAD_ScanPads();
317 VIDEO_WaitVSync();
318 if (PAD_ButtonsHeld(0) & PAD_BUTTON_START)
319 {
320 getstatus();
321 endproc();
322 }
323 }
324 246
325 if (resval & SI_GBA) 247 if (PAD_ButtonsDown(0) & PAD_BUTTON_START)
326 { 248 {
327 printf("GBA Found! Waiting on BIOS\n"); 249 endproc();
328
329 resbuf[2]=0;
330
331 // wait for the BIOS to hand over to the game
332 do
333 {
334 doreset();
335 } while (!(resbuf[1] > 4));
336
337 printf("BIOS handed over to game, waiting on game\n");
338
339 do
340 {
341 doreset();
342 } while ((resbuf[0] != 0) || !(resbuf[2] & 0x10));
343
344 // receive the game-code from GBA side.
345 u32 gamecode = recv();
346
347 printf("Ready, sending multiboot ROM\n");
348
349 unsigned int sendsize = ((gba_mb_gba_size+7)&~7);
350
351 // generate KeyA
352 unsigned int ourkey = genKeyA();
353
354 //printf("Our Key: %08x\n", ourkey);
355 printf("Sending game code that we got: 0x%08lx\n",
356 __builtin_bswap32(gamecode));
357
358 // send the game code back, then KeyA.
359 send(__builtin_bswap32(gamecode));
360 send(ourkey);
361
362 // get KeyB from GBA, check it to make sure its valid, then xor with KeyA
363 // to derive the initial CRC value and the sessionkey.
364 u32 sessionkeyraw = 0;
365 do
366 {
367 sessionkeyraw = recv();
368 } while (sessionkeyraw == gamecode);
369
370 sessionkeyraw = checkKeyB(__builtin_bswap32(sessionkeyraw));
371 if (sessionkeyraw == 0)
372 {
373 warnError("Cannot continue.\n");
374
375 continue;
376 }
377
378 u32 sessionkey = sessionkeyraw ^ ourkey;
379 u32 kcrc = sessionkey;
380 printf("start kCRC=%08lx\n",kcrc);
381
382 sessionkey = (sessionkey*0x6177614b)+1;
383
384 // send hacked up send-size in uint32s
385 u32 hackedupsize = (sendsize >> 3) - 1;
386
387 printf("Sending hacked up size 0x%08lx\n",hackedupsize);
388 send(hackedupsize);
389
390 //unsigned int fcrc = 0x00bb;
391 // send over multiboot binary header, in the clear until the end of the
392 // nintendo logo. GBA checks this, if nintendo logo does not match the
393 // one in currently inserted cart's ROM, it will not accept any more data.
394 for (int i = 0; i < 0xA0; i+=4)
395 {
396 vu32 rom_dword = *(vu32*)(gba_mb_gba+i);
397 send(__builtin_bswap32(rom_dword));
398 }
399
400 printf("\n");
401 printf("Header done! Sending ROM...\n");
402
403 // Add each uint32 of the multiboot image to the checksum, encrypt the
404 // uint32 with the session key, increment the session key, send the
405 // encrypted uint32.
406 for (int i = 0xA0; i < sendsize; i+=4)
407 {
408 u32 dec = (
409 (((gba_mb_gba[i+3]) << 24) & 0xff000000) |
410 (((gba_mb_gba[i+2]) << 16) & 0x00ff0000) |
411 (((gba_mb_gba[i+1]) << 8) & 0x0000ff00) |
412 (((gba_mb_gba[i]) << 0) & 0x000000ff)
413 );
414
415 u32 enc = (dec - kcrc) ^ sessionkey;
416 kcrc = docrc(kcrc,dec);
417 sessionkey = (sessionkey * 0x6177614B) + 1;
418 //enc^=((~(i+(0x20<<20)))+1);
419 //enc^=0x6f646573;//0x20796220;
420
421 send(enc);
422 }
423
424 //fcrc |= (sendsize<<16);
425 printf("ROM done! CRC: %08lx\n", kcrc);
426 //get crc back (unused)
427
428 // Get KeyC derivation material from GBA (eventually)
429 u32 keyCderive = 0;
430 do
431 {
432 keyCderive = recv();
433 } while (keyCderive <= 0xfeffffff);
434
435 keyCderive = __builtin_bswap32(keyCderive);
436 keyCderive >>= 8;
437
438 printf("KeyC derivation material: %08lx\n",keyCderive);
439
440 // (try to) find the KeyC, using the checksum of the multiboot image, and
441 // the derivation material that GBA sent to us
442 u32 keyc = deriveKeyC(keyCderive,kcrc);
443 if (keyc == 0)
444 {
445 printf("Could not find keyC - kcrc=0x%08lx\n",kcrc);
446 warnError("Cannot continue.\n");
447
448 continue;
449 }
450
451 // derive the boot key from the found KeyC, and send to GBA. if this is
452 // not correct, GBA will not jump to the multiboot image it was sent.
453 u32 bootkey = docrc(0xBB,keyc) | 0xbb000000;
454 printf("BootKey = 0x%08lx\n",bootkey);
455
456 send(bootkey);
457 sleep(2);
458
459 printf("Waiting for GBA...\n");
460 while (recv() != 0) {sleep(1);};
461 send(0);
462
463 VIDEO_WaitVSync();
464
465 // Get game
466 // -1 - unsupported game
467 // 1 - Ruby
468 // 2 - Sapphire
469 // 3 - FireRed
470 // 4 - LeafGreen
471 // 5 - Emerald
472 u32 gameId = getMsg();
473 if (gameId == -1)
474 {
475 warnError("ERROR: Unsupported GBA game inserted!\n");
476
477 continue;
478 }
479
480 printf("\nPokemon ");
481 switch (gameId)
482 {
483 case 1: printf("Ruby"); break;
484 case 2: printf("Sapphire"); break;
485 case 3: printf("FireRed"); break;
486 case 4: printf("LeafGreen"); break;
487 case 5: printf("Emerald"); break;
488 }
489
490 printf("\n");
491 VIDEO_WaitVSync();
492
493 u32 isValid = getMsg();
494 if (isValid == -1)
495 {
496 warnError("ERROR: Unsupported game version inserted!\n");
497
498 continue;
499 }
500
501 // Get trainer name
502 u8 trainerName[8];
503
504 u32 tnd = getMsg();
505 trainerName[0] = (tnd & 0xFF000000) >> 24;
506 trainerName[1] = (tnd & 0x00FF0000) >> 16;
507 trainerName[2] = (tnd & 0x0000FF00) >> 8;
508 trainerName[3] = (tnd & 0x000000FF);
509
510 tnd = getMsg();
511 trainerName[4] = (tnd & 0xFF000000) >> 24;
512 trainerName[5] = (tnd & 0x00FF0000) >> 16;
513 trainerName[6] = (tnd & 0x0000FF00) >> 8;
514 trainerName[7] = (tnd & 0x000000FF);
515
516 // Get trainer ID
517 u32 trainerId = getMsg();
518
519 printf("Trainer: ");
520
521 for (int i = 0; i < 8; i++)
522 {
523 if (trainerName[i] == 0xFF)
524 {
525 break;
526 } else {
527 printf("%c", debugGen3Decode(trainerName[i]));
528 }
529 }
530
531 printf(" (%ld)\n", trainerId);
532
533 // Wait for confirmation.
534 printf("Press A to import the data from this game.\n");
535 printf("Press B to cancel.\n");
536 VIDEO_WaitVSync();
537
538 if (waitForButtons(PAD_BUTTON_A | PAD_BUTTON_B) & PAD_BUTTON_B)
539 {
540 printf("Cancelling...\n");
541 VIDEO_WaitVSync();
542
543 sendMsg(0);
544
545 continue;
546 }
547
548 printf("Importing...\n");
549 VIDEO_WaitVSync();
550
551 sendMsg(1);
552
553 // Get Pokédex data
554 u32 pokedexSeen[13];
555 u32 pokedexCaught[13];
556
557 getMsgArr(pokedexSeen, 13);
558 getMsgArr(pokedexCaught, 13);
559 int numCaught = 0;
560 int numSeen = 0;
561 for (int i=0; i<(13*32); i++)
562 {
563 if (pokedexCaught[i >> 5] >> (i & 31) & 1)
564 {
565 //printf("Caught #%d\n", i);
566 numCaught++;
567 numSeen++;
568 } else if (pokedexSeen[i >> 5] >> (i & 31) & 1)
569 {
570 //printf("Saw #%d\n", i);
571 numSeen++;
572 }
573 }
574
575 printf("Caught: %d\nSeen: %d\n", numCaught, numSeen);
576
577 waitForButtons(PAD_BUTTON_START);
578 } 250 }
579 } 251 }
580 252
diff --git a/source/multiboot.c b/source/multiboot.c new file mode 100644 index 0000000..06cbd6f --- /dev/null +++ b/source/multiboot.c
@@ -0,0 +1,246 @@
1#include "multiboot.h"
2#include <gccore.h>
3#include <unistd.h>
4#include <stdio.h>
5#include <stdlib.h>
6#include "link.h"
7
8extern u8 gba_mb_gba[];
9extern u32 gba_mb_gba_size;
10
11unsigned int docrc(u32 crc,u32 val)
12{
13 u32 result;
14
15 result = val ^ crc;
16 for (int i = 0; i < 0x20; i++)
17 {
18 if (result & 1)
19 {
20 result >>= 1;
21 result ^= 0xA1C1;
22 } else {
23 result >>= 1;
24 }
25 }
26
27 return result;
28}
29
30u32 genKeyA()
31{
32 u32 retries = 0;
33
34 for (;;)
35 {
36 u32 key = 0;
37
38 if (retries > 32)
39 {
40 key = 0xDD654321;
41 } else {
42 key = (rand() & 0x00ffffff) | 0xDD000000;
43 }
44
45 u32 unk = (key % 2 != 0);
46 u32 v12 = key;
47 for (u32 v13 = 1; v13 < 32; v13++)
48 {
49 v12 >>= 1;
50 unk += (v12 % 2 != 0);
51 }
52
53 if ((unk >= 10 && unk <= 24))
54 {
55 if (retries > 4)
56 {
57 printf("KeyA retries = %ld", retries);
58 }
59
60 printf("KeyA = 0x%08lx\n", key);
61
62 return key;
63 }
64
65 retries++;
66 }
67}
68
69u32 checkKeyB(u32 KeyBRaw)
70{
71 if ((KeyBRaw & 0xFF) != 0xEE)
72 {
73 printf("Invalid KeyB - lowest 8 bits should be 0xEE, actually 0x%02x\n",
74 ((u8)(KeyBRaw)));
75
76 return 0;
77 }
78
79 u32 KeyB = KeyBRaw & 0xffffff00;
80 u32 val = KeyB;
81 u32 unk = (val < 0);
82 for (u32 i = 1; i < 24; i++)
83 {
84 val <<= 1;
85 unk += (val < 0);
86 }
87
88 if (unk > 14)
89 {
90 printf("Invalid KeyB - high 24 bits bad: 0x%08lx\n", KeyB);
91
92 return 0;
93 }
94
95 printf("Valid KeyB: 0x%08lx\n", KeyB);
96
97 return KeyB;
98}
99
100u32 deriveKeyC(u32 keyCderive, u32 kcrc)
101{
102 u32 keyc = 0;
103 u32 keyCi = 0;
104
105 do
106 {
107 u32 v5 = 0x1000000 * keyCi - 1;
108 u32 keyCattempt = docrc(kcrc,v5);
109
110 if (keyCderive == keyCattempt)
111 {
112 keyc = v5;
113
114 printf("Found keyC: %08lx\n",keyc);
115
116 return keyc;
117 }
118
119 keyCi++;
120 } while (keyCi < 256);
121
122 return keyc;
123}
124
125bool sendMultibootImage()
126{
127 printf("Waiting on BIOS\n");
128 waitForBIOS();
129
130 printf("BIOS handed over to game, waiting on game\n");
131 waitForGame();
132
133 // receive the game-code from GBA side.
134 u32 gamecode = recv();
135
136 printf("Ready, sending multiboot ROM\n");
137
138 unsigned int sendsize = ((gba_mb_gba_size+7)&~7);
139
140 // generate KeyA
141 unsigned int ourkey = genKeyA();
142
143 //printf("Our Key: %08x\n", ourkey);
144 printf("Sending game code that we got: 0x%08lx\n",
145 __builtin_bswap32(gamecode));
146
147 // send the game code back, then KeyA.
148 send(__builtin_bswap32(gamecode));
149 send(ourkey);
150
151 // get KeyB from GBA, check it to make sure its valid, then xor with KeyA
152 // to derive the initial CRC value and the sessionkey.
153 u32 sessionkeyraw = 0;
154 do
155 {
156 sessionkeyraw = recv();
157 } while (sessionkeyraw == gamecode);
158
159 sessionkeyraw = checkKeyB(__builtin_bswap32(sessionkeyraw));
160 if (sessionkeyraw == 0)
161 {
162 return false;
163 }
164
165 u32 sessionkey = sessionkeyraw ^ ourkey;
166 u32 kcrc = sessionkey;
167 printf("start kCRC=%08lx\n",kcrc);
168
169 sessionkey = (sessionkey*0x6177614b)+1;
170
171 // send hacked up send-size in uint32s
172 u32 hackedupsize = (sendsize >> 3) - 1;
173
174 printf("Sending hacked up size 0x%08lx\n",hackedupsize);
175 send(hackedupsize);
176
177 //unsigned int fcrc = 0x00bb;
178 // send over multiboot binary header, in the clear until the end of the
179 // nintendo logo. GBA checks this, if nintendo logo does not match the
180 // one in currently inserted cart's ROM, it will not accept any more data.
181 for (int i = 0; i < 0xA0; i+=4)
182 {
183 vu32 rom_dword = *(vu32*)(gba_mb_gba+i);
184 send(__builtin_bswap32(rom_dword));
185 }
186
187 printf("\n");
188 printf("Header done! Sending ROM...\n");
189
190 // Add each uint32 of the multiboot image to the checksum, encrypt the
191 // uint32 with the session key, increment the session key, send the
192 // encrypted uint32.
193 for (int i = 0xA0; i < sendsize; i+=4)
194 {
195 u32 dec = (
196 (((gba_mb_gba[i+3]) << 24) & 0xff000000) |
197 (((gba_mb_gba[i+2]) << 16) & 0x00ff0000) |
198 (((gba_mb_gba[i+1]) << 8) & 0x0000ff00) |
199 (((gba_mb_gba[i]) << 0) & 0x000000ff)
200 );
201
202 u32 enc = (dec - kcrc) ^ sessionkey;
203 kcrc = docrc(kcrc,dec);
204 sessionkey = (sessionkey * 0x6177614B) + 1;
205 //enc^=((~(i+(0x20<<20)))+1);
206 //enc^=0x6f646573;//0x20796220;
207
208 send(enc);
209 }
210
211 //fcrc |= (sendsize<<16);
212 printf("ROM done! CRC: %08lx\n", kcrc);
213 //get crc back (unused)
214
215 // Get KeyC derivation material from GBA (eventually)
216 u32 keyCderive = 0;
217 do
218 {
219 keyCderive = recv();
220 } while (keyCderive <= 0xfeffffff);
221
222 keyCderive = __builtin_bswap32(keyCderive);
223 keyCderive >>= 8;
224
225 printf("KeyC derivation material: %08lx\n",keyCderive);
226
227 // (try to) find the KeyC, using the checksum of the multiboot image, and
228 // the derivation material that GBA sent to us
229 u32 keyc = deriveKeyC(keyCderive,kcrc);
230 if (keyc == 0)
231 {
232 printf("Could not find keyC - kcrc=0x%08lx\n",kcrc);
233
234 return false;
235 }
236
237 // derive the boot key from the found KeyC, and send to GBA. if this is
238 // not correct, GBA will not jump to the multiboot image it was sent.
239 u32 bootkey = docrc(0xBB,keyc) | 0xbb000000;
240 printf("BootKey = 0x%08lx\n",bootkey);
241
242 send(bootkey);
243 sleep(2);
244
245 return true;
246}
diff --git a/source/multiboot.h b/source/multiboot.h new file mode 100644 index 0000000..a8f5d0d --- /dev/null +++ b/source/multiboot.h
@@ -0,0 +1,8 @@
1#ifndef MULTIBOOT_H_5C3CB904
2#define MULTIBOOT_H_5C3CB904
3
4#include <stdbool.h>
5
6bool sendMultibootImage();
7
8#endif /* end of include guard: MULTIBOOT_H_5C3CB904 */