release r15
[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         return 0;
1863 }
1864
1865 static int swap_cd_image(void)
1866 {
1867         char *fname;
1868
1869         fname = menu_loop_romsel(last_selected_fname, sizeof(last_selected_fname));
1870         if (fname == NULL)
1871                 return -1;
1872
1873         printf("selected file: %s\n", fname);
1874
1875         CdromId[0] = '\0';
1876         CdromLabel[0] = '\0';
1877
1878         set_cd_image(fname);
1879         if (ReloadCdromPlugin() < 0) {
1880                 me_update_msg("failed to load cdr plugin");
1881                 return -1;
1882         }
1883         if (CDR_open() < 0) {
1884                 me_update_msg("failed to open cdr plugin");
1885                 return -1;
1886         }
1887
1888         SetCdOpenCaseTime(time(NULL) + 2);
1889         LidInterrupt();
1890
1891         strcpy(last_selected_fname, rom_fname_reload);
1892         return 0;
1893 }
1894
1895 static int swap_cd_multidisk(void)
1896 {
1897         cdrIsoMultidiskSelect++;
1898         CdromId[0] = '\0';
1899         CdromLabel[0] = '\0';
1900
1901         CDR_close();
1902         if (CDR_open() < 0) {
1903                 me_update_msg("failed to open cdr plugin");
1904                 return -1;
1905         }
1906
1907         SetCdOpenCaseTime(time(NULL) + 2);
1908         LidInterrupt();
1909
1910         return 0;
1911 }
1912
1913 static void load_pcsx_cht(void)
1914 {
1915         char path[256];
1916         char *fname;
1917
1918         path[0] = 0;
1919         fname = menu_loop_romsel(path, sizeof(path));
1920         if (fname == NULL)
1921                 return;
1922
1923         printf("selected cheat file: %s\n", fname);
1924         LoadCheats(fname);
1925
1926         if (NumCheats == 0 && NumCodes == 0)
1927                 me_update_msg("failed to load cheats");
1928         else {
1929                 snprintf(path, sizeof(path), "%d cheat(s) loaded", NumCheats + NumCodes);
1930                 me_update_msg(path);
1931         }
1932         me_enable(e_menu_main, MA_MAIN_CHEATS, ready_to_go && NumCheats);
1933 }
1934
1935 static int main_menu_handler(int id, int keys)
1936 {
1937         switch (id)
1938         {
1939         case MA_MAIN_RESUME_GAME:
1940                 if (ready_to_go)
1941                         return 1;
1942                 break;
1943         case MA_MAIN_SAVE_STATE:
1944                 if (ready_to_go)
1945                         return menu_loop_savestate(0);
1946                 break;
1947         case MA_MAIN_LOAD_STATE:
1948                 if (ready_to_go)
1949                         return menu_loop_savestate(1);
1950                 break;
1951         case MA_MAIN_RESET_GAME:
1952                 if (ready_to_go && reset_game() == 0)
1953                         return 1;
1954                 break;
1955         case MA_MAIN_LOAD_ROM:
1956                 if (romsel_run() == 0)
1957                         return 1;
1958                 break;
1959         case MA_MAIN_SWAP_CD:
1960                 if (swap_cd_image() == 0)
1961                         return 1;
1962                 break;
1963         case MA_MAIN_SWAP_CD_MULTI:
1964                 if (swap_cd_multidisk() == 0)
1965                         return 1;
1966                 break;
1967         case MA_MAIN_RUN_BIOS:
1968                 if (run_bios() == 0)
1969                         return 1;
1970                 break;
1971         case MA_MAIN_RUN_EXE:
1972                 if (run_exe() == 0)
1973                         return 1;
1974                 break;
1975         case MA_MAIN_CHEATS:
1976                 menu_loop_cheats();
1977                 break;
1978         case MA_MAIN_LOAD_CHEATS:
1979                 load_pcsx_cht();
1980                 break;
1981         case MA_MAIN_CREDITS:
1982                 draw_menu_message(credits_text, draw_frame_credits);
1983                 in_menu_wait(PBTN_MOK|PBTN_MBACK, NULL, 70);
1984                 break;
1985         case MA_MAIN_EXIT:
1986                 OnFile_Exit();
1987                 break;
1988         default:
1989                 lprintf("%s: something unknown selected\n", __FUNCTION__);
1990                 break;
1991         }
1992
1993         return 0;
1994 }
1995
1996 static menu_entry e_menu_main2[] =
1997 {
1998         mee_handler_id("Change CD image",    MA_MAIN_SWAP_CD,       main_menu_handler),
1999         mee_handler_id("Next multidisk CD",  MA_MAIN_SWAP_CD_MULTI, main_menu_handler),
2000         mee_handler_id("Run BIOS",           MA_MAIN_RUN_BIOS,      main_menu_handler),
2001         mee_handler_id("Run EXE",            MA_MAIN_RUN_EXE,       main_menu_handler),
2002         mee_handler   ("Memcard manager",    menu_loop_memcards),
2003         mee_handler_id("Load PCSX cheats..", MA_MAIN_LOAD_CHEATS,   main_menu_handler),
2004         mee_end,
2005 };
2006
2007 static int main_menu2_handler(int id, int keys)
2008 {
2009         static int sel = 0;
2010
2011         me_enable(e_menu_main2, MA_MAIN_SWAP_CD,  ready_to_go);
2012         me_enable(e_menu_main2, MA_MAIN_SWAP_CD_MULTI, ready_to_go && cdrIsoMultidiskCount > 1);
2013         me_enable(e_menu_main2, MA_MAIN_RUN_BIOS, bios_sel != 0);
2014         me_enable(e_menu_main2, MA_MAIN_LOAD_CHEATS, ready_to_go);
2015
2016         return me_loop_d(e_menu_main2, &sel, NULL, draw_frame_main);
2017 }
2018
2019 static const char h_extra[] = "Change CD, manage memcards..\n";
2020
2021 static menu_entry e_menu_main[] =
2022 {
2023         mee_label     (""),
2024         mee_label     (""),
2025         mee_handler_id("Resume game",        MA_MAIN_RESUME_GAME, main_menu_handler),
2026         mee_handler_id("Save State",         MA_MAIN_SAVE_STATE,  main_menu_handler),
2027         mee_handler_id("Load State",         MA_MAIN_LOAD_STATE,  main_menu_handler),
2028         mee_handler_id("Reset game",         MA_MAIN_RESET_GAME,  main_menu_handler),
2029         mee_handler_id("Load CD image",      MA_MAIN_LOAD_ROM,    main_menu_handler),
2030         mee_handler   ("Options",            menu_loop_options),
2031         mee_handler   ("Controls",           menu_loop_keyconfig),
2032         mee_handler_id("Cheats",             MA_MAIN_CHEATS,      main_menu_handler),
2033         mee_handler_h ("Extra stuff",        main_menu2_handler,  h_extra),
2034         mee_handler_id("Credits",            MA_MAIN_CREDITS,     main_menu_handler),
2035         mee_handler_id("Exit",               MA_MAIN_EXIT,        main_menu_handler),
2036         mee_end,
2037 };
2038
2039 // ----------------------------
2040
2041 static void menu_leave_emu(void);
2042
2043 void menu_loop(void)
2044 {
2045         static int sel = 0;
2046
2047         menu_leave_emu();
2048
2049         if (bioses[1] == NULL && !warned_about_bios) {
2050                 menu_bios_warn();
2051                 warned_about_bios = 1;
2052         }
2053
2054         me_enable(e_menu_main, MA_MAIN_RESUME_GAME, ready_to_go);
2055         me_enable(e_menu_main, MA_MAIN_SAVE_STATE,  ready_to_go && CdromId[0]);
2056         me_enable(e_menu_main, MA_MAIN_LOAD_STATE,  ready_to_go && CdromId[0]);
2057         me_enable(e_menu_main, MA_MAIN_RESET_GAME,  ready_to_go);
2058         me_enable(e_menu_main, MA_MAIN_CHEATS,      ready_to_go && NumCheats);
2059
2060         in_set_config_int(0, IN_CFG_BLOCKING, 1);
2061
2062         do {
2063                 me_loop_d(e_menu_main, &sel, NULL, draw_frame_main);
2064         } while (!ready_to_go);
2065
2066         /* wait until menu, ok, back is released */
2067         while (in_menu_wait_any(NULL, 50) & (PBTN_MENU|PBTN_MOK|PBTN_MBACK))
2068                 ;
2069
2070         in_set_config_int(0, IN_CFG_BLOCKING, 0);
2071
2072         menu_prepare_emu();
2073 }
2074
2075 static int qsort_strcmp(const void *p1, const void *p2)
2076 {
2077         char * const *s1 = (char * const *)p1;
2078         char * const *s2 = (char * const *)p2;
2079         return strcasecmp(*s1, *s2);
2080 }
2081
2082 static void scan_bios_plugins(void)
2083 {
2084         char fname[MAXPATHLEN];
2085         struct dirent *ent;
2086         int bios_i, gpu_i, spu_i, mc_i;
2087         char *p;
2088         DIR *dir;
2089
2090         bioses[0] = "HLE";
2091         gpu_plugins[0] = "builtin_gpu";
2092         spu_plugins[0] = "builtin_spu";
2093         memcards[0] = "(none)";
2094         bios_i = gpu_i = spu_i = mc_i = 1;
2095
2096         snprintf(fname, sizeof(fname), "%s/", Config.BiosDir);
2097         dir = opendir(fname);
2098         if (dir == NULL) {
2099                 perror("scan_bios_plugins bios opendir");
2100                 goto do_plugins;
2101         }
2102
2103         while (1) {
2104                 struct stat st;
2105
2106                 errno = 0;
2107                 ent = readdir(dir);
2108                 if (ent == NULL) {
2109                         if (errno != 0)
2110                                 perror("readdir");
2111                         break;
2112                 }
2113
2114                 if (ent->d_type != DT_REG && ent->d_type != DT_LNK)
2115                         continue;
2116
2117                 snprintf(fname, sizeof(fname), "%s/%s", Config.BiosDir, ent->d_name);
2118                 if (stat(fname, &st) != 0 || st.st_size != 512*1024) {
2119                         printf("bad BIOS file: %s\n", ent->d_name);
2120                         continue;
2121                 }
2122
2123                 if (bios_i < ARRAY_SIZE(bioses) - 1) {
2124                         bioses[bios_i++] = strdup(ent->d_name);
2125                         continue;
2126                 }
2127
2128                 printf("too many BIOSes, dropping \"%s\"\n", ent->d_name);
2129         }
2130
2131         closedir(dir);
2132
2133 do_plugins:
2134         snprintf(fname, sizeof(fname), "%s/", Config.PluginsDir);
2135         dir = opendir(fname);
2136         if (dir == NULL) {
2137                 perror("scan_bios_plugins plugins opendir");
2138                 goto do_memcards;
2139         }
2140
2141         while (1) {
2142                 void *h, *tmp;
2143
2144                 errno = 0;
2145                 ent = readdir(dir);
2146                 if (ent == NULL) {
2147                         if (errno != 0)
2148                                 perror("readdir");
2149                         break;
2150                 }
2151                 p = strstr(ent->d_name, ".so");
2152                 if (p == NULL)
2153                         continue;
2154
2155                 snprintf(fname, sizeof(fname), "%s/%s", Config.PluginsDir, ent->d_name);
2156                 h = dlopen(fname, RTLD_LAZY | RTLD_LOCAL);
2157                 if (h == NULL) {
2158                         fprintf(stderr, "%s\n", dlerror());
2159                         continue;
2160                 }
2161
2162                 // now what do we have here?
2163                 tmp = dlsym(h, "GPUinit");
2164                 if (tmp) {
2165                         dlclose(h);
2166                         if (gpu_i < ARRAY_SIZE(gpu_plugins) - 1)
2167                                 gpu_plugins[gpu_i++] = strdup(ent->d_name);
2168                         continue;
2169                 }
2170
2171                 tmp = dlsym(h, "SPUinit");
2172                 if (tmp) {
2173                         dlclose(h);
2174                         if (spu_i < ARRAY_SIZE(spu_plugins) - 1)
2175                                 spu_plugins[spu_i++] = strdup(ent->d_name);
2176                         continue;
2177                 }
2178
2179                 fprintf(stderr, "ignoring unidentified plugin: %s\n", fname);
2180                 dlclose(h);
2181         }
2182
2183         closedir(dir);
2184
2185 do_memcards:
2186         dir = opendir("." MEMCARD_DIR);
2187         if (dir == NULL) {
2188                 perror("scan_bios_plugins memcards opendir");
2189                 return;
2190         }
2191
2192         while (1) {
2193                 struct stat st;
2194
2195                 errno = 0;
2196                 ent = readdir(dir);
2197                 if (ent == NULL) {
2198                         if (errno != 0)
2199                                 perror("readdir");
2200                         break;
2201                 }
2202
2203                 if (ent->d_type != DT_REG && ent->d_type != DT_LNK)
2204                         continue;
2205
2206                 snprintf(fname, sizeof(fname), "." MEMCARD_DIR "%s", ent->d_name);
2207                 if (stat(fname, &st) != 0) {
2208                         printf("bad memcard file: %s\n", ent->d_name);
2209                         continue;
2210                 }
2211
2212                 if (mc_i < ARRAY_SIZE(memcards) - 1) {
2213                         memcards[mc_i++] = strdup(ent->d_name);
2214                         continue;
2215                 }
2216
2217                 printf("too many memcards, dropping \"%s\"\n", ent->d_name);
2218         }
2219
2220         if (mc_i > 2)
2221                 qsort(memcards + 1, mc_i - 1, sizeof(memcards[0]), qsort_strcmp);
2222
2223         closedir(dir);
2224 }
2225
2226 void menu_init(void)
2227 {
2228         char buff[MAXPATHLEN];
2229
2230         strcpy(last_selected_fname, "/media");
2231
2232         cpu_clock_st = cpu_clock = plat_cpu_clock_get();
2233
2234         scan_bios_plugins();
2235         menu_init_common();
2236
2237         menu_set_defconfig();
2238         menu_load_config(0);
2239         menu_do_last_cd_img(1);
2240         last_vout_w = 320;
2241         last_vout_h = 240;
2242         last_vout_bpp = 16;
2243
2244         g_menubg_src_ptr = calloc(g_menuscreen_w * g_menuscreen_h * 2, 1);
2245         g_menubg_ptr = calloc(g_menuscreen_w * g_menuscreen_h * 2, 1);
2246         if (g_menubg_src_ptr == NULL || g_menubg_ptr == NULL) {
2247                 fprintf(stderr, "OOM\n");
2248                 exit(1);
2249         }
2250
2251         emu_make_path(buff, "skin/background.png", sizeof(buff));
2252         readpng(g_menubg_src_ptr, buff, READPNG_BG, g_menuscreen_w, g_menuscreen_h);
2253
2254 #ifndef __ARM_ARCH_7A__ /* XXX */
2255         me_enable(e_menu_gfx_options, MA_OPT_SCALER, 0);
2256         me_enable(e_menu_gfx_options, MA_OPT_FILTERING, 0);
2257         me_enable(e_menu_gfx_options, MA_OPT_SCALER_C, 0);
2258         me_enable(e_menu_keyconfig, MA_CTRL_NUBS_BTNS, 0);
2259 #else
2260         me_enable(e_menu_gfx_options, MA_OPT_SCALER2, 0);
2261         me_enable(e_menu_keyconfig, MA_CTRL_VIBRATION, 0);
2262         me_enable(e_menu_keyconfig, MA_CTRL_DEADZONE, 0);
2263 #endif
2264 }
2265
2266 void menu_notify_mode_change(int w, int h, int bpp)
2267 {
2268         float mult;
2269         int imult;
2270
2271         last_vout_w = w;
2272         last_vout_h = h;
2273         last_vout_bpp = bpp;
2274
2275         // XXX: should really menu code cotrol the layer size?
2276         switch (scaling) {
2277         case SCALE_1_1:
2278                 g_layer_w = w; g_layer_h = h;
2279                 break;
2280
2281         case SCALE_4_3v2:
2282                 if (h > g_menuscreen_h || (240 < h && h <= 360))
2283                         goto fractional_4_3;
2284
2285                 // 4:3 that prefers integer scaling
2286                 imult = g_menuscreen_h / h;
2287                 g_layer_w = w * imult;
2288                 g_layer_h = h * imult;
2289                 mult = (float)g_layer_w / (float)g_layer_h;
2290                 if (mult < 1.25f || mult > 1.666f)
2291                         g_layer_w = 4.0f/3.0f * (float)g_layer_h;
2292                 printf("  -> %dx%d %.1f\n", g_layer_w, g_layer_h, mult);
2293                 break;
2294
2295         fractional_4_3:
2296         case SCALE_4_3:
2297                 mult = 240.0f / (float)h * 4.0f / 3.0f;
2298                 if (h > 256)
2299                         mult *= 2.0f;
2300                 g_layer_w = mult * (float)g_menuscreen_h;
2301                 g_layer_h = g_menuscreen_h;
2302                 printf("  -> %dx%d %.1f\n", g_layer_w, g_layer_h, mult);
2303                 break;
2304
2305         case SCALE_FULLSCREEN:
2306                 g_layer_w = g_menuscreen_w;
2307                 g_layer_h = g_menuscreen_h;
2308                 break;
2309
2310         default:
2311                 break;
2312         }
2313
2314         g_layer_x = g_menuscreen_w / 2 - g_layer_w / 2;
2315         g_layer_y = g_menuscreen_h / 2 - g_layer_h / 2;
2316         if (g_layer_x < 0) g_layer_x = 0;
2317         if (g_layer_y < 0) g_layer_y = 0;
2318         if (g_layer_w > g_menuscreen_w) g_layer_w = g_menuscreen_w;
2319         if (g_layer_h > g_menuscreen_h) g_layer_w = g_menuscreen_h;
2320 }
2321
2322 static void menu_leave_emu(void)
2323 {
2324         if (GPU_close != NULL) {
2325                 int ret = GPU_close();
2326                 if (ret)
2327                         fprintf(stderr, "Warning: GPU_close returned %d\n", ret);
2328         }
2329
2330         plat_video_menu_enter(ready_to_go);
2331
2332         memcpy(g_menubg_ptr, g_menubg_src_ptr, g_menuscreen_w * g_menuscreen_h * 2);
2333         if (pl_vout_buf != NULL && ready_to_go) {
2334                 int x = max(0, g_menuscreen_w - last_vout_w);
2335                 int y = max(0, g_menuscreen_h / 2 - last_vout_h / 2);
2336                 int w = min(g_menuscreen_w, last_vout_w);
2337                 int h = min(g_menuscreen_h, last_vout_h);
2338                 u16 *d = (u16 *)g_menubg_ptr + g_menuscreen_w * y + x;
2339                 char *s = pl_vout_buf;
2340
2341                 if (last_vout_bpp == 16) {
2342                         for (; h > 0; h--, d += g_menuscreen_w, s += last_vout_w * 2)
2343                                 menu_darken_bg(d, s, w, 0);
2344                 }
2345                 else {
2346                         for (; h > 0; h--, d += g_menuscreen_w, s += last_vout_w * 3) {
2347                                 rgb888_to_rgb565(d, s, w * 3);
2348                                 menu_darken_bg(d, d, w, 0);
2349                         }
2350                 }
2351         }
2352
2353         if (ready_to_go)
2354                 cpu_clock = plat_cpu_clock_get();
2355 }
2356
2357 void menu_prepare_emu(void)
2358 {
2359         R3000Acpu *prev_cpu = psxCpu;
2360
2361         plat_video_menu_leave();
2362
2363         menu_notify_mode_change(last_vout_w, last_vout_h, last_vout_bpp);
2364
2365         psxCpu = (Config.Cpu == CPU_INTERPRETER) ? &psxInt : &psxRec;
2366         if (psxCpu != prev_cpu)
2367                 // note that this does not really reset, just clears drc caches
2368                 psxCpu->Reset();
2369
2370         // core doesn't care about Config.Cdda changes,
2371         // so handle them manually here
2372         if (Config.Cdda)
2373                 CDR_stop();
2374
2375         menu_sync_config();
2376         if (cpu_clock > 0)
2377                 plat_cpu_clock_apply(cpu_clock);
2378
2379         // push config to GPU plugin
2380         plugin_call_rearmed_cbs();
2381
2382         if (GPU_open != NULL) {
2383                 int ret = GPU_open(&gpuDisp, "PCSX", NULL);
2384                 if (ret)
2385                         fprintf(stderr, "Warning: GPU_open returned %d\n", ret);
2386         }
2387
2388         dfinput_activate();
2389 }
2390
2391 void me_update_msg(const char *msg)
2392 {
2393         strncpy(menu_error_msg, msg, sizeof(menu_error_msg));
2394         menu_error_msg[sizeof(menu_error_msg) - 1] = 0;
2395
2396         menu_error_time = plat_get_ticks_ms();
2397         lprintf("msg: %s\n", menu_error_msg);
2398 }
2399
2400 void menu_finish(void)
2401 {
2402         menu_do_last_cd_img(0);
2403         plat_cpu_clock_apply(cpu_clock_st);
2404 }