From e72e97b17457124eaffff07fe462795ac526480a Mon Sep 17 00:00:00 2001 From: Olivier Galibert Date: Thu, 29 Apr 2021 14:45:55 +0200 Subject: [PATCH] fs: Add full jasmin read support --- src/lib/formats/fs_oric_jasmin.cpp | 309 ++++++++++++++++++++++-- src/lib/formats/fs_oric_jasmin.h | 69 +++++- src/lib/formats/fs_prodos.cpp | 4 +- src/lib/formats/fs_prodos.h | 2 +- src/lib/formats/fs_unformatted.cpp | 5 - src/lib/formats/fs_unformatted.h | 1 - src/lib/formats/fsmgr.cpp | 94 ++++++-- src/lib/formats/fsmgr.h | 361 +++++++++++++---------------- src/tools/floptool.cpp | 208 ++++++++++++++++- 9 files changed, 805 insertions(+), 248 deletions(-) diff --git a/src/lib/formats/fs_oric_jasmin.cpp b/src/lib/formats/fs_oric_jasmin.cpp index 59ff60fd4b7..8cb6444aa9c 100644 --- a/src/lib/formats/fs_oric_jasmin.cpp +++ b/src/lib/formats/fs_oric_jasmin.cpp @@ -1,7 +1,7 @@ // license:BSD-3-Clause // copyright-holders:Olivier Galibert -// Creation of Apple Oric_Jasmin floppy images +// Management of Oric Jasmin floppy images #include "emu.h" #include "fs_oric_jasmin.h" @@ -29,7 +29,7 @@ // offset 04+ : 14 file entries, 18 bytes each // offset 00-01: reference to the first sector of the inode, (ff, xx) when no entry // offset 02 : U/L for (U)nlocked or (L)ocked file -// offset 03-0e: filename.ext, space padiing between filename and '.' +// offset 03-0e: filename.ext, space padding between filename and '.' // offset 0f : S/D for (S)equential (normal) or (D)irect-access files // offset 10-11: number of sectors used by the file, including inode, little-endian // @@ -67,11 +67,6 @@ bool fs_oric_jasmin::can_write() const return false; } -bool fs_oric_jasmin::has_subdirectories() const -{ - return false; -} - std::vector fs_oric_jasmin::volume_meta_description() const { std::vector res; @@ -125,22 +120,15 @@ void fs_oric_jasmin::impl::format(const fs_meta_data &meta) auto fmap = m_blockdev.get(bblk); u32 off = 0; for(u32 blk = 0; blk != blocks; blk += 17) { - if(blk == bblk) { - fmap.w8(off , 0xff); - fmap.w8(off + 1, 0x7f); - fmap.w8(off + 2, 0x00); - } else { - fmap.w8(off , 0xff); - fmap.w8(off + 1, 0xff); - fmap.w8(off + 2, 0x01); - } + if(blk == bblk) + fmap.w24l(off, 0x07fff); + else + fmap.w24l(off, 0x1ffff); off += 3; } for(u32 blk = blocks; blk != 17*42*2; blk += 17) { - fmap.w8(off , 0x00); - fmap.w8(off + 1, 0x00); - fmap.w8(off + 2, 0x80); + fmap.w24l(off, 0x800000); off += 3; } @@ -155,4 +143,287 @@ void fs_oric_jasmin::impl::format(const fs_meta_data &meta) bdir.w16l(2, 0x0000); } +fs_oric_jasmin::impl::impl(fsblk_t &blockdev) : filesystem_t(blockdev, 256), m_root(true) +{ +} + +bool fs_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 fs_oric_jasmin::impl::cs_to_block(u16 ref) +{ + u8 track = ref >> 8; + u8 sector = ref & 0xff; + return track * 17 + sector - 1; +} + +u16 fs_oric_jasmin::impl::block_to_cs(u32 block) +{ + u8 track = block / 17; + u8 sector = (block % 17) + 1; + return (track << 8) | sector; +} + +std::string fs_oric_jasmin::impl::read_file_name(const u8 *p) +{ + int main_len; + for(main_len = 8; main_len > 0; main_len--) + if(p[main_len - 1] != ' ') + break; + int ext_len; + for(ext_len = 4; ext_len > 0; ext_len--) + if(p[ext_len + 8 - 1] != ' ') + break; + std::string name; + for(int i=0; i != main_len; i++) + name += char(p[i]); + for(int i=0; i != ext_len; i++) + name += char(p[8 + i]); + return name; +} + + +filesystem_t::dir_t fs_oric_jasmin::impl::root() +{ + if(!m_root) + m_root = new root_dir(*this); + return m_root.strong(); +} + +void fs_oric_jasmin::impl::drop_root_ref() +{ + m_root = nullptr; +} + +void fs_oric_jasmin::impl::root_dir::drop_weak_references() +{ + m_fs.drop_root_ref(); +} + +fs_meta_data fs_oric_jasmin::impl::root_dir::metadata() +{ + return fs_meta_data(); +} + +std::vector fs_oric_jasmin::impl::root_dir::contents() +{ + std::vector res; + + auto bdir = m_fs.m_blockdev.get(20*17+1); + uint64_t id = 0; + for(;;) { + for(u32 i = 0; i != 14; i ++) { + u32 off = 4 + i*18; + u16 ref = bdir.r16b(off); + std::string fname = read_file_name(bdir.rodata()+off+3); + bool system = ref == 0 && id == 0 && bdir.r32b(off+0xb) == 0x2e535953; + if(system) + res.emplace_back(fs_dir_entry(fname, fs_dir_entry_type::system_file, 0)); + + else if(m_fs.ref_valid(ref)) + res.emplace_back(fs_dir_entry(fname, fs_dir_entry_type::file, id)); + + id++; + } + u16 ref = bdir.r16b(2); + if(!ref || !m_fs.ref_valid(ref)) + break; + bdir = m_fs.m_blockdev.get(cs_to_block(ref)); + } + return res; +} + +std::pair fs_oric_jasmin::impl::root_dir::get_dir_block(uint64_t 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)) + fatalerror("Incorrect file key\n"); + bdir = m_fs.m_blockdev.get(cs_to_block(ref)); + key -= 14; + } + return std::pair(bdir, 4 + key * 18); +} + +filesystem_t::file_t fs_oric_jasmin::impl::root_dir::file_get(uint64_t key) +{ + uint64_t 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, bdir.rodata() + off)); + + if(!m_fs.ref_valid(ref)) + fatalerror("Key to deleted/non-existent file\n"); + return file_t(new file(m_fs, bdir.rodata() + off, key)); +} + +filesystem_t::dir_t fs_oric_jasmin::impl::root_dir::dir_get(uint64_t key) +{ + fatalerror("Directories not supported\n"); +} + +fs_oric_jasmin::impl::file::file(impl &fs, const u8 *entry, u16 key) : m_fs(fs), m_key(key) +{ + memcpy(m_entry, entry, 18); + (void)m_key; +} + +void fs_oric_jasmin::impl::file::drop_weak_references() +{ +} + +fs_meta_data fs_oric_jasmin::impl::file::metadata() +{ + fs_meta_data res; + + res[fs_meta_name::name] = read_file_name(m_entry + 3); + res[fs_meta_name::locked] = m_entry[2] == 'L'; + res[fs_meta_name::sequential] = m_entry[0xf] == 'S'; + res[fs_meta_name::size_in_blocks] = uint64_t(m_entry[0x10] | (m_entry[0x11] << 8)); + + u16 ref = (m_entry[0] << 8) | m_entry[1]; + auto dblk = m_fs.m_blockdev.get(cs_to_block(ref)); + res[fs_meta_name::loading_address] = uint64_t(dblk.r16l(2)); + res[fs_meta_name::length] = uint64_t(dblk.r16l(4)); + + return res; +} + +std::vector fs_oric_jasmin::impl::file::read_all() +{ + auto [sect, length] = build_data_sector_table(); + std::vector data(length); + u32 pos = 0; + for(u16 ref : sect) { + u32 npos = pos + 256; + if(npos > length) + npos = length; + if(npos > pos) { + auto dblk = m_fs.m_blockdev.get(cs_to_block(ref)); + memcpy(data.data() + pos, dblk.rodata(), npos - pos); + } else + break; + pos = npos; + } + return data; +} + +std::vector fs_oric_jasmin::impl::file::read(u64 start, u64 length) +{ + auto [sect, rlength] = build_data_sector_table(); + length += start; + if(length > rlength) + length = rlength; + + if(rlength < start) + return std::vector(); + + std::vector data(length - start); + u32 pos = 0; + for(u16 ref : sect) { + u32 npos = pos + 256; + if(npos > length) + npos = length; + if(npos > pos) { + if(npos > start) { + u32 off = pos < start ? start & 0xff : 0; + auto dblk = m_fs.m_blockdev.get(cs_to_block(ref)); + memcpy(data.data() + pos + off - start, dblk.rodata() + off, npos - pos - off); + } + } else + break; + pos = npos; + } + return data; +} + +std::pair, u32> fs_oric_jasmin::impl::file::build_data_sector_table() +{ + std::pair, u32> res; + u16 ref = (m_entry[0] << 8) | m_entry[1]; + 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; pos += 2) { + u16 dref = iblk.r16b(pos); + if(!m_fs.ref_valid(dref)) + goto done; + res.first.push_back(dref); + } + ref = iblk.r16b(2); + if(!m_fs.ref_valid(ref)) + break; + iblk = m_fs.m_blockdev.get(cs_to_block(ref)); + } + done: + if(length > 256 * res.first.size()) + length = 256 * res.first.size(); + res.second = length; + return res; +} + +fs_oric_jasmin::impl::system_file::system_file(impl &fs, const u8 *entry) : m_fs(fs) +{ + memcpy(m_entry, entry, 18); +} + +void fs_oric_jasmin::impl::system_file::drop_weak_references() +{ +} + +fs_meta_data fs_oric_jasmin::impl::system_file::metadata() +{ + fs_meta_data res; + + res[fs_meta_name::name] = read_file_name(m_entry + 3); + res[fs_meta_name::locked] = m_entry[2] == 'L'; + res[fs_meta_name::sequential] = m_entry[0xf] == 'S'; + res[fs_meta_name::size_in_blocks] = uint64_t(m_entry[0x10] | (m_entry[0x11] << 8)); + res[fs_meta_name::length] = uint64_t(0x3e00); + + return res; +} + +std::vector fs_oric_jasmin::impl::system_file::read_all() +{ + std::vector data(0x3e00); + for(u32 i = 0; i != 62; i++) { + auto dblk = m_fs.m_blockdev.get(i); + memcpy(data.data() + 256 * i, dblk.rodata(), 256); + } + return data; +} + +std::vector fs_oric_jasmin::impl::system_file::read(u64 start, u64 length) +{ + if(start >= 0x3e00) + return std::vector(); + length += start; + if(length > 0x3e00) + length = 0x3e00; + std::vector data(length - start); + + for(u32 i = start / 256; i <= (length - 1) / 256; i++) { + u32 pos = i * 256; + u32 off = i * 256 < start ? start & 0xff : 0; + u32 len = i * 256 > length ? length & 0xff : 0x100; + auto dblk = m_fs.m_blockdev.get(i); + memcpy(data.data() + pos + off - start, dblk.rodata() + off, len); + } + + return data; +} + + const filesystem_manager_type FS_ORIC_JASMIN = &filesystem_manager_creator;; diff --git a/src/lib/formats/fs_oric_jasmin.h b/src/lib/formats/fs_oric_jasmin.h index 347a23ff349..823dcc90538 100644 --- a/src/lib/formats/fs_oric_jasmin.h +++ b/src/lib/formats/fs_oric_jasmin.h @@ -1,7 +1,7 @@ // license:BSD-3-Clause // copyright-holders:Olivier Galibert -// Creation of Oric Jasmin floppy images +// Management of Oric Jasmin floppy images #ifndef MAME_FORMATS_FS_ORIC_JASMIN_H #define MAME_FORMATS_FS_ORIC_JASMIN_H @@ -14,11 +14,75 @@ class fs_oric_jasmin : public filesystem_manager_t { public: class impl : public filesystem_t { public: - impl(fsblk_t &blockdev) : filesystem_t(blockdev, 256) {} + 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 fs_meta_data metadata() override; + virtual std::vector contents() override; + virtual file_t file_get(uint64_t key) override; + virtual dir_t dir_get(uint64_t key) override; + + private: + impl &m_fs; + + std::pair get_dir_block(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[18]; + + std::pair, u32> build_data_sector_table(); + }; + + class system_file : public ifile_t { + public: + system_file(impl &fs, const u8 *entry); + virtual ~system_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; + u8 m_entry[18]; + }; + + impl(fsblk_t &blockdev); virtual ~impl() = default; virtual void format(const fs_meta_data &meta) override; virtual fs_meta_data metadata() override; + virtual dir_t root() override; + + static u32 cs_to_block(u16 ref); + 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(); + + private: + dir_t m_root; }; fs_oric_jasmin() : filesystem_manager_t() {} @@ -29,7 +93,6 @@ public: virtual bool can_format() const override; virtual bool can_read() const override; virtual bool can_write() const override; - virtual bool has_subdirectories() const override; virtual std::vector volume_meta_description() const override; virtual std::vector file_meta_description() const override; diff --git a/src/lib/formats/fs_prodos.cpp b/src/lib/formats/fs_prodos.cpp index e2908f6cebd..acf47607085 100644 --- a/src/lib/formats/fs_prodos.cpp +++ b/src/lib/formats/fs_prodos.cpp @@ -70,9 +70,9 @@ bool fs_prodos::can_write() const return false; } -bool fs_prodos::has_subdirectories() const +char fs_prodos::directory_separator() const { - return true; + return '/'; } std::vector fs_prodos::volume_meta_description() const diff --git a/src/lib/formats/fs_prodos.h b/src/lib/formats/fs_prodos.h index 4ec3c8e1d4b..0d39251e2f2 100644 --- a/src/lib/formats/fs_prodos.h +++ b/src/lib/formats/fs_prodos.h @@ -31,7 +31,7 @@ public: virtual bool can_format() const override; virtual bool can_read() const override; virtual bool can_write() const override; - virtual bool has_subdirectories() const override; + virtual char directory_separator() const override; virtual std::vector volume_meta_description() const override; virtual std::vector file_meta_description() const override; diff --git a/src/lib/formats/fs_unformatted.cpp b/src/lib/formats/fs_unformatted.cpp index 451cda3c84f..573be8ac2b9 100644 --- a/src/lib/formats/fs_unformatted.cpp +++ b/src/lib/formats/fs_unformatted.cpp @@ -116,9 +116,4 @@ bool fs_unformatted::can_write() const return false; } -bool fs_unformatted::has_subdirectories() const -{ - return false; -} - const filesystem_manager_type FS_UNFORMATTED = &filesystem_manager_creator; diff --git a/src/lib/formats/fs_unformatted.h b/src/lib/formats/fs_unformatted.h index 71b9bc4feb4..ce472cafdb3 100644 --- a/src/lib/formats/fs_unformatted.h +++ b/src/lib/formats/fs_unformatted.h @@ -46,7 +46,6 @@ public: virtual bool can_format() const override; virtual bool can_read() const override; virtual bool can_write() const override; - virtual bool has_subdirectories() const override; }; extern const filesystem_manager_type FS_UNFORMATTED; diff --git a/src/lib/formats/fsmgr.cpp b/src/lib/formats/fsmgr.cpp index 048007730ce..a16eb57b288 100644 --- a/src/lib/formats/fsmgr.cpp +++ b/src/lib/formats/fsmgr.cpp @@ -8,17 +8,17 @@ #include "emu.h" #include "fsmgr.h" -void fsblk_t::iblock_t::ref() +void fs_refcounted_inner::ref() { m_ref ++; } -void fsblk_t::iblock_t::ref_weak() +void fs_refcounted_inner::ref_weak() { m_weak_ref ++; } -void fsblk_t::iblock_t::unref() +void fs_refcounted_inner::unref() { m_ref --; if(m_ref == 0) { @@ -31,7 +31,7 @@ void fsblk_t::iblock_t::unref() } } -void fsblk_t::iblock_t::unref_weak() +void fs_refcounted_inner::unref_weak() { m_weak_ref --; if(m_weak_ref == 0 && m_ref == 0) @@ -39,6 +39,7 @@ void fsblk_t::iblock_t::unref_weak() } + void filesystem_manager_t::enumerate_f(floppy_enumerator &fe, uint32_t form_factor, const std::vector &variants) const { } @@ -89,6 +90,11 @@ std::vector filesystem_manager_t::directory_meta_descriptio return res; } +char filesystem_manager_t::directory_separator() const +{ + return 0; // Subdirectories not supported by default +} + void filesystem_t::format(const fs_meta_data &meta) { fatalerror("format called on a filesystem not supporting it.\n"); @@ -126,44 +132,52 @@ const uint8_t *fsblk_t::iblock_t::rooffset(const char *function, uint32_t off, u void fsblk_t::block_t::copy(u32 offset, const uint8_t *src, u32 size) { - uint8_t *blk = m_block->offset("copy", offset, size); + uint8_t *blk = m_object->offset("copy", offset, size); memcpy(blk, src, size); } void fsblk_t::block_t::fill(u32 offset, uint8_t data, u32 size) { - uint8_t *blk = m_block->offset("fill", offset, size); + uint8_t *blk = m_object->offset("fill", offset, size); memset(blk, data, size); } void fsblk_t::block_t::fill(uint8_t data) { - uint8_t *blk = m_block->data(); - memset(blk, data, m_block->size()); + uint8_t *blk = m_object->data(); + memset(blk, data, m_object->size()); } void fsblk_t::block_t::wstr(u32 offset, const std::string &str) { - uint8_t *blk = m_block->offset("wstr", offset, str.size()); + uint8_t *blk = m_object->offset("wstr", offset, str.size()); memcpy(blk, str.data(), str.size()); } void fsblk_t::block_t::w8(u32 offset, uint8_t data) { - uint8_t *blk = m_block->offset("w8", offset, 1); + uint8_t *blk = m_object->offset("w8", offset, 1); blk[0] = data; } void fsblk_t::block_t::w16b(u32 offset, u16 data) { - uint8_t *blk = m_block->offset("w16b", offset, 2); + uint8_t *blk = m_object->offset("w16b", offset, 2); blk[0] = data >> 8; blk[1] = data; } +void fsblk_t::block_t::w24b(u32 offset, u32 data) +{ + uint8_t *blk = m_object->offset("w24b", offset, 3); + blk[0] = data >> 16; + blk[1] = data >> 8; + blk[2] = data; +} + void fsblk_t::block_t::w32b(u32 offset, u32 data) { - uint8_t *blk = m_block->offset("w32b", offset, 4); + uint8_t *blk = m_object->offset("w32b", offset, 4); blk[0] = data >> 24; blk[1] = data >> 16; blk[2] = data >> 8; @@ -172,14 +186,22 @@ void fsblk_t::block_t::w32b(u32 offset, u32 data) void fsblk_t::block_t::w16l(u32 offset, u16 data) { - uint8_t *blk = m_block->offset("w16l", offset, 2); + uint8_t *blk = m_object->offset("w16l", offset, 2); blk[0] = data; blk[1] = data >> 8; } +void fsblk_t::block_t::w24l(u32 offset, u32 data) +{ + uint8_t *blk = m_object->offset("w24l", offset, 3); + blk[0] = data; + blk[1] = data >> 8; + blk[2] = data >> 16; +} + void fsblk_t::block_t::w32l(u32 offset, u32 data) { - uint8_t *blk = m_block->offset("w32l", offset, 4); + uint8_t *blk = m_object->offset("w32l", offset, 4); blk[0] = data; blk[1] = data >> 8; blk[2] = data >> 16; @@ -188,13 +210,55 @@ void fsblk_t::block_t::w32l(u32 offset, u32 data) std::string fsblk_t::block_t::rstr(u32 offset, u32 size) { - const u8 *d = m_block->rooffset("rstr", offset, size); + const u8 *d = m_object->rooffset("rstr", offset, size); std::string res; for(u32 i=0; i != size; i++) res += char(*d++); return res; } +uint8_t fsblk_t::block_t::r8(u32 offset) +{ + const uint8_t *blk = m_object->offset("r8", offset, 1); + return blk[0]; +} + +uint16_t fsblk_t::block_t::r16b(u32 offset) +{ + const uint8_t *blk = m_object->offset("r16b", offset, 2); + return (blk[0] << 8) | blk[1]; +} + +uint32_t fsblk_t::block_t::r24b(u32 offset) +{ + const uint8_t *blk = m_object->offset("r24b", offset, 3); + return (blk[0] << 16) | (blk[1] << 8) | blk[2]; +} + +uint32_t fsblk_t::block_t::r32b(u32 offset) +{ + const uint8_t *blk = m_object->offset("r32b", offset, 4); + return (blk[0] << 24) | (blk[1] << 16) | (blk[2] << 8) | blk[3]; +} + +uint16_t fsblk_t::block_t::r16l(u32 offset) +{ + const uint8_t *blk = m_object->offset("r16l", offset, 2); + return blk[0] | (blk[1] << 8); +} + +uint32_t fsblk_t::block_t::r24l(u32 offset) +{ + const uint8_t *blk = m_object->offset("r24l", offset, 3); + return blk[0] | (blk[1] << 8) | (blk[2] << 16); +} + +uint32_t fsblk_t::block_t::r32l(u32 offset) +{ + const uint8_t *blk = m_object->offset("r32l", offset, 4); + return blk[0] | (blk[1] << 8) | (blk[2] << 16) | (blk[3] << 24); +} + const char *fs_meta_get_name(fs_meta_name name) { switch(name) { diff --git a/src/lib/formats/fsmgr.h b/src/lib/formats/fsmgr.h index dee33808388..305dd0fe92e 100644 --- a/src/lib/formats/fsmgr.h +++ b/src/lib/formats/fsmgr.h @@ -31,6 +31,12 @@ enum class fs_meta_type { flag, }; +enum class fs_dir_entry_type { + dir, + file, + system_file, +}; + using fs_meta = std::variant; using fs_meta_data = std::unordered_map; @@ -38,6 +44,77 @@ const char *fs_meta_get_name(fs_meta_name name); std::string fs_meta_to_string(fs_meta_type type, const fs_meta &m); 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) { + ref(); + } + + fs_refcounted_outer(const fs_refcounted_outer &cref) { + m_object = cref.m_object; + m_is_weak_ref = cref.m_is_weak_ref; + ref(); + } + + fs_refcounted_outer(fs_refcounted_outer &&cref) { + m_object = cref.m_object; + m_is_weak_ref = cref.m_is_weak_ref; + cref.m_object = nullptr; + } + + ~fs_refcounted_outer() { + unref(); + } + + fs_refcounted_outer &operator =(T *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(); + return *this; + } + + fs_refcounted_outer &operator =(fs_refcounted_outer &&cref) { + m_object = cref.m_object; + m_is_weak_ref = cref.m_is_weak_ref; + cref.m_object = nullptr; + return *this; + } + + operator bool() const { return m_object != nullptr; } + +protected: + T *m_object; + bool m_is_weak_ref; + +private: + void ref() { + if(m_object) { + if(m_is_weak_ref) + m_object->ref_weak(); + else + m_object->ref(); + } + } + + void unref() { + if(m_object) { + if(m_is_weak_ref) + m_object->unref_weak(); + else + m_object->unref(); + } + } +}; + struct fs_meta_description { fs_meta_name m_name; @@ -56,111 +133,82 @@ struct fs_meta_description { {} }; +class fs_refcounted_inner { +public: + fs_refcounted_inner() : m_ref(0), m_weak_ref(0) {} + virtual ~fs_refcounted_inner() = default; + + void ref(); + void ref_weak(); + void unref(); + void unref_weak(); + + virtual void drop_weak_references() = 0; + +private: + uint32_t m_ref, m_weak_ref; +}; + +struct fs_dir_entry { + std::string m_name; + fs_dir_entry_type m_type; + uint64_t m_key; + + fs_dir_entry(const std::string &name, fs_dir_entry_type type, uint64_t key) : m_name(name), m_type(type), m_key(key) {} +}; + class fsblk_t { protected: - class iblock_t { + class iblock_t : public fs_refcounted_inner { public: - iblock_t(uint32_t size) : m_ref(0), m_weak_ref(0), m_size(size) {} + iblock_t(uint32_t size) : fs_refcounted_inner(), m_size(size) {} virtual ~iblock_t() = default; - void ref(); - void ref_weak(); - void unref(); - void unref_weak(); - uint32_t size() const { return m_size; } - virtual void drop_weak_references() = 0; - virtual const uint8_t *rodata() = 0; virtual uint8_t *data() = 0; uint8_t *offset(const char *function, uint32_t off, uint32_t size); const uint8_t *rooffset(const char *function, uint32_t off, uint32_t size); protected: - uint32_t m_ref, m_weak_ref; uint32_t m_size; }; public: - class block_t { + class block_t : public fs_refcounted_outer { public: - block_t(iblock_t *block, bool weak = false) : m_block(block), m_is_weak_ref(weak) { - ref(); - } + block_t(bool weak = false) : fs_refcounted_outer(weak) {} + block_t(iblock_t *block, bool weak = false) : fs_refcounted_outer(block, weak) {} + virtual ~block_t() = default; - block_t(const block_t &cref) { - m_block = cref.m_block; - m_is_weak_ref = cref.m_is_weak_ref; - ref(); - } + block_t strong() { return block_t(m_object, false); } + block_t weak() { return block_t(m_object, true); } - block_t(block_t &&cref) { - m_block = cref.m_block; - m_is_weak_ref = cref.m_is_weak_ref; - cref.m_block = nullptr; - } - - ~block_t() { - unref(); - } - - block_t &operator =(const block_t &cref) { - m_block = cref.m_block; - m_is_weak_ref = cref.m_is_weak_ref; - ref(); - return *this; - } - - block_t &operator =(block_t &&cref) { - m_block = cref.m_block; - m_is_weak_ref = cref.m_is_weak_ref; - cref.m_block = nullptr; - return *this; - } - - const uint8_t *rodata() { - return m_block ? m_block->rodata() : nullptr; - } - - uint8_t *data() { - return m_block ? m_block->data() : nullptr; - } + const uint8_t *rodata() { return m_object->rodata(); } + uint8_t *data() { return m_object->data(); } void copy(uint32_t offset, const uint8_t *src, uint32_t size); - void fill( uint8_t data); + void fill( uint8_t data); void fill(uint32_t offset, uint8_t data, uint32_t size); void wstr(uint32_t offset, const std::string &str); void w8( uint32_t offset, uint8_t data); - void w16b(uint32_t offset, u16 data); + void w16b(uint32_t offset, uint16_t data); + void w24b(uint32_t offset, uint32_t data); void w32b(uint32_t offset, uint32_t data); - void w16l(uint32_t offset, u16 data); + void w16l(uint32_t offset, uint16_t data); + void w24l(uint32_t offset, uint32_t data); void w32l(uint32_t offset, uint32_t data); std::string rstr(uint32_t offset, uint32_t size); - - private: - iblock_t *m_block; - bool m_is_weak_ref; - - void ref() { - if(m_block) { - if(m_is_weak_ref) - m_block->ref_weak(); - else - m_block->ref(); - } - } - - void unref() { - if(m_block) { - if(m_is_weak_ref) - m_block->unref_weak(); - else - m_block->unref(); - } - } + uint8_t r8( uint32_t offset); + uint16_t r16b(uint32_t offset); + uint32_t r24b(uint32_t offset); + uint32_t r32b(uint32_t offset); + uint16_t r16l(uint32_t offset); + uint32_t r24l(uint32_t offset); + uint32_t r32l(uint32_t offset); }; fsblk_t() : m_block_size(0) {} @@ -177,148 +225,61 @@ protected: class filesystem_t { +public: + class dir_t; + class file_t; + protected: - class idir_t { + class idir_t : public fs_refcounted_inner { public: - virtual ~idir_t(); + idir_t() : fs_refcounted_inner() {} + virtual ~idir_t() = default; - void ref(); - void ref_weak(); - void unref(); - void unref_weak(); - - virtual void drop_weak_references(); + virtual fs_meta_data metadata() = 0; + virtual std::vector contents() = 0; + virtual file_t file_get(uint64_t key) = 0; + virtual dir_t dir_get(uint64_t key) = 0; }; - class ifile_t { + class ifile_t : public fs_refcounted_inner { public: - virtual ~ifile_t(); + ifile_t() : fs_refcounted_inner() {} + virtual ~ifile_t() = default; - void ref(); - void ref_weak(); - void unref(); - void unref_weak(); - - virtual void drop_weak_references(); + virtual fs_meta_data metadata() = 0; + virtual std::vector read_all() = 0; + virtual std::vector read(u64 start, u64 length) = 0; }; public: - class dir_t { + class dir_t : public fs_refcounted_outer { public: - dir_t(idir_t *dir, bool weak = false) : m_dir(dir), m_is_weak_ref(weak) { - ref(); - } + dir_t(bool weak = false) : fs_refcounted_outer(weak) {} + dir_t(idir_t *dir, bool weak = false) : fs_refcounted_outer(dir, weak) {} + virtual ~dir_t() = default; - dir_t(const dir_t &cref) { - m_dir = cref.m_dir; - m_is_weak_ref = cref.m_is_weak_ref; - ref(); - } + dir_t strong() { return dir_t(m_object, false); } + dir_t weak() { return dir_t(m_object, true); } - dir_t(dir_t &&cref) { - m_dir = cref.m_dir; - m_is_weak_ref = cref.m_is_weak_ref; - cref.m_dir = nullptr; - } - - ~dir_t() { - unref(); - } - - dir_t &operator =(const dir_t &cref) { - m_dir = cref.m_dir; - m_is_weak_ref = cref.m_is_weak_ref; - ref(); - return *this; - } - - dir_t &operator =(dir_t &&cref) { - m_dir = cref.m_dir; - m_is_weak_ref = cref.m_is_weak_ref; - cref.m_dir = nullptr; - return *this; - } - - private: - idir_t *m_dir; - bool m_is_weak_ref; - - void ref() { - if(m_dir) { - if(m_is_weak_ref) - m_dir->ref_weak(); - else - m_dir->ref(); - } - } - - void unref() { - if(m_dir) { - if(m_is_weak_ref) - m_dir->unref_weak(); - else - m_dir->unref(); - } - } + fs_meta_data metadata() { return m_object->metadata(); } + std::vector contents() { return m_object->contents(); } + file_t file_get(uint64_t key) { return m_object->file_get(key); } + dir_t dir_get(uint64_t key) { return m_object->dir_get(key); } }; - class file_t { + class file_t : public fs_refcounted_outer { public: - file_t(ifile_t *file, bool weak = false) : m_file(file), m_is_weak_ref(weak) { - ref(); - } + file_t(bool weak = false) : fs_refcounted_outer(weak) {} + file_t(ifile_t *file, bool weak = false) : fs_refcounted_outer(file, weak) {} + virtual ~file_t() = default; - file_t(const file_t &cref) { - m_file = cref.m_file; - m_is_weak_ref = cref.m_is_weak_ref; - ref(); - } + file_t strong() { return file_t(m_object, false); } + file_t weak() { return file_t(m_object, true); } - file_t(file_t &&cref) { - m_file = cref.m_file; - m_is_weak_ref = cref.m_is_weak_ref; - cref.m_file = nullptr; - } + fs_meta_data metadata() { return m_object->metadata(); } - ~file_t() { - unref(); - } - - file_t &operator =(const file_t &cref) { - m_file = cref.m_file; - m_is_weak_ref = cref.m_is_weak_ref; - ref(); - return *this; - } - - file_t &operator =(file_t &&cref) { - m_file = cref.m_file; - m_is_weak_ref = cref.m_is_weak_ref; - cref.m_file = nullptr; - return *this; - } - - private: - ifile_t *m_file; - bool m_is_weak_ref; - - void ref() { - if(m_file) { - if(m_is_weak_ref) - m_file->ref_weak(); - else - m_file->ref(); - } - } - - void unref() { - if(m_file) { - if(m_is_weak_ref) - m_file->unref_weak(); - else - m_file->unref(); - } - } + std::vector read_all() { return m_object->read_all(); } + std::vector read(u32 start, u32 length) { return m_object->read(start, length); } }; filesystem_t(fsblk_t &blockdev, u32 size) : m_blockdev(blockdev) { @@ -368,7 +329,9 @@ public: virtual bool can_format() const = 0; virtual bool can_read() const = 0; virtual bool can_write() const = 0; - virtual bool has_subdirectories() const = 0; + virtual char directory_separator() const; + + bool has_subdirectories() const { return directory_separator() != 0; } virtual std::vector volume_meta_description() const; virtual std::vector file_meta_description() const; diff --git a/src/tools/floptool.cpp b/src/tools/floptool.cpp index 73e34ef616b..bc86393dc5e 100644 --- a/src/tools/floptool.cpp +++ b/src/tools/floptool.cpp @@ -277,7 +277,8 @@ static void display_usage() fprintf(stderr, " floptool.exe identify [ ...]\n"); fprintf(stderr, " floptool.exe convert [input_format|auto] output_format \n"); fprintf(stderr, " floptool.exe create output_format filesystem \n"); - fprintf(stderr, " floptool.exe dir input_format filesystem \n"); + fprintf(stderr, " floptool.exe dir input_format filesystem \n"); + fprintf(stderr, " floptool.exe read input_format filesystem \n"); } static void display_formats() @@ -502,6 +503,51 @@ static int create(int argc, char *argv[]) return 0; } +static void dir_scan(u32 depth, filesystem_t::dir_t dir, std::vector> &entries, const std::unordered_map &nmap, size_t nc, const std::vector &dmetad, const std::vector &fmetad) +{ + std::string head; + for(u32 i = 0; i != depth; i++) + head += " "; + auto contents = dir.contents(); + 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(); + for(const auto &m : dmetad) { + if(meta.find(m.m_name) == meta.end()) + continue; + size_t slot = nmap.find(m.m_name)->second; + std::string val = fs_meta_to_string(m.m_type, meta.find(m.m_name)->second); + if(slot == 0) + val = head + "dir " + val; + entries[id][slot] = val; + } + dir_scan(depth+1, subdir, 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(); + for(const auto &m : fmetad) { + if(meta.find(m.m_name) == meta.end()) + continue; + size_t slot = nmap.find(m.m_name)->second; + std::string val = fs_meta_to_string(m.m_type, meta.find(m.m_name)->second); + if(slot == 0) + val = head + (c.m_type == fs_dir_entry_type::system_file ? "sys " : "file ") + val; + entries[id][slot] = val; + } + break; + } + } + } +} + static int dir(int argc, char *argv[]) { if (argc!=5) { @@ -555,15 +601,169 @@ static int dir(int argc, char *argv[]) fsblk_vec_t blockdev(img); auto load_fs = fs->m_manager->mount(blockdev); auto vmetad = fs->m_manager->volume_meta_description(); - auto vmeta = load_fs->metadata(); + auto fmetad = fs->m_manager->file_meta_description(); + auto dmetad = fs->m_manager->directory_meta_description(); + auto vmeta = load_fs->metadata(); if(!vmeta.empty()) { std::string vinf = "Volume:"; for(const auto &e : vmetad) vinf += util::string_format(" %s=%s", fs_meta_get_name(e.m_name), fs_meta_to_string(e.m_type, vmeta[e.m_name])); - printf("%s\n", vinf.c_str()); + printf("%s\n\n", vinf.c_str()); } + std::vector names; + names.push_back(fs_meta_name::name); + for(const auto &e : fmetad) + if(e.m_name != fs_meta_name::name) + names.push_back(e.m_name); + for(const auto &e : dmetad) + if(std::find(names.begin(), names.end(), e.m_name) == names.end()) + names.push_back(e.m_name); + + std::unordered_map nmap; + for(size_t i = 0; i != names.size(); i++) + nmap[names[i]] = i; + + auto root = load_fs->root(); + std::vector> entries; + + entries.resize(1); + for(fs_meta_name n : names) + entries[0].push_back(fs_meta_get_name(n)); + + dir_scan(0, root, entries, nmap, names.size(), dmetad, fmetad); + + std::vector sizes(names.size()); + + for(const auto &e : entries) + for(unsigned int i=0; i != names.size(); i++) + sizes[i] = std::max(sizes[i], e[i].size()); + + for(const auto &e : entries) { + std::string l; + for(unsigned int i=0; i != names.size(); i++) { + if(i) + l += ' '; + l += util::string_format("%-*s", sizes[i], e[i]); + } + printf("%s\n", l.c_str()); + } + + return 0; +} + + +static int doread(int argc, char *argv[]) +{ + if (argc!=7) { + fprintf(stderr, "Incorrect number of arguments.\n\n"); + display_usage(); + return 1; + } + + auto format = find_format_by_name(argv[2]); + if(!format) { + fprintf(stderr, "Error: Format '%s' unknown\n", argv[3]); + return 1; + } + + auto fs = find_fs_by_name(argv[3]); + if(!fs) { + fprintf(stderr, "Error: Filesystem '%s' unknown\n", argv[2]); + return 1; + } + + if(!fs->m_manager || !fs->m_manager->can_read()) { + fprintf(stderr, "Error: Filesystem '%s' does not implement reading\n", argv[2]); + return 1; + } + + char msg[4096]; + sprintf(msg, "Error opening %s for reading", argv[4]); + FILE *f = fopen(argv[4], "rb"); + if (!f) { + perror(msg); + return 1; + } + io_generic io; + io.file = f; + io.procs = &stdio_ioprocs_noclose; + io.filler = 0xff; + + floppy_image image(84, 2, floppy_image::FF_UNKNOWN); + if(!format->load(&io, floppy_image::FF_UNKNOWN, variants, &image)) { + fprintf(stderr, "Error: parsing input file as '%s' failed\n", format->name()); + return 1; + } + + std::vector img; + auto iog = ram_open(img); + auto load_format = fs->m_type(); + load_format->save(iog, variants, &image); + delete load_format; + delete iog; + + fsblk_vec_t blockdev(img); + auto load_fs = fs->m_manager->mount(blockdev); + + std::string opath = argv[5]; + std::vector path; + if(fs->m_manager->has_subdirectories()) { + abort(); + + } else + path.push_back(opath); + + auto dir = load_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(), fs->m_manager->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(), fs->m_manager->directory_separator(), path[i].c_str()); + return 1; + } + dir = dir.dir_get(c[j].m_key); + apath += fs->m_manager->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(), fs->m_manager->directory_separator(), path.back().c_str()); + return 1; + } + auto file = dir.file_get(c[j].m_key); + auto meta = file.metadata(); + + if(meta.find(fs_meta_name::length) == meta.end()) { + fprintf(stderr, "Error: %s%c%s is not a readable file\n", apath.c_str(), fs->m_manager->directory_separator(), path.back().c_str()); + return 1; + } + + auto filedata = file.read_all(); + + sprintf(msg, "Error opening %s for writing", argv[6]); + auto fo = fopen(argv[6], "wb"); + if (!fo) { + perror(msg); + return 1; + } + + fwrite(filedata.data(), filedata.size(), 1, fo); + fclose(fo); + return 0; } @@ -585,6 +785,8 @@ int CLIB_DECL main(int argc, char *argv[]) return create(argc, argv); else if (!core_stricmp("dir", argv[1])) return dir(argc, argv); + else if (!core_stricmp("read", argv[1])) + return doread(argc, argv); else { fprintf(stderr, "Unknown command '%s'\n\n", argv[1]); display_usage();