1.40a fixes
[picodrive.git] / platform / gp2x / menu.c
CommitLineData
d524c827 1// (c) Copyright 2006,2007 notaz, All rights reserved.\r
cc68a136 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 "gp2x.h"\r
14#include "emu.h"\r
15#include "menu.h"\r
16#include "usbjoy.h"\r
ea8c405f 17#include "../common/emu.h"\r
e5f426aa 18#include "../common/menu.h"\r
ea8c405f 19#include "../common/arm_utils.h"\r
e5f426aa 20#include "../common/readpng.h"\r
cc68a136 21#include "version.h"\r
22\r
b67ef287 23#include <Pico/PicoInt.h>\r
24#include <Pico/Patch.h>\r
25#include <zlib/zlib.h>\r
cc68a136 26\r
27#ifndef _DIRENT_HAVE_D_TYPE\r
28#error "need d_type for file browser\r
29#endif\r
30\r
cc68a136 31extern int mmuhack_status;\r
cc68a136 32\r
1ca2ea4f 33const char *keyNames[] = {\r
cc68a136 34 "UP", "???", "LEFT", "???", "DOWN", "???", "RIGHT", "???",\r
35 "START", "SELECT", "L", "R", "A", "B", "X", "Y",\r
36 "???", "???", "???", "???", "???", "???", "VOL DOWN", "VOL UP",\r
37 "???", "???", "???", "PUSH", "???", "???", "???", "???"\r
38};\r
39\r
a4f0cc86 40static void menu_darken_bg(void *dst, int pixels, int darker);\r
a12e0116 41static void menu_prepare_bg(int use_game_bg);\r
cc68a136 42\r
cc68a136 43static unsigned long inp_prev = 0;\r
44static int inp_prevjoy = 0;\r
45\r
46static unsigned long wait_for_input(unsigned long interesting)\r
47{\r
48 unsigned long ret;\r
721cd396 49 static int repeats = 0, wait = 50*1000;\r
cc68a136 50 int release = 0, i;\r
51\r
721cd396 52 if (repeats == 2 || repeats == 4) wait /= 2;\r
53 if (repeats == 6) wait = 15 * 1000;\r
cc68a136 54\r
55 for (i = 0; i < 6 && inp_prev == gp2x_joystick_read(1); i++) {\r
721cd396 56 if (i == 0) repeats++;\r
57 if (wait >= 30*1000) usleep(wait); // usleep sleeps for ~30ms minimum\r
58 else spend_cycles(wait * currentConfig.CPUclock);\r
cc68a136 59 }\r
60\r
61 while ( !((ret = gp2x_joystick_read(1)) & interesting) ) {\r
62 usleep(50000);\r
63 release = 1;\r
64 }\r
65\r
66 if (release || ret != inp_prev) {\r
67 repeats = 0;\r
721cd396 68 wait = 50*1000;\r
cc68a136 69 }\r
70 inp_prev = ret;\r
71 inp_prevjoy = 0;\r
72\r
73 // we don't need diagonals in menus\r
74 if ((ret&GP2X_UP) && (ret&GP2X_LEFT)) ret &= ~GP2X_LEFT;\r
75 if ((ret&GP2X_UP) && (ret&GP2X_RIGHT)) ret &= ~GP2X_RIGHT;\r
76 if ((ret&GP2X_DOWN) && (ret&GP2X_LEFT)) ret &= ~GP2X_LEFT;\r
77 if ((ret&GP2X_DOWN) && (ret&GP2X_RIGHT)) ret &= ~GP2X_RIGHT;\r
78\r
79 return ret;\r
80}\r
81\r
82static unsigned long input2_read(unsigned long interesting, int *joy)\r
83{\r
84 unsigned long ret;\r
85 int i;\r
86\r
87 do\r
88 {\r
89 *joy = 0;\r
90 if ((ret = gp2x_joystick_read(0) & interesting)) break;\r
91 gp2x_usbjoy_update();\r
92 for (i = 0; i < num_of_joys; i++) {\r
93 ret = gp2x_usbjoy_check2(i);\r
94 if (ret) { *joy = i + 1; break; }\r
95 }\r
96 if (ret) break;\r
97 }\r
98 while(0);\r
99\r
100 return ret;\r
101}\r
102\r
103// similar to wait_for_input(), but returns joy num\r
104static unsigned long wait_for_input_usbjoy(unsigned long interesting, int *joy)\r
105{\r
106 unsigned long ret;\r
107 const int wait = 300*1000;\r
108 int i;\r
109\r
110 if (inp_prevjoy == 0) inp_prev &= interesting;\r
111 for (i = 0; i < 6; i++) {\r
112 ret = input2_read(interesting, joy);\r
113 if (*joy != inp_prevjoy || ret != inp_prev) break;\r
114 usleep(wait/6);\r
115 }\r
116\r
117 while ( !(ret = input2_read(interesting, joy)) ) {\r
118 usleep(50000);\r
119 }\r
120\r
121 inp_prev = ret;\r
122 inp_prevjoy = *joy;\r
123\r
1ca2ea4f 124 // handle only 1 event at a time\r
125 for (i = 1; i != 0; i <<= 1)\r
126 if (ret & i) { ret &= i; break; }\r
a39b7867 127 // ... but allow select\r
128 ret |= inp_prev & GP2X_SELECT;\r
1ca2ea4f 129\r
cc68a136 130 return ret;\r
131}\r
132\r
e5f426aa 133static void menu_flip(void)\r
134{\r
135 gp2x_video_flush_cache();\r
136 gp2x_video_flip2();\r
137}\r
cc68a136 138\r
139\r
a9b3ffd3 140// --------- loading ROM screen ----------\r
141\r
ea08c296 142static int cdload_called = 0;\r
143\r
a9b3ffd3 144static void load_progress_cb(int percent)\r
145{\r
146 int ln, len = percent * 320 / 100;\r
a12e0116 147 unsigned short *dst = (unsigned short *)gp2x_screen + 320*20;\r
a9b3ffd3 148\r
149 if (len > 320) len = 320;\r
ea08c296 150 for (ln = 8; ln > 0; ln--, dst += 320)\r
a12e0116 151 memset(dst, 0xff, len*2);\r
152 menu_flip();\r
a9b3ffd3 153}\r
154\r
ea08c296 155static void cdload_progress_cb(int percent)\r
156{\r
157 int ln, len = percent * 320 / 100;\r
158 unsigned short *dst = (unsigned short *)gp2x_screen + 320*20;\r
159\r
160 memset(dst, 0xff, 320*2*8);\r
161\r
162 smalltext_out16(1, 3*10, "Processing CD image / MP3s", 0xffff);\r
163 smalltext_out16_lim(1, 4*10, romFileName, 0xffff, 80);\r
ca61ee42 164 dst += 320*30;\r
ea08c296 165\r
166 if (len > 320) len = 320;\r
167 for (ln = 8; ln > 0; ln--, dst += 320)\r
168 memset(dst, 0xff, len*2);\r
ca61ee42 169 menu_flip();\r
ea08c296 170 cdload_called = 1;\r
171}\r
172\r
a9b3ffd3 173void menu_romload_prepare(const char *rom_name)\r
174{\r
175 const char *p = rom_name + strlen(rom_name);\r
176 while (p > rom_name && *p != '/') p--;\r
177\r
eacee137 178 if (rom_loaded) gp2x_pd_clone_buffer2();\r
a12e0116 179 else memset(gp2x_screen, 0, 320*240*2);\r
180\r
181 smalltext_out16(1, 1, "Loading", 0xffff);\r
182 smalltext_out16_lim(1, 10, p, 0xffff, 53);\r
183 gp2x_memcpy_buffers(3, gp2x_screen, 0, 320*240*2);\r
184 menu_flip();\r
a9b3ffd3 185 PicoCartLoadProgressCB = load_progress_cb;\r
ea08c296 186 PicoCDLoadProgressCB = cdload_progress_cb;\r
187 cdload_called = 0;\r
a9b3ffd3 188}\r
189\r
190void menu_romload_end(void)\r
191{\r
ca61ee42 192 PicoCartLoadProgressCB = PicoCDLoadProgressCB = NULL;\r
ea08c296 193 smalltext_out16(1, cdload_called ? 60 : 30, "Starting emulation...", 0xffff);\r
a12e0116 194 menu_flip();\r
a9b3ffd3 195}\r
196\r
cc68a136 197// -------------- ROM selector --------------\r
198\r
a12e0116 199// rrrr rggg gggb bbbb\r
200static unsigned short file2color(const char *fname)\r
201{\r
202 const char *ext = fname + strlen(fname) - 3;\r
71de3cd9 203 static const char *rom_exts[] = { "zip", "bin", "smd", "gen", "iso", "cso" };\r
a12e0116 204 static const char *other_exts[] = { "gmv", "pat" };\r
205 int i;\r
206\r
207 if (ext < fname) ext = fname;\r
208 for (i = 0; i < sizeof(rom_exts)/sizeof(rom_exts[0]); i++)\r
209 if (strcasecmp(ext, rom_exts[i]) == 0) return 0xbdff;\r
210 for (i = 0; i < sizeof(other_exts)/sizeof(other_exts[0]); i++)\r
211 if (strcasecmp(ext, other_exts[i]) == 0) return 0xaff5;\r
212 return 0xffff;\r
213}\r
214\r
cc68a136 215static void draw_dirlist(char *curdir, struct dirent **namelist, int n, int sel)\r
216{\r
217 int start, i, pos;\r
218\r
219 start = 12 - sel;\r
220 n--; // exclude current dir (".")\r
221\r
a4f0cc86 222 gp2x_pd_clone_buffer2();\r
223\r
eacee137 224 if (!rom_loaded) {\r
a4f0cc86 225 menu_darken_bg(gp2x_screen, 320*240, 0);\r
a12e0116 226 }\r
227\r
a4f0cc86 228 menu_darken_bg((char *)gp2x_screen + 320*120*2, 320*8, 0);\r
cc68a136 229\r
230 if(start - 2 >= 0)\r
a12e0116 231 smalltext_out16_lim(14, (start - 2)*10, curdir, 0xffff, 53-2);\r
cc68a136 232 for (i = 0; i < n; i++) {\r
233 pos = start + i;\r
234 if (pos < 0) continue;\r
235 if (pos > 23) break;\r
236 if (namelist[i+1]->d_type == DT_DIR) {\r
a12e0116 237 smalltext_out16_lim(14, pos*10, "/", 0xfff6, 1);\r
238 smalltext_out16_lim(14+6, pos*10, namelist[i+1]->d_name, 0xfff6, 53-3);\r
cc68a136 239 } else {\r
a12e0116 240 unsigned short color = file2color(namelist[i+1]->d_name);\r
241 smalltext_out16_lim(14, pos*10, namelist[i+1]->d_name, color, 53-2);\r
cc68a136 242 }\r
243 }\r
a12e0116 244 text_out16(5, 120, ">");\r
245 menu_flip();\r
cc68a136 246}\r
247\r
248static int scandir_cmp(const void *p1, const void *p2)\r
249{\r
250 struct dirent **d1 = (struct dirent **)p1, **d2 = (struct dirent **)p2;\r
251 if ((*d1)->d_type == (*d2)->d_type) return alphasort(d1, d2);\r
252 if ((*d1)->d_type == DT_DIR) return -1; // put before\r
253 if ((*d2)->d_type == DT_DIR) return 1;\r
254 return alphasort(d1, d2);\r
255}\r
256\r
2433f409 257static char *filter_exts[] = {\r
258 ".mp3", ".MP3", ".srm", ".brm", "s.gz", ".mds", "bcfg", ".txt", ".htm", "html",\r
259 ".jpg", ".gpe", ".cue"\r
260};\r
c008977e 261\r
262static int scandir_filter(const struct dirent *ent)\r
263{\r
264 const char *p;\r
265 int i;\r
266\r
267 if (ent == NULL || ent->d_name == NULL) return 0;\r
268 if (strlen(ent->d_name) < 5) return 1;\r
269\r
270 p = ent->d_name + strlen(ent->d_name) - 4;\r
271\r
272 for (i = 0; i < sizeof(filter_exts)/sizeof(filter_exts[0]); i++)\r
273 {\r
274 if (strcmp(p, filter_exts[i]) == 0) return 0;\r
275 }\r
276\r
277 return 1;\r
278}\r
cc68a136 279\r
1b13dae0 280static void do_delete(const char *fpath, const char *fname)\r
281{\r
282 int len, inp;\r
283\r
284 gp2x_pd_clone_buffer2();\r
285\r
286 if (!rom_loaded)\r
287 menu_darken_bg(gp2x_screen, 320*240, 0);\r
288\r
289 len = strlen(fname);\r
290 if (len > 320/6) len = 320/6;\r
291\r
292 text_out16(320/2 - 15*8/2, 80, "About to delete");\r
293 smalltext_out16_lim(320/2 - len*6/2, 95, fname, 0xbdff, len);\r
294 text_out16(320/2 - 13*8/2, 110, "Are you sure?");\r
295 text_out16(320/2 - 25*8/2, 120, "(Y - confirm, X - cancel)");\r
296 menu_flip();\r
297\r
298\r
299 while (gp2x_joystick_read(1) & (GP2X_A|GP2X_SELECT)) usleep(50*1000);\r
300 inp = wait_for_input(GP2X_Y|GP2X_X);\r
301 if (inp & GP2X_Y)\r
302 remove(fpath);\r
303}\r
304\r
cc68a136 305static char *romsel_loop(char *curr_path)\r
306{\r
307 struct dirent **namelist;\r
308 DIR *dir;\r
309 int n, sel = 0;\r
310 unsigned long inp = 0;\r
311 char *ret = NULL, *fname = NULL;\r
312\r
1b13dae0 313rescan:\r
cc68a136 314 // is this a dir or a full path?\r
315 if ((dir = opendir(curr_path))) {\r
316 closedir(dir);\r
317 } else {\r
318 char *p;\r
319 for (p = curr_path + strlen(curr_path) - 1; p > curr_path && *p != '/'; p--);\r
320 *p = 0;\r
321 fname = p+1;\r
322 }\r
323\r
c008977e 324 n = scandir(curr_path, &namelist, scandir_filter, scandir_cmp);\r
cc68a136 325 if (n < 0) {\r
326 // try root\r
c008977e 327 n = scandir("/", &namelist, scandir_filter, scandir_cmp);\r
cc68a136 328 if (n < 0) {\r
329 // oops, we failed\r
330 printf("dir: "); printf(curr_path); printf("\n");\r
331 perror("scandir");\r
332 return NULL;\r
333 }\r
334 }\r
335\r
336 // try to find sel\r
337 if (fname != NULL) {\r
338 int i;\r
339 for (i = 1; i < n; i++) {\r
340 if (strcmp(namelist[i]->d_name, fname) == 0) {\r
341 sel = i - 1;\r
342 break;\r
343 }\r
344 }\r
345 }\r
346\r
347 for (;;)\r
348 {\r
349 draw_dirlist(curr_path, namelist, n, sel);\r
1b13dae0 350 inp = wait_for_input(GP2X_UP|GP2X_DOWN|GP2X_LEFT|GP2X_RIGHT|GP2X_L|GP2X_R|GP2X_A|GP2X_B|GP2X_X|GP2X_SELECT);\r
cc68a136 351 if(inp & GP2X_UP ) { sel--; if (sel < 0) sel = n-2; }\r
352 if(inp & GP2X_DOWN) { sel++; if (sel > n-2) sel = 0; }\r
721cd396 353 if(inp & GP2X_LEFT) { sel-=10; if (sel < 0) sel = 0; }\r
354 if(inp & GP2X_L) { sel-=24; if (sel < 0) sel = 0; }\r
355 if(inp & GP2X_RIGHT) { sel+=10; if (sel > n-2) sel = n-2; }\r
356 if(inp & GP2X_R) { sel+=24; if (sel > n-2) sel = n-2; }\r
1b13dae0 357 if ((inp & GP2X_B) || (inp & (GP2X_SELECT|GP2X_A)) == (GP2X_SELECT|GP2X_A)) // enter dir/select || delete\r
358 {\r
cc68a136 359 again:\r
1b13dae0 360 if (namelist[sel+1]->d_type == DT_REG)\r
361 {\r
cc68a136 362 strcpy(romFileName, curr_path);\r
363 strcat(romFileName, "/");\r
364 strcat(romFileName, namelist[sel+1]->d_name);\r
1b13dae0 365 if (inp & GP2X_B) { // return sel\r
366 ret = romFileName;\r
367 break;\r
368 }\r
369 do_delete(romFileName, namelist[sel+1]->d_name);\r
370 if (n > 0) {\r
371 while (n--) free(namelist[n]);\r
372 free(namelist);\r
373 }\r
374 goto rescan;\r
375 }\r
376 else if (namelist[sel+1]->d_type == DT_DIR)\r
377 {\r
378 int newlen;\r
379 char *p, *newdir;\r
380 if (!(inp & GP2X_B)) continue;\r
381 newlen = strlen(curr_path) + strlen(namelist[sel+1]->d_name) + 2;\r
382 newdir = malloc(newlen);\r
cc68a136 383 if (strcmp(namelist[sel+1]->d_name, "..") == 0) {\r
384 char *start = curr_path;\r
385 p = start + strlen(start) - 1;\r
386 while (*p == '/' && p > start) p--;\r
387 while (*p != '/' && p > start) p--;\r
388 if (p <= start) strcpy(newdir, "/");\r
389 else { strncpy(newdir, start, p-start); newdir[p-start] = 0; }\r
390 } else {\r
391 strcpy(newdir, curr_path);\r
392 p = newdir + strlen(newdir) - 1;\r
393 while (*p == '/' && p >= newdir) *p-- = 0;\r
394 strcat(newdir, "/");\r
395 strcat(newdir, namelist[sel+1]->d_name);\r
396 }\r
397 ret = romsel_loop(newdir);\r
398 free(newdir);\r
399 break;\r
1b13dae0 400 }\r
401 else\r
402 {\r
cc68a136 403 // unknown file type, happens on NTFS mounts. Try to guess.\r
404 FILE *tstf; int tmp;\r
405 strcpy(romFileName, curr_path);\r
406 strcat(romFileName, "/");\r
407 strcat(romFileName, namelist[sel+1]->d_name);\r
408 tstf = fopen(romFileName, "rb");\r
409 if (tstf != NULL)\r
410 {\r
411 if (fread(&tmp, 1, 1, tstf) > 0 || ferror(tstf) == 0)\r
412 namelist[sel+1]->d_type = DT_REG;\r
413 else namelist[sel+1]->d_type = DT_DIR;\r
414 fclose(tstf);\r
415 goto again;\r
416 }\r
417 }\r
418 }\r
419 if(inp & GP2X_X) break; // cancel\r
420 }\r
421\r
422 if (n > 0) {\r
1b13dae0 423 while (n--) free(namelist[n]);\r
cc68a136 424 free(namelist);\r
425 }\r
426\r
427 return ret;\r
428}\r
429\r
0af33fe0 430// ------------ debug menu ------------\r
431\r
432char *debugString(void);\r
433\r
434static void draw_debug(void)\r
435{\r
436 char *p, *str = debugString();\r
437 int len, line;\r
438\r
439 gp2x_pd_clone_buffer2();\r
440\r
441 p = str;\r
442 for (line = 0; line < 24; line++)\r
443 {\r
444 while (*p && *p != '\n') p++;\r
445 len = p - str;\r
446 if (len > 55) len = 55;\r
a12e0116 447 smalltext_out16_lim(1, line*10, str, 0xffff, len);\r
0af33fe0 448 if (*p == 0) break;\r
449 p++; str = p;\r
450 }\r
a12e0116 451 menu_flip();\r
0af33fe0 452}\r
453\r
454static void debug_menu_loop(void)\r
455{\r
456 draw_debug();\r
457 wait_for_input(GP2X_B|GP2X_X);\r
458}\r
459\r
b67ef287 460// ------------ patch/gg menu ------------\r
461\r
462static void draw_patchlist(int sel)\r
463{\r
a12e0116 464 int start, i, pos, active;\r
b67ef287 465\r
466 start = 12 - sel;\r
467\r
468 gp2x_pd_clone_buffer2();\r
469\r
470 for (i = 0; i < PicoPatchCount; i++) {\r
471 pos = start + i;\r
472 if (pos < 0) continue;\r
473 if (pos > 23) break;\r
a12e0116 474 active = PicoPatches[i].active;\r
475 smalltext_out16_lim(14, pos*10, active ? "ON " : "OFF", active ? 0xfff6 : 0xffff, 3);\r
476 smalltext_out16_lim(14+6*4, pos*10, PicoPatches[i].name, active ? 0xfff6 : 0xffff, 53-6);\r
b67ef287 477 }\r
478 pos = start + i;\r
a12e0116 479 if (pos < 24) smalltext_out16_lim(14, pos*10, "done", 0xffff, 4);\r
b67ef287 480\r
a12e0116 481 text_out16(5, 120, ">");\r
482 menu_flip();\r
b67ef287 483}\r
484\r
485\r
0af33fe0 486static void patches_menu_loop(void)\r
b67ef287 487{\r
488 int menu_sel = 0;\r
489 unsigned long inp = 0;\r
490\r
491 for(;;)\r
492 {\r
493 draw_patchlist(menu_sel);\r
494 inp = wait_for_input(GP2X_UP|GP2X_DOWN|GP2X_LEFT|GP2X_RIGHT|GP2X_L|GP2X_R|GP2X_B|GP2X_X);\r
495 if(inp & GP2X_UP ) { menu_sel--; if (menu_sel < 0) menu_sel = PicoPatchCount; }\r
496 if(inp & GP2X_DOWN) { menu_sel++; if (menu_sel > PicoPatchCount) menu_sel = 0; }\r
497 if(inp &(GP2X_LEFT|GP2X_L)) { menu_sel-=10; if (menu_sel < 0) menu_sel = 0; }\r
498 if(inp &(GP2X_RIGHT|GP2X_R)) { menu_sel+=10; if (menu_sel > PicoPatchCount) menu_sel = PicoPatchCount; }\r
499 if(inp & GP2X_B) { // action\r
500 if (menu_sel < PicoPatchCount)\r
501 PicoPatches[menu_sel].active = !PicoPatches[menu_sel].active;\r
502 else return;\r
503 }\r
504 if(inp & GP2X_X) return;\r
505 }\r
506\r
507}\r
508\r
860c6322 509// ------------ savestate loader ------------\r
510\r
860c6322 511static int state_slot_flags = 0;\r
512\r
513static void state_check_slots(void)\r
514{\r
515 int slot;\r
516\r
517 state_slot_flags = 0;\r
518\r
519 for (slot = 0; slot < 10; slot++)\r
520 {\r
ea8c405f 521 if (emu_checkSaveFile(slot))\r
860c6322 522 {\r
523 state_slot_flags |= 1 << slot;\r
524 }\r
525 }\r
526}\r
527\r
528static void draw_savestate_bg(int slot)\r
529{\r
530 struct PicoVideo tmp_pv;\r
531 unsigned short tmp_cram[0x40];\r
532 unsigned short tmp_vsram[0x40];\r
533 void *tmp_vram, *file;\r
534 char *fname;\r
535\r
536 fname = emu_GetSaveFName(1, 0, slot);\r
537 if (!fname) return;\r
538\r
539 tmp_vram = malloc(sizeof(Pico.vram));\r
540 if (tmp_vram == NULL) return;\r
541\r
542 memcpy(tmp_vram, Pico.vram, sizeof(Pico.vram));\r
543 memcpy(tmp_cram, Pico.cram, sizeof(Pico.cram));\r
544 memcpy(tmp_vsram, Pico.vsram, sizeof(Pico.vsram));\r
545 memcpy(&tmp_pv, &Pico.video, sizeof(Pico.video));\r
546\r
547 if (strcmp(fname + strlen(fname) - 3, ".gz") == 0) {\r
548 file = gzopen(fname, "rb");\r
ea8c405f 549 emu_setSaveStateCbs(1);\r
860c6322 550 } else {\r
551 file = fopen(fname, "rb");\r
ea8c405f 552 emu_setSaveStateCbs(0);\r
860c6322 553 }\r
554\r
555 if (file) {\r
556 if (PicoMCD & 1) {\r
557 PicoCdLoadStateGfx(file);\r
558 } else {\r
559 areaSeek(file, 0x10020, SEEK_SET); // skip header and RAM in state file\r
560 areaRead(Pico.vram, 1, sizeof(Pico.vram), file);\r
561 areaSeek(file, 0x2000, SEEK_CUR);\r
562 areaRead(Pico.cram, 1, sizeof(Pico.cram), file);\r
563 areaRead(Pico.vsram, 1, sizeof(Pico.vsram), file);\r
564 areaSeek(file, 0x221a0, SEEK_SET);\r
565 areaRead(&Pico.video, 1, sizeof(Pico.video), file);\r
566 }\r
567 areaClose(file);\r
568 }\r
569\r
ea8c405f 570 emu_forcedFrame();\r
a12e0116 571 menu_prepare_bg(1);\r
860c6322 572\r
573 memcpy(Pico.vram, tmp_vram, sizeof(Pico.vram));\r
574 memcpy(Pico.cram, tmp_cram, sizeof(Pico.cram));\r
575 memcpy(Pico.vsram, tmp_vsram, sizeof(Pico.vsram));\r
576 memcpy(&Pico.video, &tmp_pv, sizeof(Pico.video));\r
577 free(tmp_vram);\r
578}\r
579\r
580static void draw_savestate_menu(int menu_sel, int is_loading)\r
581{\r
582 int tl_x = 25, tl_y = 60, y, i;\r
583\r
584 if (state_slot_flags & (1 << menu_sel))\r
585 draw_savestate_bg(menu_sel);\r
586 gp2x_pd_clone_buffer2();\r
587\r
a12e0116 588 text_out16(tl_x, 30, is_loading ? "Load state" : "Save state");\r
589\r
e5f426aa 590 menu_draw_selection(tl_x - 16, tl_y + menu_sel*10, 108);\r
860c6322 591\r
592 /* draw all 10 slots */\r
593 y = tl_y;\r
594 for (i = 0; i < 10; i++, y+=10)\r
595 {\r
a12e0116 596 text_out16(tl_x, y, "SLOT %i (%s)", i, (state_slot_flags & (1 << i)) ? "USED" : "free");\r
860c6322 597 }\r
a12e0116 598 text_out16(tl_x, y, "back");\r
860c6322 599\r
a12e0116 600 menu_flip();\r
860c6322 601}\r
602\r
603static int savestate_menu_loop(int is_loading)\r
604{\r
aae35e84 605 static int menu_sel = 10;\r
606 int menu_sel_max = 10;\r
860c6322 607 unsigned long inp = 0;\r
608\r
609 state_check_slots();\r
610\r
611 for(;;)\r
612 {\r
613 draw_savestate_menu(menu_sel, is_loading);\r
614 inp = wait_for_input(GP2X_UP|GP2X_DOWN|GP2X_B|GP2X_X);\r
615 if(inp & GP2X_UP ) {\r
616 do {\r
617 menu_sel--; if (menu_sel < 0) menu_sel = menu_sel_max;\r
618 } while (!(state_slot_flags & (1 << menu_sel)) && menu_sel != menu_sel_max && is_loading);\r
619 }\r
620 if(inp & GP2X_DOWN) {\r
621 do {\r
622 menu_sel++; if (menu_sel > menu_sel_max) menu_sel = 0;\r
623 } while (!(state_slot_flags & (1 << menu_sel)) && menu_sel != menu_sel_max && is_loading);\r
624 }\r
625 if(inp & GP2X_B) { // save/load\r
626 if (menu_sel < 10) {\r
627 state_slot = menu_sel;\r
628 if (emu_SaveLoadGame(is_loading, 0)) {\r
629 strcpy(menuErrorMsg, is_loading ? "Load failed" : "Save failed");\r
630 return 1;\r
631 }\r
632 return 0;\r
633 } else return 1;\r
634 }\r
635 if(inp & GP2X_X) return 1;\r
636 }\r
637}\r
638\r
cc68a136 639// -------------- key config --------------\r
640\r
641static char *usb_joy_key_name(int joy, int num)\r
642{\r
643 static char name[16];\r
644 switch (num)\r
645 {\r
646 case 0: sprintf(name, "Joy%i UP", joy); break;\r
647 case 1: sprintf(name, "Joy%i DOWN", joy); break;\r
648 case 2: sprintf(name, "Joy%i LEFT", joy); break;\r
649 case 3: sprintf(name, "Joy%i RIGHT", joy); break;\r
650 default:sprintf(name, "Joy%i b%i", joy, num-3); break;\r
651 }\r
652 return name;\r
653}\r
654\r
d524c827 655static char *action_binds(int player_idx, int action_mask)\r
cc68a136 656{\r
d524c827 657 static char strkeys[32*5];\r
cc68a136 658 int joy, i;\r
659\r
660 strkeys[0] = 0;\r
d524c827 661 for (i = 0; i < 32; i++) // i is key index\r
cc68a136 662 {\r
d524c827 663 if (currentConfig.KeyBinds[i] & action_mask)\r
cc68a136 664 {\r
d524c827 665 if (player_idx >= 0 && ((currentConfig.KeyBinds[i] >> 16) & 3) != player_idx) continue;\r
1ca2ea4f 666 if (strkeys[0]) { strcat(strkeys, " + "); strcat(strkeys, keyNames[i]); break; }\r
667 else strcpy(strkeys, keyNames[i]);\r
cc68a136 668 }\r
669 }\r
670 for (joy = 0; joy < num_of_joys; joy++)\r
671 {\r
672 for (i = 0; i < 32; i++)\r
673 {\r
d524c827 674 if (currentConfig.JoyBinds[joy][i] & action_mask)\r
cc68a136 675 {\r
d524c827 676 if (player_idx >= 0 && ((currentConfig.JoyBinds[joy][i] >> 16) & 3) != player_idx) continue;\r
cc68a136 677 if (strkeys[0]) {\r
678 strcat(strkeys, ", "); strcat(strkeys, usb_joy_key_name(joy + 1, i));\r
679 break;\r
680 }\r
681 else strcpy(strkeys, usb_joy_key_name(joy + 1, i));\r
682 }\r
683 }\r
684 }\r
685\r
45499284 686 // limit..\r
687 strkeys[20] = 0;\r
688\r
d524c827 689 return strkeys;\r
690}\r
691\r
45499284 692static void unbind_action(int action, int pl_idx, int joy)\r
d524c827 693{\r
694 int i, u;\r
695\r
45499284 696 if (joy <= 0)\r
697 {\r
698 for (i = 0; i < 32; i++) {\r
699 if (pl_idx >= 0 && (currentConfig.KeyBinds[i]&0x30000) != (pl_idx<<16)) continue;\r
700 currentConfig.KeyBinds[i] &= ~action;\r
701 }\r
b4fe3a4a 702 }\r
45499284 703 if (joy < 0)\r
704 {\r
705 for (u = 0; u < 4; u++)\r
706 for (i = 0; i < 32; i++) {\r
707 if (pl_idx >= 0 && (currentConfig.JoyBinds[u][i]&0x30000) != (pl_idx<<16)) continue;\r
708 currentConfig.JoyBinds[u][i] &= ~action;\r
709 }\r
710 }\r
711 else if (joy > 0)\r
712 {\r
b4fe3a4a 713 for (i = 0; i < 32; i++) {\r
45499284 714 if (pl_idx >= 0 && (currentConfig.JoyBinds[joy-1][i]&0x30000) != (pl_idx<<16)) continue;\r
715 currentConfig.JoyBinds[joy-1][i] &= ~action;\r
b4fe3a4a 716 }\r
45499284 717 }\r
d524c827 718}\r
719\r
b4fe3a4a 720static int count_bound_keys(int action, int pl_idx, int joy)\r
d524c827 721{\r
722 int i, keys = 0;\r
723\r
724 if (joy)\r
725 {\r
b4fe3a4a 726 for (i = 0; i < 32; i++) {\r
727 if (pl_idx >= 0 && (currentConfig.JoyBinds[joy-1][i]&0x30000) != (pl_idx<<16)) continue;\r
d524c827 728 if (currentConfig.JoyBinds[joy-1][i] & action) keys++;\r
b4fe3a4a 729 }\r
d524c827 730 }\r
731 else\r
732 {\r
b4fe3a4a 733 for (i = 0; i < 32; i++) {\r
734 if (pl_idx >= 0 && (currentConfig.KeyBinds[i]&0x30000) != (pl_idx<<16)) continue;\r
d524c827 735 if (currentConfig.KeyBinds[i] & action) keys++;\r
b4fe3a4a 736 }\r
d524c827 737 }\r
738 return keys;\r
739}\r
740\r
1ca2ea4f 741static void draw_key_config(const me_bind_action *opts, int opt_cnt, int player_idx, int sel)\r
d524c827 742{\r
743 int x, y, tl_y = 40, i;\r
744\r
e11c5548 745 gp2x_pd_clone_buffer2();\r
d524c827 746 if (player_idx >= 0) {\r
a12e0116 747 text_out16(80, 20, "Player %i controls", player_idx + 1);\r
b4fe3a4a 748 x = 80;\r
d524c827 749 } else {\r
a12e0116 750 text_out16(80, 20, "Emulator controls");\r
d524c827 751 x = 40;\r
752 }\r
753\r
e5f426aa 754 menu_draw_selection(x - 16, tl_y + sel*10, (player_idx >= 0) ? 66 : 130);\r
a12e0116 755\r
d524c827 756 y = tl_y;\r
757 for (i = 0; i < opt_cnt; i++, y+=10)\r
a12e0116 758 text_out16(x, y, "%s : %s", opts[i].name, action_binds(player_idx, opts[i].mask));\r
d524c827 759\r
a12e0116 760 text_out16(x, y, "Done");\r
d524c827 761\r
762 if (sel < opt_cnt) {\r
a12e0116 763 text_out16(30, 180, "Press a button to bind/unbind");\r
764 text_out16(30, 190, "Use SELECT to clear");\r
765 text_out16(30, 200, "To bind UP/DOWN, hold SELECT");\r
766 text_out16(30, 210, "Select \"Done\" to exit");\r
d524c827 767 } else {\r
a12e0116 768 text_out16(30, 190, "Use Options -> Save cfg");\r
769 text_out16(30, 200, "to save controls");\r
770 text_out16(30, 210, "Press B or X to exit");\r
d524c827 771 }\r
a12e0116 772 menu_flip();\r
cc68a136 773}\r
774\r
1ca2ea4f 775static void key_config_loop(const me_bind_action *opts, int opt_cnt, int player_idx)\r
cc68a136 776{\r
d524c827 777 int joy = 0, sel = 0, menu_sel_max = opt_cnt, prev_select = 0, i;\r
cc68a136 778 unsigned long inp = 0;\r
779\r
780 for (;;)\r
781 {\r
d524c827 782 draw_key_config(opts, opt_cnt, player_idx, sel);\r
cc68a136 783 inp = wait_for_input_usbjoy(CONFIGURABLE_KEYS, &joy);\r
784 // printf("got %08lX from joy %i\n", inp, joy);\r
a39b7867 785 if (joy == 0)\r
786 {\r
d524c827 787 if (!(inp & GP2X_SELECT)) {\r
788 prev_select = 0;\r
789 if(inp & GP2X_UP ) { sel--; if (sel < 0) sel = menu_sel_max; continue; }\r
790 if(inp & GP2X_DOWN) { sel++; if (sel > menu_sel_max) sel = 0; continue; }\r
cc68a136 791 }\r
d524c827 792 if (sel >= opt_cnt) {\r
793 if (inp & (GP2X_B|GP2X_X)) break;\r
794 else continue;\r
795 }\r
796 // if we are here, we want to bind/unbind something\r
797 if ((inp & GP2X_SELECT) && !prev_select)\r
45499284 798 unbind_action(opts[sel].mask, player_idx, -1);\r
d524c827 799 prev_select = inp & GP2X_SELECT;\r
cc68a136 800 inp &= CONFIGURABLE_KEYS;\r
801 inp &= ~GP2X_SELECT;\r
cc68a136 802 for (i = 0; i < 32; i++)\r
803 if (inp & (1 << i)) {\r
b4fe3a4a 804 if (count_bound_keys(opts[sel].mask, player_idx, 0) >= 2)\r
d524c827 805 currentConfig.KeyBinds[i] &= ~opts[sel].mask; // allow to unbind only\r
806 else currentConfig.KeyBinds[i] ^= opts[sel].mask;\r
c9077ab4 807 if (player_idx >= 0 && (currentConfig.KeyBinds[i] & opts[sel].mask)) {\r
d524c827 808 currentConfig.KeyBinds[i] &= ~(3 << 16);\r
809 currentConfig.KeyBinds[i] |= player_idx << 16;\r
810 }\r
cc68a136 811 }\r
d524c827 812 }\r
813 else if (sel < opt_cnt)\r
814 {\r
cc68a136 815 for (i = 0; i < 32; i++)\r
816 if (inp & (1 << i)) {\r
45499284 817 int *bind = &currentConfig.JoyBinds[joy-1][i];\r
818 if ((*bind & opts[sel].mask) && (player_idx < 0 || player_idx == ((*bind>>16)&3)))\r
1ca2ea4f 819 *bind &= ~opts[sel].mask;\r
45499284 820 else {\r
821 // override\r
822 unbind_action(opts[sel].mask, player_idx, joy);\r
823 *bind = opts[sel].mask;\r
1ca2ea4f 824 if (player_idx > 0) *bind |= player_idx << 16;\r
d524c827 825 }\r
cc68a136 826 }\r
827 }\r
828 }\r
829}\r
830\r
831static void draw_kc_sel(int menu_sel)\r
832{\r
833 int tl_x = 25+40, tl_y = 60, y, i;\r
834 char joyname[36];\r
835\r
836 y = tl_y;\r
e11c5548 837 gp2x_pd_clone_buffer2();\r
e5f426aa 838 menu_draw_selection(tl_x - 16, tl_y + menu_sel*10, 138);\r
cc68a136 839\r
a12e0116 840 text_out16(tl_x, y, "Player 1");\r
841 text_out16(tl_x, (y+=10), "Player 2");\r
842 text_out16(tl_x, (y+=10), "Emulator controls");\r
843 text_out16(tl_x, (y+=10), "Done");\r
cc68a136 844\r
845 tl_x = 25;\r
a12e0116 846 text_out16(tl_x, (y=110), "USB joys detected:");\r
cc68a136 847 if (num_of_joys > 0) {\r
848 for (i = 0; i < num_of_joys; i++) {\r
849 strncpy(joyname, joy_name(joys[i]), 33); joyname[33] = 0;\r
a12e0116 850 text_out16(tl_x, (y+=10), "%i: %s", i+1, joyname);\r
cc68a136 851 }\r
852 } else {\r
a12e0116 853 text_out16(tl_x, (y+=10), "none");\r
cc68a136 854 }\r
855\r
a12e0116 856 menu_flip();\r
cc68a136 857}\r
858\r
d524c827 859\r
d524c827 860// player2_flag, ?, ?, ?, ?, ?, ?, menu\r
861// "NEXT SAVE SLOT", "PREV SAVE SLOT", "SWITCH RENDERER", "SAVE STATE",\r
862// "LOAD STATE", "VOLUME UP", "VOLUME DOWN", "DONE"\r
1ca2ea4f 863me_bind_action emuctrl_actions[] =\r
d524c827 864{\r
d524c827 865 { "Load State ", 1<<28 },\r
866 { "Save State ", 1<<27 },\r
d524c827 867 { "Prev Save Slot ", 1<<25 },\r
868 { "Next Save Slot ", 1<<24 },\r
a12e0116 869 { "Switch Renderer", 1<<26 },\r
870 { "Volume Down ", 1<<30 },\r
871 { "Volume Up ", 1<<29 },\r
4a32f01f 872 { "Fast forward ", 1<<22 },\r
d524c827 873 { "Enter Menu ", 1<<23 },\r
1ca2ea4f 874 { NULL, 0 }\r
d524c827 875};\r
876\r
cc68a136 877static void kc_sel_loop(void)\r
878{\r
d524c827 879 int menu_sel = 3, menu_sel_max = 3;\r
cc68a136 880 unsigned long inp = 0;\r
58c86d00 881 int is_6button = PicoOpt & 0x020;\r
cc68a136 882\r
d524c827 883 while (1)\r
cc68a136 884 {\r
885 draw_kc_sel(menu_sel);\r
886 inp = wait_for_input(GP2X_UP|GP2X_DOWN|GP2X_B|GP2X_X);\r
d524c827 887 if (inp & GP2X_UP ) { menu_sel--; if (menu_sel < 0) menu_sel = menu_sel_max; }\r
888 if (inp & GP2X_DOWN) { menu_sel++; if (menu_sel > menu_sel_max) menu_sel = 0; }\r
889 if (inp & GP2X_B) {\r
cc68a136 890 switch (menu_sel) {\r
1ca2ea4f 891 case 0: key_config_loop(me_ctrl_actions, is_6button ? 12 : 8, 0); return;\r
892 case 1: key_config_loop(me_ctrl_actions, is_6button ? 12 : 8, 1); return;\r
d524c827 893 case 2: key_config_loop(emuctrl_actions,\r
bdec53c9 894 sizeof(emuctrl_actions)/sizeof(emuctrl_actions[0]) - 1, -1); return;\r
eacee137 895 case 3: if (!rom_loaded) emu_WriteConfig(0); return;\r
cc68a136 896 default: return;\r
897 }\r
898 }\r
d524c827 899 if (inp & GP2X_X) return;\r
cc68a136 900 }\r
901}\r
902\r
903\r
bf098bc5 904// --------- sega/mega cd options ----------\r
905\r
4e8a534c 906menu_entry cdopt_entries[] =\r
bf098bc5 907{\r
58c86d00 908 { NULL, MB_NONE, MA_CDOPT_TESTBIOS_USA, NULL, 0, 0, 0, 1, 0 },\r
909 { NULL, MB_NONE, MA_CDOPT_TESTBIOS_EUR, NULL, 0, 0, 0, 1, 0 },\r
910 { NULL, MB_NONE, MA_CDOPT_TESTBIOS_JAP, NULL, 0, 0, 0, 1, 0 },\r
911 { "CD LEDs", MB_ONOFF, MA_CDOPT_LEDS, &currentConfig.EmuOpt, 0x0400, 0, 0, 1, 1 },\r
912 { "CDDA audio (using mp3s)", MB_ONOFF, MA_CDOPT_CDDA, &PicoOpt, 0x0800, 0, 0, 1, 1 },\r
913 { "PCM audio", MB_ONOFF, MA_CDOPT_PCM, &PicoOpt, 0x0400, 0, 0, 1, 1 },\r
914 { NULL, MB_NONE, MA_CDOPT_READAHEAD, NULL, 0, 0, 0, 1, 1 },\r
915 { "SaveRAM cart", MB_ONOFF, MA_CDOPT_SAVERAM, &PicoOpt, 0x8000, 0, 0, 1, 1 },\r
916 { "Scale/Rot. fx (slow)", MB_ONOFF, MA_CDOPT_SCALEROT_CHIP,&PicoOpt, 0x1000, 0, 0, 1, 1 },\r
917 { "Better sync (slow)", MB_ONOFF, MA_CDOPT_BETTER_SYNC, &PicoOpt, 0x2000, 0, 0, 1, 1 },\r
918 { "done", MB_NONE, MA_CDOPT_DONE, NULL, 0, 0, 0, 1, 0 },\r
4e8a534c 919};\r
920\r
58c86d00 921const int cdopt_entry_count = (sizeof(cdopt_entries) / sizeof(cdopt_entries[0]));\r
4e8a534c 922#define CDOPT_ENTRY_COUNT (sizeof(cdopt_entries) / sizeof(cdopt_entries[0]))\r
923\r
924\r
925struct bios_names_t\r
926{\r
927 char us[32], eu[32], jp[32];\r
928};\r
929\r
930static void menu_cdopt_cust_draw(const menu_entry *entry, int x, int y, void *param)\r
931{\r
932 struct bios_names_t *bios_names = param;\r
933 char ra_buff[16];\r
934\r
935 switch (entry->id)\r
936 {\r
a12e0116 937 case MA_CDOPT_TESTBIOS_USA: text_out16(x, y, "USA BIOS: %s", bios_names->us); break;\r
938 case MA_CDOPT_TESTBIOS_EUR: text_out16(x, y, "EUR BIOS: %s", bios_names->eu); break;\r
939 case MA_CDOPT_TESTBIOS_JAP: text_out16(x, y, "JAP BIOS: %s", bios_names->jp); break;\r
4e8a534c 940 case MA_CDOPT_READAHEAD:\r
941 if (PicoCDBuffers > 1) sprintf(ra_buff, "%5iK", PicoCDBuffers * 2);\r
942 else strcpy(ra_buff, " OFF");\r
a12e0116 943 text_out16(x, y, "ReadAhead buffer %s", ra_buff);\r
4e8a534c 944 break;\r
945 default:break;\r
946 }\r
947}\r
948\r
949static void draw_cd_menu_options(int menu_sel, struct bios_names_t *bios_names)\r
950{\r
951 int tl_x = 25, tl_y = 60;\r
952 menu_id selected_id;\r
0a051f55 953 char ra_buff[16];\r
954\r
955 if (PicoCDBuffers > 1) sprintf(ra_buff, "%5iK", PicoCDBuffers * 2);\r
956 else strcpy(ra_buff, " OFF");\r
bf098bc5 957\r
e11c5548 958 gp2x_pd_clone_buffer2();\r
959\r
e5f426aa 960 menu_draw_selection(tl_x - 16, tl_y + menu_sel*10, 246);\r
bf098bc5 961\r
a12e0116 962 me_draw(cdopt_entries, CDOPT_ENTRY_COUNT, tl_x, tl_y, menu_cdopt_cust_draw, bios_names);\r
bf098bc5 963\r
4e8a534c 964 selected_id = me_index2id(cdopt_entries, CDOPT_ENTRY_COUNT, menu_sel);\r
965 if ((selected_id == MA_CDOPT_TESTBIOS_USA && strcmp(bios_names->us, "NOT FOUND")) ||\r
966 (selected_id == MA_CDOPT_TESTBIOS_EUR && strcmp(bios_names->eu, "NOT FOUND")) ||\r
967 (selected_id == MA_CDOPT_TESTBIOS_JAP && strcmp(bios_names->jp, "NOT FOUND")))\r
a12e0116 968 text_out16(tl_x, 210, "Press start to test selected BIOS");\r
bf098bc5 969\r
a12e0116 970 menu_flip();\r
bf098bc5 971}\r
972\r
973static void cd_menu_loop_options(void)\r
974{\r
aae35e84 975 static int menu_sel = 0;\r
976 int menu_sel_max = 10;\r
bf098bc5 977 unsigned long inp = 0;\r
4e8a534c 978 struct bios_names_t bios_names;\r
979 menu_id selected_id;\r
980 char *bios, *p;\r
bf098bc5 981\r
ea8c405f 982 if (emu_findBios(4, &bios)) { // US\r
bf098bc5 983 for (p = bios+strlen(bios)-1; p > bios && *p != '/'; p--); p++;\r
4e8a534c 984 strncpy(bios_names.us, p, sizeof(bios_names.us)); bios_names.us[sizeof(bios_names.us)-1] = 0;\r
985 } else strcpy(bios_names.us, "NOT FOUND");\r
bf098bc5 986\r
ea8c405f 987 if (emu_findBios(8, &bios)) { // EU\r
bf098bc5 988 for (p = bios+strlen(bios)-1; p > bios && *p != '/'; p--); p++;\r
4e8a534c 989 strncpy(bios_names.eu, p, sizeof(bios_names.eu)); bios_names.eu[sizeof(bios_names.eu)-1] = 0;\r
990 } else strcpy(bios_names.eu, "NOT FOUND");\r
bf098bc5 991\r
ea8c405f 992 if (emu_findBios(1, &bios)) { // JP\r
bf098bc5 993 for (p = bios+strlen(bios)-1; p > bios && *p != '/'; p--); p++;\r
4e8a534c 994 strncpy(bios_names.jp, p, sizeof(bios_names.jp)); bios_names.jp[sizeof(bios_names.jp)-1] = 0;\r
995 } else strcpy(bios_names.jp, "NOT FOUND");\r
bf098bc5 996\r
997 for(;;)\r
998 {\r
4e8a534c 999 draw_cd_menu_options(menu_sel, &bios_names);\r
bf098bc5 1000 inp = wait_for_input(GP2X_UP|GP2X_DOWN|GP2X_LEFT|GP2X_RIGHT|GP2X_B|GP2X_X|GP2X_A|GP2X_START);\r
4e8a534c 1001 if (inp & GP2X_UP ) { menu_sel--; if (menu_sel < 0) menu_sel = menu_sel_max; }\r
1002 if (inp & GP2X_DOWN) { menu_sel++; if (menu_sel > menu_sel_max) menu_sel = 0; }\r
1003 selected_id = me_index2id(cdopt_entries, CDOPT_ENTRY_COUNT, menu_sel);\r
1004 if (inp & (GP2X_LEFT|GP2X_RIGHT)) { // multi choise\r
1005 if (!me_process(cdopt_entries, CDOPT_ENTRY_COUNT, selected_id, (inp&GP2X_RIGHT) ? 1 : 0) &&\r
1006 selected_id == MA_CDOPT_READAHEAD) {\r
1007 if (inp & GP2X_LEFT) {\r
1008 PicoCDBuffers >>= 1;\r
2d2247c2 1009 if (PicoCDBuffers < 2) PicoCDBuffers = 0;\r
4e8a534c 1010 } else {\r
2d2247c2 1011 if (PicoCDBuffers < 2) PicoCDBuffers = 2;\r
4e8a534c 1012 else PicoCDBuffers <<= 1;\r
1013 if (PicoCDBuffers > 8*1024) PicoCDBuffers = 8*1024; // 16M\r
1014 }\r
bf098bc5 1015 }\r
1016 }\r
4e8a534c 1017 if (inp & GP2X_B) { // toggleable options\r
1018 if (!me_process(cdopt_entries, CDOPT_ENTRY_COUNT, selected_id, 1) &&\r
1019 selected_id == MA_CDOPT_DONE) {\r
1020 return;\r
1021 }\r
1022 }\r
1023 if (inp & GP2X_START) { // BIOS testers\r
1024 switch (selected_id) {\r
1025 case MA_CDOPT_TESTBIOS_USA:\r
ea8c405f 1026 if (emu_findBios(4, &bios)) { // test US\r
bf098bc5 1027 strcpy(romFileName, bios);\r
1028 engineState = PGS_ReloadRom;\r
1029 return;\r
1030 }\r
1031 break;\r
4e8a534c 1032 case MA_CDOPT_TESTBIOS_EUR:\r
ea8c405f 1033 if (emu_findBios(8, &bios)) { // test EU\r
bf098bc5 1034 strcpy(romFileName, bios);\r
1035 engineState = PGS_ReloadRom;\r
1036 return;\r
1037 }\r
1038 break;\r
4e8a534c 1039 case MA_CDOPT_TESTBIOS_JAP:\r
ea8c405f 1040 if (emu_findBios(1, &bios)) { // test JP\r
bf098bc5 1041 strcpy(romFileName, bios);\r
1042 engineState = PGS_ReloadRom;\r
1043 return;\r
1044 }\r
1045 break;\r
4e8a534c 1046 default:\r
1047 break;\r
bf098bc5 1048 }\r
1049 }\r
4e8a534c 1050 if (inp & (GP2X_X|GP2X_A)) return;\r
bf098bc5 1051 }\r
1052}\r
1053\r
1054\r
1055// --------- advanced options ----------\r
1056\r
4e8a534c 1057menu_entry opt2_entries[] =\r
1058{\r
58c86d00 1059 { NULL, MB_NONE, MA_OPT2_GAMMA, NULL, 0, 0, 0, 1, 1 },\r
1060 { "A_SN's gamma curve", MB_ONOFF, MA_OPT2_A_SN_GAMMA, &currentConfig.EmuOpt, 0x1000, 0, 0, 1, 1 },\r
1061 { "Perfect vsync", MB_ONOFF, MA_OPT2_VSYNC, &currentConfig.EmuOpt, 0x2000, 0, 0, 1, 1 },\r
1ca2ea4f 1062 { "Emulate Z80", MB_ONOFF, MA_OPT2_ENABLE_Z80, &PicoOpt, 0x00004, 0, 0, 1, 1 },\r
1063 { "Emulate YM2612 (FM)", MB_ONOFF, MA_OPT2_ENABLE_YM2612, &PicoOpt, 0x00001, 0, 0, 1, 1 },\r
1064 { "Emulate SN76496 (PSG)", MB_ONOFF, MA_OPT2_ENABLE_SN76496,&PicoOpt, 0x00002, 0, 0, 1, 1 },\r
58c86d00 1065 { "gzip savestates", MB_ONOFF, MA_OPT2_GZIP_STATES, &currentConfig.EmuOpt, 0x0008, 0, 0, 1, 1 },\r
1066 { "Don't save last used ROM", MB_ONOFF, MA_OPT2_NO_LAST_ROM, &currentConfig.EmuOpt, 0x0020, 0, 0, 1, 1 },\r
1067 { "needs restart:", MB_NONE, MA_NONE, NULL, 0, 0, 0, 1, 0 },\r
1068 { "craigix's RAM timings", MB_ONOFF, MA_OPT2_RAMTIMINGS, &currentConfig.EmuOpt, 0x0100, 0, 0, 1, 1 },\r
1069 { NULL, MB_ONOFF, MA_OPT2_SQUIDGEHACK, &currentConfig.EmuOpt, 0x0010, 0, 0, 1, 1 },\r
1ca2ea4f 1070 { "SVP dynarec", MB_ONOFF, MA_OPT2_SVP_DYNAREC, &PicoOpt, 0x20000, 0, 0, 1, 1 },\r
58c86d00 1071 { "done", MB_NONE, MA_OPT2_DONE, NULL, 0, 0, 0, 1, 0 },\r
4e8a534c 1072};\r
1073\r
1074#define OPT2_ENTRY_COUNT (sizeof(opt2_entries) / sizeof(opt2_entries[0]))\r
58c86d00 1075const int opt2_entry_count = (sizeof(opt2_entries) / sizeof(opt2_entries[0]));\r
4e8a534c 1076\r
1077static void menu_opt2_cust_draw(const menu_entry *entry, int x, int y, void *param)\r
1078{\r
1079 if (entry->id == MA_OPT2_GAMMA)\r
a12e0116 1080 text_out16(x, y, "Gamma correction %i.%02i", currentConfig.gamma / 100, currentConfig.gamma%100);\r
4e8a534c 1081 else if (entry->id == MA_OPT2_SQUIDGEHACK)\r
58c86d00 1082 text_out16(x, y, "Squidgehack (now %s %s", mmuhack_status ? "active) " : "inactive)",\r
4e8a534c 1083 (currentConfig.EmuOpt&0x0010)?"ON":"OFF");\r
1084}\r
1085\r
1086\r
cc68a136 1087static void draw_amenu_options(int menu_sel)\r
1088{\r
4e8a534c 1089 int tl_x = 25, tl_y = 50;\r
cc68a136 1090\r
e11c5548 1091 gp2x_pd_clone_buffer2();\r
1092\r
e5f426aa 1093 menu_draw_selection(tl_x - 16, tl_y + menu_sel*10, 252);\r
cc68a136 1094\r
a12e0116 1095 me_draw(opt2_entries, OPT2_ENTRY_COUNT, tl_x, tl_y, menu_opt2_cust_draw, NULL);\r
cc68a136 1096\r
a12e0116 1097 menu_flip();\r
cc68a136 1098}\r
1099\r
1100static void amenu_loop_options(void)\r
1101{\r
aae35e84 1102 static int menu_sel = 0;\r
4e8a534c 1103 int menu_sel_max;\r
cc68a136 1104 unsigned long inp = 0;\r
4e8a534c 1105 menu_id selected_id;\r
1106\r
1107 menu_sel_max = me_count_enabled(opt2_entries, OPT2_ENTRY_COUNT) - 1;\r
cc68a136 1108\r
1109 for(;;)\r
1110 {\r
1111 draw_amenu_options(menu_sel);\r
1112 inp = wait_for_input(GP2X_UP|GP2X_DOWN|GP2X_LEFT|GP2X_RIGHT|GP2X_B|GP2X_X|GP2X_A);\r
4e8a534c 1113 if (inp & GP2X_UP ) { menu_sel--; if (menu_sel < 0) menu_sel = menu_sel_max; }\r
1114 if (inp & GP2X_DOWN) { menu_sel++; if (menu_sel > menu_sel_max) menu_sel = 0; }\r
1115 selected_id = me_index2id(opt2_entries, OPT2_ENTRY_COUNT, menu_sel);\r
1116 if (inp & (GP2X_LEFT|GP2X_RIGHT)) { // multi choise\r
1117 if (!me_process(opt2_entries, OPT2_ENTRY_COUNT, selected_id, (inp&GP2X_RIGHT) ? 1 : 0) &&\r
1118 selected_id == MA_OPT2_GAMMA) {\r
1119 while ((inp = gp2x_joystick_read(1)) & (GP2X_LEFT|GP2X_RIGHT)) {\r
1120 currentConfig.gamma += (inp & GP2X_LEFT) ? -1 : 1;\r
1121 if (currentConfig.gamma < 1) currentConfig.gamma = 1;\r
1122 if (currentConfig.gamma > 300) currentConfig.gamma = 300;\r
1123 draw_amenu_options(menu_sel);\r
1124 usleep(18*1000);\r
1125 }\r
cc68a136 1126 }\r
1127 }\r
4e8a534c 1128 if (inp & GP2X_B) { // toggleable options\r
1129 if (!me_process(opt2_entries, OPT2_ENTRY_COUNT, selected_id, 1) &&\r
1130 selected_id == MA_OPT2_DONE) {\r
1131 return;\r
cc68a136 1132 }\r
1133 }\r
4e8a534c 1134 if (inp & (GP2X_X|GP2X_A)) return;\r
cc68a136 1135 }\r
1136}\r
1137\r
1138// -------------- options --------------\r
1139\r
4e8a534c 1140\r
1141menu_entry opt_entries[] =\r
1142{\r
58c86d00 1143 { NULL, MB_NONE, MA_OPT_RENDERER, NULL, 0, 0, 0, 1, 1 },\r
1144 { NULL, MB_RANGE, MA_OPT_SCALING, &currentConfig.scaling, 0, 0, 3, 1, 1 },\r
1145 { "Accurate timing (slower)", MB_ONOFF, MA_OPT_ACC_TIMING, &PicoOpt, 0x040, 0, 0, 1, 1 },\r
1146 { "Accurate sprites (slower)", MB_ONOFF, MA_OPT_ACC_SPRITES, &PicoOpt, 0x080, 0, 0, 1, 1 },\r
1147 { "Show FPS", MB_ONOFF, MA_OPT_SHOW_FPS, &currentConfig.EmuOpt, 0x002, 0, 0, 1, 1 },\r
1148 { NULL, MB_RANGE, MA_OPT_FRAMESKIP, &currentConfig.Frameskip, 0, -1, 16, 1, 1 },\r
1149 { "Enable sound", MB_ONOFF, MA_OPT_ENABLE_SOUND, &currentConfig.EmuOpt, 0x004, 0, 0, 1, 1 },\r
1150 { NULL, MB_NONE, MA_OPT_SOUND_QUALITY, NULL, 0, 0, 0, 1, 1 },\r
1151 { "Use ARM940 core for sound", MB_ONOFF, MA_OPT_ARM940_SOUND, &PicoOpt, 0x200, 0, 0, 1, 1 },\r
1152 { "6 button pad", MB_ONOFF, MA_OPT_6BUTTON_PAD, &PicoOpt, 0x020, 0, 0, 1, 1 },\r
1153 { NULL, MB_NONE, MA_OPT_REGION, NULL, 0, 0, 0, 1, 1 },\r
1154 { "Use SRAM/BRAM savestates", MB_ONOFF, MA_OPT_SRAM_STATES, &currentConfig.EmuOpt, 0x001, 0, 0, 1, 1 },\r
1155 { NULL, MB_NONE, MA_OPT_CONFIRM_STATES,NULL, 0, 0, 0, 1, 1 },\r
1156 { "Save slot", MB_RANGE, MA_OPT_SAVE_SLOT, &state_slot, 0, 0, 9, 1, 1 },\r
1157 { NULL, MB_NONE, MA_OPT_CPU_CLOCKS, NULL, 0, 0, 0, 1, 1 },\r
1158 { "[Sega/Mega CD options]", MB_NONE, MA_OPT_SCD_OPTS, NULL, 0, 0, 0, 1, 0 },\r
1159 { "[advanced options]", MB_NONE, MA_OPT_ADV_OPTS, NULL, 0, 0, 0, 1, 0 },\r
1160 { NULL, MB_NONE, MA_OPT_SAVECFG, NULL, 0, 0, 0, 1, 0 },\r
1161 { "Save cfg for current game only",MB_NONE,MA_OPT_SAVECFG_GAME,NULL, 0, 0, 0, 1, 0 },\r
1162 { NULL, MB_NONE, MA_OPT_LOADCFG, NULL, 0, 0, 0, 1, 0 },\r
4e8a534c 1163};\r
1164\r
1165#define OPT_ENTRY_COUNT (sizeof(opt_entries) / sizeof(opt_entries[0]))\r
58c86d00 1166const int opt_entry_count = OPT_ENTRY_COUNT;\r
cc68a136 1167\r
4e8a534c 1168\r
1169static void menu_opt_cust_draw(const menu_entry *entry, int x, int y, void *param)\r
cc68a136 1170{\r
4e8a534c 1171 char *str, str24[24];\r
1172\r
1173 switch (entry->id)\r
1174 {\r
1175 case MA_OPT_RENDERER:\r
58c86d00 1176 if (PicoOpt&0x10)\r
4e8a534c 1177 str = " 8bit fast";\r
1178 else if (currentConfig.EmuOpt&0x80)\r
1179 str = "16bit accurate";\r
1180 else\r
1181 str = " 8bit accurate";\r
a12e0116 1182 text_out16(x, y, "Renderer: %s", str);\r
4e8a534c 1183 break;\r
1184 case MA_OPT_SCALING:\r
1185 switch (currentConfig.scaling) {\r
1186 default: str = " OFF"; break;\r
1187 case 1: str = "hw horizontal"; break;\r
1188 case 2: str = "hw horiz. + vert."; break;\r
1189 case 3: str = "sw horizontal"; break;\r
1190 }\r
a12e0116 1191 text_out16(x, y, "Scaling: %s", str);\r
4e8a534c 1192 break;\r
1193 case MA_OPT_FRAMESKIP:\r
1194 if (currentConfig.Frameskip < 0)\r
1195 strcpy(str24, "Auto");\r
1196 else sprintf(str24, "%i", currentConfig.Frameskip);\r
a12e0116 1197 text_out16(x, y, "Frameskip %s", str24);\r
4e8a534c 1198 break;\r
1199 case MA_OPT_SOUND_QUALITY:\r
58c86d00 1200 str = (PicoOpt&0x08)?"stereo":"mono";\r
1201 text_out16(x, y, "Sound Quality: %5iHz %s", PsndRate, str);\r
4e8a534c 1202 break;\r
1203 case MA_OPT_REGION:\r
58c86d00 1204 text_out16(x, y, "Region: %s", me_region_name(PicoRegionOverride, PicoAutoRgnOrder));\r
4e8a534c 1205 break;\r
1206 case MA_OPT_CONFIRM_STATES:\r
1207 switch ((currentConfig.EmuOpt >> 9) & 5) {\r
1208 default: str = "OFF"; break;\r
1209 case 1: str = "writes"; break;\r
1210 case 4: str = "loads"; break;\r
1211 case 5: str = "both"; break;\r
1212 }\r
a12e0116 1213 text_out16(x, y, "Confirm savestate %s", str);\r
4e8a534c 1214 break;\r
1215 case MA_OPT_CPU_CLOCKS:\r
a12e0116 1216 text_out16(x, y, "GP2X CPU clocks %iMhz", currentConfig.CPUclock);\r
4e8a534c 1217 break;\r
1218 case MA_OPT_SAVECFG:\r
1219 str24[0] = 0;\r
1220 if (config_slot != 0) sprintf(str24, " (profile: %i)", config_slot);\r
a12e0116 1221 text_out16(x, y, "Save cfg as default%s", str24);\r
4e8a534c 1222 break;\r
1223 case MA_OPT_LOADCFG:\r
a12e0116 1224 text_out16(x, y, "Load cfg from profile %i", config_slot);\r
4e8a534c 1225 break;\r
1226 default:\r
1227 printf("%s: unimplemented (%i)\n", __FUNCTION__, entry->id);\r
1228 break;\r
46969540 1229 }\r
4e8a534c 1230}\r
1231\r
1232\r
1233\r
1234static void draw_menu_options(int menu_sel)\r
1235{\r
a12e0116 1236 int tl_x = 25, tl_y = 24;\r
cc68a136 1237\r
e11c5548 1238 gp2x_pd_clone_buffer2();\r
1239\r
e5f426aa 1240 menu_draw_selection(tl_x - 16, tl_y + menu_sel*10, 284);\r
cc68a136 1241\r
a12e0116 1242 me_draw(opt_entries, OPT_ENTRY_COUNT, tl_x, tl_y, menu_opt_cust_draw, NULL);\r
cc68a136 1243\r
a12e0116 1244 menu_flip();\r
cc68a136 1245}\r
1246\r
1247static int sndrate_prevnext(int rate, int dir)\r
1248{\r
1249 int i, rates[] = { 8000, 11025, 16000, 22050, 44100 };\r
1250\r
1251 for (i = 0; i < 5; i++)\r
1252 if (rates[i] == rate) break;\r
1253\r
1254 i += dir ? 1 : -1;\r
1255 if (i > 4) return dir ? 44100 : 22050;\r
1256 if (i < 0) return dir ? 11025 : 8000;\r
1257 return rates[i];\r
1258}\r
1259\r
51a902ae 1260static void region_prevnext(int right)\r
1261{\r
1262 // jp_ntsc=1, jp_pal=2, usa=4, eu=8\r
1263 static int rgn_orders[] = { 0x148, 0x184, 0x814, 0x418, 0x841, 0x481 };\r
1264 int i;\r
1265 if (right) {\r
58c86d00 1266 if (!PicoRegionOverride) {\r
51a902ae 1267 for (i = 0; i < 6; i++)\r
1268 if (rgn_orders[i] == PicoAutoRgnOrder) break;\r
1269 if (i < 5) PicoAutoRgnOrder = rgn_orders[i+1];\r
58c86d00 1270 else PicoRegionOverride=1;\r
51a902ae 1271 }\r
58c86d00 1272 else PicoRegionOverride<<=1;\r
1273 if (PicoRegionOverride > 8) PicoRegionOverride = 8;\r
51a902ae 1274 } else {\r
58c86d00 1275 if (!PicoRegionOverride) {\r
51a902ae 1276 for (i = 0; i < 6; i++)\r
1277 if (rgn_orders[i] == PicoAutoRgnOrder) break;\r
1278 if (i > 0) PicoAutoRgnOrder = rgn_orders[i-1];\r
1279 }\r
58c86d00 1280 else PicoRegionOverride>>=1;\r
51a902ae 1281 }\r
1282}\r
1283\r
cc68a136 1284static void menu_options_save(void)\r
1285{\r
7d0143a2 1286 if (PicoRegionOverride) {\r
1287 // force setting possibly changed..\r
1288 Pico.m.pal = (PicoRegionOverride == 2 || PicoRegionOverride == 8) ? 1 : 0;\r
1289 }\r
d524c827 1290 if (!(PicoOpt & 0x20)) {\r
1291 // unbind XYZ MODE, just in case\r
45499284 1292 unbind_action(0xf00, -1, -1);\r
cc68a136 1293 }\r
1294}\r
1295\r
bf098bc5 1296static int menu_loop_options(void)\r
cc68a136 1297{\r
aae35e84 1298 static int menu_sel = 0;\r
4e8a534c 1299 int menu_sel_max, ret;\r
cc68a136 1300 unsigned long inp = 0;\r
4e8a534c 1301 menu_id selected_id;\r
cc68a136 1302\r
eacee137 1303 me_enable(opt_entries, OPT_ENTRY_COUNT, MA_OPT_SAVECFG_GAME, rom_loaded);\r
4e8a534c 1304 me_enable(opt_entries, OPT_ENTRY_COUNT, MA_OPT_LOADCFG, config_slot != config_slot_current);\r
1305 menu_sel_max = me_count_enabled(opt_entries, OPT_ENTRY_COUNT) - 1;\r
d524c827 1306 if (menu_sel > menu_sel_max) menu_sel = menu_sel_max;\r
4e8a534c 1307\r
1308 while (1)\r
cc68a136 1309 {\r
1310 draw_menu_options(menu_sel);\r
1311 inp = wait_for_input(GP2X_UP|GP2X_DOWN|GP2X_LEFT|GP2X_RIGHT|GP2X_B|GP2X_X|GP2X_A);\r
4e8a534c 1312 if (inp & GP2X_UP ) { menu_sel--; if (menu_sel < 0) menu_sel = menu_sel_max; }\r
1313 if (inp & GP2X_DOWN) { menu_sel++; if (menu_sel > menu_sel_max) menu_sel = 0; }\r
1314 selected_id = me_index2id(opt_entries, OPT_ENTRY_COUNT, menu_sel);\r
58c86d00 1315 if (inp & (GP2X_LEFT|GP2X_RIGHT)) { // multi choice\r
4e8a534c 1316 if (!me_process(opt_entries, OPT_ENTRY_COUNT, selected_id, (inp&GP2X_RIGHT) ? 1 : 0)) {\r
1317 switch (selected_id) {\r
1318 case MA_OPT_RENDERER:\r
1319 if (inp & GP2X_LEFT) {\r
58c86d00 1320 if (PicoOpt&0x10) PicoOpt&= ~0x10;\r
4e8a534c 1321 else if (!(currentConfig.EmuOpt &0x80))currentConfig.EmuOpt |= 0x80;\r
1322 else if ( currentConfig.EmuOpt &0x80) break;\r
1323 } else {\r
58c86d00 1324 if (PicoOpt&0x10) break;\r
1325 else if (!(currentConfig.EmuOpt &0x80))PicoOpt|= 0x10;\r
4e8a534c 1326 else if ( currentConfig.EmuOpt &0x80) currentConfig.EmuOpt &= ~0x80;\r
1327 }\r
1328 break;\r
1329 case MA_OPT_SOUND_QUALITY:\r
58c86d00 1330 if ((inp & GP2X_RIGHT) && PsndRate == 44100 && !(PicoOpt&0x08)) {\r
1331 PsndRate = 8000; PicoOpt|= 0x08;\r
1332 } else if ((inp & GP2X_LEFT) && PsndRate == 8000 && (PicoOpt&0x08)) {\r
1333 PsndRate = 44100; PicoOpt&=~0x08;\r
1334 } else PsndRate = sndrate_prevnext(PsndRate, inp & GP2X_RIGHT);\r
4e8a534c 1335 break;\r
1336 case MA_OPT_REGION:\r
1337 region_prevnext(inp & GP2X_RIGHT);\r
1338 break;\r
1339 case MA_OPT_CONFIRM_STATES: {\r
1340 int n = ((currentConfig.EmuOpt>>9)&1) | ((currentConfig.EmuOpt>>10)&2);\r
1341 n += (inp & GP2X_LEFT) ? -1 : 1;\r
1342 if (n < 0) n = 0; else if (n > 3) n = 3;\r
1343 n |= n << 1; n &= ~2;\r
1344 currentConfig.EmuOpt &= ~0xa00;\r
1345 currentConfig.EmuOpt |= n << 9;\r
1346 break;\r
1347 }\r
1348 case MA_OPT_SAVE_SLOT:\r
1349 if (inp & GP2X_RIGHT) {\r
1350 state_slot++; if (state_slot > 9) state_slot = 0;\r
1351 } else {state_slot--; if (state_slot < 0) state_slot = 9;\r
1352 }\r
1353 break;\r
1354 case MA_OPT_CPU_CLOCKS:\r
1355 while ((inp = gp2x_joystick_read(1)) & (GP2X_LEFT|GP2X_RIGHT)) {\r
1356 currentConfig.CPUclock += (inp & GP2X_LEFT) ? -1 : 1;\r
1357 if (currentConfig.CPUclock < 1) currentConfig.CPUclock = 1;\r
1358 draw_menu_options(menu_sel);\r
1359 usleep(50*1000);\r
1360 }\r
1361 break;\r
1362 case MA_OPT_SAVECFG:\r
1363 case MA_OPT_SAVECFG_GAME:\r
1364 case MA_OPT_LOADCFG:\r
1365 config_slot += (inp&GP2X_RIGHT) ? 1 : -1;\r
1366 if (config_slot > 9) config_slot = 0;\r
1367 if (config_slot < 0) config_slot = 9;\r
1368 me_enable(opt_entries, OPT_ENTRY_COUNT, MA_OPT_LOADCFG, config_slot != config_slot_current);\r
1369 menu_sel_max = me_count_enabled(opt_entries, OPT_ENTRY_COUNT) - 1;\r
1370 if (menu_sel > menu_sel_max) menu_sel = menu_sel_max;\r
1371 break;\r
1372 default:\r
1373 //printf("%s: something unknown selected (%i)\n", __FUNCTION__, selected_id);\r
1374 break;\r
1375 }\r
1376 }\r
1377 }\r
1378 if (inp & GP2X_B) {\r
1379 if (!me_process(opt_entries, OPT_ENTRY_COUNT, selected_id, 1))\r
1380 {\r
1381 switch (selected_id)\r
1382 {\r
1383 case MA_OPT_SCD_OPTS:\r
1384 cd_menu_loop_options();\r
1385 if (engineState == PGS_ReloadRom)\r
1386 return 0; // test BIOS\r
1387 break;\r
1388 case MA_OPT_ADV_OPTS:\r
1389 amenu_loop_options();\r
1390 break;\r
1391 case MA_OPT_SAVECFG: // done (update and write)\r
1392 menu_options_save();\r
1393 if (emu_WriteConfig(0)) strcpy(menuErrorMsg, "config saved");\r
1394 else strcpy(menuErrorMsg, "failed to write config");\r
1395 return 1;\r
1396 case MA_OPT_SAVECFG_GAME: // done (update and write for current game)\r
1397 menu_options_save();\r
1398 if (emu_WriteConfig(1)) strcpy(menuErrorMsg, "config saved");\r
1399 else strcpy(menuErrorMsg, "failed to write config");\r
1400 return 1;\r
1401 case MA_OPT_LOADCFG:\r
d524c827 1402 ret = emu_ReadConfig(1, 1);\r
1403 if (!ret) ret = emu_ReadConfig(0, 1);\r
1404 if (ret) strcpy(menuErrorMsg, "config loaded");\r
4e8a534c 1405 else strcpy(menuErrorMsg, "failed to load config");\r
1406 return 1;\r
1407 default:\r
1408 //printf("%s: something unknown selected (%i)\n", __FUNCTION__, selected_id);\r
1409 break;\r
1410 }\r
cc68a136 1411 }\r
1412 }\r
51a902ae 1413 if(inp & (GP2X_X|GP2X_A)) {\r
cc68a136 1414 menu_options_save();\r
bf098bc5 1415 return 0; // done (update, no write)\r
cc68a136 1416 }\r
cc68a136 1417 }\r
1418}\r
1419\r
1420// -------------- credits --------------\r
1421\r
1422static void draw_menu_credits(void)\r
1423{\r
a12e0116 1424 int tl_x = 15, tl_y = 64, y;\r
e11c5548 1425 gp2x_pd_clone_buffer2();\r
cc68a136 1426\r
a12e0116 1427 text_out16(tl_x, 20, "PicoDrive v" VERSION " (c) notaz, 2006,2007");\r
cc68a136 1428 y = tl_y;\r
a12e0116 1429 text_out16(tl_x, y, "Credits:");\r
3ec29f01 1430 text_out16(tl_x, (y+=10), "fDave: Cyclone 68000 core,");\r
a12e0116 1431 text_out16(tl_x, (y+=10), " base code of PicoDrive");\r
1432 text_out16(tl_x, (y+=10), "Reesy & FluBBa: DrZ80 core");\r
1433 text_out16(tl_x, (y+=10), "MAME devs: YM2612 and SN76496 cores");\r
1434 text_out16(tl_x, (y+=10), "Charles MacDonald: Genesis hw docs");\r
1435 text_out16(tl_x, (y+=10), "Stephane Dallongeville:");\r
1436 text_out16(tl_x, (y+=10), " opensource Gens");\r
1437 text_out16(tl_x, (y+=10), "Haze: Genesis hw info");\r
1438 text_out16(tl_x, (y+=10), "rlyeh and others: minimal SDK");\r
1439 text_out16(tl_x, (y+=10), "Squidge: squidgehack");\r
1440 text_out16(tl_x, (y+=10), "Dzz: ARM940 sample");\r
1441 text_out16(tl_x, (y+=10), "GnoStiC / Puck2099: USB joystick");\r
1442 text_out16(tl_x, (y+=10), "craigix: GP2X hardware");\r
a4f0cc86 1443 text_out16(tl_x, (y+=10), "ketchupgun: skin design");\r
a12e0116 1444\r
1445 menu_flip();\r
cc68a136 1446}\r
1447\r
1448\r
1449// -------------- root menu --------------\r
1450\r
4e8a534c 1451menu_entry main_entries[] =\r
1452{\r
58c86d00 1453 { "Resume game", MB_NONE, MA_MAIN_RESUME_GAME, NULL, 0, 0, 0, 0, 0 },\r
1454 { "Save State", MB_NONE, MA_MAIN_SAVE_STATE, NULL, 0, 0, 0, 0, 0 },\r
1455 { "Load State", MB_NONE, MA_MAIN_LOAD_STATE, NULL, 0, 0, 0, 0, 0 },\r
1456 { "Reset game", MB_NONE, MA_MAIN_RESET_GAME, NULL, 0, 0, 0, 0, 0 },\r
1457 { "Load new ROM/ISO", MB_NONE, MA_MAIN_LOAD_ROM, NULL, 0, 0, 0, 1, 0 },\r
1458 { "Change options", MB_NONE, MA_MAIN_OPTIONS, NULL, 0, 0, 0, 1, 0 },\r
1459 { "Configure controls", MB_NONE, MA_MAIN_CONTROLS, NULL, 0, 0, 0, 1, 0 },\r
1460 { "Credits", MB_NONE, MA_MAIN_CREDITS, NULL, 0, 0, 0, 1, 0 },\r
1461 { "Patches / GameGenie",MB_NONE, MA_MAIN_PATCHES, NULL, 0, 0, 0, 0, 0 },\r
1462 { "Exit", MB_NONE, MA_MAIN_EXIT, NULL, 0, 0, 0, 1, 0 }\r
4e8a534c 1463};\r
1464\r
1465#define MAIN_ENTRY_COUNT (sizeof(main_entries) / sizeof(main_entries[0]))\r
1466\r
cc68a136 1467static void draw_menu_root(int menu_sel)\r
1468{\r
4e8a534c 1469 const int tl_x = 70, tl_y = 70;\r
1470\r
e11c5548 1471 gp2x_pd_clone_buffer2();\r
cc68a136 1472\r
a12e0116 1473 text_out16(tl_x, 20, "PicoDrive v" VERSION);\r
1474\r
e5f426aa 1475 menu_draw_selection(tl_x - 16, tl_y + menu_sel*10, 146);\r
cc68a136 1476\r
4e8a534c 1477 me_draw(main_entries, MAIN_ENTRY_COUNT, tl_x, tl_y, NULL, NULL);\r
cc68a136 1478\r
cc68a136 1479 // error\r
a12e0116 1480 if (menuErrorMsg[0]) {\r
1481 memset((char *)gp2x_screen + 320*224*2, 0, 320*16*2);\r
1482 text_out16(5, 226, menuErrorMsg);\r
1483 }\r
1484 menu_flip();\r
cc68a136 1485}\r
1486\r
1487\r
1488static void menu_loop_root(void)\r
1489{\r
4e8a534c 1490 static int menu_sel = 0;\r
1491 int ret, menu_sel_max;\r
cc68a136 1492 unsigned long inp = 0;\r
cc68a136 1493\r
eacee137 1494 me_enable(main_entries, MAIN_ENTRY_COUNT, MA_MAIN_RESUME_GAME, rom_loaded);\r
1495 me_enable(main_entries, MAIN_ENTRY_COUNT, MA_MAIN_SAVE_STATE, rom_loaded);\r
1496 me_enable(main_entries, MAIN_ENTRY_COUNT, MA_MAIN_LOAD_STATE, rom_loaded);\r
1497 me_enable(main_entries, MAIN_ENTRY_COUNT, MA_MAIN_RESET_GAME, rom_loaded);\r
4e8a534c 1498 me_enable(main_entries, MAIN_ENTRY_COUNT, MA_MAIN_PATCHES, PicoPatches != NULL);\r
1499\r
1500 menu_sel_max = me_count_enabled(main_entries, MAIN_ENTRY_COUNT) - 1;\r
d524c827 1501 if (menu_sel > menu_sel_max) menu_sel = menu_sel_max;\r
cc68a136 1502\r
2433f409 1503 /* make sure action buttons are not pressed on entering menu */\r
1504 draw_menu_root(menu_sel);\r
1505 while (gp2x_joystick_read(1) & (GP2X_B|GP2X_X|GP2X_SELECT)) usleep(50*1000);\r
1506\r
1507 for (;;)\r
cc68a136 1508 {\r
1509 draw_menu_root(menu_sel);\r
0af33fe0 1510 inp = wait_for_input(GP2X_UP|GP2X_DOWN|GP2X_B|GP2X_X|GP2X_SELECT|GP2X_L|GP2X_R);\r
4e8a534c 1511 if(inp & GP2X_UP ) { menu_sel--; if (menu_sel < 0) menu_sel = menu_sel_max; }\r
1512 if(inp & GP2X_DOWN) { menu_sel++; if (menu_sel > menu_sel_max) menu_sel = 0; }\r
0af33fe0 1513 if((inp & (GP2X_L|GP2X_R)) == (GP2X_L|GP2X_R)) debug_menu_loop();\r
cc68a136 1514 if(inp &(GP2X_SELECT|GP2X_X)){\r
eacee137 1515 if (rom_loaded) {\r
cc68a136 1516 while (gp2x_joystick_read(1) & (GP2X_SELECT|GP2X_X)) usleep(50*1000); // wait until select is released\r
1517 engineState = PGS_Running;\r
1518 break;\r
1519 }\r
1520 }\r
4e8a534c 1521 if(inp & GP2X_B) {\r
1522 switch (me_index2id(main_entries, MAIN_ENTRY_COUNT, menu_sel))\r
1523 {\r
1524 case MA_MAIN_RESUME_GAME:\r
eacee137 1525 if (rom_loaded) {\r
721cd396 1526 while (gp2x_joystick_read(1) & GP2X_B) usleep(50*1000);\r
1527 engineState = PGS_Running;\r
1528 return;\r
1529 }\r
cc68a136 1530 break;\r
4e8a534c 1531 case MA_MAIN_SAVE_STATE:\r
eacee137 1532 if (rom_loaded) {\r
860c6322 1533 if(savestate_menu_loop(0))\r
cc68a136 1534 continue;\r
cc68a136 1535 engineState = PGS_Running;\r
1536 return;\r
1537 }\r
1538 break;\r
4e8a534c 1539 case MA_MAIN_LOAD_STATE:\r
eacee137 1540 if (rom_loaded) {\r
860c6322 1541 if(savestate_menu_loop(1))\r
cc68a136 1542 continue;\r
cc68a136 1543 engineState = PGS_Running;\r
1544 return;\r
1545 }\r
1546 break;\r
4e8a534c 1547 case MA_MAIN_RESET_GAME:\r
eacee137 1548 if (rom_loaded) {\r
cc68a136 1549 emu_ResetGame();\r
1550 engineState = PGS_Running;\r
1551 return;\r
1552 }\r
1553 break;\r
4e8a534c 1554 case MA_MAIN_LOAD_ROM:\r
a12e0116 1555 {\r
1556 char curr_path[PATH_MAX], *selfname;\r
1557 FILE *tstf;\r
1ca2ea4f 1558 if ( (tstf = fopen(lastRomFile, "rb")) )\r
a12e0116 1559 {\r
1560 fclose(tstf);\r
1ca2ea4f 1561 strcpy(curr_path, lastRomFile);\r
a12e0116 1562 }\r
1563 else\r
1564 getcwd(curr_path, PATH_MAX);\r
cc68a136 1565 selfname = romsel_loop(curr_path);\r
1566 if (selfname) {\r
1567 printf("selected file: %s\n", selfname);\r
cc68a136 1568 engineState = PGS_ReloadRom;\r
a12e0116 1569 return;\r
cc68a136 1570 }\r
a12e0116 1571 break;\r
1572 }\r
4e8a534c 1573 case MA_MAIN_OPTIONS:\r
bf098bc5 1574 ret = menu_loop_options();\r
1575 if (ret == 1) continue; // status update\r
1576 if (engineState == PGS_ReloadRom)\r
1577 return; // BIOS test\r
cc68a136 1578 break;\r
4e8a534c 1579 case MA_MAIN_CONTROLS:\r
cc68a136 1580 kc_sel_loop();\r
1581 break;\r
4e8a534c 1582 case MA_MAIN_CREDITS:\r
cc68a136 1583 draw_menu_credits();\r
1584 usleep(500*1000);\r
1585 inp = wait_for_input(GP2X_B|GP2X_X);\r
1586 break;\r
4e8a534c 1587 case MA_MAIN_EXIT:\r
cc68a136 1588 engineState = PGS_Quit;\r
1589 return;\r
4e8a534c 1590 case MA_MAIN_PATCHES:\r
eacee137 1591 if (rom_loaded && PicoPatches) {\r
b67ef287 1592 patches_menu_loop();\r
1593 PicoPatchApply();\r
1594 strcpy(menuErrorMsg, "Patches applied");\r
1595 continue;\r
1596 }\r
1597 break;\r
4e8a534c 1598 default:\r
1599 printf("%s: something unknown selected\n", __FUNCTION__);\r
1600 break;\r
cc68a136 1601 }\r
1602 }\r
1603 menuErrorMsg[0] = 0; // clear error msg\r
1604 }\r
1605}\r
1606\r
a4f0cc86 1607static void menu_darken_bg(void *dst, int pixels, int darker)\r
cc68a136 1608{\r
a12e0116 1609 unsigned int *screen = dst;\r
1610 pixels /= 2;\r
a4f0cc86 1611 if (darker)\r
a12e0116 1612 {\r
a4f0cc86 1613 while (pixels--)\r
1614 {\r
1615 unsigned int p = *screen;\r
1616 *screen++ = ((p&0xf79ef79e)>>1) - ((p&0xc618c618)>>3);\r
1617 }\r
1618 }\r
1619 else\r
1620 {\r
1621 while (pixels--)\r
1622 {\r
1623 unsigned int p = *screen;\r
1624 *screen++ = (p&0xf79ef79e)>>1;\r
1625 }\r
a12e0116 1626 }\r
1627}\r
e11c5548 1628\r
a12e0116 1629static void menu_prepare_bg(int use_game_bg)\r
1630{\r
1631 if (use_game_bg)\r
1632 {\r
1633 // darken the active framebuffer\r
1634 memset(gp2x_screen, 0, 320*8*2);\r
a4f0cc86 1635 menu_darken_bg((char *)gp2x_screen + 320*8*2, 320*224, 1);\r
a12e0116 1636 memset((char *)gp2x_screen + 320*232*2, 0, 320*8*2);\r
1637 }\r
1638 else\r
1639 {\r
1640 // should really only happen once, on startup..\r
1641 readpng(gp2x_screen, "skin/background.png", READPNG_BG);\r
860c6322 1642 }\r
cc68a136 1643\r
a12e0116 1644 // copy to buffer2\r
1645 gp2x_memcpy_buffers((1<<2), gp2x_screen, 0, 320*240*2);\r
860c6322 1646}\r
1647\r
1648static void menu_gfx_prepare(void)\r
1649{\r
eacee137 1650 menu_prepare_bg(rom_loaded);\r
860c6322 1651\r
a12e0116 1652 // switch to 16bpp\r
1653 gp2x_video_changemode2(16);\r
2433f409 1654 gp2x_video_RGB_setscaling(0, 320, 240);\r
a12e0116 1655 menu_flip();\r
e11c5548 1656}\r
1657\r
1658\r
1659void menu_loop(void)\r
1660{\r
1661 menu_gfx_prepare();\r
cc68a136 1662\r
1663 menu_loop_root();\r
1664\r
1665 menuErrorMsg[0] = 0;\r
1666}\r
721cd396 1667\r
1668\r
1669// --------- CD tray close menu ----------\r
1670\r
1671static void draw_menu_tray(int menu_sel)\r
1672{\r
1673 int tl_x = 70, tl_y = 90, y;\r
a12e0116 1674 memset(gp2x_screen, 0, 320*240*2);\r
721cd396 1675\r
a12e0116 1676 text_out16(tl_x, 20, "The unit is about to");\r
1677 text_out16(tl_x, 30, "close the CD tray.");\r
721cd396 1678\r
1679 y = tl_y;\r
a12e0116 1680 text_out16(tl_x, y, "Load new CD image");\r
1681 text_out16(tl_x, (y+=10), "Insert nothing");\r
721cd396 1682\r
1683 // draw cursor\r
a12e0116 1684 text_out16(tl_x - 16, tl_y + menu_sel*10, ">");\r
721cd396 1685 // error\r
a12e0116 1686 if (menuErrorMsg[0]) text_out16(5, 226, menuErrorMsg);\r
1687 menu_flip();\r
721cd396 1688}\r
1689\r
1690\r
1691int menu_loop_tray(void)\r
1692{\r
1693 int menu_sel = 0, menu_sel_max = 1;\r
1694 unsigned long inp = 0;\r
1695 char curr_path[PATH_MAX], *selfname;\r
1696 FILE *tstf;\r
1697\r
e5f426aa 1698 gp2x_memset_all_buffers(0, 0, 320*240*2);\r
721cd396 1699 menu_gfx_prepare();\r
1700\r
1ca2ea4f 1701 if ( (tstf = fopen(lastRomFile, "rb")) )\r
721cd396 1702 {\r
1703 fclose(tstf);\r
1ca2ea4f 1704 strcpy(curr_path, lastRomFile);\r
721cd396 1705 }\r
1706 else\r
1707 {\r
1708 getcwd(curr_path, PATH_MAX);\r
1709 }\r
1710\r
1711 /* make sure action buttons are not pressed on entering menu */\r
1712 draw_menu_tray(menu_sel);\r
1713 while (gp2x_joystick_read(1) & GP2X_B) usleep(50*1000);\r
1714\r
1715 for (;;)\r
1716 {\r
1717 draw_menu_tray(menu_sel);\r
1718 inp = wait_for_input(GP2X_UP|GP2X_DOWN|GP2X_B);\r
1719 if(inp & GP2X_UP ) { menu_sel--; if (menu_sel < 0) menu_sel = menu_sel_max; }\r
1720 if(inp & GP2X_DOWN) { menu_sel++; if (menu_sel > menu_sel_max) menu_sel = 0; }\r
1721 if(inp & GP2X_B ) {\r
1722 switch (menu_sel) {\r
1723 case 0: // select image\r
1724 selfname = romsel_loop(curr_path);\r
1725 if (selfname) {\r
1726 int ret = -1, cd_type;\r
ea8c405f 1727 cd_type = emu_cdCheck(NULL);\r
721cd396 1728 if (cd_type > 0)\r
1729 ret = Insert_CD(romFileName, cd_type == 2);\r
1730 if (ret != 0) {\r
1731 sprintf(menuErrorMsg, "Load failed, invalid CD image?");\r
1732 printf("%s\n", menuErrorMsg);\r
1733 continue;\r
1734 }\r
1735 engineState = PGS_RestartRun;\r
1736 return 1;\r
1737 }\r
1738 break;\r
1739 case 1: // insert nothing\r
1740 engineState = PGS_RestartRun;\r
1741 return 0;\r
1742 }\r
1743 }\r
1744 menuErrorMsg[0] = 0; // clear error msg\r
1745 }\r
1746}\r
1747\r
1748\r