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