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