From 4fd36891830912d635a3a4c79bb8ee641091df52 Mon Sep 17 00:00:00 2001 From: Olivier Galibert Date: Tue, 4 May 2021 21:29:21 +0200 Subject: prodos: experiements in directory tree reading. --- src/lib/formats/fs_prodos.cpp | 158 +++++++++++++++++++++++++++++++++++++++--- src/lib/formats/fs_prodos.h | 30 +++++++- src/lib/formats/fsmgr.cpp | 24 ++++--- src/lib/formats/fsmgr.h | 56 +++++++++------ 4 files changed, 223 insertions(+), 45 deletions(-) diff --git a/src/lib/formats/fs_prodos.cpp b/src/lib/formats/fs_prodos.cpp index f6923e587c8..5a4d3e6edf5 100644 --- a/src/lib/formats/fs_prodos.cpp +++ b/src/lib/formats/fs_prodos.cpp @@ -91,12 +91,28 @@ std::vector fs_prodos::volume_meta_description() const std::vector fs_prodos::file_meta_description() const { std::vector res; + res.emplace_back(fs_meta_description(fs_meta_name::name, fs_meta_type::string, "Empty file", false, [](const fs_meta &m) { return m.as_string().size() <= 15; }, "File name, up to 15 characters")); + res.emplace_back(fs_meta_description(fs_meta_name::length, fs_meta_type::number, 0, true, nullptr, "Size of the file in bytes")); + res.emplace_back(fs_meta_description(fs_meta_name::rsrc_length, fs_meta_type::number, 0, true, nullptr, "Size of the resource fork in bytes")); + res.emplace_back(fs_meta_description(fs_meta_name::os_version, fs_meta_type::number, 5, false, [](const fs_meta &m) { return m.as_number() <= 255; }, "Creator OS version")); + res.emplace_back(fs_meta_description(fs_meta_name::os_minimum_version, fs_meta_type::number, 5, false, [](const fs_meta &m) { return m.as_number() <= 255; }, "Minimum OS version")); + + auto now = util::arbitrary_datetime::now(); + res.emplace_back(fs_meta_description(fs_meta_name::creation_date, fs_meta_type::date, now, false, nullptr, "Creation time")); + res.emplace_back(fs_meta_description(fs_meta_name::modification_date, fs_meta_type::date, now, false, nullptr, "Modification time")); return res; } std::vector fs_prodos::directory_meta_description() const { std::vector res; + res.emplace_back(fs_meta_description(fs_meta_name::name, fs_meta_type::string, "Empty directory", false, [](const fs_meta &m) { return m.as_string().size() <= 15; }, "Directory name, up to 15 characters")); + res.emplace_back(fs_meta_description(fs_meta_name::os_version, fs_meta_type::number, 5, false, [](const fs_meta &m) { return m.as_number() <= 255; }, "Creator OS version")); + res.emplace_back(fs_meta_description(fs_meta_name::os_minimum_version, fs_meta_type::number, 5, false, [](const fs_meta &m) { return m.as_number() <= 255; }, "Minimum OS version")); + + auto now = util::arbitrary_datetime::now(); + res.emplace_back(fs_meta_description(fs_meta_name::creation_date, fs_meta_type::date, now, false, nullptr, "Creation time")); + res.emplace_back(fs_meta_description(fs_meta_name::modification_date, fs_meta_type::date, now, false, nullptr, "Modification time")); return res; } @@ -201,7 +217,7 @@ fs_meta_data fs_prodos::impl::metadata() filesystem_t::dir_t fs_prodos::impl::root() { if(!m_root) - m_root = new root_dir(*this); + m_root = new dir(*this, 2); return m_root.strong(); } @@ -211,30 +227,152 @@ void fs_prodos::impl::drop_root_ref() } -void fs_prodos::impl::root_dir::drop_weak_references() +void fs_prodos::impl::dir::drop_weak_references() { - m_fs.drop_root_ref(); + if(m_base_block == 2) + m_fs.drop_root_ref(); } -fs_meta_data fs_prodos::impl::root_dir::metadata() +fs_meta_data fs_prodos::impl::dir::metadata() { - return fs_meta_data(); + fs_meta_data res; + if(m_base_block == 2) + return res; + + auto bdir = m_fs.m_blockdev.get(m_base_block); + int len = bdir.r8(0x04) & 0xf; + res[fs_meta_name::name] = bdir.rstr(0x05, len); + res[fs_meta_name::os_version] = uint64_t(bdir.r8(0x20)); + res[fs_meta_name::os_minimum_version] = uint64_t(bdir.r8(0x21)); + res[fs_meta_name::creation_date] = prodos_to_dt(bdir.r32l(0x1c)); + res[fs_meta_name::modification_date] = prodos_to_dt(bdir.r32l(0x16)); + return res; } -std::vector fs_prodos::impl::root_dir::contents() +std::vector fs_prodos::impl::dir::contents() { std::vector res; + + u16 block = m_base_block; + u32 off = 39 + 4; + u32 id = 1; + do { + auto blk = m_fs.m_blockdev.get(block); + while(off < 511) { + u8 type = blk.r8(off); + auto name = blk.rstr(off+1, type & 0xf); + type >>= 4; + if(type == 0xd) + res.emplace_back(fs_dir_entry(name, fs_dir_entry_type::dir, id)); + else if(type != 0) + res.emplace_back(fs_dir_entry(name, fs_dir_entry_type::file, id)); + off += 39; + id ++; + } + block = blk.r16l(2); + if(block >= m_fs.m_blockdev.block_count()) + break; + off = 4; + } while(block); + return res; +} + +std::pair fs_prodos::impl::dir::get_entry_ro(uint64_t key) +{ + std::pair res; + res.first = m_fs.m_blockdev.get(m_base_block); + while(key >= 13) { + key -= 13; + u16 block = res.first.r16l(2); + if(!block || block >= m_fs.m_blockdev.block_count()) { + res.first = nullptr; + res.second = nullptr; + return res; + } + res.first = m_fs.m_blockdev.get(block); + } + res.second = res.first.rodata() + 4 + 39 * key; + return res; +} + +std::pair fs_prodos::impl::dir::get_entry(uint64_t key) +{ + std::pair res; + res.first = m_fs.m_blockdev.get(m_base_block); + while(key > 13) { + key -= 13; + u16 block = res.first.r16l(2); + if(!block || block >= m_fs.m_blockdev.block_count()) { + res.first = nullptr; + res.second = nullptr; + return res; + } + res.first = m_fs.m_blockdev.get(block); + } + res.second = res.first.data() + 4 + 39 * key; + return res; +} + +filesystem_t::file_t fs_prodos::impl::dir::file_get(uint64_t key) +{ + auto [blk, entry] = get_entry_ro(key); + if(!blk) + fatalerror("Out-of-range key on file_get\n"); + u8 type = entry[0] >> 4; + if(type == 0 || type == 4 || type > 5) + fatalerror("Unhandled file type %x\n", type); + return new file(m_fs, entry, key); +} + +filesystem_t::dir_t fs_prodos::impl::dir::dir_get(uint64_t key) +{ + auto [blk, entry] = get_entry_ro(key); + if(!blk) + fatalerror("Out-of-range key on dir_get\n"); + u8 type = entry[0] >> 4; + if(type != 0xd) + fatalerror("Unhandled directory type %x\n", type); + + return new dir(m_fs, entry[17] | (entry[18] << 8), key); +} + +fs_prodos::impl::file::file(impl &fs, const u8 *entry, u16 key) : m_fs(fs), m_key(key) +{ + memcpy(m_entry, entry, 39); +} + +void fs_prodos::impl::file::drop_weak_references() +{ +} + +fs_meta_data fs_prodos::impl::file::metadata() +{ + fs_meta_data res; + std::string name; + u8 type = m_entry[0]; + for(u8 i = 0; i != (type & 0xf); i++) + name += char(m_entry[i+1]); + type >>= 4; + res[fs_meta_name::name] = name; + if(type == 5) { + auto rootblk = m_fs.m_blockdev.get(m_entry[0x11] | (m_entry[0x12] << 8)); + res[fs_meta_name::length] = rootblk.r24l(0x005); + res[fs_meta_name::rsrc_length] = rootblk.r24l(0x105); + + } else if((type >= 1 && type <= 3) || 1) + res[fs_meta_name::length] = m_entry[0x15] | (m_entry[0x16] << 8) | (m_entry[0x17] << 16); + return res; } -filesystem_t::file_t fs_prodos::impl::root_dir::file_get(uint64_t key) +std::vector fs_prodos::impl::file::read_all() { - return nullptr; + abort(); } -filesystem_t::dir_t fs_prodos::impl::root_dir::dir_get(uint64_t key) +std::vector fs_prodos::impl::file::read(u64 start, u64 length) { - return nullptr; + abort(); } const filesystem_manager_type FS_PRODOS = &filesystem_manager_creator;; diff --git a/src/lib/formats/fs_prodos.h b/src/lib/formats/fs_prodos.h index d4b61f7d27e..2f33254842b 100644 --- a/src/lib/formats/fs_prodos.h +++ b/src/lib/formats/fs_prodos.h @@ -14,10 +14,10 @@ class fs_prodos : public filesystem_manager_t { public: class impl : public filesystem_t { public: - class root_dir : public idir_t { + class dir : public idir_t { public: - root_dir(impl &i) : m_fs(i) {} - virtual ~root_dir() = default; + dir(impl &fs, u16 base_block, u16 key = 0) : m_fs(fs), m_base_block(base_block), m_key(key) { (void)m_key; } + virtual ~dir() = default; virtual void drop_weak_references() override; @@ -28,6 +28,30 @@ public: private: impl &m_fs; + u16 m_base_block; + u16 m_key; + + std::pair get_entry_ro(uint64_t key); + std::pair get_entry(uint64_t key); + }; + + class file : public ifile_t { + public: + file(impl &fs, const u8 *entry, u16 key); + virtual ~file() = default; + + virtual void drop_weak_references() override; + + virtual fs_meta_data metadata() override; + virtual std::vector read_all() override; + virtual std::vector read(u64 start, u64 length) override; + + private: + impl &m_fs; + u16 m_key; + u8 m_entry[39]; + + std::vector build_block_table(); }; impl(fsblk_t &blockdev); diff --git a/src/lib/formats/fsmgr.cpp b/src/lib/formats/fsmgr.cpp index 2f49c3cbed9..2b15b1aeb93 100644 --- a/src/lib/formats/fsmgr.cpp +++ b/src/lib/formats/fsmgr.cpp @@ -18,24 +18,27 @@ void fs_refcounted_inner::ref_weak() m_weak_ref ++; } -void fs_refcounted_inner::unref() +bool fs_refcounted_inner::unref() { m_ref --; if(m_ref == 0) { - if(m_weak_ref) { + if(m_weak_ref) drop_weak_references(); - if(m_weak_ref) - fatalerror("drop_weak_references kept %d active references\n", m_weak_ref); - } else + else delete this; + return true; } + return false; } -void fs_refcounted_inner::unref_weak() +bool fs_refcounted_inner::unref_weak() { m_weak_ref --; - if(m_weak_ref == 0 && m_ref == 0) + if(m_weak_ref == 0 && m_ref == 0) { delete this; + return true; + } + return false; } @@ -266,12 +269,13 @@ const char *fs_meta_get_name(fs_meta_name name) case fs_meta_name::length: return "length"; case fs_meta_name::loading_address: return "loading_address"; case fs_meta_name::locked: return "locked"; - case fs_meta_name::sequential: return "sequential"; case fs_meta_name::modification_date: return "modification_date"; case fs_meta_name::name: return "name"; - case fs_meta_name::size_in_blocks: return "size_in_blocks"; - case fs_meta_name::os_version: return "os_version"; case fs_meta_name::os_minimum_version: return "os_minimum_version"; + case fs_meta_name::os_version: return "os_version"; + case fs_meta_name::rsrc_length: return "rsrc_length"; + case fs_meta_name::sequential: return "sequential"; + case fs_meta_name::size_in_blocks: return "size_in_blocks"; } return ""; } diff --git a/src/lib/formats/fsmgr.h b/src/lib/formats/fsmgr.h index f0c4ba13539..6ca909e3bcd 100644 --- a/src/lib/formats/fsmgr.h +++ b/src/lib/formats/fsmgr.h @@ -23,6 +23,7 @@ enum class fs_meta_name { name, os_minimum_version, os_version, + rsrc_length, sequential, size_in_blocks, }; @@ -45,8 +46,10 @@ public: fs_meta() { value = false; } fs_meta(std::string str) { value = str; } fs_meta(bool b) { value = b; } - fs_meta(uint64_t num) { value = num; } + fs_meta(int32_t num) { value = uint64_t(num); } + fs_meta(uint32_t num) { value = uint64_t(num); } fs_meta(int64_t num) { value = uint64_t(num); } + fs_meta(uint64_t num) { value = num; } fs_meta(util::arbitrary_datetime dt) { value = dt; } util::arbitrary_datetime as_date() const { return *std::get_if(&value); } @@ -66,8 +69,8 @@ fs_meta fs_meta_from_string(fs_meta_type type, std::string value); template class fs_refcounted_outer { public: - fs_refcounted_outer(bool weak = false) : m_object(nullptr), m_is_weak_ref(weak) {} - fs_refcounted_outer(T *object, bool weak = false) : m_object(object), m_is_weak_ref(weak) { + fs_refcounted_outer(bool weak) : m_object(nullptr), m_is_weak_ref(weak) {} + fs_refcounted_outer(T *object, bool weak) : m_object(object), m_is_weak_ref(weak) { ref(); } @@ -88,23 +91,33 @@ public: } fs_refcounted_outer &operator =(T *dir) { - unref(); - m_object = dir; - ref(); + if(m_object != dir) { + unref(); + m_object = dir; + ref(); + } return *this; } fs_refcounted_outer &operator =(const fs_refcounted_outer &cref) { - unref(); - m_object = cref.m_object; - m_is_weak_ref = cref.m_is_weak_ref; - ref(); + if(m_object != cref.m_object) { + unref(); + m_object = cref.m_object; + ref(); + } return *this; } fs_refcounted_outer &operator =(fs_refcounted_outer &&cref) { - m_object = cref.m_object; - m_is_weak_ref = cref.m_is_weak_ref; + if(m_object != cref.m_object) { + unref(); + m_object = cref.m_object; + ref(); + } else if(m_is_weak_ref != cref.m_is_weak_ref) { + ref(); + cref.unref(); + m_object = cref.m_object; // In case the object got deleted (when going from strong ref to weak on the last strong) + } cref.m_object = nullptr; return *this; } @@ -127,10 +140,9 @@ private: void unref() { if(m_object) { - if(m_is_weak_ref) - m_object->unref_weak(); - else - m_object->unref(); + bool del = m_is_weak_ref ? m_object->unref_weak() : m_object->unref(); + if(del) + m_object = nullptr; } } }; @@ -160,12 +172,12 @@ public: void ref(); void ref_weak(); - void unref(); - void unref_weak(); + bool unref(); + bool unref_weak(); virtual void drop_weak_references() = 0; -private: +public: uint32_t m_ref, m_weak_ref; }; @@ -200,7 +212,7 @@ public: class block_t : public fs_refcounted_outer { public: block_t(bool weak = false) : fs_refcounted_outer(weak) {} - block_t(iblock_t *block, bool weak = false) : fs_refcounted_outer(block, weak) {} + block_t(iblock_t *block, bool weak = true) : fs_refcounted_outer(block, weak) {} virtual ~block_t() = default; block_t strong() { return block_t(m_object, false); } @@ -275,7 +287,7 @@ public: class dir_t : public fs_refcounted_outer { public: dir_t(bool weak = false) : fs_refcounted_outer(weak) {} - dir_t(idir_t *dir, bool weak = false) : fs_refcounted_outer(dir, weak) {} + dir_t(idir_t *dir, bool weak = true) : fs_refcounted_outer(dir, weak) {} virtual ~dir_t() = default; dir_t strong() { return dir_t(m_object, false); } @@ -290,7 +302,7 @@ public: class file_t : public fs_refcounted_outer { public: file_t(bool weak = false) : fs_refcounted_outer(weak) {} - file_t(ifile_t *file, bool weak = false) : fs_refcounted_outer(file, weak) {} + file_t(ifile_t *file, bool weak = true) : fs_refcounted_outer(file, weak) {} virtual ~file_t() = default; file_t strong() { return file_t(m_object, false); } -- cgit v1.2.3