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