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