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