class File2 { static constexpr const char* NAME = "FAT32_File2"; static constexpr const uint32_t F_EOF = 0xFFFFFFFF; FS& fs; DirEntryAt dea; uint32_t curAbsPos = 0; // absolute position withithin the file uint32_t posInCluster = 0; // position within the current cluster uint32_t iCurCluster = 0; // current cluster index (within list of clusters) std::vector clusters; // all clusters the file uses public: File2(FS& fs, const DirEntryAt& dea) : fs(fs), dea(dea) { Log::addInfo(NAME, "init @ cluster %d", getFirstCluster()); getAllClusters(dea.getFirstCluster()); } /** the file's USED size */ uint32_t getSize() const {return dea.getSize();} /** the file's ALLOCATED size */ uint32_t getAllocatedSize() const {return clusters.size() * fs.tmp.bytesPerCluster;} /** the file's first cluster */ ClusterNr getFirstCluster() const {return dea.getFirstCluster();} /** get the file's name */ const std::string& getName() const {return dea.getName();} /** read x bytes from the file */ uint32_t read(uint32_t size, uint8_t* dst, std::function callback) { Log::addInfo(NAME, "read %d bytes", size); uint32_t remaining = size; uint32_t totalRead = 0; while(remaining) { const uint32_t read = _read(remaining, dst); if (read == F_EOF) { Log::addInfo(NAME, "EOF"); break; } remaining -= read; dst += read; totalRead += read; callback(totalRead*100/size); } return totalRead; } uint32_t write(uint32_t size, const uint8_t* src, std::function callback) { Log::addInfo(NAME, "write %d bytes", size); uint32_t remaining = size; uint32_t totalWritten = 0; while(remaining) { const uint32_t written = _write(remaining, src); remaining -= written; src += written; totalWritten += written; callback(totalWritten*100/size); } // update the file header (size might have changed) fs.write(dea); return totalWritten; } private: void setSize(uint32_t size) {dea.setSize(size);} /** fetch the list of all clusters the file is using */ void getAllClusters(ClusterNr startCluster) { // initial cluster clusters.push_back(startCluster); // all following clusters while(true) { startCluster = fs.getNextCluster(startCluster); if (!startCluster.isEndOfClusters()) { clusters.push_back(startCluster); } else { break; } } // debug char buf[1024]; char* dst = buf; dst += sprintf(dst, "clusters: "); for (ClusterNr nr : clusters) {dst += sprintf(dst, "%d ", nr);} Log::addInfo(NAME, buf); } int32_t _read(uint32_t readSize, uint8_t* dst) { // EOF reached? if (curAbsPos >= getSize()) { return F_EOF; } // end of current cluster reached? -> switch to the next one if (posInCluster == fs.tmp.bytesPerCluster) { ++iCurCluster; posInCluster = 0; Log::addInfo(NAME, "next cluster [%d] %d", iCurCluster, clusters[iCurCluster]); } // how many bytes are left in the current cluster? const uint32_t remainingInCluster = fs.tmp.bytesPerCluster - posInCluster; // determine how many bytes to read const uint32_t toRead = std::min(remainingInCluster, readSize); const uint32_t offset = fs.clusterToAbsPos(clusters[iCurCluster]) + posInCluster; const uint32_t read = fs.dev.read(offset, toRead, dst); posInCluster += read; curAbsPos += read; return read; } int32_t _write(uint32_t writeSize, const uint8_t* src) { if (curAbsPos >= getAllocatedSize()) { if (clusters.empty()) { const ClusterNr newCluster = fs.allocFreeCluster(0); clusters.push_back(newCluster); Log::addInfo(NAME, "allocFirstCluster: %d", newCluster); } else { const ClusterNr prevCluster = clusters.back(); const ClusterNr newCluster = fs.allocFreeCluster(prevCluster); clusters.push_back(newCluster); Log::addInfo(NAME, "allocNextCluster(%d): %d", prevCluster, newCluster); } dea.setSize(dea.getSize() + fs.tmp.bytesPerCluster); fs.write(dea); } // end of current cluster reached? -> switch to the next one if (posInCluster == fs.tmp.bytesPerCluster) { ++iCurCluster; posInCluster = 0; Log::addInfo(NAME, "next cluster [%d] %d", iCurCluster, clusters[iCurCluster]); } // how many bytes are left in the current cluster? const uint32_t remainingInCluster = fs.tmp.bytesPerCluster - posInCluster; // determine how many bytes to write const uint32_t toWrite = std::min(remainingInCluster, writeSize); const uint32_t offset = fs.clusterToAbsPos(clusters[iCurCluster]) + posInCluster; const uint32_t written = fs.dev.write(offset, toWrite, src); posInCluster += written; curAbsPos += written; // adjust the number of bytes used if (this->getSize() < curAbsPos) {this->setSize(curAbsPos);} return written; } };