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