FAMEC idle loops, PSP port sync, minor adjustments
[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
c060a9ab 435void PicoDrawShowSpriteStats(unsigned short *screen, int stride);\r
436void PicoDrawShowPalette(unsigned short *screen, int stride);\r
0af33fe0 437\r
fbc65db7 438static void draw_main_debug(void)\r
0af33fe0 439{\r
440 char *p, *str = debugString();\r
441 int len, line;\r
442\r
443 gp2x_pd_clone_buffer2();\r
444\r
445 p = str;\r
446 for (line = 0; line < 24; line++)\r
447 {\r
448 while (*p && *p != '\n') p++;\r
449 len = p - str;\r
450 if (len > 55) len = 55;\r
a12e0116 451 smalltext_out16_lim(1, line*10, str, 0xffff, len);\r
0af33fe0 452 if (*p == 0) break;\r
453 p++; str = p;\r
454 }\r
fbc65db7 455}\r
456\r
457static void draw_frame_debug(void)\r
458{\r
459 char layer_str[48] = "layers: ";\r
460 if (PicoDrawMask & PDRAW_LAYERB_ON) memcpy(layer_str + 8, "B", 1);\r
461 if (PicoDrawMask & PDRAW_LAYERA_ON) memcpy(layer_str + 10, "A", 1);\r
462 if (PicoDrawMask & PDRAW_SPRITES_LOW_ON) memcpy(layer_str + 12, "spr_lo", 6);\r
463 if (PicoDrawMask & PDRAW_SPRITES_HI_ON) memcpy(layer_str + 19, "spr_hi", 6);\r
464\r
465 memset(gp2x_screen, 0, 320*240*2);\r
0fc0e241 466 emu_forcedFrame(0);\r
fbc65db7 467 smalltext_out16(4, 232, layer_str, 0xffff);\r
0af33fe0 468}\r
469\r
470static void debug_menu_loop(void)\r
471{\r
fbc65db7 472 int inp, mode = 0;\r
473\r
474 while (1)\r
475 {\r
476 switch (mode)\r
477 {\r
478 case 0: draw_main_debug(); break;\r
479 case 1: draw_frame_debug(); break;\r
c060a9ab 480 case 2: gp2x_pd_clone_buffer2();\r
481 PicoDrawShowSpriteStats(gp2x_screen, 320); break;\r
482 case 3: memset(gp2x_screen, 0, 320*240*2);\r
483 PicoDrawShowPalette(gp2x_screen, 320); break;\r
fbc65db7 484 }\r
485 menu_flip();\r
486\r
487 inp = wait_for_input(GP2X_B|GP2X_X|GP2X_L|GP2X_R|GP2X_UP|GP2X_DOWN|GP2X_LEFT|GP2X_RIGHT);\r
488 if (inp & (GP2X_B|GP2X_X)) return;\r
489 if (inp & GP2X_L) { mode--; if (mode < 0) mode = 3; }\r
490 if (inp & GP2X_R) { mode++; if (mode > 3) mode = 0; }\r
491 if (mode == 1) {\r
492 if (inp & GP2X_LEFT) PicoDrawMask ^= PDRAW_LAYERB_ON;\r
493 if (inp & GP2X_RIGHT) PicoDrawMask ^= PDRAW_LAYERA_ON;\r
494 if (inp & GP2X_DOWN) PicoDrawMask ^= PDRAW_SPRITES_LOW_ON;\r
495 if (inp & GP2X_UP) PicoDrawMask ^= PDRAW_SPRITES_HI_ON;\r
496 }\r
497 }\r
0af33fe0 498}\r
499\r
b67ef287 500// ------------ patch/gg menu ------------\r
501\r
502static void draw_patchlist(int sel)\r
503{\r
a12e0116 504 int start, i, pos, active;\r
b67ef287 505\r
506 start = 12 - sel;\r
507\r
508 gp2x_pd_clone_buffer2();\r
509\r
510 for (i = 0; i < PicoPatchCount; i++) {\r
511 pos = start + i;\r
512 if (pos < 0) continue;\r
513 if (pos > 23) break;\r
a12e0116 514 active = PicoPatches[i].active;\r
515 smalltext_out16_lim(14, pos*10, active ? "ON " : "OFF", active ? 0xfff6 : 0xffff, 3);\r
516 smalltext_out16_lim(14+6*4, pos*10, PicoPatches[i].name, active ? 0xfff6 : 0xffff, 53-6);\r
b67ef287 517 }\r
518 pos = start + i;\r
a12e0116 519 if (pos < 24) smalltext_out16_lim(14, pos*10, "done", 0xffff, 4);\r
b67ef287 520\r
a12e0116 521 text_out16(5, 120, ">");\r
522 menu_flip();\r
b67ef287 523}\r
524\r
525\r
0af33fe0 526static void patches_menu_loop(void)\r
b67ef287 527{\r
528 int menu_sel = 0;\r
529 unsigned long inp = 0;\r
530\r
531 for(;;)\r
532 {\r
533 draw_patchlist(menu_sel);\r
534 inp = wait_for_input(GP2X_UP|GP2X_DOWN|GP2X_LEFT|GP2X_RIGHT|GP2X_L|GP2X_R|GP2X_B|GP2X_X);\r
535 if(inp & GP2X_UP ) { menu_sel--; if (menu_sel < 0) menu_sel = PicoPatchCount; }\r
536 if(inp & GP2X_DOWN) { menu_sel++; if (menu_sel > PicoPatchCount) menu_sel = 0; }\r
537 if(inp &(GP2X_LEFT|GP2X_L)) { menu_sel-=10; if (menu_sel < 0) menu_sel = 0; }\r
538 if(inp &(GP2X_RIGHT|GP2X_R)) { menu_sel+=10; if (menu_sel > PicoPatchCount) menu_sel = PicoPatchCount; }\r
539 if(inp & GP2X_B) { // action\r
540 if (menu_sel < PicoPatchCount)\r
541 PicoPatches[menu_sel].active = !PicoPatches[menu_sel].active;\r
542 else return;\r
543 }\r
544 if(inp & GP2X_X) return;\r
545 }\r
546\r
547}\r
548\r
860c6322 549// ------------ savestate loader ------------\r
550\r
860c6322 551static int state_slot_flags = 0;\r
552\r
553static void state_check_slots(void)\r
554{\r
555 int slot;\r
556\r
557 state_slot_flags = 0;\r
558\r
559 for (slot = 0; slot < 10; slot++)\r
560 {\r
ea8c405f 561 if (emu_checkSaveFile(slot))\r
860c6322 562 {\r
563 state_slot_flags |= 1 << slot;\r
564 }\r
565 }\r
566}\r
567\r
568static void draw_savestate_bg(int slot)\r
569{\r
570 struct PicoVideo tmp_pv;\r
571 unsigned short tmp_cram[0x40];\r
572 unsigned short tmp_vsram[0x40];\r
573 void *tmp_vram, *file;\r
574 char *fname;\r
575\r
576 fname = emu_GetSaveFName(1, 0, slot);\r
577 if (!fname) return;\r
578\r
579 tmp_vram = malloc(sizeof(Pico.vram));\r
580 if (tmp_vram == NULL) return;\r
581\r
582 memcpy(tmp_vram, Pico.vram, sizeof(Pico.vram));\r
583 memcpy(tmp_cram, Pico.cram, sizeof(Pico.cram));\r
584 memcpy(tmp_vsram, Pico.vsram, sizeof(Pico.vsram));\r
585 memcpy(&tmp_pv, &Pico.video, sizeof(Pico.video));\r
586\r
587 if (strcmp(fname + strlen(fname) - 3, ".gz") == 0) {\r
588 file = gzopen(fname, "rb");\r
ea8c405f 589 emu_setSaveStateCbs(1);\r
860c6322 590 } else {\r
591 file = fopen(fname, "rb");\r
ea8c405f 592 emu_setSaveStateCbs(0);\r
860c6322 593 }\r
594\r
595 if (file) {\r
602133e1 596 if (PicoAHW & PAHW_MCD) {\r
860c6322 597 PicoCdLoadStateGfx(file);\r
598 } else {\r
599 areaSeek(file, 0x10020, SEEK_SET); // skip header and RAM in state file\r
600 areaRead(Pico.vram, 1, sizeof(Pico.vram), file);\r
601 areaSeek(file, 0x2000, SEEK_CUR);\r
602 areaRead(Pico.cram, 1, sizeof(Pico.cram), file);\r
603 areaRead(Pico.vsram, 1, sizeof(Pico.vsram), file);\r
604 areaSeek(file, 0x221a0, SEEK_SET);\r
605 areaRead(&Pico.video, 1, sizeof(Pico.video), file);\r
606 }\r
607 areaClose(file);\r
608 }\r
609\r
0fc0e241 610 emu_forcedFrame(POPT_EN_SOFTSCALE);\r
a12e0116 611 menu_prepare_bg(1);\r
860c6322 612\r
613 memcpy(Pico.vram, tmp_vram, sizeof(Pico.vram));\r
614 memcpy(Pico.cram, tmp_cram, sizeof(Pico.cram));\r
615 memcpy(Pico.vsram, tmp_vsram, sizeof(Pico.vsram));\r
616 memcpy(&Pico.video, &tmp_pv, sizeof(Pico.video));\r
617 free(tmp_vram);\r
618}\r
619\r
620static void draw_savestate_menu(int menu_sel, int is_loading)\r
621{\r
622 int tl_x = 25, tl_y = 60, y, i;\r
623\r
624 if (state_slot_flags & (1 << menu_sel))\r
625 draw_savestate_bg(menu_sel);\r
626 gp2x_pd_clone_buffer2();\r
627\r
a12e0116 628 text_out16(tl_x, 30, is_loading ? "Load state" : "Save state");\r
629\r
e5f426aa 630 menu_draw_selection(tl_x - 16, tl_y + menu_sel*10, 108);\r
860c6322 631\r
632 /* draw all 10 slots */\r
633 y = tl_y;\r
634 for (i = 0; i < 10; i++, y+=10)\r
635 {\r
a12e0116 636 text_out16(tl_x, y, "SLOT %i (%s)", i, (state_slot_flags & (1 << i)) ? "USED" : "free");\r
860c6322 637 }\r
a12e0116 638 text_out16(tl_x, y, "back");\r
860c6322 639\r
a12e0116 640 menu_flip();\r
860c6322 641}\r
642\r
643static int savestate_menu_loop(int is_loading)\r
644{\r
aae35e84 645 static int menu_sel = 10;\r
646 int menu_sel_max = 10;\r
860c6322 647 unsigned long inp = 0;\r
648\r
649 state_check_slots();\r
650\r
651 for(;;)\r
652 {\r
653 draw_savestate_menu(menu_sel, is_loading);\r
654 inp = wait_for_input(GP2X_UP|GP2X_DOWN|GP2X_B|GP2X_X);\r
655 if(inp & GP2X_UP ) {\r
656 do {\r
657 menu_sel--; if (menu_sel < 0) menu_sel = menu_sel_max;\r
658 } while (!(state_slot_flags & (1 << menu_sel)) && menu_sel != menu_sel_max && is_loading);\r
659 }\r
660 if(inp & GP2X_DOWN) {\r
661 do {\r
662 menu_sel++; if (menu_sel > menu_sel_max) menu_sel = 0;\r
663 } while (!(state_slot_flags & (1 << menu_sel)) && menu_sel != menu_sel_max && is_loading);\r
664 }\r
665 if(inp & GP2X_B) { // save/load\r
666 if (menu_sel < 10) {\r
667 state_slot = menu_sel;\r
668 if (emu_SaveLoadGame(is_loading, 0)) {\r
669 strcpy(menuErrorMsg, is_loading ? "Load failed" : "Save failed");\r
670 return 1;\r
671 }\r
672 return 0;\r
673 } else return 1;\r
674 }\r
675 if(inp & GP2X_X) return 1;\r
676 }\r
677}\r
678\r
cc68a136 679// -------------- key config --------------\r
680\r
681static char *usb_joy_key_name(int joy, int num)\r
682{\r
683 static char name[16];\r
684 switch (num)\r
685 {\r
686 case 0: sprintf(name, "Joy%i UP", joy); break;\r
687 case 1: sprintf(name, "Joy%i DOWN", joy); break;\r
688 case 2: sprintf(name, "Joy%i LEFT", joy); break;\r
689 case 3: sprintf(name, "Joy%i RIGHT", joy); break;\r
690 default:sprintf(name, "Joy%i b%i", joy, num-3); break;\r
691 }\r
692 return name;\r
693}\r
694\r
d524c827 695static char *action_binds(int player_idx, int action_mask)\r
cc68a136 696{\r
d524c827 697 static char strkeys[32*5];\r
cc68a136 698 int joy, i;\r
699\r
700 strkeys[0] = 0;\r
d524c827 701 for (i = 0; i < 32; i++) // i is key index\r
cc68a136 702 {\r
d524c827 703 if (currentConfig.KeyBinds[i] & action_mask)\r
cc68a136 704 {\r
d524c827 705 if (player_idx >= 0 && ((currentConfig.KeyBinds[i] >> 16) & 3) != player_idx) continue;\r
1ca2ea4f 706 if (strkeys[0]) { strcat(strkeys, " + "); strcat(strkeys, keyNames[i]); break; }\r
707 else strcpy(strkeys, keyNames[i]);\r
cc68a136 708 }\r
709 }\r
710 for (joy = 0; joy < num_of_joys; joy++)\r
711 {\r
712 for (i = 0; i < 32; i++)\r
713 {\r
d524c827 714 if (currentConfig.JoyBinds[joy][i] & action_mask)\r
cc68a136 715 {\r
d524c827 716 if (player_idx >= 0 && ((currentConfig.JoyBinds[joy][i] >> 16) & 3) != player_idx) continue;\r
cc68a136 717 if (strkeys[0]) {\r
718 strcat(strkeys, ", "); strcat(strkeys, usb_joy_key_name(joy + 1, i));\r
719 break;\r
720 }\r
721 else strcpy(strkeys, usb_joy_key_name(joy + 1, i));\r
722 }\r
723 }\r
724 }\r
725\r
45499284 726 // limit..\r
727 strkeys[20] = 0;\r
728\r
d524c827 729 return strkeys;\r
730}\r
731\r
45499284 732static void unbind_action(int action, int pl_idx, int joy)\r
d524c827 733{\r
734 int i, u;\r
735\r
45499284 736 if (joy <= 0)\r
737 {\r
738 for (i = 0; i < 32; i++) {\r
739 if (pl_idx >= 0 && (currentConfig.KeyBinds[i]&0x30000) != (pl_idx<<16)) continue;\r
740 currentConfig.KeyBinds[i] &= ~action;\r
741 }\r
b4fe3a4a 742 }\r
45499284 743 if (joy < 0)\r
744 {\r
745 for (u = 0; u < 4; u++)\r
746 for (i = 0; i < 32; i++) {\r
747 if (pl_idx >= 0 && (currentConfig.JoyBinds[u][i]&0x30000) != (pl_idx<<16)) continue;\r
748 currentConfig.JoyBinds[u][i] &= ~action;\r
749 }\r
750 }\r
751 else if (joy > 0)\r
752 {\r
b4fe3a4a 753 for (i = 0; i < 32; i++) {\r
45499284 754 if (pl_idx >= 0 && (currentConfig.JoyBinds[joy-1][i]&0x30000) != (pl_idx<<16)) continue;\r
755 currentConfig.JoyBinds[joy-1][i] &= ~action;\r
b4fe3a4a 756 }\r
45499284 757 }\r
d524c827 758}\r
759\r
b4fe3a4a 760static int count_bound_keys(int action, int pl_idx, int joy)\r
d524c827 761{\r
762 int i, keys = 0;\r
763\r
764 if (joy)\r
765 {\r
b4fe3a4a 766 for (i = 0; i < 32; i++) {\r
767 if (pl_idx >= 0 && (currentConfig.JoyBinds[joy-1][i]&0x30000) != (pl_idx<<16)) continue;\r
d524c827 768 if (currentConfig.JoyBinds[joy-1][i] & action) keys++;\r
b4fe3a4a 769 }\r
d524c827 770 }\r
771 else\r
772 {\r
b4fe3a4a 773 for (i = 0; i < 32; i++) {\r
774 if (pl_idx >= 0 && (currentConfig.KeyBinds[i]&0x30000) != (pl_idx<<16)) continue;\r
d524c827 775 if (currentConfig.KeyBinds[i] & action) keys++;\r
b4fe3a4a 776 }\r
d524c827 777 }\r
778 return keys;\r
779}\r
780\r
1ca2ea4f 781static void draw_key_config(const me_bind_action *opts, int opt_cnt, int player_idx, int sel)\r
d524c827 782{\r
783 int x, y, tl_y = 40, i;\r
784\r
e11c5548 785 gp2x_pd_clone_buffer2();\r
d524c827 786 if (player_idx >= 0) {\r
a12e0116 787 text_out16(80, 20, "Player %i controls", player_idx + 1);\r
b4fe3a4a 788 x = 80;\r
d524c827 789 } else {\r
a12e0116 790 text_out16(80, 20, "Emulator controls");\r
d524c827 791 x = 40;\r
792 }\r
793\r
406c96c5 794 menu_draw_selection(x - 16, tl_y + sel*10, (player_idx >= 0) ? 66 : 140);\r
a12e0116 795\r
d524c827 796 y = tl_y;\r
797 for (i = 0; i < opt_cnt; i++, y+=10)\r
a12e0116 798 text_out16(x, y, "%s : %s", opts[i].name, action_binds(player_idx, opts[i].mask));\r
d524c827 799\r
a12e0116 800 text_out16(x, y, "Done");\r
d524c827 801\r
802 if (sel < opt_cnt) {\r
a12e0116 803 text_out16(30, 180, "Press a button to bind/unbind");\r
804 text_out16(30, 190, "Use SELECT to clear");\r
805 text_out16(30, 200, "To bind UP/DOWN, hold SELECT");\r
806 text_out16(30, 210, "Select \"Done\" to exit");\r
d524c827 807 } else {\r
a12e0116 808 text_out16(30, 190, "Use Options -> Save cfg");\r
809 text_out16(30, 200, "to save controls");\r
810 text_out16(30, 210, "Press B or X to exit");\r
d524c827 811 }\r
a12e0116 812 menu_flip();\r
cc68a136 813}\r
814\r
1ca2ea4f 815static void key_config_loop(const me_bind_action *opts, int opt_cnt, int player_idx)\r
cc68a136 816{\r
d524c827 817 int joy = 0, sel = 0, menu_sel_max = opt_cnt, prev_select = 0, i;\r
cc68a136 818 unsigned long inp = 0;\r
819\r
820 for (;;)\r
821 {\r
d524c827 822 draw_key_config(opts, opt_cnt, player_idx, sel);\r
cc68a136 823 inp = wait_for_input_usbjoy(CONFIGURABLE_KEYS, &joy);\r
824 // printf("got %08lX from joy %i\n", inp, joy);\r
a39b7867 825 if (joy == 0)\r
826 {\r
d524c827 827 if (!(inp & GP2X_SELECT)) {\r
828 prev_select = 0;\r
829 if(inp & GP2X_UP ) { sel--; if (sel < 0) sel = menu_sel_max; continue; }\r
830 if(inp & GP2X_DOWN) { sel++; if (sel > menu_sel_max) sel = 0; continue; }\r
cc68a136 831 }\r
d524c827 832 if (sel >= opt_cnt) {\r
833 if (inp & (GP2X_B|GP2X_X)) break;\r
834 else continue;\r
835 }\r
836 // if we are here, we want to bind/unbind something\r
837 if ((inp & GP2X_SELECT) && !prev_select)\r
45499284 838 unbind_action(opts[sel].mask, player_idx, -1);\r
d524c827 839 prev_select = inp & GP2X_SELECT;\r
cc68a136 840 inp &= CONFIGURABLE_KEYS;\r
841 inp &= ~GP2X_SELECT;\r
cc68a136 842 for (i = 0; i < 32; i++)\r
843 if (inp & (1 << i)) {\r
b4fe3a4a 844 if (count_bound_keys(opts[sel].mask, player_idx, 0) >= 2)\r
d524c827 845 currentConfig.KeyBinds[i] &= ~opts[sel].mask; // allow to unbind only\r
846 else currentConfig.KeyBinds[i] ^= opts[sel].mask;\r
c9077ab4 847 if (player_idx >= 0 && (currentConfig.KeyBinds[i] & opts[sel].mask)) {\r
d524c827 848 currentConfig.KeyBinds[i] &= ~(3 << 16);\r
849 currentConfig.KeyBinds[i] |= player_idx << 16;\r
850 }\r
cc68a136 851 }\r
d524c827 852 }\r
853 else if (sel < opt_cnt)\r
854 {\r
cc68a136 855 for (i = 0; i < 32; i++)\r
856 if (inp & (1 << i)) {\r
45499284 857 int *bind = &currentConfig.JoyBinds[joy-1][i];\r
858 if ((*bind & opts[sel].mask) && (player_idx < 0 || player_idx == ((*bind>>16)&3)))\r
1ca2ea4f 859 *bind &= ~opts[sel].mask;\r
45499284 860 else {\r
861 // override\r
862 unbind_action(opts[sel].mask, player_idx, joy);\r
863 *bind = opts[sel].mask;\r
1ca2ea4f 864 if (player_idx > 0) *bind |= player_idx << 16;\r
d524c827 865 }\r
cc68a136 866 }\r
867 }\r
868 }\r
869}\r
870\r
871static void draw_kc_sel(int menu_sel)\r
872{\r
873 int tl_x = 25+40, tl_y = 60, y, i;\r
874 char joyname[36];\r
875\r
876 y = tl_y;\r
e11c5548 877 gp2x_pd_clone_buffer2();\r
e5f426aa 878 menu_draw_selection(tl_x - 16, tl_y + menu_sel*10, 138);\r
cc68a136 879\r
a12e0116 880 text_out16(tl_x, y, "Player 1");\r
881 text_out16(tl_x, (y+=10), "Player 2");\r
882 text_out16(tl_x, (y+=10), "Emulator controls");\r
883 text_out16(tl_x, (y+=10), "Done");\r
cc68a136 884\r
885 tl_x = 25;\r
a12e0116 886 text_out16(tl_x, (y=110), "USB joys detected:");\r
cc68a136 887 if (num_of_joys > 0) {\r
888 for (i = 0; i < num_of_joys; i++) {\r
889 strncpy(joyname, joy_name(joys[i]), 33); joyname[33] = 0;\r
a12e0116 890 text_out16(tl_x, (y+=10), "%i: %s", i+1, joyname);\r
cc68a136 891 }\r
892 } else {\r
a12e0116 893 text_out16(tl_x, (y+=10), "none");\r
cc68a136 894 }\r
895\r
a12e0116 896 menu_flip();\r
cc68a136 897}\r
898\r
d524c827 899\r
406c96c5 900// player2_flag, reserved, ?, ?,\r
901// ?, ?, fast forward, menu\r
d524c827 902// "NEXT SAVE SLOT", "PREV SAVE SLOT", "SWITCH RENDERER", "SAVE STATE",\r
903// "LOAD STATE", "VOLUME UP", "VOLUME DOWN", "DONE"\r
1ca2ea4f 904me_bind_action emuctrl_actions[] =\r
d524c827 905{\r
406c96c5 906 { "Load State ", 1<<28 },\r
907 { "Save State ", 1<<27 },\r
908 { "Prev Save Slot ", 1<<25 },\r
909 { "Next Save Slot ", 1<<24 },\r
910 { "Switch Renderer ", 1<<26 },\r
911 { "Volume Down ", 1<<30 },\r
912 { "Volume Up ", 1<<29 },\r
913 { "Fast forward ", 1<<22 },\r
914 { "Enter Menu ", 1<<23 },\r
915 { "Pico Next page ", 1<<21 },\r
916 { "Pico Prev page ", 1<<20 },\r
917 { "Pico Switch input", 1<<19 },\r
918 { NULL, 0 }\r
d524c827 919};\r
920\r
cc68a136 921static void kc_sel_loop(void)\r
922{\r
d524c827 923 int menu_sel = 3, menu_sel_max = 3;\r
cc68a136 924 unsigned long inp = 0;\r
602133e1 925 int is_6button = PicoOpt & POPT_6BTN_PAD;\r
cc68a136 926\r
d524c827 927 while (1)\r
cc68a136 928 {\r
929 draw_kc_sel(menu_sel);\r
930 inp = wait_for_input(GP2X_UP|GP2X_DOWN|GP2X_B|GP2X_X);\r
d524c827 931 if (inp & GP2X_UP ) { menu_sel--; if (menu_sel < 0) menu_sel = menu_sel_max; }\r
932 if (inp & GP2X_DOWN) { menu_sel++; if (menu_sel > menu_sel_max) menu_sel = 0; }\r
933 if (inp & GP2X_B) {\r
cc68a136 934 switch (menu_sel) {\r
1ca2ea4f 935 case 0: key_config_loop(me_ctrl_actions, is_6button ? 12 : 8, 0); return;\r
936 case 1: key_config_loop(me_ctrl_actions, is_6button ? 12 : 8, 1); return;\r
d524c827 937 case 2: key_config_loop(emuctrl_actions,\r
bdec53c9 938 sizeof(emuctrl_actions)/sizeof(emuctrl_actions[0]) - 1, -1); return;\r
eacee137 939 case 3: if (!rom_loaded) emu_WriteConfig(0); return;\r
cc68a136 940 default: return;\r
941 }\r
942 }\r
d524c827 943 if (inp & GP2X_X) return;\r
cc68a136 944 }\r
945}\r
946\r
947\r
bf098bc5 948// --------- sega/mega cd options ----------\r
949\r
4e8a534c 950menu_entry cdopt_entries[] =\r
bf098bc5 951{\r
58c86d00 952 { NULL, MB_NONE, MA_CDOPT_TESTBIOS_USA, NULL, 0, 0, 0, 1, 0 },\r
953 { NULL, MB_NONE, MA_CDOPT_TESTBIOS_EUR, NULL, 0, 0, 0, 1, 0 },\r
954 { NULL, MB_NONE, MA_CDOPT_TESTBIOS_JAP, NULL, 0, 0, 0, 1, 0 },\r
955 { "CD LEDs", MB_ONOFF, MA_CDOPT_LEDS, &currentConfig.EmuOpt, 0x0400, 0, 0, 1, 1 },\r
956 { "CDDA audio (using mp3s)", MB_ONOFF, MA_CDOPT_CDDA, &PicoOpt, 0x0800, 0, 0, 1, 1 },\r
957 { "PCM audio", MB_ONOFF, MA_CDOPT_PCM, &PicoOpt, 0x0400, 0, 0, 1, 1 },\r
958 { NULL, MB_NONE, MA_CDOPT_READAHEAD, NULL, 0, 0, 0, 1, 1 },\r
959 { "SaveRAM cart", MB_ONOFF, MA_CDOPT_SAVERAM, &PicoOpt, 0x8000, 0, 0, 1, 1 },\r
960 { "Scale/Rot. fx (slow)", MB_ONOFF, MA_CDOPT_SCALEROT_CHIP,&PicoOpt, 0x1000, 0, 0, 1, 1 },\r
961 { "Better sync (slow)", MB_ONOFF, MA_CDOPT_BETTER_SYNC, &PicoOpt, 0x2000, 0, 0, 1, 1 },\r
962 { "done", MB_NONE, MA_CDOPT_DONE, NULL, 0, 0, 0, 1, 0 },\r
4e8a534c 963};\r
964\r
965#define CDOPT_ENTRY_COUNT (sizeof(cdopt_entries) / sizeof(cdopt_entries[0]))\r
f9d3ee9d 966const int cdopt_entry_count = CDOPT_ENTRY_COUNT;\r
4e8a534c 967\r
968\r
969struct bios_names_t\r
970{\r
971 char us[32], eu[32], jp[32];\r
972};\r
973\r
974static void menu_cdopt_cust_draw(const menu_entry *entry, int x, int y, void *param)\r
975{\r
976 struct bios_names_t *bios_names = param;\r
977 char ra_buff[16];\r
978\r
979 switch (entry->id)\r
980 {\r
a12e0116 981 case MA_CDOPT_TESTBIOS_USA: text_out16(x, y, "USA BIOS: %s", bios_names->us); break;\r
982 case MA_CDOPT_TESTBIOS_EUR: text_out16(x, y, "EUR BIOS: %s", bios_names->eu); break;\r
983 case MA_CDOPT_TESTBIOS_JAP: text_out16(x, y, "JAP BIOS: %s", bios_names->jp); break;\r
4e8a534c 984 case MA_CDOPT_READAHEAD:\r
985 if (PicoCDBuffers > 1) sprintf(ra_buff, "%5iK", PicoCDBuffers * 2);\r
986 else strcpy(ra_buff, " OFF");\r
a12e0116 987 text_out16(x, y, "ReadAhead buffer %s", ra_buff);\r
4e8a534c 988 break;\r
989 default:break;\r
990 }\r
991}\r
992\r
993static void draw_cd_menu_options(int menu_sel, struct bios_names_t *bios_names)\r
994{\r
995 int tl_x = 25, tl_y = 60;\r
996 menu_id selected_id;\r
0a051f55 997 char ra_buff[16];\r
998\r
999 if (PicoCDBuffers > 1) sprintf(ra_buff, "%5iK", PicoCDBuffers * 2);\r
1000 else strcpy(ra_buff, " OFF");\r
bf098bc5 1001\r
e11c5548 1002 gp2x_pd_clone_buffer2();\r
1003\r
e5f426aa 1004 menu_draw_selection(tl_x - 16, tl_y + menu_sel*10, 246);\r
bf098bc5 1005\r
a12e0116 1006 me_draw(cdopt_entries, CDOPT_ENTRY_COUNT, tl_x, tl_y, menu_cdopt_cust_draw, bios_names);\r
bf098bc5 1007\r
4e8a534c 1008 selected_id = me_index2id(cdopt_entries, CDOPT_ENTRY_COUNT, menu_sel);\r
1009 if ((selected_id == MA_CDOPT_TESTBIOS_USA && strcmp(bios_names->us, "NOT FOUND")) ||\r
1010 (selected_id == MA_CDOPT_TESTBIOS_EUR && strcmp(bios_names->eu, "NOT FOUND")) ||\r
1011 (selected_id == MA_CDOPT_TESTBIOS_JAP && strcmp(bios_names->jp, "NOT FOUND")))\r
a12e0116 1012 text_out16(tl_x, 210, "Press start to test selected BIOS");\r
bf098bc5 1013\r
a12e0116 1014 menu_flip();\r
bf098bc5 1015}\r
1016\r
1017static void cd_menu_loop_options(void)\r
1018{\r
aae35e84 1019 static int menu_sel = 0;\r
1020 int menu_sel_max = 10;\r
bf098bc5 1021 unsigned long inp = 0;\r
4e8a534c 1022 struct bios_names_t bios_names;\r
1023 menu_id selected_id;\r
1024 char *bios, *p;\r
bf098bc5 1025\r
ea8c405f 1026 if (emu_findBios(4, &bios)) { // US\r
bf098bc5 1027 for (p = bios+strlen(bios)-1; p > bios && *p != '/'; p--); p++;\r
4e8a534c 1028 strncpy(bios_names.us, p, sizeof(bios_names.us)); bios_names.us[sizeof(bios_names.us)-1] = 0;\r
1029 } else strcpy(bios_names.us, "NOT FOUND");\r
bf098bc5 1030\r
ea8c405f 1031 if (emu_findBios(8, &bios)) { // EU\r
bf098bc5 1032 for (p = bios+strlen(bios)-1; p > bios && *p != '/'; p--); p++;\r
4e8a534c 1033 strncpy(bios_names.eu, p, sizeof(bios_names.eu)); bios_names.eu[sizeof(bios_names.eu)-1] = 0;\r
1034 } else strcpy(bios_names.eu, "NOT FOUND");\r
bf098bc5 1035\r
ea8c405f 1036 if (emu_findBios(1, &bios)) { // JP\r
bf098bc5 1037 for (p = bios+strlen(bios)-1; p > bios && *p != '/'; p--); p++;\r
4e8a534c 1038 strncpy(bios_names.jp, p, sizeof(bios_names.jp)); bios_names.jp[sizeof(bios_names.jp)-1] = 0;\r
1039 } else strcpy(bios_names.jp, "NOT FOUND");\r
bf098bc5 1040\r
1041 for(;;)\r
1042 {\r
4e8a534c 1043 draw_cd_menu_options(menu_sel, &bios_names);\r
bf098bc5 1044 inp = wait_for_input(GP2X_UP|GP2X_DOWN|GP2X_LEFT|GP2X_RIGHT|GP2X_B|GP2X_X|GP2X_A|GP2X_START);\r
4e8a534c 1045 if (inp & GP2X_UP ) { menu_sel--; if (menu_sel < 0) menu_sel = menu_sel_max; }\r
1046 if (inp & GP2X_DOWN) { menu_sel++; if (menu_sel > menu_sel_max) menu_sel = 0; }\r
1047 selected_id = me_index2id(cdopt_entries, CDOPT_ENTRY_COUNT, menu_sel);\r
1048 if (inp & (GP2X_LEFT|GP2X_RIGHT)) { // multi choise\r
1049 if (!me_process(cdopt_entries, CDOPT_ENTRY_COUNT, selected_id, (inp&GP2X_RIGHT) ? 1 : 0) &&\r
1050 selected_id == MA_CDOPT_READAHEAD) {\r
1051 if (inp & GP2X_LEFT) {\r
1052 PicoCDBuffers >>= 1;\r
2d2247c2 1053 if (PicoCDBuffers < 2) PicoCDBuffers = 0;\r
4e8a534c 1054 } else {\r
2d2247c2 1055 if (PicoCDBuffers < 2) PicoCDBuffers = 2;\r
4e8a534c 1056 else PicoCDBuffers <<= 1;\r
1057 if (PicoCDBuffers > 8*1024) PicoCDBuffers = 8*1024; // 16M\r
1058 }\r
bf098bc5 1059 }\r
1060 }\r
4e8a534c 1061 if (inp & GP2X_B) { // toggleable options\r
1062 if (!me_process(cdopt_entries, CDOPT_ENTRY_COUNT, selected_id, 1) &&\r
1063 selected_id == MA_CDOPT_DONE) {\r
1064 return;\r
1065 }\r
1066 }\r
1067 if (inp & GP2X_START) { // BIOS testers\r
1068 switch (selected_id) {\r
1069 case MA_CDOPT_TESTBIOS_USA:\r
ea8c405f 1070 if (emu_findBios(4, &bios)) { // test US\r
bf098bc5 1071 strcpy(romFileName, bios);\r
1072 engineState = PGS_ReloadRom;\r
1073 return;\r
1074 }\r
1075 break;\r
4e8a534c 1076 case MA_CDOPT_TESTBIOS_EUR:\r
ea8c405f 1077 if (emu_findBios(8, &bios)) { // test EU\r
bf098bc5 1078 strcpy(romFileName, bios);\r
1079 engineState = PGS_ReloadRom;\r
1080 return;\r
1081 }\r
1082 break;\r
4e8a534c 1083 case MA_CDOPT_TESTBIOS_JAP:\r
ea8c405f 1084 if (emu_findBios(1, &bios)) { // test JP\r
bf098bc5 1085 strcpy(romFileName, bios);\r
1086 engineState = PGS_ReloadRom;\r
1087 return;\r
1088 }\r
1089 break;\r
4e8a534c 1090 default:\r
1091 break;\r
bf098bc5 1092 }\r
1093 }\r
4e8a534c 1094 if (inp & (GP2X_X|GP2X_A)) return;\r
bf098bc5 1095 }\r
1096}\r
1097\r
1098\r
1099// --------- advanced options ----------\r
1100\r
4e8a534c 1101menu_entry opt2_entries[] =\r
1102{\r
58c86d00 1103 { NULL, MB_NONE, MA_OPT2_GAMMA, NULL, 0, 0, 0, 1, 1 },\r
1104 { "A_SN's gamma curve", MB_ONOFF, MA_OPT2_A_SN_GAMMA, &currentConfig.EmuOpt, 0x1000, 0, 0, 1, 1 },\r
1105 { "Perfect vsync", MB_ONOFF, MA_OPT2_VSYNC, &currentConfig.EmuOpt, 0x2000, 0, 0, 1, 1 },\r
f9d3ee9d 1106 { "Disable sprite limit", MB_ONOFF, MA_OPT2_NO_SPRITE_LIM, &PicoOpt, 0x40000, 0, 0, 1, 1 },\r
1ca2ea4f 1107 { "Emulate Z80", MB_ONOFF, MA_OPT2_ENABLE_Z80, &PicoOpt, 0x00004, 0, 0, 1, 1 },\r
1108 { "Emulate YM2612 (FM)", MB_ONOFF, MA_OPT2_ENABLE_YM2612, &PicoOpt, 0x00001, 0, 0, 1, 1 },\r
1109 { "Emulate SN76496 (PSG)", MB_ONOFF, MA_OPT2_ENABLE_SN76496,&PicoOpt, 0x00002, 0, 0, 1, 1 },\r
58c86d00 1110 { "gzip savestates", MB_ONOFF, MA_OPT2_GZIP_STATES, &currentConfig.EmuOpt, 0x0008, 0, 0, 1, 1 },\r
1111 { "Don't save last used ROM", MB_ONOFF, MA_OPT2_NO_LAST_ROM, &currentConfig.EmuOpt, 0x0020, 0, 0, 1, 1 },\r
1112 { "needs restart:", MB_NONE, MA_NONE, NULL, 0, 0, 0, 1, 0 },\r
1113 { "craigix's RAM timings", MB_ONOFF, MA_OPT2_RAMTIMINGS, &currentConfig.EmuOpt, 0x0100, 0, 0, 1, 1 },\r
1114 { NULL, MB_ONOFF, MA_OPT2_SQUIDGEHACK, &currentConfig.EmuOpt, 0x0010, 0, 0, 1, 1 },\r
1ca2ea4f 1115 { "SVP dynarec", MB_ONOFF, MA_OPT2_SVP_DYNAREC, &PicoOpt, 0x20000, 0, 0, 1, 1 },\r
053fd9b4 1116 { "Disable idle loop patching",MB_ONOFF, MA_OPT2_NO_IDLE_LOOPS, &PicoOpt, 0x80000, 0, 0, 1, 1 },\r
58c86d00 1117 { "done", MB_NONE, MA_OPT2_DONE, NULL, 0, 0, 0, 1, 0 },\r
4e8a534c 1118};\r
1119\r
1120#define OPT2_ENTRY_COUNT (sizeof(opt2_entries) / sizeof(opt2_entries[0]))\r
f9d3ee9d 1121const int opt2_entry_count = OPT2_ENTRY_COUNT;\r
4e8a534c 1122\r
1123static void menu_opt2_cust_draw(const menu_entry *entry, int x, int y, void *param)\r
1124{\r
1125 if (entry->id == MA_OPT2_GAMMA)\r
a12e0116 1126 text_out16(x, y, "Gamma correction %i.%02i", currentConfig.gamma / 100, currentConfig.gamma%100);\r
4e8a534c 1127 else if (entry->id == MA_OPT2_SQUIDGEHACK)\r
58c86d00 1128 text_out16(x, y, "Squidgehack (now %s %s", mmuhack_status ? "active) " : "inactive)",\r
4e8a534c 1129 (currentConfig.EmuOpt&0x0010)?"ON":"OFF");\r
1130}\r
1131\r
1132\r
cc68a136 1133static void draw_amenu_options(int menu_sel)\r
1134{\r
4e8a534c 1135 int tl_x = 25, tl_y = 50;\r
cc68a136 1136\r
e11c5548 1137 gp2x_pd_clone_buffer2();\r
1138\r
e5f426aa 1139 menu_draw_selection(tl_x - 16, tl_y + menu_sel*10, 252);\r
cc68a136 1140\r
a12e0116 1141 me_draw(opt2_entries, OPT2_ENTRY_COUNT, tl_x, tl_y, menu_opt2_cust_draw, NULL);\r
cc68a136 1142\r
a12e0116 1143 menu_flip();\r
cc68a136 1144}\r
1145\r
1146static void amenu_loop_options(void)\r
1147{\r
aae35e84 1148 static int menu_sel = 0;\r
4e8a534c 1149 int menu_sel_max;\r
cc68a136 1150 unsigned long inp = 0;\r
4e8a534c 1151 menu_id selected_id;\r
1152\r
1153 menu_sel_max = me_count_enabled(opt2_entries, OPT2_ENTRY_COUNT) - 1;\r
cc68a136 1154\r
1155 for(;;)\r
1156 {\r
1157 draw_amenu_options(menu_sel);\r
1158 inp = wait_for_input(GP2X_UP|GP2X_DOWN|GP2X_LEFT|GP2X_RIGHT|GP2X_B|GP2X_X|GP2X_A);\r
4e8a534c 1159 if (inp & GP2X_UP ) { menu_sel--; if (menu_sel < 0) menu_sel = menu_sel_max; }\r
1160 if (inp & GP2X_DOWN) { menu_sel++; if (menu_sel > menu_sel_max) menu_sel = 0; }\r
1161 selected_id = me_index2id(opt2_entries, OPT2_ENTRY_COUNT, menu_sel);\r
1162 if (inp & (GP2X_LEFT|GP2X_RIGHT)) { // multi choise\r
1163 if (!me_process(opt2_entries, OPT2_ENTRY_COUNT, selected_id, (inp&GP2X_RIGHT) ? 1 : 0) &&\r
1164 selected_id == MA_OPT2_GAMMA) {\r
1165 while ((inp = gp2x_joystick_read(1)) & (GP2X_LEFT|GP2X_RIGHT)) {\r
1166 currentConfig.gamma += (inp & GP2X_LEFT) ? -1 : 1;\r
1167 if (currentConfig.gamma < 1) currentConfig.gamma = 1;\r
1168 if (currentConfig.gamma > 300) currentConfig.gamma = 300;\r
1169 draw_amenu_options(menu_sel);\r
1170 usleep(18*1000);\r
1171 }\r
cc68a136 1172 }\r
1173 }\r
4e8a534c 1174 if (inp & GP2X_B) { // toggleable options\r
1175 if (!me_process(opt2_entries, OPT2_ENTRY_COUNT, selected_id, 1) &&\r
1176 selected_id == MA_OPT2_DONE) {\r
1177 return;\r
cc68a136 1178 }\r
1179 }\r
4e8a534c 1180 if (inp & (GP2X_X|GP2X_A)) return;\r
cc68a136 1181 }\r
1182}\r
1183\r
1184// -------------- options --------------\r
1185\r
4e8a534c 1186\r
1187menu_entry opt_entries[] =\r
1188{\r
58c86d00 1189 { NULL, MB_NONE, MA_OPT_RENDERER, NULL, 0, 0, 0, 1, 1 },\r
1190 { NULL, MB_RANGE, MA_OPT_SCALING, &currentConfig.scaling, 0, 0, 3, 1, 1 },\r
c060a9ab 1191 { "Accurate sprites", MB_ONOFF, MA_OPT_ACC_SPRITES, &PicoOpt, 0x080, 0, 0, 0, 1 },\r
58c86d00 1192 { "Show FPS", MB_ONOFF, MA_OPT_SHOW_FPS, &currentConfig.EmuOpt, 0x002, 0, 0, 1, 1 },\r
1193 { NULL, MB_RANGE, MA_OPT_FRAMESKIP, &currentConfig.Frameskip, 0, -1, 16, 1, 1 },\r
1194 { "Enable sound", MB_ONOFF, MA_OPT_ENABLE_SOUND, &currentConfig.EmuOpt, 0x004, 0, 0, 1, 1 },\r
1195 { NULL, MB_NONE, MA_OPT_SOUND_QUALITY, NULL, 0, 0, 0, 1, 1 },\r
1196 { "Use ARM940 core for sound", MB_ONOFF, MA_OPT_ARM940_SOUND, &PicoOpt, 0x200, 0, 0, 1, 1 },\r
1197 { "6 button pad", MB_ONOFF, MA_OPT_6BUTTON_PAD, &PicoOpt, 0x020, 0, 0, 1, 1 },\r
1198 { NULL, MB_NONE, MA_OPT_REGION, NULL, 0, 0, 0, 1, 1 },\r
1199 { "Use SRAM/BRAM savestates", MB_ONOFF, MA_OPT_SRAM_STATES, &currentConfig.EmuOpt, 0x001, 0, 0, 1, 1 },\r
1200 { NULL, MB_NONE, MA_OPT_CONFIRM_STATES,NULL, 0, 0, 0, 1, 1 },\r
1201 { "Save slot", MB_RANGE, MA_OPT_SAVE_SLOT, &state_slot, 0, 0, 9, 1, 1 },\r
1202 { NULL, MB_NONE, MA_OPT_CPU_CLOCKS, NULL, 0, 0, 0, 1, 1 },\r
1203 { "[Sega/Mega CD options]", MB_NONE, MA_OPT_SCD_OPTS, NULL, 0, 0, 0, 1, 0 },\r
1204 { "[advanced options]", MB_NONE, MA_OPT_ADV_OPTS, NULL, 0, 0, 0, 1, 0 },\r
1205 { NULL, MB_NONE, MA_OPT_SAVECFG, NULL, 0, 0, 0, 1, 0 },\r
1206 { "Save cfg for current game only",MB_NONE,MA_OPT_SAVECFG_GAME,NULL, 0, 0, 0, 1, 0 },\r
1207 { NULL, MB_NONE, MA_OPT_LOADCFG, NULL, 0, 0, 0, 1, 0 },\r
4e8a534c 1208};\r
1209\r
1210#define OPT_ENTRY_COUNT (sizeof(opt_entries) / sizeof(opt_entries[0]))\r
58c86d00 1211const int opt_entry_count = OPT_ENTRY_COUNT;\r
cc68a136 1212\r
4e8a534c 1213\r
1214static void menu_opt_cust_draw(const menu_entry *entry, int x, int y, void *param)\r
cc68a136 1215{\r
4e8a534c 1216 char *str, str24[24];\r
1217\r
1218 switch (entry->id)\r
1219 {\r
1220 case MA_OPT_RENDERER:\r
602133e1 1221 if (PicoOpt & POPT_ALT_RENDERER)\r
4e8a534c 1222 str = " 8bit fast";\r
1223 else if (currentConfig.EmuOpt&0x80)\r
1224 str = "16bit accurate";\r
1225 else\r
1226 str = " 8bit accurate";\r
a12e0116 1227 text_out16(x, y, "Renderer: %s", str);\r
4e8a534c 1228 break;\r
1229 case MA_OPT_SCALING:\r
1230 switch (currentConfig.scaling) {\r
1231 default: str = " OFF"; break;\r
1232 case 1: str = "hw horizontal"; break;\r
1233 case 2: str = "hw horiz. + vert."; break;\r
1234 case 3: str = "sw horizontal"; break;\r
1235 }\r
a12e0116 1236 text_out16(x, y, "Scaling: %s", str);\r
4e8a534c 1237 break;\r
1238 case MA_OPT_FRAMESKIP:\r
1239 if (currentConfig.Frameskip < 0)\r
1240 strcpy(str24, "Auto");\r
1241 else sprintf(str24, "%i", currentConfig.Frameskip);\r
a12e0116 1242 text_out16(x, y, "Frameskip %s", str24);\r
4e8a534c 1243 break;\r
1244 case MA_OPT_SOUND_QUALITY:\r
602133e1 1245 str = (PicoOpt & POPT_EN_STEREO) ? "stereo" : "mono";\r
58c86d00 1246 text_out16(x, y, "Sound Quality: %5iHz %s", PsndRate, str);\r
4e8a534c 1247 break;\r
1248 case MA_OPT_REGION:\r
58c86d00 1249 text_out16(x, y, "Region: %s", me_region_name(PicoRegionOverride, PicoAutoRgnOrder));\r
4e8a534c 1250 break;\r
1251 case MA_OPT_CONFIRM_STATES:\r
1252 switch ((currentConfig.EmuOpt >> 9) & 5) {\r
1253 default: str = "OFF"; break;\r
1254 case 1: str = "writes"; break;\r
1255 case 4: str = "loads"; break;\r
1256 case 5: str = "both"; break;\r
1257 }\r
a12e0116 1258 text_out16(x, y, "Confirm savestate %s", str);\r
4e8a534c 1259 break;\r
1260 case MA_OPT_CPU_CLOCKS:\r
a12e0116 1261 text_out16(x, y, "GP2X CPU clocks %iMhz", currentConfig.CPUclock);\r
4e8a534c 1262 break;\r
1263 case MA_OPT_SAVECFG:\r
1264 str24[0] = 0;\r
1265 if (config_slot != 0) sprintf(str24, " (profile: %i)", config_slot);\r
a12e0116 1266 text_out16(x, y, "Save cfg as default%s", str24);\r
4e8a534c 1267 break;\r
1268 case MA_OPT_LOADCFG:\r
a12e0116 1269 text_out16(x, y, "Load cfg from profile %i", config_slot);\r
4e8a534c 1270 break;\r
1271 default:\r
1272 printf("%s: unimplemented (%i)\n", __FUNCTION__, entry->id);\r
1273 break;\r
46969540 1274 }\r
4e8a534c 1275}\r
1276\r
1277\r
1278\r
1279static void draw_menu_options(int menu_sel)\r
1280{\r
a12e0116 1281 int tl_x = 25, tl_y = 24;\r
cc68a136 1282\r
e11c5548 1283 gp2x_pd_clone_buffer2();\r
1284\r
e5f426aa 1285 menu_draw_selection(tl_x - 16, tl_y + menu_sel*10, 284);\r
cc68a136 1286\r
a12e0116 1287 me_draw(opt_entries, OPT_ENTRY_COUNT, tl_x, tl_y, menu_opt_cust_draw, NULL);\r
cc68a136 1288\r
a12e0116 1289 menu_flip();\r
cc68a136 1290}\r
1291\r
1292static int sndrate_prevnext(int rate, int dir)\r
1293{\r
1294 int i, rates[] = { 8000, 11025, 16000, 22050, 44100 };\r
1295\r
1296 for (i = 0; i < 5; i++)\r
1297 if (rates[i] == rate) break;\r
1298\r
1299 i += dir ? 1 : -1;\r
1300 if (i > 4) return dir ? 44100 : 22050;\r
1301 if (i < 0) return dir ? 11025 : 8000;\r
1302 return rates[i];\r
1303}\r
1304\r
51a902ae 1305static void region_prevnext(int right)\r
1306{\r
1307 // jp_ntsc=1, jp_pal=2, usa=4, eu=8\r
1308 static int rgn_orders[] = { 0x148, 0x184, 0x814, 0x418, 0x841, 0x481 };\r
1309 int i;\r
1310 if (right) {\r
58c86d00 1311 if (!PicoRegionOverride) {\r
51a902ae 1312 for (i = 0; i < 6; i++)\r
1313 if (rgn_orders[i] == PicoAutoRgnOrder) break;\r
1314 if (i < 5) PicoAutoRgnOrder = rgn_orders[i+1];\r
58c86d00 1315 else PicoRegionOverride=1;\r
51a902ae 1316 }\r
58c86d00 1317 else PicoRegionOverride<<=1;\r
1318 if (PicoRegionOverride > 8) PicoRegionOverride = 8;\r
51a902ae 1319 } else {\r
58c86d00 1320 if (!PicoRegionOverride) {\r
51a902ae 1321 for (i = 0; i < 6; i++)\r
1322 if (rgn_orders[i] == PicoAutoRgnOrder) break;\r
1323 if (i > 0) PicoAutoRgnOrder = rgn_orders[i-1];\r
1324 }\r
58c86d00 1325 else PicoRegionOverride>>=1;\r
51a902ae 1326 }\r
1327}\r
1328\r
cc68a136 1329static void menu_options_save(void)\r
1330{\r
7d0143a2 1331 if (PicoRegionOverride) {\r
1332 // force setting possibly changed..\r
1333 Pico.m.pal = (PicoRegionOverride == 2 || PicoRegionOverride == 8) ? 1 : 0;\r
1334 }\r
602133e1 1335 if (!(PicoOpt & POPT_6BTN_PAD)) {\r
d524c827 1336 // unbind XYZ MODE, just in case\r
45499284 1337 unbind_action(0xf00, -1, -1);\r
cc68a136 1338 }\r
1339}\r
1340\r
bf098bc5 1341static int menu_loop_options(void)\r
cc68a136 1342{\r
aae35e84 1343 static int menu_sel = 0;\r
4e8a534c 1344 int menu_sel_max, ret;\r
cc68a136 1345 unsigned long inp = 0;\r
4e8a534c 1346 menu_id selected_id;\r
cc68a136 1347\r
eacee137 1348 me_enable(opt_entries, OPT_ENTRY_COUNT, MA_OPT_SAVECFG_GAME, rom_loaded);\r
4e8a534c 1349 me_enable(opt_entries, OPT_ENTRY_COUNT, MA_OPT_LOADCFG, config_slot != config_slot_current);\r
1350 menu_sel_max = me_count_enabled(opt_entries, OPT_ENTRY_COUNT) - 1;\r
d524c827 1351 if (menu_sel > menu_sel_max) menu_sel = menu_sel_max;\r
4e8a534c 1352\r
1353 while (1)\r
cc68a136 1354 {\r
1355 draw_menu_options(menu_sel);\r
1356 inp = wait_for_input(GP2X_UP|GP2X_DOWN|GP2X_LEFT|GP2X_RIGHT|GP2X_B|GP2X_X|GP2X_A);\r
4e8a534c 1357 if (inp & GP2X_UP ) { menu_sel--; if (menu_sel < 0) menu_sel = menu_sel_max; }\r
1358 if (inp & GP2X_DOWN) { menu_sel++; if (menu_sel > menu_sel_max) menu_sel = 0; }\r
1359 selected_id = me_index2id(opt_entries, OPT_ENTRY_COUNT, menu_sel);\r
58c86d00 1360 if (inp & (GP2X_LEFT|GP2X_RIGHT)) { // multi choice\r
4e8a534c 1361 if (!me_process(opt_entries, OPT_ENTRY_COUNT, selected_id, (inp&GP2X_RIGHT) ? 1 : 0)) {\r
1362 switch (selected_id) {\r
1363 case MA_OPT_RENDERER:\r
1364 if (inp & GP2X_LEFT) {\r
58c86d00 1365 if (PicoOpt&0x10) PicoOpt&= ~0x10;\r
4e8a534c 1366 else if (!(currentConfig.EmuOpt &0x80))currentConfig.EmuOpt |= 0x80;\r
1367 else if ( currentConfig.EmuOpt &0x80) break;\r
1368 } else {\r
58c86d00 1369 if (PicoOpt&0x10) break;\r
1370 else if (!(currentConfig.EmuOpt &0x80))PicoOpt|= 0x10;\r
4e8a534c 1371 else if ( currentConfig.EmuOpt &0x80) currentConfig.EmuOpt &= ~0x80;\r
1372 }\r
1373 break;\r
1374 case MA_OPT_SOUND_QUALITY:\r
58c86d00 1375 if ((inp & GP2X_RIGHT) && PsndRate == 44100 && !(PicoOpt&0x08)) {\r
1376 PsndRate = 8000; PicoOpt|= 0x08;\r
1377 } else if ((inp & GP2X_LEFT) && PsndRate == 8000 && (PicoOpt&0x08)) {\r
1378 PsndRate = 44100; PicoOpt&=~0x08;\r
1379 } else PsndRate = sndrate_prevnext(PsndRate, inp & GP2X_RIGHT);\r
4e8a534c 1380 break;\r
1381 case MA_OPT_REGION:\r
1382 region_prevnext(inp & GP2X_RIGHT);\r
1383 break;\r
1384 case MA_OPT_CONFIRM_STATES: {\r
1385 int n = ((currentConfig.EmuOpt>>9)&1) | ((currentConfig.EmuOpt>>10)&2);\r
1386 n += (inp & GP2X_LEFT) ? -1 : 1;\r
1387 if (n < 0) n = 0; else if (n > 3) n = 3;\r
1388 n |= n << 1; n &= ~2;\r
1389 currentConfig.EmuOpt &= ~0xa00;\r
1390 currentConfig.EmuOpt |= n << 9;\r
1391 break;\r
1392 }\r
1393 case MA_OPT_SAVE_SLOT:\r
1394 if (inp & GP2X_RIGHT) {\r
1395 state_slot++; if (state_slot > 9) state_slot = 0;\r
1396 } else {state_slot--; if (state_slot < 0) state_slot = 9;\r
1397 }\r
1398 break;\r
1399 case MA_OPT_CPU_CLOCKS:\r
1400 while ((inp = gp2x_joystick_read(1)) & (GP2X_LEFT|GP2X_RIGHT)) {\r
1401 currentConfig.CPUclock += (inp & GP2X_LEFT) ? -1 : 1;\r
1402 if (currentConfig.CPUclock < 1) currentConfig.CPUclock = 1;\r
1403 draw_menu_options(menu_sel);\r
1404 usleep(50*1000);\r
1405 }\r
1406 break;\r
1407 case MA_OPT_SAVECFG:\r
1408 case MA_OPT_SAVECFG_GAME:\r
1409 case MA_OPT_LOADCFG:\r
1410 config_slot += (inp&GP2X_RIGHT) ? 1 : -1;\r
1411 if (config_slot > 9) config_slot = 0;\r
1412 if (config_slot < 0) config_slot = 9;\r
1413 me_enable(opt_entries, OPT_ENTRY_COUNT, MA_OPT_LOADCFG, config_slot != config_slot_current);\r
1414 menu_sel_max = me_count_enabled(opt_entries, OPT_ENTRY_COUNT) - 1;\r
1415 if (menu_sel > menu_sel_max) menu_sel = menu_sel_max;\r
1416 break;\r
1417 default:\r
1418 //printf("%s: something unknown selected (%i)\n", __FUNCTION__, selected_id);\r
1419 break;\r
1420 }\r
1421 }\r
1422 }\r
1423 if (inp & GP2X_B) {\r
1424 if (!me_process(opt_entries, OPT_ENTRY_COUNT, selected_id, 1))\r
1425 {\r
1426 switch (selected_id)\r
1427 {\r
1428 case MA_OPT_SCD_OPTS:\r
1429 cd_menu_loop_options();\r
1430 if (engineState == PGS_ReloadRom)\r
1431 return 0; // test BIOS\r
1432 break;\r
1433 case MA_OPT_ADV_OPTS:\r
1434 amenu_loop_options();\r
1435 break;\r
1436 case MA_OPT_SAVECFG: // done (update and write)\r
1437 menu_options_save();\r
1438 if (emu_WriteConfig(0)) strcpy(menuErrorMsg, "config saved");\r
1439 else strcpy(menuErrorMsg, "failed to write config");\r
1440 return 1;\r
1441 case MA_OPT_SAVECFG_GAME: // done (update and write for current game)\r
1442 menu_options_save();\r
1443 if (emu_WriteConfig(1)) strcpy(menuErrorMsg, "config saved");\r
1444 else strcpy(menuErrorMsg, "failed to write config");\r
1445 return 1;\r
1446 case MA_OPT_LOADCFG:\r
d524c827 1447 ret = emu_ReadConfig(1, 1);\r
1448 if (!ret) ret = emu_ReadConfig(0, 1);\r
1449 if (ret) strcpy(menuErrorMsg, "config loaded");\r
4e8a534c 1450 else strcpy(menuErrorMsg, "failed to load config");\r
1451 return 1;\r
1452 default:\r
1453 //printf("%s: something unknown selected (%i)\n", __FUNCTION__, selected_id);\r
1454 break;\r
1455 }\r
cc68a136 1456 }\r
1457 }\r
51a902ae 1458 if(inp & (GP2X_X|GP2X_A)) {\r
cc68a136 1459 menu_options_save();\r
bf098bc5 1460 return 0; // done (update, no write)\r
cc68a136 1461 }\r
cc68a136 1462 }\r
1463}\r
1464\r
1465// -------------- credits --------------\r
1466\r
1467static void draw_menu_credits(void)\r
1468{\r
053fd9b4 1469 int tl_x = 15, tl_y = 56, y;\r
e11c5548 1470 gp2x_pd_clone_buffer2();\r
cc68a136 1471\r
f9d3ee9d 1472 text_out16(tl_x, 20, "PicoDrive v" VERSION " (c) notaz, 2006-2008");\r
cc68a136 1473 y = tl_y;\r
a12e0116 1474 text_out16(tl_x, y, "Credits:");\r
3ec29f01 1475 text_out16(tl_x, (y+=10), "fDave: Cyclone 68000 core,");\r
a12e0116 1476 text_out16(tl_x, (y+=10), " base code of PicoDrive");\r
1477 text_out16(tl_x, (y+=10), "Reesy & FluBBa: DrZ80 core");\r
1478 text_out16(tl_x, (y+=10), "MAME devs: YM2612 and SN76496 cores");\r
a12e0116 1479 text_out16(tl_x, (y+=10), "rlyeh and others: minimal SDK");\r
1480 text_out16(tl_x, (y+=10), "Squidge: squidgehack");\r
1481 text_out16(tl_x, (y+=10), "Dzz: ARM940 sample");\r
053fd9b4 1482 text_out16(tl_x, (y+=10), "GnoStiC / Puck2099: USB joy code");\r
a12e0116 1483 text_out16(tl_x, (y+=10), "craigix: GP2X hardware");\r
a4f0cc86 1484 text_out16(tl_x, (y+=10), "ketchupgun: skin design");\r
a12e0116 1485\r
1a65e3b1 1486 text_out16(tl_x, (y+=20), "special thanks (for docs, ideas):");\r
053fd9b4 1487 text_out16(tl_x, (y+=10), " Charles MacDonald, Haze,");\r
1488 text_out16(tl_x, (y+=10), " Stephane Dallongeville,");\r
1489 text_out16(tl_x, (y+=10), " Lordus, Exophase, Rokas,");\r
1490 text_out16(tl_x, (y+=10), " Nemesis, Tasco Deluxe");\r
1491\r
a12e0116 1492 menu_flip();\r
cc68a136 1493}\r
1494\r
1495\r
1496// -------------- root menu --------------\r
1497\r
4e8a534c 1498menu_entry main_entries[] =\r
1499{\r
58c86d00 1500 { "Resume game", MB_NONE, MA_MAIN_RESUME_GAME, NULL, 0, 0, 0, 0, 0 },\r
1501 { "Save State", MB_NONE, MA_MAIN_SAVE_STATE, NULL, 0, 0, 0, 0, 0 },\r
1502 { "Load State", MB_NONE, MA_MAIN_LOAD_STATE, NULL, 0, 0, 0, 0, 0 },\r
1503 { "Reset game", MB_NONE, MA_MAIN_RESET_GAME, NULL, 0, 0, 0, 0, 0 },\r
1504 { "Load new ROM/ISO", MB_NONE, MA_MAIN_LOAD_ROM, NULL, 0, 0, 0, 1, 0 },\r
1505 { "Change options", MB_NONE, MA_MAIN_OPTIONS, NULL, 0, 0, 0, 1, 0 },\r
1506 { "Configure controls", MB_NONE, MA_MAIN_CONTROLS, NULL, 0, 0, 0, 1, 0 },\r
1507 { "Credits", MB_NONE, MA_MAIN_CREDITS, NULL, 0, 0, 0, 1, 0 },\r
1508 { "Patches / GameGenie",MB_NONE, MA_MAIN_PATCHES, NULL, 0, 0, 0, 0, 0 },\r
1509 { "Exit", MB_NONE, MA_MAIN_EXIT, NULL, 0, 0, 0, 1, 0 }\r
4e8a534c 1510};\r
1511\r
1512#define MAIN_ENTRY_COUNT (sizeof(main_entries) / sizeof(main_entries[0]))\r
1513\r
cc68a136 1514static void draw_menu_root(int menu_sel)\r
1515{\r
4e8a534c 1516 const int tl_x = 70, tl_y = 70;\r
1517\r
e11c5548 1518 gp2x_pd_clone_buffer2();\r
cc68a136 1519\r
a12e0116 1520 text_out16(tl_x, 20, "PicoDrive v" VERSION);\r
1521\r
e5f426aa 1522 menu_draw_selection(tl_x - 16, tl_y + menu_sel*10, 146);\r
cc68a136 1523\r
4e8a534c 1524 me_draw(main_entries, MAIN_ENTRY_COUNT, tl_x, tl_y, NULL, NULL);\r
cc68a136 1525\r
cc68a136 1526 // error\r
a12e0116 1527 if (menuErrorMsg[0]) {\r
1528 memset((char *)gp2x_screen + 320*224*2, 0, 320*16*2);\r
1529 text_out16(5, 226, menuErrorMsg);\r
1530 }\r
1531 menu_flip();\r
cc68a136 1532}\r
1533\r
1534\r
1535static void menu_loop_root(void)\r
1536{\r
4e8a534c 1537 static int menu_sel = 0;\r
1538 int ret, menu_sel_max;\r
cc68a136 1539 unsigned long inp = 0;\r
cc68a136 1540\r
eacee137 1541 me_enable(main_entries, MAIN_ENTRY_COUNT, MA_MAIN_RESUME_GAME, rom_loaded);\r
1542 me_enable(main_entries, MAIN_ENTRY_COUNT, MA_MAIN_SAVE_STATE, rom_loaded);\r
1543 me_enable(main_entries, MAIN_ENTRY_COUNT, MA_MAIN_LOAD_STATE, rom_loaded);\r
1544 me_enable(main_entries, MAIN_ENTRY_COUNT, MA_MAIN_RESET_GAME, rom_loaded);\r
4e8a534c 1545 me_enable(main_entries, MAIN_ENTRY_COUNT, MA_MAIN_PATCHES, PicoPatches != NULL);\r
1546\r
1547 menu_sel_max = me_count_enabled(main_entries, MAIN_ENTRY_COUNT) - 1;\r
d524c827 1548 if (menu_sel > menu_sel_max) menu_sel = menu_sel_max;\r
cc68a136 1549\r
2433f409 1550 /* make sure action buttons are not pressed on entering menu */\r
1551 draw_menu_root(menu_sel);\r
1552 while (gp2x_joystick_read(1) & (GP2X_B|GP2X_X|GP2X_SELECT)) usleep(50*1000);\r
1553\r
1554 for (;;)\r
cc68a136 1555 {\r
1556 draw_menu_root(menu_sel);\r
0af33fe0 1557 inp = wait_for_input(GP2X_UP|GP2X_DOWN|GP2X_B|GP2X_X|GP2X_SELECT|GP2X_L|GP2X_R);\r
4e8a534c 1558 if(inp & GP2X_UP ) { menu_sel--; if (menu_sel < 0) menu_sel = menu_sel_max; }\r
1559 if(inp & GP2X_DOWN) { menu_sel++; if (menu_sel > menu_sel_max) menu_sel = 0; }\r
0af33fe0 1560 if((inp & (GP2X_L|GP2X_R)) == (GP2X_L|GP2X_R)) debug_menu_loop();\r
cc68a136 1561 if(inp &(GP2X_SELECT|GP2X_X)){\r
eacee137 1562 if (rom_loaded) {\r
cc68a136 1563 while (gp2x_joystick_read(1) & (GP2X_SELECT|GP2X_X)) usleep(50*1000); // wait until select is released\r
1564 engineState = PGS_Running;\r
1565 break;\r
1566 }\r
1567 }\r
4e8a534c 1568 if(inp & GP2X_B) {\r
1569 switch (me_index2id(main_entries, MAIN_ENTRY_COUNT, menu_sel))\r
1570 {\r
1571 case MA_MAIN_RESUME_GAME:\r
eacee137 1572 if (rom_loaded) {\r
721cd396 1573 while (gp2x_joystick_read(1) & GP2X_B) usleep(50*1000);\r
1574 engineState = PGS_Running;\r
1575 return;\r
1576 }\r
cc68a136 1577 break;\r
4e8a534c 1578 case MA_MAIN_SAVE_STATE:\r
eacee137 1579 if (rom_loaded) {\r
860c6322 1580 if(savestate_menu_loop(0))\r
cc68a136 1581 continue;\r
cc68a136 1582 engineState = PGS_Running;\r
1583 return;\r
1584 }\r
1585 break;\r
4e8a534c 1586 case MA_MAIN_LOAD_STATE:\r
eacee137 1587 if (rom_loaded) {\r
860c6322 1588 if(savestate_menu_loop(1))\r
cc68a136 1589 continue;\r
5ed2561c 1590 while (gp2x_joystick_read(1) & GP2X_B) usleep(50*1000);\r
cc68a136 1591 engineState = PGS_Running;\r
1592 return;\r
1593 }\r
1594 break;\r
4e8a534c 1595 case MA_MAIN_RESET_GAME:\r
eacee137 1596 if (rom_loaded) {\r
cc68a136 1597 emu_ResetGame();\r
5ed2561c 1598 while (gp2x_joystick_read(1) & GP2X_B) usleep(50*1000);\r
cc68a136 1599 engineState = PGS_Running;\r
1600 return;\r
1601 }\r
1602 break;\r
4e8a534c 1603 case MA_MAIN_LOAD_ROM:\r
a12e0116 1604 {\r
1605 char curr_path[PATH_MAX], *selfname;\r
1606 FILE *tstf;\r
1ca2ea4f 1607 if ( (tstf = fopen(lastRomFile, "rb")) )\r
a12e0116 1608 {\r
1609 fclose(tstf);\r
1ca2ea4f 1610 strcpy(curr_path, lastRomFile);\r
a12e0116 1611 }\r
1612 else\r
1613 getcwd(curr_path, PATH_MAX);\r
cc68a136 1614 selfname = romsel_loop(curr_path);\r
1615 if (selfname) {\r
1616 printf("selected file: %s\n", selfname);\r
cc68a136 1617 engineState = PGS_ReloadRom;\r
a12e0116 1618 return;\r
cc68a136 1619 }\r
a12e0116 1620 break;\r
1621 }\r
4e8a534c 1622 case MA_MAIN_OPTIONS:\r
bf098bc5 1623 ret = menu_loop_options();\r
1624 if (ret == 1) continue; // status update\r
1625 if (engineState == PGS_ReloadRom)\r
1626 return; // BIOS test\r
cc68a136 1627 break;\r
4e8a534c 1628 case MA_MAIN_CONTROLS:\r
cc68a136 1629 kc_sel_loop();\r
1630 break;\r
4e8a534c 1631 case MA_MAIN_CREDITS:\r
cc68a136 1632 draw_menu_credits();\r
1633 usleep(500*1000);\r
1634 inp = wait_for_input(GP2X_B|GP2X_X);\r
1635 break;\r
4e8a534c 1636 case MA_MAIN_EXIT:\r
cc68a136 1637 engineState = PGS_Quit;\r
1638 return;\r
4e8a534c 1639 case MA_MAIN_PATCHES:\r
eacee137 1640 if (rom_loaded && PicoPatches) {\r
b67ef287 1641 patches_menu_loop();\r
1642 PicoPatchApply();\r
1643 strcpy(menuErrorMsg, "Patches applied");\r
1644 continue;\r
1645 }\r
1646 break;\r
4e8a534c 1647 default:\r
1648 printf("%s: something unknown selected\n", __FUNCTION__);\r
1649 break;\r
cc68a136 1650 }\r
1651 }\r
1652 menuErrorMsg[0] = 0; // clear error msg\r
1653 }\r
1654}\r
1655\r
a4f0cc86 1656static void menu_darken_bg(void *dst, int pixels, int darker)\r
cc68a136 1657{\r
a12e0116 1658 unsigned int *screen = dst;\r
1659 pixels /= 2;\r
a4f0cc86 1660 if (darker)\r
a12e0116 1661 {\r
a4f0cc86 1662 while (pixels--)\r
1663 {\r
1664 unsigned int p = *screen;\r
1665 *screen++ = ((p&0xf79ef79e)>>1) - ((p&0xc618c618)>>3);\r
1666 }\r
1667 }\r
1668 else\r
1669 {\r
1670 while (pixels--)\r
1671 {\r
1672 unsigned int p = *screen;\r
1673 *screen++ = (p&0xf79ef79e)>>1;\r
1674 }\r
a12e0116 1675 }\r
1676}\r
e11c5548 1677\r
a12e0116 1678static void menu_prepare_bg(int use_game_bg)\r
1679{\r
1680 if (use_game_bg)\r
1681 {\r
1682 // darken the active framebuffer\r
1683 memset(gp2x_screen, 0, 320*8*2);\r
a4f0cc86 1684 menu_darken_bg((char *)gp2x_screen + 320*8*2, 320*224, 1);\r
a12e0116 1685 memset((char *)gp2x_screen + 320*232*2, 0, 320*8*2);\r
1686 }\r
1687 else\r
1688 {\r
1689 // should really only happen once, on startup..\r
1690 readpng(gp2x_screen, "skin/background.png", READPNG_BG);\r
860c6322 1691 }\r
cc68a136 1692\r
a12e0116 1693 // copy to buffer2\r
1694 gp2x_memcpy_buffers((1<<2), gp2x_screen, 0, 320*240*2);\r
860c6322 1695}\r
1696\r
1697static void menu_gfx_prepare(void)\r
1698{\r
eacee137 1699 menu_prepare_bg(rom_loaded);\r
860c6322 1700\r
a12e0116 1701 // switch to 16bpp\r
1702 gp2x_video_changemode2(16);\r
2433f409 1703 gp2x_video_RGB_setscaling(0, 320, 240);\r
a12e0116 1704 menu_flip();\r
e11c5548 1705}\r
1706\r
1707\r
1708void menu_loop(void)\r
1709{\r
1710 menu_gfx_prepare();\r
cc68a136 1711\r
1712 menu_loop_root();\r
1713\r
1714 menuErrorMsg[0] = 0;\r
1715}\r
721cd396 1716\r
1717\r
1718// --------- CD tray close menu ----------\r
1719\r
1720static void draw_menu_tray(int menu_sel)\r
1721{\r
1722 int tl_x = 70, tl_y = 90, y;\r
a12e0116 1723 memset(gp2x_screen, 0, 320*240*2);\r
721cd396 1724\r
a12e0116 1725 text_out16(tl_x, 20, "The unit is about to");\r
1726 text_out16(tl_x, 30, "close the CD tray.");\r
721cd396 1727\r
1728 y = tl_y;\r
a12e0116 1729 text_out16(tl_x, y, "Load new CD image");\r
1730 text_out16(tl_x, (y+=10), "Insert nothing");\r
721cd396 1731\r
1732 // draw cursor\r
a12e0116 1733 text_out16(tl_x - 16, tl_y + menu_sel*10, ">");\r
721cd396 1734 // error\r
a12e0116 1735 if (menuErrorMsg[0]) text_out16(5, 226, menuErrorMsg);\r
1736 menu_flip();\r
721cd396 1737}\r
1738\r
1739\r
1740int menu_loop_tray(void)\r
1741{\r
1742 int menu_sel = 0, menu_sel_max = 1;\r
1743 unsigned long inp = 0;\r
1744 char curr_path[PATH_MAX], *selfname;\r
1745 FILE *tstf;\r
1746\r
e5f426aa 1747 gp2x_memset_all_buffers(0, 0, 320*240*2);\r
721cd396 1748 menu_gfx_prepare();\r
1749\r
1ca2ea4f 1750 if ( (tstf = fopen(lastRomFile, "rb")) )\r
721cd396 1751 {\r
1752 fclose(tstf);\r
1ca2ea4f 1753 strcpy(curr_path, lastRomFile);\r
721cd396 1754 }\r
1755 else\r
1756 {\r
1757 getcwd(curr_path, PATH_MAX);\r
1758 }\r
1759\r
1760 /* make sure action buttons are not pressed on entering menu */\r
1761 draw_menu_tray(menu_sel);\r
1762 while (gp2x_joystick_read(1) & GP2X_B) usleep(50*1000);\r
1763\r
1764 for (;;)\r
1765 {\r
1766 draw_menu_tray(menu_sel);\r
1767 inp = wait_for_input(GP2X_UP|GP2X_DOWN|GP2X_B);\r
1768 if(inp & GP2X_UP ) { menu_sel--; if (menu_sel < 0) menu_sel = menu_sel_max; }\r
1769 if(inp & GP2X_DOWN) { menu_sel++; if (menu_sel > menu_sel_max) menu_sel = 0; }\r
1770 if(inp & GP2X_B ) {\r
1771 switch (menu_sel) {\r
1772 case 0: // select image\r
1773 selfname = romsel_loop(curr_path);\r
1774 if (selfname) {\r
b923ecbe 1775 int ret = -1;\r
1776 cd_img_type cd_type;\r
ea8c405f 1777 cd_type = emu_cdCheck(NULL);\r
b923ecbe 1778 if (cd_type != CIT_NOT_CD)\r
1779 ret = Insert_CD(romFileName, cd_type);\r
721cd396 1780 if (ret != 0) {\r
1781 sprintf(menuErrorMsg, "Load failed, invalid CD image?");\r
1782 printf("%s\n", menuErrorMsg);\r
1783 continue;\r
1784 }\r
1785 engineState = PGS_RestartRun;\r
1786 return 1;\r
1787 }\r
1788 break;\r
1789 case 1: // insert nothing\r
1790 engineState = PGS_RestartRun;\r
1791 return 0;\r
1792 }\r
1793 }\r
1794 menuErrorMsg[0] = 0; // clear error msg\r
1795 }\r
1796}\r
1797\r
1798\r