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