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