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