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 MSXDirEntry::AttribValue 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_)
210 fatCacheDirty =
false;
211 fatBuffer.resize(sectorsPerFat);
212 disk.
readSectors(std::span{fatBuffer.data(), sectorsPerFat}, fatStart);
218 , fatBuffer(std::move(other.fatBuffer))
219 , msxChars(other.msxChars)
220 , findFirstFreeClusterStart(other.findFirstFreeClusterStart)
221 , clusterCount(other.clusterCount)
222 , fatCount(other.fatCount)
223 , sectorsPerCluster(other.sectorsPerCluster)
224 , sectorsPerFat(other.sectorsPerFat)
225 , fatStart(other.fatStart)
226 , rootDirStart(other.rootDirStart)
227 , dataStart(other.dataStart)
228 , chrootSector(other.chrootSector)
229 , fatCacheDirty(other.fatCacheDirty)
231 other.fatCacheDirty =
false;
236 if (!fatCacheDirty)
return;
238 for (
auto fat :
xrange(fatCount)) {
239 for (
auto i :
xrange(sectorsPerFat)) {
241 disk.
writeSector(i + fatStart + fat * sectorsPerFat, fatBuffer[i]);
250FatCluster MSXtar::readFAT(
Cluster cluster)
const
252 assert(!fatBuffer.empty());
253 assert(cluster.
index < clusterCount);
255 std::span<const uint8_t> data{fatBuffer[0].raw.data(), sectorsPerFat * size_t(SECTOR_SIZE)};
257 unsigned index = FAT::FIRST_CLUSTER + cluster.
index;
258 unsigned value = [&] {
260 auto p = subspan<2>(data, index * 2);
261 return p[0] | p[1] << 8;
263 auto p = subspan<2>(data, (index * 3) / 2);
265 ? (p[0] >> 4) + (p[1] << 4)
266 : p[0] + ((p[1] & 0x0F) << 8);
273 if (value == FAT::FREE) {
275 }
else if (value >= FAT::FIRST_CLUSTER && value < FAT::FIRST_CLUSTER + clusterCount) {
276 return Cluster{value - FAT::FIRST_CLUSTER};
283void MSXtar::writeFAT(Cluster cluster, FatCluster value)
285 assert(!fatBuffer.empty());
286 assert(cluster.index < clusterCount);
290 assert(!std::holds_alternative<Cluster>(value) || std::get<Cluster>(value).index < clusterCount);
292 std::span data{fatBuffer[0].raw.data(), sectorsPerFat * size_t(SECTOR_SIZE)};
294 unsigned index = FAT::FIRST_CLUSTER + cluster.index;
296 if (std::holds_alternative<Free>(value) && cluster < findFirstFreeClusterStart) {
298 findFirstFreeClusterStart = cluster;
302 unsigned fatValue = std::visit(FAT16::ToClusterNumber{}, value);
303 auto p = subspan<2>(data, index * 2);
304 p[0] = narrow_cast<uint8_t>(fatValue);
305 p[1] = narrow_cast<uint8_t>(fatValue >> 8);
307 unsigned fatValue = std::visit(FAT12::ToClusterNumber{}, value);
308 auto p = subspan<2>(data, (index * 3) / 2);
310 p[0] = narrow_cast<uint8_t>((p[0] & 0x0F) + (fatValue << 4));
311 p[1] = narrow_cast<uint8_t>(fatValue >> 4);
313 p[0] = narrow_cast<uint8_t>(fatValue);
314 p[1] = narrow_cast<uint8_t>((p[1] & 0xF0) + ((fatValue >> 8) & 0x0F));
317 fatCacheDirty =
true;
322Cluster MSXtar::findFirstFreeCluster()
324 for (
auto cluster :
xrange(findFirstFreeClusterStart.index, clusterCount)) {
325 if (readFAT({cluster}) ==
FatCluster(Free{})) {
326 findFirstFreeClusterStart = {cluster};
327 return findFirstFreeClusterStart;
330 throw MSXException(
"Disk full.");
333unsigned MSXtar::countFreeClusters()
const
336 [&](
unsigned cluster) { return readFAT({cluster}) ==
FatCluster(Free{}); }));
341unsigned MSXtar::getNextSector(
unsigned sector)
const
343 assert(sector >= rootDirStart);
344 if (sector < dataStart) {
346 return (sector == dataStart - 1) ? 0 : sector + 1;
348 Cluster currCluster = sectorToCluster(sector);
349 if (currCluster == sectorToCluster(sector + 1)) {
354 FatCluster nextCluster = readFAT(currCluster);
356 [](Free) {
return 0u; },
357 [](EndOfChain) {
return 0u; },
358 [
this](Cluster cluster) {
return clusterToSector(cluster); }
364DirCluster MSXtar::getStartCluster(
const MSXDirEntry& entry)
const
368 unsigned cluster = entry.startCluster;
369 if (cluster >= FAT::FIRST_CLUSTER && cluster < FAT::FIRST_CLUSTER + clusterCount) {
370 return Cluster{cluster - FAT::FIRST_CLUSTER};
377void MSXtar::setStartCluster(MSXDirEntry& entry, DirCluster cluster)
const
381 assert(!std::holds_alternative<Cluster>(cluster) || std::get<Cluster>(cluster).index < clusterCount);
383 entry.startCluster = narrow<uint16_t>(std::visit(FAT16::ToClusterNumber{}, cluster));
385 entry.startCluster = narrow<uint16_t>(std::visit(FAT12::ToClusterNumber{}, cluster));
394unsigned MSXtar::appendClusterToSubdir(
unsigned sector)
396 Cluster nextCl = findFirstFreeCluster();
397 unsigned nextSector = clusterToSector(nextCl);
402 for (
auto i :
xrange(sectorsPerCluster)) {
403 writeLogicalSector(i + nextSector, buf);
406 Cluster curCl = sectorToCluster(sector);
407 assert(readFAT(curCl) ==
FatCluster(EndOfChain{}));
408 writeFAT(curCl, nextCl);
409 writeFAT(nextCl, EndOfChain{});
417unsigned MSXtar::findUsableIndexInSector(
unsigned sector)
420 readLogicalSector(sector, buf);
423 for (
auto i :
xrange(DIR_ENTRIES_PER_SECTOR)) {
424 if (buf.dirEntry[i].filename[0] ==
one_of(0x00,
char(0xE5))) {
435MSXtar::DirEntry MSXtar::addEntryToDir(
unsigned sector)
440 result.sector = sector;
442 assert(sector >= rootDirStart);
443 if (sector < dataStart) {
445 for ( ; result.sector < dataStart; result.sector++) {
446 result.index = findUsableIndexInSector(result.sector);
447 if (result.index !=
unsigned(-1)) {
451 throw MSXException(
"Root directory full.");
456 result.index = findUsableIndexInSector(result.sector);
457 if (result.index !=
unsigned(-1)) {
460 unsigned nextSector = getNextSector(result.sector);
461 if (nextSector == 0) {
462 nextSector = appendClusterToSubdir(result.sector);
464 result.sector = nextSector;
470static char toFileNameChar(
char a)
472 if ((a >= 0x00 && a < 0x20) || a ==
one_of(
473 ' ',
'"',
'*',
'+',
',',
'.',
'/',
':',
';',
'<',
'=',
'>',
'?',
'[',
'\\',
']',
'|', 0x7F, 0xFF)) {
476 return narrow<char>(toupper(a));
481FileName MSXtar::hostToMSXFileName(string_view hostName)
const
483 std::vector<uint8_t> hostMSXName = msxChars.utf8ToMsx(hostName,
'_');
484 std::string_view hostMSXNameView(std::bit_cast<char*>(hostMSXName.data()), hostMSXName.size());
490 if (hostFile ==
one_of(
".",
"..")) {
496 if (file.empty()) std::swap(file, ext);
502 string fileS(file.data(), std::min<size_t>(8, file.size()));
503 string extS (ext .data(), std::min<size_t>(3, ext .
size()));
518unsigned MSXtar::addSubdir(
519 const FileName& msxName, uint16_t
t, uint16_t d,
unsigned sector)
522 DirEntry result = addEntryToDir(sector);
526 readLogicalSector(result.sector, buf);
528 auto& dirEntry = buf.dirEntry[result.index];
530 dirEntry.attrib = MSXDirEntry::Attrib::DIRECTORY;
535 Cluster curCl = findFirstFreeCluster();
536 setStartCluster(dirEntry, curCl);
537 writeFAT(curCl, EndOfChain{});
540 writeLogicalSector(result.sector, buf);
543 unsigned logicalSector = clusterToSector(curCl);
545 for (
auto i :
xrange(sectorsPerCluster)) {
546 writeLogicalSector(i + logicalSector, buf);
550 memset(&buf.dirEntry[0], 0,
sizeof(MSXDirEntry));
552 buf.dirEntry[0].filename[0] =
'.';
553 buf.dirEntry[0].attrib = MSXDirEntry::Attrib::DIRECTORY;
554 buf.dirEntry[0].time =
t;
555 buf.dirEntry[0].date = d;
556 setStartCluster(buf.dirEntry[0], curCl);
558 memset(&buf.dirEntry[1], 0,
sizeof(MSXDirEntry));
560 buf.dirEntry[1].filename[0] =
'.';
561 buf.dirEntry[1].filename[1] =
'.';
562 buf.dirEntry[1].attrib = MSXDirEntry::Attrib::DIRECTORY;
563 buf.dirEntry[1].time =
t;
564 buf.dirEntry[1].date = d;
565 if (sector == rootDirStart) {
566 setStartCluster(buf.dirEntry[1], Free{});
568 setStartCluster(buf.dirEntry[1], sectorToCluster(sector));
572 writeLogicalSector(logicalSector, buf);
574 return logicalSector;
578static DiskImageUtils::FatTimeDate getTimeDate(
zstring_view filename)
580 if (
auto st = FileOperations::getStat(filename)) {
585 return DiskImageUtils::toTimeDate(
reinterpret_cast<time_t&
>(st->st_mtime));
594unsigned MSXtar::addSubdirToDSK(
zstring_view hostName,
const FileName& msxName,
597 auto [time, date] = getTimeDate(hostName);
598 return addSubdir(msxName, time, date, sector);
605void MSXtar::alterFileInDSK(MSXDirEntry& msxDirEntry,
const string& hostName)
608 auto st = FileOperations::getStat(hostName);
610 throw MSXException(
"Error reading host file: ", hostName);
612 unsigned hostSize = narrow<unsigned>(st->st_size);
613 unsigned remaining = hostSize;
616 File file(hostName,
"rb");
621 [](Free) -> FatCluster {
return EndOfChain{}; },
622 [](Cluster cluster) -> FatCluster {
return cluster; }
623 }, getStartCluster(msxDirEntry));
630 [](Free) -> Cluster {
throw MSXException(
"Invalid entry in FAT chain."); },
632 Cluster newCl = findFirstFreeCluster();
634 [&](Free) { setStartCluster(msxDirEntry, newCl); },
635 [&](Cluster cluster_) { writeFAT(cluster_, newCl); }
637 writeFAT(newCl, EndOfChain{});
640 [](Cluster cluster_) {
return cluster_; }
642 }
catch (MSXException&) {
648 unsigned logicalSector = clusterToSector(cluster);
649 for (
unsigned j = 0; (j < sectorsPerCluster) && remaining; ++j) {
651 unsigned chunkSize = std::min(SECTOR_SIZE, remaining);
652 file.read(
subspan(buf.raw, 0, chunkSize));
654 writeLogicalSector(logicalSector + j, buf);
655 remaining -= chunkSize;
660 curCl = readFAT(cluster);
665 [&](Free free) { setStartCluster(msxDirEntry, free); },
666 [&](Cluster cluster) { writeFAT(cluster, EndOfChain{}); }
673 msxDirEntry.size = hostSize - remaining;
676 throw MSXException(
"Disk full, ", hostName,
" truncated.");
680void MSXtar::freeFatChain(FAT::FatCluster startCluster)
683 while (std::holds_alternative<Cluster>(curCl)) {
684 Cluster cluster = std::get<Cluster>(curCl);
686 writeFAT(cluster, Free{});
691std::string MSXtar::deleteEntry(
const FAT::FileName& msxName,
unsigned rootSector)
694 DirEntry entry = findEntryInDir(msxName, rootSector, buf);
695 if (entry.sector == 0) {
697 return "entry not found";
699 deleteEntry(buf.dirEntry[entry.index]);
700 writeLogicalSector(entry.sector, buf);
704void MSXtar::deleteEntry(MSXDirEntry& msxDirEntry)
706 DirCluster startCluster = getStartCluster(msxDirEntry);
708 if (msxDirEntry.attrib & MSXDirEntry::Attrib::DIRECTORY) {
711 if (
const auto& msxName = msxDirEntry.filename;
719 [&](Cluster cluster) { deleteDirectory(clusterToSector(cluster)); }
725 msxDirEntry.filename[0] = char(0xE5);
730 [&](Cluster cluster) { freeFatChain(cluster); }
736void MSXtar::deleteDirectory(
unsigned sector)
738 for ( ; sector != 0; sector = getNextSector(sector)) {
740 readLogicalSector(sector, buf);
741 for (
auto& dirEntry : buf.dirEntry) {
742 if (dirEntry.filename[0] ==
char(0x00)) {
745 if (dirEntry.filename[0] ==
one_of(
char(0xe5),
'.') || dirEntry.attrib == T_MSX_LFN) {
748 deleteEntry(dirEntry);
750 writeLogicalSector(sector, buf);
754std::string MSXtar::renameItem(std::string_view currentName, std::string_view newName)
758 FileName newMsxName = hostToMSXFileName(newName);
759 if (
auto newEntry = findEntryInDir(newMsxName, chrootSector, buf);
760 newEntry.sector != 0) {
761 return "another entry with new name already exists";
764 FileName oldMsxName = hostToMSXFileName(currentName);
765 auto oldEntry = findEntryInDir(oldMsxName, chrootSector, buf);
766 if (oldEntry.sector == 0) {
767 return "entry not found";
770 buf.
dirEntry[oldEntry.index].filename = newMsxName;
771 writeLogicalSector(oldEntry.sector, buf);
779MSXtar::DirEntry MSXtar::findEntryInDir(
780 const FileName& msxName,
unsigned sector,
SectorBuffer& buf)
783 result.sector = sector;
785 while (result.sector) {
787 readLogicalSector(result.sector, buf);
788 for (result.index = 0; result.index < DIR_ENTRIES_PER_SECTOR; ++result.index) {
794 result.sector = getNextSector(result.sector);
801string MSXtar::addFileToDSK(
const string& fullHostName,
unsigned rootSector, Add add)
804 FileName msxName = hostToMSXFileName(hostName);
808 if (DirEntry fullMsxDirEntry = findEntryInDir(msxName, rootSector, dummy);
809 fullMsxDirEntry.sector != 0) {
810 if (add == Add::PRESERVE) {
811 return strCat(
"Warning: preserving entry ", hostName,
'\n');
814 deleteEntry(dummy.dirEntry[fullMsxDirEntry.index]);
815 writeLogicalSector(fullMsxDirEntry.sector, dummy);
820 DirEntry entry = addEntryToDir(rootSector);
821 readLogicalSector(entry.sector, buf);
822 auto& dirEntry = buf.dirEntry[entry.index];
823 memset(&dirEntry, 0,
sizeof(dirEntry));
825 dirEntry.attrib = MSXDirEntry::Attrib::REGULAR;
828 auto [time, date] = getTimeDate(fullHostName);
829 dirEntry.time = time;
830 dirEntry.date = date;
833 alterFileInDSK(dirEntry, fullHostName);
834 }
catch (MSXException&) {
836 writeLogicalSector(entry.sector, buf);
839 writeLogicalSector(entry.sector, buf);
843std::string MSXtar::addOrCreateSubdir(
zstring_view hostDirName,
unsigned sector, Add add)
845 FileName msxFileName = hostToMSXFileName(hostDirName);
846 auto printableFilename = msxToHostFileName(msxFileName);
848 if (DirEntry entry = findEntryInDir(msxFileName, sector, buf);
851 auto& msxDirEntry = buf.dirEntry[entry.index];
852 if (msxDirEntry.attrib & MSXDirEntry::Attrib::DIRECTORY) {
854 DirCluster nextCluster = getStartCluster(msxDirEntry);
856 [&](Free) {
return strCat(
"Directory ", printableFilename,
" goes to root.\n"); },
857 [&](Cluster cluster) {
return recurseDirFill(hostDirName, clusterToSector(cluster), add); }
861 if (add == Add::PRESERVE) {
862 return strCat(
"MSX file ", printableFilename,
" is not a directory.\n");
865 deleteEntry(msxDirEntry);
866 writeLogicalSector(entry.sector, buf);
870 unsigned nextSector = addSubdirToDSK(hostDirName, msxFileName, sector);
871 return recurseDirFill(hostDirName, nextSector, add);
876string MSXtar::recurseDirFill(string_view dirName,
unsigned sector, Add add)
880 auto fileAction = [&](
const string& path) {
882 messages += addFileToDSK(path, sector, add);
884 auto dirAction = [&](
const string& path) {
886 messages += addOrCreateSubdir(path, sector, add);
894string MSXtar::msxToHostFileName(
const FileName& msxName)
const
897 for (
unsigned i = 0; i < 8 && msxName[i] !=
' '; ++i) {
898 result += char(tolower(msxName[i]));
900 if (msxName[8] !=
' ') {
902 for (
unsigned i = 8; i < 11 && msxName[i] !=
' '; ++i) {
903 result += char(tolower(msxName[i]));
906 std::span<const uint8_t> resultSpan(std::bit_cast<const uint8_t*>(result.data()), result.size());
907 return msxChars.msxToUtf8(resultSpan,
'_');
912static void changeTime(
zstring_view resultFile,
const MSXDirEntry& dirEntry)
914 unsigned t = dirEntry.time;
915 unsigned d = dirEntry.date;
918 mTim.tm_sec = narrow<int>((
t & 0x001f) << 1);
919 mTim.tm_min = narrow<int>((
t & 0x07e0) >> 5);
920 mTim.tm_hour = narrow<int>((
t & 0xf800) >> 11);
921 mTim.tm_mday = narrow<int>( (d & 0x001f) >> 0);
922 mTim.tm_mon = narrow<int>(((d & 0x01e0) >> 5) - 1);
923 mTim.tm_year = narrow<int>(((d & 0xfe00) >> 9) + 80);
925 uTim.actime = mktime(&mTim);
926 uTim.modtime = mktime(&mTim);
927 utime(resultFile.
c_str(), &uTim);
933 for (
unsigned sector = chrootSector; sector != 0; sector = getNextSector(sector)) {
935 readLogicalSector(sector, buf);
936 for (
auto& dirEntry : buf.
dirEntry) {
937 if (dirEntry.filename[0] ==
char(0x00)) {
940 if (dirEntry.filename[0] ==
char(0xe5) || dirEntry.attrib == T_MSX_LFN) {
944 auto filename = msxToHostFileName(dirEntry.filename);
952std::string MSXtar::dir()
955 auto list = dirRaw();
956 auto num = list.size();
957 for (
unsigned i = 0; i < num; ++i) {
958 auto entry = list.getListIndexUnchecked(i);
959 auto filename = std::string(entry.getListIndexUnchecked(0).getString());
960 auto attrib = DiskImageUtils::formatAttrib(
MSXDirEntry::AttribValue(uint8_t(entry.getListIndexUnchecked(1).getOptionalInt().value_or(0))));
962 auto size = entry.getListIndexUnchecked(3).getOptionalInt().value_or(0);
964 filename.resize(13,
' ');
965 strAppend(result, filename, attrib,
" ", size,
'\n');
971void MSXtar::chdir(string_view newRootDir)
973 chroot(newRootDir,
false);
976void MSXtar::mkdir(string_view newRootDir)
978 unsigned tmpMSXchrootSector = chrootSector;
979 chroot(newRootDir,
true);
980 chrootSector = tmpMSXchrootSector;
983void MSXtar::chroot(string_view newRootDir,
bool createDir)
985 if (newRootDir.starts_with(
'/') || newRootDir.starts_with(
'\\')) {
987 chrootSector = rootDirStart;
991 while (!newRootDir.empty()) {
993 newRootDir = lastPart;
998 FileName msxName = hostToMSXFileName(firstPart);
999 DirEntry entry = findEntryInDir(msxName, chrootSector, buf);
1000 if (entry.sector == 0) {
1002 throw MSXException(
"Subdirectory ", firstPart,
1008 auto [
t, d] = DiskImageUtils::toTimeDate(now);
1009 chrootSector = addSubdir(msxName,
t, d, chrootSector);
1011 const auto& dirEntry = buf.dirEntry[entry.index];
1012 if (!(dirEntry.attrib & MSXDirEntry::Attrib::DIRECTORY)) {
1013 throw MSXException(firstPart,
" is not a directory.");
1015 DirCluster chrootCluster = getStartCluster(dirEntry);
1017 [
this](Free) {
return rootDirStart; },
1018 [
this](Cluster cluster) {
return clusterToSector(cluster); }
1024void MSXtar::fileExtract(
const string& resultFile,
const MSXDirEntry& dirEntry)
1026 unsigned size = dirEntry.size;
1028 [](Free) {
return 0u; },
1029 [
this](Cluster cluster) {
return clusterToSector(cluster); }
1030 }, getStartCluster(dirEntry));
1032 File file(resultFile,
"wb");
1033 while (size && sector) {
1035 readLogicalSector(sector, buf);
1036 unsigned saveSize = std::min(size, SECTOR_SIZE);
1037 file.write(
subspan(buf.raw, 0, saveSize));
1039 sector = getNextSector(sector);
1042 changeTime(resultFile, dirEntry);
1046string MSXtar::singleItemExtract(string_view dirName, string_view itemName,
1051 FileName msxName = hostToMSXFileName(itemName);
1052 DirEntry entry = findEntryInDir(msxName, sector, buf);
1053 if (entry.sector == 0) {
1054 return strCat(itemName,
" not found!\n");
1057 const auto& msxDirEntry = buf.dirEntry[entry.index];
1059 string fullName =
strCat(dirName,
'/', msxToHostFileName(msxDirEntry.filename));
1062 if (msxDirEntry.attrib & MSXDirEntry::Attrib::DIRECTORY) {
1064 FileOperations::mkdirp(fullName);
1065 DirCluster nextCluster = getStartCluster(msxDirEntry);
1068 [&](Cluster cluster) { recurseDirExtract(fullName, clusterToSector(cluster)); }
1072 fileExtract(fullName, msxDirEntry);
1079void MSXtar::recurseDirExtract(string_view dirName,
unsigned sector)
1081 for ( ; sector != 0; sector = getNextSector(sector)) {
1083 readLogicalSector(sector, buf);
1084 for (
auto& dirEntry : buf.dirEntry) {
1085 if (dirEntry.filename[0] ==
char(0x00)) {
1088 if (dirEntry.filename[0] ==
one_of(
char(0xe5),
'.') || dirEntry.attrib == T_MSX_LFN) {
1091 string filename = msxToHostFileName(dirEntry.filename);
1092 string fullName = filename;
1093 if (!dirName.empty()) {
1094 fullName =
strCat(dirName,
'/', filename);
1096 if (dirEntry.attrib & MSXDirEntry::Attrib::DIRECTORY) {
1097 FileOperations::mkdirp(fullName);
1099 changeTime(fullName, dirEntry);
1102 [&](Cluster cluster) { recurseDirExtract(fullName, clusterToSector(cluster)); }
1103 }, getStartCluster(dirEntry));
1105 fileExtract(fullName, dirEntry);
1111std::string MSXtar::addItem(
const std::string& hostItemName,
Add add)
1113 if (
auto stat = FileOperations::getStat(hostItemName)) {
1114 if (FileOperations::isRegularFile(*stat)) {
1115 return addFileToDSK(hostItemName, chrootSector, add);
1116 }
else if (FileOperations::isDirectory(hostItemName)) {
1117 return addOrCreateSubdir(hostItemName, chrootSector, add);
1123string MSXtar::addDir(string_view rootDirName,
Add add)
1125 return recurseDirFill(rootDirName, chrootSector, add);
1128string MSXtar::addFile(
const string& filename,
Add add)
1130 return addFileToDSK(filename, chrootSector, add);
1133string MSXtar::getItemFromDir(string_view rootDirName, string_view itemName)
1135 return singleItemExtract(rootDirName, itemName, chrootSector);
1138void MSXtar::getDir(string_view rootDirName)
1140 recurseDirExtract(rootDirName, chrootSector);
1143std::string MSXtar::deleteItem(std::string_view itemName)
1145 FileName msxName = hostToMSXFileName(itemName);
1146 return deleteEntry(msxName, chrootSector);
1149std::string MSXtar::convertToMsxName(std::string_view name)
const
1151 FileName msxName = hostToMSXFileName(name);
1152 return msxToHostFileName(msxName);
1157 return {countFreeClusters(), sectorsPerCluster * SECTOR_SIZE};
MSXtar(SectorAccessibleDisk &disk, const MsxChar2Unicode &msxChars_)
void readSectors(std::span< SectorBuffer > buffers, size_t startSector) const
size_t getNbSectors() const
void readSector(size_t sector, SectorBuffer &buf) 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)
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)
constexpr bool equal(InputRange1 &&range1, InputRange2 &&range2, Pred pred={}, Proj1 proj1={}, Proj2 proj2={})
constexpr auto copy(InputRange &&range, OutputIter out)
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)