cd_image_memory.cpp (4521B)
1 // SPDX-FileCopyrightText: 2019-2023 Connor McLaughlin <stenzek@gmail.com> 2 // SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0) 3 4 #include "cd_image.h" 5 #include "cd_subchannel_replacement.h" 6 7 #include "common/assert.h" 8 #include "common/file_system.h" 9 #include "common/log.h" 10 #include "common/path.h" 11 12 #include <algorithm> 13 #include <cerrno> 14 15 Log_SetChannel(CDImageMemory); 16 17 namespace { 18 19 class CDImageMemory : public CDImage 20 { 21 public: 22 CDImageMemory(); 23 ~CDImageMemory() override; 24 25 bool CopyImage(CDImage* image, ProgressCallback* progress); 26 27 bool ReadSubChannelQ(SubChannelQ* subq, const Index& index, LBA lba_in_index) override; 28 bool HasNonStandardSubchannel() const override; 29 30 bool IsPrecached() const override; 31 32 protected: 33 bool ReadSectorFromIndex(void* buffer, const Index& index, LBA lba_in_index) override; 34 35 private: 36 u8* m_memory = nullptr; 37 u32 m_memory_sectors = 0; 38 CDSubChannelReplacement m_sbi; 39 }; 40 41 } // namespace 42 43 CDImageMemory::CDImageMemory() = default; 44 45 CDImageMemory::~CDImageMemory() 46 { 47 if (m_memory) 48 std::free(m_memory); 49 } 50 51 bool CDImageMemory::CopyImage(CDImage* image, ProgressCallback* progress) 52 { 53 // figure out the total number of sectors (not including blank pregaps) 54 m_memory_sectors = 0; 55 for (u32 i = 0; i < image->GetIndexCount(); i++) 56 { 57 const Index& index = image->GetIndex(i); 58 if (index.file_sector_size > 0) 59 m_memory_sectors += image->GetIndex(i).length; 60 } 61 62 if ((static_cast<u64>(RAW_SECTOR_SIZE) * static_cast<u64>(m_memory_sectors)) >= 63 static_cast<u64>(std::numeric_limits<size_t>::max())) 64 { 65 progress->ModalError("Insufficient address space"); 66 return false; 67 } 68 69 progress->FormatStatusText("Allocating memory for {} sectors...", m_memory_sectors); 70 71 m_memory = 72 static_cast<u8*>(std::malloc(static_cast<size_t>(RAW_SECTOR_SIZE) * static_cast<size_t>(m_memory_sectors))); 73 if (!m_memory) 74 { 75 progress->FormatModalError("Failed to allocate memory for {} sectors", m_memory_sectors); 76 return false; 77 } 78 79 progress->SetStatusText("Preloading CD image to RAM..."); 80 progress->SetProgressRange(m_memory_sectors); 81 progress->SetProgressValue(0); 82 83 u8* memory_ptr = m_memory; 84 u32 sectors_read = 0; 85 for (u32 i = 0; i < image->GetIndexCount(); i++) 86 { 87 const Index& index = image->GetIndex(i); 88 if (index.file_sector_size == 0) 89 continue; 90 91 for (u32 lba = 0; lba < index.length; lba++) 92 { 93 if (!image->ReadSectorFromIndex(memory_ptr, index, lba)) 94 { 95 ERROR_LOG("Failed to read LBA {} in index {}", lba, i); 96 return false; 97 } 98 99 progress->SetProgressValue(sectors_read); 100 memory_ptr += RAW_SECTOR_SIZE; 101 sectors_read++; 102 } 103 } 104 105 for (u32 i = 1; i <= image->GetTrackCount(); i++) 106 m_tracks.push_back(image->GetTrack(i)); 107 108 u32 current_offset = 0; 109 for (u32 i = 0; i < image->GetIndexCount(); i++) 110 { 111 Index new_index = image->GetIndex(i); 112 new_index.file_index = 0; 113 if (new_index.file_sector_size > 0) 114 { 115 new_index.file_offset = current_offset; 116 current_offset += new_index.length; 117 } 118 m_indices.push_back(new_index); 119 } 120 121 Assert(current_offset == m_memory_sectors); 122 m_filename = image->GetFileName(); 123 m_lba_count = image->GetLBACount(); 124 125 m_sbi.LoadFromImagePath(m_filename); 126 127 return Seek(1, Position{0, 0, 0}); 128 } 129 130 bool CDImageMemory::ReadSubChannelQ(SubChannelQ* subq, const Index& index, LBA lba_in_index) 131 { 132 if (m_sbi.GetReplacementSubChannelQ(index.start_lba_on_disc + lba_in_index, subq)) 133 return true; 134 135 return CDImage::ReadSubChannelQ(subq, index, lba_in_index); 136 } 137 138 bool CDImageMemory::HasNonStandardSubchannel() const 139 { 140 return (m_sbi.GetReplacementSectorCount() > 0); 141 } 142 143 bool CDImageMemory::IsPrecached() const 144 { 145 return true; 146 } 147 148 bool CDImageMemory::ReadSectorFromIndex(void* buffer, const Index& index, LBA lba_in_index) 149 { 150 DebugAssert(index.file_index == 0); 151 152 const u64 sector_number = index.file_offset + lba_in_index; 153 if (sector_number >= m_memory_sectors) 154 return false; 155 156 const size_t file_offset = static_cast<size_t>(sector_number) * static_cast<size_t>(RAW_SECTOR_SIZE); 157 std::memcpy(buffer, &m_memory[file_offset], RAW_SECTOR_SIZE); 158 return true; 159 } 160 161 std::unique_ptr<CDImage> 162 CDImage::CreateMemoryImage(CDImage* image, ProgressCallback* progress /* = ProgressCallback::NullProgressCallback */) 163 { 164 std::unique_ptr<CDImageMemory> memory_image = std::make_unique<CDImageMemory>(); 165 if (!memory_image->CopyImage(image, progress)) 166 return {}; 167 168 return memory_image; 169 }