nonacc mode removal, function return value audit
[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
0ae25549 1153 { "Accurate sprites (slower)", MB_ONOFF, MA_OPT_ACC_SPRITES, &PicoOpt, 0x080, 0, 0, 1, 1 },\r
1154 { "Show FPS", MB_ONOFF, MA_OPT_SHOW_FPS, &currentConfig.EmuOpt, 0x002, 0, 0, 1, 1 },\r
1155 { NULL, MB_RANGE, MA_OPT_FRAMESKIP, &currentConfig.Frameskip, 0, -1, 16, 1, 1 },\r
1156 { "Enable sound", MB_ONOFF, MA_OPT_ENABLE_SOUND, &currentConfig.EmuOpt, 0x004, 0, 0, 1, 1 },\r
1157 { NULL, MB_NONE, MA_OPT_SOUND_QUALITY, NULL, 0, 0, 0, 1, 1 },\r
1158 { "Use ARM940 core for sound", MB_ONOFF, MA_OPT_ARM940_SOUND, &PicoOpt, 0x200, 0, 0, 1, 1 },\r
1159 { "6 button pad", MB_ONOFF, MA_OPT_6BUTTON_PAD, &PicoOpt, 0x020, 0, 0, 1, 1 },\r
1160 { NULL, MB_NONE, MA_OPT_REGION, NULL, 0, 0, 0, 1, 1 },\r
1161 { "Use SRAM/BRAM savestates", MB_ONOFF, MA_OPT_SRAM_STATES, &currentConfig.EmuOpt, 0x001, 0, 0, 1, 1 },\r
1162 { NULL, MB_NONE, MA_OPT_CONFIRM_STATES,NULL, 0, 0, 0, 1, 1 },\r
1163 { "Save slot", MB_RANGE, MA_OPT_SAVE_SLOT, &state_slot, 0, 0, 9, 1, 1 },\r
1164 { NULL, MB_NONE, MA_OPT_CPU_CLOCKS, NULL, 0, 0, 0, 1, 1 },\r
1165 { "[Sega/Mega CD options]", MB_NONE, MA_OPT_SCD_OPTS, NULL, 0, 0, 0, 1, 0 },\r
1166 { "[advanced options]", MB_NONE, MA_OPT_ADV_OPTS, NULL, 0, 0, 0, 1, 0 },\r
1167 { NULL, MB_NONE, MA_OPT_SAVECFG, NULL, 0, 0, 0, 1, 0 },\r
1168 { "Save cfg for current game only",MB_NONE,MA_OPT_SAVECFG_GAME,NULL, 0, 0, 0, 1, 0 },\r
1169 { NULL, MB_NONE, MA_OPT_LOADCFG, NULL, 0, 0, 0, 1, 0 },\r
59633198 1170};\r
1171\r
1172#define OPT_ENTRY_COUNT (sizeof(opt_entries) / sizeof(opt_entries[0]))\r
0ae25549 1173const int opt_entry_count = OPT_ENTRY_COUNT;\r
720ee7f6 1174\r
59633198 1175\r
1176static void menu_opt_cust_draw(const menu_entry *entry, int x, int y, void *param)\r
720ee7f6 1177{\r
59633198 1178 char *str, str24[24];\r
1179\r
1180 switch (entry->id)\r
1181 {\r
1182 case MA_OPT_RENDERER:\r
dd5fd477 1183 if (PicoOpt & POPT_ALT_RENDERER)\r
59633198 1184 str = " 8bit fast";\r
1185 else if (currentConfig.EmuOpt&0x80)\r
1186 str = "16bit accurate";\r
1187 else\r
1188 str = " 8bit accurate";\r
13059a60 1189 text_out16(x, y, "Renderer: %s", str);\r
59633198 1190 break;\r
1191 case MA_OPT_SCALING:\r
1192 switch (currentConfig.scaling) {\r
1193 default: str = " OFF"; break;\r
1194 case 1: str = "hw horizontal"; break;\r
1195 case 2: str = "hw horiz. + vert."; break;\r
1196 case 3: str = "sw horizontal"; break;\r
1197 }\r
13059a60 1198 text_out16(x, y, "Scaling: %s", str);\r
59633198 1199 break;\r
1200 case MA_OPT_FRAMESKIP:\r
1201 if (currentConfig.Frameskip < 0)\r
1202 strcpy(str24, "Auto");\r
1203 else sprintf(str24, "%i", currentConfig.Frameskip);\r
13059a60 1204 text_out16(x, y, "Frameskip %s", str24);\r
59633198 1205 break;\r
1206 case MA_OPT_SOUND_QUALITY:\r
dd5fd477 1207 str = (PicoOpt & POPT_EN_STEREO) ? "stereo" : "mono";\r
0ae25549 1208 text_out16(x, y, "Sound Quality: %5iHz %s", PsndRate, str);\r
59633198 1209 break;\r
1210 case MA_OPT_REGION:\r
0ae25549 1211 text_out16(x, y, "Region: %s", me_region_name(PicoRegionOverride, PicoAutoRgnOrder));\r
59633198 1212 break;\r
1213 case MA_OPT_CONFIRM_STATES:\r
1214 switch ((currentConfig.EmuOpt >> 9) & 5) {\r
1215 default: str = "OFF"; break;\r
1216 case 1: str = "writes"; break;\r
1217 case 4: str = "loads"; break;\r
1218 case 5: str = "both"; break;\r
1219 }\r
13059a60 1220 text_out16(x, y, "Confirm savestate %s", str);\r
59633198 1221 break;\r
1222 case MA_OPT_CPU_CLOCKS:\r
13059a60 1223 text_out16(x, y, "GP2X CPU clocks %iMhz", currentConfig.CPUclock);\r
59633198 1224 break;\r
1225 case MA_OPT_SAVECFG:\r
1226 str24[0] = 0;\r
1227 if (config_slot != 0) sprintf(str24, " (profile: %i)", config_slot);\r
13059a60 1228 text_out16(x, y, "Save cfg as default%s", str24);\r
59633198 1229 break;\r
1230 case MA_OPT_LOADCFG:\r
13059a60 1231 text_out16(x, y, "Load cfg from profile %i", config_slot);\r
59633198 1232 break;\r
1233 default:\r
1234 printf("%s: unimplemented (%i)\n", __FUNCTION__, entry->id);\r
1235 break;\r
46ede6a6 1236 }\r
59633198 1237}\r
1238\r
1239\r
1240\r
1241static void draw_menu_options(int menu_sel)\r
1242{\r
13059a60 1243 int tl_x = 25, tl_y = 24;\r
720ee7f6 1244\r
e5d315a5 1245 gp2x_pd_clone_buffer2();\r
1246\r
c7a4ff64 1247 menu_draw_selection(tl_x - 16, tl_y + menu_sel*10, 284);\r
720ee7f6 1248\r
13059a60 1249 me_draw(opt_entries, OPT_ENTRY_COUNT, tl_x, tl_y, menu_opt_cust_draw, NULL);\r
720ee7f6 1250\r
13059a60 1251 menu_flip();\r
720ee7f6 1252}\r
1253\r
1254static int sndrate_prevnext(int rate, int dir)\r
1255{\r
1256 int i, rates[] = { 8000, 11025, 16000, 22050, 44100 };\r
1257\r
1258 for (i = 0; i < 5; i++)\r
1259 if (rates[i] == rate) break;\r
1260\r
1261 i += dir ? 1 : -1;\r
1262 if (i > 4) return dir ? 44100 : 22050;\r
1263 if (i < 0) return dir ? 11025 : 8000;\r
1264 return rates[i];\r
1265}\r
1266\r
979ba09f 1267static void region_prevnext(int right)\r
1268{\r
1269 // jp_ntsc=1, jp_pal=2, usa=4, eu=8\r
1270 static int rgn_orders[] = { 0x148, 0x184, 0x814, 0x418, 0x841, 0x481 };\r
1271 int i;\r
1272 if (right) {\r
0ae25549 1273 if (!PicoRegionOverride) {\r
979ba09f 1274 for (i = 0; i < 6; i++)\r
1275 if (rgn_orders[i] == PicoAutoRgnOrder) break;\r
1276 if (i < 5) PicoAutoRgnOrder = rgn_orders[i+1];\r
0ae25549 1277 else PicoRegionOverride=1;\r
979ba09f 1278 }\r
0ae25549 1279 else PicoRegionOverride<<=1;\r
1280 if (PicoRegionOverride > 8) PicoRegionOverride = 8;\r
979ba09f 1281 } else {\r
0ae25549 1282 if (!PicoRegionOverride) {\r
979ba09f 1283 for (i = 0; i < 6; i++)\r
1284 if (rgn_orders[i] == PicoAutoRgnOrder) break;\r
1285 if (i > 0) PicoAutoRgnOrder = rgn_orders[i-1];\r
1286 }\r
0ae25549 1287 else PicoRegionOverride>>=1;\r
979ba09f 1288 }\r
1289}\r
1290\r
720ee7f6 1291static void menu_options_save(void)\r
1292{\r
426ecc58 1293 if (PicoRegionOverride) {\r
1294 // force setting possibly changed..\r
1295 Pico.m.pal = (PicoRegionOverride == 2 || PicoRegionOverride == 8) ? 1 : 0;\r
1296 }\r
dd5fd477 1297 if (!(PicoOpt & POPT_6BTN_PAD)) {\r
95151aea 1298 // unbind XYZ MODE, just in case\r
e67f6454 1299 unbind_action(0xf00, -1, -1);\r
720ee7f6 1300 }\r
1301}\r
1302\r
daf91588 1303static int menu_loop_options(void)\r
720ee7f6 1304{\r
59d0f042 1305 static int menu_sel = 0;\r
59633198 1306 int menu_sel_max, ret;\r
720ee7f6 1307 unsigned long inp = 0;\r
59633198 1308 menu_id selected_id;\r
720ee7f6 1309\r
144a28a0 1310 me_enable(opt_entries, OPT_ENTRY_COUNT, MA_OPT_SAVECFG_GAME, rom_loaded);\r
59633198 1311 me_enable(opt_entries, OPT_ENTRY_COUNT, MA_OPT_LOADCFG, config_slot != config_slot_current);\r
1312 menu_sel_max = me_count_enabled(opt_entries, OPT_ENTRY_COUNT) - 1;\r
95151aea 1313 if (menu_sel > menu_sel_max) menu_sel = menu_sel_max;\r
59633198 1314\r
1315 while (1)\r
720ee7f6 1316 {\r
1317 draw_menu_options(menu_sel);\r
1318 inp = wait_for_input(GP2X_UP|GP2X_DOWN|GP2X_LEFT|GP2X_RIGHT|GP2X_B|GP2X_X|GP2X_A);\r
59633198 1319 if (inp & GP2X_UP ) { menu_sel--; if (menu_sel < 0) menu_sel = menu_sel_max; }\r
1320 if (inp & GP2X_DOWN) { menu_sel++; if (menu_sel > menu_sel_max) menu_sel = 0; }\r
1321 selected_id = me_index2id(opt_entries, OPT_ENTRY_COUNT, menu_sel);\r
0ae25549 1322 if (inp & (GP2X_LEFT|GP2X_RIGHT)) { // multi choice\r
59633198 1323 if (!me_process(opt_entries, OPT_ENTRY_COUNT, selected_id, (inp&GP2X_RIGHT) ? 1 : 0)) {\r
1324 switch (selected_id) {\r
1325 case MA_OPT_RENDERER:\r
1326 if (inp & GP2X_LEFT) {\r
0ae25549 1327 if (PicoOpt&0x10) PicoOpt&= ~0x10;\r
59633198 1328 else if (!(currentConfig.EmuOpt &0x80))currentConfig.EmuOpt |= 0x80;\r
1329 else if ( currentConfig.EmuOpt &0x80) break;\r
1330 } else {\r
0ae25549 1331 if (PicoOpt&0x10) break;\r
1332 else if (!(currentConfig.EmuOpt &0x80))PicoOpt|= 0x10;\r
59633198 1333 else if ( currentConfig.EmuOpt &0x80) currentConfig.EmuOpt &= ~0x80;\r
1334 }\r
1335 break;\r
1336 case MA_OPT_SOUND_QUALITY:\r
0ae25549 1337 if ((inp & GP2X_RIGHT) && PsndRate == 44100 && !(PicoOpt&0x08)) {\r
1338 PsndRate = 8000; PicoOpt|= 0x08;\r
1339 } else if ((inp & GP2X_LEFT) && PsndRate == 8000 && (PicoOpt&0x08)) {\r
1340 PsndRate = 44100; PicoOpt&=~0x08;\r
1341 } else PsndRate = sndrate_prevnext(PsndRate, inp & GP2X_RIGHT);\r
59633198 1342 break;\r
1343 case MA_OPT_REGION:\r
1344 region_prevnext(inp & GP2X_RIGHT);\r
1345 break;\r
1346 case MA_OPT_CONFIRM_STATES: {\r
1347 int n = ((currentConfig.EmuOpt>>9)&1) | ((currentConfig.EmuOpt>>10)&2);\r
1348 n += (inp & GP2X_LEFT) ? -1 : 1;\r
1349 if (n < 0) n = 0; else if (n > 3) n = 3;\r
1350 n |= n << 1; n &= ~2;\r
1351 currentConfig.EmuOpt &= ~0xa00;\r
1352 currentConfig.EmuOpt |= n << 9;\r
1353 break;\r
1354 }\r
1355 case MA_OPT_SAVE_SLOT:\r
1356 if (inp & GP2X_RIGHT) {\r
1357 state_slot++; if (state_slot > 9) state_slot = 0;\r
1358 } else {state_slot--; if (state_slot < 0) state_slot = 9;\r
1359 }\r
1360 break;\r
1361 case MA_OPT_CPU_CLOCKS:\r
1362 while ((inp = gp2x_joystick_read(1)) & (GP2X_LEFT|GP2X_RIGHT)) {\r
1363 currentConfig.CPUclock += (inp & GP2X_LEFT) ? -1 : 1;\r
1364 if (currentConfig.CPUclock < 1) currentConfig.CPUclock = 1;\r
1365 draw_menu_options(menu_sel);\r
1366 usleep(50*1000);\r
1367 }\r
1368 break;\r
1369 case MA_OPT_SAVECFG:\r
1370 case MA_OPT_SAVECFG_GAME:\r
1371 case MA_OPT_LOADCFG:\r
1372 config_slot += (inp&GP2X_RIGHT) ? 1 : -1;\r
1373 if (config_slot > 9) config_slot = 0;\r
1374 if (config_slot < 0) config_slot = 9;\r
1375 me_enable(opt_entries, OPT_ENTRY_COUNT, MA_OPT_LOADCFG, config_slot != config_slot_current);\r
1376 menu_sel_max = me_count_enabled(opt_entries, OPT_ENTRY_COUNT) - 1;\r
1377 if (menu_sel > menu_sel_max) menu_sel = menu_sel_max;\r
1378 break;\r
1379 default:\r
1380 //printf("%s: something unknown selected (%i)\n", __FUNCTION__, selected_id);\r
1381 break;\r
1382 }\r
1383 }\r
1384 }\r
1385 if (inp & GP2X_B) {\r
1386 if (!me_process(opt_entries, OPT_ENTRY_COUNT, selected_id, 1))\r
1387 {\r
1388 switch (selected_id)\r
1389 {\r
1390 case MA_OPT_SCD_OPTS:\r
1391 cd_menu_loop_options();\r
1392 if (engineState == PGS_ReloadRom)\r
1393 return 0; // test BIOS\r
1394 break;\r
1395 case MA_OPT_ADV_OPTS:\r
1396 amenu_loop_options();\r
1397 break;\r
1398 case MA_OPT_SAVECFG: // done (update and write)\r
1399 menu_options_save();\r
1400 if (emu_WriteConfig(0)) strcpy(menuErrorMsg, "config saved");\r
1401 else strcpy(menuErrorMsg, "failed to write config");\r
1402 return 1;\r
1403 case MA_OPT_SAVECFG_GAME: // done (update and write for current game)\r
1404 menu_options_save();\r
1405 if (emu_WriteConfig(1)) strcpy(menuErrorMsg, "config saved");\r
1406 else strcpy(menuErrorMsg, "failed to write config");\r
1407 return 1;\r
1408 case MA_OPT_LOADCFG:\r
95151aea 1409 ret = emu_ReadConfig(1, 1);\r
1410 if (!ret) ret = emu_ReadConfig(0, 1);\r
1411 if (ret) strcpy(menuErrorMsg, "config loaded");\r
59633198 1412 else strcpy(menuErrorMsg, "failed to load config");\r
1413 return 1;\r
1414 default:\r
1415 //printf("%s: something unknown selected (%i)\n", __FUNCTION__, selected_id);\r
1416 break;\r
1417 }\r
720ee7f6 1418 }\r
1419 }\r
979ba09f 1420 if(inp & (GP2X_X|GP2X_A)) {\r
720ee7f6 1421 menu_options_save();\r
daf91588 1422 return 0; // done (update, no write)\r
720ee7f6 1423 }\r
720ee7f6 1424 }\r
1425}\r
1426\r
1427// -------------- credits --------------\r
1428\r
1429static void draw_menu_credits(void)\r
1430{\r
260f1bcc 1431 int tl_x = 15, tl_y = 56, y;\r
e5d315a5 1432 gp2x_pd_clone_buffer2();\r
720ee7f6 1433\r
30d5518a 1434 text_out16(tl_x, 20, "PicoDrive v" VERSION " (c) notaz, 2006-2008");\r
720ee7f6 1435 y = tl_y;\r
13059a60 1436 text_out16(tl_x, y, "Credits:");\r
4b8f4f3c 1437 text_out16(tl_x, (y+=10), "fDave: Cyclone 68000 core,");\r
13059a60 1438 text_out16(tl_x, (y+=10), " base code of PicoDrive");\r
1439 text_out16(tl_x, (y+=10), "Reesy & FluBBa: DrZ80 core");\r
1440 text_out16(tl_x, (y+=10), "MAME devs: YM2612 and SN76496 cores");\r
13059a60 1441 text_out16(tl_x, (y+=10), "rlyeh and others: minimal SDK");\r
1442 text_out16(tl_x, (y+=10), "Squidge: squidgehack");\r
1443 text_out16(tl_x, (y+=10), "Dzz: ARM940 sample");\r
260f1bcc 1444 text_out16(tl_x, (y+=10), "GnoStiC / Puck2099: USB joy code");\r
13059a60 1445 text_out16(tl_x, (y+=10), "craigix: GP2X hardware");\r
f9a37a02 1446 text_out16(tl_x, (y+=10), "ketchupgun: skin design");\r
13059a60 1447\r
7a938d57 1448 text_out16(tl_x, (y+=20), "special thanks (for docs, ideas):");\r
260f1bcc 1449 text_out16(tl_x, (y+=10), " Charles MacDonald, Haze,");\r
1450 text_out16(tl_x, (y+=10), " Stephane Dallongeville,");\r
1451 text_out16(tl_x, (y+=10), " Lordus, Exophase, Rokas,");\r
1452 text_out16(tl_x, (y+=10), " Nemesis, Tasco Deluxe");\r
1453\r
13059a60 1454 menu_flip();\r
720ee7f6 1455}\r
1456\r
1457\r
1458// -------------- root menu --------------\r
1459\r
59633198 1460menu_entry main_entries[] =\r
1461{\r
0ae25549 1462 { "Resume game", MB_NONE, MA_MAIN_RESUME_GAME, NULL, 0, 0, 0, 0, 0 },\r
1463 { "Save State", MB_NONE, MA_MAIN_SAVE_STATE, NULL, 0, 0, 0, 0, 0 },\r
1464 { "Load State", MB_NONE, MA_MAIN_LOAD_STATE, NULL, 0, 0, 0, 0, 0 },\r
1465 { "Reset game", MB_NONE, MA_MAIN_RESET_GAME, NULL, 0, 0, 0, 0, 0 },\r
1466 { "Load new ROM/ISO", MB_NONE, MA_MAIN_LOAD_ROM, NULL, 0, 0, 0, 1, 0 },\r
1467 { "Change options", MB_NONE, MA_MAIN_OPTIONS, NULL, 0, 0, 0, 1, 0 },\r
1468 { "Configure controls", MB_NONE, MA_MAIN_CONTROLS, NULL, 0, 0, 0, 1, 0 },\r
1469 { "Credits", MB_NONE, MA_MAIN_CREDITS, NULL, 0, 0, 0, 1, 0 },\r
1470 { "Patches / GameGenie",MB_NONE, MA_MAIN_PATCHES, NULL, 0, 0, 0, 0, 0 },\r
1471 { "Exit", MB_NONE, MA_MAIN_EXIT, NULL, 0, 0, 0, 1, 0 }\r
59633198 1472};\r
1473\r
1474#define MAIN_ENTRY_COUNT (sizeof(main_entries) / sizeof(main_entries[0]))\r
1475\r
720ee7f6 1476static void draw_menu_root(int menu_sel)\r
1477{\r
59633198 1478 const int tl_x = 70, tl_y = 70;\r
1479\r
e5d315a5 1480 gp2x_pd_clone_buffer2();\r
720ee7f6 1481\r
13059a60 1482 text_out16(tl_x, 20, "PicoDrive v" VERSION);\r
1483\r
c7a4ff64 1484 menu_draw_selection(tl_x - 16, tl_y + menu_sel*10, 146);\r
720ee7f6 1485\r
59633198 1486 me_draw(main_entries, MAIN_ENTRY_COUNT, tl_x, tl_y, NULL, NULL);\r
720ee7f6 1487\r
720ee7f6 1488 // error\r
13059a60 1489 if (menuErrorMsg[0]) {\r
1490 memset((char *)gp2x_screen + 320*224*2, 0, 320*16*2);\r
1491 text_out16(5, 226, menuErrorMsg);\r
1492 }\r
1493 menu_flip();\r
720ee7f6 1494}\r
1495\r
1496\r
1497static void menu_loop_root(void)\r
1498{\r
59633198 1499 static int menu_sel = 0;\r
1500 int ret, menu_sel_max;\r
720ee7f6 1501 unsigned long inp = 0;\r
720ee7f6 1502\r
144a28a0 1503 me_enable(main_entries, MAIN_ENTRY_COUNT, MA_MAIN_RESUME_GAME, rom_loaded);\r
1504 me_enable(main_entries, MAIN_ENTRY_COUNT, MA_MAIN_SAVE_STATE, rom_loaded);\r
1505 me_enable(main_entries, MAIN_ENTRY_COUNT, MA_MAIN_LOAD_STATE, rom_loaded);\r
1506 me_enable(main_entries, MAIN_ENTRY_COUNT, MA_MAIN_RESET_GAME, rom_loaded);\r
59633198 1507 me_enable(main_entries, MAIN_ENTRY_COUNT, MA_MAIN_PATCHES, PicoPatches != NULL);\r
1508\r
1509 menu_sel_max = me_count_enabled(main_entries, MAIN_ENTRY_COUNT) - 1;\r
95151aea 1510 if (menu_sel > menu_sel_max) menu_sel = menu_sel_max;\r
720ee7f6 1511\r
79cad122 1512 /* make sure action buttons are not pressed on entering menu */\r
1513 draw_menu_root(menu_sel);\r
1514 while (gp2x_joystick_read(1) & (GP2X_B|GP2X_X|GP2X_SELECT)) usleep(50*1000);\r
1515\r
1516 for (;;)\r
720ee7f6 1517 {\r
1518 draw_menu_root(menu_sel);\r
0ae6813e 1519 inp = wait_for_input(GP2X_UP|GP2X_DOWN|GP2X_B|GP2X_X|GP2X_SELECT|GP2X_L|GP2X_R);\r
59633198 1520 if(inp & GP2X_UP ) { menu_sel--; if (menu_sel < 0) menu_sel = menu_sel_max; }\r
1521 if(inp & GP2X_DOWN) { menu_sel++; if (menu_sel > menu_sel_max) menu_sel = 0; }\r
0ae6813e 1522 if((inp & (GP2X_L|GP2X_R)) == (GP2X_L|GP2X_R)) debug_menu_loop();\r
720ee7f6 1523 if(inp &(GP2X_SELECT|GP2X_X)){\r
144a28a0 1524 if (rom_loaded) {\r
720ee7f6 1525 while (gp2x_joystick_read(1) & (GP2X_SELECT|GP2X_X)) usleep(50*1000); // wait until select is released\r
1526 engineState = PGS_Running;\r
1527 break;\r
1528 }\r
1529 }\r
59633198 1530 if(inp & GP2X_B) {\r
1531 switch (me_index2id(main_entries, MAIN_ENTRY_COUNT, menu_sel))\r
1532 {\r
1533 case MA_MAIN_RESUME_GAME:\r
144a28a0 1534 if (rom_loaded) {\r
5e2e14f2 1535 while (gp2x_joystick_read(1) & GP2X_B) usleep(50*1000);\r
1536 engineState = PGS_Running;\r
1537 return;\r
1538 }\r
720ee7f6 1539 break;\r
59633198 1540 case MA_MAIN_SAVE_STATE:\r
144a28a0 1541 if (rom_loaded) {\r
4ffd2858 1542 if(savestate_menu_loop(0))\r
720ee7f6 1543 continue;\r
720ee7f6 1544 engineState = PGS_Running;\r
1545 return;\r
1546 }\r
1547 break;\r
59633198 1548 case MA_MAIN_LOAD_STATE:\r
144a28a0 1549 if (rom_loaded) {\r
4ffd2858 1550 if(savestate_menu_loop(1))\r
720ee7f6 1551 continue;\r
446b090c 1552 while (gp2x_joystick_read(1) & GP2X_B) usleep(50*1000);\r
720ee7f6 1553 engineState = PGS_Running;\r
1554 return;\r
1555 }\r
1556 break;\r
59633198 1557 case MA_MAIN_RESET_GAME:\r
144a28a0 1558 if (rom_loaded) {\r
720ee7f6 1559 emu_ResetGame();\r
446b090c 1560 while (gp2x_joystick_read(1) & GP2X_B) usleep(50*1000);\r
720ee7f6 1561 engineState = PGS_Running;\r
1562 return;\r
1563 }\r
1564 break;\r
59633198 1565 case MA_MAIN_LOAD_ROM:\r
13059a60 1566 {\r
1567 char curr_path[PATH_MAX], *selfname;\r
1568 FILE *tstf;\r
367b6f1f 1569 if ( (tstf = fopen(lastRomFile, "rb")) )\r
13059a60 1570 {\r
1571 fclose(tstf);\r
367b6f1f 1572 strcpy(curr_path, lastRomFile);\r
13059a60 1573 }\r
1574 else\r
1575 getcwd(curr_path, PATH_MAX);\r
720ee7f6 1576 selfname = romsel_loop(curr_path);\r
1577 if (selfname) {\r
1578 printf("selected file: %s\n", selfname);\r
720ee7f6 1579 engineState = PGS_ReloadRom;\r
13059a60 1580 return;\r
720ee7f6 1581 }\r
13059a60 1582 break;\r
1583 }\r
59633198 1584 case MA_MAIN_OPTIONS:\r
daf91588 1585 ret = menu_loop_options();\r
1586 if (ret == 1) continue; // status update\r
1587 if (engineState == PGS_ReloadRom)\r
1588 return; // BIOS test\r
720ee7f6 1589 break;\r
59633198 1590 case MA_MAIN_CONTROLS:\r
720ee7f6 1591 kc_sel_loop();\r
1592 break;\r
59633198 1593 case MA_MAIN_CREDITS:\r
720ee7f6 1594 draw_menu_credits();\r
1595 usleep(500*1000);\r
1596 inp = wait_for_input(GP2X_B|GP2X_X);\r
1597 break;\r
59633198 1598 case MA_MAIN_EXIT:\r
720ee7f6 1599 engineState = PGS_Quit;\r
1600 return;\r
59633198 1601 case MA_MAIN_PATCHES:\r
144a28a0 1602 if (rom_loaded && PicoPatches) {\r
70d2ecc5 1603 patches_menu_loop();\r
1604 PicoPatchApply();\r
1605 strcpy(menuErrorMsg, "Patches applied");\r
1606 continue;\r
1607 }\r
1608 break;\r
59633198 1609 default:\r
1610 printf("%s: something unknown selected\n", __FUNCTION__);\r
1611 break;\r
720ee7f6 1612 }\r
1613 }\r
1614 menuErrorMsg[0] = 0; // clear error msg\r
1615 }\r
1616}\r
1617\r
f9a37a02 1618static void menu_darken_bg(void *dst, int pixels, int darker)\r
720ee7f6 1619{\r
13059a60 1620 unsigned int *screen = dst;\r
1621 pixels /= 2;\r
f9a37a02 1622 if (darker)\r
13059a60 1623 {\r
f9a37a02 1624 while (pixels--)\r
1625 {\r
1626 unsigned int p = *screen;\r
1627 *screen++ = ((p&0xf79ef79e)>>1) - ((p&0xc618c618)>>3);\r
1628 }\r
1629 }\r
1630 else\r
1631 {\r
1632 while (pixels--)\r
1633 {\r
1634 unsigned int p = *screen;\r
1635 *screen++ = (p&0xf79ef79e)>>1;\r
1636 }\r
13059a60 1637 }\r
1638}\r
e5d315a5 1639\r
13059a60 1640static void menu_prepare_bg(int use_game_bg)\r
1641{\r
1642 if (use_game_bg)\r
1643 {\r
1644 // darken the active framebuffer\r
1645 memset(gp2x_screen, 0, 320*8*2);\r
f9a37a02 1646 menu_darken_bg((char *)gp2x_screen + 320*8*2, 320*224, 1);\r
13059a60 1647 memset((char *)gp2x_screen + 320*232*2, 0, 320*8*2);\r
1648 }\r
1649 else\r
1650 {\r
1651 // should really only happen once, on startup..\r
1652 readpng(gp2x_screen, "skin/background.png", READPNG_BG);\r
4ffd2858 1653 }\r
720ee7f6 1654\r
13059a60 1655 // copy to buffer2\r
1656 gp2x_memcpy_buffers((1<<2), gp2x_screen, 0, 320*240*2);\r
4ffd2858 1657}\r
1658\r
1659static void menu_gfx_prepare(void)\r
1660{\r
144a28a0 1661 menu_prepare_bg(rom_loaded);\r
4ffd2858 1662\r
13059a60 1663 // switch to 16bpp\r
1664 gp2x_video_changemode2(16);\r
79cad122 1665 gp2x_video_RGB_setscaling(0, 320, 240);\r
13059a60 1666 menu_flip();\r
e5d315a5 1667}\r
1668\r
1669\r
1670void menu_loop(void)\r
1671{\r
1672 menu_gfx_prepare();\r
720ee7f6 1673\r
1674 menu_loop_root();\r
1675\r
1676 menuErrorMsg[0] = 0;\r
1677}\r
5e2e14f2 1678\r
1679\r
1680// --------- CD tray close menu ----------\r
1681\r
1682static void draw_menu_tray(int menu_sel)\r
1683{\r
1684 int tl_x = 70, tl_y = 90, y;\r
13059a60 1685 memset(gp2x_screen, 0, 320*240*2);\r
5e2e14f2 1686\r
13059a60 1687 text_out16(tl_x, 20, "The unit is about to");\r
1688 text_out16(tl_x, 30, "close the CD tray.");\r
5e2e14f2 1689\r
1690 y = tl_y;\r
13059a60 1691 text_out16(tl_x, y, "Load new CD image");\r
1692 text_out16(tl_x, (y+=10), "Insert nothing");\r
5e2e14f2 1693\r
1694 // draw cursor\r
13059a60 1695 text_out16(tl_x - 16, tl_y + menu_sel*10, ">");\r
5e2e14f2 1696 // error\r
13059a60 1697 if (menuErrorMsg[0]) text_out16(5, 226, menuErrorMsg);\r
1698 menu_flip();\r
5e2e14f2 1699}\r
1700\r
1701\r
1702int menu_loop_tray(void)\r
1703{\r
1704 int menu_sel = 0, menu_sel_max = 1;\r
1705 unsigned long inp = 0;\r
1706 char curr_path[PATH_MAX], *selfname;\r
1707 FILE *tstf;\r
1708\r
c7a4ff64 1709 gp2x_memset_all_buffers(0, 0, 320*240*2);\r
5e2e14f2 1710 menu_gfx_prepare();\r
1711\r
367b6f1f 1712 if ( (tstf = fopen(lastRomFile, "rb")) )\r
5e2e14f2 1713 {\r
1714 fclose(tstf);\r
367b6f1f 1715 strcpy(curr_path, lastRomFile);\r
5e2e14f2 1716 }\r
1717 else\r
1718 {\r
1719 getcwd(curr_path, PATH_MAX);\r
1720 }\r
1721\r
1722 /* make sure action buttons are not pressed on entering menu */\r
1723 draw_menu_tray(menu_sel);\r
1724 while (gp2x_joystick_read(1) & GP2X_B) usleep(50*1000);\r
1725\r
1726 for (;;)\r
1727 {\r
1728 draw_menu_tray(menu_sel);\r
1729 inp = wait_for_input(GP2X_UP|GP2X_DOWN|GP2X_B);\r
1730 if(inp & GP2X_UP ) { menu_sel--; if (menu_sel < 0) menu_sel = menu_sel_max; }\r
1731 if(inp & GP2X_DOWN) { menu_sel++; if (menu_sel > menu_sel_max) menu_sel = 0; }\r
1732 if(inp & GP2X_B ) {\r
1733 switch (menu_sel) {\r
1734 case 0: // select image\r
1735 selfname = romsel_loop(curr_path);\r
1736 if (selfname) {\r
1ceda417 1737 int ret = -1;\r
1738 cd_img_type cd_type;\r
f013066e 1739 cd_type = emu_cdCheck(NULL);\r
1ceda417 1740 if (cd_type != CIT_NOT_CD)\r
1741 ret = Insert_CD(romFileName, cd_type);\r
5e2e14f2 1742 if (ret != 0) {\r
1743 sprintf(menuErrorMsg, "Load failed, invalid CD image?");\r
1744 printf("%s\n", menuErrorMsg);\r
1745 continue;\r
1746 }\r
1747 engineState = PGS_RestartRun;\r
1748 return 1;\r
1749 }\r
1750 break;\r
1751 case 1: // insert nothing\r
1752 engineState = PGS_RestartRun;\r
1753 return 0;\r
1754 }\r
1755 }\r
1756 menuErrorMsg[0] = 0; // clear error msg\r
1757 }\r
1758}\r
1759\r
1760\r