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