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