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