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