#pragma once #include "util/File.h" #include "block_dev.hpp" #include "util/endian.hpp" #include "util/types.hpp" #include #include #include namespace iso { #pragma pack(push, 1) template struct le_be_pair { le_t le; be_t be; T value() const { if constexpr (std::endian::native == std::endian::little) { return le; } else { return be; } } }; struct PrimaryVolumeDescriptorDateTime { char year[4]; char month[2]; char day[2]; char hour[2]; char minute[2]; char second[2]; char milliseconds[2]; s8 gmt_offset; }; struct VolumeHeader { u8 type; char standard_id[5]; u8 version; }; struct DirDateTime { u8 year; // + 1900 u8 month; u8 day; u8 hour; u8 minute; u8 second; s8 gmt_offset; struct tm to_tm() const { struct tm time{}; time.tm_year = year; time.tm_mon = month; time.tm_mday = day; time.tm_hour = hour; time.tm_min = minute; time.tm_sec = second; auto set_tm_gmtoff2 = [](auto& t, s8 gmt_offset) { if constexpr (requires { t.tm_gmtoff = gmt_offset; }) { t.tm_gmtoff = gmt_offset; } }; set_tm_gmtoff2(time, gmt_offset); return time; } time_t to_time_t() const { auto tm = to_tm(); return mktime(&tm); } }; enum class DirEntryFlags : u8 { None = 0, Hidden = 1 << 0, Directory = 1 << 1, File = 1 << 2, ExtAttr = 1 << 3, Permissions = 1 << 4, }; constexpr DirEntryFlags operator&(DirEntryFlags lhs, DirEntryFlags rhs) { return static_cast(static_cast(lhs) & static_cast(rhs)); } constexpr DirEntryFlags operator|(DirEntryFlags lhs, DirEntryFlags rhs) { return static_cast(static_cast(lhs) | static_cast(rhs)); } struct DirEntry { u8 entry_length; u8 ext_attr_length; le_be_pair lba; le_be_pair length; DirDateTime create_time; DirEntryFlags flags; u8 interleave_unit_size; u8 interleave_gap_size; le_be_pair sequence; u8 filename_length; fs::stat_t to_fs_stat() const { fs::stat_t result{}; result.is_directory = (flags & iso::DirEntryFlags::Directory) != iso::DirEntryFlags::None; result.size = length.value(); result.ctime = create_time.to_time_t(); result.mtime = result.ctime; result.atime = result.ctime; return result; } fs::dir_entry to_fs_entry(std::string name) const { fs::dir_entry entry = {}; static_cast(entry) = to_fs_stat(); entry.name = std::move(name); return entry; } }; struct PrimaryVolumeDescriptor { VolumeHeader header; uint8_t pad0; char system_id[32]; char volume_id[32]; char pad1[8]; le_be_pair block_count; char pad2[32]; le_be_pair volume_set_size; le_be_pair vol_seq_num; le_be_pair block_size; le_be_pair path_table_size; le_t path_table_block_le; le_t ext_path_table_block_le; be_t path_table_block_be; be_t ext_path_table_block_be; DirEntry root; u8 pad3; uint8_t volume_set_id[128]; uint8_t publisher_id[128]; uint8_t data_preparer_id[128]; uint8_t application_id[128]; uint8_t copyright_file_id[37]; uint8_t abstract_file_id[37]; uint8_t bibliographical_file_id[37]; PrimaryVolumeDescriptorDateTime vol_creation_time; PrimaryVolumeDescriptorDateTime vol_modification_time; PrimaryVolumeDescriptorDateTime vol_expire_time; PrimaryVolumeDescriptorDateTime vol_effective_time; uint8_t version; uint8_t pad4; uint8_t app_used[512]; u32 path_table_block() const { if constexpr (std::endian::native == std::endian::little) { return path_table_block_le; } else { return path_table_block_be; } } }; struct PathTableEntryHeader { u8 name_length; u8 ext_attr_length; le_t location; le_t parent_id; }; enum class StringEncoding { ascii, utf16_be, }; #pragma pack(pop) } // namespace iso class iso_dev final : public fs::device_base { std::unique_ptr m_dev; iso::DirEntry m_root_dir; iso::StringEncoding m_encoding = iso::StringEncoding::ascii; public: iso_dev() = default; static std::optional open(std::unique_ptr device) { iso_dev result; result.m_dev = std::move(device); if (!result.initialize()) { return {}; } return result; } bool stat(const std::string& path, fs::stat_t& info) override; bool statfs(const std::string& path, fs::device_stat& info) override; std::unique_ptr open(const std::string& path, bs_t mode) override; std::unique_ptr open_dir(const std::string& path) override; private: bool initialize(); std::optional open_entry(std::string_view path); std::pair, std::vector> read_dir(const iso::DirEntry& entry); fs::file read_file(const iso::DirEntry& entry); };