24static constexpr unsigned SECTOR_SIZE =
sizeof(SectorBuffer);
25static constexpr unsigned SECTORS_PER_DIR = 7;
26static constexpr unsigned NUM_FATS = 2;
27static constexpr unsigned NUM_TRACKS = 80;
28static constexpr unsigned SECTORS_PER_CLUSTER = 2;
29static constexpr unsigned SECTORS_PER_TRACK = 9;
30static constexpr unsigned FIRST_FAT_SECTOR = 1;
31static constexpr unsigned DIR_ENTRIES_PER_SECTOR =
32 SECTOR_SIZE /
sizeof(MSXDirEntry);
35static constexpr unsigned FIRST_CLUSTER = 2;
37static constexpr unsigned FREE_FAT = 0x000;
38static constexpr unsigned BAD_FAT = 0xFF7;
39static constexpr unsigned EOF_FAT = 0xFFF;
44[[nodiscard]]
static constexpr unsigned normalizeFAT(
unsigned cluster)
46 return (cluster < BAD_FAT) ? cluster : EOF_FAT;
49unsigned DirAsDSK::readFATHelper(std::span<const SectorBuffer> fatBuf,
unsigned cluster)
const
51 assert(FIRST_CLUSTER <= cluster);
52 assert(cluster < maxCluster);
53 std::span buf{fatBuf[0].raw.data(), fatBuf.size() *
SECTOR_SIZE};
54 auto p = subspan<2>(buf, (cluster * 3) / 2);
55 unsigned result = (cluster & 1)
56 ? (p[0] >> 4) + (p[1] << 4)
57 : p[0] + ((p[1] & 0x0F) << 8);
58 return normalizeFAT(result);
61void DirAsDSK::writeFATHelper(std::span<SectorBuffer> fatBuf,
unsigned cluster,
unsigned val)
const
63 assert(FIRST_CLUSTER <= cluster);
64 assert(cluster < maxCluster);
65 std::span buf{fatBuf[0].raw.data(), fatBuf.size() *
SECTOR_SIZE};
66 auto p = subspan<2>(buf, (cluster * 3) / 2);
68 p[0] = narrow_cast<uint8_t>((p[0] & 0x0F) + (val << 4));
69 p[1] = narrow_cast<uint8_t>(val >> 4);
71 p[0] = narrow_cast<uint8_t>(val);
72 p[1] = narrow_cast<uint8_t>((p[1] & 0xF0) + ((val >> 8) & 0x0F));
76std::span<SectorBuffer> DirAsDSK::fat()
78 return {§ors[FIRST_FAT_SECTOR], nofSectorsPerFat};
80std::span<SectorBuffer> DirAsDSK::fat2()
82 return {§ors[firstSector2ndFAT], nofSectorsPerFat};
86unsigned DirAsDSK::readFAT(
unsigned cluster)
88 return readFATHelper(fat(), cluster);
92void DirAsDSK::writeFAT12(
unsigned cluster,
unsigned val)
94 writeFATHelper(fat (), cluster, val);
95 writeFATHelper(fat2(), cluster, val);
101unsigned DirAsDSK::findNextFreeCluster(
unsigned cluster)
103 assert(cluster < maxCluster);
106 assert(cluster >= FIRST_CLUSTER);
107 }
while ((cluster < maxCluster) && (readFAT(cluster) != FREE_FAT));
110unsigned DirAsDSK::findFirstFreeCluster()
112 return findNextFreeCluster(FIRST_CLUSTER - 1);
116unsigned DirAsDSK::getFreeCluster()
118 unsigned cluster = findFirstFreeCluster();
119 if (cluster == maxCluster) {
120 throw MSXException(
"disk full");
125unsigned DirAsDSK::clusterToSector(
unsigned cluster)
const
127 assert(cluster >= FIRST_CLUSTER);
128 assert(cluster < maxCluster);
129 return firstDataSector + SECTORS_PER_CLUSTER *
130 (cluster - FIRST_CLUSTER);
133std::pair<unsigned, unsigned> DirAsDSK::sectorToClusterOffset(
unsigned sector)
const
135 assert(sector >= firstDataSector);
136 assert(sector < nofSectors);
137 sector -= firstDataSector;
138 unsigned cluster = (sector / SECTORS_PER_CLUSTER) + FIRST_CLUSTER;
139 unsigned offset = (sector % SECTORS_PER_CLUSTER) *
SECTOR_SIZE;
140 return {cluster, offset};
142unsigned DirAsDSK::sectorToCluster(
unsigned sector)
const
144 auto [cluster, offset] = sectorToClusterOffset(sector);
148MSXDirEntry& DirAsDSK::msxDir(DirIndex dirIndex)
150 assert(dirIndex.sector < nofSectors);
151 assert(dirIndex.idx < DIR_ENTRIES_PER_SECTOR);
152 return sectors[dirIndex.sector].dirEntry[dirIndex.idx];
156unsigned DirAsDSK::nextMsxDirSector(
unsigned sector)
158 if (sector < firstDataSector) {
160 assert(firstDirSector <= sector);
162 if (sector == firstDataSector) {
169 auto [cluster, offset] = sectorToClusterOffset(sector);
170 if (offset < ((SECTORS_PER_CLUSTER - 1) *
SECTOR_SIZE)) {
174 unsigned nextCl = readFAT(cluster);
175 if ((nextCl < FIRST_CLUSTER) || (maxCluster <= nextCl)) {
179 return clusterToSector(nextCl);
184bool DirAsDSK::checkMSXFileExists(
185 std::span<const char, 11> msxFilename,
unsigned msxDirSector)
187 vector<bool> visited(nofSectors,
false);
189 if (visited[msxDirSector]) {
193 visited[msxDirSector] =
true;
195 for (
auto idx :
xrange(DIR_ENTRIES_PER_SECTOR)) {
196 DirIndex dirIndex(msxDirSector, idx);
201 msxDirSector = nextMsxDirSector(msxDirSector);
202 }
while (msxDirSector !=
unsigned(-1));
210DirAsDSK::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)};
221bool DirAsDSK::checkFileUsedInDSK(std::string_view hostName)
223 DirIndex dirIndex = findHostFileInDSK(hostName);
224 return dirIndex.sector != unsigned(-1);
227static std::array<char, 11> hostToMsxName(
string hostName)
231 return (a ==
' ') ?
'_' : ::toupper(a);
236 std::array<char, 8 + 3> result;
244static string msxToHostName(std::span<const char, 11> msxName)
247 for (
unsigned i = 0; (i < 8) && (msxName[i] !=
' '); ++i) {
248 result += char(tolower(msxName[i]));
250 if (msxName[8] !=
' ') {
252 for (
unsigned i = 8; (i < (8 + 3)) && (msxName[i] !=
' '); ++i) {
253 result += char(tolower(msxName[i]));
264 , diskChanger(diskChanger_)
266 , hostDir(FileOperations::
expandTilde(hostDir_.getResolved() +
'/'))
267 , syncMode(syncMode_)
268 , lastAccess(EmuTime::zero())
269 , nofSectors((diskChanger_.isDoubleSidedDrive() ? 2 : 1) * SECTORS_PER_TRACK * NUM_TRACKS)
270 , nofSectorsPerFat(
narrow<unsigned>((((3 * nofSectors) / (2 * SECTORS_PER_CLUSTER)) + SECTOR_SIZE - 1) / SECTOR_SIZE))
271 , firstSector2ndFAT(FIRST_FAT_SECTOR + nofSectorsPerFat)
272 , firstDirSector(FIRST_FAT_SECTOR + NUM_FATS * nofSectorsPerFat)
273 , firstDataSector(firstDirSector + SECTORS_PER_DIR)
274 , maxCluster((nofSectors - firstDataSector) / SECTORS_PER_CLUSTER + FIRST_CLUSTER)
275 , sectors(nofSectors)
292 uint8_t mediaDescriptor = (numSides == 2) ? 0xF9 : 0xF8;
296 sectors[0] = protoBootSector;
297 auto& bootSector = sectors[0].bootSector;
299 bootSector.spCluster = SECTORS_PER_CLUSTER;
300 bootSector.nrFats = NUM_FATS;
302 bootSector.nrSectors = narrow_cast<uint16_t>(nofSectors);
303 bootSector.descriptor = mediaDescriptor;
304 bootSector.sectorsFat = narrow_cast<uint16_t>(nofSectorsPerFat);
305 bootSector.sectorsTrack = SECTORS_PER_TRACK;
306 bootSector.nrSides = numSides;
314 auto init = [&](
auto f) {
315 f[0].raw[0] = mediaDescriptor;
326 assert(mapDirs.empty());
349 bool needSync = [&] {
351 auto now = scheduler->getCurrentTime();
352 auto delta = now - lastAccess;
368 assert(sector < nofSectors);
375 bool needSync = [&] {
377 auto now = scheduler->getCurrentTime();
378 auto delta = now - lastAccess;
397 buf = sectors[sector];
400void DirAsDSK::syncWithHost()
405 checkDeletedHostFiles();
411 checkModifiedHostFiles();
414 addNewHostFiles({}, firstDirSector);
417void DirAsDSK::checkDeletedHostFiles()
421 for (
const auto& [dirIdx, mapDir] :
copy) {
422 if (!mapDirs.contains(dirIdx)) {
433 auto fullHostName =
tmpStrCat(hostDir, mapDir.hostName);
434 bool isMSXDirectory = (msxDir(dirIdx).
attrib &
444 deleteMSXFile(dirIdx);
449void DirAsDSK::deleteMSXFile(DirIndex dirIndex)
452 mapDirs.erase(dirIndex);
454 char c = msxDir(dirIndex).
filename[0];
455 if (c ==
one_of(0,
char(0xE5))) {
463 const auto& msxName = msxDir(dirIndex).
filename;
471 if ((FIRST_CLUSTER <= cluster) && (cluster < maxCluster)) {
473 deleteMSXFilesInDir(clusterToSector(cluster));
479 msxDir(dirIndex).
filename[0] = char(0xE5);
482 freeFATChain(msxDir(dirIndex).startCluster);
485void DirAsDSK::deleteMSXFilesInDir(
unsigned msxDirSector)
487 vector<bool> visited(nofSectors,
false);
489 if (visited[msxDirSector]) {
493 visited[msxDirSector] =
true;
495 for (
auto idx :
xrange(DIR_ENTRIES_PER_SECTOR)) {
496 deleteMSXFile(DirIndex(msxDirSector, idx));
498 msxDirSector = nextMsxDirSector(msxDirSector);
499 }
while (msxDirSector !=
unsigned(-1));
502void DirAsDSK::freeFATChain(
unsigned cluster)
505 while ((FIRST_CLUSTER <= cluster) && (cluster < maxCluster)) {
506 unsigned nextCl = readFAT(cluster);
507 writeFAT12(cluster, FREE_FAT);
512void DirAsDSK::checkModifiedHostFiles()
515 for (
const auto& [dirIdx, mapDir] :
copy) {
516 if (!mapDirs.contains(dirIdx)) {
520 auto fullHostName =
tmpStrCat(hostDir, mapDir.hostName);
521 bool isMSXDirectory = (msxDir(dirIdx).
attrib &
534 if (!isMSXDirectory &&
535 ((mapDir.mtime != fst->st_mtime) ||
536 (mapDir.filesize !=
size_t(fst->st_size)))) {
537 importHostFile(dirIdx, *fst);
542 deleteMSXFile(dirIdx);
550 assert(mapDirs.contains(dirIndex));
553 setMSXTimeStamp(dirIndex, fst);
568 size_t hostSize = fst.st_size;
569 auto& mapDir = mapDirs[dirIndex];
570 mapDir.filesize = hostSize;
571 mapDir.mtime = fst.st_mtime;
573 bool moreClustersInChain =
true;
578 if ((curCl < FIRST_CLUSTER) || (curCl >= maxCluster)) {
579 moreClustersInChain =
false;
580 curCl = findFirstFreeCluster();
583 auto remainingSize = hostSize;
586 File file(hostDir + mapDir.hostName,
589 while (remainingSize && (curCl < maxCluster)) {
590 unsigned logicalSector = clusterToSector(curCl);
591 for (
auto i :
xrange(SECTORS_PER_CLUSTER)) {
592 unsigned sector = logicalSector + i;
593 assert(sector < nofSectors);
594 auto* buf = §ors[sector];
596 file.read(
subspan(buf->raw, 0, sz));
599 if (remainingSize == 0) {
607 writeFAT12(prevCl, curCl);
609 msxDir(dirIndex).
startCluster = narrow<uint16_t>(curCl);
615 if (moreClustersInChain) {
616 curCl = readFAT(curCl);
617 if ((curCl == EOF_FAT) ||
618 (curCl < FIRST_CLUSTER) ||
619 (curCl >= maxCluster)) {
622 moreClustersInChain =
false;
623 curCl = findFirstFreeCluster();
626 curCl = findNextFreeCluster(curCl);
629 if (remainingSize != 0) {
631 mapDir.hostName,
" truncated.");
633 }
catch (FileException&
e) {
636 mapDir.hostName,
": ",
e.getMessage(),
637 " Truncated file on MSX disk.");
643 writeFAT12(prevCl, EOF_FAT);
651 if (moreClustersInChain) {
656 msxDir(dirIndex).
size = uint32_t(hostSize - remainingSize);
668 time_t mtime = fst.st_mtime;
669 auto* mtim = localtime(&mtime);
670 int t1 = mtim ? (mtim->tm_sec >> 1) + (mtim->tm_min << 5) +
671 (mtim->tm_hour << 11)
673 msxDir(dirIndex).
time = narrow<uint16_t>(t1);
674 int t2 = mtim ? mtim->tm_mday + ((mtim->tm_mon + 1) << 5) +
675 ((mtim->tm_year + 1900 - 1980) << 9)
677 msxDir(dirIndex).
date = narrow<uint16_t>(t2);
688static size_t weight(
const string& hostName)
696 result += ext.size() * 10;
698 result += file.size();
702void DirAsDSK::addNewHostFiles(
const string& hostSubDir,
unsigned msxDirSector)
704 assert(!hostSubDir.starts_with(
'/'));
705 assert(hostSubDir.empty() || hostSubDir.ends_with(
'/'));
707 vector<string> hostNames;
709 ReadDir dir(
tmpStrCat(hostDir, hostSubDir));
710 while (
auto* d = dir.getEntry()) {
711 hostNames.emplace_back(d->d_name);
714 ranges::sort(hostNames, {}, [](
const string& n) {
return weight(n); });
716 for (
auto& hostName : hostNames) {
718 if (hostName.starts_with(
'.')) {
723 auto fullHostName =
tmpStrCat(hostDir, hostSubDir, hostName);
726 throw MSXException(
"Error accessing ", fullHostName);
729 addNewDirectory(hostSubDir, hostName, msxDirSector, *fst);
731 addNewHostFile(hostSubDir, hostName, msxDirSector, *fst);
733 throw MSXException(
"Not a regular file: ", fullHostName);
735 }
catch (MSXException&
e) {
741void DirAsDSK::addNewDirectory(
const string& hostSubDir,
const string& hostName,
744 DirIndex dirIndex = findHostFileInDSK(
tmpStrCat(hostSubDir, hostName));
745 unsigned newMsxDirSector;
746 if (dirIndex.sector ==
unsigned(-1)) {
749 unsigned cluster = getFreeCluster();
750 writeFAT12(cluster, EOF_FAT);
754 dirIndex = fillMSXDirEntry(hostSubDir, hostName, msxDirSector);
757 writeFAT12(cluster, FREE_FAT);
760 setMSXTimeStamp(dirIndex, fst);
762 msxDir(dirIndex).
startCluster = narrow<uint16_t>(cluster);
765 newMsxDirSector = clusterToSector(cluster);
766 ranges::fill(std::span{sectors[newMsxDirSector].raw.data(),
769 DirIndex idx0(newMsxDirSector, 0);
770 DirIndex idx1(newMsxDirSector, 1);
771 auto& e0 = msxDir(idx0);
772 auto& e1 = msxDir(idx1);
773 auto& f0 = e0.filename;
774 auto& f1 = e1.filename;
779 setMSXTimeStamp(idx0, fst);
780 setMSXTimeStamp(idx1, fst);
781 e0.startCluster = narrow<uint16_t>(cluster);
782 e1.startCluster = msxDirSector == firstDirSector
784 :
narrow<uint16_t>(sectorToCluster(msxDirSector));
795 if ((cluster < FIRST_CLUSTER) || (cluster >= maxCluster)) {
799 newMsxDirSector = clusterToSector(cluster);
803 addNewHostFiles(
strCat(hostSubDir, hostName,
'/'), newMsxDirSector);
806void DirAsDSK::addNewHostFile(
const string& hostSubDir,
const string& hostName,
809 if (checkFileUsedInDSK(
tmpStrCat(hostSubDir, hostName))) {
814 auto diskSpace = (nofSectors - firstDataSector) *
SECTOR_SIZE;
815 if (narrow<size_t>(fst.st_size) > diskSpace) {
817 hostDir, hostSubDir, hostName);
821 DirIndex dirIndex = fillMSXDirEntry(hostSubDir, hostName, msxDirSector);
822 importHostFile(dirIndex, fst);
825DirAsDSK::DirIndex DirAsDSK::fillMSXDirEntry(
826 const string& hostSubDir,
const string& hostName,
unsigned msxDirSector)
828 string hostPath = hostSubDir + hostName;
831 DirIndex dirIndex = getFreeDirEntry(msxDirSector);
834 auto msxFilename = hostToMsxName(hostName);
835 if (checkMSXFileExists(msxFilename, msxDirSector)) {
838 "MSX name ", msxToHostName(msxFilename),
843 assert(!hostPath.ends_with(
'/'));
844 mapDirs[dirIndex].hostName = hostPath;
845 memset(&msxDir(dirIndex), 0,
sizeof(MSXDirEntry));
848 }
catch (MSXException&
e) {
849 throw MSXException(
"Couldn't add ", hostPath,
": ",
854DirAsDSK::DirIndex DirAsDSK::getFreeDirEntry(
unsigned msxDirSector)
856 vector<bool> visited(nofSectors,
false);
858 if (visited[msxDirSector]) {
860 throw MSXException(
"cycle in FAT");
862 visited[msxDirSector] =
true;
864 for (
auto idx :
xrange(DIR_ENTRIES_PER_SECTOR)) {
865 DirIndex dirIndex(msxDirSector, idx);
866 const auto& msxName = msxDir(dirIndex).
filename;
867 if (msxName[0] ==
one_of(
char(0x00),
char(0xE5))) {
870 assert(!mapDirs.contains(dirIndex));
874 unsigned sector = nextMsxDirSector(msxDirSector);
875 if (sector ==
unsigned(-1))
break;
876 msxDirSector = sector;
880 if (msxDirSector == (firstDataSector - 1)) {
882 throw MSXException(
"root directory full");
887 unsigned cluster = sectorToCluster(msxDirSector);
888 unsigned newCluster = getFreeCluster();
889 unsigned sector = clusterToSector(newCluster);
891 writeFAT12(cluster, newCluster);
892 writeFAT12(newCluster, EOF_FAT);
900 assert(sector_ < nofSectors);
902 auto sector = unsigned(sector_);
906 lastAccess = scheduler->getCurrentTime();
915 }
else if (sector < firstSector2ndFAT) {
916 writeFATSector(sector, buf);
917 }
else if (sector < firstDirSector) {
920 sectors[sector] = buf;
921 }
else if (DirIndex dirDirIndex; isDirSector(sector, dirDirIndex)) {
923 writeDIRSector(sector, dirDirIndex, buf);
925 writeDataSector(sector, buf);
929void DirAsDSK::writeFATSector(
unsigned sector,
const SectorBuffer& buf)
935 sectors[sector] = buf;
938 for (
auto i :
xrange(FIRST_CLUSTER, maxCluster)) {
939 if (readFAT(i) != readFATHelper(oldFAT, i)) {
940 exportFileFromFATChange(i, oldFAT);
950 for (
auto i :
xrange(FIRST_CLUSTER, maxCluster)) {
951 assert(readFAT(i) == readFATHelper(oldFAT, i)); (void)i;
955void DirAsDSK::exportFileFromFATChange(
unsigned cluster, std::span<SectorBuffer> oldFAT)
958 auto [startCluster, chainLength] = getChainStart(cluster);
962 vector<bool> visited(maxCluster,
false);
963 unsigned tmp = startCluster;
964 while ((FIRST_CLUSTER <= tmp) && (tmp < maxCluster)) {
971 unsigned next = readFAT(tmp);
972 writeFATHelper(oldFAT, tmp, next);
978 DirIndex dirIndex, dirDirIndex;
979 if (getDirEntryForCluster(startCluster, dirIndex, dirDirIndex)) {
980 exportToHost(dirIndex, dirDirIndex);
984std::pair<unsigned, unsigned> DirAsDSK::getChainStart(
unsigned cluster)
990 unsigned chainLength = 0;
991 for (
unsigned i = FIRST_CLUSTER; i < maxCluster; ++i) {
992 if (readFAT(i) == cluster) {
996 i = FIRST_CLUSTER - 1;
999 return {cluster, chainLength};
1005template<
typename FUNC>
bool DirAsDSK::scanMsxDirs(FUNC func,
unsigned sector)
1008 vector<unsigned> dirs;
1009 vector<DirIndex> dirs2;
1013 if (func.onDirSector(sector))
return true;
1015 for (
auto idx :
xrange(DIR_ENTRIES_PER_SECTOR)) {
1017 DirIndex dirIndex(sector, idx);
1018 const MSXDirEntry& entry = msxDir(dirIndex);
1019 if (func.onDirEntry(dirIndex, entry))
return true;
1021 if ((entry.filename[0] ==
one_of(
char(0x00),
char(0xE5))) ||
1027 if ((cluster < FIRST_CLUSTER) ||
1028 (cluster >= maxCluster)) {
1034 unsigned dir = clusterToSector(cluster);
1045 dirs.push_back(dir);
1046 dirs2.push_back(dirIndex);
1048 sector = nextMsxDirSector(sector);
1049 }
while (sector !=
unsigned(-1));
1052 if (rdIdx == dirs.size()) {
1057 func.onVisitSubDir(dirs2[rdIdx]);
1058 sector = dirs[rdIdx++];
1103 return sector == dirSector;
1107bool DirAsDSK::isDirSector(
unsigned sector, DirIndex& dirDirIndex)
1109 return scanMsxDirs(
IsDirSector(sector, dirDirIndex), firstDirSector);
1115 DirAsDSK::DirIndex& dirIndex_,
1116 DirAsDSK::DirIndex& dirDirIndex_)
1130bool DirAsDSK::getDirEntryForCluster(
unsigned cluster,
1131 DirIndex& dirIndex, DirIndex& dirDirIndex)
1136DirAsDSK::DirIndex DirAsDSK::getDirEntryForCluster(
unsigned cluster)
1138 DirIndex dirIndex, dirDirIndex;
1139 if (getDirEntryForCluster(cluster, dirIndex, dirDirIndex)) {
1142 return {unsigned(-1), unsigned(-1)};
1158void DirAsDSK::unmapHostFiles(
unsigned msxDirSector)
1163void DirAsDSK::exportToHost(DirIndex dirIndex, DirIndex dirDirIndex)
1170 const auto& msxName = msxDir(dirIndex).
filename;
1172 if (
auto* v =
lookup(mapDirs, dirIndex)) {
1174 hostName = v->hostName;
1178 if (msxName[0] ==
one_of(
char(0x00),
char(0xE5))) {
1183 if (dirDirIndex.sector != 0) {
1185 auto* v2 =
lookup(mapDirs, dirDirIndex);
1187 hostSubDir = v2->hostName;
1188 assert(!hostSubDir.ends_with(
'/'));
1191 hostName = hostSubDir + msxToHostName(msxName);
1192 mapDirs[dirIndex].hostName = hostName;
1200 exportToHostDir(dirIndex, hostName);
1202 exportToHostFile(dirIndex, hostName);
1206void DirAsDSK::exportToHostDir(DirIndex dirIndex,
const string& hostName)
1210 if ((cluster < FIRST_CLUSTER) || (cluster >= maxCluster)) {
1214 unsigned msxDirSector = clusterToSector(cluster);
1220 vector<bool> visited(nofSectors,
false);
1222 if (visited[msxDirSector]) {
1226 visited[msxDirSector] =
true;
1228 if (readFAT(sectorToCluster(msxDirSector)) == FREE_FAT) {
1234 for (
auto idx :
xrange(DIR_ENTRIES_PER_SECTOR)) {
1235 exportToHost(DirIndex(msxDirSector, idx), dirIndex);
1237 msxDirSector = nextMsxDirSector(msxDirSector);
1238 }
while (msxDirSector !=
unsigned(-1));
1239 }
catch (FileException&
e) {
1240 cliComm.
printWarning(
"Error while syncing host directory: ",
1241 hostName,
": ",
e.getMessage());
1245void DirAsDSK::exportToHostFile(DirIndex dirIndex,
const string& hostName)
1253 unsigned msxSize = msxDir(dirIndex).
size;
1256 unsigned offset = 0;
1257 vector<bool> visited(maxCluster,
false);
1259 while ((FIRST_CLUSTER <= curCl) && (curCl < maxCluster)) {
1260 if (visited[curCl]) {
1264 visited[curCl] =
true;
1266 unsigned logicalSector = clusterToSector(curCl);
1267 for (
auto i :
xrange(SECTORS_PER_CLUSTER)) {
1268 if (offset >= msxSize)
break;
1269 unsigned sector = logicalSector + i;
1270 assert(sector < nofSectors);
1271 auto writeSize = std::min<size_t>(msxSize - offset,
SECTOR_SIZE);
1272 file.write(
subspan(sectors[sector].raw, 0, writeSize));
1275 if (offset >= msxSize)
break;
1276 curCl = readFAT(curCl);
1278 }
catch (FileException&
e) {
1279 cliComm.
printWarning(
"Error while syncing host file: ",
1280 hostName,
": ",
e.getMessage());
1284void DirAsDSK::writeDIRSector(
unsigned sector, DirIndex dirDirIndex,
1285 const SectorBuffer& buf)
1288 for (
auto idx :
xrange(DIR_ENTRIES_PER_SECTOR)) {
1289 const auto& newEntry = buf.dirEntry[idx];
1290 DirIndex dirIndex(sector, idx);
1291 if (msxDir(dirIndex) != newEntry) {
1292 writeDIREntry(dirIndex, dirDirIndex, newEntry);
1299void DirAsDSK::writeDIREntry(DirIndex dirIndex, DirIndex dirDirIndex,
1300 const MSXDirEntry& newEntry)
1302 if ((msxDir(dirIndex).filename != newEntry.
filename) ||
1306 if (
auto it = mapDirs.find(dirIndex); it !=
end(mapDirs)) {
1310 auto fullHostName =
tmpStrCat(hostDir, it->second.hostName);
1318 if ((FIRST_CLUSTER <= cluster) &&
1319 (cluster < maxCluster)) {
1320 unmapHostFiles(clusterToSector(cluster));
1327 msxDir(dirIndex) = newEntry;
1330 exportToHost(dirIndex, dirDirIndex);
1333void DirAsDSK::writeDataSector(
unsigned sector,
const SectorBuffer& buf)
1335 assert(sector >= firstDataSector);
1336 assert(sector < nofSectors);
1339 sectors[sector] = buf;
1342 auto [cluster, offset] = sectorToClusterOffset(sector);
1343 auto [startCluster, chainLength] = getChainStart(cluster);
1344 offset += narrow<unsigned>((
sizeof(buf) * SECTORS_PER_CLUSTER) * chainLength);
1347 DirIndex dirIndex = getDirEntryForCluster(startCluster);
1349 auto* v =
lookup(mapDirs, dirIndex);
1356 string fullHostName = hostDir + v->hostName;
1358 File file(fullHostName,
"rb+");
1360 unsigned msxSize = msxDir(dirIndex).
size;
1361 if (msxSize > offset) {
1362 auto writeSize = std::min<size_t>(msxSize - offset,
sizeof(buf));
1363 file.write(
subspan(buf.raw, 0, writeSize));
1365 }
catch (FileException&
e) {
1366 cliComm.
printWarning(
"Couldn't write to file ", fullHostName,
1367 ": ",
e.getMessage());
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.
bool isDoubleSidedDrive() const
Scheduler * getScheduler() 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 isRegularFile(const Stat &st)
bool isDirectory(const Stat &st)
std::optional< Stat > getStat(zstring_view filename)
Call stat() and return the stat structure.
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 void fill(ForwardRange &&range, const T &value)
auto copy(InputRange &&range, OutputIter out)
auto count(InputRange &&range, const T &value)
bool equal(InputRange1 &&range1, InputRange2 &&range2, Pred pred={}, Proj1 proj1={}, Proj2 proj2={})
constexpr void replace(ForwardRange &&range, const T &old_value, const T &new_value)
constexpr void sort(RandomAccessRange &&range)
void swap(openmsx::MemBuffer< T > &l, openmsx::MemBuffer< T > &r) noexcept
size_t size(std::string_view utf8)
uint32_t next(octet_iterator &it, octet_iterator end)
constexpr To narrow(From from) noexcept
constexpr auto subspan(Range &&range, size_t offset, size_t count=std::dynamic_extent)
auto to_vector(Range &&range) -> std::vector< detail::ToVectorType< T, decltype(std::begin(range))> >
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 uint8_t ATT_VOLUME
static constexpr uint8_t ATT_DIRECTORY
std::array< char, 8+3 > filename
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)