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