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