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