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