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