duckstation

duckstation, but archived from the revision just before upstream changed it to a proprietary software project, this version is the libre one
git clone https://git.neptards.moe/u3shit/duckstation.git
Log | Files | Refs | README | LICENSE

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 };