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