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