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