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