mirror of
https://github.com/davidgiven/fluxengine.git
synced 2025-10-31 11:17:01 -07:00
Merge branch 'master' into basis
This commit is contained in:
2
.github/workflows/ccpp.yml
vendored
2
.github/workflows/ccpp.yml
vendored
@@ -62,7 +62,7 @@ jobs:
|
|||||||
|
|
||||||
- name: zip
|
- name: zip
|
||||||
run: |
|
run: |
|
||||||
zip -9 fluxengine.zip fluxengine.exe fluxengine-gui.exe upgrade-flux-file.exe brother120tool.exe brother240tool.exe FluxEngine.cydsn/CortexM3/ARM_GCC_541/Release/FluxEngine.hex fluxengine-installer.exe
|
zip -9 fluxengine-windows.zip fluxengine.exe fluxengine-gui.exe upgrade-flux-file.exe brother120tool.exe brother240tool.exe FluxEngine.cydsn/CortexM3/ARM_GCC_541/Release/FluxEngine.hex fluxengine-installer.exe
|
||||||
|
|
||||||
- name: Upload build artifacts
|
- name: Upload build artifacts
|
||||||
uses: actions/upload-artifact@v2
|
uses: actions/upload-artifact@v2
|
||||||
|
|||||||
@@ -115,7 +115,8 @@ people who've had it work).
|
|||||||
| [Elektronika BK](doc/disk-bd.md) | 🦄 | 🦄 | Soviet Union PDP-11 clone |
|
| [Elektronika BK](doc/disk-bd.md) | 🦄 | 🦄 | Soviet Union PDP-11 clone |
|
||||||
| [Macintosh 400kB/800kB](doc/disk-macintosh.md) | 🦄 | 🦄 | |
|
| [Macintosh 400kB/800kB](doc/disk-macintosh.md) | 🦄 | 🦄 | |
|
||||||
| [NEC PC-98](doc/disk-ibm.md) | 🦄 | 🦄 | trimode drive not required |
|
| [NEC PC-98](doc/disk-ibm.md) | 🦄 | 🦄 | trimode drive not required |
|
||||||
| [Sharp X68000](doc/disk-ibm.md) | 🦄 | 🦄 | |
|
| [pSOS](doc/disk-ibm.md) | 🦄 | 🦖* | yet another IBM scheme |
|
||||||
|
| [Sharp X68000](doc/disk-ibm.md) | 🦄 | 🦄 | pSOS PHILE file system |
|
||||||
| [Smaky 6](doc/disk-smaky6.md) | 🦖 | | 5.25" hard sectored |
|
| [Smaky 6](doc/disk-smaky6.md) | 🦖 | | 5.25" hard sectored |
|
||||||
| [TRS-80](doc/disk-trs80.md) | 🦖 | 🦖* | a minor variation of the IBM scheme |
|
| [TRS-80](doc/disk-trs80.md) | 🦖 | 🦖* | a minor variation of the IBM scheme |
|
||||||
{: .datatable }
|
{: .datatable }
|
||||||
|
|||||||
@@ -147,6 +147,7 @@ public:
|
|||||||
_sector->logicalSide = br.read_8();
|
_sector->logicalSide = br.read_8();
|
||||||
_sector->logicalSector = br.read_8();
|
_sector->logicalSector = br.read_8();
|
||||||
_currentSectorSize = 1 << (br.read_8() + 7);
|
_currentSectorSize = 1 << (br.read_8() + 7);
|
||||||
|
|
||||||
uint16_t gotCrc = crc16(CCITT_POLY, bytes.slice(0, br.pos));
|
uint16_t gotCrc = crc16(CCITT_POLY, bytes.slice(0, br.pos));
|
||||||
uint16_t wantCrc = br.read_be16();
|
uint16_t wantCrc = br.read_be16();
|
||||||
if (wantCrc == gotCrc)
|
if (wantCrc == gotCrc)
|
||||||
@@ -206,6 +207,18 @@ public:
|
|||||||
uint16_t wantCrc = br.read_be16();
|
uint16_t wantCrc = br.read_be16();
|
||||||
_sector->status =
|
_sector->status =
|
||||||
(wantCrc == gotCrc) ? Sector::OK : Sector::BAD_CHECKSUM;
|
(wantCrc == gotCrc) ? Sector::OK : Sector::BAD_CHECKSUM;
|
||||||
|
|
||||||
|
auto layout = Layout::getLayoutOfTrack(
|
||||||
|
_sector->logicalTrack, _sector->logicalSide);
|
||||||
|
if (_currentSectorSize != layout->sectorSize)
|
||||||
|
std::cerr << fmt::format(
|
||||||
|
"Warning: configured sector size for t{}.h{}.s{} is {} bytes "
|
||||||
|
"but that seen on disk is {} bytes\n",
|
||||||
|
_sector->logicalTrack,
|
||||||
|
_sector->logicalSide,
|
||||||
|
_sector->logicalSector,
|
||||||
|
layout->sectorSize,
|
||||||
|
_currentSectorSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|||||||
@@ -155,6 +155,14 @@ more common tools.
|
|||||||
encoding. You can specify a profile if you want to write a subset of the
|
encoding. You can specify a profile if you want to write a subset of the
|
||||||
disk.
|
disk.
|
||||||
|
|
||||||
|
- `fluxengine merge -s <fluxfile> -s <fluxfile...> -d <fluxfile`
|
||||||
|
|
||||||
|
Merges data from multiple flux files together. This is useful if you have
|
||||||
|
several reads from an unreliable disk where each read has a different set
|
||||||
|
of good sectors. By merging the flux files, you get to combine all the
|
||||||
|
data. Don't use this on reads of different disks, for obvious results! Note
|
||||||
|
that this works on flux files, not on flux sources.
|
||||||
|
|
||||||
- `fluxengine inspect -s <flux source> -c <cylinder> -h <head> -B`
|
- `fluxengine inspect -s <flux source> -c <cylinder> -h <head> -B`
|
||||||
|
|
||||||
Reads flux (possibly from a disk) and does various analyses of it to try
|
Reads flux (possibly from a disk) and does various analyses of it to try
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ LIBFLUXENGINE_SRCS = \
|
|||||||
lib/decoders/fmmfm.cc \
|
lib/decoders/fmmfm.cc \
|
||||||
lib/encoders/encoders.cc \
|
lib/encoders/encoders.cc \
|
||||||
lib/environment.cc \
|
lib/environment.cc \
|
||||||
|
lib/fl2.cc \
|
||||||
lib/flags.cc \
|
lib/flags.cc \
|
||||||
lib/fluxmap.cc \
|
lib/fluxmap.cc \
|
||||||
lib/fluxsink/a2rfluxsink.cc \
|
lib/fluxsink/a2rfluxsink.cc \
|
||||||
@@ -78,6 +79,7 @@ LIBFLUXENGINE_SRCS = \
|
|||||||
lib/vfs/machfs.cc \
|
lib/vfs/machfs.cc \
|
||||||
lib/vfs/prodos.cc \
|
lib/vfs/prodos.cc \
|
||||||
lib/vfs/smaky6fs.cc \
|
lib/vfs/smaky6fs.cc \
|
||||||
|
lib/vfs/philefs.cc \
|
||||||
lib/vfs/vfs.cc \
|
lib/vfs/vfs.cc \
|
||||||
lib/vfs/fluxsectorinterface.cc \
|
lib/vfs/fluxsectorinterface.cc \
|
||||||
lib/vfs/imagesectorinterface.cc \
|
lib/vfs/imagesectorinterface.cc \
|
||||||
|
|||||||
@@ -35,6 +35,15 @@ message DriveProto
|
|||||||
optional int32 tpi = 11 [ default = 96, (help) = "TPI of drive" ];
|
optional int32 tpi = 11 [ default = 96, (help) = "TPI of drive" ];
|
||||||
optional double rotational_period_ms = 12
|
optional double rotational_period_ms = 12
|
||||||
[ default = 0, (help) = "Rotational period of the drive in milliseconds (0 to autodetect)"];
|
[ default = 0, (help) = "Rotational period of the drive in milliseconds (0 to autodetect)"];
|
||||||
|
|
||||||
|
enum ErrorBehaviour {
|
||||||
|
NOTHING = 0;
|
||||||
|
JIGGLE = 1;
|
||||||
|
RECALIBRATE = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
optional ErrorBehaviour error_behaviour = 15
|
||||||
|
[ default = JIGGLE, (help) = "what to do when an error occurs during reads" ];
|
||||||
}
|
}
|
||||||
|
|
||||||
// vim: ts=4 sw=4 et
|
// vim: ts=4 sw=4 et
|
||||||
|
|||||||
69
lib/fl2.cc
Normal file
69
lib/fl2.cc
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
#include "globals.h"
|
||||||
|
#include "proto.h"
|
||||||
|
#include "fluxmap.h"
|
||||||
|
#include "fmt/format.h"
|
||||||
|
#include "lib/fl2.pb.h"
|
||||||
|
#include <fstream>
|
||||||
|
|
||||||
|
static void upgradeFluxFile(FluxFileProto& proto)
|
||||||
|
{
|
||||||
|
if (proto.version() == FluxFileVersion::VERSION_1)
|
||||||
|
{
|
||||||
|
/* Change a flux datastream with multiple segments separated by F_DESYNC
|
||||||
|
* into multiple flux segments. */
|
||||||
|
|
||||||
|
for (auto& track : *proto.mutable_track())
|
||||||
|
{
|
||||||
|
if (track.flux_size() != 0)
|
||||||
|
{
|
||||||
|
Fluxmap oldFlux(track.flux(0));
|
||||||
|
|
||||||
|
track.clear_flux();
|
||||||
|
for (const auto& flux : oldFlux.split())
|
||||||
|
track.add_flux(flux->rawBytes());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
proto.set_version(FluxFileVersion::VERSION_2);
|
||||||
|
}
|
||||||
|
if (proto.version() > FluxFileVersion::VERSION_2)
|
||||||
|
Error() << fmt::format(
|
||||||
|
"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);
|
||||||
|
}
|
||||||
|
|
||||||
|
FluxFileProto loadFl2File(const std::string filename)
|
||||||
|
{
|
||||||
|
std::ifstream ifs(filename, std::ios::in | std::ios::binary);
|
||||||
|
if (!ifs.is_open())
|
||||||
|
Error() << fmt::format(
|
||||||
|
"cannot open input file '{}': {}", filename, strerror(errno));
|
||||||
|
|
||||||
|
char buffer[16];
|
||||||
|
ifs.read(buffer, sizeof(buffer));
|
||||||
|
if (strncmp(buffer, "SQLite format 3", 16) == 0)
|
||||||
|
Error() << "this flux file is too old; please use the "
|
||||||
|
"upgrade-flux-file tool to upgrade it";
|
||||||
|
|
||||||
|
FluxFileProto proto;
|
||||||
|
ifs.seekg(0);
|
||||||
|
if (!proto.ParseFromIstream(&ifs))
|
||||||
|
Error() << fmt::format("unable to read input file '{}'", filename);
|
||||||
|
upgradeFluxFile(proto);
|
||||||
|
return proto;
|
||||||
|
}
|
||||||
|
|
||||||
|
void saveFl2File(const std::string filename, FluxFileProto& proto)
|
||||||
|
{
|
||||||
|
proto.set_magic(FluxMagic::MAGIC);
|
||||||
|
proto.set_version(FluxFileVersion::VERSION_2);
|
||||||
|
|
||||||
|
std::ofstream of(filename, std::ios::out | std::ios::binary);
|
||||||
|
if (!proto.SerializeToOstream(&of))
|
||||||
|
Error() << fmt::format("unable to write output file '{}'", filename);
|
||||||
|
of.close();
|
||||||
|
if (of.fail())
|
||||||
|
Error() << "FL2 write I/O error: " << strerror(errno);
|
||||||
|
}
|
||||||
10
lib/fl2.h
Normal file
10
lib/fl2.h
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
#ifndef FL2_H
|
||||||
|
#define FL2_H
|
||||||
|
|
||||||
|
class FluxFileProto;
|
||||||
|
|
||||||
|
extern FluxFileProto loadFl2File(const std::string filename);
|
||||||
|
extern void saveFl2File(const std::string filename, FluxFileProto& proto);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
@@ -9,73 +9,69 @@
|
|||||||
#include "proto.h"
|
#include "proto.h"
|
||||||
#include "fmt/format.h"
|
#include "fmt/format.h"
|
||||||
#include "lib/fl2.pb.h"
|
#include "lib/fl2.pb.h"
|
||||||
|
#include "fl2.h"
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
|
#include <filesystem>
|
||||||
|
|
||||||
class Fl2FluxSink : public FluxSink
|
class Fl2FluxSink : public FluxSink
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Fl2FluxSink(const Fl2FluxSinkProto& lconfig):
|
Fl2FluxSink(const Fl2FluxSinkProto& lconfig):
|
||||||
Fl2FluxSink(lconfig.filename())
|
Fl2FluxSink(lconfig.filename())
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
Fl2FluxSink(const std::string& filename):
|
Fl2FluxSink(const std::string& filename): _filename(filename)
|
||||||
_filename(filename),
|
{
|
||||||
_of(_filename, std::ios::out | std::ios::binary)
|
std::ofstream of(filename);
|
||||||
{
|
if (!of.is_open())
|
||||||
if (!_of.is_open())
|
Error() << "cannot open output file";
|
||||||
Error() << "cannot open output file";
|
of.close();
|
||||||
}
|
std::filesystem::remove(filename);
|
||||||
|
}
|
||||||
|
|
||||||
~Fl2FluxSink()
|
~Fl2FluxSink()
|
||||||
{
|
{
|
||||||
FluxFileProto proto;
|
FluxFileProto proto;
|
||||||
proto.set_magic(FluxMagic::MAGIC);
|
for (const auto& e : _data)
|
||||||
proto.set_version(FluxFileVersion::VERSION_2);
|
{
|
||||||
for (const auto& e : _data)
|
auto track = proto.add_track();
|
||||||
{
|
track->set_track(e.first.first);
|
||||||
auto track = proto.add_track();
|
track->set_head(e.first.second);
|
||||||
track->set_track(e.first.first);
|
for (const auto& fluxBytes : e.second)
|
||||||
track->set_head(e.first.second);
|
track->add_flux(fluxBytes);
|
||||||
for (const auto& fluxBytes : e.second)
|
}
|
||||||
track->add_flux(fluxBytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!proto.SerializeToOstream(&_of))
|
saveFl2File(_filename, proto);
|
||||||
Error() << "unable to write output file";
|
}
|
||||||
_of.close();
|
|
||||||
if (_of.fail())
|
|
||||||
Error() << "FL2 write I/O error: " << strerror(errno);
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
void writeFlux(int track, int head, const Fluxmap& fluxmap) override
|
void writeFlux(int track, int head, const Fluxmap& fluxmap) override
|
||||||
{
|
{
|
||||||
auto& vector = _data[std::make_pair(track, head)];
|
auto& vector = _data[std::make_pair(track, head)];
|
||||||
vector.push_back(fluxmap.rawBytes());
|
vector.push_back(fluxmap.rawBytes());
|
||||||
}
|
}
|
||||||
|
|
||||||
operator std::string () const override
|
operator std::string() const override
|
||||||
{
|
{
|
||||||
return fmt::format("fl2({})", _filename);
|
return fmt::format("fl2({})", _filename);
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::string _filename;
|
std::string _filename;
|
||||||
std::ofstream _of;
|
std::map<std::pair<unsigned, unsigned>, std::vector<Bytes>> _data;
|
||||||
std::map<std::pair<unsigned, unsigned>, std::vector<Bytes>> _data;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
std::unique_ptr<FluxSink> FluxSink::createFl2FluxSink(const Fl2FluxSinkProto& config)
|
std::unique_ptr<FluxSink> FluxSink::createFl2FluxSink(
|
||||||
|
const Fl2FluxSinkProto& config)
|
||||||
{
|
{
|
||||||
return std::unique_ptr<FluxSink>(new Fl2FluxSink(config));
|
return std::unique_ptr<FluxSink>(new Fl2FluxSink(config));
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<FluxSink> FluxSink::createFl2FluxSink(const std::string& filename)
|
std::unique_ptr<FluxSink> FluxSink::createFl2FluxSink(
|
||||||
|
const std::string& filename)
|
||||||
{
|
{
|
||||||
return std::unique_ptr<FluxSink>(new Fl2FluxSink(filename));
|
return std::unique_ptr<FluxSink>(new Fl2FluxSink(filename));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
#include "lib/fl2.pb.h"
|
#include "lib/fl2.pb.h"
|
||||||
#include "fluxsource/fluxsource.h"
|
#include "fluxsource/fluxsource.h"
|
||||||
#include "proto.h"
|
#include "proto.h"
|
||||||
|
#include "fl2.h"
|
||||||
#include "fmt/format.h"
|
#include "fmt/format.h"
|
||||||
#include "fluxmap.h"
|
#include "fluxmap.h"
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
@@ -11,38 +12,36 @@
|
|||||||
class Fl2FluxSourceIterator : public FluxSourceIterator
|
class Fl2FluxSourceIterator : public FluxSourceIterator
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Fl2FluxSourceIterator(const TrackFluxProto& proto):
|
Fl2FluxSourceIterator(const TrackFluxProto& proto): _proto(proto) {}
|
||||||
_proto(proto)
|
|
||||||
{}
|
|
||||||
|
|
||||||
bool hasNext() const override
|
bool hasNext() const override
|
||||||
{
|
{
|
||||||
return _count < _proto.flux_size();
|
return _count < _proto.flux_size();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<const Fluxmap> next() override
|
std::unique_ptr<const Fluxmap> next() override
|
||||||
{
|
{
|
||||||
auto bytes = _proto.flux(_count);
|
auto bytes = _proto.flux(_count);
|
||||||
_count++;
|
_count++;
|
||||||
return std::make_unique<Fluxmap>(bytes);
|
return std::make_unique<Fluxmap>(bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const TrackFluxProto& _proto;
|
const TrackFluxProto& _proto;
|
||||||
int _count = 0;
|
int _count = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
class EmptyFluxSourceIterator : public FluxSourceIterator
|
class EmptyFluxSourceIterator : public FluxSourceIterator
|
||||||
{
|
{
|
||||||
bool hasNext() const override
|
bool hasNext() const override
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<const Fluxmap> next() override
|
std::unique_ptr<const Fluxmap> next() override
|
||||||
{
|
{
|
||||||
Error() << "no flux to read";
|
Error() << "no flux to read";
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
class Fl2FluxSource : public FluxSource
|
class Fl2FluxSource : public FluxSource
|
||||||
@@ -50,15 +49,7 @@ class Fl2FluxSource : public FluxSource
|
|||||||
public:
|
public:
|
||||||
Fl2FluxSource(const Fl2FluxSourceProto& config): _config(config)
|
Fl2FluxSource(const Fl2FluxSourceProto& config): _config(config)
|
||||||
{
|
{
|
||||||
std::ifstream ifs(_config.filename(), std::ios::in | std::ios::binary);
|
_proto = loadFl2File(_config.filename());
|
||||||
if (!ifs.is_open())
|
|
||||||
Error() << fmt::format("cannot open input file '{}': {}",
|
|
||||||
_config.filename(),
|
|
||||||
strerror(errno));
|
|
||||||
|
|
||||||
if (!_proto.ParseFromIstream(&ifs))
|
|
||||||
Error() << "unable to read input file";
|
|
||||||
upgradeFluxFile();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
@@ -67,7 +58,7 @@ public:
|
|||||||
for (const auto& trackFlux : _proto.track())
|
for (const auto& trackFlux : _proto.track())
|
||||||
{
|
{
|
||||||
if ((trackFlux.track() == track) && (trackFlux.head() == head))
|
if ((trackFlux.track() == track) && (trackFlux.head() == head))
|
||||||
return std::make_unique<Fl2FluxSourceIterator>(trackFlux);
|
return std::make_unique<Fl2FluxSourceIterator>(trackFlux);
|
||||||
}
|
}
|
||||||
|
|
||||||
return std::make_unique<EmptyFluxSourceIterator>();
|
return std::make_unique<EmptyFluxSourceIterator>();
|
||||||
@@ -82,31 +73,6 @@ private:
|
|||||||
Error() << fmt::format("FL2 read I/O error: {}", strerror(errno));
|
Error() << fmt::format("FL2 read I/O error: {}", strerror(errno));
|
||||||
}
|
}
|
||||||
|
|
||||||
void upgradeFluxFile()
|
|
||||||
{
|
|
||||||
if (_proto.version() == FluxFileVersion::VERSION_1)
|
|
||||||
{
|
|
||||||
/* Change a flux datastream with multiple segments separated by F_DESYNC into multiple
|
|
||||||
* flux segments. */
|
|
||||||
|
|
||||||
for (auto& track : *_proto.mutable_track())
|
|
||||||
{
|
|
||||||
if (track.flux_size() != 0)
|
|
||||||
{
|
|
||||||
Fluxmap oldFlux(track.flux(0));
|
|
||||||
|
|
||||||
track.clear_flux();
|
|
||||||
for (const auto& flux : oldFlux.split())
|
|
||||||
track.add_flux(flux->rawBytes());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_proto.set_version(FluxFileVersion::VERSION_2);
|
|
||||||
}
|
|
||||||
if (_proto.version() > FluxFileVersion::VERSION_2)
|
|
||||||
Error() << fmt::format("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);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const Fl2FluxSourceProto& _config;
|
const Fl2FluxSourceProto& _config;
|
||||||
FluxFileProto _proto;
|
FluxFileProto _proto;
|
||||||
@@ -115,12 +81,5 @@ private:
|
|||||||
std::unique_ptr<FluxSource> FluxSource::createFl2FluxSource(
|
std::unique_ptr<FluxSource> FluxSource::createFl2FluxSource(
|
||||||
const Fl2FluxSourceProto& config)
|
const Fl2FluxSourceProto& config)
|
||||||
{
|
{
|
||||||
char buffer[16];
|
|
||||||
std::ifstream(config.filename(), std::ios::in | std::ios::binary)
|
|
||||||
.read(buffer, 16);
|
|
||||||
if (strncmp(buffer, "SQLite format 3", 16) == 0)
|
|
||||||
Error() << "this flux file is too old; please use the "
|
|
||||||
"upgrade-flux-file tool to upgrade it";
|
|
||||||
|
|
||||||
return std::unique_ptr<FluxSource>(new Fl2FluxSource(config));
|
return std::unique_ptr<FluxSource>(new Fl2FluxSource(config));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,10 +19,10 @@ class FlxFluxSourceProto;
|
|||||||
class FluxSourceIterator
|
class FluxSourceIterator
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
virtual ~FluxSourceIterator() {}
|
virtual ~FluxSourceIterator() {}
|
||||||
|
|
||||||
virtual bool hasNext() const = 0;
|
virtual bool hasNext() const = 0;
|
||||||
virtual std::unique_ptr<const Fluxmap> next() = 0;
|
virtual std::unique_ptr<const Fluxmap> next() = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
class FluxSource
|
class FluxSource
|
||||||
@@ -31,33 +31,48 @@ public:
|
|||||||
virtual ~FluxSource() {}
|
virtual ~FluxSource() {}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static std::unique_ptr<FluxSource> createCwfFluxSource(const CwfFluxSourceProto& config);
|
static std::unique_ptr<FluxSource> createCwfFluxSource(
|
||||||
static std::unique_ptr<FluxSource> createEraseFluxSource(const EraseFluxSourceProto& config);
|
const CwfFluxSourceProto& config);
|
||||||
static std::unique_ptr<FluxSource> createFl2FluxSource(const Fl2FluxSourceProto& config);
|
static std::unique_ptr<FluxSource> createEraseFluxSource(
|
||||||
static std::unique_ptr<FluxSource> createFlxFluxSource(const FlxFluxSourceProto& config);
|
const EraseFluxSourceProto& config);
|
||||||
static std::unique_ptr<FluxSource> createHardwareFluxSource(const HardwareFluxSourceProto& config);
|
static std::unique_ptr<FluxSource> createFl2FluxSource(
|
||||||
static std::unique_ptr<FluxSource> createKryofluxFluxSource(const KryofluxFluxSourceProto& config);
|
const Fl2FluxSourceProto& config);
|
||||||
static std::unique_ptr<FluxSource> createScpFluxSource(const ScpFluxSourceProto& config);
|
static std::unique_ptr<FluxSource> createFlxFluxSource(
|
||||||
static std::unique_ptr<FluxSource> createTestPatternFluxSource(const TestPatternFluxSourceProto& config);
|
const FlxFluxSourceProto& config);
|
||||||
|
static std::unique_ptr<FluxSource> createHardwareFluxSource(
|
||||||
|
const HardwareFluxSourceProto& config);
|
||||||
|
static std::unique_ptr<FluxSource> createKryofluxFluxSource(
|
||||||
|
const KryofluxFluxSourceProto& config);
|
||||||
|
static std::unique_ptr<FluxSource> createScpFluxSource(
|
||||||
|
const ScpFluxSourceProto& config);
|
||||||
|
static std::unique_ptr<FluxSource> createTestPatternFluxSource(
|
||||||
|
const TestPatternFluxSourceProto& config);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static std::unique_ptr<FluxSource> createMemoryFluxSource(const DiskFlux& flux);
|
static std::unique_ptr<FluxSource> createMemoryFluxSource(
|
||||||
|
const DiskFlux& flux);
|
||||||
|
|
||||||
static std::unique_ptr<FluxSource> create(const FluxSourceProto& spec);
|
static std::unique_ptr<FluxSource> create(const FluxSourceProto& spec);
|
||||||
static void updateConfigForFilename(FluxSourceProto* proto, const std::string& filename);
|
static void updateConfigForFilename(
|
||||||
|
FluxSourceProto* proto, const std::string& filename);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
virtual std::unique_ptr<FluxSourceIterator> readFlux(int track, int side) = 0;
|
virtual std::unique_ptr<FluxSourceIterator> readFlux(
|
||||||
|
int track, int side) = 0;
|
||||||
virtual void recalibrate() {}
|
virtual void recalibrate() {}
|
||||||
virtual bool isHardware() { return false; }
|
virtual void seek(int track) {}
|
||||||
|
virtual bool isHardware()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
class TrivialFluxSource : public FluxSource
|
class TrivialFluxSource : public FluxSource
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
std::unique_ptr<FluxSourceIterator> readFlux(int track, int side);
|
std::unique_ptr<FluxSourceIterator> readFlux(int track, int side);
|
||||||
virtual std::unique_ptr<const Fluxmap> readSingleFlux(int track, int side) = 0;
|
virtual std::unique_ptr<const Fluxmap> readSingleFlux(
|
||||||
|
int track, int side) = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|||||||
@@ -30,7 +30,9 @@ private:
|
|||||||
|
|
||||||
std::unique_ptr<const Fluxmap> next()
|
std::unique_ptr<const Fluxmap> next()
|
||||||
{
|
{
|
||||||
usbSetDrive(config.drive().drive(), config.drive().high_density(), config.drive().index_mode());
|
usbSetDrive(config.drive().drive(),
|
||||||
|
config.drive().high_density(),
|
||||||
|
config.drive().index_mode());
|
||||||
usbSeek(_track);
|
usbSeek(_track);
|
||||||
|
|
||||||
Bytes data = usbRead(_head,
|
Bytes data = usbRead(_head,
|
||||||
@@ -51,7 +53,7 @@ private:
|
|||||||
public:
|
public:
|
||||||
HardwareFluxSource(const HardwareFluxSourceProto& conf): _config(conf)
|
HardwareFluxSource(const HardwareFluxSourceProto& conf): _config(conf)
|
||||||
{
|
{
|
||||||
measureDiskRotation(_oneRevolution, _hardSectorThreshold);
|
measureDiskRotation(_oneRevolution, _hardSectorThreshold);
|
||||||
}
|
}
|
||||||
|
|
||||||
~HardwareFluxSource() {}
|
~HardwareFluxSource() {}
|
||||||
@@ -59,8 +61,7 @@ public:
|
|||||||
public:
|
public:
|
||||||
std::unique_ptr<FluxSourceIterator> readFlux(int track, int head) override
|
std::unique_ptr<FluxSourceIterator> readFlux(int track, int head) override
|
||||||
{
|
{
|
||||||
return std::make_unique<HardwareFluxSourceIterator>(
|
return std::make_unique<HardwareFluxSourceIterator>(*this, track, head);
|
||||||
*this, track, head);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void recalibrate() override
|
void recalibrate() override
|
||||||
@@ -68,6 +69,11 @@ public:
|
|||||||
usbRecalibrate();
|
usbRecalibrate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void seek(int track) override
|
||||||
|
{
|
||||||
|
usbSeek(track);
|
||||||
|
}
|
||||||
|
|
||||||
bool isHardware() override
|
bool isHardware() override
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -189,6 +189,26 @@ BadSectorsState combineRecordAndSectors(TrackFlux& trackFlux,
|
|||||||
return HAS_NO_BAD_SECTORS;
|
return HAS_NO_BAD_SECTORS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void adjustTrackOnError(FluxSource& fluxSource, int baseTrack)
|
||||||
|
{
|
||||||
|
switch (config.drive().error_behaviour())
|
||||||
|
{
|
||||||
|
case DriveProto::NOTHING:
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DriveProto::RECALIBRATE:
|
||||||
|
fluxSource.recalibrate();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DriveProto::JIGGLE:
|
||||||
|
if (baseTrack > 0)
|
||||||
|
fluxSource.seek(baseTrack - 1);
|
||||||
|
else
|
||||||
|
fluxSource.seek(baseTrack + 1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ReadResult readGroup(FluxSourceIteratorHolder& fluxSourceIteratorHolder,
|
ReadResult readGroup(FluxSourceIteratorHolder& fluxSourceIteratorHolder,
|
||||||
std::shared_ptr<const TrackInfo>& trackInfo,
|
std::shared_ptr<const TrackInfo>& trackInfo,
|
||||||
TrackFlux& trackFlux,
|
TrackFlux& trackFlux,
|
||||||
@@ -343,6 +363,7 @@ void writeTracksAndVerify(FluxSink& fluxSink,
|
|||||||
|
|
||||||
if (result != GOOD_READ)
|
if (result != GOOD_READ)
|
||||||
{
|
{
|
||||||
|
adjustTrackOnError(fluxSource, trackInfo->physicalTrack);
|
||||||
Logger() << "bad read";
|
Logger() << "bad read";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -453,9 +474,13 @@ std::shared_ptr<TrackFlux> readAndDecodeTrack(FluxSource& fluxSource,
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
Logger() << fmt::format(
|
if (fluxSource.isHardware())
|
||||||
"retrying; {} retries remaining", retriesRemaining);
|
{
|
||||||
retriesRemaining--;
|
adjustTrackOnError(fluxSource, trackInfo->physicalTrack);
|
||||||
|
Logger() << fmt::format(
|
||||||
|
"retrying; {} retries remaining", retriesRemaining);
|
||||||
|
retriesRemaining--;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return trackFlux;
|
return trackFlux;
|
||||||
@@ -584,7 +609,8 @@ void rawReadDiskCommand(FluxSource& fluxsource, FluxSink& fluxsink)
|
|||||||
unsigned index = 0;
|
unsigned index = 0;
|
||||||
for (auto& trackInfo : locations)
|
for (auto& trackInfo : locations)
|
||||||
{
|
{
|
||||||
Logger() << OperationProgressLogMessage{index * 100 / (int)locations.size()};
|
Logger() << OperationProgressLogMessage{
|
||||||
|
index * 100 / (int)locations.size()};
|
||||||
index++;
|
index++;
|
||||||
|
|
||||||
testForEmergencyStop();
|
testForEmergencyStop();
|
||||||
|
|||||||
296
lib/vfs/philefs.cc
Normal file
296
lib/vfs/philefs.cc
Normal file
@@ -0,0 +1,296 @@
|
|||||||
|
#include "lib/globals.h"
|
||||||
|
#include "lib/vfs/vfs.h"
|
||||||
|
#include "lib/config.pb.h"
|
||||||
|
#include "lib/utils.h"
|
||||||
|
#include <fmt/format.h>
|
||||||
|
|
||||||
|
/* Root block:
|
||||||
|
*
|
||||||
|
* 00-0b volume name
|
||||||
|
* 0c 01
|
||||||
|
* 0d 2a
|
||||||
|
* 0e 79
|
||||||
|
* 0f 6d
|
||||||
|
* 10 07 0x07c10c19, creation timestamp
|
||||||
|
* 11 c1 ^
|
||||||
|
* 12 0c ^
|
||||||
|
* 13 19 ^
|
||||||
|
* 14 2f
|
||||||
|
* 15 00
|
||||||
|
* 16 00
|
||||||
|
* 17 18
|
||||||
|
* 18 03 0x320, number of blocks on the disk
|
||||||
|
* 19 20 ^
|
||||||
|
* 1a 00 0x0010, first data block?
|
||||||
|
* 1b 10 ^
|
||||||
|
* 1c 00 0x0010, address of bitmap in HCS
|
||||||
|
* 1d 10 ^
|
||||||
|
* 1e 00 0x0011, address of FLIST in HCS
|
||||||
|
* 1f 11 ^
|
||||||
|
* 20 00 0x0017, address of last block of FLIST?
|
||||||
|
* 21 17 ^
|
||||||
|
* 22 00
|
||||||
|
* 23 6b
|
||||||
|
* 24 00
|
||||||
|
* 25 20
|
||||||
|
*
|
||||||
|
* 14 files
|
||||||
|
* file id 3 is not used
|
||||||
|
* directory at 0xc00
|
||||||
|
* 0x4000, block 0x10, volume bitmap
|
||||||
|
* 0x4400, block 0x11, flist, 7 blocks long?
|
||||||
|
* file descriptors seem to be 64 bytes
|
||||||
|
*
|
||||||
|
* File descriptor, 64 bytes:
|
||||||
|
* 00 file type
|
||||||
|
* 0e+04 length in bytes
|
||||||
|
* 14... spans
|
||||||
|
* word: start block
|
||||||
|
* word: number of blocks
|
||||||
|
*
|
||||||
|
00000C00 00 01 42 49 54 4D 41 50 2E 53 59 53 00 00 00 00 ..BITMAP.SYS....
|
||||||
|
|
||||||
|
00008040 41 00 00 00 07 C1 0C 19 1E 00 00 18 00 02 00 00 A...............
|
||||||
|
00008050 04 00 00 01 00 10 00 01 00 00 00 00 00 00 00 00 ................
|
||||||
|
00008060 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
|
||||||
|
00008070 00 00 00 00 00 00 00 00 00 00 00 01 00 01 01 00 ................
|
||||||
|
|
||||||
|
00000C10 00 02 46 4C 49 53 54 2E 53 59 53 00 00 00 00 00 ..FLIST.SYS.....
|
||||||
|
|
||||||
|
00008080 41 00 00 00 07 C1 0C 19 1E 00 00 18 00 02 00 00 A...............
|
||||||
|
00008090 1C 00 00 01 00 11 00 07 00 00 00 00 00 00 00 00 ................
|
||||||
|
000080A0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
|
||||||
|
000080B0 00 00 00 00 00 00 00 00 00 00 00 07 00 07 01 00 ................
|
||||||
|
|
||||||
|
00000C20 00 04 53 4B 45 4C 00 00 00 00 00 00 00 00 00 00 ..SKEL..........
|
||||||
|
|
||||||
|
00008100 01 00 00 03 07 C1 0C 19 19 00 00 19 00 02 00 00 ................
|
||||||
|
00008110 55 00 00 01 00 20 00 16 00 00 00 00 00 00 00 00 U.... ..........
|
||||||
|
00008120 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
|
||||||
|
00008130 00 00 00 00 00 00 00 00 00 00 00 16 00 16 01 00 ................
|
||||||
|
|
||||||
|
|
||||||
|
00000C30 00 05 43 4F 44 45 00 00 00 00 00 00 00 00 00 00 ..CODE..........
|
||||||
|
|
||||||
|
00004540 01 00 00 03 07 C1 0C 19 26 00 00 1F 00 02 00 08 ........&.......
|
||||||
|
00004550 10 00 00 01 00 36 02 04 00 00 00 00 00 00 00 00 .....6..........
|
||||||
|
00004560 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
|
||||||
|
00004570 00 00 00 00 00 00 00 00 00 00 02 04 02 04 01 00 ................
|
||||||
|
|
||||||
|
00000C40 00 06 53 43 53 49 00 00 00 00 00 00 00 00 00 00 ..SCSI..........
|
||||||
|
00000C50 00 07 41 4F 46 00 00 00 00 00 00 00 00 00 00 00 ..AOF...........
|
||||||
|
00000C60 00 08 4D 43 46 00 00 00 00 00 00 00 00 00 00 00 ..MCF...........
|
||||||
|
00000C70 00 09 53 59 53 54 45 4D 2E 53 43 46 00 00 00 00 ..SYSTEM.SCF....
|
||||||
|
00000C80 00 0A 53 59 53 54 45 4D 2E 50 44 46 00 00 00 00 ..SYSTEM.PDF....
|
||||||
|
00000C90 00 0B 43 4C 54 31 2E 43 4C 54 00 00 00 00 00 00 ..CLT1.CLT......
|
||||||
|
00000CA0 00 0C 43 4C 54 32 2E 43 4C 54 00 00 00 00 00 00 ..CLT2.CLT......
|
||||||
|
00000CB0 00 0D 43 4C 54 33 2E 43 4C 54 00 00 00 00 00 00 ..CLT3.CLT......
|
||||||
|
00000CC0 00 0E 43 4C 54 34 2E 43 4C 54 00 00 00 00 00 00 ..CLT4.CLT......
|
||||||
|
00000CD0 00 0F 47 52 45 59 2E 43 4C 54 00 00 00 00 00 00 ..GREY.CLT......
|
||||||
|
*/
|
||||||
|
|
||||||
|
static void trimZeros(std::string s)
|
||||||
|
{
|
||||||
|
s.erase(std::remove(s.begin(), s.end(), 0), s.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
class PhileFilesystem : public Filesystem
|
||||||
|
{
|
||||||
|
struct Span
|
||||||
|
{
|
||||||
|
uint16_t startBlock;
|
||||||
|
uint16_t blockCount;
|
||||||
|
};
|
||||||
|
|
||||||
|
class PhileDirent : public Dirent
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
PhileDirent(
|
||||||
|
int fileno, const std::string& filename, const Bytes& filedes):
|
||||||
|
_fileno(fileno)
|
||||||
|
{
|
||||||
|
file_type = TYPE_FILE;
|
||||||
|
|
||||||
|
ByteReader br(filedes);
|
||||||
|
br.seek(0x0e);
|
||||||
|
length = br.read_be32();
|
||||||
|
|
||||||
|
{
|
||||||
|
std::stringstream ss;
|
||||||
|
ss << 'R';
|
||||||
|
if (filedes[0] & 0x40)
|
||||||
|
ss << 'S';
|
||||||
|
mode = ss.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
this->filename = filename;
|
||||||
|
path = {filename};
|
||||||
|
|
||||||
|
attributes[Filesystem::FILENAME] = filename;
|
||||||
|
attributes[Filesystem::LENGTH] = std::to_string(length);
|
||||||
|
attributes[Filesystem::FILE_TYPE] = "file";
|
||||||
|
attributes[Filesystem::MODE] = mode;
|
||||||
|
|
||||||
|
int spans = br.read_be16();
|
||||||
|
for (int i = 0; i < spans; i++)
|
||||||
|
{
|
||||||
|
Span span;
|
||||||
|
span.startBlock = br.read_be16();
|
||||||
|
span.blockCount = br.read_be16();
|
||||||
|
_spans.push_back(span);
|
||||||
|
}
|
||||||
|
|
||||||
|
attributes["phile.spans"] = std::to_string(spans);
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::vector<Span>& spans() const
|
||||||
|
{
|
||||||
|
return _spans;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
int _fileno;
|
||||||
|
std::vector<Span> _spans;
|
||||||
|
};
|
||||||
|
|
||||||
|
public:
|
||||||
|
PhileFilesystem(
|
||||||
|
const PhileProto& 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::string volumename = _rootBlock.reader().read(0x0c);
|
||||||
|
trimZeros(volumename);
|
||||||
|
|
||||||
|
std::map<std::string, std::string> attributes;
|
||||||
|
attributes[VOLUME_NAME] = volumename;
|
||||||
|
attributes[TOTAL_BLOCKS] = std::to_string(_totalBlocks);
|
||||||
|
attributes[USED_BLOCKS] = attributes[TOTAL_BLOCKS];
|
||||||
|
attributes[BLOCK_SIZE] = std::to_string(_config.block_size());
|
||||||
|
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.second);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
Bytes getFile(const Path& path) override
|
||||||
|
{
|
||||||
|
mount();
|
||||||
|
if (path.size() != 1)
|
||||||
|
throw BadPathException();
|
||||||
|
|
||||||
|
auto dirent = findFile(path.front());
|
||||||
|
|
||||||
|
Bytes data;
|
||||||
|
ByteWriter bw(data);
|
||||||
|
for (const auto& span : dirent->spans())
|
||||||
|
bw += getPsosBlock(span.startBlock, span.blockCount);
|
||||||
|
|
||||||
|
data.resize(dirent->length);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void mount()
|
||||||
|
{
|
||||||
|
_sectorSize = getLogicalSectorSize();
|
||||||
|
_blockSectors = _config.block_size() / _sectorSize;
|
||||||
|
|
||||||
|
_rootBlock = getPsosBlock(2, 1);
|
||||||
|
_bitmapBlockNumber = _rootBlock.reader().seek(0x1c).read_be16();
|
||||||
|
_filedesBlockNumber = _rootBlock.reader().seek(0x1e).read_be16();
|
||||||
|
_filedesLength =
|
||||||
|
_rootBlock.reader().seek(0x20).read_be16() - _filedesBlockNumber + 1;
|
||||||
|
_totalBlocks = _rootBlock.reader().seek(0x18).read_be16();
|
||||||
|
|
||||||
|
Bytes directoryBlock = getPsosBlock(3, 1);
|
||||||
|
Bytes filedesBlock = getPsosBlock(_filedesBlockNumber, _filedesLength);
|
||||||
|
|
||||||
|
_dirents.clear();
|
||||||
|
ByteReader br(directoryBlock);
|
||||||
|
ByteReader fr(filedesBlock);
|
||||||
|
while (!br.eof())
|
||||||
|
{
|
||||||
|
uint16_t fileno = br.read_be16();
|
||||||
|
std::string filename = br.read(14);
|
||||||
|
trimZeros(filename);
|
||||||
|
|
||||||
|
if (fileno)
|
||||||
|
{
|
||||||
|
fr.seek(fileno * 64);
|
||||||
|
Bytes filedes = fr.read(64);
|
||||||
|
auto dirent =
|
||||||
|
std::make_unique<PhileDirent>(fileno, filename, filedes);
|
||||||
|
_dirents[fileno] = std::move(dirent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<PhileDirent> findFile(const std::string filename)
|
||||||
|
{
|
||||||
|
for (const auto& dirent : _dirents)
|
||||||
|
{
|
||||||
|
if (dirent.second->filename == filename)
|
||||||
|
return dirent.second;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw FileNotFoundException();
|
||||||
|
}
|
||||||
|
|
||||||
|
Bytes getPsosBlock(uint32_t number, uint32_t count = 1)
|
||||||
|
{
|
||||||
|
unsigned sector = number * _blockSectors;
|
||||||
|
return getLogicalSector(sector, _blockSectors * count);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
const PhileProto& _config;
|
||||||
|
int _sectorSize;
|
||||||
|
int _blockSectors;
|
||||||
|
int _totalBlocks;
|
||||||
|
int _bitmapBlockNumber;
|
||||||
|
int _filedesBlockNumber;
|
||||||
|
int _filedesLength;
|
||||||
|
Bytes _rootBlock;
|
||||||
|
std::map<int, std::shared_ptr<PhileDirent>> _dirents;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::unique_ptr<Filesystem> Filesystem::createPhileFilesystem(
|
||||||
|
const FilesystemProto& config, std::shared_ptr<SectorInterface> sectors)
|
||||||
|
{
|
||||||
|
return std::make_unique<PhileFilesystem>(config.phile(), sectors);
|
||||||
|
}
|
||||||
@@ -209,6 +209,9 @@ std::unique_ptr<Filesystem> Filesystem::createFilesystem(
|
|||||||
case FilesystemProto::SMAKY6:
|
case FilesystemProto::SMAKY6:
|
||||||
return Filesystem::createSmaky6Filesystem(config, image);
|
return Filesystem::createSmaky6Filesystem(config, image);
|
||||||
|
|
||||||
|
case FilesystemProto::PHILE:
|
||||||
|
return Filesystem::createPhileFilesystem(config, image);
|
||||||
|
|
||||||
default:
|
default:
|
||||||
Error() << "no filesystem configured";
|
Error() << "no filesystem configured";
|
||||||
return std::unique_ptr<Filesystem>();
|
return std::unique_ptr<Filesystem>();
|
||||||
|
|||||||
@@ -252,6 +252,8 @@ public:
|
|||||||
const FilesystemProto& config, std::shared_ptr<SectorInterface> image);
|
const FilesystemProto& config, std::shared_ptr<SectorInterface> image);
|
||||||
static std::unique_ptr<Filesystem> createSmaky6Filesystem(
|
static std::unique_ptr<Filesystem> createSmaky6Filesystem(
|
||||||
const FilesystemProto& config, std::shared_ptr<SectorInterface> image);
|
const FilesystemProto& config, std::shared_ptr<SectorInterface> image);
|
||||||
|
static std::unique_ptr<Filesystem> createPhileFilesystem(
|
||||||
|
const FilesystemProto& config, std::shared_ptr<SectorInterface> image);
|
||||||
|
|
||||||
static std::unique_ptr<Filesystem> createFilesystem(
|
static std::unique_ptr<Filesystem> createFilesystem(
|
||||||
const FilesystemProto& config, std::shared_ptr<SectorInterface> image);
|
const FilesystemProto& config, std::shared_ptr<SectorInterface> image);
|
||||||
|
|||||||
@@ -68,7 +68,14 @@ message AppledosProto {
|
|||||||
|
|
||||||
message Smaky6FsProto {}
|
message Smaky6FsProto {}
|
||||||
|
|
||||||
// NEXT_TAG: 13
|
message PhileProto {
|
||||||
|
optional uint32 block_size = 1 [
|
||||||
|
default = 1024,
|
||||||
|
(help) = "Phile filesystem block size"
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
// NEXT_TAG: 14
|
||||||
message FilesystemProto
|
message FilesystemProto
|
||||||
{
|
{
|
||||||
enum FilesystemType {
|
enum FilesystemType {
|
||||||
@@ -81,8 +88,9 @@ message FilesystemProto
|
|||||||
MACHFS = 6;
|
MACHFS = 6;
|
||||||
CBMFS = 7;
|
CBMFS = 7;
|
||||||
PRODOS = 8;
|
PRODOS = 8;
|
||||||
SMAKY6 = 9;
|
SMAKY6 = 9;
|
||||||
APPLEDOS = 10;
|
APPLEDOS = 10;
|
||||||
|
PHILE = 11;
|
||||||
}
|
}
|
||||||
|
|
||||||
optional FilesystemType type = 10 [default = NOT_SET, (help) = "filesystem type"];
|
optional FilesystemType type = 10 [default = NOT_SET, (help) = "filesystem type"];
|
||||||
@@ -95,8 +103,9 @@ message FilesystemProto
|
|||||||
optional MacHfsProto machfs = 6;
|
optional MacHfsProto machfs = 6;
|
||||||
optional CbmfsProto cbmfs = 7;
|
optional CbmfsProto cbmfs = 7;
|
||||||
optional ProdosProto prodos = 8;
|
optional ProdosProto prodos = 8;
|
||||||
optional AppledosProto appledos = 12;
|
optional AppledosProto appledos = 12;
|
||||||
optional Smaky6FsProto smaky6 = 11;
|
optional Smaky6FsProto smaky6 = 11;
|
||||||
|
optional PhileProto phile = 13;
|
||||||
|
|
||||||
optional SectorListProto sector_order = 9 [(help) = "specify the filesystem order of sectors"];
|
optional SectorListProto sector_order = 9 [(help) = "specify the filesystem order of sectors"];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ FLUXENGINE_SRCS = \
|
|||||||
src/fe-getfileinfo.cc \
|
src/fe-getfileinfo.cc \
|
||||||
src/fe-inspect.cc \
|
src/fe-inspect.cc \
|
||||||
src/fe-ls.cc \
|
src/fe-ls.cc \
|
||||||
|
src/fe-merge.cc \
|
||||||
src/fe-mkdir.cc \
|
src/fe-mkdir.cc \
|
||||||
src/fe-mv.cc \
|
src/fe-mv.cc \
|
||||||
src/fe-rm.cc \
|
src/fe-rm.cc \
|
||||||
|
|||||||
65
src/fe-merge.cc
Normal file
65
src/fe-merge.cc
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
#include "globals.h"
|
||||||
|
#include "flags.h"
|
||||||
|
#include "fluxmap.h"
|
||||||
|
#include "sector.h"
|
||||||
|
#include "proto.h"
|
||||||
|
#include "flux.h"
|
||||||
|
#include "fl2.h"
|
||||||
|
#include "fl2.pb.h"
|
||||||
|
#include "fmt/format.h"
|
||||||
|
#include "fluxengine.h"
|
||||||
|
#include <fstream>
|
||||||
|
|
||||||
|
static FlagGroup flags;
|
||||||
|
|
||||||
|
static std::vector<std::string> inputFluxFiles;
|
||||||
|
|
||||||
|
static StringFlag sourceFlux({"-s", "--source"},
|
||||||
|
"flux file to read from (repeatable)",
|
||||||
|
"",
|
||||||
|
[](const auto& value)
|
||||||
|
{
|
||||||
|
inputFluxFiles.push_back(value);
|
||||||
|
});
|
||||||
|
|
||||||
|
static StringFlag destFlux(
|
||||||
|
{"-d", "--dest"}, "destination flux file to write to", "");
|
||||||
|
|
||||||
|
int mainMerge(int argc, const char* argv[])
|
||||||
|
{
|
||||||
|
flags.parseFlags(argc, argv);
|
||||||
|
|
||||||
|
if (inputFluxFiles.empty())
|
||||||
|
Error() << "you must specify at least one input flux file (with -s)";
|
||||||
|
if (destFlux.get() == "")
|
||||||
|
Error() << "you must specify an output flux file (with -d)";
|
||||||
|
|
||||||
|
std::map<std::pair<int, int>, TrackFluxProto> data;
|
||||||
|
for (const auto& s : inputFluxFiles)
|
||||||
|
{
|
||||||
|
fmt::print("Reading {}...\n", s);
|
||||||
|
FluxFileProto f = loadFl2File(s);
|
||||||
|
|
||||||
|
for (auto& trackflux : f.track())
|
||||||
|
{
|
||||||
|
auto key = std::make_pair(trackflux.track(), trackflux.head());
|
||||||
|
auto i = data.find(key);
|
||||||
|
if (i == data.end())
|
||||||
|
data[key] = trackflux;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (auto flux : trackflux.flux())
|
||||||
|
i->second.add_flux(flux);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FluxFileProto proto;
|
||||||
|
for (auto& i : data)
|
||||||
|
*proto.add_track() = i.second;
|
||||||
|
|
||||||
|
fmt::print("Writing {}...\n", destFlux.get());
|
||||||
|
saveFl2File(destFlux.get(), proto);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -12,6 +12,7 @@ extern command_cb mainGetFile;
|
|||||||
extern command_cb mainGetFileInfo;
|
extern command_cb mainGetFileInfo;
|
||||||
extern command_cb mainInspect;
|
extern command_cb mainInspect;
|
||||||
extern command_cb mainLs;
|
extern command_cb mainLs;
|
||||||
|
extern command_cb mainMerge;
|
||||||
extern command_cb mainMkDir;
|
extern command_cb mainMkDir;
|
||||||
extern command_cb mainMv;
|
extern command_cb mainMv;
|
||||||
extern command_cb mainPutFile;
|
extern command_cb mainPutFile;
|
||||||
@@ -44,6 +45,7 @@ static std::vector<Command> commands =
|
|||||||
{ "format", mainFormat, "Format a disk and make a file system on it.", },
|
{ "format", mainFormat, "Format a disk and make a file system on it.", },
|
||||||
{ "rawread", mainRawRead, "Reads raw flux from a disk. Warning: you can't use this to copy disks.", },
|
{ "rawread", mainRawRead, "Reads raw flux from a disk. Warning: you can't use this to copy disks.", },
|
||||||
{ "rawwrite", mainRawWrite, "Writes a flux file to a disk. Warning: you can't use this to copy disks.", },
|
{ "rawwrite", mainRawWrite, "Writes a flux file to a disk. Warning: you can't use this to copy disks.", },
|
||||||
|
{ "merge", mainMerge, "Merge together multiple flux files.", },
|
||||||
{ "getdiskinfo", mainGetDiskInfo, "Read volume metadata off a disk (or image).", },
|
{ "getdiskinfo", mainGetDiskInfo, "Read volume metadata off a disk (or image).", },
|
||||||
{ "ls", mainLs, "Show files on disk (or image).", },
|
{ "ls", mainLs, "Show files on disk (or image).", },
|
||||||
{ "mv", mainMv, "Rename a file on a disk (or image).", },
|
{ "mv", mainMv, "Rename a file on a disk (or image).", },
|
||||||
|
|||||||
@@ -63,6 +63,7 @@ FORMATS = \
|
|||||||
northstar175 \
|
northstar175 \
|
||||||
northstar350 \
|
northstar350 \
|
||||||
northstar87 \
|
northstar87 \
|
||||||
|
psos800 \
|
||||||
rx50 \
|
rx50 \
|
||||||
shugart_drive \
|
shugart_drive \
|
||||||
smaky6 \
|
smaky6 \
|
||||||
|
|||||||
58
src/formats/psos800.textpb
Normal file
58
src/formats/psos800.textpb
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
comment: 'pSOS generic 800kB DSDD with PHILE'
|
||||||
|
|
||||||
|
drive {
|
||||||
|
high_density: false
|
||||||
|
rotational_period_ms: 200
|
||||||
|
}
|
||||||
|
|
||||||
|
image_reader {
|
||||||
|
filename: "pme.img"
|
||||||
|
type: IMG
|
||||||
|
}
|
||||||
|
|
||||||
|
image_writer {
|
||||||
|
filename: "pme.img"
|
||||||
|
type: IMG
|
||||||
|
}
|
||||||
|
|
||||||
|
layout {
|
||||||
|
tracks: 80
|
||||||
|
sides: 2
|
||||||
|
order: HCS
|
||||||
|
swap_sides: true
|
||||||
|
layoutdata {
|
||||||
|
sector_size: 1024
|
||||||
|
physical {
|
||||||
|
sector: 1
|
||||||
|
sector: 2
|
||||||
|
sector: 3
|
||||||
|
sector: 4
|
||||||
|
sector: 5
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
encoder {
|
||||||
|
ibm {
|
||||||
|
trackdata {
|
||||||
|
target_rotational_period_ms: 200
|
||||||
|
target_clock_period_us: 4
|
||||||
|
gap0: 80
|
||||||
|
gap2: 22
|
||||||
|
gap3: 80
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
decoder {
|
||||||
|
ibm {
|
||||||
|
trackdata {
|
||||||
|
ignore_side_byte: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
filesystem {
|
||||||
|
type: PHILE
|
||||||
|
}
|
||||||
|
|
||||||
@@ -93,7 +93,7 @@ void FluxEngineApp::RunOnWorkerThread(std::function<void()> callback)
|
|||||||
std::cerr << "Cannot start new worker task as one is already running\n";
|
std::cerr << "Cannot start new worker task as one is already running\n";
|
||||||
_callback = callback;
|
_callback = callback;
|
||||||
|
|
||||||
if (GetThread())
|
if (GetThread() && GetThread()->IsRunning())
|
||||||
GetThread()->Wait();
|
GetThread()->Wait();
|
||||||
|
|
||||||
emergencyStop = false;
|
emergencyStop = false;
|
||||||
@@ -122,9 +122,16 @@ bool FluxEngineApp::IsWorkerThreadRunning() const
|
|||||||
|
|
||||||
void FluxEngineApp::OnExec(const ExecEvent& event)
|
void FluxEngineApp::OnExec(const ExecEvent& event)
|
||||||
{
|
{
|
||||||
event.RunCallback();
|
try
|
||||||
if (event.IsSynchronous())
|
{
|
||||||
execSemaphore.Post();
|
event.RunCallback();
|
||||||
|
if (event.IsSynchronous())
|
||||||
|
execSemaphore.Post();
|
||||||
|
}
|
||||||
|
catch (std::exception& e)
|
||||||
|
{
|
||||||
|
std::cerr << "Unhandled exception: " << e.what() << "\n";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void runOnUiThread(std::function<void()> callback)
|
void runOnUiThread(std::function<void()> callback)
|
||||||
|
|||||||
@@ -481,6 +481,9 @@ public:
|
|||||||
visualiser->Clear();
|
visualiser->Clear();
|
||||||
_filesystemModel->Clear(Path());
|
_filesystemModel->Clear(Path());
|
||||||
_currentDisk = nullptr;
|
_currentDisk = nullptr;
|
||||||
|
_filesystemCapabilities = 0;
|
||||||
|
_filesystemIsReadOnly = true;
|
||||||
|
_filesystemNeedsFlushing = false;
|
||||||
|
|
||||||
_state = STATE_BROWSING_WORKING;
|
_state = STATE_BROWSING_WORKING;
|
||||||
UpdateState();
|
UpdateState();
|
||||||
|
|||||||
Reference in New Issue
Block a user