summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
-rw-r--r--src/lib/formats/fs_coco_os9.cpp689
-rw-r--r--src/lib/formats/fs_coco_rsdos.cpp431
-rw-r--r--src/lib/formats/fs_oric_jasmin.cpp793
-rw-r--r--src/lib/formats/fs_oric_jasmin.h2
-rw-r--r--src/lib/formats/fs_prodos.cpp353
-rw-r--r--src/lib/formats/fs_vtech.cpp331
-rw-r--r--src/lib/formats/fs_vtech.h62
-rw-r--r--src/lib/formats/fsblk_vec.cpp2
-rw-r--r--src/lib/formats/fsmgr.cpp78
-rw-r--r--src/lib/formats/fsmgr.h127
-rw-r--r--src/tools/floptool.cpp139
11 files changed, 901 insertions, 2106 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
-//**************************************************************************
-
-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<u32, u16> 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<meta_description> entity_meta_description();
+namespace fs { const coco_os9_image COCO_OS9; }
+namespace {
-// ======================> volume_header
-
-class volume_header
-{
+class coco_os9_impl : public filesystem_t {
public:
- volume_header(fsblk_t::block_t &&block);
-
- 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;
-};
+ coco_os9_impl(fsblk_t &blockdev);
+ virtual ~coco_os9_impl() = default;
-
-// ======================> 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;
+ static bool is_ignored_filename(std::string_view name);
+ static bool validate_filename(std::string_view name);
};
-
-
-// ======================> 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<u8> 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<dir_entry> 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<u8> 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<u32> &variants) const
+void coco_os9_image::enumerate_f(floppy_enumerator &fe, u32 form_factor, const std::vector<u32> &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<meta_description> fs::coco_os9_image::volume_meta_description() const
+std::vector<meta_description> coco_os9_image::volume_meta_description() const
{
std::vector<meta_description> 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<meta_description> fs::coco_os9_image::volume_meta_description() cons
// file_meta_description
//-------------------------------------------------
-std::vector<meta_description> fs::coco_os9_image::file_meta_description() const
-{
- return entity_meta_description();
-}
-
-
-//-------------------------------------------------
-// directory_meta_description
-//-------------------------------------------------
-
-std::vector<meta_description> fs::coco_os9_image::directory_meta_description() const
-{
- return entity_meta_description();
-}
-
-
-//-------------------------------------------------
-// mount
-//-------------------------------------------------
-
-std::unique_ptr<filesystem_t> 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<impl>(blockdev, std::move(header));
-}
-
-
-//-------------------------------------------------
-// entity_meta_description
-//-------------------------------------------------
-
-namespace {
-
-std::vector<meta_description> entity_meta_description()
+std::vector<meta_description> coco_os9_image::file_meta_description() const
{
std::vector<meta_description> 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<meta_description> entity_meta_description()
//-------------------------------------------------
-// pick_os9_string
-//-------------------------------------------------
-
-std::string pick_os9_string(std::string_view raw_string)
-{
- // 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;
-
-}
-
-
-//-------------------------------------------------
-// to_os9_string
-//-------------------------------------------------
-
-std::string to_os9_string(std::string_view s, size_t length)
-{
- 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;
-}
-
-
-//-------------------------------------------------
-// pick_integer_be
-//-------------------------------------------------
-
-u32 pick_integer_be(const u8 *data, int length)
-{
- u32 result = 0;
- for (int i = 0; i < length; i++)
- result |= u32(data[length - i - 1]) << i * 8;
- return result;
-}
-
-
-//-------------------------------------------------
-// from_os9_date
+// directory_meta_description
//-------------------------------------------------
-util::arbitrary_datetime from_os9_date(u32 os9_date, u16 os9_time)
+std::vector<meta_description> coco_os9_image::directory_meta_description() const
{
- 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;
+ return file_meta_description();
}
-
//-------------------------------------------------
-// to_os9_date
+// mount
//-------------------------------------------------
-std::tuple<u32, u16> to_os9_date(const util::arbitrary_datetime &datetime)
+std::unique_ptr<filesystem_t> coco_os9_image::mount(fsblk_t &blockdev) const
{
- 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);
+ return std::make_unique<coco_os9_impl>(blockdev);
}
-
//-------------------------------------------------
-// validate_filename
+// impl ctor
//-------------------------------------------------
-bool validate_filename(std::string_view name)
+coco_os9_impl::coco_os9_impl(fsblk_t &blockdev)
+ : filesystem_t(blockdev, 256)
{
- 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
-//-------------------------------------------------
-
-volume_header::volume_header(fsblk_t::block_t &&block)
- : m_block(std::move(block))
-{
-}
-
-
-//-------------------------------------------------
-// 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<u8> 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<u8> impl::read_file_data(const file_header &header) const
-{
- std::vector<u8> 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<u8> 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<dir_entry> impl::directory::contents()
-{
- // read the directory data
- std::vector<u8> directory_data = m_fs.read_file_data(m_file_header);
-
- // and assemble results
- std::vector<dir_entry> 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
+// validate_filename
//-------------------------------------------------
-filesystem_t::dir_t impl::directory::dir_get(u64 key)
+bool coco_os9_impl::validate_filename(std::string_view name)
{
- return dir_t(m_fs.open_directory(u32(key)));
+ 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();
}
-
-};
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 <optional>
#include <string_view>
-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;
-
- virtual void drop_weak_references() override;
- virtual meta_data metadata() override;
- virtual std::vector<dir_entry> 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<u8> 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<u8> 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);
-};
+ coco_rsdos_impl(fsblk_t &blockdev);
+ virtual ~coco_rsdos_impl() = default;
-// 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<u32> &variants) const
+void coco_rsdos_image::enumerate_f(floppy_enumerator &fe, u32 form_factor, const std::vector<u32> &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<meta_description> fs::coco_rsdos_image::file_meta_description() const
+std::vector<meta_description> coco_rsdos_image::file_meta_description() const
{
std::vector<meta_description> 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<meta_description> fs::coco_rsdos_image::file_meta_description() cons
// mount
//-------------------------------------------------
-std::unique_ptr<filesystem_t> fs::coco_rsdos_image::mount(fsblk_t &blockdev) const
+std::unique_ptr<filesystem_t> coco_rsdos_image::mount(fsblk_t &blockdev) const
{
- return std::make_unique<impl>(blockdev);
+ return std::make_unique<coco_rsdos_impl>(blockdev);
}
@@ -214,9 +137,7 @@ std::unique_ptr<filesystem_t> 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<dir_entry> impl::root_dir::contents()
-{
- u64 key = 0;
- std::vector<dir_entry> 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 &sector = *reinterpret_cast<const rsdos_dirent_sector *>(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 &sector = *reinterpret_cast<const rsdos_dirent_sector *>(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<u8> 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<u8> impl::file::read_all()
-{
- std::vector<u8> 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 meta_data volume_metadata() override;
+ virtual err_t volume_metadata_change(const meta_data &info) override;
+ virtual std::pair<err_t, meta_data> metadata(const std::vector<std::string> &path) override;
+ virtual err_t metadata_change(const std::vector<std::string> &path, const meta_data &meta) override;
+
+ virtual std::pair<err_t, std::vector<dir_entry>> directory_contents(const std::vector<std::string> &path) override;
+ virtual err_t rename(const std::vector<std::string> &opath, const std::vector<std::string> &npath) override;
+ virtual err_t remove(const std::vector<std::string> &path) override;
+
+ virtual err_t file_create(const std::vector<std::string> &path, const meta_data &meta) override;
+
+ virtual std::pair<err_t, std::vector<u8>> file_read(const std::vector<std::string> &path) override;
+ virtual err_t file_write(const std::vector<std::string> &path, const std::vector<u8> &data) override;
- 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 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<u16> allocate_blocks(u32 count);
void free_blocks(const std::vector<u16> &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<dir_entry> 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<fsblk_t::block_t, u32> 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<u8> read_all() override;
- virtual void replace(const std::vector<u8> &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<u8> read_all() override;
- virtual void replace(const std::vector<u8> &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<fsblk_t::block_t, u32, bool> file_find(std::string name);
};
}
-meta_data impl::metadata()
+
+const char *oric_jasmin_image::name() const
{
- meta_data res;
- auto bdir = m_blockdev.get(20*17);
- int len = 8;
- while(len > 0 && bdir.rodata()[0xf8 + len - 1] == ' ')
- len--;
+ return "oric_jasmin";
+}
- res.set(meta_name::name, bdir.rstr(0xf8, len));
- return res;
+const char *oric_jasmin_image::description() const
+{
+ return "Oric Jasmin";
}
-bool oric_jasmin_image::validate_filename(std::string name)
+void oric_jasmin_image::enumerate_f(floppy_enumerator &fe, u32 form_factor, const std::vector<u32> &variants) 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;
+ 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<filesystem_t> oric_jasmin_image::mount(fsblk_t &blockdev) const
+{
+ return std::make_unique<oric_jasmin_impl>(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<meta_description> oric_jasmin_image::volume_meta_description() const
+{
+ std::vector<meta_description> 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<meta_description> oric_jasmin_image::file_meta_description() const
{
std::vector<meta_description> 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<meta_description> 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,38 +227,53 @@ void impl::format(const meta_data &meta)
bdir.fill(0xff);
bdir.w16l(0, 0x0000);
bdir.w16l(2, 0x0000);
-}
-impl::impl(fsblk_t &blockdev) : filesystem_t(blockdev, 256), m_root(true)
-{
+ return ERR_OK;
}
-bool impl::ref_valid(u16 ref)
+meta_data oric_jasmin_impl::volume_metadata()
{
- 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;
+ 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;
}
-u32 impl::cs_to_block(u16 ref)
+err_t oric_jasmin_impl::volume_metadata_change(const meta_data &meta)
{
- u8 track = ref >> 8;
- u8 sector = ref & 0xff;
- return track * 17 + sector - 1;
+ 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 ERR_OK;
}
-u16 impl::block_to_cs(u32 block)
+std::string oric_jasmin_impl::file_name_prepare(std::string fname)
{
- u8 track = block / 17;
- u8 sector = (block % 17) + 1;
- return (track << 8) | sector;
+ std::string nname;
+ size_t i;
+ for(i = 0; i != 8 && i != fname.size() && fname[i] != '.'; i++)
+ nname += fname[i];
+ if(nname.size() != 8)
+ nname.insert(nname.end(), 8 - nname.size(), ' ');
+ nname += '.';
+ while(i != fname.size() && fname[i] != '.')
+ i++;
+ if(i != fname.size())
+ i++;
+ while(i != fname.size() && nname.size() != 12)
+ nname += fname[i++];
+ if(nname.size() != 12)
+ nname.insert(nname.end(), 12 - nname.size(), ' ');
+ return nname;
}
-std::string impl::read_file_name(const u8 *p)
+std::string oric_jasmin_impl::file_name_read(const u8 *p)
{
int main_len;
for(main_len = 8; main_len > 0; main_len--)
@@ -250,239 +291,180 @@ std::string impl::read_file_name(const u8 *p)
return name;
}
-
-filesystem_t::dir_t impl::root()
+bool oric_jasmin_impl::file_is_system(const u8 *entry)
{
- if(!m_root)
- m_root = new root_dir(*this);
- return m_root.strong();
+ u16 ref = r16b(entry);
+ return ref == 0 && r32b(entry+0xb) == 0x2e535953;
}
-void impl::drop_root_ref()
-{
- m_root = nullptr;
-}
-void impl::root_dir::drop_weak_references()
+meta_data oric_jasmin_impl::file_metadata(const u8 *entry)
{
- m_fs.drop_root_ref();
-}
+ meta_data res;
-meta_data impl::root_dir::metadata()
-{
- return meta_data();
+ 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<dir_entry> impl::root_dir::contents()
+std::tuple<fsblk_t::block_t, u32, bool> oric_jasmin_impl::file_find(std::string name)
{
- std::vector<dir_entry> res;
-
- auto bdir = m_fs.m_blockdev.get(20*17+1);
- u64 id = 0;
+ 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 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 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 || !m_fs.ref_valid(ref))
- break;
- bdir = m_fs.m_blockdev.get(cs_to_block(ref));
- }
- return res;
-}
+ if(!ref || !ref_valid(ref))
+ return std::make_tuple(bdir, 0U, false);
-std::pair<fsblk_t::block_t, u32> impl::root_dir::get_dir_block(u64 key)
-{
- 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;
+ bdir = m_blockdev.get(cs_to_block(ref));
}
- return std::pair<fsblk_t::block_t, u32>(bdir, 4 + key * 18);
}
-filesystem_t::file_t impl::root_dir::file_get(u64 key)
+std::pair<err_t, meta_data> oric_jasmin_impl::metadata(const std::vector<std::string> &path)
{
- 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(path.size() != 1)
+ return std::make_pair(ERR_NOT_FOUND, meta_data());
- 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));
-}
+ auto [bdir, off, sys] = file_find(path[0]);
+ if(!off)
+ return std::make_pair(ERR_NOT_FOUND, meta_data());
-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);
+ return std::make_pair(ERR_OK, file_metadata(bdir.rodata() + off));
}
-filesystem_t::dir_t impl::root_dir::dir_get(u64 key)
+err_t oric_jasmin_impl::metadata_change(const std::vector<std::string> &path, const meta_data &meta)
{
- throw std::logic_error("Directories not supported");
-}
+ if(path.size() != 1)
+ return ERR_NOT_FOUND;
-std::string impl::file_name_prepare(std::string fname)
-{
- std::string nname;
- size_t i;
- for(i = 0; i != 8 && i != fname.size() && fname[i] != '.'; i++)
- nname += fname[i];
- if(nname.size() != 8)
- nname.insert(nname.end(), 8 - nname.size(), ' ');
- nname += '.';
- while(i != fname.size() && fname[i] != '.')
- i++;
- if(i != fname.size())
- i++;
- while(i != fname.size() && nname.size() != 12)
- nname += fname[i++];
- if(nname.size() != 12)
- nname.insert(nname.end(), 12 - nname.size(), ' ');
- return nname;
-}
+ auto [bdir, off, sys] = file_find(path[0]);
+ if(!off)
+ return ERR_NOT_FOUND;
-impl::file::file(impl &fs, root_dir *dir, const u8 *entry, u16 key) : m_fs(fs), m_dir(dir), m_key(key)
-{
- memcpy(m_entry, entry, 18);
-}
+ 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));
-void impl::file::drop_weak_references()
-{
+ return ERR_OK;
}
-meta_data impl::file::metadata()
+std::pair<err_t, std::vector<dir_entry>> oric_jasmin_impl::directory_contents(const std::vector<std::string> &path)
{
- meta_data res;
+ std::pair<err_t, std::vector<dir_entry>> 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));
+ if(path.size() != 0) {
+ res.first = ERR_NOT_FOUND;
+ return res;
+ }
- 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));
+ res.first = ERR_OK;
- return res;
-}
-
-std::vector<u8> impl::file::read_all()
-{
- std::vector<u8> 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;
+ 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));
+ }
}
- ref = iblk.r16b(2);
- if(!m_fs.ref_valid(ref))
+ u16 ref = bdir.r16b(2);
+ if(!ref || !ref_valid(ref))
break;
- iblk = m_fs.m_blockdev.get(cs_to_block(ref));
+ bdir = m_blockdev.get(cs_to_block(ref));
}
- done:
- data.resize(length);
- return data;
+ return res;
}
-impl::system_file::system_file(impl &fs, root_dir *dir, const u8 *entry, u16 key) : m_fs(fs), m_dir(dir), m_key(key)
+err_t oric_jasmin_impl::rename(const std::vector<std::string> &opath, const std::vector<std::string> &npath)
{
- memcpy(m_entry, entry, 18);
-}
+ if(opath.size() != 1 || npath.size() != 1)
+ return ERR_NOT_FOUND;
-void impl::system_file::drop_weak_references()
-{
-}
+ auto [bdir, off, sys] = file_find(opath[0]);
+ if(!off)
+ return ERR_NOT_FOUND;
-meta_data impl::system_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));
- res.set(meta_name::length, 0x3e00);
+ wstr(bdir.data() + off + 0x03, file_name_prepare(npath[0]));
- return res;
+ return ERR_OK;
}
-std::vector<u8> impl::system_file::read_all()
+err_t oric_jasmin_impl::remove(const std::vector<std::string> &path)
{
- std::vector<u8> 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);
- }
- return data;
+ return ERR_UNSUPPORTED;
}
-impl::file_t impl::root_dir::file_create(const meta_data &info)
+err_t oric_jasmin_impl::file_create(const std::vector<std::string> &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<err_t, std::vector<u8>> oric_jasmin_impl::file_read(const std::vector<std::string> &path)
{
-}
+ std::vector<u8> data;
-void impl::file::replace(const std::vector<u8> &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<u16> 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<u16> 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);
}
- w16l(m_entry + 0x10, need_ns);
- w16b(m_entry + 0x00, blocks[0]);
- m_dir->update_file(m_key, m_entry);
-}
-
-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);
+ return std::make_pair(ERR_OK, data);
+}
+
+err_t oric_jasmin_impl::file_write(const std::vector<std::string> &path, const std::vector<u8> &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<u16> 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<u16> 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]);
}
+ return ERR_OK;
}
-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<u8> &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<u16> impl::allocate_blocks(u32 count)
+std::vector<u16> oric_jasmin_impl::allocate_blocks(u32 count)
{
std::vector<u16> blocks;
if(free_block_count() < count)
@@ -636,7 +628,7 @@ std::vector<u16> impl::allocate_blocks(u32 count)
return blocks;
}
-void impl::free_blocks(const std::vector<u16> &blocks)
+void oric_jasmin_impl::free_blocks(const std::vector<u16> &blocks)
{
auto fmap = m_blockdev.get(20*17);
for(u16 ref : blocks) {
@@ -650,7 +642,7 @@ void impl::free_blocks(const std::vector<u16> &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<u32> &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<filesystem_t> oric_jasmin_image::mount(fsblk_t &blockdev) const
-{
- return std::make_unique<impl>(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<meta_description> oric_jasmin_image::volume_meta_description() const
-{
- std::vector<meta_description> 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<meta_description> volume_meta_description() const override;
virtual std::vector<meta_description> 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<err_t, meta_data> metadata(const std::vector<std::string> &path) override;
- virtual meta_data metadata() override;
- virtual dir_t root() override;
+ virtual std::pair<err_t, std::vector<dir_entry>> directory_contents(const std::vector<std::string> &path) override;
- void drop_root_ref();
+ virtual std::pair<err_t, std::vector<u8>> file_read(const std::vector<std::string> &path) override;
+ virtual std::pair<err_t, std::vector<u8>> file_rsrc_read(const std::vector<std::string> &path) override;
- static util::arbitrary_datetime prodos_to_dt(u32 date);
- std::vector<dir_entry> 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<dir_entry> 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<fsblk_t::block_t, const u8 *> get_entry_ro(u64 key);
- [[maybe_unused]] std::pair<fsblk_t::block_t, u8 *> 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<u8> read_all() override;
- virtual std::vector<u8> rsrc_read_all() override;
-
- private:
- impl &m_fs;
- root_dir *m_parent_dir;
- u16 m_key;
- u8 m_entry[39];
-
- std::vector<u8> any_read_all(u8 type, u16 block, u32 length);
- };
+ std::tuple<fsblk_t::block_t, u32> path_find_step(const std::string &name, u16 block);
+ std::tuple<fsblk_t::block_t, u32, bool> path_find(const std::vector<std::string> &path);
+ std::pair<err_t, std::vector<u8>> 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<filesystem_t> prodos_image::mount(fsblk_t &blockdev) const
{
- return std::make_unique<impl>(blockdev);
+ return std::make_unique<prodos_impl>(blockdev);
}
bool prodos_image::can_format() const
@@ -215,7 +165,7 @@ std::vector<meta_description> 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<err_t, std::vector<dir_entry>> prodos_impl::directory_contents(const std::vector<std::string> &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;
-}
-
-
-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();
-}
+ else {
+ auto [blk, off, dir] = path_find(path);
+ if(!off || !dir)
+ return std::make_pair(ERR_NOT_FOUND, std::vector<dir_entry>());
+ block = blk.r16l(off+0x11);
+ }
-std::vector<dir_entry> impl::root_dir::contents()
-{
std::vector<dir_entry> 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<fsblk_t::block_t, const u8 *> impl::root_dir::get_entry_ro(u64 key)
+std::tuple<fsblk_t::block_t, u32> prodos_impl::path_find_step(const std::string &name, u16 block)
{
- std::pair<fsblk_t::block_t, const u8 *> 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<fsblk_t::block_t, u8 *> impl::root_dir::get_entry(u64 key)
+std::tuple<fsblk_t::block_t, u32, bool> prodos_impl::path_find(const std::vector<std::string> &path)
{
- std::pair<fsblk_t::block_t, u8 *> 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);
- }
- res.second = res.first.data() + 4 + 39 * key;
- return res;
-}
+ if(path.size() == 0)
+ return std::tuple<fsblk_t::block_t, u32, bool>(fsblk_t::block_t(), 0, false);
-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);
-}
+ 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);
-filesystem_t::dir_t impl::root_dir::dir_get(u64 key)
-{
- 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));
-
- return new dir(m_fs, entry, r16l(entry+0x11), key, this);
-}
+ if(pathc + 1 == path.size())
+ return std::make_tuple(blk, off, (blk.r8(off) & 0xf0) == 0xd0);
-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((blk.r8(off) & 0xf0) != 0xd0)
+ return std::make_tuple(blk, 0U, false);
-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;
+ block = blk.r16l(off + 0x11);
+ }
}
-void impl::file::drop_weak_references()
-{
-}
-meta_data impl::file::metadata()
+std::pair<err_t, meta_data> prodos_impl::metadata(const std::vector<std::string> &path)
{
- 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(path.size() == 0)
+ return std::make_pair(ERR_OK, meta_data());
- } else if(type >= 1 && type <= 3)
- res.set(meta_name::length, r24l(m_entry + 0x15));
+ auto [blk, off, dir] = path_find(path);
- else
- throw std::runtime_error(util::string_format("impl::file::metadata: Unhandled file type %d", type));
+ if(!off)
+ return std::make_pair(ERR_NOT_FOUND, meta_data());
- return res;
-}
+ const u8 *entry = blk.rodata() + off;
-meta_data impl::dir::metadata()
-{
meta_data res;
- u8 type = r8(m_entry);
- std::string name = rstr(m_entry+1, type & 0xf);
- res.set(meta_name::name, name);
+ 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());
+ }
- return res;
+ return std::make_pair(ERR_OK, res);
}
-std::vector<u8> impl::file::any_read_all(u8 type, u16 block, u32 length)
+std::pair<err_t, std::vector<u8>> prodos_impl::any_read(u8 type, u16 block, u32 length)
{
- std::vector<u8> data((length + 511) & ~511);
- u32 nb = data.size()/512;
+ std::pair<err_t, std::vector<u8>> 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<u8> 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<u8> impl::file::read_all()
+std::pair<err_t, std::vector<u8>> prodos_impl::file_read(const std::vector<std::string> &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<u8>());
+
+ 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<u8>());
}
-std::vector<u8> impl::file::rsrc_read_all()
+std::pair<err_t, std::vector<u8>> prodos_impl::file_rsrc_read(const std::vector<std::string> &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<u8>());
+
+ 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<u8>());
}
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 <stdexcept>
-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<err_t, meta_data> metadata(const std::vector<std::string> &path) override;
+ virtual err_t metadata_change(const std::vector<std::string> &path, const meta_data &meta) override;
+
+ virtual std::pair<err_t, std::vector<dir_entry>> directory_contents(const std::vector<std::string> &path) override;
+ virtual err_t rename(const std::vector<std::string> &opath, const std::vector<std::string> &npath) override;
+ virtual err_t remove(const std::vector<std::string> &path) override;
+
+ virtual err_t file_create(const std::vector<std::string> &path, const meta_data &meta) override;
+
+ virtual std::pair<err_t, std::vector<u8>> file_read(const std::vector<std::string> &path) override;
+ virtual err_t file_write(const std::vector<std::string> &path, const std::vector<u8> &data) override;
+
+ virtual err_t format(const meta_data &meta) override;
+
+private:
+ meta_data file_metadata(const u8 *entry);
+ std::tuple<fsblk_t::block_t, u32> file_find(std::string name);
+ std::vector<std::pair<u8, u8>> allocate_blocks(u32 count);
+ void free_blocks(const std::vector<std::pair<u8, u8>> &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<filesystem_t> vtech_image::mount(fsblk_t &blockdev) const
{
- return std::make_unique<impl>(blockdev);
+ return std::make_unique<vtech_impl>(blockdev);
}
bool vtech_image::can_format() const
@@ -74,12 +106,6 @@ std::vector<meta_description> vtech_image::volume_meta_description() const
return res;
}
-meta_data vtech_image::impl::metadata()
-{
- meta_data res;
- return res;
-}
-
std::vector<meta_description> vtech_image::file_meta_description() const
{
std::vector<meta_description> res;
@@ -90,45 +116,42 @@ std::vector<meta_description> vtech_image::file_meta_description() const
return res;
}
-
-void vtech_image::impl::format(const meta_data &meta)
+vtech_impl::vtech_impl(fsblk_t &blockdev) : filesystem_t(blockdev, 128)
{
- m_blockdev.fill(0);
}
-vtech_image::impl::impl(fsblk_t &blockdev) : filesystem_t(blockdev, 128), m_root(true)
+err_t vtech_impl::format(const meta_data &meta)
{
+ m_blockdev.fill(0);
+ return ERR_OK;
}
-filesystem_t::dir_t vtech_image::impl::root()
+meta_data vtech_impl::volume_metadata()
{
- if(!m_root)
- m_root = new root_dir(*this);
- return m_root.strong();
+ return meta_data();
}
-void vtech_image::impl::drop_root_ref()
+err_t vtech_impl::volume_metadata_change(const meta_data &meta)
{
- m_root = nullptr;
+ return ERR_OK;
}
-void vtech_image::impl::root_dir::drop_weak_references()
+meta_data vtech_impl::file_metadata(const u8 *entry)
{
- m_fs.drop_root_ref();
-}
+ meta_data res;
-meta_data vtech_image::impl::root_dir::metadata()
-{
- return meta_data();
+ 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::vector<dir_entry> vtech_image::impl::root_dir::contents()
+std::tuple<fsblk_t::block_t, u32> vtech_impl::file_find(std::string name)
{
- std::vector<dir_entry> res;
-
- u64 id = 0;
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,115 +159,177 @@ std::vector<dir_entry> 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 res;
+ return std::make_tuple(fsblk_t::block_t(), 0xffffffff);
}
-filesystem_t::file_t vtech_image::impl::root_dir::file_get(u64 key)
+std::pair<err_t, meta_data> vtech_impl::metadata(const std::vector<std::string> &path)
{
- if(key >= 15*8)
- throw std::out_of_range("Key out of range");
+ if(path.size() != 1)
+ return std::make_pair(ERR_NOT_FOUND, meta_data());
- 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(path[0]);
+ if(off == 0xffffffff)
+ return std::make_pair(ERR_NOT_FOUND, meta_data());
-void vtech_image::impl::root_dir::update_file(u16 key, const u8 *entry)
-{
- auto bdir = m_fs.m_blockdev.get(key >> 3);
- int off = (key & 7) << 4;
- bdir.copy(off, entry, 16);
+ return std::make_pair(ERR_OK, file_metadata(bdir.rodata() + off));
}
-filesystem_t::dir_t vtech_image::impl::root_dir::dir_get(u64 key)
+err_t vtech_impl::metadata_change(const std::vector<std::string> &path, const meta_data &meta)
{
- throw std::logic_error("Directories not supported");
-}
+ if(path.size() != 1)
+ return ERR_NOT_FOUND;
-vtech_image::impl::file::file(impl &fs, root_dir *dir, const u8 *entry, u16 key) : m_fs(fs), m_dir(dir), m_key(key)
-{
- memcpy(m_entry, entry, 16);
-}
+ auto [bdir, off] = file_find(path[0]);
+ if(!off)
+ return ERR_NOT_FOUND;
-void vtech_image::impl::file::drop_weak_references()
-{
+ 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;
}
-meta_data vtech_image::impl::file::metadata()
+std::pair<err_t, std::vector<dir_entry>> vtech_impl::directory_contents(const std::vector<std::string> &path)
{
- meta_data res;
+ std::pair<err_t, std::vector<dir_entry>> res;
- 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));
+ 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;
}
-std::vector<u8> vtech_image::impl::file::read_all()
+err_t vtech_impl::rename(const std::vector<std::string> &opath, const std::vector<std::string> &npath)
{
- u8 track = m_entry[0xa];
- u8 sector = m_entry[0xb];
- int len = ((r16l(m_entry + 0xe) - r16l(m_entry + 0xc)) & 0xffff) + 1;
+ if(opath.size() != 1 || npath.size() != 1)
+ return ERR_NOT_FOUND;
- std::vector<u8> data(len, 0);
- int pos = 0;
- while(pos < len) {
- if(track >= 40 || sector >= 16)
- break;
- auto dblk = m_fs.m_blockdev.get(track*16 + sector);
- int size = len - pos;
- if(size > 126)
- size = 126;
- memcpy(data.data() + pos, dblk.data(), size);
- pos += size;
- track = dblk.r8(126);
- sector = dblk.r8(127);
- }
- return data;
+ 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;
}
-vtech_image::impl::file_t vtech_image::impl::root_dir::file_create(const meta_data &info)
+err_t vtech_impl::remove(const std::vector<std::string> &path)
{
+ return ERR_NOT_FOUND;
+}
+
+
+err_t vtech_impl::file_create(const std::vector<std::string> &path, const meta_data &meta)
+{
+ 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_fs.m_blockdev.get(sect);
- u64 id = 0;
+ 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 = info.get_string(meta_name::name, "");
+ std::string fname = meta.get_string(meta_name::name, "");
fname.resize(8, ' ');
- bdir.w8 (off+0x0, info.get_flag(meta_name::basic, true) ? 'T' : 'B');
+ 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, info.get_number(meta_name::loading_address, 0x7ae9));
+ 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 file_t(new file(m_fs, this, bdir.rodata() + off, id));
+ return ERR_OK;
}
- id++;
}
}
- return nullptr;
+ return ERR_NO_SPACE;
}
-void vtech_image::impl::root_dir::file_delete(u64 key)
+std::pair<err_t, std::vector<u8>> vtech_impl::file_read(const std::vector<std::string> &path)
{
+ std::vector<u8> data;
+
+ if(path.size() != 1)
+ return std::make_pair(ERR_NOT_FOUND, data);
+
+ auto [bdir, off] = file_find(path[0]);
+ if(off == 0xffffffff)
+ return std::make_pair(ERR_NOT_FOUND, data);
+
+ const u8 *entry = bdir.rodata() + off;
+
+ u8 track = entry[0xa];
+ u8 sector = entry[0xb];
+ int len = ((r16l(entry + 0xe) - r16l(entry + 0xc)) & 0xffff) + 1;
+
+ data.resize(len, 0);
+ int pos = 0;
+ while(pos < len) {
+ if(track >= 40 || sector >= 16)
+ break;
+ auto dblk = m_blockdev.get(track*16 + sector);
+ int size = len - pos;
+ if(size > 126)
+ size = 126;
+ memcpy(data.data() + pos, dblk.data(), size);
+ pos += size;
+ track = dblk.r8(126);
+ sector = dblk.r8(127);
+ }
+ return std::make_pair(ERR_OK, data);
}
-void vtech_image::impl::file::replace(const std::vector<u8> &data)
+err_t vtech_impl::file_write(const std::vector<std::string> &path, const std::vector<u8> &data)
{
- u32 cur_len = ((r16l(m_entry + 0xe) - r16l(m_entry + 0xc) + 1) & 0xffff);
+ if(path.size() != 1)
+ return ERR_NOT_FOUND;
+
+ auto [bdir, off] = file_find(path[0]);
+ if(off == 0xffffffff)
+ return ERR_NOT_FOUND;
+
+ u8 *entry = bdir.data() + off;
+
+ 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<u8> &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<std::pair<u8, u8>> 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<std::pair<u8, u8>> blocks = m_fs.allocate_blocks(need_ns);
+ std::vector<std::pair<u8, u8>> 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<u8> &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<std::pair<u8, u8>> vtech_image::impl::allocate_blocks(u32 count)
+std::vector<std::pair<u8, u8>> vtech_impl::allocate_blocks(u32 count)
{
std::vector<std::pair<u8, u8>> blocks;
if(free_block_count() < count)
@@ -341,7 +400,7 @@ std::vector<std::pair<u8, u8>> vtech_image::impl::allocate_blocks(u32 count)
abort();
}
-void vtech_image::impl::free_blocks(const std::vector<std::pair<u8, u8>> &blocks)
+void vtech_impl::free_blocks(const std::vector<std::pair<u8, u8>> &blocks)
{
auto fmap = m_blockdev.get(15);
for(auto ref : blocks) {
@@ -353,7 +412,7 @@ void vtech_image::impl::free_blocks(const std::vector<std::pair<u8, u8>> &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<dir_entry> 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<fsblk_t::block_t, u32> 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<u8> read_all() override;
- virtual void replace(const std::vector<u8> &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<std::pair<u8, u8>> allocate_blocks(u32 count);
- void free_blocks(const std::vector<std::pair<u8, u8>> &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()
+{
+ return meta_data();
+}
+
+err_t filesystem_t::volume_metadata_change(const meta_data &meta)
+{
+ return ERR_UNSUPPORTED;
+}
+
+std::pair<err_t, meta_data> filesystem_t::metadata(const std::vector<std::string> &path)
+{
+ return std::make_pair(ERR_UNSUPPORTED, meta_data());
+}
+
+err_t filesystem_t::metadata_change(const std::vector<std::string> &path, const meta_data &meta)
+{
+ return ERR_UNSUPPORTED;
+}
+
+std::pair<err_t, std::vector<dir_entry>> filesystem_t::directory_contents(const std::vector<std::string> &path)
{
- throw std::logic_error("file_create called on a filesystem not supporting write");
+ return std::make_pair(ERR_UNSUPPORTED, std::vector<dir_entry>());
}
-void filesystem_t::idir_t::file_delete(uint64_t key)
+err_t filesystem_t::rename(const std::vector<std::string> &opath, const std::vector<std::string> &npath)
{
- throw std::logic_error("file_delete called on a filesystem not supporting write");
+ return ERR_UNSUPPORTED;
}
+err_t filesystem_t::remove(const std::vector<std::string> &path)
+{
+ return ERR_UNSUPPORTED;
+}
-void filesystem_t::ifile_t::replace(const std::vector<u8> &data)
+err_t filesystem_t::dir_create(const std::vector<std::string> &path, const meta_data &meta)
{
- throw std::logic_error("replace called on a filesystem not supporting write");
+ return ERR_UNSUPPORTED;
}
-void filesystem_t::ifile_t::rsrc_replace(const std::vector<u8> &data)
+err_t filesystem_t::file_create(const std::vector<std::string> &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<err_t, std::vector<u8>> filesystem_t::file_read(const std::vector<std::string> &path)
{
- throw std::logic_error("metadata_change called on a filesystem not supporting write");
+ return std::make_pair(ERR_UNSUPPORTED, std::vector<u8>());
}
-void filesystem_t::idir_t::metadata_change(const meta_data &info)
+err_t filesystem_t::file_write(const std::vector<std::string> &path, const std::vector<u8> &data)
{
- 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)
+std::pair<err_t, std::vector<u8>> filesystem_t::file_rsrc_read(const std::vector<std::string> &path)
{
- throw std::logic_error("metadata_change called on a filesystem not supporting write");
+ return std::make_pair(ERR_UNSUPPORTED, std::vector<u8>());
}
-std::vector<u8> filesystem_t::ifile_t::rsrc_read_all()
+err_t filesystem_t::file_rsrc_write(const std::vector<std::string> &path, const std::vector<u8> &data)
{
- throw std::logic_error("rsrc_read_all called on a filesystem without resource forks");
+ 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<typename T> 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;
+ virtual ~filesystem_t() = default;
-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<dir_entry> 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);
- };
+ // Get the metadata for the volume
+ virtual meta_data volume_metadata();
- 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<u8> read_all() = 0;
- virtual void replace(const std::vector<u8> &data);
- virtual std::vector<u8> rsrc_read_all();
- virtual void rsrc_replace(const std::vector<u8> &data);
- };
+ // Change the metadata for the volume
+ virtual err_t volume_metadata_change(const meta_data &meta);
-public:
- class dir_t : public refcounted_outer<idir_t> {
- public:
- dir_t(bool weak = false) : refcounted_outer<idir_t>(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<dir_entry> 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); }
- };
+ // Get the metadata for a file or a directory. Empty path targets the root directory
+ virtual std::pair<err_t, meta_data> metadata(const std::vector<std::string> &path);
- class file_t : public refcounted_outer<ifile_t> {
- public:
- file_t(bool weak = false) : refcounted_outer<ifile_t>(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<u8> read_all() { return m_object->read_all(); }
- void replace(const std::vector<u8> &data) { m_object->replace(data); }
- std::vector<u8> rsrc_read_all() { return m_object->rsrc_read_all(); }
- void rsrc_replace(const std::vector<u8> &data) { m_object->rsrc_replace(data); }
- };
+ // Change the metadata for a file or a directory. Empty path targets the root directory
+ virtual err_t metadata_change(const std::vector<std::string> &path, const meta_data &meta);
- filesystem_t(fsblk_t &blockdev, u32 size) : m_blockdev(blockdev) {
- m_blockdev.set_block_size(size);
- }
+ // Get the contents of a directory, empty path targets the root directory
+ virtual std::pair<err_t, std::vector<dir_entry>> directory_contents(const std::vector<std::string> &path);
- virtual ~filesystem_t() = default;
+ // 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<std::string> &opath, const std::vector<std::string> &npath);
+
+ // Remove a file or a directory. Directories must be empty (e.g. it's not recursive)
+ virtual err_t remove(const std::vector<std::string> &path);
+
+ // Create a directory, path designates where the directory must be, directory name is in meta
+ virtual err_t dir_create(const std::vector<std::string> &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<std::string> &path, const meta_data &meta);
+
+ // Read the contents of a file
+ virtual std::pair<err_t, std::vector<u8>> file_read(const std::vector<std::string> &path);
+
+ // Replace the contents of a file, the file must already exist
+ virtual err_t file_write(const std::vector<std::string> &path, const std::vector<u8> &data);
+
+ // Read the resource fork of a file on systems that handle those
+ virtual std::pair<err_t, std::vector<u8>> file_rsrc_read(const std::vector<std::string> &path);
- virtual dir_t root();
- virtual void format(const meta_data &meta);
- virtual meta_data metadata();
- virtual void metadata_change(const meta_data &info);
+ // Replace the resource fork of a file, the file must already exist
+ virtual err_t file_rsrc_write(const std::vector<std::string> &path, const std::vector<u8> &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<std::vector<std::string>> &entries, const std::unordered_map<fs::meta_name, size_t> &nmap, size_t nc, const std::vector<fs::meta_description> &dmetad, const std::vector<fs::meta_description> &fmetad)
+static void dir_scan(fs::filesystem_t *fs, u32 depth, const std::vector<std::string> &path, std::vector<std::vector<std::string>> &entries, const std::unordered_map<fs::meta_name, size_t> &nmap, size_t nc, const std::vector<fs::meta_description> &dmetad, const std::vector<fs::meta_description> &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<std::vector<std::string>> 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<std::string>(), entries, nmap, names.size(), dmetad, fmetad);
std::vector<u32> 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<std::string> 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<std::string> path = ih.path_split(dstpath);
-
- 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());
+ auto [err, meta] = fs->metadata(path);
+
+ 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;
}
- 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;
+ }
}
}