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 }