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