key config adjustment
[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
d524c827 620 return strkeys;\r
621}\r
622\r
b4fe3a4a 623static void unbind_action(int action, int pl_idx)\r
d524c827 624{\r
625 int i, u;\r
626\r
b4fe3a4a 627 for (i = 0; i < 32; i++) {\r
628 if (pl_idx >= 0 && (currentConfig.KeyBinds[i]&0x30000) != (pl_idx<<16)) continue;\r
d524c827 629 currentConfig.KeyBinds[i] &= ~action;\r
b4fe3a4a 630 }\r
d524c827 631 for (u = 0; u < 4; u++)\r
b4fe3a4a 632 for (i = 0; i < 32; i++) {\r
633 if (pl_idx >= 0 && (currentConfig.JoyBinds[u][i]&0x30000) != (pl_idx<<16)) continue;\r
d524c827 634 currentConfig.JoyBinds[u][i] &= ~action;\r
b4fe3a4a 635 }\r
d524c827 636}\r
637\r
b4fe3a4a 638static int count_bound_keys(int action, int pl_idx, int joy)\r
d524c827 639{\r
640 int i, keys = 0;\r
641\r
642 if (joy)\r
643 {\r
b4fe3a4a 644 for (i = 0; i < 32; i++) {\r
645 if (pl_idx >= 0 && (currentConfig.JoyBinds[joy-1][i]&0x30000) != (pl_idx<<16)) continue;\r
d524c827 646 if (currentConfig.JoyBinds[joy-1][i] & action) keys++;\r
b4fe3a4a 647 }\r
d524c827 648 }\r
649 else\r
650 {\r
b4fe3a4a 651 for (i = 0; i < 32; i++) {\r
652 if (pl_idx >= 0 && (currentConfig.KeyBinds[i]&0x30000) != (pl_idx<<16)) continue;\r
d524c827 653 if (currentConfig.KeyBinds[i] & action) keys++;\r
b4fe3a4a 654 }\r
d524c827 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
e11c5548 665 gp2x_pd_clone_buffer2();\r
d524c827 666 if (player_idx >= 0) {\r
a12e0116 667 text_out16(80, 20, "Player %i controls", player_idx + 1);\r
b4fe3a4a 668 x = 80;\r
d524c827 669 } else {\r
a12e0116 670 text_out16(80, 20, "Emulator controls");\r
d524c827 671 x = 40;\r
672 }\r
673\r
e5f426aa 674 menu_draw_selection(x - 16, tl_y + sel*10, (player_idx >= 0) ? 66 : 130);\r
a12e0116 675\r
d524c827 676 y = tl_y;\r
677 for (i = 0; i < opt_cnt; i++, y+=10)\r
a12e0116 678 text_out16(x, y, "%s : %s", opts[i].name, action_binds(player_idx, opts[i].mask));\r
d524c827 679\r
a12e0116 680 text_out16(x, y, "Done");\r
d524c827 681\r
682 if (sel < opt_cnt) {\r
a12e0116 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
d524c827 687 } else {\r
a12e0116 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
d524c827 691 }\r
a12e0116 692 menu_flip();\r
cc68a136 693}\r
694\r
d524c827 695static void key_config_loop(const bind_action_t *opts, int opt_cnt, int player_idx)\r
cc68a136 696{\r
d524c827 697 int joy = 0, sel = 0, menu_sel_max = opt_cnt, prev_select = 0, i;\r
cc68a136 698 unsigned long inp = 0;\r
699\r
700 for (;;)\r
701 {\r
d524c827 702 draw_key_config(opts, opt_cnt, player_idx, sel);\r
cc68a136 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
d524c827 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
cc68a136 710 }\r
d524c827 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
b4fe3a4a 717 unbind_action(opts[sel].mask, player_idx);\r
d524c827 718 prev_select = inp & GP2X_SELECT;\r
cc68a136 719 inp &= CONFIGURABLE_KEYS;\r
720 inp &= ~GP2X_SELECT;\r
cc68a136 721 for (i = 0; i < 32; i++)\r
722 if (inp & (1 << i)) {\r
b4fe3a4a 723 if (count_bound_keys(opts[sel].mask, player_idx, 0) >= 2)\r
d524c827 724 currentConfig.KeyBinds[i] &= ~opts[sel].mask; // allow to unbind only\r
725 else currentConfig.KeyBinds[i] ^= opts[sel].mask;\r
b4fe3a4a 726 if (player_idx >= 0 && currentConfig.KeyBinds[i] & opts[sel].mask) {\r
d524c827 727 currentConfig.KeyBinds[i] &= ~(3 << 16);\r
728 currentConfig.KeyBinds[i] |= player_idx << 16;\r
729 }\r
cc68a136 730 }\r
d524c827 731 }\r
732 else if (sel < opt_cnt)\r
733 {\r
cc68a136 734 for (i = 0; i < 32; i++)\r
735 if (inp & (1 << i)) {\r
b4fe3a4a 736 if (count_bound_keys(opts[sel].mask, player_idx, joy) >= 1) // disallow combos for usbjoy\r
d524c827 737 currentConfig.JoyBinds[joy-1][i] &= ~opts[sel].mask;\r
738 else currentConfig.JoyBinds[joy-1][i] ^= opts[sel].mask;\r
b4fe3a4a 739 if (player_idx >= 0 && currentConfig.JoyBinds[joy-1][i] & opts[sel].mask) {\r
d524c827 740 currentConfig.JoyBinds[joy-1][i] &= ~(3 << 16);\r
741 currentConfig.JoyBinds[joy-1][i] |= player_idx << 16;\r
742 }\r
cc68a136 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
e11c5548 754 gp2x_pd_clone_buffer2();\r
e5f426aa 755 menu_draw_selection(tl_x - 16, tl_y + menu_sel*10, 138);\r
cc68a136 756\r
a12e0116 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
cc68a136 761\r
762 tl_x = 25;\r
a12e0116 763 text_out16(tl_x, (y=110), "USB joys detected:");\r
cc68a136 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
a12e0116 767 text_out16(tl_x, (y+=10), "%i: %s", i+1, joyname);\r
cc68a136 768 }\r
769 } else {\r
a12e0116 770 text_out16(tl_x, (y+=10), "none");\r
cc68a136 771 }\r
772\r
a12e0116 773 menu_flip();\r
cc68a136 774}\r
775\r
d524c827 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
d524c827 799 { "Load State ", 1<<28 },\r
800 { "Save State ", 1<<27 },\r
d524c827 801 { "Prev Save Slot ", 1<<25 },\r
802 { "Next Save Slot ", 1<<24 },\r
a12e0116 803 { "Switch Renderer", 1<<26 },\r
804 { "Volume Down ", 1<<30 },\r
805 { "Volume Up ", 1<<29 },\r
d524c827 806 { "Enter Menu ", 1<<23 },\r
807};\r
808\r
cc68a136 809static void kc_sel_loop(void)\r
810{\r
d524c827 811 int menu_sel = 3, menu_sel_max = 3;\r
cc68a136 812 unsigned long inp = 0;\r
d524c827 813 int is_6button = currentConfig.PicoOpt & 0x020;\r
cc68a136 814\r
d524c827 815 while (1)\r
cc68a136 816 {\r
817 draw_kc_sel(menu_sel);\r
818 inp = wait_for_input(GP2X_UP|GP2X_DOWN|GP2X_B|GP2X_X);\r
d524c827 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
cc68a136 822 switch (menu_sel) {\r
d524c827 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
cc68a136 828 default: return;\r
829 }\r
830 }\r
d524c827 831 if (inp & GP2X_X) return;\r
cc68a136 832 }\r
833}\r
834\r
835\r
bf098bc5 836// --------- sega/mega cd options ----------\r
837\r
4e8a534c 838menu_entry cdopt_entries[] =\r
bf098bc5 839{\r
4e8a534c 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
a12e0116 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
4e8a534c 871 case MA_CDOPT_READAHEAD:\r
872 if (PicoCDBuffers > 1) sprintf(ra_buff, "%5iK", PicoCDBuffers * 2);\r
873 else strcpy(ra_buff, " OFF");\r
a12e0116 874 text_out16(x, y, "ReadAhead buffer %s", ra_buff);\r
4e8a534c 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
0a051f55 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
bf098bc5 888\r
e11c5548 889 gp2x_pd_clone_buffer2();\r
890\r
e5f426aa 891 menu_draw_selection(tl_x - 16, tl_y + menu_sel*10, 246);\r
bf098bc5 892\r
a12e0116 893 me_draw(cdopt_entries, CDOPT_ENTRY_COUNT, tl_x, tl_y, menu_cdopt_cust_draw, bios_names);\r
bf098bc5 894\r
4e8a534c 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
a12e0116 899 text_out16(tl_x, 210, "Press start to test selected BIOS");\r
bf098bc5 900\r
a12e0116 901 menu_flip();\r
bf098bc5 902}\r
903\r
904static void cd_menu_loop_options(void)\r
905{\r
aae35e84 906 static int menu_sel = 0;\r
907 int menu_sel_max = 10;\r
bf098bc5 908 unsigned long inp = 0;\r
4e8a534c 909 struct bios_names_t bios_names;\r
910 menu_id selected_id;\r
911 char *bios, *p;\r
bf098bc5 912\r
913 if (find_bios(4, &bios)) { // US\r
914 for (p = bios+strlen(bios)-1; p > bios && *p != '/'; p--); p++;\r
4e8a534c 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
bf098bc5 917\r
918 if (find_bios(8, &bios)) { // EU\r
919 for (p = bios+strlen(bios)-1; p > bios && *p != '/'; p--); p++;\r
4e8a534c 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
bf098bc5 922\r
923 if (find_bios(1, &bios)) { // JP\r
924 for (p = bios+strlen(bios)-1; p > bios && *p != '/'; p--); p++;\r
4e8a534c 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
bf098bc5 927\r
928 for(;;)\r
929 {\r
4e8a534c 930 draw_cd_menu_options(menu_sel, &bios_names);\r
bf098bc5 931 inp = wait_for_input(GP2X_UP|GP2X_DOWN|GP2X_LEFT|GP2X_RIGHT|GP2X_B|GP2X_X|GP2X_A|GP2X_START);\r
4e8a534c 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
bf098bc5 946 }\r
947 }\r
4e8a534c 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
bf098bc5 958 strcpy(romFileName, bios);\r
959 engineState = PGS_ReloadRom;\r
960 return;\r
961 }\r
962 break;\r
4e8a534c 963 case MA_CDOPT_TESTBIOS_EUR:\r
964 if (find_bios(8, &bios)) { // test EU\r
bf098bc5 965 strcpy(romFileName, bios);\r
966 engineState = PGS_ReloadRom;\r
967 return;\r
968 }\r
969 break;\r
4e8a534c 970 case MA_CDOPT_TESTBIOS_JAP:\r
971 if (find_bios(1, &bios)) { // test JP\r
bf098bc5 972 strcpy(romFileName, bios);\r
973 engineState = PGS_ReloadRom;\r
974 return;\r
975 }\r
976 break;\r
4e8a534c 977 default:\r
978 break;\r
bf098bc5 979 }\r
980 }\r
4e8a534c 981 if (inp & (GP2X_X|GP2X_A)) return;\r
bf098bc5 982 }\r
983}\r
984\r
985\r
986// --------- advanced options ----------\r
987\r
4e8a534c 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
e5f426aa 992 { "Perfect vsync", MB_ONOFF, MA_OPT2_VSYNC, &currentConfig.EmuOpt, 0x2000, 0, 0, 1 },\r
4e8a534c 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
a12e0116 1009 text_out16(x, y, "Gamma correction %i.%02i", currentConfig.gamma / 100, currentConfig.gamma%100);\r
4e8a534c 1010 else if (entry->id == MA_OPT2_SQUIDGEHACK)\r
a12e0116 1011 text_out16(x, y, "squidgehack (now %s %s", mmuhack_status ? "active) " : "inactive)",\r
4e8a534c 1012 (currentConfig.EmuOpt&0x0010)?"ON":"OFF");\r
1013}\r
1014\r
1015\r
cc68a136 1016static void draw_amenu_options(int menu_sel)\r
1017{\r
4e8a534c 1018 int tl_x = 25, tl_y = 50;\r
cc68a136 1019\r
e11c5548 1020 gp2x_pd_clone_buffer2();\r
1021\r
e5f426aa 1022 menu_draw_selection(tl_x - 16, tl_y + menu_sel*10, 252);\r
cc68a136 1023\r
a12e0116 1024 me_draw(opt2_entries, OPT2_ENTRY_COUNT, tl_x, tl_y, menu_opt2_cust_draw, NULL);\r
cc68a136 1025\r
a12e0116 1026 menu_flip();\r
cc68a136 1027}\r
1028\r
1029static void amenu_loop_options(void)\r
1030{\r
aae35e84 1031 static int menu_sel = 0;\r
4e8a534c 1032 int menu_sel_max;\r
cc68a136 1033 unsigned long inp = 0;\r
4e8a534c 1034 menu_id selected_id;\r
1035\r
1036 menu_sel_max = me_count_enabled(opt2_entries, OPT2_ENTRY_COUNT) - 1;\r
cc68a136 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
4e8a534c 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
cc68a136 1055 }\r
1056 }\r
4e8a534c 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
cc68a136 1061 }\r
1062 }\r
4e8a534c 1063 if (inp & (GP2X_X|GP2X_A)) return;\r
cc68a136 1064 }\r
1065}\r
1066\r
1067// -------------- options --------------\r
1068\r
4e8a534c 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
51a902ae 1097static const char *region_name(unsigned int code)\r
cc68a136 1098{\r
51a902ae 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
cc68a136 1117}\r
1118\r
4e8a534c 1119\r
1120static void menu_opt_cust_draw(const menu_entry *entry, int x, int y, void *param)\r
cc68a136 1121{\r
4e8a534c 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
a12e0116 1133 text_out16(x, y, "Renderer: %s", str);\r
4e8a534c 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
a12e0116 1142 text_out16(x, y, "Scaling: %s", str);\r
4e8a534c 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
a12e0116 1148 text_out16(x, y, "Frameskip %s", str24);\r
4e8a534c 1149 break;\r
1150 case MA_OPT_SOUND_QUALITY:\r
1151 str = (currentConfig.PicoOpt&0x08)?"stereo":"mono";\r
a12e0116 1152 text_out16(x, y, "Sound Quality: %5iHz %s", currentConfig.PsndRate, str);\r
4e8a534c 1153 break;\r
1154 case MA_OPT_REGION:\r
a12e0116 1155 text_out16(x, y, "Region: %s", region_name(currentConfig.PicoRegion));\r
4e8a534c 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
a12e0116 1164 text_out16(x, y, "Confirm savestate %s", str);\r
4e8a534c 1165 break;\r
1166 case MA_OPT_CPU_CLOCKS:\r
a12e0116 1167 text_out16(x, y, "GP2X CPU clocks %iMhz", currentConfig.CPUclock);\r
4e8a534c 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
a12e0116 1172 text_out16(x, y, "Save cfg as default%s", str24);\r
4e8a534c 1173 break;\r
1174 case MA_OPT_LOADCFG:\r
a12e0116 1175 text_out16(x, y, "Load cfg from profile %i", config_slot);\r
4e8a534c 1176 break;\r
1177 default:\r
1178 printf("%s: unimplemented (%i)\n", __FUNCTION__, entry->id);\r
1179 break;\r
46969540 1180 }\r
4e8a534c 1181}\r
1182\r
1183\r
1184\r
1185static void draw_menu_options(int menu_sel)\r
1186{\r
a12e0116 1187 int tl_x = 25, tl_y = 24;\r
cc68a136 1188\r
e11c5548 1189 gp2x_pd_clone_buffer2();\r
1190\r
e5f426aa 1191 menu_draw_selection(tl_x - 16, tl_y + menu_sel*10, 284);\r
cc68a136 1192\r
a12e0116 1193 me_draw(opt_entries, OPT_ENTRY_COUNT, tl_x, tl_y, menu_opt_cust_draw, NULL);\r
cc68a136 1194\r
a12e0116 1195 menu_flip();\r
cc68a136 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
51a902ae 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
cc68a136 1235static void menu_options_save(void)\r
1236{\r
cc68a136 1237 PicoOpt = currentConfig.PicoOpt;\r
1238 PsndRate = currentConfig.PsndRate;\r
1239 PicoRegionOverride = currentConfig.PicoRegion;\r
d524c827 1240 if (!(PicoOpt & 0x20)) {\r
1241 // unbind XYZ MODE, just in case\r
b4fe3a4a 1242 unbind_action(0xf00, -1);\r
cc68a136 1243 }\r
2433f409 1244 scaling_update();\r
cc68a136 1245}\r
1246\r
bf098bc5 1247static int menu_loop_options(void)\r
cc68a136 1248{\r
aae35e84 1249 static int menu_sel = 0;\r
4e8a534c 1250 int menu_sel_max, ret;\r
cc68a136 1251 unsigned long inp = 0;\r
4e8a534c 1252 menu_id selected_id;\r
cc68a136 1253\r
51a902ae 1254 currentConfig.PicoOpt = PicoOpt;\r
1255 currentConfig.PsndRate = PsndRate;\r
1256 currentConfig.PicoRegion = PicoRegionOverride;\r
cc68a136 1257\r
4e8a534c 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
d524c827 1261 if (menu_sel > menu_sel_max) menu_sel = menu_sel_max;\r
4e8a534c 1262\r
1263 while (1)\r
cc68a136 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
4e8a534c 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
d524c827 1357 ret = emu_ReadConfig(1, 1);\r
1358 if (!ret) ret = emu_ReadConfig(0, 1);\r
1359 if (ret) strcpy(menuErrorMsg, "config loaded");\r
4e8a534c 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
cc68a136 1366 }\r
1367 }\r
51a902ae 1368 if(inp & (GP2X_X|GP2X_A)) {\r
cc68a136 1369 menu_options_save();\r
bf098bc5 1370 return 0; // done (update, no write)\r
cc68a136 1371 }\r
cc68a136 1372 }\r
1373}\r
1374\r
1375// -------------- credits --------------\r
1376\r
1377static void draw_menu_credits(void)\r
1378{\r
a12e0116 1379 int tl_x = 15, tl_y = 64, y;\r
e11c5548 1380 gp2x_pd_clone_buffer2();\r
cc68a136 1381\r
a12e0116 1382 text_out16(tl_x, 20, "PicoDrive v" VERSION " (c) notaz, 2006,2007");\r
cc68a136 1383 y = tl_y;\r
a12e0116 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
a4f0cc86 1398 text_out16(tl_x, (y+=10), "ketchupgun: skin design");\r
a12e0116 1399\r
1400 menu_flip();\r
cc68a136 1401}\r
1402\r
1403\r
1404// -------------- root menu --------------\r
1405\r
4e8a534c 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
cc68a136 1422static void draw_menu_root(int menu_sel)\r
1423{\r
4e8a534c 1424 const int tl_x = 70, tl_y = 70;\r
1425\r
e11c5548 1426 gp2x_pd_clone_buffer2();\r
cc68a136 1427\r
a12e0116 1428 text_out16(tl_x, 20, "PicoDrive v" VERSION);\r
1429\r
e5f426aa 1430 menu_draw_selection(tl_x - 16, tl_y + menu_sel*10, 146);\r
cc68a136 1431\r
4e8a534c 1432 me_draw(main_entries, MAIN_ENTRY_COUNT, tl_x, tl_y, NULL, NULL);\r
cc68a136 1433\r
cc68a136 1434 // error\r
a12e0116 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
cc68a136 1440}\r
1441\r
1442\r
1443static void menu_loop_root(void)\r
1444{\r
4e8a534c 1445 static int menu_sel = 0;\r
1446 int ret, menu_sel_max;\r
cc68a136 1447 unsigned long inp = 0;\r
cc68a136 1448\r
4e8a534c 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
d524c827 1456 if (menu_sel > menu_sel_max) menu_sel = menu_sel_max;\r
cc68a136 1457\r
2433f409 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
cc68a136 1463 {\r
1464 draw_menu_root(menu_sel);\r
0af33fe0 1465 inp = wait_for_input(GP2X_UP|GP2X_DOWN|GP2X_B|GP2X_X|GP2X_SELECT|GP2X_L|GP2X_R);\r
4e8a534c 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
0af33fe0 1468 if((inp & (GP2X_L|GP2X_R)) == (GP2X_L|GP2X_R)) debug_menu_loop();\r
cc68a136 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
4e8a534c 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
721cd396 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
cc68a136 1485 break;\r
4e8a534c 1486 case MA_MAIN_SAVE_STATE:\r
cc68a136 1487 if (rom_data) {\r
860c6322 1488 if(savestate_menu_loop(0))\r
cc68a136 1489 continue;\r
cc68a136 1490 engineState = PGS_Running;\r
1491 return;\r
1492 }\r
1493 break;\r
4e8a534c 1494 case MA_MAIN_LOAD_STATE:\r
cc68a136 1495 if (rom_data) {\r
860c6322 1496 if(savestate_menu_loop(1))\r
cc68a136 1497 continue;\r
cc68a136 1498 engineState = PGS_Running;\r
1499 return;\r
1500 }\r
1501 break;\r
4e8a534c 1502 case MA_MAIN_RESET_GAME:\r
cc68a136 1503 if (rom_data) {\r
1504 emu_ResetGame();\r
1505 engineState = PGS_Running;\r
1506 return;\r
1507 }\r
1508 break;\r
4e8a534c 1509 case MA_MAIN_LOAD_ROM:\r
a12e0116 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
cc68a136 1520 selfname = romsel_loop(curr_path);\r
1521 if (selfname) {\r
1522 printf("selected file: %s\n", selfname);\r
cc68a136 1523 engineState = PGS_ReloadRom;\r
a12e0116 1524 return;\r
cc68a136 1525 }\r
a12e0116 1526 break;\r
1527 }\r
4e8a534c 1528 case MA_MAIN_OPTIONS:\r
bf098bc5 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
cc68a136 1533 break;\r
4e8a534c 1534 case MA_MAIN_CONTROLS:\r
cc68a136 1535 kc_sel_loop();\r
1536 break;\r
4e8a534c 1537 case MA_MAIN_CREDITS:\r
cc68a136 1538 draw_menu_credits();\r
1539 usleep(500*1000);\r
1540 inp = wait_for_input(GP2X_B|GP2X_X);\r
1541 break;\r
4e8a534c 1542 case MA_MAIN_EXIT:\r
cc68a136 1543 engineState = PGS_Quit;\r
1544 return;\r
4e8a534c 1545 case MA_MAIN_PATCHES:\r
b67ef287 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
4e8a534c 1553 default:\r
1554 printf("%s: something unknown selected\n", __FUNCTION__);\r
1555 break;\r
cc68a136 1556 }\r
1557 }\r
1558 menuErrorMsg[0] = 0; // clear error msg\r
1559 }\r
1560}\r
1561\r
a4f0cc86 1562static void menu_darken_bg(void *dst, int pixels, int darker)\r
cc68a136 1563{\r
a12e0116 1564 unsigned int *screen = dst;\r
1565 pixels /= 2;\r
a4f0cc86 1566 if (darker)\r
a12e0116 1567 {\r
a4f0cc86 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
a12e0116 1581 }\r
1582}\r
e11c5548 1583\r
a12e0116 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
a4f0cc86 1590 menu_darken_bg((char *)gp2x_screen + 320*8*2, 320*224, 1);\r
a12e0116 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
860c6322 1597 }\r
cc68a136 1598\r
a12e0116 1599 // copy to buffer2\r
1600 gp2x_memcpy_buffers((1<<2), gp2x_screen, 0, 320*240*2);\r
860c6322 1601}\r
1602\r
1603static void menu_gfx_prepare(void)\r
1604{\r
a12e0116 1605 menu_prepare_bg(rom_data != NULL);\r
860c6322 1606\r
a12e0116 1607 // switch to 16bpp\r
1608 gp2x_video_changemode2(16);\r
2433f409 1609 gp2x_video_RGB_setscaling(0, 320, 240);\r
a12e0116 1610 menu_flip();\r
e11c5548 1611}\r
1612\r
1613\r
1614void menu_loop(void)\r
1615{\r
1616 menu_gfx_prepare();\r
cc68a136 1617\r
1618 menu_loop_root();\r
1619\r
1620 menuErrorMsg[0] = 0;\r
1621}\r
721cd396 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
a12e0116 1629 memset(gp2x_screen, 0, 320*240*2);\r
721cd396 1630\r
a12e0116 1631 text_out16(tl_x, 20, "The unit is about to");\r
1632 text_out16(tl_x, 30, "close the CD tray.");\r
721cd396 1633\r
1634 y = tl_y;\r
a12e0116 1635 text_out16(tl_x, y, "Load new CD image");\r
1636 text_out16(tl_x, (y+=10), "Insert nothing");\r
721cd396 1637\r
1638 // draw cursor\r
a12e0116 1639 text_out16(tl_x - 16, tl_y + menu_sel*10, ">");\r
721cd396 1640 // error\r
a12e0116 1641 if (menuErrorMsg[0]) text_out16(5, 226, menuErrorMsg);\r
1642 menu_flip();\r
721cd396 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
e5f426aa 1653 gp2x_memset_all_buffers(0, 0, 320*240*2);\r
721cd396 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