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 "one_of.hh"
8#include "random.hh"
9#include "ranges.hh"
10#include "view.hh"
11#include "xrange.hh"
12#include <algorithm>
13#include <bit>
14#include <cassert>
15#include <ctime>
16
18
19static constexpr unsigned DOS1_MAX_CLUSTER_COUNT = 0x3FE; // Max 3 sectors per FAT
20static constexpr unsigned FAT12_MAX_CLUSTER_COUNT = 0xFF4;
21static constexpr unsigned FAT16_MAX_CLUSTER_COUNT = 0xFFF4;
22static constexpr unsigned SECTOR_SIZE = sizeof(SectorBuffer);
23static constexpr unsigned DIR_ENTRIES_PER_SECTOR = SECTOR_SIZE / sizeof(MSXDirEntry);
24
27 NEXTOR
28};
29
30static constexpr std::array<char, 11> SUNRISE_PARTITION_TABLE_HEADER = {
31 '\353', '\376', '\220', 'M', 'S', 'X', '_', 'I', 'D', 'E', ' '
32};
33static constexpr std::array<char, 11> NEXTOR_PARTITION_TABLE_HEADER = {
34 '\353', '\376', '\220', 'N', 'E', 'X', 'T', 'O', 'R', '2', '0'
35};
36[[nodiscard]] static std::optional<PartitionTableType> getPartitionTableType(const SectorBuffer& buf)
37{
38 if (buf.ptSunrise.header == SUNRISE_PARTITION_TABLE_HEADER) {
40 } else if (buf.ptNextor.header == NEXTOR_PARTITION_TABLE_HEADER &&
41 buf.ptNextor.part[0].sys_ind != 0 && // Extra checks to distinguish MBR from VBR
42 buf.ptNextor.part[0].start == 1) { // since they have the same OEM signature.
44 }
45 return {};
46}
47
49{
50 SectorBuffer buf;
51 disk.readSector(0, buf);
52 return getPartitionTableType(buf).has_value();
53}
54
55// Get partition from Nextor extended boot record (standard EBR) chain.
56static Partition& getPartitionNextorExtended(
57 SectorAccessibleDisk& disk, unsigned partition, SectorBuffer& buf,
58 unsigned remaining, unsigned ebrOuterSector)
59{
60 unsigned ebrSector = ebrOuterSector;
61 while (true) {
62 disk.readSector(ebrSector, buf);
63
64 if (remaining == 0) {
65 // EBR partition entry. Start is relative to *this* EBR sector.
66 auto& p = buf.ptNextor.part[0];
67 if (p.start == 0) {
68 break;
69 }
70 // Adjust to absolute address before returning.
71 p.start = ebrSector + p.start;
72 return p;
73 }
74
75 // EBR link entry. Start is relative to *outermost* EBR sector.
76 auto& link = buf.ptNextor.part[1];
77 if (link.start == 0) {
78 break;
79 } else if (link.sys_ind != one_of(0x05, 0x0F)) {
80 throw CommandException("Invalid extended boot record.");
81 }
82 ebrSector = ebrOuterSector + link.start;
83 remaining--;
84 }
85 throw CommandException("No partition number ", partition);
86}
87
88// Get partition from Nextor master boot record (standard MBR).
89static Partition& getPartitionNextor(
90 SectorAccessibleDisk& disk, unsigned partition, SectorBuffer& buf)
91{
92 unsigned remaining = partition - 1;
93 for (auto& p : buf.ptNextor.part) {
94 if (p.start == 0) {
95 break;
96 } else if (p.sys_ind == one_of(0x05, 0x0F)) {
97 return getPartitionNextorExtended(disk, partition, buf, remaining, p.start);
98 } else if (remaining == 0) {
99 return p;
100 }
101 remaining--;
102 }
103 throw CommandException("No partition number ", partition);
104}
105
106// Get partition from Sunrise IDE master boot record.
107static Partition& getPartitionSunrise(unsigned partition, SectorBuffer& buf)
108{
109 // check number in range
110 if (partition < 1 || partition > buf.ptSunrise.part.size()) {
111 throw CommandException(
112 "Invalid partition number specified (must be 1-",
113 buf.ptSunrise.part.size(), ").");
114 }
115 // check valid partition number
116 auto& p = buf.ptSunrise.part[buf.ptSunrise.part.size() - partition];
117 if (p.start == 0) {
118 throw CommandException("No partition number ", partition);
119 }
120 return p;
121}
122
124{
125 // check drive has a partition table
126 // check valid partition number and return the entry
127 disk.readSector(0, buf);
128 auto partitionTableType = getPartitionTableType(buf);
129 if (partitionTableType == PartitionTableType::SUNRISE_IDE) {
130 return getPartitionSunrise(partition, buf);
131 } else if (partitionTableType == PartitionTableType::NEXTOR) {
132 return getPartitionNextor(disk, partition, buf);
133 } else {
134 throw CommandException("No (or invalid) partition table.");
135 }
136}
137
139{
140 SectorBuffer buf;
141 Partition& p = getPartition(disk, partition, buf);
142
143 // check partition type
144 if (p.sys_ind != one_of(0x01, 0x04, 0x06, 0x0E)) {
145 throw CommandException("Only FAT12 and FAT16 partitions are supported.");
146 }
147}
148
149
150// Create a correct boot sector depending on the required size of the filesystem
153 unsigned fatCount;
154 unsigned fatStart;
155 unsigned rootDirStart;
156 unsigned dataStart;
157 uint8_t descriptor;
158 bool fat16;
159};
160static SetBootSectorResult setBootSector(
161 MSXBootSector& boot, MSXBootSectorType bootType, size_t nbSectors)
162{
163 // start from the default boot block ..
164 if (bootType == MSXBootSectorType::DOS1) {
166 } else if (bootType == MSXBootSectorType::DOS2) {
168 } else if (bootType == MSXBootSectorType::NEXTOR && nbSectors > 0x10000) {
170 } else if (bootType == MSXBootSectorType::NEXTOR) {
172 } else {
174 }
175
176 // .. and fill-in image-size dependent parameters ..
177 // these are the same for most formats
178 uint8_t nbReservedSectors = 1;
179 uint8_t nbHiddenSectors = 1;
180 uint32_t vol_id = random_32bit() & 0x7F7F7F7F; // why are bits masked?;
181
182 // all these are set below (but initialize here to avoid warning)
183 uint16_t nbSides = 2;
184 uint8_t nbFats = 2;
185 uint16_t nbSectorsPerFat = 3;
186 uint8_t nbSectorsPerCluster = 2;
187 uint16_t nbDirEntry = 112;
188 uint8_t descriptor = 0xF9;
189 bool fat16 = false;
190
191 // now set correct info according to size of image (in sectors!)
192 if (bootType == MSXBootSectorType::NEXTOR && nbSectors > 0x10000) {
193 // using the same layout as Nextor 2.1.1’s FDISK
194 nbSides = 0;
195 nbFats = 2;
196 nbDirEntry = 512;
197 descriptor = 0xF0;
198 nbHiddenSectors = 0;
199 fat16 = true;
200
201 // <= 128 MB: 4, <= 256 MB: 8, ..., <= 4 GB: 128
202 nbSectorsPerCluster = narrow<uint8_t>(std::clamp(std::bit_ceil(nbSectors) >> 16, size_t(4), size_t(128)));
203
204 // Calculate fat size based on cluster count estimate
205 unsigned fatStart = nbReservedSectors + nbDirEntry / DIR_ENTRIES_PER_SECTOR;
206 unsigned estSectorCount = narrow<unsigned>(nbSectors - fatStart);
207 unsigned estClusterCount = std::min(estSectorCount / nbSectorsPerCluster, FAT16_MAX_CLUSTER_COUNT);
208 unsigned fatSize = 2 * (estClusterCount + 2);
209 nbSectorsPerFat = narrow<uint16_t>((fatSize + SECTOR_SIZE - 1) / SECTOR_SIZE); // round up
210
211 // Adjust sectors count down to match cluster count
212 unsigned dataStart = fatStart + nbFats * nbSectorsPerFat;
213 unsigned dataSectorCount = narrow<unsigned>(nbSectors - dataStart);
214 unsigned clusterCount = std::min(dataSectorCount / nbSectorsPerCluster, FAT16_MAX_CLUSTER_COUNT);
215 nbSectors = dataStart + clusterCount * nbSectorsPerCluster;
216 } else if (bootType == MSXBootSectorType::NEXTOR) {
217 // using the same layout as Nextor 2.1.1’s FDISK
218 nbSides = 0;
219 nbFats = 2;
220 nbDirEntry = 112;
221 descriptor = 0xF0;
222 nbHiddenSectors = 0;
223
224 // <= 2 MB: 1, <= 4 MB: 2, ..., <= 32 MB: 16
225 nbSectorsPerCluster = narrow<uint8_t>(std::clamp(std::bit_ceil(nbSectors) >> 12, size_t(1), size_t(16)));
226
227 // Partitions <= 16 MB are defined to have at most 3 sectors per FAT,
228 // so that they can boot DOS 1. This limits the cluster count to 1022.
229 // And for some unknown reason, Nextor limits it to one less than that.
230 unsigned maxClusterCount = FAT12_MAX_CLUSTER_COUNT;
231 if (nbSectors <= 0x8000) {
232 maxClusterCount = DOS1_MAX_CLUSTER_COUNT - 1;
233 nbSectorsPerCluster *= 4; // <= 2 MB: 4, <= 4 MB: 8, ..., <= 16 MB: 32
234 }
235
236 // Calculate fat size based on cluster count estimate
237 unsigned fatStart = nbReservedSectors + nbDirEntry / DIR_ENTRIES_PER_SECTOR;
238 unsigned estSectorCount = narrow<unsigned>(nbSectors - fatStart);
239 unsigned estClusterCount = std::min(estSectorCount / nbSectorsPerCluster, maxClusterCount);
240 unsigned fatSize = (3 * (estClusterCount + 2) + 1) / 2; // round up
241 nbSectorsPerFat = narrow<uint16_t>((fatSize + SECTOR_SIZE - 1) / SECTOR_SIZE); // round up
242
243 // Adjust sectors count down to match cluster count
244 unsigned dataStart = fatStart + nbFats * nbSectorsPerFat;
245 unsigned dataSectorCount = narrow<unsigned>(nbSectors - dataStart);
246 unsigned clusterCount = std::min(dataSectorCount / nbSectorsPerCluster, maxClusterCount);
247 nbSectors = dataStart + clusterCount * nbSectorsPerCluster;
248 } else if (bootType == MSXBootSectorType::DOS1 && nbSectors > 1440) {
249 // DOS1 supports up to 3 sectors per FAT, limiting the cluster count to 1022.
250 nbSides = 0;
251 nbFats = 2;
252 nbDirEntry = 112;
253 descriptor = 0xF0;
254 nbHiddenSectors = 0;
255
256 // <= 1 MB: 2, <= 2 MB: 4, ..., <= 32 MB: 64
257 nbSectorsPerCluster = narrow<uint8_t>(std::clamp(std::bit_ceil(nbSectors) >> 10, size_t(2), size_t(64)));
258
259 // Calculate fat size based on cluster count estimate
260 unsigned fatStart = nbReservedSectors + nbDirEntry / DIR_ENTRIES_PER_SECTOR;
261 unsigned estSectorCount = narrow<unsigned>(nbSectors - fatStart);
262 unsigned estClusterCount = std::min(estSectorCount / nbSectorsPerCluster, DOS1_MAX_CLUSTER_COUNT);
263 unsigned fatSize = (3 * (estClusterCount + 2) + 1) / 2; // round up
264 nbSectorsPerFat = narrow<uint16_t>((fatSize + SECTOR_SIZE - 1) / SECTOR_SIZE); // round up
265
266 // Adjust sectors count down to match cluster count
267 unsigned dataStart = fatStart + nbFats * nbSectorsPerFat;
268 unsigned dataSectorCount = narrow<unsigned>(nbSectors - dataStart);
269 unsigned clusterCount = std::min(dataSectorCount / nbSectorsPerCluster, DOS1_MAX_CLUSTER_COUNT);
270 nbSectors = dataStart + clusterCount * nbSectorsPerCluster;
271 } else if (nbSectors > 32732) {
272 // using the same layout as used by Jon in IDEFDISK v 3.1
273 // 32732 < nbSectors
274 // note: this format is only valid for nbSectors <= 65536
275 nbSides = 32; // copied from a partition from an IDE HD
276 nbFats = 2;
277 nbSectorsPerFat = 12; // copied from a partition from an IDE HD
278 nbSectorsPerCluster = 16;
279 nbDirEntry = 256;
280 descriptor = 0xF0;
281 nbHiddenSectors = 16; // override default from above
282 // for a 32MB disk or greater the sectors would be >= 65536
283 // since MSX use 16 bits for this, in case of sectors = 65536
284 // the truncated word will be 0 -> formatted as 320 Kb disk!
285 if (nbSectors > 65535) nbSectors = 65535; // this is the max size for fat12 :-)
286 } else if (nbSectors > 16388) {
287 // using the same layout as used by Jon in IDEFDISK v 3.1
288 // 16388 < nbSectors <= 32732
289 nbSides = 2; // unknown yet
290 nbFats = 2;
291 nbSectorsPerFat = 12;
292 nbSectorsPerCluster = 8;
293 nbDirEntry = 256;
294 descriptor = 0XF0;
295 } else if (nbSectors > 8212) {
296 // using the same layout as used by Jon in IDEFDISK v 3.1
297 // 8212 < nbSectors <= 16388
298 nbSides = 2; // unknown yet
299 nbFats = 2;
300 nbSectorsPerFat = 12;
301 nbSectorsPerCluster = 4;
302 nbDirEntry = 256;
303 descriptor = 0xF0;
304 } else if (nbSectors > 4126) {
305 // using the same layout as used by Jon in IDEFDISK v 3.1
306 // 4126 < nbSectors <= 8212
307 nbSides = 2; // unknown yet
308 nbFats = 2;
309 nbSectorsPerFat = 12;
310 nbSectorsPerCluster = 2;
311 nbDirEntry = 256;
312 descriptor = 0xF0;
313 } else if (nbSectors > 2880) {
314 // using the same layout as used by Jon in IDEFDISK v 3.1
315 // 2880 < nbSectors <= 4126
316 nbSides = 2; // unknown yet
317 nbFats = 2;
318 nbSectorsPerFat = 6;
319 nbSectorsPerCluster = 2;
320 nbDirEntry = 224;
321 descriptor = 0xF0;
322 } else if (nbSectors > 1440) {
323 // using the same layout as used by Jon in IDEFDISK v 3.1
324 // 1440 < nbSectors <= 2880
325 nbSides = 2; // unknown yet
326 nbFats = 2;
327 nbSectorsPerFat = 5;
328 nbSectorsPerCluster = 2;
329 nbDirEntry = 112;
330 descriptor = 0xF0;
331 } else if (nbSectors > 720) {
332 // normal double sided disk
333 // 720 < nbSectors <= 1440
334 nbSides = 2;
335 nbFats = 2;
336 nbSectorsPerFat = 3;
337 nbSectorsPerCluster = 2;
338 nbDirEntry = 112;
339 descriptor = 0xF9;
340 nbSectors = 1440; // force nbSectors to 1440, why?
341 } else {
342 // normal single sided disk
343 // nbSectors <= 720
344 nbSides = 1;
345 nbFats = 2;
346 nbSectorsPerFat = 2;
347 nbSectorsPerCluster = 2;
348 nbDirEntry = 112;
349 descriptor = 0xF8;
350 nbSectors = 720; // force nbSectors to 720, why?
351 }
352
353 assert(nbDirEntry % 16 == 0); // Non multiples of 16 not supported.
354
355 if (nbSectors < 0x10000) {
356 boot.nrSectors = narrow<uint16_t>(nbSectors);
357 } else if (bootType == MSXBootSectorType::NEXTOR && fat16) {
358 boot.nrSectors = 0;
359 } else {
360 throw CommandException("Too many sectors for FAT12 ", nbSectors);
361 }
362
363 boot.nrSides = nbSides;
364 boot.spCluster = nbSectorsPerCluster;
365 boot.nrFats = nbFats;
366 boot.sectorsFat = nbSectorsPerFat;
367 boot.dirEntries = nbDirEntry;
368 boot.descriptor = descriptor;
369 boot.resvSectors = nbReservedSectors;
370
371 if (bootType == MSXBootSectorType::DOS1) {
372 auto& params = boot.params.dos1;
373 params.hiddenSectors = nbHiddenSectors;
374 } else if (bootType == MSXBootSectorType::DOS2 || (bootType == MSXBootSectorType::NEXTOR && !fat16)) {
375 auto& params = boot.params.dos2;
376 params.hiddenSectors = nbHiddenSectors;
377 params.vol_id = vol_id;
378 } else if (bootType == MSXBootSectorType::NEXTOR && fat16) {
379 auto& params = boot.params.extended;
380 if (nbSectors <= 0x800000) {
381 params.nrSectors = narrow<unsigned>(nbSectors);
382 } else {
383 throw CommandException("Too many sectors for FAT16 ", nbSectors);
384 }
385 params.hiddenSectors = nbHiddenSectors;
386 params.vol_id = vol_id;
387 } else {
389 }
390
391 SetBootSectorResult result;
392 result.sectorsPerFat = nbSectorsPerFat;
393 result.fatCount = nbFats;
394 result.fatStart = nbReservedSectors;
395 result.rootDirStart = result.fatStart + nbFats * nbSectorsPerFat;
396 result.dataStart = result.rootDirStart + nbDirEntry / 16;
397 result.descriptor = descriptor;
398 result.fat16 = fat16;
399 return result;
400}
401
403{
404 format(disk, bootType, disk.getNbSectors());
405}
406
407void format(SectorAccessibleDisk& disk, MSXBootSectorType bootType, size_t nbSectors)
408{
409 // first create a boot sector for given partition size
410 nbSectors = std::min(nbSectors, disk.getNbSectors());
411 SectorBuffer buf;
412 SetBootSectorResult result = setBootSector(buf.bootSector, bootType, nbSectors);
413 disk.writeSector(0, buf);
414
415 // write empty FAT sectors (except for first sector, see below)
416 ranges::fill(buf.raw, 0);
417 for (auto fat : xrange(result.fatCount)) {
418 for (auto i : xrange(1u, result.sectorsPerFat)) {
419 disk.writeSector(i + result.fatStart + fat * result.sectorsPerFat, buf);
420 }
421 }
422
423 // write empty directory sectors
424 for (auto i : xrange(result.rootDirStart, result.dataStart)) {
425 disk.writeSector(i, buf);
426 }
427
428 // first FAT sector is special:
429 // - first byte contains the media descriptor
430 // - first two clusters must be marked as EOF
431 buf.raw[0] = result.descriptor;
432 buf.raw[1] = 0xFF;
433 buf.raw[2] = 0xFF;
434 if (result.fat16) {
435 buf.raw[3] = 0xFF;
436 }
437 for (auto fat : xrange(result.fatCount)) {
438 disk.writeSector(result.fatStart + fat * result.sectorsPerFat, buf);
439 }
440
441 // write 'empty' data sectors
442 ranges::fill(buf.raw, 0xE5);
443 for (auto i : xrange(result.dataStart, nbSectors)) {
444 disk.writeSector(i, buf);
445 }
446}
447
448struct CHS {
449 unsigned cylinder;
450 uint8_t head; // 0-15
451 uint8_t sector; // 1-32
452};
453[[nodiscard]] static constexpr CHS logicalToCHS(unsigned logical)
454{
455 // This is made to fit the openMSX hard disk configuration:
456 // 32 sectors/track 16 heads
457 unsigned tmp = logical + 1;
458 uint8_t sector = tmp % 32;
459 if (sector == 0) sector = 32;
460 tmp = (tmp - sector) / 32;
461 uint8_t head = tmp % 16;
462 unsigned cylinder = tmp / 16;
463 return {cylinder, head, sector};
464}
465
466static std::vector<unsigned> clampPartitionSizes(std::span<const unsigned> sizes,
467 size_t diskSize, unsigned initialOffset, unsigned perPartitionOffset)
468{
469 std::vector<unsigned> clampedSizes;
470 size_t sizeRemaining = std::min(diskSize, size_t(std::numeric_limits<unsigned>::max()));
471
472 if (sizeRemaining >= initialOffset) {
473 sizeRemaining -= initialOffset;
474 } else {
475 return clampedSizes;
476 }
477
478 for (auto size : sizes) {
479 if (sizeRemaining <= perPartitionOffset) {
480 break;
481 }
482 sizeRemaining -= perPartitionOffset;
483 if (size <= perPartitionOffset) {
484 throw CommandException("Partition size too small: ", size);
485 }
486 size -= perPartitionOffset;
487 if (sizeRemaining > size) {
488 clampedSizes.push_back(size);
489 sizeRemaining -= size;
490 } else {
491 clampedSizes.push_back(narrow<unsigned>(sizeRemaining));
492 break;
493 }
494 }
495 return clampedSizes;
496}
497
498// Partition with standard master / extended boot record (MBR / EBR) for Nextor.
499static std::vector<unsigned> partitionNextor(SectorAccessibleDisk& disk, std::span<const unsigned> sizes)
500{
501 SectorBuffer buf;
502 auto& pt = buf.ptNextor;
503
504 std::vector<unsigned> clampedSizes = clampPartitionSizes(sizes, disk.getNbSectors(), 0, 1);
505
506 if (clampedSizes.empty()) {
507 ranges::fill(buf.raw, 0);
508 pt.header = NEXTOR_PARTITION_TABLE_HEADER;
509 pt.end = 0xAA55;
510 disk.writeSector(0, buf);
511 return clampedSizes;
512 }
513
514 unsigned ptSector = 0;
515 for (auto [i, size] : enumerate(clampedSizes)) {
516 ranges::fill(buf.raw, 0);
517 if (i == 0) {
518 pt.header = NEXTOR_PARTITION_TABLE_HEADER;
519 }
520 pt.end = 0xAA55;
521
522 // Add partition entry
523 auto& p = pt.part[0];
524 p.boot_ind = (i == 0) ? 0x80 : 0x00; // boot flag
525 p.sys_ind = size > 0x10000 ? 0x0E : 0x01; // FAT16B (LBA), or FAT12
526 p.start = 1;
527 p.size = size;
528
529 // Add link if not the last partition
530 if (i != clampedSizes.size() - 1) {
531 auto& link = pt.part[1];
532 link.sys_ind = 0x05; // EBR
533 if (i == 0) {
534 link.start = ptSector + 1 + size;
535 link.size = sum(view::drop(sizes, 1), [](unsigned s) { return 1 + s; });
536 } else {
537 link.start = ptSector + 1 + size - (1 + clampedSizes[0]);
538 link.size = 1 + clampedSizes[i + 1];
539 }
540 }
541
542 disk.writeSector(ptSector, buf);
543
544 ptSector += 1 + size;
545 }
546 return clampedSizes;
547}
548
549// Partition with Sunrise IDE master boot record.
550static std::vector<unsigned> partitionSunrise(SectorAccessibleDisk& disk, std::span<const unsigned> sizes)
551{
552 SectorBuffer buf;
553 auto& pt = buf.ptSunrise;
554
555 std::vector<unsigned> clampedSizes = clampPartitionSizes(sizes, disk.getNbSectors(), 1, 0);
556 if (clampedSizes.size() > pt.part.size()) {
557 clampedSizes.resize(pt.part.size());
558 }
559
560 ranges::fill(buf.raw, 0);
561 pt.header = SUNRISE_PARTITION_TABLE_HEADER;
562 pt.end = 0xAA55;
563
564 unsigned partitionOffset = 1;
565 for (auto [i, size] : enumerate(clampedSizes)) {
566 unsigned partitionNbSectors = size;
567 auto& p = pt.part[30 - i];
568 auto [startCylinder, startHead, startSector] =
569 logicalToCHS(partitionOffset);
570 auto [endCylinder, endHead, endSector] =
571 logicalToCHS(partitionOffset + partitionNbSectors - 1);
572 p.boot_ind = (i == 0) ? 0x80 : 0x00; // boot flag
573 p.head = startHead;
574 p.sector = startSector;
575 p.cyl = narrow_cast<uint8_t>(startCylinder); // wraps for size larger than 64MB
576 p.sys_ind = 0x01; // FAT12
577 p.end_head = endHead;
578 p.end_sector = endSector;
579 p.end_cyl = narrow_cast<uint8_t>(endCylinder);
580 p.start = partitionOffset;
581 p.size = partitionNbSectors;
582 partitionOffset += partitionNbSectors;
583 }
584 disk.writeSector(0, buf);
585 return clampedSizes;
586}
587
588// Partition with standard master boot record (MBR) for Beer IDE 1.9.
589static std::vector<unsigned> partitionBeer(SectorAccessibleDisk& disk, std::span<const unsigned> sizes)
590{
591 SectorBuffer buf;
592 auto& pt = buf.ptNextor;
593
594 std::vector<unsigned> clampedSizes = clampPartitionSizes(sizes, disk.getNbSectors(), 1, 0);
595 if (clampedSizes.size() > pt.part.size()) {
596 clampedSizes.resize(pt.part.size());
597 }
598
599 ranges::fill(buf.raw, 0);
600 pt.header = NEXTOR_PARTITION_TABLE_HEADER; // TODO: Find out BEER IDE signature
601 pt.end = 0xAA55;
602
603 unsigned partitionOffset = 1;
604 for (auto [i, size] : enumerate(clampedSizes)) {
605 auto& p = pt.part[i];
606 p.boot_ind = (i == 0) ? 0x80 : 0x00; // boot flag
607 p.sys_ind = 0x01; // FAT12
608 p.start = partitionOffset;
609 p.size = size;
610 partitionOffset += size;
611 }
612
613 disk.writeSector(0, buf);
614 return clampedSizes;
615}
616
617unsigned partition(SectorAccessibleDisk& disk, std::span<const unsigned> sizes, MSXBootSectorType bootType)
618{
619 std::vector<unsigned> clampedSizes = [&] {
620 if (bootType == MSXBootSectorType::NEXTOR) {
621 return partitionNextor(disk, sizes);
622 } else if (bootType == MSXBootSectorType::DOS2) {
623 return partitionSunrise(disk, sizes);
624 } else if (bootType == MSXBootSectorType::DOS1) {
625 return partitionBeer(disk, sizes);
626 } else {
628 }
629 }();
630
631 for (auto [i, size] : enumerate(clampedSizes)) {
632 DiskPartition diskPartition(disk, narrow<unsigned>(i + 1));
633 format(diskPartition, bootType, size);
634 }
635
636 return narrow<unsigned>(clampedSizes.size());
637}
638
639} // namespace openmsx::DiskImageUtils
Definition: one_of.hh:7
static const SectorBuffer nextorBootBlockFAT12
Definition: BootBlocks.hh:18
static const SectorBuffer dos2BootBlock
Definition: BootBlocks.hh:15
static const SectorBuffer dos1BootBlock
Definition: BootBlocks.hh:12
static const SectorBuffer nextorBootBlockFAT16
Definition: BootBlocks.hh:21
void readSector(size_t sector, SectorBuffer &buf)
void writeSector(size_t sector, const SectorBuffer &buf)
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
constexpr vecN< N, T > min(const vecN< N, T > &x, const vecN< N, T > &y)
Definition: gl_vec.hh:267
constexpr vecN< N, T > max(const vecN< N, T > &x, const vecN< N, T > &y)
Definition: gl_vec.hh:285
constexpr vecN< N, T > clamp(const vecN< N, T > &x, const vecN< N, T > &minVal, const vecN< N, T > &maxVal)
Definition: gl_vec.hh:294
bool hasPartitionTable(SectorAccessibleDisk &disk)
Check whether the given disk is partitioned.
void checkSupportedPartition(SectorAccessibleDisk &disk, unsigned partition)
Check whether partition is of type FAT12 or FAT16.
Partition & getPartition(SectorAccessibleDisk &disk, unsigned partition, SectorBuffer &buf)
Gets the requested partition.
void format(SectorAccessibleDisk &disk, MSXBootSectorType bootType)
Format the given disk (= a single partition).
unsigned partition(SectorAccessibleDisk &disk, std::span< const unsigned > sizes, MSXBootSectorType bootType)
Write a partition table to the given disk and format each partition.
constexpr void fill(ForwardRange &&range, const T &value)
Definition: ranges.hh:287
size_t size(std::string_view utf8)
constexpr auto drop(Range &&range, size_t n)
Definition: view.hh:440
uint32_t random_32bit()
Return a random 32-bit value.
Definition: random.hh:67
auto sum(InputRange &&range, Proj proj={})
Definition: stl.hh:236
struct openmsx::MSXBootSector::@1::@2 dos1
struct openmsx::MSXBootSector::@1::@4 extended
struct openmsx::MSXBootSector::@1::@3 dos2
Endian::UA_L16 dirEntries
union openmsx::MSXBootSector::@1 params
Endian::UA_L16 nrSectors
std::array< Partition, 4 > part
std::array< uint8_t, 512 > raw
MSXBootSector bootSector
PartitionTableNextor ptNextor
#define UNREACHABLE
Definition: unreachable.hh:38
constexpr auto xrange(T e)
Definition: xrange.hh:132