43 [[nodiscard]]
static constexpr
unsigned normalizeFAT(
unsigned cluster)
48 unsigned DirAsDSK::readFATHelper(
const SectorBuffer* fatBuf,
unsigned cluster)
const
51 assert(cluster < maxCluster);
52 const auto* buf = fatBuf[0].raw;
53 const auto* p = &buf[(cluster * 3) / 2];
54 unsigned result = (cluster & 1)
55 ? (p[0] >> 4) + (p[1] << 4)
56 : p[0] + ((p[1] & 0x0F) << 8);
57 return normalizeFAT(result);
60 void DirAsDSK::writeFATHelper(SectorBuffer* fatBuf,
unsigned cluster,
unsigned val)
const
63 assert(cluster < maxCluster);
64 auto* buf = fatBuf[0].raw;
65 auto* p = &buf[(cluster * 3) / 2];
67 p[0] = (p[0] & 0x0F) + (val << 4);
71 p[1] = (p[1] & 0xF0) + ((val >> 8) & 0x0F);
75 SectorBuffer* DirAsDSK::fat()
79 SectorBuffer* DirAsDSK::fat2()
81 return §ors[firstSector2ndFAT];
85 unsigned DirAsDSK::readFAT(
unsigned cluster)
87 return readFATHelper(fat(), cluster);
91 void DirAsDSK::writeFAT12(
unsigned cluster,
unsigned val)
93 writeFATHelper(fat (), cluster, val);
94 writeFATHelper(fat2(), cluster, val);
100 unsigned DirAsDSK::findNextFreeCluster(
unsigned cluster)
102 assert(cluster < maxCluster);
106 }
while ((cluster < maxCluster) && (readFAT(cluster) !=
FREE_FAT));
109 unsigned DirAsDSK::findFirstFreeCluster()
115 unsigned DirAsDSK::getFreeCluster()
117 unsigned cluster = findFirstFreeCluster();
118 if (cluster == maxCluster) {
119 throw MSXException(
"disk full");
124 unsigned DirAsDSK::clusterToSector(
unsigned cluster)
const
127 assert(cluster < maxCluster);
132 std::pair<unsigned, unsigned> DirAsDSK::sectorToClusterOffset(
unsigned sector)
const
134 assert(sector >= firstDataSector);
135 assert(sector < nofSectors);
136 sector -= firstDataSector;
139 return {cluster, offset};
141 unsigned DirAsDSK::sectorToCluster(
unsigned sector)
const
143 auto [cluster, offset] = sectorToClusterOffset(sector);
147 MSXDirEntry& DirAsDSK::msxDir(DirIndex dirIndex)
149 assert(dirIndex.sector < nofSectors);
151 return sectors[dirIndex.sector].dirEntry[dirIndex.idx];
155 unsigned DirAsDSK::nextMsxDirSector(
unsigned sector)
157 if (sector < firstDataSector) {
159 assert(firstDirSector <= sector);
161 if (sector == firstDataSector) {
168 auto [cluster, offset] = sectorToClusterOffset(sector);
173 unsigned nextCl = readFAT(cluster);
178 return clusterToSector(nextCl);
183 bool DirAsDSK::checkMSXFileExists(
184 const string& msxFilename,
unsigned msxDirSector)
186 vector<bool> visited(nofSectors,
false);
188 if (visited[msxDirSector]) {
192 visited[msxDirSector] =
true;
195 DirIndex dirIndex(msxDirSector, idx);
196 if (memcmp(msxDir(dirIndex).
filename,
197 msxFilename.data(), 8 + 3) == 0) {
201 msxDirSector = nextMsxDirSector(msxDirSector);
202 }
while (msxDirSector !=
unsigned(-1));
210 DirAsDSK::DirIndex DirAsDSK::findHostFileInDSK(std::string_view hostName)
212 for (
const auto& [dirIdx, mapDir] : mapDirs) {
213 if (mapDir.hostName == hostName) {
217 return {unsigned(-1), unsigned(-1)};
221 bool DirAsDSK::checkFileUsedInDSK(std::string_view hostName)
223 DirIndex dirIndex = findHostFileInDSK(hostName);
224 return dirIndex.sector != unsigned(-1);
227 static string hostToMsxName(
string hostName)
231 return (a ==
' ') ?
'_' : ::toupper(a);
236 string result(8 + 3,
' ');
237 memcpy(result.data() + 0, file.data(), std::min<size_t>(8, file.size()));
238 memcpy(result.data() + 8, ext .data(), std::min<size_t>(3, ext .size()));
243 static string msxToHostName(
const char* msxName)
246 for (
unsigned i = 0; (i < 8) && (msxName[i] !=
' '); ++i) {
247 result += char(tolower(msxName[i]));
249 if (msxName[8] !=
' ') {
251 for (
unsigned i = 8; (i < (8 + 3)) && (msxName[i] !=
' '); ++i) {
252 result += char(tolower(msxName[i]));
263 , diskChanger(diskChanger_)
265 , hostDir(FileOperations::
expandTilde(hostDir_.getResolved() +
'/'))
266 , syncMode(syncMode_)
267 , lastAccess(EmuTime::zero())
274 , sectors(nofSectors)
288 memset(sectors.data(), 0xE5,
sizeof(
SectorBuffer) * nofSectors);
291 byte mediaDescriptor = (numSides == 2) ? 0xF9 : 0xF8;
295 memcpy(§ors[0], &protoBootSector,
sizeof(protoBootSector));
296 auto& bootSector = sectors[0].bootSector;
301 bootSector.nrSectors = nofSectors;
302 bootSector.descriptor = mediaDescriptor;
303 bootSector.sectorsFat = nofSectorsPerFat;
305 bootSector.nrSides = numSides;
313 fat ()->
raw[0] = mediaDescriptor; fat ()->
raw[1] = 0xFF; fat ()->
raw[2] = 0xFF;
314 fat2()->
raw[0] = mediaDescriptor; fat2()->
raw[1] = 0xFF; fat2()->
raw[2] = 0xFF;
320 assert(mapDirs.empty());
343 bool needSync = [&] {
345 auto now = scheduler->getCurrentTime();
346 auto delta = now - lastAccess;
362 assert(sector < nofSectors);
369 bool needSync = [&] {
371 auto now = scheduler->getCurrentTime();
372 auto delta = now - lastAccess;
391 memcpy(&buf, §ors[sector],
sizeof(buf));
394 void DirAsDSK::syncWithHost()
399 checkDeletedHostFiles();
405 checkModifiedHostFiles();
408 addNewHostFiles({}, firstDirSector);
411 void DirAsDSK::checkDeletedHostFiles()
415 for (
const auto& [dirIdx, mapDir] :
copy) {
416 if (!mapDirs.contains(dirIdx)) {
427 auto fullHostName =
tmpStrCat(hostDir, mapDir.hostName);
428 bool isMSXDirectory = (msxDir(dirIdx).
attrib &
439 deleteMSXFile(dirIdx);
444 void DirAsDSK::deleteMSXFile(DirIndex dirIndex)
447 mapDirs.erase(dirIndex);
449 char c = msxDir(dirIndex).
filename[0];
450 if (c ==
one_of(0,
char(0xE5))) {
458 const char* msxName = msxDir(dirIndex).
filename;
459 if ((memcmp(msxName,
". ", 11) == 0) ||
460 (memcmp(msxName,
".. ", 11) == 0)) {
468 deleteMSXFilesInDir(clusterToSector(cluster));
474 msxDir(dirIndex).
filename[0] = char(0xE5);
477 freeFATChain(msxDir(dirIndex).startCluster);
480 void DirAsDSK::deleteMSXFilesInDir(
unsigned msxDirSector)
482 vector<bool> visited(nofSectors,
false);
484 if (visited[msxDirSector]) {
488 visited[msxDirSector] =
true;
491 deleteMSXFile(DirIndex(msxDirSector, idx));
493 msxDirSector = nextMsxDirSector(msxDirSector);
494 }
while (msxDirSector !=
unsigned(-1));
497 void DirAsDSK::freeFATChain(
unsigned cluster)
500 while ((
FIRST_CLUSTER <= cluster) && (cluster < maxCluster)) {
501 unsigned nextCl = readFAT(cluster);
507 void DirAsDSK::checkModifiedHostFiles()
510 for (
const auto& [dirIdx, mapDir] :
copy) {
511 if (!mapDirs.contains(dirIdx)) {
515 auto fullHostName =
tmpStrCat(hostDir, mapDir.hostName);
516 bool isMSXDirectory = (msxDir(dirIdx).
attrib &
530 if (!isMSXDirectory &&
531 ((mapDir.mtime != fst.st_mtime) ||
532 (mapDir.filesize != size_t(fst.st_size)))) {
533 importHostFile(dirIdx, fst);
538 deleteMSXFile(dirIdx);
546 assert(mapDirs.contains(dirIndex));
549 setMSXTimeStamp(dirIndex, fst);
564 size_t hostSize = fst.st_size;
565 auto& mapDir = mapDirs[dirIndex];
566 mapDir.filesize = hostSize;
567 mapDir.mtime = fst.st_mtime;
569 bool moreClustersInChain =
true;
575 moreClustersInChain =
false;
576 curCl = findFirstFreeCluster();
579 auto remainingSize = hostSize;
582 File file(hostDir + mapDir.hostName,
585 while (remainingSize && (curCl < maxCluster)) {
586 unsigned logicalSector = clusterToSector(curCl);
588 unsigned sector = logicalSector + i;
589 assert(sector < nofSectors);
590 auto* buf = §ors[sector];
595 if (remainingSize == 0) {
603 writeFAT12(prevCl, curCl);
611 if (moreClustersInChain) {
612 curCl = readFAT(curCl);
615 (curCl >= maxCluster)) {
618 moreClustersInChain =
false;
619 curCl = findFirstFreeCluster();
622 curCl = findNextFreeCluster(curCl);
625 if (remainingSize != 0) {
627 mapDir.hostName,
" truncated.");
629 }
catch (FileException& e) {
632 mapDir.hostName,
": ", e.getMessage(),
633 " Truncated file on MSX disk.");
647 if (moreClustersInChain) {
652 msxDir(dirIndex).
size = uint32_t(hostSize - remainingSize);
664 time_t mtime = fst.st_mtime;
665 auto* mtim = localtime(&mtime);
666 int t1 = mtim ? (mtim->tm_sec >> 1) + (mtim->tm_min << 5) +
667 (mtim->tm_hour << 11)
669 msxDir(dirIndex).
time = t1;
670 int t2 = mtim ? mtim->tm_mday + ((mtim->tm_mon + 1) << 5) +
671 ((mtim->tm_year + 1900 - 1980) << 9)
673 msxDir(dirIndex).
date = t2;
684 static size_t weight(
const string& hostName)
692 result += ext.size() * 10;
694 result += file.size();
698 void DirAsDSK::addNewHostFiles(
const string& hostSubDir,
unsigned msxDirSector)
700 assert(!hostSubDir.starts_with(
'/'));
701 assert(hostSubDir.empty() || hostSubDir.ends_with(
'/'));
703 vector<string> hostNames;
705 ReadDir dir(
tmpStrCat(hostDir, hostSubDir));
706 while (
auto* d = dir.getEntry()) {
707 hostNames.emplace_back(d->d_name);
710 ranges::sort(hostNames, {}, [](
const string& n) {
return weight(n); });
712 for (
auto& hostName : hostNames) {
714 if (hostName.starts_with(
'.')) {
719 auto fullHostName =
tmpStrCat(hostDir, hostSubDir, hostName);
722 throw MSXException(
"Error accessing ", fullHostName);
725 addNewDirectory(hostSubDir, hostName, msxDirSector, fst);
727 addNewHostFile(hostSubDir, hostName, msxDirSector, fst);
729 throw MSXException(
"Not a regular file: ", fullHostName);
731 }
catch (MSXException& e) {
737 void DirAsDSK::addNewDirectory(
const string& hostSubDir,
const string& hostName,
740 DirIndex dirIndex = findHostFileInDSK(
tmpStrCat(hostSubDir, hostName));
741 unsigned newMsxDirSector;
742 if (dirIndex.sector ==
unsigned(-1)) {
745 unsigned cluster = getFreeCluster();
750 dirIndex = fillMSXDirEntry(hostSubDir, hostName, msxDirSector);
756 setMSXTimeStamp(dirIndex, fst);
761 newMsxDirSector = clusterToSector(cluster);
763 memset(§ors[newMsxDirSector + i], 0,
SECTOR_SIZE);
765 DirIndex idx0(newMsxDirSector, 0);
766 DirIndex idx1(newMsxDirSector, 1);
767 memset(msxDir(idx0).
filename,
' ', 11);
768 memset(msxDir(idx1).
filename,
' ', 11);
769 memset(msxDir(idx0).
filename,
'.', 1);
770 memset(msxDir(idx1).
filename,
'.', 2);
773 setMSXTimeStamp(idx0, fst);
774 setMSXTimeStamp(idx1, fst);
776 msxDir(idx1).
startCluster = msxDirSector == firstDirSector
777 ? 0 : sectorToCluster(msxDirSector);
792 newMsxDirSector = clusterToSector(cluster);
796 addNewHostFiles(
strCat(hostSubDir, hostName,
'/'), newMsxDirSector);
799 void DirAsDSK::addNewHostFile(
const string& hostSubDir,
const string& hostName,
802 if (checkFileUsedInDSK(
tmpStrCat(hostSubDir, hostName))) {
807 int diskSpace = (nofSectors - firstDataSector) *
SECTOR_SIZE;
808 if (fst.st_size > diskSpace) {
810 hostDir, hostSubDir, hostName);
814 DirIndex dirIndex = fillMSXDirEntry(hostSubDir, hostName, msxDirSector);
815 importHostFile(dirIndex, fst);
818 DirAsDSK::DirIndex DirAsDSK::fillMSXDirEntry(
819 const string& hostSubDir,
const string& hostName,
unsigned msxDirSector)
821 string hostPath = hostSubDir + hostName;
824 DirIndex dirIndex = getFreeDirEntry(msxDirSector);
827 string msxFilename = hostToMsxName(hostName);
828 if (checkMSXFileExists(msxFilename, msxDirSector)) {
831 "MSX name ", msxToHostName(msxFilename.c_str()),
836 assert(!hostPath.ends_with(
'/'));
837 mapDirs[dirIndex].hostName = hostPath;
838 memset(&msxDir(dirIndex), 0,
sizeof(MSXDirEntry));
839 memcpy(msxDir(dirIndex).
filename, msxFilename.data(), 8 + 3);
841 }
catch (MSXException& e) {
842 throw MSXException(
"Couldn't add ", hostPath,
": ",
847 DirAsDSK::DirIndex DirAsDSK::getFreeDirEntry(
unsigned msxDirSector)
849 vector<bool> visited(nofSectors,
false);
851 if (visited[msxDirSector]) {
853 throw MSXException(
"cycle in FAT");
855 visited[msxDirSector] =
true;
858 DirIndex dirIndex(msxDirSector, idx);
859 const char* msxName = msxDir(dirIndex).
filename;
860 if (msxName[0] ==
one_of(
char(0x00),
char(0xE5))) {
863 assert(!mapDirs.contains(dirIndex));
867 unsigned sector = nextMsxDirSector(msxDirSector);
868 if (sector ==
unsigned(-1))
break;
869 msxDirSector = sector;
873 if (msxDirSector == (firstDataSector - 1)) {
875 throw MSXException(
"root directory full");
880 unsigned cluster = sectorToCluster(msxDirSector);
881 unsigned newCluster = getFreeCluster();
882 unsigned sector = clusterToSector(newCluster);
884 writeFAT12(cluster, newCluster);
885 writeFAT12(newCluster,
EOF_FAT);
893 assert(sector_ < nofSectors);
895 auto sector = unsigned(sector_);
899 lastAccess = scheduler->getCurrentTime();
908 }
else if (sector < firstSector2ndFAT) {
909 writeFATSector(sector, buf);
910 }
else if (sector < firstDirSector) {
913 memcpy(§ors[sector], &buf,
sizeof(buf));
914 }
else if (DirIndex dirDirIndex; isDirSector(sector, dirDirIndex)) {
916 writeDIRSector(sector, dirDirIndex, buf);
918 writeDataSector(sector, buf);
922 void DirAsDSK::writeFATSector(
unsigned sector,
const SectorBuffer& buf)
925 vector<SectorBuffer> oldFAT(nofSectorsPerFat);
926 memcpy(&oldFAT[0], fat(),
SECTOR_SIZE * nofSectorsPerFat);
929 memcpy(§ors[sector], &buf,
sizeof(buf));
933 if (readFAT(i) != readFATHelper(oldFAT.data(), i)) {
934 exportFileFromFATChange(i, oldFAT.data());
945 assert(readFAT(i) == readFATHelper(oldFAT.data(), i)); (void)i;
949 void DirAsDSK::exportFileFromFATChange(
unsigned cluster, SectorBuffer* oldFAT)
952 auto [startCluster, chainLength] = getChainStart(cluster);
956 vector<bool> visited(maxCluster,
false);
957 unsigned tmp = startCluster;
965 unsigned next = readFAT(tmp);
966 writeFATHelper(oldFAT, tmp, next);
972 DirIndex dirIndex, dirDirIndex;
973 if (getDirEntryForCluster(startCluster, dirIndex, dirDirIndex)) {
974 exportToHost(dirIndex, dirDirIndex);
978 std::pair<unsigned, unsigned> DirAsDSK::getChainStart(
unsigned cluster)
984 unsigned chainLength = 0;
986 if (readFAT(i) == cluster) {
993 return {cluster, chainLength};
999 template<
typename FUNC>
bool DirAsDSK::scanMsxDirs(FUNC func,
unsigned sector)
1002 vector<unsigned> dirs;
1003 vector<DirIndex> dirs2;
1007 if (func.onDirSector(sector))
return true;
1011 DirIndex dirIndex(sector, idx);
1012 const MSXDirEntry& entry = msxDir(dirIndex);
1013 if (func.onDirEntry(dirIndex, entry))
return true;
1015 if ((entry.filename[0] ==
one_of(
char(0x00),
char(0xE5))) ||
1022 (cluster >= maxCluster)) {
1028 unsigned dir = clusterToSector(cluster);
1039 dirs.push_back(dir);
1040 dirs2.push_back(dirIndex);
1042 sector = nextMsxDirSector(sector);
1043 }
while (sector !=
unsigned(-1));
1046 if (rdIdx == dirs.size()) {
1051 func.onVisitSubDir(dirs2[rdIdx]);
1052 sector = dirs[rdIdx++];
1097 return sector == dirSector;
1101 bool DirAsDSK::isDirSector(
unsigned sector, DirIndex& dirDirIndex)
1103 return scanMsxDirs(
IsDirSector(sector, dirDirIndex), firstDirSector);
1109 DirAsDSK::DirIndex& dirIndex_,
1110 DirAsDSK::DirIndex& dirDirIndex_)
1124 bool DirAsDSK::getDirEntryForCluster(
unsigned cluster,
1125 DirIndex& dirIndex, DirIndex& dirDirIndex)
1130 DirAsDSK::DirIndex DirAsDSK::getDirEntryForCluster(
unsigned cluster)
1132 DirIndex dirIndex, dirDirIndex;
1133 if (getDirEntryForCluster(cluster, dirIndex, dirDirIndex)) {
1136 return {unsigned(-1), unsigned(-1)};
1152 void DirAsDSK::unmapHostFiles(
unsigned msxDirSector)
1157 void DirAsDSK::exportToHost(DirIndex dirIndex, DirIndex dirDirIndex)
1164 const char* msxName = msxDir(dirIndex).
filename;
1166 if (
auto* v =
lookup(mapDirs, dirIndex)) {
1168 hostName = v->hostName;
1172 if (msxName[0] ==
one_of(
char(0x00),
char(0xE5))) {
1177 if (dirDirIndex.sector != 0) {
1179 auto* v2 =
lookup(mapDirs, dirDirIndex);
1181 hostSubDir = v2->hostName;
1182 assert(!hostSubDir.ends_with(
'/'));
1185 hostName = hostSubDir + msxToHostName(msxName);
1186 mapDirs[dirIndex].hostName = hostName;
1189 if ((memcmp(msxName,
". ", 11) == 0) ||
1190 (memcmp(msxName,
".. ", 11) == 0)) {
1194 exportToHostDir(dirIndex, hostName);
1196 exportToHostFile(dirIndex, hostName);
1200 void DirAsDSK::exportToHostDir(DirIndex dirIndex,
const string& hostName)
1208 unsigned msxDirSector = clusterToSector(cluster);
1214 vector<bool> visited(nofSectors,
false);
1216 if (visited[msxDirSector]) {
1220 visited[msxDirSector] =
true;
1222 if (readFAT(sectorToCluster(msxDirSector)) ==
FREE_FAT) {
1229 exportToHost(DirIndex(msxDirSector, idx), dirIndex);
1231 msxDirSector = nextMsxDirSector(msxDirSector);
1232 }
while (msxDirSector !=
unsigned(-1));
1233 }
catch (FileException& e) {
1234 cliComm.
printWarning(
"Error while syncing host directory: ",
1235 hostName,
": ", e.getMessage());
1239 void DirAsDSK::exportToHostFile(DirIndex dirIndex,
const string& hostName)
1247 unsigned msxSize = msxDir(dirIndex).
size;
1250 unsigned offset = 0;
1251 vector<bool> visited(maxCluster,
false);
1254 if (visited[curCl]) {
1258 visited[curCl] =
true;
1260 unsigned logicalSector = clusterToSector(curCl);
1262 if (offset >= msxSize)
break;
1263 unsigned sector = logicalSector + i;
1264 assert(sector < nofSectors);
1265 auto writeSize = std::min<size_t>(msxSize - offset,
SECTOR_SIZE);
1266 file.write(§ors[sector], writeSize);
1269 if (offset >= msxSize)
break;
1270 curCl = readFAT(curCl);
1272 }
catch (FileException& e) {
1273 cliComm.
printWarning(
"Error while syncing host file: ",
1274 hostName,
": ", e.getMessage());
1278 void DirAsDSK::writeDIRSector(
unsigned sector, DirIndex dirDirIndex,
1279 const SectorBuffer& buf)
1283 const auto& newEntry = buf.dirEntry[idx];
1284 DirIndex dirIndex(sector, idx);
1285 if (memcmp(&msxDir(dirIndex), &newEntry,
sizeof(newEntry)) != 0) {
1286 writeDIREntry(dirIndex, dirDirIndex, newEntry);
1290 assert(memcmp(§ors[sector], &buf,
sizeof(buf)) == 0);
1293 void DirAsDSK::writeDIREntry(DirIndex dirIndex, DirIndex dirDirIndex,
1294 const MSXDirEntry& newEntry)
1296 if (memcmp(msxDir(dirIndex).
filename, newEntry.filename, 8 + 3) != 0 ||
1300 if (
auto it = mapDirs.find(dirIndex); it !=
end(mapDirs)) {
1304 auto fullHostName =
tmpStrCat(hostDir, it->second.hostName);
1313 (cluster < maxCluster)) {
1314 unmapHostFiles(clusterToSector(cluster));
1321 memcpy(&msxDir(dirIndex), &newEntry,
sizeof(newEntry));
1324 exportToHost(dirIndex, dirDirIndex);
1327 void DirAsDSK::writeDataSector(
unsigned sector,
const SectorBuffer& buf)
1329 assert(sector >= firstDataSector);
1330 assert(sector < nofSectors);
1333 memcpy(§ors[sector], &buf,
sizeof(buf));
1336 auto [cluster, offset] = sectorToClusterOffset(sector);
1337 auto [startCluster, chainLength] = getChainStart(cluster);
1341 DirIndex dirIndex = getDirEntryForCluster(startCluster);
1343 auto* v =
lookup(mapDirs, dirIndex);
1350 string fullHostName = hostDir + v->hostName;
1352 File file(fullHostName,
"rb+");
1354 unsigned msxSize = msxDir(dirIndex).
size;
1355 if (msxSize > offset) {
1356 auto writeSize = std::min<size_t>(msxSize - offset,
sizeof(buf));
1357 file.write(&buf, writeSize);
1359 }
catch (FileException& e) {
1360 cliComm.
printWarning(
"Couldn't write to file ", fullHostName,
1361 ": ", e.getMessage());
void swap(openmsx::MemBuffer< T > &l, openmsx::MemBuffer< T > &r) noexcept
static const SectorBuffer dos2BootBlock
static const SectorBuffer dos1BootBlock
void printWarning(std::string_view message)
friend struct UnmapHostFiles
friend struct IsDirSector
void writeSectorImpl(size_t sector, const SectorBuffer &buf) override
friend struct DirEntryForCluster
bool isWriteProtectedImpl() const override
DirAsDSK(DiskChanger &diskChanger, CliComm &cliComm, const Filename &hostDir, SyncMode syncMode, BootSectorType bootSectorType)
void checkCaches() override
void readSectorImpl(size_t sector, SectorBuffer &buf) override
bool hasChanged() const override
Has the content of this disk changed, by some other means than the MSX writing to the disk.
Scheduler * getScheduler() const
bool isDoubleSidedDrive() const
void setNbSides(unsigned num)
void setSectorsPerTrack(unsigned num)
static constexpr EmuDuration sec(unsigned x)
This class represents a filename.
static constexpr size_t SECTOR_SIZE
Abstract class for disk images that only represent the logical sector information (so not the raw tra...
void setNbSectors(size_t num)
void flushCaches() override
const Value * lookup(const hash_map< Key, Value, Hasher, Equal > &map, const Key2 &key)
std::pair< string_view, string_view > splitOnLast(string_view str, string_view chars)
constexpr vecN< N, T > min(const vecN< N, T > &x, const vecN< N, T > &y)
string expandTilde(string path)
Expand the '~' character to the users home directory.
bool getStat(zstring_view filename, Stat &st)
Call stat() and return the stat structure.
bool isRegularFile(const Stat &st)
bool isDirectory(const Stat &st)
int deleteRecursive(zstring_view path)
Recursively delete a file or directory and (in case of a directory) all its sub-components.
void mkdirp(string path)
Acts like the unix command "mkdir -p".
This file implemented 3 utility functions:
constexpr unsigned FREE_FAT
constexpr unsigned SECTORS_PER_CLUSTER
constexpr unsigned SECTORS_PER_TRACK
constexpr unsigned SECTOR_SIZE
constexpr unsigned EOF_FAT
constexpr unsigned NUM_FATS
constexpr unsigned NUM_TRACKS
constexpr unsigned DIR_ENTRIES_PER_SECTOR
constexpr unsigned SECTORS_PER_DIR
constexpr unsigned FIRST_FAT_SECTOR
constexpr const char *const filename
constexpr unsigned BAD_FAT
constexpr unsigned FIRST_CLUSTER
void replace(ForwardRange &&range, const T &old_value, const T &new_value)
auto count(InputRange &&range, const T &value)
auto copy(InputRange &&range, OutputIter out)
void sort(RandomAccessRange &&range)
uint32_t next(octet_iterator &it, octet_iterator end)
auto transform_in_place(ForwardRange &&range, UnaryOperation op)
constexpr bool contains(ITER first, ITER last, const VAL &val)
Check if a range contains a given value, using linear search.
TemporaryString tmpStrCat(Ts &&... ts)
std::string strCat(Ts &&...ts)
bool onDirEntry(DirAsDSK::DirIndex dirIndex, const MSXDirEntry &entry)
DirEntryForCluster(unsigned cluster_, DirAsDSK::DirIndex &dirIndex_, DirAsDSK::DirIndex &dirDirIndex_)
DirAsDSK::DirIndex & result
void onVisitSubDir(DirAsDSK::DirIndex subdir)
DirAsDSK::DirIndex & dirDirIndex
DirScanner(DirAsDSK::DirIndex &dirDirIndex_)
IsDirSector(unsigned sector_, DirAsDSK::DirIndex &dirDirIndex_)
bool onDirSector(unsigned dirSector) const
static constexpr byte ATT_DIRECTORY
static constexpr byte ATT_VOLUME
bool onDirEntry(DirAsDSK::DirIndex, const MSXDirEntry &)
void onVisitSubDir(DirAsDSK::DirIndex)
bool onDirSector(unsigned)
bool onDirEntry(DirAsDSK::DirIndex dirIndex, const MSXDirEntry &)
DirAsDSK::MapDirs & mapDirs
UnmapHostFiles(DirAsDSK::MapDirs &mapDirs_)
constexpr auto xrange(T e)
constexpr auto end(const zstring_view &x)