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