mirror of
https://github.com/MarlinFirmware/Marlin.git
synced 2024-11-26 21:36:21 +00:00
🐛 Fix M23 long filename support (#25540)
This commit is contained in:
parent
e0132f7a1e
commit
a3ab435acf
@ -703,7 +703,10 @@ bool SdBaseFile::open(SdBaseFile *dirFile, const uint8_t dname[11]
|
|||||||
// Get VFat dir entry
|
// Get VFat dir entry
|
||||||
pvFat = (vfat_t *) p;
|
pvFat = (vfat_t *) p;
|
||||||
// Get checksum from the last entry of the sequence
|
// Get checksum from the last entry of the sequence
|
||||||
if (pvFat->sequenceNumber & 0x40) lfnChecksum = pvFat->checksum;
|
if (pvFat->sequenceNumber & 0x40) {
|
||||||
|
lfnChecksum = pvFat->checksum;
|
||||||
|
ZERO(lfnName);
|
||||||
|
}
|
||||||
// Get LFN sequence number
|
// Get LFN sequence number
|
||||||
lfnSequenceNumber = pvFat->sequenceNumber & 0x1F;
|
lfnSequenceNumber = pvFat->sequenceNumber & 0x1F;
|
||||||
if WITHIN(lfnSequenceNumber, 1, reqEntriesNum) {
|
if WITHIN(lfnSequenceNumber, 1, reqEntriesNum) {
|
||||||
@ -711,6 +714,7 @@ bool SdBaseFile::open(SdBaseFile *dirFile, const uint8_t dname[11]
|
|||||||
if (lfnChecksum == pvFat->checksum) {
|
if (lfnChecksum == pvFat->checksum) {
|
||||||
// Set chunk of LFN from VFAT entry into lfnName
|
// Set chunk of LFN from VFAT entry into lfnName
|
||||||
getLFNName(pvFat, (char *)lfnName, lfnSequenceNumber);
|
getLFNName(pvFat, (char *)lfnName, lfnSequenceNumber);
|
||||||
|
TERN_(UTF_FILENAME_SUPPORT, convertUtf16ToUtf8((char *)lfnName));
|
||||||
// LFN found?
|
// LFN found?
|
||||||
if (!strncasecmp((char*)dlname, (char*)lfnName, lfnNameLength)) lfnFileFound = true;
|
if (!strncasecmp((char*)dlname, (char*)lfnName, lfnNameLength)) lfnFileFound = true;
|
||||||
}
|
}
|
||||||
@ -1132,13 +1136,13 @@ bool SdBaseFile::openNext(SdBaseFile *dirFile, uint8_t oflag) {
|
|||||||
* Get the LFN filename block from a dir. Get the block in lname at startOffset
|
* Get the LFN filename block from a dir. Get the block in lname at startOffset
|
||||||
*/
|
*/
|
||||||
void SdBaseFile::getLFNName(vfat_t *pFatDir, char *lname, uint8_t sequenceNumber) {
|
void SdBaseFile::getLFNName(vfat_t *pFatDir, char *lname, uint8_t sequenceNumber) {
|
||||||
uint8_t startOffset = (sequenceNumber - 1) * FILENAME_LENGTH;
|
const uint8_t startOffset = (sequenceNumber - 1) * FILENAME_LENGTH;
|
||||||
LOOP_L_N(i, FILENAME_LENGTH) {
|
LOOP_L_N(i, FILENAME_LENGTH) {
|
||||||
const uint16_t utf16_ch = (i >= 11) ? pFatDir->name3[i - 11] : (i >= 5) ? pFatDir->name2[i - 5] : pFatDir->name1[i];
|
const uint16_t utf16_ch = (i >= 11) ? pFatDir->name3[i - 11] : (i >= 5) ? pFatDir->name2[i - 5] : pFatDir->name1[i];
|
||||||
#if ENABLED(UTF_FILENAME_SUPPORT)
|
#if ENABLED(UTF_FILENAME_SUPPORT)
|
||||||
// We can't reconvert to UTF-8 here as UTF-8 is variable-size encoding, but joining LFN blocks
|
// We can't reconvert to UTF-8 here as UTF-8 is variable-size encoding, but joining LFN blocks
|
||||||
// needs static bytes addressing. So here just store full UTF-16LE words to re-convert later.
|
// needs static bytes addressing. So here just store full UTF-16LE words to re-convert later.
|
||||||
uint16_t idx = (startOffset + i) * 2; // This is fixed as FAT LFN always contain UTF-16LE encoding
|
const uint16_t idx = (startOffset + i) * 2; // This is fixed as FAT LFN always contain UTF-16LE encoding
|
||||||
lname[idx] = utf16_ch & 0xFF;
|
lname[idx] = utf16_ch & 0xFF;
|
||||||
lname[idx + 1] = (utf16_ch >> 8) & 0xFF;
|
lname[idx + 1] = (utf16_ch >> 8) & 0xFF;
|
||||||
#else
|
#else
|
||||||
@ -1152,8 +1156,8 @@ bool SdBaseFile::openNext(SdBaseFile *dirFile, uint8_t oflag) {
|
|||||||
* Set the LFN filename block lname to a dir. Put the block based on sequence number
|
* Set the LFN filename block lname to a dir. Put the block based on sequence number
|
||||||
*/
|
*/
|
||||||
void SdBaseFile::setLFNName(vfat_t *pFatDir, char *lname, uint8_t sequenceNumber) {
|
void SdBaseFile::setLFNName(vfat_t *pFatDir, char *lname, uint8_t sequenceNumber) {
|
||||||
uint8_t startOffset = (sequenceNumber - 1) * FILENAME_LENGTH;
|
const uint8_t startOffset = (sequenceNumber - 1) * FILENAME_LENGTH,
|
||||||
uint8_t nameLength = strlen(lname);
|
nameLength = strlen(lname);
|
||||||
LOOP_L_N(i, FILENAME_LENGTH) {
|
LOOP_L_N(i, FILENAME_LENGTH) {
|
||||||
uint16_t ch = 0;
|
uint16_t ch = 0;
|
||||||
if ((startOffset + i) < nameLength)
|
if ((startOffset + i) < nameLength)
|
||||||
@ -1424,7 +1428,7 @@ int16_t SdBaseFile::read(void *buf, uint16_t nbyte) {
|
|||||||
* readDir() called before a directory has been opened, this is not
|
* readDir() called before a directory has been opened, this is not
|
||||||
* a directory file or an I/O error occurred.
|
* a directory file or an I/O error occurred.
|
||||||
*/
|
*/
|
||||||
int8_t SdBaseFile::readDir(dir_t *dir, char *longFilename) {
|
int8_t SdBaseFile::readDir(dir_t *dir, char * const longFilename) {
|
||||||
int16_t n;
|
int16_t n;
|
||||||
// if not a directory file or miss-positioned return an error
|
// if not a directory file or miss-positioned return an error
|
||||||
if (!isDir() || (0x1F & curPosition_)) return -1;
|
if (!isDir() || (0x1F & curPosition_)) return -1;
|
||||||
@ -1506,44 +1510,55 @@ int8_t SdBaseFile::readDir(dir_t *dir, char *longFilename) {
|
|||||||
// Post-process normal file or subdirectory longname, if any
|
// Post-process normal file or subdirectory longname, if any
|
||||||
if (DIR_IS_FILE_OR_SUBDIR(dir)) {
|
if (DIR_IS_FILE_OR_SUBDIR(dir)) {
|
||||||
#if ENABLED(UTF_FILENAME_SUPPORT)
|
#if ENABLED(UTF_FILENAME_SUPPORT)
|
||||||
#if LONG_FILENAME_CHARSIZE > 2
|
|
||||||
// Add warning for developers for unsupported 3-byte cases.
|
|
||||||
// (Converting 2-byte codepoints to 3-byte in-place would break the rest of filename.)
|
|
||||||
#error "Currently filename re-encoding is done in-place. It may break the remaining chars to use 3-byte codepoints."
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Is there a long filename to decode?
|
// Is there a long filename to decode?
|
||||||
if (longFilename) {
|
if (longFilename) {
|
||||||
// Reset n to the start of the long name
|
n = convertUtf16ToUtf8(longFilename);
|
||||||
n = 0;
|
}
|
||||||
for (uint16_t idx = 0; idx < (LONG_FILENAME_LENGTH); idx += 2) { // idx is fixed since FAT LFN always contains UTF-16LE encoding
|
|
||||||
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
|
|
||||||
longFilename[n++] = utf16_ch & 0x007F;
|
|
||||||
else if (0 == (utf16_ch & 0xF800)) { // Encode as 2-byte UTF-8 char
|
|
||||||
longFilename[n++] = 0xC0 | ((utf16_ch >> 6) & 0x1F);
|
|
||||||
longFilename[n++] = 0x80 | ( utf16_ch & 0x3F);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
#if LONG_FILENAME_CHARSIZE > 2 // Encode as 3-byte UTF-8 char
|
|
||||||
longFilename[n++] = 0xE0 | ((utf16_ch >> 12) & 0x0F);
|
|
||||||
longFilename[n++] = 0xC0 | ((utf16_ch >> 6) & 0x3F);
|
|
||||||
longFilename[n++] = 0xC0 | ( utf16_ch & 0x3F);
|
|
||||||
#else // Encode as '_'
|
|
||||||
longFilename[n++] = '_';
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
if (0 == utf16_ch) break; // End of filename
|
|
||||||
} // idx
|
|
||||||
} // longFilename
|
|
||||||
#endif
|
#endif
|
||||||
return n;
|
return n;
|
||||||
} // DIR_IS_FILE_OR_SUBDIR
|
} // DIR_IS_FILE_OR_SUBDIR
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ENABLED(UTF_FILENAME_SUPPORT)
|
||||||
|
|
||||||
|
uint8_t SdBaseFile::convertUtf16ToUtf8(char * const longFilename) {
|
||||||
|
#if LONG_FILENAME_CHARSIZE > 2
|
||||||
|
// Add warning for developers for unsupported 3-byte cases.
|
||||||
|
// (Converting 2-byte codepoints to 3-byte in-place would break the rest of filename.)
|
||||||
|
#error "Currently filename re-encoding is done in-place. It may break the remaining chars to use 3-byte codepoints."
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int16_t n;
|
||||||
|
// Reset n to the start of the long name
|
||||||
|
n = 0;
|
||||||
|
for (uint16_t idx = 0; idx < (LONG_FILENAME_LENGTH); idx += 2) { // idx is fixed since FAT LFN always contains UTF-16LE encoding
|
||||||
|
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
|
||||||
|
longFilename[n++] = utf16_ch & 0x007F;
|
||||||
|
else if (0 == (utf16_ch & 0xF800)) { // Encode as 2-byte UTF-8 char
|
||||||
|
longFilename[n++] = 0xC0 | ((utf16_ch >> 6) & 0x1F);
|
||||||
|
longFilename[n++] = 0x80 | ( utf16_ch & 0x3F);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
#if LONG_FILENAME_CHARSIZE > 2 // Encode as 3-byte UTF-8 char
|
||||||
|
longFilename[n++] = 0xE0 | ((utf16_ch >> 12) & 0x0F);
|
||||||
|
longFilename[n++] = 0xC0 | ((utf16_ch >> 6) & 0x3F);
|
||||||
|
longFilename[n++] = 0xC0 | ( utf16_ch & 0x3F);
|
||||||
|
#else // Encode as '_'
|
||||||
|
longFilename[n++] = '_';
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
if (0 == utf16_ch) break; // End of filename
|
||||||
|
} // idx
|
||||||
|
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // UTF_FILENAME_SUPPORT
|
||||||
|
|
||||||
// Read next directory entry into the cache
|
// Read next directory entry into the cache
|
||||||
// Assumes file is correctly positioned
|
// Assumes file is correctly positioned
|
||||||
dir_t* SdBaseFile::readDirCache() {
|
dir_t* SdBaseFile::readDirCache() {
|
||||||
|
@ -298,7 +298,7 @@ class SdBaseFile {
|
|||||||
bool printName();
|
bool printName();
|
||||||
int16_t read();
|
int16_t read();
|
||||||
int16_t read(void *buf, uint16_t nbyte);
|
int16_t read(void *buf, uint16_t nbyte);
|
||||||
int8_t readDir(dir_t *dir, char *longFilename);
|
int8_t readDir(dir_t *dir, char * const longFilename);
|
||||||
static bool remove(SdBaseFile *dirFile, const char *path);
|
static bool remove(SdBaseFile *dirFile, const char *path);
|
||||||
bool remove();
|
bool remove();
|
||||||
|
|
||||||
@ -392,14 +392,16 @@ class SdBaseFile {
|
|||||||
bool openCachedEntry(uint8_t cacheIndex, uint8_t oflags);
|
bool openCachedEntry(uint8_t cacheIndex, uint8_t oflags);
|
||||||
dir_t* readDirCache();
|
dir_t* readDirCache();
|
||||||
|
|
||||||
|
#if ENABLED(UTF_FILENAME_SUPPORT)
|
||||||
|
uint8_t convertUtf16ToUtf8(char * const longFilename);
|
||||||
|
#endif
|
||||||
|
|
||||||
// Long Filename create/write support
|
// Long Filename create/write support
|
||||||
#if ENABLED(LONG_FILENAME_WRITE_SUPPORT)
|
#if ENABLED(LONG_FILENAME_WRITE_SUPPORT)
|
||||||
static bool isDirLFN(const dir_t* dir);
|
static bool isDirLFN(const dir_t* dir);
|
||||||
static bool isDirNameLFN(const char *dirname);
|
static bool isDirNameLFN(const char *dirname);
|
||||||
static bool parsePath(const char *str, uint8_t *name, uint8_t *lname, const char **ptr);
|
static bool parsePath(const char *str, uint8_t *name, uint8_t *lname, const char **ptr);
|
||||||
/**
|
// Return the number of entries needed in the FAT for this LFN
|
||||||
* Return the number of entries needed in the FAT for this LFN
|
|
||||||
*/
|
|
||||||
static inline uint8_t getLFNEntriesNum(const char *lname) { return (strlen(lname) + 12) / 13; }
|
static inline uint8_t getLFNEntriesNum(const char *lname) { return (strlen(lname) + 12) / 13; }
|
||||||
static void getLFNName(vfat_t *vFatDir, char *lname, uint8_t startOffset);
|
static void getLFNName(vfat_t *vFatDir, char *lname, uint8_t startOffset);
|
||||||
static void setLFNName(vfat_t *vFatDir, char *lname, uint8_t lfnSequenceNumber);
|
static void setLFNName(vfat_t *vFatDir, char *lname, uint8_t lfnSequenceNumber);
|
||||||
|
Loading…
Reference in New Issue
Block a user