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