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