cue support wip
[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
5e2e14f2 49 static int repeats = 0, wait = 50*1000;\r
720ee7f6 50 int release = 0, i;\r
51\r
5e2e14f2 52 if (repeats == 2 || repeats == 4) wait /= 2;\r
53 if (repeats == 6) wait = 15 * 1000;\r
720ee7f6 54\r
55 for (i = 0; i < 6 && inp_prev == gp2x_joystick_read(1); i++) {\r
5e2e14f2 56 if (i == 0) repeats++;\r
57 if (wait >= 30*1000) usleep(wait); // usleep sleeps for ~30ms minimum\r
58 else spend_cycles(wait * currentConfig.CPUclock);\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
5e2e14f2 68 wait = 50*1000;\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
c7a4ff64 754 menu_draw_selection(x - 16, tl_y + sel*10, (player_idx >= 0) ? 66 : 130);\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
95151aea 860// player2_flag, ?, ?, ?, ?, ?, ?, menu\r
861// "NEXT SAVE SLOT", "PREV SAVE SLOT", "SWITCH RENDERER", "SAVE STATE",\r
862// "LOAD STATE", "VOLUME UP", "VOLUME DOWN", "DONE"\r
367b6f1f 863me_bind_action emuctrl_actions[] =\r
95151aea 864{\r
95151aea 865 { "Load State ", 1<<28 },\r
866 { "Save State ", 1<<27 },\r
95151aea 867 { "Prev Save Slot ", 1<<25 },\r
868 { "Next Save Slot ", 1<<24 },\r
13059a60 869 { "Switch Renderer", 1<<26 },\r
870 { "Volume Down ", 1<<30 },\r
871 { "Volume Up ", 1<<29 },\r
0480e6c9 872 { "Fast forward ", 1<<22 },\r
95151aea 873 { "Enter Menu ", 1<<23 },\r
367b6f1f 874 { NULL, 0 }\r
95151aea 875};\r
876\r
720ee7f6 877static void kc_sel_loop(void)\r
878{\r
95151aea 879 int menu_sel = 3, menu_sel_max = 3;\r
720ee7f6 880 unsigned long inp = 0;\r
dd5fd477 881 int is_6button = PicoOpt & POPT_6BTN_PAD;\r
720ee7f6 882\r
95151aea 883 while (1)\r
720ee7f6 884 {\r
885 draw_kc_sel(menu_sel);\r
886 inp = wait_for_input(GP2X_UP|GP2X_DOWN|GP2X_B|GP2X_X);\r
95151aea 887 if (inp & GP2X_UP ) { menu_sel--; if (menu_sel < 0) menu_sel = menu_sel_max; }\r
888 if (inp & GP2X_DOWN) { menu_sel++; if (menu_sel > menu_sel_max) menu_sel = 0; }\r
889 if (inp & GP2X_B) {\r
720ee7f6 890 switch (menu_sel) {\r
367b6f1f 891 case 0: key_config_loop(me_ctrl_actions, is_6button ? 12 : 8, 0); return;\r
892 case 1: key_config_loop(me_ctrl_actions, is_6button ? 12 : 8, 1); return;\r
95151aea 893 case 2: key_config_loop(emuctrl_actions,\r
8e708f92 894 sizeof(emuctrl_actions)/sizeof(emuctrl_actions[0]) - 1, -1); return;\r
144a28a0 895 case 3: if (!rom_loaded) emu_WriteConfig(0); return;\r
720ee7f6 896 default: return;\r
897 }\r
898 }\r
95151aea 899 if (inp & GP2X_X) return;\r
720ee7f6 900 }\r
901}\r
902\r
903\r
daf91588 904// --------- sega/mega cd options ----------\r
905\r
59633198 906menu_entry cdopt_entries[] =\r
daf91588 907{\r
0ae25549 908 { NULL, MB_NONE, MA_CDOPT_TESTBIOS_USA, NULL, 0, 0, 0, 1, 0 },\r
909 { NULL, MB_NONE, MA_CDOPT_TESTBIOS_EUR, NULL, 0, 0, 0, 1, 0 },\r
910 { NULL, MB_NONE, MA_CDOPT_TESTBIOS_JAP, NULL, 0, 0, 0, 1, 0 },\r
911 { "CD LEDs", MB_ONOFF, MA_CDOPT_LEDS, &currentConfig.EmuOpt, 0x0400, 0, 0, 1, 1 },\r
912 { "CDDA audio (using mp3s)", MB_ONOFF, MA_CDOPT_CDDA, &PicoOpt, 0x0800, 0, 0, 1, 1 },\r
913 { "PCM audio", MB_ONOFF, MA_CDOPT_PCM, &PicoOpt, 0x0400, 0, 0, 1, 1 },\r
914 { NULL, MB_NONE, MA_CDOPT_READAHEAD, NULL, 0, 0, 0, 1, 1 },\r
915 { "SaveRAM cart", MB_ONOFF, MA_CDOPT_SAVERAM, &PicoOpt, 0x8000, 0, 0, 1, 1 },\r
916 { "Scale/Rot. fx (slow)", MB_ONOFF, MA_CDOPT_SCALEROT_CHIP,&PicoOpt, 0x1000, 0, 0, 1, 1 },\r
917 { "Better sync (slow)", MB_ONOFF, MA_CDOPT_BETTER_SYNC, &PicoOpt, 0x2000, 0, 0, 1, 1 },\r
918 { "done", MB_NONE, MA_CDOPT_DONE, NULL, 0, 0, 0, 1, 0 },\r
59633198 919};\r
920\r
921#define CDOPT_ENTRY_COUNT (sizeof(cdopt_entries) / sizeof(cdopt_entries[0]))\r
30d5518a 922const int cdopt_entry_count = CDOPT_ENTRY_COUNT;\r
59633198 923\r
924\r
925struct bios_names_t\r
926{\r
927 char us[32], eu[32], jp[32];\r
928};\r
929\r
930static void menu_cdopt_cust_draw(const menu_entry *entry, int x, int y, void *param)\r
931{\r
932 struct bios_names_t *bios_names = param;\r
933 char ra_buff[16];\r
934\r
935 switch (entry->id)\r
936 {\r
13059a60 937 case MA_CDOPT_TESTBIOS_USA: text_out16(x, y, "USA BIOS: %s", bios_names->us); break;\r
938 case MA_CDOPT_TESTBIOS_EUR: text_out16(x, y, "EUR BIOS: %s", bios_names->eu); break;\r
939 case MA_CDOPT_TESTBIOS_JAP: text_out16(x, y, "JAP BIOS: %s", bios_names->jp); break;\r
59633198 940 case MA_CDOPT_READAHEAD:\r
941 if (PicoCDBuffers > 1) sprintf(ra_buff, "%5iK", PicoCDBuffers * 2);\r
942 else strcpy(ra_buff, " OFF");\r
13059a60 943 text_out16(x, y, "ReadAhead buffer %s", ra_buff);\r
59633198 944 break;\r
945 default:break;\r
946 }\r
947}\r
948\r
949static void draw_cd_menu_options(int menu_sel, struct bios_names_t *bios_names)\r
950{\r
951 int tl_x = 25, tl_y = 60;\r
952 menu_id selected_id;\r
5f9922e6 953 char ra_buff[16];\r
954\r
955 if (PicoCDBuffers > 1) sprintf(ra_buff, "%5iK", PicoCDBuffers * 2);\r
956 else strcpy(ra_buff, " OFF");\r
daf91588 957\r
e5d315a5 958 gp2x_pd_clone_buffer2();\r
959\r
c7a4ff64 960 menu_draw_selection(tl_x - 16, tl_y + menu_sel*10, 246);\r
daf91588 961\r
13059a60 962 me_draw(cdopt_entries, CDOPT_ENTRY_COUNT, tl_x, tl_y, menu_cdopt_cust_draw, bios_names);\r
daf91588 963\r
59633198 964 selected_id = me_index2id(cdopt_entries, CDOPT_ENTRY_COUNT, menu_sel);\r
965 if ((selected_id == MA_CDOPT_TESTBIOS_USA && strcmp(bios_names->us, "NOT FOUND")) ||\r
966 (selected_id == MA_CDOPT_TESTBIOS_EUR && strcmp(bios_names->eu, "NOT FOUND")) ||\r
967 (selected_id == MA_CDOPT_TESTBIOS_JAP && strcmp(bios_names->jp, "NOT FOUND")))\r
13059a60 968 text_out16(tl_x, 210, "Press start to test selected BIOS");\r
daf91588 969\r
13059a60 970 menu_flip();\r
daf91588 971}\r
972\r
973static void cd_menu_loop_options(void)\r
974{\r
59d0f042 975 static int menu_sel = 0;\r
976 int menu_sel_max = 10;\r
daf91588 977 unsigned long inp = 0;\r
59633198 978 struct bios_names_t bios_names;\r
979 menu_id selected_id;\r
980 char *bios, *p;\r
daf91588 981\r
f013066e 982 if (emu_findBios(4, &bios)) { // US\r
daf91588 983 for (p = bios+strlen(bios)-1; p > bios && *p != '/'; p--); p++;\r
59633198 984 strncpy(bios_names.us, p, sizeof(bios_names.us)); bios_names.us[sizeof(bios_names.us)-1] = 0;\r
985 } else strcpy(bios_names.us, "NOT FOUND");\r
daf91588 986\r
f013066e 987 if (emu_findBios(8, &bios)) { // EU\r
daf91588 988 for (p = bios+strlen(bios)-1; p > bios && *p != '/'; p--); p++;\r
59633198 989 strncpy(bios_names.eu, p, sizeof(bios_names.eu)); bios_names.eu[sizeof(bios_names.eu)-1] = 0;\r
990 } else strcpy(bios_names.eu, "NOT FOUND");\r
daf91588 991\r
f013066e 992 if (emu_findBios(1, &bios)) { // JP\r
daf91588 993 for (p = bios+strlen(bios)-1; p > bios && *p != '/'; p--); p++;\r
59633198 994 strncpy(bios_names.jp, p, sizeof(bios_names.jp)); bios_names.jp[sizeof(bios_names.jp)-1] = 0;\r
995 } else strcpy(bios_names.jp, "NOT FOUND");\r
daf91588 996\r
997 for(;;)\r
998 {\r
59633198 999 draw_cd_menu_options(menu_sel, &bios_names);\r
daf91588 1000 inp = wait_for_input(GP2X_UP|GP2X_DOWN|GP2X_LEFT|GP2X_RIGHT|GP2X_B|GP2X_X|GP2X_A|GP2X_START);\r
59633198 1001 if (inp & GP2X_UP ) { menu_sel--; if (menu_sel < 0) menu_sel = menu_sel_max; }\r
1002 if (inp & GP2X_DOWN) { menu_sel++; if (menu_sel > menu_sel_max) menu_sel = 0; }\r
1003 selected_id = me_index2id(cdopt_entries, CDOPT_ENTRY_COUNT, menu_sel);\r
1004 if (inp & (GP2X_LEFT|GP2X_RIGHT)) { // multi choise\r
1005 if (!me_process(cdopt_entries, CDOPT_ENTRY_COUNT, selected_id, (inp&GP2X_RIGHT) ? 1 : 0) &&\r
1006 selected_id == MA_CDOPT_READAHEAD) {\r
1007 if (inp & GP2X_LEFT) {\r
1008 PicoCDBuffers >>= 1;\r
88bfc63d 1009 if (PicoCDBuffers < 2) PicoCDBuffers = 0;\r
59633198 1010 } else {\r
88bfc63d 1011 if (PicoCDBuffers < 2) PicoCDBuffers = 2;\r
59633198 1012 else PicoCDBuffers <<= 1;\r
1013 if (PicoCDBuffers > 8*1024) PicoCDBuffers = 8*1024; // 16M\r
1014 }\r
daf91588 1015 }\r
1016 }\r
59633198 1017 if (inp & GP2X_B) { // toggleable options\r
1018 if (!me_process(cdopt_entries, CDOPT_ENTRY_COUNT, selected_id, 1) &&\r
1019 selected_id == MA_CDOPT_DONE) {\r
1020 return;\r
1021 }\r
1022 }\r
1023 if (inp & GP2X_START) { // BIOS testers\r
1024 switch (selected_id) {\r
1025 case MA_CDOPT_TESTBIOS_USA:\r
f013066e 1026 if (emu_findBios(4, &bios)) { // test US\r
daf91588 1027 strcpy(romFileName, bios);\r
1028 engineState = PGS_ReloadRom;\r
1029 return;\r
1030 }\r
1031 break;\r
59633198 1032 case MA_CDOPT_TESTBIOS_EUR:\r
f013066e 1033 if (emu_findBios(8, &bios)) { // test EU\r
daf91588 1034 strcpy(romFileName, bios);\r
1035 engineState = PGS_ReloadRom;\r
1036 return;\r
1037 }\r
1038 break;\r
59633198 1039 case MA_CDOPT_TESTBIOS_JAP:\r
f013066e 1040 if (emu_findBios(1, &bios)) { // test JP\r
daf91588 1041 strcpy(romFileName, bios);\r
1042 engineState = PGS_ReloadRom;\r
1043 return;\r
1044 }\r
1045 break;\r
59633198 1046 default:\r
1047 break;\r
daf91588 1048 }\r
1049 }\r
59633198 1050 if (inp & (GP2X_X|GP2X_A)) return;\r
daf91588 1051 }\r
1052}\r
1053\r
1054\r
1055// --------- advanced options ----------\r
1056\r
59633198 1057menu_entry opt2_entries[] =\r
1058{\r
0ae25549 1059 { NULL, MB_NONE, MA_OPT2_GAMMA, NULL, 0, 0, 0, 1, 1 },\r
1060 { "A_SN's gamma curve", MB_ONOFF, MA_OPT2_A_SN_GAMMA, &currentConfig.EmuOpt, 0x1000, 0, 0, 1, 1 },\r
1061 { "Perfect vsync", MB_ONOFF, MA_OPT2_VSYNC, &currentConfig.EmuOpt, 0x2000, 0, 0, 1, 1 },\r
30d5518a 1062 { "Disable sprite limit", MB_ONOFF, MA_OPT2_NO_SPRITE_LIM, &PicoOpt, 0x40000, 0, 0, 1, 1 },\r
367b6f1f 1063 { "Emulate Z80", MB_ONOFF, MA_OPT2_ENABLE_Z80, &PicoOpt, 0x00004, 0, 0, 1, 1 },\r
1064 { "Emulate YM2612 (FM)", MB_ONOFF, MA_OPT2_ENABLE_YM2612, &PicoOpt, 0x00001, 0, 0, 1, 1 },\r
1065 { "Emulate SN76496 (PSG)", MB_ONOFF, MA_OPT2_ENABLE_SN76496,&PicoOpt, 0x00002, 0, 0, 1, 1 },\r
0ae25549 1066 { "gzip savestates", MB_ONOFF, MA_OPT2_GZIP_STATES, &currentConfig.EmuOpt, 0x0008, 0, 0, 1, 1 },\r
1067 { "Don't save last used ROM", MB_ONOFF, MA_OPT2_NO_LAST_ROM, &currentConfig.EmuOpt, 0x0020, 0, 0, 1, 1 },\r
1068 { "needs restart:", MB_NONE, MA_NONE, NULL, 0, 0, 0, 1, 0 },\r
1069 { "craigix's RAM timings", MB_ONOFF, MA_OPT2_RAMTIMINGS, &currentConfig.EmuOpt, 0x0100, 0, 0, 1, 1 },\r
1070 { NULL, MB_ONOFF, MA_OPT2_SQUIDGEHACK, &currentConfig.EmuOpt, 0x0010, 0, 0, 1, 1 },\r
367b6f1f 1071 { "SVP dynarec", MB_ONOFF, MA_OPT2_SVP_DYNAREC, &PicoOpt, 0x20000, 0, 0, 1, 1 },\r
0ae25549 1072 { "done", MB_NONE, MA_OPT2_DONE, NULL, 0, 0, 0, 1, 0 },\r
59633198 1073};\r
1074\r
1075#define OPT2_ENTRY_COUNT (sizeof(opt2_entries) / sizeof(opt2_entries[0]))\r
30d5518a 1076const int opt2_entry_count = OPT2_ENTRY_COUNT;\r
59633198 1077\r
1078static void menu_opt2_cust_draw(const menu_entry *entry, int x, int y, void *param)\r
1079{\r
1080 if (entry->id == MA_OPT2_GAMMA)\r
13059a60 1081 text_out16(x, y, "Gamma correction %i.%02i", currentConfig.gamma / 100, currentConfig.gamma%100);\r
59633198 1082 else if (entry->id == MA_OPT2_SQUIDGEHACK)\r
0ae25549 1083 text_out16(x, y, "Squidgehack (now %s %s", mmuhack_status ? "active) " : "inactive)",\r
59633198 1084 (currentConfig.EmuOpt&0x0010)?"ON":"OFF");\r
1085}\r
1086\r
1087\r
720ee7f6 1088static void draw_amenu_options(int menu_sel)\r
1089{\r
59633198 1090 int tl_x = 25, tl_y = 50;\r
720ee7f6 1091\r
e5d315a5 1092 gp2x_pd_clone_buffer2();\r
1093\r
c7a4ff64 1094 menu_draw_selection(tl_x - 16, tl_y + menu_sel*10, 252);\r
720ee7f6 1095\r
13059a60 1096 me_draw(opt2_entries, OPT2_ENTRY_COUNT, tl_x, tl_y, menu_opt2_cust_draw, NULL);\r
720ee7f6 1097\r
13059a60 1098 menu_flip();\r
720ee7f6 1099}\r
1100\r
1101static void amenu_loop_options(void)\r
1102{\r
59d0f042 1103 static int menu_sel = 0;\r
59633198 1104 int menu_sel_max;\r
720ee7f6 1105 unsigned long inp = 0;\r
59633198 1106 menu_id selected_id;\r
1107\r
1108 menu_sel_max = me_count_enabled(opt2_entries, OPT2_ENTRY_COUNT) - 1;\r
720ee7f6 1109\r
1110 for(;;)\r
1111 {\r
1112 draw_amenu_options(menu_sel);\r
1113 inp = wait_for_input(GP2X_UP|GP2X_DOWN|GP2X_LEFT|GP2X_RIGHT|GP2X_B|GP2X_X|GP2X_A);\r
59633198 1114 if (inp & GP2X_UP ) { menu_sel--; if (menu_sel < 0) menu_sel = menu_sel_max; }\r
1115 if (inp & GP2X_DOWN) { menu_sel++; if (menu_sel > menu_sel_max) menu_sel = 0; }\r
1116 selected_id = me_index2id(opt2_entries, OPT2_ENTRY_COUNT, menu_sel);\r
1117 if (inp & (GP2X_LEFT|GP2X_RIGHT)) { // multi choise\r
1118 if (!me_process(opt2_entries, OPT2_ENTRY_COUNT, selected_id, (inp&GP2X_RIGHT) ? 1 : 0) &&\r
1119 selected_id == MA_OPT2_GAMMA) {\r
1120 while ((inp = gp2x_joystick_read(1)) & (GP2X_LEFT|GP2X_RIGHT)) {\r
1121 currentConfig.gamma += (inp & GP2X_LEFT) ? -1 : 1;\r
1122 if (currentConfig.gamma < 1) currentConfig.gamma = 1;\r
1123 if (currentConfig.gamma > 300) currentConfig.gamma = 300;\r
1124 draw_amenu_options(menu_sel);\r
1125 usleep(18*1000);\r
1126 }\r
720ee7f6 1127 }\r
1128 }\r
59633198 1129 if (inp & GP2X_B) { // toggleable options\r
1130 if (!me_process(opt2_entries, OPT2_ENTRY_COUNT, selected_id, 1) &&\r
1131 selected_id == MA_OPT2_DONE) {\r
1132 return;\r
720ee7f6 1133 }\r
1134 }\r
59633198 1135 if (inp & (GP2X_X|GP2X_A)) return;\r
720ee7f6 1136 }\r
1137}\r
1138\r
1139// -------------- options --------------\r
1140\r
59633198 1141\r
1142menu_entry opt_entries[] =\r
1143{\r
0ae25549 1144 { NULL, MB_NONE, MA_OPT_RENDERER, NULL, 0, 0, 0, 1, 1 },\r
1145 { NULL, MB_RANGE, MA_OPT_SCALING, &currentConfig.scaling, 0, 0, 3, 1, 1 },\r
1146 { "Accurate timing (slower)", MB_ONOFF, MA_OPT_ACC_TIMING, &PicoOpt, 0x040, 0, 0, 1, 1 },\r
1147 { "Accurate sprites (slower)", MB_ONOFF, MA_OPT_ACC_SPRITES, &PicoOpt, 0x080, 0, 0, 1, 1 },\r
1148 { "Show FPS", MB_ONOFF, MA_OPT_SHOW_FPS, &currentConfig.EmuOpt, 0x002, 0, 0, 1, 1 },\r
1149 { NULL, MB_RANGE, MA_OPT_FRAMESKIP, &currentConfig.Frameskip, 0, -1, 16, 1, 1 },\r
1150 { "Enable sound", MB_ONOFF, MA_OPT_ENABLE_SOUND, &currentConfig.EmuOpt, 0x004, 0, 0, 1, 1 },\r
1151 { NULL, MB_NONE, MA_OPT_SOUND_QUALITY, NULL, 0, 0, 0, 1, 1 },\r
1152 { "Use ARM940 core for sound", MB_ONOFF, MA_OPT_ARM940_SOUND, &PicoOpt, 0x200, 0, 0, 1, 1 },\r
1153 { "6 button pad", MB_ONOFF, MA_OPT_6BUTTON_PAD, &PicoOpt, 0x020, 0, 0, 1, 1 },\r
1154 { NULL, MB_NONE, MA_OPT_REGION, NULL, 0, 0, 0, 1, 1 },\r
1155 { "Use SRAM/BRAM savestates", MB_ONOFF, MA_OPT_SRAM_STATES, &currentConfig.EmuOpt, 0x001, 0, 0, 1, 1 },\r
1156 { NULL, MB_NONE, MA_OPT_CONFIRM_STATES,NULL, 0, 0, 0, 1, 1 },\r
1157 { "Save slot", MB_RANGE, MA_OPT_SAVE_SLOT, &state_slot, 0, 0, 9, 1, 1 },\r
1158 { NULL, MB_NONE, MA_OPT_CPU_CLOCKS, NULL, 0, 0, 0, 1, 1 },\r
1159 { "[Sega/Mega CD options]", MB_NONE, MA_OPT_SCD_OPTS, NULL, 0, 0, 0, 1, 0 },\r
1160 { "[advanced options]", MB_NONE, MA_OPT_ADV_OPTS, NULL, 0, 0, 0, 1, 0 },\r
1161 { NULL, MB_NONE, MA_OPT_SAVECFG, NULL, 0, 0, 0, 1, 0 },\r
1162 { "Save cfg for current game only",MB_NONE,MA_OPT_SAVECFG_GAME,NULL, 0, 0, 0, 1, 0 },\r
1163 { NULL, MB_NONE, MA_OPT_LOADCFG, NULL, 0, 0, 0, 1, 0 },\r
59633198 1164};\r
1165\r
1166#define OPT_ENTRY_COUNT (sizeof(opt_entries) / sizeof(opt_entries[0]))\r
0ae25549 1167const int opt_entry_count = OPT_ENTRY_COUNT;\r
720ee7f6 1168\r
59633198 1169\r
1170static void menu_opt_cust_draw(const menu_entry *entry, int x, int y, void *param)\r
720ee7f6 1171{\r
59633198 1172 char *str, str24[24];\r
1173\r
1174 switch (entry->id)\r
1175 {\r
1176 case MA_OPT_RENDERER:\r
dd5fd477 1177 if (PicoOpt & POPT_ALT_RENDERER)\r
59633198 1178 str = " 8bit fast";\r
1179 else if (currentConfig.EmuOpt&0x80)\r
1180 str = "16bit accurate";\r
1181 else\r
1182 str = " 8bit accurate";\r
13059a60 1183 text_out16(x, y, "Renderer: %s", str);\r
59633198 1184 break;\r
1185 case MA_OPT_SCALING:\r
1186 switch (currentConfig.scaling) {\r
1187 default: str = " OFF"; break;\r
1188 case 1: str = "hw horizontal"; break;\r
1189 case 2: str = "hw horiz. + vert."; break;\r
1190 case 3: str = "sw horizontal"; break;\r
1191 }\r
13059a60 1192 text_out16(x, y, "Scaling: %s", str);\r
59633198 1193 break;\r
1194 case MA_OPT_FRAMESKIP:\r
1195 if (currentConfig.Frameskip < 0)\r
1196 strcpy(str24, "Auto");\r
1197 else sprintf(str24, "%i", currentConfig.Frameskip);\r
13059a60 1198 text_out16(x, y, "Frameskip %s", str24);\r
59633198 1199 break;\r
1200 case MA_OPT_SOUND_QUALITY:\r
dd5fd477 1201 str = (PicoOpt & POPT_EN_STEREO) ? "stereo" : "mono";\r
0ae25549 1202 text_out16(x, y, "Sound Quality: %5iHz %s", PsndRate, str);\r
59633198 1203 break;\r
1204 case MA_OPT_REGION:\r
0ae25549 1205 text_out16(x, y, "Region: %s", me_region_name(PicoRegionOverride, PicoAutoRgnOrder));\r
59633198 1206 break;\r
1207 case MA_OPT_CONFIRM_STATES:\r
1208 switch ((currentConfig.EmuOpt >> 9) & 5) {\r
1209 default: str = "OFF"; break;\r
1210 case 1: str = "writes"; break;\r
1211 case 4: str = "loads"; break;\r
1212 case 5: str = "both"; break;\r
1213 }\r
13059a60 1214 text_out16(x, y, "Confirm savestate %s", str);\r
59633198 1215 break;\r
1216 case MA_OPT_CPU_CLOCKS:\r
13059a60 1217 text_out16(x, y, "GP2X CPU clocks %iMhz", currentConfig.CPUclock);\r
59633198 1218 break;\r
1219 case MA_OPT_SAVECFG:\r
1220 str24[0] = 0;\r
1221 if (config_slot != 0) sprintf(str24, " (profile: %i)", config_slot);\r
13059a60 1222 text_out16(x, y, "Save cfg as default%s", str24);\r
59633198 1223 break;\r
1224 case MA_OPT_LOADCFG:\r
13059a60 1225 text_out16(x, y, "Load cfg from profile %i", config_slot);\r
59633198 1226 break;\r
1227 default:\r
1228 printf("%s: unimplemented (%i)\n", __FUNCTION__, entry->id);\r
1229 break;\r
46ede6a6 1230 }\r
59633198 1231}\r
1232\r
1233\r
1234\r
1235static void draw_menu_options(int menu_sel)\r
1236{\r
13059a60 1237 int tl_x = 25, tl_y = 24;\r
720ee7f6 1238\r
e5d315a5 1239 gp2x_pd_clone_buffer2();\r
1240\r
c7a4ff64 1241 menu_draw_selection(tl_x - 16, tl_y + menu_sel*10, 284);\r
720ee7f6 1242\r
13059a60 1243 me_draw(opt_entries, OPT_ENTRY_COUNT, tl_x, tl_y, menu_opt_cust_draw, NULL);\r
720ee7f6 1244\r
13059a60 1245 menu_flip();\r
720ee7f6 1246}\r
1247\r
1248static int sndrate_prevnext(int rate, int dir)\r
1249{\r
1250 int i, rates[] = { 8000, 11025, 16000, 22050, 44100 };\r
1251\r
1252 for (i = 0; i < 5; i++)\r
1253 if (rates[i] == rate) break;\r
1254\r
1255 i += dir ? 1 : -1;\r
1256 if (i > 4) return dir ? 44100 : 22050;\r
1257 if (i < 0) return dir ? 11025 : 8000;\r
1258 return rates[i];\r
1259}\r
1260\r
979ba09f 1261static void region_prevnext(int right)\r
1262{\r
1263 // jp_ntsc=1, jp_pal=2, usa=4, eu=8\r
1264 static int rgn_orders[] = { 0x148, 0x184, 0x814, 0x418, 0x841, 0x481 };\r
1265 int i;\r
1266 if (right) {\r
0ae25549 1267 if (!PicoRegionOverride) {\r
979ba09f 1268 for (i = 0; i < 6; i++)\r
1269 if (rgn_orders[i] == PicoAutoRgnOrder) break;\r
1270 if (i < 5) PicoAutoRgnOrder = rgn_orders[i+1];\r
0ae25549 1271 else PicoRegionOverride=1;\r
979ba09f 1272 }\r
0ae25549 1273 else PicoRegionOverride<<=1;\r
1274 if (PicoRegionOverride > 8) PicoRegionOverride = 8;\r
979ba09f 1275 } else {\r
0ae25549 1276 if (!PicoRegionOverride) {\r
979ba09f 1277 for (i = 0; i < 6; i++)\r
1278 if (rgn_orders[i] == PicoAutoRgnOrder) break;\r
1279 if (i > 0) PicoAutoRgnOrder = rgn_orders[i-1];\r
1280 }\r
0ae25549 1281 else PicoRegionOverride>>=1;\r
979ba09f 1282 }\r
1283}\r
1284\r
720ee7f6 1285static void menu_options_save(void)\r
1286{\r
426ecc58 1287 if (PicoRegionOverride) {\r
1288 // force setting possibly changed..\r
1289 Pico.m.pal = (PicoRegionOverride == 2 || PicoRegionOverride == 8) ? 1 : 0;\r
1290 }\r
dd5fd477 1291 if (!(PicoOpt & POPT_6BTN_PAD)) {\r
95151aea 1292 // unbind XYZ MODE, just in case\r
e67f6454 1293 unbind_action(0xf00, -1, -1);\r
720ee7f6 1294 }\r
1295}\r
1296\r
daf91588 1297static int menu_loop_options(void)\r
720ee7f6 1298{\r
59d0f042 1299 static int menu_sel = 0;\r
59633198 1300 int menu_sel_max, ret;\r
720ee7f6 1301 unsigned long inp = 0;\r
59633198 1302 menu_id selected_id;\r
720ee7f6 1303\r
144a28a0 1304 me_enable(opt_entries, OPT_ENTRY_COUNT, MA_OPT_SAVECFG_GAME, rom_loaded);\r
59633198 1305 me_enable(opt_entries, OPT_ENTRY_COUNT, MA_OPT_LOADCFG, config_slot != config_slot_current);\r
1306 menu_sel_max = me_count_enabled(opt_entries, OPT_ENTRY_COUNT) - 1;\r
95151aea 1307 if (menu_sel > menu_sel_max) menu_sel = menu_sel_max;\r
59633198 1308\r
1309 while (1)\r
720ee7f6 1310 {\r
1311 draw_menu_options(menu_sel);\r
1312 inp = wait_for_input(GP2X_UP|GP2X_DOWN|GP2X_LEFT|GP2X_RIGHT|GP2X_B|GP2X_X|GP2X_A);\r
59633198 1313 if (inp & GP2X_UP ) { menu_sel--; if (menu_sel < 0) menu_sel = menu_sel_max; }\r
1314 if (inp & GP2X_DOWN) { menu_sel++; if (menu_sel > menu_sel_max) menu_sel = 0; }\r
1315 selected_id = me_index2id(opt_entries, OPT_ENTRY_COUNT, menu_sel);\r
0ae25549 1316 if (inp & (GP2X_LEFT|GP2X_RIGHT)) { // multi choice\r
59633198 1317 if (!me_process(opt_entries, OPT_ENTRY_COUNT, selected_id, (inp&GP2X_RIGHT) ? 1 : 0)) {\r
1318 switch (selected_id) {\r
1319 case MA_OPT_RENDERER:\r
1320 if (inp & GP2X_LEFT) {\r
0ae25549 1321 if (PicoOpt&0x10) PicoOpt&= ~0x10;\r
59633198 1322 else if (!(currentConfig.EmuOpt &0x80))currentConfig.EmuOpt |= 0x80;\r
1323 else if ( currentConfig.EmuOpt &0x80) break;\r
1324 } else {\r
0ae25549 1325 if (PicoOpt&0x10) break;\r
1326 else if (!(currentConfig.EmuOpt &0x80))PicoOpt|= 0x10;\r
59633198 1327 else if ( currentConfig.EmuOpt &0x80) currentConfig.EmuOpt &= ~0x80;\r
1328 }\r
1329 break;\r
1330 case MA_OPT_SOUND_QUALITY:\r
0ae25549 1331 if ((inp & GP2X_RIGHT) && PsndRate == 44100 && !(PicoOpt&0x08)) {\r
1332 PsndRate = 8000; PicoOpt|= 0x08;\r
1333 } else if ((inp & GP2X_LEFT) && PsndRate == 8000 && (PicoOpt&0x08)) {\r
1334 PsndRate = 44100; PicoOpt&=~0x08;\r
1335 } else PsndRate = sndrate_prevnext(PsndRate, inp & GP2X_RIGHT);\r
59633198 1336 break;\r
1337 case MA_OPT_REGION:\r
1338 region_prevnext(inp & GP2X_RIGHT);\r
1339 break;\r
1340 case MA_OPT_CONFIRM_STATES: {\r
1341 int n = ((currentConfig.EmuOpt>>9)&1) | ((currentConfig.EmuOpt>>10)&2);\r
1342 n += (inp & GP2X_LEFT) ? -1 : 1;\r
1343 if (n < 0) n = 0; else if (n > 3) n = 3;\r
1344 n |= n << 1; n &= ~2;\r
1345 currentConfig.EmuOpt &= ~0xa00;\r
1346 currentConfig.EmuOpt |= n << 9;\r
1347 break;\r
1348 }\r
1349 case MA_OPT_SAVE_SLOT:\r
1350 if (inp & GP2X_RIGHT) {\r
1351 state_slot++; if (state_slot > 9) state_slot = 0;\r
1352 } else {state_slot--; if (state_slot < 0) state_slot = 9;\r
1353 }\r
1354 break;\r
1355 case MA_OPT_CPU_CLOCKS:\r
1356 while ((inp = gp2x_joystick_read(1)) & (GP2X_LEFT|GP2X_RIGHT)) {\r
1357 currentConfig.CPUclock += (inp & GP2X_LEFT) ? -1 : 1;\r
1358 if (currentConfig.CPUclock < 1) currentConfig.CPUclock = 1;\r
1359 draw_menu_options(menu_sel);\r
1360 usleep(50*1000);\r
1361 }\r
1362 break;\r
1363 case MA_OPT_SAVECFG:\r
1364 case MA_OPT_SAVECFG_GAME:\r
1365 case MA_OPT_LOADCFG:\r
1366 config_slot += (inp&GP2X_RIGHT) ? 1 : -1;\r
1367 if (config_slot > 9) config_slot = 0;\r
1368 if (config_slot < 0) config_slot = 9;\r
1369 me_enable(opt_entries, OPT_ENTRY_COUNT, MA_OPT_LOADCFG, config_slot != config_slot_current);\r
1370 menu_sel_max = me_count_enabled(opt_entries, OPT_ENTRY_COUNT) - 1;\r
1371 if (menu_sel > menu_sel_max) menu_sel = menu_sel_max;\r
1372 break;\r
1373 default:\r
1374 //printf("%s: something unknown selected (%i)\n", __FUNCTION__, selected_id);\r
1375 break;\r
1376 }\r
1377 }\r
1378 }\r
1379 if (inp & GP2X_B) {\r
1380 if (!me_process(opt_entries, OPT_ENTRY_COUNT, selected_id, 1))\r
1381 {\r
1382 switch (selected_id)\r
1383 {\r
1384 case MA_OPT_SCD_OPTS:\r
1385 cd_menu_loop_options();\r
1386 if (engineState == PGS_ReloadRom)\r
1387 return 0; // test BIOS\r
1388 break;\r
1389 case MA_OPT_ADV_OPTS:\r
1390 amenu_loop_options();\r
1391 break;\r
1392 case MA_OPT_SAVECFG: // done (update and write)\r
1393 menu_options_save();\r
1394 if (emu_WriteConfig(0)) strcpy(menuErrorMsg, "config saved");\r
1395 else strcpy(menuErrorMsg, "failed to write config");\r
1396 return 1;\r
1397 case MA_OPT_SAVECFG_GAME: // done (update and write for current game)\r
1398 menu_options_save();\r
1399 if (emu_WriteConfig(1)) strcpy(menuErrorMsg, "config saved");\r
1400 else strcpy(menuErrorMsg, "failed to write config");\r
1401 return 1;\r
1402 case MA_OPT_LOADCFG:\r
95151aea 1403 ret = emu_ReadConfig(1, 1);\r
1404 if (!ret) ret = emu_ReadConfig(0, 1);\r
1405 if (ret) strcpy(menuErrorMsg, "config loaded");\r
59633198 1406 else strcpy(menuErrorMsg, "failed to load config");\r
1407 return 1;\r
1408 default:\r
1409 //printf("%s: something unknown selected (%i)\n", __FUNCTION__, selected_id);\r
1410 break;\r
1411 }\r
720ee7f6 1412 }\r
1413 }\r
979ba09f 1414 if(inp & (GP2X_X|GP2X_A)) {\r
720ee7f6 1415 menu_options_save();\r
daf91588 1416 return 0; // done (update, no write)\r
720ee7f6 1417 }\r
720ee7f6 1418 }\r
1419}\r
1420\r
1421// -------------- credits --------------\r
1422\r
1423static void draw_menu_credits(void)\r
1424{\r
13059a60 1425 int tl_x = 15, tl_y = 64, y;\r
e5d315a5 1426 gp2x_pd_clone_buffer2();\r
720ee7f6 1427\r
30d5518a 1428 text_out16(tl_x, 20, "PicoDrive v" VERSION " (c) notaz, 2006-2008");\r
720ee7f6 1429 y = tl_y;\r
13059a60 1430 text_out16(tl_x, y, "Credits:");\r
4b8f4f3c 1431 text_out16(tl_x, (y+=10), "fDave: Cyclone 68000 core,");\r
13059a60 1432 text_out16(tl_x, (y+=10), " base code of PicoDrive");\r
1433 text_out16(tl_x, (y+=10), "Reesy & FluBBa: DrZ80 core");\r
1434 text_out16(tl_x, (y+=10), "MAME devs: YM2612 and SN76496 cores");\r
1435 text_out16(tl_x, (y+=10), "Charles MacDonald: Genesis hw docs");\r
1436 text_out16(tl_x, (y+=10), "Stephane Dallongeville:");\r
1437 text_out16(tl_x, (y+=10), " opensource Gens");\r
1438 text_out16(tl_x, (y+=10), "Haze: Genesis hw info");\r
1439 text_out16(tl_x, (y+=10), "rlyeh and others: minimal SDK");\r
1440 text_out16(tl_x, (y+=10), "Squidge: squidgehack");\r
1441 text_out16(tl_x, (y+=10), "Dzz: ARM940 sample");\r
1442 text_out16(tl_x, (y+=10), "GnoStiC / Puck2099: USB joystick");\r
1443 text_out16(tl_x, (y+=10), "craigix: GP2X hardware");\r
f9a37a02 1444 text_out16(tl_x, (y+=10), "ketchupgun: skin design");\r
13059a60 1445\r
1446 menu_flip();\r
720ee7f6 1447}\r
1448\r
1449\r
1450// -------------- root menu --------------\r
1451\r
59633198 1452menu_entry main_entries[] =\r
1453{\r
0ae25549 1454 { "Resume game", MB_NONE, MA_MAIN_RESUME_GAME, NULL, 0, 0, 0, 0, 0 },\r
1455 { "Save State", MB_NONE, MA_MAIN_SAVE_STATE, NULL, 0, 0, 0, 0, 0 },\r
1456 { "Load State", MB_NONE, MA_MAIN_LOAD_STATE, NULL, 0, 0, 0, 0, 0 },\r
1457 { "Reset game", MB_NONE, MA_MAIN_RESET_GAME, NULL, 0, 0, 0, 0, 0 },\r
1458 { "Load new ROM/ISO", MB_NONE, MA_MAIN_LOAD_ROM, NULL, 0, 0, 0, 1, 0 },\r
1459 { "Change options", MB_NONE, MA_MAIN_OPTIONS, NULL, 0, 0, 0, 1, 0 },\r
1460 { "Configure controls", MB_NONE, MA_MAIN_CONTROLS, NULL, 0, 0, 0, 1, 0 },\r
1461 { "Credits", MB_NONE, MA_MAIN_CREDITS, NULL, 0, 0, 0, 1, 0 },\r
1462 { "Patches / GameGenie",MB_NONE, MA_MAIN_PATCHES, NULL, 0, 0, 0, 0, 0 },\r
1463 { "Exit", MB_NONE, MA_MAIN_EXIT, NULL, 0, 0, 0, 1, 0 }\r
59633198 1464};\r
1465\r
1466#define MAIN_ENTRY_COUNT (sizeof(main_entries) / sizeof(main_entries[0]))\r
1467\r
720ee7f6 1468static void draw_menu_root(int menu_sel)\r
1469{\r
59633198 1470 const int tl_x = 70, tl_y = 70;\r
1471\r
e5d315a5 1472 gp2x_pd_clone_buffer2();\r
720ee7f6 1473\r
13059a60 1474 text_out16(tl_x, 20, "PicoDrive v" VERSION);\r
1475\r
c7a4ff64 1476 menu_draw_selection(tl_x - 16, tl_y + menu_sel*10, 146);\r
720ee7f6 1477\r
59633198 1478 me_draw(main_entries, MAIN_ENTRY_COUNT, tl_x, tl_y, NULL, NULL);\r
720ee7f6 1479\r
720ee7f6 1480 // error\r
13059a60 1481 if (menuErrorMsg[0]) {\r
1482 memset((char *)gp2x_screen + 320*224*2, 0, 320*16*2);\r
1483 text_out16(5, 226, menuErrorMsg);\r
1484 }\r
1485 menu_flip();\r
720ee7f6 1486}\r
1487\r
1488\r
1489static void menu_loop_root(void)\r
1490{\r
59633198 1491 static int menu_sel = 0;\r
1492 int ret, menu_sel_max;\r
720ee7f6 1493 unsigned long inp = 0;\r
720ee7f6 1494\r
144a28a0 1495 me_enable(main_entries, MAIN_ENTRY_COUNT, MA_MAIN_RESUME_GAME, rom_loaded);\r
1496 me_enable(main_entries, MAIN_ENTRY_COUNT, MA_MAIN_SAVE_STATE, rom_loaded);\r
1497 me_enable(main_entries, MAIN_ENTRY_COUNT, MA_MAIN_LOAD_STATE, rom_loaded);\r
1498 me_enable(main_entries, MAIN_ENTRY_COUNT, MA_MAIN_RESET_GAME, rom_loaded);\r
59633198 1499 me_enable(main_entries, MAIN_ENTRY_COUNT, MA_MAIN_PATCHES, PicoPatches != NULL);\r
1500\r
1501 menu_sel_max = me_count_enabled(main_entries, MAIN_ENTRY_COUNT) - 1;\r
95151aea 1502 if (menu_sel > menu_sel_max) menu_sel = menu_sel_max;\r
720ee7f6 1503\r
79cad122 1504 /* make sure action buttons are not pressed on entering menu */\r
1505 draw_menu_root(menu_sel);\r
1506 while (gp2x_joystick_read(1) & (GP2X_B|GP2X_X|GP2X_SELECT)) usleep(50*1000);\r
1507\r
1508 for (;;)\r
720ee7f6 1509 {\r
1510 draw_menu_root(menu_sel);\r
0ae6813e 1511 inp = wait_for_input(GP2X_UP|GP2X_DOWN|GP2X_B|GP2X_X|GP2X_SELECT|GP2X_L|GP2X_R);\r
59633198 1512 if(inp & GP2X_UP ) { menu_sel--; if (menu_sel < 0) menu_sel = menu_sel_max; }\r
1513 if(inp & GP2X_DOWN) { menu_sel++; if (menu_sel > menu_sel_max) menu_sel = 0; }\r
0ae6813e 1514 if((inp & (GP2X_L|GP2X_R)) == (GP2X_L|GP2X_R)) debug_menu_loop();\r
720ee7f6 1515 if(inp &(GP2X_SELECT|GP2X_X)){\r
144a28a0 1516 if (rom_loaded) {\r
720ee7f6 1517 while (gp2x_joystick_read(1) & (GP2X_SELECT|GP2X_X)) usleep(50*1000); // wait until select is released\r
1518 engineState = PGS_Running;\r
1519 break;\r
1520 }\r
1521 }\r
59633198 1522 if(inp & GP2X_B) {\r
1523 switch (me_index2id(main_entries, MAIN_ENTRY_COUNT, menu_sel))\r
1524 {\r
1525 case MA_MAIN_RESUME_GAME:\r
144a28a0 1526 if (rom_loaded) {\r
5e2e14f2 1527 while (gp2x_joystick_read(1) & GP2X_B) usleep(50*1000);\r
1528 engineState = PGS_Running;\r
1529 return;\r
1530 }\r
720ee7f6 1531 break;\r
59633198 1532 case MA_MAIN_SAVE_STATE:\r
144a28a0 1533 if (rom_loaded) {\r
4ffd2858 1534 if(savestate_menu_loop(0))\r
720ee7f6 1535 continue;\r
720ee7f6 1536 engineState = PGS_Running;\r
1537 return;\r
1538 }\r
1539 break;\r
59633198 1540 case MA_MAIN_LOAD_STATE:\r
144a28a0 1541 if (rom_loaded) {\r
4ffd2858 1542 if(savestate_menu_loop(1))\r
720ee7f6 1543 continue;\r
720ee7f6 1544 engineState = PGS_Running;\r
1545 return;\r
1546 }\r
1547 break;\r
59633198 1548 case MA_MAIN_RESET_GAME:\r
144a28a0 1549 if (rom_loaded) {\r
720ee7f6 1550 emu_ResetGame();\r
1551 engineState = PGS_Running;\r
1552 return;\r
1553 }\r
1554 break;\r
59633198 1555 case MA_MAIN_LOAD_ROM:\r
13059a60 1556 {\r
1557 char curr_path[PATH_MAX], *selfname;\r
1558 FILE *tstf;\r
367b6f1f 1559 if ( (tstf = fopen(lastRomFile, "rb")) )\r
13059a60 1560 {\r
1561 fclose(tstf);\r
367b6f1f 1562 strcpy(curr_path, lastRomFile);\r
13059a60 1563 }\r
1564 else\r
1565 getcwd(curr_path, PATH_MAX);\r
720ee7f6 1566 selfname = romsel_loop(curr_path);\r
1567 if (selfname) {\r
1568 printf("selected file: %s\n", selfname);\r
720ee7f6 1569 engineState = PGS_ReloadRom;\r
13059a60 1570 return;\r
720ee7f6 1571 }\r
13059a60 1572 break;\r
1573 }\r
59633198 1574 case MA_MAIN_OPTIONS:\r
daf91588 1575 ret = menu_loop_options();\r
1576 if (ret == 1) continue; // status update\r
1577 if (engineState == PGS_ReloadRom)\r
1578 return; // BIOS test\r
720ee7f6 1579 break;\r
59633198 1580 case MA_MAIN_CONTROLS:\r
720ee7f6 1581 kc_sel_loop();\r
1582 break;\r
59633198 1583 case MA_MAIN_CREDITS:\r
720ee7f6 1584 draw_menu_credits();\r
1585 usleep(500*1000);\r
1586 inp = wait_for_input(GP2X_B|GP2X_X);\r
1587 break;\r
59633198 1588 case MA_MAIN_EXIT:\r
720ee7f6 1589 engineState = PGS_Quit;\r
1590 return;\r
59633198 1591 case MA_MAIN_PATCHES:\r
144a28a0 1592 if (rom_loaded && PicoPatches) {\r
70d2ecc5 1593 patches_menu_loop();\r
1594 PicoPatchApply();\r
1595 strcpy(menuErrorMsg, "Patches applied");\r
1596 continue;\r
1597 }\r
1598 break;\r
59633198 1599 default:\r
1600 printf("%s: something unknown selected\n", __FUNCTION__);\r
1601 break;\r
720ee7f6 1602 }\r
1603 }\r
1604 menuErrorMsg[0] = 0; // clear error msg\r
1605 }\r
1606}\r
1607\r
f9a37a02 1608static void menu_darken_bg(void *dst, int pixels, int darker)\r
720ee7f6 1609{\r
13059a60 1610 unsigned int *screen = dst;\r
1611 pixels /= 2;\r
f9a37a02 1612 if (darker)\r
13059a60 1613 {\r
f9a37a02 1614 while (pixels--)\r
1615 {\r
1616 unsigned int p = *screen;\r
1617 *screen++ = ((p&0xf79ef79e)>>1) - ((p&0xc618c618)>>3);\r
1618 }\r
1619 }\r
1620 else\r
1621 {\r
1622 while (pixels--)\r
1623 {\r
1624 unsigned int p = *screen;\r
1625 *screen++ = (p&0xf79ef79e)>>1;\r
1626 }\r
13059a60 1627 }\r
1628}\r
e5d315a5 1629\r
13059a60 1630static void menu_prepare_bg(int use_game_bg)\r
1631{\r
1632 if (use_game_bg)\r
1633 {\r
1634 // darken the active framebuffer\r
1635 memset(gp2x_screen, 0, 320*8*2);\r
f9a37a02 1636 menu_darken_bg((char *)gp2x_screen + 320*8*2, 320*224, 1);\r
13059a60 1637 memset((char *)gp2x_screen + 320*232*2, 0, 320*8*2);\r
1638 }\r
1639 else\r
1640 {\r
1641 // should really only happen once, on startup..\r
1642 readpng(gp2x_screen, "skin/background.png", READPNG_BG);\r
4ffd2858 1643 }\r
720ee7f6 1644\r
13059a60 1645 // copy to buffer2\r
1646 gp2x_memcpy_buffers((1<<2), gp2x_screen, 0, 320*240*2);\r
4ffd2858 1647}\r
1648\r
1649static void menu_gfx_prepare(void)\r
1650{\r
144a28a0 1651 menu_prepare_bg(rom_loaded);\r
4ffd2858 1652\r
13059a60 1653 // switch to 16bpp\r
1654 gp2x_video_changemode2(16);\r
79cad122 1655 gp2x_video_RGB_setscaling(0, 320, 240);\r
13059a60 1656 menu_flip();\r
e5d315a5 1657}\r
1658\r
1659\r
1660void menu_loop(void)\r
1661{\r
1662 menu_gfx_prepare();\r
720ee7f6 1663\r
1664 menu_loop_root();\r
1665\r
1666 menuErrorMsg[0] = 0;\r
1667}\r
5e2e14f2 1668\r
1669\r
1670// --------- CD tray close menu ----------\r
1671\r
1672static void draw_menu_tray(int menu_sel)\r
1673{\r
1674 int tl_x = 70, tl_y = 90, y;\r
13059a60 1675 memset(gp2x_screen, 0, 320*240*2);\r
5e2e14f2 1676\r
13059a60 1677 text_out16(tl_x, 20, "The unit is about to");\r
1678 text_out16(tl_x, 30, "close the CD tray.");\r
5e2e14f2 1679\r
1680 y = tl_y;\r
13059a60 1681 text_out16(tl_x, y, "Load new CD image");\r
1682 text_out16(tl_x, (y+=10), "Insert nothing");\r
5e2e14f2 1683\r
1684 // draw cursor\r
13059a60 1685 text_out16(tl_x - 16, tl_y + menu_sel*10, ">");\r
5e2e14f2 1686 // error\r
13059a60 1687 if (menuErrorMsg[0]) text_out16(5, 226, menuErrorMsg);\r
1688 menu_flip();\r
5e2e14f2 1689}\r
1690\r
1691\r
1692int menu_loop_tray(void)\r
1693{\r
1694 int menu_sel = 0, menu_sel_max = 1;\r
1695 unsigned long inp = 0;\r
1696 char curr_path[PATH_MAX], *selfname;\r
1697 FILE *tstf;\r
1698\r
c7a4ff64 1699 gp2x_memset_all_buffers(0, 0, 320*240*2);\r
5e2e14f2 1700 menu_gfx_prepare();\r
1701\r
367b6f1f 1702 if ( (tstf = fopen(lastRomFile, "rb")) )\r
5e2e14f2 1703 {\r
1704 fclose(tstf);\r
367b6f1f 1705 strcpy(curr_path, lastRomFile);\r
5e2e14f2 1706 }\r
1707 else\r
1708 {\r
1709 getcwd(curr_path, PATH_MAX);\r
1710 }\r
1711\r
1712 /* make sure action buttons are not pressed on entering menu */\r
1713 draw_menu_tray(menu_sel);\r
1714 while (gp2x_joystick_read(1) & GP2X_B) usleep(50*1000);\r
1715\r
1716 for (;;)\r
1717 {\r
1718 draw_menu_tray(menu_sel);\r
1719 inp = wait_for_input(GP2X_UP|GP2X_DOWN|GP2X_B);\r
1720 if(inp & GP2X_UP ) { menu_sel--; if (menu_sel < 0) menu_sel = menu_sel_max; }\r
1721 if(inp & GP2X_DOWN) { menu_sel++; if (menu_sel > menu_sel_max) menu_sel = 0; }\r
1722 if(inp & GP2X_B ) {\r
1723 switch (menu_sel) {\r
1724 case 0: // select image\r
1725 selfname = romsel_loop(curr_path);\r
1726 if (selfname) {\r
1ceda417 1727 int ret = -1;\r
1728 cd_img_type cd_type;\r
f013066e 1729 cd_type = emu_cdCheck(NULL);\r
1ceda417 1730 if (cd_type != CIT_NOT_CD)\r
1731 ret = Insert_CD(romFileName, cd_type);\r
5e2e14f2 1732 if (ret != 0) {\r
1733 sprintf(menuErrorMsg, "Load failed, invalid CD image?");\r
1734 printf("%s\n", menuErrorMsg);\r
1735 continue;\r
1736 }\r
1737 engineState = PGS_RestartRun;\r
1738 return 1;\r
1739 }\r
1740 break;\r
1741 case 1: // insert nothing\r
1742 engineState = PGS_RestartRun;\r
1743 return 0;\r
1744 }\r
1745 }\r
1746 menuErrorMsg[0] = 0; // clear error msg\r
1747 }\r
1748}\r
1749\r
1750\r