* Read the next entry in a directory. * * \param[out] dir The dir_t struct that will receive the data. * * \return For success return a non-zero value (number of bytes read). * A value of zero will be returned if end of dir is reached. * If an error occurs, readDir() returns -1. Possible errors: * - readDir() called on unopened dir * - not a directory fi
| 1429 | * - I/O error |
| 1430 | */ |
| 1431 | int8_t SdBaseFile::readDir(dir_t * const dir, char * const longFilename) { |
| 1432 | int16_t n; |
| 1433 | // if not a directory file or miss-positioned return an error |
| 1434 | if (!isDir() || (0x1F & curPosition_)) return -1; |
| 1435 | |
| 1436 | #define INVALIDATE_LONGNAME() (longFilename[0] = longFilename[1] = '\0') |
| 1437 | |
| 1438 | // If we have a longFilename buffer, mark it as invalid. |
| 1439 | // If a long filename is found it will be filled automatically. |
| 1440 | if (longFilename) INVALIDATE_LONGNAME(); |
| 1441 | |
| 1442 | uint8_t checksum_error = 0xFF, checksum = 0; |
| 1443 | |
| 1444 | while (1) { |
| 1445 | |
| 1446 | n = read(dir, sizeof(dir_t)); |
| 1447 | if (n != sizeof(dir_t)) return n ? -1 : 0; |
| 1448 | |
| 1449 | // Last entry if DIR_NAME_FREE |
| 1450 | if (dir->name[0] == DIR_NAME_FREE) return 0; |
| 1451 | |
| 1452 | // Skip deleted entry and entry for . and .. |
| 1453 | if (dir->name[0] == DIR_NAME_DELETED || dir->name[0] == '.') { |
| 1454 | if (longFilename) INVALIDATE_LONGNAME(); // Invalidate erased file long name, if any |
| 1455 | continue; |
| 1456 | } |
| 1457 | |
| 1458 | if (longFilename) { |
| 1459 | // Fill the long filename if we have a long filename entry. |
| 1460 | // Long filename entries are stored before the short filename. |
| 1461 | if (DIR_IS_LONG_NAME(dir)) { |
| 1462 | vfat_t *VFAT = (vfat_t*)dir; |
| 1463 | // Sanity-check the VFAT entry. The first cluster is always set to zero. And the sequence number should be higher than 0 |
| 1464 | if (VFAT->firstClusterLow == 0) { |
| 1465 | const uint8_t seq = VFAT->sequenceNumber & 0x1F; |
| 1466 | if (WITHIN(seq, 1, VFAT_ENTRIES_LIMIT)) { |
| 1467 | if (seq == 1) { |
| 1468 | checksum = VFAT->checksum; |
| 1469 | checksum_error = 0; |
| 1470 | } |
| 1471 | else if (checksum != VFAT->checksum) // orphan detected |
| 1472 | checksum_error = 1; |
| 1473 | |
| 1474 | #if ENABLED(LONG_FILENAME_WRITE_SUPPORT) |
| 1475 | |
| 1476 | getLFNName(VFAT, longFilename, seq); // Get chunk of LFN from VFAT entry |
| 1477 | |
| 1478 | #else // !LONG_FILENAME_WRITE_SUPPORT |
| 1479 | |
| 1480 | n = (seq - 1) * (FILENAME_LENGTH); |
| 1481 | |
| 1482 | for (uint8_t i = 0; i < FILENAME_LENGTH; ++i) { |
| 1483 | const uint16_t utf16_ch = (i >= 11) ? VFAT->name3[i - 11] : (i >= 5) ? VFAT->name2[i - 5] : VFAT->name1[i]; |
| 1484 | #if ENABLED(UTF_FILENAME_SUPPORT) |
| 1485 | // We can't reconvert to UTF-8 here as UTF-8 is variable-size encoding, but joining LFN blocks |
| 1486 | // needs static bytes addressing. So here just store full UTF-16LE words to re-convert later. |
| 1487 | uint16_t idx = (n + i) * 2; // This is fixed as FAT LFN always contain UTF-16LE encoding |
| 1488 | longFilename[idx] = utf16_ch & 0xFF; |
no test coverage detected