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