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