diff options
Diffstat (limited to 'src/lib/formats/fs_oric_jasmin.cpp')
-rw-r--r-- | src/lib/formats/fs_oric_jasmin.cpp | 309 |
1 files changed, 290 insertions, 19 deletions
diff --git a/src/lib/formats/fs_oric_jasmin.cpp b/src/lib/formats/fs_oric_jasmin.cpp index 59ff60fd4b7..8cb6444aa9c 100644 --- a/src/lib/formats/fs_oric_jasmin.cpp +++ b/src/lib/formats/fs_oric_jasmin.cpp @@ -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>;; |