diff options
-rw-r--r-- | scripts/src/formats.lua | 12 | ||||
-rw-r--r-- | src/devices/bus/cbmiec/c1541.cpp | 2 | ||||
-rw-r--r-- | src/lib/formats/all.cpp | 7 | ||||
-rw-r--r-- | src/lib/formats/fs_cbmdos.cpp | 476 | ||||
-rw-r--r-- | src/lib/formats/fs_cbmdos.h | 47 | ||||
-rw-r--r-- | src/lib/util/corestr.cpp | 29 | ||||
-rw-r--r-- | src/lib/util/corestr.h | 21 |
7 files changed, 568 insertions, 26 deletions
diff --git a/scripts/src/formats.lua b/scripts/src/formats.lua index 1798880b0e0..9bee4bd271f 100644 --- a/scripts/src/formats.lua +++ b/scripts/src/formats.lua @@ -2138,6 +2138,18 @@ end -------------------------------------------------- -- +--@src/lib/formats/fs_cbmdos.h,FORMATS["FS_CBMDOS"] = true +-------------------------------------------------- + +if opt_tool(FORMATS, "FS_CBMDOS") then + files { + MAME_DIR.. "src/lib/formats/fs_cbmdos.cpp", + MAME_DIR.. "src/lib/formats/fs_cbmdos.h", + } +end + +-------------------------------------------------- +-- --@src/lib/formats/fs_coco_rsdos.h,FORMATS["FS_COCO_RSDOS"] = true -------------------------------------------------- diff --git a/src/devices/bus/cbmiec/c1541.cpp b/src/devices/bus/cbmiec/c1541.cpp index b8872947322..ba2fa5318d0 100644 --- a/src/devices/bus/cbmiec/c1541.cpp +++ b/src/devices/bus/cbmiec/c1541.cpp @@ -138,6 +138,7 @@ #include "emu.h" #include "c1541.h" +#include "formats/fs_cbmdos.h" #include "bus/centronics/ctronics.h" @@ -911,6 +912,7 @@ void c1541_device_base::floppy_formats(format_registration &fr) { fr.add(FLOPPY_D64_FORMAT); fr.add(FLOPPY_G64_FORMAT); + fr.add(fs::CBMDOS); } diff --git a/src/lib/formats/all.cpp b/src/lib/formats/all.cpp index 5dedacc1b3b..30b3f451d04 100644 --- a/src/lib/formats/all.cpp +++ b/src/lib/formats/all.cpp @@ -472,6 +472,10 @@ #include "fs_oric_jasmin.h" #endif +#ifdef HAS_FORMATS_FS_CBMDOS +#include "fs_cbmdos.h" +#endif + #ifdef HAS_FORMATS_IBMXDF_DSK #include "ibmxdf_dsk.h" #endif @@ -898,6 +902,9 @@ void mame_formats_full_list(mame_formats_enumerator &en) #ifdef HAS_FORMATS_G64_DSK en.add(FLOPPY_G64_FORMAT); // g64_dsk.h #endif +#ifdef HAS_FORMATS_FS_CBMDOS + en.add(fs::CBMDOS); // fs_cbmdos.h +#endif en.category("Camputers"); #ifdef HAS_FORMATS_CAMPLYNX_CAS diff --git a/src/lib/formats/fs_cbmdos.cpp b/src/lib/formats/fs_cbmdos.cpp new file mode 100644 index 00000000000..ca58720267d --- /dev/null +++ b/src/lib/formats/fs_cbmdos.cpp @@ -0,0 +1,476 @@ +// license:BSD-3-Clause +// copyright-holders:Nathan Woods +/*************************************************************************** + + fs_cbmdos.cpp + + Management of CBM (Commodore) DOS disk images + + http://fileformats.archiveteam.org/wiki/CBMFS + +***************************************************************************/ + +#include "fs_cbmdos.h" +#include "d64_dsk.h" +#include "corestr.h" +#include "strformat.h" + +#include <array> +#include <set> +#include <string_view> +#include <tuple> + +namespace fs { + const cbmdos_image CBMDOS; +}; + +using namespace fs; + +namespace { + +class impl : public filesystem_t { +public: + struct cbmdos_dirent + { + u8 m_next_directory_track; + u8 m_next_directory_sector; + u8 m_file_type; + u8 m_file_first_track; + u8 m_file_first_sector; + char m_file_name[16]; + u8 m_first_side_sector_block_track; + u8 m_first_side_sector_block_sector; + u8 m_rel_file_record_length; + u8 m_unused[6]; + u8 m_sector_count_low; + u8 m_sector_count_high; + }; + + class block_iterator + { + public: + block_iterator(const impl &fs, u8 first_track, u8 first_sector); + bool next(); + const void *data() const; + const std::array<cbmdos_dirent, 4> &dirent_data() const; + u8 size() const; + + private: + const impl & m_fs; + fsblk_t::block_t m_block; + std::set<std::tuple<u8, u8>> m_visited_set; + u8 m_track; + u8 m_sector; + }; + + impl(fsblk_t &blockdev); + virtual ~impl() = default; + + virtual meta_data volume_metadata() override; + virtual std::pair<err_t, meta_data> metadata(const std::vector<std::string> &path) override; + virtual std::pair<err_t, std::vector<dir_entry>> directory_contents(const std::vector<std::string> &path) override; + virtual std::pair<err_t, std::vector<u8>> file_read(const std::vector<std::string> &path) override; + +private: + fsblk_t::block_t read_sector(int track, int sector) const; + std::optional<cbmdos_dirent> dirent_from_path(const std::vector<std::string> &path) const; + void iterate_directory_entries(const std::function<bool(const cbmdos_dirent &dirent)> &callback) const; + meta_data metadata_from_dirent(const cbmdos_dirent &dirent) const; +}; + +// methods +std::string_view strtrimright_cbm(std::string_view str); +template<size_t N> std::string_view strtrimright_cbm(const char(&str)[N]); + +}; + + +//------------------------------------------------- +// name +//------------------------------------------------- + +const char *fs::cbmdos_image::name() const +{ + return "cbmdos"; +} + + +//------------------------------------------------- +// description +//------------------------------------------------- + +const char *fs::cbmdos_image::description() const +{ + return "CBMDOS"; +} + + +//------------------------------------------------- +// enumerate_f +//------------------------------------------------- + +void fs::cbmdos_image::enumerate_f(floppy_enumerator &fe, u32 form_factor, const std::vector<u32> &variants) const +{ + if (has(form_factor, variants, floppy_image::FF_525, floppy_image::SSDD)) + { + fe.add(FLOPPY_D64_FORMAT, 174848, "d64_cbmdos_35", "D64 CBMDOS single-sided 35 tracks"); + fe.add(FLOPPY_D64_FORMAT, 192256, "d64_cbmdos_40", "D64 CBMDOS single-sided 40 tracks"); + } +} + + +//------------------------------------------------- +// can_format +//------------------------------------------------- + +bool fs::cbmdos_image::can_format() const +{ + return false; +} + + +//------------------------------------------------- +// can_read +//------------------------------------------------- + +bool fs::cbmdos_image::can_read() const +{ + return true; +} + + +//------------------------------------------------- +// can_write +//------------------------------------------------- + +bool fs::cbmdos_image::can_write() const +{ + return false; +} + + +//------------------------------------------------- +// has_rsrc +//------------------------------------------------- + +bool fs::cbmdos_image::has_rsrc() const +{ + return false; +} + + +//------------------------------------------------- +// volume_meta_description +//------------------------------------------------- + +std::vector<meta_description> fs::cbmdos_image::volume_meta_description() const +{ + std::vector<meta_description> results; + results.emplace_back(meta_name::name, "UNTITLED", false, [](const meta_value &m) { return m.as_string().size() <= 16; }, "Volume name, up to 16 characters"); + return results; +} + + +//------------------------------------------------- +// file_meta_description +//------------------------------------------------- + +std::vector<meta_description> fs::cbmdos_image::file_meta_description() const +{ + std::vector<meta_description> results; + results.emplace_back(meta_name::name, "", false, [](const meta_value &m) { return m.as_string().size() <= 16; }, "File name, up to 16 characters"); + results.emplace_back(meta_name::file_type, "", true, nullptr, "Type of the file"); + results.emplace_back(meta_name::length, 0, true, nullptr, "Size of the file in bytes"); + return results; +} + + +//------------------------------------------------- +// mount +//------------------------------------------------- + +std::unique_ptr<filesystem_t> fs::cbmdos_image::mount(fsblk_t &blockdev) const +{ + return std::make_unique<impl>(blockdev); +} + + +//------------------------------------------------- +// strtrimright_cbm +//------------------------------------------------- + +namespace { + +std::string_view strtrimright_cbm(std::string_view str) +{ + return strtrimright(str, [](char c) { return c != (char)0xA0; }); +} + + +//------------------------------------------------- +// strtrimright_cbm +//------------------------------------------------- + +template<size_t N> +std::string_view strtrimright_cbm(const char (&str)[N]) +{ + std::string_view sv(str, std::size(str)); + return strtrimright_cbm(sv); +} + + +//------------------------------------------------- +// impl ctor +//------------------------------------------------- + +impl::impl(fsblk_t &blockdev) + : filesystem_t(blockdev, 256) +{ +} + + +//------------------------------------------------- +// impl::volume_metadata +//------------------------------------------------- + +meta_data impl::volume_metadata() +{ + auto bam_block = read_sector(18, 0); + std::string_view disk_name = std::string_view((const char *) bam_block.rodata() + 0x90, 16); + + meta_data results; + results.set(meta_name::name, strtrimright_cbm(disk_name)); + return results; +} + + +//------------------------------------------------- +// impl::metadata +//------------------------------------------------- + +std::pair<err_t, meta_data> impl::metadata(const std::vector<std::string> &path) +{ + std::optional<cbmdos_dirent> dirent = dirent_from_path(path); + if (!dirent) + return std::make_pair(ERR_NOT_FOUND, meta_data()); + + return std::make_pair(ERR_OK, metadata_from_dirent(*dirent)); +} + + +//------------------------------------------------- +// impl::directory_contents +//------------------------------------------------- + +std::pair<err_t, std::vector<dir_entry>> impl::directory_contents(const std::vector<std::string> &path) +{ + std::vector<dir_entry> results; + auto callback = [this, &results](const cbmdos_dirent &ent) + { + results.emplace_back(dir_entry_type::file, metadata_from_dirent(ent)); + return false; + }; + iterate_directory_entries(callback); + return std::make_pair(ERR_OK, std::move(results)); +} + + +//------------------------------------------------- +// impl::file_read +//------------------------------------------------- + +std::pair<err_t, std::vector<u8>> impl::file_read(const std::vector<std::string> &path) +{ + // find the file + std::optional<cbmdos_dirent> dirent = dirent_from_path(path); + if (!dirent) + return std::make_pair(ERR_NOT_FOUND, std::vector<u8>()); + + // and get the data + std::vector<u8> result; + block_iterator iter(*this, dirent->m_file_first_track, dirent->m_file_first_sector); + while (iter.next()) + result.insert(result.end(), (const u8 *)iter.data(), (const u8 *)iter.data() + iter.size()); + + return std::make_pair(ERR_OK, std::move(result)); +} + + +//------------------------------------------------- +// impl::read_sector +//------------------------------------------------- + +fsblk_t::block_t impl::read_sector(int track, int sector) const +{ + // CBM thinks in terms of tracks/sectors, but we have a block device abstraction + u32 block = 0; + block += std::max(std::min(track, 18) - 1, 0) * 21; + block += std::max(std::min(track, 25) - 18, 0) * 19; + block += std::max(std::min(track, 31) - 25, 0) * 18; + block += std::max(std::min(track, 41) - 31, 0) * 17; + block += sector; + return m_blockdev.get(block); +} + + +//------------------------------------------------- +// impl::dirent_from_path +//------------------------------------------------- + +std::optional<impl::cbmdos_dirent> impl::dirent_from_path(const std::vector<std::string> &path) const +{ + if (path.size() != 1) + return { }; + std::string_view path_part = path[0]; + + std::optional<cbmdos_dirent> result; + auto callback = [&result, path_part](const cbmdos_dirent &dirent) + { + bool found = strtrimright_cbm(dirent.m_file_name) == path_part; + if (found) + result = dirent; + return found; + }; + iterate_directory_entries(callback); + return result; +} + + +//------------------------------------------------- +// impl::iterate_directory_entries +//------------------------------------------------- + +void impl::iterate_directory_entries(const std::function<bool(const cbmdos_dirent &dirent)> &callback) const +{ + block_iterator iter(*this, 18, 1); + while (iter.next()) + { + for (const cbmdos_dirent &ent : iter.dirent_data()) + { + if (ent.m_file_type != 0x00) + { + if (callback(ent)) + return; + } + } + } +} + + +//------------------------------------------------- +// impl::metadata_from_dirent +//------------------------------------------------- + +meta_data impl::metadata_from_dirent(const cbmdos_dirent &dirent) const +{ + std::string file_type; + switch (dirent.m_file_type) + { + case 0x80: + file_type = "DEL"; + break; + case 0x81: + file_type = "SEQ"; + break; + case 0x82: + file_type = "PRG"; + break; + case 0x83: + file_type = "USR"; + break; + case 0x84: + file_type = "REL"; + break; + default: + file_type = util::string_format("$0x02X", (int)dirent.m_file_type); + break; + } + + // compute the file size + u32 file_size = 0; + block_iterator iter(*this, dirent.m_file_first_track, dirent.m_file_first_sector); + while (iter.next()) + file_size += iter.size(); + + // build the metadata and return it + meta_data result; + result.set(meta_name::name, strtrimright_cbm(dirent.m_file_name)); + result.set(meta_name::file_type, std::move(file_type)); + result.set(meta_name::length, file_size); + return result; +} + + +//------------------------------------------------- +// impl::block_iterator ctor +//------------------------------------------------- + +impl::block_iterator::block_iterator(const impl &fs, u8 first_track, u8 first_sector) + : m_fs(fs) + , m_track(first_track) + , m_sector(first_sector) +{ +} + + +//------------------------------------------------- +// impl::block_iterator::next +//------------------------------------------------- + +bool impl::block_iterator::next() +{ + bool result; + if (m_track != 0x00) + { + // check for the degenerate scenario where we have a cycle (this should not happen + // on a non-corrupt disk) + auto visited_tuple = std::make_tuple(m_track, m_sector); + if (m_visited_set.find(visited_tuple) != m_visited_set.end()) + return false; + m_visited_set.insert(visited_tuple); + + // with that out of the way, proceed + m_block = m_fs.read_sector(m_track, m_sector); + m_track = m_block.r8(0); + m_sector = m_block.r8(1); + result = true; + } + else + { + // the iterator has already completed + result = false; + } + return result; +} + + +//------------------------------------------------- +// impl::block_iterator::data +//------------------------------------------------- + +const void *impl::block_iterator::data() const +{ + return m_block.rodata() + 2; +} + + +//------------------------------------------------- +// impl::block_iterator::dirent_data +//------------------------------------------------- + +const std::array<impl::cbmdos_dirent, 4> &impl::block_iterator::dirent_data() const +{ + return *reinterpret_cast<const std::array<impl::cbmdos_dirent, 4> *>(m_block.rodata()); +} + + +//------------------------------------------------- +// impl::block_iterator::size +//------------------------------------------------- + +u8 impl::block_iterator::size() const +{ + return m_track != 0x00 ? 254 : m_sector - 1; +} + +} diff --git a/src/lib/formats/fs_cbmdos.h b/src/lib/formats/fs_cbmdos.h new file mode 100644 index 00000000000..bbc2a1c8a82 --- /dev/null +++ b/src/lib/formats/fs_cbmdos.h @@ -0,0 +1,47 @@ +// license:BSD-3-Clause +// copyright-holders:Nathan Woods +/*************************************************************************** + + fs_cbmdos.h + + Management of CBM (Commodore) DOS disk images + +***************************************************************************/ + +#ifndef MAME_FORMATS_FS_CBMDOS_H +#define MAME_FORMATS_FS_CBMDOS_H + +#pragma once + + +#include "fsmgr.h" +#include <optional> +#include <string_view> + +namespace fs { + + class cbmdos_image : public manager_t { + public: + cbmdos_image() = default; + + virtual const char *name() const override; + virtual const char *description() const override; + + virtual void enumerate_f(floppy_enumerator &fe, u32 form_factor, const std::vector<u32> &variants) const override; + virtual std::unique_ptr<filesystem_t> mount(fsblk_t &blockdev) const override; + + virtual bool can_format() const override; + virtual bool can_read() const override; + virtual bool can_write() const override; + virtual bool has_rsrc() const override; + + virtual std::vector<meta_description> volume_meta_description() const override; + virtual std::vector<meta_description> file_meta_description() const override; + }; + + + extern const cbmdos_image CBMDOS; + +} // namespace fs + +#endif // #define MAME_FORMATS_FS_CBMDOS_H diff --git a/src/lib/util/corestr.cpp b/src/lib/util/corestr.cpp index aff55470b13..90009411c9c 100644 --- a/src/lib/util/corestr.cpp +++ b/src/lib/util/corestr.cpp @@ -146,38 +146,15 @@ void strreplacechr(std::string& str, char ch, char newch) } } -static std::string_view internal_strtrimspace(std::string_view str, bool right_only) -{ - // identify the start - std::string_view::iterator start = str.begin(); - if (!right_only) - { - start = std::find_if( - str.begin(), - str.end(), - [](char c) { return !isspace(uint8_t(c)); }); - } - - // identify the end - std::string_view::iterator end = std::find_if( - str.rbegin(), - std::string_view::reverse_iterator(start), - [](char c) { return !isspace(uint8_t(c)); }).base(); - - // extract the string - return end > start - ? str.substr(start - str.begin(), end - start) - : std::string_view(); -} - std::string_view strtrimspace(std::string_view str) { - return internal_strtrimspace(str, false); + std::string_view str2 = strtrimleft(str, [](char c) { return !isspace(uint8_t(c)); }); + return strtrimright(str2, [](char c) { return !isspace(uint8_t(c)); }); } std::string_view strtrimrightspace(std::string_view str) { - return internal_strtrimspace(str, true); + return strtrimright(str, [](char c) { return !isspace(uint8_t(c)); }); } std::string strmakeupper(std::string_view str) diff --git a/src/lib/util/corestr.h b/src/lib/util/corestr.h index 81b04eeebaf..8caf014509b 100644 --- a/src/lib/util/corestr.h +++ b/src/lib/util/corestr.h @@ -16,6 +16,7 @@ #include <string> #include <string_view> +#include <algorithm> #include <cstring> @@ -51,6 +52,26 @@ int core_strnicmp(const char *s1, const char *s2, size_t n); int core_strwildcmp(const char *sp1, const char *sp2); bool core_iswildstr(const char *sp); +/* trim functions */ +template<typename TPred> +std::string_view strtrimleft(std::string_view str, TPred pred) +{ + std::string_view::iterator start = std::find_if( + str.begin(), + str.end(), + pred); + return str.substr(start - str.begin(), str.end() - start); +} + +template<typename TPred> +std::string_view strtrimright(std::string_view str, TPred pred) +{ + std::string_view::reverse_iterator end = std::find_if( + str.rbegin(), + str.rend(), + pred); + return str.substr(0, str.size() - (end - str.rbegin())); +} void strdelchr(std::string& str, char chr); void strreplacechr(std::string& str, char ch, char newch); |