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