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