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