CDImage: Support loading subchannel from LSD files

This commit is contained in:
Stenzek 2023-11-15 19:05:21 +10:00
parent 7aa4180e17
commit bc485ee55b
No known key found for this signature in database
9 changed files with 71 additions and 23 deletions

View file

@ -99,7 +99,7 @@ bool CDImageBin::Open(const char* filename, Error* error)
AddLeadOutIndex(); AddLeadOutIndex();
m_sbi.LoadSBIFromImagePath(filename); m_sbi.LoadFromImagePath(filename);
return Seek(1, Position{0, 0, 0}); return Seek(1, Position{0, 0, 0});
} }

View file

@ -413,7 +413,7 @@ bool CDImageCHD::Open(const char* filename, Error* error)
m_lba_count = disc_lba; m_lba_count = disc_lba;
AddLeadOutIndex(); AddLeadOutIndex();
m_sbi.LoadSBIFromImagePath(filename); m_sbi.LoadFromImagePath(filename);
return Seek(1, Position{0, 0, 0}); return Seek(1, Position{0, 0, 0});
} }

View file

@ -293,7 +293,7 @@ bool CDImageCueSheet::OpenAndParse(const char* filename, Error* error)
m_lba_count = disc_lba; m_lba_count = disc_lba;
AddLeadOutIndex(); AddLeadOutIndex();
m_sbi.LoadSBIFromImagePath(filename); m_sbi.LoadFromImagePath(filename);
return Seek(1, Position{0, 0, 0}); return Seek(1, Position{0, 0, 0});
} }

View file

@ -400,7 +400,7 @@ bool CDImageEcm::Open(const char* filename, Error* error)
AddLeadOutIndex(); AddLeadOutIndex();
m_sbi.LoadSBIFromImagePath(filename); m_sbi.LoadFromImagePath(filename);
m_chunk_buffer.reserve(RAW_SECTOR_SIZE * 2); m_chunk_buffer.reserve(RAW_SECTOR_SIZE * 2);
return Seek(1, Position{0, 0, 0}); return Seek(1, Position{0, 0, 0});

View file

@ -234,7 +234,7 @@ bool CDImageMds::OpenAndParse(const char* filename, Error* error)
m_lba_count = m_tracks.back().start_lba + m_tracks.back().length; m_lba_count = m_tracks.back().start_lba + m_tracks.back().length;
AddLeadOutIndex(); AddLeadOutIndex();
m_sbi.LoadSBIFromImagePath(filename); m_sbi.LoadFromImagePath(filename);
return Seek(1, Position{0, 0, 0}); return Seek(1, Position{0, 0, 0});
} }

View file

@ -115,7 +115,7 @@ bool CDImageMemory::CopyImage(CDImage* image, ProgressCallback* progress)
m_filename = image->GetFileName(); m_filename = image->GetFileName();
m_lba_count = image->GetLBACount(); m_lba_count = image->GetLBACount();
m_sbi.LoadSBI(Path::ReplaceExtension(m_filename, "sbi").c_str()); m_sbi.LoadFromImagePath(m_filename);
return Seek(1, Position{0, 0, 0}); return Seek(1, Position{0, 0, 0});
} }

View file

@ -668,12 +668,13 @@ bool CDImagePBP::OpenDisc(u32 index, Error* error)
if (m_disc_offsets.size() > 1) if (m_disc_offsets.size() > 1)
{ {
std::string sbi_path(Path::StripExtension(m_filename)); const std::string offset_path = fmt::format("{}_{}.pbp", Path::StripExtension(m_filename), index + 1);
sbi_path += TinyString::from_fmt("_%u.sbi", index + 1).view(); m_sbi.LoadFromImagePath(offset_path);
m_sbi.LoadSBI(sbi_path.c_str());
} }
else else
m_sbi.LoadSBI(Path::ReplaceExtension(m_filename, "sbi").c_str()); {
m_sbi.LoadFromImagePath(Path::ReplaceExtension(m_filename, "sbi"));
}
m_current_disc = index; m_current_disc = index;
return Seek(1, Position{0, 0, 0}); return Seek(1, Position{0, 0, 0});

View file

@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: 2019-2022 Connor McLaughlin <stenzek@gmail.com> // SPDX-FileCopyrightText: 2019-2023 Connor McLaughlin <stenzek@gmail.com>
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0) // SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
#include "cd_subchannel_replacement.h" #include "cd_subchannel_replacement.h"
@ -18,6 +18,14 @@ struct SBIFileEntry
u8 type; u8 type;
u8 data[10]; u8 data[10];
}; };
struct LSDFileEntry
{
u8 minute_bcd;
u8 second_bcd;
u8 frame_bcd;
u8 data[12];
};
static_assert(sizeof(LSDFileEntry) == 15);
#pragma pack(pop) #pragma pack(pop)
CDSubChannelReplacement::CDSubChannelReplacement() = default; CDSubChannelReplacement::CDSubChannelReplacement() = default;
@ -33,23 +41,23 @@ static constexpr u32 MSFToLBA(u8 minute_bcd, u8 second_bcd, u8 frame_bcd)
return (ZeroExtend32(minute) * 60 * 75) + (ZeroExtend32(second) * 75) + ZeroExtend32(frame); return (ZeroExtend32(minute) * 60 * 75) + (ZeroExtend32(second) * 75) + ZeroExtend32(frame);
} }
bool CDSubChannelReplacement::LoadSBI(const char* path) bool CDSubChannelReplacement::LoadSBI(const std::string& path)
{ {
auto fp = FileSystem::OpenManagedCFile(path, "rb"); auto fp = FileSystem::OpenManagedCFile(path.c_str(), "rb");
if (!fp) if (!fp)
return false; return false;
char header[4]; char header[4];
if (std::fread(header, sizeof(header), 1, fp.get()) != 1) if (std::fread(header, sizeof(header), 1, fp.get()) != 1)
{ {
Log_ErrorPrintf("Failed to read header for '%s'", path); Log_ErrorFmt("Failed to read header for '{}'", path);
return true; return true;
} }
static constexpr char expected_header[] = {'S', 'B', 'I', '\0'}; static constexpr char expected_header[] = {'S', 'B', 'I', '\0'};
if (std::memcmp(header, expected_header, sizeof(header)) != 0) if (std::memcmp(header, expected_header, sizeof(header)) != 0)
{ {
Log_ErrorPrintf("Invalid header in '%s'", path); Log_ErrorFmt("Invalid header in '{}'", path);
return true; return true;
} }
@ -59,14 +67,14 @@ bool CDSubChannelReplacement::LoadSBI(const char* path)
if (!IsValidPackedBCD(entry.minute_bcd) || !IsValidPackedBCD(entry.second_bcd) || if (!IsValidPackedBCD(entry.minute_bcd) || !IsValidPackedBCD(entry.second_bcd) ||
!IsValidPackedBCD(entry.frame_bcd)) !IsValidPackedBCD(entry.frame_bcd))
{ {
Log_ErrorPrintf("Invalid position [%02x:%02x:%02x] in '%s'", entry.minute_bcd, entry.second_bcd, entry.frame_bcd, Log_ErrorFmt("Invalid position [{:02x}:{:02x}:{:02x}] in '{}'", entry.minute_bcd, entry.second_bcd,
path); entry.frame_bcd, path);
return false; return false;
} }
if (entry.type != 1) if (entry.type != 1)
{ {
Log_ErrorPrintf("Invalid type 0x%02X in '%s'", entry.type, path); Log_ErrorFmt("Invalid type 0x{:02X} in '{}'", entry.type, path);
return false; return false;
} }
@ -83,13 +91,50 @@ bool CDSubChannelReplacement::LoadSBI(const char* path)
m_replacement_subq.emplace(lba, subq); m_replacement_subq.emplace(lba, subq);
} }
Log_InfoPrintf("Loaded %zu replacement sectors from '%s'", m_replacement_subq.size(), path); Log_InfoFmt("Loaded {} replacement sectors from SBI '{}'", m_replacement_subq.size(), path);
return true; return true;
} }
bool CDSubChannelReplacement::LoadSBIFromImagePath(const char* image_path) bool CDSubChannelReplacement::LoadLSD(const std::string& path)
{ {
return LoadSBI(Path::ReplaceExtension(image_path, "sbi").c_str()); auto fp = FileSystem::OpenManagedCFile(path.c_str(), "rb");
if (!fp)
return false;
LSDFileEntry entry;
while (std::fread(&entry, sizeof(entry), 1, fp.get()) == 1)
{
if (!IsValidPackedBCD(entry.minute_bcd) || !IsValidPackedBCD(entry.second_bcd) ||
!IsValidPackedBCD(entry.frame_bcd))
{
Log_ErrorFmt("Invalid position [{:02x}:{:02x}:{:02x}] in '{}'", entry.minute_bcd, entry.second_bcd,
entry.frame_bcd, path);
return false;
}
const u32 lba = MSFToLBA(entry.minute_bcd, entry.second_bcd, entry.frame_bcd);
CDImage::SubChannelQ subq;
std::copy_n(entry.data, countof(entry.data), subq.data.data());
Log_DebugFmt("{:02x}:{:02x}:{:02x}: CRC {}", entry.minute_bcd, entry.second_bcd, entry.frame_bcd,
subq.IsCRCValid() ? "VALID" : "INVALID");
m_replacement_subq.emplace(lba, subq);
}
Log_InfoFmt("Loaded {} replacement sectors from LSD '{}'", m_replacement_subq.size(), path);
return true;
}
bool CDSubChannelReplacement::LoadFromImagePath(std::string_view image_path)
{
if (const std::string filename = Path::ReplaceExtension(image_path, "sbi"); LoadSBI(filename.c_str()))
return true;
if (const std::string filename = Path::ReplaceExtension(image_path, "lsd"); LoadLSD(filename.c_str()))
return true;
return false;
} }
void CDSubChannelReplacement::AddReplacementSubChannelQ(u32 lba, const CDImage::SubChannelQ& subq) void CDSubChannelReplacement::AddReplacementSubChannelQ(u32 lba, const CDImage::SubChannelQ& subq)

View file

@ -16,8 +16,7 @@ public:
u32 GetReplacementSectorCount() const { return static_cast<u32>(m_replacement_subq.size()); } u32 GetReplacementSectorCount() const { return static_cast<u32>(m_replacement_subq.size()); }
bool LoadSBI(const char* path); bool LoadFromImagePath(std::string_view image_path);
bool LoadSBIFromImagePath(const char* image_path);
/// Adds a sector to the replacement map. /// Adds a sector to the replacement map.
void AddReplacementSubChannelQ(u32 lba, const CDImage::SubChannelQ& subq); void AddReplacementSubChannelQ(u32 lba, const CDImage::SubChannelQ& subq);
@ -31,5 +30,8 @@ public:
private: private:
using ReplacementMap = std::unordered_map<u32, CDImage::SubChannelQ>; using ReplacementMap = std::unordered_map<u32, CDImage::SubChannelQ>;
bool LoadSBI(const std::string& path);
bool LoadLSD(const std::string& path);
ReplacementMap m_replacement_subq; ReplacementMap m_replacement_subq;
}; };