some minor adjustments
[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
136static void load_progress_cb(int percent)\r
137{\r
138 int ln, len = percent * 320 / 100;\r
13059a60 139 unsigned short *dst = (unsigned short *)gp2x_screen + 320*20;\r
b9f8cb3d 140\r
141 if (len > 320) len = 320;\r
142 for (ln = 10; ln > 0; ln--, dst += 320)\r
13059a60 143 memset(dst, 0xff, len*2);\r
144 menu_flip();\r
b9f8cb3d 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
13059a60 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
b9f8cb3d 159 PicoCartLoadProgressCB = load_progress_cb;\r
160}\r
161\r
162void menu_romload_end(void)\r
163{\r
164 PicoCartLoadProgressCB = NULL;\r
13059a60 165 smalltext_out16(1, 30, "Starting emulation...", 0xffff);\r
166 menu_flip();\r
b9f8cb3d 167}\r
168\r
720ee7f6 169// -------------- ROM selector --------------\r
170\r
13059a60 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
6d741b32 175 static const char *rom_exts[] = { "zip", "bin", "smd", "gen", "iso", "cso" };\r
13059a60 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
720ee7f6 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
f9a37a02 194 gp2x_pd_clone_buffer2();\r
195\r
196 if (rom_data == NULL) {\r
197 menu_darken_bg(gp2x_screen, 320*240, 0);\r
13059a60 198 }\r
199\r
f9a37a02 200 menu_darken_bg((char *)gp2x_screen + 320*120*2, 320*8, 0);\r
720ee7f6 201\r
202 if(start - 2 >= 0)\r
13059a60 203 smalltext_out16_lim(14, (start - 2)*10, curdir, 0xffff, 53-2);\r
720ee7f6 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
13059a60 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
720ee7f6 211 } else {\r
13059a60 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
720ee7f6 214 }\r
215 }\r
13059a60 216 text_out16(5, 120, ">");\r
217 menu_flip();\r
720ee7f6 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
79cad122 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
0d0558bd 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
720ee7f6 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
0d0558bd 270 n = scandir(curr_path, &namelist, scandir_filter, scandir_cmp);\r
720ee7f6 271 if (n < 0) {\r
272 // try root\r
0d0558bd 273 n = scandir("/", &namelist, scandir_filter, scandir_cmp);\r
720ee7f6 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
5111820c 296 inp = wait_for_input(GP2X_UP|GP2X_DOWN|GP2X_LEFT|GP2X_RIGHT|GP2X_L|GP2X_R|GP2X_B|GP2X_X);\r
720ee7f6 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
5e2e14f2 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
720ee7f6 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
0ae6813e 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
13059a60 376 smalltext_out16_lim(1, line*10, str, 0xffff, len);\r
0ae6813e 377 if (*p == 0) break;\r
378 p++; str = p;\r
379 }\r
13059a60 380 menu_flip();\r
0ae6813e 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
70d2ecc5 389// ------------ patch/gg menu ------------\r
390\r
391static void draw_patchlist(int sel)\r
392{\r
13059a60 393 int start, i, pos, active;\r
70d2ecc5 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
13059a60 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
70d2ecc5 406 }\r
407 pos = start + i;\r
13059a60 408 if (pos < 24) smalltext_out16_lim(14, pos*10, "done", 0xffff, 4);\r
70d2ecc5 409\r
13059a60 410 text_out16(5, 120, ">");\r
411 menu_flip();\r
70d2ecc5 412}\r
413\r
414\r
0ae6813e 415static void patches_menu_loop(void)\r
70d2ecc5 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
4ffd2858 438// ------------ savestate loader ------------\r
439\r
4ffd2858 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
f013066e 450 if (emu_checkSaveFile(slot))\r
4ffd2858 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
f013066e 478 emu_setSaveStateCbs(1);\r
4ffd2858 479 } else {\r
480 file = fopen(fname, "rb");\r
f013066e 481 emu_setSaveStateCbs(0);\r
4ffd2858 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
f013066e 499 emu_forcedFrame();\r
13059a60 500 menu_prepare_bg(1);\r
4ffd2858 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
13059a60 517 text_out16(tl_x, 30, is_loading ? "Load state" : "Save state");\r
518\r
c7a4ff64 519 menu_draw_selection(tl_x - 16, tl_y + menu_sel*10, 108);\r
4ffd2858 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
13059a60 525 text_out16(tl_x, y, "SLOT %i (%s)", i, (state_slot_flags & (1 << i)) ? "USED" : "free");\r
4ffd2858 526 }\r
13059a60 527 text_out16(tl_x, y, "back");\r
4ffd2858 528\r
13059a60 529 menu_flip();\r
4ffd2858 530}\r
531\r
532static int savestate_menu_loop(int is_loading)\r
533{\r
59d0f042 534 static int menu_sel = 10;\r
535 int menu_sel_max = 10;\r
4ffd2858 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
720ee7f6 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
95151aea 584static char *action_binds(int player_idx, int action_mask)\r
720ee7f6 585{\r
95151aea 586 static char strkeys[32*5];\r
720ee7f6 587 int joy, i;\r
588\r
589 strkeys[0] = 0;\r
95151aea 590 for (i = 0; i < 32; i++) // i is key index\r
720ee7f6 591 {\r
95151aea 592 if (currentConfig.KeyBinds[i] & action_mask)\r
720ee7f6 593 {\r
95151aea 594 if (player_idx >= 0 && ((currentConfig.KeyBinds[i] >> 16) & 3) != player_idx) continue;\r
720ee7f6 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
95151aea 603 if (currentConfig.JoyBinds[joy][i] & action_mask)\r
720ee7f6 604 {\r
95151aea 605 if (player_idx >= 0 && ((currentConfig.JoyBinds[joy][i] >> 16) & 3) != player_idx) continue;\r
720ee7f6 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
e67f6454 615 // limit..\r
616 strkeys[20] = 0;\r
617\r
95151aea 618 return strkeys;\r
619}\r
620\r
e67f6454 621static void unbind_action(int action, int pl_idx, int joy)\r
95151aea 622{\r
623 int i, u;\r
624\r
e67f6454 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
b4c711c3 631 }\r
e67f6454 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
b4c711c3 642 for (i = 0; i < 32; i++) {\r
e67f6454 643 if (pl_idx >= 0 && (currentConfig.JoyBinds[joy-1][i]&0x30000) != (pl_idx<<16)) continue;\r
644 currentConfig.JoyBinds[joy-1][i] &= ~action;\r
b4c711c3 645 }\r
e67f6454 646 }\r
95151aea 647}\r
648\r
b4c711c3 649static int count_bound_keys(int action, int pl_idx, int joy)\r
95151aea 650{\r
651 int i, keys = 0;\r
652\r
653 if (joy)\r
654 {\r
b4c711c3 655 for (i = 0; i < 32; i++) {\r
656 if (pl_idx >= 0 && (currentConfig.JoyBinds[joy-1][i]&0x30000) != (pl_idx<<16)) continue;\r
95151aea 657 if (currentConfig.JoyBinds[joy-1][i] & action) keys++;\r
b4c711c3 658 }\r
95151aea 659 }\r
660 else\r
661 {\r
b4c711c3 662 for (i = 0; i < 32; i++) {\r
663 if (pl_idx >= 0 && (currentConfig.KeyBinds[i]&0x30000) != (pl_idx<<16)) continue;\r
95151aea 664 if (currentConfig.KeyBinds[i] & action) keys++;\r
b4c711c3 665 }\r
95151aea 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
e5d315a5 676 gp2x_pd_clone_buffer2();\r
95151aea 677 if (player_idx >= 0) {\r
13059a60 678 text_out16(80, 20, "Player %i controls", player_idx + 1);\r
b4c711c3 679 x = 80;\r
95151aea 680 } else {\r
13059a60 681 text_out16(80, 20, "Emulator controls");\r
95151aea 682 x = 40;\r
683 }\r
684\r
c7a4ff64 685 menu_draw_selection(x - 16, tl_y + sel*10, (player_idx >= 0) ? 66 : 130);\r
13059a60 686\r
95151aea 687 y = tl_y;\r
688 for (i = 0; i < opt_cnt; i++, y+=10)\r
13059a60 689 text_out16(x, y, "%s : %s", opts[i].name, action_binds(player_idx, opts[i].mask));\r
95151aea 690\r
13059a60 691 text_out16(x, y, "Done");\r
95151aea 692\r
693 if (sel < opt_cnt) {\r
13059a60 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
95151aea 698 } else {\r
13059a60 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
95151aea 702 }\r
13059a60 703 menu_flip();\r
720ee7f6 704}\r
705\r
95151aea 706static void key_config_loop(const bind_action_t *opts, int opt_cnt, int player_idx)\r
720ee7f6 707{\r
95151aea 708 int joy = 0, sel = 0, menu_sel_max = opt_cnt, prev_select = 0, i;\r
720ee7f6 709 unsigned long inp = 0;\r
710\r
711 for (;;)\r
712 {\r
95151aea 713 draw_key_config(opts, opt_cnt, player_idx, sel);\r
720ee7f6 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
95151aea 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
720ee7f6 721 }\r
95151aea 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
e67f6454 728 unbind_action(opts[sel].mask, player_idx, -1);\r
95151aea 729 prev_select = inp & GP2X_SELECT;\r
720ee7f6 730 inp &= CONFIGURABLE_KEYS;\r
731 inp &= ~GP2X_SELECT;\r
720ee7f6 732 for (i = 0; i < 32; i++)\r
733 if (inp & (1 << i)) {\r
b4c711c3 734 if (count_bound_keys(opts[sel].mask, player_idx, 0) >= 2)\r
95151aea 735 currentConfig.KeyBinds[i] &= ~opts[sel].mask; // allow to unbind only\r
736 else currentConfig.KeyBinds[i] ^= opts[sel].mask;\r
7695af0a 737 if (player_idx >= 0 && (currentConfig.KeyBinds[i] & opts[sel].mask)) {\r
95151aea 738 currentConfig.KeyBinds[i] &= ~(3 << 16);\r
739 currentConfig.KeyBinds[i] |= player_idx << 16;\r
740 }\r
720ee7f6 741 }\r
95151aea 742 }\r
743 else if (sel < opt_cnt)\r
744 {\r
720ee7f6 745 for (i = 0; i < 32; i++)\r
746 if (inp & (1 << i)) {\r
e67f6454 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
95151aea 755 }\r
720ee7f6 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
e5d315a5 767 gp2x_pd_clone_buffer2();\r
c7a4ff64 768 menu_draw_selection(tl_x - 16, tl_y + menu_sel*10, 138);\r
720ee7f6 769\r
13059a60 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
720ee7f6 774\r
775 tl_x = 25;\r
13059a60 776 text_out16(tl_x, (y=110), "USB joys detected:");\r
720ee7f6 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
13059a60 780 text_out16(tl_x, (y+=10), "%i: %s", i+1, joyname);\r
720ee7f6 781 }\r
782 } else {\r
13059a60 783 text_out16(tl_x, (y+=10), "none");\r
720ee7f6 784 }\r
785\r
13059a60 786 menu_flip();\r
720ee7f6 787}\r
788\r
95151aea 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
95151aea 812 { "Load State ", 1<<28 },\r
813 { "Save State ", 1<<27 },\r
95151aea 814 { "Prev Save Slot ", 1<<25 },\r
815 { "Next Save Slot ", 1<<24 },\r
13059a60 816 { "Switch Renderer", 1<<26 },\r
817 { "Volume Down ", 1<<30 },\r
818 { "Volume Up ", 1<<29 },\r
95151aea 819 { "Enter Menu ", 1<<23 },\r
820};\r
821\r
720ee7f6 822static void kc_sel_loop(void)\r
823{\r
95151aea 824 int menu_sel = 3, menu_sel_max = 3;\r
720ee7f6 825 unsigned long inp = 0;\r
95151aea 826 int is_6button = currentConfig.PicoOpt & 0x020;\r
720ee7f6 827\r
95151aea 828 while (1)\r
720ee7f6 829 {\r
830 draw_kc_sel(menu_sel);\r
831 inp = wait_for_input(GP2X_UP|GP2X_DOWN|GP2X_B|GP2X_X);\r
95151aea 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
720ee7f6 835 switch (menu_sel) {\r
95151aea 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
720ee7f6 841 default: return;\r
842 }\r
843 }\r
95151aea 844 if (inp & GP2X_X) return;\r
720ee7f6 845 }\r
846}\r
847\r
848\r
daf91588 849// --------- sega/mega cd options ----------\r
850\r
59633198 851menu_entry cdopt_entries[] =\r
daf91588 852{\r
59633198 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
13059a60 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
59633198 884 case MA_CDOPT_READAHEAD:\r
885 if (PicoCDBuffers > 1) sprintf(ra_buff, "%5iK", PicoCDBuffers * 2);\r
886 else strcpy(ra_buff, " OFF");\r
13059a60 887 text_out16(x, y, "ReadAhead buffer %s", ra_buff);\r
59633198 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
5f9922e6 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
daf91588 901\r
e5d315a5 902 gp2x_pd_clone_buffer2();\r
903\r
c7a4ff64 904 menu_draw_selection(tl_x - 16, tl_y + menu_sel*10, 246);\r
daf91588 905\r
13059a60 906 me_draw(cdopt_entries, CDOPT_ENTRY_COUNT, tl_x, tl_y, menu_cdopt_cust_draw, bios_names);\r
daf91588 907\r
59633198 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
13059a60 912 text_out16(tl_x, 210, "Press start to test selected BIOS");\r
daf91588 913\r
13059a60 914 menu_flip();\r
daf91588 915}\r
916\r
917static void cd_menu_loop_options(void)\r
918{\r
59d0f042 919 static int menu_sel = 0;\r
920 int menu_sel_max = 10;\r
daf91588 921 unsigned long inp = 0;\r
59633198 922 struct bios_names_t bios_names;\r
923 menu_id selected_id;\r
924 char *bios, *p;\r
daf91588 925\r
f013066e 926 if (emu_findBios(4, &bios)) { // US\r
daf91588 927 for (p = bios+strlen(bios)-1; p > bios && *p != '/'; p--); p++;\r
59633198 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
daf91588 930\r
f013066e 931 if (emu_findBios(8, &bios)) { // EU\r
daf91588 932 for (p = bios+strlen(bios)-1; p > bios && *p != '/'; p--); p++;\r
59633198 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
daf91588 935\r
f013066e 936 if (emu_findBios(1, &bios)) { // JP\r
daf91588 937 for (p = bios+strlen(bios)-1; p > bios && *p != '/'; p--); p++;\r
59633198 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
daf91588 940\r
941 for(;;)\r
942 {\r
59633198 943 draw_cd_menu_options(menu_sel, &bios_names);\r
daf91588 944 inp = wait_for_input(GP2X_UP|GP2X_DOWN|GP2X_LEFT|GP2X_RIGHT|GP2X_B|GP2X_X|GP2X_A|GP2X_START);\r
59633198 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
daf91588 959 }\r
960 }\r
59633198 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
f013066e 970 if (emu_findBios(4, &bios)) { // test US\r
daf91588 971 strcpy(romFileName, bios);\r
972 engineState = PGS_ReloadRom;\r
973 return;\r
974 }\r
975 break;\r
59633198 976 case MA_CDOPT_TESTBIOS_EUR:\r
f013066e 977 if (emu_findBios(8, &bios)) { // test EU\r
daf91588 978 strcpy(romFileName, bios);\r
979 engineState = PGS_ReloadRom;\r
980 return;\r
981 }\r
982 break;\r
59633198 983 case MA_CDOPT_TESTBIOS_JAP:\r
f013066e 984 if (emu_findBios(1, &bios)) { // test JP\r
daf91588 985 strcpy(romFileName, bios);\r
986 engineState = PGS_ReloadRom;\r
987 return;\r
988 }\r
989 break;\r
59633198 990 default:\r
991 break;\r
daf91588 992 }\r
993 }\r
59633198 994 if (inp & (GP2X_X|GP2X_A)) return;\r
daf91588 995 }\r
996}\r
997\r
998\r
999// --------- advanced options ----------\r
1000\r
59633198 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
c7a4ff64 1005 { "Perfect vsync", MB_ONOFF, MA_OPT2_VSYNC, &currentConfig.EmuOpt, 0x2000, 0, 0, 1 },\r
59633198 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
13059a60 1022 text_out16(x, y, "Gamma correction %i.%02i", currentConfig.gamma / 100, currentConfig.gamma%100);\r
59633198 1023 else if (entry->id == MA_OPT2_SQUIDGEHACK)\r
13059a60 1024 text_out16(x, y, "squidgehack (now %s %s", mmuhack_status ? "active) " : "inactive)",\r
59633198 1025 (currentConfig.EmuOpt&0x0010)?"ON":"OFF");\r
1026}\r
1027\r
1028\r
720ee7f6 1029static void draw_amenu_options(int menu_sel)\r
1030{\r
59633198 1031 int tl_x = 25, tl_y = 50;\r
720ee7f6 1032\r
e5d315a5 1033 gp2x_pd_clone_buffer2();\r
1034\r
c7a4ff64 1035 menu_draw_selection(tl_x - 16, tl_y + menu_sel*10, 252);\r
720ee7f6 1036\r
13059a60 1037 me_draw(opt2_entries, OPT2_ENTRY_COUNT, tl_x, tl_y, menu_opt2_cust_draw, NULL);\r
720ee7f6 1038\r
13059a60 1039 menu_flip();\r
720ee7f6 1040}\r
1041\r
1042static void amenu_loop_options(void)\r
1043{\r
59d0f042 1044 static int menu_sel = 0;\r
59633198 1045 int menu_sel_max;\r
720ee7f6 1046 unsigned long inp = 0;\r
59633198 1047 menu_id selected_id;\r
1048\r
1049 menu_sel_max = me_count_enabled(opt2_entries, OPT2_ENTRY_COUNT) - 1;\r
720ee7f6 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
59633198 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
720ee7f6 1068 }\r
1069 }\r
59633198 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
720ee7f6 1074 }\r
1075 }\r
59633198 1076 if (inp & (GP2X_X|GP2X_A)) return;\r
720ee7f6 1077 }\r
1078}\r
1079\r
1080// -------------- options --------------\r
1081\r
59633198 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
979ba09f 1110static const char *region_name(unsigned int code)\r
720ee7f6 1111{\r
979ba09f 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
720ee7f6 1130}\r
1131\r
59633198 1132\r
1133static void menu_opt_cust_draw(const menu_entry *entry, int x, int y, void *param)\r
720ee7f6 1134{\r
59633198 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
13059a60 1146 text_out16(x, y, "Renderer: %s", str);\r
59633198 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
13059a60 1155 text_out16(x, y, "Scaling: %s", str);\r
59633198 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
13059a60 1161 text_out16(x, y, "Frameskip %s", str24);\r
59633198 1162 break;\r
1163 case MA_OPT_SOUND_QUALITY:\r
1164 str = (currentConfig.PicoOpt&0x08)?"stereo":"mono";\r
13059a60 1165 text_out16(x, y, "Sound Quality: %5iHz %s", currentConfig.PsndRate, str);\r
59633198 1166 break;\r
1167 case MA_OPT_REGION:\r
13059a60 1168 text_out16(x, y, "Region: %s", region_name(currentConfig.PicoRegion));\r
59633198 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
13059a60 1177 text_out16(x, y, "Confirm savestate %s", str);\r
59633198 1178 break;\r
1179 case MA_OPT_CPU_CLOCKS:\r
13059a60 1180 text_out16(x, y, "GP2X CPU clocks %iMhz", currentConfig.CPUclock);\r
59633198 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
13059a60 1185 text_out16(x, y, "Save cfg as default%s", str24);\r
59633198 1186 break;\r
1187 case MA_OPT_LOADCFG:\r
13059a60 1188 text_out16(x, y, "Load cfg from profile %i", config_slot);\r
59633198 1189 break;\r
1190 default:\r
1191 printf("%s: unimplemented (%i)\n", __FUNCTION__, entry->id);\r
1192 break;\r
46ede6a6 1193 }\r
59633198 1194}\r
1195\r
1196\r
1197\r
1198static void draw_menu_options(int menu_sel)\r
1199{\r
13059a60 1200 int tl_x = 25, tl_y = 24;\r
720ee7f6 1201\r
e5d315a5 1202 gp2x_pd_clone_buffer2();\r
1203\r
c7a4ff64 1204 menu_draw_selection(tl_x - 16, tl_y + menu_sel*10, 284);\r
720ee7f6 1205\r
13059a60 1206 me_draw(opt_entries, OPT_ENTRY_COUNT, tl_x, tl_y, menu_opt_cust_draw, NULL);\r
720ee7f6 1207\r
13059a60 1208 menu_flip();\r
720ee7f6 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
979ba09f 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
720ee7f6 1248static void menu_options_save(void)\r
1249{\r
720ee7f6 1250 PicoOpt = currentConfig.PicoOpt;\r
1251 PsndRate = currentConfig.PsndRate;\r
1252 PicoRegionOverride = currentConfig.PicoRegion;\r
426ecc58 1253 if (PicoRegionOverride) {\r
1254 // force setting possibly changed..\r
1255 Pico.m.pal = (PicoRegionOverride == 2 || PicoRegionOverride == 8) ? 1 : 0;\r
1256 }\r
95151aea 1257 if (!(PicoOpt & 0x20)) {\r
1258 // unbind XYZ MODE, just in case\r
e67f6454 1259 unbind_action(0xf00, -1, -1);\r
720ee7f6 1260 }\r
1261}\r
1262\r
daf91588 1263static int menu_loop_options(void)\r
720ee7f6 1264{\r
59d0f042 1265 static int menu_sel = 0;\r
59633198 1266 int menu_sel_max, ret;\r
720ee7f6 1267 unsigned long inp = 0;\r
59633198 1268 menu_id selected_id;\r
720ee7f6 1269\r
979ba09f 1270 currentConfig.PicoOpt = PicoOpt;\r
1271 currentConfig.PsndRate = PsndRate;\r
1272 currentConfig.PicoRegion = PicoRegionOverride;\r
720ee7f6 1273\r
59633198 1274 me_enable(opt_entries, OPT_ENTRY_COUNT, MA_OPT_SAVECFG_GAME, rom_data != NULL);\r
1275 me_enable(opt_entries, OPT_ENTRY_COUNT, MA_OPT_LOADCFG, config_slot != config_slot_current);\r
1276 menu_sel_max = me_count_enabled(opt_entries, OPT_ENTRY_COUNT) - 1;\r
95151aea 1277 if (menu_sel > menu_sel_max) menu_sel = menu_sel_max;\r
59633198 1278\r
1279 while (1)\r
720ee7f6 1280 {\r
1281 draw_menu_options(menu_sel);\r
1282 inp = wait_for_input(GP2X_UP|GP2X_DOWN|GP2X_LEFT|GP2X_RIGHT|GP2X_B|GP2X_X|GP2X_A);\r
59633198 1283 if (inp & GP2X_UP ) { menu_sel--; if (menu_sel < 0) menu_sel = menu_sel_max; }\r
1284 if (inp & GP2X_DOWN) { menu_sel++; if (menu_sel > menu_sel_max) menu_sel = 0; }\r
1285 selected_id = me_index2id(opt_entries, OPT_ENTRY_COUNT, menu_sel);\r
1286 if (inp & (GP2X_LEFT|GP2X_RIGHT)) { // multi choise\r
1287 if (!me_process(opt_entries, OPT_ENTRY_COUNT, selected_id, (inp&GP2X_RIGHT) ? 1 : 0)) {\r
1288 switch (selected_id) {\r
1289 case MA_OPT_RENDERER:\r
1290 if (inp & GP2X_LEFT) {\r
1291 if ( currentConfig.PicoOpt&0x10) currentConfig.PicoOpt&= ~0x10;\r
1292 else if (!(currentConfig.EmuOpt &0x80))currentConfig.EmuOpt |= 0x80;\r
1293 else if ( currentConfig.EmuOpt &0x80) break;\r
1294 } else {\r
1295 if ( currentConfig.PicoOpt&0x10) break;\r
1296 else if (!(currentConfig.EmuOpt &0x80))currentConfig.PicoOpt|= 0x10;\r
1297 else if ( currentConfig.EmuOpt &0x80) currentConfig.EmuOpt &= ~0x80;\r
1298 }\r
1299 break;\r
1300 case MA_OPT_SOUND_QUALITY:\r
1301 if ((inp & GP2X_RIGHT) && currentConfig.PsndRate == 44100 && !(currentConfig.PicoOpt&0x08)) {\r
1302 currentConfig.PsndRate = 8000; currentConfig.PicoOpt|= 0x08;\r
1303 } else if ((inp & GP2X_LEFT) && currentConfig.PsndRate == 8000 && (currentConfig.PicoOpt&0x08)) {\r
1304 currentConfig.PsndRate = 44100; currentConfig.PicoOpt&=~0x08;\r
1305 } else currentConfig.PsndRate = sndrate_prevnext(currentConfig.PsndRate, inp & GP2X_RIGHT);\r
1306 break;\r
1307 case MA_OPT_REGION:\r
1308 region_prevnext(inp & GP2X_RIGHT);\r
1309 break;\r
1310 case MA_OPT_CONFIRM_STATES: {\r
1311 int n = ((currentConfig.EmuOpt>>9)&1) | ((currentConfig.EmuOpt>>10)&2);\r
1312 n += (inp & GP2X_LEFT) ? -1 : 1;\r
1313 if (n < 0) n = 0; else if (n > 3) n = 3;\r
1314 n |= n << 1; n &= ~2;\r
1315 currentConfig.EmuOpt &= ~0xa00;\r
1316 currentConfig.EmuOpt |= n << 9;\r
1317 break;\r
1318 }\r
1319 case MA_OPT_SAVE_SLOT:\r
1320 if (inp & GP2X_RIGHT) {\r
1321 state_slot++; if (state_slot > 9) state_slot = 0;\r
1322 } else {state_slot--; if (state_slot < 0) state_slot = 9;\r
1323 }\r
1324 break;\r
1325 case MA_OPT_CPU_CLOCKS:\r
1326 while ((inp = gp2x_joystick_read(1)) & (GP2X_LEFT|GP2X_RIGHT)) {\r
1327 currentConfig.CPUclock += (inp & GP2X_LEFT) ? -1 : 1;\r
1328 if (currentConfig.CPUclock < 1) currentConfig.CPUclock = 1;\r
1329 draw_menu_options(menu_sel);\r
1330 usleep(50*1000);\r
1331 }\r
1332 break;\r
1333 case MA_OPT_SAVECFG:\r
1334 case MA_OPT_SAVECFG_GAME:\r
1335 case MA_OPT_LOADCFG:\r
1336 config_slot += (inp&GP2X_RIGHT) ? 1 : -1;\r
1337 if (config_slot > 9) config_slot = 0;\r
1338 if (config_slot < 0) config_slot = 9;\r
1339 me_enable(opt_entries, OPT_ENTRY_COUNT, MA_OPT_LOADCFG, config_slot != config_slot_current);\r
1340 menu_sel_max = me_count_enabled(opt_entries, OPT_ENTRY_COUNT) - 1;\r
1341 if (menu_sel > menu_sel_max) menu_sel = menu_sel_max;\r
1342 break;\r
1343 default:\r
1344 //printf("%s: something unknown selected (%i)\n", __FUNCTION__, selected_id);\r
1345 break;\r
1346 }\r
1347 }\r
1348 }\r
1349 if (inp & GP2X_B) {\r
1350 if (!me_process(opt_entries, OPT_ENTRY_COUNT, selected_id, 1))\r
1351 {\r
1352 switch (selected_id)\r
1353 {\r
1354 case MA_OPT_SCD_OPTS:\r
1355 cd_menu_loop_options();\r
1356 if (engineState == PGS_ReloadRom)\r
1357 return 0; // test BIOS\r
1358 break;\r
1359 case MA_OPT_ADV_OPTS:\r
1360 amenu_loop_options();\r
1361 break;\r
1362 case MA_OPT_SAVECFG: // done (update and write)\r
1363 menu_options_save();\r
1364 if (emu_WriteConfig(0)) strcpy(menuErrorMsg, "config saved");\r
1365 else strcpy(menuErrorMsg, "failed to write config");\r
1366 return 1;\r
1367 case MA_OPT_SAVECFG_GAME: // done (update and write for current game)\r
1368 menu_options_save();\r
1369 if (emu_WriteConfig(1)) strcpy(menuErrorMsg, "config saved");\r
1370 else strcpy(menuErrorMsg, "failed to write config");\r
1371 return 1;\r
1372 case MA_OPT_LOADCFG:\r
95151aea 1373 ret = emu_ReadConfig(1, 1);\r
1374 if (!ret) ret = emu_ReadConfig(0, 1);\r
1375 if (ret) strcpy(menuErrorMsg, "config loaded");\r
59633198 1376 else strcpy(menuErrorMsg, "failed to load config");\r
1377 return 1;\r
1378 default:\r
1379 //printf("%s: something unknown selected (%i)\n", __FUNCTION__, selected_id);\r
1380 break;\r
1381 }\r
720ee7f6 1382 }\r
1383 }\r
979ba09f 1384 if(inp & (GP2X_X|GP2X_A)) {\r
720ee7f6 1385 menu_options_save();\r
daf91588 1386 return 0; // done (update, no write)\r
720ee7f6 1387 }\r
720ee7f6 1388 }\r
1389}\r
1390\r
1391// -------------- credits --------------\r
1392\r
1393static void draw_menu_credits(void)\r
1394{\r
13059a60 1395 int tl_x = 15, tl_y = 64, y;\r
e5d315a5 1396 gp2x_pd_clone_buffer2();\r
720ee7f6 1397\r
13059a60 1398 text_out16(tl_x, 20, "PicoDrive v" VERSION " (c) notaz, 2006,2007");\r
720ee7f6 1399 y = tl_y;\r
13059a60 1400 text_out16(tl_x, y, "Credits:");\r
4b8f4f3c 1401 text_out16(tl_x, (y+=10), "fDave: Cyclone 68000 core,");\r
13059a60 1402 text_out16(tl_x, (y+=10), " base code of PicoDrive");\r
1403 text_out16(tl_x, (y+=10), "Reesy & FluBBa: DrZ80 core");\r
1404 text_out16(tl_x, (y+=10), "MAME devs: YM2612 and SN76496 cores");\r
1405 text_out16(tl_x, (y+=10), "Charles MacDonald: Genesis hw docs");\r
1406 text_out16(tl_x, (y+=10), "Stephane Dallongeville:");\r
1407 text_out16(tl_x, (y+=10), " opensource Gens");\r
1408 text_out16(tl_x, (y+=10), "Haze: Genesis hw info");\r
1409 text_out16(tl_x, (y+=10), "rlyeh and others: minimal SDK");\r
1410 text_out16(tl_x, (y+=10), "Squidge: squidgehack");\r
1411 text_out16(tl_x, (y+=10), "Dzz: ARM940 sample");\r
1412 text_out16(tl_x, (y+=10), "GnoStiC / Puck2099: USB joystick");\r
1413 text_out16(tl_x, (y+=10), "craigix: GP2X hardware");\r
f9a37a02 1414 text_out16(tl_x, (y+=10), "ketchupgun: skin design");\r
13059a60 1415\r
1416 menu_flip();\r
720ee7f6 1417}\r
1418\r
1419\r
1420// -------------- root menu --------------\r
1421\r
59633198 1422menu_entry main_entries[] =\r
1423{\r
1424 { "Resume game", MB_NONE, MA_MAIN_RESUME_GAME, NULL, 0, 0, 0, 0 },\r
1425 { "Save State", MB_NONE, MA_MAIN_SAVE_STATE, NULL, 0, 0, 0, 0 },\r
1426 { "Load State", MB_NONE, MA_MAIN_LOAD_STATE, NULL, 0, 0, 0, 0 },\r
1427 { "Reset game", MB_NONE, MA_MAIN_RESET_GAME, NULL, 0, 0, 0, 0 },\r
1428 { "Load new ROM/ISO", MB_NONE, MA_MAIN_LOAD_ROM, NULL, 0, 0, 0, 1 },\r
1429 { "Change options", MB_NONE, MA_MAIN_OPTIONS, NULL, 0, 0, 0, 1 },\r
1430 { "Configure controls", MB_NONE, MA_MAIN_CONTROLS, NULL, 0, 0, 0, 1 },\r
1431 { "Credits", MB_NONE, MA_MAIN_CREDITS, NULL, 0, 0, 0, 1 },\r
1432 { "Patches / GameGenie",MB_NONE, MA_MAIN_PATCHES, NULL, 0, 0, 0, 0 },\r
1433 { "Exit", MB_NONE, MA_MAIN_EXIT, NULL, 0, 0, 0, 1 }\r
1434};\r
1435\r
1436#define MAIN_ENTRY_COUNT (sizeof(main_entries) / sizeof(main_entries[0]))\r
1437\r
720ee7f6 1438static void draw_menu_root(int menu_sel)\r
1439{\r
59633198 1440 const int tl_x = 70, tl_y = 70;\r
1441\r
e5d315a5 1442 gp2x_pd_clone_buffer2();\r
720ee7f6 1443\r
13059a60 1444 text_out16(tl_x, 20, "PicoDrive v" VERSION);\r
1445\r
c7a4ff64 1446 menu_draw_selection(tl_x - 16, tl_y + menu_sel*10, 146);\r
720ee7f6 1447\r
59633198 1448 me_draw(main_entries, MAIN_ENTRY_COUNT, tl_x, tl_y, NULL, NULL);\r
720ee7f6 1449\r
720ee7f6 1450 // error\r
13059a60 1451 if (menuErrorMsg[0]) {\r
1452 memset((char *)gp2x_screen + 320*224*2, 0, 320*16*2);\r
1453 text_out16(5, 226, menuErrorMsg);\r
1454 }\r
1455 menu_flip();\r
720ee7f6 1456}\r
1457\r
1458\r
1459static void menu_loop_root(void)\r
1460{\r
59633198 1461 static int menu_sel = 0;\r
1462 int ret, menu_sel_max;\r
720ee7f6 1463 unsigned long inp = 0;\r
720ee7f6 1464\r
59633198 1465 me_enable(main_entries, MAIN_ENTRY_COUNT, MA_MAIN_RESUME_GAME, rom_data != NULL);\r
1466 me_enable(main_entries, MAIN_ENTRY_COUNT, MA_MAIN_SAVE_STATE, rom_data != NULL);\r
1467 me_enable(main_entries, MAIN_ENTRY_COUNT, MA_MAIN_LOAD_STATE, rom_data != NULL);\r
1468 me_enable(main_entries, MAIN_ENTRY_COUNT, MA_MAIN_RESET_GAME, rom_data != NULL);\r
1469 me_enable(main_entries, MAIN_ENTRY_COUNT, MA_MAIN_PATCHES, PicoPatches != NULL);\r
1470\r
1471 menu_sel_max = me_count_enabled(main_entries, MAIN_ENTRY_COUNT) - 1;\r
95151aea 1472 if (menu_sel > menu_sel_max) menu_sel = menu_sel_max;\r
720ee7f6 1473\r
79cad122 1474 /* make sure action buttons are not pressed on entering menu */\r
1475 draw_menu_root(menu_sel);\r
1476 while (gp2x_joystick_read(1) & (GP2X_B|GP2X_X|GP2X_SELECT)) usleep(50*1000);\r
1477\r
1478 for (;;)\r
720ee7f6 1479 {\r
1480 draw_menu_root(menu_sel);\r
0ae6813e 1481 inp = wait_for_input(GP2X_UP|GP2X_DOWN|GP2X_B|GP2X_X|GP2X_SELECT|GP2X_L|GP2X_R);\r
59633198 1482 if(inp & GP2X_UP ) { menu_sel--; if (menu_sel < 0) menu_sel = menu_sel_max; }\r
1483 if(inp & GP2X_DOWN) { menu_sel++; if (menu_sel > menu_sel_max) menu_sel = 0; }\r
0ae6813e 1484 if((inp & (GP2X_L|GP2X_R)) == (GP2X_L|GP2X_R)) debug_menu_loop();\r
720ee7f6 1485 if(inp &(GP2X_SELECT|GP2X_X)){\r
1486 if (rom_data) {\r
1487 while (gp2x_joystick_read(1) & (GP2X_SELECT|GP2X_X)) usleep(50*1000); // wait until select is released\r
1488 engineState = PGS_Running;\r
1489 break;\r
1490 }\r
1491 }\r
59633198 1492 if(inp & GP2X_B) {\r
1493 switch (me_index2id(main_entries, MAIN_ENTRY_COUNT, menu_sel))\r
1494 {\r
1495 case MA_MAIN_RESUME_GAME:\r
5e2e14f2 1496 if (rom_data) {\r
1497 while (gp2x_joystick_read(1) & GP2X_B) usleep(50*1000);\r
1498 engineState = PGS_Running;\r
1499 return;\r
1500 }\r
720ee7f6 1501 break;\r
59633198 1502 case MA_MAIN_SAVE_STATE:\r
720ee7f6 1503 if (rom_data) {\r
4ffd2858 1504 if(savestate_menu_loop(0))\r
720ee7f6 1505 continue;\r
720ee7f6 1506 engineState = PGS_Running;\r
1507 return;\r
1508 }\r
1509 break;\r
59633198 1510 case MA_MAIN_LOAD_STATE:\r
720ee7f6 1511 if (rom_data) {\r
4ffd2858 1512 if(savestate_menu_loop(1))\r
720ee7f6 1513 continue;\r
720ee7f6 1514 engineState = PGS_Running;\r
1515 return;\r
1516 }\r
1517 break;\r
59633198 1518 case MA_MAIN_RESET_GAME:\r
720ee7f6 1519 if (rom_data) {\r
1520 emu_ResetGame();\r
1521 engineState = PGS_Running;\r
1522 return;\r
1523 }\r
1524 break;\r
59633198 1525 case MA_MAIN_LOAD_ROM:\r
13059a60 1526 {\r
1527 char curr_path[PATH_MAX], *selfname;\r
1528 FILE *tstf;\r
1529 if ( (tstf = fopen(currentConfig.lastRomFile, "rb")) )\r
1530 {\r
1531 fclose(tstf);\r
1532 strcpy(curr_path, currentConfig.lastRomFile);\r
1533 }\r
1534 else\r
1535 getcwd(curr_path, PATH_MAX);\r
720ee7f6 1536 selfname = romsel_loop(curr_path);\r
1537 if (selfname) {\r
1538 printf("selected file: %s\n", selfname);\r
720ee7f6 1539 engineState = PGS_ReloadRom;\r
13059a60 1540 return;\r
720ee7f6 1541 }\r
13059a60 1542 break;\r
1543 }\r
59633198 1544 case MA_MAIN_OPTIONS:\r
daf91588 1545 ret = menu_loop_options();\r
1546 if (ret == 1) continue; // status update\r
1547 if (engineState == PGS_ReloadRom)\r
1548 return; // BIOS test\r
720ee7f6 1549 break;\r
59633198 1550 case MA_MAIN_CONTROLS:\r
720ee7f6 1551 kc_sel_loop();\r
1552 break;\r
59633198 1553 case MA_MAIN_CREDITS:\r
720ee7f6 1554 draw_menu_credits();\r
1555 usleep(500*1000);\r
1556 inp = wait_for_input(GP2X_B|GP2X_X);\r
1557 break;\r
59633198 1558 case MA_MAIN_EXIT:\r
720ee7f6 1559 engineState = PGS_Quit;\r
1560 return;\r
59633198 1561 case MA_MAIN_PATCHES:\r
70d2ecc5 1562 if (rom_data && PicoPatches) {\r
1563 patches_menu_loop();\r
1564 PicoPatchApply();\r
1565 strcpy(menuErrorMsg, "Patches applied");\r
1566 continue;\r
1567 }\r
1568 break;\r
59633198 1569 default:\r
1570 printf("%s: something unknown selected\n", __FUNCTION__);\r
1571 break;\r
720ee7f6 1572 }\r
1573 }\r
1574 menuErrorMsg[0] = 0; // clear error msg\r
1575 }\r
1576}\r
1577\r
f9a37a02 1578static void menu_darken_bg(void *dst, int pixels, int darker)\r
720ee7f6 1579{\r
13059a60 1580 unsigned int *screen = dst;\r
1581 pixels /= 2;\r
f9a37a02 1582 if (darker)\r
13059a60 1583 {\r
f9a37a02 1584 while (pixels--)\r
1585 {\r
1586 unsigned int p = *screen;\r
1587 *screen++ = ((p&0xf79ef79e)>>1) - ((p&0xc618c618)>>3);\r
1588 }\r
1589 }\r
1590 else\r
1591 {\r
1592 while (pixels--)\r
1593 {\r
1594 unsigned int p = *screen;\r
1595 *screen++ = (p&0xf79ef79e)>>1;\r
1596 }\r
13059a60 1597 }\r
1598}\r
e5d315a5 1599\r
13059a60 1600static void menu_prepare_bg(int use_game_bg)\r
1601{\r
1602 if (use_game_bg)\r
1603 {\r
1604 // darken the active framebuffer\r
1605 memset(gp2x_screen, 0, 320*8*2);\r
f9a37a02 1606 menu_darken_bg((char *)gp2x_screen + 320*8*2, 320*224, 1);\r
13059a60 1607 memset((char *)gp2x_screen + 320*232*2, 0, 320*8*2);\r
1608 }\r
1609 else\r
1610 {\r
1611 // should really only happen once, on startup..\r
1612 readpng(gp2x_screen, "skin/background.png", READPNG_BG);\r
4ffd2858 1613 }\r
720ee7f6 1614\r
13059a60 1615 // copy to buffer2\r
1616 gp2x_memcpy_buffers((1<<2), gp2x_screen, 0, 320*240*2);\r
4ffd2858 1617}\r
1618\r
1619static void menu_gfx_prepare(void)\r
1620{\r
13059a60 1621 menu_prepare_bg(rom_data != NULL);\r
4ffd2858 1622\r
13059a60 1623 // switch to 16bpp\r
1624 gp2x_video_changemode2(16);\r
79cad122 1625 gp2x_video_RGB_setscaling(0, 320, 240);\r
13059a60 1626 menu_flip();\r
e5d315a5 1627}\r
1628\r
1629\r
1630void menu_loop(void)\r
1631{\r
1632 menu_gfx_prepare();\r
720ee7f6 1633\r
1634 menu_loop_root();\r
1635\r
1636 menuErrorMsg[0] = 0;\r
1637}\r
5e2e14f2 1638\r
1639\r
1640// --------- CD tray close menu ----------\r
1641\r
1642static void draw_menu_tray(int menu_sel)\r
1643{\r
1644 int tl_x = 70, tl_y = 90, y;\r
13059a60 1645 memset(gp2x_screen, 0, 320*240*2);\r
5e2e14f2 1646\r
13059a60 1647 text_out16(tl_x, 20, "The unit is about to");\r
1648 text_out16(tl_x, 30, "close the CD tray.");\r
5e2e14f2 1649\r
1650 y = tl_y;\r
13059a60 1651 text_out16(tl_x, y, "Load new CD image");\r
1652 text_out16(tl_x, (y+=10), "Insert nothing");\r
5e2e14f2 1653\r
1654 // draw cursor\r
13059a60 1655 text_out16(tl_x - 16, tl_y + menu_sel*10, ">");\r
5e2e14f2 1656 // error\r
13059a60 1657 if (menuErrorMsg[0]) text_out16(5, 226, menuErrorMsg);\r
1658 menu_flip();\r
5e2e14f2 1659}\r
1660\r
1661\r
1662int menu_loop_tray(void)\r
1663{\r
1664 int menu_sel = 0, menu_sel_max = 1;\r
1665 unsigned long inp = 0;\r
1666 char curr_path[PATH_MAX], *selfname;\r
1667 FILE *tstf;\r
1668\r
c7a4ff64 1669 gp2x_memset_all_buffers(0, 0, 320*240*2);\r
5e2e14f2 1670 menu_gfx_prepare();\r
1671\r
1672 if ( (tstf = fopen(currentConfig.lastRomFile, "rb")) )\r
1673 {\r
1674 fclose(tstf);\r
1675 strcpy(curr_path, currentConfig.lastRomFile);\r
1676 }\r
1677 else\r
1678 {\r
1679 getcwd(curr_path, PATH_MAX);\r
1680 }\r
1681\r
1682 /* make sure action buttons are not pressed on entering menu */\r
1683 draw_menu_tray(menu_sel);\r
1684 while (gp2x_joystick_read(1) & GP2X_B) usleep(50*1000);\r
1685\r
1686 for (;;)\r
1687 {\r
1688 draw_menu_tray(menu_sel);\r
1689 inp = wait_for_input(GP2X_UP|GP2X_DOWN|GP2X_B);\r
1690 if(inp & GP2X_UP ) { menu_sel--; if (menu_sel < 0) menu_sel = menu_sel_max; }\r
1691 if(inp & GP2X_DOWN) { menu_sel++; if (menu_sel > menu_sel_max) menu_sel = 0; }\r
1692 if(inp & GP2X_B ) {\r
1693 switch (menu_sel) {\r
1694 case 0: // select image\r
1695 selfname = romsel_loop(curr_path);\r
1696 if (selfname) {\r
1697 int ret = -1, cd_type;\r
f013066e 1698 cd_type = emu_cdCheck(NULL);\r
5e2e14f2 1699 if (cd_type > 0)\r
1700 ret = Insert_CD(romFileName, cd_type == 2);\r
1701 if (ret != 0) {\r
1702 sprintf(menuErrorMsg, "Load failed, invalid CD image?");\r
1703 printf("%s\n", menuErrorMsg);\r
1704 continue;\r
1705 }\r
1706 engineState = PGS_RestartRun;\r
1707 return 1;\r
1708 }\r
1709 break;\r
1710 case 1: // insert nothing\r
1711 engineState = PGS_RestartRun;\r
1712 return 0;\r
1713 }\r
1714 }\r
1715 menuErrorMsg[0] = 0; // clear error msg\r
1716 }\r
1717}\r
1718\r
1719\r