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