OXYGENHUB
◆ main openmsx / src / fdc / SectorAccessibleDisk.cc
4.0 KiB · C++ · last changed chore(): initial commit 1 month ago source blame history raw
e8555c89 Andy McCall 1
#include "SectorAccessibleDisk.hh"
2
3
#include "DiskExceptions.hh"
4
#include "DiskImageUtils.hh"
5
#include "EmptyDiskPatch.hh"
6
#include "IPSPatch.hh"
7
8
#include "enumerate.hh"
9
#include "sha1.hh"
10
11
#include <array>
12
#include <memory>
13
14
namespace openmsx {
15
16
SectorAccessibleDisk::SectorAccessibleDisk()
17
	: patch(std::make_unique<EmptyDiskPatch>(*this))
18
{
19
}
20
21
SectorAccessibleDisk::~SectorAccessibleDisk() = default;
22
23
void SectorAccessibleDisk::readSector(size_t sector, SectorBuffer& buf)
24
{
25
	readSectors(std::span{&buf, 1}, sector);
26
}
27
28
void SectorAccessibleDisk::readSectors(
29
	std::span<SectorBuffer> buffers, size_t startSector)
30
{
31
	auto last = startSector + buffers.size() - 1;
32
	if (!isDummyDisk() && // in that case we want DriveEmptyException
33
	    (last > 1) && // allow reading sector 0 and 1 without calling
34
	                  // getNbSectors() because this potentially calls
35
	                  // detectGeometry() and that would cause an
36
	                  // infinite loop
37
	    (getNbSectors() <= last)) {
38
		throw NoSuchSectorException("No such sector");
39
	}
40
	try {
41
		// in the end this calls readSectorsImpl()
42
		patch->copyBlock(startSector * sizeof(SectorBuffer),
43
		                 std::span{buffers[0].raw.data(),
44
		                           buffers.size_bytes()});
45
	} catch (MSXException& e) {
46
		throw DiskIOErrorException("Disk I/O error: ", e.getMessage());
47
	}
48
}
49
50
void SectorAccessibleDisk::readSectorsImpl(
51
	std::span<SectorBuffer> buffers, size_t startSector)
52
{
53
	// Default implementation reads one sector at a time. But subclasses can
54
	// override this method if they can do it more efficiently.
55
	for (auto [i, buf] : enumerate(buffers)) {
56
		readSectorImpl(startSector + i, buf);
57
	}
58
}
59
60
void SectorAccessibleDisk::readSectorImpl(size_t /*sector*/, SectorBuffer& /*buf*/)
61
{
62
	// subclass should override exactly one of
63
	//    readSectorImpl() or readSectorsImpl()
64
	assert(false);
65
}
66
67
void SectorAccessibleDisk::writeSector(size_t sector, const SectorBuffer& buf)
68
{
69
	if (isWriteProtected()) {
70
		throw WriteProtectedException();
71
	}
72
	if (!isDummyDisk() && (getNbSectors() <= sector)) {
73
		throw NoSuchSectorException("No such sector");
74
	}
75
	try {
76
		writeSectorImpl(sector, buf);
77
	} catch (MSXException& e) {
78
		throw DiskIOErrorException("Disk I/O error: ", e.getMessage());
79
	}
80
	flushCaches();
81
}
82
83
void SectorAccessibleDisk::writeSectors(
84
	std::span<const SectorBuffer> buffers, size_t startSector)
85
{
86
	// Simply write one-at-a-time. There's no possibility (nor any need) yet
87
	// to allow to optimize this
88
	for (auto [i, buf] : enumerate(buffers)) {
89
		writeSector(startSector + i, buf);
90
	}
91
}
92
93
94
size_t SectorAccessibleDisk::getNbSectors()
95
{
96
	return getNbSectorsImpl();
97
}
98
99
void SectorAccessibleDisk::applyPatch(Filename patchFile)
100
{
101
	patch = std::make_unique<IPSPatch>(std::move(patchFile), std::move(patch));
102
}
103
104
std::vector<Filename> SectorAccessibleDisk::getPatches() const
105
{
106
	return patch->getFilenames();
107
}
108
109
bool SectorAccessibleDisk::hasPatches() const
110
{
111
	return !patch->isEmptyPatch();
112
}
113
114
Sha1Sum SectorAccessibleDisk::getSha1Sum(FilePool& filePool)
115
{
116
	checkCaches();
117
	if (sha1cache.empty()) {
118
		sha1cache = getSha1SumImpl(filePool);
119
	}
120
	return sha1cache;
121
}
122
123
Sha1Sum SectorAccessibleDisk::getSha1SumImpl(FilePool& /*filePool*/)
124
{
125
	try {
126
		setPeekMode(true);
127
		SHA1 sha1;
128
129
		std::array<SectorBuffer, 32> buf;
130
		size_t total = getNbSectors();
131
		size_t sector = 0;
132
		while (sector < total) {
133
			auto chunk = std::min(buf.size(), total - sector);
134
			auto sub = subspan(buf, 0, chunk);
135
			readSectors(sub, sector);
136
			sha1.update({sub[0].raw.data(), sub.size_bytes()});
137
			sector += chunk;
138
		}
139
140
		setPeekMode(false);
141
		return sha1.digest();
142
	} catch (MSXException&) {
143
		setPeekMode(false);
144
		throw;
145
	}
146
}
147
148
bool SectorAccessibleDisk::isWriteProtected() const
149
{
150
	return forcedWriteProtect || isWriteProtectedImpl();
151
}
152
153
void SectorAccessibleDisk::forceWriteProtect()
154
{
155
	// can't be undone
156
	forcedWriteProtect = true;
157
}
158
159
bool SectorAccessibleDisk::isDummyDisk() const
160
{
161
	return false;
162
}
163
164
void SectorAccessibleDisk::checkCaches()
165
{
166
	// nothing
167
}
168
169
void SectorAccessibleDisk::flushCaches()
170
{
171
	sha1cache.clear();
172
}
173
174
} // namespace openmsx