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