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