summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
author Olivier Galibert <galibert@pobox.com>2021-04-29 14:45:55 +0200
committer Olivier Galibert <galibert@pobox.com>2021-04-30 21:43:16 +0200
commite72e97b17457124eaffff07fe462795ac526480a (patch)
treecbbe169ada04f6d8659ebd0ffa9ec7badf2d8439
parent7ae546e0b6d12ea3d02fb501a87f6d2a5b42817c (diff)
fs: Add full jasmin read support
-rw-r--r--src/lib/formats/fs_oric_jasmin.cpp309
-rw-r--r--src/lib/formats/fs_oric_jasmin.h69
-rw-r--r--src/lib/formats/fs_prodos.cpp4
-rw-r--r--src/lib/formats/fs_prodos.h2
-rw-r--r--src/lib/formats/fs_unformatted.cpp5
-rw-r--r--src/lib/formats/fs_unformatted.h1
-rw-r--r--src/lib/formats/fsmgr.cpp94
-rw-r--r--src/lib/formats/fsmgr.h361
-rw-r--r--src/tools/floptool.cpp208
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_meta_description> fs_oric_jasmin::volume_meta_description() const
{
std::vector<fs_meta_description> 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_dir_entry> fs_oric_jasmin::impl::root_dir::contents()
+{
+ std::vector<fs_dir_entry> 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<fsblk_t::block_t, u32> 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<fsblk_t::block_t, u32>(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<u8> fs_oric_jasmin::impl::file::read_all()
+{
+ auto [sect, length] = build_data_sector_table();
+ std::vector<u8> 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<u8> 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<u8>();
+
+ std::vector<u8> 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<std::vector<u16>, u32> fs_oric_jasmin::impl::file::build_data_sector_table()
+{
+ std::pair<std::vector<u16>, 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<u8> fs_oric_jasmin::impl::system_file::read_all()
+{
+ 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;
+}
+
+std::vector<u8> fs_oric_jasmin::impl::system_file::read(u64 start, u64 length)
+{
+ if(start >= 0x3e00)
+ return std::vector<u8>();
+ length += start;
+ if(length > 0x3e00)
+ length = 0x3e00;
+ std::vector<u8> 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<fs_oric_jasmin>;;
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<fs_dir_entry> 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<fsblk_t::block_t, u32> 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<u8> read_all() override;
+ virtual std::vector<u8> read(u64 start, u64 length) override;
+
+ private:
+ impl &m_fs;
+ u16 m_key;
+ u8 m_entry[18];
+
+ std::pair<std::vector<u16>, 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<u8> read_all() override;
+ virtual std::vector<u8> 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<fs_meta_description> volume_meta_description() const override;
virtual std::vector<fs_meta_description> 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_meta_description> 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<fs_meta_description> volume_meta_description() const override;
virtual std::vector<fs_meta_description> 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<fs_unformatted>;
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<uint32_t> &variants) const
{
}
@@ -89,6 +90,11 @@ std::vector<fs_meta_description> 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<std::string, uint64_t, bool>;
using fs_meta_data = std::unordered_map<fs_meta_name, fs_meta>;
@@ -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<typename T> 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<T> &operator =(T *dir) {
+ unref();
+ m_object = dir;
+ ref();
+ return *this;
+ }
+
+ fs_refcounted_outer<T> &operator =(const fs_refcounted_outer<T> &cref) {
+ unref();
+ m_object = cref.m_object;
+ m_is_weak_ref = cref.m_is_weak_ref;
+ ref();
+ return *this;
+ }
+
+ fs_refcounted_outer<T> &operator =(fs_refcounted_outer<T> &&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<iblock_t> {
public:
- block_t(iblock_t *block, bool weak = false) : m_block(block), m_is_weak_ref(weak) {
- ref();
- }
-
- block_t(const block_t &cref) {
- m_block = cref.m_block;
- m_is_weak_ref = cref.m_is_weak_ref;
- ref();
- }
-
- 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(bool weak = false) : fs_refcounted_outer<iblock_t>(weak) {}
+ block_t(iblock_t *block, bool weak = false) : fs_refcounted_outer(block, weak) {}
+ virtual ~block_t() = default;
- ~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 strong() { return block_t(m_object, false); }
+ block_t weak() { return block_t(m_object, true); }
- 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<fs_dir_entry> 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();
-
- void ref();
- void ref_weak();
- void unref();
- void unref_weak();
+ ifile_t() : fs_refcounted_inner() {}
+ virtual ~ifile_t() = default;
- virtual void drop_weak_references();
+ virtual fs_meta_data metadata() = 0;
+ virtual std::vector<u8> read_all() = 0;
+ virtual std::vector<u8> read(u64 start, u64 length) = 0;
};
public:
- class dir_t {
+ class dir_t : public fs_refcounted_outer<idir_t> {
public:
- dir_t(idir_t *dir, bool weak = false) : m_dir(dir), m_is_weak_ref(weak) {
- ref();
- }
-
- dir_t(const dir_t &cref) {
- m_dir = cref.m_dir;
- m_is_weak_ref = cref.m_is_weak_ref;
- ref();
- }
-
- 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(bool weak = false) : fs_refcounted_outer<idir_t>(weak) {}
+ dir_t(idir_t *dir, bool weak = false) : fs_refcounted_outer(dir, weak) {}
+ virtual ~dir_t() = default;
- ~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 strong() { return dir_t(m_object, false); }
+ dir_t weak() { return dir_t(m_object, true); }
- 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<fs_dir_entry> 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<ifile_t> {
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<ifile_t>(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<u8> read_all() { return m_object->read_all(); }
+ std::vector<u8> 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<fs_meta_description> volume_meta_description() const;
virtual std::vector<fs_meta_description> 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 <inputfile> [<inputfile> ...]\n");
fprintf(stderr, " floptool.exe convert [input_format|auto] output_format <inputfile> <outputfile>\n");
fprintf(stderr, " floptool.exe create output_format filesystem <outputfile>\n");
- fprintf(stderr, " floptool.exe dir input_format filesystem <inputfile>\n");
+ fprintf(stderr, " floptool.exe dir input_format filesystem <image>\n");
+ fprintf(stderr, " floptool.exe read input_format filesystem <image> <path> <outputfile>\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<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();
+ 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<fs_meta_name> 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<fs_meta_name, size_t> nmap;
+ for(size_t i = 0; i != names.size(); i++)
+ nmap[names[i]] = i;
+
+ auto root = load_fs->root();
+ std::vector<std::vector<std::string>> 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<u32> sizes(names.size());
+
+ for(const auto &e : entries)
+ for(unsigned int i=0; i != names.size(); i++)
+ sizes[i] = std::max<u32>(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<u8> 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<std::string> 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();