51476f53ca2732b391071229cfe4de8e608b63fc
[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 "common/plat.h"
29 #include "common/input.h"
30 #include "linux/in_evdev.h"
31 #include "../libpcsxcore/misc.h"
32 #include "../libpcsxcore/cdrom.h"
33 #include "../libpcsxcore/cdriso.h"
34 #include "../libpcsxcore/psemu_plugin_defs.h"
35 #include "../libpcsxcore/new_dynarec/new_dynarec.h"
36 #include "../plugins/dfinput/main.h"
37 #include "../plugins/gpulib/cspace.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 #ifdef __ARM_ARCH_7A__ /* XXX */
760         { "Minimize         ", 1 << SACTION_MINIMIZE },
761 #endif
762         { "Gun Trigger      ", 1 << SACTION_GUN_TRIGGER },
763         { "Gun A button     ", 1 << SACTION_GUN_A },
764         { "Gun B button     ", 1 << SACTION_GUN_B },
765         { "Gun Offscreen Trigger", 1 << SACTION_GUN_TRIGGER2 },
766 #ifndef __ARM_ARCH_7A__ /* XXX */
767         { "Volume Up        ", 1 << SACTION_VOLUME_UP },
768         { "Volume Down      ", 1 << SACTION_VOLUME_DOWN },
769 #endif
770         { NULL,                0 }
771 };
772
773 static char *mystrip(char *str)
774 {
775         int i, len;
776
777         len = strlen(str);
778         for (i = 0; i < len; i++)
779                 if (str[i] != ' ') break;
780         if (i > 0) memmove(str, str + i, len - i + 1);
781
782         len = strlen(str);
783         for (i = len - 1; i >= 0; i--)
784                 if (str[i] != ' ') break;
785         str[i+1] = 0;
786
787         return str;
788 }
789
790 static void get_line(char *d, size_t size, const char *s)
791 {
792         const char *pe;
793         size_t len;
794
795         for (pe = s; *pe != '\r' && *pe != '\n' && *pe != 0; pe++)
796                 ;
797         len = pe - s;
798         if (len > size - 1)
799                 len = size - 1;
800         strncpy(d, s, len);
801         d[len] = 0;
802
803         mystrip(d);
804 }
805
806 static void keys_write_all(FILE *f)
807 {
808         int d;
809
810         for (d = 0; d < IN_MAX_DEVS; d++)
811         {
812                 const int *binds = in_get_dev_binds(d);
813                 const char *name = in_get_dev_name(d, 0, 0);
814                 int k, count = 0;
815
816                 if (binds == NULL || name == NULL)
817                         continue;
818
819                 fprintf(f, "binddev = %s\n", name);
820                 in_get_config(d, IN_CFG_BIND_COUNT, &count);
821
822                 for (k = 0; k < count; k++)
823                 {
824                         int i, kbinds, mask;
825                         char act[32];
826
827                         act[0] = act[31] = 0;
828                         name = in_get_key_name(d, k);
829
830                         kbinds = binds[IN_BIND_OFFS(k, IN_BINDTYPE_PLAYER12)];
831                         for (i = 0; kbinds && i < ARRAY_SIZE(me_ctrl_actions) - 1; i++) {
832                                 mask = me_ctrl_actions[i].mask;
833                                 if (mask & kbinds) {
834                                         strncpy(act, me_ctrl_actions[i].name, 31);
835                                         fprintf(f, "bind %s = player1 %s\n", name, mystrip(act));
836                                         kbinds &= ~mask;
837                                 }
838                                 mask = me_ctrl_actions[i].mask << 16;
839                                 if (mask & kbinds) {
840                                         strncpy(act, me_ctrl_actions[i].name, 31);
841                                         fprintf(f, "bind %s = player2 %s\n", name, mystrip(act));
842                                         kbinds &= ~mask;
843                                 }
844                         }
845
846                         kbinds = binds[IN_BIND_OFFS(k, IN_BINDTYPE_EMU)];
847                         for (i = 0; kbinds && emuctrl_actions[i].name != NULL; i++) {
848                                 mask = emuctrl_actions[i].mask;
849                                 if (mask & kbinds) {
850                                         strncpy(act, emuctrl_actions[i].name, 31);
851                                         fprintf(f, "bind %s = %s\n", name, mystrip(act));
852                                         kbinds &= ~mask;
853                                 }
854                         }
855                 }
856
857                 for (k = 0; k < array_size(in_adev); k++)
858                 {
859                         if (in_adev[k] == d)
860                                 fprintf(f, "bind_analog = %d\n", k);
861                 }
862         }
863 }
864
865 static int parse_bind_val(const char *val, int *type)
866 {
867         int i;
868
869         *type = IN_BINDTYPE_NONE;
870         if (val[0] == 0)
871                 return 0;
872         
873         if (strncasecmp(val, "player", 6) == 0)
874         {
875                 int player, shift = 0;
876                 player = atoi(val + 6) - 1;
877
878                 if ((unsigned int)player > 1)
879                         return -1;
880                 if (player == 1)
881                         shift = 16;
882
883                 *type = IN_BINDTYPE_PLAYER12;
884                 for (i = 0; me_ctrl_actions[i].name != NULL; i++) {
885                         if (strncasecmp(me_ctrl_actions[i].name, val + 8, strlen(val + 8)) == 0)
886                                 return me_ctrl_actions[i].mask << shift;
887                 }
888         }
889         for (i = 0; emuctrl_actions[i].name != NULL; i++) {
890                 if (strncasecmp(emuctrl_actions[i].name, val, strlen(val)) == 0) {
891                         *type = IN_BINDTYPE_EMU;
892                         return emuctrl_actions[i].mask;
893                 }
894         }
895
896         return -1;
897 }
898
899 static void keys_load_all(const char *cfg)
900 {
901         char dev[256], key[128], *act;
902         const char *p;
903         int bind, bindtype;
904         int ret, dev_id;
905
906         p = cfg;
907         while (p != NULL && (p = strstr(p, "binddev = ")) != NULL) {
908                 p += 10;
909
910                 get_line(dev, sizeof(dev), p);
911                 dev_id = in_config_parse_dev(dev);
912                 if (dev_id < 0) {
913                         printf("input: can't handle dev: %s\n", dev);
914                         continue;
915                 }
916
917                 in_unbind_all(dev_id, -1, -1);
918                 while ((p = strstr(p, "bind"))) {
919                         if (strncmp(p, "binddev = ", 10) == 0)
920                                 break;
921
922                         if (strncmp(p, "bind_analog", 11) == 0) {
923                                 ret = sscanf(p, "bind_analog = %d", &bind);
924                                 p += 11;
925                                 if (ret != 1) {
926                                         printf("input: parse error: %16s..\n", p);
927                                         continue;
928                                 }
929                                 if ((unsigned int)bind >= array_size(in_adev)) {
930                                         printf("input: analog id %d out of range\n", bind);
931                                         continue;
932                                 }
933                                 in_adev[bind] = dev_id;
934                                 continue;
935                         }
936
937                         p += 4;
938                         if (*p != ' ') {
939                                 printf("input: parse error: %16s..\n", p);
940                                 continue;
941                         }
942
943                         get_line(key, sizeof(key), p);
944                         act = strchr(key, '=');
945                         if (act == NULL) {
946                                 printf("parse failed: %16s..\n", p);
947                                 continue;
948                         }
949                         *act = 0;
950                         act++;
951                         mystrip(key);
952                         mystrip(act);
953
954                         bind = parse_bind_val(act, &bindtype);
955                         if (bind != -1 && bind != 0) {
956                                 //printf("bind #%d '%s' %08x (%s)\n", dev_id, key, bind, act);
957                                 in_config_bind_key(dev_id, key, bind, bindtype);
958                         }
959                         else
960                                 lprintf("config: unhandled action \"%s\"\n", act);
961                 }
962         }
963         in_clean_binds();
964 }
965
966 static int key_config_loop_wrap(int id, int keys)
967 {
968         switch (id) {
969                 case MA_CTRL_PLAYER1:
970                         key_config_loop(me_ctrl_actions, array_size(me_ctrl_actions) - 1, 0);
971                         break;
972                 case MA_CTRL_PLAYER2:
973                         key_config_loop(me_ctrl_actions, array_size(me_ctrl_actions) - 1, 1);
974                         break;
975                 case MA_CTRL_EMU:
976                         key_config_loop(emuctrl_actions, array_size(emuctrl_actions) - 1, -1);
977                         break;
978                 default:
979                         break;
980         }
981         return 0;
982 }
983
984 static const char *adevnames[IN_MAX_DEVS + 2];
985 static int stick_sel[2];
986
987 static menu_entry e_menu_keyconfig_analog[] =
988 {
989         mee_enum ("Left stick (L3)",  0, stick_sel[0], adevnames),
990         mee_range("  X axis",    0, in_adev_axis[0][0], 0, 7),
991         mee_range("  Y axis",    0, in_adev_axis[0][1], 0, 7),
992         mee_enum ("Right stick (R3)", 0, stick_sel[1], adevnames),
993         mee_range("  X axis",    0, in_adev_axis[1][0], 0, 7),
994         mee_range("  Y axis",    0, in_adev_axis[1][1], 0, 7),
995         mee_end,
996 };
997
998 static int key_config_analog(int id, int keys)
999 {
1000         int i, d, count, sel = 0;
1001         int sel2dev_map[IN_MAX_DEVS];
1002
1003         memset(adevnames, 0, sizeof(adevnames));
1004         memset(sel2dev_map, 0xff, sizeof(sel2dev_map));
1005         memset(stick_sel, 0, sizeof(stick_sel));
1006
1007         adevnames[0] = "None";
1008         i = 1;
1009         for (d = 0; d < IN_MAX_DEVS; d++)
1010         {
1011                 const char *name = in_get_dev_name(d, 0, 1);
1012                 if (name == NULL)
1013                         continue;
1014
1015                 count = 0;
1016                 in_get_config(d, IN_CFG_ABS_AXIS_COUNT, &count);
1017                 if (count == 0)
1018                         continue;
1019
1020                 if (in_adev[0] == d) stick_sel[0] = i;
1021                 if (in_adev[1] == d) stick_sel[1] = i;
1022                 sel2dev_map[i] = d;
1023                 adevnames[i++] = name;
1024         }
1025         adevnames[i] = NULL;
1026
1027         me_loop(e_menu_keyconfig_analog, &sel);
1028
1029         in_adev[0] = sel2dev_map[stick_sel[0]];
1030         in_adev[1] = sel2dev_map[stick_sel[1]];
1031
1032         return 0;
1033 }
1034
1035 static const char *mgn_dev_name(int id, int *offs)
1036 {
1037         const char *name = NULL;
1038         static int it = 0;
1039
1040         if (id == MA_CTRL_DEV_FIRST)
1041                 it = 0;
1042
1043         for (; it < IN_MAX_DEVS; it++) {
1044                 name = in_get_dev_name(it, 1, 1);
1045                 if (name != NULL)
1046                         break;
1047         }
1048
1049         it++;
1050         return name;
1051 }
1052
1053 static const char *mgn_saveloadcfg(int id, int *offs)
1054 {
1055         return "";
1056 }
1057
1058 static int mh_savecfg(int id, int keys)
1059 {
1060         if (menu_write_config(id == MA_OPT_SAVECFG_GAME ? 1 : 0) == 0)
1061                 me_update_msg("config saved");
1062         else
1063                 me_update_msg("failed to write config");
1064
1065         return 1;
1066 }
1067
1068 static int mh_input_rescan(int id, int keys)
1069 {
1070         //menu_sync_config();
1071         in_probe();
1072         me_update_msg("rescan complete.");
1073
1074         return 0;
1075 }
1076
1077 static const char *men_in_type_sel[] = {
1078         "Standard (SCPH-1080)",
1079         "Analog (SCPH-1150)",
1080         "GunCon",
1081         NULL
1082 };
1083 static const char h_nub_btns[] = "Experimental, keep this OFF if unsure. Select rescan after change.";
1084 static const char h_notsgun[]  = "Don't trigger (shoot) when touching screen in gun games.";
1085 static const char h_vibration[]= "Must select analog above and enable this ingame too.";
1086
1087 static menu_entry e_menu_keyconfig[] =
1088 {
1089         mee_handler_id("Player 1",              MA_CTRL_PLAYER1,    key_config_loop_wrap),
1090         mee_handler_id("Player 2",              MA_CTRL_PLAYER2,    key_config_loop_wrap),
1091         mee_handler_id("Analog controls",       MA_CTRL_ANALOG,     key_config_analog),
1092         mee_handler_id("Emulator/Gun controls", MA_CTRL_EMU,        key_config_loop_wrap),
1093         mee_label     (""),
1094         mee_enum      ("Port 1 device",     0, in_type_sel1,    men_in_type_sel),
1095         mee_enum      ("Port 2 device",     0, in_type_sel2,    men_in_type_sel),
1096         mee_onoff_h   ("Nubs as buttons",   MA_CTRL_NUBS_BTNS,  in_evdev_allow_abs_only, 1, h_nub_btns),
1097         mee_onoff_h   ("Vibration",         MA_CTRL_VIBRATION,  in_enable_vibration, 1, h_vibration),
1098         mee_range     ("Analog deadzone",   MA_CTRL_DEADZONE,   analog_deadzone, 1, 99),
1099         mee_onoff_h   ("No TS Gun trigger", 0, g_opts, OPT_TSGUN_NOTRIGGER, h_notsgun),
1100         mee_cust_nosave("Save global config",       MA_OPT_SAVECFG,      mh_savecfg, mgn_saveloadcfg),
1101         mee_cust_nosave("Save cfg for loaded game", MA_OPT_SAVECFG_GAME, mh_savecfg, mgn_saveloadcfg),
1102         mee_handler   ("Rescan devices:",  mh_input_rescan),
1103         mee_label     (""),
1104         mee_label_mk  (MA_CTRL_DEV_FIRST, 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_label_mk  (MA_CTRL_DEV_NEXT,  mgn_dev_name),
1109         mee_label_mk  (MA_CTRL_DEV_NEXT,  mgn_dev_name),
1110         mee_label_mk  (MA_CTRL_DEV_NEXT,  mgn_dev_name),
1111         mee_end,
1112 };
1113
1114 static int menu_loop_keyconfig(int id, int keys)
1115 {
1116         static int sel = 0;
1117
1118 //      me_enable(e_menu_keyconfig, MA_OPT_SAVECFG_GAME, ready_to_go && CdromId[0]);
1119         me_loop(e_menu_keyconfig, &sel);
1120         return 0;
1121 }
1122
1123 // ------------ gfx options menu ------------
1124
1125 static const char *men_scaler[] = { "1x1", "scaled 4:3", "integer scaled 4:3", "fullscreen", "custom", NULL };
1126 static const char h_cscaler[]   = "Displays the scaler layer, you can resize it\n"
1127                                   "using d-pad or move it using R+d-pad";
1128 static const char *men_dummy[] = { NULL };
1129
1130 static int menu_loop_cscaler(int id, int keys)
1131 {
1132         unsigned int inp;
1133
1134         scaling = SCALE_CUSTOM;
1135
1136         omap_enable_layer(1);
1137
1138         for (;;)
1139         {
1140                 menu_draw_begin(0);
1141                 memset(g_menuscreen_ptr, 4, g_menuscreen_w * g_menuscreen_h * 2);
1142                 text_out16(2, 2, "%d,%d", g_layer_x, g_layer_y);
1143                 text_out16(2, 480 - 18, "%dx%d | d-pad: resize, R+d-pad: move", g_layer_w, g_layer_h);
1144                 menu_draw_end();
1145
1146                 inp = in_menu_wait(PBTN_UP|PBTN_DOWN|PBTN_LEFT|PBTN_RIGHT|PBTN_R|PBTN_MOK|PBTN_MBACK, 40);
1147                 if (inp & PBTN_UP)    g_layer_y--;
1148                 if (inp & PBTN_DOWN)  g_layer_y++;
1149                 if (inp & PBTN_LEFT)  g_layer_x--;
1150                 if (inp & PBTN_RIGHT) g_layer_x++;
1151                 if (!(inp & PBTN_R)) {
1152                         if (inp & PBTN_UP)    g_layer_h += 2;
1153                         if (inp & PBTN_DOWN)  g_layer_h -= 2;
1154                         if (inp & PBTN_LEFT)  g_layer_w += 2;
1155                         if (inp & PBTN_RIGHT) g_layer_w -= 2;
1156                 }
1157                 if (inp & (PBTN_MOK|PBTN_MBACK))
1158                         break;
1159
1160                 if (inp & (PBTN_UP|PBTN_DOWN|PBTN_LEFT|PBTN_RIGHT)) {
1161                         if (g_layer_x < 0)   g_layer_x = 0;
1162                         if (g_layer_x > 640) g_layer_x = 640;
1163                         if (g_layer_y < 0)   g_layer_y = 0;
1164                         if (g_layer_y > 420) g_layer_y = 420;
1165                         if (g_layer_w < 160) g_layer_w = 160;
1166                         if (g_layer_h < 60)  g_layer_h = 60;
1167                         if (g_layer_x + g_layer_w > 800)
1168                                 g_layer_w = 800 - g_layer_x;
1169                         if (g_layer_y + g_layer_h > 480)
1170                                 g_layer_h = 480 - g_layer_y;
1171                         omap_enable_layer(1);
1172                 }
1173         }
1174
1175         omap_enable_layer(0);
1176
1177         return 0;
1178 }
1179
1180 static menu_entry e_menu_gfx_options[] =
1181 {
1182         mee_enum      ("Scaler",                   0, scaling, men_scaler),
1183         mee_enum      ("Filter",                   MA_OPT_FILTERING, filter, men_dummy),
1184 //      mee_onoff     ("Vsync",                    0, vsync, 1),
1185         mee_cust_h    ("Setup custom scaler",      0, menu_loop_cscaler, NULL, h_cscaler),
1186         mee_end,
1187 };
1188
1189 static int menu_loop_gfx_options(int id, int keys)
1190 {
1191         static int sel = 0;
1192
1193         me_loop(e_menu_gfx_options, &sel);
1194
1195         return 0;
1196 }
1197
1198 // ------------ bios/plugins ------------
1199
1200 #ifdef __ARM_NEON__
1201
1202 static const char h_gpu_neon[] = "Configure built-in NEON GPU plugin";
1203 static const char *men_gpu_interlace[] = { "Off", "On", "Auto", NULL };
1204
1205 static menu_entry e_menu_plugin_gpu_neon[] =
1206 {
1207         mee_enum      ("Enable interlace mode",      0, pl_rearmed_cbs.gpu_neon.allow_interlace, men_gpu_interlace),
1208         mee_end,
1209 };
1210
1211 static int menu_loop_plugin_gpu_neon(int id, int keys)
1212 {
1213         int sel = 0;
1214         me_loop(e_menu_plugin_gpu_neon, &sel);
1215         return 0;
1216 }
1217
1218 #endif
1219
1220 static menu_entry e_menu_plugin_gpu_unai[] =
1221 {
1222         mee_onoff     ("Skip every 2nd line",        0, pl_rearmed_cbs.gpu_unai.lineskip, 1),
1223         mee_onoff     ("Abe's Odyssey hack",         0, pl_rearmed_cbs.gpu_unai.abe_hack, 1),
1224         mee_onoff     ("Disable lighting",           0, pl_rearmed_cbs.gpu_unai.no_light, 1),
1225         mee_onoff     ("Disable blending",           0, pl_rearmed_cbs.gpu_unai.no_blend, 1),
1226         mee_end,
1227 };
1228
1229 static int menu_loop_plugin_gpu_unai(int id, int keys)
1230 {
1231         int sel = 0;
1232         me_loop(e_menu_plugin_gpu_unai, &sel);
1233         return 0;
1234 }
1235
1236 static const char *men_gpu_dithering[] = { "None", "Game dependant", "Always", NULL };
1237 //static const char h_gpu_0[]            = "Needed for Chrono Cross";
1238 static const char h_gpu_1[]            = "Capcom fighting games";
1239 static const char h_gpu_2[]            = "Black screens in Lunar";
1240 static const char h_gpu_3[]            = "Compatibility mode";
1241 static const char h_gpu_6[]            = "Pandemonium 2";
1242 //static const char h_gpu_7[]            = "Skip every second frame";
1243 static const char h_gpu_8[]            = "Needed by Dark Forces";
1244 static const char h_gpu_9[]            = "better g-colors, worse textures";
1245 static const char h_gpu_10[]           = "Toggle busy flags after drawing";
1246
1247 static menu_entry e_menu_plugin_gpu_peops[] =
1248 {
1249         mee_enum      ("Dithering",                  0, pl_rearmed_cbs.gpu_peops.iUseDither, men_gpu_dithering),
1250 //      mee_onoff_h   ("Odd/even bit hack",          0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<0, h_gpu_0),
1251         mee_onoff_h   ("Expand screen width",        0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<1, h_gpu_1),
1252         mee_onoff_h   ("Ignore brightness color",    0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<2, h_gpu_2),
1253         mee_onoff_h   ("Disable coordinate check",   0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<3, h_gpu_3),
1254         mee_onoff_h   ("Lazy screen update",         0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<6, h_gpu_6),
1255 //      mee_onoff_h   ("Old frame skipping",         0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<7, h_gpu_7),
1256         mee_onoff_h   ("Repeated flat tex triangles ",0,pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<8, h_gpu_8),
1257         mee_onoff_h   ("Draw quads with triangles",  0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<9, h_gpu_9),
1258         mee_onoff_h   ("Fake 'gpu busy' states",     0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<10, h_gpu_10),
1259         mee_end,
1260 };
1261
1262 static int menu_loop_plugin_gpu_peops(int id, int keys)
1263 {
1264         static int sel = 0;
1265         me_loop(e_menu_plugin_gpu_peops, &sel);
1266         return 0;
1267 }
1268
1269 static const char *men_peopsgl_texfilter[] = { "None", "Standard", "Extended",
1270         "Standard-sprites", "Extended-sprites", "Standard+sprites", "Extended+sprites", NULL };
1271 static const char *men_peopsgl_fbtex[] = { "Emulated VRam", "Black", "Card", "Card+soft" };
1272
1273 static menu_entry e_menu_plugin_gpu_peopsgl[] =
1274 {
1275         mee_onoff     ("Dithering",                  0, pl_rearmed_cbs.gpu_peopsgl.bDrawDither, 1),
1276         mee_enum      ("Texture Filtering",          0, pl_rearmed_cbs.gpu_peopsgl.iFilterType, men_peopsgl_texfilter),
1277         mee_enum      ("Framebuffer Textures",       0, pl_rearmed_cbs.gpu_peopsgl.iFrameTexType, men_peopsgl_fbtex),
1278         mee_onoff     ("Mask Detect",                0, pl_rearmed_cbs.gpu_peopsgl.iUseMask, 1),
1279         mee_onoff     ("Opaque Pass",                0, pl_rearmed_cbs.gpu_peopsgl.bOpaquePass, 1),
1280         mee_onoff     ("Advanced Blend",             0, pl_rearmed_cbs.gpu_peopsgl.bAdvancedBlend, 1),
1281         mee_onoff     ("Use Fast Mdec",              0, pl_rearmed_cbs.gpu_peopsgl.bUseFastMdec, 1),
1282         mee_range     ("Texture RAM size (MB)",      0, pl_rearmed_cbs.gpu_peopsgl.iVRamSize, 4, 128),
1283         mee_onoff     ("Texture garbage collection", 0, pl_rearmed_cbs.gpu_peopsgl.iTexGarbageCollection, 1),
1284         mee_label     ("Fixes/hacks:"),
1285         mee_onoff     ("FF7 cursor",                 0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<0),
1286         mee_onoff     ("Direct FB updates",          0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<1),
1287         mee_onoff     ("Black brightness",           0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<2),
1288         mee_onoff     ("Swap front detection",       0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<3),
1289         mee_onoff     ("Disable coord check",        0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<4),
1290         mee_onoff     ("No blue glitches (LoD)",     0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<5),
1291         mee_onoff     ("Soft FB access",             0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<6),
1292         mee_onoff     ("FF9 rect",                   0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<9),
1293         mee_onoff     ("No subtr. blending",         0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<10),
1294         mee_onoff     ("Lazy upload (DW7)",          0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<11),
1295         mee_onoff     ("Additional uploads",         0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<15),
1296         mee_end,
1297 };
1298
1299 static int menu_loop_plugin_gpu_peopsgl(int id, int keys)
1300 {
1301         static int sel = 0;
1302         me_loop(e_menu_plugin_gpu_peopsgl, &sel);
1303         return 0;
1304 }
1305
1306 static const char *men_spu_interp[] = { "None", "Simple", "Gaussian", "Cubic", NULL };
1307 static const char h_spu_volboost[]  = "Large values cause distortion";
1308 static const char h_spu_irq_wait[]  = "Wait for CPU (recommended set to ON)";
1309 static const char h_spu_thread[]    = "Run sound emulation in main thread (recommended)";
1310
1311 static menu_entry e_menu_plugin_spu[] =
1312 {
1313         mee_range_h   ("Volume boost",              0, volume_boost, -5, 30, h_spu_volboost),
1314         mee_onoff     ("Reverb",                    0, iUseReverb, 2),
1315         mee_enum      ("Interpolation",             0, iUseInterpolation, men_spu_interp),
1316         mee_onoff     ("Adjust XA pitch",           0, iXAPitch, 1),
1317         mee_onoff_h   ("SPU IRQ Wait",              0, iSPUIRQWait, 1, h_spu_irq_wait),
1318         mee_onoff_h   ("Sound in main thread",      0, iUseTimer, 2, h_spu_thread),
1319         mee_end,
1320 };
1321
1322 static int menu_loop_plugin_spu(int id, int keys)
1323 {
1324         static int sel = 0;
1325         me_loop(e_menu_plugin_spu, &sel);
1326         return 0;
1327 }
1328
1329 static const char h_bios[]       = "HLE is simulated BIOS. BIOS selection is saved in\n"
1330                                    "savestates and can't be changed there. Must save\n"
1331                                    "config and reload the game for change to take effect";
1332 static const char h_plugin_gpu[] = 
1333 #ifdef __ARM_NEON__
1334                                    "builtin_gpu is the NEON GPU, very fast and accurate\n"
1335                                    "gpuPEOPS "
1336 #else
1337                                    "builtin_gpu "
1338 #endif
1339                                                 "is Pete's soft GPU, slow but accurate\n"
1340                                    "gpuPCSX4ALL is GPU from PCSX4ALL, fast but glitchy\n"
1341                                    "gpuGLES Pete's hw GPU, uses 3D chip but is glitchy\n"
1342                                    "must save config and reload the game if changed";
1343 static const char h_plugin_spu[] = "spunull effectively disables sound\n"
1344                                    "must save config and reload the game if changed";
1345 static const char h_gpu_peops[]  = "Configure P.E.Op.S. SoftGL Driver V1.17";
1346 static const char h_gpu_peopsgl[]= "Configure P.E.Op.S. MesaGL Driver V1.78";
1347 static const char h_gpu_unai[]   = "Configure Unai/PCSX4ALL Team GPU plugin";
1348 static const char h_spu[]        = "Configure built-in P.E.Op.S. Sound Driver V1.7";
1349
1350 static menu_entry e_menu_plugin_options[] =
1351 {
1352         mee_enum_h    ("BIOS",                          0, bios_sel, bioses, h_bios),
1353         mee_enum_h    ("GPU plugin",                    0, gpu_plugsel, gpu_plugins, h_plugin_gpu),
1354         mee_enum_h    ("SPU plugin",                    0, spu_plugsel, spu_plugins, h_plugin_spu),
1355 #ifdef __ARM_NEON__
1356         mee_handler_h ("Configure built-in GPU plugin", menu_loop_plugin_gpu_neon, h_gpu_neon),
1357 #endif
1358         mee_handler_h ("Configure gpu_peops plugin",    menu_loop_plugin_gpu_peops, h_gpu_peops),
1359         mee_handler_h ("Configure PCSX4ALL GPU plugin", menu_loop_plugin_gpu_unai, h_gpu_unai),
1360         mee_handler_h ("Configure GLES GPU plugin",     menu_loop_plugin_gpu_peopsgl, h_gpu_peopsgl),
1361         mee_handler_h ("Configure built-in SPU plugin", menu_loop_plugin_spu, h_spu),
1362         mee_end,
1363 };
1364
1365 static menu_entry e_menu_main2[];
1366
1367 static int menu_loop_plugin_options(int id, int keys)
1368 {
1369         static int sel = 0;
1370         me_loop(e_menu_plugin_options, &sel);
1371
1372         // sync BIOS/plugins
1373         snprintf(Config.Bios, sizeof(Config.Bios), "%s", bioses[bios_sel]);
1374         snprintf(Config.Gpu, sizeof(Config.Gpu), "%s", gpu_plugins[gpu_plugsel]);
1375         snprintf(Config.Spu, sizeof(Config.Spu), "%s", spu_plugins[spu_plugsel]);
1376         me_enable(e_menu_main2, MA_MAIN_RUN_BIOS, bios_sel != 0);
1377
1378         return 0;
1379 }
1380
1381 // ------------ adv options menu ------------
1382
1383 static const char h_cfg_psxclk[]  = "Over/under-clock the PSX, default is " DEFAULT_PSX_CLOCK_S "\n";
1384 static const char h_cfg_nosmc[]   = "Will cause crashes when loading, break memcards";
1385 static const char h_cfg_gteunn[]  = "May cause graphical glitches";
1386 static const char h_cfg_gteflgs[] = "Will cause graphical glitches";
1387
1388 static menu_entry e_menu_speed_hacks[] =
1389 {
1390         mee_range_h   ("PSX CPU clock, %%",        0, psx_clock, 1, 500, h_cfg_psxclk),
1391         mee_onoff_h   ("Disable SMC checks",       0, new_dynarec_hacks, NDHACK_NO_SMC_CHECK, h_cfg_nosmc),
1392         mee_onoff_h   ("Assume GTE regs unneeded", 0, new_dynarec_hacks, NDHACK_GTE_UNNEEDED, h_cfg_gteunn),
1393         mee_onoff_h   ("Disable GTE flags",        0, new_dynarec_hacks, NDHACK_GTE_NO_FLAGS, h_cfg_gteflgs),
1394         mee_end,
1395 };
1396
1397 static int menu_loop_speed_hacks(int id, int keys)
1398 {
1399         static int sel = 0;
1400         me_loop(e_menu_speed_hacks, &sel);
1401         return 0;
1402 }
1403
1404 static const char *men_cfg_cdrr[] = { "Auto", "ON", "OFF", NULL };
1405 static const char h_cfg_cpul[]   = "Shows CPU usage in %";
1406 static const char h_cfg_spu[]    = "Shows active SPU channels\n"
1407                                    "(green: normal, red: fmod, blue: noise)";
1408 static const char h_cfg_fl[]     = "Frame Limiter keeps the game from running too fast";
1409 static const char h_cfg_xa[]     = "Disables XA sound, which can sometimes improve performance";
1410 static const char h_cfg_cdda[]   = "Disable CD Audio for a performance boost\n"
1411                                    "(proper .cue/.bin dump is needed otherwise)";
1412 static const char h_cfg_sio[]    = "You should not need this, breaks games";
1413 static const char h_cfg_spuirq[] = "Compatibility tweak; should be left off";
1414 static const char h_cfg_rcnt1[]  = "Parasite Eve 2, Vandal Hearts 1/2 Fix\n"
1415                                    "(timing hack, breaks other games)";
1416 static const char h_cfg_rcnt2[]  = "InuYasha Sengoku Battle Fix\n"
1417                                    "(timing hack, breaks other games)";
1418 static const char h_cfg_cdrr[]   = "Compatibility tweak (CD timing hack, breaks FMVs)";
1419 static const char h_cfg_nodrc[]  = "Disable dynamic recompiler and use interpreter\n"
1420                                    "Might be useful to overcome some dynarec bugs";
1421 static const char h_cfg_shacks[] = "Breaks games but may give better performance\n"
1422                                    "must reload game for any change to take effect";
1423
1424 static menu_entry e_menu_adv_options[] =
1425 {
1426         mee_onoff_h   ("Show CPU load",          0, g_opts, OPT_SHOWCPU, h_cfg_cpul),
1427         mee_onoff_h   ("Show SPU channels",      0, g_opts, OPT_SHOWSPU, h_cfg_spu),
1428         mee_onoff_h   ("Disable Frame Limiter",  0, g_opts, OPT_NO_FRAMELIM, h_cfg_fl),
1429         mee_onoff_h   ("Disable XA Decoding",    0, Config.Xa, 1, h_cfg_xa),
1430         mee_onoff_h   ("Disable CD Audio",       0, Config.Cdda, 1, h_cfg_cdda),
1431         mee_onoff_h   ("SIO IRQ Always Enabled", 0, Config.Sio, 1, h_cfg_sio),
1432         mee_onoff_h   ("SPU IRQ Always Enabled", 0, Config.SpuIrq, 1, h_cfg_spuirq),
1433         //mee_onoff_h   ("Rootcounter hack",       0, Config.RCntFix, 1, h_cfg_rcnt1),
1434         mee_onoff_h   ("Rootcounter hack 2",     0, Config.VSyncWA, 1, h_cfg_rcnt2),
1435         mee_enum_h    ("CD read reschedule hack",0, Config.CdrReschedule, men_cfg_cdrr, h_cfg_cdrr),
1436         mee_onoff_h   ("Disable dynarec (slow!)",0, Config.Cpu, 1, h_cfg_nodrc),
1437         mee_handler_h ("[Speed hacks]",             menu_loop_speed_hacks, h_cfg_shacks),
1438         mee_end,
1439 };
1440
1441 static int menu_loop_adv_options(int id, int keys)
1442 {
1443         static int sel = 0;
1444         me_loop(e_menu_adv_options, &sel);
1445         return 0;
1446 }
1447
1448 // ------------ options menu ------------
1449
1450 static int mh_restore_defaults(int id, int keys)
1451 {
1452         menu_set_defconfig();
1453         me_update_msg("defaults restored");
1454         return 1;
1455 }
1456
1457 static const char *men_region[]       = { "Auto", "NTSC", "PAL", NULL };
1458 static const char *men_frameskip[]    = { "Auto", "Off", "1", "2", "3", NULL };
1459 /*
1460 static const char *men_confirm_save[] = { "OFF", "writes", "loads", "both", NULL };
1461 static const char h_confirm_save[]    = "Ask for confirmation when overwriting save,\n"
1462                                         "loading state or both";
1463 */
1464 static const char h_restore_def[]     = "Switches back to default / recommended\n"
1465                                         "configuration";
1466 static const char h_frameskip[]       = "Warning: frameskip sometimes causes glitches\n";
1467
1468 static menu_entry e_menu_options[] =
1469 {
1470 //      mee_range     ("Save slot",                0, state_slot, 0, 9),
1471 //      mee_enum_h    ("Confirm savestate",        0, dummy, men_confirm_save, h_confirm_save),
1472         mee_enum_h    ("Frameskip",                0, frameskip, men_frameskip, h_frameskip),
1473         mee_onoff     ("Show FPS",                 0, g_opts, OPT_SHOWFPS),
1474         mee_enum      ("Region",                   0, region, men_region),
1475         mee_range     ("CPU clock",                MA_OPT_CPU_CLOCKS, cpu_clock, 20, 5000),
1476         mee_handler_id("[Display]",                MA_OPT_DISP_OPTS, menu_loop_gfx_options),
1477         mee_handler   ("[BIOS/Plugins]",           menu_loop_plugin_options),
1478         mee_handler   ("[Advanced]",               menu_loop_adv_options),
1479         mee_cust_nosave("Save global config",      MA_OPT_SAVECFG,      mh_savecfg, mgn_saveloadcfg),
1480         mee_cust_nosave("Save cfg for loaded game",MA_OPT_SAVECFG_GAME, mh_savecfg, mgn_saveloadcfg),
1481         mee_handler_h ("Restore default config",   mh_restore_defaults, h_restore_def),
1482         mee_end,
1483 };
1484
1485 static int menu_loop_options(int id, int keys)
1486 {
1487         static int sel = 0;
1488         int i;
1489
1490         i = me_id2offset(e_menu_options, MA_OPT_CPU_CLOCKS);
1491         e_menu_options[i].enabled = cpu_clock != 0 ? 1 : 0;
1492         me_enable(e_menu_options, MA_OPT_SAVECFG_GAME, ready_to_go && CdromId[0]);
1493
1494         me_loop(e_menu_options, &sel);
1495
1496         return 0;
1497 }
1498
1499 // ------------ debug menu ------------
1500
1501 static void draw_frame_debug(GPUFreeze_t *gpuf)
1502 {
1503         int w = min(g_menuscreen_w, 1024);
1504         int h = min(g_menuscreen_h, 512);
1505         u16 *d = g_menuscreen_ptr;
1506         u16 *s = (u16 *)gpuf->psxVRam;
1507         char buff[64];
1508         int ty = 1;
1509
1510         gpuf->ulFreezeVersion = 1;
1511         if (GPU_freeze != NULL)
1512                 GPU_freeze(1, gpuf);
1513
1514         for (; h > 0; h--, d += g_menuscreen_w, s += 1024)
1515                 bgr555_to_rgb565(d, s, w * 2);
1516
1517         smalltext_out16(4, 1, "build: "__DATE__ " " __TIME__ " " REV, 0xe7fc);
1518         snprintf(buff, sizeof(buff), "GPU sr: %08x", gpuf->ulStatus);
1519         smalltext_out16(4, (ty += me_sfont_h), buff, 0xe7fc);
1520         snprintf(buff, sizeof(buff), "PC/SP: %08x %08x", psxRegs.pc, psxRegs.GPR.n.sp);
1521         smalltext_out16(4, (ty += me_sfont_h), buff, 0xe7fc);
1522 }
1523
1524 static void debug_menu_loop(void)
1525 {
1526         GPUFreeze_t *gpuf;
1527         int inp;
1528
1529         gpuf = malloc(sizeof(*gpuf));
1530         if (gpuf == NULL)
1531                 return;
1532
1533         while (1)
1534         {
1535                 menu_draw_begin(0);
1536                 draw_frame_debug(gpuf);
1537                 menu_draw_end();
1538
1539                 inp = in_menu_wait(PBTN_MOK|PBTN_MBACK|PBTN_MA2|PBTN_MA3|PBTN_L|PBTN_R |
1540                                         PBTN_UP|PBTN_DOWN|PBTN_LEFT|PBTN_RIGHT, 70);
1541                 if (inp & PBTN_MBACK)
1542                         break;
1543         }
1544
1545         free(gpuf);
1546 }
1547
1548 // --------- memcard manager ---------
1549
1550 static void draw_mc_icon(int dx, int dy, const u16 *s)
1551 {
1552         u16 *d;
1553         int x, y, l, p;
1554         
1555         d = (u16 *)g_menuscreen_ptr + g_menuscreen_w * dy + dx;
1556
1557         for (y = 0; y < 16; y++, s += 16) {
1558                 for (l = 0; l < 2; l++, d += g_menuscreen_w) {
1559                         for (x = 0; x < 16; x++) {
1560                                 p = s[x];
1561                                 d[x*2] = d[x*2 + 1] = ((p & 0x7c00) >> 10)
1562                                         | ((p & 0x03e0) << 1) | ((p & 0x1f) << 11);
1563                         }
1564                 }
1565         }
1566 }
1567
1568 static void draw_mc_bg(void)
1569 {
1570         McdBlock *blocks1, *blocks2;
1571         int maxicons = 15;
1572         int i, y, row2;
1573
1574         blocks1 = malloc(15 * sizeof(blocks1[0]));
1575         blocks2 = malloc(15 * sizeof(blocks1[0]));
1576         if (blocks1 == NULL || blocks2 == NULL)
1577                 goto out;
1578
1579         for (i = 0; i < 15; i++) {
1580                 GetMcdBlockInfo(1, i + 1, &blocks1[i]);
1581                 GetMcdBlockInfo(2, i + 1, &blocks2[i]);
1582         }
1583
1584         menu_draw_begin(1);
1585
1586         memcpy(g_menuscreen_ptr, g_menubg_src_ptr, g_menuscreen_w * g_menuscreen_h * 2);
1587
1588         y = g_menuscreen_h / 2 - 15 * 32 / 2;
1589         if (y < 0) {
1590                 // doesn't fit..
1591                 y = 0;
1592                 maxicons = g_menuscreen_h / 32;
1593         }
1594
1595         row2 = g_menuscreen_w / 2;
1596         for (i = 0; i < maxicons; i++) {
1597                 draw_mc_icon(8, y + i * 32, (u16 *)blocks1[i].Icon);
1598                 smalltext_out16(10+32, y + i * 32 + 8, blocks1[i].sTitle, 0xf71e);
1599
1600                 draw_mc_icon(row2 + 8, y + i * 32, (u16 *)blocks2[i].Icon);
1601                 smalltext_out16(row2 + 10+32, y + i * 32 + 8, blocks2[i].sTitle, 0xf71e);
1602         }
1603
1604         menu_darken_bg(g_menubg_ptr, g_menuscreen_ptr, g_menuscreen_w * g_menuscreen_h, 0);
1605
1606         menu_draw_end();
1607 out:
1608         free(blocks1);
1609         free(blocks2);
1610 }
1611
1612 static void handle_memcard_sel(void)
1613 {
1614         Config.Mcd1[0] = 0;
1615         if (memcard1_sel != 0)
1616                 snprintf(Config.Mcd1, sizeof(Config.Mcd1), ".%s%s", MEMCARD_DIR, memcards[memcard1_sel]);
1617         Config.Mcd2[0] = 0;
1618         if (memcard2_sel != 0)
1619                 snprintf(Config.Mcd2, sizeof(Config.Mcd2), ".%s%s", MEMCARD_DIR, memcards[memcard2_sel]);
1620         LoadMcds(Config.Mcd1, Config.Mcd2);
1621         draw_mc_bg();
1622 }
1623
1624 static menu_entry e_memcard_options[] =
1625 {
1626         mee_enum("Memory card 1", 0, memcard1_sel, memcards),
1627         mee_enum("Memory card 2", 0, memcard2_sel, memcards),
1628         mee_end,
1629 };
1630
1631 static int menu_loop_memcards(int id, int keys)
1632 {
1633         static int sel = 0;
1634         char *p;
1635         int i;
1636
1637         memcard1_sel = memcard2_sel = 0;
1638         p = strrchr(Config.Mcd1, '/');
1639         if (p != NULL)
1640                 for (i = 0; memcards[i] != NULL; i++)
1641                         if (strcmp(p + 1, memcards[i]) == 0)
1642                                 { memcard1_sel = i; break; }
1643         p = strrchr(Config.Mcd2, '/');
1644         if (p != NULL)
1645                 for (i = 0; memcards[i] != NULL; i++)
1646                         if (strcmp(p + 1, memcards[i]) == 0)
1647                                 { memcard2_sel = i; break; }
1648
1649         me_loop_d(e_memcard_options, &sel, handle_memcard_sel, NULL);
1650
1651         memcpy(g_menubg_ptr, g_menubg_src_ptr, g_menuscreen_w * g_menuscreen_h * 2);
1652
1653         return 0;
1654 }
1655
1656 // --------- main menu help ----------
1657
1658 static void menu_bios_warn(void)
1659 {
1660         int inp;
1661         static const char msg[] =
1662                 "You don't seem to have copied any BIOS\n"
1663                 "files to\n"
1664 #ifdef __ARM_ARCH_7A__ // XXX
1665                 "<SD card>/pandora/appdata/pcsx_rearmed/bios/\n\n"
1666 #else
1667                 "pcsx_rearmed/bios/\n\n"
1668 #endif
1669                 "While many games work fine with fake\n"
1670                 "(HLE) BIOS, others (like MGS and FF8)\n"
1671                 "require BIOS to work.\n"
1672                 "After copying the file, you'll also need\n"
1673                 "to select it in the emu's menu:\n"
1674                 "options->[BIOS/Plugins]\n\n"
1675                 "The file is usually named SCPH1001.BIN,\n"
1676                 "but other not compressed files can be\n"
1677                 "used too.\n\n"
1678                 "Press (B) or (X) to continue";
1679
1680         while (1)
1681         {
1682                 draw_menu_message(msg, NULL);
1683
1684                 inp = in_menu_wait(PBTN_MOK|PBTN_MBACK, 70);
1685                 if (inp & (PBTN_MBACK|PBTN_MOK))
1686                         return;
1687         }
1688 }
1689
1690 // ------------ main menu ------------
1691
1692 void OnFile_Exit();
1693
1694 static void draw_frame_main(void)
1695 {
1696         struct tm *tmp;
1697         time_t ltime;
1698         char ltime_s[16];
1699         char buff[64];
1700
1701         if (CdromId[0] != 0) {
1702                 snprintf(buff, sizeof(buff), "%.32s/%.9s (running as %s, with %s)",
1703                          get_cd_label(), CdromId, Config.PsxType ? "PAL" : "NTSC",
1704                          Config.HLE ? "HLE" : "BIOS");
1705                 smalltext_out16(4, 1, buff, 0x105f);
1706         }
1707
1708         if (ready_to_go) {
1709                 ltime = time(NULL);
1710                 tmp = localtime(&ltime);
1711                 strftime(ltime_s, sizeof(ltime_s), "%H:%M", tmp);
1712                 snprintf(buff, sizeof(buff), "%s %3d%%", ltime_s, plat_get_bat_capacity());
1713                 smalltext_out16(4, 1 + me_sfont_h, buff, 0x105f);
1714         }
1715 }
1716
1717 static void draw_frame_credits(void)
1718 {
1719         smalltext_out16(4, 1, "build: " __DATE__ " " __TIME__ " " REV, 0xe7fc);
1720 }
1721
1722 static const char credits_text[] = 
1723         "PCSX-ReARMed\n\n"
1724         "(C) 1999-2003 PCSX Team\n"
1725         "(C) 2005-2009 PCSX-df Team\n"
1726         "(C) 2009-2011 PCSX-Reloaded Team\n\n"
1727         "ARM recompiler (C) 2009-2011 Ari64\n"
1728 #ifdef __ARM_NEON__
1729         "ARM NEON GPU (c) 2011-2012 Exophase\n"
1730 #endif
1731         "PEOpS GPU and SPU by Pete Bernert\n"
1732         "  and the P.E.Op.S. team\n"
1733         "PCSX4ALL plugin by PCSX4ALL team\n"
1734         "  Chui, Franxis, Unai\n\n"
1735         "integration, optimization and\n"
1736         "  frontend (C) 2010-2012 notaz\n";
1737
1738 static int reset_game(void)
1739 {
1740         // sanity check
1741         if (bios_sel == 0 && !Config.HLE)
1742                 return -1;
1743
1744         ClosePlugins();
1745         OpenPlugins();
1746         SysReset();
1747         if (CheckCdrom() != -1) {
1748                 LoadCdrom();
1749         }
1750         return 0;
1751 }
1752
1753 static int reload_plugins(const char *cdimg)
1754 {
1755         pl_vout_buf = NULL;
1756
1757         ClosePlugins();
1758
1759         set_cd_image(cdimg);
1760         LoadPlugins();
1761         pcnt_hook_plugins();
1762         NetOpened = 0;
1763         if (OpenPlugins() == -1) {
1764                 me_update_msg("failed to open plugins");
1765                 return -1;
1766         }
1767         plugin_call_rearmed_cbs();
1768
1769         cdrIsoMultidiskCount = 1;
1770         CdromId[0] = '\0';
1771         CdromLabel[0] = '\0';
1772
1773         return 0;
1774 }
1775
1776 static int run_bios(void)
1777 {
1778         if (bios_sel == 0)
1779                 return -1;
1780
1781         ready_to_go = 0;
1782         if (reload_plugins(NULL) != 0)
1783                 return -1;
1784         SysReset();
1785
1786         ready_to_go = 1;
1787         return 0;
1788 }
1789
1790 static int run_exe(void)
1791 {
1792         const char *fname;
1793
1794         fname = menu_loop_romsel(last_selected_fname, sizeof(last_selected_fname));
1795         if (fname == NULL)
1796                 return -1;
1797
1798         ready_to_go = 0;
1799         if (reload_plugins(NULL) != 0)
1800                 return -1;
1801
1802         SysReset();
1803         if (Load(fname) != 0) {
1804                 me_update_msg("exe load failed, bad file?");
1805                 printf("meh\n");
1806                 return -1;
1807         }
1808
1809         ready_to_go = 1;
1810         return 0;
1811 }
1812
1813 static int run_cd_image(const char *fname)
1814 {
1815         ready_to_go = 0;
1816         reload_plugins(fname);
1817
1818         // always autodetect, menu_sync_config will override as needed
1819         Config.PsxAuto = 1;
1820
1821         if (CheckCdrom() == -1) {
1822                 // Only check the CD if we are starting the console with a CD
1823                 ClosePlugins();
1824                 me_update_msg("unsupported/invalid CD image");
1825                 return -1;
1826         }
1827
1828         SysReset();
1829
1830         // Read main executable directly from CDRom and start it
1831         if (LoadCdrom() == -1) {
1832                 ClosePlugins();
1833                 me_update_msg("failed to load CD image");
1834                 return -1;
1835         }
1836
1837         ready_to_go = 1;
1838         snprintf(hud_msg, sizeof(hud_msg), "Booting up...");
1839         hud_new_msg = 2;
1840         return 0;
1841 }
1842
1843 static int romsel_run(void)
1844 {
1845         int prev_gpu, prev_spu;
1846         const char *fname;
1847
1848         fname = menu_loop_romsel(last_selected_fname, sizeof(last_selected_fname));
1849         if (fname == NULL)
1850                 return -1;
1851
1852         printf("selected file: %s\n", fname);
1853
1854         new_dynarec_clear_full();
1855
1856         if (run_cd_image(fname) != 0)
1857                 return -1;
1858
1859         prev_gpu = gpu_plugsel;
1860         prev_spu = spu_plugsel;
1861         if (menu_load_config(1) != 0)
1862                 menu_load_config(0);
1863
1864         // check for plugin changes, have to repeat
1865         // loading if game config changed plugins to reload them
1866         if (prev_gpu != gpu_plugsel || prev_spu != spu_plugsel) {
1867                 printf("plugin change detected, reloading plugins..\n");
1868                 if (run_cd_image(fname) != 0)
1869                         return -1;
1870         }
1871
1872         if (Config.HLE)
1873                 printf("note: running without BIOS, expect compatibility problems\n");
1874
1875         strcpy(last_selected_fname, rom_fname_reload);
1876         return 0;
1877 }
1878
1879 static int swap_cd_image(void)
1880 {
1881         char *fname;
1882
1883         fname = menu_loop_romsel(last_selected_fname, sizeof(last_selected_fname));
1884         if (fname == NULL)
1885                 return -1;
1886
1887         printf("selected file: %s\n", fname);
1888
1889         CdromId[0] = '\0';
1890         CdromLabel[0] = '\0';
1891
1892         set_cd_image(fname);
1893         if (ReloadCdromPlugin() < 0) {
1894                 me_update_msg("failed to load cdr plugin");
1895                 return -1;
1896         }
1897         if (CDR_open() < 0) {
1898                 me_update_msg("failed to open cdr plugin");
1899                 return -1;
1900         }
1901
1902         SetCdOpenCaseTime(time(NULL) + 2);
1903         LidInterrupt();
1904
1905         strcpy(last_selected_fname, rom_fname_reload);
1906         return 0;
1907 }
1908
1909 static int swap_cd_multidisk(void)
1910 {
1911         cdrIsoMultidiskSelect++;
1912         CdromId[0] = '\0';
1913         CdromLabel[0] = '\0';
1914
1915         CDR_close();
1916         if (CDR_open() < 0) {
1917                 me_update_msg("failed to open cdr plugin");
1918                 return -1;
1919         }
1920
1921         SetCdOpenCaseTime(time(NULL) + 2);
1922         LidInterrupt();
1923
1924         return 0;
1925 }
1926
1927 static int main_menu_handler(int id, int keys)
1928 {
1929         switch (id)
1930         {
1931         case MA_MAIN_RESUME_GAME:
1932                 if (ready_to_go)
1933                         return 1;
1934                 break;
1935         case MA_MAIN_SAVE_STATE:
1936                 if (ready_to_go)
1937                         return menu_loop_savestate(0);
1938                 break;
1939         case MA_MAIN_LOAD_STATE:
1940                 if (ready_to_go)
1941                         return menu_loop_savestate(1);
1942                 break;
1943         case MA_MAIN_RESET_GAME:
1944                 if (ready_to_go && reset_game() == 0)
1945                         return 1;
1946                 break;
1947         case MA_MAIN_LOAD_ROM:
1948                 if (romsel_run() == 0)
1949                         return 1;
1950                 break;
1951         case MA_MAIN_SWAP_CD:
1952                 if (swap_cd_image() == 0)
1953                         return 1;
1954                 break;
1955         case MA_MAIN_SWAP_CD_MULTI:
1956                 if (swap_cd_multidisk() == 0)
1957                         return 1;
1958                 break;
1959         case MA_MAIN_RUN_BIOS:
1960                 if (run_bios() == 0)
1961                         return 1;
1962                 break;
1963         case MA_MAIN_RUN_EXE:
1964                 if (run_exe() == 0)
1965                         return 1;
1966                 break;
1967         case MA_MAIN_CREDITS:
1968                 draw_menu_message(credits_text, draw_frame_credits);
1969                 in_menu_wait(PBTN_MOK|PBTN_MBACK, 70);
1970                 break;
1971         case MA_MAIN_EXIT:
1972                 OnFile_Exit();
1973                 break;
1974         default:
1975                 lprintf("%s: something unknown selected\n", __FUNCTION__);
1976                 break;
1977         }
1978
1979         return 0;
1980 }
1981
1982 static menu_entry e_menu_main2[] =
1983 {
1984         mee_handler_id("Change CD image",    MA_MAIN_SWAP_CD,       main_menu_handler),
1985         mee_handler_id("Next multidisk CD",  MA_MAIN_SWAP_CD_MULTI, main_menu_handler),
1986         mee_handler_id("Run BIOS",           MA_MAIN_RUN_BIOS,      main_menu_handler),
1987         mee_handler_id("Run EXE",            MA_MAIN_RUN_EXE,       main_menu_handler),
1988         mee_handler   ("Memcard manager",    menu_loop_memcards),
1989         mee_end,
1990 };
1991
1992 static int main_menu2_handler(int id, int keys)
1993 {
1994         static int sel = 0;
1995
1996         me_enable(e_menu_main2, MA_MAIN_SWAP_CD,  ready_to_go);
1997         me_enable(e_menu_main2, MA_MAIN_SWAP_CD_MULTI, ready_to_go && cdrIsoMultidiskCount > 1);
1998         me_enable(e_menu_main2, MA_MAIN_RUN_BIOS, bios_sel != 0);
1999
2000         return me_loop_d(e_menu_main2, &sel, NULL, draw_frame_main);
2001 }
2002
2003 static const char h_extra[] = "Change CD, manage memcards..\n";
2004
2005 static menu_entry e_menu_main[] =
2006 {
2007         mee_label     (""),
2008         mee_label     (""),
2009         mee_handler_id("Resume game",        MA_MAIN_RESUME_GAME, main_menu_handler),
2010         mee_handler_id("Save State",         MA_MAIN_SAVE_STATE,  main_menu_handler),
2011         mee_handler_id("Load State",         MA_MAIN_LOAD_STATE,  main_menu_handler),
2012         mee_handler_id("Reset game",         MA_MAIN_RESET_GAME,  main_menu_handler),
2013         mee_handler_id("Load CD image",      MA_MAIN_LOAD_ROM,    main_menu_handler),
2014         mee_handler   ("Options",            menu_loop_options),
2015         mee_handler   ("Controls",           menu_loop_keyconfig),
2016         mee_handler_h ("Extra stuff",        main_menu2_handler,  h_extra),
2017         mee_handler_id("Credits",            MA_MAIN_CREDITS,     main_menu_handler),
2018         mee_handler_id("Exit",               MA_MAIN_EXIT,        main_menu_handler),
2019         mee_end,
2020 };
2021
2022 // ----------------------------
2023
2024 static void menu_leave_emu(void);
2025
2026 void menu_loop(void)
2027 {
2028         static int sel = 0;
2029
2030         menu_leave_emu();
2031
2032         if (bioses[1] == NULL && !warned_about_bios) {
2033                 menu_bios_warn();
2034                 warned_about_bios = 1;
2035         }
2036
2037         me_enable(e_menu_main, MA_MAIN_RESUME_GAME, ready_to_go);
2038         me_enable(e_menu_main, MA_MAIN_SAVE_STATE,  ready_to_go && CdromId[0]);
2039         me_enable(e_menu_main, MA_MAIN_LOAD_STATE,  ready_to_go && CdromId[0]);
2040         me_enable(e_menu_main, MA_MAIN_RESET_GAME,  ready_to_go);
2041
2042         in_set_config_int(0, IN_CFG_BLOCKING, 1);
2043
2044         do {
2045                 me_loop_d(e_menu_main, &sel, NULL, draw_frame_main);
2046         } while (!ready_to_go);
2047
2048         /* wait until menu, ok, back is released */
2049         while (in_menu_wait_any(50) & (PBTN_MENU|PBTN_MOK|PBTN_MBACK))
2050                 ;
2051
2052         in_set_config_int(0, IN_CFG_BLOCKING, 0);
2053
2054         menu_prepare_emu();
2055 }
2056
2057 static int qsort_strcmp(const void *p1, const void *p2)
2058 {
2059         char * const *s1 = (char * const *)p1;
2060         char * const *s2 = (char * const *)p2;
2061         return strcasecmp(*s1, *s2);
2062 }
2063
2064 static void scan_bios_plugins(void)
2065 {
2066         char fname[MAXPATHLEN];
2067         struct dirent *ent;
2068         int bios_i, gpu_i, spu_i, mc_i;
2069         char *p;
2070         DIR *dir;
2071
2072         bioses[0] = "HLE";
2073         gpu_plugins[0] = "builtin_gpu";
2074         spu_plugins[0] = "builtin_spu";
2075         memcards[0] = "(none)";
2076         bios_i = gpu_i = spu_i = mc_i = 1;
2077
2078         snprintf(fname, sizeof(fname), "%s/", Config.BiosDir);
2079         dir = opendir(fname);
2080         if (dir == NULL) {
2081                 perror("scan_bios_plugins bios opendir");
2082                 goto do_plugins;
2083         }
2084
2085         while (1) {
2086                 struct stat st;
2087
2088                 errno = 0;
2089                 ent = readdir(dir);
2090                 if (ent == NULL) {
2091                         if (errno != 0)
2092                                 perror("readdir");
2093                         break;
2094                 }
2095
2096                 if (ent->d_type != DT_REG && ent->d_type != DT_LNK)
2097                         continue;
2098
2099                 snprintf(fname, sizeof(fname), "%s/%s", Config.BiosDir, ent->d_name);
2100                 if (stat(fname, &st) != 0 || st.st_size != 512*1024) {
2101                         printf("bad BIOS file: %s\n", ent->d_name);
2102                         continue;
2103                 }
2104
2105                 if (bios_i < ARRAY_SIZE(bioses) - 1) {
2106                         bioses[bios_i++] = strdup(ent->d_name);
2107                         continue;
2108                 }
2109
2110                 printf("too many BIOSes, dropping \"%s\"\n", ent->d_name);
2111         }
2112
2113         closedir(dir);
2114
2115 do_plugins:
2116         snprintf(fname, sizeof(fname), "%s/", Config.PluginsDir);
2117         dir = opendir(fname);
2118         if (dir == NULL) {
2119                 perror("scan_bios_plugins plugins opendir");
2120                 goto do_memcards;
2121         }
2122
2123         while (1) {
2124                 void *h, *tmp;
2125
2126                 errno = 0;
2127                 ent = readdir(dir);
2128                 if (ent == NULL) {
2129                         if (errno != 0)
2130                                 perror("readdir");
2131                         break;
2132                 }
2133                 p = strstr(ent->d_name, ".so");
2134                 if (p == NULL)
2135                         continue;
2136
2137                 snprintf(fname, sizeof(fname), "%s/%s", Config.PluginsDir, ent->d_name);
2138                 h = dlopen(fname, RTLD_LAZY | RTLD_LOCAL);
2139                 if (h == NULL) {
2140                         fprintf(stderr, "%s\n", dlerror());
2141                         continue;
2142                 }
2143
2144                 // now what do we have here?
2145                 tmp = dlsym(h, "GPUinit");
2146                 if (tmp) {
2147                         dlclose(h);
2148                         if (gpu_i < ARRAY_SIZE(gpu_plugins) - 1)
2149                                 gpu_plugins[gpu_i++] = strdup(ent->d_name);
2150                         continue;
2151                 }
2152
2153                 tmp = dlsym(h, "SPUinit");
2154                 if (tmp) {
2155                         dlclose(h);
2156                         if (spu_i < ARRAY_SIZE(spu_plugins) - 1)
2157                                 spu_plugins[spu_i++] = strdup(ent->d_name);
2158                         continue;
2159                 }
2160
2161                 fprintf(stderr, "ignoring unidentified plugin: %s\n", fname);
2162                 dlclose(h);
2163         }
2164
2165         closedir(dir);
2166
2167 do_memcards:
2168         dir = opendir("." MEMCARD_DIR);
2169         if (dir == NULL) {
2170                 perror("scan_bios_plugins memcards opendir");
2171                 return;
2172         }
2173
2174         while (1) {
2175                 struct stat st;
2176
2177                 errno = 0;
2178                 ent = readdir(dir);
2179                 if (ent == NULL) {
2180                         if (errno != 0)
2181                                 perror("readdir");
2182                         break;
2183                 }
2184
2185                 if (ent->d_type != DT_REG && ent->d_type != DT_LNK)
2186                         continue;
2187
2188                 snprintf(fname, sizeof(fname), "." MEMCARD_DIR "%s", ent->d_name);
2189                 if (stat(fname, &st) != 0) {
2190                         printf("bad memcard file: %s\n", ent->d_name);
2191                         continue;
2192                 }
2193
2194                 if (mc_i < ARRAY_SIZE(memcards) - 1) {
2195                         memcards[mc_i++] = strdup(ent->d_name);
2196                         continue;
2197                 }
2198
2199                 printf("too many memcards, dropping \"%s\"\n", ent->d_name);
2200         }
2201
2202         if (mc_i > 2)
2203                 qsort(memcards + 1, mc_i - 1, sizeof(memcards[0]), qsort_strcmp);
2204
2205         closedir(dir);
2206 }
2207
2208 void menu_init(void)
2209 {
2210         char buff[MAXPATHLEN];
2211
2212         strcpy(last_selected_fname, "/media");
2213
2214         scan_bios_plugins();
2215         pnd_menu_init();
2216         menu_init_common();
2217
2218         menu_set_defconfig();
2219         menu_load_config(0);
2220         last_psx_w = 320;
2221         last_psx_h = 240;
2222         last_psx_bpp = 16;
2223
2224         g_menubg_src_ptr = calloc(g_menuscreen_w * g_menuscreen_h * 2, 1);
2225         if (g_menubg_src_ptr == NULL)
2226                 exit(1);
2227         emu_make_path(buff, "skin/background.png", sizeof(buff));
2228         readpng(g_menubg_src_ptr, buff, READPNG_BG, g_menuscreen_w, g_menuscreen_h);
2229
2230 #ifndef __ARM_ARCH_7A__ /* XXX */
2231         me_enable(e_menu_options, MA_OPT_DISP_OPTS, 0);
2232         me_enable(e_menu_keyconfig, MA_CTRL_NUBS_BTNS, 0);
2233 #else
2234         me_enable(e_menu_keyconfig, MA_CTRL_VIBRATION, 0);
2235         me_enable(e_menu_keyconfig, MA_CTRL_DEADZONE, 0);
2236 #endif
2237 }
2238
2239 void menu_notify_mode_change(int w, int h, int bpp)
2240 {
2241         float mult;
2242         int imult;
2243
2244         last_psx_w = w;
2245         last_psx_h = h;
2246         last_psx_bpp = bpp;
2247
2248         switch (scaling) {
2249         case SCALE_1_1:
2250                 g_layer_w = w; g_layer_h = h;
2251                 break;
2252
2253         case SCALE_4_3v2:
2254                 if (h > g_menuscreen_h || (240 < h && h <= 360))
2255                         goto fractional_4_3;
2256
2257                 // 4:3 that prefers integer scaling
2258                 imult = g_menuscreen_h / h;
2259                 g_layer_w = w * imult;
2260                 g_layer_h = h * imult;
2261                 mult = (float)g_layer_w / (float)g_layer_h;
2262                 if (mult < 1.25f || mult > 1.666f)
2263                         g_layer_w = 4.0f/3.0f * (float)g_layer_h;
2264                 printf("  -> %dx%d %.1f\n", g_layer_w, g_layer_h, mult);
2265                 break;
2266
2267         fractional_4_3:
2268         case SCALE_4_3:
2269                 mult = 240.0f / (float)h * 4.0f / 3.0f;
2270                 if (h > 256)
2271                         mult *= 2.0f;
2272                 g_layer_w = mult * (float)g_menuscreen_h;
2273                 g_layer_h = g_menuscreen_h;
2274                 printf("  -> %dx%d %.1f\n", g_layer_w, g_layer_h, mult);
2275                 break;
2276
2277         case SCALE_FULLSCREEN:
2278                 g_layer_w = g_menuscreen_w;
2279                 g_layer_h = g_menuscreen_h;
2280                 break;
2281
2282         default:
2283                 break;
2284         }
2285
2286         g_layer_x = g_menuscreen_w / 2 - g_layer_w / 2;
2287         g_layer_y = g_menuscreen_h / 2 - g_layer_h / 2;
2288         if (g_layer_x < 0) g_layer_x = 0;
2289         if (g_layer_y < 0) g_layer_y = 0;
2290         if (g_layer_w > g_menuscreen_w) g_layer_w = g_menuscreen_w;
2291         if (g_layer_h > g_menuscreen_h) g_layer_w = g_menuscreen_h;
2292 }
2293
2294 static void menu_leave_emu(void)
2295 {
2296         if (GPU_close != NULL) {
2297                 int ret = GPU_close();
2298                 if (ret)
2299                         fprintf(stderr, "Warning: GPU_close returned %d\n", ret);
2300         }
2301
2302         plat_video_menu_enter(ready_to_go);
2303
2304         memcpy(g_menubg_ptr, g_menubg_src_ptr, g_menuscreen_w * g_menuscreen_h * 2);
2305         if (pl_vout_buf != NULL && ready_to_go && last_psx_bpp == 16) {
2306                 int x = max(0, g_menuscreen_w - last_psx_w);
2307                 int y = max(0, g_menuscreen_h / 2 - last_psx_h / 2);
2308                 int w = min(g_menuscreen_w, last_psx_w);
2309                 int h = min(g_menuscreen_h, last_psx_h);
2310                 u16 *d = (u16 *)g_menubg_ptr + g_menuscreen_w * y + x;
2311                 u16 *s = pl_vout_buf;
2312
2313                 for (; h > 0; h--, d += g_menuscreen_w, s += last_psx_w)
2314                         menu_darken_bg(d, s, w, 0);
2315         }
2316
2317         if (ready_to_go)
2318                 cpu_clock = plat_cpu_clock_get();
2319 }
2320
2321 void menu_prepare_emu(void)
2322 {
2323         R3000Acpu *prev_cpu = psxCpu;
2324
2325         plat_video_menu_leave();
2326
2327         menu_notify_mode_change(last_psx_w, last_psx_h, last_psx_bpp);
2328
2329         psxCpu = (Config.Cpu == CPU_INTERPRETER) ? &psxInt : &psxRec;
2330         if (psxCpu != prev_cpu)
2331                 // note that this does not really reset, just clears drc caches
2332                 psxCpu->Reset();
2333
2334         // core doesn't care about Config.Cdda changes,
2335         // so handle them manually here
2336         if (Config.Cdda)
2337                 CDR_stop();
2338
2339         menu_sync_config();
2340         apply_lcdrate(Config.PsxType);
2341         apply_filter(filter);
2342         plat_cpu_clock_apply(cpu_clock);
2343
2344         // push config to GPU plugin
2345         plugin_call_rearmed_cbs();
2346
2347         if (GPU_open != NULL) {
2348                 int ret = GPU_open(&gpuDisp, "PCSX", NULL);
2349                 if (ret)
2350                         fprintf(stderr, "Warning: GPU_open returned %d\n", ret);
2351         }
2352
2353         dfinput_activate();
2354 }
2355
2356 void me_update_msg(const char *msg)
2357 {
2358         strncpy(menu_error_msg, msg, sizeof(menu_error_msg));
2359         menu_error_msg[sizeof(menu_error_msg) - 1] = 0;
2360
2361         menu_error_time = plat_get_ticks_ms();
2362         lprintf("msg: %s\n", menu_error_msg);
2363 }
2364