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