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