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