Initial FAT file system support for Floptool (#9119)

* Initial FAT file system support for Floptool

Current Limitations:
- Read only
- Only supports floppy disks
- No FAT32 support
- No Long Filenames Support
This commit is contained in:
npwoods 2022-08-25 14:48:45 -04:00 committed by GitHub
parent 9558786f3b
commit 695a9eb091
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 910 additions and 1 deletions

View File

@ -2136,6 +2136,18 @@ if opt_tool(FORMATS, "FS_PRODOS") then
}
end
--------------------------------------------------
--
--@src/lib/formats/fs_fat.h,FORMATS["FS_FAT"] = true
--------------------------------------------------
if opt_tool(FORMATS, "FS_FAT") then
files {
MAME_DIR.. "src/lib/formats/fs_fat.cpp",
MAME_DIR.. "src/lib/formats/fs_fat.h",
}
end
--------------------------------------------------
--
--@src/lib/formats/fs_oric_jasmin.h,FORMATS["FS_ORIC_JASMIN"] = true

View File

@ -708,6 +708,10 @@
#include "fs_prodos.h"
#endif
#ifdef HAS_FORMATS_FS_FAT
#include "fs_fat.h"
#endif
void mame_formats_full_list(mame_formats_enumerator &en)
{
en.category("Generic");
@ -718,6 +722,9 @@ void mame_formats_full_list(mame_formats_enumerator &en)
#endif
en.add(FLOPPY_MFI_FORMAT); // mfi_dsk.h
en.add(FLOPPY_DFI_FORMAT); // dfi_dsk.h
#ifdef HAS_FORMATS_FS_FAT
en.add(fs::PC_FAT);
#endif
en.category("Container FM/MFM");
en.add(FLOPPY_HFE_FORMAT); // hxchfe_dsk.h

828
src/lib/formats/fs_fat.cpp Normal file
View File

@ -0,0 +1,828 @@
// license:BSD-3-Clause
// copyright-holders:Nathan Woods
/***************************************************************************
fs_fat.cpp
PC FAT disk images
Current Limitations:
- Read only
- Only supports floppy disks
- No FAT32 support
- No Long Filenames Support
*****************************************************************************
Master boot record format:
Offset Length Description
------ ------ -----------
0 446 Boot machine code
446 16 Partion #1 info
462 16 Partion #2 info
478 16 Partion #3 info
494 16 Partion #4 info
510 2 Magic bytes (0x55 0xAA)
Partition info format:
Offset Length Description
------ ------ -----------
0 1 Active byte (0x80=active 0x00=inactive)
1 1 Starting head
2 1 Starting sector (bits 5-0) and high bits of starting track (bits 6-5)
3 1 Low bits of starting track
4 1 Partition type:
0x00 Unused
0x?1 FAT12 (0-15 MB)
0x?2 XENIX
0x?4 FAT16 (16-32 MB)
0x?6 FAT16` (32 MB-2 GB)
0x?7 HPFS or NTFS
0x?A Boot Manager
0x?B FAT32 (512 MB-2 TB)
0x?C FAT32 (512 MB-2 TB LBA)
0x1? OS/2 Boot manager/Win95 hidden
0xC? DR-DOS secured partition
0xD? Multiuser DOS secured partition
0xE? SpeedStor extended partition
5 1 Ending head
6 1 Ending sector (bits 5-0) and high bits of ending track (bits 6-5)
7 1 Low bits of ending track
8 4 Sector index of beginning of partition
12 4 Total sectors in partition
Boot sector format:
Offset Length Description
------ ------ -----------
0 3 Jump instruction (to skip over header on boot)
3 8 OEM Name
11 2 Bytes per sector
13 1 Sectors per cluster
14 2 Reserved sector count (including boot sector)
16 1 Number of FATs (file allocation tables)
17 2 Number of root directory entries
19 2 Total sectors (bits 0-15)
21 1 Media descriptor
22 2 Sectors per FAT
24 2 Sectors per track
26 2 Number of heads
28 4 Hidden sectors
32 4 Total sectors (bits 16-47)
36 1 Physical drive number
37 1 Current head
38 1 Signature
39 4 ID
43 11 Volume Label
54 8 FAT file system type
62 448 Boot machine code
510 2 Magic bytes (0x55 0xAA)
For more information:
http://support.microsoft.com/kb/q140418/
Directory Entry Format:
Offset Length Description
------ ------ -----------
0 8 DOS File Name (padded with spaces)
8 3 DOS File Extension (padded with spaces)
11 1 File Attributes
12 2 Unknown
14 4 Time of Creation
18 2 Last Access Time
20 2 EA-Index (OS/2 stuff)
22 4 Last Modified Time
26 2 First Cluster
28 4 File Size
Dates and times are stored in separate words; when together, the time is
first and the date is second.
Time:
bits 15-11 Hour
bits 10- 5 Minute
bits 4- 0 Second / 2
Date:
bits 15- 9 Year - 1980
bits 8- 5 Month
bits 4- 0 Day
LFN Entry Format:
Offset Length Description
------ ------ -----------
0 1 Sequence Number (bit 6 is set on highest sequence)
1 10 Name characters (five UTF-16LE chars)
11 1 Attributes (always 0x0F)
12 1 Reserved (always 0x00)
13 1 Checksum of short filename entry
14 12 Name characters (six UTF-16LE chars)
26 2 Entry Cluster (always 0x00)
28 4 Name characters (two UTF-16LE chars)
Valid characters in DOS file names:
- Upper case letters A-Z
- Numbers 0-9
- Space (though there is no way to identify a trailing space)
- ! # $ % & ( ) - @ ^ _ ` { } ~
- Characters 128-255 (though the code page is indeterminate)
For more information:
http://en.wikipedia.org/wiki/File_Allocation_Table
****************************************************************************/
#include "fs_fat.h"
#include "pc_dsk.h"
#include "strformat.h"
#include "util/corestr.h"
#include "util/strformat.h"
using namespace fs;
const fs::pc_fat_image fs::PC_FAT;
//**************************************************************************
// TYPE DECLARATIONS
//**************************************************************************
namespace {
// ======================> directory_entry
class directory_entry
{
public:
static const int SIZE = 32;
directory_entry(const fsblk_t::block_t &block, u32 offset)
: m_block(block)
, m_offset(offset)
{
}
std::string_view raw_stem() const { return std::string_view((const char *) &m_block.rodata()[m_offset + 0], 8); }
std::string_view raw_ext() const { return std::string_view((const char *) &m_block.rodata()[m_offset + 8], 3); }
u8 attributes() const { return m_block.r8(m_offset + 11); }
u32 raw_create_datetime() const { return m_block.r32l(m_offset + 14); }
u32 raw_modified_datetime() const { return m_block.r32l(m_offset + 22); }
u32 start_cluster() const { return ((u32)m_block.r16l(m_offset + 20)) << 16 | m_block.r16l(m_offset + 26); }
u32 file_size() const { return m_block.r32l(m_offset + 28); }
bool is_read_only() const { return (attributes() & 0x01) != 0x00; }
bool is_hidden() const { return (attributes() & 0x02) != 0x00; }
bool is_system() const { return (attributes() & 0x04) != 0x00; }
bool is_volume_label() const { return (attributes() & 0x08) != 0x00; }
bool is_subdirectory() const { return (attributes() & 0x10) != 0x00; }
bool is_archive() const { return (attributes() & 0x20) != 0x00; }
std::string name() const;
meta_data metadata() const;
private:
fsblk_t::block_t m_block;
u32 m_offset;
};
// ======================> directory_span
class directory_span
{
public:
typedef std::unique_ptr<directory_span> ptr;
directory_span() = default;
virtual ~directory_span() = default;
virtual std::vector<u32> get_directory_sectors() const = 0;
};
// ======================> directory_entry
class impl : public filesystem_t
{
public:
// ctor/dtor
impl(fsblk_t &blockdev, fsblk_t::block_t &&boot_sector_block, std::vector<u8> &&file_allocation_table, u32 starting_sector, u32 sector_count, u16 reserved_sector_count, u8 bits_per_fat_entry);
virtual ~impl() = default;
// accessors
fsblk_t &blockdev() { return m_blockdev; }
u16 bytes_per_sector() const { return m_bytes_per_sector; }
u32 dirents_per_sector() const { return bytes_per_sector() / directory_entry::SIZE; }
// virtuals
virtual meta_data volume_metadata() override;
virtual std::pair<err_t, meta_data> metadata(const std::vector<std::string> &path) override;
virtual std::pair<err_t, std::vector<dir_entry>> directory_contents(const std::vector<std::string> &path) override;
virtual std::pair<err_t, std::vector<u8>> file_read(const std::vector<std::string> &path) override;
// methods
std::vector<u32> get_sectors_from_fat(const directory_entry &dirent) const;
private:
fsblk_t::block_t m_boot_sector_block;
std::vector<u8> m_file_allocation_table;
u32 m_starting_sector;
u32 m_sector_count;
u16 m_reserved_sector_count;
u16 m_bytes_per_sector;
u8 m_bits_per_fat_entry;
// methods
std::optional<directory_entry> find_entity(const std::vector<std::string> &path) const;
directory_span::ptr find_directory(std::vector<std::string>::const_iterator path_begin, std::vector<std::string>::const_iterator path_end) const;
std::optional<directory_entry> find_child(const directory_span &current_dir, std::string_view target) const;
void iterate_directory_entries(const directory_span &dir, const std::function<bool(const directory_entry &dirent)> &callback) const;
};
// ======================> root_directory_span
class root_directory_span : public directory_span
{
public:
root_directory_span(const impl &fs, u32 first_sector, u16 directory_entry_count);
virtual std::vector<u32> get_directory_sectors() const override;
private:
const impl & m_fs;
u32 m_first_sector;
u16 m_directory_entry_count;
};
// ======================> subdirectory_span
class subdirectory_span : public directory_span
{
public:
subdirectory_span(const impl &fs, directory_entry &&dirent);
virtual std::vector<u32> get_directory_sectors() const override;
private:
const impl & m_fs;
directory_entry m_dirent;
};
}
//**************************************************************************
// IMPLEMENTATION
//**************************************************************************
//-------------------------------------------------
// validate_filename
//-------------------------------------------------
namespace {
bool validate_filename(std::string_view name)
{
auto is_invalid_filename_char = [](char ch)
{
return ch == '\0' || strchr("\\/:*?\"<>|", ch);
};
return !name.empty()
&& std::find_if(name.begin(), name.end(), is_invalid_filename_char) == name.end();
}
//-------------------------------------------------
// decode_fat_datetime
//-------------------------------------------------
util::arbitrary_datetime decode_fat_datetime(u32 dt)
{
util::arbitrary_datetime result;
memset(&result, 0, sizeof(result));
result.year = ((dt >> 25) & 0x7F) + 1980;
result.month = (dt >> 21) & 0x0F;
result.day_of_month = (dt >> 16) & 0x1F;
result.hour = (dt >> 11) & 0x1F;
result.minute = (dt >> 5) & 0x3F;
result.second = ((dt >> 0) & 0x1F) * 2;
return result;
}
}
//-------------------------------------------------
// fat_image::can_format
//-------------------------------------------------
bool fs::fat_image::can_format() const
{
return false;
}
//-------------------------------------------------
// fat_image::can_read
//-------------------------------------------------
bool fs::fat_image::can_read() const
{
return true;
}
//-------------------------------------------------
// fat_image::can_write
//-------------------------------------------------
bool fs::fat_image::can_write() const
{
return false;
}
//-------------------------------------------------
// fat_image::has_rsrc
//-------------------------------------------------
bool fs::fat_image::has_rsrc() const
{
return false;
}
//-------------------------------------------------
// fat_image::directory_separator
//-------------------------------------------------
char fs::fat_image::directory_separator() const
{
return '\\';
}
//-------------------------------------------------
// fat_image::volume_meta_description
//-------------------------------------------------
std::vector<meta_description> fs::fat_image::volume_meta_description() const
{
std::vector<meta_description> results;
results.emplace_back(meta_name::name, "UNTITLED", false, [](const meta_value &m) { return m.as_string().size() <= 11; }, "Volume name, up to 11 characters");
results.emplace_back(meta_name::oem_name, "", false, [](const meta_value &m) { return m.as_string().size() <= 8; }, "OEM name, up to 8 characters");
return results;
}
//-------------------------------------------------
// fat_image::file_meta_description
//-------------------------------------------------
std::vector<meta_description> fs::fat_image::file_meta_description() const
{
std::vector<meta_description> results;
results.emplace_back(meta_name::name, "", false, [](const meta_value &m) { return validate_filename(m.as_string()); }, "File name");
results.emplace_back(meta_name::creation_date, util::arbitrary_datetime::now(), false, nullptr, "Creation time");
results.emplace_back(meta_name::modification_date, util::arbitrary_datetime::now(), false, nullptr, "Modification time");
results.emplace_back(meta_name::length, 0, true, nullptr, "Size of the file in bytes");
return results;
}
//-------------------------------------------------
// fat_image::directory_meta_description
//-------------------------------------------------
std::vector<meta_description> fs::fat_image::directory_meta_description() const
{
std::vector<meta_description> results;
results.emplace_back(meta_name::name, "", false, [](const meta_value &m) { return validate_filename(m.as_string()); }, "File name");
results.emplace_back(meta_name::creation_date, util::arbitrary_datetime::now(), false, nullptr, "Creation time");
results.emplace_back(meta_name::modification_date, util::arbitrary_datetime::now(), false, nullptr, "Modification time");
return results;
}
//-------------------------------------------------
// fat_image::mount_partition
//-------------------------------------------------
std::unique_ptr<filesystem_t> fs::fat_image::mount_partition(fsblk_t &blockdev, u32 starting_sector, u32 sector_count, u8 bits_per_fat_entry)
{
// load the boot sector block and get some basic info
fsblk_t::block_t boot_sector_block = blockdev.get(starting_sector);
u16 reserved_sector_count = boot_sector_block.r16l(14);
// load all file allocation table sectors
u32 fat_count = boot_sector_block.r8(16);
u32 sectors_per_fat = boot_sector_block.r16l(22);
u16 bytes_per_sector = boot_sector_block.r16l(11);
std::vector<u8> file_allocation_table;
file_allocation_table.reserve(fat_count * sectors_per_fat * bytes_per_sector);
for (auto i = 0; i < fat_count * sectors_per_fat; i++)
{
fsblk_t::block_t fatblk = blockdev.get(starting_sector + reserved_sector_count + i);
file_allocation_table.insert(file_allocation_table.end(), fatblk.rodata(), fatblk.rodata() + bytes_per_sector);
}
// and return the implementation
return std::make_unique<impl>(blockdev, std::move(boot_sector_block), std::move(file_allocation_table),
starting_sector, sector_count, reserved_sector_count, bits_per_fat_entry);
}
//-------------------------------------------------
// directory_entry::name
//-------------------------------------------------
std::string directory_entry::name() const
{
std::string_view stem = strtrimrightspace(raw_stem());
std::string_view ext = strtrimrightspace(raw_ext());
return !ext.empty()
? util::string_format("%s.%s", stem, ext)
: std::string(stem);
}
//-------------------------------------------------
// directory_entry::metadata
//-------------------------------------------------
meta_data directory_entry::metadata() const
{
meta_data result;
result.set(meta_name::name, name());
result.set(meta_name::creation_date, decode_fat_datetime(raw_create_datetime()));
result.set(meta_name::modification_date, decode_fat_datetime(raw_modified_datetime()));
result.set(meta_name::length, file_size());
return result;
}
//-------------------------------------------------
// impl ctor
//-------------------------------------------------
impl::impl(fsblk_t &blockdev, fsblk_t::block_t &&boot_sector_block, std::vector<u8> &&file_allocation_table, u32 starting_sector, u32 sector_count, u16 reserved_sector_count, u8 bits_per_fat_entry)
: filesystem_t(blockdev, 512)
, m_boot_sector_block(std::move(boot_sector_block))
, m_file_allocation_table(std::move(file_allocation_table))
, m_starting_sector(starting_sector)
, m_sector_count(sector_count)
, m_reserved_sector_count(reserved_sector_count)
, m_bytes_per_sector(boot_sector_block.r16l(11))
, m_bits_per_fat_entry(bits_per_fat_entry)
{
}
//-------------------------------------------------
// impl::volume_metadata
//-------------------------------------------------
meta_data impl::volume_metadata()
{
meta_data results;
results.set(meta_name::name, m_boot_sector_block.rstr(43, 11));
results.set(meta_name::oem_name, m_boot_sector_block.rstr(3, 8));
return results;
}
//-------------------------------------------------
// impl::metadata
//-------------------------------------------------
std::pair<err_t, meta_data> impl::metadata(const std::vector<std::string> &path)
{
std::optional<directory_entry> dirent = find_entity(path);
if (!dirent)
return std::make_pair(ERR_NOT_FOUND, meta_data());
return std::make_pair(ERR_OK, dirent->metadata());
}
//-------------------------------------------------
// impl::directory_contents
//-------------------------------------------------
std::pair<err_t, std::vector<dir_entry>> impl::directory_contents(const std::vector<std::string> &path)
{
directory_span::ptr dir = find_directory(path.begin(), path.end());
if (!dir)
return std::make_pair(ERR_NOT_FOUND, std::vector<dir_entry>());
std::vector<dir_entry> results;
auto callback = [&results](const directory_entry &dirent)
{
dir_entry_type entry_type = dirent.is_subdirectory() ? dir_entry_type::dir : dir_entry_type::file;
results.emplace_back(entry_type, dirent.metadata());
return false;
};
iterate_directory_entries(*dir, callback);
return std::make_pair(ERR_OK, std::move(results));
}
//-------------------------------------------------
// impl::file_read
//-------------------------------------------------
std::pair<err_t, std::vector<u8>> impl::file_read(const std::vector<std::string> &path)
{
// find the file
std::optional<directory_entry> dirent = find_entity(path);
if (!dirent || dirent->is_subdirectory())
return std::make_pair(ERR_NOT_FOUND, std::vector<u8>());
// get the list of sectors for this file
std::vector<u32> sectors = get_sectors_from_fat(*dirent);
// prepare the results
std::vector<u8> result;
result.reserve(dirent->file_size());
// and add data from all sectors
for (u32 sector : sectors)
{
fsblk_t::block_t block = m_blockdev.get(sector);
const u8 *data = block.rodata();
size_t length = std::min((size_t)dirent->file_size() - result.size(), (size_t)block.size());
result.insert(result.end(), data, data + length);
}
return std::make_pair(ERR_OK, std::move(result));
}
//-------------------------------------------------
// impl::get_sectors_from_fat
//-------------------------------------------------
std::vector<u32> impl::get_sectors_from_fat(const directory_entry &dirent) const
{
// prepare results
std::vector<u32> results;
results.reserve(dirent.file_size() / bytes_per_sector());
// get critical information
u8 sectors_per_cluster = m_boot_sector_block.r8(13);
u16 root_directory_entry_count = m_boot_sector_block.r16l(17);
u16 root_directory_sector_count = (root_directory_entry_count + 1) / dirents_per_sector();
u32 fat_sector_count = (u32)(m_file_allocation_table.size() / m_bytes_per_sector);
u32 data_starting_sector = m_starting_sector + m_reserved_sector_count + fat_sector_count + root_directory_sector_count;
u32 data_cluster_count = (m_sector_count - data_starting_sector) / sectors_per_cluster;
// find all clusters
u32 start_cluster_mask = ((u64)1 << m_bits_per_fat_entry) - 1;
u32 cluster = dirent.start_cluster() & start_cluster_mask;
while (cluster >= 2 && cluster < (data_cluster_count + 2))
{
// add the sectors for this cluster
for (auto i = 0; i < sectors_per_cluster; i++)
results.push_back(data_starting_sector + (cluster - 2) * sectors_per_cluster + i);
// determine the bit position of this entry
u32 entry_bit_position = cluster * m_bits_per_fat_entry;
// sanity check; check for overflows
u32 new_cluster = 0;
if (entry_bit_position + m_bits_per_fat_entry <= m_file_allocation_table.size() * 8)
{
// this awkward logic is here because we cannot rely on FAT entries all being in one
// sector (thank you FAT12)
u32 current_bit = 0;
while (current_bit < m_bits_per_fat_entry)
{
u32 pos = entry_bit_position + current_bit;
u32 shift = pos % 8;
u32 bit_count = std::min(8 - shift, m_bits_per_fat_entry - current_bit);
u32 bits = (m_file_allocation_table[pos / 8] >> shift) & ((1 << bit_count) - 1);
new_cluster |= (bits << current_bit);
current_bit += bit_count;
}
// normalize special cluster IDs
if (new_cluster > ((u32)1 << m_bits_per_fat_entry) - 0x10)
new_cluster |= ~(((u32)1 << m_bits_per_fat_entry) - 1);
}
cluster = new_cluster;
}
return results;
}
//-------------------------------------------------
// impl::find_entity
//-------------------------------------------------
std::optional<directory_entry> impl::find_entity(const std::vector<std::string> &path) const
{
// special case; reject empty paths
if (path.empty())
return { };
// find the containing directory
directory_span::ptr dir = find_directory(path.begin(), path.end() - 1);
if (!dir)
return { };
// find the last child
return find_child(*dir, path[path.size() - 1]);
}
//-------------------------------------------------
// impl::find_directory
//-------------------------------------------------
directory_span::ptr impl::find_directory(std::vector<std::string>::const_iterator path_begin, std::vector<std::string>::const_iterator path_end) const
{
// the root directory is treated differently
u32 first_sector = m_starting_sector + m_reserved_sector_count + (u32)m_file_allocation_table.size() / m_bytes_per_sector;
u16 directory_entry_count = m_boot_sector_block.r16l(17);
directory_span::ptr current_dir = std::make_unique<root_directory_span>(*this, first_sector, directory_entry_count);
// traverse the directory
for (auto iter = path_begin; iter != path_end; iter++)
{
// find the child file
std::optional<directory_entry> child_directory = find_child(*current_dir, *iter);
if (!child_directory)
return { };
// advance into the child directory
current_dir = std::make_unique<subdirectory_span>(*this, std::move(*child_directory));
}
return current_dir;
}
//-------------------------------------------------
// impl::find_child
//-------------------------------------------------
std::optional<directory_entry> impl::find_child(const directory_span &current_dir, std::string_view target) const
{
std::optional<directory_entry> result;
auto callback = [&result, target](const directory_entry &dirent)
{
bool found = dirent.name() == target;
if (found)
result = dirent;
return found;
};
iterate_directory_entries(current_dir, callback);
return result;
}
//-------------------------------------------------
// impl::iterate_directory_entries
//-------------------------------------------------
void impl::iterate_directory_entries(const directory_span &dir, const std::function<bool(const directory_entry &dirent)> &callback) const
{
std::vector<u32> sectors = dir.get_directory_sectors();
for (u32 sector : sectors)
{
bool done = false;
fsblk_t::block_t block = m_blockdev.get(sector);
for (u32 index = 0; !done && (index < dirents_per_sector()); index++)
{
directory_entry dirent(block, index * 32);
if (dirent.raw_stem()[0] != 0x00)
{
// get the filename
std::string_view stem = strtrimrightspace(dirent.raw_stem());
std::string_view ext = strtrimrightspace(dirent.raw_ext());
if (ext.empty() && (stem == "." || stem == ".."))
continue;
// invoke the callback
done = callback(dirent);
}
}
if (done)
break;
}
}
//-------------------------------------------------
// root_directory_span ctor
//-------------------------------------------------
root_directory_span::root_directory_span(const impl &fs, u32 first_sector, u16 directory_entry_count)
: m_fs(fs)
, m_first_sector(first_sector)
, m_directory_entry_count(directory_entry_count)
{
}
//-------------------------------------------------
// root_directory_span::get_directory_sectors
//-------------------------------------------------
std::vector<u32> root_directory_span::get_directory_sectors() const
{
u32 directory_sector_count = (m_directory_entry_count + m_fs.dirents_per_sector() - 1) / m_fs.dirents_per_sector();
std::vector<u32> result;
result.reserve(directory_sector_count);
for (auto i = 0; i < directory_sector_count; i++)
result.push_back(m_first_sector + i);
return result;
}
//-------------------------------------------------
// subdirectory_span ctor
//-------------------------------------------------
subdirectory_span::subdirectory_span(const impl &fs, directory_entry &&dirent)
: m_fs(fs)
, m_dirent(std::move(dirent))
{
}
//-------------------------------------------------
// subdirectory_span::get_directory_sectors
//-------------------------------------------------
std::vector<u32> subdirectory_span::get_directory_sectors() const
{
return m_fs.get_sectors_from_fat(m_dirent);
}
//**************************************************************************
// PC FAT SPECIFIC
//**************************************************************************
//-------------------------------------------------
// pc_fat_image::name
//-------------------------------------------------
const char *fs::pc_fat_image::name() const
{
return "pc_fat";
}
//-------------------------------------------------
// pc_fat_image::description
//-------------------------------------------------
const char *fs::pc_fat_image::description() const
{
return "PC FAT";
}
//-------------------------------------------------
// pc_fat_image::enumerate_f
//-------------------------------------------------
void pc_fat_image::enumerate_f(floppy_enumerator &fe, u32 form_factor, const std::vector<u32> &variants) const
{
if (has(form_factor, variants, floppy_image::FF_35, floppy_image::DSSD))
fe.add(FLOPPY_PC_FORMAT, 368640, "pc_fat_dssd", "PC FAT 3.5\" dual-sided single density");
if (has(form_factor, variants, floppy_image::FF_35, floppy_image::DSDD))
fe.add(FLOPPY_PC_FORMAT, 737280, "pc_fat_dsdd", "PC FAT 3.5\" dual-sided double density");
if (has(form_factor, variants, floppy_image::FF_35, floppy_image::DSHD))
fe.add(FLOPPY_PC_FORMAT, 1474560, "pc_fat_dshd", "PC FAT 3.5\" dual-sided high density");
if (has(form_factor, variants, floppy_image::FF_35, floppy_image::DSED))
fe.add(FLOPPY_PC_FORMAT, 2949120, "pc_fat_dsed", "PC FAT 3.5\" dual-sided extra density");
}
//-------------------------------------------------
// pc_fat_image::mount
//-------------------------------------------------
std::unique_ptr<filesystem_t> pc_fat_image::mount(fsblk_t &blockdev) const
{
blockdev.set_block_size(512);
return mount_partition(blockdev, 0, blockdev.block_count(), 12);
}

60
src/lib/formats/fs_fat.h Normal file
View File

@ -0,0 +1,60 @@
// license:BSD-3-Clause
// copyright-holders:Nathan Woods
/***************************************************************************
fs_fat.h
PC FAT disk images
***************************************************************************/
#ifndef MAME_FORMATS_FS_FAT_H
#define MAME_FORMATS_FS_FAT_H
#pragma once
#include "fsmgr.h"
#include <optional>
#include <string_view>
namespace fs {
// ======================> fat_image
class fat_image : public manager_t {
public:
fat_image() = default;
virtual bool can_format() const override;
virtual bool can_read() const override;
virtual bool can_write() const override;
virtual bool has_rsrc() const override;
virtual char directory_separator() const override;
virtual std::vector<meta_description> volume_meta_description() const override;
virtual std::vector<meta_description> file_meta_description() const override;
virtual std::vector<meta_description> directory_meta_description() const override;
protected:
static std::unique_ptr<filesystem_t> mount_partition(fsblk_t &blockdev, u32 starting_sector, u32 sector_count, u8 bits_per_fat_entry);
};
// ======================> fat_image
class pc_fat_image : public fat_image {
public:
pc_fat_image() = default;
virtual const char *name() const override;
virtual const char *description() const override;
virtual void enumerate_f(floppy_enumerator &fe, u32 form_factor, const std::vector<u32> &variants) const override;
virtual std::unique_ptr<filesystem_t> mount(fsblk_t &blockdev) const override;
};
extern const pc_fat_image PC_FAT;
} // namespace fs
#endif // MAME_FORMATS_FS_FAT_H

View File

@ -30,6 +30,7 @@ const char *meta_data::entry_name(meta_name name)
case meta_name::ascii_flag: return "ascii_flag";
case meta_name::owner_id: return "owner_id";
case meta_name::attributes: return "attributes";
case meta_name::oem_name: return "oem_name";
}
return "";
}

View File

@ -35,7 +35,8 @@ enum class meta_name {
file_type,
ascii_flag,
owner_id,
attributes
attributes,
oem_name
};
enum class meta_type {