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