Cyclone poll detection problem fixed
[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
d524c827 841 { "Enter Menu ", 1<<23 },\r
842};\r
843\r
cc68a136 844static void kc_sel_loop(void)\r
845{\r
d524c827 846 int menu_sel = 3, menu_sel_max = 3;\r
cc68a136 847 unsigned long inp = 0;\r
d524c827 848 int is_6button = currentConfig.PicoOpt & 0x020;\r
cc68a136 849\r
d524c827 850 while (1)\r
cc68a136 851 {\r
852 draw_kc_sel(menu_sel);\r
853 inp = wait_for_input(GP2X_UP|GP2X_DOWN|GP2X_B|GP2X_X);\r
d524c827 854 if (inp & GP2X_UP ) { menu_sel--; if (menu_sel < 0) menu_sel = menu_sel_max; }\r
855 if (inp & GP2X_DOWN) { menu_sel++; if (menu_sel > menu_sel_max) menu_sel = 0; }\r
856 if (inp & GP2X_B) {\r
cc68a136 857 switch (menu_sel) {\r
d524c827 858 case 0: key_config_loop(ctrl_actions, is_6button ? 12 : 8, 0); return;\r
859 case 1: key_config_loop(ctrl_actions, is_6button ? 12 : 8, 1); return;\r
860 case 2: key_config_loop(emuctrl_actions,\r
861 sizeof(emuctrl_actions)/sizeof(emuctrl_actions[0]), -1); return;\r
862 case 3: if (rom_data == NULL) emu_WriteConfig(0); return;\r
cc68a136 863 default: return;\r
864 }\r
865 }\r
d524c827 866 if (inp & GP2X_X) return;\r
cc68a136 867 }\r
868}\r
869\r
870\r
bf098bc5 871// --------- sega/mega cd options ----------\r
872\r
4e8a534c 873menu_entry cdopt_entries[] =\r
bf098bc5 874{\r
4e8a534c 875 { NULL, MB_NONE, MA_CDOPT_TESTBIOS_USA, NULL, 0, 0, 0, 1 },\r
876 { NULL, MB_NONE, MA_CDOPT_TESTBIOS_EUR, NULL, 0, 0, 0, 1 },\r
877 { NULL, MB_NONE, MA_CDOPT_TESTBIOS_JAP, NULL, 0, 0, 0, 1 },\r
878 { "CD LEDs", MB_ONOFF, MA_CDOPT_LEDS, &currentConfig.EmuOpt, 0x0400, 0, 0, 1 },\r
879 { "CDDA audio (using mp3s)", MB_ONOFF, MA_CDOPT_CDDA, &currentConfig.PicoOpt, 0x0800, 0, 0, 1 },\r
880 { "PCM audio", MB_ONOFF, MA_CDOPT_PCM, &currentConfig.PicoOpt, 0x0400, 0, 0, 1 },\r
881 { NULL, MB_NONE, MA_CDOPT_READAHEAD, NULL, 0, 0, 0, 1 },\r
882 { "SaveRAM cart", MB_ONOFF, MA_CDOPT_SAVERAM, &currentConfig.PicoOpt, 0x8000, 0, 0, 1 },\r
883 { "Scale/Rot. fx (slow)", MB_ONOFF, MA_CDOPT_SCALEROT_CHIP,&currentConfig.PicoOpt, 0x1000, 0, 0, 1 },\r
884 { "Better sync (slow)", MB_ONOFF, MA_CDOPT_BETTER_SYNC, &currentConfig.PicoOpt, 0x2000, 0, 0, 1 },\r
885 { "done", MB_NONE, MA_CDOPT_DONE, NULL, 0, 0, 0, 1 },\r
886};\r
887\r
888#define CDOPT_ENTRY_COUNT (sizeof(cdopt_entries) / sizeof(cdopt_entries[0]))\r
889\r
890\r
891struct bios_names_t\r
892{\r
893 char us[32], eu[32], jp[32];\r
894};\r
895\r
896static void menu_cdopt_cust_draw(const menu_entry *entry, int x, int y, void *param)\r
897{\r
898 struct bios_names_t *bios_names = param;\r
899 char ra_buff[16];\r
900\r
901 switch (entry->id)\r
902 {\r
a12e0116 903 case MA_CDOPT_TESTBIOS_USA: text_out16(x, y, "USA BIOS: %s", bios_names->us); break;\r
904 case MA_CDOPT_TESTBIOS_EUR: text_out16(x, y, "EUR BIOS: %s", bios_names->eu); break;\r
905 case MA_CDOPT_TESTBIOS_JAP: text_out16(x, y, "JAP BIOS: %s", bios_names->jp); break;\r
4e8a534c 906 case MA_CDOPT_READAHEAD:\r
907 if (PicoCDBuffers > 1) sprintf(ra_buff, "%5iK", PicoCDBuffers * 2);\r
908 else strcpy(ra_buff, " OFF");\r
a12e0116 909 text_out16(x, y, "ReadAhead buffer %s", ra_buff);\r
4e8a534c 910 break;\r
911 default:break;\r
912 }\r
913}\r
914\r
915static void draw_cd_menu_options(int menu_sel, struct bios_names_t *bios_names)\r
916{\r
917 int tl_x = 25, tl_y = 60;\r
918 menu_id selected_id;\r
0a051f55 919 char ra_buff[16];\r
920\r
921 if (PicoCDBuffers > 1) sprintf(ra_buff, "%5iK", PicoCDBuffers * 2);\r
922 else strcpy(ra_buff, " OFF");\r
bf098bc5 923\r
e11c5548 924 gp2x_pd_clone_buffer2();\r
925\r
e5f426aa 926 menu_draw_selection(tl_x - 16, tl_y + menu_sel*10, 246);\r
bf098bc5 927\r
a12e0116 928 me_draw(cdopt_entries, CDOPT_ENTRY_COUNT, tl_x, tl_y, menu_cdopt_cust_draw, bios_names);\r
bf098bc5 929\r
4e8a534c 930 selected_id = me_index2id(cdopt_entries, CDOPT_ENTRY_COUNT, menu_sel);\r
931 if ((selected_id == MA_CDOPT_TESTBIOS_USA && strcmp(bios_names->us, "NOT FOUND")) ||\r
932 (selected_id == MA_CDOPT_TESTBIOS_EUR && strcmp(bios_names->eu, "NOT FOUND")) ||\r
933 (selected_id == MA_CDOPT_TESTBIOS_JAP && strcmp(bios_names->jp, "NOT FOUND")))\r
a12e0116 934 text_out16(tl_x, 210, "Press start to test selected BIOS");\r
bf098bc5 935\r
a12e0116 936 menu_flip();\r
bf098bc5 937}\r
938\r
939static void cd_menu_loop_options(void)\r
940{\r
aae35e84 941 static int menu_sel = 0;\r
942 int menu_sel_max = 10;\r
bf098bc5 943 unsigned long inp = 0;\r
4e8a534c 944 struct bios_names_t bios_names;\r
945 menu_id selected_id;\r
946 char *bios, *p;\r
bf098bc5 947\r
ea8c405f 948 if (emu_findBios(4, &bios)) { // US\r
bf098bc5 949 for (p = bios+strlen(bios)-1; p > bios && *p != '/'; p--); p++;\r
4e8a534c 950 strncpy(bios_names.us, p, sizeof(bios_names.us)); bios_names.us[sizeof(bios_names.us)-1] = 0;\r
951 } else strcpy(bios_names.us, "NOT FOUND");\r
bf098bc5 952\r
ea8c405f 953 if (emu_findBios(8, &bios)) { // EU\r
bf098bc5 954 for (p = bios+strlen(bios)-1; p > bios && *p != '/'; p--); p++;\r
4e8a534c 955 strncpy(bios_names.eu, p, sizeof(bios_names.eu)); bios_names.eu[sizeof(bios_names.eu)-1] = 0;\r
956 } else strcpy(bios_names.eu, "NOT FOUND");\r
bf098bc5 957\r
ea8c405f 958 if (emu_findBios(1, &bios)) { // JP\r
bf098bc5 959 for (p = bios+strlen(bios)-1; p > bios && *p != '/'; p--); p++;\r
4e8a534c 960 strncpy(bios_names.jp, p, sizeof(bios_names.jp)); bios_names.jp[sizeof(bios_names.jp)-1] = 0;\r
961 } else strcpy(bios_names.jp, "NOT FOUND");\r
bf098bc5 962\r
963 for(;;)\r
964 {\r
4e8a534c 965 draw_cd_menu_options(menu_sel, &bios_names);\r
bf098bc5 966 inp = wait_for_input(GP2X_UP|GP2X_DOWN|GP2X_LEFT|GP2X_RIGHT|GP2X_B|GP2X_X|GP2X_A|GP2X_START);\r
4e8a534c 967 if (inp & GP2X_UP ) { menu_sel--; if (menu_sel < 0) menu_sel = menu_sel_max; }\r
968 if (inp & GP2X_DOWN) { menu_sel++; if (menu_sel > menu_sel_max) menu_sel = 0; }\r
969 selected_id = me_index2id(cdopt_entries, CDOPT_ENTRY_COUNT, menu_sel);\r
970 if (inp & (GP2X_LEFT|GP2X_RIGHT)) { // multi choise\r
971 if (!me_process(cdopt_entries, CDOPT_ENTRY_COUNT, selected_id, (inp&GP2X_RIGHT) ? 1 : 0) &&\r
972 selected_id == MA_CDOPT_READAHEAD) {\r
973 if (inp & GP2X_LEFT) {\r
974 PicoCDBuffers >>= 1;\r
975 if (PicoCDBuffers < 64) PicoCDBuffers = 0;\r
976 } else {\r
977 if (PicoCDBuffers < 64) PicoCDBuffers = 64;\r
978 else PicoCDBuffers <<= 1;\r
979 if (PicoCDBuffers > 8*1024) PicoCDBuffers = 8*1024; // 16M\r
980 }\r
bf098bc5 981 }\r
982 }\r
4e8a534c 983 if (inp & GP2X_B) { // toggleable options\r
984 if (!me_process(cdopt_entries, CDOPT_ENTRY_COUNT, selected_id, 1) &&\r
985 selected_id == MA_CDOPT_DONE) {\r
986 return;\r
987 }\r
988 }\r
989 if (inp & GP2X_START) { // BIOS testers\r
990 switch (selected_id) {\r
991 case MA_CDOPT_TESTBIOS_USA:\r
ea8c405f 992 if (emu_findBios(4, &bios)) { // test US\r
bf098bc5 993 strcpy(romFileName, bios);\r
994 engineState = PGS_ReloadRom;\r
995 return;\r
996 }\r
997 break;\r
4e8a534c 998 case MA_CDOPT_TESTBIOS_EUR:\r
ea8c405f 999 if (emu_findBios(8, &bios)) { // test EU\r
bf098bc5 1000 strcpy(romFileName, bios);\r
1001 engineState = PGS_ReloadRom;\r
1002 return;\r
1003 }\r
1004 break;\r
4e8a534c 1005 case MA_CDOPT_TESTBIOS_JAP:\r
ea8c405f 1006 if (emu_findBios(1, &bios)) { // test JP\r
bf098bc5 1007 strcpy(romFileName, bios);\r
1008 engineState = PGS_ReloadRom;\r
1009 return;\r
1010 }\r
1011 break;\r
4e8a534c 1012 default:\r
1013 break;\r
bf098bc5 1014 }\r
1015 }\r
4e8a534c 1016 if (inp & (GP2X_X|GP2X_A)) return;\r
bf098bc5 1017 }\r
1018}\r
1019\r
1020\r
1021// --------- advanced options ----------\r
1022\r
4e8a534c 1023menu_entry opt2_entries[] =\r
1024{\r
1025 { NULL, MB_NONE, MA_OPT2_GAMMA, NULL, 0, 0, 0, 1 },\r
1026 { "A_SN's gamma curve", MB_ONOFF, MA_OPT2_A_SN_GAMMA, &currentConfig.EmuOpt, 0x1000, 0, 0, 1 },\r
e5f426aa 1027 { "Perfect vsync", MB_ONOFF, MA_OPT2_VSYNC, &currentConfig.EmuOpt, 0x2000, 0, 0, 1 },\r
4e8a534c 1028 { "Emulate Z80", MB_ONOFF, MA_OPT2_ENABLE_Z80, &currentConfig.PicoOpt,0x0004, 0, 0, 1 },\r
1029 { "Emulate YM2612 (FM)", MB_ONOFF, MA_OPT2_ENABLE_YM2612, &currentConfig.PicoOpt,0x0001, 0, 0, 1 },\r
1030 { "Emulate SN76496 (PSG)", MB_ONOFF, MA_OPT2_ENABLE_SN76496,&currentConfig.PicoOpt,0x0002, 0, 0, 1 },\r
1031 { "gzip savestates", MB_ONOFF, MA_OPT2_GZIP_STATES, &currentConfig.EmuOpt, 0x0008, 0, 0, 1 },\r
1032 { "Don't save last used ROM", MB_ONOFF, MA_OPT2_NO_LAST_ROM, &currentConfig.EmuOpt, 0x0020, 0, 0, 1 },\r
1033 { "needs restart:", MB_NONE, MA_NONE, NULL, 0, 0, 0, 1 },\r
1034 { "craigix's RAM timings", MB_ONOFF, MA_OPT2_RAMTIMINGS, &currentConfig.EmuOpt, 0x0100, 0, 0, 1 },\r
1035 { NULL, MB_ONOFF, MA_OPT2_SQUIDGEHACK, &currentConfig.EmuOpt, 0x0010, 0, 0, 1 },\r
1036 { "done", MB_NONE, MA_OPT2_DONE, NULL, 0, 0, 0, 1 },\r
1037};\r
1038\r
1039#define OPT2_ENTRY_COUNT (sizeof(opt2_entries) / sizeof(opt2_entries[0]))\r
1040\r
1041static void menu_opt2_cust_draw(const menu_entry *entry, int x, int y, void *param)\r
1042{\r
1043 if (entry->id == MA_OPT2_GAMMA)\r
a12e0116 1044 text_out16(x, y, "Gamma correction %i.%02i", currentConfig.gamma / 100, currentConfig.gamma%100);\r
4e8a534c 1045 else if (entry->id == MA_OPT2_SQUIDGEHACK)\r
a12e0116 1046 text_out16(x, y, "squidgehack (now %s %s", mmuhack_status ? "active) " : "inactive)",\r
4e8a534c 1047 (currentConfig.EmuOpt&0x0010)?"ON":"OFF");\r
1048}\r
1049\r
1050\r
cc68a136 1051static void draw_amenu_options(int menu_sel)\r
1052{\r
4e8a534c 1053 int tl_x = 25, tl_y = 50;\r
cc68a136 1054\r
e11c5548 1055 gp2x_pd_clone_buffer2();\r
1056\r
e5f426aa 1057 menu_draw_selection(tl_x - 16, tl_y + menu_sel*10, 252);\r
cc68a136 1058\r
a12e0116 1059 me_draw(opt2_entries, OPT2_ENTRY_COUNT, tl_x, tl_y, menu_opt2_cust_draw, NULL);\r
cc68a136 1060\r
a12e0116 1061 menu_flip();\r
cc68a136 1062}\r
1063\r
1064static void amenu_loop_options(void)\r
1065{\r
aae35e84 1066 static int menu_sel = 0;\r
4e8a534c 1067 int menu_sel_max;\r
cc68a136 1068 unsigned long inp = 0;\r
4e8a534c 1069 menu_id selected_id;\r
1070\r
1071 menu_sel_max = me_count_enabled(opt2_entries, OPT2_ENTRY_COUNT) - 1;\r
cc68a136 1072\r
1073 for(;;)\r
1074 {\r
1075 draw_amenu_options(menu_sel);\r
1076 inp = wait_for_input(GP2X_UP|GP2X_DOWN|GP2X_LEFT|GP2X_RIGHT|GP2X_B|GP2X_X|GP2X_A);\r
4e8a534c 1077 if (inp & GP2X_UP ) { menu_sel--; if (menu_sel < 0) menu_sel = menu_sel_max; }\r
1078 if (inp & GP2X_DOWN) { menu_sel++; if (menu_sel > menu_sel_max) menu_sel = 0; }\r
1079 selected_id = me_index2id(opt2_entries, OPT2_ENTRY_COUNT, menu_sel);\r
1080 if (inp & (GP2X_LEFT|GP2X_RIGHT)) { // multi choise\r
1081 if (!me_process(opt2_entries, OPT2_ENTRY_COUNT, selected_id, (inp&GP2X_RIGHT) ? 1 : 0) &&\r
1082 selected_id == MA_OPT2_GAMMA) {\r
1083 while ((inp = gp2x_joystick_read(1)) & (GP2X_LEFT|GP2X_RIGHT)) {\r
1084 currentConfig.gamma += (inp & GP2X_LEFT) ? -1 : 1;\r
1085 if (currentConfig.gamma < 1) currentConfig.gamma = 1;\r
1086 if (currentConfig.gamma > 300) currentConfig.gamma = 300;\r
1087 draw_amenu_options(menu_sel);\r
1088 usleep(18*1000);\r
1089 }\r
cc68a136 1090 }\r
1091 }\r
4e8a534c 1092 if (inp & GP2X_B) { // toggleable options\r
1093 if (!me_process(opt2_entries, OPT2_ENTRY_COUNT, selected_id, 1) &&\r
1094 selected_id == MA_OPT2_DONE) {\r
1095 return;\r
cc68a136 1096 }\r
1097 }\r
4e8a534c 1098 if (inp & (GP2X_X|GP2X_A)) return;\r
cc68a136 1099 }\r
1100}\r
1101\r
1102// -------------- options --------------\r
1103\r
4e8a534c 1104\r
1105menu_entry opt_entries[] =\r
1106{\r
1107 { NULL, MB_NONE, MA_OPT_RENDERER, NULL, 0, 0, 0, 1 },\r
1108 { NULL, MB_RANGE, MA_OPT_SCALING, &currentConfig.scaling, 0, 0, 3, 1 },\r
1109 { "Accurate timing (slower)", MB_ONOFF, MA_OPT_ACC_TIMING, &currentConfig.PicoOpt, 0x040, 0, 0, 1 },\r
1110 { "Accurate sprites (slower)", MB_ONOFF, MA_OPT_ACC_SPRITES, &currentConfig.PicoOpt, 0x080, 0, 0, 1 },\r
1111 { "Show FPS", MB_ONOFF, MA_OPT_SHOW_FPS, &currentConfig.EmuOpt, 0x002, 0, 0, 1 },\r
1112 { NULL, MB_RANGE, MA_OPT_FRAMESKIP, &currentConfig.Frameskip, 0, -1, 16, 1 },\r
1113 { "Enable sound", MB_ONOFF, MA_OPT_ENABLE_SOUND, &currentConfig.EmuOpt, 0x004, 0, 0, 1 },\r
1114 { NULL, MB_NONE, MA_OPT_SOUND_QUALITY, NULL, 0, 0, 0, 1 },\r
1115 { "Use ARM940 core for sound", MB_ONOFF, MA_OPT_ARM940_SOUND, &currentConfig.PicoOpt, 0x200, 0, 0, 1 },\r
1116 { "6 button pad", MB_ONOFF, MA_OPT_6BUTTON_PAD, &currentConfig.PicoOpt, 0x020, 0, 0, 1 },\r
1117 { NULL, MB_NONE, MA_OPT_REGION, NULL, 0, 0, 0, 1 },\r
1118 { "Use SRAM/BRAM savestates", MB_ONOFF, MA_OPT_SRAM_STATES, &currentConfig.EmuOpt, 0x001, 0, 0, 1 },\r
1119 { NULL, MB_NONE, MA_OPT_CONFIRM_STATES,NULL, 0, 0, 0, 1 },\r
1120 { "Save slot", MB_RANGE, MA_OPT_SAVE_SLOT, &state_slot, 0, 0, 9, 1 },\r
1121 { NULL, MB_NONE, MA_OPT_CPU_CLOCKS, NULL, 0, 0, 0, 1 },\r
1122 { "[Sega/Mega CD options]", MB_NONE, MA_OPT_SCD_OPTS, NULL, 0, 0, 0, 1 },\r
1123 { "[advanced options]", MB_NONE, MA_OPT_ADV_OPTS, NULL, 0, 0, 0, 1 },\r
1124 { NULL, MB_NONE, MA_OPT_SAVECFG, NULL, 0, 0, 0, 1 },\r
1125 { "Save cfg for current game only",MB_NONE,MA_OPT_SAVECFG_GAME,NULL, 0, 0, 0, 1 },\r
1126 { NULL, MB_NONE, MA_OPT_LOADCFG, NULL, 0, 0, 0, 1 },\r
1127};\r
1128\r
1129#define OPT_ENTRY_COUNT (sizeof(opt_entries) / sizeof(opt_entries[0]))\r
1130\r
1131\r
51a902ae 1132static const char *region_name(unsigned int code)\r
cc68a136 1133{\r
51a902ae 1134 static const char *names[] = { "Auto", " Japan NTSC", " Japan PAL", " USA", " Europe" };\r
1135 static const char *names_short[] = { "", " JP", " JP", " US", " EU" };\r
1136 int u, i = 0;\r
1137 if (code) {\r
1138 code <<= 1;\r
1139 while((code >>= 1)) i++;\r
1140 if (i > 4) return "unknown";\r
1141 return names[i];\r
1142 } else {\r
1143 static char name[24];\r
1144 strcpy(name, "Auto:");\r
1145 for (u = 0; u < 3; u++) {\r
1146 i = 0; code = ((PicoAutoRgnOrder >> u*4) & 0xf) << 1;\r
1147 while((code >>= 1)) i++;\r
1148 strcat(name, names_short[i]);\r
1149 }\r
1150 return name;\r
1151 }\r
cc68a136 1152}\r
1153\r
4e8a534c 1154\r
1155static void menu_opt_cust_draw(const menu_entry *entry, int x, int y, void *param)\r
cc68a136 1156{\r
4e8a534c 1157 char *str, str24[24];\r
1158\r
1159 switch (entry->id)\r
1160 {\r
1161 case MA_OPT_RENDERER:\r
1162 if (currentConfig.PicoOpt&0x10)\r
1163 str = " 8bit fast";\r
1164 else if (currentConfig.EmuOpt&0x80)\r
1165 str = "16bit accurate";\r
1166 else\r
1167 str = " 8bit accurate";\r
a12e0116 1168 text_out16(x, y, "Renderer: %s", str);\r
4e8a534c 1169 break;\r
1170 case MA_OPT_SCALING:\r
1171 switch (currentConfig.scaling) {\r
1172 default: str = " OFF"; break;\r
1173 case 1: str = "hw horizontal"; break;\r
1174 case 2: str = "hw horiz. + vert."; break;\r
1175 case 3: str = "sw horizontal"; break;\r
1176 }\r
a12e0116 1177 text_out16(x, y, "Scaling: %s", str);\r
4e8a534c 1178 break;\r
1179 case MA_OPT_FRAMESKIP:\r
1180 if (currentConfig.Frameskip < 0)\r
1181 strcpy(str24, "Auto");\r
1182 else sprintf(str24, "%i", currentConfig.Frameskip);\r
a12e0116 1183 text_out16(x, y, "Frameskip %s", str24);\r
4e8a534c 1184 break;\r
1185 case MA_OPT_SOUND_QUALITY:\r
1186 str = (currentConfig.PicoOpt&0x08)?"stereo":"mono";\r
a12e0116 1187 text_out16(x, y, "Sound Quality: %5iHz %s", currentConfig.PsndRate, str);\r
4e8a534c 1188 break;\r
1189 case MA_OPT_REGION:\r
a12e0116 1190 text_out16(x, y, "Region: %s", region_name(currentConfig.PicoRegion));\r
4e8a534c 1191 break;\r
1192 case MA_OPT_CONFIRM_STATES:\r
1193 switch ((currentConfig.EmuOpt >> 9) & 5) {\r
1194 default: str = "OFF"; break;\r
1195 case 1: str = "writes"; break;\r
1196 case 4: str = "loads"; break;\r
1197 case 5: str = "both"; break;\r
1198 }\r
a12e0116 1199 text_out16(x, y, "Confirm savestate %s", str);\r
4e8a534c 1200 break;\r
1201 case MA_OPT_CPU_CLOCKS:\r
a12e0116 1202 text_out16(x, y, "GP2X CPU clocks %iMhz", currentConfig.CPUclock);\r
4e8a534c 1203 break;\r
1204 case MA_OPT_SAVECFG:\r
1205 str24[0] = 0;\r
1206 if (config_slot != 0) sprintf(str24, " (profile: %i)", config_slot);\r
a12e0116 1207 text_out16(x, y, "Save cfg as default%s", str24);\r
4e8a534c 1208 break;\r
1209 case MA_OPT_LOADCFG:\r
a12e0116 1210 text_out16(x, y, "Load cfg from profile %i", config_slot);\r
4e8a534c 1211 break;\r
1212 default:\r
1213 printf("%s: unimplemented (%i)\n", __FUNCTION__, entry->id);\r
1214 break;\r
46969540 1215 }\r
4e8a534c 1216}\r
1217\r
1218\r
1219\r
1220static void draw_menu_options(int menu_sel)\r
1221{\r
a12e0116 1222 int tl_x = 25, tl_y = 24;\r
cc68a136 1223\r
e11c5548 1224 gp2x_pd_clone_buffer2();\r
1225\r
e5f426aa 1226 menu_draw_selection(tl_x - 16, tl_y + menu_sel*10, 284);\r
cc68a136 1227\r
a12e0116 1228 me_draw(opt_entries, OPT_ENTRY_COUNT, tl_x, tl_y, menu_opt_cust_draw, NULL);\r
cc68a136 1229\r
a12e0116 1230 menu_flip();\r
cc68a136 1231}\r
1232\r
1233static int sndrate_prevnext(int rate, int dir)\r
1234{\r
1235 int i, rates[] = { 8000, 11025, 16000, 22050, 44100 };\r
1236\r
1237 for (i = 0; i < 5; i++)\r
1238 if (rates[i] == rate) break;\r
1239\r
1240 i += dir ? 1 : -1;\r
1241 if (i > 4) return dir ? 44100 : 22050;\r
1242 if (i < 0) return dir ? 11025 : 8000;\r
1243 return rates[i];\r
1244}\r
1245\r
51a902ae 1246static void region_prevnext(int right)\r
1247{\r
1248 // jp_ntsc=1, jp_pal=2, usa=4, eu=8\r
1249 static int rgn_orders[] = { 0x148, 0x184, 0x814, 0x418, 0x841, 0x481 };\r
1250 int i;\r
1251 if (right) {\r
1252 if (!currentConfig.PicoRegion) {\r
1253 for (i = 0; i < 6; i++)\r
1254 if (rgn_orders[i] == PicoAutoRgnOrder) break;\r
1255 if (i < 5) PicoAutoRgnOrder = rgn_orders[i+1];\r
1256 else currentConfig.PicoRegion=1;\r
1257 }\r
1258 else currentConfig.PicoRegion<<=1;\r
1259 if (currentConfig.PicoRegion > 8) currentConfig.PicoRegion = 8;\r
1260 } else {\r
1261 if (!currentConfig.PicoRegion) {\r
1262 for (i = 0; i < 6; i++)\r
1263 if (rgn_orders[i] == PicoAutoRgnOrder) break;\r
1264 if (i > 0) PicoAutoRgnOrder = rgn_orders[i-1];\r
1265 }\r
1266 else currentConfig.PicoRegion>>=1;\r
1267 }\r
1268}\r
1269\r
cc68a136 1270static void menu_options_save(void)\r
1271{\r
cc68a136 1272 PicoOpt = currentConfig.PicoOpt;\r
1273 PsndRate = currentConfig.PsndRate;\r
1274 PicoRegionOverride = currentConfig.PicoRegion;\r
7d0143a2 1275 if (PicoRegionOverride) {\r
1276 // force setting possibly changed..\r
1277 Pico.m.pal = (PicoRegionOverride == 2 || PicoRegionOverride == 8) ? 1 : 0;\r
1278 }\r
d524c827 1279 if (!(PicoOpt & 0x20)) {\r
1280 // unbind XYZ MODE, just in case\r
45499284 1281 unbind_action(0xf00, -1, -1);\r
cc68a136 1282 }\r
1283}\r
1284\r
bf098bc5 1285static int menu_loop_options(void)\r
cc68a136 1286{\r
aae35e84 1287 static int menu_sel = 0;\r
4e8a534c 1288 int menu_sel_max, ret;\r
cc68a136 1289 unsigned long inp = 0;\r
4e8a534c 1290 menu_id selected_id;\r
cc68a136 1291\r
51a902ae 1292 currentConfig.PicoOpt = PicoOpt;\r
1293 currentConfig.PsndRate = PsndRate;\r
1294 currentConfig.PicoRegion = PicoRegionOverride;\r
cc68a136 1295\r
4e8a534c 1296 me_enable(opt_entries, OPT_ENTRY_COUNT, MA_OPT_SAVECFG_GAME, rom_data != NULL);\r
1297 me_enable(opt_entries, OPT_ENTRY_COUNT, MA_OPT_LOADCFG, config_slot != config_slot_current);\r
1298 menu_sel_max = me_count_enabled(opt_entries, OPT_ENTRY_COUNT) - 1;\r
d524c827 1299 if (menu_sel > menu_sel_max) menu_sel = menu_sel_max;\r
4e8a534c 1300\r
1301 while (1)\r
cc68a136 1302 {\r
1303 draw_menu_options(menu_sel);\r
1304 inp = wait_for_input(GP2X_UP|GP2X_DOWN|GP2X_LEFT|GP2X_RIGHT|GP2X_B|GP2X_X|GP2X_A);\r
4e8a534c 1305 if (inp & GP2X_UP ) { menu_sel--; if (menu_sel < 0) menu_sel = menu_sel_max; }\r
1306 if (inp & GP2X_DOWN) { menu_sel++; if (menu_sel > menu_sel_max) menu_sel = 0; }\r
1307 selected_id = me_index2id(opt_entries, OPT_ENTRY_COUNT, menu_sel);\r
1308 if (inp & (GP2X_LEFT|GP2X_RIGHT)) { // multi choise\r
1309 if (!me_process(opt_entries, OPT_ENTRY_COUNT, selected_id, (inp&GP2X_RIGHT) ? 1 : 0)) {\r
1310 switch (selected_id) {\r
1311 case MA_OPT_RENDERER:\r
1312 if (inp & GP2X_LEFT) {\r
1313 if ( currentConfig.PicoOpt&0x10) currentConfig.PicoOpt&= ~0x10;\r
1314 else if (!(currentConfig.EmuOpt &0x80))currentConfig.EmuOpt |= 0x80;\r
1315 else if ( currentConfig.EmuOpt &0x80) break;\r
1316 } else {\r
1317 if ( currentConfig.PicoOpt&0x10) break;\r
1318 else if (!(currentConfig.EmuOpt &0x80))currentConfig.PicoOpt|= 0x10;\r
1319 else if ( currentConfig.EmuOpt &0x80) currentConfig.EmuOpt &= ~0x80;\r
1320 }\r
1321 break;\r
1322 case MA_OPT_SOUND_QUALITY:\r
1323 if ((inp & GP2X_RIGHT) && currentConfig.PsndRate == 44100 && !(currentConfig.PicoOpt&0x08)) {\r
1324 currentConfig.PsndRate = 8000; currentConfig.PicoOpt|= 0x08;\r
1325 } else if ((inp & GP2X_LEFT) && currentConfig.PsndRate == 8000 && (currentConfig.PicoOpt&0x08)) {\r
1326 currentConfig.PsndRate = 44100; currentConfig.PicoOpt&=~0x08;\r
1327 } else currentConfig.PsndRate = sndrate_prevnext(currentConfig.PsndRate, inp & GP2X_RIGHT);\r
1328 break;\r
1329 case MA_OPT_REGION:\r
1330 region_prevnext(inp & GP2X_RIGHT);\r
1331 break;\r
1332 case MA_OPT_CONFIRM_STATES: {\r
1333 int n = ((currentConfig.EmuOpt>>9)&1) | ((currentConfig.EmuOpt>>10)&2);\r
1334 n += (inp & GP2X_LEFT) ? -1 : 1;\r
1335 if (n < 0) n = 0; else if (n > 3) n = 3;\r
1336 n |= n << 1; n &= ~2;\r
1337 currentConfig.EmuOpt &= ~0xa00;\r
1338 currentConfig.EmuOpt |= n << 9;\r
1339 break;\r
1340 }\r
1341 case MA_OPT_SAVE_SLOT:\r
1342 if (inp & GP2X_RIGHT) {\r
1343 state_slot++; if (state_slot > 9) state_slot = 0;\r
1344 } else {state_slot--; if (state_slot < 0) state_slot = 9;\r
1345 }\r
1346 break;\r
1347 case MA_OPT_CPU_CLOCKS:\r
1348 while ((inp = gp2x_joystick_read(1)) & (GP2X_LEFT|GP2X_RIGHT)) {\r
1349 currentConfig.CPUclock += (inp & GP2X_LEFT) ? -1 : 1;\r
1350 if (currentConfig.CPUclock < 1) currentConfig.CPUclock = 1;\r
1351 draw_menu_options(menu_sel);\r
1352 usleep(50*1000);\r
1353 }\r
1354 break;\r
1355 case MA_OPT_SAVECFG:\r
1356 case MA_OPT_SAVECFG_GAME:\r
1357 case MA_OPT_LOADCFG:\r
1358 config_slot += (inp&GP2X_RIGHT) ? 1 : -1;\r
1359 if (config_slot > 9) config_slot = 0;\r
1360 if (config_slot < 0) config_slot = 9;\r
1361 me_enable(opt_entries, OPT_ENTRY_COUNT, MA_OPT_LOADCFG, config_slot != config_slot_current);\r
1362 menu_sel_max = me_count_enabled(opt_entries, OPT_ENTRY_COUNT) - 1;\r
1363 if (menu_sel > menu_sel_max) menu_sel = menu_sel_max;\r
1364 break;\r
1365 default:\r
1366 //printf("%s: something unknown selected (%i)\n", __FUNCTION__, selected_id);\r
1367 break;\r
1368 }\r
1369 }\r
1370 }\r
1371 if (inp & GP2X_B) {\r
1372 if (!me_process(opt_entries, OPT_ENTRY_COUNT, selected_id, 1))\r
1373 {\r
1374 switch (selected_id)\r
1375 {\r
1376 case MA_OPT_SCD_OPTS:\r
1377 cd_menu_loop_options();\r
1378 if (engineState == PGS_ReloadRom)\r
1379 return 0; // test BIOS\r
1380 break;\r
1381 case MA_OPT_ADV_OPTS:\r
1382 amenu_loop_options();\r
1383 break;\r
1384 case MA_OPT_SAVECFG: // done (update and write)\r
1385 menu_options_save();\r
1386 if (emu_WriteConfig(0)) strcpy(menuErrorMsg, "config saved");\r
1387 else strcpy(menuErrorMsg, "failed to write config");\r
1388 return 1;\r
1389 case MA_OPT_SAVECFG_GAME: // done (update and write for current game)\r
1390 menu_options_save();\r
1391 if (emu_WriteConfig(1)) strcpy(menuErrorMsg, "config saved");\r
1392 else strcpy(menuErrorMsg, "failed to write config");\r
1393 return 1;\r
1394 case MA_OPT_LOADCFG:\r
d524c827 1395 ret = emu_ReadConfig(1, 1);\r
1396 if (!ret) ret = emu_ReadConfig(0, 1);\r
1397 if (ret) strcpy(menuErrorMsg, "config loaded");\r
4e8a534c 1398 else strcpy(menuErrorMsg, "failed to load config");\r
1399 return 1;\r
1400 default:\r
1401 //printf("%s: something unknown selected (%i)\n", __FUNCTION__, selected_id);\r
1402 break;\r
1403 }\r
cc68a136 1404 }\r
1405 }\r
51a902ae 1406 if(inp & (GP2X_X|GP2X_A)) {\r
cc68a136 1407 menu_options_save();\r
bf098bc5 1408 return 0; // done (update, no write)\r
cc68a136 1409 }\r
cc68a136 1410 }\r
1411}\r
1412\r
1413// -------------- credits --------------\r
1414\r
1415static void draw_menu_credits(void)\r
1416{\r
a12e0116 1417 int tl_x = 15, tl_y = 64, y;\r
e11c5548 1418 gp2x_pd_clone_buffer2();\r
cc68a136 1419\r
a12e0116 1420 text_out16(tl_x, 20, "PicoDrive v" VERSION " (c) notaz, 2006,2007");\r
cc68a136 1421 y = tl_y;\r
a12e0116 1422 text_out16(tl_x, y, "Credits:");\r
3ec29f01 1423 text_out16(tl_x, (y+=10), "fDave: Cyclone 68000 core,");\r
a12e0116 1424 text_out16(tl_x, (y+=10), " base code of PicoDrive");\r
1425 text_out16(tl_x, (y+=10), "Reesy & FluBBa: DrZ80 core");\r
1426 text_out16(tl_x, (y+=10), "MAME devs: YM2612 and SN76496 cores");\r
1427 text_out16(tl_x, (y+=10), "Charles MacDonald: Genesis hw docs");\r
1428 text_out16(tl_x, (y+=10), "Stephane Dallongeville:");\r
1429 text_out16(tl_x, (y+=10), " opensource Gens");\r
1430 text_out16(tl_x, (y+=10), "Haze: Genesis hw info");\r
1431 text_out16(tl_x, (y+=10), "rlyeh and others: minimal SDK");\r
1432 text_out16(tl_x, (y+=10), "Squidge: squidgehack");\r
1433 text_out16(tl_x, (y+=10), "Dzz: ARM940 sample");\r
1434 text_out16(tl_x, (y+=10), "GnoStiC / Puck2099: USB joystick");\r
1435 text_out16(tl_x, (y+=10), "craigix: GP2X hardware");\r
a4f0cc86 1436 text_out16(tl_x, (y+=10), "ketchupgun: skin design");\r
a12e0116 1437\r
1438 menu_flip();\r
cc68a136 1439}\r
1440\r
1441\r
1442// -------------- root menu --------------\r
1443\r
4e8a534c 1444menu_entry main_entries[] =\r
1445{\r
1446 { "Resume game", MB_NONE, MA_MAIN_RESUME_GAME, NULL, 0, 0, 0, 0 },\r
1447 { "Save State", MB_NONE, MA_MAIN_SAVE_STATE, NULL, 0, 0, 0, 0 },\r
1448 { "Load State", MB_NONE, MA_MAIN_LOAD_STATE, NULL, 0, 0, 0, 0 },\r
1449 { "Reset game", MB_NONE, MA_MAIN_RESET_GAME, NULL, 0, 0, 0, 0 },\r
1450 { "Load new ROM/ISO", MB_NONE, MA_MAIN_LOAD_ROM, NULL, 0, 0, 0, 1 },\r
1451 { "Change options", MB_NONE, MA_MAIN_OPTIONS, NULL, 0, 0, 0, 1 },\r
1452 { "Configure controls", MB_NONE, MA_MAIN_CONTROLS, NULL, 0, 0, 0, 1 },\r
1453 { "Credits", MB_NONE, MA_MAIN_CREDITS, NULL, 0, 0, 0, 1 },\r
1454 { "Patches / GameGenie",MB_NONE, MA_MAIN_PATCHES, NULL, 0, 0, 0, 0 },\r
1455 { "Exit", MB_NONE, MA_MAIN_EXIT, NULL, 0, 0, 0, 1 }\r
1456};\r
1457\r
1458#define MAIN_ENTRY_COUNT (sizeof(main_entries) / sizeof(main_entries[0]))\r
1459\r
cc68a136 1460static void draw_menu_root(int menu_sel)\r
1461{\r
4e8a534c 1462 const int tl_x = 70, tl_y = 70;\r
1463\r
e11c5548 1464 gp2x_pd_clone_buffer2();\r
cc68a136 1465\r
a12e0116 1466 text_out16(tl_x, 20, "PicoDrive v" VERSION);\r
1467\r
e5f426aa 1468 menu_draw_selection(tl_x - 16, tl_y + menu_sel*10, 146);\r
cc68a136 1469\r
4e8a534c 1470 me_draw(main_entries, MAIN_ENTRY_COUNT, tl_x, tl_y, NULL, NULL);\r
cc68a136 1471\r
cc68a136 1472 // error\r
a12e0116 1473 if (menuErrorMsg[0]) {\r
1474 memset((char *)gp2x_screen + 320*224*2, 0, 320*16*2);\r
1475 text_out16(5, 226, menuErrorMsg);\r
1476 }\r
1477 menu_flip();\r
cc68a136 1478}\r
1479\r
1480\r
1481static void menu_loop_root(void)\r
1482{\r
4e8a534c 1483 static int menu_sel = 0;\r
1484 int ret, menu_sel_max;\r
cc68a136 1485 unsigned long inp = 0;\r
cc68a136 1486\r
4e8a534c 1487 me_enable(main_entries, MAIN_ENTRY_COUNT, MA_MAIN_RESUME_GAME, rom_data != NULL);\r
1488 me_enable(main_entries, MAIN_ENTRY_COUNT, MA_MAIN_SAVE_STATE, rom_data != NULL);\r
1489 me_enable(main_entries, MAIN_ENTRY_COUNT, MA_MAIN_LOAD_STATE, rom_data != NULL);\r
1490 me_enable(main_entries, MAIN_ENTRY_COUNT, MA_MAIN_RESET_GAME, rom_data != NULL);\r
1491 me_enable(main_entries, MAIN_ENTRY_COUNT, MA_MAIN_PATCHES, PicoPatches != NULL);\r
1492\r
1493 menu_sel_max = me_count_enabled(main_entries, MAIN_ENTRY_COUNT) - 1;\r
d524c827 1494 if (menu_sel > menu_sel_max) menu_sel = menu_sel_max;\r
cc68a136 1495\r
2433f409 1496 /* make sure action buttons are not pressed on entering menu */\r
1497 draw_menu_root(menu_sel);\r
1498 while (gp2x_joystick_read(1) & (GP2X_B|GP2X_X|GP2X_SELECT)) usleep(50*1000);\r
1499\r
1500 for (;;)\r
cc68a136 1501 {\r
1502 draw_menu_root(menu_sel);\r
0af33fe0 1503 inp = wait_for_input(GP2X_UP|GP2X_DOWN|GP2X_B|GP2X_X|GP2X_SELECT|GP2X_L|GP2X_R);\r
4e8a534c 1504 if(inp & GP2X_UP ) { menu_sel--; if (menu_sel < 0) menu_sel = menu_sel_max; }\r
1505 if(inp & GP2X_DOWN) { menu_sel++; if (menu_sel > menu_sel_max) menu_sel = 0; }\r
0af33fe0 1506 if((inp & (GP2X_L|GP2X_R)) == (GP2X_L|GP2X_R)) debug_menu_loop();\r
cc68a136 1507 if(inp &(GP2X_SELECT|GP2X_X)){\r
1508 if (rom_data) {\r
1509 while (gp2x_joystick_read(1) & (GP2X_SELECT|GP2X_X)) usleep(50*1000); // wait until select is released\r
1510 engineState = PGS_Running;\r
1511 break;\r
1512 }\r
1513 }\r
4e8a534c 1514 if(inp & GP2X_B) {\r
1515 switch (me_index2id(main_entries, MAIN_ENTRY_COUNT, menu_sel))\r
1516 {\r
1517 case MA_MAIN_RESUME_GAME:\r
721cd396 1518 if (rom_data) {\r
1519 while (gp2x_joystick_read(1) & GP2X_B) usleep(50*1000);\r
1520 engineState = PGS_Running;\r
1521 return;\r
1522 }\r
cc68a136 1523 break;\r
4e8a534c 1524 case MA_MAIN_SAVE_STATE:\r
cc68a136 1525 if (rom_data) {\r
860c6322 1526 if(savestate_menu_loop(0))\r
cc68a136 1527 continue;\r
cc68a136 1528 engineState = PGS_Running;\r
1529 return;\r
1530 }\r
1531 break;\r
4e8a534c 1532 case MA_MAIN_LOAD_STATE:\r
cc68a136 1533 if (rom_data) {\r
860c6322 1534 if(savestate_menu_loop(1))\r
cc68a136 1535 continue;\r
cc68a136 1536 engineState = PGS_Running;\r
1537 return;\r
1538 }\r
1539 break;\r
4e8a534c 1540 case MA_MAIN_RESET_GAME:\r
cc68a136 1541 if (rom_data) {\r
1542 emu_ResetGame();\r
1543 engineState = PGS_Running;\r
1544 return;\r
1545 }\r
1546 break;\r
4e8a534c 1547 case MA_MAIN_LOAD_ROM:\r
a12e0116 1548 {\r
1549 char curr_path[PATH_MAX], *selfname;\r
1550 FILE *tstf;\r
1551 if ( (tstf = fopen(currentConfig.lastRomFile, "rb")) )\r
1552 {\r
1553 fclose(tstf);\r
1554 strcpy(curr_path, currentConfig.lastRomFile);\r
1555 }\r
1556 else\r
1557 getcwd(curr_path, PATH_MAX);\r
cc68a136 1558 selfname = romsel_loop(curr_path);\r
1559 if (selfname) {\r
1560 printf("selected file: %s\n", selfname);\r
cc68a136 1561 engineState = PGS_ReloadRom;\r
a12e0116 1562 return;\r
cc68a136 1563 }\r
a12e0116 1564 break;\r
1565 }\r
4e8a534c 1566 case MA_MAIN_OPTIONS:\r
bf098bc5 1567 ret = menu_loop_options();\r
1568 if (ret == 1) continue; // status update\r
1569 if (engineState == PGS_ReloadRom)\r
1570 return; // BIOS test\r
cc68a136 1571 break;\r
4e8a534c 1572 case MA_MAIN_CONTROLS:\r
cc68a136 1573 kc_sel_loop();\r
1574 break;\r
4e8a534c 1575 case MA_MAIN_CREDITS:\r
cc68a136 1576 draw_menu_credits();\r
1577 usleep(500*1000);\r
1578 inp = wait_for_input(GP2X_B|GP2X_X);\r
1579 break;\r
4e8a534c 1580 case MA_MAIN_EXIT:\r
cc68a136 1581 engineState = PGS_Quit;\r
1582 return;\r
4e8a534c 1583 case MA_MAIN_PATCHES:\r
b67ef287 1584 if (rom_data && PicoPatches) {\r
1585 patches_menu_loop();\r
1586 PicoPatchApply();\r
1587 strcpy(menuErrorMsg, "Patches applied");\r
1588 continue;\r
1589 }\r
1590 break;\r
4e8a534c 1591 default:\r
1592 printf("%s: something unknown selected\n", __FUNCTION__);\r
1593 break;\r
cc68a136 1594 }\r
1595 }\r
1596 menuErrorMsg[0] = 0; // clear error msg\r
1597 }\r
1598}\r
1599\r
a4f0cc86 1600static void menu_darken_bg(void *dst, int pixels, int darker)\r
cc68a136 1601{\r
a12e0116 1602 unsigned int *screen = dst;\r
1603 pixels /= 2;\r
a4f0cc86 1604 if (darker)\r
a12e0116 1605 {\r
a4f0cc86 1606 while (pixels--)\r
1607 {\r
1608 unsigned int p = *screen;\r
1609 *screen++ = ((p&0xf79ef79e)>>1) - ((p&0xc618c618)>>3);\r
1610 }\r
1611 }\r
1612 else\r
1613 {\r
1614 while (pixels--)\r
1615 {\r
1616 unsigned int p = *screen;\r
1617 *screen++ = (p&0xf79ef79e)>>1;\r
1618 }\r
a12e0116 1619 }\r
1620}\r
e11c5548 1621\r
a12e0116 1622static void menu_prepare_bg(int use_game_bg)\r
1623{\r
1624 if (use_game_bg)\r
1625 {\r
1626 // darken the active framebuffer\r
1627 memset(gp2x_screen, 0, 320*8*2);\r
a4f0cc86 1628 menu_darken_bg((char *)gp2x_screen + 320*8*2, 320*224, 1);\r
a12e0116 1629 memset((char *)gp2x_screen + 320*232*2, 0, 320*8*2);\r
1630 }\r
1631 else\r
1632 {\r
1633 // should really only happen once, on startup..\r
1634 readpng(gp2x_screen, "skin/background.png", READPNG_BG);\r
860c6322 1635 }\r
cc68a136 1636\r
a12e0116 1637 // copy to buffer2\r
1638 gp2x_memcpy_buffers((1<<2), gp2x_screen, 0, 320*240*2);\r
860c6322 1639}\r
1640\r
1641static void menu_gfx_prepare(void)\r
1642{\r
a12e0116 1643 menu_prepare_bg(rom_data != NULL);\r
860c6322 1644\r
a12e0116 1645 // switch to 16bpp\r
1646 gp2x_video_changemode2(16);\r
2433f409 1647 gp2x_video_RGB_setscaling(0, 320, 240);\r
a12e0116 1648 menu_flip();\r
e11c5548 1649}\r
1650\r
1651\r
1652void menu_loop(void)\r
1653{\r
1654 menu_gfx_prepare();\r
cc68a136 1655\r
1656 menu_loop_root();\r
1657\r
1658 menuErrorMsg[0] = 0;\r
1659}\r
721cd396 1660\r
1661\r
1662// --------- CD tray close menu ----------\r
1663\r
1664static void draw_menu_tray(int menu_sel)\r
1665{\r
1666 int tl_x = 70, tl_y = 90, y;\r
a12e0116 1667 memset(gp2x_screen, 0, 320*240*2);\r
721cd396 1668\r
a12e0116 1669 text_out16(tl_x, 20, "The unit is about to");\r
1670 text_out16(tl_x, 30, "close the CD tray.");\r
721cd396 1671\r
1672 y = tl_y;\r
a12e0116 1673 text_out16(tl_x, y, "Load new CD image");\r
1674 text_out16(tl_x, (y+=10), "Insert nothing");\r
721cd396 1675\r
1676 // draw cursor\r
a12e0116 1677 text_out16(tl_x - 16, tl_y + menu_sel*10, ">");\r
721cd396 1678 // error\r
a12e0116 1679 if (menuErrorMsg[0]) text_out16(5, 226, menuErrorMsg);\r
1680 menu_flip();\r
721cd396 1681}\r
1682\r
1683\r
1684int menu_loop_tray(void)\r
1685{\r
1686 int menu_sel = 0, menu_sel_max = 1;\r
1687 unsigned long inp = 0;\r
1688 char curr_path[PATH_MAX], *selfname;\r
1689 FILE *tstf;\r
1690\r
e5f426aa 1691 gp2x_memset_all_buffers(0, 0, 320*240*2);\r
721cd396 1692 menu_gfx_prepare();\r
1693\r
1694 if ( (tstf = fopen(currentConfig.lastRomFile, "rb")) )\r
1695 {\r
1696 fclose(tstf);\r
1697 strcpy(curr_path, currentConfig.lastRomFile);\r
1698 }\r
1699 else\r
1700 {\r
1701 getcwd(curr_path, PATH_MAX);\r
1702 }\r
1703\r
1704 /* make sure action buttons are not pressed on entering menu */\r
1705 draw_menu_tray(menu_sel);\r
1706 while (gp2x_joystick_read(1) & GP2X_B) usleep(50*1000);\r
1707\r
1708 for (;;)\r
1709 {\r
1710 draw_menu_tray(menu_sel);\r
1711 inp = wait_for_input(GP2X_UP|GP2X_DOWN|GP2X_B);\r
1712 if(inp & GP2X_UP ) { menu_sel--; if (menu_sel < 0) menu_sel = menu_sel_max; }\r
1713 if(inp & GP2X_DOWN) { menu_sel++; if (menu_sel > menu_sel_max) menu_sel = 0; }\r
1714 if(inp & GP2X_B ) {\r
1715 switch (menu_sel) {\r
1716 case 0: // select image\r
1717 selfname = romsel_loop(curr_path);\r
1718 if (selfname) {\r
1719 int ret = -1, cd_type;\r
ea8c405f 1720 cd_type = emu_cdCheck(NULL);\r
721cd396 1721 if (cd_type > 0)\r
1722 ret = Insert_CD(romFileName, cd_type == 2);\r
1723 if (ret != 0) {\r
1724 sprintf(menuErrorMsg, "Load failed, invalid CD image?");\r
1725 printf("%s\n", menuErrorMsg);\r
1726 continue;\r
1727 }\r
1728 engineState = PGS_RestartRun;\r
1729 return 1;\r
1730 }\r
1731 break;\r
1732 case 1: // insert nothing\r
1733 engineState = PGS_RestartRun;\r
1734 return 0;\r
1735 }\r
1736 }\r
1737 menuErrorMsg[0] = 0; // clear error msg\r
1738 }\r
1739}\r
1740\r
1741\r