From 8b115f8156471421c6db67c83ef2245a8b1daef1 Mon Sep 17 00:00:00 2001 From: David Given Date: Wed, 28 Jul 2021 21:51:12 +0200 Subject: [PATCH 1/4] Convert the IBM encoder/decoder to use lists of sector IDs rather than sector_skew strings. --- arch/ibm/decoder.cc | 5 ++++- arch/ibm/encoder.cc | 18 +++--------------- arch/ibm/ibm.proto | 14 ++++++++++++-- src/formats/atarist360.textpb | 12 +++++++++++- src/formats/atarist370.textpb | 12 +++++++++++- src/formats/atarist400.textpb | 13 ++++++++++++- src/formats/atarist410.textpb | 13 ++++++++++++- src/formats/atarist720.textpb | 12 +++++++++++- src/formats/atarist740.textpb | 12 +++++++++++- src/formats/atarist800.textpb | 13 ++++++++++++- src/formats/atarist820.textpb | 13 ++++++++++++- src/formats/commodore1581.textpb | 13 ++++++++++++- src/formats/hplif770.textpb | 8 +++++++- src/formats/ibm1200_525.textpb | 18 +++++++++++++++++- src/formats/ibm1440.textpb | 21 ++++++++++++++++++++- src/formats/ibm180_525.textpb | 12 +++++++++++- src/formats/ibm360_525.textpb | 12 +++++++++++- src/formats/ibm720.textpb | 12 +++++++++++- src/formats/ibm720_525.textpb | 12 +++++++++++- 19 files changed, 211 insertions(+), 34 deletions(-) diff --git a/arch/ibm/decoder.cc b/arch/ibm/decoder.cc index 971c80e9..1bd0b9e1 100644 --- a/arch/ibm/decoder.cc +++ b/arch/ibm/decoder.cc @@ -171,7 +171,10 @@ public: std::set requiredSectors(unsigned cylinder, unsigned head) const override { - return iterate(_config.required_sectors()); + std::set s; + for (int sectorId : _config.sectors().sector()) + s.insert(sectorId); + return s; } private: diff --git a/arch/ibm/encoder.cc b/arch/ibm/encoder.cc index 55e63947..4c04a1a0 100644 --- a/arch/ibm/encoder.cc +++ b/arch/ibm/encoder.cc @@ -57,13 +57,6 @@ * mfm: 01 01 01 01 01 00 01 01 = 0x5545 */ -static int charToInt(char c) -{ - if (isdigit(c)) - return c - '0'; - return 10 + tolower(c) - 'a'; -} - static uint8_t decodeUint16(uint16_t raw) { Bytes b; @@ -115,9 +108,8 @@ public: IbmEncoderProto::TrackdataProto trackdata; getTrackFormat(trackdata, physicalTrack, physicalSide); - for (char sectorChar : trackdata.sector_skew()) + for (int sectorId : trackdata.sectors().sector()) { - int sectorId = charToInt(sectorChar); const auto& sector = image.get(physicalTrack, physicalSide, sectorId); if (sector) sectors.push_back(sector); @@ -185,19 +177,15 @@ public: } bool first = true; - for (char sectorChar : trackdata.sector_skew()) + for (int sectorId : trackdata.sectors().sector()) { - int sectorId = charToInt(sectorChar); if (!first) writeFillerBytes(trackdata.gap3(), gapFill); first = false; const auto& sectorData = image.get(physicalTrack, physicalSide, sectorId); if (!sectorData) - { - /* If there are any missing sectors, this is an empty track. */ - return std::unique_ptr(); - } + continue; /* Writing the sector and data records are fantastically annoying. * The CRC is calculated from the *very start* of the record, and diff --git a/arch/ibm/ibm.proto b/arch/ibm/ibm.proto index d76bcaa3..afb19b27 100644 --- a/arch/ibm/ibm.proto +++ b/arch/ibm/ibm.proto @@ -2,15 +2,25 @@ syntax = "proto2"; import "lib/common.proto"; +// Next: 6 message IbmDecoderProto { + message SectorsProto { + repeated int32 sector = 1 [(help) = "require these sectors to exist for a good read"]; + } + optional int32 sector_id_base = 1 [default = 1, (help) = "ID of first sector"]; optional bool ignore_side_byte = 2 [default = false, (help) = "ignore side byte in sector header"]; - optional RangeProto required_sectors = 3 [(help) = "require these sectors to exist for a good read"]; optional bool swap_sides = 4 [default = false, (help) = "swap side bytes when reading"]; + optional SectorsProto sectors = 5 [(help) = "require these sectors to exist for a good read"]; } message IbmEncoderProto { + // Next: 18 message TrackdataProto { + message SectorsProto { + repeated int32 sector = 1 [(help) = "write these sectors (in order) on each track"]; + } + optional int32 cylinder = 15 [(help) = "if set, the format applies only to this track"]; optional int32 head = 16 [(help) = "if set, the format applies only to this head"]; @@ -26,8 +36,8 @@ message IbmEncoderProto { optional int32 gap1 = 10 [default=50, (help) = "size of gap 2 (the post-ID gap)"]; optional int32 gap2 = 11 [default=22, (help) = "size of gap 3 (the pre-data gap)"]; optional int32 gap3 = 12 [default=80, (help) = "size of gap 4 (the post-data or format gap)"]; - optional string sector_skew = 13 [(help) = "order to emit sectors"]; optional bool swap_sides = 14 [default=false, (help) = "swap side bytes when writing"]; + optional SectorsProto sectors = 17 [(help) = "write these sectors (in order) on each track"]; } repeated TrackdataProto trackdata = 1; diff --git a/src/formats/atarist360.textpb b/src/formats/atarist360.textpb index da33b12d..9fce8aa6 100644 --- a/src/formats/atarist360.textpb +++ b/src/formats/atarist360.textpb @@ -26,8 +26,18 @@ encoder { gap0: 80 gap2: 22 gap3: 34 - sector_skew: "012345678" swap_sides: true + sectors { + sector: 0 + sector: 1 + sector: 2 + sector: 3 + sector: 4 + sector: 5 + sector: 6 + sector: 7 + sector: 8 + } } } } diff --git a/src/formats/atarist370.textpb b/src/formats/atarist370.textpb index c9006ff3..a50027ae 100644 --- a/src/formats/atarist370.textpb +++ b/src/formats/atarist370.textpb @@ -26,8 +26,18 @@ encoder { gap0: 80 gap2: 22 gap3: 34 - sector_skew: "012345678" swap_sides: true + sectors { + sector: 0 + sector: 1 + sector: 2 + sector: 3 + sector: 4 + sector: 5 + sector: 6 + sector: 7 + sector: 8 + } } } } diff --git a/src/formats/atarist400.textpb b/src/formats/atarist400.textpb index c86e9f2b..b2a10f26 100644 --- a/src/formats/atarist400.textpb +++ b/src/formats/atarist400.textpb @@ -26,8 +26,19 @@ encoder { gap0: 80 gap2: 22 gap3: 34 - sector_skew: "0123456789" swap_sides: true + sectors { + sector: 0 + sector: 1 + sector: 2 + sector: 3 + sector: 4 + sector: 5 + sector: 6 + sector: 7 + sector: 8 + sector: 9 + } } } } diff --git a/src/formats/atarist410.textpb b/src/formats/atarist410.textpb index d09d8fd8..81b21db6 100644 --- a/src/formats/atarist410.textpb +++ b/src/formats/atarist410.textpb @@ -26,8 +26,19 @@ encoder { gap0: 80 gap2: 22 gap3: 34 - sector_skew: "0123456789" swap_sides: true + sectors { + sector: 0 + sector: 1 + sector: 2 + sector: 3 + sector: 4 + sector: 5 + sector: 6 + sector: 7 + sector: 8 + sector: 9 + } } } } diff --git a/src/formats/atarist720.textpb b/src/formats/atarist720.textpb index f5446dba..a2d2189e 100644 --- a/src/formats/atarist720.textpb +++ b/src/formats/atarist720.textpb @@ -26,8 +26,18 @@ encoder { gap0: 80 gap2: 22 gap3: 34 - sector_skew: "012345678" swap_sides: true + sectors { + sector: 0 + sector: 1 + sector: 2 + sector: 3 + sector: 4 + sector: 5 + sector: 6 + sector: 7 + sector: 8 + } } } } diff --git a/src/formats/atarist740.textpb b/src/formats/atarist740.textpb index 12a9fbed..d4effd1e 100644 --- a/src/formats/atarist740.textpb +++ b/src/formats/atarist740.textpb @@ -26,8 +26,18 @@ encoder { gap0: 80 gap2: 22 gap3: 34 - sector_skew: "012345678" swap_sides: true + sectors { + sector: 0 + sector: 1 + sector: 2 + sector: 3 + sector: 4 + sector: 5 + sector: 6 + sector: 7 + sector: 8 + } } } } diff --git a/src/formats/atarist800.textpb b/src/formats/atarist800.textpb index 5e4edec4..29de3d57 100644 --- a/src/formats/atarist800.textpb +++ b/src/formats/atarist800.textpb @@ -26,8 +26,19 @@ encoder { gap0: 80 gap2: 22 gap3: 34 - sector_skew: "0123456789" swap_sides: true + sectors { + sector: 0 + sector: 1 + sector: 2 + sector: 3 + sector: 4 + sector: 5 + sector: 6 + sector: 7 + sector: 8 + sector: 9 + } } } } diff --git a/src/formats/atarist820.textpb b/src/formats/atarist820.textpb index d86e7fee..f8478028 100644 --- a/src/formats/atarist820.textpb +++ b/src/formats/atarist820.textpb @@ -26,8 +26,19 @@ encoder { gap0: 80 gap2: 22 gap3: 34 - sector_skew: "0123456789" swap_sides: true + sectors { + sector: 0 + sector: 1 + sector: 2 + sector: 3 + sector: 4 + sector: 5 + sector: 6 + sector: 7 + sector: 8 + sector: 9 + } } } } diff --git a/src/formats/commodore1581.textpb b/src/formats/commodore1581.textpb index 794e0054..5d5978ae 100644 --- a/src/formats/commodore1581.textpb +++ b/src/formats/commodore1581.textpb @@ -33,7 +33,18 @@ encoder { gap0: 80 gap2: 22 gap3: 34 - sector_skew: "0123456789" + sectors { + sector: 0 + sector: 1 + sector: 2 + sector: 3 + sector: 4 + sector: 5 + sector: 6 + sector: 7 + sector: 8 + sector: 9 + } } } } diff --git a/src/formats/hplif770.textpb b/src/formats/hplif770.textpb index decb325c..1e037719 100644 --- a/src/formats/hplif770.textpb +++ b/src/formats/hplif770.textpb @@ -21,7 +21,13 @@ encoder { gap0: 80 gap2: 22 gap3: 44 - sector_skew: "01234" + sectors { + sector: 0 + sector: 1 + sector: 2 + sector: 3 + sector: 4 + } } } } diff --git a/src/formats/ibm1200_525.textpb b/src/formats/ibm1200_525.textpb index 8a619fbf..99bc2b01 100644 --- a/src/formats/ibm1200_525.textpb +++ b/src/formats/ibm1200_525.textpb @@ -34,7 +34,23 @@ encoder { trackdata { track_length_ms: 167 clock_rate_khz: 500 - sector_skew: "0123456789abcde" + sectors { + sector: 0 + sector: 1 + sector: 2 + sector: 3 + sector: 4 + sector: 5 + sector: 6 + sector: 7 + sector: 8 + sector: 9 + sector: 10 + sector: 11 + sector: 12 + sector: 13 + sector: 14 + } } } } diff --git a/src/formats/ibm1440.textpb b/src/formats/ibm1440.textpb index af9cdf1b..e3abc3f4 100644 --- a/src/formats/ibm1440.textpb +++ b/src/formats/ibm1440.textpb @@ -22,7 +22,26 @@ encoder { trackdata { track_length_ms: 200 clock_rate_khz: 500 - sector_skew: "0123456789abcdefgh" + sectors { + sector: 0 + sector: 1 + sector: 2 + sector: 3 + sector: 4 + sector: 5 + sector: 6 + sector: 7 + sector: 8 + sector: 9 + sector: 10 + sector: 11 + sector: 12 + sector: 13 + sector: 14 + sector: 15 + sector: 16 + sector: 17 + } } } } diff --git a/src/formats/ibm180_525.textpb b/src/formats/ibm180_525.textpb index 510bc380..536e9b76 100644 --- a/src/formats/ibm180_525.textpb +++ b/src/formats/ibm180_525.textpb @@ -23,7 +23,17 @@ encoder { trackdata { track_length_ms: 167 clock_rate_khz: 300 - sector_skew: "012345678" + sectors { + sector: 0 + sector: 1 + sector: 2 + sector: 3 + sector: 4 + sector: 5 + sector: 6 + sector: 7 + sector: 8 + } } } } diff --git a/src/formats/ibm360_525.textpb b/src/formats/ibm360_525.textpb index 9ce8ffbc..cd54daf9 100644 --- a/src/formats/ibm360_525.textpb +++ b/src/formats/ibm360_525.textpb @@ -23,7 +23,17 @@ encoder { trackdata { track_length_ms: 167 clock_rate_khz: 300 - sector_skew: "012345678" + sectors { + sector: 0 + sector: 1 + sector: 2 + sector: 3 + sector: 4 + sector: 5 + sector: 6 + sector: 7 + sector: 8 + } } } } diff --git a/src/formats/ibm720.textpb b/src/formats/ibm720.textpb index d9054908..69765369 100644 --- a/src/formats/ibm720.textpb +++ b/src/formats/ibm720.textpb @@ -22,7 +22,17 @@ encoder { trackdata { track_length_ms: 200 clock_rate_khz: 250 - sector_skew: "012345678" + sectors { + sector: 0 + sector: 1 + sector: 2 + sector: 3 + sector: 4 + sector: 5 + sector: 6 + sector: 7 + sector: 8 + } } } } diff --git a/src/formats/ibm720_525.textpb b/src/formats/ibm720_525.textpb index 8c70c52e..4770063f 100644 --- a/src/formats/ibm720_525.textpb +++ b/src/formats/ibm720_525.textpb @@ -22,7 +22,17 @@ encoder { trackdata { track_length_ms: 167 clock_rate_khz: 300 - sector_skew: "012345678" + sectors { + sector: 0 + sector: 1 + sector: 2 + sector: 3 + sector: 4 + sector: 5 + sector: 6 + sector: 7 + sector: 8 + } } } } From 29e8c99b4f9d05182e53090f9715e13d19618c9e Mon Sep 17 00:00:00 2001 From: David Given Date: Thu, 29 Jul 2021 00:02:46 +0200 Subject: [PATCH 2/4] Hopefully get the imagereader/imagewriter and IBM encoder/decoders using sector IDs. --- arch/ibm/decoder.cc | 2 +- arch/ibm/encoder.cc | 2 +- arch/ibm/ibm.proto | 2 -- lib/globals.h | 1 + lib/image.cc | 5 ++++- lib/image.h | 1 + lib/imagereader/imagereader.proto | 15 +++++++++++++- lib/imagereader/imgimagereader.cc | 28 ++++++++++++++++++++++++- lib/imagewriter/imagewriter.cc | 6 +++++- lib/imagewriter/imgimagewriter.cc | 34 +++++++++++++++++++++++++++++-- src/formats/acornadfs.textpb | 12 +++++++---- src/formats/acorndfs.textpb | 12 +++++++---- src/formats/amiga.textpb | 10 +++++++-- src/formats/ampro.textpb | 12 +++++++---- src/formats/atarist360.textpb | 17 ++++++++++++++-- src/formats/atarist370.textpb | 17 ++++++++++++++-- src/formats/atarist400.textpb | 17 ++++++++++++++-- src/formats/atarist410.textpb | 17 ++++++++++++++-- src/formats/atarist720.textpb | 17 ++++++++++++++-- src/formats/atarist740.textpb | 17 ++++++++++++++-- src/formats/atarist800.textpb | 17 ++++++++++++++-- src/formats/atarist820.textpb | 17 ++++++++++++++-- src/formats/brother120.textpb | 5 ++++- src/formats/brother240.textpb | 5 ++++- src/formats/commodore1581.textpb | 10 +++++++-- src/formats/eco1.textpb | 15 +++++++++++--- src/formats/hplif770.textpb | 5 ++++- src/formats/ibm1200_525.textpb | 7 +++++-- src/formats/ibm1440.textpb | 7 +++++-- src/formats/ibm180_525.textpb | 7 +++++-- src/formats/ibm360_525.textpb | 7 +++++-- src/formats/ibm720.textpb | 7 +++++-- src/formats/ibm720_525.textpb | 7 +++++-- src/formats/tids990.textpb | 5 ++++- 34 files changed, 304 insertions(+), 61 deletions(-) diff --git a/arch/ibm/decoder.cc b/arch/ibm/decoder.cc index 1bd0b9e1..3273e2f3 100644 --- a/arch/ibm/decoder.cc +++ b/arch/ibm/decoder.cc @@ -140,7 +140,7 @@ public: br.read_8(); /* skip ID byte */ _sector->logicalTrack = br.read_8(); _sector->logicalSide = br.read_8(); - _sector->logicalSector = br.read_8() - _config.sector_id_base(); + _sector->logicalSector = br.read_8(); _currentSectorSize = 1 << (br.read_8() + 7); uint16_t wantCrc = br.read_be16(); uint16_t gotCrc = crc16(CCITT_POLY, bytes.slice(0, _currentHeaderLength + 5)); diff --git a/arch/ibm/encoder.cc b/arch/ibm/encoder.cc index 4c04a1a0..993051bb 100644 --- a/arch/ibm/encoder.cc +++ b/arch/ibm/encoder.cc @@ -208,7 +208,7 @@ public: bw.write_8(idamUnencoded); bw.write_8(sectorData->logicalTrack); bw.write_8(sectorData->logicalSide); - bw.write_8(sectorData->logicalSector + trackdata.start_sector_id()); + bw.write_8(sectorData->logicalSector); bw.write_8(sectorSize); uint16_t crc = crc16(CCITT_POLY, header); bw.write_be16(crc); diff --git a/arch/ibm/ibm.proto b/arch/ibm/ibm.proto index afb19b27..016e93df 100644 --- a/arch/ibm/ibm.proto +++ b/arch/ibm/ibm.proto @@ -8,7 +8,6 @@ message IbmDecoderProto { repeated int32 sector = 1 [(help) = "require these sectors to exist for a good read"]; } - optional int32 sector_id_base = 1 [default = 1, (help) = "ID of first sector"]; optional bool ignore_side_byte = 2 [default = false, (help) = "ignore side byte in sector header"]; optional bool swap_sides = 4 [default = false, (help) = "swap side bytes when reading"]; optional SectorsProto sectors = 5 [(help) = "require these sectors to exist for a good read"]; @@ -27,7 +26,6 @@ message IbmEncoderProto { optional double track_length_ms = 1 [(help) = "length of track"]; optional int32 sector_size = 2 [default=512, (help) = "number of bytes per sector"]; optional bool emit_iam = 3 [default=true, (help) = "whether to emit an IAM record"]; - optional int32 start_sector_id = 4 [default=1, (help) = "ID of first sector"]; optional double clock_rate_khz = 5 [(help) = "data clock rate"]; optional bool use_fm = 6 [default=false, (help) = "whether to use FM encoding rather than MFM"]; optional int32 idam_byte = 7 [default=0x5554, (help) = "16-bit raw bit pattern of IDAM byte"]; diff --git a/lib/globals.h b/lib/globals.h index 88a31d86..499c19d3 100644 --- a/lib/globals.h +++ b/lib/globals.h @@ -11,6 +11,7 @@ #include #include #include +#include #if defined(_WIN32) || defined(__WIN32__) #include diff --git a/lib/image.cc b/lib/image.cc index 582bd771..70ba4948 100644 --- a/lib/image.cc +++ b/lib/image.cc @@ -36,6 +36,7 @@ const std::shared_ptr& Image::put(unsigned track, unsigned side, unsigne void Image::calculateSize() { _geometry = {}; + unsigned maxSector = 0; for (const auto& i : _sectors) { const auto& sector = i.second; @@ -43,9 +44,11 @@ void Image::calculateSize() { _geometry.numTracks = std::max(_geometry.numTracks, (unsigned)sector->logicalTrack+1); _geometry.numSides = std::max(_geometry.numSides, (unsigned)sector->logicalSide+1); - _geometry.numSectors = std::max(_geometry.numSectors, (unsigned)sector->logicalSector+1); + _geometry.firstSector = std::min(_geometry.firstSector, (unsigned)sector->logicalSector); + maxSector = std::max(maxSector, (unsigned)sector->logicalSector); _geometry.sectorSize = std::max(_geometry.sectorSize, (unsigned)sector->data.size()); } } + _geometry.numSectors = maxSector - _geometry.firstSector + 1; } diff --git a/lib/image.h b/lib/image.h index 1036da92..80aa5c70 100644 --- a/lib/image.h +++ b/lib/image.h @@ -5,6 +5,7 @@ struct Geometry { unsigned numTracks = 0; unsigned numSides = 0; + unsigned firstSector = UINT_MAX; unsigned numSectors = 0; unsigned sectorSize = 0; bool irregular = false; diff --git a/lib/imagereader/imagereader.proto b/lib/imagereader/imagereader.proto index 31af843d..0ff22d61 100644 --- a/lib/imagereader/imagereader.proto +++ b/lib/imagereader/imagereader.proto @@ -3,12 +3,25 @@ syntax = "proto2"; import "lib/common.proto"; message ImgInputOutputProto { + message SectorsProto { + repeated int32 sector = 1 [(help) = "sector ID"]; + } + + message SectorRangeProto { + optional int32 start_sector = 1 [default=0, (help) = "first sector ID"]; + optional int32 sector_count = 2 [default=1, (help) = "total number of sectors"]; + } + 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 sector_size = 3 [default=512, (help) = "number of bytes per sector"]; - optional int32 sectors = 4 [(help) = "number of sectors in this track"]; + + oneof sectors_oneof { + SectorsProto sectors = 4 [(help) = "use a list of sector IDs"]; + SectorRangeProto sector_range = 5 [(help) = "use a range of contiguous IDs"]; + } } repeated TrackdataProto trackdata = 4 [(help) = "per-track format information (repeatable)"]; diff --git a/lib/imagereader/imgimagereader.cc b/lib/imagereader/imgimagereader.cc index b8b45cf1..993b63fe 100644 --- a/lib/imagereader/imgimagereader.cc +++ b/lib/imagereader/imgimagereader.cc @@ -35,7 +35,7 @@ public: ImgInputOutputProto::TrackdataProto trackdata; getTrackFormat(trackdata, track, side); - for (int sectorId = 0; sectorId < trackdata.sectors(); sectorId++) + for (int sectorId : getSectors(trackdata)) { Bytes data(trackdata.sector_size()); inputFile.read((char*) data.begin(), data.size()); @@ -75,6 +75,32 @@ private: trackdata.MergeFrom(f); } } + + std::vector getSectors(const ImgInputOutputProto::TrackdataProto& trackdata) + { + std::vector sectors; + switch (trackdata.sectors_oneof_case()) + { + case ImgInputOutputProto::TrackdataProto::SectorsOneofCase::kSectors: + { + for (int sectorId : trackdata.sectors().sector()) + sectors.push_back(sectorId); + break; + } + + case ImgInputOutputProto::TrackdataProto::SectorsOneofCase::kSectorRange: + { + int sectorId = trackdata.sector_range().start_sector(); + for (int i=0; i ImageReader::createImgImageReader( diff --git a/lib/imagewriter/imagewriter.cc b/lib/imagewriter/imagewriter.cc index a3d664e9..ec0b1350 100644 --- a/lib/imagewriter/imagewriter.cc +++ b/lib/imagewriter/imagewriter.cc @@ -126,8 +126,12 @@ void ImageWriter::printMap(const Image& image) for (int side = 0; side < geometry.numSides; side++) { - for (int sectorId = 0; sectorId < geometry.numSectors; sectorId++) + int maxSector = geometry.firstSector + geometry.numSectors - 1; + for (int sectorId = 0; sectorId <= maxSector; sectorId++) { + if (sectorId < geometry.firstSector) + continue; + std::cout << fmt::format("{}.{:2} ", side, sectorId); for (int track = 0; track < geometry.numTracks; track++) { diff --git a/lib/imagewriter/imgimagewriter.cc b/lib/imagewriter/imgimagewriter.cc index 9238c2b6..1fa0b50a 100644 --- a/lib/imagewriter/imgimagewriter.cc +++ b/lib/imagewriter/imgimagewriter.cc @@ -34,10 +34,17 @@ public: ImgInputOutputProto::TrackdataProto trackdata; getTrackFormat(trackdata, track, side); - int numSectors = trackdata.has_sectors() ? trackdata.sectors() : geometry.numSectors; + auto sectors = getSectors(trackdata); + if (sectors.empty()) + { + int maxSector = geometry.firstSector + geometry.numSectors - 1; + for (int i=geometry.firstSector; i<=maxSector; i++) + sectors.push_back(i); + } + int sectorSize = trackdata.has_sector_size() ? trackdata.sector_size() : geometry.sectorSize; - for (int sectorId = 0; sectorId < numSectors; sectorId++) + for (int sectorId : sectors) { const auto& sector = image.get(track, side, sectorId); if (sector) @@ -67,6 +74,29 @@ private: trackdata.MergeFrom(f); } } + + std::vector getSectors(const ImgInputOutputProto::TrackdataProto& trackdata) + { + std::vector sectors; + switch (trackdata.sectors_oneof_case()) + { + case ImgInputOutputProto::TrackdataProto::SectorsOneofCase::kSectors: + { + for (int sectorId : trackdata.sectors().sector()) + sectors.push_back(sectorId); + break; + } + + case ImgInputOutputProto::TrackdataProto::SectorsOneofCase::kSectorRange: + { + int sectorId = trackdata.sector_range().start_sector(); + for (int i=0; i ImageWriter::createImgImageWriter( diff --git a/src/formats/acornadfs.textpb b/src/formats/acornadfs.textpb index 9a53b6db..24f32bbd 100644 --- a/src/formats/acornadfs.textpb +++ b/src/formats/acornadfs.textpb @@ -2,13 +2,17 @@ comment: 'Acorn ADFS L/D/E/F 640kB/800kB/1600kB 3.5" or 5.25" 80-track DS (ro)' image_writer { filename: "acornadfs.img" - img {} + img { + trackdata { + sector_range { + start_sector: 0 + } + } + } } decoder { - ibm { - sector_id_base: 0 - } + ibm {} } cylinders { diff --git a/src/formats/acorndfs.textpb b/src/formats/acorndfs.textpb index 5a15c8ca..71dc7fca 100644 --- a/src/formats/acorndfs.textpb +++ b/src/formats/acorndfs.textpb @@ -2,13 +2,17 @@ comment: 'Acorn DFS 100kB/200kB 3.5" or 5.25" 40- or 80-track SS (ro)' image_writer { filename: "acorndfs.img" - img {} + img { + trackdata { + sector_range { + start_sector: 0 + } + } + } } decoder { - ibm { - sector_id_base: 0 - } + ibm {} } cylinders { diff --git a/src/formats/amiga.textpb b/src/formats/amiga.textpb index 26d0b107..eb3084be 100644 --- a/src/formats/amiga.textpb +++ b/src/formats/amiga.textpb @@ -6,8 +6,11 @@ image_reader { tracks: 80 sides: 2 trackdata { - sectors: 11 sector_size: 512 + sector_range { + start_sector: 1 + sector_count: 11 + } } } } @@ -19,7 +22,10 @@ image_writer { sides: 2 trackdata { sector_size: 512 - sectors: 11 + sector_range { + start_sector: 1 + sector_count: 11 + } } } } diff --git a/src/formats/ampro.textpb b/src/formats/ampro.textpb index 8c547482..501cdbc2 100644 --- a/src/formats/ampro.textpb +++ b/src/formats/ampro.textpb @@ -2,13 +2,17 @@ comment: 'Ampro 400kB/800kB 5.25" 40/80 track SSDD/DSDD (ro)' image_writer { filename: "ampro.img" - img {} + img { + trackdata { + sector_range { + start_sector: 17 + } + } + } } decoder { - ibm { - sector_id_base: 17 - } + ibm {} } cylinders { diff --git a/src/formats/atarist360.textpb b/src/formats/atarist360.textpb index 9fce8aa6..13c24e30 100644 --- a/src/formats/atarist360.textpb +++ b/src/formats/atarist360.textpb @@ -6,15 +6,28 @@ image_reader { tracks: 80 sides: 1 trackdata { - sectors: 9 sector_size: 512 + sector_range { + start_sector: 0 + sector_count: 9 + } } } } image_writer { filename: "atarist360.st" - img {} + img { + tracks: 80 + sides: 1 + trackdata { + sector_size: 512 + sector_range { + start_sector: 0 + sector_count: 9 + } + } + } } encoder { diff --git a/src/formats/atarist370.textpb b/src/formats/atarist370.textpb index a50027ae..0f59b56f 100644 --- a/src/formats/atarist370.textpb +++ b/src/formats/atarist370.textpb @@ -6,15 +6,28 @@ image_reader { tracks: 82 sides: 1 trackdata { - sectors: 9 sector_size: 512 + sector_range { + start_sector: 0 + sector_count: 9 + } } } } image_writer { filename: "atarist370.st" - img {} + img { + tracks: 82 + sides: 1 + trackdata { + sector_size: 512 + sector_range { + start_sector: 0 + sector_count: 9 + } + } + } } encoder { diff --git a/src/formats/atarist400.textpb b/src/formats/atarist400.textpb index b2a10f26..ff5cac6a 100644 --- a/src/formats/atarist400.textpb +++ b/src/formats/atarist400.textpb @@ -6,15 +6,28 @@ image_reader { tracks: 80 sides: 1 trackdata { - sectors: 10 sector_size: 512 + sector_range { + start_sector: 0 + sector_count: 10 + } } } } image_writer { filename: "atarist400.st" - img {} + img { + tracks: 80 + sides: 1 + trackdata { + sector_size: 512 + sector_range { + start_sector: 0 + sector_count: 10 + } + } + } } encoder { diff --git a/src/formats/atarist410.textpb b/src/formats/atarist410.textpb index 81b21db6..0cf264fd 100644 --- a/src/formats/atarist410.textpb +++ b/src/formats/atarist410.textpb @@ -6,15 +6,28 @@ image_reader { tracks: 82 sides: 1 trackdata { - sectors: 10 sector_size: 512 + sector_range { + start_sector: 0 + sector_count: 10 + } } } } image_writer { filename: "atarist410.st" - img {} + img { + tracks: 82 + sides: 1 + trackdata { + sector_size: 512 + sector_range { + start_sector: 0 + sector_count: 10 + } + } + } } encoder { diff --git a/src/formats/atarist720.textpb b/src/formats/atarist720.textpb index a2d2189e..c46cfd81 100644 --- a/src/formats/atarist720.textpb +++ b/src/formats/atarist720.textpb @@ -6,15 +6,28 @@ image_reader { tracks: 80 sides: 2 trackdata { - sectors: 9 sector_size: 512 + sector_range { + start_sector: 0 + sector_count: 9 + } } } } image_writer { filename: "atarist720.st" - img {} + img { + tracks: 80 + sides: 2 + trackdata { + sector_size: 512 + sector_range { + start_sector: 0 + sector_count: 9 + } + } + } } encoder { diff --git a/src/formats/atarist740.textpb b/src/formats/atarist740.textpb index d4effd1e..8dadc210 100644 --- a/src/formats/atarist740.textpb +++ b/src/formats/atarist740.textpb @@ -6,15 +6,28 @@ image_reader { tracks: 82 sides: 2 trackdata { - sectors: 9 sector_size: 512 + sector_range { + start_sector: 0 + sector_count: 9 + } } } } image_writer { filename: "atarist740.st" - img {} + img { + tracks: 82 + sides: 2 + trackdata { + sector_size: 512 + sector_range { + start_sector: 0 + sector_count: 9 + } + } + } } encoder { diff --git a/src/formats/atarist800.textpb b/src/formats/atarist800.textpb index 29de3d57..46306d4f 100644 --- a/src/formats/atarist800.textpb +++ b/src/formats/atarist800.textpb @@ -6,15 +6,28 @@ image_reader { tracks: 80 sides: 2 trackdata { - sectors: 10 sector_size: 512 + sector_range { + start_sector: 0 + sector_count: 10 + } } } } image_writer { filename: "atarist800.st" - img {} + img { + tracks: 80 + sides: 2 + trackdata { + sector_size: 512 + sector_range { + start_sector: 0 + sector_count: 10 + } + } + } } encoder { diff --git a/src/formats/atarist820.textpb b/src/formats/atarist820.textpb index f8478028..e612d0ae 100644 --- a/src/formats/atarist820.textpb +++ b/src/formats/atarist820.textpb @@ -6,15 +6,28 @@ image_reader { tracks: 82 sides: 2 trackdata { - sectors: 10 sector_size: 512 + sector_range { + start_sector: 0 + sector_count: 10 + } } } } image_writer { filename: "atarist820.st" - img {} + img { + tracks: 82 + sides: 2 + trackdata { + sector_size: 512 + sector_range { + start_sector: 0 + sector_count: 10 + } + } + } } encoder { diff --git a/src/formats/brother120.textpb b/src/formats/brother120.textpb index b6b5713e..65beef8e 100644 --- a/src/formats/brother120.textpb +++ b/src/formats/brother120.textpb @@ -6,8 +6,11 @@ image_reader { tracks: 39 sides: 1 trackdata { - sectors: 12 sector_size: 256 + sector_range { + start_sector: 0 + sector_count: 12 + } } } } diff --git a/src/formats/brother240.textpb b/src/formats/brother240.textpb index 04a13c67..3506476f 100644 --- a/src/formats/brother240.textpb +++ b/src/formats/brother240.textpb @@ -6,8 +6,11 @@ image_reader { tracks: 78 sides: 1 trackdata { - sectors: 12 sector_size: 256 + sector_range { + start_sector: 0 + sector_count: 12 + } } } } diff --git a/src/formats/commodore1581.textpb b/src/formats/commodore1581.textpb index 5d5978ae..b770d41d 100644 --- a/src/formats/commodore1581.textpb +++ b/src/formats/commodore1581.textpb @@ -6,8 +6,11 @@ image_reader { tracks: 80 sides: 2 trackdata { - sectors: 10 sector_size: 512 + sector_range { + start_sector: 0 + sector_count: 10 + } } } } @@ -18,8 +21,11 @@ image_writer { tracks: 80 sides: 2 trackdata { - sectors: 10 sector_size: 512 + sector_range { + start_sector: 0 + sector_count: 10 + } } } } diff --git a/src/formats/eco1.textpb b/src/formats/eco1.textpb index e7d74f22..b5a6cac4 100644 --- a/src/formats/eco1.textpb +++ b/src/formats/eco1.textpb @@ -6,20 +6,29 @@ image_writer { tracks: 77 sides: 2 trackdata { - sectors: 16 sector_size: 512 + sector_range { + start_sector: 1 + sector_count: 16 + } } trackdata { track: 0 side: 0 - sectors: 26 sector_size: 128 + sector_range { + start_sector: 1 + sector_count: 26 + } } trackdata { track: 0 side: 1 - sectors: 26 sector_size: 256 + sector_range { + start_sector: 1 + sector_count: 26 + } } } } diff --git a/src/formats/hplif770.textpb b/src/formats/hplif770.textpb index 1e037719..4f7a2919 100644 --- a/src/formats/hplif770.textpb +++ b/src/formats/hplif770.textpb @@ -6,8 +6,11 @@ image_reader { tracks: 77 sides: 2 trackdata { - sectors: 5 sector_size: 1024 + sector_range { + start_sector: 0 + sector_count: 5 + } } } } diff --git a/src/formats/ibm1200_525.textpb b/src/formats/ibm1200_525.textpb index 99bc2b01..e3a68838 100644 --- a/src/formats/ibm1200_525.textpb +++ b/src/formats/ibm1200_525.textpb @@ -18,8 +18,11 @@ image_reader { tracks: 80 sides: 2 trackdata { - sectors: 15 sector_size: 512 + sector_range { + start_sector: 1 + sector_count: 15 + } } } } @@ -35,7 +38,6 @@ encoder { track_length_ms: 167 clock_rate_khz: 500 sectors { - sector: 0 sector: 1 sector: 2 sector: 3 @@ -50,6 +52,7 @@ encoder { sector: 12 sector: 13 sector: 14 + sector: 15 } } } diff --git a/src/formats/ibm1440.textpb b/src/formats/ibm1440.textpb index e3abc3f4..7710eea7 100644 --- a/src/formats/ibm1440.textpb +++ b/src/formats/ibm1440.textpb @@ -6,8 +6,11 @@ image_reader { tracks: 80 sides: 2 trackdata { - sectors: 18 sector_size: 512 + sector_range { + start_sector: 1 + sector_count: 18 + } } } } @@ -23,7 +26,6 @@ encoder { track_length_ms: 200 clock_rate_khz: 500 sectors { - sector: 0 sector: 1 sector: 2 sector: 3 @@ -41,6 +43,7 @@ encoder { sector: 15 sector: 16 sector: 17 + sector: 18 } } } diff --git a/src/formats/ibm180_525.textpb b/src/formats/ibm180_525.textpb index 536e9b76..b40e5bae 100644 --- a/src/formats/ibm180_525.textpb +++ b/src/formats/ibm180_525.textpb @@ -7,8 +7,11 @@ image_reader { sides: 1 physical_step: 2 trackdata { - sectors: 9 sector_size: 512 + sector_range { + start_sector: 1 + sector_count: 9 + } } } } @@ -24,7 +27,6 @@ encoder { track_length_ms: 167 clock_rate_khz: 300 sectors { - sector: 0 sector: 1 sector: 2 sector: 3 @@ -33,6 +35,7 @@ encoder { sector: 6 sector: 7 sector: 8 + sector: 9 } } } diff --git a/src/formats/ibm360_525.textpb b/src/formats/ibm360_525.textpb index cd54daf9..52d7f91e 100644 --- a/src/formats/ibm360_525.textpb +++ b/src/formats/ibm360_525.textpb @@ -7,8 +7,11 @@ image_reader { sides: 2 physical_step: 2 trackdata { - sectors: 9 sector_size: 512 + sector_range { + start_sector: 1 + sector_count: 9 + } } } } @@ -24,7 +27,6 @@ encoder { track_length_ms: 167 clock_rate_khz: 300 sectors { - sector: 0 sector: 1 sector: 2 sector: 3 @@ -33,6 +35,7 @@ encoder { sector: 6 sector: 7 sector: 8 + sector: 9 } } } diff --git a/src/formats/ibm720.textpb b/src/formats/ibm720.textpb index 69765369..900870cd 100644 --- a/src/formats/ibm720.textpb +++ b/src/formats/ibm720.textpb @@ -6,8 +6,11 @@ image_reader { tracks: 80 sides: 2 trackdata { - sectors: 9 sector_size: 512 + sector_range { + start_sector: 1 + sector_count: 9 + } } } } @@ -23,7 +26,6 @@ encoder { track_length_ms: 200 clock_rate_khz: 250 sectors { - sector: 0 sector: 1 sector: 2 sector: 3 @@ -32,6 +34,7 @@ encoder { sector: 6 sector: 7 sector: 8 + sector: 9 } } } diff --git a/src/formats/ibm720_525.textpb b/src/formats/ibm720_525.textpb index 4770063f..e5f78521 100644 --- a/src/formats/ibm720_525.textpb +++ b/src/formats/ibm720_525.textpb @@ -6,8 +6,11 @@ image_reader { tracks: 80 sides: 2 trackdata { - sectors: 9 sector_size: 512 + sector_range { + start_sector: 1 + sector_count: 9 + } } } } @@ -23,7 +26,6 @@ encoder { track_length_ms: 167 clock_rate_khz: 300 sectors { - sector: 0 sector: 1 sector: 2 sector: 3 @@ -32,6 +34,7 @@ encoder { sector: 6 sector: 7 sector: 8 + sector: 9 } } } diff --git a/src/formats/tids990.textpb b/src/formats/tids990.textpb index 577440bc..80db5efd 100644 --- a/src/formats/tids990.textpb +++ b/src/formats/tids990.textpb @@ -6,8 +6,11 @@ image_reader { tracks: 77 sides: 2 trackdata { - sectors: 26 sector_size: 288 + sector_range { + start_sector: 0 + sector_count: 26 + } } } } From 2ff50c0c56bbd035a87171ace0f028cae9729a7e Mon Sep 17 00:00:00 2001 From: David Given Date: Sun, 1 Aug 2021 20:46:48 +0200 Subject: [PATCH 3/4] Fix merge issues. --- lib/imagereader/imagereader.proto | 2 +- scripts/mac400_test.textpb | 46 +++++++++++++++++++++++------- scripts/mac800_test.textpb | 47 ++++++++++++++++++++++++------- src/formats/amiga.textpb | 4 +-- 4 files changed, 76 insertions(+), 23 deletions(-) diff --git a/lib/imagereader/imagereader.proto b/lib/imagereader/imagereader.proto index e608f49a..927087f8 100644 --- a/lib/imagereader/imagereader.proto +++ b/lib/imagereader/imagereader.proto @@ -21,7 +21,7 @@ message ImgInputOutputProto { oneof sectors_oneof { SectorsProto sectors = 4 [(help) = "use a list of sector IDs"]; - SectorRangeProto sector_range = 5 [(help) = "use a range of contiguous IDs"]; + SectorRangeProto sector_range = 6 [(help) = "use a range of contiguous IDs"]; } } diff --git a/scripts/mac400_test.textpb b/scripts/mac400_test.textpb index 015cec5e..0476399f 100644 --- a/scripts/mac400_test.textpb +++ b/scripts/mac400_test.textpb @@ -4,31 +4,44 @@ image_reader { sides: 1 trackdata { sector_size: 524 + sector_range { + start_sector: 0 + } } trackdata { track: 0 up_to_track: 15 - sectors: 12 + sector_range { + sector_count: 12 + } } trackdata { track: 16 up_to_track: 31 - sectors: 11 + sector_range { + sector_count: 11 + } } trackdata { track: 32 up_to_track: 47 - sectors: 10 + sector_range { + sector_count: 10 + } } trackdata { track: 48 up_to_track: 63 - sectors: 9 + sector_range { + sector_count: 9 + } } trackdata { track: 64 up_to_track: 79 - sectors: 8 + sector_range { + sector_count: 8 + } } } } @@ -39,31 +52,44 @@ image_writer { sides: 1 trackdata { sector_size: 524 + sector_range { + start_sector: 0 + } } trackdata { track: 0 up_to_track: 15 - sectors: 12 + sector_range { + sector_count: 12 + } } trackdata { track: 16 up_to_track: 31 - sectors: 11 + sector_range { + sector_count: 11 + } } trackdata { track: 32 up_to_track: 47 - sectors: 10 + sector_range { + sector_count: 10 + } } trackdata { track: 48 up_to_track: 63 - sectors: 9 + sector_range { + sector_count: 9 + } } trackdata { track: 64 up_to_track: 79 - sectors: 8 + sector_range { + sector_count: 8 + } } } } diff --git a/scripts/mac800_test.textpb b/scripts/mac800_test.textpb index 09e615ff..97ddc030 100644 --- a/scripts/mac800_test.textpb +++ b/scripts/mac800_test.textpb @@ -4,31 +4,44 @@ image_reader { sides: 2 trackdata { sector_size: 524 + sector_range { + start_sector: 0 + } } trackdata { track: 0 up_to_track: 15 - sectors: 12 + sector_range { + sector_count: 12 + } } trackdata { track: 16 up_to_track: 31 - sectors: 11 + sector_range { + sector_count: 11 + } } trackdata { track: 32 up_to_track: 47 - sectors: 10 + sector_range { + sector_count: 10 + } } trackdata { track: 48 up_to_track: 63 - sectors: 9 + sector_range { + sector_count: 9 + } } trackdata { track: 64 up_to_track: 79 - sectors: 8 + sector_range { + sector_count: 8 + } } } } @@ -39,32 +52,46 @@ image_writer { sides: 2 trackdata { sector_size: 524 + sector_range { + start_sector: 0 + } } trackdata { track: 0 up_to_track: 15 - sectors: 12 + sector_range { + sector_count: 12 + } } trackdata { track: 16 up_to_track: 31 - sectors: 11 + sector_range { + sector_count: 11 + } } trackdata { track: 32 up_to_track: 47 - sectors: 10 + sector_range { + sector_count: 10 + } } trackdata { track: 48 up_to_track: 63 - sectors: 9 + sector_range { + sector_count: 9 + } } trackdata { track: 64 up_to_track: 79 - sectors: 8 + sector_range { + sector_count: 8 + } } } } + diff --git a/src/formats/amiga.textpb b/src/formats/amiga.textpb index eb3084be..d112507f 100644 --- a/src/formats/amiga.textpb +++ b/src/formats/amiga.textpb @@ -8,7 +8,7 @@ image_reader { trackdata { sector_size: 512 sector_range { - start_sector: 1 + start_sector: 0 sector_count: 11 } } @@ -23,7 +23,7 @@ image_writer { trackdata { sector_size: 512 sector_range { - start_sector: 1 + start_sector: 0 sector_count: 11 } } From 1177ef6f8d8ee382c8446a18bd1ac35aef209305 Mon Sep 17 00:00:00 2001 From: David Given Date: Tue, 3 Aug 2021 22:31:33 +0200 Subject: [PATCH 4/4] Allow the LDBS data rate and recording mode to be specified, or (crudely) guessed if not. --- lib/imagewriter/imagewriter.proto | 24 +++++++++++++++++++++++- lib/imagewriter/ldbsimagewriter.cc | 22 +++++++++++++++++++--- 2 files changed, 42 insertions(+), 4 deletions(-) diff --git a/lib/imagewriter/imagewriter.proto b/lib/imagewriter/imagewriter.proto index 6561b431..6585e026 100644 --- a/lib/imagewriter/imagewriter.proto +++ b/lib/imagewriter/imagewriter.proto @@ -4,7 +4,29 @@ import "lib/imagereader/imagereader.proto"; import "lib/common.proto"; message D64OutputProto {} -message LDBSOutputProto {} + +message LDBSOutputProto { + enum DataRate { + RATE_HD = 0; + RATE_DD = 1; + RATE_SD = 2; + RATE_ED = 3; + RATE_GUESS = -1; + } + + enum RecordingMode { + RECMODE_MFM = 0; + RECMODE_FM = 1; + RECMODE_GCR_MAC = 0x12; + RECMODE_GCR_PRODOS = 0x14; + RECMODE_GCR_LISA = 0x22; + RECMODE_GUESS = -1; + } + + optional DataRate data_rate = 1 [default=RATE_GUESS, (help) = "data rate to use in LDBS file"]; + optional RecordingMode recording_mode = 2 [default=RECMODE_GUESS, (help) = "recording mode to use in LDBS file"]; +} + message DiskCopyOutputProto {} message NsiOutputProto {} diff --git a/lib/imagewriter/ldbsimagewriter.cc b/lib/imagewriter/ldbsimagewriter.cc index 71ed390b..89923a37 100644 --- a/lib/imagewriter/ldbsimagewriter.cc +++ b/lib/imagewriter/ldbsimagewriter.cc @@ -23,7 +23,7 @@ public: const Geometry geometry = image.getGeometry(); - std::cout << fmt::format("writing {} tracks, {} sides, {} sectors, {} bytes per sector", + std::cout << fmt::format("LDBS: writing {} tracks, {} sides, {} sectors, {} bytes per sector", geometry.numTracks, geometry.numSides, geometry.numSectors, geometry.sectorSize) << std::endl; @@ -33,6 +33,22 @@ public: int trackDirectorySize = 0; trackDirectoryWriter.write_le16(0); + LDBSOutputProto::DataRate dataRate = _config.ldbs().data_rate(); + if (dataRate == LDBSOutputProto::RATE_GUESS) + { + dataRate = (geometry.numSectors > 10) ? LDBSOutputProto::RATE_HD : LDBSOutputProto::RATE_DD; + if (geometry.sectorSize <= 256) + dataRate = LDBSOutputProto::RATE_SD; + std::cout << fmt::format("LDBS: guessing data rate as {}\n", LDBSOutputProto::DataRate_Name(dataRate)); + } + + LDBSOutputProto::RecordingMode recordingMode = _config.ldbs().recording_mode(); + if (recordingMode == LDBSOutputProto::RECMODE_GUESS) + { + recordingMode = LDBSOutputProto::RECMODE_MFM; + std::cout << fmt::format("LDBS: guessing recording mode as {}\n", LDBSOutputProto::RecordingMode_Name(recordingMode)); + } + for (int track = 0; track < geometry.numTracks; track++) { for (int side = 0; side < geometry.numSides; side++) @@ -51,8 +67,8 @@ public: trackHeaderWriter.write_le16(0x000C); /* offset of sector sideers */ trackHeaderWriter.write_le16(0x0012); /* length of each sector descriptor */ trackHeaderWriter.write_le16(actualSectors); - trackHeaderWriter.write_8(0); /* data rate unknown */ - trackHeaderWriter.write_8(0); /* recording mode unknown */ + trackHeaderWriter.write_8(dataRate); + trackHeaderWriter.write_8(recordingMode); trackHeaderWriter.write_8(0); /* format gap length */ trackHeaderWriter.write_8(0); /* filler byte */ trackHeaderWriter.write_le16(0); /* approximate track length */