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