mirror of
https://github.com/davidgiven/fluxengine.git
synced 2025-10-31 11:17:01 -07:00
Merge pull request #87 from davidgiven/write
Add support for pluggable input and output formats.
This commit is contained in:
@@ -3,6 +3,10 @@ streams
|
||||
.*\.flux
|
||||
.*\.img
|
||||
.*\.raw
|
||||
.*\.orig
|
||||
.vscode
|
||||
remote
|
||||
FluxEngine.cydsn/CortexM3
|
||||
FluxEngine.cydsn/Generated_Source
|
||||
FluxEngine.cydsn/codegentemp
|
||||
|
||||
|
||||
@@ -92,6 +92,8 @@ void AmigaDecoder::decodeSectorRecord()
|
||||
|
||||
uint32_t wanteddatachecksum = deinterleave(ptr, 4).reader().read_be32();
|
||||
uint32_t gotdatachecksum = checksum(rawbytes.slice(62, 1024));
|
||||
_sector->data = deinterleave(ptr, 512);
|
||||
|
||||
_sector->data.clear();
|
||||
_sector->data.writer().append(deinterleave(ptr, 512)).append(recoveryinfo);
|
||||
_sector->status = (gotdatachecksum == wanteddatachecksum) ? Sector::OK : Sector::BAD_CHECKSUM;
|
||||
}
|
||||
|
||||
@@ -180,5 +180,7 @@ void MacintoshDecoder::decodeDataRecord()
|
||||
inputbuffer[i] = decode_data_gcr(inputbuffer[i]);
|
||||
|
||||
_sector->status = Sector::BAD_CHECKSUM;
|
||||
_sector->data = decode_crazy_data(inputbuffer, _sector->status);
|
||||
Bytes userData = decode_crazy_data(inputbuffer, _sector->status);
|
||||
_sector->data.clear();
|
||||
_sector->data.writer().append(userData.slice(12, 512)).append(userData.slice(0, 12));
|
||||
}
|
||||
|
||||
@@ -23,6 +23,17 @@ You should end up with an `amiga.adf` which is 901120 bytes long (for a
|
||||
normal DD disk) --- it ought to be a perfectly normal ADF file which you can
|
||||
use in an emulator.
|
||||
|
||||
If you want the metadata as well, specify a 528 byte sector size for the
|
||||
output image:
|
||||
|
||||
```
|
||||
fluxengine read amiga -o amiga.adf:b=528
|
||||
```
|
||||
|
||||
You will end up with a 929280 byte long image which you probably _can't_ use
|
||||
in an emulator; each sector will contain the 512 bytes of user payload
|
||||
followed by the 16 bytes of metadata.
|
||||
|
||||
Useful references
|
||||
-----------------
|
||||
|
||||
|
||||
@@ -50,6 +50,10 @@ with holes where missing sectors should be. This was easiest. If anyone can
|
||||
suggest a better way, please [get in
|
||||
touch](https://github.com/davidgiven/fluxengine/issues/new).
|
||||
|
||||
The 12 bytes of metadata _follow_ the 512 bytes of user payload in the sector
|
||||
image. If you don't want it, specify a geometry in the output file with a
|
||||
512-byte sectore size like `-o mac.img:c=80:h=1:s=12:b=512`.
|
||||
|
||||
Useful references
|
||||
-----------------
|
||||
|
||||
|
||||
17
doc/using.md
17
doc/using.md
@@ -112,6 +112,23 @@ sensible for the command you're using.
|
||||
**Important note:** FluxEngine _always_ uses zero-based units (even if the
|
||||
*disk format says otherwise).
|
||||
|
||||
### Input and output specifiers
|
||||
|
||||
These use a very similar syntax to the source and destination specifiers
|
||||
(because they're based on the same microformat library!) but are used for
|
||||
input and output _images_: i.e. nicely lined up arrays of sectors which you
|
||||
can actually do something with.
|
||||
|
||||
Use `--input` (`-i`) or `--output` (`-o`) as appropriate to tell FluxEngine
|
||||
where you want to read from or write to. The actual format is autodetected
|
||||
based on the extension:
|
||||
|
||||
- `.img` or `.adf`: raw sector images in CHS order. Append
|
||||
`:c=80:h=2:s=9:b=512` to set the geometry; that specifies 80 cylinders, 2
|
||||
heads, 9 sectors, 512 bytes per sector. For output files (`--output`) the
|
||||
geometry will be autodetected if left unspecified. For input files you
|
||||
normally have to specify it.
|
||||
|
||||
### High density disks
|
||||
|
||||
High density disks use a different magnetic medium to low and double density
|
||||
|
||||
@@ -5,6 +5,11 @@
|
||||
#include <regex>
|
||||
#include <sstream>
|
||||
|
||||
MissingModifierException::MissingModifierException(const std::string& mod):
|
||||
mod(mod),
|
||||
std::runtime_error(fmt::format("missing mandatory modifier '{}'", mod))
|
||||
{}
|
||||
|
||||
std::vector<std::string> DataSpec::split(
|
||||
const std::string& s, const std::string& delimiter)
|
||||
{
|
||||
@@ -74,31 +79,108 @@ void DataSpec::set(const std::string& spec)
|
||||
filename = words[0];
|
||||
if (words.size() > 1)
|
||||
{
|
||||
locations.clear();
|
||||
|
||||
for (size_t i = 1; i < words.size(); i++)
|
||||
{
|
||||
auto mod = parseMod(words[i]);
|
||||
if ((mod.name != "t") && (mod.name != "s") && (mod.name != "d"))
|
||||
Error() << fmt::format("unknown data modifier '{}'", mod.name);
|
||||
modifiers[mod.name] = mod;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const auto& drives = modifiers["d"].data;
|
||||
const DataSpec::Modifier& DataSpec::at(const std::string& mod) const
|
||||
{
|
||||
try
|
||||
{
|
||||
return modifiers.at(mod);
|
||||
}
|
||||
catch (const std::out_of_range& e)
|
||||
{
|
||||
throw MissingModifierException(mod);
|
||||
}
|
||||
}
|
||||
|
||||
bool DataSpec::has(const std::string& mod) const
|
||||
{
|
||||
return modifiers.find(mod) != modifiers.end();
|
||||
}
|
||||
|
||||
FluxSpec::FluxSpec(const DataSpec& spec)
|
||||
{
|
||||
try
|
||||
{
|
||||
filename = spec.filename;
|
||||
|
||||
locations.clear();
|
||||
|
||||
const auto& drives = spec.at("d").data;
|
||||
if (drives.size() != 1)
|
||||
Error() << "you must specify exactly one drive";
|
||||
drive = *drives.begin();
|
||||
|
||||
const auto& tracks = modifiers["t"].data;
|
||||
const auto& sides = modifiers["s"].data;
|
||||
const auto& tracks = spec.at("t").data;
|
||||
const auto& sides = spec.at("s").data;
|
||||
for (auto track : tracks)
|
||||
{
|
||||
for (auto side : sides)
|
||||
locations.push_back({ drive, track, side });
|
||||
}
|
||||
|
||||
for (const auto& e : spec.modifiers)
|
||||
{
|
||||
const auto name = e.second.name;
|
||||
if ((name != "t") && (name != "s") && (name != "d"))
|
||||
Error() << fmt::format("unknown fluxspec modifier '{}'", name);
|
||||
}
|
||||
}
|
||||
catch (const MissingModifierException& e)
|
||||
{
|
||||
Error() << e.what() << " in fluxspec '" << spec << "'";
|
||||
}
|
||||
}
|
||||
|
||||
ImageSpec::ImageSpec(const DataSpec& spec)
|
||||
{
|
||||
try
|
||||
{
|
||||
filename = spec.filename;
|
||||
|
||||
if (!spec.has("c") && !spec.has("h") && !spec.has("s") && !spec.has("b"))
|
||||
{
|
||||
cylinders = heads = sectors = bytes = 0;
|
||||
initialised = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
cylinders = spec.at("c").only();
|
||||
heads = spec.at("h").only();
|
||||
sectors = spec.at("s").only();
|
||||
bytes = spec.at("b").only();
|
||||
initialised = true;
|
||||
}
|
||||
}
|
||||
catch (const MissingModifierException& e)
|
||||
{
|
||||
Error() << e.what() << " in imagespec '" << spec << "'";
|
||||
}
|
||||
|
||||
for (const auto& e : spec.modifiers)
|
||||
{
|
||||
const auto name = e.second.name;
|
||||
if ((name != "c") && (name != "h") && (name != "s") && (name != "b"))
|
||||
Error() << fmt::format("unknown fluxspec modifier '{}'", name);
|
||||
}
|
||||
}
|
||||
|
||||
ImageSpec::ImageSpec(const std::string filename,
|
||||
unsigned cylinders, unsigned heads, unsigned sectors, unsigned bytes):
|
||||
filename(filename),
|
||||
cylinders(cylinders),
|
||||
heads(heads),
|
||||
sectors(sectors),
|
||||
bytes(bytes),
|
||||
initialised(true)
|
||||
{}
|
||||
|
||||
DataSpec::operator std::string(void) const
|
||||
{
|
||||
std::stringstream ss;
|
||||
|
||||
@@ -1,8 +1,57 @@
|
||||
#ifndef DATASPEC_H
|
||||
#define DATASPEC_H
|
||||
|
||||
class MissingModifierException : public std::runtime_error
|
||||
{
|
||||
public:
|
||||
MissingModifierException(const std::string& mod);
|
||||
const std::string mod;
|
||||
};
|
||||
|
||||
class DataSpec
|
||||
{
|
||||
public:
|
||||
struct Modifier
|
||||
{
|
||||
std::string name;
|
||||
std::set<unsigned> data;
|
||||
std::string source;
|
||||
|
||||
bool operator == (const Modifier& other) const
|
||||
{ return (name == other.name) && (data == other.data); }
|
||||
|
||||
bool operator != (const Modifier& other) const
|
||||
{ return (name != other.name) || (data != other.data); }
|
||||
|
||||
unsigned only() const
|
||||
{
|
||||
if (data.size() != 1)
|
||||
Error() << "modifier " << name << " can only have one value";
|
||||
return *(data.begin());
|
||||
}
|
||||
};
|
||||
|
||||
public:
|
||||
static std::vector<std::string> split(
|
||||
const std::string& s, const std::string& delimiter);
|
||||
static Modifier parseMod(const std::string& spec);
|
||||
|
||||
public:
|
||||
DataSpec(const std::string& spec)
|
||||
{ set(spec); }
|
||||
|
||||
void set(const std::string& spec);
|
||||
operator std::string () const;
|
||||
|
||||
const Modifier& at(const std::string& mod) const;
|
||||
bool has(const std::string& mod) const;
|
||||
|
||||
std::string filename;
|
||||
std::map<std::string, Modifier> modifiers;
|
||||
};
|
||||
|
||||
class FluxSpec
|
||||
{
|
||||
public:
|
||||
struct Location
|
||||
{
|
||||
@@ -17,36 +66,29 @@ public:
|
||||
{ return (drive != other.drive) || (track != other.track) || (side != other.side); }
|
||||
};
|
||||
|
||||
struct Modifier
|
||||
{
|
||||
std::string name;
|
||||
std::set<unsigned> data;
|
||||
std::string source;
|
||||
|
||||
bool operator == (const Modifier& other) const
|
||||
{ return (name == other.name) && (data == other.data); }
|
||||
|
||||
bool operator != (const Modifier& other) const
|
||||
{ return (name != other.name) || (data != other.data); }
|
||||
};
|
||||
public:
|
||||
FluxSpec(const DataSpec& dataspec);
|
||||
|
||||
public:
|
||||
static std::vector<std::string> split(
|
||||
const std::string& s, const std::string& delimiter);
|
||||
static Modifier parseMod(const std::string& spec);
|
||||
|
||||
public:
|
||||
DataSpec(const std::string& spec)
|
||||
{ set(spec); }
|
||||
|
||||
void set(const std::string& spec);
|
||||
operator std::string () const;
|
||||
|
||||
std::string filename;
|
||||
std::map<std::string, Modifier> modifiers;
|
||||
std::vector<Location> locations;
|
||||
unsigned drive;
|
||||
unsigned revolutions;
|
||||
};
|
||||
|
||||
class ImageSpec
|
||||
{
|
||||
public:
|
||||
ImageSpec(const DataSpec& dataspec);
|
||||
ImageSpec(const std::string filename,
|
||||
unsigned cylinders, unsigned heads, unsigned sectors, unsigned bytes);
|
||||
|
||||
public:
|
||||
std::string filename;
|
||||
unsigned cylinders;
|
||||
unsigned heads;
|
||||
unsigned sectors;
|
||||
unsigned bytes;
|
||||
bool initialised : 1;
|
||||
};
|
||||
|
||||
static inline std::ostream& operator << (std::ostream& os, const DataSpec& dataSpec)
|
||||
|
||||
@@ -10,7 +10,7 @@ static bool ends_with(const std::string& value, const std::string& ending)
|
||||
return std::equal(ending.rbegin(), ending.rend(), value.rbegin());
|
||||
}
|
||||
|
||||
std::unique_ptr<FluxSink> FluxSink::create(const DataSpec& spec)
|
||||
std::unique_ptr<FluxSink> FluxSink::create(const FluxSpec& spec)
|
||||
{
|
||||
const auto& filename = spec.filename;
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
#define FLUXSINK_H
|
||||
|
||||
class Fluxmap;
|
||||
class DataSpec;
|
||||
class FluxSpec;
|
||||
|
||||
class FluxSink
|
||||
{
|
||||
@@ -14,7 +14,7 @@ private:
|
||||
static std::unique_ptr<FluxSink> createHardwareFluxSink(unsigned drive);
|
||||
|
||||
public:
|
||||
static std::unique_ptr<FluxSink> create(const DataSpec& spec);
|
||||
static std::unique_ptr<FluxSink> create(const FluxSpec& spec);
|
||||
|
||||
public:
|
||||
virtual void writeFlux(int track, int side, Fluxmap& fluxmap) = 0;
|
||||
|
||||
@@ -10,7 +10,7 @@ static bool ends_with(const std::string& value, const std::string& ending)
|
||||
return std::equal(ending.rbegin(), ending.rend(), value.rbegin());
|
||||
}
|
||||
|
||||
std::unique_ptr<FluxSource> FluxSource::create(const DataSpec& spec)
|
||||
std::unique_ptr<FluxSource> FluxSource::create(const FluxSpec& spec)
|
||||
{
|
||||
const auto& filename = spec.filename;
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
extern FlagGroup hardwareFluxSourceFlags;
|
||||
|
||||
class Fluxmap;
|
||||
class DataSpec;
|
||||
class FluxSpec;
|
||||
|
||||
class FluxSource
|
||||
{
|
||||
@@ -19,7 +19,7 @@ private:
|
||||
static std::unique_ptr<FluxSource> createStreamFluxSource(const std::string& path);
|
||||
|
||||
public:
|
||||
static std::unique_ptr<FluxSource> create(const DataSpec& spec);
|
||||
static std::unique_ptr<FluxSource> create(const FluxSpec& spec);
|
||||
|
||||
public:
|
||||
virtual std::unique_ptr<Fluxmap> readFlux(int track, int side) = 0;
|
||||
|
||||
147
lib/image.cc
147
lib/image.cc
@@ -1,150 +1,25 @@
|
||||
#include "globals.h"
|
||||
#include "image.h"
|
||||
#include "flags.h"
|
||||
#include "dataspec.h"
|
||||
#include "sector.h"
|
||||
#include "sectorset.h"
|
||||
#include "imagereader/imagereader.h"
|
||||
#include "imagewriter/imagewriter.h"
|
||||
#include "fmt/format.h"
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
|
||||
Geometry guessGeometry(const SectorSet& sectors)
|
||||
SectorSet readSectorsFromFile(const ImageSpec& spec)
|
||||
{
|
||||
Geometry g;
|
||||
sectors.calculateSize(g.tracks, g.heads, g.sectors, g.sectorSize);
|
||||
return g;
|
||||
return ImageReader::create(spec)->readImage();
|
||||
}
|
||||
|
||||
void readSectorsFromFile(SectorSet& sectors, const Geometry& geometry,
|
||||
const std::string& filename)
|
||||
void writeSectorsToFile(const SectorSet& sectors, const ImageSpec& spec)
|
||||
{
|
||||
std::ifstream inputFile(filename, std::ios::in | std::ios::binary);
|
||||
if (!inputFile.is_open())
|
||||
Error() << "cannot open input file";
|
||||
|
||||
size_t headSize = geometry.sectors * geometry.sectorSize;
|
||||
size_t trackSize = headSize * geometry.heads;
|
||||
|
||||
std::cout << fmt::format("{} tracks, {} heads, {} sectors, {} bytes per sector, {} kB total",
|
||||
geometry.tracks, geometry.heads,
|
||||
geometry.sectors, geometry.sectorSize,
|
||||
geometry.tracks * trackSize / 1024)
|
||||
<< std::endl;
|
||||
|
||||
for (int track = 0; track < geometry.tracks; track++)
|
||||
{
|
||||
for (int head = 0; head < geometry.heads; head++)
|
||||
{
|
||||
for (int sectorId = 0; sectorId < geometry.sectors; sectorId++)
|
||||
{
|
||||
inputFile.seekg(track*trackSize + head*headSize + sectorId*geometry.sectorSize, std::ios::beg);
|
||||
|
||||
Bytes data(geometry.sectorSize);
|
||||
inputFile.read((char*) data.begin(), geometry.sectorSize);
|
||||
|
||||
std::unique_ptr<Sector>& sector = sectors.get(track, head, sectorId);
|
||||
sector.reset(new Sector);
|
||||
sector->status = Sector::OK;
|
||||
sector->logicalTrack = sector->physicalTrack = track;
|
||||
sector->logicalSide = sector->physicalSide = head;
|
||||
sector->logicalSector = sectorId;
|
||||
sector->data = data;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void writeSectorsToFile(const SectorSet& sectors, const Geometry& geometry,
|
||||
const std::string& filename)
|
||||
{
|
||||
/* Emit the map. */
|
||||
|
||||
int badSectors = 0;
|
||||
int missingSectors = 0;
|
||||
int totalSectors = 0;
|
||||
std::cout << "H.SS Tracks --->" << std::endl;
|
||||
for (int head = 0; head < geometry.heads; head++)
|
||||
{
|
||||
for (int sectorId = 0; sectorId < geometry.sectors; sectorId++)
|
||||
{
|
||||
std::cout << fmt::format("{}.{:2} ", head, sectorId);
|
||||
for (int track = 0; track < geometry.tracks; track++)
|
||||
{
|
||||
Sector* sector = sectors.get(track, head, sectorId);
|
||||
if (!sector)
|
||||
{
|
||||
std::cout << 'X';
|
||||
missingSectors++;
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (sector->status)
|
||||
{
|
||||
case Sector::OK:
|
||||
std::cout << '.';
|
||||
break;
|
||||
|
||||
case Sector::BAD_CHECKSUM:
|
||||
std::cout << 'B';
|
||||
badSectors++;
|
||||
break;
|
||||
|
||||
case Sector::CONFLICT:
|
||||
std::cout << 'C';
|
||||
badSectors++;
|
||||
break;
|
||||
|
||||
default:
|
||||
std::cout << '?';
|
||||
break;
|
||||
}
|
||||
}
|
||||
totalSectors++;
|
||||
}
|
||||
std::cout << std::endl;
|
||||
}
|
||||
}
|
||||
int goodSectors = totalSectors - missingSectors - badSectors;
|
||||
if (totalSectors == 0)
|
||||
std::cout << "No sectors in output; skipping analysis" << std::endl;
|
||||
else
|
||||
{
|
||||
std::cout << "Good sectors: " << goodSectors << "/" << totalSectors
|
||||
<< " (" << (100*goodSectors/totalSectors) << "%)"
|
||||
<< std::endl;
|
||||
std::cout << "Missing sectors: " << missingSectors << "/" << totalSectors
|
||||
<< " (" << (100*missingSectors/totalSectors) << "%)"
|
||||
<< std::endl;
|
||||
std::cout << "Bad sectors: " << badSectors << "/" << totalSectors
|
||||
<< " (" << (100*badSectors/totalSectors) << "%)"
|
||||
<< std::endl;
|
||||
}
|
||||
|
||||
size_t headSize = geometry.sectors * geometry.sectorSize;
|
||||
size_t trackSize = headSize * geometry.heads;
|
||||
|
||||
std::cout << fmt::format("{} tracks, {} heads, {} sectors, {} bytes per sector, {} kB total",
|
||||
geometry.tracks, geometry.heads,
|
||||
geometry.sectors, geometry.sectorSize,
|
||||
geometry.tracks * trackSize / 1024)
|
||||
<< std::endl;
|
||||
|
||||
std::ofstream outputFile(filename, std::ios::out | std::ios::binary);
|
||||
if (!outputFile.is_open())
|
||||
Error() << "cannot open output file";
|
||||
|
||||
for (int track = 0; track < geometry.tracks; track++)
|
||||
{
|
||||
for (int head = 0; head < geometry.heads; head++)
|
||||
{
|
||||
for (int sectorId = 0; sectorId < geometry.sectors; sectorId++)
|
||||
{
|
||||
auto sector = sectors.get(track, head, sectorId);
|
||||
if (sector)
|
||||
{
|
||||
outputFile.seekp(sector->logicalTrack*trackSize + sector->logicalSide*headSize + sector->logicalSector*geometry.sectorSize, std::ios::beg);
|
||||
outputFile.write((const char*) sector->data.cbegin(), sector->data.size());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
std::unique_ptr<ImageWriter> writer(ImageWriter::create(sectors, spec));
|
||||
writer->adjustGeometry();
|
||||
writer->printMap();
|
||||
writer->writeImage();
|
||||
}
|
||||
|
||||
21
lib/image.h
21
lib/image.h
@@ -2,26 +2,13 @@
|
||||
#define IMAGE_H
|
||||
|
||||
class SectorSet;
|
||||
class ImageSpec;
|
||||
|
||||
class Geometry
|
||||
{
|
||||
public:
|
||||
int tracks;
|
||||
int heads;
|
||||
int sectors;
|
||||
int sectorSize;
|
||||
};
|
||||
|
||||
extern Geometry guessGeometry(const SectorSet& sectors);
|
||||
|
||||
extern void readSectorsFromFile(
|
||||
SectorSet& sectors,
|
||||
const Geometry& geometry,
|
||||
const std::string& filename);
|
||||
extern SectorSet readSectorsFromFile(
|
||||
const ImageSpec& filename);
|
||||
|
||||
extern void writeSectorsToFile(
|
||||
const SectorSet& sectors,
|
||||
const Geometry& geometry,
|
||||
const std::string& filename);
|
||||
const ImageSpec& filename);
|
||||
|
||||
#endif
|
||||
|
||||
31
lib/imagereader/imagereader.cc
Normal file
31
lib/imagereader/imagereader.cc
Normal file
@@ -0,0 +1,31 @@
|
||||
#include "globals.h"
|
||||
#include "image.h"
|
||||
#include "flags.h"
|
||||
#include "dataspec.h"
|
||||
#include "sector.h"
|
||||
#include "sectorset.h"
|
||||
#include "imagereader/imagereader.h"
|
||||
#include "fmt/format.h"
|
||||
|
||||
static bool ends_with(const std::string& value, const std::string& ending)
|
||||
{
|
||||
if (ending.size() > value.size())
|
||||
return false;
|
||||
return std::equal(ending.rbegin(), ending.rend(), value.rbegin());
|
||||
}
|
||||
|
||||
std::unique_ptr<ImageReader> ImageReader::create(const ImageSpec& spec)
|
||||
{
|
||||
const auto& filename = spec.filename;
|
||||
|
||||
if (ends_with(filename, ".img") || ends_with(filename, ".adf"))
|
||||
return createImgImageReader(spec);
|
||||
|
||||
Error() << "unrecognised image filename extension";
|
||||
return std::unique_ptr<ImageReader>();
|
||||
}
|
||||
|
||||
ImageReader::ImageReader(const ImageSpec& spec):
|
||||
spec(spec)
|
||||
{}
|
||||
|
||||
27
lib/imagereader/imagereader.h
Normal file
27
lib/imagereader/imagereader.h
Normal file
@@ -0,0 +1,27 @@
|
||||
#ifndef IMAGEREADER_H
|
||||
#define IMAGEREADER_H
|
||||
|
||||
class SectorSet;
|
||||
class ImageSpec;
|
||||
|
||||
class ImageReader
|
||||
{
|
||||
public:
|
||||
ImageReader(const ImageSpec& spec);
|
||||
virtual ~ImageReader() {};
|
||||
|
||||
public:
|
||||
static std::unique_ptr<ImageReader> create(const ImageSpec& spec);
|
||||
|
||||
private:
|
||||
static std::unique_ptr<ImageReader> createImgImageReader(const ImageSpec& spec);
|
||||
|
||||
public:
|
||||
virtual SectorSet readImage() = 0;
|
||||
|
||||
protected:
|
||||
ImageSpec spec;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
66
lib/imagereader/imgimagereader.cc
Normal file
66
lib/imagereader/imgimagereader.cc
Normal file
@@ -0,0 +1,66 @@
|
||||
#include "globals.h"
|
||||
#include "image.h"
|
||||
#include "flags.h"
|
||||
#include "dataspec.h"
|
||||
#include "sector.h"
|
||||
#include "sectorset.h"
|
||||
#include "imagereader/imagereader.h"
|
||||
#include "fmt/format.h"
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
|
||||
class ImgImageReader : public ImageReader
|
||||
{
|
||||
public:
|
||||
ImgImageReader(const ImageSpec& spec):
|
||||
ImageReader(spec)
|
||||
{}
|
||||
|
||||
SectorSet readImage()
|
||||
{
|
||||
std::ifstream inputFile(spec.filename, std::ios::in | std::ios::binary);
|
||||
if (!inputFile.is_open())
|
||||
Error() << "cannot open input file";
|
||||
|
||||
size_t headSize = spec.sectors * spec.bytes;
|
||||
size_t trackSize = headSize * spec.heads;
|
||||
|
||||
std::cout << fmt::format("reading {} tracks, {} heads, {} sectors, {} bytes per sector, {} kB total",
|
||||
spec.cylinders, spec.heads,
|
||||
spec.sectors, spec.bytes,
|
||||
spec.cylinders * trackSize / 1024)
|
||||
<< std::endl;
|
||||
|
||||
SectorSet sectors;
|
||||
for (int track = 0; track < spec.cylinders; track++)
|
||||
{
|
||||
for (int head = 0; head < spec.heads; head++)
|
||||
{
|
||||
for (int sectorId = 0; sectorId < spec.sectors; sectorId++)
|
||||
{
|
||||
inputFile.seekg(track*trackSize + head*headSize + sectorId*spec.bytes, std::ios::beg);
|
||||
|
||||
Bytes data(spec.bytes);
|
||||
inputFile.read((char*) data.begin(), spec.bytes);
|
||||
|
||||
std::unique_ptr<Sector>& sector = sectors.get(track, head, sectorId);
|
||||
sector.reset(new Sector);
|
||||
sector->status = Sector::OK;
|
||||
sector->logicalTrack = sector->physicalTrack = track;
|
||||
sector->logicalSide = sector->physicalSide = head;
|
||||
sector->logicalSector = sectorId;
|
||||
sector->data = data;
|
||||
}
|
||||
}
|
||||
}
|
||||
return sectors;
|
||||
}
|
||||
};
|
||||
|
||||
std::unique_ptr<ImageReader> ImageReader::createImgImageReader(
|
||||
const ImageSpec& spec)
|
||||
{
|
||||
return std::unique_ptr<ImageReader>(new ImgImageReader(spec));
|
||||
}
|
||||
|
||||
105
lib/imagewriter/imagewriter.cc
Normal file
105
lib/imagewriter/imagewriter.cc
Normal file
@@ -0,0 +1,105 @@
|
||||
#include "globals.h"
|
||||
#include "image.h"
|
||||
#include "flags.h"
|
||||
#include "dataspec.h"
|
||||
#include "sector.h"
|
||||
#include "sectorset.h"
|
||||
#include "imagewriter/imagewriter.h"
|
||||
#include "fmt/format.h"
|
||||
|
||||
static bool ends_with(const std::string& value, const std::string& ending)
|
||||
{
|
||||
if (ending.size() > value.size())
|
||||
return false;
|
||||
return std::equal(ending.rbegin(), ending.rend(), value.rbegin());
|
||||
}
|
||||
|
||||
std::unique_ptr<ImageWriter> ImageWriter::create(const SectorSet& sectors, const ImageSpec& spec)
|
||||
{
|
||||
const auto& filename = spec.filename;
|
||||
|
||||
if (ends_with(filename, ".img") || ends_with(filename, ".adf"))
|
||||
return createImgImageWriter(sectors, spec);
|
||||
|
||||
Error() << "unrecognised image filename extension";
|
||||
return std::unique_ptr<ImageWriter>();
|
||||
}
|
||||
|
||||
ImageWriter::ImageWriter(const SectorSet& sectors, const ImageSpec& spec):
|
||||
sectors(sectors),
|
||||
spec(spec)
|
||||
{}
|
||||
|
||||
void ImageWriter::adjustGeometry()
|
||||
{
|
||||
if (!spec.initialised)
|
||||
{
|
||||
sectors.calculateSize(spec.cylinders, spec.heads, spec.sectors, spec.bytes);
|
||||
spec.initialised = true;
|
||||
std::cout << "Autodetecting output geometry\n";
|
||||
}
|
||||
}
|
||||
|
||||
void ImageWriter::printMap()
|
||||
{
|
||||
int badSectors = 0;
|
||||
int missingSectors = 0;
|
||||
int totalSectors = 0;
|
||||
std::cout << "H.SS Tracks --->" << std::endl;
|
||||
for (int head = 0; head < spec.heads; head++)
|
||||
{
|
||||
for (int sectorId = 0; sectorId < spec.sectors; sectorId++)
|
||||
{
|
||||
std::cout << fmt::format("{}.{:2} ", head, sectorId);
|
||||
for (int track = 0; track < spec.cylinders; track++)
|
||||
{
|
||||
const auto& sector = sectors.get(track, head, sectorId);
|
||||
if (!sector)
|
||||
{
|
||||
std::cout << 'X';
|
||||
missingSectors++;
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (sector->status)
|
||||
{
|
||||
case Sector::OK:
|
||||
std::cout << '.';
|
||||
break;
|
||||
|
||||
case Sector::BAD_CHECKSUM:
|
||||
std::cout << 'B';
|
||||
badSectors++;
|
||||
break;
|
||||
|
||||
case Sector::CONFLICT:
|
||||
std::cout << 'C';
|
||||
badSectors++;
|
||||
break;
|
||||
|
||||
default:
|
||||
std::cout << '?';
|
||||
break;
|
||||
}
|
||||
}
|
||||
totalSectors++;
|
||||
}
|
||||
std::cout << std::endl;
|
||||
}
|
||||
}
|
||||
int goodSectors = totalSectors - missingSectors - badSectors;
|
||||
if (totalSectors == 0)
|
||||
std::cout << "No sectors in output; skipping analysis" << std::endl;
|
||||
else
|
||||
{
|
||||
std::cout << "Good sectors: " << goodSectors << "/" << totalSectors
|
||||
<< " (" << (100*goodSectors/totalSectors) << "%)"
|
||||
<< std::endl;
|
||||
std::cout << "Missing sectors: " << missingSectors << "/" << totalSectors
|
||||
<< " (" << (100*missingSectors/totalSectors) << "%)"
|
||||
<< std::endl;
|
||||
std::cout << "Bad sectors: " << badSectors << "/" << totalSectors
|
||||
<< " (" << (100*badSectors/totalSectors) << "%)"
|
||||
<< std::endl;
|
||||
}
|
||||
}
|
||||
30
lib/imagewriter/imagewriter.h
Normal file
30
lib/imagewriter/imagewriter.h
Normal file
@@ -0,0 +1,30 @@
|
||||
#ifndef IMAGEWRITER_H
|
||||
#define IMAGEWRITER_H
|
||||
|
||||
class SectorSet;
|
||||
class ImageSpec;
|
||||
|
||||
class ImageWriter
|
||||
{
|
||||
public:
|
||||
ImageWriter(const SectorSet& sectors, const ImageSpec& spec);
|
||||
virtual ~ImageWriter() {};
|
||||
|
||||
public:
|
||||
static std::unique_ptr<ImageWriter> create(const SectorSet& sectors, const ImageSpec& spec);
|
||||
|
||||
private:
|
||||
static std::unique_ptr<ImageWriter> createImgImageWriter(
|
||||
const SectorSet& sectors, const ImageSpec& spec);
|
||||
|
||||
public:
|
||||
virtual void adjustGeometry();
|
||||
void printMap();
|
||||
virtual void writeImage() = 0;
|
||||
|
||||
protected:
|
||||
const SectorSet& sectors;
|
||||
ImageSpec spec;
|
||||
};
|
||||
|
||||
#endif
|
||||
62
lib/imagewriter/imgimagewriter.cc
Normal file
62
lib/imagewriter/imgimagewriter.cc
Normal file
@@ -0,0 +1,62 @@
|
||||
#include "globals.h"
|
||||
#include "image.h"
|
||||
#include "flags.h"
|
||||
#include "dataspec.h"
|
||||
#include "sector.h"
|
||||
#include "sectorset.h"
|
||||
#include "imagewriter/imagewriter.h"
|
||||
#include "fmt/format.h"
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
|
||||
class ImgImageWriter : public ImageWriter
|
||||
{
|
||||
public:
|
||||
ImgImageWriter(const SectorSet& sectors, const ImageSpec& spec):
|
||||
ImageWriter(sectors, spec)
|
||||
{}
|
||||
|
||||
void writeImage()
|
||||
{
|
||||
unsigned numCylinders = spec.cylinders;
|
||||
unsigned numHeads = spec.heads;
|
||||
unsigned numSectors = spec.sectors;
|
||||
unsigned numBytes = spec.bytes;
|
||||
|
||||
size_t headSize = numSectors * numBytes;
|
||||
size_t trackSize = headSize * numHeads;
|
||||
|
||||
std::cout << fmt::format("writing {} tracks, {} heads, {} sectors, {} bytes per sector, {} kB total",
|
||||
numCylinders, numHeads,
|
||||
numSectors, numBytes,
|
||||
numCylinders * trackSize / 1024)
|
||||
<< std::endl;
|
||||
|
||||
std::ofstream outputFile(spec.filename, std::ios::out | std::ios::binary);
|
||||
if (!outputFile.is_open())
|
||||
Error() << "cannot open output file";
|
||||
|
||||
for (int track = 0; track < numCylinders; track++)
|
||||
{
|
||||
for (int head = 0; head < numHeads; head++)
|
||||
{
|
||||
for (int sectorId = 0; sectorId < numSectors; sectorId++)
|
||||
{
|
||||
const auto& sector = sectors.get(track, head, sectorId);
|
||||
if (sector)
|
||||
{
|
||||
outputFile.seekp(sector->logicalTrack*trackSize + sector->logicalSide*headSize + sector->logicalSector*numBytes, std::ios::beg);
|
||||
outputFile.write((const char*) sector->data.cbegin(), sector->data.size());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
std::unique_ptr<ImageWriter> ImageWriter::createImgImageWriter(
|
||||
const SectorSet& sectors, const ImageSpec& spec)
|
||||
{
|
||||
return std::unique_ptr<ImageWriter>(new ImgImageWriter(sectors, spec));
|
||||
}
|
||||
@@ -23,6 +23,11 @@ static DataSpecFlag source(
|
||||
"source for data",
|
||||
":t=0-79:s=0-1:d=0");
|
||||
|
||||
static DataSpecFlag output(
|
||||
{ "--output", "-o" },
|
||||
"output image file to write to",
|
||||
"");
|
||||
|
||||
static StringFlag destination(
|
||||
{ "--write-flux", "-f" },
|
||||
"write the raw magnetic flux to this file",
|
||||
@@ -52,6 +57,11 @@ void setReaderDefaultSource(const std::string& source)
|
||||
::source.set(source);
|
||||
}
|
||||
|
||||
void setReaderDefaultOutput(const std::string& output)
|
||||
{
|
||||
::output.set(output);
|
||||
}
|
||||
|
||||
void setReaderRevolutions(int revolutions)
|
||||
{
|
||||
setHardwareFluxSourceRevolutions(revolutions);
|
||||
@@ -71,9 +81,9 @@ void Track::readFluxmap()
|
||||
|
||||
std::vector<std::unique_ptr<Track>> readTracks()
|
||||
{
|
||||
const DataSpec& dataSpec = source;
|
||||
const FluxSpec spec(source);
|
||||
|
||||
std::cout << "Reading from: " << dataSpec << std::endl;
|
||||
std::cout << "Reading from: " << source << std::endl;
|
||||
|
||||
setHardwareFluxSourceDensity(highDensityFlag);
|
||||
|
||||
@@ -92,10 +102,10 @@ std::vector<std::unique_ptr<Track>> readTracks()
|
||||
);
|
||||
}
|
||||
|
||||
std::shared_ptr<FluxSource> fluxSource = FluxSource::create(dataSpec);
|
||||
std::shared_ptr<FluxSource> fluxSource = FluxSource::create(spec);
|
||||
|
||||
std::vector<std::unique_ptr<Track>> tracks;
|
||||
for (const auto& location : dataSpec.locations)
|
||||
for (const auto& location : spec.locations)
|
||||
{
|
||||
auto track = std::make_unique<Track>(location.track, location.side);
|
||||
track->fluxsource = fluxSource;
|
||||
@@ -140,8 +150,10 @@ static void replace_sector(std::unique_ptr<Sector>& replacing, Sector& replaceme
|
||||
}
|
||||
}
|
||||
|
||||
void readDiskCommand(AbstractDecoder& decoder, const std::string& outputFilename)
|
||||
void readDiskCommand(AbstractDecoder& decoder)
|
||||
{
|
||||
const ImageSpec outputSpec(output);
|
||||
|
||||
bool failures = false;
|
||||
SectorSet allSectors;
|
||||
auto tracks = readTracks();
|
||||
@@ -239,8 +251,7 @@ void readDiskCommand(AbstractDecoder& decoder, const std::string& outputFilename
|
||||
std::cout << size << " bytes decoded." << std::endl;
|
||||
}
|
||||
|
||||
Geometry geometry = guessGeometry(allSectors);
|
||||
writeSectorsToFile(allSectors, geometry, outputFilename);
|
||||
writeSectorsToFile(allSectors, outputSpec);
|
||||
if (failures)
|
||||
std::cerr << "Warning: some sectors could not be decoded." << std::endl;
|
||||
}
|
||||
|
||||
@@ -11,10 +11,11 @@ class Track;
|
||||
extern FlagGroup readerFlags;
|
||||
|
||||
extern void setReaderDefaultSource(const std::string& source);
|
||||
extern void setReaderDefaultOutput(const std::string& output);
|
||||
extern void setReaderRevolutions(int revolutions);
|
||||
|
||||
extern std::vector<std::unique_ptr<Track>> readTracks();
|
||||
|
||||
extern void readDiskCommand(AbstractDecoder& decoder, const std::string& outputFilename);
|
||||
extern void readDiskCommand(AbstractDecoder& decoder);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -18,8 +18,9 @@ Sector* SectorSet::get(int track, int head, int sector) const
|
||||
return i->second.get();
|
||||
}
|
||||
|
||||
void SectorSet::calculateSize(int& numTracks, int& numHeads, int& numSectors,
|
||||
int& sectorSize) const
|
||||
void SectorSet::calculateSize(
|
||||
unsigned& numTracks, unsigned& numHeads,
|
||||
unsigned& numSectors, unsigned& sectorSize) const
|
||||
{
|
||||
numTracks = numHeads = numSectors = sectorSize = 0;
|
||||
|
||||
@@ -28,10 +29,10 @@ void SectorSet::calculateSize(int& numTracks, int& numHeads, int& numSectors,
|
||||
auto& sector = i.second;
|
||||
if (sector)
|
||||
{
|
||||
numTracks = std::max(numTracks, sector->logicalTrack+1);
|
||||
numHeads = std::max(numHeads, sector->logicalSide+1);
|
||||
numSectors = std::max(numSectors, sector->logicalSector+1);
|
||||
sectorSize = std::max(sectorSize, (int)sector->data.size());
|
||||
numTracks = std::max(numTracks, (unsigned)sector->logicalTrack+1);
|
||||
numHeads = std::max(numHeads, (unsigned)sector->logicalSide+1);
|
||||
numSectors = std::max(numSectors, (unsigned)sector->logicalSector+1);
|
||||
sectorSize = std::max(sectorSize, (unsigned)sector->data.size());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,8 +17,9 @@ public:
|
||||
std::unique_ptr<Sector>& get(int track, int head, int sector);
|
||||
Sector* get(int track, int head, int sector) const;
|
||||
|
||||
void calculateSize(int& numTracks, int& numHeads, int& numSectors,
|
||||
int& sectorSize) const;
|
||||
void calculateSize(
|
||||
unsigned& numTracks, unsigned& numHeads, unsigned& numSectors,
|
||||
unsigned& sectorSize) const;
|
||||
|
||||
private:
|
||||
std::map<const key_t, std::unique_ptr<Sector>> _data;
|
||||
|
||||
@@ -22,6 +22,11 @@ static DataSpecFlag dest(
|
||||
"destination for data",
|
||||
":d=0:t=0-79:s=0-1");
|
||||
|
||||
static DataSpecFlag input(
|
||||
{ "--input", "-i" },
|
||||
"input image file to read from",
|
||||
"");
|
||||
|
||||
static SettableFlag highDensityFlag(
|
||||
{ "--high-density", "-H" },
|
||||
"set the drive to high density mode");
|
||||
@@ -33,12 +38,17 @@ void setWriterDefaultDest(const std::string& dest)
|
||||
::dest.set(dest);
|
||||
}
|
||||
|
||||
void setWriterDefaultInput(const std::string& input)
|
||||
{
|
||||
::input.set(input);
|
||||
}
|
||||
|
||||
void writeTracks(
|
||||
const std::function<std::unique_ptr<Fluxmap>(int track, int side)> producer)
|
||||
{
|
||||
const DataSpec& spec = dest;
|
||||
const FluxSpec spec(dest);
|
||||
|
||||
std::cout << "Writing to: " << spec << std::endl;
|
||||
std::cout << "Writing to: " << dest << std::endl;
|
||||
|
||||
setHardwareFluxSourceDensity(highDensityFlag);
|
||||
setHardwareFluxSinkDensity(highDensityFlag);
|
||||
@@ -103,12 +113,10 @@ void fillBitmapTo(std::vector<bool>& bitmap,
|
||||
}
|
||||
}
|
||||
|
||||
void writeDiskCommand(
|
||||
AbstractEncoder& encoder, const Geometry& geometry, const std::string& inputFilename)
|
||||
void writeDiskCommand(AbstractEncoder& encoder)
|
||||
{
|
||||
SectorSet allSectors;
|
||||
|
||||
readSectorsFromFile(allSectors, geometry, inputFilename);
|
||||
const ImageSpec spec(input);
|
||||
SectorSet allSectors = readSectorsFromFile(spec);
|
||||
writeTracks(
|
||||
[&](int track, int side) -> std::unique_ptr<Fluxmap>
|
||||
{
|
||||
|
||||
@@ -10,6 +10,7 @@ class AbstractEncoder;
|
||||
class Geometry;
|
||||
|
||||
extern void setWriterDefaultDest(const std::string& dest);
|
||||
extern void setWriterDefaultInput(const std::string& input);
|
||||
|
||||
extern void writeTracks(const std::function<std::unique_ptr<Fluxmap>(int track, int side)> producer);
|
||||
|
||||
@@ -17,7 +18,6 @@ extern void fillBitmapTo(std::vector<bool>& bitmap,
|
||||
unsigned& cursor, unsigned terminateAt,
|
||||
const std::vector<bool>& pattern);
|
||||
|
||||
extern void writeDiskCommand(
|
||||
AbstractEncoder& encoder, const Geometry& geometry, const std::string& inputFilename);
|
||||
extern void writeDiskCommand(AbstractEncoder& encoder);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -168,6 +168,10 @@ buildlibrary libbackend.a \
|
||||
lib/fluxsource/kryoflux.cc \
|
||||
lib/fluxsource/sqlitefluxsource.cc \
|
||||
lib/fluxsource/streamfluxsource.cc \
|
||||
lib/imagereader/imagereader.cc \
|
||||
lib/imagereader/imgimagereader.cc \
|
||||
lib/imagewriter/imagewriter.cc \
|
||||
lib/imagewriter/imgimagewriter.cc \
|
||||
lib/globals.cc \
|
||||
lib/hexdump.cc \
|
||||
lib/image.cc \
|
||||
|
||||
@@ -34,14 +34,15 @@ int mainConvertFluxToAu(int argc, const char* argv[])
|
||||
{
|
||||
flags.parseFlags(argc, argv);
|
||||
|
||||
const auto& locations = source.get().locations;
|
||||
FluxSpec spec(source);
|
||||
const auto& locations = spec.locations;
|
||||
if (locations.size() != 1)
|
||||
Error() << "the source dataspec must contain exactly one track (two sides count as two tracks)";
|
||||
const auto& location = *(locations.begin());
|
||||
|
||||
std::cerr << "Reading source flux...\n";
|
||||
setHardwareFluxSourceDensity(highDensityFlag);
|
||||
std::shared_ptr<FluxSource> fluxsource = FluxSource::create(source);
|
||||
std::shared_ptr<FluxSource> fluxsource = FluxSource::create(spec);
|
||||
const auto& fluxmap = fluxsource->readFlux(location.track, location.side);
|
||||
unsigned totalTicks = fluxmap->ticks() + 2;
|
||||
unsigned channels = withIndex ? 2 : 1;
|
||||
|
||||
@@ -30,14 +30,15 @@ int mainConvertFluxToVcd(int argc, const char* argv[])
|
||||
{
|
||||
flags.parseFlags(argc, argv);
|
||||
|
||||
const auto& locations = source.get().locations;
|
||||
const FluxSpec spec(source);
|
||||
const auto& locations = spec.locations;
|
||||
if (locations.size() != 1)
|
||||
Error() << "the source dataspec must contain exactly one track (two sides count as two tracks)";
|
||||
const auto& location = *(locations.begin());
|
||||
|
||||
std::cerr << "Reading source flux...\n";
|
||||
setHardwareFluxSourceDensity(highDensityFlag);
|
||||
std::shared_ptr<FluxSource> fluxsource = FluxSource::create(source);
|
||||
std::shared_ptr<FluxSource> fluxsource = FluxSource::create(spec);
|
||||
const auto& fluxmap = fluxsource->readFlux(location.track, location.side);
|
||||
|
||||
std::cerr << "Writing destination VCD...\n";
|
||||
|
||||
@@ -12,11 +12,6 @@
|
||||
|
||||
static FlagGroup flags { &readerFlags };
|
||||
|
||||
static StringFlag outputFilename(
|
||||
{ "--output", "-o" },
|
||||
"The output image file to write to.",
|
||||
"adfs.img");
|
||||
|
||||
static IntFlag sectorIdBase(
|
||||
{ "--sector-id-base" },
|
||||
"Sector ID of the first sector.",
|
||||
@@ -25,10 +20,11 @@ static IntFlag sectorIdBase(
|
||||
int mainReadADFS(int argc, const char* argv[])
|
||||
{
|
||||
setReaderDefaultSource(":t=0-79:s=0-1");
|
||||
setReaderDefaultOutput("adfs.img");
|
||||
flags.parseFlags(argc, argv);
|
||||
|
||||
IbmDecoder decoder(sectorIdBase);
|
||||
readDiskCommand(decoder, outputFilename);
|
||||
readDiskCommand(decoder);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -20,11 +20,12 @@ static StringFlag outputFilename(
|
||||
int mainReadAESLanier(int argc, const char* argv[])
|
||||
{
|
||||
setReaderDefaultSource(":t=0-79:s=0");
|
||||
setReaderDefaultOutput("aeslanier.img");
|
||||
setReaderRevolutions(2);
|
||||
flags.parseFlags(argc, argv);
|
||||
|
||||
AesLanierDecoder decoder;
|
||||
readDiskCommand(decoder, outputFilename);
|
||||
readDiskCommand(decoder);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -13,19 +13,15 @@
|
||||
|
||||
static FlagGroup flags { &readerFlags };
|
||||
|
||||
static StringFlag outputFilename(
|
||||
{ "--output", "-o" },
|
||||
"The output image file to write to.",
|
||||
"amiga.adf");
|
||||
|
||||
int mainReadAmiga(int argc, const char* argv[])
|
||||
{
|
||||
setReaderDefaultSource(":t=0-79:s=0-1");
|
||||
setReaderDefaultOutput("amiga.adf:c=80:h=2:s=11:b=512");
|
||||
setReaderRevolutions(2);
|
||||
flags.parseFlags(argc, argv);
|
||||
|
||||
AmigaDecoder decoder;
|
||||
readDiskCommand(decoder, outputFilename);
|
||||
readDiskCommand(decoder);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -12,11 +12,6 @@
|
||||
|
||||
static FlagGroup flags { &readerFlags };
|
||||
|
||||
static StringFlag outputFilename(
|
||||
{ "--output", "-o" },
|
||||
"The output image file to write to.",
|
||||
"ampro.img");
|
||||
|
||||
static IntFlag sectorIdBase(
|
||||
{ "--sector-id-base" },
|
||||
"Sector ID of the first sector.",
|
||||
@@ -25,11 +20,12 @@ static IntFlag sectorIdBase(
|
||||
int mainReadAmpro(int argc, const char* argv[])
|
||||
{
|
||||
setReaderDefaultSource(":t=0-79:s=0");
|
||||
setReaderDefaultOutput("ampro.adf");
|
||||
setReaderRevolutions(2);
|
||||
flags.parseFlags(argc, argv);
|
||||
|
||||
IbmDecoder decoder(sectorIdBase);
|
||||
readDiskCommand(decoder, outputFilename);
|
||||
readDiskCommand(decoder);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -13,19 +13,15 @@
|
||||
|
||||
static FlagGroup flags { &readerFlags };
|
||||
|
||||
static StringFlag outputFilename(
|
||||
{ "--output", "-o" },
|
||||
"The output image file to write to.",
|
||||
"apple2.img");
|
||||
|
||||
int mainReadApple2(int argc, const char* argv[])
|
||||
{
|
||||
setReaderDefaultSource(":t=0-79:s=0");
|
||||
setReaderDefaultOutput("apple2.adf");
|
||||
setReaderRevolutions(2);
|
||||
flags.parseFlags(argc, argv);
|
||||
|
||||
Apple2Decoder decoder;
|
||||
readDiskCommand(decoder, outputFilename);
|
||||
readDiskCommand(decoder);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -14,19 +14,15 @@
|
||||
|
||||
static FlagGroup flags { &readerFlags };
|
||||
|
||||
static StringFlag outputFilename(
|
||||
{ "--output", "-o" },
|
||||
"The output image file to write to.",
|
||||
"brother.img");
|
||||
|
||||
int mainReadBrother(int argc, const char* argv[])
|
||||
{
|
||||
setReaderDefaultSource(":t=0-81:s=0");
|
||||
setReaderDefaultOutput("brother.img");
|
||||
setReaderRevolutions(2);
|
||||
flags.parseFlags(argc, argv);
|
||||
|
||||
BrotherDecoder decoder;
|
||||
readDiskCommand(decoder, outputFilename);
|
||||
readDiskCommand(decoder);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -13,19 +13,15 @@
|
||||
|
||||
static FlagGroup flags { &readerFlags };
|
||||
|
||||
static StringFlag outputFilename(
|
||||
{ "--output", "-o" },
|
||||
"The output image file to write to.",
|
||||
"c64.img");
|
||||
|
||||
int mainReadC64(int argc, const char* argv[])
|
||||
{
|
||||
setReaderDefaultSource(":t=0-79x2:s=0");
|
||||
setReaderDefaultOutput("c64.img");
|
||||
setReaderRevolutions(2);
|
||||
flags.parseFlags(argc, argv);
|
||||
|
||||
Commodore64Decoder decoder;
|
||||
readDiskCommand(decoder, outputFilename);
|
||||
readDiskCommand(decoder);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -12,11 +12,6 @@
|
||||
|
||||
static FlagGroup flags { &readerFlags };
|
||||
|
||||
static StringFlag outputFilename(
|
||||
{ "--output", "-o" },
|
||||
"The output image file to write to.",
|
||||
"dfs.img");
|
||||
|
||||
static IntFlag sectorIdBase(
|
||||
{ "--sector-id-base" },
|
||||
"Sector ID of the first sector.",
|
||||
@@ -25,11 +20,12 @@ static IntFlag sectorIdBase(
|
||||
int mainReadDFS(int argc, const char* argv[])
|
||||
{
|
||||
setReaderDefaultSource(":t=0-79:s=0");
|
||||
setReaderDefaultOutput("dfs.img");
|
||||
setReaderRevolutions(2);
|
||||
flags.parseFlags(argc, argv);
|
||||
|
||||
IbmDecoder decoder(sectorIdBase);
|
||||
readDiskCommand(decoder, outputFilename);
|
||||
readDiskCommand(decoder);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -13,19 +13,15 @@
|
||||
|
||||
static FlagGroup flags { &readerFlags };
|
||||
|
||||
static StringFlag outputFilename(
|
||||
{ "--output", "-o" },
|
||||
"The output image file to write to.",
|
||||
"f85.img");
|
||||
|
||||
int mainReadF85(int argc, const char* argv[])
|
||||
{
|
||||
setReaderDefaultSource(":t=0-79:s=0");
|
||||
setReaderDefaultOutput("f85.img");
|
||||
setReaderRevolutions(2);
|
||||
flags.parseFlags(argc, argv);
|
||||
|
||||
DurangoF85Decoder decoder;
|
||||
readDiskCommand(decoder, outputFilename);
|
||||
readDiskCommand(decoder);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -12,19 +12,15 @@
|
||||
|
||||
static FlagGroup flags { &readerFlags };
|
||||
|
||||
static StringFlag outputFilename(
|
||||
{ "--output", "-o" },
|
||||
"The output image file to write to.",
|
||||
"fb100.img");
|
||||
|
||||
int mainReadFB100(int argc, const char* argv[])
|
||||
{
|
||||
setReaderDefaultSource(":t=0-79x2:s=0");
|
||||
setReaderDefaultOutput("fb100.img");
|
||||
setReaderRevolutions(2);
|
||||
flags.parseFlags(argc, argv);
|
||||
|
||||
Fb100Decoder decoder;
|
||||
readDiskCommand(decoder, outputFilename);
|
||||
readDiskCommand(decoder);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -12,11 +12,6 @@
|
||||
|
||||
static FlagGroup flags { &readerFlags };
|
||||
|
||||
static StringFlag outputFilename(
|
||||
{ "--output", "-o" },
|
||||
"The output image file to write to.",
|
||||
"ibm.img");
|
||||
|
||||
static IntFlag sectorIdBase(
|
||||
{ "--sector-id-base" },
|
||||
"Sector ID of the first sector.",
|
||||
@@ -25,10 +20,11 @@ static IntFlag sectorIdBase(
|
||||
int mainReadIBM(int argc, const char* argv[])
|
||||
{
|
||||
setReaderDefaultSource(":t=0-79:s=0-1");
|
||||
setReaderDefaultOutput("ibm.img");
|
||||
flags.parseFlags(argc, argv);
|
||||
|
||||
IbmDecoder decoder(sectorIdBase);
|
||||
readDiskCommand(decoder, outputFilename);
|
||||
readDiskCommand(decoder);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -13,19 +13,15 @@
|
||||
|
||||
static FlagGroup flags { &readerFlags };
|
||||
|
||||
static StringFlag outputFilename(
|
||||
{ "--output", "-o" },
|
||||
"The output image file to write to.",
|
||||
"mac.img");
|
||||
|
||||
int mainReadMac(int argc, const char* argv[])
|
||||
{
|
||||
setReaderDefaultSource(":t=0-79:s=0-1");
|
||||
setReaderDefaultOutput("mac.img");
|
||||
setReaderRevolutions(2);
|
||||
flags.parseFlags(argc, argv);
|
||||
|
||||
MacintoshDecoder decoder;
|
||||
readDiskCommand(decoder, outputFilename);
|
||||
readDiskCommand(decoder);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -12,18 +12,14 @@
|
||||
|
||||
static FlagGroup flags { &readerFlags };
|
||||
|
||||
static StringFlag outputFilename(
|
||||
{ "--output", "-o" },
|
||||
"The output image file to write to.",
|
||||
"mx.img");
|
||||
|
||||
int mainReadMx(int argc, const char* argv[])
|
||||
{
|
||||
setReaderDefaultSource(":t=0-79:s=0-1");
|
||||
setReaderDefaultOutput("mx.img");
|
||||
flags.parseFlags(argc, argv);
|
||||
|
||||
MxDecoder decoder;
|
||||
readDiskCommand(decoder, outputFilename);
|
||||
readDiskCommand(decoder);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -13,19 +13,15 @@
|
||||
|
||||
static FlagGroup flags { &readerFlags };
|
||||
|
||||
static StringFlag outputFilename(
|
||||
{ "--output", "-o" },
|
||||
"The output image file to write to.",
|
||||
"victor9k.img");
|
||||
|
||||
int mainReadVictor9K(int argc, const char* argv[])
|
||||
{
|
||||
setReaderDefaultSource(":t=0-79:s=0");
|
||||
setReaderDefaultOutput("victor9k.img");
|
||||
setReaderRevolutions(2);
|
||||
flags.parseFlags(argc, argv);
|
||||
|
||||
Victor9kDecoder decoder;
|
||||
readDiskCommand(decoder, outputFilename);
|
||||
readDiskCommand(decoder);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -12,19 +12,15 @@
|
||||
|
||||
static FlagGroup flags { &readerFlags };
|
||||
|
||||
static StringFlag outputFilename(
|
||||
{ "--output", "-o" },
|
||||
"The output image file to write to.",
|
||||
"zilogmcz.img");
|
||||
|
||||
int mainReadZilogMCZ(int argc, const char* argv[])
|
||||
{
|
||||
setReaderDefaultSource(":t=0-76:s=0");
|
||||
setReaderDefaultOutput("zilogmcz.img");
|
||||
setReaderRevolutions(2);
|
||||
flags.parseFlags(argc, argv);
|
||||
|
||||
ZilogMczDecoder decoder;
|
||||
readDiskCommand(decoder, outputFilename);
|
||||
readDiskCommand(decoder);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -14,7 +14,8 @@ int mainRpm(int argc, const char* argv[])
|
||||
{
|
||||
flags.parseFlags(argc, argv);
|
||||
|
||||
usbSetDrive(source.get().drive, false);
|
||||
FluxSpec spec(source);
|
||||
usbSetDrive(spec.drive, false);
|
||||
nanoseconds_t period = usbGetRotationalPeriod();
|
||||
std::cout << "Rotational period is " << period/1000 << " ms (" << 60e6/period << " rpm)" << std::endl;
|
||||
|
||||
|
||||
@@ -10,19 +10,14 @@
|
||||
|
||||
static FlagGroup flags { &writerFlags, &brotherEncoderFlags };
|
||||
|
||||
static StringFlag inputFilename(
|
||||
{ "--input", "-i" },
|
||||
"The input image file to read from.",
|
||||
"brother.img");
|
||||
|
||||
int mainWriteBrother(int argc, const char* argv[])
|
||||
{
|
||||
setWriterDefaultInput(":c=78:h=1:s=12:b=256");
|
||||
setWriterDefaultDest(":d=0:t=0-77:s=0");
|
||||
flags.parseFlags(argc, argv);
|
||||
|
||||
BrotherEncoder encoder;
|
||||
Geometry geometry = {78, 1, 12, 256};
|
||||
writeDiskCommand(encoder, geometry, inputFilename);
|
||||
writeDiskCommand(encoder);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -33,48 +33,79 @@ static void test_parsemod(void)
|
||||
== (DataSpec::Modifier{"x", {2, 4, 9}}));
|
||||
}
|
||||
|
||||
static void test_dataspec(void)
|
||||
static void test_fluxspec(void)
|
||||
{
|
||||
DataSpec spec("foo:t=0-2:s=0-1:d=0");
|
||||
assert(spec.filename == "foo");
|
||||
assert((spec.locations
|
||||
== std::vector<DataSpec::Location>
|
||||
{{0, 0, 0}, {0, 0, 1}, {0, 1, 0}, {0, 1, 1}, {0, 2, 0}, {0, 2, 1}}));
|
||||
assert((std::string)spec == "foo:d=0:s=0-1:t=0-2");
|
||||
|
||||
{
|
||||
FluxSpec fspec(spec);
|
||||
assert(fspec.filename == "foo");
|
||||
assert((fspec.locations
|
||||
== std::vector<FluxSpec::Location>
|
||||
{{0, 0, 0}, {0, 0, 1}, {0, 1, 0}, {0, 1, 1}, {0, 2, 0}, {0, 2, 1}}));
|
||||
assert((std::string)spec == "foo:d=0:s=0-1:t=0-2");
|
||||
}
|
||||
|
||||
spec.set("bar");
|
||||
assert(spec.filename == "bar");
|
||||
assert((spec.locations
|
||||
== std::vector<DataSpec::Location>
|
||||
{{0, 0, 0}, {0, 0, 1}, {0, 1, 0}, {0, 1, 1}, {0, 2, 0}, {0, 2, 1}}));
|
||||
assert((std::string)spec == "bar:d=0:s=0-1:t=0-2");
|
||||
{
|
||||
FluxSpec fspec(spec);
|
||||
assert(fspec.filename == "bar");
|
||||
assert((fspec.locations
|
||||
== std::vector<FluxSpec::Location>
|
||||
{{0, 0, 0}, {0, 0, 1}, {0, 1, 0}, {0, 1, 1}, {0, 2, 0}, {0, 2, 1}}));
|
||||
assert((std::string)spec == "bar:d=0:s=0-1:t=0-2");
|
||||
}
|
||||
|
||||
spec.set(":t=0");
|
||||
assert(spec.filename.empty());
|
||||
assert((spec.locations
|
||||
== std::vector<DataSpec::Location>
|
||||
{{0, 0, 0}, {0, 0, 1}}));
|
||||
assert((std::string)spec == ":d=0:s=0-1:t=0");
|
||||
{
|
||||
FluxSpec fspec(spec);
|
||||
assert(fspec.filename.empty());
|
||||
assert((fspec.locations
|
||||
== std::vector<FluxSpec::Location>
|
||||
{{0, 0, 0}, {0, 0, 1}}));
|
||||
assert((std::string)spec == ":d=0:s=0-1:t=0");
|
||||
}
|
||||
|
||||
spec.set(":s=1");
|
||||
assert(spec.filename.empty());
|
||||
assert((spec.locations
|
||||
== std::vector<DataSpec::Location>
|
||||
{{0, 0, 1}}));
|
||||
assert((std::string)spec == ":d=0:s=1:t=0");
|
||||
{
|
||||
FluxSpec fspec(spec);
|
||||
assert(fspec.filename.empty());
|
||||
assert((fspec.locations
|
||||
== std::vector<FluxSpec::Location>
|
||||
{{0, 0, 1}}));
|
||||
assert((std::string)spec == ":d=0:s=1:t=0");
|
||||
}
|
||||
|
||||
spec.set(":t=9:d=1");
|
||||
assert(spec.filename.empty());
|
||||
assert((spec.locations
|
||||
== std::vector<DataSpec::Location>
|
||||
{{1, 9, 1}}));
|
||||
assert((std::string)spec == ":d=1:s=1:t=9");
|
||||
{
|
||||
FluxSpec fspec(spec);
|
||||
assert(fspec.filename.empty());
|
||||
assert((fspec.locations
|
||||
== std::vector<FluxSpec::Location>
|
||||
{{1, 9, 1}}));
|
||||
assert((std::string)spec == ":d=1:s=1:t=9");
|
||||
}
|
||||
}
|
||||
|
||||
static void test_imagespec(void)
|
||||
{
|
||||
DataSpec spec("foo:c=9:h=2:s=99:b=256");
|
||||
|
||||
{
|
||||
ImageSpec ispec(spec);
|
||||
assert(ispec.filename == "foo");
|
||||
assert(ispec.cylinders == 9);
|
||||
assert(ispec.heads == 2);
|
||||
assert(ispec.sectors == 99);
|
||||
assert(ispec.bytes = 256);
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, const char* argv[])
|
||||
{
|
||||
test_split();
|
||||
test_parsemod();
|
||||
test_dataspec();
|
||||
test_fluxspec();
|
||||
test_imagespec();
|
||||
return 0;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user