From 3d8be787a24d141082fe8ba6a5e3a5b2c977ff7a Mon Sep 17 00:00:00 2001 From: Jonathan Li Date: Fri, 2 Sep 2016 00:00:22 +0100 Subject: [PATCH] cdvdgigaherz: Use SPTI to read raw CD sectors IOCTL_CDROM_RAW_READ apparently does not work for some read modes on some optical drives, which makes some CD-ROM games unplayable from the disc. Work around the issue by using SPTI to retrieve the raw sector data. The old reading method has been retained in case SPTI cannot be used (if the device could not be opened with write access). --- plugins/cdvdGigaherz/src/CDVD.h | 2 +- plugins/cdvdGigaherz/src/Windows/IOCtlSrc.cpp | 54 ++++++++++++++++++- 2 files changed, 54 insertions(+), 2 deletions(-) diff --git a/plugins/cdvdGigaherz/src/CDVD.h b/plugins/cdvdGigaherz/src/CDVD.h index e2d96a6d5f..71f96af193 100644 --- a/plugins/cdvdGigaherz/src/CDVD.h +++ b/plugins/cdvdGigaherz/src/CDVD.h @@ -90,7 +90,7 @@ class IOCtlSrc: public Source IOCtlSrc(IOCtlSrc&); HANDLE device; - + bool m_can_use_spti; bool OpenOK; s32 last_read_mode; diff --git a/plugins/cdvdGigaherz/src/Windows/IOCtlSrc.cpp b/plugins/cdvdGigaherz/src/Windows/IOCtlSrc.cpp index 949e696c7c..165b467c1f 100644 --- a/plugins/cdvdGigaherz/src/Windows/IOCtlSrc.cpp +++ b/plugins/cdvdGigaherz/src/Windows/IOCtlSrc.cpp @@ -18,6 +18,12 @@ #include #include #include +// "typedef ignored" warning will disappear once we move to the Windows 10 SDK. +#pragma warning (push) +#pragma warning (disable: 4091) +#include +#pragma warning (pop) + #include #include @@ -284,13 +290,15 @@ s32 IOCtlSrc::Reopen() DWORD size; OpenOK = false; - + // SPTI only works if the device is opened with GENERIC_WRITE access. + m_can_use_spti = true; device = CreateFile(fName, GENERIC_READ|GENERIC_WRITE|FILE_READ_ATTRIBUTES, share, NULL, OPEN_EXISTING, flags, 0); if(device==INVALID_HANDLE_VALUE) { device = CreateFile(fName, GENERIC_READ|FILE_READ_ATTRIBUTES, share, NULL, OPEN_EXISTING, flags, 0); if(device==INVALID_HANDLE_VALUE) return -1; + m_can_use_spti = false; } // Dual layer DVDs cannot read from layer 1 without this ioctl DeviceIoControl(device, FSCTL_ALLOW_EXTENDED_DASD_IO, nullptr, 0, nullptr, 0, &size, nullptr); @@ -624,6 +632,8 @@ s32 IOCtlSrc::ReadSectors2048(u32 sector, u32 count, char *buffer) } +// FIXME: Probably doesn't work if the sectors to be read are from two tracks +// of different types. s32 IOCtlSrc::ReadSectors2352(u32 sector, u32 count, char *buffer) { RAW_READ_INFO rri; @@ -632,6 +642,48 @@ s32 IOCtlSrc::ReadSectors2352(u32 sector, u32 count, char *buffer) if(!OpenOK) return -1; + if (m_can_use_spti) + { + struct sptdinfo + { + SCSI_PASS_THROUGH_DIRECT info; + char sense_buffer[20]; + } sptd = {}; + + // READ CD command + sptd.info.Cdb[0] = 0xBE; + // Don't care about sector type. + sptd.info.Cdb[1] = 0; + sptd.info.Cdb[2] = (sector >> 24) & 0xFF; + sptd.info.Cdb[3] = (sector >> 16) & 0xFF; + sptd.info.Cdb[4] = (sector >> 8) & 0xFF; + sptd.info.Cdb[5] = sector & 0xFF; + sptd.info.Cdb[6] = (count >> 16) & 0xFF; + sptd.info.Cdb[7] = (count >> 8) & 0xFF; + sptd.info.Cdb[8] = count & 0xFF; + // Sync + all headers + user data + EDC/ECC. Excludes C2 + subchannel + sptd.info.Cdb[9] = 0xF8; + sptd.info.Cdb[10] = 0; + sptd.info.Cdb[11] = 0; + + sptd.info.CdbLength = 12; + sptd.info.Length = sizeof(SCSI_PASS_THROUGH_DIRECT); + sptd.info.DataIn = SCSI_IOCTL_DATA_IN; + sptd.info.DataTransferLength = 2352 * count; + sptd.info.DataBuffer = buffer; + sptd.info.SenseInfoLength = sizeof(sptd.sense_buffer); + sptd.info.SenseInfoOffset = offsetof(sptdinfo, sense_buffer); + sptd.info.TimeOutValue = 5; + + if (DeviceIoControl(device, IOCTL_SCSI_PASS_THROUGH_DIRECT, &sptd, + sizeof(sptd), &sptd, sizeof(sptd), &size, nullptr)) + { + if (sptd.info.DataTransferLength == 2352 * count) + return 0; + } + printf(" * CDVD: SPTI failed reading sectors %u-%u\n", sector, sector + count - 1); + } + rri.DiskOffset.QuadPart=sector*(u64)2048; rri.SectorCount=count;