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 )