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