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