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