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