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