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