openMSX
DiskImageUtils.cc
Go to the documentation of this file.
1 #include "DiskImageUtils.hh"
2 #include "DiskPartition.hh"
3 #include "CommandException.hh"
4 #include "BootBlocks.hh"
5 #include "endian.hh"
6 #include "enumerate.hh"
7 #include "random.hh"
8 #include "xrange.hh"
9 #include <cstring>
10 #include <cassert>
11 #include <ctime>
12 
14 
15 static constexpr char PARTAB_HEADER[11] = {
16  '\353', '\376', '\220', 'M', 'S', 'X', '_', 'I', 'D', 'E', ' '
17 };
18 [[nodiscard]] static bool isPartitionTableSector(const PartitionTable& pt)
19 {
20  return memcmp(pt.header, PARTAB_HEADER, sizeof(PARTAB_HEADER)) == 0;
21 }
22 
24 {
25  SectorBuffer buf;
26  disk.readSector(0, buf);
27  return isPartitionTableSector(buf.pt);
28 }
29 
30 
31 static Partition& checkImpl(
32  SectorAccessibleDisk& disk, unsigned partition, SectorBuffer& buf)
33 {
34  // check number in range
35  if (partition < 1 || partition > 31) {
36  throw CommandException(
37  "Invalid partition number specified (must be 1-31).");
38  }
39  // check drive has a partition table
40  disk.readSector(0, buf);
41  if (!isPartitionTableSector(buf.pt)) {
42  throw CommandException(
43  "No (or invalid) partition table.");
44  }
45  // check valid partition number
46  auto& p = buf.pt.part[31 - partition];
47  if (p.start == 0) {
48  throw CommandException("No partition number ", partition);
49  }
50  return p;
51 }
52 
54 {
55  SectorBuffer buf;
56  checkImpl(disk, partition, buf);
57 }
58 
60 {
61  SectorBuffer buf;
62  Partition& p = checkImpl(disk, partition, buf);
63 
64  // check partition type
65  if (p.sys_ind != 0x01) {
66  throw CommandException("Only FAT12 partitions are supported.");
67  }
68 }
69 
70 
71 // Create a correct bootsector depending on the required size of the filesystem
73  unsigned firstDataSector;
74  byte descriptor;
75 };
76 static SetBootSectorResult setBootSector(
77  MSXBootSector& boot, size_t nbSectors, bool dos1)
78 {
79  // start from the default bootblock ..
80  const auto& defaultBootBlock = dos1 ? BootBlocks::dos1BootBlock : BootBlocks::dos2BootBlock;
81  memcpy(&boot, &defaultBootBlock, sizeof(boot));
82 
83  // .. and fill-in image-size dependent parameters ..
84  // these are the same for most formats
85  byte nbReservedSectors = 1;
86  byte nbHiddenSectors = 1;
87 
88  // all these are initialized below (in this order)
89  word nbSides;
90  byte nbFats;
91  byte nbSectorsPerFat;
92  byte nbSectorsPerCluster;
93  word nbDirEntry;
94  byte descriptor;
95 
96  // now set correct info according to size of image (in sectors!)
97  // and using the same layout as used by Jon in IDEFDISK v 3.1
98  if (nbSectors > 32732) {
99  // 32732 < nbSectors
100  // note: this format is only valid for nbSectors <= 65536
101  nbSides = 32; // copied from a partition from an IDE HD
102  nbFats = 2;
103  nbSectorsPerFat = 12; // copied from a partition from an IDE HD
104  nbSectorsPerCluster = 16;
105  nbDirEntry = 256;
106  descriptor = 0xF0;
107  nbHiddenSectors = 16; // override default from above
108  } else if (nbSectors > 16388) {
109  // 16388 < nbSectors <= 32732
110  nbSides = 2; // unknown yet
111  nbFats = 2;
112  nbSectorsPerFat = 12;
113  nbSectorsPerCluster = 8;
114  nbDirEntry = 256;
115  descriptor = 0XF0;
116  } else if (nbSectors > 8212) {
117  // 8212 < nbSectors <= 16388
118  nbSides = 2; // unknown yet
119  nbFats = 2;
120  nbSectorsPerFat = 12;
121  nbSectorsPerCluster = 4;
122  nbDirEntry = 256;
123  descriptor = 0xF0;
124  } else if (nbSectors > 4126) {
125  // 4126 < nbSectors <= 8212
126  nbSides = 2; // unknown yet
127  nbFats = 2;
128  nbSectorsPerFat = 12;
129  nbSectorsPerCluster = 2;
130  nbDirEntry = 256;
131  descriptor = 0xF0;
132  } else if (nbSectors > 2880) {
133  // 2880 < nbSectors <= 4126
134  nbSides = 2; // unknown yet
135  nbFats = 2;
136  nbSectorsPerFat = 6;
137  nbSectorsPerCluster = 2;
138  nbDirEntry = 224;
139  descriptor = 0xF0;
140  } else if (nbSectors > 1440) {
141  // 1440 < nbSectors <= 2880
142  nbSides = 2; // unknown yet
143  nbFats = 2;
144  nbSectorsPerFat = 5;
145  nbSectorsPerCluster = 2;
146  nbDirEntry = 112;
147  descriptor = 0xF0;
148  } else if (nbSectors > 720) {
149  // normal double sided disk
150  // 720 < nbSectors <= 1440
151  nbSides = 2;
152  nbFats = 2;
153  nbSectorsPerFat = 3;
154  nbSectorsPerCluster = 2;
155  nbDirEntry = 112;
156  descriptor = 0xF9;
157  nbSectors = 1440; // force nbSectors to 1440, why?
158  } else {
159  // normal single sided disk
160  // nbSectors <= 720
161  nbSides = 1;
162  nbFats = 2;
163  nbSectorsPerFat = 2;
164  nbSectorsPerCluster = 2;
165  nbDirEntry = 112;
166  descriptor = 0xF8;
167  nbSectors = 720; // force nbSectors to 720, why?
168  }
169 
170  boot.nrSectors = uint16_t(nbSectors); // TODO check for overflow
171  boot.nrSides = nbSides;
172  boot.spCluster = nbSectorsPerCluster;
173  boot.nrFats = nbFats;
174  boot.sectorsFat = nbSectorsPerFat;
175  boot.dirEntries = nbDirEntry;
176  boot.descriptor = descriptor;
177  boot.resvSectors = nbReservedSectors;
178  boot.hiddenSectors = nbHiddenSectors;
179 
180  if (!dos1) {
181  // set random volume id
182  boot.vol_id = random_32bit() & 0x7F7F7F7F; // why are bits masked?
183  }
184  unsigned nbRootDirSectors = nbDirEntry / 16;
185  unsigned rootDirStart = 1 + nbFats * nbSectorsPerFat;
186  unsigned firstDataSector = rootDirStart + nbRootDirSectors;
187  return {firstDataSector, descriptor};
188 }
189 
190 void format(SectorAccessibleDisk& disk, bool dos1)
191 {
192  // first create a bootsector for given partition size
193  size_t nbSectors = disk.getNbSectors();
194  SectorBuffer buf;
195  auto [firstDataSector, descriptor] = setBootSector(buf.bootSector, nbSectors, dos1);
196  disk.writeSector(0, buf);
197 
198  // write empty FAT and directory sectors
199  memset(&buf, 0, sizeof(buf));
200  for (auto i : xrange(2u, firstDataSector)) {
201  disk.writeSector(i, buf);
202  }
203  // first FAT sector is special:
204  // - first byte contains the media descriptor
205  // - first two clusters must be marked as EOF
206  buf.raw[0] = descriptor;
207  buf.raw[1] = 0xFF;
208  buf.raw[2] = 0xFF;
209  disk.writeSector(1, buf);
210 
211  // write 'empty' data sectors
212  memset(&buf, 0xE5, sizeof(buf));
213  for (auto i : xrange(firstDataSector, nbSectors)) {
214  disk.writeSector(i, buf);
215  }
216 }
217 
218 struct CHS {
219  unsigned cylinder;
220  unsigned head;
221  unsigned sector;
222 };
223 [[nodiscard]] static constexpr CHS logicalToCHS(unsigned logical)
224 {
225  // This is made to fit the openMSX harddisk configuration:
226  // 32 sectors/track 16 heads
227  unsigned tmp = logical + 1;
228  unsigned sector = tmp % 32;
229  if (sector == 0) sector = 32;
230  tmp = (tmp - sector) / 32;
231  unsigned head = tmp % 16;
232  unsigned cylinder = tmp / 16;
233  return {cylinder, head, sector};
234 }
235 
237 {
238  assert(sizes.size() <= 31);
239 
240  SectorBuffer buf{};
241  memset(&buf, 0, sizeof(buf));
242  memcpy(buf.pt.header, PARTAB_HEADER, sizeof(PARTAB_HEADER));
243  buf.pt.end = 0xAA55;
244 
245  unsigned partitionOffset = 1;
246  for (auto [i, size] : enumerate(sizes)) {
247  unsigned partitionNbSectors = size;
248  auto& p = buf.pt.part[30 - i];
249  auto [startCylinder, startHead, startSector] =
250  logicalToCHS(partitionOffset);
251  auto [endCylinder, endHead, endSector] =
252  logicalToCHS(partitionOffset + partitionNbSectors - 1);
253  p.boot_ind = (i == 0) ? 0x80 : 0x00; // bootflag
254  p.head = startHead;
255  p.sector = startSector;
256  p.cyl = startCylinder;
257  p.sys_ind = 0x01; // FAT12
258  p.end_head = endHead;
259  p.end_sector = endSector;
260  p.end_cyl = endCylinder;
261  p.start = partitionOffset;
262  p.size = size;
263  DiskPartition diskPartition(disk, partitionOffset, partitionNbSectors);
264  format(diskPartition);
265  partitionOffset += partitionNbSectors;
266  }
267  disk.writeSector(0, buf);
268 }
269 
270 } // namespace openmsx::DiskImageUtils
static const SectorBuffer dos2BootBlock
Definition: BootBlocks.hh:15
static const SectorBuffer dos1BootBlock
Definition: BootBlocks.hh:12
void readSector(size_t sector, SectorBuffer &buf)
void writeSector(size_t sector, const SectorBuffer &buf)
Definition: span.hh:126
constexpr index_type size() const noexcept
Definition: span.hh:296
constexpr auto enumerate(Iterable &&iterable)
Heavily inspired by Nathan Reed's blog post: Python-Like enumerate() In C++17 http://reedbeta....
Definition: enumerate.hh:28
bool hasPartitionTable(SectorAccessibleDisk &disk)
Check whether the given disk is partitioned.
void format(SectorAccessibleDisk &disk, bool dos1)
Format the given disk (= a single partition).
void checkValidPartition(SectorAccessibleDisk &disk, unsigned partition)
Checks whether the disk is partitioned the specified partition exists throws a CommandException if on...
void checkFAT12Partition(SectorAccessibleDisk &disk, unsigned partition)
Like above, but also check whether partition is of type FAT12.
void partition(SectorAccessibleDisk &disk, span< const unsigned > sizes)
Write a partition table to the given disk and format each partition.
uint16_t word
16 bit unsigned integer
Definition: openmsx.hh:29
size_t size(std::string_view utf8)
uint32_t random_32bit()
Return a random 32-bit value.
Definition: random.hh:67
Endian::UA_L16 dirEntries
Endian::UA_L16 nrSectors
MSXBootSector bootSector
constexpr auto xrange(T e)
Definition: xrange.hh:155