about summary refs log tree commit diff stats
path: root/source
diff options
context:
space:
mode:
authorslipstream/RoL <l33twax@yahoo.com>2017-02-19 01:19:35 +0000
committerslipstream/RoL <l33twax@yahoo.com>2017-02-19 01:19:35 +0000
commitd4e309bb19b74c4b21ca19952c9b1cdd067c667a (patch)
tree36d4e67a43ab746135a18de3eef4edb09867b568 /source
parent004575f7cec14946c1936aceca6efee38b7f8a74 (diff)
downloadgen3uploader-d4e309bb19b74c4b21ca19952c9b1cdd067c667a.tar.gz
gen3uploader-d4e309bb19b74c4b21ca19952c9b1cdd067c667a.tar.bz2
gen3uploader-d4e309bb19b74c4b21ca19952c9b1cdd067c667a.zip
Initial commit of the fork
Forking gba-link-cable-dumper to gba-gen3multiboot
Diffstat (limited to 'source')
-rw-r--r--source/main.c551
1 files changed, 157 insertions, 394 deletions
diff --git a/source/main.c b/source/main.c index 7fd5683..ad34eff 100644 --- a/source/main.c +++ b/source/main.c
@@ -1,4 +1,5 @@
1/* 1/*
2 * Copyright (c) 2017 slipstream/RoL
2 * Copyright (C) 2016 FIX94 3 * Copyright (C) 2016 FIX94
3 * 4 *
4 * This software may be modified and distributed under the terms 5 * This software may be modified and distributed under the terms
@@ -13,8 +14,6 @@
13#include <sys/types.h> 14#include <sys/types.h>
14#include <sys/stat.h> 15#include <sys/stat.h>
15#include <fcntl.h> 16#include <fcntl.h>
16#include <dirent.h>
17#include <fat.h>
18 17
19//from my tests 50us seems to be the lowest 18//from my tests 50us seems to be the lowest
20//safe si transfer delay in between calls 19//safe si transfer delay in between calls
@@ -27,9 +26,8 @@ void printmain()
27{ 26{
28 printf("\x1b[2J"); 27 printf("\x1b[2J");
29 printf("\x1b[37m"); 28 printf("\x1b[37m");
30 printf("GBA Link Cable Dumper v1.6 by FIX94\n"); 29 printf("Pokemon Gen 3 GBA Multiboot PoC by slipstream/RoL\n");
31 printf("Save Support based on SendSave by Chishm\n"); 30 printf("Based on GBA Link Cable Dumper by FIX94\n\n");
32 printf("GBA BIOS Dumper by Dark Fader\n \n");
33} 31}
34 32
35u8 *resbuf,*cmdbuf; 33u8 *resbuf,*cmdbuf;
@@ -46,21 +44,17 @@ void acb(s32 res, u32 val)
46 resval = val; 44 resval = val;
47} 45}
48 46
49unsigned int docrc(u32 crc, u32 val) 47unsigned int docrc(u32 crc,u32 val) {
50{ 48 u32 result;
51 int i; 49
52 for(i = 0; i < 0x20; i++) 50 result = val ^ crc;
53 { 51 for (int i = 0; i < 0x20; i++) {
54 if((crc^val)&1) 52 if (result & 1) {
55 { 53 result >>= 1;
56 crc>>=1; 54 result ^= 0xA1C1;
57 crc^=0xa1c1; 55 } else result >>= 1;
58 }
59 else
60 crc>>=1;
61 val>>=1;
62 } 56 }
63 return crc; 57 return result;
64} 58}
65 59
66void endproc() 60void endproc()
@@ -70,64 +64,7 @@ void endproc()
70 VIDEO_WaitVSync(); 64 VIDEO_WaitVSync();
71 exit(0); 65 exit(0);
72} 66}
73void fixFName(char *str)
74{
75 u8 i = 0;
76 for(i = 0; i < strlen(str); ++i)
77 {
78 if(str[i] < 0x20 || str[i] > 0x7F)
79 str[i] = '_';
80 else switch(str[i])
81 {
82 case '\\':
83 case '/':
84 case ':':
85 case '*':
86 case '?':
87 case '\"':
88 case '<':
89 case '>':
90 case '|':
91 str[i] = '_';
92 break;
93 default:
94 break;
95 }
96 }
97}
98unsigned int calckey(unsigned int size)
99{
100 unsigned int ret = 0;
101 size=(size-0x200) >> 3;
102 int res1 = (size&0x3F80) << 1;
103 res1 |= (size&0x4000) << 2;
104 res1 |= (size&0x7F);
105 res1 |= 0x380000;
106 int res2 = res1;
107 res1 = res2 >> 0x10;
108 int res3 = res2 >> 8;
109 res3 += res1;
110 res3 += res2;
111 res3 <<= 24;
112 res3 |= res2;
113 res3 |= 0x80808080;
114 67
115 if((res3&0x200) == 0)
116 {
117 ret |= (((res3)&0xFF)^0x4B)<<24;
118 ret |= (((res3>>8)&0xFF)^0x61)<<16;
119 ret |= (((res3>>16)&0xFF)^0x77)<<8;
120 ret |= (((res3>>24)&0xFF)^0x61);
121 }
122 else
123 {
124 ret |= (((res3)&0xFF)^0x73)<<24;
125 ret |= (((res3>>8)&0xFF)^0x65)<<16;
126 ret |= (((res3>>16)&0xFF)^0x64)<<8;
127 ret |= (((res3>>24)&0xFF)^0x6F);
128 }
129 return ret;
130}
131void doreset() 68void doreset()
132{ 69{
133 cmdbuf[0] = 0xFF; //reset 70 cmdbuf[0] = 0xFF; //reset
@@ -135,6 +72,7 @@ void doreset()
135 SI_Transfer(1,cmdbuf,1,resbuf,3,transcb,SI_TRANS_DELAY); 72 SI_Transfer(1,cmdbuf,1,resbuf,3,transcb,SI_TRANS_DELAY);
136 while(transval == 0) ; 73 while(transval == 0) ;
137} 74}
75
138void getstatus() 76void getstatus()
139{ 77{
140 cmdbuf[0] = 0; //status 78 cmdbuf[0] = 0; //status
@@ -142,6 +80,7 @@ void getstatus()
142 SI_Transfer(1,cmdbuf,1,resbuf,3,transcb,SI_TRANS_DELAY); 80 SI_Transfer(1,cmdbuf,1,resbuf,3,transcb,SI_TRANS_DELAY);
143 while(transval == 0) ; 81 while(transval == 0) ;
144} 82}
83
145u32 recv() 84u32 recv()
146{ 85{
147 memset(resbuf,0,32); 86 memset(resbuf,0,32);
@@ -151,6 +90,7 @@ u32 recv()
151 while(transval == 0) ; 90 while(transval == 0) ;
152 return *(vu32*)resbuf; 91 return *(vu32*)resbuf;
153} 92}
93
154void send(u32 msg) 94void send(u32 msg)
155{ 95{
156 cmdbuf[0]=0x15;cmdbuf[1]=(msg>>0)&0xFF;cmdbuf[2]=(msg>>8)&0xFF; 96 cmdbuf[0]=0x15;cmdbuf[1]=(msg>>0)&0xFF;cmdbuf[2]=(msg>>8)&0xFF;
@@ -160,26 +100,7 @@ void send(u32 msg)
160 SI_Transfer(1,cmdbuf,5,resbuf,1,transcb,SI_TRANS_DELAY); 100 SI_Transfer(1,cmdbuf,5,resbuf,1,transcb,SI_TRANS_DELAY);
161 while(transval == 0) ; 101 while(transval == 0) ;
162} 102}
163bool dirExists(const char *path) 103
164{
165 DIR *dir;
166 dir = opendir(path);
167 if(dir)
168 {
169 closedir(dir);
170 return true;
171 }
172 return false;
173}
174void createFile(const char *path, size_t size)
175{
176 int fd = open(path, O_WRONLY|O_CREAT);
177 if(fd >= 0)
178 {
179 ftruncate(fd, size);
180 close(fd);
181 }
182}
183void warnError(char *msg) 104void warnError(char *msg)
184{ 105{
185 puts(msg); 106 puts(msg);
@@ -195,6 +116,68 @@ void fatalError(char *msg)
195 sleep(5); 116 sleep(5);
196 exit(0); 117 exit(0);
197} 118}
119
120u32 genKeyA() {
121 u32 retries = 0;
122 while (true) {
123 u32 key = 0;
124 if (retries > 32) {
125 key = 0xDD654321;
126 } else {
127 key = (rand() & 0x00ffffff) | 0xDD000000;
128 }
129 u32 unk = (key % 2 != 0);
130 u32 v12 = key;
131 for (u32 v13 = 1; v13 < 32; v13++) {
132 v12 >>= 1;
133 unk += (v12 % 2 != 0);
134 }
135 if ((unk >= 10 && unk <= 24)) {
136 if (retries > 4) printf("KeyA retries = %d",retries);
137 printf("KeyA = 0x%08x\n",key);
138 return key;
139 }
140 retries++;
141 }
142}
143
144u32 checkKeyB(u32 KeyBRaw) {
145 if ((KeyBRaw & 0xFF) != 0xEE) {
146 printf("Invalid KeyB - lowest 8 bits should be 0xEE, actually 0x%02x\n",((u8)(KeyBRaw)));
147 return 0;
148 }
149 u32 KeyB = KeyBRaw & 0xffffff00;
150 u32 val = KeyB;
151 u32 unk = (val < 0);
152 for (u32 i = 1; i < 24; i++) {
153 val <<= 1;
154 unk += (val < 0);
155 }
156 if (unk > 14) {
157 printf("Invalid KeyB - high 24 bits bad: 0x%08x\n",KeyB);
158 return 0;
159 }
160 printf("Valid KeyB: 0x%08x\n",KeyB);
161 return KeyB;
162}
163
164u32 deriveKeyC(u32 keyCderive, u32 kcrc) {
165 u32 keyc = 0;
166 u32 keyCi = 0;
167 do {
168 u32 v5 = 0x1000000 * keyCi - 1;
169 u32 keyCattempt = docrc(kcrc,v5);
170 //printf("i = %d; keyCderive = %08x; keyCattempt = %08x\n",keyCi,keyCderive,keyCattempt);
171 if (keyCderive == keyCattempt) {
172 keyc = v5;
173 printf("Found keyC: %08x\n",keyc);
174 return keyc;
175 }
176 keyCi++;
177 } while (keyCi < 256);
178 return keyc;
179}
180
198int main(int argc, char *argv[]) 181int main(int argc, char *argv[])
199{ 182{
200 void *xfb = NULL; 183 void *xfb = NULL;
@@ -216,19 +199,6 @@ int main(int argc, char *argv[])
216 PAD_Init(); 199 PAD_Init();
217 cmdbuf = memalign(32,32); 200 cmdbuf = memalign(32,32);
218 resbuf = memalign(32,32); 201 resbuf = memalign(32,32);
219 u8 *testdump = memalign(32,0x400000);
220 if(!testdump) return 0;
221 if(!fatInitDefault())
222 {
223 printmain();
224 fatalError("ERROR: No usable device found to write dumped files to!");
225 }
226 mkdir("/dumps", S_IREAD | S_IWRITE);
227 if(!dirExists("/dumps"))
228 {
229 printmain();
230 fatalError("ERROR: Could not create dumps folder, make sure you have a supported device connected!");
231 }
232 int i; 202 int i;
233 while(1) 203 while(1)
234 { 204 {
@@ -251,305 +221,98 @@ int main(int argc, char *argv[])
251 } 221 }
252 PAD_ScanPads(); 222 PAD_ScanPads();
253 VIDEO_WaitVSync(); 223 VIDEO_WaitVSync();
254 if(PAD_ButtonsHeld(0)) 224 if(PAD_ButtonsDown(0) & PAD_BUTTON_START)
255 endproc(); 225 endproc();
256 } 226 }
257 if(resval & SI_GBA) 227 if (resval & SI_GBA)
258 { 228 {
259 printf("GBA Found! Waiting on BIOS\n"); 229 printf("GBA Found! Waiting on BIOS\n");
260 resbuf[2]=0; 230 resbuf[2]=0;
261 while(!(resbuf[2]&0x10)) 231 u32 oldresult = 0;
232 u32 newresult = 0;
233 // wait for the BIOS to hand over to the game
234 do {
235 doreset();
236 } while (!(resbuf[1] > 4));
237 printf("BIOS handed over to game, waiting on game\n");
238 do
262 { 239 {
263 doreset(); 240 doreset();
264 getstatus(); 241 } while((resbuf[0] != 0) || !(resbuf[2]&0x10));
265 } 242 // receive the game-code from GBA side.
266 printf("Ready, sending dumper\n"); 243 u32 gamecode = recv();
244 printf("Ready, sending multiboot ROM\n");
267 unsigned int sendsize = ((gba_mb_gba_size+7)&~7); 245 unsigned int sendsize = ((gba_mb_gba_size+7)&~7);
268 unsigned int ourkey = calckey(sendsize); 246 // generate KeyA
247 unsigned int ourkey = genKeyA();
269 //printf("Our Key: %08x\n", ourkey); 248 //printf("Our Key: %08x\n", ourkey);
270 //get current sessionkey 249 printf("Sending game code that we got: 0x%08x\n",__builtin_bswap32(gamecode));
271 u32 sessionkeyraw = recv(); 250 // send the game code back, then KeyA.
272 u32 sessionkey = __builtin_bswap32(sessionkeyraw^0x7365646F); 251 send(__builtin_bswap32(gamecode));
273 //send over our own key 252 send(ourkey);
274 send(__builtin_bswap32(ourkey)); 253 // get KeyB from GBA, check it to make sure its valid, then xor with KeyA to derive the initial CRC value and the sessionkey.
275 unsigned int fcrc = 0x15a0; 254 u32 sessionkeyraw = 0;
276 //send over gba header 255 do {
277 for(i = 0; i < 0xC0; i+=4) 256 sessionkeyraw = recv();
278 send(__builtin_bswap32(*(vu32*)(gba_mb_gba+i))); 257 } while (sessionkeyraw == gamecode);
279 //printf("Header done! Sending ROM...\n"); 258 sessionkeyraw = checkKeyB(__builtin_bswap32(sessionkeyraw));
280 for(i = 0xC0; i < sendsize; i+=4) 259 u32 sessionkey = sessionkeyraw ^ ourkey;
260 u32 kcrc = sessionkey;
261 printf("start kCRC=%08x\n",kcrc);
262 sessionkey = (sessionkey*0x6177614b)+1;
263 // send hacked up send-size in uint32s
264 u32 hackedupsize = (sendsize >> 3) - 1;
265 printf("Sending hacked up size 0x%08x\n",hackedupsize);
266 send(hackedupsize);
267 //unsigned int fcrc = 0x00bb;
268 // send over multiboot binary header, in the clear until the end of the nintendo logo.
269 // GBA checks this, if nintendo logo does not match the one in currently inserted cart's ROM, it will not accept any more data.
270 for(i = 0; i < 0xA0; i+=4) {
271 vu32 rom_dword = *(vu32*)(gba_mb_gba+i);
272 send(__builtin_bswap32(rom_dword));
273 }
274 printf("\n");
275 printf("Header done! Sending ROM...\n");
276 // Add each uint32 of the multiboot image to the checksum, encrypt the uint32 with the session key, increment the session key, send the encrypted uint32.
277 for(i = 0xA0; i < sendsize; i+=4)
281 { 278 {
282 u32 enc = ((gba_mb_gba[i+3]<<24)|(gba_mb_gba[i+2]<<16)|(gba_mb_gba[i+1]<<8)|(gba_mb_gba[i])); 279 u32 dec = (
283 fcrc=docrc(fcrc,enc); 280 ((gba_mb_gba[i+3]) << 24) & 0xff000000 |
281 ((gba_mb_gba[i+2]) << 16) & 0x00ff0000 |
282 ((gba_mb_gba[i+1]) << 8) & 0x0000ff00 |
283 ((gba_mb_gba[i]) << 0) & 0x000000ff
284 );
285 u32 enc = (dec - kcrc) ^ sessionkey;
286 kcrc=docrc(kcrc,dec);
284 sessionkey = (sessionkey*0x6177614B)+1; 287 sessionkey = (sessionkey*0x6177614B)+1;
285 enc^=sessionkey; 288 //enc^=((~(i+(0x20<<20)))+1);
286 enc^=((~(i+(0x20<<20)))+1); 289 //enc^=0x6f646573;//0x20796220;
287 enc^=0x20796220;
288 send(enc); 290 send(enc);
289 } 291 }
290 fcrc |= (sendsize<<16); 292 //fcrc |= (sendsize<<16);
291 //printf("ROM done! CRC: %08x\n", fcrc); 293 printf("ROM done! CRC: %08x\n", kcrc);
292 //send over CRC
293 sessionkey = (sessionkey*0x6177614B)+1;
294 fcrc^=sessionkey;
295 fcrc^=((~(i+(0x20<<20)))+1);
296 fcrc^=0x20796220;
297 send(fcrc);
298 //get crc back (unused) 294 //get crc back (unused)
299 recv(); 295 // Get KeyC derivation material from GBA (eventually)
300 printf("Done!\n"); 296 u32 keyCderive = 0;
301 sleep(2); 297 do {
302 //hm 298 keyCderive = recv();
303 while(1) 299 } while (keyCderive <= 0xfeffffff);
304 { 300 keyCderive = __builtin_bswap32(keyCderive);
305 printmain(); 301 keyCderive >>= 8;
306 printf("Press A once you have a GBA Game inserted.\n"); 302 printf("KeyC derivation material: %08x\n",keyCderive);
307 printf("Press Y to backup the GBA BIOS.\n \n"); 303
308 PAD_ScanPads(); 304 // (try to) find the KeyC, using the checksum of the multiboot image, and the derivation material that GBA sent to us
309 VIDEO_WaitVSync(); 305
310 u32 btns = PAD_ButtonsDown(0); 306 u32 keyc = deriveKeyC(keyCderive,kcrc);
311 if(btns&PAD_BUTTON_START) 307 if (keyc == 0) printf("Could not find keyC - kcrc=0x%08x\n",kcrc);
312 endproc(); 308
313 else if(btns&PAD_BUTTON_A) 309 // derive the boot key from the found KeyC, and send to GBA. if this is not correct, GBA will not jump to the multiboot image it was sent.
314 { 310 u32 bootkey = docrc(0xBB,keyc) | 0xbb000000;
315 if(recv() == 0) //ready 311 printf("BootKey = 0x%08x\n",bootkey);
316 { 312 send(bootkey);
317 printf("Waiting for GBA\n"); 313
318 VIDEO_WaitVSync(); 314 printf("Done! Press any key to start sending again.\n");
319 int gbasize = 0; 315 do { PAD_ScanPads(); } while (!PAD_ButtonsDown(0));
320 while(gbasize == 0)
321 gbasize = __builtin_bswap32(recv());
322 send(0); //got gbasize
323 u32 savesize = __builtin_bswap32(recv());
324 send(0); //got savesize
325 if(gbasize == -1)
326 {
327 warnError("ERROR: No (Valid) GBA Card inserted!\n");
328 continue;
329 }
330 //get rom header
331 for(i = 0; i < 0xC0; i+=4)
332 *(vu32*)(testdump+i) = recv();
333 //print out all the info from the game
334 printf("Game Name: %.12s\n",(char*)(testdump+0xA0));
335 printf("Game ID: %.4s\n",(char*)(testdump+0xAC));
336 printf("Company ID: %.2s\n",(char*)(testdump+0xB0));
337 printf("ROM Size: %02.02f MB\n",((float)(gbasize/1024))/1024.f);
338 if(savesize > 0)
339 printf("Save Size: %02.02f KB\n \n",((float)(savesize))/1024.f);
340 else
341 printf("No Save File\n \n");
342 //generate file paths
343 char gamename[64];
344 sprintf(gamename,"/dumps/%.12s [%.4s%.2s].gba",
345 (char*)(testdump+0xA0),(char*)(testdump+0xAC),(char*)(testdump+0xB0));
346 fixFName(gamename+7); //fix name behind "/dumps/"
347 char savename[64];
348 sprintf(savename,"/dumps/%.12s [%.4s%.2s].sav",
349 (char*)(testdump+0xA0),(char*)(testdump+0xAC),(char*)(testdump+0xB0));
350 fixFName(savename+7); //fix name behind "/dumps/"
351 //let the user choose the option
352 printf("Press A to dump this game, it will take about %i minutes.\n",gbasize/1024/1024*3/2);
353 printf("Press B if you want to cancel dumping this game.\n");
354 if(savesize > 0)
355 {
356 printf("Press Y to backup this save file.\n");
357 printf("Press X to restore this save file.\n");
358 printf("Press Z to clear the save file on the GBA Cartridge.\n\n");
359 }
360 else
361 printf("\n");
362 int command = 0;
363 while(1)
364 {
365 PAD_ScanPads();
366 VIDEO_WaitVSync();
367 u32 btns = PAD_ButtonsDown(0);
368 if(btns&PAD_BUTTON_START)
369 endproc();
370 else if(btns&PAD_BUTTON_A)
371 {
372 command = 1;
373 break;
374 }
375 else if(btns&PAD_BUTTON_B)
376 break;
377 else if(savesize > 0)
378 {
379 if(btns&PAD_BUTTON_Y)
380 {
381 command = 2;
382 break;
383 }
384 else if(btns&PAD_BUTTON_X)
385 {
386 command = 3;
387 break;
388 }
389 else if(btns&PAD_TRIGGER_Z)
390 {
391 command = 4;
392 break;
393 }
394 }
395 }
396 if(command == 1)
397 {
398 FILE *f = fopen(gamename,"rb");
399 if(f)
400 {
401 fclose(f);
402 command = 0;
403 warnError("ERROR: Game already dumped!\n");
404 }
405 }
406 else if(command == 2)
407 {
408 FILE *f = fopen(savename,"rb");
409 if(f)
410 {
411 fclose(f);
412 command = 0;
413 warnError("ERROR: Save already backed up!\n");
414 }
415 }
416 else if(command == 3)
417 {
418 size_t readsize = 0;
419 FILE *f = fopen(savename,"rb");
420 if(f)
421 {
422 fseek(f,0,SEEK_END);
423 readsize = ftell(f);
424 if(readsize != savesize)
425 {
426 command = 0;
427 warnError("ERROR: Save has the wrong size, aborting restore!\n");
428 }
429 else
430 {
431 rewind(f);
432 fread(testdump,readsize,1,f);
433 }
434 fclose(f);
435 }
436 else
437 {
438 command = 0;
439 warnError("ERROR: No Save to restore!\n");
440 }
441 }
442 send(command);
443 //let gba prepare
444 sleep(1);
445 if(command == 0)
446 continue;
447 else if(command == 1)
448 {
449 //create base file with size
450 printf("Preparing file...\n");
451 createFile(gamename,gbasize);
452 FILE *f = fopen(gamename,"wb");
453 if(!f)
454 fatalError("ERROR: Could not create file! Exit...");
455 printf("Dumping...\n");
456 u32 bytes_read = 0;
457 while(gbasize > 0)
458 {
459 int toread = (gbasize > 0x400000 ? 0x400000 : gbasize);
460 int j;
461 for(j = 0; j < toread; j+=4)
462 {
463 *(vu32*)(testdump+j) = recv();
464 bytes_read+=4;
465 if((bytes_read&0xFFFF) == 0)
466 printf("\r%02.02f MB done",(float)(bytes_read/1024)/1024.f);
467 }
468 fwrite(testdump,toread,1,f);
469 gbasize -= toread;
470 }
471 printf("\nClosing file\n");
472 fclose(f);
473 printf("Game dumped!\n");
474 sleep(5);
475 }
476 else if(command == 2)
477 {
478 //create base file with size
479 printf("Preparing file...\n");
480 createFile(savename,savesize);
481 FILE *f = fopen(savename,"wb");
482 if(!f)
483 fatalError("ERROR: Could not create file! Exit...");
484 printf("Waiting for GBA\n");
485 VIDEO_WaitVSync();
486 u32 readval = 0;
487 while(readval != savesize)
488 readval = __builtin_bswap32(recv());
489 send(0); //got savesize
490 printf("Receiving...\n");
491 for(i = 0; i < savesize; i+=4)
492 *(vu32*)(testdump+i) = recv();
493 printf("Writing save...\n");
494 fwrite(testdump,savesize,1,f);
495 fclose(f);
496 printf("Save backed up!\n");
497 sleep(5);
498 }
499 else if(command == 3 || command == 4)
500 {
501 u32 readval = 0;
502 while(readval != savesize)
503 readval = __builtin_bswap32(recv());
504 if(command == 3)
505 {
506 printf("Sending save\n");
507 VIDEO_WaitVSync();
508 for(i = 0; i < savesize; i+=4)
509 send(__builtin_bswap32(*(vu32*)(testdump+i)));
510 }
511 printf("Waiting for GBA\n");
512 while(recv() != 0)
513 VIDEO_WaitVSync();
514 printf(command == 3 ? "Save restored!\n" : "Save cleared!\n");
515 send(0);
516 sleep(5);
517 }
518 }
519 }
520 else if(btns&PAD_BUTTON_Y)
521 {
522 const char *biosname = "/dumps/gba_bios.bin";
523 FILE *f = fopen(biosname,"rb");
524 if(f)
525 {
526 fclose(f);
527 warnError("ERROR: BIOS already backed up!\n");
528 }
529 else
530 {
531 //create base file with size
532 printf("Preparing file...\n");
533 createFile(biosname,0x4000);
534 f = fopen(biosname,"wb");
535 if(!f)
536 fatalError("ERROR: Could not create file! Exit...");
537 //send over bios dump command
538 send(5);
539 //the gba might still be in a loop itself
540 sleep(1);
541 //lets go!
542 printf("Dumping...\n");
543 for(i = 0; i < 0x4000; i+=4)
544 *(vu32*)(testdump+i) = recv();
545 fwrite(testdump,0x4000,1,f);
546 printf("Closing file\n");
547 fclose(f);
548 printf("BIOS dumped!\n");
549 sleep(5);
550 }
551 }
552 }
553 } 316 }
554 } 317 }
555 return 0; 318 return 0;