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