33using std::string_view;
45 static constexpr unsigned FREE = 0x000;
46 static constexpr unsigned FIRST_CLUSTER = 0x002;
50 static constexpr unsigned BAD = 0xFF7;
51 static constexpr unsigned END_OF_CHAIN = 0xFFF;
53 static constexpr unsigned MAX_CLUSTER_COUNT = 0xFF4;
64 static constexpr unsigned BAD = 0xFFF7;
65 static constexpr unsigned END_OF_CHAIN = 0xFFFF;
67 static constexpr unsigned MAX_CLUSTER_COUNT = 0xFFF4;
77static constexpr unsigned SECTOR_SIZE =
sizeof(SectorBuffer);
78static constexpr unsigned DIR_ENTRIES_PER_SECTOR = SECTOR_SIZE /
sizeof(MSXDirEntry);
80static constexpr uint8_t EBPB_SIGNATURE = 0x29;
85static constexpr uint8_t T_MSX_LFN = 0x0F;
90unsigned MSXtar::clusterToSector(Cluster cluster)
const
92 return dataStart + sectorsPerCluster * cluster.index;
98Cluster MSXtar::sectorToCluster(
unsigned sector)
const
102 assert(sector >= dataStart);
103 return Cluster{(sector - dataStart) / sectorsPerCluster};
109void MSXtar::parseBootSector(
const MSXBootSector& boot)
111 fatCount = boot.nrFats;
112 sectorsPerFat = boot.sectorsFat;
113 sectorsPerCluster = boot.spCluster;
115 unsigned nrSectors = boot.nrSectors;
116 if (nrSectors == 0 && boot.params.extended.extendedBootSignature == EBPB_SIGNATURE) {
117 nrSectors = boot.params.extended.nrSectors;
120 if (boot.bpSector != SECTOR_SIZE) {
121 throw MSXException(
"Illegal sector size: ", boot.bpSector);
123 if (boot.resvSectors == 0) {
124 throw MSXException(
"Illegal number of reserved sectors: ", boot.resvSectors);
127 throw MSXException(
"Illegal number of FATs: ", fatCount);
129 if (sectorsPerFat == 0 || sectorsPerFat > 0x100) {
130 throw MSXException(
"Illegal number of sectors per FAT: ", sectorsPerFat);
132 if (boot.dirEntries == 0 || boot.dirEntries % DIR_ENTRIES_PER_SECTOR != 0) {
133 throw MSXException(
"Illegal number of root directory entries: ", boot.dirEntries);
135 if (!std::has_single_bit(sectorsPerCluster)) {
136 throw MSXException(
"Illegal number of sectors per cluster: ", sectorsPerCluster);
139 unsigned nbRootDirSectors = boot.dirEntries / DIR_ENTRIES_PER_SECTOR;
140 fatStart = boot.resvSectors;
141 rootDirStart = fatStart + fatCount * sectorsPerFat;
142 chrootSector = rootDirStart;
143 dataStart = rootDirStart + nbRootDirSectors;
150 constexpr unsigned fat16Threshold = (0x1000 * 3 / 2 + 0x1000 * 2) / 2 / SECTOR_SIZE;
151 fat16 = sectorsPerFat >= fat16Threshold;
153 if (dataStart + sectorsPerCluster > nrSectors) {
154 throw MSXException(
"Illegal number of sectors: ", nrSectors);
157 clusterCount = std::min((nrSectors - dataStart) / sectorsPerCluster,
158 fat16 ? FAT16::MAX_CLUSTER_COUNT : FAT12::MAX_CLUSTER_COUNT);
163 unsigned fatCapacity = (2 * SECTOR_SIZE * sectorsPerFat) / 3 - FAT::FIRST_CLUSTER;
164 clusterCount = std::min(clusterCount, fatCapacity);
167void MSXtar::writeLogicalSector(
unsigned sector,
const SectorBuffer& buf)
169 assert(!fatBuffer.empty());
170 unsigned fatSector = sector - fatStart;
171 if (fatSector < sectorsPerFat) {
174 fatBuffer[fatSector] = buf;
175 fatCacheDirty =
true;
181void MSXtar::readLogicalSector(
unsigned sector, SectorBuffer& buf)
183 assert(!fatBuffer.empty());
184 unsigned fatSector = sector - fatStart;
185 if (fatSector < sectorsPerFat) {
188 buf = fatBuffer[fatSector];
196 , msxChars(msxChars_)
197 , findFirstFreeClusterStart{0}
211 fatCacheDirty =
false;
212 fatBuffer.resize(sectorsPerFat);
213 disk.
readSectors(std::span{fatBuffer.data(), sectorsPerFat}, fatStart);
219 , fatBuffer(std::move(other.fatBuffer))
220 , msxChars(other.msxChars)
221 , findFirstFreeClusterStart(other.findFirstFreeClusterStart)
222 , clusterCount(other.clusterCount)
223 , fatCount(other.fatCount)
224 , sectorsPerCluster(other.sectorsPerCluster)
225 , sectorsPerFat(other.sectorsPerFat)
226 , fatStart(other.fatStart)
227 , rootDirStart(other.rootDirStart)
228 , dataStart(other.dataStart)
229 , chrootSector(other.chrootSector)
230 , fatCacheDirty(other.fatCacheDirty)
232 other.fatCacheDirty =
false;
237 if (!fatCacheDirty)
return;
239 for (
auto fat :
xrange(fatCount)) {
240 for (
auto i :
xrange(sectorsPerFat)) {
242 disk.
writeSector(i + fatStart + fat * sectorsPerFat, fatBuffer[i]);
251FatCluster MSXtar::readFAT(
Cluster cluster)
const
253 assert(!fatBuffer.empty());
254 assert(cluster.
index < clusterCount);
256 std::span<const uint8_t> data{fatBuffer[0].raw.data(), sectorsPerFat * size_t(SECTOR_SIZE)};
258 unsigned index = FAT::FIRST_CLUSTER + cluster.
index;
259 unsigned value = [&] {
261 auto p = subspan<2>(data, index * 2);
262 return p[0] | p[1] << 8;
264 auto p = subspan<2>(data, (index * 3) / 2);
266 ? (p[0] >> 4) + (p[1] << 4)
267 : p[0] + ((p[1] & 0x0F) << 8);
274 if (value == FAT::FREE) {
276 }
else if (value >= FAT::FIRST_CLUSTER && value < FAT::FIRST_CLUSTER + clusterCount) {
277 return Cluster{value - FAT::FIRST_CLUSTER};
284void MSXtar::writeFAT(Cluster cluster, FatCluster value)
286 assert(!fatBuffer.empty());
287 assert(cluster.index < clusterCount);
291 assert(!std::holds_alternative<Cluster>(value) || std::get<Cluster>(value).index < clusterCount);
293 std::span data{fatBuffer[0].raw.data(), sectorsPerFat * size_t(SECTOR_SIZE)};
295 unsigned index = FAT::FIRST_CLUSTER + cluster.index;
297 if (std::holds_alternative<Free>(value) && cluster < findFirstFreeClusterStart) {
299 findFirstFreeClusterStart = cluster;
304 auto p = subspan<2>(data, index * 2);
305 p[0] = narrow_cast<uint8_t>(fatValue);
306 p[1] = narrow_cast<uint8_t>(fatValue >> 8);
309 auto p = subspan<2>(data, (index * 3) / 2);
311 p[0] = narrow_cast<uint8_t>((p[0] & 0x0F) + (fatValue << 4));
312 p[1] = narrow_cast<uint8_t>(fatValue >> 4);
314 p[0] = narrow_cast<uint8_t>(fatValue);
315 p[1] = narrow_cast<uint8_t>((p[1] & 0xF0) + ((fatValue >> 8) & 0x0F));
318 fatCacheDirty =
true;
323Cluster MSXtar::findFirstFreeCluster()
325 for (
auto cluster :
xrange(findFirstFreeClusterStart.index, clusterCount)) {
326 if (readFAT({cluster}) ==
FatCluster(Free{})) {
327 findFirstFreeClusterStart = {cluster};
328 return findFirstFreeClusterStart;
331 throw MSXException(
"Disk full.");
334unsigned MSXtar::countFreeClusters()
const
337 [&](
unsigned cluster) { return readFAT({cluster}) ==
FatCluster(Free{}); }));
342unsigned MSXtar::getNextSector(
unsigned sector)
344 assert(sector >= rootDirStart);
345 if (sector < dataStart) {
347 return (sector == dataStart - 1) ? 0 : sector + 1;
349 Cluster currCluster = sectorToCluster(sector);
350 if (currCluster == sectorToCluster(sector + 1)) {
355 FatCluster nextCluster = readFAT(currCluster);
357 [](Free) {
return 0u; },
358 [](EndOfChain) {
return 0u; },
359 [
this](Cluster cluster) {
return clusterToSector(cluster); }
365DirCluster MSXtar::getStartCluster(
const MSXDirEntry& entry)
const
369 unsigned cluster = entry.startCluster;
370 if (cluster >= FAT::FIRST_CLUSTER && cluster < FAT::FIRST_CLUSTER + clusterCount) {
371 return Cluster{cluster - FAT::FIRST_CLUSTER};
378void MSXtar::setStartCluster(MSXDirEntry& entry, DirCluster cluster)
const
382 assert(!std::holds_alternative<Cluster>(cluster) || std::get<Cluster>(cluster).index < clusterCount);
384 entry.startCluster = narrow<uint16_t>(std::visit(FAT16::toClusterNumber, cluster));
386 entry.startCluster = narrow<uint16_t>(std::visit(FAT12::toClusterNumber, cluster));
395unsigned MSXtar::appendClusterToSubdir(
unsigned sector)
397 Cluster nextCl = findFirstFreeCluster();
398 unsigned nextSector = clusterToSector(nextCl);
403 for (
auto i :
xrange(sectorsPerCluster)) {
404 writeLogicalSector(i + nextSector, buf);
407 Cluster curCl = sectorToCluster(sector);
408 assert(readFAT(curCl) ==
FatCluster(EndOfChain{}));
409 writeFAT(curCl, nextCl);
410 writeFAT(nextCl, EndOfChain{});
418unsigned MSXtar::findUsableIndexInSector(
unsigned sector)
421 readLogicalSector(sector, buf);
424 for (
auto i :
xrange(DIR_ENTRIES_PER_SECTOR)) {
425 if (buf.dirEntry[i].filename[0] ==
one_of(0x00,
char(0xE5))) {
436MSXtar::DirEntry MSXtar::addEntryToDir(
unsigned sector)
441 result.sector = sector;
443 assert(sector >= rootDirStart);
444 if (sector < dataStart) {
446 for ( ; result.sector < dataStart; result.sector++) {
447 result.index = findUsableIndexInSector(result.sector);
448 if (result.index !=
unsigned(-1)) {
452 throw MSXException(
"Root directory full.");
457 result.index = findUsableIndexInSector(result.sector);
458 if (result.index !=
unsigned(-1)) {
461 unsigned nextSector = getNextSector(result.sector);
462 if (nextSector == 0) {
463 nextSector = appendClusterToSubdir(result.sector);
465 result.sector = nextSector;
471static char toFileNameChar(
char a)
473 if ((a >= 0x00 && a < 0x20) || a ==
one_of(
474 ' ',
'"',
'*',
'+',
',',
'.',
'/',
':',
';',
'<',
'=',
'>',
'?',
'[',
'\\',
']',
'|', 0x7F, 0xFF)) {
477 return narrow<char>(toupper(a));
482FileName MSXtar::hostToMSXFileName(string_view hostName)
const
484 std::vector<uint8_t> hostMSXName = msxChars.utf8ToMsx(hostName,
'_');
485 std::string_view hostMSXNameView(
reinterpret_cast<char*
>(hostMSXName.data()), hostMSXName.size());
491 if (hostFile ==
one_of(
".",
"..")) {
497 if (file.empty()) std::swap(file, ext);
503 string fileS(file.data(), std::min<size_t>(8, file.size()));
504 string extS (ext .data(), std::min<size_t>(3, ext .
size()));
519unsigned MSXtar::addSubdir(
520 const FileName& msxName, uint16_t
t, uint16_t d,
unsigned sector)
523 DirEntry result = addEntryToDir(sector);
527 readLogicalSector(result.sector, buf);
529 auto& dirEntry = buf.dirEntry[result.index];
531 dirEntry.attrib = MSXDirEntry::Attrib::DIRECTORY;
536 Cluster curCl = findFirstFreeCluster();
537 setStartCluster(dirEntry, curCl);
538 writeFAT(curCl, EndOfChain{});
541 writeLogicalSector(result.sector, buf);
544 unsigned logicalSector = clusterToSector(curCl);
546 for (
auto i :
xrange(sectorsPerCluster)) {
547 writeLogicalSector(i + logicalSector, buf);
551 memset(&buf.dirEntry[0], 0,
sizeof(MSXDirEntry));
553 buf.dirEntry[0].filename[0] =
'.';
554 buf.dirEntry[0].attrib = MSXDirEntry::Attrib::DIRECTORY;
555 buf.dirEntry[0].time =
t;
556 buf.dirEntry[0].date = d;
557 setStartCluster(buf.dirEntry[0], curCl);
559 memset(&buf.dirEntry[1], 0,
sizeof(MSXDirEntry));
561 buf.dirEntry[1].filename[0] =
'.';
562 buf.dirEntry[1].filename[1] =
'.';
563 buf.dirEntry[1].attrib = MSXDirEntry::Attrib::DIRECTORY;
564 buf.dirEntry[1].time =
t;
565 buf.dirEntry[1].date = d;
566 if (sector == rootDirStart) {
567 setStartCluster(buf.dirEntry[1], Free{});
569 setStartCluster(buf.dirEntry[1], sectorToCluster(sector));
573 writeLogicalSector(logicalSector, buf);
575 return logicalSector;
579static DiskImageUtils::FatTimeDate getTimeDate(
zstring_view filename)
581 if (
auto st = FileOperations::getStat(filename)) {
586 return DiskImageUtils::toTimeDate(
reinterpret_cast<time_t&
>(st->st_mtime));
595unsigned MSXtar::addSubdirToDSK(
zstring_view hostName,
const FileName& msxName,
598 auto [time, date] = getTimeDate(hostName);
599 return addSubdir(msxName, time, date, sector);
606void MSXtar::alterFileInDSK(MSXDirEntry& msxDirEntry,
const string& hostName)
609 auto st = FileOperations::getStat(hostName);
611 throw MSXException(
"Error reading host file: ", hostName);
613 unsigned hostSize = narrow<unsigned>(st->st_size);
614 unsigned remaining = hostSize;
617 File file(hostName,
"rb");
622 [](Free) -> FatCluster {
return EndOfChain{}; },
623 [](Cluster cluster) -> FatCluster {
return cluster; }
624 }, getStartCluster(msxDirEntry));
631 [](Free) -> Cluster {
throw new MSXException(
"Invalid entry in FAT chain."); },
633 Cluster newCl = findFirstFreeCluster();
635 [&](Free) { setStartCluster(msxDirEntry, newCl); },
636 [&](Cluster cluster_) { writeFAT(cluster_, newCl); }
638 writeFAT(newCl, EndOfChain{});
641 [](Cluster cluster_) {
return cluster_; }
643 }
catch (MSXException&) {
649 unsigned logicalSector = clusterToSector(cluster);
650 for (
unsigned j = 0; (j < sectorsPerCluster) && remaining; ++j) {
652 unsigned chunkSize = std::min(SECTOR_SIZE, remaining);
653 file.read(
subspan(buf.raw, 0, chunkSize));
655 writeLogicalSector(logicalSector + j, buf);
656 remaining -= chunkSize;
661 curCl = readFAT(cluster);
666 [&](Free free) { setStartCluster(msxDirEntry, free); },
667 [&](Cluster cluster) { writeFAT(cluster, EndOfChain{}); }
674 msxDirEntry.size = hostSize - remaining;
677 throw MSXException(
"Disk full, ", hostName,
" truncated.");
681void MSXtar::freeFatChain(FAT::FatCluster startCluster)
684 while (std::holds_alternative<Cluster>(curCl)) {
685 Cluster cluster = std::get<Cluster>(curCl);
687 writeFAT(cluster, Free{});
692std::string MSXtar::deleteEntry(
const FAT::FileName& msxName,
unsigned rootSector)
695 DirEntry entry = findEntryInDir(msxName, rootSector, buf);
696 if (entry.sector == 0) {
698 return "entry not found";
700 deleteEntry(buf.dirEntry[entry.index]);
701 writeLogicalSector(entry.sector, buf);
705void MSXtar::deleteEntry(MSXDirEntry& msxDirEntry)
707 DirCluster startCluster = getStartCluster(msxDirEntry);
709 if (msxDirEntry.attrib & MSXDirEntry::Attrib::DIRECTORY) {
712 const auto& msxName = msxDirEntry.filename;
720 [&](Cluster cluster) { deleteDirectory(clusterToSector(cluster)); }
726 msxDirEntry.filename[0] = char(0xE5);
731 [&](Cluster cluster) { freeFatChain(cluster); }
737void MSXtar::deleteDirectory(
unsigned sector)
739 for ( ; sector != 0; sector = getNextSector(sector)) {
741 readLogicalSector(sector, buf);
742 for (
auto& dirEntry : buf.dirEntry) {
743 if (dirEntry.filename[0] ==
char(0x00)) {
746 if (dirEntry.filename[0] ==
one_of(
char(0xe5),
'.') || dirEntry.attrib == T_MSX_LFN) {
749 deleteEntry(dirEntry);
751 writeLogicalSector(sector, buf);
755std::string MSXtar::renameItem(std::string_view currentName, std::string_view newName)
759 FileName newMsxName = hostToMSXFileName(newName);
760 auto newEntry = findEntryInDir(newMsxName, chrootSector, buf);
761 if (newEntry.sector != 0) {
762 return "another entry with new name already exists";
765 FileName oldMsxName = hostToMSXFileName(currentName);
766 auto oldEntry = findEntryInDir(oldMsxName, chrootSector, buf);
767 if (oldEntry.sector == 0) {
768 return "entry not found";
771 buf.
dirEntry[oldEntry.index].filename = newMsxName;
772 writeLogicalSector(oldEntry.sector, buf);
780MSXtar::DirEntry MSXtar::findEntryInDir(
781 const FileName& msxName,
unsigned sector,
SectorBuffer& buf)
784 result.sector = sector;
786 while (result.sector) {
788 readLogicalSector(result.sector, buf);
789 for (result.index = 0; result.index < DIR_ENTRIES_PER_SECTOR; ++result.index) {
795 result.sector = getNextSector(result.sector);
802string MSXtar::addFileToDSK(
const string& fullHostName,
unsigned rootSector, Add add)
805 FileName msxName = hostToMSXFileName(hostName);
809 DirEntry fullMsxDirEntry = findEntryInDir(msxName, rootSector, dummy);
810 if (fullMsxDirEntry.sector != 0) {
811 if (add == Add::PRESERVE) {
812 return strCat(
"Warning: preserving entry ", hostName,
'\n');
815 deleteEntry(dummy.dirEntry[fullMsxDirEntry.index]);
816 writeLogicalSector(fullMsxDirEntry.sector, dummy);
821 DirEntry entry = addEntryToDir(rootSector);
822 readLogicalSector(entry.sector, buf);
823 auto& dirEntry = buf.dirEntry[entry.index];
824 memset(&dirEntry, 0,
sizeof(dirEntry));
826 dirEntry.attrib = MSXDirEntry::Attrib::REGULAR;
829 auto [time, date] = getTimeDate(fullHostName);
830 dirEntry.time = time;
831 dirEntry.date = date;
834 alterFileInDSK(dirEntry, fullHostName);
835 }
catch (MSXException&) {
837 writeLogicalSector(entry.sector, buf);
840 writeLogicalSector(entry.sector, buf);
844std::string MSXtar::addOrCreateSubdir(
zstring_view hostDirName,
unsigned sector, Add add)
846 FileName msxFileName = hostToMSXFileName(hostDirName);
847 auto printableFilename = msxToHostFileName(msxFileName);
849 DirEntry entry = findEntryInDir(msxFileName, sector, buf);
850 if (entry.sector != 0) {
852 auto& msxDirEntry = buf.dirEntry[entry.index];
853 if (msxDirEntry.attrib & MSXDirEntry::Attrib::DIRECTORY) {
855 DirCluster nextCluster = getStartCluster(msxDirEntry);
857 [&](Free) {
return strCat(
"Directory ", printableFilename,
" goes to root.\n"); },
858 [&](Cluster cluster) {
return recurseDirFill(hostDirName, clusterToSector(cluster), add); }
863 if (add == Add::PRESERVE) {
864 return strCat(
"MSX file ", printableFilename,
" is not a directory.\n");
867 deleteEntry(msxDirEntry);
868 writeLogicalSector(entry.sector, buf);
872 unsigned nextSector = addSubdirToDSK(hostDirName, msxFileName, sector);
873 return recurseDirFill(hostDirName, nextSector, add);
878string MSXtar::recurseDirFill(string_view dirName,
unsigned sector, Add add)
882 auto fileAction = [&](
const string& path) {
884 messages += addFileToDSK(path, sector, add);
886 auto dirAction = [&](
const string& path) {
888 messages += addOrCreateSubdir(path, sector, add);
896string MSXtar::msxToHostFileName(
const FileName& msxName)
const
899 for (
unsigned i = 0; i < 8 && msxName[i] !=
' '; ++i) {
900 result += char(tolower(msxName[i]));
902 if (msxName[8] !=
' ') {
904 for (
unsigned i = 8; i < 11 && msxName[i] !=
' '; ++i) {
905 result += char(tolower(msxName[i]));
908 std::span<const uint8_t> resultSpan(
reinterpret_cast<const uint8_t*
>(result.data()), result.size());
909 return msxChars.msxToUtf8(resultSpan,
'_');
914static void changeTime(
zstring_view resultFile,
const MSXDirEntry& dirEntry)
916 unsigned t = dirEntry.time;
917 unsigned d = dirEntry.date;
920 mTim.tm_sec = narrow<int>((
t & 0x001f) << 1);
921 mTim.tm_min = narrow<int>((
t & 0x07e0) >> 5);
922 mTim.tm_hour = narrow<int>((
t & 0xf800) >> 11);
923 mTim.tm_mday = narrow<int>( (d & 0x001f));
924 mTim.tm_mon = narrow<int>(((d & 0x01e0) >> 5) - 1);
925 mTim.tm_year = narrow<int>(((d & 0xfe00) >> 9) + 80);
927 uTim.actime = mktime(&mTim);
928 uTim.modtime = mktime(&mTim);
929 utime(resultFile.
c_str(), &uTim);
935 for (
unsigned sector = chrootSector; sector != 0; sector = getNextSector(sector)) {
937 readLogicalSector(sector, buf);
938 for (
auto& dirEntry : buf.
dirEntry) {
939 if (dirEntry.filename[0] ==
char(0x00)) {
942 if (dirEntry.filename[0] ==
char(0xe5) || dirEntry.attrib == T_MSX_LFN) {
946 auto filename = msxToHostFileName(dirEntry.filename);
954std::string MSXtar::dir()
957 auto list = dirRaw();
958 auto num = list.size();
959 for (
unsigned i = 0; i < num; ++i) {
960 auto entry = list.getListIndexUnchecked(i);
961 auto filename = std::string(entry.getListIndexUnchecked(0).getString());
962 auto attrib = DiskImageUtils::formatAttrib(narrow<uint8_t>(entry.getListIndexUnchecked(1).getOptionalInt().value_or(0)));
964 auto size = entry.getListIndexUnchecked(3).getOptionalInt().value_or(0);
966 filename.resize(13,
' ');
967 strAppend(result, filename, attrib,
" ", size,
'\n');
973void MSXtar::chdir(string_view newRootDir)
975 chroot(newRootDir,
false);
978void MSXtar::mkdir(string_view newRootDir)
980 unsigned tmpMSXchrootSector = chrootSector;
981 chroot(newRootDir,
true);
982 chrootSector = tmpMSXchrootSector;
985void MSXtar::chroot(string_view newRootDir,
bool createDir)
987 if (newRootDir.starts_with(
'/') || newRootDir.starts_with(
'\\')) {
989 chrootSector = rootDirStart;
993 while (!newRootDir.empty()) {
995 newRootDir = lastPart;
1000 FileName msxName = hostToMSXFileName(firstPart);
1001 DirEntry entry = findEntryInDir(msxName, chrootSector, buf);
1002 if (entry.sector == 0) {
1004 throw MSXException(
"Subdirectory ", firstPart,
1010 auto [
t, d] = DiskImageUtils::toTimeDate(now);
1011 chrootSector = addSubdir(msxName,
t, d, chrootSector);
1013 auto& dirEntry = buf.dirEntry[entry.index];
1014 if (!(dirEntry.attrib & MSXDirEntry::Attrib::DIRECTORY)) {
1015 throw MSXException(firstPart,
" is not a directory.");
1017 DirCluster chrootCluster = getStartCluster(dirEntry);
1019 [
this](Free) {
return rootDirStart; },
1020 [
this](Cluster cluster) {
return clusterToSector(cluster); }
1026void MSXtar::fileExtract(
const string& resultFile,
const MSXDirEntry& dirEntry)
1028 unsigned size = dirEntry.size;
1030 [](Free) {
return 0u; },
1031 [
this](Cluster cluster) {
return clusterToSector(cluster); }
1032 }, getStartCluster(dirEntry));
1034 File file(resultFile,
"wb");
1035 while (size && sector) {
1037 readLogicalSector(sector, buf);
1038 unsigned saveSize = std::min(size, SECTOR_SIZE);
1039 file.write(
subspan(buf.raw, 0, saveSize));
1041 sector = getNextSector(sector);
1044 changeTime(resultFile, dirEntry);
1048string MSXtar::singleItemExtract(string_view dirName, string_view itemName,
1053 FileName msxName = hostToMSXFileName(itemName);
1054 DirEntry entry = findEntryInDir(msxName, sector, buf);
1055 if (entry.sector == 0) {
1056 return strCat(itemName,
" not found!\n");
1059 auto& msxDirEntry = buf.dirEntry[entry.index];
1061 string fullName =
strCat(dirName,
'/', msxToHostFileName(msxDirEntry.filename));
1064 if (msxDirEntry.attrib & MSXDirEntry::Attrib::DIRECTORY) {
1066 FileOperations::mkdirp(fullName);
1067 DirCluster nextCluster = getStartCluster(msxDirEntry);
1070 [&](Cluster cluster) { recurseDirExtract(fullName, clusterToSector(cluster)); }
1074 fileExtract(fullName, msxDirEntry);
1081void MSXtar::recurseDirExtract(string_view dirName,
unsigned sector)
1083 for ( ; sector != 0; sector = getNextSector(sector)) {
1085 readLogicalSector(sector, buf);
1086 for (
auto& dirEntry : buf.dirEntry) {
1087 if (dirEntry.filename[0] ==
char(0x00)) {
1090 if (dirEntry.filename[0] ==
one_of(
char(0xe5),
'.') || dirEntry.attrib == T_MSX_LFN) {
1093 string filename = msxToHostFileName(dirEntry.filename);
1094 string fullName = filename;
1095 if (!dirName.empty()) {
1096 fullName =
strCat(dirName,
'/', filename);
1098 if (dirEntry.attrib & MSXDirEntry::Attrib::DIRECTORY) {
1099 FileOperations::mkdirp(fullName);
1101 changeTime(fullName, dirEntry);
1104 [&](Cluster cluster) { recurseDirExtract(fullName, clusterToSector(cluster)); }
1105 }, getStartCluster(dirEntry));
1107 fileExtract(fullName, dirEntry);
1113std::string MSXtar::addItem(
const std::string& hostItemName,
Add add)
1115 if (
auto stat = FileOperations::getStat(hostItemName)) {
1116 if (FileOperations::isRegularFile(*stat)) {
1117 return addFileToDSK(hostItemName, chrootSector, add);
1118 }
else if (FileOperations::isDirectory(hostItemName)) {
1119 return addOrCreateSubdir(hostItemName, chrootSector, add);
1125string MSXtar::addDir(string_view rootDirName,
Add add)
1127 return recurseDirFill(rootDirName, chrootSector, add);
1130string MSXtar::addFile(
const string& filename,
Add add)
1132 return addFileToDSK(filename, chrootSector, add);
1135string MSXtar::getItemFromDir(string_view rootDirName, string_view itemName)
1137 return singleItemExtract(rootDirName, itemName, chrootSector);
1140void MSXtar::getDir(string_view rootDirName)
1142 recurseDirExtract(rootDirName, chrootSector);
1145std::string MSXtar::deleteItem(std::string_view itemName)
1147 FileName msxName = hostToMSXFileName(itemName);
1148 return deleteEntry(msxName, chrootSector);
1151std::string MSXtar::convertToMsxName(std::string_view name)
const
1153 FileName msxName = hostToMSXFileName(name);
1154 return msxToHostFileName(msxName);
1159 return {countFreeClusters(), sectorsPerCluster * SECTOR_SIZE};
MSXtar(SectorAccessibleDisk &disk, const MsxChar2Unicode &msxChars_)
void readSector(size_t sector, SectorBuffer &buf)
void readSectors(std::span< SectorBuffer > buffers, size_t startSector)
size_t getNbSectors() const
void writeSector(size_t sector, const SectorBuffer &buf)
void addListElement(const T &t)
Like std::string_view, but with the extra guarantee that it refers to a zero-terminated string.
constexpr auto size() const
constexpr const char * c_str() const
std::pair< string_view, string_view > splitOnLast(string_view str, string_view chars)
void trimRight(string &str, const char *chars)
std::pair< string_view, string_view > splitOnFirst(string_view str, string_view chars)
void trimLeft(string &str, const char *chars)
struct openmsx::FAT12::ToClusterNumber toClusterNumber
struct openmsx::FAT16::ToClusterNumber toClusterNumber
std::variant< Free, Cluster > DirCluster
std::variant< Free, EndOfChain, Cluster > FatCluster
decltype(MSXDirEntry::filename) FileName
This file implemented 3 utility functions:
TclObject makeTclList(Args &&... args)
bool foreach_file_and_directory(std::string path, FileAction fileAction, DirAction dirAction)
constexpr void fill(ForwardRange &&range, const T &value)
auto copy(InputRange &&range, OutputIter out)
bool equal(InputRange1 &&range1, InputRange2 &&range2, Pred pred={}, Proj1 proj1={}, Proj2 proj2={})
auto count_if(InputRange &&range, UnaryPredicate pred)
size_t size(std::string_view utf8)
constexpr auto subspan(Range &&range, size_t offset, size_t count=std::dynamic_extent)
auto transform_in_place(ForwardRange &&range, UnaryOperation op)
void strAppend(std::string &result, Ts &&...ts)
unsigned operator()(Free) const
unsigned operator()(Cluster cluster) const
unsigned operator()(EndOfChain) const
unsigned operator()(EndOfChain) const
unsigned operator()(Cluster cluster) const
unsigned operator()(Free) const
std::array< MSXDirEntry, 16 > dirEntry
constexpr auto xrange(T e)