CDImageDevice: Verify MSF of SUBQ before use

This commit is contained in:
Stenzek 2024-03-23 13:05:54 +10:00
parent 4d5c8cb134
commit 084a76afa0
No known key found for this signature in database

View file

@ -4,6 +4,9 @@
#include "assert.h" #include "assert.h"
#include "cd_image.h" #include "cd_image.h"
// TODO: Remove me..
#include "core/host.h"
#include "common/assert.h" #include "common/assert.h"
#include "common/error.h" #include "common/error.h"
#include "common/log.h" #include "common/log.h"
@ -91,7 +94,8 @@ enum class SCSIReadMode : u8
} }
} }
[[maybe_unused]] static bool VerifySCSIReadData(std::span<const u8> buffer, SCSIReadMode mode) [[maybe_unused]] static bool VerifySCSIReadData(std::span<const u8> buffer, SCSIReadMode mode,
CDImage::LBA expected_sector)
{ {
const u32 expected_size = SCSIReadCommandOutputSize(mode); const u32 expected_size = SCSIReadCommandOutputSize(mode);
if (buffer.size() != expected_size) if (buffer.size() != expected_size)
@ -100,6 +104,8 @@ enum class SCSIReadMode : u8
return false; return false;
} }
const CDImage::Position expected_pos = CDImage::Position::FromLBA(expected_sector);
if (mode == SCSIReadMode::Full) if (mode == SCSIReadMode::Full)
{ {
// Validate subcode. // Validate subcode.
@ -107,6 +113,11 @@ enum class SCSIReadMode : u8
CDImage::SubChannelQ subq; CDImage::SubChannelQ subq;
CDImage::DeinterleaveSubcode(buffer.data() + CDImage::RAW_SECTOR_SIZE, deinterleaved_subcode); CDImage::DeinterleaveSubcode(buffer.data() + CDImage::RAW_SECTOR_SIZE, deinterleaved_subcode);
std::memcpy(&subq, &deinterleaved_subcode[CDImage::SUBCHANNEL_BYTES_PER_FRAME], sizeof(subq)); std::memcpy(&subq, &deinterleaved_subcode[CDImage::SUBCHANNEL_BYTES_PER_FRAME], sizeof(subq));
Log_DevFmt("SCSI full subcode read returned [{}] for {:02d}:{:02d}:{:02d}",
StringUtil::EncodeHex(subq.data.data(), static_cast<int>(subq.data.size())), expected_pos.minute,
expected_pos.second, expected_pos.frame);
if (!subq.IsCRCValid()) if (!subq.IsCRCValid())
{ {
Log_WarningFmt("SCSI full subcode read returned invalid SubQ CRC (got {:02X} expected {:02X})", subq.crc, Log_WarningFmt("SCSI full subcode read returned invalid SubQ CRC (got {:02X} expected {:02X})", subq.crc,
@ -114,12 +125,27 @@ enum class SCSIReadMode : u8
return false; return false;
} }
const CDImage::Position got_pos =
CDImage::Position::FromBCD(subq.absolute_minute_bcd, subq.absolute_second_bcd, subq.absolute_frame_bcd);
if (expected_pos != got_pos)
{
Log_WarningFmt(
"SCSI full subcode read returned invalid MSF (got {:02x}:{:02x}:{:02x}, expected {:02d}:{:02d}:{:02d})",
subq.absolute_minute_bcd, subq.absolute_second_bcd, subq.absolute_frame_bcd, expected_pos.minute,
expected_pos.second, expected_pos.frame);
return false;
}
return true; return true;
} }
else if (mode == SCSIReadMode::SubQOnly) else if (mode == SCSIReadMode::SubQOnly)
{ {
CDImage::SubChannelQ subq; CDImage::SubChannelQ subq;
std::memcpy(&subq, buffer.data() + CDImage::RAW_SECTOR_SIZE, sizeof(subq)); std::memcpy(&subq, buffer.data() + CDImage::RAW_SECTOR_SIZE, sizeof(subq));
Log_DevFmt("SCSI subq read returned [{}] for {:02d}:{:02d}:{:02d}",
StringUtil::EncodeHex(subq.data.data(), static_cast<int>(subq.data.size())), expected_pos.minute,
expected_pos.second, expected_pos.frame);
if (!subq.IsCRCValid()) if (!subq.IsCRCValid())
{ {
Log_WarningFmt("SCSI subq read returned invalid SubQ CRC (got {:02X} expected {:02X})", subq.crc, Log_WarningFmt("SCSI subq read returned invalid SubQ CRC (got {:02X} expected {:02X})", subq.crc,
@ -127,6 +153,16 @@ enum class SCSIReadMode : u8
return false; return false;
} }
const CDImage::Position got_pos =
CDImage::Position::FromBCD(subq.absolute_minute_bcd, subq.absolute_second_bcd, subq.absolute_frame_bcd);
if (expected_pos != got_pos)
{
Log_WarningFmt("SCSI subq read returned invalid MSF (got {:02x}:{:02x}:{:02x}, expected {:02d}:{:02d}:{:02d})",
subq.absolute_minute_bcd, subq.absolute_second_bcd, subq.absolute_frame_bcd, expected_pos.minute,
expected_pos.second, expected_pos.frame);
return false;
}
return true; return true;
} }
else // if (mode == SCSIReadMode::None || mode == SCSIReadMode::Raw) else // if (mode == SCSIReadMode::None || mode == SCSIReadMode::Raw)
@ -136,6 +172,11 @@ enum class SCSIReadMode : u8
} }
} }
[[maybe_unused]] static bool ShouldTryReadingSubcode()
{
return !Host::GetBaseBoolSettingValue("CDROM", "IgnoreHostSubcode", false);
}
#if defined(_WIN32) #if defined(_WIN32)
// The include order here is critical. // The include order here is critical.
@ -522,13 +563,15 @@ 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 LBA track_1_lba = static_cast<LBA>(m_indices[m_tracks[0].first_index].file_offset);
const LBA track_1_subq_lba = track_1_lba + FRAMES_PER_SECOND * 2;
const bool check_subcode = ShouldTryReadingSubcode();
Log_DevPrint("Trying raw reads..."); Log_DevPrint("Trying raw reads...");
if (DoRawRead(track_1_lba)) if (check_subcode && 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)),
SCSIReadMode::Full)) SCSIReadMode::Full, track_1_subq_lba))
{ {
Log_VerbosePrint("Using raw reads with full subcode"); Log_VerbosePrint("Using raw reads with full subcode");
m_scsi_read_mode = SCSIReadMode::None; m_scsi_read_mode = SCSIReadMode::None;
@ -540,9 +583,9 @@ bool CDImageDeviceWin32::DetermineReadMode(bool try_sptd)
std::optional<u32> transfer_size; std::optional<u32> transfer_size;
Log_DevPrint("Trying SCSI read with full subcode..."); Log_DevPrint("Trying SCSI read with full subcode...");
if ((transfer_size = DoSCSIRead(track_1_lba, SCSIReadMode::Full)).has_value()) 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)) if (VerifySCSIReadData(std::span<u8>(m_buffer.data(), transfer_size.value()), SCSIReadMode::Full, track_1_subq_lba))
{ {
Log_VerbosePrint("Using SCSI reads with subcode"); Log_VerbosePrint("Using SCSI reads with subcode");
m_scsi_read_mode = SCSIReadMode::Full; m_scsi_read_mode = SCSIReadMode::Full;
@ -552,9 +595,10 @@ bool CDImageDeviceWin32::DetermineReadMode(bool try_sptd)
} }
Log_WarningPrint("Full subcode failed, trying SCSI read with only subq..."); Log_WarningPrint("Full subcode failed, trying SCSI read with only subq...");
if ((transfer_size = DoSCSIRead(track_1_lba, SCSIReadMode::SubQOnly)).has_value()) 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)) 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"); Log_VerbosePrint("Using SCSI reads with subq only");
m_scsi_read_mode = SCSIReadMode::SubQOnly; m_scsi_read_mode = SCSIReadMode::SubQOnly;
@ -567,7 +611,7 @@ bool CDImageDeviceWin32::DetermineReadMode(bool try_sptd)
Log_WarningPrint("Subq only failed failed, trying 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 ((transfer_size = DoSCSIRead(track_1_lba, SCSIReadMode::Raw)).has_value())
{ {
if (VerifySCSIReadData(std::span<u8>(m_buffer.data(), transfer_size.value()), SCSIReadMode::Raw)) 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"); Log_WarningPrint("Using SCSI raw reads, libcrypt games will not run correctly");
m_scsi_read_mode = SCSIReadMode::Raw; m_scsi_read_mode = SCSIReadMode::Raw;
@ -990,12 +1034,14 @@ bool CDImageDeviceLinux::ReadSectorToBuffer(LBA lba)
bool CDImageDeviceLinux::DetermineReadMode(Error* error) bool CDImageDeviceLinux::DetermineReadMode(Error* error)
{ {
const LBA track_1_lba = static_cast<LBA>(m_indices[m_tracks[0].first_index].file_offset); const LBA track_1_lba = static_cast<LBA>(m_indices[m_tracks[0].first_index].file_offset);
const LBA track_1_subq_lba = track_1_lba + FRAMES_PER_SECOND * 2;
const bool check_subcode = ShouldTryReadingSubcode();
std::optional<u32> transfer_size; std::optional<u32> transfer_size;
Log_DevPrint("Trying SCSI read with full subcode..."); Log_DevPrint("Trying SCSI read with full subcode...");
if ((transfer_size = DoSCSIRead(track_1_lba, SCSIReadMode::Full)).has_value()) 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)) if (VerifySCSIReadData(std::span<u8>(m_buffer.data(), transfer_size.value()), SCSIReadMode::Full, track_1_subq_lba))
{ {
Log_VerbosePrint("Using SCSI reads with subcode"); Log_VerbosePrint("Using SCSI reads with subcode");
m_scsi_read_mode = SCSIReadMode::Full; m_scsi_read_mode = SCSIReadMode::Full;
@ -1004,9 +1050,10 @@ bool CDImageDeviceLinux::DetermineReadMode(Error* error)
} }
Log_WarningPrint("Full subcode failed, trying SCSI read with only subq..."); Log_WarningPrint("Full subcode failed, trying SCSI read with only subq...");
if ((transfer_size = DoSCSIRead(track_1_lba, SCSIReadMode::SubQOnly)).has_value()) 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)) 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"); Log_VerbosePrint("Using SCSI reads with subq only");
m_scsi_read_mode = SCSIReadMode::SubQOnly; m_scsi_read_mode = SCSIReadMode::SubQOnly;
@ -1026,7 +1073,7 @@ bool CDImageDeviceLinux::DetermineReadMode(Error* error)
Log_WarningPrint("CDROMREADRAW failed, trying SCSI without subcode..."); Log_WarningPrint("CDROMREADRAW failed, trying SCSI without subcode...");
if ((transfer_size = DoSCSIRead(track_1_lba, SCSIReadMode::Raw)).has_value()) if ((transfer_size = DoSCSIRead(track_1_lba, SCSIReadMode::Raw)).has_value())
{ {
if (VerifySCSIReadData(std::span<u8>(m_buffer.data(), transfer_size.value()), SCSIReadMode::Raw)) 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"); Log_WarningPrint("Using SCSI raw reads, libcrypt games will not run correctly");
m_scsi_read_mode = SCSIReadMode::Raw; m_scsi_read_mode = SCSIReadMode::Raw;