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