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