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