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