summaryrefslogtreecommitdiffstatshomepage
path: root/src
diff options
context:
space:
mode:
author Olivier Galibert <galibert@pobox.com>2021-05-16 19:43:32 +0200
committer Olivier Galibert <galibert@pobox.com>2021-05-16 19:43:43 +0200
commit3afebb520a0217285bfc7ffc341cd5acbf7bd988 (patch)
tree464274565218e5f0a77423161ab2fb254bc9deab /src
parent919468ac248764ffbac7737e109aba915654de97 (diff)
jasmin: Support write
Diffstat (limited to 'src')
-rw-r--r--src/frontend/mame/ui/floppycntrl.cpp6
-rw-r--r--src/lib/formats/fs_oric_jasmin.cpp361
-rw-r--r--src/lib/formats/fs_oric_jasmin.h27
-rw-r--r--src/lib/formats/fs_prodos.cpp88
-rw-r--r--src/lib/formats/fs_prodos.h2
-rw-r--r--src/lib/formats/fsmgr.cpp43
-rw-r--r--src/lib/formats/fsmgr.h53
-rw-r--r--src/tools/floptool.cpp329
8 files changed, 662 insertions, 247 deletions
diff --git a/src/frontend/mame/ui/floppycntrl.cpp b/src/frontend/mame/ui/floppycntrl.cpp
index f06c122280f..d933fafac74 100644
--- a/src/frontend/mame/ui/floppycntrl.cpp
+++ b/src/frontend/mame/ui/floppycntrl.cpp
@@ -54,12 +54,6 @@ void menu_control_floppy_image::do_load_create()
err = fd.finish_load();
if (err == image_init_result::PASS) {
fs_meta_data meta;
- if(create_fs->m_manager) {
- auto metav = create_fs->m_manager->volume_meta_description();
- for(const auto &e : metav)
- if(!e.m_ro)
- meta[e.m_name] = e.m_default;
- }
fd.init_fs(create_fs, meta);
}
}
diff --git a/src/lib/formats/fs_oric_jasmin.cpp b/src/lib/formats/fs_oric_jasmin.cpp
index fec9261364e..686eb2bb71a 100644
--- a/src/lib/formats/fs_oric_jasmin.cpp
+++ b/src/lib/formats/fs_oric_jasmin.cpp
@@ -64,7 +64,7 @@ bool fs_oric_jasmin::can_read() const
bool fs_oric_jasmin::can_write() const
{
- return false;
+ return true;
}
bool fs_oric_jasmin::has_rsrc() const
@@ -88,7 +88,7 @@ fs_meta_data fs_oric_jasmin::impl::metadata()
while(len > 0 && bdir.rodata()[0xf8 + len - 1] == ' ')
len--;
- res[fs_meta_name::name] = bdir.rstr(0xf8, len);
+ res.set(fs_meta_name::name, bdir.rstr(0xf8, len));
return res;
}
@@ -116,7 +116,7 @@ std::vector<fs_meta_description> fs_oric_jasmin::file_meta_description() const
void fs_oric_jasmin::impl::format(const fs_meta_data &meta)
{
- std::string volume_name = meta.find(fs_meta_name::name)->second.as_string();
+ std::string volume_name = meta.get_string(fs_meta_name::name, "UNTITLED");
u32 blocks = m_blockdev.block_count();
m_blockdev.fill(0x6c);
@@ -139,7 +139,7 @@ void fs_oric_jasmin::impl::format(const fs_meta_data &meta)
fmap.w8(0xf6, 0x80);
fmap.w8(0xf7, 0x80);
- fmap.fill(0xf8, 0x20, 8);
+ volume_name.resize(8, ' ');
fmap.wstr(0xf8, volume_name);
auto bdir = m_blockdev.get(20*17+1);
@@ -266,11 +266,18 @@ filesystem_t::file_t fs_oric_jasmin::impl::root_dir::file_get(uint64_t key)
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));
+ return file_t(new system_file(m_fs, this, bdir.rodata() + off, key));
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));
+ return file_t(new file(m_fs, this, bdir.rodata() + off, key));
+}
+
+void fs_oric_jasmin::impl::root_dir::update_file(u16 key, const u8 *entry)
+{
+ uint64_t rkey = key;
+ auto [bdir, off] = get_dir_block(rkey);
+ bdir.copy(off, entry, 18);
}
filesystem_t::dir_t fs_oric_jasmin::impl::root_dir::dir_get(uint64_t key)
@@ -278,10 +285,29 @@ 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)
+std::string fs_oric_jasmin::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;
+}
+
+fs_oric_jasmin::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);
- (void)m_key;
}
void fs_oric_jasmin::impl::file::drop_weak_references()
@@ -292,79 +318,36 @@ 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] = r16l(m_entry + 0x10);
+ res.set(fs_meta_name::name, read_file_name(m_entry + 3));
+ res.set(fs_meta_name::locked, m_entry[2] == 'L');
+ res.set(fs_meta_name::sequential, m_entry[0xf] == 'S');
+ res.set(fs_meta_name::size_in_blocks, r16l(m_entry + 0x10));
u16 ref = r16b(m_entry);
auto dblk = m_fs.m_blockdev.get(cs_to_block(ref));
- res[fs_meta_name::loading_address] = uint64_t(dblk.r16l(2));
- res[fs_meta_name::length] = uint64_t(dblk.r16l(4));
+ res.set(fs_meta_name::loading_address, dblk.r16l(2));
+ res.set(fs_meta_name::length, 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;
+ 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; pos += 2) {
+ for(u32 pos = 6; pos != 256 && data.size() < length; pos += 2) {
u16 dref = iblk.r16b(pos);
if(!m_fs.ref_valid(dref))
goto done;
- res.first.push_back(dref);
+ auto dblk = m_fs.m_blockdev.get(cs_to_block(dref));
+ u32 dpos = data.size();
+ data.resize(dpos + 256);
+ memcpy(data.data() + dpos, dblk.rodata(), 256);
+ if(data.size() >= length)
+ goto done;
}
ref = iblk.r16b(2);
if(!m_fs.ref_valid(ref))
@@ -372,13 +355,11 @@ std::pair<std::vector<u16>, u32> fs_oric_jasmin::impl::file::build_data_sector_t
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;
+ data.resize(length);
+ return data;
}
-fs_oric_jasmin::impl::system_file::system_file(impl &fs, const u8 *entry) : m_fs(fs)
+fs_oric_jasmin::impl::system_file::system_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);
}
@@ -391,11 +372,11 @@ 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] = r16l(m_entry + 0x10);
- res[fs_meta_name::length] = 0x3e00;
+ res.set(fs_meta_name::name, read_file_name(m_entry + 3));
+ res.set(fs_meta_name::locked, m_entry[2] == 'L');
+ res.set(fs_meta_name::sequential, m_entry[0xf] == 'S');
+ res.set(fs_meta_name::size_in_blocks, r16l(m_entry + 0x10));
+ res.set(fs_meta_name::length, 0x3e00);
return res;
}
@@ -410,25 +391,225 @@ std::vector<u8> fs_oric_jasmin::impl::system_file::read_all()
return data;
}
-std::vector<u8> fs_oric_jasmin::impl::system_file::read(u64 start, u64 length)
+fs_oric_jasmin::impl::file_t fs_oric_jasmin::impl::root_dir::file_create(const fs_meta_data &info)
{
- 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);
+ // 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);
+ uint64_t 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))
+ goto found;
+ id++;
+ }
+ u16 ref = bdir.r16b(2);
+ if(!ref || !m_fs.ref_valid(ref)) {
+ nb ++;
+ break;
+ }
+ bdir = m_fs.m_blockdev.get(cs_to_block(ref));
+ }
+ found:
+ auto block = m_fs.allocate_blocks(nb);
+ if(block.empty())
+ return nullptr;
+
+ auto sblk = m_fs.m_blockdev.get(cs_to_block(block[0]));
+ sblk.w16b(0, 0xff00); // Next sector
+ sblk.w16l(2, info.get_number(fs_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.fill(0xff);
+ bdir.w16l(0, block[2]); // Reference to itself
+ bdir.w16l(2, 0xff00); // No next directory sector
}
- return data;
+ 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(fs_meta_name::locked, false) ? 'L' : 'U');
+ bdir.wstr(off+0x03, file_name_prepare(info.get_string(fs_meta_name::name, "")));
+ bdir.w8 (off+0x0f, info.get_flag(fs_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));
}
+
+void fs_oric_jasmin::impl::root_dir::file_delete(uint64_t key)
+{
+}
+
+void fs_oric_jasmin::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;
+
+ // Enough space?
+ if(cur_ns < need_ns && m_fs.free_block_count() < need_ns - cur_ns)
+ return;
+
+ 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(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);
+ }
+ }
+ }
+ w16l(m_entry + 0x10, need_ns);
+ w16b(m_entry + 0x00, blocks[0]);
+ m_dir->update_file(m_key, m_entry);
+}
+
+void fs_oric_jasmin::impl::root_dir::metadata_change(const fs_meta_data &info)
+{
+}
+
+void fs_oric_jasmin::impl::metadata_change(const fs_meta_data &info)
+{
+ if(info.has(fs_meta_name::name)) {
+ std::string volume_name = info.get_string(fs_meta_name::name);
+ volume_name.resize(8, ' ');
+ m_blockdev.get(20*17).wstr(0xf8, volume_name);
+ }
+}
+
+void fs_oric_jasmin::impl::file::metadata_change(const fs_meta_data &info)
+{
+ if(info.has(fs_meta_name::locked))
+ w8 (m_entry+0x02, info.get_flag(fs_meta_name::locked) ? 'L' : 'U');
+ if(info.has(fs_meta_name::name))
+ wstr(m_entry+0x03, file_name_prepare(info.get_string(fs_meta_name::name)));
+ if(info.has(fs_meta_name::sequential))
+ w8 (m_entry+0x0f, info.get_flag(fs_meta_name::sequential) ? 'D' : 'S');
+ if(info.has(fs_meta_name::loading_address))
+ m_fs.m_blockdev.get(cs_to_block(r16b(m_entry))).w16l(2, info.get_number(fs_meta_name::loading_address));
+ m_dir->update_file(m_key, m_entry);
+}
+
+void fs_oric_jasmin::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 fs_oric_jasmin::impl::system_file::metadata_change(const fs_meta_data &info)
+{
+ if(info.has(fs_meta_name::locked))
+ w8 (m_entry+0x02, info.get_flag(fs_meta_name::locked) ? 'L' : 'U');
+ if(info.has(fs_meta_name::name))
+ wstr(m_entry+0x03, file_name_prepare(info.get_string(fs_meta_name::name)));
+ if(info.has(fs_meta_name::sequential))
+ w8 (m_entry+0x0f, info.get_flag(fs_meta_name::sequential) ? 'S' : 'D');
+ if(info.has(fs_meta_name::loading_address))
+ m_fs.m_blockdev.get(cs_to_block(r16b(m_entry))).w16l(2, info.get_number(fs_meta_name::loading_address));
+ m_dir->update_file(m_key, m_entry);
+}
+
+std::vector<u16> fs_oric_jasmin::impl::allocate_blocks(u32 count)
+{
+ std::vector<u16> blocks;
+ if(free_block_count() < count)
+ return blocks;
+
+ auto fmap = m_blockdev.get(20*17);
+ u32 nf = 0;
+ for(u32 track = 0; track != 2*41 && nf != count; track++) {
+ u32 map = fmap.r24l(track*3);
+ if(map != 0x800000) {
+ for(u32 sect = 1; sect <= 17 && nf != count; sect++)
+ if(map & (0x20000 >> sect)) {
+ blocks.push_back((track << 8) | sect);
+ map &= ~(0x20000 >> sect);
+ nf++;
+ }
+ if(!map)
+ map = 0x800000;
+ fmap.w24l(track*3, map);
+ }
+ }
+ return blocks;
+}
+
+void fs_oric_jasmin::impl::free_blocks(const std::vector<u16> &blocks)
+{
+ auto fmap = m_blockdev.get(20*17);
+ for(u16 ref : blocks) {
+ u32 track = ref >> 8;
+ u32 sect = ref & 0xff;
+ u32 map = fmap.r24l(track*3);
+ if(map == 0x800000)
+ map = 0;
+ map |= 0x20000 >> sect;
+ fmap.w24l(track*3, map);
+ }
+}
+
+u32 fs_oric_jasmin::impl::free_block_count()
+{
+ auto fmap = m_blockdev.get(20*17);
+ u32 nf = 0;
+ for(u32 track = 0; track != 2*41; track++) {
+ u32 map = fmap.r24l(track*3);
+ if(map != 0x800000) {
+ for(u32 sect = 1; sect <= 17; sect++)
+ if(map & (0x20000 >> sect))
+ nf++;
+ }
+ }
+ return nf;
+}
+
+
+
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 a38fe2b6952..c71cbf9097e 100644
--- a/src/lib/formats/fs_oric_jasmin.h
+++ b/src/lib/formats/fs_oric_jasmin.h
@@ -22,9 +22,14 @@ public:
virtual void drop_weak_references() override;
virtual fs_meta_data metadata() override;
+ virtual void metadata_change(const fs_meta_data &info) 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;
+ virtual file_t file_create(const fs_meta_data &info) override;
+ virtual void file_delete(uint64_t key) override;
+
+ void update_file(u16 key, const u8 *entry);
private:
impl &m_fs;
@@ -34,36 +39,39 @@ public:
class file : public ifile_t {
public:
- file(impl &fs, const u8 *entry, u16 key);
+ file(impl &fs, root_dir *dir, const u8 *entry, u16 key);
virtual ~file() = default;
virtual void drop_weak_references() override;
virtual fs_meta_data metadata() override;
+ virtual void metadata_change(const fs_meta_data &info) override;
virtual std::vector<u8> read_all() override;
- virtual std::vector<u8> read(u64 start, u64 length) 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];
-
- std::pair<std::vector<u16>, u32> build_data_sector_table();
};
class system_file : public ifile_t {
public:
- system_file(impl &fs, const u8 *entry);
+ system_file(impl &fs, root_dir *dir, const u8 *entry, u16 key);
virtual ~system_file() = default;
virtual void drop_weak_references() override;
virtual fs_meta_data metadata() override;
+ virtual void metadata_change(const fs_meta_data &info) override;
virtual std::vector<u8> read_all() override;
- virtual std::vector<u8> read(u64 start, u64 length) 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];
};
@@ -72,6 +80,7 @@ public:
virtual void format(const fs_meta_data &meta) override;
virtual fs_meta_data metadata() override;
+ virtual void metadata_change(const fs_meta_data &info) override;
virtual dir_t root() override;
static u32 cs_to_block(u16 ref);
@@ -81,6 +90,12 @@ public:
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_prepare(std::string name);
+
private:
dir_t m_root;
};
diff --git a/src/lib/formats/fs_prodos.cpp b/src/lib/formats/fs_prodos.cpp
index 1845f4eb853..844e1d9c81d 100644
--- a/src/lib/formats/fs_prodos.cpp
+++ b/src/lib/formats/fs_prodos.cpp
@@ -123,7 +123,7 @@ std::vector<fs_meta_description> fs_prodos::directory_meta_description() const
void fs_prodos::impl::format(const fs_meta_data &meta)
{
- std::string volume_name = meta.find(fs_meta_name::name)->second.as_string();
+ std::string volume_name = meta.get_string(fs_meta_name::name, "UNTITLED");
u32 blocks = m_blockdev.block_count();
// Maximum usable partition size = 32M - 512 bytes (65535 blocks)
@@ -211,11 +211,11 @@ fs_meta_data fs_prodos::impl::metadata()
fs_meta_data res;
auto bdir = m_blockdev.get(2);
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));
+ res.set(fs_meta_name::name, bdir.rstr(0x05, len));
+ res.set(fs_meta_name::os_version, bdir.r8(0x20));
+ res.set(fs_meta_name::os_minimum_version, bdir.r8(0x21));
+ res.set(fs_meta_name::creation_date, prodos_to_dt(bdir.r32l(0x1c)));
+ res.set(fs_meta_name::modification_date, prodos_to_dt(bdir.r32l(0x16)));
return res;
}
@@ -246,11 +246,11 @@ fs_meta_data fs_prodos::impl::dir::metadata()
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));
+ res.set(fs_meta_name::name, bdir.rstr(0x05, len));
+ res.set(fs_meta_name::os_version, bdir.r8(0x20));
+ res.set(fs_meta_name::os_minimum_version, bdir.r8(0x21));
+ res.set(fs_meta_name::creation_date, prodos_to_dt(bdir.r32l(0x1c)));
+ res.set(fs_meta_name::modification_date, prodos_to_dt(bdir.r32l(0x16)));
return res;
}
@@ -357,14 +357,14 @@ fs_meta_data fs_prodos::impl::file::metadata()
u8 type = r8(m_entry);
std::string name = rstr(m_entry+1, type & 0xf);
type >>= 4;
- res[fs_meta_name::name] = name;
+ res.set(fs_meta_name::name, name);
if(type == 5) {
auto rootblk = m_fs.m_blockdev.get(r16l(m_entry+0x11));
- res[fs_meta_name::length] = rootblk.r24l(0x005);
- res[fs_meta_name::rsrc_length] = rootblk.r24l(0x105);
+ res.set(fs_meta_name::length, rootblk.r24l(0x005));
+ res.set(fs_meta_name::rsrc_length, rootblk.r24l(0x105));
} else if(type >= 1 && type <= 3)
- res[fs_meta_name::length] = r24l(m_entry + 0x15);
+ res.set(fs_meta_name::length, r24l(m_entry + 0x15));
else
fatalerror("fs_prodos::impl::file::metadata: Unhandled file type %d\n", type);
@@ -467,35 +467,6 @@ std::vector<u8> fs_prodos::impl::file::read_all()
return data;
}
-std::vector<u8> fs_prodos::impl::file::read(u64 start, u64 length)
-{
- auto [blocks, rlength] = data_blocks();
- 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 block : blocks) {
- u32 npos = pos + 512;
- 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(block);
- memcpy(data.data() + pos + off - start, dblk.rodata() + off, npos - pos - off);
- }
- } else
- break;
- pos = npos;
- }
- return data;
-}
-
std::vector<u8> fs_prodos::impl::file::rsrc_read_all()
{
auto [blocks, length] = rsrc_blocks();
@@ -516,33 +487,4 @@ std::vector<u8> fs_prodos::impl::file::rsrc_read_all()
return data;
}
-std::vector<u8> fs_prodos::impl::file::rsrc_read(u64 start, u64 length)
-{
- auto [blocks, rlength] = rsrc_blocks();
- 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 block : blocks) {
- u32 npos = pos + 512;
- 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(block);
- memcpy(data.data() + pos + off - start, dblk.rodata() + off, npos - pos - off);
- }
- } else
- break;
- pos = npos;
- }
- return data;
-}
-
const filesystem_manager_type FS_PRODOS = &filesystem_manager_creator<fs_prodos>;;
diff --git a/src/lib/formats/fs_prodos.h b/src/lib/formats/fs_prodos.h
index be83ab5cd03..49ac796783b 100644
--- a/src/lib/formats/fs_prodos.h
+++ b/src/lib/formats/fs_prodos.h
@@ -44,9 +44,7 @@ public:
virtual fs_meta_data metadata() override;
virtual std::vector<u8> read_all() override;
- virtual std::vector<u8> read(u64 start, u64 length) override;
virtual std::vector<u8> rsrc_read_all() override;
- virtual std::vector<u8> rsrc_read(u64 start, u64 length) override;
private:
impl &m_fs;
diff --git a/src/lib/formats/fsmgr.cpp b/src/lib/formats/fsmgr.cpp
index ddf9e04869e..2f762796e4c 100644
--- a/src/lib/formats/fsmgr.cpp
+++ b/src/lib/formats/fsmgr.cpp
@@ -369,17 +369,48 @@ uint32_t filesystem_t::r32l(const uint8_t *p)
return p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24);
}
-std::vector<u8> filesystem_t::ifile_t::rsrc_read_all()
+filesystem_t::file_t filesystem_t::idir_t::file_create(const fs_meta_data &info)
+{
+ fatalerror("file_create called on a filesystem not supporting write\n");
+}
+
+void filesystem_t::idir_t::file_delete(uint64_t key)
+{
+ fatalerror("file_delete called on a filesystem not supporting write\n");
+}
+
+
+void filesystem_t::ifile_t::replace(const std::vector<u8> &data)
+{
+ fatalerror("replace called on a filesystem not supporting write \n");
+}
+
+void filesystem_t::ifile_t::rsrc_replace(const std::vector<u8> &data)
{
- fatalerror("rsrc_read_all called on filesystem without resource forks\n");
+ fatalerror("rsrc_replace called on a filesystem not supporting write or resource forks \n");
}
-std::vector<u8> filesystem_t::ifile_t::rsrc_read(u64 start, u64 length)
+void filesystem_t::ifile_t::metadata_change(const fs_meta_data &info)
+{
+ fatalerror("metadata_change called on a filesystem not supporting write \n");
+}
+
+void filesystem_t::idir_t::metadata_change(const fs_meta_data &info)
+{
+ fatalerror("metadata_change called on a filesystem not supporting write \n");
+}
+
+void filesystem_t::metadata_change(const fs_meta_data &info)
+{
+ fatalerror("metadata_change called on a filesystem not supporting write \n");
+}
+
+std::vector<u8> filesystem_t::ifile_t::rsrc_read_all()
{
- fatalerror("rsrc_read called on filesystem without resource forks\n");
+ fatalerror("rsrc_read_all called on a filesystem without resource forks\n");
}
-const char *fs_meta_get_name(fs_meta_name name)
+const char *fs_meta_data::entry_name(fs_meta_name name)
{
switch(name) {
case fs_meta_name::creation_date: return "creation_date";
@@ -397,7 +428,7 @@ const char *fs_meta_get_name(fs_meta_name name)
return "";
}
-std::string fs_meta_to_string(fs_meta_type type, const fs_meta &m)
+std::string fs_meta::to_string(fs_meta_type type, const fs_meta &m)
{
switch(type) {
case fs_meta_type::string: return m.as_string();
diff --git a/src/lib/formats/fsmgr.h b/src/lib/formats/fsmgr.h
index ed3a553843e..3404f422265 100644
--- a/src/lib/formats/fsmgr.h
+++ b/src/lib/formats/fsmgr.h
@@ -43,6 +43,9 @@ enum class fs_dir_entry_type {
class fs_meta {
public:
+ static std::string to_string(fs_meta_type type, const fs_meta &m);
+ static fs_meta from_string(fs_meta_type type, std::string value);
+
fs_meta() { value = false; }
fs_meta(std::string str) { value = str; }
fs_meta(bool b) { value = b; }
@@ -61,11 +64,31 @@ private:
std::variant<std::string, uint64_t, bool, util::arbitrary_datetime> value;
};
-using fs_meta_data = std::unordered_map<fs_meta_name, fs_meta>;
-
-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);
+class fs_meta_data {
+public:
+ std::unordered_map<fs_meta_name, fs_meta> meta;
+
+ static const char *entry_name(fs_meta_name name);
+
+ bool has(fs_meta_name name) const { return meta.find(name) != meta.end(); }
+ bool empty() const { return meta.empty(); }
+
+ void set(fs_meta_name name, const fs_meta &val) { meta[name] = val; }
+ void set(fs_meta_name name, std::string str) { set(name, fs_meta(str)); }
+ void set(fs_meta_name name, bool b) { set(name, fs_meta(b)); }
+ void set(fs_meta_name name, int32_t num) { set(name, fs_meta(num)); }
+ void set(fs_meta_name name, uint32_t num) { set(name, fs_meta(num)); }
+ void set(fs_meta_name name, int64_t num) { set(name, fs_meta(num)); }
+ void set(fs_meta_name name, uint64_t num) { set(name, fs_meta(num)); }
+ void set(fs_meta_name name, util::arbitrary_datetime dt) { set(name, fs_meta(dt)); }
+ void set_now(fs_meta_name name) { set(name, fs_meta(util::arbitrary_datetime::now())); }
+
+ fs_meta get(fs_meta_name name) const { auto i = meta.find(name); if(i == meta.end()) fatalerror("Entry %s not found\n", entry_name(name)); else return i->second; }
+ util::arbitrary_datetime get_date(fs_meta_name name, util::arbitrary_datetime def = util::arbitrary_datetime::now()) const { auto i = meta.find(name); if(i == meta.end()) return def; else return i->second.as_date(); }
+ bool get_flag(fs_meta_name name, bool def = false) const { auto i = meta.find(name); if(i == meta.end()) return def; else return i->second.as_flag(); }
+ uint64_t get_number(fs_meta_name name, uint64_t def = 0) const { auto i = meta.find(name); if(i == meta.end()) return def; else return i->second.as_number(); }
+ std::string get_string(fs_meta_name name, std::string def = "") const { auto i = meta.find(name); if(i == meta.end()) return def; else return i->second.as_string(); }
+};
template<typename T> class fs_refcounted_outer {
public:
@@ -268,9 +291,12 @@ protected:
virtual ~idir_t() = default;
virtual fs_meta_data metadata() = 0;
+ virtual void metadata_change(const fs_meta_data &info);
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;
+ virtual file_t file_create(const fs_meta_data &info);
+ virtual void file_delete(uint64_t key);
};
class ifile_t : public fs_refcounted_inner {
@@ -279,10 +305,11 @@ protected:
virtual ~ifile_t() = default;
virtual fs_meta_data metadata() = 0;
+ virtual void metadata_change(const fs_meta_data &info);
virtual std::vector<u8> read_all() = 0;
- virtual std::vector<u8> read(u64 start, u64 length) = 0;
+ virtual void replace(const std::vector<u8> &data);
virtual std::vector<u8> rsrc_read_all();
- virtual std::vector<u8> rsrc_read(u64 start, u64 length);
+ virtual void rsrc_replace(const std::vector<u8> &data);
};
public:
@@ -296,9 +323,12 @@ public:
dir_t weak() { return dir_t(m_object, true); }
fs_meta_data metadata() { return m_object->metadata(); }
+ void metadata_change(const fs_meta_data &info) { m_object->metadata_change(info); }
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); }
+ file_t file_create(const fs_meta_data &info) { return m_object->file_create(info); }
+ void file_delete(uint64_t key) { m_object->file_delete(key); }
};
class file_t : public fs_refcounted_outer<ifile_t> {
@@ -311,11 +341,11 @@ public:
file_t weak() { return file_t(m_object, true); }
fs_meta_data metadata() { return m_object->metadata(); }
-
+ void metadata_change(const fs_meta_data &info) { m_object->metadata_change(info); }
std::vector<u8> read_all() { return m_object->read_all(); }
- std::vector<u8> read(u32 start, u32 length) { return m_object->read(start, length); }
+ void replace(const std::vector<u8> &data) { m_object->replace(data); }
std::vector<u8> rsrc_read_all() { return m_object->rsrc_read_all(); }
- std::vector<u8> rsrc_read(u32 start, u32 length) { return m_object->rsrc_read(start, length); }
+ void rsrc_replace(const std::vector<u8> &data) { m_object->rsrc_replace(data); }
};
filesystem_t(fsblk_t &blockdev, u32 size) : m_blockdev(blockdev) {
@@ -324,9 +354,10 @@ public:
virtual ~filesystem_t() = default;
+ virtual dir_t root();
virtual void format(const fs_meta_data &meta);
virtual fs_meta_data metadata();
- virtual dir_t root();
+ virtual void metadata_change(const fs_meta_data &info);
static void copy(uint8_t *p, const uint8_t *src, uint32_t size);
static void fill(uint8_t *p, uint8_t data, uint32_t size);
diff --git a/src/tools/floptool.cpp b/src/tools/floptool.cpp
index a3186b2374b..515ee3a87dd 100644
--- a/src/tools/floptool.cpp
+++ b/src/tools/floptool.cpp
@@ -275,6 +275,7 @@ static void display_usage()
fprintf(stderr, " floptool.exe flopcreate output_format filesystem <outputfile> -- Create a preformatted floppy image\n");
fprintf(stderr, " floptool.exe flopdir input_format filesystem <image> -- List the contents of a floppy image\n");
fprintf(stderr, " floptool.exe flopread input_format filesystem <image> <path> <outputfile> -- Extract a file from a floppy image\n");
+ fprintf(stderr, " floptool.exe flopwrite input_format filesystem <image> <inputfile> <path> -- Write a file into a floppy image\n");
}
static void display_formats()
@@ -456,11 +457,7 @@ static int create(int argc, char *argv[])
floppy_image image(84, 2, floppy_image::FF_UNKNOWN);
if(source_fs->m_type) {
- auto metav = source_fs->m_manager->volume_meta_description();
fs_meta_data meta;
- for(const auto &e : metav)
- if(!e.m_ro)
- meta[e.m_name] = e.m_default;
std::vector<u8> img(source_fs->m_image_size);
fsblk_vec_t blockdev(img);
@@ -514,10 +511,10 @@ static void dir_scan(u32 depth, filesystem_t::dir_t dir, std::vector<std::vector
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())
+ if(!meta.has(m.m_name))
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);
+ std::string val = fs_meta::to_string(m.m_type, meta.get(m.m_name));
if(slot == 0)
val = head + "dir " + val;
entries[id][slot] = val;
@@ -530,10 +527,10 @@ static void dir_scan(u32 depth, filesystem_t::dir_t dir, std::vector<std::vector
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())
+ if(!meta.has(m.m_name))
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);
+ std::string val = fs_meta::to_string(m.m_type, meta.get(m.m_name));
if(slot == 0)
val = head + (c.m_type == fs_dir_entry_type::system_file ? "sys " : "file ") + val;
entries[id][slot] = val;
@@ -555,7 +552,7 @@ static int generic_dir(const filesystem_manager_t *fm, fsblk_t &blockdev)
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]));
+ vinf += util::string_format(" %s=%s", fs_meta_data::entry_name(e.m_name), fs_meta::to_string(e.m_type, vmeta.get(e.m_name)));
printf("%s\n\n", vinf.c_str());
}
@@ -577,7 +574,7 @@ static int generic_dir(const filesystem_manager_t *fm, fsblk_t &blockdev)
entries.resize(1);
for(fs_meta_name n : names)
- entries[0].push_back(fs_meta_get_name(n));
+ entries[0].push_back(fs_meta_data::entry_name(n));
dir_scan(0, root, entries, nmap, names.size(), dmetad, fmetad);
@@ -694,31 +691,93 @@ static int hddir(int argc, char *argv[])
}
-
-
-static int generic_read(const filesystem_manager_t *fm, fsblk_t &blockdev, const char *srcpath, const char *dstpath)
+static std::vector<std::string> path_split(const filesystem_manager_t *fm, std::string path)
{
- auto load_fs = fm->mount(blockdev);
-
- std::string opath = srcpath;
- std::vector<std::string> path;
+ std::string opath = path;
+ std::vector<std::string> rpath;
if(fm->has_subdirectories()) {
std::string element;
char sep = fm->directory_separator();
for(char c : opath) {
if(c == sep) {
if(!element.empty()) {
- path.push_back(element);
+ rpath.push_back(element);
element.clear();
}
} else
element += c;
}
if(!element.empty())
- path.push_back(element);
+ rpath.push_back(element);
} else
- path.push_back(opath);
+ rpath.push_back(opath);
+
+ return rpath;
+}
+
+static std::vector<u8> fload(std::string path)
+{
+ char msg[4096];
+ sprintf(msg, "Error opening %s for reading", path.c_str());
+ auto fi = fopen(path.c_str(), "rb");
+ if (!fi) {
+ perror(msg);
+ exit(1);
+ }
+ fseek(fi, 0, SEEK_END);
+ long size = ftell(fi);
+ std::vector<u8> filedata(size);
+ fseek(fi, 0, SEEK_SET);
+ fread(filedata.data(), filedata.size(), 1, fi);
+ fclose(fi);
+
+ return filedata;
+}
+
+static void fsave(std::string path, const std::vector<u8> &data)
+{
+ char msg[4096];
+ sprintf(msg, "Error opening %s for writing", path.c_str());
+ auto fo = fopen(path.c_str(), "wb");
+ if (!fo) {
+ perror(msg);
+ exit(1);
+ }
+
+ fwrite(data.data(), data.size(), 1, fo);
+ fclose(fo);
+}
+
+static bool fexists(std::string path)
+{
+ auto f = fopen(path.c_str(), "rb");
+ if(f != nullptr) {
+ fclose(f);
+ return true;
+ }
+ return false;
+}
+
+
+static std::string path_make_rsrc(std::string path)
+{
+ auto p = path.end();
+ while(p != path.begin() && p[-1] != '/')
+ p--;
+ std::string rpath(path.begin(), p);
+ rpath += "._";
+ rpath += std::string(p, path.end());
+ return rpath;
+}
+
+
+
+static int generic_read(const filesystem_manager_t *fm, fsblk_t &blockdev, const char *srcpath, const char *dstpath)
+{
+ auto load_fs = fm->mount(blockdev);
+
+ std::vector<std::string> path = path_split(fm, srcpath);
auto dir = load_fs->root();
std::string apath;
@@ -752,43 +811,22 @@ static int generic_read(const filesystem_manager_t *fm, fsblk_t &blockdev, const
auto file = dir.file_get(c[j].m_key);
auto meta = file.metadata();
- if(meta.find(fs_meta_name::length) == meta.end()) {
+ if(!meta.has(fs_meta_name::length)) {
fprintf(stderr, "Error: %s%c%s is not a readable file\n", apath.c_str(), fm->directory_separator(), path.back().c_str());
return 1;
}
- auto filedata = file.read_all();
-
- char msg[4096];
- sprintf(msg, "Error opening %s for writing", dstpath);
- auto fo = fopen(dstpath, "wb");
- if (!fo) {
- perror(msg);
- return 1;
- }
-
- fwrite(filedata.data(), filedata.size(), 1, fo);
- fclose(fo);
+ fsave(dstpath, file.read_all());
- bool has_rsrc = fm->has_rsrc() && meta.find(fs_meta_name::rsrc_length) != meta.end();
+ bool has_rsrc = fm->has_rsrc() && meta.has(fs_meta_name::rsrc_length);
if(has_rsrc) {
- const char *d = dstpath + strlen(dstpath);
- while(d != dstpath && d[-1] != '/')
- d--;
- std::string dpath(dstpath, d);
- dpath += "._";
- dpath += d;
-
- sprintf(msg, "Error opening %s for writing", dstpath);
- auto fo = fopen(dpath.c_str(), "wb");
- if (!fo) {
- perror(msg);
- return 1;
- }
+ std::string rpath = path_make_rsrc(dstpath);
+
+ auto filedata = file.rsrc_read_all();
+ filedata.insert(filedata.begin(), 0x2a, 0);
- filedata = file.rsrc_read_all();
- u8 head[0x2a];
+ u8 *head = filedata.data();
filesystem_t::w32b(head+0x00, 0x00051607); // Magic
filesystem_t::w32b(head+0x04, 0x00020000); // Version
filesystem_t::fill(head+0x08, 0, 16); // Filler
@@ -797,9 +835,7 @@ static int generic_read(const filesystem_manager_t *fm, fsblk_t &blockdev, const
filesystem_t::w32b(head+0x22, 0x2a); // Offset in the file
filesystem_t::w32b(head+0x26, filedata.size()); // Length
- fwrite(head, 0x2a, 1, fo);
- fwrite(filedata.data(), filedata.size(), 1, fo);
- fclose(fo);
+ fsave(rpath, filedata);
}
return 0;
@@ -901,6 +937,189 @@ static int hdread(int argc, char *argv[])
}
+
+static int generic_write(const filesystem_manager_t *fm, fsblk_t &blockdev, const char *srcpath, const char *dstpath)
+{
+ auto load_fs = fm->mount(blockdev);
+
+ std::vector<std::string> path = path_split(fm, dstpath);
+
+ 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(), fm->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(), fm->directory_separator(), path[i].c_str());
+ return 1;
+ }
+ dir = dir.dir_get(c[j].m_key);
+ apath += fm->directory_separator() + path[i];
+ }
+
+
+ fs_meta_data meta;
+ meta.set(fs_meta_name::name, path.back());
+
+ auto file = dir.file_create(meta);
+ auto filedata = fload(srcpath);
+ file.replace(filedata);
+
+ bool has_rsrc = fm->has_rsrc();
+
+ if(has_rsrc) {
+ std::string rpath = path_make_rsrc(dstpath);
+
+ if(fexists(rpath)) {
+ filedata = fload(rpath);
+ const u8 *head = filedata.data();
+
+ if(filesystem_t::r32b(head+0x00) == 0x00051607 &&
+ filesystem_t::r32b(head+0x04) == 0x00020000) {
+ u16 nent = filesystem_t::r16b(head+0x18);
+ for(u16 i=0; i != nent; i++) {
+ const u8 *e = head + 12*i;
+ if(filesystem_t::r32b(e+0) == 2) {
+ u32 start = filesystem_t::r32b(e+4);
+ u32 len = filesystem_t::r32b(e+8);
+ filedata.erase(filedata.begin(), filedata.begin() + start);
+ filedata.erase(filedata.begin() + len, filedata.end());
+ file.rsrc_replace(filedata);
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ return 0;
+}
+
+
+static int flopwrite(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[2]);
+ return 1;
+ }
+
+ auto fs = find_fs_by_name(argv[3]);
+ if(!fs) {
+ fprintf(stderr, "Error: Filesystem '%s' unknown\n", argv[3]);
+ 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);
+
+ fsblk_vec_t blockdev(img);
+ generic_write(fs->m_manager, blockdev, argv[5], argv[6]);
+
+ load_format->load(iog, image.get_form_factor(), variants, &image);
+ delete load_format;
+ delete iog;
+
+ sprintf(msg, "Error oapening %s for writing", argv[4]);
+ f = fopen(argv[4], "wb");
+ if (!f) {
+ perror(msg);
+ return 1;
+ }
+
+ io_generic dest_io;
+ dest_io.file = f;
+ dest_io.procs = &stdio_ioprocs_noclose;
+ dest_io.filler = 0xff;
+
+ if(!format->save(&dest_io, variants, &image)) {
+ fprintf(stderr, "Error: writing output file as '%s' failed\n", format->name());
+ return 1;
+ }
+
+ fclose((FILE *)dest_io.file);
+ return 0;
+}
+
+
+// Should use chd&friends instead, but one thing at a time
+
+static int hdwrite(int argc, char *argv[])
+{
+ if (argc!=6) {
+ fprintf(stderr, "Incorrect number of arguments.\n\n");
+ display_usage();
+ return 1;
+ }
+
+ auto fs = find_fs_by_name(argv[2]);
+ 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[3]);
+ FILE *f = fopen(argv[3], "rb");
+ if (!f) {
+ perror(msg);
+ return 1;
+ }
+ fseek(f, 0, SEEK_END);
+ size_t size = ftell(f);
+ rewind(f);
+ std::vector<u8> img(size);
+ fread(img.data(), size, 1, f);
+ fclose(f);
+
+ fsblk_vec_t blockdev(img);
+ return generic_write(fs->m_manager, blockdev, argv[4], argv[5]);
+}
+
+
int CLIB_DECL main(int argc, char *argv[])
{
init_formats();
@@ -915,16 +1134,20 @@ int CLIB_DECL main(int argc, char *argv[])
return identify(argc, argv);
else if (!core_stricmp("convert", argv[1]))
return convert(argc, argv);
- else if (!core_stricmp("create", argv[1]))
+ else if (!core_stricmp("flopcreate", argv[1]))
return create(argc, argv);
else if (!core_stricmp("flopdir", argv[1]))
return flopdir(argc, argv);
else if (!core_stricmp("flopread", argv[1]))
return flopread(argc, argv);
+ else if (!core_stricmp("flopwrite", argv[1]))
+ return flopwrite(argc, argv);
else if (!core_stricmp("hddir", argv[1]))
return hddir(argc, argv);
else if (!core_stricmp("hdread", argv[1]))
return hdread(argc, argv);
+ else if (!core_stricmp("hdwrite", argv[1]))
+ return hdwrite(argc, argv);
else {
fprintf(stderr, "Unknown command '%s'\n\n", argv[1]);
display_usage();