Compare commits

..

46 Commits
gw ... d20

Author SHA1 Message Date
David Given
1b859015ae Update documentation. 2023-08-02 13:42:23 +02:00
David Given
294ac87503 Update documentation for the Roland D20 format. 2023-07-31 23:36:45 +02:00
David Given
c297adb0c7 Try to fix Mac builds. 2023-07-31 22:30:52 +02:00
David Given
446b965794 Handle Roland extents properly if the directory entries are in the wrong order.
Deal with block numbers >39 (they go in the bottom of the disk).
2023-07-31 22:20:08 +02:00
David Given
1927cc7fe1 Fix issue where trying to rename files by clicking on the tree wasn't working. 2023-07-27 23:44:33 +02:00
David Given
4eca254daf Add support for renaming files. 2023-07-27 23:44:04 +02:00
David Given
c7d4fee3f6 Add support for deleting files. 2023-07-27 23:19:50 +02:00
David Given
a6f798ae5b Mangle and demangle filenames. Remember to write the correct extent numbers in
multiextent files.
2023-07-27 23:09:57 +02:00
David Given
c9ae836e52 Add very brittle write support. 2023-07-27 22:49:10 +02:00
David Given
e3ffa63f7f Make sure that the rotational speed is measured even if reads are done through
Browse Disk.
2023-07-27 22:14:48 +02:00
David Given
4ffc2cc1dc Add support for, hopefully, multi-extent files. 2023-07-27 00:30:44 +02:00
David Given
7f9ba14687 Correct erroneous index. 2023-07-26 22:37:56 +02:00
David Given
a24933e272 Merge from master. 2023-07-26 22:33:40 +02:00
David Given
20bdacbecf Add initial support for the Roland-D20 filesystem. 2023-07-26 22:31:20 +02:00
David Given
1f5903a9a0 Don't use std::filesystem; it makes life harder on Windows with its wide
strings.
2023-07-25 23:35:01 +02:00
David Given
bb073b6bb3 Apparently Mingw can't automatically convert from path to string. 2023-07-25 23:23:04 +02:00
David Given
516241f8f5 Replace the image read file picker with a simple one. 2023-07-25 23:11:52 +02:00
David Given
977b6831a0 When reading Kryoflux streams, you can specify any file in the directory and it
will work (as the GUI now forces you to do this).
2023-07-25 22:48:17 +02:00
David Given
c61effb54f Add a file type box to the flux source selection page. 2023-07-25 22:27:09 +02:00
David Given
346d989944 When reading Kryoflux streams, allow the user to specify any file within the
directory and have it work (as that's what the GUI does now).
2023-07-25 22:51:34 +02:00
David Given
60a73c8d1e Add a file type box to the flux source selection page. 2023-07-25 22:27:09 +02:00
dg
e52db4a837 Typo fix. 2023-07-24 20:56:37 +00:00
dg
4e317643bc Try and install compatible versions of protobuf. 2023-07-24 20:53:51 +00:00
David Given
5f520bf375 Merge pull request #698 from davidgiven/zilogmcz
Add support for the ZDOS filesystem for the Zilog MCZ.
2023-07-24 22:16:33 +02:00
David Given
2efe521b3a Update documentation. 2023-07-24 21:48:37 +02:00
David Given
5c21103646 Get the ZDOS filesystem driver working. 2023-07-24 21:46:49 +02:00
David Given
9444696f37 Merge pull request #697 from davidgiven/ro
Allow read-only flux and image in the GUI.
2023-07-24 08:20:39 +02:00
David Given
082fe4e787 Hack in boilerplate for a ZDos filesystem. 2023-07-24 08:18:18 +02:00
David Given
5e13cf23f9 Allow read-only image reader/writers in the GUI. 2023-07-24 07:53:47 +02:00
David Given
8f98a1f557 Consolidate the image constructors in the same way that's been done for the
flux constructors.
2023-07-24 07:50:16 +02:00
David Given
5b21e8798b Allow read-only flux sources in the GUI. 2023-07-24 07:39:59 +02:00
David Given
b9ef5b7db8 Rename all the flux and image types to prefix the enums, due to them being in
the global namespace now.
2023-07-24 02:18:53 +02:00
David Given
9867f8c302 Combine enums for flux source/sink types. config.cc now knows whether they're
read-only, write-only, and read-write.
2023-07-24 00:50:54 +02:00
David Given
315889faf6 Warning fix. 2023-07-23 22:49:23 +02:00
David Given
798e8fee89 Merge pull request #692 from davidgiven/protobuf
Rename the `requires` config field to `prerequisite`
2023-07-08 00:43:15 +02:00
dg
e1c49db329 Use brew --prefix to detect the installation path when copying licenses from
packages.
2023-07-07 22:10:52 +00:00
dg
dae9537472 Warning fixes. 2023-07-07 21:51:24 +00:00
dg
1330d56cdd Fix a bunch of errors caused by changes to libfmt. 2023-07-07 21:32:21 +00:00
David Given
6ce3ce20d0 Remove stray debugging code. 2023-07-07 01:03:31 +02:00
David Given
362c5ee9b0 Rename the requires config field to prerequisite, as requires is about to
become a C++ keyword.
2023-07-07 00:34:03 +02:00
David Given
0f34ce0278 Merge pull request #690 from Deledrius/nsi-fix
Fix incorrect product name in installer.
2023-06-26 14:27:39 +02:00
Joseph Davies
0c27c7c4c8 Fix incorrect product name in installer. 2023-06-25 16:18:03 -07:00
David Given
9db6efe7a2 Merge pull request #686 from davidgiven/docs
Update documentation.
2023-06-03 00:30:34 +02:00
David Given
8b8a22d7fb Add the PCB schematic. 2023-06-03 00:05:51 +02:00
David Given
0a70344bc1 Add Fedora package list. 2023-06-02 23:38:09 +02:00
David Given
e77d01911c Merge pull request #683 from davidgiven/gw
Reset the Greaseweazle data stream when connecting
2023-05-25 22:43:49 +02:00
110 changed files with 2071 additions and 1932 deletions

View File

@@ -43,7 +43,7 @@ jobs:
uses: actions/upload-artifact@v2
with:
name: ${{ github.event.repository.name }}.${{ github.sha }}
path: fluxengine.FluxEngine.pkg
path: fluxengine/FluxEngine.pkg
build-windows:
runs-on: windows-latest
@@ -69,6 +69,9 @@ jobs:
mingw-w64-i686-nsis
zip
vim
- name: update-protobuf
run: |
pacman -U --noconfirm https://repo.msys2.org/mingw/mingw32/mingw-w64-i686-protobuf-21.9-1-any.pkg.tar.zst
- uses: actions/checkout@v2
with:
repository: 'davidgiven/fluxengine'

View File

@@ -36,6 +36,10 @@ jobs:
vim
- uses: actions/checkout@v3
- name: update-protobuf
run: |
pacman -U --noconfirm https://repo.msys2.org/mingw/mingw32/mingw-w64-i686-protobuf-21.9-1-any.pkg.tar.zst
- name: build
run: |
make -j2

View File

@@ -132,13 +132,13 @@ choices because they can store multiple types of file system.
| [`n88basic`](doc/disk-n88basic.md) | N88-BASIC: PC8800/PC98 5.25" 77-track 26-sector DSHD | 🦄 | 🦄 | |
| [`northstar`](doc/disk-northstar.md) | Northstar: 5.25" hard sectored | 🦄 | 🦄 | |
| [`psos`](doc/disk-psos.md) | pSOS: 800kB DSDD with PHILE | 🦄 | 🦄 | PHILE |
| [`rolandd20`](doc/disk-rolandd20.md) | Roland D20: 3.5" electronic synthesiser disks | 🦖 | | |
| [`rolandd20`](doc/disk-rolandd20.md) | Roland D20: 3.5" electronic synthesiser disks | 🦄 | 🦖 | ROLAND |
| [`rx50`](doc/disk-rx50.md) | Digital RX50: 400kB 5.25" 80-track 10-sector SSDD | 🦖 | 🦖 | |
| [`smaky6`](doc/disk-smaky6.md) | Smaky 6: 308kB 5.25" 77-track 16-sector SSDD, hard sectored | 🦖 | | SMAKY6 |
| [`tids990`](doc/disk-tids990.md) | Texas Instruments DS990: 1126kB 8" DSSD | 🦖 | 🦖 | |
| [`tiki`](doc/disk-tiki.md) | Tiki 100: CP/M | | | CPMFS |
| [`victor9k`](doc/disk-victor9k.md) | Victor 9000 / Sirius One: 1224kB 5.25" DSDD GCR | 🦖 | 🦖 | |
| [`zilogmcz`](doc/disk-zilogmcz.md) | Zilog MCZ: 320kB 8" 77-track SSSD hard-sectored | 🦖 | | |
| [`zilogmcz`](doc/disk-zilogmcz.md) | Zilog MCZ: 320kB 8" 77-track SSSD hard-sectored | 🦖 | | ZDOS |
{: .datatable }
<!-- FORMATSEND -->

View File

@@ -51,26 +51,6 @@ static void write_bits(
}
}
void bindump(std::ostream& stream, std::vector<bool>& buffer)
{
size_t pos = 0;
while ((pos < buffer.size()) and (pos < 520))
{
stream << fmt::format("{:5d} : ", pos);
for (int i = 0; i < 40; i++)
{
if ((pos + i) < buffer.size())
stream << fmt::format("{:01b}", (buffer[pos + i]));
else
stream << "-- ";
if ((((pos + i + 1) % 8) == 0) and i != 0)
stream << " ";
}
stream << std::endl;
pos += 40;
}
}
static std::vector<bool> encode_data(uint8_t input)
{
/*

View File

Binary file not shown.

View File

@@ -93,6 +93,22 @@ You're now looking at the _top_ of the board.
row of header sockets allowing you to plug the board directly onto the floppy
disk drive; for simplicity I'm leaving that as an exercise for the reader.)
### If you want to use a PCB
Alternatively, you can make an actual PCB!
<div style="text-align: center">
<a href="pcb.png"><img src="pcb.png" style="width:80%" alt="the PCB schematic"></a>
</div>
This is a passive breakout board designed to take a PSoC5 development board, a
standard 34-way PC connector, and a 50-way 8" drive connector. It was
contributed by a user --- thanks!
<a href="FluxEngine_eagle_pcb.zip">Download this to get it</a>. This package
contains the layout in Eagle format, a printable PDF of the PCB layout, and
gerbers suitable for sending off for manufacture.
### Grounding
You _also_ need to solder a wire between a handy GND pin on the board and
@@ -185,10 +201,14 @@ generic libusb stuff and should build and run on Windows, Linux and OSX as
well, although on Windows it'll need MSYS2 and mingw32. You'll need to
install some support packages.
- For Linux (this is Ubuntu, but this should apply to Debian too):
- For Linux with Ubuntu/Debian:
`libusb-1.0-0-dev`, `libsqlite3-dev`, `zlib1g-dev`,
`libudev-dev`, `protobuf-compiler`, `libwxgtk3.0-gtk3-dev`,
`libfmt-dev`.
- For Linux with Fedora/Red Hat:
`git`, `make`, `gcc`, `gcc-c++`, `xxd`, `protobuf-compiler`,
`protobuf-devel`, `fmt-devel`, `systemd-devel`, `wxGTK3-devel`,
`libsqlite3x-devel`
- For OSX with Homebrew: `libusb`, `pkg-config`, `sqlite`,
`protobuf`, `truncate`, `wxwidgets`, `fmt`.
- For Windows with MSYS2: `make`, `mingw-w64-i686-libusb`,

View File

@@ -9,9 +9,13 @@ drive, used for saving MIDI sequences and samples.
Weirdly, it seems to use precisely the same format as the Brother word
processors: a thoroughly non-IBM-compatible custom GCR system.
FluxEngine pretends to support this, but it has had almost no testing, the only
disk image I have seen for it was mostly corrupt, and very little is known
about the format, so I have no idea whether it's correct or not.
FluxEngine supports both reading and writing D20 disks, as well as basic support
for the filesystem, allowing files to be read from and written to D20 disks.
Note that the D20 was never intended to support arbitrary files on its disks and
is very likely to crash if you put unexpected files on a disk. In addition,
while the file format itself is currently unknown, there is a header at the top
of the file containing what appears to be the name shown in the D20 file
browser, so the name by which you see it is not necessarily the filename.
Please [get in touch](https://github.com/davidgiven/fluxengine/issues/new) if
you know anything about it.
@@ -26,3 +30,7 @@ To read:
- `fluxengine read rolandd20 -s drive:0 -o rolandd20.img`
To write:
- `fluxengine write rolandd20 -d drive:0 -i rolandd20.img`

View File

@@ -20,11 +20,8 @@ bytes per sector --- 128 bytes of user payload plus two two-byte metadata
words used to construct linked lists of sectors for storing files. These
stored 320kB each.
FluxEngine has experimental read support for these disks, based on a single
Catweasel flux file I've been able to obtain, which only contained 70 tracks.
I haven't been able to try this for real. If anyone has any of these disks,
an 8-inch drive, a FluxEngine and the appropriate adapter, please [get in
touch](https://github.com/davidgiven/fluxengine/issues/new)...
FluxEngine has read support for these, including support for RIO's ZDOS file
system.
## Options

View File

@@ -26,6 +26,7 @@ The following file systems are supported so far.
| Macintosh HFS | Y | Y | Only AppleDouble files may be written |
| pSOS' PHILE | Y | | Probably unreliable due to lack of documentation |
| Smaky 6 | Y | | |
| Zilog MCZ RIO's ZDOS | Y | | |
{: .datatable }
Please not that Atari disks do _not_ use standard FatFS, and the library I'm

BIN
doc/pcb.png Normal file
View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

View File

@@ -25,7 +25,7 @@ SetCompressor /solid lzma
GreaseWeazle hardware. It also allows manipulation of flux files and disk \
images, so it's useful without any hardware.$\r$\n\
$\r$\n\
This wizard will install WordGrinder on your computer.$\r$\n\
This wizard will install FluxEngine on your computer.$\r$\n\
$\r$\n\
$_CLICK"
@@ -130,7 +130,7 @@ SectionEnd
Section "Desktop Shortcut"
SetOutPath "$DOCUMENTS"
CreateShortCut "$DESKTOP\WordGrinder.lnk" "$INSTDIR\fluxengine-gui.exe" "" "$INSTDIR\fluxengine-gui.exe" 0
CreateShortCut "$DESKTOP\FluxEngine.lnk" "$INSTDIR\fluxengine-gui.exe" "" "$INSTDIR\fluxengine-gui.exe" 0
SectionEnd
;--------------------------------

View File

@@ -77,14 +77,16 @@ LIBFLUXENGINE_SRCS = \
lib/vfs/cbmfs.cc \
lib/vfs/cpmfs.cc \
lib/vfs/fatfs.cc \
lib/vfs/lif.cc \
lib/vfs/machfs.cc \
lib/vfs/prodos.cc \
lib/vfs/smaky6fs.cc \
lib/vfs/philefs.cc \
lib/vfs/vfs.cc \
lib/vfs/fluxsectorinterface.cc \
lib/vfs/imagesectorinterface.cc \
lib/vfs/lif.cc \
lib/vfs/machfs.cc \
lib/vfs/philefs.cc \
lib/vfs/prodos.cc \
lib/vfs/roland.cc \
lib/vfs/smaky6fs.cc \
lib/vfs/vfs.cc \
lib/vfs/zdos.cc \
LIBFLUXENGINE_OBJS = $(patsubst %.cc, $(OBJDIR)/%.o, $(LIBFLUXENGINE_SRCS))
OBJS += $(LIBFLUXENGINE_OBJS)

View File

@@ -362,6 +362,14 @@ ByteWriter& ByteWriter::operator+=(std::istream& stream)
return *this;
}
ByteWriter& ByteWriter::pad(unsigned count, uint8_t b)
{
while (count--)
this->write_8(b);
return *this;
}
void BitWriter::push(uint32_t bits, size_t size)
{
bits <<= 32 - size;

View File

@@ -345,6 +345,8 @@ public:
return *this += stream;
}
ByteWriter& pad(unsigned count, uint8_t byte = 0);
private:
Bytes& _bytes;
};

View File

@@ -19,4 +19,36 @@ enum IndexMode {
INDEXMODE_360 = 2;
}
enum FluxSourceSinkType {
FLUXTYPE_NOT_SET = 0;
FLUXTYPE_A2R = 1;
FLUXTYPE_AU = 2;
FLUXTYPE_CWF = 3;
FLUXTYPE_DRIVE = 4;
FLUXTYPE_ERASE = 5;
FLUXTYPE_FLUX = 6;
FLUXTYPE_FLX = 7;
FLUXTYPE_KRYOFLUX = 8;
FLUXTYPE_SCP = 9;
FLUXTYPE_TEST_PATTERN = 10;
FLUXTYPE_VCD = 11;
}
enum ImageReaderWriterType {
IMAGETYPE_NOT_SET = 0;
IMAGETYPE_D64 = 1;
IMAGETYPE_D88 = 2;
IMAGETYPE_DIM = 3;
IMAGETYPE_DISKCOPY = 4;
IMAGETYPE_FDI = 5;
IMAGETYPE_IMD = 6;
IMAGETYPE_IMG = 7;
IMAGETYPE_JV3 = 8;
IMAGETYPE_LDBS = 9;
IMAGETYPE_NFD = 10;
IMAGETYPE_NSI = 11;
IMAGETYPE_RAW = 12;
IMAGETYPE_TD0 = 13;
}

View File

@@ -11,10 +11,153 @@
#include "lib/decoders/decoders.h"
#include <fstream>
#include <google/protobuf/text_format.h>
#include <regex>
static Config config;
enum ConstructorMode
{
MODE_RO,
MODE_WO,
MODE_RW
};
struct ImageConstructor
{
std::string extension;
ImageReaderWriterType type;
ConstructorMode mode;
};
static const std::vector<FluxConstructor> fluxConstructors = {
{/* The .flux format must be first. */
.name = "FluxEngine (.flux)",
.pattern = std::regex("^(.*\\.flux)$"),
.source =
[](auto& s, auto* proto)
{
proto->set_type(FLUXTYPE_FLUX);
proto->mutable_fl2()->set_filename(s);
}, .sink =
[](auto& s, auto* proto)
{
proto->set_type(FLUXTYPE_FLUX);
proto->mutable_fl2()->set_filename(s);
}},
{
.name = "Supercard Pro (.scp)",
.pattern = std::regex("^(.*\\.scp)$"),
.source =
[](auto& s, auto* proto)
{
proto->set_type(FLUXTYPE_SCP);
proto->mutable_scp()->set_filename(s);
}, .sink =
[](auto& s, auto* proto)
{
proto->set_type(FLUXTYPE_SCP);
proto->mutable_scp()->set_filename(s);
}, },
{.name = "AppleSauce (.a2r)",
.pattern = std::regex("^(.*\\.a2r)$"),
.source =
[](auto& s, auto* proto)
{
proto->set_type(FLUXTYPE_A2R);
proto->mutable_a2r()->set_filename(s);
}, .sink =
[](auto& s, auto* proto)
{
proto->set_type(FLUXTYPE_A2R);
proto->mutable_a2r()->set_filename(s);
}},
{.name = "CatWeazle (.cwf)",
.pattern = std::regex("^(.*\\.cwf)$"),
.source =
[](auto& s, auto* proto)
{
proto->set_type(FLUXTYPE_CWF);
proto->mutable_cwf()->set_filename(s);
}},
{.pattern = std::regex("^erase:$"),
.source =
[](auto& s, auto* proto)
{
proto->set_type(FLUXTYPE_ERASE);
}},
{.name = "KryoFlux directory",
.pattern = std::regex("^kryoflux:(.*)$"),
.source =
[](auto& s, auto* proto)
{
proto->set_type(FLUXTYPE_KRYOFLUX);
proto->mutable_kryoflux()->set_directory(s);
}},
{.pattern = std::regex("^testpattern:(.*)"),
.source =
[](auto& s, auto* proto)
{
proto->set_type(FLUXTYPE_TEST_PATTERN);
}},
{.pattern = std::regex("^drive:(.*)"),
.source =
[](auto& s, auto* proto)
{
proto->set_type(FLUXTYPE_DRIVE);
globalConfig().overrides()->mutable_drive()->set_drive(
std::stoi(s));
}, .sink =
[](auto& s, auto* proto)
{
proto->set_type(FLUXTYPE_DRIVE);
globalConfig().overrides()->mutable_drive()->set_drive(
std::stoi(s));
}},
{.name = "FluxCopy directory",
.pattern = std::regex("^flx:(.*)$"),
.source =
[](auto& s, auto* proto)
{
proto->set_type(FLUXTYPE_FLX);
proto->mutable_flx()->set_directory(s);
}},
{.name = "Value Change Dump directory",
.pattern = std::regex("^vcd:(.*)$"),
.sink =
[](auto& s, auto* proto)
{
proto->set_type(FLUXTYPE_VCD);
proto->mutable_vcd()->set_directory(s);
}},
{.name = "Audio file directory",
.pattern = std::regex("^au:(.*)$"),
.sink =
[](auto& s, auto* proto)
{
proto->set_type(FLUXTYPE_AU);
proto->mutable_au()->set_directory(s);
}},
};
static const std::vector<ImageConstructor> imageConstructors = {
{".adf", IMAGETYPE_IMG, MODE_RW},
{".d64", IMAGETYPE_D64, MODE_RW},
{".d81", IMAGETYPE_IMG, MODE_RW},
{".d88", IMAGETYPE_D88, MODE_RW},
{".dim", IMAGETYPE_DIM, MODE_RO},
{".diskcopy", IMAGETYPE_DISKCOPY, MODE_RW},
{".dsk", IMAGETYPE_IMG, MODE_RW},
{".fdi", IMAGETYPE_FDI, MODE_RO},
{".imd", IMAGETYPE_IMD, MODE_RW},
{".img", IMAGETYPE_IMG, MODE_RW},
{".jv3", IMAGETYPE_JV3, MODE_RO},
{".nfd", IMAGETYPE_NFD, MODE_RO},
{".nsi", IMAGETYPE_NSI, MODE_RW},
{".st", IMAGETYPE_IMG, MODE_RW},
{".td0", IMAGETYPE_TD0, MODE_RO},
{".vgi", IMAGETYPE_IMG, MODE_RW},
{".xdf", IMAGETYPE_IMG, MODE_RW},
};
Config& globalConfig()
{
return config;
@@ -29,7 +172,7 @@ ConfigProto* Config::combined()
/* First apply any standalone options. */
std::set<std::string> options = _appliedOptions;
std::set<const OptionRequirementProto*> requirements;
std::set<const OptionPrerequisiteProto*> prereqs;
for (const auto& option : _baseConfig.option())
{
if (options.find(option.name()) != options.end())
@@ -257,7 +400,7 @@ const OptionProto& Config::findOption(const std::string& optionName)
void Config::checkOptionValid(const OptionProto& option)
{
for (const auto& req : option.requires())
for (const auto& req : option.prerequisite())
{
bool matched = false;
try
@@ -334,72 +477,17 @@ void Config::clearOptions()
invalidate();
}
static void setFluxSourceImpl(std::string filename, FluxSourceProto* proto)
static void setFluxSourceImpl(
const std::string& filename, FluxSourceProto* proto)
{
static const std::vector<std::pair<std::regex,
std::function<void(const std::string&, FluxSourceProto*)>>>
formats = {
{std::regex("^(.*\\.flux)$"),
[](auto& s, auto* proto)
{
proto->set_type(FluxSourceProto::FLUX);
proto->mutable_fl2()->set_filename(s);
}},
{std::regex("^(.*\\.scp)$"),
[](auto& s, auto* proto)
{
proto->set_type(FluxSourceProto::SCP);
proto->mutable_scp()->set_filename(s);
}},
{std::regex("^(.*\\.a2r)$"),
[](auto& s, auto* proto)
{
proto->set_type(FluxSourceProto::A2R);
proto->mutable_a2r()->set_filename(s);
}},
{std::regex("^(.*\\.cwf)$"),
[](auto& s, auto* proto)
{
proto->set_type(FluxSourceProto::CWF);
proto->mutable_cwf()->set_filename(s);
}},
{std::regex("^erase:$"),
[](auto& s, auto* proto)
{
proto->set_type(FluxSourceProto::ERASE);
}},
{std::regex("^kryoflux:(.*)$"),
[](auto& s, auto* proto)
{
proto->set_type(FluxSourceProto::KRYOFLUX);
proto->mutable_kryoflux()->set_directory(s);
}},
{std::regex("^testpattern:(.*)"),
[](auto& s, auto* proto)
{
proto->set_type(FluxSourceProto::TEST_PATTERN);
}},
{std::regex("^drive:(.*)"),
[](auto& s, auto* proto)
{
proto->set_type(FluxSourceProto::DRIVE);
globalConfig().overrides()->mutable_drive()->set_drive(
std::stoi(s));
}},
{std::regex("^flx:(.*)$"),
[](auto& s, auto* proto)
{
proto->set_type(FluxSourceProto::FLX);
proto->mutable_flx()->set_directory(s);
}},
};
for (const auto& it : formats)
for (const auto& it : fluxConstructors)
{
std::smatch match;
if (std::regex_match(filename, match, it.first))
if (std::regex_match(filename, match, it.pattern))
{
it.second(match[1], proto);
if (!it.source)
throw new InapplicableValueException();
it.source(match[1], proto);
return;
}
}
@@ -412,56 +500,16 @@ void Config::setFluxSource(std::string filename)
setFluxSourceImpl(filename, overrides()->mutable_flux_source());
}
static void setFluxSinkImpl(std::string filename, FluxSinkProto* proto)
static void setFluxSinkImpl(const std::string& filename, FluxSinkProto* proto)
{
static const std::vector<std::pair<std::regex,
std::function<void(const std::string&, FluxSinkProto*)>>>
formats = {
{std::regex("^(.*\\.a2r)$"),
[](auto& s, auto* proto)
{
proto->set_type(FluxSinkProto::A2R);
proto->mutable_a2r()->set_filename(s);
}},
{std::regex("^(.*\\.flux)$"),
[](auto& s, auto* proto)
{
proto->set_type(FluxSinkProto::FLUX);
proto->mutable_fl2()->set_filename(s);
}},
{std::regex("^(.*\\.scp)$"),
[](auto& s, auto* proto)
{
proto->set_type(FluxSinkProto::SCP);
proto->mutable_scp()->set_filename(s);
}},
{std::regex("^vcd:(.*)$"),
[](auto& s, auto* proto)
{
proto->set_type(FluxSinkProto::VCD);
proto->mutable_vcd()->set_directory(s);
}},
{std::regex("^au:(.*)$"),
[](auto& s, auto* proto)
{
proto->set_type(FluxSinkProto::AU);
proto->mutable_au()->set_directory(s);
}},
{std::regex("^drive:(.*)"),
[](auto& s, auto* proto)
{
proto->set_type(FluxSinkProto::DRIVE);
globalConfig().overrides()->mutable_drive()->set_drive(
std::stoi(s));
}},
};
for (const auto& it : formats)
for (const auto& it : fluxConstructors)
{
std::smatch match;
if (std::regex_match(filename, match, it.first))
if (std::regex_match(filename, match, it.pattern))
{
it.second(match[1], proto);
if (!it.sink)
throw new InapplicableValueException();
it.sink(match[1], proto);
return;
}
}
@@ -487,34 +535,14 @@ void Config::setVerificationFluxSource(std::string filename)
void Config::setImageReader(std::string filename)
{
static const std::map<std::string, std::function<void(ImageReaderProto*)>>
formats = {
// clang-format off
{".adf", [](auto* proto) { proto->set_type(ImageReaderProto::IMG); }},
{".d64", [](auto* proto) { proto->set_type(ImageReaderProto::D64); }},
{".d81", [](auto* proto) { proto->set_type(ImageReaderProto::IMG); }},
{".d88", [](auto* proto) { proto->set_type(ImageReaderProto::D88); }},
{".dim", [](auto* proto) { proto->set_type(ImageReaderProto::DIM); }},
{".diskcopy", [](auto* proto) { proto->set_type(ImageReaderProto::DISKCOPY); }},
{".dsk", [](auto* proto) { proto->set_type(ImageReaderProto::IMG); }},
{".fdi", [](auto* proto) { proto->set_type(ImageReaderProto::FDI); }},
{".imd", [](auto* proto) { proto->set_type(ImageReaderProto::IMD); }},
{".img", [](auto* proto) { proto->set_type(ImageReaderProto::IMG); }},
{".jv3", [](auto* proto) { proto->set_type(ImageReaderProto::JV3); }},
{".nfd", [](auto* proto) { proto->set_type(ImageReaderProto::NFD); }},
{".nsi", [](auto* proto) { proto->set_type(ImageReaderProto::NSI); }},
{".st", [](auto* proto) { proto->set_type(ImageReaderProto::IMG); }},
{".td0", [](auto* proto) { proto->set_type(ImageReaderProto::TD0); }},
{".vgi", [](auto* proto) { proto->set_type(ImageReaderProto::IMG); }},
{".xdf", [](auto* proto) { proto->set_type(ImageReaderProto::IMG); }},
// clang-format on
};
for (const auto& it : formats)
for (const auto& it : imageConstructors)
{
if (endsWith(filename, it.first))
if (endsWith(filename, it.extension))
{
it.second(overrides()->mutable_image_reader());
if (it.mode == MODE_WO)
throw new InapplicableValueException();
overrides()->mutable_image_reader()->set_type(it.type);
overrides()->mutable_image_reader()->set_filename(filename);
return;
}
@@ -525,31 +553,14 @@ void Config::setImageReader(std::string filename)
void Config::setImageWriter(std::string filename)
{
static const std::map<std::string, std::function<void(ImageWriterProto*)>>
formats = {
// clang-format off
{".adf", [](auto* proto) { proto->set_type(ImageWriterProto::IMG); }},
{".d64", [](auto* proto) { proto->set_type(ImageWriterProto::D64); }},
{".d81", [](auto* proto) { proto->set_type(ImageWriterProto::IMG); }},
{".d88", [](auto* proto) { proto->set_type(ImageWriterProto::D88); }},
{".diskcopy", [](auto* proto) { proto->set_type(ImageWriterProto::DISKCOPY); }},
{".dsk", [](auto* proto) { proto->set_type(ImageWriterProto::IMG); }},
{".img", [](auto* proto) { proto->set_type(ImageWriterProto::IMG); }},
{".imd", [](auto* proto) { proto->set_type(ImageWriterProto::IMD); }},
{".ldbs", [](auto* proto) { proto->set_type(ImageWriterProto::LDBS); }},
{".nsi", [](auto* proto) { proto->set_type(ImageWriterProto::NSI); }},
{".raw", [](auto* proto) { proto->set_type(ImageWriterProto::RAW); }},
{".st", [](auto* proto) { proto->set_type(ImageWriterProto::IMG); }},
{".vgi", [](auto* proto) { proto->set_type(ImageWriterProto::IMG); }},
{".xdf", [](auto* proto) { proto->set_type(ImageWriterProto::IMG); }},
// clang-format on
};
for (const auto& it : formats)
for (const auto& it : imageConstructors)
{
if (endsWith(filename, it.first))
if (endsWith(filename, it.extension))
{
it.second(overrides()->mutable_image_writer());
if (it.mode == MODE_RO)
throw new InapplicableValueException();
overrides()->mutable_image_writer()->set_type(it.type);
overrides()->mutable_image_writer()->set_filename(filename);
return;
}
@@ -560,7 +571,7 @@ void Config::setImageWriter(std::string filename)
bool Config::hasFluxSource()
{
return (*this)->flux_source().type() != FluxSourceProto::NOT_SET;
return (*this)->flux_source().type() != FLUXTYPE_NOT_SET;
}
std::shared_ptr<FluxSource>& Config::getFluxSource()
@@ -578,7 +589,7 @@ std::shared_ptr<FluxSource>& Config::getFluxSource()
bool Config::hasVerificationFluxSource() const
{
return _verificationFluxSourceProto.type() != FluxSourceProto::NOT_SET;
return _verificationFluxSourceProto.type() != FLUXTYPE_NOT_SET;
}
std::shared_ptr<FluxSource>& Config::getVerificationFluxSource()
@@ -596,7 +607,7 @@ std::shared_ptr<FluxSource>& Config::getVerificationFluxSource()
bool Config::hasImageReader()
{
return (*this)->image_reader().type() != ImageReaderProto::NOT_SET;
return (*this)->image_reader().type() != IMAGETYPE_NOT_SET;
}
std::shared_ptr<ImageReader>& Config::getImageReader()
@@ -614,7 +625,7 @@ std::shared_ptr<ImageReader>& Config::getImageReader()
bool Config::hasFluxSink()
{
return (*this)->flux_sink().type() != FluxSinkProto::NOT_SET;
return (*this)->flux_sink().type() != FLUXTYPE_NOT_SET;
}
std::unique_ptr<FluxSink> Config::getFluxSink()
@@ -627,7 +638,7 @@ std::unique_ptr<FluxSink> Config::getFluxSink()
bool Config::hasImageWriter()
{
return (*this)->image_writer().type() != ImageWriterProto::NOT_SET;
return (*this)->image_writer().type() != IMAGETYPE_NOT_SET;
}
std::unique_ptr<ImageWriter> Config::getImageWriter()
@@ -671,3 +682,8 @@ std::shared_ptr<Decoder>& Config::getDecoder()
}
return _decoder;
}
const std::vector<FluxConstructor>& Config::getFluxFormats()
{
return fluxConstructors;
}

View File

@@ -4,6 +4,7 @@
#include <google/protobuf/message.h>
#include "lib/config.pb.h"
#include "lib/common.pb.h"
class ConfigProto;
class OptionProto;
@@ -46,6 +47,22 @@ public:
}
};
class InapplicableValueException : public ErrorException
{
public:
InapplicableValueException():
ErrorException("selected format cannot be used here")
{}
};
struct FluxConstructor
{
std::string name;
std::regex pattern;
std::function<void(const std::string& filename, FluxSourceProto*)> source;
std::function<void(const std::string& filename, FluxSinkProto*)> sink;
};
class Config
{
public:
@@ -146,6 +163,10 @@ public:
bool hasImageWriter();
std::unique_ptr<ImageWriter> getImageWriter();
public:
static const std::vector<FluxConstructor>& getFluxFormats();
static std::vector<std::string> getImageFormats();
private:
ConfigProto _baseConfig;
ConfigProto _overridesConfig;

View File

@@ -50,7 +50,7 @@ message ConfigProto
repeated OptionGroupProto option_group = 20;
}
message OptionRequirementProto
message OptionPrerequisiteProto
{
optional string key = 1 [ (help) = "path to config value" ];
repeated string value = 2 [ (help) = "list of required values" ];
@@ -65,7 +65,7 @@ message OptionProto
[ (help) = "message to display when option is in use" ];
optional bool set_by_default = 6
[ (help) = "this option is applied by default", default = false ];
repeated OptionRequirementProto requires = 7
repeated OptionPrerequisiteProto prerequisite = 7
[ (help) = "prerequisites for this option" ];
optional ConfigProto config = 4

View File

@@ -30,8 +30,8 @@ static void upgradeFluxFile(FluxFileProto& proto)
error(
"this is a version {} flux file, but this build of the client can "
"only handle up to version {} --- please upgrade",
proto.version(),
FluxFileVersion::VERSION_2);
(int)proto.version(),
(int)FluxFileVersion::VERSION_2);
}
FluxFileProto loadFl2File(const std::string filename)

View File

@@ -10,26 +10,25 @@ std::unique_ptr<FluxSink> FluxSink::create(const FluxSinkProto& config)
{
switch (config.type())
{
case FluxSinkProto::DRIVE:
case FLUXTYPE_DRIVE:
return createHardwareFluxSink(config.drive());
case FluxSinkProto::A2R:
case FLUXTYPE_A2R:
return createA2RFluxSink(config.a2r());
case FluxSinkProto::AU:
case FLUXTYPE_AU:
return createAuFluxSink(config.au());
case FluxSinkProto::VCD:
case FLUXTYPE_VCD:
return createVcdFluxSink(config.vcd());
case FluxSinkProto::SCP:
case FLUXTYPE_SCP:
return createScpFluxSink(config.scp());
case FluxSinkProto::FLUX:
case FLUXTYPE_FLUX:
return createFl2FluxSink(config.fl2());
default:
error("bad output disk config");
return std::unique_ptr<FluxSink>();
}
}

View File

@@ -29,17 +29,8 @@ message Fl2FluxSinkProto {
// Next: 10
message FluxSinkProto {
enum FluxSinkType {
NOT_SET = 0;
DRIVE = 1;
A2R = 2;
AU = 3;
VCD = 4;
SCP = 5;
FLUX = 6;
}
optional FluxSinkType type = 9 [default = NOT_SET, (help) = "flux sink type"];
optional FluxSourceSinkType type = 9
[default = FLUXTYPE_NOT_SET, (help) = "flux sink type"];
optional HardwareFluxSinkProto drive = 2;
optional A2RFluxSinkProto a2r = 8;

View File

@@ -180,7 +180,7 @@ public:
trackdataWriter += fluxdata;
}
operator std::string() const
operator std::string() const override
{
return fmt::format("scp({})", _config.filename());
}

View File

@@ -64,7 +64,7 @@ public:
of << "\n";
}
operator std::string() const
operator std::string() const override
{
return fmt::format("vcd({})", _config.directory());
}

View File

@@ -142,7 +142,7 @@ public:
}
}
void recalibrate() {}
void recalibrate() override {}
private:
Bytes findChunk(Bytes id)

View File

@@ -15,7 +15,7 @@ public:
return std::unique_ptr<const Fluxmap>();
}
void recalibrate() {}
void recalibrate() override {}
};
std::unique_ptr<FluxSource> FluxSource::createEraseFluxSource(

View File

@@ -39,8 +39,8 @@ public:
_extraConfig.mutable_drive()->set_rotational_period_ms(
_proto.rotational_period_ms());
if (_proto.has_tpi())
_extraConfig.mutable_drive()->set_tpi(_proto.tpi());
if (_proto.has_tpi())
_extraConfig.mutable_drive()->set_tpi(_proto.tpi());
}
public:
@@ -55,7 +55,7 @@ public:
return std::make_unique<EmptyFluxSourceIterator>();
}
void recalibrate() {}
void recalibrate() override {}
private:
void check_for_error(std::ifstream& ifs)

View File

@@ -10,35 +10,34 @@ std::unique_ptr<FluxSource> FluxSource::create(const FluxSourceProto& config)
{
switch (config.type())
{
case FluxSourceProto::DRIVE:
case FLUXTYPE_DRIVE:
return createHardwareFluxSource(config.drive());
case FluxSourceProto::ERASE:
case FLUXTYPE_ERASE:
return createEraseFluxSource(config.erase());
case FluxSourceProto::KRYOFLUX:
case FLUXTYPE_KRYOFLUX:
return createKryofluxFluxSource(config.kryoflux());
case FluxSourceProto::TEST_PATTERN:
case FLUXTYPE_TEST_PATTERN:
return createTestPatternFluxSource(config.test_pattern());
case FluxSourceProto::SCP:
case FLUXTYPE_SCP:
return createScpFluxSource(config.scp());
case FluxSourceProto::A2R:
case FLUXTYPE_A2R:
return createA2rFluxSource(config.a2r());
case FluxSourceProto::CWF:
case FLUXTYPE_CWF:
return createCwfFluxSource(config.cwf());
case FluxSourceProto::FLUX:
case FLUXTYPE_FLUX:
return createFl2FluxSource(config.fl2());
case FluxSourceProto::FLX:
case FLUXTYPE_FLX:
return createFlxFluxSource(config.flx());
default:
error("bad input disk configuration");
return std::unique_ptr<FluxSource>();
}
}

View File

@@ -41,20 +41,8 @@ message FlxFluxSourceProto {
// NEXT: 12
message FluxSourceProto {
enum FluxSourceType {
NOT_SET = 0;
DRIVE = 1;
TEST_PATTERN = 2;
ERASE = 3;
KRYOFLUX = 4;
SCP = 5;
CWF = 6;
FLUX = 7;
FLX = 8;
A2R = 9;
}
optional FluxSourceType type = 9 [default = NOT_SET, (help) = "flux source type"];
optional FluxSourceSinkType type = 9
[default = FLUXTYPE_NOT_SET, (help) = "flux source type"];
optional A2rFluxSourceProto a2r = 11;
optional CwfFluxSourceProto cwf = 7;

View File

@@ -19,7 +19,7 @@ public:
return readFlxBytes(Bytes::readFromFile(path));
}
void recalibrate() {}
void recalibrate() override {}
private:
const std::string _path;

View File

@@ -5,6 +5,7 @@
#include <fstream>
#include <sys/types.h>
#include <dirent.h>
#include <filesystem>
#define MCLK_HZ (((18432000.0 * 73.0) / 14.0) / 2.0)
#define SCLK_HZ (MCLK_HZ / 2)
@@ -23,10 +24,18 @@ static bool has_suffix(const std::string& haystack, const std::string& needle)
}
std::unique_ptr<Fluxmap> readStream(
const std::string& dir, unsigned track, unsigned side)
std::string dir, unsigned track, unsigned side)
{
std::string suffix = fmt::format("{:02}.{}.raw", track, side);
FILE* fp = fopen(dir.c_str(), "r");
if (fp)
{
fclose(fp);
int i = dir.find_last_of("/\\");
dir = dir.substr(0, i);
}
DIR* dirp = opendir(dir.c_str());
if (!dirp)
error("cannot access path '{}'", dir);

View File

@@ -2,7 +2,7 @@
#define STREAM_H
extern std::unique_ptr<Fluxmap> readStream(
const std::string& dir, unsigned track, unsigned side);
std::string dir, unsigned track, unsigned side);
extern std::unique_ptr<Fluxmap> readStream(const std::string& path);
extern std::unique_ptr<Fluxmap> readStream(const Bytes& bytes);

View File

@@ -128,7 +128,7 @@ public:
return fluxmap;
}
void recalibrate() {}
void recalibrate() override {}
private:
void check_for_error()

View File

@@ -15,6 +15,7 @@
#include <climits>
#include <variant>
#include <optional>
#include <regex>
#include <fmt/format.h>
#if defined(_WIN32) || defined(__WIN32__)

View File

@@ -15,37 +15,37 @@ std::unique_ptr<ImageReader> ImageReader::create(const ImageReaderProto& config)
{
switch (config.type())
{
case ImageReaderProto::DIM:
case IMAGETYPE_DIM:
return ImageReader::createDimImageReader(config);
case ImageReaderProto::D88:
case IMAGETYPE_D88:
return ImageReader::createD88ImageReader(config);
case ImageReaderProto::FDI:
case IMAGETYPE_FDI:
return ImageReader::createFdiImageReader(config);
case ImageReaderProto::IMD:
case IMAGETYPE_IMD:
return ImageReader::createIMDImageReader(config);
case ImageReaderProto::IMG:
case IMAGETYPE_IMG:
return ImageReader::createImgImageReader(config);
case ImageReaderProto::DISKCOPY:
case IMAGETYPE_DISKCOPY:
return ImageReader::createDiskCopyImageReader(config);
case ImageReaderProto::JV3:
case IMAGETYPE_JV3:
return ImageReader::createJv3ImageReader(config);
case ImageReaderProto::D64:
case IMAGETYPE_D64:
return ImageReader::createD64ImageReader(config);
case ImageReaderProto::NFD:
case IMAGETYPE_NFD:
return ImageReader::createNFDImageReader(config);
case ImageReaderProto::NSI:
case IMAGETYPE_NSI:
return ImageReader::createNsiImageReader(config);
case ImageReaderProto::TD0:
case IMAGETYPE_TD0:
return ImageReader::createTd0ImageReader(config);
default:

View File

@@ -24,22 +24,8 @@ message ImageReaderProto
default = false
];
enum ImageReaderType {
NOT_SET = 0;
IMG = 1;
DISKCOPY = 2;
IMD = 3;
JV3 = 4;
D64 = 5;
NSI = 6;
TD0 = 7;
DIM = 8;
FDI = 9;
D88 = 10;
NFD = 11;
}
optional ImageReaderType type = 14 [default = NOT_SET, (help) = "input image type"];
optional ImageReaderWriterType type = 14
[default = IMAGETYPE_NOT_SET, (help) = "input image type"];
optional ImgInputOutputProto img = 2;
optional DiskCopyInputProto diskcopy = 3;

View File

@@ -15,28 +15,28 @@ std::unique_ptr<ImageWriter> ImageWriter::create(const ImageWriterProto& config)
{
switch (config.type())
{
case ImageWriterProto::IMG:
case IMAGETYPE_IMG:
return ImageWriter::createImgImageWriter(config);
case ImageWriterProto::D64:
case IMAGETYPE_D64:
return ImageWriter::createD64ImageWriter(config);
case ImageWriterProto::LDBS:
case IMAGETYPE_LDBS:
return ImageWriter::createLDBSImageWriter(config);
case ImageWriterProto::DISKCOPY:
case IMAGETYPE_DISKCOPY:
return ImageWriter::createDiskCopyImageWriter(config);
case ImageWriterProto::NSI:
case IMAGETYPE_NSI:
return ImageWriter::createNsiImageWriter(config);
case ImageWriterProto::RAW:
case IMAGETYPE_RAW:
return ImageWriter::createRawImageWriter(config);
case ImageWriterProto::D88:
case IMAGETYPE_D88:
return ImageWriter::createD88ImageWriter(config);
case ImageWriterProto::IMD:
case IMAGETYPE_IMD:
return ImageWriter::createImdImageWriter(config);
default:

View File

@@ -66,25 +66,14 @@ message ImdOutputProto
// NEXT_TAG: 12
message ImageWriterProto
{
enum ImageWriterType {
NOT_SET = 0;
IMG = 1;
D64 = 2;
LDBS = 3;
DISKCOPY = 4;
NSI = 5;
RAW = 6;
D88 = 7;
IMD = 8;
}
optional string filename = 1 [ (help) = "filename of output sector image" ];
optional bool filesystem_sector_order = 10 [
(help) = "read/write sector image in filesystem order",
default = false
];
optional ImageWriterType type = 11 [ default = NOT_SET, (help) = "image writer type" ];
optional ImageReaderWriterType type = 11
[ default = IMAGETYPE_NOT_SET, (help) = "image writer type" ];
optional ImgInputOutputProto img = 2;
optional D64OutputProto d64 = 3;

View File

@@ -273,6 +273,8 @@ public:
case ImdOutputProto::RATE_DD:
RATE = 2000;
break;
case ImdOutputProto::RATE_GUESS:
break;
}
}
header.ModeValue =

View File

@@ -119,6 +119,7 @@ static ProtoField resolveProtoPath(
switch (field->label())
{
case google::protobuf::FieldDescriptor::LABEL_OPTIONAL:
case google::protobuf::FieldDescriptor::LABEL_REQUIRED:
if (!create && !reflection->HasField(*message, field))
throw ProtoPathNotFoundException(fmt::format(
"could not find config field '{}'", field->name()));
@@ -139,7 +140,7 @@ static ProtoField resolveProtoPath(
break;
default:
error("bad proto label {}", field->label());
error("bad proto label for field '{}' in '{}'", item, path);
}
descriptor = message->GetDescriptor();

View File

@@ -2,6 +2,7 @@
#define PROTO_H
#include <google/protobuf/message.h>
#include "lib/common.pb.h"
#include "lib/config.pb.h"
class ProtoPathNotFoundException : public ErrorException

View File

@@ -456,6 +456,9 @@ std::shared_ptr<TrackFlux> readAndDecodeTrack(FluxSource& fluxSource,
auto trackFlux = std::make_shared<TrackFlux>();
trackFlux->trackInfo = trackInfo;
if (fluxSource.isHardware())
measureDiskRotation();
FluxSourceIteratorHolder fluxSourceIteratorHolder(fluxSource);
int retriesRemaining = globalConfig()->decoder().retries();
for (;;)
@@ -498,8 +501,6 @@ std::shared_ptr<const DiskFlux> readDiskCommand(
auto diskflux = std::make_shared<DiskFlux>();
log(BeginOperationLogMessage{"Reading and decoding disk"});
if (fluxSource.isHardware())
measureDiskRotation();
auto locations = Layout::computeLocations();
unsigned index = 0;
for (auto& trackInfo : locations)

View File

@@ -90,6 +90,16 @@ struct Sector : public LogicalLocation
}
};
template <>
struct fmt::formatter<Sector::Status> : formatter<string_view>
{
auto format(Sector::Status status, format_context& ctx) const
{
return formatter<string_view>::format(
Sector::statusToString(status), ctx);
}
};
extern bool sectorPointerSortPredicate(const std::shared_ptr<const Sector>& lhs,
const std::shared_ptr<const Sector>& rhs);
extern bool sectorPointerEqualsPredicate(

View File

@@ -76,7 +76,7 @@ public:
"your FluxEngine firmware is at version {} but the client is "
"for version {}; please upgrade",
version,
FLUXENGINE_PROTOCOL_VERSION);
(int) FLUXENGINE_PROTOCOL_VERSION);
}
private:

View File

@@ -56,7 +56,7 @@ public:
{
}
uint32_t capabilities() const
uint32_t capabilities() const override
{
return OP_GETFSDATA | OP_CREATE | OP_LIST | OP_GETFILE | OP_PUTFILE |
OP_GETDIRENT | OP_DELETE | OP_MOVE | OP_CREATEDIR;
@@ -229,7 +229,7 @@ public:
throw CannotWriteException();
}
void createDirectory(const Path& path)
void createDirectory(const Path& path) override
{
AdfMount m(this);
if (path.empty())

View File

@@ -50,7 +50,7 @@ public:
{
}
uint32_t capabilities() const
uint32_t capabilities() const override
{
return OP_LIST | OP_GETDIRENT | OP_GETFSDATA | OP_GETFILE;
}

View File

@@ -232,7 +232,7 @@ public:
{
}
uint32_t capabilities() const
uint32_t capabilities() const override
{
return OP_GETFSDATA | OP_LIST | OP_GETFILE | OP_PUTFILE | OP_GETDIRENT |
OP_DELETE;

View File

@@ -195,7 +195,7 @@ public:
{
}
uint32_t capabilities() const
uint32_t capabilities() const override
{
return OP_GETFSDATA | OP_LIST | OP_GETFILE | OP_GETDIRENT;
}

View File

@@ -81,7 +81,7 @@ public:
{
}
uint32_t capabilities() const
uint32_t capabilities() const override
{
return OP_GETFSDATA | OP_LIST | OP_GETFILE | OP_GETDIRENT;
}

View File

@@ -36,7 +36,7 @@ public:
{
}
uint32_t capabilities() const
uint32_t capabilities() const override
{
return OP_GETFSDATA | OP_CREATE | OP_LIST | OP_GETFILE | OP_PUTFILE |
OP_GETDIRENT | OP_MOVE | OP_CREATEDIR | OP_DELETE;
@@ -199,7 +199,7 @@ public:
throwError(res);
}
void createDirectory(const Path& path)
void createDirectory(const Path& path) override
{
mount();
auto pathStr = path.to_str();
@@ -296,7 +296,7 @@ private:
default:
throw FilesystemException(
fmt::format("unknown fatfs error {}", res));
fmt::format("unknown fatfs error {}", (int)res));
}
}

View File

@@ -44,7 +44,7 @@ public:
return _changedSectors.put(track, side, sectorId);
}
virtual bool isReadOnly()
virtual bool isReadOnly() override
{
return (_fluxSink == nullptr);
}

View File

@@ -20,19 +20,19 @@ public:
public:
std::shared_ptr<const Sector> get(
unsigned track, unsigned side, unsigned sectorId)
unsigned track, unsigned side, unsigned sectorId) override
{
return _image->get(track, side, sectorId);
}
std::shared_ptr<Sector> put(
unsigned track, unsigned side, unsigned sectorId)
unsigned track, unsigned side, unsigned sectorId) override
{
_changed = true;
return _image->put(track, side, sectorId);
}
virtual bool isReadOnly()
virtual bool isReadOnly() override
{
return (_writer == nullptr);
}

View File

@@ -23,7 +23,7 @@ public:
{
}
uint32_t capabilities() const
uint32_t capabilities() const override
{
return OP_GETFSDATA | OP_CREATE | OP_LIST | OP_GETFILE | OP_PUTFILE |
OP_GETDIRENT | OP_MOVE | OP_CREATEDIR | OP_DELETE;

View File

@@ -160,7 +160,7 @@ public:
{
}
uint32_t capabilities() const
uint32_t capabilities() const override
{
return OP_GETFSDATA | OP_LIST | OP_GETFILE | OP_GETDIRENT;
}

View File

@@ -140,7 +140,7 @@ public:
{
}
uint32_t capabilities() const
uint32_t capabilities() const override
{
return OP_LIST | OP_GETDIRENT | OP_GETFILE | OP_GETFSDATA;
}

427
lib/vfs/roland.cc Normal file
View File

@@ -0,0 +1,427 @@
#include "lib/globals.h"
#include "lib/vfs/vfs.h"
#include "lib/config.pb.h"
#include "lib/utils.h"
#include <regex>
static std::string unmangleFilename(const std::string& mangled)
{
std::string extension = mangled.substr(10);
extension.erase(extension.find_last_not_of("_") + 1);
std::string root = mangled.substr(0, 10);
root.erase(root.find_last_not_of("_") + 1);
if (!extension.empty())
return root + "." + extension;
return root;
}
static std::string mangleFilename(const std::string& human)
{
int dot = human.rfind('.');
std::string extension =
(dot == std::string::npos) ? "" : human.substr(dot + 1);
std::string root =
(dot == std::string::npos) ? human : human.substr(0, dot);
if (extension.empty())
extension = "___";
if (extension.size() > 3)
throw BadPathException("Invalid filename: extension too long");
if (root.size() > 10)
throw BadPathException("Invalid filename: root too long");
root = (root + std::string(10, '_')).substr(0, 10);
std::string mangled = root + extension;
static const std::regex checker("[A-Z0-9_$.]*");
if (!std::regex_match(mangled, checker))
throw BadPathException(
"Invalid filename: unsupported characters (remember to use "
"uppercase)");
return mangled;
}
class RolandFsFilesystem : public Filesystem
{
private:
class RolandDirent : public Dirent
{
public:
RolandDirent(const std::string& filename)
{
file_type = TYPE_FILE;
rename(filename);
length = 0;
attributes[Filesystem::FILENAME] = filename;
attributes[Filesystem::LENGTH] = std::to_string(length);
attributes[Filesystem::FILE_TYPE] = "file";
attributes[Filesystem::MODE] = "";
}
void rename(const std::string& name)
{
filename = name;
path = {filename};
}
void putBlock(RolandFsFilesystem* fs, uint8_t offset, uint8_t block)
{
if (blocks.size() <= offset)
blocks.resize(offset+1);
blocks[offset] = block;
length = (offset+1) * fs->_blockSectors * fs->_sectorSize;
attributes[Filesystem::LENGTH] = std::to_string(length);
}
void putBlocks(RolandFsFilesystem* fs, uint8_t offset, Bytes& dirent)
{
for (int i = 0; i < 16; i++)
{
uint8_t blocknumber = dirent[16 + i];
if (!blocknumber)
break;
putBlock(fs, offset+i, blocknumber);
}
}
public:
std::vector<int> blocks;
};
public:
RolandFsFilesystem(
const RolandFsProto& config, std::shared_ptr<SectorInterface> sectors):
Filesystem(sectors),
_config(config)
{
}
uint32_t capabilities() const override
{
return OP_CREATE | OP_GETFSDATA | OP_LIST | OP_GETFILE | OP_PUTFILE |
OP_GETDIRENT | OP_DELETE | OP_MOVE;
}
std::map<std::string, std::string> getMetadata() override
{
mount();
int usedBlocks = 0;
for (bool b : _allocationBitmap)
usedBlocks += b;
std::map<std::string, std::string> attributes;
attributes[VOLUME_NAME] = "";
attributes[TOTAL_BLOCKS] = std::to_string(_filesystemBlocks);
attributes[USED_BLOCKS] = std::to_string(usedBlocks);
attributes[BLOCK_SIZE] = std::to_string(_config.block_size());
return attributes;
}
FilesystemStatus check() override
{
return FS_OK;
}
void create(bool quick, const std::string& volumeName) override
{
if (!quick)
eraseEverythingOnDisk();
init();
_allocationBitmap[0] = true;
rewriteDirectory();
}
std::vector<std::shared_ptr<Dirent>> list(const Path& path) override
{
mount();
if (!path.empty())
throw FileNotFoundException();
std::vector<std::shared_ptr<Dirent>> result;
for (auto& de : _dirents)
result.push_back(de);
return result;
}
std::shared_ptr<Dirent> getDirent(const Path& path) override
{
mount();
if (path.size() != 1)
throw BadPathException();
return findFile(path.front());
}
Bytes getFile(const Path& path) override
{
mount();
if (path.size() != 1)
throw BadPathException();
Bytes data;
ByteWriter bw(data);
auto f = findFile(path.front());
for (uint8_t b : f->blocks)
bw += getRolandBlock(b);
return data;
}
void putFile(const Path& path, const Bytes& bytes) override
{
mount();
if (path.size() != 1)
throw BadPathException();
if (findFileOrReturnNull(path.front()))
throw BadPathException("File exists");
int blocks = bytes.size() / _config.block_size();
auto de = std::make_shared<RolandDirent>(
RolandDirent(mangleFilename(path.front())));
ByteReader br(bytes);
int offset = 0;
while (!br.eof())
{
Bytes data =
br.read(_config.block_size()).slice(0, _config.block_size());
int block = allocateBlock();
de->putBlock(this, offset, block);
putRolandBlock(block, data);
offset++;
}
_dirents.push_back(de);
rewriteDirectory();
}
void deleteFile(const Path& path) override
{
mount();
if (path.size() != 1)
throw BadPathException();
auto de = findFile(path.front());
for (uint8_t b : de->blocks)
freeBlock(b);
for (auto it = _dirents.begin(); it != _dirents.end(); it++)
{
if (*it == de)
{
_dirents.erase(it);
break;
}
}
rewriteDirectory();
}
void moveFile(const Path& oldName, const Path& newName) override
{
mount();
if ((oldName.size() != 1) || (newName.size() != 1))
throw BadPathException();
if (findFileOrReturnNull(newName.front()))
throw BadPathException("File exists");
auto de = findFile(oldName.front());
de->rename(mangleFilename(newName.front()));
rewriteDirectory();
}
private:
void init()
{
_directoryLba = getOffsetOfSector(_config.directory_track(), 0, 0);
_sectorSize = getLogicalSectorSize(0, 0);
_blockSectors = _config.block_size() / _sectorSize;
_filesystemBlocks = getLogicalSectorCount() / _blockSectors;
_midBlock = (getLogicalSectorCount() - _directoryLba) / _blockSectors;
_dirents.clear();
_allocationBitmap.clear();
_allocationBitmap.resize(_filesystemBlocks);
}
void rewriteDirectory()
{
Bytes directory;
ByteWriter bw(directory);
bw.write_8(0);
bw.append("ROLAND-GCRDOS");
bw.write_le16(0x4e);
bw.pad(16);
for (auto& de : _dirents)
{
int blockIndex = 0;
for (;;)
{
if (bw.pos == 0xa00)
throw DiskFullException();
if ((blockIndex % 16) == 0)
{
bw.write_8(0);
int len = de->filename.size();
bw.append(de->filename);
bw.pad(13 - len, '_');
bw.write_8(0);
bw.write_8(blockIndex / 16);
}
if (blockIndex == de->blocks.size())
break;
bw.write_8(de->blocks[blockIndex]);
blockIndex++;
}
bw.pad(16 - (blockIndex % 16), 0);
}
while (bw.pos != 0xa00)
{
bw.write_8(0xe5);
bw.pad(13, ' ');
bw.write_le16(0);
bw.pad(16);
}
for (bool b : _allocationBitmap)
bw.write_8(b ? 0xff : 0x00);
putRolandBlock(0, directory.slice(0, _config.block_size()));
}
void mount()
{
init();
Bytes directory = getRolandBlock(0);
ByteReader br(directory);
br.seek(1);
if (br.read(13) != "ROLAND-GCRDOS")
throw BadFilesystemException();
br.seek(32);
std::map<std::string, std::shared_ptr<RolandDirent>> files;
for (int i = 0; i < _config.directory_entries(); i++)
{
Bytes direntBytes = br.read(32);
if (direntBytes[0] == 0)
{
int extent = direntBytes[15];
std::string filename =
unmangleFilename(direntBytes.slice(1, 13));
std::shared_ptr<RolandDirent> de;
auto it = files.find(filename);
if (it == files.end())
{
files[filename] = de =
std::make_shared<RolandDirent>(filename);
_dirents.push_back(de);
}
else
de = it->second;
de->putBlocks(this, extent*16, direntBytes);
}
}
br.seek(0xa00);
for (int i = 0; i < _filesystemBlocks; i++)
_allocationBitmap[i] = br.read_8();
}
std::shared_ptr<RolandDirent> findFileOrReturnNull(
const std::string filename)
{
for (const auto& dirent : _dirents)
{
if (dirent->filename == filename)
return dirent;
}
return nullptr;
}
std::shared_ptr<RolandDirent> findFile(const std::string filename)
{
std::shared_ptr<RolandDirent> de = findFileOrReturnNull(filename);
if (!de)
throw FileNotFoundException();
return de;
}
int allocateBlock()
{
for (int i = 0; i < _filesystemBlocks; i++)
if (!_allocationBitmap[i])
{
_allocationBitmap[i] = true;
return i;
}
throw DiskFullException();
}
void freeBlock(int block)
{
if (block >= _filesystemBlocks)
throw BadFilesystemException();
if (!_allocationBitmap[block])
throw BadFilesystemException();
_allocationBitmap[block] = false;
}
unsigned blockToLogicalSectorNumber(int block)
{
int track;
if (block < _midBlock)
track = _config.directory_track() + block;
else
track = _config.directory_track() - (1 + block - _midBlock);
return track * _blockSectors;
}
Bytes getRolandBlock(int number)
{
return getLogicalSector(
blockToLogicalSectorNumber(number), _blockSectors);
}
void putRolandBlock(int number, const Bytes& bytes)
{
assert(bytes.size() == _config.block_size());
putLogicalSector(blockToLogicalSectorNumber(number), bytes);
}
private:
const RolandFsProto& _config;
unsigned _sectorSize;
unsigned _blockSectors;
unsigned _midBlock;
unsigned _directoryLba;
unsigned _filesystemBlocks;
std::vector<std::shared_ptr<RolandDirent>> _dirents;
std::vector<bool> _allocationBitmap;
};
std::unique_ptr<Filesystem> Filesystem::createRolandFsFilesystem(
const FilesystemProto& config, std::shared_ptr<SectorInterface> sectors)
{
return std::make_unique<RolandFsFilesystem>(config.roland(), sectors);
}

View File

@@ -11,6 +11,9 @@ class Encoder;
class SectorInterface
{
public:
virtual ~SectorInterface() {}
public:
virtual std::shared_ptr<const Sector> get(
unsigned track, unsigned side, unsigned sectorId) = 0;

View File

@@ -139,7 +139,7 @@ public:
{
}
uint32_t capabilities() const
uint32_t capabilities() const override
{
return OP_LIST | OP_GETFILE | OP_GETFSDATA | OP_GETDIRENT;
}

View File

@@ -216,6 +216,12 @@ std::unique_ptr<Filesystem> Filesystem::createFilesystem(
case FilesystemProto::LIF:
return Filesystem::createLifFilesystem(config, image);
case FilesystemProto::ZDOS:
return Filesystem::createZDosFilesystem(config, image);
case FilesystemProto::ROLAND:
return Filesystem::createRolandFsFilesystem(config, image);
default:
error("no filesystem configured");
return std::unique_ptr<Filesystem>();
@@ -236,7 +242,7 @@ std::unique_ptr<Filesystem> Filesystem::createFilesystemFromConfig()
fluxSource = globalConfig().getFluxSource();
decoder = globalConfig().getDecoder();
}
if (globalConfig()->flux_sink().type() == FluxSinkProto::DRIVE)
if (globalConfig()->flux_sink().type() == FLUXTYPE_DRIVE)
{
fluxSink = globalConfig().getFluxSink();
encoder = globalConfig().getEncoder();
@@ -273,8 +279,8 @@ Bytes Filesystem::getLogicalSector(uint32_t number, uint32_t count)
{
if ((number + count) > _locations.size())
throw BadFilesystemException(
fmt::format("invalid filesystem: sector {} is out of bounds",
number + count - 1));
fmt::format("invalid filesystem: sector {} is out of bounds ({} maximum)",
number + count - 1, _locations.size()));
Bytes data;
ByteWriter bw(data);

View File

@@ -101,6 +101,14 @@ public:
DiskFullException(const std::string& msg): CannotWriteException(msg) {}
};
class ReadErrorException : public FilesystemException
{
public:
ReadErrorException(): FilesystemException("Fatal read error") {}
ReadErrorException(const std::string& msg): FilesystemException(msg) {}
};
class ReadOnlyFilesystemException : public FilesystemException
{
public:
@@ -256,6 +264,10 @@ public:
const FilesystemProto& config, std::shared_ptr<SectorInterface> image);
static std::unique_ptr<Filesystem> createLifFilesystem(
const FilesystemProto& config, std::shared_ptr<SectorInterface> image);
static std::unique_ptr<Filesystem> createZDosFilesystem(
const FilesystemProto& config, std::shared_ptr<SectorInterface> image);
static std::unique_ptr<Filesystem> createRolandFsFilesystem(
const FilesystemProto& config, std::shared_ptr<SectorInterface> image);
static std::unique_ptr<Filesystem> createFilesystem(
const FilesystemProto& config, std::shared_ptr<SectorInterface> image);

View File

@@ -88,7 +88,29 @@ message LifProto
[ default = 256, (help) = "LIF filesystem block size" ];
}
// NEXT_TAG: 15
message ZDosProto
{
message Location
{
optional uint32 track = 1 [ (help) = "track number" ];
optional uint32 sector = 3 [ (help) = "sector ID" ];
}
optional Location filesystem_start = 1
[ (help) = "position of the filesystem superblock" ];
}
message RolandFsProto
{
optional uint32 directory_track = 1
[ (help) = "position of the directory", default = 39 ];
optional uint32 block_size = 2
[ (help) = "filesystem block size", default = 3072 ];
optional uint32 directory_entries = 3
[ (help) = "number of directory entries", default = 79 ];
}
// NEXT_TAG: 17
message FilesystemProto
{
enum FilesystemType
@@ -106,6 +128,8 @@ message FilesystemProto
APPLEDOS = 10;
PHILE = 11;
LIF = 12;
ZDOS = 13;
ROLAND = 14;
}
optional FilesystemType type = 10
@@ -123,6 +147,8 @@ message FilesystemProto
optional Smaky6FsProto smaky6 = 11;
optional PhileProto phile = 13;
optional LifProto lif = 14;
optional ZDosProto zdos = 15;
optional RolandFsProto roland = 16;
optional SectorListProto sector_order = 9
[ (help) = "specify the filesystem order of sectors" ];

325
lib/vfs/zdos.cc Normal file
View File

@@ -0,0 +1,325 @@
#include "lib/globals.h"
#include "lib/vfs/vfs.h"
#include "lib/config.pb.h"
#include "lib/layout.h"
#include <iomanip>
/* See
* https://oldcomputers.dyndns.org/public/pub/rechner/zilog/zds/manuals/z80-rio_os_userman.pdf,
* page 116. */
enum
{
ZDOS_TYPE_DATA = 0x10,
ZDOS_TYPE_ASCII = 0x20,
ZDOS_TYPE_DIRECTORY = 0x40,
ZDOS_TYPE_PROCEDURE = 0x80
};
enum
{
ZDOS_MODE_FORCE = 1 << 2,
ZDOS_MODE_RANDOM = 1 << 3,
ZDOS_MODE_SECRET = 1 << 4,
ZDOS_MODE_LOCKED = 1 << 5,
ZDOS_MODE_ERASEPROTECT = 1 << 6,
ZDOS_MODE_WRITEPROTECT = 1 << 7
};
static const std::map<uint8_t, std::string> fileTypeMap = {
{0, "INVALID" },
{ZDOS_TYPE_DATA, "DATA" },
{ZDOS_TYPE_ASCII, "ASCII" },
{ZDOS_TYPE_DIRECTORY, "DIRECTORY"},
{ZDOS_TYPE_PROCEDURE, "PROCEDURE"}
};
static std::string convertTime(std::string zdosTime)
{
/* Due to a bug in std::get_time, we can't parse the string directly --- a
* pattern of %y%m%d causes the first four digits of the string to become
* the year. So we need to reform the string. */
zdosTime = fmt::format("{}-{}-{}",
zdosTime.substr(0, 2),
zdosTime.substr(2, 2),
zdosTime.substr(4, 2));
std::tm tm = {};
std::stringstream(zdosTime) >> std::get_time(&tm, "%y-%m-%d");
std::stringstream ss;
ss << std::put_time(&tm, "%FT%T%z");
return ss.str();
}
class ZDosFilesystem : public Filesystem
{
class ZDosDescriptor
{
public:
ZDosDescriptor(ZDosFilesystem* zfs, int block): zfs(zfs)
{
Bytes bytes = zfs->getLogicalSector(block);
ByteReader br(bytes);
br.seek(8);
firstRecord = zfs->readBlockNumber(br);
br.seek(12);
type = br.read_8();
recordCount = br.read_le16();
recordSize = br.read_le16();
br.seek(19);
properties = br.read_8();
startAddress = br.read_le16();
lastRecordSize = br.read_le16();
br.seek(24);
ctime = br.read(8);
mtime = br.read(8);
rewind();
}
void rewind()
{
currentRecord = firstRecord;
eof = false;
}
Bytes readRecord()
{
assert(!eof);
int count = recordSize / 0x80;
Bytes result;
ByteWriter bw(result);
while (count--)
{
Bytes sector = zfs->getLogicalSector(currentRecord);
ByteReader br(sector);
bw += br.read(0x80);
br.skip(2);
int sectorId = br.read_8();
int track = br.read_8();
currentRecord = zfs->toBlockNumber(sectorId, track);
if (sectorId == 0xff)
eof = true;
}
return result;
}
public:
ZDosFilesystem* zfs;
uint16_t firstRecord;
uint8_t type;
uint16_t recordCount;
uint16_t recordSize;
uint8_t properties;
uint16_t startAddress;
uint16_t lastRecordSize;
std::string ctime;
std::string mtime;
uint16_t currentRecord;
bool eof;
};
class ZDosDirent : public Dirent
{
public:
ZDosDirent(ZDosFilesystem* zfs,
const std::string& filename,
int descriptorBlock):
zfs(zfs),
descriptorBlock(descriptorBlock),
zd(zfs, descriptorBlock)
{
file_type = TYPE_FILE;
this->filename = filename;
length = (zd.recordCount - 1) * zd.recordSize + zd.lastRecordSize;
mode = "";
if (zd.properties & ZDOS_MODE_FORCE)
mode += 'F';
if (zd.properties & ZDOS_MODE_RANDOM)
mode += 'R';
if (zd.properties & ZDOS_MODE_SECRET)
mode += 'S';
if (zd.properties & ZDOS_MODE_LOCKED)
mode += 'L';
if (zd.properties & ZDOS_MODE_ERASEPROTECT)
mode += 'E';
if (zd.properties & ZDOS_MODE_WRITEPROTECT)
mode += 'W';
path = {filename};
attributes[Filesystem::FILENAME] = filename;
attributes[Filesystem::LENGTH] = std::to_string(length);
attributes[Filesystem::FILE_TYPE] = "file";
attributes[Filesystem::MODE] = mode;
attributes["zdos.descriptor_record"] =
std::to_string(descriptorBlock);
attributes["zdos.first_record"] = std::to_string(zd.firstRecord);
attributes["zdos.record_size"] = std::to_string(zd.recordSize);
attributes["zdos.record_count"] = std::to_string(zd.recordCount);
attributes["zdos.last_record_size"] =
std::to_string(zd.lastRecordSize);
attributes["zdos.start_address"] =
fmt::format("0x{:04x}", zd.startAddress);
attributes["zdos.type"] = fileTypeMap.at(zd.type & 0xf0);
attributes["zdos.ctime"] = convertTime(zd.ctime);
attributes["zdos.mtime"] = convertTime(zd.mtime);
}
public:
ZDosFilesystem* zfs;
int descriptorBlock;
ZDosDescriptor zd;
};
public:
ZDosFilesystem(
const ZDosProto& config, std::shared_ptr<SectorInterface> sectors):
Filesystem(sectors),
_config(config)
{
}
uint32_t capabilities() const
{
return OP_GETFSDATA | OP_LIST | OP_GETFILE | OP_GETDIRENT;
}
FilesystemStatus check() override
{
return FS_OK;
}
std::map<std::string, std::string> getMetadata() override
{
mount();
std::map<std::string, std::string> attributes;
attributes[VOLUME_NAME] = "";
attributes[TOTAL_BLOCKS] = std::to_string(_totalBlocks);
attributes[USED_BLOCKS] = std::to_string(_usedBlocks);
attributes[BLOCK_SIZE] = "128";
return attributes;
}
std::shared_ptr<Dirent> getDirent(const Path& path) override
{
mount();
if (path.size() != 1)
throw BadPathException();
return findFile(path.front());
}
std::vector<std::shared_ptr<Dirent>> list(const Path& path) override
{
mount();
if (!path.empty())
throw FileNotFoundException();
std::vector<std::shared_ptr<Dirent>> result;
for (auto& de : _dirents)
result.push_back(de);
return result;
}
Bytes getFile(const Path& path) override
{
mount();
if (path.size() != 1)
throw BadPathException();
auto dirent = findFile(path.front());
dirent->zd.rewind();
Bytes bytes;
ByteWriter bw(bytes);
while (!dirent->zd.eof)
{
bw += dirent->zd.readRecord();
}
return bytes.slice(0, dirent->length);
}
private:
void mount()
{
_sectorsPerTrack = Layout::getLayoutOfTrack(0, 0)->numSectors;
int rootBlock = toBlockNumber(_config.filesystem_start().sector(),
_config.filesystem_start().track());
ZDosDescriptor zd(this, rootBlock);
if (zd.type != ZDOS_TYPE_DIRECTORY)
throw BadFilesystemException();
_totalBlocks = getLogicalSectorCount();
_usedBlocks = (zd.recordCount * zd.recordSize) / 0x80 + 1;
while (!zd.eof)
{
Bytes bytes = zd.readRecord();
ByteReader br(bytes);
for (;;)
{
int len = br.read_8();
if (len == 0xff)
break;
std::string filename = br.read(len & 0x7f);
int descriptorBlock = readBlockNumber(br);
auto dirent = std::make_unique<ZDosDirent>(
this, filename, descriptorBlock);
_usedBlocks +=
(dirent->zd.recordCount * dirent->zd.recordSize) / 0x80 + 1;
_dirents.push_back(std::move(dirent));
}
}
}
std::shared_ptr<ZDosDirent> findFile(const std::string filename)
{
for (const auto& dirent : _dirents)
{
if (dirent->filename == filename)
return dirent;
}
throw FileNotFoundException();
}
int toBlockNumber(int sectorId, int track)
{
return track * _sectorsPerTrack + sectorId;
}
int readBlockNumber(ByteReader& br)
{
int sectorId = br.read_8();
int track = br.read_8();
return toBlockNumber(sectorId, track);
}
private:
const ZDosProto& _config;
unsigned _sectorsPerTrack;
unsigned _totalBlocks;
unsigned _usedBlocks;
std::vector<std::shared_ptr<ZDosDirent>> _dirents;
};
std::unique_ptr<Filesystem> Filesystem::createZDosFilesystem(
const FilesystemProto& config, std::shared_ptr<SectorInterface> sectors)
{
return std::make_unique<ZDosFilesystem>(config.zdos(), sectors);
}

View File

@@ -14,9 +14,12 @@ static std::string supportStatus(SupportStatus status)
case SupportStatus::UNICORN:
return "🦄";
case SupportStatus::UNSUPPORTED:
return "";
}
return "";
return "";
}
int main(int argc, const char* argv[])

View File

@@ -247,10 +247,10 @@ static void draw_x_graticules(Agg2D& painter,
int mainAnalyseDriveResponse(int argc, const char* argv[])
{
globalConfig().overrides()->mutable_flux_source()->set_type(
FluxSourceProto::DRIVE);
FLUXTYPE_DRIVE);
flags.parseFlagsWithConfigFiles(argc, argv, {});
if (globalConfig()->flux_sink().type() != FluxSinkProto::DRIVE)
if (globalConfig()->flux_sink().type() != FLUXTYPE_DRIVE)
error("this only makes sense with a real disk drive");
usbSetDrive(globalConfig()->drive().drive(),

View File

@@ -132,7 +132,7 @@ static nanoseconds_t guessClock(const Fluxmap& fluxmap)
int mainInspect(int argc, const char* argv[])
{
globalConfig().overrides()->mutable_flux_source()->set_type(
FluxSourceProto::DRIVE);
FLUXTYPE_DRIVE);
flags.parseFlagsWithConfigFiles(argc, argv, {});
auto& fluxSource = globalConfig().getFluxSource();

View File

@@ -57,10 +57,10 @@ int mainRawRead(int argc, const char* argv[])
if (argc == 1)
showProfiles("rawread", formats);
globalConfig().overrides()->mutable_flux_source()->set_type(
FluxSourceProto::DRIVE);
FLUXTYPE_DRIVE);
flags.parseFlagsWithConfigFiles(argc, argv, formats);
if (globalConfig()->flux_sink().type() == FluxSinkProto::DRIVE)
if (globalConfig()->flux_sink().type() == FLUXTYPE_DRIVE)
error("you can't use rawread to write to hardware");
std::shared_ptr<FluxSource> fluxSource = globalConfig().getFluxSource();

View File

@@ -49,7 +49,7 @@ static ActionFlag eraseFlag({"--erase"},
[]()
{
globalConfig().overrides()->mutable_flux_source()->set_type(
FluxSourceProto::ERASE);
FLUXTYPE_ERASE);
});
int mainRawWrite(int argc, const char* argv[])
@@ -60,10 +60,10 @@ int mainRawWrite(int argc, const char* argv[])
if (argc == 1)
showProfiles("rawwrite", formats);
globalConfig().overrides()->mutable_flux_sink()->set_type(
FluxSinkProto::DRIVE);
FLUXTYPE_DRIVE);
flags.parseFlagsWithConfigFiles(argc, argv, formats);
if (globalConfig()->flux_source().type() == FluxSourceProto::DRIVE)
if (globalConfig()->flux_source().type() == FLUXTYPE_DRIVE)
error("you can't use rawwrite to read from hardware");
auto& fluxSource = globalConfig().getFluxSource();

View File

@@ -61,10 +61,10 @@ int mainRead(int argc, const char* argv[])
{
if (argc == 1)
showProfiles("read", formats);
globalConfig().set("flux_source.type", "DRIVE");
globalConfig().set("flux_source.type", "FLUXTYPE_DRIVE");
flags.parseFlagsWithConfigFiles(argc, argv, formats);
if (globalConfig()->decoder().copy_flux_to().type() == FluxSinkProto::DRIVE)
if (globalConfig()->decoder().copy_flux_to().type() == FLUXTYPE_DRIVE)
error("you cannot copy flux to a hardware device");
auto& fluxSource = globalConfig().getFluxSource();

View File

@@ -19,7 +19,7 @@ int mainRpm(int argc, const char* argv[])
{
flags.parseFlagsWithConfigFiles(argc, argv, {});
if (globalConfig()->flux_source().type() != FluxSourceProto::DRIVE)
if (globalConfig()->flux_source().type() != FLUXTYPE_DRIVE)
error("this only makes sense with a real disk drive");
usbSetDrive(globalConfig()->drive().drive(),

View File

@@ -23,7 +23,7 @@ int mainSeek(int argc, const char* argv[])
{
flags.parseFlagsWithConfigFiles(argc, argv, {});
if (globalConfig()->flux_source().type() != FluxSourceProto::DRIVE)
if (globalConfig()->flux_source().type() != FLUXTYPE_DRIVE)
error("this only makes sense with a real disk drive");
usbSetDrive(globalConfig()->drive().drive(),

View File

@@ -14,6 +14,18 @@
#include <google/protobuf/text_format.h>
#include <fstream>
static void ignoreInapplicableValueExceptions(std::function<void(void)> cb)
{
try
{
cb();
}
catch (const InapplicableValueException* e)
{
/* swallow */
}
}
FlagGroup fileFlags;
static StringFlag image({"-i", "--image"},
@@ -21,8 +33,16 @@ static StringFlag image({"-i", "--image"},
"",
[](const auto& value)
{
globalConfig().setImageReader(value);
globalConfig().setImageWriter(value);
ignoreInapplicableValueExceptions(
[&]()
{
globalConfig().setImageReader(value);
});
ignoreInapplicableValueExceptions(
[&]()
{
globalConfig().setImageWriter(value);
});
});
static StringFlag flux({"-f", "--flux"},
@@ -30,6 +50,14 @@ static StringFlag flux({"-f", "--flux"},
"",
[](const auto& value)
{
globalConfig().setFluxSource(value);
globalConfig().setFluxSink(value);
ignoreInapplicableValueExceptions(
[&]()
{
globalConfig().setFluxSink(value);
});
ignoreInapplicableValueExceptions(
[&]()
{
globalConfig().setFluxSource(value);
});
});

View File

@@ -22,7 +22,7 @@ they might require nudging as the side order can't be reliably autodetected.
image_writer {
filename: "acornadfs.img"
type: IMG
type: IMAGETYPE_IMG
}
decoder {

View File

@@ -25,12 +25,12 @@ documentation:
image_reader {
filename: "acorndfs.img"
type: IMG
type: IMAGETYPE_IMG
}
image_writer {
filename: "acorndfs.img"
type: IMG
type: IMAGETYPE_IMG
}
layout {

View File

@@ -43,7 +43,7 @@ documentation:
image_writer {
filename: "aeslanier.img"
type: IMG
type: IMAGETYPE_IMG
}
decoder {

View File

@@ -28,7 +28,7 @@ documentation:
image_writer {
filename: "agat.img"
type: IMG
type: IMAGETYPE_IMG
}
layout {

View File

@@ -31,12 +31,12 @@ documentation:
image_reader {
filename: "amiga.adf"
type: IMG
type: IMAGETYPE_IMG
}
image_writer {
filename: "amiga.adf"
type: IMG
type: IMAGETYPE_IMG
}
layout {

View File

@@ -44,7 +44,7 @@ documentation:
image_writer {
filename: "ampro.img"
type: IMG
type: IMAGETYPE_IMG
}
layout {

View File

@@ -19,12 +19,12 @@ on what was available at the time, with the same format on both.
image_reader {
filename: "bk800.img"
type: IMG
type: IMAGETYPE_IMG
}
image_writer {
filename: "bk800.img"
type: IMG
type: IMAGETYPE_IMG
}
layout {

View File

@@ -116,12 +116,12 @@ file system supports this.
image_reader {
filename: "brother.img"
type: IMG
type: IMAGETYPE_IMG
}
image_writer {
filename: "brother.img"
type: IMG
type: IMAGETYPE_IMG
}
encoder {

View File

@@ -54,12 +54,12 @@ documentation:
image_reader {
filename: "commodore.d64"
type: D64
type: IMAGETYPE_D64
}
image_writer {
filename: "commodore.d64"
type: D64
type: IMAGETYPE_D64
}
filesystem {

View File

@@ -37,7 +37,7 @@ documentation:
image_writer {
filename: "eco1.img"
type: IMG
type: IMAGETYPE_IMG
}
layout {

View File

@@ -11,7 +11,7 @@ format itself is yet another IBM scheme variant.
image_writer {
filename: "epsonpf10.img"
type: IMG
type: IMAGETYPE_IMG
}
layout {

View File

@@ -42,7 +42,7 @@ There's amazingly little information about these things.
image_writer {
filename: "f85.img"
type: IMG
type: IMAGETYPE_IMG
}
decoder {

View File

@@ -43,7 +43,7 @@ documentation:
image_writer {
filename: "fb100.img"
type: IMG
type: IMAGETYPE_IMG
}
decoder {

View File

@@ -20,12 +20,12 @@ drive {
image_reader {
filename: "hplif.img"
type: IMG
type: IMAGETYPE_IMG
}
image_writer {
filename: "hplif.img"
type: IMG
type: IMAGETYPE_IMG
}
decoder {

View File

@@ -84,12 +84,12 @@ versa, so it shouldn't matter.
image_reader {
filename: "ibm.img"
type: IMG
type: IMAGETYPE_IMG
}
image_writer {
filename: "ibm.img"
type: IMG
type: IMAGETYPE_IMG
}
decoder {

View File

@@ -11,7 +11,7 @@ track! Other than that it's another IBM scheme variation.
image_writer {
filename: "icl30.img"
type: IMG
type: IMAGETYPE_IMG
}
layout {

View File

@@ -61,12 +61,12 @@ documentation:
image_reader {
filename: "mac.dsk"
type: IMG
type: IMAGETYPE_IMG
}
image_writer {
filename: "mac.dsk"
type: IMG
type: IMAGETYPE_IMG
}
layout {

View File

@@ -67,12 +67,12 @@ drive {
image_reader {
filename: "micropolis.img"
type: IMG
type: IMAGETYPE_IMG
}
image_writer {
filename: "micropolis.img"
type: IMG
type: IMAGETYPE_IMG
}
layout {
@@ -100,12 +100,12 @@ option {
config {
image_reader {
filename: "micropolis.vgi"
type: IMG
type: IMAGETYPE_IMG
}
image_writer {
filename: "micropolis.vgi"
type: IMG
type: IMAGETYPE_IMG
}
layout {

View File

@@ -57,7 +57,7 @@ documentation:
image_writer {
filename: "mx.img"
type: IMG
type: IMAGETYPE_IMG
}
layout {

View File

@@ -19,12 +19,12 @@ drive {
image_reader {
filename: "n88basic.img"
type: IMG
type: IMAGETYPE_IMG
}
image_writer {
filename: "n88basic.img"
type: IMG
type: IMAGETYPE_IMG
}
layout {

View File

@@ -35,12 +35,12 @@ documentation:
image_reader {
filename: "northstar.nsi"
type: NSI
type: IMAGETYPE_NSI
}
image_writer {
filename: "northstar.nsi"
type: NSI
type: IMAGETYPE_NSI
}
layout {

View File

@@ -26,12 +26,12 @@ drive {
image_reader {
filename: "pme.img"
type: IMG
type: IMAGETYPE_IMG
}
image_writer {
filename: "pme.img"
type: IMG
type: IMAGETYPE_IMG
}
layout {

View File

@@ -1,6 +1,7 @@
shortname: 'Roland D20'
comment: '3.5" electronic synthesiser disks'
read_support_status: DINOSAUR
read_support_status: UNICORN
write_support_status: DINOSAUR
documentation:
<<<
@@ -10,17 +11,38 @@ drive, used for saving MIDI sequences and samples.
Weirdly, it seems to use precisely the same format as the Brother word
processors: a thoroughly non-IBM-compatible custom GCR system.
FluxEngine pretends to support this, but it has had almost no testing, the only
disk image I have seen for it was mostly corrupt, and very little is known
about the format, so I have no idea whether it's correct or not.
FluxEngine supports both reading and writing D20 disks, as well as basic support
for the filesystem, allowing files to be read from and written to D20 disks.
Note that the D20 was never intended to support arbitrary files on its disks and
is very likely to crash if you put unexpected files on a disk. In addition,
while the file format itself is currently unknown, there is a header at the top
of the file containing what appears to be the name shown in the D20 file
browser, so the name by which you see it is not necessarily the filename.
A word of warning --- just like the Brother word processors, the D20 floppy
drive isn't very well aligned. The drive itself uses quarter-stepping to
automatically adapt to whatever alignment the disk was formatted with. This
means that trying to read such a disk on a PC drive, which does _not_ have
adjustable alignment, may not work very well. In these situations it is possible
to adjust the alignment of most modern drives, but this is a somewhat risky
process and may result in permanently wrecking the drive alignment.
Please [get in touch](https://github.com/davidgiven/fluxengine/issues/new) if
you know anything about it.
Many thanks to trondl [on the VCF
forums](https://forum.vcfed.org/index.php?threads/roland-d-20-decoding-the-mysterious-floppy-format.1243226/)
for assistance with this!
>>>
image_reader {
filename: "rolandd20.img"
type: IMAGETYPE_IMG
}
image_writer {
filename: "rolandd20.img"
type: IMG
type: IMAGETYPE_IMG
}
layout {
@@ -37,6 +59,10 @@ layout {
}
}
encoder {
brother {}
}
decoder {
brother {}
}
@@ -45,3 +71,6 @@ drive {
head_bias: 1
}
filesystem {
type: ROLAND
}

View File

@@ -16,12 +16,12 @@ drive {
image_reader {
filename: "rx50.img"
type: IMG
type: IMAGETYPE_IMG
}
image_writer {
filename: "rx50.img"
type: IMG
type: IMAGETYPE_IMG
}
layout {

View File

@@ -29,7 +29,7 @@ documentation:
image_writer {
filename: "smaky6.img"
type: IMG
type: IMAGETYPE_IMG
}
layout {

View File

@@ -31,12 +31,12 @@ documentation:
image_reader {
filename: "tids990.img"
type: IMG
type: IMAGETYPE_IMG
}
image_writer {
filename: "tids990.img"
type: IMG
type: IMAGETYPE_IMG
}
layout {

View File

@@ -11,7 +11,7 @@ on the precise format.
image_writer {
filename: "tiki.img"
type: IMG
type: IMAGETYPE_IMG
}
decoder {

View File

@@ -50,12 +50,12 @@ documentation:
image_reader {
filename: "victor9k.img"
type: IMG
type: IMAGETYPE_IMG
}
image_writer {
filename: "victor9k.img"
type: IMG
type: IMAGETYPE_IMG
}
layout {

View File

@@ -21,11 +21,8 @@ bytes per sector --- 128 bytes of user payload plus two two-byte metadata
words used to construct linked lists of sectors for storing files. These
stored 320kB each.
FluxEngine has experimental read support for these disks, based on a single
Catweasel flux file I've been able to obtain, which only contained 70 tracks.
I haven't been able to try this for real. If anyone has any of these disks,
an 8-inch drive, a FluxEngine and the appropriate adapter, please [get in
touch](https://github.com/davidgiven/fluxengine/issues/new)...
FluxEngine has read support for these, including support for RIO's ZDOS file
system.
>>>
documentation:
@@ -40,7 +37,7 @@ documentation:
image_writer {
filename: "zilogmcz.img"
type: IMG
type: IMAGETYPE_IMG
}
decoder {
@@ -60,4 +57,13 @@ layout {
}
}
filesystem {
type: ZDOS
zdos {
filesystem_start {
track: 22
sector: 0
}
}
}

View File

@@ -483,6 +483,7 @@ private:
return;
_filesystem->moveFile(oldPath, newPath);
node->newname = "";
auto dirent = _filesystem->getDirent(newPath);
runOnUiThread(

Some files were not shown because too many files have changed in this diff Show More