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