From da1bd3b0ec0a1f5f5999cd8a98de0e8d76dca48b Mon Sep 17 00:00:00 2001 From: Olivier Galibert Date: Sun, 23 May 2021 19:11:45 +0200 Subject: reorganize the floptool code, add some write support --- scripts/src/tools.lua | 2 + src/devices/imagedev/floppy.cpp | 22 +- src/devices/imagedev/floppy.h | 10 +- src/lib/formats/all.h | 2 +- src/lib/formats/fs_oric_jasmin.cpp | 20 +- src/lib/formats/fs_oric_jasmin.h | 5 +- src/lib/formats/fs_prodos.cpp | 179 +++---- src/lib/formats/fs_prodos.h | 35 +- src/lib/formats/fs_unformatted.cpp | 14 +- src/lib/formats/fs_unformatted.h | 5 +- src/lib/formats/fsmgr.h | 15 +- src/tools/floptool.cpp | 953 ++++++++++--------------------------- src/tools/image_handler.cpp | 481 +++++++++++++++++++ src/tools/image_handler.h | 121 +++++ 14 files changed, 1014 insertions(+), 850 deletions(-) create mode 100644 src/tools/image_handler.cpp create mode 100644 src/tools/image_handler.h diff --git a/scripts/src/tools.lua b/scripts/src/tools.lua index b3fb64c30c0..1f1e4215ce8 100644 --- a/scripts/src/tools.lua +++ b/scripts/src/tools.lua @@ -609,6 +609,8 @@ includedirs { } files { + MAME_DIR .. "src/tools/image_handler.cpp", + MAME_DIR .. "src/tools/image_handler.h", MAME_DIR .. "src/tools/floptool.cpp", } diff --git a/src/devices/imagedev/floppy.cpp b/src/devices/imagedev/floppy.cpp index a0b891220ce..c73b7c87cb0 100644 --- a/src/devices/imagedev/floppy.cpp +++ b/src/devices/imagedev/floppy.cpp @@ -177,9 +177,9 @@ void format_registration::add(floppy_format_type format) m_formats.push_back(format); } -void format_registration::add(filesystem_manager_type fs) +void format_registration::add(const filesystem_manager_t &fs) { - m_fs.push_back(fs); + m_fs.push_back(&fs); } void floppy_image_device::default_fm_floppy_formats(format_registration &fr) @@ -311,12 +311,12 @@ void floppy_image_device::setup_led_cb(led_cb cb) cur_led_cb = cb; } -void floppy_image_device::fs_enum::add(const filesystem_manager_t *manager, floppy_format_type type, u32 image_size, const char *name, const char *description) +void floppy_image_device::fs_enum::add(floppy_format_type type, u32 image_size, const char *name, const char *description) { - if(manager->can_format()) - m_fid->m_create_fs.emplace_back(fs_info(manager, type, image_size, name, description)); - if(manager->can_read()) - m_fid->m_io_fs.emplace_back(fs_info(manager, type, image_size, name, description)); + if(m_manager->can_format()) + m_fid->m_create_fs.emplace_back(fs_info(m_manager, type, image_size, name, description)); + if(m_manager->can_read()) + m_fid->m_io_fs.emplace_back(fs_info(m_manager, type, image_size, name, description)); } void floppy_image_device::fs_enum::add_raw(const char *name, u32 key, const char *description) @@ -347,11 +347,11 @@ void floppy_image_device::register_formats() } fs_enum fse(this); - for(filesystem_manager_type fmt : fr.m_fs) + for(const filesystem_manager_t *fmt : fr.m_fs) { - auto ff = fmt(); - ff->enumerate_f(fse, form_factor, variants); - m_fs_managers.push_back(std::unique_ptr(ff)); + fse.m_manager = fmt; + fmt->enumerate_f(fse, form_factor, variants); + m_fs_managers.push_back(fmt); } } diff --git a/src/devices/imagedev/floppy.h b/src/devices/imagedev/floppy.h index c9fdc220ad3..ea3eea381cc 100644 --- a/src/devices/imagedev/floppy.h +++ b/src/devices/imagedev/floppy.h @@ -27,14 +27,14 @@ public: format_registration(); void add(floppy_format_type format); - void add(filesystem_manager_type fs); + void add(const filesystem_manager_t &fs); void add_fm_containers(); void add_mfm_containers(); void add_pc_formats(); std::vector m_formats; - std::vector m_fs; + std::vector m_fs; }; class floppy_image_device : public device_t, @@ -157,9 +157,11 @@ public: protected: struct fs_enum : public filesystem_manager_t::floppy_enumerator { floppy_image_device *m_fid; + const filesystem_manager_t *m_manager; + fs_enum(floppy_image_device *fid) : filesystem_manager_t::floppy_enumerator(), m_fid(fid) {}; - virtual void add(const filesystem_manager_t *manager, floppy_format_type type, u32 image_size, const char *name, const char *description) override; + virtual void add(floppy_format_type type, u32 image_size, const char *name, const char *description) override; virtual void add_raw(const char *name, u32 key, const char *description) override; }; @@ -188,7 +190,7 @@ protected: char extension_list[256]; floppy_image_format_t *fif_list; std::vector m_create_fs, m_io_fs; - std::vector> m_fs_managers; + std::vector m_fs_managers; emu_timer *index_timer; /* Physical characteristics, filled by setup_characteristics */ diff --git a/src/lib/formats/all.h b/src/lib/formats/all.h index 63de5560949..1284bf9f509 100644 --- a/src/lib/formats/all.h +++ b/src/lib/formats/all.h @@ -18,7 +18,7 @@ struct mame_formats_enumerator { virtual void category(const char *name) = 0; virtual void add(const cassette_image::Format *const *formats) = 0; virtual void add(floppy_format_type format) = 0; - virtual void add(filesystem_manager_type fs) = 0; + virtual void add(const filesystem_manager_t &fs) = 0; }; void mame_formats_full_list(mame_formats_enumerator &en); diff --git a/src/lib/formats/fs_oric_jasmin.cpp b/src/lib/formats/fs_oric_jasmin.cpp index 0b6740c49c3..dadf76026a7 100644 --- a/src/lib/formats/fs_oric_jasmin.cpp +++ b/src/lib/formats/fs_oric_jasmin.cpp @@ -7,6 +7,8 @@ #include "fs_oric_jasmin.h" #include "oric_dsk.h" +const fs_oric_jasmin FS_ORIC_JASMIN; + // Floppy only, format is 41 tracks, 1/2 heads, 17 sectors. // Filesystem has no subdirectories. // @@ -39,12 +41,22 @@ // offset 04-05: length of the file in bytes on the first sector, ffff otherwise // offset 06+ : reference to data sectors, (ff, ff) when done +const char *fs_oric_jasmin::name() const +{ + return "oric_jasmin"; +} + +const char *fs_oric_jasmin::description() const +{ + return "Oric Jasmin"; +} + void fs_oric_jasmin::enumerate_f(floppy_enumerator &fe, uint32_t form_factor, const std::vector &variants) const { if(has(form_factor, variants, floppy_image::FF_3, floppy_image::DSDD)) - fe.add(this, FLOPPY_ORIC_JASMIN_FORMAT, 356864, "oric_jasmin_ds", "Oric Jasmin dual-sided"); + fe.add(FLOPPY_ORIC_JASMIN_FORMAT, 356864, "oric_jasmin_ds", "Oric Jasmin dual-sided"); if(has(form_factor, variants, floppy_image::FF_3, floppy_image::SSDD)) - fe.add(this, FLOPPY_ORIC_JASMIN_FORMAT, 178432, "oric_jasmin_ss", "Oric Jasmin single-sided"); + fe.add(FLOPPY_ORIC_JASMIN_FORMAT, 178432, "oric_jasmin_ss", "Oric Jasmin single-sided"); } std::unique_ptr fs_oric_jasmin::mount(fsblk_t &blockdev) const @@ -609,7 +621,3 @@ u32 fs_oric_jasmin::impl::free_block_count() } return nf; } - - - -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 304fbf1eb05..2ffe27fb7cf 100644 --- a/src/lib/formats/fs_oric_jasmin.h +++ b/src/lib/formats/fs_oric_jasmin.h @@ -102,6 +102,9 @@ public: fs_oric_jasmin() : filesystem_manager_t() {} + virtual const char *name() const override; + virtual const char *description() const override; + virtual void enumerate_f(floppy_enumerator &fe, uint32_t form_factor, const std::vector &variants) const override; virtual std::unique_ptr mount(fsblk_t &blockdev) const override; @@ -116,6 +119,6 @@ public: static bool validate_filename(std::string name); }; -extern const filesystem_manager_type FS_ORIC_JASMIN; +extern const fs_oric_jasmin FS_ORIC_JASMIN; #endif diff --git a/src/lib/formats/fs_prodos.cpp b/src/lib/formats/fs_prodos.cpp index a79c02fb800..a5a6b11eea3 100644 --- a/src/lib/formats/fs_prodos.cpp +++ b/src/lib/formats/fs_prodos.cpp @@ -7,6 +7,19 @@ #include "fs_prodos.h" #include "ap_dsk35.h" + +const fs_prodos FS_PRODOS; + +const char *fs_prodos::name() const +{ + return "prodos"; +} + +const char *fs_prodos::description() const +{ + return "Apple ProDOS"; +} + const u8 fs_prodos::impl::boot[512] = { 0x01, 0x38, 0xb0, 0x03, 0x4c, 0x1c, 0x09, 0x78, 0x86, 0x43, 0xc9, 0x03, 0x08, 0x8a, 0x29, 0x70, 0x4a, 0x4a, 0x4a, 0x4a, 0x09, 0xc0, 0x85, 0x49, 0xa0, 0xff, 0x84, 0x48, 0x28, 0xc8, 0xb1, 0x48, @@ -45,9 +58,9 @@ const u8 fs_prodos::impl::boot[512] = { void fs_prodos::enumerate_f(floppy_enumerator &fe, uint32_t form_factor, const std::vector &variants) const { if(has(form_factor, variants, floppy_image::FF_35, floppy_image::DSDD)) - fe.add(this, FLOPPY_APPLE_GCR_FORMAT, 819200, "prodos_800k", "Apple ProDOS 800K"); + fe.add(FLOPPY_APPLE_GCR_FORMAT, 819200, "prodos_800k", "Apple ProDOS 800K"); if(has(form_factor, variants, floppy_image::FF_35, floppy_image::SSDD)) - fe.add(this, FLOPPY_APPLE_GCR_FORMAT, 409600, "prodos_400k", "Apple ProDOS 400K"); + fe.add(FLOPPY_APPLE_GCR_FORMAT, 409600, "prodos_400k", "Apple ProDOS 400K"); } std::unique_ptr fs_prodos::mount(fsblk_t &blockdev) const @@ -222,7 +235,7 @@ fs_meta_data fs_prodos::impl::metadata() filesystem_t::dir_t fs_prodos::impl::root() { if(!m_root) - m_root = new dir(*this, 2); + m_root = new root_dir(*this, 2); return m_root.strong(); } @@ -232,29 +245,18 @@ void fs_prodos::impl::drop_root_ref() } -void fs_prodos::impl::dir::drop_weak_references() +void fs_prodos::impl::root_dir::drop_weak_references() { if(m_base_block == 2) m_fs.drop_root_ref(); } -fs_meta_data fs_prodos::impl::dir::metadata() +fs_meta_data fs_prodos::impl::root_dir::metadata() { - fs_meta_data res; - if(m_base_block == 2) - return res; - - auto bdir = m_fs.m_blockdev.get(m_base_block); - int len = bdir.r8(0x04) & 0xf; - res.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; + return fs_meta_data(); } -std::vector fs_prodos::impl::dir::contents() +std::vector fs_prodos::impl::root_dir::contents() { std::vector res; @@ -282,7 +284,8 @@ std::vector fs_prodos::impl::dir::contents() return res; } -std::pair fs_prodos::impl::dir::get_entry_ro(uint64_t key) + +std::pair fs_prodos::impl::root_dir::get_entry_ro(uint64_t key) { std::pair res; res.first = m_fs.m_blockdev.get(m_base_block); @@ -300,7 +303,7 @@ std::pair fs_prodos::impl::dir::get_entry_ro(uint6 return res; } -std::pair fs_prodos::impl::dir::get_entry(uint64_t key) +std::pair fs_prodos::impl::root_dir::get_entry(uint64_t key) { std::pair res; res.first = m_fs.m_blockdev.get(m_base_block); @@ -318,7 +321,7 @@ std::pair fs_prodos::impl::dir::get_entry(uint64_t key) return res; } -filesystem_t::file_t fs_prodos::impl::dir::file_get(uint64_t key) +filesystem_t::file_t fs_prodos::impl::root_dir::file_get(uint64_t key) { auto [blk, entry] = get_entry_ro(key); if(!blk) @@ -326,10 +329,10 @@ filesystem_t::file_t fs_prodos::impl::dir::file_get(uint64_t key) u8 type = entry[0] >> 4; if(type == 0 || type == 4 || type > 5) fatalerror("Unhandled file type %x\n", type); - return new file(m_fs, entry, key); + return new file(m_fs, entry, key, this); } -filesystem_t::dir_t fs_prodos::impl::dir::dir_get(uint64_t key) +filesystem_t::dir_t fs_prodos::impl::root_dir::dir_get(uint64_t key) { auto [blk, entry] = get_entry_ro(key); if(!blk) @@ -338,13 +341,21 @@ filesystem_t::dir_t fs_prodos::impl::dir::dir_get(uint64_t key) if(type != 0xd) fatalerror("Unhandled directory type %x\n", type); - return new dir(m_fs, r16l(entry+0x11), key); + return new dir(m_fs, entry, r16l(entry+0x11), key, this); } -fs_prodos::impl::file::file(impl &fs, const u8 *entry, u16 key) : m_fs(fs), m_key(key) +fs_prodos::impl::dir::dir(impl &fs, const u8 *entry, u16 base_block, u16 key, root_dir *parent_dir) : root_dir(fs, base_block), m_parent_dir(parent_dir), m_key(key) +{ + memcpy(m_entry, entry, 39); + (void)m_key; + (void)m_parent_dir; +} + +fs_prodos::impl::file::file(impl &fs, const u8 *entry, u16 key, root_dir *parent_dir) : m_fs(fs), m_parent_dir(parent_dir), m_key(key) { memcpy(m_entry, entry, 39); (void)m_key; + (void)m_parent_dir; } void fs_prodos::impl::file::drop_weak_references() @@ -372,32 +383,51 @@ fs_meta_data fs_prodos::impl::file::metadata() return res; } -std::vector fs_prodos::impl::file::get_file_blocks(uint8_t type, u16 block, u32 length) +fs_meta_data fs_prodos::impl::dir::metadata() { - u32 nb = (length+1)/512; - std::vector res; + fs_meta_data res; + u8 type = r8(m_entry); + std::string name = rstr(m_entry+1, type & 0xf); + res.set(fs_meta_name::name, name); + + return res; +} + +std::vector fs_prodos::impl::file::any_read_all(uint8_t type, u16 block, u32 length) +{ + std::vector data((length + 511) & ~511); + u32 nb = data.size()/512; + if(!nb) + return data; + + u8 *dst = data.data(); + u8 *end = dst + data.size(); switch(type) { case 1: - if(nb) - res.push_back(block); + memcpy(dst, m_fs.m_blockdev.get(block).rodata(), 512); + dst += 512; break; case 2: { auto iblk = m_fs.m_blockdev.get(block); - if(nb > 255) - nb = 255; - for(u32 i=0; i != nb; i++) - res.push_back(iblk.r8(i) | (iblk.r8(i | 0x100) << 8)); + for(u32 i=0; i != 256 && dst != end; i++) { + u16 blk = iblk.r8(i) | (iblk.r8(i | 0x100) << 8); + memcpy(dst, m_fs.m_blockdev.get(blk).rodata(), 512); + dst += 512; + } break; } case 3: { auto mblk = m_fs.m_blockdev.get(block); - for(u32 j=0; j < nb; j += 256) { + for(u32 j=0; dst != end; j += 256) { u32 idx = j/256; auto iblk = m_fs.m_blockdev.get(mblk.r8(idx) | (mblk.r8(idx | 0x100) << 8)); - for(u32 i=0; i != 256 && res.size() != nb; i++) - res.push_back(iblk.r8(i) | (iblk.r8(i | 0x100) << 8)); + for(u32 i=0; i != 256 && dst != end; i++) { + u16 blk = iblk.r8(i) | (iblk.r8(i | 0x100) << 8); + memcpy(dst, m_fs.m_blockdev.get(blk).rodata(), 512); + dst += 512; + } } break; } @@ -405,86 +435,33 @@ std::vector fs_prodos::impl::file::get_file_blocks(uint8_t type, u16 b default: fatalerror("fs_prodos::impl::file::get_file_blocks: unknown file type %d\n", type); } - return res; + + data.resize(length); + return data; } -std::pair, u32> fs_prodos::impl::file::data_blocks() +std::vector fs_prodos::impl::file::read_all() { - std::vector blocks; - u8 type = r8(m_entry) >> 4; - u32 length = 0; - if(type >= 1 && type <= 3) { - length = r24l(m_entry + 0x15); - blocks = get_file_blocks(type, r16l(m_entry+0x11), length); + if(type >= 1 && type <= 3) + return any_read_all(type, r16l(m_entry+0x11), r24l(m_entry + 0x15)); - } else if(type == 5) { + else if(type == 5) { auto kblk = m_fs.m_blockdev.get(r16l(m_entry+0x11)); - length = kblk.r24l(0x005); - blocks = get_file_blocks(kblk.r8(0x000), kblk.r16l(0x001), length); + return any_read_all(kblk.r8(0x000), kblk.r16l(0x001), kblk.r24l(0x005)); } else - fatalerror("fs_prodos::impl::file::data_blocks: Unhandled file type %d\n", type); - - return std::make_pair(blocks, length); + fatalerror("fs_prodos::impl::file::read_all: Unhandled file type %d\n", type); } -std::pair, u32> fs_prodos::impl::file::rsrc_blocks() +std::vector fs_prodos::impl::file::rsrc_read_all() { - std::vector blocks; - u8 type = r8(m_entry) >> 4; - u32 length = 0; if(type == 5) { auto kblk = m_fs.m_blockdev.get(r16l(m_entry+0x11)); - length = kblk.r24l(0x105); - blocks = get_file_blocks(kblk.r8(0x100), kblk.r16l(0x101), length); + return any_read_all(kblk.r8(0x100), kblk.r16l(0x101), kblk.r24l(0x105)); } else fatalerror("fs_prodos::impl::file::rsrc_blocks: Unhandled file type %d\n", type); - - return std::make_pair(blocks, length); } - -std::vector fs_prodos::impl::file::read_all() -{ - auto [blocks, length] = data_blocks(); - - std::vector data(length); - u32 pos = 0; - for(u16 block : blocks) { - u32 npos = pos + 512; - if(npos > length) - npos = length; - if(npos > pos) { - auto dblk = m_fs.m_blockdev.get(block); - memcpy(data.data() + pos, dblk.rodata(), npos - pos); - } else - break; - pos = npos; - } - return data; -} - -std::vector fs_prodos::impl::file::rsrc_read_all() -{ - auto [blocks, length] = rsrc_blocks(); - - std::vector data(length); - u32 pos = 0; - for(u16 block : blocks) { - u32 npos = pos + 512; - if(npos > length) - npos = length; - if(npos > pos) { - auto dblk = m_fs.m_blockdev.get(block); - memcpy(data.data() + pos, dblk.rodata(), npos - pos); - } else - break; - pos = npos; - } - return data; -} - -const filesystem_manager_type FS_PRODOS = &filesystem_manager_creator;; diff --git a/src/lib/formats/fs_prodos.h b/src/lib/formats/fs_prodos.h index 626dcbea821..1f5d2405b98 100644 --- a/src/lib/formats/fs_prodos.h +++ b/src/lib/formats/fs_prodos.h @@ -14,10 +14,10 @@ class fs_prodos : public filesystem_manager_t { public: class impl : public filesystem_t { public: - class dir : public idir_t { + class root_dir : public idir_t { public: - dir(impl &fs, u16 base_block, u16 key = 0) : m_fs(fs), m_base_block(base_block), m_key(key) { (void)m_key; } - virtual ~dir() = default; + root_dir(impl &fs, u16 base_block) : m_fs(fs), m_base_block(base_block) { } + virtual ~root_dir() = default; virtual void drop_weak_references() override; @@ -26,18 +26,30 @@ public: virtual file_t file_get(uint64_t key) override; virtual dir_t dir_get(uint64_t key) override; - private: + protected: impl &m_fs; u16 m_base_block; - u16 m_key; std::pair get_entry_ro(uint64_t key); std::pair get_entry(uint64_t key); }; + class dir : public root_dir { + public: + dir(impl &fs, const u8 *entry, u16 base_block, u16 key, root_dir *parent_dir); + virtual ~dir() = default; + + virtual fs_meta_data metadata() override; + + protected: + root_dir *m_parent_dir; + u16 m_key; + u8 m_entry[39]; + }; + class file : public ifile_t { public: - file(impl &fs, const u8 *entry, u16 key); + file(impl &fs, const u8 *entry, u16 key, root_dir *parent_dir); virtual ~file() = default; virtual void drop_weak_references() override; @@ -48,12 +60,11 @@ public: private: impl &m_fs; + root_dir *m_parent_dir; u16 m_key; u8 m_entry[39]; - std::vector get_file_blocks(uint8_t type, u16 block, u32 length); - std::pair, uint32_t> data_blocks(); - std::pair, uint32_t> rsrc_blocks(); + std::vector any_read_all(uint8_t type, u16 block, u32 length); }; impl(fsblk_t &blockdev); @@ -67,6 +78,7 @@ public: void drop_root_ref(); static util::arbitrary_datetime prodos_to_dt(u32 date); + std::vector contents(u16 block); private: static const u8 boot[512]; @@ -76,6 +88,9 @@ public: fs_prodos() : filesystem_manager_t() {} + virtual const char *name() const override; + virtual const char *description() const override; + virtual void enumerate_f(floppy_enumerator &fe, uint32_t form_factor, const std::vector &variants) const override; virtual std::unique_ptr mount(fsblk_t &blockdev) const override; @@ -90,6 +105,6 @@ public: virtual std::vector directory_meta_description() const override; }; -extern const filesystem_manager_type FS_PRODOS; +extern const fs_prodos FS_PRODOS; #endif diff --git a/src/lib/formats/fs_unformatted.cpp b/src/lib/formats/fs_unformatted.cpp index 2dc927b70fd..81df9ecae69 100644 --- a/src/lib/formats/fs_unformatted.cpp +++ b/src/lib/formats/fs_unformatted.cpp @@ -6,6 +6,18 @@ #include "emu.h" #include "fs_unformatted.h" +const fs_unformatted FS_UNFORMATTED; + +const char *fs_unformatted::name() const +{ + return "unformatted"; +} + +const char *fs_unformatted::description() const +{ + return "Unformatted floppy image"; +} + void fs_unformatted::enumerate_f(floppy_enumerator &fe, uint32_t form_factor, const std::vector &variants) const { bool all = form_factor == floppy_image::FF_UNKNOWN; @@ -120,5 +132,3 @@ bool fs_unformatted::has_rsrc() 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 fe5ac68973d..d3c741013a5 100644 --- a/src/lib/formats/fs_unformatted.h +++ b/src/lib/formats/fs_unformatted.h @@ -38,6 +38,9 @@ public: fs_unformatted() : filesystem_manager_t() {} + virtual const char *name() const override; + virtual const char *description() const override; + static void format(u32 key, floppy_image *image); virtual void enumerate_f(floppy_enumerator &fe, uint32_t form_factor, const std::vector &variants) const override; @@ -49,6 +52,6 @@ public: virtual bool has_rsrc() const override; }; -extern const filesystem_manager_type FS_UNFORMATTED; +extern const fs_unformatted FS_UNFORMATTED; #endif diff --git a/src/lib/formats/fsmgr.h b/src/lib/formats/fsmgr.h index 4546e83d89b..8ae9ddeb452 100644 --- a/src/lib/formats/fsmgr.h +++ b/src/lib/formats/fsmgr.h @@ -391,7 +391,7 @@ public: struct floppy_enumerator { virtual ~floppy_enumerator() = default; - virtual void add(const filesystem_manager_t *manager, floppy_format_type type, uint32_t image_size, const char *name, const char *description) = 0; + virtual void add(floppy_format_type type, uint32_t image_size, const char *name, const char *description) = 0; virtual void add_raw(const char *name, uint32_t key, const char *description) = 0; }; @@ -410,6 +410,9 @@ public: virtual ~filesystem_manager_t() = default; + virtual const char *name() const = 0; + virtual const char *description() const = 0; + virtual void enumerate_f(floppy_enumerator &fe, uint32_t form_factor, const std::vector &variants) const; virtual void enumerate_h(hd_enumerator &he) const; virtual void enumerate_c(cdrom_enumerator &ce) const; @@ -436,14 +439,4 @@ protected: static bool has_variant(const std::vector &variants, uint32_t variant); }; - -typedef filesystem_manager_t *(*filesystem_manager_type)(); - -// this template function creates a stub which constructs a filesystem manager -template -filesystem_manager_t *filesystem_manager_creator() -{ - return new _FormatClass(); -} - #endif diff --git a/src/tools/floptool.cpp b/src/tools/floptool.cpp index bff08afb600..7cd7c2ad7f0 100644 --- a/src/tools/floptool.cpp +++ b/src/tools/floptool.cpp @@ -15,263 +15,16 @@ #include #include -#include "../emu/emucore.h" +#include "image_handler.h" #include "corestr.h" -#include "osdcomm.h" - -using u8 = uint8_t; -using u16 = uint16_t; -using u32 = uint32_t; - -#include "formats/all.h" -#include "formats/fs_unformatted.h" -#include "formats/fsblk_vec.h" - -emu_fatalerror::emu_fatalerror(util::format_argument_pack const &args) - : emu_fatalerror(0, args) -{ -} - -emu_fatalerror::emu_fatalerror(int _exitcode, util::format_argument_pack const &args) - : m_text(util::string_format(args)) - , m_code(_exitcode) -{ -} - -struct fs_info { - const filesystem_manager_t *m_manager; - floppy_format_type m_type; - u32 m_image_size; - const char *m_name; - u32 m_key; - const char *m_description; - - fs_info(const filesystem_manager_t *manager, floppy_format_type type, u32 image_size, const char *name, const char *description) : - m_manager(manager), - m_type(type), - m_image_size(image_size), - m_name(name), - m_key(0), - m_description(description) - {} - - fs_info(const char *name, u32 key, const char *description) : - m_manager(nullptr), - m_type(nullptr), - m_image_size(0), - m_name(name), - m_key(key), - m_description(description) - {} -}; - -struct iofile_ram { - std::vector *data; - int64_t pos; -}; - -static void ram_closeproc(void *file) -{ - auto f = (iofile_ram *)file; - delete f; -} - -static int ram_seekproc(void *file, int64_t offset, int whence) -{ - auto f = (iofile_ram *)file; - switch(whence) { - case SEEK_SET: f->pos = offset; break; - case SEEK_CUR: f->pos += offset; break; - case SEEK_END: f->pos = f->data->size() + offset; break; - } - - if(whence == SEEK_CUR) - f->pos = std::max(f->pos, 0); - else - f->pos = std::clamp(f->pos, 0, f->data->size()); - return 0; -} - -static size_t ram_readproc(void *file, void *buffer, size_t length) -{ - auto f = (iofile_ram *)file; - size_t l = std::min >(length, f->data->size() - f->pos); - memcpy(buffer, f->data->data() + f->pos, l); - return l; -} - -static size_t ram_writeproc(void *file, const void *buffer, size_t length) -{ - auto f = (iofile_ram *)file; - size_t l = std::max >(f->pos + length, f->data->size()); - f->data->resize(l); - memcpy(f->data->data() + f->pos, buffer, length); - return length; -} - -static uint64_t ram_filesizeproc(void *file) -{ - auto f = (iofile_ram *)file; - return f->data->size(); -} - - -static const io_procs iop_ram = { - ram_closeproc, - ram_seekproc, - ram_readproc, - ram_writeproc, - ram_filesizeproc -}; - -static io_generic *ram_open(std::vector &data) -{ - iofile_ram *f = new iofile_ram; - f->data = &data; - f->pos = 0; - return new io_generic({ &iop_ram, f }); -} - -std::map> formats_by_category; -std::map formats_by_key; - -std::map> fs_by_category; -std::map fs_by_key; - -static std::vector variants; - -struct enumerator; - -struct fs_enum : public filesystem_manager_t::floppy_enumerator { - enumerator *m_en; - fs_enum(enumerator *en) : filesystem_manager_t::floppy_enumerator(), m_en(en) {}; - - void reg(const fs_info &fsi) const; - virtual void add(const filesystem_manager_t *manager, floppy_format_type type, u32 image_size, const char *name, const char *description) override; - virtual void add_raw(const char *name, u32 key, const char *description) override; -}; - -struct enumerator : public mame_formats_enumerator { - fs_enum fse; - - enumerator() : mame_formats_enumerator(), fse(this) {} - - virtual ~enumerator() = default; - virtual void add(const cassette_image::Format *const *formats) {} - - std::vector *cf = nullptr; - std::vector *cfs = nullptr; - virtual void category(const char *name) { - auto i = formats_by_category.find(name); - if(i != formats_by_category.end()) { - fprintf(stderr, "Collision on category name %s\n", name); - exit(1); - } - cf = &formats_by_category[name]; - cfs = &fs_by_category[name]; - } - - virtual void add(floppy_format_type format) { - auto f = format(); - std::string key = f->name(); - auto i = formats_by_key.find(key); - if(i != formats_by_key.end()) { - fprintf(stderr, "Collision on format key %s between \"%s\" and \"%s\".\n", - key.c_str(), - i->second->description(), - f->description()); - exit(1); - } - cf->push_back(f); - formats_by_key[key] = f; - } - - virtual void add(filesystem_manager_type fs) { - auto ff = fs(); - ff->enumerate_f(fse, floppy_image::FF_UNKNOWN, variants); - } -}; - -void fs_enum::reg(const fs_info &fsi) const -{ - std::string key = fsi.m_name; - auto i = fs_by_key.find(key); - if(i != fs_by_key.end()) { - fprintf(stderr, "Collision on fs key %s between \"%s\" and \"%s\".\n", - key.c_str(), - i->second.m_description, - fsi.m_description); - exit(1); - } - m_en->cfs->push_back(fsi); - fs_by_key.emplace(key, fsi); -} - -void fs_enum::add(const filesystem_manager_t *manager, floppy_format_type type, u32 image_size, const char *name, const char *description) -{ - fs_info fsi(manager, type, image_size, name, description); - reg(fsi); -} - -void fs_enum::add_raw(const char *name, u32 key, const char *description) -{ - fs_info fsi(name, key, description); - reg(fsi); -} - -void CLIB_DECL ATTR_PRINTF(1,2) logerror(const char *format, ...) -{ - va_list arg; - va_start(arg, format); - vprintf(format, arg); - va_end(arg); -} - - -static void init_formats() -{ - enumerator en; - mame_formats_full_list(en); -} - -static floppy_image_format_t *find_format_by_name(const char *name) -{ - auto i = formats_by_key.find(name); - if(i == formats_by_key.end()) - return nullptr; - return i->second; -} - -static floppy_image_format_t *find_format_by_identify(io_generic *image) -{ - int best = 0; - floppy_image_format_t *best_fif = nullptr; - - for(const auto &e : formats_by_key) { - floppy_image_format_t *fif = e.second; - int score = fif->identify(image, floppy_image::FF_UNKNOWN, variants); - if(score > best) { - best = score; - best_fif = fif; - } - } - return best_fif; -} - -static const fs_info *find_fs_by_name(const char *name) -{ - auto i = fs_by_key.find(name); - if(i == fs_by_key.end()) - return nullptr; - return &i->second; -} +static formats_table formats; static void display_usage() { fprintf(stderr, "Usage: \n"); - fprintf(stderr, " floptool.exe identify [ ...] -- Identify a floppy image format\n"); - fprintf(stderr, " floptool.exe convert [input_format|auto] output_format -- Convert a floppy image\n"); + fprintf(stderr, " floptool.exe identify [ ...] -- Identify an image format\n"); + fprintf(stderr, " floptool.exe flopconvert [input_format|auto] output_format -- Convert a floppy image\n"); fprintf(stderr, " floptool.exe flopcreate output_format filesystem -- Create a preformatted floppy image\n"); fprintf(stderr, " floptool.exe flopdir input_format filesystem -- List the contents of a floppy image\n"); fprintf(stderr, " floptool.exe flopread input_format filesystem -- Extract a file from a floppy image\n"); @@ -279,40 +32,56 @@ static void display_usage() } static void display_formats() -{ +{ int sk = 0; - for(const auto &e : formats_by_key) { + for(const auto &e : formats.floppy_format_info_by_key) { + int sz = e.first.size(); + if(sz > sk) + sk = sz; + } + + for(const auto &e : formats.filesystem_format_by_key) { int sz = e.first.size(); if(sz > sk) sk = sz; } - for(const auto &e : fs_by_key) { + + for(const auto &e : formats.floppy_create_info_by_key) { int sz = e.first.size(); if(sz > sk) sk = sz; } fprintf(stderr, "Supported floppy formats:\n\n"); - for(const auto &e : formats_by_category) + for(const auto &e : formats.floppy_format_info_by_category) if(!e.second.empty()) { fprintf(stderr, "%s:\n", e.first.c_str()); - for(floppy_image_format_t *fif : e.second) - fprintf(stderr, " %-*s - %s [%s]\n", sk, fif->name(), fif->description(), fif->extensions()); + for(auto *fif : e.second) + fprintf(stderr, " %-*s r%c - %s [%s]\n", sk, fif->m_format->name(), fif->m_format->supports_save() ? 'w' : '-', fif->m_format->description(), fif->m_format->extensions()); } fprintf(stderr, "\n\n"); - fprintf(stderr, "Supported floppy filesystems:\n\n"); - for(const auto &e : fs_by_category) + fprintf(stderr, "Supported filesystems (with floppy formatting names):\n\n"); + for(const auto &e : formats.filesystem_format_by_category) if(!e.second.empty()) { fprintf(stderr, "%s:\n", e.first.c_str()); - for(const fs_info &fs : e.second) - fprintf(stderr, " %-*s %c%c%c - %s\n", + for(const auto &f : e.second) { + fprintf(stderr, " %-*s %c%c%c %c%c%c - %s\n", sk, - fs.m_name, - !fs.m_manager || fs.m_manager->can_format() ? 'f' : '-', - fs.m_manager && fs.m_manager->can_read() ? 'r' : '-', - fs.m_manager && fs.m_manager->can_write() ? 'w' : '-', - fs.m_description); + f->m_manager->name(), + f->m_floppy || f->m_floppy_raw ? 'F' : '-', + f->m_hd ? 'H' : '-', + f->m_cd ? 'C' : '-', + f->m_manager->can_format() || f->m_floppy_raw ? 'f' : '-', + f->m_manager->can_read() ? 'r' : '-', + f->m_manager->can_write() ? 'w' : '-', + f->m_manager->description()); + for(auto &f2 : f->m_floppy_create) + fprintf(stderr, " %-*s - %s\n", + sk, + f2->m_name, + f2->m_description); + } } } @@ -323,177 +92,157 @@ static void display_full_usage() display_usage(); fprintf(stderr, "\n"); display_formats(); - fprintf(stderr, "\nExample usage:\n"); - fprintf(stderr, " floptool.exe identify image.dsk\n\n"); } static int identify(int argc, char *argv[]) { - if (argc<3) { + // Need to detect CHDs too + + if(argc<3) { fprintf(stderr, "Missing name of file to identify.\n\n"); display_usage(); return 1; } + int sz = 0; for(int i=2; i sz) + sz = len; + } + + for(int i=2; im_format->name()); + if(len > sz2) + sz2 = len; + } + + bool first = true; + for(const auto &e : scores) { + printf("%-*s %c %3d - %-*s %s\n", sz, first ? argv[i] : "", first ? ':' : ' ', e.first, sz2, e.second->m_format->name(), e.second->m_format->description()); + first = false; } - io_generic io; - io.file = f; - io.procs = &stdio_ioprocs_noclose; - io.filler = 0xff; - - floppy_image_format_t *best_fif = find_format_by_identify(&io); - if (best_fif) - printf("%s : %s\n", argv[i], best_fif->description()); - else - printf("%s : Unknown format\n", argv[i]); - fclose(f); } return 0; } -static int convert(int argc, char *argv[]) +static const floppy_format_info *find_floppy_source_format(const char *name, image_handler &ih) { - if (argc!=6) { - fprintf(stderr, "Incorrect number of arguments.\n\n"); - display_usage(); - return 1; - } - - floppy_image_format_t *source_format, *dest_format; - - 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 source_io; - source_io.file = f; - source_io.procs = &stdio_ioprocs_noclose; - source_io.filler = 0xff; - - if(!core_stricmp(argv[2], "auto")) { - source_format = find_format_by_identify(&source_io); - if(!source_format) { - fprintf(stderr, "Error: Could not identify the format of file %s\n", argv[4]); - return 1; + const floppy_format_info *source_format; + if(!core_stricmp(name, "auto")) { + auto scores = ih.identify(formats); + if(scores.empty()) { + fprintf(stderr, "Error: Could not identify the format of file %s\n", ih.get_on_disk_path().c_str()); + return nullptr; + } + if(scores.size() >= 2 && scores[0].first == scores[1].first) { + fprintf(stderr, "Ambiguous source format. Possible formats:\n"); + int sz = 0; + for(const auto &e : scores) { + int len = strlen(e.second->m_format->name()); + if(len > sz) + sz = len; + } + + for(const auto &e : scores) + printf(" %3d - %-*s %s\n", e.first, sz, e.second->m_format->name(), e.second->m_format->description()); + return nullptr; } + source_format = scores[0].second; } else { - source_format = find_format_by_name(argv[2]); + source_format = formats.find_floppy_format_info_by_key(name); if(!source_format) { - fprintf(stderr, "Error: Format '%s' unknown\n", argv[2]); - return 1; + fprintf(stderr, "Error: Format '%s' unknown\n", name); + return nullptr; + } } + return source_format; +} - dest_format = find_format_by_name(argv[3]); - if(!dest_format) { - fprintf(stderr, "Error: Format '%s' unknown\n", argv[3]); +static int flopconvert(int argc, char *argv[]) +{ + if(argc!=6) { + fprintf(stderr, "Incorrect number of arguments.\n\n"); + display_usage(); return 1; } - if(!dest_format->supports_save()) { - fprintf(stderr, "Error: saving to format '%s' unsupported\n", argv[3]); + + image_handler ih; + ih.set_on_disk_path(argv[4]); + + const floppy_format_info *source_format = find_floppy_source_format(argv[2], ih); + if(!source_format) return 1; - } - sprintf(msg, "Error opening %s for writing", argv[5]); - f = fopen(argv[5], "wb"); - if (!f) { - perror(msg); + const floppy_format_info *dest_format = formats.find_floppy_format_info_by_key(argv[3]); + if(!dest_format) { + fprintf(stderr, "Error: Format '%s' unknown\n", argv[3]); return 1; } - io_generic dest_io; - dest_io.file = f; - dest_io.procs = &stdio_ioprocs_noclose; - dest_io.filler = 0xff; - - floppy_image image(84, 2, floppy_image::FF_UNKNOWN); - if(!source_format->load(&source_io, floppy_image::FF_UNKNOWN, variants, &image)) { - fprintf(stderr, "Error: parsing input file as '%s' failed\n", source_format->name()); + if(!dest_format->m_format->supports_save()) { + fprintf(stderr, "Error: Aaving to format '%s' unsupported\n", argv[3]); return 1; } - if(!dest_format->save(&dest_io, variants, &image)) { - fprintf(stderr, "Error: writing output file as '%s' failed\n", dest_format->name()); + if(ih.floppy_load(source_format)) { + fprintf(stderr, "Error: Loading as format '%s' failed\n", source_format->m_format->name()); return 1; } - fclose((FILE *)source_io.file); - fclose((FILE *)dest_io.file); + ih.set_on_disk_path(argv[5]); + + if(ih.floppy_save(dest_format)) { + fprintf(stderr, "Error: Saving as format '%s' failed\n", dest_format->m_format->name()); + return 1; + } return 0; } -static int create(int argc, char *argv[]) +static int flopcreate(int argc, char *argv[]) { - if (argc!=5) { + if(argc!=5) { fprintf(stderr, "Incorrect number of arguments.\n\n"); display_usage(); return 1; } - auto dest_format = find_format_by_name(argv[2]); + auto dest_format = formats.find_floppy_format_info_by_key(argv[2]); if(!dest_format) { - fprintf(stderr, "Error: Format '%s' unknown\n", argv[2]); + fprintf(stderr, "Error: Floppy format '%s' unknown\n", argv[3]); return 1; } - - auto source_fs = find_fs_by_name(argv[3]); - if(!source_fs) { - fprintf(stderr, "Error: Filesystem '%s' unknown\n", argv[3]); + if(!dest_format->m_format->supports_save()) { + fprintf(stderr, "Error: Saving to format '%s' unsupported\n", argv[3]); return 1; } - floppy_image image(84, 2, floppy_image::FF_UNKNOWN); - - if(source_fs->m_type) { - fs_meta_data meta; - - std::vector img(source_fs->m_image_size); - fsblk_vec_t blockdev(img); - auto fs = source_fs->m_manager->mount(blockdev); - fs->format(meta); - - auto iog = ram_open(img); - auto source_format = source_fs->m_type(); - source_format->load(iog, floppy_image::FF_UNKNOWN, variants, &image); - delete source_format; - delete iog; - - } else - fs_unformatted::format(source_fs->m_key, &image); - - char msg[4096]; - sprintf(msg, "Error opening %s for writing", argv[4]); - auto f = fopen(argv[4], "wb"); - if (!f) { - perror(msg); + auto create_fs = formats.find_floppy_create_info_by_key(argv[3]); + if(!create_fs) { + fprintf(stderr, "Error: Floppy creation format '%s' unknown\n", argv[3]); return 1; } - - io_generic dest_io; - dest_io.file = f; - dest_io.procs = &stdio_ioprocs_noclose; - dest_io.filler = 0xff; - - if(!dest_format->save(&dest_io, variants, &image)) { - fprintf(stderr, "Error: writing output file as '%s' failed\n", dest_format->name()); + if(!create_fs->m_manager->can_format()) { + fprintf(stderr, "Error: Floppy creation format '%s' does not support creating new images\n", argv[3]); return 1; } - fclose((FILE *)dest_io.file); + fs_meta_data meta; + image_handler ih; + ih.set_on_disk_path(argv[4]); - return 0; + ih.floppy_create(create_fs, meta); + return ih.floppy_save(dest_format); } 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) @@ -541,14 +290,14 @@ static void dir_scan(u32 depth, filesystem_t::dir_t dir, std::vectormount(blockdev); - auto vmetad = fm->volume_meta_description(); - auto fmetad = fm->file_meta_description(); - auto dmetad = fm->directory_meta_description(); + auto [fsm, fs] = ih.get_fs(); + auto vmetad = fsm->volume_meta_description(); + auto fmetad = fsm->file_meta_description(); + auto dmetad = fsm->directory_meta_description(); - auto vmeta = load_fs->metadata(); + auto vmeta = fs->metadata(); if(!vmeta.empty()) { std::string vinf = "Volume:"; for(const auto &e : vmetad) @@ -568,8 +317,8 @@ static int generic_dir(const filesystem_manager_t *fm, fsblk_t &blockdev) std::unordered_map nmap; for(size_t i = 0; i != names.size(); i++) nmap[names[i]] = i; - - auto root = load_fs->root(); + + auto root = fs->root(); std::vector> entries; entries.resize(1); @@ -599,69 +348,55 @@ static int generic_dir(const filesystem_manager_t *fm, fsblk_t &blockdev) static int flopdir(int argc, char *argv[]) { - if (argc!=5) { + if(argc!=5) { 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]); + image_handler ih; + ih.set_on_disk_path(argv[4]); + + const floppy_format_info *source_format = find_floppy_source_format(argv[2], ih); + if(!source_format) return 1; - } - auto fs = find_fs_by_name(argv[3]); + auto fs = formats.find_filesystem_format_by_key(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]); + fprintf(stderr, "Error: Filesystem '%s' does not implement reading\n", argv[3]); return 1; } - char msg[4096]; - sprintf(msg, "Error opening %s for reading", argv[4]); - FILE *f = fopen(argv[4], "rb"); - if (!f) { - perror(msg); + if(ih.floppy_load(source_format)) { + fprintf(stderr, "Error: Loading as format '%s' failed\n", source_format->m_format->name()); 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()); + + if(ih.floppy_mount_fs(fs)) { + fprintf(stderr, "Error: Parsing as filesystem '%s' failed\n", fs->m_manager->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); - return generic_dir(fs->m_manager, blockdev); + return generic_dir(ih); } -// Should use chd&friends instead, but one thing at a time - static int hddir(int argc, char *argv[]) { - if (argc!=4) { + if(argc!=4) { fprintf(stderr, "Incorrect number of arguments.\n\n"); display_usage(); return 1; } - auto fs = find_fs_by_name(argv[2]); + image_handler ih; + ih.set_on_disk_path(argv[3]); + + auto fs = formats.find_filesystem_format_by_key(argv[2]); if(!fs) { fprintf(stderr, "Error: Filesystem '%s' unknown\n", argv[2]); return 1; @@ -672,114 +407,22 @@ static int hddir(int argc, char *argv[]) return 1; } - char msg[4096]; - sprintf(msg, "Error opening %s for reading", argv[3]); - FILE *f = fopen(argv[3], "rb"); - if (!f) { - perror(msg); + if(ih.hd_mount_fs(fs)) { + fprintf(stderr, "Error: Parsing as filesystem '%s' failed\n", fs->m_manager->name()); return 1; } - fseek(f, 0, SEEK_END); - size_t size = ftell(f); - rewind(f); - std::vector img(size); - fread(img.data(), size, 1, f); - fclose(f); - - fsblk_vec_t blockdev(img); - return generic_dir(fs->m_manager, blockdev); -} - -static std::vector path_split(const filesystem_manager_t *fm, std::string path) -{ - std::string opath = path; - std::vector rpath; - if(fm->has_subdirectories()) { - std::string element; - char sep = fm->directory_separator(); - for(char c : opath) { - if(c == sep) { - if(!element.empty()) { - rpath.push_back(element); - element.clear(); - } - } else - element += c; - } - if(!element.empty()) - rpath.push_back(element); - - } else - rpath.push_back(opath); - - return rpath; -} - -static std::vector 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 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 &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; + return generic_dir(ih); } - -static int generic_read(const filesystem_manager_t *fm, fsblk_t &blockdev, const char *srcpath, const char *dstpath) +static int generic_read(image_handler &ih, const char *srcpath, const char *dstpath) { - auto load_fs = fm->mount(blockdev); + auto [fsm, fs] = ih.get_fs(); - std::vector path = path_split(fm, srcpath); + std::vector path = ih.path_split(srcpath); - auto dir = load_fs->root(); + auto dir = fs->root(); std::string apath; for(unsigned int i = 0; i < path.size() - 1; i++) { auto c = dir.contents(); @@ -788,15 +431,15 @@ static int generic_read(const filesystem_manager_t *fm, fsblk_t &blockdev, const 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()); + fprintf(stderr, "Error: directory %s%c%s not found\n", apath.c_str(), fsm->directory_separator(), path[i].c_str()); return 1; } if(c[j].m_type != fs_dir_entry_type::dir) { - fprintf(stderr, "Error: %s%c%s is not a directory\n", apath.c_str(), fm->directory_separator(), path[i].c_str()); + fprintf(stderr, "Error: %s%c%s is not a directory\n", apath.c_str(), fsm->directory_separator(), path[i].c_str()); return 1; } dir = dir.dir_get(c[j].m_key); - apath += fm->directory_separator() + path[i]; + apath += fsm->directory_separator() + path[i]; } auto c = dir.contents(); @@ -805,38 +448,23 @@ static int generic_read(const filesystem_manager_t *fm, fsblk_t &blockdev, const 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(), fm->directory_separator(), path.back().c_str()); + fprintf(stderr, "Error: file %s%c%s not found\n", apath.c_str(), fsm->directory_separator(), path.back().c_str()); return 1; } auto file = dir.file_get(c[j].m_key); auto meta = file.metadata(); if(!meta.has(fs_meta_name::length)) { - fprintf(stderr, "Error: %s%c%s is not a readable file\n", apath.c_str(), fm->directory_separator(), path.back().c_str()); + fprintf(stderr, "Error: %s%c%s is not a readable file\n", apath.c_str(), fsm->directory_separator(), path.back().c_str()); return 1; } - fsave(dstpath, file.read_all()); - - bool has_rsrc = fm->has_rsrc() && meta.has(fs_meta_name::rsrc_length); - - if(has_rsrc) { - std::string rpath = path_make_rsrc(dstpath); - - auto filedata = file.rsrc_read_all(); - filedata.insert(filedata.begin(), 0x2a, 0); + image_handler::fsave(dstpath, file.read_all()); - 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 - filesystem_t::w16b(head+0x18, 1); // Number of entries - filesystem_t::w32b(head+0x1a, 2); // Resource fork - filesystem_t::w32b(head+0x22, 0x2a); // Offset in the file - filesystem_t::w32b(head+0x26, filedata.size()); // Length + bool has_rsrc = fsm->has_rsrc() && meta.has(fs_meta_name::rsrc_length); - fsave(rpath, filedata); - } + if(has_rsrc) + image_handler::fsave_rsrc(image_handler::path_make_rsrc(dstpath), file.rsrc_read_all()); return 0; } @@ -844,70 +472,55 @@ static int generic_read(const filesystem_manager_t *fm, fsblk_t &blockdev, const static int flopread(int argc, char *argv[]) { - if (argc!=7) { + 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]); + image_handler ih; + ih.set_on_disk_path(argv[4]); + + const floppy_format_info *source_format = find_floppy_source_format(argv[2], ih); + if(!source_format) return 1; - } - auto fs = find_fs_by_name(argv[3]); + auto fs = formats.find_filesystem_format_by_key(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]); + fprintf(stderr, "Error: Filesystem '%s' does not implement reading\n", argv[3]); return 1; } - char msg[4096]; - sprintf(msg, "Error opening %s for reading", argv[4]); - FILE *f = fopen(argv[4], "rb"); - if (!f) { - perror(msg); + if(ih.floppy_load(source_format)) { + fprintf(stderr, "Error: Loading as format '%s' failed\n", source_format->m_format->name()); 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()); + + if(ih.floppy_mount_fs(fs)) { + fprintf(stderr, "Error: Parsing as filesystem '%s' failed\n", fs->m_manager->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); - return generic_read(fs->m_manager, blockdev, argv[5], argv[6]); + return generic_read(ih, argv[5], argv[6]); } - -// Should use chd&friends instead, but one thing at a time - static int hdread(int argc, char *argv[]) { - if (argc!=6) { + if(argc!=6) { fprintf(stderr, "Incorrect number of arguments.\n\n"); display_usage(); return 1; } - auto fs = find_fs_by_name(argv[2]); + image_handler ih; + ih.set_on_disk_path(argv[3]); + + auto fs = formats.find_filesystem_format_by_key(argv[2]); if(!fs) { fprintf(stderr, "Error: Filesystem '%s' unknown\n", argv[2]); return 1; @@ -918,33 +531,23 @@ static int hdread(int argc, char *argv[]) return 1; } - char msg[4096]; - sprintf(msg, "Error opening %s for reading", argv[3]); - FILE *f = fopen(argv[3], "rb"); - if (!f) { - perror(msg); + if(ih.hd_mount_fs(fs)) { + fprintf(stderr, "Error: Parsing as filesystem '%s' failed\n", fs->m_manager->name()); return 1; } - fseek(f, 0, SEEK_END); - size_t size = ftell(f); - rewind(f); - std::vector img(size); - fread(img.data(), size, 1, f); - fclose(f); - - fsblk_vec_t blockdev(img); - return generic_read(fs->m_manager, blockdev, argv[4], argv[5]); + + return generic_read(ih, argv[4], argv[5]); } -static int generic_write(const filesystem_manager_t *fm, fsblk_t &blockdev, const char *srcpath, const char *dstpath) +static int generic_write(image_handler &ih, const char *srcpath, const char *dstpath) { - auto load_fs = fm->mount(blockdev); + auto [fsm, fs] = ih.get_fs(); - std::vector path = path_split(fm, dstpath); + std::vector path = ih.path_split(dstpath); - auto dir = load_fs->root(); + auto dir = fs->root(); std::string apath; for(unsigned int i = 0; i < path.size() - 1; i++) { auto c = dir.contents(); @@ -953,15 +556,15 @@ static int generic_write(const filesystem_manager_t *fm, fsblk_t &blockdev, cons 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()); + fprintf(stderr, "Error: directory %s%c%s not found\n", apath.c_str(), fsm->directory_separator(), path[i].c_str()); return 1; } if(c[j].m_type != fs_dir_entry_type::dir) { - fprintf(stderr, "Error: %s%c%s is not a directory\n", apath.c_str(), fm->directory_separator(), path[i].c_str()); + fprintf(stderr, "Error: %s%c%s is not a directory\n", apath.c_str(), fsm->directory_separator(), path[i].c_str()); return 1; } dir = dir.dir_get(c[j].m_key); - apath += fm->directory_separator() + path[i]; + apath += fsm->directory_separator() + path[i]; } @@ -969,33 +572,18 @@ static int generic_write(const filesystem_manager_t *fm, fsblk_t &blockdev, cons meta.set(fs_meta_name::name, path.back()); auto file = dir.file_create(meta); - auto filedata = fload(srcpath); + auto filedata = image_handler::fload(srcpath); file.replace(filedata); - bool has_rsrc = fm->has_rsrc(); + bool has_rsrc = fsm->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; - } - } - } + std::string rpath = image_handler::path_make_rsrc(dstpath); + + if(image_handler::fexists(rpath)) { + filedata = image_handler::fload_rsrc(rpath); + if(!filedata.empty()) + file.rsrc_replace(filedata); } } @@ -1005,92 +593,64 @@ static int generic_write(const filesystem_manager_t *fm, fsblk_t &blockdev, cons static int flopwrite(int argc, char *argv[]) { - if (argc!=7) { + 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]); + image_handler ih; + ih.set_on_disk_path(argv[4]); + + const floppy_format_info *source_format = find_floppy_source_format(argv[2], ih); + if(!source_format) return 1; - } - auto fs = find_fs_by_name(argv[3]); + auto fs = formats.find_filesystem_format_by_key(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]); + fprintf(stderr, "Error: Filesystem '%s' does not implement reading\n", argv[3]); return 1; } - char msg[4096]; - sprintf(msg, "Error opening %s for reading", argv[4]); - FILE *f = fopen(argv[4], "rb"); - if (!f) { - perror(msg); + if(ih.floppy_load(source_format)) { + fprintf(stderr, "Error: Loading as format '%s' failed\n", source_format->m_format->name()); 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); - 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); + if(ih.floppy_mount_fs(fs)) { + fprintf(stderr, "Error: Parsing as filesystem '%s' failed\n", fs->m_manager->name()); return 1; } + + int err = generic_write(ih, argv[5], argv[6]); + if(err) + return err; - 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()); + ih.fs_to_floppy(); + if(ih.floppy_save(source_format)) 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) { + if(argc!=6) { fprintf(stderr, "Incorrect number of arguments.\n\n"); display_usage(); return 1; } - auto fs = find_fs_by_name(argv[2]); + + image_handler ih; + ih.set_on_disk_path(argv[3]); + + auto fs = formats.find_filesystem_format_by_key(argv[2]); if(!fs) { fprintf(stderr, "Error: Filesystem '%s' unknown\n", argv[2]); return 1; @@ -1101,52 +661,41 @@ static int hdwrite(int argc, char *argv[]) return 1; } - char msg[4096]; - sprintf(msg, "Error opening %s for reading", argv[3]); - FILE *f = fopen(argv[3], "rb"); - if (!f) { - perror(msg); + if(ih.hd_mount_fs(fs)) { + fprintf(stderr, "Error: Parsing as filesystem '%s' failed\n", fs->m_manager->name()); return 1; } - fseek(f, 0, SEEK_END); - size_t size = ftell(f); - rewind(f); - std::vector 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]); -} + return generic_write(ih, argv[4], argv[5]); +} int CLIB_DECL main(int argc, char *argv[]) { - init_formats(); + formats.init(); - if (argc == 1) { + if(argc == 1) { display_full_usage(); return 0; } try { - if (!core_stricmp("identify", argv[1])) + if(!core_stricmp("identify", argv[1])) return identify(argc, argv); - else if (!core_stricmp("convert", argv[1])) - return convert(argc, argv); - else if (!core_stricmp("flopcreate", argv[1])) - return create(argc, argv); - else if (!core_stricmp("flopdir", argv[1])) + else if(!core_stricmp("flopconvert", argv[1])) + return flopconvert(argc, argv); + else if(!core_stricmp("flopcreate", argv[1])) + return flopcreate(argc, argv); + else if(!core_stricmp("flopdir", argv[1])) return flopdir(argc, argv); - else if (!core_stricmp("flopread", argv[1])) + else if(!core_stricmp("flopread", argv[1])) return flopread(argc, argv); - else if (!core_stricmp("flopwrite", argv[1])) + else if(!core_stricmp("flopwrite", argv[1])) return flopwrite(argc, argv); - else if (!core_stricmp("hddir", argv[1])) + else if(!core_stricmp("hddir", argv[1])) return hddir(argc, argv); - else if (!core_stricmp("hdread", argv[1])) + else if(!core_stricmp("hdread", argv[1])) return hdread(argc, argv); - else if (!core_stricmp("hdwrite", argv[1])) + else if(!core_stricmp("hdwrite", argv[1])) return hdwrite(argc, argv); else { fprintf(stderr, "Unknown command '%s'\n\n", argv[1]); diff --git a/src/tools/image_handler.cpp b/src/tools/image_handler.cpp new file mode 100644 index 00000000000..a1bec2d34ef --- /dev/null +++ b/src/tools/image_handler.cpp @@ -0,0 +1,481 @@ +// license:BSD-3-Clause +// copyright-holders:Olivier Galibert + +// Image generic handler class and helpers + +#include "image_handler.h" +#include "formats/all.h" +#include "formats/fsblk_vec.h" +#include "formats/fs_unformatted.h" + +// Fatalerror implementation + +emu_fatalerror::emu_fatalerror(util::format_argument_pack const &args) + : emu_fatalerror(0, args) +{ +} + +emu_fatalerror::emu_fatalerror(int _exitcode, util::format_argument_pack const &args) + : m_text(util::string_format(args)) + , m_code(_exitcode) +{ +} + +// io_generic from vector + +static void ram_closeproc(void *file) +{ + auto f = (iofile_ram *)file; + delete f; +} + +static int ram_seekproc(void *file, int64_t offset, int whence) +{ + auto f = (iofile_ram *)file; + switch(whence) { + case SEEK_SET: f->pos = offset; break; + case SEEK_CUR: f->pos += offset; break; + case SEEK_END: f->pos = f->data->size() + offset; break; + } + + if(whence == SEEK_CUR) + f->pos = std::max(f->pos, 0); + else + f->pos = std::clamp(f->pos, 0, f->data->size()); + return 0; +} + +static size_t ram_readproc(void *file, void *buffer, size_t length) +{ + auto f = (iofile_ram *)file; + size_t l = std::min >(length, f->data->size() - f->pos); + memcpy(buffer, f->data->data() + f->pos, l); + return l; +} + +static size_t ram_writeproc(void *file, const void *buffer, size_t length) +{ + auto f = (iofile_ram *)file; + size_t l = std::max >(f->pos + length, f->data->size()); + f->data->resize(l); + memcpy(f->data->data() + f->pos, buffer, length); + return length; +} + +static uint64_t ram_filesizeproc(void *file) +{ + auto f = (iofile_ram *)file; + return f->data->size(); +} + + +static const io_procs iop_ram = { + ram_closeproc, + ram_seekproc, + ram_readproc, + ram_writeproc, + ram_filesizeproc +}; + +io_generic *ram_open(std::vector &data) +{ + iofile_ram *f = new iofile_ram; + f->data = &data; + f->pos = 0; + return new io_generic({ &iop_ram, f }); +} + + +// Format enumeration + +namespace { + struct enumerator : public mame_formats_enumerator { + formats_table *m_table; + std::string m_category; + + enumerator(formats_table *table) : mame_formats_enumerator(), m_table(table), m_category("None") {} + + virtual ~enumerator() = default; + virtual void add(const cassette_image::Format *const *formats) {} + + virtual void category(const char *name) { + m_category = name; + } + + virtual void add(floppy_format_type format) { + m_table->floppy_format_infos.emplace_back(std::make_unique(format(), m_category)); + } + + virtual void add(const filesystem_manager_t &fs) { + m_table->filesystem_formats.emplace_back(std::make_unique(&fs, m_category)); + } + }; + + struct fs_enum : public filesystem_manager_t::floppy_enumerator { + filesystem_format *m_format; + + fs_enum(filesystem_format *format) : m_format(format) {} + + virtual void add(floppy_format_type type, u32 image_size, const char *name, const char *description) override { + m_format->m_floppy = true; + m_format->m_floppy_create.emplace_back(std::make_unique(m_format->m_manager, type, image_size, name, description)); + } + + virtual void add_raw(const char *name, u32 key, const char *description) override { + m_format->m_floppy_raw = true; + m_format->m_floppy_create.emplace_back(std::make_unique(name, key, description)); + } + }; +} + +void formats_table::init() +{ + std::vector variants; + + enumerator en(this); + mame_formats_full_list(en); + + for(auto &f : filesystem_formats) { + fs_enum fen(f.get()); + f->m_manager->enumerate_f(fen, floppy_image::FF_UNKNOWN, variants); + } + + for(auto &f : floppy_format_infos) { + auto *ff = f.get(); + std::string key = ff->m_format->name(); + auto i = floppy_format_info_by_key.find(key); + if(i != floppy_format_info_by_key.end()) { + fprintf(stderr, "Collision on floppy format name %s between \"%s\" and \"%s\".\n", + ff->m_format->name(), + ff->m_format->description(), + i->second->m_format->description()); + exit(1); + } + + floppy_format_info_by_key[key] = ff; + floppy_format_info_by_category[ff->m_category].push_back(ff); + } + + for(auto &f : filesystem_formats) { + auto *ff = f.get(); + std::string key = ff->m_manager->name(); + auto i = filesystem_format_by_key.find(key); + if(i != filesystem_format_by_key.end()) { + fprintf(stderr, "Collision on filesystem name %s between \"%s\" and \"%s\".\n", + ff->m_manager->name(), + ff->m_manager->description(), + i->second->m_manager->description()); + exit(1); + } + + filesystem_format_by_key[key] = ff; + filesystem_format_by_category[ff->m_category].push_back(ff); + + for(auto &f2 : ff->m_floppy_create) { + auto *ff2 = f2.get(); + key = ff2->m_name; + auto i = floppy_create_info_by_key.find(key); + if(i != floppy_create_info_by_key.end()) { + fprintf(stderr, "Collision on floppy create name %s between \"%s\" and \"%s\".\n", + ff2->m_name, + ff2->m_description, + i->second->m_description); + exit(1); + } + + floppy_create_info_by_key[key] = ff2; + } + } +} + +const floppy_format_info *formats_table::find_floppy_format_info_by_key(const std::string &key) const +{ + auto i = floppy_format_info_by_key.find(key); + return i == floppy_format_info_by_key.end() ? nullptr : i->second; +} + +const filesystem_format *formats_table::find_filesystem_format_by_key(const std::string &key) const +{ + auto i = filesystem_format_by_key.find(key); + return i == filesystem_format_by_key.end() ? nullptr : i->second; +} + +const floppy_create_info *formats_table::find_floppy_create_info_by_key(const std::string &key) const +{ + auto i = floppy_create_info_by_key.find(key); + return i == floppy_create_info_by_key.end() ? nullptr : i->second; +} + + +// Image handling + +std::vector image_handler::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 filedata(size); + fseek(fi, 0, SEEK_SET); + fread(filedata.data(), filedata.size(), 1, fi); + fclose(fi); + + return filedata; +} + +std::vector image_handler::fload_rsrc(std::string path) +{ + auto filedata = fload(path); + 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()); + return filedata; + } + } + } + filedata.clear(); + return filedata; +} + +void image_handler::fsave(std::string path, const std::vector &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); +} + +void image_handler::fsave_rsrc(std::string path, const std::vector &data) +{ + u8 head[0x2a]; + + filesystem_t::w32b(head+0x00, 0x00051607); // Magic + filesystem_t::w32b(head+0x04, 0x00020000); // Version + filesystem_t::fill(head+0x08, 0, 16); // Filler + filesystem_t::w16b(head+0x18, 1); // Number of entries + filesystem_t::w32b(head+0x1a, 2); // Resource fork + filesystem_t::w32b(head+0x22, 0x2a); // Offset in the file + filesystem_t::w32b(head+0x26, data.size()); // Length + + 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(head, sizeof(head), 1, fo); + fwrite(data.data(), data.size(), 1, fo); + fclose(fo); +} + +image_handler::image_handler() : m_floppy_image(84, 2, floppy_image::FF_UNKNOWN) +{ +} + +void image_handler::set_on_disk_path(std::string path) +{ + m_on_disk_path = path; +} + +std::vector> image_handler::identify(const formats_table &formats) +{ + std::vector> res; + std::vector variants; + + std::string msg = util::string_format("Error opening %s for reading", m_on_disk_path); + FILE *f = fopen(m_on_disk_path.c_str(), "rb"); + if (!f) { + perror(msg.c_str()); + return res; + } + + io_generic io; + io.file = f; + io.procs = &stdio_ioprocs_noclose; + io.filler = 0xff; + + for(const auto &e : formats.floppy_format_info_by_key) { + u8 score = e.second->m_format->identify(&io, floppy_image::FF_UNKNOWN, variants); + if(score) + res.emplace_back(std::make_pair(score, e.second)); + } + + fclose(f); + + return res; +} + +bool image_handler::floppy_load(const floppy_format_info *format) +{ + std::vector variants; + std::string msg = util::string_format("Error opening %s for reading", m_on_disk_path); + FILE *f = fopen(m_on_disk_path.c_str(), "rb"); + if (!f) { + perror(msg.c_str()); + return true; + } + + io_generic io; + io.file = f; + io.procs = &stdio_ioprocs_noclose; + io.filler = 0xff; + + return !format->m_format->load(&io, floppy_image::FF_UNKNOWN, variants, &m_floppy_image); +} + +bool image_handler::floppy_save(const floppy_format_info *format) +{ + std::vector variants; + std::string msg = util::string_format("Error opening %s for writing", m_on_disk_path); + FILE *f = fopen(m_on_disk_path.c_str(), "wb"); + if (!f) { + perror(msg.c_str()); + return true; + } + + io_generic io; + io.file = f; + io.procs = &stdio_ioprocs_noclose; + io.filler = 0xff; + + return !format->m_format->save(&io, variants, &m_floppy_image); +} + +void image_handler::floppy_create(const floppy_create_info *format, fs_meta_data meta) +{ + if(format->m_type) { + std::vector variants; + std::vector img(format->m_image_size); + fsblk_vec_t blockdev(img); + auto fs = format->m_manager->mount(blockdev); + fs->format(meta); + + auto iog = ram_open(img); + auto source_format = format->m_type(); + source_format->load(iog, floppy_image::FF_UNKNOWN, variants, &m_floppy_image); + delete source_format; + delete iog; + + } else + fs_unformatted::format(format->m_key, &m_floppy_image); +} + +bool image_handler::floppy_mount_fs(const filesystem_format *format) +{ + m_floppy_fs_converter = nullptr; + for(const auto &ci : format->m_floppy_create) { + if(ci->m_type != m_floppy_fs_converter) { + std::vector variants; + m_floppy_fs_converter = ci->m_type; + m_sector_image.clear(); + auto iog = ram_open(m_sector_image); + auto load_format = m_floppy_fs_converter(); + load_format->save(iog, variants, &m_floppy_image); + delete load_format; + delete iog; + } + + if(ci->m_image_size == m_sector_image.size()) + goto success; + } + m_floppy_fs_converter = nullptr; + m_sector_image.clear(); + return true; + + success: + m_fsblk.reset(new fsblk_vec_t(m_sector_image)); + m_fsm = format->m_manager; + m_fs = m_fsm->mount(*m_fsblk); + return false; +} + +bool image_handler::hd_mount_fs(const filesystem_format *format) +{ + // Should use the chd mechanisms, one thing at a time... + + m_sector_image = fload(m_on_disk_path); + m_fsblk.reset(new fsblk_vec_t(m_sector_image)); + m_fsm = format->m_manager; + m_fs = m_fsm->mount(*m_fsblk); + return false; +} + +void image_handler::fs_to_floppy() +{ + std::vector variants; + auto iog = ram_open(m_sector_image); + auto format = m_floppy_fs_converter(); + format->load(iog, floppy_image::FF_UNKNOWN, variants, &m_floppy_image); + delete format; + delete iog; +} + +std::vector image_handler::path_split(std::string path) const +{ + std::string opath = path; + std::vector rpath; + if(m_fsm->has_subdirectories()) { + std::string element; + char sep = m_fsm->directory_separator(); + for(char c : opath) { + if(c == sep) { + if(!element.empty()) { + rpath.push_back(element); + element.clear(); + } + } else + element += c; + } + if(!element.empty()) + rpath.push_back(element); + + } else + rpath.push_back(opath); + + return rpath; +} + +bool image_handler::fexists(std::string path) +{ + auto f = fopen(path.c_str(), "rb"); + if(f != nullptr) { + fclose(f); + return true; + } + return false; +} + + +std::string image_handler::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; +} + diff --git a/src/tools/image_handler.h b/src/tools/image_handler.h new file mode 100644 index 00000000000..bef852aaed6 --- /dev/null +++ b/src/tools/image_handler.h @@ -0,0 +1,121 @@ +// license:BSD-3-Clause +// copyright-holders:Olivier Galibert + +// Image generic handler class and helpers + +#ifndef TOOLS_IMAGE_HANDLER_H +#define TOOLS_IMAGE_HANDLER_H + +#include "../emu/emucore.h" +#include "formats/fsmgr.h" + +#include +#include +#include + +using u8 = uint8_t; +using u16 = uint16_t; +using u32 = uint32_t; + +struct iofile_ram { + std::vector *data; + int64_t pos; +}; + +io_generic *ram_open(std::vector &data); + +struct floppy_format_info { + floppy_image_format_t *m_format; + std::string m_category; + + floppy_format_info(floppy_image_format_t *format, std::string category) : m_format(format), m_category(category) {} +}; + +struct floppy_create_info { + const filesystem_manager_t *m_manager; + + floppy_format_type m_type; + u32 m_image_size; + u32 m_key; + const char *m_name; + const char *m_description; + + floppy_create_info(const filesystem_manager_t *manager, floppy_format_type type, u32 image_size, const char *name, const char *description) : + m_manager(manager), m_type(type), m_image_size(image_size), m_key(0), m_name(name), m_description(description) + { } + + floppy_create_info(const char *name, u32 key, const char *description) : + m_manager(nullptr), m_type(nullptr), m_image_size(0), m_key(key), m_name(name), m_description(description) + { } +}; + +struct filesystem_format { + const filesystem_manager_t *m_manager; + std::vector> m_floppy_create; + std::string m_category; + bool m_floppy, m_floppy_raw, m_hd, m_cd; + + filesystem_format(const filesystem_manager_t *manager, std::string category) : m_manager(manager), m_category(category), m_floppy(false), m_floppy_raw(false), m_hd(false), m_cd(false) {} +}; + +struct formats_table { + std::vector> floppy_format_infos; + std::vector> filesystem_formats; + + std::map floppy_format_info_by_key; + std::map filesystem_format_by_key; + std::map floppy_create_info_by_key; + + std::map> floppy_format_info_by_category; + std::map> filesystem_format_by_category; + + void init(); + + const floppy_format_info *find_floppy_format_info_by_key(const std::string &key) const; + const filesystem_format *find_filesystem_format_by_key(const std::string &key) const; + const floppy_create_info *find_floppy_create_info_by_key(const std::string &key) const; +}; + +class image_handler { +public: + image_handler(); + + void set_on_disk_path(std::string path); + const std::string &get_on_disk_path() const { return m_on_disk_path; } + + std::vector> identify(const formats_table &formats); + + bool floppy_load(const floppy_format_info *format); + bool floppy_save(const floppy_format_info *format); + + void floppy_create(const floppy_create_info *format, fs_meta_data meta); + bool floppy_mount_fs(const filesystem_format *format); + bool hd_mount_fs(const filesystem_format *format); + void fs_to_floppy(); + + std::pair get_fs() const { return std::make_pair(m_fsm, m_fs.get()); } + + std::vector path_split(std::string path) const; + + static std::vector fload(std::string path); + static std::vector fload_rsrc(std::string path); + static void fsave(std::string path, const std::vector &data); + static void fsave_rsrc(std::string path, const std::vector &data); + static bool fexists(std::string path); + static std::string path_make_rsrc(std::string path); + +private: + std::string m_on_disk_path; + + floppy_image m_floppy_image; + + floppy_format_type m_floppy_fs_converter; + std::vector m_sector_image; + std::unique_ptr m_fsblk; + const filesystem_manager_t *m_fsm; + std::unique_ptr m_fs; + +}; + +#endif + -- cgit v1.2.3