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.cpp (16166B)


      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 
      6 #include "common/assert.h"
      7 #include "common/bitutils.h"
      8 #include "common/error.h"
      9 #include "common/file_system.h"
     10 #include "common/log.h"
     11 #include "common/path.h"
     12 #include "common/string_util.h"
     13 
     14 #include <array>
     15 
     16 Log_SetChannel(CDImage);
     17 
     18 CDImage::CDImage() = default;
     19 
     20 CDImage::~CDImage() = default;
     21 
     22 u32 CDImage::GetBytesPerSector(TrackMode mode)
     23 {
     24   static constexpr std::array<u32, 8> sizes = {{2352, 2048, 2352, 2336, 2048, 2324, 2332, 2352}};
     25   return sizes[static_cast<u32>(mode)];
     26 }
     27 
     28 // Adapted from
     29 // https://github.com/saramibreak/DiscImageCreator/blob/5a8fe21730872d67991211f1319c87f0780f2d0f/DiscImageCreator/convert.cpp
     30 void CDImage::DeinterleaveSubcode(const u8* subcode_in, u8* subcode_out)
     31 {
     32   std::memset(subcode_out, 0, ALL_SUBCODE_SIZE);
     33 
     34   u32 row = 0;
     35   for (u32 bitNum = 0; bitNum < 8; bitNum++)
     36   {
     37     for (u32 nColumn = 0; nColumn < ALL_SUBCODE_SIZE; row++)
     38     {
     39       u32 mask = 0x80;
     40       for (int nShift = 0; nShift < 8; nShift++, nColumn++)
     41       {
     42         const s32 n = static_cast<s32>(nShift) - static_cast<s32>(bitNum);
     43         if (n > 0)
     44           subcode_out[row] |= static_cast<u8>((subcode_in[nColumn] >> n) & mask);
     45         else
     46           subcode_out[row] |= static_cast<u8>((subcode_in[nColumn] << std::abs(n)) & mask);
     47         mask >>= 1;
     48       }
     49     }
     50   }
     51 }
     52 
     53 std::unique_ptr<CDImage> CDImage::Open(const char* filename, bool allow_patches, Error* error)
     54 {
     55   const char* extension;
     56 
     57 #ifdef __ANDROID__
     58   std::string filename_display_name(FileSystem::GetDisplayNameFromPath(filename));
     59   if (filename_display_name.empty())
     60     filename_display_name = filename;
     61 
     62   extension = std::strrchr(filename_display_name.c_str(), '.');
     63 #else
     64   extension = std::strrchr(filename, '.');
     65 #endif
     66 
     67   std::unique_ptr<CDImage> image;
     68   if (!extension)
     69   {
     70     // Device filenames on Linux don't have extensions.
     71     if (IsDeviceName(filename))
     72     {
     73       image = OpenDeviceImage(filename, error);
     74     }
     75     else
     76     {
     77       Error::SetStringFmt(error, "Invalid filename: '{}'", Path::GetFileName(filename));
     78       return nullptr;
     79     }
     80   }
     81   else if (StringUtil::Strcasecmp(extension, ".cue") == 0)
     82   {
     83     image = OpenCueSheetImage(filename, error);
     84   }
     85   else if (StringUtil::Strcasecmp(extension, ".bin") == 0 || StringUtil::Strcasecmp(extension, ".img") == 0 ||
     86            StringUtil::Strcasecmp(extension, ".iso") == 0)
     87   {
     88     image = OpenBinImage(filename, error);
     89   }
     90   else if (StringUtil::Strcasecmp(extension, ".chd") == 0)
     91   {
     92     image = OpenCHDImage(filename, error);
     93   }
     94   else if (StringUtil::Strcasecmp(extension, ".ecm") == 0)
     95   {
     96     image = OpenEcmImage(filename, error);
     97   }
     98   else if (StringUtil::Strcasecmp(extension, ".mds") == 0)
     99   {
    100     image = OpenMdsImage(filename, error);
    101   }
    102   else if (StringUtil::Strcasecmp(extension, ".pbp") == 0)
    103   {
    104     image = OpenPBPImage(filename, error);
    105   }
    106   else if (StringUtil::Strcasecmp(extension, ".m3u") == 0)
    107   {
    108     image = OpenM3uImage(filename, allow_patches, error);
    109   }
    110   else if (IsDeviceName(filename))
    111   {
    112     image = OpenDeviceImage(filename, error);
    113   }
    114   else
    115   {
    116     Error::SetStringFmt(error, "Unknown extension '{}' from filename '{}'", extension, Path::GetFileName(filename));
    117     return nullptr;
    118   }
    119 
    120   if (allow_patches)
    121   {
    122 #ifdef __ANDROID__
    123     const std::string ppf_filename(
    124       Path::BuildRelativePath(filename, Path::ReplaceExtension(filename_display_name, "ppf")));
    125 #else
    126     const std::string ppf_filename(
    127       Path::BuildRelativePath(filename, Path::ReplaceExtension(Path::GetFileName(filename), "ppf")));
    128 #endif
    129     if (FileSystem::FileExists(ppf_filename.c_str()))
    130     {
    131       image = CDImage::OverlayPPFPatch(ppf_filename.c_str(), std::move(image));
    132       if (!image)
    133         Error::SetString(error, fmt::format("Failed to apply ppf patch from '{}'.", ppf_filename));
    134     }
    135   }
    136 
    137   return image;
    138 }
    139 
    140 CDImage::LBA CDImage::GetTrackStartPosition(u8 track) const
    141 {
    142   Assert(track > 0 && track <= m_tracks.size());
    143   return m_tracks[track - 1].start_lba;
    144 }
    145 
    146 CDImage::Position CDImage::GetTrackStartMSFPosition(u8 track) const
    147 {
    148   Assert(track > 0 && track <= m_tracks.size());
    149   return Position::FromLBA(m_tracks[track - 1].start_lba);
    150 }
    151 
    152 CDImage::LBA CDImage::GetTrackLength(u8 track) const
    153 {
    154   Assert(track > 0 && track <= m_tracks.size());
    155   return m_tracks[track - 1].length;
    156 }
    157 
    158 CDImage::Position CDImage::GetTrackMSFLength(u8 track) const
    159 {
    160   Assert(track > 0 && track <= m_tracks.size());
    161   return Position::FromLBA(m_tracks[track - 1].length);
    162 }
    163 
    164 CDImage::TrackMode CDImage::GetTrackMode(u8 track) const
    165 {
    166   Assert(track > 0 && track <= m_tracks.size());
    167   return m_tracks[track - 1].mode;
    168 }
    169 
    170 CDImage::LBA CDImage::GetTrackIndexPosition(u8 track, u8 index) const
    171 {
    172   for (const Index& current_index : m_indices)
    173   {
    174     if (current_index.track_number == track && current_index.index_number == index)
    175       return current_index.start_lba_on_disc;
    176   }
    177 
    178   return m_lba_count;
    179 }
    180 
    181 CDImage::LBA CDImage::GetTrackIndexLength(u8 track, u8 index) const
    182 {
    183   for (const Index& current_index : m_indices)
    184   {
    185     if (current_index.track_number == track && current_index.index_number == index)
    186       return current_index.length;
    187   }
    188 
    189   return 0;
    190 }
    191 
    192 const CDImage::CDImage::Track& CDImage::GetTrack(u32 track) const
    193 {
    194   Assert(track > 0 && track <= m_tracks.size());
    195   return m_tracks[track - 1];
    196 }
    197 
    198 const CDImage::CDImage::Index& CDImage::GetIndex(u32 i) const
    199 {
    200   return m_indices[i];
    201 }
    202 
    203 bool CDImage::Seek(LBA lba)
    204 {
    205   const Index* new_index;
    206   if (m_current_index && lba >= m_current_index->start_lba_on_disc &&
    207       (lba - m_current_index->start_lba_on_disc) < m_current_index->length)
    208   {
    209     new_index = m_current_index;
    210   }
    211   else
    212   {
    213     new_index = GetIndexForDiscPosition(lba);
    214     if (!new_index)
    215       return false;
    216   }
    217 
    218   const LBA new_index_offset = lba - new_index->start_lba_on_disc;
    219   if (new_index_offset >= new_index->length)
    220     return false;
    221 
    222   m_current_index = new_index;
    223   m_position_on_disc = lba;
    224   m_position_in_index = new_index_offset;
    225   m_position_in_track = new_index->start_lba_in_track + new_index_offset;
    226   return true;
    227 }
    228 
    229 bool CDImage::Seek(u32 track_number, const Position& pos_in_track)
    230 {
    231   if (track_number < 1 || track_number > m_tracks.size())
    232     return false;
    233 
    234   const Track& track = m_tracks[track_number - 1];
    235   const LBA pos_lba = pos_in_track.ToLBA();
    236   if (pos_lba >= track.length)
    237     return false;
    238 
    239   return Seek(track.start_lba + pos_lba);
    240 }
    241 
    242 bool CDImage::Seek(const Position& pos)
    243 {
    244   return Seek(pos.ToLBA());
    245 }
    246 
    247 bool CDImage::Seek(u32 track_number, LBA lba)
    248 {
    249   if (track_number < 1 || track_number > m_tracks.size())
    250     return false;
    251 
    252   const Track& track = m_tracks[track_number - 1];
    253   return Seek(track.start_lba + lba);
    254 }
    255 
    256 u32 CDImage::Read(ReadMode read_mode, u32 sector_count, void* buffer)
    257 {
    258   u8* buffer_ptr = static_cast<u8*>(buffer);
    259   u32 sectors_read = 0;
    260   for (; sectors_read < sector_count; sectors_read++)
    261   {
    262     // get raw sector
    263     u8 raw_sector[RAW_SECTOR_SIZE];
    264     if (!ReadRawSector(raw_sector, nullptr))
    265       break;
    266 
    267     switch (read_mode)
    268     {
    269       case ReadMode::DataOnly:
    270         std::memcpy(buffer_ptr, raw_sector + 24, DATA_SECTOR_SIZE);
    271         buffer_ptr += DATA_SECTOR_SIZE;
    272         break;
    273 
    274       case ReadMode::RawNoSync:
    275         std::memcpy(buffer_ptr, raw_sector + SECTOR_SYNC_SIZE, RAW_SECTOR_SIZE - SECTOR_SYNC_SIZE);
    276         buffer_ptr += RAW_SECTOR_SIZE - SECTOR_SYNC_SIZE;
    277         break;
    278 
    279       case ReadMode::RawSector:
    280         std::memcpy(buffer_ptr, raw_sector, RAW_SECTOR_SIZE);
    281         buffer_ptr += RAW_SECTOR_SIZE;
    282         break;
    283 
    284       default:
    285         UnreachableCode();
    286         break;
    287     }
    288   }
    289 
    290   return sectors_read;
    291 }
    292 
    293 bool CDImage::ReadRawSector(void* buffer, SubChannelQ* subq)
    294 {
    295   if (m_position_in_index == m_current_index->length)
    296   {
    297     if (!Seek(m_position_on_disc))
    298       return false;
    299   }
    300 
    301   if (buffer)
    302   {
    303     if (m_current_index->file_sector_size > 0)
    304     {
    305       // TODO: This is where we'd reconstruct the header for other mode tracks.
    306       if (!ReadSectorFromIndex(buffer, *m_current_index, m_position_in_index))
    307       {
    308         ERROR_LOG("Read of LBA {} failed", m_position_on_disc);
    309         Seek(m_position_on_disc);
    310         return false;
    311       }
    312     }
    313     else
    314     {
    315       if (m_current_index->track_number == LEAD_OUT_TRACK_NUMBER)
    316       {
    317         // Lead-out area.
    318         std::fill(static_cast<u8*>(buffer), static_cast<u8*>(buffer) + RAW_SECTOR_SIZE, u8(0xAA));
    319       }
    320       else
    321       {
    322         // This in an implicit pregap. Return silence.
    323         std::fill(static_cast<u8*>(buffer), static_cast<u8*>(buffer) + RAW_SECTOR_SIZE, u8(0));
    324       }
    325     }
    326   }
    327 
    328   if (subq && !ReadSubChannelQ(subq, *m_current_index, m_position_in_index))
    329   {
    330     ERROR_LOG("Subchannel read of LBA {} failed", m_position_on_disc);
    331     Seek(m_position_on_disc);
    332     return false;
    333   }
    334 
    335   m_position_on_disc++;
    336   m_position_in_index++;
    337   m_position_in_track++;
    338   return true;
    339 }
    340 
    341 bool CDImage::ReadSubChannelQ(SubChannelQ* subq, const Index& index, LBA lba_in_index)
    342 {
    343   GenerateSubChannelQ(subq, index, lba_in_index);
    344   return true;
    345 }
    346 
    347 bool CDImage::HasNonStandardSubchannel() const
    348 {
    349   return false;
    350 }
    351 
    352 std::string CDImage::GetMetadata(std::string_view type) const
    353 {
    354   std::string result;
    355   if (type == "title")
    356   {
    357     const std::string display_name(FileSystem::GetDisplayNameFromPath(m_filename));
    358     result = Path::StripExtension(display_name);
    359   }
    360 
    361   return result;
    362 }
    363 
    364 bool CDImage::HasSubImages() const
    365 {
    366   return false;
    367 }
    368 
    369 u32 CDImage::GetSubImageCount() const
    370 {
    371   return 0;
    372 }
    373 
    374 u32 CDImage::GetCurrentSubImage() const
    375 {
    376   return 0;
    377 }
    378 
    379 bool CDImage::SwitchSubImage(u32 index, Error* error)
    380 {
    381   return false;
    382 }
    383 
    384 std::string CDImage::GetSubImageMetadata(u32 index, std::string_view type) const
    385 {
    386   return {};
    387 }
    388 
    389 CDImage::PrecacheResult CDImage::Precache(ProgressCallback* progress /*= ProgressCallback::NullProgressCallback*/)
    390 {
    391   return PrecacheResult::Unsupported;
    392 }
    393 
    394 bool CDImage::IsPrecached() const
    395 {
    396   return false;
    397 }
    398 
    399 s64 CDImage::GetSizeOnDisk() const
    400 {
    401   return -1;
    402 }
    403 
    404 void CDImage::ClearTOC()
    405 {
    406   m_lba_count = 0;
    407   m_indices.clear();
    408   m_tracks.clear();
    409   m_current_index = nullptr;
    410   m_position_in_index = 0;
    411   m_position_in_track = 0;
    412   m_position_on_disc = 0;
    413 }
    414 
    415 void CDImage::CopyTOC(const CDImage* image)
    416 {
    417   m_lba_count = image->m_lba_count;
    418   decltype(m_indices)().swap(m_indices);
    419   decltype(m_tracks)().swap(m_tracks);
    420   m_indices.reserve(image->m_indices.size());
    421   m_tracks.reserve(image->m_tracks.size());
    422 
    423   // Damn bitfield copy constructor...
    424   for (const Index& index : image->m_indices)
    425   {
    426     Index new_index;
    427     std::memcpy(&new_index, &index, sizeof(new_index));
    428     m_indices.push_back(new_index);
    429   }
    430   for (const Track& track : image->m_tracks)
    431   {
    432     Track new_track;
    433     std::memcpy(&new_track, &track, sizeof(new_track));
    434     m_tracks.push_back(new_track);
    435   }
    436   m_current_index = nullptr;
    437   m_position_in_index = 0;
    438   m_position_in_track = 0;
    439   m_position_on_disc = 0;
    440 }
    441 
    442 const CDImage::Index* CDImage::GetIndexForDiscPosition(LBA pos)
    443 {
    444   for (const Index& index : m_indices)
    445   {
    446     if (pos < index.start_lba_on_disc)
    447       continue;
    448 
    449     const LBA index_offset = pos - index.start_lba_on_disc;
    450     if (index_offset >= index.length)
    451       continue;
    452 
    453     return &index;
    454   }
    455 
    456   return nullptr;
    457 }
    458 
    459 const CDImage::Index* CDImage::GetIndexForTrackPosition(u32 track_number, LBA track_pos)
    460 {
    461   if (track_number < 1 || track_number > m_tracks.size())
    462     return nullptr;
    463 
    464   const Track& track = m_tracks[track_number - 1];
    465   if (track_pos >= track.length)
    466     return nullptr;
    467 
    468   return GetIndexForDiscPosition(track.start_lba + track_pos);
    469 }
    470 
    471 bool CDImage::GenerateSubChannelQ(SubChannelQ* subq, LBA lba)
    472 {
    473   const Index* index = GetIndexForDiscPosition(lba);
    474   if (!index)
    475     return false;
    476 
    477   const u32 index_offset = index->start_lba_on_disc - lba;
    478   GenerateSubChannelQ(subq, *index, index_offset);
    479   return true;
    480 }
    481 
    482 void CDImage::GenerateSubChannelQ(SubChannelQ* subq, const Index& index, u32 index_offset)
    483 {
    484   subq->control_bits = index.control.bits;
    485   subq->track_number_bcd = (index.track_number <= m_tracks.size() ? BinaryToBCD(static_cast<u8>(index.track_number)) :
    486                                                                     static_cast<u8>(index.track_number));
    487   subq->index_number_bcd = BinaryToBCD(static_cast<u8>(index.index_number));
    488 
    489   Position relative_position;
    490   if (index.is_pregap)
    491   {
    492     // position should count down to the end of the pregap
    493     relative_position = Position::FromLBA(index.length - index_offset - 1);
    494   }
    495   else
    496   {
    497     // count up from the start of the track
    498     relative_position = Position::FromLBA(index.start_lba_in_track + index_offset);
    499   }
    500 
    501   std::tie(subq->relative_minute_bcd, subq->relative_second_bcd, subq->relative_frame_bcd) = relative_position.ToBCD();
    502 
    503   subq->reserved = 0;
    504 
    505   const Position absolute_position = Position::FromLBA(index.start_lba_on_disc + index_offset);
    506   std::tie(subq->absolute_minute_bcd, subq->absolute_second_bcd, subq->absolute_frame_bcd) = absolute_position.ToBCD();
    507   subq->crc = SubChannelQ::ComputeCRC(subq->data);
    508 }
    509 
    510 void CDImage::AddLeadOutIndex()
    511 {
    512   Assert(!m_indices.empty());
    513   const Index& last_index = m_indices.back();
    514 
    515   Index index = {};
    516   index.start_lba_on_disc = last_index.start_lba_on_disc + last_index.length;
    517   index.length = LEAD_OUT_SECTOR_COUNT;
    518   index.track_number = LEAD_OUT_TRACK_NUMBER;
    519   index.index_number = 0;
    520   index.control.bits = last_index.control.bits;
    521   m_indices.push_back(index);
    522 }
    523 
    524 u16 CDImage::SubChannelQ::ComputeCRC(const Data& data)
    525 {
    526   static constexpr std::array<u16, 256> crc16_table = {
    527     {0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7, 0x8108, 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD,
    528      0xE1CE, 0xF1EF, 0x1231, 0x0210, 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6, 0x9339, 0x8318, 0xB37B, 0xA35A,
    529      0xD3BD, 0xC39C, 0xF3FF, 0xE3DE, 0x2462, 0x3443, 0x0420, 0x1401, 0x64E6, 0x74C7, 0x44A4, 0x5485, 0xA56A, 0xB54B,
    530      0x8528, 0x9509, 0xE5EE, 0xF5CF, 0xC5AC, 0xD58D, 0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6, 0x5695, 0x46B4,
    531      0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D, 0xC7BC, 0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861,
    532      0x2802, 0x3823, 0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B, 0x5AF5, 0x4AD4, 0x7AB7, 0x6A96,
    533      0x1A71, 0x0A50, 0x3A33, 0x2A12, 0xDBFD, 0xCBDC, 0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A, 0x6CA6, 0x7C87,
    534      0x4CE4, 0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41, 0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD, 0xAD2A, 0xBD0B, 0x8D68, 0x9D49,
    535      0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13, 0x2E32, 0x1E51, 0x0E70, 0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A,
    536      0x9F59, 0x8F78, 0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E, 0xE16F, 0x1080, 0x00A1, 0x30C2, 0x20E3,
    537      0x5004, 0x4025, 0x7046, 0x6067, 0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E, 0x02B1, 0x1290,
    538      0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256, 0xB5EA, 0xA5CB, 0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D,
    539      0x34E2, 0x24C3, 0x14A0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, 0xA7DB, 0xB7FA, 0x8799, 0x97B8, 0xE75F, 0xF77E,
    540      0xC71D, 0xD73C, 0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657, 0x7676, 0x4615, 0x5634, 0xD94C, 0xC96D, 0xF90E, 0xE92F,
    541      0x99C8, 0x89E9, 0xB98A, 0xA9AB, 0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882, 0x28A3, 0xCB7D, 0xDB5C,
    542      0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A, 0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92,
    543      0xFD2E, 0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9, 0x7C26, 0x6C07, 0x5C64, 0x4C45, 0x3CA2, 0x2C83,
    544      0x1CE0, 0x0CC1, 0xEF1F, 0xFF3E, 0xCF5D, 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8, 0x6E17, 0x7E36, 0x4E55, 0x5E74,
    545      0x2E93, 0x3EB2, 0x0ED1, 0x1EF0}};
    546 
    547   u16 value = 0;
    548   for (u32 i = 0; i < 10; i++)
    549     value = crc16_table[(value >> 8) ^ data[i]] ^ (value << 8);
    550 
    551   // Invert and swap
    552   return ByteSwap(static_cast<u16>(~value));
    553 }
    554 
    555 bool CDImage::SubChannelQ::IsCRCValid() const
    556 {
    557   return crc == ComputeCRC(data);
    558 }