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