|
|
@ -40,7 +40,7 @@ |
|
|
|
#include "SdBaseFile.h" |
|
|
|
|
|
|
|
#include "../MarlinCore.h" |
|
|
|
SdBaseFile* SdBaseFile::cwd_ = 0; // Pointer to Current Working Directory
|
|
|
|
SdBaseFile *SdBaseFile::cwd_ = 0; // Pointer to Current Working Directory
|
|
|
|
|
|
|
|
// callback function for date/time
|
|
|
|
void (*SdBaseFile::dateTime_)(uint16_t *date, uint16_t *time) = 0; |
|
|
@ -155,7 +155,7 @@ bool SdBaseFile::contiguousRange(uint32_t *bgnBlock, uint32_t *endBlock) { |
|
|
|
* a file is already open, the file already exists, the root |
|
|
|
* directory is full or an I/O error. |
|
|
|
*/ |
|
|
|
bool SdBaseFile::createContiguous(SdBaseFile* dirFile, const char *path, uint32_t size) { |
|
|
|
bool SdBaseFile::createContiguous(SdBaseFile *dirFile, const char *path, uint32_t size) { |
|
|
|
if (ENABLED(SDCARD_READONLY)) return false; |
|
|
|
|
|
|
|
uint32_t count; |
|
|
@ -187,12 +187,11 @@ bool SdBaseFile::createContiguous(SdBaseFile* dirFile, const char *path, uint32_ |
|
|
|
* \return true for success, false for failure. |
|
|
|
*/ |
|
|
|
bool SdBaseFile::dirEntry(dir_t *dir) { |
|
|
|
dir_t *p; |
|
|
|
// make sure fields on SD are correct
|
|
|
|
if (!sync()) return false; |
|
|
|
|
|
|
|
// read entry
|
|
|
|
p = cacheDirEntry(SdVolume::CACHE_FOR_READ); |
|
|
|
dir_t *p = cacheDirEntry(SdVolume::CACHE_FOR_READ); |
|
|
|
if (!p) return false; |
|
|
|
|
|
|
|
// copy to caller's struct
|
|
|
@ -207,7 +206,7 @@ bool SdBaseFile::dirEntry(dir_t *dir) { |
|
|
|
* \param[in] dir The directory structure containing the name. |
|
|
|
* \param[out] name A 13 byte char array for the formatted name. |
|
|
|
*/ |
|
|
|
void SdBaseFile::dirName(const dir_t& dir, char *name) { |
|
|
|
void SdBaseFile::dirName(const dir_t &dir, char *name) { |
|
|
|
uint8_t j = 0; |
|
|
|
LOOP_L_N(i, 11) { |
|
|
|
if (dir.name[i] == ' ')continue; |
|
|
@ -386,7 +385,7 @@ int8_t SdBaseFile::lsPrintNext(uint8_t flags, uint8_t indent) { |
|
|
|
} |
|
|
|
|
|
|
|
// Format directory name field from a 8.3 name string
|
|
|
|
bool SdBaseFile::make83Name(const char *str, uint8_t *name, const char** ptr) { |
|
|
|
bool SdBaseFile::make83Name(const char *str, uint8_t *name, const char **ptr) { |
|
|
|
uint8_t n = 7, // Max index until a dot is found
|
|
|
|
i = 11; |
|
|
|
while (i) name[--i] = ' '; // Set whole FILENAME.EXT to spaces
|
|
|
@ -423,13 +422,13 @@ bool SdBaseFile::make83Name(const char *str, uint8_t *name, const char** ptr) { |
|
|
|
* Reasons for failure include this file is already open, \a parent is not a |
|
|
|
* directory, \a path is invalid or already exists in \a parent. |
|
|
|
*/ |
|
|
|
bool SdBaseFile::mkdir(SdBaseFile* parent, const char *path, bool pFlag) { |
|
|
|
bool SdBaseFile::mkdir(SdBaseFile *parent, const char *path, bool pFlag) { |
|
|
|
if (ENABLED(SDCARD_READONLY)) return false; |
|
|
|
|
|
|
|
uint8_t dname[11]; |
|
|
|
SdBaseFile dir1, dir2; |
|
|
|
SdBaseFile* sub = &dir1; |
|
|
|
SdBaseFile* start = parent; |
|
|
|
SdBaseFile *sub = &dir1; |
|
|
|
SdBaseFile *start = parent; |
|
|
|
|
|
|
|
if (!parent || isOpen()) return false; |
|
|
|
|
|
|
@ -455,13 +454,9 @@ bool SdBaseFile::mkdir(SdBaseFile* parent, const char *path, bool pFlag) { |
|
|
|
return mkdir(parent, dname); |
|
|
|
} |
|
|
|
|
|
|
|
bool SdBaseFile::mkdir(SdBaseFile* parent, const uint8_t dname[11]) { |
|
|
|
bool SdBaseFile::mkdir(SdBaseFile *parent, const uint8_t dname[11]) { |
|
|
|
if (ENABLED(SDCARD_READONLY)) return false; |
|
|
|
|
|
|
|
uint32_t block; |
|
|
|
dir_t d; |
|
|
|
dir_t *p; |
|
|
|
|
|
|
|
if (!parent->isDir()) return false; |
|
|
|
|
|
|
|
// create a normal file
|
|
|
@ -478,19 +473,20 @@ bool SdBaseFile::mkdir(SdBaseFile* parent, const uint8_t dname[11]) { |
|
|
|
if (!sync()) return false; |
|
|
|
|
|
|
|
// cache entry - should already be in cache due to sync() call
|
|
|
|
p = cacheDirEntry(SdVolume::CACHE_FOR_WRITE); |
|
|
|
dir_t *p = cacheDirEntry(SdVolume::CACHE_FOR_WRITE); |
|
|
|
if (!p) return false; |
|
|
|
|
|
|
|
// change directory entry attribute
|
|
|
|
p->attributes = DIR_ATT_DIRECTORY; |
|
|
|
|
|
|
|
// make entry for '.'
|
|
|
|
dir_t d; |
|
|
|
memcpy(&d, p, sizeof(d)); |
|
|
|
d.name[0] = '.'; |
|
|
|
LOOP_S_L_N(i, 1, 11) d.name[i] = ' '; |
|
|
|
|
|
|
|
// cache block for '.' and '..'
|
|
|
|
block = vol_->clusterStartBlock(firstCluster_); |
|
|
|
uint32_t block = vol_->clusterStartBlock(firstCluster_); |
|
|
|
if (!vol_->cacheRawBlock(block, SdVolume::CACHE_FOR_WRITE)) return false; |
|
|
|
|
|
|
|
// copy '.' to block
|
|
|
@ -577,7 +573,7 @@ bool SdBaseFile::open(const char *path, uint8_t oflag) { |
|
|
|
* a directory, \a path is invalid, the file does not exist |
|
|
|
* or can't be opened in the access mode specified by oflag. |
|
|
|
*/ |
|
|
|
bool SdBaseFile::open(SdBaseFile* dirFile, const char *path, uint8_t oflag) { |
|
|
|
bool SdBaseFile::open(SdBaseFile *dirFile, const char *path, uint8_t oflag) { |
|
|
|
uint8_t dname[11]; |
|
|
|
SdBaseFile dir1, dir2; |
|
|
|
SdBaseFile *parent = dirFile, *sub = &dir1; |
|
|
@ -605,7 +601,7 @@ bool SdBaseFile::open(SdBaseFile* dirFile, const char *path, uint8_t oflag) { |
|
|
|
} |
|
|
|
|
|
|
|
// open with filename in dname
|
|
|
|
bool SdBaseFile::open(SdBaseFile* dirFile, const uint8_t dname[11], uint8_t oflag) { |
|
|
|
bool SdBaseFile::open(SdBaseFile *dirFile, const uint8_t dname[11], uint8_t oflag) { |
|
|
|
bool emptyFound = false, fileFound = false; |
|
|
|
uint8_t index; |
|
|
|
dir_t *p; |
|
|
@ -696,9 +692,7 @@ bool SdBaseFile::open(SdBaseFile* dirFile, const uint8_t dname[11], uint8_t ofla |
|
|
|
* See open() by path for definition of flags. |
|
|
|
* \return true for success or false for failure. |
|
|
|
*/ |
|
|
|
bool SdBaseFile::open(SdBaseFile* dirFile, uint16_t index, uint8_t oflag) { |
|
|
|
dir_t *p; |
|
|
|
|
|
|
|
bool SdBaseFile::open(SdBaseFile *dirFile, uint16_t index, uint8_t oflag) { |
|
|
|
vol_ = dirFile->vol_; |
|
|
|
|
|
|
|
// error if already open
|
|
|
@ -711,7 +705,7 @@ bool SdBaseFile::open(SdBaseFile* dirFile, uint16_t index, uint8_t oflag) { |
|
|
|
if (!dirFile->seekSet(32 * index)) return false; |
|
|
|
|
|
|
|
// read entry into cache
|
|
|
|
p = dirFile->readDirCache(); |
|
|
|
dir_t *p = dirFile->readDirCache(); |
|
|
|
if (!p) return false; |
|
|
|
|
|
|
|
// error if empty slot or '.' or '..'
|
|
|
@ -784,10 +778,7 @@ bool SdBaseFile::openCachedEntry(uint8_t dirIndex, uint8_t oflag) { |
|
|
|
* See open() by path for definition of flags. |
|
|
|
* \return true for success or false for failure. |
|
|
|
*/ |
|
|
|
bool SdBaseFile::openNext(SdBaseFile* dirFile, uint8_t oflag) { |
|
|
|
dir_t *p; |
|
|
|
uint8_t index; |
|
|
|
|
|
|
|
bool SdBaseFile::openNext(SdBaseFile *dirFile, uint8_t oflag) { |
|
|
|
if (!dirFile) return false; |
|
|
|
|
|
|
|
// error if already open
|
|
|
@ -796,10 +787,10 @@ bool SdBaseFile::openNext(SdBaseFile* dirFile, uint8_t oflag) { |
|
|
|
vol_ = dirFile->vol_; |
|
|
|
|
|
|
|
while (1) { |
|
|
|
index = 0xF & (dirFile->curPosition_ >> 5); |
|
|
|
uint8_t index = 0xF & (dirFile->curPosition_ >> 5); |
|
|
|
|
|
|
|
// read entry into cache
|
|
|
|
p = dirFile->readDirCache(); |
|
|
|
dir_t *p = dirFile->readDirCache(); |
|
|
|
if (!p) return false; |
|
|
|
|
|
|
|
// done if last entry
|
|
|
@ -825,9 +816,8 @@ bool SdBaseFile::openNext(SdBaseFile* dirFile, uint8_t oflag) { |
|
|
|
* |
|
|
|
* \return true for success, false for failure. |
|
|
|
*/ |
|
|
|
bool SdBaseFile::openParent(SdBaseFile* dir) { |
|
|
|
bool SdBaseFile::openParent(SdBaseFile *dir) { |
|
|
|
dir_t entry; |
|
|
|
dir_t *p; |
|
|
|
SdBaseFile file; |
|
|
|
uint32_t c; |
|
|
|
uint32_t cluster; |
|
|
@ -850,7 +840,7 @@ bool SdBaseFile::openParent(SdBaseFile* dir) { |
|
|
|
// first block of parent dir
|
|
|
|
if (!vol_->cacheRawBlock(lbn, SdVolume::CACHE_FOR_READ)) return false; |
|
|
|
|
|
|
|
p = &vol_->cacheBuffer_.dir[1]; |
|
|
|
dir_t *p = &vol_->cacheBuffer_.dir[1]; |
|
|
|
// verify name for '../..'
|
|
|
|
if (p->name[0] != '.' || p->name[1] != '.') return false; |
|
|
|
// '..' is pointer to first cluster of parent. open '../..' to find parent
|
|
|
@ -881,7 +871,7 @@ bool SdBaseFile::openParent(SdBaseFile* dir) { |
|
|
|
* Reasons for failure include the file is already open, the FAT volume has |
|
|
|
* not been initialized or it a FAT12 volume. |
|
|
|
*/ |
|
|
|
bool SdBaseFile::openRoot(SdVolume* vol) { |
|
|
|
bool SdBaseFile::openRoot(SdVolume *vol) { |
|
|
|
// error if file is already open
|
|
|
|
if (isOpen()) return false; |
|
|
|
|
|
|
@ -1008,7 +998,7 @@ int16_t SdBaseFile::read() { |
|
|
|
* read() called before a file has been opened, corrupt file system |
|
|
|
* or an I/O error occurred. |
|
|
|
*/ |
|
|
|
int16_t SdBaseFile::read(void* buf, uint16_t nbyte) { |
|
|
|
int16_t SdBaseFile::read(void *buf, uint16_t nbyte) { |
|
|
|
uint8_t *dst = reinterpret_cast<uint8_t*>(buf); |
|
|
|
uint16_t offset, toRead; |
|
|
|
uint32_t block; // raw device block number
|
|
|
@ -1136,7 +1126,7 @@ int8_t SdBaseFile::readDir(dir_t *dir, char *longFilename) { |
|
|
|
// Reset n to the start of the long name
|
|
|
|
n = 0; |
|
|
|
for (uint16_t idx = 0; idx < (LONG_FILENAME_LENGTH) / 2; idx += 2) { // idx is fixed since FAT LFN always contains UTF-16LE encoding
|
|
|
|
uint16_t utf16_ch = longFilename[idx] | (longFilename[idx + 1] << 8); |
|
|
|
const uint16_t utf16_ch = longFilename[idx] | (longFilename[idx + 1] << 8); |
|
|
|
if (0xD800 == (utf16_ch & 0xF800)) // Surrogate pair - encode as '_'
|
|
|
|
longFilename[n++] = '_'; |
|
|
|
else if (0 == (utf16_ch & 0xFF80)) // Encode as 1-byte UTF-8 char
|
|
|
@ -1199,12 +1189,11 @@ dir_t* SdBaseFile::readDirCache() { |
|
|
|
bool SdBaseFile::remove() { |
|
|
|
if (ENABLED(SDCARD_READONLY)) return false; |
|
|
|
|
|
|
|
dir_t *d; |
|
|
|
// free any clusters - will fail if read-only or directory
|
|
|
|
if (!truncate(0)) return false; |
|
|
|
|
|
|
|
// cache directory entry
|
|
|
|
d = cacheDirEntry(SdVolume::CACHE_FOR_WRITE); |
|
|
|
dir_t *d = cacheDirEntry(SdVolume::CACHE_FOR_WRITE); |
|
|
|
if (!d) return false; |
|
|
|
|
|
|
|
// mark entry deleted
|
|
|
@ -1235,7 +1224,7 @@ bool SdBaseFile::remove() { |
|
|
|
* \a dirFile is not a directory, \a path is not found |
|
|
|
* or an I/O error occurred. |
|
|
|
*/ |
|
|
|
bool SdBaseFile::remove(SdBaseFile* dirFile, const char *path) { |
|
|
|
bool SdBaseFile::remove(SdBaseFile *dirFile, const char *path) { |
|
|
|
if (ENABLED(SDCARD_READONLY)) return false; |
|
|
|
|
|
|
|
SdBaseFile file; |
|
|
@ -1252,13 +1241,10 @@ bool SdBaseFile::remove(SdBaseFile* dirFile, const char *path) { |
|
|
|
* Reasons for failure include \a dirFile is not open or is not a directory |
|
|
|
* file, newPath is invalid or already exists, or an I/O error occurs. |
|
|
|
*/ |
|
|
|
bool SdBaseFile::rename(SdBaseFile* dirFile, const char *newPath) { |
|
|
|
bool SdBaseFile::rename(SdBaseFile *dirFile, const char *newPath) { |
|
|
|
if (ENABLED(SDCARD_READONLY)) return false; |
|
|
|
|
|
|
|
dir_t entry; |
|
|
|
uint32_t dirCluster = 0; |
|
|
|
SdBaseFile file; |
|
|
|
dir_t *d; |
|
|
|
|
|
|
|
// must be an open file or subdirectory
|
|
|
|
if (!(isFile() || isSubDir())) return false; |
|
|
@ -1268,16 +1254,18 @@ bool SdBaseFile::rename(SdBaseFile* dirFile, const char *newPath) { |
|
|
|
|
|
|
|
// sync() and cache directory entry
|
|
|
|
sync(); |
|
|
|
d = cacheDirEntry(SdVolume::CACHE_FOR_WRITE); |
|
|
|
dir_t *d = cacheDirEntry(SdVolume::CACHE_FOR_WRITE); |
|
|
|
if (!d) return false; |
|
|
|
|
|
|
|
// save directory entry
|
|
|
|
dir_t entry; |
|
|
|
memcpy(&entry, d, sizeof(entry)); |
|
|
|
|
|
|
|
// mark entry deleted
|
|
|
|
d->name[0] = DIR_NAME_DELETED; |
|
|
|
|
|
|
|
// make directory entry for new path
|
|
|
|
SdBaseFile file; |
|
|
|
if (isFile()) { |
|
|
|
if (!file.open(dirFile, newPath, O_CREAT | O_EXCL | O_WRITE)) { |
|
|
|
goto restore; |
|
|
@ -1536,8 +1524,7 @@ bool SdBaseFile::sync() { |
|
|
|
* |
|
|
|
* \return true for success, false for failure. |
|
|
|
*/ |
|
|
|
bool SdBaseFile::timestamp(SdBaseFile* file) { |
|
|
|
dir_t *d; |
|
|
|
bool SdBaseFile::timestamp(SdBaseFile *file) { |
|
|
|
dir_t dir; |
|
|
|
|
|
|
|
// get timestamps
|
|
|
@ -1546,7 +1533,7 @@ bool SdBaseFile::timestamp(SdBaseFile* file) { |
|
|
|
// update directory fields
|
|
|
|
if (!sync()) return false; |
|
|
|
|
|
|
|
d = cacheDirEntry(SdVolume::CACHE_FOR_WRITE); |
|
|
|
dir_t *d = cacheDirEntry(SdVolume::CACHE_FOR_WRITE); |
|
|
|
if (!d) return false; |
|
|
|
|
|
|
|
// copy timestamps
|
|
|
@ -1599,7 +1586,6 @@ bool SdBaseFile::timestamp(uint8_t flags, uint16_t year, uint8_t month, |
|
|
|
if (ENABLED(SDCARD_READONLY)) return false; |
|
|
|
|
|
|
|
uint16_t dirDate, dirTime; |
|
|
|
dir_t *d; |
|
|
|
|
|
|
|
if (!isOpen() |
|
|
|
|| year < 1980 |
|
|
@ -1616,7 +1602,7 @@ bool SdBaseFile::timestamp(uint8_t flags, uint16_t year, uint8_t month, |
|
|
|
// update directory entry
|
|
|
|
if (!sync()) return false; |
|
|
|
|
|
|
|
d = cacheDirEntry(SdVolume::CACHE_FOR_WRITE); |
|
|
|
dir_t *d = cacheDirEntry(SdVolume::CACHE_FOR_WRITE); |
|
|
|
if (!d) return false; |
|
|
|
|
|
|
|
dirDate = FAT_DATE(year, month, day); |
|
|
@ -1710,7 +1696,7 @@ bool SdBaseFile::truncate(uint32_t length) { |
|
|
|
* include write() is called before a file has been opened, write is called |
|
|
|
* for a read-only file, device is full, a corrupt file system or an I/O error. |
|
|
|
*/ |
|
|
|
int16_t SdBaseFile::write(const void* buf, uint16_t nbyte) { |
|
|
|
int16_t SdBaseFile::write(const void *buf, uint16_t nbyte) { |
|
|
|
#if ENABLED(SDCARD_READONLY) |
|
|
|
writeError = true; return -1; |
|
|
|
#endif |
|
|
|