prepare for external gpu plugins
[pcsx_rearmed.git] / frontend / menu.c
CommitLineData
69af03a2 1/*
2 * (C) GraÅžvydas "notaz" Ignotas, 2010
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>
69af03a2 15
3c70c47b 16#include "menu.h"
69af03a2 17#include "config.h"
18#include "plugin_lib.h"
19#include "omap.h"
20#include "common/plat.h"
3c70c47b 21#include "../gui/Linux.h"
69af03a2 22#include "../libpcsxcore/misc.h"
7eda34c1 23#include "../libpcsxcore/new_dynarec/new_dynarec.h"
3c70c47b 24#include "revision.h"
69af03a2 25
26#define MENU_X2 1
27#define array_size(x) (sizeof(x) / sizeof(x[0]))
28
29typedef enum
30{
31 MA_NONE = 1,
32 MA_MAIN_RESUME_GAME,
33 MA_MAIN_SAVE_STATE,
34 MA_MAIN_LOAD_STATE,
35 MA_MAIN_RESET_GAME,
36 MA_MAIN_LOAD_ROM,
37 MA_MAIN_CONTROLS,
38 MA_MAIN_CREDITS,
39 MA_MAIN_EXIT,
40 MA_CTRL_PLAYER1,
41 MA_CTRL_PLAYER2,
42 MA_CTRL_EMU,
43 MA_CTRL_DEV_FIRST,
44 MA_CTRL_DEV_NEXT,
45 MA_CTRL_DONE,
46 MA_OPT_SAVECFG,
47 MA_OPT_SAVECFG_GAME,
48 MA_OPT_CPU_CLOCKS,
3c70c47b 49 MA_OPT_FILTERING,
69af03a2 50} menu_id;
51
3c70c47b 52enum {
53 SCALE_1_1,
54 SCALE_4_3,
55 SCALE_FULLSCREEN,
56 SCALE_CUSTOM,
57};
69af03a2 58
3c70c47b 59extern int ready_to_go;
bd6267e6 60static int last_psx_w, last_psx_h, last_psx_bpp;
3c70c47b 61static int scaling, filter, state_slot, cpu_clock;
69af03a2 62static char rom_fname_reload[MAXPATHLEN];
63static char last_selected_fname[MAXPATHLEN];
bd6267e6 64int g_opts;
65
66// from softgpu plugin
67extern int iUseDither;
68extern int UseFrameSkip;
fba06457 69extern int UseFrameLimit;
bd6267e6 70extern uint32_t dwActFixes;
fba06457 71extern float fFrameRateHz;
72extern int dwFrameRateTicks;
bd6267e6 73
74// sound plugin
75extern int iUseReverb;
76extern int iUseInterpolation;
77extern int iXAPitch;
78extern int iSPUIRQWait;
79extern int iUseTimer;
80
bbd837c6 81static const char *gpu_plugins[16];
82static const char *spu_plugins[16];
83static int gpu_plugsel, spu_plugsel;
84
bd6267e6 85
86static int min(int x, int y) { return x < y ? x : y; }
87static int max(int x, int y) { return x > y ? x : y; }
69af03a2 88
89void emu_make_path(char *buff, const char *end, int size)
90{
91 int pos, end_len;
92
93 end_len = strlen(end);
94 pos = plat_get_root_dir(buff, size);
95 strncpy(buff + pos, end, size - pos);
96 buff[size - 1] = 0;
97 if (pos + end_len > size - 1)
98 printf("Warning: path truncated: %s\n", buff);
99}
100
101static int emu_check_save_file(int slot)
102{
3c70c47b 103 char *fname;
104 int ret;
105
106 fname = get_state_filename(slot);
107 if (fname == NULL)
108 return 0;
109
110 ret = CheckState(fname);
111 free(fname);
112 return ret == 0 ? 1 : 0;
69af03a2 113}
114
115static int emu_save_load_game(int load, int sram)
116{
3c70c47b 117 char *fname;
118 int ret;
119
120 fname = get_state_filename(state_slot);
121 if (fname == NULL)
122 return 0;
123
124 if (load)
125 ret = LoadState(fname);
126 else
127 ret = SaveState(fname);
128 free(fname);
129
130 return ret;
69af03a2 131}
132
3c70c47b 133static void draw_savestate_bg(int slot)
69af03a2 134{
69af03a2 135}
136
3c70c47b 137static void menu_set_defconfig(void)
138{
139 scaling = SCALE_4_3;
bd6267e6 140
141 Config.Xa = Config.Cdda = Config.Sio =
142 Config.SpuIrq = Config.RCntFix = Config.VSyncWA = 0;
143
144 iUseDither = UseFrameSkip = 0;
fba06457 145 UseFrameLimit = 1;
bd6267e6 146 dwActFixes = 1<<7;
147
148 iUseReverb = 2;
149 iUseInterpolation = 1;
150 iXAPitch = iSPUIRQWait = 0;
151 iUseTimer = 2;
3c70c47b 152}
153
4f3639fa 154#define CE_CONFIG_STR(val) \
155 { #val, 0, Config.val }
156
157#define CE_CONFIG_VAL(val) \
158 { #val, sizeof(Config.val), &Config.val }
159
160#define CE_STR(val) \
161 { #val, 0, val }
162
163#define CE_INTVAL(val) \
164 { #val, sizeof(val), &val }
165
166static const struct {
167 const char *name;
168 size_t len;
169 void *val;
170} config_data[] = {
171 CE_CONFIG_STR(Bios),
172 CE_CONFIG_STR(Gpu),
173 CE_CONFIG_STR(Spu),
174 CE_CONFIG_STR(Cdr),
175 CE_CONFIG_VAL(Xa),
176 CE_CONFIG_VAL(Sio),
177 CE_CONFIG_VAL(Mdec),
178 CE_CONFIG_VAL(PsxAuto),
179 CE_CONFIG_VAL(Cdda),
180 CE_CONFIG_VAL(Debug),
181 CE_CONFIG_VAL(PsxOut),
182 CE_CONFIG_VAL(SpuIrq),
183 CE_CONFIG_VAL(RCntFix),
184 CE_CONFIG_VAL(VSyncWA),
185 CE_CONFIG_VAL(Cpu),
186 CE_CONFIG_VAL(PsxType),
187 CE_INTVAL(scaling),
cd6e8d0f 188 CE_INTVAL(g_layer_x),
189 CE_INTVAL(g_layer_y),
190 CE_INTVAL(g_layer_w),
191 CE_INTVAL(g_layer_h),
4f3639fa 192 CE_INTVAL(filter),
193 CE_INTVAL(state_slot),
194 CE_INTVAL(cpu_clock),
195 CE_INTVAL(g_opts),
196 CE_INTVAL(iUseDither),
197 CE_INTVAL(UseFrameSkip),
fba06457 198 CE_INTVAL(UseFrameLimit),
4f3639fa 199 CE_INTVAL(dwActFixes),
200 CE_INTVAL(iUseReverb),
201 CE_INTVAL(iUseInterpolation),
202 CE_INTVAL(iXAPitch),
203 CE_INTVAL(iSPUIRQWait),
204 CE_INTVAL(iUseTimer),
205};
206
cd6e8d0f 207static void make_cfg_fname(char *buf, size_t size, int is_game)
208{
209 char trimlabel[33];
210 int j;
211
212 strncpy(trimlabel, CdromLabel, 32);
213 trimlabel[32] = 0;
214 for (j = 31; j >= 0; j--)
215 if (trimlabel[j] == ' ')
216 trimlabel[j] = 0;
217
218 if (is_game)
bbd837c6 219 snprintf(buf, size, "." PCSX_DOT_DIR "cfg/%.32s-%.9s.cfg", trimlabel, CdromId);
cd6e8d0f 220 else
bbd837c6 221 snprintf(buf, size, "." PCSX_DOT_DIR "%s", cfgfile_basename);
cd6e8d0f 222}
223
3c70c47b 224static int menu_write_config(int is_game)
225{
4f3639fa 226 char cfgfile[MAXPATHLEN];
227 FILE *f;
228 int i;
229
cd6e8d0f 230 make_cfg_fname(cfgfile, sizeof(cfgfile), is_game);
4f3639fa 231 f = fopen(cfgfile, "w");
232 if (f == NULL) {
bbd837c6 233 printf("menu_write_config: failed to open: %s\n", cfgfile);
4f3639fa 234 return -1;
235 }
236
237 for (i = 0; i < ARRAY_SIZE(config_data); i++) {
238 fprintf(f, "%s = ", config_data[i].name);
239 switch (config_data[i].len) {
240 case 0:
241 fprintf(f, "%s\n", (char *)config_data[i].val);
242 break;
243 case 1:
244 fprintf(f, "%x\n", *(u8 *)config_data[i].val);
245 break;
246 case 2:
247 fprintf(f, "%x\n", *(u16 *)config_data[i].val);
248 break;
249 case 4:
250 fprintf(f, "%x\n", *(u32 *)config_data[i].val);
251 break;
252 default:
253 printf("menu_write_config: unhandled len %d for %s\n",
254 config_data[i].len, config_data[i].name);
255 break;
256 }
257 }
258
259 if (!is_game)
260 fprintf(f, "lastcdimg = %s\n", last_selected_fname);
261
262 fclose(f);
263 return 0;
264}
265
266static void parse_str_val(char *cval, const char *src)
267{
268 char *tmp;
269 strncpy(cval, src, MAXPATHLEN);
270 cval[MAXPATHLEN - 1] = 0;
271 tmp = strchr(cval, '\n');
272 if (tmp == NULL)
273 tmp = strchr(cval, '\r');
274 if (tmp != NULL)
275 *tmp = 0;
3c70c47b 276}
277
278static int menu_load_config(int is_game)
69af03a2 279{
4f3639fa 280 char cfgfile[MAXPATHLEN];
281 int i, ret = -1;
282 long size;
283 char *cfg;
284 FILE *f;
285
cd6e8d0f 286 make_cfg_fname(cfgfile, sizeof(cfgfile), is_game);
4f3639fa 287 f = fopen(cfgfile, "r");
288 if (f == NULL) {
bbd837c6 289 printf("menu_load_config: failed to open: %s\n", cfgfile);
4f3639fa 290 return -1;
291 }
292
293 fseek(f, 0, SEEK_END);
294 size = ftell(f);
295 if (size <= 0) {
296 printf("bad size %ld: %s\n", size, cfgfile);
297 goto fail;
298 }
299
300 cfg = malloc(size + 1);
301 if (cfg == NULL)
302 goto fail;
303
304 fseek(f, 0, SEEK_SET);
305 if (fread(cfg, 1, size, f) != size) {
306 printf("failed to read: %s\n", cfgfile);
307 goto fail_read;
308 }
309 cfg[size] = 0;
310
311 for (i = 0; i < ARRAY_SIZE(config_data); i++) {
312 char *tmp, *tmp2;
313 u32 val;
314
315 tmp = strstr(cfg, config_data[i].name);
316 if (tmp == NULL)
317 continue;
318 tmp += strlen(config_data[i].name);
319 if (strncmp(tmp, " = ", 3) != 0)
320 continue;
321 tmp += 3;
322
323 if (config_data[i].len == 0) {
324 parse_str_val(config_data[i].val, tmp);
325 continue;
326 }
327
328 tmp2 = NULL;
329 val = strtoul(tmp, &tmp2, 16);
330 if (tmp2 == NULL || tmp == tmp2)
331 continue; // parse failed
332
333 switch (config_data[i].len) {
334 case 1:
335 *(u8 *)config_data[i].val = val;
336 break;
337 case 2:
338 *(u16 *)config_data[i].val = val;
339 break;
340 case 4:
341 *(u32 *)config_data[i].val = val;
342 break;
343 default:
344 printf("menu_load_config: unhandled len %d for %s\n",
345 config_data[i].len, config_data[i].name);
346 break;
347 }
348 }
349
350 if (!is_game) {
351 char *tmp = strstr(cfg, "lastcdimg = ");
352 if (tmp != NULL) {
353 tmp += 12;
354 parse_str_val(last_selected_fname, tmp);
355 }
356 }
357
bbd837c6 358 // sync plugins
359 for (i = gpu_plugsel = 0; gpu_plugins[i] != NULL; i++)
360 if (strcmp(Config.Gpu, gpu_plugins[i]) == 0)
361 { gpu_plugsel = i; break; }
362
363 for (i = spu_plugsel = 0; spu_plugins[i] != NULL; i++)
364 if (strcmp(Config.Spu, spu_plugins[i]) == 0)
365 { spu_plugsel = i; break; }
366
367 ret = 0;
4f3639fa 368fail_read:
369 free(cfg);
370fail:
371 fclose(f);
372 return ret;
69af03a2 373}
374
9564e73d 375// rrrr rggg gggb bbbb
376static unsigned short fname2color(const char *fname)
377{
87c06e51 378 static const char *cdimg_exts[] = { ".bin", ".img", ".iso", ".z", ".cue" };
379 static const char *other_exts[] = { ".ccd", ".toc", ".mds", ".sub", ".table" };
9564e73d 380 const char *ext = strrchr(fname, '.');
381 int i;
382
383 if (ext == NULL)
384 return 0xffff;
385 for (i = 0; i < array_size(cdimg_exts); i++)
386 if (strcasecmp(ext, cdimg_exts[i]) == 0)
387 return 0x7bff;
388 for (i = 0; i < array_size(other_exts); i++)
389 if (strcasecmp(ext, other_exts[i]) == 0)
390 return 0xa514;
391 return 0xffff;
392}
393
bd6267e6 394#define MENU_ALIGN_LEFT
3c70c47b 395#define menu_init menu_init_common
69af03a2 396#include "common/menu.c"
3c70c47b 397#undef menu_init
69af03a2 398
3c70c47b 399// ---------- pandora specific -----------
400
401static const char pnd_script_base[] = "sudo -n /usr/pandora/scripts";
402static char **pnd_filter_list;
403
404static int get_cpu_clock(void)
69af03a2 405{
3c70c47b 406 FILE *f;
407 int ret = 0;
408 f = fopen("/proc/pandora/cpu_mhz_max", "r");
409 if (f) {
410 fscanf(f, "%d", &ret);
411 fclose(f);
412 }
413 return ret;
414}
415
416static void apply_cpu_clock(void)
417{
418 char buf[128];
419
420 if (cpu_clock != 0 && cpu_clock != get_cpu_clock()) {
421 snprintf(buf, sizeof(buf), "unset DISPLAY; echo y | %s/op_cpuspeed.sh %d",
422 pnd_script_base, cpu_clock);
423 system(buf);
424 }
425}
426
427static void apply_filter(int which)
428{
4f3639fa 429 static int old = -1;
3c70c47b 430 char buf[128];
431 int i;
432
4f3639fa 433 if (pnd_filter_list == NULL || which == old)
3c70c47b 434 return;
435
436 for (i = 0; i < which; i++)
437 if (pnd_filter_list[i] == NULL)
438 return;
439
440 if (pnd_filter_list[i] == NULL)
441 return;
442
443 snprintf(buf, sizeof(buf), "%s/op_videofir.sh %s", pnd_script_base, pnd_filter_list[i]);
444 system(buf);
4f3639fa 445 old = which;
3c70c47b 446}
447
448static menu_entry e_menu_gfx_options[];
449
450static void pnd_menu_init(void)
451{
452 struct dirent *ent;
453 int i, count = 0;
454 char **mfilters;
455 char buff[64], *p;
456 DIR *dir;
457
458 cpu_clock = get_cpu_clock();
459
460 dir = opendir("/etc/pandora/conf/dss_fir");
461 if (dir == NULL) {
462 perror("filter opendir");
463 return;
464 }
465
466 while (1) {
467 errno = 0;
468 ent = readdir(dir);
469 if (ent == NULL) {
470 if (errno != 0)
471 perror("readdir");
472 break;
473 }
474 p = strstr(ent->d_name, "_up");
475 if (p != NULL && (p[3] == 0 || !strcmp(p + 3, "_h")))
476 count++;
477 }
478
479 if (count == 0)
480 return;
481
482 mfilters = calloc(count + 1, sizeof(mfilters[0]));
483 if (mfilters == NULL)
484 return;
485
486 rewinddir(dir);
487 for (i = 0; (ent = readdir(dir)); ) {
488 size_t len;
489
490 p = strstr(ent->d_name, "_up");
491 if (p == NULL || (p[3] != 0 && strcmp(p + 3, "_h")))
492 continue;
493
494 len = p - ent->d_name;
495 if (len > sizeof(buff) - 1)
496 continue;
497
498 strncpy(buff, ent->d_name, len);
499 buff[len] = 0;
500 mfilters[i] = strdup(buff);
501 if (mfilters[i] != NULL)
502 i++;
503 }
504 closedir(dir);
505
506 i = me_id2offset(e_menu_gfx_options, MA_OPT_FILTERING);
507 e_menu_gfx_options[i].data = (void *)mfilters;
508 pnd_filter_list = mfilters;
69af03a2 509}
510
511// -------------- key config --------------
512
513me_bind_action me_ctrl_actions[] =
514{
515 { "UP ", 1 << DKEY_UP},
516 { "DOWN ", 1 << DKEY_DOWN },
517 { "LEFT ", 1 << DKEY_LEFT },
518 { "RIGHT ", 1 << DKEY_RIGHT },
519 { "TRIANGLE", 1 << DKEY_TRIANGLE },
22a8a805 520 { "CIRCLE ", 1 << DKEY_CIRCLE },
69af03a2 521 { "CROSS ", 1 << DKEY_CROSS },
522 { "SQUARE ", 1 << DKEY_SQUARE },
523 { "L1 ", 1 << DKEY_L1 },
524 { "R1 ", 1 << DKEY_R1 },
525 { "L2 ", 1 << DKEY_L2 },
526 { "R2 ", 1 << DKEY_R2 },
527 { "START ", 1 << DKEY_START },
528 { "SELECT ", 1 << DKEY_SELECT },
529 { NULL, 0 }
530};
531
532me_bind_action emuctrl_actions[] =
533{
bd6267e6 534/*
69af03a2 535 { "Load State ", PEV_STATE_LOAD },
536 { "Save State ", PEV_STATE_SAVE },
537 { "Prev Save Slot ", PEV_SSLOT_PREV },
538 { "Next Save Slot ", PEV_SSLOT_NEXT },
bd6267e6 539*/
69af03a2 540 { "Enter Menu ", PEV_MENU },
541 { NULL, 0 }
542};
543
544static int key_config_loop_wrap(int id, int keys)
545{
546 switch (id) {
547 case MA_CTRL_PLAYER1:
548 key_config_loop(me_ctrl_actions, array_size(me_ctrl_actions) - 1, 0);
549 break;
550 case MA_CTRL_PLAYER2:
551 key_config_loop(me_ctrl_actions, array_size(me_ctrl_actions) - 1, 1);
552 break;
553 case MA_CTRL_EMU:
554 key_config_loop(emuctrl_actions, array_size(emuctrl_actions) - 1, -1);
555 break;
556 default:
557 break;
558 }
559 return 0;
560}
561
562static const char *mgn_dev_name(int id, int *offs)
563{
564 const char *name = NULL;
565 static int it = 0;
566
567 if (id == MA_CTRL_DEV_FIRST)
568 it = 0;
569
570 for (; it < IN_MAX_DEVS; it++) {
571 name = in_get_dev_name(it, 1, 1);
572 if (name != NULL)
573 break;
574 }
575
576 it++;
577 return name;
578}
579
580static const char *mgn_saveloadcfg(int id, int *offs)
581{
582 return "";
583}
584
cd6e8d0f 585static int mh_savecfg(int id, int keys)
69af03a2 586{
cd6e8d0f 587 if (menu_write_config(id == MA_OPT_SAVECFG_GAME ? 1 : 0) == 0)
588 me_update_msg("config saved");
589 else
590 me_update_msg("failed to write config");
69af03a2 591
592 return 1;
593}
594
595static menu_entry e_menu_keyconfig[] =
596{
597 mee_handler_id("Player 1", MA_CTRL_PLAYER1, key_config_loop_wrap),
598 mee_handler_id("Player 2", MA_CTRL_PLAYER2, key_config_loop_wrap),
599 mee_handler_id("Emulator controls", MA_CTRL_EMU, key_config_loop_wrap),
cd6e8d0f 600// mee_cust_nosave("Save global config", MA_OPT_SAVECFG, mh_savecfg, mgn_saveloadcfg),
601// mee_cust_nosave("Save cfg for loaded game", MA_OPT_SAVECFG_GAME, mh_savecfg, mgn_saveloadcfg),
69af03a2 602 mee_label (""),
603 mee_label ("Input devices:"),
604 mee_label_mk (MA_CTRL_DEV_FIRST, mgn_dev_name),
605 mee_label_mk (MA_CTRL_DEV_NEXT, mgn_dev_name),
606 mee_label_mk (MA_CTRL_DEV_NEXT, mgn_dev_name),
607 mee_label_mk (MA_CTRL_DEV_NEXT, mgn_dev_name),
608 mee_label_mk (MA_CTRL_DEV_NEXT, mgn_dev_name),
609 mee_label_mk (MA_CTRL_DEV_NEXT, mgn_dev_name),
610 mee_label_mk (MA_CTRL_DEV_NEXT, mgn_dev_name),
611 mee_end,
612};
613
614static int menu_loop_keyconfig(int id, int keys)
615{
616 static int sel = 0;
617
4f3639fa 618// me_enable(e_menu_keyconfig, MA_OPT_SAVECFG_GAME, ready_to_go);
69af03a2 619 me_loop(e_menu_keyconfig, &sel, NULL);
620 return 0;
621}
622
69af03a2 623// ------------ gfx options menu ------------
624
3c70c47b 625static const char *men_scaler[] = { "1x1", "scaled 4:3", "fullscreen", "custom", NULL };
626static const char h_cscaler[] = "Displays the scaler layer, you can resize it\n"
627 "using d-pad or move it using R+d-pad";
628static const char *men_dummy[] = { NULL };
629
630static int menu_loop_cscaler(int id, int keys)
631{
632 unsigned int inp;
633
634 scaling = SCALE_CUSTOM;
635
636 omap_enable_layer(1);
637 //pnd_restore_layer_data();
638
639 for (;;)
640 {
641 menu_draw_begin(0);
642 memset(g_menuscreen_ptr, 0, g_menuscreen_w * g_menuscreen_h * 2);
643 text_out16(2, 480 - 18, "%dx%d | d-pad to resize, R+d-pad to move", g_layer_w, g_layer_h);
644 menu_draw_end();
645
646 inp = in_menu_wait(PBTN_UP|PBTN_DOWN|PBTN_LEFT|PBTN_RIGHT|PBTN_R|PBTN_MOK|PBTN_MBACK, 40);
647 if (inp & PBTN_UP) g_layer_y--;
648 if (inp & PBTN_DOWN) g_layer_y++;
649 if (inp & PBTN_LEFT) g_layer_x--;
650 if (inp & PBTN_RIGHT) g_layer_x++;
651 if (!(inp & PBTN_R)) {
652 if (inp & PBTN_UP) g_layer_h += 2;
653 if (inp & PBTN_DOWN) g_layer_h -= 2;
654 if (inp & PBTN_LEFT) g_layer_w += 2;
655 if (inp & PBTN_RIGHT) g_layer_w -= 2;
656 }
657 if (inp & (PBTN_MOK|PBTN_MBACK))
658 break;
659
660 if (inp & (PBTN_UP|PBTN_DOWN|PBTN_LEFT|PBTN_RIGHT)) {
661 if (g_layer_x < 0) g_layer_x = 0;
662 if (g_layer_x > 640) g_layer_x = 640;
663 if (g_layer_y < 0) g_layer_y = 0;
664 if (g_layer_y > 420) g_layer_y = 420;
665 if (g_layer_w < 160) g_layer_w = 160;
666 if (g_layer_h < 60) g_layer_h = 60;
667 if (g_layer_x + g_layer_w > 800)
668 g_layer_w = 800 - g_layer_x;
669 if (g_layer_y + g_layer_h > 480)
670 g_layer_h = 480 - g_layer_y;
671 omap_enable_layer(1);
672 }
673 }
674
675 omap_enable_layer(0);
676
677 return 0;
678}
679
69af03a2 680static menu_entry e_menu_gfx_options[] =
681{
3c70c47b 682 mee_enum ("Scaler", 0, scaling, men_scaler),
683 mee_enum ("Filter", MA_OPT_FILTERING, filter, men_dummy),
684// mee_onoff ("Vsync", 0, vsync, 1),
685 mee_cust_h ("Setup custom scaler", 0, menu_loop_cscaler, NULL, h_cscaler),
69af03a2 686 mee_end,
687};
688
689static int menu_loop_gfx_options(int id, int keys)
690{
691 static int sel = 0;
692
693 me_loop(e_menu_gfx_options, &sel, NULL);
694
695 return 0;
696}
697
bd6267e6 698// ------------ bios/plugins ------------
699
700static const char *men_gpu_dithering[] = { "None", "Game dependant", "Always", NULL };
bd6267e6 701static const char h_gpu_0[] = "Needed for Chrono Cross";
702static const char h_gpu_1[] = "Capcom fighting games";
703static const char h_gpu_2[] = "Black screens in Lunar";
704static const char h_gpu_3[] = "Compatibility mode";
705static const char h_gpu_6[] = "Pandemonium 2";
706static const char h_gpu_7[] = "Skip every second frame";
707static const char h_gpu_8[] = "Needed by Dark Forces";
708static const char h_gpu_9[] = "better g-colors, worse textures";
709static const char h_gpu_10[] = "Toggle busy flags after drawing";
710
711static menu_entry e_menu_plugin_gpu[] =
712{
713 mee_enum ("Dithering", 0, iUseDither, men_gpu_dithering),
714 mee_onoff_h ("Odd/even bit hack", 0, dwActFixes, 1<<0, h_gpu_0),
715 mee_onoff_h ("Expand screen width", 0, dwActFixes, 1<<1, h_gpu_1),
716 mee_onoff_h ("Ignore brightness color", 0, dwActFixes, 1<<2, h_gpu_2),
717 mee_onoff_h ("Disable coordinate check", 0, dwActFixes, 1<<3, h_gpu_3),
718 mee_onoff_h ("Lazy screen update", 0, dwActFixes, 1<<6, h_gpu_6),
719 mee_onoff_h ("Old frame skipping", 0, dwActFixes, 1<<7, h_gpu_7),
720 mee_onoff_h ("Repeated flat tex triangles ",0,dwActFixes, 1<<8, h_gpu_8),
721 mee_onoff_h ("Draw quads with triangles", 0, dwActFixes, 1<<9, h_gpu_9),
722 mee_onoff_h ("Fake 'gpu busy' states", 0, dwActFixes, 1<<10, h_gpu_10),
723 mee_end,
724};
725
726static int menu_loop_plugin_gpu(int id, int keys)
727{
728 static int sel = 0;
729 me_loop(e_menu_plugin_gpu, &sel, NULL);
730 return 0;
731}
732
733static const char *men_spu_reverb[] = { "Off", "Fake", "On", NULL };
734static const char *men_spu_interp[] = { "None", "Simple", "Gaussian", "Cubic", NULL };
bd6267e6 735static const char h_spu_irq_wait[] = "Wait for CPU; only useful for some games, may cause glitches";
736static const char h_spu_thread[] = "Run sound emulation in separate thread";
737
738static menu_entry e_menu_plugin_spu[] =
739{
740 mee_enum ("Reverb", 0, iUseReverb, men_spu_reverb),
741 mee_enum ("Interpolation", 0, iUseInterpolation, men_spu_interp),
742 mee_onoff ("Adjust XA pitch", 0, iXAPitch, 1),
743 mee_onoff_h ("SPU IRQ Wait", 0, iSPUIRQWait, 1, h_spu_irq_wait),
744 mee_onoff_h ("Use sound thread", 0, iUseTimer, 1, h_spu_thread),
745 mee_end,
746};
747
748static int menu_loop_plugin_spu(int id, int keys)
749{
750 static int sel = 0;
751 me_loop(e_menu_plugin_spu, &sel, NULL);
752 return 0;
753}
754
bbd837c6 755static const char h_gpu[] = "Configure built-in P.E.Op.S. SoftGL Driver V1.17";
756static const char h_spu[] = "Configure built-in P.E.Op.S. Sound Driver V1.7";
757static const char h_plugin_xpu[] = "Must save config and reload the game\n"
758 "for plugin change to take effect";
759
bd6267e6 760static menu_entry e_menu_plugin_options[] =
761{
bbd837c6 762 mee_enum_h ("GPU plugin", 0, gpu_plugsel, gpu_plugins, h_plugin_xpu),
763 mee_enum_h ("SPU plugin", 0, spu_plugsel, spu_plugins, h_plugin_xpu),
bd6267e6 764 mee_handler_h ("Configure built-in GPU plugin", menu_loop_plugin_gpu, h_gpu),
765 mee_handler_h ("Configure built-in SPU plugin", menu_loop_plugin_spu, h_spu),
766 mee_end,
767};
768
769static int menu_loop_plugin_options(int id, int keys)
770{
771 static int sel = 0;
772 me_loop(e_menu_plugin_options, &sel, NULL);
bbd837c6 773
774 // sync plugins
775 snprintf(Config.Gpu, sizeof(Config.Gpu), "%s", gpu_plugins[gpu_plugsel]);
776 snprintf(Config.Spu, sizeof(Config.Spu), "%s", spu_plugins[spu_plugsel]);
777
bd6267e6 778 return 0;
779}
780
781// ------------ adv options menu ------------
782
fba06457 783static const char h_cfg_cpul[] = "Shows CPU usage in %%";
784static const char h_cfg_fl[] = "Keeps the game from running too fast";
bd6267e6 785static const char h_cfg_xa[] = "Disables XA sound, which can sometimes improve performance";
786static const char h_cfg_cdda[] = "Disable CD Audio for a performance boost\n"
787 "(proper .cue/.bin dump is needed otherwise)";
788static const char h_cfg_sio[] = "This should be enabled for certain memcards/gamepads";
789static const char h_cfg_spuirq[] = "Compatibility tweak; should probably be left off";
790static const char h_cfg_rcnt1[] = "Parasite Eve 2, Vandal Hearts 1/2 Fix";
791static const char h_cfg_rcnt2[] = "InuYasha Sengoku Battle Fix";
792
793static menu_entry e_menu_adv_options[] =
794{
fba06457 795 mee_onoff_h ("Show CPU load", 0, g_opts, OPT_SHOWCPU, h_cfg_cpul),
796 mee_onoff_h ("Frame Limiter", 0, UseFrameLimit, 1, h_cfg_fl),
bd6267e6 797 mee_onoff_h ("Disable XA Decoding", 0, Config.Xa, 1, h_cfg_xa),
798 mee_onoff_h ("Disable CD Audio", 0, Config.Cdda, 1, h_cfg_cdda),
799 mee_onoff_h ("SIO IRQ Always Enabled", 0, Config.Sio, 1, h_cfg_sio),
800 mee_onoff_h ("SPU IRQ Always Enabled", 0, Config.SpuIrq, 1, h_cfg_spuirq),
801 mee_onoff_h ("Rootcounter hack", 0, Config.RCntFix, 1, h_cfg_rcnt1),
802 mee_onoff_h ("Rootcounter hack 2", 0, Config.VSyncWA, 1, h_cfg_rcnt2),
803 mee_end,
804};
805
806static int menu_loop_adv_options(int id, int keys)
807{
808 static int sel = 0;
809 me_loop(e_menu_adv_options, &sel, NULL);
810 return 0;
811}
812
69af03a2 813// ------------ options menu ------------
814
69af03a2 815static int mh_restore_defaults(int id, int keys)
816{
3c70c47b 817 menu_set_defconfig();
69af03a2 818 me_update_msg("defaults restored");
819 return 1;
820}
821
bd6267e6 822static const char *men_region[] = { "NTSC", "PAL", NULL };
823/*
69af03a2 824static const char *men_confirm_save[] = { "OFF", "writes", "loads", "both", NULL };
825static const char h_confirm_save[] = "Ask for confirmation when overwriting save,\n"
826 "loading state or both";
bd6267e6 827*/
828static const char h_restore_def[] = "Switches back to default / recommended\n"
829 "configuration";
69af03a2 830
831static menu_entry e_menu_options[] =
832{
bd6267e6 833// mee_range ("Save slot", 0, state_slot, 0, 9),
834// mee_enum_h ("Confirm savestate", 0, dummy, men_confirm_save, h_confirm_save),
835 mee_onoff ("Frameskip", 0, UseFrameSkip, 1),
836 mee_onoff ("Show FPS", 0, g_opts, OPT_SHOWFPS),
837 mee_enum ("Region", 0, Config.PsxType, men_region),
3c70c47b 838 mee_range ("CPU clock", MA_OPT_CPU_CLOCKS, cpu_clock, 20, 5000),
69af03a2 839 mee_handler ("[Display]", menu_loop_gfx_options),
bd6267e6 840 mee_handler ("[BIOS/Plugins]", menu_loop_plugin_options),
69af03a2 841 mee_handler ("[Advanced]", menu_loop_adv_options),
cd6e8d0f 842 mee_cust_nosave("Save global config", MA_OPT_SAVECFG, mh_savecfg, mgn_saveloadcfg),
843 mee_cust_nosave("Save cfg for loaded game",MA_OPT_SAVECFG_GAME, mh_savecfg, mgn_saveloadcfg),
bd6267e6 844 mee_handler_h ("Restore default config", mh_restore_defaults, h_restore_def),
69af03a2 845 mee_end,
846};
847
848static int menu_loop_options(int id, int keys)
849{
850 static int sel = 0;
851 int i;
852
853 i = me_id2offset(e_menu_options, MA_OPT_CPU_CLOCKS);
3c70c47b 854 e_menu_options[i].enabled = cpu_clock != 0 ? 1 : 0;
cd6e8d0f 855 me_enable(e_menu_options, MA_OPT_SAVECFG_GAME, ready_to_go);
69af03a2 856
857 me_loop(e_menu_options, &sel, NULL);
858
859 return 0;
860}
861
862// ------------ debug menu ------------
863
69af03a2 864static void draw_frame_debug(void)
865{
3c70c47b 866 smalltext_out16(4, 1, "build: "__DATE__ " " __TIME__ " " REV, 0xe7fc);
69af03a2 867}
868
869static void debug_menu_loop(void)
870{
871 int inp;
872
873 while (1)
874 {
875 menu_draw_begin(1);
876 draw_frame_debug();
877 menu_draw_end();
878
879 inp = in_menu_wait(PBTN_MOK|PBTN_MBACK|PBTN_MA2|PBTN_MA3|PBTN_L|PBTN_R |
880 PBTN_UP|PBTN_DOWN|PBTN_LEFT|PBTN_RIGHT, 70);
881 if (inp & PBTN_MBACK)
882 return;
883 }
884}
885
886// ------------ main menu ------------
887
bd6267e6 888void OnFile_Exit();
889
69af03a2 890const char *plat_get_credits(void)
891{
bd6267e6 892 return "PCSX-ReARMed\n\n"
893 "(C) 1999-2003 PCSX Team\n"
69af03a2 894 "(C) 2005-2009 PCSX-df Team\n"
895 "(C) 2009-2010 PCSX-Reloaded Team\n\n"
896 "GPU and SPU code by Pete Bernert\n"
897 " and the P.E.Op.S. team\n"
bd6267e6 898 "ARM recompiler (C) 2009-2010 Ari64\n\n"
69af03a2 899 "integration, optimization and\n"
bd6267e6 900 " frontend (C) 2010 notaz\n";
69af03a2 901}
902
bbd837c6 903static int run_cd_image(const char *fname)
69af03a2 904{
47bf65ab 905 extern void set_cd_image(const char *fname);
69af03a2 906 ready_to_go = 0;
fba06457 907 pl_fbdev_buf = NULL;
69af03a2 908
fba06457 909 ClosePlugins();
bbd837c6 910 set_cd_image(fname);
69af03a2 911 LoadPlugins();
912 NetOpened = 0;
913 if (OpenPlugins() == -1) {
914 me_update_msg("failed to open plugins");
bbd837c6 915 return -1;
69af03a2 916 }
917
69af03a2 918 if (CheckCdrom() == -1) {
919 // Only check the CD if we are starting the console with a CD
920 ClosePlugins();
921 me_update_msg("unsupported/invalid CD image");
bbd837c6 922 return -1;
69af03a2 923 }
924
bbd837c6 925 SysReset();
926
69af03a2 927 // Read main executable directly from CDRom and start it
928 if (LoadCdrom() == -1) {
929 ClosePlugins();
930 me_update_msg("failed to load CD image");
bbd837c6 931 return -1;
69af03a2 932 }
933
934 ready_to_go = 1;
bbd837c6 935 return 0;
69af03a2 936}
937
bbd837c6 938static int romsel_run(void)
69af03a2 939{
bbd837c6 940 int prev_gpu, prev_spu;
941 char *fname;
942
943 fname = menu_loop_romsel(last_selected_fname, sizeof(last_selected_fname));
944 if (fname == NULL)
945 return -1;
69af03a2 946
bbd837c6 947 printf("selected file: %s\n", fname);
948
949 if (run_cd_image(fname) != 0)
950 return -1;
951
952 prev_gpu = gpu_plugsel;
953 prev_spu = spu_plugsel;
954 if (menu_load_config(1) != 0)
955 menu_load_config(0);
956
957 // check for plugin changes, have to repeat
958 // loading if game config changed plugins to reload them
959 if (prev_gpu != gpu_plugsel || prev_spu != spu_plugsel) {
960 printf("plugin change detected, reloading plugins..\n");
961 if (run_cd_image(fname) != 0)
962 return -1;
963 }
964
965 strcpy(last_selected_fname, rom_fname_reload);
966 return 0;
967}
968
969static int main_menu_handler(int id, int keys)
970{
69af03a2 971 switch (id)
972 {
973 case MA_MAIN_RESUME_GAME:
3c70c47b 974 if (ready_to_go)
975 return 1;
69af03a2 976 break;
977 case MA_MAIN_SAVE_STATE:
978 if (ready_to_go)
979 return menu_loop_savestate(0);
980 break;
981 case MA_MAIN_LOAD_STATE:
982 if (ready_to_go)
983 return menu_loop_savestate(1);
984 break;
985 case MA_MAIN_RESET_GAME:
3c70c47b 986 if (ready_to_go) {
fba06457 987 ClosePlugins();
3c70c47b 988 OpenPlugins();
989 SysReset();
990 if (CheckCdrom() != -1) {
991 LoadCdrom();
992 }
993 return 1;
994 }
69af03a2 995 break;
996 case MA_MAIN_LOAD_ROM:
bbd837c6 997 if (romsel_run() == 0)
69af03a2 998 return 1;
999 break;
1000 case MA_MAIN_CREDITS:
3c70c47b 1001 draw_menu_credits(draw_frame_debug);
69af03a2 1002 in_menu_wait(PBTN_MOK|PBTN_MBACK, 70);
1003 break;
1004 case MA_MAIN_EXIT:
bd6267e6 1005 OnFile_Exit();
1006 break;
69af03a2 1007 default:
1008 lprintf("%s: something unknown selected\n", __FUNCTION__);
1009 break;
1010 }
1011
1012 return 0;
1013}
1014
1015static menu_entry e_menu_main[] =
1016{
1017 mee_label (""),
1018 mee_label (""),
1019 mee_handler_id("Resume game", MA_MAIN_RESUME_GAME, main_menu_handler),
1020 mee_handler_id("Save State", MA_MAIN_SAVE_STATE, main_menu_handler),
1021 mee_handler_id("Load State", MA_MAIN_LOAD_STATE, main_menu_handler),
1022 mee_handler_id("Reset game", MA_MAIN_RESET_GAME, main_menu_handler),
1023 mee_handler_id("Load CD image", MA_MAIN_LOAD_ROM, main_menu_handler),
1024 mee_handler ("Options", menu_loop_options),
1025 mee_handler ("Controls", menu_loop_keyconfig),
1026 mee_handler_id("Credits", MA_MAIN_CREDITS, main_menu_handler),
1027 mee_handler_id("Exit", MA_MAIN_EXIT, main_menu_handler),
1028 mee_end,
1029};
1030
3c70c47b 1031// ----------------------------
1032
bd6267e6 1033static void menu_leave_emu(void);
1034
69af03a2 1035void menu_loop(void)
1036{
1037 static int sel = 0;
1038
bd6267e6 1039 menu_leave_emu();
69af03a2 1040
1041 me_enable(e_menu_main, MA_MAIN_RESUME_GAME, ready_to_go);
1042 me_enable(e_menu_main, MA_MAIN_SAVE_STATE, ready_to_go);
1043 me_enable(e_menu_main, MA_MAIN_LOAD_STATE, ready_to_go);
1044 me_enable(e_menu_main, MA_MAIN_RESET_GAME, ready_to_go);
1045
4f3639fa 1046// menu_enter(ready_to_go);
69af03a2 1047 in_set_config_int(0, IN_CFG_BLOCKING, 1);
1048
1049 do {
bd6267e6 1050 me_loop(e_menu_main, &sel, NULL);
69af03a2 1051 } while (!ready_to_go);
1052
1053 /* wait until menu, ok, back is released */
1054 while (in_menu_wait_any(50) & (PBTN_MENU|PBTN_MOK|PBTN_MBACK))
1055 ;
1056
1057 in_set_config_int(0, IN_CFG_BLOCKING, 0);
1058
3c70c47b 1059 menu_prepare_emu();
1060}
1061
bbd837c6 1062static void scan_plugins(void)
1063{
1064 char fname[MAXPATHLEN];
1065 struct dirent *ent;
1066 int gpu_i, spu_i;
1067 char *p;
1068 DIR *dir;
1069
1070 gpu_plugins[0] = "builtin_gpu";
1071 spu_plugins[0] = "builtin_spu";
1072 gpu_i = spu_i = 1;
1073
1074 snprintf(fname, sizeof(fname), "%s/", Config.PluginsDir);
1075 dir = opendir(fname);
1076 if (dir == NULL) {
1077 perror("scan_plugins opendir");
1078 return;
1079 }
1080
1081 while (1) {
1082 void *h, *tmp;
1083
1084 errno = 0;
1085 ent = readdir(dir);
1086 if (ent == NULL) {
1087 if (errno != 0)
1088 perror("readdir");
1089 break;
1090 }
1091 p = strstr(ent->d_name, ".so");
1092 if (p == NULL)
1093 continue;
1094
1095 snprintf(fname, sizeof(fname), "%s/%s", Config.PluginsDir, ent->d_name);
1096 h = dlopen(fname, RTLD_LAZY | RTLD_LOCAL);
1097 if (h == NULL) {
1098 fprintf(stderr, "%s\n", dlerror());
1099 continue;
1100 }
1101
1102 // now what do we have here?
1103 tmp = dlsym(h, "GPUinit");
1104 if (tmp) {
1105 dlclose(h);
1106 if (gpu_i < ARRAY_SIZE(gpu_plugins) - 1)
1107 gpu_plugins[gpu_i++] = strdup(ent->d_name);
1108 continue;
1109 }
1110
1111 tmp = dlsym(h, "SPUinit");
1112 if (tmp) {
1113 dlclose(h);
1114 if (spu_i < ARRAY_SIZE(spu_plugins) - 1)
1115 spu_plugins[spu_i++] = strdup(ent->d_name);
1116 continue;
1117 }
1118
1119 fprintf(stderr, "ignoring unidentified plugin: %s\n", fname);
1120 dlclose(h);
1121 }
1122
1123 closedir(dir);
1124}
1125
3c70c47b 1126void menu_init(void)
1127{
4f3639fa 1128 char buff[MAXPATHLEN];
1129
1130 strcpy(last_selected_fname, "/media");
1131
bbd837c6 1132 scan_plugins();
4f3639fa 1133 pnd_menu_init();
1134 menu_init_common();
1135
3c70c47b 1136 menu_set_defconfig();
1137 menu_load_config(0);
3c70c47b 1138 last_psx_w = 320;
1139 last_psx_h = 240;
bd6267e6 1140 last_psx_bpp = 16;
1141
4f3639fa 1142 g_menubg_src_ptr = calloc(g_menuscreen_w * g_menuscreen_h * 2, 1);
1143 if (g_menubg_src_ptr == NULL)
1144 exit(1);
1145 emu_make_path(buff, "skin/background.png", sizeof(buff));
1146 readpng(g_menubg_src_ptr, buff, READPNG_BG, g_menuscreen_w, g_menuscreen_h);
3c70c47b 1147}
1148
bd6267e6 1149void menu_notify_mode_change(int w, int h, int bpp)
3c70c47b 1150{
1151 last_psx_w = w;
1152 last_psx_h = h;
bd6267e6 1153 last_psx_bpp = bpp;
3c70c47b 1154
1155 if (scaling == SCALE_1_1) {
1156 g_layer_x = 800/2 - w/2; g_layer_y = 480/2 - h/2;
1157 g_layer_w = w; g_layer_h = h;
3c70c47b 1158 }
1159}
1160
bd6267e6 1161static void menu_leave_emu(void)
1162{
6d1a1ac2 1163 if (GPU_close != NULL) {
1164 int ret = GPU_close();
1165 if (ret)
1166 fprintf(stderr, "Warning: GPU_close returned %d\n", ret);
1167 }
bd6267e6 1168
4f3639fa 1169 memcpy(g_menubg_ptr, g_menubg_src_ptr, g_menuscreen_w * g_menuscreen_h * 2);
fba06457 1170 if (pl_fbdev_buf != NULL && ready_to_go && last_psx_bpp == 16) {
bd6267e6 1171 int x = max(0, g_menuscreen_w - last_psx_w);
1172 int y = max(0, g_menuscreen_h / 2 - last_psx_h / 2);
1173 int w = min(g_menuscreen_w, last_psx_w);
1174 int h = min(g_menuscreen_h, last_psx_h);
4f3639fa 1175 u16 *d = (u16 *)g_menubg_ptr + g_menuscreen_w * y + x;
bd6267e6 1176 u16 *s = pl_fbdev_buf;
1177
1178 for (; h > 0; h--, d += g_menuscreen_w, s += last_psx_w)
4f3639fa 1179 menu_darken_bg(d, s, w, 0);
bd6267e6 1180 }
fba06457 1181
1182 plat_video_menu_enter(ready_to_go);
bd6267e6 1183}
1184
3c70c47b 1185void menu_prepare_emu(void)
1186{
fba06457 1187 plat_video_menu_leave();
1188
3c70c47b 1189 switch (scaling) {
1190 case SCALE_1_1:
bd6267e6 1191 menu_notify_mode_change(last_psx_w, last_psx_h, last_psx_bpp);
3c70c47b 1192 break;
1193 case SCALE_4_3:
1194 g_layer_x = 80; g_layer_y = 0;
1195 g_layer_w = 640; g_layer_h = 480;
1196 break;
1197 case SCALE_FULLSCREEN:
1198 g_layer_x = 0; g_layer_y = 0;
1199 g_layer_w = 800; g_layer_h = 480;
1200 break;
1201 case SCALE_CUSTOM:
1202 break;
1203 }
3c70c47b 1204 apply_filter(filter);
1205 apply_cpu_clock();
7eda34c1 1206 stop = 0;
bd6267e6 1207
1208 // core doesn't care about Config.Cdda changes,
1209 // so handle them manually here
1210 if (Config.Cdda)
1211 CDR_stop();
6d1a1ac2 1212
fba06457 1213 // HACK to set up the frame limiter if softgpu is not used..
1214 if (gpu_plugsel != 0) {
1215 fFrameRateHz = Config.PsxType ? 50.0f : 59.94f;
1216 dwFrameRateTicks = (100000*100 / (unsigned long)(fFrameRateHz*100));
1217 }
1218
6d1a1ac2 1219 if (GPU_open != NULL) {
1220 extern unsigned long gpuDisp;
1221 int ret = GPU_open(&gpuDisp, "PCSX", NULL);
1222 if (ret)
1223 fprintf(stderr, "Warning: GPU_open returned %d\n", ret);
1224 }
69af03a2 1225}
1226
1227void me_update_msg(const char *msg)
1228{
1229 strncpy(menu_error_msg, msg, sizeof(menu_error_msg));
1230 menu_error_msg[sizeof(menu_error_msg) - 1] = 0;
1231
1232 menu_error_time = plat_get_ticks_ms();
1233 lprintf("msg: %s\n", menu_error_msg);
1234}
1235