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