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