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