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