diff --git a/pcsx2/CDVD/Darwin/DriveUtility.cpp b/pcsx2/CDVD/Darwin/DriveUtility.cpp
new file mode 100644
index 0000000000..125803419f
--- /dev/null
+++ b/pcsx2/CDVD/Darwin/DriveUtility.cpp
@@ -0,0 +1,119 @@
+/* PCSX2 - PS2 Emulator for PCs
+ * Copyright (C) 2002-2022 PCSX2 Dev Team
+ *
+ * PCSX2 is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU Lesser General Public License as published by the Free Software Found-
+ * ation, either version 3 of the License, or (at your option) any later version.
+ *
+ * PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with PCSX2.
+ * If not, see .
+ */
+
+#include "PrecompiledHeader.h"
+#include "CDVD/CDVDdiscReader.h"
+
+#ifdef __APPLE__
+#include
+
+#include
+#include
+#include
+#include
+#include
+#endif
+
+#include
+#include
+
+#ifdef __APPLE__
+
+std::vector GetDriveListFromClasses(CFMutableDictionaryRef classes)
+{
+ io_iterator_t iterator = IO_OBJECT_NULL;
+ kern_return_t result;
+ std::vector drives;
+
+ CFDictionarySetValue(classes, CFSTR(kIOMediaEjectableKey), kCFBooleanTrue);
+ result = IOServiceGetMatchingServices(kIOMasterPortDefault, classes, &iterator);
+ if (result != KERN_SUCCESS)
+ return drives;
+ io_object_t media = IOIteratorNext(iterator);
+ while (media)
+ {
+ CFTypeRef path_cfstr = IORegistryEntryCreateCFProperty(media, CFSTR(kIOBSDNameKey), kCFAllocatorDefault, 0);
+ if (path_cfstr)
+ {
+ char path[PATH_MAX] = {0};
+ strlcpy(path, "/dev/r", PATH_MAX);
+ size_t path_prefix_len = strnlen(path, PATH_MAX);
+ result = CFStringGetCString((CFStringRef)path_cfstr, path + path_prefix_len, PATH_MAX - path_prefix_len, kCFStringEncodingUTF8);
+ if (result)
+ {
+ drives.emplace_back(path);
+ }
+ CFRelease(path_cfstr);
+ }
+ IOObjectRelease(media);
+ media = IOIteratorNext(iterator);
+ }
+ IOObjectRelease(iterator);
+ return drives;
+}
+
+#endif
+
+std::vector GetOpticalDriveList()
+{
+#ifdef __APPLE__
+ std::vector drives;
+
+ CFMutableDictionaryRef cd_classes = IOServiceMatching(kIOCDMediaClass);
+ if (cd_classes)
+ {
+ std::vector cd = GetDriveListFromClasses(cd_classes);
+ drives.insert(drives.end(), cd.begin(), cd.end());
+ }
+
+ CFMutableDictionaryRef dvd_classes = IOServiceMatching(kIODVDMediaClass);
+ if (dvd_classes)
+ {
+ std::vector dvd = GetDriveListFromClasses(dvd_classes);
+ drives.insert(drives.end(), dvd.begin(), dvd.end());
+ }
+ return drives;
+#else
+ return {};
+#endif
+}
+
+void GetValidDrive(std::string& drive)
+{
+ if (!drive.empty())
+ {
+#ifdef __APPLE__
+ int fd = open(drive.c_str(), O_RDONLY | O_NONBLOCK);
+ if (fd != -1)
+ {
+ close(fd);
+ }
+ else
+ {
+ drive.clear();
+ }
+#else
+ drive.clear();
+#endif
+ }
+ if (drive.empty())
+ {
+ auto drives = GetOpticalDriveList();
+ if (!drives.empty())
+ drive = drives.front();
+ }
+ if (!drive.empty())
+ printf(" * CDVD: Opening drive '%s'...\n", drive.c_str());
+}
diff --git a/pcsx2/CDVD/Darwin/IOCtlSrc.cpp b/pcsx2/CDVD/Darwin/IOCtlSrc.cpp
new file mode 100644
index 0000000000..dd3412522c
--- /dev/null
+++ b/pcsx2/CDVD/Darwin/IOCtlSrc.cpp
@@ -0,0 +1,251 @@
+/* PCSX2 - PS2 Emulator for PCs
+ * Copyright (C) 2002-2022 PCSX2 Dev Team
+ *
+ * PCSX2 is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU Lesser General Public License as published by the Free Software Found-
+ * ation, either version 3 of the License, or (at your option) any later version.
+ *
+ * PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with PCSX2.
+ * If not, see .
+ */
+
+#include "PrecompiledHeader.h"
+#include "CDVD/CDVDdiscReader.h"
+#include "CDVD/CDVD.h"
+
+#ifdef __APPLE__
+#include
+#include
+#endif
+
+#include
+#include
+#include
+
+#include
+#include
+
+IOCtlSrc::IOCtlSrc(decltype(m_filename) filename)
+ : m_filename(filename)
+{
+ if (!Reopen())
+ throw std::runtime_error(" * CDVD: Error opening source.\n");
+}
+
+IOCtlSrc::~IOCtlSrc()
+{
+ if (m_device != -1)
+ {
+ SetSpindleSpeed(true);
+ close(m_device);
+ }
+}
+
+bool IOCtlSrc::Reopen()
+{
+ if (m_device != -1)
+ close(m_device);
+
+ // O_NONBLOCK allows a valid file descriptor to be returned even if the
+ // drive is empty. Probably does other things too.
+ m_device = open(m_filename.c_str(), O_RDONLY | O_NONBLOCK);
+ if (m_device == -1)
+ return false;
+
+ // DVD detection MUST be first on Linux - The TOC ioctls work for both
+ // CDs and DVDs.
+ if (ReadDVDInfo() || ReadCDInfo())
+ SetSpindleSpeed(false);
+
+ return true;
+}
+
+void IOCtlSrc::SetSpindleSpeed(bool restore_defaults) const
+{
+ // TODO: Seems it's actually able to set it (DKIOCCDSETSPEED) but I don't have
+ // physical drives right now to test
+}
+
+u32 IOCtlSrc::GetSectorCount() const
+{
+ return m_sectors;
+}
+
+u32 IOCtlSrc::GetLayerBreakAddress() const
+{
+ return m_layer_break;
+}
+
+s32 IOCtlSrc::GetMediaType() const
+{
+ return m_media_type;
+}
+
+const std::vector& IOCtlSrc::ReadTOC() const
+{
+ return m_toc;
+}
+
+bool IOCtlSrc::ReadSectors2048(u32 sector, u32 count, u8* buffer) const
+{
+ const ssize_t bytes_to_read = 2048 * count;
+ ssize_t bytes_read = pread(m_device, buffer, bytes_to_read, sector * 2048ULL);
+ if (bytes_read == bytes_to_read)
+ return true;
+
+ if (bytes_read == -1)
+ fprintf(stderr, " * CDVD read sectors %u-%u failed: %s\n",
+ sector, sector + count - 1, strerror(errno));
+ else
+ fprintf(stderr, " * CDVD read sectors %u-%u: %zd bytes read, %zd bytes expected\n",
+ sector, sector + count - 1, bytes_read, bytes_to_read);
+ return false;
+}
+
+bool IOCtlSrc::ReadSectors2352(u32 sector, u32 count, u8* buffer) const
+{
+#ifdef __APPLE__
+ dk_cd_read_t desc;
+ memset(&desc, 0, sizeof(dk_cd_read_t));
+ desc.sectorArea = kCDSectorAreaUser;
+ desc.sectorType = kCDSectorTypeCDDA;
+ for (u32 i = 0; i < count; ++i)
+ {
+ desc.offset = (sector + i) * 2352ULL;
+ desc.buffer = buffer + i * 2352;
+ desc.bufferLength = 2352;
+ if (ioctl(m_device, DKIOCCDREAD, &desc) == -1)
+ {
+ fprintf(stderr, " * CDVD CDROMREADRAW sector %u failed: %s\n",
+ sector + i, strerror(errno));
+ return false;
+ }
+ }
+ return true;
+#else
+ return false;
+#endif
+}
+
+bool IOCtlSrc::ReadDVDInfo()
+{
+#ifdef __APPLE__
+ dk_dvd_read_structure_t dvdrs;
+ memset(&dvdrs, 0, sizeof(dk_dvd_read_structure_t));
+ dvdrs.format = kDVDStructureFormatPhysicalFormatInfo;
+ dvdrs.layer = 0;
+
+ DVDPhysicalFormatInfo layer0;
+ dvdrs.buffer = &layer0;
+ dvdrs.bufferLength = sizeof(DVDPhysicalFormatInfo);
+
+ int ret = ioctl(m_device, DKIOCDVDREADSTRUCTURE, &dvdrs);
+ if (ret == -1)
+ {
+ return false;
+ }
+
+ u32 start_sector = *(u32*)layer0.startingPhysicalSectorNumberOfDataArea;
+ u32 end_sector = *(u32*)layer0.endPhysicalSectorNumberOfDataArea;
+ if (layer0.numberOfLayers == 0)
+ {
+ // Single layer
+ m_media_type = 0;
+ m_layer_break = 0;
+ m_sectors = end_sector - start_sector + 1;
+ }
+ else if (layer0.trackPath == 0)
+ {
+ // Dual layer, Parallel Track Path
+ DVDPhysicalFormatInfo layer1;
+ dvdrs.layer = 1;
+ dvdrs.buffer = &layer1;
+ dvdrs.bufferLength = sizeof(DVDPhysicalFormatInfo);
+ ret = ioctl(m_device, DKIOCDVDREADSTRUCTURE, &dvdrs);
+ if (ret == -1)
+ return false;
+ u32 layer1_start_sector = *(u32*)layer1.startingPhysicalSectorNumberOfDataArea;
+ u32 layer1_end_sector = *(u32*)layer1.endPhysicalSectorNumberOfDataArea;
+
+ m_media_type = 1;
+ m_layer_break = end_sector - start_sector;
+ m_sectors = end_sector - start_sector + 1 + layer1_end_sector - layer1_start_sector + 1;
+ }
+ else
+ {
+ // Dual layer, Opposite Track Path
+ u32 end_sector_layer0 = *(u32*)layer0.endSectorNumberInLayerZero;
+ m_media_type = 2;
+ m_layer_break = end_sector_layer0 - start_sector;
+ m_sectors = end_sector_layer0 - start_sector + 1 + end_sector - (~end_sector_layer0 & 0xFFFFFFU) + 1;
+ }
+
+ return true;
+#else
+ return false;
+#endif
+}
+
+bool IOCtlSrc::ReadCDInfo()
+{
+#ifdef __APPLE__
+ u8* buffer = (u8*)malloc(2048);
+ dk_cd_read_toc_t cdrt;
+ memset(&cdrt, 0, sizeof(dk_cd_read_toc_t));
+ cdrt.format = kCDTOCFormatTOC;
+ cdrt.formatAsTime = 1;
+ cdrt.address.track = 0;
+ cdrt.buffer = buffer;
+ cdrt.bufferLength = 2048;
+ memset(buffer, 0, 2048);
+
+ if (ioctl(m_device, DKIOCCDREADTOC, &cdrt) == -1)
+ {
+ fprintf(stderr, "%s\n", strerror(errno));
+ return false;
+ }
+
+ CDTOC* toc = (CDTOC*)buffer;
+
+ u32 desc_count = CDTOCGetDescriptorCount(toc);
+
+ for (u32 i = 0; i < desc_count; ++i)
+ {
+ CDTOCDescriptor desc = toc->descriptors[i];
+ if (desc.point < 0xa0 && desc.adr == 1)
+ {
+ u32 lba = CDConvertMSFToLBA(desc.p);
+ m_toc.push_back({lba, desc.point, desc.adr, desc.control});
+ m_sectors = lba;
+ }
+ }
+
+ m_media_type = -1;
+
+ free(buffer);
+ return true;
+#else
+ return false;
+#endif
+}
+
+bool IOCtlSrc::DiscReady()
+{
+#ifdef __APPLE__
+ if (m_device == -1)
+ return false;
+
+ if (!m_sectors)
+ {
+ Reopen();
+ }
+
+ return !!m_sectors;
+#else
+ return false;
+#endif
+}
diff --git a/pcsx2/CDVD/Linux/DriveUtility.cpp b/pcsx2/CDVD/Linux/DriveUtility.cpp
index 9ba07d154f..ad91e2f8b9 100644
--- a/pcsx2/CDVD/Linux/DriveUtility.cpp
+++ b/pcsx2/CDVD/Linux/DriveUtility.cpp
@@ -16,18 +16,14 @@
#include "PrecompiledHeader.h"
#include "CDVD/CDVDdiscReader.h"
-#ifdef __linux__
#include
#include
-#endif
-
#include
#include
#include
std::vector GetOpticalDriveList()
{
-#ifdef __linux__
udev* udev_context = udev_new();
if (!udev_context)
return {};
@@ -56,16 +52,12 @@ std::vector GetOpticalDriveList()
udev_unref(udev_context);
return drives;
-#else
- return {};
-#endif
}
void GetValidDrive(std::string& drive)
{
if (!drive.empty())
{
-#ifdef __linux__
int fd = open(drive.c_str(), O_RDONLY | O_NONBLOCK);
if (fd != -1)
{
@@ -77,9 +69,6 @@ void GetValidDrive(std::string& drive)
{
drive.clear();
}
-#else
- drive.clear();
-#endif
}
if (drive.empty())
{
diff --git a/pcsx2/CDVD/Linux/IOCtlSrc.cpp b/pcsx2/CDVD/Linux/IOCtlSrc.cpp
index 4adee1f3e0..a424d05ceb 100644
--- a/pcsx2/CDVD/Linux/IOCtlSrc.cpp
+++ b/pcsx2/CDVD/Linux/IOCtlSrc.cpp
@@ -17,10 +17,7 @@
#include "CDVD/CDVDdiscReader.h"
#include "CDVD/CDVD.h"
-#ifdef __linux__
#include
-#endif
-
#include
#include
#include
@@ -108,7 +105,6 @@ bool IOCtlSrc::ReadSectors2048(u32 sector, u32 count, u8* buffer) const
bool IOCtlSrc::ReadSectors2352(u32 sector, u32 count, u8* buffer) const
{
-#ifdef __linux__
union
{
cdrom_msf msf;
@@ -130,14 +126,10 @@ bool IOCtlSrc::ReadSectors2352(u32 sector, u32 count, u8* buffer) const
}
return true;
-#else
- return false;
-#endif
}
bool IOCtlSrc::ReadDVDInfo()
{
-#ifdef __linux__
dvd_struct dvdrs;
dvdrs.type = DVD_STRUCT_PHYSICAL;
dvdrs.physical.layer_num = 0;
@@ -180,14 +172,10 @@ bool IOCtlSrc::ReadDVDInfo()
}
return true;
-#else
- return false;
-#endif
}
bool IOCtlSrc::ReadCDInfo()
{
-#ifdef __linux__
cdrom_tochdr header;
if (ioctl(m_device, CDROMREADTOCHDR, &header) == -1)
@@ -214,14 +202,10 @@ bool IOCtlSrc::ReadCDInfo()
m_media_type = -1;
return true;
-#else
- return false;
-#endif
}
bool IOCtlSrc::DiscReady()
{
-#ifdef __linux__
if (m_device == -1)
return false;
@@ -239,7 +223,4 @@ bool IOCtlSrc::DiscReady()
}
return !!m_sectors;
-#else
- return false;
-#endif
}
diff --git a/pcsx2/CMakeLists.txt b/pcsx2/CMakeLists.txt
index 0fda95c59b..22282b0dd2 100644
--- a/pcsx2/CMakeLists.txt
+++ b/pcsx2/CMakeLists.txt
@@ -916,14 +916,14 @@ set(pcsx2LinuxSources
)
set(pcsx2OSXSources
- CDVD/Linux/DriveUtility.cpp
- CDVD/Linux/IOCtlSrc.cpp
+ CDVD/Darwin/DriveUtility.cpp
+ CDVD/Darwin/IOCtlSrc.cpp
Darwin/DarwinFlatFileReader.cpp
)
set(pcsx2FreeBSDSources
- CDVD/Linux/DriveUtility.cpp
- CDVD/Linux/IOCtlSrc.cpp
+ CDVD/Darwin/DriveUtility.cpp
+ CDVD/Darwin/IOCtlSrc.cpp
Darwin/DarwinFlatFileReader.cpp
)