about summary refs log tree commit diff stats
path: root/source/main.c
diff options
context:
space:
mode:
Diffstat (limited to 'source/main.c')
-rw-r--r--source/main.c596
1 files changed, 134 insertions, 462 deletions
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