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