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