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