automatically build plugins
[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 (B) or (X) to continue";
1702
1703 while (1)
1704 {
1705 draw_menu_message(msg, NULL);
1706
1707 inp = in_menu_wait(PBTN_MOK|PBTN_MBACK, 70);
1708 if (inp & (PBTN_MBACK|PBTN_MOK))
1709 return;
1710 }
1711}
1712
1713// ------------ main menu ------------
1714
1715void OnFile_Exit();
1716
1717static void draw_frame_main(void)
1718{
1719 struct tm *tmp;
1720 time_t ltime;
1721 int capacity;
1722 char ltime_s[16];
1723 char buff[64];
1724 char *out;
1725
1726 if (CdromId[0] != 0) {
1727 snprintf(buff, sizeof(buff), "%.32s/%.9s (running as %s, with %s)",
1728 get_cd_label(), CdromId, Config.PsxType ? "PAL" : "NTSC",
1729 Config.HLE ? "HLE" : "BIOS");
1730 smalltext_out16(4, 1, buff, 0x105f);
1731 }
1732
1733 if (ready_to_go) {
1734 capacity = plat_get_bat_capacity();
1735 ltime = time(NULL);
1736 tmp = localtime(&ltime);
1737 strftime(ltime_s, sizeof(ltime_s), "%H:%M", tmp);
1738 if (capacity >= 0) {
1739 snprintf(buff, sizeof(buff), "%s %3d%%", ltime_s, capacity);
1740 out = buff;
1741 }
1742 else
1743 out = ltime_s;
1744 smalltext_out16(4, 1 + me_sfont_h, out, 0x105f);
1745 }
1746}
1747
1748static void draw_frame_credits(void)
1749{
1750 smalltext_out16(4, 1, "build: " __DATE__ " " __TIME__ " " REV, 0xe7fc);
1751}
1752
1753static const char credits_text[] =
1754 "PCSX-ReARMed\n\n"
1755 "(C) 1999-2003 PCSX Team\n"
1756 "(C) 2005-2009 PCSX-df Team\n"
1757 "(C) 2009-2011 PCSX-Reloaded Team\n\n"
1758 "ARM recompiler (C) 2009-2011 Ari64\n"
1759#ifdef __ARM_NEON__
1760 "ARM NEON GPU (c) 2011-2012 Exophase\n"
1761#endif
1762 "PEOpS GPU and SPU by Pete Bernert\n"
1763 " and the P.E.Op.S. team\n"
1764 "PCSX4ALL plugin by PCSX4ALL team\n"
1765 " Chui, Franxis, Unai\n\n"
1766 "integration, optimization and\n"
1767 " frontend (C) 2010-2012 notaz\n";
1768
1769static int reset_game(void)
1770{
1771 // sanity check
1772 if (bios_sel == 0 && !Config.HLE)
1773 return -1;
1774
1775 ClosePlugins();
1776 OpenPlugins();
1777 SysReset();
1778 if (CheckCdrom() != -1) {
1779 LoadCdrom();
1780 }
1781 return 0;
1782}
1783
1784static int reload_plugins(const char *cdimg)
1785{
1786 pl_vout_buf = NULL;
1787
1788 ClosePlugins();
1789
1790 set_cd_image(cdimg);
1791 LoadPlugins();
1792 pcnt_hook_plugins();
1793 NetOpened = 0;
1794 if (OpenPlugins() == -1) {
1795 me_update_msg("failed to open plugins");
1796 return -1;
1797 }
1798 plugin_call_rearmed_cbs();
1799
1800 cdrIsoMultidiskCount = 1;
1801 CdromId[0] = '\0';
1802 CdromLabel[0] = '\0';
1803
1804 return 0;
1805}
1806
1807static int run_bios(void)
1808{
1809 if (bios_sel == 0)
1810 return -1;
1811
1812 ready_to_go = 0;
1813 if (reload_plugins(NULL) != 0)
1814 return -1;
1815 SysReset();
1816
1817 ready_to_go = 1;
1818 return 0;
1819}
1820
1821static int run_exe(void)
1822{
1823 const char *fname;
1824
1825 fname = menu_loop_romsel(last_selected_fname, sizeof(last_selected_fname));
1826 if (fname == NULL)
1827 return -1;
1828
1829 ready_to_go = 0;
1830 if (reload_plugins(NULL) != 0)
1831 return -1;
1832
1833 SysReset();
1834 if (Load(fname) != 0) {
1835 me_update_msg("exe load failed, bad file?");
1836 printf("meh\n");
1837 return -1;
1838 }
1839
1840 ready_to_go = 1;
1841 return 0;
1842}
1843
1844static int run_cd_image(const char *fname)
1845{
1846 ready_to_go = 0;
1847 reload_plugins(fname);
1848
1849 // always autodetect, menu_sync_config will override as needed
1850 Config.PsxAuto = 1;
1851
1852 if (CheckCdrom() == -1) {
1853 // Only check the CD if we are starting the console with a CD
1854 ClosePlugins();
1855 me_update_msg("unsupported/invalid CD image");
1856 return -1;
1857 }
1858
1859 SysReset();
1860
1861 // Read main executable directly from CDRom and start it
1862 if (LoadCdrom() == -1) {
1863 ClosePlugins();
1864 me_update_msg("failed to load CD image");
1865 return -1;
1866 }
1867
1868 ready_to_go = 1;
1869 snprintf(hud_msg, sizeof(hud_msg), "Booting up...");
1870 hud_new_msg = 2;
1871 return 0;
1872}
1873
1874static int romsel_run(void)
1875{
1876 int prev_gpu, prev_spu;
1877 const char *fname;
1878
1879 fname = menu_loop_romsel(last_selected_fname, sizeof(last_selected_fname));
1880 if (fname == NULL)
1881 return -1;
1882
1883 printf("selected file: %s\n", fname);
1884
1885 new_dynarec_clear_full();
1886
1887 if (run_cd_image(fname) != 0)
1888 return -1;
1889
1890 prev_gpu = gpu_plugsel;
1891 prev_spu = spu_plugsel;
1892 if (menu_load_config(1) != 0)
1893 menu_load_config(0);
1894
1895 // check for plugin changes, have to repeat
1896 // loading if game config changed plugins to reload them
1897 if (prev_gpu != gpu_plugsel || prev_spu != spu_plugsel) {
1898 printf("plugin change detected, reloading plugins..\n");
1899 if (run_cd_image(fname) != 0)
1900 return -1;
1901 }
1902
1903 if (Config.HLE)
1904 printf("note: running without BIOS, expect compatibility problems\n");
1905
1906 strcpy(last_selected_fname, rom_fname_reload);
1907 return 0;
1908}
1909
1910static int swap_cd_image(void)
1911{
1912 char *fname;
1913
1914 fname = menu_loop_romsel(last_selected_fname, sizeof(last_selected_fname));
1915 if (fname == NULL)
1916 return -1;
1917
1918 printf("selected file: %s\n", fname);
1919
1920 CdromId[0] = '\0';
1921 CdromLabel[0] = '\0';
1922
1923 set_cd_image(fname);
1924 if (ReloadCdromPlugin() < 0) {
1925 me_update_msg("failed to load cdr plugin");
1926 return -1;
1927 }
1928 if (CDR_open() < 0) {
1929 me_update_msg("failed to open cdr plugin");
1930 return -1;
1931 }
1932
1933 SetCdOpenCaseTime(time(NULL) + 2);
1934 LidInterrupt();
1935
1936 strcpy(last_selected_fname, rom_fname_reload);
1937 return 0;
1938}
1939
1940static int swap_cd_multidisk(void)
1941{
1942 cdrIsoMultidiskSelect++;
1943 CdromId[0] = '\0';
1944 CdromLabel[0] = '\0';
1945
1946 CDR_close();
1947 if (CDR_open() < 0) {
1948 me_update_msg("failed to open cdr plugin");
1949 return -1;
1950 }
1951
1952 SetCdOpenCaseTime(time(NULL) + 2);
1953 LidInterrupt();
1954
1955 return 0;
1956}
1957
1958static int main_menu_handler(int id, int keys)
1959{
1960 switch (id)
1961 {
1962 case MA_MAIN_RESUME_GAME:
1963 if (ready_to_go)
1964 return 1;
1965 break;
1966 case MA_MAIN_SAVE_STATE:
1967 if (ready_to_go)
1968 return menu_loop_savestate(0);
1969 break;
1970 case MA_MAIN_LOAD_STATE:
1971 if (ready_to_go)
1972 return menu_loop_savestate(1);
1973 break;
1974 case MA_MAIN_RESET_GAME:
1975 if (ready_to_go && reset_game() == 0)
1976 return 1;
1977 break;
1978 case MA_MAIN_LOAD_ROM:
1979 if (romsel_run() == 0)
1980 return 1;
1981 break;
1982 case MA_MAIN_SWAP_CD:
1983 if (swap_cd_image() == 0)
1984 return 1;
1985 break;
1986 case MA_MAIN_SWAP_CD_MULTI:
1987 if (swap_cd_multidisk() == 0)
1988 return 1;
1989 break;
1990 case MA_MAIN_RUN_BIOS:
1991 if (run_bios() == 0)
1992 return 1;
1993 break;
1994 case MA_MAIN_RUN_EXE:
1995 if (run_exe() == 0)
1996 return 1;
1997 break;
1998 case MA_MAIN_CREDITS:
1999 draw_menu_message(credits_text, draw_frame_credits);
2000 in_menu_wait(PBTN_MOK|PBTN_MBACK, 70);
2001 break;
2002 case MA_MAIN_EXIT:
2003 OnFile_Exit();
2004 break;
2005 default:
2006 lprintf("%s: something unknown selected\n", __FUNCTION__);
2007 break;
2008 }
2009
2010 return 0;
2011}
2012
2013static menu_entry e_menu_main2[] =
2014{
2015 mee_handler_id("Change CD image", MA_MAIN_SWAP_CD, main_menu_handler),
2016 mee_handler_id("Next multidisk CD", MA_MAIN_SWAP_CD_MULTI, main_menu_handler),
2017 mee_handler_id("Run BIOS", MA_MAIN_RUN_BIOS, main_menu_handler),
2018 mee_handler_id("Run EXE", MA_MAIN_RUN_EXE, main_menu_handler),
2019 mee_handler ("Memcard manager", menu_loop_memcards),
2020 mee_end,
2021};
2022
2023static int main_menu2_handler(int id, int keys)
2024{
2025 static int sel = 0;
2026
2027 me_enable(e_menu_main2, MA_MAIN_SWAP_CD, ready_to_go);
2028 me_enable(e_menu_main2, MA_MAIN_SWAP_CD_MULTI, ready_to_go && cdrIsoMultidiskCount > 1);
2029 me_enable(e_menu_main2, MA_MAIN_RUN_BIOS, bios_sel != 0);
2030
2031 return me_loop_d(e_menu_main2, &sel, NULL, draw_frame_main);
2032}
2033
2034static const char h_extra[] = "Change CD, manage memcards..\n";
2035
2036static menu_entry e_menu_main[] =
2037{
2038 mee_label (""),
2039 mee_label (""),
2040 mee_handler_id("Resume game", MA_MAIN_RESUME_GAME, main_menu_handler),
2041 mee_handler_id("Save State", MA_MAIN_SAVE_STATE, main_menu_handler),
2042 mee_handler_id("Load State", MA_MAIN_LOAD_STATE, main_menu_handler),
2043 mee_handler_id("Reset game", MA_MAIN_RESET_GAME, main_menu_handler),
2044 mee_handler_id("Load CD image", MA_MAIN_LOAD_ROM, main_menu_handler),
2045 mee_handler ("Options", menu_loop_options),
2046 mee_handler ("Controls", menu_loop_keyconfig),
2047 mee_handler_h ("Extra stuff", main_menu2_handler, h_extra),
2048 mee_handler_id("Credits", MA_MAIN_CREDITS, main_menu_handler),
2049 mee_handler_id("Exit", MA_MAIN_EXIT, main_menu_handler),
2050 mee_end,
2051};
2052
2053// ----------------------------
2054
2055static void menu_leave_emu(void);
2056
2057void menu_loop(void)
2058{
2059 static int sel = 0;
2060
2061 menu_leave_emu();
2062
2063 if (bioses[1] == NULL && !warned_about_bios) {
2064 menu_bios_warn();
2065 warned_about_bios = 1;
2066 }
2067
2068 me_enable(e_menu_main, MA_MAIN_RESUME_GAME, ready_to_go);
2069 me_enable(e_menu_main, MA_MAIN_SAVE_STATE, ready_to_go && CdromId[0]);
2070 me_enable(e_menu_main, MA_MAIN_LOAD_STATE, ready_to_go && CdromId[0]);
2071 me_enable(e_menu_main, MA_MAIN_RESET_GAME, ready_to_go);
2072
2073 in_set_config_int(0, IN_CFG_BLOCKING, 1);
2074
2075 do {
2076 me_loop_d(e_menu_main, &sel, NULL, draw_frame_main);
2077 } while (!ready_to_go);
2078
2079 /* wait until menu, ok, back is released */
2080 while (in_menu_wait_any(50) & (PBTN_MENU|PBTN_MOK|PBTN_MBACK))
2081 ;
2082
2083 in_set_config_int(0, IN_CFG_BLOCKING, 0);
2084
2085 menu_prepare_emu();
2086}
2087
2088static int qsort_strcmp(const void *p1, const void *p2)
2089{
2090 char * const *s1 = (char * const *)p1;
2091 char * const *s2 = (char * const *)p2;
2092 return strcasecmp(*s1, *s2);
2093}
2094
2095static void scan_bios_plugins(void)
2096{
2097 char fname[MAXPATHLEN];
2098 struct dirent *ent;
2099 int bios_i, gpu_i, spu_i, mc_i;
2100 char *p;
2101 DIR *dir;
2102
2103 bioses[0] = "HLE";
2104 gpu_plugins[0] = "builtin_gpu";
2105 spu_plugins[0] = "builtin_spu";
2106 memcards[0] = "(none)";
2107 bios_i = gpu_i = spu_i = mc_i = 1;
2108
2109 snprintf(fname, sizeof(fname), "%s/", Config.BiosDir);
2110 dir = opendir(fname);
2111 if (dir == NULL) {
2112 perror("scan_bios_plugins bios opendir");
2113 goto do_plugins;
2114 }
2115
2116 while (1) {
2117 struct stat st;
2118
2119 errno = 0;
2120 ent = readdir(dir);
2121 if (ent == NULL) {
2122 if (errno != 0)
2123 perror("readdir");
2124 break;
2125 }
2126
2127 if (ent->d_type != DT_REG && ent->d_type != DT_LNK)
2128 continue;
2129
2130 snprintf(fname, sizeof(fname), "%s/%s", Config.BiosDir, ent->d_name);
2131 if (stat(fname, &st) != 0 || st.st_size != 512*1024) {
2132 printf("bad BIOS file: %s\n", ent->d_name);
2133 continue;
2134 }
2135
2136 if (bios_i < ARRAY_SIZE(bioses) - 1) {
2137 bioses[bios_i++] = strdup(ent->d_name);
2138 continue;
2139 }
2140
2141 printf("too many BIOSes, dropping \"%s\"\n", ent->d_name);
2142 }
2143
2144 closedir(dir);
2145
2146do_plugins:
2147 snprintf(fname, sizeof(fname), "%s/", Config.PluginsDir);
2148 dir = opendir(fname);
2149 if (dir == NULL) {
2150 perror("scan_bios_plugins plugins opendir");
2151 goto do_memcards;
2152 }
2153
2154 while (1) {
2155 void *h, *tmp;
2156
2157 errno = 0;
2158 ent = readdir(dir);
2159 if (ent == NULL) {
2160 if (errno != 0)
2161 perror("readdir");
2162 break;
2163 }
2164 p = strstr(ent->d_name, ".so");
2165 if (p == NULL)
2166 continue;
2167
2168 snprintf(fname, sizeof(fname), "%s/%s", Config.PluginsDir, ent->d_name);
2169 h = dlopen(fname, RTLD_LAZY | RTLD_LOCAL);
2170 if (h == NULL) {
2171 fprintf(stderr, "%s\n", dlerror());
2172 continue;
2173 }
2174
2175 // now what do we have here?
2176 tmp = dlsym(h, "GPUinit");
2177 if (tmp) {
2178 dlclose(h);
2179 if (gpu_i < ARRAY_SIZE(gpu_plugins) - 1)
2180 gpu_plugins[gpu_i++] = strdup(ent->d_name);
2181 continue;
2182 }
2183
2184 tmp = dlsym(h, "SPUinit");
2185 if (tmp) {
2186 dlclose(h);
2187 if (spu_i < ARRAY_SIZE(spu_plugins) - 1)
2188 spu_plugins[spu_i++] = strdup(ent->d_name);
2189 continue;
2190 }
2191
2192 fprintf(stderr, "ignoring unidentified plugin: %s\n", fname);
2193 dlclose(h);
2194 }
2195
2196 closedir(dir);
2197
2198do_memcards:
2199 dir = opendir("." MEMCARD_DIR);
2200 if (dir == NULL) {
2201 perror("scan_bios_plugins memcards opendir");
2202 return;
2203 }
2204
2205 while (1) {
2206 struct stat st;
2207
2208 errno = 0;
2209 ent = readdir(dir);
2210 if (ent == NULL) {
2211 if (errno != 0)
2212 perror("readdir");
2213 break;
2214 }
2215
2216 if (ent->d_type != DT_REG && ent->d_type != DT_LNK)
2217 continue;
2218
2219 snprintf(fname, sizeof(fname), "." MEMCARD_DIR "%s", ent->d_name);
2220 if (stat(fname, &st) != 0) {
2221 printf("bad memcard file: %s\n", ent->d_name);
2222 continue;
2223 }
2224
2225 if (mc_i < ARRAY_SIZE(memcards) - 1) {
2226 memcards[mc_i++] = strdup(ent->d_name);
2227 continue;
2228 }
2229
2230 printf("too many memcards, dropping \"%s\"\n", ent->d_name);
2231 }
2232
2233 if (mc_i > 2)
2234 qsort(memcards + 1, mc_i - 1, sizeof(memcards[0]), qsort_strcmp);
2235
2236 closedir(dir);
2237}
2238
2239void menu_init(void)
2240{
2241 char buff[MAXPATHLEN];
2242
2243 strcpy(last_selected_fname, "/media");
2244
2245 scan_bios_plugins();
2246 pnd_menu_init();
2247 menu_init_common();
2248
2249 menu_set_defconfig();
2250 menu_load_config(0);
2251 last_psx_w = 320;
2252 last_psx_h = 240;
2253 last_psx_bpp = 16;
2254
2255 g_menubg_src_ptr = calloc(g_menuscreen_w * g_menuscreen_h * 2, 1);
2256 g_menubg_ptr = calloc(g_menuscreen_w * g_menuscreen_h * 2, 1);
2257 if (g_menubg_src_ptr == NULL || g_menubg_ptr == NULL) {
2258 fprintf(stderr, "OOM\n");
2259 exit(1);
2260 }
2261
2262 emu_make_path(buff, "skin/background.png", sizeof(buff));
2263 readpng(g_menubg_src_ptr, buff, READPNG_BG, g_menuscreen_w, g_menuscreen_h);
2264
2265#ifndef __ARM_ARCH_7A__ /* XXX */
2266 me_enable(e_menu_gfx_options, MA_OPT_SCALER, 0);
2267 me_enable(e_menu_gfx_options, MA_OPT_FILTERING, 0);
2268 me_enable(e_menu_gfx_options, MA_OPT_SCALER_C, 0);
2269 me_enable(e_menu_keyconfig, MA_CTRL_NUBS_BTNS, 0);
2270#else
2271 me_enable(e_menu_gfx_options, MA_OPT_SCALER2, 0);
2272 me_enable(e_menu_keyconfig, MA_CTRL_VIBRATION, 0);
2273 me_enable(e_menu_keyconfig, MA_CTRL_DEADZONE, 0);
2274#endif
2275}
2276
2277void menu_notify_mode_change(int w, int h, int bpp)
2278{
2279 float mult;
2280 int imult;
2281
2282 last_psx_w = w;
2283 last_psx_h = h;
2284 last_psx_bpp = bpp;
2285
2286 // XXX: should really menu code cotrol the layer size?
2287 switch (scaling) {
2288 case SCALE_1_1:
2289 g_layer_w = w; g_layer_h = h;
2290 break;
2291
2292 case SCALE_4_3v2:
2293 if (h > g_menuscreen_h || (240 < h && h <= 360))
2294 goto fractional_4_3;
2295
2296 // 4:3 that prefers integer scaling
2297 imult = g_menuscreen_h / h;
2298 g_layer_w = w * imult;
2299 g_layer_h = h * imult;
2300 mult = (float)g_layer_w / (float)g_layer_h;
2301 if (mult < 1.25f || mult > 1.666f)
2302 g_layer_w = 4.0f/3.0f * (float)g_layer_h;
2303 printf(" -> %dx%d %.1f\n", g_layer_w, g_layer_h, mult);
2304 break;
2305
2306 fractional_4_3:
2307 case SCALE_4_3:
2308 mult = 240.0f / (float)h * 4.0f / 3.0f;
2309 if (h > 256)
2310 mult *= 2.0f;
2311 g_layer_w = mult * (float)g_menuscreen_h;
2312 g_layer_h = g_menuscreen_h;
2313 printf(" -> %dx%d %.1f\n", g_layer_w, g_layer_h, mult);
2314 break;
2315
2316 case SCALE_FULLSCREEN:
2317 g_layer_w = g_menuscreen_w;
2318 g_layer_h = g_menuscreen_h;
2319 break;
2320
2321 default:
2322 break;
2323 }
2324
2325 g_layer_x = g_menuscreen_w / 2 - g_layer_w / 2;
2326 g_layer_y = g_menuscreen_h / 2 - g_layer_h / 2;
2327 if (g_layer_x < 0) g_layer_x = 0;
2328 if (g_layer_y < 0) g_layer_y = 0;
2329 if (g_layer_w > g_menuscreen_w) g_layer_w = g_menuscreen_w;
2330 if (g_layer_h > g_menuscreen_h) g_layer_w = g_menuscreen_h;
2331}
2332
2333static void menu_leave_emu(void)
2334{
2335 if (GPU_close != NULL) {
2336 int ret = GPU_close();
2337 if (ret)
2338 fprintf(stderr, "Warning: GPU_close returned %d\n", ret);
2339 }
2340
2341 plat_video_menu_enter(ready_to_go);
2342
2343 memcpy(g_menubg_ptr, g_menubg_src_ptr, g_menuscreen_w * g_menuscreen_h * 2);
2344 if (pl_vout_buf != NULL && ready_to_go) {
2345 int x = max(0, g_menuscreen_w - last_psx_w);
2346 int y = max(0, g_menuscreen_h / 2 - last_psx_h / 2);
2347 int w = min(g_menuscreen_w, last_psx_w);
2348 int h = min(g_menuscreen_h, last_psx_h);
2349 u16 *d = (u16 *)g_menubg_ptr + g_menuscreen_w * y + x;
2350 char *s = pl_vout_buf;
2351
2352 if (last_psx_bpp == 16) {
2353 for (; h > 0; h--, d += g_menuscreen_w, s += last_psx_w * 2)
2354 menu_darken_bg(d, s, w, 0);
2355 }
2356 else {
2357 for (; h > 0; h--, d += g_menuscreen_w, s += last_psx_w * 3) {
2358 bgr888_to_rgb565(d, s, w * 3);
2359 menu_darken_bg(d, d, w, 0);
2360 }
2361 }
2362 }
2363
2364 if (ready_to_go)
2365 cpu_clock = plat_cpu_clock_get();
2366}
2367
2368void menu_prepare_emu(void)
2369{
2370 R3000Acpu *prev_cpu = psxCpu;
2371
2372 plat_video_menu_leave();
2373
2374 menu_notify_mode_change(last_psx_w, last_psx_h, last_psx_bpp);
2375
2376 psxCpu = (Config.Cpu == CPU_INTERPRETER) ? &psxInt : &psxRec;
2377 if (psxCpu != prev_cpu)
2378 // note that this does not really reset, just clears drc caches
2379 psxCpu->Reset();
2380
2381 // core doesn't care about Config.Cdda changes,
2382 // so handle them manually here
2383 if (Config.Cdda)
2384 CDR_stop();
2385
2386 menu_sync_config();
2387 apply_lcdrate(Config.PsxType);
2388 apply_filter(filter);
2389 if (cpu_clock > 0)
2390 plat_cpu_clock_apply(cpu_clock);
2391
2392 // push config to GPU plugin
2393 plugin_call_rearmed_cbs();
2394
2395 if (GPU_open != NULL) {
2396 int ret = GPU_open(&gpuDisp, "PCSX", NULL);
2397 if (ret)
2398 fprintf(stderr, "Warning: GPU_open returned %d\n", ret);
2399 }
2400
2401 dfinput_activate();
2402}
2403
2404void me_update_msg(const char *msg)
2405{
2406 strncpy(menu_error_msg, msg, sizeof(menu_error_msg));
2407 menu_error_msg[sizeof(menu_error_msg) - 1] = 0;
2408
2409 menu_error_time = plat_get_ticks_ms();
2410 lprintf("msg: %s\n", menu_error_msg);
2411}
2412