summaryrefslogtreecommitdiffstats
path: root/xbmc/storage/cdioSupport.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'xbmc/storage/cdioSupport.cpp')
-rw-r--r--xbmc/storage/cdioSupport.cpp958
1 files changed, 958 insertions, 0 deletions
diff --git a/xbmc/storage/cdioSupport.cpp b/xbmc/storage/cdioSupport.cpp
new file mode 100644
index 0000000..70e2463
--- /dev/null
+++ b/xbmc/storage/cdioSupport.cpp
@@ -0,0 +1,958 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "cdioSupport.h"
+
+#include "platform/Environment.h"
+#include "utils/log.h"
+
+#include <mutex>
+
+#include <cdio/cd_types.h>
+#include <cdio/cdio.h>
+#include <cdio/logging.h>
+#include <cdio/mmc.h>
+#include <cdio/util.h>
+
+namespace
+{
+/* Helper constexpr to hide the 0 return code for tray closed. */
+constexpr int CDIO_TRAY_STATUS_CLOSED = 0;
+/* Helper constexpr to hide the 1 return code for tray open */
+constexpr int CDIO_TRAY_STATUS_OPEN = 1;
+/* Helper constexpr to hide the -2 driver code for unsupported tray status get operation */
+constexpr int CDIO_TRAY_STATUS_OP_UNSUPPORTED = -2;
+} // namespace
+
+using namespace MEDIA_DETECT;
+
+std::shared_ptr<CLibcdio> CLibcdio::m_pInstance;
+
+/* Some interesting sector numbers stored in the above buffer. */
+#define ISO_SUPERBLOCK_SECTOR 16 /* buffer[0] */
+#define UFS_SUPERBLOCK_SECTOR 4 /* buffer[2] */
+#define BOOT_SECTOR 17 /* buffer[3] */
+#define VCD_INFO_SECTOR 150 /* buffer[4] */
+#define UDF_ANCHOR_SECTOR 256 /* buffer[5] */
+
+
+signature_t CCdIoSupport::sigs[] = {
+ /*buffer[x] off look for description */
+ {0, 1, "CD001\0", "ISO 9660\0"},
+ {0, 1, "CD-I", "CD-I"},
+ {0, 8, "CDTV", "CDTV"},
+ {0, 8, "CD-RTOS", "CD-RTOS"},
+ {0, 9, "CDROM", "HIGH SIERRA"},
+ {0, 16, "CD-BRIDGE", "BRIDGE"},
+ {0, 1024, "CD-XA001", "XA"},
+ {1, 64, "PPPPHHHHOOOOTTTTOOOO____CCCCDDDD", "PHOTO CD"},
+ {1, 0x438, "\x53\xef", "EXT2 FS"},
+ {2, 1372, "\x54\x19\x01\x0", "UFS"},
+ {3, 7, "EL TORITO", "BOOTABLE"},
+ {4, 0, "VIDEO_CD", "VIDEO CD"},
+ {4, 0, "SUPERVCD", "Chaoji VCD"},
+ {0, 1, "BEA01", "UDF"},
+ {}};
+
+#undef DEBUG_CDIO
+
+static void
+cdio_log_handler (cdio_log_level_t level, const char message[])
+{
+#ifdef DEBUG_CDIO
+ switch (level)
+ {
+ case CDIO_LOG_ERROR:
+ CLog::Log(LOGDEBUG, "**ERROR: {}", message);
+ break;
+ case CDIO_LOG_DEBUG:
+ CLog::Log(LOGDEBUG, "--DEBUG: {}", message);
+ break;
+ case CDIO_LOG_WARN:
+ CLog::Log(LOGDEBUG, "++ WARN: {}", message);
+ break;
+ case CDIO_LOG_INFO:
+ CLog::Log(LOGDEBUG, " INFO: {}", message);
+ break;
+ case CDIO_LOG_ASSERT:
+ CLog::Log(LOGDEBUG, "!ASSERT: {}", message);
+ break;
+ default:
+ //cdio_assert_not_reached ();
+ break;
+ }
+#endif
+}
+
+//////////////////////////////////////////////////////////////////////
+// Construction/Destruction
+//////////////////////////////////////////////////////////////////////
+CLibcdio::CLibcdio(): s_defaultDevice(NULL)
+{
+ cdio_log_set_handler( cdio_log_handler );
+}
+
+CLibcdio::~CLibcdio()
+{
+ free(s_defaultDevice);
+ s_defaultDevice = NULL;
+}
+
+void CLibcdio::ReleaseInstance()
+{
+ m_pInstance.reset();
+}
+
+std::shared_ptr<CLibcdio> CLibcdio::GetInstance()
+{
+ if (!m_pInstance)
+ {
+ m_pInstance = std::shared_ptr<CLibcdio>(new CLibcdio());
+ }
+ return m_pInstance;
+}
+
+CdIo_t* CLibcdio::cdio_open(const char *psz_source, driver_id_t driver_id)
+{
+ std::unique_lock<CCriticalSection> lock(*this);
+
+ return( ::cdio_open(psz_source, driver_id) );
+}
+
+CdIo_t* CLibcdio::cdio_open_win32(const char *psz_source)
+{
+ std::unique_lock<CCriticalSection> lock(*this);
+
+ return( ::cdio_open_win32(psz_source) );
+}
+
+void CLibcdio::cdio_destroy(CdIo_t *p_cdio)
+{
+ std::unique_lock<CCriticalSection> lock(*this);
+
+ ::cdio_destroy(p_cdio);
+}
+
+discmode_t CLibcdio::cdio_get_discmode(CdIo_t *p_cdio)
+{
+ std::unique_lock<CCriticalSection> lock(*this);
+
+ return( ::cdio_get_discmode(p_cdio) );
+}
+
+CdioTrayStatus CLibcdio::mmc_get_tray_status(const CdIo_t* p_cdio)
+{
+ std::unique_lock<CCriticalSection> lock(*this);
+
+ int status = ::mmc_get_tray_status(p_cdio);
+ switch (status)
+ {
+ case CDIO_TRAY_STATUS_CLOSED:
+ return CdioTrayStatus::CLOSED;
+ case CDIO_TRAY_STATUS_OPEN:
+ return CdioTrayStatus::OPEN;
+ case CDIO_TRAY_STATUS_OP_UNSUPPORTED:
+ return CdioTrayStatus::UNKNOWN;
+ default:
+ break;
+ }
+ return CdioTrayStatus::DRIVER_ERROR;
+}
+
+driver_return_code_t CLibcdio::cdio_eject_media(CdIo_t** p_cdio)
+{
+ std::unique_lock<CCriticalSection> lock(*this);
+
+ return( ::cdio_eject_media(p_cdio) );
+}
+
+track_t CLibcdio::cdio_get_last_track_num(const CdIo_t *p_cdio)
+{
+ std::unique_lock<CCriticalSection> lock(*this);
+
+ return( ::cdio_get_last_track_num(p_cdio) );
+}
+
+lsn_t CLibcdio::cdio_get_track_lsn(const CdIo_t *p_cdio, track_t i_track)
+{
+ std::unique_lock<CCriticalSection> lock(*this);
+
+ return( ::cdio_get_track_lsn(p_cdio, i_track) );
+}
+
+lsn_t CLibcdio::cdio_get_track_last_lsn(const CdIo_t *p_cdio, track_t i_track)
+{
+ std::unique_lock<CCriticalSection> lock(*this);
+
+ return( ::cdio_get_track_last_lsn(p_cdio, i_track) );
+}
+
+driver_return_code_t CLibcdio::cdio_read_audio_sectors(
+ const CdIo_t *p_cdio, void *p_buf, lsn_t i_lsn, uint32_t i_blocks)
+{
+ std::unique_lock<CCriticalSection> lock(*this);
+
+ return( ::cdio_read_audio_sectors(p_cdio, p_buf, i_lsn, i_blocks) );
+}
+
+driver_return_code_t CLibcdio::cdio_close_tray(const char* psz_source, driver_id_t* driver_id)
+{
+ std::unique_lock<CCriticalSection> lock(*this);
+ return (::cdio_close_tray(psz_source, driver_id));
+}
+
+const char* CLibcdio::cdio_driver_errmsg(driver_return_code_t drc)
+{
+ return (::cdio_driver_errmsg(drc));
+}
+
+char* CLibcdio::GetDeviceFileName()
+{
+ std::unique_lock<CCriticalSection> lock(*this);
+
+ // If We don't have a DVD device initially present (Darwin or a USB DVD drive),
+ // We have to keep checking in case one appears.
+ if (s_defaultDevice && strlen(s_defaultDevice) == 0)
+ {
+ free(s_defaultDevice);
+ s_defaultDevice = NULL;
+ }
+
+ if (s_defaultDevice == NULL)
+ {
+ std::string strEnvDvd = CEnvironment::getenv("KODI_DVD_DEVICE");
+ if (!strEnvDvd.empty())
+ s_defaultDevice = strdup(strEnvDvd.c_str());
+ else
+ {
+ CdIo_t *p_cdio = ::cdio_open(NULL, DRIVER_UNKNOWN);
+ if (p_cdio != NULL)
+ {
+ s_defaultDevice = strdup(::cdio_get_arg(p_cdio, "source"));
+ ::cdio_destroy(p_cdio);
+ }
+ else
+ s_defaultDevice = strdup("");
+ }
+ }
+ return s_defaultDevice;
+}
+
+
+//////////////////////////////////////////////////////////////////////
+// Construction/Destruction
+//////////////////////////////////////////////////////////////////////
+CCdIoSupport::CCdIoSupport()
+: cdio(nullptr)
+{
+ m_cdio = CLibcdio::GetInstance();
+ m_nFirstData = -1; /* # of first data track */
+ m_nNumData = 0; /* # of data tracks */
+ m_nFirstAudio = -1; /* # of first audio track */
+ m_nNumAudio = 0; /* # of audio tracks */
+ m_nIsofsSize = 0; /* size of session */
+ m_nJolietLevel = 0;
+ m_nFs = 0;
+ m_nUDFVerMinor = 0;
+ m_nUDFVerMajor = 0;
+ m_nDataStart = 0;
+ m_nMsOffset = 0;
+ m_nStartTrack = 0;
+}
+
+CCdIoSupport::~CCdIoSupport() = default;
+
+bool CCdIoSupport::EjectTray()
+{
+ return false;
+}
+
+bool CCdIoSupport::CloseTray()
+{
+ return false;
+}
+
+HANDLE CCdIoSupport::OpenCDROM()
+{
+ std::unique_lock<CCriticalSection> lock(*m_cdio);
+
+ char* source_name = m_cdio->GetDeviceFileName();
+ CdIo* cdio = ::cdio_open(source_name, DRIVER_UNKNOWN);
+
+ return reinterpret_cast<HANDLE>(cdio);
+}
+
+HANDLE CCdIoSupport::OpenIMAGE( std::string& strFilename )
+{
+ std::unique_lock<CCriticalSection> lock(*m_cdio);
+
+ CdIo* cdio = ::cdio_open(strFilename.c_str(), DRIVER_UNKNOWN);
+
+ return reinterpret_cast<HANDLE>(cdio);
+}
+
+int CCdIoSupport::ReadSector(HANDLE hDevice, DWORD dwSector, char* lpczBuffer)
+{
+ std::unique_lock<CCriticalSection> lock(*m_cdio);
+
+ CdIo* cdio = (CdIo*) hDevice;
+ if ( cdio == NULL )
+ return -1;
+
+ if ( ::cdio_read_mode1_sector( cdio, lpczBuffer, dwSector, false ) == 0 )
+ return dwSector;
+
+ return -1;
+}
+
+int CCdIoSupport::ReadSectorMode2(HANDLE hDevice, DWORD dwSector, char* lpczBuffer)
+{
+ std::unique_lock<CCriticalSection> lock(*m_cdio);
+
+ CdIo* cdio = (CdIo*) hDevice;
+ if ( cdio == NULL )
+ return -1;
+
+ if ( ::cdio_read_mode2_sector( cdio, lpczBuffer, dwSector, false ) == 0 )
+ return dwSector;
+
+ return -1;
+}
+
+int CCdIoSupport::ReadSectorCDDA(HANDLE hDevice, DWORD dwSector, char* lpczBuffer)
+{
+ std::unique_lock<CCriticalSection> lock(*m_cdio);
+
+ CdIo* cdio = (CdIo*) hDevice;
+ if ( cdio == NULL )
+ return -1;
+
+ if ( ::cdio_read_audio_sector( cdio, lpczBuffer, dwSector ) == 0 )
+ return dwSector;
+
+ return -1;
+}
+
+void CCdIoSupport::CloseCDROM(HANDLE hDevice)
+{
+ std::unique_lock<CCriticalSection> lock(*m_cdio);
+
+ CdIo* cdio = (CdIo*) hDevice;
+
+ if ( cdio == NULL )
+ return ;
+
+ ::cdio_destroy( cdio );
+}
+
+void CCdIoSupport::PrintAnalysis(int fs, int num_audio)
+{
+ switch (fs & FS_MASK)
+ {
+ case FS_UDF:
+ CLog::Log(LOGINFO, "CD-ROM with UDF filesystem");
+ break;
+ case FS_NO_DATA:
+ CLog::Log(LOGINFO, "CD-ROM with audio tracks");
+ break;
+ case FS_ISO_9660:
+ CLog::Log(LOGINFO, "CD-ROM with ISO 9660 filesystem");
+ if (fs & JOLIET)
+ {
+ CLog::Log(LOGINFO, " with joliet extension level {}", m_nJolietLevel);
+ }
+ if (fs & ROCKRIDGE)
+ {
+ CLog::Log(LOGINFO, " and rockridge extensions");
+ }
+ break;
+ case FS_ISO_9660_INTERACTIVE:
+ CLog::Log(LOGINFO, "CD-ROM with CD-RTOS and ISO 9660 filesystem");
+ break;
+ case FS_HIGH_SIERRA:
+ CLog::Log(LOGINFO, "CD-ROM with High Sierra filesystem");
+ break;
+ case FS_INTERACTIVE:
+ CLog::Log(LOGINFO, "CD-Interactive{}", num_audio > 0 ? "/Ready" : "");
+ break;
+ case FS_HFS:
+ CLog::Log(LOGINFO, "CD-ROM with Macintosh HFS");
+ break;
+ case FS_ISO_HFS:
+ CLog::Log(LOGINFO, "CD-ROM with both Macintosh HFS and ISO 9660 filesystem");
+ break;
+ case FS_ISO_UDF:
+ CLog::Log(LOGINFO, "CD-ROM with both UDF and ISO 9660 filesystem");
+ break;
+ case FS_UFS:
+ CLog::Log(LOGINFO, "CD-ROM with Unix UFS");
+ break;
+ case FS_EXT2:
+ CLog::Log(LOGINFO, "CD-ROM with Linux second extended filesystem");
+ break;
+ case FS_3DO:
+ CLog::Log(LOGINFO, "CD-ROM with Panasonic 3DO filesystem");
+ break;
+ case FS_UNKNOWN:
+ CLog::Log(LOGINFO, "CD-ROM with unknown filesystem");
+ break;
+ }
+
+ switch (fs & FS_MASK)
+ {
+ case FS_ISO_9660:
+ case FS_ISO_9660_INTERACTIVE:
+ case FS_ISO_HFS:
+ case FS_ISO_UDF:
+ CLog::Log(LOGINFO, "ISO 9660: {} blocks, label {}", m_nIsofsSize, m_strDiscLabel);
+ break;
+ }
+
+ switch (fs & FS_MASK)
+ {
+ case FS_UDF:
+ case FS_ISO_UDF:
+ CLog::Log(LOGINFO, "UDF: version {:x}.{:02x}", m_nUDFVerMajor, m_nUDFVerMinor);
+ break;
+ }
+
+ if (m_nFirstData == 1 && num_audio > 0)
+ {
+ CLog::Log(LOGINFO, "mixed mode CD ");
+ }
+ if (fs & XA)
+ {
+ CLog::Log(LOGINFO, "XA sectors ");
+ }
+ if (fs & MULTISESSION)
+ {
+ CLog::Log(LOGINFO, "Multisession, offset = {} ", m_nMsOffset);
+ }
+ if (fs & HIDDEN_TRACK)
+ {
+ CLog::Log(LOGINFO, "Hidden Track ");
+ }
+ if (fs & PHOTO_CD)
+ {
+ CLog::Log(LOGINFO, "{}Photo CD ", num_audio > 0 ? " Portfolio " : "");
+ }
+ if (fs & CDTV)
+ {
+ CLog::Log(LOGINFO, "Commodore CDTV ");
+ }
+ if (m_nFirstData > 1)
+ {
+ CLog::Log(LOGINFO, "CD-Plus/Extra ");
+ }
+ if (fs & BOOTABLE)
+ {
+ CLog::Log(LOGINFO, "bootable CD ");
+ }
+ if (fs & VIDEOCDI && num_audio == 0)
+ {
+ CLog::Log(LOGINFO, "Video CD ");
+#if defined(HAVE_VCDINFO) && defined(DEBUG)
+ if (!opts.no_vcd)
+ {
+ printf("\n");
+ print_vcd_info();
+ }
+#endif
+
+ }
+ if (fs & CVD)
+ {
+ CLog::Log(LOGINFO, "Chaoji Video CD");
+ }
+}
+
+int CCdIoSupport::ReadBlock(int superblock, uint32_t offset, uint8_t bufnum, track_t track_num)
+{
+ std::unique_lock<CCriticalSection> lock(*m_cdio);
+
+ unsigned int track_sec_count = ::cdio_get_track_sec_count(cdio, track_num);
+ memset(buffer[bufnum], 0, CDIO_CD_FRAMESIZE);
+
+ if ( track_sec_count < static_cast<unsigned int>(superblock))
+ {
+ ::cdio_debug("reading block %u skipped track %d has only %u sectors\n",
+ superblock, track_num, track_sec_count);
+ return -1;
+ }
+
+ ::cdio_debug("about to read sector %lu\n",
+ (long unsigned int) offset + superblock);
+
+ if (::cdio_get_track_green(cdio, track_num))
+ {
+ if (0 < ::cdio_read_mode2_sector(cdio, buffer[bufnum],
+ offset + superblock, false))
+ return -1;
+ }
+ else
+ {
+ if (0 < ::cdio_read_mode1_sector(cdio, buffer[bufnum],
+ offset + superblock, false))
+ return -1;
+ }
+
+ return 0;
+}
+
+bool CCdIoSupport::IsIt(int num)
+{
+ signature_t *sigp = &sigs[num];
+ int len = strlen(sigp->sig_str);
+
+ //! @todo check that num < largest sig.
+ return 0 == memcmp(&buffer[sigp->buf_num][sigp->offset], sigp->sig_str, len);
+}
+
+int CCdIoSupport::IsHFS(void)
+{
+ return (0 == memcmp(&buffer[1][512], "PM", 2)) ||
+ (0 == memcmp(&buffer[1][512], "TS", 2)) ||
+ (0 == memcmp(&buffer[1][1024], "BD", 2));
+}
+
+int CCdIoSupport::Is3DO(void)
+{
+ return (0 == memcmp(&buffer[1][0], "\x01\x5a\x5a\x5a\x5a\x5a\x01", 7)) &&
+ (0 == memcmp(&buffer[1][40], "CD-ROM", 6));
+}
+
+int CCdIoSupport::IsJoliet(void)
+{
+ return 2 == buffer[3][0] && buffer[3][88] == 0x25 && buffer[3][89] == 0x2f;
+}
+
+int CCdIoSupport::IsUDF(void)
+{
+ return 2 == ((uint16_t)buffer[5][0] | ((uint16_t)buffer[5][1] << 8));
+}
+
+/* ISO 9660 volume space in M2F1_SECTOR_SIZE byte units */
+int CCdIoSupport::GetSize(void)
+{
+ return ((buffer[0][80] & 0xff) |
+ ((buffer[0][81] & 0xff) << 8) |
+ ((buffer[0][82] & 0xff) << 16) |
+ ((buffer[0][83] & 0xff) << 24));
+}
+
+int CCdIoSupport::GetJolietLevel( void )
+{
+ switch (buffer[3][90])
+ {
+ case 0x40:
+ return 1;
+ case 0x43:
+ return 2;
+ case 0x45:
+ return 3;
+ }
+ return 0;
+}
+
+#define is_it_dbg(sig) /*\
+ if (is_it(sig)) printf("%s, ", sigs[sig].description)*/
+
+int CCdIoSupport::GuessFilesystem(int start_session, track_t track_num)
+{
+ std::unique_lock<CCriticalSection> lock(*m_cdio);
+
+ int ret = FS_UNKNOWN;
+ cdio_iso_analysis_t anal;
+ cdio_fs_anal_t fs;
+ bool udf = false;
+
+ memset(&anal, 0, sizeof(anal));
+ discmode_t mode = ::cdio_get_discmode(cdio);
+ if (::cdio_is_discmode_dvd(mode))
+ {
+ m_strDiscLabel = "";
+ m_nIsofsSize = ::cdio_get_disc_last_lsn(cdio);
+ m_nJolietLevel = ::cdio_get_joliet_level(cdio);
+
+ return FS_ISO_9660;
+ }
+
+ fs = ::cdio_guess_cd_type(cdio, start_session, track_num, &anal);
+
+ switch(CDIO_FSTYPE(fs))
+ {
+ case CDIO_FS_AUDIO:
+ ret = FS_NO_DATA;
+ break;
+
+ case CDIO_FS_HIGH_SIERRA:
+ ret = FS_HIGH_SIERRA;
+ break;
+
+ case CDIO_FS_ISO_9660:
+ ret = FS_ISO_9660;
+ break;
+
+ case CDIO_FS_INTERACTIVE:
+ ret = FS_ISO_9660_INTERACTIVE;
+ break;
+
+ case CDIO_FS_HFS:
+ ret = FS_HFS;
+ break;
+
+ case CDIO_FS_UFS:
+ ret = FS_UFS;
+ break;
+
+ case CDIO_FS_EXT2:
+ ret = FS_EXT2;
+ break;
+
+ case CDIO_FS_UDF:
+ ret = FS_UDF;
+ udf = true;
+ break;
+
+ case CDIO_FS_ISO_UDF:
+ ret = FS_ISO_UDF;
+ udf = true;
+ break;
+
+ default:
+ break;
+ }
+
+ if (udf)
+ {
+ m_nUDFVerMinor = anal.UDFVerMinor;
+ m_nUDFVerMajor = anal.UDFVerMajor;
+ }
+
+ m_strDiscLabel = anal.iso_label;
+ m_nIsofsSize = anal.isofs_size;
+ m_nJolietLevel = anal.joliet_level;
+
+ return ret;
+}
+
+void CCdIoSupport::GetCdTextInfo(xbmc_cdtext_t &xcdt, int trackNum)
+{
+ // cdtext disabled for windows as some setup doesn't like mmc commands
+ // and stall for over a minute in cdio_get_cdtext 83
+#if !defined(TARGET_WINDOWS)
+ std::unique_lock<CCriticalSection> lock(*m_cdio);
+
+ // Get the CD-Text , if any
+#if defined(LIBCDIO_VERSION_NUM) && (LIBCDIO_VERSION_NUM >= 84)
+ cdtext_t *pcdtext = static_cast<cdtext_t*>( cdio_get_cdtext(cdio) );
+#else
+ //! @todo - remove after Ubuntu 16.04 (Xenial) is EOL
+ cdtext_t *pcdtext = (cdtext_t *)::cdio_get_cdtext(cdio, trackNum);
+#endif
+
+ if (pcdtext == NULL)
+ return ;
+
+#if defined(LIBCDIO_VERSION_NUM) && (LIBCDIO_VERSION_NUM >= 84)
+ for (int i=0; i < MAX_CDTEXT_FIELDS; i++)
+ if (cdtext_get_const(pcdtext, (cdtext_field_t)i, trackNum))
+ xcdt[(cdtext_field_t)i] = cdtext_field2str((cdtext_field_t)i);
+#else
+ //! @todo - remove after Ubuntu 16.04 (Xenial) is EOL
+ // Same ids used in libcdio and for our structure + the ids are consecutive make this copy loop safe.
+ for (int i = 0; i < MAX_CDTEXT_FIELDS; i++)
+ if (pcdtext->field[i])
+ xcdt[(cdtext_field_t)i] = pcdtext->field[(cdtext_field_t)i];
+#endif
+#endif // TARGET_WINDOWS
+}
+
+CCdInfo* CCdIoSupport::GetCdInfo(char* cDeviceFileName)
+{
+ std::unique_lock<CCriticalSection> lock(*m_cdio);
+
+ char* source_name;
+ if(cDeviceFileName == NULL)
+ source_name = m_cdio->GetDeviceFileName();
+ else
+ source_name = cDeviceFileName;
+
+ cdio = ::cdio_open(source_name, DRIVER_UNKNOWN);
+ if (cdio == NULL)
+ {
+ CLog::Log(LOGERROR, "{}: Error in automatically selecting driver with input", __FUNCTION__);
+ return NULL;
+ }
+
+ bool bIsCDRom = true;
+
+ m_nFirstTrackNum = ::cdio_get_first_track_num(cdio);
+ if (m_nFirstTrackNum == CDIO_INVALID_TRACK)
+ {
+#if !defined(TARGET_DARWIN)
+ ::cdio_destroy(cdio);
+ return NULL;
+#else
+ m_nFirstTrackNum = 1;
+ bIsCDRom = false;
+#endif
+ }
+
+ m_nNumTracks = ::cdio_get_num_tracks(cdio);
+ if (m_nNumTracks == CDIO_INVALID_TRACK)
+ {
+#if !defined(TARGET_DARWIN)
+ ::cdio_destroy(cdio);
+ return NULL;
+#else
+ m_nNumTracks = 1;
+ bIsCDRom = false;
+#endif
+ }
+
+ CCdInfo* info = new CCdInfo;
+ info->SetFirstTrack( m_nFirstTrackNum );
+ info->SetTrackCount( m_nNumTracks );
+
+ for (i = m_nFirstTrackNum; i <= CDIO_CDROM_LEADOUT_TRACK; i++)
+ {
+ msf_t msf;
+ if (bIsCDRom && !::cdio_get_track_msf(cdio, i, &msf))
+ {
+ trackinfo ti;
+ ti.nfsInfo = FS_UNKNOWN;
+ ti.ms_offset = 0;
+ ti.isofs_size = 0;
+ ti.nJolietLevel = 0;
+ ti.nFrames = 0;
+ ti.nMins = 0;
+ ti.nSecs = 0;
+ info->SetTrackInformation( i, ti );
+ CLog::Log(LOGDEBUG, "cdio_track_msf for track {} failed, I give up.", i);
+ delete info;
+ ::cdio_destroy(cdio);
+ return NULL;
+ }
+
+ trackinfo ti;
+ if (bIsCDRom && TRACK_FORMAT_AUDIO == ::cdio_get_track_format(cdio, i))
+ {
+ m_nNumAudio++;
+ ti.nfsInfo = FS_NO_DATA;
+ m_nFs = FS_NO_DATA;
+ int temp1 = ::cdio_get_track_lba(cdio, i) - CDIO_PREGAP_SECTORS;
+ int temp2 = ::cdio_get_track_lba(cdio, i + 1) - CDIO_PREGAP_SECTORS;
+ // The length is the address of the second track minus the address of the first track
+ temp2 -= temp1; // temp2 now has length of track1 in frames
+ ti.nMins = temp2 / (60 * 75); // calculate the number of minutes
+ temp2 %= 60 * 75; // calculate the left-over frames
+ ti.nSecs = temp2 / 75; // calculate the number of seconds
+ if ( -1 == m_nFirstAudio)
+ m_nFirstAudio = i;
+
+ // Make sure that we have the Disc related info available
+ if (i == 1)
+ {
+ xbmc_cdtext_t xcdt;
+ GetCdTextInfo(xcdt, 0);
+ info->SetDiscCDTextInformation( xcdt );
+ }
+
+ // Get this tracks info
+ GetCdTextInfo(ti.cdtext, i);
+ }
+ else
+ {
+ m_nNumData++;
+ if ( -1 == m_nFirstData)
+ m_nFirstData = i;
+ }
+ ti.nfsInfo = FS_NO_DATA;
+ ti.ms_offset = 0;
+ ti.isofs_size = 0;
+ ti.nJolietLevel = 0;
+ ti.nFrames = ::cdio_get_track_lba(cdio, i);
+ ti.nMins = 0;
+ ti.nSecs = 0;
+
+ info->SetTrackInformation( i, ti );
+ /* skip to leadout? */
+ if (i == m_nNumTracks)
+ i = CDIO_CDROM_LEADOUT_TRACK;
+ }
+
+ info->SetCddbDiscId( CddbDiscId() );
+ info->SetDiscLength( ::cdio_get_track_lba(cdio, CDIO_CDROM_LEADOUT_TRACK) / CDIO_CD_FRAMES_PER_SEC );
+
+ info->SetAudioTrackCount( m_nNumAudio );
+ info->SetDataTrackCount( m_nNumData );
+ info->SetFirstAudioTrack( m_nFirstAudio );
+ info->SetFirstDataTrack( m_nFirstData );
+
+ CLog::Log(LOGINFO, "CD Analysis Report");
+ CLog::Log(LOGINFO, STRONG);
+
+ /* Try to find out what sort of CD we have */
+ if (0 == m_nNumData)
+ {
+ /* no data track, may be a "real" audio CD or hidden track CD */
+
+ msf_t msf;
+ ::cdio_get_track_msf(cdio, 1, &msf);
+ m_nStartTrack = ::cdio_msf_to_lsn(&msf);
+
+ /* CD-I/Ready says start_track <= 30*75 then CDDA */
+ if (m_nStartTrack > 100 /* 100 is just a guess */)
+ {
+ m_nFs = GuessFilesystem(0, 1);
+ if ((m_nFs & FS_MASK) != FS_UNKNOWN)
+ m_nFs |= HIDDEN_TRACK;
+ else
+ {
+ m_nFs &= ~FS_MASK; /* del filesystem info */
+ CLog::Log(LOGDEBUG, "Oops: {} unused sectors at start, but hidden track check failed.",
+ m_nStartTrack);
+ }
+ }
+ PrintAnalysis(m_nFs, m_nNumAudio);
+ }
+ else
+ {
+ /* We have data track(s) */
+ for (j = 2, i = m_nFirstData; i <= m_nNumTracks; i++)
+ {
+ msf_t msf;
+ track_format_t track_format = ::cdio_get_track_format(cdio, i);
+
+ ::cdio_get_track_msf(cdio, i, &msf);
+
+ switch ( track_format )
+ {
+ case TRACK_FORMAT_AUDIO:
+ {
+ trackinfo ti;
+ ti.nfsInfo = FS_NO_DATA;
+ m_nFs = FS_NO_DATA;
+ ti.ms_offset = 0;
+ ti.isofs_size = 0;
+ ti.nJolietLevel = 0;
+ ti.nFrames = ::cdio_get_track_lba(cdio, i);
+ ti.nMins = 0;
+ ti.nSecs = 0;
+ info->SetTrackInformation( i + 1, ti );
+ }
+ case TRACK_FORMAT_ERROR:
+ break;
+ case TRACK_FORMAT_CDI:
+ case TRACK_FORMAT_XA:
+ case TRACK_FORMAT_DATA:
+ case TRACK_FORMAT_PSX:
+ break;
+ }
+
+ m_nStartTrack = (i == 1) ? 0 : ::cdio_msf_to_lsn(&msf);
+
+ /* Save the start of the data area */
+ if (i == m_nFirstData)
+ m_nDataStart = m_nStartTrack;
+
+ /* Skip tracks which belong to the current walked session */
+ if (m_nStartTrack < m_nDataStart + m_nIsofsSize)
+ continue;
+
+ m_nFs = GuessFilesystem(m_nStartTrack, i);
+ trackinfo ti;
+ ti.nfsInfo = m_nFs;
+ ti.ms_offset = m_nMsOffset;
+ ti.isofs_size = m_nIsofsSize;
+ ti.nJolietLevel = m_nJolietLevel;
+ ti.nFrames = ::cdio_get_track_lba(cdio, i);
+ ti.nMins = 0;
+ ti.nSecs = 0;
+ info->SetDiscLabel(m_strDiscLabel);
+
+
+ if (i > 1)
+ {
+ /* Track is beyond last session -> new session found */
+ m_nMsOffset = m_nStartTrack;
+
+ CLog::Log(LOGINFO,
+ "Session #{} starts at track {:2}, LSN: {:6},"
+ " ISO 9660 blocks: {:6}",
+ j++, i, m_nStartTrack, m_nIsofsSize);
+
+ CLog::Log(LOGINFO, "ISO 9660: {} blocks, label {}", m_nIsofsSize, m_strDiscLabel);
+ m_nFs |= MULTISESSION;
+ ti.nfsInfo = m_nFs;
+ }
+ else
+ {
+ PrintAnalysis(m_nFs, m_nNumAudio);
+ }
+
+ info->SetTrackInformation( i, ti );
+
+ }
+ }
+ ::cdio_destroy( cdio );
+ return info;
+}
+
+
+// Returns the sum of the decimal digits in a number. Eg. 1955 = 20
+int CCdIoSupport::CddbDecDigitSum(int n)
+{
+ int ret = 0;
+
+ for (;;)
+ {
+ ret += n % 10;
+ n = n / 10;
+ if (!n)
+ return ret;
+ }
+}
+
+// Return the number of seconds (discarding frame portion) of an MSF
+unsigned int CCdIoSupport::MsfSeconds(msf_t *msf)
+{
+ std::unique_lock<CCriticalSection> lock(*m_cdio);
+
+ return ::cdio_from_bcd8(msf->m)*60 + ::cdio_from_bcd8(msf->s);
+}
+
+
+// Compute the CDDB disk ID for an Audio disk.
+// This is a funny checksum consisting of the concatenation of 3 things:
+// The sum of the decimal digits of sizes of all tracks,
+// The total length of the disk, and
+// The number of tracks.
+
+uint32_t CCdIoSupport::CddbDiscId()
+{
+ std::unique_lock<CCriticalSection> lock(*m_cdio);
+
+ int i, t, n = 0;
+ msf_t start_msf;
+ msf_t msf;
+
+ for (i = 1; i <= m_nNumTracks; i++)
+ {
+ ::cdio_get_track_msf(cdio, i, &msf);
+ n += CddbDecDigitSum(MsfSeconds(&msf));
+ }
+
+ ::cdio_get_track_msf(cdio, 1, &start_msf);
+ ::cdio_get_track_msf(cdio, CDIO_CDROM_LEADOUT_TRACK, &msf);
+
+ t = MsfSeconds(&msf) - MsfSeconds(&start_msf);
+
+ return ((n % 0xff) << 24 | t << 8 | m_nNumTracks);
+}