lowercasing filenames, part1; makefile adjustments
[picodrive.git] / platform / gp2x / menu.c
CommitLineData
d524c827 1// (c) Copyright 2006,2007 notaz, All rights reserved.\r
cc68a136 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
ea8c405f 17#include "../common/emu.h"\r
e5f426aa 18#include "../common/menu.h"\r
ea8c405f 19#include "../common/arm_utils.h"\r
e5f426aa 20#include "../common/readpng.h"\r
cc68a136 21#include "version.h"\r
22\r
efcba75f 23#include <pico/pico_int.h>\r
24#include <pico/patch.h>\r
b67ef287 25#include <zlib/zlib.h>\r
cc68a136 26\r
27#ifndef _DIRENT_HAVE_D_TYPE\r
053fd9b4 28#error "need d_type for file browser"\r
cc68a136 29#endif\r
30\r
cc68a136 31extern int mmuhack_status;\r
cc68a136 32\r
f9d3ee9d 33const char * const keyNames[] = {\r
cc68a136 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
f579f7b8 40void menu_darken_bg(void *dst, int pixels, int darker);\r
a12e0116 41static void menu_prepare_bg(int use_game_bg);\r
cc68a136 42\r
cc68a136 43static unsigned long inp_prev = 0;\r
44static int inp_prevjoy = 0;\r
45\r
f579f7b8 46unsigned long wait_for_input(unsigned long interesting)\r
cc68a136 47{\r
48 unsigned long ret;\r
38576063 49 static int repeats = 0, wait = 20;\r
cc68a136 50 int release = 0, i;\r
51\r
385a8491 52 if (repeats == 2) wait = 3;\r
53 else if (repeats == 4) wait = 2;\r
54 else if (repeats == 6) wait = 1;\r
cc68a136 55\r
385a8491 56 for (i = 0; i < wait && inp_prev == gp2x_joystick_read(1); i++) {\r
721cd396 57 if (i == 0) repeats++;\r
385a8491 58 usleep(30000);\r
cc68a136 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
38576063 68 wait = 20;\r
cc68a136 69 }\r
053fd9b4 70 if (wait > 6 && (ret&(GP2X_UP|GP2X_LEFT|GP2X_DOWN|GP2X_RIGHT|GP2X_L|GP2X_R)))\r
38576063 71 wait = 6;\r
cc68a136 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
1ca2ea4f 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
a39b7867 129 // ... but allow select\r
130 ret |= inp_prev & GP2X_SELECT;\r
1ca2ea4f 131\r
cc68a136 132 return ret;\r
133}\r
134\r
f579f7b8 135void menu_flip(void)\r
e5f426aa 136{\r
137 gp2x_video_flush_cache();\r
138 gp2x_video_flip2();\r
139}\r
cc68a136 140\r
141\r
a9b3ffd3 142// --------- loading ROM screen ----------\r
143\r
ea08c296 144static int cdload_called = 0;\r
145\r
a9b3ffd3 146static void load_progress_cb(int percent)\r
147{\r
148 int ln, len = percent * 320 / 100;\r
a12e0116 149 unsigned short *dst = (unsigned short *)gp2x_screen + 320*20;\r
a9b3ffd3 150\r
151 if (len > 320) len = 320;\r
ea08c296 152 for (ln = 8; ln > 0; ln--, dst += 320)\r
a12e0116 153 memset(dst, 0xff, len*2);\r
154 menu_flip();\r
a9b3ffd3 155}\r
156\r
ea08c296 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
ca61ee42 166 dst += 320*30;\r
ea08c296 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
ca61ee42 171 menu_flip();\r
ea08c296 172 cdload_called = 1;\r
173}\r
174\r
a9b3ffd3 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
eacee137 180 if (rom_loaded) gp2x_pd_clone_buffer2();\r
a12e0116 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
a9b3ffd3 187 PicoCartLoadProgressCB = load_progress_cb;\r
ea08c296 188 PicoCDLoadProgressCB = cdload_progress_cb;\r
189 cdload_called = 0;\r
a9b3ffd3 190}\r
191\r
192void menu_romload_end(void)\r
193{\r
ca61ee42 194 PicoCartLoadProgressCB = PicoCDLoadProgressCB = NULL;\r
ea08c296 195 smalltext_out16(1, cdload_called ? 60 : 30, "Starting emulation...", 0xffff);\r
a12e0116 196 menu_flip();\r
a9b3ffd3 197}\r
198\r
cc68a136 199// -------------- ROM selector --------------\r
200\r
a12e0116 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
b923ecbe 205 static const char *rom_exts[] = { "zip", "bin", "smd", "gen", "iso", "cso", "cue" };\r
a12e0116 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
cc68a136 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
a4f0cc86 224 gp2x_pd_clone_buffer2();\r
225\r
eacee137 226 if (!rom_loaded) {\r
a4f0cc86 227 menu_darken_bg(gp2x_screen, 320*240, 0);\r
a12e0116 228 }\r
229\r
a4f0cc86 230 menu_darken_bg((char *)gp2x_screen + 320*120*2, 320*8, 0);\r
cc68a136 231\r
232 if(start - 2 >= 0)\r
a12e0116 233 smalltext_out16_lim(14, (start - 2)*10, curdir, 0xffff, 53-2);\r
cc68a136 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
a12e0116 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
cc68a136 241 } else {\r
a12e0116 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
cc68a136 244 }\r
245 }\r
a12e0116 246 text_out16(5, 120, ">");\r
247 menu_flip();\r
cc68a136 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
2433f409 259static char *filter_exts[] = {\r
260 ".mp3", ".MP3", ".srm", ".brm", "s.gz", ".mds", "bcfg", ".txt", ".htm", "html",\r
b923ecbe 261 ".jpg", ".gpe"\r
2433f409 262};\r
c008977e 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
cc68a136 281\r
1b13dae0 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
cc68a136 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
1b13dae0 315rescan:\r
cc68a136 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
c008977e 326 n = scandir(curr_path, &namelist, scandir_filter, scandir_cmp);\r
cc68a136 327 if (n < 0) {\r
328 // try root\r
c008977e 329 n = scandir("/", &namelist, scandir_filter, scandir_cmp);\r
cc68a136 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
1b13dae0 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
cc68a136 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
721cd396 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
1b13dae0 359 if ((inp & GP2X_B) || (inp & (GP2X_SELECT|GP2X_A)) == (GP2X_SELECT|GP2X_A)) // enter dir/select || delete\r
360 {\r
cc68a136 361 again:\r
1b13dae0 362 if (namelist[sel+1]->d_type == DT_REG)\r
363 {\r
cc68a136 364 strcpy(romFileName, curr_path);\r
365 strcat(romFileName, "/");\r
366 strcat(romFileName, namelist[sel+1]->d_name);\r
1b13dae0 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
cc68a136 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
1b13dae0 402 }\r
403 else\r
404 {\r
cc68a136 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
1b13dae0 425 while (n--) free(namelist[n]);\r
cc68a136 426 free(namelist);\r
427 }\r
428\r
429 return ret;\r
430}\r
431\r
b67ef287 432// ------------ patch/gg menu ------------\r
433\r
434static void draw_patchlist(int sel)\r
435{\r
a12e0116 436 int start, i, pos, active;\r
b67ef287 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
a12e0116 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
b67ef287 449 }\r
450 pos = start + i;\r
a12e0116 451 if (pos < 24) smalltext_out16_lim(14, pos*10, "done", 0xffff, 4);\r
b67ef287 452\r
a12e0116 453 text_out16(5, 120, ">");\r
454 menu_flip();\r
b67ef287 455}\r
456\r
457\r
0af33fe0 458static void patches_menu_loop(void)\r
b67ef287 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
860c6322 481// ------------ savestate loader ------------\r
482\r
860c6322 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
ea8c405f 493 if (emu_checkSaveFile(slot))\r
860c6322 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
ea8c405f 521 emu_setSaveStateCbs(1);\r
860c6322 522 } else {\r
523 file = fopen(fname, "rb");\r
ea8c405f 524 emu_setSaveStateCbs(0);\r
860c6322 525 }\r
526\r
527 if (file) {\r
602133e1 528 if (PicoAHW & PAHW_MCD) {\r
860c6322 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
0fc0e241 542 emu_forcedFrame(POPT_EN_SOFTSCALE);\r
a12e0116 543 menu_prepare_bg(1);\r
860c6322 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
a12e0116 560 text_out16(tl_x, 30, is_loading ? "Load state" : "Save state");\r
561\r
e5f426aa 562 menu_draw_selection(tl_x - 16, tl_y + menu_sel*10, 108);\r
860c6322 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
a12e0116 568 text_out16(tl_x, y, "SLOT %i (%s)", i, (state_slot_flags & (1 << i)) ? "USED" : "free");\r
860c6322 569 }\r
a12e0116 570 text_out16(tl_x, y, "back");\r
860c6322 571\r
a12e0116 572 menu_flip();\r
860c6322 573}\r
574\r
575static int savestate_menu_loop(int is_loading)\r
576{\r
aae35e84 577 static int menu_sel = 10;\r
578 int menu_sel_max = 10;\r
860c6322 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
cc68a136 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
d524c827 627static char *action_binds(int player_idx, int action_mask)\r
cc68a136 628{\r
d524c827 629 static char strkeys[32*5];\r
cc68a136 630 int joy, i;\r
631\r
632 strkeys[0] = 0;\r
d524c827 633 for (i = 0; i < 32; i++) // i is key index\r
cc68a136 634 {\r
d524c827 635 if (currentConfig.KeyBinds[i] & action_mask)\r
cc68a136 636 {\r
d524c827 637 if (player_idx >= 0 && ((currentConfig.KeyBinds[i] >> 16) & 3) != player_idx) continue;\r
1ca2ea4f 638 if (strkeys[0]) { strcat(strkeys, " + "); strcat(strkeys, keyNames[i]); break; }\r
639 else strcpy(strkeys, keyNames[i]);\r
cc68a136 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
d524c827 646 if (currentConfig.JoyBinds[joy][i] & action_mask)\r
cc68a136 647 {\r
d524c827 648 if (player_idx >= 0 && ((currentConfig.JoyBinds[joy][i] >> 16) & 3) != player_idx) continue;\r
cc68a136 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
45499284 658 // limit..\r
659 strkeys[20] = 0;\r
660\r
d524c827 661 return strkeys;\r
662}\r
663\r
45499284 664static void unbind_action(int action, int pl_idx, int joy)\r
d524c827 665{\r
666 int i, u;\r
667\r
45499284 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
b4fe3a4a 674 }\r
45499284 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
b4fe3a4a 685 for (i = 0; i < 32; i++) {\r
45499284 686 if (pl_idx >= 0 && (currentConfig.JoyBinds[joy-1][i]&0x30000) != (pl_idx<<16)) continue;\r
687 currentConfig.JoyBinds[joy-1][i] &= ~action;\r
b4fe3a4a 688 }\r
45499284 689 }\r
d524c827 690}\r
691\r
b4fe3a4a 692static int count_bound_keys(int action, int pl_idx, int joy)\r
d524c827 693{\r
694 int i, keys = 0;\r
695\r
696 if (joy)\r
697 {\r
b4fe3a4a 698 for (i = 0; i < 32; i++) {\r
699 if (pl_idx >= 0 && (currentConfig.JoyBinds[joy-1][i]&0x30000) != (pl_idx<<16)) continue;\r
d524c827 700 if (currentConfig.JoyBinds[joy-1][i] & action) keys++;\r
b4fe3a4a 701 }\r
d524c827 702 }\r
703 else\r
704 {\r
b4fe3a4a 705 for (i = 0; i < 32; i++) {\r
706 if (pl_idx >= 0 && (currentConfig.KeyBinds[i]&0x30000) != (pl_idx<<16)) continue;\r
d524c827 707 if (currentConfig.KeyBinds[i] & action) keys++;\r
b4fe3a4a 708 }\r
d524c827 709 }\r
710 return keys;\r
711}\r
712\r
1ca2ea4f 713static void draw_key_config(const me_bind_action *opts, int opt_cnt, int player_idx, int sel)\r
d524c827 714{\r
f0f0d2df 715 int x, y, tl_y = 30, i;\r
d524c827 716\r
e11c5548 717 gp2x_pd_clone_buffer2();\r
d524c827 718 if (player_idx >= 0) {\r
f0f0d2df 719 text_out16(80, 10, "Player %i controls", player_idx + 1);\r
b4fe3a4a 720 x = 80;\r
d524c827 721 } else {\r
f0f0d2df 722 text_out16(80, 10, "Emulator controls");\r
d524c827 723 x = 40;\r
724 }\r
725\r
406c96c5 726 menu_draw_selection(x - 16, tl_y + sel*10, (player_idx >= 0) ? 66 : 140);\r
a12e0116 727\r
d524c827 728 y = tl_y;\r
729 for (i = 0; i < opt_cnt; i++, y+=10)\r
a12e0116 730 text_out16(x, y, "%s : %s", opts[i].name, action_binds(player_idx, opts[i].mask));\r
d524c827 731\r
a12e0116 732 text_out16(x, y, "Done");\r
d524c827 733\r
734 if (sel < opt_cnt) {\r
f0f0d2df 735 text_out16(30, 195, "Press a button to bind/unbind");\r
736 text_out16(30, 205, "Use SELECT to clear");\r
737 text_out16(30, 215, "To bind UP/DOWN, hold SELECT");\r
738 text_out16(30, 225, "Select \"Done\" to exit");\r
d524c827 739 } else {\r
f0f0d2df 740 text_out16(30, 205, "Use Options -> Save cfg");\r
741 text_out16(30, 215, "to save controls");\r
742 text_out16(30, 225, "Press B or X to exit");\r
d524c827 743 }\r
a12e0116 744 menu_flip();\r
cc68a136 745}\r
746\r
1ca2ea4f 747static void key_config_loop(const me_bind_action *opts, int opt_cnt, int player_idx)\r
cc68a136 748{\r
d524c827 749 int joy = 0, sel = 0, menu_sel_max = opt_cnt, prev_select = 0, i;\r
cc68a136 750 unsigned long inp = 0;\r
751\r
752 for (;;)\r
753 {\r
d524c827 754 draw_key_config(opts, opt_cnt, player_idx, sel);\r
cc68a136 755 inp = wait_for_input_usbjoy(CONFIGURABLE_KEYS, &joy);\r
756 // printf("got %08lX from joy %i\n", inp, joy);\r
a39b7867 757 if (joy == 0)\r
758 {\r
d524c827 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
cc68a136 763 }\r
d524c827 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
45499284 770 unbind_action(opts[sel].mask, player_idx, -1);\r
d524c827 771 prev_select = inp & GP2X_SELECT;\r
cc68a136 772 inp &= CONFIGURABLE_KEYS;\r
773 inp &= ~GP2X_SELECT;\r
cc68a136 774 for (i = 0; i < 32; i++)\r
775 if (inp & (1 << i)) {\r
b4fe3a4a 776 if (count_bound_keys(opts[sel].mask, player_idx, 0) >= 2)\r
d524c827 777 currentConfig.KeyBinds[i] &= ~opts[sel].mask; // allow to unbind only\r
778 else currentConfig.KeyBinds[i] ^= opts[sel].mask;\r
c9077ab4 779 if (player_idx >= 0 && (currentConfig.KeyBinds[i] & opts[sel].mask)) {\r
d524c827 780 currentConfig.KeyBinds[i] &= ~(3 << 16);\r
781 currentConfig.KeyBinds[i] |= player_idx << 16;\r
782 }\r
cc68a136 783 }\r
d524c827 784 }\r
785 else if (sel < opt_cnt)\r
786 {\r
cc68a136 787 for (i = 0; i < 32; i++)\r
788 if (inp & (1 << i)) {\r
45499284 789 int *bind = &currentConfig.JoyBinds[joy-1][i];\r
790 if ((*bind & opts[sel].mask) && (player_idx < 0 || player_idx == ((*bind>>16)&3)))\r
1ca2ea4f 791 *bind &= ~opts[sel].mask;\r
45499284 792 else {\r
793 // override\r
794 unbind_action(opts[sel].mask, player_idx, joy);\r
795 *bind = opts[sel].mask;\r
1ca2ea4f 796 if (player_idx > 0) *bind |= player_idx << 16;\r
d524c827 797 }\r
cc68a136 798 }\r
799 }\r
800 }\r
801}\r
802\r
f0f0d2df 803\r
804menu_entry ctrlopt_entries[] =\r
805{\r
806 { "Player 1", MB_NONE, MA_CTRL_PLAYER1, NULL, 0, 0, 0, 1, 0 },\r
807 { "Player 2", MB_NONE, MA_CTRL_PLAYER2, NULL, 0, 0, 0, 1, 0 },\r
808 { "Emulator controls", MB_NONE, MA_CTRL_EMU, NULL, 0, 0, 0, 1, 0 },\r
809 { "6 button pad", MB_ONOFF, MA_OPT_6BUTTON_PAD, &PicoOpt, 0x020, 0, 0, 1, 1 },\r
810 { "Turbo rate", MB_RANGE, MA_CTRL_TURBO_RATE, &currentConfig.turbo_rate, 0, 1, 30, 1, 1 },\r
811 { "Done", MB_NONE, MA_CTRL_DONE, NULL, 0, 0, 0, 1, 0 },\r
812};\r
813\r
814#define CTRLOPT_ENTRY_COUNT (sizeof(ctrlopt_entries) / sizeof(ctrlopt_entries[0]))\r
815const int ctrlopt_entry_count = CTRLOPT_ENTRY_COUNT;\r
816\r
817\r
cc68a136 818static void draw_kc_sel(int menu_sel)\r
819{\r
820 int tl_x = 25+40, tl_y = 60, y, i;\r
821 char joyname[36];\r
822\r
823 y = tl_y;\r
e11c5548 824 gp2x_pd_clone_buffer2();\r
e5f426aa 825 menu_draw_selection(tl_x - 16, tl_y + menu_sel*10, 138);\r
cc68a136 826\r
f0f0d2df 827 me_draw(ctrlopt_entries, ctrlopt_entry_count, tl_x, tl_y, NULL, NULL);\r
cc68a136 828\r
829 tl_x = 25;\r
f0f0d2df 830 text_out16(tl_x, (y=130), "USB joys detected:");\r
cc68a136 831 if (num_of_joys > 0) {\r
832 for (i = 0; i < num_of_joys; i++) {\r
833 strncpy(joyname, joy_name(joys[i]), 33); joyname[33] = 0;\r
a12e0116 834 text_out16(tl_x, (y+=10), "%i: %s", i+1, joyname);\r
cc68a136 835 }\r
836 } else {\r
a12e0116 837 text_out16(tl_x, (y+=10), "none");\r
cc68a136 838 }\r
839\r
a12e0116 840 menu_flip();\r
cc68a136 841}\r
842\r
d524c827 843\r
406c96c5 844// player2_flag, reserved, ?, ?,\r
845// ?, ?, fast forward, menu\r
d524c827 846// "NEXT SAVE SLOT", "PREV SAVE SLOT", "SWITCH RENDERER", "SAVE STATE",\r
847// "LOAD STATE", "VOLUME UP", "VOLUME DOWN", "DONE"\r
1ca2ea4f 848me_bind_action emuctrl_actions[] =\r
d524c827 849{\r
406c96c5 850 { "Load State ", 1<<28 },\r
851 { "Save State ", 1<<27 },\r
852 { "Prev Save Slot ", 1<<25 },\r
853 { "Next Save Slot ", 1<<24 },\r
854 { "Switch Renderer ", 1<<26 },\r
855 { "Volume Down ", 1<<30 },\r
856 { "Volume Up ", 1<<29 },\r
857 { "Fast forward ", 1<<22 },\r
858 { "Enter Menu ", 1<<23 },\r
859 { "Pico Next page ", 1<<21 },\r
860 { "Pico Prev page ", 1<<20 },\r
861 { "Pico Switch input", 1<<19 },\r
862 { NULL, 0 }\r
d524c827 863};\r
864\r
cc68a136 865static void kc_sel_loop(void)\r
866{\r
f0f0d2df 867 int menu_sel = 5, menu_sel_max = 5;\r
cc68a136 868 unsigned long inp = 0;\r
f0f0d2df 869 menu_id selected_id;\r
cc68a136 870\r
d524c827 871 while (1)\r
cc68a136 872 {\r
873 draw_kc_sel(menu_sel);\r
f0f0d2df 874 inp = wait_for_input(GP2X_UP|GP2X_DOWN|GP2X_RIGHT|GP2X_LEFT|GP2X_B|GP2X_X);\r
875 selected_id = me_index2id(ctrlopt_entries, CTRLOPT_ENTRY_COUNT, menu_sel);\r
876 if (inp & (GP2X_LEFT|GP2X_RIGHT)) // multi choise\r
877 me_process(ctrlopt_entries, CTRLOPT_ENTRY_COUNT, selected_id, (inp&GP2X_RIGHT) ? 1 : 0);\r
d524c827 878 if (inp & GP2X_UP ) { menu_sel--; if (menu_sel < 0) menu_sel = menu_sel_max; }\r
879 if (inp & GP2X_DOWN) { menu_sel++; if (menu_sel > menu_sel_max) menu_sel = 0; }\r
880 if (inp & GP2X_B) {\r
f0f0d2df 881 int is_6button = PicoOpt & POPT_6BTN_PAD;\r
882 switch (selected_id) {\r
883 case MA_CTRL_PLAYER1: key_config_loop(me_ctrl_actions, is_6button ? 15 : 11, 0); return;\r
884 case MA_CTRL_PLAYER2: key_config_loop(me_ctrl_actions, is_6button ? 15 : 11, 1); return;\r
885 case MA_CTRL_EMU: key_config_loop(emuctrl_actions,\r
886 sizeof(emuctrl_actions)/sizeof(emuctrl_actions[0]) - 1, -1); return;\r
887 case MA_CTRL_DONE: if (!rom_loaded) emu_WriteConfig(0); return;\r
cc68a136 888 default: return;\r
889 }\r
890 }\r
d524c827 891 if (inp & GP2X_X) return;\r
cc68a136 892 }\r
893}\r
894\r
895\r
bf098bc5 896// --------- sega/mega cd options ----------\r
897\r
4e8a534c 898menu_entry cdopt_entries[] =\r
bf098bc5 899{\r
58c86d00 900 { NULL, MB_NONE, MA_CDOPT_TESTBIOS_USA, NULL, 0, 0, 0, 1, 0 },\r
901 { NULL, MB_NONE, MA_CDOPT_TESTBIOS_EUR, NULL, 0, 0, 0, 1, 0 },\r
902 { NULL, MB_NONE, MA_CDOPT_TESTBIOS_JAP, NULL, 0, 0, 0, 1, 0 },\r
903 { "CD LEDs", MB_ONOFF, MA_CDOPT_LEDS, &currentConfig.EmuOpt, 0x0400, 0, 0, 1, 1 },\r
ca482e5d 904 { "CDDA audio", MB_ONOFF, MA_CDOPT_CDDA, &PicoOpt, 0x0800, 0, 0, 1, 1 },\r
58c86d00 905 { "PCM audio", MB_ONOFF, MA_CDOPT_PCM, &PicoOpt, 0x0400, 0, 0, 1, 1 },\r
906 { NULL, MB_NONE, MA_CDOPT_READAHEAD, NULL, 0, 0, 0, 1, 1 },\r
907 { "SaveRAM cart", MB_ONOFF, MA_CDOPT_SAVERAM, &PicoOpt, 0x8000, 0, 0, 1, 1 },\r
908 { "Scale/Rot. fx (slow)", MB_ONOFF, MA_CDOPT_SCALEROT_CHIP,&PicoOpt, 0x1000, 0, 0, 1, 1 },\r
909 { "Better sync (slow)", MB_ONOFF, MA_CDOPT_BETTER_SYNC, &PicoOpt, 0x2000, 0, 0, 1, 1 },\r
910 { "done", MB_NONE, MA_CDOPT_DONE, NULL, 0, 0, 0, 1, 0 },\r
4e8a534c 911};\r
912\r
913#define CDOPT_ENTRY_COUNT (sizeof(cdopt_entries) / sizeof(cdopt_entries[0]))\r
f9d3ee9d 914const int cdopt_entry_count = CDOPT_ENTRY_COUNT;\r
4e8a534c 915\r
916\r
917struct bios_names_t\r
918{\r
919 char us[32], eu[32], jp[32];\r
920};\r
921\r
922static void menu_cdopt_cust_draw(const menu_entry *entry, int x, int y, void *param)\r
923{\r
924 struct bios_names_t *bios_names = param;\r
925 char ra_buff[16];\r
926\r
927 switch (entry->id)\r
928 {\r
a12e0116 929 case MA_CDOPT_TESTBIOS_USA: text_out16(x, y, "USA BIOS: %s", bios_names->us); break;\r
930 case MA_CDOPT_TESTBIOS_EUR: text_out16(x, y, "EUR BIOS: %s", bios_names->eu); break;\r
931 case MA_CDOPT_TESTBIOS_JAP: text_out16(x, y, "JAP BIOS: %s", bios_names->jp); break;\r
4e8a534c 932 case MA_CDOPT_READAHEAD:\r
933 if (PicoCDBuffers > 1) sprintf(ra_buff, "%5iK", PicoCDBuffers * 2);\r
934 else strcpy(ra_buff, " OFF");\r
a12e0116 935 text_out16(x, y, "ReadAhead buffer %s", ra_buff);\r
4e8a534c 936 break;\r
937 default:break;\r
938 }\r
939}\r
940\r
941static void draw_cd_menu_options(int menu_sel, struct bios_names_t *bios_names)\r
942{\r
943 int tl_x = 25, tl_y = 60;\r
944 menu_id selected_id;\r
0a051f55 945 char ra_buff[16];\r
946\r
947 if (PicoCDBuffers > 1) sprintf(ra_buff, "%5iK", PicoCDBuffers * 2);\r
948 else strcpy(ra_buff, " OFF");\r
bf098bc5 949\r
e11c5548 950 gp2x_pd_clone_buffer2();\r
951\r
e5f426aa 952 menu_draw_selection(tl_x - 16, tl_y + menu_sel*10, 246);\r
bf098bc5 953\r
a12e0116 954 me_draw(cdopt_entries, CDOPT_ENTRY_COUNT, tl_x, tl_y, menu_cdopt_cust_draw, bios_names);\r
bf098bc5 955\r
4e8a534c 956 selected_id = me_index2id(cdopt_entries, CDOPT_ENTRY_COUNT, menu_sel);\r
957 if ((selected_id == MA_CDOPT_TESTBIOS_USA && strcmp(bios_names->us, "NOT FOUND")) ||\r
958 (selected_id == MA_CDOPT_TESTBIOS_EUR && strcmp(bios_names->eu, "NOT FOUND")) ||\r
959 (selected_id == MA_CDOPT_TESTBIOS_JAP && strcmp(bios_names->jp, "NOT FOUND")))\r
a12e0116 960 text_out16(tl_x, 210, "Press start to test selected BIOS");\r
bf098bc5 961\r
a12e0116 962 menu_flip();\r
bf098bc5 963}\r
964\r
965static void cd_menu_loop_options(void)\r
966{\r
aae35e84 967 static int menu_sel = 0;\r
968 int menu_sel_max = 10;\r
bf098bc5 969 unsigned long inp = 0;\r
4e8a534c 970 struct bios_names_t bios_names;\r
971 menu_id selected_id;\r
972 char *bios, *p;\r
bf098bc5 973\r
ea8c405f 974 if (emu_findBios(4, &bios)) { // US\r
bf098bc5 975 for (p = bios+strlen(bios)-1; p > bios && *p != '/'; p--); p++;\r
4e8a534c 976 strncpy(bios_names.us, p, sizeof(bios_names.us)); bios_names.us[sizeof(bios_names.us)-1] = 0;\r
977 } else strcpy(bios_names.us, "NOT FOUND");\r
bf098bc5 978\r
ea8c405f 979 if (emu_findBios(8, &bios)) { // EU\r
bf098bc5 980 for (p = bios+strlen(bios)-1; p > bios && *p != '/'; p--); p++;\r
4e8a534c 981 strncpy(bios_names.eu, p, sizeof(bios_names.eu)); bios_names.eu[sizeof(bios_names.eu)-1] = 0;\r
982 } else strcpy(bios_names.eu, "NOT FOUND");\r
bf098bc5 983\r
ea8c405f 984 if (emu_findBios(1, &bios)) { // JP\r
bf098bc5 985 for (p = bios+strlen(bios)-1; p > bios && *p != '/'; p--); p++;\r
4e8a534c 986 strncpy(bios_names.jp, p, sizeof(bios_names.jp)); bios_names.jp[sizeof(bios_names.jp)-1] = 0;\r
987 } else strcpy(bios_names.jp, "NOT FOUND");\r
bf098bc5 988\r
989 for(;;)\r
990 {\r
4e8a534c 991 draw_cd_menu_options(menu_sel, &bios_names);\r
bf098bc5 992 inp = wait_for_input(GP2X_UP|GP2X_DOWN|GP2X_LEFT|GP2X_RIGHT|GP2X_B|GP2X_X|GP2X_A|GP2X_START);\r
4e8a534c 993 if (inp & GP2X_UP ) { menu_sel--; if (menu_sel < 0) menu_sel = menu_sel_max; }\r
994 if (inp & GP2X_DOWN) { menu_sel++; if (menu_sel > menu_sel_max) menu_sel = 0; }\r
995 selected_id = me_index2id(cdopt_entries, CDOPT_ENTRY_COUNT, menu_sel);\r
996 if (inp & (GP2X_LEFT|GP2X_RIGHT)) { // multi choise\r
997 if (!me_process(cdopt_entries, CDOPT_ENTRY_COUNT, selected_id, (inp&GP2X_RIGHT) ? 1 : 0) &&\r
998 selected_id == MA_CDOPT_READAHEAD) {\r
999 if (inp & GP2X_LEFT) {\r
1000 PicoCDBuffers >>= 1;\r
2d2247c2 1001 if (PicoCDBuffers < 2) PicoCDBuffers = 0;\r
4e8a534c 1002 } else {\r
2d2247c2 1003 if (PicoCDBuffers < 2) PicoCDBuffers = 2;\r
4e8a534c 1004 else PicoCDBuffers <<= 1;\r
1005 if (PicoCDBuffers > 8*1024) PicoCDBuffers = 8*1024; // 16M\r
1006 }\r
bf098bc5 1007 }\r
1008 }\r
4e8a534c 1009 if (inp & GP2X_B) { // toggleable options\r
1010 if (!me_process(cdopt_entries, CDOPT_ENTRY_COUNT, selected_id, 1) &&\r
1011 selected_id == MA_CDOPT_DONE) {\r
1012 return;\r
1013 }\r
1014 }\r
1015 if (inp & GP2X_START) { // BIOS testers\r
1016 switch (selected_id) {\r
1017 case MA_CDOPT_TESTBIOS_USA:\r
ea8c405f 1018 if (emu_findBios(4, &bios)) { // test US\r
bf098bc5 1019 strcpy(romFileName, bios);\r
1020 engineState = PGS_ReloadRom;\r
1021 return;\r
1022 }\r
1023 break;\r
4e8a534c 1024 case MA_CDOPT_TESTBIOS_EUR:\r
ea8c405f 1025 if (emu_findBios(8, &bios)) { // test EU\r
bf098bc5 1026 strcpy(romFileName, bios);\r
1027 engineState = PGS_ReloadRom;\r
1028 return;\r
1029 }\r
1030 break;\r
4e8a534c 1031 case MA_CDOPT_TESTBIOS_JAP:\r
ea8c405f 1032 if (emu_findBios(1, &bios)) { // test JP\r
bf098bc5 1033 strcpy(romFileName, bios);\r
1034 engineState = PGS_ReloadRom;\r
1035 return;\r
1036 }\r
1037 break;\r
4e8a534c 1038 default:\r
1039 break;\r
bf098bc5 1040 }\r
1041 }\r
4e8a534c 1042 if (inp & (GP2X_X|GP2X_A)) return;\r
bf098bc5 1043 }\r
1044}\r
1045\r
1046\r
1047// --------- advanced options ----------\r
1048\r
4e8a534c 1049menu_entry opt2_entries[] =\r
1050{\r
58c86d00 1051 { NULL, MB_NONE, MA_OPT2_GAMMA, NULL, 0, 0, 0, 1, 1 },\r
1052 { "A_SN's gamma curve", MB_ONOFF, MA_OPT2_A_SN_GAMMA, &currentConfig.EmuOpt, 0x1000, 0, 0, 1, 1 },\r
1053 { "Perfect vsync", MB_ONOFF, MA_OPT2_VSYNC, &currentConfig.EmuOpt, 0x2000, 0, 0, 1, 1 },\r
f9d3ee9d 1054 { "Disable sprite limit", MB_ONOFF, MA_OPT2_NO_SPRITE_LIM, &PicoOpt, 0x40000, 0, 0, 1, 1 },\r
1ca2ea4f 1055 { "Emulate Z80", MB_ONOFF, MA_OPT2_ENABLE_Z80, &PicoOpt, 0x00004, 0, 0, 1, 1 },\r
1056 { "Emulate YM2612 (FM)", MB_ONOFF, MA_OPT2_ENABLE_YM2612, &PicoOpt, 0x00001, 0, 0, 1, 1 },\r
1057 { "Emulate SN76496 (PSG)", MB_ONOFF, MA_OPT2_ENABLE_SN76496,&PicoOpt, 0x00002, 0, 0, 1, 1 },\r
58c86d00 1058 { "gzip savestates", MB_ONOFF, MA_OPT2_GZIP_STATES, &currentConfig.EmuOpt, 0x0008, 0, 0, 1, 1 },\r
1059 { "Don't save last used ROM", MB_ONOFF, MA_OPT2_NO_LAST_ROM, &currentConfig.EmuOpt, 0x0020, 0, 0, 1, 1 },\r
1060 { "needs restart:", MB_NONE, MA_NONE, NULL, 0, 0, 0, 1, 0 },\r
1061 { "craigix's RAM timings", MB_ONOFF, MA_OPT2_RAMTIMINGS, &currentConfig.EmuOpt, 0x0100, 0, 0, 1, 1 },\r
1062 { NULL, MB_ONOFF, MA_OPT2_SQUIDGEHACK, &currentConfig.EmuOpt, 0x0010, 0, 0, 1, 1 },\r
1ca2ea4f 1063 { "SVP dynarec", MB_ONOFF, MA_OPT2_SVP_DYNAREC, &PicoOpt, 0x20000, 0, 0, 1, 1 },\r
053fd9b4 1064 { "Disable idle loop patching",MB_ONOFF, MA_OPT2_NO_IDLE_LOOPS, &PicoOpt, 0x80000, 0, 0, 1, 1 },\r
58c86d00 1065 { "done", MB_NONE, MA_OPT2_DONE, NULL, 0, 0, 0, 1, 0 },\r
4e8a534c 1066};\r
1067\r
1068#define OPT2_ENTRY_COUNT (sizeof(opt2_entries) / sizeof(opt2_entries[0]))\r
f9d3ee9d 1069const int opt2_entry_count = OPT2_ENTRY_COUNT;\r
4e8a534c 1070\r
1071static void menu_opt2_cust_draw(const menu_entry *entry, int x, int y, void *param)\r
1072{\r
1073 if (entry->id == MA_OPT2_GAMMA)\r
a12e0116 1074 text_out16(x, y, "Gamma correction %i.%02i", currentConfig.gamma / 100, currentConfig.gamma%100);\r
4e8a534c 1075 else if (entry->id == MA_OPT2_SQUIDGEHACK)\r
58c86d00 1076 text_out16(x, y, "Squidgehack (now %s %s", mmuhack_status ? "active) " : "inactive)",\r
4e8a534c 1077 (currentConfig.EmuOpt&0x0010)?"ON":"OFF");\r
1078}\r
1079\r
1080\r
cc68a136 1081static void draw_amenu_options(int menu_sel)\r
1082{\r
4e8a534c 1083 int tl_x = 25, tl_y = 50;\r
cc68a136 1084\r
e11c5548 1085 gp2x_pd_clone_buffer2();\r
1086\r
e5f426aa 1087 menu_draw_selection(tl_x - 16, tl_y + menu_sel*10, 252);\r
cc68a136 1088\r
a12e0116 1089 me_draw(opt2_entries, OPT2_ENTRY_COUNT, tl_x, tl_y, menu_opt2_cust_draw, NULL);\r
cc68a136 1090\r
a12e0116 1091 menu_flip();\r
cc68a136 1092}\r
1093\r
1094static void amenu_loop_options(void)\r
1095{\r
aae35e84 1096 static int menu_sel = 0;\r
4e8a534c 1097 int menu_sel_max;\r
cc68a136 1098 unsigned long inp = 0;\r
4e8a534c 1099 menu_id selected_id;\r
1100\r
1101 menu_sel_max = me_count_enabled(opt2_entries, OPT2_ENTRY_COUNT) - 1;\r
cc68a136 1102\r
1103 for(;;)\r
1104 {\r
1105 draw_amenu_options(menu_sel);\r
1106 inp = wait_for_input(GP2X_UP|GP2X_DOWN|GP2X_LEFT|GP2X_RIGHT|GP2X_B|GP2X_X|GP2X_A);\r
4e8a534c 1107 if (inp & GP2X_UP ) { menu_sel--; if (menu_sel < 0) menu_sel = menu_sel_max; }\r
1108 if (inp & GP2X_DOWN) { menu_sel++; if (menu_sel > menu_sel_max) menu_sel = 0; }\r
1109 selected_id = me_index2id(opt2_entries, OPT2_ENTRY_COUNT, menu_sel);\r
1110 if (inp & (GP2X_LEFT|GP2X_RIGHT)) { // multi choise\r
1111 if (!me_process(opt2_entries, OPT2_ENTRY_COUNT, selected_id, (inp&GP2X_RIGHT) ? 1 : 0) &&\r
1112 selected_id == MA_OPT2_GAMMA) {\r
1113 while ((inp = gp2x_joystick_read(1)) & (GP2X_LEFT|GP2X_RIGHT)) {\r
1114 currentConfig.gamma += (inp & GP2X_LEFT) ? -1 : 1;\r
1115 if (currentConfig.gamma < 1) currentConfig.gamma = 1;\r
1116 if (currentConfig.gamma > 300) currentConfig.gamma = 300;\r
1117 draw_amenu_options(menu_sel);\r
1118 usleep(18*1000);\r
1119 }\r
cc68a136 1120 }\r
1121 }\r
4e8a534c 1122 if (inp & GP2X_B) { // toggleable options\r
1123 if (!me_process(opt2_entries, OPT2_ENTRY_COUNT, selected_id, 1) &&\r
1124 selected_id == MA_OPT2_DONE) {\r
1125 return;\r
cc68a136 1126 }\r
1127 }\r
4e8a534c 1128 if (inp & (GP2X_X|GP2X_A)) return;\r
cc68a136 1129 }\r
1130}\r
1131\r
1132// -------------- options --------------\r
1133\r
4e8a534c 1134\r
1135menu_entry opt_entries[] =\r
1136{\r
58c86d00 1137 { NULL, MB_NONE, MA_OPT_RENDERER, NULL, 0, 0, 0, 1, 1 },\r
1138 { NULL, MB_RANGE, MA_OPT_SCALING, &currentConfig.scaling, 0, 0, 3, 1, 1 },\r
c060a9ab 1139 { "Accurate sprites", MB_ONOFF, MA_OPT_ACC_SPRITES, &PicoOpt, 0x080, 0, 0, 0, 1 },\r
58c86d00 1140 { "Show FPS", MB_ONOFF, MA_OPT_SHOW_FPS, &currentConfig.EmuOpt, 0x002, 0, 0, 1, 1 },\r
1141 { NULL, MB_RANGE, MA_OPT_FRAMESKIP, &currentConfig.Frameskip, 0, -1, 16, 1, 1 },\r
1142 { "Enable sound", MB_ONOFF, MA_OPT_ENABLE_SOUND, &currentConfig.EmuOpt, 0x004, 0, 0, 1, 1 },\r
1143 { NULL, MB_NONE, MA_OPT_SOUND_QUALITY, NULL, 0, 0, 0, 1, 1 },\r
1144 { "Use ARM940 core for sound", MB_ONOFF, MA_OPT_ARM940_SOUND, &PicoOpt, 0x200, 0, 0, 1, 1 },\r
58c86d00 1145 { NULL, MB_NONE, MA_OPT_REGION, NULL, 0, 0, 0, 1, 1 },\r
1146 { "Use SRAM/BRAM savestates", MB_ONOFF, MA_OPT_SRAM_STATES, &currentConfig.EmuOpt, 0x001, 0, 0, 1, 1 },\r
1147 { NULL, MB_NONE, MA_OPT_CONFIRM_STATES,NULL, 0, 0, 0, 1, 1 },\r
1148 { "Save slot", MB_RANGE, MA_OPT_SAVE_SLOT, &state_slot, 0, 0, 9, 1, 1 },\r
1149 { NULL, MB_NONE, MA_OPT_CPU_CLOCKS, NULL, 0, 0, 0, 1, 1 },\r
1150 { "[Sega/Mega CD options]", MB_NONE, MA_OPT_SCD_OPTS, NULL, 0, 0, 0, 1, 0 },\r
1151 { "[advanced options]", MB_NONE, MA_OPT_ADV_OPTS, NULL, 0, 0, 0, 1, 0 },\r
1152 { NULL, MB_NONE, MA_OPT_SAVECFG, NULL, 0, 0, 0, 1, 0 },\r
1153 { "Save cfg for current game only",MB_NONE,MA_OPT_SAVECFG_GAME,NULL, 0, 0, 0, 1, 0 },\r
1154 { NULL, MB_NONE, MA_OPT_LOADCFG, NULL, 0, 0, 0, 1, 0 },\r
4e8a534c 1155};\r
1156\r
1157#define OPT_ENTRY_COUNT (sizeof(opt_entries) / sizeof(opt_entries[0]))\r
58c86d00 1158const int opt_entry_count = OPT_ENTRY_COUNT;\r
cc68a136 1159\r
4e8a534c 1160\r
1161static void menu_opt_cust_draw(const menu_entry *entry, int x, int y, void *param)\r
cc68a136 1162{\r
4e8a534c 1163 char *str, str24[24];\r
1164\r
1165 switch (entry->id)\r
1166 {\r
1167 case MA_OPT_RENDERER:\r
602133e1 1168 if (PicoOpt & POPT_ALT_RENDERER)\r
4e8a534c 1169 str = " 8bit fast";\r
1170 else if (currentConfig.EmuOpt&0x80)\r
1171 str = "16bit accurate";\r
1172 else\r
1173 str = " 8bit accurate";\r
a12e0116 1174 text_out16(x, y, "Renderer: %s", str);\r
4e8a534c 1175 break;\r
1176 case MA_OPT_SCALING:\r
1177 switch (currentConfig.scaling) {\r
1178 default: str = " OFF"; break;\r
1179 case 1: str = "hw horizontal"; break;\r
1180 case 2: str = "hw horiz. + vert."; break;\r
1181 case 3: str = "sw horizontal"; break;\r
1182 }\r
a12e0116 1183 text_out16(x, y, "Scaling: %s", str);\r
4e8a534c 1184 break;\r
1185 case MA_OPT_FRAMESKIP:\r
1186 if (currentConfig.Frameskip < 0)\r
1187 strcpy(str24, "Auto");\r
1188 else sprintf(str24, "%i", currentConfig.Frameskip);\r
a12e0116 1189 text_out16(x, y, "Frameskip %s", str24);\r
4e8a534c 1190 break;\r
1191 case MA_OPT_SOUND_QUALITY:\r
602133e1 1192 str = (PicoOpt & POPT_EN_STEREO) ? "stereo" : "mono";\r
58c86d00 1193 text_out16(x, y, "Sound Quality: %5iHz %s", PsndRate, str);\r
4e8a534c 1194 break;\r
1195 case MA_OPT_REGION:\r
58c86d00 1196 text_out16(x, y, "Region: %s", me_region_name(PicoRegionOverride, PicoAutoRgnOrder));\r
4e8a534c 1197 break;\r
1198 case MA_OPT_CONFIRM_STATES:\r
1199 switch ((currentConfig.EmuOpt >> 9) & 5) {\r
1200 default: str = "OFF"; break;\r
1201 case 1: str = "writes"; break;\r
1202 case 4: str = "loads"; break;\r
1203 case 5: str = "both"; break;\r
1204 }\r
a12e0116 1205 text_out16(x, y, "Confirm savestate %s", str);\r
4e8a534c 1206 break;\r
1207 case MA_OPT_CPU_CLOCKS:\r
a12e0116 1208 text_out16(x, y, "GP2X CPU clocks %iMhz", currentConfig.CPUclock);\r
4e8a534c 1209 break;\r
1210 case MA_OPT_SAVECFG:\r
1211 str24[0] = 0;\r
1212 if (config_slot != 0) sprintf(str24, " (profile: %i)", config_slot);\r
a12e0116 1213 text_out16(x, y, "Save cfg as default%s", str24);\r
4e8a534c 1214 break;\r
1215 case MA_OPT_LOADCFG:\r
a12e0116 1216 text_out16(x, y, "Load cfg from profile %i", config_slot);\r
4e8a534c 1217 break;\r
1218 default:\r
1219 printf("%s: unimplemented (%i)\n", __FUNCTION__, entry->id);\r
1220 break;\r
46969540 1221 }\r
4e8a534c 1222}\r
1223\r
1224\r
1225\r
1226static void draw_menu_options(int menu_sel)\r
1227{\r
a12e0116 1228 int tl_x = 25, tl_y = 24;\r
cc68a136 1229\r
e11c5548 1230 gp2x_pd_clone_buffer2();\r
1231\r
e5f426aa 1232 menu_draw_selection(tl_x - 16, tl_y + menu_sel*10, 284);\r
cc68a136 1233\r
a12e0116 1234 me_draw(opt_entries, OPT_ENTRY_COUNT, tl_x, tl_y, menu_opt_cust_draw, NULL);\r
cc68a136 1235\r
a12e0116 1236 menu_flip();\r
cc68a136 1237}\r
1238\r
1239static int sndrate_prevnext(int rate, int dir)\r
1240{\r
1241 int i, rates[] = { 8000, 11025, 16000, 22050, 44100 };\r
1242\r
1243 for (i = 0; i < 5; i++)\r
1244 if (rates[i] == rate) break;\r
1245\r
1246 i += dir ? 1 : -1;\r
1247 if (i > 4) return dir ? 44100 : 22050;\r
1248 if (i < 0) return dir ? 11025 : 8000;\r
1249 return rates[i];\r
1250}\r
1251\r
51a902ae 1252static void region_prevnext(int right)\r
1253{\r
1254 // jp_ntsc=1, jp_pal=2, usa=4, eu=8\r
1255 static int rgn_orders[] = { 0x148, 0x184, 0x814, 0x418, 0x841, 0x481 };\r
1256 int i;\r
1257 if (right) {\r
58c86d00 1258 if (!PicoRegionOverride) {\r
51a902ae 1259 for (i = 0; i < 6; i++)\r
1260 if (rgn_orders[i] == PicoAutoRgnOrder) break;\r
1261 if (i < 5) PicoAutoRgnOrder = rgn_orders[i+1];\r
58c86d00 1262 else PicoRegionOverride=1;\r
51a902ae 1263 }\r
58c86d00 1264 else PicoRegionOverride<<=1;\r
1265 if (PicoRegionOverride > 8) PicoRegionOverride = 8;\r
51a902ae 1266 } else {\r
58c86d00 1267 if (!PicoRegionOverride) {\r
51a902ae 1268 for (i = 0; i < 6; i++)\r
1269 if (rgn_orders[i] == PicoAutoRgnOrder) break;\r
1270 if (i > 0) PicoAutoRgnOrder = rgn_orders[i-1];\r
1271 }\r
58c86d00 1272 else PicoRegionOverride>>=1;\r
51a902ae 1273 }\r
1274}\r
1275\r
cc68a136 1276static void menu_options_save(void)\r
1277{\r
7d0143a2 1278 if (PicoRegionOverride) {\r
1279 // force setting possibly changed..\r
1280 Pico.m.pal = (PicoRegionOverride == 2 || PicoRegionOverride == 8) ? 1 : 0;\r
1281 }\r
602133e1 1282 if (!(PicoOpt & POPT_6BTN_PAD)) {\r
d524c827 1283 // unbind XYZ MODE, just in case\r
45499284 1284 unbind_action(0xf00, -1, -1);\r
cc68a136 1285 }\r
1286}\r
1287\r
bf098bc5 1288static int menu_loop_options(void)\r
cc68a136 1289{\r
aae35e84 1290 static int menu_sel = 0;\r
4e8a534c 1291 int menu_sel_max, ret;\r
cc68a136 1292 unsigned long inp = 0;\r
4e8a534c 1293 menu_id selected_id;\r
cc68a136 1294\r
eacee137 1295 me_enable(opt_entries, OPT_ENTRY_COUNT, MA_OPT_SAVECFG_GAME, rom_loaded);\r
4e8a534c 1296 me_enable(opt_entries, OPT_ENTRY_COUNT, MA_OPT_LOADCFG, config_slot != config_slot_current);\r
1297 menu_sel_max = me_count_enabled(opt_entries, OPT_ENTRY_COUNT) - 1;\r
d524c827 1298 if (menu_sel > menu_sel_max) menu_sel = menu_sel_max;\r
4e8a534c 1299\r
1300 while (1)\r
cc68a136 1301 {\r
1302 draw_menu_options(menu_sel);\r
1303 inp = wait_for_input(GP2X_UP|GP2X_DOWN|GP2X_LEFT|GP2X_RIGHT|GP2X_B|GP2X_X|GP2X_A);\r
4e8a534c 1304 if (inp & GP2X_UP ) { menu_sel--; if (menu_sel < 0) menu_sel = menu_sel_max; }\r
1305 if (inp & GP2X_DOWN) { menu_sel++; if (menu_sel > menu_sel_max) menu_sel = 0; }\r
1306 selected_id = me_index2id(opt_entries, OPT_ENTRY_COUNT, menu_sel);\r
58c86d00 1307 if (inp & (GP2X_LEFT|GP2X_RIGHT)) { // multi choice\r
4e8a534c 1308 if (!me_process(opt_entries, OPT_ENTRY_COUNT, selected_id, (inp&GP2X_RIGHT) ? 1 : 0)) {\r
1309 switch (selected_id) {\r
1310 case MA_OPT_RENDERER:\r
1311 if (inp & GP2X_LEFT) {\r
58c86d00 1312 if (PicoOpt&0x10) PicoOpt&= ~0x10;\r
4e8a534c 1313 else if (!(currentConfig.EmuOpt &0x80))currentConfig.EmuOpt |= 0x80;\r
1314 else if ( currentConfig.EmuOpt &0x80) break;\r
1315 } else {\r
58c86d00 1316 if (PicoOpt&0x10) break;\r
1317 else if (!(currentConfig.EmuOpt &0x80))PicoOpt|= 0x10;\r
4e8a534c 1318 else if ( currentConfig.EmuOpt &0x80) currentConfig.EmuOpt &= ~0x80;\r
1319 }\r
1320 break;\r
1321 case MA_OPT_SOUND_QUALITY:\r
58c86d00 1322 if ((inp & GP2X_RIGHT) && PsndRate == 44100 && !(PicoOpt&0x08)) {\r
1323 PsndRate = 8000; PicoOpt|= 0x08;\r
1324 } else if ((inp & GP2X_LEFT) && PsndRate == 8000 && (PicoOpt&0x08)) {\r
1325 PsndRate = 44100; PicoOpt&=~0x08;\r
1326 } else PsndRate = sndrate_prevnext(PsndRate, inp & GP2X_RIGHT);\r
4e8a534c 1327 break;\r
1328 case MA_OPT_REGION:\r
1329 region_prevnext(inp & GP2X_RIGHT);\r
1330 break;\r
1331 case MA_OPT_CONFIRM_STATES: {\r
1332 int n = ((currentConfig.EmuOpt>>9)&1) | ((currentConfig.EmuOpt>>10)&2);\r
1333 n += (inp & GP2X_LEFT) ? -1 : 1;\r
1334 if (n < 0) n = 0; else if (n > 3) n = 3;\r
1335 n |= n << 1; n &= ~2;\r
1336 currentConfig.EmuOpt &= ~0xa00;\r
1337 currentConfig.EmuOpt |= n << 9;\r
1338 break;\r
1339 }\r
1340 case MA_OPT_SAVE_SLOT:\r
1341 if (inp & GP2X_RIGHT) {\r
1342 state_slot++; if (state_slot > 9) state_slot = 0;\r
1343 } else {state_slot--; if (state_slot < 0) state_slot = 9;\r
1344 }\r
1345 break;\r
1346 case MA_OPT_CPU_CLOCKS:\r
1347 while ((inp = gp2x_joystick_read(1)) & (GP2X_LEFT|GP2X_RIGHT)) {\r
1348 currentConfig.CPUclock += (inp & GP2X_LEFT) ? -1 : 1;\r
1349 if (currentConfig.CPUclock < 1) currentConfig.CPUclock = 1;\r
1350 draw_menu_options(menu_sel);\r
1351 usleep(50*1000);\r
1352 }\r
1353 break;\r
1354 case MA_OPT_SAVECFG:\r
1355 case MA_OPT_SAVECFG_GAME:\r
1356 case MA_OPT_LOADCFG:\r
1357 config_slot += (inp&GP2X_RIGHT) ? 1 : -1;\r
1358 if (config_slot > 9) config_slot = 0;\r
1359 if (config_slot < 0) config_slot = 9;\r
1360 me_enable(opt_entries, OPT_ENTRY_COUNT, MA_OPT_LOADCFG, config_slot != config_slot_current);\r
1361 menu_sel_max = me_count_enabled(opt_entries, OPT_ENTRY_COUNT) - 1;\r
1362 if (menu_sel > menu_sel_max) menu_sel = menu_sel_max;\r
1363 break;\r
1364 default:\r
1365 //printf("%s: something unknown selected (%i)\n", __FUNCTION__, selected_id);\r
1366 break;\r
1367 }\r
1368 }\r
1369 }\r
1370 if (inp & GP2X_B) {\r
1371 if (!me_process(opt_entries, OPT_ENTRY_COUNT, selected_id, 1))\r
1372 {\r
1373 switch (selected_id)\r
1374 {\r
1375 case MA_OPT_SCD_OPTS:\r
1376 cd_menu_loop_options();\r
1377 if (engineState == PGS_ReloadRom)\r
1378 return 0; // test BIOS\r
1379 break;\r
1380 case MA_OPT_ADV_OPTS:\r
1381 amenu_loop_options();\r
1382 break;\r
1383 case MA_OPT_SAVECFG: // done (update and write)\r
1384 menu_options_save();\r
1385 if (emu_WriteConfig(0)) strcpy(menuErrorMsg, "config saved");\r
1386 else strcpy(menuErrorMsg, "failed to write config");\r
1387 return 1;\r
1388 case MA_OPT_SAVECFG_GAME: // done (update and write for current game)\r
1389 menu_options_save();\r
1390 if (emu_WriteConfig(1)) strcpy(menuErrorMsg, "config saved");\r
1391 else strcpy(menuErrorMsg, "failed to write config");\r
1392 return 1;\r
1393 case MA_OPT_LOADCFG:\r
d524c827 1394 ret = emu_ReadConfig(1, 1);\r
1395 if (!ret) ret = emu_ReadConfig(0, 1);\r
1396 if (ret) strcpy(menuErrorMsg, "config loaded");\r
4e8a534c 1397 else strcpy(menuErrorMsg, "failed to load config");\r
1398 return 1;\r
1399 default:\r
1400 //printf("%s: something unknown selected (%i)\n", __FUNCTION__, selected_id);\r
1401 break;\r
1402 }\r
cc68a136 1403 }\r
1404 }\r
51a902ae 1405 if(inp & (GP2X_X|GP2X_A)) {\r
cc68a136 1406 menu_options_save();\r
bf098bc5 1407 return 0; // done (update, no write)\r
cc68a136 1408 }\r
cc68a136 1409 }\r
1410}\r
1411\r
1412// -------------- credits --------------\r
1413\r
1414static void draw_menu_credits(void)\r
1415{\r
053fd9b4 1416 int tl_x = 15, tl_y = 56, y;\r
e11c5548 1417 gp2x_pd_clone_buffer2();\r
cc68a136 1418\r
f9d3ee9d 1419 text_out16(tl_x, 20, "PicoDrive v" VERSION " (c) notaz, 2006-2008");\r
cc68a136 1420 y = tl_y;\r
a12e0116 1421 text_out16(tl_x, y, "Credits:");\r
3ec29f01 1422 text_out16(tl_x, (y+=10), "fDave: Cyclone 68000 core,");\r
a12e0116 1423 text_out16(tl_x, (y+=10), " base code of PicoDrive");\r
1424 text_out16(tl_x, (y+=10), "Reesy & FluBBa: DrZ80 core");\r
1425 text_out16(tl_x, (y+=10), "MAME devs: YM2612 and SN76496 cores");\r
a12e0116 1426 text_out16(tl_x, (y+=10), "rlyeh and others: minimal SDK");\r
1427 text_out16(tl_x, (y+=10), "Squidge: squidgehack");\r
1428 text_out16(tl_x, (y+=10), "Dzz: ARM940 sample");\r
053fd9b4 1429 text_out16(tl_x, (y+=10), "GnoStiC / Puck2099: USB joy code");\r
a12e0116 1430 text_out16(tl_x, (y+=10), "craigix: GP2X hardware");\r
a4f0cc86 1431 text_out16(tl_x, (y+=10), "ketchupgun: skin design");\r
a12e0116 1432\r
1a65e3b1 1433 text_out16(tl_x, (y+=20), "special thanks (for docs, ideas):");\r
053fd9b4 1434 text_out16(tl_x, (y+=10), " Charles MacDonald, Haze,");\r
1435 text_out16(tl_x, (y+=10), " Stephane Dallongeville,");\r
1436 text_out16(tl_x, (y+=10), " Lordus, Exophase, Rokas,");\r
1437 text_out16(tl_x, (y+=10), " Nemesis, Tasco Deluxe");\r
1438\r
a12e0116 1439 menu_flip();\r
cc68a136 1440}\r
1441\r
1442\r
1443// -------------- root menu --------------\r
1444\r
4e8a534c 1445menu_entry main_entries[] =\r
1446{\r
58c86d00 1447 { "Resume game", MB_NONE, MA_MAIN_RESUME_GAME, NULL, 0, 0, 0, 0, 0 },\r
1448 { "Save State", MB_NONE, MA_MAIN_SAVE_STATE, NULL, 0, 0, 0, 0, 0 },\r
1449 { "Load State", MB_NONE, MA_MAIN_LOAD_STATE, NULL, 0, 0, 0, 0, 0 },\r
1450 { "Reset game", MB_NONE, MA_MAIN_RESET_GAME, NULL, 0, 0, 0, 0, 0 },\r
1451 { "Load new ROM/ISO", MB_NONE, MA_MAIN_LOAD_ROM, NULL, 0, 0, 0, 1, 0 },\r
1452 { "Change options", MB_NONE, MA_MAIN_OPTIONS, NULL, 0, 0, 0, 1, 0 },\r
1453 { "Configure controls", MB_NONE, MA_MAIN_CONTROLS, NULL, 0, 0, 0, 1, 0 },\r
1454 { "Credits", MB_NONE, MA_MAIN_CREDITS, NULL, 0, 0, 0, 1, 0 },\r
1455 { "Patches / GameGenie",MB_NONE, MA_MAIN_PATCHES, NULL, 0, 0, 0, 0, 0 },\r
1456 { "Exit", MB_NONE, MA_MAIN_EXIT, NULL, 0, 0, 0, 1, 0 }\r
4e8a534c 1457};\r
1458\r
1459#define MAIN_ENTRY_COUNT (sizeof(main_entries) / sizeof(main_entries[0]))\r
1460\r
cc68a136 1461static void draw_menu_root(int menu_sel)\r
1462{\r
4e8a534c 1463 const int tl_x = 70, tl_y = 70;\r
1464\r
e11c5548 1465 gp2x_pd_clone_buffer2();\r
cc68a136 1466\r
a12e0116 1467 text_out16(tl_x, 20, "PicoDrive v" VERSION);\r
1468\r
e5f426aa 1469 menu_draw_selection(tl_x - 16, tl_y + menu_sel*10, 146);\r
cc68a136 1470\r
4e8a534c 1471 me_draw(main_entries, MAIN_ENTRY_COUNT, tl_x, tl_y, NULL, NULL);\r
cc68a136 1472\r
cc68a136 1473 // error\r
a12e0116 1474 if (menuErrorMsg[0]) {\r
1475 memset((char *)gp2x_screen + 320*224*2, 0, 320*16*2);\r
1476 text_out16(5, 226, menuErrorMsg);\r
1477 }\r
1478 menu_flip();\r
cc68a136 1479}\r
1480\r
1481\r
1482static void menu_loop_root(void)\r
1483{\r
4e8a534c 1484 static int menu_sel = 0;\r
1485 int ret, menu_sel_max;\r
cc68a136 1486 unsigned long inp = 0;\r
cc68a136 1487\r
eacee137 1488 me_enable(main_entries, MAIN_ENTRY_COUNT, MA_MAIN_RESUME_GAME, rom_loaded);\r
1489 me_enable(main_entries, MAIN_ENTRY_COUNT, MA_MAIN_SAVE_STATE, rom_loaded);\r
1490 me_enable(main_entries, MAIN_ENTRY_COUNT, MA_MAIN_LOAD_STATE, rom_loaded);\r
1491 me_enable(main_entries, MAIN_ENTRY_COUNT, MA_MAIN_RESET_GAME, rom_loaded);\r
4e8a534c 1492 me_enable(main_entries, MAIN_ENTRY_COUNT, MA_MAIN_PATCHES, PicoPatches != NULL);\r
1493\r
1494 menu_sel_max = me_count_enabled(main_entries, MAIN_ENTRY_COUNT) - 1;\r
d524c827 1495 if (menu_sel > menu_sel_max) menu_sel = menu_sel_max;\r
cc68a136 1496\r
2433f409 1497 /* make sure action buttons are not pressed on entering menu */\r
1498 draw_menu_root(menu_sel);\r
1499 while (gp2x_joystick_read(1) & (GP2X_B|GP2X_X|GP2X_SELECT)) usleep(50*1000);\r
1500\r
1501 for (;;)\r
cc68a136 1502 {\r
1503 draw_menu_root(menu_sel);\r
0af33fe0 1504 inp = wait_for_input(GP2X_UP|GP2X_DOWN|GP2X_B|GP2X_X|GP2X_SELECT|GP2X_L|GP2X_R);\r
4e8a534c 1505 if(inp & GP2X_UP ) { menu_sel--; if (menu_sel < 0) menu_sel = menu_sel_max; }\r
1506 if(inp & GP2X_DOWN) { menu_sel++; if (menu_sel > menu_sel_max) menu_sel = 0; }\r
0af33fe0 1507 if((inp & (GP2X_L|GP2X_R)) == (GP2X_L|GP2X_R)) debug_menu_loop();\r
cc68a136 1508 if(inp &(GP2X_SELECT|GP2X_X)){\r
eacee137 1509 if (rom_loaded) {\r
cc68a136 1510 while (gp2x_joystick_read(1) & (GP2X_SELECT|GP2X_X)) usleep(50*1000); // wait until select is released\r
1511 engineState = PGS_Running;\r
1512 break;\r
1513 }\r
1514 }\r
4e8a534c 1515 if(inp & GP2X_B) {\r
1516 switch (me_index2id(main_entries, MAIN_ENTRY_COUNT, menu_sel))\r
1517 {\r
1518 case MA_MAIN_RESUME_GAME:\r
eacee137 1519 if (rom_loaded) {\r
721cd396 1520 while (gp2x_joystick_read(1) & GP2X_B) usleep(50*1000);\r
1521 engineState = PGS_Running;\r
1522 return;\r
1523 }\r
cc68a136 1524 break;\r
4e8a534c 1525 case MA_MAIN_SAVE_STATE:\r
eacee137 1526 if (rom_loaded) {\r
860c6322 1527 if(savestate_menu_loop(0))\r
cc68a136 1528 continue;\r
cc68a136 1529 engineState = PGS_Running;\r
1530 return;\r
1531 }\r
1532 break;\r
4e8a534c 1533 case MA_MAIN_LOAD_STATE:\r
eacee137 1534 if (rom_loaded) {\r
860c6322 1535 if(savestate_menu_loop(1))\r
cc68a136 1536 continue;\r
5ed2561c 1537 while (gp2x_joystick_read(1) & GP2X_B) usleep(50*1000);\r
cc68a136 1538 engineState = PGS_Running;\r
1539 return;\r
1540 }\r
1541 break;\r
4e8a534c 1542 case MA_MAIN_RESET_GAME:\r
eacee137 1543 if (rom_loaded) {\r
cc68a136 1544 emu_ResetGame();\r
5ed2561c 1545 while (gp2x_joystick_read(1) & GP2X_B) usleep(50*1000);\r
cc68a136 1546 engineState = PGS_Running;\r
1547 return;\r
1548 }\r
1549 break;\r
4e8a534c 1550 case MA_MAIN_LOAD_ROM:\r
a12e0116 1551 {\r
1552 char curr_path[PATH_MAX], *selfname;\r
1553 FILE *tstf;\r
ca482e5d 1554 if ( (tstf = fopen(loadedRomFName, "rb")) )\r
a12e0116 1555 {\r
1556 fclose(tstf);\r
ca482e5d 1557 strcpy(curr_path, loadedRomFName);\r
a12e0116 1558 }\r
1559 else\r
1560 getcwd(curr_path, PATH_MAX);\r
cc68a136 1561 selfname = romsel_loop(curr_path);\r
1562 if (selfname) {\r
1563 printf("selected file: %s\n", selfname);\r
cc68a136 1564 engineState = PGS_ReloadRom;\r
a12e0116 1565 return;\r
cc68a136 1566 }\r
a12e0116 1567 break;\r
1568 }\r
4e8a534c 1569 case MA_MAIN_OPTIONS:\r
bf098bc5 1570 ret = menu_loop_options();\r
1571 if (ret == 1) continue; // status update\r
1572 if (engineState == PGS_ReloadRom)\r
1573 return; // BIOS test\r
cc68a136 1574 break;\r
4e8a534c 1575 case MA_MAIN_CONTROLS:\r
cc68a136 1576 kc_sel_loop();\r
1577 break;\r
4e8a534c 1578 case MA_MAIN_CREDITS:\r
cc68a136 1579 draw_menu_credits();\r
1580 usleep(500*1000);\r
1581 inp = wait_for_input(GP2X_B|GP2X_X);\r
1582 break;\r
4e8a534c 1583 case MA_MAIN_EXIT:\r
cc68a136 1584 engineState = PGS_Quit;\r
1585 return;\r
4e8a534c 1586 case MA_MAIN_PATCHES:\r
eacee137 1587 if (rom_loaded && PicoPatches) {\r
b67ef287 1588 patches_menu_loop();\r
1589 PicoPatchApply();\r
1590 strcpy(menuErrorMsg, "Patches applied");\r
1591 continue;\r
1592 }\r
1593 break;\r
4e8a534c 1594 default:\r
1595 printf("%s: something unknown selected\n", __FUNCTION__);\r
1596 break;\r
cc68a136 1597 }\r
1598 }\r
1599 menuErrorMsg[0] = 0; // clear error msg\r
1600 }\r
1601}\r
1602\r
f579f7b8 1603void menu_darken_bg(void *dst, int pixels, int darker)\r
cc68a136 1604{\r
a12e0116 1605 unsigned int *screen = dst;\r
1606 pixels /= 2;\r
a4f0cc86 1607 if (darker)\r
a12e0116 1608 {\r
a4f0cc86 1609 while (pixels--)\r
1610 {\r
1611 unsigned int p = *screen;\r
1612 *screen++ = ((p&0xf79ef79e)>>1) - ((p&0xc618c618)>>3);\r
1613 }\r
1614 }\r
1615 else\r
1616 {\r
1617 while (pixels--)\r
1618 {\r
1619 unsigned int p = *screen;\r
1620 *screen++ = (p&0xf79ef79e)>>1;\r
1621 }\r
a12e0116 1622 }\r
1623}\r
e11c5548 1624\r
a12e0116 1625static void menu_prepare_bg(int use_game_bg)\r
1626{\r
1627 if (use_game_bg)\r
1628 {\r
1629 // darken the active framebuffer\r
1630 memset(gp2x_screen, 0, 320*8*2);\r
a4f0cc86 1631 menu_darken_bg((char *)gp2x_screen + 320*8*2, 320*224, 1);\r
a12e0116 1632 memset((char *)gp2x_screen + 320*232*2, 0, 320*8*2);\r
1633 }\r
1634 else\r
1635 {\r
1636 // should really only happen once, on startup..\r
1637 readpng(gp2x_screen, "skin/background.png", READPNG_BG);\r
860c6322 1638 }\r
cc68a136 1639\r
a12e0116 1640 // copy to buffer2\r
1641 gp2x_memcpy_buffers((1<<2), gp2x_screen, 0, 320*240*2);\r
860c6322 1642}\r
1643\r
1644static void menu_gfx_prepare(void)\r
1645{\r
eacee137 1646 menu_prepare_bg(rom_loaded);\r
860c6322 1647\r
a12e0116 1648 // switch to 16bpp\r
1649 gp2x_video_changemode2(16);\r
2433f409 1650 gp2x_video_RGB_setscaling(0, 320, 240);\r
a12e0116 1651 menu_flip();\r
e11c5548 1652}\r
1653\r
1654\r
1655void menu_loop(void)\r
1656{\r
1657 menu_gfx_prepare();\r
cc68a136 1658\r
1659 menu_loop_root();\r
1660\r
1661 menuErrorMsg[0] = 0;\r
1662}\r
721cd396 1663\r
1664\r
1665// --------- CD tray close menu ----------\r
1666\r
1667static void draw_menu_tray(int menu_sel)\r
1668{\r
1669 int tl_x = 70, tl_y = 90, y;\r
a12e0116 1670 memset(gp2x_screen, 0, 320*240*2);\r
721cd396 1671\r
a12e0116 1672 text_out16(tl_x, 20, "The unit is about to");\r
1673 text_out16(tl_x, 30, "close the CD tray.");\r
721cd396 1674\r
1675 y = tl_y;\r
a12e0116 1676 text_out16(tl_x, y, "Load new CD image");\r
1677 text_out16(tl_x, (y+=10), "Insert nothing");\r
721cd396 1678\r
1679 // draw cursor\r
a12e0116 1680 text_out16(tl_x - 16, tl_y + menu_sel*10, ">");\r
721cd396 1681 // error\r
a12e0116 1682 if (menuErrorMsg[0]) text_out16(5, 226, menuErrorMsg);\r
1683 menu_flip();\r
721cd396 1684}\r
1685\r
1686\r
1687int menu_loop_tray(void)\r
1688{\r
1689 int menu_sel = 0, menu_sel_max = 1;\r
1690 unsigned long inp = 0;\r
1691 char curr_path[PATH_MAX], *selfname;\r
1692 FILE *tstf;\r
1693\r
e5f426aa 1694 gp2x_memset_all_buffers(0, 0, 320*240*2);\r
721cd396 1695 menu_gfx_prepare();\r
1696\r
ca482e5d 1697 if ( (tstf = fopen(loadedRomFName, "rb")) )\r
721cd396 1698 {\r
1699 fclose(tstf);\r
ca482e5d 1700 strcpy(curr_path, loadedRomFName);\r
721cd396 1701 }\r
1702 else\r
1703 {\r
1704 getcwd(curr_path, PATH_MAX);\r
1705 }\r
1706\r
1707 /* make sure action buttons are not pressed on entering menu */\r
1708 draw_menu_tray(menu_sel);\r
1709 while (gp2x_joystick_read(1) & GP2X_B) usleep(50*1000);\r
1710\r
1711 for (;;)\r
1712 {\r
1713 draw_menu_tray(menu_sel);\r
1714 inp = wait_for_input(GP2X_UP|GP2X_DOWN|GP2X_B);\r
1715 if(inp & GP2X_UP ) { menu_sel--; if (menu_sel < 0) menu_sel = menu_sel_max; }\r
1716 if(inp & GP2X_DOWN) { menu_sel++; if (menu_sel > menu_sel_max) menu_sel = 0; }\r
1717 if(inp & GP2X_B ) {\r
1718 switch (menu_sel) {\r
1719 case 0: // select image\r
1720 selfname = romsel_loop(curr_path);\r
1721 if (selfname) {\r
b923ecbe 1722 int ret = -1;\r
1723 cd_img_type cd_type;\r
ca482e5d 1724 cd_type = emu_cdCheck(NULL, romFileName);\r
b923ecbe 1725 if (cd_type != CIT_NOT_CD)\r
1726 ret = Insert_CD(romFileName, cd_type);\r
721cd396 1727 if (ret != 0) {\r
1728 sprintf(menuErrorMsg, "Load failed, invalid CD image?");\r
1729 printf("%s\n", menuErrorMsg);\r
1730 continue;\r
1731 }\r
1732 engineState = PGS_RestartRun;\r
1733 return 1;\r
1734 }\r
1735 break;\r
1736 case 1: // insert nothing\r
1737 engineState = PGS_RestartRun;\r
1738 return 0;\r
1739 }\r
1740 }\r
1741 menuErrorMsg[0] = 0; // clear error msg\r
1742 }\r
1743}\r
1744\r
1745\r