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