OXYGENHUB
◆ main openmsx / src / fdc / DiskImageUtils.hh
7.8 KiB · C++ · last changed chore(): initial commit 1 month ago source blame history raw
1
#ifndef DISK_IMAGE_UTILS_HH
2
#define DISK_IMAGE_UTILS_HH
3
4
#include "AlignedBuffer.hh"
5
#include "endian.hh"
6
#include "ranges.hh"
7
8
#include <array>
9
#include <cstdint>
10
#include <ctime>
11
#include <span>
12
#include <string>
13
#include <utility>
14
15
namespace openmsx {
16
17
class SectorAccessibleDisk;
18
19
enum class MSXBootSectorType : int { // int for ImGui
20
	DOS1 = 0,
21
	DOS2 = 1,
22
	NEXTOR = 2,
23
};
24
25
struct MSXBootSector {
26
	std::array<uint8_t, 3>      jumpCode;      // + 0 0xE5 to boot program
27
	std::array<uint8_t, 8>      name;          // + 3
28
	Endian::UA_L16              bpSector;      // +11 bytes per sector (always 512)
29
	uint8_t                     spCluster;     // +13 sectors per cluster
30
	Endian::L16                 resvSectors;   // +14 nb of non-data sectors (incl boot sector)
31
	uint8_t                     nrFats;        // +16 nb of fats
32
	Endian::UA_L16              dirEntries;    // +17 max nb of files in root directory
33
	Endian::UA_L16              nrSectors;     // +19 nb of sectors on this disk (< 65536)
34
	uint8_t                     descriptor;    // +21 media descriptor
35
	Endian::L16                 sectorsFat;    // +22 sectors per FAT
36
	Endian::L16                 sectorsTrack;  // +24 sectors per track (0 for LBA volumes)
37
	Endian::L16                 nrSides;       // +26 number of sides (heads) (0 for LBA volumes)
38
	union {
39
		struct {
40
			Endian::L16                 hiddenSectors; // +28 not used
41
			std::array<uint8_t, 512-30> pad;           // +30
42
		} dos1;
43
		struct {
44
			Endian::L16                 hiddenSectors; // +28 not used
45
			std::array<uint8_t, 9>      pad1;          // +30
46
			Endian::UA_L32              vol_id;        // +39
47
			std::array<uint8_t, 512-43> pad2;          // +43
48
		} dos2;
49
		struct {
50
			Endian::L32                 hiddenSectors;          // +28 not used
51
			Endian::L32                 nrSectors;              // +32 nb of sectors on this disk (>= 65536)
52
			uint8_t                     physicalDriveNum;       // +36
53
			uint8_t                     reserved;               // +37
54
			uint8_t                     extendedBootSignature;  // +38
55
			Endian::UA_L32              vol_id;                 // +39
56
			std::array<uint8_t, 11>     volumeLabel;            // +43
57
			std::array<uint8_t, 8>      fileSystemType;         // +54
58
			std::array<uint8_t, 512-62> padding;                // +62
59
		} extended;
60
	} params;
61
};
62
static_assert(sizeof(MSXBootSector) == 512);
63
64
struct MSXDirEntry {
65
	enum class Attrib : uint8_t {
66
		REGULAR   = 0x00, // Normal file
67
		READONLY  = 0x01, // Read-Only file
68
		HIDDEN    = 0x02, // Hidden file
69
		SYSTEM    = 0x04, // System file
70
		VOLUME    = 0x08, // filename is Volume Label
71
		DIRECTORY = 0x10, // entry is a subdir
72
		ARCHIVE   = 0x20, // Archive bit
73
	};
74
	struct AttribValue {
75
		uint8_t value;
76
77
		constexpr AttribValue() = default;
78
		constexpr explicit(false) AttribValue(Attrib v) : value(std::to_underlying(v)) {}
79
		constexpr explicit AttribValue(uint8_t v) : value(v) {}
80
		constexpr explicit operator bool() const { return value != 0; }
81
		constexpr auto operator<=>(const AttribValue&) const = default;
82
		friend constexpr AttribValue operator|(AttribValue x, AttribValue y) { return AttribValue(x.value | y.value); }
83
		friend constexpr AttribValue operator&(AttribValue x, AttribValue y) { return AttribValue(x.value & y.value); }
84
	};
85
86
	std::array<char, 8 + 3> filename;     // + 0
87
	AttribValue             attrib;       // +11
88
	std::array<uint8_t, 10> reserved;     // +12 unused
89
	Endian::L16             time;         // +22
90
	Endian::L16             date;         // +24
91
	Endian::L16             startCluster; // +26
92
	Endian::L32             size;         // +28
93
94
	[[nodiscard]] auto base() const { return subspan<8>(filename, 0); }
95
	[[nodiscard]] auto ext () const { return subspan<3>(filename, 8); }
96
97
	bool operator==(const MSXDirEntry& other) const = default;
98
};
99
static_assert(sizeof(MSXDirEntry) == 32);
100
101
// Note: can't use Endian::L32 for 'start' and 'size' because the Partition
102
//       struct itself is not 4-bytes aligned.
103
struct Partition {
104
	uint8_t        boot_ind;   // + 0 0x80 - active
105
	uint8_t        head;       // + 1 starting head
106
	uint8_t        sector;     // + 2 starting sector
107
	uint8_t        cyl;        // + 3 starting cylinder
108
	uint8_t        sys_ind;    // + 4 what partition type
109
	uint8_t        end_head;   // + 5 end head
110
	uint8_t        end_sector; // + 6 end sector
111
	uint8_t        end_cyl;    // + 7 end cylinder
112
	Endian::UA_L32 start;      // + 8 starting sector counting from 0
113
	Endian::UA_L32 size;       // +12 nr of sectors in partition
114
};
115
static_assert(sizeof(Partition) == 16);
116
static_assert(alignof(Partition) == 1, "must not have alignment requirements");
117
118
struct PartitionTableSunrise {
119
	std::array<char, 11>      header; // +  0
120
	std::array<char,  3>      pad;    // +  3
121
	std::array<Partition, 31> part;   // + 14,+30,..,+494    Not 4-byte aligned!!
122
	Endian::L16               end;    // +510
123
};
124
static_assert(sizeof(PartitionTableSunrise) == 512);
125
126
struct PartitionTableNextor {
127
	std::array<char,  11>     header; // +  0
128
	std::array<char, 435>     pad;    // +  3
129
	std::array<Partition, 4>  part;   // +446,+462,+478,+494 Not 4-byte aligned!!
130
	Endian::L16               end;    // +510
131
};
132
static_assert(sizeof(PartitionTableNextor) == 512);
133
134
135
// Buffer that can hold a (512-byte) disk sector.
136
// The main advantages of this type over something like 'byte buf[512]' are:
137
//  - No need for reinterpret_cast<> when interpreting the data in a
138
//    specific way (this could in theory cause alignment problems).
139
//  - This type has a stricter alignment, so memcpy() and memset() can work
140
//    faster compared to using a raw byte array.
141
union SectorBuffer {
142
	std::array<uint8_t, 512>    raw;        // raw byte data
143
	MSXBootSector               bootSector; // interpreted as bootSector
144
	std::array<MSXDirEntry, 16> dirEntry;   // interpreted as 16 dir entries
145
	PartitionTableSunrise       ptSunrise;  // interpreted as Sunrise-IDE partition table
146
	PartitionTableNextor        ptNextor;   // interpreted as Nextor partition table
147
	AlignedBuffer               aligned;    // force big alignment (for faster memcpy)
148
};
149
static_assert(sizeof(SectorBuffer) == 512);
150
151
152
namespace DiskImageUtils {
153
154
	/** Gets the requested partition. Checks whether:
155
	 *   the disk is partitioned
156
	 *   the specified partition exists
157
	 * throws a CommandException if one of these conditions is false
158
	 * @param disk The disk to check.
159
	 * @param partition Partition number, in range [1..].
160
	 * @param buf Sector buffer for partition table.
161
	 * @return Reference to partition information struct in sector buffer.
162
	 */
163
	Partition& getPartition(SectorAccessibleDisk& disk, unsigned partition, SectorBuffer& buf);
164
165
	/** Check whether partition is of type FAT12 or FAT16.
166
	 */
167
	void checkSupportedPartition(SectorAccessibleDisk& disk, unsigned partition);
168
169
	/** Check whether the given disk is partitioned.
170
	 */
171
	[[nodiscard]] bool hasPartitionTable(SectorAccessibleDisk& disk);
172
173
	/** Format the given disk (= a single partition).
174
	 * The formatting depends on the size of the image.
175
	 * @param disk The disk/partition image to be formatted.
176
	 * @param bootType The boot sector type to use.
177
	 */
178
	void format(SectorAccessibleDisk& disk, MSXBootSectorType bootType);
179
	void format(SectorAccessibleDisk& disk, MSXBootSectorType bootType, size_t nbSectors);
180
181
	/** Write a partition table to the given disk and format each partition
182
	 * @param disk The disk to partition.
183
	 * @param sizes The number of sectors for each partition.
184
	 * @param bootType The boot sector type to use.
185
	 */
186
	unsigned partition(SectorAccessibleDisk& disk,
187
	               std::span<const unsigned> sizes, MSXBootSectorType bootType);
188
189
	struct FatTimeDate {
190
		uint16_t time, date;
191
	};
192
	FatTimeDate toTimeDate(time_t totalSeconds);
193
	time_t fromTimeDate(FatTimeDate timeDate);
194
195
	[[nodiscard]] std::string formatAttrib(MSXDirEntry::AttribValue attrib);
196
};
197
198
} // namespace openmsx
199
200
#endif