frontend: add screenshot functionality
[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 {
1246 menu_draw_begin(1);
1247 draw_menu_message(msg, NULL);
1248 menu_draw_end();
1249
1250 inp = in_menu_wait(PBTN_MOK|PBTN_MBACK, 70);
1251 if (inp & (PBTN_MBACK|PBTN_MOK))
1252 return;
1253 }
1254}
1255
1256// ------------ main menu ------------
1257
bd6267e6 1258void OnFile_Exit();
1259
1bd9ee68 1260static void draw_frame_main(void)
1261{
1262 if (CdromId[0] != 0) {
1263 char buff[64];
c22b95ab 1264 snprintf(buff, sizeof(buff), "%.32s/%.9s (running as %s, with %s)",
1265 get_cd_label(), CdromId, Config.PsxType ? "PAL" : "NTSC",
1266 Config.HLE ? "HLE" : "BIOS");
1bd9ee68 1267 smalltext_out16(4, 1, buff, 0x105f);
1268 }
1269}
1270
1271static void draw_frame_credits(void)
1272{
1273 smalltext_out16(4, 1, "build: "__DATE__ " " __TIME__ " " REV, 0xe7fc);
1274}
1275
4f5a1b2a 1276static const char credits_text[] =
1277 "PCSX-ReARMed\n\n"
1278 "(C) 1999-2003 PCSX Team\n"
1279 "(C) 2005-2009 PCSX-df Team\n"
1280 "(C) 2009-2011 PCSX-Reloaded Team\n\n"
1281 "GPU and SPU code by Pete Bernert\n"
1282 " and the P.E.Op.S. team\n"
1283 "ARM recompiler (C) 2009-2011 Ari64\n"
1284 "PCSX4ALL plugins by PCSX4ALL team\n"
1285 " Chui, Franxis, Unai\n\n"
1286 "integration, optimization and\n"
1287 " frontend (C) 2010-2011 notaz\n";
69af03a2 1288
e16a7e51 1289static int reset_game(void)
1290{
1291 // sanity check
1292 if (bios_sel == 0 && !Config.HLE)
1293 return -1;
1294
1295 ClosePlugins();
1296 OpenPlugins();
1297 SysReset();
1298 if (CheckCdrom() != -1) {
1299 LoadCdrom();
1300 }
1301 return 0;
1302}
1303
1304static int run_bios(void)
1305{
1306 if (bios_sel == 0)
1307 return -1;
1308
1309 ready_to_go = 0;
1310 pl_fbdev_buf = NULL;
1311
1312 ClosePlugins();
1313 set_cd_image(NULL);
1314 LoadPlugins();
c22b95ab 1315 pcnt_hook_plugins();
e16a7e51 1316 NetOpened = 0;
1317 if (OpenPlugins() == -1) {
1318 me_update_msg("failed to open plugins");
1319 return -1;
1320 }
1321 plugin_call_rearmed_cbs();
1322
1323 CdromId[0] = '\0';
1324 CdromLabel[0] = '\0';
1325
1326 SysReset();
1327
1328 ready_to_go = 1;
1329 return 0;
1330}
1331
bbd837c6 1332static int run_cd_image(const char *fname)
69af03a2 1333{
69af03a2 1334 ready_to_go = 0;
fba06457 1335 pl_fbdev_buf = NULL;
69af03a2 1336
fba06457 1337 ClosePlugins();
bbd837c6 1338 set_cd_image(fname);
69af03a2 1339 LoadPlugins();
c22b95ab 1340 pcnt_hook_plugins();
69af03a2 1341 NetOpened = 0;
1342 if (OpenPlugins() == -1) {
1343 me_update_msg("failed to open plugins");
bbd837c6 1344 return -1;
69af03a2 1345 }
201c21e2 1346 plugin_call_rearmed_cbs();
69af03a2 1347
69af03a2 1348 if (CheckCdrom() == -1) {
1349 // Only check the CD if we are starting the console with a CD
1350 ClosePlugins();
1351 me_update_msg("unsupported/invalid CD image");
bbd837c6 1352 return -1;
69af03a2 1353 }
1354
bbd837c6 1355 SysReset();
1356
69af03a2 1357 // Read main executable directly from CDRom and start it
1358 if (LoadCdrom() == -1) {
1359 ClosePlugins();
1360 me_update_msg("failed to load CD image");
bbd837c6 1361 return -1;
69af03a2 1362 }
1363
1364 ready_to_go = 1;
bbd837c6 1365 return 0;
69af03a2 1366}
1367
bbd837c6 1368static int romsel_run(void)
69af03a2 1369{
bbd837c6 1370 int prev_gpu, prev_spu;
1371 char *fname;
1372
1373 fname = menu_loop_romsel(last_selected_fname, sizeof(last_selected_fname));
1374 if (fname == NULL)
1375 return -1;
69af03a2 1376
bbd837c6 1377 printf("selected file: %s\n", fname);
1378
dc990066 1379 new_dynarec_clear_full();
1380
bbd837c6 1381 if (run_cd_image(fname) != 0)
1382 return -1;
1383
1384 prev_gpu = gpu_plugsel;
1385 prev_spu = spu_plugsel;
1386 if (menu_load_config(1) != 0)
1387 menu_load_config(0);
1388
1389 // check for plugin changes, have to repeat
1390 // loading if game config changed plugins to reload them
1391 if (prev_gpu != gpu_plugsel || prev_spu != spu_plugsel) {
1392 printf("plugin change detected, reloading plugins..\n");
1393 if (run_cd_image(fname) != 0)
1394 return -1;
1395 }
1396
1397 strcpy(last_selected_fname, rom_fname_reload);
1398 return 0;
1399}
1400
1df403c5 1401static int swap_cd_image(void)
1402{
1403 char *fname;
1404
1405 fname = menu_loop_romsel(last_selected_fname, sizeof(last_selected_fname));
1406 if (fname == NULL)
1407 return -1;
1408
1409 printf("selected file: %s\n", fname);
1410
1411 CdromId[0] = '\0';
1412 CdromLabel[0] = '\0';
1413
1414 set_cd_image(fname);
1415 if (ReloadCdromPlugin() < 0) {
1416 me_update_msg("failed to load cdr plugin");
1417 return -1;
1418 }
1419 if (CDR_open() < 0) {
1420 me_update_msg("failed to open cdr plugin");
1421 return -1;
1422 }
1423
1424 SetCdOpenCaseTime(time(NULL) + 2);
1425 LidInterrupt();
1426
1427 strcpy(last_selected_fname, rom_fname_reload);
1428 return 0;
1429}
1430
bbd837c6 1431static int main_menu_handler(int id, int keys)
1432{
69af03a2 1433 switch (id)
1434 {
1435 case MA_MAIN_RESUME_GAME:
3c70c47b 1436 if (ready_to_go)
1437 return 1;
69af03a2 1438 break;
1439 case MA_MAIN_SAVE_STATE:
1440 if (ready_to_go)
1441 return menu_loop_savestate(0);
1442 break;
1443 case MA_MAIN_LOAD_STATE:
1444 if (ready_to_go)
1445 return menu_loop_savestate(1);
1446 break;
1447 case MA_MAIN_RESET_GAME:
e16a7e51 1448 if (ready_to_go && reset_game() == 0)
3c70c47b 1449 return 1;
69af03a2 1450 break;
1451 case MA_MAIN_LOAD_ROM:
bbd837c6 1452 if (romsel_run() == 0)
69af03a2 1453 return 1;
1454 break;
1df403c5 1455 case MA_MAIN_SWAP_CD:
1456 if (swap_cd_image() == 0)
1457 return 1;
1458 break;
e16a7e51 1459 case MA_MAIN_RUN_BIOS:
1460 if (run_bios() == 0)
1461 return 1;
1462 break;
69af03a2 1463 case MA_MAIN_CREDITS:
4f5a1b2a 1464 draw_menu_message(credits_text, draw_frame_credits);
69af03a2 1465 in_menu_wait(PBTN_MOK|PBTN_MBACK, 70);
1466 break;
1467 case MA_MAIN_EXIT:
bd6267e6 1468 OnFile_Exit();
1469 break;
69af03a2 1470 default:
1471 lprintf("%s: something unknown selected\n", __FUNCTION__);
1472 break;
1473 }
1474
1475 return 0;
1476}
1477
1478static menu_entry e_menu_main[] =
1479{
1480 mee_label (""),
1481 mee_label (""),
1482 mee_handler_id("Resume game", MA_MAIN_RESUME_GAME, main_menu_handler),
1483 mee_handler_id("Save State", MA_MAIN_SAVE_STATE, main_menu_handler),
1484 mee_handler_id("Load State", MA_MAIN_LOAD_STATE, main_menu_handler),
1485 mee_handler_id("Reset game", MA_MAIN_RESET_GAME, main_menu_handler),
1486 mee_handler_id("Load CD image", MA_MAIN_LOAD_ROM, main_menu_handler),
1df403c5 1487 mee_handler_id("Change CD image", MA_MAIN_SWAP_CD, main_menu_handler),
e16a7e51 1488 mee_handler_id("Run BIOS", MA_MAIN_RUN_BIOS, main_menu_handler),
69af03a2 1489 mee_handler ("Options", menu_loop_options),
1490 mee_handler ("Controls", menu_loop_keyconfig),
1491 mee_handler_id("Credits", MA_MAIN_CREDITS, main_menu_handler),
1492 mee_handler_id("Exit", MA_MAIN_EXIT, main_menu_handler),
1493 mee_end,
1494};
1495
3c70c47b 1496// ----------------------------
1497
bd6267e6 1498static void menu_leave_emu(void);
1499
69af03a2 1500void menu_loop(void)
1501{
1502 static int sel = 0;
1503
bd6267e6 1504 menu_leave_emu();
69af03a2 1505
4f5a1b2a 1506 if (bioses[1] == NULL && !warned_about_bios) {
1507 menu_bios_warn();
1508 warned_about_bios = 1;
1509 }
1510
69af03a2 1511 me_enable(e_menu_main, MA_MAIN_RESUME_GAME, ready_to_go);
e16a7e51 1512 me_enable(e_menu_main, MA_MAIN_SAVE_STATE, ready_to_go && CdromId[0]);
1513 me_enable(e_menu_main, MA_MAIN_LOAD_STATE, ready_to_go && CdromId[0]);
69af03a2 1514 me_enable(e_menu_main, MA_MAIN_RESET_GAME, ready_to_go);
1df403c5 1515 me_enable(e_menu_main, MA_MAIN_SWAP_CD, ready_to_go);
e16a7e51 1516 me_enable(e_menu_main, MA_MAIN_RUN_BIOS, bios_sel != 0);
69af03a2 1517
69af03a2 1518 in_set_config_int(0, IN_CFG_BLOCKING, 1);
1519
1520 do {
1bd9ee68 1521 me_loop(e_menu_main, &sel, draw_frame_main);
69af03a2 1522 } while (!ready_to_go);
1523
1524 /* wait until menu, ok, back is released */
1525 while (in_menu_wait_any(50) & (PBTN_MENU|PBTN_MOK|PBTN_MBACK))
1526 ;
1527
1528 in_set_config_int(0, IN_CFG_BLOCKING, 0);
1529
3c70c47b 1530 menu_prepare_emu();
1531}
1532
e6eb2066 1533static void scan_bios_plugins(void)
bbd837c6 1534{
1535 char fname[MAXPATHLEN];
1536 struct dirent *ent;
e6eb2066 1537 int bios_i, gpu_i, spu_i;
bbd837c6 1538 char *p;
1539 DIR *dir;
1540
e6eb2066 1541 bioses[0] = "HLE";
bbd837c6 1542 gpu_plugins[0] = "builtin_gpu";
1543 spu_plugins[0] = "builtin_spu";
e6eb2066 1544 bios_i = gpu_i = spu_i = 1;
1545
1546 snprintf(fname, sizeof(fname), "%s/", Config.BiosDir);
1547 dir = opendir(fname);
1548 if (dir == NULL) {
1549 perror("scan_bios_plugins bios opendir");
1550 goto do_plugins;
1551 }
1552
1553 while (1) {
1554 struct stat st;
1555
1556 errno = 0;
1557 ent = readdir(dir);
1558 if (ent == NULL) {
1559 if (errno != 0)
1560 perror("readdir");
1561 break;
1562 }
1563
1564 if (ent->d_type != DT_REG && ent->d_type != DT_LNK)
1565 continue;
1566
1567 snprintf(fname, sizeof(fname), "%s/%s", Config.BiosDir, ent->d_name);
1568 if (stat(fname, &st) != 0 || st.st_size != 512*1024) {
1569 printf("bad BIOS file: %s\n", ent->d_name);
1570 continue;
1571 }
1572
1573 if (bios_i < ARRAY_SIZE(bioses) - 1) {
1574 bioses[bios_i++] = strdup(ent->d_name);
1575 continue;
1576 }
1577
1578 printf("too many BIOSes, dropping \"%s\"\n", ent->d_name);
1579 }
1580
1581 closedir(dir);
bbd837c6 1582
e6eb2066 1583do_plugins:
bbd837c6 1584 snprintf(fname, sizeof(fname), "%s/", Config.PluginsDir);
1585 dir = opendir(fname);
1586 if (dir == NULL) {
e6eb2066 1587 perror("scan_bios_plugins opendir");
bbd837c6 1588 return;
1589 }
1590
1591 while (1) {
1592 void *h, *tmp;
1593
1594 errno = 0;
1595 ent = readdir(dir);
1596 if (ent == NULL) {
1597 if (errno != 0)
1598 perror("readdir");
1599 break;
1600 }
1601 p = strstr(ent->d_name, ".so");
1602 if (p == NULL)
1603 continue;
1604
1605 snprintf(fname, sizeof(fname), "%s/%s", Config.PluginsDir, ent->d_name);
1606 h = dlopen(fname, RTLD_LAZY | RTLD_LOCAL);
1607 if (h == NULL) {
1608 fprintf(stderr, "%s\n", dlerror());
1609 continue;
1610 }
1611
1612 // now what do we have here?
1613 tmp = dlsym(h, "GPUinit");
1614 if (tmp) {
1615 dlclose(h);
1616 if (gpu_i < ARRAY_SIZE(gpu_plugins) - 1)
1617 gpu_plugins[gpu_i++] = strdup(ent->d_name);
1618 continue;
1619 }
1620
1621 tmp = dlsym(h, "SPUinit");
1622 if (tmp) {
1623 dlclose(h);
1624 if (spu_i < ARRAY_SIZE(spu_plugins) - 1)
1625 spu_plugins[spu_i++] = strdup(ent->d_name);
1626 continue;
1627 }
1628
1629 fprintf(stderr, "ignoring unidentified plugin: %s\n", fname);
1630 dlclose(h);
1631 }
1632
1633 closedir(dir);
1634}
1635
3c70c47b 1636void menu_init(void)
1637{
4f3639fa 1638 char buff[MAXPATHLEN];
1639
1640 strcpy(last_selected_fname, "/media");
1641
e6eb2066 1642 scan_bios_plugins();
4f3639fa 1643 pnd_menu_init();
1644 menu_init_common();
1645
3c70c47b 1646 menu_set_defconfig();
1647 menu_load_config(0);
3c70c47b 1648 last_psx_w = 320;
1649 last_psx_h = 240;
bd6267e6 1650 last_psx_bpp = 16;
1651
4f3639fa 1652 g_menubg_src_ptr = calloc(g_menuscreen_w * g_menuscreen_h * 2, 1);
1653 if (g_menubg_src_ptr == NULL)
1654 exit(1);
1655 emu_make_path(buff, "skin/background.png", sizeof(buff));
1656 readpng(g_menubg_src_ptr, buff, READPNG_BG, g_menuscreen_w, g_menuscreen_h);
3c70c47b 1657}
1658
bd6267e6 1659void menu_notify_mode_change(int w, int h, int bpp)
3c70c47b 1660{
1661 last_psx_w = w;
1662 last_psx_h = h;
bd6267e6 1663 last_psx_bpp = bpp;
3c70c47b 1664
1665 if (scaling == SCALE_1_1) {
1666 g_layer_x = 800/2 - w/2; g_layer_y = 480/2 - h/2;
1667 g_layer_w = w; g_layer_h = h;
3c70c47b 1668 }
1669}
1670
bd6267e6 1671static void menu_leave_emu(void)
1672{
6d1a1ac2 1673 if (GPU_close != NULL) {
1674 int ret = GPU_close();
1675 if (ret)
1676 fprintf(stderr, "Warning: GPU_close returned %d\n", ret);
1677 }
bd6267e6 1678
4f3639fa 1679 memcpy(g_menubg_ptr, g_menubg_src_ptr, g_menuscreen_w * g_menuscreen_h * 2);
fba06457 1680 if (pl_fbdev_buf != NULL && ready_to_go && last_psx_bpp == 16) {
bd6267e6 1681 int x = max(0, g_menuscreen_w - last_psx_w);
1682 int y = max(0, g_menuscreen_h / 2 - last_psx_h / 2);
1683 int w = min(g_menuscreen_w, last_psx_w);
1684 int h = min(g_menuscreen_h, last_psx_h);
4f3639fa 1685 u16 *d = (u16 *)g_menubg_ptr + g_menuscreen_w * y + x;
bd6267e6 1686 u16 *s = pl_fbdev_buf;
1687
1688 for (; h > 0; h--, d += g_menuscreen_w, s += last_psx_w)
4f3639fa 1689 menu_darken_bg(d, s, w, 0);
bd6267e6 1690 }
fba06457 1691
1bd9ee68 1692 if (ready_to_go)
1693 cpu_clock = get_cpu_clock();
1694
fba06457 1695 plat_video_menu_enter(ready_to_go);
bd6267e6 1696}
1697
3c70c47b 1698void menu_prepare_emu(void)
1699{
b5e7e49a 1700 R3000Acpu *prev_cpu = psxCpu;
1701
fba06457 1702 plat_video_menu_leave();
1703
3c70c47b 1704 switch (scaling) {
1705 case SCALE_1_1:
bd6267e6 1706 menu_notify_mode_change(last_psx_w, last_psx_h, last_psx_bpp);
3c70c47b 1707 break;
1708 case SCALE_4_3:
1709 g_layer_x = 80; g_layer_y = 0;
1710 g_layer_w = 640; g_layer_h = 480;
1711 break;
1712 case SCALE_FULLSCREEN:
1713 g_layer_x = 0; g_layer_y = 0;
1714 g_layer_w = 800; g_layer_h = 480;
1715 break;
1716 case SCALE_CUSTOM:
1717 break;
1718 }
3c70c47b 1719 apply_filter(filter);
1720 apply_cpu_clock();
bd6267e6 1721
b5e7e49a 1722 psxCpu = (Config.Cpu == CPU_INTERPRETER) ? &psxInt : &psxRec;
1723 if (psxCpu != prev_cpu)
1724 // note that this does not really reset, just clears drc caches
1725 psxCpu->Reset();
1726
bd6267e6 1727 // core doesn't care about Config.Cdda changes,
1728 // so handle them manually here
1729 if (Config.Cdda)
1730 CDR_stop();
6d1a1ac2 1731
907b1e90 1732 menu_sync_config();
fba06457 1733
6d1a1ac2 1734 if (GPU_open != NULL) {
6d1a1ac2 1735 int ret = GPU_open(&gpuDisp, "PCSX", NULL);
1736 if (ret)
1737 fprintf(stderr, "Warning: GPU_open returned %d\n", ret);
1738 }
384f5f43 1739
1740 dfinput_activate(in_type == PSE_PAD_TYPE_ANALOGPAD);
69af03a2 1741}
1742
1743void me_update_msg(const char *msg)
1744{
1745 strncpy(menu_error_msg, msg, sizeof(menu_error_msg));
1746 menu_error_msg[sizeof(menu_error_msg) - 1] = 0;
1747
1748 menu_error_time = plat_get_ticks_ms();
1749 lprintf("msg: %s\n", menu_error_msg);
1750}
1751