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