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