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