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