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