From 43d01755e61da415cc12b3215688d23d912fe185 Mon Sep 17 00:00:00 2001 From: Olivier Galibert Date: Mon, 27 Jun 2022 10:26:36 +0200 Subject: [PATCH] fs: new API, blk_t is probably going to change too --- src/lib/formats/fs_coco_os9.cpp | 691 ++--------------------- src/lib/formats/fs_coco_rsdos.cpp | 431 +-------------- src/lib/formats/fs_oric_jasmin.cpp | 851 ++++++++++++++--------------- src/lib/formats/fs_oric_jasmin.h | 2 - src/lib/formats/fs_prodos.cpp | 359 +++++------- src/lib/formats/fs_vtech.cpp | 359 +++++++----- src/lib/formats/fs_vtech.h | 62 --- src/lib/formats/fsblk_vec.cpp | 2 +- src/lib/formats/fsmgr.cpp | 80 +-- src/lib/formats/fsmgr.h | 137 ++--- src/tools/floptool.cpp | 137 ++--- 11 files changed, 953 insertions(+), 2158 deletions(-) diff --git a/src/lib/formats/fs_coco_os9.cpp b/src/lib/formats/fs_coco_os9.cpp index 3813255482c..c4fcd14ea16 100644 --- a/src/lib/formats/fs_coco_os9.cpp +++ b/src/lib/formats/fs_coco_os9.cpp @@ -18,147 +18,22 @@ #include "strformat.h" -//************************************************************************** -// TYPE DECLARATIONS -//************************************************************************** +using namespace fs; + +namespace fs { const coco_os9_image COCO_OS9; } namespace { -using namespace fs; - -// methods -std::string pick_os9_string(std::string_view raw_string); -std::string to_os9_string(std::string_view s, size_t length); -util::arbitrary_datetime from_os9_date(u32 os9_date, u16 os9_time = 0); -std::tuple to_os9_date(const util::arbitrary_datetime &datetime); -u32 pick_integer_be(const u8 *data, int length); -bool validate_filename(std::string_view name); -bool is_ignored_filename(std::string_view name); -std::vector entity_meta_description(); - - -// ======================> volume_header - -class volume_header -{ +class coco_os9_impl : public filesystem_t { public: - volume_header(fsblk_t::block_t &&block); + coco_os9_impl(fsblk_t &blockdev); + virtual ~coco_os9_impl() = default; - u32 total_sectors() const { return m_block.r24b(0); } - u8 track_size_in_sectors() const { return m_block.r8(3); } - u16 allocation_bitmap_bytes() const { return m_block.r16b(4); } - u16 cluster_size() const { return m_block.r16b(6); } - u32 root_dir_lsn() const { return m_block.r24b(8); } - u16 owner_id() const { return m_block.r16b(11); } - u16 disk_id() const { return m_block.r16b(14); } - u8 format_flags() const { return m_block.r8(16); } - u16 sectors_per_track() const { return m_block.r16b(17); } - u32 bootstrap_lsn() const { return m_block.r24b(21); } - u16 bootstrap_size() const { return m_block.r16b(24); } - util::arbitrary_datetime creation_date() const { return from_os9_date(m_block.r24b(26), m_block.r16b(29)); } - u16 sector_size() const { u16 result = m_block.r16b(104); return result != 0 ? result : 256; } - u8 sides() const { return (format_flags() & 0x01) ? 2 : 1; } - bool double_density() const { return (format_flags() & 0x02) != 0; } - bool double_track() const { return (format_flags() & 0x04) != 0; } - bool quad_track_density() const { return (format_flags() & 0x08) != 0; } - bool octal_track_density() const { return (format_flags() & 0x10) != 0; } - - std::string name() const; - -private: - fsblk_t::block_t m_block; + static bool is_ignored_filename(std::string_view name); + static bool validate_filename(std::string_view name); }; - - -// ======================> file_header - -class file_header -{ -public: - file_header(fsblk_t::block_t &&block); - - u8 attributes() const { return m_block.r8(0); } - u16 owner_id() const { return m_block.r16b(1); } - u8 link_count() const { return m_block.r8(8); } - u32 file_size() const { return m_block.r32b(9); } - util::arbitrary_datetime creation_date() const; - bool is_directory() const { return (attributes() & 0x80) != 0; } - bool is_non_sharable() const { return (attributes() & 0x40) != 0; } - bool is_public_execute() const { return (attributes() & 0x20) != 0; } - bool is_public_write() const { return (attributes() & 0x10) != 0; } - bool is_public_read() const { return (attributes() & 0x08) != 0; } - bool is_user_execute() const { return (attributes() & 0x04) != 0; } - bool is_user_write() const { return (attributes() & 0x02) != 0; } - bool is_user_read() const { return (attributes() & 0x01) != 0; } - - meta_data metadata() const; - int get_sector_map_entry_count() const; - void get_sector_map_entry(int entry_number, u32 &start_lsn, u16 &count) const; - -private: - fsblk_t::block_t m_block; -}; - - -// ======================> impl - -class impl : public filesystem_t { -public: - class file : public ifile_t { - public: - file(impl &fs, file_header &&file_header); - virtual ~file() = default; - - virtual void drop_weak_references() override; - - virtual meta_data metadata() override; - virtual std::vector read_all() override; - - private: - impl &m_fs; - file_header m_file_header; - }; - - class directory : public idir_t { - public: - directory(impl &i, file_header &&file_header); - virtual ~directory() = default; - - virtual void drop_weak_references() override; - virtual meta_data metadata() override; - virtual std::vector contents() override; - virtual file_t file_get(u64 key) override; - virtual dir_t dir_get(u64 key) override; - - private: - impl &m_fs; - file_header m_file_header; - }; - - impl(fsblk_t &blockdev, volume_header &&header); - virtual ~impl() = default; - - virtual meta_data metadata() override; - virtual dir_t root() override; - virtual void format(const meta_data &meta) override; - -private: - volume_header m_volume_header; - dir_t m_root; - - directory *open_directory(u32 lsn); - std::vector read_file_data(const file_header &header) const; -}; - } -namespace fs { - -const coco_os9_image COCO_OS9; - -}; - - //************************************************************************** // IMPLEMENTATION //************************************************************************** @@ -167,7 +42,7 @@ const coco_os9_image COCO_OS9; // name //------------------------------------------------- -const char *fs::coco_os9_image::name() const +const char *coco_os9_image::name() const { return "coco_os9"; } @@ -177,7 +52,7 @@ const char *fs::coco_os9_image::name() const // description //------------------------------------------------- -const char *fs::coco_os9_image::description() const +const char *coco_os9_image::description() const { return "CoCo OS-9"; } @@ -187,7 +62,7 @@ const char *fs::coco_os9_image::description() const // enumerate_f //------------------------------------------------- -void fs::coco_os9_image::enumerate_f(floppy_enumerator &fe, u32 form_factor, const std::vector &variants) const +void coco_os9_image::enumerate_f(floppy_enumerator &fe, u32 form_factor, const std::vector &variants) const { if (has(form_factor, variants, floppy_image::FF_525, floppy_image::SSDD)) { @@ -201,7 +76,7 @@ void fs::coco_os9_image::enumerate_f(floppy_enumerator &fe, u32 form_factor, con // can_format //------------------------------------------------- -bool fs::coco_os9_image::can_format() const +bool coco_os9_image::can_format() const { return true; } @@ -211,7 +86,7 @@ bool fs::coco_os9_image::can_format() const // can_read //------------------------------------------------- -bool fs::coco_os9_image::can_read() const +bool coco_os9_image::can_read() const { return true; } @@ -221,7 +96,7 @@ bool fs::coco_os9_image::can_read() const // can_write //------------------------------------------------- -bool fs::coco_os9_image::can_write() const +bool coco_os9_image::can_write() const { return false; } @@ -231,7 +106,7 @@ bool fs::coco_os9_image::can_write() const // has_rsrc //------------------------------------------------- -bool fs::coco_os9_image::has_rsrc() const +bool coco_os9_image::has_rsrc() const { return false; } @@ -241,7 +116,7 @@ bool fs::coco_os9_image::has_rsrc() const // directory_separator //------------------------------------------------- -char fs::coco_os9_image::directory_separator() const +char coco_os9_image::directory_separator() const { return '/'; } @@ -251,7 +126,7 @@ char fs::coco_os9_image::directory_separator() const // volume_meta_description //------------------------------------------------- -std::vector fs::coco_os9_image::volume_meta_description() const +std::vector coco_os9_image::volume_meta_description() const { std::vector results; results.emplace_back(meta_description(meta_name::name, "UNTITLED", false, [](const meta_value &m) { return m.as_string().size() <= 32; }, "Volume name, up to 32 characters")); @@ -264,51 +139,10 @@ std::vector fs::coco_os9_image::volume_meta_description() cons // file_meta_description //------------------------------------------------- -std::vector fs::coco_os9_image::file_meta_description() const -{ - return entity_meta_description(); -} - - -//------------------------------------------------- -// directory_meta_description -//------------------------------------------------- - -std::vector fs::coco_os9_image::directory_meta_description() const -{ - return entity_meta_description(); -} - - -//------------------------------------------------- -// mount -//------------------------------------------------- - -std::unique_ptr fs::coco_os9_image::mount(fsblk_t &blockdev) const -{ - // read the header block - blockdev.set_block_size(256); - volume_header header(blockdev.get(0)); - - // sanity checks - if (header.sectors_per_track() != header.track_size_in_sectors()) - return { }; - - // create the implementation - return std::make_unique(blockdev, std::move(header)); -} - - -//------------------------------------------------- -// entity_meta_description -//------------------------------------------------- - -namespace { - -std::vector entity_meta_description() +std::vector coco_os9_image::file_meta_description() const { std::vector results; - results.emplace_back(meta_description(meta_name::name, "", false, [](const meta_value &m) { return validate_filename(m.as_string()); }, "File name")); + results.emplace_back(meta_description(meta_name::name, "", false, [](const meta_value &m) { return coco_os9_impl::validate_filename(m.as_string()); }, "File name")); results.emplace_back(meta_description(meta_name::creation_date, util::arbitrary_datetime::now(), false, nullptr, "Creation time")); results.emplace_back(meta_description(meta_name::owner_id, 0, true, nullptr, "Owner ID")); results.emplace_back(meta_description(meta_name::attributes, "", true, nullptr, "File attributes")); @@ -318,107 +152,38 @@ std::vector entity_meta_description() //------------------------------------------------- -// pick_os9_string +// directory_meta_description //------------------------------------------------- -std::string pick_os9_string(std::string_view raw_string) +std::vector coco_os9_image::directory_meta_description() const { - // find the last NUL or high bit character - auto iter = std::find_if(raw_string.begin(), raw_string.end(), [](char ch) - { - return ch == '\0' || ch & 0x80; - }); - - // get the preliminary result - std::string result(raw_string.begin(), iter); - - // and add the final character if we have to - if (iter < raw_string.end() && *iter & 0x80) - result.append(1, *iter & 0x7F); - return result; - + return file_meta_description(); } - //------------------------------------------------- -// to_os9_string +// mount //------------------------------------------------- -std::string to_os9_string(std::string_view s, size_t length) +std::unique_ptr coco_os9_image::mount(fsblk_t &blockdev) const { - std::string result(length, '\0'); - for (auto i = 0; i < std::min(length, s.size()); i++) - { - result[i] = (s[i] & 0x7F) - | (i == s.size() ? 0x80 : 0x00); - } - return result; + return std::make_unique(blockdev); } - //------------------------------------------------- -// pick_integer_be +// impl ctor //------------------------------------------------- -u32 pick_integer_be(const u8 *data, int length) +coco_os9_impl::coco_os9_impl(fsblk_t &blockdev) + : filesystem_t(blockdev, 256) { - u32 result = 0; - for (int i = 0; i < length; i++) - result |= u32(data[length - i - 1]) << i * 8; - return result; } - -//------------------------------------------------- -// from_os9_date -//------------------------------------------------- - -util::arbitrary_datetime from_os9_date(u32 os9_date, u16 os9_time) -{ - util::arbitrary_datetime dt; - memset(&dt, 0, sizeof(dt)); - dt.year = ((os9_date >> 16) & 0xFF) + 1900; - dt.month = (os9_date >> 8) & 0xFF; - dt.day_of_month = (os9_date >> 0) & 0xFF; - dt.hour = (os9_time >> 8) & 0xFF; - dt.minute = (os9_time >> 0) & 0xFF; - return dt; -} - - -//------------------------------------------------- -// to_os9_date -//------------------------------------------------- - -std::tuple to_os9_date(const util::arbitrary_datetime &datetime) -{ - u32 os9_date = ((datetime.year - 1900) & 0xFF) << 16 - | (datetime.month & 0xFF) << 8 - | (datetime.day_of_month & 0xFF) << 0; - u16 os9_time = (datetime.hour & 0xFF) << 8 - | (datetime.minute & 0xFF) << 0; - return std::make_tuple(os9_date, os9_time); -} - - -//------------------------------------------------- -// validate_filename -//------------------------------------------------- - -bool validate_filename(std::string_view name) -{ - return !is_ignored_filename(name) - && name.size() <= 29 - && std::find_if(name.begin(), name.end(), [](const char ch) { return ch == '\0' || ch == '/' || ch >= 0x80; }) == name.end(); -} - - //------------------------------------------------- // is_ignored_filename - should this file name be // ignored if it is in the file system? //------------------------------------------------- -bool is_ignored_filename(std::string_view name) +bool coco_os9_impl::is_ignored_filename(std::string_view name) { return name.empty() || name[0] == '\0' @@ -426,401 +191,13 @@ bool is_ignored_filename(std::string_view name) || name == ".."; } - //------------------------------------------------- -// volume_header ctor +// validate_filename //------------------------------------------------- -volume_header::volume_header(fsblk_t::block_t &&block) - : m_block(std::move(block)) +bool coco_os9_impl::validate_filename(std::string_view name) { + return !is_ignored_filename(name) + && name.size() <= 29 + && std::find_if(name.begin(), name.end(), [](const char ch) { return ch == '\0' || ch == '/' || ch >= 0x80; }) == name.end(); } - - -//------------------------------------------------- -// volume_header::name -//------------------------------------------------- - -std::string volume_header::name() const -{ - std::string_view raw_name((const char *)&m_block.rodata()[31], 32); - return pick_os9_string(raw_name); -} - - -//------------------------------------------------- -// file_header ctor -//------------------------------------------------- - -file_header::file_header(fsblk_t::block_t &&block) - : m_block(std::move(block)) -{ -} - - -//------------------------------------------------- -// file_header::creation_date -//------------------------------------------------- - -util::arbitrary_datetime file_header::creation_date() const -{ - return from_os9_date(m_block.r24b(13)); -} - - -//------------------------------------------------- -// file_header::metadata -//------------------------------------------------- - -meta_data file_header::metadata() const -{ - // format the attributes - std::string attributes = util::string_format("%c%c%c%c%c%c%c%c", - is_directory() ? 'd' : '-', - is_non_sharable() ? 's' : '-', - is_public_execute() ? 'x' : '-', - is_public_write() ? 'w' : '-', - is_public_read() ? 'r' : '-', - is_user_execute() ? 'x' : '-', - is_user_write() ? 'w' : '-', - is_user_read() ? 'r' : '-'); - - meta_data result; - result.set(meta_name::creation_date, creation_date()); - result.set(meta_name::owner_id, owner_id()); - result.set(meta_name::attributes, std::move(attributes)); - result.set(meta_name::length, file_size()); - return result; -} - - -//------------------------------------------------- -// file_header::get_sector_map_entry_count -//------------------------------------------------- - -int file_header::get_sector_map_entry_count() const -{ - return (m_block.size() - 16) / 5; -} - - -//------------------------------------------------- -// file_header::get_sector_map_entry -//------------------------------------------------- - -void file_header::get_sector_map_entry(int entry_number, u32 &start_lsn, u16 &count) const -{ - start_lsn = m_block.r24b(16 + (entry_number * 5) + 0); - count = m_block.r16b(16 + (entry_number * 5) + 3); -} - - -//------------------------------------------------- -// impl ctor -//------------------------------------------------- - -impl::impl(fsblk_t &blockdev, volume_header &&header) - : filesystem_t(blockdev, 256) - , m_volume_header(std::move(header)) -{ -} - - -//------------------------------------------------- -// impl::metadata -//------------------------------------------------- - -meta_data impl::metadata() -{ - meta_data results; - results.set(meta_name::name, m_volume_header.name()); - results.set(meta_name::creation_date, m_volume_header.creation_date()); - return results; -} - - -//------------------------------------------------- -// impl::root -//------------------------------------------------- - -filesystem_t::dir_t impl::root() -{ - if (!m_root) - m_root = open_directory(m_volume_header.root_dir_lsn()); - return m_root.strong(); -} - - -//------------------------------------------------- -// impl::format -//------------------------------------------------- - -void impl::format(const meta_data &meta) -{ - // for some reason, the OS-9 world favored filling with 0xE5 - m_blockdev.fill(0xE5); - - // identify geometry info - u8 sectors = 18; // TODO - we need a definitive technique to get the floppy geometry - u8 heads = 1; // TODO - we need a definitive technique to get the floppy geometry - u16 sector_bytes = 256; // TODO - we need a definitive technique to get the floppy geometry - bool is_double_density = true; // TODO - we need a definitive technique to get the floppy geometry - u32 tracks = m_blockdev.block_count() / sectors / heads; - - // get attributes from metadata - std::string volume_title = meta.get_string(meta_name::name, "UNTITLED"); - util::arbitrary_datetime creation_datetime = meta.get_date(meta_name::creation_date); - auto [creation_os9date, creation_os9time] = to_os9_date(creation_datetime); - - u32 lsn_count = m_blockdev.block_count(); - u16 cluster_size = 1; - u16 owner_id = 1; - u16 disk_id = 1; - u8 attributes = 0; - u32 allocation_bitmap_bits = lsn_count / cluster_size; - u32 allocation_bitmap_lsns = (allocation_bitmap_bits / 8 + sector_bytes - 1) / sector_bytes; - u8 format_flags = ((heads > 1) ? 0x01 : 0x00) - | (is_double_density ? 0x02 : 0x00); - - // volume header - auto volume_header = m_blockdev.get(0); - volume_header.fill(0x00); - volume_header.w24b(0, lsn_count); // DD.TOT - total secctors - volume_header.w8(3, sectors); // DD.TKS - track size in sectors - volume_header.w16b(4, (allocation_bitmap_bits + 7) / 8); // DD.MAP - allocation bitmap in bytes - volume_header.w16b(6, cluster_size); // DD.BIT - cluster size - volume_header.w24b(8, 1 + allocation_bitmap_lsns); // DD.DIR - root directory LSN - volume_header.w16b(11, owner_id); // DD.OWN - owner ID - volume_header.w8(13, attributes); // DD.ATT - Dattributes - volume_header.w16b(14, disk_id); // DD.DSK - disk ID - volume_header.w8(16, format_flags); // DD.FMT - format flags - volume_header.w16b(17, sectors); // DD.SPT - sectors per track - volume_header.w24b(26, creation_os9date); // DD.DAT - date of creation - volume_header.w16b(29, creation_os9time); // DD.DAT - time of creation - volume_header.wstr(31, to_os9_string(volume_title, 32)); // DD.NAM - title - volume_header.w16b(103, sector_bytes / 256); // sector bytes - - // path descriptor options - volume_header.w8(0x3f + 0x00, 1); // device class - volume_header.w8(0x3f + 0x01, 1); // drive number - volume_header.w8(0x3f + 0x03, 0x20); // device type - volume_header.w8(0x3f + 0x04, 1); // density capability - volume_header.w16b(0x3f + 0x05, tracks); // number of tracks - volume_header.w8(0x3f + 0x07, heads); // number of sides - volume_header.w16b(0x3f + 0x09, sectors); // sectors per track - volume_header.w16b(0x3f + 0x0b, sectors); // sectors on track zero - volume_header.w8(0x3f + 0x0d, 3); // sector interleave factor - volume_header.w8(0x3f + 0x0e, 8); // default sectors per allocation - - // allocation bitmap - u32 total_allocated_sectors = 1 + allocation_bitmap_lsns + 1 + 8; - std::vector abblk_bytes; - abblk_bytes.resize(sector_bytes); - for (u32 i = 0; i < allocation_bitmap_lsns; i++) - { - for (u32 j = 0; j < sector_bytes; j++) - { - u32 pos = (i * sector_bytes + j) * 8; - if (pos + 8 < total_allocated_sectors) - abblk_bytes[j] = 0xFF; - else if (pos >= total_allocated_sectors) - abblk_bytes[j] = 0x00; - else - abblk_bytes[j] = ~((1 << (8 - total_allocated_sectors + pos)) - 1); - } - - auto abblk = m_blockdev.get(1 + i); - abblk.copy(0, abblk_bytes.data(), sector_bytes); - } - - // root directory header - auto roothdr_blk = m_blockdev.get(1 + allocation_bitmap_lsns); - roothdr_blk.fill(0x00); - roothdr_blk.w8(0x00, 0xBF); - roothdr_blk.w8(0x01, 0x00); - roothdr_blk.w8(0x02, 0x00); - roothdr_blk.w24b(0x03, creation_os9date); - roothdr_blk.w16b(0x06, creation_os9time); - roothdr_blk.w8(0x08, 0x01); - roothdr_blk.w8(0x09, 0x00); - roothdr_blk.w8(0x0A, 0x00); - roothdr_blk.w8(0x0B, 0x00); - roothdr_blk.w8(0x0C, 0x40); - roothdr_blk.w24b(0x0D, creation_os9date); - roothdr_blk.w24b(0x10, 1 + allocation_bitmap_lsns + 1); - roothdr_blk.w16b(0x13, 8); - - // root directory data - auto rootdata_blk = m_blockdev.get(1 + allocation_bitmap_lsns + 1); - rootdata_blk.fill(0x00); - rootdata_blk.w8(0x00, 0x2E); - rootdata_blk.w8(0x01, 0xAE); - rootdata_blk.w8(0x1F, 1 + allocation_bitmap_lsns); - rootdata_blk.w8(0x20, 0xAE); - rootdata_blk.w8(0x3F, 1 + allocation_bitmap_lsns); -} - - -//------------------------------------------------- -// impl::open_directory -//------------------------------------------------- - -impl::directory *impl::open_directory(u32 lsn) -{ - file_header header(m_blockdev.get(lsn)); - return new directory(*this, std::move(header)); -} - - -//------------------------------------------------- -// impl::read_file_data -//------------------------------------------------- - -std::vector impl::read_file_data(const file_header &header) const -{ - std::vector data; - data.reserve(header.file_size()); - int entry_count = header.get_sector_map_entry_count(); - for (int i = 0; i < entry_count; i++) - { - u32 start_lsn; - u16 count; - header.get_sector_map_entry(i, start_lsn, count); - - for (u32 lsn = start_lsn; lsn < start_lsn + count; lsn++) - { - auto block = m_blockdev.get(lsn); - size_t block_size = std::min(std::min(u32(m_volume_header.sector_size()), block.size()), header.file_size() - u32(data.size())); - for (auto i = 0; i < block_size; i++) - data.push_back(block.rodata()[i]); - } - } - return data; -} - - -//------------------------------------------------- -// file ctor -//------------------------------------------------- - -impl::file::file(impl &i, file_header &&file_header) - : m_fs(i) - , m_file_header(std::move(file_header)) -{ -} - - -//------------------------------------------------- -// file::drop_weak_references -//------------------------------------------------- - -void impl::file::drop_weak_references() -{ -} - - -//------------------------------------------------- -// file::metadata -//------------------------------------------------- - -meta_data impl::file::metadata() -{ - return m_file_header.metadata(); -} - - -//------------------------------------------------- -// file::read_all -//------------------------------------------------- - -std::vector impl::file::read_all() -{ - return m_fs.read_file_data(m_file_header); -} - - -//------------------------------------------------- -// directory ctor -//------------------------------------------------- - -impl::directory::directory(impl &i, file_header &&file_header) - : m_fs(i) - , m_file_header(std::move(file_header)) -{ -} - - -//------------------------------------------------- -// directory::drop_weak_references -//------------------------------------------------- - -void impl::directory::drop_weak_references() -{ -} - - -//------------------------------------------------- -// directory::metadata -//------------------------------------------------- - -meta_data impl::directory::metadata() -{ - return m_file_header.metadata(); -} - - -//------------------------------------------------- -// directory::contents -//------------------------------------------------- - -std::vector impl::directory::contents() -{ - // read the directory data - std::vector directory_data = m_fs.read_file_data(m_file_header); - - // and assemble results - std::vector results; - int directory_count = directory_data.size() / 32; - for (int i = 0; i < directory_count; i++) - { - // determine the filename - std::string_view raw_filename((const char *) &directory_data[i * 32], 29); - std::string filename = pick_os9_string(raw_filename); - if (is_ignored_filename(filename)) - continue; - - // determine the entry type - u32 lsn = pick_integer_be(&directory_data[i * 32] + 29, 3); - file_header file_header(m_fs.m_blockdev.get(lsn)); - dir_entry_type entry_type = file_header.is_directory() - ? dir_entry_type::dir - : dir_entry_type::file; - - // and return the results - results.emplace_back(std::move(filename), entry_type, lsn); - } - return results; -} - - -//------------------------------------------------- -// directory::file_get -//------------------------------------------------- - -filesystem_t::file_t impl::directory::file_get(u64 key) -{ - file_header header(m_fs.m_blockdev.get(u32(key))); - return file_t(new file(m_fs, std::move(header))); -} - - -//------------------------------------------------- -// directory::dir_get -//------------------------------------------------- - -filesystem_t::dir_t impl::directory::dir_get(u64 key) -{ - return dir_t(m_fs.open_directory(u32(key))); -} - -}; diff --git a/src/lib/formats/fs_coco_rsdos.cpp b/src/lib/formats/fs_coco_rsdos.cpp index 3829159938f..6028ded3467 100644 --- a/src/lib/formats/fs_coco_rsdos.cpp +++ b/src/lib/formats/fs_coco_rsdos.cpp @@ -17,104 +17,27 @@ #include #include -namespace fs { - const coco_rsdos_image COCO_RSDOS; -}; - using namespace fs; +namespace fs { const coco_rsdos_image COCO_RSDOS; } + namespace { -class impl : public filesystem_t { +class coco_rsdos_impl : public filesystem_t { public: - class root_dir : public idir_t { - public: - root_dir(impl &i) : m_fs(i) {} - virtual ~root_dir() = default; + coco_rsdos_impl(fsblk_t &blockdev); + virtual ~coco_rsdos_impl() = default; - virtual void drop_weak_references() override; - virtual meta_data metadata() override; - virtual std::vector contents() override; - virtual file_t file_get(u64 key) override; - virtual dir_t dir_get(u64 key) override; - - private: - impl &m_fs; - }; - - struct rsdos_dirent - { - char m_filename[11]; - u8 m_filetype; - u8 m_asciiflag; - u8 m_first_granule; - u8 m_last_sector_bytes_msb; - u8 m_last_sector_bytes_lsb; - }; - - struct rsdos_dirent_sector - { - struct - { - rsdos_dirent m_dirent; - u8 m_unused[16]; - } m_entries[4]; - }; - - class granule_iterator { - public: - granule_iterator(impl &fs, const rsdos_dirent &dirent); - bool next(u8 &granule, u16 &byte_count); - - private: - fsblk_t::block_t m_granule_map; - std::optional m_current_granule; - u8 m_maximum_granules; - u16 m_last_sector_bytes; - std::bitset<256> m_visited_granules; - }; - - class file : public ifile_t { - public: - file(impl &fs, rsdos_dirent &&dirent); - virtual ~file() = default; - - virtual void drop_weak_references() override; - - virtual meta_data metadata() override; - virtual std::vector read_all() override; - - private: - impl &m_fs; - rsdos_dirent m_dirent; - }; - - impl(fsblk_t &blockdev); - virtual ~impl() = default; - - virtual meta_data metadata() override; - virtual dir_t root() override; - virtual void format(const meta_data &meta) override; - -private: - dir_t m_root; - - void drop_root_ref(); - fsblk_t::block_t read_sector(int track, int sector) const; - u8 maximum_granules() const; - static std::string get_filename_from_dirent(const rsdos_dirent &dirent); -}; - -// methods -bool validate_filename(std::string_view name); + static bool validate_filename(std::string_view name); }; +} //------------------------------------------------- // name //------------------------------------------------- -const char *fs::coco_rsdos_image::name() const +const char *coco_rsdos_image::name() const { return "coco_rsdos"; } @@ -124,7 +47,7 @@ const char *fs::coco_rsdos_image::name() const // description //------------------------------------------------- -const char *fs::coco_rsdos_image::description() const +const char *coco_rsdos_image::description() const { return "CoCo RS-DOS"; } @@ -134,7 +57,7 @@ const char *fs::coco_rsdos_image::description() const // enumerate_f //------------------------------------------------- -void fs::coco_rsdos_image::enumerate_f(floppy_enumerator &fe, u32 form_factor, const std::vector &variants) const +void coco_rsdos_image::enumerate_f(floppy_enumerator &fe, u32 form_factor, const std::vector &variants) const { if (has(form_factor, variants, floppy_image::FF_525, floppy_image::SSDD)) { @@ -148,7 +71,7 @@ void fs::coco_rsdos_image::enumerate_f(floppy_enumerator &fe, u32 form_factor, c // can_format //------------------------------------------------- -bool fs::coco_rsdos_image::can_format() const +bool coco_rsdos_image::can_format() const { return true; } @@ -158,7 +81,7 @@ bool fs::coco_rsdos_image::can_format() const // can_read //------------------------------------------------- -bool fs::coco_rsdos_image::can_read() const +bool coco_rsdos_image::can_read() const { return true; } @@ -168,7 +91,7 @@ bool fs::coco_rsdos_image::can_read() const // can_write //------------------------------------------------- -bool fs::coco_rsdos_image::can_write() const +bool coco_rsdos_image::can_write() const { return false; } @@ -178,7 +101,7 @@ bool fs::coco_rsdos_image::can_write() const // has_rsrc //------------------------------------------------- -bool fs::coco_rsdos_image::has_rsrc() const +bool coco_rsdos_image::has_rsrc() const { return false; } @@ -188,10 +111,10 @@ bool fs::coco_rsdos_image::has_rsrc() const // file_meta_description //------------------------------------------------- -std::vector fs::coco_rsdos_image::file_meta_description() const +std::vector coco_rsdos_image::file_meta_description() const { std::vector results; - results.emplace_back(meta_description(meta_name::name, "", false, [](const meta_value &m) { return validate_filename(m.as_string()); }, "File name, 8.3")); + results.emplace_back(meta_description(meta_name::name, "", false, [](const meta_value &m) { return coco_rsdos_impl::validate_filename(m.as_string()); }, "File name, 8.3")); results.emplace_back(meta_description(meta_name::file_type, 0, true, nullptr, "Type of the file")); results.emplace_back(meta_description(meta_name::ascii_flag, "B", true, nullptr, "Ascii or binary flag")); results.emplace_back(meta_description(meta_name::size_in_blocks, 0, true, nullptr, "Number of granules used by the file")); @@ -204,9 +127,9 @@ std::vector fs::coco_rsdos_image::file_meta_description() cons // mount //------------------------------------------------- -std::unique_ptr fs::coco_rsdos_image::mount(fsblk_t &blockdev) const +std::unique_ptr coco_rsdos_image::mount(fsblk_t &blockdev) const { - return std::make_unique(blockdev); + return std::make_unique(blockdev); } @@ -214,9 +137,7 @@ std::unique_ptr fs::coco_rsdos_image::mount(fsblk_t &blockdev) con // validate_filename //------------------------------------------------- -namespace { - -bool validate_filename(std::string_view name) +bool coco_rsdos_impl::validate_filename(std::string_view name) { auto pos = name.find('.'); auto stem_length = pos != std::string::npos ? pos : name.size(); @@ -229,321 +150,9 @@ bool validate_filename(std::string_view name) // impl ctor //------------------------------------------------- -impl::impl(fsblk_t &blockdev) +coco_rsdos_impl::coco_rsdos_impl(fsblk_t &blockdev) : filesystem_t(blockdev, 256) { } -//------------------------------------------------- -// impl::metadata -//------------------------------------------------- - -meta_data impl::metadata() -{ - return meta_data(); -} - - -//------------------------------------------------- -// impl::root -//------------------------------------------------- - -filesystem_t::dir_t impl::root() -{ - if (!m_root) - m_root = new root_dir(*this); - return m_root.strong(); -} - - -//------------------------------------------------- -// impl::drop_root_ref -//------------------------------------------------- - -void impl::drop_root_ref() -{ - m_root = nullptr; -} - - -//------------------------------------------------- -// impl::format -//------------------------------------------------- - -void impl::format(const meta_data &meta) -{ - // formatting RS-DOS is easy - just fill everything with 0xFF - m_blockdev.fill(0xFF); -} - - -//------------------------------------------------- -// fsblk_t::block_t impl::read_sector -//------------------------------------------------- - -fsblk_t::block_t impl::read_sector(int track, int sector) const -{ - // the CoCo RS-DOS world thinks in terms of tracks/sectors, but we have a block device - // abstraction - return m_blockdev.get(track * 18 + sector - 1); -} - - -//------------------------------------------------- -// impl::maximum_granules -//------------------------------------------------- - -u8 impl::maximum_granules() const -{ - u32 sector_count = m_blockdev.block_count(); - u32 granule_count = (sector_count / 9) - 2; - return granule_count <= 0xFF ? u8(granule_count) : 0xFF; -} - - -//------------------------------------------------- -// impl::get_filename_from_dirent -//------------------------------------------------- - -std::string impl::get_filename_from_dirent(const rsdos_dirent &dirent) -{ - std::string_view stem = strtrimrightspace(std::string_view(&dirent.m_filename[0], 8)); - std::string_view ext = strtrimrightspace(std::string_view(&dirent.m_filename[8], 3)); - return util::string_format("%s.%s", stem, ext); -} - - -//------------------------------------------------- -// impl::root_dir::drop_weak_references -//------------------------------------------------- - -void impl::root_dir::drop_weak_references() -{ - m_fs.drop_root_ref(); -} - - -//------------------------------------------------- -// impl::root_dir::metadata -//------------------------------------------------- - -meta_data impl::root_dir::metadata() -{ - return meta_data(); -} - - -//------------------------------------------------- -// impl::root_dir::contents -//------------------------------------------------- - -std::vector impl::root_dir::contents() -{ - u64 key = 0; - std::vector results; - for (int dir_sector = 3; dir_sector <= 18; dir_sector++) - { - // read this directory sector - auto dir_block = m_fs.read_sector(17, dir_sector); - const rsdos_dirent_sector §or = *reinterpret_cast(dir_block.rodata()); - - // and loop through all entries - for (const auto &ent : sector.m_entries) - { - // 0xFF marks the end of the directory - if (ent.m_dirent.m_filename[0] == '\xFF') - return results; - - // 0x00 marks a deleted file - if (ent.m_dirent.m_filename[0] != '\0') - results.emplace_back(get_filename_from_dirent(ent.m_dirent), dir_entry_type::file, key); - - key++; - } - } - return results; -} - - -//------------------------------------------------- -// impl::root_dir::file_get -//------------------------------------------------- - -filesystem_t::file_t impl::root_dir::file_get(u64 key) -{ - auto dir_block = m_fs.read_sector(17, 3 + key / 4); - const rsdos_dirent_sector §or = *reinterpret_cast(dir_block.rodata()); - const rsdos_dirent &ent = sector.m_entries[key % 4].m_dirent; - return file_t(new file(m_fs, rsdos_dirent(ent))); -} - - -//------------------------------------------------- -// impl::root_dir::dir_get -//------------------------------------------------- - -filesystem_t::dir_t impl::root_dir::dir_get(u64 key) -{ - throw std::logic_error("Directories not supported"); -} - - -//------------------------------------------------- -// impl::granule_iterator ctor -//------------------------------------------------- - -impl::granule_iterator::granule_iterator(impl &fs, const rsdos_dirent &dirent) - : m_granule_map(fs.read_sector(17, 2)) - , m_current_granule(dirent.m_first_granule) - , m_maximum_granules(fs.maximum_granules()) - , m_last_sector_bytes((u16(dirent.m_last_sector_bytes_msb) << 8) | dirent.m_last_sector_bytes_lsb) -{ - // visit the first granule - m_visited_granules.set(dirent.m_first_granule); -} - - -//------------------------------------------------- -// impl::granule_iterator::next -//------------------------------------------------- - -bool impl::granule_iterator::next(u8 &granule, u16 &byte_count) -{ - bool success = false; - granule = ~0; - byte_count = 0; - - if (m_current_granule) - { - std::optional next_granule; - const u8 *granule_map_data = m_granule_map.rodata(); - if (granule_map_data[*m_current_granule] < m_maximum_granules) - { - // this entry points to the next granule - success = true; - granule = *m_current_granule; - byte_count = 9 * 256; - next_granule = granule_map_data[*m_current_granule]; - - // check for cycles, which should only happen if the disk is corrupt (or not in RS-DOS format) - if (m_visited_granules[*next_granule]) - next_granule = std::nullopt; // this is corrupt! - else - m_visited_granules.set(*next_granule); - } - else if (granule_map_data[*m_current_granule] >= 0xC0 && granule_map_data[*m_current_granule] <= 0xC9) - { - // this is the last granule in the file - success = true; - granule = *m_current_granule; - u16 sector_count = std::max(granule_map_data[*m_current_granule], u8(0xC1)) - 0xC1; - byte_count = sector_count * 256 + m_last_sector_bytes; - next_granule = std::nullopt; - } - else - { - // should not happen; but we'll treat this as an EOF - next_granule = std::nullopt; - } - m_current_granule = next_granule; - } - return success; -} - - -//------------------------------------------------- -// impl::file ctor -//------------------------------------------------- - -impl::file::file(impl &fs, rsdos_dirent &&dirent) - : m_fs(fs) - , m_dirent(std::move(dirent)) -{ -} - - -//------------------------------------------------- -// impl::file::drop_weak_references -//------------------------------------------------- - -void impl::file::drop_weak_references() -{ -} - - -//------------------------------------------------- -// impl::file::metadata -//------------------------------------------------- - -meta_data impl::file::metadata() -{ - u32 file_size = 0; - int granule_count = 0; - - // we need to iterate on the file to determine the size and granule/block count - u8 granule; - u16 byte_count; - granule_iterator iter(m_fs, m_dirent); - while (iter.next(granule, byte_count)) - { - granule_count++; - file_size += byte_count; - } - - // turn the ASCII flag to a single character (this reflects what the user sees when doing a directory listing on a real CoCo) - char file_type_char = 'B' + m_dirent.m_asciiflag; - - // build the metadata and return it - meta_data result; - result.set(meta_name::name, get_filename_from_dirent(m_dirent)); - result.set(meta_name::file_type, m_dirent.m_filetype); - result.set(meta_name::ascii_flag, std::string(1, file_type_char)); - result.set(meta_name::size_in_blocks, granule_count); - result.set(meta_name::length, file_size); - return result; -} - - -//------------------------------------------------- -// impl::file::read_all -//------------------------------------------------- - -std::vector impl::file::read_all() -{ - std::vector result; - - u8 granule; - u16 byte_count; - granule_iterator iter(m_fs, m_dirent); - while (iter.next(granule, byte_count)) - { - // resize the results - size_t current_size = result.size(); - result.resize(current_size + byte_count); - - // determine which track and sector this granule starts at - int track = granule / 2 + (granule >= 34 ? 1 : 0); - int sector = granule % 2 * 9 + 1; - - // and read all the sectors - while (byte_count > 0) - { - // read this sector - auto block = m_fs.read_sector(track, sector); - const u8 *data = block.rodata(); - u16 data_length = std::min(byte_count, u16(256)); - - // and append it to the results - memcpy(result.data() + current_size, data, data_length); - - // and advance - current_size += data_length; - byte_count -= data_length; - sector++; - } - } - return result; -} - -} diff --git a/src/lib/formats/fs_oric_jasmin.cpp b/src/lib/formats/fs_oric_jasmin.cpp index 361f40332ea..147e49e123e 100644 --- a/src/lib/formats/fs_oric_jasmin.cpp +++ b/src/lib/formats/fs_oric_jasmin.cpp @@ -46,120 +46,105 @@ namespace fs { const oric_jasmin_image ORIC_JASMIN; } // offset 06+ : reference to data sectors, (ff, ff) when done namespace { -class impl : public filesystem_t { + +class oric_jasmin_impl : public filesystem_t { public: - impl(fsblk_t &blockdev); - virtual ~impl() = default; + oric_jasmin_impl(fsblk_t &blockdev); + virtual ~oric_jasmin_impl() = default; - virtual void format(const meta_data &meta) override; - virtual meta_data metadata() override; - virtual void metadata_change(const meta_data &info) override; - virtual dir_t root() override; + virtual meta_data volume_metadata() override; + virtual err_t volume_metadata_change(const meta_data &info) override; + virtual std::pair metadata(const std::vector &path) override; + virtual err_t metadata_change(const std::vector &path, const meta_data &meta) override; + virtual std::pair> directory_contents(const std::vector &path) override; + virtual err_t rename(const std::vector &opath, const std::vector &npath) override; + virtual err_t remove(const std::vector &path) override; + + virtual err_t file_create(const std::vector &path, const meta_data &meta) override; + + virtual std::pair> file_read(const std::vector &path) override; + virtual err_t file_write(const std::vector &path, const std::vector &data) override; + + virtual err_t format(const meta_data &meta) override; + + static bool validate_filename(std::string name); + +private: static u32 cs_to_block(u16 ref); [[maybe_unused]] static u16 block_to_cs(u32 block); bool ref_valid(u16 ref); static std::string read_file_name(const u8 *p); - void drop_root_ref(); std::vector allocate_blocks(u32 count); void free_blocks(const std::vector &blocks); u32 free_block_count(); + static std::string file_name_read(const u8 *p); static std::string file_name_prepare(std::string name); - -private: - dir_t m_root; - - class root_dir : public idir_t { - public: - root_dir(impl &i) : m_fs(i) {} - virtual ~root_dir() = default; - - virtual void drop_weak_references() override; - - virtual meta_data metadata() override; - virtual void metadata_change(const meta_data &info) override; - virtual std::vector contents() override; - virtual file_t file_get(u64 key) override; - virtual dir_t dir_get(u64 key) override; - virtual file_t file_create(const meta_data &info) override; - virtual void file_delete(u64 key) override; - - void update_file(u16 key, const u8 *entry); - - private: - impl &m_fs; - - std::pair get_dir_block(u64 key); - }; - - class file : public impl::ifile_t { - public: - file(impl &fs, root_dir *dir, const u8 *entry, u16 key); - virtual ~file() = default; - - virtual void drop_weak_references() override; - - virtual meta_data metadata() override; - virtual void metadata_change(const meta_data &info) override; - virtual std::vector read_all() override; - virtual void replace(const std::vector &data) override; - - private: - impl &m_fs; - root_dir *m_dir; - u16 m_key; - u8 m_entry[18]; - }; - - class system_file : public impl::ifile_t { - public: - system_file(impl &fs, root_dir *dir, const u8 *entry, u16 key); - virtual ~system_file() = default; - - virtual void drop_weak_references() override; - - virtual meta_data metadata() override; - virtual void metadata_change(const meta_data &info) override; - virtual std::vector read_all() override; - virtual void replace(const std::vector &data) override; - - private: - impl &m_fs; - root_dir *m_dir; - u16 m_key; - u8 m_entry[18]; - }; + static bool file_is_system(const u8 *entry); + meta_data file_metadata(const u8 *entry); + std::tuple file_find(std::string name); }; } -meta_data impl::metadata() -{ - meta_data res; - auto bdir = m_blockdev.get(20*17); - int len = 8; - while(len > 0 && bdir.rodata()[0xf8 + len - 1] == ' ') - len--; - res.set(meta_name::name, bdir.rstr(0xf8, len)); - return res; +const char *oric_jasmin_image::name() const +{ + return "oric_jasmin"; } -bool oric_jasmin_image::validate_filename(std::string name) +const char *oric_jasmin_image::description() const { - auto pos = name.find('.'); - if(pos != std::string::npos) - return pos <= 8 && pos > 0 && name.size()-pos-1 <= 3; - else - return name.size() > 0 && name.size() <= 8; + return "Oric Jasmin"; +} + +void oric_jasmin_image::enumerate_f(floppy_enumerator &fe, u32 form_factor, const std::vector &variants) const +{ + if(has(form_factor, variants, floppy_image::FF_3, floppy_image::DSDD)) + fe.add(FLOPPY_ORIC_JASMIN_FORMAT, 356864, "oric_jasmin_ds", "Oric Jasmin dual-sided"); + if(has(form_factor, variants, floppy_image::FF_3, floppy_image::SSDD)) + fe.add(FLOPPY_ORIC_JASMIN_FORMAT, 178432, "oric_jasmin_ss", "Oric Jasmin single-sided"); +} + +std::unique_ptr oric_jasmin_image::mount(fsblk_t &blockdev) const +{ + return std::make_unique(blockdev); +} + +bool oric_jasmin_image::can_format() const +{ + return true; +} + +bool oric_jasmin_image::can_read() const +{ + return true; +} + +bool oric_jasmin_image::can_write() const +{ + return true; +} + +bool oric_jasmin_image::has_rsrc() const +{ + return false; +} + +std::vector oric_jasmin_image::volume_meta_description() const +{ + std::vector res; + res.emplace_back(meta_description(meta_name::name, "UNTITLED", false, [](const meta_value &m) { return m.as_string().size() <= 8; }, "Volume name, up to 8 characters")); + + return res; } std::vector oric_jasmin_image::file_meta_description() const { std::vector res; - res.emplace_back(meta_description(meta_name::name, "", false, [](const meta_value &m) { return validate_filename(m.as_string()); }, "File name, 8.3")); + res.emplace_back(meta_description(meta_name::name, "", false, [](const meta_value &m) { return oric_jasmin_impl::validate_filename(m.as_string()); }, "File name, 8.3")); res.emplace_back(meta_description(meta_name::loading_address, 0x501, false, [](const meta_value &m) { return m.as_number() < 0x10000; }, "Loading address of the file")); res.emplace_back(meta_description(meta_name::length, 0, true, nullptr, "Size of the file in bytes")); res.emplace_back(meta_description(meta_name::size_in_blocks, 0, true, nullptr, "Number of blocks used by the file")); @@ -169,7 +154,48 @@ std::vector oric_jasmin_image::file_meta_description() const } -void impl::format(const meta_data &meta) + +oric_jasmin_impl::oric_jasmin_impl(fsblk_t &blockdev) : filesystem_t(blockdev, 256) +{ +} + +bool oric_jasmin_impl::ref_valid(u16 ref) +{ + u8 track = ref >> 8; + u8 sector = ref & 0xff; + if(sector < 1 || sector > 17) + return false; + if(track >= m_blockdev.block_count()/17) + return false; + return true; +} + +u32 oric_jasmin_impl::cs_to_block(u16 ref) +{ + u8 track = ref >> 8; + if(track == 0xff) + abort(); + u8 sector = ref & 0xff; + return track * 17 + sector - 1; +} + +u16 oric_jasmin_impl::block_to_cs(u32 block) +{ + u8 track = block / 17; + u8 sector = (block % 17) + 1; + return (track << 8) | sector; +} + +bool oric_jasmin_impl::validate_filename(std::string name) +{ + auto pos = name.find('.'); + if(pos != std::string::npos) + return pos <= 8 && pos > 0 && name.size()-pos-1 <= 3; + else + return name.size() > 0 && name.size() <= 8; +} + +err_t oric_jasmin_impl::format(const meta_data &meta) { std::string volume_name = meta.get_string(meta_name::name, "UNTITLED"); u32 blocks = m_blockdev.block_count(); @@ -201,146 +227,33 @@ void impl::format(const meta_data &meta) bdir.fill(0xff); bdir.w16l(0, 0x0000); bdir.w16l(2, 0x0000); + + return ERR_OK; } -impl::impl(fsblk_t &blockdev) : filesystem_t(blockdev, 256), m_root(true) +meta_data oric_jasmin_impl::volume_metadata() { -} + meta_data res; + auto bdir = m_blockdev.get(20*17); + int len = 8; + while(len > 0 && bdir.rodata()[0xf8 + len - 1] == ' ') + len--; -bool impl::ref_valid(u16 ref) -{ - u8 track = ref >> 8; - u8 sector = ref & 0xff; - if(sector < 1 || sector > 17) - return false; - if(track >= m_blockdev.block_count()/17) - return false; - return true; -} - -u32 impl::cs_to_block(u16 ref) -{ - u8 track = ref >> 8; - u8 sector = ref & 0xff; - return track * 17 + sector - 1; -} - -u16 impl::block_to_cs(u32 block) -{ - u8 track = block / 17; - u8 sector = (block % 17) + 1; - return (track << 8) | sector; -} - -std::string impl::read_file_name(const u8 *p) -{ - int main_len; - for(main_len = 8; main_len > 0; main_len--) - if(p[main_len - 1] != ' ') - break; - int ext_len; - for(ext_len = 4; ext_len > 0; ext_len--) - if(p[ext_len + 8 - 1] != ' ') - break; - std::string name; - for(int i=0; i != main_len; i++) - name += char(p[i]); - for(int i=0; i != ext_len; i++) - name += char(p[8 + i]); - return name; -} - - -filesystem_t::dir_t impl::root() -{ - if(!m_root) - m_root = new root_dir(*this); - return m_root.strong(); -} - -void impl::drop_root_ref() -{ - m_root = nullptr; -} - -void impl::root_dir::drop_weak_references() -{ - m_fs.drop_root_ref(); -} - -meta_data impl::root_dir::metadata() -{ - return meta_data(); -} - -std::vector impl::root_dir::contents() -{ - std::vector res; - - auto bdir = m_fs.m_blockdev.get(20*17+1); - u64 id = 0; - for(;;) { - for(u32 i = 0; i != 14; i ++) { - u32 off = 4 + i*18; - u16 ref = bdir.r16b(off); - std::string fname = read_file_name(bdir.rodata()+off+3); - bool system = ref == 0 && id == 0 && bdir.r32b(off+0xb) == 0x2e535953; - if(system) - res.emplace_back(dir_entry(std::move(fname), dir_entry_type::system_file, 0)); - - else if(m_fs.ref_valid(ref)) - res.emplace_back(dir_entry(std::move(fname), dir_entry_type::file, id)); - - id++; - } - u16 ref = bdir.r16b(2); - if(!ref || !m_fs.ref_valid(ref)) - break; - bdir = m_fs.m_blockdev.get(cs_to_block(ref)); - } + res.set(meta_name::name, bdir.rstr(0xf8, len)); return res; } -std::pair impl::root_dir::get_dir_block(u64 key) +err_t oric_jasmin_impl::volume_metadata_change(const meta_data &meta) { - auto bdir = m_fs.m_blockdev.get(20*17+1); - while(key >= 14) { - u16 ref = bdir.r16b(2); - if(!ref || !m_fs.ref_valid(ref)) - throw std::invalid_argument("Incorrect file key"); - bdir = m_fs.m_blockdev.get(cs_to_block(ref)); - key -= 14; + if(meta.has(meta_name::name)) { + std::string volume_name = meta.get_string(meta_name::name); + volume_name.resize(8, ' '); + m_blockdev.get(20*17).wstr(0xf8, volume_name); } - return std::pair(bdir, 4 + key * 18); + return ERR_OK; } -filesystem_t::file_t impl::root_dir::file_get(u64 key) -{ - u64 rkey = key; - auto [bdir, off] = get_dir_block(rkey); - u16 ref = bdir.r16b(off); - bool system = ref == 0 && key == 0 && bdir.r32b(off+0xb) == 0x2e535953; - if(system) - return file_t(new system_file(m_fs, this, bdir.rodata() + off, key)); - - if(!m_fs.ref_valid(ref)) - throw std::invalid_argument("Key to deleted/non-existent file"); - return file_t(new file(m_fs, this, bdir.rodata() + off, key)); -} - -void impl::root_dir::update_file(u16 key, const u8 *entry) -{ - u64 rkey = key; - auto [bdir, off] = get_dir_block(rkey); - bdir.copy(off, entry, 18); -} - -filesystem_t::dir_t impl::root_dir::dir_get(u64 key) -{ - throw std::logic_error("Directories not supported"); -} - -std::string impl::file_name_prepare(std::string fname) +std::string oric_jasmin_impl::file_name_prepare(std::string fname) { std::string nname; size_t i; @@ -360,129 +273,198 @@ std::string impl::file_name_prepare(std::string fname) return nname; } -impl::file::file(impl &fs, root_dir *dir, const u8 *entry, u16 key) : m_fs(fs), m_dir(dir), m_key(key) +std::string oric_jasmin_impl::file_name_read(const u8 *p) { - memcpy(m_entry, entry, 18); -} - -void impl::file::drop_weak_references() -{ -} - -meta_data impl::file::metadata() -{ - meta_data res; - - res.set(meta_name::name, read_file_name(m_entry + 3)); - res.set(meta_name::locked, m_entry[2] == 'L'); - res.set(meta_name::sequential, m_entry[0xf] == 'S'); - res.set(meta_name::size_in_blocks, r16l(m_entry + 0x10)); - - u16 ref = r16b(m_entry); - auto dblk = m_fs.m_blockdev.get(cs_to_block(ref)); - res.set(meta_name::loading_address, dblk.r16l(2)); - res.set(meta_name::length, dblk.r16l(4)); - - return res; -} - -std::vector impl::file::read_all() -{ - std::vector data; - u16 ref = r16b(m_entry); - auto iblk = m_fs.m_blockdev.get(cs_to_block(ref)); - u32 length = iblk.r16l(4); - while(m_fs.ref_valid(ref)) { - for(u32 pos = 6; pos != 256 && data.size() < length; pos += 2) { - u16 dref = iblk.r16b(pos); - if(!m_fs.ref_valid(dref)) - goto done; - auto dblk = m_fs.m_blockdev.get(cs_to_block(dref)); - u32 dpos = data.size(); - data.resize(dpos + 256); - memcpy(data.data() + dpos, dblk.rodata(), 256); - if(data.size() >= length) - goto done; - } - ref = iblk.r16b(2); - if(!m_fs.ref_valid(ref)) + int main_len; + for(main_len = 8; main_len > 0; main_len--) + if(p[main_len - 1] != ' ') break; - iblk = m_fs.m_blockdev.get(cs_to_block(ref)); - } - done: - data.resize(length); - return data; + int ext_len; + for(ext_len = 4; ext_len > 0; ext_len--) + if(p[ext_len + 8 - 1] != ' ') + break; + std::string name; + for(int i=0; i != main_len; i++) + name += char(p[i]); + for(int i=0; i != ext_len; i++) + name += char(p[8 + i]); + return name; } -impl::system_file::system_file(impl &fs, root_dir *dir, const u8 *entry, u16 key) : m_fs(fs), m_dir(dir), m_key(key) +bool oric_jasmin_impl::file_is_system(const u8 *entry) { - memcpy(m_entry, entry, 18); + u16 ref = r16b(entry); + return ref == 0 && r32b(entry+0xb) == 0x2e535953; } -void impl::system_file::drop_weak_references() -{ -} -meta_data impl::system_file::metadata() +meta_data oric_jasmin_impl::file_metadata(const u8 *entry) { meta_data res; - res.set(meta_name::name, read_file_name(m_entry + 3)); - res.set(meta_name::locked, m_entry[2] == 'L'); - res.set(meta_name::sequential, m_entry[0xf] == 'S'); - res.set(meta_name::size_in_blocks, r16l(m_entry + 0x10)); - res.set(meta_name::length, 0x3e00); + res.set(meta_name::name, file_name_read(entry + 3)); + res.set(meta_name::locked, entry[2] == 'L'); + res.set(meta_name::sequential, entry[0xf] == 'S'); + res.set(meta_name::size_in_blocks, r16l(entry + 0x10)); + bool sys = file_is_system(entry); + if(sys) + res.set(meta_name::length, 0x3e00); + + else { + u16 ref = r16b(entry); + auto dblk = m_blockdev.get(cs_to_block(ref)); + res.set(meta_name::loading_address, dblk.r16l(2)); + res.set(meta_name::length, dblk.r16l(4)); + } return res; } -std::vector impl::system_file::read_all() +std::tuple oric_jasmin_impl::file_find(std::string name) { - std::vector data(0x3e00); - for(u32 i = 0; i != 62; i++) { - auto dblk = m_fs.m_blockdev.get(i); - memcpy(data.data() + 256 * i, dblk.rodata(), 256); + name = file_name_prepare(name); + auto bdir = m_blockdev.get(20*17+1); + for(;;) { + for(u32 i = 0; i != 14; i ++) { + u32 off = 4 + i*18; + u16 fref = bdir.r16b(off); + if(ref_valid(fref) || file_is_system(bdir.rodata()+off)) { + if(memcmp(bdir.rodata() + off + 3, name.data(), 12)) { + bool sys = file_is_system(bdir.rodata() + off); + return std::make_tuple(bdir, off, sys); + } + } + } + u16 ref = bdir.r16b(2); + if(!ref || !ref_valid(ref)) + return std::make_tuple(bdir, 0U, false); + + bdir = m_blockdev.get(cs_to_block(ref)); } - return data; } -impl::file_t impl::root_dir::file_create(const meta_data &info) +std::pair oric_jasmin_impl::metadata(const std::vector &path) { + if(path.size() != 1) + return std::make_pair(ERR_NOT_FOUND, meta_data()); + + auto [bdir, off, sys] = file_find(path[0]); + if(!off) + return std::make_pair(ERR_NOT_FOUND, meta_data()); + + return std::make_pair(ERR_OK, file_metadata(bdir.rodata() + off)); +} + +err_t oric_jasmin_impl::metadata_change(const std::vector &path, const meta_data &meta) +{ + if(path.size() != 1) + return ERR_NOT_FOUND; + + auto [bdir, off, sys] = file_find(path[0]); + if(!off) + return ERR_NOT_FOUND; + + u8 *entry = bdir.data() + off; + if(meta.has(meta_name::locked)) + w8 (entry+0x02, meta.get_flag(meta_name::locked) ? 'L' : 'U'); + if(meta.has(meta_name::name)) + wstr(entry+0x03, file_name_prepare(meta.get_string(meta_name::name))); + if(meta.has(meta_name::sequential)) + w8 (entry+0x0f, meta.get_flag(meta_name::sequential) ? 'D' : 'S'); + if(!sys && meta.has(meta_name::loading_address)) + m_blockdev.get(cs_to_block(r16b(entry))).w16l(2, meta.get_number(meta_name::loading_address)); + + return ERR_OK; +} + +std::pair> oric_jasmin_impl::directory_contents(const std::vector &path) +{ + std::pair> res; + + if(path.size() != 0) { + res.first = ERR_NOT_FOUND; + return res; + } + + res.first = ERR_OK; + + auto bdir = m_blockdev.get(20*17+1); + for(;;) { + for(u32 i = 0; i != 14; i ++) { + u32 off = 4 + i*18; + u16 fref = bdir.r16b(off); + if(ref_valid(fref) || file_is_system(bdir.rodata()+off)) { + meta_data meta = file_metadata(bdir.rodata()+off); + res.second.emplace_back(dir_entry(dir_entry_type::file, meta)); + } + } + u16 ref = bdir.r16b(2); + if(!ref || !ref_valid(ref)) + break; + bdir = m_blockdev.get(cs_to_block(ref)); + } + return res; +} + +err_t oric_jasmin_impl::rename(const std::vector &opath, const std::vector &npath) +{ + if(opath.size() != 1 || npath.size() != 1) + return ERR_NOT_FOUND; + + auto [bdir, off, sys] = file_find(opath[0]); + if(!off) + return ERR_NOT_FOUND; + + wstr(bdir.data() + off + 0x03, file_name_prepare(npath[0])); + + return ERR_OK; +} + +err_t oric_jasmin_impl::remove(const std::vector &path) +{ + return ERR_UNSUPPORTED; +} + +err_t oric_jasmin_impl::file_create(const std::vector &path, const meta_data &meta) +{ + if(path.size() != 0) + return ERR_NOT_FOUND; + // One block of sector list, one block of data u32 nb = 2; // Find the key for the next entry, increase nb if needed - auto bdir = m_fs.m_blockdev.get(20*17+1); + auto bdir = m_blockdev.get(20*17+1); u64 id = 0; for(;;) { for(u32 i = 0; i != 14; i ++) { u32 off = 4 + i*18; u16 ref = bdir.r16b(off); - if(!m_fs.ref_valid(ref)) + if(!ref_valid(ref)) goto found; id++; } u16 ref = bdir.r16b(2); - if(!ref || !m_fs.ref_valid(ref)) { + if(!ref || !ref_valid(ref)) { nb ++; break; } - bdir = m_fs.m_blockdev.get(cs_to_block(ref)); + bdir = m_blockdev.get(cs_to_block(ref)); } found: - auto block = m_fs.allocate_blocks(nb); + auto block = allocate_blocks(nb); if(block.empty()) - return nullptr; + return ERR_NO_SPACE; - auto sblk = m_fs.m_blockdev.get(cs_to_block(block[0])); + auto sblk = m_blockdev.get(cs_to_block(block[0])); sblk.w16b(0, 0xff00); // Next sector - sblk.w16l(2, info.get_number(meta_name::loading_address, 0x500)); + sblk.w16l(2, meta.get_number(meta_name::loading_address, 0x500)); sblk.w16l(4, 0); // Length sblk.w16b(6, block[1]); // Data block if(nb == 3) { bdir.w16l(0, block[2]); // Link to the next directory sector - bdir = m_fs.m_blockdev.get(cs_to_block(block[2])); + bdir = m_blockdev.get(cs_to_block(block[2])); bdir.fill(0xff); bdir.w16l(0, block[2]); // Reference to itself bdir.w16l(2, 0xff00); // No next directory sector @@ -490,128 +472,138 @@ impl::file_t impl::root_dir::file_create(const meta_data &info) u32 off = 4 + (id % 14) * 18; bdir.w16b(off+0x00, block[0]); // First (and only) sector in the sector list - bdir.w8 (off+0x02, info.get_flag(meta_name::locked, false) ? 'L' : 'U'); - bdir.wstr(off+0x03, file_name_prepare(info.get_string(meta_name::name, ""))); - bdir.w8 (off+0x0f, info.get_flag(meta_name::sequential, true) ? 'S' : 'D'); + bdir.w8 (off+0x02, meta.get_flag(meta_name::locked, false) ? 'L' : 'U'); + bdir.wstr(off+0x03, file_name_prepare(meta.get_string(meta_name::name, ""))); + bdir.w8 (off+0x0f, meta.get_flag(meta_name::sequential, true) ? 'S' : 'D'); bdir.w16l(off+0x10, 2); // 2 sectors for an empty file - return file_t(new file(m_fs, this, bdir.rodata() + off, id)); + return ERR_OK; } - - -void impl::root_dir::file_delete(u64 key) +std::pair> oric_jasmin_impl::file_read(const std::vector &path) { -} + std::vector data; -void impl::file::replace(const std::vector &data) -{ - u32 cur_ns = r16l(m_entry + 0x10); - // Data sectors first - u32 need_ns = (data.size() + 255) / 256; - if(need_ns == 0) - need_ns = 1; - // Add the sector list sectors, 125 entries/sector - need_ns += (need_ns + 124)/125; + if(path.size() != 1) + return std::make_pair(ERR_NOT_FOUND, data); - // Enough space? - if(cur_ns < need_ns && m_fs.free_block_count() < need_ns - cur_ns) - return; + auto [bdir, off, sys] = file_find(path[0]); + if(!off) + return std::make_pair(ERR_NOT_FOUND, data); - u16 load_address = 0; - std::vector tofree; - u16 iref = r16b(m_entry); - for(u32 i=0; i < cur_ns; i += 125+1) { - auto iblk = m_fs.m_blockdev.get(cs_to_block(iref)); - if(!i) - load_address = iblk.r16l(2); - tofree.push_back(iref); - for(u32 j=0; j != 125 && i+j+1 != cur_ns; j++) - tofree.push_back(iblk.r16b(6+2*j)); - iref = iblk.r16b(2); - } - m_fs.free_blocks(tofree); - - std::vector blocks = m_fs.allocate_blocks(need_ns); - for(u32 i=0; i < need_ns; i += 125+1) { - auto iblk = m_fs.m_blockdev.get(cs_to_block(blocks[i])); - iblk.fill(0xff); - if(!i) { - iblk.w16l(2, load_address); - iblk.w16l(4, data.size()); + if(sys) { + data.resize(0x3e00); + for(u32 i = 0; i != 62; i++) { + auto dblk = m_blockdev.get(i); + memcpy(data.data() + 256 * i, dblk.rodata(), 256); } - if(i + 126 < need_ns) - iblk.w16b(0, blocks[i+126]); - else - iblk.w16b(0, 0xff00); - for(u32 j=0; j != 125 && i+j+1 != need_ns; j++) { - u32 dpos = 256 * (j + i/126*125); - u32 size = data.size() - dpos; - iblk.w16b(6+j*2, blocks[i+j+1]); - auto dblk = m_fs.m_blockdev.get(cs_to_block(blocks[i+j+1])); - if(size >= 256) - dblk.copy(0, data.data() + dpos, 256); - else { - dblk.copy(0, data.data() + dpos, size); - dblk.fill(size, 0x55, 256-size); + } else { + const u8 *entry = bdir.rodata() + off; + u16 ref = r16b(entry); + auto iblk = m_blockdev.get(cs_to_block(ref)); + u32 length = iblk.r16l(4); + while(ref_valid(ref)) { + for(u32 pos = 6; pos != 256 && data.size() < length; pos += 2) { + u16 dref = iblk.r16b(pos); + if(!ref_valid(dref)) + goto done; + auto dblk = m_blockdev.get(cs_to_block(dref)); + u32 dpos = data.size(); + data.resize(dpos + 256); + memcpy(data.data() + dpos, dblk.rodata(), 256); + if(data.size() >= length) + goto done; + } + ref = iblk.r16b(2); + if(!ref_valid(ref)) + break; + iblk = m_blockdev.get(cs_to_block(ref)); + } + done: + data.resize(length); + } + + return std::make_pair(ERR_OK, data); +} + +err_t oric_jasmin_impl::file_write(const std::vector &path, const std::vector &data) +{ + if(path.size() != 1) + return ERR_NOT_FOUND; + + auto [bdir, off, sys] = file_find(path[0]); + if(!off) + return ERR_NOT_FOUND; + + if(sys) { + if(data.size() != 0x3e00) + return ERR_INVALID; + + for(u32 i=0; i != 0x3e; i++) + m_blockdev.get(i).copy(0, data.data() + i * 256, 256); + + } else { + u8 *entry = bdir.data() + off; + u32 cur_ns = r16l(entry + 0x10); + // Data sectors first + u32 need_ns = (data.size() + 255) / 256; + if(need_ns == 0) + need_ns = 1; + // Add the sector list sectors, 125 entries/sector + need_ns += (need_ns + 124)/125; + + // Enough space? + if(cur_ns < need_ns && free_block_count() < need_ns - cur_ns) + return ERR_NO_SPACE; + + u16 load_address = 0; + std::vector tofree; + u16 iref = r16b(entry); + for(u32 i=0; i < cur_ns; i += 125+1) { + auto iblk = m_blockdev.get(cs_to_block(iref)); + if(!i) + load_address = iblk.r16l(2); + tofree.push_back(iref); + for(u32 j=0; j != 125 && i+j+1 != cur_ns; j++) + tofree.push_back(iblk.r16b(6+2*j)); + iref = iblk.r16b(2); + } + free_blocks(tofree); + + std::vector blocks = allocate_blocks(need_ns); + for(u32 i=0; i < need_ns; i += 125+1) { + auto iblk = m_blockdev.get(cs_to_block(blocks[i])); + iblk.fill(0xff); + if(!i) { + iblk.w16l(2, load_address); + iblk.w16l(4, data.size()); + } + if(i + 126 < need_ns) + iblk.w16b(0, blocks[i+126]); + else + iblk.w16b(0, 0xff00); + + for(u32 j=0; j != 125 && i+j+1 != need_ns; j++) { + u32 dpos = 256 * (j + i/126*125); + u32 size = data.size() - dpos; + iblk.w16b(6+j*2, blocks[i+j+1]); + auto dblk = m_blockdev.get(cs_to_block(blocks[i+j+1])); + if(size >= 256) + dblk.copy(0, data.data() + dpos, 256); + else { + dblk.copy(0, data.data() + dpos, size); + dblk.fill(size, 0x55, 256-size); + } } } + w16l(entry + 0x10, need_ns); + w16b(entry + 0x00, blocks[0]); } - w16l(m_entry + 0x10, need_ns); - w16b(m_entry + 0x00, blocks[0]); - m_dir->update_file(m_key, m_entry); + return ERR_OK; } -void impl::root_dir::metadata_change(const meta_data &info) -{ -} - -void impl::metadata_change(const meta_data &info) -{ - if(info.has(meta_name::name)) { - std::string volume_name = info.get_string(meta_name::name); - volume_name.resize(8, ' '); - m_blockdev.get(20*17).wstr(0xf8, volume_name); - } -} - -void impl::file::metadata_change(const meta_data &info) -{ - if(info.has(meta_name::locked)) - w8 (m_entry+0x02, info.get_flag(meta_name::locked) ? 'L' : 'U'); - if(info.has(meta_name::name)) - wstr(m_entry+0x03, file_name_prepare(info.get_string(meta_name::name))); - if(info.has(meta_name::sequential)) - w8 (m_entry+0x0f, info.get_flag(meta_name::sequential) ? 'D' : 'S'); - if(info.has(meta_name::loading_address)) - m_fs.m_blockdev.get(cs_to_block(r16b(m_entry))).w16l(2, info.get_number(meta_name::loading_address)); - m_dir->update_file(m_key, m_entry); -} - -void impl::system_file::replace(const std::vector &data) -{ - if(data.size() != 0x3e00) - return; - - for(u32 i=0; i != 0x3e; i++) - m_fs.m_blockdev.get(i).copy(0, data.data() + i * 256, 256); -} - -void impl::system_file::metadata_change(const meta_data &info) -{ - if(info.has(meta_name::locked)) - w8 (m_entry+0x02, info.get_flag(meta_name::locked) ? 'L' : 'U'); - if(info.has(meta_name::name)) - wstr(m_entry+0x03, file_name_prepare(info.get_string(meta_name::name))); - if(info.has(meta_name::sequential)) - w8 (m_entry+0x0f, info.get_flag(meta_name::sequential) ? 'S' : 'D'); - if(info.has(meta_name::loading_address)) - m_fs.m_blockdev.get(cs_to_block(r16b(m_entry))).w16l(2, info.get_number(meta_name::loading_address)); - m_dir->update_file(m_key, m_entry); -} - -std::vector impl::allocate_blocks(u32 count) +std::vector oric_jasmin_impl::allocate_blocks(u32 count) { std::vector blocks; if(free_block_count() < count) @@ -636,7 +628,7 @@ std::vector impl::allocate_blocks(u32 count) return blocks; } -void impl::free_blocks(const std::vector &blocks) +void oric_jasmin_impl::free_blocks(const std::vector &blocks) { auto fmap = m_blockdev.get(20*17); for(u16 ref : blocks) { @@ -650,7 +642,7 @@ void impl::free_blocks(const std::vector &blocks) } } -u32 impl::free_block_count() +u32 oric_jasmin_impl::free_block_count() { auto fmap = m_blockdev.get(20*17); u32 nf = 0; @@ -664,54 +656,3 @@ u32 impl::free_block_count() } return nf; } - -const char *oric_jasmin_image::name() const -{ - return "oric_jasmin"; -} - -const char *oric_jasmin_image::description() const -{ - return "Oric Jasmin"; -} - -void oric_jasmin_image::enumerate_f(floppy_enumerator &fe, u32 form_factor, const std::vector &variants) const -{ - if(has(form_factor, variants, floppy_image::FF_3, floppy_image::DSDD)) - fe.add(FLOPPY_ORIC_JASMIN_FORMAT, 356864, "oric_jasmin_ds", "Oric Jasmin dual-sided"); - if(has(form_factor, variants, floppy_image::FF_3, floppy_image::SSDD)) - fe.add(FLOPPY_ORIC_JASMIN_FORMAT, 178432, "oric_jasmin_ss", "Oric Jasmin single-sided"); -} - -std::unique_ptr oric_jasmin_image::mount(fsblk_t &blockdev) const -{ - return std::make_unique(blockdev); -} - -bool oric_jasmin_image::can_format() const -{ - return true; -} - -bool oric_jasmin_image::can_read() const -{ - return true; -} - -bool oric_jasmin_image::can_write() const -{ - return true; -} - -bool oric_jasmin_image::has_rsrc() const -{ - return false; -} - -std::vector oric_jasmin_image::volume_meta_description() const -{ - std::vector res; - res.emplace_back(meta_description(meta_name::name, "UNTITLED", false, [](const meta_value &m) { return m.as_string().size() <= 8; }, "Volume name, up to 8 characters")); - - return res; -} diff --git a/src/lib/formats/fs_oric_jasmin.h b/src/lib/formats/fs_oric_jasmin.h index b759ce0aafc..76bbce2403d 100644 --- a/src/lib/formats/fs_oric_jasmin.h +++ b/src/lib/formats/fs_oric_jasmin.h @@ -29,8 +29,6 @@ public: virtual std::vector volume_meta_description() const override; virtual std::vector file_meta_description() const override; - - static bool validate_filename(std::string name); }; extern const oric_jasmin_image ORIC_JASMIN; diff --git a/src/lib/formats/fs_prodos.cpp b/src/lib/formats/fs_prodos.cpp index 2ce67301999..5350c20024a 100644 --- a/src/lib/formats/fs_prodos.cpp +++ b/src/lib/formats/fs_prodos.cpp @@ -15,78 +15,28 @@ using namespace fs; namespace fs { const prodos_image PRODOS; } namespace { -class impl : public filesystem_t { +class prodos_impl : public filesystem_t { public: - impl(fsblk_t &blockdev); - virtual ~impl() = default; + prodos_impl(fsblk_t &blockdev); + virtual ~prodos_impl() = default; - virtual void format(const meta_data &meta) override; + virtual meta_data volume_metadata() override; + virtual std::pair metadata(const std::vector &path) override; - virtual meta_data metadata() override; - virtual dir_t root() override; + virtual std::pair> directory_contents(const std::vector &path) override; - void drop_root_ref(); + virtual std::pair> file_read(const std::vector &path) override; + virtual std::pair> file_rsrc_read(const std::vector &path) override; - static util::arbitrary_datetime prodos_to_dt(u32 date); - std::vector contents(u16 block); + virtual err_t format(const meta_data &meta) override; private: static const u8 boot[512]; + static util::arbitrary_datetime prodos_to_dt(u32 date); - dir_t m_root; - - class root_dir : public idir_t { - public: - root_dir(impl &fs, u16 base_block) : m_fs(fs), m_base_block(base_block) { } - virtual ~root_dir() = default; - - virtual void drop_weak_references() override; - - virtual meta_data metadata() override; - virtual std::vector contents() override; - virtual file_t file_get(u64 key) override; - virtual dir_t dir_get(u64 key) override; - - protected: - impl &m_fs; - u16 m_base_block; - - std::pair get_entry_ro(u64 key); - [[maybe_unused]] std::pair get_entry(u64 key); - }; - - class dir : public root_dir { - public: - dir(impl &fs, const u8 *entry, u16 base_block, u16 key, root_dir *parent_dir); - virtual ~dir() = default; - - virtual meta_data metadata() override; - - protected: - root_dir *m_parent_dir; - u16 m_key; - u8 m_entry[39]; - }; - - class file : public ifile_t { - public: - file(impl &fs, const u8 *entry, u16 key, root_dir *parent_dir); - virtual ~file() = default; - - virtual void drop_weak_references() override; - - virtual meta_data metadata() override; - virtual std::vector read_all() override; - virtual std::vector rsrc_read_all() override; - - private: - impl &m_fs; - root_dir *m_parent_dir; - u16 m_key; - u8 m_entry[39]; - - std::vector any_read_all(u8 type, u16 block, u32 length); - }; + std::tuple path_find_step(const std::string &name, u16 block); + std::tuple path_find(const std::vector &path); + std::pair> any_read(u8 type, u16 block, u32 length); }; } @@ -101,7 +51,7 @@ const char *prodos_image::description() const return "Apple ProDOS"; } -const u8 impl::boot[512] = { +const u8 prodos_impl::boot[512] = { 0x01, 0x38, 0xb0, 0x03, 0x4c, 0x1c, 0x09, 0x78, 0x86, 0x43, 0xc9, 0x03, 0x08, 0x8a, 0x29, 0x70, 0x4a, 0x4a, 0x4a, 0x4a, 0x09, 0xc0, 0x85, 0x49, 0xa0, 0xff, 0x84, 0x48, 0x28, 0xc8, 0xb1, 0x48, 0xd0, 0x3a, 0xb0, 0x0e, 0xa9, 0x03, 0x8d, 0x00, 0x08, 0xe6, 0x3d, 0xa5, 0x49, 0x48, 0xa9, 0x5b, @@ -146,7 +96,7 @@ void prodos_image::enumerate_f(floppy_enumerator &fe, u32 form_factor, const std std::unique_ptr prodos_image::mount(fsblk_t &blockdev) const { - return std::make_unique(blockdev); + return std::make_unique(blockdev); } bool prodos_image::can_format() const @@ -215,7 +165,7 @@ std::vector prodos_image::directory_meta_description() const return res; } -void impl::format(const meta_data &meta) +err_t prodos_impl::format(const meta_data &meta) { std::string volume_name = meta.get_string(meta_name::name, "UNTITLED"); u32 blocks = m_blockdev.block_count(); @@ -279,13 +229,14 @@ void impl::format(const meta_data &meta) memset(fdata+sb, 0xff, eb-sb-1); } } + return ERR_OK; } -impl::impl(fsblk_t &blockdev) : filesystem_t(blockdev, 512), m_root(true) +prodos_impl::prodos_impl(fsblk_t &blockdev) : filesystem_t(blockdev, 512) { } -util::arbitrary_datetime impl::prodos_to_dt(u32 date) +util::arbitrary_datetime prodos_impl::prodos_to_dt(u32 date) { util::arbitrary_datetime dt; dt.second = 0; @@ -300,7 +251,7 @@ util::arbitrary_datetime impl::prodos_to_dt(u32 date) return dt; } -meta_data impl::metadata() +meta_data prodos_impl::volume_metadata() { meta_data res; auto bdir = m_blockdev.get(2); @@ -313,200 +264,161 @@ meta_data impl::metadata() return res; } -filesystem_t::dir_t impl::root() +std::pair> prodos_impl::directory_contents(const std::vector &path) { - if(!m_root) - m_root = new root_dir(*this, 2); - return m_root.strong(); -} + u16 block; + if(path.empty()) + block = 2; -void impl::drop_root_ref() -{ - m_root = nullptr; -} + else { + auto [blk, off, dir] = path_find(path); + if(!off || !dir) + return std::make_pair(ERR_NOT_FOUND, std::vector()); + block = blk.r16l(off+0x11); + } - -void impl::root_dir::drop_weak_references() -{ - if(m_base_block == 2) - m_fs.drop_root_ref(); -} - -meta_data impl::root_dir::metadata() -{ - return meta_data(); -} - -std::vector impl::root_dir::contents() -{ std::vector res; - u16 block = m_base_block; u32 off = 39 + 4; - u32 id = 1; do { - auto blk = m_fs.m_blockdev.get(block); + auto blk = m_blockdev.get(block); while(off < 511) { + meta_data meta; u8 type = blk.r8(off); - auto name = blk.rstr(off+1, type & 0xf); + meta.set(meta_name::name, blk.rstr(off+1, type & 0xf)); type >>= 4; - if(type == 0xd) - res.emplace_back(dir_entry(std::move(name), dir_entry_type::dir, id)); - else if(type != 0) - res.emplace_back(dir_entry(std::move(name), dir_entry_type::file, id)); + + if(type == 5) { + auto rootblk = m_blockdev.get(blk.r16l(off+0x11)); + meta.set(meta_name::length, rootblk.r24l(0x005)); + meta.set(meta_name::rsrc_length, rootblk.r24l(0x105)); + + } else if(type >= 1 && type <= 3) + meta.set(meta_name::length, blk.r24l(off + 0x15)); + + res.emplace_back(dir_entry(type == 0xd ? dir_entry_type::dir : dir_entry_type::file, meta)); off += 39; - id ++; } block = blk.r16l(2); - if(block >= m_fs.m_blockdev.block_count()) + if(block >= m_blockdev.block_count()) break; off = 4; } while(block); - return res; + return std::make_pair(ERR_OK, res); } - -std::pair impl::root_dir::get_entry_ro(u64 key) +std::tuple prodos_impl::path_find_step(const std::string &name, u16 block) { - std::pair res; - res.first = m_fs.m_blockdev.get(m_base_block); - while(key >= 13) { - key -= 13; - u16 block = res.first.r16l(2); - if(!block || block >= m_fs.m_blockdev.block_count()) { - res.first = nullptr; - res.second = nullptr; - return res; + u32 off = 39 + 4; + for(;;) { + auto blk = m_blockdev.get(block); + while(off < 511) { + u8 type = blk.r8(off); + if(name == blk.rstr(off+1, type & 0xf)) + return std::make_tuple(blk, off); + off += 39; } - res.first = m_fs.m_blockdev.get(block); + block = blk.r16l(2); + if(!block || block >= m_blockdev.block_count()) + return std::make_tuple(blk, 0U); + off = 4; } - res.second = res.first.rodata() + 4 + 39 * key; - return res; } -std::pair impl::root_dir::get_entry(u64 key) +std::tuple prodos_impl::path_find(const std::vector &path) { - std::pair res; - res.first = m_fs.m_blockdev.get(m_base_block); - while(key > 13) { - key -= 13; - u16 block = res.first.r16l(2); - if(!block || block >= m_fs.m_blockdev.block_count()) { - res.first = nullptr; - res.second = nullptr; - return res; - } - res.first = m_fs.m_blockdev.get(block); + if(path.size() == 0) + return std::tuple(fsblk_t::block_t(), 0, false); + + u16 block = 2; + for(u32 pathc = 0;; pathc++) { + auto [blk, off] = path_find_step(path[pathc], block); + if(!off) + return std::make_tuple(blk, off, false); + + if(pathc + 1 == path.size()) + return std::make_tuple(blk, off, (blk.r8(off) & 0xf0) == 0xd0); + + if((blk.r8(off) & 0xf0) != 0xd0) + return std::make_tuple(blk, 0U, false); + + block = blk.r16l(off + 0x11); } - res.second = res.first.data() + 4 + 39 * key; - return res; } -filesystem_t::file_t impl::root_dir::file_get(u64 key) -{ - auto [blk, entry] = get_entry_ro(key); - if(!blk) - throw std::out_of_range("Out-of-range key on file_get"); - u8 type = entry[0] >> 4; - if(type == 0 || type == 4 || type > 5) - throw std::runtime_error(util::string_format("Unhandled file type %x", type)); - return new file(m_fs, entry, key, this); -} -filesystem_t::dir_t impl::root_dir::dir_get(u64 key) +std::pair prodos_impl::metadata(const std::vector &path) { - auto [blk, entry] = get_entry_ro(key); - if(!blk) - throw std::out_of_range("Out-of-range key on dir_get"); - u8 type = entry[0] >> 4; - if(type != 0xd) - throw std::runtime_error(util::string_format("Unhandled directory type %x", type)); + if(path.size() == 0) + return std::make_pair(ERR_OK, meta_data()); - return new dir(m_fs, entry, r16l(entry+0x11), key, this); -} + auto [blk, off, dir] = path_find(path); -impl::dir::dir(impl &fs, const u8 *entry, u16 base_block, u16 key, root_dir *parent_dir) : root_dir(fs, base_block), m_parent_dir(parent_dir), m_key(key) -{ - memcpy(m_entry, entry, 39); - (void)m_key; - (void)m_parent_dir; -} + if(!off) + return std::make_pair(ERR_NOT_FOUND, meta_data()); -impl::file::file(impl &fs, const u8 *entry, u16 key, root_dir *parent_dir) : m_fs(fs), m_parent_dir(parent_dir), m_key(key) -{ - memcpy(m_entry, entry, 39); - (void)m_key; - (void)m_parent_dir; -} + const u8 *entry = blk.rodata() + off; -void impl::file::drop_weak_references() -{ -} - -meta_data impl::file::metadata() -{ meta_data res; - u8 type = r8(m_entry); - std::string name = rstr(m_entry+1, type & 0xf); - type >>= 4; - res.set(meta_name::name, name); - if(type == 5) { - auto rootblk = m_fs.m_blockdev.get(r16l(m_entry+0x11)); - res.set(meta_name::length, rootblk.r24l(0x005)); - res.set(meta_name::rsrc_length, rootblk.r24l(0x105)); + if(dir) { + u8 type = r8(entry); + std::string name = rstr(entry+1, type & 0xf); + res.set(meta_name::name, name); + } else { + u8 type = r8(entry); + std::string name = rstr(entry+1, type & 0xf); + type >>= 4; + res.set(meta_name::name, name); + if(type == 5) { + auto rootblk = m_blockdev.get(r16l(entry+0x11)); + res.set(meta_name::length, rootblk.r24l(0x005)); + res.set(meta_name::rsrc_length, rootblk.r24l(0x105)); + + } else if(type >= 1 && type <= 3) + res.set(meta_name::length, r24l(entry + 0x15)); + + else + return std::make_pair(ERR_UNSUPPORTED, meta_data()); + } - } else if(type >= 1 && type <= 3) - res.set(meta_name::length, r24l(m_entry + 0x15)); - - else - throw std::runtime_error(util::string_format("impl::file::metadata: Unhandled file type %d", type)); - - return res; + return std::make_pair(ERR_OK, res); } -meta_data impl::dir::metadata() +std::pair> prodos_impl::any_read(u8 type, u16 block, u32 length) { - meta_data res; - u8 type = r8(m_entry); - std::string name = rstr(m_entry+1, type & 0xf); - res.set(meta_name::name, name); - - return res; -} - -std::vector impl::file::any_read_all(u8 type, u16 block, u32 length) -{ - std::vector data((length + 511) & ~511); - u32 nb = data.size()/512; + std::pair> data; + data.first = ERR_OK; + data.second.resize((length + 511) & ~511); + u32 nb = data.second.size()/512; if(!nb) return data; - u8 *dst = data.data(); - u8 *end = dst + data.size(); + u8 *dst = data.second.data(); + u8 *end = dst + data.second.size(); switch(type) { case 1: - memcpy(dst, m_fs.m_blockdev.get(block).rodata(), 512); + memcpy(dst, m_blockdev.get(block).rodata(), 512); dst += 512; break; case 2: { - auto iblk = m_fs.m_blockdev.get(block); + auto iblk = m_blockdev.get(block); for(u32 i=0; i != 256 && dst != end; i++) { u16 blk = iblk.r8(i) | (iblk.r8(i | 0x100) << 8); - memcpy(dst, m_fs.m_blockdev.get(blk).rodata(), 512); + memcpy(dst, m_blockdev.get(blk).rodata(), 512); dst += 512; } break; } case 3: { - auto mblk = m_fs.m_blockdev.get(block); + auto mblk = m_blockdev.get(block); for(u32 j=0; dst != end; j += 256) { u32 idx = j/256; - auto iblk = m_fs.m_blockdev.get(mblk.r8(idx) | (mblk.r8(idx | 0x100) << 8)); + auto iblk = m_blockdev.get(mblk.r8(idx) | (mblk.r8(idx | 0x100) << 8)); for(u32 i=0; i != 256 && dst != end; i++) { u16 blk = iblk.r8(i) | (iblk.r8(i | 0x100) << 8); - memcpy(dst, m_fs.m_blockdev.get(blk).rodata(), 512); + memcpy(dst, m_blockdev.get(blk).rodata(), 512); dst += 512; } } @@ -514,35 +426,48 @@ std::vector impl::file::any_read_all(u8 type, u16 block, u32 length) } default: - throw std::runtime_error(util::string_format("impl::file::get_file_blocks: unknown file type %d", type)); + data.first = ERR_UNSUPPORTED; + data.second.clear(); + return data; } - data.resize(length); + data.second.resize(length); return data; } -std::vector impl::file::read_all() +std::pair> prodos_impl::file_read(const std::vector &path) { - u8 type = r8(m_entry) >> 4; + auto [blk, off, dir] = path_find(path); + if(!off || dir) + return std::make_pair(ERR_NOT_FOUND, std::vector()); + + const u8 *entry = blk.rodata() + off; + u8 type = r8(entry) >> 4; + if(type >= 1 && type <= 3) - return any_read_all(type, r16l(m_entry+0x11), r24l(m_entry + 0x15)); + return any_read(type, r16l(entry+0x11), r24l(entry + 0x15)); else if(type == 5) { - auto kblk = m_fs.m_blockdev.get(r16l(m_entry+0x11)); - return any_read_all(kblk.r8(0x000), kblk.r16l(0x001), kblk.r24l(0x005)); + auto kblk = m_blockdev.get(r16l(entry+0x11)); + return any_read(kblk.r8(0x000), kblk.r16l(0x001), kblk.r24l(0x005)); } else - throw std::runtime_error(util::string_format("impl::file::read_all: Unhandled file type %d", type)); + return std::make_pair(ERR_UNSUPPORTED, std::vector()); } -std::vector impl::file::rsrc_read_all() +std::pair> prodos_impl::file_rsrc_read(const std::vector &path) { - u8 type = r8(m_entry) >> 4; + auto [blk, off, dir] = path_find(path); + if(!off || dir) + return std::make_pair(ERR_NOT_FOUND, std::vector()); + + const u8 *entry = blk.rodata() + off; + u8 type = r8(entry) >> 4; if(type == 5) { - auto kblk = m_fs.m_blockdev.get(r16l(m_entry+0x11)); - return any_read_all(kblk.r8(0x100), kblk.r16l(0x101), kblk.r24l(0x105)); + auto kblk = m_blockdev.get(r16l(entry+0x11)); + return any_read(kblk.r8(0x100), kblk.r16l(0x101), kblk.r24l(0x105)); } else - throw std::runtime_error(util::string_format("impl::file::rsrc_blocks: Unhandled file type %d", type)); + return std::make_pair(ERR_UNSUPPORTED, std::vector()); } diff --git a/src/lib/formats/fs_vtech.cpp b/src/lib/formats/fs_vtech.cpp index 7b8edf53f63..f3a9b7d091f 100644 --- a/src/lib/formats/fs_vtech.cpp +++ b/src/lib/formats/fs_vtech.cpp @@ -8,9 +8,9 @@ #include -namespace fs { +using namespace fs; -const vtech_image VTECH; +namespace fs { const vtech_image VTECH; } // Floppy only, format is 40 tracks, 1 head, 16 sectors numbered 0-15, 256 bytes/sector. // Filesystem has no subdirectories. @@ -27,6 +27,38 @@ const vtech_image VTECH; // Files are stored with 126 bytes/sector, and bytes 126 and 127 are // track/sector of the next file data sector. +namespace { + +class vtech_impl : public filesystem_t { +public: + vtech_impl(fsblk_t &blockdev); + virtual ~vtech_impl() = default; + + virtual meta_data volume_metadata() override; + virtual err_t volume_metadata_change(const meta_data &info) override; + virtual std::pair metadata(const std::vector &path) override; + virtual err_t metadata_change(const std::vector &path, const meta_data &meta) override; + + virtual std::pair> directory_contents(const std::vector &path) override; + virtual err_t rename(const std::vector &opath, const std::vector &npath) override; + virtual err_t remove(const std::vector &path) override; + + virtual err_t file_create(const std::vector &path, const meta_data &meta) override; + + virtual std::pair> file_read(const std::vector &path) override; + virtual err_t file_write(const std::vector &path, const std::vector &data) override; + + virtual err_t format(const meta_data &meta) override; + +private: + meta_data file_metadata(const u8 *entry); + std::tuple file_find(std::string name); + std::vector> allocate_blocks(u32 count); + void free_blocks(const std::vector> &blocks); + u32 free_block_count(); +}; +} + const char *vtech_image::name() const { return "vtech"; @@ -45,7 +77,7 @@ void vtech_image::enumerate_f(floppy_enumerator &fe, u32 form_factor, const std: std::unique_ptr vtech_image::mount(fsblk_t &blockdev) const { - return std::make_unique(blockdev); + return std::make_unique(blockdev); } bool vtech_image::can_format() const @@ -74,12 +106,6 @@ std::vector vtech_image::volume_meta_description() const return res; } -meta_data vtech_image::impl::metadata() -{ - meta_data res; - return res; -} - std::vector vtech_image::file_meta_description() const { std::vector res; @@ -90,45 +116,42 @@ std::vector vtech_image::file_meta_description() const return res; } +vtech_impl::vtech_impl(fsblk_t &blockdev) : filesystem_t(blockdev, 128) +{ +} -void vtech_image::impl::format(const meta_data &meta) +err_t vtech_impl::format(const meta_data &meta) { m_blockdev.fill(0); + return ERR_OK; } -vtech_image::impl::impl(fsblk_t &blockdev) : filesystem_t(blockdev, 128), m_root(true) -{ -} - -filesystem_t::dir_t vtech_image::impl::root() -{ - if(!m_root) - m_root = new root_dir(*this); - return m_root.strong(); -} - -void vtech_image::impl::drop_root_ref() -{ - m_root = nullptr; -} - -void vtech_image::impl::root_dir::drop_weak_references() -{ - m_fs.drop_root_ref(); -} - -meta_data vtech_image::impl::root_dir::metadata() +meta_data vtech_impl::volume_metadata() { return meta_data(); } -std::vector vtech_image::impl::root_dir::contents() +err_t vtech_impl::volume_metadata_change(const meta_data &meta) { - std::vector res; + return ERR_OK; +} - u64 id = 0; +meta_data vtech_impl::file_metadata(const u8 *entry) +{ + meta_data res; + + res.set(meta_name::name, trim_end_spaces(rstr(entry+2, 8))); + res.set(meta_name::basic, entry[0] == 'T'); + res.set(meta_name::loading_address, r16l(entry + 0xc)); + res.set(meta_name::length, ((r16l(entry + 0xe) - r16l(entry + 0xc) + 1) & 0xffff)); + + return res; +} + +std::tuple vtech_impl::file_find(std::string name) +{ for(int sect = 0; sect != 14; sect++) { - auto bdir = m_fs.m_blockdev.get(sect); + auto bdir = m_blockdev.get(sect); for(u32 i = 0; i != 8; i ++) { u32 off = i*16; u8 type = bdir.r8(off); @@ -136,69 +159,154 @@ std::vector vtech_image::impl::root_dir::contents() continue; if(bdir.r8(off+1) != ':') continue; - std::string fname = trim_end_spaces(bdir.rstr(off+2, 8)); - res.emplace_back(dir_entry(std::move(fname), dir_entry_type::file, id)); - id++; + if(trim_end_spaces(bdir.rstr(off+2, 8)) == name) { + return std::make_tuple(bdir, i); + } + } + } + return std::make_tuple(fsblk_t::block_t(), 0xffffffff); +} + +std::pair vtech_impl::metadata(const std::vector &path) +{ + if(path.size() != 1) + return std::make_pair(ERR_NOT_FOUND, meta_data()); + + auto [bdir, off] = file_find(path[0]); + if(off == 0xffffffff) + return std::make_pair(ERR_NOT_FOUND, meta_data()); + + return std::make_pair(ERR_OK, file_metadata(bdir.rodata() + off)); +} + +err_t vtech_impl::metadata_change(const std::vector &path, const meta_data &meta) +{ + if(path.size() != 1) + return ERR_NOT_FOUND; + + auto [bdir, off] = file_find(path[0]); + if(!off) + return ERR_NOT_FOUND; + + u8 *entry = bdir.data() + off; + if(meta.has(meta_name::basic)) + w8 (entry+0x0, meta.get_flag(meta_name::basic) ? 'T' : 'B'); + if(meta.has(meta_name::name)) { + std::string name = meta.get_string(meta_name::name); + name.resize(8, ' '); + wstr(entry+0x2, name); + } + if(meta.has(meta_name::loading_address)) { + u16 new_loading = meta.get_number(meta_name::loading_address); + u16 new_end = r16l(entry + 0xe) - r16l(entry + 0xc) + new_loading; + w16l(entry + 0xc, new_loading); + w16l(entry + 0xe, new_end); + } + + return ERR_OK; +} + +std::pair> vtech_impl::directory_contents(const std::vector &path) +{ + std::pair> res; + + if(path.size() != 0) { + res.first = ERR_NOT_FOUND; + return res; + } + + res.first = ERR_OK; + + for(int sect = 0; sect != 14; sect++) { + auto bdir = m_blockdev.get(sect); + for(u32 i = 0; i != 8; i ++) { + u32 off = i*16; + u8 type = bdir.r8(off); + if(type != 'T' && type != 'B') + continue; + if(bdir.r8(off+1) != ':') + continue; + meta_data meta = file_metadata(bdir.rodata()+off); + res.second.emplace_back(dir_entry(dir_entry_type::file, meta)); } } return res; } -filesystem_t::file_t vtech_image::impl::root_dir::file_get(u64 key) +err_t vtech_impl::rename(const std::vector &opath, const std::vector &npath) { - if(key >= 15*8) - throw std::out_of_range("Key out of range"); + if(opath.size() != 1 || npath.size() != 1) + return ERR_NOT_FOUND; - auto bdir = m_fs.m_blockdev.get(key >> 3); - int off = (key & 7) << 4; - return file_t(new file(m_fs, this, bdir.rodata() + off, key)); + auto [bdir, off] = file_find(opath[0]); + if(!off) + return ERR_NOT_FOUND; + + std::string name = npath[0]; + name.resize(8, ' '); + wstr(bdir.data() + off + 2, name); + + return ERR_OK; } -void vtech_image::impl::root_dir::update_file(u16 key, const u8 *entry) +err_t vtech_impl::remove(const std::vector &path) { - auto bdir = m_fs.m_blockdev.get(key >> 3); - int off = (key & 7) << 4; - bdir.copy(off, entry, 16); + return ERR_NOT_FOUND; } -filesystem_t::dir_t vtech_image::impl::root_dir::dir_get(u64 key) + +err_t vtech_impl::file_create(const std::vector &path, const meta_data &meta) { - throw std::logic_error("Directories not supported"); + if(path.size() != 0) + return ERR_NOT_FOUND; + + // Find the key for the next unused entry + for(int sect = 0; sect != 14; sect++) { + auto bdir = m_blockdev.get(sect); + for(u32 i = 0; i != 16; i ++) { + u32 off = i*16; + u8 type = bdir.r8(off); + if(type != 'T' && type != 'B') { + std::string fname = meta.get_string(meta_name::name, ""); + fname.resize(8, ' '); + + bdir.w8 (off+0x0, meta.get_flag(meta_name::basic, true) ? 'T' : 'B'); + bdir.w8 (off+0x1, ':'); + bdir.wstr(off+0x2, fname); + bdir.w8 (off+0xa, 0x00); + bdir.w8 (off+0xb, 0x00); + bdir.w16l(off+0xc, meta.get_number(meta_name::loading_address, 0x7ae9)); + bdir.w16l(off+0xe, bdir.r16l(off+0xc) - 1); // Size 0 initially + return ERR_OK; + } + } + } + return ERR_NO_SPACE; } -vtech_image::impl::file::file(impl &fs, root_dir *dir, const u8 *entry, u16 key) : m_fs(fs), m_dir(dir), m_key(key) +std::pair> vtech_impl::file_read(const std::vector &path) { - memcpy(m_entry, entry, 16); -} + std::vector data; -void vtech_image::impl::file::drop_weak_references() -{ -} + if(path.size() != 1) + return std::make_pair(ERR_NOT_FOUND, data); -meta_data vtech_image::impl::file::metadata() -{ - meta_data res; + auto [bdir, off] = file_find(path[0]); + if(off == 0xffffffff) + return std::make_pair(ERR_NOT_FOUND, data); - res.set(meta_name::name, trim_end_spaces(rstr(m_entry+2, 8))); - res.set(meta_name::basic, m_entry[0] == 'T'); - res.set(meta_name::loading_address, r16l(m_entry + 0xc)); - res.set(meta_name::length, ((r16l(m_entry + 0xe) - r16l(m_entry + 0xc) + 1) & 0xffff)); + const u8 *entry = bdir.rodata() + off; - return res; -} + u8 track = entry[0xa]; + u8 sector = entry[0xb]; + int len = ((r16l(entry + 0xe) - r16l(entry + 0xc)) & 0xffff) + 1; -std::vector vtech_image::impl::file::read_all() -{ - u8 track = m_entry[0xa]; - u8 sector = m_entry[0xb]; - int len = ((r16l(m_entry + 0xe) - r16l(m_entry + 0xc)) & 0xffff) + 1; - - std::vector data(len, 0); + data.resize(len, 0); int pos = 0; while(pos < len) { if(track >= 40 || sector >= 16) break; - auto dblk = m_fs.m_blockdev.get(track*16 + sector); + auto dblk = m_blockdev.get(track*16 + sector); int size = len - pos; if(size > 126) size = 126; @@ -207,44 +315,21 @@ std::vector vtech_image::impl::file::read_all() track = dblk.r8(126); sector = dblk.r8(127); } - return data; + return std::make_pair(ERR_OK, data); } -vtech_image::impl::file_t vtech_image::impl::root_dir::file_create(const meta_data &info) +err_t vtech_impl::file_write(const std::vector &path, const std::vector &data) { - // Find the key for the next unused entry - for(int sect = 0; sect != 14; sect++) { - auto bdir = m_fs.m_blockdev.get(sect); - u64 id = 0; - for(u32 i = 0; i != 16; i ++) { - u32 off = i*16; - u8 type = bdir.r8(off); - if(type != 'T' && type != 'B') { - std::string fname = info.get_string(meta_name::name, ""); - fname.resize(8, ' '); + if(path.size() != 1) + return ERR_NOT_FOUND; - bdir.w8 (off+0x0, info.get_flag(meta_name::basic, true) ? 'T' : 'B'); - bdir.w8 (off+0x1, ':'); - bdir.wstr(off+0x2, fname); - bdir.w8 (off+0xa, 0x00); - bdir.w8 (off+0xb, 0x00); - bdir.w16l(off+0xc, info.get_number(meta_name::loading_address, 0x7ae9)); - bdir.w16l(off+0xe, bdir.r16l(off+0xc) - 1); // Size 0 initially - return file_t(new file(m_fs, this, bdir.rodata() + off, id)); - } - id++; - } - } - return nullptr; -} + auto [bdir, off] = file_find(path[0]); + if(off == 0xffffffff) + return ERR_NOT_FOUND; -void vtech_image::impl::root_dir::file_delete(u64 key) -{ -} + u8 *entry = bdir.data() + off; -void vtech_image::impl::file::replace(const std::vector &data) -{ - u32 cur_len = ((r16l(m_entry + 0xe) - r16l(m_entry + 0xc) + 1) & 0xffff); + u32 cur_len = ((r16l(entry + 0xe) - r16l(entry + 0xc) + 1) & 0xffff); u32 new_len = data.size(); if(new_len > 65535) new_len = 65535; @@ -252,24 +337,24 @@ void vtech_image::impl::file::replace(const std::vector &data) u32 need_ns = (new_len + 125) / 126; // Enough space? - if(cur_ns < need_ns && m_fs.free_block_count() < need_ns - cur_ns) - return; + if(cur_ns < need_ns && free_block_count() < need_ns - cur_ns) + return ERR_NO_SPACE; - u8 track = m_entry[0xa]; - u8 sector = m_entry[0xb]; + u8 track = entry[0xa]; + u8 sector = entry[0xb]; std::vector> tofree; for(u32 i = 0; i != cur_ns; i++) { tofree.emplace_back(std::make_pair(track, sector)); - auto dblk = m_fs.m_blockdev.get(track*16 + sector); + auto dblk = m_blockdev.get(track*16 + sector); track = dblk.r8(126); sector = dblk.r8(127); } - m_fs.free_blocks(tofree); + free_blocks(tofree); - std::vector> blocks = m_fs.allocate_blocks(need_ns); + std::vector> blocks = allocate_blocks(need_ns); for(u32 i=0; i != need_ns; i ++) { - auto dblk = m_fs.m_blockdev.get(blocks[i].first * 16 + blocks[i].second); + auto dblk = m_blockdev.get(blocks[i].first * 16 + blocks[i].second); u32 len = new_len - i*126; if(len > 126) len = 126; @@ -283,44 +368,18 @@ void vtech_image::impl::file::replace(const std::vector &data) dblk.w16l(126, 0); } - u16 end_address = (r16l(m_entry + 0xc) + data.size() - 1) & 0xffff; - w16l(m_entry + 0xe, end_address); + u16 end_address = (r16l(entry + 0xc) + data.size() - 1) & 0xffff; + w16l(entry + 0xe, end_address); if(need_ns) { - w8(m_entry + 0xa, blocks[0].first); - w8(m_entry + 0xb, blocks[0].second); + w8(entry + 0xa, blocks[0].first); + w8(entry + 0xb, blocks[0].second); } else - w16l(m_entry + 0xa, 0); + w16l(entry + 0xa, 0); - m_dir->update_file(m_key, m_entry); + return ERR_OK; } -void vtech_image::impl::root_dir::metadata_change(const meta_data &info) -{ -} - -void vtech_image::impl::metadata_change(const meta_data &info) -{ -} - -void vtech_image::impl::file::metadata_change(const meta_data &info) -{ - if(info.has(meta_name::basic)) - w8 (m_entry+0x0, info.get_flag(meta_name::basic) ? 'T' : 'B'); - if(info.has(meta_name::name)) { - std::string name = info.get_string(meta_name::name); - name.resize(8, ' '); - wstr(m_entry+0x2, name); - } - if(info.has(meta_name::loading_address)) { - u16 new_loading = info.get_number(meta_name::loading_address); - u16 new_end = r16l(m_entry + 0xe) - r16l(m_entry + 0xc) + new_loading; - w16l(m_entry + 0xc, new_loading); - w16l(m_entry + 0xe, new_end); - } - m_dir->update_file(m_key, m_entry); -} - -std::vector> vtech_image::impl::allocate_blocks(u32 count) +std::vector> vtech_impl::allocate_blocks(u32 count) { std::vector> blocks; if(free_block_count() < count) @@ -341,7 +400,7 @@ std::vector> vtech_image::impl::allocate_blocks(u32 count) abort(); } -void vtech_image::impl::free_blocks(const std::vector> &blocks) +void vtech_impl::free_blocks(const std::vector> &blocks) { auto fmap = m_blockdev.get(15); for(auto ref : blocks) { @@ -353,7 +412,7 @@ void vtech_image::impl::free_blocks(const std::vector> &blocks } } -u32 vtech_image::impl::free_block_count() +u32 vtech_impl::free_block_count() { auto fmap = m_blockdev.get(15); u32 nf = 0; @@ -367,5 +426,3 @@ u32 vtech_image::impl::free_block_count() } return nf; } - -} // namespace fs diff --git a/src/lib/formats/fs_vtech.h b/src/lib/formats/fs_vtech.h index 16afee02cad..360021708ac 100644 --- a/src/lib/formats/fs_vtech.h +++ b/src/lib/formats/fs_vtech.h @@ -14,68 +14,6 @@ namespace fs { class vtech_image : public manager_t { public: - class impl : public filesystem_t { - public: - class root_dir : public idir_t { - public: - root_dir(impl &i) : m_fs(i) {} - virtual ~root_dir() = default; - - virtual void drop_weak_references() override; - - virtual meta_data metadata() override; - virtual void metadata_change(const meta_data &info) override; - virtual std::vector contents() override; - virtual file_t file_get(u64 key) override; - virtual dir_t dir_get(u64 key) override; - virtual file_t file_create(const meta_data &info) override; - virtual void file_delete(u64 key) override; - - void update_file(u16 key, const u8 *entry); - - private: - impl &m_fs; - - std::pair get_dir_block(u64 key); - }; - - class file : public ifile_t { - public: - file(impl &fs, root_dir *dir, const u8 *entry, u16 key); - virtual ~file() = default; - - virtual void drop_weak_references() override; - - virtual meta_data metadata() override; - virtual void metadata_change(const meta_data &info) override; - virtual std::vector read_all() override; - virtual void replace(const std::vector &data) override; - - private: - impl &m_fs; - root_dir *m_dir; - u16 m_key; - u8 m_entry[18]; - }; - - impl(fsblk_t &blockdev); - virtual ~impl() = default; - - virtual void format(const meta_data &meta) override; - virtual meta_data metadata() override; - virtual void metadata_change(const meta_data &info) override; - virtual dir_t root() override; - - void drop_root_ref(); - - std::vector> allocate_blocks(u32 count); - void free_blocks(const std::vector> &blocks); - u32 free_block_count(); - - private: - dir_t m_root; - }; - vtech_image() : manager_t() {} virtual const char *name() const override; diff --git a/src/lib/formats/fsblk_vec.cpp b/src/lib/formats/fsblk_vec.cpp index e80cad8c51c..36f11cc9d89 100644 --- a/src/lib/formats/fsblk_vec.cpp +++ b/src/lib/formats/fsblk_vec.cpp @@ -34,7 +34,7 @@ u32 fsblk_vec_t::block_count() const fsblk_t::block_t fsblk_vec_t::get(u32 id) { if(id >= block_count()) - throw std::out_of_range(util::string_format("Block number overflow: requiring block %d on device of size %d (%d bytes, block size %d)", id, block_count(), m_data.size(), m_block_size)); + throw std::out_of_range(util::string_format("Block number overflow: requiring block %d on device of size %d (%d bytes, block size %d)\n", id, block_count(), m_data.size(), m_block_size)); return block_t(new blk_t(m_data.data() + m_block_size*id, m_block_size)); } diff --git a/src/lib/formats/fsmgr.cpp b/src/lib/formats/fsmgr.cpp index 3f37c79691a..599a2f32d61 100644 --- a/src/lib/formats/fsmgr.cpp +++ b/src/lib/formats/fsmgr.cpp @@ -103,27 +103,11 @@ char manager_t::directory_separator() const return 0; // Subdirectories not supported by default } -void filesystem_t::format(const meta_data &meta) -{ - throw std::logic_error("format called on a filesystem not supporting it"); -} - -filesystem_t::dir_t filesystem_t::root() -{ - throw std::logic_error("root called on a filesystem not supporting it"); -} - -meta_data filesystem_t::metadata() -{ - throw std::logic_error("filesystem_t::metadata called on a filesystem not supporting it"); -} - void fsblk_t::set_block_size(u32 block_size) { m_block_size = block_size; } - u8 *fsblk_t::iblock_t::offset(const char *function, u32 off, u32 size) { if(off + size > m_size) @@ -374,45 +358,75 @@ std::string filesystem_t::trim_end_spaces(const std::string &str) return str.substr(0, (std::string::npos != i) ? (i + 1) : 0); } -filesystem_t::file_t filesystem_t::idir_t::file_create(const meta_data &info) +meta_data filesystem_t::volume_metadata() { - throw std::logic_error("file_create called on a filesystem not supporting write"); + return meta_data(); } -void filesystem_t::idir_t::file_delete(uint64_t key) +err_t filesystem_t::volume_metadata_change(const meta_data &meta) { - throw std::logic_error("file_delete called on a filesystem not supporting write"); + return ERR_UNSUPPORTED; } - -void filesystem_t::ifile_t::replace(const std::vector &data) +std::pair filesystem_t::metadata(const std::vector &path) { - throw std::logic_error("replace called on a filesystem not supporting write"); + return std::make_pair(ERR_UNSUPPORTED, meta_data()); } -void filesystem_t::ifile_t::rsrc_replace(const std::vector &data) +err_t filesystem_t::metadata_change(const std::vector &path, const meta_data &meta) { - throw std::logic_error("rsrc_replace called on a filesystem not supporting write or resource forks"); + return ERR_UNSUPPORTED; } -void filesystem_t::ifile_t::metadata_change(const meta_data &info) +std::pair> filesystem_t::directory_contents(const std::vector &path) { - throw std::logic_error("metadata_change called on a filesystem not supporting write"); + return std::make_pair(ERR_UNSUPPORTED, std::vector()); } -void filesystem_t::idir_t::metadata_change(const meta_data &info) +err_t filesystem_t::rename(const std::vector &opath, const std::vector &npath) { - throw std::logic_error("metadata_change called on a filesystem not supporting write"); + return ERR_UNSUPPORTED; } -void filesystem_t::metadata_change(const meta_data &info) +err_t filesystem_t::remove(const std::vector &path) { - throw std::logic_error("metadata_change called on a filesystem not supporting write"); + return ERR_UNSUPPORTED; } -std::vector filesystem_t::ifile_t::rsrc_read_all() +err_t filesystem_t::dir_create(const std::vector &path, const meta_data &meta) { - throw std::logic_error("rsrc_read_all called on a filesystem without resource forks"); + return ERR_UNSUPPORTED; } +err_t filesystem_t::file_create(const std::vector &path, const meta_data &meta) +{ + return ERR_UNSUPPORTED; +} + +std::pair> filesystem_t::file_read(const std::vector &path) +{ + return std::make_pair(ERR_UNSUPPORTED, std::vector()); +} + +err_t filesystem_t::file_write(const std::vector &path, const std::vector &data) +{ + return ERR_UNSUPPORTED; +} + +std::pair> filesystem_t::file_rsrc_read(const std::vector &path) +{ + return std::make_pair(ERR_UNSUPPORTED, std::vector()); +} + +err_t filesystem_t::file_rsrc_write(const std::vector &path, const std::vector &data) +{ + return ERR_UNSUPPORTED; +} + +err_t filesystem_t::format(const meta_data &meta) +{ + return ERR_UNSUPPORTED; +} + + } // namespace fs diff --git a/src/lib/formats/fsmgr.h b/src/lib/formats/fsmgr.h index 201dfe389bb..c75b932cce9 100644 --- a/src/lib/formats/fsmgr.h +++ b/src/lib/formats/fsmgr.h @@ -18,6 +18,15 @@ using u16 = uint16_t; using u32 = uint32_t; using u64 = uint64_t; +enum err_t { + ERR_OK = 0, + ERR_UNSUPPORTED, + ERR_INVALID, + ERR_NOT_FOUND, + ERR_NOT_EMPTY, + ERR_NO_SPACE, +}; + template class refcounted_outer { public: refcounted_outer(bool weak) : m_object(nullptr), m_is_weak_ref(weak) {} @@ -118,15 +127,14 @@ public: enum class dir_entry_type { dir, file, - system_file, }; struct dir_entry { std::string m_name; dir_entry_type m_type; - u64 m_key; + meta_data m_meta; - dir_entry(std::string &&name, dir_entry_type type, u64 key) : m_name(std::move(name)), m_type(type), m_key(key) {} + dir_entry(dir_entry_type type, const meta_data &meta) : m_name(meta.get_string(meta_name::name)), m_type(type), m_meta(std::move(meta)) {} }; class fsblk_t { @@ -200,83 +208,50 @@ protected: class filesystem_t { public: - class dir_t; - class file_t; - -protected: - class idir_t : public refcounted_inner { - public: - idir_t() : refcounted_inner() {} - virtual ~idir_t() = default; - - virtual meta_data metadata() = 0; - virtual void metadata_change(const meta_data &info); - virtual std::vector contents() = 0; - virtual file_t file_get(u64 key) = 0; - virtual dir_t dir_get(u64 key) = 0; - virtual file_t file_create(const meta_data &info); - virtual void file_delete(u64 key); - }; - - class ifile_t : public refcounted_inner { - public: - ifile_t() : refcounted_inner() {} - virtual ~ifile_t() = default; - - virtual meta_data metadata() = 0; - virtual void metadata_change(const meta_data &info); - virtual std::vector read_all() = 0; - virtual void replace(const std::vector &data); - virtual std::vector rsrc_read_all(); - virtual void rsrc_replace(const std::vector &data); - }; - -public: - class dir_t : public refcounted_outer { - public: - dir_t(bool weak = false) : refcounted_outer(weak) {} - dir_t(idir_t *dir, bool weak = true) : refcounted_outer(dir, weak) {} - virtual ~dir_t() = default; - - dir_t strong() { return dir_t(m_object, false); } - dir_t weak() { return dir_t(m_object, true); } - - meta_data metadata() { return m_object->metadata(); } - void metadata_change(const meta_data &info) { m_object->metadata_change(info); } - std::vector contents() { return m_object->contents(); } - file_t file_get(u64 key) { return m_object->file_get(key); } - dir_t dir_get(u64 key) { return m_object->dir_get(key); } - file_t file_create(const meta_data &info) { return m_object->file_create(info); } - void file_delete(u64 key) { m_object->file_delete(key); } - }; - - class file_t : public refcounted_outer { - public: - file_t(bool weak = false) : refcounted_outer(weak) {} - file_t(ifile_t *file, bool weak = true) : refcounted_outer(file, weak) {} - virtual ~file_t() = default; - - file_t strong() { return file_t(m_object, false); } - file_t weak() { return file_t(m_object, true); } - - meta_data metadata() { return m_object->metadata(); } - void metadata_change(const meta_data &info) { m_object->metadata_change(info); } - std::vector read_all() { return m_object->read_all(); } - void replace(const std::vector &data) { m_object->replace(data); } - std::vector rsrc_read_all() { return m_object->rsrc_read_all(); } - void rsrc_replace(const std::vector &data) { m_object->rsrc_replace(data); } - }; - - filesystem_t(fsblk_t &blockdev, u32 size) : m_blockdev(blockdev) { - m_blockdev.set_block_size(size); - } - virtual ~filesystem_t() = default; - virtual dir_t root(); - virtual void format(const meta_data &meta); - virtual meta_data metadata(); - virtual void metadata_change(const meta_data &info); + // Get the metadata for the volume + virtual meta_data volume_metadata(); + + // Change the metadata for the volume + virtual err_t volume_metadata_change(const meta_data &meta); + + // Get the metadata for a file or a directory. Empty path targets the root directory + virtual std::pair metadata(const std::vector &path); + + // Change the metadata for a file or a directory. Empty path targets the root directory + virtual err_t metadata_change(const std::vector &path, const meta_data &meta); + + // Get the contents of a directory, empty path targets the root directory + virtual std::pair> directory_contents(const std::vector &path); + + // Rename a file or a directory. In contrast to metadata_change, this can move the object + // between directories + virtual err_t rename(const std::vector &opath, const std::vector &npath); + + // Remove a file or a directory. Directories must be empty (e.g. it's not recursive) + virtual err_t remove(const std::vector &path); + + // Create a directory, path designates where the directory must be, directory name is in meta + virtual err_t dir_create(const std::vector &path, const meta_data &meta); + + // Create an empty file, path designates where the file must be, file name is in meta + virtual err_t file_create(const std::vector &path, const meta_data &meta); + + // Read the contents of a file + virtual std::pair> file_read(const std::vector &path); + + // Replace the contents of a file, the file must already exist + virtual err_t file_write(const std::vector &path, const std::vector &data); + + // Read the resource fork of a file on systems that handle those + virtual std::pair> file_rsrc_read(const std::vector &path); + + // Replace the resource fork of a file, the file must already exist + virtual err_t file_rsrc_write(const std::vector &path, const std::vector &data); + + // Format an image, provide the volume metadata + virtual err_t format(const meta_data &meta); static void copy(u8 *p, const u8 *src, u32 size); static void fill(u8 *p, u8 data, u32 size); @@ -298,9 +273,13 @@ public: static u32 r24l(const u8 *p); static u32 r32l(const u8 *p); +protected: + filesystem_t(fsblk_t &blockdev, u32 size) : m_blockdev(blockdev) { + m_blockdev.set_block_size(size); + } + static std::string trim_end_spaces(const std::string &str); -protected: fsblk_t &m_blockdev; }; diff --git a/src/tools/floptool.cpp b/src/tools/floptool.cpp index f68c4110edc..e09ccccd349 100644 --- a/src/tools/floptool.cpp +++ b/src/tools/floptool.cpp @@ -263,47 +263,42 @@ static int flopcreate(int argc, char *argv[]) return ih.floppy_save(dest_format); } -static void dir_scan(u32 depth, fs::filesystem_t::dir_t dir, std::vector> &entries, const std::unordered_map &nmap, size_t nc, const std::vector &dmetad, const std::vector &fmetad) +static void dir_scan(fs::filesystem_t *fs, u32 depth, const std::vector &path, std::vector> &entries, const std::unordered_map &nmap, size_t nc, const std::vector &dmetad, const std::vector &fmetad) { std::string head; for(u32 i = 0; i != depth; i++) head += " "; - auto contents = dir.contents(); + auto [err, contents] = fs->directory_contents(path); + if(err) + return; for(const auto &c : contents) { size_t id = entries.size(); entries.resize(id+1); entries[id].resize(nc); switch(c.m_type) { case fs::dir_entry_type::dir: { - auto subdir = dir.dir_get(c.m_key); - auto meta = subdir.metadata(); - if (!meta.has(fs::meta_name::name)) - meta.set(fs::meta_name::name, c.m_name); for(const auto &m : dmetad) { - if(!meta.has(m.m_name)) + if(!c.m_meta.has(m.m_name)) continue; size_t slot = nmap.find(m.m_name)->second; - std::string val = meta.get(m.m_name).to_string(); + std::string val = c.m_meta.get(m.m_name).to_string(); if(slot == 0) val = head + "dir " + val; entries[id][slot] = val; } - dir_scan(depth+1, subdir, entries, nmap, nc, dmetad, fmetad); + auto npath = path; + npath.push_back(c.m_name); + dir_scan(fs, depth+1, npath, entries, nmap, nc, dmetad, fmetad); break; } - case fs::dir_entry_type::file: - case fs::dir_entry_type::system_file: { - auto file = dir.file_get(c.m_key); - auto meta = file.metadata(); - if (!meta.has(fs::meta_name::name)) - meta.set(fs::meta_name::name, c.m_name); + case fs::dir_entry_type::file: { for(const auto &m : fmetad) { - if(!meta.has(m.m_name)) + if(!c.m_meta.has(m.m_name)) continue; size_t slot = nmap.find(m.m_name)->second; - std::string val = meta.get(m.m_name).to_string(); + std::string val = c.m_meta.get(m.m_name).to_string(); if(slot == 0) - val = head + (c.m_type == fs::dir_entry_type::system_file ? "sys " : "file ") + val; + val = head + "file " + val; entries[id][slot] = val; } break; @@ -319,7 +314,7 @@ static int generic_dir(image_handler &ih) auto fmetad = fsm->file_meta_description(); auto dmetad = fsm->directory_meta_description(); - auto vmeta = fs->metadata(); + auto vmeta = fs->volume_metadata(); if(!vmeta.empty()) { std::string vinf = "Volume:"; for(const auto &e : vmetad) @@ -340,14 +335,13 @@ static int generic_dir(image_handler &ih) for(size_t i = 0; i != names.size(); i++) nmap[names[i]] = i; - auto root = fs->root(); std::vector> entries; entries.resize(1); for(fs::meta_name n : names) entries[0].push_back(fs::meta_data::entry_name(n)); - dir_scan(0, root, entries, nmap, names.size(), dmetad, fmetad); + dir_scan(fs, 0, std::vector(), entries, nmap, names.size(), dmetad, fmetad); std::vector sizes(names.size()); @@ -443,50 +437,20 @@ static int generic_read(image_handler &ih, const char *srcpath, const char *dstp auto [fsm, fs] = ih.get_fs(); std::vector path = ih.path_split(srcpath); - - auto dir = fs->root(); - std::string apath; - for(unsigned int i = 0; i < path.size() - 1; i++) { - auto c = dir.contents(); - unsigned int j; - for(j = 0; j != c.size(); j++) - if(c[j].m_name == path[i]) - break; - if(j == c.size()) { - fprintf(stderr, "Error: directory %s%c%s not found\n", apath.c_str(), fsm->directory_separator(), path[i].c_str()); - return 1; - } - if(c[j].m_type != fs::dir_entry_type::dir) { - fprintf(stderr, "Error: %s%c%s is not a directory\n", apath.c_str(), fsm->directory_separator(), path[i].c_str()); - return 1; - } - dir = dir.dir_get(c[j].m_key); - apath += fsm->directory_separator() + path[i]; - } - - auto c = dir.contents(); - unsigned int j; - for(j = 0; j != c.size(); j++) - if(c[j].m_name == path.back()) - break; - if(j == c.size()) { - fprintf(stderr, "Error: file %s%c%s not found\n", apath.c_str(), fsm->directory_separator(), path.back().c_str()); - return 1; - } - auto file = dir.file_get(c[j].m_key); - auto meta = file.metadata(); - - if(!meta.has(fs::meta_name::length)) { - fprintf(stderr, "Error: %s%c%s is not a readable file\n", apath.c_str(), fsm->directory_separator(), path.back().c_str()); + auto [err, dfork] = fs->file_read(path); + if(err) { + if(err == fs::ERR_NOT_FOUND) + fprintf(stderr, "File not found.\n"); + else + fprintf(stderr, "Unknown error (%d).\n", err); return 1; } - image_handler::fsave(dstpath, file.read_all()); + image_handler::fsave(dstpath, dfork); - bool has_rsrc = fsm->has_rsrc() && meta.has(fs::meta_name::rsrc_length); - - if(has_rsrc) - image_handler::fsave_rsrc(image_handler::path_make_rsrc(dstpath), file.rsrc_read_all()); + auto [err2, rfork] = fs->file_rsrc_read(path); + if(!err2 && !rfork.empty()) + image_handler::fsave_rsrc(image_handler::path_make_rsrc(dstpath), rfork); return 0; } @@ -568,44 +532,37 @@ static int generic_write(image_handler &ih, const char *srcpath, const char *dst auto [fsm, fs] = ih.get_fs(); std::vector path = ih.path_split(dstpath); + auto [err, meta] = fs->metadata(path); - auto dir = fs->root(); - std::string apath; - for(unsigned int i = 0; i < path.size() - 1; i++) { - auto c = dir.contents(); - unsigned int j; - for(j = 0; j != c.size(); j++) - if(c[j].m_name == path[i]) - break; - if(j == c.size()) { - fprintf(stderr, "Error: directory %s%c%s not found\n", apath.c_str(), fsm->directory_separator(), path[i].c_str()); + if(err) { + fs::meta_data meta; + meta.set(fs::meta_name::name, path.back()); + auto dpath = path; + dpath.pop_back(); + err = fs->file_create(dpath, meta); + if(!err) { + fprintf(stderr, "File creation failure.\n"); return 1; } - if(c[j].m_type != fs::dir_entry_type::dir) { - fprintf(stderr, "Error: %s%c%s is not a directory\n", apath.c_str(), fsm->directory_separator(), path[i].c_str()); - return 1; - } - dir = dir.dir_get(c[j].m_key); - apath += fsm->directory_separator() + path[i]; } + auto dfork = image_handler::fload(srcpath); + err = fs->file_write(path, dfork); + if(!err) { + fprintf(stderr, "File writing failure.\n"); + return 1; + } - fs::meta_data meta; - meta.set(fs::meta_name::name, path.back()); - - auto file = dir.file_create(meta); - auto filedata = image_handler::fload(srcpath); - file.replace(filedata); - - bool has_rsrc = fsm->has_rsrc(); - - if(has_rsrc) { + if(fsm->has_rsrc()) { std::string rpath = image_handler::path_make_rsrc(dstpath); if(image_handler::fexists(rpath)) { - filedata = image_handler::fload_rsrc(rpath); - if(!filedata.empty()) - file.rsrc_replace(filedata); + auto rfork = image_handler::fload_rsrc(rpath); + if(!rfork.empty()) { + err = fs->file_rsrc_write(path, rfork); + fprintf(stderr, "File resource fork writing failure.\n"); + return 1; + } } }