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