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