diff --git a/arch/brother/encoder.cc b/arch/brother/encoder.cc index ed827c42..1d831987 100644 --- a/arch/brother/encoder.cc +++ b/arch/brother/encoder.cc @@ -8,33 +8,6 @@ #include "arch/brother/brother.pb.h" #include "lib/encoders/encoders.pb.h" -FlagGroup brotherEncoderFlags; - -static DoubleFlag clockRateUs( - { "--clock-rate" }, - "Encoded data clock rate (microseconds).", - 3.83); - -static DoubleFlag postIndexGapMs( - { "--post-index-gap" }, - "Post-index gap before first sector header (milliseconds).", - 1.0); - -static DoubleFlag sectorSpacingMs( - { "--sector-spacing" }, - "Time between successive sector headers (milliseconds).", - 16.2); - -static DoubleFlag postHeaderSpacingMs( - { "--post-header-spacing" }, - "Time between a sector's header and data records (milliseconds).", - 0.69); - -static StringFlag sectorSkew( - { "--sector-skew" }, - "Order in which to write sectors.", - "05a3816b4927"); - static int encode_header_gcr(uint16_t word) { switch (word) @@ -193,18 +166,18 @@ public: break; } - int bitsPerRevolution = 200000.0 / clockRateUs; - const std::string& skew = sectorSkew.get(); + int bitsPerRevolution = 200000.0 / _config.clock_rate_us(); + const std::string& skew = _config.sector_skew(); std::vector bits(bitsPerRevolution); unsigned cursor = 0; for (int sectorCount=0; sectorCount fluxmap(new Fluxmap); - fluxmap->appendBits(bits, clockRateUs*1e3); + fluxmap->appendBits(bits, _config.clock_rate_us()*1e3); return fluxmap; } diff --git a/arch/macintosh/encoder.cc b/arch/macintosh/encoder.cc index 8be1e054..e85421ea 100644 --- a/arch/macintosh/encoder.cc +++ b/arch/macintosh/encoder.cc @@ -174,9 +174,9 @@ static void write_sector(std::vector& bits, unsigned& cursor, const std::s write_bits(bits, cursor, 0xff3fcff3fcffLL, 6*8); /* sync */ write_bits(bits, cursor, MAC_SECTOR_RECORD, 3*8); - uint8_t encodedTrack = sector->physicalHead & 0x3f; + uint8_t encodedTrack = sector->logicalTrack & 0x3f; uint8_t encodedSector = sector->logicalSector; - uint8_t encodedSide = encode_side(sector->physicalCylinder, sector->logicalSide); + uint8_t encodedSide = encode_side(sector->logicalTrack, sector->logicalSide); uint8_t formatByte = MAC_FORMAT_BYTE; uint8_t headerChecksum = (encodedTrack ^ encodedSector ^ encodedSide ^ formatByte) & 0x3f; @@ -240,7 +240,7 @@ public: fillBitmapTo(bits, cursor, _config.post_index_gap_us() / clockRateUs, { true, false }); lastBit = false; - for (const auto& sector : collectSectors(physicalTrack, physicalSide, image)) + for (const auto& sector : sectors) write_sector(bits, cursor, sector); if (cursor >= bits.size()) diff --git a/arch/northstar/encoder.cc b/arch/northstar/encoder.cc index 44059018..44c09d4f 100644 --- a/arch/northstar/encoder.cc +++ b/arch/northstar/encoder.cc @@ -131,11 +131,10 @@ public: int bitsPerRevolution = 100000; double clockRateUs = 4.00; - if ((physicalTrack < 0) || (physicalTrack >= 35)) + if ((physicalTrack < 0) || (physicalTrack >= 35) || sectors.empty()) return std::unique_ptr(); - const auto& sector = image.get(physicalTrack, physicalSide, 0); - + const auto& sector = *sectors.begin(); if (sector->data.size() == NORTHSTAR_PAYLOAD_SIZE_SD) { bitsPerRevolution /= 2; // FM } else { @@ -145,11 +144,8 @@ public: std::vector bits(bitsPerRevolution); unsigned cursor = 0; - for (int sectorId = 0; sectorId < 10; sectorId++) - { - const auto& sectorData = image.get(physicalTrack, physicalSide, sectorId); + for (const auto& sectorData : sectors) write_sector(bits, cursor, sectorData); - } if (cursor > bits.size()) Error() << "track data overrun"; diff --git a/arch/tids990/tids990.h b/arch/tids990/tids990.h index 5f0a9601..da1d2187 100644 --- a/arch/tids990/tids990.h +++ b/arch/tids990/tids990.h @@ -11,6 +11,7 @@ class DecoderProto; class EncoderProto; extern std::unique_ptr createTids990Decoder(const DecoderProto& config); +extern std::unique_ptr createTids990Encoder(const EncoderProto& config); #endif diff --git a/lib/encoders/encoders.cc b/lib/encoders/encoders.cc index bc091158..30a4486b 100644 --- a/lib/encoders/encoders.cc +++ b/lib/encoders/encoders.cc @@ -23,6 +23,7 @@ std::unique_ptr AbstractEncoder::create(const EncoderProto& con { EncoderProto::kIbm, createIbmEncoder }, { EncoderProto::kMacintosh, createMacintoshEncoder }, { EncoderProto::kNorthstar, createNorthstarEncoder }, + { EncoderProto::kTids990, createTids990Encoder }, }; auto encoder = encoders.find(config.format_case()); diff --git a/lib/flags.cc b/lib/flags.cc index 6f6f8ff5..0c91ed04 100644 --- a/lib/flags.cc +++ b/lib/flags.cc @@ -183,9 +183,13 @@ void FlagGroup::parseFlagsWithConfigFiles(int argc, const char* argv[], parseFlags(argc, argv, [&](const auto& filename) { const auto& it = configFiles.find(filename); - std::string data; if (it != configFiles.end()) - data = it->second; + { + ConfigProto newConfig; + if (!newConfig.ParseFromString(it->second)) + Error() << "couldn't load built-in config proto"; + config.MergeFrom(newConfig); + } else { std::ifstream f(filename, std::ios::out); @@ -194,13 +198,10 @@ void FlagGroup::parseFlagsWithConfigFiles(int argc, const char* argv[], std::ostringstream ss; ss << f.rdbuf(); - data = ss.str(); - } - ConfigProto newConfig; - if (!newConfig.ParseFromString(data)) - Error() << "couldn't load config proto"; - config.MergeFrom(newConfig); + if (!google::protobuf::TextFormat::MergeFromString(ss.str(), &config)) + Error() << "couldn't load external config proto"; + } return true; } diff --git a/lib/imagereader/imagereader.cc b/lib/imagereader/imagereader.cc index 88749862..0cf3adc5 100644 --- a/lib/imagereader/imagereader.cc +++ b/lib/imagereader/imagereader.cc @@ -69,3 +69,20 @@ ImageReader::ImageReader(const ImageReaderProto& config): _config(config) {} +void getTrackFormat(const ImgInputOutputProto& config, + ImgInputOutputProto::TrackdataProto& trackdata, unsigned track, unsigned side) +{ + trackdata.Clear(); + for (const ImgInputOutputProto::TrackdataProto& f : config.trackdata()) + { + if (f.has_track() && f.has_up_to_track() && ((track < f.track()) || (track > f.up_to_track()))) + continue; + if (f.has_track() && !f.has_up_to_track() && (track != f.track())) + continue; + if (f.has_side() && (f.side() != side)) + continue; + + trackdata.MergeFrom(f); + } +} + diff --git a/lib/imagereader/imagereader.proto b/lib/imagereader/imagereader.proto index 31af843d..3e181bc6 100644 --- a/lib/imagereader/imagereader.proto +++ b/lib/imagereader/imagereader.proto @@ -3,17 +3,19 @@ syntax = "proto2"; import "lib/common.proto"; message ImgInputOutputProto { + // NEXT: 6 message TrackdataProto { - optional int32 track = 1 [(help) = "if present, this format only applies to this track"]; - optional int32 side = 2 [(help) = "if present, this format only applies to this side"]; + optional int32 track = 1 [(help) = "if present, this format only applies to this track"]; + optional int32 up_to_track = 5 [(help) = "if present, forms a range with track"]; + optional int32 side = 2 [(help) = "if present, this format only applies to this side"]; optional int32 sector_size = 3 [default=512, (help) = "number of bytes per sector"]; - optional int32 sectors = 4 [(help) = "number of sectors in this track"]; + optional int32 sectors = 4 [(help) = "number of sectors in this track"]; } repeated TrackdataProto trackdata = 4 [(help) = "per-track format information (repeatable)"]; - optional int32 tracks = 5 [default=80, (help) = "number of tracks in image"]; - optional int32 sides = 6 [default=2, (help) = "number of sides in image"]; + optional int32 tracks = 5 [default=0, (help) = "number of tracks in image"]; + optional int32 sides = 6 [default=0, (help) = "number of sides in image"]; optional int32 physical_offset = 7 [default=0, (help) = "logical:physical track offset"]; optional int32 physical_step = 8 [default=1, (help) = "logical:physical track step"]; } diff --git a/lib/imagereader/imagereaderimpl.h b/lib/imagereader/imagereaderimpl.h new file mode 100644 index 00000000..ed80cba7 --- /dev/null +++ b/lib/imagereader/imagereaderimpl.h @@ -0,0 +1,8 @@ +#ifndef IMAGEREADERIMPL_H +#define IMAGEREADERIMPL_H + +extern void getTrackFormat(const ImgInputOutputProto& config, + ImgInputOutputProto::TrackdataProto& trackdata, unsigned track, unsigned side); + +#endif + diff --git a/lib/imagereader/imgimagereader.cc b/lib/imagereader/imgimagereader.cc index b8b45cf1..89dadf99 100644 --- a/lib/imagereader/imgimagereader.cc +++ b/lib/imagereader/imgimagereader.cc @@ -4,6 +4,7 @@ #include "imagereader/imagereader.h" #include "image.h" #include "lib/config.pb.h" +#include "imagereader/imagereaderimpl.h" #include "fmt/format.h" #include #include @@ -22,6 +23,9 @@ public: if (!inputFile.is_open()) Error() << "cannot open input file"; + if (!_config.img().tracks() || !_config.img().sides()) + Error() << "IMG: bad configuration; did you remember to set the tracks, sides and trackdata fields?"; + Image image; int trackCount = 0; for (int track = 0; track < _config.img().tracks(); track++) @@ -33,7 +37,7 @@ public: for (int side = 0; side < _config.img().sides(); side++) { ImgInputOutputProto::TrackdataProto trackdata; - getTrackFormat(trackdata, track, side); + getTrackFormat(_config.img(), trackdata, track, side); for (int sectorId = 0; sectorId < trackdata.sectors(); sectorId++) { @@ -55,26 +59,11 @@ public: image.calculateSize(); const Geometry& geometry = image.getGeometry(); - std::cout << fmt::format("reading {} tracks, {} sides, {} kB total\n", + std::cout << fmt::format("IMG: read {} tracks, {} sides, {} kB total\n", geometry.numTracks, geometry.numSides, inputFile.tellg() / 1024); return image; } - -private: - void getTrackFormat(ImgInputOutputProto::TrackdataProto& trackdata, unsigned track, unsigned side) - { - trackdata.Clear(); - for (const ImgInputOutputProto::TrackdataProto& f : _config.img().trackdata()) - { - if (f.has_track() && (f.track() != track)) - continue; - if (f.has_side() && (f.side() != side)) - continue; - - trackdata.MergeFrom(f); - } - } }; std::unique_ptr ImageReader::createImgImageReader( diff --git a/lib/imagewriter/imgimagewriter.cc b/lib/imagewriter/imgimagewriter.cc index 9238c2b6..8d40e6a6 100644 --- a/lib/imagewriter/imgimagewriter.cc +++ b/lib/imagewriter/imgimagewriter.cc @@ -4,6 +4,7 @@ #include "imagewriter/imagewriter.h" #include "image.h" #include "lib/config.pb.h" +#include "imagereader/imagereaderimpl.h" #include "fmt/format.h" #include #include @@ -32,7 +33,7 @@ public: for (int side = 0; side < sides; side++) { ImgInputOutputProto::TrackdataProto trackdata; - getTrackFormat(trackdata, track, side); + getTrackFormat(_config.img(), trackdata, track, side); int numSectors = trackdata.has_sectors() ? trackdata.sectors() : geometry.numSectors; int sectorSize = trackdata.has_sector_size() ? trackdata.sector_size() : geometry.sectorSize; @@ -48,25 +49,10 @@ public: } } - std::cout << fmt::format("wrote {} tracks, {} sides, {} kB total\n", + std::cout << fmt::format("IMG: wrote {} tracks, {} sides, {} kB total\n", tracks, sides, outputFile.tellp() / 1024); } - -private: - void getTrackFormat(ImgInputOutputProto::TrackdataProto& trackdata, unsigned track, unsigned side) - { - trackdata.Clear(); - for (const ImgInputOutputProto::TrackdataProto& f : _config.img().trackdata()) - { - if (f.has_track() && (f.track() != track)) - continue; - if (f.has_side() && (f.side() != side)) - continue; - - trackdata.MergeFrom(f); - } - } }; std::unique_ptr ImageWriter::createImgImageWriter( diff --git a/lib/proto.cc b/lib/proto.cc index 29765800..41d93b4c 100644 --- a/lib/proto.cc +++ b/lib/proto.cc @@ -162,6 +162,11 @@ void setProtoFieldFromString(ProtoField& protoField, const std::string& value) setRange((RangeProto*)reflection->MutableMessage(message, field), value); break; } + if (field->containing_oneof() && value.empty()) + { + reflection->MutableMessage(message, field); + break; + } /* fall through */ default: Error() << "can't set this config value type"; diff --git a/mkninja.sh b/mkninja.sh index 2470e77f..69ce1eca 100644 --- a/mkninja.sh +++ b/mkninja.sh @@ -35,6 +35,10 @@ rule test command = \$in && touch \$out description = TEST \$in +rule encodedecode + command = sh scripts/encodedecodetest.sh \$format \$configs > \$out + description = ENCODEDECODE \$format + rule strip command = cp -f \$in \$out && $STRIP \$out description = STRIP \$in @@ -255,6 +259,16 @@ runtest() { echo build $OBJDIR/$prog.stamp : test $OBJDIR/$prog-debug$EXTENSION } +encodedecodetest() { + local format + format=$1 + shift + + echo "build $OBJDIR/$format.encodedecode.stamp : encodedecode | fluxengine$EXTENSION scripts/encodedecodetest.sh $*" + echo " format=$format" + echo " configs=$*" +} + buildlibrary libagg.a \ -Idep/agg/include \ dep/stb/stb_image_write.c \ @@ -408,9 +422,9 @@ FORMATS="\ mac800 \ micropolis \ mx \ - northstar87 \ northstar175 \ northstar350 \ + northstar87 \ tids990 \ victor9k \ zilogmcz \ @@ -492,5 +506,28 @@ runtest proto-test -I$OBJDIR/proto \ tests/proto.cc \ $OBJDIR/proto/tests/testproto.cc +encodedecodetest amiga +encodedecodetest atarist360 +encodedecodetest atarist370 +encodedecodetest atarist400 +encodedecodetest atarist410 +encodedecodetest atarist720 +encodedecodetest atarist740 +encodedecodetest atarist800 +encodedecodetest atarist820 +encodedecodetest brother120 +encodedecodetest brother240 +encodedecodetest ibm1200_525 +encodedecodetest ibm1440 +encodedecodetest ibm180_525 +encodedecodetest ibm360_525 +encodedecodetest ibm720 +encodedecodetest ibm720_525 +encodedecodetest tids990 +encodedecodetest commodore1581 +encodedecodetest commodore1541 scripts/commodore1541_test.textpb +encodedecodetest mac400 scripts/mac400_test.textpb +encodedecodetest mac800 scripts/mac800_test.textpb + # vim: sw=4 ts=4 et diff --git a/scripts/commodore1541_test.textpb b/scripts/commodore1541_test.textpb new file mode 100644 index 00000000..b5e9a6d9 --- /dev/null +++ b/scripts/commodore1541_test.textpb @@ -0,0 +1,8 @@ +image_reader { + d64 {} +} + +image_writer { + d64 {} +} + diff --git a/scripts/encodedecodetest.sh b/scripts/encodedecodetest.sh new file mode 100755 index 00000000..939ea020 --- /dev/null +++ b/scripts/encodedecodetest.sh @@ -0,0 +1,33 @@ +#!/bin/sh +set -e + +if [ $(uname) != "Linux" ]; then + echo "Skipping test as not on Linux" + exit 0 +fi + +tmp=/tmp/$$ +srcfile=$tmp.src.img +fluxfile=$tmp.flux +destfile=$tmp.dest.img +format=$1 +shift + +trap "rm -f $srcfile $fluxfile $destfile" EXIT + +dd if=/dev/urandom of=$srcfile bs=1M count=2 2>1 + +./fluxengine write $format -i $srcfile -d $fluxfile "$@" +./fluxengine read $format -s $fluxfile -o $destfile "$@" +if [ ! -s $destfile ]; then + echo "Zero length output file!" >&2 + exit 1 +fi + +truncate $srcfile -r $destfile +if ! cmp $srcfile $destfile; then + echo "Comparison failed!" >&2 + exit 1 +fi +exit 0 + diff --git a/scripts/mac400_test.textpb b/scripts/mac400_test.textpb new file mode 100644 index 00000000..015cec5e --- /dev/null +++ b/scripts/mac400_test.textpb @@ -0,0 +1,70 @@ +image_reader { + img { + tracks: 80 + sides: 1 + trackdata { + sector_size: 524 + } + trackdata { + track: 0 + up_to_track: 15 + sectors: 12 + } + trackdata { + track: 16 + up_to_track: 31 + sectors: 11 + } + trackdata { + track: 32 + up_to_track: 47 + sectors: 10 + } + trackdata { + track: 48 + up_to_track: 63 + sectors: 9 + } + trackdata { + track: 64 + up_to_track: 79 + sectors: 8 + } + } +} + +image_writer { + img { + tracks: 80 + sides: 1 + trackdata { + sector_size: 524 + } + trackdata { + track: 0 + up_to_track: 15 + sectors: 12 + } + trackdata { + track: 16 + up_to_track: 31 + sectors: 11 + } + trackdata { + track: 32 + up_to_track: 47 + sectors: 10 + } + trackdata { + track: 48 + up_to_track: 63 + sectors: 9 + } + trackdata { + track: 64 + up_to_track: 79 + sectors: 8 + } + } +} + diff --git a/scripts/mac800_test.textpb b/scripts/mac800_test.textpb new file mode 100644 index 00000000..09e615ff --- /dev/null +++ b/scripts/mac800_test.textpb @@ -0,0 +1,70 @@ +image_reader { + img { + tracks: 80 + sides: 2 + trackdata { + sector_size: 524 + } + trackdata { + track: 0 + up_to_track: 15 + sectors: 12 + } + trackdata { + track: 16 + up_to_track: 31 + sectors: 11 + } + trackdata { + track: 32 + up_to_track: 47 + sectors: 10 + } + trackdata { + track: 48 + up_to_track: 63 + sectors: 9 + } + trackdata { + track: 64 + up_to_track: 79 + sectors: 8 + } + } +} + +image_writer { + img { + tracks: 80 + sides: 2 + trackdata { + sector_size: 524 + } + trackdata { + track: 0 + up_to_track: 15 + sectors: 12 + } + trackdata { + track: 16 + up_to_track: 31 + sectors: 11 + } + trackdata { + track: 32 + up_to_track: 47 + sectors: 10 + } + trackdata { + track: 48 + up_to_track: 63 + sectors: 9 + } + trackdata { + track: 64 + up_to_track: 79 + sectors: 8 + } + } +} + diff --git a/src/formats/atarist360.textpb b/src/formats/atarist360.textpb index f2890366..fb0aa564 100644 --- a/src/formats/atarist360.textpb +++ b/src/formats/atarist360.textpb @@ -27,15 +27,13 @@ encoder { gap2: 22 gap3: 34 sector_skew: "012345678" - swap_sides: true + sector_size: 512 } } } decoder { - ibm { - swap_sides: false - } + ibm {} } cylinders { diff --git a/src/formats/atarist370.textpb b/src/formats/atarist370.textpb index 7fd624de..9f640d4c 100644 --- a/src/formats/atarist370.textpb +++ b/src/formats/atarist370.textpb @@ -27,15 +27,12 @@ encoder { gap2: 22 gap3: 34 sector_skew: "012345678" - swap_sides: true } } } decoder { - ibm { - swap_sides: false - } + ibm {} } cylinders { diff --git a/src/formats/atarist400.textpb b/src/formats/atarist400.textpb index 890b6686..6d48232a 100644 --- a/src/formats/atarist400.textpb +++ b/src/formats/atarist400.textpb @@ -27,15 +27,12 @@ encoder { gap2: 22 gap3: 34 sector_skew: "0123456789" - swap_sides: true } } } decoder { - ibm { - swap_sides: false - } + ibm {} } cylinders { diff --git a/src/formats/atarist410.textpb b/src/formats/atarist410.textpb index 6142d54f..1aa93bb9 100644 --- a/src/formats/atarist410.textpb +++ b/src/formats/atarist410.textpb @@ -27,15 +27,12 @@ encoder { gap2: 22 gap3: 34 sector_skew: "0123456789" - swap_sides: true } } } decoder { - ibm { - swap_sides: false - } + ibm {} } cylinders { diff --git a/src/formats/atarist720.textpb b/src/formats/atarist720.textpb index 59961cac..ebb2f06f 100644 --- a/src/formats/atarist720.textpb +++ b/src/formats/atarist720.textpb @@ -27,15 +27,12 @@ encoder { gap2: 22 gap3: 34 sector_skew: "012345678" - swap_sides: true } } } decoder { - ibm { - swap_sides: false - } + ibm {} } cylinders { diff --git a/src/formats/atarist740.textpb b/src/formats/atarist740.textpb index 1f02ce91..88842e3b 100644 --- a/src/formats/atarist740.textpb +++ b/src/formats/atarist740.textpb @@ -27,15 +27,12 @@ encoder { gap2: 22 gap3: 34 sector_skew: "012345678" - swap_sides: true } } } decoder { - ibm { - swap_sides: false - } + ibm {} } cylinders { diff --git a/src/formats/atarist800.textpb b/src/formats/atarist800.textpb index ec54e3e5..5cfd83ce 100644 --- a/src/formats/atarist800.textpb +++ b/src/formats/atarist800.textpb @@ -27,15 +27,12 @@ encoder { gap2: 22 gap3: 34 sector_skew: "0123456789" - swap_sides: true } } } decoder { - ibm { - swap_sides: false - } + ibm {} } cylinders { diff --git a/src/formats/atarist820.textpb b/src/formats/atarist820.textpb index e3354b59..4a5c8418 100644 --- a/src/formats/atarist820.textpb +++ b/src/formats/atarist820.textpb @@ -27,15 +27,12 @@ encoder { gap2: 22 gap3: 34 sector_skew: "0123456789" - swap_sides: true } } } decoder { - ibm { - swap_sides: false - } + ibm {} } cylinders {