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