c6a52ded8d68effe258c6fa52c1f41539e2571ff
[pcsx_rearmed.git] / frontend / menu.c
1 /*
2  * (C) GraÅžvydas "notaz" Ignotas, 2010-2011
3  *
4  * This work is licensed under the terms of any of these licenses
5  * (at your option):
6  *  - GNU GPL, version 2 or later.
7  *  - GNU LGPL, version 2.1 or later.
8  * See the COPYING file in the top-level directory.
9  */
10
11 #include <stdio.h>
12 #include <string.h>
13 #include <errno.h>
14 #include <dlfcn.h>
15 #include <zlib.h>
16 #include <sys/types.h>
17 #include <sys/stat.h>
18 #include <unistd.h>
19
20 #include "main.h"
21 #include "menu.h"
22 #include "config.h"
23 #include "plugin.h"
24 #include "plugin_lib.h"
25 #include "omap.h"
26 #include "plat.h"
27 #include "pcnt.h"
28 #include "cspace.h"
29 #include "common/plat.h"
30 #include "common/input.h"
31 #include "linux/in_evdev.h"
32 #include "../libpcsxcore/misc.h"
33 #include "../libpcsxcore/cdrom.h"
34 #include "../libpcsxcore/cdriso.h"
35 #include "../libpcsxcore/psemu_plugin_defs.h"
36 #include "../libpcsxcore/new_dynarec/new_dynarec.h"
37 #include "../plugins/dfinput/main.h"
38 #include "revision.h"
39
40 #define REARMED_BIRTHDAY_TIME 1293306830        /* 25 Dec 2010 */
41
42 #define array_size(x) (sizeof(x) / sizeof(x[0]))
43
44 typedef enum
45 {
46         MA_NONE = 1,
47         MA_MAIN_RESUME_GAME,
48         MA_MAIN_SAVE_STATE,
49         MA_MAIN_LOAD_STATE,
50         MA_MAIN_RESET_GAME,
51         MA_MAIN_LOAD_ROM,
52         MA_MAIN_SWAP_CD,
53         MA_MAIN_SWAP_CD_MULTI,
54         MA_MAIN_RUN_BIOS,
55         MA_MAIN_RUN_EXE,
56         MA_MAIN_CONTROLS,
57         MA_MAIN_CREDITS,
58         MA_MAIN_EXIT,
59         MA_CTRL_PLAYER1,
60         MA_CTRL_PLAYER2,
61         MA_CTRL_ANALOG,
62         MA_CTRL_EMU,
63         MA_CTRL_DEV_FIRST,
64         MA_CTRL_DEV_NEXT,
65         MA_CTRL_NUBS_BTNS,
66         MA_CTRL_DEADZONE,
67         MA_CTRL_VIBRATION,
68         MA_CTRL_DONE,
69         MA_OPT_SAVECFG,
70         MA_OPT_SAVECFG_GAME,
71         MA_OPT_CPU_CLOCKS,
72         MA_OPT_FILTERING,
73         MA_OPT_DISP_OPTS,
74 } menu_id;
75
76 enum {
77         SCALE_1_1,
78         SCALE_4_3,
79         SCALE_4_3v2,
80         SCALE_FULLSCREEN,
81         SCALE_CUSTOM,
82 };
83
84 static int last_psx_w, last_psx_h, last_psx_bpp;
85 static int scaling, filter, cpu_clock, cpu_clock_st, volume_boost, frameskip;
86 static char rom_fname_reload[MAXPATHLEN];
87 static char last_selected_fname[MAXPATHLEN];
88 static int warned_about_bios, region, in_type_sel1, in_type_sel2;
89 static int psx_clock;
90 static int memcard1_sel, memcard2_sel;
91 int g_opts;
92 int analog_deadzone; // for Caanoo
93
94 #ifdef __ARM_ARCH_7A__
95 #define DEFAULT_PSX_CLOCK 57
96 #define DEFAULT_PSX_CLOCK_S "57"
97 #else
98 #define DEFAULT_PSX_CLOCK 50
99 #define DEFAULT_PSX_CLOCK_S "50"
100 #endif
101
102 // sound plugin
103 extern int iUseReverb;
104 extern int iUseInterpolation;
105 extern int iXAPitch;
106 extern int iSPUIRQWait;
107 extern int iUseTimer;
108 extern int iVolume;
109
110 static const char *bioses[24];
111 static const char *gpu_plugins[16];
112 static const char *spu_plugins[16];
113 static const char *memcards[32];
114 static int bios_sel, gpu_plugsel, spu_plugsel;
115
116
117 static int min(int x, int y) { return x < y ? x : y; }
118 static int max(int x, int y) { return x > y ? x : y; }
119
120 void emu_make_path(char *buff, const char *end, int size)
121 {
122         int pos, end_len;
123
124         end_len = strlen(end);
125         pos = plat_get_root_dir(buff, size);
126         strncpy(buff + pos, end, size - pos);
127         buff[size - 1] = 0;
128         if (pos + end_len > size - 1)
129                 printf("Warning: path truncated: %s\n", buff);
130 }
131
132 static int emu_check_save_file(int slot, int *time)
133 {
134         char fname[MAXPATHLEN];
135         struct stat status;
136         int ret;
137         
138         ret = emu_check_state(slot);
139         if (ret != 0 || time == NULL)
140                 return ret == 0 ? 1 : 0;
141
142         ret = get_state_filename(fname, sizeof(fname), slot);
143         if (ret != 0)
144                 return 1;
145
146         ret = stat(fname, &status);
147         if (ret != 0)
148                 return 1;
149
150         if (status.st_mtime < REARMED_BIRTHDAY_TIME)
151                 return 1; // probably bad rtc like on some Caanoos
152
153         *time = status.st_mtime;
154
155         return 1;
156 }
157
158 static int emu_save_load_game(int load, int unused)
159 {
160         int ret;
161
162         if (load) {
163                 ret = emu_load_state(state_slot);
164
165                 // reflect hle/bios mode from savestate
166                 if (Config.HLE)
167                         bios_sel = 0;
168                 else if (bios_sel == 0 && bioses[1] != NULL)
169                         // XXX: maybe find the right bios instead
170                         bios_sel = 1;
171         }
172         else
173                 ret = emu_save_state(state_slot);
174
175         return ret;
176 }
177
178 // propagate menu settings to the emu vars
179 static void menu_sync_config(void)
180 {
181         static int allow_abs_only_old;
182
183         Config.PsxAuto = 1;
184         if (region > 0) {
185                 Config.PsxAuto = 0;
186                 Config.PsxType = region - 1;
187         }
188         cycle_multiplier = 10000 / psx_clock;
189
190         switch (in_type_sel1) {
191         case 1:  in_type1 = PSE_PAD_TYPE_ANALOGPAD; break;
192         case 2:  in_type1 = PSE_PAD_TYPE_GUNCON;    break;
193         default: in_type1 = PSE_PAD_TYPE_STANDARD;
194         }
195         switch (in_type_sel2) {
196         case 1:  in_type2 = PSE_PAD_TYPE_ANALOGPAD; break;
197         case 2:  in_type2 = PSE_PAD_TYPE_GUNCON;    break;
198         default: in_type2 = PSE_PAD_TYPE_STANDARD;
199         }
200         if (in_evdev_allow_abs_only != allow_abs_only_old) {
201                 in_probe();
202                 allow_abs_only_old = in_evdev_allow_abs_only;
203         }
204
205         iVolume = 768 + 128 * volume_boost;
206         pl_rearmed_cbs.frameskip = frameskip - 1;
207         pl_timing_prepare(Config.PsxType);
208 }
209
210 static void menu_set_defconfig(void)
211 {
212         emu_set_default_config();
213
214         g_opts = 0;
215         scaling = SCALE_4_3;
216         volume_boost = 0;
217         frameskip = 0;
218         analog_deadzone = 50;
219         psx_clock = DEFAULT_PSX_CLOCK;
220
221         region = 0;
222         in_type_sel1 = in_type_sel2 = 0;
223         in_evdev_allow_abs_only = 0;
224
225         menu_sync_config();
226 }
227
228 #define CE_CONFIG_STR(val) \
229         { #val, 0, Config.val }
230
231 #define CE_CONFIG_VAL(val) \
232         { #val, sizeof(Config.val), &Config.val }
233
234 #define CE_STR(val) \
235         { #val, 0, val }
236
237 #define CE_INTVAL(val) \
238         { #val, sizeof(val), &val }
239
240 #define CE_INTVAL_P(val) \
241         { #val, sizeof(pl_rearmed_cbs.val), &pl_rearmed_cbs.val }
242
243 // 'versioned' var, used when defaults change
244 #define CE_CONFIG_STR_V(val, ver) \
245         { #val #ver, 0, Config.val }
246
247 #define CE_INTVAL_V(val, ver) \
248         { #val #ver, sizeof(val), &val }
249
250 #define CE_INTVAL_PV(val, ver) \
251         { #val #ver, sizeof(pl_rearmed_cbs.val), &pl_rearmed_cbs.val }
252
253 static const struct {
254         const char *name;
255         size_t len;
256         void *val;
257 } config_data[] = {
258         CE_CONFIG_STR(Bios),
259         CE_CONFIG_STR_V(Gpu, 2),
260         CE_CONFIG_STR(Spu),
261 //      CE_CONFIG_STR(Cdr),
262         CE_CONFIG_VAL(Xa),
263         CE_CONFIG_VAL(Sio),
264         CE_CONFIG_VAL(Mdec),
265         CE_CONFIG_VAL(Cdda),
266         CE_CONFIG_VAL(Debug),
267         CE_CONFIG_VAL(PsxOut),
268         CE_CONFIG_VAL(SpuIrq),
269         CE_CONFIG_VAL(RCntFix),
270         CE_CONFIG_VAL(VSyncWA),
271         CE_CONFIG_VAL(Cpu),
272         CE_CONFIG_VAL(CdrReschedule),
273         CE_INTVAL(region),
274         CE_INTVAL_V(scaling, 2),
275         CE_INTVAL(g_layer_x),
276         CE_INTVAL(g_layer_y),
277         CE_INTVAL(g_layer_w),
278         CE_INTVAL(g_layer_h),
279         CE_INTVAL(filter),
280         CE_INTVAL(state_slot),
281         CE_INTVAL(cpu_clock),
282         CE_INTVAL(g_opts),
283         CE_INTVAL(in_type_sel1),
284         CE_INTVAL(in_type_sel2),
285         CE_INTVAL(analog_deadzone),
286         CE_INTVAL_V(frameskip, 3),
287         CE_INTVAL_P(gpu_peops.iUseDither),
288         CE_INTVAL_P(gpu_peops.dwActFixes),
289         CE_INTVAL_P(gpu_unai.lineskip),
290         CE_INTVAL_P(gpu_unai.abe_hack),
291         CE_INTVAL_P(gpu_unai.no_light),
292         CE_INTVAL_P(gpu_unai.no_blend),
293         CE_INTVAL_P(gpu_neon.allow_interlace),
294         CE_INTVAL_V(iUseReverb, 3),
295         CE_INTVAL_V(iXAPitch, 3),
296         CE_INTVAL_V(iUseInterpolation, 3),
297         CE_INTVAL_V(iSPUIRQWait, 3),
298         CE_INTVAL_V(iUseTimer, 3),
299         CE_INTVAL(warned_about_bios),
300         CE_INTVAL(in_evdev_allow_abs_only),
301         CE_INTVAL(volume_boost),
302         CE_INTVAL(psx_clock),
303         CE_INTVAL(new_dynarec_hacks),
304         CE_INTVAL(in_enable_vibration),
305 };
306
307 static char *get_cd_label(void)
308 {
309         static char trimlabel[33];
310         int j;
311
312         strncpy(trimlabel, CdromLabel, 32);
313         trimlabel[32] = 0;
314         for (j = 31; j >= 0; j--)
315                 if (trimlabel[j] == ' ')
316                         trimlabel[j] = 0;
317
318         return trimlabel;
319 }
320
321 static void make_cfg_fname(char *buf, size_t size, int is_game)
322 {
323         if (is_game)
324                 snprintf(buf, size, "." PCSX_DOT_DIR "cfg/%.32s-%.9s.cfg", get_cd_label(), CdromId);
325         else
326                 snprintf(buf, size, "." PCSX_DOT_DIR "%s", cfgfile_basename);
327 }
328
329 static void keys_write_all(FILE *f);
330
331 static int menu_write_config(int is_game)
332 {
333         char cfgfile[MAXPATHLEN];
334         FILE *f;
335         int i;
336
337         make_cfg_fname(cfgfile, sizeof(cfgfile), is_game);
338         f = fopen(cfgfile, "w");
339         if (f == NULL) {
340                 printf("menu_write_config: failed to open: %s\n", cfgfile);
341                 return -1;
342         }
343
344         for (i = 0; i < ARRAY_SIZE(config_data); i++) {
345                 fprintf(f, "%s = ", config_data[i].name);
346                 switch (config_data[i].len) {
347                 case 0:
348                         fprintf(f, "%s\n", (char *)config_data[i].val);
349                         break;
350                 case 1:
351                         fprintf(f, "%x\n", *(u8 *)config_data[i].val);
352                         break;
353                 case 2:
354                         fprintf(f, "%x\n", *(u16 *)config_data[i].val);
355                         break;
356                 case 4:
357                         fprintf(f, "%x\n", *(u32 *)config_data[i].val);
358                         break;
359                 default:
360                         printf("menu_write_config: unhandled len %d for %s\n",
361                                  config_data[i].len, config_data[i].name);
362                         break;
363                 }
364         }
365
366         if (!is_game)
367                 fprintf(f, "lastcdimg = %s\n", last_selected_fname);
368
369         keys_write_all(f);
370         fclose(f);
371
372         return 0;
373 }
374
375 static void parse_str_val(char *cval, const char *src)
376 {
377         char *tmp;
378         strncpy(cval, src, MAXPATHLEN);
379         cval[MAXPATHLEN - 1] = 0;
380         tmp = strchr(cval, '\n');
381         if (tmp == NULL)
382                 tmp = strchr(cval, '\r');
383         if (tmp != NULL)
384                 *tmp = 0;
385 }
386
387 static void keys_load_all(const char *cfg);
388
389 static int menu_load_config(int is_game)
390 {
391         char cfgfile[MAXPATHLEN];
392         int i, ret = -1;
393         long size;
394         char *cfg;
395         FILE *f;
396
397         make_cfg_fname(cfgfile, sizeof(cfgfile), is_game);
398         f = fopen(cfgfile, "r");
399         if (f == NULL) {
400                 printf("menu_load_config: failed to open: %s\n", cfgfile);
401                 goto fail;
402         }
403
404         fseek(f, 0, SEEK_END);
405         size = ftell(f);
406         if (size <= 0) {
407                 printf("bad size %ld: %s\n", size, cfgfile);
408                 goto fail;
409         }
410
411         cfg = malloc(size + 1);
412         if (cfg == NULL)
413                 goto fail;
414
415         fseek(f, 0, SEEK_SET);
416         if (fread(cfg, 1, size, f) != size) {
417                 printf("failed to read: %s\n", cfgfile);
418                 goto fail_read;
419         }
420         cfg[size] = 0;
421
422         for (i = 0; i < ARRAY_SIZE(config_data); i++) {
423                 char *tmp, *tmp2;
424                 u32 val;
425
426                 tmp = strstr(cfg, config_data[i].name);
427                 if (tmp == NULL)
428                         continue;
429                 tmp += strlen(config_data[i].name);
430                 if (strncmp(tmp, " = ", 3) != 0)
431                         continue;
432                 tmp += 3;
433
434                 if (config_data[i].len == 0) {
435                         parse_str_val(config_data[i].val, tmp);
436                         continue;
437                 }
438
439                 tmp2 = NULL;
440                 val = strtoul(tmp, &tmp2, 16);
441                 if (tmp2 == NULL || tmp == tmp2)
442                         continue; // parse failed
443
444                 switch (config_data[i].len) {
445                 case 1:
446                         *(u8 *)config_data[i].val = val;
447                         break;
448                 case 2:
449                         *(u16 *)config_data[i].val = val;
450                         break;
451                 case 4:
452                         *(u32 *)config_data[i].val = val;
453                         break;
454                 default:
455                         printf("menu_load_config: unhandled len %d for %s\n",
456                                  config_data[i].len, config_data[i].name);
457                         break;
458                 }
459         }
460
461         if (!is_game) {
462                 char *tmp = strstr(cfg, "lastcdimg = ");
463                 if (tmp != NULL) {
464                         tmp += 12;
465                         parse_str_val(last_selected_fname, tmp);
466                 }
467         }
468
469         keys_load_all(cfg);
470         ret = 0;
471 fail_read:
472         free(cfg);
473 fail:
474         if (f != NULL)
475                 fclose(f);
476
477         menu_sync_config();
478
479         // sync plugins
480         for (i = bios_sel = 0; bioses[i] != NULL; i++)
481                 if (strcmp(Config.Bios, bioses[i]) == 0)
482                         { bios_sel = i; break; }
483
484         for (i = gpu_plugsel = 0; gpu_plugins[i] != NULL; i++)
485                 if (strcmp(Config.Gpu, gpu_plugins[i]) == 0)
486                         { gpu_plugsel = i; break; }
487
488         for (i = spu_plugsel = 0; spu_plugins[i] != NULL; i++)
489                 if (strcmp(Config.Spu, spu_plugins[i]) == 0)
490                         { spu_plugsel = i; break; }
491
492         return ret;
493 }
494
495 // rrrr rggg gggb bbbb
496 static unsigned short fname2color(const char *fname)
497 {
498         static const char *cdimg_exts[] = { ".bin", ".img", ".mdf", ".iso", ".cue", ".z",
499                                             ".bz", ".znx", ".pbp", ".cbn" };
500         static const char *other_exts[] = { ".ccd", ".toc", ".mds", ".sub",
501                                             ".table", ".index", ".sbi" };
502         const char *ext = strrchr(fname, '.');
503         int i;
504
505         if (ext == NULL)
506                 return 0xffff;
507         for (i = 0; i < array_size(cdimg_exts); i++)
508                 if (strcasecmp(ext, cdimg_exts[i]) == 0)
509                         return 0x7bff;
510         for (i = 0; i < array_size(other_exts); i++)
511                 if (strcasecmp(ext, other_exts[i]) == 0)
512                         return 0xa514;
513         return 0xffff;
514 }
515
516 static void draw_savestate_bg(int slot);
517
518 static const char *filter_exts[] = {
519         ".mp3", ".MP3", ".txt", ".htm", "html", ".jpg", ".pnd"
520 };
521
522 #define MENU_ALIGN_LEFT
523 #ifdef __ARM_ARCH_7A__ // assume hires device
524 #define MENU_X2 1
525 #else
526 #define MENU_X2 0
527 #endif
528
529 #define menu_init menu_init_common
530 #include "common/menu.c"
531 #undef menu_init
532
533 // a bit of black magic here
534 static void draw_savestate_bg(int slot)
535 {
536         static const int psx_widths[8]  = { 256, 368, 320, 384, 512, 512, 640, 640 };
537         int x, y, w, h;
538         char fname[MAXPATHLEN];
539         GPUFreeze_t *gpu;
540         u16 *s, *d;
541         gzFile f;
542         int ret;
543         u32 tmp;
544
545         ret = get_state_filename(fname, sizeof(fname), slot);
546         if (ret != 0)
547                 return;
548
549         f = gzopen(fname, "rb");
550         if (f == NULL)
551                 return;
552
553         if (gzseek(f, 0x29933d, SEEK_SET) != 0x29933d) {
554                 fprintf(stderr, "gzseek failed\n");
555                 gzclose(f);
556                 return;
557         }
558
559         gpu = malloc(sizeof(*gpu));
560         if (gpu == NULL) {
561                 gzclose(f);
562                 return;
563         }
564
565         ret = gzread(f, gpu, sizeof(*gpu));
566         gzclose(f);
567         if (ret != sizeof(*gpu)) {
568                 fprintf(stderr, "gzread failed\n");
569                 goto out;
570         }
571
572         memcpy(g_menubg_ptr, g_menubg_src_ptr, g_menuscreen_w * g_menuscreen_h * 2);
573
574         if (gpu->ulStatus & 0x800000)
575                 goto out; // disabled
576
577         x = gpu->ulControl[5] & 0x3ff;
578         y = (gpu->ulControl[5] >> 10) & 0x1ff;
579         s = (u16 *)gpu->psxVRam + y * 1024 + (x & ~1);
580         w = psx_widths[(gpu->ulStatus >> 16) & 7];
581         tmp = gpu->ulControl[7];
582         h = ((tmp >> 10) & 0x3ff) - (tmp & 0x3ff);
583         if (gpu->ulStatus & 0x80000) // doubleheight
584                 h *= 2;
585
586         x = max(0, g_menuscreen_w - w) & ~3;
587         y = max(0, g_menuscreen_h / 2 - h / 2);
588         w = min(g_menuscreen_w, w);
589         h = min(g_menuscreen_h, h);
590         d = (u16 *)g_menubg_ptr + g_menuscreen_w * y + x;
591
592         for (; h > 0; h--, d += g_menuscreen_w, s += 1024) {
593                 if (gpu->ulStatus & 0x200000)
594                         bgr888_to_rgb565(d, s, w * 3);
595                 else
596                         bgr555_to_rgb565(d, s, w * 2);
597 #ifndef __ARM_ARCH_7A__
598                 // better darken this on small screens
599                 menu_darken_bg(d, d, w * 2, 0);
600 #endif
601         }
602
603 out:
604         free(gpu);
605 }
606
607 // ---------- XXX: pandora specific -----------
608
609 static const char pnd_script_base[] = "sudo -n /usr/pandora/scripts";
610 static char **pnd_filter_list;
611
612 static void apply_filter(int which)
613 {
614         static int old = -1;
615         char buf[128];
616         int i;
617
618         if (pnd_filter_list == NULL || which == old)
619                 return;
620
621         for (i = 0; i < which; i++)
622                 if (pnd_filter_list[i] == NULL)
623                         return;
624
625         if (pnd_filter_list[i] == NULL)
626                 return;
627
628         snprintf(buf, sizeof(buf), "%s/op_videofir.sh %s", pnd_script_base, pnd_filter_list[i]);
629         system(buf);
630         old = which;
631 }
632
633 static void apply_lcdrate(int pal)
634 {
635         static int old = -1;
636         char buf[128];
637
638         if (pal == old)
639                 return;
640
641         snprintf(buf, sizeof(buf), "%s/op_lcdrate.sh %d",
642                         pnd_script_base, pal ? 50 : 60);
643         system(buf);
644         old = pal;
645 }
646
647 static menu_entry e_menu_gfx_options[];
648
649 static void pnd_menu_init(void)
650 {
651         struct dirent *ent;
652         int i, count = 0;
653         char **mfilters;
654         char buff[64];
655         DIR *dir;
656
657         cpu_clock_st = cpu_clock = plat_cpu_clock_get();
658
659         dir = opendir("/etc/pandora/conf/dss_fir");
660         if (dir == NULL) {
661                 perror("filter opendir");
662                 return;
663         }
664
665         while (1) {
666                 errno = 0;
667                 ent = readdir(dir);
668                 if (ent == NULL) {
669                         if (errno != 0)
670                                 perror("readdir");
671                         break;
672                 }
673
674                 if (ent->d_type != DT_REG && ent->d_type != DT_LNK)
675                         continue;
676
677                 count++;
678         }
679
680         if (count == 0)
681                 return;
682
683         mfilters = calloc(count + 1, sizeof(mfilters[0]));
684         if (mfilters == NULL)
685                 return;
686
687         rewinddir(dir);
688         for (i = 0; (ent = readdir(dir)); ) {
689                 size_t len;
690
691                 if (ent->d_type != DT_REG && ent->d_type != DT_LNK)
692                         continue;
693
694                 len = strlen(ent->d_name);
695
696                 // skip pre-HF5 extra files
697                 if (len >= 3 && strcmp(ent->d_name + len - 3, "_v3") == 0)
698                         continue;
699                 if (len >= 3 && strcmp(ent->d_name + len - 3, "_v5") == 0)
700                         continue;
701
702                 // have to cut "_up_h" for pre-HF5
703                 if (len > 5 && strcmp(ent->d_name + len - 5, "_up_h") == 0)
704                         len -= 5;
705
706                 if (len > sizeof(buff) - 1)
707                         continue;
708
709                 strncpy(buff, ent->d_name, len);
710                 buff[len] = 0;
711                 mfilters[i] = strdup(buff);
712                 if (mfilters[i] != NULL)
713                         i++;
714         }
715         closedir(dir);
716
717         i = me_id2offset(e_menu_gfx_options, MA_OPT_FILTERING);
718         e_menu_gfx_options[i].data = (void *)mfilters;
719         pnd_filter_list = mfilters;
720 }
721
722 void menu_finish(void)
723 {
724         plat_cpu_clock_apply(cpu_clock_st);
725 }
726
727 // -------------- key config --------------
728
729 me_bind_action me_ctrl_actions[] =
730 {
731         { "UP      ", 1 << DKEY_UP},
732         { "DOWN    ", 1 << DKEY_DOWN },
733         { "LEFT    ", 1 << DKEY_LEFT },
734         { "RIGHT   ", 1 << DKEY_RIGHT },
735         { "TRIANGLE", 1 << DKEY_TRIANGLE },
736         { "CIRCLE  ", 1 << DKEY_CIRCLE },
737         { "CROSS   ", 1 << DKEY_CROSS },
738         { "SQUARE  ", 1 << DKEY_SQUARE },
739         { "L1      ", 1 << DKEY_L1 },
740         { "R1      ", 1 << DKEY_R1 },
741         { "L2      ", 1 << DKEY_L2 },
742         { "R2      ", 1 << DKEY_R2 },
743         { "L3      ", 1 << DKEY_L3 },
744         { "R3      ", 1 << DKEY_R3 },
745         { "START   ", 1 << DKEY_START },
746         { "SELECT  ", 1 << DKEY_SELECT },
747         { NULL,       0 }
748 };
749
750 me_bind_action emuctrl_actions[] =
751 {
752         { "Save State       ", 1 << SACTION_SAVE_STATE },
753         { "Load State       ", 1 << SACTION_LOAD_STATE },
754         { "Prev Save Slot   ", 1 << SACTION_PREV_SSLOT },
755         { "Next Save Slot   ", 1 << SACTION_NEXT_SSLOT },
756         { "Toggle Frameskip ", 1 << SACTION_TOGGLE_FSKIP },
757         { "Take Screenshot  ", 1 << SACTION_SCREENSHOT },
758         { "Enter Menu       ", 1 << SACTION_ENTER_MENU },
759         { "Gun Trigger      ", 1 << SACTION_GUN_TRIGGER },
760         { "Gun A button     ", 1 << SACTION_GUN_A },
761         { "Gun B button     ", 1 << SACTION_GUN_B },
762         { "Gun Offscreen Trigger", 1 << SACTION_GUN_TRIGGER2 },
763 #ifndef __ARM_ARCH_7A__ /* XXX */
764         { "Volume Up        ", 1 << SACTION_VOLUME_UP },
765         { "Volume Down      ", 1 << SACTION_VOLUME_DOWN },
766 #endif
767         { NULL,                0 }
768 };
769
770 static char *mystrip(char *str)
771 {
772         int i, len;
773
774         len = strlen(str);
775         for (i = 0; i < len; i++)
776                 if (str[i] != ' ') break;
777         if (i > 0) memmove(str, str + i, len - i + 1);
778
779         len = strlen(str);
780         for (i = len - 1; i >= 0; i--)
781                 if (str[i] != ' ') break;
782         str[i+1] = 0;
783
784         return str;
785 }
786
787 static void get_line(char *d, size_t size, const char *s)
788 {
789         const char *pe;
790         size_t len;
791
792         for (pe = s; *pe != '\r' && *pe != '\n' && *pe != 0; pe++)
793                 ;
794         len = pe - s;
795         if (len > size - 1)
796                 len = size - 1;
797         strncpy(d, s, len);
798         d[len] = 0;
799
800         mystrip(d);
801 }
802
803 static void keys_write_all(FILE *f)
804 {
805         int d;
806
807         for (d = 0; d < IN_MAX_DEVS; d++)
808         {
809                 const int *binds = in_get_dev_binds(d);
810                 const char *name = in_get_dev_name(d, 0, 0);
811                 int k, count = 0;
812
813                 if (binds == NULL || name == NULL)
814                         continue;
815
816                 fprintf(f, "binddev = %s\n", name);
817                 in_get_config(d, IN_CFG_BIND_COUNT, &count);
818
819                 for (k = 0; k < count; k++)
820                 {
821                         int i, kbinds, mask;
822                         char act[32];
823
824                         act[0] = act[31] = 0;
825                         name = in_get_key_name(d, k);
826
827                         kbinds = binds[IN_BIND_OFFS(k, IN_BINDTYPE_PLAYER12)];
828                         for (i = 0; kbinds && i < ARRAY_SIZE(me_ctrl_actions) - 1; i++) {
829                                 mask = me_ctrl_actions[i].mask;
830                                 if (mask & kbinds) {
831                                         strncpy(act, me_ctrl_actions[i].name, 31);
832                                         fprintf(f, "bind %s = player1 %s\n", name, mystrip(act));
833                                         kbinds &= ~mask;
834                                 }
835                                 mask = me_ctrl_actions[i].mask << 16;
836                                 if (mask & kbinds) {
837                                         strncpy(act, me_ctrl_actions[i].name, 31);
838                                         fprintf(f, "bind %s = player2 %s\n", name, mystrip(act));
839                                         kbinds &= ~mask;
840                                 }
841                         }
842
843                         kbinds = binds[IN_BIND_OFFS(k, IN_BINDTYPE_EMU)];
844                         for (i = 0; kbinds && emuctrl_actions[i].name != NULL; i++) {
845                                 mask = emuctrl_actions[i].mask;
846                                 if (mask & kbinds) {
847                                         strncpy(act, emuctrl_actions[i].name, 31);
848                                         fprintf(f, "bind %s = %s\n", name, mystrip(act));
849                                         kbinds &= ~mask;
850                                 }
851                         }
852                 }
853
854                 for (k = 0; k < array_size(in_adev); k++)
855                 {
856                         if (in_adev[k] == d)
857                                 fprintf(f, "bind_analog = %d\n", k);
858                 }
859         }
860 }
861
862 static int parse_bind_val(const char *val, int *type)
863 {
864         int i;
865
866         *type = IN_BINDTYPE_NONE;
867         if (val[0] == 0)
868                 return 0;
869         
870         if (strncasecmp(val, "player", 6) == 0)
871         {
872                 int player, shift = 0;
873                 player = atoi(val + 6) - 1;
874
875                 if ((unsigned int)player > 1)
876                         return -1;
877                 if (player == 1)
878                         shift = 16;
879
880                 *type = IN_BINDTYPE_PLAYER12;
881                 for (i = 0; me_ctrl_actions[i].name != NULL; i++) {
882                         if (strncasecmp(me_ctrl_actions[i].name, val + 8, strlen(val + 8)) == 0)
883                                 return me_ctrl_actions[i].mask << shift;
884                 }
885         }
886         for (i = 0; emuctrl_actions[i].name != NULL; i++) {
887                 if (strncasecmp(emuctrl_actions[i].name, val, strlen(val)) == 0) {
888                         *type = IN_BINDTYPE_EMU;
889                         return emuctrl_actions[i].mask;
890                 }
891         }
892
893         return -1;
894 }
895
896 static void keys_load_all(const char *cfg)
897 {
898         char dev[256], key[128], *act;
899         const char *p;
900         int bind, bindtype;
901         int ret, dev_id;
902
903         p = cfg;
904         while (p != NULL && (p = strstr(p, "binddev = ")) != NULL) {
905                 p += 10;
906
907                 get_line(dev, sizeof(dev), p);
908                 dev_id = in_config_parse_dev(dev);
909                 if (dev_id < 0) {
910                         printf("input: can't handle dev: %s\n", dev);
911                         continue;
912                 }
913
914                 in_unbind_all(dev_id, -1, -1);
915                 while ((p = strstr(p, "bind"))) {
916                         if (strncmp(p, "binddev = ", 10) == 0)
917                                 break;
918
919                         if (strncmp(p, "bind_analog", 11) == 0) {
920                                 ret = sscanf(p, "bind_analog = %d", &bind);
921                                 p += 11;
922                                 if (ret != 1) {
923                                         printf("input: parse error: %16s..\n", p);
924                                         continue;
925                                 }
926                                 if ((unsigned int)bind >= array_size(in_adev)) {
927                                         printf("input: analog id %d out of range\n", bind);
928                                         continue;
929                                 }
930                                 in_adev[bind] = dev_id;
931                                 continue;
932                         }
933
934                         p += 4;
935                         if (*p != ' ') {
936                                 printf("input: parse error: %16s..\n", p);
937                                 continue;
938                         }
939
940                         get_line(key, sizeof(key), p);
941                         act = strchr(key, '=');
942                         if (act == NULL) {
943                                 printf("parse failed: %16s..\n", p);
944                                 continue;
945                         }
946                         *act = 0;
947                         act++;
948                         mystrip(key);
949                         mystrip(act);
950
951                         bind = parse_bind_val(act, &bindtype);
952                         if (bind != -1 && bind != 0) {
953                                 //printf("bind #%d '%s' %08x (%s)\n", dev_id, key, bind, act);
954                                 in_config_bind_key(dev_id, key, bind, bindtype);
955                         }
956                         else
957                                 lprintf("config: unhandled action \"%s\"\n", act);
958                 }
959         }
960         in_clean_binds();
961 }
962
963 static int key_config_loop_wrap(int id, int keys)
964 {
965         switch (id) {
966                 case MA_CTRL_PLAYER1:
967                         key_config_loop(me_ctrl_actions, array_size(me_ctrl_actions) - 1, 0);
968                         break;
969                 case MA_CTRL_PLAYER2:
970                         key_config_loop(me_ctrl_actions, array_size(me_ctrl_actions) - 1, 1);
971                         break;
972                 case MA_CTRL_EMU:
973                         key_config_loop(emuctrl_actions, array_size(emuctrl_actions) - 1, -1);
974                         break;
975                 default:
976                         break;
977         }
978         return 0;
979 }
980
981 static const char *adevnames[IN_MAX_DEVS + 2];
982 static int stick_sel[2];
983
984 static menu_entry e_menu_keyconfig_analog[] =
985 {
986         mee_enum ("Left stick (L3)",  0, stick_sel[0], adevnames),
987         mee_range("  X axis",    0, in_adev_axis[0][0], 0, 7),
988         mee_range("  Y axis",    0, in_adev_axis[0][1], 0, 7),
989         mee_enum ("Right stick (R3)", 0, stick_sel[1], adevnames),
990         mee_range("  X axis",    0, in_adev_axis[1][0], 0, 7),
991         mee_range("  Y axis",    0, in_adev_axis[1][1], 0, 7),
992         mee_end,
993 };
994
995 static int key_config_analog(int id, int keys)
996 {
997         int i, d, count, sel = 0;
998         int sel2dev_map[IN_MAX_DEVS];
999
1000         memset(adevnames, 0, sizeof(adevnames));
1001         memset(sel2dev_map, 0xff, sizeof(sel2dev_map));
1002         memset(stick_sel, 0, sizeof(stick_sel));
1003
1004         adevnames[0] = "None";
1005         i = 1;
1006         for (d = 0; d < IN_MAX_DEVS; d++)
1007         {
1008                 const char *name = in_get_dev_name(d, 0, 1);
1009                 if (name == NULL)
1010                         continue;
1011
1012                 count = 0;
1013                 in_get_config(d, IN_CFG_ABS_AXIS_COUNT, &count);
1014                 if (count == 0)
1015                         continue;
1016
1017                 if (in_adev[0] == d) stick_sel[0] = i;
1018                 if (in_adev[1] == d) stick_sel[1] = i;
1019                 sel2dev_map[i] = d;
1020                 adevnames[i++] = name;
1021         }
1022         adevnames[i] = NULL;
1023
1024         me_loop(e_menu_keyconfig_analog, &sel);
1025
1026         in_adev[0] = sel2dev_map[stick_sel[0]];
1027         in_adev[1] = sel2dev_map[stick_sel[1]];
1028
1029         return 0;
1030 }
1031
1032 static const char *mgn_dev_name(int id, int *offs)
1033 {
1034         const char *name = NULL;
1035         static int it = 0;
1036
1037         if (id == MA_CTRL_DEV_FIRST)
1038                 it = 0;
1039
1040         for (; it < IN_MAX_DEVS; it++) {
1041                 name = in_get_dev_name(it, 1, 1);
1042                 if (name != NULL)
1043                         break;
1044         }
1045
1046         it++;
1047         return name;
1048 }
1049
1050 static const char *mgn_saveloadcfg(int id, int *offs)
1051 {
1052         return "";
1053 }
1054
1055 static int mh_savecfg(int id, int keys)
1056 {
1057         if (menu_write_config(id == MA_OPT_SAVECFG_GAME ? 1 : 0) == 0)
1058                 me_update_msg("config saved");
1059         else
1060                 me_update_msg("failed to write config");
1061
1062         return 1;
1063 }
1064
1065 static int mh_input_rescan(int id, int keys)
1066 {
1067         //menu_sync_config();
1068         in_probe();
1069         me_update_msg("rescan complete.");
1070
1071         return 0;
1072 }
1073
1074 static const char *men_in_type_sel[] = {
1075         "Standard (SCPH-1080)",
1076         "Analog (SCPH-1150)",
1077         "GunCon",
1078         NULL
1079 };
1080 static const char h_nub_btns[] = "Experimental, keep this OFF if unsure. Select rescan after change.";
1081 static const char h_notsgun[]  = "Don't trigger (shoot) when touching screen in gun games.";
1082 static const char h_vibration[]= "Must select analog above and enable this ingame too.";
1083
1084 static menu_entry e_menu_keyconfig[] =
1085 {
1086         mee_handler_id("Player 1",              MA_CTRL_PLAYER1,    key_config_loop_wrap),
1087         mee_handler_id("Player 2",              MA_CTRL_PLAYER2,    key_config_loop_wrap),
1088         mee_handler_id("Analog controls",       MA_CTRL_ANALOG,     key_config_analog),
1089         mee_handler_id("Emulator/Gun controls", MA_CTRL_EMU,        key_config_loop_wrap),
1090         mee_label     (""),
1091         mee_enum      ("Port 1 device",     0, in_type_sel1,    men_in_type_sel),
1092         mee_enum      ("Port 2 device",     0, in_type_sel2,    men_in_type_sel),
1093         mee_onoff_h   ("Nubs as buttons",   MA_CTRL_NUBS_BTNS,  in_evdev_allow_abs_only, 1, h_nub_btns),
1094         mee_onoff_h   ("Vibration",         MA_CTRL_VIBRATION,  in_enable_vibration, 1, h_vibration),
1095         mee_range     ("Analog deadzone",   MA_CTRL_DEADZONE,   analog_deadzone, 1, 99),
1096         mee_onoff_h   ("No TS Gun trigger", 0, g_opts, OPT_TSGUN_NOTRIGGER, h_notsgun),
1097         mee_cust_nosave("Save global config",       MA_OPT_SAVECFG,      mh_savecfg, mgn_saveloadcfg),
1098         mee_cust_nosave("Save cfg for loaded game", MA_OPT_SAVECFG_GAME, mh_savecfg, mgn_saveloadcfg),
1099         mee_handler   ("Rescan devices:",  mh_input_rescan),
1100         mee_label     (""),
1101         mee_label_mk  (MA_CTRL_DEV_FIRST, mgn_dev_name),
1102         mee_label_mk  (MA_CTRL_DEV_NEXT,  mgn_dev_name),
1103         mee_label_mk  (MA_CTRL_DEV_NEXT,  mgn_dev_name),
1104         mee_label_mk  (MA_CTRL_DEV_NEXT,  mgn_dev_name),
1105         mee_label_mk  (MA_CTRL_DEV_NEXT,  mgn_dev_name),
1106         mee_label_mk  (MA_CTRL_DEV_NEXT,  mgn_dev_name),
1107         mee_label_mk  (MA_CTRL_DEV_NEXT,  mgn_dev_name),
1108         mee_end,
1109 };
1110
1111 static int menu_loop_keyconfig(int id, int keys)
1112 {
1113         static int sel = 0;
1114
1115 //      me_enable(e_menu_keyconfig, MA_OPT_SAVECFG_GAME, ready_to_go && CdromId[0]);
1116         me_loop(e_menu_keyconfig, &sel);
1117         return 0;
1118 }
1119
1120 // ------------ gfx options menu ------------
1121
1122 static const char *men_scaler[] = { "1x1", "scaled 4:3", "integer scaled 4:3", "fullscreen", "custom", NULL };
1123 static const char h_cscaler[]   = "Displays the scaler layer, you can resize it\n"
1124                                   "using d-pad or move it using R+d-pad";
1125 static const char *men_dummy[] = { NULL };
1126
1127 static int menu_loop_cscaler(int id, int keys)
1128 {
1129         unsigned int inp;
1130
1131         scaling = SCALE_CUSTOM;
1132
1133         omap_enable_layer(1);
1134
1135         for (;;)
1136         {
1137                 menu_draw_begin(0);
1138                 memset(g_menuscreen_ptr, 4, g_menuscreen_w * g_menuscreen_h * 2);
1139                 text_out16(2, 2, "%d,%d", g_layer_x, g_layer_y);
1140                 text_out16(2, 480 - 18, "%dx%d | d-pad: resize, R+d-pad: move", g_layer_w, g_layer_h);
1141                 menu_draw_end();
1142
1143                 inp = in_menu_wait(PBTN_UP|PBTN_DOWN|PBTN_LEFT|PBTN_RIGHT|PBTN_R|PBTN_MOK|PBTN_MBACK, 40);
1144                 if (inp & PBTN_UP)    g_layer_y--;
1145                 if (inp & PBTN_DOWN)  g_layer_y++;
1146                 if (inp & PBTN_LEFT)  g_layer_x--;
1147                 if (inp & PBTN_RIGHT) g_layer_x++;
1148                 if (!(inp & PBTN_R)) {
1149                         if (inp & PBTN_UP)    g_layer_h += 2;
1150                         if (inp & PBTN_DOWN)  g_layer_h -= 2;
1151                         if (inp & PBTN_LEFT)  g_layer_w += 2;
1152                         if (inp & PBTN_RIGHT) g_layer_w -= 2;
1153                 }
1154                 if (inp & (PBTN_MOK|PBTN_MBACK))
1155                         break;
1156
1157                 if (inp & (PBTN_UP|PBTN_DOWN|PBTN_LEFT|PBTN_RIGHT)) {
1158                         if (g_layer_x < 0)   g_layer_x = 0;
1159                         if (g_layer_x > 640) g_layer_x = 640;
1160                         if (g_layer_y < 0)   g_layer_y = 0;
1161                         if (g_layer_y > 420) g_layer_y = 420;
1162                         if (g_layer_w < 160) g_layer_w = 160;
1163                         if (g_layer_h < 60)  g_layer_h = 60;
1164                         if (g_layer_x + g_layer_w > 800)
1165                                 g_layer_w = 800 - g_layer_x;
1166                         if (g_layer_y + g_layer_h > 480)
1167                                 g_layer_h = 480 - g_layer_y;
1168                         omap_enable_layer(1);
1169                 }
1170         }
1171
1172         omap_enable_layer(0);
1173
1174         return 0;
1175 }
1176
1177 static menu_entry e_menu_gfx_options[] =
1178 {
1179         mee_enum      ("Scaler",                   0, scaling, men_scaler),
1180         mee_enum      ("Filter",                   MA_OPT_FILTERING, filter, men_dummy),
1181 //      mee_onoff     ("Vsync",                    0, vsync, 1),
1182         mee_cust_h    ("Setup custom scaler",      0, menu_loop_cscaler, NULL, h_cscaler),
1183         mee_end,
1184 };
1185
1186 static int menu_loop_gfx_options(int id, int keys)
1187 {
1188         static int sel = 0;
1189
1190         me_loop(e_menu_gfx_options, &sel);
1191
1192         return 0;
1193 }
1194
1195 // ------------ bios/plugins ------------
1196
1197 #ifdef __ARM_NEON__
1198
1199 static const char h_gpu_neon[] = "Configure built-in NEON GPU plugin";
1200 static const char *men_gpu_interlace[] = { "Off", "On", "Auto", NULL };
1201
1202 static menu_entry e_menu_plugin_gpu_neon[] =
1203 {
1204         mee_enum      ("Enable interlace mode",      0, pl_rearmed_cbs.gpu_neon.allow_interlace, men_gpu_interlace),
1205         mee_end,
1206 };
1207
1208 static int menu_loop_plugin_gpu_neon(int id, int keys)
1209 {
1210         int sel = 0;
1211         me_loop(e_menu_plugin_gpu_neon, &sel);
1212         return 0;
1213 }
1214
1215 #endif
1216
1217 static menu_entry e_menu_plugin_gpu_unai[] =
1218 {
1219         mee_onoff     ("Skip every 2nd line",        0, pl_rearmed_cbs.gpu_unai.lineskip, 1),
1220         mee_onoff     ("Abe's Odyssey hack",         0, pl_rearmed_cbs.gpu_unai.abe_hack, 1),
1221         mee_onoff     ("Disable lighting",           0, pl_rearmed_cbs.gpu_unai.no_light, 1),
1222         mee_onoff     ("Disable blending",           0, pl_rearmed_cbs.gpu_unai.no_blend, 1),
1223         mee_end,
1224 };
1225
1226 static int menu_loop_plugin_gpu_unai(int id, int keys)
1227 {
1228         int sel = 0;
1229         me_loop(e_menu_plugin_gpu_unai, &sel);
1230         return 0;
1231 }
1232
1233 static const char *men_gpu_dithering[] = { "None", "Game dependant", "Always", NULL };
1234 //static const char h_gpu_0[]            = "Needed for Chrono Cross";
1235 static const char h_gpu_1[]            = "Capcom fighting games";
1236 static const char h_gpu_2[]            = "Black screens in Lunar";
1237 static const char h_gpu_3[]            = "Compatibility mode";
1238 static const char h_gpu_6[]            = "Pandemonium 2";
1239 //static const char h_gpu_7[]            = "Skip every second frame";
1240 static const char h_gpu_8[]            = "Needed by Dark Forces";
1241 static const char h_gpu_9[]            = "better g-colors, worse textures";
1242 static const char h_gpu_10[]           = "Toggle busy flags after drawing";
1243
1244 static menu_entry e_menu_plugin_gpu_peops[] =
1245 {
1246         mee_enum      ("Dithering",                  0, pl_rearmed_cbs.gpu_peops.iUseDither, men_gpu_dithering),
1247 //      mee_onoff_h   ("Odd/even bit hack",          0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<0, h_gpu_0),
1248         mee_onoff_h   ("Expand screen width",        0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<1, h_gpu_1),
1249         mee_onoff_h   ("Ignore brightness color",    0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<2, h_gpu_2),
1250         mee_onoff_h   ("Disable coordinate check",   0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<3, h_gpu_3),
1251         mee_onoff_h   ("Lazy screen update",         0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<6, h_gpu_6),
1252 //      mee_onoff_h   ("Old frame skipping",         0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<7, h_gpu_7),
1253         mee_onoff_h   ("Repeated flat tex triangles ",0,pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<8, h_gpu_8),
1254         mee_onoff_h   ("Draw quads with triangles",  0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<9, h_gpu_9),
1255         mee_onoff_h   ("Fake 'gpu busy' states",     0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<10, h_gpu_10),
1256         mee_end,
1257 };
1258
1259 static int menu_loop_plugin_gpu_peops(int id, int keys)
1260 {
1261         static int sel = 0;
1262         me_loop(e_menu_plugin_gpu_peops, &sel);
1263         return 0;
1264 }
1265
1266 static const char *men_spu_interp[] = { "None", "Simple", "Gaussian", "Cubic", NULL };
1267 static const char h_spu_volboost[]  = "Large values cause distortion";
1268 static const char h_spu_irq_wait[]  = "Wait for CPU (recommended set to ON)";
1269 static const char h_spu_thread[]    = "Run sound emulation in main thread (recommended)";
1270
1271 static menu_entry e_menu_plugin_spu[] =
1272 {
1273         mee_range_h   ("Volume boost",              0, volume_boost, -5, 30, h_spu_volboost),
1274         mee_onoff     ("Reverb",                    0, iUseReverb, 2),
1275         mee_enum      ("Interpolation",             0, iUseInterpolation, men_spu_interp),
1276         mee_onoff     ("Adjust XA pitch",           0, iXAPitch, 1),
1277         mee_onoff_h   ("SPU IRQ Wait",              0, iSPUIRQWait, 1, h_spu_irq_wait),
1278         mee_onoff_h   ("Sound in main thread",      0, iUseTimer, 2, h_spu_thread),
1279         mee_end,
1280 };
1281
1282 static int menu_loop_plugin_spu(int id, int keys)
1283 {
1284         static int sel = 0;
1285         me_loop(e_menu_plugin_spu, &sel);
1286         return 0;
1287 }
1288
1289 static const char h_bios[]       = "HLE is simulated BIOS. BIOS selection is saved in\n"
1290                                    "savestates and can't be changed there. Must save\n"
1291                                    "config and reload the game for change to take effect";
1292 static const char h_plugin_xpu[] = "Must save config and reload the game\n"
1293                                    "for plugin change to take effect";
1294 static const char h_gpu_peops[]  = "Configure P.E.Op.S. SoftGL Driver V1.17";
1295 static const char h_gpu_unai[]   = "Configure Unai/PCSX4ALL Team GPU plugin";
1296 static const char h_spu[]        = "Configure built-in P.E.Op.S. Sound Driver V1.7";
1297
1298 static menu_entry e_menu_plugin_options[] =
1299 {
1300         mee_enum_h    ("BIOS",                          0, bios_sel, bioses, h_bios),
1301         mee_enum_h    ("GPU plugin",                    0, gpu_plugsel, gpu_plugins, h_plugin_xpu),
1302         mee_enum_h    ("SPU plugin",                    0, spu_plugsel, spu_plugins, h_plugin_xpu),
1303 #ifdef __ARM_NEON__
1304         mee_handler_h ("Configure built-in GPU plugin", menu_loop_plugin_gpu_neon, h_gpu_neon),
1305 #endif
1306         mee_handler_h ("Configure gpu_peops plugin",    menu_loop_plugin_gpu_peops, h_gpu_peops),
1307         mee_handler_h ("Configure PCSX4ALL GPU plugin", menu_loop_plugin_gpu_unai, h_gpu_unai),
1308         mee_handler_h ("Configure built-in SPU plugin", menu_loop_plugin_spu, h_spu),
1309         mee_end,
1310 };
1311
1312 static menu_entry e_menu_main2[];
1313
1314 static int menu_loop_plugin_options(int id, int keys)
1315 {
1316         static int sel = 0;
1317         me_loop(e_menu_plugin_options, &sel);
1318
1319         // sync BIOS/plugins
1320         snprintf(Config.Bios, sizeof(Config.Bios), "%s", bioses[bios_sel]);
1321         snprintf(Config.Gpu, sizeof(Config.Gpu), "%s", gpu_plugins[gpu_plugsel]);
1322         snprintf(Config.Spu, sizeof(Config.Spu), "%s", spu_plugins[spu_plugsel]);
1323         me_enable(e_menu_main2, MA_MAIN_RUN_BIOS, bios_sel != 0);
1324
1325         return 0;
1326 }
1327
1328 // ------------ adv options menu ------------
1329
1330 static const char h_cfg_psxclk[]  = "Over/under-clock the PSX, default is " DEFAULT_PSX_CLOCK_S "\n";
1331 static const char h_cfg_nosmc[]   = "Will cause crashes when loading, break memcards";
1332 static const char h_cfg_gteunn[]  = "May cause graphical glitches";
1333 static const char h_cfg_gteflgs[] = "Will cause graphical glitches";
1334
1335 static menu_entry e_menu_speed_hacks[] =
1336 {
1337         mee_range_h   ("PSX CPU clock, %%",        0, psx_clock, 1, 500, h_cfg_psxclk),
1338         mee_onoff_h   ("Disable SMC checks",       0, new_dynarec_hacks, NDHACK_NO_SMC_CHECK, h_cfg_nosmc),
1339         mee_onoff_h   ("Assume GTE regs unneeded", 0, new_dynarec_hacks, NDHACK_GTE_UNNEEDED, h_cfg_gteunn),
1340         mee_onoff_h   ("Disable GTE flags",        0, new_dynarec_hacks, NDHACK_GTE_NO_FLAGS, h_cfg_gteflgs),
1341         mee_end,
1342 };
1343
1344 static int menu_loop_speed_hacks(int id, int keys)
1345 {
1346         static int sel = 0;
1347         me_loop(e_menu_speed_hacks, &sel);
1348         return 0;
1349 }
1350
1351 static const char *men_cfg_cdrr[] = { "Auto", "ON", "OFF", NULL };
1352 static const char h_cfg_cpul[]   = "Shows CPU usage in %";
1353 static const char h_cfg_spu[]    = "Shows active SPU channels\n"
1354                                    "(green: normal, red: fmod, blue: noise)";
1355 static const char h_cfg_fl[]     = "Frame Limiter keeps the game from running too fast";
1356 static const char h_cfg_xa[]     = "Disables XA sound, which can sometimes improve performance";
1357 static const char h_cfg_cdda[]   = "Disable CD Audio for a performance boost\n"
1358                                    "(proper .cue/.bin dump is needed otherwise)";
1359 static const char h_cfg_sio[]    = "You should not need this, breaks games";
1360 static const char h_cfg_spuirq[] = "Compatibility tweak; should be left off";
1361 static const char h_cfg_rcnt1[]  = "Parasite Eve 2, Vandal Hearts 1/2 Fix\n"
1362                                    "(timing hack, breaks other games)";
1363 static const char h_cfg_rcnt2[]  = "InuYasha Sengoku Battle Fix\n"
1364                                    "(timing hack, breaks other games)";
1365 static const char h_cfg_cdrr[]   = "Compatibility tweak (CD timing hack, breaks FMVs)";
1366 static const char h_cfg_nodrc[]  = "Disable dynamic recompiler and use interpreter\n"
1367                                    "Might be useful to overcome some dynarec bugs";
1368 static const char h_cfg_shacks[] = "Breaks games but may give better performance\n"
1369                                    "must reload game for any change to take effect";
1370
1371 static menu_entry e_menu_adv_options[] =
1372 {
1373         mee_onoff_h   ("Show CPU load",          0, g_opts, OPT_SHOWCPU, h_cfg_cpul),
1374         mee_onoff_h   ("Show SPU channels",      0, g_opts, OPT_SHOWSPU, h_cfg_spu),
1375         mee_onoff_h   ("Disable Frame Limiter",  0, g_opts, OPT_NO_FRAMELIM, h_cfg_fl),
1376         mee_onoff_h   ("Disable XA Decoding",    0, Config.Xa, 1, h_cfg_xa),
1377         mee_onoff_h   ("Disable CD Audio",       0, Config.Cdda, 1, h_cfg_cdda),
1378         mee_onoff_h   ("SIO IRQ Always Enabled", 0, Config.Sio, 1, h_cfg_sio),
1379         mee_onoff_h   ("SPU IRQ Always Enabled", 0, Config.SpuIrq, 1, h_cfg_spuirq),
1380         //mee_onoff_h   ("Rootcounter hack",       0, Config.RCntFix, 1, h_cfg_rcnt1),
1381         mee_onoff_h   ("Rootcounter hack 2",     0, Config.VSyncWA, 1, h_cfg_rcnt2),
1382         mee_enum_h    ("CD read reschedule hack",0, Config.CdrReschedule, men_cfg_cdrr, h_cfg_cdrr),
1383         mee_onoff_h   ("Disable dynarec (slow!)",0, Config.Cpu, 1, h_cfg_nodrc),
1384         mee_handler_h ("[Speed hacks]",             menu_loop_speed_hacks, h_cfg_shacks),
1385         mee_end,
1386 };
1387
1388 static int menu_loop_adv_options(int id, int keys)
1389 {
1390         static int sel = 0;
1391         me_loop(e_menu_adv_options, &sel);
1392         return 0;
1393 }
1394
1395 // ------------ options menu ------------
1396
1397 static int mh_restore_defaults(int id, int keys)
1398 {
1399         menu_set_defconfig();
1400         me_update_msg("defaults restored");
1401         return 1;
1402 }
1403
1404 static const char *men_region[]       = { "Auto", "NTSC", "PAL", NULL };
1405 static const char *men_frameskip[]    = { "Auto", "Off", "1", "2", "3", NULL };
1406 /*
1407 static const char *men_confirm_save[] = { "OFF", "writes", "loads", "both", NULL };
1408 static const char h_confirm_save[]    = "Ask for confirmation when overwriting save,\n"
1409                                         "loading state or both";
1410 */
1411 static const char h_restore_def[]     = "Switches back to default / recommended\n"
1412                                         "configuration";
1413 static const char h_frameskip[]       = "Warning: frameskip sometimes causes glitches\n";
1414
1415 static menu_entry e_menu_options[] =
1416 {
1417 //      mee_range     ("Save slot",                0, state_slot, 0, 9),
1418 //      mee_enum_h    ("Confirm savestate",        0, dummy, men_confirm_save, h_confirm_save),
1419         mee_enum_h    ("Frameskip",                0, frameskip, men_frameskip, h_frameskip),
1420         mee_onoff     ("Show FPS",                 0, g_opts, OPT_SHOWFPS),
1421         mee_enum      ("Region",                   0, region, men_region),
1422         mee_range     ("CPU clock",                MA_OPT_CPU_CLOCKS, cpu_clock, 20, 5000),
1423         mee_handler_id("[Display]",                MA_OPT_DISP_OPTS, menu_loop_gfx_options),
1424         mee_handler   ("[BIOS/Plugins]",           menu_loop_plugin_options),
1425         mee_handler   ("[Advanced]",               menu_loop_adv_options),
1426         mee_cust_nosave("Save global config",      MA_OPT_SAVECFG,      mh_savecfg, mgn_saveloadcfg),
1427         mee_cust_nosave("Save cfg for loaded game",MA_OPT_SAVECFG_GAME, mh_savecfg, mgn_saveloadcfg),
1428         mee_handler_h ("Restore default config",   mh_restore_defaults, h_restore_def),
1429         mee_end,
1430 };
1431
1432 static int menu_loop_options(int id, int keys)
1433 {
1434         static int sel = 0;
1435         int i;
1436
1437         i = me_id2offset(e_menu_options, MA_OPT_CPU_CLOCKS);
1438         e_menu_options[i].enabled = cpu_clock != 0 ? 1 : 0;
1439         me_enable(e_menu_options, MA_OPT_SAVECFG_GAME, ready_to_go && CdromId[0]);
1440
1441         me_loop(e_menu_options, &sel);
1442
1443         return 0;
1444 }
1445
1446 // ------------ debug menu ------------
1447
1448 static void draw_frame_debug(GPUFreeze_t *gpuf)
1449 {
1450         int w = min(g_menuscreen_w, 1024);
1451         int h = min(g_menuscreen_h, 512);
1452         u16 *d = g_menuscreen_ptr;
1453         u16 *s = (u16 *)gpuf->psxVRam;
1454         char buff[64];
1455         int ty = 1;
1456
1457         gpuf->ulFreezeVersion = 1;
1458         if (GPU_freeze != NULL)
1459                 GPU_freeze(1, gpuf);
1460
1461         for (; h > 0; h--, d += g_menuscreen_w, s += 1024)
1462                 bgr555_to_rgb565(d, s, w * 2);
1463
1464         smalltext_out16(4, 1, "build: "__DATE__ " " __TIME__ " " REV, 0xe7fc);
1465         snprintf(buff, sizeof(buff), "GPU sr: %08x", gpuf->ulStatus);
1466         smalltext_out16(4, (ty += me_sfont_h), buff, 0xe7fc);
1467         snprintf(buff, sizeof(buff), "PC/SP: %08x %08x", psxRegs.pc, psxRegs.GPR.n.sp);
1468         smalltext_out16(4, (ty += me_sfont_h), buff, 0xe7fc);
1469 }
1470
1471 static void debug_menu_loop(void)
1472 {
1473         GPUFreeze_t *gpuf;
1474         int inp;
1475
1476         gpuf = malloc(sizeof(*gpuf));
1477         if (gpuf == NULL)
1478                 return;
1479
1480         while (1)
1481         {
1482                 menu_draw_begin(0);
1483                 draw_frame_debug(gpuf);
1484                 menu_draw_end();
1485
1486                 inp = in_menu_wait(PBTN_MOK|PBTN_MBACK|PBTN_MA2|PBTN_MA3|PBTN_L|PBTN_R |
1487                                         PBTN_UP|PBTN_DOWN|PBTN_LEFT|PBTN_RIGHT, 70);
1488                 if (inp & PBTN_MBACK)
1489                         break;
1490         }
1491
1492         free(gpuf);
1493 }
1494
1495 // --------- memcard manager ---------
1496
1497 static void draw_mc_icon(int dx, int dy, const u16 *s)
1498 {
1499         u16 *d;
1500         int x, y, l, p;
1501         
1502         d = (u16 *)g_menuscreen_ptr + g_menuscreen_w * dy + dx;
1503
1504         for (y = 0; y < 16; y++, s += 16) {
1505                 for (l = 0; l < 2; l++, d += g_menuscreen_w) {
1506                         for (x = 0; x < 16; x++) {
1507                                 p = s[x];
1508                                 d[x*2] = d[x*2 + 1] = ((p & 0x7c00) >> 10)
1509                                         | ((p & 0x03e0) << 1) | ((p & 0x1f) << 11);
1510                         }
1511                 }
1512         }
1513 }
1514
1515 static void draw_mc_bg(void)
1516 {
1517         McdBlock *blocks1, *blocks2;
1518         int maxicons = 15;
1519         int i, y, row2;
1520
1521         blocks1 = malloc(15 * sizeof(blocks1[0]));
1522         blocks2 = malloc(15 * sizeof(blocks1[0]));
1523         if (blocks1 == NULL || blocks2 == NULL)
1524                 goto out;
1525
1526         for (i = 0; i < 15; i++) {
1527                 GetMcdBlockInfo(1, i + 1, &blocks1[i]);
1528                 GetMcdBlockInfo(2, i + 1, &blocks2[i]);
1529         }
1530
1531         menu_draw_begin(1);
1532
1533         memcpy(g_menuscreen_ptr, g_menubg_src_ptr, g_menuscreen_w * g_menuscreen_h * 2);
1534
1535         y = g_menuscreen_h / 2 - 15 * 32 / 2;
1536         if (y < 0) {
1537                 // doesn't fit..
1538                 y = 0;
1539                 maxicons = g_menuscreen_h / 32;
1540         }
1541
1542         row2 = g_menuscreen_w / 2;
1543         for (i = 0; i < maxicons; i++) {
1544                 draw_mc_icon(8, y + i * 32, (u16 *)blocks1[i].Icon);
1545                 smalltext_out16(10+32, y + i * 32 + 8, blocks1[i].sTitle, 0xf71e);
1546
1547                 draw_mc_icon(row2 + 8, y + i * 32, (u16 *)blocks2[i].Icon);
1548                 smalltext_out16(row2 + 10+32, y + i * 32 + 8, blocks2[i].sTitle, 0xf71e);
1549         }
1550
1551         menu_darken_bg(g_menubg_ptr, g_menuscreen_ptr, g_menuscreen_w * g_menuscreen_h, 0);
1552
1553         menu_draw_end();
1554 out:
1555         free(blocks1);
1556         free(blocks2);
1557 }
1558
1559 static void handle_memcard_sel(void)
1560 {
1561         Config.Mcd1[0] = 0;
1562         if (memcard1_sel != 0)
1563                 snprintf(Config.Mcd1, sizeof(Config.Mcd1), ".%s%s", MEMCARD_DIR, memcards[memcard1_sel]);
1564         Config.Mcd2[0] = 0;
1565         if (memcard2_sel != 0)
1566                 snprintf(Config.Mcd2, sizeof(Config.Mcd2), ".%s%s", MEMCARD_DIR, memcards[memcard2_sel]);
1567         LoadMcds(Config.Mcd1, Config.Mcd2);
1568         draw_mc_bg();
1569 }
1570
1571 static menu_entry e_memcard_options[] =
1572 {
1573         mee_enum("Memory card 1", 0, memcard1_sel, memcards),
1574         mee_enum("Memory card 2", 0, memcard2_sel, memcards),
1575         mee_end,
1576 };
1577
1578 static int menu_loop_memcards(int id, int keys)
1579 {
1580         static int sel = 0;
1581         char *p;
1582         int i;
1583
1584         memcard1_sel = memcard2_sel = 0;
1585         p = strrchr(Config.Mcd1, '/');
1586         if (p != NULL)
1587                 for (i = 0; memcards[i] != NULL; i++)
1588                         if (strcmp(p + 1, memcards[i]) == 0)
1589                                 { memcard1_sel = i; break; }
1590         p = strrchr(Config.Mcd2, '/');
1591         if (p != NULL)
1592                 for (i = 0; memcards[i] != NULL; i++)
1593                         if (strcmp(p + 1, memcards[i]) == 0)
1594                                 { memcard2_sel = i; break; }
1595
1596         me_loop_d(e_memcard_options, &sel, handle_memcard_sel, NULL);
1597
1598         memcpy(g_menubg_ptr, g_menubg_src_ptr, g_menuscreen_w * g_menuscreen_h * 2);
1599
1600         return 0;
1601 }
1602
1603 // --------- main menu help ----------
1604
1605 static void menu_bios_warn(void)
1606 {
1607         int inp;
1608         static const char msg[] =
1609                 "You don't seem to have copied any BIOS\n"
1610                 "files to\n"
1611 #ifdef __ARM_ARCH_7A__ // XXX
1612                 "<SD card>/pandora/appdata/pcsx_rearmed/bios/\n\n"
1613 #else
1614                 "pcsx_rearmed/bios/\n\n"
1615 #endif
1616                 "While many games work fine with fake\n"
1617                 "(HLE) BIOS, others (like MGS and FF8)\n"
1618                 "require BIOS to work.\n"
1619                 "After copying the file, you'll also need\n"
1620                 "to select it in the emu's menu:\n"
1621                 "options->[BIOS/Plugins]\n\n"
1622                 "The file is usually named SCPH1001.BIN,\n"
1623                 "but other not compressed files can be\n"
1624                 "used too.\n\n"
1625                 "Press (B) or (X) to continue";
1626
1627         while (1)
1628         {
1629                 draw_menu_message(msg, NULL);
1630
1631                 inp = in_menu_wait(PBTN_MOK|PBTN_MBACK, 70);
1632                 if (inp & (PBTN_MBACK|PBTN_MOK))
1633                         return;
1634         }
1635 }
1636
1637 // ------------ main menu ------------
1638
1639 void OnFile_Exit();
1640
1641 static void draw_frame_main(void)
1642 {
1643         struct tm *tmp;
1644         time_t ltime;
1645         char ltime_s[16];
1646         char buff[64];
1647
1648         if (CdromId[0] != 0) {
1649                 snprintf(buff, sizeof(buff), "%.32s/%.9s (running as %s, with %s)",
1650                          get_cd_label(), CdromId, Config.PsxType ? "PAL" : "NTSC",
1651                          Config.HLE ? "HLE" : "BIOS");
1652                 smalltext_out16(4, 1, buff, 0x105f);
1653         }
1654
1655         if (ready_to_go) {
1656                 ltime = time(NULL);
1657                 tmp = localtime(&ltime);
1658                 strftime(ltime_s, sizeof(ltime_s), "%H:%M", tmp);
1659                 snprintf(buff, sizeof(buff), "%s %3d%%", ltime_s, plat_get_bat_capacity());
1660                 smalltext_out16(4, 1 + me_sfont_h, buff, 0x105f);
1661         }
1662 }
1663
1664 static void draw_frame_credits(void)
1665 {
1666         smalltext_out16(4, 1, "build: "__DATE__ " " __TIME__ " " REV, 0xe7fc);
1667 }
1668
1669 static const char credits_text[] = 
1670         "PCSX-ReARMed\n\n"
1671         "(C) 1999-2003 PCSX Team\n"
1672         "(C) 2005-2009 PCSX-df Team\n"
1673         "(C) 2009-2011 PCSX-Reloaded Team\n\n"
1674         "ARM recompiler (C) 2009-2011 Ari64\n"
1675 #ifdef __ARM_NEON__
1676         "ARM NEON GPU (c) 2011 Exophase\n"
1677 #endif
1678         "PEOpS GPU and SPU by Pete Bernert\n"
1679         "  and the P.E.Op.S. team\n"
1680         "PCSX4ALL plugin by PCSX4ALL team\n"
1681         "  Chui, Franxis, Unai\n\n"
1682         "integration, optimization and\n"
1683         "  frontend (C) 2010-2011 notaz\n";
1684
1685 static int reset_game(void)
1686 {
1687         // sanity check
1688         if (bios_sel == 0 && !Config.HLE)
1689                 return -1;
1690
1691         ClosePlugins();
1692         OpenPlugins();
1693         SysReset();
1694         if (CheckCdrom() != -1) {
1695                 LoadCdrom();
1696         }
1697         return 0;
1698 }
1699
1700 static int reload_plugins(const char *cdimg)
1701 {
1702         pl_vout_buf = NULL;
1703
1704         ClosePlugins();
1705
1706         set_cd_image(cdimg);
1707         LoadPlugins();
1708         pcnt_hook_plugins();
1709         NetOpened = 0;
1710         if (OpenPlugins() == -1) {
1711                 me_update_msg("failed to open plugins");
1712                 return -1;
1713         }
1714         plugin_call_rearmed_cbs();
1715
1716         cdrIsoMultidiskCount = 1;
1717         CdromId[0] = '\0';
1718         CdromLabel[0] = '\0';
1719
1720         return 0;
1721 }
1722
1723 static int run_bios(void)
1724 {
1725         if (bios_sel == 0)
1726                 return -1;
1727
1728         ready_to_go = 0;
1729         if (reload_plugins(NULL) != 0)
1730                 return -1;
1731         SysReset();
1732
1733         ready_to_go = 1;
1734         return 0;
1735 }
1736
1737 static int run_exe(void)
1738 {
1739         const char *fname;
1740
1741         fname = menu_loop_romsel(last_selected_fname, sizeof(last_selected_fname));
1742         if (fname == NULL)
1743                 return -1;
1744
1745         ready_to_go = 0;
1746         if (reload_plugins(NULL) != 0)
1747                 return -1;
1748
1749         SysReset();
1750         if (Load(fname) != 0) {
1751                 me_update_msg("exe load failed, bad file?");
1752                 printf("meh\n");
1753                 return -1;
1754         }
1755
1756         ready_to_go = 1;
1757         return 0;
1758 }
1759
1760 static int run_cd_image(const char *fname)
1761 {
1762         ready_to_go = 0;
1763         reload_plugins(fname);
1764
1765         // always autodetect, menu_sync_config will override as needed
1766         Config.PsxAuto = 1;
1767
1768         if (CheckCdrom() == -1) {
1769                 // Only check the CD if we are starting the console with a CD
1770                 ClosePlugins();
1771                 me_update_msg("unsupported/invalid CD image");
1772                 return -1;
1773         }
1774
1775         SysReset();
1776
1777         // Read main executable directly from CDRom and start it
1778         if (LoadCdrom() == -1) {
1779                 ClosePlugins();
1780                 me_update_msg("failed to load CD image");
1781                 return -1;
1782         }
1783
1784         ready_to_go = 1;
1785         snprintf(hud_msg, sizeof(hud_msg), "Booting up...");
1786         hud_new_msg = 2;
1787         return 0;
1788 }
1789
1790 static int romsel_run(void)
1791 {
1792         int prev_gpu, prev_spu;
1793         const char *fname;
1794
1795         fname = menu_loop_romsel(last_selected_fname, sizeof(last_selected_fname));
1796         if (fname == NULL)
1797                 return -1;
1798
1799         printf("selected file: %s\n", fname);
1800
1801         new_dynarec_clear_full();
1802
1803         if (run_cd_image(fname) != 0)
1804                 return -1;
1805
1806         prev_gpu = gpu_plugsel;
1807         prev_spu = spu_plugsel;
1808         if (menu_load_config(1) != 0)
1809                 menu_load_config(0);
1810
1811         // check for plugin changes, have to repeat
1812         // loading if game config changed plugins to reload them
1813         if (prev_gpu != gpu_plugsel || prev_spu != spu_plugsel) {
1814                 printf("plugin change detected, reloading plugins..\n");
1815                 if (run_cd_image(fname) != 0)
1816                         return -1;
1817         }
1818
1819         if (Config.HLE)
1820                 printf("note: running without BIOS, expect compatibility problems\n");
1821
1822         strcpy(last_selected_fname, rom_fname_reload);
1823         return 0;
1824 }
1825
1826 static int swap_cd_image(void)
1827 {
1828         char *fname;
1829
1830         fname = menu_loop_romsel(last_selected_fname, sizeof(last_selected_fname));
1831         if (fname == NULL)
1832                 return -1;
1833
1834         printf("selected file: %s\n", fname);
1835
1836         CdromId[0] = '\0';
1837         CdromLabel[0] = '\0';
1838
1839         set_cd_image(fname);
1840         if (ReloadCdromPlugin() < 0) {
1841                 me_update_msg("failed to load cdr plugin");
1842                 return -1;
1843         }
1844         if (CDR_open() < 0) {
1845                 me_update_msg("failed to open cdr plugin");
1846                 return -1;
1847         }
1848
1849         SetCdOpenCaseTime(time(NULL) + 2);
1850         LidInterrupt();
1851
1852         strcpy(last_selected_fname, rom_fname_reload);
1853         return 0;
1854 }
1855
1856 static int swap_cd_multidisk(void)
1857 {
1858         cdrIsoMultidiskSelect++;
1859         CdromId[0] = '\0';
1860         CdromLabel[0] = '\0';
1861
1862         CDR_close();
1863         if (CDR_open() < 0) {
1864                 me_update_msg("failed to open cdr plugin");
1865                 return -1;
1866         }
1867
1868         SetCdOpenCaseTime(time(NULL) + 2);
1869         LidInterrupt();
1870
1871         return 0;
1872 }
1873
1874 static int main_menu_handler(int id, int keys)
1875 {
1876         switch (id)
1877         {
1878         case MA_MAIN_RESUME_GAME:
1879                 if (ready_to_go)
1880                         return 1;
1881                 break;
1882         case MA_MAIN_SAVE_STATE:
1883                 if (ready_to_go)
1884                         return menu_loop_savestate(0);
1885                 break;
1886         case MA_MAIN_LOAD_STATE:
1887                 if (ready_to_go)
1888                         return menu_loop_savestate(1);
1889                 break;
1890         case MA_MAIN_RESET_GAME:
1891                 if (ready_to_go && reset_game() == 0)
1892                         return 1;
1893                 break;
1894         case MA_MAIN_LOAD_ROM:
1895                 if (romsel_run() == 0)
1896                         return 1;
1897                 break;
1898         case MA_MAIN_SWAP_CD:
1899                 if (swap_cd_image() == 0)
1900                         return 1;
1901                 break;
1902         case MA_MAIN_SWAP_CD_MULTI:
1903                 if (swap_cd_multidisk() == 0)
1904                         return 1;
1905                 break;
1906         case MA_MAIN_RUN_BIOS:
1907                 if (run_bios() == 0)
1908                         return 1;
1909                 break;
1910         case MA_MAIN_RUN_EXE:
1911                 if (run_exe() == 0)
1912                         return 1;
1913                 break;
1914         case MA_MAIN_CREDITS:
1915                 draw_menu_message(credits_text, draw_frame_credits);
1916                 in_menu_wait(PBTN_MOK|PBTN_MBACK, 70);
1917                 break;
1918         case MA_MAIN_EXIT:
1919                 OnFile_Exit();
1920                 break;
1921         default:
1922                 lprintf("%s: something unknown selected\n", __FUNCTION__);
1923                 break;
1924         }
1925
1926         return 0;
1927 }
1928
1929 static menu_entry e_menu_main2[] =
1930 {
1931         mee_handler_id("Change CD image",    MA_MAIN_SWAP_CD,       main_menu_handler),
1932         mee_handler_id("Next multidisk CD",  MA_MAIN_SWAP_CD_MULTI, main_menu_handler),
1933         mee_handler_id("Run BIOS",           MA_MAIN_RUN_BIOS,      main_menu_handler),
1934         mee_handler_id("Run EXE",            MA_MAIN_RUN_EXE,       main_menu_handler),
1935         mee_handler   ("Memcard manager",    menu_loop_memcards),
1936         mee_end,
1937 };
1938
1939 static int main_menu2_handler(int id, int keys)
1940 {
1941         static int sel = 0;
1942
1943         me_enable(e_menu_main2, MA_MAIN_SWAP_CD,  ready_to_go);
1944         me_enable(e_menu_main2, MA_MAIN_SWAP_CD_MULTI, ready_to_go && cdrIsoMultidiskCount > 1);
1945         me_enable(e_menu_main2, MA_MAIN_RUN_BIOS, bios_sel != 0);
1946
1947         return me_loop_d(e_menu_main2, &sel, NULL, draw_frame_main);
1948 }
1949
1950 static const char h_extra[] = "Change CD, manage memcards..\n";
1951
1952 static menu_entry e_menu_main[] =
1953 {
1954         mee_label     (""),
1955         mee_label     (""),
1956         mee_handler_id("Resume game",        MA_MAIN_RESUME_GAME, main_menu_handler),
1957         mee_handler_id("Save State",         MA_MAIN_SAVE_STATE,  main_menu_handler),
1958         mee_handler_id("Load State",         MA_MAIN_LOAD_STATE,  main_menu_handler),
1959         mee_handler_id("Reset game",         MA_MAIN_RESET_GAME,  main_menu_handler),
1960         mee_handler_id("Load CD image",      MA_MAIN_LOAD_ROM,    main_menu_handler),
1961         mee_handler   ("Options",            menu_loop_options),
1962         mee_handler   ("Controls",           menu_loop_keyconfig),
1963         mee_handler_h ("Extra stuff",        main_menu2_handler,  h_extra),
1964         mee_handler_id("Credits",            MA_MAIN_CREDITS,     main_menu_handler),
1965         mee_handler_id("Exit",               MA_MAIN_EXIT,        main_menu_handler),
1966         mee_end,
1967 };
1968
1969 // ----------------------------
1970
1971 static void menu_leave_emu(void);
1972
1973 void menu_loop(void)
1974 {
1975         static int sel = 0;
1976
1977         menu_leave_emu();
1978
1979         if (bioses[1] == NULL && !warned_about_bios) {
1980                 menu_bios_warn();
1981                 warned_about_bios = 1;
1982         }
1983
1984         me_enable(e_menu_main, MA_MAIN_RESUME_GAME, ready_to_go);
1985         me_enable(e_menu_main, MA_MAIN_SAVE_STATE,  ready_to_go && CdromId[0]);
1986         me_enable(e_menu_main, MA_MAIN_LOAD_STATE,  ready_to_go && CdromId[0]);
1987         me_enable(e_menu_main, MA_MAIN_RESET_GAME,  ready_to_go);
1988
1989         in_set_config_int(0, IN_CFG_BLOCKING, 1);
1990
1991         do {
1992                 me_loop_d(e_menu_main, &sel, NULL, draw_frame_main);
1993         } while (!ready_to_go);
1994
1995         /* wait until menu, ok, back is released */
1996         while (in_menu_wait_any(50) & (PBTN_MENU|PBTN_MOK|PBTN_MBACK))
1997                 ;
1998
1999         in_set_config_int(0, IN_CFG_BLOCKING, 0);
2000
2001         menu_prepare_emu();
2002 }
2003
2004 static int qsort_strcmp(const void *p1, const void *p2)
2005 {
2006         char * const *s1 = (char * const *)p1;
2007         char * const *s2 = (char * const *)p2;
2008         return strcasecmp(*s1, *s2);
2009 }
2010
2011 static void scan_bios_plugins(void)
2012 {
2013         char fname[MAXPATHLEN];
2014         struct dirent *ent;
2015         int bios_i, gpu_i, spu_i, mc_i;
2016         char *p;
2017         DIR *dir;
2018
2019         bioses[0] = "HLE";
2020         gpu_plugins[0] = "builtin_gpu";
2021         spu_plugins[0] = "builtin_spu";
2022         memcards[0] = "(none)";
2023         bios_i = gpu_i = spu_i = mc_i = 1;
2024
2025         snprintf(fname, sizeof(fname), "%s/", Config.BiosDir);
2026         dir = opendir(fname);
2027         if (dir == NULL) {
2028                 perror("scan_bios_plugins bios opendir");
2029                 goto do_plugins;
2030         }
2031
2032         while (1) {
2033                 struct stat st;
2034
2035                 errno = 0;
2036                 ent = readdir(dir);
2037                 if (ent == NULL) {
2038                         if (errno != 0)
2039                                 perror("readdir");
2040                         break;
2041                 }
2042
2043                 if (ent->d_type != DT_REG && ent->d_type != DT_LNK)
2044                         continue;
2045
2046                 snprintf(fname, sizeof(fname), "%s/%s", Config.BiosDir, ent->d_name);
2047                 if (stat(fname, &st) != 0 || st.st_size != 512*1024) {
2048                         printf("bad BIOS file: %s\n", ent->d_name);
2049                         continue;
2050                 }
2051
2052                 if (bios_i < ARRAY_SIZE(bioses) - 1) {
2053                         bioses[bios_i++] = strdup(ent->d_name);
2054                         continue;
2055                 }
2056
2057                 printf("too many BIOSes, dropping \"%s\"\n", ent->d_name);
2058         }
2059
2060         closedir(dir);
2061
2062 do_plugins:
2063         snprintf(fname, sizeof(fname), "%s/", Config.PluginsDir);
2064         dir = opendir(fname);
2065         if (dir == NULL) {
2066                 perror("scan_bios_plugins plugins opendir");
2067                 goto do_memcards;
2068         }
2069
2070         while (1) {
2071                 void *h, *tmp;
2072
2073                 errno = 0;
2074                 ent = readdir(dir);
2075                 if (ent == NULL) {
2076                         if (errno != 0)
2077                                 perror("readdir");
2078                         break;
2079                 }
2080                 p = strstr(ent->d_name, ".so");
2081                 if (p == NULL)
2082                         continue;
2083
2084                 snprintf(fname, sizeof(fname), "%s/%s", Config.PluginsDir, ent->d_name);
2085                 h = dlopen(fname, RTLD_LAZY | RTLD_LOCAL);
2086                 if (h == NULL) {
2087                         fprintf(stderr, "%s\n", dlerror());
2088                         continue;
2089                 }
2090
2091                 // now what do we have here?
2092                 tmp = dlsym(h, "GPUinit");
2093                 if (tmp) {
2094                         dlclose(h);
2095                         if (gpu_i < ARRAY_SIZE(gpu_plugins) - 1)
2096                                 gpu_plugins[gpu_i++] = strdup(ent->d_name);
2097                         continue;
2098                 }
2099
2100                 tmp = dlsym(h, "SPUinit");
2101                 if (tmp) {
2102                         dlclose(h);
2103                         if (spu_i < ARRAY_SIZE(spu_plugins) - 1)
2104                                 spu_plugins[spu_i++] = strdup(ent->d_name);
2105                         continue;
2106                 }
2107
2108                 fprintf(stderr, "ignoring unidentified plugin: %s\n", fname);
2109                 dlclose(h);
2110         }
2111
2112         closedir(dir);
2113
2114 do_memcards:
2115         dir = opendir("." MEMCARD_DIR);
2116         if (dir == NULL) {
2117                 perror("scan_bios_plugins memcards opendir");
2118                 return;
2119         }
2120
2121         while (1) {
2122                 struct stat st;
2123
2124                 errno = 0;
2125                 ent = readdir(dir);
2126                 if (ent == NULL) {
2127                         if (errno != 0)
2128                                 perror("readdir");
2129                         break;
2130                 }
2131
2132                 if (ent->d_type != DT_REG && ent->d_type != DT_LNK)
2133                         continue;
2134
2135                 snprintf(fname, sizeof(fname), "." MEMCARD_DIR "%s", ent->d_name);
2136                 if (stat(fname, &st) != 0) {
2137                         printf("bad memcard file: %s\n", ent->d_name);
2138                         continue;
2139                 }
2140
2141                 if (mc_i < ARRAY_SIZE(memcards) - 1) {
2142                         memcards[mc_i++] = strdup(ent->d_name);
2143                         continue;
2144                 }
2145
2146                 printf("too many memcards, dropping \"%s\"\n", ent->d_name);
2147         }
2148
2149         if (mc_i > 2)
2150                 qsort(memcards + 1, mc_i - 1, sizeof(memcards[0]), qsort_strcmp);
2151
2152         closedir(dir);
2153 }
2154
2155 void menu_init(void)
2156 {
2157         char buff[MAXPATHLEN];
2158
2159         strcpy(last_selected_fname, "/media");
2160
2161         scan_bios_plugins();
2162         pnd_menu_init();
2163         menu_init_common();
2164
2165         menu_set_defconfig();
2166         menu_load_config(0);
2167         last_psx_w = 320;
2168         last_psx_h = 240;
2169         last_psx_bpp = 16;
2170
2171         g_menubg_src_ptr = calloc(g_menuscreen_w * g_menuscreen_h * 2, 1);
2172         if (g_menubg_src_ptr == NULL)
2173                 exit(1);
2174         emu_make_path(buff, "skin/background.png", sizeof(buff));
2175         readpng(g_menubg_src_ptr, buff, READPNG_BG, g_menuscreen_w, g_menuscreen_h);
2176
2177 #ifndef __ARM_ARCH_7A__ /* XXX */
2178         me_enable(e_menu_options, MA_OPT_DISP_OPTS, 0);
2179         me_enable(e_menu_keyconfig, MA_CTRL_NUBS_BTNS, 0);
2180 #else
2181         me_enable(e_menu_keyconfig, MA_CTRL_VIBRATION, 0);
2182         me_enable(e_menu_keyconfig, MA_CTRL_DEADZONE, 0);
2183 #endif
2184 }
2185
2186 void menu_notify_mode_change(int w, int h, int bpp)
2187 {
2188         float mult;
2189         int imult;
2190
2191         last_psx_w = w;
2192         last_psx_h = h;
2193         last_psx_bpp = bpp;
2194
2195         switch (scaling) {
2196         case SCALE_1_1:
2197                 g_layer_w = w; g_layer_h = h;
2198                 break;
2199
2200         case SCALE_4_3v2:
2201                 if (h > g_menuscreen_h || (240 < h && h <= 360))
2202                         goto fractional_4_3;
2203
2204                 // 4:3 that prefers integer scaling
2205                 imult = g_menuscreen_h / h;
2206                 g_layer_w = w * imult;
2207                 g_layer_h = h * imult;
2208                 mult = (float)g_layer_w / (float)g_layer_h;
2209                 if (mult < 1.25f || mult > 1.666f)
2210                         g_layer_w = 4.0f/3.0f * (float)g_layer_h;
2211                 printf("  -> %dx%d %.1f\n", g_layer_w, g_layer_h, mult);
2212                 break;
2213
2214         fractional_4_3:
2215         case SCALE_4_3:
2216                 mult = 240.0f / (float)h * 4.0f / 3.0f;
2217                 if (h > 256)
2218                         mult *= 2.0f;
2219                 g_layer_w = mult * (float)g_menuscreen_h;
2220                 g_layer_h = g_menuscreen_h;
2221                 printf("  -> %dx%d %.1f\n", g_layer_w, g_layer_h, mult);
2222                 break;
2223
2224         case SCALE_FULLSCREEN:
2225                 g_layer_w = g_menuscreen_w;
2226                 g_layer_h = g_menuscreen_h;
2227                 break;
2228
2229         default:
2230                 break;
2231         }
2232
2233         g_layer_x = g_menuscreen_w / 2 - g_layer_w / 2;
2234         g_layer_y = g_menuscreen_h / 2 - g_layer_h / 2;
2235         if (g_layer_x < 0) g_layer_x = 0;
2236         if (g_layer_y < 0) g_layer_y = 0;
2237         if (g_layer_w > g_menuscreen_w) g_layer_w = g_menuscreen_w;
2238         if (g_layer_h > g_menuscreen_h) g_layer_w = g_menuscreen_h;
2239 }
2240
2241 static void menu_leave_emu(void)
2242 {
2243         if (GPU_close != NULL) {
2244                 int ret = GPU_close();
2245                 if (ret)
2246                         fprintf(stderr, "Warning: GPU_close returned %d\n", ret);
2247         }
2248
2249         plat_video_menu_enter(ready_to_go);
2250
2251         memcpy(g_menubg_ptr, g_menubg_src_ptr, g_menuscreen_w * g_menuscreen_h * 2);
2252         if (pl_vout_buf != NULL && ready_to_go && last_psx_bpp == 16) {
2253                 int x = max(0, g_menuscreen_w - last_psx_w);
2254                 int y = max(0, g_menuscreen_h / 2 - last_psx_h / 2);
2255                 int w = min(g_menuscreen_w, last_psx_w);
2256                 int h = min(g_menuscreen_h, last_psx_h);
2257                 u16 *d = (u16 *)g_menubg_ptr + g_menuscreen_w * y + x;
2258                 u16 *s = pl_vout_buf;
2259
2260                 for (; h > 0; h--, d += g_menuscreen_w, s += last_psx_w)
2261                         menu_darken_bg(d, s, w, 0);
2262         }
2263
2264         if (ready_to_go)
2265                 cpu_clock = plat_cpu_clock_get();
2266 }
2267
2268 void menu_prepare_emu(void)
2269 {
2270         R3000Acpu *prev_cpu = psxCpu;
2271
2272         plat_video_menu_leave();
2273
2274         menu_notify_mode_change(last_psx_w, last_psx_h, last_psx_bpp);
2275
2276         psxCpu = (Config.Cpu == CPU_INTERPRETER) ? &psxInt : &psxRec;
2277         if (psxCpu != prev_cpu)
2278                 // note that this does not really reset, just clears drc caches
2279                 psxCpu->Reset();
2280
2281         // core doesn't care about Config.Cdda changes,
2282         // so handle them manually here
2283         if (Config.Cdda)
2284                 CDR_stop();
2285
2286         menu_sync_config();
2287         apply_lcdrate(Config.PsxType);
2288         apply_filter(filter);
2289         plat_cpu_clock_apply(cpu_clock);
2290
2291         // push config to GPU plugin
2292         plugin_call_rearmed_cbs();
2293
2294         if (GPU_open != NULL) {
2295                 int ret = GPU_open(&gpuDisp, "PCSX", NULL);
2296                 if (ret)
2297                         fprintf(stderr, "Warning: GPU_open returned %d\n", ret);
2298         }
2299
2300         dfinput_activate();
2301 }
2302
2303 void me_update_msg(const char *msg)
2304 {
2305         strncpy(menu_error_msg, msg, sizeof(menu_error_msg));
2306         menu_error_msg[sizeof(menu_error_msg) - 1] = 0;
2307
2308         menu_error_time = plat_get_ticks_ms();
2309         lprintf("msg: %s\n", menu_error_msg);
2310 }
2311