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