diff --git a/src/common/cd_image.cpp b/src/common/cd_image.cpp index 8dd3e702a..dcf723784 100644 --- a/src/common/cd_image.cpp +++ b/src/common/cd_image.cpp @@ -33,6 +33,8 @@ std::unique_ptr CDImage::Open(const char* filename) return OpenCueSheetImage(filename); else if (CASE_COMPARE(extension, ".bin") == 0 || CASE_COMPARE(extension, ".img") == 0) return OpenBinImage(filename); + else if (CASE_COMPARE(extension, ".chd") == 0) + return OpenCHDImage(filename); #undef CASE_COMPARE @@ -71,10 +73,6 @@ bool CDImage::Seek(LBA lba) if (new_index_offset >= new_index->length) return false; - const u64 new_file_offset = new_index->file_offset + (u64(new_index_offset) * new_index->file_sector_size); - if (new_index->file && std::fseek(new_index->file, static_cast(new_file_offset), SEEK_SET) != 0) - return false; - m_current_index = new_index; m_position_on_disc = lba; m_position_in_index = new_index_offset; @@ -115,28 +113,10 @@ u32 CDImage::Read(ReadMode read_mode, u32 sector_count, void* buffer) u32 sectors_read = 0; for (; sectors_read < sector_count; sectors_read++) { - if (m_position_in_index == m_current_index->length) - { - if (!Seek(m_position_on_disc)) - break; - } - // get raw sector u8 raw_sector[RAW_SECTOR_SIZE]; - if (m_current_index->file) - { - if (std::fread(raw_sector, RAW_SECTOR_SIZE, 1, m_current_index->file) != 1) - { - Log_ErrorPrintf("Read of LBA %u failed", m_position_on_disc); - Seek(m_position_on_disc); - return false; - } - } - else - { - // This in an implicit pregap. Return silence. - std::fill(raw_sector, raw_sector + RAW_SECTOR_SIZE, u8(0)); - } + if (!ReadRawSector(raw_sector)) + break; switch (read_mode) { @@ -176,9 +156,10 @@ bool CDImage::ReadRawSector(void* buffer) return false; } - if (m_current_index->file) + if (m_current_index->file_sector_size > 0) { - if (std::fread(buffer, RAW_SECTOR_SIZE, 1, m_current_index->file) != 1) + // TODO: This is where we'd reconstruct the header for other mode tracks. + if (!ReadSectorFromIndex(buffer, *m_current_index, m_position_in_index)) { Log_ErrorPrintf("Read of LBA %u failed", m_position_on_disc); Seek(m_position_on_disc); diff --git a/src/common/cd_image.h b/src/common/cd_image.h index 62fb1597a..707cafc46 100644 --- a/src/common/cd_image.h +++ b/src/common/cd_image.h @@ -1,14 +1,11 @@ #pragma once #include "bitfield.h" #include "types.h" -#include #include #include #include #include -class ByteStream; - class CDImage { public: @@ -211,7 +208,7 @@ protected: struct Index { u64 file_offset; - std::FILE* file; + u32 file_index; u32 file_sector_size; LBA start_lba_on_disc; u32 track_number; @@ -223,6 +220,9 @@ protected: bool is_pregap; }; + // Reads a single sector from an index. + virtual bool ReadSectorFromIndex(void* buffer, const Index& index, LBA lba_in_index) = 0; + const Index* GetIndexForDiscPosition(LBA pos); const Index* GetIndexForTrackPosition(u32 track_number, LBA track_pos); diff --git a/src/common/cd_image_bin.cpp b/src/common/cd_image_bin.cpp index 2be68ab4f..26479387a 100644 --- a/src/common/cd_image_bin.cpp +++ b/src/common/cd_image_bin.cpp @@ -14,8 +14,12 @@ public: bool ReadSubChannelQ(SubChannelQ* subq) override; +protected: + bool ReadSectorFromIndex(void* buffer, const Index& index, LBA lba_in_index) override; + private: std::FILE* m_fp = nullptr; + u64 m_file_position = 0; CDSubChannelReplacement m_sbi; }; @@ -78,7 +82,7 @@ bool CDImageBin::Open(const char* filename) // Data index. Index data_index = {}; - data_index.file = m_fp; + data_index.file_index = 0; data_index.file_offset = 0; data_index.file_sector_size = track_sector_size; data_index.start_lba_on_disc = pregap_index.length; @@ -107,6 +111,27 @@ bool CDImageBin::ReadSubChannelQ(SubChannelQ* subq) return CDImage::ReadSubChannelQ(subq); } +bool CDImageBin::ReadSectorFromIndex(void* buffer, const Index& index, LBA lba_in_index) +{ + const u64 file_position = index.file_offset + (static_cast(lba_in_index) * index.file_sector_size); + if (m_file_position != file_position) + { + if (std::fseek(m_fp, static_cast(file_position), SEEK_SET) != 0) + return false; + + m_file_position = file_position; + } + + if (std::fread(buffer, index.file_sector_size, 1, m_fp) != 1) + { + std::fseek(m_fp, static_cast(m_file_position), SEEK_SET); + return false; + } + + m_file_position += index.file_sector_size; + return true; +} + std::unique_ptr CDImage::OpenBinImage(const char* filename) { std::unique_ptr image = std::make_unique(); diff --git a/src/common/cd_image_cue.cpp b/src/common/cd_image_cue.cpp index 57e0e711c..a59bb2db2 100644 --- a/src/common/cd_image_cue.cpp +++ b/src/common/cd_image_cue.cpp @@ -18,9 +18,20 @@ public: bool ReadSubChannelQ(SubChannelQ* subq) override; +protected: + bool ReadSectorFromIndex(void* buffer, const Index& index, LBA lba_in_index) override; + private: Cd* m_cd = nullptr; - std::map m_files; + + struct TrackFile + { + std::string filename; + std::FILE* file; + u64 file_position; + }; + + std::vector m_files; CDSubChannelReplacement m_sbi; }; @@ -28,7 +39,7 @@ CDImageCueSheet::CDImageCueSheet() = default; CDImageCueSheet::~CDImageCueSheet() { - std::for_each(m_files.begin(), m_files.end(), [](const auto& it) { std::fclose(it.second); }); + std::for_each(m_files.begin(), m_files.end(), [](TrackFile& t) { std::fclose(t.file); }); cd_delete(m_cd); } @@ -67,8 +78,14 @@ bool CDImageCueSheet::OpenAndParse(const char* filename) long track_start = track_get_start(track); long track_length = track_get_length(track); - auto it = m_files.find(track_filename); - if (it == m_files.end()) + u32 track_file_index = 0; + for (; track_file_index < m_files.size(); track_file_index++) + { + const TrackFile& t = m_files[track_file_index]; + if (t.filename == track_filename) + break; + } + if (track_file_index == m_files.size()) { std::string track_full_filename = basepath + track_filename; std::FILE* track_fp = FileSystem::OpenCFile(track_full_filename.c_str(), "rb"); @@ -79,7 +96,7 @@ bool CDImageCueSheet::OpenAndParse(const char* filename) return false; } - it = m_files.emplace(track_filename, track_fp).first; + m_files.push_back(TrackFile{std::move(track_filename), track_fp, 0}); } // data type determines the sector size @@ -96,9 +113,9 @@ bool CDImageCueSheet::OpenAndParse(const char* filename) // determine the length from the file if (track_length < 0) { - std::fseek(it->second, 0, SEEK_END); - long file_size = std::ftell(it->second); - std::fseek(it->second, 0, SEEK_SET); + std::fseek(m_files[track_file_index].file, 0, SEEK_END); + long file_size = std::ftell(m_files[track_file_index].file); + std::fseek(m_files[track_file_index].file, 0, SEEK_SET); file_size /= track_sector_size; Assert(track_start < file_size); @@ -125,7 +142,7 @@ bool CDImageCueSheet::OpenAndParse(const char* filename) pregap_index.is_pregap = true; if (pregap_in_file) { - pregap_index.file = it->second; + pregap_index.file_index = track_file_index; pregap_index.file_offset = static_cast(static_cast(track_start - pregap_frames)) * track_sector_size; pregap_index.file_sector_size = track_sector_size; } @@ -146,7 +163,7 @@ bool CDImageCueSheet::OpenAndParse(const char* filename) last_index.start_lba_in_track = 0; last_index.track_number = track_num; last_index.index_number = 1; - last_index.file = it->second; + last_index.file_index = track_file_index; last_index.file_sector_size = track_sector_size; last_index.file_offset = static_cast(static_cast(track_start)) * track_sector_size; last_index.mode = mode; @@ -204,6 +221,30 @@ bool CDImageCueSheet::ReadSubChannelQ(SubChannelQ* subq) return CDImage::ReadSubChannelQ(subq); } +bool CDImageCueSheet::ReadSectorFromIndex(void* buffer, const Index& index, LBA lba_in_index) +{ + DebugAssert(index.file_index < m_files.size()); + + TrackFile& tf = m_files[index.file_index]; + const u64 file_position = index.file_offset + (static_cast(lba_in_index) * index.file_sector_size); + if (tf.file_position != file_position) + { + if (std::fseek(tf.file, static_cast(file_position), SEEK_SET) != 0) + return false; + + tf.file_position = file_position; + } + + if (std::fread(buffer, index.file_sector_size, 1, tf.file) != 1) + { + std::fseek(tf.file, static_cast(tf.file_position), SEEK_SET); + return false; + } + + tf.file_position += index.file_sector_size; + return true; +} + std::unique_ptr CDImage::OpenCueSheetImage(const char* filename) { std::unique_ptr image = std::make_unique();