From 05552cc3e5f0b0d889e9fb220d509c4cdc6dfc71 Mon Sep 17 00:00:00 2001 From: David Given Date: Fri, 23 Jul 2021 23:21:30 +0200 Subject: [PATCH] Add a basic and largely untested TD0 reader. --- doc/using.md | 6 + lib/image.cc | 3 + lib/imagereader/imagereader.cc | 4 + lib/imagereader/imagereader.h | 1 + lib/imagereader/imagereader.proto | 2 + lib/imagereader/td0imagereader.cc | 202 ++++++++++++++++++++++++++++++ mkninja.sh | 1 + 7 files changed, 219 insertions(+) create mode 100644 lib/imagereader/td0imagereader.cc diff --git a/doc/using.md b/doc/using.md index e4d5c7c8..c32a5c59 100644 --- a/doc/using.md +++ b/doc/using.md @@ -246,6 +246,12 @@ FluxEngine also supports a number of file system image formats. When using the 4.2](https://en.wikipedia.org/wiki/Disk_Copy) image file, commonly used by Apple Macintosh emulators. + - `` + + Read a [Sydex Teledisk TD0 + file](https://web.archive.org/web/20210420224336/http://dunfield.classiccmp.org/img47321/teledisk.htm) + image file. Note that only uncompressed images are supported (so far). + - `` Read from a JV3 image file, commonly used by TRS-80 emulators. **Read diff --git a/lib/image.cc b/lib/image.cc index 582bd771..2e544c38 100644 --- a/lib/image.cc +++ b/lib/image.cc @@ -30,6 +30,9 @@ const std::shared_ptr& Image::put(unsigned track, unsigned side, unsigne { key_t key = std::make_tuple(track, side, sectorid); std::shared_ptr sector = std::make_shared(); + sector->logicalTrack = track; + sector->logicalSide = side; + sector->logicalSector = sectorid; return _sectors[key] = sector; } diff --git a/lib/imagereader/imagereader.cc b/lib/imagereader/imagereader.cc index 88749862..3b68555a 100644 --- a/lib/imagereader/imagereader.cc +++ b/lib/imagereader/imagereader.cc @@ -32,6 +32,9 @@ std::unique_ptr ImageReader::create(const ImageReaderProto& config) case ImageReaderProto::kNsi: return ImageReader::createNsiImageReader(config); + case ImageReaderProto::kTd0: + return ImageReader::createTd0ImageReader(config); + default: Error() << "bad input file config"; return std::unique_ptr(); @@ -50,6 +53,7 @@ void ImageReader::updateConfigForFilename(ImageReaderProto* proto, const std::st {".img", [&]() { proto->mutable_img(); }}, {".st", [&]() { proto->mutable_img(); }}, {".nsi", [&]() { proto->mutable_nsi(); }}, + {".td0", [&]() { proto->mutable_td0(); }}, }; for (const auto& it : formats) diff --git a/lib/imagereader/imagereader.h b/lib/imagereader/imagereader.h index 2f1d65d7..98aebc58 100644 --- a/lib/imagereader/imagereader.h +++ b/lib/imagereader/imagereader.h @@ -22,6 +22,7 @@ public: static std::unique_ptr createJv3ImageReader(const ImageReaderProto& config); static std::unique_ptr createIMDImageReader(const ImageReaderProto& config); static std::unique_ptr createNsiImageReader(const ImageReaderProto& config); + static std::unique_ptr createTd0ImageReader(const ImageReaderProto& config); public: virtual Image readImage() = 0; diff --git a/lib/imagereader/imagereader.proto b/lib/imagereader/imagereader.proto index 31af843d..526556a9 100644 --- a/lib/imagereader/imagereader.proto +++ b/lib/imagereader/imagereader.proto @@ -23,6 +23,7 @@ message ImdInputProto {} message Jv3InputProto {} message D64InputProto {} message NsiInputProto {} +message Td0InputProto {} message ImageReaderProto { optional string filename = 1 [(help) = "filename of input sector image"]; @@ -33,6 +34,7 @@ message ImageReaderProto { Jv3InputProto jv3 = 5; D64InputProto d64 = 6; NsiInputProto nsi = 7; + Td0InputProto td0 = 8; } } diff --git a/lib/imagereader/td0imagereader.cc b/lib/imagereader/td0imagereader.cc new file mode 100644 index 00000000..ad5d27a3 --- /dev/null +++ b/lib/imagereader/td0imagereader.cc @@ -0,0 +1,202 @@ +#include "globals.h" +#include "flags.h" +#include "sector.h" +#include "imagereader/imagereader.h" +#include "image.h" +#include "crc.h" +#include "fmt/format.h" +#include "lib/config.pb.h" +#include "fmt/format.h" +#include +#include +#include + +/* The best description of the Teledisk format I've found is available here: + * + * https://web.archive.org/web/20210420230238/http://dunfield.classiccmp.org/img47321/td0notes.txt + * + * Header: + * + * Signature (2 bytes); TD for normal, td for compressed + * Sequence (1 byte) + * Checksequence (1 byte) + * Teledisk version (1 byte) + * Data rate (1 byte) + * Drive type (1 byte) + * Stepping (1 byte) + * DOS allocation flag (1 byte) + * Sides (1 byte) + * Cyclic Redundancy Check (2 bytes) + */ + +enum +{ + TD0_ENCODING_RAW = 0, + TD0_ENCODING_REPEATED = 1, + TD0_ENCODING_RLE = 2, + + TD0_FLAG_DUPLICATE = 0x01, + TD0_FLAG_CRC_ERROR = 0x02, + TD0_FLAG_DELETED = 0x04, + TD0_FLAG_SKIPPED = 0x10, + TD0_FLAG_IDNODATA = 0x20, + TD0_FLAG_DATANOID = 0x40, +}; + +class Td0ImageReader : public ImageReader +{ +public: + Td0ImageReader(const ImageReaderProto& config): + ImageReader(config) + {} + + Image readImage() + { + std::ifstream inputFile(_config.filename(), std::ios::in | std::ios::binary); + if (!inputFile.is_open()) + Error() << "cannot open input file"; + + Bytes input; + input.writer() += inputFile; + ByteReader br(input); + + uint16_t signature = br.read_be16(); + br.skip(2); /* sequence and checksequence */ + uint8_t version = br.read_8(); + br.skip(2); /* data rate, drive type */ + uint8_t stepping = br.read_8(); + br.skip(1); /* sparse flag */ + uint8_t sides = (br.read_8() == 1) ? 1 : 2; + uint16_t headerCrc = br.read_le16(); + + uint16_t gotCrc = crc16(0xa097, 0, input.slice(0, 10)); + if (gotCrc != headerCrc) + Error() << "TD0: header checksum mismatch"; + if (signature != 0x5444) + Error() << "TD0: unsupported file type (only uncompressed files are supported for now)"; + + std::string comment = "(no comment)"; + if (stepping & 0x80) + { + /* Comment block */ + + br.skip(2); /* comment CRC */ + uint16_t length = br.read_le16(); + br.skip(6); /* timestamp */ + comment = br.read(length); + std::replace(comment.begin(), comment.end(), '\0', '\n'); + + /* Strip trailing newlines */ + + auto nl = std::find_if(comment.rbegin(), comment.rend(), + [](unsigned char ch) { return !std::isspace(ch); }); + comment.erase(nl.base(), comment.end()); + } + + std::cout << fmt::format("TD0: TeleDisk {:.1}: {}\n", + (double)version / 10.0, comment); + + Image image; + for (;;) + { + /* Read track header */ + + uint8_t sectorCount = br.read_8(); + if (sectorCount == 0xff) + break; + + uint8_t physicalCylinder = br.read_8(); + uint8_t physicalHead = br.read_8() & 1; + br.skip(1); /* crc */ + + for (int i = 0; i < sectorCount; i++) + { + /* Read sector */ + + uint8_t logicalTrack = br.read_8(); + uint8_t logicalSide = br.read_8(); + uint8_t sectorId = br.read_8(); + uint8_t sectorSizeEncoded = br.read_8(); + unsigned sectorSize = 128<status = Sector::OK; + sector->physicalCylinder = physicalCylinder; + sector->physicalHead = physicalHead; + sector->data = data.slice(0, sectorSize); + } + } + + //image.calculateSize(); + const Geometry& geometry = image.getGeometry(); + std::cout << fmt::format("TD0: found {} tracks, {} sides, {} kB total\n", + geometry.numTracks, geometry.numSides, + input.size() / 1024); + return image; + } +}; + +std::unique_ptr ImageReader::createTd0ImageReader(const ImageReaderProto& config) +{ + return std::unique_ptr(new Td0ImageReader(config)); +} + + + diff --git a/mkninja.sh b/mkninja.sh index 2470e77f..47d31f61 100644 --- a/mkninja.sh +++ b/mkninja.sh @@ -355,6 +355,7 @@ buildlibrary libbackend.a \ lib/imagereader/imgimagereader.cc \ lib/imagereader/jv3imagereader.cc \ lib/imagereader/nsiimagereader.cc \ + lib/imagereader/td0imagereader.cc \ lib/imagewriter/d64imagewriter.cc \ lib/imagewriter/diskcopyimagewriter.cc \ lib/imagewriter/imagewriter.cc \