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