cd_image_bin.cpp (4307B)
1 // SPDX-FileCopyrightText: 2019-2024 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/error.h" 8 #include "common/file_system.h" 9 #include "common/path.h" 10 11 namespace { 12 13 class CDImageBin : public CDImage 14 { 15 public: 16 CDImageBin(); 17 ~CDImageBin() override; 18 19 bool Open(const char* filename, Error* error); 20 21 bool ReadSubChannelQ(SubChannelQ* subq, const Index& index, LBA lba_in_index) override; 22 bool HasNonStandardSubchannel() const override; 23 24 s64 GetSizeOnDisk() const override; 25 26 protected: 27 bool ReadSectorFromIndex(void* buffer, const Index& index, LBA lba_in_index) override; 28 29 private: 30 std::FILE* m_fp = nullptr; 31 u64 m_file_position = 0; 32 33 CDSubChannelReplacement m_sbi; 34 }; 35 36 } // namespace 37 38 CDImageBin::CDImageBin() = default; 39 40 CDImageBin::~CDImageBin() 41 { 42 if (m_fp) 43 std::fclose(m_fp); 44 } 45 46 bool CDImageBin::Open(const char* filename, Error* error) 47 { 48 m_filename = filename; 49 m_fp = FileSystem::OpenSharedCFile(filename, "rb", FileSystem::FileShareMode::DenyWrite, error); 50 if (!m_fp) 51 { 52 Error::AddPrefixFmt(error, "Failed to open binfile '{}': ", Path::GetFileName(filename)); 53 return false; 54 } 55 56 const u32 track_sector_size = RAW_SECTOR_SIZE; 57 58 // determine the length from the file 59 std::fseek(m_fp, 0, SEEK_END); 60 const u32 file_size = static_cast<u32>(std::ftell(m_fp)); 61 std::fseek(m_fp, 0, SEEK_SET); 62 63 m_lba_count = file_size / track_sector_size; 64 65 SubChannelQ::Control control = {}; 66 TrackMode mode = TrackMode::Mode2Raw; 67 control.data = mode != TrackMode::Audio; 68 69 // Two seconds default pregap. 70 const u32 pregap_frames = 2 * FRAMES_PER_SECOND; 71 Index pregap_index = {}; 72 pregap_index.file_sector_size = track_sector_size; 73 pregap_index.start_lba_on_disc = 0; 74 pregap_index.start_lba_in_track = static_cast<LBA>(-static_cast<s32>(pregap_frames)); 75 pregap_index.length = pregap_frames; 76 pregap_index.track_number = 1; 77 pregap_index.index_number = 0; 78 pregap_index.mode = mode; 79 pregap_index.submode = CDImage::SubchannelMode::None; 80 pregap_index.control.bits = control.bits; 81 pregap_index.is_pregap = true; 82 m_indices.push_back(pregap_index); 83 84 // Data index. 85 Index data_index = {}; 86 data_index.file_index = 0; 87 data_index.file_offset = 0; 88 data_index.file_sector_size = track_sector_size; 89 data_index.start_lba_on_disc = pregap_index.length; 90 data_index.track_number = 1; 91 data_index.index_number = 1; 92 data_index.start_lba_in_track = 0; 93 data_index.length = m_lba_count; 94 data_index.mode = mode; 95 data_index.submode = CDImage::SubchannelMode::None; 96 data_index.control.bits = control.bits; 97 m_indices.push_back(data_index); 98 99 // Assume a single track. 100 m_tracks.push_back(Track{static_cast<u32>(1), data_index.start_lba_on_disc, static_cast<u32>(0), m_lba_count, mode, 101 SubchannelMode::None, control}); 102 103 AddLeadOutIndex(); 104 105 m_sbi.LoadFromImagePath(filename); 106 107 return Seek(1, Position{0, 0, 0}); 108 } 109 110 bool CDImageBin::ReadSubChannelQ(SubChannelQ* subq, const Index& index, LBA lba_in_index) 111 { 112 if (m_sbi.GetReplacementSubChannelQ(index.start_lba_on_disc + lba_in_index, subq)) 113 return true; 114 115 return CDImage::ReadSubChannelQ(subq, index, lba_in_index); 116 } 117 118 bool CDImageBin::HasNonStandardSubchannel() const 119 { 120 return (m_sbi.GetReplacementSectorCount() > 0); 121 } 122 123 bool CDImageBin::ReadSectorFromIndex(void* buffer, const Index& index, LBA lba_in_index) 124 { 125 const u64 file_position = index.file_offset + (static_cast<u64>(lba_in_index) * index.file_sector_size); 126 if (m_file_position != file_position) 127 { 128 if (std::fseek(m_fp, static_cast<long>(file_position), SEEK_SET) != 0) 129 return false; 130 131 m_file_position = file_position; 132 } 133 134 if (std::fread(buffer, index.file_sector_size, 1, m_fp) != 1) 135 { 136 std::fseek(m_fp, static_cast<long>(m_file_position), SEEK_SET); 137 return false; 138 } 139 140 m_file_position += index.file_sector_size; 141 return true; 142 } 143 144 s64 CDImageBin::GetSizeOnDisk() const 145 { 146 return FileSystem::FSize64(m_fp); 147 } 148 149 std::unique_ptr<CDImage> CDImage::OpenBinImage(const char* filename, Error* error) 150 { 151 std::unique_ptr<CDImageBin> image = std::make_unique<CDImageBin>(); 152 if (!image->Open(filename, error)) 153 return {}; 154 155 return image; 156 }