diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index 03e1c12cc..b732f8194 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -15,6 +15,7 @@ add_library(common cd_image_chd.cpp cd_image_hasher.cpp cd_image_hasher.h + cd_image_memory.cpp cd_subchannel_replacement.cpp cd_subchannel_replacement.h cd_xa.cpp diff --git a/src/common/cd_image.cpp b/src/common/cd_image.cpp index 1f682875c..f64e764b3 100644 --- a/src/common/cd_image.cpp +++ b/src/common/cd_image.cpp @@ -94,6 +94,17 @@ CDImage::LBA CDImage::GetTrackIndexLength(u8 track, u8 index) const return 0; } +const CDImage::CDImage::Track& CDImage::GetTrack(u32 track) const +{ + Assert(track > 0 && track <= m_tracks.size()); + return m_tracks[track - 1]; +} + +const CDImage::CDImage::Index& CDImage::GetIndex(u32 i) const +{ + return m_indices[i]; +} + bool CDImage::Seek(LBA lba) { const Index* new_index; diff --git a/src/common/cd_image.h b/src/common/cd_image.h index 43702ff62..1977cc2e9 100644 --- a/src/common/cd_image.h +++ b/src/common/cd_image.h @@ -1,5 +1,6 @@ #pragma once #include "bitfield.h" +#include "progress_callback.h" #include "types.h" #include #include @@ -161,57 +162,6 @@ public: }; static_assert(sizeof(SubChannelQ) == SUBCHANNEL_BYTES_PER_FRAME, "SubChannelQ is correct size"); - // Helper functions. - static u32 GetBytesPerSector(TrackMode mode); - - // Opening disc image. - static std::unique_ptr Open(const char* filename); - static std::unique_ptr OpenBinImage(const char* filename); - static std::unique_ptr OpenCueSheetImage(const char* filename); - static std::unique_ptr OpenCHDImage(const char* filename); - - // Accessors. - const std::string& GetFileName() const { return m_filename; } - LBA GetPositionOnDisc() const { return m_position_on_disc; } - Position GetMSFPositionOnDisc() const { return Position::FromLBA(m_position_on_disc); } - LBA GetPositionInTrack() const { return m_position_in_track; } - Position GetMSFPositionInTrack() const { return Position::FromLBA(m_position_in_track); } - LBA GetLBACount() const { return m_lba_count; } - u32 GetIndexNumber() const { return m_current_index->index_number; } - u32 GetTrackNumber() const { return m_current_index->track_number; } - u32 GetTrackCount() const { return static_cast(m_tracks.size()); } - LBA GetTrackStartPosition(u8 track) const; - Position GetTrackStartMSFPosition(u8 track) const; - LBA GetTrackLength(u8 track) const; - Position GetTrackMSFLength(u8 track) const; - TrackMode GetTrackMode(u8 track) const; - LBA GetTrackIndexPosition(u8 track, u8 index) const; - LBA GetTrackIndexLength(u8 track, u8 index) const; - u32 GetFirstTrackNumber() const { return m_tracks.front().track_number; } - u32 GetLastTrackNumber() const { return m_tracks.back().track_number; } - - // Seek to data LBA. - bool Seek(LBA lba); - - // Seek to disc position (MSF). - bool Seek(const Position& pos); - - // Seek to track and position. - bool Seek(u32 track_number, const Position& pos_in_track); - - // Seek to track and LBA. - bool Seek(u32 track_number, LBA lba); - - // Read from the current LBA. Returns the number of sectors read. - u32 Read(ReadMode read_mode, u32 sector_count, void* buffer); - - // Read a single raw sector from the current LBA. - bool ReadRawSector(void* buffer); - - // Reads sub-channel Q for the current LBA. - virtual bool ReadSubChannelQ(SubChannelQ* subq); - -protected: struct Track { u32 track_number; @@ -237,9 +187,65 @@ protected: bool is_pregap; }; + // Helper functions. + static u32 GetBytesPerSector(TrackMode mode); + + // Opening disc image. + static std::unique_ptr Open(const char* filename); + static std::unique_ptr OpenBinImage(const char* filename); + static std::unique_ptr OpenCueSheetImage(const char* filename); + static std::unique_ptr OpenCHDImage(const char* filename); + static std::unique_ptr + CreateMemoryImage(CDImage* image, ProgressCallback* progress = ProgressCallback::NullProgressCallback); + + // Accessors. + const std::string& GetFileName() const { return m_filename; } + LBA GetPositionOnDisc() const { return m_position_on_disc; } + Position GetMSFPositionOnDisc() const { return Position::FromLBA(m_position_on_disc); } + LBA GetPositionInTrack() const { return m_position_in_track; } + Position GetMSFPositionInTrack() const { return Position::FromLBA(m_position_in_track); } + LBA GetLBACount() const { return m_lba_count; } + u32 GetIndexNumber() const { return m_current_index->index_number; } + u32 GetTrackNumber() const { return m_current_index->track_number; } + u32 GetTrackCount() const { return static_cast(m_tracks.size()); } + LBA GetTrackStartPosition(u8 track) const; + Position GetTrackStartMSFPosition(u8 track) const; + LBA GetTrackLength(u8 track) const; + Position GetTrackMSFLength(u8 track) const; + TrackMode GetTrackMode(u8 track) const; + LBA GetTrackIndexPosition(u8 track, u8 index) const; + LBA GetTrackIndexLength(u8 track, u8 index) const; + u32 GetFirstTrackNumber() const { return m_tracks.front().track_number; } + u32 GetLastTrackNumber() const { return m_tracks.back().track_number; } + u32 GetIndexCount() const { return static_cast(m_indices.size()); } + const Track& GetTrack(u32 track) const; + const Index& GetIndex(u32 i) const; + + // Seek to data LBA. + bool Seek(LBA lba); + + // Seek to disc position (MSF). + bool Seek(const Position& pos); + + // Seek to track and position. + bool Seek(u32 track_number, const Position& pos_in_track); + + // Seek to track and LBA. + bool Seek(u32 track_number, LBA lba); + + // Read from the current LBA. Returns the number of sectors read. + u32 Read(ReadMode read_mode, u32 sector_count, void* buffer); + + // Read a single raw sector from the current LBA. + bool ReadRawSector(void* buffer); + + // Reads sub-channel Q for the current LBA. + virtual bool ReadSubChannelQ(SubChannelQ* subq); + // Reads a single sector from an index. virtual bool ReadSectorFromIndex(void* buffer, const Index& index, LBA lba_in_index) = 0; +protected: const Index* GetIndexForDiscPosition(LBA pos); const Index* GetIndexForTrackPosition(u32 track_number, LBA track_pos); diff --git a/src/common/cd_image_memory.cpp b/src/common/cd_image_memory.cpp new file mode 100644 index 000000000..54a85a692 --- /dev/null +++ b/src/common/cd_image_memory.cpp @@ -0,0 +1,160 @@ +#include "assert.h" +#include "cd_image.h" +#include "cd_subchannel_replacement.h" +#include "file_system.h" +#include "log.h" +#include +#include +#include +#include +Log_SetChannel(CDImageMemory); + +class CDImageMemory : public CDImage +{ +public: + CDImageMemory(); + ~CDImageMemory() override; + + bool CopyImage(CDImage* image, ProgressCallback* progress); + + bool ReadSubChannelQ(SubChannelQ* subq) override; + +protected: + bool ReadSectorFromIndex(void* buffer, const Index& index, LBA lba_in_index) override; + +private: + u8* m_memory = nullptr; + u32 m_memory_sectors = 0; + CDSubChannelReplacement m_sbi; +}; + +CDImageMemory::CDImageMemory() = default; + +CDImageMemory::~CDImageMemory() +{ + if (m_memory) + std::free(m_memory); +} + +bool CDImageMemory::CopyImage(CDImage* image, ProgressCallback* progress) +{ + // figure out the total number of sectors (not including blank pregaps) + m_memory_sectors = 0; + for (u32 i = 0; i < image->GetIndexCount(); i++) + { + const Index& index = image->GetIndex(i); + if (index.file_sector_size > 0) + m_memory_sectors += image->GetIndex(i).length; + } + + if ((static_cast(RAW_SECTOR_SIZE) * static_cast(m_memory_sectors)) >= + static_cast(std::numeric_limits::max())) + { + progress->DisplayFormattedModalError("Insufficient address space"); + return false; + } + + progress->SetFormattedStatusText("Allocating memory for %u sectors...", m_memory_sectors); + + m_memory = + static_cast(std::malloc(static_cast(RAW_SECTOR_SIZE) * static_cast(m_memory_sectors))); + if (!m_memory) + { + progress->DisplayFormattedModalError("Failed to allocate memory for %llu sectors", m_memory_sectors); + return false; + } + + progress->SetStatusText("Preloading CD image to RAM..."); + progress->SetProgressRange(m_memory_sectors); + progress->SetProgressValue(0); + + u8* memory_ptr = m_memory; + u32 sectors_read = 0; + for (u32 i = 0; i < image->GetIndexCount(); i++) + { + const Index& index = image->GetIndex(i); + if (index.file_sector_size == 0) + continue; + + for (u32 lba = 0; lba < index.length; lba++) + { + if (!image->ReadSectorFromIndex(memory_ptr, index, lba)) + { + Log_ErrorPrintf("Failed to read LBA %u in index %u", lba, index); + return false; + } + + progress->SetProgressValue(sectors_read); + memory_ptr += RAW_SECTOR_SIZE; + sectors_read++; + } + } + + for (u32 i = 1; i <= image->GetTrackCount(); i++) + m_tracks.push_back(image->GetTrack(i)); + + u32 current_offset = 0; + for (u32 i = 0; i < image->GetIndexCount(); i++) + { + Index new_index = image->GetIndex(i); + new_index.file_index = 0; + if (new_index.file_sector_size > 0) + { + new_index.file_offset = current_offset; + current_offset += new_index.length; + } + m_indices.push_back(new_index); + } + + Assert(current_offset == m_memory_sectors); + m_filename = image->GetFileName(); + m_lba_count = image->GetLBACount(); + + if (!image->Seek(0)) + { + progress->ModalError("Failed to seek to start of image for subq read"); + return false; + } + + progress->SetStatusText("Looking for invalid subchannel data..."); + + CDImage::SubChannelQ subq; + for (LBA lba = 0; lba < m_lba_count; lba++) + { + if (ReadSubChannelQ(&subq) && !subq.IsCRCValid()) + m_sbi.AddReplacementSubChannelQ(lba, subq); + } + + return Seek(1, Position{0, 0, 0}); +} + +bool CDImageMemory::ReadSubChannelQ(SubChannelQ* subq) +{ + if (m_sbi.GetReplacementSubChannelQ(m_position_on_disc, subq)) + return true; + + return CDImage::ReadSubChannelQ(subq); +} + +bool CDImageMemory::ReadSectorFromIndex(void* buffer, const Index& index, LBA lba_in_index) +{ + DebugAssert(index.file_index == 0); + + const u64 sector_number = index.file_offset + lba_in_index; + if (sector_number >= m_memory_sectors) + return false; + + const size_t file_offset = static_cast(sector_number) * static_cast(RAW_SECTOR_SIZE); + std::memcpy(buffer, &m_memory[file_offset], RAW_SECTOR_SIZE); + return true; +} + +std::unique_ptr +CDImage::CreateMemoryImage(CDImage* image, ProgressCallback* progress /* = ProgressCallback::NullProgressCallback */) +{ + std::unique_ptr memory_image = std::make_unique(); + if (!memory_image->CopyImage(image, progress)) + return {}; + + return memory_image; +} diff --git a/src/common/common.vcxproj b/src/common/common.vcxproj index 771a21083..ac71178a3 100644 --- a/src/common/common.vcxproj +++ b/src/common/common.vcxproj @@ -101,6 +101,7 @@ + diff --git a/src/common/common.vcxproj.filters b/src/common/common.vcxproj.filters index 442f5ff09..20566df9f 100644 --- a/src/common/common.vcxproj.filters +++ b/src/common/common.vcxproj.filters @@ -190,6 +190,7 @@ vulkan +