+ menu_prepare_emu();
+}
+
+static int qsort_strcmp(const void *p1, const void *p2)
+{
+ char * const *s1 = (char * const *)p1;
+ char * const *s2 = (char * const *)p2;
+ return strcasecmp(*s1, *s2);
+}
+
+static void scan_bios_plugins(void)
+{
+ char fname[MAXPATHLEN];
+ struct dirent *ent;
+ int bios_i, gpu_i, spu_i, mc_i;
+ char *p;
+ DIR *dir;
+
+ bioses[0] = "HLE";
+ gpu_plugins[0] = "builtin_gpu";
+ spu_plugins[0] = "builtin_spu";
+ memcards[0] = "(none)";
+ bios_i = gpu_i = spu_i = mc_i = 1;
+
+ snprintf(fname, sizeof(fname), "%s/", Config.BiosDir);
+ dir = opendir(fname);
+ if (dir == NULL) {
+ perror("scan_bios_plugins bios opendir");
+ goto do_plugins;
+ }
+
+ while (1) {
+ struct stat st;
+
+ errno = 0;
+ ent = readdir(dir);
+ if (ent == NULL) {
+ if (errno != 0)
+ perror("readdir");
+ break;
+ }
+
+ if (ent->d_type != DT_REG && ent->d_type != DT_LNK)
+ continue;
+
+ snprintf(fname, sizeof(fname), "%s/%s", Config.BiosDir, ent->d_name);
+ if (stat(fname, &st) != 0 || st.st_size != 512*1024) {
+ printf("bad BIOS file: %s\n", ent->d_name);
+ continue;
+ }
+
+ if (bios_i < ARRAY_SIZE(bioses) - 1) {
+ bioses[bios_i++] = strdup(ent->d_name);
+ continue;
+ }
+
+ printf("too many BIOSes, dropping \"%s\"\n", ent->d_name);
+ }
+
+ closedir(dir);
+
+do_plugins:
+ snprintf(fname, sizeof(fname), "%s/", Config.PluginsDir);
+ dir = opendir(fname);
+ if (dir == NULL) {
+ perror("scan_bios_plugins plugins opendir");
+ goto do_memcards;
+ }
+
+ while (1) {
+ void *h, *tmp;
+
+ errno = 0;
+ ent = readdir(dir);
+ if (ent == NULL) {
+ if (errno != 0)
+ perror("readdir");
+ break;
+ }
+ p = strstr(ent->d_name, ".so");
+ if (p == NULL)
+ continue;
+
+ snprintf(fname, sizeof(fname), "%s/%s", Config.PluginsDir, ent->d_name);
+ h = dlopen(fname, RTLD_LAZY | RTLD_LOCAL);
+ if (h == NULL) {
+ fprintf(stderr, "%s\n", dlerror());
+ continue;
+ }
+
+ // now what do we have here?
+ tmp = dlsym(h, "GPUinit");
+ if (tmp) {
+ dlclose(h);
+ if (gpu_i < ARRAY_SIZE(gpu_plugins) - 1)
+ gpu_plugins[gpu_i++] = strdup(ent->d_name);
+ continue;
+ }
+
+ tmp = dlsym(h, "SPUinit");
+ if (tmp) {
+ dlclose(h);
+ if (spu_i < ARRAY_SIZE(spu_plugins) - 1)
+ spu_plugins[spu_i++] = strdup(ent->d_name);
+ continue;
+ }
+
+ fprintf(stderr, "ignoring unidentified plugin: %s\n", fname);
+ dlclose(h);
+ }
+
+ closedir(dir);
+
+do_memcards:
+ dir = opendir("." MEMCARD_DIR);
+ if (dir == NULL) {
+ perror("scan_bios_plugins memcards opendir");
+ return;
+ }
+
+ while (1) {
+ struct stat st;
+
+ errno = 0;
+ ent = readdir(dir);
+ if (ent == NULL) {
+ if (errno != 0)
+ perror("readdir");
+ break;
+ }
+
+ if (ent->d_type != DT_REG && ent->d_type != DT_LNK)
+ continue;
+
+ snprintf(fname, sizeof(fname), "." MEMCARD_DIR "%s", ent->d_name);
+ if (stat(fname, &st) != 0) {
+ printf("bad memcard file: %s\n", ent->d_name);
+ continue;
+ }
+
+ if (mc_i < ARRAY_SIZE(memcards) - 1) {
+ memcards[mc_i++] = strdup(ent->d_name);
+ continue;
+ }
+
+ printf("too many memcards, dropping \"%s\"\n", ent->d_name);
+ }
+
+ if (mc_i > 2)
+ qsort(memcards + 1, mc_i - 1, sizeof(memcards[0]), qsort_strcmp);
+
+ closedir(dir);
+}
+
+void menu_init(void)
+{
+ char buff[MAXPATHLEN];
+
+ strcpy(last_selected_fname, "/media");
+
+ scan_bios_plugins();
+ pnd_menu_init();
+ menu_init_common();
+
+ menu_set_defconfig();
+ menu_load_config(0);
+ last_psx_w = 320;
+ last_psx_h = 240;
+ last_psx_bpp = 16;
+
+ g_menubg_src_ptr = calloc(g_menuscreen_w * g_menuscreen_h * 2, 1);
+ if (g_menubg_src_ptr == NULL)
+ exit(1);
+ emu_make_path(buff, "skin/background.png", sizeof(buff));
+ readpng(g_menubg_src_ptr, buff, READPNG_BG, g_menuscreen_w, g_menuscreen_h);
+
+#ifndef __ARM_ARCH_7A__ /* XXX */
+ me_enable(e_menu_gfx_options, MA_OPT_SCALER, 0);
+ me_enable(e_menu_gfx_options, MA_OPT_FILTERING, 0);
+ me_enable(e_menu_gfx_options, MA_OPT_SCALER_C, 0);
+ me_enable(e_menu_keyconfig, MA_CTRL_NUBS_BTNS, 0);
+#else
+ me_enable(e_menu_gfx_options, MA_OPT_SCALER2, 0);
+ me_enable(e_menu_keyconfig, MA_CTRL_VIBRATION, 0);
+ me_enable(e_menu_keyconfig, MA_CTRL_DEADZONE, 0);
+#endif
+}
+
+void menu_notify_mode_change(int w, int h, int bpp)
+{
+ float mult;
+ int imult;
+
+ last_psx_w = w;
+ last_psx_h = h;
+ last_psx_bpp = bpp;
+
+ switch (scaling) {
+ case SCALE_1_1:
+ g_layer_w = w; g_layer_h = h;
+ break;
+
+ case SCALE_4_3v2:
+ if (h > g_menuscreen_h || (240 < h && h <= 360))
+ goto fractional_4_3;
+
+ // 4:3 that prefers integer scaling
+ imult = g_menuscreen_h / h;
+ g_layer_w = w * imult;
+ g_layer_h = h * imult;
+ mult = (float)g_layer_w / (float)g_layer_h;
+ if (mult < 1.25f || mult > 1.666f)
+ g_layer_w = 4.0f/3.0f * (float)g_layer_h;
+ printf(" -> %dx%d %.1f\n", g_layer_w, g_layer_h, mult);
+ break;
+
+ fractional_4_3:
+ case SCALE_4_3:
+ mult = 240.0f / (float)h * 4.0f / 3.0f;
+ if (h > 256)
+ mult *= 2.0f;
+ g_layer_w = mult * (float)g_menuscreen_h;
+ g_layer_h = g_menuscreen_h;
+ printf(" -> %dx%d %.1f\n", g_layer_w, g_layer_h, mult);
+ break;
+
+ case SCALE_FULLSCREEN:
+ g_layer_w = g_menuscreen_w;
+ g_layer_h = g_menuscreen_h;
+ break;
+
+ default:
+ break;
+ }
+
+ g_layer_x = g_menuscreen_w / 2 - g_layer_w / 2;
+ g_layer_y = g_menuscreen_h / 2 - g_layer_h / 2;
+ if (g_layer_x < 0) g_layer_x = 0;
+ if (g_layer_y < 0) g_layer_y = 0;
+ if (g_layer_w > g_menuscreen_w) g_layer_w = g_menuscreen_w;
+ if (g_layer_h > g_menuscreen_h) g_layer_w = g_menuscreen_h;
+}
+
+static void menu_leave_emu(void)
+{
+ if (GPU_close != NULL) {
+ int ret = GPU_close();
+ if (ret)
+ fprintf(stderr, "Warning: GPU_close returned %d\n", ret);
+ }
+
+ plat_video_menu_enter(ready_to_go);
+
+ memcpy(g_menubg_ptr, g_menubg_src_ptr, g_menuscreen_w * g_menuscreen_h * 2);
+ if (pl_vout_buf != NULL && ready_to_go && last_psx_bpp == 16) {
+ int x = max(0, g_menuscreen_w - last_psx_w);
+ int y = max(0, g_menuscreen_h / 2 - last_psx_h / 2);
+ int w = min(g_menuscreen_w, last_psx_w);
+ int h = min(g_menuscreen_h, last_psx_h);
+ u16 *d = (u16 *)g_menubg_ptr + g_menuscreen_w * y + x;
+ u16 *s = pl_vout_buf;
+
+ for (; h > 0; h--, d += g_menuscreen_w, s += last_psx_w)
+ menu_darken_bg(d, s, w, 0);
+ }
+
+ if (ready_to_go)
+ cpu_clock = plat_cpu_clock_get();
+}
+
+void menu_prepare_emu(void)
+{
+ R3000Acpu *prev_cpu = psxCpu;
+
+ plat_video_menu_leave();
+
+ menu_notify_mode_change(last_psx_w, last_psx_h, last_psx_bpp);
+
+ psxCpu = (Config.Cpu == CPU_INTERPRETER) ? &psxInt : &psxRec;
+ if (psxCpu != prev_cpu)
+ // note that this does not really reset, just clears drc caches
+ psxCpu->Reset();
+
+ // core doesn't care about Config.Cdda changes,
+ // so handle them manually here
+ if (Config.Cdda)
+ CDR_stop();
+
+ menu_sync_config();
+ apply_lcdrate(Config.PsxType);
+ apply_filter(filter);
+ plat_cpu_clock_apply(cpu_clock);
+
+ // push config to GPU plugin
+ plugin_call_rearmed_cbs();
+
+ if (GPU_open != NULL) {
+ int ret = GPU_open(&gpuDisp, "PCSX", NULL);
+ if (ret)
+ fprintf(stderr, "Warning: GPU_open returned %d\n", ret);
+ }
+
+ dfinput_activate();