diff options
Diffstat (limited to 'source')
| -rw-r--r-- | source/main.c | 400 |
1 files changed, 400 insertions, 0 deletions
| diff --git a/source/main.c b/source/main.c new file mode 100644 index 0000000..afe3423 --- /dev/null +++ b/source/main.c | |||
| @@ -0,0 +1,400 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2016 FIX94 | ||
| 3 | * | ||
| 4 | * This software may be modified and distributed under the terms | ||
| 5 | * of the MIT license. See the LICENSE file for details. | ||
| 6 | */ | ||
| 7 | #include <gccore.h> | ||
| 8 | #include <stdio.h> | ||
| 9 | #include <malloc.h> | ||
| 10 | #include <unistd.h> | ||
| 11 | #include <string.h> | ||
| 12 | #include <stdlib.h> | ||
| 13 | #include <sys/types.h> | ||
| 14 | #include <sys/stat.h> | ||
| 15 | #include <fcntl.h> | ||
| 16 | #include <dirent.h> | ||
| 17 | #include <fat.h> | ||
| 18 | |||
| 19 | extern u8 gba_mb_gba[]; | ||
| 20 | extern u32 gba_mb_gba_size; | ||
| 21 | |||
| 22 | void printmain() | ||
| 23 | { | ||
| 24 | printf("\x1b[2J"); | ||
| 25 | printf("\x1b[37m"); | ||
| 26 | printf("GBA Link Cable Dumper v1.0 by FIX94\n"); | ||
| 27 | } | ||
| 28 | |||
| 29 | u8 *resbuf,*cmdbuf; | ||
| 30 | volatile u16 pads = 0; | ||
| 31 | volatile bool ctrlerr = false; | ||
| 32 | void ctrlcb(s32 chan, u32 ret) | ||
| 33 | { | ||
| 34 | if(ret) | ||
| 35 | { | ||
| 36 | ctrlerr = true; | ||
| 37 | return; | ||
| 38 | } | ||
| 39 | //just call us again | ||
| 40 | pads = (~((resbuf[1]<<8)|resbuf[0]))&0x3FF; | ||
| 41 | SI_Transfer(1,cmdbuf,1,resbuf,5,ctrlcb,350); | ||
| 42 | } | ||
| 43 | |||
| 44 | volatile u32 transval = 0; | ||
| 45 | void transcb(s32 chan, u32 ret) | ||
| 46 | { | ||
| 47 | transval = 1; | ||
| 48 | } | ||
| 49 | |||
| 50 | volatile u32 resval = 0; | ||
| 51 | void acb(s32 res, u32 val) | ||
| 52 | { | ||
| 53 | resval = val; | ||
| 54 | } | ||
| 55 | |||
| 56 | unsigned int docrc(u32 crc, u32 val) | ||
| 57 | { | ||
| 58 | int i; | ||
| 59 | for(i = 0; i < 0x20; i++) | ||
| 60 | { | ||
| 61 | if((crc^val)&1) | ||
| 62 | { | ||
| 63 | crc>>=1; | ||
| 64 | crc^=0xa1c1; | ||
| 65 | } | ||
| 66 | else | ||
| 67 | crc>>=1; | ||
| 68 | val>>=1; | ||
| 69 | } | ||
| 70 | return crc; | ||
| 71 | } | ||
| 72 | |||
| 73 | static inline void wait_for_transfer() | ||
| 74 | { | ||
| 75 | //350 is REALLY pushing it already, cant go further | ||
| 76 | do{ usleep(350); }while(transval == 0); | ||
| 77 | } | ||
| 78 | |||
| 79 | void endproc() | ||
| 80 | { | ||
| 81 | printf("Start pressed, exit\n"); | ||
| 82 | VIDEO_WaitVSync(); | ||
| 83 | VIDEO_WaitVSync(); | ||
| 84 | exit(0); | ||
| 85 | } | ||
| 86 | unsigned int calckey(unsigned int size) | ||
| 87 | { | ||
| 88 | unsigned int ret = 0; | ||
| 89 | size=(size-0x200) >> 3; | ||
| 90 | int res1 = (size&0x3F80) << 1; | ||
| 91 | res1 |= (size&0x4000) << 2; | ||
| 92 | res1 |= (size&0x7F); | ||
| 93 | res1 |= 0x380000; | ||
| 94 | int res2 = res1; | ||
| 95 | res1 = res2 >> 0x10; | ||
| 96 | int res3 = res2 >> 8; | ||
| 97 | res3 += res1; | ||
| 98 | res3 += res2; | ||
| 99 | res3 <<= 24; | ||
| 100 | res3 |= res2; | ||
| 101 | res3 |= 0x80808080; | ||
| 102 | |||
| 103 | if((res3&0x200) == 0) | ||
| 104 | { | ||
| 105 | ret |= (((res3)&0xFF)^0x4B)<<24; | ||
| 106 | ret |= (((res3>>8)&0xFF)^0x61)<<16; | ||
| 107 | ret |= (((res3>>16)&0xFF)^0x77)<<8; | ||
| 108 | ret |= (((res3>>24)&0xFF)^0x61); | ||
| 109 | } | ||
| 110 | else | ||
| 111 | { | ||
| 112 | ret |= (((res3)&0xFF)^0x73)<<24; | ||
| 113 | ret |= (((res3>>8)&0xFF)^0x65)<<16; | ||
| 114 | ret |= (((res3>>16)&0xFF)^0x64)<<8; | ||
| 115 | ret |= (((res3>>24)&0xFF)^0x6F); | ||
| 116 | } | ||
| 117 | return ret; | ||
| 118 | } | ||
| 119 | void doreset() | ||
| 120 | { | ||
| 121 | cmdbuf[0] = 0xFF; //reset | ||
| 122 | transval = 0; | ||
| 123 | SI_Transfer(1,cmdbuf,1,resbuf,3,transcb,0); | ||
| 124 | wait_for_transfer(); | ||
| 125 | } | ||
| 126 | void getstatus() | ||
| 127 | { | ||
| 128 | cmdbuf[0] = 0; //status | ||
| 129 | transval = 0; | ||
| 130 | SI_Transfer(1,cmdbuf,1,resbuf,3,transcb,0); | ||
| 131 | wait_for_transfer(); | ||
| 132 | } | ||
| 133 | u32 recvsafe() | ||
| 134 | { | ||
| 135 | memset(resbuf,0,32); | ||
| 136 | cmdbuf[0]=0x14; //read | ||
| 137 | transval = 0; | ||
| 138 | SI_Transfer(1,cmdbuf,1,resbuf,5,transcb,0); | ||
| 139 | wait_for_transfer(); | ||
| 140 | return *(vu32*)resbuf; | ||
| 141 | } | ||
| 142 | void sendsafe(u32 msg) | ||
| 143 | { | ||
| 144 | cmdbuf[0]=0x15;cmdbuf[1]=(msg>>0)&0xFF;cmdbuf[2]=(msg>>8)&0xFF; | ||
| 145 | cmdbuf[3]=(msg>>16)&0xFF;cmdbuf[4]=(msg>>24)&0xFF; | ||
| 146 | transval = 0; | ||
| 147 | resbuf[0] = 0; | ||
| 148 | SI_Transfer(1,cmdbuf,5,resbuf,1,transcb,0); | ||
| 149 | wait_for_transfer(); | ||
| 150 | } | ||
| 151 | u32 recvfast() | ||
| 152 | { | ||
| 153 | cmdbuf[0]=0x14; //read | ||
| 154 | transval = 0; | ||
| 155 | SI_Transfer(1,cmdbuf,1,resbuf,5,transcb,0); | ||
| 156 | usleep(275); | ||
| 157 | while(transval == 0) ; | ||
| 158 | return *(vu32*)resbuf; | ||
| 159 | } | ||
| 160 | bool dirExists(const char *path) | ||
| 161 | { | ||
| 162 | DIR *dir; | ||
| 163 | dir = opendir(path); | ||
| 164 | if(dir) | ||
| 165 | { | ||
| 166 | closedir(dir); | ||
| 167 | return true; | ||
| 168 | } | ||
| 169 | return false; | ||
| 170 | } | ||
| 171 | int main(int argc, char *argv[]) | ||
| 172 | { | ||
| 173 | void *xfb = NULL; | ||
| 174 | GXRModeObj *rmode = NULL; | ||
| 175 | VIDEO_Init(); | ||
| 176 | rmode = VIDEO_GetPreferredMode(NULL); | ||
| 177 | xfb = MEM_K0_TO_K1(SYS_AllocateFramebuffer(rmode)); | ||
| 178 | VIDEO_Configure(rmode); | ||
| 179 | VIDEO_SetNextFramebuffer(xfb); | ||
| 180 | VIDEO_SetBlack(FALSE); | ||
| 181 | VIDEO_Flush(); | ||
| 182 | VIDEO_WaitVSync(); | ||
| 183 | if(rmode->viTVMode&VI_NON_INTERLACE) VIDEO_WaitVSync(); | ||
| 184 | int x = 24, y = 32, w, h; | ||
| 185 | w = rmode->fbWidth - (32); | ||
| 186 | h = rmode->xfbHeight - (48); | ||
| 187 | CON_InitEx(rmode, x, y, w, h); | ||
| 188 | VIDEO_ClearFrameBuffer(rmode, xfb, COLOR_BLACK); | ||
| 189 | PAD_Init(); | ||
| 190 | cmdbuf = memalign(32,32); | ||
| 191 | resbuf = memalign(32,32); | ||
| 192 | u8 *testdump = memalign(32,0x400000); | ||
| 193 | if(!testdump) return 0; | ||
| 194 | if(!fatInitDefault()) | ||
| 195 | { | ||
| 196 | printmain(); | ||
| 197 | printf("ERROR: No usable device found to write dumped files to!\n"); | ||
| 198 | VIDEO_WaitVSync(); | ||
| 199 | VIDEO_WaitVSync(); | ||
| 200 | sleep(5); | ||
| 201 | exit(0); | ||
| 202 | } | ||
| 203 | mkdir("/dumps", S_IREAD | S_IWRITE); | ||
| 204 | if(!dirExists("/dumps")) | ||
| 205 | { | ||
| 206 | printmain(); | ||
| 207 | printf("ERROR: Could not create dumps folder, make sure you have a supported device connected!\n"); | ||
| 208 | VIDEO_WaitVSync(); | ||
| 209 | VIDEO_WaitVSync(); | ||
| 210 | sleep(5); | ||
| 211 | exit(0); | ||
| 212 | } | ||
| 213 | int i; | ||
| 214 | while(1) | ||
| 215 | { | ||
| 216 | printmain(); | ||
| 217 | printf("Waiting for a GBA in port 2...\n"); | ||
| 218 | resval = 0; | ||
| 219 | ctrlerr = false; | ||
| 220 | |||
| 221 | SI_GetTypeAsync(1,acb); | ||
| 222 | while(1) | ||
| 223 | { | ||
| 224 | if(resval) | ||
| 225 | { | ||
| 226 | if(resval == 0x80 || resval & 8) | ||
| 227 | { | ||
| 228 | resval = 0; | ||
| 229 | SI_GetTypeAsync(1,acb); | ||
| 230 | } | ||
| 231 | else if(resval) | ||
| 232 | break; | ||
| 233 | } | ||
| 234 | PAD_ScanPads(); | ||
| 235 | VIDEO_WaitVSync(); | ||
| 236 | if(PAD_ButtonsHeld(0)) | ||
| 237 | endproc(); | ||
| 238 | } | ||
| 239 | if(resval & SI_GBA) | ||
| 240 | { | ||
| 241 | printf("GBA Found! Waiting on BIOS\n"); | ||
| 242 | resbuf[2]=0; | ||
| 243 | while(!(resbuf[2]&0x10)) | ||
| 244 | { | ||
| 245 | doreset(); | ||
| 246 | getstatus(); | ||
| 247 | } | ||
| 248 | printf("Ready, sending dumper\n"); | ||
| 249 | unsigned int sendsize = ((gba_mb_gba_size+7)&~7); | ||
| 250 | unsigned int ourkey = calckey(sendsize); | ||
| 251 | //printf("Our Key: %08x\n", ourkey); | ||
| 252 | //get current sessionkey | ||
| 253 | u32 sessionkeyraw = recvsafe(); | ||
| 254 | u32 sessionkey = __builtin_bswap32(sessionkeyraw^0x7365646F); | ||
| 255 | //send over our own key | ||
| 256 | sendsafe(__builtin_bswap32(ourkey)); | ||
| 257 | unsigned int fcrc = 0x15a0; | ||
| 258 | //send over gba header | ||
| 259 | for(i = 0; i < 0xC0; i+=4) | ||
| 260 | { | ||
| 261 | sendsafe(__builtin_bswap32(*(vu32*)(gba_mb_gba+i))); | ||
| 262 | if(!(resbuf[0]&0x2)) printf("Possible error %02x\n",resbuf[0]); | ||
| 263 | } | ||
| 264 | //printf("Header done! Sending ROM...\n"); | ||
| 265 | for(i = 0xC0; i < sendsize; i+=4) | ||
| 266 | { | ||
| 267 | u32 enc = ((gba_mb_gba[i+3]<<24)|(gba_mb_gba[i+2]<<16)|(gba_mb_gba[i+1]<<8)|(gba_mb_gba[i])); | ||
| 268 | fcrc=docrc(fcrc,enc); | ||
| 269 | sessionkey = (sessionkey*0x6177614B)+1; | ||
| 270 | enc^=sessionkey; | ||
| 271 | enc^=((~(i+(0x20<<20)))+1); | ||
| 272 | enc^=0x20796220; | ||
| 273 | sendsafe(enc); | ||
| 274 | if(!(resbuf[0]&0x2)) printf("Possible error %02x\n",resbuf[0]); | ||
| 275 | } | ||
| 276 | fcrc |= (sendsize<<16); | ||
| 277 | //printf("ROM done! CRC: %08x\n", fcrc); | ||
| 278 | //send over CRC | ||
| 279 | sessionkey = (sessionkey*0x6177614B)+1; | ||
| 280 | fcrc^=sessionkey; | ||
| 281 | fcrc^=((~(i+(0x20<<20)))+1); | ||
| 282 | fcrc^=0x20796220; | ||
| 283 | sendsafe(fcrc); | ||
| 284 | //get crc back (unused) | ||
| 285 | recvsafe(); | ||
| 286 | printf("Done!\n"); | ||
| 287 | sleep(2); | ||
| 288 | //hm | ||
| 289 | while(1) | ||
| 290 | { | ||
| 291 | printmain(); | ||
| 292 | printf("Press A once you have a GBA Game inserted.\n \n"); | ||
| 293 | PAD_ScanPads(); | ||
| 294 | VIDEO_WaitVSync(); | ||
| 295 | u32 btns = PAD_ButtonsDown(0); | ||
| 296 | if(btns&PAD_BUTTON_START) | ||
| 297 | endproc(); | ||
| 298 | else if(btns&PAD_BUTTON_A) | ||
| 299 | { | ||
| 300 | if(recvsafe() == 0) //ready | ||
| 301 | { | ||
| 302 | sleep(1); //gba rom prepare | ||
| 303 | u32 gbasize = __builtin_bswap32(recvsafe()); | ||
| 304 | if(gbasize == 0) | ||
| 305 | { | ||
| 306 | printf("ERROR: No (Valid) GBA Card inserted!\n"); | ||
| 307 | VIDEO_WaitVSync(); | ||
| 308 | VIDEO_WaitVSync(); | ||
| 309 | sleep(2); | ||
| 310 | continue; | ||
| 311 | } | ||
| 312 | for(i = 0; i < 0xC0; i+=4) | ||
| 313 | *(vu32*)(testdump+i) = recvfast(); | ||
| 314 | printf("Game Name: %.12s\n",(char*)(testdump+0xA0)); | ||
| 315 | printf("Game ID: %.4s\n",(char*)(testdump+0xAC)); | ||
| 316 | printf("Company ID: %.2s\n",(char*)(testdump+0xB0)); | ||
| 317 | printf("ROM Size: %02.02f MB\n \n",((float)(gbasize/1024))/1024.f); | ||
| 318 | char gamename[64]; | ||
| 319 | sprintf(gamename,"/dumps/%.12s [%.4s%.2s].gba", | ||
| 320 | (char*)(testdump+0xA0),(char*)(testdump+0xAC),(char*)(testdump+0xB0)); | ||
| 321 | FILE *f = fopen(gamename,"rb"); | ||
| 322 | if(f) | ||
| 323 | { | ||
| 324 | fclose(f); | ||
| 325 | sendsafe(0); | ||
| 326 | printf("ERROR: Game already dumped! Please insert another game.\n"); | ||
| 327 | VIDEO_WaitVSync(); | ||
| 328 | VIDEO_WaitVSync(); | ||
| 329 | sleep(2); | ||
| 330 | continue; | ||
| 331 | } | ||
| 332 | printf("Press A to dump this game, it will take about %i minutes.\n",gbasize/1024/1024*3/2); | ||
| 333 | printf("Press B if you want to cancel dumping this game.\n\n"); | ||
| 334 | int dumping = 0; | ||
| 335 | while(1) | ||
| 336 | { | ||
| 337 | PAD_ScanPads(); | ||
| 338 | VIDEO_WaitVSync(); | ||
| 339 | u32 btns = PAD_ButtonsDown(0); | ||
| 340 | if(btns&PAD_BUTTON_START) | ||
| 341 | endproc(); | ||
| 342 | else if(btns&PAD_BUTTON_A) | ||
| 343 | { | ||
| 344 | dumping = 1; | ||
| 345 | break; | ||
| 346 | } | ||
| 347 | else if(btns&PAD_BUTTON_B) | ||
| 348 | break; | ||
| 349 | } | ||
| 350 | sendsafe(dumping); | ||
| 351 | if(dumping == 0) | ||
| 352 | continue; | ||
| 353 | //create base file with size | ||
| 354 | printf("Creating file...\n"); | ||
| 355 | int fd = open(gamename, O_WRONLY|O_CREAT); | ||
| 356 | if(fd >= 0) | ||
| 357 | { | ||
| 358 | ftruncate(fd, gbasize); | ||
| 359 | close(fd); | ||
| 360 | } | ||
| 361 | f = fopen(gamename,"wb"); | ||
| 362 | if(!f) | ||
| 363 | { | ||
| 364 | printf("ERROR: Could not create file! Exit...\n"); | ||
| 365 | VIDEO_WaitVSync(); | ||
| 366 | VIDEO_WaitVSync(); | ||
| 367 | sleep(5); | ||
| 368 | exit(0); | ||
| 369 | } | ||
| 370 | printf("Dumping...\n"); | ||
| 371 | u32 bytes_read = 0; | ||
| 372 | while(gbasize > 0) | ||
| 373 | { | ||
| 374 | int toread = (gbasize > 0x400000 ? 0x400000 : gbasize); | ||
| 375 | int j; | ||
| 376 | for(j = 0; j < toread; j+=4) | ||
| 377 | { | ||
| 378 | *(vu32*)(testdump+j) = recvfast(); | ||
| 379 | bytes_read+=4; | ||
| 380 | if((bytes_read&0xFFFF) == 0) | ||
| 381 | { | ||
| 382 | printf("\r%02.02f MB done",(float)(bytes_read/1024)/1024.f); | ||
| 383 | VIDEO_WaitVSync(); | ||
| 384 | } | ||
| 385 | //printf("%02x%02x%02x%02x",resbuf[0],resbuf[1],resbuf[2],resbuf[3]); | ||
| 386 | } | ||
| 387 | fwrite(testdump,toread,1,f); | ||
| 388 | gbasize -= toread; | ||
| 389 | } | ||
| 390 | printf("\nClosing file\n"); | ||
| 391 | fclose(f); | ||
| 392 | printf("Game dumped!\n"); | ||
| 393 | sleep(5); | ||
| 394 | } | ||
| 395 | } | ||
| 396 | } | ||
| 397 | } | ||
| 398 | } | ||
| 399 | return 0; | ||
| 400 | } | ||
