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