CDImageDevice: Fix reading of pure audio CDs

This commit is contained in:
Stenzek 2024-03-25 23:15:35 +10:00
parent 83ebad1129
commit 3e8dd2c374
No known key found for this signature in database

View file

@ -232,7 +232,6 @@ private:
bool m_has_valid_subcode = false; bool m_has_valid_subcode = false;
std::array<u8, CD_RAW_SECTOR_WITH_SUBCODE_SIZE> m_buffer; std::array<u8, CD_RAW_SECTOR_WITH_SUBCODE_SIZE> m_buffer;
std::array<u8, SUBCHANNEL_BYTES_PER_FRAME> m_subq;
}; };
} // namespace } // namespace
@ -339,7 +338,7 @@ bool CDImageDeviceWin32::Open(const char* filename, Error* error)
const TrackMode track_mode = control.data ? CDImage::TrackMode::Mode2Raw : CDImage::TrackMode::Audio; const TrackMode track_mode = control.data ? CDImage::TrackMode::Mode2Raw : CDImage::TrackMode::Audio;
// TODO: How the hell do we handle pregaps here? // TODO: How the hell do we handle pregaps here?
const u32 pregap_frames = (control.data && track_index == 0) ? 150 : 0; const u32 pregap_frames = (track_index == 0) ? (FRAMES_PER_SECOND * 2) : 0;
if (pregap_frames > 0) if (pregap_frames > 0)
{ {
Index pregap_index = {}; Index pregap_index = {};
@ -424,9 +423,22 @@ bool CDImageDeviceWin32::ReadSubChannelQ(SubChannelQ* subq, const Index& index,
if (m_current_lba != offset && !ReadSectorToBuffer(offset)) if (m_current_lba != offset && !ReadSectorToBuffer(offset))
return false; return false;
// P, Q, ... if (m_scsi_read_mode == SCSIReadMode::SubQOnly)
std::memcpy(subq->data.data(), m_subq.data(), SUBCHANNEL_BYTES_PER_FRAME); {
return true; // copy out subq
std::memcpy(subq->data.data(), m_buffer.data() + RAW_SECTOR_SIZE, SUBCHANNEL_BYTES_PER_FRAME);
return true;
}
else // if (m_scsi_read_mode == SCSIReadMode::Full || m_scsi_read_mode == SCSIReadMode::None)
{
// need to deinterleave the subcode
u8 deinterleaved_subcode[ALL_SUBCODE_SIZE];
DeinterleaveSubcode(m_buffer.data() + RAW_SECTOR_SIZE, deinterleaved_subcode);
// P, Q, ...
std::memcpy(subq->data.data(), deinterleaved_subcode + SUBCHANNEL_BYTES_PER_FRAME, SUBCHANNEL_BYTES_PER_FRAME);
return true;
}
} }
bool CDImageDeviceWin32::HasNonStandardSubchannel() const bool CDImageDeviceWin32::HasNonStandardSubchannel() const
@ -535,19 +547,6 @@ bool CDImageDeviceWin32::ReadSectorToBuffer(LBA lba)
Log_ErrorFmt("Read of LBA {} failed: only got {} of {} bytes", lba, size.value(), expected_size); Log_ErrorFmt("Read of LBA {} failed: only got {} of {} bytes", lba, size.value(), expected_size);
return false; return false;
} }
if (m_scsi_read_mode == SCSIReadMode::Full)
{
// need to deinterleave the subcode
u8 deinterleaved_subcode[ALL_SUBCODE_SIZE];
DeinterleaveSubcode(m_buffer.data() + RAW_SECTOR_SIZE, deinterleaved_subcode);
std::memcpy(&m_subq, deinterleaved_subcode + SUBCHANNEL_BYTES_PER_FRAME, sizeof(m_subq)); // P,Q
}
else if (m_scsi_read_mode == SCSIReadMode::SubQOnly)
{
// copy out subq
std::memcpy(&m_subq, m_buffer.data() + RAW_SECTOR_SIZE, sizeof(m_subq));
}
} }
else else
{ {
@ -562,12 +561,58 @@ bool CDImageDeviceWin32::ReadSectorToBuffer(LBA lba)
bool CDImageDeviceWin32::DetermineReadMode(bool try_sptd) bool CDImageDeviceWin32::DetermineReadMode(bool try_sptd)
{ {
// Prefer raw reads if we can use them // Prefer raw reads if we can use them
const LBA track_1_lba = static_cast<LBA>(m_indices[m_tracks[0].first_index].file_offset); const Index& first_index = m_indices[m_tracks[0].first_index];
const LBA track_1_subq_lba = track_1_lba + FRAMES_PER_SECOND * 2; const LBA track_1_lba = static_cast<LBA>(first_index.file_offset);
const LBA track_1_subq_lba = first_index.start_lba_on_disc;
const bool check_subcode = ShouldTryReadingSubcode(); const bool check_subcode = ShouldTryReadingSubcode();
Log_DevPrint("Trying raw reads..."); if (try_sptd)
if (check_subcode && DoRawRead(track_1_lba)) {
std::optional<u32> transfer_size;
Log_DevPrint("Trying SCSI read with full subcode...");
if (check_subcode && (transfer_size = DoSCSIRead(track_1_lba, SCSIReadMode::Full)).has_value())
{
if (VerifySCSIReadData(std::span<u8>(m_buffer.data(), transfer_size.value()), SCSIReadMode::Full,
track_1_subq_lba))
{
Log_VerbosePrint("Using SCSI reads with subcode");
m_scsi_read_mode = SCSIReadMode::Full;
m_has_valid_subcode = true;
return true;
}
}
Log_WarningPrint("Full subcode failed, trying SCSI read with only subq...");
if (check_subcode && (transfer_size = DoSCSIRead(track_1_lba, SCSIReadMode::SubQOnly)).has_value())
{
if (VerifySCSIReadData(std::span<u8>(m_buffer.data(), transfer_size.value()), SCSIReadMode::SubQOnly,
track_1_subq_lba))
{
Log_VerbosePrint("Using SCSI reads with subq only");
m_scsi_read_mode = SCSIReadMode::SubQOnly;
m_has_valid_subcode = true;
return true;
}
}
// As a last ditch effort, try SCSI without subcode.
Log_WarningPrint("Subq only failed failed, trying SCSI without subcode...");
if ((transfer_size = DoSCSIRead(track_1_lba, SCSIReadMode::Raw)).has_value())
{
if (VerifySCSIReadData(std::span<u8>(m_buffer.data(), transfer_size.value()), SCSIReadMode::Raw,
track_1_subq_lba))
{
Log_WarningPrint("Using SCSI raw reads, libcrypt games will not run correctly");
m_scsi_read_mode = SCSIReadMode::Raw;
m_has_valid_subcode = false;
return true;
}
}
}
Log_WarningPrint("SCSI reads failed, trying raw read...");
if (DoRawRead(track_1_lba))
{ {
// verify subcode // verify subcode
if (VerifySCSIReadData(std::span<u8>(m_buffer.data(), SCSIReadCommandOutputSize(SCSIReadMode::Full)), if (VerifySCSIReadData(std::span<u8>(m_buffer.data(), SCSIReadCommandOutputSize(SCSIReadMode::Full)),
@ -578,51 +623,7 @@ bool CDImageDeviceWin32::DetermineReadMode(bool try_sptd)
m_has_valid_subcode = true; m_has_valid_subcode = true;
return true; return true;
} }
}
std::optional<u32> transfer_size;
Log_DevPrint("Trying SCSI read with full subcode...");
if (check_subcode && (transfer_size = DoSCSIRead(track_1_lba, SCSIReadMode::Full)).has_value())
{
if (VerifySCSIReadData(std::span<u8>(m_buffer.data(), transfer_size.value()), SCSIReadMode::Full, track_1_subq_lba))
{
Log_VerbosePrint("Using SCSI reads with subcode");
m_scsi_read_mode = SCSIReadMode::Full;
m_has_valid_subcode = true;
return true;
}
}
Log_WarningPrint("Full subcode failed, trying SCSI read with only subq...");
if (check_subcode && (transfer_size = DoSCSIRead(track_1_lba, SCSIReadMode::SubQOnly)).has_value())
{
if (VerifySCSIReadData(std::span<u8>(m_buffer.data(), transfer_size.value()), SCSIReadMode::SubQOnly,
track_1_subq_lba))
{
Log_VerbosePrint("Using SCSI reads with subq only");
m_scsi_read_mode = SCSIReadMode::SubQOnly;
m_has_valid_subcode = true;
return true;
}
}
// As a last ditch effort, try SCSI without subcode.
Log_WarningPrint("Subq only failed failed, trying SCSI without subcode...");
if ((transfer_size = DoSCSIRead(track_1_lba, SCSIReadMode::Raw)).has_value())
{
if (VerifySCSIReadData(std::span<u8>(m_buffer.data(), transfer_size.value()), SCSIReadMode::Raw, track_1_subq_lba))
{
Log_WarningPrint("Using SCSI raw reads, libcrypt games will not run correctly");
m_scsi_read_mode = SCSIReadMode::Raw;
m_has_valid_subcode = false;
return true;
}
}
Log_WarningPrint("Raw SCSI read failed, trying raw but ignoring subcode...");
if (DoRawRead(track_1_lba))
{
Log_WarningPrint("Using raw reads without subcode, libcrypt games will not run correctly"); Log_WarningPrint("Using raw reads without subcode, libcrypt games will not run correctly");
m_scsi_read_mode = SCSIReadMode::None; m_scsi_read_mode = SCSIReadMode::None;
m_has_valid_subcode = false; m_has_valid_subcode = false;
@ -726,7 +727,6 @@ private:
SCSIReadMode m_scsi_read_mode = SCSIReadMode::None; SCSIReadMode m_scsi_read_mode = SCSIReadMode::None;
std::array<u8, RAW_SECTOR_SIZE + ALL_SUBCODE_SIZE> m_buffer; std::array<u8, RAW_SECTOR_SIZE + ALL_SUBCODE_SIZE> m_buffer;
std::array<u8, SUBCHANNEL_BYTES_PER_FRAME> m_subq;
}; };
} // namespace } // namespace
@ -814,7 +814,7 @@ bool CDImageDeviceLinux::Open(const char* filename, Error* error)
const TrackMode track_mode = control.data ? CDImage::TrackMode::Mode2Raw : CDImage::TrackMode::Audio; const TrackMode track_mode = control.data ? CDImage::TrackMode::Mode2Raw : CDImage::TrackMode::Audio;
// TODO: How the hell do we handle pregaps here? // TODO: How the hell do we handle pregaps here?
const u32 pregap_frames = (control.data && track_index == 0) ? 150 : 0; const u32 pregap_frames = (track_index == 0) ? 150 : 0;
if (pregap_frames > 0) if (pregap_frames > 0)
{ {
Index pregap_index = {}; Index pregap_index = {};
@ -917,8 +917,22 @@ bool CDImageDeviceLinux::ReadSubChannelQ(SubChannelQ* subq, const Index& index,
if (m_current_lba != disc_lba && !ReadSectorToBuffer(disc_lba)) if (m_current_lba != disc_lba && !ReadSectorToBuffer(disc_lba))
return false; return false;
std::memcpy(subq->data.data(), m_subq.data(), SUBCHANNEL_BYTES_PER_FRAME); if (m_scsi_read_mode == SCSIReadMode::SubQOnly)
return true; {
// copy out subq
std::memcpy(subq->data.data(), m_buffer.data() + RAW_SECTOR_SIZE, SUBCHANNEL_BYTES_PER_FRAME);
return true;
}
else // if (m_scsi_read_mode == SCSIReadMode::Full)
{
// need to deinterleave the subcode
u8 deinterleaved_subcode[ALL_SUBCODE_SIZE];
DeinterleaveSubcode(m_buffer.data() + RAW_SECTOR_SIZE, deinterleaved_subcode);
// P, Q, ...
std::memcpy(subq->data.data(), deinterleaved_subcode + SUBCHANNEL_BYTES_PER_FRAME, SUBCHANNEL_BYTES_PER_FRAME);
return true;
}
} }
bool CDImageDeviceLinux::HasNonStandardSubchannel() const bool CDImageDeviceLinux::HasNonStandardSubchannel() const
@ -1007,19 +1021,6 @@ bool CDImageDeviceLinux::ReadSectorToBuffer(LBA lba)
Log_ErrorFmt("Read of LBA {} failed: only got {} of {} bytes", lba, size.value(), expected_size); Log_ErrorFmt("Read of LBA {} failed: only got {} of {} bytes", lba, size.value(), expected_size);
return false; return false;
} }
if (m_scsi_read_mode == SCSIReadMode::Full)
{
// need to deinterleave the subcode
u8 deinterleaved_subcode[ALL_SUBCODE_SIZE];
DeinterleaveSubcode(m_buffer.data() + RAW_SECTOR_SIZE, deinterleaved_subcode);
std::memcpy(&m_subq, deinterleaved_subcode + SUBCHANNEL_BYTES_PER_FRAME, sizeof(m_subq)); // P,Q
}
else if (m_scsi_read_mode == SCSIReadMode::SubQOnly)
{
// copy out subq
std::memcpy(&m_subq, m_buffer.data() + RAW_SECTOR_SIZE, sizeof(m_subq));
}
} }
else else
{ {