giz menu works
[libpicofe.git] / gp2x / menu.c
CommitLineData
95151aea 1// (c) Copyright 2006,2007 notaz, All rights reserved.\r
720ee7f6 2// Free for non-commercial use.\r
3\r
4// For commercial use, separate licencing terms must be obtained.\r
5\r
6#include <stdio.h>\r
7#include <string.h>\r
8#include <stdlib.h>\r
9#include <stdarg.h>\r
10#include <unistd.h>\r
11#include <dirent.h>\r
12\r
13#include "gp2x.h"\r
14#include "emu.h"\r
15#include "menu.h"\r
16#include "usbjoy.h"\r
c7a4ff64 17#include "../common/arm_utils.h"\r
18#include "../common/menu.h"\r
19#include "../common/readpng.h"\r
720ee7f6 20#include "version.h"\r
21\r
70d2ecc5 22#include <Pico/PicoInt.h>\r
23#include <Pico/Patch.h>\r
24#include <zlib/zlib.h>\r
720ee7f6 25\r
26#ifndef _DIRENT_HAVE_D_TYPE\r
27#error "need d_type for file browser\r
28#endif\r
29\r
720ee7f6 30extern char romFileName[PATH_MAX];\r
31extern char *rom_data;\r
32extern int mmuhack_status;\r
33extern int state_slot;\r
59633198 34extern int config_slot, config_slot_current;\r
720ee7f6 35\r
c7a4ff64 36static const char *gp2xKeyNames[] = {\r
720ee7f6 37 "UP", "???", "LEFT", "???", "DOWN", "???", "RIGHT", "???",\r
38 "START", "SELECT", "L", "R", "A", "B", "X", "Y",\r
39 "???", "???", "???", "???", "???", "???", "VOL DOWN", "VOL UP",\r
40 "???", "???", "???", "PUSH", "???", "???", "???", "???"\r
41};\r
42\r
43char menuErrorMsg[40] = {0, };\r
44\r
f9a37a02 45static void menu_darken_bg(void *dst, int pixels, int darker);\r
13059a60 46static void menu_prepare_bg(int use_game_bg);\r
720ee7f6 47\r
720ee7f6 48static unsigned long inp_prev = 0;\r
49static int inp_prevjoy = 0;\r
50\r
51static unsigned long wait_for_input(unsigned long interesting)\r
52{\r
53 unsigned long ret;\r
5e2e14f2 54 static int repeats = 0, wait = 50*1000;\r
720ee7f6 55 int release = 0, i;\r
56\r
5e2e14f2 57 if (repeats == 2 || repeats == 4) wait /= 2;\r
58 if (repeats == 6) wait = 15 * 1000;\r
720ee7f6 59\r
60 for (i = 0; i < 6 && inp_prev == gp2x_joystick_read(1); i++) {\r
5e2e14f2 61 if (i == 0) repeats++;\r
62 if (wait >= 30*1000) usleep(wait); // usleep sleeps for ~30ms minimum\r
63 else spend_cycles(wait * currentConfig.CPUclock);\r
720ee7f6 64 }\r
65\r
66 while ( !((ret = gp2x_joystick_read(1)) & interesting) ) {\r
67 usleep(50000);\r
68 release = 1;\r
69 }\r
70\r
71 if (release || ret != inp_prev) {\r
72 repeats = 0;\r
5e2e14f2 73 wait = 50*1000;\r
720ee7f6 74 }\r
75 inp_prev = ret;\r
76 inp_prevjoy = 0;\r
77\r
78 // we don't need diagonals in menus\r
79 if ((ret&GP2X_UP) && (ret&GP2X_LEFT)) ret &= ~GP2X_LEFT;\r
80 if ((ret&GP2X_UP) && (ret&GP2X_RIGHT)) ret &= ~GP2X_RIGHT;\r
81 if ((ret&GP2X_DOWN) && (ret&GP2X_LEFT)) ret &= ~GP2X_LEFT;\r
82 if ((ret&GP2X_DOWN) && (ret&GP2X_RIGHT)) ret &= ~GP2X_RIGHT;\r
83\r
84 return ret;\r
85}\r
86\r
87static unsigned long input2_read(unsigned long interesting, int *joy)\r
88{\r
89 unsigned long ret;\r
90 int i;\r
91\r
92 do\r
93 {\r
94 *joy = 0;\r
95 if ((ret = gp2x_joystick_read(0) & interesting)) break;\r
96 gp2x_usbjoy_update();\r
97 for (i = 0; i < num_of_joys; i++) {\r
98 ret = gp2x_usbjoy_check2(i);\r
99 if (ret) { *joy = i + 1; break; }\r
100 }\r
101 if (ret) break;\r
102 }\r
103 while(0);\r
104\r
105 return ret;\r
106}\r
107\r
108// similar to wait_for_input(), but returns joy num\r
109static unsigned long wait_for_input_usbjoy(unsigned long interesting, int *joy)\r
110{\r
111 unsigned long ret;\r
112 const int wait = 300*1000;\r
113 int i;\r
114\r
115 if (inp_prevjoy == 0) inp_prev &= interesting;\r
116 for (i = 0; i < 6; i++) {\r
117 ret = input2_read(interesting, joy);\r
118 if (*joy != inp_prevjoy || ret != inp_prev) break;\r
119 usleep(wait/6);\r
120 }\r
121\r
122 while ( !(ret = input2_read(interesting, joy)) ) {\r
123 usleep(50000);\r
124 }\r
125\r
126 inp_prev = ret;\r
127 inp_prevjoy = *joy;\r
128\r
129 return ret;\r
130}\r
131\r
c7a4ff64 132static void menu_flip(void)\r
133{\r
134 gp2x_video_flush_cache();\r
135 gp2x_video_flip2();\r
136}\r
720ee7f6 137\r
138\r
b9f8cb3d 139// --------- loading ROM screen ----------\r
140\r
141static void load_progress_cb(int percent)\r
142{\r
143 int ln, len = percent * 320 / 100;\r
13059a60 144 unsigned short *dst = (unsigned short *)gp2x_screen + 320*20;\r
b9f8cb3d 145\r
146 if (len > 320) len = 320;\r
147 for (ln = 10; ln > 0; ln--, dst += 320)\r
13059a60 148 memset(dst, 0xff, len*2);\r
149 menu_flip();\r
b9f8cb3d 150}\r
151\r
152void menu_romload_prepare(const char *rom_name)\r
153{\r
154 const char *p = rom_name + strlen(rom_name);\r
155 while (p > rom_name && *p != '/') p--;\r
156\r
13059a60 157 if (rom_data) gp2x_pd_clone_buffer2();\r
158 else memset(gp2x_screen, 0, 320*240*2);\r
159\r
160 smalltext_out16(1, 1, "Loading", 0xffff);\r
161 smalltext_out16_lim(1, 10, p, 0xffff, 53);\r
162 gp2x_memcpy_buffers(3, gp2x_screen, 0, 320*240*2);\r
163 menu_flip();\r
b9f8cb3d 164 PicoCartLoadProgressCB = load_progress_cb;\r
165}\r
166\r
167void menu_romload_end(void)\r
168{\r
169 PicoCartLoadProgressCB = NULL;\r
13059a60 170 smalltext_out16(1, 30, "Starting emulation...", 0xffff);\r
171 menu_flip();\r
b9f8cb3d 172}\r
173\r
720ee7f6 174// -------------- ROM selector --------------\r
175\r
13059a60 176// rrrr rggg gggb bbbb\r
177static unsigned short file2color(const char *fname)\r
178{\r
179 const char *ext = fname + strlen(fname) - 3;\r
180 static const char *rom_exts[] = { "zip", "bin", "smd", "gen", "iso" };\r
181 static const char *other_exts[] = { "gmv", "pat" };\r
182 int i;\r
183\r
184 if (ext < fname) ext = fname;\r
185 for (i = 0; i < sizeof(rom_exts)/sizeof(rom_exts[0]); i++)\r
186 if (strcasecmp(ext, rom_exts[i]) == 0) return 0xbdff;\r
187 for (i = 0; i < sizeof(other_exts)/sizeof(other_exts[0]); i++)\r
188 if (strcasecmp(ext, other_exts[i]) == 0) return 0xaff5;\r
189 return 0xffff;\r
190}\r
191\r
720ee7f6 192static void draw_dirlist(char *curdir, struct dirent **namelist, int n, int sel)\r
193{\r
194 int start, i, pos;\r
195\r
196 start = 12 - sel;\r
197 n--; // exclude current dir (".")\r
198\r
f9a37a02 199 gp2x_pd_clone_buffer2();\r
200\r
201 if (rom_data == NULL) {\r
202 menu_darken_bg(gp2x_screen, 320*240, 0);\r
13059a60 203 }\r
204\r
f9a37a02 205 menu_darken_bg((char *)gp2x_screen + 320*120*2, 320*8, 0);\r
720ee7f6 206\r
207 if(start - 2 >= 0)\r
13059a60 208 smalltext_out16_lim(14, (start - 2)*10, curdir, 0xffff, 53-2);\r
720ee7f6 209 for (i = 0; i < n; i++) {\r
210 pos = start + i;\r
211 if (pos < 0) continue;\r
212 if (pos > 23) break;\r
213 if (namelist[i+1]->d_type == DT_DIR) {\r
13059a60 214 smalltext_out16_lim(14, pos*10, "/", 0xfff6, 1);\r
215 smalltext_out16_lim(14+6, pos*10, namelist[i+1]->d_name, 0xfff6, 53-3);\r
720ee7f6 216 } else {\r
13059a60 217 unsigned short color = file2color(namelist[i+1]->d_name);\r
218 smalltext_out16_lim(14, pos*10, namelist[i+1]->d_name, color, 53-2);\r
720ee7f6 219 }\r
220 }\r
13059a60 221 text_out16(5, 120, ">");\r
222 menu_flip();\r
720ee7f6 223}\r
224\r
225static int scandir_cmp(const void *p1, const void *p2)\r
226{\r
227 struct dirent **d1 = (struct dirent **)p1, **d2 = (struct dirent **)p2;\r
228 if ((*d1)->d_type == (*d2)->d_type) return alphasort(d1, d2);\r
229 if ((*d1)->d_type == DT_DIR) return -1; // put before\r
230 if ((*d2)->d_type == DT_DIR) return 1;\r
231 return alphasort(d1, d2);\r
232}\r
233\r
79cad122 234static char *filter_exts[] = {\r
235 ".mp3", ".MP3", ".srm", ".brm", "s.gz", ".mds", "bcfg", ".txt", ".htm", "html",\r
236 ".jpg", ".gpe", ".cue"\r
237};\r
0d0558bd 238\r
239static int scandir_filter(const struct dirent *ent)\r
240{\r
241 const char *p;\r
242 int i;\r
243\r
244 if (ent == NULL || ent->d_name == NULL) return 0;\r
245 if (strlen(ent->d_name) < 5) return 1;\r
246\r
247 p = ent->d_name + strlen(ent->d_name) - 4;\r
248\r
249 for (i = 0; i < sizeof(filter_exts)/sizeof(filter_exts[0]); i++)\r
250 {\r
251 if (strcmp(p, filter_exts[i]) == 0) return 0;\r
252 }\r
253\r
254 return 1;\r
255}\r
720ee7f6 256\r
257static char *romsel_loop(char *curr_path)\r
258{\r
259 struct dirent **namelist;\r
260 DIR *dir;\r
261 int n, sel = 0;\r
262 unsigned long inp = 0;\r
263 char *ret = NULL, *fname = NULL;\r
264\r
265 // is this a dir or a full path?\r
266 if ((dir = opendir(curr_path))) {\r
267 closedir(dir);\r
268 } else {\r
269 char *p;\r
270 for (p = curr_path + strlen(curr_path) - 1; p > curr_path && *p != '/'; p--);\r
271 *p = 0;\r
272 fname = p+1;\r
273 }\r
274\r
0d0558bd 275 n = scandir(curr_path, &namelist, scandir_filter, scandir_cmp);\r
720ee7f6 276 if (n < 0) {\r
277 // try root\r
0d0558bd 278 n = scandir("/", &namelist, scandir_filter, scandir_cmp);\r
720ee7f6 279 if (n < 0) {\r
280 // oops, we failed\r
281 printf("dir: "); printf(curr_path); printf("\n");\r
282 perror("scandir");\r
283 return NULL;\r
284 }\r
285 }\r
286\r
287 // try to find sel\r
288 if (fname != NULL) {\r
289 int i;\r
290 for (i = 1; i < n; i++) {\r
291 if (strcmp(namelist[i]->d_name, fname) == 0) {\r
292 sel = i - 1;\r
293 break;\r
294 }\r
295 }\r
296 }\r
297\r
298 for (;;)\r
299 {\r
300 draw_dirlist(curr_path, namelist, n, sel);\r
5111820c 301 inp = wait_for_input(GP2X_UP|GP2X_DOWN|GP2X_LEFT|GP2X_RIGHT|GP2X_L|GP2X_R|GP2X_B|GP2X_X);\r
720ee7f6 302 if(inp & GP2X_UP ) { sel--; if (sel < 0) sel = n-2; }\r
303 if(inp & GP2X_DOWN) { sel++; if (sel > n-2) sel = 0; }\r
5e2e14f2 304 if(inp & GP2X_LEFT) { sel-=10; if (sel < 0) sel = 0; }\r
305 if(inp & GP2X_L) { sel-=24; if (sel < 0) sel = 0; }\r
306 if(inp & GP2X_RIGHT) { sel+=10; if (sel > n-2) sel = n-2; }\r
307 if(inp & GP2X_R) { sel+=24; if (sel > n-2) sel = n-2; }\r
720ee7f6 308 if(inp & GP2X_B) { // enter dir/select\r
309 again:\r
310 if (namelist[sel+1]->d_type == DT_REG) {\r
311 strcpy(romFileName, curr_path);\r
312 strcat(romFileName, "/");\r
313 strcat(romFileName, namelist[sel+1]->d_name);\r
314 ret = romFileName;\r
315 break;\r
316 } else if (namelist[sel+1]->d_type == DT_DIR) {\r
317 int newlen = strlen(curr_path) + strlen(namelist[sel+1]->d_name) + 2;\r
318 char *p, *newdir = malloc(newlen);\r
319 if (strcmp(namelist[sel+1]->d_name, "..") == 0) {\r
320 char *start = curr_path;\r
321 p = start + strlen(start) - 1;\r
322 while (*p == '/' && p > start) p--;\r
323 while (*p != '/' && p > start) p--;\r
324 if (p <= start) strcpy(newdir, "/");\r
325 else { strncpy(newdir, start, p-start); newdir[p-start] = 0; }\r
326 } else {\r
327 strcpy(newdir, curr_path);\r
328 p = newdir + strlen(newdir) - 1;\r
329 while (*p == '/' && p >= newdir) *p-- = 0;\r
330 strcat(newdir, "/");\r
331 strcat(newdir, namelist[sel+1]->d_name);\r
332 }\r
333 ret = romsel_loop(newdir);\r
334 free(newdir);\r
335 break;\r
336 } else {\r
337 // unknown file type, happens on NTFS mounts. Try to guess.\r
338 FILE *tstf; int tmp;\r
339 strcpy(romFileName, curr_path);\r
340 strcat(romFileName, "/");\r
341 strcat(romFileName, namelist[sel+1]->d_name);\r
342 tstf = fopen(romFileName, "rb");\r
343 if (tstf != NULL)\r
344 {\r
345 if (fread(&tmp, 1, 1, tstf) > 0 || ferror(tstf) == 0)\r
346 namelist[sel+1]->d_type = DT_REG;\r
347 else namelist[sel+1]->d_type = DT_DIR;\r
348 fclose(tstf);\r
349 goto again;\r
350 }\r
351 }\r
352 }\r
353 if(inp & GP2X_X) break; // cancel\r
354 }\r
355\r
356 if (n > 0) {\r
357 while(n--) free(namelist[n]);\r
358 free(namelist);\r
359 }\r
360\r
361 return ret;\r
362}\r
363\r
0ae6813e 364// ------------ debug menu ------------\r
365\r
366char *debugString(void);\r
367\r
368static void draw_debug(void)\r
369{\r
370 char *p, *str = debugString();\r
371 int len, line;\r
372\r
373 gp2x_pd_clone_buffer2();\r
374\r
375 p = str;\r
376 for (line = 0; line < 24; line++)\r
377 {\r
378 while (*p && *p != '\n') p++;\r
379 len = p - str;\r
380 if (len > 55) len = 55;\r
13059a60 381 smalltext_out16_lim(1, line*10, str, 0xffff, len);\r
0ae6813e 382 if (*p == 0) break;\r
383 p++; str = p;\r
384 }\r
13059a60 385 menu_flip();\r
0ae6813e 386}\r
387\r
388static void debug_menu_loop(void)\r
389{\r
390 draw_debug();\r
391 wait_for_input(GP2X_B|GP2X_X);\r
392}\r
393\r
70d2ecc5 394// ------------ patch/gg menu ------------\r
395\r
396static void draw_patchlist(int sel)\r
397{\r
13059a60 398 int start, i, pos, active;\r
70d2ecc5 399\r
400 start = 12 - sel;\r
401\r
402 gp2x_pd_clone_buffer2();\r
403\r
404 for (i = 0; i < PicoPatchCount; i++) {\r
405 pos = start + i;\r
406 if (pos < 0) continue;\r
407 if (pos > 23) break;\r
13059a60 408 active = PicoPatches[i].active;\r
409 smalltext_out16_lim(14, pos*10, active ? "ON " : "OFF", active ? 0xfff6 : 0xffff, 3);\r
410 smalltext_out16_lim(14+6*4, pos*10, PicoPatches[i].name, active ? 0xfff6 : 0xffff, 53-6);\r
70d2ecc5 411 }\r
412 pos = start + i;\r
13059a60 413 if (pos < 24) smalltext_out16_lim(14, pos*10, "done", 0xffff, 4);\r
70d2ecc5 414\r
13059a60 415 text_out16(5, 120, ">");\r
416 menu_flip();\r
70d2ecc5 417}\r
418\r
419\r
0ae6813e 420static void patches_menu_loop(void)\r
70d2ecc5 421{\r
422 int menu_sel = 0;\r
423 unsigned long inp = 0;\r
424\r
425 for(;;)\r
426 {\r
427 draw_patchlist(menu_sel);\r
428 inp = wait_for_input(GP2X_UP|GP2X_DOWN|GP2X_LEFT|GP2X_RIGHT|GP2X_L|GP2X_R|GP2X_B|GP2X_X);\r
429 if(inp & GP2X_UP ) { menu_sel--; if (menu_sel < 0) menu_sel = PicoPatchCount; }\r
430 if(inp & GP2X_DOWN) { menu_sel++; if (menu_sel > PicoPatchCount) menu_sel = 0; }\r
431 if(inp &(GP2X_LEFT|GP2X_L)) { menu_sel-=10; if (menu_sel < 0) menu_sel = 0; }\r
432 if(inp &(GP2X_RIGHT|GP2X_R)) { menu_sel+=10; if (menu_sel > PicoPatchCount) menu_sel = PicoPatchCount; }\r
433 if(inp & GP2X_B) { // action\r
434 if (menu_sel < PicoPatchCount)\r
435 PicoPatches[menu_sel].active = !PicoPatches[menu_sel].active;\r
436 else return;\r
437 }\r
438 if(inp & GP2X_X) return;\r
439 }\r
440\r
441}\r
442\r
4ffd2858 443// ------------ savestate loader ------------\r
444\r
4ffd2858 445static int state_slot_flags = 0;\r
446\r
447static void state_check_slots(void)\r
448{\r
449 int slot;\r
450\r
451 state_slot_flags = 0;\r
452\r
453 for (slot = 0; slot < 10; slot++)\r
454 {\r
455 if (emu_check_save_file(slot))\r
456 {\r
457 state_slot_flags |= 1 << slot;\r
458 }\r
459 }\r
460}\r
461\r
462static void draw_savestate_bg(int slot)\r
463{\r
464 struct PicoVideo tmp_pv;\r
465 unsigned short tmp_cram[0x40];\r
466 unsigned short tmp_vsram[0x40];\r
467 void *tmp_vram, *file;\r
468 char *fname;\r
469\r
470 fname = emu_GetSaveFName(1, 0, slot);\r
471 if (!fname) return;\r
472\r
473 tmp_vram = malloc(sizeof(Pico.vram));\r
474 if (tmp_vram == NULL) return;\r
475\r
476 memcpy(tmp_vram, Pico.vram, sizeof(Pico.vram));\r
477 memcpy(tmp_cram, Pico.cram, sizeof(Pico.cram));\r
478 memcpy(tmp_vsram, Pico.vsram, sizeof(Pico.vsram));\r
479 memcpy(&tmp_pv, &Pico.video, sizeof(Pico.video));\r
480\r
481 if (strcmp(fname + strlen(fname) - 3, ".gz") == 0) {\r
482 file = gzopen(fname, "rb");\r
483 emu_set_save_cbs(1);\r
484 } else {\r
485 file = fopen(fname, "rb");\r
486 emu_set_save_cbs(0);\r
487 }\r
488\r
489 if (file) {\r
490 if (PicoMCD & 1) {\r
491 PicoCdLoadStateGfx(file);\r
492 } else {\r
493 areaSeek(file, 0x10020, SEEK_SET); // skip header and RAM in state file\r
494 areaRead(Pico.vram, 1, sizeof(Pico.vram), file);\r
495 areaSeek(file, 0x2000, SEEK_CUR);\r
496 areaRead(Pico.cram, 1, sizeof(Pico.cram), file);\r
497 areaRead(Pico.vsram, 1, sizeof(Pico.vsram), file);\r
498 areaSeek(file, 0x221a0, SEEK_SET);\r
499 areaRead(&Pico.video, 1, sizeof(Pico.video), file);\r
500 }\r
501 areaClose(file);\r
502 }\r
503\r
504 emu_forced_frame();\r
13059a60 505 menu_prepare_bg(1);\r
4ffd2858 506\r
507 memcpy(Pico.vram, tmp_vram, sizeof(Pico.vram));\r
508 memcpy(Pico.cram, tmp_cram, sizeof(Pico.cram));\r
509 memcpy(Pico.vsram, tmp_vsram, sizeof(Pico.vsram));\r
510 memcpy(&Pico.video, &tmp_pv, sizeof(Pico.video));\r
511 free(tmp_vram);\r
512}\r
513\r
514static void draw_savestate_menu(int menu_sel, int is_loading)\r
515{\r
516 int tl_x = 25, tl_y = 60, y, i;\r
517\r
518 if (state_slot_flags & (1 << menu_sel))\r
519 draw_savestate_bg(menu_sel);\r
520 gp2x_pd_clone_buffer2();\r
521\r
13059a60 522 text_out16(tl_x, 30, is_loading ? "Load state" : "Save state");\r
523\r
c7a4ff64 524 menu_draw_selection(tl_x - 16, tl_y + menu_sel*10, 108);\r
4ffd2858 525\r
526 /* draw all 10 slots */\r
527 y = tl_y;\r
528 for (i = 0; i < 10; i++, y+=10)\r
529 {\r
13059a60 530 text_out16(tl_x, y, "SLOT %i (%s)", i, (state_slot_flags & (1 << i)) ? "USED" : "free");\r
4ffd2858 531 }\r
13059a60 532 text_out16(tl_x, y, "back");\r
4ffd2858 533\r
13059a60 534 menu_flip();\r
4ffd2858 535}\r
536\r
537static int savestate_menu_loop(int is_loading)\r
538{\r
59d0f042 539 static int menu_sel = 10;\r
540 int menu_sel_max = 10;\r
4ffd2858 541 unsigned long inp = 0;\r
542\r
543 state_check_slots();\r
544\r
545 for(;;)\r
546 {\r
547 draw_savestate_menu(menu_sel, is_loading);\r
548 inp = wait_for_input(GP2X_UP|GP2X_DOWN|GP2X_B|GP2X_X);\r
549 if(inp & GP2X_UP ) {\r
550 do {\r
551 menu_sel--; if (menu_sel < 0) menu_sel = menu_sel_max;\r
552 } while (!(state_slot_flags & (1 << menu_sel)) && menu_sel != menu_sel_max && is_loading);\r
553 }\r
554 if(inp & GP2X_DOWN) {\r
555 do {\r
556 menu_sel++; if (menu_sel > menu_sel_max) menu_sel = 0;\r
557 } while (!(state_slot_flags & (1 << menu_sel)) && menu_sel != menu_sel_max && is_loading);\r
558 }\r
559 if(inp & GP2X_B) { // save/load\r
560 if (menu_sel < 10) {\r
561 state_slot = menu_sel;\r
562 if (emu_SaveLoadGame(is_loading, 0)) {\r
563 strcpy(menuErrorMsg, is_loading ? "Load failed" : "Save failed");\r
564 return 1;\r
565 }\r
566 return 0;\r
567 } else return 1;\r
568 }\r
569 if(inp & GP2X_X) return 1;\r
570 }\r
571}\r
572\r
720ee7f6 573// -------------- key config --------------\r
574\r
575static char *usb_joy_key_name(int joy, int num)\r
576{\r
577 static char name[16];\r
578 switch (num)\r
579 {\r
580 case 0: sprintf(name, "Joy%i UP", joy); break;\r
581 case 1: sprintf(name, "Joy%i DOWN", joy); break;\r
582 case 2: sprintf(name, "Joy%i LEFT", joy); break;\r
583 case 3: sprintf(name, "Joy%i RIGHT", joy); break;\r
584 default:sprintf(name, "Joy%i b%i", joy, num-3); break;\r
585 }\r
586 return name;\r
587}\r
588\r
95151aea 589static char *action_binds(int player_idx, int action_mask)\r
720ee7f6 590{\r
95151aea 591 static char strkeys[32*5];\r
720ee7f6 592 int joy, i;\r
593\r
594 strkeys[0] = 0;\r
95151aea 595 for (i = 0; i < 32; i++) // i is key index\r
720ee7f6 596 {\r
95151aea 597 if (currentConfig.KeyBinds[i] & action_mask)\r
720ee7f6 598 {\r
95151aea 599 if (player_idx >= 0 && ((currentConfig.KeyBinds[i] >> 16) & 3) != player_idx) continue;\r
720ee7f6 600 if (strkeys[0]) { strcat(strkeys, " + "); strcat(strkeys, gp2xKeyNames[i]); break; }\r
601 else strcpy(strkeys, gp2xKeyNames[i]);\r
602 }\r
603 }\r
604 for (joy = 0; joy < num_of_joys; joy++)\r
605 {\r
606 for (i = 0; i < 32; i++)\r
607 {\r
95151aea 608 if (currentConfig.JoyBinds[joy][i] & action_mask)\r
720ee7f6 609 {\r
95151aea 610 if (player_idx >= 0 && ((currentConfig.JoyBinds[joy][i] >> 16) & 3) != player_idx) continue;\r
720ee7f6 611 if (strkeys[0]) {\r
612 strcat(strkeys, ", "); strcat(strkeys, usb_joy_key_name(joy + 1, i));\r
613 break;\r
614 }\r
615 else strcpy(strkeys, usb_joy_key_name(joy + 1, i));\r
616 }\r
617 }\r
618 }\r
619\r
95151aea 620 return strkeys;\r
621}\r
622\r
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
e5d315a5 657 gp2x_pd_clone_buffer2();\r
95151aea 658 if (player_idx >= 0) {\r
13059a60 659 text_out16(80, 20, "Player %i controls", player_idx + 1);\r
95151aea 660 x = 100;\r
661 } else {\r
13059a60 662 text_out16(80, 20, "Emulator controls");\r
95151aea 663 x = 40;\r
664 }\r
665\r
c7a4ff64 666 menu_draw_selection(x - 16, tl_y + sel*10, (player_idx >= 0) ? 66 : 130);\r
13059a60 667\r
95151aea 668 y = tl_y;\r
669 for (i = 0; i < opt_cnt; i++, y+=10)\r
13059a60 670 text_out16(x, y, "%s : %s", opts[i].name, action_binds(player_idx, opts[i].mask));\r
95151aea 671\r
13059a60 672 text_out16(x, y, "Done");\r
95151aea 673\r
674 if (sel < opt_cnt) {\r
13059a60 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
95151aea 679 } else {\r
13059a60 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
95151aea 683 }\r
13059a60 684 menu_flip();\r
720ee7f6 685}\r
686\r
95151aea 687static void key_config_loop(const bind_action_t *opts, int opt_cnt, int player_idx)\r
720ee7f6 688{\r
95151aea 689 int joy = 0, sel = 0, menu_sel_max = opt_cnt, prev_select = 0, i;\r
720ee7f6 690 unsigned long inp = 0;\r
691\r
692 for (;;)\r
693 {\r
95151aea 694 draw_key_config(opts, opt_cnt, player_idx, sel);\r
720ee7f6 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
95151aea 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
720ee7f6 702 }\r
95151aea 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
720ee7f6 711 inp &= CONFIGURABLE_KEYS;\r
712 inp &= ~GP2X_SELECT;\r
720ee7f6 713 for (i = 0; i < 32; i++)\r
714 if (inp & (1 << i)) {\r
95151aea 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
720ee7f6 722 }\r
95151aea 723 }\r
724 else if (sel < opt_cnt)\r
725 {\r
720ee7f6 726 for (i = 0; i < 32; i++)\r
727 if (inp & (1 << i)) {\r
95151aea 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
720ee7f6 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
e5d315a5 746 gp2x_pd_clone_buffer2();\r
c7a4ff64 747 menu_draw_selection(tl_x - 16, tl_y + menu_sel*10, 138);\r
720ee7f6 748\r
13059a60 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
720ee7f6 753\r
754 tl_x = 25;\r
13059a60 755 text_out16(tl_x, (y=110), "USB joys detected:");\r
720ee7f6 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
13059a60 759 text_out16(tl_x, (y+=10), "%i: %s", i+1, joyname);\r
720ee7f6 760 }\r
761 } else {\r
13059a60 762 text_out16(tl_x, (y+=10), "none");\r
720ee7f6 763 }\r
764\r
13059a60 765 menu_flip();\r
720ee7f6 766}\r
767\r
95151aea 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
95151aea 791 { "Load State ", 1<<28 },\r
792 { "Save State ", 1<<27 },\r
95151aea 793 { "Prev Save Slot ", 1<<25 },\r
794 { "Next Save Slot ", 1<<24 },\r
13059a60 795 { "Switch Renderer", 1<<26 },\r
796 { "Volume Down ", 1<<30 },\r
797 { "Volume Up ", 1<<29 },\r
95151aea 798 { "Enter Menu ", 1<<23 },\r
799};\r
800\r
720ee7f6 801static void kc_sel_loop(void)\r
802{\r
95151aea 803 int menu_sel = 3, menu_sel_max = 3;\r
720ee7f6 804 unsigned long inp = 0;\r
95151aea 805 int is_6button = currentConfig.PicoOpt & 0x020;\r
720ee7f6 806\r
95151aea 807 while (1)\r
720ee7f6 808 {\r
809 draw_kc_sel(menu_sel);\r
810 inp = wait_for_input(GP2X_UP|GP2X_DOWN|GP2X_B|GP2X_X);\r
95151aea 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
720ee7f6 814 switch (menu_sel) {\r
95151aea 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
720ee7f6 820 default: return;\r
821 }\r
822 }\r
95151aea 823 if (inp & GP2X_X) return;\r
720ee7f6 824 }\r
825}\r
826\r
827\r
daf91588 828// --------- sega/mega cd options ----------\r
829\r
59633198 830menu_entry cdopt_entries[] =\r
daf91588 831{\r
59633198 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
13059a60 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
59633198 863 case MA_CDOPT_READAHEAD:\r
864 if (PicoCDBuffers > 1) sprintf(ra_buff, "%5iK", PicoCDBuffers * 2);\r
865 else strcpy(ra_buff, " OFF");\r
13059a60 866 text_out16(x, y, "ReadAhead buffer %s", ra_buff);\r
59633198 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
5f9922e6 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
daf91588 880\r
e5d315a5 881 gp2x_pd_clone_buffer2();\r
882\r
c7a4ff64 883 menu_draw_selection(tl_x - 16, tl_y + menu_sel*10, 246);\r
daf91588 884\r
13059a60 885 me_draw(cdopt_entries, CDOPT_ENTRY_COUNT, tl_x, tl_y, menu_cdopt_cust_draw, bios_names);\r
daf91588 886\r
59633198 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
13059a60 891 text_out16(tl_x, 210, "Press start to test selected BIOS");\r
daf91588 892\r
13059a60 893 menu_flip();\r
daf91588 894}\r
895\r
896static void cd_menu_loop_options(void)\r
897{\r
59d0f042 898 static int menu_sel = 0;\r
899 int menu_sel_max = 10;\r
daf91588 900 unsigned long inp = 0;\r
59633198 901 struct bios_names_t bios_names;\r
902 menu_id selected_id;\r
903 char *bios, *p;\r
daf91588 904\r
905 if (find_bios(4, &bios)) { // US\r
906 for (p = bios+strlen(bios)-1; p > bios && *p != '/'; p--); p++;\r
59633198 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
daf91588 909\r
910 if (find_bios(8, &bios)) { // EU\r
911 for (p = bios+strlen(bios)-1; p > bios && *p != '/'; p--); p++;\r
59633198 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
daf91588 914\r
915 if (find_bios(1, &bios)) { // JP\r
916 for (p = bios+strlen(bios)-1; p > bios && *p != '/'; p--); p++;\r
59633198 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
daf91588 919\r
920 for(;;)\r
921 {\r
59633198 922 draw_cd_menu_options(menu_sel, &bios_names);\r
daf91588 923 inp = wait_for_input(GP2X_UP|GP2X_DOWN|GP2X_LEFT|GP2X_RIGHT|GP2X_B|GP2X_X|GP2X_A|GP2X_START);\r
59633198 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
daf91588 938 }\r
939 }\r
59633198 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
daf91588 950 strcpy(romFileName, bios);\r
951 engineState = PGS_ReloadRom;\r
952 return;\r
953 }\r
954 break;\r
59633198 955 case MA_CDOPT_TESTBIOS_EUR:\r
956 if (find_bios(8, &bios)) { // test EU\r
daf91588 957 strcpy(romFileName, bios);\r
958 engineState = PGS_ReloadRom;\r
959 return;\r
960 }\r
961 break;\r
59633198 962 case MA_CDOPT_TESTBIOS_JAP:\r
963 if (find_bios(1, &bios)) { // test JP\r
daf91588 964 strcpy(romFileName, bios);\r
965 engineState = PGS_ReloadRom;\r
966 return;\r
967 }\r
968 break;\r
59633198 969 default:\r
970 break;\r
daf91588 971 }\r
972 }\r
59633198 973 if (inp & (GP2X_X|GP2X_A)) return;\r
daf91588 974 }\r
975}\r
976\r
977\r
978// --------- advanced options ----------\r
979\r
59633198 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
c7a4ff64 984 { "Perfect vsync", MB_ONOFF, MA_OPT2_VSYNC, &currentConfig.EmuOpt, 0x2000, 0, 0, 1 },\r
59633198 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
13059a60 1001 text_out16(x, y, "Gamma correction %i.%02i", currentConfig.gamma / 100, currentConfig.gamma%100);\r
59633198 1002 else if (entry->id == MA_OPT2_SQUIDGEHACK)\r
13059a60 1003 text_out16(x, y, "squidgehack (now %s %s", mmuhack_status ? "active) " : "inactive)",\r
59633198 1004 (currentConfig.EmuOpt&0x0010)?"ON":"OFF");\r
1005}\r
1006\r
1007\r
720ee7f6 1008static void draw_amenu_options(int menu_sel)\r
1009{\r
59633198 1010 int tl_x = 25, tl_y = 50;\r
720ee7f6 1011\r
e5d315a5 1012 gp2x_pd_clone_buffer2();\r
1013\r
c7a4ff64 1014 menu_draw_selection(tl_x - 16, tl_y + menu_sel*10, 252);\r
720ee7f6 1015\r
13059a60 1016 me_draw(opt2_entries, OPT2_ENTRY_COUNT, tl_x, tl_y, menu_opt2_cust_draw, NULL);\r
720ee7f6 1017\r
13059a60 1018 menu_flip();\r
720ee7f6 1019}\r
1020\r
1021static void amenu_loop_options(void)\r
1022{\r
59d0f042 1023 static int menu_sel = 0;\r
59633198 1024 int menu_sel_max;\r
720ee7f6 1025 unsigned long inp = 0;\r
59633198 1026 menu_id selected_id;\r
1027\r
1028 menu_sel_max = me_count_enabled(opt2_entries, OPT2_ENTRY_COUNT) - 1;\r
720ee7f6 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
59633198 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
720ee7f6 1047 }\r
1048 }\r
59633198 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
720ee7f6 1053 }\r
1054 }\r
59633198 1055 if (inp & (GP2X_X|GP2X_A)) return;\r
720ee7f6 1056 }\r
1057}\r
1058\r
1059// -------------- options --------------\r
1060\r
59633198 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
979ba09f 1089static const char *region_name(unsigned int code)\r
720ee7f6 1090{\r
979ba09f 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
720ee7f6 1109}\r
1110\r
59633198 1111\r
1112static void menu_opt_cust_draw(const menu_entry *entry, int x, int y, void *param)\r
720ee7f6 1113{\r
59633198 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
13059a60 1125 text_out16(x, y, "Renderer: %s", str);\r
59633198 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
13059a60 1134 text_out16(x, y, "Scaling: %s", str);\r
59633198 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
13059a60 1140 text_out16(x, y, "Frameskip %s", str24);\r
59633198 1141 break;\r
1142 case MA_OPT_SOUND_QUALITY:\r
1143 str = (currentConfig.PicoOpt&0x08)?"stereo":"mono";\r
13059a60 1144 text_out16(x, y, "Sound Quality: %5iHz %s", currentConfig.PsndRate, str);\r
59633198 1145 break;\r
1146 case MA_OPT_REGION:\r
13059a60 1147 text_out16(x, y, "Region: %s", region_name(currentConfig.PicoRegion));\r
59633198 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
13059a60 1156 text_out16(x, y, "Confirm savestate %s", str);\r
59633198 1157 break;\r
1158 case MA_OPT_CPU_CLOCKS:\r
13059a60 1159 text_out16(x, y, "GP2X CPU clocks %iMhz", currentConfig.CPUclock);\r
59633198 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
13059a60 1164 text_out16(x, y, "Save cfg as default%s", str24);\r
59633198 1165 break;\r
1166 case MA_OPT_LOADCFG:\r
13059a60 1167 text_out16(x, y, "Load cfg from profile %i", config_slot);\r
59633198 1168 break;\r
1169 default:\r
1170 printf("%s: unimplemented (%i)\n", __FUNCTION__, entry->id);\r
1171 break;\r
46ede6a6 1172 }\r
59633198 1173}\r
1174\r
1175\r
1176\r
1177static void draw_menu_options(int menu_sel)\r
1178{\r
13059a60 1179 int tl_x = 25, tl_y = 24;\r
720ee7f6 1180\r
e5d315a5 1181 gp2x_pd_clone_buffer2();\r
1182\r
c7a4ff64 1183 menu_draw_selection(tl_x - 16, tl_y + menu_sel*10, 284);\r
720ee7f6 1184\r
13059a60 1185 me_draw(opt_entries, OPT_ENTRY_COUNT, tl_x, tl_y, menu_opt_cust_draw, NULL);\r
720ee7f6 1186\r
13059a60 1187 menu_flip();\r
720ee7f6 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
979ba09f 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
720ee7f6 1227static void menu_options_save(void)\r
1228{\r
720ee7f6 1229 PicoOpt = currentConfig.PicoOpt;\r
1230 PsndRate = currentConfig.PsndRate;\r
1231 PicoRegionOverride = currentConfig.PicoRegion;\r
95151aea 1232 if (!(PicoOpt & 0x20)) {\r
1233 // unbind XYZ MODE, just in case\r
1234 unbind_action(0xf00);\r
720ee7f6 1235 }\r
79cad122 1236 scaling_update();\r
720ee7f6 1237}\r
1238\r
daf91588 1239static int menu_loop_options(void)\r
720ee7f6 1240{\r
59d0f042 1241 static int menu_sel = 0;\r
59633198 1242 int menu_sel_max, ret;\r
720ee7f6 1243 unsigned long inp = 0;\r
59633198 1244 menu_id selected_id;\r
720ee7f6 1245\r
979ba09f 1246 currentConfig.PicoOpt = PicoOpt;\r
1247 currentConfig.PsndRate = PsndRate;\r
1248 currentConfig.PicoRegion = PicoRegionOverride;\r
720ee7f6 1249\r
59633198 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
95151aea 1253 if (menu_sel > menu_sel_max) menu_sel = menu_sel_max;\r
59633198 1254\r
1255 while (1)\r
720ee7f6 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
59633198 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
95151aea 1349 ret = emu_ReadConfig(1, 1);\r
1350 if (!ret) ret = emu_ReadConfig(0, 1);\r
1351 if (ret) strcpy(menuErrorMsg, "config loaded");\r
59633198 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
720ee7f6 1358 }\r
1359 }\r
979ba09f 1360 if(inp & (GP2X_X|GP2X_A)) {\r
720ee7f6 1361 menu_options_save();\r
daf91588 1362 return 0; // done (update, no write)\r
720ee7f6 1363 }\r
720ee7f6 1364 }\r
1365}\r
1366\r
1367// -------------- credits --------------\r
1368\r
1369static void draw_menu_credits(void)\r
1370{\r
13059a60 1371 int tl_x = 15, tl_y = 64, y;\r
e5d315a5 1372 gp2x_pd_clone_buffer2();\r
720ee7f6 1373\r
13059a60 1374 text_out16(tl_x, 20, "PicoDrive v" VERSION " (c) notaz, 2006,2007");\r
720ee7f6 1375 y = tl_y;\r
13059a60 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
f9a37a02 1390 text_out16(tl_x, (y+=10), "ketchupgun: skin design");\r
13059a60 1391\r
1392 menu_flip();\r
720ee7f6 1393}\r
1394\r
1395\r
1396// -------------- root menu --------------\r
1397\r
59633198 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
720ee7f6 1414static void draw_menu_root(int menu_sel)\r
1415{\r
59633198 1416 const int tl_x = 70, tl_y = 70;\r
1417\r
e5d315a5 1418 gp2x_pd_clone_buffer2();\r
720ee7f6 1419\r
13059a60 1420 text_out16(tl_x, 20, "PicoDrive v" VERSION);\r
1421\r
c7a4ff64 1422 menu_draw_selection(tl_x - 16, tl_y + menu_sel*10, 146);\r
720ee7f6 1423\r
59633198 1424 me_draw(main_entries, MAIN_ENTRY_COUNT, tl_x, tl_y, NULL, NULL);\r
720ee7f6 1425\r
720ee7f6 1426 // error\r
13059a60 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
720ee7f6 1432}\r
1433\r
1434\r
1435static void menu_loop_root(void)\r
1436{\r
59633198 1437 static int menu_sel = 0;\r
1438 int ret, menu_sel_max;\r
720ee7f6 1439 unsigned long inp = 0;\r
720ee7f6 1440\r
59633198 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
95151aea 1448 if (menu_sel > menu_sel_max) menu_sel = menu_sel_max;\r
720ee7f6 1449\r
79cad122 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
720ee7f6 1455 {\r
1456 draw_menu_root(menu_sel);\r
0ae6813e 1457 inp = wait_for_input(GP2X_UP|GP2X_DOWN|GP2X_B|GP2X_X|GP2X_SELECT|GP2X_L|GP2X_R);\r
59633198 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
0ae6813e 1460 if((inp & (GP2X_L|GP2X_R)) == (GP2X_L|GP2X_R)) debug_menu_loop();\r
720ee7f6 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
59633198 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
5e2e14f2 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
720ee7f6 1477 break;\r
59633198 1478 case MA_MAIN_SAVE_STATE:\r
720ee7f6 1479 if (rom_data) {\r
4ffd2858 1480 if(savestate_menu_loop(0))\r
720ee7f6 1481 continue;\r
720ee7f6 1482 engineState = PGS_Running;\r
1483 return;\r
1484 }\r
1485 break;\r
59633198 1486 case MA_MAIN_LOAD_STATE:\r
720ee7f6 1487 if (rom_data) {\r
4ffd2858 1488 if(savestate_menu_loop(1))\r
720ee7f6 1489 continue;\r
720ee7f6 1490 engineState = PGS_Running;\r
1491 return;\r
1492 }\r
1493 break;\r
59633198 1494 case MA_MAIN_RESET_GAME:\r
720ee7f6 1495 if (rom_data) {\r
1496 emu_ResetGame();\r
1497 engineState = PGS_Running;\r
1498 return;\r
1499 }\r
1500 break;\r
59633198 1501 case MA_MAIN_LOAD_ROM:\r
13059a60 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
720ee7f6 1512 selfname = romsel_loop(curr_path);\r
1513 if (selfname) {\r
1514 printf("selected file: %s\n", selfname);\r
720ee7f6 1515 engineState = PGS_ReloadRom;\r
13059a60 1516 return;\r
720ee7f6 1517 }\r
13059a60 1518 break;\r
1519 }\r
59633198 1520 case MA_MAIN_OPTIONS:\r
daf91588 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
720ee7f6 1525 break;\r
59633198 1526 case MA_MAIN_CONTROLS:\r
720ee7f6 1527 kc_sel_loop();\r
1528 break;\r
59633198 1529 case MA_MAIN_CREDITS:\r
720ee7f6 1530 draw_menu_credits();\r
1531 usleep(500*1000);\r
1532 inp = wait_for_input(GP2X_B|GP2X_X);\r
1533 break;\r
59633198 1534 case MA_MAIN_EXIT:\r
720ee7f6 1535 engineState = PGS_Quit;\r
1536 return;\r
59633198 1537 case MA_MAIN_PATCHES:\r
70d2ecc5 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
59633198 1545 default:\r
1546 printf("%s: something unknown selected\n", __FUNCTION__);\r
1547 break;\r
720ee7f6 1548 }\r
1549 }\r
1550 menuErrorMsg[0] = 0; // clear error msg\r
1551 }\r
1552}\r
1553\r
f9a37a02 1554static void menu_darken_bg(void *dst, int pixels, int darker)\r
720ee7f6 1555{\r
13059a60 1556 unsigned int *screen = dst;\r
1557 pixels /= 2;\r
f9a37a02 1558 if (darker)\r
13059a60 1559 {\r
f9a37a02 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
13059a60 1573 }\r
1574}\r
e5d315a5 1575\r
13059a60 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
f9a37a02 1582 menu_darken_bg((char *)gp2x_screen + 320*8*2, 320*224, 1);\r
13059a60 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
4ffd2858 1589 }\r
720ee7f6 1590\r
13059a60 1591 // copy to buffer2\r
1592 gp2x_memcpy_buffers((1<<2), gp2x_screen, 0, 320*240*2);\r
4ffd2858 1593}\r
1594\r
1595static void menu_gfx_prepare(void)\r
1596{\r
13059a60 1597 menu_prepare_bg(rom_data != NULL);\r
4ffd2858 1598\r
13059a60 1599 // switch to 16bpp\r
1600 gp2x_video_changemode2(16);\r
79cad122 1601 gp2x_video_RGB_setscaling(0, 320, 240);\r
13059a60 1602 menu_flip();\r
e5d315a5 1603}\r
1604\r
1605\r
1606void menu_loop(void)\r
1607{\r
1608 menu_gfx_prepare();\r
720ee7f6 1609\r
1610 menu_loop_root();\r
1611\r
1612 menuErrorMsg[0] = 0;\r
1613}\r
5e2e14f2 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
13059a60 1621 memset(gp2x_screen, 0, 320*240*2);\r
5e2e14f2 1622\r
13059a60 1623 text_out16(tl_x, 20, "The unit is about to");\r
1624 text_out16(tl_x, 30, "close the CD tray.");\r
5e2e14f2 1625\r
1626 y = tl_y;\r
13059a60 1627 text_out16(tl_x, y, "Load new CD image");\r
1628 text_out16(tl_x, (y+=10), "Insert nothing");\r
5e2e14f2 1629\r
1630 // draw cursor\r
13059a60 1631 text_out16(tl_x - 16, tl_y + menu_sel*10, ">");\r
5e2e14f2 1632 // error\r
13059a60 1633 if (menuErrorMsg[0]) text_out16(5, 226, menuErrorMsg);\r
1634 menu_flip();\r
5e2e14f2 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
c7a4ff64 1645 gp2x_memset_all_buffers(0, 0, 320*240*2);\r
5e2e14f2 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