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