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