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