diff --git a/lib/bytes.h b/lib/bytes.h index 6f8c2554..0a5d82c9 100644 --- a/lib/bytes.h +++ b/lib/bytes.h @@ -1,6 +1,8 @@ #ifndef BYTES_H #define BYTES_H +#include + class ByteReader; class ByteWriter; @@ -10,6 +12,7 @@ public: Bytes(); Bytes(unsigned size); Bytes(const uint8_t* ptr, size_t len); + Bytes(const std::string& data); Bytes(std::initializer_list data); Bytes(std::shared_ptr> data); Bytes(std::shared_ptr> data, unsigned start, unsigned end); @@ -271,6 +274,16 @@ public: ByteWriter& operator += (std::istream& stream); + ByteWriter& append(const char* data) + { + return *this += Bytes((const uint8_t*)data, strlen(data)); + } + + ByteWriter& append(const std::string& data) + { + return *this += Bytes(data); + } + ByteWriter& append(const Bytes data) { return *this += data; diff --git a/lib/imagewriter/diskcopyimagewriter.cc b/lib/imagewriter/diskcopyimagewriter.cc new file mode 100644 index 00000000..65a3e36f --- /dev/null +++ b/lib/imagewriter/diskcopyimagewriter.cc @@ -0,0 +1,169 @@ +#include "globals.h" +#include "flags.h" +#include "dataspec.h" +#include "sector.h" +#include "sectorset.h" +#include "imagewriter/imagewriter.h" +#include "fmt/format.h" +#include "ldbs.h" +#include +#include +#include + +static const char LABEL[] = "FluxEngine image"; + +static void write_and_update_checksum(ByteWriter& bw, uint32_t& checksum, const Bytes& data) +{ + ByteReader br(data); + while (!br.eof()) + { + uint32_t i = br.read_be16(); + checksum += i; + checksum = (checksum >> 1) | (checksum << 31); + bw.write_be16(i); + } +} + +class DiskCopyImageWriter : public ImageWriter +{ +public: + DiskCopyImageWriter(const SectorSet& sectors, const ImageSpec& spec): + ImageWriter(sectors, spec) + {} + + void writeImage() + { + bool mfm = false; + + if (spec.bytes == 524) + { + /* GCR disk */ + } + else if (spec.bytes == 512) + { + /* MFM disk */ + mfm = true; + } + else + Error() << "this image is not compatible with the DiskCopy 4.2 format"; + + + std::cout << "writing DiskCopy 4.2 image\n" + << fmt::format("{} tracks, {} heads, {} sectors, {} bytes per sector; {}\n", + spec.cylinders, spec.heads, spec.sectors, spec.bytes, + mfm ? "MFM" : "GCR"); + + auto sectors_per_track = [&](int track) -> int + { + if (mfm) + return spec.sectors; + + if (track < 16) + return 12; + if (track < 32) + return 11; + if (track < 48) + return 10; + if (track < 64) + return 9; + return 8; + }; + + Bytes image; + ByteWriter bw(image); + + /* Write the actual sectr data. */ + + uint32_t dataChecksum = 0; + uint32_t tagChecksum = 0; + uint32_t offset = 0x54; + uint32_t sectorDataStart = offset; + for (int track = 0; track < spec.cylinders; track++) + { + for (int head = 0; head < spec.heads; head++) + { + int sectorCount = sectors_per_track(track); + for (int sectorId = 0; sectorId < sectorCount; sectorId++) + { + const auto& sector = sectors.get(track, head, sectorId); + if (sector) + { + bw.seek(offset); + write_and_update_checksum(bw, dataChecksum, sector->data.slice(0, 512)); + } + offset += 512; + } + } + } + uint32_t sectorDataEnd = offset; + if (!mfm) + { + for (int track = 0; track < spec.cylinders; track++) + { + for (int head = 0; head < spec.heads; head++) + { + int sectorCount = sectors_per_track(track); + for (int sectorId = 0; sectorId < sectorCount; sectorId++) + { + const auto& sector = sectors.get(track, head, sectorId); + if (sector) + { + bw.seek(offset); + write_and_update_checksum(bw, tagChecksum, sector->data.slice(512, 12)); + } + offset += 12; + } + } + } + } + uint32_t tagDataEnd = offset; + + /* Write the header. */ + + uint8_t encoding; + uint8_t format; + if (mfm) + { + format = 0x22; + if (spec.sectors == 18) + encoding = 3; + else + encoding = 2; + } + else + { + if (spec.heads == 2) + { + encoding = 1; + format = 0x22; + } + else + { + encoding = 0; + format = 0x02; + } + } + + bw.seek(0); + bw.write_8(sizeof(LABEL)); + bw.append(LABEL); + bw.seek(0x40); + bw.write_be32(sectorDataEnd - sectorDataStart); /* data size */ + bw.write_be32(tagDataEnd - sectorDataEnd); /* tag size */ + bw.write_be32(dataChecksum); /* data checksum */ + bw.write_be32(tagChecksum); /* tag checksum */ + bw.write_8(encoding); /* encoding */ + bw.write_8(format); /* format byte */ + bw.write_be16(0x0100); /* magic number */ + + image.writeToFile(spec.filename); + } +}; + +std::unique_ptr ImageWriter::createDiskCopyImageWriter( + const SectorSet& sectors, const ImageSpec& spec) +{ + return std::unique_ptr(new DiskCopyImageWriter(sectors, spec)); +} + + diff --git a/lib/imagewriter/imagewriter.cc b/lib/imagewriter/imagewriter.cc index 35ce7043..e0f9fec3 100644 --- a/lib/imagewriter/imagewriter.cc +++ b/lib/imagewriter/imagewriter.cc @@ -13,6 +13,7 @@ std::map ImageWriter::formats = {".adf", ImageWriter::createImgImageWriter}, {".d64", ImageWriter::createD64ImageWriter}, {".d81", ImageWriter::createImgImageWriter}, + {".diskcopy", ImageWriter::createDiskCopyImageWriter}, {".img", ImageWriter::createImgImageWriter}, {".ldbs", ImageWriter::createLDBSImageWriter}, }; diff --git a/lib/imagewriter/imagewriter.h b/lib/imagewriter/imagewriter.h index 7d40e1f8..1a9b2569 100644 --- a/lib/imagewriter/imagewriter.h +++ b/lib/imagewriter/imagewriter.h @@ -29,6 +29,8 @@ private: const SectorSet& sectors, const ImageSpec& spec); static std::unique_ptr createD64ImageWriter( const SectorSet& sectors, const ImageSpec& spec); + static std::unique_ptr createDiskCopyImageWriter( + const SectorSet& sectors, const ImageSpec& spec); static Constructor findConstructor(const ImageSpec& spec); diff --git a/mkninja.sh b/mkninja.sh index e6c1e1b2..f4e02b44 100644 --- a/mkninja.sh +++ b/mkninja.sh @@ -154,6 +154,7 @@ buildlibrary libbackend.a \ lib/imagereader/imagereader.cc \ lib/imagereader/imgimagereader.cc \ lib/imagewriter/d64imagewriter.cc \ + lib/imagewriter/diskcopyimagewriter.cc \ lib/imagewriter/imagewriter.cc \ lib/imagewriter/imgimagewriter.cc \ lib/imagewriter/ldbsimagewriter.cc \