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