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