diff options
Diffstat (limited to 'source/main.c')
-rw-r--r-- | source/main.c | 596 |
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 | |||
22 | extern u8 gba_mb_gba[]; | ||
23 | extern u32 gba_mb_gba_size; | ||
24 | 16 | ||
25 | void printmain() | 17 | void 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 | ||
34 | u8 *resbuf,*cmdbuf; | ||
35 | |||
36 | volatile u32 transval = 0; | ||
37 | void transcb(s32 chan, u32 ret) | ||
38 | { | ||
39 | transval = 1; | ||
40 | } | ||
41 | |||
42 | volatile u32 resval = 0; | ||
43 | void acb(s32 res, u32 val) | ||
44 | { | ||
45 | resval = val; | ||
46 | } | ||
47 | |||
48 | unsigned 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 | |||
67 | void 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 | |||
76 | void 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 | |||
85 | void endproc() | 26 | void 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 | ||
94 | u32 recv() | 34 | u32 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 | |||
106 | void 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 | ||
121 | void warnError(char *msg) | 49 | void warnError(char *msg) |
@@ -135,132 +63,148 @@ void fatalError(char *msg) | |||
135 | exit(0); | 63 | exit(0); |
136 | } | 64 | } |
137 | 65 | ||
138 | u32 genKeyA() | 66 | void* 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 | ||
177 | u32 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 | ||
208 | u32 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 | ||
233 | u32 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 | ||
249 | void 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 | ||
258 | void 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 | ||
266 | int main(int argc, char *argv[]) | 210 | int 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 | ||