summaryrefslogtreecommitdiffstats
path: root/xbmc/storage
diff options
context:
space:
mode:
Diffstat (limited to 'xbmc/storage')
-rw-r--r--xbmc/storage/AutorunMediaJob.cpp75
-rw-r--r--xbmc/storage/AutorunMediaJob.h25
-rw-r--r--xbmc/storage/CMakeLists.txt16
-rw-r--r--xbmc/storage/DetectDVDType.cpp446
-rw-r--r--xbmc/storage/DetectDVDType.h88
-rw-r--r--xbmc/storage/IStorageProvider.h95
-rw-r--r--xbmc/storage/MediaManager.cpp821
-rw-r--r--xbmc/storage/MediaManager.h147
-rw-r--r--xbmc/storage/cdioSupport.cpp958
-rw-r--r--xbmc/storage/cdioSupport.h347
-rw-r--r--xbmc/storage/discs/IDiscDriveHandler.h93
11 files changed, 3111 insertions, 0 deletions
diff --git a/xbmc/storage/AutorunMediaJob.cpp b/xbmc/storage/AutorunMediaJob.cpp
new file mode 100644
index 0000000..72e784f
--- /dev/null
+++ b/xbmc/storage/AutorunMediaJob.cpp
@@ -0,0 +1,75 @@
+/*
+ * 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 "AutorunMediaJob.h"
+
+#include "ServiceBroker.h"
+#include "application/ApplicationComponents.h"
+#include "application/ApplicationPowerHandling.h"
+#include "dialogs/GUIDialogSelect.h"
+#include "guilib/GUIComponent.h"
+#include "guilib/GUIWindowManager.h"
+#include "guilib/LocalizeStrings.h"
+#include "interfaces/builtins/Builtins.h"
+#include "utils/StringUtils.h"
+#include "utils/Variant.h"
+
+CAutorunMediaJob::CAutorunMediaJob(const std::string &label, const std::string &path):
+ m_path(path),
+ m_label(label)
+{
+}
+
+bool CAutorunMediaJob::DoWork()
+{
+ CGUIDialogSelect* pDialog= CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIDialogSelect>(WINDOW_DIALOG_SELECT);
+
+ // wake up and turn off the screensaver if it's active
+ auto& components = CServiceBroker::GetAppComponents();
+ const auto appPower = components.GetComponent<CApplicationPowerHandling>();
+ appPower->WakeUpScreenSaverAndDPMS();
+
+ pDialog->Reset();
+ if (!m_label.empty())
+ pDialog->SetHeading(CVariant{m_label});
+ else
+ pDialog->SetHeading(CVariant{g_localizeStrings.Get(21331)});
+
+ pDialog->Add(g_localizeStrings.Get(21332));
+ pDialog->Add(g_localizeStrings.Get(21333));
+ pDialog->Add(g_localizeStrings.Get(21334));
+ pDialog->Add(g_localizeStrings.Get(21335));
+
+ pDialog->Open();
+
+ int selection = pDialog->GetSelectedItem();
+ if (selection >= 0)
+ {
+ std::string strAction =
+ StringUtils::Format("ActivateWindow({}, {})", GetWindowString(selection), m_path);
+ CBuiltins::GetInstance().Execute(strAction);
+ }
+
+ return true;
+}
+
+const char *CAutorunMediaJob::GetWindowString(int selection)
+{
+ switch (selection)
+ {
+ case 0:
+ return "Videos";
+ case 1:
+ return "Music";
+ case 2:
+ return "Pictures";
+ case 3:
+ return "FileManager";
+ default:
+ return "FileManager";
+ }
+}
diff --git a/xbmc/storage/AutorunMediaJob.h b/xbmc/storage/AutorunMediaJob.h
new file mode 100644
index 0000000..52524f1
--- /dev/null
+++ b/xbmc/storage/AutorunMediaJob.h
@@ -0,0 +1,25 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include "utils/Job.h"
+
+#include <string>
+
+class CAutorunMediaJob : public CJob
+{
+public:
+ CAutorunMediaJob(const std::string &label, const std::string &path);
+
+ bool DoWork() override;
+private:
+ const char *GetWindowString(int selection);
+
+ std::string m_path, m_label;
+};
diff --git a/xbmc/storage/CMakeLists.txt b/xbmc/storage/CMakeLists.txt
new file mode 100644
index 0000000..eb282bb
--- /dev/null
+++ b/xbmc/storage/CMakeLists.txt
@@ -0,0 +1,16 @@
+set(SOURCES AutorunMediaJob.cpp
+ MediaManager.cpp)
+
+set(HEADERS AutorunMediaJob.h
+ IStorageProvider.h
+ MediaManager.h)
+
+if(ENABLE_OPTICAL)
+ list(APPEND SOURCES cdioSupport.cpp
+ DetectDVDType.cpp)
+ list(APPEND HEADERS cdioSupport.h
+ DetectDVDType.h
+ discs/IDiscDriveHandler.h)
+endif()
+
+core_add_library(storage)
diff --git a/xbmc/storage/DetectDVDType.cpp b/xbmc/storage/DetectDVDType.cpp
new file mode 100644
index 0000000..2392901
--- /dev/null
+++ b/xbmc/storage/DetectDVDType.cpp
@@ -0,0 +1,446 @@
+/*
+ * 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 "DetectDVDType.h"
+
+#include "GUIUserMessages.h"
+#include "ServiceBroker.h"
+#include "application/ApplicationComponents.h"
+#include "application/ApplicationPlayer.h"
+#include "cdioSupport.h"
+#include "guilib/GUIComponent.h"
+#include "guilib/GUIWindowManager.h"
+#include "guilib/LocalizeStrings.h"
+#include "settings/AdvancedSettings.h"
+#include "settings/SettingsComponent.h"
+#include "storage/MediaManager.h"
+#include "utils/StringUtils.h"
+#include "utils/log.h"
+
+#include <mutex>
+
+using namespace MEDIA_DETECT;
+using namespace std::chrono_literals;
+
+CCriticalSection CDetectDVDMedia::m_muReadingMedia;
+CEvent CDetectDVDMedia::m_evAutorun;
+DriveState CDetectDVDMedia::m_DriveState{DriveState::CLOSED_NO_MEDIA};
+CCdInfo* CDetectDVDMedia::m_pCdInfo = NULL;
+time_t CDetectDVDMedia::m_LastPoll = 0;
+CDetectDVDMedia* CDetectDVDMedia::m_pInstance = NULL;
+std::string CDetectDVDMedia::m_diskLabel = "";
+std::string CDetectDVDMedia::m_diskPath = "";
+UTILS::DISCS::DiscInfo CDetectDVDMedia::m_discInfo;
+
+CDetectDVDMedia::CDetectDVDMedia() : CThread("DetectDVDMedia"),
+ m_cdio(CLibcdio::GetInstance())
+{
+ m_bStop = false;
+ m_pInstance = this;
+}
+
+CDetectDVDMedia::~CDetectDVDMedia() = default;
+
+void CDetectDVDMedia::OnStartup()
+{
+ // SetPriority( ThreadPriority::LOWEST );
+ CLog::Log(LOGDEBUG, "Compiled with libcdio Version 0.{}", LIBCDIO_VERSION_NUM);
+}
+
+void CDetectDVDMedia::Process()
+{
+// for apple - currently disable this check since cdio will return null if no media is loaded
+#if !defined(TARGET_DARWIN)
+ //Before entering loop make sure we actually have a CDrom drive
+ CdIo_t *p_cdio = m_cdio->cdio_open(NULL, DRIVER_DEVICE);
+ if (p_cdio == NULL)
+ return;
+ else
+ m_cdio->cdio_destroy(p_cdio);
+#endif
+
+ const auto& components = CServiceBroker::GetAppComponents();
+ const auto appPlayer = components.GetComponent<CApplicationPlayer>();
+
+ while (!m_bStop)
+ {
+ if (appPlayer->IsPlayingVideo())
+ {
+ CThread::Sleep(10000ms);
+ }
+ else
+ {
+ UpdateDvdrom();
+ m_bStartup = false;
+ CThread::Sleep(2000ms);
+ if (m_bAutorun)
+ {
+ // Media in drive, wait 1.5s more to be sure the device is ready for playback
+ CThread::Sleep(1500ms);
+ m_evAutorun.Set();
+ m_bAutorun = false;
+ }
+ }
+ }
+}
+
+void CDetectDVDMedia::OnExit()
+{
+}
+
+// Gets state of the DVD drive
+void CDetectDVDMedia::UpdateDvdrom()
+{
+ // Signal for WaitMediaReady()
+ // that we are busy detecting the
+ // newly inserted media.
+ {
+ std::unique_lock<CCriticalSection> waitLock(m_muReadingMedia);
+ switch (PollDriveState())
+ {
+ case DriveState::NONE:
+ //! @todo reduce / stop polling for drive updates
+ break;
+
+ case DriveState::OPEN:
+ {
+ // Send Message to GUI that disc been ejected
+ SetNewDVDShareUrl(CServiceBroker::GetMediaManager().TranslateDevicePath(m_diskPath), false,
+ g_localizeStrings.Get(502));
+ CGUIMessage msg(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_REMOVED_MEDIA);
+ CServiceBroker::GetGUI()->GetWindowManager().SendThreadMessage(msg);
+ // Clear all stored info
+ Clear();
+ // Update drive state
+ waitLock.unlock();
+ m_DriveState = DriveState::OPEN;
+ return;
+ }
+ break;
+ case DriveState::NOT_READY:
+ {
+ // Drive is not ready (closing, opening)
+ SetNewDVDShareUrl(CServiceBroker::GetMediaManager().TranslateDevicePath(m_diskPath), false,
+ g_localizeStrings.Get(503));
+ m_DriveState = DriveState::NOT_READY;
+ // DVD-ROM in undefined state
+ // Better delete old CD Information
+ if (m_pCdInfo != nullptr)
+ {
+ delete m_pCdInfo;
+ m_pCdInfo = nullptr;
+ }
+ waitLock.unlock();
+ CGUIMessage msg(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_UPDATE_SOURCES);
+ CServiceBroker::GetGUI()->GetWindowManager().SendThreadMessage(msg);
+ // Do we really need sleep here? This will fix: [ 1530771 ] "Open tray" problem
+ // CThread::Sleep(6000ms);
+ return;
+ }
+ break;
+
+ case DriveState::CLOSED_NO_MEDIA:
+ {
+ // Nothing in there...
+ SetNewDVDShareUrl(CServiceBroker::GetMediaManager().TranslateDevicePath(m_diskPath), false,
+ g_localizeStrings.Get(504));
+ m_DriveState = DriveState::CLOSED_NO_MEDIA;
+ // Send Message to GUI that disc has changed
+ CGUIMessage msg(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_UPDATE_SOURCES);
+ waitLock.unlock();
+ CServiceBroker::GetGUI()->GetWindowManager().SendThreadMessage(msg);
+ return;
+ }
+ break;
+ case DriveState::READY:
+#if !defined(TARGET_DARWIN)
+ return ;
+#endif
+ break;
+ case DriveState::CLOSED_MEDIA_PRESENT:
+ {
+ if (m_DriveState != DriveState::CLOSED_MEDIA_PRESENT)
+ {
+ m_DriveState = DriveState::CLOSED_MEDIA_PRESENT;
+ // Detect ISO9660(mode1/mode2) or CDDA filesystem
+ DetectMediaType();
+ CGUIMessage msg(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_UPDATE_SOURCES);
+ waitLock.unlock();
+ CServiceBroker::GetGUI()->GetWindowManager().SendThreadMessage(msg);
+ // Tell the application object that a new Cd is inserted
+ // So autorun can be started.
+ if (!m_bStartup)
+ m_bAutorun = true;
+ }
+ return;
+ }
+ default:
+ break;
+ }
+
+ // We have finished media detection
+ // Signal for WaitMediaReady()
+ }
+
+
+}
+
+// Generates the drive url, (like iso9660://)
+// from the CCdInfo class
+void CDetectDVDMedia::DetectMediaType()
+{
+ bool bCDDA(false);
+ CLog::Log(LOGINFO, "Detecting DVD-ROM media filesystem...");
+
+ // Probe and store DiscInfo result
+ // even if no valid tracks are detected we might still be able to play the disc via libdvdnav or libbluray
+ // as long as they can correctly detect the disc
+ UTILS::DISCS::DiscInfo discInfo;
+ if (UTILS::DISCS::GetDiscInfo(discInfo,
+ CServiceBroker::GetMediaManager().TranslateDevicePath(m_diskPath)))
+ {
+ m_discInfo = discInfo;
+ }
+
+ std::string strNewUrl;
+ CCdIoSupport cdio;
+
+ // Delete old CD-Information
+ if ( m_pCdInfo != NULL )
+ {
+ delete m_pCdInfo;
+ m_pCdInfo = NULL;
+ }
+
+ // Detect new CD-Information
+ m_pCdInfo = cdio.GetCdInfo();
+ if (m_pCdInfo == NULL)
+ {
+ CLog::Log(LOGERROR, "Detection of DVD-ROM media failed.");
+ return ;
+ }
+ CLog::Log(LOGINFO, "Tracks overall:{}; Audio tracks:{}; Data tracks:{}",
+ m_pCdInfo->GetTrackCount(), m_pCdInfo->GetAudioTrackCount(),
+ m_pCdInfo->GetDataTrackCount());
+
+ // Detect ISO9660(mode1/mode2), CDDA filesystem or UDF
+ if (m_pCdInfo->IsISOHFS(1) || m_pCdInfo->IsIso9660(1) || m_pCdInfo->IsIso9660Interactive(1))
+ {
+ strNewUrl = "iso9660://";
+ }
+ else
+ {
+ if (m_pCdInfo->IsUDF(1))
+ strNewUrl = CServiceBroker::GetMediaManager().TranslateDevicePath(m_diskPath);
+ else if (m_pCdInfo->IsAudio(1))
+ {
+ strNewUrl = "cdda://local/";
+ bCDDA = true;
+ }
+ else
+ strNewUrl = CServiceBroker::GetMediaManager().TranslateDevicePath(m_diskPath);
+ }
+
+ if (m_pCdInfo->IsISOUDF(1))
+ {
+ if (!CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_detectAsUdf)
+ {
+ strNewUrl = "iso9660://";
+ }
+ else
+ {
+ strNewUrl = CServiceBroker::GetMediaManager().TranslateDevicePath(m_diskPath);
+ }
+ }
+
+ CLog::Log(LOGINFO, "Using protocol {}", strNewUrl);
+
+ if (m_pCdInfo->IsValidFs())
+ {
+ if (!m_pCdInfo->IsAudio(1))
+ CLog::Log(LOGINFO, "Disc label: {}", m_pCdInfo->GetDiscLabel());
+ }
+ else
+ {
+ CLog::Log(LOGWARNING, "Filesystem is not supported");
+ }
+
+ std::string strLabel;
+ if (bCDDA)
+ {
+ strLabel = "Audio-CD";
+ }
+ else
+ {
+ strLabel = m_pCdInfo->GetDiscLabel();
+ StringUtils::TrimRight(strLabel);
+ }
+
+ SetNewDVDShareUrl( strNewUrl , bCDDA, strLabel);
+}
+
+void CDetectDVDMedia::SetNewDVDShareUrl( const std::string& strNewUrl, bool bCDDA, const std::string& strDiscLabel )
+{
+ std::string strDescription = "DVD";
+ if (bCDDA) strDescription = "CD";
+
+ if (strDiscLabel != "") strDescription = strDiscLabel;
+
+ // Store it in case others want it
+ m_diskLabel = strDescription;
+ m_diskPath = strNewUrl;
+}
+
+DriveState CDetectDVDMedia::PollDriveState()
+{
+ const std::shared_ptr<IDiscDriveHandler> platformDiscDriveHandler =
+ CServiceBroker::GetMediaManager().GetDiscDriveHandler();
+ if (!platformDiscDriveHandler)
+ {
+ return DriveState::NONE;
+ }
+
+ const std::string discPath = CServiceBroker::GetMediaManager().TranslateDevicePath("");
+ const DriveState driveState = platformDiscDriveHandler->GetDriveState(discPath);
+ switch (driveState)
+ {
+ case DriveState::CLOSED_MEDIA_UNDEFINED:
+ // We only poll for new traystatus when driveState has changed or if the last recorded
+ // tray state is undefined
+ if (driveState == DriveState::CLOSED_MEDIA_UNDEFINED &&
+ (m_LastTrayState == TrayState::UNDEFINED || driveState != m_LastDriveState))
+ {
+ m_TrayState = platformDiscDriveHandler->GetTrayState(discPath);
+ }
+ break;
+ case DriveState::OPEN:
+ m_TrayState = TrayState::OPEN;
+ break;
+ default:
+ m_TrayState = TrayState::UNDEFINED;
+ break;
+ }
+ m_LastDriveState = driveState;
+
+ if (m_TrayState == TrayState::CLOSED_MEDIA_PRESENT)
+ {
+ if (m_LastTrayState != TrayState::CLOSED_MEDIA_PRESENT)
+ {
+ m_LastTrayState = m_TrayState;
+ return DriveState::CLOSED_MEDIA_PRESENT;
+ }
+ else
+ {
+ return DriveState::READY;
+ }
+ }
+ else if (m_TrayState == TrayState::CLOSED_NO_MEDIA)
+ {
+ if ((m_LastTrayState != TrayState::CLOSED_NO_MEDIA) &&
+ (m_LastTrayState != TrayState::CLOSED_MEDIA_PRESENT))
+ {
+ m_LastTrayState = m_TrayState;
+ return DriveState::CLOSED_NO_MEDIA;
+ }
+ else
+ {
+ return DriveState::READY;
+ }
+ }
+ else if (m_TrayState == TrayState::OPEN)
+ {
+ if (m_LastTrayState != TrayState::OPEN)
+ {
+ m_LastTrayState = m_TrayState;
+ return DriveState::OPEN;
+ }
+ else
+ {
+ return DriveState::READY;
+ }
+ }
+ else
+ {
+ m_LastTrayState = m_TrayState;
+ }
+
+#ifdef HAS_DVD_DRIVE
+ return DriveState::NOT_READY;
+#else
+ return DriveState::READY;
+#endif
+}
+
+void CDetectDVDMedia::UpdateState()
+{
+ std::unique_lock<CCriticalSection> waitLock(m_muReadingMedia);
+ m_pInstance->DetectMediaType();
+}
+
+// Static function
+// Wait for drive, to finish media detection.
+void CDetectDVDMedia::WaitMediaReady()
+{
+ std::unique_lock<CCriticalSection> waitLock(m_muReadingMedia);
+}
+
+// Static function
+// Returns status of the DVD Drive
+bool CDetectDVDMedia::DriveReady()
+{
+ return m_DriveState == DriveState::READY;
+}
+
+DriveState CDetectDVDMedia::GetDriveState()
+{
+ return m_DriveState;
+}
+
+// Static function
+// Whether a disc is in drive
+bool CDetectDVDMedia::IsDiscInDrive()
+{
+ return m_DriveState == DriveState::CLOSED_MEDIA_PRESENT;
+}
+
+// Static function
+// Returns a CCdInfo class, which contains
+// Media information of the current inserted CD.
+// Can be NULL
+CCdInfo* CDetectDVDMedia::GetCdInfo()
+{
+ std::unique_lock<CCriticalSection> waitLock(m_muReadingMedia);
+ CCdInfo* pCdInfo = m_pCdInfo;
+ return pCdInfo;
+}
+
+const std::string &CDetectDVDMedia::GetDVDLabel()
+{
+ if (!m_discInfo.empty())
+ {
+ return m_discInfo.name;
+ }
+
+ return m_diskLabel;
+}
+
+const std::string &CDetectDVDMedia::GetDVDPath()
+{
+ return m_diskPath;
+}
+
+
+void CDetectDVDMedia::Clear()
+{
+ if (!m_discInfo.empty())
+ {
+ m_discInfo.clear();
+ }
+ m_diskLabel.clear();
+ m_diskPath.clear();
+}
diff --git a/xbmc/storage/DetectDVDType.h b/xbmc/storage/DetectDVDType.h
new file mode 100644
index 0000000..c54272f
--- /dev/null
+++ b/xbmc/storage/DetectDVDType.h
@@ -0,0 +1,88 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+// CDetectDVDMedia
+// Thread running in the background to detect a CD change and the filesystem
+//
+// by Bobbin007 in 2003
+
+#include "PlatformDefs.h"
+
+#ifdef HAS_DVD_DRIVE
+
+#include "storage/discs/IDiscDriveHandler.h"
+#include "threads/CriticalSection.h"
+#include "threads/Thread.h"
+#include "utils/DiscsUtils.h"
+
+#include <memory>
+#include <string>
+
+namespace MEDIA_DETECT
+{
+class CCdInfo;
+class CLibcdio;
+
+class CDetectDVDMedia : public CThread
+{
+public:
+ CDetectDVDMedia();
+ ~CDetectDVDMedia() override;
+
+ void OnStartup() override;
+ void OnExit() override;
+ void Process() override;
+
+ static void WaitMediaReady();
+ static bool IsDiscInDrive();
+ static bool DriveReady();
+ static DriveState GetDriveState();
+ static CCdInfo* GetCdInfo();
+ static CEvent m_evAutorun;
+
+ static const std::string &GetDVDLabel();
+ static const std::string &GetDVDPath();
+
+ static void UpdateState();
+protected:
+ void UpdateDvdrom();
+ DriveState PollDriveState();
+
+
+ void DetectMediaType();
+ void SetNewDVDShareUrl( const std::string& strNewUrl, bool bCDDA, const std::string& strDiscLabel );
+
+ void Clear();
+
+private:
+ static CCriticalSection m_muReadingMedia;
+
+ static DriveState m_DriveState;
+ static time_t m_LastPoll;
+ static CDetectDVDMedia* m_pInstance;
+
+ static CCdInfo* m_pCdInfo;
+
+ bool m_bStartup = true; // Do not autorun on startup
+ bool m_bAutorun = false;
+ TrayState m_TrayState{TrayState::UNDEFINED};
+ TrayState m_LastTrayState{TrayState::UNDEFINED};
+ DriveState m_LastDriveState{DriveState::NONE};
+
+ static std::string m_diskLabel;
+ static std::string m_diskPath;
+
+ std::shared_ptr<CLibcdio> m_cdio;
+
+ /*! \brief Stores the DiscInfo of the current disk */
+ static UTILS::DISCS::DiscInfo m_discInfo;
+};
+}
+#endif
diff --git a/xbmc/storage/IStorageProvider.h b/xbmc/storage/IStorageProvider.h
new file mode 100644
index 0000000..aa9ecaf
--- /dev/null
+++ b/xbmc/storage/IStorageProvider.h
@@ -0,0 +1,95 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include "MediaSource.h"
+
+#include <memory>
+#include <string>
+#include <vector>
+#ifdef HAS_DVD_DRIVE
+#include "cdioSupport.h"
+#endif
+
+namespace MEDIA_DETECT
+{
+namespace STORAGE
+{
+/*! \brief Abstracts a generic storage device type*/
+enum class Type
+{
+ UNKNOWN, /*!< the storage type is unknown */
+ OPTICAL /*!< an optical device (e.g. DVD or Bluray) */
+};
+
+/*! \brief Abstracts a generic storage device */
+struct StorageDevice
+{
+ /*! Device name/label */
+ std::string label{};
+ /*! Device mountpoint/path */
+ std::string path{};
+ /*! The storage type (e.g. OPTICAL) */
+ STORAGE::Type type{STORAGE::Type::UNKNOWN};
+};
+} // namespace STORAGE
+} // namespace MEDIA_DETECT
+
+class IStorageEventsCallback
+{
+public:
+ virtual ~IStorageEventsCallback() = default;
+
+ /*! \brief Callback executed when a new storage device is added
+ * @param device the storage device
+ */
+ virtual void OnStorageAdded(const MEDIA_DETECT::STORAGE::StorageDevice& device) = 0;
+
+ /*! \brief Callback executed when a new storage device is safely removed
+ * @param device the storage device
+ */
+ virtual void OnStorageSafelyRemoved(const MEDIA_DETECT::STORAGE::StorageDevice& device) = 0;
+
+ /*! \brief Callback executed when a new storage device is unsafely removed
+ * @param device the storage device
+ */
+ virtual void OnStorageUnsafelyRemoved(const MEDIA_DETECT::STORAGE::StorageDevice& device) = 0;
+};
+
+class IStorageProvider
+{
+public:
+ virtual ~IStorageProvider() = default;
+
+ virtual void Initialize() = 0;
+ virtual void Stop() = 0;
+
+ virtual void GetLocalDrives(VECSOURCES &localDrives) = 0;
+ virtual void GetRemovableDrives(VECSOURCES &removableDrives) = 0;
+ virtual std::string GetFirstOpticalDeviceFileName()
+ {
+#ifdef HAS_DVD_DRIVE
+ return std::string(MEDIA_DETECT::CLibcdio::GetInstance()->GetDeviceFileName());
+#else
+ return "";
+#endif
+ }
+
+ virtual bool Eject(const std::string& mountpath) = 0;
+
+ virtual std::vector<std::string> GetDiskUsage() = 0;
+
+ virtual bool PumpDriveChangeEvents(IStorageEventsCallback *callback) = 0;
+
+ /**\brief Called by media manager to create platform storage provider
+ *
+ * This method used to create platform specified storage provider
+ */
+ static std::unique_ptr<IStorageProvider> CreateInstance();
+};
diff --git a/xbmc/storage/MediaManager.cpp b/xbmc/storage/MediaManager.cpp
new file mode 100644
index 0000000..a1efbd9
--- /dev/null
+++ b/xbmc/storage/MediaManager.cpp
@@ -0,0 +1,821 @@
+/*
+ * 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 "MediaManager.h"
+
+#include "FileItem.h"
+#include "ServiceBroker.h"
+#include "URL.h"
+#include "guilib/GUIComponent.h"
+#include "guilib/LocalizeStrings.h"
+#include "utils/URIUtils.h"
+
+#include <mutex>
+#ifdef TARGET_WINDOWS
+#include "platform/win32/WIN32Util.h"
+#include "utils/CharsetConverter.h"
+#endif
+#include "guilib/GUIWindowManager.h"
+#ifdef HAS_DVD_DRIVE
+#ifndef TARGET_WINDOWS
+//! @todo switch all ports to use auto sources
+#include <map>
+#include <utility>
+#include "DetectDVDType.h"
+#endif
+#endif
+#include "Autorun.h"
+#include "AutorunMediaJob.h"
+#include "GUIUserMessages.h"
+#include "addons/VFSEntry.h"
+#include "dialogs/GUIDialogKaiToast.h"
+#include "dialogs/GUIDialogPlayEject.h"
+#include "messaging/helpers/DialogOKHelper.h"
+#include "settings/AdvancedSettings.h"
+#include "settings/MediaSourceSettings.h"
+#include "settings/Settings.h"
+#include "settings/SettingsComponent.h"
+#include "utils/FileUtils.h"
+#include "utils/JobManager.h"
+#include "utils/StringUtils.h"
+#include "utils/XBMCTinyXML.h"
+#include "utils/XMLUtils.h"
+#include "utils/log.h"
+
+#include <string>
+#include <vector>
+
+#ifdef HAS_DVD_DRIVE
+using namespace MEDIA_DETECT;
+#endif
+
+const char MEDIA_SOURCES_XML[] = { "special://profile/mediasources.xml" };
+
+CMediaManager::CMediaManager()
+{
+ m_bhasoptical = false;
+}
+
+void CMediaManager::Stop()
+{
+ if (m_platformStorage)
+ m_platformStorage->Stop();
+
+ m_platformStorage.reset();
+}
+
+void CMediaManager::Initialize()
+{
+ if (!m_platformStorage)
+ {
+ m_platformStorage = IStorageProvider::CreateInstance();
+ }
+#ifdef HAS_DVD_DRIVE
+ m_platformDiscDriveHander = IDiscDriveHandler::CreateInstance();
+ m_strFirstAvailDrive = m_platformStorage->GetFirstOpticalDeviceFileName();
+#endif
+ m_platformStorage->Initialize();
+}
+
+bool CMediaManager::LoadSources()
+{
+ // clear our location list
+ m_locations.clear();
+
+ // load xml file...
+ CXBMCTinyXML xmlDoc;
+ if ( !xmlDoc.LoadFile( MEDIA_SOURCES_XML ) )
+ return false;
+
+ TiXmlElement* pRootElement = xmlDoc.RootElement();
+ if (!pRootElement || StringUtils::CompareNoCase(pRootElement->Value(), "mediasources") != 0)
+ {
+ CLog::Log(LOGERROR, "Error loading {}, Line {} ({})", MEDIA_SOURCES_XML, xmlDoc.ErrorRow(),
+ xmlDoc.ErrorDesc());
+ return false;
+ }
+
+ // load the <network> block
+ TiXmlNode *pNetwork = pRootElement->FirstChild("network");
+ if (pNetwork)
+ {
+ TiXmlElement *pLocation = pNetwork->FirstChildElement("location");
+ while (pLocation)
+ {
+ CNetworkLocation location;
+ pLocation->Attribute("id", &location.id);
+ if (pLocation->FirstChild())
+ {
+ location.path = pLocation->FirstChild()->Value();
+ m_locations.push_back(location);
+ }
+ pLocation = pLocation->NextSiblingElement("location");
+ }
+ }
+ LoadAddonSources();
+ return true;
+}
+
+bool CMediaManager::SaveSources()
+{
+ CXBMCTinyXML xmlDoc;
+ TiXmlElement xmlRootElement("mediasources");
+ TiXmlNode *pRoot = xmlDoc.InsertEndChild(xmlRootElement);
+ if (!pRoot) return false;
+
+ TiXmlElement networkNode("network");
+ TiXmlNode *pNetworkNode = pRoot->InsertEndChild(networkNode);
+ if (pNetworkNode)
+ {
+ for (std::vector<CNetworkLocation>::iterator it = m_locations.begin(); it != m_locations.end(); ++it)
+ {
+ TiXmlElement locationNode("location");
+ locationNode.SetAttribute("id", (*it).id);
+ TiXmlText value((*it).path);
+ locationNode.InsertEndChild(value);
+ pNetworkNode->InsertEndChild(locationNode);
+ }
+ }
+ return xmlDoc.SaveFile(MEDIA_SOURCES_XML);
+}
+
+void CMediaManager::GetLocalDrives(VECSOURCES &localDrives, bool includeQ)
+{
+ std::unique_lock<CCriticalSection> lock(m_CritSecStorageProvider);
+ m_platformStorage->GetLocalDrives(localDrives);
+}
+
+void CMediaManager::GetRemovableDrives(VECSOURCES &removableDrives)
+{
+ std::unique_lock<CCriticalSection> lock(m_CritSecStorageProvider);
+ if (m_platformStorage)
+ m_platformStorage->GetRemovableDrives(removableDrives);
+}
+
+void CMediaManager::GetNetworkLocations(VECSOURCES &locations, bool autolocations)
+{
+ for (unsigned int i = 0; i < m_locations.size(); i++)
+ {
+ CMediaSource share;
+ share.strPath = m_locations[i].path;
+ CURL url(share.strPath);
+ share.strName = url.GetWithoutUserDetails();
+ locations.push_back(share);
+ }
+ if (autolocations)
+ {
+ CMediaSource share;
+ share.m_ignore = true;
+#ifdef HAS_FILESYSTEM_SMB
+ share.strPath = "smb://";
+ share.strName = g_localizeStrings.Get(20171);
+ locations.push_back(share);
+#endif
+
+#ifdef HAS_FILESYSTEM_NFS
+ share.strPath = "nfs://";
+ share.strName = g_localizeStrings.Get(20259);
+ locations.push_back(share);
+#endif// HAS_FILESYSTEM_NFS
+
+#ifdef HAS_UPNP
+ if (CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_SERVICES_UPNP))
+ {
+ const std::string& strDevices = g_localizeStrings.Get(33040); //"% Devices"
+ share.strPath = "upnp://";
+ share.strName = StringUtils::Format(strDevices, "UPnP"); //"UPnP Devices"
+ locations.push_back(share);
+ }
+#endif
+
+#ifdef HAS_ZEROCONF
+ share.strPath = "zeroconf://";
+ share.strName = g_localizeStrings.Get(20262);
+ locations.push_back(share);
+#endif
+
+ if (CServiceBroker::IsAddonInterfaceUp())
+ {
+ for (const auto& addon : CServiceBroker::GetVFSAddonCache().GetAddonInstances())
+ {
+ const auto& info = addon->GetProtocolInfo();
+ if (!info.type.empty() && info.supportBrowsing)
+ {
+ share.strPath = info.type + "://";
+ share.strName = g_localizeStrings.GetAddonString(addon->ID(), info.label);
+ if (share.strName.empty())
+ share.strName = g_localizeStrings.Get(info.label);
+ locations.push_back(share);
+ }
+ }
+ }
+ }
+}
+
+bool CMediaManager::AddNetworkLocation(const std::string &path)
+{
+ CNetworkLocation location;
+ location.path = path;
+ location.id = (int)m_locations.size();
+ m_locations.push_back(location);
+ return SaveSources();
+}
+
+bool CMediaManager::HasLocation(const std::string& path) const
+{
+ for (unsigned int i=0;i<m_locations.size();++i)
+ {
+ if (URIUtils::CompareWithoutSlashAtEnd(m_locations[i].path, path))
+ return true;
+ }
+
+ return false;
+}
+
+
+bool CMediaManager::RemoveLocation(const std::string& path)
+{
+ for (unsigned int i=0;i<m_locations.size();++i)
+ {
+ if (URIUtils::CompareWithoutSlashAtEnd(m_locations[i].path, path))
+ {
+ // prompt for sources, remove, cancel,
+ m_locations.erase(m_locations.begin()+i);
+ return SaveSources();
+ }
+ }
+
+ return false;
+}
+
+bool CMediaManager::SetLocationPath(const std::string& oldPath, const std::string& newPath)
+{
+ for (unsigned int i=0;i<m_locations.size();++i)
+ {
+ if (URIUtils::CompareWithoutSlashAtEnd(m_locations[i].path, oldPath))
+ {
+ m_locations[i].path = newPath;
+ return SaveSources();
+ }
+ }
+
+ return false;
+}
+
+void CMediaManager::LoadAddonSources() const
+{
+ if (CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_bVirtualShares)
+ {
+ CMediaSourceSettings::GetInstance().AddShare("video", GetRootAddonTypeSource("video"));
+ CMediaSourceSettings::GetInstance().AddShare("programs", GetRootAddonTypeSource("programs"));
+ CMediaSourceSettings::GetInstance().AddShare("pictures", GetRootAddonTypeSource("pictures"));
+ CMediaSourceSettings::GetInstance().AddShare("music", GetRootAddonTypeSource("music"));
+ CMediaSourceSettings::GetInstance().AddShare("games", GetRootAddonTypeSource("games"));
+ }
+}
+
+CMediaSource CMediaManager::GetRootAddonTypeSource(const std::string& type) const
+{
+ if (type == "programs" || type == "myprograms")
+ {
+ return ComputeRootAddonTypeSource("executable", g_localizeStrings.Get(1043),
+ "DefaultAddonProgram.png");
+ }
+ else if (type == "video" || type == "videos")
+ {
+ return ComputeRootAddonTypeSource("video", g_localizeStrings.Get(1037),
+ "DefaultAddonVideo.png");
+ }
+ else if (type == "music")
+ {
+ return ComputeRootAddonTypeSource("audio", g_localizeStrings.Get(1038),
+ "DefaultAddonMusic.png");
+ }
+ else if (type == "pictures")
+ {
+ return ComputeRootAddonTypeSource("image", g_localizeStrings.Get(1039),
+ "DefaultAddonPicture.png");
+ }
+ else if (type == "games")
+ {
+ return ComputeRootAddonTypeSource("game", g_localizeStrings.Get(35049), "DefaultAddonGame.png");
+ }
+ else
+ {
+ CLog::LogF(LOGERROR, "Invalid type {} provided", type);
+ return {};
+ }
+}
+
+CMediaSource CMediaManager::ComputeRootAddonTypeSource(const std::string& type,
+ const std::string& label,
+ const std::string& thumb) const
+{
+ CMediaSource source;
+ source.strPath = "addons://sources/" + type + "/";
+ source.strName = label;
+ source.m_strThumbnailImage = thumb;
+ source.m_iDriveType = CMediaSource::SOURCE_TYPE_VPATH;
+ source.m_ignore = true;
+ return source;
+}
+
+void CMediaManager::AddAutoSource(const CMediaSource &share, bool bAutorun)
+{
+ CMediaSourceSettings::GetInstance().AddShare("files", share);
+ CMediaSourceSettings::GetInstance().AddShare("video", share);
+ CMediaSourceSettings::GetInstance().AddShare("pictures", share);
+ CMediaSourceSettings::GetInstance().AddShare("music", share);
+ CMediaSourceSettings::GetInstance().AddShare("programs", share);
+ CGUIMessage msg(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_UPDATE_SOURCES);
+ CGUIComponent *gui = CServiceBroker::GetGUI();
+ if (gui)
+ gui->GetWindowManager().SendThreadMessage( msg );
+
+#ifdef HAS_DVD_DRIVE
+ if(bAutorun)
+ MEDIA_DETECT::CAutorun::ExecuteAutorun(share.strPath);
+#endif
+}
+
+void CMediaManager::RemoveAutoSource(const CMediaSource &share)
+{
+ CMediaSourceSettings::GetInstance().DeleteSource("files", share.strName, share.strPath, true);
+ CMediaSourceSettings::GetInstance().DeleteSource("video", share.strName, share.strPath, true);
+ CMediaSourceSettings::GetInstance().DeleteSource("pictures", share.strName, share.strPath, true);
+ CMediaSourceSettings::GetInstance().DeleteSource("music", share.strName, share.strPath, true);
+ CMediaSourceSettings::GetInstance().DeleteSource("programs", share.strName, share.strPath, true);
+ CGUIMessage msg(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_UPDATE_SOURCES);
+ CServiceBroker::GetGUI()->GetWindowManager().SendThreadMessage( msg );
+
+#ifdef HAS_DVD_DRIVE
+ // delete cached CdInfo if any
+ RemoveCdInfo(TranslateDevicePath(share.strPath, true));
+ RemoveDiscInfo(TranslateDevicePath(share.strPath, true));
+#endif
+}
+
+/////////////////////////////////////////////////////////////
+// AutoSource status functions:
+//! @todo translate cdda://<device>/
+
+std::string CMediaManager::TranslateDevicePath(const std::string& devicePath, bool bReturnAsDevice)
+{
+ std::unique_lock<CCriticalSection> waitLock(m_muAutoSource);
+ std::string strDevice = devicePath;
+ // fallback for cdda://local/ and empty devicePath
+#ifdef HAS_DVD_DRIVE
+ if(devicePath.empty() || StringUtils::StartsWith(devicePath, "cdda://local"))
+ strDevice = m_strFirstAvailDrive;
+#endif
+
+#ifdef TARGET_WINDOWS
+ if(!m_bhasoptical)
+ return "";
+
+ if(bReturnAsDevice == false)
+ StringUtils::Replace(strDevice, "\\\\.\\","");
+ else if(!strDevice.empty() && strDevice[1]==':')
+ strDevice = StringUtils::Format("\\\\.\\{}:", strDevice[0]);
+
+ URIUtils::RemoveSlashAtEnd(strDevice);
+#endif
+ return strDevice;
+}
+
+bool CMediaManager::IsDiscInDrive(const std::string& devicePath)
+{
+#ifdef HAS_DVD_DRIVE
+#ifdef TARGET_WINDOWS
+ if(!m_bhasoptical)
+ return false;
+
+ std::string strDevice = TranslateDevicePath(devicePath, false);
+ std::map<std::string,CCdInfo*>::iterator it;
+ std::unique_lock<CCriticalSection> waitLock(m_muAutoSource);
+ it = m_mapCdInfo.find(strDevice);
+ if(it != m_mapCdInfo.end())
+ return true;
+ else
+ return false;
+#else
+ if(URIUtils::IsDVD(devicePath) || devicePath.empty())
+ return MEDIA_DETECT::CDetectDVDMedia::IsDiscInDrive(); //! @todo switch all ports to use auto sources
+ else
+ return true; // Assume other paths to be mounted already
+#endif
+#else
+ return false;
+#endif
+}
+
+bool CMediaManager::IsAudio(const std::string& devicePath)
+{
+#ifdef HAS_DVD_DRIVE
+#ifdef TARGET_WINDOWS
+ if(!m_bhasoptical)
+ return false;
+
+ CCdInfo* pCdInfo = GetCdInfo(devicePath);
+ if(pCdInfo != NULL && pCdInfo->IsAudio(1))
+ return true;
+
+ return false;
+#else
+ //! @todo switch all ports to use auto sources
+ MEDIA_DETECT::CCdInfo* pInfo = MEDIA_DETECT::CDetectDVDMedia::GetCdInfo();
+ if (pInfo != NULL && pInfo->IsAudio(1))
+ return true;
+#endif
+#endif
+ return false;
+}
+
+bool CMediaManager::HasOpticalDrive()
+{
+#ifdef HAS_DVD_DRIVE
+ if (!m_strFirstAvailDrive.empty())
+ return true;
+#endif
+ return false;
+}
+
+DriveState CMediaManager::GetDriveStatus(const std::string& devicePath)
+{
+#ifdef HAS_DVD_DRIVE
+#ifdef TARGET_WINDOWS
+ if (!m_bhasoptical || !m_platformDiscDriveHander)
+ return DriveState::NOT_READY;
+
+ std::string translatedDevicePath = TranslateDevicePath(devicePath, true);
+ return m_platformDiscDriveHander->GetDriveState(translatedDevicePath);
+#else
+ return MEDIA_DETECT::CDetectDVDMedia::GetDriveState();
+#endif
+#else
+ return DriveState::NOT_READY;
+#endif
+}
+
+#ifdef HAS_DVD_DRIVE
+CCdInfo* CMediaManager::GetCdInfo(const std::string& devicePath)
+{
+#ifdef TARGET_WINDOWS
+ if(!m_bhasoptical)
+ return NULL;
+
+ std::string strDevice = TranslateDevicePath(devicePath, false);
+ std::map<std::string,CCdInfo*>::iterator it;
+ {
+ std::unique_lock<CCriticalSection> waitLock(m_muAutoSource);
+ it = m_mapCdInfo.find(strDevice);
+ if(it != m_mapCdInfo.end())
+ return it->second;
+ }
+
+ CCdInfo* pCdInfo=NULL;
+ CCdIoSupport cdio;
+ pCdInfo = cdio.GetCdInfo((char*)strDevice.c_str());
+ if(pCdInfo!=NULL)
+ {
+ std::unique_lock<CCriticalSection> waitLock(m_muAutoSource);
+ m_mapCdInfo.insert(std::pair<std::string,CCdInfo*>(strDevice,pCdInfo));
+ }
+
+ return pCdInfo;
+#else
+ return MEDIA_DETECT::CDetectDVDMedia::GetCdInfo();
+#endif
+}
+
+bool CMediaManager::RemoveCdInfo(const std::string& devicePath)
+{
+ if(!m_bhasoptical)
+ return false;
+
+ std::string strDevice = TranslateDevicePath(devicePath, false);
+
+ std::map<std::string,CCdInfo*>::iterator it;
+ std::unique_lock<CCriticalSection> waitLock(m_muAutoSource);
+ it = m_mapCdInfo.find(strDevice);
+ if(it != m_mapCdInfo.end())
+ {
+ if(it->second != NULL)
+ delete it->second;
+
+ m_mapCdInfo.erase(it);
+ return true;
+ }
+ return false;
+}
+
+std::string CMediaManager::GetDiskLabel(const std::string& devicePath)
+{
+#ifdef TARGET_WINDOWS_STORE
+ return ""; // GetVolumeInformationW nut support in UWP app
+#elif defined(TARGET_WINDOWS)
+ if(!m_bhasoptical)
+ return "";
+
+ std::string mediaPath = CServiceBroker::GetMediaManager().TranslateDevicePath(devicePath);
+
+ auto cached = m_mapDiscInfo.find(mediaPath);
+ if (cached != m_mapDiscInfo.end())
+ return cached->second.name;
+
+ // try to minimize the chance of a "device not ready" dialog
+ std::string drivePath = CServiceBroker::GetMediaManager().TranslateDevicePath(devicePath, true);
+ if (CServiceBroker::GetMediaManager().GetDriveStatus(drivePath) !=
+ DriveState::CLOSED_MEDIA_PRESENT)
+ return "";
+
+ UTILS::DISCS::DiscInfo info;
+ info = GetDiscInfo(mediaPath);
+ if (!info.name.empty())
+ {
+ m_mapDiscInfo[mediaPath] = info;
+ return info.name;
+ }
+
+ std::string strDevice = TranslateDevicePath(devicePath);
+ WCHAR cVolumenName[128];
+ WCHAR cFSName[128];
+ URIUtils::AddSlashAtEnd(strDevice);
+ std::wstring strDeviceW;
+ g_charsetConverter.utf8ToW(strDevice, strDeviceW);
+ if(GetVolumeInformationW(strDeviceW.c_str(), cVolumenName, 127, NULL, NULL, NULL, cFSName, 127)==0)
+ return "";
+ g_charsetConverter.wToUTF8(cVolumenName, strDevice);
+ info.name = StringUtils::TrimRight(strDevice, " ");
+ if (!info.name.empty())
+ m_mapDiscInfo[mediaPath] = info;
+
+ return info.name;
+#else
+ return MEDIA_DETECT::CDetectDVDMedia::GetDVDLabel();
+#endif
+}
+
+std::string CMediaManager::GetDiskUniqueId(const std::string& devicePath)
+{
+ std::string mediaPath;
+
+ CCdInfo* pInfo = CServiceBroker::GetMediaManager().GetCdInfo(devicePath);
+ if (pInfo == NULL)
+ return "";
+
+ if (pInfo->IsAudio(1))
+ mediaPath = "cdda://local/";
+
+ if (mediaPath.empty() && (pInfo->IsISOUDF(1) || pInfo->IsISOHFS(1) || pInfo->IsIso9660(1) || pInfo->IsIso9660Interactive(1)))
+ mediaPath = "iso9660://";
+
+ if (mediaPath.empty())
+ mediaPath = devicePath;
+
+#ifdef TARGET_WINDOWS
+ if (mediaPath.empty() || mediaPath == "iso9660://")
+ {
+ mediaPath = CServiceBroker::GetMediaManager().TranslateDevicePath(devicePath);
+ }
+#endif
+
+ UTILS::DISCS::DiscInfo info = GetDiscInfo(mediaPath);
+ if (info.empty())
+ {
+ CLog::Log(LOGDEBUG, "GetDiskUniqueId: Retrieving ID for path {} failed, ID is empty.",
+ CURL::GetRedacted(mediaPath));
+ return "";
+ }
+
+ std::string strID = StringUtils::Format("removable://{}_{}", info.name, info.serial);
+ CLog::Log(LOGDEBUG, "GetDiskUniqueId: Got ID {} for disc with path {}", strID,
+ CURL::GetRedacted(mediaPath));
+
+ return strID;
+}
+
+std::string CMediaManager::GetDiscPath()
+{
+#ifdef TARGET_WINDOWS
+ return CServiceBroker::GetMediaManager().TranslateDevicePath("");
+#else
+
+ std::unique_lock<CCriticalSection> lock(m_CritSecStorageProvider);
+ VECSOURCES drives;
+ m_platformStorage->GetRemovableDrives(drives);
+ for(unsigned i = 0; i < drives.size(); ++i)
+ {
+ if(drives[i].m_iDriveType == CMediaSource::SOURCE_TYPE_DVD && !drives[i].strPath.empty())
+ return drives[i].strPath;
+ }
+
+ // iso9660://, cdda://local/ or D:\ depending on disc type
+ return MEDIA_DETECT::CDetectDVDMedia::GetDVDPath();
+#endif
+}
+
+std::shared_ptr<IDiscDriveHandler> CMediaManager::GetDiscDriveHandler()
+{
+ return m_platformDiscDriveHander;
+}
+#endif
+
+void CMediaManager::SetHasOpticalDrive(bool bstatus)
+{
+ std::unique_lock<CCriticalSection> waitLock(m_muAutoSource);
+ m_bhasoptical = bstatus;
+}
+
+bool CMediaManager::Eject(const std::string& mountpath)
+{
+ std::unique_lock<CCriticalSection> lock(m_CritSecStorageProvider);
+ return m_platformStorage->Eject(mountpath);
+}
+
+void CMediaManager::EjectTray( const bool bEject, const char cDriveLetter )
+{
+#ifdef HAS_DVD_DRIVE
+ if (m_platformDiscDriveHander)
+ {
+ m_platformDiscDriveHander->EjectDriveTray(TranslateDevicePath(""));
+ }
+#endif
+}
+
+void CMediaManager::CloseTray(const char cDriveLetter)
+{
+#ifdef HAS_DVD_DRIVE
+ if (m_platformDiscDriveHander)
+ {
+ m_platformDiscDriveHander->ToggleDriveTray(TranslateDevicePath(""));
+ }
+#endif
+}
+
+void CMediaManager::ToggleTray(const char cDriveLetter)
+{
+#ifdef HAS_DVD_DRIVE
+ if (m_platformDiscDriveHander)
+ {
+ m_platformDiscDriveHander->ToggleDriveTray(TranslateDevicePath(""));
+ }
+#endif
+}
+
+void CMediaManager::ProcessEvents()
+{
+ std::unique_lock<CCriticalSection> lock(m_CritSecStorageProvider);
+ if (m_platformStorage->PumpDriveChangeEvents(this))
+ {
+#if defined(HAS_DVD_DRIVE) && defined(TARGET_DARWIN_OSX)
+ // darwins GetFirstOpticalDeviceFileName only gives us something
+ // when a disc is inserted
+ // so we have to refresh m_strFirstAvailDrive when this happens after Initialize
+ // was called (e.x. the disc was inserted after the start of xbmc)
+ // else TranslateDevicePath wouldn't give the correct device
+ m_strFirstAvailDrive = m_platformStorage->GetFirstOpticalDeviceFileName();
+#endif
+
+ CGUIMessage msg(GUI_MSG_NOTIFY_ALL,0,0,GUI_MSG_UPDATE_SOURCES);
+ CServiceBroker::GetGUI()->GetWindowManager().SendThreadMessage(msg);
+ }
+}
+
+std::vector<std::string> CMediaManager::GetDiskUsage()
+{
+ std::unique_lock<CCriticalSection> lock(m_CritSecStorageProvider);
+ return m_platformStorage->GetDiskUsage();
+}
+
+void CMediaManager::OnStorageAdded(const MEDIA_DETECT::STORAGE::StorageDevice& device)
+{
+#ifdef HAS_DVD_DRIVE
+ const std::shared_ptr<CSettings> settings = CServiceBroker::GetSettingsComponent()->GetSettings();
+ if (settings->GetInt(CSettings::SETTING_AUDIOCDS_AUTOACTION) != AUTOCD_NONE || settings->GetBool(CSettings::SETTING_DVDS_AUTORUN))
+ {
+ if (settings->GetInt(CSettings::SETTING_AUDIOCDS_AUTOACTION) == AUTOCD_RIP)
+ {
+ CServiceBroker::GetJobManager()->AddJob(new CAutorunMediaJob(device.label, device.path), this,
+ CJob::PRIORITY_LOW);
+ }
+ else
+ {
+ if (device.type == MEDIA_DETECT::STORAGE::Type::OPTICAL)
+ {
+ if (MEDIA_DETECT::CAutorun::ExecuteAutorun(device.path))
+ {
+ return;
+ }
+ CLog::Log(LOGDEBUG, "{}: Could not execute autorun for optical disc with path {}",
+ __FUNCTION__, device.path);
+ }
+ CServiceBroker::GetJobManager()->AddJob(new CAutorunMediaJob(device.label, device.path), this,
+ CJob::PRIORITY_HIGH);
+ }
+ }
+ else
+ {
+ CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Info, g_localizeStrings.Get(13021),
+ device.label, TOAST_DISPLAY_TIME, false);
+ }
+#endif
+}
+
+void CMediaManager::OnStorageSafelyRemoved(const MEDIA_DETECT::STORAGE::StorageDevice& device)
+{
+ CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Info, g_localizeStrings.Get(13023),
+ device.label, TOAST_DISPLAY_TIME, false);
+}
+
+void CMediaManager::OnStorageUnsafelyRemoved(const MEDIA_DETECT::STORAGE::StorageDevice& device)
+{
+ CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Warning, g_localizeStrings.Get(13022),
+ device.label);
+}
+
+UTILS::DISCS::DiscInfo CMediaManager::GetDiscInfo(const std::string& mediaPath)
+{
+ UTILS::DISCS::DiscInfo info;
+
+ if (mediaPath.empty())
+ return info;
+
+ // Try finding VIDEO_TS/VIDEO_TS.IFO - this indicates a DVD disc is inserted
+ std::string pathVideoTS = URIUtils::AddFileToFolder(mediaPath, "VIDEO_TS", "VIDEO_TS.IFO");
+ // correct the filename if needed
+ if (StringUtils::StartsWith(mediaPath, "dvd://") ||
+ StringUtils::StartsWith(mediaPath, "iso9660://"))
+ {
+ pathVideoTS = TranslateDevicePath("");
+ }
+
+ // check for DVD discs
+ if (CFileUtils::Exists(pathVideoTS))
+ {
+ info = UTILS::DISCS::ProbeDVDDiscInfo(pathVideoTS);
+ if (!info.empty())
+ return info;
+ }
+ // check for Blu-ray discs
+ if (CFileUtils::Exists(URIUtils::AddFileToFolder(mediaPath, "BDMV", "index.bdmv")))
+ {
+ info = UTILS::DISCS::ProbeBlurayDiscInfo(mediaPath);
+ }
+
+ return info;
+}
+
+void CMediaManager::RemoveDiscInfo(const std::string& devicePath)
+{
+ std::string strDevice = TranslateDevicePath(devicePath, false);
+
+ auto it = m_mapDiscInfo.find(strDevice);
+ if (it != m_mapDiscInfo.end())
+ m_mapDiscInfo.erase(it);
+}
+
+bool CMediaManager::playStubFile(const CFileItem& item)
+{
+ // Figure out Lines 1 and 2 of the dialog
+ std::string strLine1, strLine2;
+
+ // use generic message by default
+ strLine1 = g_localizeStrings.Get(435).c_str();
+ strLine2 = g_localizeStrings.Get(436).c_str();
+
+ CXBMCTinyXML discStubXML;
+ if (discStubXML.LoadFile(item.GetPath()))
+ {
+ TiXmlElement* pRootElement = discStubXML.RootElement();
+ if (!pRootElement || StringUtils::CompareNoCase(pRootElement->Value(), "discstub") != 0)
+ CLog::Log(LOGINFO, "No <discstub> node found for {}. Using default info dialog message",
+ item.GetPath());
+ else
+ {
+ XMLUtils::GetString(pRootElement, "title", strLine1);
+ XMLUtils::GetString(pRootElement, "message", strLine2);
+ // no title? use the label of the CFileItem as line 1
+ if (strLine1.empty())
+ strLine1 = item.GetLabel();
+ }
+ }
+
+ if (HasOpticalDrive())
+ {
+#ifdef HAS_DVD_DRIVE
+ if (CGUIDialogPlayEject::ShowAndGetInput(strLine1, strLine2))
+ return MEDIA_DETECT::CAutorun::PlayDiscAskResume();
+#endif
+ }
+ else
+ {
+ KODI::MESSAGING::HELPERS::ShowOKDialogText(strLine1, strLine2);
+ }
+ return true;
+}
diff --git a/xbmc/storage/MediaManager.h b/xbmc/storage/MediaManager.h
new file mode 100644
index 0000000..af76bb6
--- /dev/null
+++ b/xbmc/storage/MediaManager.h
@@ -0,0 +1,147 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include "IStorageProvider.h"
+#include "MediaSource.h" // for VECSOURCES
+#include "storage/discs/IDiscDriveHandler.h"
+#include "threads/CriticalSection.h"
+#include "utils/DiscsUtils.h"
+#include "utils/Job.h"
+
+#include <map>
+#include <memory>
+#include <vector>
+
+#include "PlatformDefs.h"
+
+class CFileItem;
+
+class CNetworkLocation
+{
+public:
+ CNetworkLocation() { id = 0; }
+ int id;
+ std::string path;
+};
+
+class CMediaManager : public IStorageEventsCallback, public IJobCallback
+{
+public:
+ CMediaManager();
+
+ void Initialize();
+ void Stop();
+
+ bool LoadSources();
+ bool SaveSources();
+
+ void GetLocalDrives(VECSOURCES &localDrives, bool includeQ = true);
+ void GetRemovableDrives(VECSOURCES &removableDrives);
+ void GetNetworkLocations(VECSOURCES &locations, bool autolocations = true);
+
+ bool AddNetworkLocation(const std::string &path);
+ bool HasLocation(const std::string& path) const;
+ bool RemoveLocation(const std::string& path);
+ bool SetLocationPath(const std::string& oldPath, const std::string& newPath);
+
+ void AddAutoSource(const CMediaSource &share, bool bAutorun=false);
+ void RemoveAutoSource(const CMediaSource &share);
+ bool IsDiscInDrive(const std::string& devicePath="");
+ bool IsAudio(const std::string& devicePath="");
+ bool HasOpticalDrive();
+ std::string TranslateDevicePath(const std::string& devicePath, bool bReturnAsDevice=false);
+ DriveState GetDriveStatus(const std::string& devicePath = "");
+#ifdef HAS_DVD_DRIVE
+ MEDIA_DETECT::CCdInfo* GetCdInfo(const std::string& devicePath="");
+ bool RemoveCdInfo(const std::string& devicePath="");
+ std::string GetDiskLabel(const std::string& devicePath="");
+ std::string GetDiskUniqueId(const std::string& devicePath="");
+
+ /*! \brief Gets the platform disc drive handler
+ * @todo this likely doesn't belong here but in some discsupport component owned by media manager
+ * let's keep it here for now
+ * \return The platform disc drive handler
+ */
+ std::shared_ptr<IDiscDriveHandler> GetDiscDriveHandler();
+#endif
+ std::string GetDiscPath();
+ void SetHasOpticalDrive(bool bstatus);
+
+ bool Eject(const std::string& mountpath);
+ void EjectTray( const bool bEject=true, const char cDriveLetter='\0' );
+ void CloseTray(const char cDriveLetter='\0');
+ void ToggleTray(const char cDriveLetter='\0');
+
+ void ProcessEvents();
+
+ std::vector<std::string> GetDiskUsage();
+
+ /*! \brief Callback executed when a new storage device is added
+ * \sa IStorageEventsCallback
+ * @param device the storage device
+ */
+ void OnStorageAdded(const MEDIA_DETECT::STORAGE::StorageDevice& device) override;
+
+ /*! \brief Callback executed when a new storage device is safely removed
+ * \sa IStorageEventsCallback
+ * @param device the storage device
+ */
+ void OnStorageSafelyRemoved(const MEDIA_DETECT::STORAGE::StorageDevice& device) override;
+
+ /*! \brief Callback executed when a new storage device is unsafely removed
+ * \sa IStorageEventsCallback
+ * @param device the storage device
+ */
+ void OnStorageUnsafelyRemoved(const MEDIA_DETECT::STORAGE::StorageDevice& device) override;
+
+ void OnJobComplete(unsigned int jobID, bool success, CJob *job) override { }
+
+ bool playStubFile(const CFileItem& item);
+
+protected:
+ std::vector<CNetworkLocation> m_locations;
+
+ CCriticalSection m_muAutoSource, m_CritSecStorageProvider;
+#ifdef HAS_DVD_DRIVE
+ std::map<std::string,MEDIA_DETECT::CCdInfo*> m_mapCdInfo;
+#endif
+ bool m_bhasoptical;
+ std::string m_strFirstAvailDrive;
+
+private:
+ /*! \brief Loads the addon sources for the different supported browsable addon types
+ */
+ void LoadAddonSources() const;
+
+ /*! \brief Get the addons root source for the given content type
+ \param type the type of addon content desired
+ \return the given CMediaSource for the addon root directory
+ */
+ CMediaSource GetRootAddonTypeSource(const std::string& type) const;
+
+ /*! \brief Generate the addons source for the given content type
+ \param type the type of addon content desired
+ \param label the name of the addons source
+ \param thumb image to use as the icon
+ \return the given CMediaSource for the addon root directory
+ */
+ CMediaSource ComputeRootAddonTypeSource(const std::string& type,
+ const std::string& label,
+ const std::string& thumb) const;
+
+ std::unique_ptr<IStorageProvider> m_platformStorage;
+#ifdef HAS_DVD_DRIVE
+ std::shared_ptr<IDiscDriveHandler> m_platformDiscDriveHander;
+#endif
+
+ UTILS::DISCS::DiscInfo GetDiscInfo(const std::string& mediaPath);
+ void RemoveDiscInfo(const std::string& devicePath);
+ std::map<std::string, UTILS::DISCS::DiscInfo> m_mapDiscInfo;
+};
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);
+}
diff --git a/xbmc/storage/cdioSupport.h b/xbmc/storage/cdioSupport.h
new file mode 100644
index 0000000..b786ff2
--- /dev/null
+++ b/xbmc/storage/cdioSupport.h
@@ -0,0 +1,347 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+// CCdInfo - Information about media type of an inserted cd
+// CCdIoSupport - Wrapper class for libcdio with the interface of CIoSupport
+// and detecting the filesystem on the Disc.
+//
+// by Bobbin007 in 2003
+// CD-Text support by Mog - Oct 2004
+
+#include "threads/CriticalSection.h"
+
+#include <map>
+#include <memory>
+#include <string>
+#include <utility>
+
+#include "PlatformDefs.h" // for ssize_t typedef, used by cdio
+
+#include <cdio/cdio.h>
+
+namespace MEDIA_DETECT
+{
+
+#define STRONG "__________________________________\n"
+//#define NORMAL ""
+
+#define FS_NO_DATA 0 /* audio only */
+#define FS_HIGH_SIERRA 1
+#define FS_ISO_9660 2
+#define FS_INTERACTIVE 3
+#define FS_HFS 4
+#define FS_UFS 5
+#define FS_EXT2 6
+#define FS_ISO_HFS 7 /* both hfs & isofs filesystem */
+#define FS_ISO_9660_INTERACTIVE 8 /* both CD-RTOS and isofs filesystem */
+#define FS_3DO 9
+#define FS_UDF 11
+#define FS_ISO_UDF 12
+#define FS_UNKNOWN 15
+#define FS_MASK 15
+
+#define XA 16
+#define MULTISESSION 32
+#define PHOTO_CD 64
+#define HIDDEN_TRACK 128
+#define CDTV 256
+#define BOOTABLE 512
+#define VIDEOCDI 1024
+#define ROCKRIDGE 2048
+#define JOLIET 4096
+#define CVD 8192 /* Choiji Video CD */
+
+#define IS_ISOFS 0
+#define IS_CD_I 1
+#define IS_CDTV 2
+#define IS_CD_RTOS 3
+#define IS_HS 4
+#define IS_BRIDGE 5
+#define IS_XA 6
+#define IS_PHOTO_CD 7
+#define IS_EXT2 8
+#define IS_UFS 9
+#define IS_BOOTABLE 10
+#define IS_VIDEO_CD 11 /* Video CD */
+#define IS_CVD 12 /* Chinese Video CD - slightly incompatible with SVCD */
+#define IS_UDF 14
+
+typedef struct signature
+{
+ unsigned int buf_num;
+ unsigned int offset;
+ const char *sig_str;
+ const char *description;
+}
+signature_t;
+
+typedef std::map<cdtext_field_t, std::string> xbmc_cdtext_t;
+
+typedef struct TRACKINFO
+{
+ int nfsInfo; // Information of the Tracks Filesystem
+ int nJolietLevel; // Jouliet Level
+ int ms_offset; // Multisession Offset
+ int isofs_size; // Size of the ISO9660 Filesystem
+ int nFrames; // Can be used for cddb query
+ int nMins; // minutes playtime part of Track
+ int nSecs; // seconds playtime part of Track
+ xbmc_cdtext_t cdtext; // CD-Text for this track
+}
+trackinfo;
+
+/*! \brief Helper enum class for the MMC tray state
+*/
+enum class CdioTrayStatus
+{
+ /* The MMC tray state is reported closed */
+ CLOSED,
+ /* The MMC tray state is reported open */
+ OPEN,
+ /* The MMC tray status operation is not supported */
+ UNKNOWN,
+ /* Generic driver error */
+ DRIVER_ERROR
+};
+
+class CCdInfo
+{
+public:
+ CCdInfo()
+ {
+ m_bHasCDDBInfo = true;
+ m_nLength = m_nFirstTrack = m_nNumTrack = m_nNumAudio = m_nFirstAudio = m_nNumData = m_nFirstData = 0;
+ }
+
+ trackinfo GetTrackInformation( int nTrack ) { return m_ti[nTrack -1]; }
+ xbmc_cdtext_t GetDiscCDTextInformation() { return m_cdtext; }
+
+ bool HasDataTracks() { return (m_nNumData > 0); }
+ bool HasAudioTracks() { return (m_nNumAudio > 0); }
+ int GetFirstTrack() { return m_nFirstTrack; }
+ int GetTrackCount() { return m_nNumTrack; }
+ int GetFirstAudioTrack() { return m_nFirstAudio; }
+ int GetFirstDataTrack() { return m_nFirstData; }
+ int GetDataTrackCount() { return m_nNumData; }
+ int GetAudioTrackCount() { return m_nNumAudio; }
+ uint32_t GetCddbDiscId() { return m_ulCddbDiscId; }
+ int GetDiscLength() { return m_nLength; }
+ std::string GetDiscLabel(){ return m_strDiscLabel; }
+
+ // CD-ROM with ISO 9660 filesystem
+ bool IsIso9660( int nTrack ) { return ((m_ti[nTrack - 1].nfsInfo & FS_MASK) == FS_ISO_9660); }
+ // CD-ROM with joliet extension
+ bool IsJoliet( int nTrack ) { return (m_ti[nTrack - 1].nfsInfo & JOLIET) ? false : true; }
+ // Joliet extension level
+ int GetJolietLevel( int nTrack ) { return m_ti[nTrack - 1].nJolietLevel; }
+ // ISO filesystem size
+ int GetIsoSize( int nTrack ) { return m_ti[nTrack - 1].isofs_size; }
+ // CD-ROM with rockridge extensions
+ bool IsRockridge( int nTrack ) { return (m_ti[nTrack - 1].nfsInfo & ROCKRIDGE) ? false : true; }
+
+ // CD-ROM with CD-RTOS and ISO 9660 filesystem
+ bool IsIso9660Interactive( int nTrack ) { return ((m_ti[nTrack - 1].nfsInfo & FS_MASK) == FS_ISO_9660_INTERACTIVE); }
+
+ // CD-ROM with High Sierra filesystem
+ bool IsHighSierra( int nTrack ) { return ((m_ti[nTrack - 1].nfsInfo & FS_MASK) == FS_HIGH_SIERRA); }
+
+ // CD-Interactive, with audiotracks > 0 CD-Interactive/Ready
+ bool IsCDInteractive( int nTrack ) { return ((m_ti[nTrack - 1].nfsInfo & FS_MASK) == FS_INTERACTIVE); }
+
+ // CD-ROM with Macintosh HFS
+ bool IsHFS( int nTrack ) { return ((m_ti[nTrack - 1].nfsInfo & FS_MASK) == FS_HFS); }
+
+ // CD-ROM with both Macintosh HFS and ISO 9660 filesystem
+ bool IsISOHFS( int nTrack ) { return ((m_ti[nTrack - 1].nfsInfo & FS_MASK) == FS_ISO_HFS); }
+
+ // CD-ROM with both UDF and ISO 9660 filesystem
+ bool IsISOUDF( int nTrack ) { return ((m_ti[nTrack - 1].nfsInfo & FS_MASK) == FS_ISO_UDF); }
+
+ // CD-ROM with Unix UFS
+ bool IsUFS( int nTrack ) { return ((m_ti[nTrack - 1].nfsInfo & FS_MASK) == FS_UFS); }
+
+ // CD-ROM with Linux second extended filesystem
+ bool IsEXT2( int nTrack ) { return ((m_ti[nTrack - 1].nfsInfo & FS_MASK) == FS_EXT2); }
+
+ // CD-ROM with Panasonic 3DO filesystem
+ bool Is3DO( int nTrack ) { return ((m_ti[nTrack - 1].nfsInfo & FS_MASK) == FS_3DO); }
+
+ // Mixed Mode CD-ROM
+ bool IsMixedMode( int nTrack ) { return (m_nFirstData == 1 && m_nNumAudio > 0); }
+
+ // CD-ROM with XA sectors
+ bool IsXA( int nTrack ) { return (m_ti[nTrack - 1].nfsInfo & XA) ? false : true; }
+
+ // Multisession CD-ROM
+ bool IsMultiSession( int nTrack ) { return (m_ti[nTrack - 1].nfsInfo & MULTISESSION) ? false : true; }
+ // Gets multisession offset
+ int GetMultisessionOffset( int nTrack ) { return m_ti[nTrack - 1].ms_offset; }
+
+ // Hidden Track on Audio CD
+ bool IsHiddenTrack( int nTrack ) { return (m_ti[nTrack - 1].nfsInfo & HIDDEN_TRACK) ? false : true; }
+
+ // Photo CD, with audiotracks > 0 Portfolio Photo CD
+ bool IsPhotoCd( int nTrack ) { return (m_ti[nTrack - 1].nfsInfo & PHOTO_CD) ? false : true; }
+
+ // CD-ROM with Commodore CDTV
+ bool IsCdTv( int nTrack ) { return (m_ti[nTrack - 1].nfsInfo & CDTV) ? false : true; }
+
+ // CD-Plus/Extra
+ bool IsCDExtra( int nTrack ) { return (m_nFirstData > 1); }
+
+ // Bootable CD
+ bool IsBootable( int nTrack ) { return (m_ti[nTrack - 1].nfsInfo & BOOTABLE) ? false : true; }
+
+ // Video CD
+ bool IsVideoCd( int nTrack ) { return (m_ti[nTrack - 1].nfsInfo & VIDEOCDI && m_nNumAudio == 0); }
+
+ // Chaoji Video CD
+ bool IsChaojiVideoCD( int nTrack ) { return (m_ti[nTrack - 1].nfsInfo & CVD) ? false : true; }
+
+ // Audio Track
+ bool IsAudio( int nTrack ) { return ((m_ti[nTrack - 1].nfsInfo & FS_MASK) == FS_NO_DATA); }
+
+ // UDF filesystem
+ bool IsUDF( int nTrack ) { return ((m_ti[nTrack - 1].nfsInfo & FS_MASK) == FS_UDF); }
+
+ // Has the cd a filesystem that is readable by the xbox
+ bool IsValidFs() { return (IsISOHFS(1) || IsIso9660(1) || IsIso9660Interactive(1) || IsISOUDF(1) || IsUDF(1) || IsAudio(1)); }
+
+ void SetFirstTrack( int nTrack ) { m_nFirstTrack = nTrack; }
+ void SetTrackCount( int nCount ) { m_nNumTrack = nCount; }
+ void SetFirstAudioTrack( int nTrack ) { m_nFirstAudio = nTrack; }
+ void SetFirstDataTrack( int nTrack ) { m_nFirstData = nTrack; }
+ void SetDataTrackCount( int nCount ) { m_nNumData = nCount; }
+ void SetAudioTrackCount( int nCount ) { m_nNumAudio = nCount; }
+ void SetTrackInformation(int nTrack, trackinfo nInfo)
+ {
+ if (nTrack > 0 && nTrack <= 99)
+ m_ti[nTrack - 1] = std::move(nInfo);
+ }
+ void SetDiscCDTextInformation(xbmc_cdtext_t cdtext) { m_cdtext = std::move(cdtext); }
+
+ void SetCddbDiscId( uint32_t ulCddbDiscId ) { m_ulCddbDiscId = ulCddbDiscId; }
+ void SetDiscLength( int nLength ) { m_nLength = nLength; }
+ bool HasCDDBInfo() { return m_bHasCDDBInfo; }
+ void SetNoCDDBInfo() { m_bHasCDDBInfo = false; }
+
+ void SetDiscLabel(const std::string& strDiscLabel){ m_strDiscLabel = strDiscLabel; }
+
+private:
+ int m_nFirstData; /* # of first data track */
+ int m_nNumData; /* # of data tracks */
+ int m_nFirstAudio; /* # of first audio track */
+ int m_nNumAudio; /* # of audio tracks */
+ int m_nNumTrack;
+ int m_nFirstTrack;
+ trackinfo m_ti[100];
+ uint32_t m_ulCddbDiscId;
+ int m_nLength; // Disclength can be used for cddb query, also see trackinfo.nFrames
+ bool m_bHasCDDBInfo;
+ std::string m_strDiscLabel;
+ xbmc_cdtext_t m_cdtext; // CD-Text for this disc
+};
+
+class CLibcdio : public CCriticalSection
+{
+private:
+ CLibcdio();
+public:
+ virtual ~CLibcdio();
+
+ static void ReleaseInstance();
+ static std::shared_ptr<CLibcdio> GetInstance();
+
+ // libcdio is not thread safe so these are wrappers to libcdio routines
+ CdIo_t* cdio_open(const char *psz_source, driver_id_t driver_id);
+ CdIo_t* cdio_open_win32(const char *psz_source);
+ void cdio_destroy(CdIo_t *p_cdio);
+ discmode_t cdio_get_discmode(CdIo_t *p_cdio);
+ CdioTrayStatus mmc_get_tray_status(const CdIo_t* p_cdio);
+ driver_return_code_t cdio_eject_media(CdIo_t** p_cdio);
+ track_t cdio_get_last_track_num(const CdIo_t *p_cdio);
+ lsn_t cdio_get_track_lsn(const CdIo_t *p_cdio, track_t i_track);
+ lsn_t cdio_get_track_last_lsn(const CdIo_t *p_cdio, track_t i_track);
+ driver_return_code_t cdio_read_audio_sectors(const CdIo_t *p_cdio, void *p_buf, lsn_t i_lsn, uint32_t i_blocks);
+ driver_return_code_t cdio_close_tray(const char* psz_source, driver_id_t* driver_id);
+ const char* cdio_driver_errmsg(driver_return_code_t drc);
+
+ char* GetDeviceFileName();
+
+private:
+ char* s_defaultDevice;
+ CCriticalSection m_critSection;
+ static std::shared_ptr<CLibcdio> m_pInstance;
+};
+
+class CCdIoSupport
+{
+public:
+ CCdIoSupport();
+ virtual ~CCdIoSupport();
+
+ bool EjectTray();
+ bool CloseTray();
+
+ HANDLE OpenCDROM();
+ HANDLE OpenIMAGE( std::string& strFilename );
+ int ReadSector(HANDLE hDevice, DWORD dwSector, char* lpczBuffer);
+ int ReadSectorMode2(HANDLE hDevice, DWORD dwSector, char* lpczBuffer);
+ int ReadSectorCDDA(HANDLE hDevice, DWORD dwSector, char* lpczBuffer);
+ void CloseCDROM(HANDLE hDevice);
+
+ void PrintAnalysis(int fs, int num_audio);
+
+ CCdInfo* GetCdInfo(char* cDeviceFileName=NULL);
+ void GetCdTextInfo(xbmc_cdtext_t &xcdt, int trackNum);
+
+protected:
+ int ReadBlock(int superblock, uint32_t offset, uint8_t bufnum, track_t track_num);
+ bool IsIt(int num);
+ int IsHFS(void);
+ int Is3DO(void);
+ int IsJoliet(void);
+ int IsUDF(void);
+ int GetSize(void);
+ int GetJolietLevel( void );
+ int GuessFilesystem(int start_session, track_t track_num);
+
+ uint32_t CddbDiscId();
+ int CddbDecDigitSum(int n);
+ unsigned int MsfSeconds(msf_t *msf);
+
+private:
+ char buffer[7][CDIO_CD_FRAMESIZE_RAW]; /* for CD-Data */
+ static signature_t sigs[17];
+ int i = 0, j = 0; /* index */
+ int m_nStartTrack; /* first sector of track */
+ int m_nIsofsSize; /* size of session */
+ int m_nJolietLevel;
+ int m_nMsOffset; /* multisession offset found by track-walking */
+ int m_nDataStart; /* start of data area */
+ int m_nFs;
+ int m_nUDFVerMinor;
+ int m_nUDFVerMajor;
+
+ CdIo* cdio;
+ track_t m_nNumTracks = CDIO_INVALID_TRACK;
+ track_t m_nFirstTrackNum = CDIO_INVALID_TRACK;
+
+ std::string m_strDiscLabel;
+
+ int m_nFirstData; /* # of first data track */
+ int m_nNumData; /* # of data tracks */
+ int m_nFirstAudio; /* # of first audio track */
+ int m_nNumAudio; /* # of audio tracks */
+
+ std::shared_ptr<CLibcdio> m_cdio;
+};
+
+}
diff --git a/xbmc/storage/discs/IDiscDriveHandler.h b/xbmc/storage/discs/IDiscDriveHandler.h
new file mode 100644
index 0000000..a3b007a
--- /dev/null
+++ b/xbmc/storage/discs/IDiscDriveHandler.h
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+#pragma once
+
+#include <memory>
+#include <string>
+
+/*! \brief Represents the state of a disc (optical) drive */
+enum class DriveState
+{
+ /*! The drive is open */
+ OPEN,
+ /*! The drive is not ready (happens when openning or closing) */
+ NOT_READY,
+ /*! The drive is ready */
+ READY,
+ /*! The drive is closed but no media could be detected in the drive */
+ CLOSED_NO_MEDIA,
+ /*! The drive is closed and there is media in the drive */
+ CLOSED_MEDIA_PRESENT,
+ /*! The system does not have an optical drive */
+ NONE,
+ /*! The drive is closed but we don't know yet if there's media there */
+ CLOSED_MEDIA_UNDEFINED
+};
+
+/*! \brief Represents the state of the drive tray */
+enum class TrayState
+{
+ /*! The tray is in an undefined state, we don't know yet */
+ UNDEFINED,
+ /*! The tray is open */
+ OPEN,
+ /*! The tray is closed and doesn't have any optical media */
+ CLOSED_NO_MEDIA,
+ /*! The tray is closed and contains optical media */
+ CLOSED_MEDIA_PRESENT
+};
+
+/*! \brief Generic interface for platform disc drive handling
+*/
+class IDiscDriveHandler
+{
+public:
+ /*! \brief Get the optical drive state provided its device path
+ * \param devicePath the path for the device drive (e.g. /dev/sr0)
+ * \return The drive state
+ */
+ virtual DriveState GetDriveState(const std::string& devicePath) = 0;
+
+ /*! \brief Get the optical drive tray state provided the drive device path
+ * \param devicePath the path for the device drive (e.g. /dev/sr0)
+ * \return The drive state
+ */
+ virtual TrayState GetTrayState(const std::string& devicePath) = 0;
+
+ /*! \brief Eject the provided drive device
+ * \param devicePath the path for the device drive (e.g. /dev/sr0)
+ */
+ virtual void EjectDriveTray(const std::string& devicePath) = 0;
+
+ /*! \brief Close the provided drive device
+ * \note Some drives support closing appart from opening/eject
+ * \param devicePath the path for the device drive (e.g. /dev/sr0)
+ */
+ virtual void CloseDriveTray(const std::string& devicePath) = 0;
+
+ /*! \brief Toggle the state of a given drive device
+ *
+ * Will internally call EjectDriveTray or CloseDriveTray depending on
+ * the internal state of the drive (i.e. if open -> CloseDriveTray /
+ * if closed -> EjectDriveTray)
+ *
+ * \param devicePath the path for the device drive (e.g. /dev/sr0)
+ */
+ virtual void ToggleDriveTray(const std::string& devicePath) = 0;
+
+ /*! \brief Called to create platform-specific disc drive handler
+ *
+ * This method is used to create platform-specific disc drive handler
+ */
+ static std::shared_ptr<IDiscDriveHandler> CreateInstance();
+
+protected:
+ virtual ~IDiscDriveHandler() = default;
+ IDiscDriveHandler() = default;
+};