fs: Add full jasmin read support

This commit is contained in:
Olivier Galibert 2021-04-29 14:45:55 +02:00
parent 7ae546e0b6
commit e72e97b174
9 changed files with 805 additions and 248 deletions

View File

@ -1,7 +1,7 @@
// license:BSD-3-Clause
// copyright-holders:Olivier Galibert
// Creation of Apple Oric_Jasmin floppy images
// Management of Oric Jasmin floppy images
#include "emu.h"
#include "fs_oric_jasmin.h"
@ -29,7 +29,7 @@
// offset 04+ : 14 file entries, 18 bytes each
// offset 00-01: reference to the first sector of the inode, (ff, xx) when no entry
// offset 02 : U/L for (U)nlocked or (L)ocked file
// offset 03-0e: filename.ext, space padiing between filename and '.'
// offset 03-0e: filename.ext, space padding between filename and '.'
// offset 0f : S/D for (S)equential (normal) or (D)irect-access files
// offset 10-11: number of sectors used by the file, including inode, little-endian
//
@ -67,11 +67,6 @@ bool fs_oric_jasmin::can_write() const
return false;
}
bool fs_oric_jasmin::has_subdirectories() const
{
return false;
}
std::vector<fs_meta_description> fs_oric_jasmin::volume_meta_description() const
{
std::vector<fs_meta_description> res;
@ -125,22 +120,15 @@ void fs_oric_jasmin::impl::format(const fs_meta_data &meta)
auto fmap = m_blockdev.get(bblk);
u32 off = 0;
for(u32 blk = 0; blk != blocks; blk += 17) {
if(blk == bblk) {
fmap.w8(off , 0xff);
fmap.w8(off + 1, 0x7f);
fmap.w8(off + 2, 0x00);
} else {
fmap.w8(off , 0xff);
fmap.w8(off + 1, 0xff);
fmap.w8(off + 2, 0x01);
}
if(blk == bblk)
fmap.w24l(off, 0x07fff);
else
fmap.w24l(off, 0x1ffff);
off += 3;
}
for(u32 blk = blocks; blk != 17*42*2; blk += 17) {
fmap.w8(off , 0x00);
fmap.w8(off + 1, 0x00);
fmap.w8(off + 2, 0x80);
fmap.w24l(off, 0x800000);
off += 3;
}
@ -155,4 +143,287 @@ void fs_oric_jasmin::impl::format(const fs_meta_data &meta)
bdir.w16l(2, 0x0000);
}
fs_oric_jasmin::impl::impl(fsblk_t &blockdev) : filesystem_t(blockdev, 256), m_root(true)
{
}
bool fs_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 fs_oric_jasmin::impl::cs_to_block(u16 ref)
{
u8 track = ref >> 8;
u8 sector = ref & 0xff;
return track * 17 + sector - 1;
}
u16 fs_oric_jasmin::impl::block_to_cs(u32 block)
{
u8 track = block / 17;
u8 sector = (block % 17) + 1;
return (track << 8) | sector;
}
std::string fs_oric_jasmin::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 fs_oric_jasmin::impl::root()
{
if(!m_root)
m_root = new root_dir(*this);
return m_root.strong();
}
void fs_oric_jasmin::impl::drop_root_ref()
{
m_root = nullptr;
}
void fs_oric_jasmin::impl::root_dir::drop_weak_references()
{
m_fs.drop_root_ref();
}
fs_meta_data fs_oric_jasmin::impl::root_dir::metadata()
{
return fs_meta_data();
}
std::vector<fs_dir_entry> fs_oric_jasmin::impl::root_dir::contents()
{
std::vector<fs_dir_entry> res;
auto bdir = m_fs.m_blockdev.get(20*17+1);
uint64_t 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(fs_dir_entry(fname, fs_dir_entry_type::system_file, 0));
else if(m_fs.ref_valid(ref))
res.emplace_back(fs_dir_entry(fname, fs_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));
}
return res;
}
std::pair<fsblk_t::block_t, u32> fs_oric_jasmin::impl::root_dir::get_dir_block(uint64_t key)
{
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))
fatalerror("Incorrect file key\n");
bdir = m_fs.m_blockdev.get(cs_to_block(ref));
key -= 14;
}
return std::pair<fsblk_t::block_t, u32>(bdir, 4 + key * 18);
}
filesystem_t::file_t fs_oric_jasmin::impl::root_dir::file_get(uint64_t key)
{
uint64_t 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, bdir.rodata() + off));
if(!m_fs.ref_valid(ref))
fatalerror("Key to deleted/non-existent file\n");
return file_t(new file(m_fs, bdir.rodata() + off, key));
}
filesystem_t::dir_t fs_oric_jasmin::impl::root_dir::dir_get(uint64_t key)
{
fatalerror("Directories not supported\n");
}
fs_oric_jasmin::impl::file::file(impl &fs, const u8 *entry, u16 key) : m_fs(fs), m_key(key)
{
memcpy(m_entry, entry, 18);
(void)m_key;
}
void fs_oric_jasmin::impl::file::drop_weak_references()
{
}
fs_meta_data fs_oric_jasmin::impl::file::metadata()
{
fs_meta_data res;
res[fs_meta_name::name] = read_file_name(m_entry + 3);
res[fs_meta_name::locked] = m_entry[2] == 'L';
res[fs_meta_name::sequential] = m_entry[0xf] == 'S';
res[fs_meta_name::size_in_blocks] = uint64_t(m_entry[0x10] | (m_entry[0x11] << 8));
u16 ref = (m_entry[0] << 8) | m_entry[1];
auto dblk = m_fs.m_blockdev.get(cs_to_block(ref));
res[fs_meta_name::loading_address] = uint64_t(dblk.r16l(2));
res[fs_meta_name::length] = uint64_t(dblk.r16l(4));
return res;
}
std::vector<u8> fs_oric_jasmin::impl::file::read_all()
{
auto [sect, length] = build_data_sector_table();
std::vector<u8> data(length);
u32 pos = 0;
for(u16 ref : sect) {
u32 npos = pos + 256;
if(npos > length)
npos = length;
if(npos > pos) {
auto dblk = m_fs.m_blockdev.get(cs_to_block(ref));
memcpy(data.data() + pos, dblk.rodata(), npos - pos);
} else
break;
pos = npos;
}
return data;
}
std::vector<u8> fs_oric_jasmin::impl::file::read(u64 start, u64 length)
{
auto [sect, rlength] = build_data_sector_table();
length += start;
if(length > rlength)
length = rlength;
if(rlength < start)
return std::vector<u8>();
std::vector<u8> data(length - start);
u32 pos = 0;
for(u16 ref : sect) {
u32 npos = pos + 256;
if(npos > length)
npos = length;
if(npos > pos) {
if(npos > start) {
u32 off = pos < start ? start & 0xff : 0;
auto dblk = m_fs.m_blockdev.get(cs_to_block(ref));
memcpy(data.data() + pos + off - start, dblk.rodata() + off, npos - pos - off);
}
} else
break;
pos = npos;
}
return data;
}
std::pair<std::vector<u16>, u32> fs_oric_jasmin::impl::file::build_data_sector_table()
{
std::pair<std::vector<u16>, u32> res;
u16 ref = (m_entry[0] << 8) | m_entry[1];
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; pos += 2) {
u16 dref = iblk.r16b(pos);
if(!m_fs.ref_valid(dref))
goto done;
res.first.push_back(dref);
}
ref = iblk.r16b(2);
if(!m_fs.ref_valid(ref))
break;
iblk = m_fs.m_blockdev.get(cs_to_block(ref));
}
done:
if(length > 256 * res.first.size())
length = 256 * res.first.size();
res.second = length;
return res;
}
fs_oric_jasmin::impl::system_file::system_file(impl &fs, const u8 *entry) : m_fs(fs)
{
memcpy(m_entry, entry, 18);
}
void fs_oric_jasmin::impl::system_file::drop_weak_references()
{
}
fs_meta_data fs_oric_jasmin::impl::system_file::metadata()
{
fs_meta_data res;
res[fs_meta_name::name] = read_file_name(m_entry + 3);
res[fs_meta_name::locked] = m_entry[2] == 'L';
res[fs_meta_name::sequential] = m_entry[0xf] == 'S';
res[fs_meta_name::size_in_blocks] = uint64_t(m_entry[0x10] | (m_entry[0x11] << 8));
res[fs_meta_name::length] = uint64_t(0x3e00);
return res;
}
std::vector<u8> fs_oric_jasmin::impl::system_file::read_all()
{
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);
}
return data;
}
std::vector<u8> fs_oric_jasmin::impl::system_file::read(u64 start, u64 length)
{
if(start >= 0x3e00)
return std::vector<u8>();
length += start;
if(length > 0x3e00)
length = 0x3e00;
std::vector<u8> data(length - start);
for(u32 i = start / 256; i <= (length - 1) / 256; i++) {
u32 pos = i * 256;
u32 off = i * 256 < start ? start & 0xff : 0;
u32 len = i * 256 > length ? length & 0xff : 0x100;
auto dblk = m_fs.m_blockdev.get(i);
memcpy(data.data() + pos + off - start, dblk.rodata() + off, len);
}
return data;
}
const filesystem_manager_type FS_ORIC_JASMIN = &filesystem_manager_creator<fs_oric_jasmin>;;

View File

@ -1,7 +1,7 @@
// license:BSD-3-Clause
// copyright-holders:Olivier Galibert
// Creation of Oric Jasmin floppy images
// Management of Oric Jasmin floppy images
#ifndef MAME_FORMATS_FS_ORIC_JASMIN_H
#define MAME_FORMATS_FS_ORIC_JASMIN_H
@ -14,11 +14,75 @@ class fs_oric_jasmin : public filesystem_manager_t {
public:
class impl : public filesystem_t {
public:
impl(fsblk_t &blockdev) : filesystem_t(blockdev, 256) {}
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 fs_meta_data metadata() override;
virtual std::vector<fs_dir_entry> contents() override;
virtual file_t file_get(uint64_t key) override;
virtual dir_t dir_get(uint64_t key) override;
private:
impl &m_fs;
std::pair<fsblk_t::block_t, u32> get_dir_block(uint64_t key);
};
class file : public ifile_t {
public:
file(impl &fs, const u8 *entry, u16 key);
virtual ~file() = default;
virtual void drop_weak_references() override;
virtual fs_meta_data metadata() override;
virtual std::vector<u8> read_all() override;
virtual std::vector<u8> read(u64 start, u64 length) override;
private:
impl &m_fs;
u16 m_key;
u8 m_entry[18];
std::pair<std::vector<u16>, u32> build_data_sector_table();
};
class system_file : public ifile_t {
public:
system_file(impl &fs, const u8 *entry);
virtual ~system_file() = default;
virtual void drop_weak_references() override;
virtual fs_meta_data metadata() override;
virtual std::vector<u8> read_all() override;
virtual std::vector<u8> read(u64 start, u64 length) override;
private:
impl &m_fs;
u8 m_entry[18];
};
impl(fsblk_t &blockdev);
virtual ~impl() = default;
virtual void format(const fs_meta_data &meta) override;
virtual fs_meta_data metadata() override;
virtual dir_t root() override;
static u32 cs_to_block(u16 ref);
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();
private:
dir_t m_root;
};
fs_oric_jasmin() : filesystem_manager_t() {}
@ -29,7 +93,6 @@ public:
virtual bool can_format() const override;
virtual bool can_read() const override;
virtual bool can_write() const override;
virtual bool has_subdirectories() const override;
virtual std::vector<fs_meta_description> volume_meta_description() const override;
virtual std::vector<fs_meta_description> file_meta_description() const override;

View File

@ -70,9 +70,9 @@ bool fs_prodos::can_write() const
return false;
}
bool fs_prodos::has_subdirectories() const
char fs_prodos::directory_separator() const
{
return true;
return '/';
}
std::vector<fs_meta_description> fs_prodos::volume_meta_description() const

View File

@ -31,7 +31,7 @@ public:
virtual bool can_format() const override;
virtual bool can_read() const override;
virtual bool can_write() const override;
virtual bool has_subdirectories() const override;
virtual char directory_separator() const override;
virtual std::vector<fs_meta_description> volume_meta_description() const override;
virtual std::vector<fs_meta_description> file_meta_description() const override;

View File

@ -116,9 +116,4 @@ bool fs_unformatted::can_write() const
return false;
}
bool fs_unformatted::has_subdirectories() const
{
return false;
}
const filesystem_manager_type FS_UNFORMATTED = &filesystem_manager_creator<fs_unformatted>;

View File

@ -46,7 +46,6 @@ public:
virtual bool can_format() const override;
virtual bool can_read() const override;
virtual bool can_write() const override;
virtual bool has_subdirectories() const override;
};
extern const filesystem_manager_type FS_UNFORMATTED;

View File

@ -8,17 +8,17 @@
#include "emu.h"
#include "fsmgr.h"
void fsblk_t::iblock_t::ref()
void fs_refcounted_inner::ref()
{
m_ref ++;
}
void fsblk_t::iblock_t::ref_weak()
void fs_refcounted_inner::ref_weak()
{
m_weak_ref ++;
}
void fsblk_t::iblock_t::unref()
void fs_refcounted_inner::unref()
{
m_ref --;
if(m_ref == 0) {
@ -31,7 +31,7 @@ void fsblk_t::iblock_t::unref()
}
}
void fsblk_t::iblock_t::unref_weak()
void fs_refcounted_inner::unref_weak()
{
m_weak_ref --;
if(m_weak_ref == 0 && m_ref == 0)
@ -39,6 +39,7 @@ void fsblk_t::iblock_t::unref_weak()
}
void filesystem_manager_t::enumerate_f(floppy_enumerator &fe, uint32_t form_factor, const std::vector<uint32_t> &variants) const
{
}
@ -89,6 +90,11 @@ std::vector<fs_meta_description> filesystem_manager_t::directory_meta_descriptio
return res;
}
char filesystem_manager_t::directory_separator() const
{
return 0; // Subdirectories not supported by default
}
void filesystem_t::format(const fs_meta_data &meta)
{
fatalerror("format called on a filesystem not supporting it.\n");
@ -126,44 +132,52 @@ const uint8_t *fsblk_t::iblock_t::rooffset(const char *function, uint32_t off, u
void fsblk_t::block_t::copy(u32 offset, const uint8_t *src, u32 size)
{
uint8_t *blk = m_block->offset("copy", offset, size);
uint8_t *blk = m_object->offset("copy", offset, size);
memcpy(blk, src, size);
}
void fsblk_t::block_t::fill(u32 offset, uint8_t data, u32 size)
{
uint8_t *blk = m_block->offset("fill", offset, size);
uint8_t *blk = m_object->offset("fill", offset, size);
memset(blk, data, size);
}
void fsblk_t::block_t::fill(uint8_t data)
{
uint8_t *blk = m_block->data();
memset(blk, data, m_block->size());
uint8_t *blk = m_object->data();
memset(blk, data, m_object->size());
}
void fsblk_t::block_t::wstr(u32 offset, const std::string &str)
{
uint8_t *blk = m_block->offset("wstr", offset, str.size());
uint8_t *blk = m_object->offset("wstr", offset, str.size());
memcpy(blk, str.data(), str.size());
}
void fsblk_t::block_t::w8(u32 offset, uint8_t data)
{
uint8_t *blk = m_block->offset("w8", offset, 1);
uint8_t *blk = m_object->offset("w8", offset, 1);
blk[0] = data;
}
void fsblk_t::block_t::w16b(u32 offset, u16 data)
{
uint8_t *blk = m_block->offset("w16b", offset, 2);
uint8_t *blk = m_object->offset("w16b", offset, 2);
blk[0] = data >> 8;
blk[1] = data;
}
void fsblk_t::block_t::w24b(u32 offset, u32 data)
{
uint8_t *blk = m_object->offset("w24b", offset, 3);
blk[0] = data >> 16;
blk[1] = data >> 8;
blk[2] = data;
}
void fsblk_t::block_t::w32b(u32 offset, u32 data)
{
uint8_t *blk = m_block->offset("w32b", offset, 4);
uint8_t *blk = m_object->offset("w32b", offset, 4);
blk[0] = data >> 24;
blk[1] = data >> 16;
blk[2] = data >> 8;
@ -172,14 +186,22 @@ void fsblk_t::block_t::w32b(u32 offset, u32 data)
void fsblk_t::block_t::w16l(u32 offset, u16 data)
{
uint8_t *blk = m_block->offset("w16l", offset, 2);
uint8_t *blk = m_object->offset("w16l", offset, 2);
blk[0] = data;
blk[1] = data >> 8;
}
void fsblk_t::block_t::w24l(u32 offset, u32 data)
{
uint8_t *blk = m_object->offset("w24l", offset, 3);
blk[0] = data;
blk[1] = data >> 8;
blk[2] = data >> 16;
}
void fsblk_t::block_t::w32l(u32 offset, u32 data)
{
uint8_t *blk = m_block->offset("w32l", offset, 4);
uint8_t *blk = m_object->offset("w32l", offset, 4);
blk[0] = data;
blk[1] = data >> 8;
blk[2] = data >> 16;
@ -188,13 +210,55 @@ void fsblk_t::block_t::w32l(u32 offset, u32 data)
std::string fsblk_t::block_t::rstr(u32 offset, u32 size)
{
const u8 *d = m_block->rooffset("rstr", offset, size);
const u8 *d = m_object->rooffset("rstr", offset, size);
std::string res;
for(u32 i=0; i != size; i++)
res += char(*d++);
return res;
}
uint8_t fsblk_t::block_t::r8(u32 offset)
{
const uint8_t *blk = m_object->offset("r8", offset, 1);
return blk[0];
}
uint16_t fsblk_t::block_t::r16b(u32 offset)
{
const uint8_t *blk = m_object->offset("r16b", offset, 2);
return (blk[0] << 8) | blk[1];
}
uint32_t fsblk_t::block_t::r24b(u32 offset)
{
const uint8_t *blk = m_object->offset("r24b", offset, 3);
return (blk[0] << 16) | (blk[1] << 8) | blk[2];
}
uint32_t fsblk_t::block_t::r32b(u32 offset)
{
const uint8_t *blk = m_object->offset("r32b", offset, 4);
return (blk[0] << 24) | (blk[1] << 16) | (blk[2] << 8) | blk[3];
}
uint16_t fsblk_t::block_t::r16l(u32 offset)
{
const uint8_t *blk = m_object->offset("r16l", offset, 2);
return blk[0] | (blk[1] << 8);
}
uint32_t fsblk_t::block_t::r24l(u32 offset)
{
const uint8_t *blk = m_object->offset("r24l", offset, 3);
return blk[0] | (blk[1] << 8) | (blk[2] << 16);
}
uint32_t fsblk_t::block_t::r32l(u32 offset)
{
const uint8_t *blk = m_object->offset("r32l", offset, 4);
return blk[0] | (blk[1] << 8) | (blk[2] << 16) | (blk[3] << 24);
}
const char *fs_meta_get_name(fs_meta_name name)
{
switch(name) {

View File

@ -31,6 +31,12 @@ enum class fs_meta_type {
flag,
};
enum class fs_dir_entry_type {
dir,
file,
system_file,
};
using fs_meta = std::variant<std::string, uint64_t, bool>;
using fs_meta_data = std::unordered_map<fs_meta_name, fs_meta>;
@ -38,6 +44,77 @@ const char *fs_meta_get_name(fs_meta_name name);
std::string fs_meta_to_string(fs_meta_type type, const fs_meta &m);
fs_meta fs_meta_from_string(fs_meta_type type, std::string value);
template<typename T> class fs_refcounted_outer {
public:
fs_refcounted_outer(bool weak = false) : m_object(nullptr), m_is_weak_ref(weak) {}
fs_refcounted_outer(T *object, bool weak = false) : m_object(object), m_is_weak_ref(weak) {
ref();
}
fs_refcounted_outer(const fs_refcounted_outer &cref) {
m_object = cref.m_object;
m_is_weak_ref = cref.m_is_weak_ref;
ref();
}
fs_refcounted_outer(fs_refcounted_outer &&cref) {
m_object = cref.m_object;
m_is_weak_ref = cref.m_is_weak_ref;
cref.m_object = nullptr;
}
~fs_refcounted_outer() {
unref();
}
fs_refcounted_outer<T> &operator =(T *dir) {
unref();
m_object = dir;
ref();
return *this;
}
fs_refcounted_outer<T> &operator =(const fs_refcounted_outer<T> &cref) {
unref();
m_object = cref.m_object;
m_is_weak_ref = cref.m_is_weak_ref;
ref();
return *this;
}
fs_refcounted_outer<T> &operator =(fs_refcounted_outer<T> &&cref) {
m_object = cref.m_object;
m_is_weak_ref = cref.m_is_weak_ref;
cref.m_object = nullptr;
return *this;
}
operator bool() const { return m_object != nullptr; }
protected:
T *m_object;
bool m_is_weak_ref;
private:
void ref() {
if(m_object) {
if(m_is_weak_ref)
m_object->ref_weak();
else
m_object->ref();
}
}
void unref() {
if(m_object) {
if(m_is_weak_ref)
m_object->unref_weak();
else
m_object->unref();
}
}
};
struct fs_meta_description {
fs_meta_name m_name;
@ -56,111 +133,82 @@ struct fs_meta_description {
{}
};
class fs_refcounted_inner {
public:
fs_refcounted_inner() : m_ref(0), m_weak_ref(0) {}
virtual ~fs_refcounted_inner() = default;
void ref();
void ref_weak();
void unref();
void unref_weak();
virtual void drop_weak_references() = 0;
private:
uint32_t m_ref, m_weak_ref;
};
struct fs_dir_entry {
std::string m_name;
fs_dir_entry_type m_type;
uint64_t m_key;
fs_dir_entry(const std::string &name, fs_dir_entry_type type, uint64_t key) : m_name(name), m_type(type), m_key(key) {}
};
class fsblk_t {
protected:
class iblock_t {
class iblock_t : public fs_refcounted_inner {
public:
iblock_t(uint32_t size) : m_ref(0), m_weak_ref(0), m_size(size) {}
iblock_t(uint32_t size) : fs_refcounted_inner(), m_size(size) {}
virtual ~iblock_t() = default;
void ref();
void ref_weak();
void unref();
void unref_weak();
uint32_t size() const { return m_size; }
virtual void drop_weak_references() = 0;
virtual const uint8_t *rodata() = 0;
virtual uint8_t *data() = 0;
uint8_t *offset(const char *function, uint32_t off, uint32_t size);
const uint8_t *rooffset(const char *function, uint32_t off, uint32_t size);
protected:
uint32_t m_ref, m_weak_ref;
uint32_t m_size;
};
public:
class block_t {
class block_t : public fs_refcounted_outer<iblock_t> {
public:
block_t(iblock_t *block, bool weak = false) : m_block(block), m_is_weak_ref(weak) {
ref();
}
block_t(bool weak = false) : fs_refcounted_outer<iblock_t>(weak) {}
block_t(iblock_t *block, bool weak = false) : fs_refcounted_outer(block, weak) {}
virtual ~block_t() = default;
block_t(const block_t &cref) {
m_block = cref.m_block;
m_is_weak_ref = cref.m_is_weak_ref;
ref();
}
block_t strong() { return block_t(m_object, false); }
block_t weak() { return block_t(m_object, true); }
block_t(block_t &&cref) {
m_block = cref.m_block;
m_is_weak_ref = cref.m_is_weak_ref;
cref.m_block = nullptr;
}
~block_t() {
unref();
}
block_t &operator =(const block_t &cref) {
m_block = cref.m_block;
m_is_weak_ref = cref.m_is_weak_ref;
ref();
return *this;
}
block_t &operator =(block_t &&cref) {
m_block = cref.m_block;
m_is_weak_ref = cref.m_is_weak_ref;
cref.m_block = nullptr;
return *this;
}
const uint8_t *rodata() {
return m_block ? m_block->rodata() : nullptr;
}
uint8_t *data() {
return m_block ? m_block->data() : nullptr;
}
const uint8_t *rodata() { return m_object->rodata(); }
uint8_t *data() { return m_object->data(); }
void copy(uint32_t offset, const uint8_t *src, uint32_t size);
void fill( uint8_t data);
void fill( uint8_t data);
void fill(uint32_t offset, uint8_t data, uint32_t size);
void wstr(uint32_t offset, const std::string &str);
void w8( uint32_t offset, uint8_t data);
void w16b(uint32_t offset, u16 data);
void w16b(uint32_t offset, uint16_t data);
void w24b(uint32_t offset, uint32_t data);
void w32b(uint32_t offset, uint32_t data);
void w16l(uint32_t offset, u16 data);
void w16l(uint32_t offset, uint16_t data);
void w24l(uint32_t offset, uint32_t data);
void w32l(uint32_t offset, uint32_t data);
std::string rstr(uint32_t offset, uint32_t size);
private:
iblock_t *m_block;
bool m_is_weak_ref;
void ref() {
if(m_block) {
if(m_is_weak_ref)
m_block->ref_weak();
else
m_block->ref();
}
}
void unref() {
if(m_block) {
if(m_is_weak_ref)
m_block->unref_weak();
else
m_block->unref();
}
}
uint8_t r8( uint32_t offset);
uint16_t r16b(uint32_t offset);
uint32_t r24b(uint32_t offset);
uint32_t r32b(uint32_t offset);
uint16_t r16l(uint32_t offset);
uint32_t r24l(uint32_t offset);
uint32_t r32l(uint32_t offset);
};
fsblk_t() : m_block_size(0) {}
@ -177,148 +225,61 @@ protected:
class filesystem_t {
public:
class dir_t;
class file_t;
protected:
class idir_t {
class idir_t : public fs_refcounted_inner {
public:
virtual ~idir_t();
idir_t() : fs_refcounted_inner() {}
virtual ~idir_t() = default;
void ref();
void ref_weak();
void unref();
void unref_weak();
virtual void drop_weak_references();
virtual fs_meta_data metadata() = 0;
virtual std::vector<fs_dir_entry> contents() = 0;
virtual file_t file_get(uint64_t key) = 0;
virtual dir_t dir_get(uint64_t key) = 0;
};
class ifile_t {
class ifile_t : public fs_refcounted_inner {
public:
virtual ~ifile_t();
ifile_t() : fs_refcounted_inner() {}
virtual ~ifile_t() = default;
void ref();
void ref_weak();
void unref();
void unref_weak();
virtual void drop_weak_references();
virtual fs_meta_data metadata() = 0;
virtual std::vector<u8> read_all() = 0;
virtual std::vector<u8> read(u64 start, u64 length) = 0;
};
public:
class dir_t {
class dir_t : public fs_refcounted_outer<idir_t> {
public:
dir_t(idir_t *dir, bool weak = false) : m_dir(dir), m_is_weak_ref(weak) {
ref();
}
dir_t(bool weak = false) : fs_refcounted_outer<idir_t>(weak) {}
dir_t(idir_t *dir, bool weak = false) : fs_refcounted_outer(dir, weak) {}
virtual ~dir_t() = default;
dir_t(const dir_t &cref) {
m_dir = cref.m_dir;
m_is_weak_ref = cref.m_is_weak_ref;
ref();
}
dir_t strong() { return dir_t(m_object, false); }
dir_t weak() { return dir_t(m_object, true); }
dir_t(dir_t &&cref) {
m_dir = cref.m_dir;
m_is_weak_ref = cref.m_is_weak_ref;
cref.m_dir = nullptr;
}
~dir_t() {
unref();
}
dir_t &operator =(const dir_t &cref) {
m_dir = cref.m_dir;
m_is_weak_ref = cref.m_is_weak_ref;
ref();
return *this;
}
dir_t &operator =(dir_t &&cref) {
m_dir = cref.m_dir;
m_is_weak_ref = cref.m_is_weak_ref;
cref.m_dir = nullptr;
return *this;
}
private:
idir_t *m_dir;
bool m_is_weak_ref;
void ref() {
if(m_dir) {
if(m_is_weak_ref)
m_dir->ref_weak();
else
m_dir->ref();
}
}
void unref() {
if(m_dir) {
if(m_is_weak_ref)
m_dir->unref_weak();
else
m_dir->unref();
}
}
fs_meta_data metadata() { return m_object->metadata(); }
std::vector<fs_dir_entry> contents() { return m_object->contents(); }
file_t file_get(uint64_t key) { return m_object->file_get(key); }
dir_t dir_get(uint64_t key) { return m_object->dir_get(key); }
};
class file_t {
class file_t : public fs_refcounted_outer<ifile_t> {
public:
file_t(ifile_t *file, bool weak = false) : m_file(file), m_is_weak_ref(weak) {
ref();
}
file_t(bool weak = false) : fs_refcounted_outer<ifile_t>(weak) {}
file_t(ifile_t *file, bool weak = false) : fs_refcounted_outer(file, weak) {}
virtual ~file_t() = default;
file_t(const file_t &cref) {
m_file = cref.m_file;
m_is_weak_ref = cref.m_is_weak_ref;
ref();
}
file_t strong() { return file_t(m_object, false); }
file_t weak() { return file_t(m_object, true); }
file_t(file_t &&cref) {
m_file = cref.m_file;
m_is_weak_ref = cref.m_is_weak_ref;
cref.m_file = nullptr;
}
fs_meta_data metadata() { return m_object->metadata(); }
~file_t() {
unref();
}
file_t &operator =(const file_t &cref) {
m_file = cref.m_file;
m_is_weak_ref = cref.m_is_weak_ref;
ref();
return *this;
}
file_t &operator =(file_t &&cref) {
m_file = cref.m_file;
m_is_weak_ref = cref.m_is_weak_ref;
cref.m_file = nullptr;
return *this;
}
private:
ifile_t *m_file;
bool m_is_weak_ref;
void ref() {
if(m_file) {
if(m_is_weak_ref)
m_file->ref_weak();
else
m_file->ref();
}
}
void unref() {
if(m_file) {
if(m_is_weak_ref)
m_file->unref_weak();
else
m_file->unref();
}
}
std::vector<u8> read_all() { return m_object->read_all(); }
std::vector<u8> read(u32 start, u32 length) { return m_object->read(start, length); }
};
filesystem_t(fsblk_t &blockdev, u32 size) : m_blockdev(blockdev) {
@ -368,7 +329,9 @@ public:
virtual bool can_format() const = 0;
virtual bool can_read() const = 0;
virtual bool can_write() const = 0;
virtual bool has_subdirectories() const = 0;
virtual char directory_separator() const;
bool has_subdirectories() const { return directory_separator() != 0; }
virtual std::vector<fs_meta_description> volume_meta_description() const;
virtual std::vector<fs_meta_description> file_meta_description() const;

View File

@ -277,7 +277,8 @@ static void display_usage()
fprintf(stderr, " floptool.exe identify <inputfile> [<inputfile> ...]\n");
fprintf(stderr, " floptool.exe convert [input_format|auto] output_format <inputfile> <outputfile>\n");
fprintf(stderr, " floptool.exe create output_format filesystem <outputfile>\n");
fprintf(stderr, " floptool.exe dir input_format filesystem <inputfile>\n");
fprintf(stderr, " floptool.exe dir input_format filesystem <image>\n");
fprintf(stderr, " floptool.exe read input_format filesystem <image> <path> <outputfile>\n");
}
static void display_formats()
@ -502,6 +503,51 @@ static int create(int argc, char *argv[])
return 0;
}
static void dir_scan(u32 depth, 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)
{
std::string head;
for(u32 i = 0; i != depth; i++)
head += " ";
auto contents = dir.contents();
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();
for(const auto &m : dmetad) {
if(meta.find(m.m_name) == meta.end())
continue;
size_t slot = nmap.find(m.m_name)->second;
std::string val = fs_meta_to_string(m.m_type, meta.find(m.m_name)->second);
if(slot == 0)
val = head + "dir " + val;
entries[id][slot] = val;
}
dir_scan(depth+1, subdir, 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();
for(const auto &m : fmetad) {
if(meta.find(m.m_name) == meta.end())
continue;
size_t slot = nmap.find(m.m_name)->second;
std::string val = fs_meta_to_string(m.m_type, meta.find(m.m_name)->second);
if(slot == 0)
val = head + (c.m_type == fs_dir_entry_type::system_file ? "sys " : "file ") + val;
entries[id][slot] = val;
}
break;
}
}
}
}
static int dir(int argc, char *argv[])
{
if (argc!=5) {
@ -555,15 +601,169 @@ static int dir(int argc, char *argv[])
fsblk_vec_t blockdev(img);
auto load_fs = fs->m_manager->mount(blockdev);
auto vmetad = fs->m_manager->volume_meta_description();
auto vmeta = load_fs->metadata();
auto fmetad = fs->m_manager->file_meta_description();
auto dmetad = fs->m_manager->directory_meta_description();
auto vmeta = load_fs->metadata();
if(!vmeta.empty()) {
std::string vinf = "Volume:";
for(const auto &e : vmetad)
vinf += util::string_format(" %s=%s", fs_meta_get_name(e.m_name), fs_meta_to_string(e.m_type, vmeta[e.m_name]));
printf("%s\n", vinf.c_str());
printf("%s\n\n", vinf.c_str());
}
std::vector<fs_meta_name> names;
names.push_back(fs_meta_name::name);
for(const auto &e : fmetad)
if(e.m_name != fs_meta_name::name)
names.push_back(e.m_name);
for(const auto &e : dmetad)
if(std::find(names.begin(), names.end(), e.m_name) == names.end())
names.push_back(e.m_name);
std::unordered_map<fs_meta_name, size_t> nmap;
for(size_t i = 0; i != names.size(); i++)
nmap[names[i]] = i;
auto root = load_fs->root();
std::vector<std::vector<std::string>> entries;
entries.resize(1);
for(fs_meta_name n : names)
entries[0].push_back(fs_meta_get_name(n));
dir_scan(0, root, entries, nmap, names.size(), dmetad, fmetad);
std::vector<u32> sizes(names.size());
for(const auto &e : entries)
for(unsigned int i=0; i != names.size(); i++)
sizes[i] = std::max<u32>(sizes[i], e[i].size());
for(const auto &e : entries) {
std::string l;
for(unsigned int i=0; i != names.size(); i++) {
if(i)
l += ' ';
l += util::string_format("%-*s", sizes[i], e[i]);
}
printf("%s\n", l.c_str());
}
return 0;
}
static int doread(int argc, char *argv[])
{
if (argc!=7) {
fprintf(stderr, "Incorrect number of arguments.\n\n");
display_usage();
return 1;
}
auto format = find_format_by_name(argv[2]);
if(!format) {
fprintf(stderr, "Error: Format '%s' unknown\n", argv[3]);
return 1;
}
auto fs = find_fs_by_name(argv[3]);
if(!fs) {
fprintf(stderr, "Error: Filesystem '%s' unknown\n", argv[2]);
return 1;
}
if(!fs->m_manager || !fs->m_manager->can_read()) {
fprintf(stderr, "Error: Filesystem '%s' does not implement reading\n", argv[2]);
return 1;
}
char msg[4096];
sprintf(msg, "Error opening %s for reading", argv[4]);
FILE *f = fopen(argv[4], "rb");
if (!f) {
perror(msg);
return 1;
}
io_generic io;
io.file = f;
io.procs = &stdio_ioprocs_noclose;
io.filler = 0xff;
floppy_image image(84, 2, floppy_image::FF_UNKNOWN);
if(!format->load(&io, floppy_image::FF_UNKNOWN, variants, &image)) {
fprintf(stderr, "Error: parsing input file as '%s' failed\n", format->name());
return 1;
}
std::vector<u8> img;
auto iog = ram_open(img);
auto load_format = fs->m_type();
load_format->save(iog, variants, &image);
delete load_format;
delete iog;
fsblk_vec_t blockdev(img);
auto load_fs = fs->m_manager->mount(blockdev);
std::string opath = argv[5];
std::vector<std::string> path;
if(fs->m_manager->has_subdirectories()) {
abort();
} else
path.push_back(opath);
auto dir = load_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(), fs->m_manager->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(), fs->m_manager->directory_separator(), path[i].c_str());
return 1;
}
dir = dir.dir_get(c[j].m_key);
apath += fs->m_manager->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(), fs->m_manager->directory_separator(), path.back().c_str());
return 1;
}
auto file = dir.file_get(c[j].m_key);
auto meta = file.metadata();
if(meta.find(fs_meta_name::length) == meta.end()) {
fprintf(stderr, "Error: %s%c%s is not a readable file\n", apath.c_str(), fs->m_manager->directory_separator(), path.back().c_str());
return 1;
}
auto filedata = file.read_all();
sprintf(msg, "Error opening %s for writing", argv[6]);
auto fo = fopen(argv[6], "wb");
if (!fo) {
perror(msg);
return 1;
}
fwrite(filedata.data(), filedata.size(), 1, fo);
fclose(fo);
return 0;
}
@ -585,6 +785,8 @@ int CLIB_DECL main(int argc, char *argv[])
return create(argc, argv);
else if (!core_stricmp("dir", argv[1]))
return dir(argc, argv);
else if (!core_stricmp("read", argv[1]))
return doread(argc, argv);
else {
fprintf(stderr, "Unknown command '%s'\n\n", argv[1]);
display_usage();