+ const char *fname = NULL;
+
+ if (len < 1)
+ return false;
+
+ if (index >= sizeof(disks) / sizeof(disks[0]))
+ return false;
+
+ fname = disks[index].fname;
+
+ if (!fname || (*fname == '\0'))
+ return false;
+
+ strncpy(path, fname, len - 1);
+ path[len - 1] = '\0';
+
+ return true;
+}
+
+static bool disk_get_image_label(unsigned index, char *label, size_t len)
+{
+ const char *flabel = NULL;
+
+ if (len < 1)
+ return false;
+
+ if (index >= sizeof(disks) / sizeof(disks[0]))
+ return false;
+
+ flabel = disks[index].flabel;
+
+ if (!flabel || (*flabel == '\0'))
+ return false;
+
+ strncpy(label, flabel, len - 1);
+ label[len - 1] = '\0';
+
+ return true;
+}
+
+static struct retro_disk_control_callback disk_control = {
+ .set_eject_state = disk_set_eject_state,
+ .get_eject_state = disk_get_eject_state,
+ .get_image_index = disk_get_image_index,
+ .set_image_index = disk_set_image_index,
+ .get_num_images = disk_get_num_images,
+ .replace_image_index = disk_replace_image_index,
+ .add_image_index = disk_add_image_index,
+};
+
+static struct retro_disk_control_ext_callback disk_control_ext = {
+ .set_eject_state = disk_set_eject_state,
+ .get_eject_state = disk_get_eject_state,
+ .get_image_index = disk_get_image_index,
+ .set_image_index = disk_set_image_index,
+ .get_num_images = disk_get_num_images,
+ .replace_image_index = disk_replace_image_index,
+ .add_image_index = disk_add_image_index,
+ .set_initial_image = disk_set_initial_image,
+ .get_image_path = disk_get_image_path,
+ .get_image_label = disk_get_image_label,
+};
+
+static char base_dir[1024];
+
+static bool read_m3u(const char *file)
+{
+ char line[1024];
+ char name[PATH_MAX];
+ FILE *fp = fopen(file, "r");
+ if (!fp)
+ return false;
+
+ while (fgets(line, sizeof(line), fp) && disk_count < sizeof(disks) / sizeof(disks[0]))
+ {
+ if (line[0] == '#')
+ continue;
+ char *carrige_return = strchr(line, '\r');
+ if (carrige_return)
+ *carrige_return = '\0';
+ char *newline = strchr(line, '\n');
+ if (newline)
+ *newline = '\0';
+
+ if (line[0] != '\0')
+ {
+ char disk_label[PATH_MAX];
+ disk_label[0] = '\0';
+
+ snprintf(name, sizeof(name), "%s%c%s", base_dir, SLASH, line);
+ disks[disk_count].fname = strdup(name);
+
+ get_disk_label(disk_label, name, PATH_MAX);
+ disks[disk_count].flabel = strdup(disk_label);
+
+ disk_count++;
+ }
+ }
+
+ fclose(fp);
+ return (disk_count != 0);
+}
+
+static void extract_directory(char *buf, const char *path, size_t size)
+{
+ char *base;
+ strncpy(buf, path, size - 1);
+ buf[size - 1] = '\0';
+
+ base = strrchr(buf, '/');
+ if (!base)
+ base = strrchr(buf, '\\');
+
+ if (base)
+ *base = '\0';
+ else
+ {
+ buf[0] = '.';
+ buf[1] = '\0';
+ }
+}
+
+// raw cdrom support
+#ifdef HAVE_CDROM
+#include "vfs/vfs_implementation.h"
+#include "vfs/vfs_implementation_cdrom.h"
+#include "libretro-cdrom.h"
+#include "rthreads/rthreads.h"
+#include "retro_timers.h"
+struct cached_buf {
+ unsigned char buf[2352];
+ unsigned int lba;
+};
+static struct {
+ libretro_vfs_implementation_file *h;
+ sthread_t *thread;
+ slock_t *read_lock;
+ slock_t *buf_lock;
+ scond_t *cond;
+ struct cached_buf *buf;
+ unsigned int buf_cnt, thread_exit, do_prefetch;
+ unsigned int total_lba, prefetch_lba;
+ int check_eject_delay;
+} rcdrom;
+
+static void lbacache_do(unsigned int lba)
+{
+ unsigned char m, s, f, buf[2352];
+ unsigned int i = lba % rcdrom.buf_cnt;
+ int ret;
+
+ cdrom_lba_to_msf(lba + 150, &m, &s, &f);
+ slock_lock(rcdrom.read_lock);
+ ret = cdrom_read_sector(rcdrom.h, lba, buf);
+ slock_lock(rcdrom.buf_lock);
+ slock_unlock(rcdrom.read_lock);
+ //printf("%d:%02d:%02d m%d f%d\n", m, s, f, buf[12+3], ((buf[12+4+2] >> 5) & 1) + 1);
+ if (ret) {
+ rcdrom.do_prefetch = 0;
+ slock_unlock(rcdrom.buf_lock);
+ LogErr("prefetch: cdrom_read_sector failed for lba %d\n", lba);
+ return;
+ }
+ rcdrom.check_eject_delay = 100;
+
+ if (lba != rcdrom.buf[i].lba) {
+ memcpy(rcdrom.buf[i].buf, buf, sizeof(rcdrom.buf[i].buf));
+ rcdrom.buf[i].lba = lba;
+ }
+ slock_unlock(rcdrom.buf_lock);
+ retro_sleep(0); // why does the main thread stall without this?
+}
+
+static int lbacache_get(unsigned int lba, void *buf)
+{
+ unsigned int i;
+ int ret = 0;
+
+ i = lba % rcdrom.buf_cnt;
+ slock_lock(rcdrom.buf_lock);
+ if (lba == rcdrom.buf[i].lba) {
+ memcpy(buf, rcdrom.buf[i].buf, 2352);
+ ret = 1;
+ }
+ slock_unlock(rcdrom.buf_lock);
+ return ret;
+}
+
+static void rcdrom_prefetch_thread(void *unused)
+{
+ unsigned int buf_cnt, lba, lba_to;
+
+ slock_lock(rcdrom.buf_lock);
+ while (!rcdrom.thread_exit)
+ {
+#ifdef __GNUC__
+ __asm__ __volatile__("":::"memory"); // barrier
+#endif
+ if (!rcdrom.do_prefetch)
+ scond_wait(rcdrom.cond, rcdrom.buf_lock);
+ if (!rcdrom.do_prefetch || !rcdrom.h || rcdrom.thread_exit)
+ continue;
+
+ buf_cnt = rcdrom.buf_cnt;
+ lba = rcdrom.prefetch_lba;
+ lba_to = lba + buf_cnt;
+ if (lba_to > rcdrom.total_lba)
+ lba_to = rcdrom.total_lba;
+ for (; lba < lba_to; lba++) {
+ if (lba != rcdrom.buf[lba % buf_cnt].lba)
+ break;
+ }
+ if (lba == lba_to) {
+ // caching complete
+ rcdrom.do_prefetch = 0;
+ continue;
+ }
+
+ slock_unlock(rcdrom.buf_lock);
+ lbacache_do(lba);
+ slock_lock(rcdrom.buf_lock);
+ }
+ slock_unlock(rcdrom.buf_lock);
+}
+
+static void rcdrom_stop_thread(void)
+{
+ rcdrom.thread_exit = 1;
+ if (rcdrom.buf_lock) {
+ slock_lock(rcdrom.buf_lock);
+ rcdrom.do_prefetch = 0;
+ if (rcdrom.cond)
+ scond_signal(rcdrom.cond);
+ slock_unlock(rcdrom.buf_lock);
+ }
+ if (rcdrom.thread) {
+ sthread_join(rcdrom.thread);
+ rcdrom.thread = NULL;
+ }
+ if (rcdrom.cond) { scond_free(rcdrom.cond); rcdrom.cond = NULL; }
+ if (rcdrom.buf_lock) { slock_free(rcdrom.buf_lock); rcdrom.buf_lock = NULL; }
+ if (rcdrom.read_lock) { slock_free(rcdrom.read_lock); rcdrom.read_lock = NULL; }
+ free(rcdrom.buf);
+ rcdrom.buf = NULL;
+}
+
+// the thread is optional, if anything fails we can do direct reads
+static void rcdrom_start_thread(void)
+{
+ rcdrom_stop_thread();
+ rcdrom.thread_exit = rcdrom.prefetch_lba = rcdrom.do_prefetch = 0;
+ if (rcdrom.buf_cnt == 0)
+ return;
+ rcdrom.buf = calloc(rcdrom.buf_cnt, sizeof(rcdrom.buf[0]));
+ rcdrom.buf_lock = slock_new();
+ rcdrom.read_lock = slock_new();
+ rcdrom.cond = scond_new();
+ if (rcdrom.buf && rcdrom.buf_lock && rcdrom.read_lock && rcdrom.cond) {
+ rcdrom.thread = sthread_create(rcdrom_prefetch_thread, NULL);
+ rcdrom.buf[0].lba = ~0;
+ }
+ if (!rcdrom.thread) {
+ LogErr("cdrom precache thread init failed.\n");
+ rcdrom_stop_thread();
+ }
+}
+
+static long CALLBACK rcdrom_open(void)
+{
+ const char *name = GetIsoFile();
+ //printf("%s %s\n", __func__, name);
+ rcdrom.h = retro_vfs_file_open_impl(name, RETRO_VFS_FILE_ACCESS_READ,
+ RETRO_VFS_FILE_ACCESS_HINT_NONE);
+ if (rcdrom.h) {
+ int ret = cdrom_set_read_speed_x(rcdrom.h, 4);
+ if (ret) LogErr("CD speed set failed\n");
+ const cdrom_toc_t *toc = retro_vfs_file_get_cdrom_toc();
+ const cdrom_track_t *last = &toc->track[toc->num_tracks - 1];
+ unsigned int lba = cdrom_msf_to_lba(last->min, last->sec, last->frame) - 150;
+ rcdrom.total_lba = lba + last->track_size;
+ //cdrom_get_current_config_random_readable(rcdrom.h);
+ //cdrom_get_current_config_multiread(rcdrom.h);
+ //cdrom_get_current_config_cdread(rcdrom.h);
+ //cdrom_get_current_config_profiles(rcdrom.h);
+ rcdrom_start_thread();
+ return 0;
+ }
+ LogErr("retro_vfs_file_open failed for '%s'\n", name);
+ return -1;
+}
+
+static long CALLBACK rcdrom_close(void)
+{
+ //printf("%s\n", __func__);
+ if (rcdrom.h) {
+ rcdrom_stop_thread();
+ retro_vfs_file_close_impl(rcdrom.h);
+ rcdrom.h = NULL;
+ }
+ return 0;
+}
+
+static long CALLBACK rcdrom_getTN(unsigned char *tn)
+{
+ const cdrom_toc_t *toc = retro_vfs_file_get_cdrom_toc();
+ tn[0] = 1;
+ tn[1] = toc->num_tracks;
+ //printf("%s -> %d %d\n", __func__, tn[0], tn[1]);
+ return 0;
+}
+
+static long CALLBACK rcdrom_getTD(unsigned char track, unsigned char *rt)
+{
+ const cdrom_toc_t *toc = retro_vfs_file_get_cdrom_toc();
+ rt[0] = 0, rt[1] = 2, rt[2] = 0;
+ if (track == 0) {
+ cdrom_lba_to_msf(rcdrom.total_lba + 150, &rt[2], &rt[1], &rt[0]);
+ }
+ else if (track <= toc->num_tracks) {
+ int i = track - 1;
+ rt[2] = toc->track[i].min;
+ rt[1] = toc->track[i].sec;
+ rt[0] = toc->track[i].frame;
+ }
+ //printf("%s %d -> %d:%02d:%02d\n", __func__, track, rt[2], rt[1], rt[0]);
+ return 0;
+}
+
+static long CALLBACK rcdrom_prefetch(unsigned char m, unsigned char s, unsigned char f)
+{
+ unsigned int lba = cdrom_msf_to_lba(m, s, f) - 150;
+ if (rcdrom.cond && rcdrom.h) {
+ rcdrom.prefetch_lba = lba;
+ rcdrom.do_prefetch = 1;
+ scond_signal(rcdrom.cond);
+ }
+ if (rcdrom.buf) {
+ unsigned int c = rcdrom.buf_cnt;
+ if (c)
+ return rcdrom.buf[lba % c].lba == lba;
+ }
+ return 1;
+}
+
+static int rcdrom_read_msf(unsigned char m, unsigned char s, unsigned char f,
+ void *buf, const char *func)
+{
+ unsigned int lba = cdrom_msf_to_lba(m, s, f) - 150;
+ int hit = 0, ret = -1;
+ if (rcdrom.buf_lock)
+ hit = lbacache_get(lba, buf);
+ if (!hit && rcdrom.read_lock) {
+ // maybe still prefetching
+ slock_lock(rcdrom.read_lock);
+ slock_unlock(rcdrom.read_lock);
+ hit = lbacache_get(lba, buf);
+ if (hit)
+ hit = 2;
+ }
+ if (!hit) {
+ slock_t *lock = rcdrom.read_lock;
+ rcdrom.do_prefetch = 0;
+ if (lock)
+ slock_lock(lock);
+ if (rcdrom.h) {
+ ret = cdrom_read_sector(rcdrom.h, lba, buf);
+ if (ret)
+ LogErr("cdrom_read_sector failed for lba %d\n", lba);
+ }
+ if (lock)
+ slock_unlock(lock);
+ }
+ else
+ ret = 0;
+ rcdrom.check_eject_delay = ret ? 0 : 100;
+ //printf("%s %d:%02d:%02d -> %d hit %d\n", func, m, s, f, ret, hit);
+ return ret;
+}
+
+static boolean CALLBACK rcdrom_readTrack(unsigned char *time)
+{
+ unsigned char m = btoi(time[0]), s = btoi(time[1]), f = btoi(time[2]);
+ return !rcdrom_read_msf(m, s, f, ISOgetBuffer() - 12, __func__);
+}
+
+static long CALLBACK rcdrom_readCDDA(unsigned char m, unsigned char s, unsigned char f,
+ unsigned char *buffer)
+{
+ return rcdrom_read_msf(m, s, f, buffer, __func__);
+}
+
+static unsigned char * CALLBACK rcdrom_getBuffer(void)
+{
+ //printf("%s\n", __func__);
+ return ISOgetBuffer();
+}
+
+static unsigned char * CALLBACK rcdrom_getBufferSub(int sector)
+{
+ //printf("%s %d %d\n", __func__, sector, rcdrom_h->cdrom.last_frame_lba);
+ return NULL;
+}
+
+static long CALLBACK rcdrom_getStatus(struct CdrStat *stat)
+{
+ const cdrom_toc_t *toc = retro_vfs_file_get_cdrom_toc();
+ //printf("%s %p\n", __func__, stat);
+ CDR__getStatus(stat);
+ stat->Type = toc->track[0].audio ? 2 : 1;
+ return 0;
+}
+
+static void rcdrom_check_eject(void)
+{
+ bool media_inserted;
+ if (!rcdrom.h || rcdrom.do_prefetch || rcdrom.check_eject_delay-- > 0)
+ return;
+ rcdrom.check_eject_delay = 100;
+ media_inserted = cdrom_is_media_inserted(rcdrom.h); // 1-2ms
+ if (!media_inserted != disk_ejected)
+ disk_set_eject_state(!media_inserted);
+}
+#endif // HAVE_CDROM
+
+#if defined(__QNX__) || defined(_WIN32)
+/* Blackberry QNX doesn't have strcasestr */
+
+/*
+ * Find the first occurrence of find in s, ignore case.
+ */
+char *
+strcasestr(const char *s, const char *find)
+{
+ char c, sc;
+ size_t len;
+
+ if ((c = *find++) != 0)
+ {
+ c = tolower((unsigned char)c);
+ len = strlen(find);
+ do
+ {
+ do
+ {
+ if ((sc = *s++) == 0)
+ return (NULL);
+ } while ((char)tolower((unsigned char)sc) != c);
+ } while (strncasecmp(s, find, len) != 0);
+ s--;
+ }
+ return ((char *)s);
+}
+#endif
+
+static void set_retro_memmap(void)
+{
+#ifndef NDEBUG
+ struct retro_memory_map retromap = { 0 };
+ struct retro_memory_descriptor mmap = {
+ 0, psxM, 0, 0, 0, 0, 0x200000
+ };
+
+ retromap.descriptors = &mmap;
+ retromap.num_descriptors = 1;
+
+ environ_cb(RETRO_ENVIRONMENT_SET_MEMORY_MAPS, &retromap);
+#endif
+}
+
+static void show_notification(const char *msg_str,
+ unsigned duration_ms, unsigned priority)
+{
+ if (msg_interface_version >= 1)
+ {
+ struct retro_message_ext msg = {
+ msg_str,
+ duration_ms,
+ 3,
+ RETRO_LOG_WARN,
+ RETRO_MESSAGE_TARGET_ALL,
+ RETRO_MESSAGE_TYPE_NOTIFICATION,
+ -1
+ };
+ environ_cb(RETRO_ENVIRONMENT_SET_MESSAGE_EXT, &msg);
+ }
+ else
+ {
+ struct retro_message msg = {
+ msg_str,
+ 180
+ };
+ environ_cb(RETRO_ENVIRONMENT_SET_MESSAGE, &msg);
+ }
+}
+
+static void retro_audio_buff_status_cb(
+ bool active, unsigned occupancy, bool underrun_likely)
+{
+ retro_audio_buff_active = active;
+ retro_audio_buff_occupancy = occupancy;
+ retro_audio_buff_underrun = underrun_likely;
+}
+
+static void retro_set_audio_buff_status_cb(void)
+{
+ if (frameskip_type == FRAMESKIP_NONE)
+ {
+ environ_cb(RETRO_ENVIRONMENT_SET_AUDIO_BUFFER_STATUS_CALLBACK, NULL);
+ retro_audio_latency = 0;
+ }
+ else
+ {
+ bool calculate_audio_latency = true;
+
+ if (frameskip_type == FRAMESKIP_FIXED_INTERVAL)
+ environ_cb(RETRO_ENVIRONMENT_SET_AUDIO_BUFFER_STATUS_CALLBACK, NULL);
+ else
+ {
+ struct retro_audio_buffer_status_callback buf_status_cb;
+ buf_status_cb.callback = retro_audio_buff_status_cb;
+ if (!environ_cb(RETRO_ENVIRONMENT_SET_AUDIO_BUFFER_STATUS_CALLBACK,
+ &buf_status_cb))
+ {
+ retro_audio_buff_active = false;
+ retro_audio_buff_occupancy = 0;
+ retro_audio_buff_underrun = false;
+ retro_audio_latency = 0;
+ calculate_audio_latency = false;
+ }
+ }
+
+ if (calculate_audio_latency)
+ {
+ /* Frameskip is enabled - increase frontend
+ * audio latency to minimise potential
+ * buffer underruns */
+ uint32_t frame_time_usec = 1000000.0 / (is_pal_mode ? 50.0 : 60.0);
+
+ /* Set latency to 6x current frame time... */
+ retro_audio_latency = (unsigned)(6 * frame_time_usec / 1000);
+
+ /* ...then round up to nearest multiple of 32 */
+ retro_audio_latency = (retro_audio_latency + 0x1F) & ~0x1F;
+ }
+ }
+
+ update_audio_latency = true;
+ frameskip_counter = 0;
+}
+
+static void update_variables(bool in_flight);
+bool retro_load_game(const struct retro_game_info *info)
+{
+ size_t i;
+ unsigned int cd_index = 0;
+ bool is_m3u = (strcasestr(info->path, ".m3u") != NULL);
+ bool is_exe = (strcasestr(info->path, ".exe") != NULL);
+ int ret;
+
+ struct retro_input_descriptor desc[] = {
+#define JOYP(port) \
+ { port, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_LEFT, "D-Pad Left" }, \
+ { port, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_UP, "D-Pad Up" }, \
+ { port, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_DOWN, "D-Pad Down" }, \
+ { port, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_RIGHT, "D-Pad Right" }, \
+ { port, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_B, "Cross" }, \
+ { port, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_A, "Circle" }, \
+ { port, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_X, "Triangle" }, \
+ { port, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_Y, "Square" }, \
+ { port, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L, "L1" }, \
+ { port, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L2, "L2" }, \
+ { port, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L3, "L3" }, \
+ { port, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R, "R1" }, \
+ { port, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R2, "R2" }, \
+ { port, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R3, "R3" }, \
+ { port, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_SELECT, "Select" }, \
+ { port, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_START, "Start" }, \
+ { port, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_LEFT, RETRO_DEVICE_ID_ANALOG_X, "Left Analog X" }, \
+ { port, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_LEFT, RETRO_DEVICE_ID_ANALOG_Y, "Left Analog Y" }, \
+ { port, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_RIGHT, RETRO_DEVICE_ID_ANALOG_X, "Right Analog X" }, \
+ { port, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_RIGHT, RETRO_DEVICE_ID_ANALOG_Y, "Right Analog Y" }, \
+ { port, RETRO_DEVICE_LIGHTGUN, 0, RETRO_DEVICE_ID_LIGHTGUN_TRIGGER, "Gun Trigger" }, \
+ { port, RETRO_DEVICE_LIGHTGUN, 0, RETRO_DEVICE_ID_LIGHTGUN_RELOAD, "Gun Reload" }, \
+ { port, RETRO_DEVICE_LIGHTGUN, 0, RETRO_DEVICE_ID_LIGHTGUN_AUX_A, "Gun Aux A" }, \
+ { port, RETRO_DEVICE_LIGHTGUN, 0, RETRO_DEVICE_ID_LIGHTGUN_AUX_B, "Gun Aux B" }, \
+ { port, RETRO_DEVICE_LIGHTGUN, 0, RETRO_DEVICE_ID_LIGHTGUN_START, "Gun Start" },
+
+ JOYP(0)
+ JOYP(1)
+ JOYP(2)
+ JOYP(3)
+ JOYP(4)
+ JOYP(5)
+ JOYP(6)
+ JOYP(7)
+
+ { 0 },
+ };
+
+ frame_count = 0;
+
+ environ_cb(RETRO_ENVIRONMENT_SET_INPUT_DESCRIPTORS, desc);
+
+#ifdef FRONTEND_SUPPORTS_RGB565
+ enum retro_pixel_format fmt = RETRO_PIXEL_FORMAT_RGB565;
+ if (environ_cb(RETRO_ENVIRONMENT_SET_PIXEL_FORMAT, &fmt))
+ {
+ SysPrintf("RGB565 supported, using it\n");
+ }
+#endif
+
+ if (info == NULL || info->path == NULL)
+ {
+ LogErr("info->path required\n");
+ return false;
+ }
+
+ update_variables(false);
+
+ if (plugins_opened)
+ {
+ ClosePlugins();
+ plugins_opened = 0;
+ }
+
+ disk_init();
+
+ extract_directory(base_dir, info->path, sizeof(base_dir));
+
+ if (is_m3u)
+ {
+ if (!read_m3u(info->path))
+ {
+ LogErr("failed to read m3u file\n");
+ return false;
+ }
+ }
+ else
+ {
+ char disk_label[PATH_MAX];
+ disk_label[0] = '\0';
+
+ disk_count = 1;
+ disks[0].fname = strdup(info->path);
+
+ get_disk_label(disk_label, info->path, PATH_MAX);
+ disks[0].flabel = strdup(disk_label);
+ }
+
+ /* If this is an M3U file, attempt to set the
+ * initial disk image */
+ if (is_m3u && (disk_initial_index > 0) && (disk_initial_index < disk_count))
+ {
+ const char *fname = disks[disk_initial_index].fname;
+
+ if (fname && (*fname != '\0'))
+ if (strcmp(disk_initial_path, fname) == 0)
+ cd_index = disk_initial_index;
+ }
+
+ set_cd_image(disks[cd_index].fname);
+ disk_current_index = cd_index;
+
+ /* have to reload after set_cd_image for correct cdr plugin */
+ if (LoadPlugins() == -1)
+ {
+ LogErr("failed to load plugins\n");
+ return false;
+ }
+ if (!strncmp(info->path, "cdrom:", 6))
+ {
+#ifdef HAVE_CDROM
+ CDR_open = rcdrom_open;
+ CDR_close = rcdrom_close;
+ CDR_getTN = rcdrom_getTN;
+ CDR_getTD = rcdrom_getTD;
+ CDR_readTrack = rcdrom_readTrack;
+ CDR_getBuffer = rcdrom_getBuffer;
+ CDR_getBufferSub = rcdrom_getBufferSub;
+ CDR_getStatus = rcdrom_getStatus;
+ CDR_readCDDA = rcdrom_readCDDA;
+ CDR_prefetch = rcdrom_prefetch;
+#elif !defined(USE_LIBRETRO_VFS)
+ ReleasePlugins();
+ LogErr("%s\n", "Physical CD-ROM support is not compiled in.");
+ show_notification("Physical CD-ROM support is not compiled in.", 6000, 3);
+ return false;
+#endif
+ }
+
+ plugins_opened = 1;
+ NetOpened = 0;
+
+ if (OpenPlugins() == -1)
+ {
+ LogErr("failed to open plugins\n");
+ return false;
+ }
+
+ /* Handle multi-disk images (i.e. PBP)
+ * > Cannot do this until after OpenPlugins() is
+ * called (since this sets the value of
+ * cdrIsoMultidiskCount) */
+ if (!is_m3u && (cdrIsoMultidiskCount > 1))
+ {
+ disk_count = cdrIsoMultidiskCount < 8 ? cdrIsoMultidiskCount : 8;
+
+ /* Small annoyance: We need to change the label
+ * of disk 0, so have to clear existing entries */
+ if (disks[0].fname != NULL)
+ free(disks[0].fname);
+ disks[0].fname = NULL;
+
+ if (disks[0].flabel != NULL)
+ free(disks[0].flabel);
+ disks[0].flabel = NULL;
+
+ for (i = 0; i < sizeof(disks) / sizeof(disks[0]) && i < cdrIsoMultidiskCount; i++)
+ {
+ char disk_name[PATH_MAX - 16] = { 0 };
+ char disk_label[PATH_MAX] = { 0 };
+
+ disks[i].fname = strdup(info->path);
+
+ get_disk_label(disk_name, info->path, sizeof(disk_name));
+ snprintf(disk_label, sizeof(disk_label), "%s #%u", disk_name, (unsigned)i + 1);
+ disks[i].flabel = strdup(disk_label);
+
+ disks[i].internal_index = i;
+ }
+
+ /* This is not an M3U file, so initial disk
+ * image has not yet been set - attempt to
+ * do so now */
+ if ((disk_initial_index > 0) && (disk_initial_index < disk_count))
+ {
+ const char *fname = disks[disk_initial_index].fname;
+
+ if (fname && (*fname != '\0'))
+ if (strcmp(disk_initial_path, fname) == 0)
+ cd_index = disk_initial_index;
+ }
+
+ if (cd_index > 0)
+ {
+ CdromId[0] = '\0';
+ CdromLabel[0] = '\0';
+
+ cdrIsoMultidiskSelect = disks[cd_index].internal_index;
+ disk_current_index = cd_index;
+ set_cd_image(disks[cd_index].fname);
+
+ if (ReloadCdromPlugin() < 0)
+ {
+ LogErr("failed to reload cdr plugins\n");
+ return false;
+ }
+ if (CDR_open() < 0)
+ {
+ LogErr("failed to open cdr plugin\n");
+ return false;
+ }
+ }
+ }
+
+ /* set ports to use "standard controller" initially */
+ for (i = 0; i < 8; ++i)
+ in_type[i] = PSE_PAD_TYPE_STANDARD;
+
+ plugin_call_rearmed_cbs();
+ /* dfinput_activate(); */
+
+ if (!is_exe && CheckCdrom() == -1)
+ {
+ LogErr("unsupported/invalid CD image: %s\n", info->path);
+ return false;
+ }
+
+ SysReset();
+
+ if (is_exe)
+ ret = Load(info->path);
+ else
+ ret = LoadCdrom();
+ if (ret != 0)
+ {
+ LogErr("could not load %s (%d)\n", is_exe ? "exe" : "CD", ret);
+ return false;
+ }
+ emu_on_new_cd(0);
+
+ set_retro_memmap();
+ retro_set_audio_buff_status_cb();
+
+ if (check_unsatisfied_libcrypt())
+ show_notification("LibCrypt protected game with missing SBI detected", 3000, 3);
+
+ return true;