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


      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 #include "cd_subchannel_replacement.h"
      6 
      7 #include "common/assert.h"
      8 #include "common/error.h"
      9 #include "common/file_system.h"
     10 #include "common/log.h"
     11 #include "common/path.h"
     12 
     13 #include <algorithm>
     14 #include <cerrno>
     15 #include <map>
     16 #include <sstream>
     17 
     18 Log_SetChannel(CDImageMemory);
     19 
     20 namespace {
     21 
     22 class CDImageM3u : public CDImage
     23 {
     24 public:
     25   CDImageM3u();
     26   ~CDImageM3u() override;
     27 
     28   bool Open(const char* path, bool apply_patches, Error* Error);
     29 
     30   bool ReadSubChannelQ(SubChannelQ* subq, const Index& index, LBA lba_in_index) override;
     31   bool HasNonStandardSubchannel() const override;
     32 
     33   bool HasSubImages() const override;
     34   u32 GetSubImageCount() const override;
     35   u32 GetCurrentSubImage() const override;
     36   std::string GetSubImageMetadata(u32 index, std::string_view type) const override;
     37   bool SwitchSubImage(u32 index, Error* error) override;
     38 
     39 protected:
     40   bool ReadSectorFromIndex(void* buffer, const Index& index, LBA lba_in_index) override;
     41 
     42 private:
     43   struct Entry
     44   {
     45     // TODO: Worth storing any other data?
     46     std::string filename;
     47     std::string title;
     48   };
     49 
     50   std::vector<Entry> m_entries;
     51   std::unique_ptr<CDImage> m_current_image;
     52   u32 m_current_image_index = UINT32_C(0xFFFFFFFF);
     53   bool m_apply_patches = false;
     54 };
     55 
     56 } // namespace
     57 
     58 CDImageM3u::CDImageM3u() = default;
     59 
     60 CDImageM3u::~CDImageM3u() = default;
     61 
     62 bool CDImageM3u::Open(const char* path, bool apply_patches, Error* error)
     63 {
     64   std::FILE* fp = FileSystem::OpenSharedCFile(path, "rb", FileSystem::FileShareMode::DenyWrite, error);
     65   if (!fp)
     66     return false;
     67 
     68   std::optional<std::string> m3u_file(FileSystem::ReadFileToString(fp));
     69   std::fclose(fp);
     70   if (!m3u_file.has_value() || m3u_file->empty())
     71   {
     72     Error::SetString(error, "Failed to read M3u file");
     73     return false;
     74   }
     75 
     76   std::istringstream ifs(m3u_file.value());
     77   m_filename = path;
     78   m_apply_patches = apply_patches;
     79 
     80   std::vector<std::string> entries;
     81   std::string line;
     82   while (std::getline(ifs, line))
     83   {
     84     u32 start_offset = 0;
     85     while (start_offset < line.size() && std::isspace(line[start_offset]))
     86       start_offset++;
     87 
     88     // skip comments
     89     if (start_offset == line.size() || line[start_offset] == '#')
     90       continue;
     91 
     92     // strip ending whitespace
     93     u32 end_offset = static_cast<u32>(line.size()) - 1;
     94     while (std::isspace(line[end_offset]) && end_offset > start_offset)
     95       end_offset--;
     96 
     97     // anything?
     98     if (start_offset == end_offset)
     99       continue;
    100 
    101     Entry entry;
    102     std::string entry_filename =
    103       Path::ToNativePath(std::string_view(line.begin() + start_offset, line.begin() + end_offset + 1));
    104     entry.title = Path::GetFileTitle(entry_filename);
    105     if (!Path::IsAbsolute(entry_filename))
    106       entry.filename = Path::BuildRelativePath(path, entry_filename);
    107     else
    108       entry.filename = std::move(entry_filename);
    109 
    110     DEV_LOG("Read path from m3u: '{}'", entry.filename);
    111     m_entries.push_back(std::move(entry));
    112   }
    113 
    114   INFO_LOG("Loaded {} paths from m3u '{}'", m_entries.size(), path);
    115   return !m_entries.empty() && SwitchSubImage(0, error);
    116 }
    117 
    118 bool CDImageM3u::HasNonStandardSubchannel() const
    119 {
    120   return m_current_image->HasNonStandardSubchannel();
    121 }
    122 
    123 bool CDImageM3u::HasSubImages() const
    124 {
    125   return true;
    126 }
    127 
    128 u32 CDImageM3u::GetSubImageCount() const
    129 {
    130   return static_cast<u32>(m_entries.size());
    131 }
    132 
    133 u32 CDImageM3u::GetCurrentSubImage() const
    134 {
    135   return m_current_image_index;
    136 }
    137 
    138 bool CDImageM3u::SwitchSubImage(u32 index, Error* error)
    139 {
    140   if (index >= m_entries.size())
    141     return false;
    142   else if (index == m_current_image_index)
    143     return true;
    144 
    145   const Entry& entry = m_entries[index];
    146   std::unique_ptr<CDImage> new_image = CDImage::Open(entry.filename.c_str(), m_apply_patches, error);
    147   if (!new_image)
    148   {
    149     ERROR_LOG("Failed to load subimage {} ({})", index, entry.filename);
    150     return false;
    151   }
    152 
    153   CopyTOC(new_image.get());
    154   m_current_image = std::move(new_image);
    155   m_current_image_index = index;
    156   if (!Seek(1, Position{0, 0, 0}))
    157     Panic("Failed to seek to start after sub-image change.");
    158 
    159   return true;
    160 }
    161 
    162 std::string CDImageM3u::GetSubImageMetadata(u32 index, std::string_view type) const
    163 {
    164   if (index >= m_entries.size())
    165     return {};
    166 
    167   if (type == "title")
    168     return m_entries[index].title;
    169   else if (type == "file_title")
    170     return std::string(Path::GetFileTitle(m_entries[index].filename));
    171 
    172   return CDImage::GetSubImageMetadata(index, type);
    173 }
    174 
    175 bool CDImageM3u::ReadSectorFromIndex(void* buffer, const Index& index, LBA lba_in_index)
    176 {
    177   return m_current_image->ReadSectorFromIndex(buffer, index, lba_in_index);
    178 }
    179 
    180 bool CDImageM3u::ReadSubChannelQ(SubChannelQ* subq, const Index& index, LBA lba_in_index)
    181 {
    182   return m_current_image->ReadSubChannelQ(subq, index, lba_in_index);
    183 }
    184 
    185 std::unique_ptr<CDImage> CDImage::OpenM3uImage(const char* filename, bool apply_patches, Error* error)
    186 {
    187   std::unique_ptr<CDImageM3u> image = std::make_unique<CDImageM3u>();
    188   if (!image->Open(filename, apply_patches, error))
    189     return {};
    190 
    191   return image;
    192 }