27static constexpr unsigned SECTOR_SIZE =
sizeof(SectorBuffer);
28static constexpr unsigned SECTORS_PER_DIR = 7;
29static constexpr unsigned NUM_FATS = 2;
30static constexpr unsigned NUM_TRACKS = 80;
31static constexpr unsigned SECTORS_PER_CLUSTER = 2;
32static constexpr unsigned SECTORS_PER_TRACK = 9;
33static constexpr unsigned FIRST_FAT_SECTOR = 1;
34static constexpr unsigned DIR_ENTRIES_PER_SECTOR =
35 SECTOR_SIZE /
sizeof(MSXDirEntry);
38static constexpr unsigned FIRST_CLUSTER = 2;
40static constexpr unsigned FREE_FAT = 0x000;
41static constexpr unsigned BAD_FAT = 0xFF7;
42static constexpr unsigned EOF_FAT = 0xFFF;
47[[nodiscard]]
static constexpr unsigned normalizeFAT(
unsigned cluster)
49 return (cluster < BAD_FAT) ? cluster : EOF_FAT;
52unsigned DirAsDSK::readFATHelper(std::span<const SectorBuffer> fatBuf,
unsigned cluster)
const
54 assert(FIRST_CLUSTER <= cluster);
55 assert(cluster < maxCluster);
56 std::span buf{fatBuf[0].raw.data(), fatBuf.size() * SECTOR_SIZE};
57 auto p = subspan<2>(buf, (cluster * 3) / 2);
58 unsigned result = (cluster & 1)
59 ? (p[0] >> 4) + (p[1] << 4)
60 : p[0] + ((p[1] & 0x0F) << 8);
61 return normalizeFAT(result);
64void DirAsDSK::writeFATHelper(std::span<SectorBuffer> fatBuf,
unsigned cluster,
unsigned val)
const
66 assert(FIRST_CLUSTER <= cluster);
67 assert(cluster < maxCluster);
68 std::span buf{fatBuf[0].raw.data(), fatBuf.size() * SECTOR_SIZE};
69 auto p = subspan<2>(buf, (cluster * 3) / 2);
71 p[0] = narrow_cast<uint8_t>((p[0] & 0x0F) + (val << 4));
72 p[1] = narrow_cast<uint8_t>(val >> 4);
74 p[0] = narrow_cast<uint8_t>(val);
75 p[1] = narrow_cast<uint8_t>((p[1] & 0xF0) + ((val >> 8) & 0x0F));
79std::span<SectorBuffer> DirAsDSK::fat()
81 return {§ors[FIRST_FAT_SECTOR], nofSectorsPerFat};
83std::span<SectorBuffer> DirAsDSK::fat2()
85 return {§ors[firstSector2ndFAT], nofSectorsPerFat};
89unsigned DirAsDSK::readFAT(
unsigned cluster)
91 return readFATHelper(fat(), cluster);
95void DirAsDSK::writeFAT12(
unsigned cluster,
unsigned val)
97 writeFATHelper(fat (), cluster, val);
98 writeFATHelper(fat2(), cluster, val);
104unsigned DirAsDSK::findNextFreeCluster(
unsigned cluster)
106 assert(cluster < maxCluster);
109 assert(cluster >= FIRST_CLUSTER);
110 }
while ((cluster < maxCluster) && (readFAT(cluster) != FREE_FAT));
113unsigned DirAsDSK::findFirstFreeCluster()
115 return findNextFreeCluster(FIRST_CLUSTER - 1);
119unsigned DirAsDSK::getFreeCluster()
121 unsigned cluster = findFirstFreeCluster();
122 if (cluster == maxCluster) {
123 throw MSXException(
"disk full");
128unsigned DirAsDSK::clusterToSector(
unsigned cluster)
const
130 assert(cluster >= FIRST_CLUSTER);
131 assert(cluster < maxCluster);
132 return firstDataSector + SECTORS_PER_CLUSTER *
133 (cluster - FIRST_CLUSTER);
136std::pair<unsigned, unsigned> DirAsDSK::sectorToClusterOffset(
unsigned sector)
const
138 assert(sector >= firstDataSector);
139 assert(sector < nofSectors);
140 sector -= firstDataSector;
141 unsigned cluster = (sector / SECTORS_PER_CLUSTER) + FIRST_CLUSTER;
142 unsigned offset = (sector % SECTORS_PER_CLUSTER) * SECTOR_SIZE;
143 return {cluster, offset};
145unsigned DirAsDSK::sectorToCluster(
unsigned sector)
const
147 auto [cluster, offset] = sectorToClusterOffset(sector);
151MSXDirEntry& DirAsDSK::msxDir(DirIndex dirIndex)
153 assert(dirIndex.sector < nofSectors);
154 assert(dirIndex.idx < DIR_ENTRIES_PER_SECTOR);
155 return sectors[dirIndex.sector].dirEntry[dirIndex.idx];
159unsigned DirAsDSK::nextMsxDirSector(
unsigned sector)
161 if (sector < firstDataSector) {
163 assert(firstDirSector <= sector);
165 if (sector == firstDataSector) {
172 auto [cluster, offset] = sectorToClusterOffset(sector);
173 if (offset < ((SECTORS_PER_CLUSTER - 1) * SECTOR_SIZE)) {
177 unsigned nextCl = readFAT(cluster);
178 if ((nextCl < FIRST_CLUSTER) || (maxCluster <= nextCl)) {
182 return clusterToSector(nextCl);
187bool DirAsDSK::checkMSXFileExists(
188 std::span<const char, 11> msxFilename,
unsigned msxDirSector)
190 vector<bool> visited(nofSectors,
false);
192 if (visited[msxDirSector]) {
196 visited[msxDirSector] =
true;
198 for (
auto idx :
xrange(DIR_ENTRIES_PER_SECTOR)) {
199 DirIndex dirIndex(msxDirSector, idx);
204 msxDirSector = nextMsxDirSector(msxDirSector);
205 }
while (msxDirSector !=
unsigned(-1));
213DirAsDSK::DirIndex DirAsDSK::findHostFileInDSK(std::string_view hostName)
const
215 for (
const auto& [dirIdx, mapDir] : mapDirs) {
216 if (mapDir.hostName == hostName) {
220 return {unsigned(-1), unsigned(-1)};
224bool DirAsDSK::checkFileUsedInDSK(std::string_view hostName)
const
226 DirIndex dirIndex = findHostFileInDSK(hostName);
227 return dirIndex.sector != unsigned(-1);
230static std::array<char, 11> hostToMsxName(
string hostName)
234 return (a ==
' ') ?
'_' : ::toupper(a);
237 if (file.empty()) std::swap(file, ext);
239 std::array<char, 8 + 3> result;
247static string msxToHostName(std::span<const char, 11> msxName)
250 for (
unsigned i = 0; (i < 8) && (msxName[i] !=
' '); ++i) {
251 result += char(tolower(msxName[i]));
253 if (msxName[8] !=
' ') {
255 for (
unsigned i = 8; (i < (8 + 3)) && (msxName[i] !=
' '); ++i) {
256 result += char(tolower(msxName[i]));
267 , diskChanger(diskChanger_)
269 , hostDir(FileOperations::expandTilde(hostDir_.getResolved() +
'/'))
270 , syncMode(syncMode_)
271 , nofSectors((diskChanger_.isDoubleSidedDrive() ? 2 : 1) * SECTORS_PER_TRACK * NUM_TRACKS)
272 , nofSectorsPerFat(
narrow<unsigned>((((3 * nofSectors) / (2 * SECTORS_PER_CLUSTER)) + SECTOR_SIZE - 1) / SECTOR_SIZE))
273 , firstSector2ndFAT(FIRST_FAT_SECTOR + nofSectorsPerFat)
274 , firstDirSector(FIRST_FAT_SECTOR + NUM_FATS * nofSectorsPerFat)
275 , firstDataSector(firstDirSector + SECTORS_PER_DIR)
276 , maxCluster((nofSectors - firstDataSector) / SECTORS_PER_CLUSTER + FIRST_CLUSTER)
277 , sectors(nofSectors)
294 uint8_t mediaDescriptor = (numSides == 2) ? 0xF9 : 0xF8;
298 sectors[0] = protoBootSector;
299 auto& bootSector = sectors[0].bootSector;
300 bootSector.bpSector = SECTOR_SIZE;
301 bootSector.spCluster = SECTORS_PER_CLUSTER;
302 bootSector.nrFats = NUM_FATS;
303 bootSector.dirEntries = SECTORS_PER_DIR * (SECTOR_SIZE /
sizeof(
MSXDirEntry));
304 bootSector.nrSectors = narrow_cast<uint16_t>(nofSectors);
305 bootSector.descriptor = mediaDescriptor;
306 bootSector.sectorsFat = narrow_cast<uint16_t>(nofSectorsPerFat);
307 bootSector.sectorsTrack = SECTORS_PER_TRACK;
308 bootSector.nrSides = numSides;
311 ranges::fill(std::span{fat()[0].raw.data(), SECTOR_SIZE * nofSectorsPerFat * NUM_FATS}, 0);
316 auto init = [&](
auto f) {
317 f[0].raw[0] = mediaDescriptor;
325 ranges::fill(std::span{sectors[firstDirSector].raw.data(), SECTOR_SIZE * SECTORS_PER_DIR}, 0);
328 assert(mapDirs.
empty());
351 bool needSync = [&] {
352 if (
const auto* scheduler = diskChanger.
getScheduler()) {
353 auto now = scheduler->getCurrentTime();
354 auto delta = now - lastAccess;
370 assert(sector < nofSectors);
377 bool needSync = [&] {
378 if (
const auto* scheduler = diskChanger.
getScheduler()) {
379 auto now = scheduler->getCurrentTime();
380 auto delta = now - lastAccess;
399 buf = sectors[sector];
402void DirAsDSK::syncWithHost()
407 checkDeletedHostFiles();
413 checkModifiedHostFiles();
416 addNewHostFiles({}, firstDirSector);
419void DirAsDSK::checkDeletedHostFiles()
423 for (
const auto& [dirIdx, mapDir] :
copy) {
435 auto fullHostName =
tmpStrCat(hostDir, mapDir.hostName);
436 auto isMSXDirectory = bool(msxDir(dirIdx).attrib &
446 deleteMSXFile(dirIdx);
451void DirAsDSK::deleteMSXFile(DirIndex dirIndex)
454 mapDirs.
erase(dirIndex);
456 if (msxDir(dirIndex).filename[0] ==
one_of(0,
char(0xE5))) {
464 if (
const auto& msxName = msxDir(dirIndex).filename;
472 if ((FIRST_CLUSTER <= cluster) && (cluster < maxCluster)) {
474 deleteMSXFilesInDir(clusterToSector(cluster));
480 msxDir(dirIndex).
filename[0] = char(0xE5);
483 freeFATChain(msxDir(dirIndex).startCluster);
486void DirAsDSK::deleteMSXFilesInDir(
unsigned msxDirSector)
488 vector<bool> visited(nofSectors,
false);
490 if (visited[msxDirSector]) {
494 visited[msxDirSector] =
true;
496 for (
auto idx :
xrange(DIR_ENTRIES_PER_SECTOR)) {
497 deleteMSXFile(DirIndex(msxDirSector, idx));
499 msxDirSector = nextMsxDirSector(msxDirSector);
500 }
while (msxDirSector !=
unsigned(-1));
503void DirAsDSK::freeFATChain(
unsigned cluster)
506 while ((FIRST_CLUSTER <= cluster) && (cluster < maxCluster)) {
507 unsigned nextCl = readFAT(cluster);
508 writeFAT12(cluster, FREE_FAT);
513void DirAsDSK::checkModifiedHostFiles()
516 for (
const auto& [dirIdx, mapDir] :
copy) {
521 auto fullHostName =
tmpStrCat(hostDir, mapDir.hostName);
522 auto isMSXDirectory = bool(msxDir(dirIdx).attrib &
535 if (!isMSXDirectory &&
536 ((mapDir.mtime != fst->st_mtime) ||
537 (mapDir.filesize !=
size_t(fst->st_size)))) {
538 importHostFile(dirIdx, *fst);
543 deleteMSXFile(dirIdx);
554 setMSXTimeStamp(dirIndex, fst);
569 size_t hostSize = fst.st_size;
570 auto& mapDir = mapDirs[dirIndex];
571 mapDir.filesize = hostSize;
572 mapDir.mtime = fst.st_mtime;
574 bool moreClustersInChain =
true;
579 if ((curCl < FIRST_CLUSTER) || (curCl >= maxCluster)) {
580 moreClustersInChain =
false;
581 curCl = findFirstFreeCluster();
584 auto remainingSize = hostSize;
587 File file(hostDir + mapDir.hostName,
590 while (remainingSize && (curCl < maxCluster)) {
591 unsigned logicalSector = clusterToSector(curCl);
592 for (
auto i :
xrange(SECTORS_PER_CLUSTER)) {
593 unsigned sector = logicalSector + i;
594 assert(sector < nofSectors);
595 auto* buf = §ors[sector];
596 auto sz = std::min(remainingSize, SECTOR_SIZE);
597 file.read(
subspan(buf->raw, 0, sz));
600 if (remainingSize == 0) {
608 writeFAT12(prevCl, curCl);
610 msxDir(dirIndex).
startCluster = narrow<uint16_t>(curCl);
616 if (moreClustersInChain) {
617 curCl = readFAT(curCl);
618 if ((curCl == EOF_FAT) ||
619 (curCl < FIRST_CLUSTER) ||
620 (curCl >= maxCluster)) {
623 moreClustersInChain =
false;
624 curCl = findFirstFreeCluster();
627 curCl = findNextFreeCluster(curCl);
630 if (remainingSize != 0) {
632 mapDir.hostName,
" truncated.");
634 }
catch (FileException& e) {
637 mapDir.hostName,
": ",
e.getMessage(),
638 " Truncated file on MSX disk.");
644 writeFAT12(prevCl, EOF_FAT);
652 if (moreClustersInChain) {
657 msxDir(dirIndex).
size = uint32_t(hostSize - remainingSize);
669 time_t mtime = fst.st_mtime;
670 const auto* mtim = localtime(&mtime);
671 int t1 = mtim ? (mtim->tm_sec >> 1) + (mtim->tm_min << 5) +
672 (mtim->tm_hour << 11)
674 msxDir(dirIndex).
time = narrow<uint16_t>(t1);
675 int t2 = mtim ? mtim->tm_mday + ((mtim->tm_mon + 1) << 5) +
676 ((mtim->tm_year + 1900 - 1980) << 9)
678 msxDir(dirIndex).
date = narrow<uint16_t>(t2);
689static size_t weight(
const string& hostName)
697 result += ext.size() * 10;
699 result += file.size();
703void DirAsDSK::addNewHostFiles(
const string& hostSubDir,
unsigned msxDirSector)
705 assert(!hostSubDir.starts_with(
'/'));
706 assert(hostSubDir.empty() || hostSubDir.ends_with(
'/'));
708 vector<string> hostNames;
710 ReadDir dir(
tmpStrCat(hostDir, hostSubDir));
711 while (
auto* d = dir.getEntry()) {
712 hostNames.emplace_back(d->d_name);
715 ranges::sort(hostNames, {}, [](
const string& n) {
return weight(n); });
717 for (
auto& hostName : hostNames) {
719 if (hostName.starts_with(
'.')) {
724 auto fullHostName =
tmpStrCat(hostDir, hostSubDir, hostName);
727 throw MSXException(
"Error accessing ", fullHostName);
730 addNewDirectory(hostSubDir, hostName, msxDirSector, *fst);
732 addNewHostFile(hostSubDir, hostName, msxDirSector, *fst);
734 throw MSXException(
"Not a regular file: ", fullHostName);
736 }
catch (MSXException& e) {
742void DirAsDSK::addNewDirectory(
const string& hostSubDir,
const string& hostName,
745 DirIndex dirIndex = findHostFileInDSK(
tmpStrCat(hostSubDir, hostName));
746 unsigned newMsxDirSector;
747 if (dirIndex.sector ==
unsigned(-1)) {
750 unsigned cluster = getFreeCluster();
751 writeFAT12(cluster, EOF_FAT);
755 dirIndex = fillMSXDirEntry(hostSubDir, hostName, msxDirSector);
758 writeFAT12(cluster, FREE_FAT);
761 setMSXTimeStamp(dirIndex, fst);
763 msxDir(dirIndex).
startCluster = narrow<uint16_t>(cluster);
766 newMsxDirSector = clusterToSector(cluster);
767 ranges::fill(std::span{sectors[newMsxDirSector].raw.data(),
768 SECTORS_PER_CLUSTER * SECTOR_SIZE},
770 DirIndex idx0(newMsxDirSector, 0);
771 DirIndex idx1(newMsxDirSector, 1);
772 auto& e0 = msxDir(idx0);
773 auto& e1 = msxDir(idx1);
774 auto& f0 = e0.filename;
775 auto& f1 = e1.filename;
780 setMSXTimeStamp(idx0, fst);
781 setMSXTimeStamp(idx1, fst);
782 e0.startCluster = narrow<uint16_t>(cluster);
783 e1.startCluster = msxDirSector == firstDirSector
785 :
narrow<uint16_t>(sectorToCluster(msxDirSector));
796 if ((cluster < FIRST_CLUSTER) || (cluster >= maxCluster)) {
800 newMsxDirSector = clusterToSector(cluster);
804 addNewHostFiles(
strCat(hostSubDir, hostName,
'/'), newMsxDirSector);
807void DirAsDSK::addNewHostFile(
const string& hostSubDir,
const string& hostName,
810 if (checkFileUsedInDSK(
tmpStrCat(hostSubDir, hostName))) {
815 if (
auto diskSpace = (nofSectors - firstDataSector) * SECTOR_SIZE;
816 narrow<size_t>(fst.st_size) > diskSpace) {
818 hostDir, hostSubDir, hostName);
822 DirIndex dirIndex = fillMSXDirEntry(hostSubDir, hostName, msxDirSector);
823 importHostFile(dirIndex, fst);
826DirAsDSK::DirIndex DirAsDSK::fillMSXDirEntry(
827 const string& hostSubDir,
const string& hostName,
unsigned msxDirSector)
829 string hostPath = hostSubDir + hostName;
832 DirIndex dirIndex = getFreeDirEntry(msxDirSector);
835 auto msxFilename = hostToMsxName(hostName);
836 if (checkMSXFileExists(msxFilename, msxDirSector)) {
839 "MSX name ", msxToHostName(msxFilename),
844 assert(!hostPath.ends_with(
'/'));
845 mapDirs[dirIndex].hostName = hostPath;
846 memset(&msxDir(dirIndex), 0,
sizeof(MSXDirEntry));
849 }
catch (MSXException& e) {
850 throw MSXException(
"Couldn't add ", hostPath,
": ",
855DirAsDSK::DirIndex DirAsDSK::getFreeDirEntry(
unsigned msxDirSector)
857 vector<bool> visited(nofSectors,
false);
859 if (visited[msxDirSector]) {
861 throw MSXException(
"cycle in FAT");
863 visited[msxDirSector] =
true;
865 for (
auto idx :
xrange(DIR_ENTRIES_PER_SECTOR)) {
866 DirIndex dirIndex(msxDirSector, idx);
867 const auto& msxName = msxDir(dirIndex).
filename;
868 if (msxName[0] ==
one_of(
char(0x00),
char(0xE5))) {
871 assert(!mapDirs.
contains(dirIndex));
875 unsigned sector = nextMsxDirSector(msxDirSector);
876 if (sector ==
unsigned(-1))
break;
877 msxDirSector = sector;
881 if (msxDirSector == (firstDataSector - 1)) {
883 throw MSXException(
"root directory full");
888 unsigned cluster = sectorToCluster(msxDirSector);
889 unsigned newCluster = getFreeCluster();
890 unsigned sector = clusterToSector(newCluster);
891 ranges::fill(std::span{sectors[sector].raw.data(), SECTORS_PER_CLUSTER * SECTOR_SIZE}, 0);
892 writeFAT12(cluster, newCluster);
893 writeFAT12(newCluster, EOF_FAT);
901 assert(sector_ < nofSectors);
903 auto sector = unsigned(sector_);
906 if (
const auto* scheduler = diskChanger.
getScheduler()) {
907 lastAccess = scheduler->getCurrentTime();
916 }
else if (sector < firstSector2ndFAT) {
917 writeFATSector(sector, buf);
918 }
else if (sector < firstDirSector) {
921 sectors[sector] = buf;
922 }
else if (
auto dirDirIndex = isDirSector(sector)) {
924 writeDIRSector(sector, *dirDirIndex, buf);
926 writeDataSector(sector, buf);
930void DirAsDSK::writeFATSector(
unsigned sector,
const SectorBuffer& buf)
936 sectors[sector] = buf;
939 for (
auto i :
xrange(FIRST_CLUSTER, maxCluster)) {
940 if (readFAT(i) != readFATHelper(oldFAT, i)) {
941 exportFileFromFATChange(i, oldFAT);
951 for (
auto i :
xrange(FIRST_CLUSTER, maxCluster)) {
952 assert(readFAT(i) == readFATHelper(oldFAT, i)); (void)i;
956void DirAsDSK::exportFileFromFATChange(
unsigned cluster, std::span<SectorBuffer> oldFAT)
959 auto [startCluster, chainLength] = getChainStart(cluster);
963 vector<bool> visited(maxCluster,
false);
964 unsigned tmp = startCluster;
965 while ((FIRST_CLUSTER <= tmp) && (tmp < maxCluster)) {
972 unsigned next = readFAT(tmp);
973 writeFATHelper(oldFAT, tmp, next);
979 if (
auto result = getDirEntryForCluster(startCluster)) {
980 exportToHost(result->dirIndex, result->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++];
1096 return sector == dirSector;
1101std::optional<DirAsDSK::DirIndex> DirAsDSK::isDirSector(
unsigned sector)
1104 scanMsxDirs(scanner, firstDirSector)) {
1105 return scanner.dirDirIndex;
1107 return std::nullopt;
1125std::optional<DirAsDSK::DirEntryForClusterResult> DirAsDSK::getDirEntryForCluster(
unsigned cluster)
1128 scanMsxDirs(scanner, firstDirSector)) {
1129 return DirEntryForClusterResult{scanner.dirIndex, scanner.dirDirIndex};
1131 return std::nullopt;
1146void DirAsDSK::unmapHostFiles(
unsigned msxDirSector)
1151void DirAsDSK::exportToHost(DirIndex dirIndex, DirIndex dirDirIndex)
1158 const auto& msxName = msxDir(dirIndex).
filename;
1160 if (
const auto* v =
lookup(mapDirs, dirIndex)) {
1162 hostName = v->hostName;
1166 if (msxName[0] ==
one_of(
char(0x00),
char(0xE5))) {
1171 if (dirDirIndex.sector != 0) {
1173 const auto* v2 =
lookup(mapDirs, dirDirIndex);
1175 hostSubDir = v2->hostName;
1176 assert(!hostSubDir.ends_with(
'/'));
1179 hostName = hostSubDir + msxToHostName(msxName);
1180 mapDirs[dirIndex].hostName = hostName;
1188 exportToHostDir(dirIndex, hostName);
1190 exportToHostFile(dirIndex, hostName);
1194void DirAsDSK::exportToHostDir(DirIndex dirIndex,
const string& hostName)
1198 if ((cluster < FIRST_CLUSTER) || (cluster >= maxCluster)) {
1202 unsigned msxDirSector = clusterToSector(cluster);
1208 vector<bool> visited(nofSectors,
false);
1210 if (visited[msxDirSector]) {
1214 visited[msxDirSector] =
true;
1216 if (readFAT(sectorToCluster(msxDirSector)) == FREE_FAT) {
1222 for (
auto idx :
xrange(DIR_ENTRIES_PER_SECTOR)) {
1223 exportToHost(DirIndex(msxDirSector, idx), dirIndex);
1225 msxDirSector = nextMsxDirSector(msxDirSector);
1226 }
while (msxDirSector !=
unsigned(-1));
1227 }
catch (FileException& e) {
1228 cliComm.
printWarning(
"Error while syncing host directory: ",
1229 hostName,
": ",
e.getMessage());
1233void DirAsDSK::exportToHostFile(DirIndex dirIndex,
const string& hostName)
1241 unsigned msxSize = msxDir(dirIndex).
size;
1244 unsigned offset = 0;
1245 vector<bool> visited(maxCluster,
false);
1247 while ((FIRST_CLUSTER <= curCl) && (curCl < maxCluster)) {
1248 if (visited[curCl]) {
1252 visited[curCl] =
true;
1254 unsigned logicalSector = clusterToSector(curCl);
1255 for (
auto i :
xrange(SECTORS_PER_CLUSTER)) {
1256 if (offset >= msxSize)
break;
1257 unsigned sector = logicalSector + i;
1258 assert(sector < nofSectors);
1259 auto writeSize = std::min<size_t>(msxSize - offset, SECTOR_SIZE);
1260 file.write(
subspan(sectors[sector].raw, 0, writeSize));
1261 offset += SECTOR_SIZE;
1263 if (offset >= msxSize)
break;
1264 curCl = readFAT(curCl);
1266 }
catch (FileException& e) {
1267 cliComm.
printWarning(
"Error while syncing host file: ",
1268 hostName,
": ",
e.getMessage());
1272void DirAsDSK::writeDIRSector(
unsigned sector, DirIndex dirDirIndex,
1273 const SectorBuffer& buf)
1276 for (
auto idx :
xrange(DIR_ENTRIES_PER_SECTOR)) {
1277 const auto& newEntry = buf.dirEntry[idx];
1278 DirIndex dirIndex(sector, idx);
1279 if (msxDir(dirIndex) != newEntry) {
1280 writeDIREntry(dirIndex, dirDirIndex, newEntry);
1287void DirAsDSK::writeDIREntry(DirIndex dirIndex, DirIndex dirDirIndex,
1288 const MSXDirEntry& newEntry)
1290 if ((msxDir(dirIndex).filename != newEntry.
filename) ||
1294 if (
auto it = mapDirs.
find(dirIndex); it !=
end(mapDirs)) {
1298 auto fullHostName =
tmpStrCat(hostDir, it->second.hostName);
1306 if ((FIRST_CLUSTER <= cluster) &&
1307 (cluster < maxCluster)) {
1308 unmapHostFiles(clusterToSector(cluster));
1315 msxDir(dirIndex) = newEntry;
1318 exportToHost(dirIndex, dirDirIndex);
1321void DirAsDSK::writeDataSector(
unsigned sector,
const SectorBuffer& buf)
1323 assert(sector >= firstDataSector);
1324 assert(sector < nofSectors);
1327 sectors[sector] = buf;
1330 auto [cluster, offset] = sectorToClusterOffset(sector);
1331 auto [startCluster, chainLength] = getChainStart(cluster);
1332 offset += narrow<unsigned>((
sizeof(buf) * SECTORS_PER_CLUSTER) * chainLength);
1335 auto entry = getDirEntryForCluster(startCluster);
1337 const auto* v =
lookup(mapDirs, entry->dirIndex);
1341 string fullHostName = hostDir + v->hostName;
1343 File file(fullHostName,
"rb+");
1345 unsigned msxSize = msxDir(entry->dirIndex).
size;
1346 if (msxSize > offset) {
1347 auto writeSize = std::min<size_t>(msxSize - offset,
sizeof(buf));
1348 file.write(
subspan(buf.raw, 0, writeSize));
1350 }
catch (FileException& e) {
1351 cliComm.
printWarning(
"Couldn't write to file ", fullHostName,
1352 ": ",
e.getMessage());
bool contains(const K &k) const
iterator find(const K &key)
static const SectorBuffer dos2BootBlock
static const SectorBuffer dos1BootBlock
void printWarning(std::string_view message)
friend struct UnmapHostFiles
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.
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)
bool isRegularFile(const Stat &st)
int deleteRecursive(const std::string &path)
bool isDirectory(const Stat &st)
std::optional< Stat > getStat(zstring_view filename)
Call stat() and return the stat structure.
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 count(InputRange &&range, const T &value)
constexpr bool equal(InputRange1 &&range1, InputRange2 &&range2, Pred pred={}, Proj1 proj1={}, Proj2 proj2={})
constexpr auto copy(InputRange &&range, OutputIter out)
constexpr void replace(ForwardRange &&range, const T &old_value, const T &new_value)
constexpr void sort(RandomAccessRange &&range)
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)
DirAsDSK::DirIndex dirIndex
bool onDirEntry(DirAsDSK::DirIndex dirIndex_, const MSXDirEntry &entry)
DirEntryForCluster(unsigned cluster_)
void onVisitSubDir(DirAsDSK::DirIndex subdir)
DirAsDSK::DirIndex dirDirIndex
IsDirSector(unsigned sector_)
bool onDirSector(unsigned dirSector) const
std::array< char, 8+3 > filename
bool onDirSector(unsigned) const
void onVisitSubDir(DirAsDSK::DirIndex) const
bool onDirEntry(DirAsDSK::DirIndex, const MSXDirEntry &) const
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)