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