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