mirror of
https://github.com/holub/mame
synced 2025-04-19 15:11:37 +03:00
fs: new API, blk_t is probably going to change too
This commit is contained in:
parent
735bd1f626
commit
43d01755e6
@ -18,147 +18,22 @@
|
||||
#include "strformat.h"
|
||||
|
||||
|
||||
//**************************************************************************
|
||||
// TYPE DECLARATIONS
|
||||
//**************************************************************************
|
||||
using namespace fs;
|
||||
|
||||
namespace fs { const coco_os9_image COCO_OS9; }
|
||||
|
||||
namespace {
|
||||
|
||||
using namespace fs;
|
||||
|
||||
// methods
|
||||
std::string pick_os9_string(std::string_view raw_string);
|
||||
std::string to_os9_string(std::string_view s, size_t length);
|
||||
util::arbitrary_datetime from_os9_date(u32 os9_date, u16 os9_time = 0);
|
||||
std::tuple<u32, u16> to_os9_date(const util::arbitrary_datetime &datetime);
|
||||
u32 pick_integer_be(const u8 *data, int length);
|
||||
bool validate_filename(std::string_view name);
|
||||
bool is_ignored_filename(std::string_view name);
|
||||
std::vector<meta_description> entity_meta_description();
|
||||
|
||||
|
||||
// ======================> volume_header
|
||||
|
||||
class volume_header
|
||||
{
|
||||
class coco_os9_impl : public filesystem_t {
|
||||
public:
|
||||
volume_header(fsblk_t::block_t &&block);
|
||||
coco_os9_impl(fsblk_t &blockdev);
|
||||
virtual ~coco_os9_impl() = default;
|
||||
|
||||
u32 total_sectors() const { return m_block.r24b(0); }
|
||||
u8 track_size_in_sectors() const { return m_block.r8(3); }
|
||||
u16 allocation_bitmap_bytes() const { return m_block.r16b(4); }
|
||||
u16 cluster_size() const { return m_block.r16b(6); }
|
||||
u32 root_dir_lsn() const { return m_block.r24b(8); }
|
||||
u16 owner_id() const { return m_block.r16b(11); }
|
||||
u16 disk_id() const { return m_block.r16b(14); }
|
||||
u8 format_flags() const { return m_block.r8(16); }
|
||||
u16 sectors_per_track() const { return m_block.r16b(17); }
|
||||
u32 bootstrap_lsn() const { return m_block.r24b(21); }
|
||||
u16 bootstrap_size() const { return m_block.r16b(24); }
|
||||
util::arbitrary_datetime creation_date() const { return from_os9_date(m_block.r24b(26), m_block.r16b(29)); }
|
||||
u16 sector_size() const { u16 result = m_block.r16b(104); return result != 0 ? result : 256; }
|
||||
u8 sides() const { return (format_flags() & 0x01) ? 2 : 1; }
|
||||
bool double_density() const { return (format_flags() & 0x02) != 0; }
|
||||
bool double_track() const { return (format_flags() & 0x04) != 0; }
|
||||
bool quad_track_density() const { return (format_flags() & 0x08) != 0; }
|
||||
bool octal_track_density() const { return (format_flags() & 0x10) != 0; }
|
||||
|
||||
std::string name() const;
|
||||
|
||||
private:
|
||||
fsblk_t::block_t m_block;
|
||||
static bool is_ignored_filename(std::string_view name);
|
||||
static bool validate_filename(std::string_view name);
|
||||
};
|
||||
|
||||
|
||||
// ======================> file_header
|
||||
|
||||
class file_header
|
||||
{
|
||||
public:
|
||||
file_header(fsblk_t::block_t &&block);
|
||||
|
||||
u8 attributes() const { return m_block.r8(0); }
|
||||
u16 owner_id() const { return m_block.r16b(1); }
|
||||
u8 link_count() const { return m_block.r8(8); }
|
||||
u32 file_size() const { return m_block.r32b(9); }
|
||||
util::arbitrary_datetime creation_date() const;
|
||||
bool is_directory() const { return (attributes() & 0x80) != 0; }
|
||||
bool is_non_sharable() const { return (attributes() & 0x40) != 0; }
|
||||
bool is_public_execute() const { return (attributes() & 0x20) != 0; }
|
||||
bool is_public_write() const { return (attributes() & 0x10) != 0; }
|
||||
bool is_public_read() const { return (attributes() & 0x08) != 0; }
|
||||
bool is_user_execute() const { return (attributes() & 0x04) != 0; }
|
||||
bool is_user_write() const { return (attributes() & 0x02) != 0; }
|
||||
bool is_user_read() const { return (attributes() & 0x01) != 0; }
|
||||
|
||||
meta_data metadata() const;
|
||||
int get_sector_map_entry_count() const;
|
||||
void get_sector_map_entry(int entry_number, u32 &start_lsn, u16 &count) const;
|
||||
|
||||
private:
|
||||
fsblk_t::block_t m_block;
|
||||
};
|
||||
|
||||
|
||||
// ======================> impl
|
||||
|
||||
class impl : public filesystem_t {
|
||||
public:
|
||||
class file : public ifile_t {
|
||||
public:
|
||||
file(impl &fs, file_header &&file_header);
|
||||
virtual ~file() = default;
|
||||
|
||||
virtual void drop_weak_references() override;
|
||||
|
||||
virtual meta_data metadata() override;
|
||||
virtual std::vector<u8> read_all() override;
|
||||
|
||||
private:
|
||||
impl &m_fs;
|
||||
file_header m_file_header;
|
||||
};
|
||||
|
||||
class directory : public idir_t {
|
||||
public:
|
||||
directory(impl &i, file_header &&file_header);
|
||||
virtual ~directory() = default;
|
||||
|
||||
virtual void drop_weak_references() override;
|
||||
virtual meta_data metadata() override;
|
||||
virtual std::vector<dir_entry> contents() override;
|
||||
virtual file_t file_get(u64 key) override;
|
||||
virtual dir_t dir_get(u64 key) override;
|
||||
|
||||
private:
|
||||
impl &m_fs;
|
||||
file_header m_file_header;
|
||||
};
|
||||
|
||||
impl(fsblk_t &blockdev, volume_header &&header);
|
||||
virtual ~impl() = default;
|
||||
|
||||
virtual meta_data metadata() override;
|
||||
virtual dir_t root() override;
|
||||
virtual void format(const meta_data &meta) override;
|
||||
|
||||
private:
|
||||
volume_header m_volume_header;
|
||||
dir_t m_root;
|
||||
|
||||
directory *open_directory(u32 lsn);
|
||||
std::vector<u8> read_file_data(const file_header &header) const;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
namespace fs {
|
||||
|
||||
const coco_os9_image COCO_OS9;
|
||||
|
||||
};
|
||||
|
||||
|
||||
//**************************************************************************
|
||||
// IMPLEMENTATION
|
||||
//**************************************************************************
|
||||
@ -167,7 +42,7 @@ const coco_os9_image COCO_OS9;
|
||||
// name
|
||||
//-------------------------------------------------
|
||||
|
||||
const char *fs::coco_os9_image::name() const
|
||||
const char *coco_os9_image::name() const
|
||||
{
|
||||
return "coco_os9";
|
||||
}
|
||||
@ -177,7 +52,7 @@ const char *fs::coco_os9_image::name() const
|
||||
// description
|
||||
//-------------------------------------------------
|
||||
|
||||
const char *fs::coco_os9_image::description() const
|
||||
const char *coco_os9_image::description() const
|
||||
{
|
||||
return "CoCo OS-9";
|
||||
}
|
||||
@ -187,7 +62,7 @@ const char *fs::coco_os9_image::description() const
|
||||
// enumerate_f
|
||||
//-------------------------------------------------
|
||||
|
||||
void fs::coco_os9_image::enumerate_f(floppy_enumerator &fe, u32 form_factor, const std::vector<u32> &variants) const
|
||||
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))
|
||||
{
|
||||
@ -201,7 +76,7 @@ void fs::coco_os9_image::enumerate_f(floppy_enumerator &fe, u32 form_factor, con
|
||||
// can_format
|
||||
//-------------------------------------------------
|
||||
|
||||
bool fs::coco_os9_image::can_format() const
|
||||
bool coco_os9_image::can_format() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
@ -211,7 +86,7 @@ bool fs::coco_os9_image::can_format() const
|
||||
// can_read
|
||||
//-------------------------------------------------
|
||||
|
||||
bool fs::coco_os9_image::can_read() const
|
||||
bool coco_os9_image::can_read() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
@ -221,7 +96,7 @@ bool fs::coco_os9_image::can_read() const
|
||||
// can_write
|
||||
//-------------------------------------------------
|
||||
|
||||
bool fs::coco_os9_image::can_write() const
|
||||
bool coco_os9_image::can_write() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@ -231,7 +106,7 @@ bool fs::coco_os9_image::can_write() const
|
||||
// has_rsrc
|
||||
//-------------------------------------------------
|
||||
|
||||
bool fs::coco_os9_image::has_rsrc() const
|
||||
bool coco_os9_image::has_rsrc() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@ -241,7 +116,7 @@ bool fs::coco_os9_image::has_rsrc() const
|
||||
// directory_separator
|
||||
//-------------------------------------------------
|
||||
|
||||
char fs::coco_os9_image::directory_separator() const
|
||||
char coco_os9_image::directory_separator() const
|
||||
{
|
||||
return '/';
|
||||
}
|
||||
@ -251,7 +126,7 @@ char fs::coco_os9_image::directory_separator() const
|
||||
// volume_meta_description
|
||||
//-------------------------------------------------
|
||||
|
||||
std::vector<meta_description> fs::coco_os9_image::volume_meta_description() const
|
||||
std::vector<meta_description> coco_os9_image::volume_meta_description() const
|
||||
{
|
||||
std::vector<meta_description> results;
|
||||
results.emplace_back(meta_description(meta_name::name, "UNTITLED", false, [](const meta_value &m) { return m.as_string().size() <= 32; }, "Volume name, up to 32 characters"));
|
||||
@ -264,51 +139,10 @@ std::vector<meta_description> fs::coco_os9_image::volume_meta_description() cons
|
||||
// file_meta_description
|
||||
//-------------------------------------------------
|
||||
|
||||
std::vector<meta_description> fs::coco_os9_image::file_meta_description() const
|
||||
{
|
||||
return entity_meta_description();
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// directory_meta_description
|
||||
//-------------------------------------------------
|
||||
|
||||
std::vector<meta_description> fs::coco_os9_image::directory_meta_description() const
|
||||
{
|
||||
return entity_meta_description();
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// mount
|
||||
//-------------------------------------------------
|
||||
|
||||
std::unique_ptr<filesystem_t> fs::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));
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// entity_meta_description
|
||||
//-------------------------------------------------
|
||||
|
||||
namespace {
|
||||
|
||||
std::vector<meta_description> entity_meta_description()
|
||||
std::vector<meta_description> coco_os9_image::file_meta_description() const
|
||||
{
|
||||
std::vector<meta_description> results;
|
||||
results.emplace_back(meta_description(meta_name::name, "", false, [](const meta_value &m) { return validate_filename(m.as_string()); }, "File name"));
|
||||
results.emplace_back(meta_description(meta_name::name, "", false, [](const meta_value &m) { return coco_os9_impl::validate_filename(m.as_string()); }, "File name"));
|
||||
results.emplace_back(meta_description(meta_name::creation_date, util::arbitrary_datetime::now(), false, nullptr, "Creation time"));
|
||||
results.emplace_back(meta_description(meta_name::owner_id, 0, true, nullptr, "Owner ID"));
|
||||
results.emplace_back(meta_description(meta_name::attributes, "", true, nullptr, "File attributes"));
|
||||
@ -318,107 +152,38 @@ std::vector<meta_description> entity_meta_description()
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// pick_os9_string
|
||||
// directory_meta_description
|
||||
//-------------------------------------------------
|
||||
|
||||
std::string pick_os9_string(std::string_view raw_string)
|
||||
std::vector<meta_description> coco_os9_image::directory_meta_description() const
|
||||
{
|
||||
// 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;
|
||||
|
||||
return file_meta_description();
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// to_os9_string
|
||||
// mount
|
||||
//-------------------------------------------------
|
||||
|
||||
std::string to_os9_string(std::string_view s, size_t length)
|
||||
std::unique_ptr<filesystem_t> coco_os9_image::mount(fsblk_t &blockdev) const
|
||||
{
|
||||
std::string result(length, '\0');
|
||||
for (auto i = 0; i < std::min(length, s.size()); i++)
|
||||
{
|
||||
result[i] = (s[i] & 0x7F)
|
||||
| (i == s.size() ? 0x80 : 0x00);
|
||||
}
|
||||
return result;
|
||||
return std::make_unique<coco_os9_impl>(blockdev);
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// pick_integer_be
|
||||
// impl ctor
|
||||
//-------------------------------------------------
|
||||
|
||||
u32 pick_integer_be(const u8 *data, int length)
|
||||
coco_os9_impl::coco_os9_impl(fsblk_t &blockdev)
|
||||
: filesystem_t(blockdev, 256)
|
||||
{
|
||||
u32 result = 0;
|
||||
for (int i = 0; i < length; i++)
|
||||
result |= u32(data[length - i - 1]) << i * 8;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// from_os9_date
|
||||
//-------------------------------------------------
|
||||
|
||||
util::arbitrary_datetime from_os9_date(u32 os9_date, u16 os9_time)
|
||||
{
|
||||
util::arbitrary_datetime dt;
|
||||
memset(&dt, 0, sizeof(dt));
|
||||
dt.year = ((os9_date >> 16) & 0xFF) + 1900;
|
||||
dt.month = (os9_date >> 8) & 0xFF;
|
||||
dt.day_of_month = (os9_date >> 0) & 0xFF;
|
||||
dt.hour = (os9_time >> 8) & 0xFF;
|
||||
dt.minute = (os9_time >> 0) & 0xFF;
|
||||
return dt;
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// to_os9_date
|
||||
//-------------------------------------------------
|
||||
|
||||
std::tuple<u32, u16> to_os9_date(const util::arbitrary_datetime &datetime)
|
||||
{
|
||||
u32 os9_date = ((datetime.year - 1900) & 0xFF) << 16
|
||||
| (datetime.month & 0xFF) << 8
|
||||
| (datetime.day_of_month & 0xFF) << 0;
|
||||
u16 os9_time = (datetime.hour & 0xFF) << 8
|
||||
| (datetime.minute & 0xFF) << 0;
|
||||
return std::make_tuple(os9_date, os9_time);
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// validate_filename
|
||||
//-------------------------------------------------
|
||||
|
||||
bool 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 is_ignored_filename(std::string_view name)
|
||||
bool coco_os9_impl::is_ignored_filename(std::string_view name)
|
||||
{
|
||||
return name.empty()
|
||||
|| name[0] == '\0'
|
||||
@ -426,401 +191,13 @@ bool is_ignored_filename(std::string_view name)
|
||||
|| name == "..";
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// volume_header ctor
|
||||
// validate_filename
|
||||
//-------------------------------------------------
|
||||
|
||||
volume_header::volume_header(fsblk_t::block_t &&block)
|
||||
: m_block(std::move(block))
|
||||
bool coco_os9_impl::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();
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// volume_header::name
|
||||
//-------------------------------------------------
|
||||
|
||||
std::string 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
|
||||
//-------------------------------------------------
|
||||
|
||||
file_header::file_header(fsblk_t::block_t &&block)
|
||||
: m_block(std::move(block))
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// file_header::creation_date
|
||||
//-------------------------------------------------
|
||||
|
||||
util::arbitrary_datetime file_header::creation_date() const
|
||||
{
|
||||
return from_os9_date(m_block.r24b(13));
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// file_header::metadata
|
||||
//-------------------------------------------------
|
||||
|
||||
meta_data 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 file_header::get_sector_map_entry_count() const
|
||||
{
|
||||
return (m_block.size() - 16) / 5;
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// file_header::get_sector_map_entry
|
||||
//-------------------------------------------------
|
||||
|
||||
void 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
|
||||
//-------------------------------------------------
|
||||
|
||||
impl::impl(fsblk_t &blockdev, volume_header &&header)
|
||||
: filesystem_t(blockdev, 256)
|
||||
, m_volume_header(std::move(header))
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// impl::metadata
|
||||
//-------------------------------------------------
|
||||
|
||||
meta_data impl::metadata()
|
||||
{
|
||||
meta_data results;
|
||||
results.set(meta_name::name, m_volume_header.name());
|
||||
results.set(meta_name::creation_date, m_volume_header.creation_date());
|
||||
return results;
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// impl::root
|
||||
//-------------------------------------------------
|
||||
|
||||
filesystem_t::dir_t impl::root()
|
||||
{
|
||||
if (!m_root)
|
||||
m_root = open_directory(m_volume_header.root_dir_lsn());
|
||||
return m_root.strong();
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// impl::format
|
||||
//-------------------------------------------------
|
||||
|
||||
void impl::format(const meta_data &meta)
|
||||
{
|
||||
// for some reason, the OS-9 world favored filling with 0xE5
|
||||
m_blockdev.fill(0xE5);
|
||||
|
||||
// identify geometry info
|
||||
u8 sectors = 18; // TODO - we need a definitive technique to get the floppy geometry
|
||||
u8 heads = 1; // TODO - we need a definitive technique to get the floppy geometry
|
||||
u16 sector_bytes = 256; // TODO - we need a definitive technique to get the floppy geometry
|
||||
bool is_double_density = true; // TODO - we need a definitive technique to get the floppy geometry
|
||||
u32 tracks = m_blockdev.block_count() / sectors / heads;
|
||||
|
||||
// get attributes from metadata
|
||||
std::string volume_title = meta.get_string(meta_name::name, "UNTITLED");
|
||||
util::arbitrary_datetime creation_datetime = meta.get_date(meta_name::creation_date);
|
||||
auto [creation_os9date, creation_os9time] = to_os9_date(creation_datetime);
|
||||
|
||||
u32 lsn_count = m_blockdev.block_count();
|
||||
u16 cluster_size = 1;
|
||||
u16 owner_id = 1;
|
||||
u16 disk_id = 1;
|
||||
u8 attributes = 0;
|
||||
u32 allocation_bitmap_bits = lsn_count / cluster_size;
|
||||
u32 allocation_bitmap_lsns = (allocation_bitmap_bits / 8 + sector_bytes - 1) / sector_bytes;
|
||||
u8 format_flags = ((heads > 1) ? 0x01 : 0x00)
|
||||
| (is_double_density ? 0x02 : 0x00);
|
||||
|
||||
// volume header
|
||||
auto volume_header = m_blockdev.get(0);
|
||||
volume_header.fill(0x00);
|
||||
volume_header.w24b(0, lsn_count); // DD.TOT - total secctors
|
||||
volume_header.w8(3, sectors); // DD.TKS - track size in sectors
|
||||
volume_header.w16b(4, (allocation_bitmap_bits + 7) / 8); // DD.MAP - allocation bitmap in bytes
|
||||
volume_header.w16b(6, cluster_size); // DD.BIT - cluster size
|
||||
volume_header.w24b(8, 1 + allocation_bitmap_lsns); // DD.DIR - root directory LSN
|
||||
volume_header.w16b(11, owner_id); // DD.OWN - owner ID
|
||||
volume_header.w8(13, attributes); // DD.ATT - Dattributes
|
||||
volume_header.w16b(14, disk_id); // DD.DSK - disk ID
|
||||
volume_header.w8(16, format_flags); // DD.FMT - format flags
|
||||
volume_header.w16b(17, sectors); // DD.SPT - sectors per track
|
||||
volume_header.w24b(26, creation_os9date); // DD.DAT - date of creation
|
||||
volume_header.w16b(29, creation_os9time); // DD.DAT - time of creation
|
||||
volume_header.wstr(31, to_os9_string(volume_title, 32)); // DD.NAM - title
|
||||
volume_header.w16b(103, sector_bytes / 256); // sector bytes
|
||||
|
||||
// path descriptor options
|
||||
volume_header.w8(0x3f + 0x00, 1); // device class
|
||||
volume_header.w8(0x3f + 0x01, 1); // drive number
|
||||
volume_header.w8(0x3f + 0x03, 0x20); // device type
|
||||
volume_header.w8(0x3f + 0x04, 1); // density capability
|
||||
volume_header.w16b(0x3f + 0x05, tracks); // number of tracks
|
||||
volume_header.w8(0x3f + 0x07, heads); // number of sides
|
||||
volume_header.w16b(0x3f + 0x09, sectors); // sectors per track
|
||||
volume_header.w16b(0x3f + 0x0b, sectors); // sectors on track zero
|
||||
volume_header.w8(0x3f + 0x0d, 3); // sector interleave factor
|
||||
volume_header.w8(0x3f + 0x0e, 8); // default sectors per allocation
|
||||
|
||||
// allocation bitmap
|
||||
u32 total_allocated_sectors = 1 + allocation_bitmap_lsns + 1 + 8;
|
||||
std::vector<u8> abblk_bytes;
|
||||
abblk_bytes.resize(sector_bytes);
|
||||
for (u32 i = 0; i < allocation_bitmap_lsns; i++)
|
||||
{
|
||||
for (u32 j = 0; j < sector_bytes; j++)
|
||||
{
|
||||
u32 pos = (i * sector_bytes + j) * 8;
|
||||
if (pos + 8 < total_allocated_sectors)
|
||||
abblk_bytes[j] = 0xFF;
|
||||
else if (pos >= total_allocated_sectors)
|
||||
abblk_bytes[j] = 0x00;
|
||||
else
|
||||
abblk_bytes[j] = ~((1 << (8 - total_allocated_sectors + pos)) - 1);
|
||||
}
|
||||
|
||||
auto abblk = m_blockdev.get(1 + i);
|
||||
abblk.copy(0, abblk_bytes.data(), sector_bytes);
|
||||
}
|
||||
|
||||
// root directory header
|
||||
auto roothdr_blk = m_blockdev.get(1 + allocation_bitmap_lsns);
|
||||
roothdr_blk.fill(0x00);
|
||||
roothdr_blk.w8(0x00, 0xBF);
|
||||
roothdr_blk.w8(0x01, 0x00);
|
||||
roothdr_blk.w8(0x02, 0x00);
|
||||
roothdr_blk.w24b(0x03, creation_os9date);
|
||||
roothdr_blk.w16b(0x06, creation_os9time);
|
||||
roothdr_blk.w8(0x08, 0x01);
|
||||
roothdr_blk.w8(0x09, 0x00);
|
||||
roothdr_blk.w8(0x0A, 0x00);
|
||||
roothdr_blk.w8(0x0B, 0x00);
|
||||
roothdr_blk.w8(0x0C, 0x40);
|
||||
roothdr_blk.w24b(0x0D, creation_os9date);
|
||||
roothdr_blk.w24b(0x10, 1 + allocation_bitmap_lsns + 1);
|
||||
roothdr_blk.w16b(0x13, 8);
|
||||
|
||||
// root directory data
|
||||
auto rootdata_blk = m_blockdev.get(1 + allocation_bitmap_lsns + 1);
|
||||
rootdata_blk.fill(0x00);
|
||||
rootdata_blk.w8(0x00, 0x2E);
|
||||
rootdata_blk.w8(0x01, 0xAE);
|
||||
rootdata_blk.w8(0x1F, 1 + allocation_bitmap_lsns);
|
||||
rootdata_blk.w8(0x20, 0xAE);
|
||||
rootdata_blk.w8(0x3F, 1 + allocation_bitmap_lsns);
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// impl::open_directory
|
||||
//-------------------------------------------------
|
||||
|
||||
impl::directory *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> 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
|
||||
//-------------------------------------------------
|
||||
|
||||
impl::file::file(impl &i, file_header &&file_header)
|
||||
: m_fs(i)
|
||||
, m_file_header(std::move(file_header))
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// file::drop_weak_references
|
||||
//-------------------------------------------------
|
||||
|
||||
void impl::file::drop_weak_references()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// file::metadata
|
||||
//-------------------------------------------------
|
||||
|
||||
meta_data impl::file::metadata()
|
||||
{
|
||||
return m_file_header.metadata();
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// file::read_all
|
||||
//-------------------------------------------------
|
||||
|
||||
std::vector<u8> impl::file::read_all()
|
||||
{
|
||||
return m_fs.read_file_data(m_file_header);
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// directory ctor
|
||||
//-------------------------------------------------
|
||||
|
||||
impl::directory::directory(impl &i, file_header &&file_header)
|
||||
: m_fs(i)
|
||||
, m_file_header(std::move(file_header))
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// directory::drop_weak_references
|
||||
//-------------------------------------------------
|
||||
|
||||
void impl::directory::drop_weak_references()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// directory::metadata
|
||||
//-------------------------------------------------
|
||||
|
||||
meta_data impl::directory::metadata()
|
||||
{
|
||||
return m_file_header.metadata();
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// directory::contents
|
||||
//-------------------------------------------------
|
||||
|
||||
std::vector<dir_entry> 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 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 impl::directory::dir_get(u64 key)
|
||||
{
|
||||
return dir_t(m_fs.open_directory(u32(key)));
|
||||
}
|
||||
|
||||
};
|
||||
|
@ -17,104 +17,27 @@
|
||||
#include <optional>
|
||||
#include <string_view>
|
||||
|
||||
namespace fs {
|
||||
const coco_rsdos_image COCO_RSDOS;
|
||||
};
|
||||
|
||||
using namespace fs;
|
||||
|
||||
namespace fs { const coco_rsdos_image COCO_RSDOS; }
|
||||
|
||||
namespace {
|
||||
|
||||
class impl : public filesystem_t {
|
||||
class coco_rsdos_impl : public filesystem_t {
|
||||
public:
|
||||
class root_dir : public idir_t {
|
||||
public:
|
||||
root_dir(impl &i) : m_fs(i) {}
|
||||
virtual ~root_dir() = default;
|
||||
coco_rsdos_impl(fsblk_t &blockdev);
|
||||
virtual ~coco_rsdos_impl() = default;
|
||||
|
||||
virtual void drop_weak_references() override;
|
||||
virtual meta_data metadata() override;
|
||||
virtual std::vector<dir_entry> contents() override;
|
||||
virtual file_t file_get(u64 key) override;
|
||||
virtual dir_t dir_get(u64 key) override;
|
||||
|
||||
private:
|
||||
impl &m_fs;
|
||||
};
|
||||
|
||||
struct rsdos_dirent
|
||||
{
|
||||
char m_filename[11];
|
||||
u8 m_filetype;
|
||||
u8 m_asciiflag;
|
||||
u8 m_first_granule;
|
||||
u8 m_last_sector_bytes_msb;
|
||||
u8 m_last_sector_bytes_lsb;
|
||||
};
|
||||
|
||||
struct rsdos_dirent_sector
|
||||
{
|
||||
struct
|
||||
{
|
||||
rsdos_dirent m_dirent;
|
||||
u8 m_unused[16];
|
||||
} m_entries[4];
|
||||
};
|
||||
|
||||
class granule_iterator {
|
||||
public:
|
||||
granule_iterator(impl &fs, const rsdos_dirent &dirent);
|
||||
bool next(u8 &granule, u16 &byte_count);
|
||||
|
||||
private:
|
||||
fsblk_t::block_t m_granule_map;
|
||||
std::optional<u8> m_current_granule;
|
||||
u8 m_maximum_granules;
|
||||
u16 m_last_sector_bytes;
|
||||
std::bitset<256> m_visited_granules;
|
||||
};
|
||||
|
||||
class file : public ifile_t {
|
||||
public:
|
||||
file(impl &fs, rsdos_dirent &&dirent);
|
||||
virtual ~file() = default;
|
||||
|
||||
virtual void drop_weak_references() override;
|
||||
|
||||
virtual meta_data metadata() override;
|
||||
virtual std::vector<u8> read_all() override;
|
||||
|
||||
private:
|
||||
impl &m_fs;
|
||||
rsdos_dirent m_dirent;
|
||||
};
|
||||
|
||||
impl(fsblk_t &blockdev);
|
||||
virtual ~impl() = default;
|
||||
|
||||
virtual meta_data metadata() override;
|
||||
virtual dir_t root() override;
|
||||
virtual void format(const meta_data &meta) override;
|
||||
|
||||
private:
|
||||
dir_t m_root;
|
||||
|
||||
void drop_root_ref();
|
||||
fsblk_t::block_t read_sector(int track, int sector) const;
|
||||
u8 maximum_granules() const;
|
||||
static std::string get_filename_from_dirent(const rsdos_dirent &dirent);
|
||||
};
|
||||
|
||||
// methods
|
||||
bool validate_filename(std::string_view name);
|
||||
static bool validate_filename(std::string_view name);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// name
|
||||
//-------------------------------------------------
|
||||
|
||||
const char *fs::coco_rsdos_image::name() const
|
||||
const char *coco_rsdos_image::name() const
|
||||
{
|
||||
return "coco_rsdos";
|
||||
}
|
||||
@ -124,7 +47,7 @@ const char *fs::coco_rsdos_image::name() const
|
||||
// description
|
||||
//-------------------------------------------------
|
||||
|
||||
const char *fs::coco_rsdos_image::description() const
|
||||
const char *coco_rsdos_image::description() const
|
||||
{
|
||||
return "CoCo RS-DOS";
|
||||
}
|
||||
@ -134,7 +57,7 @@ const char *fs::coco_rsdos_image::description() const
|
||||
// enumerate_f
|
||||
//-------------------------------------------------
|
||||
|
||||
void fs::coco_rsdos_image::enumerate_f(floppy_enumerator &fe, u32 form_factor, const std::vector<u32> &variants) const
|
||||
void coco_rsdos_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))
|
||||
{
|
||||
@ -148,7 +71,7 @@ void fs::coco_rsdos_image::enumerate_f(floppy_enumerator &fe, u32 form_factor, c
|
||||
// can_format
|
||||
//-------------------------------------------------
|
||||
|
||||
bool fs::coco_rsdos_image::can_format() const
|
||||
bool coco_rsdos_image::can_format() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
@ -158,7 +81,7 @@ bool fs::coco_rsdos_image::can_format() const
|
||||
// can_read
|
||||
//-------------------------------------------------
|
||||
|
||||
bool fs::coco_rsdos_image::can_read() const
|
||||
bool coco_rsdos_image::can_read() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
@ -168,7 +91,7 @@ bool fs::coco_rsdos_image::can_read() const
|
||||
// can_write
|
||||
//-------------------------------------------------
|
||||
|
||||
bool fs::coco_rsdos_image::can_write() const
|
||||
bool coco_rsdos_image::can_write() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@ -178,7 +101,7 @@ bool fs::coco_rsdos_image::can_write() const
|
||||
// has_rsrc
|
||||
//-------------------------------------------------
|
||||
|
||||
bool fs::coco_rsdos_image::has_rsrc() const
|
||||
bool coco_rsdos_image::has_rsrc() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@ -188,10 +111,10 @@ bool fs::coco_rsdos_image::has_rsrc() const
|
||||
// file_meta_description
|
||||
//-------------------------------------------------
|
||||
|
||||
std::vector<meta_description> fs::coco_rsdos_image::file_meta_description() const
|
||||
std::vector<meta_description> coco_rsdos_image::file_meta_description() const
|
||||
{
|
||||
std::vector<meta_description> results;
|
||||
results.emplace_back(meta_description(meta_name::name, "", false, [](const meta_value &m) { return validate_filename(m.as_string()); }, "File name, 8.3"));
|
||||
results.emplace_back(meta_description(meta_name::name, "", false, [](const meta_value &m) { return coco_rsdos_impl::validate_filename(m.as_string()); }, "File name, 8.3"));
|
||||
results.emplace_back(meta_description(meta_name::file_type, 0, true, nullptr, "Type of the file"));
|
||||
results.emplace_back(meta_description(meta_name::ascii_flag, "B", true, nullptr, "Ascii or binary flag"));
|
||||
results.emplace_back(meta_description(meta_name::size_in_blocks, 0, true, nullptr, "Number of granules used by the file"));
|
||||
@ -204,9 +127,9 @@ std::vector<meta_description> fs::coco_rsdos_image::file_meta_description() cons
|
||||
// mount
|
||||
//-------------------------------------------------
|
||||
|
||||
std::unique_ptr<filesystem_t> fs::coco_rsdos_image::mount(fsblk_t &blockdev) const
|
||||
std::unique_ptr<filesystem_t> coco_rsdos_image::mount(fsblk_t &blockdev) const
|
||||
{
|
||||
return std::make_unique<impl>(blockdev);
|
||||
return std::make_unique<coco_rsdos_impl>(blockdev);
|
||||
}
|
||||
|
||||
|
||||
@ -214,9 +137,7 @@ std::unique_ptr<filesystem_t> fs::coco_rsdos_image::mount(fsblk_t &blockdev) con
|
||||
// validate_filename
|
||||
//-------------------------------------------------
|
||||
|
||||
namespace {
|
||||
|
||||
bool validate_filename(std::string_view name)
|
||||
bool coco_rsdos_impl::validate_filename(std::string_view name)
|
||||
{
|
||||
auto pos = name.find('.');
|
||||
auto stem_length = pos != std::string::npos ? pos : name.size();
|
||||
@ -229,321 +150,9 @@ bool validate_filename(std::string_view name)
|
||||
// impl ctor
|
||||
//-------------------------------------------------
|
||||
|
||||
impl::impl(fsblk_t &blockdev)
|
||||
coco_rsdos_impl::coco_rsdos_impl(fsblk_t &blockdev)
|
||||
: filesystem_t(blockdev, 256)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// impl::metadata
|
||||
//-------------------------------------------------
|
||||
|
||||
meta_data impl::metadata()
|
||||
{
|
||||
return meta_data();
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// impl::root
|
||||
//-------------------------------------------------
|
||||
|
||||
filesystem_t::dir_t impl::root()
|
||||
{
|
||||
if (!m_root)
|
||||
m_root = new root_dir(*this);
|
||||
return m_root.strong();
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// impl::drop_root_ref
|
||||
//-------------------------------------------------
|
||||
|
||||
void impl::drop_root_ref()
|
||||
{
|
||||
m_root = nullptr;
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// impl::format
|
||||
//-------------------------------------------------
|
||||
|
||||
void impl::format(const meta_data &meta)
|
||||
{
|
||||
// formatting RS-DOS is easy - just fill everything with 0xFF
|
||||
m_blockdev.fill(0xFF);
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// fsblk_t::block_t impl::read_sector
|
||||
//-------------------------------------------------
|
||||
|
||||
fsblk_t::block_t impl::read_sector(int track, int sector) const
|
||||
{
|
||||
// the CoCo RS-DOS world thinks in terms of tracks/sectors, but we have a block device
|
||||
// abstraction
|
||||
return m_blockdev.get(track * 18 + sector - 1);
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// impl::maximum_granules
|
||||
//-------------------------------------------------
|
||||
|
||||
u8 impl::maximum_granules() const
|
||||
{
|
||||
u32 sector_count = m_blockdev.block_count();
|
||||
u32 granule_count = (sector_count / 9) - 2;
|
||||
return granule_count <= 0xFF ? u8(granule_count) : 0xFF;
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// impl::get_filename_from_dirent
|
||||
//-------------------------------------------------
|
||||
|
||||
std::string impl::get_filename_from_dirent(const rsdos_dirent &dirent)
|
||||
{
|
||||
std::string_view stem = strtrimrightspace(std::string_view(&dirent.m_filename[0], 8));
|
||||
std::string_view ext = strtrimrightspace(std::string_view(&dirent.m_filename[8], 3));
|
||||
return util::string_format("%s.%s", stem, ext);
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// impl::root_dir::drop_weak_references
|
||||
//-------------------------------------------------
|
||||
|
||||
void impl::root_dir::drop_weak_references()
|
||||
{
|
||||
m_fs.drop_root_ref();
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// impl::root_dir::metadata
|
||||
//-------------------------------------------------
|
||||
|
||||
meta_data impl::root_dir::metadata()
|
||||
{
|
||||
return meta_data();
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// impl::root_dir::contents
|
||||
//-------------------------------------------------
|
||||
|
||||
std::vector<dir_entry> impl::root_dir::contents()
|
||||
{
|
||||
u64 key = 0;
|
||||
std::vector<dir_entry> results;
|
||||
for (int dir_sector = 3; dir_sector <= 18; dir_sector++)
|
||||
{
|
||||
// read this directory sector
|
||||
auto dir_block = m_fs.read_sector(17, dir_sector);
|
||||
const rsdos_dirent_sector §or = *reinterpret_cast<const rsdos_dirent_sector *>(dir_block.rodata());
|
||||
|
||||
// and loop through all entries
|
||||
for (const auto &ent : sector.m_entries)
|
||||
{
|
||||
// 0xFF marks the end of the directory
|
||||
if (ent.m_dirent.m_filename[0] == '\xFF')
|
||||
return results;
|
||||
|
||||
// 0x00 marks a deleted file
|
||||
if (ent.m_dirent.m_filename[0] != '\0')
|
||||
results.emplace_back(get_filename_from_dirent(ent.m_dirent), dir_entry_type::file, key);
|
||||
|
||||
key++;
|
||||
}
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// impl::root_dir::file_get
|
||||
//-------------------------------------------------
|
||||
|
||||
filesystem_t::file_t impl::root_dir::file_get(u64 key)
|
||||
{
|
||||
auto dir_block = m_fs.read_sector(17, 3 + key / 4);
|
||||
const rsdos_dirent_sector §or = *reinterpret_cast<const rsdos_dirent_sector *>(dir_block.rodata());
|
||||
const rsdos_dirent &ent = sector.m_entries[key % 4].m_dirent;
|
||||
return file_t(new file(m_fs, rsdos_dirent(ent)));
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// impl::root_dir::dir_get
|
||||
//-------------------------------------------------
|
||||
|
||||
filesystem_t::dir_t impl::root_dir::dir_get(u64 key)
|
||||
{
|
||||
throw std::logic_error("Directories not supported");
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// impl::granule_iterator ctor
|
||||
//-------------------------------------------------
|
||||
|
||||
impl::granule_iterator::granule_iterator(impl &fs, const rsdos_dirent &dirent)
|
||||
: m_granule_map(fs.read_sector(17, 2))
|
||||
, m_current_granule(dirent.m_first_granule)
|
||||
, m_maximum_granules(fs.maximum_granules())
|
||||
, m_last_sector_bytes((u16(dirent.m_last_sector_bytes_msb) << 8) | dirent.m_last_sector_bytes_lsb)
|
||||
{
|
||||
// visit the first granule
|
||||
m_visited_granules.set(dirent.m_first_granule);
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// impl::granule_iterator::next
|
||||
//-------------------------------------------------
|
||||
|
||||
bool impl::granule_iterator::next(u8 &granule, u16 &byte_count)
|
||||
{
|
||||
bool success = false;
|
||||
granule = ~0;
|
||||
byte_count = 0;
|
||||
|
||||
if (m_current_granule)
|
||||
{
|
||||
std::optional<u8> next_granule;
|
||||
const u8 *granule_map_data = m_granule_map.rodata();
|
||||
if (granule_map_data[*m_current_granule] < m_maximum_granules)
|
||||
{
|
||||
// this entry points to the next granule
|
||||
success = true;
|
||||
granule = *m_current_granule;
|
||||
byte_count = 9 * 256;
|
||||
next_granule = granule_map_data[*m_current_granule];
|
||||
|
||||
// check for cycles, which should only happen if the disk is corrupt (or not in RS-DOS format)
|
||||
if (m_visited_granules[*next_granule])
|
||||
next_granule = std::nullopt; // this is corrupt!
|
||||
else
|
||||
m_visited_granules.set(*next_granule);
|
||||
}
|
||||
else if (granule_map_data[*m_current_granule] >= 0xC0 && granule_map_data[*m_current_granule] <= 0xC9)
|
||||
{
|
||||
// this is the last granule in the file
|
||||
success = true;
|
||||
granule = *m_current_granule;
|
||||
u16 sector_count = std::max(granule_map_data[*m_current_granule], u8(0xC1)) - 0xC1;
|
||||
byte_count = sector_count * 256 + m_last_sector_bytes;
|
||||
next_granule = std::nullopt;
|
||||
}
|
||||
else
|
||||
{
|
||||
// should not happen; but we'll treat this as an EOF
|
||||
next_granule = std::nullopt;
|
||||
}
|
||||
m_current_granule = next_granule;
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// impl::file ctor
|
||||
//-------------------------------------------------
|
||||
|
||||
impl::file::file(impl &fs, rsdos_dirent &&dirent)
|
||||
: m_fs(fs)
|
||||
, m_dirent(std::move(dirent))
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// impl::file::drop_weak_references
|
||||
//-------------------------------------------------
|
||||
|
||||
void impl::file::drop_weak_references()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// impl::file::metadata
|
||||
//-------------------------------------------------
|
||||
|
||||
meta_data impl::file::metadata()
|
||||
{
|
||||
u32 file_size = 0;
|
||||
int granule_count = 0;
|
||||
|
||||
// we need to iterate on the file to determine the size and granule/block count
|
||||
u8 granule;
|
||||
u16 byte_count;
|
||||
granule_iterator iter(m_fs, m_dirent);
|
||||
while (iter.next(granule, byte_count))
|
||||
{
|
||||
granule_count++;
|
||||
file_size += byte_count;
|
||||
}
|
||||
|
||||
// turn the ASCII flag to a single character (this reflects what the user sees when doing a directory listing on a real CoCo)
|
||||
char file_type_char = 'B' + m_dirent.m_asciiflag;
|
||||
|
||||
// build the metadata and return it
|
||||
meta_data result;
|
||||
result.set(meta_name::name, get_filename_from_dirent(m_dirent));
|
||||
result.set(meta_name::file_type, m_dirent.m_filetype);
|
||||
result.set(meta_name::ascii_flag, std::string(1, file_type_char));
|
||||
result.set(meta_name::size_in_blocks, granule_count);
|
||||
result.set(meta_name::length, file_size);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// impl::file::read_all
|
||||
//-------------------------------------------------
|
||||
|
||||
std::vector<u8> impl::file::read_all()
|
||||
{
|
||||
std::vector<u8> result;
|
||||
|
||||
u8 granule;
|
||||
u16 byte_count;
|
||||
granule_iterator iter(m_fs, m_dirent);
|
||||
while (iter.next(granule, byte_count))
|
||||
{
|
||||
// resize the results
|
||||
size_t current_size = result.size();
|
||||
result.resize(current_size + byte_count);
|
||||
|
||||
// determine which track and sector this granule starts at
|
||||
int track = granule / 2 + (granule >= 34 ? 1 : 0);
|
||||
int sector = granule % 2 * 9 + 1;
|
||||
|
||||
// and read all the sectors
|
||||
while (byte_count > 0)
|
||||
{
|
||||
// read this sector
|
||||
auto block = m_fs.read_sector(track, sector);
|
||||
const u8 *data = block.rodata();
|
||||
u16 data_length = std::min(byte_count, u16(256));
|
||||
|
||||
// and append it to the results
|
||||
memcpy(result.data() + current_size, data, data_length);
|
||||
|
||||
// and advance
|
||||
current_size += data_length;
|
||||
byte_count -= data_length;
|
||||
sector++;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -46,120 +46,105 @@ namespace fs { const oric_jasmin_image ORIC_JASMIN; }
|
||||
// offset 06+ : reference to data sectors, (ff, ff) when done
|
||||
|
||||
namespace {
|
||||
class impl : public filesystem_t {
|
||||
|
||||
class oric_jasmin_impl : public filesystem_t {
|
||||
public:
|
||||
impl(fsblk_t &blockdev);
|
||||
virtual ~impl() = default;
|
||||
oric_jasmin_impl(fsblk_t &blockdev);
|
||||
virtual ~oric_jasmin_impl() = default;
|
||||
|
||||
virtual void format(const meta_data &meta) override;
|
||||
virtual meta_data metadata() override;
|
||||
virtual void metadata_change(const meta_data &info) override;
|
||||
virtual dir_t root() override;
|
||||
virtual meta_data volume_metadata() override;
|
||||
virtual err_t volume_metadata_change(const meta_data &info) override;
|
||||
virtual std::pair<err_t, meta_data> metadata(const std::vector<std::string> &path) override;
|
||||
virtual err_t metadata_change(const std::vector<std::string> &path, const meta_data &meta) override;
|
||||
|
||||
virtual std::pair<err_t, std::vector<dir_entry>> directory_contents(const std::vector<std::string> &path) override;
|
||||
virtual err_t rename(const std::vector<std::string> &opath, const std::vector<std::string> &npath) override;
|
||||
virtual err_t remove(const std::vector<std::string> &path) override;
|
||||
|
||||
virtual err_t file_create(const std::vector<std::string> &path, const meta_data &meta) override;
|
||||
|
||||
virtual std::pair<err_t, std::vector<u8>> file_read(const std::vector<std::string> &path) override;
|
||||
virtual err_t file_write(const std::vector<std::string> &path, const std::vector<u8> &data) override;
|
||||
|
||||
virtual err_t format(const meta_data &meta) override;
|
||||
|
||||
static bool validate_filename(std::string name);
|
||||
|
||||
private:
|
||||
static u32 cs_to_block(u16 ref);
|
||||
[[maybe_unused]] static u16 block_to_cs(u32 block);
|
||||
|
||||
bool ref_valid(u16 ref);
|
||||
static std::string read_file_name(const u8 *p);
|
||||
void drop_root_ref();
|
||||
|
||||
std::vector<u16> allocate_blocks(u32 count);
|
||||
void free_blocks(const std::vector<u16> &blocks);
|
||||
u32 free_block_count();
|
||||
|
||||
static std::string file_name_read(const u8 *p);
|
||||
static std::string file_name_prepare(std::string name);
|
||||
|
||||
private:
|
||||
dir_t m_root;
|
||||
|
||||
class root_dir : public idir_t {
|
||||
public:
|
||||
root_dir(impl &i) : m_fs(i) {}
|
||||
virtual ~root_dir() = default;
|
||||
|
||||
virtual void drop_weak_references() override;
|
||||
|
||||
virtual meta_data metadata() override;
|
||||
virtual void metadata_change(const meta_data &info) override;
|
||||
virtual std::vector<dir_entry> contents() override;
|
||||
virtual file_t file_get(u64 key) override;
|
||||
virtual dir_t dir_get(u64 key) override;
|
||||
virtual file_t file_create(const meta_data &info) override;
|
||||
virtual void file_delete(u64 key) override;
|
||||
|
||||
void update_file(u16 key, const u8 *entry);
|
||||
|
||||
private:
|
||||
impl &m_fs;
|
||||
|
||||
std::pair<fsblk_t::block_t, u32> get_dir_block(u64 key);
|
||||
};
|
||||
|
||||
class file : public impl::ifile_t {
|
||||
public:
|
||||
file(impl &fs, root_dir *dir, const u8 *entry, u16 key);
|
||||
virtual ~file() = default;
|
||||
|
||||
virtual void drop_weak_references() override;
|
||||
|
||||
virtual meta_data metadata() override;
|
||||
virtual void metadata_change(const meta_data &info) override;
|
||||
virtual std::vector<u8> read_all() override;
|
||||
virtual void replace(const std::vector<u8> &data) override;
|
||||
|
||||
private:
|
||||
impl &m_fs;
|
||||
root_dir *m_dir;
|
||||
u16 m_key;
|
||||
u8 m_entry[18];
|
||||
};
|
||||
|
||||
class system_file : public impl::ifile_t {
|
||||
public:
|
||||
system_file(impl &fs, root_dir *dir, const u8 *entry, u16 key);
|
||||
virtual ~system_file() = default;
|
||||
|
||||
virtual void drop_weak_references() override;
|
||||
|
||||
virtual meta_data metadata() override;
|
||||
virtual void metadata_change(const meta_data &info) override;
|
||||
virtual std::vector<u8> read_all() override;
|
||||
virtual void replace(const std::vector<u8> &data) override;
|
||||
|
||||
private:
|
||||
impl &m_fs;
|
||||
root_dir *m_dir;
|
||||
u16 m_key;
|
||||
u8 m_entry[18];
|
||||
};
|
||||
static bool file_is_system(const u8 *entry);
|
||||
meta_data file_metadata(const u8 *entry);
|
||||
std::tuple<fsblk_t::block_t, u32, bool> file_find(std::string name);
|
||||
};
|
||||
}
|
||||
|
||||
meta_data impl::metadata()
|
||||
{
|
||||
meta_data res;
|
||||
auto bdir = m_blockdev.get(20*17);
|
||||
int len = 8;
|
||||
while(len > 0 && bdir.rodata()[0xf8 + len - 1] == ' ')
|
||||
len--;
|
||||
|
||||
res.set(meta_name::name, bdir.rstr(0xf8, len));
|
||||
return res;
|
||||
const char *oric_jasmin_image::name() const
|
||||
{
|
||||
return "oric_jasmin";
|
||||
}
|
||||
|
||||
bool oric_jasmin_image::validate_filename(std::string name)
|
||||
const char *oric_jasmin_image::description() const
|
||||
{
|
||||
auto pos = name.find('.');
|
||||
if(pos != std::string::npos)
|
||||
return pos <= 8 && pos > 0 && name.size()-pos-1 <= 3;
|
||||
else
|
||||
return name.size() > 0 && name.size() <= 8;
|
||||
return "Oric Jasmin";
|
||||
}
|
||||
|
||||
void oric_jasmin_image::enumerate_f(floppy_enumerator &fe, u32 form_factor, const std::vector<u32> &variants) const
|
||||
{
|
||||
if(has(form_factor, variants, floppy_image::FF_3, floppy_image::DSDD))
|
||||
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(FLOPPY_ORIC_JASMIN_FORMAT, 178432, "oric_jasmin_ss", "Oric Jasmin single-sided");
|
||||
}
|
||||
|
||||
std::unique_ptr<filesystem_t> oric_jasmin_image::mount(fsblk_t &blockdev) const
|
||||
{
|
||||
return std::make_unique<oric_jasmin_impl>(blockdev);
|
||||
}
|
||||
|
||||
bool oric_jasmin_image::can_format() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool oric_jasmin_image::can_read() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool oric_jasmin_image::can_write() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool oric_jasmin_image::has_rsrc() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<meta_description> oric_jasmin_image::volume_meta_description() const
|
||||
{
|
||||
std::vector<meta_description> res;
|
||||
res.emplace_back(meta_description(meta_name::name, "UNTITLED", false, [](const meta_value &m) { return m.as_string().size() <= 8; }, "Volume name, up to 8 characters"));
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
std::vector<meta_description> oric_jasmin_image::file_meta_description() const
|
||||
{
|
||||
std::vector<meta_description> res;
|
||||
res.emplace_back(meta_description(meta_name::name, "", false, [](const meta_value &m) { return validate_filename(m.as_string()); }, "File name, 8.3"));
|
||||
res.emplace_back(meta_description(meta_name::name, "", false, [](const meta_value &m) { return oric_jasmin_impl::validate_filename(m.as_string()); }, "File name, 8.3"));
|
||||
res.emplace_back(meta_description(meta_name::loading_address, 0x501, false, [](const meta_value &m) { return m.as_number() < 0x10000; }, "Loading address of the file"));
|
||||
res.emplace_back(meta_description(meta_name::length, 0, true, nullptr, "Size of the file in bytes"));
|
||||
res.emplace_back(meta_description(meta_name::size_in_blocks, 0, true, nullptr, "Number of blocks used by the file"));
|
||||
@ -169,7 +154,48 @@ std::vector<meta_description> oric_jasmin_image::file_meta_description() const
|
||||
}
|
||||
|
||||
|
||||
void impl::format(const meta_data &meta)
|
||||
|
||||
oric_jasmin_impl::oric_jasmin_impl(fsblk_t &blockdev) : filesystem_t(blockdev, 256)
|
||||
{
|
||||
}
|
||||
|
||||
bool oric_jasmin_impl::ref_valid(u16 ref)
|
||||
{
|
||||
u8 track = ref >> 8;
|
||||
u8 sector = ref & 0xff;
|
||||
if(sector < 1 || sector > 17)
|
||||
return false;
|
||||
if(track >= m_blockdev.block_count()/17)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
u32 oric_jasmin_impl::cs_to_block(u16 ref)
|
||||
{
|
||||
u8 track = ref >> 8;
|
||||
if(track == 0xff)
|
||||
abort();
|
||||
u8 sector = ref & 0xff;
|
||||
return track * 17 + sector - 1;
|
||||
}
|
||||
|
||||
u16 oric_jasmin_impl::block_to_cs(u32 block)
|
||||
{
|
||||
u8 track = block / 17;
|
||||
u8 sector = (block % 17) + 1;
|
||||
return (track << 8) | sector;
|
||||
}
|
||||
|
||||
bool oric_jasmin_impl::validate_filename(std::string name)
|
||||
{
|
||||
auto pos = name.find('.');
|
||||
if(pos != std::string::npos)
|
||||
return pos <= 8 && pos > 0 && name.size()-pos-1 <= 3;
|
||||
else
|
||||
return name.size() > 0 && name.size() <= 8;
|
||||
}
|
||||
|
||||
err_t oric_jasmin_impl::format(const meta_data &meta)
|
||||
{
|
||||
std::string volume_name = meta.get_string(meta_name::name, "UNTITLED");
|
||||
u32 blocks = m_blockdev.block_count();
|
||||
@ -201,146 +227,33 @@ void impl::format(const meta_data &meta)
|
||||
bdir.fill(0xff);
|
||||
bdir.w16l(0, 0x0000);
|
||||
bdir.w16l(2, 0x0000);
|
||||
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
impl::impl(fsblk_t &blockdev) : filesystem_t(blockdev, 256), m_root(true)
|
||||
meta_data oric_jasmin_impl::volume_metadata()
|
||||
{
|
||||
}
|
||||
meta_data res;
|
||||
auto bdir = m_blockdev.get(20*17);
|
||||
int len = 8;
|
||||
while(len > 0 && bdir.rodata()[0xf8 + len - 1] == ' ')
|
||||
len--;
|
||||
|
||||
bool impl::ref_valid(u16 ref)
|
||||
{
|
||||
u8 track = ref >> 8;
|
||||
u8 sector = ref & 0xff;
|
||||
if(sector < 1 || sector > 17)
|
||||
return false;
|
||||
if(track >= m_blockdev.block_count()/17)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
u32 impl::cs_to_block(u16 ref)
|
||||
{
|
||||
u8 track = ref >> 8;
|
||||
u8 sector = ref & 0xff;
|
||||
return track * 17 + sector - 1;
|
||||
}
|
||||
|
||||
u16 impl::block_to_cs(u32 block)
|
||||
{
|
||||
u8 track = block / 17;
|
||||
u8 sector = (block % 17) + 1;
|
||||
return (track << 8) | sector;
|
||||
}
|
||||
|
||||
std::string impl::read_file_name(const u8 *p)
|
||||
{
|
||||
int main_len;
|
||||
for(main_len = 8; main_len > 0; main_len--)
|
||||
if(p[main_len - 1] != ' ')
|
||||
break;
|
||||
int ext_len;
|
||||
for(ext_len = 4; ext_len > 0; ext_len--)
|
||||
if(p[ext_len + 8 - 1] != ' ')
|
||||
break;
|
||||
std::string name;
|
||||
for(int i=0; i != main_len; i++)
|
||||
name += char(p[i]);
|
||||
for(int i=0; i != ext_len; i++)
|
||||
name += char(p[8 + i]);
|
||||
return name;
|
||||
}
|
||||
|
||||
|
||||
filesystem_t::dir_t impl::root()
|
||||
{
|
||||
if(!m_root)
|
||||
m_root = new root_dir(*this);
|
||||
return m_root.strong();
|
||||
}
|
||||
|
||||
void impl::drop_root_ref()
|
||||
{
|
||||
m_root = nullptr;
|
||||
}
|
||||
|
||||
void impl::root_dir::drop_weak_references()
|
||||
{
|
||||
m_fs.drop_root_ref();
|
||||
}
|
||||
|
||||
meta_data impl::root_dir::metadata()
|
||||
{
|
||||
return meta_data();
|
||||
}
|
||||
|
||||
std::vector<dir_entry> impl::root_dir::contents()
|
||||
{
|
||||
std::vector<dir_entry> res;
|
||||
|
||||
auto bdir = m_fs.m_blockdev.get(20*17+1);
|
||||
u64 id = 0;
|
||||
for(;;) {
|
||||
for(u32 i = 0; i != 14; i ++) {
|
||||
u32 off = 4 + i*18;
|
||||
u16 ref = bdir.r16b(off);
|
||||
std::string fname = read_file_name(bdir.rodata()+off+3);
|
||||
bool system = ref == 0 && id == 0 && bdir.r32b(off+0xb) == 0x2e535953;
|
||||
if(system)
|
||||
res.emplace_back(dir_entry(std::move(fname), dir_entry_type::system_file, 0));
|
||||
|
||||
else if(m_fs.ref_valid(ref))
|
||||
res.emplace_back(dir_entry(std::move(fname), dir_entry_type::file, id));
|
||||
|
||||
id++;
|
||||
}
|
||||
u16 ref = bdir.r16b(2);
|
||||
if(!ref || !m_fs.ref_valid(ref))
|
||||
break;
|
||||
bdir = m_fs.m_blockdev.get(cs_to_block(ref));
|
||||
}
|
||||
res.set(meta_name::name, bdir.rstr(0xf8, len));
|
||||
return res;
|
||||
}
|
||||
|
||||
std::pair<fsblk_t::block_t, u32> impl::root_dir::get_dir_block(u64 key)
|
||||
err_t oric_jasmin_impl::volume_metadata_change(const meta_data &meta)
|
||||
{
|
||||
auto bdir = m_fs.m_blockdev.get(20*17+1);
|
||||
while(key >= 14) {
|
||||
u16 ref = bdir.r16b(2);
|
||||
if(!ref || !m_fs.ref_valid(ref))
|
||||
throw std::invalid_argument("Incorrect file key");
|
||||
bdir = m_fs.m_blockdev.get(cs_to_block(ref));
|
||||
key -= 14;
|
||||
if(meta.has(meta_name::name)) {
|
||||
std::string volume_name = meta.get_string(meta_name::name);
|
||||
volume_name.resize(8, ' ');
|
||||
m_blockdev.get(20*17).wstr(0xf8, volume_name);
|
||||
}
|
||||
return std::pair<fsblk_t::block_t, u32>(bdir, 4 + key * 18);
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
filesystem_t::file_t impl::root_dir::file_get(u64 key)
|
||||
{
|
||||
u64 rkey = key;
|
||||
auto [bdir, off] = get_dir_block(rkey);
|
||||
u16 ref = bdir.r16b(off);
|
||||
bool system = ref == 0 && key == 0 && bdir.r32b(off+0xb) == 0x2e535953;
|
||||
if(system)
|
||||
return file_t(new system_file(m_fs, this, bdir.rodata() + off, key));
|
||||
|
||||
if(!m_fs.ref_valid(ref))
|
||||
throw std::invalid_argument("Key to deleted/non-existent file");
|
||||
return file_t(new file(m_fs, this, bdir.rodata() + off, key));
|
||||
}
|
||||
|
||||
void impl::root_dir::update_file(u16 key, const u8 *entry)
|
||||
{
|
||||
u64 rkey = key;
|
||||
auto [bdir, off] = get_dir_block(rkey);
|
||||
bdir.copy(off, entry, 18);
|
||||
}
|
||||
|
||||
filesystem_t::dir_t impl::root_dir::dir_get(u64 key)
|
||||
{
|
||||
throw std::logic_error("Directories not supported");
|
||||
}
|
||||
|
||||
std::string impl::file_name_prepare(std::string fname)
|
||||
std::string oric_jasmin_impl::file_name_prepare(std::string fname)
|
||||
{
|
||||
std::string nname;
|
||||
size_t i;
|
||||
@ -360,129 +273,198 @@ std::string impl::file_name_prepare(std::string fname)
|
||||
return nname;
|
||||
}
|
||||
|
||||
impl::file::file(impl &fs, root_dir *dir, const u8 *entry, u16 key) : m_fs(fs), m_dir(dir), m_key(key)
|
||||
std::string oric_jasmin_impl::file_name_read(const u8 *p)
|
||||
{
|
||||
memcpy(m_entry, entry, 18);
|
||||
}
|
||||
|
||||
void impl::file::drop_weak_references()
|
||||
{
|
||||
}
|
||||
|
||||
meta_data impl::file::metadata()
|
||||
{
|
||||
meta_data res;
|
||||
|
||||
res.set(meta_name::name, read_file_name(m_entry + 3));
|
||||
res.set(meta_name::locked, m_entry[2] == 'L');
|
||||
res.set(meta_name::sequential, m_entry[0xf] == 'S');
|
||||
res.set(meta_name::size_in_blocks, r16l(m_entry + 0x10));
|
||||
|
||||
u16 ref = r16b(m_entry);
|
||||
auto dblk = m_fs.m_blockdev.get(cs_to_block(ref));
|
||||
res.set(meta_name::loading_address, dblk.r16l(2));
|
||||
res.set(meta_name::length, dblk.r16l(4));
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
std::vector<u8> impl::file::read_all()
|
||||
{
|
||||
std::vector<u8> data;
|
||||
u16 ref = r16b(m_entry);
|
||||
auto iblk = m_fs.m_blockdev.get(cs_to_block(ref));
|
||||
u32 length = iblk.r16l(4);
|
||||
while(m_fs.ref_valid(ref)) {
|
||||
for(u32 pos = 6; pos != 256 && data.size() < length; pos += 2) {
|
||||
u16 dref = iblk.r16b(pos);
|
||||
if(!m_fs.ref_valid(dref))
|
||||
goto done;
|
||||
auto dblk = m_fs.m_blockdev.get(cs_to_block(dref));
|
||||
u32 dpos = data.size();
|
||||
data.resize(dpos + 256);
|
||||
memcpy(data.data() + dpos, dblk.rodata(), 256);
|
||||
if(data.size() >= length)
|
||||
goto done;
|
||||
}
|
||||
ref = iblk.r16b(2);
|
||||
if(!m_fs.ref_valid(ref))
|
||||
int main_len;
|
||||
for(main_len = 8; main_len > 0; main_len--)
|
||||
if(p[main_len - 1] != ' ')
|
||||
break;
|
||||
iblk = m_fs.m_blockdev.get(cs_to_block(ref));
|
||||
}
|
||||
done:
|
||||
data.resize(length);
|
||||
return data;
|
||||
int ext_len;
|
||||
for(ext_len = 4; ext_len > 0; ext_len--)
|
||||
if(p[ext_len + 8 - 1] != ' ')
|
||||
break;
|
||||
std::string name;
|
||||
for(int i=0; i != main_len; i++)
|
||||
name += char(p[i]);
|
||||
for(int i=0; i != ext_len; i++)
|
||||
name += char(p[8 + i]);
|
||||
return name;
|
||||
}
|
||||
|
||||
impl::system_file::system_file(impl &fs, root_dir *dir, const u8 *entry, u16 key) : m_fs(fs), m_dir(dir), m_key(key)
|
||||
bool oric_jasmin_impl::file_is_system(const u8 *entry)
|
||||
{
|
||||
memcpy(m_entry, entry, 18);
|
||||
u16 ref = r16b(entry);
|
||||
return ref == 0 && r32b(entry+0xb) == 0x2e535953;
|
||||
}
|
||||
|
||||
void impl::system_file::drop_weak_references()
|
||||
{
|
||||
}
|
||||
|
||||
meta_data impl::system_file::metadata()
|
||||
meta_data oric_jasmin_impl::file_metadata(const u8 *entry)
|
||||
{
|
||||
meta_data res;
|
||||
|
||||
res.set(meta_name::name, read_file_name(m_entry + 3));
|
||||
res.set(meta_name::locked, m_entry[2] == 'L');
|
||||
res.set(meta_name::sequential, m_entry[0xf] == 'S');
|
||||
res.set(meta_name::size_in_blocks, r16l(m_entry + 0x10));
|
||||
res.set(meta_name::length, 0x3e00);
|
||||
res.set(meta_name::name, file_name_read(entry + 3));
|
||||
res.set(meta_name::locked, entry[2] == 'L');
|
||||
res.set(meta_name::sequential, entry[0xf] == 'S');
|
||||
res.set(meta_name::size_in_blocks, r16l(entry + 0x10));
|
||||
|
||||
bool sys = file_is_system(entry);
|
||||
if(sys)
|
||||
res.set(meta_name::length, 0x3e00);
|
||||
|
||||
else {
|
||||
u16 ref = r16b(entry);
|
||||
auto dblk = m_blockdev.get(cs_to_block(ref));
|
||||
res.set(meta_name::loading_address, dblk.r16l(2));
|
||||
res.set(meta_name::length, dblk.r16l(4));
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
std::vector<u8> impl::system_file::read_all()
|
||||
std::tuple<fsblk_t::block_t, u32, bool> oric_jasmin_impl::file_find(std::string name)
|
||||
{
|
||||
std::vector<u8> data(0x3e00);
|
||||
for(u32 i = 0; i != 62; i++) {
|
||||
auto dblk = m_fs.m_blockdev.get(i);
|
||||
memcpy(data.data() + 256 * i, dblk.rodata(), 256);
|
||||
name = file_name_prepare(name);
|
||||
auto bdir = m_blockdev.get(20*17+1);
|
||||
for(;;) {
|
||||
for(u32 i = 0; i != 14; i ++) {
|
||||
u32 off = 4 + i*18;
|
||||
u16 fref = bdir.r16b(off);
|
||||
if(ref_valid(fref) || file_is_system(bdir.rodata()+off)) {
|
||||
if(memcmp(bdir.rodata() + off + 3, name.data(), 12)) {
|
||||
bool sys = file_is_system(bdir.rodata() + off);
|
||||
return std::make_tuple(bdir, off, sys);
|
||||
}
|
||||
}
|
||||
}
|
||||
u16 ref = bdir.r16b(2);
|
||||
if(!ref || !ref_valid(ref))
|
||||
return std::make_tuple(bdir, 0U, false);
|
||||
|
||||
bdir = m_blockdev.get(cs_to_block(ref));
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
impl::file_t impl::root_dir::file_create(const meta_data &info)
|
||||
std::pair<err_t, meta_data> oric_jasmin_impl::metadata(const std::vector<std::string> &path)
|
||||
{
|
||||
if(path.size() != 1)
|
||||
return std::make_pair(ERR_NOT_FOUND, meta_data());
|
||||
|
||||
auto [bdir, off, sys] = file_find(path[0]);
|
||||
if(!off)
|
||||
return std::make_pair(ERR_NOT_FOUND, meta_data());
|
||||
|
||||
return std::make_pair(ERR_OK, file_metadata(bdir.rodata() + off));
|
||||
}
|
||||
|
||||
err_t oric_jasmin_impl::metadata_change(const std::vector<std::string> &path, const meta_data &meta)
|
||||
{
|
||||
if(path.size() != 1)
|
||||
return ERR_NOT_FOUND;
|
||||
|
||||
auto [bdir, off, sys] = file_find(path[0]);
|
||||
if(!off)
|
||||
return ERR_NOT_FOUND;
|
||||
|
||||
u8 *entry = bdir.data() + off;
|
||||
if(meta.has(meta_name::locked))
|
||||
w8 (entry+0x02, meta.get_flag(meta_name::locked) ? 'L' : 'U');
|
||||
if(meta.has(meta_name::name))
|
||||
wstr(entry+0x03, file_name_prepare(meta.get_string(meta_name::name)));
|
||||
if(meta.has(meta_name::sequential))
|
||||
w8 (entry+0x0f, meta.get_flag(meta_name::sequential) ? 'D' : 'S');
|
||||
if(!sys && meta.has(meta_name::loading_address))
|
||||
m_blockdev.get(cs_to_block(r16b(entry))).w16l(2, meta.get_number(meta_name::loading_address));
|
||||
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
std::pair<err_t, std::vector<dir_entry>> oric_jasmin_impl::directory_contents(const std::vector<std::string> &path)
|
||||
{
|
||||
std::pair<err_t, std::vector<dir_entry>> res;
|
||||
|
||||
if(path.size() != 0) {
|
||||
res.first = ERR_NOT_FOUND;
|
||||
return res;
|
||||
}
|
||||
|
||||
res.first = ERR_OK;
|
||||
|
||||
auto bdir = m_blockdev.get(20*17+1);
|
||||
for(;;) {
|
||||
for(u32 i = 0; i != 14; i ++) {
|
||||
u32 off = 4 + i*18;
|
||||
u16 fref = bdir.r16b(off);
|
||||
if(ref_valid(fref) || file_is_system(bdir.rodata()+off)) {
|
||||
meta_data meta = file_metadata(bdir.rodata()+off);
|
||||
res.second.emplace_back(dir_entry(dir_entry_type::file, meta));
|
||||
}
|
||||
}
|
||||
u16 ref = bdir.r16b(2);
|
||||
if(!ref || !ref_valid(ref))
|
||||
break;
|
||||
bdir = m_blockdev.get(cs_to_block(ref));
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
err_t oric_jasmin_impl::rename(const std::vector<std::string> &opath, const std::vector<std::string> &npath)
|
||||
{
|
||||
if(opath.size() != 1 || npath.size() != 1)
|
||||
return ERR_NOT_FOUND;
|
||||
|
||||
auto [bdir, off, sys] = file_find(opath[0]);
|
||||
if(!off)
|
||||
return ERR_NOT_FOUND;
|
||||
|
||||
wstr(bdir.data() + off + 0x03, file_name_prepare(npath[0]));
|
||||
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
err_t oric_jasmin_impl::remove(const std::vector<std::string> &path)
|
||||
{
|
||||
return ERR_UNSUPPORTED;
|
||||
}
|
||||
|
||||
err_t oric_jasmin_impl::file_create(const std::vector<std::string> &path, const meta_data &meta)
|
||||
{
|
||||
if(path.size() != 0)
|
||||
return ERR_NOT_FOUND;
|
||||
|
||||
// One block of sector list, one block of data
|
||||
u32 nb = 2;
|
||||
|
||||
// Find the key for the next entry, increase nb if needed
|
||||
auto bdir = m_fs.m_blockdev.get(20*17+1);
|
||||
auto bdir = m_blockdev.get(20*17+1);
|
||||
u64 id = 0;
|
||||
for(;;) {
|
||||
for(u32 i = 0; i != 14; i ++) {
|
||||
u32 off = 4 + i*18;
|
||||
u16 ref = bdir.r16b(off);
|
||||
if(!m_fs.ref_valid(ref))
|
||||
if(!ref_valid(ref))
|
||||
goto found;
|
||||
id++;
|
||||
}
|
||||
u16 ref = bdir.r16b(2);
|
||||
if(!ref || !m_fs.ref_valid(ref)) {
|
||||
if(!ref || !ref_valid(ref)) {
|
||||
nb ++;
|
||||
break;
|
||||
}
|
||||
bdir = m_fs.m_blockdev.get(cs_to_block(ref));
|
||||
bdir = m_blockdev.get(cs_to_block(ref));
|
||||
}
|
||||
found:
|
||||
auto block = m_fs.allocate_blocks(nb);
|
||||
auto block = allocate_blocks(nb);
|
||||
if(block.empty())
|
||||
return nullptr;
|
||||
return ERR_NO_SPACE;
|
||||
|
||||
auto sblk = m_fs.m_blockdev.get(cs_to_block(block[0]));
|
||||
auto sblk = m_blockdev.get(cs_to_block(block[0]));
|
||||
sblk.w16b(0, 0xff00); // Next sector
|
||||
sblk.w16l(2, info.get_number(meta_name::loading_address, 0x500));
|
||||
sblk.w16l(2, meta.get_number(meta_name::loading_address, 0x500));
|
||||
sblk.w16l(4, 0); // Length
|
||||
sblk.w16b(6, block[1]); // Data block
|
||||
|
||||
if(nb == 3) {
|
||||
bdir.w16l(0, block[2]); // Link to the next directory sector
|
||||
bdir = m_fs.m_blockdev.get(cs_to_block(block[2]));
|
||||
bdir = m_blockdev.get(cs_to_block(block[2]));
|
||||
bdir.fill(0xff);
|
||||
bdir.w16l(0, block[2]); // Reference to itself
|
||||
bdir.w16l(2, 0xff00); // No next directory sector
|
||||
@ -490,128 +472,138 @@ impl::file_t impl::root_dir::file_create(const meta_data &info)
|
||||
|
||||
u32 off = 4 + (id % 14) * 18;
|
||||
bdir.w16b(off+0x00, block[0]); // First (and only) sector in the sector list
|
||||
bdir.w8 (off+0x02, info.get_flag(meta_name::locked, false) ? 'L' : 'U');
|
||||
bdir.wstr(off+0x03, file_name_prepare(info.get_string(meta_name::name, "")));
|
||||
bdir.w8 (off+0x0f, info.get_flag(meta_name::sequential, true) ? 'S' : 'D');
|
||||
bdir.w8 (off+0x02, meta.get_flag(meta_name::locked, false) ? 'L' : 'U');
|
||||
bdir.wstr(off+0x03, file_name_prepare(meta.get_string(meta_name::name, "")));
|
||||
bdir.w8 (off+0x0f, meta.get_flag(meta_name::sequential, true) ? 'S' : 'D');
|
||||
bdir.w16l(off+0x10, 2); // 2 sectors for an empty file
|
||||
|
||||
return file_t(new file(m_fs, this, bdir.rodata() + off, id));
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void impl::root_dir::file_delete(u64 key)
|
||||
std::pair<err_t, std::vector<u8>> oric_jasmin_impl::file_read(const std::vector<std::string> &path)
|
||||
{
|
||||
}
|
||||
std::vector<u8> data;
|
||||
|
||||
void impl::file::replace(const std::vector<u8> &data)
|
||||
{
|
||||
u32 cur_ns = r16l(m_entry + 0x10);
|
||||
// Data sectors first
|
||||
u32 need_ns = (data.size() + 255) / 256;
|
||||
if(need_ns == 0)
|
||||
need_ns = 1;
|
||||
// Add the sector list sectors, 125 entries/sector
|
||||
need_ns += (need_ns + 124)/125;
|
||||
if(path.size() != 1)
|
||||
return std::make_pair(ERR_NOT_FOUND, data);
|
||||
|
||||
// Enough space?
|
||||
if(cur_ns < need_ns && m_fs.free_block_count() < need_ns - cur_ns)
|
||||
return;
|
||||
auto [bdir, off, sys] = file_find(path[0]);
|
||||
if(!off)
|
||||
return std::make_pair(ERR_NOT_FOUND, data);
|
||||
|
||||
u16 load_address = 0;
|
||||
std::vector<u16> tofree;
|
||||
u16 iref = r16b(m_entry);
|
||||
for(u32 i=0; i < cur_ns; i += 125+1) {
|
||||
auto iblk = m_fs.m_blockdev.get(cs_to_block(iref));
|
||||
if(!i)
|
||||
load_address = iblk.r16l(2);
|
||||
tofree.push_back(iref);
|
||||
for(u32 j=0; j != 125 && i+j+1 != cur_ns; j++)
|
||||
tofree.push_back(iblk.r16b(6+2*j));
|
||||
iref = iblk.r16b(2);
|
||||
}
|
||||
m_fs.free_blocks(tofree);
|
||||
|
||||
std::vector<u16> blocks = m_fs.allocate_blocks(need_ns);
|
||||
for(u32 i=0; i < need_ns; i += 125+1) {
|
||||
auto iblk = m_fs.m_blockdev.get(cs_to_block(blocks[i]));
|
||||
iblk.fill(0xff);
|
||||
if(!i) {
|
||||
iblk.w16l(2, load_address);
|
||||
iblk.w16l(4, data.size());
|
||||
if(sys) {
|
||||
data.resize(0x3e00);
|
||||
for(u32 i = 0; i != 62; i++) {
|
||||
auto dblk = m_blockdev.get(i);
|
||||
memcpy(data.data() + 256 * i, dblk.rodata(), 256);
|
||||
}
|
||||
if(i + 126 < need_ns)
|
||||
iblk.w16b(0, blocks[i+126]);
|
||||
else
|
||||
iblk.w16b(0, 0xff00);
|
||||
|
||||
for(u32 j=0; j != 125 && i+j+1 != need_ns; j++) {
|
||||
u32 dpos = 256 * (j + i/126*125);
|
||||
u32 size = data.size() - dpos;
|
||||
iblk.w16b(6+j*2, blocks[i+j+1]);
|
||||
auto dblk = m_fs.m_blockdev.get(cs_to_block(blocks[i+j+1]));
|
||||
if(size >= 256)
|
||||
dblk.copy(0, data.data() + dpos, 256);
|
||||
else {
|
||||
dblk.copy(0, data.data() + dpos, size);
|
||||
dblk.fill(size, 0x55, 256-size);
|
||||
} else {
|
||||
const u8 *entry = bdir.rodata() + off;
|
||||
u16 ref = r16b(entry);
|
||||
auto iblk = m_blockdev.get(cs_to_block(ref));
|
||||
u32 length = iblk.r16l(4);
|
||||
while(ref_valid(ref)) {
|
||||
for(u32 pos = 6; pos != 256 && data.size() < length; pos += 2) {
|
||||
u16 dref = iblk.r16b(pos);
|
||||
if(!ref_valid(dref))
|
||||
goto done;
|
||||
auto dblk = m_blockdev.get(cs_to_block(dref));
|
||||
u32 dpos = data.size();
|
||||
data.resize(dpos + 256);
|
||||
memcpy(data.data() + dpos, dblk.rodata(), 256);
|
||||
if(data.size() >= length)
|
||||
goto done;
|
||||
}
|
||||
ref = iblk.r16b(2);
|
||||
if(!ref_valid(ref))
|
||||
break;
|
||||
iblk = m_blockdev.get(cs_to_block(ref));
|
||||
}
|
||||
done:
|
||||
data.resize(length);
|
||||
}
|
||||
|
||||
return std::make_pair(ERR_OK, data);
|
||||
}
|
||||
|
||||
err_t oric_jasmin_impl::file_write(const std::vector<std::string> &path, const std::vector<u8> &data)
|
||||
{
|
||||
if(path.size() != 1)
|
||||
return ERR_NOT_FOUND;
|
||||
|
||||
auto [bdir, off, sys] = file_find(path[0]);
|
||||
if(!off)
|
||||
return ERR_NOT_FOUND;
|
||||
|
||||
if(sys) {
|
||||
if(data.size() != 0x3e00)
|
||||
return ERR_INVALID;
|
||||
|
||||
for(u32 i=0; i != 0x3e; i++)
|
||||
m_blockdev.get(i).copy(0, data.data() + i * 256, 256);
|
||||
|
||||
} else {
|
||||
u8 *entry = bdir.data() + off;
|
||||
u32 cur_ns = r16l(entry + 0x10);
|
||||
// Data sectors first
|
||||
u32 need_ns = (data.size() + 255) / 256;
|
||||
if(need_ns == 0)
|
||||
need_ns = 1;
|
||||
// Add the sector list sectors, 125 entries/sector
|
||||
need_ns += (need_ns + 124)/125;
|
||||
|
||||
// Enough space?
|
||||
if(cur_ns < need_ns && free_block_count() < need_ns - cur_ns)
|
||||
return ERR_NO_SPACE;
|
||||
|
||||
u16 load_address = 0;
|
||||
std::vector<u16> tofree;
|
||||
u16 iref = r16b(entry);
|
||||
for(u32 i=0; i < cur_ns; i += 125+1) {
|
||||
auto iblk = m_blockdev.get(cs_to_block(iref));
|
||||
if(!i)
|
||||
load_address = iblk.r16l(2);
|
||||
tofree.push_back(iref);
|
||||
for(u32 j=0; j != 125 && i+j+1 != cur_ns; j++)
|
||||
tofree.push_back(iblk.r16b(6+2*j));
|
||||
iref = iblk.r16b(2);
|
||||
}
|
||||
free_blocks(tofree);
|
||||
|
||||
std::vector<u16> blocks = allocate_blocks(need_ns);
|
||||
for(u32 i=0; i < need_ns; i += 125+1) {
|
||||
auto iblk = m_blockdev.get(cs_to_block(blocks[i]));
|
||||
iblk.fill(0xff);
|
||||
if(!i) {
|
||||
iblk.w16l(2, load_address);
|
||||
iblk.w16l(4, data.size());
|
||||
}
|
||||
if(i + 126 < need_ns)
|
||||
iblk.w16b(0, blocks[i+126]);
|
||||
else
|
||||
iblk.w16b(0, 0xff00);
|
||||
|
||||
for(u32 j=0; j != 125 && i+j+1 != need_ns; j++) {
|
||||
u32 dpos = 256 * (j + i/126*125);
|
||||
u32 size = data.size() - dpos;
|
||||
iblk.w16b(6+j*2, blocks[i+j+1]);
|
||||
auto dblk = m_blockdev.get(cs_to_block(blocks[i+j+1]));
|
||||
if(size >= 256)
|
||||
dblk.copy(0, data.data() + dpos, 256);
|
||||
else {
|
||||
dblk.copy(0, data.data() + dpos, size);
|
||||
dblk.fill(size, 0x55, 256-size);
|
||||
}
|
||||
}
|
||||
}
|
||||
w16l(entry + 0x10, need_ns);
|
||||
w16b(entry + 0x00, blocks[0]);
|
||||
}
|
||||
w16l(m_entry + 0x10, need_ns);
|
||||
w16b(m_entry + 0x00, blocks[0]);
|
||||
m_dir->update_file(m_key, m_entry);
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
void impl::root_dir::metadata_change(const meta_data &info)
|
||||
{
|
||||
}
|
||||
|
||||
void impl::metadata_change(const meta_data &info)
|
||||
{
|
||||
if(info.has(meta_name::name)) {
|
||||
std::string volume_name = info.get_string(meta_name::name);
|
||||
volume_name.resize(8, ' ');
|
||||
m_blockdev.get(20*17).wstr(0xf8, volume_name);
|
||||
}
|
||||
}
|
||||
|
||||
void impl::file::metadata_change(const meta_data &info)
|
||||
{
|
||||
if(info.has(meta_name::locked))
|
||||
w8 (m_entry+0x02, info.get_flag(meta_name::locked) ? 'L' : 'U');
|
||||
if(info.has(meta_name::name))
|
||||
wstr(m_entry+0x03, file_name_prepare(info.get_string(meta_name::name)));
|
||||
if(info.has(meta_name::sequential))
|
||||
w8 (m_entry+0x0f, info.get_flag(meta_name::sequential) ? 'D' : 'S');
|
||||
if(info.has(meta_name::loading_address))
|
||||
m_fs.m_blockdev.get(cs_to_block(r16b(m_entry))).w16l(2, info.get_number(meta_name::loading_address));
|
||||
m_dir->update_file(m_key, m_entry);
|
||||
}
|
||||
|
||||
void impl::system_file::replace(const std::vector<u8> &data)
|
||||
{
|
||||
if(data.size() != 0x3e00)
|
||||
return;
|
||||
|
||||
for(u32 i=0; i != 0x3e; i++)
|
||||
m_fs.m_blockdev.get(i).copy(0, data.data() + i * 256, 256);
|
||||
}
|
||||
|
||||
void impl::system_file::metadata_change(const meta_data &info)
|
||||
{
|
||||
if(info.has(meta_name::locked))
|
||||
w8 (m_entry+0x02, info.get_flag(meta_name::locked) ? 'L' : 'U');
|
||||
if(info.has(meta_name::name))
|
||||
wstr(m_entry+0x03, file_name_prepare(info.get_string(meta_name::name)));
|
||||
if(info.has(meta_name::sequential))
|
||||
w8 (m_entry+0x0f, info.get_flag(meta_name::sequential) ? 'S' : 'D');
|
||||
if(info.has(meta_name::loading_address))
|
||||
m_fs.m_blockdev.get(cs_to_block(r16b(m_entry))).w16l(2, info.get_number(meta_name::loading_address));
|
||||
m_dir->update_file(m_key, m_entry);
|
||||
}
|
||||
|
||||
std::vector<u16> impl::allocate_blocks(u32 count)
|
||||
std::vector<u16> oric_jasmin_impl::allocate_blocks(u32 count)
|
||||
{
|
||||
std::vector<u16> blocks;
|
||||
if(free_block_count() < count)
|
||||
@ -636,7 +628,7 @@ std::vector<u16> impl::allocate_blocks(u32 count)
|
||||
return blocks;
|
||||
}
|
||||
|
||||
void impl::free_blocks(const std::vector<u16> &blocks)
|
||||
void oric_jasmin_impl::free_blocks(const std::vector<u16> &blocks)
|
||||
{
|
||||
auto fmap = m_blockdev.get(20*17);
|
||||
for(u16 ref : blocks) {
|
||||
@ -650,7 +642,7 @@ void impl::free_blocks(const std::vector<u16> &blocks)
|
||||
}
|
||||
}
|
||||
|
||||
u32 impl::free_block_count()
|
||||
u32 oric_jasmin_impl::free_block_count()
|
||||
{
|
||||
auto fmap = m_blockdev.get(20*17);
|
||||
u32 nf = 0;
|
||||
@ -664,54 +656,3 @@ u32 impl::free_block_count()
|
||||
}
|
||||
return nf;
|
||||
}
|
||||
|
||||
const char *oric_jasmin_image::name() const
|
||||
{
|
||||
return "oric_jasmin";
|
||||
}
|
||||
|
||||
const char *oric_jasmin_image::description() const
|
||||
{
|
||||
return "Oric Jasmin";
|
||||
}
|
||||
|
||||
void oric_jasmin_image::enumerate_f(floppy_enumerator &fe, u32 form_factor, const std::vector<u32> &variants) const
|
||||
{
|
||||
if(has(form_factor, variants, floppy_image::FF_3, floppy_image::DSDD))
|
||||
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(FLOPPY_ORIC_JASMIN_FORMAT, 178432, "oric_jasmin_ss", "Oric Jasmin single-sided");
|
||||
}
|
||||
|
||||
std::unique_ptr<filesystem_t> oric_jasmin_image::mount(fsblk_t &blockdev) const
|
||||
{
|
||||
return std::make_unique<impl>(blockdev);
|
||||
}
|
||||
|
||||
bool oric_jasmin_image::can_format() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool oric_jasmin_image::can_read() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool oric_jasmin_image::can_write() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool oric_jasmin_image::has_rsrc() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<meta_description> oric_jasmin_image::volume_meta_description() const
|
||||
{
|
||||
std::vector<meta_description> res;
|
||||
res.emplace_back(meta_description(meta_name::name, "UNTITLED", false, [](const meta_value &m) { return m.as_string().size() <= 8; }, "Volume name, up to 8 characters"));
|
||||
|
||||
return res;
|
||||
}
|
||||
|
@ -29,8 +29,6 @@ public:
|
||||
|
||||
virtual std::vector<meta_description> volume_meta_description() const override;
|
||||
virtual std::vector<meta_description> file_meta_description() const override;
|
||||
|
||||
static bool validate_filename(std::string name);
|
||||
};
|
||||
|
||||
extern const oric_jasmin_image ORIC_JASMIN;
|
||||
|
@ -15,78 +15,28 @@ using namespace fs;
|
||||
namespace fs { const prodos_image PRODOS; }
|
||||
|
||||
namespace {
|
||||
class impl : public filesystem_t {
|
||||
class prodos_impl : public filesystem_t {
|
||||
public:
|
||||
impl(fsblk_t &blockdev);
|
||||
virtual ~impl() = default;
|
||||
prodos_impl(fsblk_t &blockdev);
|
||||
virtual ~prodos_impl() = default;
|
||||
|
||||
virtual void format(const meta_data &meta) override;
|
||||
virtual meta_data volume_metadata() override;
|
||||
virtual std::pair<err_t, meta_data> metadata(const std::vector<std::string> &path) override;
|
||||
|
||||
virtual meta_data metadata() override;
|
||||
virtual dir_t root() override;
|
||||
virtual std::pair<err_t, std::vector<dir_entry>> directory_contents(const std::vector<std::string> &path) override;
|
||||
|
||||
void drop_root_ref();
|
||||
virtual std::pair<err_t, std::vector<u8>> file_read(const std::vector<std::string> &path) override;
|
||||
virtual std::pair<err_t, std::vector<u8>> file_rsrc_read(const std::vector<std::string> &path) override;
|
||||
|
||||
static util::arbitrary_datetime prodos_to_dt(u32 date);
|
||||
std::vector<dir_entry> contents(u16 block);
|
||||
virtual err_t format(const meta_data &meta) override;
|
||||
|
||||
private:
|
||||
static const u8 boot[512];
|
||||
static util::arbitrary_datetime prodos_to_dt(u32 date);
|
||||
|
||||
dir_t m_root;
|
||||
|
||||
class root_dir : public idir_t {
|
||||
public:
|
||||
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;
|
||||
|
||||
virtual meta_data metadata() override;
|
||||
virtual std::vector<dir_entry> contents() override;
|
||||
virtual file_t file_get(u64 key) override;
|
||||
virtual dir_t dir_get(u64 key) override;
|
||||
|
||||
protected:
|
||||
impl &m_fs;
|
||||
u16 m_base_block;
|
||||
|
||||
std::pair<fsblk_t::block_t, const u8 *> get_entry_ro(u64 key);
|
||||
[[maybe_unused]] std::pair<fsblk_t::block_t, u8 *> get_entry(u64 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 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, root_dir *parent_dir);
|
||||
virtual ~file() = default;
|
||||
|
||||
virtual void drop_weak_references() override;
|
||||
|
||||
virtual meta_data metadata() override;
|
||||
virtual std::vector<u8> read_all() override;
|
||||
virtual std::vector<u8> rsrc_read_all() override;
|
||||
|
||||
private:
|
||||
impl &m_fs;
|
||||
root_dir *m_parent_dir;
|
||||
u16 m_key;
|
||||
u8 m_entry[39];
|
||||
|
||||
std::vector<u8> any_read_all(u8 type, u16 block, u32 length);
|
||||
};
|
||||
std::tuple<fsblk_t::block_t, u32> path_find_step(const std::string &name, u16 block);
|
||||
std::tuple<fsblk_t::block_t, u32, bool> path_find(const std::vector<std::string> &path);
|
||||
std::pair<err_t, std::vector<u8>> any_read(u8 type, u16 block, u32 length);
|
||||
};
|
||||
}
|
||||
|
||||
@ -101,7 +51,7 @@ const char *prodos_image::description() const
|
||||
return "Apple ProDOS";
|
||||
}
|
||||
|
||||
const u8 impl::boot[512] = {
|
||||
const u8 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,
|
||||
0xd0, 0x3a, 0xb0, 0x0e, 0xa9, 0x03, 0x8d, 0x00, 0x08, 0xe6, 0x3d, 0xa5, 0x49, 0x48, 0xa9, 0x5b,
|
||||
@ -146,7 +96,7 @@ void prodos_image::enumerate_f(floppy_enumerator &fe, u32 form_factor, const std
|
||||
|
||||
std::unique_ptr<filesystem_t> prodos_image::mount(fsblk_t &blockdev) const
|
||||
{
|
||||
return std::make_unique<impl>(blockdev);
|
||||
return std::make_unique<prodos_impl>(blockdev);
|
||||
}
|
||||
|
||||
bool prodos_image::can_format() const
|
||||
@ -215,7 +165,7 @@ std::vector<meta_description> prodos_image::directory_meta_description() const
|
||||
return res;
|
||||
}
|
||||
|
||||
void impl::format(const meta_data &meta)
|
||||
err_t prodos_impl::format(const meta_data &meta)
|
||||
{
|
||||
std::string volume_name = meta.get_string(meta_name::name, "UNTITLED");
|
||||
u32 blocks = m_blockdev.block_count();
|
||||
@ -279,13 +229,14 @@ void impl::format(const meta_data &meta)
|
||||
memset(fdata+sb, 0xff, eb-sb-1);
|
||||
}
|
||||
}
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
impl::impl(fsblk_t &blockdev) : filesystem_t(blockdev, 512), m_root(true)
|
||||
prodos_impl::prodos_impl(fsblk_t &blockdev) : filesystem_t(blockdev, 512)
|
||||
{
|
||||
}
|
||||
|
||||
util::arbitrary_datetime impl::prodos_to_dt(u32 date)
|
||||
util::arbitrary_datetime prodos_impl::prodos_to_dt(u32 date)
|
||||
{
|
||||
util::arbitrary_datetime dt;
|
||||
dt.second = 0;
|
||||
@ -300,7 +251,7 @@ util::arbitrary_datetime impl::prodos_to_dt(u32 date)
|
||||
return dt;
|
||||
}
|
||||
|
||||
meta_data impl::metadata()
|
||||
meta_data prodos_impl::volume_metadata()
|
||||
{
|
||||
meta_data res;
|
||||
auto bdir = m_blockdev.get(2);
|
||||
@ -313,200 +264,161 @@ meta_data impl::metadata()
|
||||
return res;
|
||||
}
|
||||
|
||||
filesystem_t::dir_t impl::root()
|
||||
std::pair<err_t, std::vector<dir_entry>> prodos_impl::directory_contents(const std::vector<std::string> &path)
|
||||
{
|
||||
if(!m_root)
|
||||
m_root = new root_dir(*this, 2);
|
||||
return m_root.strong();
|
||||
}
|
||||
u16 block;
|
||||
if(path.empty())
|
||||
block = 2;
|
||||
|
||||
void impl::drop_root_ref()
|
||||
{
|
||||
m_root = nullptr;
|
||||
}
|
||||
else {
|
||||
auto [blk, off, dir] = path_find(path);
|
||||
if(!off || !dir)
|
||||
return std::make_pair(ERR_NOT_FOUND, std::vector<dir_entry>());
|
||||
block = blk.r16l(off+0x11);
|
||||
}
|
||||
|
||||
|
||||
void impl::root_dir::drop_weak_references()
|
||||
{
|
||||
if(m_base_block == 2)
|
||||
m_fs.drop_root_ref();
|
||||
}
|
||||
|
||||
meta_data impl::root_dir::metadata()
|
||||
{
|
||||
return meta_data();
|
||||
}
|
||||
|
||||
std::vector<dir_entry> impl::root_dir::contents()
|
||||
{
|
||||
std::vector<dir_entry> res;
|
||||
|
||||
u16 block = m_base_block;
|
||||
u32 off = 39 + 4;
|
||||
u32 id = 1;
|
||||
do {
|
||||
auto blk = m_fs.m_blockdev.get(block);
|
||||
auto blk = m_blockdev.get(block);
|
||||
while(off < 511) {
|
||||
meta_data meta;
|
||||
u8 type = blk.r8(off);
|
||||
auto name = blk.rstr(off+1, type & 0xf);
|
||||
meta.set(meta_name::name, blk.rstr(off+1, type & 0xf));
|
||||
type >>= 4;
|
||||
if(type == 0xd)
|
||||
res.emplace_back(dir_entry(std::move(name), dir_entry_type::dir, id));
|
||||
else if(type != 0)
|
||||
res.emplace_back(dir_entry(std::move(name), dir_entry_type::file, id));
|
||||
|
||||
if(type == 5) {
|
||||
auto rootblk = m_blockdev.get(blk.r16l(off+0x11));
|
||||
meta.set(meta_name::length, rootblk.r24l(0x005));
|
||||
meta.set(meta_name::rsrc_length, rootblk.r24l(0x105));
|
||||
|
||||
} else if(type >= 1 && type <= 3)
|
||||
meta.set(meta_name::length, blk.r24l(off + 0x15));
|
||||
|
||||
res.emplace_back(dir_entry(type == 0xd ? dir_entry_type::dir : dir_entry_type::file, meta));
|
||||
off += 39;
|
||||
id ++;
|
||||
}
|
||||
block = blk.r16l(2);
|
||||
if(block >= m_fs.m_blockdev.block_count())
|
||||
if(block >= m_blockdev.block_count())
|
||||
break;
|
||||
off = 4;
|
||||
} while(block);
|
||||
return res;
|
||||
return std::make_pair(ERR_OK, res);
|
||||
}
|
||||
|
||||
|
||||
std::pair<fsblk_t::block_t, const u8 *> impl::root_dir::get_entry_ro(u64 key)
|
||||
std::tuple<fsblk_t::block_t, u32> prodos_impl::path_find_step(const std::string &name, u16 block)
|
||||
{
|
||||
std::pair<fsblk_t::block_t, const u8 *> res;
|
||||
res.first = m_fs.m_blockdev.get(m_base_block);
|
||||
while(key >= 13) {
|
||||
key -= 13;
|
||||
u16 block = res.first.r16l(2);
|
||||
if(!block || block >= m_fs.m_blockdev.block_count()) {
|
||||
res.first = nullptr;
|
||||
res.second = nullptr;
|
||||
return res;
|
||||
u32 off = 39 + 4;
|
||||
for(;;) {
|
||||
auto blk = m_blockdev.get(block);
|
||||
while(off < 511) {
|
||||
u8 type = blk.r8(off);
|
||||
if(name == blk.rstr(off+1, type & 0xf))
|
||||
return std::make_tuple(blk, off);
|
||||
off += 39;
|
||||
}
|
||||
res.first = m_fs.m_blockdev.get(block);
|
||||
block = blk.r16l(2);
|
||||
if(!block || block >= m_blockdev.block_count())
|
||||
return std::make_tuple(blk, 0U);
|
||||
off = 4;
|
||||
}
|
||||
res.second = res.first.rodata() + 4 + 39 * key;
|
||||
return res;
|
||||
}
|
||||
|
||||
std::pair<fsblk_t::block_t, u8 *> impl::root_dir::get_entry(u64 key)
|
||||
std::tuple<fsblk_t::block_t, u32, bool> prodos_impl::path_find(const std::vector<std::string> &path)
|
||||
{
|
||||
std::pair<fsblk_t::block_t, u8 *> res;
|
||||
res.first = m_fs.m_blockdev.get(m_base_block);
|
||||
while(key > 13) {
|
||||
key -= 13;
|
||||
u16 block = res.first.r16l(2);
|
||||
if(!block || block >= m_fs.m_blockdev.block_count()) {
|
||||
res.first = nullptr;
|
||||
res.second = nullptr;
|
||||
return res;
|
||||
}
|
||||
res.first = m_fs.m_blockdev.get(block);
|
||||
if(path.size() == 0)
|
||||
return std::tuple<fsblk_t::block_t, u32, bool>(fsblk_t::block_t(), 0, false);
|
||||
|
||||
u16 block = 2;
|
||||
for(u32 pathc = 0;; pathc++) {
|
||||
auto [blk, off] = path_find_step(path[pathc], block);
|
||||
if(!off)
|
||||
return std::make_tuple(blk, off, false);
|
||||
|
||||
if(pathc + 1 == path.size())
|
||||
return std::make_tuple(blk, off, (blk.r8(off) & 0xf0) == 0xd0);
|
||||
|
||||
if((blk.r8(off) & 0xf0) != 0xd0)
|
||||
return std::make_tuple(blk, 0U, false);
|
||||
|
||||
block = blk.r16l(off + 0x11);
|
||||
}
|
||||
res.second = res.first.data() + 4 + 39 * key;
|
||||
return res;
|
||||
}
|
||||
|
||||
filesystem_t::file_t impl::root_dir::file_get(u64 key)
|
||||
{
|
||||
auto [blk, entry] = get_entry_ro(key);
|
||||
if(!blk)
|
||||
throw std::out_of_range("Out-of-range key on file_get");
|
||||
u8 type = entry[0] >> 4;
|
||||
if(type == 0 || type == 4 || type > 5)
|
||||
throw std::runtime_error(util::string_format("Unhandled file type %x", type));
|
||||
return new file(m_fs, entry, key, this);
|
||||
}
|
||||
|
||||
filesystem_t::dir_t impl::root_dir::dir_get(u64 key)
|
||||
std::pair<err_t, meta_data> prodos_impl::metadata(const std::vector<std::string> &path)
|
||||
{
|
||||
auto [blk, entry] = get_entry_ro(key);
|
||||
if(!blk)
|
||||
throw std::out_of_range("Out-of-range key on dir_get");
|
||||
u8 type = entry[0] >> 4;
|
||||
if(type != 0xd)
|
||||
throw std::runtime_error(util::string_format("Unhandled directory type %x", type));
|
||||
if(path.size() == 0)
|
||||
return std::make_pair(ERR_OK, meta_data());
|
||||
|
||||
return new dir(m_fs, entry, r16l(entry+0x11), key, this);
|
||||
}
|
||||
auto [blk, off, dir] = path_find(path);
|
||||
|
||||
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;
|
||||
}
|
||||
if(!off)
|
||||
return std::make_pair(ERR_NOT_FOUND, meta_data());
|
||||
|
||||
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;
|
||||
}
|
||||
const u8 *entry = blk.rodata() + off;
|
||||
|
||||
void impl::file::drop_weak_references()
|
||||
{
|
||||
}
|
||||
|
||||
meta_data impl::file::metadata()
|
||||
{
|
||||
meta_data res;
|
||||
u8 type = r8(m_entry);
|
||||
std::string name = rstr(m_entry+1, type & 0xf);
|
||||
type >>= 4;
|
||||
res.set(meta_name::name, name);
|
||||
if(type == 5) {
|
||||
auto rootblk = m_fs.m_blockdev.get(r16l(m_entry+0x11));
|
||||
res.set(meta_name::length, rootblk.r24l(0x005));
|
||||
res.set(meta_name::rsrc_length, rootblk.r24l(0x105));
|
||||
if(dir) {
|
||||
u8 type = r8(entry);
|
||||
std::string name = rstr(entry+1, type & 0xf);
|
||||
res.set(meta_name::name, name);
|
||||
} else {
|
||||
u8 type = r8(entry);
|
||||
std::string name = rstr(entry+1, type & 0xf);
|
||||
type >>= 4;
|
||||
res.set(meta_name::name, name);
|
||||
if(type == 5) {
|
||||
auto rootblk = m_blockdev.get(r16l(entry+0x11));
|
||||
res.set(meta_name::length, rootblk.r24l(0x005));
|
||||
res.set(meta_name::rsrc_length, rootblk.r24l(0x105));
|
||||
|
||||
} else if(type >= 1 && type <= 3)
|
||||
res.set(meta_name::length, r24l(entry + 0x15));
|
||||
|
||||
else
|
||||
return std::make_pair(ERR_UNSUPPORTED, meta_data());
|
||||
}
|
||||
|
||||
} else if(type >= 1 && type <= 3)
|
||||
res.set(meta_name::length, r24l(m_entry + 0x15));
|
||||
|
||||
else
|
||||
throw std::runtime_error(util::string_format("impl::file::metadata: Unhandled file type %d", type));
|
||||
|
||||
return res;
|
||||
return std::make_pair(ERR_OK, res);
|
||||
}
|
||||
|
||||
meta_data impl::dir::metadata()
|
||||
std::pair<err_t, std::vector<u8>> prodos_impl::any_read(u8 type, u16 block, u32 length)
|
||||
{
|
||||
meta_data res;
|
||||
u8 type = r8(m_entry);
|
||||
std::string name = rstr(m_entry+1, type & 0xf);
|
||||
res.set(meta_name::name, name);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
std::vector<u8> impl::file::any_read_all(u8 type, u16 block, u32 length)
|
||||
{
|
||||
std::vector<u8> data((length + 511) & ~511);
|
||||
u32 nb = data.size()/512;
|
||||
std::pair<err_t, std::vector<u8>> data;
|
||||
data.first = ERR_OK;
|
||||
data.second.resize((length + 511) & ~511);
|
||||
u32 nb = data.second.size()/512;
|
||||
if(!nb)
|
||||
return data;
|
||||
|
||||
u8 *dst = data.data();
|
||||
u8 *end = dst + data.size();
|
||||
u8 *dst = data.second.data();
|
||||
u8 *end = dst + data.second.size();
|
||||
switch(type) {
|
||||
case 1:
|
||||
memcpy(dst, m_fs.m_blockdev.get(block).rodata(), 512);
|
||||
memcpy(dst, m_blockdev.get(block).rodata(), 512);
|
||||
dst += 512;
|
||||
break;
|
||||
|
||||
case 2: {
|
||||
auto iblk = m_fs.m_blockdev.get(block);
|
||||
auto iblk = m_blockdev.get(block);
|
||||
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);
|
||||
memcpy(dst, m_blockdev.get(blk).rodata(), 512);
|
||||
dst += 512;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case 3: {
|
||||
auto mblk = m_fs.m_blockdev.get(block);
|
||||
auto mblk = m_blockdev.get(block);
|
||||
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));
|
||||
auto iblk = m_blockdev.get(mblk.r8(idx) | (mblk.r8(idx | 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);
|
||||
memcpy(dst, m_blockdev.get(blk).rodata(), 512);
|
||||
dst += 512;
|
||||
}
|
||||
}
|
||||
@ -514,35 +426,48 @@ std::vector<u8> impl::file::any_read_all(u8 type, u16 block, u32 length)
|
||||
}
|
||||
|
||||
default:
|
||||
throw std::runtime_error(util::string_format("impl::file::get_file_blocks: unknown file type %d", type));
|
||||
data.first = ERR_UNSUPPORTED;
|
||||
data.second.clear();
|
||||
return data;
|
||||
}
|
||||
|
||||
data.resize(length);
|
||||
data.second.resize(length);
|
||||
return data;
|
||||
}
|
||||
|
||||
std::vector<u8> impl::file::read_all()
|
||||
std::pair<err_t, std::vector<u8>> prodos_impl::file_read(const std::vector<std::string> &path)
|
||||
{
|
||||
u8 type = r8(m_entry) >> 4;
|
||||
auto [blk, off, dir] = path_find(path);
|
||||
if(!off || dir)
|
||||
return std::make_pair(ERR_NOT_FOUND, std::vector<u8>());
|
||||
|
||||
const u8 *entry = blk.rodata() + off;
|
||||
u8 type = r8(entry) >> 4;
|
||||
|
||||
if(type >= 1 && type <= 3)
|
||||
return any_read_all(type, r16l(m_entry+0x11), r24l(m_entry + 0x15));
|
||||
return any_read(type, r16l(entry+0x11), r24l(entry + 0x15));
|
||||
|
||||
else if(type == 5) {
|
||||
auto kblk = m_fs.m_blockdev.get(r16l(m_entry+0x11));
|
||||
return any_read_all(kblk.r8(0x000), kblk.r16l(0x001), kblk.r24l(0x005));
|
||||
auto kblk = m_blockdev.get(r16l(entry+0x11));
|
||||
return any_read(kblk.r8(0x000), kblk.r16l(0x001), kblk.r24l(0x005));
|
||||
|
||||
} else
|
||||
throw std::runtime_error(util::string_format("impl::file::read_all: Unhandled file type %d", type));
|
||||
return std::make_pair(ERR_UNSUPPORTED, std::vector<u8>());
|
||||
}
|
||||
|
||||
std::vector<u8> impl::file::rsrc_read_all()
|
||||
std::pair<err_t, std::vector<u8>> prodos_impl::file_rsrc_read(const std::vector<std::string> &path)
|
||||
{
|
||||
u8 type = r8(m_entry) >> 4;
|
||||
auto [blk, off, dir] = path_find(path);
|
||||
if(!off || dir)
|
||||
return std::make_pair(ERR_NOT_FOUND, std::vector<u8>());
|
||||
|
||||
const u8 *entry = blk.rodata() + off;
|
||||
u8 type = r8(entry) >> 4;
|
||||
|
||||
if(type == 5) {
|
||||
auto kblk = m_fs.m_blockdev.get(r16l(m_entry+0x11));
|
||||
return any_read_all(kblk.r8(0x100), kblk.r16l(0x101), kblk.r24l(0x105));
|
||||
auto kblk = m_blockdev.get(r16l(entry+0x11));
|
||||
return any_read(kblk.r8(0x100), kblk.r16l(0x101), kblk.r24l(0x105));
|
||||
|
||||
} else
|
||||
throw std::runtime_error(util::string_format("impl::file::rsrc_blocks: Unhandled file type %d", type));
|
||||
return std::make_pair(ERR_UNSUPPORTED, std::vector<u8>());
|
||||
}
|
||||
|
@ -8,9 +8,9 @@
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
namespace fs {
|
||||
using namespace fs;
|
||||
|
||||
const vtech_image VTECH;
|
||||
namespace fs { const vtech_image VTECH; }
|
||||
|
||||
// Floppy only, format is 40 tracks, 1 head, 16 sectors numbered 0-15, 256 bytes/sector.
|
||||
// Filesystem has no subdirectories.
|
||||
@ -27,6 +27,38 @@ const vtech_image VTECH;
|
||||
// Files are stored with 126 bytes/sector, and bytes 126 and 127 are
|
||||
// track/sector of the next file data sector.
|
||||
|
||||
namespace {
|
||||
|
||||
class vtech_impl : public filesystem_t {
|
||||
public:
|
||||
vtech_impl(fsblk_t &blockdev);
|
||||
virtual ~vtech_impl() = default;
|
||||
|
||||
virtual meta_data volume_metadata() override;
|
||||
virtual err_t volume_metadata_change(const meta_data &info) override;
|
||||
virtual std::pair<err_t, meta_data> metadata(const std::vector<std::string> &path) override;
|
||||
virtual err_t metadata_change(const std::vector<std::string> &path, const meta_data &meta) override;
|
||||
|
||||
virtual std::pair<err_t, std::vector<dir_entry>> directory_contents(const std::vector<std::string> &path) override;
|
||||
virtual err_t rename(const std::vector<std::string> &opath, const std::vector<std::string> &npath) override;
|
||||
virtual err_t remove(const std::vector<std::string> &path) override;
|
||||
|
||||
virtual err_t file_create(const std::vector<std::string> &path, const meta_data &meta) override;
|
||||
|
||||
virtual std::pair<err_t, std::vector<u8>> file_read(const std::vector<std::string> &path) override;
|
||||
virtual err_t file_write(const std::vector<std::string> &path, const std::vector<u8> &data) override;
|
||||
|
||||
virtual err_t format(const meta_data &meta) override;
|
||||
|
||||
private:
|
||||
meta_data file_metadata(const u8 *entry);
|
||||
std::tuple<fsblk_t::block_t, u32> file_find(std::string name);
|
||||
std::vector<std::pair<u8, u8>> allocate_blocks(u32 count);
|
||||
void free_blocks(const std::vector<std::pair<u8, u8>> &blocks);
|
||||
u32 free_block_count();
|
||||
};
|
||||
}
|
||||
|
||||
const char *vtech_image::name() const
|
||||
{
|
||||
return "vtech";
|
||||
@ -45,7 +77,7 @@ void vtech_image::enumerate_f(floppy_enumerator &fe, u32 form_factor, const std:
|
||||
|
||||
std::unique_ptr<filesystem_t> vtech_image::mount(fsblk_t &blockdev) const
|
||||
{
|
||||
return std::make_unique<impl>(blockdev);
|
||||
return std::make_unique<vtech_impl>(blockdev);
|
||||
}
|
||||
|
||||
bool vtech_image::can_format() const
|
||||
@ -74,12 +106,6 @@ std::vector<meta_description> vtech_image::volume_meta_description() const
|
||||
return res;
|
||||
}
|
||||
|
||||
meta_data vtech_image::impl::metadata()
|
||||
{
|
||||
meta_data res;
|
||||
return res;
|
||||
}
|
||||
|
||||
std::vector<meta_description> vtech_image::file_meta_description() const
|
||||
{
|
||||
std::vector<meta_description> res;
|
||||
@ -90,45 +116,42 @@ std::vector<meta_description> vtech_image::file_meta_description() const
|
||||
return res;
|
||||
}
|
||||
|
||||
vtech_impl::vtech_impl(fsblk_t &blockdev) : filesystem_t(blockdev, 128)
|
||||
{
|
||||
}
|
||||
|
||||
void vtech_image::impl::format(const meta_data &meta)
|
||||
err_t vtech_impl::format(const meta_data &meta)
|
||||
{
|
||||
m_blockdev.fill(0);
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
vtech_image::impl::impl(fsblk_t &blockdev) : filesystem_t(blockdev, 128), m_root(true)
|
||||
{
|
||||
}
|
||||
|
||||
filesystem_t::dir_t vtech_image::impl::root()
|
||||
{
|
||||
if(!m_root)
|
||||
m_root = new root_dir(*this);
|
||||
return m_root.strong();
|
||||
}
|
||||
|
||||
void vtech_image::impl::drop_root_ref()
|
||||
{
|
||||
m_root = nullptr;
|
||||
}
|
||||
|
||||
void vtech_image::impl::root_dir::drop_weak_references()
|
||||
{
|
||||
m_fs.drop_root_ref();
|
||||
}
|
||||
|
||||
meta_data vtech_image::impl::root_dir::metadata()
|
||||
meta_data vtech_impl::volume_metadata()
|
||||
{
|
||||
return meta_data();
|
||||
}
|
||||
|
||||
std::vector<dir_entry> vtech_image::impl::root_dir::contents()
|
||||
err_t vtech_impl::volume_metadata_change(const meta_data &meta)
|
||||
{
|
||||
std::vector<dir_entry> res;
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
u64 id = 0;
|
||||
meta_data vtech_impl::file_metadata(const u8 *entry)
|
||||
{
|
||||
meta_data res;
|
||||
|
||||
res.set(meta_name::name, trim_end_spaces(rstr(entry+2, 8)));
|
||||
res.set(meta_name::basic, entry[0] == 'T');
|
||||
res.set(meta_name::loading_address, r16l(entry + 0xc));
|
||||
res.set(meta_name::length, ((r16l(entry + 0xe) - r16l(entry + 0xc) + 1) & 0xffff));
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
std::tuple<fsblk_t::block_t, u32> vtech_impl::file_find(std::string name)
|
||||
{
|
||||
for(int sect = 0; sect != 14; sect++) {
|
||||
auto bdir = m_fs.m_blockdev.get(sect);
|
||||
auto bdir = m_blockdev.get(sect);
|
||||
for(u32 i = 0; i != 8; i ++) {
|
||||
u32 off = i*16;
|
||||
u8 type = bdir.r8(off);
|
||||
@ -136,69 +159,154 @@ std::vector<dir_entry> vtech_image::impl::root_dir::contents()
|
||||
continue;
|
||||
if(bdir.r8(off+1) != ':')
|
||||
continue;
|
||||
std::string fname = trim_end_spaces(bdir.rstr(off+2, 8));
|
||||
res.emplace_back(dir_entry(std::move(fname), dir_entry_type::file, id));
|
||||
id++;
|
||||
if(trim_end_spaces(bdir.rstr(off+2, 8)) == name) {
|
||||
return std::make_tuple(bdir, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
return std::make_tuple(fsblk_t::block_t(), 0xffffffff);
|
||||
}
|
||||
|
||||
std::pair<err_t, meta_data> vtech_impl::metadata(const std::vector<std::string> &path)
|
||||
{
|
||||
if(path.size() != 1)
|
||||
return std::make_pair(ERR_NOT_FOUND, meta_data());
|
||||
|
||||
auto [bdir, off] = file_find(path[0]);
|
||||
if(off == 0xffffffff)
|
||||
return std::make_pair(ERR_NOT_FOUND, meta_data());
|
||||
|
||||
return std::make_pair(ERR_OK, file_metadata(bdir.rodata() + off));
|
||||
}
|
||||
|
||||
err_t vtech_impl::metadata_change(const std::vector<std::string> &path, const meta_data &meta)
|
||||
{
|
||||
if(path.size() != 1)
|
||||
return ERR_NOT_FOUND;
|
||||
|
||||
auto [bdir, off] = file_find(path[0]);
|
||||
if(!off)
|
||||
return ERR_NOT_FOUND;
|
||||
|
||||
u8 *entry = bdir.data() + off;
|
||||
if(meta.has(meta_name::basic))
|
||||
w8 (entry+0x0, meta.get_flag(meta_name::basic) ? 'T' : 'B');
|
||||
if(meta.has(meta_name::name)) {
|
||||
std::string name = meta.get_string(meta_name::name);
|
||||
name.resize(8, ' ');
|
||||
wstr(entry+0x2, name);
|
||||
}
|
||||
if(meta.has(meta_name::loading_address)) {
|
||||
u16 new_loading = meta.get_number(meta_name::loading_address);
|
||||
u16 new_end = r16l(entry + 0xe) - r16l(entry + 0xc) + new_loading;
|
||||
w16l(entry + 0xc, new_loading);
|
||||
w16l(entry + 0xe, new_end);
|
||||
}
|
||||
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
std::pair<err_t, std::vector<dir_entry>> vtech_impl::directory_contents(const std::vector<std::string> &path)
|
||||
{
|
||||
std::pair<err_t, std::vector<dir_entry>> res;
|
||||
|
||||
if(path.size() != 0) {
|
||||
res.first = ERR_NOT_FOUND;
|
||||
return res;
|
||||
}
|
||||
|
||||
res.first = ERR_OK;
|
||||
|
||||
for(int sect = 0; sect != 14; sect++) {
|
||||
auto bdir = m_blockdev.get(sect);
|
||||
for(u32 i = 0; i != 8; i ++) {
|
||||
u32 off = i*16;
|
||||
u8 type = bdir.r8(off);
|
||||
if(type != 'T' && type != 'B')
|
||||
continue;
|
||||
if(bdir.r8(off+1) != ':')
|
||||
continue;
|
||||
meta_data meta = file_metadata(bdir.rodata()+off);
|
||||
res.second.emplace_back(dir_entry(dir_entry_type::file, meta));
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
filesystem_t::file_t vtech_image::impl::root_dir::file_get(u64 key)
|
||||
err_t vtech_impl::rename(const std::vector<std::string> &opath, const std::vector<std::string> &npath)
|
||||
{
|
||||
if(key >= 15*8)
|
||||
throw std::out_of_range("Key out of range");
|
||||
if(opath.size() != 1 || npath.size() != 1)
|
||||
return ERR_NOT_FOUND;
|
||||
|
||||
auto bdir = m_fs.m_blockdev.get(key >> 3);
|
||||
int off = (key & 7) << 4;
|
||||
return file_t(new file(m_fs, this, bdir.rodata() + off, key));
|
||||
auto [bdir, off] = file_find(opath[0]);
|
||||
if(!off)
|
||||
return ERR_NOT_FOUND;
|
||||
|
||||
std::string name = npath[0];
|
||||
name.resize(8, ' ');
|
||||
wstr(bdir.data() + off + 2, name);
|
||||
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
void vtech_image::impl::root_dir::update_file(u16 key, const u8 *entry)
|
||||
err_t vtech_impl::remove(const std::vector<std::string> &path)
|
||||
{
|
||||
auto bdir = m_fs.m_blockdev.get(key >> 3);
|
||||
int off = (key & 7) << 4;
|
||||
bdir.copy(off, entry, 16);
|
||||
return ERR_NOT_FOUND;
|
||||
}
|
||||
|
||||
filesystem_t::dir_t vtech_image::impl::root_dir::dir_get(u64 key)
|
||||
|
||||
err_t vtech_impl::file_create(const std::vector<std::string> &path, const meta_data &meta)
|
||||
{
|
||||
throw std::logic_error("Directories not supported");
|
||||
if(path.size() != 0)
|
||||
return ERR_NOT_FOUND;
|
||||
|
||||
// Find the key for the next unused entry
|
||||
for(int sect = 0; sect != 14; sect++) {
|
||||
auto bdir = m_blockdev.get(sect);
|
||||
for(u32 i = 0; i != 16; i ++) {
|
||||
u32 off = i*16;
|
||||
u8 type = bdir.r8(off);
|
||||
if(type != 'T' && type != 'B') {
|
||||
std::string fname = meta.get_string(meta_name::name, "");
|
||||
fname.resize(8, ' ');
|
||||
|
||||
bdir.w8 (off+0x0, meta.get_flag(meta_name::basic, true) ? 'T' : 'B');
|
||||
bdir.w8 (off+0x1, ':');
|
||||
bdir.wstr(off+0x2, fname);
|
||||
bdir.w8 (off+0xa, 0x00);
|
||||
bdir.w8 (off+0xb, 0x00);
|
||||
bdir.w16l(off+0xc, meta.get_number(meta_name::loading_address, 0x7ae9));
|
||||
bdir.w16l(off+0xe, bdir.r16l(off+0xc) - 1); // Size 0 initially
|
||||
return ERR_OK;
|
||||
}
|
||||
}
|
||||
}
|
||||
return ERR_NO_SPACE;
|
||||
}
|
||||
|
||||
vtech_image::impl::file::file(impl &fs, root_dir *dir, const u8 *entry, u16 key) : m_fs(fs), m_dir(dir), m_key(key)
|
||||
std::pair<err_t, std::vector<u8>> vtech_impl::file_read(const std::vector<std::string> &path)
|
||||
{
|
||||
memcpy(m_entry, entry, 16);
|
||||
}
|
||||
std::vector<u8> data;
|
||||
|
||||
void vtech_image::impl::file::drop_weak_references()
|
||||
{
|
||||
}
|
||||
if(path.size() != 1)
|
||||
return std::make_pair(ERR_NOT_FOUND, data);
|
||||
|
||||
meta_data vtech_image::impl::file::metadata()
|
||||
{
|
||||
meta_data res;
|
||||
auto [bdir, off] = file_find(path[0]);
|
||||
if(off == 0xffffffff)
|
||||
return std::make_pair(ERR_NOT_FOUND, data);
|
||||
|
||||
res.set(meta_name::name, trim_end_spaces(rstr(m_entry+2, 8)));
|
||||
res.set(meta_name::basic, m_entry[0] == 'T');
|
||||
res.set(meta_name::loading_address, r16l(m_entry + 0xc));
|
||||
res.set(meta_name::length, ((r16l(m_entry + 0xe) - r16l(m_entry + 0xc) + 1) & 0xffff));
|
||||
const u8 *entry = bdir.rodata() + off;
|
||||
|
||||
return res;
|
||||
}
|
||||
u8 track = entry[0xa];
|
||||
u8 sector = entry[0xb];
|
||||
int len = ((r16l(entry + 0xe) - r16l(entry + 0xc)) & 0xffff) + 1;
|
||||
|
||||
std::vector<u8> vtech_image::impl::file::read_all()
|
||||
{
|
||||
u8 track = m_entry[0xa];
|
||||
u8 sector = m_entry[0xb];
|
||||
int len = ((r16l(m_entry + 0xe) - r16l(m_entry + 0xc)) & 0xffff) + 1;
|
||||
|
||||
std::vector<u8> data(len, 0);
|
||||
data.resize(len, 0);
|
||||
int pos = 0;
|
||||
while(pos < len) {
|
||||
if(track >= 40 || sector >= 16)
|
||||
break;
|
||||
auto dblk = m_fs.m_blockdev.get(track*16 + sector);
|
||||
auto dblk = m_blockdev.get(track*16 + sector);
|
||||
int size = len - pos;
|
||||
if(size > 126)
|
||||
size = 126;
|
||||
@ -207,44 +315,21 @@ std::vector<u8> vtech_image::impl::file::read_all()
|
||||
track = dblk.r8(126);
|
||||
sector = dblk.r8(127);
|
||||
}
|
||||
return data;
|
||||
return std::make_pair(ERR_OK, data);
|
||||
}
|
||||
|
||||
vtech_image::impl::file_t vtech_image::impl::root_dir::file_create(const meta_data &info)
|
||||
err_t vtech_impl::file_write(const std::vector<std::string> &path, const std::vector<u8> &data)
|
||||
{
|
||||
// Find the key for the next unused entry
|
||||
for(int sect = 0; sect != 14; sect++) {
|
||||
auto bdir = m_fs.m_blockdev.get(sect);
|
||||
u64 id = 0;
|
||||
for(u32 i = 0; i != 16; i ++) {
|
||||
u32 off = i*16;
|
||||
u8 type = bdir.r8(off);
|
||||
if(type != 'T' && type != 'B') {
|
||||
std::string fname = info.get_string(meta_name::name, "");
|
||||
fname.resize(8, ' ');
|
||||
if(path.size() != 1)
|
||||
return ERR_NOT_FOUND;
|
||||
|
||||
bdir.w8 (off+0x0, info.get_flag(meta_name::basic, true) ? 'T' : 'B');
|
||||
bdir.w8 (off+0x1, ':');
|
||||
bdir.wstr(off+0x2, fname);
|
||||
bdir.w8 (off+0xa, 0x00);
|
||||
bdir.w8 (off+0xb, 0x00);
|
||||
bdir.w16l(off+0xc, info.get_number(meta_name::loading_address, 0x7ae9));
|
||||
bdir.w16l(off+0xe, bdir.r16l(off+0xc) - 1); // Size 0 initially
|
||||
return file_t(new file(m_fs, this, bdir.rodata() + off, id));
|
||||
}
|
||||
id++;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
auto [bdir, off] = file_find(path[0]);
|
||||
if(off == 0xffffffff)
|
||||
return ERR_NOT_FOUND;
|
||||
|
||||
void vtech_image::impl::root_dir::file_delete(u64 key)
|
||||
{
|
||||
}
|
||||
u8 *entry = bdir.data() + off;
|
||||
|
||||
void vtech_image::impl::file::replace(const std::vector<u8> &data)
|
||||
{
|
||||
u32 cur_len = ((r16l(m_entry + 0xe) - r16l(m_entry + 0xc) + 1) & 0xffff);
|
||||
u32 cur_len = ((r16l(entry + 0xe) - r16l(entry + 0xc) + 1) & 0xffff);
|
||||
u32 new_len = data.size();
|
||||
if(new_len > 65535)
|
||||
new_len = 65535;
|
||||
@ -252,24 +337,24 @@ void vtech_image::impl::file::replace(const std::vector<u8> &data)
|
||||
u32 need_ns = (new_len + 125) / 126;
|
||||
|
||||
// Enough space?
|
||||
if(cur_ns < need_ns && m_fs.free_block_count() < need_ns - cur_ns)
|
||||
return;
|
||||
if(cur_ns < need_ns && free_block_count() < need_ns - cur_ns)
|
||||
return ERR_NO_SPACE;
|
||||
|
||||
u8 track = m_entry[0xa];
|
||||
u8 sector = m_entry[0xb];
|
||||
u8 track = entry[0xa];
|
||||
u8 sector = entry[0xb];
|
||||
std::vector<std::pair<u8, u8>> tofree;
|
||||
for(u32 i = 0; i != cur_ns; i++) {
|
||||
tofree.emplace_back(std::make_pair(track, sector));
|
||||
auto dblk = m_fs.m_blockdev.get(track*16 + sector);
|
||||
auto dblk = m_blockdev.get(track*16 + sector);
|
||||
track = dblk.r8(126);
|
||||
sector = dblk.r8(127);
|
||||
}
|
||||
|
||||
m_fs.free_blocks(tofree);
|
||||
free_blocks(tofree);
|
||||
|
||||
std::vector<std::pair<u8, u8>> blocks = m_fs.allocate_blocks(need_ns);
|
||||
std::vector<std::pair<u8, u8>> blocks = allocate_blocks(need_ns);
|
||||
for(u32 i=0; i != need_ns; i ++) {
|
||||
auto dblk = m_fs.m_blockdev.get(blocks[i].first * 16 + blocks[i].second);
|
||||
auto dblk = m_blockdev.get(blocks[i].first * 16 + blocks[i].second);
|
||||
u32 len = new_len - i*126;
|
||||
if(len > 126)
|
||||
len = 126;
|
||||
@ -283,44 +368,18 @@ void vtech_image::impl::file::replace(const std::vector<u8> &data)
|
||||
dblk.w16l(126, 0);
|
||||
}
|
||||
|
||||
u16 end_address = (r16l(m_entry + 0xc) + data.size() - 1) & 0xffff;
|
||||
w16l(m_entry + 0xe, end_address);
|
||||
u16 end_address = (r16l(entry + 0xc) + data.size() - 1) & 0xffff;
|
||||
w16l(entry + 0xe, end_address);
|
||||
if(need_ns) {
|
||||
w8(m_entry + 0xa, blocks[0].first);
|
||||
w8(m_entry + 0xb, blocks[0].second);
|
||||
w8(entry + 0xa, blocks[0].first);
|
||||
w8(entry + 0xb, blocks[0].second);
|
||||
} else
|
||||
w16l(m_entry + 0xa, 0);
|
||||
w16l(entry + 0xa, 0);
|
||||
|
||||
m_dir->update_file(m_key, m_entry);
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
void vtech_image::impl::root_dir::metadata_change(const meta_data &info)
|
||||
{
|
||||
}
|
||||
|
||||
void vtech_image::impl::metadata_change(const meta_data &info)
|
||||
{
|
||||
}
|
||||
|
||||
void vtech_image::impl::file::metadata_change(const meta_data &info)
|
||||
{
|
||||
if(info.has(meta_name::basic))
|
||||
w8 (m_entry+0x0, info.get_flag(meta_name::basic) ? 'T' : 'B');
|
||||
if(info.has(meta_name::name)) {
|
||||
std::string name = info.get_string(meta_name::name);
|
||||
name.resize(8, ' ');
|
||||
wstr(m_entry+0x2, name);
|
||||
}
|
||||
if(info.has(meta_name::loading_address)) {
|
||||
u16 new_loading = info.get_number(meta_name::loading_address);
|
||||
u16 new_end = r16l(m_entry + 0xe) - r16l(m_entry + 0xc) + new_loading;
|
||||
w16l(m_entry + 0xc, new_loading);
|
||||
w16l(m_entry + 0xe, new_end);
|
||||
}
|
||||
m_dir->update_file(m_key, m_entry);
|
||||
}
|
||||
|
||||
std::vector<std::pair<u8, u8>> vtech_image::impl::allocate_blocks(u32 count)
|
||||
std::vector<std::pair<u8, u8>> vtech_impl::allocate_blocks(u32 count)
|
||||
{
|
||||
std::vector<std::pair<u8, u8>> blocks;
|
||||
if(free_block_count() < count)
|
||||
@ -341,7 +400,7 @@ std::vector<std::pair<u8, u8>> vtech_image::impl::allocate_blocks(u32 count)
|
||||
abort();
|
||||
}
|
||||
|
||||
void vtech_image::impl::free_blocks(const std::vector<std::pair<u8, u8>> &blocks)
|
||||
void vtech_impl::free_blocks(const std::vector<std::pair<u8, u8>> &blocks)
|
||||
{
|
||||
auto fmap = m_blockdev.get(15);
|
||||
for(auto ref : blocks) {
|
||||
@ -353,7 +412,7 @@ void vtech_image::impl::free_blocks(const std::vector<std::pair<u8, u8>> &blocks
|
||||
}
|
||||
}
|
||||
|
||||
u32 vtech_image::impl::free_block_count()
|
||||
u32 vtech_impl::free_block_count()
|
||||
{
|
||||
auto fmap = m_blockdev.get(15);
|
||||
u32 nf = 0;
|
||||
@ -367,5 +426,3 @@ u32 vtech_image::impl::free_block_count()
|
||||
}
|
||||
return nf;
|
||||
}
|
||||
|
||||
} // namespace fs
|
||||
|
@ -14,68 +14,6 @@ namespace fs {
|
||||
|
||||
class vtech_image : public manager_t {
|
||||
public:
|
||||
class impl : public filesystem_t {
|
||||
public:
|
||||
class root_dir : public idir_t {
|
||||
public:
|
||||
root_dir(impl &i) : m_fs(i) {}
|
||||
virtual ~root_dir() = default;
|
||||
|
||||
virtual void drop_weak_references() override;
|
||||
|
||||
virtual meta_data metadata() override;
|
||||
virtual void metadata_change(const meta_data &info) override;
|
||||
virtual std::vector<dir_entry> contents() override;
|
||||
virtual file_t file_get(u64 key) override;
|
||||
virtual dir_t dir_get(u64 key) override;
|
||||
virtual file_t file_create(const meta_data &info) override;
|
||||
virtual void file_delete(u64 key) override;
|
||||
|
||||
void update_file(u16 key, const u8 *entry);
|
||||
|
||||
private:
|
||||
impl &m_fs;
|
||||
|
||||
std::pair<fsblk_t::block_t, u32> get_dir_block(u64 key);
|
||||
};
|
||||
|
||||
class file : public ifile_t {
|
||||
public:
|
||||
file(impl &fs, root_dir *dir, const u8 *entry, u16 key);
|
||||
virtual ~file() = default;
|
||||
|
||||
virtual void drop_weak_references() override;
|
||||
|
||||
virtual meta_data metadata() override;
|
||||
virtual void metadata_change(const meta_data &info) override;
|
||||
virtual std::vector<u8> read_all() override;
|
||||
virtual void replace(const std::vector<u8> &data) override;
|
||||
|
||||
private:
|
||||
impl &m_fs;
|
||||
root_dir *m_dir;
|
||||
u16 m_key;
|
||||
u8 m_entry[18];
|
||||
};
|
||||
|
||||
impl(fsblk_t &blockdev);
|
||||
virtual ~impl() = default;
|
||||
|
||||
virtual void format(const meta_data &meta) override;
|
||||
virtual meta_data metadata() override;
|
||||
virtual void metadata_change(const meta_data &info) override;
|
||||
virtual dir_t root() override;
|
||||
|
||||
void drop_root_ref();
|
||||
|
||||
std::vector<std::pair<u8, u8>> allocate_blocks(u32 count);
|
||||
void free_blocks(const std::vector<std::pair<u8, u8>> &blocks);
|
||||
u32 free_block_count();
|
||||
|
||||
private:
|
||||
dir_t m_root;
|
||||
};
|
||||
|
||||
vtech_image() : manager_t() {}
|
||||
|
||||
virtual const char *name() const override;
|
||||
|
@ -34,7 +34,7 @@ u32 fsblk_vec_t::block_count() const
|
||||
fsblk_t::block_t fsblk_vec_t::get(u32 id)
|
||||
{
|
||||
if(id >= block_count())
|
||||
throw std::out_of_range(util::string_format("Block number overflow: requiring block %d on device of size %d (%d bytes, block size %d)", id, block_count(), m_data.size(), m_block_size));
|
||||
throw std::out_of_range(util::string_format("Block number overflow: requiring block %d on device of size %d (%d bytes, block size %d)\n", id, block_count(), m_data.size(), m_block_size));
|
||||
return block_t(new blk_t(m_data.data() + m_block_size*id, m_block_size));
|
||||
}
|
||||
|
||||
|
@ -103,27 +103,11 @@ char manager_t::directory_separator() const
|
||||
return 0; // Subdirectories not supported by default
|
||||
}
|
||||
|
||||
void filesystem_t::format(const meta_data &meta)
|
||||
{
|
||||
throw std::logic_error("format called on a filesystem not supporting it");
|
||||
}
|
||||
|
||||
filesystem_t::dir_t filesystem_t::root()
|
||||
{
|
||||
throw std::logic_error("root called on a filesystem not supporting it");
|
||||
}
|
||||
|
||||
meta_data filesystem_t::metadata()
|
||||
{
|
||||
throw std::logic_error("filesystem_t::metadata called on a filesystem not supporting it");
|
||||
}
|
||||
|
||||
void fsblk_t::set_block_size(u32 block_size)
|
||||
{
|
||||
m_block_size = block_size;
|
||||
}
|
||||
|
||||
|
||||
u8 *fsblk_t::iblock_t::offset(const char *function, u32 off, u32 size)
|
||||
{
|
||||
if(off + size > m_size)
|
||||
@ -374,45 +358,75 @@ std::string filesystem_t::trim_end_spaces(const std::string &str)
|
||||
return str.substr(0, (std::string::npos != i) ? (i + 1) : 0);
|
||||
}
|
||||
|
||||
filesystem_t::file_t filesystem_t::idir_t::file_create(const meta_data &info)
|
||||
meta_data filesystem_t::volume_metadata()
|
||||
{
|
||||
throw std::logic_error("file_create called on a filesystem not supporting write");
|
||||
return meta_data();
|
||||
}
|
||||
|
||||
void filesystem_t::idir_t::file_delete(uint64_t key)
|
||||
err_t filesystem_t::volume_metadata_change(const meta_data &meta)
|
||||
{
|
||||
throw std::logic_error("file_delete called on a filesystem not supporting write");
|
||||
return ERR_UNSUPPORTED;
|
||||
}
|
||||
|
||||
|
||||
void filesystem_t::ifile_t::replace(const std::vector<u8> &data)
|
||||
std::pair<err_t, meta_data> filesystem_t::metadata(const std::vector<std::string> &path)
|
||||
{
|
||||
throw std::logic_error("replace called on a filesystem not supporting write");
|
||||
return std::make_pair(ERR_UNSUPPORTED, meta_data());
|
||||
}
|
||||
|
||||
void filesystem_t::ifile_t::rsrc_replace(const std::vector<u8> &data)
|
||||
err_t filesystem_t::metadata_change(const std::vector<std::string> &path, const meta_data &meta)
|
||||
{
|
||||
throw std::logic_error("rsrc_replace called on a filesystem not supporting write or resource forks");
|
||||
return ERR_UNSUPPORTED;
|
||||
}
|
||||
|
||||
void filesystem_t::ifile_t::metadata_change(const meta_data &info)
|
||||
std::pair<err_t, std::vector<dir_entry>> filesystem_t::directory_contents(const std::vector<std::string> &path)
|
||||
{
|
||||
throw std::logic_error("metadata_change called on a filesystem not supporting write");
|
||||
return std::make_pair(ERR_UNSUPPORTED, std::vector<dir_entry>());
|
||||
}
|
||||
|
||||
void filesystem_t::idir_t::metadata_change(const meta_data &info)
|
||||
err_t filesystem_t::rename(const std::vector<std::string> &opath, const std::vector<std::string> &npath)
|
||||
{
|
||||
throw std::logic_error("metadata_change called on a filesystem not supporting write");
|
||||
return ERR_UNSUPPORTED;
|
||||
}
|
||||
|
||||
void filesystem_t::metadata_change(const meta_data &info)
|
||||
err_t filesystem_t::remove(const std::vector<std::string> &path)
|
||||
{
|
||||
throw std::logic_error("metadata_change called on a filesystem not supporting write");
|
||||
return ERR_UNSUPPORTED;
|
||||
}
|
||||
|
||||
std::vector<u8> filesystem_t::ifile_t::rsrc_read_all()
|
||||
err_t filesystem_t::dir_create(const std::vector<std::string> &path, const meta_data &meta)
|
||||
{
|
||||
throw std::logic_error("rsrc_read_all called on a filesystem without resource forks");
|
||||
return ERR_UNSUPPORTED;
|
||||
}
|
||||
|
||||
err_t filesystem_t::file_create(const std::vector<std::string> &path, const meta_data &meta)
|
||||
{
|
||||
return ERR_UNSUPPORTED;
|
||||
}
|
||||
|
||||
std::pair<err_t, std::vector<u8>> filesystem_t::file_read(const std::vector<std::string> &path)
|
||||
{
|
||||
return std::make_pair(ERR_UNSUPPORTED, std::vector<u8>());
|
||||
}
|
||||
|
||||
err_t filesystem_t::file_write(const std::vector<std::string> &path, const std::vector<u8> &data)
|
||||
{
|
||||
return ERR_UNSUPPORTED;
|
||||
}
|
||||
|
||||
std::pair<err_t, std::vector<u8>> filesystem_t::file_rsrc_read(const std::vector<std::string> &path)
|
||||
{
|
||||
return std::make_pair(ERR_UNSUPPORTED, std::vector<u8>());
|
||||
}
|
||||
|
||||
err_t filesystem_t::file_rsrc_write(const std::vector<std::string> &path, const std::vector<u8> &data)
|
||||
{
|
||||
return ERR_UNSUPPORTED;
|
||||
}
|
||||
|
||||
err_t filesystem_t::format(const meta_data &meta)
|
||||
{
|
||||
return ERR_UNSUPPORTED;
|
||||
}
|
||||
|
||||
|
||||
} // namespace fs
|
||||
|
@ -18,6 +18,15 @@ using u16 = uint16_t;
|
||||
using u32 = uint32_t;
|
||||
using u64 = uint64_t;
|
||||
|
||||
enum err_t {
|
||||
ERR_OK = 0,
|
||||
ERR_UNSUPPORTED,
|
||||
ERR_INVALID,
|
||||
ERR_NOT_FOUND,
|
||||
ERR_NOT_EMPTY,
|
||||
ERR_NO_SPACE,
|
||||
};
|
||||
|
||||
template<typename T> class refcounted_outer {
|
||||
public:
|
||||
refcounted_outer(bool weak) : m_object(nullptr), m_is_weak_ref(weak) {}
|
||||
@ -118,15 +127,14 @@ public:
|
||||
enum class dir_entry_type {
|
||||
dir,
|
||||
file,
|
||||
system_file,
|
||||
};
|
||||
|
||||
struct dir_entry {
|
||||
std::string m_name;
|
||||
dir_entry_type m_type;
|
||||
u64 m_key;
|
||||
meta_data m_meta;
|
||||
|
||||
dir_entry(std::string &&name, dir_entry_type type, u64 key) : m_name(std::move(name)), m_type(type), m_key(key) {}
|
||||
dir_entry(dir_entry_type type, const meta_data &meta) : m_name(meta.get_string(meta_name::name)), m_type(type), m_meta(std::move(meta)) {}
|
||||
};
|
||||
|
||||
class fsblk_t {
|
||||
@ -200,83 +208,50 @@ protected:
|
||||
|
||||
class filesystem_t {
|
||||
public:
|
||||
class dir_t;
|
||||
class file_t;
|
||||
|
||||
protected:
|
||||
class idir_t : public refcounted_inner {
|
||||
public:
|
||||
idir_t() : refcounted_inner() {}
|
||||
virtual ~idir_t() = default;
|
||||
|
||||
virtual meta_data metadata() = 0;
|
||||
virtual void metadata_change(const meta_data &info);
|
||||
virtual std::vector<dir_entry> contents() = 0;
|
||||
virtual file_t file_get(u64 key) = 0;
|
||||
virtual dir_t dir_get(u64 key) = 0;
|
||||
virtual file_t file_create(const meta_data &info);
|
||||
virtual void file_delete(u64 key);
|
||||
};
|
||||
|
||||
class ifile_t : public refcounted_inner {
|
||||
public:
|
||||
ifile_t() : refcounted_inner() {}
|
||||
virtual ~ifile_t() = default;
|
||||
|
||||
virtual meta_data metadata() = 0;
|
||||
virtual void metadata_change(const meta_data &info);
|
||||
virtual std::vector<u8> read_all() = 0;
|
||||
virtual void replace(const std::vector<u8> &data);
|
||||
virtual std::vector<u8> rsrc_read_all();
|
||||
virtual void rsrc_replace(const std::vector<u8> &data);
|
||||
};
|
||||
|
||||
public:
|
||||
class dir_t : public refcounted_outer<idir_t> {
|
||||
public:
|
||||
dir_t(bool weak = false) : refcounted_outer<idir_t>(weak) {}
|
||||
dir_t(idir_t *dir, bool weak = true) : refcounted_outer(dir, weak) {}
|
||||
virtual ~dir_t() = default;
|
||||
|
||||
dir_t strong() { return dir_t(m_object, false); }
|
||||
dir_t weak() { return dir_t(m_object, true); }
|
||||
|
||||
meta_data metadata() { return m_object->metadata(); }
|
||||
void metadata_change(const meta_data &info) { m_object->metadata_change(info); }
|
||||
std::vector<dir_entry> contents() { return m_object->contents(); }
|
||||
file_t file_get(u64 key) { return m_object->file_get(key); }
|
||||
dir_t dir_get(u64 key) { return m_object->dir_get(key); }
|
||||
file_t file_create(const meta_data &info) { return m_object->file_create(info); }
|
||||
void file_delete(u64 key) { m_object->file_delete(key); }
|
||||
};
|
||||
|
||||
class file_t : public refcounted_outer<ifile_t> {
|
||||
public:
|
||||
file_t(bool weak = false) : refcounted_outer<ifile_t>(weak) {}
|
||||
file_t(ifile_t *file, bool weak = true) : refcounted_outer(file, weak) {}
|
||||
virtual ~file_t() = default;
|
||||
|
||||
file_t strong() { return file_t(m_object, false); }
|
||||
file_t weak() { return file_t(m_object, true); }
|
||||
|
||||
meta_data metadata() { return m_object->metadata(); }
|
||||
void metadata_change(const meta_data &info) { m_object->metadata_change(info); }
|
||||
std::vector<u8> read_all() { return m_object->read_all(); }
|
||||
void replace(const std::vector<u8> &data) { m_object->replace(data); }
|
||||
std::vector<u8> rsrc_read_all() { return m_object->rsrc_read_all(); }
|
||||
void rsrc_replace(const std::vector<u8> &data) { m_object->rsrc_replace(data); }
|
||||
};
|
||||
|
||||
filesystem_t(fsblk_t &blockdev, u32 size) : m_blockdev(blockdev) {
|
||||
m_blockdev.set_block_size(size);
|
||||
}
|
||||
|
||||
virtual ~filesystem_t() = default;
|
||||
|
||||
virtual dir_t root();
|
||||
virtual void format(const meta_data &meta);
|
||||
virtual meta_data metadata();
|
||||
virtual void metadata_change(const meta_data &info);
|
||||
// Get the metadata for the volume
|
||||
virtual meta_data volume_metadata();
|
||||
|
||||
// Change the metadata for the volume
|
||||
virtual err_t volume_metadata_change(const meta_data &meta);
|
||||
|
||||
// Get the metadata for a file or a directory. Empty path targets the root directory
|
||||
virtual std::pair<err_t, meta_data> metadata(const std::vector<std::string> &path);
|
||||
|
||||
// Change the metadata for a file or a directory. Empty path targets the root directory
|
||||
virtual err_t metadata_change(const std::vector<std::string> &path, const meta_data &meta);
|
||||
|
||||
// Get the contents of a directory, empty path targets the root directory
|
||||
virtual std::pair<err_t, std::vector<dir_entry>> directory_contents(const std::vector<std::string> &path);
|
||||
|
||||
// Rename a file or a directory. In contrast to metadata_change, this can move the object
|
||||
// between directories
|
||||
virtual err_t rename(const std::vector<std::string> &opath, const std::vector<std::string> &npath);
|
||||
|
||||
// Remove a file or a directory. Directories must be empty (e.g. it's not recursive)
|
||||
virtual err_t remove(const std::vector<std::string> &path);
|
||||
|
||||
// Create a directory, path designates where the directory must be, directory name is in meta
|
||||
virtual err_t dir_create(const std::vector<std::string> &path, const meta_data &meta);
|
||||
|
||||
// Create an empty file, path designates where the file must be, file name is in meta
|
||||
virtual err_t file_create(const std::vector<std::string> &path, const meta_data &meta);
|
||||
|
||||
// Read the contents of a file
|
||||
virtual std::pair<err_t, std::vector<u8>> file_read(const std::vector<std::string> &path);
|
||||
|
||||
// Replace the contents of a file, the file must already exist
|
||||
virtual err_t file_write(const std::vector<std::string> &path, const std::vector<u8> &data);
|
||||
|
||||
// Read the resource fork of a file on systems that handle those
|
||||
virtual std::pair<err_t, std::vector<u8>> file_rsrc_read(const std::vector<std::string> &path);
|
||||
|
||||
// Replace the resource fork of a file, the file must already exist
|
||||
virtual err_t file_rsrc_write(const std::vector<std::string> &path, const std::vector<u8> &data);
|
||||
|
||||
// Format an image, provide the volume metadata
|
||||
virtual err_t format(const meta_data &meta);
|
||||
|
||||
static void copy(u8 *p, const u8 *src, u32 size);
|
||||
static void fill(u8 *p, u8 data, u32 size);
|
||||
@ -298,9 +273,13 @@ public:
|
||||
static u32 r24l(const u8 *p);
|
||||
static u32 r32l(const u8 *p);
|
||||
|
||||
protected:
|
||||
filesystem_t(fsblk_t &blockdev, u32 size) : m_blockdev(blockdev) {
|
||||
m_blockdev.set_block_size(size);
|
||||
}
|
||||
|
||||
static std::string trim_end_spaces(const std::string &str);
|
||||
|
||||
protected:
|
||||
fsblk_t &m_blockdev;
|
||||
};
|
||||
|
||||
|
@ -263,47 +263,42 @@ static int flopcreate(int argc, char *argv[])
|
||||
return ih.floppy_save(dest_format);
|
||||
}
|
||||
|
||||
static void dir_scan(u32 depth, fs::filesystem_t::dir_t dir, std::vector<std::vector<std::string>> &entries, const std::unordered_map<fs::meta_name, size_t> &nmap, size_t nc, const std::vector<fs::meta_description> &dmetad, const std::vector<fs::meta_description> &fmetad)
|
||||
static void dir_scan(fs::filesystem_t *fs, u32 depth, const std::vector<std::string> &path, std::vector<std::vector<std::string>> &entries, const std::unordered_map<fs::meta_name, size_t> &nmap, size_t nc, const std::vector<fs::meta_description> &dmetad, const std::vector<fs::meta_description> &fmetad)
|
||||
{
|
||||
std::string head;
|
||||
for(u32 i = 0; i != depth; i++)
|
||||
head += " ";
|
||||
auto contents = dir.contents();
|
||||
auto [err, contents] = fs->directory_contents(path);
|
||||
if(err)
|
||||
return;
|
||||
for(const auto &c : contents) {
|
||||
size_t id = entries.size();
|
||||
entries.resize(id+1);
|
||||
entries[id].resize(nc);
|
||||
switch(c.m_type) {
|
||||
case fs::dir_entry_type::dir: {
|
||||
auto subdir = dir.dir_get(c.m_key);
|
||||
auto meta = subdir.metadata();
|
||||
if (!meta.has(fs::meta_name::name))
|
||||
meta.set(fs::meta_name::name, c.m_name);
|
||||
for(const auto &m : dmetad) {
|
||||
if(!meta.has(m.m_name))
|
||||
if(!c.m_meta.has(m.m_name))
|
||||
continue;
|
||||
size_t slot = nmap.find(m.m_name)->second;
|
||||
std::string val = meta.get(m.m_name).to_string();
|
||||
std::string val = c.m_meta.get(m.m_name).to_string();
|
||||
if(slot == 0)
|
||||
val = head + "dir " + val;
|
||||
entries[id][slot] = val;
|
||||
}
|
||||
dir_scan(depth+1, subdir, entries, nmap, nc, dmetad, fmetad);
|
||||
auto npath = path;
|
||||
npath.push_back(c.m_name);
|
||||
dir_scan(fs, depth+1, npath, entries, nmap, nc, dmetad, fmetad);
|
||||
break;
|
||||
}
|
||||
case fs::dir_entry_type::file:
|
||||
case fs::dir_entry_type::system_file: {
|
||||
auto file = dir.file_get(c.m_key);
|
||||
auto meta = file.metadata();
|
||||
if (!meta.has(fs::meta_name::name))
|
||||
meta.set(fs::meta_name::name, c.m_name);
|
||||
case fs::dir_entry_type::file: {
|
||||
for(const auto &m : fmetad) {
|
||||
if(!meta.has(m.m_name))
|
||||
if(!c.m_meta.has(m.m_name))
|
||||
continue;
|
||||
size_t slot = nmap.find(m.m_name)->second;
|
||||
std::string val = meta.get(m.m_name).to_string();
|
||||
std::string val = c.m_meta.get(m.m_name).to_string();
|
||||
if(slot == 0)
|
||||
val = head + (c.m_type == fs::dir_entry_type::system_file ? "sys " : "file ") + val;
|
||||
val = head + "file " + val;
|
||||
entries[id][slot] = val;
|
||||
}
|
||||
break;
|
||||
@ -319,7 +314,7 @@ static int generic_dir(image_handler &ih)
|
||||
auto fmetad = fsm->file_meta_description();
|
||||
auto dmetad = fsm->directory_meta_description();
|
||||
|
||||
auto vmeta = fs->metadata();
|
||||
auto vmeta = fs->volume_metadata();
|
||||
if(!vmeta.empty()) {
|
||||
std::string vinf = "Volume:";
|
||||
for(const auto &e : vmetad)
|
||||
@ -340,14 +335,13 @@ static int generic_dir(image_handler &ih)
|
||||
for(size_t i = 0; i != names.size(); i++)
|
||||
nmap[names[i]] = i;
|
||||
|
||||
auto root = fs->root();
|
||||
std::vector<std::vector<std::string>> entries;
|
||||
|
||||
entries.resize(1);
|
||||
for(fs::meta_name n : names)
|
||||
entries[0].push_back(fs::meta_data::entry_name(n));
|
||||
|
||||
dir_scan(0, root, entries, nmap, names.size(), dmetad, fmetad);
|
||||
dir_scan(fs, 0, std::vector<std::string>(), entries, nmap, names.size(), dmetad, fmetad);
|
||||
|
||||
std::vector<u32> sizes(names.size());
|
||||
|
||||
@ -443,50 +437,20 @@ static int generic_read(image_handler &ih, const char *srcpath, const char *dstp
|
||||
auto [fsm, fs] = ih.get_fs();
|
||||
|
||||
std::vector<std::string> path = ih.path_split(srcpath);
|
||||
|
||||
auto dir = fs->root();
|
||||
std::string apath;
|
||||
for(unsigned int i = 0; i < path.size() - 1; i++) {
|
||||
auto c = dir.contents();
|
||||
unsigned int j;
|
||||
for(j = 0; j != c.size(); j++)
|
||||
if(c[j].m_name == path[i])
|
||||
break;
|
||||
if(j == c.size()) {
|
||||
fprintf(stderr, "Error: directory %s%c%s not found\n", apath.c_str(), 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(), fsm->directory_separator(), path[i].c_str());
|
||||
return 1;
|
||||
}
|
||||
dir = dir.dir_get(c[j].m_key);
|
||||
apath += fsm->directory_separator() + path[i];
|
||||
}
|
||||
|
||||
auto c = dir.contents();
|
||||
unsigned int j;
|
||||
for(j = 0; j != c.size(); j++)
|
||||
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(), 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(), fsm->directory_separator(), path.back().c_str());
|
||||
auto [err, dfork] = fs->file_read(path);
|
||||
if(err) {
|
||||
if(err == fs::ERR_NOT_FOUND)
|
||||
fprintf(stderr, "File not found.\n");
|
||||
else
|
||||
fprintf(stderr, "Unknown error (%d).\n", err);
|
||||
return 1;
|
||||
}
|
||||
|
||||
image_handler::fsave(dstpath, file.read_all());
|
||||
image_handler::fsave(dstpath, dfork);
|
||||
|
||||
bool has_rsrc = fsm->has_rsrc() && meta.has(fs::meta_name::rsrc_length);
|
||||
|
||||
if(has_rsrc)
|
||||
image_handler::fsave_rsrc(image_handler::path_make_rsrc(dstpath), file.rsrc_read_all());
|
||||
auto [err2, rfork] = fs->file_rsrc_read(path);
|
||||
if(!err2 && !rfork.empty())
|
||||
image_handler::fsave_rsrc(image_handler::path_make_rsrc(dstpath), rfork);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -568,44 +532,37 @@ static int generic_write(image_handler &ih, const char *srcpath, const char *dst
|
||||
auto [fsm, fs] = ih.get_fs();
|
||||
|
||||
std::vector<std::string> path = ih.path_split(dstpath);
|
||||
auto [err, meta] = fs->metadata(path);
|
||||
|
||||
auto dir = fs->root();
|
||||
std::string apath;
|
||||
for(unsigned int i = 0; i < path.size() - 1; i++) {
|
||||
auto c = dir.contents();
|
||||
unsigned int j;
|
||||
for(j = 0; j != c.size(); j++)
|
||||
if(c[j].m_name == path[i])
|
||||
break;
|
||||
if(j == c.size()) {
|
||||
fprintf(stderr, "Error: directory %s%c%s not found\n", apath.c_str(), fsm->directory_separator(), path[i].c_str());
|
||||
if(err) {
|
||||
fs::meta_data meta;
|
||||
meta.set(fs::meta_name::name, path.back());
|
||||
auto dpath = path;
|
||||
dpath.pop_back();
|
||||
err = fs->file_create(dpath, meta);
|
||||
if(!err) {
|
||||
fprintf(stderr, "File creation failure.\n");
|
||||
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(), fsm->directory_separator(), path[i].c_str());
|
||||
return 1;
|
||||
}
|
||||
dir = dir.dir_get(c[j].m_key);
|
||||
apath += fsm->directory_separator() + path[i];
|
||||
}
|
||||
|
||||
auto dfork = image_handler::fload(srcpath);
|
||||
err = fs->file_write(path, dfork);
|
||||
if(!err) {
|
||||
fprintf(stderr, "File writing failure.\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
fs::meta_data meta;
|
||||
meta.set(fs::meta_name::name, path.back());
|
||||
|
||||
auto file = dir.file_create(meta);
|
||||
auto filedata = image_handler::fload(srcpath);
|
||||
file.replace(filedata);
|
||||
|
||||
bool has_rsrc = fsm->has_rsrc();
|
||||
|
||||
if(has_rsrc) {
|
||||
if(fsm->has_rsrc()) {
|
||||
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);
|
||||
auto rfork = image_handler::fload_rsrc(rpath);
|
||||
if(!rfork.empty()) {
|
||||
err = fs->file_rsrc_write(path, rfork);
|
||||
fprintf(stderr, "File resource fork writing failure.\n");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user