menu and controls wip..
[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 \r
24 #ifndef _DIRENT_HAVE_D_TYPE\r
25 #error "need d_type for file browser\r
26 #endif\r
27 \r
28 static int GP2X_PORT_REV =\r
29 #include "rev.h"\r
30 ;\r
31 \r
32 extern char lastLoadedGameName[PATH_MAX];\r
33 extern int  mmuhack_status;\r
34 //extern int  state_slot; // TODO\r
35 extern uint8 Exit; // exit emu loop flag\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 char menuErrorMsg[40] = {0, };\r
47 \r
48 // TODO\r
49 void gp2x_fceu_copy_bg(void)\r
50 {\r
51         memset(gp2x_screen, 0, 320*240*2);\r
52 }\r
53 \r
54 // draws white text to current bbp15 screen\r
55 static void gp2x_text_out15_(int x, int y, const char *text)\r
56 {\r
57         int i,l;\r
58         unsigned short *screen = gp2x_screen;\r
59 \r
60         screen = screen + x + y*320;\r
61 \r
62         for (i = 0; i < strlen(text); i++)\r
63         {\r
64                 for (l=0;l<8;l++)\r
65                 {\r
66                         if(fontdata8x8[((text[i])*8)+l]&0x80) screen[l*320+0]=0xffff;\r
67                         if(fontdata8x8[((text[i])*8)+l]&0x40) screen[l*320+1]=0xffff;\r
68                         if(fontdata8x8[((text[i])*8)+l]&0x20) screen[l*320+2]=0xffff;\r
69                         if(fontdata8x8[((text[i])*8)+l]&0x10) screen[l*320+3]=0xffff;\r
70                         if(fontdata8x8[((text[i])*8)+l]&0x08) screen[l*320+4]=0xffff;\r
71                         if(fontdata8x8[((text[i])*8)+l]&0x04) screen[l*320+5]=0xffff;\r
72                         if(fontdata8x8[((text[i])*8)+l]&0x02) screen[l*320+6]=0xffff;\r
73                         if(fontdata8x8[((text[i])*8)+l]&0x01) screen[l*320+7]=0xffff;\r
74                 }\r
75                 screen += 8;\r
76         }\r
77 }\r
78 \r
79 void gp2x_text_out15(int x, int y, const char *texto, ...)\r
80 {\r
81         va_list args;\r
82         char    buffer[512];\r
83 \r
84         va_start(args,texto);\r
85         vsprintf(buffer,texto,args);\r
86         va_end(args);\r
87 \r
88         gp2x_text_out15_(x,y,buffer);\r
89 }\r
90 \r
91 \r
92 void gp2x_text_out15_lim(int x, int y, const char *texto, int max)\r
93 {\r
94         char    buffer[320/8+1];\r
95 \r
96         strncpy(buffer, texto, 320/8);\r
97         if (max > 320/8) max = 320/8;\r
98         if (max < 0) max = 0;\r
99         buffer[max] = 0;\r
100 \r
101         gp2x_text_out15(x,y,buffer);\r
102 }\r
103 \r
104 static void gp2x_smalltext16(int x, int y, const char *texto)\r
105 {\r
106         int i;\r
107         unsigned char  *src;\r
108         unsigned short *dst;\r
109 \r
110         for (i = 0;; i++, x += 6)\r
111         {\r
112                 unsigned char c = (unsigned char) texto[i];\r
113                 int h = 8;\r
114 \r
115                 if (!c) break;\r
116 \r
117                 src = fontdata6x8[c];\r
118                 dst = (unsigned short *)gp2x_screen + x + y*320;\r
119 \r
120                 while (h--)\r
121                 {\r
122                         int w = 0x20;\r
123                         while (w)\r
124                         {\r
125                                 if( *src & w ) *dst = 0xffff;\r
126                                 dst++;\r
127                                 w>>=1;\r
128                         }\r
129                         src++;\r
130 \r
131                         dst += 320-6;\r
132                 }\r
133         }\r
134 }\r
135 \r
136 static void gp2x_smalltext8_lim(int x, int y, const char *texto, int max)\r
137 {\r
138         char    buffer[320/6+1];\r
139 \r
140         strncpy(buffer, texto, 320/6);\r
141         if (max > 320/6) max = 320/6;\r
142         if (max < 0) max = 0;\r
143         buffer[max] = 0;\r
144 \r
145         gp2x_smalltext16(x, y, buffer);\r
146 }\r
147 \r
148 \r
149 static unsigned long inp_prev = 0;\r
150 static int inp_prevjoy = 0;\r
151 \r
152 static unsigned long wait_for_input(unsigned long interesting)\r
153 {\r
154         unsigned long ret;\r
155         static int repeats = 0, wait = 50*1000;\r
156         int release = 0, i;\r
157 \r
158         if (repeats == 2 || repeats == 4) wait /= 2;\r
159         if (repeats == 6) wait = 15 * 1000;\r
160 \r
161         for (i = 0; i < 6 && inp_prev == gp2x_joystick_read(1); i++) {\r
162                 if (i == 0) repeats++;\r
163                 if (wait >= 30*1000) usleep(wait); // usleep sleeps for ~30ms minimum\r
164                 else spend_cycles(wait * Settings.cpuclock);\r
165         }\r
166 \r
167         while ( !((ret = gp2x_joystick_read(1)) & interesting) ) {\r
168                 usleep(50000);\r
169                 release = 1;\r
170         }\r
171 \r
172         if (release || ret != inp_prev) {\r
173                 repeats = 0;\r
174                 wait = 50*1000;\r
175         }\r
176         inp_prev = ret;\r
177         inp_prevjoy = 0;\r
178 \r
179         // we don't need diagonals in menus\r
180         if ((ret&GP2X_UP)   && (ret&GP2X_LEFT))  ret &= ~GP2X_LEFT;\r
181         if ((ret&GP2X_UP)   && (ret&GP2X_RIGHT)) ret &= ~GP2X_RIGHT;\r
182         if ((ret&GP2X_DOWN) && (ret&GP2X_LEFT))  ret &= ~GP2X_LEFT;\r
183         if ((ret&GP2X_DOWN) && (ret&GP2X_RIGHT)) ret &= ~GP2X_RIGHT;\r
184 \r
185         return ret;\r
186 }\r
187 \r
188 static unsigned long input2_read(unsigned long interesting, int *joy)\r
189 {\r
190         unsigned long ret;\r
191         int i;\r
192 \r
193         do\r
194         {\r
195                 *joy = 0;\r
196                 if ((ret = gp2x_joystick_read(0) & interesting)) break;\r
197                 gp2x_usbjoy_update();\r
198                 for (i = 0; i < num_of_joys; i++) {\r
199                         ret = gp2x_usbjoy_check2(i);\r
200                         if (ret) { *joy = i + 1; break; }\r
201                 }\r
202                 if (ret) break;\r
203         }\r
204         while(0);\r
205 \r
206         return ret;\r
207 }\r
208 \r
209 // similar to wait_for_input(), but returns joy num\r
210 static unsigned long wait_for_input_usbjoy(unsigned long interesting, int *joy)\r
211 {\r
212         unsigned long ret;\r
213         const int wait = 300*1000;\r
214         int i;\r
215 \r
216         if (inp_prevjoy == 0) inp_prev &= interesting;\r
217         for (i = 0; i < 6; i++) {\r
218                 ret = input2_read(interesting, joy);\r
219                 if (*joy != inp_prevjoy || ret != inp_prev) break;\r
220                 usleep(wait/6);\r
221         }\r
222 \r
223         while ( !(ret = input2_read(interesting, joy)) ) {\r
224                 usleep(50000);\r
225         }\r
226 \r
227         inp_prev = ret;\r
228         inp_prevjoy = *joy;\r
229 \r
230         return ret;\r
231 }\r
232 \r
233 \r
234 \r
235 // -------------- ROM selector --------------\r
236 \r
237 static void draw_dirlist(char *curdir, struct dirent **namelist, int n, int sel)\r
238 {\r
239         int start, i, pos;\r
240 \r
241         start = 12 - sel;\r
242         n--; // exclude current dir (".")\r
243 \r
244         //memset(gp2x_screen, 0, 320*240);\r
245         gp2x_fceu_copy_bg();\r
246 \r
247         if(start - 2 >= 0)\r
248                 gp2x_smalltext8_lim(14, (start - 2)*10, curdir, 53-2);\r
249         for (i = 0; i < n; i++) {\r
250                 pos = start + i;\r
251                 if (pos < 0)  continue;\r
252                 if (pos > 23) break;\r
253                 if (namelist[i+1]->d_type == DT_DIR) {\r
254                         gp2x_smalltext8_lim(14,   pos*10, "/", 1);\r
255                         gp2x_smalltext8_lim(14+6, pos*10, namelist[i+1]->d_name, 53-3);\r
256                 } else {\r
257                         gp2x_smalltext8_lim(14,   pos*10, namelist[i+1]->d_name, 53-2);\r
258                 }\r
259         }\r
260         gp2x_text_out15(5, 120, ">");\r
261         gp2x_video_flip();\r
262 }\r
263 \r
264 static int scandir_cmp(const void *p1, const void *p2)\r
265 {\r
266         struct dirent **d1 = (struct dirent **)p1, **d2 = (struct dirent **)p2;\r
267         if ((*d1)->d_type == (*d2)->d_type) return alphasort(d1, d2);\r
268         if ((*d1)->d_type == DT_DIR) return -1; // put before\r
269         if ((*d2)->d_type == DT_DIR) return  1;\r
270         return alphasort(d1, d2);\r
271 }\r
272 \r
273 static char *filter_exts[] = {\r
274         ".gpe", ".png", "ck.o", ".txt"\r
275 };\r
276 \r
277 static int scandir_filter(const struct dirent *ent)\r
278 {\r
279         const char *p;\r
280         int i;\r
281 \r
282         if (ent == NULL || ent->d_name == NULL) return 0;\r
283         if (strlen(ent->d_name) < 5) return 1;\r
284 \r
285         p = ent->d_name + strlen(ent->d_name) - 4;\r
286 \r
287         for (i = 0; i < sizeof(filter_exts)/sizeof(filter_exts[0]); i++)\r
288         {\r
289                 if (strcmp(p, filter_exts[i]) == 0) return 0;\r
290         }\r
291 \r
292         return 1;\r
293 }\r
294 \r
295 static char *romsel_loop(char *curr_path)\r
296 {\r
297         struct dirent **namelist;\r
298         DIR *dir;\r
299         int n, sel = 0;\r
300         unsigned long inp = 0;\r
301         char *ret = NULL, *fname = NULL;\r
302 \r
303         // is this a dir or a full path?\r
304         if ((dir = opendir(curr_path))) {\r
305                 closedir(dir);\r
306         } else {\r
307                 char *p;\r
308                 for (p = curr_path + strlen(curr_path) - 1; p > curr_path && *p != '/'; p--);\r
309                 *p = 0;\r
310                 fname = p+1;\r
311         }\r
312 \r
313         n = scandir(curr_path, &namelist, scandir_filter, scandir_cmp);\r
314         if (n < 0) {\r
315                 // try root\r
316                 n = scandir("/", &namelist, scandir_filter, scandir_cmp);\r
317                 if (n < 0) {\r
318                         // oops, we failed\r
319                         printf("dir: %s\n", curr_path);\r
320                         perror("scandir");\r
321                         return NULL;\r
322                 }\r
323         }\r
324 \r
325         // try to find sel\r
326         if (fname != NULL) {\r
327                 int i;\r
328                 for (i = 1; i < n; i++) {\r
329                         if (strcmp(namelist[i]->d_name, fname) == 0) {\r
330                                 sel = i - 1;\r
331                                 break;\r
332                         }\r
333                 }\r
334         }\r
335 \r
336         for (;;)\r
337         {\r
338                 draw_dirlist(curr_path, namelist, n, sel);\r
339                 inp = wait_for_input(GP2X_UP|GP2X_DOWN|GP2X_LEFT|GP2X_RIGHT|GP2X_L|GP2X_R|GP2X_B|GP2X_X);\r
340                 if(inp & GP2X_UP  )  { sel--;   if (sel < 0)   sel = n-2; }\r
341                 if(inp & GP2X_DOWN)  { sel++;   if (sel > n-2) sel = 0; }\r
342                 if(inp & GP2X_LEFT)  { sel-=10; if (sel < 0)   sel = 0; }\r
343                 if(inp & GP2X_L)     { sel-=24; if (sel < 0)   sel = 0; }\r
344                 if(inp & GP2X_RIGHT) { sel+=10; if (sel > n-2) sel = n-2; }\r
345                 if(inp & GP2X_R)     { sel+=24; if (sel > n-2) sel = n-2; }\r
346                 if(inp & GP2X_B)     { // enter dir/select\r
347                         again:\r
348                         if (namelist[sel+1]->d_type == DT_REG) {\r
349                                 strcpy(lastLoadedGameName, curr_path);\r
350                                 strcat(lastLoadedGameName, "/");\r
351                                 strcat(lastLoadedGameName, namelist[sel+1]->d_name);\r
352                                 ret = lastLoadedGameName;\r
353                                 break;\r
354                         } else if (namelist[sel+1]->d_type == DT_DIR) {\r
355                                 int newlen = strlen(curr_path) + strlen(namelist[sel+1]->d_name) + 2;\r
356                                 char *p, *newdir = malloc(newlen);\r
357                                 if (strcmp(namelist[sel+1]->d_name, "..") == 0) {\r
358                                         char *start = curr_path;\r
359                                         p = start + strlen(start) - 1;\r
360                                         while (*p == '/' && p > start) p--;\r
361                                         while (*p != '/' && p > start) p--;\r
362                                         if (p <= start) strcpy(newdir, "/");\r
363                                         else { strncpy(newdir, start, p-start); newdir[p-start] = 0; }\r
364                                 } else {\r
365                                         strcpy(newdir, curr_path);\r
366                                         p = newdir + strlen(newdir) - 1;\r
367                                         while (*p == '/' && p >= newdir) *p-- = 0;\r
368                                         strcat(newdir, "/");\r
369                                         strcat(newdir, namelist[sel+1]->d_name);\r
370                                 }\r
371                                 ret = romsel_loop(newdir);\r
372                                 free(newdir);\r
373                                 break;\r
374                         } else {\r
375                                 // unknown file type, happens on NTFS mounts. Try to guess.\r
376                                 FILE *tstf; int tmp;\r
377                                 strcpy(lastLoadedGameName, curr_path);\r
378                                 strcat(lastLoadedGameName, "/");\r
379                                 strcat(lastLoadedGameName, namelist[sel+1]->d_name);\r
380                                 tstf = fopen(lastLoadedGameName, "rb");\r
381                                 if (tstf != NULL)\r
382                                 {\r
383                                         if (fread(&tmp, 1, 1, tstf) > 0 || ferror(tstf) == 0)\r
384                                                 namelist[sel+1]->d_type = DT_REG;\r
385                                         else    namelist[sel+1]->d_type = DT_DIR;\r
386                                         fclose(tstf);\r
387                                         goto again;\r
388                                 }\r
389                         }\r
390                 }\r
391                 if(inp & GP2X_X) break; // cancel\r
392         }\r
393 \r
394         if (n > 0) {\r
395                 while(n--) free(namelist[n]);\r
396                 free(namelist);\r
397         }\r
398 \r
399         return ret;\r
400 }\r
401 \r
402 // ------------ patch/gg menu ------------\r
403 \r
404 #if 0 // TODO?\r
405 static void draw_patchlist(int sel)\r
406 {\r
407         int start, i, pos;\r
408 \r
409         start = 12 - sel;\r
410 \r
411         gp2x_fceu_copy_bg();\r
412 \r
413         for (i = 0; i < PicoPatchCount; i++) {\r
414                 pos = start + i;\r
415                 if (pos < 0)  continue;\r
416                 if (pos > 23) break;\r
417                 gp2x_smalltext8_lim(14,     pos*10, PicoPatches[i].active ? "ON " : "OFF", 3);\r
418                 gp2x_smalltext8_lim(14+6*4, pos*10, PicoPatches[i].name, 53-6);\r
419         }\r
420         pos = start + i;\r
421         if (pos < 24) gp2x_smalltext8_lim(14, pos*10, "done", 4);\r
422 \r
423         gp2x_text_out15(5, 120, ">");\r
424         gp2x_video_flip();\r
425 }\r
426 \r
427 \r
428 void patches_menu_loop(void)\r
429 {\r
430         int menu_sel = 0;\r
431         unsigned long inp = 0;\r
432 \r
433         for(;;)\r
434         {\r
435                 draw_patchlist(menu_sel);\r
436                 inp = wait_for_input(GP2X_UP|GP2X_DOWN|GP2X_LEFT|GP2X_RIGHT|GP2X_L|GP2X_R|GP2X_B|GP2X_X);\r
437                 if(inp & GP2X_UP  ) { menu_sel--; if (menu_sel < 0) menu_sel = PicoPatchCount; }\r
438                 if(inp & GP2X_DOWN) { menu_sel++; if (menu_sel > PicoPatchCount) menu_sel = 0; }\r
439                 if(inp &(GP2X_LEFT|GP2X_L))  { menu_sel-=10; if (menu_sel < 0) menu_sel = 0; }\r
440                 if(inp &(GP2X_RIGHT|GP2X_R)) { menu_sel+=10; if (menu_sel > PicoPatchCount) menu_sel = PicoPatchCount; }\r
441                 if(inp & GP2X_B) { // action\r
442                         if (menu_sel < PicoPatchCount)\r
443                                 PicoPatches[menu_sel].active = !PicoPatches[menu_sel].active;\r
444                         else    return;\r
445                 }\r
446                 if(inp & GP2X_X) return;\r
447         }\r
448 \r
449 }\r
450 \r
451 // ------------ savestate loader ------------\r
452 \r
453 static void menu_prepare_bg(void);\r
454 \r
455 static int state_slot_flags = 0;\r
456 \r
457 static void state_check_slots(void)\r
458 {\r
459         int slot;\r
460 \r
461         state_slot_flags = 0;\r
462 \r
463         for (slot = 0; slot < 10; slot++)\r
464         {\r
465                 if (emu_check_save_file(slot))\r
466                 {\r
467                         state_slot_flags |= 1 << slot;\r
468                 }\r
469         }\r
470 }\r
471 \r
472 static void draw_savestate_bg(int slot)\r
473 {\r
474         struct PicoVideo tmp_pv;\r
475         unsigned short tmp_cram[0x40];\r
476         unsigned short tmp_vsram[0x40];\r
477         void *tmp_vram, *file;\r
478         char *fname;\r
479 \r
480         fname = emu_GetSaveFName(1, 0, slot);\r
481         if (!fname) return;\r
482 \r
483         tmp_vram = malloc(sizeof(Pico.vram));\r
484         if (tmp_vram == NULL) return;\r
485 \r
486         memcpy(tmp_vram, Pico.vram, sizeof(Pico.vram));\r
487         memcpy(tmp_cram, Pico.cram, sizeof(Pico.cram));\r
488         memcpy(tmp_vsram, Pico.vsram, sizeof(Pico.vsram));\r
489         memcpy(&tmp_pv, &Pico.video, sizeof(Pico.video));\r
490 \r
491         if (strcmp(fname + strlen(fname) - 3, ".gz") == 0) {\r
492                 file = gzopen(fname, "rb");\r
493                 emu_set_save_cbs(1);\r
494         } else {\r
495                 file = fopen(fname, "rb");\r
496                 emu_set_save_cbs(0);\r
497         }\r
498 \r
499         if (file) {\r
500                 if (PicoMCD & 1) {\r
501                         PicoCdLoadStateGfx(file);\r
502                 } else {\r
503                         areaSeek(file, 0x10020, SEEK_SET);  // skip header and RAM in state file\r
504                         areaRead(Pico.vram, 1, sizeof(Pico.vram), file);\r
505                         areaSeek(file, 0x2000, SEEK_CUR);\r
506                         areaRead(Pico.cram, 1, sizeof(Pico.cram), file);\r
507                         areaRead(Pico.vsram, 1, sizeof(Pico.vsram), file);\r
508                         areaSeek(file, 0x221a0, SEEK_SET);\r
509                         areaRead(&Pico.video, 1, sizeof(Pico.video), file);\r
510                 }\r
511                 areaClose(file);\r
512         }\r
513 \r
514         emu_forced_frame();\r
515         gp2x_memcpy_buffers((1<<2), gp2x_screen, 0, 320*240*2);\r
516         menu_prepare_bg();\r
517 \r
518         memcpy(Pico.vram, tmp_vram, sizeof(Pico.vram));\r
519         memcpy(Pico.cram, tmp_cram, sizeof(Pico.cram));\r
520         memcpy(Pico.vsram, tmp_vsram, sizeof(Pico.vsram));\r
521         memcpy(&Pico.video, &tmp_pv,  sizeof(Pico.video));\r
522         free(tmp_vram);\r
523 }\r
524 \r
525 static void draw_savestate_menu(int menu_sel, int is_loading)\r
526 {\r
527         int tl_x = 25, tl_y = 60, y, i;\r
528 \r
529         if (state_slot_flags & (1 << menu_sel))\r
530                 draw_savestate_bg(menu_sel);\r
531         gp2x_fceu_copy_bg();\r
532 \r
533         gp2x_text_out15(tl_x, 30, is_loading ? "Load state" : "Save state");\r
534 \r
535         /* draw all 10 slots */\r
536         y = tl_y;\r
537         for (i = 0; i < 10; i++, y+=10)\r
538         {\r
539                 gp2x_text_out15(tl_x, y, "SLOT %i (%s)", i, (state_slot_flags & (1 << i)) ? "USED" : "free");\r
540         }\r
541         gp2x_text_out15(tl_x, y, "back");\r
542 \r
543         // draw cursor\r
544         gp2x_text_out15(tl_x - 16, tl_y + menu_sel*10, ">");\r
545 \r
546         gp2x_video_flip();\r
547 }\r
548 \r
549 static int savestate_menu_loop(int is_loading)\r
550 {\r
551         int menu_sel = 10, menu_sel_max = 10;\r
552         unsigned long inp = 0;\r
553 \r
554         state_check_slots();\r
555 \r
556         for(;;)\r
557         {\r
558                 draw_savestate_menu(menu_sel, is_loading);\r
559                 inp = wait_for_input(GP2X_UP|GP2X_DOWN|GP2X_B|GP2X_X);\r
560                 if(inp & GP2X_UP  ) {\r
561                         do {\r
562                                 menu_sel--; if (menu_sel < 0) menu_sel = menu_sel_max;\r
563                         } while (!(state_slot_flags & (1 << menu_sel)) && menu_sel != menu_sel_max && is_loading);\r
564                 }\r
565                 if(inp & GP2X_DOWN) {\r
566                         do {\r
567                                 menu_sel++; if (menu_sel > menu_sel_max) menu_sel = 0;\r
568                         } while (!(state_slot_flags & (1 << menu_sel)) && menu_sel != menu_sel_max && is_loading);\r
569                 }\r
570                 if(inp & GP2X_B) { // save/load\r
571                         if (menu_sel < 10) {\r
572                                 state_slot = menu_sel;\r
573                                 if (emu_SaveLoadGame(is_loading, 0)) {\r
574                                         strcpy(menuErrorMsg, is_loading ? "Load failed" : "Save failed");\r
575                                         return 1;\r
576                                 }\r
577                                 return 0;\r
578                         } else  return 1;\r
579                 }\r
580                 if(inp & GP2X_X) return 1;\r
581         }\r
582 }\r
583 #endif\r
584 \r
585 // -------------- key config --------------\r
586 \r
587 static char *usb_joy_key_name(int joy, int num)\r
588 {\r
589         static char name[16];\r
590         switch (num)\r
591         {\r
592                 case 0: sprintf(name, "Joy%i UP", joy); break;\r
593                 case 1: sprintf(name, "Joy%i DOWN", joy); break;\r
594                 case 2: sprintf(name, "Joy%i LEFT", joy); break;\r
595                 case 3: sprintf(name, "Joy%i RIGHT", joy); break;\r
596                 default:sprintf(name, "Joy%i b%i", joy, num-3); break;\r
597         }\r
598         return name;\r
599 }\r
600 \r
601 static char *action_binds(int player_idx, int action_mask)\r
602 {\r
603         static char strkeys[32*5];\r
604         int joy, i;\r
605 \r
606         strkeys[0] = 0;\r
607         for (i = 0; i < 32; i++) // i is key index\r
608         {\r
609                 if (Settings.KeyBinds[i] & action_mask)\r
610                 {\r
611                         if (player_idx >= 0 && ((Settings.KeyBinds[i] >> 16) & 3) != player_idx) continue;\r
612                         if (strkeys[0]) { strcat(strkeys, " + "); strcat(strkeys, gp2xKeyNames[i]); break; }\r
613                         else strcpy(strkeys, gp2xKeyNames[i]);\r
614                 }\r
615         }\r
616         for (joy = 0; joy < num_of_joys; joy++)\r
617         {\r
618                 for (i = 0; i < 32; i++)\r
619                 {\r
620                         if (Settings.JoyBinds[joy][i] & action_mask)\r
621                         {\r
622                                 if (player_idx >= 0 && ((Settings.KeyBinds[i] >> 16) & 3) != player_idx) continue;\r
623                                 if (strkeys[0]) {\r
624                                         strcat(strkeys, ", "); strcat(strkeys, usb_joy_key_name(joy + 1, i));\r
625                                         break;\r
626                                 }\r
627                                 else strcpy(strkeys, usb_joy_key_name(joy + 1, i));\r
628                         }\r
629                 }\r
630         }\r
631 \r
632         return strkeys;\r
633 }\r
634 \r
635 static void unbind_action(int action)\r
636 {\r
637         int i, u;\r
638 \r
639         for (i = 0; i < 32; i++)\r
640                 Settings.KeyBinds[i] &= ~action;\r
641         for (u = 0; u < 4; u++)\r
642                 for (i = 0; i < 32; i++)\r
643                         Settings.JoyBinds[u][i] &= ~action;\r
644 }\r
645 \r
646 static int count_bound_keys(int action, int is_joy)\r
647 {\r
648         int i, u, keys = 0;\r
649 \r
650         if (is_joy)\r
651         {\r
652                 for (u = 0; u < 4; u++)\r
653                         for (i = 0; i < 32; i++)\r
654                                 if (Settings.JoyBinds[u][i] & action) keys++;\r
655         }\r
656         else\r
657         {\r
658                 for (i = 0; i < 32; i++)\r
659                         if (Settings.KeyBinds[i] & action) keys++;\r
660         }\r
661         return keys;\r
662 }\r
663 \r
664 typedef struct { char *name; int mask; } bind_action_t;\r
665 \r
666 // b_turbo,a_turbo  RLDU SEBA\r
667 static bind_action_t ctrl_actions[] =\r
668 {\r
669         { "UP     ", 0x010 },\r
670         { "DOWN   ", 0x020 },\r
671         { "LEFT   ", 0x040 },\r
672         { "RIGHT  ", 0x080 },\r
673         { "A      ", 0x001 },\r
674         { "B      ", 0x002 },\r
675         { "A TURBO", 0x100 },\r
676         { "B TURBO", 0x200 },\r
677         { "START  ", 0x008 },\r
678         { "SELECT ", 0x004 },\r
679 };\r
680 \r
681 static void draw_key_config(const bind_action_t *opts, int opt_cnt, int player_idx, int sel)\r
682 {\r
683         int x, y, tl_y = 40, i;\r
684 \r
685         gp2x_fceu_copy_bg();\r
686         gp2x_text_out15(80, 20, "Player %i controls", player_idx + 1);\r
687 \r
688         x = 40; y = tl_y;\r
689         for (i = 0; i < opt_cnt; i++, y+=10)\r
690                 gp2x_text_out15(x, y, "%s : %s", opts[i].name, action_binds(player_idx, opts[i].mask));\r
691 \r
692         gp2x_text_out15(x, y, "Done");\r
693 \r
694         // draw cursor\r
695         gp2x_text_out15(x - 16, tl_y + sel*10, ">");\r
696 \r
697         if (sel < 10) {\r
698                 gp2x_text_out15(30, 190, "Press a button to bind/unbind");\r
699                 gp2x_text_out15(30, 200, "Use VOL+ to clear");\r
700                 gp2x_text_out15(30, 210, "To bind UP/DOWN, hold VOL-");\r
701                 gp2x_text_out15(30, 220, "Select \"Done\" to exit");\r
702         } else {\r
703                 gp2x_text_out15(30, 220, "Press B or X to exit");\r
704         }\r
705         gp2x_video_flip();\r
706 }\r
707 \r
708 static void key_config_loop(const bind_action_t *opts, int opt_cnt, int player_idx)\r
709 {\r
710         int joy = 0, sel = 0, menu_sel_max = opt_cnt, i;\r
711         unsigned long inp = 0;\r
712 \r
713         for (;;)\r
714         {\r
715                 draw_key_config(opts, opt_cnt, player_idx, sel);\r
716                 inp = wait_for_input_usbjoy(CONFIGURABLE_KEYS|GP2X_VOL_DOWN|GP2X_VOL_UP, &joy);\r
717                 // printf("got %08lX from joy %i\n", inp, joy);\r
718                 if (joy == 0) {\r
719                         if (!(inp & GP2X_VOL_DOWN)) {\r
720                                 if(inp & GP2X_UP  ) { sel--; if (sel < 0) sel = menu_sel_max; continue; }\r
721                                 if(inp & GP2X_DOWN) { sel++; if (sel > menu_sel_max) sel = 0; continue; }\r
722                         }\r
723                         if (sel >= opt_cnt) {\r
724                                 if (inp & (GP2X_B|GP2X_X)) break;\r
725                                 else continue;\r
726                         }\r
727                         // if we are here, we want to bind/unbind something\r
728                         if (inp & GP2X_VOL_UP)\r
729                                 unbind_action(opts[sel].mask);\r
730                         inp &= CONFIGURABLE_KEYS;\r
731                         for (i = 0; i < 32; i++)\r
732                                 if (inp & (1 << i)) {\r
733                                         if (count_bound_keys(opts[sel].mask, 0) >= 2)\r
734                                              Settings.KeyBinds[i] &= ~opts[sel].mask; // allow to unbind only\r
735                                         else Settings.KeyBinds[i] ^=  opts[sel].mask;\r
736                                         Settings.KeyBinds[i] &= ~(3 << 16);\r
737                                         Settings.KeyBinds[i] |= player_idx << 16;\r
738                                 }\r
739                 } else if (sel < opt_cnt) {\r
740                         for (i = 0; i < 32; i++)\r
741                                 if (inp & (1 << i)) {\r
742                                         if (count_bound_keys(opts[sel].mask, 1) >= 1) // disallow combos for usbjoy\r
743                                              Settings.JoyBinds[joy-1][i] &= ~opts[sel].mask;\r
744                                         else Settings.JoyBinds[joy-1][i] ^=  opts[sel].mask;\r
745                                         Settings.JoyBinds[joy-1][i] &= ~(3 << 16);\r
746                                         Settings.JoyBinds[joy-1][i] |= player_idx << 16;\r
747                                 }\r
748                 }\r
749         }\r
750 }\r
751 \r
752 static void draw_kc_sel(int menu_sel)\r
753 {\r
754         int tl_x = 25+40, tl_y = 60, y, i;\r
755         char joyname[36];\r
756 \r
757         y = tl_y;\r
758         gp2x_fceu_copy_bg();\r
759         gp2x_text_out15(tl_x, y,       "Player 1");\r
760         gp2x_text_out15(tl_x, (y+=10), "Player 2");\r
761         gp2x_text_out15(tl_x, (y+=10), "Emulator controls");\r
762         gp2x_text_out15(tl_x, (y+=10), "Done");\r
763 \r
764         // draw cursor\r
765         gp2x_text_out15(tl_x - 16, tl_y + menu_sel*10, ">");\r
766 \r
767         tl_x = 25;\r
768         gp2x_text_out15(tl_x, (y=110), "USB joys detected:");\r
769         if (num_of_joys > 0) {\r
770                 for (i = 0; i < num_of_joys; i++) {\r
771                         strncpy(joyname, joy_name(joys[i]), 33); joyname[33] = 0;\r
772                         gp2x_text_out15(tl_x, (y+=10), "%i: %s", i+1, joyname);\r
773                 }\r
774         } else {\r
775                 gp2x_text_out15(tl_x, (y+=10), "none");\r
776         }\r
777 \r
778         gp2x_video_flip();\r
779 }\r
780 \r
781 static void kc_sel_loop(void)\r
782 {\r
783         int menu_sel = 3, menu_sel_max = 3;\r
784         unsigned long inp = 0;\r
785 \r
786         for(;;)\r
787         {\r
788                 draw_kc_sel(menu_sel);\r
789                 inp = wait_for_input(GP2X_UP|GP2X_DOWN|GP2X_B|GP2X_X);\r
790                 if(inp & GP2X_UP  ) { menu_sel--; if (menu_sel < 0) menu_sel = menu_sel_max; }\r
791                 if(inp & GP2X_DOWN) { menu_sel++; if (menu_sel > menu_sel_max) menu_sel = 0; }\r
792                 if(inp & GP2X_B) {\r
793                         switch (menu_sel) {\r
794                                 case 0: key_config_loop(ctrl_actions, 10, 0); return;\r
795                                 case 1: key_config_loop(ctrl_actions,  8, 1); return;\r
796                                 default: return;\r
797                         }\r
798                 }\r
799                 if(inp & GP2X_X) return;\r
800         }\r
801 }\r
802 \r
803 \r
804 \r
805 // --------- advanced options ----------\r
806 #if 0\r
807 static void draw_amenu_options(int menu_sel)\r
808 {\r
809         int tl_x = 25, tl_y = 60, y;\r
810         char *mms = mmuhack_status ? "active)  " : "inactive)";\r
811 \r
812         y = tl_y;\r
813         //memset(gp2x_screen, 0, 320*240);\r
814         gp2x_fceu_copy_bg();\r
815 \r
816         gp2x_text_out15(tl_x, y,       "Gamma correction           %i.%02i", currentConfig.gamma / 100, currentConfig.gamma%100); // 0\r
817         gp2x_text_out15(tl_x, (y+=10), "Don't save last used ROM   %s", (currentConfig.EmuOpt &0x020)?"ON":"OFF"); // 5\r
818         gp2x_text_out15(tl_x, (y+=10), "needs restart:");\r
819         gp2x_text_out15(tl_x, (y+=10), "craigix's RAM timings      %s", (currentConfig.EmuOpt &0x100)?"ON":"OFF"); // 7\r
820         gp2x_text_out15(tl_x, (y+=10), "squidgehack (now %s %s",   mms, (currentConfig.EmuOpt &0x010)?"ON":"OFF"); // 8\r
821         gp2x_text_out15(tl_x, (y+=10), "Done");\r
822 \r
823         // draw cursor\r
824         gp2x_text_out15(tl_x - 16, tl_y + menu_sel*10, ">");\r
825 \r
826         gp2x_video_flip();\r
827 }\r
828 \r
829 static void amenu_loop_options(void)\r
830 {\r
831         int menu_sel = 0, menu_sel_max = 9;\r
832         unsigned long inp = 0;\r
833 \r
834         for(;;)\r
835         {\r
836                 draw_amenu_options(menu_sel);\r
837                 inp = wait_for_input(GP2X_UP|GP2X_DOWN|GP2X_LEFT|GP2X_RIGHT|GP2X_B|GP2X_X|GP2X_A);\r
838                 if(inp & GP2X_UP  ) { menu_sel--; if (menu_sel < 0) menu_sel = menu_sel_max; }\r
839                 if(inp & GP2X_DOWN) { menu_sel++; if (menu_sel > menu_sel_max) menu_sel = 0; }\r
840                 if((inp& GP2X_B)||(inp&GP2X_LEFT)||(inp&GP2X_RIGHT)) { // toggleable options\r
841                         switch (menu_sel) {\r
842                                 case  1: break;\r
843                                 case  9: return;\r
844                         }\r
845                 }\r
846                 if(inp & (GP2X_X|GP2X_A)) return;\r
847                 if(inp & (GP2X_LEFT|GP2X_RIGHT)) { // multi choise\r
848                         switch (menu_sel) {\r
849                                 case 0:\r
850                                         while ((inp = gp2x_joystick_read(1)) & (GP2X_LEFT|GP2X_RIGHT)) {\r
851                                                 currentConfig.gamma += (inp & GP2X_LEFT) ? -1 : 1;\r
852                                                 if (currentConfig.gamma <   1) currentConfig.gamma =   1;\r
853                                                 if (currentConfig.gamma > 300) currentConfig.gamma = 300;\r
854                                                 draw_amenu_options(menu_sel);\r
855                                                 usleep(18*1000);\r
856                                         }\r
857                                         break;\r
858                         }\r
859                 }\r
860         }\r
861 }\r
862 #endif\r
863 \r
864 // -------------- options --------------\r
865 \r
866 static void draw_menu_options(int menu_sel)\r
867 {\r
868         int tl_x = 25, tl_y = 32, y;\r
869         char /*monostereo[8],*/ strframeskip[8], *strscaling, *strssconfirm;\r
870 \r
871         //strcpy(monostereo, (currentConfig.PicoOpt&0x08)?"stereo":"mono");\r
872         if (Settings.frameskip < 0)\r
873              strcpy(strframeskip, "Auto");\r
874         else sprintf(strframeskip, "%i", Settings.frameskip);\r
875         switch (Settings.scaling) {\r
876                 default: strscaling = "            OFF";   break;\r
877                 case 1:  strscaling = "hw horizontal";     break;\r
878                 case 2:  strscaling = "hw horiz. + vert."; break;\r
879                 case 3:  strscaling = "sw horizontal";     break;\r
880         }\r
881         switch (Settings.sstate_confirm) {\r
882                 default: strssconfirm = "OFF";    break;\r
883                 case 1:  strssconfirm = "writes"; break;\r
884                 case 2:  strssconfirm = "loads";  break;\r
885                 case 3:  strssconfirm = "both";   break;\r
886         }\r
887 \r
888         y = tl_y;\r
889         //memset(gp2x_screen, 0, 320*240);\r
890         gp2x_fceu_copy_bg();\r
891 \r
892         gp2x_text_out15(tl_x,  y,      "Scaling:       %s", strscaling);                                // 0\r
893         gp2x_text_out15(tl_x, (y+=10), "Show FPS                   %s", Settings.showfps?"ON":"OFF");   // 1\r
894         gp2x_text_out15(tl_x, (y+=10), "Frameskip                  %s", strframeskip);                  // 2\r
895         gp2x_text_out15(tl_x, (y+=10), "Enable sound               %s", /*(currentConfig.EmuOpt &0x004)?"ON":*/"OFF"); // 3\r
896         gp2x_text_out15(tl_x, (y+=10), "Sound Quality:     %5iHz %s",   0, "" /*currentConfig.PsndRate, monostereo*/); // 4\r
897         gp2x_text_out15(tl_x, (y+=10), "Region:                    %s",\r
898                 Settings.region_force == 2 ? "NTSC" : Settings.region_force == 1 ? "PAL" : "OFF");      // 5\r
899         gp2x_text_out15(tl_x, (y+=10), "Use SRAM savestates        %s", "OFF");\r
900         gp2x_text_out15(tl_x, (y+=10), "Confirm savestate          %s", strssconfirm);\r
901         gp2x_text_out15(tl_x, (y+=10), "Save slot                  %i", 0/*state_slot*/);               // 8\r
902         gp2x_text_out15(tl_x, (y+=10), "GP2X CPU clock             %iMhz", Settings.cpuclock);\r
903         gp2x_text_out15(tl_x, (y+=10), "[advanced options]");                                           // 10\r
904         gp2x_text_out15(tl_x, (y+=10), "Save cfg as default");\r
905         if (fceugi)\r
906                 gp2x_text_out15(tl_x, (y+=10), "Save cfg for current game only");\r
907 \r
908         // draw cursor\r
909         gp2x_text_out15(tl_x - 16, tl_y + menu_sel*10, ">");\r
910 \r
911         gp2x_video_flip();\r
912 }\r
913 \r
914 /*\r
915 static int sndrate_prevnext(int rate, int dir)\r
916 {\r
917         int i, rates[] = { 8000, 11025, 16000, 22050, 44100 };\r
918 \r
919         for (i = 0; i < 5; i++)\r
920                 if (rates[i] == rate) break;\r
921 \r
922         i += dir ? 1 : -1;\r
923         if (i > 4) return dir ? 44100 : 22050;\r
924         if (i < 0) return dir ? 11025 : 8000;\r
925         return rates[i];\r
926 }\r
927 */\r
928 static void int_incdec(int *p, int inc, int min, int max)\r
929 {\r
930         *p += inc;\r
931         if      (*p < min) *p = min;\r
932         else if (*p > max) *p = max;\r
933 }\r
934 \r
935 static int menu_loop_options(void)\r
936 {\r
937         int menu_sel = 0, menu_sel_max = 11;\r
938         unsigned long inp = 0;\r
939 \r
940         if (fceugi) menu_sel_max++;\r
941 \r
942         for(;;)\r
943         {\r
944                 draw_menu_options(menu_sel);\r
945                 inp = wait_for_input(GP2X_UP|GP2X_DOWN|GP2X_LEFT|GP2X_RIGHT|GP2X_B|GP2X_X|GP2X_A);\r
946                 if(inp & GP2X_UP  ) { menu_sel--; if (menu_sel < 0) menu_sel = menu_sel_max; }\r
947                 if(inp & GP2X_DOWN) { menu_sel++; if (menu_sel > menu_sel_max) menu_sel = 0; }\r
948                 if((inp& GP2X_B)||(inp&GP2X_LEFT)||(inp&GP2X_RIGHT)) { // toggleable options\r
949                         switch (menu_sel) {\r
950                                 case  1: Settings.showfps = !Settings.showfps; break;\r
951                                 //case  6: Settings. = !Settings.showfps; break;\r
952                                 //case 10: amenu_loop_options();    break;\r
953                                 case 11: // done (update and write)\r
954                                         gp2x_cpuclock_update();\r
955                                         SaveConfig(NULL);\r
956                                         return 1;\r
957                                 case 12: // done (update and write for current game)\r
958                                         gp2x_cpuclock_update();\r
959                                         if (lastLoadedGameName[0])\r
960                                                 SaveConfig(lastLoadedGameName);\r
961                                         return 1;\r
962                         }\r
963                 }\r
964                 if(inp & (GP2X_X|GP2X_A)) {\r
965                         gp2x_cpuclock_update();\r
966                         return 0;  // done (update, no write)\r
967                 }\r
968                 if(inp & (GP2X_LEFT|GP2X_RIGHT)) { // multi choise\r
969                         switch (menu_sel) {\r
970                                 case  0: int_incdec(&Settings.scaling,   (inp & GP2X_LEFT) ? -1 : 1,  0,  3); break;\r
971                                 case  2: int_incdec(&Settings.frameskip, (inp & GP2X_LEFT) ? -1 : 1, -1, 32); break;\r
972 /*\r
973                                 case  4:\r
974                                         if ((inp & GP2X_RIGHT) && currentConfig.PsndRate == 44100 && !(currentConfig.PicoOpt&0x08)) {\r
975                                                 currentConfig.PsndRate = 8000;  currentConfig.PicoOpt|= 0x08;\r
976                                         } else if ((inp & GP2X_LEFT) && currentConfig.PsndRate == 8000 && (currentConfig.PicoOpt&0x08)) {\r
977                                                 currentConfig.PsndRate = 44100; currentConfig.PicoOpt&=~0x08;\r
978                                         } else currentConfig.PsndRate = sndrate_prevnext(currentConfig.PsndRate, inp & GP2X_RIGHT);\r
979                                         break;\r
980 */\r
981                                 case  5: int_incdec(&Settings.region_force,   (inp & GP2X_LEFT) ? -1 : 1, 0, 2); break;\r
982                                 case  7: int_incdec(&Settings.sstate_confirm, (inp & GP2X_LEFT) ? -1 : 1, 0, 3); break;\r
983                                 case  8: int_incdec(&CurrentState,            (inp & GP2X_LEFT) ? -1 : 1, 0, 9); break;\r
984                                 case  9:\r
985                                         while ((inp = gp2x_joystick_read(1)) & (GP2X_LEFT|GP2X_RIGHT)) {\r
986                                                 Settings.cpuclock += (inp & GP2X_LEFT) ? -1 : 1;\r
987                                                 if (Settings.cpuclock < 0) Settings.cpuclock = 0; // 0 ~ do not change\r
988                                                 draw_menu_options(menu_sel);\r
989                                                 usleep(50*1000);\r
990                                         }\r
991                                         break;\r
992                         }\r
993                 }\r
994         }\r
995 }\r
996 \r
997 // -------------- credits --------------\r
998 \r
999 static void draw_menu_credits(void)\r
1000 {\r
1001         //int tl_x = 15, tl_y = 70;\r
1002         gp2x_fceu_copy_bg();\r
1003 \r
1004         // TODO\r
1005 \r
1006         gp2x_video_flip();\r
1007 }\r
1008 \r
1009 \r
1010 // -------------- root menu --------------\r
1011 \r
1012 static void draw_menu_root(int menu_sel)\r
1013 {\r
1014         int tl_x = 30, tl_y = 128, y;\r
1015         gp2x_fceu_copy_bg();\r
1016 \r
1017         y = tl_y;\r
1018         if (fceugi) {\r
1019                 gp2x_text_out15(tl_x, y,       "Resume game");\r
1020                 gp2x_text_out15(tl_x, (y+=10), "Save State");\r
1021                 gp2x_text_out15(tl_x, (y+=10), "Load State");\r
1022                 gp2x_text_out15(tl_x, (y+=10), "Reset game");\r
1023         } else {\r
1024                 y += 30;\r
1025         }\r
1026         gp2x_text_out15(tl_x, (y+=10), "Load new ROM");\r
1027         gp2x_text_out15(tl_x, (y+=10), "Options");\r
1028         gp2x_text_out15(tl_x, (y+=10), "Controls");\r
1029         gp2x_text_out15(tl_x, (y+=10), "Credits");\r
1030         gp2x_text_out15(tl_x, (y+=10), "Exit");\r
1031 // TODO\r
1032 //      if (PicoPatches)\r
1033 //              gp2x_text_out15(tl_x, (y+=10), "Patches / GameGenie");\r
1034 \r
1035         // draw cursor\r
1036         gp2x_text_out15(tl_x - 16, tl_y + menu_sel*10, ">");\r
1037         // error\r
1038         if (menuErrorMsg[0]) gp2x_text_out15(1, 230, menuErrorMsg);\r
1039         else {\r
1040                 char vstr[16];\r
1041                 sprintf(vstr, "v" GP2X_PORT_VERSION " r%i", GP2X_PORT_REV);\r
1042                 gp2x_text_out15(320-strlen(vstr)*8-1, 230, vstr);\r
1043         }\r
1044         gp2x_video_flip();\r
1045 }\r
1046 \r
1047 \r
1048 static int menu_loop_root(void)\r
1049 {\r
1050         int ret, menu_sel_max = 8, menu_sel_min = 4;\r
1051         static int menu_sel = 4;\r
1052         unsigned long inp = 0;\r
1053         char curr_path[PATH_MAX], *selfname;\r
1054         FILE *tstf;\r
1055 \r
1056         if ( (tstf = fopen(lastLoadedGameName, "rb")) )\r
1057         {\r
1058                 fclose(tstf);\r
1059                 strncpy(curr_path, lastLoadedGameName, sizeof(curr_path));\r
1060                 curr_path[sizeof(curr_path)-1] = 0;\r
1061         }\r
1062         else\r
1063         {\r
1064                 getcwd(curr_path, PATH_MAX);\r
1065         }\r
1066 \r
1067         if (fceugi) menu_sel_min = 0;\r
1068 // TODO if (PicoPatches) menu_sel_max = 9;\r
1069         if (menu_sel < menu_sel_min || menu_sel > menu_sel_max)\r
1070                 menu_sel = menu_sel_min;\r
1071 \r
1072         /* make sure action buttons are not pressed on entering menu */\r
1073         draw_menu_root(menu_sel);\r
1074         while (gp2x_joystick_read(1) & (GP2X_B|GP2X_X|GP2X_SELECT)) usleep(50*1000);\r
1075 \r
1076         for (;;)\r
1077         {\r
1078                 draw_menu_root(menu_sel);\r
1079                 inp = wait_for_input(GP2X_UP|GP2X_DOWN|GP2X_B|GP2X_X);\r
1080                 if(inp & GP2X_UP  )  { menu_sel--; if (menu_sel < menu_sel_min) menu_sel = menu_sel_max; }\r
1081                 if(inp & GP2X_DOWN)  { menu_sel++; if (menu_sel > menu_sel_max) menu_sel = menu_sel_min; }\r
1082                 if(inp &(GP2X_SELECT|GP2X_X)){\r
1083                         if (fceugi) {\r
1084                                 while (gp2x_joystick_read(1) & GP2X_X) usleep(50*1000); // wait until X is released\r
1085                                 Exit = 0;\r
1086                                 return 0;\r
1087                         }\r
1088                 }\r
1089                 if(inp & GP2X_B   )  {\r
1090                         switch (menu_sel) {\r
1091                                 case 0: // resume game\r
1092                                         if (fceugi) {\r
1093                                                 while (gp2x_joystick_read(1) & GP2X_B) usleep(50*1000);\r
1094                                                 Exit = 0;\r
1095                                                 return 0;\r
1096                                         }\r
1097                                         break;\r
1098                                 case 1: // save state\r
1099                                         if (fceugi) {\r
1100                                                 /*if(savestate_menu_loop(0))\r
1101                                                         continue;*/\r
1102                                                 FCEUI_SaveState();\r
1103                                                 return 0;\r
1104                                         }\r
1105                                         break;\r
1106                                 case 2: // load state\r
1107                                         if (fceugi) {\r
1108                                                 /*if(savestate_menu_loop(1))\r
1109                                                         continue;*/\r
1110                                                 FCEUI_LoadState();\r
1111                                                 return 0;\r
1112                                         }\r
1113                                         break;\r
1114                                 case 3: // reset game\r
1115                                         if (fceugi) {\r
1116                                                 FCEU_DoSimpleCommand(FCEUNPCMD_RESET);\r
1117                                                 Exit = 0;\r
1118                                                 return 0;\r
1119                                         }\r
1120                                         break;\r
1121                                 case 4: // select rom\r
1122                                         selfname = romsel_loop(curr_path);\r
1123                                         if (selfname) {\r
1124                                                 printf("selected file: %s\n", selfname);\r
1125                                                 return 2;\r
1126                                         }\r
1127                                         break;\r
1128                                 case 5: // options\r
1129                                         ret = menu_loop_options();\r
1130                                         if (ret == 1) continue; // status update\r
1131                                         break;\r
1132                                 case 6: // controls\r
1133                                         kc_sel_loop();\r
1134                                         break;\r
1135                                 case 7: // credits\r
1136                                         draw_menu_credits();\r
1137                                         usleep(500*1000);\r
1138                                         inp = wait_for_input(GP2X_B|GP2X_X);\r
1139                                         break;\r
1140                                 case 8: // exit\r
1141                                         return 1;\r
1142                                 case 9: // patches/gg\r
1143                                         break;\r
1144                         }\r
1145                 }\r
1146                 menuErrorMsg[0] = 0; // clear error msg\r
1147         }\r
1148 }\r
1149 \r
1150 \r
1151 static void menu_prepare_bg(void)\r
1152 {\r
1153         // TODO...\r
1154 }\r
1155 \r
1156 static void menu_gfx_prepare(void)\r
1157 {\r
1158         menu_prepare_bg();\r
1159 \r
1160         // switch bpp\r
1161         gp2x_video_changemode(16);\r
1162         gp2x_video_RGB_setscaling(0, 320, 240);\r
1163         gp2x_video_flip();\r
1164 }\r
1165 \r
1166 \r
1167 int gp2x_menu_do(void)\r
1168 {\r
1169         int ret;\r
1170 \r
1171         menu_gfx_prepare();\r
1172 \r
1173         ret = menu_loop_root();\r
1174 \r
1175         menuErrorMsg[0] = 0;\r
1176 \r
1177         return ret;\r
1178 }\r
1179 \r
1180 \r