diff --git a/lib/imagereader/imdimagereader.cc b/lib/imagereader/imdimagereader.cc index 1524eee6..c0da063d 100644 --- a/lib/imagereader/imdimagereader.cc +++ b/lib/imagereader/imdimagereader.cc @@ -12,44 +12,44 @@ #include #include -static unsigned getModulationandSpeed(uint8_t flags, bool *mfm) +static unsigned getModulationandSpeed(uint8_t flags, bool *fm) { switch (flags) { case 0: /* 500 kbps FM */ //clockRateKhz.setDefaultValue(250); - *mfm = false; + *fm = true; return 500; break; case 1: /* 300 kbps FM */ - *mfm = false; + *fm = true; return 300; break; case 2: /* 250 kbps FM */ - *mfm = false; + *fm = true; return 250; break; case 3: /* 500 kbps MFM */ - *mfm = true; + *fm = false; return 500; break; case 4: /* 300 kbps MFM */ - *mfm = true; + *fm = false; return 300; break; case 5: /* 250 kbps MFM */ - *mfm = true; + *fm = false; return 250; break; default: - Error() << fmt::format("don't understand IMD disks with this modulation and speed {}", flags); + Error() << fmt::format("IMD: don't understand IMD disks with this modulation and speed {}", flags); throw 0; } } @@ -88,9 +88,7 @@ static unsigned getSectorSize(uint8_t flags) class IMDImageReader : public ImageReader { public: - IMDImageReader(const ImageReaderProto& config): - ImageReader(config) - {} + IMDImageReader(const ImageReaderProto& config): ImageReader(config) {} std::unique_ptr readImage() /* @@ -115,9 +113,9 @@ public: //Read File std::ifstream inputFile(_config.filename(), std::ios::in | std::ios::binary); if (!inputFile.is_open()) - Error() << "cannot open input file"; + Error() << "IMD: cannot open input file"; //define some variables - bool mfm = false; //define coding just to show in comment for setting the right write parameters + bool fm = false; //define coding just to show in comment for setting the right write parameters inputFile.seekg(0, inputFile.end); int inputFileSize = inputFile.tellg(); // determine filesize inputFile.seekg(0, inputFile.beg); @@ -126,34 +124,41 @@ public: ByteReader br(data); std::unique_ptr image(new Image); TrackHeader header = {0, 0, 0, 0, 0}; + TrackHeader previousheader = {0, 0, 0, 0, 0}; unsigned n = 0; unsigned headerPtr = 0; unsigned Modulation_Speed = 0; unsigned sectorSize = 0; - std::string sector_skew; - int b; - unsigned char comment[8192]; //i choose a fixed value. dont know how to make dynamic arrays in C++. This should be enough - // Read comment - while ((b = br.read_8()) != EOF && b != 0x1A) - { - comment[n++] = (unsigned char)b; - } - headerPtr = n; //set pointer to after comment - comment[n] = '\0'; // null-terminate the string - //write comment to screen - Logger() << fmt::format("IMD: comment: {}", comment); + std::string sector_skew; + + int b; + std::string comment; + bool blnOptionalCylinderMap = false; + bool blnOptionalHeadMap = false; + int trackSectorSize = -1; + // Read comment + comment.clear(); + while ((b = br.read_8()) != EOF && b != END_OF_FILE) + { + comment.push_back(b); + n++; + } + headerPtr = n; //set pointer to after comment + Logger() << "Comment in IMD file:" + << fmt::format("{}", + comment); - //first read header for (;;) { if (headerPtr >= inputFileSize-1) { break; } + //first read header header.ModeValue = br.read_8(); headerPtr++; - Modulation_Speed = getModulationandSpeed(header.ModeValue, &mfm); + Modulation_Speed = getModulationandSpeed(header.ModeValue, &fm); header.track = br.read_8(); headerPtr++; header.Head = br.read_8(); @@ -164,87 +169,239 @@ public: headerPtr++; sectorSize = getSectorSize(header.SectorSize); - //Read optional track map To Do - - //Read optional sector head map To Do - + unsigned optionalsector_map[header.numSectors]; + //The Sector Cylinder Map has one entry for each sector, and contains the logical Cylinder ID for the corresponding sector in the Sector Numbering Map. + if (header.Head & SEC_CYL_MAP_FLAG) + { + //Read optional cylinder map + for (b = 0; b < header.numSectors; b++) + { + optionalsector_map[b] = br.read_8(); + headerPtr++; + } + blnOptionalCylinderMap = true; //set bool so we know there is an optional cylinder map + header.Head = header.Head^SEC_CYL_MAP_FLAG; //remove flag 10000001 ^ 10000000 = 00000001 and 10000000 ^ 10000000 = 00000000 + } + //Read optional sector head map + //The Sector Head Map has one entry for each sector, and contains the logical Head ID for the corresponding sector in the Sector Numbering Map. + unsigned optionalhead_map[header.numSectors]; + if (header.Head & SEC_HEAD_MAP_FLAG) + { + //Read optional sector head map + for (b = 0; b < header.numSectors; b++) + { + optionalhead_map[b] = br.read_8(); + headerPtr++; + } + blnOptionalHeadMap = true; //set bool so we know there is an optional head map + header.Head = header.Head^SEC_HEAD_MAP_FLAG; //remove flag 01000001 ^ 01000001 = 00000001 and 01000000 ^ 0100000 = 00000000 for writing sector head later + } //read sector numbering map - std::vector sector_map(header.numSectors); - sector_skew.clear(); - for (int b = 0; b < header.numSectors; b++) - { - sector_map[b] = br.read_8(); - headerPtr++; + sector_skew.clear(); + for (b = 0; b < header.numSectors; b++) + { + uint8_t t; + t = br.read_8(); + sector_skew.push_back(t); + headerPtr++; } + auto ibm = config.mutable_encoder()->mutable_ibm(); + + auto trackdata = ibm->add_trackdata(); + trackdata->set_target_clock_period_us(1e3 / Modulation_Speed); + trackdata->set_target_rotational_period_ms(200); + if (trackSectorSize < 0) + { + trackSectorSize = sectorSize; + // this is the first sector we've read, use it settings for + // per-track data + trackdata->set_track(header.track); + trackdata->set_head(header.Head); + trackdata->set_sector_size(sectorSize); + trackdata->set_use_fm(fm); + } + else if (trackSectorSize != sectorSize) + { + Error() << "IMD: multiple sector sizes per track are " + "currently unsupported"; + } + auto sectors = trackdata->mutable_sectors(); + //read the sectors for (int s = 0; s < header.numSectors; s++) { Bytes sectordata; - const auto& sector = image->put(header.track, header.Head, sector_map[s]); + Bytes compressed(sectorSize); + int SectorID; + SectorID = sector_skew[s]; + const auto& sector = image->put(header.track, header.Head, SectorID); + sector->logicalSector = SectorID; //read the status of the sector unsigned int Status_Sector = br.read_8(); headerPtr++; switch (Status_Sector) { - case 0: /* Sector data unavailable - could not be read */ - break; + /*fluxengine knows of a few sector statussen but not all of the statussen in IMD. + * // the statussen are in sector.h. Translation to fluxengine is as follows: + * Statussen fluxengine | Status IMD + *-------------------------------------------------------------------------------------------------------------------- + * OK, | 1, 2 (Normal data: (Sector Size) of (compressed) bytes follow) + * BAD_CHECKSUM, | 5, 6, 7, 8 + * MISSING, sector not found | 0 (Sector data unavailable - could not be read) + * DATA_MISSING, sector present but no data found | 3, 4 + * CONFLICT, | + * INTERNAL_ERROR | + */ + case 0: /* Sector data unavailable - could not be read */ - case 1: /* Normal data: (Sector Size) bytes follow */ - sectordata = br.read(sectorSize); - headerPtr += sectorSize; - sector->data.writer().append(sectordata); - break; + sector->status = Sector::MISSING; + break; - case 2: /* Compressed: All bytes in sector have same value (xx) */ - sectordata = br.read(1); - headerPtr++; - sector->data.writer().append(sectordata); + case 1: /* Normal data: (Sector Size) bytes follow */ + sectordata = br.read(sectorSize); + headerPtr += sectorSize; + sector->data.writer().append(sectordata); + sector->status = Sector::OK; + break; - for (int k = 1; k < sectorSize; k++) - { - //fill data till sector is full - sector->data.writer().append(sectordata); - } - break; + case 2: /* Compressed: All bytes in sector have same value (xx) */ + compressed[0] = br.read_8(); + headerPtr++; + for (int k = 1; k < sectorSize; k++) + { + //fill data till sector is full + br.seek(headerPtr); + compressed[k] = br.read_8(); + } + sector->data.writer().append(compressed); + sector->status = Sector::OK; + break; - case 3: /* Normal data with "Deleted-Data address mark" */ - case 4: /* Compressed with "Deleted-Data address mark"*/ - case 5: /* Normal data read with data error */ - case 6: /* Compressed read with data error" */ - case 7: /* Deleted data read with data error" */ - case 8: /* Compressed, Deleted read with data error" */ - default: - Error() << fmt::format("don't understand IMD disks with sector status {}", Status_Sector); - } - sector->status = Sector::OK; - sector->logicalTrack = header.track; - sector->physicalTrack = Mapper::remapTrackLogicalToPhysical(header.track); - sector->logicalSide = sector->physicalHead = header.Head; - sector->logicalSector = (sector_map[s]); + case 3: /* Normal data with "Deleted-Data address mark" */ + sector->status = Sector::DATA_MISSING; + sectordata = br.read(sectorSize); + headerPtr += sectorSize; + sector->data.writer().append(sectordata); + break; + + case 4: /* Compressed with "Deleted-Data address mark"*/ + compressed[0] = br.read_8(); + headerPtr++; + for (int k = 1; k < sectorSize; k++) + { + //fill data till sector is full + br.seek(headerPtr); + compressed[k] = br.read_8(); + } + sector->data.writer().append(compressed); + sector->status = Sector::DATA_MISSING; + break; + + case 5: /* Normal data read with data error*/ + sectordata = br.read(sectorSize); + headerPtr += sectorSize; + sector->status = Sector::BAD_CHECKSUM; + sector->data.writer().append(sectordata); + break; + + case 6: /* Compressed read with data error*/ + compressed[0] = br.read_8(); + headerPtr++; + for (int k = 1; k < sectorSize; k++) + { + //fill data till sector is full + br.seek(headerPtr); + compressed[k] = br.read_8(); + } + sector->data.writer().append(compressed); + sector->status = Sector::BAD_CHECKSUM; + break; + + case 7: /* Deleted data read with data error*/ + sectordata = br.read(sectorSize); + headerPtr += sectorSize; + sector->status = Sector::BAD_CHECKSUM; + sector->data.writer().append(sectordata); + break; + + case 8: /* Compressed, Deleted read with data error*/ + compressed[0] = br.read_8(); + headerPtr++; + for (int k = 1; k < sectorSize; k++) + { + //fill data till sector is full + br.seek(headerPtr); + compressed[k] = br.read_8(); + } + sector->data.writer().append(compressed); + sector->status = Sector::BAD_CHECKSUM; + break; + + default: + Error() << fmt::format("IMD: Don't understand IMD files with sector status {}, track {}, sector {}", Status_Sector, header.track, s); + } + if (blnOptionalCylinderMap) //there was een optional cylinder map. write it to the sector + //The Sector Cylinder Map has one entry for each sector, and contains the logical Cylinder ID for the corresponding sector in the Sector Numbering Map. + { + sector->physicalTrack = Mapper::remapTrackLogicalToPhysical(header.track); + sector->logicalTrack = optionalsector_map[s]; + blnOptionalCylinderMap = false; + } + else + { + sector->logicalTrack = header.track; + sector->physicalTrack = Mapper::remapTrackLogicalToPhysical(header.track); + } + if (blnOptionalHeadMap) //there was een optional head map. write it to the sector + //The Sector Head Map has one entry for each sector, and contains the logical Head ID for the corresponding sector in the Sector Numbering Map. + { + sector->physicalHead = header.Head; + sector->logicalSide = optionalhead_map[s]; + blnOptionalHeadMap = false; + } + else + { + sector->logicalSide = header.Head; + sector->physicalHead = header.Head; + } } } - //Write format detected in IMD image to screen to help user set the right write parameters + + if (config.encoder().format_case() != EncoderProto::FormatCase::FORMAT_NOT_SET) + Logger() << "IMD: overriding configured format"; - image->setGeometry({ - .numTracks = header.track, - .numSides = header.Head + 1U, - .numSectors = header.numSectors, - .sectorSize = sectorSize - }); - size_t headSize = header.numSectors * sectorSize; - size_t trackSize = headSize * (header.Head + 1); + image->calculateSize(); + const Geometry& geometry = image->getGeometry(); + size_t headSize = ((header.numSectors) * (sectorSize)); + size_t trackSize = (headSize * (header.Head + 1)); - Logger() << fmt::format("IMD: {} tracks, {} heads; {}; {} kbps; {} sectors; sectorsize {};" - " sectormap {}; {} kB total\n", - header.track, header.Head + 1, - mfm ? "MFM" : "FM", - Modulation_Speed, header.numSectors, sectorSize, sector_skew, (header.track+1) * trackSize / 1024); + + Logger() << "IMD: read " + << fmt::format("{} tracks, {} heads; {}; {} kbps; {} sectoren; sectorsize {}; {} kB total.", + header.track + 1, header.Head + 1, + fm ? "FM" : "MFM", + Modulation_Speed, header.numSectors, sectorSize, (header.track+1) * trackSize / 1024); + + if (!config.has_heads()) + { + auto* heads = config.mutable_heads(); + heads->set_start(0); + heads->set_end(geometry.numSides - 1); + } + + if (!config.has_tracks()) + { + auto* tracks = config.mutable_tracks(); + tracks->set_start(0); + tracks->set_end(geometry.numTracks - 1); + } return image; + } }; diff --git a/src/formats/hplif770.textpb b/src/formats/hplif770.textpb index e91101a8..3616172a 100644 --- a/src/formats/hplif770.textpb +++ b/src/formats/hplif770.textpb @@ -1,5 +1,10 @@ comment: 'Hewlett-Packard LIF 770kB 3.5" DSDD' +drive { + high_density: false + rotational_period_ms: 200 +} + image_reader { filename: "hplif770.img" img { @@ -8,7 +13,7 @@ image_reader { trackdata { sector_size: 1024 sector_range { - start_sector: 0 + start_sector: 1 sector_count: 5 } } @@ -23,13 +28,13 @@ encoder { sector_size: 1024 gap0: 80 gap2: 22 - gap3: 44 + gap3: 80 sectors { - sector: 0 sector: 1 sector: 2 sector: 3 sector: 4 + sector: 5 } } }