cd_image.h (11511B)
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 #pragma once 5 6 #include "common/bitfield.h" 7 #include "common/bitutils.h" 8 #include "common/progress_callback.h" 9 #include "common/types.h" 10 11 #include <array> 12 #include <memory> 13 #include <string> 14 #include <tuple> 15 #include <vector> 16 17 class Error; 18 19 class CDImage 20 { 21 public: 22 CDImage(); 23 virtual ~CDImage(); 24 25 using LBA = u32; 26 27 enum : u32 28 { 29 RAW_SECTOR_SIZE = 2352, 30 DATA_SECTOR_SIZE = 2048, 31 SECTOR_SYNC_SIZE = 12, 32 SECTOR_HEADER_SIZE = 4, 33 FRAMES_PER_SECOND = 75, // "sectors", or "timecode frames" (not "channel frames") 34 SECONDS_PER_MINUTE = 60, 35 FRAMES_PER_MINUTE = FRAMES_PER_SECOND * SECONDS_PER_MINUTE, 36 SUBCHANNEL_BYTES_PER_FRAME = 12, 37 LEAD_OUT_SECTOR_COUNT = 6750, 38 ALL_SUBCODE_SIZE = 96, 39 }; 40 41 enum : u8 42 { 43 LEAD_OUT_TRACK_NUMBER = 0xAA 44 }; 45 46 enum class ReadMode : u8 47 { 48 DataOnly, // 2048 bytes per sector. 49 RawSector, // 2352 bytes per sector. 50 RawNoSync, // 2340 bytes per sector. 51 }; 52 53 enum class TrackMode : u8 54 { 55 Audio, // 2352 bytes per sector 56 Mode1, // 2048 bytes per sector 57 Mode1Raw, // 2352 bytes per sector 58 Mode2, // 2336 bytes per sector 59 Mode2Form1, // 2048 bytes per sector 60 Mode2Form2, // 2324 bytes per sector 61 Mode2FormMix, // 2332 bytes per sector 62 Mode2Raw // 2352 bytes per sector 63 }; 64 65 enum class SubchannelMode : u8 66 { 67 None, // no subcode data stored 68 RawInterleaved, // raw interleaved 96 bytes per sector 69 Raw, // raw uninterleaved 96 bytes per sector 70 }; 71 72 enum class PrecacheResult : u8 73 { 74 Unsupported, 75 ReadError, 76 Success, 77 }; 78 79 struct SectorHeader 80 { 81 u8 minute; 82 u8 second; 83 u8 frame; 84 u8 sector_mode; 85 }; 86 87 struct Position 88 { 89 u8 minute; 90 u8 second; 91 u8 frame; 92 93 static constexpr Position FromBCD(u8 minute, u8 second, u8 frame) 94 { 95 return Position{PackedBCDToBinary(minute), PackedBCDToBinary(second), PackedBCDToBinary(frame)}; 96 } 97 98 static constexpr Position FromLBA(LBA lba) 99 { 100 const u8 frame = Truncate8(lba % FRAMES_PER_SECOND); 101 lba /= FRAMES_PER_SECOND; 102 103 const u8 second = Truncate8(lba % SECONDS_PER_MINUTE); 104 lba /= SECONDS_PER_MINUTE; 105 106 const u8 minute = Truncate8(lba); 107 108 return Position{minute, second, frame}; 109 } 110 111 LBA ToLBA() const 112 { 113 return ZeroExtend32(minute) * FRAMES_PER_MINUTE + ZeroExtend32(second) * FRAMES_PER_SECOND + ZeroExtend32(frame); 114 } 115 116 constexpr std::tuple<u8, u8, u8> ToBCD() const 117 { 118 return std::make_tuple<u8, u8, u8>(BinaryToBCD(minute), BinaryToBCD(second), BinaryToBCD(frame)); 119 } 120 121 Position operator+(const Position& rhs) { return FromLBA(ToLBA() + rhs.ToLBA()); } 122 Position& operator+=(const Position& pos) 123 { 124 *this = *this + pos; 125 return *this; 126 } 127 128 #define RELATIONAL_OPERATOR(op) \ 129 bool operator op(const Position& rhs) const \ 130 { \ 131 return std::tie(minute, second, frame) op std::tie(rhs.minute, rhs.second, rhs.frame); \ 132 } 133 134 RELATIONAL_OPERATOR(==); 135 RELATIONAL_OPERATOR(!=); 136 RELATIONAL_OPERATOR(<); 137 RELATIONAL_OPERATOR(<=); 138 RELATIONAL_OPERATOR(>); 139 RELATIONAL_OPERATOR(>=); 140 141 #undef RELATIONAL_OPERATOR 142 }; 143 144 union SubChannelQ 145 { 146 using Data = std::array<u8, SUBCHANNEL_BYTES_PER_FRAME>; 147 148 union Control 149 { 150 u8 bits; 151 152 BitField<u8, u8, 0, 4> adr; 153 BitField<u8, bool, 4, 1> audio_preemphasis; 154 BitField<u8, bool, 5, 1> digital_copy_permitted; 155 BitField<u8, bool, 6, 1> data; 156 BitField<u8, bool, 7, 1> four_channel_audio; 157 158 Control() = default; 159 160 Control(u8 bits_) : bits(bits_) {} 161 162 Control(const Control& rhs) : bits(rhs.bits) {} 163 164 Control& operator=(const Control& rhs) 165 { 166 bits = rhs.bits; 167 return *this; 168 } 169 }; 170 171 struct 172 { 173 u8 control_bits; 174 u8 track_number_bcd; 175 u8 index_number_bcd; 176 u8 relative_minute_bcd; 177 u8 relative_second_bcd; 178 u8 relative_frame_bcd; 179 u8 reserved; 180 u8 absolute_minute_bcd; 181 u8 absolute_second_bcd; 182 u8 absolute_frame_bcd; 183 u16 crc; 184 }; 185 186 Data data; 187 188 static u16 ComputeCRC(const Data& data); 189 190 Control GetControl() const { return Control(control_bits); } 191 bool IsData() const { return GetControl().data; } 192 193 bool IsCRCValid() const; 194 195 SubChannelQ() = default; 196 197 SubChannelQ(const SubChannelQ& q) : data(q.data) {} 198 199 SubChannelQ& operator=(const SubChannelQ& q) 200 { 201 data = q.data; 202 return *this; 203 } 204 }; 205 static_assert(sizeof(SubChannelQ) == SUBCHANNEL_BYTES_PER_FRAME, "SubChannelQ is correct size"); 206 207 struct Track 208 { 209 u32 track_number; 210 LBA start_lba; 211 u32 first_index; 212 u32 length; 213 TrackMode mode; 214 SubchannelMode submode; 215 SubChannelQ::Control control; 216 }; 217 218 struct Index 219 { 220 u64 file_offset; 221 u32 file_index; 222 u32 file_sector_size; 223 LBA start_lba_on_disc; 224 u32 track_number; 225 u32 index_number; 226 LBA start_lba_in_track; 227 u32 length; 228 TrackMode mode; 229 SubchannelMode submode; 230 SubChannelQ::Control control; 231 bool is_pregap; 232 }; 233 234 // Helper functions. 235 static u32 GetBytesPerSector(TrackMode mode); 236 static void DeinterleaveSubcode(const u8* subcode_in, u8* subcode_out); 237 238 /// Returns a list of physical CD-ROM devices, .first being the device path, .second being the device name. 239 static std::vector<std::pair<std::string, std::string>> GetDeviceList(); 240 241 /// Returns true if the specified filename is a CD-ROM device name. 242 static bool IsDeviceName(const char* filename); 243 244 // Opening disc image. 245 static std::unique_ptr<CDImage> Open(const char* filename, bool allow_patches, Error* error); 246 static std::unique_ptr<CDImage> OpenBinImage(const char* filename, Error* error); 247 static std::unique_ptr<CDImage> OpenCueSheetImage(const char* filename, Error* error); 248 static std::unique_ptr<CDImage> OpenCHDImage(const char* filename, Error* error); 249 static std::unique_ptr<CDImage> OpenEcmImage(const char* filename, Error* error); 250 static std::unique_ptr<CDImage> OpenMdsImage(const char* filename, Error* error); 251 static std::unique_ptr<CDImage> OpenPBPImage(const char* filename, Error* error); 252 static std::unique_ptr<CDImage> OpenM3uImage(const char* filename, bool apply_patches, Error* error); 253 static std::unique_ptr<CDImage> OpenDeviceImage(const char* filename, Error* error); 254 static std::unique_ptr<CDImage> 255 CreateMemoryImage(CDImage* image, ProgressCallback* progress = ProgressCallback::NullProgressCallback); 256 static std::unique_ptr<CDImage> OverlayPPFPatch(const char* filename, std::unique_ptr<CDImage> parent_image, 257 ProgressCallback* progress = ProgressCallback::NullProgressCallback); 258 259 // Accessors. 260 const std::string& GetFileName() const { return m_filename; } 261 LBA GetPositionOnDisc() const { return m_position_on_disc; } 262 Position GetMSFPositionOnDisc() const { return Position::FromLBA(m_position_on_disc); } 263 LBA GetPositionInTrack() const { return m_position_in_track; } 264 Position GetMSFPositionInTrack() const { return Position::FromLBA(m_position_in_track); } 265 LBA GetLBACount() const { return m_lba_count; } 266 u32 GetIndexNumber() const { return m_current_index->index_number; } 267 u32 GetTrackNumber() const { return m_current_index->track_number; } 268 u32 GetTrackCount() const { return static_cast<u32>(m_tracks.size()); } 269 LBA GetTrackStartPosition(u8 track) const; 270 Position GetTrackStartMSFPosition(u8 track) const; 271 LBA GetTrackLength(u8 track) const; 272 Position GetTrackMSFLength(u8 track) const; 273 TrackMode GetTrackMode(u8 track) const; 274 LBA GetTrackIndexPosition(u8 track, u8 index) const; 275 LBA GetTrackIndexLength(u8 track, u8 index) const; 276 u32 GetFirstTrackNumber() const { return m_tracks.front().track_number; } 277 u32 GetLastTrackNumber() const { return m_tracks.back().track_number; } 278 u32 GetIndexCount() const { return static_cast<u32>(m_indices.size()); } 279 const std::vector<Track>& GetTracks() const { return m_tracks; } 280 const std::vector<Index>& GetIndices() const { return m_indices; } 281 const Track& GetTrack(u32 track) const; 282 const Index& GetIndex(u32 i) const; 283 284 // Seek to data LBA. 285 bool Seek(LBA lba); 286 287 // Seek to disc position (MSF). 288 bool Seek(const Position& pos); 289 290 // Seek to track and position. 291 bool Seek(u32 track_number, const Position& pos_in_track); 292 293 // Seek to track and LBA. 294 bool Seek(u32 track_number, LBA lba); 295 296 // Read from the current LBA. Returns the number of sectors read. 297 u32 Read(ReadMode read_mode, u32 sector_count, void* buffer); 298 299 // Read a single raw sector, and subchannel from the current LBA. 300 bool ReadRawSector(void* buffer, SubChannelQ* subq); 301 302 // Reads sub-channel Q for the specified index+LBA. 303 virtual bool ReadSubChannelQ(SubChannelQ* subq, const Index& index, LBA lba_in_index); 304 305 // Returns true if the image has replacement subchannel data. 306 virtual bool HasNonStandardSubchannel() const; 307 308 // Reads a single sector from an index. 309 virtual bool ReadSectorFromIndex(void* buffer, const Index& index, LBA lba_in_index) = 0; 310 311 // Retrieve image metadata. 312 virtual std::string GetMetadata(std::string_view type) const; 313 314 // Returns true if this image type has sub-images (e.g. m3u). 315 virtual bool HasSubImages() const; 316 317 // Returns the number of sub-images in this image, if the format supports multiple. 318 virtual u32 GetSubImageCount() const; 319 320 // Returns the current sub-image index, if any. 321 virtual u32 GetCurrentSubImage() const; 322 323 // Changes the current sub-image. If this fails, the image state is unchanged. 324 virtual bool SwitchSubImage(u32 index, Error* error); 325 326 // Retrieve sub-image metadata. 327 virtual std::string GetSubImageMetadata(u32 index, std::string_view type) const; 328 329 // Returns true if the source supports precaching, which may be more optimal than an in-memory copy. 330 virtual PrecacheResult Precache(ProgressCallback* progress = ProgressCallback::NullProgressCallback); 331 virtual bool IsPrecached() const; 332 333 // Returns the size on disk of the image. This could be multiple files. 334 // If this function returns -1, it means the size could not be computed. 335 virtual s64 GetSizeOnDisk() const; 336 337 protected: 338 void ClearTOC(); 339 void CopyTOC(const CDImage* image); 340 341 const Index* GetIndexForDiscPosition(LBA pos); 342 const Index* GetIndexForTrackPosition(u32 track_number, LBA track_pos); 343 344 /// Generates sub-channel Q given the specified position. 345 bool GenerateSubChannelQ(SubChannelQ* subq, LBA lba); 346 347 /// Generates sub-channel Q from the given index and index-offset. 348 void GenerateSubChannelQ(SubChannelQ* subq, const Index& index, u32 index_offset); 349 350 /// Synthesis of lead-out data. 351 void AddLeadOutIndex(); 352 353 std::string m_filename; 354 u32 m_lba_count = 0; 355 356 std::vector<Track> m_tracks; 357 std::vector<Index> m_indices; 358 359 private: 360 // Position on disc. 361 LBA m_position_on_disc = 0; 362 363 // Position in track/index. 364 const Index* m_current_index = nullptr; 365 LBA m_position_in_index = 0; 366 LBA m_position_in_track = 0; 367 };