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