openMSX
DMKDiskImage.cc
Go to the documentation of this file.
1 #include "DMKDiskImage.hh"
2 #include "RawTrack.hh"
3 #include "DiskExceptions.hh"
4 #include "File.hh"
5 #include "FilePool.hh"
6 #include "likely.hh"
7 #include "one_of.hh"
8 #include "ranges.hh"
9 #include <cassert>
10 
11 namespace openmsx {
12 
13 struct DmkHeader
14 {
16  byte numTracks;
17  byte trackLen[2];
18  byte flags;
19  byte reserved[7];
20  byte format[4];
21 };
22 static_assert(sizeof(DmkHeader) == 16);
23 
24 constexpr byte FLAG_SINGLE_SIDED = 0x10;
25 constexpr unsigned IDAM_FLAGS_MASK = 0xC000;
26 constexpr unsigned FLAG_MFM_SECTOR = 0x8000;
27 
28 
29 static bool isValidDmkHeader(const DmkHeader& header)
30 {
31  if (header.writeProtected != one_of(0x00, 0xff)) {
32  return false;
33  }
34  unsigned trackLen = header.trackLen[0] + 256 * header.trackLen[1];
35  if (trackLen >= 0x4000) return false; // too large track length
36  if (trackLen <= 128) return false; // too small
37  if (header.flags & ~0xd0) return false; // unknown flag set
38  return ranges::all_of(header.reserved, [](auto& r) { return r == 0; }) &&
39  ranges::all_of(header.format, [](auto& f) { return f == 0; });
40 }
41 
42 DMKDiskImage::DMKDiskImage(Filename filename, std::shared_ptr<File> file_)
43  : Disk(std::move(filename))
44  , file(std::move(file_))
45 {
46  DmkHeader header;
47  file->seek(0);
48  file->read(&header, sizeof(header));
49  if (!isValidDmkHeader(header)) {
50  throw MSXException("Not a DMK image");
51  }
52 
53  numTracks = header.numTracks;
54  dmkTrackLen = header.trackLen[0] + 256 * header.trackLen[1] - 128;
55  singleSided = (header.flags & FLAG_SINGLE_SIDED) != 0;;
56  writeProtected = header.writeProtected == 0xff;
57 
58  // TODO should we print a warning when dmkTrackLen is too far from the
59  // ideal value RawTrack::SIZE? This might indicate the disk image
60  // was not a 3.5" DD disk image and data will be lost on either
61  // read or write.
62 }
63 
64 void DMKDiskImage::seekTrack(byte track, byte side)
65 {
66  unsigned t = singleSided ? track : (2 * track + side);
67  file->seek(sizeof(DmkHeader) + t * (dmkTrackLen + 128));
68 }
69 
70 void DMKDiskImage::readTrack(byte track, byte side, RawTrack& output)
71 {
72  assert(side < 2);
73  output.clear(dmkTrackLen);
74  if ((singleSided && side) || (track >= numTracks)) {
75  // no such side/track, only clear output
76  return;
77  }
78 
79  seekTrack(track, side);
80 
81  // Read idam data (still needs to be converted).
82  byte idamBuf[2 * 64];
83  file->read(idamBuf, sizeof(idamBuf));
84 
85  // Read raw track data.
86  file->read(output.getRawBuffer(), dmkTrackLen);
87 
88  // Convert idam data into an easier to work with internal format.
89  int lastIdam = -1;
90  for (int i = 0; i < 64; ++i) {
91  unsigned idx = idamBuf[2 * i + 0] + 256 * idamBuf[2 * i + 1];
92  if (idx == 0) break; // end of table reached
93 
94  if ((idx & IDAM_FLAGS_MASK) != FLAG_MFM_SECTOR) {
95  // single density (FM) sector not yet supported, ignore
96  continue;
97  }
98  idx &= ~IDAM_FLAGS_MASK; // clear flags
99 
100  if (idx < 128) {
101  // Invalid IDAM offset, ignore
102  continue;
103  }
104  idx -= 128;
105  if (idx >= dmkTrackLen) {
106  // Invalid IDAM offset, ignore
107  continue;
108  }
109 
110  if (int(idx) <= lastIdam) {
111  // Invalid IDAM offset:
112  // must be strictly bigger than previous, ignore
113  continue;
114  }
115 
116  output.addIdam(idx);
117  lastIdam = idx;
118  }
119 }
120 
121 void DMKDiskImage::writeTrackImpl(byte track, byte side, const RawTrack& input)
122 {
123  assert(side < 2);
124  if (singleSided && (side != 0)) {
125  // second side on a single-side image, ignore write.
126  // TODO possible enhancement: convert to double sided
127  return;
128  }
129  if (unlikely(numTracks <= track)) {
130  extendImageToTrack(track);
131  }
132  doWriteTrack(track, side, input);
133 }
134 
135 void DMKDiskImage::doWriteTrack(byte track, byte side, const RawTrack& input)
136 {
137  seekTrack(track, side);
138 
139  // Write idam table.
140  byte idamOut[2 * 64] = {}; // zero-initialize
141  const auto& idamIn = input.getIdamBuffer();
142  for (int i = 0; i < std::min(64, int(idamIn.size())); ++i) {
143  int t = (idamIn[i] + 128) | FLAG_MFM_SECTOR;
144  idamOut[2 * i + 0] = t & 0xff;
145  idamOut[2 * i + 1] = t >> 8;
146  }
147  file->write(idamOut, sizeof(idamOut));
148 
149  // Write raw track data.
150  assert(input.getLength() == dmkTrackLen);
151  file->write(input.getRawBuffer(), dmkTrackLen);
152 }
153 
154 void DMKDiskImage::extendImageToTrack(byte track)
155 {
156  // extend image with empty tracks
157  RawTrack emptyTrack(dmkTrackLen);
158  byte numSides = singleSided ? 1 : 2;
159  while (numTracks <= track) {
160  for (byte side = 0; side < numSides; ++side) {
161  doWriteTrack(numTracks, side, emptyTrack);
162  }
163  ++numTracks;
164  }
165 
166  // update header
167  file->seek(1); // position in header where numTracks is stored
168  byte numTracksByte = numTracks;
169  file->write(&numTracksByte, sizeof(numTracksByte));
170 }
171 
172 
173 void DMKDiskImage::readSectorImpl(size_t logicalSector, SectorBuffer& buf)
174 {
175  auto [track, side, sector] = logToPhys(logicalSector);
176  RawTrack rawTrack;
177  readTrack(track, side, rawTrack);
178 
179  RawTrack::Sector sectorInfo;
180  if (!rawTrack.decodeSector(sector, sectorInfo)) {
181  throw NoSuchSectorException("Sector not found");
182  }
183  // TODO should we check sector size == 512?
184  // crc errors? correct track/head?
185  rawTrack.readBlock(sectorInfo.dataIdx, sizeof(buf), buf.raw);
186 }
187 
188 void DMKDiskImage::writeSectorImpl(size_t logicalSector, const SectorBuffer& buf)
189 {
190  auto [track, side, sector] = logToPhys(logicalSector);
191  RawTrack rawTrack;
192  readTrack(track, side, rawTrack);
193 
194  RawTrack::Sector sectorInfo;
195  if (!rawTrack.decodeSector(sector, sectorInfo)) {
196  throw NoSuchSectorException("Sector not found");
197  }
198  // TODO do checks? see readSectorImpl()
199  rawTrack.writeBlock(sectorInfo.dataIdx, sizeof(buf), buf.raw);
200 
201  writeTrack(track, side, rawTrack);
202 }
203 
205 {
206  unsigned t = singleSided ? numTracks : (2 * numTracks);
207  return t * const_cast<DMKDiskImage*>(this)->getSectorsPerTrack();
208 }
209 
211 {
212  return writeProtected || file->isReadOnly();
213 }
214 
216 {
217  return filepool.getSha1Sum(*file);
218 }
219 
220 void DMKDiskImage::detectGeometryFallback()
221 {
222  // The implementation in Disk::detectGeometryFallback() uses
223  // getNbSectors(), but for DMK images that doesn't work before we know
224  // the geometry.
225 
226  // detectGeometryFallback() is for example used when the bootsector
227  // could not be read. For a DMK image this can happen when that sector
228  // has CRC errors or is missing or deleted.
229 
231  setNbSides(singleSided ? 1 : 2);
232 }
233 
234 } // namespace openmsx
DMKDiskImage.hh
one_of.hh
openmsx::DmkHeader::trackLen
byte trackLen[2]
Definition: DMKDiskImage.cc:17
openmsx::SectorBuffer
Definition: DiskImageUtils.hh:90
openmsx::Disk::writeTrack
void writeTrack(byte track, byte side, const RawTrack &input)
Replace a full track in this image with the given track.
Definition: Disk.cc:14
gl::min
vecN< N, T > min(const vecN< N, T > &x, const vecN< N, T > &y)
Definition: gl_vec.hh:274
unlikely
#define unlikely(x)
Definition: likely.hh:15
openmsx::DmkHeader::numTracks
byte numTracks
Definition: DMKDiskImage.cc:16
DiskExceptions.hh
openmsx::RawTrack::getRawBuffer
byte * getRawBuffer()
Definition: RawTrack.hh:113
openmsx::DmkHeader::reserved
byte reserved[7]
Definition: DMKDiskImage.cc:19
openmsx::RawTrack
Definition: RawTrack.hh:62
openmsx::Disk
Definition: Disk.hh:13
t
TclObject t
Definition: TclObject_test.cc:264
openmsx::DmkHeader::flags
byte flags
Definition: DMKDiskImage.cc:18
ranges.hh
openmsx::MSXException
Definition: MSXException.hh:10
openmsx::DmkHeader
Definition: DMKDiskImage.cc:14
ranges::all_of
bool all_of(InputRange &&range, UnaryPredicate pred)
Definition: ranges.hh:119
openmsx::RawTrack::readBlock
void readBlock(int idx, unsigned size, byte *destination) const
Like memcpy() but copy from/to circular buffer.
Definition: RawTrack.cc:156
openmsx::Disk::logToPhys
TSS logToPhys(size_t log)
Definition: Disk.cc:47
openmsx::RawTrack::decodeSector
bool decodeSector(byte sectorNum, Sector &sector) const
Get a sector with a specific number.
Definition: RawTrack.cc:143
openmsx::DMKDiskImage::getSha1SumImpl
Sha1Sum getSha1SumImpl(FilePool &filepool) override
Definition: DMKDiskImage.cc:215
openmsx::RawTrack::getLength
unsigned getLength() const
Get track length.
Definition: RawTrack.hh:97
likely.hh
openmsx::SectorBuffer::raw
byte raw[512]
Definition: DiskImageUtils.hh:91
openmsx::FLAG_MFM_SECTOR
constexpr unsigned FLAG_MFM_SECTOR
Definition: DMKDiskImage.cc:26
openmsx::FilePool::getSha1Sum
Sha1Sum getSha1Sum(File &file)
Calculate sha1sum for the given File object.
Definition: FilePool.cc:72
openmsx::RawTrack
RawTrack
Definition: RawTrack.cc:279
openmsx::RawTrack::writeBlock
void writeBlock(int idx, unsigned size, const byte *source)
Definition: RawTrack.cc:162
openmsx::FLAG_SINGLE_SIDED
constexpr byte FLAG_SINGLE_SIDED
Definition: DMKDiskImage.cc:24
openmsx::DMKDiskImage::DMKDiskImage
DMKDiskImage(Filename filename, std::shared_ptr< File > file)
Definition: DMKDiskImage.cc:42
File.hh
one_of
Definition: one_of.hh:7
openmsx::Disk::setNbSides
void setNbSides(unsigned num)
Definition: Disk.hh:38
openmsx::filename
constexpr const char *const filename
Definition: FirmwareSwitch.cc:10
openmsx::DMKDiskImage::isWriteProtectedImpl
bool isWriteProtectedImpl() const override
Definition: DMKDiskImage.cc:210
openmsx::DMKDiskImage::readSectorImpl
void readSectorImpl(size_t sector, SectorBuffer &buf) override
Definition: DMKDiskImage.cc:173
openmsx::Disk::setSectorsPerTrack
void setSectorsPerTrack(unsigned num)
Definition: Disk.hh:36
openmsx::DMKDiskImage::writeSectorImpl
void writeSectorImpl(size_t sector, const SectorBuffer &buf) override
Definition: DMKDiskImage.cc:188
openmsx::RawTrack::addIdam
void addIdam(unsigned idx)
Definition: RawTrack.cc:24
openmsx::DMKDiskImage::readTrack
void readTrack(byte track, byte side, RawTrack &output) override
Read a full track from this disk image.
Definition: DMKDiskImage.cc:70
openmsx::Sha1Sum
This class represents the result of a sha1 calculation (a 160-bit value).
Definition: sha1.hh:20
FilePool.hh
openmsx::Disk::getSectorsPerTrack
unsigned getSectorsPerTrack()
Definition: Disk.cc:64
openmsx::DMKDiskImage
DMK disk image.
Definition: DMKDiskImage.hh:17
RawTrack.hh
openmsx::DMKDiskImage::getNbSectorsImpl
size_t getNbSectorsImpl() const override
Definition: DMKDiskImage.cc:204
openmsx::IDAM_FLAGS_MASK
constexpr unsigned IDAM_FLAGS_MASK
Definition: DMKDiskImage.cc:25
openmsx::RawTrack::Sector
Definition: RawTrack.hh:74
openmsx::DmkHeader::format
byte format[4]
Definition: DMKDiskImage.cc:20
openmsx::DmkHeader::writeProtected
byte writeProtected
Definition: DMKDiskImage.cc:15
openmsx::FilePool
Definition: FilePool.hh:16
openmsx::RawTrack::getIdamBuffer
const std::vector< unsigned > & getIdamBuffer() const
Definition: RawTrack.hh:115
openmsx::Filename
This class represents a filename.
Definition: Filename.hh:18
openmsx::RawTrack::Sector::dataIdx
int dataIdx
Definition: RawTrack.hh:77
openmsx::NoSuchSectorException
Definition: DiskExceptions.hh:8
openmsx
This file implemented 3 utility functions:
Definition: Autofire.cc:5
openmsx::RawTrack::clear
void clear(unsigned size)
Clear track data.
Definition: RawTrack.cc:18
openmsx::DMKDiskImage::writeTrackImpl
void writeTrackImpl(byte track, byte side, const RawTrack &input) override
Definition: DMKDiskImage.cc:121