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