fix events loss
[libpicofe.git] / gp2x / menu.c
CommitLineData
95151aea 1// (c) Copyright 2006,2007 notaz, All rights reserved.\r
720ee7f6 2// Free for non-commercial use.\r
3\r
4// For commercial use, separate licencing terms must be obtained.\r
5\r
6#include <stdio.h>\r
7#include <string.h>\r
8#include <stdlib.h>\r
9#include <stdarg.h>\r
10#include <unistd.h>\r
11#include <dirent.h>\r
12\r
13#include "gp2x.h"\r
14#include "emu.h"\r
15#include "menu.h"\r
b3972d82 16#include "../linux/usbjoy.h"\r
f013066e 17#include "../common/emu.h"\r
c7a4ff64 18#include "../common/menu.h"\r
f013066e 19#include "../common/arm_utils.h"\r
c7a4ff64 20#include "../common/readpng.h"\r
bc0420cd 21#include "../common/common.h"\r
22#include "../common/input.h"\r
720ee7f6 23#include "version.h"\r
24\r
f11bad75 25#include <pico/pico_int.h>\r
26#include <pico/patch.h>\r
70d2ecc5 27#include <zlib/zlib.h>\r
720ee7f6 28\r
29#ifndef _DIRENT_HAVE_D_TYPE\r
260f1bcc 30#error "need d_type for file browser"\r
720ee7f6 31#endif\r
32\r
720ee7f6 33extern int mmuhack_status;\r
720ee7f6 34\r
30d5518a 35const char * const keyNames[] = {\r
720ee7f6 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
b8464531 42void menu_darken_bg(void *dst, int pixels, int darker);\r
13059a60 43static void menu_prepare_bg(int use_game_bg);\r
720ee7f6 44\r
bc0420cd 45/* wait for input, do autorepeat */\r
46int wait_for_input(int interesting)\r
720ee7f6 47{\r
bc0420cd 48 static int inp_prev = 0;\r
6af8fee5 49 static int repeats = 0, wait = 20;\r
bc0420cd 50 int ret, release = 0, i;\r
720ee7f6 51\r
704c0ea3 52 if (repeats == 2) wait = 3;\r
53 else if (repeats == 4) wait = 2;\r
54 else if (repeats == 6) wait = 1;\r
720ee7f6 55\r
bc0420cd 56 for (i = 0; i < wait; i++) {\r
57 ret = in_update_menu(30);\r
58 if (ret != inp_prev) break;\r
5e2e14f2 59 if (i == 0) repeats++;\r
720ee7f6 60 }\r
61\r
bc0420cd 62 while (!(ret & interesting)) {\r
63 ret = in_update_menu(0);\r
720ee7f6 64 release = 1;\r
65 }\r
66\r
67 if (release || ret != inp_prev) {\r
68 repeats = 0;\r
6af8fee5 69 wait = 20;\r
720ee7f6 70 }\r
bc0420cd 71 if (wait > 6 && (ret & (PBTN_UP|PBTN_DOWN|PBTN_LEFT|PBTN_RIGHT)))\r
6af8fee5 72 wait = 6;\r
720ee7f6 73 inp_prev = ret;\r
720ee7f6 74\r
75 // we don't need diagonals in menus\r
bc0420cd 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
367b6f1f 80\r
720ee7f6 81 return ret;\r
82}\r
83\r
b8464531 84void menu_flip(void)\r
c7a4ff64 85{\r
86 gp2x_video_flush_cache();\r
87 gp2x_video_flip2();\r
88}\r
720ee7f6 89\r
90\r
b9f8cb3d 91// --------- loading ROM screen ----------\r
92\r
677b5dd8 93static int cdload_called = 0;\r
94\r
b9f8cb3d 95static void load_progress_cb(int percent)\r
96{\r
97 int ln, len = percent * 320 / 100;\r
13059a60 98 unsigned short *dst = (unsigned short *)gp2x_screen + 320*20;\r
b9f8cb3d 99\r
100 if (len > 320) len = 320;\r
677b5dd8 101 for (ln = 8; ln > 0; ln--, dst += 320)\r
13059a60 102 memset(dst, 0xff, len*2);\r
103 menu_flip();\r
b9f8cb3d 104}\r
105\r
677b5dd8 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
6245d5a0 115 dst += 320*30;\r
677b5dd8 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
6245d5a0 120 menu_flip();\r
677b5dd8 121 cdload_called = 1;\r
122}\r
123\r
b9f8cb3d 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
144a28a0 129 if (rom_loaded) gp2x_pd_clone_buffer2();\r
13059a60 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
b9f8cb3d 136 PicoCartLoadProgressCB = load_progress_cb;\r
677b5dd8 137 PicoCDLoadProgressCB = cdload_progress_cb;\r
138 cdload_called = 0;\r
b9f8cb3d 139}\r
140\r
141void menu_romload_end(void)\r
142{\r
6245d5a0 143 PicoCartLoadProgressCB = PicoCDLoadProgressCB = NULL;\r
677b5dd8 144 smalltext_out16(1, cdload_called ? 60 : 30, "Starting emulation...", 0xffff);\r
13059a60 145 menu_flip();\r
b9f8cb3d 146}\r
147\r
720ee7f6 148// -------------- ROM selector --------------\r
149\r
13059a60 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
1ceda417 154 static const char *rom_exts[] = { "zip", "bin", "smd", "gen", "iso", "cso", "cue" };\r
13059a60 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
720ee7f6 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
f9a37a02 173 gp2x_pd_clone_buffer2();\r
174\r
144a28a0 175 if (!rom_loaded) {\r
f9a37a02 176 menu_darken_bg(gp2x_screen, 320*240, 0);\r
13059a60 177 }\r
178\r
f9a37a02 179 menu_darken_bg((char *)gp2x_screen + 320*120*2, 320*8, 0);\r
720ee7f6 180\r
181 if(start - 2 >= 0)\r
13059a60 182 smalltext_out16_lim(14, (start - 2)*10, curdir, 0xffff, 53-2);\r
720ee7f6 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
13059a60 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
720ee7f6 190 } else {\r
13059a60 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
720ee7f6 193 }\r
194 }\r
13059a60 195 text_out16(5, 120, ">");\r
196 menu_flip();\r
720ee7f6 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
79cad122 208static char *filter_exts[] = {\r
209 ".mp3", ".MP3", ".srm", ".brm", "s.gz", ".mds", "bcfg", ".txt", ".htm", "html",\r
1ceda417 210 ".jpg", ".gpe"\r
79cad122 211};\r
0d0558bd 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
720ee7f6 230\r
55c7b464 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
720ee7f6 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
55c7b464 264rescan:\r
720ee7f6 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
0d0558bd 275 n = scandir(curr_path, &namelist, scandir_filter, scandir_cmp);\r
720ee7f6 276 if (n < 0) {\r
277 // try root\r
0d0558bd 278 n = scandir("/", &namelist, scandir_filter, scandir_cmp);\r
720ee7f6 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
55c7b464 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
720ee7f6 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
5e2e14f2 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
55c7b464 308 if ((inp & GP2X_B) || (inp & (GP2X_SELECT|GP2X_A)) == (GP2X_SELECT|GP2X_A)) // enter dir/select || delete\r
309 {\r
720ee7f6 310 again:\r
55c7b464 311 if (namelist[sel+1]->d_type == DT_REG)\r
312 {\r
720ee7f6 313 strcpy(romFileName, curr_path);\r
314 strcat(romFileName, "/");\r
315 strcat(romFileName, namelist[sel+1]->d_name);\r
55c7b464 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
720ee7f6 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
55c7b464 351 }\r
352 else\r
353 {\r
720ee7f6 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
55c7b464 374 while (n--) free(namelist[n]);\r
720ee7f6 375 free(namelist);\r
376 }\r
377\r
378 return ret;\r
379}\r
380\r
70d2ecc5 381// ------------ patch/gg menu ------------\r
382\r
383static void draw_patchlist(int sel)\r
384{\r
13059a60 385 int start, i, pos, active;\r
70d2ecc5 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
13059a60 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
70d2ecc5 398 }\r
399 pos = start + i;\r
13059a60 400 if (pos < 24) smalltext_out16_lim(14, pos*10, "done", 0xffff, 4);\r
70d2ecc5 401\r
13059a60 402 text_out16(5, 120, ">");\r
403 menu_flip();\r
70d2ecc5 404}\r
405\r
406\r
0ae6813e 407static void patches_menu_loop(void)\r
70d2ecc5 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
4ffd2858 430// ------------ savestate loader ------------\r
431\r
4ffd2858 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
f013066e 442 if (emu_checkSaveFile(slot))\r
4ffd2858 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
f013066e 470 emu_setSaveStateCbs(1);\r
4ffd2858 471 } else {\r
472 file = fopen(fname, "rb");\r
f013066e 473 emu_setSaveStateCbs(0);\r
4ffd2858 474 }\r
475\r
476 if (file) {\r
dd5fd477 477 if (PicoAHW & PAHW_MCD) {\r
4ffd2858 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
84e21f25 491 emu_forcedFrame(POPT_EN_SOFTSCALE);\r
13059a60 492 menu_prepare_bg(1);\r
4ffd2858 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
13059a60 509 text_out16(tl_x, 30, is_loading ? "Load state" : "Save state");\r
510\r
c7a4ff64 511 menu_draw_selection(tl_x - 16, tl_y + menu_sel*10, 108);\r
4ffd2858 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
13059a60 517 text_out16(tl_x, y, "SLOT %i (%s)", i, (state_slot_flags & (1 << i)) ? "USED" : "free");\r
4ffd2858 518 }\r
13059a60 519 text_out16(tl_x, y, "back");\r
4ffd2858 520\r
13059a60 521 menu_flip();\r
4ffd2858 522}\r
523\r
524static int savestate_menu_loop(int is_loading)\r
525{\r
59d0f042 526 static int menu_sel = 10;\r
527 int menu_sel_max = 10;\r
4ffd2858 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
720ee7f6 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
95151aea 576static char *action_binds(int player_idx, int action_mask)\r
720ee7f6 577{\r
bc0420cd 578 static char strkeys[32];\r
579 int d, k, d_prev = -1;\r
720ee7f6 580\r
581 strkeys[0] = 0;\r
bc0420cd 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
95151aea 613 for (i = 0; i < 32; i++) // i is key index\r
720ee7f6 614 {\r
95151aea 615 if (currentConfig.KeyBinds[i] & action_mask)\r
720ee7f6 616 {\r
95151aea 617 if (player_idx >= 0 && ((currentConfig.KeyBinds[i] >> 16) & 3) != player_idx) continue;\r
367b6f1f 618 if (strkeys[0]) { strcat(strkeys, " + "); strcat(strkeys, keyNames[i]); break; }\r
619 else strcpy(strkeys, keyNames[i]);\r
720ee7f6 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
95151aea 626 if (currentConfig.JoyBinds[joy][i] & action_mask)\r
720ee7f6 627 {\r
95151aea 628 if (player_idx >= 0 && ((currentConfig.JoyBinds[joy][i] >> 16) & 3) != player_idx) continue;\r
720ee7f6 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
bc0420cd 637#endif\r
638\r
720ee7f6 639\r
e67f6454 640 // limit..\r
641 strkeys[20] = 0;\r
642\r
95151aea 643 return strkeys;\r
644}\r
645\r
e67f6454 646static void unbind_action(int action, int pl_idx, int joy)\r
95151aea 647{\r
648 int i, u;\r
649\r
e67f6454 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
b4c711c3 656 }\r
e67f6454 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
b4c711c3 667 for (i = 0; i < 32; i++) {\r
e67f6454 668 if (pl_idx >= 0 && (currentConfig.JoyBinds[joy-1][i]&0x30000) != (pl_idx<<16)) continue;\r
669 currentConfig.JoyBinds[joy-1][i] &= ~action;\r
b4c711c3 670 }\r
e67f6454 671 }\r
95151aea 672}\r
673\r
bc0420cd 674static int count_bound_keys(int dev_id, int action_mask, int player_idx)\r
95151aea 675{\r
bc0420cd 676 const int *binds;\r
677 int k, keys = 0;\r
678 int count;\r
95151aea 679\r
bc0420cd 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
95151aea 686 {\r
bc0420cd 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
95151aea 694 }\r
bc0420cd 695\r
95151aea 696 return keys;\r
697}\r
698\r
bc0420cd 699static void draw_key_config(const me_bind_action *opts, int opt_cnt, int player_idx,\r
700 int sel, int is_bind)\r
95151aea 701{\r
6589c840 702 int x, y, tl_y = 30, i;\r
95151aea 703\r
e5d315a5 704 gp2x_pd_clone_buffer2();\r
95151aea 705 if (player_idx >= 0) {\r
6589c840 706 text_out16(80, 10, "Player %i controls", player_idx + 1);\r
b4c711c3 707 x = 80;\r
95151aea 708 } else {\r
6589c840 709 text_out16(80, 10, "Emulator controls");\r
95151aea 710 x = 40;\r
711 }\r
712\r
e1cd546f 713 menu_draw_selection(x - 16, tl_y + sel*10, (player_idx >= 0) ? 66 : 140);\r
13059a60 714\r
95151aea 715 y = tl_y;\r
716 for (i = 0; i < opt_cnt; i++, y+=10)\r
13059a60 717 text_out16(x, y, "%s : %s", opts[i].name, action_binds(player_idx, opts[i].mask));\r
95151aea 718\r
13059a60 719 text_out16(x, y, "Done");\r
95151aea 720\r
721 if (sel < opt_cnt) {\r
bc0420cd 722 text_out16(30, 205, is_bind ? "Press a button to bind/unbind" : "Press B to define");\r
6589c840 723 text_out16(30, 225, "Select \"Done\" to exit");\r
95151aea 724 } else {\r
6589c840 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
95151aea 728 }\r
13059a60 729 menu_flip();\r
720ee7f6 730}\r
731\r
367b6f1f 732static void key_config_loop(const me_bind_action *opts, int opt_cnt, int player_idx)\r
720ee7f6 733{\r
bc0420cd 734 int sel = 0, menu_sel_max = opt_cnt;\r
735 int kc, dev_id, is_down, mkey, unbind;\r
720ee7f6 736\r
737 for (;;)\r
738 {\r
bc0420cd 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
95151aea 753 }\r
bc0420cd 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
720ee7f6 767 }\r
768 }\r
769}\r
770\r
6589c840 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
720ee7f6 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
e5d315a5 792 gp2x_pd_clone_buffer2();\r
c7a4ff64 793 menu_draw_selection(tl_x - 16, tl_y + menu_sel*10, 138);\r
720ee7f6 794\r
6589c840 795 me_draw(ctrlopt_entries, ctrlopt_entry_count, tl_x, tl_y, NULL, NULL);\r
720ee7f6 796\r
797 tl_x = 25;\r
6589c840 798 text_out16(tl_x, (y=130), "USB joys detected:");\r
720ee7f6 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
13059a60 802 text_out16(tl_x, (y+=10), "%i: %s", i+1, joyname);\r
720ee7f6 803 }\r
804 } else {\r
13059a60 805 text_out16(tl_x, (y+=10), "none");\r
720ee7f6 806 }\r
807\r
13059a60 808 menu_flip();\r
720ee7f6 809}\r
810\r
95151aea 811\r
e1cd546f 812// player2_flag, reserved, ?, ?,\r
813// ?, ?, fast forward, menu\r
95151aea 814// "NEXT SAVE SLOT", "PREV SAVE SLOT", "SWITCH RENDERER", "SAVE STATE",\r
815// "LOAD STATE", "VOLUME UP", "VOLUME DOWN", "DONE"\r
367b6f1f 816me_bind_action emuctrl_actions[] =\r
95151aea 817{\r
e1cd546f 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
95151aea 831};\r
832\r
720ee7f6 833static void kc_sel_loop(void)\r
834{\r
6589c840 835 int menu_sel = 5, menu_sel_max = 5;\r
720ee7f6 836 unsigned long inp = 0;\r
6589c840 837 menu_id selected_id;\r
720ee7f6 838\r
95151aea 839 while (1)\r
720ee7f6 840 {\r
841 draw_kc_sel(menu_sel);\r
6589c840 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
95151aea 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
6589c840 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
720ee7f6 856 default: return;\r
857 }\r
858 }\r
95151aea 859 if (inp & GP2X_X) return;\r
720ee7f6 860 }\r
861}\r
862\r
863\r
daf91588 864// --------- sega/mega cd options ----------\r
865\r
59633198 866menu_entry cdopt_entries[] =\r
daf91588 867{\r
0ae25549 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
36f6fd5a 872 { "CDDA audio", MB_ONOFF, MA_CDOPT_CDDA, &PicoOpt, 0x0800, 0, 0, 1, 1 },\r
0ae25549 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
59633198 879};\r
880\r
881#define CDOPT_ENTRY_COUNT (sizeof(cdopt_entries) / sizeof(cdopt_entries[0]))\r
30d5518a 882const int cdopt_entry_count = CDOPT_ENTRY_COUNT;\r
59633198 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
13059a60 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
59633198 900 case MA_CDOPT_READAHEAD:\r
901 if (PicoCDBuffers > 1) sprintf(ra_buff, "%5iK", PicoCDBuffers * 2);\r
902 else strcpy(ra_buff, " OFF");\r
13059a60 903 text_out16(x, y, "ReadAhead buffer %s", ra_buff);\r
59633198 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
5f9922e6 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
daf91588 917\r
e5d315a5 918 gp2x_pd_clone_buffer2();\r
919\r
c7a4ff64 920 menu_draw_selection(tl_x - 16, tl_y + menu_sel*10, 246);\r
daf91588 921\r
13059a60 922 me_draw(cdopt_entries, CDOPT_ENTRY_COUNT, tl_x, tl_y, menu_cdopt_cust_draw, bios_names);\r
daf91588 923\r
59633198 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
13059a60 928 text_out16(tl_x, 210, "Press start to test selected BIOS");\r
daf91588 929\r
13059a60 930 menu_flip();\r
daf91588 931}\r
932\r
933static void cd_menu_loop_options(void)\r
934{\r
59d0f042 935 static int menu_sel = 0;\r
936 int menu_sel_max = 10;\r
daf91588 937 unsigned long inp = 0;\r
59633198 938 struct bios_names_t bios_names;\r
939 menu_id selected_id;\r
940 char *bios, *p;\r
daf91588 941\r
f013066e 942 if (emu_findBios(4, &bios)) { // US\r
daf91588 943 for (p = bios+strlen(bios)-1; p > bios && *p != '/'; p--); p++;\r
59633198 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
daf91588 946\r
f013066e 947 if (emu_findBios(8, &bios)) { // EU\r
daf91588 948 for (p = bios+strlen(bios)-1; p > bios && *p != '/'; p--); p++;\r
59633198 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
daf91588 951\r
f013066e 952 if (emu_findBios(1, &bios)) { // JP\r
daf91588 953 for (p = bios+strlen(bios)-1; p > bios && *p != '/'; p--); p++;\r
59633198 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
daf91588 956\r
957 for(;;)\r
958 {\r
59633198 959 draw_cd_menu_options(menu_sel, &bios_names);\r
daf91588 960 inp = wait_for_input(GP2X_UP|GP2X_DOWN|GP2X_LEFT|GP2X_RIGHT|GP2X_B|GP2X_X|GP2X_A|GP2X_START);\r
59633198 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
88bfc63d 969 if (PicoCDBuffers < 2) PicoCDBuffers = 0;\r
59633198 970 } else {\r
88bfc63d 971 if (PicoCDBuffers < 2) PicoCDBuffers = 2;\r
59633198 972 else PicoCDBuffers <<= 1;\r
973 if (PicoCDBuffers > 8*1024) PicoCDBuffers = 8*1024; // 16M\r
974 }\r
daf91588 975 }\r
976 }\r
59633198 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
f013066e 986 if (emu_findBios(4, &bios)) { // test US\r
daf91588 987 strcpy(romFileName, bios);\r
988 engineState = PGS_ReloadRom;\r
989 return;\r
990 }\r
991 break;\r
59633198 992 case MA_CDOPT_TESTBIOS_EUR:\r
f013066e 993 if (emu_findBios(8, &bios)) { // test EU\r
daf91588 994 strcpy(romFileName, bios);\r
995 engineState = PGS_ReloadRom;\r
996 return;\r
997 }\r
998 break;\r
59633198 999 case MA_CDOPT_TESTBIOS_JAP:\r
f013066e 1000 if (emu_findBios(1, &bios)) { // test JP\r
daf91588 1001 strcpy(romFileName, bios);\r
1002 engineState = PGS_ReloadRom;\r
1003 return;\r
1004 }\r
1005 break;\r
59633198 1006 default:\r
1007 break;\r
daf91588 1008 }\r
1009 }\r
59633198 1010 if (inp & (GP2X_X|GP2X_A)) return;\r
daf91588 1011 }\r
1012}\r
1013\r
1014\r
1015// --------- advanced options ----------\r
1016\r
59633198 1017menu_entry opt2_entries[] =\r
1018{\r
0ae25549 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
30d5518a 1022 { "Disable sprite limit", MB_ONOFF, MA_OPT2_NO_SPRITE_LIM, &PicoOpt, 0x40000, 0, 0, 1, 1 },\r
367b6f1f 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
0ae25549 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
367b6f1f 1031 { "SVP dynarec", MB_ONOFF, MA_OPT2_SVP_DYNAREC, &PicoOpt, 0x20000, 0, 0, 1, 1 },\r
260f1bcc 1032 { "Disable idle loop patching",MB_ONOFF, MA_OPT2_NO_IDLE_LOOPS, &PicoOpt, 0x80000, 0, 0, 1, 1 },\r
0ae25549 1033 { "done", MB_NONE, MA_OPT2_DONE, NULL, 0, 0, 0, 1, 0 },\r
59633198 1034};\r
1035\r
1036#define OPT2_ENTRY_COUNT (sizeof(opt2_entries) / sizeof(opt2_entries[0]))\r
30d5518a 1037const int opt2_entry_count = OPT2_ENTRY_COUNT;\r
59633198 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
13059a60 1042 text_out16(x, y, "Gamma correction %i.%02i", currentConfig.gamma / 100, currentConfig.gamma%100);\r
59633198 1043 else if (entry->id == MA_OPT2_SQUIDGEHACK)\r
0ae25549 1044 text_out16(x, y, "Squidgehack (now %s %s", mmuhack_status ? "active) " : "inactive)",\r
59633198 1045 (currentConfig.EmuOpt&0x0010)?"ON":"OFF");\r
1046}\r
1047\r
1048\r
720ee7f6 1049static void draw_amenu_options(int menu_sel)\r
1050{\r
59633198 1051 int tl_x = 25, tl_y = 50;\r
720ee7f6 1052\r
e5d315a5 1053 gp2x_pd_clone_buffer2();\r
1054\r
c7a4ff64 1055 menu_draw_selection(tl_x - 16, tl_y + menu_sel*10, 252);\r
720ee7f6 1056\r
13059a60 1057 me_draw(opt2_entries, OPT2_ENTRY_COUNT, tl_x, tl_y, menu_opt2_cust_draw, NULL);\r
720ee7f6 1058\r
13059a60 1059 menu_flip();\r
720ee7f6 1060}\r
1061\r
1062static void amenu_loop_options(void)\r
1063{\r
59d0f042 1064 static int menu_sel = 0;\r
59633198 1065 int menu_sel_max;\r
720ee7f6 1066 unsigned long inp = 0;\r
59633198 1067 menu_id selected_id;\r
1068\r
1069 menu_sel_max = me_count_enabled(opt2_entries, OPT2_ENTRY_COUNT) - 1;\r
720ee7f6 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
59633198 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
720ee7f6 1088 }\r
1089 }\r
59633198 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
720ee7f6 1094 }\r
1095 }\r
59633198 1096 if (inp & (GP2X_X|GP2X_A)) return;\r
720ee7f6 1097 }\r
1098}\r
1099\r
1100// -------------- options --------------\r
1101\r
59633198 1102\r
1103menu_entry opt_entries[] =\r
1104{\r
0ae25549 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
8a091e48 1107 { "Accurate sprites", MB_ONOFF, MA_OPT_ACC_SPRITES, &PicoOpt, 0x080, 0, 0, 0, 1 },\r
0ae25549 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
0ae25549 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
59633198 1123};\r
1124\r
1125#define OPT_ENTRY_COUNT (sizeof(opt_entries) / sizeof(opt_entries[0]))\r
0ae25549 1126const int opt_entry_count = OPT_ENTRY_COUNT;\r
720ee7f6 1127\r
59633198 1128\r
1129static void menu_opt_cust_draw(const menu_entry *entry, int x, int y, void *param)\r
720ee7f6 1130{\r
59633198 1131 char *str, str24[24];\r
1132\r
1133 switch (entry->id)\r
1134 {\r
1135 case MA_OPT_RENDERER:\r
dd5fd477 1136 if (PicoOpt & POPT_ALT_RENDERER)\r
59633198 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
13059a60 1142 text_out16(x, y, "Renderer: %s", str);\r
59633198 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
13059a60 1151 text_out16(x, y, "Scaling: %s", str);\r
59633198 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
13059a60 1157 text_out16(x, y, "Frameskip %s", str24);\r
59633198 1158 break;\r
1159 case MA_OPT_SOUND_QUALITY:\r
dd5fd477 1160 str = (PicoOpt & POPT_EN_STEREO) ? "stereo" : "mono";\r
0ae25549 1161 text_out16(x, y, "Sound Quality: %5iHz %s", PsndRate, str);\r
59633198 1162 break;\r
1163 case MA_OPT_REGION:\r
0ae25549 1164 text_out16(x, y, "Region: %s", me_region_name(PicoRegionOverride, PicoAutoRgnOrder));\r
59633198 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
13059a60 1173 text_out16(x, y, "Confirm savestate %s", str);\r
59633198 1174 break;\r
1175 case MA_OPT_CPU_CLOCKS:\r
13059a60 1176 text_out16(x, y, "GP2X CPU clocks %iMhz", currentConfig.CPUclock);\r
59633198 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
13059a60 1181 text_out16(x, y, "Save cfg as default%s", str24);\r
59633198 1182 break;\r
1183 case MA_OPT_LOADCFG:\r
13059a60 1184 text_out16(x, y, "Load cfg from profile %i", config_slot);\r
59633198 1185 break;\r
1186 default:\r
1187 printf("%s: unimplemented (%i)\n", __FUNCTION__, entry->id);\r
1188 break;\r
46ede6a6 1189 }\r
59633198 1190}\r
1191\r
1192\r
1193\r
1194static void draw_menu_options(int menu_sel)\r
1195{\r
13059a60 1196 int tl_x = 25, tl_y = 24;\r
720ee7f6 1197\r
e5d315a5 1198 gp2x_pd_clone_buffer2();\r
1199\r
c7a4ff64 1200 menu_draw_selection(tl_x - 16, tl_y + menu_sel*10, 284);\r
720ee7f6 1201\r
13059a60 1202 me_draw(opt_entries, OPT_ENTRY_COUNT, tl_x, tl_y, menu_opt_cust_draw, NULL);\r
720ee7f6 1203\r
13059a60 1204 menu_flip();\r
720ee7f6 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
979ba09f 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
0ae25549 1226 if (!PicoRegionOverride) {\r
979ba09f 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
0ae25549 1230 else PicoRegionOverride=1;\r
979ba09f 1231 }\r
0ae25549 1232 else PicoRegionOverride<<=1;\r
1233 if (PicoRegionOverride > 8) PicoRegionOverride = 8;\r
979ba09f 1234 } else {\r
0ae25549 1235 if (!PicoRegionOverride) {\r
979ba09f 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
0ae25549 1240 else PicoRegionOverride>>=1;\r
979ba09f 1241 }\r
1242}\r
1243\r
720ee7f6 1244static void menu_options_save(void)\r
1245{\r
426ecc58 1246 if (PicoRegionOverride) {\r
1247 // force setting possibly changed..\r
1248 Pico.m.pal = (PicoRegionOverride == 2 || PicoRegionOverride == 8) ? 1 : 0;\r
1249 }\r
dd5fd477 1250 if (!(PicoOpt & POPT_6BTN_PAD)) {\r
95151aea 1251 // unbind XYZ MODE, just in case\r
e67f6454 1252 unbind_action(0xf00, -1, -1);\r
720ee7f6 1253 }\r
1254}\r
1255\r
daf91588 1256static int menu_loop_options(void)\r
720ee7f6 1257{\r
59d0f042 1258 static int menu_sel = 0;\r
59633198 1259 int menu_sel_max, ret;\r
720ee7f6 1260 unsigned long inp = 0;\r
59633198 1261 menu_id selected_id;\r
720ee7f6 1262\r
144a28a0 1263 me_enable(opt_entries, OPT_ENTRY_COUNT, MA_OPT_SAVECFG_GAME, rom_loaded);\r
59633198 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
95151aea 1266 if (menu_sel > menu_sel_max) menu_sel = menu_sel_max;\r
59633198 1267\r
1268 while (1)\r
720ee7f6 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
59633198 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
0ae25549 1275 if (inp & (GP2X_LEFT|GP2X_RIGHT)) { // multi choice\r
59633198 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
0ae25549 1280 if (PicoOpt&0x10) PicoOpt&= ~0x10;\r
59633198 1281 else if (!(currentConfig.EmuOpt &0x80))currentConfig.EmuOpt |= 0x80;\r
1282 else if ( currentConfig.EmuOpt &0x80) break;\r
1283 } else {\r
0ae25549 1284 if (PicoOpt&0x10) break;\r
1285 else if (!(currentConfig.EmuOpt &0x80))PicoOpt|= 0x10;\r
59633198 1286 else if ( currentConfig.EmuOpt &0x80) currentConfig.EmuOpt &= ~0x80;\r
1287 }\r
1288 break;\r
1289 case MA_OPT_SOUND_QUALITY:\r
0ae25549 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
59633198 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
95151aea 1362 ret = emu_ReadConfig(1, 1);\r
1363 if (!ret) ret = emu_ReadConfig(0, 1);\r
1364 if (ret) strcpy(menuErrorMsg, "config loaded");\r
59633198 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
720ee7f6 1371 }\r
1372 }\r
979ba09f 1373 if(inp & (GP2X_X|GP2X_A)) {\r
720ee7f6 1374 menu_options_save();\r
daf91588 1375 return 0; // done (update, no write)\r
720ee7f6 1376 }\r
720ee7f6 1377 }\r
1378}\r
1379\r
1380// -------------- credits --------------\r
1381\r
1382static void draw_menu_credits(void)\r
1383{\r
260f1bcc 1384 int tl_x = 15, tl_y = 56, y;\r
e5d315a5 1385 gp2x_pd_clone_buffer2();\r
720ee7f6 1386\r
30d5518a 1387 text_out16(tl_x, 20, "PicoDrive v" VERSION " (c) notaz, 2006-2008");\r
720ee7f6 1388 y = tl_y;\r
13059a60 1389 text_out16(tl_x, y, "Credits:");\r
4b8f4f3c 1390 text_out16(tl_x, (y+=10), "fDave: Cyclone 68000 core,");\r
13059a60 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
13059a60 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
260f1bcc 1397 text_out16(tl_x, (y+=10), "GnoStiC / Puck2099: USB joy code");\r
13059a60 1398 text_out16(tl_x, (y+=10), "craigix: GP2X hardware");\r
f9a37a02 1399 text_out16(tl_x, (y+=10), "ketchupgun: skin design");\r
13059a60 1400\r
7a938d57 1401 text_out16(tl_x, (y+=20), "special thanks (for docs, ideas):");\r
260f1bcc 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
13059a60 1407 menu_flip();\r
720ee7f6 1408}\r
1409\r
1410\r
1411// -------------- root menu --------------\r
1412\r
59633198 1413menu_entry main_entries[] =\r
1414{\r
0ae25549 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
59633198 1425};\r
1426\r
1427#define MAIN_ENTRY_COUNT (sizeof(main_entries) / sizeof(main_entries[0]))\r
1428\r
720ee7f6 1429static void draw_menu_root(int menu_sel)\r
1430{\r
59633198 1431 const int tl_x = 70, tl_y = 70;\r
1432\r
e5d315a5 1433 gp2x_pd_clone_buffer2();\r
720ee7f6 1434\r
13059a60 1435 text_out16(tl_x, 20, "PicoDrive v" VERSION);\r
1436\r
c7a4ff64 1437 menu_draw_selection(tl_x - 16, tl_y + menu_sel*10, 146);\r
720ee7f6 1438\r
59633198 1439 me_draw(main_entries, MAIN_ENTRY_COUNT, tl_x, tl_y, NULL, NULL);\r
720ee7f6 1440\r
720ee7f6 1441 // error\r
13059a60 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
720ee7f6 1447}\r
1448\r
1449\r
1450static void menu_loop_root(void)\r
1451{\r
59633198 1452 static int menu_sel = 0;\r
1453 int ret, menu_sel_max;\r
720ee7f6 1454 unsigned long inp = 0;\r
720ee7f6 1455\r
144a28a0 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
59633198 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
95151aea 1463 if (menu_sel > menu_sel_max) menu_sel = menu_sel_max;\r
720ee7f6 1464\r
79cad122 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
720ee7f6 1470 {\r
1471 draw_menu_root(menu_sel);\r
0ae6813e 1472 inp = wait_for_input(GP2X_UP|GP2X_DOWN|GP2X_B|GP2X_X|GP2X_SELECT|GP2X_L|GP2X_R);\r
59633198 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
0ae6813e 1475 if((inp & (GP2X_L|GP2X_R)) == (GP2X_L|GP2X_R)) debug_menu_loop();\r
720ee7f6 1476 if(inp &(GP2X_SELECT|GP2X_X)){\r
144a28a0 1477 if (rom_loaded) {\r
720ee7f6 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
59633198 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
144a28a0 1487 if (rom_loaded) {\r
5e2e14f2 1488 while (gp2x_joystick_read(1) & GP2X_B) usleep(50*1000);\r
1489 engineState = PGS_Running;\r
1490 return;\r
1491 }\r
720ee7f6 1492 break;\r
59633198 1493 case MA_MAIN_SAVE_STATE:\r
144a28a0 1494 if (rom_loaded) {\r
4ffd2858 1495 if(savestate_menu_loop(0))\r
720ee7f6 1496 continue;\r
720ee7f6 1497 engineState = PGS_Running;\r
1498 return;\r
1499 }\r
1500 break;\r
59633198 1501 case MA_MAIN_LOAD_STATE:\r
144a28a0 1502 if (rom_loaded) {\r
4ffd2858 1503 if(savestate_menu_loop(1))\r
720ee7f6 1504 continue;\r
446b090c 1505 while (gp2x_joystick_read(1) & GP2X_B) usleep(50*1000);\r
720ee7f6 1506 engineState = PGS_Running;\r
1507 return;\r
1508 }\r
1509 break;\r
59633198 1510 case MA_MAIN_RESET_GAME:\r
144a28a0 1511 if (rom_loaded) {\r
720ee7f6 1512 emu_ResetGame();\r
446b090c 1513 while (gp2x_joystick_read(1) & GP2X_B) usleep(50*1000);\r
720ee7f6 1514 engineState = PGS_Running;\r
1515 return;\r
1516 }\r
1517 break;\r
59633198 1518 case MA_MAIN_LOAD_ROM:\r
13059a60 1519 {\r
1520 char curr_path[PATH_MAX], *selfname;\r
1521 FILE *tstf;\r
36f6fd5a 1522 if ( (tstf = fopen(loadedRomFName, "rb")) )\r
13059a60 1523 {\r
1524 fclose(tstf);\r
36f6fd5a 1525 strcpy(curr_path, loadedRomFName);\r
13059a60 1526 }\r
1527 else\r
1528 getcwd(curr_path, PATH_MAX);\r
720ee7f6 1529 selfname = romsel_loop(curr_path);\r
1530 if (selfname) {\r
1531 printf("selected file: %s\n", selfname);\r
720ee7f6 1532 engineState = PGS_ReloadRom;\r
13059a60 1533 return;\r
720ee7f6 1534 }\r
13059a60 1535 break;\r
1536 }\r
59633198 1537 case MA_MAIN_OPTIONS:\r
daf91588 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
720ee7f6 1542 break;\r
59633198 1543 case MA_MAIN_CONTROLS:\r
720ee7f6 1544 kc_sel_loop();\r
1545 break;\r
59633198 1546 case MA_MAIN_CREDITS:\r
720ee7f6 1547 draw_menu_credits();\r
1548 usleep(500*1000);\r
1549 inp = wait_for_input(GP2X_B|GP2X_X);\r
1550 break;\r
59633198 1551 case MA_MAIN_EXIT:\r
720ee7f6 1552 engineState = PGS_Quit;\r
1553 return;\r
59633198 1554 case MA_MAIN_PATCHES:\r
144a28a0 1555 if (rom_loaded && PicoPatches) {\r
70d2ecc5 1556 patches_menu_loop();\r
1557 PicoPatchApply();\r
1558 strcpy(menuErrorMsg, "Patches applied");\r
1559 continue;\r
1560 }\r
1561 break;\r
59633198 1562 default:\r
1563 printf("%s: something unknown selected\n", __FUNCTION__);\r
1564 break;\r
720ee7f6 1565 }\r
1566 }\r
1567 menuErrorMsg[0] = 0; // clear error msg\r
1568 }\r
1569}\r
1570\r
b8464531 1571void menu_darken_bg(void *dst, int pixels, int darker)\r
720ee7f6 1572{\r
13059a60 1573 unsigned int *screen = dst;\r
1574 pixels /= 2;\r
f9a37a02 1575 if (darker)\r
13059a60 1576 {\r
f9a37a02 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
13059a60 1590 }\r
1591}\r
e5d315a5 1592\r
13059a60 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
f9a37a02 1599 menu_darken_bg((char *)gp2x_screen + 320*8*2, 320*224, 1);\r
13059a60 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
4ffd2858 1606 }\r
720ee7f6 1607\r
13059a60 1608 // copy to buffer2\r
1609 gp2x_memcpy_buffers((1<<2), gp2x_screen, 0, 320*240*2);\r
4ffd2858 1610}\r
1611\r
1612static void menu_gfx_prepare(void)\r
1613{\r
144a28a0 1614 menu_prepare_bg(rom_loaded);\r
4ffd2858 1615\r
13059a60 1616 // switch to 16bpp\r
1617 gp2x_video_changemode2(16);\r
79cad122 1618 gp2x_video_RGB_setscaling(0, 320, 240);\r
13059a60 1619 menu_flip();\r
e5d315a5 1620}\r
1621\r
1622\r
1623void menu_loop(void)\r
1624{\r
bc0420cd 1625 in_set_blocking(1);\r
e5d315a5 1626 menu_gfx_prepare();\r
720ee7f6 1627\r
1628 menu_loop_root();\r
1629\r
bc0420cd 1630 in_set_blocking(0);\r
720ee7f6 1631 menuErrorMsg[0] = 0;\r
1632}\r
5e2e14f2 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
13059a60 1640 memset(gp2x_screen, 0, 320*240*2);\r
5e2e14f2 1641\r
13059a60 1642 text_out16(tl_x, 20, "The unit is about to");\r
1643 text_out16(tl_x, 30, "close the CD tray.");\r
5e2e14f2 1644\r
1645 y = tl_y;\r
13059a60 1646 text_out16(tl_x, y, "Load new CD image");\r
1647 text_out16(tl_x, (y+=10), "Insert nothing");\r
5e2e14f2 1648\r
1649 // draw cursor\r
13059a60 1650 text_out16(tl_x - 16, tl_y + menu_sel*10, ">");\r
5e2e14f2 1651 // error\r
13059a60 1652 if (menuErrorMsg[0]) text_out16(5, 226, menuErrorMsg);\r
1653 menu_flip();\r
5e2e14f2 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
c7a4ff64 1664 gp2x_memset_all_buffers(0, 0, 320*240*2);\r
5e2e14f2 1665 menu_gfx_prepare();\r
1666\r
36f6fd5a 1667 if ( (tstf = fopen(loadedRomFName, "rb")) )\r
5e2e14f2 1668 {\r
1669 fclose(tstf);\r
36f6fd5a 1670 strcpy(curr_path, loadedRomFName);\r
5e2e14f2 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
1ceda417 1692 int ret = -1;\r
1693 cd_img_type cd_type;\r
36f6fd5a 1694 cd_type = emu_cdCheck(NULL, romFileName);\r
1ceda417 1695 if (cd_type != CIT_NOT_CD)\r
1696 ret = Insert_CD(romFileName, cd_type);\r
5e2e14f2 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