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 | } | ||