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