about summary refs log tree commit diff stats
path: root/source
diff options
context:
space:
mode:
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 */