control configurator adjustments
[picodrive.git] / platform / gp2x / menu.c
CommitLineData
d524c827 1// (c) Copyright 2006,2007 notaz, All rights reserved.\r
cc68a136 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
e5f426aa 17#include "../common/arm_utils.h"\r
18#include "../common/menu.h"\r
19#include "../common/readpng.h"\r
cc68a136 20#include "version.h"\r
21\r
b67ef287 22#include <Pico/PicoInt.h>\r
23#include <Pico/Patch.h>\r
24#include <zlib/zlib.h>\r
cc68a136 25\r
26#ifndef _DIRENT_HAVE_D_TYPE\r
27#error "need d_type for file browser\r
28#endif\r
29\r
cc68a136 30extern char romFileName[PATH_MAX];\r
31extern char *rom_data;\r
32extern int mmuhack_status;\r
33extern int state_slot;\r
4e8a534c 34extern int config_slot, config_slot_current;\r
cc68a136 35\r
e5f426aa 36static const char *gp2xKeyNames[] = {\r
cc68a136 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
a4f0cc86 45static void menu_darken_bg(void *dst, int pixels, int darker);\r
a12e0116 46static void menu_prepare_bg(int use_game_bg);\r
cc68a136 47\r
cc68a136 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
721cd396 54 static int repeats = 0, wait = 50*1000;\r
cc68a136 55 int release = 0, i;\r
56\r
721cd396 57 if (repeats == 2 || repeats == 4) wait /= 2;\r
58 if (repeats == 6) wait = 15 * 1000;\r
cc68a136 59\r
60 for (i = 0; i < 6 && inp_prev == gp2x_joystick_read(1); i++) {\r
721cd396 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
cc68a136 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
721cd396 73 wait = 50*1000;\r
cc68a136 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
e5f426aa 132static void menu_flip(void)\r
133{\r
134 gp2x_video_flush_cache();\r
135 gp2x_video_flip2();\r
136}\r
cc68a136 137\r
138\r
a9b3ffd3 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
a12e0116 144 unsigned short *dst = (unsigned short *)gp2x_screen + 320*20;\r
a9b3ffd3 145\r
146 if (len > 320) len = 320;\r
147 for (ln = 10; ln > 0; ln--, dst += 320)\r
a12e0116 148 memset(dst, 0xff, len*2);\r
149 menu_flip();\r
a9b3ffd3 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
a12e0116 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
a9b3ffd3 164 PicoCartLoadProgressCB = load_progress_cb;\r
165}\r
166\r
167void menu_romload_end(void)\r
168{\r
169 PicoCartLoadProgressCB = NULL;\r
a12e0116 170 smalltext_out16(1, 30, "Starting emulation...", 0xffff);\r
171 menu_flip();\r
a9b3ffd3 172}\r
173\r
cc68a136 174// -------------- ROM selector --------------\r
175\r
a12e0116 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
cc68a136 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
a4f0cc86 199 gp2x_pd_clone_buffer2();\r
200\r
201 if (rom_data == NULL) {\r
202 menu_darken_bg(gp2x_screen, 320*240, 0);\r
a12e0116 203 }\r
204\r
a4f0cc86 205 menu_darken_bg((char *)gp2x_screen + 320*120*2, 320*8, 0);\r
cc68a136 206\r
207 if(start - 2 >= 0)\r
a12e0116 208 smalltext_out16_lim(14, (start - 2)*10, curdir, 0xffff, 53-2);\r
cc68a136 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
a12e0116 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
cc68a136 216 } else {\r
a12e0116 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
cc68a136 219 }\r
220 }\r
a12e0116 221 text_out16(5, 120, ">");\r
222 menu_flip();\r
cc68a136 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
2433f409 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
c008977e 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
cc68a136 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
c008977e 275 n = scandir(curr_path, &namelist, scandir_filter, scandir_cmp);\r
cc68a136 276 if (n < 0) {\r
277 // try root\r
c008977e 278 n = scandir("/", &namelist, scandir_filter, scandir_cmp);\r
cc68a136 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
672ad671 301 inp = wait_for_input(GP2X_UP|GP2X_DOWN|GP2X_LEFT|GP2X_RIGHT|GP2X_L|GP2X_R|GP2X_B|GP2X_X);\r
cc68a136 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
721cd396 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
cc68a136 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
0af33fe0 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
a12e0116 381 smalltext_out16_lim(1, line*10, str, 0xffff, len);\r
0af33fe0 382 if (*p == 0) break;\r
383 p++; str = p;\r
384 }\r
a12e0116 385 menu_flip();\r
0af33fe0 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
b67ef287 394// ------------ patch/gg menu ------------\r
395\r
396static void draw_patchlist(int sel)\r
397{\r
a12e0116 398 int start, i, pos, active;\r
b67ef287 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
a12e0116 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
b67ef287 411 }\r
412 pos = start + i;\r
a12e0116 413 if (pos < 24) smalltext_out16_lim(14, pos*10, "done", 0xffff, 4);\r
b67ef287 414\r
a12e0116 415 text_out16(5, 120, ">");\r
416 menu_flip();\r
b67ef287 417}\r
418\r
419\r
0af33fe0 420static void patches_menu_loop(void)\r
b67ef287 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
860c6322 443// ------------ savestate loader ------------\r
444\r
860c6322 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
a12e0116 505 menu_prepare_bg(1);\r
860c6322 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
a12e0116 522 text_out16(tl_x, 30, is_loading ? "Load state" : "Save state");\r
523\r
e5f426aa 524 menu_draw_selection(tl_x - 16, tl_y + menu_sel*10, 108);\r
860c6322 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
a12e0116 530 text_out16(tl_x, y, "SLOT %i (%s)", i, (state_slot_flags & (1 << i)) ? "USED" : "free");\r
860c6322 531 }\r
a12e0116 532 text_out16(tl_x, y, "back");\r
860c6322 533\r
a12e0116 534 menu_flip();\r
860c6322 535}\r
536\r
537static int savestate_menu_loop(int is_loading)\r
538{\r
aae35e84 539 static int menu_sel = 10;\r
540 int menu_sel_max = 10;\r
860c6322 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
cc68a136 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
d524c827 589static char *action_binds(int player_idx, int action_mask)\r
cc68a136 590{\r
d524c827 591 static char strkeys[32*5];\r
cc68a136 592 int joy, i;\r
593\r
594 strkeys[0] = 0;\r
d524c827 595 for (i = 0; i < 32; i++) // i is key index\r
cc68a136 596 {\r
d524c827 597 if (currentConfig.KeyBinds[i] & action_mask)\r
cc68a136 598 {\r
d524c827 599 if (player_idx >= 0 && ((currentConfig.KeyBinds[i] >> 16) & 3) != player_idx) continue;\r
cc68a136 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
d524c827 608 if (currentConfig.JoyBinds[joy][i] & action_mask)\r
cc68a136 609 {\r
d524c827 610 if (player_idx >= 0 && ((currentConfig.JoyBinds[joy][i] >> 16) & 3) != player_idx) continue;\r
cc68a136 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
45499284 620 // limit..\r
621 strkeys[20] = 0;\r
622\r
d524c827 623 return strkeys;\r
624}\r
625\r
45499284 626static void unbind_action(int action, int pl_idx, int joy)\r
d524c827 627{\r
628 int i, u;\r
629\r
45499284 630 if (joy <= 0)\r
631 {\r
632 for (i = 0; i < 32; i++) {\r
633 if (pl_idx >= 0 && (currentConfig.KeyBinds[i]&0x30000) != (pl_idx<<16)) continue;\r
634 currentConfig.KeyBinds[i] &= ~action;\r
635 }\r
b4fe3a4a 636 }\r
45499284 637 if (joy < 0)\r
638 {\r
639 for (u = 0; u < 4; u++)\r
640 for (i = 0; i < 32; i++) {\r
641 if (pl_idx >= 0 && (currentConfig.JoyBinds[u][i]&0x30000) != (pl_idx<<16)) continue;\r
642 currentConfig.JoyBinds[u][i] &= ~action;\r
643 }\r
644 }\r
645 else if (joy > 0)\r
646 {\r
b4fe3a4a 647 for (i = 0; i < 32; i++) {\r
45499284 648 if (pl_idx >= 0 && (currentConfig.JoyBinds[joy-1][i]&0x30000) != (pl_idx<<16)) continue;\r
649 currentConfig.JoyBinds[joy-1][i] &= ~action;\r
b4fe3a4a 650 }\r
45499284 651 }\r
d524c827 652}\r
653\r
b4fe3a4a 654static int count_bound_keys(int action, int pl_idx, int joy)\r
d524c827 655{\r
656 int i, keys = 0;\r
657\r
658 if (joy)\r
659 {\r
b4fe3a4a 660 for (i = 0; i < 32; i++) {\r
661 if (pl_idx >= 0 && (currentConfig.JoyBinds[joy-1][i]&0x30000) != (pl_idx<<16)) continue;\r
d524c827 662 if (currentConfig.JoyBinds[joy-1][i] & action) keys++;\r
b4fe3a4a 663 }\r
d524c827 664 }\r
665 else\r
666 {\r
b4fe3a4a 667 for (i = 0; i < 32; i++) {\r
668 if (pl_idx >= 0 && (currentConfig.KeyBinds[i]&0x30000) != (pl_idx<<16)) continue;\r
d524c827 669 if (currentConfig.KeyBinds[i] & action) keys++;\r
b4fe3a4a 670 }\r
d524c827 671 }\r
672 return keys;\r
673}\r
674\r
675typedef struct { char *name; int mask; } bind_action_t;\r
676\r
677static void draw_key_config(const bind_action_t *opts, int opt_cnt, int player_idx, int sel)\r
678{\r
679 int x, y, tl_y = 40, i;\r
680\r
e11c5548 681 gp2x_pd_clone_buffer2();\r
d524c827 682 if (player_idx >= 0) {\r
a12e0116 683 text_out16(80, 20, "Player %i controls", player_idx + 1);\r
b4fe3a4a 684 x = 80;\r
d524c827 685 } else {\r
a12e0116 686 text_out16(80, 20, "Emulator controls");\r
d524c827 687 x = 40;\r
688 }\r
689\r
e5f426aa 690 menu_draw_selection(x - 16, tl_y + sel*10, (player_idx >= 0) ? 66 : 130);\r
a12e0116 691\r
d524c827 692 y = tl_y;\r
693 for (i = 0; i < opt_cnt; i++, y+=10)\r
a12e0116 694 text_out16(x, y, "%s : %s", opts[i].name, action_binds(player_idx, opts[i].mask));\r
d524c827 695\r
a12e0116 696 text_out16(x, y, "Done");\r
d524c827 697\r
698 if (sel < opt_cnt) {\r
a12e0116 699 text_out16(30, 180, "Press a button to bind/unbind");\r
700 text_out16(30, 190, "Use SELECT to clear");\r
701 text_out16(30, 200, "To bind UP/DOWN, hold SELECT");\r
702 text_out16(30, 210, "Select \"Done\" to exit");\r
d524c827 703 } else {\r
a12e0116 704 text_out16(30, 190, "Use Options -> Save cfg");\r
705 text_out16(30, 200, "to save controls");\r
706 text_out16(30, 210, "Press B or X to exit");\r
d524c827 707 }\r
a12e0116 708 menu_flip();\r
cc68a136 709}\r
710\r
d524c827 711static void key_config_loop(const bind_action_t *opts, int opt_cnt, int player_idx)\r
cc68a136 712{\r
d524c827 713 int joy = 0, sel = 0, menu_sel_max = opt_cnt, prev_select = 0, i;\r
cc68a136 714 unsigned long inp = 0;\r
715\r
716 for (;;)\r
717 {\r
d524c827 718 draw_key_config(opts, opt_cnt, player_idx, sel);\r
cc68a136 719 inp = wait_for_input_usbjoy(CONFIGURABLE_KEYS, &joy);\r
720 // printf("got %08lX from joy %i\n", inp, joy);\r
721 if (joy == 0) {\r
d524c827 722 if (!(inp & GP2X_SELECT)) {\r
723 prev_select = 0;\r
724 if(inp & GP2X_UP ) { sel--; if (sel < 0) sel = menu_sel_max; continue; }\r
725 if(inp & GP2X_DOWN) { sel++; if (sel > menu_sel_max) sel = 0; continue; }\r
cc68a136 726 }\r
d524c827 727 if (sel >= opt_cnt) {\r
728 if (inp & (GP2X_B|GP2X_X)) break;\r
729 else continue;\r
730 }\r
731 // if we are here, we want to bind/unbind something\r
732 if ((inp & GP2X_SELECT) && !prev_select)\r
45499284 733 unbind_action(opts[sel].mask, player_idx, -1);\r
d524c827 734 prev_select = inp & GP2X_SELECT;\r
cc68a136 735 inp &= CONFIGURABLE_KEYS;\r
736 inp &= ~GP2X_SELECT;\r
cc68a136 737 for (i = 0; i < 32; i++)\r
738 if (inp & (1 << i)) {\r
b4fe3a4a 739 if (count_bound_keys(opts[sel].mask, player_idx, 0) >= 2)\r
d524c827 740 currentConfig.KeyBinds[i] &= ~opts[sel].mask; // allow to unbind only\r
741 else currentConfig.KeyBinds[i] ^= opts[sel].mask;\r
b4fe3a4a 742 if (player_idx >= 0 && currentConfig.KeyBinds[i] & opts[sel].mask) {\r
d524c827 743 currentConfig.KeyBinds[i] &= ~(3 << 16);\r
744 currentConfig.KeyBinds[i] |= player_idx << 16;\r
745 }\r
cc68a136 746 }\r
d524c827 747 }\r
748 else if (sel < opt_cnt)\r
749 {\r
cc68a136 750 for (i = 0; i < 32; i++)\r
751 if (inp & (1 << i)) {\r
45499284 752 int *bind = &currentConfig.JoyBinds[joy-1][i];\r
753 if ((*bind & opts[sel].mask) && (player_idx < 0 || player_idx == ((*bind>>16)&3)))\r
754 currentConfig.JoyBinds[joy-1][i] &= ~opts[sel].mask;\r
755 else {\r
756 // override\r
757 unbind_action(opts[sel].mask, player_idx, joy);\r
758 *bind = opts[sel].mask;\r
759 if (player_idx >= 0) *bind |= player_idx << 16;\r
d524c827 760 }\r
cc68a136 761 }\r
762 }\r
763 }\r
764}\r
765\r
766static void draw_kc_sel(int menu_sel)\r
767{\r
768 int tl_x = 25+40, tl_y = 60, y, i;\r
769 char joyname[36];\r
770\r
771 y = tl_y;\r
e11c5548 772 gp2x_pd_clone_buffer2();\r
e5f426aa 773 menu_draw_selection(tl_x - 16, tl_y + menu_sel*10, 138);\r
cc68a136 774\r
a12e0116 775 text_out16(tl_x, y, "Player 1");\r
776 text_out16(tl_x, (y+=10), "Player 2");\r
777 text_out16(tl_x, (y+=10), "Emulator controls");\r
778 text_out16(tl_x, (y+=10), "Done");\r
cc68a136 779\r
780 tl_x = 25;\r
a12e0116 781 text_out16(tl_x, (y=110), "USB joys detected:");\r
cc68a136 782 if (num_of_joys > 0) {\r
783 for (i = 0; i < num_of_joys; i++) {\r
784 strncpy(joyname, joy_name(joys[i]), 33); joyname[33] = 0;\r
a12e0116 785 text_out16(tl_x, (y+=10), "%i: %s", i+1, joyname);\r
cc68a136 786 }\r
787 } else {\r
a12e0116 788 text_out16(tl_x, (y+=10), "none");\r
cc68a136 789 }\r
790\r
a12e0116 791 menu_flip();\r
cc68a136 792}\r
793\r
d524c827 794\r
795// PicoPad[] format: MXYZ SACB RLDU\r
796static bind_action_t ctrl_actions[] =\r
797{\r
798 { "UP ", 0x001 },\r
799 { "DOWN ", 0x002 },\r
800 { "LEFT ", 0x004 },\r
801 { "RIGHT ", 0x008 },\r
802 { "A ", 0x040 },\r
803 { "B ", 0x010 },\r
804 { "C ", 0x020 },\r
805 { "START ", 0x080 },\r
806 { "MODE ", 0x800 },\r
807 { "X ", 0x400 },\r
808 { "Y ", 0x200 },\r
809 { "Z ", 0x100 },\r
810};\r
811\r
812// player2_flag, ?, ?, ?, ?, ?, ?, menu\r
813// "NEXT SAVE SLOT", "PREV SAVE SLOT", "SWITCH RENDERER", "SAVE STATE",\r
814// "LOAD STATE", "VOLUME UP", "VOLUME DOWN", "DONE"\r
815static bind_action_t emuctrl_actions[] =\r
816{\r
d524c827 817 { "Load State ", 1<<28 },\r
818 { "Save State ", 1<<27 },\r
d524c827 819 { "Prev Save Slot ", 1<<25 },\r
820 { "Next Save Slot ", 1<<24 },\r
a12e0116 821 { "Switch Renderer", 1<<26 },\r
822 { "Volume Down ", 1<<30 },\r
823 { "Volume Up ", 1<<29 },\r
d524c827 824 { "Enter Menu ", 1<<23 },\r
825};\r
826\r
cc68a136 827static void kc_sel_loop(void)\r
828{\r
d524c827 829 int menu_sel = 3, menu_sel_max = 3;\r
cc68a136 830 unsigned long inp = 0;\r
d524c827 831 int is_6button = currentConfig.PicoOpt & 0x020;\r
cc68a136 832\r
d524c827 833 while (1)\r
cc68a136 834 {\r
835 draw_kc_sel(menu_sel);\r
836 inp = wait_for_input(GP2X_UP|GP2X_DOWN|GP2X_B|GP2X_X);\r
d524c827 837 if (inp & GP2X_UP ) { menu_sel--; if (menu_sel < 0) menu_sel = menu_sel_max; }\r
838 if (inp & GP2X_DOWN) { menu_sel++; if (menu_sel > menu_sel_max) menu_sel = 0; }\r
839 if (inp & GP2X_B) {\r
cc68a136 840 switch (menu_sel) {\r
d524c827 841 case 0: key_config_loop(ctrl_actions, is_6button ? 12 : 8, 0); return;\r
842 case 1: key_config_loop(ctrl_actions, is_6button ? 12 : 8, 1); return;\r
843 case 2: key_config_loop(emuctrl_actions,\r
844 sizeof(emuctrl_actions)/sizeof(emuctrl_actions[0]), -1); return;\r
845 case 3: if (rom_data == NULL) emu_WriteConfig(0); return;\r
cc68a136 846 default: return;\r
847 }\r
848 }\r
d524c827 849 if (inp & GP2X_X) return;\r
cc68a136 850 }\r
851}\r
852\r
853\r
bf098bc5 854// --------- sega/mega cd options ----------\r
855\r
4e8a534c 856menu_entry cdopt_entries[] =\r
bf098bc5 857{\r
4e8a534c 858 { NULL, MB_NONE, MA_CDOPT_TESTBIOS_USA, NULL, 0, 0, 0, 1 },\r
859 { NULL, MB_NONE, MA_CDOPT_TESTBIOS_EUR, NULL, 0, 0, 0, 1 },\r
860 { NULL, MB_NONE, MA_CDOPT_TESTBIOS_JAP, NULL, 0, 0, 0, 1 },\r
861 { "CD LEDs", MB_ONOFF, MA_CDOPT_LEDS, &currentConfig.EmuOpt, 0x0400, 0, 0, 1 },\r
862 { "CDDA audio (using mp3s)", MB_ONOFF, MA_CDOPT_CDDA, &currentConfig.PicoOpt, 0x0800, 0, 0, 1 },\r
863 { "PCM audio", MB_ONOFF, MA_CDOPT_PCM, &currentConfig.PicoOpt, 0x0400, 0, 0, 1 },\r
864 { NULL, MB_NONE, MA_CDOPT_READAHEAD, NULL, 0, 0, 0, 1 },\r
865 { "SaveRAM cart", MB_ONOFF, MA_CDOPT_SAVERAM, &currentConfig.PicoOpt, 0x8000, 0, 0, 1 },\r
866 { "Scale/Rot. fx (slow)", MB_ONOFF, MA_CDOPT_SCALEROT_CHIP,&currentConfig.PicoOpt, 0x1000, 0, 0, 1 },\r
867 { "Better sync (slow)", MB_ONOFF, MA_CDOPT_BETTER_SYNC, &currentConfig.PicoOpt, 0x2000, 0, 0, 1 },\r
868 { "done", MB_NONE, MA_CDOPT_DONE, NULL, 0, 0, 0, 1 },\r
869};\r
870\r
871#define CDOPT_ENTRY_COUNT (sizeof(cdopt_entries) / sizeof(cdopt_entries[0]))\r
872\r
873\r
874struct bios_names_t\r
875{\r
876 char us[32], eu[32], jp[32];\r
877};\r
878\r
879static void menu_cdopt_cust_draw(const menu_entry *entry, int x, int y, void *param)\r
880{\r
881 struct bios_names_t *bios_names = param;\r
882 char ra_buff[16];\r
883\r
884 switch (entry->id)\r
885 {\r
a12e0116 886 case MA_CDOPT_TESTBIOS_USA: text_out16(x, y, "USA BIOS: %s", bios_names->us); break;\r
887 case MA_CDOPT_TESTBIOS_EUR: text_out16(x, y, "EUR BIOS: %s", bios_names->eu); break;\r
888 case MA_CDOPT_TESTBIOS_JAP: text_out16(x, y, "JAP BIOS: %s", bios_names->jp); break;\r
4e8a534c 889 case MA_CDOPT_READAHEAD:\r
890 if (PicoCDBuffers > 1) sprintf(ra_buff, "%5iK", PicoCDBuffers * 2);\r
891 else strcpy(ra_buff, " OFF");\r
a12e0116 892 text_out16(x, y, "ReadAhead buffer %s", ra_buff);\r
4e8a534c 893 break;\r
894 default:break;\r
895 }\r
896}\r
897\r
898static void draw_cd_menu_options(int menu_sel, struct bios_names_t *bios_names)\r
899{\r
900 int tl_x = 25, tl_y = 60;\r
901 menu_id selected_id;\r
0a051f55 902 char ra_buff[16];\r
903\r
904 if (PicoCDBuffers > 1) sprintf(ra_buff, "%5iK", PicoCDBuffers * 2);\r
905 else strcpy(ra_buff, " OFF");\r
bf098bc5 906\r
e11c5548 907 gp2x_pd_clone_buffer2();\r
908\r
e5f426aa 909 menu_draw_selection(tl_x - 16, tl_y + menu_sel*10, 246);\r
bf098bc5 910\r
a12e0116 911 me_draw(cdopt_entries, CDOPT_ENTRY_COUNT, tl_x, tl_y, menu_cdopt_cust_draw, bios_names);\r
bf098bc5 912\r
4e8a534c 913 selected_id = me_index2id(cdopt_entries, CDOPT_ENTRY_COUNT, menu_sel);\r
914 if ((selected_id == MA_CDOPT_TESTBIOS_USA && strcmp(bios_names->us, "NOT FOUND")) ||\r
915 (selected_id == MA_CDOPT_TESTBIOS_EUR && strcmp(bios_names->eu, "NOT FOUND")) ||\r
916 (selected_id == MA_CDOPT_TESTBIOS_JAP && strcmp(bios_names->jp, "NOT FOUND")))\r
a12e0116 917 text_out16(tl_x, 210, "Press start to test selected BIOS");\r
bf098bc5 918\r
a12e0116 919 menu_flip();\r
bf098bc5 920}\r
921\r
922static void cd_menu_loop_options(void)\r
923{\r
aae35e84 924 static int menu_sel = 0;\r
925 int menu_sel_max = 10;\r
bf098bc5 926 unsigned long inp = 0;\r
4e8a534c 927 struct bios_names_t bios_names;\r
928 menu_id selected_id;\r
929 char *bios, *p;\r
bf098bc5 930\r
931 if (find_bios(4, &bios)) { // US\r
932 for (p = bios+strlen(bios)-1; p > bios && *p != '/'; p--); p++;\r
4e8a534c 933 strncpy(bios_names.us, p, sizeof(bios_names.us)); bios_names.us[sizeof(bios_names.us)-1] = 0;\r
934 } else strcpy(bios_names.us, "NOT FOUND");\r
bf098bc5 935\r
936 if (find_bios(8, &bios)) { // EU\r
937 for (p = bios+strlen(bios)-1; p > bios && *p != '/'; p--); p++;\r
4e8a534c 938 strncpy(bios_names.eu, p, sizeof(bios_names.eu)); bios_names.eu[sizeof(bios_names.eu)-1] = 0;\r
939 } else strcpy(bios_names.eu, "NOT FOUND");\r
bf098bc5 940\r
941 if (find_bios(1, &bios)) { // JP\r
942 for (p = bios+strlen(bios)-1; p > bios && *p != '/'; p--); p++;\r
4e8a534c 943 strncpy(bios_names.jp, p, sizeof(bios_names.jp)); bios_names.jp[sizeof(bios_names.jp)-1] = 0;\r
944 } else strcpy(bios_names.jp, "NOT FOUND");\r
bf098bc5 945\r
946 for(;;)\r
947 {\r
4e8a534c 948 draw_cd_menu_options(menu_sel, &bios_names);\r
bf098bc5 949 inp = wait_for_input(GP2X_UP|GP2X_DOWN|GP2X_LEFT|GP2X_RIGHT|GP2X_B|GP2X_X|GP2X_A|GP2X_START);\r
4e8a534c 950 if (inp & GP2X_UP ) { menu_sel--; if (menu_sel < 0) menu_sel = menu_sel_max; }\r
951 if (inp & GP2X_DOWN) { menu_sel++; if (menu_sel > menu_sel_max) menu_sel = 0; }\r
952 selected_id = me_index2id(cdopt_entries, CDOPT_ENTRY_COUNT, menu_sel);\r
953 if (inp & (GP2X_LEFT|GP2X_RIGHT)) { // multi choise\r
954 if (!me_process(cdopt_entries, CDOPT_ENTRY_COUNT, selected_id, (inp&GP2X_RIGHT) ? 1 : 0) &&\r
955 selected_id == MA_CDOPT_READAHEAD) {\r
956 if (inp & GP2X_LEFT) {\r
957 PicoCDBuffers >>= 1;\r
958 if (PicoCDBuffers < 64) PicoCDBuffers = 0;\r
959 } else {\r
960 if (PicoCDBuffers < 64) PicoCDBuffers = 64;\r
961 else PicoCDBuffers <<= 1;\r
962 if (PicoCDBuffers > 8*1024) PicoCDBuffers = 8*1024; // 16M\r
963 }\r
bf098bc5 964 }\r
965 }\r
4e8a534c 966 if (inp & GP2X_B) { // toggleable options\r
967 if (!me_process(cdopt_entries, CDOPT_ENTRY_COUNT, selected_id, 1) &&\r
968 selected_id == MA_CDOPT_DONE) {\r
969 return;\r
970 }\r
971 }\r
972 if (inp & GP2X_START) { // BIOS testers\r
973 switch (selected_id) {\r
974 case MA_CDOPT_TESTBIOS_USA:\r
975 if (find_bios(4, &bios)) { // test US\r
bf098bc5 976 strcpy(romFileName, bios);\r
977 engineState = PGS_ReloadRom;\r
978 return;\r
979 }\r
980 break;\r
4e8a534c 981 case MA_CDOPT_TESTBIOS_EUR:\r
982 if (find_bios(8, &bios)) { // test EU\r
bf098bc5 983 strcpy(romFileName, bios);\r
984 engineState = PGS_ReloadRom;\r
985 return;\r
986 }\r
987 break;\r
4e8a534c 988 case MA_CDOPT_TESTBIOS_JAP:\r
989 if (find_bios(1, &bios)) { // test JP\r
bf098bc5 990 strcpy(romFileName, bios);\r
991 engineState = PGS_ReloadRom;\r
992 return;\r
993 }\r
994 break;\r
4e8a534c 995 default:\r
996 break;\r
bf098bc5 997 }\r
998 }\r
4e8a534c 999 if (inp & (GP2X_X|GP2X_A)) return;\r
bf098bc5 1000 }\r
1001}\r
1002\r
1003\r
1004// --------- advanced options ----------\r
1005\r
4e8a534c 1006menu_entry opt2_entries[] =\r
1007{\r
1008 { NULL, MB_NONE, MA_OPT2_GAMMA, NULL, 0, 0, 0, 1 },\r
1009 { "A_SN's gamma curve", MB_ONOFF, MA_OPT2_A_SN_GAMMA, &currentConfig.EmuOpt, 0x1000, 0, 0, 1 },\r
e5f426aa 1010 { "Perfect vsync", MB_ONOFF, MA_OPT2_VSYNC, &currentConfig.EmuOpt, 0x2000, 0, 0, 1 },\r
4e8a534c 1011 { "Emulate Z80", MB_ONOFF, MA_OPT2_ENABLE_Z80, &currentConfig.PicoOpt,0x0004, 0, 0, 1 },\r
1012 { "Emulate YM2612 (FM)", MB_ONOFF, MA_OPT2_ENABLE_YM2612, &currentConfig.PicoOpt,0x0001, 0, 0, 1 },\r
1013 { "Emulate SN76496 (PSG)", MB_ONOFF, MA_OPT2_ENABLE_SN76496,&currentConfig.PicoOpt,0x0002, 0, 0, 1 },\r
1014 { "gzip savestates", MB_ONOFF, MA_OPT2_GZIP_STATES, &currentConfig.EmuOpt, 0x0008, 0, 0, 1 },\r
1015 { "Don't save last used ROM", MB_ONOFF, MA_OPT2_NO_LAST_ROM, &currentConfig.EmuOpt, 0x0020, 0, 0, 1 },\r
1016 { "needs restart:", MB_NONE, MA_NONE, NULL, 0, 0, 0, 1 },\r
1017 { "craigix's RAM timings", MB_ONOFF, MA_OPT2_RAMTIMINGS, &currentConfig.EmuOpt, 0x0100, 0, 0, 1 },\r
1018 { NULL, MB_ONOFF, MA_OPT2_SQUIDGEHACK, &currentConfig.EmuOpt, 0x0010, 0, 0, 1 },\r
1019 { "done", MB_NONE, MA_OPT2_DONE, NULL, 0, 0, 0, 1 },\r
1020};\r
1021\r
1022#define OPT2_ENTRY_COUNT (sizeof(opt2_entries) / sizeof(opt2_entries[0]))\r
1023\r
1024static void menu_opt2_cust_draw(const menu_entry *entry, int x, int y, void *param)\r
1025{\r
1026 if (entry->id == MA_OPT2_GAMMA)\r
a12e0116 1027 text_out16(x, y, "Gamma correction %i.%02i", currentConfig.gamma / 100, currentConfig.gamma%100);\r
4e8a534c 1028 else if (entry->id == MA_OPT2_SQUIDGEHACK)\r
a12e0116 1029 text_out16(x, y, "squidgehack (now %s %s", mmuhack_status ? "active) " : "inactive)",\r
4e8a534c 1030 (currentConfig.EmuOpt&0x0010)?"ON":"OFF");\r
1031}\r
1032\r
1033\r
cc68a136 1034static void draw_amenu_options(int menu_sel)\r
1035{\r
4e8a534c 1036 int tl_x = 25, tl_y = 50;\r
cc68a136 1037\r
e11c5548 1038 gp2x_pd_clone_buffer2();\r
1039\r
e5f426aa 1040 menu_draw_selection(tl_x - 16, tl_y + menu_sel*10, 252);\r
cc68a136 1041\r
a12e0116 1042 me_draw(opt2_entries, OPT2_ENTRY_COUNT, tl_x, tl_y, menu_opt2_cust_draw, NULL);\r
cc68a136 1043\r
a12e0116 1044 menu_flip();\r
cc68a136 1045}\r
1046\r
1047static void amenu_loop_options(void)\r
1048{\r
aae35e84 1049 static int menu_sel = 0;\r
4e8a534c 1050 int menu_sel_max;\r
cc68a136 1051 unsigned long inp = 0;\r
4e8a534c 1052 menu_id selected_id;\r
1053\r
1054 menu_sel_max = me_count_enabled(opt2_entries, OPT2_ENTRY_COUNT) - 1;\r
cc68a136 1055\r
1056 for(;;)\r
1057 {\r
1058 draw_amenu_options(menu_sel);\r
1059 inp = wait_for_input(GP2X_UP|GP2X_DOWN|GP2X_LEFT|GP2X_RIGHT|GP2X_B|GP2X_X|GP2X_A);\r
4e8a534c 1060 if (inp & GP2X_UP ) { menu_sel--; if (menu_sel < 0) menu_sel = menu_sel_max; }\r
1061 if (inp & GP2X_DOWN) { menu_sel++; if (menu_sel > menu_sel_max) menu_sel = 0; }\r
1062 selected_id = me_index2id(opt2_entries, OPT2_ENTRY_COUNT, menu_sel);\r
1063 if (inp & (GP2X_LEFT|GP2X_RIGHT)) { // multi choise\r
1064 if (!me_process(opt2_entries, OPT2_ENTRY_COUNT, selected_id, (inp&GP2X_RIGHT) ? 1 : 0) &&\r
1065 selected_id == MA_OPT2_GAMMA) {\r
1066 while ((inp = gp2x_joystick_read(1)) & (GP2X_LEFT|GP2X_RIGHT)) {\r
1067 currentConfig.gamma += (inp & GP2X_LEFT) ? -1 : 1;\r
1068 if (currentConfig.gamma < 1) currentConfig.gamma = 1;\r
1069 if (currentConfig.gamma > 300) currentConfig.gamma = 300;\r
1070 draw_amenu_options(menu_sel);\r
1071 usleep(18*1000);\r
1072 }\r
cc68a136 1073 }\r
1074 }\r
4e8a534c 1075 if (inp & GP2X_B) { // toggleable options\r
1076 if (!me_process(opt2_entries, OPT2_ENTRY_COUNT, selected_id, 1) &&\r
1077 selected_id == MA_OPT2_DONE) {\r
1078 return;\r
cc68a136 1079 }\r
1080 }\r
4e8a534c 1081 if (inp & (GP2X_X|GP2X_A)) return;\r
cc68a136 1082 }\r
1083}\r
1084\r
1085// -------------- options --------------\r
1086\r
4e8a534c 1087\r
1088menu_entry opt_entries[] =\r
1089{\r
1090 { NULL, MB_NONE, MA_OPT_RENDERER, NULL, 0, 0, 0, 1 },\r
1091 { NULL, MB_RANGE, MA_OPT_SCALING, &currentConfig.scaling, 0, 0, 3, 1 },\r
1092 { "Accurate timing (slower)", MB_ONOFF, MA_OPT_ACC_TIMING, &currentConfig.PicoOpt, 0x040, 0, 0, 1 },\r
1093 { "Accurate sprites (slower)", MB_ONOFF, MA_OPT_ACC_SPRITES, &currentConfig.PicoOpt, 0x080, 0, 0, 1 },\r
1094 { "Show FPS", MB_ONOFF, MA_OPT_SHOW_FPS, &currentConfig.EmuOpt, 0x002, 0, 0, 1 },\r
1095 { NULL, MB_RANGE, MA_OPT_FRAMESKIP, &currentConfig.Frameskip, 0, -1, 16, 1 },\r
1096 { "Enable sound", MB_ONOFF, MA_OPT_ENABLE_SOUND, &currentConfig.EmuOpt, 0x004, 0, 0, 1 },\r
1097 { NULL, MB_NONE, MA_OPT_SOUND_QUALITY, NULL, 0, 0, 0, 1 },\r
1098 { "Use ARM940 core for sound", MB_ONOFF, MA_OPT_ARM940_SOUND, &currentConfig.PicoOpt, 0x200, 0, 0, 1 },\r
1099 { "6 button pad", MB_ONOFF, MA_OPT_6BUTTON_PAD, &currentConfig.PicoOpt, 0x020, 0, 0, 1 },\r
1100 { NULL, MB_NONE, MA_OPT_REGION, NULL, 0, 0, 0, 1 },\r
1101 { "Use SRAM/BRAM savestates", MB_ONOFF, MA_OPT_SRAM_STATES, &currentConfig.EmuOpt, 0x001, 0, 0, 1 },\r
1102 { NULL, MB_NONE, MA_OPT_CONFIRM_STATES,NULL, 0, 0, 0, 1 },\r
1103 { "Save slot", MB_RANGE, MA_OPT_SAVE_SLOT, &state_slot, 0, 0, 9, 1 },\r
1104 { NULL, MB_NONE, MA_OPT_CPU_CLOCKS, NULL, 0, 0, 0, 1 },\r
1105 { "[Sega/Mega CD options]", MB_NONE, MA_OPT_SCD_OPTS, NULL, 0, 0, 0, 1 },\r
1106 { "[advanced options]", MB_NONE, MA_OPT_ADV_OPTS, NULL, 0, 0, 0, 1 },\r
1107 { NULL, MB_NONE, MA_OPT_SAVECFG, NULL, 0, 0, 0, 1 },\r
1108 { "Save cfg for current game only",MB_NONE,MA_OPT_SAVECFG_GAME,NULL, 0, 0, 0, 1 },\r
1109 { NULL, MB_NONE, MA_OPT_LOADCFG, NULL, 0, 0, 0, 1 },\r
1110};\r
1111\r
1112#define OPT_ENTRY_COUNT (sizeof(opt_entries) / sizeof(opt_entries[0]))\r
1113\r
1114\r
51a902ae 1115static const char *region_name(unsigned int code)\r
cc68a136 1116{\r
51a902ae 1117 static const char *names[] = { "Auto", " Japan NTSC", " Japan PAL", " USA", " Europe" };\r
1118 static const char *names_short[] = { "", " JP", " JP", " US", " EU" };\r
1119 int u, i = 0;\r
1120 if (code) {\r
1121 code <<= 1;\r
1122 while((code >>= 1)) i++;\r
1123 if (i > 4) return "unknown";\r
1124 return names[i];\r
1125 } else {\r
1126 static char name[24];\r
1127 strcpy(name, "Auto:");\r
1128 for (u = 0; u < 3; u++) {\r
1129 i = 0; code = ((PicoAutoRgnOrder >> u*4) & 0xf) << 1;\r
1130 while((code >>= 1)) i++;\r
1131 strcat(name, names_short[i]);\r
1132 }\r
1133 return name;\r
1134 }\r
cc68a136 1135}\r
1136\r
4e8a534c 1137\r
1138static void menu_opt_cust_draw(const menu_entry *entry, int x, int y, void *param)\r
cc68a136 1139{\r
4e8a534c 1140 char *str, str24[24];\r
1141\r
1142 switch (entry->id)\r
1143 {\r
1144 case MA_OPT_RENDERER:\r
1145 if (currentConfig.PicoOpt&0x10)\r
1146 str = " 8bit fast";\r
1147 else if (currentConfig.EmuOpt&0x80)\r
1148 str = "16bit accurate";\r
1149 else\r
1150 str = " 8bit accurate";\r
a12e0116 1151 text_out16(x, y, "Renderer: %s", str);\r
4e8a534c 1152 break;\r
1153 case MA_OPT_SCALING:\r
1154 switch (currentConfig.scaling) {\r
1155 default: str = " OFF"; break;\r
1156 case 1: str = "hw horizontal"; break;\r
1157 case 2: str = "hw horiz. + vert."; break;\r
1158 case 3: str = "sw horizontal"; break;\r
1159 }\r
a12e0116 1160 text_out16(x, y, "Scaling: %s", str);\r
4e8a534c 1161 break;\r
1162 case MA_OPT_FRAMESKIP:\r
1163 if (currentConfig.Frameskip < 0)\r
1164 strcpy(str24, "Auto");\r
1165 else sprintf(str24, "%i", currentConfig.Frameskip);\r
a12e0116 1166 text_out16(x, y, "Frameskip %s", str24);\r
4e8a534c 1167 break;\r
1168 case MA_OPT_SOUND_QUALITY:\r
1169 str = (currentConfig.PicoOpt&0x08)?"stereo":"mono";\r
a12e0116 1170 text_out16(x, y, "Sound Quality: %5iHz %s", currentConfig.PsndRate, str);\r
4e8a534c 1171 break;\r
1172 case MA_OPT_REGION:\r
a12e0116 1173 text_out16(x, y, "Region: %s", region_name(currentConfig.PicoRegion));\r
4e8a534c 1174 break;\r
1175 case MA_OPT_CONFIRM_STATES:\r
1176 switch ((currentConfig.EmuOpt >> 9) & 5) {\r
1177 default: str = "OFF"; break;\r
1178 case 1: str = "writes"; break;\r
1179 case 4: str = "loads"; break;\r
1180 case 5: str = "both"; break;\r
1181 }\r
a12e0116 1182 text_out16(x, y, "Confirm savestate %s", str);\r
4e8a534c 1183 break;\r
1184 case MA_OPT_CPU_CLOCKS:\r
a12e0116 1185 text_out16(x, y, "GP2X CPU clocks %iMhz", currentConfig.CPUclock);\r
4e8a534c 1186 break;\r
1187 case MA_OPT_SAVECFG:\r
1188 str24[0] = 0;\r
1189 if (config_slot != 0) sprintf(str24, " (profile: %i)", config_slot);\r
a12e0116 1190 text_out16(x, y, "Save cfg as default%s", str24);\r
4e8a534c 1191 break;\r
1192 case MA_OPT_LOADCFG:\r
a12e0116 1193 text_out16(x, y, "Load cfg from profile %i", config_slot);\r
4e8a534c 1194 break;\r
1195 default:\r
1196 printf("%s: unimplemented (%i)\n", __FUNCTION__, entry->id);\r
1197 break;\r
46969540 1198 }\r
4e8a534c 1199}\r
1200\r
1201\r
1202\r
1203static void draw_menu_options(int menu_sel)\r
1204{\r
a12e0116 1205 int tl_x = 25, tl_y = 24;\r
cc68a136 1206\r
e11c5548 1207 gp2x_pd_clone_buffer2();\r
1208\r
e5f426aa 1209 menu_draw_selection(tl_x - 16, tl_y + menu_sel*10, 284);\r
cc68a136 1210\r
a12e0116 1211 me_draw(opt_entries, OPT_ENTRY_COUNT, tl_x, tl_y, menu_opt_cust_draw, NULL);\r
cc68a136 1212\r
a12e0116 1213 menu_flip();\r
cc68a136 1214}\r
1215\r
1216static int sndrate_prevnext(int rate, int dir)\r
1217{\r
1218 int i, rates[] = { 8000, 11025, 16000, 22050, 44100 };\r
1219\r
1220 for (i = 0; i < 5; i++)\r
1221 if (rates[i] == rate) break;\r
1222\r
1223 i += dir ? 1 : -1;\r
1224 if (i > 4) return dir ? 44100 : 22050;\r
1225 if (i < 0) return dir ? 11025 : 8000;\r
1226 return rates[i];\r
1227}\r
1228\r
51a902ae 1229static void region_prevnext(int right)\r
1230{\r
1231 // jp_ntsc=1, jp_pal=2, usa=4, eu=8\r
1232 static int rgn_orders[] = { 0x148, 0x184, 0x814, 0x418, 0x841, 0x481 };\r
1233 int i;\r
1234 if (right) {\r
1235 if (!currentConfig.PicoRegion) {\r
1236 for (i = 0; i < 6; i++)\r
1237 if (rgn_orders[i] == PicoAutoRgnOrder) break;\r
1238 if (i < 5) PicoAutoRgnOrder = rgn_orders[i+1];\r
1239 else currentConfig.PicoRegion=1;\r
1240 }\r
1241 else currentConfig.PicoRegion<<=1;\r
1242 if (currentConfig.PicoRegion > 8) currentConfig.PicoRegion = 8;\r
1243 } else {\r
1244 if (!currentConfig.PicoRegion) {\r
1245 for (i = 0; i < 6; i++)\r
1246 if (rgn_orders[i] == PicoAutoRgnOrder) break;\r
1247 if (i > 0) PicoAutoRgnOrder = rgn_orders[i-1];\r
1248 }\r
1249 else currentConfig.PicoRegion>>=1;\r
1250 }\r
1251}\r
1252\r
cc68a136 1253static void menu_options_save(void)\r
1254{\r
cc68a136 1255 PicoOpt = currentConfig.PicoOpt;\r
1256 PsndRate = currentConfig.PsndRate;\r
1257 PicoRegionOverride = currentConfig.PicoRegion;\r
d524c827 1258 if (!(PicoOpt & 0x20)) {\r
1259 // unbind XYZ MODE, just in case\r
45499284 1260 unbind_action(0xf00, -1, -1);\r
cc68a136 1261 }\r
2433f409 1262 scaling_update();\r
cc68a136 1263}\r
1264\r
bf098bc5 1265static int menu_loop_options(void)\r
cc68a136 1266{\r
aae35e84 1267 static int menu_sel = 0;\r
4e8a534c 1268 int menu_sel_max, ret;\r
cc68a136 1269 unsigned long inp = 0;\r
4e8a534c 1270 menu_id selected_id;\r
cc68a136 1271\r
51a902ae 1272 currentConfig.PicoOpt = PicoOpt;\r
1273 currentConfig.PsndRate = PsndRate;\r
1274 currentConfig.PicoRegion = PicoRegionOverride;\r
cc68a136 1275\r
4e8a534c 1276 me_enable(opt_entries, OPT_ENTRY_COUNT, MA_OPT_SAVECFG_GAME, rom_data != NULL);\r
1277 me_enable(opt_entries, OPT_ENTRY_COUNT, MA_OPT_LOADCFG, config_slot != config_slot_current);\r
1278 menu_sel_max = me_count_enabled(opt_entries, OPT_ENTRY_COUNT) - 1;\r
d524c827 1279 if (menu_sel > menu_sel_max) menu_sel = menu_sel_max;\r
4e8a534c 1280\r
1281 while (1)\r
cc68a136 1282 {\r
1283 draw_menu_options(menu_sel);\r
1284 inp = wait_for_input(GP2X_UP|GP2X_DOWN|GP2X_LEFT|GP2X_RIGHT|GP2X_B|GP2X_X|GP2X_A);\r
4e8a534c 1285 if (inp & GP2X_UP ) { menu_sel--; if (menu_sel < 0) menu_sel = menu_sel_max; }\r
1286 if (inp & GP2X_DOWN) { menu_sel++; if (menu_sel > menu_sel_max) menu_sel = 0; }\r
1287 selected_id = me_index2id(opt_entries, OPT_ENTRY_COUNT, menu_sel);\r
1288 if (inp & (GP2X_LEFT|GP2X_RIGHT)) { // multi choise\r
1289 if (!me_process(opt_entries, OPT_ENTRY_COUNT, selected_id, (inp&GP2X_RIGHT) ? 1 : 0)) {\r
1290 switch (selected_id) {\r
1291 case MA_OPT_RENDERER:\r
1292 if (inp & GP2X_LEFT) {\r
1293 if ( currentConfig.PicoOpt&0x10) currentConfig.PicoOpt&= ~0x10;\r
1294 else if (!(currentConfig.EmuOpt &0x80))currentConfig.EmuOpt |= 0x80;\r
1295 else if ( currentConfig.EmuOpt &0x80) break;\r
1296 } else {\r
1297 if ( currentConfig.PicoOpt&0x10) break;\r
1298 else if (!(currentConfig.EmuOpt &0x80))currentConfig.PicoOpt|= 0x10;\r
1299 else if ( currentConfig.EmuOpt &0x80) currentConfig.EmuOpt &= ~0x80;\r
1300 }\r
1301 break;\r
1302 case MA_OPT_SOUND_QUALITY:\r
1303 if ((inp & GP2X_RIGHT) && currentConfig.PsndRate == 44100 && !(currentConfig.PicoOpt&0x08)) {\r
1304 currentConfig.PsndRate = 8000; currentConfig.PicoOpt|= 0x08;\r
1305 } else if ((inp & GP2X_LEFT) && currentConfig.PsndRate == 8000 && (currentConfig.PicoOpt&0x08)) {\r
1306 currentConfig.PsndRate = 44100; currentConfig.PicoOpt&=~0x08;\r
1307 } else currentConfig.PsndRate = sndrate_prevnext(currentConfig.PsndRate, inp & GP2X_RIGHT);\r
1308 break;\r
1309 case MA_OPT_REGION:\r
1310 region_prevnext(inp & GP2X_RIGHT);\r
1311 break;\r
1312 case MA_OPT_CONFIRM_STATES: {\r
1313 int n = ((currentConfig.EmuOpt>>9)&1) | ((currentConfig.EmuOpt>>10)&2);\r
1314 n += (inp & GP2X_LEFT) ? -1 : 1;\r
1315 if (n < 0) n = 0; else if (n > 3) n = 3;\r
1316 n |= n << 1; n &= ~2;\r
1317 currentConfig.EmuOpt &= ~0xa00;\r
1318 currentConfig.EmuOpt |= n << 9;\r
1319 break;\r
1320 }\r
1321 case MA_OPT_SAVE_SLOT:\r
1322 if (inp & GP2X_RIGHT) {\r
1323 state_slot++; if (state_slot > 9) state_slot = 0;\r
1324 } else {state_slot--; if (state_slot < 0) state_slot = 9;\r
1325 }\r
1326 break;\r
1327 case MA_OPT_CPU_CLOCKS:\r
1328 while ((inp = gp2x_joystick_read(1)) & (GP2X_LEFT|GP2X_RIGHT)) {\r
1329 currentConfig.CPUclock += (inp & GP2X_LEFT) ? -1 : 1;\r
1330 if (currentConfig.CPUclock < 1) currentConfig.CPUclock = 1;\r
1331 draw_menu_options(menu_sel);\r
1332 usleep(50*1000);\r
1333 }\r
1334 break;\r
1335 case MA_OPT_SAVECFG:\r
1336 case MA_OPT_SAVECFG_GAME:\r
1337 case MA_OPT_LOADCFG:\r
1338 config_slot += (inp&GP2X_RIGHT) ? 1 : -1;\r
1339 if (config_slot > 9) config_slot = 0;\r
1340 if (config_slot < 0) config_slot = 9;\r
1341 me_enable(opt_entries, OPT_ENTRY_COUNT, MA_OPT_LOADCFG, config_slot != config_slot_current);\r
1342 menu_sel_max = me_count_enabled(opt_entries, OPT_ENTRY_COUNT) - 1;\r
1343 if (menu_sel > menu_sel_max) menu_sel = menu_sel_max;\r
1344 break;\r
1345 default:\r
1346 //printf("%s: something unknown selected (%i)\n", __FUNCTION__, selected_id);\r
1347 break;\r
1348 }\r
1349 }\r
1350 }\r
1351 if (inp & GP2X_B) {\r
1352 if (!me_process(opt_entries, OPT_ENTRY_COUNT, selected_id, 1))\r
1353 {\r
1354 switch (selected_id)\r
1355 {\r
1356 case MA_OPT_SCD_OPTS:\r
1357 cd_menu_loop_options();\r
1358 if (engineState == PGS_ReloadRom)\r
1359 return 0; // test BIOS\r
1360 break;\r
1361 case MA_OPT_ADV_OPTS:\r
1362 amenu_loop_options();\r
1363 break;\r
1364 case MA_OPT_SAVECFG: // done (update and write)\r
1365 menu_options_save();\r
1366 if (emu_WriteConfig(0)) strcpy(menuErrorMsg, "config saved");\r
1367 else strcpy(menuErrorMsg, "failed to write config");\r
1368 return 1;\r
1369 case MA_OPT_SAVECFG_GAME: // done (update and write for current game)\r
1370 menu_options_save();\r
1371 if (emu_WriteConfig(1)) strcpy(menuErrorMsg, "config saved");\r
1372 else strcpy(menuErrorMsg, "failed to write config");\r
1373 return 1;\r
1374 case MA_OPT_LOADCFG:\r
d524c827 1375 ret = emu_ReadConfig(1, 1);\r
1376 if (!ret) ret = emu_ReadConfig(0, 1);\r
1377 if (ret) strcpy(menuErrorMsg, "config loaded");\r
4e8a534c 1378 else strcpy(menuErrorMsg, "failed to load config");\r
1379 return 1;\r
1380 default:\r
1381 //printf("%s: something unknown selected (%i)\n", __FUNCTION__, selected_id);\r
1382 break;\r
1383 }\r
cc68a136 1384 }\r
1385 }\r
51a902ae 1386 if(inp & (GP2X_X|GP2X_A)) {\r
cc68a136 1387 menu_options_save();\r
bf098bc5 1388 return 0; // done (update, no write)\r
cc68a136 1389 }\r
cc68a136 1390 }\r
1391}\r
1392\r
1393// -------------- credits --------------\r
1394\r
1395static void draw_menu_credits(void)\r
1396{\r
a12e0116 1397 int tl_x = 15, tl_y = 64, y;\r
e11c5548 1398 gp2x_pd_clone_buffer2();\r
cc68a136 1399\r
a12e0116 1400 text_out16(tl_x, 20, "PicoDrive v" VERSION " (c) notaz, 2006,2007");\r
cc68a136 1401 y = tl_y;\r
a12e0116 1402 text_out16(tl_x, y, "Credits:");\r
1403 text_out16(tl_x, (y+=10), "Dave: Cyclone 68000 core,");\r
1404 text_out16(tl_x, (y+=10), " base code of PicoDrive");\r
1405 text_out16(tl_x, (y+=10), "Reesy & FluBBa: DrZ80 core");\r
1406 text_out16(tl_x, (y+=10), "MAME devs: YM2612 and SN76496 cores");\r
1407 text_out16(tl_x, (y+=10), "Charles MacDonald: Genesis hw docs");\r
1408 text_out16(tl_x, (y+=10), "Stephane Dallongeville:");\r
1409 text_out16(tl_x, (y+=10), " opensource Gens");\r
1410 text_out16(tl_x, (y+=10), "Haze: Genesis hw info");\r
1411 text_out16(tl_x, (y+=10), "rlyeh and others: minimal SDK");\r
1412 text_out16(tl_x, (y+=10), "Squidge: squidgehack");\r
1413 text_out16(tl_x, (y+=10), "Dzz: ARM940 sample");\r
1414 text_out16(tl_x, (y+=10), "GnoStiC / Puck2099: USB joystick");\r
1415 text_out16(tl_x, (y+=10), "craigix: GP2X hardware");\r
a4f0cc86 1416 text_out16(tl_x, (y+=10), "ketchupgun: skin design");\r
a12e0116 1417\r
1418 menu_flip();\r
cc68a136 1419}\r
1420\r
1421\r
1422// -------------- root menu --------------\r
1423\r
4e8a534c 1424menu_entry main_entries[] =\r
1425{\r
1426 { "Resume game", MB_NONE, MA_MAIN_RESUME_GAME, NULL, 0, 0, 0, 0 },\r
1427 { "Save State", MB_NONE, MA_MAIN_SAVE_STATE, NULL, 0, 0, 0, 0 },\r
1428 { "Load State", MB_NONE, MA_MAIN_LOAD_STATE, NULL, 0, 0, 0, 0 },\r
1429 { "Reset game", MB_NONE, MA_MAIN_RESET_GAME, NULL, 0, 0, 0, 0 },\r
1430 { "Load new ROM/ISO", MB_NONE, MA_MAIN_LOAD_ROM, NULL, 0, 0, 0, 1 },\r
1431 { "Change options", MB_NONE, MA_MAIN_OPTIONS, NULL, 0, 0, 0, 1 },\r
1432 { "Configure controls", MB_NONE, MA_MAIN_CONTROLS, NULL, 0, 0, 0, 1 },\r
1433 { "Credits", MB_NONE, MA_MAIN_CREDITS, NULL, 0, 0, 0, 1 },\r
1434 { "Patches / GameGenie",MB_NONE, MA_MAIN_PATCHES, NULL, 0, 0, 0, 0 },\r
1435 { "Exit", MB_NONE, MA_MAIN_EXIT, NULL, 0, 0, 0, 1 }\r
1436};\r
1437\r
1438#define MAIN_ENTRY_COUNT (sizeof(main_entries) / sizeof(main_entries[0]))\r
1439\r
cc68a136 1440static void draw_menu_root(int menu_sel)\r
1441{\r
4e8a534c 1442 const int tl_x = 70, tl_y = 70;\r
1443\r
e11c5548 1444 gp2x_pd_clone_buffer2();\r
cc68a136 1445\r
a12e0116 1446 text_out16(tl_x, 20, "PicoDrive v" VERSION);\r
1447\r
e5f426aa 1448 menu_draw_selection(tl_x - 16, tl_y + menu_sel*10, 146);\r
cc68a136 1449\r
4e8a534c 1450 me_draw(main_entries, MAIN_ENTRY_COUNT, tl_x, tl_y, NULL, NULL);\r
cc68a136 1451\r
cc68a136 1452 // error\r
a12e0116 1453 if (menuErrorMsg[0]) {\r
1454 memset((char *)gp2x_screen + 320*224*2, 0, 320*16*2);\r
1455 text_out16(5, 226, menuErrorMsg);\r
1456 }\r
1457 menu_flip();\r
cc68a136 1458}\r
1459\r
1460\r
1461static void menu_loop_root(void)\r
1462{\r
4e8a534c 1463 static int menu_sel = 0;\r
1464 int ret, menu_sel_max;\r
cc68a136 1465 unsigned long inp = 0;\r
cc68a136 1466\r
4e8a534c 1467 me_enable(main_entries, MAIN_ENTRY_COUNT, MA_MAIN_RESUME_GAME, rom_data != NULL);\r
1468 me_enable(main_entries, MAIN_ENTRY_COUNT, MA_MAIN_SAVE_STATE, rom_data != NULL);\r
1469 me_enable(main_entries, MAIN_ENTRY_COUNT, MA_MAIN_LOAD_STATE, rom_data != NULL);\r
1470 me_enable(main_entries, MAIN_ENTRY_COUNT, MA_MAIN_RESET_GAME, rom_data != NULL);\r
1471 me_enable(main_entries, MAIN_ENTRY_COUNT, MA_MAIN_PATCHES, PicoPatches != NULL);\r
1472\r
1473 menu_sel_max = me_count_enabled(main_entries, MAIN_ENTRY_COUNT) - 1;\r
d524c827 1474 if (menu_sel > menu_sel_max) menu_sel = menu_sel_max;\r
cc68a136 1475\r
2433f409 1476 /* make sure action buttons are not pressed on entering menu */\r
1477 draw_menu_root(menu_sel);\r
1478 while (gp2x_joystick_read(1) & (GP2X_B|GP2X_X|GP2X_SELECT)) usleep(50*1000);\r
1479\r
1480 for (;;)\r
cc68a136 1481 {\r
1482 draw_menu_root(menu_sel);\r
0af33fe0 1483 inp = wait_for_input(GP2X_UP|GP2X_DOWN|GP2X_B|GP2X_X|GP2X_SELECT|GP2X_L|GP2X_R);\r
4e8a534c 1484 if(inp & GP2X_UP ) { menu_sel--; if (menu_sel < 0) menu_sel = menu_sel_max; }\r
1485 if(inp & GP2X_DOWN) { menu_sel++; if (menu_sel > menu_sel_max) menu_sel = 0; }\r
0af33fe0 1486 if((inp & (GP2X_L|GP2X_R)) == (GP2X_L|GP2X_R)) debug_menu_loop();\r
cc68a136 1487 if(inp &(GP2X_SELECT|GP2X_X)){\r
1488 if (rom_data) {\r
1489 while (gp2x_joystick_read(1) & (GP2X_SELECT|GP2X_X)) usleep(50*1000); // wait until select is released\r
1490 engineState = PGS_Running;\r
1491 break;\r
1492 }\r
1493 }\r
4e8a534c 1494 if(inp & GP2X_B) {\r
1495 switch (me_index2id(main_entries, MAIN_ENTRY_COUNT, menu_sel))\r
1496 {\r
1497 case MA_MAIN_RESUME_GAME:\r
721cd396 1498 if (rom_data) {\r
1499 while (gp2x_joystick_read(1) & GP2X_B) usleep(50*1000);\r
1500 engineState = PGS_Running;\r
1501 return;\r
1502 }\r
cc68a136 1503 break;\r
4e8a534c 1504 case MA_MAIN_SAVE_STATE:\r
cc68a136 1505 if (rom_data) {\r
860c6322 1506 if(savestate_menu_loop(0))\r
cc68a136 1507 continue;\r
cc68a136 1508 engineState = PGS_Running;\r
1509 return;\r
1510 }\r
1511 break;\r
4e8a534c 1512 case MA_MAIN_LOAD_STATE:\r
cc68a136 1513 if (rom_data) {\r
860c6322 1514 if(savestate_menu_loop(1))\r
cc68a136 1515 continue;\r
cc68a136 1516 engineState = PGS_Running;\r
1517 return;\r
1518 }\r
1519 break;\r
4e8a534c 1520 case MA_MAIN_RESET_GAME:\r
cc68a136 1521 if (rom_data) {\r
1522 emu_ResetGame();\r
1523 engineState = PGS_Running;\r
1524 return;\r
1525 }\r
1526 break;\r
4e8a534c 1527 case MA_MAIN_LOAD_ROM:\r
a12e0116 1528 {\r
1529 char curr_path[PATH_MAX], *selfname;\r
1530 FILE *tstf;\r
1531 if ( (tstf = fopen(currentConfig.lastRomFile, "rb")) )\r
1532 {\r
1533 fclose(tstf);\r
1534 strcpy(curr_path, currentConfig.lastRomFile);\r
1535 }\r
1536 else\r
1537 getcwd(curr_path, PATH_MAX);\r
cc68a136 1538 selfname = romsel_loop(curr_path);\r
1539 if (selfname) {\r
1540 printf("selected file: %s\n", selfname);\r
cc68a136 1541 engineState = PGS_ReloadRom;\r
a12e0116 1542 return;\r
cc68a136 1543 }\r
a12e0116 1544 break;\r
1545 }\r
4e8a534c 1546 case MA_MAIN_OPTIONS:\r
bf098bc5 1547 ret = menu_loop_options();\r
1548 if (ret == 1) continue; // status update\r
1549 if (engineState == PGS_ReloadRom)\r
1550 return; // BIOS test\r
cc68a136 1551 break;\r
4e8a534c 1552 case MA_MAIN_CONTROLS:\r
cc68a136 1553 kc_sel_loop();\r
1554 break;\r
4e8a534c 1555 case MA_MAIN_CREDITS:\r
cc68a136 1556 draw_menu_credits();\r
1557 usleep(500*1000);\r
1558 inp = wait_for_input(GP2X_B|GP2X_X);\r
1559 break;\r
4e8a534c 1560 case MA_MAIN_EXIT:\r
cc68a136 1561 engineState = PGS_Quit;\r
1562 return;\r
4e8a534c 1563 case MA_MAIN_PATCHES:\r
b67ef287 1564 if (rom_data && PicoPatches) {\r
1565 patches_menu_loop();\r
1566 PicoPatchApply();\r
1567 strcpy(menuErrorMsg, "Patches applied");\r
1568 continue;\r
1569 }\r
1570 break;\r
4e8a534c 1571 default:\r
1572 printf("%s: something unknown selected\n", __FUNCTION__);\r
1573 break;\r
cc68a136 1574 }\r
1575 }\r
1576 menuErrorMsg[0] = 0; // clear error msg\r
1577 }\r
1578}\r
1579\r
a4f0cc86 1580static void menu_darken_bg(void *dst, int pixels, int darker)\r
cc68a136 1581{\r
a12e0116 1582 unsigned int *screen = dst;\r
1583 pixels /= 2;\r
a4f0cc86 1584 if (darker)\r
a12e0116 1585 {\r
a4f0cc86 1586 while (pixels--)\r
1587 {\r
1588 unsigned int p = *screen;\r
1589 *screen++ = ((p&0xf79ef79e)>>1) - ((p&0xc618c618)>>3);\r
1590 }\r
1591 }\r
1592 else\r
1593 {\r
1594 while (pixels--)\r
1595 {\r
1596 unsigned int p = *screen;\r
1597 *screen++ = (p&0xf79ef79e)>>1;\r
1598 }\r
a12e0116 1599 }\r
1600}\r
e11c5548 1601\r
a12e0116 1602static void menu_prepare_bg(int use_game_bg)\r
1603{\r
1604 if (use_game_bg)\r
1605 {\r
1606 // darken the active framebuffer\r
1607 memset(gp2x_screen, 0, 320*8*2);\r
a4f0cc86 1608 menu_darken_bg((char *)gp2x_screen + 320*8*2, 320*224, 1);\r
a12e0116 1609 memset((char *)gp2x_screen + 320*232*2, 0, 320*8*2);\r
1610 }\r
1611 else\r
1612 {\r
1613 // should really only happen once, on startup..\r
1614 readpng(gp2x_screen, "skin/background.png", READPNG_BG);\r
860c6322 1615 }\r
cc68a136 1616\r
a12e0116 1617 // copy to buffer2\r
1618 gp2x_memcpy_buffers((1<<2), gp2x_screen, 0, 320*240*2);\r
860c6322 1619}\r
1620\r
1621static void menu_gfx_prepare(void)\r
1622{\r
a12e0116 1623 menu_prepare_bg(rom_data != NULL);\r
860c6322 1624\r
a12e0116 1625 // switch to 16bpp\r
1626 gp2x_video_changemode2(16);\r
2433f409 1627 gp2x_video_RGB_setscaling(0, 320, 240);\r
a12e0116 1628 menu_flip();\r
e11c5548 1629}\r
1630\r
1631\r
1632void menu_loop(void)\r
1633{\r
1634 menu_gfx_prepare();\r
cc68a136 1635\r
1636 menu_loop_root();\r
1637\r
1638 menuErrorMsg[0] = 0;\r
1639}\r
721cd396 1640\r
1641\r
1642// --------- CD tray close menu ----------\r
1643\r
1644static void draw_menu_tray(int menu_sel)\r
1645{\r
1646 int tl_x = 70, tl_y = 90, y;\r
a12e0116 1647 memset(gp2x_screen, 0, 320*240*2);\r
721cd396 1648\r
a12e0116 1649 text_out16(tl_x, 20, "The unit is about to");\r
1650 text_out16(tl_x, 30, "close the CD tray.");\r
721cd396 1651\r
1652 y = tl_y;\r
a12e0116 1653 text_out16(tl_x, y, "Load new CD image");\r
1654 text_out16(tl_x, (y+=10), "Insert nothing");\r
721cd396 1655\r
1656 // draw cursor\r
a12e0116 1657 text_out16(tl_x - 16, tl_y + menu_sel*10, ">");\r
721cd396 1658 // error\r
a12e0116 1659 if (menuErrorMsg[0]) text_out16(5, 226, menuErrorMsg);\r
1660 menu_flip();\r
721cd396 1661}\r
1662\r
1663\r
1664int menu_loop_tray(void)\r
1665{\r
1666 int menu_sel = 0, menu_sel_max = 1;\r
1667 unsigned long inp = 0;\r
1668 char curr_path[PATH_MAX], *selfname;\r
1669 FILE *tstf;\r
1670\r
e5f426aa 1671 gp2x_memset_all_buffers(0, 0, 320*240*2);\r
721cd396 1672 menu_gfx_prepare();\r
1673\r
1674 if ( (tstf = fopen(currentConfig.lastRomFile, "rb")) )\r
1675 {\r
1676 fclose(tstf);\r
1677 strcpy(curr_path, currentConfig.lastRomFile);\r
1678 }\r
1679 else\r
1680 {\r
1681 getcwd(curr_path, PATH_MAX);\r
1682 }\r
1683\r
1684 /* make sure action buttons are not pressed on entering menu */\r
1685 draw_menu_tray(menu_sel);\r
1686 while (gp2x_joystick_read(1) & GP2X_B) usleep(50*1000);\r
1687\r
1688 for (;;)\r
1689 {\r
1690 draw_menu_tray(menu_sel);\r
1691 inp = wait_for_input(GP2X_UP|GP2X_DOWN|GP2X_B);\r
1692 if(inp & GP2X_UP ) { menu_sel--; if (menu_sel < 0) menu_sel = menu_sel_max; }\r
1693 if(inp & GP2X_DOWN) { menu_sel++; if (menu_sel > menu_sel_max) menu_sel = 0; }\r
1694 if(inp & GP2X_B ) {\r
1695 switch (menu_sel) {\r
1696 case 0: // select image\r
1697 selfname = romsel_loop(curr_path);\r
1698 if (selfname) {\r
1699 int ret = -1, cd_type;\r
1700 cd_type = emu_cd_check(NULL);\r
1701 if (cd_type > 0)\r
1702 ret = Insert_CD(romFileName, cd_type == 2);\r
1703 if (ret != 0) {\r
1704 sprintf(menuErrorMsg, "Load failed, invalid CD image?");\r
1705 printf("%s\n", menuErrorMsg);\r
1706 continue;\r
1707 }\r
1708 engineState = PGS_RestartRun;\r
1709 return 1;\r
1710 }\r
1711 break;\r
1712 case 1: // insert nothing\r
1713 engineState = PGS_RestartRun;\r
1714 return 0;\r
1715 }\r
1716 }\r
1717 menuErrorMsg[0] = 0; // clear error msg\r
1718 }\r
1719}\r
1720\r
1721\r