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