summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
-rw-r--r--scripts/src/formats.lua12
-rw-r--r--src/devices/bus/cbmiec/c1541.cpp2
-rw-r--r--src/lib/formats/all.cpp7
-rw-r--r--src/lib/formats/fs_cbmdos.cpp476
-rw-r--r--src/lib/formats/fs_cbmdos.h47
-rw-r--r--src/lib/util/corestr.cpp29
-rw-r--r--src/lib/util/corestr.h21
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);