Cyclone poll detection problem fixed
[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
c7a4ff64 33static const char *gp2xKeyNames[] = {\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
124 return ret;\r
125}\r
126\r
c7a4ff64 127static void menu_flip(void)\r
128{\r
129 gp2x_video_flush_cache();\r
130 gp2x_video_flip2();\r
131}\r
720ee7f6 132\r
133\r
b9f8cb3d 134// --------- loading ROM screen ----------\r
135\r
677b5dd8 136static int cdload_called = 0;\r
137\r
b9f8cb3d 138static void load_progress_cb(int percent)\r
139{\r
140 int ln, len = percent * 320 / 100;\r
13059a60 141 unsigned short *dst = (unsigned short *)gp2x_screen + 320*20;\r
b9f8cb3d 142\r
143 if (len > 320) len = 320;\r
677b5dd8 144 for (ln = 8; ln > 0; ln--, dst += 320)\r
13059a60 145 memset(dst, 0xff, len*2);\r
146 menu_flip();\r
b9f8cb3d 147}\r
148\r
677b5dd8 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
6245d5a0 158 dst += 320*30;\r
677b5dd8 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
6245d5a0 163 menu_flip();\r
677b5dd8 164 cdload_called = 1;\r
165}\r
166\r
b9f8cb3d 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
13059a60 172 if (rom_data) gp2x_pd_clone_buffer2();\r
173 else memset(gp2x_screen, 0, 320*240*2);\r
174\r
175 smalltext_out16(1, 1, "Loading", 0xffff);\r
176 smalltext_out16_lim(1, 10, p, 0xffff, 53);\r
177 gp2x_memcpy_buffers(3, gp2x_screen, 0, 320*240*2);\r
178 menu_flip();\r
b9f8cb3d 179 PicoCartLoadProgressCB = load_progress_cb;\r
677b5dd8 180 PicoCDLoadProgressCB = cdload_progress_cb;\r
181 cdload_called = 0;\r
b9f8cb3d 182}\r
183\r
184void menu_romload_end(void)\r
185{\r
6245d5a0 186 PicoCartLoadProgressCB = PicoCDLoadProgressCB = NULL;\r
677b5dd8 187 smalltext_out16(1, cdload_called ? 60 : 30, "Starting emulation...", 0xffff);\r
13059a60 188 menu_flip();\r
b9f8cb3d 189}\r
190\r
720ee7f6 191// -------------- ROM selector --------------\r
192\r
13059a60 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
6d741b32 197 static const char *rom_exts[] = { "zip", "bin", "smd", "gen", "iso", "cso" };\r
13059a60 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
720ee7f6 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
f9a37a02 216 gp2x_pd_clone_buffer2();\r
217\r
218 if (rom_data == NULL) {\r
219 menu_darken_bg(gp2x_screen, 320*240, 0);\r
13059a60 220 }\r
221\r
f9a37a02 222 menu_darken_bg((char *)gp2x_screen + 320*120*2, 320*8, 0);\r
720ee7f6 223\r
224 if(start - 2 >= 0)\r
13059a60 225 smalltext_out16_lim(14, (start - 2)*10, curdir, 0xffff, 53-2);\r
720ee7f6 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
13059a60 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
720ee7f6 233 } else {\r
13059a60 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
720ee7f6 236 }\r
237 }\r
13059a60 238 text_out16(5, 120, ">");\r
239 menu_flip();\r
720ee7f6 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
79cad122 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
0d0558bd 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
720ee7f6 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
0d0558bd 292 n = scandir(curr_path, &namelist, scandir_filter, scandir_cmp);\r
720ee7f6 293 if (n < 0) {\r
294 // try root\r
0d0558bd 295 n = scandir("/", &namelist, scandir_filter, scandir_cmp);\r
720ee7f6 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
5111820c 318 inp = wait_for_input(GP2X_UP|GP2X_DOWN|GP2X_LEFT|GP2X_RIGHT|GP2X_L|GP2X_R|GP2X_B|GP2X_X);\r
720ee7f6 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
5e2e14f2 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
720ee7f6 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
0ae6813e 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
13059a60 398 smalltext_out16_lim(1, line*10, str, 0xffff, len);\r
0ae6813e 399 if (*p == 0) break;\r
400 p++; str = p;\r
401 }\r
13059a60 402 menu_flip();\r
0ae6813e 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
70d2ecc5 411// ------------ patch/gg menu ------------\r
412\r
413static void draw_patchlist(int sel)\r
414{\r
13059a60 415 int start, i, pos, active;\r
70d2ecc5 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
13059a60 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
70d2ecc5 428 }\r
429 pos = start + i;\r
13059a60 430 if (pos < 24) smalltext_out16_lim(14, pos*10, "done", 0xffff, 4);\r
70d2ecc5 431\r
13059a60 432 text_out16(5, 120, ">");\r
433 menu_flip();\r
70d2ecc5 434}\r
435\r
436\r
0ae6813e 437static void patches_menu_loop(void)\r
70d2ecc5 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
4ffd2858 460// ------------ savestate loader ------------\r
461\r
4ffd2858 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
f013066e 472 if (emu_checkSaveFile(slot))\r
4ffd2858 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
f013066e 500 emu_setSaveStateCbs(1);\r
4ffd2858 501 } else {\r
502 file = fopen(fname, "rb");\r
f013066e 503 emu_setSaveStateCbs(0);\r
4ffd2858 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
f013066e 521 emu_forcedFrame();\r
13059a60 522 menu_prepare_bg(1);\r
4ffd2858 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
13059a60 539 text_out16(tl_x, 30, is_loading ? "Load state" : "Save state");\r
540\r
c7a4ff64 541 menu_draw_selection(tl_x - 16, tl_y + menu_sel*10, 108);\r
4ffd2858 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
13059a60 547 text_out16(tl_x, y, "SLOT %i (%s)", i, (state_slot_flags & (1 << i)) ? "USED" : "free");\r
4ffd2858 548 }\r
13059a60 549 text_out16(tl_x, y, "back");\r
4ffd2858 550\r
13059a60 551 menu_flip();\r
4ffd2858 552}\r
553\r
554static int savestate_menu_loop(int is_loading)\r
555{\r
59d0f042 556 static int menu_sel = 10;\r
557 int menu_sel_max = 10;\r
4ffd2858 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
720ee7f6 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
95151aea 606static char *action_binds(int player_idx, int action_mask)\r
720ee7f6 607{\r
95151aea 608 static char strkeys[32*5];\r
720ee7f6 609 int joy, i;\r
610\r
611 strkeys[0] = 0;\r
95151aea 612 for (i = 0; i < 32; i++) // i is key index\r
720ee7f6 613 {\r
95151aea 614 if (currentConfig.KeyBinds[i] & action_mask)\r
720ee7f6 615 {\r
95151aea 616 if (player_idx >= 0 && ((currentConfig.KeyBinds[i] >> 16) & 3) != player_idx) continue;\r
720ee7f6 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
95151aea 625 if (currentConfig.JoyBinds[joy][i] & action_mask)\r
720ee7f6 626 {\r
95151aea 627 if (player_idx >= 0 && ((currentConfig.JoyBinds[joy][i] >> 16) & 3) != player_idx) continue;\r
720ee7f6 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
e67f6454 637 // limit..\r
638 strkeys[20] = 0;\r
639\r
95151aea 640 return strkeys;\r
641}\r
642\r
e67f6454 643static void unbind_action(int action, int pl_idx, int joy)\r
95151aea 644{\r
645 int i, u;\r
646\r
e67f6454 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
b4c711c3 653 }\r
e67f6454 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
b4c711c3 664 for (i = 0; i < 32; i++) {\r
e67f6454 665 if (pl_idx >= 0 && (currentConfig.JoyBinds[joy-1][i]&0x30000) != (pl_idx<<16)) continue;\r
666 currentConfig.JoyBinds[joy-1][i] &= ~action;\r
b4c711c3 667 }\r
e67f6454 668 }\r
95151aea 669}\r
670\r
b4c711c3 671static int count_bound_keys(int action, int pl_idx, int joy)\r
95151aea 672{\r
673 int i, keys = 0;\r
674\r
675 if (joy)\r
676 {\r
b4c711c3 677 for (i = 0; i < 32; i++) {\r
678 if (pl_idx >= 0 && (currentConfig.JoyBinds[joy-1][i]&0x30000) != (pl_idx<<16)) continue;\r
95151aea 679 if (currentConfig.JoyBinds[joy-1][i] & action) keys++;\r
b4c711c3 680 }\r
95151aea 681 }\r
682 else\r
683 {\r
b4c711c3 684 for (i = 0; i < 32; i++) {\r
685 if (pl_idx >= 0 && (currentConfig.KeyBinds[i]&0x30000) != (pl_idx<<16)) continue;\r
95151aea 686 if (currentConfig.KeyBinds[i] & action) keys++;\r
b4c711c3 687 }\r
95151aea 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
e5d315a5 698 gp2x_pd_clone_buffer2();\r
95151aea 699 if (player_idx >= 0) {\r
13059a60 700 text_out16(80, 20, "Player %i controls", player_idx + 1);\r
b4c711c3 701 x = 80;\r
95151aea 702 } else {\r
13059a60 703 text_out16(80, 20, "Emulator controls");\r
95151aea 704 x = 40;\r
705 }\r
706\r
c7a4ff64 707 menu_draw_selection(x - 16, tl_y + sel*10, (player_idx >= 0) ? 66 : 130);\r
13059a60 708\r
95151aea 709 y = tl_y;\r
710 for (i = 0; i < opt_cnt; i++, y+=10)\r
13059a60 711 text_out16(x, y, "%s : %s", opts[i].name, action_binds(player_idx, opts[i].mask));\r
95151aea 712\r
13059a60 713 text_out16(x, y, "Done");\r
95151aea 714\r
715 if (sel < opt_cnt) {\r
13059a60 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
95151aea 720 } else {\r
13059a60 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
95151aea 724 }\r
13059a60 725 menu_flip();\r
720ee7f6 726}\r
727\r
95151aea 728static void key_config_loop(const bind_action_t *opts, int opt_cnt, int player_idx)\r
720ee7f6 729{\r
95151aea 730 int joy = 0, sel = 0, menu_sel_max = opt_cnt, prev_select = 0, i;\r
720ee7f6 731 unsigned long inp = 0;\r
732\r
733 for (;;)\r
734 {\r
95151aea 735 draw_key_config(opts, opt_cnt, player_idx, sel);\r
720ee7f6 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
95151aea 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
720ee7f6 743 }\r
95151aea 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
e67f6454 750 unbind_action(opts[sel].mask, player_idx, -1);\r
95151aea 751 prev_select = inp & GP2X_SELECT;\r
720ee7f6 752 inp &= CONFIGURABLE_KEYS;\r
753 inp &= ~GP2X_SELECT;\r
720ee7f6 754 for (i = 0; i < 32; i++)\r
755 if (inp & (1 << i)) {\r
b4c711c3 756 if (count_bound_keys(opts[sel].mask, player_idx, 0) >= 2)\r
95151aea 757 currentConfig.KeyBinds[i] &= ~opts[sel].mask; // allow to unbind only\r
758 else currentConfig.KeyBinds[i] ^= opts[sel].mask;\r
7695af0a 759 if (player_idx >= 0 && (currentConfig.KeyBinds[i] & opts[sel].mask)) {\r
95151aea 760 currentConfig.KeyBinds[i] &= ~(3 << 16);\r
761 currentConfig.KeyBinds[i] |= player_idx << 16;\r
762 }\r
720ee7f6 763 }\r
95151aea 764 }\r
765 else if (sel < opt_cnt)\r
766 {\r
720ee7f6 767 for (i = 0; i < 32; i++)\r
768 if (inp & (1 << i)) {\r
e67f6454 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
95151aea 777 }\r
720ee7f6 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
e5d315a5 789 gp2x_pd_clone_buffer2();\r
c7a4ff64 790 menu_draw_selection(tl_x - 16, tl_y + menu_sel*10, 138);\r
720ee7f6 791\r
13059a60 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
720ee7f6 796\r
797 tl_x = 25;\r
13059a60 798 text_out16(tl_x, (y=110), "USB joys detected:");\r
720ee7f6 799 if (num_of_joys > 0) {\r
800 for (i = 0; i < num_of_joys; i++) {\r
801 strncpy(joyname, joy_name(joys[i]), 33); joyname[33] = 0;\r
13059a60 802 text_out16(tl_x, (y+=10), "%i: %s", i+1, joyname);\r
720ee7f6 803 }\r
804 } else {\r
13059a60 805 text_out16(tl_x, (y+=10), "none");\r
720ee7f6 806 }\r
807\r
13059a60 808 menu_flip();\r
720ee7f6 809}\r
810\r
95151aea 811\r
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
95151aea 834 { "Load State ", 1<<28 },\r
835 { "Save State ", 1<<27 },\r
95151aea 836 { "Prev Save Slot ", 1<<25 },\r
837 { "Next Save Slot ", 1<<24 },\r
13059a60 838 { "Switch Renderer", 1<<26 },\r
839 { "Volume Down ", 1<<30 },\r
840 { "Volume Up ", 1<<29 },\r
95151aea 841 { "Enter Menu ", 1<<23 },\r
842};\r
843\r
720ee7f6 844static void kc_sel_loop(void)\r
845{\r
95151aea 846 int menu_sel = 3, menu_sel_max = 3;\r
720ee7f6 847 unsigned long inp = 0;\r
95151aea 848 int is_6button = currentConfig.PicoOpt & 0x020;\r
720ee7f6 849\r
95151aea 850 while (1)\r
720ee7f6 851 {\r
852 draw_kc_sel(menu_sel);\r
853 inp = wait_for_input(GP2X_UP|GP2X_DOWN|GP2X_B|GP2X_X);\r
95151aea 854 if (inp & GP2X_UP ) { menu_sel--; if (menu_sel < 0) menu_sel = menu_sel_max; }\r
855 if (inp & GP2X_DOWN) { menu_sel++; if (menu_sel > menu_sel_max) menu_sel = 0; }\r
856 if (inp & GP2X_B) {\r
720ee7f6 857 switch (menu_sel) {\r
95151aea 858 case 0: key_config_loop(ctrl_actions, is_6button ? 12 : 8, 0); return;\r
859 case 1: key_config_loop(ctrl_actions, is_6button ? 12 : 8, 1); return;\r
860 case 2: key_config_loop(emuctrl_actions,\r
861 sizeof(emuctrl_actions)/sizeof(emuctrl_actions[0]), -1); return;\r
862 case 3: if (rom_data == NULL) emu_WriteConfig(0); return;\r
720ee7f6 863 default: return;\r
864 }\r
865 }\r
95151aea 866 if (inp & GP2X_X) return;\r
720ee7f6 867 }\r
868}\r
869\r
870\r
daf91588 871// --------- sega/mega cd options ----------\r
872\r
59633198 873menu_entry cdopt_entries[] =\r
daf91588 874{\r
59633198 875 { NULL, MB_NONE, MA_CDOPT_TESTBIOS_USA, NULL, 0, 0, 0, 1 },\r
876 { NULL, MB_NONE, MA_CDOPT_TESTBIOS_EUR, NULL, 0, 0, 0, 1 },\r
877 { NULL, MB_NONE, MA_CDOPT_TESTBIOS_JAP, NULL, 0, 0, 0, 1 },\r
878 { "CD LEDs", MB_ONOFF, MA_CDOPT_LEDS, &currentConfig.EmuOpt, 0x0400, 0, 0, 1 },\r
879 { "CDDA audio (using mp3s)", MB_ONOFF, MA_CDOPT_CDDA, &currentConfig.PicoOpt, 0x0800, 0, 0, 1 },\r
880 { "PCM audio", MB_ONOFF, MA_CDOPT_PCM, &currentConfig.PicoOpt, 0x0400, 0, 0, 1 },\r
881 { NULL, MB_NONE, MA_CDOPT_READAHEAD, NULL, 0, 0, 0, 1 },\r
882 { "SaveRAM cart", MB_ONOFF, MA_CDOPT_SAVERAM, &currentConfig.PicoOpt, 0x8000, 0, 0, 1 },\r
883 { "Scale/Rot. fx (slow)", MB_ONOFF, MA_CDOPT_SCALEROT_CHIP,&currentConfig.PicoOpt, 0x1000, 0, 0, 1 },\r
884 { "Better sync (slow)", MB_ONOFF, MA_CDOPT_BETTER_SYNC, &currentConfig.PicoOpt, 0x2000, 0, 0, 1 },\r
885 { "done", MB_NONE, MA_CDOPT_DONE, NULL, 0, 0, 0, 1 },\r
886};\r
887\r
888#define CDOPT_ENTRY_COUNT (sizeof(cdopt_entries) / sizeof(cdopt_entries[0]))\r
889\r
890\r
891struct bios_names_t\r
892{\r
893 char us[32], eu[32], jp[32];\r
894};\r
895\r
896static void menu_cdopt_cust_draw(const menu_entry *entry, int x, int y, void *param)\r
897{\r
898 struct bios_names_t *bios_names = param;\r
899 char ra_buff[16];\r
900\r
901 switch (entry->id)\r
902 {\r
13059a60 903 case MA_CDOPT_TESTBIOS_USA: text_out16(x, y, "USA BIOS: %s", bios_names->us); break;\r
904 case MA_CDOPT_TESTBIOS_EUR: text_out16(x, y, "EUR BIOS: %s", bios_names->eu); break;\r
905 case MA_CDOPT_TESTBIOS_JAP: text_out16(x, y, "JAP BIOS: %s", bios_names->jp); break;\r
59633198 906 case MA_CDOPT_READAHEAD:\r
907 if (PicoCDBuffers > 1) sprintf(ra_buff, "%5iK", PicoCDBuffers * 2);\r
908 else strcpy(ra_buff, " OFF");\r
13059a60 909 text_out16(x, y, "ReadAhead buffer %s", ra_buff);\r
59633198 910 break;\r
911 default:break;\r
912 }\r
913}\r
914\r
915static void draw_cd_menu_options(int menu_sel, struct bios_names_t *bios_names)\r
916{\r
917 int tl_x = 25, tl_y = 60;\r
918 menu_id selected_id;\r
5f9922e6 919 char ra_buff[16];\r
920\r
921 if (PicoCDBuffers > 1) sprintf(ra_buff, "%5iK", PicoCDBuffers * 2);\r
922 else strcpy(ra_buff, " OFF");\r
daf91588 923\r
e5d315a5 924 gp2x_pd_clone_buffer2();\r
925\r
c7a4ff64 926 menu_draw_selection(tl_x - 16, tl_y + menu_sel*10, 246);\r
daf91588 927\r
13059a60 928 me_draw(cdopt_entries, CDOPT_ENTRY_COUNT, tl_x, tl_y, menu_cdopt_cust_draw, bios_names);\r
daf91588 929\r
59633198 930 selected_id = me_index2id(cdopt_entries, CDOPT_ENTRY_COUNT, menu_sel);\r
931 if ((selected_id == MA_CDOPT_TESTBIOS_USA && strcmp(bios_names->us, "NOT FOUND")) ||\r
932 (selected_id == MA_CDOPT_TESTBIOS_EUR && strcmp(bios_names->eu, "NOT FOUND")) ||\r
933 (selected_id == MA_CDOPT_TESTBIOS_JAP && strcmp(bios_names->jp, "NOT FOUND")))\r
13059a60 934 text_out16(tl_x, 210, "Press start to test selected BIOS");\r
daf91588 935\r
13059a60 936 menu_flip();\r
daf91588 937}\r
938\r
939static void cd_menu_loop_options(void)\r
940{\r
59d0f042 941 static int menu_sel = 0;\r
942 int menu_sel_max = 10;\r
daf91588 943 unsigned long inp = 0;\r
59633198 944 struct bios_names_t bios_names;\r
945 menu_id selected_id;\r
946 char *bios, *p;\r
daf91588 947\r
f013066e 948 if (emu_findBios(4, &bios)) { // US\r
daf91588 949 for (p = bios+strlen(bios)-1; p > bios && *p != '/'; p--); p++;\r
59633198 950 strncpy(bios_names.us, p, sizeof(bios_names.us)); bios_names.us[sizeof(bios_names.us)-1] = 0;\r
951 } else strcpy(bios_names.us, "NOT FOUND");\r
daf91588 952\r
f013066e 953 if (emu_findBios(8, &bios)) { // EU\r
daf91588 954 for (p = bios+strlen(bios)-1; p > bios && *p != '/'; p--); p++;\r
59633198 955 strncpy(bios_names.eu, p, sizeof(bios_names.eu)); bios_names.eu[sizeof(bios_names.eu)-1] = 0;\r
956 } else strcpy(bios_names.eu, "NOT FOUND");\r
daf91588 957\r
f013066e 958 if (emu_findBios(1, &bios)) { // JP\r
daf91588 959 for (p = bios+strlen(bios)-1; p > bios && *p != '/'; p--); p++;\r
59633198 960 strncpy(bios_names.jp, p, sizeof(bios_names.jp)); bios_names.jp[sizeof(bios_names.jp)-1] = 0;\r
961 } else strcpy(bios_names.jp, "NOT FOUND");\r
daf91588 962\r
963 for(;;)\r
964 {\r
59633198 965 draw_cd_menu_options(menu_sel, &bios_names);\r
daf91588 966 inp = wait_for_input(GP2X_UP|GP2X_DOWN|GP2X_LEFT|GP2X_RIGHT|GP2X_B|GP2X_X|GP2X_A|GP2X_START);\r
59633198 967 if (inp & GP2X_UP ) { menu_sel--; if (menu_sel < 0) menu_sel = menu_sel_max; }\r
968 if (inp & GP2X_DOWN) { menu_sel++; if (menu_sel > menu_sel_max) menu_sel = 0; }\r
969 selected_id = me_index2id(cdopt_entries, CDOPT_ENTRY_COUNT, menu_sel);\r
970 if (inp & (GP2X_LEFT|GP2X_RIGHT)) { // multi choise\r
971 if (!me_process(cdopt_entries, CDOPT_ENTRY_COUNT, selected_id, (inp&GP2X_RIGHT) ? 1 : 0) &&\r
972 selected_id == MA_CDOPT_READAHEAD) {\r
973 if (inp & GP2X_LEFT) {\r
974 PicoCDBuffers >>= 1;\r
975 if (PicoCDBuffers < 64) PicoCDBuffers = 0;\r
976 } else {\r
977 if (PicoCDBuffers < 64) PicoCDBuffers = 64;\r
978 else PicoCDBuffers <<= 1;\r
979 if (PicoCDBuffers > 8*1024) PicoCDBuffers = 8*1024; // 16M\r
980 }\r
daf91588 981 }\r
982 }\r
59633198 983 if (inp & GP2X_B) { // toggleable options\r
984 if (!me_process(cdopt_entries, CDOPT_ENTRY_COUNT, selected_id, 1) &&\r
985 selected_id == MA_CDOPT_DONE) {\r
986 return;\r
987 }\r
988 }\r
989 if (inp & GP2X_START) { // BIOS testers\r
990 switch (selected_id) {\r
991 case MA_CDOPT_TESTBIOS_USA:\r
f013066e 992 if (emu_findBios(4, &bios)) { // test US\r
daf91588 993 strcpy(romFileName, bios);\r
994 engineState = PGS_ReloadRom;\r
995 return;\r
996 }\r
997 break;\r
59633198 998 case MA_CDOPT_TESTBIOS_EUR:\r
f013066e 999 if (emu_findBios(8, &bios)) { // test EU\r
daf91588 1000 strcpy(romFileName, bios);\r
1001 engineState = PGS_ReloadRom;\r
1002 return;\r
1003 }\r
1004 break;\r
59633198 1005 case MA_CDOPT_TESTBIOS_JAP:\r
f013066e 1006 if (emu_findBios(1, &bios)) { // test JP\r
daf91588 1007 strcpy(romFileName, bios);\r
1008 engineState = PGS_ReloadRom;\r
1009 return;\r
1010 }\r
1011 break;\r
59633198 1012 default:\r
1013 break;\r
daf91588 1014 }\r
1015 }\r
59633198 1016 if (inp & (GP2X_X|GP2X_A)) return;\r
daf91588 1017 }\r
1018}\r
1019\r
1020\r
1021// --------- advanced options ----------\r
1022\r
59633198 1023menu_entry opt2_entries[] =\r
1024{\r
1025 { NULL, MB_NONE, MA_OPT2_GAMMA, NULL, 0, 0, 0, 1 },\r
1026 { "A_SN's gamma curve", MB_ONOFF, MA_OPT2_A_SN_GAMMA, &currentConfig.EmuOpt, 0x1000, 0, 0, 1 },\r
c7a4ff64 1027 { "Perfect vsync", MB_ONOFF, MA_OPT2_VSYNC, &currentConfig.EmuOpt, 0x2000, 0, 0, 1 },\r
59633198 1028 { "Emulate Z80", MB_ONOFF, MA_OPT2_ENABLE_Z80, &currentConfig.PicoOpt,0x0004, 0, 0, 1 },\r
1029 { "Emulate YM2612 (FM)", MB_ONOFF, MA_OPT2_ENABLE_YM2612, &currentConfig.PicoOpt,0x0001, 0, 0, 1 },\r
1030 { "Emulate SN76496 (PSG)", MB_ONOFF, MA_OPT2_ENABLE_SN76496,&currentConfig.PicoOpt,0x0002, 0, 0, 1 },\r
1031 { "gzip savestates", MB_ONOFF, MA_OPT2_GZIP_STATES, &currentConfig.EmuOpt, 0x0008, 0, 0, 1 },\r
1032 { "Don't save last used ROM", MB_ONOFF, MA_OPT2_NO_LAST_ROM, &currentConfig.EmuOpt, 0x0020, 0, 0, 1 },\r
1033 { "needs restart:", MB_NONE, MA_NONE, NULL, 0, 0, 0, 1 },\r
1034 { "craigix's RAM timings", MB_ONOFF, MA_OPT2_RAMTIMINGS, &currentConfig.EmuOpt, 0x0100, 0, 0, 1 },\r
1035 { NULL, MB_ONOFF, MA_OPT2_SQUIDGEHACK, &currentConfig.EmuOpt, 0x0010, 0, 0, 1 },\r
1036 { "done", MB_NONE, MA_OPT2_DONE, NULL, 0, 0, 0, 1 },\r
1037};\r
1038\r
1039#define OPT2_ENTRY_COUNT (sizeof(opt2_entries) / sizeof(opt2_entries[0]))\r
1040\r
1041static void menu_opt2_cust_draw(const menu_entry *entry, int x, int y, void *param)\r
1042{\r
1043 if (entry->id == MA_OPT2_GAMMA)\r
13059a60 1044 text_out16(x, y, "Gamma correction %i.%02i", currentConfig.gamma / 100, currentConfig.gamma%100);\r
59633198 1045 else if (entry->id == MA_OPT2_SQUIDGEHACK)\r
13059a60 1046 text_out16(x, y, "squidgehack (now %s %s", mmuhack_status ? "active) " : "inactive)",\r
59633198 1047 (currentConfig.EmuOpt&0x0010)?"ON":"OFF");\r
1048}\r
1049\r
1050\r
720ee7f6 1051static void draw_amenu_options(int menu_sel)\r
1052{\r
59633198 1053 int tl_x = 25, tl_y = 50;\r
720ee7f6 1054\r
e5d315a5 1055 gp2x_pd_clone_buffer2();\r
1056\r
c7a4ff64 1057 menu_draw_selection(tl_x - 16, tl_y + menu_sel*10, 252);\r
720ee7f6 1058\r
13059a60 1059 me_draw(opt2_entries, OPT2_ENTRY_COUNT, tl_x, tl_y, menu_opt2_cust_draw, NULL);\r
720ee7f6 1060\r
13059a60 1061 menu_flip();\r
720ee7f6 1062}\r
1063\r
1064static void amenu_loop_options(void)\r
1065{\r
59d0f042 1066 static int menu_sel = 0;\r
59633198 1067 int menu_sel_max;\r
720ee7f6 1068 unsigned long inp = 0;\r
59633198 1069 menu_id selected_id;\r
1070\r
1071 menu_sel_max = me_count_enabled(opt2_entries, OPT2_ENTRY_COUNT) - 1;\r
720ee7f6 1072\r
1073 for(;;)\r
1074 {\r
1075 draw_amenu_options(menu_sel);\r
1076 inp = wait_for_input(GP2X_UP|GP2X_DOWN|GP2X_LEFT|GP2X_RIGHT|GP2X_B|GP2X_X|GP2X_A);\r
59633198 1077 if (inp & GP2X_UP ) { menu_sel--; if (menu_sel < 0) menu_sel = menu_sel_max; }\r
1078 if (inp & GP2X_DOWN) { menu_sel++; if (menu_sel > menu_sel_max) menu_sel = 0; }\r
1079 selected_id = me_index2id(opt2_entries, OPT2_ENTRY_COUNT, menu_sel);\r
1080 if (inp & (GP2X_LEFT|GP2X_RIGHT)) { // multi choise\r
1081 if (!me_process(opt2_entries, OPT2_ENTRY_COUNT, selected_id, (inp&GP2X_RIGHT) ? 1 : 0) &&\r
1082 selected_id == MA_OPT2_GAMMA) {\r
1083 while ((inp = gp2x_joystick_read(1)) & (GP2X_LEFT|GP2X_RIGHT)) {\r
1084 currentConfig.gamma += (inp & GP2X_LEFT) ? -1 : 1;\r
1085 if (currentConfig.gamma < 1) currentConfig.gamma = 1;\r
1086 if (currentConfig.gamma > 300) currentConfig.gamma = 300;\r
1087 draw_amenu_options(menu_sel);\r
1088 usleep(18*1000);\r
1089 }\r
720ee7f6 1090 }\r
1091 }\r
59633198 1092 if (inp & GP2X_B) { // toggleable options\r
1093 if (!me_process(opt2_entries, OPT2_ENTRY_COUNT, selected_id, 1) &&\r
1094 selected_id == MA_OPT2_DONE) {\r
1095 return;\r
720ee7f6 1096 }\r
1097 }\r
59633198 1098 if (inp & (GP2X_X|GP2X_A)) return;\r
720ee7f6 1099 }\r
1100}\r
1101\r
1102// -------------- options --------------\r
1103\r
59633198 1104\r
1105menu_entry opt_entries[] =\r
1106{\r
1107 { NULL, MB_NONE, MA_OPT_RENDERER, NULL, 0, 0, 0, 1 },\r
1108 { NULL, MB_RANGE, MA_OPT_SCALING, &currentConfig.scaling, 0, 0, 3, 1 },\r
1109 { "Accurate timing (slower)", MB_ONOFF, MA_OPT_ACC_TIMING, &currentConfig.PicoOpt, 0x040, 0, 0, 1 },\r
1110 { "Accurate sprites (slower)", MB_ONOFF, MA_OPT_ACC_SPRITES, &currentConfig.PicoOpt, 0x080, 0, 0, 1 },\r
1111 { "Show FPS", MB_ONOFF, MA_OPT_SHOW_FPS, &currentConfig.EmuOpt, 0x002, 0, 0, 1 },\r
1112 { NULL, MB_RANGE, MA_OPT_FRAMESKIP, &currentConfig.Frameskip, 0, -1, 16, 1 },\r
1113 { "Enable sound", MB_ONOFF, MA_OPT_ENABLE_SOUND, &currentConfig.EmuOpt, 0x004, 0, 0, 1 },\r
1114 { NULL, MB_NONE, MA_OPT_SOUND_QUALITY, NULL, 0, 0, 0, 1 },\r
1115 { "Use ARM940 core for sound", MB_ONOFF, MA_OPT_ARM940_SOUND, &currentConfig.PicoOpt, 0x200, 0, 0, 1 },\r
1116 { "6 button pad", MB_ONOFF, MA_OPT_6BUTTON_PAD, &currentConfig.PicoOpt, 0x020, 0, 0, 1 },\r
1117 { NULL, MB_NONE, MA_OPT_REGION, NULL, 0, 0, 0, 1 },\r
1118 { "Use SRAM/BRAM savestates", MB_ONOFF, MA_OPT_SRAM_STATES, &currentConfig.EmuOpt, 0x001, 0, 0, 1 },\r
1119 { NULL, MB_NONE, MA_OPT_CONFIRM_STATES,NULL, 0, 0, 0, 1 },\r
1120 { "Save slot", MB_RANGE, MA_OPT_SAVE_SLOT, &state_slot, 0, 0, 9, 1 },\r
1121 { NULL, MB_NONE, MA_OPT_CPU_CLOCKS, NULL, 0, 0, 0, 1 },\r
1122 { "[Sega/Mega CD options]", MB_NONE, MA_OPT_SCD_OPTS, NULL, 0, 0, 0, 1 },\r
1123 { "[advanced options]", MB_NONE, MA_OPT_ADV_OPTS, NULL, 0, 0, 0, 1 },\r
1124 { NULL, MB_NONE, MA_OPT_SAVECFG, NULL, 0, 0, 0, 1 },\r
1125 { "Save cfg for current game only",MB_NONE,MA_OPT_SAVECFG_GAME,NULL, 0, 0, 0, 1 },\r
1126 { NULL, MB_NONE, MA_OPT_LOADCFG, NULL, 0, 0, 0, 1 },\r
1127};\r
1128\r
1129#define OPT_ENTRY_COUNT (sizeof(opt_entries) / sizeof(opt_entries[0]))\r
1130\r
1131\r
979ba09f 1132static const char *region_name(unsigned int code)\r
720ee7f6 1133{\r
979ba09f 1134 static const char *names[] = { "Auto", " Japan NTSC", " Japan PAL", " USA", " Europe" };\r
1135 static const char *names_short[] = { "", " JP", " JP", " US", " EU" };\r
1136 int u, i = 0;\r
1137 if (code) {\r
1138 code <<= 1;\r
1139 while((code >>= 1)) i++;\r
1140 if (i > 4) return "unknown";\r
1141 return names[i];\r
1142 } else {\r
1143 static char name[24];\r
1144 strcpy(name, "Auto:");\r
1145 for (u = 0; u < 3; u++) {\r
1146 i = 0; code = ((PicoAutoRgnOrder >> u*4) & 0xf) << 1;\r
1147 while((code >>= 1)) i++;\r
1148 strcat(name, names_short[i]);\r
1149 }\r
1150 return name;\r
1151 }\r
720ee7f6 1152}\r
1153\r
59633198 1154\r
1155static void menu_opt_cust_draw(const menu_entry *entry, int x, int y, void *param)\r
720ee7f6 1156{\r
59633198 1157 char *str, str24[24];\r
1158\r
1159 switch (entry->id)\r
1160 {\r
1161 case MA_OPT_RENDERER:\r
1162 if (currentConfig.PicoOpt&0x10)\r
1163 str = " 8bit fast";\r
1164 else if (currentConfig.EmuOpt&0x80)\r
1165 str = "16bit accurate";\r
1166 else\r
1167 str = " 8bit accurate";\r
13059a60 1168 text_out16(x, y, "Renderer: %s", str);\r
59633198 1169 break;\r
1170 case MA_OPT_SCALING:\r
1171 switch (currentConfig.scaling) {\r
1172 default: str = " OFF"; break;\r
1173 case 1: str = "hw horizontal"; break;\r
1174 case 2: str = "hw horiz. + vert."; break;\r
1175 case 3: str = "sw horizontal"; break;\r
1176 }\r
13059a60 1177 text_out16(x, y, "Scaling: %s", str);\r
59633198 1178 break;\r
1179 case MA_OPT_FRAMESKIP:\r
1180 if (currentConfig.Frameskip < 0)\r
1181 strcpy(str24, "Auto");\r
1182 else sprintf(str24, "%i", currentConfig.Frameskip);\r
13059a60 1183 text_out16(x, y, "Frameskip %s", str24);\r
59633198 1184 break;\r
1185 case MA_OPT_SOUND_QUALITY:\r
1186 str = (currentConfig.PicoOpt&0x08)?"stereo":"mono";\r
13059a60 1187 text_out16(x, y, "Sound Quality: %5iHz %s", currentConfig.PsndRate, str);\r
59633198 1188 break;\r
1189 case MA_OPT_REGION:\r
13059a60 1190 text_out16(x, y, "Region: %s", region_name(currentConfig.PicoRegion));\r
59633198 1191 break;\r
1192 case MA_OPT_CONFIRM_STATES:\r
1193 switch ((currentConfig.EmuOpt >> 9) & 5) {\r
1194 default: str = "OFF"; break;\r
1195 case 1: str = "writes"; break;\r
1196 case 4: str = "loads"; break;\r
1197 case 5: str = "both"; break;\r
1198 }\r
13059a60 1199 text_out16(x, y, "Confirm savestate %s", str);\r
59633198 1200 break;\r
1201 case MA_OPT_CPU_CLOCKS:\r
13059a60 1202 text_out16(x, y, "GP2X CPU clocks %iMhz", currentConfig.CPUclock);\r
59633198 1203 break;\r
1204 case MA_OPT_SAVECFG:\r
1205 str24[0] = 0;\r
1206 if (config_slot != 0) sprintf(str24, " (profile: %i)", config_slot);\r
13059a60 1207 text_out16(x, y, "Save cfg as default%s", str24);\r
59633198 1208 break;\r
1209 case MA_OPT_LOADCFG:\r
13059a60 1210 text_out16(x, y, "Load cfg from profile %i", config_slot);\r
59633198 1211 break;\r
1212 default:\r
1213 printf("%s: unimplemented (%i)\n", __FUNCTION__, entry->id);\r
1214 break;\r
46ede6a6 1215 }\r
59633198 1216}\r
1217\r
1218\r
1219\r
1220static void draw_menu_options(int menu_sel)\r
1221{\r
13059a60 1222 int tl_x = 25, tl_y = 24;\r
720ee7f6 1223\r
e5d315a5 1224 gp2x_pd_clone_buffer2();\r
1225\r
c7a4ff64 1226 menu_draw_selection(tl_x - 16, tl_y + menu_sel*10, 284);\r
720ee7f6 1227\r
13059a60 1228 me_draw(opt_entries, OPT_ENTRY_COUNT, tl_x, tl_y, menu_opt_cust_draw, NULL);\r
720ee7f6 1229\r
13059a60 1230 menu_flip();\r
720ee7f6 1231}\r
1232\r
1233static int sndrate_prevnext(int rate, int dir)\r
1234{\r
1235 int i, rates[] = { 8000, 11025, 16000, 22050, 44100 };\r
1236\r
1237 for (i = 0; i < 5; i++)\r
1238 if (rates[i] == rate) break;\r
1239\r
1240 i += dir ? 1 : -1;\r
1241 if (i > 4) return dir ? 44100 : 22050;\r
1242 if (i < 0) return dir ? 11025 : 8000;\r
1243 return rates[i];\r
1244}\r
1245\r
979ba09f 1246static void region_prevnext(int right)\r
1247{\r
1248 // jp_ntsc=1, jp_pal=2, usa=4, eu=8\r
1249 static int rgn_orders[] = { 0x148, 0x184, 0x814, 0x418, 0x841, 0x481 };\r
1250 int i;\r
1251 if (right) {\r
1252 if (!currentConfig.PicoRegion) {\r
1253 for (i = 0; i < 6; i++)\r
1254 if (rgn_orders[i] == PicoAutoRgnOrder) break;\r
1255 if (i < 5) PicoAutoRgnOrder = rgn_orders[i+1];\r
1256 else currentConfig.PicoRegion=1;\r
1257 }\r
1258 else currentConfig.PicoRegion<<=1;\r
1259 if (currentConfig.PicoRegion > 8) currentConfig.PicoRegion = 8;\r
1260 } else {\r
1261 if (!currentConfig.PicoRegion) {\r
1262 for (i = 0; i < 6; i++)\r
1263 if (rgn_orders[i] == PicoAutoRgnOrder) break;\r
1264 if (i > 0) PicoAutoRgnOrder = rgn_orders[i-1];\r
1265 }\r
1266 else currentConfig.PicoRegion>>=1;\r
1267 }\r
1268}\r
1269\r
720ee7f6 1270static void menu_options_save(void)\r
1271{\r
720ee7f6 1272 PicoOpt = currentConfig.PicoOpt;\r
1273 PsndRate = currentConfig.PsndRate;\r
1274 PicoRegionOverride = currentConfig.PicoRegion;\r
426ecc58 1275 if (PicoRegionOverride) {\r
1276 // force setting possibly changed..\r
1277 Pico.m.pal = (PicoRegionOverride == 2 || PicoRegionOverride == 8) ? 1 : 0;\r
1278 }\r
95151aea 1279 if (!(PicoOpt & 0x20)) {\r
1280 // unbind XYZ MODE, just in case\r
e67f6454 1281 unbind_action(0xf00, -1, -1);\r
720ee7f6 1282 }\r
1283}\r
1284\r
daf91588 1285static int menu_loop_options(void)\r
720ee7f6 1286{\r
59d0f042 1287 static int menu_sel = 0;\r
59633198 1288 int menu_sel_max, ret;\r
720ee7f6 1289 unsigned long inp = 0;\r
59633198 1290 menu_id selected_id;\r
720ee7f6 1291\r
979ba09f 1292 currentConfig.PicoOpt = PicoOpt;\r
1293 currentConfig.PsndRate = PsndRate;\r
1294 currentConfig.PicoRegion = PicoRegionOverride;\r
720ee7f6 1295\r
59633198 1296 me_enable(opt_entries, OPT_ENTRY_COUNT, MA_OPT_SAVECFG_GAME, rom_data != NULL);\r
1297 me_enable(opt_entries, OPT_ENTRY_COUNT, MA_OPT_LOADCFG, config_slot != config_slot_current);\r
1298 menu_sel_max = me_count_enabled(opt_entries, OPT_ENTRY_COUNT) - 1;\r
95151aea 1299 if (menu_sel > menu_sel_max) menu_sel = menu_sel_max;\r
59633198 1300\r
1301 while (1)\r
720ee7f6 1302 {\r
1303 draw_menu_options(menu_sel);\r
1304 inp = wait_for_input(GP2X_UP|GP2X_DOWN|GP2X_LEFT|GP2X_RIGHT|GP2X_B|GP2X_X|GP2X_A);\r
59633198 1305 if (inp & GP2X_UP ) { menu_sel--; if (menu_sel < 0) menu_sel = menu_sel_max; }\r
1306 if (inp & GP2X_DOWN) { menu_sel++; if (menu_sel > menu_sel_max) menu_sel = 0; }\r
1307 selected_id = me_index2id(opt_entries, OPT_ENTRY_COUNT, menu_sel);\r
1308 if (inp & (GP2X_LEFT|GP2X_RIGHT)) { // multi choise\r
1309 if (!me_process(opt_entries, OPT_ENTRY_COUNT, selected_id, (inp&GP2X_RIGHT) ? 1 : 0)) {\r
1310 switch (selected_id) {\r
1311 case MA_OPT_RENDERER:\r
1312 if (inp & GP2X_LEFT) {\r
1313 if ( currentConfig.PicoOpt&0x10) currentConfig.PicoOpt&= ~0x10;\r
1314 else if (!(currentConfig.EmuOpt &0x80))currentConfig.EmuOpt |= 0x80;\r
1315 else if ( currentConfig.EmuOpt &0x80) break;\r
1316 } else {\r
1317 if ( currentConfig.PicoOpt&0x10) break;\r
1318 else if (!(currentConfig.EmuOpt &0x80))currentConfig.PicoOpt|= 0x10;\r
1319 else if ( currentConfig.EmuOpt &0x80) currentConfig.EmuOpt &= ~0x80;\r
1320 }\r
1321 break;\r
1322 case MA_OPT_SOUND_QUALITY:\r
1323 if ((inp & GP2X_RIGHT) && currentConfig.PsndRate == 44100 && !(currentConfig.PicoOpt&0x08)) {\r
1324 currentConfig.PsndRate = 8000; currentConfig.PicoOpt|= 0x08;\r
1325 } else if ((inp & GP2X_LEFT) && currentConfig.PsndRate == 8000 && (currentConfig.PicoOpt&0x08)) {\r
1326 currentConfig.PsndRate = 44100; currentConfig.PicoOpt&=~0x08;\r
1327 } else currentConfig.PsndRate = sndrate_prevnext(currentConfig.PsndRate, inp & GP2X_RIGHT);\r
1328 break;\r
1329 case MA_OPT_REGION:\r
1330 region_prevnext(inp & GP2X_RIGHT);\r
1331 break;\r
1332 case MA_OPT_CONFIRM_STATES: {\r
1333 int n = ((currentConfig.EmuOpt>>9)&1) | ((currentConfig.EmuOpt>>10)&2);\r
1334 n += (inp & GP2X_LEFT) ? -1 : 1;\r
1335 if (n < 0) n = 0; else if (n > 3) n = 3;\r
1336 n |= n << 1; n &= ~2;\r
1337 currentConfig.EmuOpt &= ~0xa00;\r
1338 currentConfig.EmuOpt |= n << 9;\r
1339 break;\r
1340 }\r
1341 case MA_OPT_SAVE_SLOT:\r
1342 if (inp & GP2X_RIGHT) {\r
1343 state_slot++; if (state_slot > 9) state_slot = 0;\r
1344 } else {state_slot--; if (state_slot < 0) state_slot = 9;\r
1345 }\r
1346 break;\r
1347 case MA_OPT_CPU_CLOCKS:\r
1348 while ((inp = gp2x_joystick_read(1)) & (GP2X_LEFT|GP2X_RIGHT)) {\r
1349 currentConfig.CPUclock += (inp & GP2X_LEFT) ? -1 : 1;\r
1350 if (currentConfig.CPUclock < 1) currentConfig.CPUclock = 1;\r
1351 draw_menu_options(menu_sel);\r
1352 usleep(50*1000);\r
1353 }\r
1354 break;\r
1355 case MA_OPT_SAVECFG:\r
1356 case MA_OPT_SAVECFG_GAME:\r
1357 case MA_OPT_LOADCFG:\r
1358 config_slot += (inp&GP2X_RIGHT) ? 1 : -1;\r
1359 if (config_slot > 9) config_slot = 0;\r
1360 if (config_slot < 0) config_slot = 9;\r
1361 me_enable(opt_entries, OPT_ENTRY_COUNT, MA_OPT_LOADCFG, config_slot != config_slot_current);\r
1362 menu_sel_max = me_count_enabled(opt_entries, OPT_ENTRY_COUNT) - 1;\r
1363 if (menu_sel > menu_sel_max) menu_sel = menu_sel_max;\r
1364 break;\r
1365 default:\r
1366 //printf("%s: something unknown selected (%i)\n", __FUNCTION__, selected_id);\r
1367 break;\r
1368 }\r
1369 }\r
1370 }\r
1371 if (inp & GP2X_B) {\r
1372 if (!me_process(opt_entries, OPT_ENTRY_COUNT, selected_id, 1))\r
1373 {\r
1374 switch (selected_id)\r
1375 {\r
1376 case MA_OPT_SCD_OPTS:\r
1377 cd_menu_loop_options();\r
1378 if (engineState == PGS_ReloadRom)\r
1379 return 0; // test BIOS\r
1380 break;\r
1381 case MA_OPT_ADV_OPTS:\r
1382 amenu_loop_options();\r
1383 break;\r
1384 case MA_OPT_SAVECFG: // done (update and write)\r
1385 menu_options_save();\r
1386 if (emu_WriteConfig(0)) strcpy(menuErrorMsg, "config saved");\r
1387 else strcpy(menuErrorMsg, "failed to write config");\r
1388 return 1;\r
1389 case MA_OPT_SAVECFG_GAME: // done (update and write for current game)\r
1390 menu_options_save();\r
1391 if (emu_WriteConfig(1)) strcpy(menuErrorMsg, "config saved");\r
1392 else strcpy(menuErrorMsg, "failed to write config");\r
1393 return 1;\r
1394 case MA_OPT_LOADCFG:\r
95151aea 1395 ret = emu_ReadConfig(1, 1);\r
1396 if (!ret) ret = emu_ReadConfig(0, 1);\r
1397 if (ret) strcpy(menuErrorMsg, "config loaded");\r
59633198 1398 else strcpy(menuErrorMsg, "failed to load config");\r
1399 return 1;\r
1400 default:\r
1401 //printf("%s: something unknown selected (%i)\n", __FUNCTION__, selected_id);\r
1402 break;\r
1403 }\r
720ee7f6 1404 }\r
1405 }\r
979ba09f 1406 if(inp & (GP2X_X|GP2X_A)) {\r
720ee7f6 1407 menu_options_save();\r
daf91588 1408 return 0; // done (update, no write)\r
720ee7f6 1409 }\r
720ee7f6 1410 }\r
1411}\r
1412\r
1413// -------------- credits --------------\r
1414\r
1415static void draw_menu_credits(void)\r
1416{\r
13059a60 1417 int tl_x = 15, tl_y = 64, y;\r
e5d315a5 1418 gp2x_pd_clone_buffer2();\r
720ee7f6 1419\r
13059a60 1420 text_out16(tl_x, 20, "PicoDrive v" VERSION " (c) notaz, 2006,2007");\r
720ee7f6 1421 y = tl_y;\r
13059a60 1422 text_out16(tl_x, y, "Credits:");\r
4b8f4f3c 1423 text_out16(tl_x, (y+=10), "fDave: Cyclone 68000 core,");\r
13059a60 1424 text_out16(tl_x, (y+=10), " base code of PicoDrive");\r
1425 text_out16(tl_x, (y+=10), "Reesy & FluBBa: DrZ80 core");\r
1426 text_out16(tl_x, (y+=10), "MAME devs: YM2612 and SN76496 cores");\r
1427 text_out16(tl_x, (y+=10), "Charles MacDonald: Genesis hw docs");\r
1428 text_out16(tl_x, (y+=10), "Stephane Dallongeville:");\r
1429 text_out16(tl_x, (y+=10), " opensource Gens");\r
1430 text_out16(tl_x, (y+=10), "Haze: Genesis hw info");\r
1431 text_out16(tl_x, (y+=10), "rlyeh and others: minimal SDK");\r
1432 text_out16(tl_x, (y+=10), "Squidge: squidgehack");\r
1433 text_out16(tl_x, (y+=10), "Dzz: ARM940 sample");\r
1434 text_out16(tl_x, (y+=10), "GnoStiC / Puck2099: USB joystick");\r
1435 text_out16(tl_x, (y+=10), "craigix: GP2X hardware");\r
f9a37a02 1436 text_out16(tl_x, (y+=10), "ketchupgun: skin design");\r
13059a60 1437\r
1438 menu_flip();\r
720ee7f6 1439}\r
1440\r
1441\r
1442// -------------- root menu --------------\r
1443\r
59633198 1444menu_entry main_entries[] =\r
1445{\r
1446 { "Resume game", MB_NONE, MA_MAIN_RESUME_GAME, NULL, 0, 0, 0, 0 },\r
1447 { "Save State", MB_NONE, MA_MAIN_SAVE_STATE, NULL, 0, 0, 0, 0 },\r
1448 { "Load State", MB_NONE, MA_MAIN_LOAD_STATE, NULL, 0, 0, 0, 0 },\r
1449 { "Reset game", MB_NONE, MA_MAIN_RESET_GAME, NULL, 0, 0, 0, 0 },\r
1450 { "Load new ROM/ISO", MB_NONE, MA_MAIN_LOAD_ROM, NULL, 0, 0, 0, 1 },\r
1451 { "Change options", MB_NONE, MA_MAIN_OPTIONS, NULL, 0, 0, 0, 1 },\r
1452 { "Configure controls", MB_NONE, MA_MAIN_CONTROLS, NULL, 0, 0, 0, 1 },\r
1453 { "Credits", MB_NONE, MA_MAIN_CREDITS, NULL, 0, 0, 0, 1 },\r
1454 { "Patches / GameGenie",MB_NONE, MA_MAIN_PATCHES, NULL, 0, 0, 0, 0 },\r
1455 { "Exit", MB_NONE, MA_MAIN_EXIT, NULL, 0, 0, 0, 1 }\r
1456};\r
1457\r
1458#define MAIN_ENTRY_COUNT (sizeof(main_entries) / sizeof(main_entries[0]))\r
1459\r
720ee7f6 1460static void draw_menu_root(int menu_sel)\r
1461{\r
59633198 1462 const int tl_x = 70, tl_y = 70;\r
1463\r
e5d315a5 1464 gp2x_pd_clone_buffer2();\r
720ee7f6 1465\r
13059a60 1466 text_out16(tl_x, 20, "PicoDrive v" VERSION);\r
1467\r
c7a4ff64 1468 menu_draw_selection(tl_x - 16, tl_y + menu_sel*10, 146);\r
720ee7f6 1469\r
59633198 1470 me_draw(main_entries, MAIN_ENTRY_COUNT, tl_x, tl_y, NULL, NULL);\r
720ee7f6 1471\r
720ee7f6 1472 // error\r
13059a60 1473 if (menuErrorMsg[0]) {\r
1474 memset((char *)gp2x_screen + 320*224*2, 0, 320*16*2);\r
1475 text_out16(5, 226, menuErrorMsg);\r
1476 }\r
1477 menu_flip();\r
720ee7f6 1478}\r
1479\r
1480\r
1481static void menu_loop_root(void)\r
1482{\r
59633198 1483 static int menu_sel = 0;\r
1484 int ret, menu_sel_max;\r
720ee7f6 1485 unsigned long inp = 0;\r
720ee7f6 1486\r
59633198 1487 me_enable(main_entries, MAIN_ENTRY_COUNT, MA_MAIN_RESUME_GAME, rom_data != NULL);\r
1488 me_enable(main_entries, MAIN_ENTRY_COUNT, MA_MAIN_SAVE_STATE, rom_data != NULL);\r
1489 me_enable(main_entries, MAIN_ENTRY_COUNT, MA_MAIN_LOAD_STATE, rom_data != NULL);\r
1490 me_enable(main_entries, MAIN_ENTRY_COUNT, MA_MAIN_RESET_GAME, rom_data != NULL);\r
1491 me_enable(main_entries, MAIN_ENTRY_COUNT, MA_MAIN_PATCHES, PicoPatches != NULL);\r
1492\r
1493 menu_sel_max = me_count_enabled(main_entries, MAIN_ENTRY_COUNT) - 1;\r
95151aea 1494 if (menu_sel > menu_sel_max) menu_sel = menu_sel_max;\r
720ee7f6 1495\r
79cad122 1496 /* make sure action buttons are not pressed on entering menu */\r
1497 draw_menu_root(menu_sel);\r
1498 while (gp2x_joystick_read(1) & (GP2X_B|GP2X_X|GP2X_SELECT)) usleep(50*1000);\r
1499\r
1500 for (;;)\r
720ee7f6 1501 {\r
1502 draw_menu_root(menu_sel);\r
0ae6813e 1503 inp = wait_for_input(GP2X_UP|GP2X_DOWN|GP2X_B|GP2X_X|GP2X_SELECT|GP2X_L|GP2X_R);\r
59633198 1504 if(inp & GP2X_UP ) { menu_sel--; if (menu_sel < 0) menu_sel = menu_sel_max; }\r
1505 if(inp & GP2X_DOWN) { menu_sel++; if (menu_sel > menu_sel_max) menu_sel = 0; }\r
0ae6813e 1506 if((inp & (GP2X_L|GP2X_R)) == (GP2X_L|GP2X_R)) debug_menu_loop();\r
720ee7f6 1507 if(inp &(GP2X_SELECT|GP2X_X)){\r
1508 if (rom_data) {\r
1509 while (gp2x_joystick_read(1) & (GP2X_SELECT|GP2X_X)) usleep(50*1000); // wait until select is released\r
1510 engineState = PGS_Running;\r
1511 break;\r
1512 }\r
1513 }\r
59633198 1514 if(inp & GP2X_B) {\r
1515 switch (me_index2id(main_entries, MAIN_ENTRY_COUNT, menu_sel))\r
1516 {\r
1517 case MA_MAIN_RESUME_GAME:\r
5e2e14f2 1518 if (rom_data) {\r
1519 while (gp2x_joystick_read(1) & GP2X_B) usleep(50*1000);\r
1520 engineState = PGS_Running;\r
1521 return;\r
1522 }\r
720ee7f6 1523 break;\r
59633198 1524 case MA_MAIN_SAVE_STATE:\r
720ee7f6 1525 if (rom_data) {\r
4ffd2858 1526 if(savestate_menu_loop(0))\r
720ee7f6 1527 continue;\r
720ee7f6 1528 engineState = PGS_Running;\r
1529 return;\r
1530 }\r
1531 break;\r
59633198 1532 case MA_MAIN_LOAD_STATE:\r
720ee7f6 1533 if (rom_data) {\r
4ffd2858 1534 if(savestate_menu_loop(1))\r
720ee7f6 1535 continue;\r
720ee7f6 1536 engineState = PGS_Running;\r
1537 return;\r
1538 }\r
1539 break;\r
59633198 1540 case MA_MAIN_RESET_GAME:\r
720ee7f6 1541 if (rom_data) {\r
1542 emu_ResetGame();\r
1543 engineState = PGS_Running;\r
1544 return;\r
1545 }\r
1546 break;\r
59633198 1547 case MA_MAIN_LOAD_ROM:\r
13059a60 1548 {\r
1549 char curr_path[PATH_MAX], *selfname;\r
1550 FILE *tstf;\r
1551 if ( (tstf = fopen(currentConfig.lastRomFile, "rb")) )\r
1552 {\r
1553 fclose(tstf);\r
1554 strcpy(curr_path, currentConfig.lastRomFile);\r
1555 }\r
1556 else\r
1557 getcwd(curr_path, PATH_MAX);\r
720ee7f6 1558 selfname = romsel_loop(curr_path);\r
1559 if (selfname) {\r
1560 printf("selected file: %s\n", selfname);\r
720ee7f6 1561 engineState = PGS_ReloadRom;\r
13059a60 1562 return;\r
720ee7f6 1563 }\r
13059a60 1564 break;\r
1565 }\r
59633198 1566 case MA_MAIN_OPTIONS:\r
daf91588 1567 ret = menu_loop_options();\r
1568 if (ret == 1) continue; // status update\r
1569 if (engineState == PGS_ReloadRom)\r
1570 return; // BIOS test\r
720ee7f6 1571 break;\r
59633198 1572 case MA_MAIN_CONTROLS:\r
720ee7f6 1573 kc_sel_loop();\r
1574 break;\r
59633198 1575 case MA_MAIN_CREDITS:\r
720ee7f6 1576 draw_menu_credits();\r
1577 usleep(500*1000);\r
1578 inp = wait_for_input(GP2X_B|GP2X_X);\r
1579 break;\r
59633198 1580 case MA_MAIN_EXIT:\r
720ee7f6 1581 engineState = PGS_Quit;\r
1582 return;\r
59633198 1583 case MA_MAIN_PATCHES:\r
70d2ecc5 1584 if (rom_data && PicoPatches) {\r
1585 patches_menu_loop();\r
1586 PicoPatchApply();\r
1587 strcpy(menuErrorMsg, "Patches applied");\r
1588 continue;\r
1589 }\r
1590 break;\r
59633198 1591 default:\r
1592 printf("%s: something unknown selected\n", __FUNCTION__);\r
1593 break;\r
720ee7f6 1594 }\r
1595 }\r
1596 menuErrorMsg[0] = 0; // clear error msg\r
1597 }\r
1598}\r
1599\r
f9a37a02 1600static void menu_darken_bg(void *dst, int pixels, int darker)\r
720ee7f6 1601{\r
13059a60 1602 unsigned int *screen = dst;\r
1603 pixels /= 2;\r
f9a37a02 1604 if (darker)\r
13059a60 1605 {\r
f9a37a02 1606 while (pixels--)\r
1607 {\r
1608 unsigned int p = *screen;\r
1609 *screen++ = ((p&0xf79ef79e)>>1) - ((p&0xc618c618)>>3);\r
1610 }\r
1611 }\r
1612 else\r
1613 {\r
1614 while (pixels--)\r
1615 {\r
1616 unsigned int p = *screen;\r
1617 *screen++ = (p&0xf79ef79e)>>1;\r
1618 }\r
13059a60 1619 }\r
1620}\r
e5d315a5 1621\r
13059a60 1622static void menu_prepare_bg(int use_game_bg)\r
1623{\r
1624 if (use_game_bg)\r
1625 {\r
1626 // darken the active framebuffer\r
1627 memset(gp2x_screen, 0, 320*8*2);\r
f9a37a02 1628 menu_darken_bg((char *)gp2x_screen + 320*8*2, 320*224, 1);\r
13059a60 1629 memset((char *)gp2x_screen + 320*232*2, 0, 320*8*2);\r
1630 }\r
1631 else\r
1632 {\r
1633 // should really only happen once, on startup..\r
1634 readpng(gp2x_screen, "skin/background.png", READPNG_BG);\r
4ffd2858 1635 }\r
720ee7f6 1636\r
13059a60 1637 // copy to buffer2\r
1638 gp2x_memcpy_buffers((1<<2), gp2x_screen, 0, 320*240*2);\r
4ffd2858 1639}\r
1640\r
1641static void menu_gfx_prepare(void)\r
1642{\r
13059a60 1643 menu_prepare_bg(rom_data != NULL);\r
4ffd2858 1644\r
13059a60 1645 // switch to 16bpp\r
1646 gp2x_video_changemode2(16);\r
79cad122 1647 gp2x_video_RGB_setscaling(0, 320, 240);\r
13059a60 1648 menu_flip();\r
e5d315a5 1649}\r
1650\r
1651\r
1652void menu_loop(void)\r
1653{\r
1654 menu_gfx_prepare();\r
720ee7f6 1655\r
1656 menu_loop_root();\r
1657\r
1658 menuErrorMsg[0] = 0;\r
1659}\r
5e2e14f2 1660\r
1661\r
1662// --------- CD tray close menu ----------\r
1663\r
1664static void draw_menu_tray(int menu_sel)\r
1665{\r
1666 int tl_x = 70, tl_y = 90, y;\r
13059a60 1667 memset(gp2x_screen, 0, 320*240*2);\r
5e2e14f2 1668\r
13059a60 1669 text_out16(tl_x, 20, "The unit is about to");\r
1670 text_out16(tl_x, 30, "close the CD tray.");\r
5e2e14f2 1671\r
1672 y = tl_y;\r
13059a60 1673 text_out16(tl_x, y, "Load new CD image");\r
1674 text_out16(tl_x, (y+=10), "Insert nothing");\r
5e2e14f2 1675\r
1676 // draw cursor\r
13059a60 1677 text_out16(tl_x - 16, tl_y + menu_sel*10, ">");\r
5e2e14f2 1678 // error\r
13059a60 1679 if (menuErrorMsg[0]) text_out16(5, 226, menuErrorMsg);\r
1680 menu_flip();\r
5e2e14f2 1681}\r
1682\r
1683\r
1684int menu_loop_tray(void)\r
1685{\r
1686 int menu_sel = 0, menu_sel_max = 1;\r
1687 unsigned long inp = 0;\r
1688 char curr_path[PATH_MAX], *selfname;\r
1689 FILE *tstf;\r
1690\r
c7a4ff64 1691 gp2x_memset_all_buffers(0, 0, 320*240*2);\r
5e2e14f2 1692 menu_gfx_prepare();\r
1693\r
1694 if ( (tstf = fopen(currentConfig.lastRomFile, "rb")) )\r
1695 {\r
1696 fclose(tstf);\r
1697 strcpy(curr_path, currentConfig.lastRomFile);\r
1698 }\r
1699 else\r
1700 {\r
1701 getcwd(curr_path, PATH_MAX);\r
1702 }\r
1703\r
1704 /* make sure action buttons are not pressed on entering menu */\r
1705 draw_menu_tray(menu_sel);\r
1706 while (gp2x_joystick_read(1) & GP2X_B) usleep(50*1000);\r
1707\r
1708 for (;;)\r
1709 {\r
1710 draw_menu_tray(menu_sel);\r
1711 inp = wait_for_input(GP2X_UP|GP2X_DOWN|GP2X_B);\r
1712 if(inp & GP2X_UP ) { menu_sel--; if (menu_sel < 0) menu_sel = menu_sel_max; }\r
1713 if(inp & GP2X_DOWN) { menu_sel++; if (menu_sel > menu_sel_max) menu_sel = 0; }\r
1714 if(inp & GP2X_B ) {\r
1715 switch (menu_sel) {\r
1716 case 0: // select image\r
1717 selfname = romsel_loop(curr_path);\r
1718 if (selfname) {\r
1719 int ret = -1, cd_type;\r
f013066e 1720 cd_type = emu_cdCheck(NULL);\r
5e2e14f2 1721 if (cd_type > 0)\r
1722 ret = Insert_CD(romFileName, cd_type == 2);\r
1723 if (ret != 0) {\r
1724 sprintf(menuErrorMsg, "Load failed, invalid CD image?");\r
1725 printf("%s\n", menuErrorMsg);\r
1726 continue;\r
1727 }\r
1728 engineState = PGS_RestartRun;\r
1729 return 1;\r
1730 }\r
1731 break;\r
1732 case 1: // insert nothing\r
1733 engineState = PGS_RestartRun;\r
1734 return 0;\r
1735 }\r
1736 }\r
1737 menuErrorMsg[0] = 0; // clear error msg\r
1738 }\r
1739}\r
1740\r
1741\r