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