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