input changes part3
[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 CE_INTVAL_P(gpu_senquack.ilace_force),
437 CE_INTVAL_P(gpu_senquack.pixel_skip),
438 CE_INTVAL_P(gpu_senquack.lighting),
439 CE_INTVAL_P(gpu_senquack.fast_lighting),
440 CE_INTVAL_P(gpu_senquack.blending),
441 CE_INTVAL_P(gpu_senquack.dithering),
442 CE_INTVAL_P(gpu_senquack.scale_hires),
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 "None",
1209 NULL
1210};
1211static const char h_nub_btns[] = "Experimental, keep this OFF if unsure. Select rescan after change.";
1212static const char h_notsgun[] = "Don't trigger (shoot) when touching screen in gun games.";
1213static const char h_vibration[]= "Must select analog above and enable this ingame too.";
1214
1215static menu_entry e_menu_keyconfig[] =
1216{
1217 mee_handler_id("Player 1", MA_CTRL_PLAYER1, key_config_loop_wrap),
1218 mee_handler_id("Player 2", MA_CTRL_PLAYER2, key_config_loop_wrap),
1219 mee_handler_id("Analog controls", MA_CTRL_ANALOG, key_config_analog),
1220 mee_handler_id("Emulator/Gun controls", MA_CTRL_EMU, key_config_loop_wrap),
1221 mee_label (""),
1222 mee_enum ("Port 1 device", 0, in_type_sel1, men_in_type_sel),
1223 mee_enum ("Port 2 device", 0, in_type_sel2, men_in_type_sel),
1224 mee_onoff_h ("Nubs as buttons", MA_CTRL_NUBS_BTNS, in_evdev_allow_abs_only, 1, h_nub_btns),
1225 mee_onoff_h ("Vibration", MA_CTRL_VIBRATION, in_enable_vibration, 1, h_vibration),
1226 mee_range ("Analog deadzone", MA_CTRL_DEADZONE, analog_deadzone, 1, 99),
1227 mee_onoff_h ("No TS Gun trigger", 0, g_opts, OPT_TSGUN_NOTRIGGER, h_notsgun),
1228 mee_cust_nosave("Save global config", MA_OPT_SAVECFG, mh_savecfg, mgn_saveloadcfg),
1229 mee_cust_nosave("Save cfg for loaded game", MA_OPT_SAVECFG_GAME, mh_savecfg, mgn_saveloadcfg),
1230 mee_handler ("Rescan devices:", mh_input_rescan),
1231 mee_label (""),
1232 mee_label_mk (MA_CTRL_DEV_FIRST, 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_label_mk (MA_CTRL_DEV_NEXT, mgn_dev_name),
1239 mee_end,
1240};
1241
1242static int menu_loop_keyconfig(int id, int keys)
1243{
1244 static int sel = 0;
1245
1246// me_enable(e_menu_keyconfig, MA_OPT_SAVECFG_GAME, ready_to_go && CdromId[0]);
1247 me_loop(e_menu_keyconfig, &sel);
1248 return 0;
1249}
1250
1251// ------------ gfx options menu ------------
1252
1253static const char *men_scaler[] = {
1254 "1x1", "integer scaled 2x", "scaled 4:3", "integer scaled 4:3", "fullscreen", "custom", NULL
1255};
1256static const char *men_soft_filter[] = { "None",
1257#ifdef __ARM_NEON__
1258 "scale2x", "eagle2x",
1259#endif
1260 NULL };
1261static const char *men_dummy[] = { NULL };
1262static const char *men_centering[] = { "Auto", "Ingame", "Force", NULL };
1263static const char h_scaler[] = "int. 2x - scales w. or h. 2x if it fits on screen\n"
1264 "int. 4:3 - uses integer if possible, else fractional";
1265static const char h_cscaler[] = "Displays the scaler layer, you can resize it\n"
1266 "using d-pad or move it using R+d-pad";
1267static const char h_soft_filter[] = "Works only if game uses low resolution modes";
1268static const char h_gamma[] = "Gamma/brightness adjustment (default 100)";
1269#ifdef __ARM_NEON__
1270static const char h_scanline_l[] = "Scanline brightness, 0-100%";
1271#endif
1272
1273static int menu_loop_cscaler(int id, int keys)
1274{
1275 unsigned int inp;
1276
1277 g_scaler = SCALE_CUSTOM;
1278
1279 plat_gvideo_open(Config.PsxType);
1280
1281 for (;;)
1282 {
1283 menu_draw_begin(0, 1);
1284 memset(g_menuscreen_ptr, 4, g_menuscreen_w * g_menuscreen_h * 2);
1285 text_out16(2, 2, "%d,%d", g_layer_x, g_layer_y);
1286 text_out16(2, 480 - 18, "%dx%d | d-pad: resize, R+d-pad: move", g_layer_w, g_layer_h);
1287 menu_draw_end();
1288
1289 inp = in_menu_wait(PBTN_UP|PBTN_DOWN|PBTN_LEFT|PBTN_RIGHT
1290 |PBTN_R|PBTN_MOK|PBTN_MBACK, NULL, 40);
1291 if (inp & PBTN_UP) g_layer_y--;
1292 if (inp & PBTN_DOWN) g_layer_y++;
1293 if (inp & PBTN_LEFT) g_layer_x--;
1294 if (inp & PBTN_RIGHT) g_layer_x++;
1295 if (!(inp & PBTN_R)) {
1296 if (inp & PBTN_UP) g_layer_h += 2;
1297 if (inp & PBTN_DOWN) g_layer_h -= 2;
1298 if (inp & PBTN_LEFT) g_layer_w += 2;
1299 if (inp & PBTN_RIGHT) g_layer_w -= 2;
1300 }
1301 if (inp & (PBTN_MOK|PBTN_MBACK))
1302 break;
1303
1304 if (inp & (PBTN_UP|PBTN_DOWN|PBTN_LEFT|PBTN_RIGHT)) {
1305 if (g_layer_x < 0) g_layer_x = 0;
1306 if (g_layer_x > 640) g_layer_x = 640;
1307 if (g_layer_y < 0) g_layer_y = 0;
1308 if (g_layer_y > 420) g_layer_y = 420;
1309 if (g_layer_w < 160) g_layer_w = 160;
1310 if (g_layer_h < 60) g_layer_h = 60;
1311 if (g_layer_x + g_layer_w > 800)
1312 g_layer_w = 800 - g_layer_x;
1313 if (g_layer_y + g_layer_h > 480)
1314 g_layer_h = 480 - g_layer_y;
1315 // resize the layer
1316 plat_gvideo_open(Config.PsxType);
1317 }
1318 }
1319
1320 plat_gvideo_close();
1321
1322 return 0;
1323}
1324
1325static menu_entry e_menu_gfx_options[] =
1326{
1327 mee_enum ("Screen centering", MA_OPT_CENTERING, pl_rearmed_cbs.screen_centering_type, men_centering),
1328 mee_enum_h ("Scaler", MA_OPT_VARSCALER, g_scaler, men_scaler, h_scaler),
1329 mee_enum ("Video output mode", MA_OPT_VOUT_MODE, plat_target.vout_method, men_dummy),
1330 mee_onoff ("Software Scaling", MA_OPT_SCALER2, soft_scaling, 1),
1331 mee_enum ("Hardware Filter", MA_OPT_HWFILTER, plat_target.hwfilter, men_dummy),
1332 mee_enum_h ("Software Filter", MA_OPT_SWFILTER, soft_filter, men_soft_filter, h_soft_filter),
1333#ifdef __ARM_NEON__
1334 mee_onoff ("Scanlines", MA_OPT_SCANLINES, scanlines, 1),
1335 mee_range_h ("Scanline brightness", MA_OPT_SCANLINE_LEVEL, scanline_level, 0, 100, h_scanline_l),
1336#endif
1337 mee_range_h ("Gamma adjustment", MA_OPT_GAMMA, g_gamma, 1, 200, h_gamma),
1338// mee_onoff ("Vsync", 0, vsync, 1),
1339 mee_cust_h ("Setup custom scaler", MA_OPT_VARSCALER_C, menu_loop_cscaler, NULL, h_cscaler),
1340 mee_end,
1341};
1342
1343static int menu_loop_gfx_options(int id, int keys)
1344{
1345 static int sel = 0;
1346
1347 me_loop(e_menu_gfx_options, &sel);
1348
1349 return 0;
1350}
1351
1352// ------------ bios/plugins ------------
1353
1354#ifdef BUILTIN_GPU_NEON
1355
1356static const char h_gpu_neon[] =
1357 "Configure built-in NEON GPU plugin";
1358static const char h_gpu_neon_enhanced[] =
1359 "Renders in double resolution at the cost of lower performance\n"
1360 "(not available for high resolution games)";
1361static const char h_gpu_neon_enhanced_hack[] =
1362 "Speed hack for above option (glitches some games)";
1363static const char *men_gpu_interlace[] = { "Off", "On", "Auto", NULL };
1364
1365static menu_entry e_menu_plugin_gpu_neon[] =
1366{
1367 mee_enum ("Enable interlace mode", 0, pl_rearmed_cbs.gpu_neon.allow_interlace, men_gpu_interlace),
1368 mee_onoff_h ("Enhanced resolution", 0, pl_rearmed_cbs.gpu_neon.enhancement_enable, 1, h_gpu_neon_enhanced),
1369 mee_onoff_h ("Enhanced res. speed hack", 0, pl_rearmed_cbs.gpu_neon.enhancement_no_main, 1, h_gpu_neon_enhanced_hack),
1370 mee_end,
1371};
1372
1373static int menu_loop_plugin_gpu_neon(int id, int keys)
1374{
1375 static int sel = 0;
1376 me_loop(e_menu_plugin_gpu_neon, &sel);
1377 return 0;
1378}
1379
1380#endif
1381
1382static menu_entry e_menu_plugin_gpu_unai[] =
1383{
1384 mee_onoff ("Skip every 2nd line", 0, pl_rearmed_cbs.gpu_unai.lineskip, 1),
1385 mee_onoff ("Abe's Odyssey hack", 0, pl_rearmed_cbs.gpu_unai.abe_hack, 1),
1386 mee_onoff ("Disable lighting", 0, pl_rearmed_cbs.gpu_unai.no_light, 1),
1387 mee_onoff ("Disable blending", 0, pl_rearmed_cbs.gpu_unai.no_blend, 1),
1388 mee_end,
1389};
1390
1391static int menu_loop_plugin_gpu_unai(int id, int keys)
1392{
1393 int sel = 0;
1394 me_loop(e_menu_plugin_gpu_unai, &sel);
1395 return 0;
1396}
1397
1398static menu_entry e_menu_plugin_gpu_senquack[] =
1399{
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 mee_end,
1407};
1408
1409static int menu_loop_plugin_gpu_senquack(int id, int keys)
1410{
1411 int sel = 0;
1412 me_loop(e_menu_plugin_gpu_senquack, &sel);
1413 return 0;
1414}
1415
1416
1417static const char *men_gpu_dithering[] = { "None", "Game dependant", "Always", NULL };
1418//static const char h_gpu_0[] = "Needed for Chrono Cross";
1419static const char h_gpu_1[] = "Capcom fighting games";
1420static const char h_gpu_2[] = "Black screens in Lunar";
1421static const char h_gpu_3[] = "Compatibility mode";
1422static const char h_gpu_6[] = "Pandemonium 2";
1423//static const char h_gpu_7[] = "Skip every second frame";
1424static const char h_gpu_8[] = "Needed by Dark Forces";
1425static const char h_gpu_9[] = "better g-colors, worse textures";
1426static const char h_gpu_10[] = "Toggle busy flags after drawing";
1427
1428static menu_entry e_menu_plugin_gpu_peops[] =
1429{
1430 mee_enum ("Dithering", 0, pl_rearmed_cbs.gpu_peops.iUseDither, men_gpu_dithering),
1431// mee_onoff_h ("Odd/even bit hack", 0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<0, h_gpu_0),
1432 mee_onoff_h ("Expand screen width", 0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<1, h_gpu_1),
1433 mee_onoff_h ("Ignore brightness color", 0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<2, h_gpu_2),
1434 mee_onoff_h ("Disable coordinate check", 0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<3, h_gpu_3),
1435 mee_onoff_h ("Lazy screen update", 0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<6, h_gpu_6),
1436// mee_onoff_h ("Old frame skipping", 0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<7, h_gpu_7),
1437 mee_onoff_h ("Repeated flat tex triangles ",0,pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<8, h_gpu_8),
1438 mee_onoff_h ("Draw quads with triangles", 0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<9, h_gpu_9),
1439 mee_onoff_h ("Fake 'gpu busy' states", 0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<10, h_gpu_10),
1440 mee_end,
1441};
1442
1443static int menu_loop_plugin_gpu_peops(int id, int keys)
1444{
1445 static int sel = 0;
1446 me_loop(e_menu_plugin_gpu_peops, &sel);
1447 return 0;
1448}
1449
1450static const char *men_peopsgl_texfilter[] = { "None", "Standard", "Extended",
1451 "Standard-sprites", "Extended-sprites", "Standard+sprites", "Extended+sprites", NULL };
1452static const char *men_peopsgl_fbtex[] = { "Emulated VRam", "Black", "Card", "Card+soft" };
1453
1454static menu_entry e_menu_plugin_gpu_peopsgl[] =
1455{
1456 mee_onoff ("Dithering", 0, pl_rearmed_cbs.gpu_peopsgl.bDrawDither, 1),
1457 mee_enum ("Texture Filtering", 0, pl_rearmed_cbs.gpu_peopsgl.iFilterType, men_peopsgl_texfilter),
1458 mee_enum ("Framebuffer Textures", 0, pl_rearmed_cbs.gpu_peopsgl.iFrameTexType, men_peopsgl_fbtex),
1459 mee_onoff ("Mask Detect", 0, pl_rearmed_cbs.gpu_peopsgl.iUseMask, 1),
1460 mee_onoff ("Opaque Pass", 0, pl_rearmed_cbs.gpu_peopsgl.bOpaquePass, 1),
1461 mee_onoff ("Advanced Blend", 0, pl_rearmed_cbs.gpu_peopsgl.bAdvancedBlend, 1),
1462 mee_onoff ("Use Fast Mdec", 0, pl_rearmed_cbs.gpu_peopsgl.bUseFastMdec, 1),
1463 mee_range ("Texture RAM size (MB)", 0, pl_rearmed_cbs.gpu_peopsgl.iVRamSize, 4, 128),
1464 mee_onoff ("Texture garbage collection", 0, pl_rearmed_cbs.gpu_peopsgl.iTexGarbageCollection, 1),
1465 mee_label ("Fixes/hacks:"),
1466 mee_onoff ("FF7 cursor", 0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<0),
1467 mee_onoff ("Direct FB updates", 0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<1),
1468 mee_onoff ("Black brightness", 0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<2),
1469 mee_onoff ("Swap front detection", 0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<3),
1470 mee_onoff ("Disable coord check", 0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<4),
1471 mee_onoff ("No blue glitches (LoD)", 0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<5),
1472 mee_onoff ("Soft FB access", 0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<6),
1473 mee_onoff ("FF9 rect", 0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<9),
1474 mee_onoff ("No subtr. blending", 0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<10),
1475 mee_onoff ("Lazy upload (DW7)", 0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<11),
1476 mee_onoff ("Additional uploads", 0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<15),
1477 mee_end,
1478};
1479
1480static int menu_loop_plugin_gpu_peopsgl(int id, int keys)
1481{
1482 static int sel = 0;
1483 me_loop(e_menu_plugin_gpu_peopsgl, &sel);
1484 return 0;
1485}
1486
1487static const char *men_spu_interp[] = { "None", "Simple", "Gaussian", "Cubic", NULL };
1488static const char h_spu_volboost[] = "Large values cause distortion";
1489static const char h_spu_tempo[] = "Slows down audio if emu is too slow\n"
1490 "This is inaccurate and breaks games";
1491
1492static menu_entry e_menu_plugin_spu[] =
1493{
1494 mee_range_h ("Volume boost", 0, volume_boost, -5, 30, h_spu_volboost),
1495 mee_onoff ("Reverb", 0, spu_config.iUseReverb, 1),
1496 mee_enum ("Interpolation", 0, spu_config.iUseInterpolation, men_spu_interp),
1497 //mee_onoff ("Adjust XA pitch", 0, spu_config.iXAPitch, 1),
1498 mee_onoff_h ("Adjust tempo", 0, spu_config.iTempo, 1, h_spu_tempo),
1499 mee_end,
1500};
1501
1502static int menu_loop_plugin_spu(int id, int keys)
1503{
1504 static int sel = 0;
1505 me_loop(e_menu_plugin_spu, &sel);
1506 return 0;
1507}
1508
1509static const char h_bios[] = "HLE is simulated BIOS. BIOS selection is saved in\n"
1510 "savestates and can't be changed there. Must save\n"
1511 "config and reload the game for change to take effect";
1512static const char h_plugin_gpu[] =
1513#ifdef BUILTIN_GPU_NEON
1514 "builtin_gpu is the NEON GPU, very fast and accurate\n"
1515#endif
1516 "gpu_peops is Pete's soft GPU, slow but accurate\n"
1517 "gpu_unai is GPU from PCSX4ALL, fast but glitchy\n"
1518 "gpu_senquack is more accurate but slower\n"
1519 "gpu_gles Pete's hw GPU, uses 3D chip but is glitchy\n"
1520 "must save config and reload the game if changed";
1521static const char h_plugin_spu[] = "spunull effectively disables sound\n"
1522 "must save config and reload the game if changed";
1523static const char h_gpu_peops[] = "Configure P.E.Op.S. SoftGL Driver V1.17";
1524static const char h_gpu_peopsgl[]= "Configure P.E.Op.S. MesaGL Driver V1.78";
1525static const char h_gpu_unai[] = "Configure Unai/PCSX4ALL Team GPU plugin";
1526static const char h_gpu_senquack[] = "Configure Unai/PCSX4ALL Senquack plugin";
1527static const char h_spu[] = "Configure built-in P.E.Op.S. Sound Driver V1.7";
1528
1529static menu_entry e_menu_plugin_options[] =
1530{
1531 mee_enum_h ("BIOS", 0, bios_sel, bioses, h_bios),
1532 mee_enum_h ("GPU plugin", 0, gpu_plugsel, gpu_plugins, h_plugin_gpu),
1533 mee_enum_h ("SPU plugin", 0, spu_plugsel, spu_plugins, h_plugin_spu),
1534#ifdef BUILTIN_GPU_NEON
1535 mee_handler_h ("Configure built-in GPU plugin", menu_loop_plugin_gpu_neon, h_gpu_neon),
1536#endif
1537 mee_handler_h ("Configure gpu_peops plugin", menu_loop_plugin_gpu_peops, h_gpu_peops),
1538 mee_handler_h ("Configure gpu_unai GPU plugin", menu_loop_plugin_gpu_unai, h_gpu_unai),
1539 mee_handler_h ("Configure gpu_senquack GPU plugin", menu_loop_plugin_gpu_senquack, h_gpu_senquack),
1540 mee_handler_h ("Configure gpu_gles GPU plugin", menu_loop_plugin_gpu_peopsgl, h_gpu_peopsgl),
1541 mee_handler_h ("Configure built-in SPU plugin", menu_loop_plugin_spu, h_spu),
1542 mee_end,
1543};
1544
1545static menu_entry e_menu_main2[];
1546
1547static int menu_loop_plugin_options(int id, int keys)
1548{
1549 static int sel = 0;
1550 me_loop(e_menu_plugin_options, &sel);
1551
1552 // sync BIOS/plugins
1553 snprintf(Config.Bios, sizeof(Config.Bios), "%s", bioses[bios_sel]);
1554 snprintf(Config.Gpu, sizeof(Config.Gpu), "%s", gpu_plugins[gpu_plugsel]);
1555 snprintf(Config.Spu, sizeof(Config.Spu), "%s", spu_plugins[spu_plugsel]);
1556 me_enable(e_menu_main2, MA_MAIN_RUN_BIOS, bios_sel != 0);
1557
1558 return 0;
1559}
1560
1561// ------------ adv options menu ------------
1562
1563#ifndef DRC_DISABLE
1564static const char h_cfg_noch[] = "Disables game-specific compatibility hacks";
1565static const char h_cfg_nosmc[] = "Will cause crashes when loading, break memcards";
1566static const char h_cfg_gteunn[] = "May cause graphical glitches";
1567static const char h_cfg_gteflgs[] = "Will cause graphical glitches";
1568#endif
1569static const char h_cfg_stalls[] = "Will cause some games to run too fast";
1570
1571static menu_entry e_menu_speed_hacks[] =
1572{
1573#ifndef DRC_DISABLE
1574 mee_onoff_h ("Disable compat hacks", 0, new_dynarec_hacks, NDHACK_NO_COMPAT_HACKS, h_cfg_noch),
1575 mee_onoff_h ("Disable SMC checks", 0, new_dynarec_hacks, NDHACK_NO_SMC_CHECK, h_cfg_nosmc),
1576 mee_onoff_h ("Assume GTE regs unneeded", 0, new_dynarec_hacks, NDHACK_GTE_UNNEEDED, h_cfg_gteunn),
1577 mee_onoff_h ("Disable GTE flags", 0, new_dynarec_hacks, NDHACK_GTE_NO_FLAGS, h_cfg_gteflgs),
1578#endif
1579 mee_onoff_h ("Disable CPU/GTE stalls", 0, menu_iopts[0], 1, h_cfg_stalls),
1580 mee_end,
1581};
1582
1583static int menu_loop_speed_hacks(int id, int keys)
1584{
1585 static int sel = 0;
1586 menu_iopts[0] = Config.DisableStalls;
1587 me_loop(e_menu_speed_hacks, &sel);
1588 Config.DisableStalls = menu_iopts[0];
1589 return 0;
1590}
1591
1592static const char *men_gpul[] = { "Auto", "Off", "On", NULL };
1593
1594static const char h_cfg_cpul[] = "Shows CPU usage in %";
1595static const char h_cfg_spu[] = "Shows active SPU channels\n"
1596 "(green: normal, red: fmod, blue: noise)";
1597static const char h_cfg_fl[] = "Frame Limiter keeps the game from running too fast";
1598static const char h_cfg_xa[] = "Disables XA sound, which can sometimes improve performance";
1599static const char h_cfg_cdda[] = "Disable CD Audio for a performance boost\n"
1600 "(proper .cue/.bin dump is needed otherwise)";
1601#ifndef DRC_DISABLE
1602static const char h_cfg_nodrc[] = "Disable dynamic recompiler and use interpreter\n"
1603 "Might be useful to overcome some dynarec bugs";
1604#endif
1605static const char h_cfg_shacks[] = "Breaks games but may give better performance";
1606static const char h_cfg_icache[] = "Support F1 games (only when dynarec is off)";
1607static const char h_cfg_exc[] = "Emulate some PSX's debug hw like breakpoints\n"
1608 "and exceptions (slow, interpreter only, keep off)";
1609static const char h_cfg_gpul[] = "Try enabling this if the game misses some graphics\n"
1610 "causes a performance hit";
1611static const char h_cfg_psxclk[] = "Over/under-clock the PSX, default is " DEFAULT_PSX_CLOCK_S "\n"
1612 "(adjust this if the game is too slow/too fast/hangs)";
1613
1614enum { AMO_XA, AMO_CDDA, AMO_IC, AMO_BP, AMO_CPU, AMO_GPUL };
1615
1616static menu_entry e_menu_adv_options[] =
1617{
1618 mee_onoff_h ("Show CPU load", 0, g_opts, OPT_SHOWCPU, h_cfg_cpul),
1619 mee_onoff_h ("Show SPU channels", 0, g_opts, OPT_SHOWSPU, h_cfg_spu),
1620 mee_onoff_h ("Disable Frame Limiter", 0, g_opts, OPT_NO_FRAMELIM, h_cfg_fl),
1621 mee_onoff_h ("Disable XA Decoding", 0, menu_iopts[AMO_XA], 1, h_cfg_xa),
1622 mee_onoff_h ("Disable CD Audio", 0, menu_iopts[AMO_CDDA], 1, h_cfg_cdda),
1623 mee_onoff_h ("ICache emulation", 0, menu_iopts[AMO_IC], 1, h_cfg_icache),
1624 mee_onoff_h ("BP exception emulation", 0, menu_iopts[AMO_BP], 1, h_cfg_exc),
1625 mee_enum_h ("GPU l-list slow walking",0, menu_iopts[AMO_GPUL], men_gpul, h_cfg_gpul),
1626#if !defined(DRC_DISABLE) || defined(LIGHTREC)
1627 mee_onoff_h ("Disable dynarec (slow!)",0, menu_iopts[AMO_CPU], 1, h_cfg_nodrc),
1628#endif
1629 mee_range_h ("PSX CPU clock, %", 0, psx_clock, 1, 500, h_cfg_psxclk),
1630 mee_handler_h ("[Speed hacks]", menu_loop_speed_hacks, h_cfg_shacks),
1631 mee_end,
1632};
1633
1634static int menu_loop_adv_options(int id, int keys)
1635{
1636 static int sel = 0;
1637 static struct {
1638 boolean *opt;
1639 int *mopt;
1640 } opts[] = {
1641 { &Config.Xa, &menu_iopts[AMO_XA] },
1642 { &Config.Cdda, &menu_iopts[AMO_CDDA] },
1643 { &Config.icache_emulation, &menu_iopts[AMO_IC] },
1644 { &Config.PreciseExceptions, &menu_iopts[AMO_BP] },
1645 { &Config.Cpu, &menu_iopts[AMO_CPU] },
1646 };
1647 int i;
1648 for (i = 0; i < ARRAY_SIZE(opts); i++)
1649 *opts[i].mopt = *opts[i].opt;
1650 menu_iopts[AMO_GPUL] = Config.GpuListWalking + 1;
1651
1652 me_loop(e_menu_adv_options, &sel);
1653
1654 for (i = 0; i < ARRAY_SIZE(opts); i++)
1655 *opts[i].opt = *opts[i].mopt;
1656 Config.GpuListWalking = menu_iopts[AMO_GPUL] - 1;
1657
1658 return 0;
1659}
1660
1661// ------------ options menu ------------
1662
1663static int mh_restore_defaults(int id, int keys)
1664{
1665 menu_set_defconfig();
1666 menu_update_msg("defaults restored");
1667 return 1;
1668}
1669
1670static const char *men_region[] = { "Auto", "NTSC", "PAL", NULL };
1671static const char *men_frameskip[] = { "Auto", "Off", "1", "2", "3", NULL };
1672/*
1673static const char *men_confirm_save[] = { "OFF", "writes", "loads", "both", NULL };
1674static const char h_confirm_save[] = "Ask for confirmation when overwriting save,\n"
1675 "loading state or both";
1676*/
1677static const char h_restore_def[] = "Switches back to default / recommended\n"
1678 "configuration";
1679static const char h_frameskip[] = "Warning: frameskip sometimes causes glitches\n";
1680
1681static menu_entry e_menu_options[] =
1682{
1683// mee_range ("Save slot", 0, state_slot, 0, 9),
1684// mee_enum_h ("Confirm savestate", 0, dummy, men_confirm_save, h_confirm_save),
1685 mee_enum_h ("Frameskip", 0, frameskip, men_frameskip, h_frameskip),
1686 mee_onoff ("Show FPS", 0, g_opts, OPT_SHOWFPS),
1687 mee_enum ("Region", 0, region, men_region),
1688 mee_range ("CPU clock", MA_OPT_CPU_CLOCKS, cpu_clock, 20, 5000),
1689#ifdef C64X_DSP
1690 mee_onoff ("Use C64x DSP for sound", MA_OPT_SPU_THREAD, spu_config.iUseThread, 1),
1691#else
1692 mee_onoff ("Threaded SPU", MA_OPT_SPU_THREAD, spu_config.iUseThread, 1),
1693#endif
1694 mee_handler_id("[Display]", MA_OPT_DISP_OPTS, menu_loop_gfx_options),
1695 mee_handler ("[BIOS/Plugins]", menu_loop_plugin_options),
1696 mee_handler ("[Advanced]", menu_loop_adv_options),
1697 mee_cust_nosave("Save global config", MA_OPT_SAVECFG, mh_savecfg, mgn_saveloadcfg),
1698 mee_cust_nosave("Save cfg for loaded game",MA_OPT_SAVECFG_GAME, mh_savecfg, mgn_saveloadcfg),
1699 mee_handler_h ("Restore default config", mh_restore_defaults, h_restore_def),
1700 mee_end,
1701};
1702
1703static int menu_loop_options(int id, int keys)
1704{
1705 static int sel = 0;
1706
1707 me_enable(e_menu_options, MA_OPT_CPU_CLOCKS, cpu_clock_st > 0);
1708 me_enable(e_menu_options, MA_OPT_SPU_THREAD, spu_config.iThreadAvail);
1709 me_enable(e_menu_options, MA_OPT_SAVECFG_GAME, ready_to_go && CdromId[0]);
1710
1711 me_loop(e_menu_options, &sel);
1712
1713 return 0;
1714}
1715
1716// ------------ debug menu ------------
1717
1718static void draw_frame_debug(GPUFreeze_t *gpuf, int x, int y)
1719{
1720 int w = min(g_menuscreen_w, 1024);
1721 int h = min(g_menuscreen_h, 512);
1722 u16 *d = g_menuscreen_ptr;
1723 u16 *s = (u16 *)gpuf->psxVRam + y * 1024 + x;
1724 char buff[64];
1725 int ty = 1;
1726
1727 gpuf->ulFreezeVersion = 1;
1728 if (GPU_freeze != NULL)
1729 GPU_freeze(1, gpuf);
1730
1731 for (; h > 0; h--, d += g_menuscreen_w, s += 1024)
1732 bgr555_to_rgb565(d, s, w * 2);
1733
1734 smalltext_out16(4, 1, "build: "__DATE__ " " __TIME__ " " REV, 0xe7fc);
1735 snprintf(buff, sizeof(buff), "GPU sr: %08x", gpuf->ulStatus);
1736 smalltext_out16(4, (ty += me_sfont_h), buff, 0xe7fc);
1737 snprintf(buff, sizeof(buff), "PC/SP: %08x %08x", psxRegs.pc, psxRegs.GPR.n.sp);
1738 smalltext_out16(4, (ty += me_sfont_h), buff, 0xe7fc);
1739}
1740
1741static void debug_menu_loop(void)
1742{
1743 int inp, df_x = 0, df_y = 0;
1744 GPUFreeze_t *gpuf;
1745
1746 gpuf = malloc(sizeof(*gpuf));
1747 if (gpuf == NULL)
1748 return;
1749
1750 while (1)
1751 {
1752 menu_draw_begin(0, 1);
1753 draw_frame_debug(gpuf, df_x, df_y);
1754 menu_draw_end();
1755
1756 inp = in_menu_wait(PBTN_MOK|PBTN_MBACK|PBTN_MA2|PBTN_MA3|PBTN_L|PBTN_R |
1757 PBTN_UP|PBTN_DOWN|PBTN_LEFT|PBTN_RIGHT, NULL, 10);
1758 if (inp & PBTN_MBACK) break;
1759 else if (inp & PBTN_UP) { if (df_y > 0) df_y--; }
1760 else if (inp & PBTN_DOWN) { if (df_y < 512 - g_menuscreen_h) df_y++; }
1761 else if (inp & PBTN_LEFT) { if (df_x > 0) df_x -= 2; }
1762 else if (inp & PBTN_RIGHT) { if (df_x < 1024 - g_menuscreen_w) df_x += 2; }
1763 }
1764
1765 free(gpuf);
1766}
1767
1768// --------- memcard manager ---------
1769
1770static void draw_mc_icon(int dx, int dy, const u16 *s)
1771{
1772 u16 *d;
1773 int x, y, l, p;
1774
1775 d = (u16 *)g_menuscreen_ptr + g_menuscreen_w * dy + dx;
1776
1777 for (y = 0; y < 16; y++, s += 16) {
1778 for (l = 0; l < 2; l++, d += g_menuscreen_w) {
1779 for (x = 0; x < 16; x++) {
1780 p = s[x];
1781 d[x*2] = d[x*2 + 1] = ((p & 0x7c00) >> 10)
1782 | ((p & 0x03e0) << 1) | ((p & 0x1f) << 11);
1783 }
1784 }
1785 }
1786}
1787
1788static void draw_mc_bg(void)
1789{
1790 McdBlock *blocks1, *blocks2;
1791 int maxicons = 15;
1792 int i, y, row2;
1793
1794 blocks1 = malloc(15 * sizeof(blocks1[0]));
1795 blocks2 = malloc(15 * sizeof(blocks1[0]));
1796 if (blocks1 == NULL || blocks2 == NULL)
1797 goto out;
1798
1799 for (i = 0; i < 15; i++) {
1800 GetMcdBlockInfo(1, i + 1, &blocks1[i]);
1801 GetMcdBlockInfo(2, i + 1, &blocks2[i]);
1802 }
1803
1804 menu_draw_begin(1, 1);
1805
1806 memcpy(g_menuscreen_ptr, g_menubg_src_ptr, g_menuscreen_w * g_menuscreen_h * 2);
1807
1808 y = g_menuscreen_h / 2 - 15 * 32 / 2;
1809 if (y < 0) {
1810 // doesn't fit..
1811 y = 0;
1812 maxicons = g_menuscreen_h / 32;
1813 }
1814
1815 row2 = g_menuscreen_w / 2;
1816 for (i = 0; i < maxicons; i++) {
1817 draw_mc_icon(8, y + i * 32, (u16 *)blocks1[i].Icon);
1818 smalltext_out16(10+32, y + i * 32 + 8, blocks1[i].sTitle, 0xf71e);
1819
1820 draw_mc_icon(row2 + 8, y + i * 32, (u16 *)blocks2[i].Icon);
1821 smalltext_out16(row2 + 10+32, y + i * 32 + 8, blocks2[i].sTitle, 0xf71e);
1822 }
1823
1824 menu_darken_bg(g_menubg_ptr, g_menuscreen_ptr, g_menuscreen_w * g_menuscreen_h, 0);
1825
1826 menu_draw_end();
1827out:
1828 free(blocks1);
1829 free(blocks2);
1830}
1831
1832static void handle_memcard_sel(void)
1833{
1834 strcpy(Config.Mcd1, "none");
1835 if (memcard1_sel != 0)
1836 snprintf(Config.Mcd1, sizeof(Config.Mcd1), ".%s%s", MEMCARD_DIR, memcards[memcard1_sel]);
1837 strcpy(Config.Mcd2, "none");
1838 if (memcard2_sel != 0)
1839 snprintf(Config.Mcd2, sizeof(Config.Mcd2), ".%s%s", MEMCARD_DIR, memcards[memcard2_sel]);
1840 LoadMcds(Config.Mcd1, Config.Mcd2);
1841 draw_mc_bg();
1842}
1843
1844static menu_entry e_memcard_options[] =
1845{
1846 mee_enum("Memory card 1", 0, memcard1_sel, memcards),
1847 mee_enum("Memory card 2", 0, memcard2_sel, memcards),
1848 mee_end,
1849};
1850
1851static int menu_loop_memcards(int id, int keys)
1852{
1853 static int sel = 0;
1854 char *p;
1855 int i;
1856
1857 memcard1_sel = memcard2_sel = 0;
1858 p = strrchr(Config.Mcd1, '/');
1859 if (p != NULL)
1860 for (i = 0; memcards[i] != NULL; i++)
1861 if (strcmp(p + 1, memcards[i]) == 0)
1862 { memcard1_sel = i; break; }
1863 p = strrchr(Config.Mcd2, '/');
1864 if (p != NULL)
1865 for (i = 0; memcards[i] != NULL; i++)
1866 if (strcmp(p + 1, memcards[i]) == 0)
1867 { memcard2_sel = i; break; }
1868
1869 me_loop_d(e_memcard_options, &sel, handle_memcard_sel, NULL);
1870
1871 memcpy(g_menubg_ptr, g_menubg_src_ptr, g_menuscreen_w * g_menuscreen_h * 2);
1872
1873 return 0;
1874}
1875
1876// ------------ cheats menu ------------
1877
1878static void draw_cheatlist(int sel)
1879{
1880 int max_cnt, start, i, pos, active;
1881
1882 max_cnt = g_menuscreen_h / me_sfont_h;
1883 start = max_cnt / 2 - sel;
1884
1885 menu_draw_begin(1, 1);
1886
1887 for (i = 0; i < NumCheats; i++) {
1888 pos = start + i;
1889 if (pos < 0) continue;
1890 if (pos >= max_cnt) break;
1891 active = Cheats[i].Enabled;
1892 smalltext_out16(14, pos * me_sfont_h,
1893 active ? "ON " : "OFF", active ? 0xfff6 : 0xffff);
1894 smalltext_out16(14 + me_sfont_w*4, pos * me_sfont_h,
1895 Cheats[i].Descr, active ? 0xfff6 : 0xffff);
1896 }
1897 pos = start + i;
1898 if (pos < max_cnt)
1899 smalltext_out16(14, pos * me_sfont_h, "done", 0xffff);
1900
1901 text_out16(5, max_cnt / 2 * me_sfont_h, ">");
1902 menu_draw_end();
1903}
1904
1905static void menu_loop_cheats(void)
1906{
1907 static int menu_sel = 0;
1908 int inp;
1909
1910 for (;;)
1911 {
1912 draw_cheatlist(menu_sel);
1913 inp = in_menu_wait(PBTN_UP|PBTN_DOWN|PBTN_LEFT|PBTN_RIGHT|PBTN_L|PBTN_R
1914 |PBTN_MOK|PBTN_MBACK, NULL, 33);
1915 if (inp & PBTN_UP ) { menu_sel--; if (menu_sel < 0) menu_sel = NumCheats; }
1916 if (inp & PBTN_DOWN) { menu_sel++; if (menu_sel > NumCheats) menu_sel = 0; }
1917 if (inp &(PBTN_LEFT|PBTN_L)) { menu_sel-=10; if (menu_sel < 0) menu_sel = 0; }
1918 if (inp &(PBTN_RIGHT|PBTN_R)) { menu_sel+=10; if (menu_sel > NumCheats) menu_sel = NumCheats; }
1919 if (inp & PBTN_MOK) { // action
1920 if (menu_sel < NumCheats)
1921 Cheats[menu_sel].Enabled = !Cheats[menu_sel].Enabled;
1922 else break;
1923 }
1924 if (inp & PBTN_MBACK)
1925 break;
1926 }
1927}
1928
1929// --------- main menu help ----------
1930
1931static void menu_bios_warn(void)
1932{
1933 int inp;
1934 static const char msg[] =
1935 "You don't seem to have copied any BIOS\n"
1936 "files to\n"
1937 MENU_BIOS_PATH "\n\n"
1938
1939 "While many games work fine with fake\n"
1940 "(HLE) BIOS, others (like MGS and FF8)\n"
1941 "require BIOS to work.\n"
1942 "After copying the file, you'll also need\n"
1943 "to select it in the emu's menu:\n"
1944 "options->[BIOS/Plugins]\n\n"
1945 "The file is usually named SCPH1001.BIN,\n"
1946 "but other not compressed files can be\n"
1947 "used too.\n\n"
1948 "Press %s or %s to continue";
1949 char tmp_msg[sizeof(msg) + 64];
1950
1951 snprintf(tmp_msg, sizeof(tmp_msg), msg,
1952 in_get_key_name(-1, -PBTN_MOK), in_get_key_name(-1, -PBTN_MBACK));
1953 while (1)
1954 {
1955 draw_menu_message(tmp_msg, NULL);
1956
1957 inp = in_menu_wait(PBTN_MOK|PBTN_MBACK, NULL, 70);
1958 if (inp & (PBTN_MBACK|PBTN_MOK))
1959 return;
1960 }
1961}
1962
1963// ------------ main menu ------------
1964
1965static menu_entry e_menu_main[];
1966
1967static void draw_frame_main(void)
1968{
1969 struct tm *tmp;
1970 time_t ltime;
1971 int capacity;
1972 char ltime_s[16];
1973 char buff[64];
1974 char *out;
1975
1976 if (CdromId[0] != 0) {
1977 snprintf(buff, sizeof(buff), "%.32s/%.9s (running as %s, with %s)",
1978 get_cd_label(), CdromId, Config.PsxType ? "PAL" : "NTSC",
1979 Config.HLE ? "HLE" : "BIOS");
1980 smalltext_out16(4, 1, buff, 0x105f);
1981 }
1982
1983 if (ready_to_go) {
1984 capacity = plat_target_bat_capacity_get();
1985 ltime = time(NULL);
1986 tmp = localtime(&ltime);
1987 strftime(ltime_s, sizeof(ltime_s), "%H:%M", tmp);
1988 if (capacity >= 0) {
1989 snprintf(buff, sizeof(buff), "%s %3d%%", ltime_s, capacity);
1990 out = buff;
1991 }
1992 else
1993 out = ltime_s;
1994 smalltext_out16(4, 1 + me_sfont_h, out, 0x105f);
1995 }
1996}
1997
1998static void draw_frame_credits(void)
1999{
2000 smalltext_out16(4, 1, "build: " __DATE__ " " __TIME__ " " REV, 0xe7fc);
2001}
2002
2003static const char credits_text[] =
2004 "PCSX-ReARMed\n\n"
2005 "(C) 1999-2003 PCSX Team\n"
2006 "(C) 2005-2009 PCSX-df Team\n"
2007 "(C) 2009-2011 PCSX-Reloaded Team\n\n"
2008 "ARM recompiler (C) 2009-2011 Ari64\n"
2009#ifdef BUILTIN_GPU_NEON
2010 "ARM NEON GPU (c) 2011-2012 Exophase\n"
2011#endif
2012 "PEOpS GPU and SPU by Pete Bernert\n"
2013 " and the P.E.Op.S. team\n"
2014 "PCSX4ALL plugin by PCSX4ALL team\n"
2015 " Chui, Franxis, Unai\n\n"
2016 "integration, optimization and\n"
2017 " frontend (C) 2010-2015 notaz\n";
2018
2019static int reset_game(void)
2020{
2021 // sanity check
2022 if (bios_sel == 0 && !Config.HLE)
2023 return -1;
2024
2025 ClosePlugins();
2026 OpenPlugins();
2027 SysReset();
2028 if (Config.HLE) {
2029 if (LoadCdrom() == -1)
2030 return -1;
2031 }
2032 return 0;
2033}
2034
2035static int reload_plugins(const char *cdimg)
2036{
2037 pl_vout_buf = NULL;
2038
2039 ClosePlugins();
2040
2041 set_cd_image(cdimg);
2042 LoadPlugins();
2043 pcnt_hook_plugins();
2044 NetOpened = 0;
2045 if (OpenPlugins() == -1) {
2046 menu_update_msg("failed to open plugins");
2047 return -1;
2048 }
2049 plugin_call_rearmed_cbs();
2050
2051 cdrIsoMultidiskCount = 1;
2052 CdromId[0] = '\0';
2053 CdromLabel[0] = '\0';
2054
2055 return 0;
2056}
2057
2058static int run_bios(void)
2059{
2060 boolean origSlowBoot = Config.SlowBoot;
2061
2062 if (bios_sel == 0)
2063 return -1;
2064
2065 ready_to_go = 0;
2066 if (reload_plugins(NULL) != 0)
2067 return -1;
2068 Config.SlowBoot = 1;
2069 SysReset();
2070 Config.SlowBoot = origSlowBoot;
2071
2072 ready_to_go = 1;
2073 return 0;
2074}
2075
2076static int run_exe(void)
2077{
2078 const char *exts[] = { "exe", NULL };
2079 const char *fname;
2080
2081 fname = menu_loop_romsel(last_selected_fname,
2082 sizeof(last_selected_fname), exts, NULL);
2083 if (fname == NULL)
2084 return -1;
2085
2086 ready_to_go = 0;
2087 if (reload_plugins(NULL) != 0)
2088 return -1;
2089
2090 SysReset();
2091 if (Load(fname) != 0) {
2092 menu_update_msg("exe load failed, bad file?");
2093 printf("meh\n");
2094 return -1;
2095 }
2096
2097 ready_to_go = 1;
2098 return 0;
2099}
2100
2101static int run_cd_image(const char *fname)
2102{
2103 int autoload_state = g_autostateld_opt;
2104
2105 ready_to_go = 0;
2106 reload_plugins(fname);
2107
2108 // always autodetect, menu_sync_config will override as needed
2109 Config.PsxAuto = 1;
2110
2111 if (CheckCdrom() == -1) {
2112 // Only check the CD if we are starting the console with a CD
2113 ClosePlugins();
2114 menu_update_msg("unsupported/invalid CD image");
2115 return -1;
2116 }
2117
2118 SysReset();
2119
2120 // Read main executable directly from CDRom and start it
2121 if (LoadCdrom() == -1) {
2122 ClosePlugins();
2123 menu_update_msg("failed to load CD image");
2124 return -1;
2125 }
2126
2127 emu_on_new_cd(1);
2128 ready_to_go = 1;
2129
2130 if (autoload_state) {
2131 unsigned int newest = 0;
2132 int time, slot, newest_slot = -1;
2133
2134 for (slot = 0; slot < 10; slot++) {
2135 if (emu_check_save_file(slot, &time)) {
2136 if ((unsigned int)time > newest) {
2137 newest = time;
2138 newest_slot = slot;
2139 }
2140 }
2141 }
2142
2143 if (newest_slot >= 0) {
2144 lprintf("autoload slot %d\n", newest_slot);
2145 emu_load_state(newest_slot);
2146 }
2147 else {
2148 lprintf("no save to autoload.\n");
2149 }
2150 }
2151
2152 return 0;
2153}
2154
2155static int romsel_run(void)
2156{
2157 int prev_gpu, prev_spu;
2158 const char *fname;
2159
2160 fname = menu_loop_romsel(last_selected_fname,
2161 sizeof(last_selected_fname), filter_exts,
2162 optional_cdimg_filter);
2163 if (fname == NULL)
2164 return -1;
2165
2166 printf("selected file: %s\n", fname);
2167
2168 new_dynarec_clear_full();
2169
2170 if (run_cd_image(fname) != 0)
2171 return -1;
2172
2173 prev_gpu = gpu_plugsel;
2174 prev_spu = spu_plugsel;
2175 if (menu_load_config(1) != 0)
2176 menu_load_config(0);
2177
2178 // check for plugin changes, have to repeat
2179 // loading if game config changed plugins to reload them
2180 if (prev_gpu != gpu_plugsel || prev_spu != spu_plugsel) {
2181 printf("plugin change detected, reloading plugins..\n");
2182 if (run_cd_image(fname) != 0)
2183 return -1;
2184 }
2185
2186 strcpy(last_selected_fname, fname);
2187 menu_do_last_cd_img(0);
2188 return 0;
2189}
2190
2191static int swap_cd_image(void)
2192{
2193 const char *fname;
2194
2195 fname = menu_loop_romsel(last_selected_fname,
2196 sizeof(last_selected_fname), filter_exts,
2197 optional_cdimg_filter);
2198 if (fname == NULL)
2199 return -1;
2200
2201 printf("selected file: %s\n", fname);
2202
2203 CdromId[0] = '\0';
2204 CdromLabel[0] = '\0';
2205
2206 set_cd_image(fname);
2207 if (ReloadCdromPlugin() < 0) {
2208 menu_update_msg("failed to load cdr plugin");
2209 return -1;
2210 }
2211 if (CDR_open() < 0) {
2212 menu_update_msg("failed to open cdr plugin");
2213 return -1;
2214 }
2215
2216 SetCdOpenCaseTime(time(NULL) + 2);
2217 LidInterrupt();
2218
2219 strcpy(last_selected_fname, fname);
2220 return 0;
2221}
2222
2223static int swap_cd_multidisk(void)
2224{
2225 cdrIsoMultidiskSelect++;
2226 CdromId[0] = '\0';
2227 CdromLabel[0] = '\0';
2228
2229 CDR_close();
2230 if (CDR_open() < 0) {
2231 menu_update_msg("failed to open cdr plugin");
2232 return -1;
2233 }
2234
2235 SetCdOpenCaseTime(time(NULL) + 2);
2236 LidInterrupt();
2237
2238 return 0;
2239}
2240
2241static void load_pcsx_cht(void)
2242{
2243 static const char *exts[] = { "cht", NULL };
2244 const char *fname;
2245 char msg[64];
2246
2247 fname = menu_loop_romsel(last_selected_fname,
2248 sizeof(last_selected_fname), exts, NULL);
2249 if (fname == NULL)
2250 return;
2251
2252 printf("selected cheat file: %s\n", fname);
2253 LoadCheats(fname);
2254
2255 if (NumCheats == 0 && NumCodes == 0)
2256 menu_update_msg("failed to load cheats");
2257 else {
2258 snprintf(msg, sizeof(msg), "%d cheat(s) loaded", NumCheats + NumCodes);
2259 menu_update_msg(msg);
2260 }
2261 me_enable(e_menu_main, MA_MAIN_CHEATS, ready_to_go && NumCheats);
2262}
2263
2264static int main_menu_handler(int id, int keys)
2265{
2266 switch (id)
2267 {
2268 case MA_MAIN_RESUME_GAME:
2269 if (ready_to_go)
2270 return 1;
2271 break;
2272 case MA_MAIN_SAVE_STATE:
2273 if (ready_to_go)
2274 return menu_loop_savestate(0);
2275 break;
2276 case MA_MAIN_LOAD_STATE:
2277 if (ready_to_go)
2278 return menu_loop_savestate(1);
2279 break;
2280 case MA_MAIN_RESET_GAME:
2281 if (ready_to_go && reset_game() == 0)
2282 return 1;
2283 break;
2284 case MA_MAIN_LOAD_ROM:
2285 if (romsel_run() == 0)
2286 return 1;
2287 break;
2288 case MA_MAIN_SWAP_CD:
2289 if (swap_cd_image() == 0)
2290 return 1;
2291 break;
2292 case MA_MAIN_SWAP_CD_MULTI:
2293 if (swap_cd_multidisk() == 0)
2294 return 1;
2295 break;
2296 case MA_MAIN_RUN_BIOS:
2297 if (run_bios() == 0)
2298 return 1;
2299 break;
2300 case MA_MAIN_RUN_EXE:
2301 if (run_exe() == 0)
2302 return 1;
2303 break;
2304 case MA_MAIN_CHEATS:
2305 menu_loop_cheats();
2306 break;
2307 case MA_MAIN_LOAD_CHEATS:
2308 load_pcsx_cht();
2309 break;
2310 case MA_MAIN_CREDITS:
2311 draw_menu_message(credits_text, draw_frame_credits);
2312 in_menu_wait(PBTN_MOK|PBTN_MBACK, NULL, 70);
2313 break;
2314 case MA_MAIN_EXIT:
2315 emu_core_ask_exit();
2316 return 1;
2317 default:
2318 lprintf("%s: something unknown selected\n", __FUNCTION__);
2319 break;
2320 }
2321
2322 return 0;
2323}
2324
2325static menu_entry e_menu_main2[] =
2326{
2327 mee_handler_id("Change CD image", MA_MAIN_SWAP_CD, main_menu_handler),
2328 mee_handler_id("Next multidisk CD", MA_MAIN_SWAP_CD_MULTI, main_menu_handler),
2329 mee_handler_id("Run BIOS", MA_MAIN_RUN_BIOS, main_menu_handler),
2330 mee_handler_id("Run EXE", MA_MAIN_RUN_EXE, main_menu_handler),
2331 mee_handler ("Memcard manager", menu_loop_memcards),
2332 mee_handler_id("Load PCSX cheats..", MA_MAIN_LOAD_CHEATS, main_menu_handler),
2333 mee_end,
2334};
2335
2336static int main_menu2_handler(int id, int keys)
2337{
2338 static int sel = 0;
2339
2340 me_enable(e_menu_main2, MA_MAIN_SWAP_CD, ready_to_go);
2341 me_enable(e_menu_main2, MA_MAIN_SWAP_CD_MULTI, ready_to_go && cdrIsoMultidiskCount > 1);
2342 me_enable(e_menu_main2, MA_MAIN_RUN_BIOS, bios_sel != 0);
2343 me_enable(e_menu_main2, MA_MAIN_LOAD_CHEATS, ready_to_go);
2344
2345 return me_loop_d(e_menu_main2, &sel, NULL, draw_frame_main);
2346}
2347
2348static const char h_extra[] = "Change CD, manage memcards..\n";
2349
2350static menu_entry e_menu_main[] =
2351{
2352 mee_label (""),
2353 mee_label (""),
2354 mee_handler_id("Resume game", MA_MAIN_RESUME_GAME, main_menu_handler),
2355 mee_handler_id("Save State", MA_MAIN_SAVE_STATE, main_menu_handler),
2356 mee_handler_id("Load State", MA_MAIN_LOAD_STATE, main_menu_handler),
2357 mee_handler_id("Reset game", MA_MAIN_RESET_GAME, main_menu_handler),
2358 mee_handler_id("Load CD image", MA_MAIN_LOAD_ROM, main_menu_handler),
2359 mee_handler ("Options", menu_loop_options),
2360 mee_handler ("Controls", menu_loop_keyconfig),
2361 mee_handler_id("Cheats", MA_MAIN_CHEATS, main_menu_handler),
2362 mee_handler_h ("Extra stuff", main_menu2_handler, h_extra),
2363 mee_handler_id("Credits", MA_MAIN_CREDITS, main_menu_handler),
2364 mee_handler_id("Exit", MA_MAIN_EXIT, main_menu_handler),
2365 mee_end,
2366};
2367
2368// ----------------------------
2369
2370static void menu_leave_emu(void);
2371
2372void menu_loop(void)
2373{
2374 static int warned_about_bios = 0;
2375 static int sel = 0;
2376
2377 menu_leave_emu();
2378
2379 if (config_save_counter == 0) {
2380 // assume first run
2381 if (bioses[1] != NULL) {
2382 // autoselect BIOS to make user's life easier
2383 snprintf(Config.Bios, sizeof(Config.Bios), "%s", bioses[1]);
2384 bios_sel = 1;
2385 }
2386 else if (!warned_about_bios) {
2387 menu_bios_warn();
2388 warned_about_bios = 1;
2389 }
2390 }
2391
2392 me_enable(e_menu_main, MA_MAIN_RESUME_GAME, ready_to_go);
2393 me_enable(e_menu_main, MA_MAIN_SAVE_STATE, ready_to_go && CdromId[0]);
2394 me_enable(e_menu_main, MA_MAIN_LOAD_STATE, ready_to_go && CdromId[0]);
2395 me_enable(e_menu_main, MA_MAIN_RESET_GAME, ready_to_go);
2396 me_enable(e_menu_main, MA_MAIN_CHEATS, ready_to_go && NumCheats);
2397
2398 in_set_config_int(0, IN_CFG_BLOCKING, 1);
2399
2400 do {
2401 me_loop_d(e_menu_main, &sel, NULL, draw_frame_main);
2402 } while (!ready_to_go && !g_emu_want_quit);
2403
2404 /* wait until menu, ok, back is released */
2405 while (in_menu_wait_any(NULL, 50) & (PBTN_MENU|PBTN_MOK|PBTN_MBACK))
2406 ;
2407
2408 in_set_config_int(0, IN_CFG_BLOCKING, 0);
2409
2410 menu_prepare_emu();
2411}
2412
2413static int qsort_strcmp(const void *p1, const void *p2)
2414{
2415 char * const *s1 = (char * const *)p1;
2416 char * const *s2 = (char * const *)p2;
2417 return strcasecmp(*s1, *s2);
2418}
2419
2420static void scan_bios_plugins(void)
2421{
2422 char fname[MAXPATHLEN];
2423 struct dirent *ent;
2424 int bios_i, gpu_i, spu_i, mc_i;
2425 char *p;
2426 DIR *dir;
2427
2428 bioses[0] = "HLE";
2429 gpu_plugins[0] = "builtin_gpu";
2430 spu_plugins[0] = "builtin_spu";
2431 memcards[0] = "(none)";
2432 bios_i = gpu_i = spu_i = mc_i = 1;
2433
2434 snprintf(fname, sizeof(fname), "%s/", Config.BiosDir);
2435 dir = opendir(fname);
2436 if (dir == NULL) {
2437 perror("scan_bios_plugins bios opendir");
2438 goto do_plugins;
2439 }
2440
2441 while (1) {
2442 struct stat st;
2443
2444 errno = 0;
2445 ent = readdir(dir);
2446 if (ent == NULL) {
2447 if (errno != 0)
2448 perror("readdir");
2449 break;
2450 }
2451
2452 if (ent->d_type != DT_REG && ent->d_type != DT_LNK)
2453 continue;
2454
2455 snprintf(fname, sizeof(fname), "%s/%s", Config.BiosDir, ent->d_name);
2456 if (stat(fname, &st) != 0
2457 || (st.st_size != 512*1024 && st.st_size != 4*1024*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
2703void menu_update_msg(const char *msg)
2704{
2705 strncpy(menu_error_msg, msg, sizeof(menu_error_msg));
2706 menu_error_msg[sizeof(menu_error_msg) - 1] = 0;
2707
2708 menu_error_time = plat_get_ticks_ms();
2709 lprintf("msg: %s\n", menu_error_msg);
2710}
2711
2712void menu_finish(void)
2713{
2714 if (cpu_clock_st > 0)
2715 plat_target_cpu_clock_set(cpu_clock_st);
2716}