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