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