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