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