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