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