Pickle's port for this emu
[pcsx_rearmed.git] / frontend / menu.c
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>
13 #include <errno.h>
14 #include <dlfcn.h>
15
16 #include "menu.h"
17 #include "config.h"
18 #include "plugin_lib.h"
19 #include "omap.h"
20 #include "common/plat.h"
21 #include "../gui/Linux.h"
22 #include "../libpcsxcore/misc.h"
23 #include "../libpcsxcore/new_dynarec/new_dynarec.h"
24 #include "revision.h"
25
26 #define MENU_X2 1
27 #define array_size(x) (sizeof(x) / sizeof(x[0]))
28
29 typedef 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,
49         MA_OPT_FILTERING,
50 } menu_id;
51
52 enum {
53         SCALE_1_1,
54         SCALE_4_3,
55         SCALE_FULLSCREEN,
56         SCALE_CUSTOM,
57 };
58
59 extern int ready_to_go;
60 static int last_psx_w, last_psx_h, last_psx_bpp;
61 static int scaling, filter, state_slot, cpu_clock;
62 static char rom_fname_reload[MAXPATHLEN];
63 static char last_selected_fname[MAXPATHLEN];
64 int g_opts;
65
66 // from softgpu plugin
67 extern int iUseDither;
68 extern int UseFrameSkip;
69 extern int UseFrameLimit;
70 extern uint32_t dwActFixes;
71 extern float fFrameRateHz;
72 extern int dwFrameRateTicks;
73
74 // sound plugin
75 extern int iUseReverb;
76 extern int iUseInterpolation;
77 extern int iXAPitch;
78 extern int iSPUIRQWait;
79 extern int iUseTimer;
80
81 static const char *gpu_plugins[16];
82 static const char *spu_plugins[16];
83 static int gpu_plugsel, spu_plugsel;
84
85
86 static int min(int x, int y) { return x < y ? x : y; }
87 static int max(int x, int y) { return x > y ? x : y; }
88
89 void 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
101 static int emu_check_save_file(int slot)
102 {
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;
113 }
114
115 static int emu_save_load_game(int load, int sram)
116 {
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;
131 }
132
133 static void draw_savestate_bg(int slot)
134 {
135 }
136
137 static void menu_set_defconfig(void)
138 {
139         scaling = SCALE_4_3;
140
141         Config.Xa = Config.Cdda = Config.Sio =
142         Config.SpuIrq = Config.RCntFix = Config.VSyncWA = 0;
143
144         iUseDither = UseFrameSkip = 0;
145         UseFrameLimit = 1;
146         dwActFixes = 1<<7;
147
148         iUseReverb = 2;
149         iUseInterpolation = 1;
150         iXAPitch = iSPUIRQWait = 0;
151         iUseTimer = 2;
152 }
153
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
166 static 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),
188         CE_INTVAL(g_layer_x),
189         CE_INTVAL(g_layer_y),
190         CE_INTVAL(g_layer_w),
191         CE_INTVAL(g_layer_h),
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),
198         CE_INTVAL(UseFrameLimit),
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
207 static 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)
219                 snprintf(buf, size, "." PCSX_DOT_DIR "cfg/%.32s-%.9s.cfg", trimlabel, CdromId);
220         else
221                 snprintf(buf, size, "." PCSX_DOT_DIR "%s", cfgfile_basename);
222 }
223
224 static int menu_write_config(int is_game)
225 {
226         char cfgfile[MAXPATHLEN];
227         FILE *f;
228         int i;
229
230         make_cfg_fname(cfgfile, sizeof(cfgfile), is_game);
231         f = fopen(cfgfile, "w");
232         if (f == NULL) {
233                 printf("menu_write_config: failed to open: %s\n", cfgfile);
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
266 static 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;
276 }
277
278 static int menu_load_config(int is_game)
279 {
280         char cfgfile[MAXPATHLEN];
281         int i, ret = -1;
282         long size;
283         char *cfg;
284         FILE *f;
285
286         make_cfg_fname(cfgfile, sizeof(cfgfile), is_game);
287         f = fopen(cfgfile, "r");
288         if (f == NULL) {
289                 printf("menu_load_config: failed to open: %s\n", cfgfile);
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
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;
368 fail_read:
369         free(cfg);
370 fail:
371         fclose(f);
372         return ret;
373 }
374
375 // rrrr rggg gggb bbbb
376 static unsigned short fname2color(const char *fname)
377 {
378         static const char *cdimg_exts[] = { ".bin", ".img", ".iso", ".z", ".cue" };
379         static const char *other_exts[] = { ".ccd", ".toc", ".mds", ".sub", ".table" };
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
394 #define MENU_ALIGN_LEFT
395 #define menu_init menu_init_common
396 #include "common/menu.c"
397 #undef menu_init
398
399 // ---------- pandora specific -----------
400
401 static const char pnd_script_base[] = "sudo -n /usr/pandora/scripts";
402 static char **pnd_filter_list;
403
404 static int get_cpu_clock(void)
405 {
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
416 static 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
427 static void apply_filter(int which)
428 {
429         static int old = -1;
430         char buf[128];
431         int i;
432
433         if (pnd_filter_list == NULL || which == old)
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);
445         old = which;
446 }
447
448 static menu_entry e_menu_gfx_options[];
449
450 static 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;
509 }
510
511 // -------------- key config --------------
512
513 me_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 },
520         { "CIRCLE  ", 1 << DKEY_CIRCLE },
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
532 me_bind_action emuctrl_actions[] =
533 {
534 /*
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 },
539 */
540         { "Enter Menu       ", PEV_MENU },
541         { NULL,                0 }
542 };
543
544 static 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
562 static 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
580 static const char *mgn_saveloadcfg(int id, int *offs)
581 {
582         return "";
583 }
584
585 static int mh_savecfg(int id, int keys)
586 {
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");
591
592         return 1;
593 }
594
595 static 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),
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),
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
614 static int menu_loop_keyconfig(int id, int keys)
615 {
616         static int sel = 0;
617
618 //      me_enable(e_menu_keyconfig, MA_OPT_SAVECFG_GAME, ready_to_go);
619         me_loop(e_menu_keyconfig, &sel, NULL);
620         return 0;
621 }
622
623 // ------------ gfx options menu ------------
624
625 static const char *men_scaler[] = { "1x1", "scaled 4:3", "fullscreen", "custom", NULL };
626 static const char h_cscaler[]   = "Displays the scaler layer, you can resize it\n"
627                                   "using d-pad or move it using R+d-pad";
628 static const char *men_dummy[] = { NULL };
629
630 static 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
680 static menu_entry e_menu_gfx_options[] =
681 {
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),
686         mee_end,
687 };
688
689 static 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
698 // ------------ bios/plugins ------------
699
700 static const char *men_gpu_dithering[] = { "None", "Game dependant", "Always", NULL };
701 static const char h_gpu_0[]            = "Needed for Chrono Cross";
702 static const char h_gpu_1[]            = "Capcom fighting games";
703 static const char h_gpu_2[]            = "Black screens in Lunar";
704 static const char h_gpu_3[]            = "Compatibility mode";
705 static const char h_gpu_6[]            = "Pandemonium 2";
706 static const char h_gpu_7[]            = "Skip every second frame";
707 static const char h_gpu_8[]            = "Needed by Dark Forces";
708 static const char h_gpu_9[]            = "better g-colors, worse textures";
709 static const char h_gpu_10[]           = "Toggle busy flags after drawing";
710
711 static 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
726 static 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
733 static const char *men_spu_reverb[] = { "Off", "Fake", "On", NULL };
734 static const char *men_spu_interp[] = { "None", "Simple", "Gaussian", "Cubic", NULL };
735 static const char h_spu_irq_wait[]  = "Wait for CPU; only useful for some games, may cause glitches";
736 static const char h_spu_thread[]    = "Run sound emulation in separate thread";
737
738 static 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
748 static 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
755 static const char h_gpu[]        = "Configure built-in P.E.Op.S. SoftGL Driver V1.17";
756 static const char h_spu[]        = "Configure built-in P.E.Op.S. Sound Driver V1.7";
757 static const char h_plugin_xpu[] = "Must save config and reload the game\n"
758                                    "for plugin change to take effect";
759
760 static menu_entry e_menu_plugin_options[] =
761 {
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),
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
769 static int menu_loop_plugin_options(int id, int keys)
770 {
771         static int sel = 0;
772         me_loop(e_menu_plugin_options, &sel, NULL);
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
778         return 0;
779 }
780
781 // ------------ adv options menu ------------
782
783 static const char h_cfg_cpul[]   = "Shows CPU usage in %%";
784 static const char h_cfg_fl[]     = "Keeps the game from running too fast";
785 static const char h_cfg_xa[]     = "Disables XA sound, which can sometimes improve performance";
786 static const char h_cfg_cdda[]   = "Disable CD Audio for a performance boost\n"
787                                    "(proper .cue/.bin dump is needed otherwise)";
788 static const char h_cfg_sio[]    = "This should be enabled for certain memcards/gamepads";
789 static const char h_cfg_spuirq[] = "Compatibility tweak; should probably be left off";
790 static const char h_cfg_rcnt1[]  = "Parasite Eve 2, Vandal Hearts 1/2 Fix";
791 static const char h_cfg_rcnt2[]  = "InuYasha Sengoku Battle Fix";
792
793 static menu_entry e_menu_adv_options[] =
794 {
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),
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
806 static 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
813 // ------------ options menu ------------
814
815 static int mh_restore_defaults(int id, int keys)
816 {
817         menu_set_defconfig();
818         me_update_msg("defaults restored");
819         return 1;
820 }
821
822 static const char *men_region[]       = { "NTSC", "PAL", NULL };
823 /*
824 static const char *men_confirm_save[] = { "OFF", "writes", "loads", "both", NULL };
825 static const char h_confirm_save[]    = "Ask for confirmation when overwriting save,\n"
826                                         "loading state or both";
827 */
828 static const char h_restore_def[]     = "Switches back to default / recommended\n"
829                                         "configuration";
830
831 static menu_entry e_menu_options[] =
832 {
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),
838         mee_range     ("CPU clock",                MA_OPT_CPU_CLOCKS, cpu_clock, 20, 5000),
839         mee_handler   ("[Display]",                menu_loop_gfx_options),
840         mee_handler   ("[BIOS/Plugins]",           menu_loop_plugin_options),
841         mee_handler   ("[Advanced]",               menu_loop_adv_options),
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),
844         mee_handler_h ("Restore default config",   mh_restore_defaults, h_restore_def),
845         mee_end,
846 };
847
848 static 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);
854         e_menu_options[i].enabled = cpu_clock != 0 ? 1 : 0;
855         me_enable(e_menu_options, MA_OPT_SAVECFG_GAME, ready_to_go);
856
857         me_loop(e_menu_options, &sel, NULL);
858
859         return 0;
860 }
861
862 // ------------ debug menu ------------
863
864 static void draw_frame_debug(void)
865 {
866         smalltext_out16(4, 1, "build: "__DATE__ " " __TIME__ " " REV, 0xe7fc);
867 }
868
869 static 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
888 void OnFile_Exit();
889
890 const char *plat_get_credits(void)
891 {
892         return  "PCSX-ReARMed\n\n"
893                 "(C) 1999-2003 PCSX Team\n"
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"
898                 "ARM recompiler (C) 2009-2010 Ari64\n\n"
899                 "integration, optimization and\n"
900                 "  frontend (C) 2010 notaz\n";
901 }
902
903 static int run_cd_image(const char *fname)
904 {
905         extern void set_cd_image(const char *fname);
906         ready_to_go = 0;
907         pl_fbdev_buf = NULL;
908
909         ClosePlugins();
910         set_cd_image(fname);
911         LoadPlugins();
912         NetOpened = 0;
913         if (OpenPlugins() == -1) {
914                 me_update_msg("failed to open plugins");
915                 return -1;
916         }
917
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");
922                 return -1;
923         }
924
925         SysReset();
926
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");
931                 return -1;
932         }
933
934         ready_to_go = 1;
935         return 0;
936 }
937
938 static int romsel_run(void)
939 {
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;
946
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
969 static int main_menu_handler(int id, int keys)
970 {
971         switch (id)
972         {
973         case MA_MAIN_RESUME_GAME:
974                 if (ready_to_go)
975                         return 1;
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:
986                 if (ready_to_go) {
987                         ClosePlugins();
988                         OpenPlugins();
989                         SysReset();
990                         if (CheckCdrom() != -1) {
991                                 LoadCdrom();
992                         }
993                         return 1;
994                 }
995                 break;
996         case MA_MAIN_LOAD_ROM:
997                 if (romsel_run() == 0)
998                         return 1;
999                 break;
1000         case MA_MAIN_CREDITS:
1001                 draw_menu_credits(draw_frame_debug);
1002                 in_menu_wait(PBTN_MOK|PBTN_MBACK, 70);
1003                 break;
1004         case MA_MAIN_EXIT:
1005                 OnFile_Exit();
1006                 break;
1007         default:
1008                 lprintf("%s: something unknown selected\n", __FUNCTION__);
1009                 break;
1010         }
1011
1012         return 0;
1013 }
1014
1015 static 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
1031 // ----------------------------
1032
1033 static void menu_leave_emu(void);
1034
1035 void menu_loop(void)
1036 {
1037         static int sel = 0;
1038
1039         menu_leave_emu();
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
1046 //      menu_enter(ready_to_go);
1047         in_set_config_int(0, IN_CFG_BLOCKING, 1);
1048
1049         do {
1050                 me_loop(e_menu_main, &sel, NULL);
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
1059         menu_prepare_emu();
1060 }
1061
1062 static 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
1126 void menu_init(void)
1127 {
1128         char buff[MAXPATHLEN];
1129
1130         strcpy(last_selected_fname, "/media");
1131
1132         scan_plugins();
1133         pnd_menu_init();
1134         menu_init_common();
1135
1136         menu_set_defconfig();
1137         menu_load_config(0);
1138         last_psx_w = 320;
1139         last_psx_h = 240;
1140         last_psx_bpp = 16;
1141
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);
1147 }
1148
1149 void menu_notify_mode_change(int w, int h, int bpp)
1150 {
1151         last_psx_w = w;
1152         last_psx_h = h;
1153         last_psx_bpp = bpp;
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;
1158         }
1159 }
1160
1161 static void menu_leave_emu(void)
1162 {
1163         if (GPU_close != NULL) {
1164                 int ret = GPU_close();
1165                 if (ret)
1166                         fprintf(stderr, "Warning: GPU_close returned %d\n", ret);
1167         }
1168
1169         memcpy(g_menubg_ptr, g_menubg_src_ptr, g_menuscreen_w * g_menuscreen_h * 2);
1170         if (pl_fbdev_buf != NULL && ready_to_go && last_psx_bpp == 16) {
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);
1175                 u16 *d = (u16 *)g_menubg_ptr + g_menuscreen_w * y + x;
1176                 u16 *s = pl_fbdev_buf;
1177
1178                 for (; h > 0; h--, d += g_menuscreen_w, s += last_psx_w)
1179                         menu_darken_bg(d, s, w, 0);
1180         }
1181
1182         plat_video_menu_enter(ready_to_go);
1183 }
1184
1185 void menu_prepare_emu(void)
1186 {
1187         plat_video_menu_leave();
1188
1189         switch (scaling) {
1190         case SCALE_1_1:
1191                 menu_notify_mode_change(last_psx_w, last_psx_h, last_psx_bpp);
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         }
1204         apply_filter(filter);
1205         apply_cpu_clock();
1206         stop = 0;
1207
1208         // core doesn't care about Config.Cdda changes,
1209         // so handle them manually here
1210         if (Config.Cdda)
1211                 CDR_stop();
1212
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
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         }
1225 }
1226
1227 void 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