diff --git a/doc/using.md b/doc/using.md index d462351e..f205d4c6 100644 --- a/doc/using.md +++ b/doc/using.md @@ -274,14 +274,19 @@ FluxEngine also supports a number of file system image formats. When using the - `` Read from a [D88 image file](https://www.pc98.org/project/doc/d88.html), - commonly used by various Japanese PC emulators, including the NEC PC-88. **Read only.** + commonly used by various Japanese PC emulators, including the NEC PC-88. FluxEngine is currently limited to reading only the first floppy image in a - D88 file. + D88 file. When writing, a single unnamed floppy will be created + within the image file. The D88 reader should be used with the `ibm` profile and will override most encoding parameters on a track-by-track basis. + The D88 writer should likewise be used with the `ibm` profile in most + circumstances as it can represent arbitrary sector layouts as read + from the floppy. + - `` Read from a [NFD r0 image file](https://www.pc98.org/project/doc/nfdr0.html), diff --git a/lib/imagewriter/d88imagewriter.cc b/lib/imagewriter/d88imagewriter.cc new file mode 100644 index 00000000..93bd1486 --- /dev/null +++ b/lib/imagewriter/d88imagewriter.cc @@ -0,0 +1,113 @@ +#include "globals.h" +#include "flags.h" +#include "sector.h" +#include "imagewriter/imagewriter.h" +#include "image.h" +#include "lib/config.pb.h" +#include "imginputoutpututils.h" +#include "fmt/format.h" +#include "logger.h" +#include +#include +#include + +class D88ImageWriter : public ImageWriter +{ +public: + D88ImageWriter(const ImageWriterProto& config): + ImageWriter(config) + {} + + void writeImage(const Image& image) + { + const Geometry geometry = image.getGeometry(); + + int tracks = geometry.numTracks; + int sides = geometry.numSides; + + std::ofstream outputFile(_config.filename(), std::ios::out | std::ios::binary); + if (!outputFile.is_open()) + Error() << "cannot open output file"; + + Bytes header; + ByteWriter headerWriter(header); + for(int i = 0; i < 26; i++) { + headerWriter.write_8(0x0); // image name + reserved bytes + } + headerWriter.write_8(0x00); // not write protected + if (geometry.numTracks > 42) { + headerWriter.write_8(0x20); // 2HD + } else { + headerWriter.write_8(0x00); // 2D + } + headerWriter.write_le32(0); // disk size (will be overridden at the end of writing) + for (int i = 0; i < 164; i++) { + headerWriter.write_le32(0); // track pointer (will be overridden in track loop) + } + header.writeTo(outputFile); + + uint32_t trackOffset = 688; + + for (int track = 0; track < geometry.numTracks * geometry.numSides; track++) + { + headerWriter.seek(0x20 + 4 * track); + headerWriter.write_le32(trackOffset); + int side = track & 1; + std::vector> sectors; + for (int sectorId = 0; sectorId < geometry.numSectors; sectorId++) + { + const auto& sector = image.get(track >> 1, side, sectorId); + if (sector) + { + sectors.push_back(sector); + } + } + std::sort(begin(sectors), + end(sectors), + [](std::shared_ptr a, std::shared_ptr b) { return a->position < b->position; }); + for (auto& sector : sectors) + { + Bytes sectorBytes; + ByteWriter sectorWriter(sectorBytes); + sectorWriter.write_8(sector->logicalTrack); + sectorWriter.write_8(sector->logicalSide); + sectorWriter.write_8(sector->logicalSector); + sectorWriter.write_8(24 - std::countl_zero(uint32_t(sector->data.size()))); + sectorWriter.write_le16(sectors.size()); + sectorWriter.write_8(0x00); // always write mfm + sectorWriter.write_8(0x00); // always write not deleted data + if (sector->status == Sector::Status::BAD_CHECKSUM) { + sectorWriter.write_8(0xB0); + } else { + sectorWriter.write_8(0x00); + } + sectorWriter.write_8(0x00); // reserved + sectorWriter.write_8(0x00); + sectorWriter.write_8(0x00); + sectorWriter.write_8(0x00); + sectorWriter.write_8(0x00); + sectorWriter.write_le16(sector->data.size()); + sectorBytes.writeTo(outputFile); + sector->data.writeTo(outputFile); + trackOffset += sectorBytes.size(); + trackOffset += sector->data.size(); + } + } + + headerWriter.seek(0x1c); + headerWriter.write_le32(outputFile.tellp()); + outputFile.seekp(0); + header.writeTo(outputFile); + + Logger() << fmt::format("D88: wrote {} tracks, {} sides, {} kB total", + tracks, sides, + outputFile.tellp() / 1024); + } + +}; + +std::unique_ptr ImageWriter::createD88ImageWriter( + const ImageWriterProto& config) +{ + return std::unique_ptr(new D88ImageWriter(config)); +} diff --git a/lib/imagewriter/imagewriter.cc b/lib/imagewriter/imagewriter.cc index 56d66141..dcf9639a 100644 --- a/lib/imagewriter/imagewriter.cc +++ b/lib/imagewriter/imagewriter.cc @@ -32,6 +32,9 @@ std::unique_ptr ImageWriter::create(const ImageWriterProto& config) case ImageWriterProto::kRaw: return ImageWriter::createRawImageWriter(config); + case ImageWriterProto::kD88: + return ImageWriter::createD88ImageWriter(config); + default: Error() << "bad output image config"; return std::unique_ptr(); @@ -45,6 +48,7 @@ void ImageWriter::updateConfigForFilename(ImageWriterProto* proto, const std::st {".adf", [](auto* proto) { proto->mutable_img(); }}, {".d64", [](auto* proto) { proto->mutable_d64(); }}, {".d81", [](auto* proto) { proto->mutable_img(); }}, + {".d88", [](auto* proto) { proto->mutable_d88(); }}, {".diskcopy", [](auto* proto) { proto->mutable_diskcopy(); }}, {".dsk", [](auto* proto) { proto->mutable_img(); }}, {".img", [](auto* proto) { proto->mutable_img(); }}, diff --git a/lib/imagewriter/imagewriter.h b/lib/imagewriter/imagewriter.h index 143c4af9..32eb7225 100644 --- a/lib/imagewriter/imagewriter.h +++ b/lib/imagewriter/imagewriter.h @@ -20,6 +20,7 @@ public: static std::unique_ptr createLDBSImageWriter(const ImageWriterProto& config); static std::unique_ptr createNsiImageWriter(const ImageWriterProto& config); static std::unique_ptr createRawImageWriter(const ImageWriterProto& config); + static std::unique_ptr createD88ImageWriter(const ImageWriterProto& config); public: void printMap(const Image& sectors); diff --git a/lib/imagewriter/imagewriter.proto b/lib/imagewriter/imagewriter.proto index 22490fbf..aa3beba5 100644 --- a/lib/imagewriter/imagewriter.proto +++ b/lib/imagewriter/imagewriter.proto @@ -30,6 +30,7 @@ message LDBSOutputProto { message DiskCopyOutputProto {} message NsiOutputProto {} message RawOutputProto {} +message D88OutputProto {} message ImageWriterProto { optional string filename = 1 [(help) = "filename of output sector image"]; @@ -40,6 +41,7 @@ message ImageWriterProto { DiskCopyOutputProto diskcopy = 5; NsiOutputProto nsi = 6; RawOutputProto raw = 7; + D88OutputProto d88 = 8; } } diff --git a/mkninja.sh b/mkninja.sh index 0b29a17e..99352c9e 100644 --- a/mkninja.sh +++ b/mkninja.sh @@ -466,6 +466,7 @@ buildlibrary libbackend.a \ lib/imagereader/fdiimagereader.cc \ lib/imagereader/d88imagereader.cc \ lib/imagewriter/d64imagewriter.cc \ + lib/imagewriter/d88imagewriter.cc \ lib/imagewriter/diskcopyimagewriter.cc \ lib/imagewriter/imagewriter.cc \ lib/imagewriter/imgimagewriter.cc \