frontend: few more pollux/caanoo tweaks
[pcsx_rearmed.git] / frontend / menu.c
CommitLineData
69af03a2 1/*
201c21e2 2 * (C) GraÅžvydas "notaz" Ignotas, 2010-2011
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
11#include <stdio.h>
12#include <string.h>
3c70c47b 13#include <errno.h>
bbd837c6 14#include <dlfcn.h>
61363062 15#include <zlib.h>
69af03a2 16
c5061935 17#include "main.h"
3c70c47b 18#include "menu.h"
69af03a2 19#include "config.h"
201c21e2 20#include "plugin.h"
69af03a2 21#include "plugin_lib.h"
22#include "omap.h"
55b0eeea 23#include "plat.h"
c22b95ab 24#include "pcnt.h"
a80ae4a0 25#include "cspace.h"
69af03a2 26#include "common/plat.h"
ef94866c 27#include "common/input.h"
28#include "linux/in_evdev.h"
69af03a2 29#include "../libpcsxcore/misc.h"
1df403c5 30#include "../libpcsxcore/cdrom.h"
799b0b87 31#include "../libpcsxcore/psemu_plugin_defs.h"
dc990066 32#include "../libpcsxcore/new_dynarec/new_dynarec.h"
4c08b9e7 33#include "../plugins/dfinput/main.h"
3c70c47b 34#include "revision.h"
69af03a2 35
69af03a2 36#define array_size(x) (sizeof(x) / sizeof(x[0]))
37
38typedef enum
39{
40 MA_NONE = 1,
41 MA_MAIN_RESUME_GAME,
42 MA_MAIN_SAVE_STATE,
43 MA_MAIN_LOAD_STATE,
44 MA_MAIN_RESET_GAME,
45 MA_MAIN_LOAD_ROM,
1df403c5 46 MA_MAIN_SWAP_CD,
e16a7e51 47 MA_MAIN_RUN_BIOS,
51f77282 48 MA_MAIN_RUN_EXE,
69af03a2 49 MA_MAIN_CONTROLS,
50 MA_MAIN_CREDITS,
51 MA_MAIN_EXIT,
52 MA_CTRL_PLAYER1,
53 MA_CTRL_PLAYER2,
54 MA_CTRL_EMU,
55 MA_CTRL_DEV_FIRST,
56 MA_CTRL_DEV_NEXT,
55b0eeea 57 MA_CTRL_NUBS_BTNS,
58 MA_CTRL_DEADZONE,
69af03a2 59 MA_CTRL_DONE,
60 MA_OPT_SAVECFG,
61 MA_OPT_SAVECFG_GAME,
62 MA_OPT_CPU_CLOCKS,
3c70c47b 63 MA_OPT_FILTERING,
55b0eeea 64 MA_OPT_DISP_OPTS,
69af03a2 65} menu_id;
66
3c70c47b 67enum {
68 SCALE_1_1,
69 SCALE_4_3,
a185be70 70 SCALE_4_3v2,
3c70c47b 71 SCALE_FULLSCREEN,
72 SCALE_CUSTOM,
73};
69af03a2 74
bd6267e6 75static int last_psx_w, last_psx_h, last_psx_bpp;
ea4a16e7 76static int scaling, filter, cpu_clock, cpu_clock_st, volume_boost, frameskip;
69af03a2 77static char rom_fname_reload[MAXPATHLEN];
78static char last_selected_fname[MAXPATHLEN];
4c08b9e7 79static int warned_about_bios, region, in_type_sel1, in_type_sel2;
51f77282 80static int memcard1_sel, memcard2_sel;
55b0eeea 81int g_opts, analog_deadzone;
bd6267e6 82
bd6267e6 83// sound plugin
84extern int iUseReverb;
85extern int iUseInterpolation;
86extern int iXAPitch;
87extern int iSPUIRQWait;
88extern int iUseTimer;
9e7a7352 89extern int iVolume;
bd6267e6 90
e6eb2066 91static const char *bioses[24];
bbd837c6 92static const char *gpu_plugins[16];
93static const char *spu_plugins[16];
51f77282 94static const char *memcards[32];
e6eb2066 95static int bios_sel, gpu_plugsel, spu_plugsel;
bbd837c6 96
bd6267e6 97
98static int min(int x, int y) { return x < y ? x : y; }
99static int max(int x, int y) { return x > y ? x : y; }
69af03a2 100
101void emu_make_path(char *buff, const char *end, int size)
102{
103 int pos, end_len;
104
105 end_len = strlen(end);
106 pos = plat_get_root_dir(buff, size);
107 strncpy(buff + pos, end, size - pos);
108 buff[size - 1] = 0;
109 if (pos + end_len > size - 1)
110 printf("Warning: path truncated: %s\n", buff);
111}
112
113static int emu_check_save_file(int slot)
114{
8f892648 115 int ret = emu_check_state(slot);
3c70c47b 116 return ret == 0 ? 1 : 0;
69af03a2 117}
118
8f892648 119static int emu_save_load_game(int load, int unused)
69af03a2 120{
3c70c47b 121 int ret;
122
33f56da1 123 if (load) {
8f892648 124 ret = emu_load_state(state_slot);
33f56da1 125
126 // reflect hle/bios mode from savestate
127 if (Config.HLE)
128 bios_sel = 0;
129 else if (bios_sel == 0 && bioses[1] != NULL)
130 // XXX: maybe find the right bios instead
131 bios_sel = 1;
132 }
3c70c47b 133 else
8f892648 134 ret = emu_save_state(state_slot);
3c70c47b 135
136 return ret;
69af03a2 137}
138
907b1e90 139// propagate menu settings to the emu vars
140static void menu_sync_config(void)
141{
ef94866c 142 static int allow_abs_only_old;
143
907b1e90 144 Config.PsxAuto = 1;
145 if (region > 0) {
146 Config.PsxAuto = 0;
147 Config.PsxType = region - 1;
148 }
4c08b9e7 149 switch (in_type_sel1) {
150 case 1: in_type1 = PSE_PAD_TYPE_ANALOGPAD; break;
151 case 2: in_type1 = PSE_PAD_TYPE_GUNCON; break;
152 default: in_type1 = PSE_PAD_TYPE_STANDARD;
153 }
154 switch (in_type_sel2) {
155 case 1: in_type2 = PSE_PAD_TYPE_ANALOGPAD; break;
156 case 2: in_type2 = PSE_PAD_TYPE_GUNCON; break;
157 default: in_type2 = PSE_PAD_TYPE_STANDARD;
158 }
ef94866c 159 if (in_evdev_allow_abs_only != allow_abs_only_old) {
55b0eeea 160 plat_rescan_inputs();
ef94866c 161 allow_abs_only_old = in_evdev_allow_abs_only;
162 }
907b1e90 163
9e7a7352 164 iVolume = 768 + 128 * volume_boost;
ea4a16e7 165 pl_rearmed_cbs.frameskip = frameskip - 1;
76f7048e 166 pl_timing_prepare(Config.PsxType);
907b1e90 167}
168
3c70c47b 169static void menu_set_defconfig(void)
170{
bce6b056 171 g_opts = 0;
3c70c47b 172 scaling = SCALE_4_3;
9e7a7352 173 volume_boost = 0;
ea4a16e7 174 frameskip = 0;
41f55c9f 175 analog_deadzone = 70;
bd6267e6 176
907b1e90 177 region = 0;
4c08b9e7 178 in_type_sel1 = in_type_sel2 = 0;
ef94866c 179 in_evdev_allow_abs_only = 0;
bd6267e6 180 Config.Xa = Config.Cdda = Config.Sio =
181 Config.SpuIrq = Config.RCntFix = Config.VSyncWA = 0;
4feed8d3 182 Config.CdrReschedule = 0;
bd6267e6 183
e64dc4c5 184 pl_rearmed_cbs.gpu_peops.iUseDither = 0;
185 pl_rearmed_cbs.gpu_peops.dwActFixes = 1<<7;
17a54a4a 186 pl_rearmed_cbs.gpu_unai.abe_hack =
187 pl_rearmed_cbs.gpu_unai.no_light =
188 pl_rearmed_cbs.gpu_unai.no_blend = 0;
bd6267e6 189
190 iUseReverb = 2;
191 iUseInterpolation = 1;
cdb31c95 192 iXAPitch = 0;
193 iSPUIRQWait = 1;
bd6267e6 194 iUseTimer = 2;
907b1e90 195
196 menu_sync_config();
3c70c47b 197}
198
4f3639fa 199#define CE_CONFIG_STR(val) \
200 { #val, 0, Config.val }
201
202#define CE_CONFIG_VAL(val) \
203 { #val, sizeof(Config.val), &Config.val }
204
205#define CE_STR(val) \
206 { #val, 0, val }
207
208#define CE_INTVAL(val) \
209 { #val, sizeof(val), &val }
210
e64dc4c5 211#define CE_INTVAL_P(val) \
212 { #val, sizeof(pl_rearmed_cbs.val), &pl_rearmed_cbs.val }
213
cdb31c95 214// 'versioned' var, used when defaults change
215#define CE_INTVAL_V(val, ver) \
216 { #val #ver, sizeof(val), &val }
217
ea4a16e7 218#define CE_INTVAL_PV(val, ver) \
219 { #val #ver, sizeof(pl_rearmed_cbs.val), &pl_rearmed_cbs.val }
220
4f3639fa 221static const struct {
222 const char *name;
223 size_t len;
224 void *val;
225} config_data[] = {
226 CE_CONFIG_STR(Bios),
227 CE_CONFIG_STR(Gpu),
228 CE_CONFIG_STR(Spu),
33716956 229// CE_CONFIG_STR(Cdr),
4f3639fa 230 CE_CONFIG_VAL(Xa),
231 CE_CONFIG_VAL(Sio),
232 CE_CONFIG_VAL(Mdec),
4f3639fa 233 CE_CONFIG_VAL(Cdda),
234 CE_CONFIG_VAL(Debug),
235 CE_CONFIG_VAL(PsxOut),
236 CE_CONFIG_VAL(SpuIrq),
237 CE_CONFIG_VAL(RCntFix),
238 CE_CONFIG_VAL(VSyncWA),
239 CE_CONFIG_VAL(Cpu),
4feed8d3 240 CE_CONFIG_VAL(CdrReschedule),
907b1e90 241 CE_INTVAL(region),
a185be70 242 CE_INTVAL_V(scaling, 2),
cd6e8d0f 243 CE_INTVAL(g_layer_x),
244 CE_INTVAL(g_layer_y),
245 CE_INTVAL(g_layer_w),
246 CE_INTVAL(g_layer_h),
4f3639fa 247 CE_INTVAL(filter),
248 CE_INTVAL(state_slot),
249 CE_INTVAL(cpu_clock),
250 CE_INTVAL(g_opts),
4c08b9e7 251 CE_INTVAL(in_type_sel1),
252 CE_INTVAL(in_type_sel2),
55b0eeea 253 CE_INTVAL(analog_deadzone),
ea4a16e7 254 CE_INTVAL_V(frameskip, 2),
e64dc4c5 255 CE_INTVAL_P(gpu_peops.iUseDither),
256 CE_INTVAL_P(gpu_peops.dwActFixes),
17a54a4a 257 CE_INTVAL_P(gpu_unai.abe_hack),
258 CE_INTVAL_P(gpu_unai.no_light),
259 CE_INTVAL_P(gpu_unai.no_blend),
ffdbda29 260 CE_INTVAL_V(iUseReverb, 3),
261 CE_INTVAL_V(iXAPitch, 3),
262 CE_INTVAL_V(iUseInterpolation, 3),
263 CE_INTVAL_V(iSPUIRQWait, 3),
264 CE_INTVAL_V(iUseTimer, 3),
4f5a1b2a 265 CE_INTVAL(warned_about_bios),
ef94866c 266 CE_INTVAL(in_evdev_allow_abs_only),
9e7a7352 267 CE_INTVAL(volume_boost),
4f3639fa 268};
269
1bd9ee68 270static char *get_cd_label(void)
cd6e8d0f 271{
1bd9ee68 272 static char trimlabel[33];
cd6e8d0f 273 int j;
274
275 strncpy(trimlabel, CdromLabel, 32);
276 trimlabel[32] = 0;
277 for (j = 31; j >= 0; j--)
278 if (trimlabel[j] == ' ')
279 trimlabel[j] = 0;
280
1bd9ee68 281 return trimlabel;
282}
283
284static void make_cfg_fname(char *buf, size_t size, int is_game)
285{
cd6e8d0f 286 if (is_game)
1bd9ee68 287 snprintf(buf, size, "." PCSX_DOT_DIR "cfg/%.32s-%.9s.cfg", get_cd_label(), CdromId);
cd6e8d0f 288 else
bbd837c6 289 snprintf(buf, size, "." PCSX_DOT_DIR "%s", cfgfile_basename);
cd6e8d0f 290}
291
43bca6fb 292static void keys_write_all(FILE *f);
293
3c70c47b 294static int menu_write_config(int is_game)
295{
4f3639fa 296 char cfgfile[MAXPATHLEN];
297 FILE *f;
298 int i;
299
cd6e8d0f 300 make_cfg_fname(cfgfile, sizeof(cfgfile), is_game);
4f3639fa 301 f = fopen(cfgfile, "w");
302 if (f == NULL) {
bbd837c6 303 printf("menu_write_config: failed to open: %s\n", cfgfile);
4f3639fa 304 return -1;
305 }
306
307 for (i = 0; i < ARRAY_SIZE(config_data); i++) {
308 fprintf(f, "%s = ", config_data[i].name);
309 switch (config_data[i].len) {
310 case 0:
311 fprintf(f, "%s\n", (char *)config_data[i].val);
312 break;
313 case 1:
314 fprintf(f, "%x\n", *(u8 *)config_data[i].val);
315 break;
316 case 2:
317 fprintf(f, "%x\n", *(u16 *)config_data[i].val);
318 break;
319 case 4:
320 fprintf(f, "%x\n", *(u32 *)config_data[i].val);
321 break;
322 default:
323 printf("menu_write_config: unhandled len %d for %s\n",
324 config_data[i].len, config_data[i].name);
325 break;
326 }
327 }
328
329 if (!is_game)
330 fprintf(f, "lastcdimg = %s\n", last_selected_fname);
331
43bca6fb 332 keys_write_all(f);
4f3639fa 333 fclose(f);
43bca6fb 334
4f3639fa 335 return 0;
336}
337
338static void parse_str_val(char *cval, const char *src)
339{
340 char *tmp;
341 strncpy(cval, src, MAXPATHLEN);
342 cval[MAXPATHLEN - 1] = 0;
343 tmp = strchr(cval, '\n');
344 if (tmp == NULL)
345 tmp = strchr(cval, '\r');
346 if (tmp != NULL)
347 *tmp = 0;
3c70c47b 348}
349
43bca6fb 350static void keys_load_all(const char *cfg);
351
3c70c47b 352static int menu_load_config(int is_game)
69af03a2 353{
4f3639fa 354 char cfgfile[MAXPATHLEN];
355 int i, ret = -1;
356 long size;
357 char *cfg;
358 FILE *f;
359
cd6e8d0f 360 make_cfg_fname(cfgfile, sizeof(cfgfile), is_game);
4f3639fa 361 f = fopen(cfgfile, "r");
362 if (f == NULL) {
bbd837c6 363 printf("menu_load_config: failed to open: %s\n", cfgfile);
4f3639fa 364 return -1;
365 }
366
367 fseek(f, 0, SEEK_END);
368 size = ftell(f);
369 if (size <= 0) {
370 printf("bad size %ld: %s\n", size, cfgfile);
371 goto fail;
372 }
373
374 cfg = malloc(size + 1);
375 if (cfg == NULL)
376 goto fail;
377
378 fseek(f, 0, SEEK_SET);
379 if (fread(cfg, 1, size, f) != size) {
380 printf("failed to read: %s\n", cfgfile);
381 goto fail_read;
382 }
383 cfg[size] = 0;
384
385 for (i = 0; i < ARRAY_SIZE(config_data); i++) {
386 char *tmp, *tmp2;
387 u32 val;
388
389 tmp = strstr(cfg, config_data[i].name);
390 if (tmp == NULL)
391 continue;
392 tmp += strlen(config_data[i].name);
393 if (strncmp(tmp, " = ", 3) != 0)
394 continue;
395 tmp += 3;
396
397 if (config_data[i].len == 0) {
398 parse_str_val(config_data[i].val, tmp);
399 continue;
400 }
401
402 tmp2 = NULL;
403 val = strtoul(tmp, &tmp2, 16);
404 if (tmp2 == NULL || tmp == tmp2)
405 continue; // parse failed
406
407 switch (config_data[i].len) {
408 case 1:
409 *(u8 *)config_data[i].val = val;
410 break;
411 case 2:
412 *(u16 *)config_data[i].val = val;
413 break;
414 case 4:
415 *(u32 *)config_data[i].val = val;
416 break;
417 default:
418 printf("menu_load_config: unhandled len %d for %s\n",
419 config_data[i].len, config_data[i].name);
420 break;
421 }
422 }
423
424 if (!is_game) {
425 char *tmp = strstr(cfg, "lastcdimg = ");
426 if (tmp != NULL) {
427 tmp += 12;
428 parse_str_val(last_selected_fname, tmp);
429 }
430 }
431
907b1e90 432 menu_sync_config();
433
bbd837c6 434 // sync plugins
e6eb2066 435 for (i = bios_sel = 0; bioses[i] != NULL; i++)
436 if (strcmp(Config.Bios, bioses[i]) == 0)
437 { bios_sel = i; break; }
438
bbd837c6 439 for (i = gpu_plugsel = 0; gpu_plugins[i] != NULL; i++)
440 if (strcmp(Config.Gpu, gpu_plugins[i]) == 0)
441 { gpu_plugsel = i; break; }
442
443 for (i = spu_plugsel = 0; spu_plugins[i] != NULL; i++)
444 if (strcmp(Config.Spu, spu_plugins[i]) == 0)
445 { spu_plugsel = i; break; }
446
43bca6fb 447 keys_load_all(cfg);
bbd837c6 448 ret = 0;
4f3639fa 449fail_read:
450 free(cfg);
451fail:
452 fclose(f);
453 return ret;
69af03a2 454}
455
9564e73d 456// rrrr rggg gggb bbbb
457static unsigned short fname2color(const char *fname)
458{
f246f461 459 static const char *cdimg_exts[] = { ".bin", ".img", ".mdf", ".iso", ".cue", ".z", ".bz", ".znx", ".pbp" };
c22b95ab 460 static const char *other_exts[] = { ".ccd", ".toc", ".mds", ".sub", ".table", ".index", ".sbi" };
9564e73d 461 const char *ext = strrchr(fname, '.');
462 int i;
463
464 if (ext == NULL)
465 return 0xffff;
466 for (i = 0; i < array_size(cdimg_exts); i++)
467 if (strcasecmp(ext, cdimg_exts[i]) == 0)
468 return 0x7bff;
469 for (i = 0; i < array_size(other_exts); i++)
470 if (strcasecmp(ext, other_exts[i]) == 0)
471 return 0xa514;
472 return 0xffff;
473}
474
61363062 475static void draw_savestate_bg(int slot);
476
f246f461 477static const char *filter_exts[] = {
478 ".mp3", ".MP3", ".txt", ".htm", "html", ".jpg", ".pnd"
479};
480
bd6267e6 481#define MENU_ALIGN_LEFT
55b0eeea 482#ifdef __ARM_ARCH_7A__ // assume hires device
483#define MENU_X2 1
484#else
485#define MENU_X2 0
486#endif
487
3c70c47b 488#define menu_init menu_init_common
69af03a2 489#include "common/menu.c"
3c70c47b 490#undef menu_init
69af03a2 491
61363062 492// a bit of black magic here
493static void draw_savestate_bg(int slot)
494{
61363062 495 static const int psx_widths[8] = { 256, 368, 320, 384, 512, 512, 640, 640 };
496 int x, y, w, h;
497 char fname[MAXPATHLEN];
498 GPUFreeze_t *gpu;
499 u16 *s, *d;
500 gzFile f;
501 int ret;
502 u32 tmp;
503
504 ret = get_state_filename(fname, sizeof(fname), slot);
505 if (ret != 0)
506 return;
507
508 f = gzopen(fname, "rb");
509 if (f == NULL)
510 return;
511
512 if (gzseek(f, 0x29933d, SEEK_SET) != 0x29933d) {
513 fprintf(stderr, "gzseek failed\n");
514 gzclose(f);
515 return;
516 }
517
518 gpu = malloc(sizeof(*gpu));
519 if (gpu == NULL) {
520 gzclose(f);
521 return;
522 }
523
524 ret = gzread(f, gpu, sizeof(*gpu));
525 gzclose(f);
526 if (ret != sizeof(*gpu)) {
527 fprintf(stderr, "gzread failed\n");
528 goto out;
529 }
530
531 memcpy(g_menubg_ptr, g_menubg_src_ptr, g_menuscreen_w * g_menuscreen_h * 2);
532
ff847a2e 533 if (gpu->ulStatus & 0x800000)
534 goto out; // disabled
61363062 535
536 x = gpu->ulControl[5] & 0x3ff;
537 y = (gpu->ulControl[5] >> 10) & 0x1ff;
ff847a2e 538 s = (u16 *)gpu->psxVRam + y * 1024 + (x & ~1);
61363062 539 w = psx_widths[(gpu->ulStatus >> 16) & 7];
540 tmp = gpu->ulControl[7];
541 h = ((tmp >> 10) & 0x3ff) - (tmp & 0x3ff);
542 if (gpu->ulStatus & 0x80000) // doubleheight
543 h *= 2;
544
545 x = max(0, g_menuscreen_w - w) & ~3;
546 y = max(0, g_menuscreen_h / 2 - h / 2);
547 w = min(g_menuscreen_w, w);
548 h = min(g_menuscreen_h, h);
549 d = (u16 *)g_menubg_ptr + g_menuscreen_w * y + x;
550
55b0eeea 551 for (; h > 0; h--, d += g_menuscreen_w, s += 1024) {
ff847a2e 552 if (gpu->ulStatus & 0x200000)
553 bgr888_to_rgb565(d, s, w * 3);
554 else
555 bgr555_to_rgb565(d, s, w * 2);
55b0eeea 556#ifndef __ARM_ARCH_7A__
557 // better darken this on small screens
558 menu_darken_bg(d, d, w * 2, 0);
559#endif
560 }
61363062 561
562out:
563 free(gpu);
564}
565
55b0eeea 566// ---------- XXX: pandora specific -----------
3c70c47b 567
568static const char pnd_script_base[] = "sudo -n /usr/pandora/scripts";
569static char **pnd_filter_list;
570
3c70c47b 571static void apply_filter(int which)
572{
4f3639fa 573 static int old = -1;
3c70c47b 574 char buf[128];
575 int i;
576
4f3639fa 577 if (pnd_filter_list == NULL || which == old)
3c70c47b 578 return;
579
580 for (i = 0; i < which; i++)
581 if (pnd_filter_list[i] == NULL)
582 return;
583
584 if (pnd_filter_list[i] == NULL)
585 return;
586
587 snprintf(buf, sizeof(buf), "%s/op_videofir.sh %s", pnd_script_base, pnd_filter_list[i]);
588 system(buf);
4f3639fa 589 old = which;
3c70c47b 590}
591
cefe86b7 592static void apply_lcdrate(int pal)
593{
594 static int old = -1;
595 char buf[128];
596
597 if (pal == old)
598 return;
599
600 snprintf(buf, sizeof(buf), "%s/op_lcdrate.sh %d",
601 pnd_script_base, pal ? 50 : 60);
602 system(buf);
603 old = pal;
604}
605
3c70c47b 606static menu_entry e_menu_gfx_options[];
607
608static void pnd_menu_init(void)
609{
610 struct dirent *ent;
611 int i, count = 0;
612 char **mfilters;
1bd9ee68 613 char buff[64];
3c70c47b 614 DIR *dir;
615
55b0eeea 616 cpu_clock_st = cpu_clock = plat_cpu_clock_get();
3c70c47b 617
618 dir = opendir("/etc/pandora/conf/dss_fir");
619 if (dir == NULL) {
620 perror("filter opendir");
621 return;
622 }
623
624 while (1) {
625 errno = 0;
626 ent = readdir(dir);
627 if (ent == NULL) {
628 if (errno != 0)
629 perror("readdir");
630 break;
631 }
1bd9ee68 632
633 if (ent->d_type != DT_REG && ent->d_type != DT_LNK)
634 continue;
635
636 count++;
3c70c47b 637 }
638
639 if (count == 0)
640 return;
641
642 mfilters = calloc(count + 1, sizeof(mfilters[0]));
643 if (mfilters == NULL)
644 return;
645
646 rewinddir(dir);
647 for (i = 0; (ent = readdir(dir)); ) {
648 size_t len;
649
1bd9ee68 650 if (ent->d_type != DT_REG && ent->d_type != DT_LNK)
651 continue;
652
653 len = strlen(ent->d_name);
654
655 // skip pre-HF5 extra files
656 if (len >= 3 && strcmp(ent->d_name + len - 3, "_v3") == 0)
657 continue;
658 if (len >= 3 && strcmp(ent->d_name + len - 3, "_v5") == 0)
3c70c47b 659 continue;
660
1bd9ee68 661 // have to cut "_up_h" for pre-HF5
662 if (len > 5 && strcmp(ent->d_name + len - 5, "_up_h") == 0)
663 len -= 5;
664
3c70c47b 665 if (len > sizeof(buff) - 1)
666 continue;
667
668 strncpy(buff, ent->d_name, len);
669 buff[len] = 0;
670 mfilters[i] = strdup(buff);
671 if (mfilters[i] != NULL)
672 i++;
673 }
674 closedir(dir);
675
676 i = me_id2offset(e_menu_gfx_options, MA_OPT_FILTERING);
677 e_menu_gfx_options[i].data = (void *)mfilters;
678 pnd_filter_list = mfilters;
69af03a2 679}
680
201c21e2 681void menu_finish(void)
682{
55b0eeea 683 plat_cpu_clock_apply(cpu_clock_st);
201c21e2 684}
685
69af03a2 686// -------------- key config --------------
687
688me_bind_action me_ctrl_actions[] =
689{
690 { "UP ", 1 << DKEY_UP},
691 { "DOWN ", 1 << DKEY_DOWN },
692 { "LEFT ", 1 << DKEY_LEFT },
693 { "RIGHT ", 1 << DKEY_RIGHT },
694 { "TRIANGLE", 1 << DKEY_TRIANGLE },
22a8a805 695 { "CIRCLE ", 1 << DKEY_CIRCLE },
69af03a2 696 { "CROSS ", 1 << DKEY_CROSS },
697 { "SQUARE ", 1 << DKEY_SQUARE },
698 { "L1 ", 1 << DKEY_L1 },
699 { "R1 ", 1 << DKEY_R1 },
700 { "L2 ", 1 << DKEY_L2 },
701 { "R2 ", 1 << DKEY_R2 },
43bca6fb 702 { "L3 ", 1 << DKEY_L3 },
703 { "R3 ", 1 << DKEY_R3 },
69af03a2 704 { "START ", 1 << DKEY_START },
705 { "SELECT ", 1 << DKEY_SELECT },
706 { NULL, 0 }
707};
708
709me_bind_action emuctrl_actions[] =
710{
8f892648 711 { "Save State ", 1 << SACTION_SAVE_STATE },
712 { "Load State ", 1 << SACTION_LOAD_STATE },
713 { "Prev Save Slot ", 1 << SACTION_PREV_SSLOT },
714 { "Next Save Slot ", 1 << SACTION_NEXT_SSLOT },
715 { "Toggle Frameskip ", 1 << SACTION_TOGGLE_FSKIP },
29a8c4f3 716 { "Take Screenshot ", 1 << SACTION_SCREENSHOT },
8f892648 717 { "Enter Menu ", 1 << SACTION_ENTER_MENU },
4c08b9e7 718 { "Gun Trigger ", 1 << SACTION_GUN_TRIGGER },
719 { "Gun A button ", 1 << SACTION_GUN_A },
720 { "Gun B button ", 1 << SACTION_GUN_B },
721 { "Gun Offscreen Trigger", 1 << SACTION_GUN_TRIGGER2 },
69af03a2 722 { NULL, 0 }
723};
724
43bca6fb 725static char *mystrip(char *str)
726{
727 int i, len;
728
729 len = strlen(str);
730 for (i = 0; i < len; i++)
731 if (str[i] != ' ') break;
732 if (i > 0) memmove(str, str + i, len - i + 1);
733
734 len = strlen(str);
735 for (i = len - 1; i >= 0; i--)
736 if (str[i] != ' ') break;
737 str[i+1] = 0;
738
739 return str;
740}
741
742static void get_line(char *d, size_t size, const char *s)
743{
744 const char *pe;
745 size_t len;
746
747 for (pe = s; *pe != '\r' && *pe != '\n' && *pe != 0; pe++)
748 ;
749 len = pe - s;
750 if (len > size - 1)
751 len = size - 1;
752 strncpy(d, s, len);
753 d[len] = 0;
754
755 mystrip(d);
756}
757
758static void keys_write_all(FILE *f)
759{
760 int d;
761
762 for (d = 0; d < IN_MAX_DEVS; d++)
763 {
764 const int *binds = in_get_dev_binds(d);
765 const char *name = in_get_dev_name(d, 0, 0);
766 int k, count = 0;
767
768 if (binds == NULL || name == NULL)
769 continue;
770
771 fprintf(f, "binddev = %s\n", name);
772 in_get_config(d, IN_CFG_BIND_COUNT, &count);
773
774 for (k = 0; k < count; k++)
775 {
776 int i, kbinds, mask;
777 char act[32];
778
779 act[0] = act[31] = 0;
780 name = in_get_key_name(d, k);
781
782 kbinds = binds[IN_BIND_OFFS(k, IN_BINDTYPE_PLAYER12)];
783 for (i = 0; kbinds && i < ARRAY_SIZE(me_ctrl_actions) - 1; i++) {
784 mask = me_ctrl_actions[i].mask;
785 if (mask & kbinds) {
786 strncpy(act, me_ctrl_actions[i].name, 31);
787 fprintf(f, "bind %s = player1 %s\n", name, mystrip(act));
788 kbinds &= ~mask;
789 }
790 mask = me_ctrl_actions[i].mask << 16;
791 if (mask & kbinds) {
792 strncpy(act, me_ctrl_actions[i].name, 31);
793 fprintf(f, "bind %s = player2 %s\n", name, mystrip(act));
794 kbinds &= ~mask;
795 }
796 }
797
798 kbinds = binds[IN_BIND_OFFS(k, IN_BINDTYPE_EMU)];
799 for (i = 0; kbinds && i < ARRAY_SIZE(emuctrl_actions) - 1; i++) {
800 mask = emuctrl_actions[i].mask;
801 if (mask & kbinds) {
802 strncpy(act, emuctrl_actions[i].name, 31);
803 fprintf(f, "bind %s = %s\n", name, mystrip(act));
804 kbinds &= ~mask;
805 }
806 }
807 }
808 }
809}
810
811static int parse_bind_val(const char *val, int *type)
812{
813 int i;
814
815 *type = IN_BINDTYPE_NONE;
816 if (val[0] == 0)
817 return 0;
818
819 if (strncasecmp(val, "player", 6) == 0)
820 {
821 int player, shift = 0;
822 player = atoi(val + 6) - 1;
823
824 if ((unsigned int)player > 1)
825 return -1;
826 if (player == 1)
827 shift = 16;
828
829 *type = IN_BINDTYPE_PLAYER12;
830 for (i = 0; me_ctrl_actions[i].name != NULL; i++) {
831 if (strncasecmp(me_ctrl_actions[i].name, val + 8, strlen(val + 8)) == 0)
832 return me_ctrl_actions[i].mask << shift;
833 }
834 }
835 for (i = 0; emuctrl_actions[i].name != NULL; i++) {
836 if (strncasecmp(emuctrl_actions[i].name, val, strlen(val)) == 0) {
837 *type = IN_BINDTYPE_EMU;
838 return emuctrl_actions[i].mask;
839 }
840 }
841
842 return -1;
843}
844
845static void keys_load_all(const char *cfg)
846{
847 char dev[256], key[128], *act;
848 const char *p;
849 int bind, bindtype;
850 int dev_id;
851
852 p = cfg;
853 while (p != NULL && (p = strstr(p, "binddev = ")) != NULL) {
854 p += 10;
855
856 get_line(dev, sizeof(dev), p);
857 dev_id = in_config_parse_dev(dev);
858 if (dev_id < 0) {
859 printf("input: can't handle dev: %s\n", dev);
860 continue;
861 }
862
863 in_unbind_all(dev_id, -1, -1);
864 while ((p = strstr(p, "bind"))) {
865 if (strncmp(p, "binddev = ", 10) == 0)
866 break;
867
868 p += 4;
869 if (*p != ' ') {
870 printf("input: parse error: %16s..\n", p);
871 continue;
872 }
873
874 get_line(key, sizeof(key), p);
875 act = strchr(key, '=');
876 if (act == NULL) {
877 printf("parse failed: %16s..\n", p);
878 continue;
879 }
880 *act = 0;
881 act++;
882 mystrip(key);
883 mystrip(act);
884
885 bind = parse_bind_val(act, &bindtype);
886 if (bind != -1 && bind != 0) {
8f892648 887 //printf("bind #%d '%s' %08x (%s)\n", dev_id, key, bind, act);
43bca6fb 888 in_config_bind_key(dev_id, key, bind, bindtype);
889 }
890 else
891 lprintf("config: unhandled action \"%s\"\n", act);
892 }
893 }
ef94866c 894 in_clean_binds();
43bca6fb 895}
896
69af03a2 897static int key_config_loop_wrap(int id, int keys)
898{
899 switch (id) {
900 case MA_CTRL_PLAYER1:
901 key_config_loop(me_ctrl_actions, array_size(me_ctrl_actions) - 1, 0);
902 break;
903 case MA_CTRL_PLAYER2:
904 key_config_loop(me_ctrl_actions, array_size(me_ctrl_actions) - 1, 1);
905 break;
906 case MA_CTRL_EMU:
907 key_config_loop(emuctrl_actions, array_size(emuctrl_actions) - 1, -1);
908 break;
909 default:
910 break;
911 }
912 return 0;
913}
914
915static const char *mgn_dev_name(int id, int *offs)
916{
917 const char *name = NULL;
918 static int it = 0;
919
920 if (id == MA_CTRL_DEV_FIRST)
921 it = 0;
922
923 for (; it < IN_MAX_DEVS; it++) {
924 name = in_get_dev_name(it, 1, 1);
925 if (name != NULL)
926 break;
927 }
928
929 it++;
930 return name;
931}
932
933static const char *mgn_saveloadcfg(int id, int *offs)
934{
935 return "";
936}
937
cd6e8d0f 938static int mh_savecfg(int id, int keys)
69af03a2 939{
cd6e8d0f 940 if (menu_write_config(id == MA_OPT_SAVECFG_GAME ? 1 : 0) == 0)
941 me_update_msg("config saved");
942 else
943 me_update_msg("failed to write config");
69af03a2 944
945 return 1;
946}
947
ef94866c 948static int mh_input_rescan(int id, int keys)
949{
950 //menu_sync_config();
55b0eeea 951 plat_rescan_inputs();
ef94866c 952 me_update_msg("rescan complete.");
953
954 return 0;
955}
956
4c08b9e7 957static const char *men_in_type_sel[] = {
958 "Standard (SCPH-1080)",
959 "Analog (SCPH-1150)",
960 "GunCon",
961 NULL
962};
ef94866c 963static const char h_nub_btns[] = "Experimental, keep this OFF if unsure. Select rescan after change.";
4c08b9e7 964static const char h_notsgun[] = "Don't trigger (shoot) when touching screen in gun games.";
799b0b87 965
69af03a2 966static menu_entry e_menu_keyconfig[] =
967{
4c08b9e7 968 mee_handler_id("Player 1", MA_CTRL_PLAYER1, key_config_loop_wrap),
969 mee_handler_id("Player 2", MA_CTRL_PLAYER2, key_config_loop_wrap),
970 mee_handler_id("Emulator/Gun controls", MA_CTRL_EMU, key_config_loop_wrap),
799b0b87 971 mee_label (""),
4c08b9e7 972 mee_enum ("Port 1 device", 0, in_type_sel1, men_in_type_sel),
973 mee_enum ("Port 2 device", 0, in_type_sel2, men_in_type_sel),
55b0eeea 974 mee_onoff_h ("Nubs as buttons", MA_CTRL_NUBS_BTNS, in_evdev_allow_abs_only, 1, h_nub_btns),
975 mee_range ("Analog deadzone", MA_CTRL_DEADZONE, analog_deadzone, 1, 99),
4c08b9e7 976 mee_onoff_h ("No TS Gun trigger", 0, g_opts, OPT_TSGUN_NOTRIGGER, h_notsgun),
799b0b87 977 mee_cust_nosave("Save global config", MA_OPT_SAVECFG, mh_savecfg, mgn_saveloadcfg),
978 mee_cust_nosave("Save cfg for loaded game", MA_OPT_SAVECFG_GAME, mh_savecfg, mgn_saveloadcfg),
ef94866c 979 mee_handler ("Rescan devices", mh_input_rescan),
69af03a2 980 mee_label (""),
981 mee_label ("Input devices:"),
982 mee_label_mk (MA_CTRL_DEV_FIRST, mgn_dev_name),
983 mee_label_mk (MA_CTRL_DEV_NEXT, mgn_dev_name),
984 mee_label_mk (MA_CTRL_DEV_NEXT, mgn_dev_name),
985 mee_label_mk (MA_CTRL_DEV_NEXT, mgn_dev_name),
986 mee_label_mk (MA_CTRL_DEV_NEXT, mgn_dev_name),
987 mee_label_mk (MA_CTRL_DEV_NEXT, mgn_dev_name),
988 mee_label_mk (MA_CTRL_DEV_NEXT, mgn_dev_name),
989 mee_end,
990};
991
992static int menu_loop_keyconfig(int id, int keys)
993{
994 static int sel = 0;
995
e16a7e51 996// me_enable(e_menu_keyconfig, MA_OPT_SAVECFG_GAME, ready_to_go && CdromId[0]);
51f77282 997 me_loop(e_menu_keyconfig, &sel);
69af03a2 998 return 0;
999}
1000
69af03a2 1001// ------------ gfx options menu ------------
1002
a185be70 1003static const char *men_scaler[] = { "1x1", "scaled 4:3", "integer scaled 4:3", "fullscreen", "custom", NULL };
3c70c47b 1004static const char h_cscaler[] = "Displays the scaler layer, you can resize it\n"
1005 "using d-pad or move it using R+d-pad";
1006static const char *men_dummy[] = { NULL };
1007
1008static int menu_loop_cscaler(int id, int keys)
1009{
1010 unsigned int inp;
1011
1012 scaling = SCALE_CUSTOM;
1013
1014 omap_enable_layer(1);
3c70c47b 1015
1016 for (;;)
1017 {
1018 menu_draw_begin(0);
201c21e2 1019 memset(g_menuscreen_ptr, 4, g_menuscreen_w * g_menuscreen_h * 2);
1020 text_out16(2, 2, "%d,%d", g_layer_x, g_layer_y);
1021 text_out16(2, 480 - 18, "%dx%d | d-pad: resize, R+d-pad: move", g_layer_w, g_layer_h);
3c70c47b 1022 menu_draw_end();
1023
1024 inp = in_menu_wait(PBTN_UP|PBTN_DOWN|PBTN_LEFT|PBTN_RIGHT|PBTN_R|PBTN_MOK|PBTN_MBACK, 40);
1025 if (inp & PBTN_UP) g_layer_y--;
1026 if (inp & PBTN_DOWN) g_layer_y++;
1027 if (inp & PBTN_LEFT) g_layer_x--;
1028 if (inp & PBTN_RIGHT) g_layer_x++;
1029 if (!(inp & PBTN_R)) {
1030 if (inp & PBTN_UP) g_layer_h += 2;
1031 if (inp & PBTN_DOWN) g_layer_h -= 2;
1032 if (inp & PBTN_LEFT) g_layer_w += 2;
1033 if (inp & PBTN_RIGHT) g_layer_w -= 2;
1034 }
1035 if (inp & (PBTN_MOK|PBTN_MBACK))
1036 break;
1037
1038 if (inp & (PBTN_UP|PBTN_DOWN|PBTN_LEFT|PBTN_RIGHT)) {
1039 if (g_layer_x < 0) g_layer_x = 0;
1040 if (g_layer_x > 640) g_layer_x = 640;
1041 if (g_layer_y < 0) g_layer_y = 0;
1042 if (g_layer_y > 420) g_layer_y = 420;
1043 if (g_layer_w < 160) g_layer_w = 160;
1044 if (g_layer_h < 60) g_layer_h = 60;
1045 if (g_layer_x + g_layer_w > 800)
1046 g_layer_w = 800 - g_layer_x;
1047 if (g_layer_y + g_layer_h > 480)
1048 g_layer_h = 480 - g_layer_y;
1049 omap_enable_layer(1);
1050 }
1051 }
1052
1053 omap_enable_layer(0);
1054
1055 return 0;
1056}
1057
69af03a2 1058static menu_entry e_menu_gfx_options[] =
1059{
3c70c47b 1060 mee_enum ("Scaler", 0, scaling, men_scaler),
1061 mee_enum ("Filter", MA_OPT_FILTERING, filter, men_dummy),
1062// mee_onoff ("Vsync", 0, vsync, 1),
1063 mee_cust_h ("Setup custom scaler", 0, menu_loop_cscaler, NULL, h_cscaler),
69af03a2 1064 mee_end,
1065};
1066
1067static int menu_loop_gfx_options(int id, int keys)
1068{
1069 static int sel = 0;
1070
51f77282 1071 me_loop(e_menu_gfx_options, &sel);
69af03a2 1072
1073 return 0;
1074}
1075
bd6267e6 1076// ------------ bios/plugins ------------
1077
17a54a4a 1078static menu_entry e_menu_plugin_gpu_unai[] =
1079{
1080 mee_onoff ("Abe's Odyssey hack", 0, pl_rearmed_cbs.gpu_unai.abe_hack, 1),
1081 mee_onoff ("Disable lighting", 0, pl_rearmed_cbs.gpu_unai.no_light, 1),
1082 mee_onoff ("Disable blending", 0, pl_rearmed_cbs.gpu_unai.no_blend, 1),
1083 mee_end,
1084};
1085
1086static int menu_loop_plugin_gpu_unai(int id, int keys)
1087{
1088 int sel = 0;
1089 me_loop(e_menu_plugin_gpu_unai, &sel);
1090 return 0;
1091}
1092
bd6267e6 1093static const char *men_gpu_dithering[] = { "None", "Game dependant", "Always", NULL };
bd6267e6 1094static const char h_gpu_0[] = "Needed for Chrono Cross";
1095static const char h_gpu_1[] = "Capcom fighting games";
1096static const char h_gpu_2[] = "Black screens in Lunar";
1097static const char h_gpu_3[] = "Compatibility mode";
1098static const char h_gpu_6[] = "Pandemonium 2";
1099static const char h_gpu_7[] = "Skip every second frame";
1100static const char h_gpu_8[] = "Needed by Dark Forces";
1101static const char h_gpu_9[] = "better g-colors, worse textures";
1102static const char h_gpu_10[] = "Toggle busy flags after drawing";
1103
17a54a4a 1104static menu_entry e_menu_plugin_gpu_peops[] =
bd6267e6 1105{
e64dc4c5 1106 mee_enum ("Dithering", 0, pl_rearmed_cbs.gpu_peops.iUseDither, men_gpu_dithering),
1107 mee_onoff_h ("Odd/even bit hack", 0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<0, h_gpu_0),
1108 mee_onoff_h ("Expand screen width", 0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<1, h_gpu_1),
1109 mee_onoff_h ("Ignore brightness color", 0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<2, h_gpu_2),
1110 mee_onoff_h ("Disable coordinate check", 0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<3, h_gpu_3),
1111 mee_onoff_h ("Lazy screen update", 0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<6, h_gpu_6),
1112 mee_onoff_h ("Old frame skipping", 0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<7, h_gpu_7),
1113 mee_onoff_h ("Repeated flat tex triangles ",0,pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<8, h_gpu_8),
1114 mee_onoff_h ("Draw quads with triangles", 0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<9, h_gpu_9),
1115 mee_onoff_h ("Fake 'gpu busy' states", 0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<10, h_gpu_10),
bd6267e6 1116 mee_end,
1117};
1118
17a54a4a 1119static int menu_loop_plugin_gpu_peops(int id, int keys)
bd6267e6 1120{
1121 static int sel = 0;
17a54a4a 1122 me_loop(e_menu_plugin_gpu_peops, &sel);
bd6267e6 1123 return 0;
1124}
1125
bd6267e6 1126static const char *men_spu_interp[] = { "None", "Simple", "Gaussian", "Cubic", NULL };
9e7a7352 1127static const char h_spu_volboost[] = "Large values cause distortion";
cdb31c95 1128static const char h_spu_irq_wait[] = "Wait for CPU (recommended set to ON)";
1bd9ee68 1129static const char h_spu_thread[] = "Run sound emulation in main thread (recommended)";
bd6267e6 1130
1131static menu_entry e_menu_plugin_spu[] =
1132{
9e7a7352 1133 mee_range_h ("Volume boost", 0, volume_boost, -5, 30, h_spu_volboost),
381ea103 1134 mee_onoff ("Reverb", 0, iUseReverb, 2),
bd6267e6 1135 mee_enum ("Interpolation", 0, iUseInterpolation, men_spu_interp),
1136 mee_onoff ("Adjust XA pitch", 0, iXAPitch, 1),
1137 mee_onoff_h ("SPU IRQ Wait", 0, iSPUIRQWait, 1, h_spu_irq_wait),
1bd9ee68 1138 mee_onoff_h ("Sound in main thread", 0, iUseTimer, 2, h_spu_thread),
bd6267e6 1139 mee_end,
1140};
1141
1142static int menu_loop_plugin_spu(int id, int keys)
1143{
1144 static int sel = 0;
51f77282 1145 me_loop(e_menu_plugin_spu, &sel);
bd6267e6 1146 return 0;
1147}
1148
55b0eeea 1149static const char h_bios[] = "HLE is simulated BIOS. BIOS selection is saved in\n"
1150 "savestates and can't be changed there. Must save\n"
1151 "config and reload the game for change to take effect";
bbd837c6 1152static const char h_plugin_xpu[] = "Must save config and reload the game\n"
1153 "for plugin change to take effect";
17a54a4a 1154static const char h_gpu_peops[] = "Configure P.E.Op.S. SoftGL Driver V1.17";
1155static const char h_gpu_unai[] = "Configure Unai/PCSX4ALL Team GPU plugin";
e6eb2066 1156static const char h_spu[] = "Configure built-in P.E.Op.S. Sound Driver V1.7";
bbd837c6 1157
bd6267e6 1158static menu_entry e_menu_plugin_options[] =
1159{
e6eb2066 1160 mee_enum_h ("BIOS", 0, bios_sel, bioses, h_bios),
bbd837c6 1161 mee_enum_h ("GPU plugin", 0, gpu_plugsel, gpu_plugins, h_plugin_xpu),
1162 mee_enum_h ("SPU plugin", 0, spu_plugsel, spu_plugins, h_plugin_xpu),
17a54a4a 1163 mee_handler_h ("Configure gpu_peops plugin", menu_loop_plugin_gpu_peops, h_gpu_peops),
1164 mee_handler_h ("Configure PCSX4ALL GPU plugin", menu_loop_plugin_gpu_unai, h_gpu_unai),
bd6267e6 1165 mee_handler_h ("Configure built-in SPU plugin", menu_loop_plugin_spu, h_spu),
1166 mee_end,
1167};
1168
51f77282 1169static menu_entry e_menu_main2[];
e16a7e51 1170
bd6267e6 1171static int menu_loop_plugin_options(int id, int keys)
1172{
1173 static int sel = 0;
51f77282 1174 me_loop(e_menu_plugin_options, &sel);
bbd837c6 1175
e6eb2066 1176 // sync BIOS/plugins
1177 snprintf(Config.Bios, sizeof(Config.Bios), "%s", bioses[bios_sel]);
bbd837c6 1178 snprintf(Config.Gpu, sizeof(Config.Gpu), "%s", gpu_plugins[gpu_plugsel]);
1179 snprintf(Config.Spu, sizeof(Config.Spu), "%s", spu_plugins[spu_plugsel]);
51f77282 1180 me_enable(e_menu_main2, MA_MAIN_RUN_BIOS, bios_sel != 0);
bbd837c6 1181
bd6267e6 1182 return 0;
1183}
1184
1185// ------------ adv options menu ------------
1186
4feed8d3 1187static const char *men_cfg_cdrr[] = { "Auto", "ON", "OFF", NULL };
8f892648 1188static const char h_cfg_cpul[] = "Shows CPU usage in %";
90f1d26c 1189static const char h_cfg_spu[] = "Shows active SPU channels\n"
1190 "(green: normal, red: fmod, blue: noise)";
1bd9ee68 1191static const char h_cfg_fl[] = "Frame Limiter keeps the game from running too fast";
bd6267e6 1192static const char h_cfg_xa[] = "Disables XA sound, which can sometimes improve performance";
1193static const char h_cfg_cdda[] = "Disable CD Audio for a performance boost\n"
1194 "(proper .cue/.bin dump is needed otherwise)";
4feed8d3 1195static const char h_cfg_sio[] = "You should not need this, breaks games";
1196static const char h_cfg_spuirq[] = "Compatibility tweak; should be left off";
1197static const char h_cfg_rcnt1[] = "Parasite Eve 2, Vandal Hearts 1/2 Fix\n"
1198 "(timing hack, breaks other games)";
1199static const char h_cfg_rcnt2[] = "InuYasha Sengoku Battle Fix\n"
1200 "(timing hack, breaks other games)";
1201static const char h_cfg_cdrr[] = "Compatibility tweak (fixes Team Buddies, maybe more)\n"
1202 "(CD timing hack, breaks FMVs)";
b5e7e49a 1203static const char h_cfg_nodrc[] = "Disable dynamic recompiler and use interpreter\n"
1204 "Might be useful to overcome some dynarec bugs";
bd6267e6 1205
1206static menu_entry e_menu_adv_options[] =
1207{
fba06457 1208 mee_onoff_h ("Show CPU load", 0, g_opts, OPT_SHOWCPU, h_cfg_cpul),
90f1d26c 1209 mee_onoff_h ("Show SPU channels", 0, g_opts, OPT_SHOWSPU, h_cfg_spu),
bce6b056 1210 mee_onoff_h ("Disable Frame Limiter", 0, g_opts, OPT_NO_FRAMELIM, h_cfg_fl),
bd6267e6 1211 mee_onoff_h ("Disable XA Decoding", 0, Config.Xa, 1, h_cfg_xa),
1212 mee_onoff_h ("Disable CD Audio", 0, Config.Cdda, 1, h_cfg_cdda),
1213 mee_onoff_h ("SIO IRQ Always Enabled", 0, Config.Sio, 1, h_cfg_sio),
1214 mee_onoff_h ("SPU IRQ Always Enabled", 0, Config.SpuIrq, 1, h_cfg_spuirq),
1215 mee_onoff_h ("Rootcounter hack", 0, Config.RCntFix, 1, h_cfg_rcnt1),
1216 mee_onoff_h ("Rootcounter hack 2", 0, Config.VSyncWA, 1, h_cfg_rcnt2),
4feed8d3 1217 mee_enum_h ("CD read reschedule hack",0, Config.CdrReschedule, men_cfg_cdrr, h_cfg_cdrr),
b5e7e49a 1218 mee_onoff_h ("Disable dynarec (slow!)",0, Config.Cpu, 1, h_cfg_nodrc),
bd6267e6 1219 mee_end,
1220};
1221
1222static int menu_loop_adv_options(int id, int keys)
1223{
1224 static int sel = 0;
51f77282 1225 me_loop(e_menu_adv_options, &sel);
bd6267e6 1226 return 0;
1227}
1228
69af03a2 1229// ------------ options menu ------------
1230
69af03a2 1231static int mh_restore_defaults(int id, int keys)
1232{
3c70c47b 1233 menu_set_defconfig();
69af03a2 1234 me_update_msg("defaults restored");
1235 return 1;
1236}
1237
907b1e90 1238static const char *men_region[] = { "Auto", "NTSC", "PAL", NULL };
ea4a16e7 1239static const char *men_frameskip[] = { "Auto", "Off", "1", NULL };
bd6267e6 1240/*
69af03a2 1241static const char *men_confirm_save[] = { "OFF", "writes", "loads", "both", NULL };
1242static const char h_confirm_save[] = "Ask for confirmation when overwriting save,\n"
1243 "loading state or both";
bd6267e6 1244*/
1245static const char h_restore_def[] = "Switches back to default / recommended\n"
1246 "configuration";
9f4a4237 1247static const char h_frameskip[] = "Warning: frameskip sometimes causes glitches\n";
69af03a2 1248
1249static menu_entry e_menu_options[] =
1250{
bd6267e6 1251// mee_range ("Save slot", 0, state_slot, 0, 9),
1252// mee_enum_h ("Confirm savestate", 0, dummy, men_confirm_save, h_confirm_save),
ea4a16e7 1253 mee_enum_h ("Frameskip", 0, frameskip, men_frameskip, h_frameskip),
bd6267e6 1254 mee_onoff ("Show FPS", 0, g_opts, OPT_SHOWFPS),
907b1e90 1255 mee_enum ("Region", 0, region, men_region),
3c70c47b 1256 mee_range ("CPU clock", MA_OPT_CPU_CLOCKS, cpu_clock, 20, 5000),
55b0eeea 1257 mee_handler_id("[Display]", MA_OPT_DISP_OPTS, menu_loop_gfx_options),
bd6267e6 1258 mee_handler ("[BIOS/Plugins]", menu_loop_plugin_options),
69af03a2 1259 mee_handler ("[Advanced]", menu_loop_adv_options),
cd6e8d0f 1260 mee_cust_nosave("Save global config", MA_OPT_SAVECFG, mh_savecfg, mgn_saveloadcfg),
1261 mee_cust_nosave("Save cfg for loaded game",MA_OPT_SAVECFG_GAME, mh_savecfg, mgn_saveloadcfg),
bd6267e6 1262 mee_handler_h ("Restore default config", mh_restore_defaults, h_restore_def),
69af03a2 1263 mee_end,
1264};
1265
1266static int menu_loop_options(int id, int keys)
1267{
1268 static int sel = 0;
1269 int i;
1270
1271 i = me_id2offset(e_menu_options, MA_OPT_CPU_CLOCKS);
3c70c47b 1272 e_menu_options[i].enabled = cpu_clock != 0 ? 1 : 0;
e16a7e51 1273 me_enable(e_menu_options, MA_OPT_SAVECFG_GAME, ready_to_go && CdromId[0]);
69af03a2 1274
51f77282 1275 me_loop(e_menu_options, &sel);
69af03a2 1276
1277 return 0;
1278}
1279
1280// ------------ debug menu ------------
1281
72bb6fdd 1282static void draw_frame_debug(GPUFreeze_t *gpuf)
69af03a2 1283{
72bb6fdd 1284 int w = min(g_menuscreen_w, 1024);
1285 int h = min(g_menuscreen_h, 512);
1286 u16 *d = g_menuscreen_ptr;
1287 u16 *s = (u16 *)gpuf->psxVRam;
1288 char buff[64];
1289 int ty = 1;
1290
1291 gpuf->ulFreezeVersion = 1;
1292 if (GPU_freeze != NULL)
1293 GPU_freeze(1, gpuf);
1294
1295 for (; h > 0; h--, d += g_menuscreen_w, s += 1024)
1296 bgr555_to_rgb565(d, s, w * 2);
1297
3c70c47b 1298 smalltext_out16(4, 1, "build: "__DATE__ " " __TIME__ " " REV, 0xe7fc);
72bb6fdd 1299 snprintf(buff, sizeof(buff), "GPU sr: %08x", gpuf->ulStatus);
1300 smalltext_out16(4, (ty += me_sfont_h), buff, 0xe7fc);
1301 snprintf(buff, sizeof(buff), "PC/SP: %08x %08x", psxRegs.pc, psxRegs.GPR.n.sp);
1302 smalltext_out16(4, (ty += me_sfont_h), buff, 0xe7fc);
69af03a2 1303}
1304
1305static void debug_menu_loop(void)
1306{
72bb6fdd 1307 GPUFreeze_t *gpuf;
69af03a2 1308 int inp;
1309
72bb6fdd 1310 gpuf = malloc(sizeof(*gpuf));
1311 if (gpuf == NULL)
1312 return;
1313
69af03a2 1314 while (1)
1315 {
72bb6fdd 1316 menu_draw_begin(0);
1317 draw_frame_debug(gpuf);
69af03a2 1318 menu_draw_end();
1319
1320 inp = in_menu_wait(PBTN_MOK|PBTN_MBACK|PBTN_MA2|PBTN_MA3|PBTN_L|PBTN_R |
1321 PBTN_UP|PBTN_DOWN|PBTN_LEFT|PBTN_RIGHT, 70);
1322 if (inp & PBTN_MBACK)
72bb6fdd 1323 break;
69af03a2 1324 }
72bb6fdd 1325
1326 free(gpuf);
69af03a2 1327}
1328
51f77282 1329// --------- memcard manager ---------
1330
1331static void draw_mc_icon(int dx, int dy, const u16 *s)
1332{
1333 u16 *d;
1334 int x, y, l, p;
1335
1336 d = (u16 *)g_menuscreen_ptr + g_menuscreen_w * dy + dx;
1337
1338 for (y = 0; y < 16; y++, s += 16) {
1339 for (l = 0; l < 2; l++, d += g_menuscreen_w) {
1340 for (x = 0; x < 16; x++) {
1341 p = s[x];
1342 d[x*2] = d[x*2 + 1] = ((p & 0x7c00) >> 10)
1343 | ((p & 0x03e0) << 1) | ((p & 0x1f) << 11);
1344 }
1345 }
1346 }
1347}
1348
1349static void draw_mc_bg(void)
1350{
1351 McdBlock *blocks1, *blocks2;
1352 int maxicons = 15;
1353 int i, y, row2;
1354
1355 blocks1 = malloc(15 * sizeof(blocks1[0]));
1356 blocks2 = malloc(15 * sizeof(blocks1[0]));
1357 if (blocks1 == NULL || blocks2 == NULL)
1358 goto out;
1359
1360 for (i = 0; i < 15; i++) {
1361 GetMcdBlockInfo(1, i + 1, &blocks1[i]);
1362 GetMcdBlockInfo(2, i + 1, &blocks2[i]);
1363 }
1364
1365 menu_draw_begin(1);
1366
1367 memcpy(g_menuscreen_ptr, g_menubg_src_ptr, g_menuscreen_w * g_menuscreen_h * 2);
1368
1369 y = g_menuscreen_h / 2 - 15 * 32 / 2;
1370 if (y < 0) {
1371 // doesn't fit..
1372 y = 0;
1373 maxicons = g_menuscreen_h / 32;
1374 }
1375
1376 row2 = g_menuscreen_w / 2;
1377 for (i = 0; i < maxicons; i++) {
1378 draw_mc_icon(8, y + i * 32, (u16 *)blocks1[i].Icon);
1379 smalltext_out16(10+32, y + i * 32 + 8, blocks1[i].sTitle, 0xf71e);
1380
1381 draw_mc_icon(row2 + 8, y + i * 32, (u16 *)blocks2[i].Icon);
1382 smalltext_out16(row2 + 10+32, y + i * 32 + 8, blocks2[i].sTitle, 0xf71e);
1383 }
1384
1385 menu_darken_bg(g_menubg_ptr, g_menuscreen_ptr, g_menuscreen_w * g_menuscreen_h, 0);
1386
1387 menu_draw_end();
1388out:
1389 free(blocks1);
1390 free(blocks2);
1391}
1392
1393static void handle_memcard_sel(void)
1394{
1395 Config.Mcd1[0] = 0;
1396 if (memcard1_sel != 0)
1397 snprintf(Config.Mcd1, sizeof(Config.Mcd1), ".%s%s", MEMCARD_DIR, memcards[memcard1_sel]);
1398 Config.Mcd2[0] = 0;
1399 if (memcard2_sel != 0)
1400 snprintf(Config.Mcd2, sizeof(Config.Mcd2), ".%s%s", MEMCARD_DIR, memcards[memcard2_sel]);
1401 LoadMcds(Config.Mcd1, Config.Mcd2);
1402 draw_mc_bg();
1403}
1404
1405static menu_entry e_memcard_options[] =
1406{
1407 mee_enum("Memory card 1", 0, memcard1_sel, memcards),
1408 mee_enum("Memory card 2", 0, memcard2_sel, memcards),
1409 mee_end,
1410};
1411
1412static int menu_loop_memcards(int id, int keys)
1413{
1414 static int sel = 0;
1415 char *p;
1416 int i;
1417
1418 memcard1_sel = memcard2_sel = 0;
1419 p = strrchr(Config.Mcd1, '/');
1420 if (p != NULL)
1421 for (i = 0; memcards[i] != NULL; i++)
1422 if (strcmp(p + 1, memcards[i]) == 0)
1423 { memcard1_sel = i; break; }
1424 p = strrchr(Config.Mcd2, '/');
1425 if (p != NULL)
1426 for (i = 0; memcards[i] != NULL; i++)
1427 if (strcmp(p + 1, memcards[i]) == 0)
1428 { memcard2_sel = i; break; }
1429
1430 me_loop_d(e_memcard_options, &sel, handle_memcard_sel, NULL);
1431
1432 memcpy(g_menubg_ptr, g_menubg_src_ptr, g_menuscreen_w * g_menuscreen_h * 2);
1433
1434 return 0;
1435}
1436
1437// --------- main menu help ----------
69af03a2 1438
4f5a1b2a 1439static void menu_bios_warn(void)
1440{
1441 int inp;
1442 static const char msg[] =
1443 "You don't seem to have copied any BIOS files to\n"
1444 "<SD card>/pandora/appdata/pcsx_rearmed/bios/\n\n"
1445 "While many games work fine with fake (HLE) BIOS,\n"
1446 "others (like MGS and FF8) require BIOS to work.\n"
1447 "After copying the file, you'll also need to\n"
1448 "select it in the emu's options->[BIOS/Plugins]\n\n"
1449 "The file is usually named SCPH1001.BIN, but\n"
1450 "other not compressed files can be used too.\n\n"
1451 "Press (B) or (X) to continue";
1452
1453 while (1)
1454 {
4f5a1b2a 1455 draw_menu_message(msg, NULL);
4f5a1b2a 1456
1457 inp = in_menu_wait(PBTN_MOK|PBTN_MBACK, 70);
1458 if (inp & (PBTN_MBACK|PBTN_MOK))
1459 return;
1460 }
1461}
1462
1463// ------------ main menu ------------
1464
bd6267e6 1465void OnFile_Exit();
1466
1bd9ee68 1467static void draw_frame_main(void)
1468{
65092fd8 1469 struct tm *tmp;
1470 time_t ltime;
1471 char ltime_s[16];
1472 char buff[64];
1473
1bd9ee68 1474 if (CdromId[0] != 0) {
c22b95ab 1475 snprintf(buff, sizeof(buff), "%.32s/%.9s (running as %s, with %s)",
1476 get_cd_label(), CdromId, Config.PsxType ? "PAL" : "NTSC",
1477 Config.HLE ? "HLE" : "BIOS");
1bd9ee68 1478 smalltext_out16(4, 1, buff, 0x105f);
1479 }
65092fd8 1480
1481 if (ready_to_go) {
1482 ltime = time(NULL);
1483 tmp = localtime(&ltime);
1484 strftime(ltime_s, sizeof(ltime_s), "%H:%M", tmp);
55b0eeea 1485 snprintf(buff, sizeof(buff), "%s %3d%%", ltime_s, plat_get_bat_capacity());
65092fd8 1486 smalltext_out16(4, 1 + me_sfont_h, buff, 0x105f);
1487 }
1bd9ee68 1488}
1489
1490static void draw_frame_credits(void)
1491{
1492 smalltext_out16(4, 1, "build: "__DATE__ " " __TIME__ " " REV, 0xe7fc);
1493}
1494
4f5a1b2a 1495static const char credits_text[] =
1496 "PCSX-ReARMed\n\n"
1497 "(C) 1999-2003 PCSX Team\n"
1498 "(C) 2005-2009 PCSX-df Team\n"
1499 "(C) 2009-2011 PCSX-Reloaded Team\n\n"
1500 "GPU and SPU code by Pete Bernert\n"
1501 " and the P.E.Op.S. team\n"
1502 "ARM recompiler (C) 2009-2011 Ari64\n"
1503 "PCSX4ALL plugins by PCSX4ALL team\n"
1504 " Chui, Franxis, Unai\n\n"
1505 "integration, optimization and\n"
1506 " frontend (C) 2010-2011 notaz\n";
69af03a2 1507
e16a7e51 1508static int reset_game(void)
1509{
1510 // sanity check
1511 if (bios_sel == 0 && !Config.HLE)
1512 return -1;
1513
1514 ClosePlugins();
1515 OpenPlugins();
1516 SysReset();
1517 if (CheckCdrom() != -1) {
1518 LoadCdrom();
1519 }
1520 return 0;
1521}
1522
51f77282 1523static int reload_plugins(const char *cdimg)
e16a7e51 1524{
76f7048e 1525 pl_vout_buf = NULL;
e16a7e51 1526
1527 ClosePlugins();
51f77282 1528
1529 set_cd_image(cdimg);
e16a7e51 1530 LoadPlugins();
c22b95ab 1531 pcnt_hook_plugins();
e16a7e51 1532 NetOpened = 0;
1533 if (OpenPlugins() == -1) {
1534 me_update_msg("failed to open plugins");
1535 return -1;
1536 }
1537 plugin_call_rearmed_cbs();
1538
1539 CdromId[0] = '\0';
1540 CdromLabel[0] = '\0';
1541
51f77282 1542 return 0;
1543}
1544
1545static int run_bios(void)
1546{
1547 if (bios_sel == 0)
1548 return -1;
1549
1550 ready_to_go = 0;
1551 if (reload_plugins(NULL) != 0)
1552 return -1;
e16a7e51 1553 SysReset();
1554
1555 ready_to_go = 1;
1556 return 0;
1557}
1558
51f77282 1559static int run_exe(void)
69af03a2 1560{
51f77282 1561 const char *fname;
1562
1563 fname = menu_loop_romsel(last_selected_fname, sizeof(last_selected_fname));
1564 if (fname == NULL)
1565 return -1;
1566
69af03a2 1567 ready_to_go = 0;
51f77282 1568 if (reload_plugins(NULL) != 0)
1569 return -1;
69af03a2 1570
51f77282 1571 SysReset();
1572 if (Load(fname) != 0) {
1573 me_update_msg("exe load failed, bad file?");
1574 printf("meh\n");
bbd837c6 1575 return -1;
69af03a2 1576 }
51f77282 1577
1578 ready_to_go = 1;
1579 return 0;
1580}
1581
1582static int run_cd_image(const char *fname)
1583{
1584 ready_to_go = 0;
1585 reload_plugins(fname);
69af03a2 1586
76d63edf 1587 // always autodetect, menu_sync_config will override as needed
1588 Config.PsxAuto = 1;
1589
69af03a2 1590 if (CheckCdrom() == -1) {
1591 // Only check the CD if we are starting the console with a CD
1592 ClosePlugins();
1593 me_update_msg("unsupported/invalid CD image");
bbd837c6 1594 return -1;
69af03a2 1595 }
1596
bbd837c6 1597 SysReset();
1598
69af03a2 1599 // Read main executable directly from CDRom and start it
1600 if (LoadCdrom() == -1) {
1601 ClosePlugins();
1602 me_update_msg("failed to load CD image");
bbd837c6 1603 return -1;
69af03a2 1604 }
1605
1606 ready_to_go = 1;
bbd837c6 1607 return 0;
69af03a2 1608}
1609
bbd837c6 1610static int romsel_run(void)
69af03a2 1611{
bbd837c6 1612 int prev_gpu, prev_spu;
51f77282 1613 const char *fname;
bbd837c6 1614
1615 fname = menu_loop_romsel(last_selected_fname, sizeof(last_selected_fname));
1616 if (fname == NULL)
1617 return -1;
69af03a2 1618
bbd837c6 1619 printf("selected file: %s\n", fname);
1620
dc990066 1621 new_dynarec_clear_full();
1622
bbd837c6 1623 if (run_cd_image(fname) != 0)
1624 return -1;
1625
1626 prev_gpu = gpu_plugsel;
1627 prev_spu = spu_plugsel;
1628 if (menu_load_config(1) != 0)
1629 menu_load_config(0);
1630
1631 // check for plugin changes, have to repeat
1632 // loading if game config changed plugins to reload them
1633 if (prev_gpu != gpu_plugsel || prev_spu != spu_plugsel) {
1634 printf("plugin change detected, reloading plugins..\n");
1635 if (run_cd_image(fname) != 0)
1636 return -1;
1637 }
1638
1639 strcpy(last_selected_fname, rom_fname_reload);
1640 return 0;
1641}
1642
1df403c5 1643static int swap_cd_image(void)
1644{
1645 char *fname;
1646
1647 fname = menu_loop_romsel(last_selected_fname, sizeof(last_selected_fname));
1648 if (fname == NULL)
1649 return -1;
1650
1651 printf("selected file: %s\n", fname);
1652
1653 CdromId[0] = '\0';
1654 CdromLabel[0] = '\0';
1655
1656 set_cd_image(fname);
1657 if (ReloadCdromPlugin() < 0) {
1658 me_update_msg("failed to load cdr plugin");
1659 return -1;
1660 }
1661 if (CDR_open() < 0) {
1662 me_update_msg("failed to open cdr plugin");
1663 return -1;
1664 }
1665
1666 SetCdOpenCaseTime(time(NULL) + 2);
1667 LidInterrupt();
1668
1669 strcpy(last_selected_fname, rom_fname_reload);
1670 return 0;
1671}
1672
bbd837c6 1673static int main_menu_handler(int id, int keys)
1674{
69af03a2 1675 switch (id)
1676 {
1677 case MA_MAIN_RESUME_GAME:
3c70c47b 1678 if (ready_to_go)
1679 return 1;
69af03a2 1680 break;
1681 case MA_MAIN_SAVE_STATE:
1682 if (ready_to_go)
1683 return menu_loop_savestate(0);
1684 break;
1685 case MA_MAIN_LOAD_STATE:
1686 if (ready_to_go)
1687 return menu_loop_savestate(1);
1688 break;
1689 case MA_MAIN_RESET_GAME:
e16a7e51 1690 if (ready_to_go && reset_game() == 0)
3c70c47b 1691 return 1;
69af03a2 1692 break;
1693 case MA_MAIN_LOAD_ROM:
bbd837c6 1694 if (romsel_run() == 0)
69af03a2 1695 return 1;
1696 break;
1df403c5 1697 case MA_MAIN_SWAP_CD:
1698 if (swap_cd_image() == 0)
1699 return 1;
1700 break;
e16a7e51 1701 case MA_MAIN_RUN_BIOS:
1702 if (run_bios() == 0)
1703 return 1;
1704 break;
51f77282 1705 case MA_MAIN_RUN_EXE:
1706 if (run_exe() == 0)
1707 return 1;
1708 break;
69af03a2 1709 case MA_MAIN_CREDITS:
4f5a1b2a 1710 draw_menu_message(credits_text, draw_frame_credits);
69af03a2 1711 in_menu_wait(PBTN_MOK|PBTN_MBACK, 70);
1712 break;
1713 case MA_MAIN_EXIT:
bd6267e6 1714 OnFile_Exit();
1715 break;
69af03a2 1716 default:
1717 lprintf("%s: something unknown selected\n", __FUNCTION__);
1718 break;
1719 }
1720
1721 return 0;
1722}
1723
51f77282 1724static menu_entry e_menu_main2[] =
1725{
1726 mee_handler_id("Change CD image", MA_MAIN_SWAP_CD, main_menu_handler),
1727 mee_handler_id("Run BIOS", MA_MAIN_RUN_BIOS, main_menu_handler),
1728 mee_handler_id("Run EXE", MA_MAIN_RUN_EXE, main_menu_handler),
1729 mee_handler ("Memcard manager", menu_loop_memcards),
1730 mee_end,
1731};
1732
1733static int main_menu2_handler(int id, int keys)
1734{
1735 static int sel = 0;
1736
1737 me_enable(e_menu_main2, MA_MAIN_SWAP_CD, ready_to_go);
1738 me_enable(e_menu_main2, MA_MAIN_RUN_BIOS, bios_sel != 0);
1739
1740 return me_loop_d(e_menu_main2, &sel, NULL, draw_frame_main);
1741}
1742
1743static const char h_extra[] = "Change CD, manage memcards..\n";
1744
69af03a2 1745static menu_entry e_menu_main[] =
1746{
1747 mee_label (""),
1748 mee_label (""),
1749 mee_handler_id("Resume game", MA_MAIN_RESUME_GAME, main_menu_handler),
1750 mee_handler_id("Save State", MA_MAIN_SAVE_STATE, main_menu_handler),
1751 mee_handler_id("Load State", MA_MAIN_LOAD_STATE, main_menu_handler),
1752 mee_handler_id("Reset game", MA_MAIN_RESET_GAME, main_menu_handler),
1753 mee_handler_id("Load CD image", MA_MAIN_LOAD_ROM, main_menu_handler),
1754 mee_handler ("Options", menu_loop_options),
1755 mee_handler ("Controls", menu_loop_keyconfig),
51f77282 1756 mee_handler_h ("Extra stuff", main_menu2_handler, h_extra),
69af03a2 1757 mee_handler_id("Credits", MA_MAIN_CREDITS, main_menu_handler),
1758 mee_handler_id("Exit", MA_MAIN_EXIT, main_menu_handler),
1759 mee_end,
1760};
1761
3c70c47b 1762// ----------------------------
1763
bd6267e6 1764static void menu_leave_emu(void);
1765
69af03a2 1766void menu_loop(void)
1767{
1768 static int sel = 0;
1769
bd6267e6 1770 menu_leave_emu();
69af03a2 1771
4f5a1b2a 1772 if (bioses[1] == NULL && !warned_about_bios) {
1773 menu_bios_warn();
1774 warned_about_bios = 1;
1775 }
1776
69af03a2 1777 me_enable(e_menu_main, MA_MAIN_RESUME_GAME, ready_to_go);
e16a7e51 1778 me_enable(e_menu_main, MA_MAIN_SAVE_STATE, ready_to_go && CdromId[0]);
1779 me_enable(e_menu_main, MA_MAIN_LOAD_STATE, ready_to_go && CdromId[0]);
69af03a2 1780 me_enable(e_menu_main, MA_MAIN_RESET_GAME, ready_to_go);
1781
69af03a2 1782 in_set_config_int(0, IN_CFG_BLOCKING, 1);
1783
1784 do {
51f77282 1785 me_loop_d(e_menu_main, &sel, NULL, draw_frame_main);
69af03a2 1786 } while (!ready_to_go);
1787
1788 /* wait until menu, ok, back is released */
1789 while (in_menu_wait_any(50) & (PBTN_MENU|PBTN_MOK|PBTN_MBACK))
1790 ;
1791
1792 in_set_config_int(0, IN_CFG_BLOCKING, 0);
1793
3c70c47b 1794 menu_prepare_emu();
1795}
1796
51f77282 1797static int qsort_strcmp(const void *p1, const void *p2)
1798{
1799 char * const *s1 = (char * const *)p1;
1800 char * const *s2 = (char * const *)p2;
1801 return strcasecmp(*s1, *s2);
1802}
1803
e6eb2066 1804static void scan_bios_plugins(void)
bbd837c6 1805{
1806 char fname[MAXPATHLEN];
1807 struct dirent *ent;
51f77282 1808 int bios_i, gpu_i, spu_i, mc_i;
bbd837c6 1809 char *p;
1810 DIR *dir;
1811
e6eb2066 1812 bioses[0] = "HLE";
bbd837c6 1813 gpu_plugins[0] = "builtin_gpu";
1814 spu_plugins[0] = "builtin_spu";
51f77282 1815 memcards[0] = "(none)";
1816 bios_i = gpu_i = spu_i = mc_i = 1;
e6eb2066 1817
1818 snprintf(fname, sizeof(fname), "%s/", Config.BiosDir);
1819 dir = opendir(fname);
1820 if (dir == NULL) {
1821 perror("scan_bios_plugins bios opendir");
1822 goto do_plugins;
1823 }
1824
1825 while (1) {
1826 struct stat st;
1827
1828 errno = 0;
1829 ent = readdir(dir);
1830 if (ent == NULL) {
1831 if (errno != 0)
1832 perror("readdir");
1833 break;
1834 }
1835
1836 if (ent->d_type != DT_REG && ent->d_type != DT_LNK)
1837 continue;
1838
1839 snprintf(fname, sizeof(fname), "%s/%s", Config.BiosDir, ent->d_name);
1840 if (stat(fname, &st) != 0 || st.st_size != 512*1024) {
1841 printf("bad BIOS file: %s\n", ent->d_name);
1842 continue;
1843 }
1844
1845 if (bios_i < ARRAY_SIZE(bioses) - 1) {
1846 bioses[bios_i++] = strdup(ent->d_name);
1847 continue;
1848 }
1849
1850 printf("too many BIOSes, dropping \"%s\"\n", ent->d_name);
1851 }
1852
1853 closedir(dir);
bbd837c6 1854
e6eb2066 1855do_plugins:
bbd837c6 1856 snprintf(fname, sizeof(fname), "%s/", Config.PluginsDir);
1857 dir = opendir(fname);
1858 if (dir == NULL) {
51f77282 1859 perror("scan_bios_plugins plugins opendir");
1860 goto do_memcards;
bbd837c6 1861 }
1862
1863 while (1) {
1864 void *h, *tmp;
1865
1866 errno = 0;
1867 ent = readdir(dir);
1868 if (ent == NULL) {
1869 if (errno != 0)
1870 perror("readdir");
1871 break;
1872 }
1873 p = strstr(ent->d_name, ".so");
1874 if (p == NULL)
1875 continue;
1876
1877 snprintf(fname, sizeof(fname), "%s/%s", Config.PluginsDir, ent->d_name);
1878 h = dlopen(fname, RTLD_LAZY | RTLD_LOCAL);
1879 if (h == NULL) {
1880 fprintf(stderr, "%s\n", dlerror());
1881 continue;
1882 }
1883
1884 // now what do we have here?
1885 tmp = dlsym(h, "GPUinit");
1886 if (tmp) {
1887 dlclose(h);
1888 if (gpu_i < ARRAY_SIZE(gpu_plugins) - 1)
1889 gpu_plugins[gpu_i++] = strdup(ent->d_name);
1890 continue;
1891 }
1892
1893 tmp = dlsym(h, "SPUinit");
1894 if (tmp) {
1895 dlclose(h);
1896 if (spu_i < ARRAY_SIZE(spu_plugins) - 1)
1897 spu_plugins[spu_i++] = strdup(ent->d_name);
1898 continue;
1899 }
1900
1901 fprintf(stderr, "ignoring unidentified plugin: %s\n", fname);
1902 dlclose(h);
1903 }
1904
1905 closedir(dir);
51f77282 1906
1907do_memcards:
1908 dir = opendir("." MEMCARD_DIR);
1909 if (dir == NULL) {
1910 perror("scan_bios_plugins memcards opendir");
1911 return;
1912 }
1913
1914 while (1) {
1915 struct stat st;
1916
1917 errno = 0;
1918 ent = readdir(dir);
1919 if (ent == NULL) {
1920 if (errno != 0)
1921 perror("readdir");
1922 break;
1923 }
1924
1925 if (ent->d_type != DT_REG && ent->d_type != DT_LNK)
1926 continue;
1927
1928 snprintf(fname, sizeof(fname), "." MEMCARD_DIR "%s", ent->d_name);
1929 if (stat(fname, &st) != 0) {
1930 printf("bad memcard file: %s\n", ent->d_name);
1931 continue;
1932 }
1933
1934 if (mc_i < ARRAY_SIZE(memcards) - 1) {
1935 memcards[mc_i++] = strdup(ent->d_name);
1936 continue;
1937 }
1938
1939 printf("too many memcards, dropping \"%s\"\n", ent->d_name);
1940 }
1941
1942 if (mc_i > 2)
1943 qsort(memcards + 1, mc_i - 1, sizeof(memcards[0]), qsort_strcmp);
1944
1945 closedir(dir);
bbd837c6 1946}
1947
3c70c47b 1948void menu_init(void)
1949{
4f3639fa 1950 char buff[MAXPATHLEN];
1951
1952 strcpy(last_selected_fname, "/media");
1953
e6eb2066 1954 scan_bios_plugins();
4f3639fa 1955 pnd_menu_init();
1956 menu_init_common();
1957
3c70c47b 1958 menu_set_defconfig();
1959 menu_load_config(0);
3c70c47b 1960 last_psx_w = 320;
1961 last_psx_h = 240;
bd6267e6 1962 last_psx_bpp = 16;
1963
4f3639fa 1964 g_menubg_src_ptr = calloc(g_menuscreen_w * g_menuscreen_h * 2, 1);
1965 if (g_menubg_src_ptr == NULL)
1966 exit(1);
1967 emu_make_path(buff, "skin/background.png", sizeof(buff));
1968 readpng(g_menubg_src_ptr, buff, READPNG_BG, g_menuscreen_w, g_menuscreen_h);
55b0eeea 1969
1970#ifndef __ARM_ARCH_7A__ /* XXX */
1971 me_enable(e_menu_options, MA_OPT_DISP_OPTS, 0);
1972 me_enable(e_menu_keyconfig, MA_CTRL_NUBS_BTNS, 0);
1973#else
1974 me_enable(e_menu_keyconfig, MA_CTRL_DEADZONE, 0);
1975#endif
3c70c47b 1976}
1977
bd6267e6 1978void menu_notify_mode_change(int w, int h, int bpp)
3c70c47b 1979{
a185be70 1980 float mult;
1981 int imult;
1982
3c70c47b 1983 last_psx_w = w;
1984 last_psx_h = h;
bd6267e6 1985 last_psx_bpp = bpp;
3c70c47b 1986
a185be70 1987 switch (scaling) {
1988 case SCALE_1_1:
3c70c47b 1989 g_layer_w = w; g_layer_h = h;
a185be70 1990 break;
1991
1992 case SCALE_4_3:
1993 mult = 240.0f / (float)h * 4.0f / 3.0f;
1994 if (h > 256)
1995 mult *= 2.0f;
1996 g_layer_w = mult * (float)g_menuscreen_h;
1997 g_layer_h = g_menuscreen_h;
1998 printf(" -> %dx%d %.1f\n", g_layer_w, g_layer_h, mult);
1999 break;
2000
2001 case SCALE_4_3v2:
2002 // 4:3 that prefers integer scaling
2003 imult = g_menuscreen_h / h;
2004 g_layer_w = w * imult;
2005 g_layer_h = h * imult;
2006 mult = (float)g_layer_w / (float)g_layer_h;
2007 if (mult < 1.25f || mult > 1.666f)
2008 g_layer_w = 4.0f/3.0f * (float)g_layer_h;
2009 printf(" -> %dx%d %.1f\n", g_layer_w, g_layer_h, mult);
2010 break;
2011
2012 case SCALE_FULLSCREEN:
2013 g_layer_w = g_menuscreen_w;
2014 g_layer_h = g_menuscreen_h;
2015 break;
2016
2017 default:
2018 break;
3c70c47b 2019 }
a185be70 2020
2021 g_layer_x = g_menuscreen_w / 2 - g_layer_w / 2;
2022 g_layer_y = g_menuscreen_h / 2 - g_layer_h / 2;
2023 if (g_layer_x < 0) g_layer_x = 0;
2024 if (g_layer_y < 0) g_layer_y = 0;
2025 if (g_layer_w > g_menuscreen_w) g_layer_w = g_menuscreen_w;
2026 if (g_layer_h > g_menuscreen_h) g_layer_w = g_menuscreen_h;
3c70c47b 2027}
2028
bd6267e6 2029static void menu_leave_emu(void)
2030{
6d1a1ac2 2031 if (GPU_close != NULL) {
2032 int ret = GPU_close();
2033 if (ret)
2034 fprintf(stderr, "Warning: GPU_close returned %d\n", ret);
2035 }
bd6267e6 2036
55b0eeea 2037 plat_video_menu_enter(ready_to_go);
2038
4f3639fa 2039 memcpy(g_menubg_ptr, g_menubg_src_ptr, g_menuscreen_w * g_menuscreen_h * 2);
76f7048e 2040 if (pl_vout_buf != NULL && ready_to_go && last_psx_bpp == 16) {
bd6267e6 2041 int x = max(0, g_menuscreen_w - last_psx_w);
2042 int y = max(0, g_menuscreen_h / 2 - last_psx_h / 2);
2043 int w = min(g_menuscreen_w, last_psx_w);
2044 int h = min(g_menuscreen_h, last_psx_h);
4f3639fa 2045 u16 *d = (u16 *)g_menubg_ptr + g_menuscreen_w * y + x;
76f7048e 2046 u16 *s = pl_vout_buf;
bd6267e6 2047
2048 for (; h > 0; h--, d += g_menuscreen_w, s += last_psx_w)
4f3639fa 2049 menu_darken_bg(d, s, w, 0);
bd6267e6 2050 }
fba06457 2051
1bd9ee68 2052 if (ready_to_go)
55b0eeea 2053 cpu_clock = plat_cpu_clock_get();
bd6267e6 2054}
2055
3c70c47b 2056void menu_prepare_emu(void)
2057{
b5e7e49a 2058 R3000Acpu *prev_cpu = psxCpu;
2059
fba06457 2060 plat_video_menu_leave();
2061
a185be70 2062 menu_notify_mode_change(last_psx_w, last_psx_h, last_psx_bpp);
bd6267e6 2063
b5e7e49a 2064 psxCpu = (Config.Cpu == CPU_INTERPRETER) ? &psxInt : &psxRec;
2065 if (psxCpu != prev_cpu)
2066 // note that this does not really reset, just clears drc caches
2067 psxCpu->Reset();
2068
bd6267e6 2069 // core doesn't care about Config.Cdda changes,
2070 // so handle them manually here
2071 if (Config.Cdda)
2072 CDR_stop();
6d1a1ac2 2073
907b1e90 2074 menu_sync_config();
cefe86b7 2075 apply_lcdrate(Config.PsxType);
2076 apply_filter(filter);
55b0eeea 2077 plat_cpu_clock_apply(cpu_clock);
fba06457 2078
e64dc4c5 2079 // push config to GPU plugin
2080 plugin_call_rearmed_cbs();
2081
6d1a1ac2 2082 if (GPU_open != NULL) {
6d1a1ac2 2083 int ret = GPU_open(&gpuDisp, "PCSX", NULL);
2084 if (ret)
2085 fprintf(stderr, "Warning: GPU_open returned %d\n", ret);
2086 }
384f5f43 2087
4c08b9e7 2088 dfinput_activate();
69af03a2 2089}
2090
2091void me_update_msg(const char *msg)
2092{
2093 strncpy(menu_error_msg, msg, sizeof(menu_error_msg));
2094 menu_error_msg[sizeof(menu_error_msg) - 1] = 0;
2095
2096 menu_error_time = plat_get_ticks_ms();
2097 lprintf("msg: %s\n", menu_error_msg);
2098}
2099