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