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