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