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