summaryrefslogtreecommitdiffstatshomepage
path: root/src/lib/formats/fs_coco_os9.cpp
diff options
context:
space:
mode:
author npwoods <npwoods@mess.org>2022-01-07 12:04:07 -0500
committer GitHub <noreply@github.com>2022-01-07 18:04:07 +0100
commitfd736212854673efa8e2bcf2e1b2d3420bcacdb5 (patch)
tree0ce9a5c6224cfb8f9d02f287b3122e9c67987c5d /src/lib/formats/fs_coco_os9.cpp
parent3abc1a4805412a600ae572699db6dc6b1516d15f (diff)
Created an initial file system implementation for OS-9 under the CoCo (#9096)
* Created an initial file system implementation for OS-9 under the CoCo * Minor fixes to directory entry handling * Changed to use fsblk_t::r[8|16|24|32]b() - Changed the above functions to be const
Diffstat (limited to 'src/lib/formats/fs_coco_os9.cpp')
-rw-r--r--src/lib/formats/fs_coco_os9.cpp542
1 files changed, 542 insertions, 0 deletions
diff --git a/src/lib/formats/fs_coco_os9.cpp b/src/lib/formats/fs_coco_os9.cpp
new file mode 100644
index 00000000000..aeacaf66d36
--- /dev/null
+++ b/src/lib/formats/fs_coco_os9.cpp
@@ -0,0 +1,542 @@
+// license:BSD-3-Clause
+// copyright-holders:Nathan Woods
+/***************************************************************************
+
+ fs_coco_os9.cpp
+
+ Management of CoCo OS-9 floppy images
+
+ OS-9 Level 2 Technical Reference, Chapter 5, Random Block File Manager,
+ page 2
+
+ http://www.colorcomputerarchive.com/coco/Documents/Manuals/Operating Systems/OS-9 Level 2 Manual (Tandy).pdf
+
+***************************************************************************/
+
+#include "fs_coco_os9.h"
+#include "coco_rawdsk.h"
+#include "strformat.h"
+
+namespace fs {
+
+const coco_os9_image COCO_OS9;
+
+
+//**************************************************************************
+// IMPLEMENTATION
+//**************************************************************************
+
+//-------------------------------------------------
+// name
+//-------------------------------------------------
+
+const char *coco_os9_image::name() const
+{
+ return "coco_os9";
+}
+
+
+//-------------------------------------------------
+// description
+//-------------------------------------------------
+
+const char *coco_os9_image::description() const
+{
+ return "CoCo OS-9";
+}
+
+
+//-------------------------------------------------
+// enumerate_f
+//-------------------------------------------------
+
+void coco_os9_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_COCO_RAWDSK_FORMAT, 161280, "coco_rawdsk_os9_35", "CoCo Raw Disk OS-9 single-sided 35 tracks");
+ fe.add(FLOPPY_COCO_RAWDSK_FORMAT, 184320, "coco_rawdsk_os9_40", "CoCo Raw Disk OS-9 single-sided 40 tracks");
+ }
+}
+
+
+//-------------------------------------------------
+// can_format
+//-------------------------------------------------
+
+bool coco_os9_image::can_format() const
+{
+ return false;
+}
+
+
+//-------------------------------------------------
+// can_read
+//-------------------------------------------------
+
+bool coco_os9_image::can_read() const
+{
+ return true;
+}
+
+
+//-------------------------------------------------
+// can_write
+//-------------------------------------------------
+
+bool coco_os9_image::can_write() const
+{
+ return false;
+}
+
+
+//-------------------------------------------------
+// has_rsrc
+//-------------------------------------------------
+
+bool coco_os9_image::has_rsrc() const
+{
+ return false;
+}
+
+
+//-------------------------------------------------
+// directory_separator
+//-------------------------------------------------
+
+char coco_os9_image::directory_separator() const
+{
+ return '/';
+}
+
+
+//-------------------------------------------------
+// volume_meta_description
+//-------------------------------------------------
+
+std::vector<meta_description> coco_os9_image::volume_meta_description() const
+{
+ std::vector<meta_description> results;
+ results.emplace_back(meta_description(meta_name::name, meta_type::string, "UNTITLED", false, [](const meta_value &m) { return m.as_string().size() <= 32; }, "Volume name, up to 32 characters"));
+ return results;
+}
+
+
+//-------------------------------------------------
+// file_meta_description
+//-------------------------------------------------
+
+std::vector<meta_description> coco_os9_image::file_meta_description() const
+{
+ return entity_meta_description();
+}
+
+
+//-------------------------------------------------
+// directory_meta_description
+//-------------------------------------------------
+
+std::vector<meta_description> coco_os9_image::directory_meta_description() const
+{
+ return entity_meta_description();
+}
+
+
+//-------------------------------------------------
+// entity_meta_description
+//-------------------------------------------------
+
+std::vector<meta_description> coco_os9_image::entity_meta_description() const
+{
+ std::vector<meta_description> results;
+ results.emplace_back(meta_description(meta_name::name, meta_type::string, "", false, [](const meta_value &m) { return validate_filename(m.as_string()); }, "File name"));
+ results.emplace_back(meta_description(meta_name::creation_date, meta_type::date, util::arbitrary_datetime::now(), false, nullptr, "Creation time"));
+ results.emplace_back(meta_description(meta_name::owner_id, meta_type::number, 0, true, nullptr, "Owner ID"));
+ results.emplace_back(meta_description(meta_name::attributes, meta_type::string, 0, true, nullptr, "File attributes"));
+ results.emplace_back(meta_description(meta_name::length, meta_type::number, 0, true, nullptr, "Size of the file in bytes"));
+ return results;
+}
+
+
+//-------------------------------------------------
+// mount
+//-------------------------------------------------
+
+std::unique_ptr<filesystem_t> coco_os9_image::mount(fsblk_t &blockdev) const
+{
+ // read the header block
+ blockdev.set_block_size(256);
+ volume_header header(blockdev.get(0));
+
+ // sanity checks
+ if (header.sectors_per_track() != header.track_size_in_sectors())
+ return { };
+
+ // create the implementation
+ return std::make_unique<impl>(blockdev, std::move(header));
+}
+
+
+//-------------------------------------------------
+// pick_os9_string
+//-------------------------------------------------
+
+std::string coco_os9_image::pick_os9_string(std::string_view raw_string)
+{
+ // find the last NUL or high bit character
+ auto iter = std::find_if(raw_string.begin(), raw_string.end(), [](char ch)
+ {
+ return ch == '\0' || ch & 0x80;
+ });
+
+ // get the preliminary result
+ std::string result(raw_string.begin(), iter);
+
+ // and add the final character if we have to
+ if (iter < raw_string.end() && *iter & 0x80)
+ result.append(1, *iter & 0x7F);
+ return result;
+
+}
+
+
+//-------------------------------------------------
+// pick_integer_be
+//-------------------------------------------------
+
+u32 coco_os9_image::pick_integer_be(const u8 *data, int length)
+{
+ u32 result = 0;
+ for (int i = 0; i < length; i++)
+ result |= ((u32)data[length - i - 1]) << i * 8;
+ return result;
+}
+
+
+//-------------------------------------------------
+// validate_filename
+//-------------------------------------------------
+
+bool coco_os9_image::validate_filename(std::string_view name)
+{
+ return !is_ignored_filename(name)
+ && name.size() <= 29
+ && std::find_if(name.begin(), name.end(), [](const char ch) { return ch == '\0' || ch == '/' || ch >= 0x80; }) == name.end();
+}
+
+
+//-------------------------------------------------
+// is_ignored_filename - should this file name be
+// ignored if it is in the file system?
+//-------------------------------------------------
+
+bool coco_os9_image::is_ignored_filename(std::string_view name)
+{
+ return name.empty()
+ || name[0] == '\0'
+ || name == "."
+ || name == "..";
+}
+
+
+//-------------------------------------------------
+// volume_header ctor
+//-------------------------------------------------
+
+coco_os9_image::volume_header::volume_header(fsblk_t::block_t &&block)
+ : m_block(std::move(block))
+{
+}
+
+
+//-------------------------------------------------
+// volume_header::name
+//-------------------------------------------------
+
+std::string coco_os9_image::volume_header::name() const
+{
+ std::string_view raw_name((const char *)&m_block.rodata()[31], 32);
+ return pick_os9_string(raw_name);
+}
+
+
+//-------------------------------------------------
+// file_header ctor
+//-------------------------------------------------
+
+coco_os9_image::file_header::file_header(fsblk_t::block_t &&block)
+ : m_block(std::move(block))
+{
+}
+
+
+//-------------------------------------------------
+// file_header::creation_date
+//-------------------------------------------------
+
+util::arbitrary_datetime coco_os9_image::file_header::creation_date() const
+{
+ util::arbitrary_datetime dt;
+ memset(&dt, 0, sizeof(dt));
+ dt.year = 1900 + m_block.r8(13);
+ dt.month = m_block.r8(14);
+ dt.day_of_month = m_block.r8(15);
+ return dt;
+}
+
+
+//-------------------------------------------------
+// file_header::metadata
+//-------------------------------------------------
+
+meta_data coco_os9_image::file_header::metadata() const
+{
+ // format the attributes
+ std::string attributes = util::string_format("%c%c%c%c%c%c%c%c",
+ is_directory() ? 'd' : '-',
+ is_non_sharable() ? 's' : '-',
+ is_public_execute() ? 'x' : '-',
+ is_public_write() ? 'w' : '-',
+ is_public_read() ? 'r' : '-',
+ is_user_execute() ? 'x' : '-',
+ is_user_write() ? 'w' : '-',
+ is_user_read() ? 'r' : '-');
+
+ meta_data result;
+ result.set(meta_name::creation_date, creation_date());
+ result.set(meta_name::owner_id, owner_id());
+ result.set(meta_name::attributes, std::move(attributes));
+ result.set(meta_name::length, file_size());
+ return result;
+}
+
+
+//-------------------------------------------------
+// file_header::get_sector_map_entry_count
+//-------------------------------------------------
+
+int coco_os9_image::file_header::get_sector_map_entry_count() const
+{
+ return (m_block.size() - 16) / 5;
+}
+
+
+//-------------------------------------------------
+// file_header::get_sector_map_entry
+//-------------------------------------------------
+
+void coco_os9_image::file_header::get_sector_map_entry(int entry_number, u32 &start_lsn, u16 &count) const
+{
+ start_lsn = m_block.r24b(16 + (entry_number * 5) + 0);
+ count = m_block.r16b(16 + (entry_number * 5) + 3);
+}
+
+
+//-------------------------------------------------
+// impl ctor
+//-------------------------------------------------
+
+coco_os9_image::impl::impl(fsblk_t &blockdev, volume_header &&header)
+ : filesystem_t(blockdev, 256)
+ , m_volume_header(std::move(header))
+{
+}
+
+
+//-------------------------------------------------
+// impl::metadata
+//-------------------------------------------------
+
+meta_data coco_os9_image::impl::metadata()
+{
+ meta_data results;
+ results.set(meta_name::name, m_volume_header.name());
+ return results;
+}
+
+
+//-------------------------------------------------
+// impl::root
+//-------------------------------------------------
+
+filesystem_t::dir_t coco_os9_image::impl::root()
+{
+ if (!m_root)
+ m_root = open_directory(m_volume_header.root_dir_lsn());
+ return m_root.strong();
+}
+
+
+//-------------------------------------------------
+// impl::drop_root_ref
+//-------------------------------------------------
+
+void coco_os9_image::impl::drop_root_ref()
+{
+ m_root = nullptr;
+}
+
+
+//-------------------------------------------------
+// impl::open_directory
+//-------------------------------------------------
+
+coco_os9_image::impl::directory *coco_os9_image::impl::open_directory(u32 lsn)
+{
+ file_header header(m_blockdev.get(lsn));
+ return new directory(*this, std::move(header));
+}
+
+
+//-------------------------------------------------
+// impl::read_file_data
+//-------------------------------------------------
+
+std::vector<u8> coco_os9_image::impl::read_file_data(const file_header &header) const
+{
+ std::vector<u8> data;
+ data.reserve(header.file_size());
+ int entry_count = header.get_sector_map_entry_count();
+ for (int i = 0; i < entry_count; i++)
+ {
+ u32 start_lsn;
+ u16 count;
+ header.get_sector_map_entry(i, start_lsn, count);
+
+ for (u32 lsn = start_lsn; lsn < start_lsn + count; lsn++)
+ {
+ auto block = m_blockdev.get(lsn);
+ size_t block_size = std::min(std::min((u32)m_volume_header.sector_size(), block.size()), header.file_size() - (u32)data.size());
+ for (auto i = 0; i < block_size; i++)
+ data.push_back(block.rodata()[i]);
+ }
+ }
+ return data;
+}
+
+
+//-------------------------------------------------
+// file ctor
+//-------------------------------------------------
+
+coco_os9_image::impl::file::file(impl &i, file_header &&file_header)
+ : m_fs(i)
+ , m_file_header(std::move(file_header))
+{
+}
+
+
+//-------------------------------------------------
+// file::drop_weak_references
+//-------------------------------------------------
+
+void coco_os9_image::impl::file::drop_weak_references()
+{
+}
+
+
+//-------------------------------------------------
+// file::metadata
+//-------------------------------------------------
+
+meta_data coco_os9_image::impl::file::metadata()
+{
+ return m_file_header.metadata();
+}
+
+
+//-------------------------------------------------
+// file::read_all
+//-------------------------------------------------
+
+std::vector<u8> coco_os9_image::impl::file::read_all()
+{
+ return m_fs.read_file_data(m_file_header);
+}
+
+
+//-------------------------------------------------
+// directory ctor
+//-------------------------------------------------
+
+coco_os9_image::impl::directory::directory(impl &i, file_header &&file_header)
+ : m_fs(i)
+ , m_file_header(std::move(file_header))
+{
+}
+
+
+//-------------------------------------------------
+// directory::drop_weak_references
+//-------------------------------------------------
+
+void coco_os9_image::impl::directory::drop_weak_references()
+{
+}
+
+
+//-------------------------------------------------
+// directory::metadata
+//-------------------------------------------------
+
+meta_data coco_os9_image::impl::directory::metadata()
+{
+ return m_file_header.metadata();
+}
+
+
+//-------------------------------------------------
+// directory::contents
+//-------------------------------------------------
+
+std::vector<dir_entry> coco_os9_image::impl::directory::contents()
+{
+ // read the directory data
+ std::vector<u8> directory_data = m_fs.read_file_data(m_file_header);
+
+ // and assemble results
+ std::vector<dir_entry> results;
+ int directory_count = directory_data.size() / 32;
+ for (int i = 0; i < directory_count; i++)
+ {
+ // determine the filename
+ std::string_view raw_filename((const char *) &directory_data[i * 32], 29);
+ std::string filename = pick_os9_string(raw_filename);
+ if (is_ignored_filename(filename))
+ continue;
+
+ // determine the entry type
+ u32 lsn = pick_integer_be(&directory_data[i * 32] + 29, 3);
+ file_header file_header(m_fs.m_blockdev.get(lsn));
+ dir_entry_type entry_type = file_header.is_directory()
+ ? dir_entry_type::dir
+ : dir_entry_type::file;
+
+ // and return the results
+ results.emplace_back(std::move(filename), entry_type, lsn);
+ }
+ return results;
+}
+
+
+//-------------------------------------------------
+// directory::file_get
+//-------------------------------------------------
+
+filesystem_t::file_t coco_os9_image::impl::directory::file_get(u64 key)
+{
+ file_header header(m_fs.m_blockdev.get((u32) key));
+ return file_t(new file(m_fs, std::move(header)));
+}
+
+
+//-------------------------------------------------
+// directory::dir_get
+//-------------------------------------------------
+
+filesystem_t::dir_t coco_os9_image::impl::directory::dir_get(u64 key)
+{
+ return dir_t(m_fs.open_directory((u32) key));
+}
+
+}