psp late-night fixes
[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
175 static const char *rom_exts[] = { "zip", "bin", "smd", "gen", "iso" };\r
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
95151aea 1253 if (!(PicoOpt & 0x20)) {\r
1254 // unbind XYZ MODE, just in case\r
e67f6454 1255 unbind_action(0xf00, -1, -1);\r
720ee7f6 1256 }\r
1257}\r
1258\r
daf91588 1259static int menu_loop_options(void)\r
720ee7f6 1260{\r
59d0f042 1261 static int menu_sel = 0;\r
59633198 1262 int menu_sel_max, ret;\r
720ee7f6 1263 unsigned long inp = 0;\r
59633198 1264 menu_id selected_id;\r
720ee7f6 1265\r
979ba09f 1266 currentConfig.PicoOpt = PicoOpt;\r
1267 currentConfig.PsndRate = PsndRate;\r
1268 currentConfig.PicoRegion = PicoRegionOverride;\r
720ee7f6 1269\r
59633198 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
95151aea 1273 if (menu_sel > menu_sel_max) menu_sel = menu_sel_max;\r
59633198 1274\r
1275 while (1)\r
720ee7f6 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
59633198 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
95151aea 1369 ret = emu_ReadConfig(1, 1);\r
1370 if (!ret) ret = emu_ReadConfig(0, 1);\r
1371 if (ret) strcpy(menuErrorMsg, "config loaded");\r
59633198 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
720ee7f6 1378 }\r
1379 }\r
979ba09f 1380 if(inp & (GP2X_X|GP2X_A)) {\r
720ee7f6 1381 menu_options_save();\r
daf91588 1382 return 0; // done (update, no write)\r
720ee7f6 1383 }\r
720ee7f6 1384 }\r
1385}\r
1386\r
1387// -------------- credits --------------\r
1388\r
1389static void draw_menu_credits(void)\r
1390{\r
13059a60 1391 int tl_x = 15, tl_y = 64, y;\r
e5d315a5 1392 gp2x_pd_clone_buffer2();\r
720ee7f6 1393\r
13059a60 1394 text_out16(tl_x, 20, "PicoDrive v" VERSION " (c) notaz, 2006,2007");\r
720ee7f6 1395 y = tl_y;\r
13059a60 1396 text_out16(tl_x, y, "Credits:");\r
4b8f4f3c 1397 text_out16(tl_x, (y+=10), "fDave: Cyclone 68000 core,");\r
13059a60 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
f9a37a02 1410 text_out16(tl_x, (y+=10), "ketchupgun: skin design");\r
13059a60 1411\r
1412 menu_flip();\r
720ee7f6 1413}\r
1414\r
1415\r
1416// -------------- root menu --------------\r
1417\r
59633198 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
720ee7f6 1434static void draw_menu_root(int menu_sel)\r
1435{\r
59633198 1436 const int tl_x = 70, tl_y = 70;\r
1437\r
e5d315a5 1438 gp2x_pd_clone_buffer2();\r
720ee7f6 1439\r
13059a60 1440 text_out16(tl_x, 20, "PicoDrive v" VERSION);\r
1441\r
c7a4ff64 1442 menu_draw_selection(tl_x - 16, tl_y + menu_sel*10, 146);\r
720ee7f6 1443\r
59633198 1444 me_draw(main_entries, MAIN_ENTRY_COUNT, tl_x, tl_y, NULL, NULL);\r
720ee7f6 1445\r
720ee7f6 1446 // error\r
13059a60 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
720ee7f6 1452}\r
1453\r
1454\r
1455static void menu_loop_root(void)\r
1456{\r
59633198 1457 static int menu_sel = 0;\r
1458 int ret, menu_sel_max;\r
720ee7f6 1459 unsigned long inp = 0;\r
720ee7f6 1460\r
59633198 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
95151aea 1468 if (menu_sel > menu_sel_max) menu_sel = menu_sel_max;\r
720ee7f6 1469\r
79cad122 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
720ee7f6 1475 {\r
1476 draw_menu_root(menu_sel);\r
0ae6813e 1477 inp = wait_for_input(GP2X_UP|GP2X_DOWN|GP2X_B|GP2X_X|GP2X_SELECT|GP2X_L|GP2X_R);\r
59633198 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
0ae6813e 1480 if((inp & (GP2X_L|GP2X_R)) == (GP2X_L|GP2X_R)) debug_menu_loop();\r
720ee7f6 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
59633198 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
5e2e14f2 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
720ee7f6 1497 break;\r
59633198 1498 case MA_MAIN_SAVE_STATE:\r
720ee7f6 1499 if (rom_data) {\r
4ffd2858 1500 if(savestate_menu_loop(0))\r
720ee7f6 1501 continue;\r
720ee7f6 1502 engineState = PGS_Running;\r
1503 return;\r
1504 }\r
1505 break;\r
59633198 1506 case MA_MAIN_LOAD_STATE:\r
720ee7f6 1507 if (rom_data) {\r
4ffd2858 1508 if(savestate_menu_loop(1))\r
720ee7f6 1509 continue;\r
720ee7f6 1510 engineState = PGS_Running;\r
1511 return;\r
1512 }\r
1513 break;\r
59633198 1514 case MA_MAIN_RESET_GAME:\r
720ee7f6 1515 if (rom_data) {\r
1516 emu_ResetGame();\r
1517 engineState = PGS_Running;\r
1518 return;\r
1519 }\r
1520 break;\r
59633198 1521 case MA_MAIN_LOAD_ROM:\r
13059a60 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
720ee7f6 1532 selfname = romsel_loop(curr_path);\r
1533 if (selfname) {\r
1534 printf("selected file: %s\n", selfname);\r
720ee7f6 1535 engineState = PGS_ReloadRom;\r
13059a60 1536 return;\r
720ee7f6 1537 }\r
13059a60 1538 break;\r
1539 }\r
59633198 1540 case MA_MAIN_OPTIONS:\r
daf91588 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
720ee7f6 1545 break;\r
59633198 1546 case MA_MAIN_CONTROLS:\r
720ee7f6 1547 kc_sel_loop();\r
1548 break;\r
59633198 1549 case MA_MAIN_CREDITS:\r
720ee7f6 1550 draw_menu_credits();\r
1551 usleep(500*1000);\r
1552 inp = wait_for_input(GP2X_B|GP2X_X);\r
1553 break;\r
59633198 1554 case MA_MAIN_EXIT:\r
720ee7f6 1555 engineState = PGS_Quit;\r
1556 return;\r
59633198 1557 case MA_MAIN_PATCHES:\r
70d2ecc5 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
59633198 1565 default:\r
1566 printf("%s: something unknown selected\n", __FUNCTION__);\r
1567 break;\r
720ee7f6 1568 }\r
1569 }\r
1570 menuErrorMsg[0] = 0; // clear error msg\r
1571 }\r
1572}\r
1573\r
f9a37a02 1574static void menu_darken_bg(void *dst, int pixels, int darker)\r
720ee7f6 1575{\r
13059a60 1576 unsigned int *screen = dst;\r
1577 pixels /= 2;\r
f9a37a02 1578 if (darker)\r
13059a60 1579 {\r
f9a37a02 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
13059a60 1593 }\r
1594}\r
e5d315a5 1595\r
13059a60 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
f9a37a02 1602 menu_darken_bg((char *)gp2x_screen + 320*8*2, 320*224, 1);\r
13059a60 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
4ffd2858 1609 }\r
720ee7f6 1610\r
13059a60 1611 // copy to buffer2\r
1612 gp2x_memcpy_buffers((1<<2), gp2x_screen, 0, 320*240*2);\r
4ffd2858 1613}\r
1614\r
1615static void menu_gfx_prepare(void)\r
1616{\r
13059a60 1617 menu_prepare_bg(rom_data != NULL);\r
4ffd2858 1618\r
13059a60 1619 // switch to 16bpp\r
1620 gp2x_video_changemode2(16);\r
79cad122 1621 gp2x_video_RGB_setscaling(0, 320, 240);\r
13059a60 1622 menu_flip();\r
e5d315a5 1623}\r
1624\r
1625\r
1626void menu_loop(void)\r
1627{\r
1628 menu_gfx_prepare();\r
720ee7f6 1629\r
1630 menu_loop_root();\r
1631\r
1632 menuErrorMsg[0] = 0;\r
1633}\r
5e2e14f2 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
13059a60 1641 memset(gp2x_screen, 0, 320*240*2);\r
5e2e14f2 1642\r
13059a60 1643 text_out16(tl_x, 20, "The unit is about to");\r
1644 text_out16(tl_x, 30, "close the CD tray.");\r
5e2e14f2 1645\r
1646 y = tl_y;\r
13059a60 1647 text_out16(tl_x, y, "Load new CD image");\r
1648 text_out16(tl_x, (y+=10), "Insert nothing");\r
5e2e14f2 1649\r
1650 // draw cursor\r
13059a60 1651 text_out16(tl_x - 16, tl_y + menu_sel*10, ">");\r
5e2e14f2 1652 // error\r
13059a60 1653 if (menuErrorMsg[0]) text_out16(5, 226, menuErrorMsg);\r
1654 menu_flip();\r
5e2e14f2 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
c7a4ff64 1665 gp2x_memset_all_buffers(0, 0, 320*240*2);\r
5e2e14f2 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
f013066e 1694 cd_type = emu_cdCheck(NULL);\r
5e2e14f2 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