PSP sustend/resume and stuff
[libpicofe.git] / gp2x / menu.c
CommitLineData
95151aea 1// (c) Copyright 2006,2007 notaz, All rights reserved.\r
720ee7f6 2// Free for non-commercial use.\r
3\r
4// For commercial use, separate licencing terms must be obtained.\r
5\r
6#include <stdio.h>\r
7#include <string.h>\r
8#include <stdlib.h>\r
9#include <stdarg.h>\r
10#include <unistd.h>\r
11#include <dirent.h>\r
12\r
13#include "gp2x.h"\r
14#include "emu.h"\r
15#include "menu.h"\r
16#include "usbjoy.h"\r
f013066e 17#include "../common/emu.h"\r
c7a4ff64 18#include "../common/menu.h"\r
f013066e 19#include "../common/arm_utils.h"\r
c7a4ff64 20#include "../common/readpng.h"\r
720ee7f6 21#include "version.h"\r
22\r
70d2ecc5 23#include <Pico/PicoInt.h>\r
24#include <Pico/Patch.h>\r
25#include <zlib/zlib.h>\r
720ee7f6 26\r
27#ifndef _DIRENT_HAVE_D_TYPE\r
28#error "need d_type for file browser\r
29#endif\r
30\r
720ee7f6 31extern int mmuhack_status;\r
720ee7f6 32\r
c7a4ff64 33static const char *gp2xKeyNames[] = {\r
720ee7f6 34 "UP", "???", "LEFT", "???", "DOWN", "???", "RIGHT", "???",\r
35 "START", "SELECT", "L", "R", "A", "B", "X", "Y",\r
36 "???", "???", "???", "???", "???", "???", "VOL DOWN", "VOL UP",\r
37 "???", "???", "???", "PUSH", "???", "???", "???", "???"\r
38};\r
39\r
f9a37a02 40static void menu_darken_bg(void *dst, int pixels, int darker);\r
13059a60 41static void menu_prepare_bg(int use_game_bg);\r
720ee7f6 42\r
720ee7f6 43static unsigned long inp_prev = 0;\r
44static int inp_prevjoy = 0;\r
45\r
46static unsigned long wait_for_input(unsigned long interesting)\r
47{\r
48 unsigned long ret;\r
5e2e14f2 49 static int repeats = 0, wait = 50*1000;\r
720ee7f6 50 int release = 0, i;\r
51\r
5e2e14f2 52 if (repeats == 2 || repeats == 4) wait /= 2;\r
53 if (repeats == 6) wait = 15 * 1000;\r
720ee7f6 54\r
55 for (i = 0; i < 6 && inp_prev == gp2x_joystick_read(1); i++) {\r
5e2e14f2 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
720ee7f6 59 }\r
60\r
61 while ( !((ret = gp2x_joystick_read(1)) & interesting) ) {\r
62 usleep(50000);\r
63 release = 1;\r
64 }\r
65\r
66 if (release || ret != inp_prev) {\r
67 repeats = 0;\r
5e2e14f2 68 wait = 50*1000;\r
720ee7f6 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
c7a4ff64 127static void menu_flip(void)\r
128{\r
129 gp2x_video_flush_cache();\r
130 gp2x_video_flip2();\r
131}\r
720ee7f6 132\r
133\r
b9f8cb3d 134// --------- loading ROM screen ----------\r
135\r
677b5dd8 136static int cdload_called = 0;\r
137\r
b9f8cb3d 138static void load_progress_cb(int percent)\r
139{\r
140 int ln, len = percent * 320 / 100;\r
13059a60 141 unsigned short *dst = (unsigned short *)gp2x_screen + 320*20;\r
b9f8cb3d 142\r
143 if (len > 320) len = 320;\r
677b5dd8 144 for (ln = 8; ln > 0; ln--, dst += 320)\r
13059a60 145 memset(dst, 0xff, len*2);\r
146 menu_flip();\r
b9f8cb3d 147}\r
148\r
677b5dd8 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
b9f8cb3d 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
13059a60 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
b9f8cb3d 178 PicoCartLoadProgressCB = load_progress_cb;\r
677b5dd8 179 PicoCDLoadProgressCB = cdload_progress_cb;\r
180 cdload_called = 0;\r
b9f8cb3d 181}\r
182\r
183void menu_romload_end(void)\r
184{\r
185 PicoCartLoadProgressCB = NULL;\r
677b5dd8 186 smalltext_out16(1, cdload_called ? 60 : 30, "Starting emulation...", 0xffff);\r
13059a60 187 menu_flip();\r
b9f8cb3d 188}\r
189\r
720ee7f6 190// -------------- ROM selector --------------\r
191\r
13059a60 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
6d741b32 196 static const char *rom_exts[] = { "zip", "bin", "smd", "gen", "iso", "cso" };\r
13059a60 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
720ee7f6 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
f9a37a02 215 gp2x_pd_clone_buffer2();\r
216\r
217 if (rom_data == NULL) {\r
218 menu_darken_bg(gp2x_screen, 320*240, 0);\r
13059a60 219 }\r
220\r
f9a37a02 221 menu_darken_bg((char *)gp2x_screen + 320*120*2, 320*8, 0);\r
720ee7f6 222\r
223 if(start - 2 >= 0)\r
13059a60 224 smalltext_out16_lim(14, (start - 2)*10, curdir, 0xffff, 53-2);\r
720ee7f6 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
13059a60 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
720ee7f6 232 } else {\r
13059a60 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
720ee7f6 235 }\r
236 }\r
13059a60 237 text_out16(5, 120, ">");\r
238 menu_flip();\r
720ee7f6 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
79cad122 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
0d0558bd 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
720ee7f6 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
0d0558bd 291 n = scandir(curr_path, &namelist, scandir_filter, scandir_cmp);\r
720ee7f6 292 if (n < 0) {\r
293 // try root\r
0d0558bd 294 n = scandir("/", &namelist, scandir_filter, scandir_cmp);\r
720ee7f6 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
5111820c 317 inp = wait_for_input(GP2X_UP|GP2X_DOWN|GP2X_LEFT|GP2X_RIGHT|GP2X_L|GP2X_R|GP2X_B|GP2X_X);\r
720ee7f6 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
5e2e14f2 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
720ee7f6 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
0ae6813e 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
13059a60 397 smalltext_out16_lim(1, line*10, str, 0xffff, len);\r
0ae6813e 398 if (*p == 0) break;\r
399 p++; str = p;\r
400 }\r
13059a60 401 menu_flip();\r
0ae6813e 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
70d2ecc5 410// ------------ patch/gg menu ------------\r
411\r
412static void draw_patchlist(int sel)\r
413{\r
13059a60 414 int start, i, pos, active;\r
70d2ecc5 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
13059a60 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
70d2ecc5 427 }\r
428 pos = start + i;\r
13059a60 429 if (pos < 24) smalltext_out16_lim(14, pos*10, "done", 0xffff, 4);\r
70d2ecc5 430\r
13059a60 431 text_out16(5, 120, ">");\r
432 menu_flip();\r
70d2ecc5 433}\r
434\r
435\r
0ae6813e 436static void patches_menu_loop(void)\r
70d2ecc5 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
4ffd2858 459// ------------ savestate loader ------------\r
460\r
4ffd2858 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
f013066e 471 if (emu_checkSaveFile(slot))\r
4ffd2858 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
f013066e 499 emu_setSaveStateCbs(1);\r
4ffd2858 500 } else {\r
501 file = fopen(fname, "rb");\r
f013066e 502 emu_setSaveStateCbs(0);\r
4ffd2858 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
f013066e 520 emu_forcedFrame();\r
13059a60 521 menu_prepare_bg(1);\r
4ffd2858 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
13059a60 538 text_out16(tl_x, 30, is_loading ? "Load state" : "Save state");\r
539\r
c7a4ff64 540 menu_draw_selection(tl_x - 16, tl_y + menu_sel*10, 108);\r
4ffd2858 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
13059a60 546 text_out16(tl_x, y, "SLOT %i (%s)", i, (state_slot_flags & (1 << i)) ? "USED" : "free");\r
4ffd2858 547 }\r
13059a60 548 text_out16(tl_x, y, "back");\r
4ffd2858 549\r
13059a60 550 menu_flip();\r
4ffd2858 551}\r
552\r
553static int savestate_menu_loop(int is_loading)\r
554{\r
59d0f042 555 static int menu_sel = 10;\r
556 int menu_sel_max = 10;\r
4ffd2858 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
720ee7f6 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
95151aea 605static char *action_binds(int player_idx, int action_mask)\r
720ee7f6 606{\r
95151aea 607 static char strkeys[32*5];\r
720ee7f6 608 int joy, i;\r
609\r
610 strkeys[0] = 0;\r
95151aea 611 for (i = 0; i < 32; i++) // i is key index\r
720ee7f6 612 {\r
95151aea 613 if (currentConfig.KeyBinds[i] & action_mask)\r
720ee7f6 614 {\r
95151aea 615 if (player_idx >= 0 && ((currentConfig.KeyBinds[i] >> 16) & 3) != player_idx) continue;\r
720ee7f6 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
95151aea 624 if (currentConfig.JoyBinds[joy][i] & action_mask)\r
720ee7f6 625 {\r
95151aea 626 if (player_idx >= 0 && ((currentConfig.JoyBinds[joy][i] >> 16) & 3) != player_idx) continue;\r
720ee7f6 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
e67f6454 636 // limit..\r
637 strkeys[20] = 0;\r
638\r
95151aea 639 return strkeys;\r
640}\r
641\r
e67f6454 642static void unbind_action(int action, int pl_idx, int joy)\r
95151aea 643{\r
644 int i, u;\r
645\r
e67f6454 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
b4c711c3 652 }\r
e67f6454 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
b4c711c3 663 for (i = 0; i < 32; i++) {\r
e67f6454 664 if (pl_idx >= 0 && (currentConfig.JoyBinds[joy-1][i]&0x30000) != (pl_idx<<16)) continue;\r
665 currentConfig.JoyBinds[joy-1][i] &= ~action;\r
b4c711c3 666 }\r
e67f6454 667 }\r
95151aea 668}\r
669\r
b4c711c3 670static int count_bound_keys(int action, int pl_idx, int joy)\r
95151aea 671{\r
672 int i, keys = 0;\r
673\r
674 if (joy)\r
675 {\r
b4c711c3 676 for (i = 0; i < 32; i++) {\r
677 if (pl_idx >= 0 && (currentConfig.JoyBinds[joy-1][i]&0x30000) != (pl_idx<<16)) continue;\r
95151aea 678 if (currentConfig.JoyBinds[joy-1][i] & action) keys++;\r
b4c711c3 679 }\r
95151aea 680 }\r
681 else\r
682 {\r
b4c711c3 683 for (i = 0; i < 32; i++) {\r
684 if (pl_idx >= 0 && (currentConfig.KeyBinds[i]&0x30000) != (pl_idx<<16)) continue;\r
95151aea 685 if (currentConfig.KeyBinds[i] & action) keys++;\r
b4c711c3 686 }\r
95151aea 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
e5d315a5 697 gp2x_pd_clone_buffer2();\r
95151aea 698 if (player_idx >= 0) {\r
13059a60 699 text_out16(80, 20, "Player %i controls", player_idx + 1);\r
b4c711c3 700 x = 80;\r
95151aea 701 } else {\r
13059a60 702 text_out16(80, 20, "Emulator controls");\r
95151aea 703 x = 40;\r
704 }\r
705\r
c7a4ff64 706 menu_draw_selection(x - 16, tl_y + sel*10, (player_idx >= 0) ? 66 : 130);\r
13059a60 707\r
95151aea 708 y = tl_y;\r
709 for (i = 0; i < opt_cnt; i++, y+=10)\r
13059a60 710 text_out16(x, y, "%s : %s", opts[i].name, action_binds(player_idx, opts[i].mask));\r
95151aea 711\r
13059a60 712 text_out16(x, y, "Done");\r
95151aea 713\r
714 if (sel < opt_cnt) {\r
13059a60 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
95151aea 719 } else {\r
13059a60 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
95151aea 723 }\r
13059a60 724 menu_flip();\r
720ee7f6 725}\r
726\r
95151aea 727static void key_config_loop(const bind_action_t *opts, int opt_cnt, int player_idx)\r
720ee7f6 728{\r
95151aea 729 int joy = 0, sel = 0, menu_sel_max = opt_cnt, prev_select = 0, i;\r
720ee7f6 730 unsigned long inp = 0;\r
731\r
732 for (;;)\r
733 {\r
95151aea 734 draw_key_config(opts, opt_cnt, player_idx, sel);\r
720ee7f6 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
95151aea 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
720ee7f6 742 }\r
95151aea 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
e67f6454 749 unbind_action(opts[sel].mask, player_idx, -1);\r
95151aea 750 prev_select = inp & GP2X_SELECT;\r
720ee7f6 751 inp &= CONFIGURABLE_KEYS;\r
752 inp &= ~GP2X_SELECT;\r
720ee7f6 753 for (i = 0; i < 32; i++)\r
754 if (inp & (1 << i)) {\r
b4c711c3 755 if (count_bound_keys(opts[sel].mask, player_idx, 0) >= 2)\r
95151aea 756 currentConfig.KeyBinds[i] &= ~opts[sel].mask; // allow to unbind only\r
757 else currentConfig.KeyBinds[i] ^= opts[sel].mask;\r
7695af0a 758 if (player_idx >= 0 && (currentConfig.KeyBinds[i] & opts[sel].mask)) {\r
95151aea 759 currentConfig.KeyBinds[i] &= ~(3 << 16);\r
760 currentConfig.KeyBinds[i] |= player_idx << 16;\r
761 }\r
720ee7f6 762 }\r
95151aea 763 }\r
764 else if (sel < opt_cnt)\r
765 {\r
720ee7f6 766 for (i = 0; i < 32; i++)\r
767 if (inp & (1 << i)) {\r
e67f6454 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
95151aea 776 }\r
720ee7f6 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
e5d315a5 788 gp2x_pd_clone_buffer2();\r
c7a4ff64 789 menu_draw_selection(tl_x - 16, tl_y + menu_sel*10, 138);\r
720ee7f6 790\r
13059a60 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
720ee7f6 795\r
796 tl_x = 25;\r
13059a60 797 text_out16(tl_x, (y=110), "USB joys detected:");\r
720ee7f6 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
13059a60 801 text_out16(tl_x, (y+=10), "%i: %s", i+1, joyname);\r
720ee7f6 802 }\r
803 } else {\r
13059a60 804 text_out16(tl_x, (y+=10), "none");\r
720ee7f6 805 }\r
806\r
13059a60 807 menu_flip();\r
720ee7f6 808}\r
809\r
95151aea 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
95151aea 833 { "Load State ", 1<<28 },\r
834 { "Save State ", 1<<27 },\r
95151aea 835 { "Prev Save Slot ", 1<<25 },\r
836 { "Next Save Slot ", 1<<24 },\r
13059a60 837 { "Switch Renderer", 1<<26 },\r
838 { "Volume Down ", 1<<30 },\r
839 { "Volume Up ", 1<<29 },\r
95151aea 840 { "Enter Menu ", 1<<23 },\r
841};\r
842\r
720ee7f6 843static void kc_sel_loop(void)\r
844{\r
95151aea 845 int menu_sel = 3, menu_sel_max = 3;\r
720ee7f6 846 unsigned long inp = 0;\r
95151aea 847 int is_6button = currentConfig.PicoOpt & 0x020;\r
720ee7f6 848\r
95151aea 849 while (1)\r
720ee7f6 850 {\r
851 draw_kc_sel(menu_sel);\r
852 inp = wait_for_input(GP2X_UP|GP2X_DOWN|GP2X_B|GP2X_X);\r
95151aea 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
720ee7f6 856 switch (menu_sel) {\r
95151aea 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
720ee7f6 862 default: return;\r
863 }\r
864 }\r
95151aea 865 if (inp & GP2X_X) return;\r
720ee7f6 866 }\r
867}\r
868\r
869\r
daf91588 870// --------- sega/mega cd options ----------\r
871\r
59633198 872menu_entry cdopt_entries[] =\r
daf91588 873{\r
59633198 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
13059a60 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
59633198 905 case MA_CDOPT_READAHEAD:\r
906 if (PicoCDBuffers > 1) sprintf(ra_buff, "%5iK", PicoCDBuffers * 2);\r
907 else strcpy(ra_buff, " OFF");\r
13059a60 908 text_out16(x, y, "ReadAhead buffer %s", ra_buff);\r
59633198 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
5f9922e6 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
daf91588 922\r
e5d315a5 923 gp2x_pd_clone_buffer2();\r
924\r
c7a4ff64 925 menu_draw_selection(tl_x - 16, tl_y + menu_sel*10, 246);\r
daf91588 926\r
13059a60 927 me_draw(cdopt_entries, CDOPT_ENTRY_COUNT, tl_x, tl_y, menu_cdopt_cust_draw, bios_names);\r
daf91588 928\r
59633198 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
13059a60 933 text_out16(tl_x, 210, "Press start to test selected BIOS");\r
daf91588 934\r
13059a60 935 menu_flip();\r
daf91588 936}\r
937\r
938static void cd_menu_loop_options(void)\r
939{\r
59d0f042 940 static int menu_sel = 0;\r
941 int menu_sel_max = 10;\r
daf91588 942 unsigned long inp = 0;\r
59633198 943 struct bios_names_t bios_names;\r
944 menu_id selected_id;\r
945 char *bios, *p;\r
daf91588 946\r
f013066e 947 if (emu_findBios(4, &bios)) { // US\r
daf91588 948 for (p = bios+strlen(bios)-1; p > bios && *p != '/'; p--); p++;\r
59633198 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
daf91588 951\r
f013066e 952 if (emu_findBios(8, &bios)) { // EU\r
daf91588 953 for (p = bios+strlen(bios)-1; p > bios && *p != '/'; p--); p++;\r
59633198 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
daf91588 956\r
f013066e 957 if (emu_findBios(1, &bios)) { // JP\r
daf91588 958 for (p = bios+strlen(bios)-1; p > bios && *p != '/'; p--); p++;\r
59633198 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
daf91588 961\r
962 for(;;)\r
963 {\r
59633198 964 draw_cd_menu_options(menu_sel, &bios_names);\r
daf91588 965 inp = wait_for_input(GP2X_UP|GP2X_DOWN|GP2X_LEFT|GP2X_RIGHT|GP2X_B|GP2X_X|GP2X_A|GP2X_START);\r
59633198 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
daf91588 980 }\r
981 }\r
59633198 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
f013066e 991 if (emu_findBios(4, &bios)) { // test US\r
daf91588 992 strcpy(romFileName, bios);\r
993 engineState = PGS_ReloadRom;\r
994 return;\r
995 }\r
996 break;\r
59633198 997 case MA_CDOPT_TESTBIOS_EUR:\r
f013066e 998 if (emu_findBios(8, &bios)) { // test EU\r
daf91588 999 strcpy(romFileName, bios);\r
1000 engineState = PGS_ReloadRom;\r
1001 return;\r
1002 }\r
1003 break;\r
59633198 1004 case MA_CDOPT_TESTBIOS_JAP:\r
f013066e 1005 if (emu_findBios(1, &bios)) { // test JP\r
daf91588 1006 strcpy(romFileName, bios);\r
1007 engineState = PGS_ReloadRom;\r
1008 return;\r
1009 }\r
1010 break;\r
59633198 1011 default:\r
1012 break;\r
daf91588 1013 }\r
1014 }\r
59633198 1015 if (inp & (GP2X_X|GP2X_A)) return;\r
daf91588 1016 }\r
1017}\r
1018\r
1019\r
1020// --------- advanced options ----------\r
1021\r
59633198 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
c7a4ff64 1026 { "Perfect vsync", MB_ONOFF, MA_OPT2_VSYNC, &currentConfig.EmuOpt, 0x2000, 0, 0, 1 },\r
59633198 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
13059a60 1043 text_out16(x, y, "Gamma correction %i.%02i", currentConfig.gamma / 100, currentConfig.gamma%100);\r
59633198 1044 else if (entry->id == MA_OPT2_SQUIDGEHACK)\r
13059a60 1045 text_out16(x, y, "squidgehack (now %s %s", mmuhack_status ? "active) " : "inactive)",\r
59633198 1046 (currentConfig.EmuOpt&0x0010)?"ON":"OFF");\r
1047}\r
1048\r
1049\r
720ee7f6 1050static void draw_amenu_options(int menu_sel)\r
1051{\r
59633198 1052 int tl_x = 25, tl_y = 50;\r
720ee7f6 1053\r
e5d315a5 1054 gp2x_pd_clone_buffer2();\r
1055\r
c7a4ff64 1056 menu_draw_selection(tl_x - 16, tl_y + menu_sel*10, 252);\r
720ee7f6 1057\r
13059a60 1058 me_draw(opt2_entries, OPT2_ENTRY_COUNT, tl_x, tl_y, menu_opt2_cust_draw, NULL);\r
720ee7f6 1059\r
13059a60 1060 menu_flip();\r
720ee7f6 1061}\r
1062\r
1063static void amenu_loop_options(void)\r
1064{\r
59d0f042 1065 static int menu_sel = 0;\r
59633198 1066 int menu_sel_max;\r
720ee7f6 1067 unsigned long inp = 0;\r
59633198 1068 menu_id selected_id;\r
1069\r
1070 menu_sel_max = me_count_enabled(opt2_entries, OPT2_ENTRY_COUNT) - 1;\r
720ee7f6 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
59633198 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
720ee7f6 1089 }\r
1090 }\r
59633198 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
720ee7f6 1095 }\r
1096 }\r
59633198 1097 if (inp & (GP2X_X|GP2X_A)) return;\r
720ee7f6 1098 }\r
1099}\r
1100\r
1101// -------------- options --------------\r
1102\r
59633198 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
979ba09f 1131static const char *region_name(unsigned int code)\r
720ee7f6 1132{\r
979ba09f 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
720ee7f6 1151}\r
1152\r
59633198 1153\r
1154static void menu_opt_cust_draw(const menu_entry *entry, int x, int y, void *param)\r
720ee7f6 1155{\r
59633198 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
13059a60 1167 text_out16(x, y, "Renderer: %s", str);\r
59633198 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
13059a60 1176 text_out16(x, y, "Scaling: %s", str);\r
59633198 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
13059a60 1182 text_out16(x, y, "Frameskip %s", str24);\r
59633198 1183 break;\r
1184 case MA_OPT_SOUND_QUALITY:\r
1185 str = (currentConfig.PicoOpt&0x08)?"stereo":"mono";\r
13059a60 1186 text_out16(x, y, "Sound Quality: %5iHz %s", currentConfig.PsndRate, str);\r
59633198 1187 break;\r
1188 case MA_OPT_REGION:\r
13059a60 1189 text_out16(x, y, "Region: %s", region_name(currentConfig.PicoRegion));\r
59633198 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
13059a60 1198 text_out16(x, y, "Confirm savestate %s", str);\r
59633198 1199 break;\r
1200 case MA_OPT_CPU_CLOCKS:\r
13059a60 1201 text_out16(x, y, "GP2X CPU clocks %iMhz", currentConfig.CPUclock);\r
59633198 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
13059a60 1206 text_out16(x, y, "Save cfg as default%s", str24);\r
59633198 1207 break;\r
1208 case MA_OPT_LOADCFG:\r
13059a60 1209 text_out16(x, y, "Load cfg from profile %i", config_slot);\r
59633198 1210 break;\r
1211 default:\r
1212 printf("%s: unimplemented (%i)\n", __FUNCTION__, entry->id);\r
1213 break;\r
46ede6a6 1214 }\r
59633198 1215}\r
1216\r
1217\r
1218\r
1219static void draw_menu_options(int menu_sel)\r
1220{\r
13059a60 1221 int tl_x = 25, tl_y = 24;\r
720ee7f6 1222\r
e5d315a5 1223 gp2x_pd_clone_buffer2();\r
1224\r
c7a4ff64 1225 menu_draw_selection(tl_x - 16, tl_y + menu_sel*10, 284);\r
720ee7f6 1226\r
13059a60 1227 me_draw(opt_entries, OPT_ENTRY_COUNT, tl_x, tl_y, menu_opt_cust_draw, NULL);\r
720ee7f6 1228\r
13059a60 1229 menu_flip();\r
720ee7f6 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
979ba09f 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
720ee7f6 1269static void menu_options_save(void)\r
1270{\r
720ee7f6 1271 PicoOpt = currentConfig.PicoOpt;\r
1272 PsndRate = currentConfig.PsndRate;\r
1273 PicoRegionOverride = currentConfig.PicoRegion;\r
426ecc58 1274 if (PicoRegionOverride) {\r
1275 // force setting possibly changed..\r
1276 Pico.m.pal = (PicoRegionOverride == 2 || PicoRegionOverride == 8) ? 1 : 0;\r
1277 }\r
95151aea 1278 if (!(PicoOpt & 0x20)) {\r
1279 // unbind XYZ MODE, just in case\r
e67f6454 1280 unbind_action(0xf00, -1, -1);\r
720ee7f6 1281 }\r
1282}\r
1283\r
daf91588 1284static int menu_loop_options(void)\r
720ee7f6 1285{\r
59d0f042 1286 static int menu_sel = 0;\r
59633198 1287 int menu_sel_max, ret;\r
720ee7f6 1288 unsigned long inp = 0;\r
59633198 1289 menu_id selected_id;\r
720ee7f6 1290\r
979ba09f 1291 currentConfig.PicoOpt = PicoOpt;\r
1292 currentConfig.PsndRate = PsndRate;\r
1293 currentConfig.PicoRegion = PicoRegionOverride;\r
720ee7f6 1294\r
59633198 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
95151aea 1298 if (menu_sel > menu_sel_max) menu_sel = menu_sel_max;\r
59633198 1299\r
1300 while (1)\r
720ee7f6 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
59633198 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
95151aea 1394 ret = emu_ReadConfig(1, 1);\r
1395 if (!ret) ret = emu_ReadConfig(0, 1);\r
1396 if (ret) strcpy(menuErrorMsg, "config loaded");\r
59633198 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
720ee7f6 1403 }\r
1404 }\r
979ba09f 1405 if(inp & (GP2X_X|GP2X_A)) {\r
720ee7f6 1406 menu_options_save();\r
daf91588 1407 return 0; // done (update, no write)\r
720ee7f6 1408 }\r
720ee7f6 1409 }\r
1410}\r
1411\r
1412// -------------- credits --------------\r
1413\r
1414static void draw_menu_credits(void)\r
1415{\r
13059a60 1416 int tl_x = 15, tl_y = 64, y;\r
e5d315a5 1417 gp2x_pd_clone_buffer2();\r
720ee7f6 1418\r
13059a60 1419 text_out16(tl_x, 20, "PicoDrive v" VERSION " (c) notaz, 2006,2007");\r
720ee7f6 1420 y = tl_y;\r
13059a60 1421 text_out16(tl_x, y, "Credits:");\r
4b8f4f3c 1422 text_out16(tl_x, (y+=10), "fDave: Cyclone 68000 core,");\r
13059a60 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
f9a37a02 1435 text_out16(tl_x, (y+=10), "ketchupgun: skin design");\r
13059a60 1436\r
1437 menu_flip();\r
720ee7f6 1438}\r
1439\r
1440\r
1441// -------------- root menu --------------\r
1442\r
59633198 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
720ee7f6 1459static void draw_menu_root(int menu_sel)\r
1460{\r
59633198 1461 const int tl_x = 70, tl_y = 70;\r
1462\r
e5d315a5 1463 gp2x_pd_clone_buffer2();\r
720ee7f6 1464\r
13059a60 1465 text_out16(tl_x, 20, "PicoDrive v" VERSION);\r
1466\r
c7a4ff64 1467 menu_draw_selection(tl_x - 16, tl_y + menu_sel*10, 146);\r
720ee7f6 1468\r
59633198 1469 me_draw(main_entries, MAIN_ENTRY_COUNT, tl_x, tl_y, NULL, NULL);\r
720ee7f6 1470\r
720ee7f6 1471 // error\r
13059a60 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
720ee7f6 1477}\r
1478\r
1479\r
1480static void menu_loop_root(void)\r
1481{\r
59633198 1482 static int menu_sel = 0;\r
1483 int ret, menu_sel_max;\r
720ee7f6 1484 unsigned long inp = 0;\r
720ee7f6 1485\r
59633198 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
95151aea 1493 if (menu_sel > menu_sel_max) menu_sel = menu_sel_max;\r
720ee7f6 1494\r
79cad122 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
720ee7f6 1500 {\r
1501 draw_menu_root(menu_sel);\r
0ae6813e 1502 inp = wait_for_input(GP2X_UP|GP2X_DOWN|GP2X_B|GP2X_X|GP2X_SELECT|GP2X_L|GP2X_R);\r
59633198 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
0ae6813e 1505 if((inp & (GP2X_L|GP2X_R)) == (GP2X_L|GP2X_R)) debug_menu_loop();\r
720ee7f6 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
59633198 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
5e2e14f2 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
720ee7f6 1522 break;\r
59633198 1523 case MA_MAIN_SAVE_STATE:\r
720ee7f6 1524 if (rom_data) {\r
4ffd2858 1525 if(savestate_menu_loop(0))\r
720ee7f6 1526 continue;\r
720ee7f6 1527 engineState = PGS_Running;\r
1528 return;\r
1529 }\r
1530 break;\r
59633198 1531 case MA_MAIN_LOAD_STATE:\r
720ee7f6 1532 if (rom_data) {\r
4ffd2858 1533 if(savestate_menu_loop(1))\r
720ee7f6 1534 continue;\r
720ee7f6 1535 engineState = PGS_Running;\r
1536 return;\r
1537 }\r
1538 break;\r
59633198 1539 case MA_MAIN_RESET_GAME:\r
720ee7f6 1540 if (rom_data) {\r
1541 emu_ResetGame();\r
1542 engineState = PGS_Running;\r
1543 return;\r
1544 }\r
1545 break;\r
59633198 1546 case MA_MAIN_LOAD_ROM:\r
13059a60 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
720ee7f6 1557 selfname = romsel_loop(curr_path);\r
1558 if (selfname) {\r
1559 printf("selected file: %s\n", selfname);\r
720ee7f6 1560 engineState = PGS_ReloadRom;\r
13059a60 1561 return;\r
720ee7f6 1562 }\r
13059a60 1563 break;\r
1564 }\r
59633198 1565 case MA_MAIN_OPTIONS:\r
daf91588 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
720ee7f6 1570 break;\r
59633198 1571 case MA_MAIN_CONTROLS:\r
720ee7f6 1572 kc_sel_loop();\r
1573 break;\r
59633198 1574 case MA_MAIN_CREDITS:\r
720ee7f6 1575 draw_menu_credits();\r
1576 usleep(500*1000);\r
1577 inp = wait_for_input(GP2X_B|GP2X_X);\r
1578 break;\r
59633198 1579 case MA_MAIN_EXIT:\r
720ee7f6 1580 engineState = PGS_Quit;\r
1581 return;\r
59633198 1582 case MA_MAIN_PATCHES:\r
70d2ecc5 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
59633198 1590 default:\r
1591 printf("%s: something unknown selected\n", __FUNCTION__);\r
1592 break;\r
720ee7f6 1593 }\r
1594 }\r
1595 menuErrorMsg[0] = 0; // clear error msg\r
1596 }\r
1597}\r
1598\r
f9a37a02 1599static void menu_darken_bg(void *dst, int pixels, int darker)\r
720ee7f6 1600{\r
13059a60 1601 unsigned int *screen = dst;\r
1602 pixels /= 2;\r
f9a37a02 1603 if (darker)\r
13059a60 1604 {\r
f9a37a02 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
13059a60 1618 }\r
1619}\r
e5d315a5 1620\r
13059a60 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
f9a37a02 1627 menu_darken_bg((char *)gp2x_screen + 320*8*2, 320*224, 1);\r
13059a60 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
4ffd2858 1634 }\r
720ee7f6 1635\r
13059a60 1636 // copy to buffer2\r
1637 gp2x_memcpy_buffers((1<<2), gp2x_screen, 0, 320*240*2);\r
4ffd2858 1638}\r
1639\r
1640static void menu_gfx_prepare(void)\r
1641{\r
13059a60 1642 menu_prepare_bg(rom_data != NULL);\r
4ffd2858 1643\r
13059a60 1644 // switch to 16bpp\r
1645 gp2x_video_changemode2(16);\r
79cad122 1646 gp2x_video_RGB_setscaling(0, 320, 240);\r
13059a60 1647 menu_flip();\r
e5d315a5 1648}\r
1649\r
1650\r
1651void menu_loop(void)\r
1652{\r
1653 menu_gfx_prepare();\r
720ee7f6 1654\r
1655 menu_loop_root();\r
1656\r
1657 menuErrorMsg[0] = 0;\r
1658}\r
5e2e14f2 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
13059a60 1666 memset(gp2x_screen, 0, 320*240*2);\r
5e2e14f2 1667\r
13059a60 1668 text_out16(tl_x, 20, "The unit is about to");\r
1669 text_out16(tl_x, 30, "close the CD tray.");\r
5e2e14f2 1670\r
1671 y = tl_y;\r
13059a60 1672 text_out16(tl_x, y, "Load new CD image");\r
1673 text_out16(tl_x, (y+=10), "Insert nothing");\r
5e2e14f2 1674\r
1675 // draw cursor\r
13059a60 1676 text_out16(tl_x - 16, tl_y + menu_sel*10, ">");\r
5e2e14f2 1677 // error\r
13059a60 1678 if (menuErrorMsg[0]) text_out16(5, 226, menuErrorMsg);\r
1679 menu_flip();\r
5e2e14f2 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
c7a4ff64 1690 gp2x_memset_all_buffers(0, 0, 320*240*2);\r
5e2e14f2 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
f013066e 1719 cd_type = emu_cdCheck(NULL);\r
5e2e14f2 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