+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) \