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