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