Skip to content

Commit a4f64b7

Browse files
committed
Improve physical CD-ROM read reliability. #117
1 parent 9d6d1a0 commit a4f64b7

6 files changed

Lines changed: 553 additions & 95 deletions

File tree

src/cdrom_drive.cpp

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,12 @@
2121

2222
#if defined(GG_ENABLE_PHYSICAL_CDROM)
2323

24+
#if !defined(_WIN32)
25+
#define CDROM_DRIVE_DATA_SECTOR_SIZE 2048
26+
#define CDROM_DRIVE_DATA_SECTOR_OFFSET 16
27+
#define CDROM_DRIVE_RAW_SECTOR_SIZE 2352
28+
#endif
29+
2430
CdRomDrive::CdRomDrive()
2531
{
2632
#if defined(_WIN32)
@@ -50,6 +56,21 @@ bool CdRomDrive::ReadRawSector2352(u32 lba, u8* buffer, bool audio, bool report_
5056
return ReadRawSectors2352(lba, 1, buffer, audio, report_errors);
5157
}
5258

59+
#if !defined(_WIN32)
60+
bool CdRomDrive::ReadDataSector2048(u32 lba, u8* buffer, bool report_errors)
61+
{
62+
if (!IsValidPointer(buffer))
63+
return false;
64+
65+
u8 raw[CDROM_DRIVE_RAW_SECTOR_SIZE];
66+
if (!ReadRawSector2352(lba, raw, false, report_errors))
67+
return false;
68+
69+
memcpy(buffer, raw + CDROM_DRIVE_DATA_SECTOR_OFFSET, CDROM_DRIVE_DATA_SECTOR_SIZE);
70+
return true;
71+
}
72+
#endif
73+
5374
const char* CdRomDrive::GetDeviceId() const
5475
{
5576
return m_device_id;

src/cdrom_drive.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ class CdRomDrive
5353
void Close();
5454
bool IsOpen() const;
5555
bool ReadTOC(std::vector<CdRomDriveTrackInfo>& tracks, u32* lead_out_lba);
56+
bool ReadDataSector2048(u32 lba, u8* buffer, bool report_errors = true);
5657
bool ReadRawSectors2352(u32 lba, u32 sector_count, u8* buffer, bool audio, bool report_errors = true);
5758
bool ReadRawSector2352(u32 lba, u8* buffer, bool audio = false, bool report_errors = true);
5859
bool SetSpeed(u16 speed);

src/cdrom_drive_linux.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
*
1818
*/
1919

20-
#include "cdrom_drive.h"
20+
#include "cdrom_drive.h"
2121

2222
#if defined(GG_ENABLE_PHYSICAL_CDROM) && defined(__linux__)
2323

src/cdrom_drive_win32.cpp

Lines changed: 79 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
*
1818
*/
1919

20-
#include "cdrom_drive.h"
20+
#include "cdrom_drive.h"
2121

2222
#if defined(GG_ENABLE_PHYSICAL_CDROM) && defined(_WIN32)
2323

@@ -28,6 +28,7 @@
2828
#include <ntddscsi.h>
2929

3030
#define CDROM_DRIVE_RAW_SECTOR_SIZE 2352
31+
#define CDROM_DRIVE_DATA_SECTOR_SIZE 2048
3132
#define CDROM_DRIVE_SENSE_SIZE 32
3233
#define CDROM_DRIVE_TIMEOUT_SECONDS 20
3334
#define SCSI_READ_CD_EXPECTED_TYPE_CDDA 0x04
@@ -50,6 +51,42 @@ struct GeargrafxCdRomSetSpeed
5051
ULONG rotation_control;
5152
};
5253

54+
static bool windows_error_is_media_unavailable(DWORD error);
55+
static bool scsi_sense_is_media_unavailable(const UCHAR* sense);
56+
57+
static void init_scsi_read_request(ScsiPassThroughDirectWithSense& request, u8* buffer, ULONG length, UCHAR cdb_length)
58+
{
59+
memset(&request, 0, sizeof(request));
60+
request.pass_through.Length = sizeof(SCSI_PASS_THROUGH_DIRECT);
61+
request.pass_through.CdbLength = cdb_length;
62+
request.pass_through.SenseInfoLength = CDROM_DRIVE_SENSE_SIZE;
63+
request.pass_through.DataIn = SCSI_IOCTL_DATA_IN;
64+
request.pass_through.DataTransferLength = length;
65+
request.pass_through.TimeOutValue = CDROM_DRIVE_TIMEOUT_SECONDS;
66+
request.pass_through.DataBuffer = buffer;
67+
request.pass_through.SenseInfoOffset = offsetof(ScsiPassThroughDirectWithSense, sense);
68+
}
69+
70+
static bool execute_scsi_read(HANDLE file, ScsiPassThroughDirectWithSense& request, ULONG expected_length, bool* media_unavailable)
71+
{
72+
DWORD bytes_returned = 0;
73+
if (!DeviceIoControl(file, IOCTL_SCSI_PASS_THROUGH_DIRECT, &request, sizeof(request), &request, sizeof(request), &bytes_returned, NULL))
74+
{
75+
if (IsValidPointer(media_unavailable))
76+
*media_unavailable = windows_error_is_media_unavailable(GetLastError());
77+
return false;
78+
}
79+
80+
if (request.pass_through.ScsiStatus != 0)
81+
{
82+
if (IsValidPointer(media_unavailable))
83+
*media_unavailable = scsi_sense_is_media_unavailable(request.sense);
84+
return false;
85+
}
86+
87+
return request.pass_through.DataTransferLength == expected_length;
88+
}
89+
5390
static void init_drive_info(CdRomDriveInfo& info)
5491
{
5592
info.id[0] = 0;
@@ -197,16 +234,9 @@ static bool read_cd_spti(HANDLE file, u32 lba, u32 sector_count, u8* buffer, boo
197234
if (!IsValidPointer(buffer) || (sector_count == 0))
198235
return false;
199236

200-
ScsiPassThroughDirectWithSense request = {};
201-
request.pass_through.Length = sizeof(SCSI_PASS_THROUGH_DIRECT);
202-
request.pass_through.CdbLength = 12;
203-
request.pass_through.SenseInfoLength = CDROM_DRIVE_SENSE_SIZE;
204-
request.pass_through.DataIn = SCSI_IOCTL_DATA_IN;
205237
ULONG expected_length = sector_count * CDROM_DRIVE_RAW_SECTOR_SIZE;
206-
request.pass_through.DataTransferLength = expected_length;
207-
request.pass_through.TimeOutValue = CDROM_DRIVE_TIMEOUT_SECONDS;
208-
request.pass_through.DataBuffer = buffer;
209-
request.pass_through.SenseInfoOffset = offsetof(ScsiPassThroughDirectWithSense, sense);
238+
ScsiPassThroughDirectWithSense request;
239+
init_scsi_read_request(request, buffer, expected_length, 12);
210240
request.pass_through.Cdb[0] = 0xBE;
211241
request.pass_through.Cdb[1] = audio ? SCSI_READ_CD_EXPECTED_TYPE_CDDA : SCSI_READ_CD_EXPECTED_TYPE_MODE1;
212242
request.pass_through.Cdb[2] = (UCHAR)(lba >> 24);
@@ -218,25 +248,27 @@ static bool read_cd_spti(HANDLE file, u32 lba, u32 sector_count, u8* buffer, boo
218248
request.pass_through.Cdb[8] = (UCHAR)sector_count;
219249
request.pass_through.Cdb[9] = audio ? SCSI_READ_CD_READ_CDDA : SCSI_READ_CD_READ_MODE1_RAW;
220250

221-
DWORD bytes_returned = 0;
222-
if (!DeviceIoControl(file, IOCTL_SCSI_PASS_THROUGH_DIRECT, &request, sizeof(request), &request, sizeof(request), &bytes_returned, NULL))
223-
{
224-
if (IsValidPointer(media_unavailable))
225-
*media_unavailable = windows_error_is_media_unavailable(GetLastError());
226-
return false;
227-
}
251+
return execute_scsi_read(file, request, expected_length, media_unavailable);
252+
}
228253

229-
if (request.pass_through.ScsiStatus != 0)
230-
{
231-
if (IsValidPointer(media_unavailable))
232-
*media_unavailable = scsi_sense_is_media_unavailable(request.sense);
233-
return false;
234-
}
254+
static bool read_data_spti(HANDLE file, u32 lba, u8* buffer, bool* media_unavailable)
255+
{
256+
if (IsValidPointer(media_unavailable))
257+
*media_unavailable = false;
235258

236-
if (request.pass_through.DataTransferLength != expected_length)
259+
if (!IsValidPointer(buffer))
237260
return false;
238261

239-
return true;
262+
ScsiPassThroughDirectWithSense request;
263+
init_scsi_read_request(request, buffer, CDROM_DRIVE_DATA_SECTOR_SIZE, 10);
264+
request.pass_through.Cdb[0] = 0x28;
265+
request.pass_through.Cdb[2] = (UCHAR)(lba >> 24);
266+
request.pass_through.Cdb[3] = (UCHAR)(lba >> 16);
267+
request.pass_through.Cdb[4] = (UCHAR)(lba >> 8);
268+
request.pass_through.Cdb[5] = (UCHAR)lba;
269+
request.pass_through.Cdb[8] = 1;
270+
271+
return execute_scsi_read(file, request, CDROM_DRIVE_DATA_SECTOR_SIZE, media_unavailable);
240272
}
241273

242274
static HANDLE open_device_handle(const char* device_id, bool read_write)
@@ -496,6 +528,28 @@ bool CdRomDrive::ReadRawSectors2352(u32 lba, u32 sector_count, u8* buffer, bool
496528
return true;
497529
}
498530

531+
bool CdRomDrive::ReadDataSector2048(u32 lba, u8* buffer, bool report_errors)
532+
{
533+
if (!IsOpen() || !IsValidPointer(buffer))
534+
return false;
535+
536+
bool media_unavailable = false;
537+
if (read_data_spti(m_file, lba, buffer, &media_unavailable))
538+
return true;
539+
540+
if (media_unavailable)
541+
{
542+
if (report_errors)
543+
Error("Physical CD-ROM media unavailable for %s at LBA %u", m_device_id, lba);
544+
return false;
545+
}
546+
547+
if (report_errors)
548+
Error("SCSI READ(10) failed for %s at LBA %u", m_device_id, lba);
549+
550+
return false;
551+
}
552+
499553
bool CdRomDrive::SetSpeed(u16 speed)
500554
{
501555
if (!IsOpen())

0 commit comments

Comments
 (0)