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