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