Add minimal D88 image format reader.

This implements a subset of D88, only supporting the first floppy
in an image. It only supports images with equal sector sizes,
equal numbers of sectors per track, and no bad sectors.
This commit is contained in:
Thomas Daede
2021-12-01 06:25:02 -08:00
parent 91e794add1
commit 8a3cd14723
6 changed files with 157 additions and 0 deletions

View File

@@ -267,6 +267,17 @@ FluxEngine also supports a number of file system image formats. When using the
Read from a [FDI image file](https://www.pc98.org/project/doc/hdi.html),
commonly used by PC-98 emulators. **Read Only.**
- `<filename.d88>`
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.**
FluxEngine is currently limited to reading only the first floppy image in a
D88 file.
In addition, it only supports a limited subset of D88 features and will
reject files not in that subset.
- `<filename.ldbs>`
Write to a [LDBS generic image

View File

@@ -0,0 +1,138 @@
#include "globals.h"
#include "flags.h"
#include "sector.h"
#include "imagereader/imagereader.h"
#include "image.h"
#include "lib/config.pb.h"
#include "imagereader/imagereaderimpl.h"
#include "fmt/format.h"
#include <algorithm>
#include <iostream>
#include <fstream>
// reader based on this partial documentation of the D88 format:
// https://www.pc98.org/project/doc/d88.html
class D88ImageReader : public ImageReader
{
public:
D88ImageReader(const ImageReaderProto& config):
ImageReader(config)
{}
std::unique_ptr<Image> readImage()
{
std::ifstream inputFile(_config.filename(), std::ios::in | std::ios::binary);
if (!inputFile.is_open())
Error() << "cannot open input file";
Bytes header(0x24); // read first entry of track table as well
inputFile.read((char*) header.begin(), header.size());
// the DIM header technically has a bit field for sectors present,
// however it is currently ignored by this reader
std::string diskName = header.slice(0, 0x16);
if (diskName[0])
std::cout << "D88: disk name: " << diskName << "\n";
ByteReader headerReader(header);
// media flag indicates media density, currently unused
char mediaFlag = headerReader.seek(0x1b).read_8();
inputFile.seekg( 0, std::ios::end );
int fileSize = inputFile.tellg();
int diskSize = headerReader.seek(0x1c).read_le32();
if (diskSize > fileSize)
std::cout << "D88: found multiple disk images. Only using first\n";
int trackTableEnd = headerReader.seek(0x20).read_le32();
int trackTableSize = trackTableEnd - 0x20;
Bytes trackTable(trackTableSize);
inputFile.seekg(0x20);
inputFile.read((char*) trackTable.begin(), trackTable.size());
ByteReader trackTableReader(trackTable);
int diskSectorsPerTrack = -1;
int diskSectorSize = -1;
std::unique_ptr<Image> image(new Image);
for (int track = 0; track < trackTableSize / 4; track++)
{
int trackOffset = trackTableReader.seek(track * 4).read_le32();
if (trackOffset == 0) continue;
int currentTrackOffset = trackOffset;
int currentTrackCylinder = -1;
int currentSectorsInTrack = 0xffff; // don't know # of sectors until we read the first one
for (int sectorInTrack = 0; sectorInTrack < currentSectorsInTrack; sectorInTrack++){
Bytes sectorHeader(0x10);
inputFile.read((char*) sectorHeader.begin(), sectorHeader.size());
ByteReader sectorHeaderReader(sectorHeader);
char cylinder = sectorHeaderReader.seek(0).read_8();
char head = sectorHeaderReader.seek(1).read_8();
char sectorId = sectorHeaderReader.seek(2).read_8();
int sectorSize = 128 << sectorHeaderReader.seek(3).read_8();
int sectorsInTrack = sectorHeaderReader.seek(4).read_le16();
int mfm = sectorHeaderReader.seek(6).read_8();
int ddam = sectorHeaderReader.seek(7).read_8();
int fddStatusCode = sectorHeaderReader.seek(8).read_8();
// D88 provides much more sector information that is currently ignored
if (ddam != 0)
Error() << "D88: nonzero ddam currently unsupported";
if (fddStatusCode != 0)
Error() << "D88: nonzero fdd status codes are currently unsupported";
if (currentSectorsInTrack == 0xffff) {
currentSectorsInTrack = sectorsInTrack;
} else if (currentSectorsInTrack != sectorsInTrack) {
Error() << "D88: mismatched number of sectors in track";
}
if (diskSectorsPerTrack < 0) {
diskSectorsPerTrack = sectorsInTrack;
} else if (diskSectorsPerTrack != sectorsInTrack) {
Error() << "D88: varying numbers of sectors per track is currently unsupported";
}
if (diskSectorSize < 0) {
diskSectorSize = sectorSize;
} else if (diskSectorSize != sectorSize) {
Error() << "D88: variable sector sizes are currently unsupported";
}
if (mfm != 0)
Error() << "D88: Non-MFM sectors are currenty unsupported";
if (currentTrackCylinder < 0) {
currentTrackCylinder = cylinder;
} else if (currentTrackCylinder != cylinder) {
Error() << "D88: all sectors in a track must belong to the same cylinder";
}
Bytes data(sectorSize);
inputFile.read((char*) data.begin(), data.size());
const auto& sector = image->put(cylinder, head, sectorId);
sector->status = Sector::OK;
sector->logicalTrack = cylinder;
sector->physicalCylinder = cylinder;
sector->logicalSide = sector->physicalHead = head;
sector->logicalSector = sectorId;
sector->data = data;
}
}
image->calculateSize();
const Geometry& geometry = image->getGeometry();
std::cout << fmt::format("D88: read {} tracks, {} sides\n",
geometry.numTracks, geometry.numSides);
return image;
}
};
std::unique_ptr<ImageReader> ImageReader::createD88ImageReader(
const ImageReaderProto& config)
{
return std::unique_ptr<ImageReader>(new D88ImageReader(config));
}

View File

@@ -17,6 +17,9 @@ std::unique_ptr<ImageReader> ImageReader::create(const ImageReaderProto& config)
case ImageReaderProto::kDim:
return ImageReader::createDimImageReader(config);
case ImageReaderProto::kD88:
return ImageReader::createD88ImageReader(config);
case ImageReaderProto::kFdi:
return ImageReader::createFdiImageReader(config);
@@ -55,6 +58,7 @@ void ImageReader::updateConfigForFilename(ImageReaderProto* proto, const std::st
{".jv3", [&]() { proto->mutable_jv3(); }},
{".d64", [&]() { proto->mutable_d64(); }},
{".d81", [&]() { proto->mutable_img(); }},
{".d88", [&]() { proto->mutable_d88(); }},
{".dim", [&]() { proto->mutable_dim(); }},
{".diskcopy", [&]() { proto->mutable_diskcopy(); }},
{".fdi", [&]() { proto->mutable_fdi(); }},

View File

@@ -26,6 +26,7 @@ public:
static std::unique_ptr<ImageReader> createTd0ImageReader(const ImageReaderProto& config);
static std::unique_ptr<ImageReader> createDimImageReader(const ImageReaderProto& config);
static std::unique_ptr<ImageReader> createFdiImageReader(const ImageReaderProto& config);
static std::unique_ptr<ImageReader> createD88ImageReader(const ImageReaderProto& config);
public:
virtual std::unique_ptr<Image> readImage() = 0;

View File

@@ -40,6 +40,7 @@ message NsiInputProto {}
message Td0InputProto {}
message DimInputProto {}
message FdiInputProto {}
message D88InputProto {}
message ImageReaderProto {
optional string filename = 1 [(help) = "filename of input sector image"];
@@ -53,6 +54,7 @@ message ImageReaderProto {
Td0InputProto td0 = 8;
DimInputProto dim = 9;
FdiInputProto fdi = 10;
D88InputProto d88 = 11;
}
}

View File

@@ -374,6 +374,7 @@ buildlibrary libbackend.a \
lib/imagereader/td0imagereader.cc \
lib/imagereader/dimimagereader.cc \
lib/imagereader/fdiimagereader.cc \
lib/imagereader/d88imagereader.cc \
lib/imagewriter/d64imagewriter.cc \
lib/imagewriter/diskcopyimagewriter.cc \
lib/imagewriter/imagewriter.cc \