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