menubg, png, bugfix
[fceu.git] / drivers / gp2x / menu.c
1 // (c) Copyright 2006,2007 notaz, All rights reserved.\r
2 // Free for non-commercial use.\r
3 \r
4 // For commercial use, separate licencing terms must be obtained.\r
5 \r
6 #include <stdio.h>\r
7 #include <string.h>\r
8 #include <stdlib.h>\r
9 #include <stdarg.h>\r
10 #include <unistd.h>\r
11 #include <dirent.h>\r
12 \r
13 #include "minimal.h"\r
14 #include "usbjoy.h"\r
15 #include "asmutils.h"\r
16 #include "menu.h"\r
17 #include "main.h"\r
18 #include "fonts.h"\r
19 #include "gp2x.h"\r
20 \r
21 #include "../../input.h"\r
22 #include "../../state.h"\r
23 #include "../../palette.h"\r
24 #include "readpng.h"\r
25 \r
26 #ifndef _DIRENT_HAVE_D_TYPE\r
27 #error "need d_type for file browser\r
28 #endif\r
29 \r
30 extern int GP2X_PORT_REV;\r
31 extern char lastLoadedGameName[PATH_MAX];\r
32 extern int mmuhack_status;\r
33 extern int soundvol;\r
34 extern uint8 Exit; // exit emu loop flag\r
35 extern int InitSound(void);\r
36 \r
37 #define CONFIGURABLE_KEYS (GP2X_UP|GP2X_LEFT|GP2X_DOWN|GP2X_RIGHT|GP2X_START|GP2X_SELECT|GP2X_L|GP2X_R|GP2X_A|GP2X_B|GP2X_X|GP2X_Y|GP2X_PUSH)\r
38 \r
39 static char *gp2xKeyNames[] = {\r
40         "UP",    "01???",  "LEFT", "03???", "DOWN", "05???", "RIGHT",    "07???",\r
41         "START", "SELECT", "L",    "R",     "A",    "B",     "X",        "Y",\r
42         "10???", "11???",  "12???","13???", "14???","15???", "VOL DOWN", "VOL UP",\r
43         "18???", "19???",  "1a???","PUSH",  "1c???","1d???", "1e???",    "1f???"\r
44 };\r
45 \r
46 \r
47 static char path_buffer[PATH_MAX];\r
48 static unsigned short *menu_bg = 0;\r
49 static int txt_xmin, txt_xmax, txt_ymin, txt_ymax;\r
50 \r
51 char menuErrorMsg[40] = {0, };\r
52 \r
53 static void gp2x_fceu_darken_reset(void)\r
54 {\r
55         txt_xmin = 320; txt_xmax = 0;\r
56         txt_ymin = 240; txt_ymax = 0;\r
57 }\r
58 \r
59 static void gp2x_fceu_copy_bg(void)\r
60 {\r
61         if (menu_bg)\r
62              memcpy(gp2x_screen, menu_bg, 320*240*2);\r
63         else memset(gp2x_screen, 0, 320*240*2);\r
64         gp2x_fceu_darken_reset();\r
65 }\r
66 \r
67 static void gp2x_fceu_darken_text_bg(void)\r
68 {\r
69         int x, y, xmin, xmax, ymax;\r
70         unsigned short *screen = gp2x_screen;\r
71 \r
72         xmin = txt_xmin - 3;\r
73         if (xmin < 0) xmin = 0;\r
74         xmax = txt_xmax + 2;\r
75         if (xmax > 319) xmax = 319;\r
76 \r
77         y = txt_ymin - 3;\r
78         if (y < 0) y = 0;\r
79         ymax = txt_ymax + 2;\r
80         if (ymax > 239) ymax = 239;\r
81 \r
82         for (x = xmin; x <= xmax; x++)\r
83                 screen[y*320+x] = 0xa514;\r
84         for (y++; y < ymax; y++)\r
85         {\r
86                 screen[y*320+xmin] = 0xffff;\r
87                 for (x = xmin+1; x < xmax; x++)\r
88                 {\r
89                         unsigned int p = screen[y*320+x];\r
90                         if (p != 0xffff)\r
91                                 screen[y*320+x] = ((p&0xf79e)>>1) - ((p&0xc618)>>3);\r
92                 }\r
93                 screen[y*320+xmax] = 0xffff;\r
94         }\r
95         for (x = xmin; x <= xmax; x++)\r
96                 screen[y*320+x] = 0xffff;\r
97 }\r
98 \r
99 static void gp2x_fceu_darken_all(void)\r
100 {\r
101         unsigned int *screen = gp2x_screen;\r
102         int count = 320*240/2;\r
103 \r
104         while (count--)\r
105         {\r
106                 unsigned int p = screen[count];\r
107                 screen[count] = ((p&0xf79ef79e)>>1) - ((p&0xc618c618)>>3);\r
108         }\r
109 }\r
110 \r
111 // draws white text to current bbp15 screen\r
112 static void gp2x_text_out15_(int x, int y, const char *text)\r
113 {\r
114         int i,l;\r
115         unsigned short *screen = gp2x_screen;\r
116 \r
117         screen = screen + x + y*320;\r
118 \r
119         for (i = 0; i < strlen(text); i++)\r
120         {\r
121                 for (l=0;l<8;l++)\r
122                 {\r
123                         if(fontdata8x8[((text[i])*8)+l]&0x80) screen[l*320+0]=0xffff;\r
124                         if(fontdata8x8[((text[i])*8)+l]&0x40) screen[l*320+1]=0xffff;\r
125                         if(fontdata8x8[((text[i])*8)+l]&0x20) screen[l*320+2]=0xffff;\r
126                         if(fontdata8x8[((text[i])*8)+l]&0x10) screen[l*320+3]=0xffff;\r
127                         if(fontdata8x8[((text[i])*8)+l]&0x08) screen[l*320+4]=0xffff;\r
128                         if(fontdata8x8[((text[i])*8)+l]&0x04) screen[l*320+5]=0xffff;\r
129                         if(fontdata8x8[((text[i])*8)+l]&0x02) screen[l*320+6]=0xffff;\r
130                         if(fontdata8x8[((text[i])*8)+l]&0x01) screen[l*320+7]=0xffff;\r
131                 }\r
132                 screen += 8;\r
133         }\r
134         if (x < txt_xmin) txt_xmin = x;\r
135         if (x+i*8 > txt_xmax) txt_xmax = x+i*8;\r
136         if (y < txt_ymin) txt_ymin = y;\r
137         if (y+8   > txt_ymax) txt_ymax = y+8;\r
138 }\r
139 \r
140 void gp2x_text_out15(int x, int y, const char *texto, ...)\r
141 {\r
142         va_list args;\r
143         char    buffer[512];\r
144 \r
145         va_start(args,texto);\r
146         vsprintf(buffer,texto,args);\r
147         va_end(args);\r
148 \r
149         gp2x_text_out15_(x,y,buffer);\r
150 }\r
151 \r
152 \r
153 void gp2x_text_out15_lim(int x, int y, const char *texto, int max)\r
154 {\r
155         char    buffer[320/8+1];\r
156 \r
157         strncpy(buffer, texto, 320/8);\r
158         if (max > 320/8) max = 320/8;\r
159         if (max < 0) max = 0;\r
160         buffer[max] = 0;\r
161 \r
162         gp2x_text_out15(x,y,buffer);\r
163 }\r
164 \r
165 static void gp2x_smalltext16(int x, int y, const char *texto)\r
166 {\r
167         int i;\r
168         unsigned char  *src;\r
169         unsigned short *dst;\r
170 \r
171         for (i = 0;; i++, x += 6)\r
172         {\r
173                 unsigned char c = (unsigned char) texto[i];\r
174                 int h = 8;\r
175 \r
176                 if (!c) break;\r
177 \r
178                 src = fontdata6x8[c];\r
179                 dst = (unsigned short *)gp2x_screen + x + y*320;\r
180 \r
181                 while (h--)\r
182                 {\r
183                         int w = 0x20;\r
184                         while (w)\r
185                         {\r
186                                 if( *src & w ) *dst = 0xffff;\r
187                                 dst++;\r
188                                 w>>=1;\r
189                         }\r
190                         src++;\r
191 \r
192                         dst += 320-6;\r
193                 }\r
194         }\r
195 }\r
196 \r
197 static void gp2x_smalltext8_lim(int x, int y, const char *texto, int max)\r
198 {\r
199         char    buffer[320/6+1];\r
200 \r
201         strncpy(buffer, texto, 320/6);\r
202         if (max > 320/6) max = 320/6;\r
203         if (max < 0) max = 0;\r
204         buffer[max] = 0;\r
205 \r
206         gp2x_smalltext16(x, y, buffer);\r
207 }\r
208 \r
209 \r
210 static unsigned long inp_prev = 0;\r
211 static int inp_prevjoy = 0;\r
212 \r
213 static unsigned long wait_for_input(unsigned long interesting)\r
214 {\r
215         unsigned long ret;\r
216         static int repeats = 0, wait = 50*1000;\r
217         int release = 0, i;\r
218 \r
219         if (repeats == 2 || repeats == 4) wait /= 2;\r
220         if (repeats == 6) wait = 15 * 1000;\r
221 \r
222         for (i = 0; i < 6 && inp_prev == gp2x_joystick_read(1); i++) {\r
223                 if (i == 0) repeats++;\r
224                 if (wait >= 30*1000) usleep(wait); // usleep sleeps for ~30ms minimum\r
225                 else spend_cycles(wait * Settings.cpuclock);\r
226         }\r
227 \r
228         while ( !((ret = gp2x_joystick_read(1)) & interesting) ) {\r
229                 usleep(50000);\r
230                 release = 1;\r
231         }\r
232 \r
233         if (release || ret != inp_prev) {\r
234                 repeats = 0;\r
235                 wait = 50*1000;\r
236         }\r
237         inp_prev = ret;\r
238         inp_prevjoy = 0;\r
239 \r
240         // we don't need diagonals in menus\r
241         if ((ret&GP2X_UP)   && (ret&GP2X_LEFT))  ret &= ~GP2X_LEFT;\r
242         if ((ret&GP2X_UP)   && (ret&GP2X_RIGHT)) ret &= ~GP2X_RIGHT;\r
243         if ((ret&GP2X_DOWN) && (ret&GP2X_LEFT))  ret &= ~GP2X_LEFT;\r
244         if ((ret&GP2X_DOWN) && (ret&GP2X_RIGHT)) ret &= ~GP2X_RIGHT;\r
245 \r
246         return ret;\r
247 }\r
248 \r
249 static unsigned long input2_read(unsigned long interesting, int *joy)\r
250 {\r
251         unsigned long ret;\r
252         int i;\r
253 \r
254         do\r
255         {\r
256                 *joy = 0;\r
257                 if ((ret = gp2x_joystick_read(0) & interesting)) break;\r
258                 gp2x_usbjoy_update();\r
259                 for (i = 0; i < num_of_joys; i++) {\r
260                         ret = gp2x_usbjoy_check2(i);\r
261                         if (ret) { *joy = i + 1; break; }\r
262                 }\r
263                 if (ret) break;\r
264         }\r
265         while(0);\r
266 \r
267         return ret;\r
268 }\r
269 \r
270 // similar to wait_for_input(), but returns joy num\r
271 static unsigned long wait_for_input_usbjoy(unsigned long interesting, int *joy)\r
272 {\r
273         unsigned long ret;\r
274         const int wait = 300*1000;\r
275         int i;\r
276 \r
277         if (inp_prevjoy == 0) inp_prev &= interesting;\r
278         for (i = 0; i < 6; i++) {\r
279                 ret = input2_read(interesting, joy);\r
280                 if (*joy != inp_prevjoy || ret != inp_prev) break;\r
281                 usleep(wait/6);\r
282         }\r
283 \r
284         while ( !(ret = input2_read(interesting, joy)) ) {\r
285                 usleep(50000);\r
286         }\r
287 \r
288         inp_prev = ret;\r
289         inp_prevjoy = *joy;\r
290 \r
291         return ret;\r
292 }\r
293 \r
294 \r
295 \r
296 // -------------- ROM selector --------------\r
297 \r
298 static void draw_dirlist(char *curdir, struct dirent **namelist, int n, int sel)\r
299 {\r
300         int start, i, pos;\r
301 \r
302         start = 12 - sel;\r
303         n--; // exclude current dir (".")\r
304 \r
305         gp2x_fceu_copy_bg();\r
306         gp2x_fceu_darken_all();\r
307 \r
308         if(start - 2 >= 0)\r
309                 gp2x_smalltext8_lim(14, (start - 2)*10, curdir, 53-2);\r
310         for (i = 0; i < n; i++) {\r
311                 pos = start + i;\r
312                 if (pos < 0)  continue;\r
313                 if (pos > 23) break;\r
314                 if (namelist[i+1]->d_type == DT_DIR) {\r
315                         gp2x_smalltext8_lim(14,   pos*10, "/", 1);\r
316                         gp2x_smalltext8_lim(14+6, pos*10, namelist[i+1]->d_name, 53-3);\r
317                 } else {\r
318                         gp2x_smalltext8_lim(14,   pos*10, namelist[i+1]->d_name, 53-2);\r
319                 }\r
320         }\r
321         gp2x_text_out15(5, 120, ">");\r
322         gp2x_video_flip();\r
323 }\r
324 \r
325 static int scandir_cmp(const void *p1, const void *p2)\r
326 {\r
327         struct dirent **d1 = (struct dirent **)p1, **d2 = (struct dirent **)p2;\r
328         if ((*d1)->d_type == (*d2)->d_type) return alphasort(d1, d2);\r
329         if ((*d1)->d_type == DT_DIR) return -1; // put before\r
330         if ((*d2)->d_type == DT_DIR) return  1;\r
331         return alphasort(d1, d2);\r
332 }\r
333 \r
334 static char *filter_exts[] = {\r
335         ".gpe", ".png", "ck.o", ".txt"\r
336 };\r
337 \r
338 static int scandir_filter(const struct dirent *ent)\r
339 {\r
340         const char *p;\r
341         int i;\r
342 \r
343         if (ent == NULL || ent->d_name == NULL) return 0;\r
344         if (strlen(ent->d_name) < 5) return 1;\r
345 \r
346         p = ent->d_name + strlen(ent->d_name) - 4;\r
347 \r
348         for (i = 0; i < sizeof(filter_exts)/sizeof(filter_exts[0]); i++)\r
349         {\r
350                 if (strcmp(p, filter_exts[i]) == 0) return 0;\r
351         }\r
352 \r
353         return 1;\r
354 }\r
355 \r
356 static char *filesel_loop(char *curr_path, char *final_dest)\r
357 {\r
358         struct dirent **namelist;\r
359         DIR *dir;\r
360         int n, newlen, sel = 0;\r
361         unsigned long inp = 0;\r
362         char *ret = NULL, *fname = NULL;\r
363 \r
364         // is this a dir or a full path?\r
365         if ((dir = opendir(curr_path))) {\r
366                 closedir(dir);\r
367         } else {\r
368                 char *p;\r
369                 for (p = curr_path + strlen(curr_path) - 1; p > curr_path && *p != '/'; p--);\r
370                 *p = 0;\r
371                 fname = p+1;\r
372         }\r
373 \r
374         n = scandir(curr_path, &namelist, scandir_filter, scandir_cmp);\r
375         if (n < 0) {\r
376                 // try root\r
377                 n = scandir("/", &namelist, scandir_filter, scandir_cmp);\r
378                 if (n < 0) {\r
379                         // oops, we failed\r
380                         printf("dir: %s\n", curr_path);\r
381                         perror("scandir");\r
382                         return NULL;\r
383                 }\r
384         }\r
385 \r
386         // try to find sel\r
387         if (fname != NULL) {\r
388                 int i;\r
389                 for (i = 1; i < n; i++) {\r
390                         if (strcmp(namelist[i]->d_name, fname) == 0) {\r
391                                 sel = i - 1;\r
392                                 break;\r
393                         }\r
394                 }\r
395         }\r
396 \r
397         for (;;)\r
398         {\r
399                 draw_dirlist(curr_path, namelist, n, sel);\r
400                 inp = wait_for_input(GP2X_UP|GP2X_DOWN|GP2X_LEFT|GP2X_RIGHT|GP2X_L|GP2X_R|GP2X_B|GP2X_X);\r
401                 if(inp & GP2X_UP  )  { sel--;   if (sel < 0)   sel = n-2; }\r
402                 if(inp & GP2X_DOWN)  { sel++;   if (sel > n-2) sel = 0; }\r
403                 if(inp & GP2X_LEFT)  { sel-=10; if (sel < 0)   sel = 0; }\r
404                 if(inp & GP2X_L)     { sel-=24; if (sel < 0)   sel = 0; }\r
405                 if(inp & GP2X_RIGHT) { sel+=10; if (sel > n-2) sel = n-2; }\r
406                 if(inp & GP2X_R)     { sel+=24; if (sel > n-2) sel = n-2; }\r
407                 if(inp & GP2X_B)     { // enter dir/select\r
408                         again:\r
409                         newlen = strlen(curr_path) + strlen(namelist[sel+1]->d_name) + 2;\r
410                         if (namelist[sel+1]->d_type == DT_REG) { // file selected\r
411                                 if (final_dest == NULL) final_dest = malloc(newlen);\r
412                                 if (final_dest == NULL) break;\r
413                                 strcpy(final_dest, curr_path);\r
414                                 strcat(final_dest, "/");\r
415                                 strcat(final_dest, namelist[sel+1]->d_name);\r
416                                 ret = final_dest;\r
417                                 break;\r
418                         } else if (namelist[sel+1]->d_type == DT_DIR) {\r
419                                 char *p, *newdir = malloc(newlen);\r
420                                 if (newdir == NULL) break;\r
421                                 if (strcmp(namelist[sel+1]->d_name, "..") == 0) {\r
422                                         char *start = curr_path;\r
423                                         p = start + strlen(start) - 1;\r
424                                         while (*p == '/' && p > start) p--;\r
425                                         while (*p != '/' && p > start) p--;\r
426                                         if (p <= start) strcpy(newdir, "/");\r
427                                         else { strncpy(newdir, start, p-start); newdir[p-start] = 0; }\r
428                                 } else {\r
429                                         strcpy(newdir, curr_path);\r
430                                         p = newdir + strlen(newdir) - 1;\r
431                                         while (p >= newdir && *p == '/') *p-- = 0;\r
432                                         strcat(newdir, "/");\r
433                                         strcat(newdir, namelist[sel+1]->d_name);\r
434                                 }\r
435                                 ret = filesel_loop(newdir, final_dest);\r
436                                 free(newdir);\r
437                                 break;\r
438                         } else {\r
439                                 // unknown file type, happens on NTFS mounts. Try to guess.\r
440                                 char *tstfn; FILE *tstf; int tmp;\r
441                                 tstfn = malloc(newlen);\r
442                                 if (tstfn == NULL) break;\r
443                                 strcpy(tstfn, curr_path);\r
444                                 strcat(tstfn, "/");\r
445                                 strcat(tstfn, namelist[sel+1]->d_name);\r
446                                 tstf = fopen(tstfn, "rb");\r
447                                 free(tstfn);\r
448                                 if (tstf != NULL)\r
449                                 {\r
450                                         if (fread(&tmp, 1, 1, tstf) > 0 || ferror(tstf) == 0)\r
451                                                 namelist[sel+1]->d_type = DT_REG;\r
452                                         else    namelist[sel+1]->d_type = DT_DIR;\r
453                                         fclose(tstf);\r
454                                         goto again;\r
455                                 }\r
456                         }\r
457                 }\r
458                 if(inp & GP2X_X) break; // cancel\r
459         }\r
460 \r
461         if (n > 0) {\r
462                 while(n--) free(namelist[n]);\r
463                 free(namelist);\r
464         }\r
465 \r
466         return ret;\r
467 }\r
468 \r
469 // ------------ patch/gg menu ------------\r
470 \r
471 #if 0 // TODO?\r
472 static void draw_patchlist(int sel)\r
473 {\r
474         int start, i, pos;\r
475 \r
476         start = 12 - sel;\r
477 \r
478         gp2x_fceu_copy_bg();\r
479 \r
480         for (i = 0; i < PicoPatchCount; i++) {\r
481                 pos = start + i;\r
482                 if (pos < 0)  continue;\r
483                 if (pos > 23) break;\r
484                 gp2x_smalltext8_lim(14,     pos*10, PicoPatches[i].active ? "ON " : "OFF", 3);\r
485                 gp2x_smalltext8_lim(14+6*4, pos*10, PicoPatches[i].name, 53-6);\r
486         }\r
487         pos = start + i;\r
488         if (pos < 24) gp2x_smalltext8_lim(14, pos*10, "done", 4);\r
489 \r
490         gp2x_text_out15(5, 120, ">");\r
491         gp2x_video_flip();\r
492 }\r
493 \r
494 \r
495 void patches_menu_loop(void)\r
496 {\r
497         int menu_sel = 0;\r
498         unsigned long inp = 0;\r
499 \r
500         for(;;)\r
501         {\r
502                 draw_patchlist(menu_sel);\r
503                 inp = wait_for_input(GP2X_UP|GP2X_DOWN|GP2X_LEFT|GP2X_RIGHT|GP2X_L|GP2X_R|GP2X_B|GP2X_X);\r
504                 if(inp & GP2X_UP  ) { menu_sel--; if (menu_sel < 0) menu_sel = PicoPatchCount; }\r
505                 if(inp & GP2X_DOWN) { menu_sel++; if (menu_sel > PicoPatchCount) menu_sel = 0; }\r
506                 if(inp &(GP2X_LEFT|GP2X_L))  { menu_sel-=10; if (menu_sel < 0) menu_sel = 0; }\r
507                 if(inp &(GP2X_RIGHT|GP2X_R)) { menu_sel+=10; if (menu_sel > PicoPatchCount) menu_sel = PicoPatchCount; }\r
508                 if(inp & GP2X_B) { // action\r
509                         if (menu_sel < PicoPatchCount)\r
510                                 PicoPatches[menu_sel].active = !PicoPatches[menu_sel].active;\r
511                         else    return;\r
512                 }\r
513                 if(inp & GP2X_X) return;\r
514         }\r
515 \r
516 }\r
517 \r
518 // ------------ savestate loader ------------\r
519 \r
520 static void menu_prepare_bg(void);\r
521 \r
522 static int state_slot_flags = 0;\r
523 \r
524 static void state_check_slots(void)\r
525 {\r
526         int slot;\r
527 \r
528         state_slot_flags = 0;\r
529 \r
530         for (slot = 0; slot < 10; slot++)\r
531         {\r
532                 if (emu_check_save_file(slot))\r
533                 {\r
534                         state_slot_flags |= 1 << slot;\r
535                 }\r
536         }\r
537 }\r
538 \r
539 static void draw_savestate_bg(int slot)\r
540 {\r
541         struct PicoVideo tmp_pv;\r
542         unsigned short tmp_cram[0x40];\r
543         unsigned short tmp_vsram[0x40];\r
544         void *tmp_vram, *file;\r
545         char *fname;\r
546 \r
547         fname = emu_GetSaveFName(1, 0, slot);\r
548         if (!fname) return;\r
549 \r
550         tmp_vram = malloc(sizeof(Pico.vram));\r
551         if (tmp_vram == NULL) return;\r
552 \r
553         memcpy(tmp_vram, Pico.vram, sizeof(Pico.vram));\r
554         memcpy(tmp_cram, Pico.cram, sizeof(Pico.cram));\r
555         memcpy(tmp_vsram, Pico.vsram, sizeof(Pico.vsram));\r
556         memcpy(&tmp_pv, &Pico.video, sizeof(Pico.video));\r
557 \r
558         if (strcmp(fname + strlen(fname) - 3, ".gz") == 0) {\r
559                 file = gzopen(fname, "rb");\r
560                 emu_set_save_cbs(1);\r
561         } else {\r
562                 file = fopen(fname, "rb");\r
563                 emu_set_save_cbs(0);\r
564         }\r
565 \r
566         if (file) {\r
567                 if (PicoMCD & 1) {\r
568                         PicoCdLoadStateGfx(file);\r
569                 } else {\r
570                         areaSeek(file, 0x10020, SEEK_SET);  // skip header and RAM in state file\r
571                         areaRead(Pico.vram, 1, sizeof(Pico.vram), file);\r
572                         areaSeek(file, 0x2000, SEEK_CUR);\r
573                         areaRead(Pico.cram, 1, sizeof(Pico.cram), file);\r
574                         areaRead(Pico.vsram, 1, sizeof(Pico.vsram), file);\r
575                         areaSeek(file, 0x221a0, SEEK_SET);\r
576                         areaRead(&Pico.video, 1, sizeof(Pico.video), file);\r
577                 }\r
578                 areaClose(file);\r
579         }\r
580 \r
581         emu_forced_frame();\r
582         gp2x_memcpy_buffers((1<<2), gp2x_screen, 0, 320*240*2);\r
583         menu_prepare_bg();\r
584 \r
585         memcpy(Pico.vram, tmp_vram, sizeof(Pico.vram));\r
586         memcpy(Pico.cram, tmp_cram, sizeof(Pico.cram));\r
587         memcpy(Pico.vsram, tmp_vsram, sizeof(Pico.vsram));\r
588         memcpy(&Pico.video, &tmp_pv,  sizeof(Pico.video));\r
589         free(tmp_vram);\r
590 }\r
591 \r
592 static void draw_savestate_menu(int menu_sel, int is_loading)\r
593 {\r
594         int tl_x = 25, tl_y = 60, y, i;\r
595 \r
596         if (state_slot_flags & (1 << menu_sel))\r
597                 draw_savestate_bg(menu_sel);\r
598         gp2x_fceu_copy_bg();\r
599 \r
600         gp2x_text_out15(tl_x, 30, is_loading ? "Load state" : "Save state");\r
601 \r
602         /* draw all 10 slots */\r
603         y = tl_y;\r
604         for (i = 0; i < 10; i++, y+=10)\r
605         {\r
606                 gp2x_text_out15(tl_x, y, "SLOT %i (%s)", i, (state_slot_flags & (1 << i)) ? "USED" : "free");\r
607         }\r
608         gp2x_text_out15(tl_x, y, "back");\r
609 \r
610         // draw cursor\r
611         gp2x_text_out15(tl_x - 16, tl_y + menu_sel*10, ">");\r
612 \r
613         gp2x_video_flip();\r
614 }\r
615 \r
616 static int savestate_menu_loop(int is_loading)\r
617 {\r
618         int menu_sel = 10, menu_sel_max = 10;\r
619         unsigned long inp = 0;\r
620 \r
621         state_check_slots();\r
622 \r
623         for(;;)\r
624         {\r
625                 draw_savestate_menu(menu_sel, is_loading);\r
626                 inp = wait_for_input(GP2X_UP|GP2X_DOWN|GP2X_B|GP2X_X);\r
627                 if(inp & GP2X_UP  ) {\r
628                         do {\r
629                                 menu_sel--; if (menu_sel < 0) menu_sel = menu_sel_max;\r
630                         } while (!(state_slot_flags & (1 << menu_sel)) && menu_sel != menu_sel_max && is_loading);\r
631                 }\r
632                 if(inp & GP2X_DOWN) {\r
633                         do {\r
634                                 menu_sel++; if (menu_sel > menu_sel_max) menu_sel = 0;\r
635                         } while (!(state_slot_flags & (1 << menu_sel)) && menu_sel != menu_sel_max && is_loading);\r
636                 }\r
637                 if(inp & GP2X_B) { // save/load\r
638                         if (menu_sel < 10) {\r
639                                 state_slot = menu_sel;\r
640                                 if (emu_SaveLoadGame(is_loading, 0)) {\r
641                                         strcpy(menuErrorMsg, is_loading ? "Load failed" : "Save failed");\r
642                                         return 1;\r
643                                 }\r
644                                 return 0;\r
645                         } else  return 1;\r
646                 }\r
647                 if(inp & GP2X_X) return 1;\r
648         }\r
649 }\r
650 #endif\r
651 \r
652 // -------------- key config --------------\r
653 \r
654 static char *usb_joy_key_name(int joy, int num)\r
655 {\r
656         static char name[16];\r
657         switch (num)\r
658         {\r
659                 case 0: sprintf(name, "Joy%i UP", joy); break;\r
660                 case 1: sprintf(name, "Joy%i DOWN", joy); break;\r
661                 case 2: sprintf(name, "Joy%i LEFT", joy); break;\r
662                 case 3: sprintf(name, "Joy%i RIGHT", joy); break;\r
663                 default:sprintf(name, "Joy%i b%i", joy, num-3); break;\r
664         }\r
665         return name;\r
666 }\r
667 \r
668 static char *action_binds(int player_idx, int action_mask)\r
669 {\r
670         static char strkeys[32*5];\r
671         int joy, i;\r
672 \r
673         strkeys[0] = 0;\r
674         for (i = 0; i < 32; i++) // i is key index\r
675         {\r
676                 if (Settings.KeyBinds[i] & action_mask)\r
677                 {\r
678                         if (player_idx >= 0 && ((Settings.KeyBinds[i] >> 16) & 3) != player_idx) continue;\r
679                         if (strkeys[0]) { strcat(strkeys, " + "); strcat(strkeys, gp2xKeyNames[i]); break; }\r
680                         else strcpy(strkeys, gp2xKeyNames[i]);\r
681                 }\r
682         }\r
683         for (joy = 0; joy < num_of_joys; joy++)\r
684         {\r
685                 for (i = 0; i < 32; i++)\r
686                 {\r
687                         if (Settings.JoyBinds[joy][i] & action_mask)\r
688                         {\r
689                                 if (player_idx >= 0 && ((Settings.JoyBinds[joy][i] >> 16) & 3) != player_idx) continue;\r
690                                 if (strkeys[0]) {\r
691                                         strcat(strkeys, ", "); strcat(strkeys, usb_joy_key_name(joy + 1, i));\r
692                                         break;\r
693                                 }\r
694                                 else strcpy(strkeys, usb_joy_key_name(joy + 1, i));\r
695                         }\r
696                 }\r
697         }\r
698 \r
699         return strkeys;\r
700 }\r
701 \r
702 static void unbind_action(int action)\r
703 {\r
704         int i, u;\r
705 \r
706         for (i = 0; i < 32; i++)\r
707                 Settings.KeyBinds[i] &= ~action;\r
708         for (u = 0; u < 4; u++)\r
709                 for (i = 0; i < 32; i++)\r
710                         Settings.JoyBinds[u][i] &= ~action;\r
711 }\r
712 \r
713 static int count_bound_keys(int action, int is_joy)\r
714 {\r
715         int i, u, keys = 0;\r
716 \r
717         if (is_joy)\r
718         {\r
719                 for (u = 0; u < 4; u++)\r
720                         for (i = 0; i < 32; i++)\r
721                                 if (Settings.JoyBinds[u][i] & action) keys++;\r
722         }\r
723         else\r
724         {\r
725                 for (i = 0; i < 32; i++)\r
726                         if (Settings.KeyBinds[i] & action) keys++;\r
727         }\r
728         return keys;\r
729 }\r
730 \r
731 typedef struct { char *name; int mask; } bind_action_t;\r
732 \r
733 static void draw_key_config(const bind_action_t *opts, int opt_cnt, int player_idx, int sel)\r
734 {\r
735         int x, y, tl_y = 40, i;\r
736 \r
737         gp2x_fceu_copy_bg();\r
738         if (player_idx >= 0)\r
739              gp2x_text_out15(80, 20, "Player %i controls", player_idx + 1);\r
740         else gp2x_text_out15(80, 20, "Emulator controls");\r
741 \r
742         x = 40; y = tl_y;\r
743         for (i = 0; i < opt_cnt; i++, y+=10)\r
744                 gp2x_text_out15(x, y, "%s : %s", opts[i].name, action_binds(player_idx, opts[i].mask));\r
745 \r
746         gp2x_text_out15(x, y, "Done");\r
747 \r
748         // draw cursor\r
749         gp2x_text_out15(x - 16, tl_y + sel*10, ">");\r
750 \r
751         gp2x_fceu_darken_text_bg();\r
752         gp2x_fceu_darken_reset();\r
753 \r
754         if (sel < opt_cnt) {\r
755                 gp2x_text_out15(30, 190, "Press a button to bind/unbind");\r
756                 gp2x_text_out15(30, 200, "Use VOL+ to clear");\r
757                 gp2x_text_out15(30, 210, "To bind UP/DOWN, hold VOL-");\r
758                 gp2x_text_out15(30, 220, "Select \"Done\" to exit");\r
759         } else {\r
760                 gp2x_text_out15(30, 200, "Use Options -> Save cfg");\r
761                 gp2x_text_out15(30, 210, "to save controls");\r
762                 gp2x_text_out15(30, 220, "Press B or X to exit");\r
763         }\r
764         gp2x_fceu_darken_text_bg();\r
765         gp2x_video_flip();\r
766 }\r
767 \r
768 static void key_config_loop(const bind_action_t *opts, int opt_cnt, int player_idx)\r
769 {\r
770         int joy = 0, sel = 0, menu_sel_max = opt_cnt, i;\r
771         unsigned long inp = 0;\r
772 \r
773         for (;;)\r
774         {\r
775                 draw_key_config(opts, opt_cnt, player_idx, sel);\r
776                 inp = wait_for_input_usbjoy(CONFIGURABLE_KEYS|GP2X_VOL_DOWN|GP2X_VOL_UP, &joy);\r
777                 // printf("got %08lX from joy %i\n", inp, joy);\r
778                 if (joy == 0) {\r
779                         if (!(inp & GP2X_VOL_DOWN)) {\r
780                                 if(inp & GP2X_UP  ) { sel--; if (sel < 0) sel = menu_sel_max; continue; }\r
781                                 if(inp & GP2X_DOWN) { sel++; if (sel > menu_sel_max) sel = 0; continue; }\r
782                         }\r
783                         if (sel >= opt_cnt) {\r
784                                 if (inp & (GP2X_B|GP2X_X)) break;\r
785                                 else continue;\r
786                         }\r
787                         // if we are here, we want to bind/unbind something\r
788                         if (inp & GP2X_VOL_UP)\r
789                                 unbind_action(opts[sel].mask);\r
790                         inp &= CONFIGURABLE_KEYS;\r
791                         for (i = 0; i < 32; i++)\r
792                                 if (inp & (1 << i)) {\r
793                                         if (count_bound_keys(opts[sel].mask, 0) >= 2)\r
794                                              Settings.KeyBinds[i] &= ~opts[sel].mask; // allow to unbind only\r
795                                         else Settings.KeyBinds[i] ^=  opts[sel].mask;\r
796                                         if (player_idx >= 0) {\r
797                                                 Settings.KeyBinds[i] &= ~(3 << 16);\r
798                                                 Settings.KeyBinds[i] |= player_idx << 16;\r
799                                         }\r
800                                 }\r
801                 }\r
802                 else if (sel < opt_cnt)\r
803                 {\r
804                         for (i = 0; i < 32; i++)\r
805                                 if (inp & (1 << i)) {\r
806                                         if (count_bound_keys(opts[sel].mask, 1) >= 1) // disallow combos for usbjoy\r
807                                              Settings.JoyBinds[joy-1][i] &= ~opts[sel].mask;\r
808                                         else Settings.JoyBinds[joy-1][i] ^=  opts[sel].mask;\r
809                                         if (player_idx >= 0) {\r
810                                                 Settings.JoyBinds[joy-1][i] &= ~(3 << 16);\r
811                                                 Settings.JoyBinds[joy-1][i] |= player_idx << 16;\r
812                                         }\r
813                                 }\r
814                 }\r
815         }\r
816 }\r
817 \r
818 static void draw_kc_sel(int menu_sel)\r
819 {\r
820         int tl_x = 25+40, tl_y = 60, y, i;\r
821         char joyname[36];\r
822 \r
823         y = tl_y;\r
824         gp2x_fceu_copy_bg();\r
825         gp2x_text_out15(tl_x, y,       "Player 1");\r
826         gp2x_text_out15(tl_x, (y+=10), "Player 2");\r
827         gp2x_text_out15(tl_x, (y+=10), "Emulator controls");\r
828         gp2x_text_out15(tl_x, (y+=10), "Done");\r
829 \r
830         // draw cursor\r
831         gp2x_text_out15(tl_x - 16, tl_y + menu_sel*10, ">");\r
832 \r
833         tl_x = 25;\r
834         gp2x_text_out15(tl_x, (y=110), "USB joys detected:");\r
835         if (num_of_joys > 0) {\r
836                 for (i = 0; i < num_of_joys; i++) {\r
837                         strncpy(joyname, joy_name(joys[i]), 33); joyname[33] = 0;\r
838                         gp2x_text_out15(tl_x, (y+=10), "%i: %s", i+1, joyname);\r
839                 }\r
840         } else {\r
841                 gp2x_text_out15(tl_x, (y+=10), "none");\r
842         }\r
843 \r
844         gp2x_fceu_darken_text_bg();\r
845         gp2x_video_flip();\r
846 }\r
847 \r
848 // b_turbo,a_turbo  RLDU SEBA\r
849 static bind_action_t ctrl_actions[] =\r
850 {\r
851         { "UP     ", 0x010 },\r
852         { "DOWN   ", 0x020 },\r
853         { "LEFT   ", 0x040 },\r
854         { "RIGHT  ", 0x080 },\r
855         { "A      ", 0x001 },\r
856         { "B      ", 0x002 },\r
857         { "A TURBO", 0x100 },\r
858         { "B TURBO", 0x200 },\r
859         { "START  ", 0x008 },\r
860         { "SELECT ", 0x004 },\r
861 };\r
862 \r
863 static bind_action_t emuctrl_actions[] =\r
864 {\r
865         { "Save State       ", 1<<31 },\r
866         { "Load State       ", 1<<30 },\r
867         { "Next State Slot  ", 1<<29 },\r
868         { "Prev State Slot  ", 1<<28 },\r
869         { "FDS Insert/Eject ", 1<<27 },\r
870         { "FDS Select Disk  ", 1<<26 },\r
871         { "VSUni Insert Coin", 1<<25 },\r
872 };\r
873 \r
874 static void kc_sel_loop(void)\r
875 {\r
876         int menu_sel = 3, menu_sel_max = 3;\r
877         unsigned long inp = 0;\r
878 \r
879         for(;;)\r
880         {\r
881                 draw_kc_sel(menu_sel);\r
882                 inp = wait_for_input(GP2X_UP|GP2X_DOWN|GP2X_B|GP2X_X);\r
883                 if(inp & GP2X_UP  ) { menu_sel--; if (menu_sel < 0) menu_sel = menu_sel_max; }\r
884                 if(inp & GP2X_DOWN) { menu_sel++; if (menu_sel > menu_sel_max) menu_sel = 0; }\r
885                 if(inp & GP2X_B) {\r
886                         switch (menu_sel) {\r
887                                 case 0: key_config_loop(ctrl_actions, 10, 0); return;\r
888                                 case 1: key_config_loop(ctrl_actions,  8, 1); return;\r
889                                 case 2: key_config_loop(emuctrl_actions,\r
890                                                 sizeof(emuctrl_actions)/sizeof(emuctrl_actions[0]), -1); return;\r
891                                 case 3: if (!fceugi) SaveConfig(NULL); return;\r
892                                 default: return;\r
893                         }\r
894                 }\r
895                 if(inp & GP2X_X) return;\r
896         }\r
897 }\r
898 \r
899 \r
900 // --------- FCEU options ----------\r
901 \r
902 extern int ntsccol,ntschue,ntsctint;\r
903 extern int srendlinev[2];\r
904 extern int erendlinev[2];\r
905 extern int eoptions;\r
906 extern char *cpalette;\r
907 extern void LoadCPalette(void);\r
908 \r
909 \r
910 static void int_incdec(int *p, int inc, int min, int max)\r
911 {\r
912         *p += inc;\r
913         if      (*p < min) *p = min;\r
914         else if (*p > max) *p = max;\r
915 }\r
916 \r
917 static void draw_fcemenu_options(int menu_sel)\r
918 {\r
919         int tl_x = 25, tl_y = 60, y;\r
920         char cpal[32];\r
921 \r
922         if (cpalette != NULL)\r
923         {\r
924                 char *p = cpalette + strlen(cpalette) - 1;\r
925                 while (*p != '/' && p > cpalette) p--;\r
926                 if (*p == '/') p++;\r
927                 strncpy(cpal, p, 16);\r
928                 cpal[16] = 0;\r
929         }\r
930         else strcpy(cpal, "           OFF");\r
931 \r
932         y = tl_y;\r
933         gp2x_fceu_copy_bg();\r
934 \r
935         gp2x_text_out15(tl_x,  y,      "Custom palette: %s", cpal);                             // 0\r
936         gp2x_text_out15(tl_x, (y+=10), "NTSC Color Emulation       %s", ntsccol?"ON":"OFF");\r
937         gp2x_text_out15(tl_x, (y+=10), "  Tint (default: 56)       %i", ntsctint);\r
938         gp2x_text_out15(tl_x, (y+=10), "  Hue  (default: 72)       %i", ntschue);\r
939         gp2x_text_out15(tl_x, (y+=10), "First visible line (NTSC)  %i", srendlinev[0]);\r
940         gp2x_text_out15(tl_x, (y+=10), "Last visible line (NTSC)   %i", erendlinev[0]);         // 5\r
941         gp2x_text_out15(tl_x, (y+=10), "First visible line (PAL)   %i", srendlinev[1]);\r
942         gp2x_text_out15(tl_x, (y+=10), "Last visible line (PAL)    %i", erendlinev[1]);\r
943         gp2x_text_out15(tl_x, (y+=10), "Clip 8 left/right columns  %s", (eoptions&EO_CLIPSIDES)?"ON":"OFF");\r
944         gp2x_text_out15(tl_x, (y+=10), "Disable 8 sprite limit     %s", (eoptions&EO_NO8LIM)?"ON":"OFF");\r
945         gp2x_text_out15(tl_x, (y+=10), "Done");                                                 // 10\r
946 \r
947         // draw cursor\r
948         gp2x_text_out15(tl_x - 16, tl_y + menu_sel*10, ">");\r
949 \r
950         if (menu_sel == 0) {\r
951                 gp2x_fceu_darken_text_bg();\r
952                 gp2x_fceu_darken_reset();\r
953 \r
954                 gp2x_text_out15(30, 210, "Press B to browse,");\r
955                 gp2x_text_out15(30, 220, "START to use default");\r
956         }\r
957 \r
958         gp2x_fceu_darken_text_bg();\r
959         gp2x_video_flip();\r
960 }\r
961 \r
962 static void fcemenu_loop_options(void)\r
963 {\r
964         int menu_sel = 0, menu_sel_max = 10, i;\r
965         unsigned long inp = 0;\r
966 \r
967         FCEUI_GetNTSCTH(&ntsctint, &ntschue);\r
968 \r
969         for(;;)\r
970         {\r
971                 draw_fcemenu_options(menu_sel);\r
972                 inp = wait_for_input(GP2X_UP|GP2X_DOWN|GP2X_LEFT|GP2X_RIGHT|GP2X_B|GP2X_X|GP2X_A|GP2X_START);\r
973                 if(inp & GP2X_UP  ) { menu_sel--; if (menu_sel < 0) menu_sel = menu_sel_max; }\r
974                 if(inp & GP2X_DOWN) { menu_sel++; if (menu_sel > menu_sel_max) menu_sel = 0; }\r
975                 if((inp& GP2X_B)||(inp&GP2X_LEFT)||(inp&GP2X_RIGHT)) { // toggleable options\r
976                         switch (menu_sel) {\r
977                                 case  1: ntsccol = !ntsccol; break;\r
978                                 case  8: eoptions^=EO_CLIPSIDES; break;\r
979                                 case  9: eoptions^=EO_NO8LIM; break;\r
980                                 case 10: return;\r
981                         }\r
982                 }\r
983                 if(inp & (GP2X_X|GP2X_A)) {\r
984                         for(i=0;i<2;i++)\r
985                         {\r
986                                 if(srendlinev[i]<0 || srendlinev[i]>239) srendlinev[i]=0;\r
987                                 if(erendlinev[i]<srendlinev[i] || erendlinev[i]>239) erendlinev[i]=239;\r
988                         }\r
989                         FCEUI_SetNTSCTH(ntsccol, ntsctint, ntschue);\r
990                         FCEUI_SetRenderedLines(srendlinev[0],erendlinev[0],srendlinev[1],erendlinev[1]);\r
991                         FCEUI_DisableSpriteLimitation(eoptions&EO_NO8LIM);\r
992                         if (cpalette) LoadCPalette();\r
993                         else FCEUI_SetPaletteArray(0); // set to default\r
994                         FCEU_ResetPalette();\r
995                         return;\r
996                 }\r
997                 if(inp & (GP2X_LEFT|GP2X_RIGHT)) { // multi choise\r
998                         switch (menu_sel) {\r
999                                 case  2: int_incdec(&ntsctint,      (inp & GP2X_LEFT) ? -1 : 1, 0, 128); break;\r
1000                                 case  3: int_incdec(&ntschue,       (inp & GP2X_LEFT) ? -1 : 1, 0, 128); break;\r
1001                                 case  4: int_incdec(&srendlinev[0], (inp & GP2X_LEFT) ? -1 : 1, 0, 239); break;\r
1002                                 case  5: int_incdec(&erendlinev[0], (inp & GP2X_LEFT) ? -1 : 1, 0, 239); break;\r
1003                                 case  6: int_incdec(&srendlinev[1], (inp & GP2X_LEFT) ? -1 : 1, 0, 239); break;\r
1004                                 case  7: int_incdec(&erendlinev[1], (inp & GP2X_LEFT) ? -1 : 1, 0, 239); break;\r
1005                         }\r
1006                 }\r
1007                 if(menu_sel == 0 && (inp & (GP2X_START|GP2X_B))) { // custom palette\r
1008                         if ((inp & GP2X_START) && cpalette) {\r
1009                                 free(cpalette);\r
1010                                 cpalette=NULL;\r
1011                         }\r
1012                         else if (inp & GP2X_B) {\r
1013                                 char *selfname;\r
1014                                 if (cpalette) strncpy(path_buffer, cpalette, sizeof(path_buffer));\r
1015                                 else getcwd(path_buffer, PATH_MAX);\r
1016                                 path_buffer[sizeof(path_buffer)-1] = 0;\r
1017 \r
1018                                 selfname = filesel_loop(path_buffer, NULL);\r
1019                                 if (selfname) {\r
1020                                         if (cpalette) free(cpalette);\r
1021                                         cpalette = selfname;\r
1022                                 }\r
1023                         }\r
1024                 }\r
1025         }\r
1026 }\r
1027 \r
1028 // -------------- options --------------\r
1029 \r
1030 static void draw_menu_options(int menu_sel)\r
1031 {\r
1032         int tl_x = 25, tl_y = 32, y;\r
1033         char strframeskip[8], *strscaling, *strssconfirm;\r
1034         char *mms = mmuhack_status ? "active)  " : "inactive)";\r
1035 \r
1036         if (Settings.frameskip < 0)\r
1037              strcpy(strframeskip, "Auto");\r
1038         else sprintf(strframeskip, "%i", Settings.frameskip);\r
1039         switch (Settings.scaling) {\r
1040                 default: strscaling = "            OFF";   break;\r
1041                 case 1:  strscaling = "hw horizontal";     break;\r
1042                 case 2:  strscaling = "hw horiz. + vert."; break;\r
1043                 case 3:  strscaling = "sw horizontal";     break;\r
1044         }\r
1045         switch (Settings.sstate_confirm) {\r
1046                 default: strssconfirm = "OFF";    break;\r
1047                 case 1:  strssconfirm = "writes"; break;\r
1048                 case 2:  strssconfirm = "loads";  break;\r
1049                 case 3:  strssconfirm = "both";   break;\r
1050         }\r
1051 \r
1052         y = tl_y;\r
1053         gp2x_fceu_copy_bg();\r
1054 \r
1055         gp2x_text_out15(tl_x,  y,      "Scaling:       %s", strscaling);                                // 0\r
1056         gp2x_text_out15(tl_x, (y+=10), "Show FPS                   %s", Settings.showfps?"ON":"OFF");   // 1\r
1057         gp2x_text_out15(tl_x, (y+=10), "Frameskip                  %s", strframeskip);                  // 2\r
1058         gp2x_text_out15(tl_x, (y+=10), "Enable sound               %s", soundvol?"ON":"OFF");\r
1059         gp2x_text_out15(tl_x, (y+=10), "Sound Rate:           %5iHz", Settings.sound_rate);             // 4\r
1060         gp2x_text_out15(tl_x, (y+=10), "Force Region:              %s",\r
1061                 Settings.region_force == 2 ? "PAL" : Settings.region_force == 1 ? "NTSC" : "OFF");      // 5\r
1062         gp2x_text_out15(tl_x, (y+=10), "Turbo rate                 %iHz", (Settings.turbo_rate_add*60/2) >> 24);\r
1063         gp2x_text_out15(tl_x, (y+=10), "Confirm savestate          %s", strssconfirm);                  // 7\r
1064         gp2x_text_out15(tl_x, (y+=10), "Save slot                  %i", CurrentState);\r
1065         gp2x_text_out15(tl_x, (y+=10), "Faster RAM timings         %s", Settings.ramtimings?"ON":"OFF");\r
1066         gp2x_text_out15(tl_x, (y+=10), "squidgehack (now %s %s",   mms, Settings.mmuhack?"ON":"OFF");   // 10\r
1067         gp2x_text_out15(tl_x, (y+=10), "Gamma correction           %i.%02i", Settings.gamma / 100, Settings.gamma%100);\r
1068         gp2x_text_out15(tl_x, (y+=10), "GP2X CPU clock             %iMhz", Settings.cpuclock);          // 12\r
1069         gp2x_text_out15(tl_x, (y+=10), "[FCE Ultra options]");\r
1070         gp2x_text_out15(tl_x, (y+=10), "Save cfg as default");\r
1071         if (fceugi)\r
1072                 gp2x_text_out15(tl_x, (y+=10), "Save cfg for current game only");\r
1073 \r
1074         // draw cursor\r
1075         gp2x_text_out15(tl_x - 16, tl_y + menu_sel*10, ">");\r
1076 \r
1077         gp2x_fceu_darken_text_bg();\r
1078         gp2x_video_flip();\r
1079 }\r
1080 \r
1081 static int sndrate_prevnext(int rate, int dir)\r
1082 {\r
1083         int i, rates[] = { 8000, 11025, 16000, 22050, 44100 };\r
1084 \r
1085         for (i = 0; i < 5; i++)\r
1086                 if (rates[i] == rate) break;\r
1087 \r
1088         i += dir ? 1 : -1;\r
1089         if (i > 4) return dir ? 44100 : 22050;\r
1090         if (i < 0) return dir ? 11025 : 8000;\r
1091         return rates[i];\r
1092 }\r
1093 \r
1094 static void config_commit(void)\r
1095 {\r
1096         gp2x_cpuclock_gamma_update();\r
1097         if (Settings.region_force)\r
1098                 FCEUI_SetVidSystem(Settings.region_force - 1);\r
1099 }\r
1100 \r
1101 static int menu_loop_options(void)\r
1102 {\r
1103         static int menu_sel = 0;\r
1104         int menu_sel_max = 14;\r
1105         unsigned long inp = 0;\r
1106 \r
1107         if (fceugi) menu_sel_max++;\r
1108 \r
1109         for(;;)\r
1110         {\r
1111                 draw_menu_options(menu_sel);\r
1112                 inp = wait_for_input(GP2X_UP|GP2X_DOWN|GP2X_LEFT|GP2X_RIGHT|GP2X_B|GP2X_X|GP2X_A);\r
1113                 if(inp & GP2X_UP  ) { menu_sel--; if (menu_sel < 0) menu_sel = menu_sel_max; }\r
1114                 if(inp & GP2X_DOWN) { menu_sel++; if (menu_sel > menu_sel_max) menu_sel = 0; }\r
1115                 if((inp& GP2X_B)||(inp&GP2X_LEFT)||(inp&GP2X_RIGHT)) { // toggleable options\r
1116                         switch (menu_sel) {\r
1117                                 case  1: Settings.showfps    = !Settings.showfps; break;\r
1118                                 case  3: soundvol = soundvol ? 0 : 100; break;\r
1119                                 case  9: Settings.ramtimings = !Settings.ramtimings; break;\r
1120                                 case 10: Settings.mmuhack    = !Settings.mmuhack; break;\r
1121                                 case 13: fcemenu_loop_options(); break;\r
1122                                 case 14: // done (update and write)\r
1123                                         config_commit();\r
1124                                         SaveConfig(NULL);\r
1125                                         return 1;\r
1126                                 case 15: // done (update and write for current game)\r
1127                                         config_commit();\r
1128                                         if (lastLoadedGameName[0])\r
1129                                                 SaveConfig(lastLoadedGameName);\r
1130                                         return 1;\r
1131                         }\r
1132                 }\r
1133                 if(inp & (GP2X_X|GP2X_A)) {\r
1134                         config_commit();\r
1135                         return 0;  // done (update, no write)\r
1136                 }\r
1137                 if(inp & (GP2X_LEFT|GP2X_RIGHT)) { // multi choise\r
1138                         switch (menu_sel) {\r
1139                                 case  0: int_incdec(&Settings.scaling,   (inp & GP2X_LEFT) ? -1 : 1,  0,  3); break;\r
1140                                 case  2: int_incdec(&Settings.frameskip, (inp & GP2X_LEFT) ? -1 : 1, -1, 32); break;\r
1141                                 case  4:\r
1142                                         Settings.sound_rate = sndrate_prevnext(Settings.sound_rate, inp & GP2X_RIGHT);\r
1143                                         InitSound();\r
1144                                         break;\r
1145                                 case  5: int_incdec(&Settings.region_force,   (inp & GP2X_LEFT) ? -1 : 1, 0, 2); break;\r
1146                                 case  6: {\r
1147                                         int hz = Settings.turbo_rate_add*60/2 >> 24;\r
1148                                         int_incdec(&hz, (inp & GP2X_LEFT) ? -1 : 1, 1, 30);\r
1149                                         Settings.turbo_rate_add = (hz*2 << 24) / 60 + 1;\r
1150                                         break;\r
1151                                 }\r
1152                                 case  7: int_incdec(&Settings.sstate_confirm, (inp & GP2X_LEFT) ? -1 : 1, 0, 3); break;\r
1153                                 case  8: int_incdec(&CurrentState,            (inp & GP2X_LEFT) ? -1 : 1, 0, 9); break;\r
1154                                 case 11: int_incdec(&Settings.gamma,          (inp & GP2X_LEFT) ? -1 : 1, 0, 300); break;\r
1155                                 case 12:\r
1156                                         while ((inp = gp2x_joystick_read(1)) & (GP2X_LEFT|GP2X_RIGHT)) {\r
1157                                                 Settings.cpuclock += (inp & GP2X_LEFT) ? -1 : 1;\r
1158                                                 if (Settings.cpuclock < 0) Settings.cpuclock = 0; // 0 ~ do not change\r
1159                                                 draw_menu_options(menu_sel);\r
1160                                                 usleep(50*1000);\r
1161                                         }\r
1162                                         break;\r
1163                         }\r
1164                 }\r
1165         }\r
1166 }\r
1167 \r
1168 // -------------- credits --------------\r
1169 \r
1170 static void draw_menu_credits(void)\r
1171 {\r
1172         char vstr[32];\r
1173 \r
1174         //int tl_x = 15, tl_y = 70;\r
1175         gp2x_fceu_copy_bg();\r
1176 \r
1177         sprintf(vstr, "GPFCE v" GP2X_PORT_VERSION " rev%i", GP2X_PORT_REV);\r
1178         gp2x_text_out15(20,  30, vstr);\r
1179         gp2x_text_out15(20,  40, "(c) notaz, 2007");\r
1180 \r
1181         gp2x_text_out15(20,  70, "Based on FCE Ultra versions");\r
1182         gp2x_text_out15(20,  80, "0.81 and 0.98.1x");\r
1183 \r
1184         gp2x_text_out15(20, 110, "         - Credits - ");\r
1185         gp2x_text_out15(20, 130, "Bero: FCE");\r
1186         gp2x_text_out15(20, 140, "Xodnizel: FCE Ultra");\r
1187         gp2x_text_out15(20, 150, "zzhu8192: original port");\r
1188         gp2x_text_out15(20, 160, "rlyeh: minimal lib");\r
1189         gp2x_text_out15(20, 170, "Hermes, theoddbot, god_at_hell:");\r
1190         gp2x_text_out15(20, 180, "  cpuctrl, gamma libs");\r
1191         gp2x_text_out15(20, 190, "Squidge: squidgehack");\r
1192 \r
1193         gp2x_fceu_darken_text_bg();\r
1194         gp2x_video_flip();\r
1195 }\r
1196 \r
1197 \r
1198 // -------------- root menu --------------\r
1199 \r
1200 static void draw_menu_root(int menu_sel)\r
1201 {\r
1202         int tl_x = 30, tl_y = 128, y;\r
1203         gp2x_fceu_copy_bg();\r
1204 \r
1205         y = tl_y;\r
1206         if (fceugi) {\r
1207                 gp2x_text_out15(tl_x, y,       "Resume game");\r
1208                 gp2x_text_out15(tl_x, (y+=10), "Save State");\r
1209                 gp2x_text_out15(tl_x, (y+=10), "Load State");\r
1210                 gp2x_text_out15(tl_x, (y+=10), "Reset game");\r
1211         } else {\r
1212                 y += 30;\r
1213         }\r
1214         gp2x_text_out15(tl_x, (y+=10), "Load new ROM");\r
1215         gp2x_text_out15(tl_x, (y+=10), "Options");\r
1216         gp2x_text_out15(tl_x, (y+=10), "Controls");\r
1217         gp2x_text_out15(tl_x, (y+=10), "Credits");\r
1218         gp2x_text_out15(tl_x, (y+=10), "Exit");\r
1219 // TODO\r
1220 //      if (PicoPatches)\r
1221 //              gp2x_text_out15(tl_x, (y+=10), "Patches / GameGenie");\r
1222 \r
1223         // draw cursor\r
1224         gp2x_text_out15(tl_x - 16, tl_y + menu_sel*10, ">");\r
1225 \r
1226         gp2x_fceu_darken_text_bg();\r
1227         gp2x_fceu_darken_reset();\r
1228 \r
1229         // error / version\r
1230         if (menuErrorMsg[0]) gp2x_text_out15(1, 230, menuErrorMsg);\r
1231         else {\r
1232                 char vstr[16];\r
1233                 sprintf(vstr, "v" GP2X_PORT_VERSION " r%i", GP2X_PORT_REV);\r
1234                 gp2x_text_out15(320-strlen(vstr)*8-1, 230, vstr);\r
1235         }\r
1236         gp2x_fceu_darken_text_bg();\r
1237         gp2x_video_flip();\r
1238 }\r
1239 \r
1240 \r
1241 static int menu_loop_root(void)\r
1242 {\r
1243         int ret, menu_sel_max = 8, menu_sel_min = 4;\r
1244         static int menu_sel = 4;\r
1245         unsigned long inp = 0;\r
1246 \r
1247         if (fceugi) menu_sel_min = 0;\r
1248 // TODO if (PicoPatches) menu_sel_max = 9;\r
1249         if (menu_sel < menu_sel_min || menu_sel > menu_sel_max)\r
1250                 menu_sel = menu_sel_min;\r
1251 \r
1252         /* make sure action buttons are not pressed on entering menu */\r
1253         draw_menu_root(menu_sel);\r
1254         while (gp2x_joystick_read(1) & (GP2X_B|GP2X_X|GP2X_SELECT)) usleep(50*1000);\r
1255 \r
1256         for (;;)\r
1257         {\r
1258                 draw_menu_root(menu_sel);\r
1259                 inp = wait_for_input(GP2X_UP|GP2X_DOWN|GP2X_B|GP2X_X);\r
1260                 if(inp & GP2X_UP  )  { menu_sel--; if (menu_sel < menu_sel_min) menu_sel = menu_sel_max; }\r
1261                 if(inp & GP2X_DOWN)  { menu_sel++; if (menu_sel > menu_sel_max) menu_sel = menu_sel_min; }\r
1262                 if(inp &(GP2X_SELECT|GP2X_X)){\r
1263                         if (fceugi) {\r
1264                                 while (gp2x_joystick_read(1) & GP2X_X) usleep(50*1000); // wait until X is released\r
1265                                 Exit = 0;\r
1266                                 return 0;\r
1267                         }\r
1268                 }\r
1269                 if(inp & GP2X_B   )  {\r
1270                         switch (menu_sel) {\r
1271                                 case 0: // resume game\r
1272                                         if (fceugi) {\r
1273                                                 while (gp2x_joystick_read(1) & GP2X_B) usleep(50*1000);\r
1274                                                 Exit = 0;\r
1275                                                 return 0;\r
1276                                         }\r
1277                                         break;\r
1278                                 case 1: // save state\r
1279                                         if (fceugi) {\r
1280                                                 /*if(savestate_menu_loop(0))\r
1281                                                         continue;*/\r
1282                                                 FCEUI_SaveState();\r
1283                                                 Exit = 0;\r
1284                                                 while (gp2x_joystick_read(1) & GP2X_B) usleep(50*1000);\r
1285                                                 return 0;\r
1286                                         }\r
1287                                         break;\r
1288                                 case 2: // load state\r
1289                                         if (fceugi) {\r
1290                                                 /*if(savestate_menu_loop(1))\r
1291                                                         continue;*/\r
1292                                                 FCEUI_LoadState();\r
1293                                                 Exit = 0;\r
1294                                                 while (gp2x_joystick_read(1) & GP2X_B) usleep(50*1000);\r
1295                                                 return 0;\r
1296                                         }\r
1297                                         break;\r
1298                                 case 3: // reset game\r
1299                                         if (fceugi) {\r
1300                                                 FCEU_DoSimpleCommand(FCEUNPCMD_RESET);\r
1301                                                 Exit = 0;\r
1302                                                 return 0;\r
1303                                         }\r
1304                                         break;\r
1305                                 case 4: // select rom\r
1306                                 {\r
1307                                         FILE *tstf;\r
1308                                         char *selfname;\r
1309 \r
1310                                         if ( (tstf = fopen(lastLoadedGameName, "rb")) )\r
1311                                         {\r
1312                                                 fclose(tstf);\r
1313                                                 strncpy(path_buffer, lastLoadedGameName, sizeof(path_buffer));\r
1314                                                 path_buffer[sizeof(path_buffer)-1] = 0;\r
1315                                         }\r
1316                                         else\r
1317                                         {\r
1318                                                 getcwd(path_buffer, PATH_MAX);\r
1319                                         }\r
1320 \r
1321                                         selfname = filesel_loop(path_buffer, lastLoadedGameName);\r
1322                                         if (selfname) {\r
1323                                                 printf("selected file: %s\n", selfname);\r
1324                                                 while (gp2x_joystick_read(1) & GP2X_B) usleep(50*1000);\r
1325                                                 return 2;\r
1326                                         }\r
1327                                         break;\r
1328                                 }\r
1329                                 case 5: // options\r
1330                                         ret = menu_loop_options();\r
1331                                         if (ret == 1) continue; // status update\r
1332                                         break;\r
1333                                 case 6: // controls\r
1334                                         kc_sel_loop();\r
1335                                         break;\r
1336                                 case 7: // credits\r
1337                                         draw_menu_credits();\r
1338                                         usleep(500*1000);\r
1339                                         inp = wait_for_input(GP2X_B|GP2X_X);\r
1340                                         break;\r
1341                                 case 8: // exit\r
1342                                         return 1;\r
1343                                 case 9: // patches/gg\r
1344                                         break;\r
1345                         }\r
1346                 }\r
1347                 menuErrorMsg[0] = 0; // clear error msg\r
1348         }\r
1349 }\r
1350 \r
1351 \r
1352 extern unsigned short gp2x_palette16[256];\r
1353 \r
1354 static void menu_prepare_bg(void)\r
1355 {\r
1356         menu_bg = malloc(320*240*2);\r
1357         if (menu_bg == NULL) return;\r
1358 \r
1359         if (fceugi)\r
1360         {\r
1361                 /* raw emu frame should now be at gp2x_screen */\r
1362                 soft_scale((char *)gp2x_screen + 32, gp2x_palette16, srendline, erendline-srendline);\r
1363                 if (srendline)\r
1364                         memset32((int *)((char *)gp2x_screen + 32), 0, srendline*320*2/4);\r
1365                 memcpy(menu_bg, gp2x_screen + 32, 320*240*2);\r
1366         }\r
1367         else\r
1368         {\r
1369                 memset32((int *)menu_bg, 0, 320*240*2/4);\r
1370                 readpng(menu_bg, "background.png");\r
1371         }\r
1372 }\r
1373 \r
1374 static void menu_gfx_prepare(void)\r
1375 {\r
1376         menu_prepare_bg();\r
1377 \r
1378         // switch bpp\r
1379         gp2x_video_changemode(16);\r
1380         gp2x_video_set_offs(0);\r
1381         gp2x_video_RGB_setscaling(0, 320, 240);\r
1382         gp2x_video_flip();\r
1383 }\r
1384 \r
1385 \r
1386 int gp2x_menu_do(void)\r
1387 {\r
1388         int ret;\r
1389 \r
1390         menu_gfx_prepare();\r
1391 \r
1392         ret = menu_loop_root();\r
1393 \r
1394         if (menu_bg) free(menu_bg);\r
1395         menu_bg = NULL;\r
1396         menuErrorMsg[0] = 0;\r
1397 \r
1398         return ret;\r
1399 }\r
1400 \r
1401 \r