Bill Greiman 3 лет назад
Родитель
Сommit
a5e4bdeb76
100 измененных файлов с 2212 добавлено и 1206 удалено
  1. 12 0
      README.md
  2. BIN
      doc/html.zip
  3. 5 3
      examples/AvrAdcLogger/AvrAdcLogger.ino
  4. 5 0
      examples/BackwardCompatibility/BackwardCompatibility.ino
  5. 6 3
      examples/BufferedPrint/BufferedPrint.ino
  6. 7 5
      examples/DirectoryFunctions/DirectoryFunctions.ino
  7. 12 10
      examples/ExFatLogger/ExFatLogger.ino
  8. 0 47
      examples/ExFatUnicodeTest/ExFatUnicodeTest.ino
  9. 6 3
      examples/OpenNext/OpenNext.ino
  10. 6 3
      examples/ReadCsvFile/ReadCsvFile.ino
  11. 6 3
      examples/RtcTimestampTest/RtcTimestampTest.ino
  12. 3 2
      examples/STM32Test/STM32Test.ino
  13. 6 3
      examples/SdFormatter/SdFormatter.ino
  14. 6 3
      examples/TeensyRtcTimestamp/TeensyRtcTimestamp.ino
  15. 98 0
      examples/UnicodeFilenames/UnicodeFilenames.ino
  16. 3 3
      examples/UserChipSelectFunction/UserChipSelectFunction.ino
  17. 1 1
      examples/examplesV1/#attic/AnalogLogger/AnalogLogger.ino
  18. 2 2
      examples/examplesV1/#attic/BaseExtCaseTest/BaseExtCaseTest.ino
  19. 1 1
      examples/examplesV1/#attic/HelloWorld/HelloWorld.ino
  20. 3 3
      examples/examplesV1/#attic/PrintBenchmarkSD/PrintBenchmarkSD.ino
  21. 2 2
      examples/examplesV1/#attic/SD_Size/SD_Size.ino
  22. 3 3
      examples/examplesV1/#attic/SdFatSize/SdFatSize.ino
  23. 1 1
      examples/examplesV1/#attic/StreamParseInt/StreamParseInt.ino
  24. 2 2
      examples/examplesV1/#attic/append/append.ino
  25. 2 2
      examples/examplesV1/#attic/average/average.ino
  26. 3 3
      examples/examplesV1/#attic/benchSD/benchSD.ino
  27. 2 2
      examples/examplesV1/#attic/bufstream/bufstream.ino
  28. 1 1
      examples/examplesV1/#attic/cin_cout/cin_cout.ino
  29. 1 1
      examples/examplesV1/#attic/eventlog/eventlog.ino
  30. 2 2
      examples/examplesV1/#attic/fgetsRewrite/fgetsRewrite.ino
  31. 2 2
      examples/examplesV1/#attic/readlog/readlog.ino
  32. 4 4
      examples/examplesV1/LowLatencyLoggerADXL345/UserFunctions.cpp
  33. 2 2
      examples/examplesV1/LowLatencyLoggerMPU6050/UserFunctions.cpp
  34. 3 3
      examples/examplesV1/OpenNext/OpenNext.ino
  35. 1 1
      examples/examplesV1/PrintBenchmark/PrintBenchmark.ino
  36. 4 4
      examples/examplesV1/RawWrite/RawWrite.ino
  37. 7 7
      examples/examplesV1/ReadCsv/ReadCsv.ino
  38. 5 5
      examples/examplesV1/ReadCsvArray/ReadCsvArray.ino
  39. 3 3
      examples/examplesV1/ReadCsvStream/ReadCsvStream.ino
  40. 2 2
      examples/examplesV1/STM32Test/STM32Test.ino
  41. 7 7
      examples/examplesV1/SdFormatter/SdFormatter.ino
  42. 6 6
      examples/examplesV1/SdInfo/SdInfo.ino
  43. 2 2
      examples/examplesV1/SoftwareSpi/SoftwareSpi.ino
  44. 3 3
      examples/examplesV1/StdioBench/StdioBench.ino
  45. 12 12
      examples/examplesV1/TeensySdioDemo/TeensySdioDemo.ino
  46. 2 2
      examples/examplesV1/Timestamp/Timestamp.ino
  47. 2 2
      examples/examplesV1/TwoCards/TwoCards.ino
  48. 3 3
      examples/examplesV1/dataLogger/dataLogger.ino
  49. 2 2
      examples/examplesV1/fgets/fgets.ino
  50. 1 1
      examples/examplesV1/formatting/formatting.ino
  51. 2 2
      examples/examplesV1/getline/getline.ino
  52. 3 3
      examples/examplesV1/wipe/wipe.ino
  53. 5 3
      examples/rename/rename.ino
  54. 1 1
      library.properties
  55. 3 4
      src/ExFatLib/ExFatConfig.h
  56. 25 5
      src/ExFatLib/ExFatDbg.cpp
  57. 118 166
      src/ExFatLib/ExFatFile.cpp
  58. 94 80
      src/ExFatLib/ExFatFile.h
  59. 57 10
      src/ExFatLib/ExFatFilePrint.cpp
  60. 15 40
      src/ExFatLib/ExFatFileWrite.cpp
  61. 3 1
      src/ExFatLib/ExFatFormatter.cpp
  62. 0 1
      src/ExFatLib/ExFatFormatter.h
  63. 189 0
      src/ExFatLib/ExFatName.cpp
  64. 4 7
      src/ExFatLib/ExFatPartition.cpp
  65. 2 3
      src/ExFatLib/ExFatPartition.h
  66. 15 16
      src/ExFatLib/ExFatTypes.h
  67. 5 1
      src/ExFatLib/ExFatVolume.cpp
  68. 10 20
      src/ExFatLib/ExFatVolume.h
  69. 63 11
      src/FatLib/FatDbg.cpp
  70. 32 29
      src/FatLib/FatFile.cpp
  71. 80 29
      src/FatLib/FatFile.h
  72. 230 338
      src/FatLib/FatFileLFN.cpp
  73. 54 122
      src/FatLib/FatFileSFN.cpp
  74. 1 1
      src/FatLib/FatFormatter.cpp
  75. 0 1
      src/FatLib/FatLib.h
  76. 354 0
      src/FatLib/FatName.cpp
  77. 6 5
      src/FatLib/FatPartition.h
  78. 4 0
      src/FatLib/FatVolume.cpp
  79. 4 7
      src/FsLib/FsFile.h
  80. 1 2
      src/FsLib/FsVolume.h
  81. 11 3
      src/RingBuf.h
  82. 5 9
      src/SdCard/SdSpiCard.cpp
  83. 3 3
      src/SdCard/SdSpiCard.h
  84. 1 2
      src/SdCard/SdioCard.h
  85. 3 0
      src/SdCard/SdioTeensy.cpp
  86. 22 13
      src/SdFat.h
  87. 128 43
      src/SdFatConfig.h
  88. 1 1
      src/SpiDriver/SdSpiArtemis.cpp
  89. 43 9
      src/SpiDriver/SdSpiAvr.h
  90. 3 3
      src/SpiDriver/SdSpiLibDriver.h
  91. 1 0
      src/SpiDriver/SdSpiSTM32.cpp
  92. 71 0
      src/SpiDriver/SdSpiSTM32Core.cpp
  93. 4 1
      src/common/ArduinoFiles.h
  94. 1 1
      src/common/BlockDeviceInterface.h
  95. 30 9
      src/common/DebugMacros.h
  96. 1 1
      src/common/FsApiConstants.h
  97. 30 8
      src/common/FsName.cpp
  98. 66 0
      src/common/FsName.h
  99. 13 5
      src/common/FsStructs.h
  100. 115 0
      src/common/FsUtf.cpp

+ 12 - 0
README.md

@@ -4,6 +4,18 @@ Earlier releases of Version 1 are here:
 
 https://github.com/greiman/SdFat/releases
 
+###### UTF-8 encoded filenames are supported in v2.1.0.
+
+Try the UnicodeFilenames example.  Here is output from ls:
+<pre>
+Type any character to begin
+ls:
+         0 😀/
+          20 россиянин
+          17 très élégant
+           9 狗.txt
+</pre>
+
 SdFat Version 2 supports FAT16/FAT32 and exFAT SD cards. It is mostly
 backward compatible with SdFat Version 1 for FAT16/FAT32 cards.
 


+ 5 - 3
examples/AvrAdcLogger/AvrAdcLogger.ino

@@ -152,11 +152,13 @@ const uint16_t ISR_TIMER0 = 160;
 //==============================================================================
 const uint32_t MAX_FILE_SIZE = MAX_FILE_SIZE_MiB << 20;
 
-// Select fastest interface.
+// Select fastest interface.  Max SPI rate for AVR is 10 MHx.
+#define SPI_CLOCK SD_SCK_MHZ(10)
+
 #if ENABLE_DEDICATED_SPI
-#define SD_CONFIG SdSpiConfig(SD_CS_PIN, DEDICATED_SPI)
+#define SD_CONFIG SdSpiConfig(SD_CS_PIN, DEDICATED_SPI, SPI_CLOCK)
 #else  // ENABLE_DEDICATED_SPI
-#define SD_CONFIG SdSpiConfig(SD_CS_PIN, SHARED_SPI)
+#define SD_CONFIG SdSpiConfig(SD_CS_PIN, SHARED_SPI, SPI_CLOCK)
 #endif  // ENABLE_DEDICATED_SPI
 
 #if SD_FAT_TYPE == 0

+ 5 - 0
examples/BackwardCompatibility/BackwardCompatibility.ino

@@ -3,6 +3,11 @@
 //
 // Your SD must be formatted FAT16/FAT32.
 //
+// SD.h does not support some default SdFat features.
+// To compare flash size, set USE_FAT_FILE_FLAG_CONTIGUOUS,
+// ENABLE_DEDICATED_SPI, and USE_LONG_FILE_NAMES to zero also
+// set SDFAT_FILE_TYPE to one in SdFat/src/SdFatCongfig.h
+//
 // Set USE_SD_H nonzero to use SD.h.
 // Set USE_SD_H zero to use SdFat.h.
 //

+ 6 - 3
examples/BufferedPrint/BufferedPrint.ino

@@ -24,13 +24,16 @@ const uint8_t SD_CS_PIN = SS;
 const uint8_t SD_CS_PIN = SDCARD_SS_PIN;
 #endif  // SDCARD_SS_PIN
 
+// Try max SPI clock for an SD. Reduce SPI_CLOCK if errors occur.
+#define SPI_CLOCK SD_SCK_MHZ(50)
+
 // Try to select the best SD card configuration.
 #if HAS_SDIO_CLASS
 #define SD_CONFIG SdioConfig(FIFO_SDIO)
-#elif ENABLE_DEDICATED_SPI
-#define SD_CONFIG SdSpiConfig(SD_CS_PIN, DEDICATED_SPI)
+#elif  ENABLE_DEDICATED_SPI
+#define SD_CONFIG SdSpiConfig(SD_CS_PIN, DEDICATED_SPI, SPI_CLOCK)
 #else  // HAS_SDIO_CLASS
-#define SD_CONFIG SdSpiConfig(SD_CS_PIN, SHARED_SPI)
+#define SD_CONFIG SdSpiConfig(SD_CS_PIN, SHARED_SPI, SPI_CLOCK)
 #endif  // HAS_SDIO_CLASS
 
 #if SD_FAT_TYPE == 0

+ 7 - 5
examples/DirectoryFunctions/DirectoryFunctions.ino

@@ -24,13 +24,16 @@ const uint8_t SD_CS_PIN = SS;
 const uint8_t SD_CS_PIN = SDCARD_SS_PIN;
 #endif  // SDCARD_SS_PIN
 
+// Try max SPI clock for an SD. Reduce SPI_CLOCK if errors occur.
+#define SPI_CLOCK SD_SCK_MHZ(50)
+
 // Try to select the best SD card configuration.
 #if HAS_SDIO_CLASS
 #define SD_CONFIG SdioConfig(FIFO_SDIO)
-#elif ENABLE_DEDICATED_SPI
-#define SD_CONFIG SdSpiConfig(SD_CS_PIN, DEDICATED_SPI)
+#elif  ENABLE_DEDICATED_SPI
+#define SD_CONFIG SdSpiConfig(SD_CS_PIN, DEDICATED_SPI, SPI_CLOCK)
 #else  // HAS_SDIO_CLASS
-#define SD_CONFIG SdSpiConfig(SD_CS_PIN, SHARED_SPI)
+#define SD_CONFIG SdSpiConfig(SD_CS_PIN, SHARED_SPI, SPI_CLOCK)
 #endif  // HAS_SDIO_CLASS
 //------------------------------------------------------------------------------
 
@@ -66,7 +69,6 @@ void setup() {
     SysCall::yield();
   }
   delay(1000);
-
   cout << F("Type any character to start\n");
   while (!Serial.available()) {
     SysCall::yield();
@@ -153,4 +155,4 @@ void setup() {
 }
 //------------------------------------------------------------------------------
 // Nothing happens in loop.
-void loop() {}
+void loop() {}

+ 12 - 10
examples/ExFatLogger/ExFatLogger.ino

@@ -1,9 +1,6 @@
 // Example to demonstrate write latency for preallocated exFAT files.
 // I suggest you write a PC program to convert very large bin files.
 //
-// If an exFAT SD is required, the ExFatFormatter example will format
-// smaller cards with an exFAT file system.
-//
 // The maximum data rate will depend on the quality of your SD,
 // the size of the FIFO, and using dedicated SPI.
 #include "SdFat.h"
@@ -68,12 +65,17 @@ const uint8_t SD_CS_PIN = SDCARD_SS_PIN;
 // Preallocate 1GiB file.
 const uint32_t PREALLOCATE_SIZE_MiB = 1024UL;
 
-// Select the fastest interface. Assumes no other SPI devices.
-#if ENABLE_DEDICATED_SPI
-#define SD_CONFIG SdSpiConfig(SD_CS_PIN, DEDICATED_SPI)
-#else  // ENABLE_DEDICATED_SPI
-#define SD_CONFIG SdSpiConfig(SD_CS_PIN, SHARED_SPI)
-#endif  // ENABLE_DEDICATED_SPI
+// Try max SPI clock for an SD. Reduce SPI_CLOCK if errors occur.
+#define SPI_CLOCK SD_SCK_MHZ(50)
+
+// Try to select the best SD card configuration.
+#if HAS_SDIO_CLASS
+#define SD_CONFIG SdioConfig(FIFO_SDIO)
+#elif  ENABLE_DEDICATED_SPI
+#define SD_CONFIG SdSpiConfig(SD_CS_PIN, DEDICATED_SPI, SPI_CLOCK)
+#else  // HAS_SDIO_CLASS
+#define SD_CONFIG SdSpiConfig(SD_CS_PIN, SHARED_SPI, SPI_CLOCK)
+#endif  // HAS_SDIO_CLASS
 
 // Save SRAM if 328.
 #ifdef __AVR_ATmega328P__
@@ -189,7 +191,7 @@ void binaryToCsv() {
   data_t binData[FIFO_DIM];
 
   if (!binFile.seekSet(512)) {
-	  error("binFile.seek faile");
+	  error("binFile.seek failed");
   }
   uint32_t tPct = millis();
   printRecord(&csvFile, nullptr);

+ 0 - 47
examples/ExFatUnicodeTest/ExFatUnicodeTest.ino

@@ -1,47 +0,0 @@
-// Simple test of Unicode file name.
-// Note: Unicode is only supported by the SdExFat class.
-//       No exFAT functions will be defined for char* paths.
-//       The SdFs class cannot be used.
-#include "SdFat.h"
-#if USE_EXFAT_UNICODE_NAMES
-
-// SDCARD_SS_PIN is defined for the built-in SD on some boards.
-#ifndef SDCARD_SS_PIN
-const uint8_t SD_CS_PIN = SS;
-#else  // SDCARD_SS_PIN
-// Assume built-in SD is used.
-const uint8_t SD_CS_PIN = SDCARD_SS_PIN;
-#endif  // SDCARD_SS_PIN
-
-// Use SPI, SD_CS_PIN, SHARED_SPI, 50 MHz.
-#define SD_CONFIG SdSpiConfig(SD_CS_PIN)
-
-SdExFat sd;
-
-ExFile file;
-
-void setup() {
-  Serial.begin(9600);
-  while (!Serial) {
-    yield();
-  }
-  Serial.println("Type any character to begin");
-  while (!Serial.available()) {
-    yield();
-  }
-  if (!sd.begin(SD_CONFIG)) {
-    sd.initErrorHalt(&Serial);
-  }
-  if (!file.open(u"Euros \u20AC test.txt", FILE_WRITE)) {
-    Serial.println("file.open failed");
-    return;
-  }
-  file.println("This is not Unicode");
-  file.close();
-  Serial.println("Done!");
-}
-void loop() {
-}
-#else  // USE_EXFAT_UNICODE_NAMES
-#error USE_EXFAT_UNICODE_NAMES must be nonzero in SdFat/src/ExFatLib/ExFatCongfig.h
-#endif  // USE_EXFAT_UNICODE_NAMES

+ 6 - 3
examples/OpenNext/OpenNext.ino

@@ -23,13 +23,16 @@ const uint8_t SD_CS_PIN = SS;
 const uint8_t SD_CS_PIN = SDCARD_SS_PIN;
 #endif  // SDCARD_SS_PIN
 
+// Try max SPI clock for an SD. Reduce SPI_CLOCK if errors occur.
+#define SPI_CLOCK SD_SCK_MHZ(50)
+
 // Try to select the best SD card configuration.
 #if HAS_SDIO_CLASS
 #define SD_CONFIG SdioConfig(FIFO_SDIO)
-#elif ENABLE_DEDICATED_SPI
-#define SD_CONFIG SdSpiConfig(SD_CS_PIN, DEDICATED_SPI)
+#elif  ENABLE_DEDICATED_SPI
+#define SD_CONFIG SdSpiConfig(SD_CS_PIN, DEDICATED_SPI, SPI_CLOCK)
 #else  // HAS_SDIO_CLASS
-#define SD_CONFIG SdSpiConfig(SD_CS_PIN, SHARED_SPI)
+#define SD_CONFIG SdSpiConfig(SD_CS_PIN, SHARED_SPI, SPI_CLOCK)
 #endif  // HAS_SDIO_CLASS
 
 #if SD_FAT_TYPE == 0

+ 6 - 3
examples/ReadCsvFile/ReadCsvFile.ino

@@ -20,13 +20,16 @@ const uint8_t SD_CS_PIN = SS;
 const uint8_t SD_CS_PIN = SDCARD_SS_PIN;
 #endif  // SDCARD_SS_PIN
 
+// Try max SPI clock for an SD. Reduce SPI_CLOCK if errors occur.
+#define SPI_CLOCK SD_SCK_MHZ(50)
+
 // Try to select the best SD card configuration.
 #if HAS_SDIO_CLASS
 #define SD_CONFIG SdioConfig(FIFO_SDIO)
-#elif ENABLE_DEDICATED_SPI
-#define SD_CONFIG SdSpiConfig(SD_CS_PIN, DEDICATED_SPI)
+#elif  ENABLE_DEDICATED_SPI
+#define SD_CONFIG SdSpiConfig(SD_CS_PIN, DEDICATED_SPI, SPI_CLOCK)
 #else  // HAS_SDIO_CLASS
-#define SD_CONFIG SdSpiConfig(SD_CS_PIN, SHARED_SPI)
+#define SD_CONFIG SdSpiConfig(SD_CS_PIN, SHARED_SPI, SPI_CLOCK)
 #endif  // HAS_SDIO_CLASS
 
 #if SD_FAT_TYPE == 0

+ 6 - 3
examples/RtcTimestampTest/RtcTimestampTest.ino

@@ -31,13 +31,16 @@ const uint8_t SD_CS_PIN = SS;
 const uint8_t SD_CS_PIN = SDCARD_SS_PIN;
 #endif  // SDCARD_SS_PIN
 
+// Try max SPI clock for an SD. Reduce SPI_CLOCK if errors occur.
+#define SPI_CLOCK SD_SCK_MHZ(50)
+
 // Try to select the best SD card configuration.
 #if HAS_SDIO_CLASS
 #define SD_CONFIG SdioConfig(FIFO_SDIO)
-#elif ENABLE_DEDICATED_SPI
-#define SD_CONFIG SdSpiConfig(SD_CS_PIN, DEDICATED_SPI)
+#elif  ENABLE_DEDICATED_SPI
+#define SD_CONFIG SdSpiConfig(SD_CS_PIN, DEDICATED_SPI, SPI_CLOCK)
 #else  // HAS_SDIO_CLASS
-#define SD_CONFIG SdSpiConfig(SD_CS_PIN, SHARED_SPI)
+#define SD_CONFIG SdSpiConfig(SD_CS_PIN, SHARED_SPI, SPI_CLOCK)
 #endif  // HAS_SDIO_CLASS
 
 #if SD_FAT_TYPE == 0

+ 3 - 2
examples/STM32Test/STM32Test.ino

@@ -1,4 +1,5 @@
-/*
+/* This example is for https://github.com/rogerclarkmelbourne/Arduino_STM32
+ *
  * Example use of two SPI ports on an STM32 board.
  * Note SPI speed is limited to 18 MHz.
  */
@@ -13,7 +14,7 @@ FsFile file1;
 
 // Use mySPI2 since SPI2 is used in SPI.h as a different type.
 static SPIClass mySPI2(2);
-// Chip select PB21, dedicated SPI, 18 MHz, port 2.
+// Chip select PB12, dedicated SPI, 18 MHz, port 2.
 #if ENABLE_DEDICATED_SPI
 #define SD2_CONFIG SdSpiConfig(PB12, DEDICATED_SPI, SD_SCK_MHZ(18), &mySPI2)
 #else  // ENABLE_DEDICATED_SPI

+ 6 - 3
examples/SdFormatter/SdFormatter.ino

@@ -36,13 +36,16 @@ const uint8_t SD_CS_PIN = SS;
 const uint8_t SD_CS_PIN = SDCARD_SS_PIN;
 #endif  // SDCARD_SS_PIN
 
+// Try max SPI clock for an SD. Reduce SPI_CLOCK if errors occur.
+#define SPI_CLOCK SD_SCK_MHZ(50)
+
 // Try to select the best SD card configuration.
 #if HAS_SDIO_CLASS
 #define SD_CONFIG SdioConfig(FIFO_SDIO)
-#elif ENABLE_DEDICATED_SPI
-#define SD_CONFIG SdSpiConfig(SD_CS_PIN, DEDICATED_SPI)
+#elif  ENABLE_DEDICATED_SPI
+#define SD_CONFIG SdSpiConfig(SD_CS_PIN, DEDICATED_SPI, SPI_CLOCK)
 #else  // HAS_SDIO_CLASS
-#define SD_CONFIG SdSpiConfig(SD_CS_PIN, SHARED_SPI)
+#define SD_CONFIG SdSpiConfig(SD_CS_PIN, SHARED_SPI, SPI_CLOCK)
 #endif  // HAS_SDIO_CLASS
 //==============================================================================
 // Serial output stream

+ 6 - 3
examples/TeensyRtcTimestamp/TeensyRtcTimestamp.ino

@@ -24,13 +24,16 @@ const uint8_t SD_CS_PIN = SS;
 const uint8_t SD_CS_PIN = SDCARD_SS_PIN;
 #endif  // SDCARD_SS_PIN
 
+// Try max SPI clock for an SD. Reduce SPI_CLOCK if errors occur.
+#define SPI_CLOCK SD_SCK_MHZ(50)
+
 // Try to select the best SD card configuration.
 #if HAS_SDIO_CLASS
 #define SD_CONFIG SdioConfig(FIFO_SDIO)
-#elif ENABLE_DEDICATED_SPI
-#define SD_CONFIG SdSpiConfig(SD_CS_PIN, DEDICATED_SPI)
+#elif  ENABLE_DEDICATED_SPI
+#define SD_CONFIG SdSpiConfig(SD_CS_PIN, DEDICATED_SPI, SPI_CLOCK)
 #else  // HAS_SDIO_CLASS
-#define SD_CONFIG SdSpiConfig(SD_CS_PIN, SHARED_SPI)
+#define SD_CONFIG SdSpiConfig(SD_CS_PIN, SHARED_SPI, SPI_CLOCK)
 #endif  // HAS_SDIO_CLASS
 
 #if SD_FAT_TYPE == 0

+ 98 - 0
examples/UnicodeFilenames/UnicodeFilenames.ino

@@ -0,0 +1,98 @@
+// Simple test of Unicode filename.
+// Unicode is supported as UTF-8 encoded strings.
+#include "SdFat.h"
+
+// USE_UTF8_LONG_NAMES must be non-zero in SdFat/src/SdFatCongfig.h
+#if USE_UTF8_LONG_NAMES
+
+#define UTF8_FOLDER u8"😀"
+const char* names[] = {u8"россиянин", u8"très élégant", u8"狗.txt", nullptr};
+
+// Remove files if non-zero.
+#define REMOVE_UTF8_FILES 1
+
+// SD_FAT_TYPE = 0 for SdFat/File as defined in SdFatConfig.h,
+// 1 for FAT16/FAT32, 2 for exFAT, 3 for FAT16/FAT32 and exFAT.
+#define SD_FAT_TYPE 0
+
+// SDCARD_SS_PIN is defined for the built-in SD on some boards.
+#ifndef SDCARD_SS_PIN
+const uint8_t SD_CS_PIN = SS;
+#else  // SDCARD_SS_PIN
+// Assume built-in SD is used.
+const uint8_t SD_CS_PIN = SDCARD_SS_PIN;
+#endif  // SDCARD_SS_PIN
+
+// Try to select the best SD card configuration.
+#if HAS_SDIO_CLASS
+#define SD_CONFIG SdioConfig(FIFO_SDIO)
+#elif ENABLE_DEDICATED_SPI
+#define SD_CONFIG SdSpiConfig(SD_CS_PIN, DEDICATED_SPI, SD_SCK_MHZ(16))
+#else  // HAS_SDIO_CLASS
+#define SD_CONFIG SdSpiConfig(SD_CS_PIN, SHARED_SPI, SD_SCK_MHZ(16))
+#endif  // HAS_SDIO_CLASS
+
+#if SD_FAT_TYPE == 0
+SdFat sd;
+File file;
+#elif SD_FAT_TYPE == 1
+SdFat32 sd;
+File32 file;
+#elif SD_FAT_TYPE == 2
+SdExFat sd;
+ExFile file;
+#elif SD_FAT_TYPE == 3
+SdFs sd;
+FsFile file;
+#else  // SD_FAT_TYPE
+#error Invalid SD_FAT_TYPE
+#endif  // SD_FAT_TYPE
+
+void setup() {
+  Serial.begin(9600);
+  while (!Serial) {
+    yield();
+  }
+  Serial.println("Type any character to begin");
+  while (!Serial.available()) {
+    yield();
+  }
+  if (!sd.begin(SD_CONFIG)) {
+    sd.initErrorHalt(&Serial);
+  }
+  if (!sd.exists(UTF8_FOLDER)) {
+    if (!sd.mkdir(UTF8_FOLDER)) {
+      Serial.println("sd.mkdir failed");
+      return;
+    }
+  }
+  if (!sd.chdir(UTF8_FOLDER)) {
+    Serial.println("sd.chdir failed");
+    return;
+  }
+  for (uint8_t i = 0; names[i]; i++) {
+    if (!file.open(names[i], O_WRONLY | O_CREAT)) {
+      Serial.println("file.open failed");
+      return;
+    }
+    file.println(names[i]);
+    file.close();
+  }
+  Serial.println("ls:");
+  sd.ls("/", LS_SIZE | LS_R);
+#if REMOVE_UTF8_FILES  // For debug test of remove and rmdir.
+  for (uint8_t i = 0; names[i]; i++) {
+    sd.remove(names[i]);
+  }
+  sd.chdir();
+  sd.rmdir(UTF8_FOLDER);
+  Serial.println("After remove and rmdir");
+  sd.ls(LS_SIZE | LS_R);
+#endif  // REMOVE_UTF8_FILES
+  Serial.println("Done!");
+}
+void loop() {
+}
+#else  // USE_UTF8_LONG_NAMES
+#error USE_UTF8_LONG_NAMES must be non-zero in SdFat/src/SdFatCongfig.h
+#endif  // USE_UTF8_LONG_NAMES

+ 3 - 3
examples/UserChipSelectFunction/UserChipSelectFunction.ino

@@ -5,7 +5,7 @@
 
 // SD_CHIP_SELECT_MODE must be set to one or two in SdFat/SdFatConfig.h.
 // A value of one allows optional replacement and two requires replacement.
-#if SD_CHIP_SELECT_MODE == 1 || SD_CHIP_SELECT_MODE == 2
+#if SD_CHIP_SELECT_MODE == 1 || SD_CHIP_SELECT_MODE == 2  
 
 // SD chip select pin.
 #define SD_CS_PIN SS
@@ -34,11 +34,11 @@ void setup() {
     sd.initErrorHalt(&Serial);
   }
   sd.ls(&Serial, LS_SIZE);
-
+  
   Serial.print(F("sdCsInit calls: "));
   Serial.println(initCalls);
   Serial.print(F("sdCsWrite calls: "));
-  Serial.println(writeCalls);
+  Serial.println(writeCalls); 
 }
 //------------------------------------------------------------------------------
 void loop() {}

+ 1 - 1
examples/examplesV1/#attic/AnalogLogger/AnalogLogger.ino

@@ -66,7 +66,7 @@ ostream& operator << (ostream& os, DateTime& dt) {
 //------------------------------------------------------------------------------
 void setup() {
   Serial.begin(9600);
-
+  
   // Wait for USB Serial.
   while (!Serial) {
     SysCall::yield();

+ 2 - 2
examples/examplesV1/#attic/BaseExtCaseTest/BaseExtCaseTest.ino

@@ -17,8 +17,8 @@ const char* name[] = {
 //------------------------------------------------------------------------------
 void setup() {
   Serial.begin(9600);
-
-  // Wait for USB Serial
+  
+  // Wait for USB Serial 
   while (!Serial) {
     SysCall::yield();
   }

+ 1 - 1
examples/examplesV1/#attic/HelloWorld/HelloWorld.ino

@@ -8,7 +8,7 @@ ArduinoOutStream cout(Serial);
 void setup() {
   Serial.begin(9600);
 
-  // Wait for USB Serial
+  // Wait for USB Serial 
   while (!Serial) {
     SysCall::yield();
   }

+ 3 - 3
examples/examplesV1/#attic/PrintBenchmarkSD/PrintBenchmarkSD.ino

@@ -24,8 +24,8 @@ void error(const char* s) {
 //------------------------------------------------------------------------------
 void setup() {
   Serial.begin(9600);
-
-  // Wait for USB Serial
+  
+  // Wait for USB Serial 
   while (!Serial) {
     yield();
   }
@@ -51,7 +51,7 @@ void loop() {
   if (!SD.begin(chipSelect)) {
     error("begin");
   }
-
+  
   Serial.println(F("Starting print test.  Please wait.\n"));
 
   // do write test

+ 2 - 2
examples/examplesV1/#attic/SD_Size/SD_Size.ino

@@ -9,8 +9,8 @@ File file;
 //------------------------------------------------------------------------------
 void setup() {
   Serial.begin(9600);
-
-  // Wait for USB Serial
+  
+  // Wait for USB Serial 
   while (!Serial) {
     yield();
   }

+ 3 - 3
examples/examplesV1/#attic/SdFatSize/SdFatSize.ino

@@ -12,12 +12,12 @@ SdFile file;
 //------------------------------------------------------------------------------
 void setup() {
   Serial.begin(9600);
-
-  // Wait for USB Serial
+  
+  // Wait for USB Serial 
   while (!Serial) {
     SysCall::yield();
   }
-
+  
   if (!sd.begin()) {
     Serial.println("begin failed");
     return;

+ 1 - 1
examples/examplesV1/#attic/StreamParseInt/StreamParseInt.ino

@@ -17,7 +17,7 @@ void setup() {
   }
   Serial.println(F("Type any character to start"));
   while (!Serial.available()) {
-    SysCall::yield();
+    SysCall::yield(); 
   }
   // Initialize the SD.
   if (!SD.begin(csPin)) {

+ 2 - 2
examples/examplesV1/#attic/append/append.ino

@@ -26,8 +26,8 @@ void setup() {
   char name[] = "append.txt";
 
   Serial.begin(9600);
-
-  // Wait for USB Serial
+  
+  // Wait for USB Serial 
   while (!Serial) {
     SysCall::yield();
   }

+ 2 - 2
examples/examplesV1/#attic/average/average.ino

@@ -55,8 +55,8 @@ void calcAverage() {
 //------------------------------------------------------------------------------
 void setup() {
   Serial.begin(9600);
-
-  // Wait for USB Serial
+  
+  // Wait for USB Serial 
   while (!Serial) {
     SysCall::yield();
   }

+ 3 - 3
examples/examplesV1/#attic/benchSD/benchSD.ino

@@ -27,8 +27,8 @@ void error(const char* s) {
 //------------------------------------------------------------------------------
 void setup() {
   Serial.begin(9600);
-
-  // Wait for USB Serial
+  
+  // Wait for USB Serial 
   while (!Serial) {
     yield();
   }
@@ -46,7 +46,7 @@ void loop() {
 
   // F() stores strings in flash to save RAM
   Serial.println(F("Type any character to start"));
-
+  
   while (!Serial.available()) {
     yield();
   }

+ 2 - 2
examples/examplesV1/#attic/bufstream/bufstream.ino

@@ -13,8 +13,8 @@ void setup() {
   int i, j, k;    // values from parsed line
 
   Serial.begin(9600);
-
-  // Wait for USB Serial
+  
+  // Wait for USB Serial 
   while (!Serial) {
     SysCall::yield();
   }

+ 1 - 1
examples/examplesV1/#attic/cin_cout/cin_cout.ino

@@ -16,7 +16,7 @@ ArduinoInStream cin(Serial, cinBuf, sizeof(cinBuf));
 //------------------------------------------------------------------------------
 void setup() {
   Serial.begin(9600);
-  // Wait for USB Serial
+  // Wait for USB Serial 
   while (!Serial) {
     SysCall::yield();
   }

+ 1 - 1
examples/examplesV1/#attic/eventlog/eventlog.ino

@@ -36,7 +36,7 @@ void logEvent(const char *msg) {
 //------------------------------------------------------------------------------
 void setup() {
   Serial.begin(9600);
-  // Wait for USB Serial
+  // Wait for USB Serial 
   while (!Serial) {
     SysCall::yield();
   }

+ 2 - 2
examples/examplesV1/#attic/fgetsRewrite/fgetsRewrite.ino

@@ -86,8 +86,8 @@ void makeTestFile() {
 //------------------------------------------------------------------------------
 void setup() {
   Serial.begin(9600);
-
-  // Wait for USB Serial
+  
+  // Wait for USB Serial 
   while (!Serial) {
     SysCall::yield();
   }

+ 2 - 2
examples/examplesV1/#attic/readlog/readlog.ino

@@ -18,8 +18,8 @@ ArduinoOutStream cout(Serial);
 void setup() {
   int c;
   Serial.begin(9600);
-
-  // Wait for USB Serial
+  
+  // Wait for USB Serial 
   while (!Serial) {
     SysCall::yield();
   }

+ 4 - 4
examples/examplesV1/LowLatencyLoggerADXL345/UserFunctions.cpp

@@ -17,12 +17,12 @@ const uint8_t DATAZ1 = 0x37; //Z-Axis Data 1
 
 void writeADXL345Register(const uint8_t registerAddress, const uint8_t value) {
   // Max SPI clock frequency is 5 MHz with CPOL = 1 and CPHA = 1.
-  SPI.beginTransaction(SPISettings(5000000, MSBFIRST, SPI_MODE3));
+  SPI.beginTransaction(SPISettings(5000000, MSBFIRST, SPI_MODE3));  
   digitalWrite(ADXL345_CS, LOW);
   SPI.transfer(registerAddress);
   SPI.transfer(value);
   digitalWrite(ADXL345_CS, HIGH);
-  SPI.endTransaction();
+  SPI.endTransaction();  
 }
 
 void userSetup() {
@@ -32,7 +32,7 @@ void userSetup() {
   //Put the ADXL345 into +/- 4G range by writing the value 0x01 to the DATA_FORMAT register.
   writeADXL345Register(DATA_FORMAT, 0x01);
   //Put the ADXL345 into Measurement Mode by writing 0x08 to the POWER_CTL register.
-  writeADXL345Register(POWER_CTL, 0x08);  //Measurement mode
+  writeADXL345Register(POWER_CTL, 0x08);  //Measurement mode  
 }
 
 // Acquire a data record.
@@ -45,7 +45,7 @@ void acquireData(data_t* data) {
   SPI.transfer(DATAX0 | 0XC0);
   data->accel[0] = SPI.transfer(0) | (SPI.transfer(0) << 8);
   data->accel[1] = SPI.transfer(0) | (SPI.transfer(0) << 8);
-  data->accel[2] = SPI.transfer(0) | (SPI.transfer(0) << 8);
+  data->accel[2] = SPI.transfer(0) | (SPI.transfer(0) << 8); 
   digitalWrite(ADXL345_CS, HIGH);
   SPI.endTransaction();
 }

+ 2 - 2
examples/examplesV1/LowLatencyLoggerMPU6050/UserFunctions.cpp

@@ -9,7 +9,7 @@ static uint32_t startMicros;
 // Acquire a data record.
 void acquireData(data_t* data) {
   data->time = micros();
-  mpu.getMotion6(&data->ax, &data->ay, &data->az,
+  mpu.getMotion6(&data->ax, &data->ay, &data->az, 
                  &data->gx, &data->gy, &data->gz);
 }
 
@@ -21,7 +21,7 @@ void userSetup() {
 #elif I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_FASTWIRE
   Fastwire::setup(400, true);
 #endif
-  mpu.initialize();
+  mpu.initialize();  
 }
 
 // Print a data record.

+ 3 - 3
examples/examplesV1/OpenNext/OpenNext.ino

@@ -15,12 +15,12 @@ SdFile file;
 //------------------------------------------------------------------------------
 void setup() {
   Serial.begin(9600);
-
-  // Wait for USB Serial
+  
+  // Wait for USB Serial 
   while (!Serial) {
     SysCall::yield();
   }
-
+  
   Serial.println("Type any character to start");
   while (!Serial.available()) {
     SysCall::yield();

+ 1 - 1
examples/examplesV1/PrintBenchmark/PrintBenchmark.ino

@@ -26,7 +26,7 @@ ArduinoOutStream cout(Serial);
 //------------------------------------------------------------------------------
 void setup() {
   Serial.begin(9600);
-  // Wait for USB Serial
+  // Wait for USB Serial 
   while (!Serial) {
     SysCall::yield();
   }

+ 4 - 4
examples/examplesV1/RawWrite/RawWrite.ino

@@ -45,8 +45,8 @@ ArduinoOutStream cout(Serial);
 //------------------------------------------------------------------------------
 void setup(void) {
   Serial.begin(9600);
-
-  // Wait for USB Serial
+  
+  // Wait for USB Serial 
   while (!Serial) {
     SysCall::yield();
   }
@@ -101,7 +101,7 @@ void loop(void) {
   cout << F("Start raw write of ") << file.fileSize()/1000UL << F(" KB\n");
   cout << F("Target rate: ") << RATE_KB_PER_SEC << F(" KB/sec\n");
   cout << F("Target time: ") << TEST_TIME_SEC << F(" seconds\n");
-
+  
   // tell card to setup for multiple block write with pre-erase
   if (!sd.card()->writeStart(bgnBlock, BLOCK_COUNT)) {
     error("writeStart failed");
@@ -173,7 +173,7 @@ void loop(void) {
   cout << F(" seconds\n");
   cout << F("Min block write time: ") << minWriteTime << F(" micros\n");
   cout << F("Max block write time: ") << maxWriteTime << F(" micros\n");
-  cout << F("Avg block write time: ") << avgWriteTime << F(" micros\n");
+  cout << F("Avg block write time: ") << avgWriteTime << F(" micros\n");  
   // close file for next pass of loop
   file.close();
   Serial.println();

+ 7 - 7
examples/examplesV1/ReadCsv/ReadCsv.ino

@@ -30,7 +30,7 @@ File file;
  * delim - csv delimiter.
  *
  * return - negative value for failure.
- *          delimiter, '\n' or zero(EOF) for success.
+ *          delimiter, '\n' or zero(EOF) for success.           
  */
 int csvReadText(File* file, char* str, size_t size, char delim) {
   char ch;
@@ -129,8 +129,8 @@ int csvReadFloat(File* file, float* num, char delim) {
 //------------------------------------------------------------------------------
 void setup() {
   Serial.begin(9600);
-
-  // Wait for USB Serial
+  
+  // Wait for USB Serial 
   while (!Serial) {
     yield();
   }
@@ -144,8 +144,8 @@ void setup() {
     return;
   }
   // Remove existing file.
-   SD.remove("READTEST.TXT");
-
+   SD.remove("READTEST.TXT"); 
+   
   // Create the file.
   file = SD.open("READTEST.TXT", FILE_WRITE);
   if (!file) {
@@ -169,7 +169,7 @@ void setup() {
   file.seek(0);
 
   // Read the file and print fields.
-  int16_t tcalc;
+  int16_t tcalc; 
   float t1, t2, h1, h2;
   // Must be dim 9 to allow for zero byte.
   char timeS[9], dateS[9];
@@ -188,7 +188,7 @@ void setup() {
       while ((ch = file.read()) > 0 && nr++ < 100) {
         Serial.write(ch);
       }
-      break;
+      break;            
     }
     Serial.print(tcalc);
     Serial.print(CSV_DELIM);

+ 5 - 5
examples/examplesV1/ReadCsvArray/ReadCsvArray.ino

@@ -51,8 +51,8 @@ size_t readField(File* file, char* str, size_t size, const char* delim) {
 //------------------------------------------------------------------------------
 void setup() {
   Serial.begin(9600);
-
-  // Wait for USB Serial
+  
+  // Wait for USB Serial 
   while (!Serial) {
     SysCall::yield();
   }
@@ -63,7 +63,7 @@ void setup() {
   // Initialize the SD.
   if (!SD.begin(CS_PIN)) {
     errorHalt("begin failed");
-  }
+  } 
   // Create or open the file.
   file = SD.open("READNUM.TXT", FILE_WRITE);
   if (!file) {
@@ -93,7 +93,7 @@ void setup() {
   char *ptr;     // Test for valid field.
 
   // Read the file and store the data.
-
+  
   for (i = 0; i < ROW_DIM; i++) {
     for (j = 0; j < COL_DIM; j++) {
       n = readField(&file, str, sizeof(str), ",\n");
@@ -117,7 +117,7 @@ void setup() {
     // Allow missing endl at eof.
     if (str[n-1] != '\n' && file.available()) {
       errorHalt("missing endl");
-    }
+    }    
   }
 
   // Print the array.

+ 3 - 3
examples/examplesV1/ReadCsvStream/ReadCsvStream.ino

@@ -92,14 +92,14 @@ void writeFile() {
 //------------------------------------------------------------------------------
 void setup() {
   Serial.begin(9600);
-
-  // Wait for USB Serial
+  
+  // Wait for USB Serial 
   while (!Serial) {
     SysCall::yield();
   }
   cout << F("Type any character to start\n");
   while (!Serial.available()) {
-    SysCall::yield();
+    SysCall::yield();  
   }
 
   // Initialize at the highest speed supported by the board that is

+ 2 - 2
examples/examplesV1/STM32Test/STM32Test.ino

@@ -33,7 +33,7 @@ const uint16_t NWRITE = FILE_SIZE/BUF_DIM;
 //------------------------------------------------------------------------------
 void setup() {
   Serial.begin(9600);
-  // Wait for USB Serial
+  // Wait for USB Serial 
   while (!Serial) {
   }
 
@@ -151,7 +151,7 @@ void setup() {
   Serial.println(F(" millis"));
   // close test.bin
   file1.close();
-  file2.close();
+  file2.close(); 
   // list current directory on both cards
   Serial.println(F("------sd1 -------"));
   sd1.ls("/", LS_R | LS_DATE | LS_SIZE);

+ 7 - 7
examples/examplesV1/SdFormatter/SdFormatter.ino

@@ -12,7 +12,7 @@
  * and SDFormatter uses FAT12.
  */
 #error  use new Version 2 SdFormatter
-// Set USE_SDIO to zero for SPI card access.
+// Set USE_SDIO to zero for SPI card access. 
 #define USE_SDIO 0
 //
 // Change the value of chipSelect if your hardware does
@@ -45,7 +45,7 @@ SdioCardEX card;
 #else  // USE_SDIO
 Sd2Card card;
 #endif  // USE_SDIO
-
+ 
 uint32_t cardSizeBlocks;
 uint32_t cardCapacityMB;
 
@@ -171,10 +171,10 @@ void clearFatDir(uint32_t bgn, uint32_t count) {
   for (uint32_t i = 0; i < count; i++) {
     if (!card.writeBlock(bgn + i, cache.data)) {
        sdError("Clear FAT/DIR writeBlock failed");
-    }
+    }     
     if ((i & 0XFF) == 0) {
       cout << '.';
-    }
+    }    
   }
 #else  // USE_SDIO
   if (!card.writeStart(bgn, count)) {
@@ -461,7 +461,7 @@ void formatCard() {
 void setup() {
   char c;
   Serial.begin(9600);
-  // Wait for USB Serial
+  // Wait for USB Serial 
   while (!Serial) {
     SysCall::yield();
   }
@@ -521,7 +521,7 @@ void setup() {
   }
 #if USE_SDIO
   if (!card.begin()) {
-    sdError("card.begin failed");
+    sdError("card.begin failed");  
   }
 #else  // USE_SDIO
   if (!card.begin(chipSelect, SPI_SPEED)) {
@@ -531,7 +531,7 @@ void setup() {
            "Is chip select correct at the top of this program?\n");
     sdError("card.begin failed");
   }
-#endif
+#endif  
   cardSizeBlocks = card.cardSize();
   if (cardSizeBlocks == 0) {
     sdError("cardSize");

+ 6 - 6
examples/examplesV1/SdInfo/SdInfo.ino

@@ -5,7 +5,7 @@
 #include "SdFat.h"
 #include "sdios.h"
 #error Use new Version 2 SdInfo
-// Set USE_SDIO to zero for SPI card access.
+// Set USE_SDIO to zero for SPI card access. 
 #define USE_SDIO 0
 /*
  * SD chip select pin.  Common values are:
@@ -143,8 +143,8 @@ void volDmp() {
 //------------------------------------------------------------------------------
 void setup() {
   Serial.begin(9600);
-
-  // Wait for USB Serial
+  
+  // Wait for USB Serial 
   while (!Serial) {
     SysCall::yield();
   }
@@ -154,7 +154,7 @@ void setup() {
 
   // F stores strings in flash to save RAM
   cout << F("SdFat version: ") << SD_FAT_VERSION << endl;
-#if !USE_SDIO
+#if !USE_SDIO  
   if (DISABLE_CHIP_SELECT < 0) {
     cout << F(
            "\nAssuming the SD is the only SPI device.\n"
@@ -167,7 +167,7 @@ void setup() {
   }
   cout << F("\nAssuming the SD chip select pin is: ") <<int(SD_CHIP_SELECT);
   cout << F("\nEdit SD_CHIP_SELECT to change the SD chip select pin.\n");
-#endif  // !USE_SDIO
+#endif  // !USE_SDIO  
 }
 //------------------------------------------------------------------------------
 void loop() {
@@ -195,7 +195,7 @@ void loop() {
     sdErrorMsg("cardBegin failed");
     return;
   }
- #endif  // USE_SDIO
+ #endif  // USE_SDIO 
   t = millis() - t;
 
   cardSize = sd.card()->cardSize();

+ 2 - 2
examples/examplesV1/SoftwareSpi/SoftwareSpi.ino

@@ -24,7 +24,7 @@ SdFile file;
 
 void setup() {
   Serial.begin(9600);
-  // Wait for USB Serial
+  // Wait for USB Serial 
   while (!Serial) {
     SysCall::yield();
   }
@@ -43,7 +43,7 @@ void setup() {
   file.println(F("This line was printed using software SPI."));
 
   file.rewind();
-
+  
   while (file.available()) {
     Serial.write(file.read());
   }

+ 3 - 3
examples/examplesV1/StdioBench/StdioBench.ino

@@ -42,7 +42,7 @@ void setup() {
   Serial.println(F("Starting test"));
 
   // Initialize at the highest speed supported by the board that is
-  // not over 50 MHz. Try a lower speed if SPI errors occur.
+  // not over 50 MHz. Try a lower speed if SPI errors occur.  
   if (!sd.begin(SD_CS_PIN, SD_SCK_MHZ(50))) {
     sd.errorHalt();
   }
@@ -146,7 +146,7 @@ void setup() {
             stdioFile.printField(n, '\n');
 #else  // PRINT_FIELD
             stdioFile.println(n);
-#endif  // PRINT_FIELD
+#endif  // PRINT_FIELD      
           }
           break;
 
@@ -157,7 +157,7 @@ void setup() {
               stdioFile.printField(f[i], '\n', 4);
 #else  // PRINT_FIELD
               stdioFile.println(f[i], 4);
-#endif  // PRINT_FIELD
+#endif  // PRINT_FIELD                            
             }
           }
           break;

+ 12 - 12
examples/examplesV1/TeensySdioDemo/TeensySdioDemo.ino

@@ -1,7 +1,7 @@
 // Simple performance test for Teensy 3.5/3.6 SDHC.
 // Demonstrates yield() efficiency.
 
-// Warning SdFatSdio and SdFatSdioEX normally should
+// Warning SdFatSdio and SdFatSdioEX normally should 
 // not both be used in a program.
 // Each has its own cache and member variables.
 
@@ -48,8 +48,8 @@ void errorHalt(const char* msg) {
 }
 //------------------------------------------------------------------------------
 uint32_t kHzSdClk() {
-  return useEx ? sdEx.card()->kHzSdClk() : sd.card()->kHzSdClk();
-}
+  return useEx ? sdEx.card()->kHzSdClk() : sd.card()->kHzSdClk(); 
+}  
 //------------------------------------------------------------------------------
 // Replace "weak" system yield() function.
 void yield() {
@@ -74,7 +74,7 @@ void runTest() {
   totalMicros = 0;
   yieldMicros = 0;
   yieldCalls = 0;
-  yieldMaxUsec = 0;
+  yieldMaxUsec = 0; 
   if (!file.open("TeensyDemo.bin", O_RDWR | O_CREAT)) {
     errorHalt("open failed");
   }
@@ -100,19 +100,19 @@ void runTest() {
     Serial.print(',');
     file.rewind();
     t = micros();
-
+    
     for (uint32_t n = 0; n < nRdWr; n++) {
       if ((int)nb != file.read(buf, nb)) {
         errorHalt("read failed");
       }
-      // crude check of data.
+      // crude check of data.     
       if (buf32[0] != n || buf32[nb/4 - 1] != n) {
         errorHalt("data check");
       }
     }
     t = micros() - t;
-    totalMicros += t;
-    Serial.println(1000.0*FILE_SIZE/t);
+    totalMicros += t;   
+    Serial.println(1000.0*FILE_SIZE/t);    
   }
   file.close();
   Serial.print("\ntotalMicros  ");
@@ -122,7 +122,7 @@ void runTest() {
   Serial.print("yieldCalls   ");
   Serial.println(yieldCalls);
   Serial.print("yieldMaxUsec ");
-  Serial.println(yieldMaxUsec);
+  Serial.println(yieldMaxUsec); 
   Serial.print("kHzSdClk     ");
   Serial.println(kHzSdClk());
   Serial.println("Done");
@@ -133,8 +133,8 @@ void setup() {
   while (!Serial) {
   }
   Serial.println("SdFatSdioEX uses extended multi-block transfers without DMA.");
-  Serial.println("SdFatSdio uses a traditional DMA SDIO implementation.");
-  Serial.println("Note the difference is speed and busy yield time.\n");
+  Serial.println("SdFatSdio uses a traditional DMA SDIO implementation."); 
+  Serial.println("Note the difference is speed and busy yield time.\n"); 
 }
 //-----------------------------------------------------------------------------
 void loop() {
@@ -163,7 +163,7 @@ void loop() {
       sd.initErrorHalt("SdFatSdio begin() failed");
     }
     // make sd the current volume.
-    sd.chvol();
+    sd.chvol();  
   }
   runTest();
 }

+ 2 - 2
examples/examplesV1/Timestamp/Timestamp.ino

@@ -53,7 +53,7 @@ void dateTime(uint16_t* date, uint16_t* time) {
 void printTimestamps(SdFile& f) {
   cout << F("Creation: ");
   f.printCreateDateTime(&Serial);
-  cout << endl << F("Modify: ");
+  cout << endl << F("Modify: ");  
   f.printModifyDateTime(&Serial);
   cout << endl << F("Access: ");
   f.printAccessDateTime(&Serial);
@@ -68,7 +68,7 @@ void setup(void) {
   }
   cout << F("Type any character to start\n");
   while (!Serial.available()) {
-    SysCall::yield();
+    SysCall::yield();  
   }
   // Initialize at the highest speed supported by the board that is
   // not over 50 MHz. Try a lower speed if SPI errors occur.

+ 2 - 2
examples/examplesV1/TwoCards/TwoCards.ino

@@ -25,7 +25,7 @@ const uint32_t NWRITE = FILE_SIZE/BUF_DIM;
 //------------------------------------------------------------------------------
 void setup() {
   Serial.begin(9600);
-  // Wait for USB Serial
+  // Wait for USB Serial 
   while (!Serial) {
     SysCall::yield();
   }
@@ -146,7 +146,7 @@ void setup() {
   Serial.println(F(" millis"));
   // close test.bin
   file1.close();
-  file2.close();
+  file2.close(); 
   // list current directory on both cards
   Serial.println(F("------sd1 -------"));
   sd1.ls("/", LS_R | LS_DATE | LS_SIZE);

+ 3 - 3
examples/examplesV1/dataLogger/dataLogger.ino

@@ -67,8 +67,8 @@ void setup() {
   char fileName[13] = FILE_BASE_NAME "00.csv";
 
   Serial.begin(9600);
-
-  // Wait for USB Serial
+  
+  // Wait for USB Serial 
   while (!Serial) {
     SysCall::yield();
   }
@@ -78,7 +78,7 @@ void setup() {
   while (!Serial.available()) {
     SysCall::yield();
   }
-
+  
   // Initialize at the highest speed supported by the board that is
   // not over 50 MHz. Try a lower speed if SPI errors occur.
   if (!sd.begin(chipSelect, SD_SCK_MHZ(50))) {

+ 2 - 2
examples/examplesV1/fgets/fgets.ino

@@ -61,8 +61,8 @@ void makeTestFile() {
 //------------------------------------------------------------------------------
 void setup(void) {
   Serial.begin(9600);
-
-  // Wait for USB Serial
+  
+  // Wait for USB Serial 
   while (!Serial) {
     SysCall::yield();
   }

+ 1 - 1
examples/examplesV1/formatting/formatting.ino

@@ -43,7 +43,7 @@ void showDate(int m, int d, int y) {
 void setup(void) {
   Serial.begin(9600);
 
-  // Wait for USB Serial
+  // Wait for USB Serial 
   while (!Serial) {
     SysCall::yield();
   }

+ 2 - 2
examples/examplesV1/getline/getline.ino

@@ -55,8 +55,8 @@ void testGetline() {
 //------------------------------------------------------------------------------
 void setup(void) {
   Serial.begin(9600);
-
-  // Wait for USB Serial
+  
+  // Wait for USB Serial 
   while (!Serial) {
     SysCall::yield();
   }

+ 3 - 3
examples/examplesV1/wipe/wipe.ino

@@ -9,7 +9,7 @@ SdFat sd;
 void setup() {
   int c;
   Serial.begin(9600);
-  // Wait for USB Serial
+  // Wait for USB Serial 
   while (!Serial) {
     SysCall::yield();
   }
@@ -22,7 +22,7 @@ void setup() {
     sd.errorHalt("Quitting, you did not type 'Y'.");
   }
   // Initialize at the highest speed supported by the board that is
-  // not over 50 MHz. Try a lower speed if SPI errors occur.
+  // not over 50 MHz. Try a lower speed if SPI errors occur. 
   if (!sd.begin(chipSelect, SD_SCK_MHZ(50))) {
     sd.initErrorHalt();
   }
@@ -32,7 +32,7 @@ void setup() {
   }
   // Must reinitialize after wipe.
   // Initialize at the highest speed supported by the board that is
-  // not over 50 MHz. Try a lower speed if SPI errors occur.
+  // not over 50 MHz. Try a lower speed if SPI errors occur.  
   if (!sd.begin(chipSelect, SD_SCK_MHZ(50))) {
     sd.errorHalt("Second init failed.");
   }

+ 5 - 3
examples/rename/rename.ino

@@ -25,14 +25,16 @@ const uint8_t SD_CS_PIN = SS;
 const uint8_t SD_CS_PIN = SDCARD_SS_PIN;
 #endif  // SDCARD_SS_PIN
 
+// Try max SPI clock for an SD. Reduce SPI_CLOCK if errors occur.
+#define SPI_CLOCK SD_SCK_MHZ(50)
 
 // Try to select the best SD card configuration.
 #if HAS_SDIO_CLASS
 #define SD_CONFIG SdioConfig(FIFO_SDIO)
-#elif ENABLE_DEDICATED_SPI
-#define SD_CONFIG SdSpiConfig(SD_CS_PIN, DEDICATED_SPI)
+#elif  ENABLE_DEDICATED_SPI
+#define SD_CONFIG SdSpiConfig(SD_CS_PIN, DEDICATED_SPI, SPI_CLOCK)
 #else  // HAS_SDIO_CLASS
-#define SD_CONFIG SdSpiConfig(SD_CS_PIN, SHARED_SPI)
+#define SD_CONFIG SdSpiConfig(SD_CS_PIN, SHARED_SPI, SPI_CLOCK)
 #endif  // HAS_SDIO_CLASS
 
 #if SD_FAT_TYPE == 0

+ 1 - 1
library.properties

@@ -1,5 +1,5 @@
 name=SdFat
-version=2.0.7
+version=2.1.0
 license=MIT
 author=Bill Greiman <fat16lib@sbcglobal.net>
 maintainer=Bill Greiman <fat16lib@sbcglobal.net>

+ 3 - 4
src/ExFatLib/ExFatConfig.h

@@ -24,11 +24,10 @@
  */
 #ifndef ExFatConfig_h
 #define ExFatConfig_h
-#include "../SdFatConfig.h"
-#ifndef USE_EXFAT_UNICODE_NAMES
-#define USE_EXFAT_UNICODE_NAMES 0
-#endif  // USE_EXFAT_UNICODE_NAMES
+#include "SdFatConfig.h"
+
 #ifndef READ_ONLY
 #define READ_ONLY 0
 #endif  // READ_ONLY
+
 #endif  // ExFatConfig_h

+ 25 - 5
src/ExFatLib/ExFatDbg.cpp

@@ -23,7 +23,7 @@
  * DEALINGS IN THE SOFTWARE.
  */
 #include "ExFatVolume.h"
-#include "upcase.h"
+#include "../common/upcase.h"
 #include "ExFatFile.h"
 #ifndef DOXYGEN_SHOULD_SKIP_THIS
 //------------------------------------------------------------------------------
@@ -51,6 +51,20 @@ static uint16_t exFatDirChecksum(const void* dir, uint16_t checksum) {
   }
   return checksum;
 }
+
+//------------------------------------------------------------------------------
+static uint16_t hashDir(DirName_t* dir, uint16_t hash) {
+  for (uint8_t i = 0; i < 30; i += 2) {
+    uint16_t u = getLe16(dir->unicode + i);
+    if (!u) {
+      break;
+    }
+  uint16_t c = toUpcase(u);
+  hash = ((hash << 15) | (hash >> 1)) + (c & 0XFF);
+  hash = ((hash << 15) | (hash >> 1)) + (c >> 8);
+  }
+  return hash;
+}
 //------------------------------------------------------------------------------
 static void printDateTime(print_t* pr,
                           uint32_t timeDate, uint8_t ms, int8_t tz) {
@@ -105,8 +119,15 @@ static void printDirName(print_t* pr, DirName_t* dir) {
   pr->println(dir->type, HEX);
   pr->print(F("unicode: "));
   for (size_t i = 0; i < 30; i += 2) {
-    if (dir->unicode[i] == 0) break;
-    pr->write(dir->unicode[i]);
+    uint16_t c = getLe16(dir->unicode + i);
+    if (c == 0) break;
+    if (c < 128) {
+      pr->print(static_cast<char>(c));
+    } else {
+      pr->print("0x");
+      pr->print(c, HEX);
+    }
+    pr->print(' ');
   }
   pr->println();
 }
@@ -475,8 +496,7 @@ bool ExFatPartition::printDir(print_t* pr, ExFatFile* file) {
         printDirName(pr, dirName);
         calcChecksum = exFatDirChecksum(dir, calcChecksum);
         nUnicode = nameLength > 15 ? 15 : nameLength;
-        calcHash = exFatHashName(reinterpret_cast<ExChar16_t*>
-                                (dirName->unicode), nUnicode, calcHash);
+        calcHash = hashDir(dirName, calcHash);
         nameLength -= nUnicode;
         setCount--;
         if (nameLength == 0  || setCount == 0) {

+ 118 - 166
src/ExFatLib/ExFatFile.cpp

@@ -24,9 +24,31 @@
  */
 #define DBG_FILE "ExFatFile.cpp"
 #include "../common/DebugMacros.h"
+#include "../common/FsUtf.h"
 #include "ExFatFile.h"
 #include "ExFatVolume.h"
-#include "upcase.h"
+//------------------------------------------------------------------------------
+/** test for legal character.
+ *
+ * \param[in] c character to be tested.
+ *
+ * \return true for legal character else false.
+ */
+inline bool lfnLegalChar(uint8_t c) {
+#if USE_UTF8_LONG_NAMES
+  return !lfnReservedChar(c);
+#else  // USE_UTF8_LONG_NAMES
+  return !(lfnReservedChar(c) || c & 0X80);
+#endif  // USE_UTF8_LONG_NAMES
+}
+//------------------------------------------------------------------------------
+uint8_t* ExFatFile::dirCache(uint8_t set, uint8_t options) {
+  DirPos_t pos = m_dirPos;
+  if (m_vol->dirSeek(&pos, 32*set) != 1) {
+    return nullptr;
+  }
+  return m_vol->dirCache(&pos, options);
+}
 //------------------------------------------------------------------------------
 bool ExFatFile::close() {
   bool rtn = sync();
@@ -136,55 +158,19 @@ bool ExFatFile::getModifyDateTime(uint16_t* pdate, uint16_t* ptime) {
   return false;
 }
 //------------------------------------------------------------------------------
-size_t ExFatFile::getName(ExChar_t* name, size_t length) {
-  DirName_t* dn;
-  DirPos_t pos = m_dirPos;
-  size_t n = 0;
-  if (!isOpen()) {
-      DBG_FAIL_MACRO;
-      goto fail;
-  }
-  for (uint8_t is = 1; is < m_setCount; is++) {
-    if (m_vol->dirSeek(&pos, is == 1 ? 64: 32) != 1) {
-      DBG_FAIL_MACRO;
-      goto fail;
-    }
-    dn = reinterpret_cast<DirName_t*>
-         (m_vol->dirCache(&pos, FsCache::CACHE_FOR_READ));
-    if (!dn || dn->type != EXFAT_TYPE_NAME) {
-      DBG_FAIL_MACRO;
-      goto fail;
-    }
-    for (uint8_t in = 0; in < 15; in++) {
-      uint16_t c = getLe16(dn->unicode + 2*in);
-      if (c == 0 || (n + 1) >= length) {
-        goto done;
-      }
-      name[n++] = sizeof(ExChar_t) > 1 || c < 0X7F ? c : '?';
-    }
-  }
- done:
-  name[n] = 0;
-  return n;
-
- fail:
-  *name = 0;
-  return 0;
-}
-//------------------------------------------------------------------------------
 bool ExFatFile::isBusy() {
   return m_vol->isBusy();
 }
 //------------------------------------------------------------------------------
-bool ExFatFile::open(const ExChar_t* path, int oflag) {
+bool ExFatFile::open(const char* path, oflag_t oflag) {
   return open(ExFatVolume::cwv(), path, oflag);
 }
 //------------------------------------------------------------------------------
-bool ExFatFile::open(ExFatVolume* vol, const ExChar_t* path, int oflag) {
+bool ExFatFile::open(ExFatVolume* vol, const char* path, oflag_t oflag) {
   return vol && open(vol->vwd(), path, oflag);
 }
 //------------------------------------------------------------------------------
-bool ExFatFile::open(ExFatFile* dirFile, const ExChar_t* path, oflag_t oflag) {
+bool ExFatFile::open(ExFatFile* dirFile, const char* path, oflag_t oflag) {
   ExFatFile tmpDir;
   ExName_t fname;
   // error if already open
@@ -213,15 +199,15 @@ bool ExFatFile::open(ExFatFile* dirFile, const ExChar_t* path, oflag_t oflag) {
     if (*path == 0) {
       break;
     }
-    if (!open(dirFile, &fname, O_RDONLY)) {
-      DBG_FAIL_MACRO;
+    if (!openPrivate(dirFile, &fname, O_RDONLY)) {
+      DBG_WARN_MACRO;
       goto fail;
     }
     tmpDir = *this;
     dirFile = &tmpDir;
     close();
   }
-  return open(dirFile, &fname, oflag);
+  return openPrivate(dirFile, &fname, oflag);
 
  fail:
   return false;
@@ -243,19 +229,15 @@ bool ExFatFile::openNext(ExFatFile* dir, oflag_t oflag) {
     DBG_FAIL_MACRO;
     goto fail;
   }
-  return openRootFile(dir, nullptr, 0, oflag);
+  return openPrivate(dir, nullptr, oflag);
 
  fail:
   return false;
 }
 //------------------------------------------------------------------------------
-bool ExFatFile::openRootFile(ExFatFile* dir, const ExChar_t* name,
-                          uint8_t nameLength, oflag_t oflag) {
+bool ExFatFile::openPrivate(ExFatFile* dir, ExName_t* fname, oflag_t oflag) {
   int n;
-  uint8_t nameOffset = 0;
-  uint8_t nCmp;
   uint8_t modeFlags;
-  uint16_t nameHash = 0;
   uint32_t curCluster __attribute__((unused));
   uint8_t* cache __attribute__((unused));
   DirPos_t freePos __attribute__((unused));
@@ -265,14 +247,15 @@ bool ExFatFile::openRootFile(ExFatFile* dir, const ExChar_t* name,
   DirName_t*   dirName;
   uint8_t buf[32];
   uint8_t freeCount = 0;
-  uint8_t freeNeed;
+  uint8_t freeNeed = 3;
   bool inSet = false;
 
-  // error if already open
+  // error if already open, no access mode, or no directory.
   if (isOpen() || !dir->isDir()) {
     DBG_FAIL_MACRO;
     goto fail;
   }
+
   switch (oflag & O_ACCMODE) {
     case O_RDONLY:
       modeFlags = FILE_FLAG_READ;
@@ -288,11 +271,11 @@ bool ExFatFile::openRootFile(ExFatFile* dir, const ExChar_t* name,
       goto fail;
   }
   modeFlags |= oflag & O_APPEND ? FILE_FLAG_APPEND : 0;
-  if (name) {
-    nameHash = exFatHashName(name, nameLength, 0);
+
+  if (fname) {
+    freeNeed = 2 + (fname->nameLength + 14)/15;
     dir->rewind();
   }
-  freeNeed = 2 + (nameLength + 14)/15;
 
   while (1) {
     n = dir->read(buf, 32);
@@ -304,6 +287,7 @@ bool ExFatFile::openRootFile(ExFatFile* dir, const ExChar_t* name,
       goto fail;
     }
     if (!(buf[0] & 0x80)) {
+      // Unused entry.
       if (freeCount == 0) {
         freePos.position = dir->curPosition() - 32;
         freePos.cluster = dir->curCluster();
@@ -312,8 +296,14 @@ bool ExFatFile::openRootFile(ExFatFile* dir, const ExChar_t* name,
         freeCount++;
       }
       if (!buf[0]) {
-        goto create;
+        if (fname) {
+          goto create;
+        }
+        // Likely openNext call.
+        DBG_WARN_MACRO;
+        goto fail;
       }
+      inSet = false;
     } else if (!inSet) {
       if (freeCount < freeNeed) {
         freeCount = 0;
@@ -322,62 +312,45 @@ bool ExFatFile::openRootFile(ExFatFile* dir, const ExChar_t* name,
         continue;
       }
       inSet = true;
-    }
-    switch (buf[0]) {
-      case EXFAT_TYPE_FILE:
-        memset(this, 0, sizeof(ExFatFile));
-        dirFile = reinterpret_cast<DirFile_t*>(buf);
-        m_setCount = dirFile->setCount;
-        m_attributes = getLe16(dirFile->attributes) & FILE_ATTR_COPY;
-        if (!(m_attributes & EXFAT_ATTRIB_DIRECTORY)) {
-          m_attributes |= FILE_ATTR_FILE;
-        }
-        m_vol = dir->volume();
-
-        m_dirPos.cluster = dir->curCluster();
-        m_dirPos.position = dir->curPosition() - 32;
-        m_dirPos.isContiguous = dir->isContiguous();
-        break;
-
-      case EXFAT_TYPE_STREAM:
-        dirStream = reinterpret_cast<DirStream_t*>(buf);
-        m_flags = modeFlags;
-        if (dirStream->flags & EXFAT_FLAG_CONTIGUOUS) {
-          m_flags |= FILE_FLAG_CONTIGUOUS;
-        }
-        nameOffset = 0;
-        m_validLength = getLe64(dirStream->validLength);
-        m_firstCluster = getLe32(dirStream->firstCluster);
-        m_dataLength = getLe64(dirStream->dataLength);
-        if (!name) {
-          goto found;
-        }
-        if (nameLength != dirStream->nameLength ||
-            nameHash != getLe16(dirStream->nameHash)) {
-          inSet = false;
-          break;
-        }
-        break;
-
-      case EXFAT_TYPE_NAME:
-        dirName = reinterpret_cast<DirName_t*>(buf);
-        nCmp = nameLength - nameOffset;
-        if (nCmp > 15) {
-          nCmp = 15;
-        }
-        if (!exFatCmpName(dirName, name, nameOffset, nCmp)) {
-          inSet = false;
-          break;
-        }
-        nameOffset += nCmp;
-
-        if (nameOffset == nameLength) {
-          goto found;
-        }
-        break;
-
-      default:
-        break;
+      memset(this, 0, sizeof(ExFatFile));
+      dirFile = reinterpret_cast<DirFile_t*>(buf);
+      m_setCount = dirFile->setCount;
+      m_attributes = getLe16(dirFile->attributes) & FILE_ATTR_COPY;
+      if (!(m_attributes & EXFAT_ATTRIB_DIRECTORY)) {
+        m_attributes |= FILE_ATTR_FILE;
+      }
+      m_vol = dir->volume();
+      m_dirPos.cluster = dir->curCluster();
+      m_dirPos.position = dir->curPosition() - 32;
+      m_dirPos.isContiguous = dir->isContiguous();
+    } else if (buf[0] == EXFAT_TYPE_STREAM) {
+      dirStream = reinterpret_cast<DirStream_t*>(buf);
+      m_flags = modeFlags;
+      if (dirStream->flags & EXFAT_FLAG_CONTIGUOUS) {
+        m_flags |= FILE_FLAG_CONTIGUOUS;
+      }
+      m_validLength = getLe64(dirStream->validLength);
+      m_firstCluster = getLe32(dirStream->firstCluster);
+      m_dataLength = getLe64(dirStream->dataLength);
+      if (!fname) {
+        goto found;
+      }
+      fname->reset();
+      if (fname->nameLength != dirStream->nameLength ||
+          fname->nameHash != getLe16(dirStream->nameHash)) {
+        inSet = false;
+      }
+    } else if (buf[0] == EXFAT_TYPE_NAME) {
+      dirName = reinterpret_cast<DirName_t*>(buf);
+      if (!cmpName(dirName, fname)) {
+        inSet = false;
+        continue;
+      }
+      if (fname->atEnd()) {
+        goto found;
+      }
+    } else {
+      inSet = false;
     }
   }
 
@@ -409,7 +382,7 @@ bool ExFatFile::openRootFile(ExFatFile* dir, const ExChar_t* name,
     DBG_FAIL_MACRO;
     goto fail;
   }
-#endif  // READ_ONLY
+#endif  // !READ_ONLY
   return true;
 
  create:
@@ -418,8 +391,8 @@ bool ExFatFile::openRootFile(ExFatFile* dir, const ExChar_t* name,
   goto fail;
 #else  // READ_ONLY
   // don't create unless O_CREAT and write
-  if (!(oflag & O_CREAT) || !(modeFlags & FILE_FLAG_WRITE) || !name) {
-    DBG_FAIL_MACRO;
+  if (!(oflag & O_CREAT) || !(modeFlags & FILE_FLAG_WRITE) || !fname) {
+    DBG_WARN_MACRO;
     goto fail;
   }
   while (freeCount < freeNeed) {
@@ -443,20 +416,14 @@ bool ExFatFile::openRootFile(ExFatFile* dir, const ExChar_t* name,
     }
     freeCount++;
   }
-
   freePos.isContiguous = dir->isContiguous();
   memset(this, 0, sizeof(ExFatFile));
   m_vol = dir->volume();
   m_attributes = FILE_ATTR_FILE;
   m_dirPos = freePos;
+  fname->reset();
   for (uint8_t i = 0; i < freeNeed; i++) {
-    if (i) {
-      if (1 != m_vol->dirSeek(&freePos, 32)) {
-        DBG_FAIL_MACRO;
-        goto fail;
-      }
-    }
-    cache = m_vol->dirCache(&freePos, FsCache::CACHE_FOR_WRITE);
+    cache = dirCache(i, FsCache::CACHE_FOR_WRITE);
     if (!cache || (cache[0] & 0x80)) {
       DBG_FAIL_MACRO;
       goto fail;
@@ -488,28 +455,26 @@ bool ExFatFile::openRootFile(ExFatFile* dir, const ExChar_t* name,
     } else if (i == 1) {
       dirStream = reinterpret_cast<DirStream_t*>(cache);
       dirStream->type = EXFAT_TYPE_STREAM;
-      dirStream->flags = EXFAT_FLAG_ALWAYS1 | EXFAT_FLAG_CONTIGUOUS;
-      m_flags = modeFlags | FILE_FLAG_CONTIGUOUS | FILE_FLAG_DIR_DIRTY;
-
-      dirStream->nameLength = nameLength;
-      setLe16(dirStream->nameHash, nameHash);
+      dirStream->flags = EXFAT_FLAG_ALWAYS1;
+      m_flags = modeFlags | FILE_FLAG_DIR_DIRTY;
+      dirStream->nameLength = fname->nameLength;
+      setLe16(dirStream->nameHash, fname->nameHash);
     } else {
       dirName = reinterpret_cast<DirName_t*>(cache);
       dirName->type = EXFAT_TYPE_NAME;
-      nameOffset = 15*(i - 2);
-      nCmp = nameLength - nameOffset;
-      if (nCmp > 15) {
-        nCmp = 15;
-      }
-      for (size_t k = 0; k < nCmp; k++) {
-        setLe16(dirName->unicode + 2*k, name[k + nameOffset]);
+      for (size_t k = 0; k < 15; k++) {
+        if (fname->atEnd()) {
+          break;
+        }
+        uint16_t u = fname->get16();
+        setLe16(dirName->unicode + 2*k, u);
       }
     }
   }
   return sync();
 #endif  // READ_ONLY
- fail:
 
+ fail:
   // close file
   m_attributes = FILE_ATTR_CLOSED;
   m_flags = 0;
@@ -531,45 +496,32 @@ bool ExFatFile::openRoot(ExFatVolume* vol) {
   return false;
 }
 //------------------------------------------------------------------------------
-bool ExFatFile::parsePathName(const ExChar_t* path,
-                            ExName_t* fname, const ExChar_t** ptr) {
-  ExChar_t c;
-  int end;
-  int len = 0;
-
+bool ExFatFile::parsePathName(const char* path,
+                            ExName_t* fname, const char** ptr) {
   // Skip leading spaces.
   while (*path == ' ') {
     path++;
   }
-  fname->lfn = path;
-
-  for (len = 0; ; len++) {
-    c = path[len];
-    if (c == 0 || isDirSeparator(c)) {
-      break;
-    }
+  fname->begin = path;
+  fname->end = path;
+  while (*path && !isDirSeparator(*path)) {
+    uint8_t c = *path++;
     if (!lfnLegalChar(c)) {
-      return false;
+      DBG_FAIL_MACRO;
+      goto fail;
     }
-  }
-  // Advance to next path component.
-  for (end = len; path[end] ==  ' ' || isDirSeparator(path[end]); end++) {}
-  *ptr = &path[end];
-
-  // Back over spaces and dots.
-  while (len) {
-    c = path[len - 1];
     if (c != '.' && c != ' ') {
-      break;
+      // Need to trim trailing dots spaces.
+      fname->end = path;
     }
-    len--;
-  }
-  // Max length of LFN is 255.
-  if (len > EXFAT_MAX_NAME_LENGTH) {
-    return false;
   }
-  fname->len = len;
-  return true;
+  // Advance to next path component.
+  for (; *path == ' ' || isDirSeparator(*path); path++) {}
+  *ptr = path;
+  return hashName(fname);
+
+ fail:
+  return false;
 }
 //------------------------------------------------------------------------------
 int ExFatFile::peek() {
@@ -675,7 +627,7 @@ int ExFatFile::read(void* buf, size_t count) {
   return -1;
 }
 //------------------------------------------------------------------------------
-bool ExFatFile::remove(const ExChar_t* path) {
+bool ExFatFile::remove(const char* path) {
   ExFatFile file;
   if (!file.open(this, path, O_WRONLY)) {
     DBG_FAIL_MACRO;

+ 94 - 80
src/ExFatLib/ExFatFile.h

@@ -39,51 +39,10 @@
 #include "ExFatPartition.h"
 
 class ExFatVolume;
-
 //------------------------------------------------------------------------------
 /** Expression for path name separator. */
 #define isDirSeparator(c) ((c) == '/')
 //------------------------------------------------------------------------------
-/** test for legal character.
- *
- * \param[in] c character to be tested.
- *
- * \return true for legal character else false.
- */
-inline bool lfnLegalChar(ExChar_t c) {
-  if (c == '/' || c == '\\' || c == '"' || c == '*' ||
-      c == ':' || c == '<' || c == '>' || c == '?' || c == '|') {
-    return false;
-  }
-#if USE_EXFAT_UNICODE_NAMES
-  return 0X1F < c;
-#else  // USE_EXFAT_UNICODE_NAMES
-  return 0X1F < c && c < 0X7F;
-#endif  // USE_EXFAT_UNICODE_NAMES
-}
-//------------------------------------------------------------------------------
-/**
- * \struct ExName_t
- * \brief Internal type for file name - do not use in user apps.
- */
-struct ExName_t {
-  /** length of Long File Name */
-  size_t len;
-  /** Long File Name start. */
-  const ExChar_t* lfn;
-};
-//------------------------------------------------------------------------------
-/**
- * \struct ExFatPos_t
- * \brief Internal type for file position - do not use in user apps.
- */
-struct ExFatPos_t {
-  /** stream position */
-  uint64_t position;
-  /** cluster for position */
-  uint32_t cluster;
-};
-//------------------------------------------------------------------------------
 /**
  * \class ExFatFile
  * \brief Basic file class.
@@ -153,7 +112,7 @@ class ExFatFile {
   /** Check for contiguous file and return its raw sector range.
    *
    * \param[out] bgnSector the first sector address for the file.
-   * \param[out] endSector the last  sector address for the file.
+   * \param[out] endSector the last sector address for the file.
    *
    * Parameters may be nullptr.
    *
@@ -173,15 +132,15 @@ class ExFatFile {
    *
    * The calling instance must be an open directory file.
    *
-   * dirFile.exists("TOFIND.TXT") searches for "TOFIND.TXT" in  the directory
+   * dirFile.exists("TOFIND.TXT") searches for "TOFIND.TXT" in the directory
    * dirFile.
    *
    * \return true if the file exists else false.
    */
-  bool exists(const ExChar_t* path) {
-    ExFatFile file;
-    return file.open(this, path, O_RDONLY);
-  }
+    bool exists(const char* path) {
+      ExFatFile file;
+      return file.open(this, path, O_RDONLY);
+    }
   /** get position for streams
    * \param[out] pos struct to receive position
    */
@@ -247,13 +206,35 @@ class ExFatFile {
    */
   bool getModifyDateTime(uint16_t* pdate, uint16_t* ptime);
   /**
-   * Get a file's name followed by a zero byte.
+   * Get a file's name followed by a zero.
+   *
+   * \param[out] name An array of characters for the file's name.
+   * \param[in] size The size of the array in characters.
+   * \return the name length.
+   */
+  size_t getName(char* name, size_t size) {
+#if USE_UTF8_LONG_NAMES
+    return getName8(name, size);
+#else  // USE_UTF8_LONG_NAMES
+    return getName7(name, size);
+#endif  // USE_UTF8_LONG_NAMES
+  }
+  /**
+   * Get a file's ASCII name followed by a zero.
    *
    * \param[out] name An array of characters for the file's name.
    * \param[in] size The size of the array in characters.
    * \return the name length.
    */
-  size_t getName(ExChar_t* name, size_t size);
+  size_t getName7(char* name, size_t size);
+  /**
+   * Get a file's UTF-8 name followed by a zero.
+   *
+   * \param[out] name An array of characters for the file's name.
+   * \param[in] size The size of the array in characters.
+   * \return the name length.
+   */
+  size_t getName8(char* name, size_t size);
   /** \return value of writeError */
   bool getWriteError() const {
     return isOpen() ? m_error & WRITE_ERROR : true;
@@ -319,7 +300,7 @@ class ExFatFile {
    *
    * \return true for success or false for failure.
    */
-  bool mkdir(ExFatFile* parent, const ExChar_t* path, bool pFlag = true);
+  bool mkdir(ExFatFile* parent, const char* path, bool pFlag = true);
   /** Open a file or directory by name.
    *
    * \param[in] dirFile An open directory containing the file to be opened.
@@ -364,7 +345,7 @@ class ExFatFile {
    *
    * \return true for success or false for failure.
    */
-  bool open(ExFatFile* dirFile, const ExChar_t* path, oflag_t oflag);
+  bool open(ExFatFile* dirFile, const char* path, oflag_t oflag);
   /** Open a file in the volume working directory.
    *
    * \param[in] vol Volume where the file is located.
@@ -376,7 +357,7 @@ class ExFatFile {
    *
    * \return true for success or false for failure.
    */
-  bool open(ExFatVolume* vol, const ExChar_t* path, int oflag);
+  bool open(ExFatVolume* vol, const char* path, oflag_t oflag);
   /** Open a file by index.
    *
    * \param[in] dirFile An open ExFatFile instance for the directory.
@@ -385,7 +366,7 @@ class ExFatFile {
    * opened.  The value for \a index is (directory file position)/32.
    *
    * \param[in] oflag bitwise-inclusive OR of open flags.
-   *            See see ExFatFile::open(ExFatFile*, const ExChar_t*, uint8_t).
+   *            See see ExFatFile::open(ExFatFile*, const char*, uint8_t).
    *
    * See open() by path for definition of flags.
    * \return true for success or false for failure.
@@ -400,7 +381,7 @@ class ExFatFile {
    *
    * \return true for success or false for failure.
    */
-  bool open(const ExChar_t* path, int oflag = O_RDONLY);
+  bool open(const char* path, oflag_t oflag = O_RDONLY);
   /** Open the next file or subdirectory in a directory.
    *
    * \param[in] dirFile An open instance for the directory
@@ -520,12 +501,32 @@ class ExFatFile {
    */
   size_t printModifyDateTime(print_t* pr);
   /** Print a file's name
+   *
+   * \param[in] pr Print stream for output.
+   *
+   * \return length for success or zero for failure.
+   */
+  size_t printName(print_t* pr) {
+#if USE_UTF8_LONG_NAMES
+    return printName8(pr);
+#else  // USE_UTF8_LONG_NAMES
+    return printName7(pr);
+#endif  // USE_UTF8_LONG_NAMES
+  }
+  /** Print a file's ASCII name
    *
    * \param[in] pr Print stream for output.
    *
    * \return true for success or false for failure.
    */
-  size_t printName(print_t* pr);
+  size_t printName7(print_t* pr);
+  /** Print a file's UTF-8 name
+   *
+   * \param[in] pr Print stream for output.
+   *
+   * \return true for success or false for failure.
+   */
+  size_t printName8(print_t* pr);
   /** Read the next byte from a file.
    *
    * \return For success read returns the next byte in the file as an int.
@@ -572,14 +573,14 @@ class ExFatFile {
    *
    * \return true for success or false for failure.
    */
-  bool remove(const ExChar_t* path);
+  bool remove(const char* path);
    /** Rename a file or subdirectory.
    *
    * \param[in] newPath New path name for the file/directory.
    *
    * \return true for success or false for failure.
    */
-  bool rename(const ExChar_t* newPath);
+  bool rename(const char* newPath);
    /** Rename a file or subdirectory.
    *
    * \param[in] dirFile Directory for the new path.
@@ -587,7 +588,7 @@ class ExFatFile {
    *
    * \return true for success or false for failure.
    */
-  bool rename(ExFatFile* dirFile, const ExChar_t* newPath);
+  bool rename(ExFatFile* dirFile, const char* newPath);
   /** Set the file's current position to zero. */
   void rewind() {
     seekSet(0);
@@ -627,6 +628,8 @@ class ExFatFile {
    * \return true for success or false for failure.
    */
   bool seekSet(uint64_t pos);
+  /** \return directory set count */
+  uint8_t setCount() const {return m_setCount;}
   /** The sync() call causes all modified data and directory fields
    * to be written to the storage device.
    *
@@ -714,37 +717,48 @@ class ExFatFile {
    * \param[in] count Number of bytes to write.
    *
    * \return For success write() returns the number of bytes written, always
-   * \a count.
+   * \a count. If an error occurs, write() returns zero and writeError is set.
    */
   size_t write(const void* buf, size_t count);
-  //============================================================================
-#if USE_EXFAT_UNICODE_NAMES
-  // Not Implemented when Unicode is selected.
-  bool exists(const char* path);
-  size_t getName(char *name, size_t size);
-  bool mkdir(ExFatFile* parent, const char* path, bool pFlag = true);
-  bool open(ExFatVolume* vol, const char* path, int oflag);
-  bool open(ExFatFile* dir, const char* path, int oflag);
-  bool open(const char* path, int oflag = O_RDONLY);
-  bool remove(const char* path);
-  bool rename(const char* newPath);
-  bool rename(ExFatFile* dirFile, const char* newPath);
-#endif  // USE_EXFAT_UNICODE_NAMES
+//------------------------------------------------------------------------------
+#if ENABLE_ARDUINO_SERIAL
+  /** List directory contents.
+   *
+   * \param[in] flags The inclusive OR of
+   *
+   * LS_DATE - %Print file modification date
+   *
+   * LS_SIZE - %Print file size.
+   *
+   * LS_R - Recursive list of subdirectories.
+   *
+   * \return true for success or false for failure.
+   */
+  bool ls(uint8_t flags = 0) {
+    return ls(&Serial, flags);
+  }
+  /** Print a file's name.
+   *
+   * \return length for success or zero for failure.
+   */
+  size_t printName() {
+    return ExFatFile::printName(&Serial);
+  }
+#endif  // ENABLE_ARDUINO_SERIAL
 
  private:
   /** ExFatVolume allowed access to private members. */
   friend class ExFatVolume;
   bool addCluster();
   bool addDirCluster();
-  uint8_t setCount() const {return m_setCount;}
+  bool cmpName(const DirName_t* dirName, ExName_t* fname);
+  uint8_t* dirCache(uint8_t set, uint8_t options);
+  bool hashName(ExName_t* fname);
   bool mkdir(ExFatFile* parent, ExName_t* fname);
-  bool openRootFile(ExFatFile* dir,
-                    const ExChar_t* name, uint8_t nameLength, oflag_t oflag);
-  bool open(ExFatFile* dirFile, ExName_t* fname, oflag_t oflag) {
-    return openRootFile(dirFile, fname->lfn, fname->len, oflag);
-  }
-  bool parsePathName(const ExChar_t* path,
-                            ExName_t* fname, const ExChar_t** ptr);
+
+  bool openPrivate(ExFatFile* dir, ExName_t* fname, oflag_t oflag);
+  bool parsePathName(const char* path,
+                            ExName_t* fname, const char** ptr);
   uint32_t curCluster() const {return m_curCluster;}
   ExFatVolume* volume() const {return m_vol;}
   bool syncDir();

+ 57 - 10
src/ExFatLib/ExFatFilePrint.cpp

@@ -25,7 +25,6 @@
 #define DBG_FILE "ExFatFilePrint.cpp"
 #include "../common/DebugMacros.h"
 #include "ExFatFile.h"
-#include "upcase.h"
 #include "ExFatVolume.h"
 //------------------------------------------------------------------------------
 bool ExFatFile::ls(print_t* pr) {
@@ -143,9 +142,8 @@ size_t ExFatFile::printModifyDateTime(print_t* pr) {
   return 0;
 }
 //------------------------------------------------------------------------------
-size_t ExFatFile::printName(print_t* pr) {
+size_t ExFatFile::printName7(print_t* pr) {
   DirName_t* dn;
-  DirPos_t pos = m_dirPos;
   size_t n = 0;
   uint8_t in;
   uint8_t buf[15];
@@ -153,13 +151,9 @@ size_t ExFatFile::printName(print_t* pr) {
       DBG_FAIL_MACRO;
       goto fail;
   }
-  for (uint8_t is = 1; is < m_setCount; is++) {
-    if (m_vol->dirSeek(&pos, is == 1 ? 64: 32) != 1) {
-      DBG_FAIL_MACRO;
-      goto fail;
-    }
+  for (uint8_t is = 2; is <= m_setCount; is++) {
     dn = reinterpret_cast<DirName_t*>
-         (m_vol->dirCache(&pos, FsCache::CACHE_FOR_READ));
+         (dirCache(is, FsCache::CACHE_FOR_READ));
     if (!dn || dn->type != EXFAT_TYPE_NAME) {
       DBG_FAIL_MACRO;
       goto fail;
@@ -169,7 +163,7 @@ size_t ExFatFile::printName(print_t* pr) {
       if (!c) {
         break;
       }
-      buf[in] = c < 0X7f ? c : '?';
+      buf[in] = c < 0X7F ? c : '?';
       n++;
     }
     pr->write(buf, in);
@@ -179,3 +173,56 @@ size_t ExFatFile::printName(print_t* pr) {
  fail:
   return 0;
 }
+//------------------------------------------------------------------------------
+size_t ExFatFile::printName8(print_t *pr) {
+  DirName_t* dn;
+  uint16_t hs = 0;
+  uint32_t cp;
+  size_t n = 0;
+  uint8_t in;
+  char buf[5];
+  if (!isOpen()) {
+      DBG_FAIL_MACRO;
+      goto fail;
+  }
+  for (uint8_t is = 2; is <= m_setCount; is++) {
+    dn = reinterpret_cast<DirName_t*>
+         (dirCache(is, FsCache::CACHE_FOR_READ));
+    if (!dn || dn->type != EXFAT_TYPE_NAME) {
+      DBG_FAIL_MACRO;
+      goto fail;
+    }
+    for (in = 0; in < 15; in++) {
+      uint16_t c = getLe16(dn->unicode + 2*in);
+      if (hs) {
+        if (!FsUtf::isLowSurrogate(c)) {
+          DBG_FAIL_MACRO;
+          goto fail;
+        }
+        cp = FsUtf::u16ToCp(hs, c);
+        hs = 0;
+      } else if (!FsUtf::isSurrogate(c)) {
+        if (c == 0) {
+          break;
+        }
+        cp = c;
+      } else if (FsUtf::isHighSurrogate(c)) {
+        hs = c;
+        continue;
+      } else {
+        DBG_FAIL_MACRO;
+        goto fail;
+      }
+      char* str = FsUtf::cpToMb(cp, buf, buf + sizeof(buf));
+      if (!str) {
+        DBG_FAIL_MACRO;
+        goto fail;
+      }
+      n += pr->write(buf, str - buf);
+    }
+  }
+  return n;
+
+ fail:
+  return 0;
+}

+ 15 - 40
src/ExFatLib/ExFatFileWrite.cpp

@@ -26,10 +26,9 @@
 #include "../common/DebugMacros.h"
 #include "ExFatFile.h"
 #include "ExFatVolume.h"
-#include "upcase.h"
 //==============================================================================
 #if READ_ONLY
-bool ExFatFile::mkdir(ExFatFile* parent, const ExChar_t* path, bool pFlag) {
+bool ExFatFile::mkdir(ExFatFile* parent, const char* path, bool pFlag) {
   (void) parent;
   (void)path;
   (void)pFlag;
@@ -39,11 +38,11 @@ bool ExFatFile::preAllocate(uint64_t length) {
   (void)length;
   return false;
 }
-bool ExFatFile::rename(const ExChar_t* newPath) {
+bool ExFatFile::rename(const char* newPath) {
   (void)newPath;
   return false;
 }
-bool ExFatFile::rename(ExFatFile* dirFile, const ExChar_t* newPath) {
+bool ExFatFile::rename(ExFatFile* dirFile, const char* newPath) {
   (void)dirFile;
   (void)newPath;
   return false;
@@ -156,7 +155,7 @@ bool ExFatFile::addDirCluster() {
   return false;
 }
 //------------------------------------------------------------------------------
-bool ExFatFile::mkdir(ExFatFile* parent, const ExChar_t* path, bool pFlag) {
+bool ExFatFile::mkdir(ExFatFile* parent, const char* path, bool pFlag) {
   ExName_t fname;
   ExFatFile tmpDir;
 
@@ -182,7 +181,7 @@ bool ExFatFile::mkdir(ExFatFile* parent, const ExChar_t* path, bool pFlag) {
     if (!*path) {
       break;
     }
-    if (!open(parent, &fname, O_RDONLY)) {
+    if (!openPrivate(parent, &fname, O_RDONLY)) {
       if (!pFlag || !mkdir(parent, &fname)) {
         DBG_FAIL_MACRO;
         goto fail;
@@ -204,12 +203,11 @@ bool ExFatFile::mkdir(ExFatFile* parent, ExName_t* fname) {
     goto fail;
   }
   // create a normal file
-  if (!open(parent, fname, O_CREAT | O_EXCL | O_RDWR)) {
+  if (!openPrivate(parent, fname, O_CREAT | O_EXCL | O_RDWR)) {
     DBG_FAIL_MACRO;
     goto fail;
   }
   // convert file to directory
-
   m_attributes = FILE_ATTR_SUBDIR;
 
   // allocate and zero first cluster
@@ -259,7 +257,6 @@ bool ExFatFile::preAllocate(uint64_t length) {
 }
 //------------------------------------------------------------------------------
 bool ExFatFile::remove() {
-  DirPos_t pos = m_dirPos;
   uint8_t* cache;
   if (!isWritable()) {
     DBG_FAIL_MACRO;
@@ -281,12 +278,8 @@ bool ExFatFile::remove() {
     }
   }
 
-  for (uint8_t i = 0; i <= m_setCount; i++) {
-    if (i && m_vol->dirSeek(&pos, 32) != 1) {
-      DBG_FAIL_MACRO;
-      goto fail;
-    }
-    cache = m_vol->dirCache(&pos, FsCache::CACHE_FOR_WRITE);
+  for (uint8_t is = 0; is <= m_setCount; is++) {
+    cache = dirCache(is, FsCache::CACHE_FOR_WRITE);
     if (!cache) {
       DBG_FAIL_MACRO;
       goto fail;
@@ -305,11 +298,11 @@ bool ExFatFile::remove() {
   return false;
 }
 //------------------------------------------------------------------------------
-bool ExFatFile::rename(const ExChar_t* newPath) {
+bool ExFatFile::rename(const char* newPath) {
   return rename(m_vol->vwd(), newPath);
 }
 //------------------------------------------------------------------------------
-bool ExFatFile::rename(ExFatFile* dirFile, const ExChar_t* newPath) {
+bool ExFatFile::rename(ExFatFile* dirFile, const char* newPath) {
   ExFatFile file;
   ExFatFile oldFile;
 
@@ -403,12 +396,9 @@ bool ExFatFile::syncDir() {
   DirStream_t* ds;
   uint8_t* cache;
   uint16_t checksum = 0;
-  uint8_t setCount = 0;
-
-  DirPos_t pos = m_dirPos;
 
-  for (uint8_t i = 0;; i++) {
-    cache = m_vol->dirCache(&pos, FsCache::CACHE_FOR_READ);
+  for (uint8_t is = 0; is <= m_setCount ; is++) {
+    cache = dirCache(is, FsCache::CACHE_FOR_READ);
     if (!cache) {
       DBG_FAIL_MACRO;
       goto fail;
@@ -416,7 +406,6 @@ bool ExFatFile::syncDir() {
     switch (cache[0]) {
       case EXFAT_TYPE_FILE:
         df = reinterpret_cast<DirFile_t*>(cache);
-        setCount = df->setCount;
         setLe16(df->attributes, m_attributes & FILE_ATTR_COPY);
         if (FsDateTime::callback) {
           uint16_t date, time;
@@ -453,11 +442,6 @@ bool ExFatFile::syncDir() {
         break;
     }
     checksum = exFatDirChecksum(cache, checksum);
-    if (i == setCount) break;
-    if (m_vol->dirSeek(&pos, 32) != 1) {
-      DBG_FAIL_MACRO;
-      goto fail;
-    }
   }
   df = reinterpret_cast<DirFile_t*>
        (m_vol->dirCache(&m_dirPos, FsCache::CACHE_FOR_WRITE));
@@ -482,11 +466,9 @@ bool ExFatFile::timestamp(uint8_t flags, uint16_t year, uint8_t month,
   DirFile_t* df;
   uint8_t* cache;
   uint16_t checksum = 0;
-  uint8_t setCount = 0;
   uint16_t date;
   uint16_t time;
   uint8_t ms10;
-  DirPos_t pos;
 
   if (!isFile()
       || year < 1980
@@ -510,10 +492,9 @@ bool ExFatFile::timestamp(uint8_t flags, uint16_t year, uint8_t month,
   date = FS_DATE(year, month, day);
   time = FS_TIME(hour, minute, second);
   ms10 = second & 1 ? 100 : 0;
-  pos = m_dirPos;
 
-  for (uint8_t i = 0;; i++) {
-    cache = m_vol->dirCache(&pos, FsCache::CACHE_FOR_READ);
+  for (uint8_t is = 0;; is++) {
+    cache = dirCache(is, FsCache::CACHE_FOR_READ);
     if (!cache) {
       DBG_FAIL_MACRO;
       goto fail;
@@ -521,7 +502,6 @@ bool ExFatFile::timestamp(uint8_t flags, uint16_t year, uint8_t month,
     switch (cache[0]) {
       case EXFAT_TYPE_FILE:
         df = reinterpret_cast<DirFile_t*>(cache);
-        setCount = df->setCount;
         setLe16(df->attributes, m_attributes & FILE_ATTR_COPY);
         m_vol->dataCacheDirty();
         if (flags & T_ACCESS) {
@@ -552,11 +532,6 @@ bool ExFatFile::timestamp(uint8_t flags, uint16_t year, uint8_t month,
         break;
     }
     checksum = exFatDirChecksum(cache, checksum);
-    if (i == setCount) break;
-    if (m_vol->dirSeek(&pos, 32) != 1) {
-      DBG_FAIL_MACRO;
-      goto fail;
-    }
   }
   df = reinterpret_cast<DirFile_t*>
        (m_vol->dirCache(&m_dirPos, FsCache::CACHE_FOR_WRITE));
@@ -780,6 +755,6 @@ size_t ExFatFile::write(const void* buf, size_t nbyte) {
  fail:
   // return for write error
   m_error |= WRITE_ERROR;
-  return -1;
+  return 0;
 }
 #endif  // READ_ONLY

+ 3 - 1
src/ExFatLib/ExFatFormatter.cpp

@@ -24,6 +24,8 @@
  */
 #define DBG_FILE "ExFatFormatter.cpp"
 #include "../common/DebugMacros.h"
+#include "../common/upcase.h"
+#include "../common/FsStructs.h"
 #include "ExFatFormatter.h"
 //------------------------------------------------------------------------------
 // Formatter assumes 512 byte sectors.
@@ -257,7 +259,7 @@ bool ExFatFormatter::format(BlockDevice* dev, uint8_t* secBuf, print_t* pr) {
   label = reinterpret_cast<DirLabel_t*>(secBuf);
   label->type = EXFAT_TYPE_LABEL & 0X7F;
 
-  // bitmap directory  entry.
+  // bitmap directory entry.
   dbm = reinterpret_cast<DirBitmap_t*>(secBuf + 32);
   dbm->type = EXFAT_TYPE_BITMAP;
   setLe32(dbm->firstCluster, BITMAP_CLUSTER);

+ 0 - 1
src/ExFatLib/ExFatFormatter.h

@@ -27,7 +27,6 @@
 #include "ExFatConfig.h"
 #include "../common/SysCall.h"
 #include "../common/BlockDevice.h"
-#include "upcase.h"
 /**
  * \class ExFatFormatter
  * \brief Format an exFAT volume.

+ 189 - 0
src/ExFatLib/ExFatName.cpp

@@ -0,0 +1,189 @@
+/**
+ * Copyright (c) 2011-2020 Bill Greiman
+ * This file is part of the SdFat library for SD memory cards.
+ *
+ * MIT License
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+#define DBG_FILE "ExFatName.cpp"
+#include "../common/DebugMacros.h"
+#include "../common/upcase.h"
+#include "ExFatFile.h"
+//------------------------------------------------------------------------------
+static char toUpper(char c) {
+  return 'a' <= c && c <= 'z' ? c - 'a' + 'A' : c;
+}
+//------------------------------------------------------------------------------
+inline uint16_t exFatHash(char c, uint16_t hash) {
+  uint8_t u = toUpper(c);
+  hash = ((hash << 15) | (hash >> 1)) + u;
+  hash = ((hash << 15) | (hash >> 1));
+  return hash;
+}
+//------------------------------------------------------------------------------
+inline uint16_t exFatHash(uint16_t u, uint16_t hash) {
+  uint16_t c = toUpcase(u);
+  hash = ((hash << 15) | (hash >> 1)) + (c & 0XFF);
+  hash = ((hash << 15) | (hash >> 1)) + (c >> 8);
+  return hash;
+}
+//------------------------------------------------------------------------------
+bool ExFatFile::cmpName(const DirName_t* dirName, ExName_t* fname) {
+  for (uint8_t i = 0; i < 15; i++) {
+    uint16_t u = getLe16(dirName->unicode + 2*i);
+    if (fname->atEnd()) {
+      return u == 0;
+    }
+#if USE_UTF8_LONG_NAMES
+    uint16_t cp = fname->get16();
+    if (toUpcase(cp) != toUpcase(u)) {
+       return false;
+    }
+#else  // USE_UTF8_LONG_NAMES
+    char c = fname->getch();
+    if (u >= 0x7F || toUpper(c) != toUpper(u)) {
+      return false;
+    }
+#endif  // USE_UTF8_LONG_NAMES
+  }
+  return true;
+}
+//------------------------------------------------------------------------------
+size_t ExFatFile::getName7(char* name, size_t count) {
+  DirName_t* dn;
+  size_t n = 0;
+  if (!isOpen()) {
+      DBG_FAIL_MACRO;
+      goto fail;
+  }
+  for (uint8_t is = 2; is <= m_setCount; is++) {
+    dn = reinterpret_cast<DirName_t*>
+         (dirCache(is, FsCache::CACHE_FOR_READ));
+    if (!dn || dn->type != EXFAT_TYPE_NAME) {
+      DBG_FAIL_MACRO;
+      goto fail;
+    }
+    for (uint8_t in = 0; in < 15; in++) {
+      uint16_t c = getLe16(dn->unicode + 2*in);
+      if (c == 0 || (n + 1) >= count) {
+        goto done;
+      }
+      name[n++] = c < 0X7F ? c : '?';
+    }
+  }
+ done:
+  name[n] = 0;
+  return n;
+
+ fail:
+  *name = 0;
+  return 0;
+}
+//------------------------------------------------------------------------------
+size_t ExFatFile::getName8(char* name, size_t count) {
+  char* end = name + count;
+  char* str = name;
+  char* ptr;
+  DirName_t* dn;
+  uint16_t hs = 0;
+  uint32_t cp;
+  if (!isOpen()) {
+      DBG_FAIL_MACRO;
+      goto fail;
+  }
+  for (uint8_t is = 2; is <= m_setCount; is++) {
+    dn = reinterpret_cast<DirName_t*>
+         (dirCache(is, FsCache::CACHE_FOR_READ));
+    if (!dn || dn->type != EXFAT_TYPE_NAME) {
+      DBG_FAIL_MACRO;
+      goto fail;
+    }
+    for (uint8_t in = 0; in < 15; in++) {
+      uint16_t c = getLe16(dn->unicode + 2*in);
+      if (hs) {
+        if (!FsUtf::isLowSurrogate(c)) {
+          DBG_FAIL_MACRO;
+          goto fail;
+        }
+        cp = FsUtf::u16ToCp(hs, c);
+        hs = 0;
+      } else if (!FsUtf::isSurrogate(c)) {
+        if (c == 0) {
+          goto done;
+        }
+        cp = c;
+      } else if (FsUtf::isHighSurrogate(c)) {
+        hs = c;
+        continue;
+      } else {
+        DBG_FAIL_MACRO;
+        goto fail;
+      }
+      // Save space for zero byte.
+      ptr = FsUtf::cpToMb(cp, str, end - 1);
+      if (!ptr) {
+        // Truncate name.  Could goto fail.
+        goto done;
+      }
+      str = ptr;
+    }
+  }
+ done:
+  *str = '\0';
+  return str - name;
+
+ fail:
+  *name = 0;
+  return 0;
+}
+//------------------------------------------------------------------------------
+bool ExFatFile::hashName(ExName_t* fname) {
+  uint16_t hash = 0;
+  fname->reset();
+#if USE_UTF8_LONG_NAMES
+  fname->nameLength = 0;
+  while (!fname->atEnd()) {
+    uint16_t u = fname->get16();
+    if (u == 0XFFFF) {
+    DBG_FAIL_MACRO;
+      goto fail;
+    }
+    hash = exFatHash(u, hash);
+    fname->nameLength++;
+  }
+#else  // USE_UTF8_LONG_NAMES
+  while (!fname->atEnd()) {
+    // Convert to byte for smaller exFatHash.
+    char c = fname->getch();
+    hash = exFatHash(c, hash);
+  }
+  fname->nameLength = fname->end - fname->begin;
+#endif  // USE_UTF8_LONG_NAMES
+  fname->nameHash = hash;
+  if (!fname->nameLength || fname->nameLength > EXFAT_MAX_NAME_LENGTH) {
+    DBG_FAIL_MACRO;
+    goto fail;
+  }
+  return true;
+
+ fail:
+  return false;
+}
+

+ 4 - 7
src/ExFatLib/ExFatPartition.cpp

@@ -164,7 +164,8 @@ int8_t ExFatPartition::dirSeek(DirPos_t* pos, uint32_t offset) {
   return 1;
 }
 //------------------------------------------------------------------------------
-uint8_t ExFatPartition::fatGet(uint32_t cluster, uint32_t* value) {
+// return -1 error, 0 EOC, 1 OK
+int8_t ExFatPartition::fatGet(uint32_t cluster, uint32_t* value) {
   uint8_t* cache;
   uint32_t next;
   uint32_t sector;
@@ -180,12 +181,8 @@ uint8_t ExFatPartition::fatGet(uint32_t cluster, uint32_t* value) {
     return -1;
   }
   next = getLe32(cache + ((cluster << 2) & m_sectorMask));
-
-  if (next == EXFAT_EOC) {
-    return 0;
-  }
   *value = next;
-  return 1;
+  return next == EXFAT_EOC ? 0 : 1;
 }
 //------------------------------------------------------------------------------
 bool ExFatPartition::fatPut(uint32_t cluster, uint32_t value) {
@@ -222,7 +219,7 @@ bool ExFatPartition::freeChain(uint32_t cluster) {
       DBG_FAIL_MACRO;
       goto fail;
     }
-    if ((cluster + 1) != next || status == 0) {
+    if (status == 0 || (cluster + 1) != next) {
       if (!bitmapModify(start, cluster - start + 1, 0)) {
         DBG_FAIL_MACRO;
         goto fail;

+ 2 - 3
src/ExFatLib/ExFatPartition.h

@@ -98,8 +98,7 @@ class ExFatPartition {
   /** \return the number of sectors in a cluster. */
   uint32_t sectorsPerCluster() const {return 1UL << m_sectorsPerClusterShift;}
 #ifndef DOXYGEN_SHOULD_SKIP_THIS
-  // Use sectorsPerCluster(). blocksPerCluster() will be removed in the future.
-  uint32_t blocksPerCluster() __attribute__ ((deprecated)) {return sectorsPerCluster();} //NOLINT
+  uint32_t __attribute__((error("use sectorsPerCluster()"))) blocksPerCluster();
 #endif  // DOXYGEN_SHOULD_SKIP_THIS
   /** \return the power of two for sectors per cluster. */
   uint8_t  sectorsPerClusterShift() const {return m_sectorsPerClusterShift;}
@@ -159,7 +158,7 @@ class ExFatPartition {
   }
   uint8_t* dirCache(DirPos_t* pos, uint8_t options);
   int8_t dirSeek(DirPos_t* pos, uint32_t offset);
-  uint8_t fatGet(uint32_t cluster, uint32_t* value);
+  int8_t fatGet(uint32_t cluster, uint32_t* value);
   bool fatPut(uint32_t cluster, uint32_t value);
   uint32_t chainSize(uint32_t cluster);
   bool freeChain(uint32_t cluster);

+ 15 - 16
src/ExFatLib/ExFatTypes.h

@@ -25,22 +25,9 @@
 #ifndef ExFatTypes_h
 #define ExFatTypes_h
 #include "ExFatConfig.h"
-
-#if __cplusplus < 201103
-#warning no char16_t
-typedef uint16_t ExChar16_t;
-//  #error C++11 Support required
-#else  // __cplusplus < 201103
-typedef char16_t ExChar16_t;
-#endif  // __cplusplus < 201103
-
-#if USE_EXFAT_UNICODE_NAMES
-/** exFAT API character type */
-typedef ExChar16_t ExChar_t;
-#else  // USE_EXFAT_UNICODE_NAMES
-/** exFAT API character type */
-typedef char ExChar_t;
-#endif  // USE_EXFAT_UNICODE_NAMES
+#include "../common/FsUtf.h"
+#include "../common/FsName.h"
+//------------------------------------------------------------------------------
 /**
  * \struct DirPos_t
  * \brief Internal type for position in directory file.
@@ -53,4 +40,16 @@ struct DirPos_t {
   /** directory is contiguous */
   bool     isContiguous;
 };
+//------------------------------------------------------------------------------
+/**
+ * \class ExName_t
+ * \brief Internal type for file name - do not use in user apps.
+ */
+class ExName_t : public FsName {
+ public:
+  /** Length of UTF-16 name */
+  size_t nameLength;
+  /** Hash for UTF-16 name */
+  uint16_t nameHash;
+};
 #endif  // ExFatTypes_h

+ 5 - 1
src/ExFatLib/ExFatVolume.cpp

@@ -22,15 +22,19 @@
  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  * DEALINGS IN THE SOFTWARE.
  */
+#define DBG_FILE "ExFatVolume.cpp"
+#include "../common/DebugMacros.h"
 #include "ExFatVolume.h"
 ExFatVolume* ExFatVolume::m_cwv = nullptr;
 //-----------------------------------------------------------------------------
-bool ExFatVolume::chdir(const ExChar_t* path) {
+bool ExFatVolume::chdir(const char* path) {
   ExFatFile dir;
   if (!dir.open(vwd(), path, O_RDONLY)) {
+    DBG_FAIL_MACRO;
     goto fail;
   }
   if (!dir.isDir()) {
+    DBG_FAIL_MACRO;
     goto fail;
   }
   m_vwd = dir;

+ 10 - 20
src/ExFatLib/ExFatVolume.h

@@ -66,7 +66,7 @@ class ExFatVolume : public ExFatPartition {
    * \param[in] path Path for volume working directory.
    * \return true for success or false for failure.
    */
-  bool chdir(const ExChar_t* path);
+  bool chdir(const char* path);
 
   /** Change global working volume to this volume. */
   void chvol() {m_cwv = this;}
@@ -78,11 +78,10 @@ class ExFatVolume : public ExFatPartition {
    *
    * \return true if the file exists else false.
    */
-  bool exists(const ExChar_t* path) {
+  bool exists(const char* path) {
     ExFatFile tmp;
     return tmp.open(this, path, O_RDONLY);
   }
-
   //----------------------------------------------------------------------------
   /** List the directory contents of the root directory.
    *
@@ -117,7 +116,7 @@ class ExFatVolume : public ExFatPartition {
    *
    * \return true for success or false for failure.
    */
-  bool ls(print_t* pr, const ExChar_t* path, uint8_t flags) {
+  bool ls(print_t* pr, const char* path, uint8_t flags) {
     ExFatFile dir;
     return dir.open(this, path, O_RDONLY) && dir.ls(pr, flags);
   }
@@ -129,7 +128,7 @@ class ExFatVolume : public ExFatPartition {
    *
    * \return true for success or false for failure.
    */
-  bool mkdir(const ExChar_t* path, bool pFlag = true) {
+  bool mkdir(const char* path, bool pFlag = true) {
     ExFatFile sub;
     return sub.mkdir(vwd(), path, pFlag);
   }
@@ -139,7 +138,7 @@ class ExFatVolume : public ExFatPartition {
    * \param[in] oflag open flags.
    * \return a ExFile object.
    */
-  ExFile open(const ExChar_t* path, oflag_t oflag = O_RDONLY) {
+  ExFile open(const char* path, oflag_t oflag = O_RDONLY) {
     ExFile tmpFile;
     tmpFile.open(this, path, oflag);
     return tmpFile;
@@ -150,7 +149,7 @@ class ExFatVolume : public ExFatPartition {
    *
    * \return true for success or false for failure.
    */
-  bool remove(const ExChar_t* path) {
+  bool remove(const char* path) {
     ExFatFile tmp;
     return tmp.open(this, path, O_WRONLY) && tmp.remove();
   }
@@ -168,7 +167,7 @@ class ExFatVolume : public ExFatPartition {
    *
    * \return true for success or false for failure.
    */
-  bool rename(const ExChar_t* oldPath, const ExChar_t* newPath) {
+  bool rename(const char* oldPath, const char* newPath) {
     ExFatFile file;
     return file.open(vwd(), oldPath, O_RDONLY) && file.rename(vwd(), newPath);
   }
@@ -180,7 +179,7 @@ class ExFatVolume : public ExFatPartition {
    *
    * \return true for success or false for failure.
    */
-  bool rmdir(const ExChar_t* path) {
+  bool rmdir(const char* path) {
     ExFatFile sub;
     return sub.open(this, path, O_RDONLY) && sub.rmdir();
   }
@@ -192,7 +191,7 @@ class ExFatVolume : public ExFatPartition {
    *
    * \return true for success or false for failure.
    */
-  bool truncate(const ExChar_t* path, uint64_t length) {
+  bool truncate(const char* path, uint64_t length) {
     ExFatFile file;
     if (!file.open(this, path, O_WRONLY)) {
       return false;
@@ -236,7 +235,7 @@ class ExFatVolume : public ExFatPartition {
    *
    * \return true for success or false for failure.
    */
-  bool ls(const ExChar_t* path, uint8_t flags = 0) {
+  bool ls(const char* path, uint8_t flags = 0) {
     return ls(&Serial, path, flags);
   }
 #endif  // ENABLE_ARDUINO_SERIAL
@@ -327,15 +326,6 @@ class ExFatVolume : public ExFatPartition {
     return truncate(path.c_str(), length);
   }
 #endif  // ENABLE_ARDUINO_STRING
-  //============================================================================
-#if  USE_EXFAT_UNICODE_NAMES
-  // Not implemented when Unicode is selected.
-  bool exists(const char* path);
-  bool mkdir(const char* path, bool pFlag = true);
-  bool remove(const char* path);
-  bool rename(const char* oldPath, const char* newPath);
-  bool rmdir(const char* path);
-#endif  //  USE_EXFAT_UNICODE_NAMES
 
  private:
   friend ExFatFile;

+ 63 - 11
src/FatLib/FatDbg.cpp

@@ -26,6 +26,17 @@
 #include "FatFile.h"
 #ifndef DOXYGEN_SHOULD_SKIP_THIS
 //------------------------------------------------------------------------------
+static uint16_t getLfnChar(DirLfn_t* ldir, uint8_t i) {
+  if (i < 5) {
+    return getLe16(ldir->unicode1 + 2*i);
+  } else if (i < 11) {
+    return getLe16(ldir->unicode2 + 2*i - 10);
+  } else if (i < 13) {
+    return getLe16(ldir->unicode3 + 2*i - 22);
+  }
+  return 0;
+}
+//------------------------------------------------------------------------------
 static void printHex(print_t* pr, uint8_t h) {
   if (h < 16) {
     pr->write('0');
@@ -63,13 +74,32 @@ static void printHex(print_t* pr, uint32_t val) {
   }
 }
 //------------------------------------------------------------------------------
-static void printDir(print_t* pr, DirFat_t* dir) {
-  if (!dir->name[0] || dir->name[0] == FAT_NAME_DELETED) {
-    pr->println(F("Not Used"));
+template<typename Uint>
+static void printHexLn(print_t* pr, Uint val) {
+  printHex(pr, val);
+  pr->println();
+}
+//------------------------------------------------------------------------------
+bool printFatDir(print_t* pr, DirFat_t* dir) {
+  DirLfn_t* ldir = reinterpret_cast<DirLfn_t*>(dir);
+  if (!dir->name[0]) {
+    pr->println(F("Unused"));
+    return false;
+  } else if (dir->name[0] == FAT_NAME_DELETED) {
+    pr->println(F("Deleted"));
   } else if (isFileOrSubdir(dir)) {
-    pr->print(F("name: "));
+    pr->print(F("SFN: "));
+    for (uint8_t i = 0; i < 11; i++) {
+      printHex(pr, dir->name[i]);
+      pr->write(' ');
+    }
+    pr->write(' ');
     pr->write(dir->name, 11);
     pr->println();
+    pr->print(F("attributes: 0X"));
+    printHexLn(pr, dir->attributes);
+    pr->print(F("caseFlags: 0X"));
+    printHexLn(pr, dir->caseFlags);
     uint32_t fc = ((uint32_t)getLe16(dir->firstClusterHigh) << 16)
                  | getLe16(dir->firstClusterLow);
     pr->print(F("firstCluster: "));
@@ -77,24 +107,46 @@ static void printDir(print_t* pr, DirFat_t* dir) {
     pr->print(F("fileSize: "));
     pr->println(getLe32(dir->fileSize));
   } else if (isLongName(dir)) {
-    pr->println(F("LFN"));
+    pr->print(F("LFN: "));
+    for (uint8_t i = 0; i < 13; i++) {
+      uint16_t c = getLfnChar(ldir, i);
+      if (15 < c && c < 128) {
+        pr->print(static_cast<char>(c));
+      } else {
+        pr->print("0X");
+        pr->print(c, HEX);
+      }
+      pr->print(' ');
+    }
+    pr->println();
+    pr->print(F("order: 0X"));
+    pr->println(ldir->order, HEX);
+    pr->print(F("attributes: 0X"));
+    pr->println(ldir->attributes, HEX);
+    pr->print(F("checksum: 0X"));
+    pr->println(ldir->checksum, HEX);
   } else {
     pr->println(F("Other"));
   }
+  pr->println();
+  return true;
 }
 //------------------------------------------------------------------------------
-void FatPartition::dmpDirSector(print_t* pr, uint32_t sector) {
+bool FatPartition::dmpDirSector(print_t* pr, uint32_t sector) {
   DirFat_t dir[16];
   if (!readSector(sector, reinterpret_cast<uint8_t*>(dir))) {
     pr->println(F("dmpDir failed"));
-    return;
+    return false;
   }
   for (uint8_t i = 0; i < 16; i++) {
-    printDir(pr, dir + i);
+    if (!printFatDir(pr, dir + i)) {
+      return false;
+    }
   }
+  return true;
 }
 //------------------------------------------------------------------------------
-void FatPartition::dmpRootDir(print_t* pr) {
+bool FatPartition::dmpRootDir(print_t* pr, uint32_t n) {
   uint32_t sector;
   if (fatType() == 16) {
     sector = rootDirStart();
@@ -102,9 +154,9 @@ void FatPartition::dmpRootDir(print_t* pr) {
     sector = clusterStartSector(rootDirStart());
   } else {
     pr->println(F("dmpRootDir failed"));
-    return;
+    return false;
   }
-  dmpDirSector(pr, sector);
+  return dmpDirSector(pr, sector + n);
 }
 //------------------------------------------------------------------------------
 void FatPartition::dmpSector(print_t* pr, uint32_t sector, uint8_t bits) {

+ 32 - 29
src/FatLib/FatFile.cpp

@@ -308,7 +308,7 @@ bool FatFile::isBusy() {
 }
 //------------------------------------------------------------------------------
 bool FatFile::mkdir(FatFile* parent, const char* path, bool pFlag) {
-  fname_t fname;
+  FatName_t fname;
   FatFile tmpDir;
 
   if (isOpen() || !parent->isDir()) {
@@ -349,7 +349,7 @@ bool FatFile::mkdir(FatFile* parent, const char* path, bool pFlag) {
   return false;
 }
 //------------------------------------------------------------------------------
-bool FatFile::mkdir(FatFile* parent, fname_t* fname) {
+bool FatFile::mkdir(FatFile* parent, FatName_t* fname) {
   uint32_t sector;
   DirFat_t dot;
   DirFat_t* dir;
@@ -387,7 +387,7 @@ bool FatFile::mkdir(FatFile* parent, fname_t* fname) {
     DBG_FAIL_MACRO;
     goto fail;
   }
-  // change directory entry  attribute
+  // change directory entry attribute
   dir->attributes = FAT_ATTRIB_DIRECTORY;
 
   // make entry for '.'
@@ -429,7 +429,7 @@ bool FatFile::open(FatVolume* vol, const char* path, oflag_t oflag) {
 //------------------------------------------------------------------------------
 bool FatFile::open(FatFile* dirFile, const char* path, oflag_t oflag) {
   FatFile tmpDir;
-  fname_t fname;
+  FatName_t fname;
 
   // error if already open
   if (isOpen() || !dirFile->isDir()) {
@@ -458,7 +458,7 @@ bool FatFile::open(FatFile* dirFile, const char* path, oflag_t oflag) {
       break;
     }
     if (!open(dirFile, &fname, O_RDONLY)) {
-      DBG_FAIL_MACRO;
+      DBG_WARN_MACRO;
       goto fail;
     }
     tmpDir = *this;
@@ -477,11 +477,7 @@ bool FatFile::open(FatFile* dirFile, uint16_t index, oflag_t oflag) {
     DirLfn_t* ldir;
     uint8_t n = index < 20 ? index : 20;
     for (uint8_t i = 1; i <= n; i++) {
-      if (!dirFile->seekSet(32UL*(index - i))) {
-        DBG_FAIL_MACRO;
-        goto fail;
-      }
-      ldir = reinterpret_cast<DirLfn_t*>(dirFile->readDirCache());
+      ldir = reinterpret_cast<DirLfn_t*>(dirFile->cacheDir(index - i));
       if (!ldir) {
         DBG_FAIL_MACRO;
         goto fail;
@@ -598,6 +594,18 @@ bool FatFile::openCachedEntry(FatFile* dirFile, uint16_t dirIndex,
   return false;
 }
 //------------------------------------------------------------------------------
+bool FatFile::openCluster(FatFile* file) {
+  if (file->m_dirCluster == 0) {
+    return openRoot(file->m_vol);
+  }
+  memset(this, 0, sizeof(FatFile));
+  m_attributes = FILE_ATTR_SUBDIR;
+  m_flags = FILE_FLAG_READ;
+  m_vol = file->m_vol;
+  m_firstCluster = file->m_dirCluster;
+  return true;
+}
+//------------------------------------------------------------------------------
 bool FatFile::openNext(FatFile* dirFile, oflag_t oflag) {
   uint8_t checksum = 0;
   DirLfn_t* ldir;
@@ -684,6 +692,15 @@ bool FatFile::openRoot(FatVolume* vol) {
   return false;
 }
 //------------------------------------------------------------------------------
+int FatFile::peek() {
+  uint32_t curPosition = m_curPosition;
+  uint32_t curCluster = m_curCluster;
+  int c = read();
+  m_curPosition = curPosition;
+  m_curCluster = curCluster;
+  return c;
+}
+//------------------------------------------------------------------------------
 bool FatFile::preAllocate(uint32_t length) {
   uint32_t need;
   if (!length || !isWritable() || m_firstCluster) {
@@ -711,15 +728,6 @@ bool FatFile::preAllocate(uint32_t length) {
   return false;
 }
 //------------------------------------------------------------------------------
-int FatFile::peek() {
-  uint32_t curPosition = m_curPosition;
-  uint32_t curCluster = m_curCluster;
-  int c = read();
-  m_curPosition = curPosition;
-  m_curCluster = curCluster;
-  return c;
-}
-//------------------------------------------------------------------------------
 int FatFile::read(void* buf, size_t nbyte) {
   int8_t fg;
   uint8_t sectorOfCluster = 0;
@@ -858,9 +866,10 @@ int8_t FatFile::readDir(DirFat_t* dir) {
   }
 }
 //------------------------------------------------------------------------------
-// Read next directory entry into the cache
-// Assumes file is correctly positioned
+// Read next directory entry into the cache.
+// Assumes file is correctly positioned.
 DirFat_t* FatFile::readDirCache(bool skipReadOk) {
+  DBG_HALT_IF(m_curPosition & 0X1F);
   uint8_t i = (m_curPosition >> 5) & 0XF;
 
   if (i == 0 || !skipReadOk) {
@@ -1205,7 +1214,6 @@ bool FatFile::sync() {
     if (isFile()) {
       setLe32(dir->fileSize, m_fileSize);
     }
-
     // update first cluster fields
     setLe16(dir->firstClusterLow, m_firstCluster & 0XFFFF);
     setLe16(dir->firstClusterHigh, m_firstCluster >> 16);
@@ -1318,12 +1326,7 @@ bool FatFile::truncate() {
 
   // need to update directory entry
   m_flags |= FILE_FLAG_DIR_DIRTY;
-
-  if (!sync()) {
-    DBG_FAIL_MACRO;
-    goto fail;
-  }
-  return true;
+  return sync();
 
  fail:
   return false;
@@ -1473,5 +1476,5 @@ size_t FatFile::write(const void* buf, size_t nbyte) {
  fail:
   // return for write error
   m_error |= WRITE_ERROR;
-  return -1;
+  return 0;
 }

+ 80 - 29
src/FatLib/FatFile.h

@@ -31,10 +31,10 @@
 #include <string.h>
 #include <stddef.h>
 #include <limits.h>
-#include "FatLibConfig.h"
 #include "../common/FmtNumber.h"
 #include "../common/FsApiConstants.h"
 #include "../common/FsDateTime.h"
+#include "../common/FsName.h"
 #include "../common/FsStructs.h"
 #include "FatPartition.h"
 class FatVolume;
@@ -76,18 +76,22 @@ struct FatPos_t {
 #define isDirSeparator(c) ((c) == '/')
 //------------------------------------------------------------------------------
 /**
- * \struct fname_t
- * \brief Internal type for Short File Name - do not use in user apps.
+ * \class FatName_t
+ * \brief Internal type for File Name - do not use in user apps.
  */
-struct fname_t {
-  /** Flags for base and extension character case and LFN. */
-  uint8_t flags;
-  /** length of Long File Name */
+#if USE_LONG_FILE_NAMES
+class FatName_t : public FsName {
+ public:
+  /** UTF-16 length of Long File Name */
   size_t len;
-  /** Long File Name start. */
-  const char* lfn;
-  /** position for sequence number */
+  /** Position for sequence number. */
   uint8_t seqPos;
+#else  // USE_LONG_FILE_NAMES
+class FatName_t {
+ public:
+#endif  // USE_LONG_FILE_NAMES
+  /** Flags for base and extension character case and LFN. */
+  uint8_t flags;
   /** Short File Name */
   uint8_t sfn[11];
 };
@@ -102,6 +106,9 @@ const uint8_t FNAME_FLAG_NEED_LFN =
 const uint8_t FNAME_FLAG_LC_BASE = FAT_CASE_LC_BASE;
 /** Filename extension is all lower case. */
 const uint8_t FNAME_FLAG_LC_EXT = FAT_CASE_LC_EXT;
+#if FNAME_FLAG_NEED_LFN & (FAT_CASE_LC_BASE || FAT_CASE_LC_EXT)
+#error FNAME_FLAG_NEED_LFN & (FAT_CASE_LC_BASE || FAT_CASE_LC_EXT)
+#endif  // FNAME_FLAG_NEED_LFN & (FAT_CASE_LC_BASE || FAT_CASE_LC_EXT)
 //==============================================================================
 /**
  * \class FatFile
@@ -169,7 +176,7 @@ class FatFile {
   /** Check for contiguous file and return its raw sector range.
    *
    * \param[out] bgnSector the first sector address for the file.
-   * \param[out] endSector the last  sector address for the file.
+   * \param[out] endSector the last sector address for the file.
    *
    * Set the contiguous flag if the file is contiguous.
    * The parameters may be nullptr to only set the flag.
@@ -224,7 +231,7 @@ class FatFile {
    *
    * The calling instance must be an open directory file.
    *
-   * dirFile.exists("TOFIND.TXT") searches for "TOFIND.TXT" in  the directory
+   * dirFile.exists("TOFIND.TXT") searches for "TOFIND.TXT" in the directory
    * dirFile.
    *
    * \return True if the file exists.
@@ -322,14 +329,34 @@ class FatFile {
    * \return length for success or zero for failure.
    */
   size_t getName(char* name, size_t size);
+  /**
+   * Get a file's ASCII name followed by a zero.
+   *
+   * \param[out] name An array of characters for the file's name.
+   * \param[in] size The size of the array in characters.
+   * \return the name length.
+   */
+  size_t getName7(char* name, size_t size);
+  /**
+   * Get a file's UTF-8 name followed by a zero.
+   *
+   * \param[out] name An array of characters for the file's name.
+   * \param[in] size The size of the array in characters.
+   * \return the name length.
+   */
+  size_t getName8(char* name, size_t size);
+#ifndef DOXYGEN_SHOULD_SKIP_THIS
+  size_t __attribute__((error("use getSFN(name, size)"))) getSFN(char* name);
+#endif  // DOXYGEN_SHOULD_SKIP_THIS
   /**
    * Get a file's Short File Name followed by a zero byte.
    *
    * \param[out] name An array of characters for the file's name.
-   *                  The array must be at least 13 bytes long.
+   *                  The array should be at least 13 bytes long.
+   * \param[in] size size of name array.
    * \return true for success or false for failure.
    */
-  size_t getSFN(char* name);
+  size_t getSFN(char* name, size_t size);
   /** \return value of writeError */
   bool getWriteError() const {
     return isOpen() ? m_error & WRITE_ERROR : true;
@@ -427,9 +454,9 @@ class FatFile {
    * Use getName(char* name, size_t size).
    * \return a pointer to replacement suggestion.
    */
-  const char* name() const {
-    return "use getName()";
-  }
+#ifndef DOXYGEN_SHOULD_SKIP_THIS
+  const char* __attribute__((error("use getName(name, size)"))) name();
+#endif  // DOXYGEN_SHOULD_SKIP_THIS
   /** Open a file in the volume root directory.
    *
    * \param[in] vol Volume where the file is located.
@@ -661,10 +688,23 @@ class FatFile {
    *
    * \param[in] pr Print stream for output.
    *
-   * \return true for success or false for failure.
+   * \return length for success or zero for failure.
    */
   size_t printName(print_t* pr);
-
+  /** Print a file's ASCII name
+   *
+   * \param[in] pr Print stream for output.
+   *
+   * \return true for success or false for failure.
+   */
+  size_t printName7(print_t* pr);
+  /** Print a file's UTF-8 name
+   *
+   * \param[in] pr Print stream for output.
+   *
+   * \return true for success or false for failure.
+   */
+  size_t printName8(print_t* pr);
   /** Print a file's Short File Name.
    *
    * \param[in] pr Print stream for output.
@@ -885,10 +925,7 @@ class FatFile {
    * \param[in] count Number of bytes to write.
    *
    * \return For success write() returns the number of bytes written, always
-   * \a count.  If an error occurs, write() returns -1.  Possible errors
-   * 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.
+   * \a count.  If an error occurs, write() returns zero and writeError is set.
    *
    */
   size_t write(const void* buf, size_t count);
@@ -911,7 +948,7 @@ class FatFile {
   }
   /** Print a file's name.
    *
-   * \return true for success or false for failure.
+   * \return length for success or zero for failure.
    */
   size_t printName() {
     return FatFile::printName(&Serial);
@@ -949,15 +986,29 @@ class FatFile {
                        FAT_ATTRIB_SYSTEM | FAT_ATTRIB_DIRECTORY;
 
   // private functions
+
   bool addCluster();
   bool addDirCluster();
+  DirFat_t* cacheDir(uint16_t index) {
+    return seekSet(32UL*index) ? readDirCache() : nullptr;
+  }
   DirFat_t* cacheDirEntry(uint8_t action);
-  static uint8_t lfnChecksum(uint8_t* name);
-  bool lfnUniqueSfn(fname_t* fname);
+  bool cmpName(uint16_t index, FatName_t* fname, uint8_t lfnOrd);
+  bool createLFN(uint16_t index, FatName_t* fname, uint8_t lfnOrd);
+  uint16_t getLfnChar(DirLfn_t* ldir, uint8_t i);
+  uint8_t lfnChecksum(uint8_t* name) {
+    uint8_t sum = 0;
+    for (uint8_t i = 0; i < 11; i++) {
+        sum = (((sum & 1) << 7) | (sum >> 1)) + name[i];
+    }
+    return sum;
+  }
+  static bool makeSFN(FatName_t* fname);
+  bool makeUniqueSfn(FatName_t* fname);
   bool openCluster(FatFile* file);
-  static bool parsePathName(const char* str, fname_t* fname, const char** ptr);
-  bool mkdir(FatFile* parent, fname_t* fname);
-  bool open(FatFile* dirFile, fname_t* fname, oflag_t oflag);
+  bool parsePathName(const char* str, FatName_t* fname, const char** ptr);
+  bool mkdir(FatFile* parent, FatName_t* fname);
+  bool open(FatFile* dirFile, FatName_t* fname, oflag_t oflag);
   bool openCachedEntry(FatFile* dirFile, uint16_t cacheIndex, oflag_t oflag,
                        uint8_t lfnOrd);
   DirFat_t* readDirCache(bool skipReadOk = false);

+ 230 - 338
src/FatLib/FatFileLFN.cpp

@@ -24,76 +24,23 @@
  */
 #define DBG_FILE "FatFileLFN.cpp"
 #include "../common/DebugMacros.h"
+#include "../common/upcase.h"
+#include "../common/FsUtf.h"
 #include "FatFile.h"
 #include "FatVolume.h"
-//------------------------------------------------------------------------------
-//
-uint8_t FatFile::lfnChecksum(uint8_t* name) {
-  uint8_t sum = 0;
-  for (uint8_t i = 0; i < 11; i++) {
-    sum = (((sum & 1) << 7) | ((sum & 0xfe) >> 1)) + name[i];
-  }
-  return sum;
-}
 #if USE_LONG_FILE_NAMES
 //------------------------------------------------------------------------------
-// Saves about 90 bytes of flash on 328 over tolower().
-inline char lfnToLower(char c) {
-  return 'A' <= c && c <= 'Z' ? c + 'a' - 'A' : c;
+static bool isLower(char c) {
+  return 'a' <= c && c <= 'z';
 }
 //------------------------------------------------------------------------------
-// Daniel Bernstein University of Illinois at Chicago.
-// Original had + instead of ^
-static uint16_t Bernstein(uint16_t hash, const char *str, size_t len) {
-  for (size_t i = 0; i < len; i++) {
-    // hash = hash * 33 ^ str[i];
-    hash = ((hash << 5) + hash) ^ str[i];
-  }
-  return hash;
+static bool isUpper(char c) {
+  return 'A' <= c && c <= 'Z';
 }
 //------------------------------------------------------------------------------
-/**
- * Fetch a 16-bit long file name character.
- *
- * \param[in] ldir Pointer to long file name directory entry.
- * \param[in] i Index of character.
- * \return The 16-bit character.
- */
-static uint16_t lfnGetChar(DirLfn_t* ldir, uint8_t i) {
-  if (i < 5) {
-    return getLe16(ldir->unicode1 + 2*i);
-  } else if (i < 11) {
-    return getLe16(ldir->unicode2 + 2*i - 10);
-  } else if (i < 13) {
-    return getLe16(ldir->unicode3 + 2*i - 22);
-  }
-  return 0;
-}
-//------------------------------------------------------------------------------
-static size_t lfnGetName(DirLfn_t* ldir, char* name, size_t n) {
-  uint8_t i;
-  size_t k = 13*((ldir->order & 0X1F) - 1);
-  for (i = 0; i < 13; i++) {
-    uint16_t c = lfnGetChar(ldir, i);
-    if (c == 0 || k >= (n - 1)) {
-      break;
-    }
-    name[k++] = c >= 0X7F ? '?' : c;
-  }
-  // Terminate with zero byte.
-  if (k >= n) {
-    k = n - 1;
-  }
-  name[k] = '\0';
-  return k;
-}
-//------------------------------------------------------------------------------
-inline bool lfnLegalChar(uint8_t c) {
-  if (c == '/' || c == '\\' || c == '"' || c == '*' ||
-      c == ':' || c == '<' || c == '>' || c == '?' || c == '|') {
-    return false;
-  }
-  return 0X1F < c && c < 0X7F;
+// A bit smaller than toupper in AVR 328.
+inline char toUpper(char c) {
+  return isLower(c) ? c - 'a' + 'A' : c;
 }
 //------------------------------------------------------------------------------
 /**
@@ -101,9 +48,9 @@ inline bool lfnLegalChar(uint8_t c) {
  *
  * \param[in] ldir Pointer to long file name directory entry.
  * \param[in] i Index of character.
- * \param[in] c  The 16-bit character.
+ * \param[in] c The 16-bit character.
  */
-static void lfnPutChar(DirLfn_t* ldir, uint8_t i, uint16_t c) {
+static void putLfnChar(DirLfn_t* ldir, uint8_t i, uint16_t c) {
   if (i < 5) {
     setLe16(ldir->unicode1 + 2*i, c);
   } else if (i < 11) {
@@ -113,167 +60,152 @@ static void lfnPutChar(DirLfn_t* ldir, uint8_t i, uint16_t c) {
   }
 }
 //------------------------------------------------------------------------------
-static void lfnPutName(DirLfn_t* ldir, const char* name, size_t n) {
-  size_t k = 13*((ldir->order & 0X1F) - 1);
-  for (uint8_t i = 0; i < 13; i++, k++) {
-    uint16_t c = k < n ? name[k] : k == n ? 0 : 0XFFFF;
-    lfnPutChar(ldir, i, c);
+// Daniel Bernstein University of Illinois at Chicago.
+// Original had + instead of ^
+__attribute__((unused))
+static uint16_t Bernstein(const char* bgn, const char* end, uint16_t hash) {
+  while (bgn < end) {
+    // hash = hash * 33 ^ str[i];
+    hash = ((hash << 5) + hash) ^ (*bgn++);
   }
+  return hash;
 }
 //==============================================================================
-size_t FatFile::getName(char* name, size_t size) {
-  size_t n = 0;
-  FatFile dirFile;
+bool FatFile::cmpName(uint16_t index, FatName_t* fname, uint8_t lfnOrd) {
+  FatFile dir = *this;
   DirLfn_t* ldir;
-  if (!isOpen() || size < 13) {
-    DBG_FAIL_MACRO;
-    goto fail;
-  }
-  if (!isLFN()) {
-    return getSFN(name);
-  }
-  if (!dirFile.openCluster(this)) {
-    DBG_FAIL_MACRO;
-    goto fail;
-  }
-  for (uint8_t order = 1; order <= m_lfnOrd; order++) {
-    if (!dirFile.seekSet(32UL*(m_dirIndex - order))) {
-      DBG_FAIL_MACRO;
-      goto fail;
-    }
-    ldir = reinterpret_cast<DirLfn_t*>(dirFile.readDirCache());
+  fname->reset();
+  for (uint8_t order = 1; order <= lfnOrd; order++) {
+    ldir = reinterpret_cast<DirLfn_t*>(dir.cacheDir(index - order));
     if (!ldir) {
       DBG_FAIL_MACRO;
       goto fail;
     }
-    if (ldir->attributes != FAT_ATTRIB_LONG_NAME) {
-      DBG_FAIL_MACRO;
-      goto fail;
-    }
-    if (order != (ldir->order & 0X1F)) {
-      DBG_FAIL_MACRO;
-      goto fail;
-    }
-    n = lfnGetName(ldir, name, size);
-    if (n == 0) {
-      DBG_FAIL_MACRO;
-      goto fail;
-    }
-    if (ldir->order & FAT_ORDER_LAST_LONG_ENTRY) {
-      return n;
+    // These should be checked in caller.
+    DBG_HALT_IF(ldir->attributes != FAT_ATTRIB_LONG_NAME);
+    DBG_HALT_IF(order != (ldir->order & 0X1F));
+    for (uint8_t i = 0; i < 13; i++) {
+      uint16_t u = getLfnChar(ldir, i);
+      if (fname->atEnd()) {
+        return u == 0;
+      }
+#if USE_UTF8_LONG_NAMES
+      uint16_t cp = fname->get16();
+      // Make sure caller checked for valid UTF-8.
+      DBG_HALT_IF(cp == 0XFFFF);
+      if (toUpcase(u) != toUpcase(cp)) {
+        return false;
+      }
+#else  // USE_UTF8_LONG_NAMES
+      if (u > 0X7F || toUpper(u) != toUpper(fname->getch())) {
+        return false;
+      }
+#endif  // USE_UTF8_LONG_NAMES
     }
   }
-  // Fall into fail.
-  DBG_FAIL_MACRO;
+  return true;
 
  fail:
-  name[0] = '\0';
-  return 0;
+  return false;
 }
 //------------------------------------------------------------------------------
-bool FatFile::openCluster(FatFile* file) {
-  if (file->m_dirCluster == 0) {
-    return openRoot(file->m_vol);
+bool FatFile::createLFN(uint16_t index, FatName_t* fname, uint8_t lfnOrd) {
+  FatFile dir = *this;
+  DirLfn_t* ldir;
+  uint8_t checksum = lfnChecksum(fname->sfn);
+  uint8_t fc = 0;
+  fname->reset();
+
+  for (uint8_t order = 1; order <= lfnOrd; order++) {
+    ldir = reinterpret_cast<DirLfn_t*>(dir.cacheDir(index - order));
+    if (!ldir) {
+      DBG_FAIL_MACRO;
+      goto fail;
+    }
+    dir.m_vol->cacheDirty();
+    ldir->order = order == lfnOrd ? FAT_ORDER_LAST_LONG_ENTRY | order : order;
+    ldir->attributes = FAT_ATTRIB_LONG_NAME;
+    ldir->mustBeZero1 = 0;
+    ldir->checksum = checksum;
+    setLe16(ldir->mustBeZero2, 0);
+    for (uint8_t i = 0; i < 13; i++) {
+      uint16_t cp;
+      if (fname->atEnd()) {
+        cp = fc++ ? 0XFFFF : 0;
+      } else {
+        cp = fname->get16();
+        // Verify caller checked for valid UTF-8.
+        DBG_HALT_IF(cp == 0XFFFF);
+      }
+      putLfnChar(ldir, i, cp);
+    }
   }
-  memset(this, 0, sizeof(FatFile));
-  m_attributes = FILE_ATTR_SUBDIR;
-  m_flags = FILE_FLAG_READ;
-  m_vol = file->m_vol;
-  m_firstCluster = file->m_dirCluster;
   return true;
+
+ fail:
+  return false;
 }
 //------------------------------------------------------------------------------
-bool FatFile::parsePathName(const char* path,
-                            fname_t* fname, const char** ptr) {
-  char c;
+bool FatFile::makeSFN(FatName_t* fname) {
   bool is83;
+//  char c;
+  uint8_t c;
   uint8_t bit = FAT_CASE_LC_BASE;
   uint8_t lc = 0;
   uint8_t uc = 0;
   uint8_t i = 0;
   uint8_t in = 7;
-  int end;
-  int len = 0;
-  int si;
-  int dot;
+  const char* dot;
+  const char* end = fname->end;
+  const char* ptr = fname->begin;
 
-  // Skip leading spaces.
-  while (*path == ' ') {
-    path++;
-  }
-  fname->lfn = path;
+  // Assume not zero length.
+  DBG_HALT_IF(end == ptr);
+  // Assume blanks removed from start and end.
+  DBG_HALT_IF(*ptr == ' ' || *(end - 1) == ' ' || *(end - 1) == '.');
 
-  for (len = 0; ; len++) {
-    c = path[len];
-    if (c == 0 || isDirSeparator(c)) {
-      break;
-    }
-    if (!lfnLegalChar(c)) {
-      DBG_FAIL_MACRO;
-      goto fail;
-    }
-  }
-  // Advance to next path component.
-  for (end = len; path[end] ==  ' ' || isDirSeparator(path[end]); end++) {}
-  *ptr = &path[end];
-
-  // Back over spaces and dots.
-  while (len) {
-    c = path[len - 1];
-    if (c != '.' && c != ' ') {
-      break;
-    }
-    len--;
-  }
-  // Max length of LFN is 255.
-  if (len > 255) {
-    DBG_FAIL_MACRO;
-    goto fail;
-  }
-  fname->len = len;
   // Blank file short name.
   for (uint8_t k = 0; k < 11; k++) {
     fname->sfn[k] = ' ';
   }
-  // skip leading spaces and dots.
-  for (si = 0; path[si] == '.' || path[si] == ' '; si++) {}
-  // Not 8.3 if leading dot or space.
-  is83 = !si;
+  // Not 8.3 if starts with dot.
+  is83 = *ptr == '.' ? false : true;
+  // Skip leading dots.
+  for (; *ptr == '.'; ptr++) {}
+  // Find last dot.
+  for (dot = end - 1; dot > ptr && *dot != '.'; dot--) {}
 
-  // find last dot.
-  for (dot = len - 1; dot >= 0 && path[dot] != '.'; dot--) {}
-  for (; si < len; si++) {
-    c = path[si];
-    if (c == ' ' || (c == '.' && dot != si)) {
-      is83 = false;
-      continue;
-    }
-    if (!legal83Char(c) && si != dot) {
-      is83 = false;
-      c = '_';
-    }
-    if (si == dot || i > in) {
-      if (in == 10) {
-        // Done - extension longer than three characters.
-        is83 = false;
-        break;
-      }
-      if (si != dot) {
-        is83 = false;
-      }
-      // Break if no dot and base-name is longer than eight characters.
-      if (si > dot) {
-        break;
-      }
-      si = dot;
+  for (; ptr < end; ptr++) {
+    c = *ptr;
+//  Could skip UTF-8 units where (0XC0 & c) == 0X80
+
+    if (c == '.' && ptr == dot) {
       in = 10;  // Max index for full 8.3 name.
       i = 8;    // Place for extension.
       bit = FAT_CASE_LC_EXT;  // bit for extension.
     } else {
-      if ('a' <= c && c <= 'z') {
+      if (!legal83Char(c)) {
+        is83 = false;
+        // Skip UTF-8 trailing characters.
+        if ((c & 0XC0) == 0X80) {
+          continue;
+        }
+        c = '_';
+      }
+      if (i > in) {
+        is83 = false;
+        if (in == 10 || ptr > dot) {
+         // Done - extension longer than three characters or no extension.
+          break;
+        }
+        // Skip to dot.
+        ptr = dot - 1;
+        continue;
+      }
+      if (isLower(c)) {
         c += 'A' - 'a';
         lc |= bit;
-      } else if ('A' <= c && c <= 'Z') {
+      } else if (isUpper(c)) {
         uc |= bit;
       }
       fname->sfn[i++] = c;
@@ -283,10 +215,9 @@ bool FatFile::parsePathName(const char* path,
     }
   }
   if (fname->sfn[0] == ' ') {
-    DBG_FAIL_MACRO;
+    DBG_HALT_MACRO;
     goto fail;
   }
-
   if (is83) {
     fname->flags = lc & uc ? FNAME_FLAG_MIXED_CASE : lc;
   } else {
@@ -300,7 +231,63 @@ bool FatFile::parsePathName(const char* path,
   return false;
 }
 //------------------------------------------------------------------------------
-bool FatFile::open(FatFile* dirFile, fname_t* fname, oflag_t oflag) {
+bool FatFile::makeUniqueSfn(FatName_t* fname) {
+  const uint8_t FIRST_HASH_SEQ = 2;  // min value is 2
+  uint8_t pos = fname->seqPos;
+  DirFat_t* dir;
+  uint16_t hex;
+
+  DBG_HALT_IF(!(fname->flags & FNAME_FLAG_LOST_CHARS));
+  DBG_HALT_IF(fname->sfn[pos] != '~' && fname->sfn[pos + 1] != '1');
+
+  for (uint8_t seq = FIRST_HASH_SEQ; seq < 100; seq++) {
+     DBG_WARN_IF(seq > FIRST_HASH_SEQ);
+#ifdef USE_LFN_HASH
+    hex = Bernstein(fname->begin, fname->end, seq);
+#else
+    hex = micros();
+#endif
+    if (pos > 3) {
+      // Make space in name for ~HHHH.
+      pos = 3;
+    }
+    for (uint8_t i = pos + 4 ; i > pos; i--) {
+      uint8_t h = hex & 0XF;
+      fname->sfn[i] = h < 10 ? h + '0' : h + 'A' - 10;
+      hex >>= 4;
+    }
+    fname->sfn[pos] = '~';
+    rewind();
+    while (1) {
+      dir = readDirCache(true);
+      if (!dir) {
+        if (!getError()) {
+          // At EOF and name not found if no error.
+          goto done;
+        }
+        DBG_FAIL_MACRO;
+        goto fail;
+      }
+      if (dir->name[0] == FAT_NAME_FREE) {
+        goto done;
+      }
+      if (isFileOrSubdir(dir) && !memcmp(fname->sfn, dir->name, 11)) {
+        // Name found - try another.
+        break;
+      }
+    }
+  }
+  // fall inti fail - too many tries.
+  DBG_FAIL_MACRO;
+
+ fail:
+  return false;
+
+ done:
+  return true;
+}
+//------------------------------------------------------------------------------
+bool FatFile::open(FatFile* dirFile, FatName_t* fname, oflag_t oflag) {
   bool fnameFound = false;
   uint8_t lfnOrd = 0;
   uint8_t freeNeed;
@@ -308,25 +295,25 @@ bool FatFile::open(FatFile* dirFile, fname_t* fname, oflag_t oflag) {
   uint8_t order = 0;
   uint8_t checksum = 0;
   uint8_t ms10;
+  uint8_t nameOrd;
   uint16_t freeIndex = 0;
   uint16_t curIndex;
   uint16_t date;
   uint16_t time;
   DirFat_t* dir;
   DirLfn_t* ldir;
-  size_t len = fname->len;
 
   if (!dirFile->isDir() || isOpen()) {
     DBG_FAIL_MACRO;
     goto fail;
   }
   // Number of directory entries needed.
-  freeNeed = fname->flags & FNAME_FLAG_NEED_LFN ? 1 + (len + 12)/13 : 1;
-
+  nameOrd = (fname->len + 12)/13;
+  freeNeed = fname->flags & FNAME_FLAG_NEED_LFN ? 1 + nameOrd : 1;
   dirFile->rewind();
   while (1) {
     curIndex = dirFile->m_curPosition/32;
-    dir = dirFile->readDirCache(true);
+    dir = dirFile->readDirCache();
     if (!dir) {
       if (dirFile->getError()) {
         DBG_FAIL_MACRO;
@@ -356,38 +343,20 @@ bool FatFile::open(FatFile* dirFile, fname_t* fname, oflag_t oflag) {
     } else if (isLongName(dir)) {
       ldir = reinterpret_cast<DirLfn_t*>(dir);
       if (!lfnOrd) {
-        if ((ldir->order & FAT_ORDER_LAST_LONG_ENTRY) == 0) {
-          continue;
-        }
         order = ldir->order & 0X1F;
-        if (order != (freeNeed - 1)) {
+        if (order != nameOrd ||
+          (ldir->order & FAT_ORDER_LAST_LONG_ENTRY) == 0) {
           continue;
         }
-        lfnOrd = order;
+        lfnOrd = nameOrd;
         checksum = ldir->checksum;
       } else if (ldir->order != --order || checksum != ldir->checksum) {
         lfnOrd = 0;
         continue;
       }
-      size_t k = 13*(order - 1);
-      if (k >= len) {
-        // Not found.
-        lfnOrd = 0;
-        continue;
-      }
-      for (uint8_t i = 0; i < 13; i++) {
-        uint16_t u = lfnGetChar(ldir, i);
-        if (k == len) {
-          if (u != 0) {
-            // Not found.
-            lfnOrd = 0;
-          }
-          break;
-        }
-        if (u > 255 || lfnToLower(u) != lfnToLower(fname->lfn[k++])) {
-          // Not found.
+      if (order == 1) {
+        if (!dirFile->cmpName(curIndex + 1, fname, lfnOrd)) {
           lfnOrd = 0;
-          break;
         }
       }
     } else if (isFileOrSubdir(dir)) {
@@ -420,10 +389,10 @@ bool FatFile::open(FatFile* dirFile, fname_t* fname, oflag_t oflag) {
  create:
   // don't create unless O_CREAT and write mode
   if (!(oflag & O_CREAT) || !isWriteMode(oflag)) {
-    DBG_FAIL_MACRO;
+    DBG_WARN_MACRO;
     goto fail;
   }
-  // If at EOF start in next cluster.
+  // Keep found entries or start at current index if no free entries found.
   if (freeFound == 0) {
     freeIndex = curIndex;
   }
@@ -446,38 +415,19 @@ bool FatFile::open(FatFile* dirFile, fname_t* fname, oflag_t oflag) {
       DBG_FAIL_MACRO;
       goto fail;
     }
-    // Done if more than one sector per cluster.  Max freeNeed is 21.
-    if (dirFile->m_vol->sectorsPerCluster() > 1) {
-      break;
-    }
-    freeFound += 16;
+    freeFound += dirFile->m_vol->sectorsPerCluster();
   }
   if (fnameFound) {
-    if (!dirFile->lfnUniqueSfn(fname)) {
+    if (!dirFile->makeUniqueSfn(fname)) {
       goto fail;
     }
   }
-  if (!dirFile->seekSet(32UL*freeIndex)) {
-    DBG_FAIL_MACRO;
-    goto fail;
-  }
   lfnOrd = freeNeed - 1;
-  for (order = lfnOrd ; order ; order--) {
-    ldir = reinterpret_cast<DirLfn_t*>(dirFile->readDirCache());
-    if (!ldir) {
-      DBG_FAIL_MACRO;
-      goto fail;
-    }
-    dirFile->m_vol->cacheDirty();
-    ldir->order = order == lfnOrd ? FAT_ORDER_LAST_LONG_ENTRY | order : order;
-    ldir->attributes = FAT_ATTRIB_LONG_NAME;
-    ldir->mustBeZero1 = 0;
-    ldir->checksum = lfnChecksum(fname->sfn);
-    setLe16(ldir->mustBeZero2, 0);
-    lfnPutName(ldir, fname->lfn, len);
+  curIndex = freeIndex + lfnOrd;
+  if (!dirFile->createLFN(curIndex, fname, lfnOrd)) {
+    goto fail;
   }
-  curIndex = dirFile->m_curPosition/32;
-  dir = dirFile->readDirCache();
+  dir = dirFile->cacheDir(curIndex);
   if (!dir) {
     DBG_FAIL_MACRO;
     goto fail;
@@ -487,7 +437,7 @@ bool FatFile::open(FatFile* dirFile, fname_t* fname, oflag_t oflag) {
   memcpy(dir->name, fname->sfn, 11);
 
   // Set base-name and extension lower case bits.
-  dir->caseFlags =  (FAT_CASE_LC_BASE | FAT_CASE_LC_EXT) & fname->flags;
+  dir->caseFlags = (FAT_CASE_LC_BASE | FAT_CASE_LC_EXT) & fname->flags;
 
   // Set timestamps.
   if (FsDateTime::callback) {
@@ -520,51 +470,53 @@ bool FatFile::open(FatFile* dirFile, fname_t* fname, oflag_t oflag) {
   return false;
 }
 //------------------------------------------------------------------------------
-size_t FatFile::printName(print_t* pr) {
-  FatFile dirFile;
-  DirLfn_t* ldir;
-  size_t n = 0;
-  uint16_t u;
-  uint8_t buf[13];
-  uint8_t i;
-
-  if (!isLFN()) {
-    return printSFN(pr);
-  }
-  if (!dirFile.openCluster(this)) {
-    DBG_FAIL_MACRO;
-    goto fail;
+bool FatFile::parsePathName(const char* path,
+                            FatName_t* fname, const char** ptr) {
+  size_t len = 0;
+  // Skip leading spaces.
+  while (*path == ' ') {
+    path++;
   }
-  for (uint8_t order = 1; order <= m_lfnOrd; order++) {
-    if (!dirFile.seekSet(32UL*(m_dirIndex - order))) {
+  fname->begin = path;
+  while (*path && !isDirSeparator(*path)) {
+#if USE_UTF8_LONG_NAMES
+    uint32_t cp;
+    // Allow end = path + 4 since path is zero terminated.
+    path = FsUtf::mbToCp(path, path + 4, &cp);
+    if (!path) {
       DBG_FAIL_MACRO;
       goto fail;
     }
-    ldir = reinterpret_cast<DirLfn_t*>(dirFile.readDirCache());
-    if (!ldir) {
+    len += cp <= 0XFFFF ? 1 : 2;
+    if (cp < 0X80 && lfnReservedChar(cp)) {
       DBG_FAIL_MACRO;
       goto fail;
     }
-    if (ldir->attributes != FAT_ATTRIB_LONG_NAME ||
-        order != (ldir->order & 0X1F)) {
+#else  // USE_UTF8_LONG_NAMES
+    uint8_t cp = *path++;
+    if (cp >= 0X80 || lfnReservedChar(cp)) {
       DBG_FAIL_MACRO;
       goto fail;
     }
-    for (i = 0; i < 13; i++) {
-      u = lfnGetChar(ldir, i);
-      if (u == 0) {
-        // End of name.
-        break;
-      }
-      buf[i] = u < 0X7F ? u : '?';
-      n++;
+    len++;
+#endif  // USE_UTF8_LONG_NAMES
+    if (cp != '.' && cp != ' ') {
+      // Need to trim trailing dots spaces.
+      fname->len = len;
+      fname->end = path;
     }
-    pr->write(buf, i);
   }
-  return n;
+  if (!fname->len || fname->len > FAT_MAX_LFN_LENGTH) {
+    DBG_FAIL_MACRO;
+    goto fail;
+  }
+  // Advance to next path component.
+  for (; *path == ' ' || isDirSeparator(*path); path++) {}
+  *ptr = path;
+  return makeSFN(fname);
 
  fail:
-  return 0;
+  return false;
 }
 //------------------------------------------------------------------------------
 bool FatFile::remove() {
@@ -613,11 +565,7 @@ bool FatFile::remove() {
     goto fail;
   }
   for (uint8_t order = 1; order <= m_lfnOrd; order++) {
-    if (!dirFile.seekSet(32UL*(m_dirIndex - order))) {
-      DBG_FAIL_MACRO;
-      goto fail;
-    }
-    ldir = reinterpret_cast<DirLfn_t*>(dirFile.readDirCache());
+    ldir = reinterpret_cast<DirLfn_t*>(dirFile.cacheDir(m_dirIndex - order));
     if (!ldir) {
       DBG_FAIL_MACRO;
       goto fail;
@@ -645,60 +593,4 @@ bool FatFile::remove() {
  fail:
   return false;
 }
-//------------------------------------------------------------------------------
-bool FatFile::lfnUniqueSfn(fname_t* fname) {
-  const uint8_t FIRST_HASH_SEQ = 2;  // min value is 2
-  uint8_t pos = fname->seqPos;
-  DirFat_t* dir;
-  uint16_t hex;
-
-  DBG_HALT_IF(!(fname->flags & FNAME_FLAG_LOST_CHARS));
-  DBG_HALT_IF(fname->sfn[pos] != '~' && fname->sfn[pos + 1] != '1');
-
-  for (uint8_t seq = 2; seq < 100; seq++) {
-    if (seq < FIRST_HASH_SEQ) {
-      fname->sfn[pos + 1] = '0' + seq;
-    } else {
-      DBG_PRINT_IF(seq > FIRST_HASH_SEQ);
-      hex = Bernstein(seq + fname->len, fname->lfn, fname->len);
-      if (pos > 3) {
-        // Make space in name for ~HHHH.
-        pos = 3;
-      }
-      for (uint8_t i = pos + 4 ; i > pos; i--) {
-        uint8_t h = hex & 0XF;
-        fname->sfn[i] = h < 10 ? h + '0' : h + 'A' - 10;
-        hex >>= 4;
-      }
-    }
-    fname->sfn[pos] = '~';
-    rewind();
-    while (1) {
-      dir = readDirCache(true);
-      if (!dir) {
-        if (!getError()) {
-          // At EOF and name not found if no error.
-          goto done;
-        }
-        DBG_FAIL_MACRO;
-        goto fail;
-      }
-      if (dir->name[0] == FAT_NAME_FREE) {
-        goto done;
-      }
-      if (isFileOrSubdir(dir) && !memcmp(fname->sfn, dir->name, 11)) {
-        // Name found - try another.
-        break;
-      }
-    }
-  }
-  // fall inti fail - too many tries.
-  DBG_FAIL_MACRO;
-
- fail:
-  return false;
-
- done:
-  return true;
-}
 #endif  // #if USE_LONG_FILE_NAMES

+ 54 - 122
src/FatLib/FatFileSFN.cpp

@@ -27,125 +27,12 @@
 #include "../common/FsStructs.h"
 #include "FatFile.h"
 #include "FatVolume.h"
-//------------------------------------------------------------------------------
-size_t FatFile::getSFN(char* name) {
-  uint8_t j = 0;
-  uint8_t lcBit = FAT_CASE_LC_BASE;
-  DirFat_t* dir;
-
-  if (!isOpen()) {
-    DBG_FAIL_MACRO;
-    goto fail;
-  }
-  if (isRoot()) {
-    name[0] = '/';
-    name[1] = '\0';
-    return 1;
-  }
-  // cache entry
-  dir = reinterpret_cast<DirFat_t*>(cacheDirEntry(FsCache::CACHE_FOR_READ));
-  if (!dir) {
-    DBG_FAIL_MACRO;
-    goto fail;
-  }
-  // format name
-  for (uint8_t i = 0; i < 11; i++) {
-    if (dir->name[i] == ' ') {
-      continue;
-    }
-    if (i == 8) {
-      // Position bit for extension.
-      lcBit = FAT_CASE_LC_EXT;
-      name[j++] = '.';
-    }
-    char c = dir->name[i];
-    if ('A' <= c && c <= 'Z' && (lcBit & dir->caseFlags)) {
-      c += 'a' - 'A';
-    }
-    name[j++] = c;
-  }
-  name[j] = '\0';
-  return j;
-
- fail:
-  name[0] = '\0';
-  return 0;
-}
-//------------------------------------------------------------------------------
-size_t FatFile::printSFN(print_t* pr) {
-  char name[13];
-  if (!getSFN(name)) {
-    DBG_FAIL_MACRO;
-    goto fail;
-  }
-  return pr->write(name);
 
- fail:
-  return 0;
-}
 #if !USE_LONG_FILE_NAMES
 //------------------------------------------------------------------------------
-size_t FatFile::getName(char* name, size_t size) {
-  return size < 13 ? 0 : getSFN(name);
-}
-//------------------------------------------------------------------------------
-// format directory name field from a 8.3 name string
-bool FatFile::parsePathName(const char* path, fname_t* fname,
-                            const char** ptr) {
-  uint8_t uc = 0;
-  uint8_t lc = 0;
-  uint8_t bit = FNAME_FLAG_LC_BASE;
-  // blank fill name and extension
-  for (uint8_t i = 0; i < 11; i++) {
-    fname->sfn[i] = ' ';
-  }
-
-  for (uint8_t i = 0, n = 7;; path++) {
-    uint8_t c = *path;
-    if (c == 0 || isDirSeparator(c)) {
-      // Done.
-      break;
-    }
-    if (c == '.' && n == 7) {
-      n = 10;  // max index for full 8.3 name
-      i = 8;   // place for extension
-
-      // bit for extension.
-      bit = FNAME_FLAG_LC_EXT;
-    } else {
-      if (!legal83Char(c) || i > n) {
-        DBG_FAIL_MACRO;
-        goto fail;
-      }
-      if ('a' <= c && c <= 'z') {
-        c += 'A' - 'a';
-        lc |= bit;
-      } else if ('A' <= c && c <= 'Z') {
-        uc |= bit;
-      }
-      fname->sfn[i++] = c;
-    }
-  }
-  // must have a file name, extension is optional
-  if (fname->sfn[0] == ' ') {
-    DBG_FAIL_MACRO;
-    goto fail;
-  }
-  // Set base-name and extension bits.
-  fname->flags = lc & uc ? 0 : lc;
-  while (isDirSeparator(*path)) {
-    path++;
-  }
-  *ptr = path;
-  return true;
-
- fail:
-  return false;
-}
-//------------------------------------------------------------------------------
 // open with filename in fname
 #define SFN_OPEN_USES_CHKSUM 0
-bool FatFile::open(FatFile* dirFile, fname_t* fname, oflag_t oflag) {
+bool FatFile::open(FatFile* dirFile, FatName_t* fname, oflag_t oflag) {
   uint16_t date;
   uint16_t time;
   uint8_t ms10;
@@ -227,11 +114,7 @@ bool FatFile::open(FatFile* dirFile, fname_t* fname, oflag_t oflag) {
       goto fail;
     }
   }
-  if (!dirFile->seekSet(32UL*index)) {
-    DBG_FAIL_MACRO;
-    goto fail;
-  }
-  dir = reinterpret_cast<DirFat_t*>(dirFile->readDirCache());
+  dir = reinterpret_cast<DirFat_t*>(dirFile->cacheDir(index));
   if (!dir) {
     DBG_FAIL_MACRO;
     goto fail;
@@ -269,8 +152,57 @@ bool FatFile::open(FatFile* dirFile, fname_t* fname, oflag_t oflag) {
   return false;
 }
 //------------------------------------------------------------------------------
-size_t FatFile::printName(print_t* pr) {
-  return printSFN(pr);
+// format directory name field from a 8.3 name string
+bool FatFile::parsePathName(const char* path, FatName_t* fname,
+                            const char** ptr) {
+  uint8_t uc = 0;
+  uint8_t lc = 0;
+  uint8_t bit = FNAME_FLAG_LC_BASE;
+  // blank fill name and extension
+  for (uint8_t i = 0; i < 11; i++) {
+    fname->sfn[i] = ' ';
+  }
+  for (uint8_t i = 0, n = 7;; path++) {
+    uint8_t c = *path;
+    if (c == 0 || isDirSeparator(c)) {
+      // Done.
+      break;
+    }
+    if (c == '.' && n == 7) {
+      n = 10;  // max index for full 8.3 name
+      i = 8;   // place for extension
+
+      // bit for extension.
+      bit = FNAME_FLAG_LC_EXT;
+    } else {
+      if (!legal83Char(c) || i > n) {
+        DBG_FAIL_MACRO;
+        goto fail;
+      }
+      if ('a' <= c && c <= 'z') {
+        c += 'A' - 'a';
+        lc |= bit;
+      } else if ('A' <= c && c <= 'Z') {
+        uc |= bit;
+      }
+      fname->sfn[i++] = c;
+    }
+  }
+  // must have a file name, extension is optional
+  if (fname->sfn[0] == ' ') {
+    DBG_FAIL_MACRO;
+    goto fail;
+  }
+  // Set base-name and extension bits.
+  fname->flags = lc & uc ? 0 : lc;
+  while (isDirSeparator(*path)) {
+    path++;
+  }
+  *ptr = path;
+  return true;
+
+ fail:
+  return false;
 }
 //------------------------------------------------------------------------------
 bool FatFile::remove() {
@@ -286,7 +218,7 @@ bool FatFile::remove() {
     goto fail;
   }
   // Cache directory entry.
-  dir = reinterpret_cast<DirFat_t*>(cacheDirEntry(FsCache::CACHE_FOR_WRITE));
+  dir = cacheDirEntry(FsCache::CACHE_FOR_WRITE);
   if (!dir) {
     DBG_FAIL_MACRO;
     goto fail;

+ 1 - 1
src/FatLib/FatFormatter.cpp

@@ -72,7 +72,7 @@ bool FatFormatter::format(BlockDevice* dev, uint8_t* secBuf, print_t* pr) {
     // SDXC cards
     m_sectorsPerCluster = 128;
   }
-  rtn = m_sectorCount < 0X400000 ? makeFat16() :makeFat32();
+  rtn = m_sectorCount < 0X400000 ? makeFat16() : makeFat32();
   if (rtn) {
     writeMsg("Format Done\r\n");
   } else {

+ 0 - 1
src/FatLib/FatLib.h

@@ -25,6 +25,5 @@
 #ifndef FatLib_h
 #define FatLib_h
 #include "FatVolume.h"
-#include "FatLibConfig.h"
 #include "FatFormatter.h"
 #endif  // FatLib_h

+ 354 - 0
src/FatLib/FatName.cpp

@@ -0,0 +1,354 @@
+/**
+ * Copyright (c) 2011-2020 Bill Greiman
+ * This file is part of the SdFat library for SD memory cards.
+ *
+ * MIT License
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+#define DBG_FILE "FatName.cpp"
+#include "../common/DebugMacros.h"
+#include "../common/FsUtf.h"
+#include "FatFile.h"
+#include "FatVolume.h"
+
+//------------------------------------------------------------------------------
+uint16_t FatFile::getLfnChar(DirLfn_t* ldir, uint8_t i) {
+  if (i < 5) {
+    return getLe16(ldir->unicode1 + 2*i);
+  } else if (i < 11) {
+    return getLe16(ldir->unicode2 + 2*i - 10);
+  } else if (i < 13) {
+    return getLe16(ldir->unicode3 + 2*i - 22);
+  }
+  DBG_HALT_IF(i >= 13);
+  return 0;
+}
+//------------------------------------------------------------------------------
+size_t FatFile::getName(char* name, size_t size) {
+#if !USE_LONG_FILE_NAMES
+  return getSFN(name, size);
+#elif USE_UTF8_LONG_NAMES
+  return getName8(name, size);
+#else
+  return getName7(name, size);
+#endif  // !USE_LONG_FILE_NAMES
+}
+//------------------------------------------------------------------------------
+size_t FatFile::getName7(char* name, size_t size) {
+  FatFile dir;
+  DirLfn_t* ldir;
+  char* ptr = name;
+  char* end = ptr + size;
+  if (!isOpen()) {
+    DBG_FAIL_MACRO;
+    goto fail;
+  }
+  if (!isLFN()) {
+    return getSFN(name, size);
+  }
+  if (!dir.openCluster(this)) {
+    DBG_FAIL_MACRO;
+    goto fail;
+  }
+  for (uint8_t order = 1; order <= m_lfnOrd; order++) {
+    ldir = reinterpret_cast<DirLfn_t*>(dir.cacheDir(m_dirIndex - order));
+    if (!ldir) {
+      DBG_FAIL_MACRO;
+      goto fail;
+    }
+    if (ldir->attributes != FAT_ATTRIB_LONG_NAME ||
+        order != (ldir->order & 0X1F)) {
+      DBG_FAIL_MACRO;
+      goto fail;
+    }
+    for (uint8_t i = 0; i < 13; i++) {
+      uint16_t c = getLfnChar(ldir, i);
+      if (c == 0 || (ptr + 1) == end) {
+        goto done;
+      }
+      *ptr++ = c >= 0X7F ? '?' : c;
+    }
+  }
+ done:
+  *ptr = '\0';
+  return ptr - name;
+
+ fail:
+  name[0] = '\0';
+  return 0;
+}
+//------------------------------------------------------------------------------
+size_t FatFile::getName8(char* name, size_t size) {
+  char* end = name + size;
+  char* str = name;
+  char* ptr;
+  FatFile dir;
+  DirLfn_t* ldir;
+  uint16_t hs = 0;
+  uint32_t cp;
+  if (!isOpen()) {
+      DBG_FAIL_MACRO;
+      goto fail;
+  }
+  if (!isLFN()) {
+    return getSFN(name, size);
+  }
+  if (!dir.openCluster(this)) {
+    DBG_FAIL_MACRO;
+    goto fail;
+  }
+  for (uint8_t order = 1; order <= m_lfnOrd; order++) {
+    ldir = reinterpret_cast<DirLfn_t*>(dir.cacheDir(m_dirIndex - order));
+    if (!ldir) {
+      DBG_FAIL_MACRO;
+      goto fail;
+    }
+    if (ldir->attributes != FAT_ATTRIB_LONG_NAME ||
+        order != (ldir->order & 0X1F)) {
+      DBG_FAIL_MACRO;
+      goto fail;
+    }
+    for (uint8_t i = 0; i < 13; i++) {
+      uint16_t c = getLfnChar(ldir, i);
+      if (hs) {
+        if (!FsUtf::isLowSurrogate(c)) {
+          DBG_FAIL_MACRO;
+          goto fail;
+        }
+        cp = FsUtf::u16ToCp(hs, c);
+        hs = 0;
+      } else if (!FsUtf::isSurrogate(c)) {
+        if (c == 0) {
+          goto done;
+        }
+        cp = c;
+      } else if (FsUtf::isHighSurrogate(c)) {
+        hs = c;
+        continue;
+      } else {
+        DBG_FAIL_MACRO;
+        goto fail;
+      }
+      // Save space for zero byte.
+      ptr = FsUtf::cpToMb(cp, str, end - 1);
+      if (!ptr) {
+        // Truncate name.  Could goto fail.
+        goto done;
+      }
+      str = ptr;
+    }
+  }
+ done:
+  *str = '\0';
+  return str - name;
+
+ fail:
+  *name = 0;
+  return 0;
+}
+//------------------------------------------------------------------------------
+size_t FatFile::getSFN(char* name, size_t size) {
+  char c;
+  uint8_t j = 0;
+  uint8_t lcBit = FAT_CASE_LC_BASE;
+  uint8_t* ptr;
+  DirFat_t* dir;
+  if (!isOpen()) {
+    DBG_FAIL_MACRO;
+    goto fail;
+  }
+  if (isRoot()) {
+    if (size < 2) {
+      DBG_FAIL_MACRO;
+      goto fail;
+    }
+    name[0] = '/';
+    name[1] = '\0';
+    return 1;
+  }
+  // cache entry
+  dir = cacheDirEntry(FsCache::CACHE_FOR_READ);
+  if (!dir) {
+    DBG_FAIL_MACRO;
+    goto fail;
+  }
+  ptr = dir->name;
+  // format name
+  for (uint8_t i = 0; i < 12; i++) {
+    if (i == 8) {
+      if (*ptr == ' ') {
+        break;
+      }
+      lcBit = FAT_CASE_LC_EXT;
+      c = '.';
+    } else {
+      c = *ptr++;
+      if ('A' <= c && c <= 'Z' && (lcBit & dir->caseFlags)) {
+        c += 'a' - 'A';
+      }
+      if (c == ' ') {
+        continue;
+      }
+    }
+    if ((j + 1u) == size) {
+      break;
+    }
+    name[j++] = c;
+  }
+  name[j] = '\0';
+  return j;
+
+ fail:
+  name[0] = '\0';
+  return 0;
+}
+//------------------------------------------------------------------------------
+size_t FatFile::printName(print_t* pr) {
+#if !USE_LONG_FILE_NAMES
+  return printSFN(pr);
+#elif USE_UTF8_LONG_NAMES
+  return printName8(pr);
+# else  // USE_LONG_FILE_NAMES
+  return printName7(pr);
+#endif  // !USE_LONG_FILE_NAMES
+  }
+//------------------------------------------------------------------------------
+size_t FatFile::printName7(print_t* pr) {
+  FatFile dir;
+  DirLfn_t* ldir;
+  size_t n = 0;
+  uint8_t buf[13];
+  uint8_t i;
+
+  if (!isOpen()) {
+      DBG_FAIL_MACRO;
+      goto fail;
+  }
+  if (!isLFN()) {
+    return printSFN(pr);
+  }
+  if (!dir.openCluster(this)) {
+    DBG_FAIL_MACRO;
+    goto fail;
+  }
+  for (uint8_t order = 1; order <= m_lfnOrd; order++) {
+    ldir = reinterpret_cast<DirLfn_t*>(dir.cacheDir(m_dirIndex - order));
+    if (!ldir) {
+      DBG_FAIL_MACRO;
+      goto fail;
+    }
+    if (ldir->attributes != FAT_ATTRIB_LONG_NAME ||
+        order != (ldir->order & 0X1F)) {
+      DBG_FAIL_MACRO;
+      goto fail;
+    }
+    for (i = 0; i < 13; i++) {
+      uint16_t u = getLfnChar(ldir, i);
+      if (u == 0) {
+        // End of name.
+        break;
+      }
+      buf[i] = u < 0X7F ? u : '?';
+      n++;
+    }
+    pr->write(buf, i);
+  }
+  return n;
+
+ fail:
+  return 0;
+}
+//------------------------------------------------------------------------------
+size_t FatFile::printName8(print_t *pr) {
+  FatFile dir;
+  DirLfn_t* ldir;
+  uint16_t hs = 0;
+  uint32_t cp;
+  size_t n = 0;
+  char buf[5];
+  char* end = buf + sizeof(buf);
+  if (!isOpen()) {
+      DBG_FAIL_MACRO;
+      goto fail;
+  }
+  if (!isLFN()) {
+    return printSFN(pr);
+  }
+  if (!dir.openCluster(this)) {
+    DBG_FAIL_MACRO;
+    goto fail;
+  }
+  for (uint8_t order = 1; order <= m_lfnOrd; order++) {
+    ldir = reinterpret_cast<DirLfn_t*>(dir.cacheDir(m_dirIndex - order));
+    if (!ldir) {
+      DBG_FAIL_MACRO;
+      goto fail;
+    }
+    if (ldir->attributes != FAT_ATTRIB_LONG_NAME ||
+        order != (ldir->order & 0X1F)) {
+      DBG_FAIL_MACRO;
+      goto fail;
+    }
+    for (uint8_t i = 0; i < 13; i++) {
+      uint16_t c = getLfnChar(ldir, i);;
+      if (hs) {
+        if (!FsUtf::isLowSurrogate(c)) {
+          DBG_FAIL_MACRO;
+          goto fail;
+        }
+        cp = FsUtf::u16ToCp(hs, c);
+        hs = 0;
+      } else if (!FsUtf::isSurrogate(c)) {
+        if (c == 0) {
+          break;
+        }
+        cp = c;
+      } else if (FsUtf::isHighSurrogate(c)) {
+        hs = c;
+        continue;
+      } else {
+        DBG_FAIL_MACRO;
+        goto fail;
+      }
+      char* str = FsUtf::cpToMb(cp, buf, end);
+      if (!str) {
+        DBG_FAIL_MACRO;
+        goto fail;
+      }
+      n += pr->write(buf, str - buf);
+    }
+  }
+  return n;
+
+ fail:
+  return 0;
+}
+//------------------------------------------------------------------------------
+size_t FatFile::printSFN(print_t* pr) {
+  char name[13];
+  if (!getSFN(name, sizeof(name))) {
+    DBG_FAIL_MACRO;
+    goto fail;
+  }
+  return pr->write(name);
+
+ fail:
+  return 0;
+}

+ 6 - 5
src/FatLib/FatPartition.h

@@ -29,7 +29,6 @@
  * \brief FatPartition class
  */
 #include <stddef.h>
-#include "FatLibConfig.h"
 #include "../common/SysCall.h"
 #include "../common/BlockDevice.h"
 #include "../common/FsCache.h"
@@ -94,8 +93,7 @@ class FatPartition {
     return m_sectorsPerCluster;
   }
 #ifndef DOXYGEN_SHOULD_SKIP_THIS
-  // Use sectorsPerCluster(). blocksPerCluster() will be removed in the future.
-  uint8_t blocksPerCluster() __attribute__ ((deprecated)) {return sectorsPerCluster();} //NOLINT
+  uint8_t __attribute__((error("use sectorsPerCluster()"))) blocksPerCluster();
 #endif  // DOXYGEN_SHOULD_SKIP_THIS
   /** \return The number of sectors in one FAT. */
   uint32_t sectorsPerFat()  const {
@@ -177,9 +175,9 @@ class FatPartition {
   bool isBusy() {return m_blockDev->isBusy();}
   //----------------------------------------------------------------------------
 #ifndef DOXYGEN_SHOULD_SKIP_THIS
-  void dmpDirSector(print_t* pr, uint32_t sector);
+  bool dmpDirSector(print_t* pr, uint32_t sector);
   void dmpFat(print_t* pr, uint32_t start, uint32_t count);
-  void dmpRootDir(print_t* pr);
+  bool dmpRootDir(print_t* pr, uint32_t n = 0);
   void dmpSector(print_t* pr, uint32_t sector, uint8_t bits = 8);
 #endif  // DOXYGEN_SHOULD_SKIP_THIS
   //----------------------------------------------------------------------------
@@ -301,4 +299,7 @@ class FatPartition {
     return cluster > m_lastCluster;
   }
 };
+#ifndef DOXYGEN_SHOULD_SKIP_THIS
+bool printFatDir(print_t* pr, DirFat_t* dir);
+#endif  // DOXYGEN_SHOULD_SKIP_THIS
 #endif  // FatPartition

+ 4 - 0
src/FatLib/FatVolume.cpp

@@ -22,15 +22,19 @@
  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  * DEALINGS IN THE SOFTWARE.
  */
+#define DBG_FILE "FatVolume.cpp"
+#include "../common/DebugMacros.h"
 #include "FatVolume.h"
 FatVolume* FatVolume::m_cwv = nullptr;
 //------------------------------------------------------------------------------
 bool FatVolume::chdir(const char *path) {
   FatFile dir;
   if (!dir.open(vwd(), path, O_RDONLY)) {
+    DBG_FAIL_MACRO;
     goto fail;
   }
   if (!dir.isDir()) {
+    DBG_FAIL_MACRO;
     goto fail;
   }
   m_vwd = dir;

+ 4 - 7
src/FsLib/FsFile.h

@@ -360,9 +360,9 @@ class FsBaseFile {
    * Use getName(char* name, size_t size).
    * \return a pointer to replacement suggestion.
    */
-  const char* name() const {
-    return "use getName()";
-  }
+#ifndef DOXYGEN_SHOULD_SKIP_THIS
+  const char* __attribute__((error("use getName(name, size)"))) name();
+#endif  // DOXYGEN_SHOULD_SKIP_THIS
   /** Open a file or directory by name.
    *
    * \param[in] dir An open file instance for the directory containing
@@ -777,10 +777,7 @@ class FsBaseFile {
    * \param[in] count Number of bytes to write.
    *
    * \return For success write() returns the number of bytes written, always
-   * \a nbyte.  If an error occurs, write() returns -1.  Possible errors
-   * 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.
+   * \a nbyte.  If an error occurs, write() returns zero and writeError is set.
    */
   size_t write(const void* buf, size_t count) {
     return m_fFile ? m_fFile->write(buf, count) :

+ 1 - 2
src/FsLib/FsVolume.h

@@ -50,8 +50,7 @@ class FsVolume {
    */
   bool begin(BlockDevice* blockDev);
 #ifndef DOXYGEN_SHOULD_SKIP_THIS
-  // Use sectorsPerCluster(). blocksPerCluster() will be removed in the future.
-  uint32_t blocksPerCluster() __attribute__ ((deprecated)) {return sectorsPerCluster();} //NOLINT
+  uint32_t __attribute__((error("use sectorsPerCluster()"))) blocksPerCluster();
 #endif  // DOXYGEN_SHOULD_SKIP_THIS
   /** \return the number of bytes in a cluster. */
   uint32_t bytesPerCluster() const {

+ 11 - 3
src/RingBuf.h

@@ -28,7 +28,7 @@
  * \file
  * \brief Ring buffer for data loggers.
  */
-#include "Arduino.h"
+#include "common/SysCall.h"
 #include "common/FmtNumber.h"
 
 #ifndef DOXYGEN_SHOULD_SKIP_THIS
@@ -266,8 +266,7 @@ class RingBuf : public Print {
   }
   /**
    * Write all data in the RingBuf to the underlying file.
-   * \param[in] data Byte to be written.
-   * \return Number of bytes actually written.
+   * \return true for success.
    */
   bool sync() {
     size_t n = bytesUsed();
@@ -291,6 +290,15 @@ class RingBuf : public Print {
     }
     return memcpyIn(buf, count);
   }
+  /**
+   * Copy str to RingBuf.
+   *
+   * \param[in] str Location of data to be written.
+   * \return Number of bytes actually written.
+   */
+  size_t write(const char* str) {
+    return Print::write(str);
+  }
   /**
    * Override virtual function in Print for efficiency.
    *

+ 5 - 9
src/SdCard/SdSpiCard.cpp

@@ -437,17 +437,11 @@ bool SdSpiCard::isBusy() {
     return false;
   }
 #endif  // ENABLE_DEDICATED_SPI
-  bool rtn = true;
   bool spiActive = m_spiActive;
   if (!spiActive) {
     spiStart();
   }
-  for (uint8_t i = 0; i < 8; i++) {
-    if (0XFF == spiReceive()) {
-      rtn = false;
-      break;
-    }
-  }
+  bool rtn = 0XFF != spiReceive();
   if (!spiActive) {
     spiStop();
   }
@@ -576,7 +570,6 @@ bool SdSpiCard::readStart(uint32_t sector) {
     error(SD_CARD_ERROR_CMD18);
     goto fail;
   }
-//  spiStop();
   return true;
 
  fail:
@@ -654,6 +647,8 @@ void SdSpiCard::spiStart() {
   if (!m_spiActive) {
     spiActivate();
     spiSelect();
+    // Dummy byte to drive MISO busy status.
+    spiSend(0XFF);    
     m_spiActive = true;
   }
 }
@@ -661,7 +656,8 @@ void SdSpiCard::spiStart() {
 void SdSpiCard::spiStop() {
   if (m_spiActive) {
     spiUnselect();
-    spiSend(0XFF);
+    // Insure MISO goes to low Z.
+    spiSend(0XFF);        
     spiDeactivate();
     m_spiActive = false;
   }

+ 3 - 3
src/SdCard/SdSpiCard.h

@@ -66,7 +66,7 @@ class SdSpiCard {
   uint32_t sectorCount();
 #ifndef DOXYGEN_SHOULD_SKIP_THIS
   // Use sectorCount(). cardSize() will be removed in the future.
-  uint32_t cardSize() __attribute__ ((deprecated)) {return sectorCount();}
+  uint32_t __attribute__((error("use sectorCount()"))) cardSize();
 #endif  // DOXYGEN_SHOULD_SKIP_THIS
   /** Erase a range of sectors.
    *
@@ -310,7 +310,7 @@ class SdSpiCard {
     return m_spiDriver.receive();
   }
   uint8_t spiReceive(uint8_t* buf, size_t n) {
-    return  m_spiDriver.receive(buf, n);
+    return m_spiDriver.receive(buf, n);
   }
   void spiSend(uint8_t data) {
     m_spiDriver.send(data);
@@ -336,7 +336,7 @@ class SdSpiCard {
     return m_spiDriverPtr->receive();
   }
   uint8_t spiReceive(uint8_t* buf, size_t n) {
-    return  m_spiDriverPtr->receive(buf, n);
+    return m_spiDriverPtr->receive(buf, n);
   }
   void spiSend(uint8_t data) {
     m_spiDriverPtr->send(data);

+ 1 - 2
src/SdCard/SdioCard.h

@@ -66,8 +66,7 @@ class SdioCard : public SdCardInterface {
   bool end() {return false;}
 
 #ifndef DOXYGEN_SHOULD_SKIP_THIS
-  // Use sectorCount(). cardSize() will be removed in the future.
-  uint32_t cardSize() __attribute__ ((deprecated)) {return sectorCount();}
+    uint32_t __attribute__((error("use sectorCount()"))) cardSize();
 #endif  // DOXYGEN_SHOULD_SKIP_THIS
   /** Erase a range of sectors.
    *

+ 3 - 0
src/SdCard/SdioTeensy.cpp

@@ -27,6 +27,9 @@
 #include "SdCardInfo.h"
 #include "SdioCard.h"
 //==============================================================================
+/** Set zero to disable mod for non-blocking write. */
+#define ENABLE_TEENSY_SDIO_MOD 1
+//==============================================================================
 // limit of K66 due to errata KINETIS_K_0N65N.
 const uint32_t MAX_BLKCNT = 0XFFFF;
 //==============================================================================

+ 22 - 13
src/SdFat.h

@@ -37,10 +37,10 @@
 #include "sdios.h"
 #endif  // INCLUDE_SDIOS
 //------------------------------------------------------------------------------
-/** SdFat version  for cpp use. */
-#define SD_FAT_VERSION 20007
+/** SdFat version for cpp use. */
+#define SD_FAT_VERSION 20100
 /** SdFat version as string. */
-#define SD_FAT_VERSION_STR "2.0.7"
+#define SD_FAT_VERSION_STR "2.1.0"
 //==============================================================================
 /**
  * \class SdBase
@@ -418,27 +418,36 @@ class SdFs : public SdBase<FsVolume> {
 #if SDFAT_FILE_TYPE == 1
 /** Select type for SdFat. */
 typedef SdFat32 SdFat;
-/** Select type for File. */
-#if !defined(__has_include) || !__has_include(<FS.h>)
-typedef File32 File;
-#endif
 /** Select type for SdBaseFile. */
 typedef FatFile SdBaseFile;
 #elif SDFAT_FILE_TYPE == 2
 typedef SdExFat SdFat;
-#if !defined(__has_include) || !__has_include(<FS.h>)
-typedef ExFile File;
-#endif
 typedef ExFatFile SdBaseFile;
 #elif SDFAT_FILE_TYPE == 3
 typedef SdFs SdFat;
-#if !defined(__has_include) || !__has_include(<FS.h>)
-typedef FsFile File;
-#endif
 typedef FsBaseFile SdBaseFile;
 #else  // SDFAT_FILE_TYPE
 #error Invalid SDFAT_FILE_TYPE
 #endif  // SDFAT_FILE_TYPE
+//
+// Only define File if FS.h is not included.
+// Line with test for __has_include must not have operators or parentheses.
+#if defined __has_include
+#if __has_include(<FS.h>)
+#define HAS_INCLUDE_FS_H
+#warning File not defined because __has__include(FS.h)
+#endif  // __has_include(<FS.h>)
+#endif  // defined __has_include
+#ifndef HAS_INCLUDE_FS_H
+#if SDFAT_FILE_TYPE == 1
+/** Select type for File. */
+typedef File32 File;
+#elif SDFAT_FILE_TYPE == 2
+typedef ExFile File;
+#elif SDFAT_FILE_TYPE == 3
+typedef FsFile File;
+#endif  // SDFAT_FILE_TYPE
+#endif  // HAS_INCLUDE_FS_H
 /**
  * \class SdFile
  * \brief FAT16/FAT32 file with Print.

+ 128 - 43
src/SdFatConfig.h

@@ -32,6 +32,19 @@
 #ifdef __AVR__
 #include <avr/io.h>
 #endif  // __AVR__
+//
+// To try UTF-8 encoded filenames.
+// #define USE_UTF8_LONG_NAMES 1
+//
+// For minimum flash size use these settings:
+// #define USE_FAT_FILE_FLAG_CONTIGUOUS 0
+// #define ENABLE_DEDICATED_SPI 0
+// #define USE_LONG_FILE_NAMES 0
+// #define SDFAT_FILE_TYPE 1
+//
+// Options can be set in a makefile or an IDE like platformIO
+// if they are in a #ifndef/#endif block below.
+//------------------------------------------------------------------------------
 /** For Debug - must be one */
 #define ENABLE_ARDUINO_FEATURES 1
 /** For Debug - must be one */
@@ -39,12 +52,6 @@
 /** For Debug - must be one */
 #define ENABLE_ARDUINO_STRING 1
 //------------------------------------------------------------------------------
-/** Set zero to disable mod for non-blocking write. */
-#define ENABLE_TEENSY_SDIO_MOD 1
-//------------------------------------------------------------------------------
-/** Set USE_BLOCK_DEVICE_INTERFACE nonzero to use generic block device */
-#define USE_BLOCK_DEVICE_INTERFACE 0
-//------------------------------------------------------------------------------
 #if ENABLE_ARDUINO_FEATURES
 #include "Arduino.h"
 #ifdef PLATFORM_ID
@@ -53,18 +60,6 @@
 #endif  // PLATFORM_ID
 #endif  // ENABLE_ARDUINO_FEATURES
 //------------------------------------------------------------------------------
-/**
- * Set INCLUDE_SDIOS nonzero to include sdios.h in SdFat.h.
- * sdios.h provides C++ style IO Streams.
- */
-#define INCLUDE_SDIOS 0
-//------------------------------------------------------------------------------
-/**
- * Set USE_FAT_FILE_FLAG_CONTIGUOUS nonzero to optimize access to
- * contiguous files.
- */
-#define USE_FAT_FILE_FLAG_CONTIGUOUS 1
-//------------------------------------------------------------------------------
 /**
  * File types for SdFat, File, SdFile, SdBaseFile, fstream,
  * ifstream, and ofstream.
@@ -73,6 +68,7 @@
  *
  * 1 for FAT16/FAT32, 2 for exFAT, 3 for FAT16/FAT32 and exFAT.
  */
+#ifndef SDFAT_FILE_TYPE
 #if defined(__AVR__) && FLASHEND < 0X8000
 // 32K AVR boards.
 #define SDFAT_FILE_TYPE 1
@@ -83,6 +79,15 @@
 // All other boards.
 #define SDFAT_FILE_TYPE 1
 #endif  // defined(__AVR__) && FLASHEND < 0X8000
+#endif  // SDFAT_FILE_TYPE
+//------------------------------------------------------------------------------
+/**
+ * Set USE_FAT_FILE_FLAG_CONTIGUOUS nonzero to optimize access to
+ * contiguous files.  A small amount of flash is flash is used.
+ */
+#ifndef USE_FAT_FILE_FLAG_CONTIGUOUS
+#define USE_FAT_FILE_FLAG_CONTIGUOUS 1
+#endif  // USE_FAT_FILE_FLAG_CONTIGUOUS
 //------------------------------------------------------------------------------
 /**
  * Set ENABLE_DEDICATED_SPI non-zero to enable dedicated use of the SPI bus.
@@ -90,8 +95,9 @@
  * performance by using very large multi-block transfers to and
  * from the SD card.
  *
- * Enabling dedicated SPI will cost some extra flash and RAM.
+ * Enabling dedicated SPI will cost extra flash and RAM.
  */
+#ifndef ENABLE_DEDICATED_SPI
 #if defined(__AVR__) && FLASHEND < 0X8000
 // 32K AVR boards.
 #define ENABLE_DEDICATED_SPI 1
@@ -99,7 +105,9 @@
 // All other boards.
 #define ENABLE_DEDICATED_SPI 1
 #endif  // defined(__AVR__) && FLASHEND < 0X8000
+#endif  // ENABLE_DEDICATED_SPI
 //------------------------------------------------------------------------------
+// Driver options
 /**
  * If the symbol SPI_DRIVER_SELECT is:
  *
@@ -112,16 +120,50 @@
  *
  * 3 - An external SPI driver derived from SdSpiBaseClass is always used.
  */
+#ifndef SPI_DRIVER_SELECT
 #define SPI_DRIVER_SELECT 0
+#endif  // SPI_DRIVER_SELECT
 /**
  * If USE_SPI_ARRAY_TRANSFER is non-zero and the standard SPI library is
  * use, the array transfer function, transfer(buf, size), will be used.
  * This option will allocate up to a 512 byte temporary buffer for send.
  * This may be faster for some boards.  Do not use this with AVR boards.
  */
+#ifndef USE_SPI_ARRAY_TRANSFER
 #define USE_SPI_ARRAY_TRANSFER 0
-//------------------------------------------------------------------------------
+#endif  // USE_SPI_ARRAY_TRANSFER
+/**
+ * SD maximum initialization clock rate.
+ */
+#ifndef SD_MAX_INIT_RATE_KHZ
+#define SD_MAX_INIT_RATE_KHZ 400
+#endif  // SD_MAX_INIT_RATE_KHZ
 /**
+ * Set USE_BLOCK_DEVICE_INTERFACE nonzero to use a generic block device.
+ * This allow use of an external BlockDevice driver that is derived from
+ * the BlockDeviceInterface like this:
+ *
+ * class UsbMscDriver : public BlockDeviceInterface {
+ *   ... code for USB mass storage class driver.
+ * };
+ *
+ * UsbMscDriver usbMsc;
+ * FsVolume key;
+ * ...
+ *
+ *   // Init USB MSC driver.
+ *   if (!usbMsc.begin()) {
+ *     ... handle driver init failure.
+ *   }
+ *   // Init FAT/exFAT volume.
+ *   if (!key.begin(&usbMsc)) {
+ *     ... handle FAT/exFAT failure.
+ *   }
+ */
+#ifndef USE_BLOCK_DEVICE_INTERFACE
+#define USE_BLOCK_DEVICE_INTERFACE 0
+#endif  // USE_BLOCK_DEVICE_INTERFACE
+ /**
  * SD_CHIP_SELECT_MODE defines how the functions
  * void sdCsInit(SdCsPin_t pin) {pinMode(pin, OUTPUT);}
  * and
@@ -134,15 +176,12 @@
  *
  * 2 - No internal definition and must be defined in the application.
  */
+#ifndef SD_CHIP_SELECT_MODE
 #define SD_CHIP_SELECT_MODE 0
+#endif  // SD_CHIP_SELECT_MODE
 /** Type for card chip select pin. */
 typedef uint8_t SdCsPin_t;
 //------------------------------------------------------------------------------
-/**
- * SD maximum initialization clock rate.
- */
-#define SD_MAX_INIT_RATE_KHZ 400
-//------------------------------------------------------------------------------
 /**
  * Set USE_LONG_FILE_NAMES nonzero to use long file names (LFN) in FAT16/FAT32.
  * exFAT always uses long file names.
@@ -163,7 +202,37 @@ typedef uint8_t SdCsPin_t;
  *  * (asterisk)
  *
  */
+#ifndef USE_LONG_FILE_NAMES
 #define USE_LONG_FILE_NAMES 1
+#endif  // USE_LONG_FILE_NAMES
+/**
+ * Set USE_UTF8_LONG_NAMES nonzero to use UTF-8 file names. Use of UTF-8 names
+ * will require significantly more flash memory and a small amount of extra
+ * RAM.
+ *
+ * UTF-8 filenames allow encoding of 1,112,064 code points in Unicode using
+ * one to four one-byte (8-bit) code units.
+ *
+ * As of Version 13.0, the Unicode Standard defines 143,859 characters.
+ *
+ * getName() will return UTF-8 strings and printName() will write UTF-8 strings.
+ */
+#ifndef USE_UTF8_LONG_NAMES
+#define USE_UTF8_LONG_NAMES 0
+#endif  // USE_UTF8_LONG_NAMES
+
+#if USE_UTF8_LONG_NAMES && !USE_LONG_FILE_NAMES
+#error "USE_UTF8_LONG_NAMES requires USE_LONG_FILE_NAMES to be non-zero."
+#endif  // USE_UTF8_LONG_NAMES && !USE_LONG_FILE_NAMES
+//------------------------------------------------------------------------------
+/**
+ * Set MAINTAIN_FREE_CLUSTER_COUNT nonzero to keep the count of free clusters
+ * updated.  This will increase the speed of the freeClusterCount() call
+ * after the first call.  Extra flash will be required.
+ */
+#ifndef MAINTAIN_FREE_CLUSTER_COUNT
+#define MAINTAIN_FREE_CLUSTER_COUNT 0
+#endif  // MAINTAIN_FREE_CLUSTER_COUNT
 //------------------------------------------------------------------------------
 /**
  * Set the default file time stamp when a RTC callback is not used.
@@ -191,14 +260,9 @@ typedef uint8_t SdCsPin_t;
  * Some cards will not sleep in low power mode unless CHECK_FLASH_PROGRAMMING
  * is non-zero.
  */
+#ifndef CHECK_FLASH_PROGRAMMING
 #define CHECK_FLASH_PROGRAMMING 0
-//------------------------------------------------------------------------------
-/**
- * Set MAINTAIN_FREE_CLUSTER_COUNT nonzero to keep the count of free clusters
- * updated.  This will increase the speed of the freeClusterCount() call
- * after the first call.  Extra flash will be required.
- */
-#define MAINTAIN_FREE_CLUSTER_COUNT 0
+#endif  // CHECK_FLASH_PROGRAMMING
 //------------------------------------------------------------------------------
 /**
  * To enable SD card CRC checking for SPI, set USE_SD_CRC nonzero.
@@ -209,12 +273,15 @@ typedef uint8_t SdCsPin_t;
  * Set USE_SD_CRC to 2 to used a larger table driven CRC-CCITT function.  This
  * function is faster for AVR but may be slower for ARM and other processors.
  */
+#ifndef USE_SD_CRC
 #define USE_SD_CRC 0
+#endif  // USE_SD_CRC
 //------------------------------------------------------------------------------
 /** If the symbol USE_FCNTL_H is nonzero, open flags for access modes O_RDONLY,
  * O_WRONLY, O_RDWR and the open modifiers O_APPEND, O_CREAT, O_EXCL, O_SYNC
  * will be defined by including the system file fcntl.h.
  */
+#ifndef USE_FCNTL_H
 #if defined(__AVR__)
 // AVR fcntl.h does not define open flags.
 #define USE_FCNTL_H 0
@@ -229,32 +296,32 @@ typedef uint8_t SdCsPin_t;
 #else  // defined(__AVR__)
 #define USE_FCNTL_H 0
 #endif  // defined(__AVR__)
+#endif  // USE_FCNTL_H
 //------------------------------------------------------------------------------
 /**
- * Handle Watchdog Timer for WiFi modules.
- *
- * Yield will be called before accessing the SPI bus if it has been more
- * than WDT_YIELD_TIME_MILLIS milliseconds since the last yield call by SdFat.
+ * Set INCLUDE_SDIOS nonzero to include sdios.h in SdFat.h.
+ * sdios.h provides C++ style IO Streams.
  */
-#if defined(PLATFORM_ID) || defined(ESP8266)
-// If Particle device or ESP8266 call yield.
-#define WDT_YIELD_TIME_MILLIS 100
-#else  // defined(PLATFORM_ID) || defined(ESP8266)
-#define WDT_YIELD_TIME_MILLIS 0
-#endif  // defined(PLATFORM_ID) || defined(ESP8266)
+#ifndef INCLUDE_SDIOS
+#define INCLUDE_SDIOS 0
+#endif  // INCLUDE_SDIOS
 //------------------------------------------------------------------------------
 /**
  * Set FAT12_SUPPORT nonzero to enable use if FAT12 volumes.
  * FAT12 has not been well tested and requires additional flash.
  */
+#ifndef FAT12_SUPPORT
 #define FAT12_SUPPORT 0
+#endif  // FAT12_SUPPORT
 //------------------------------------------------------------------------------
 /**
  * Set DESTRUCTOR_CLOSES_FILE nonzero to close a file in its destructor.
  *
  * Causes use of lots of heap in ARM.
  */
+#ifndef DESTRUCTOR_CLOSES_FILE
 #define DESTRUCTOR_CLOSES_FILE 0
+#endif  // DESTRUCTOR_CLOSES_FILE
 //------------------------------------------------------------------------------
 /**
  * Call flush for endl if ENDL_CALLS_FLUSH is nonzero
@@ -273,7 +340,22 @@ typedef uint8_t SdCsPin_t;
  * If ENDL_CALLS_FLUSH is zero, you must call flush and/or close to force
  * all data to be written to the SD.
  */
+#ifndef ENDL_CALLS_FLUSH
 #define ENDL_CALLS_FLUSH 0
+#endif  // ENDL_CALLS_FLUSH
+//------------------------------------------------------------------------------
+/**
+ * Handle Watchdog Timer for WiFi modules.
+ *
+ * Yield will be called before accessing the SPI bus if it has been more
+ * than WDT_YIELD_TIME_MILLIS milliseconds since the last yield call by SdFat.
+ */
+#if defined(PLATFORM_ID) || defined(ESP8266)
+// If Particle device or ESP8266 call yield.
+#define WDT_YIELD_TIME_MILLIS 100
+#else  // defined(PLATFORM_ID) || defined(ESP8266)
+#define WDT_YIELD_TIME_MILLIS 0
+#endif  // defined(PLATFORM_ID) || defined(ESP8266)
 //------------------------------------------------------------------------------
 /**
  * Set USE_SIMPLE_LITTLE_ENDIAN nonzero for little endian processors
@@ -343,10 +425,12 @@ typedef uint8_t SdCsPin_t;
  * Determine the default SPI configuration.
  */
 #if defined(ARDUINO_ARCH_APOLLO3)\
-  || defined(__AVR__)\
+  || (defined(__AVR__) && defined(SPDR) && defined(SPSR) && defined(SPIF))\
+  || (defined(__AVR__) && defined(SPI0) && defined(SPI_RXCIF_bm))\
   || defined(ESP8266) || defined(ESP32)\
   || defined(PLATFORM_ID)\
   || defined(ARDUINO_SAM_DUE)\
+  || defined(STM32_CORE_VERSION)\
   || defined(__STM32F1__) || defined(__STM32F4__)\
   || (defined(CORE_TEENSY) && defined(__arm__))
 #define SD_HAS_CUSTOM_SPI 1
@@ -359,4 +443,5 @@ typedef uint8_t SdCsPin_t;
 /** Default is no SDIO. */
 #define HAS_SDIO_CLASS 0
 #endif  // HAS_SDIO_CLASS
+
 #endif  // SdFatConfig_h

+ 1 - 1
src/SpiDriver/SdSpiArtemis.cpp

@@ -47,7 +47,7 @@ uint8_t SdSpiArduinoDriver::receive() {
 }
 //------------------------------------------------------------------------------
 uint8_t SdSpiArduinoDriver::receive(uint8_t* buf, size_t count) {
-  memset(buf, 0XFF, count); 
+  memset(buf, 0XFF, count);
   m_spi->transfer(buf, count);
   return 0;
 }

+ 43 - 9
src/SpiDriver/SdSpiAvr.h

@@ -48,19 +48,37 @@ inline uint8_t SdSpiArduinoDriver::receive(uint8_t* buf, size_t count) {
   if (count == 0) {
     return 0;
   }
-  uint8_t* pr = buf;
+#ifdef SPSR
   SPDR = 0XFF;
-  while (--count > 0) {
+  while (--count) {
+    // nops optimize loop for 16MHz CPU 8 MHz SPI
+    nop;
+    nop;
     while (!(SPSR & _BV(SPIF))) {}
     uint8_t in = SPDR;
     SPDR = 0XFF;
-    *pr++ = in;
-    // nops to optimize loop for 16MHz CPU 8 MHz SPI
+    *buf++ = in;
+  }
+  while (!(SPSR & _BV(SPIF))) {}
+  *buf = SPDR;
+#elif defined(SPI_RXCIF_bm)
+  SPI0.DATA = 0XFF;
+  while (--count) {
+    // nops optimize loop for ATmega4809 16MHz CPU 8 MHz SPI
     nop;
     nop;
+    nop;
+    nop;
+    while (!(SPI0.INTFLAGS & SPI_RXCIF_bm)) {}
+    uint8_t in = SPI0.DATA;
+    SPI0.DATA = 0XFF;
+    *buf++ = in;
   }
-  while (!(SPSR & _BV(SPIF))) {}
-  *pr = SPDR;
+  while (!(SPI0.INTFLAGS & SPI_RXCIF_bm)) {}
+  *buf = SPI0.DATA;
+#else  // SPSR
+#error Unsupported AVR CPU - edit SdFatConfig.h to use standard SPI library.
+#endif  // SPSR
   return 0;
 }
 //------------------------------------------------------------------------------
@@ -72,15 +90,31 @@ inline void SdSpiArduinoDriver::send(const uint8_t* buf , size_t count) {
   if (count == 0) {
     return;
   }
+#ifdef SPSR
   SPDR = *buf++;
-  while (--count > 0) {
+  while (--count) {
     uint8_t b = *buf++;
+    // nops optimize loop for 16MHz CPU 8 MHz SPI
+    nop;
+    nop;
     while (!(SPSR & (1 << SPIF))) {}
     SPDR = b;
-    // nops to optimize loop for 16MHz CPU 8 MHz SPI
+  }
+  while (!(SPSR & (1 << SPIF))) {}
+#elif defined(SPI_RXCIF_bm)
+  SPI0.DATA = *buf++;
+  while (--count) {
+    uint8_t b = *buf++;
+    // nops optimize loop for ATmega4809 16MHz CPU 8 MHz SPI
+    nop;
     nop;
     nop;
+    while (!(SPI0.INTFLAGS & SPI_RXCIF_bm)) {}
+    SPI0.DATA = b;
   }
-  while (!(SPSR & (1 << SPIF))) {}
+  while (!(SPI0.INTFLAGS & SPI_RXCIF_bm)) {}
+#else  // SPSR
+#error Unsupported AVR CPU - edit SdFatConfig.h to use standard SPI library.
+#endif  // SPSR
 }
 #endif  // SdSpiAvr_h

+ 3 - 3
src/SpiDriver/SdSpiLibDriver.h

@@ -73,14 +73,14 @@ inline void SdSpiArduinoDriver::send(uint8_t data) {
 inline void SdSpiArduinoDriver::send(const uint8_t* buf, size_t count) {
 #if USE_SPI_ARRAY_TRANSFER
   if (count <= 512) {
-    uint8_t tmp[count];    // NOLINT
+    uint8_t tmp[512];
     memcpy(tmp, buf, count);
     m_spi->transfer(tmp, count);
-    return;
   }
-#endif  // USE_SPI_ARRAY_TRANSFER
+#else  // USE_SPI_ARRAY_TRANSFER
   for (size_t i = 0; i < count; i++) {
     m_spi->transfer(buf[i]);
   }
+#endif  // USE_SPI_ARRAY_TRANSFER  
 }
 #endif  // SdSpiLibDriver_h

+ 1 - 0
src/SpiDriver/SdSpiSTM32.cpp

@@ -22,6 +22,7 @@
  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  * DEALINGS IN THE SOFTWARE.
  */
+// Driver for: https://github.com/rogerclarkmelbourne/Arduino_STM32
 #include "SdSpiDriver.h"
 #if defined(SD_USE_CUSTOM_SPI)\
   && (defined(__STM32F1__) || defined(__STM32F4__))

+ 71 - 0
src/SpiDriver/SdSpiSTM32Core.cpp

@@ -0,0 +1,71 @@
+/**
+ * Copyright (c) 2011-2020 Bill Greiman
+ * This file is part of the SdFat library for SD memory cards.
+ *
+ * MIT License
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+// Driver for: https://github.com/stm32duino/Arduino_Core_STM32
+#include "SdSpiDriver.h"
+#if defined(SD_USE_CUSTOM_SPI) && defined(STM32_CORE_VERSION)
+//------------------------------------------------------------------------------
+void SdSpiArduinoDriver::activate() {
+  m_spi->beginTransaction(m_spiSettings);
+}
+//------------------------------------------------------------------------------
+void SdSpiArduinoDriver::begin(SdSpiConfig spiConfig) {
+  if (spiConfig.spiPort) {
+    m_spi = spiConfig.spiPort;
+  } else {
+    m_spi = &SPI;
+  }
+  m_spi->begin();
+}
+//------------------------------------------------------------------------------
+void SdSpiArduinoDriver::deactivate() {
+  m_spi->endTransaction();
+}
+//------------------------------------------------------------------------------
+uint8_t SdSpiArduinoDriver::receive() {
+  return m_spi->transfer(0XFF);
+}
+//------------------------------------------------------------------------------
+uint8_t SdSpiArduinoDriver::receive(uint8_t* buf, size_t count) {
+  // Must send 0XFF - SD looks at send data for command.
+  memset(buf, 0XFF, count);
+  m_spi->transfer(buf, count);
+  return 0;
+}
+//------------------------------------------------------------------------------
+void SdSpiArduinoDriver::send(uint8_t data) {
+  m_spi->transfer(data);
+}
+//------------------------------------------------------------------------------
+void SdSpiArduinoDriver::send(const uint8_t* buf, size_t count) {
+  // Avoid stack overflow if bad count.  This should cause a write error.
+  if (count > 512) {
+    return;
+  }
+  // Not easy to avoid receive so use tmp RX buffer.
+  uint8_t rxBuf[512];
+  // Discard const - STM32 not const correct.
+  m_spi->transfer(const_cast<uint8_t*>(buf), rxBuf, count);
+}
+#endif  // defined(SD_USE_CUSTOM_SPI) && defined(STM32_CORE_VERSION)

+ 4 - 1
src/common/ArduinoFiles.h

@@ -24,7 +24,7 @@
  */
 #ifndef ArduinoFiles_h
 #define ArduinoFiles_h
-#include "../SdFatConfig.h"
+#include "SdFatConfig.h"
 //------------------------------------------------------------------------------
 /** Arduino SD.h style flag for open for read. */
 #ifndef FILE_READ
@@ -90,6 +90,9 @@ class StreamFile : public stream_t, public BaseFile {
    * Use getName(char* name, size_t size).
    * \return a pointer to replacement suggestion.
    */
+#ifndef DOXYGEN_SHOULD_SKIP_THIS
+  const char* __attribute__((error("use getName(name, size)"))) name();
+#endif  // DOXYGEN_SHOULD_SKIP_THIS
   const char* name() const {return "use getName()";}
   /** Return the next available byte without consuming it.
    *

+ 1 - 1
src/common/BlockDeviceInterface.h

@@ -30,7 +30,7 @@
 #define BlockDeviceInterface_h
 #include <stdint.h>
 #include <stddef.h>
-#include "../SdFatConfig.h"
+#include "SdFatConfig.h"
 /**
  * \class BlockDeviceInterface
  * \brief BlockDeviceInterface class.

+ 30 - 9
src/common/DebugMacros.h

@@ -24,7 +24,9 @@
  */
 #ifndef DebugMacros_h
 #define DebugMacros_h
-#include "../SdFatConfig.h"
+#include "SdFatConfig.h"
+
+// 0 - disable, 1 - fail, halt 2 - fail, halt, warn
 #define USE_DBG_MACROS 0
 
 #if USE_DBG_MACROS
@@ -32,22 +34,41 @@
 #ifndef DBG_FILE
 #error DBG_FILE not defined
 #endif  // DBG_FILE
-static void dbgPrint(uint16_t line) {
+
+__attribute__((unused)) static void dbgFail(uint16_t line) {
   Serial.print(F("DBG_FAIL: "));
   Serial.print(F(DBG_FILE));
   Serial.write('.');
   Serial.println(line);
 }
+__attribute__((unused)) static void dbgHalt(uint16_t line) {
+  Serial.print(F("DBG_HALT: "));
+  Serial.print(F(DBG_FILE));
+  Serial.write('.');
+  Serial.println(line);
+  while (true) {}
+}
+#define DBG_FAIL_MACRO dbgFail(__LINE__)
+#define DBG_HALT_MACRO dbgHalt(__LINE__)
+#define DBG_HALT_IF(b) if (b) {dbgHalt(__LINE__);}
 
-#define DBG_PRINT_IF(b) if (b) {Serial.print(F(__FILE__));\
-                        Serial.println(__LINE__);}
-#define DBG_HALT_IF(b) if (b) {Serial.print(F("DBG_HALT "));\
-                       Serial.print(F(__FILE__)); Serial.println(__LINE__);\
-                       while (true) {}}
-#define DBG_FAIL_MACRO dbgPrint(__LINE__);
 #else  // USE_DBG_MACROS
 #define DBG_FAIL_MACRO
-#define DBG_PRINT_IF(b)
+#define DBG_HALT_MACRO
 #define DBG_HALT_IF(b)
 #endif  // USE_DBG_MACROS
+
+#if USE_DBG_MACROS > 1
+__attribute__((unused)) static void dbgWarn(uint16_t line) {
+  Serial.print(F("DBG_WARN: "));
+  Serial.print(F(DBG_FILE));
+  Serial.write('.');
+  Serial.println(line);
+}
+#define DBG_WARN_MACRO dbgWarn(__LINE__)
+#define DBG_WARN_IF(b) if (b) {dbgWarn(__LINE__);}
+#else  // USE_DBG_MACROS > 1
+#define DBG_WARN_MACRO
+#define DBG_WARN_IF(b)
+#endif  // USE_DBG_MACROS > 1
 #endif  // DebugMacros_h

+ 1 - 1
src/common/FsApiConstants.h

@@ -24,7 +24,7 @@
  */
 #ifndef FsApiConstants_h
 #define FsApiConstants_h
-#include "../SdFatConfig.h"
+#include "SdFatConfig.h"
 
 #if USE_FCNTL_H
 #include <fcntl.h>

+ 30 - 8
src/FatLib/FatLibConfig.h → src/common/FsName.cpp

@@ -22,11 +22,33 @@
  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  * DEALINGS IN THE SOFTWARE.
  */
-/**
- * \file
- * \brief configuration definitions
- */
-#ifndef FatLibConfig_h
-#define FatLibConfig_h
-#include "SdFatConfig.h"
-#endif  // FatLibConfig_h
+#include "FsName.h"
+#include "FsUtf.h"
+#if USE_UTF8_LONG_NAMES
+uint16_t FsName::get16() {
+  uint16_t rtn;
+  if (ls) {
+    rtn = ls;
+    ls = 0;
+  } else if (next >= end) {
+    rtn = 0;
+  } else {
+    uint32_t cp;
+    const char* ptr = FsUtf::mbToCp(next, end, &cp);
+    if (!ptr) {
+      goto fail;
+    }
+    next = ptr;
+    if (cp <= 0XFFFF) {
+      rtn = cp;
+    } else {
+      ls = FsUtf::lowSurrogate(cp);
+      rtn = FsUtf::highSurrogate(cp);
+    }
+  }
+  return rtn;
+
+ fail:
+  return 0XFFFF;
+}
+#endif  // USE_UTF8_LONG_NAMES

+ 66 - 0
src/common/FsName.h

@@ -0,0 +1,66 @@
+/**
+ * Copyright (c) 2011-2020 Bill Greiman
+ * This file is part of the SdFat library for SD memory cards.
+ *
+ * MIT License
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+#ifndef FsName_h
+#define FsName_h
+#include "SdFatConfig.h"
+#include <stdint.h>
+/**
+ * \file
+ * \brief FsName class.
+ */
+/**
+ * \class FsName
+ * \brief Handle UTF-8 file names.
+ */
+class FsName {
+ public:
+  /** Beginning of LFN. */
+  const char* begin;
+  /** Next LFN character of end. */
+  const char* next;
+  /** Position one beyond last LFN character. */
+  const char* end;
+#if !USE_UTF8_LONG_NAMES
+  /** \return true if at end. */
+  bool atEnd() {return next == end;}
+  /** Reset to start of LFN. */
+  void reset() {next = begin;}
+  /** \return next char of LFN. */
+  char getch() {return atEnd() ? 0 : *next++;}
+  /** \return next UTF-16 unit of LFN. */
+  uint16_t get16() {return atEnd() ? 0 : *next++;}
+#else  // !USE_UTF8_LONG_NAMES
+  uint16_t ls = 0;
+  bool atEnd() {
+    return !ls && next == end;
+  }
+  void reset() {
+    next = begin;
+    ls = 0;
+  }
+  uint16_t get16();
+#endif  // !USE_UTF8_LONG_NAMES
+};
+#endif  // FsName_h

+ 13 - 5
src/common/FsStructs.h

@@ -91,7 +91,13 @@ inline void setLe64(uint8_t* dst, uint64_t src) {
   dst[7] = src >> 56;
 }
 #endif  // USE_SIMPLE_LITTLE_ENDIAN
-//-----------------------------------------------------------------------------
+//------------------------------------------------------------------------------
+// Reserved characters for exFAT names and FAT LFN.
+inline bool lfnReservedChar(uint8_t c) {
+  return c < 0X20 || c == '"' || c == '*' || c == '/' || c == ':'
+      || c == '<' || c == '>' || c == '?' || c == '\\'|| c == '|';
+}
+//------------------------------------------------------------------------------
 const uint16_t MBR_SIGNATURE = 0xAA55;
 const uint16_t PBR_SIGNATURE = 0xAA55;
 
@@ -103,13 +109,13 @@ typedef struct mbrPartition {
   uint8_t relativeSectors[4];
   uint8_t totalSectors[4];
 } MbrPart_t;
-//-----------------------------------------------------------------------------
+//------------------------------------------------------------------------------
 typedef struct masterBootRecordSector {
   uint8_t   bootCode[446];
   MbrPart_t part[4];
   uint8_t   signature[2];
 } MbrSector_t;
-//-----------------------------------------------------------------------------
+//------------------------------------------------------------------------------
 typedef struct partitionBootSector {
   uint8_t  jmpInstruction[3];
   char     oemName[8];
@@ -117,12 +123,12 @@ typedef struct partitionBootSector {
   uint8_t  bootCode[390];
   uint8_t  signature[2];
 } pbs_t;
-//-----------------------------------------------------------------------------
+//------------------------------------------------------------------------------
 typedef struct {
   uint8_t type;
   uint8_t data[31];
 } DirGeneric_t;
-//=============================================================================
+//==============================================================================
 typedef struct {
   uint64_t position;
   uint32_t cluster;
@@ -257,7 +263,9 @@ static inline bool isSubdir(const DirFat_t* dir) {
  * begin with an entry having this mask.
  */
 const uint8_t FAT_ORDER_LAST_LONG_ENTRY = 0X40;
+/** Max long file name length */
 
+const uint8_t FAT_MAX_LFN_LENGTH = 255;
 typedef struct {
   uint8_t  order;
   uint8_t  unicode1[10];

+ 115 - 0
src/common/FsUtf.cpp

@@ -0,0 +1,115 @@
+/**
+ * Copyright (c) 2011-2020 Bill Greiman
+ * This file is part of the SdFat library for SD memory cards.
+ *
+ * MIT License
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+#include "FsUtf.h"
+namespace FsUtf {
+  //----------------------------------------------------------------------------
+  char* cpToMb(uint32_t cp, char* str, char* end) {
+    size_t n = end - str;
+    if (cp < 0X80) {
+      if (n < 1) goto fail;
+      *(str++) = static_cast<uint8_t>(cp);
+    } else if (cp < 0X800) {
+      if (n < 2) goto fail;
+      *(str++) = static_cast<uint8_t>((cp >> 6)          | 0XC0);
+      *(str++) = static_cast<uint8_t>((cp & 0X3F)        | 0X80);
+    } else if (cp < 0X10000) {
+      if (n < 3) goto fail;
+      *(str++) = static_cast<uint8_t>((cp >> 12)         | 0XE0);
+      *(str++) = static_cast<uint8_t>(((cp >> 6) & 0X3F) | 0X80);
+      *(str++) = static_cast<uint8_t>((cp & 0X3F)        | 0X80);
+    } else {
+       if (n < 4) goto fail;
+      *(str++) = static_cast<uint8_t>((cp >> 18)         | 0XF0);
+      *(str++) = static_cast<uint8_t>(((cp >> 12) & 0X3F)| 0X80);
+      *(str++) = static_cast<uint8_t>(((cp >> 6) & 0X3F) | 0X80);
+      *(str++) = static_cast<uint8_t>((cp & 0X3F)        | 0X80);
+    }
+    return str;
+
+   fail:
+    return nullptr;
+  }
+  //----------------------------------------------------------------------------
+  // to do?  improve error check
+  const char* mbToCp(const char* str, const char* end, uint32_t* rtn) {
+    size_t n;
+    uint32_t cp;
+    if (str >= end) {
+      return nullptr;
+    }
+    uint8_t ch = str[0];
+    if ((ch & 0X80) == 0) {
+      *rtn = ch;
+      return str + 1;
+    }
+    if ((ch & 0XE0) == 0XC0) {
+      cp = ch & 0X1F;
+      n = 2;
+    } else if ((ch & 0XF0) == 0XE0) {
+      cp = ch & 0X0F;
+      n = 3;
+    } else if ((ch & 0XF8) == 0XF0) {
+      cp = ch & 0X07;
+      n = 4;
+    } else {
+      return nullptr;
+    }
+    if ((str + n) > end) {
+      return nullptr;
+    }
+    for (size_t i = 1; i < n; i++) {
+      ch = str[i];
+      if ((ch & 0XC0) != 0X80) {
+        return nullptr;
+      }
+      cp <<= 6;
+      cp |= ch & 0X3F;
+    }
+    // Don't allow over long as ASCII.
+    if (cp < 0X80 || !isValidCp(cp)) {
+      return nullptr;
+    }
+    *rtn = cp;
+    return str + n;
+  }
+  //----------------------------------------------------------------------------
+  const char* mbToU16(const char* str,
+                      const char* end, uint16_t* hs, uint16_t* ls) {
+    uint32_t cp;
+    const char* ptr = mbToCp(str, end, &cp);
+    if (!ptr) {
+      return nullptr;
+    }
+    if (cp <= 0XFFFF) {
+      *hs = cp;
+      *ls = 0;
+    } else {
+      *hs = highSurrogate(cp);
+      *ls = lowSurrogate(cp);
+    }
+    return ptr;
+  }
+}  // namespace FsUtf
+

Некоторые файлы не были показаны из-за большого количества измененных файлов