Skip to content

Commit 70ead47

Browse files
Enable LFN file name on emulated disk
It's almost 2023, allow LFN (long file names) on the emulated USB disk.
1 parent 03f4249 commit 70ead47

File tree

3 files changed

+73
-22
lines changed

3 files changed

+73
-22
lines changed

libraries/SingleFileDrive/examples/DataLoggerUSB/DataLoggerUSB.ino

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ void setup() {
5050
singleFileDrive.onDelete(deleteCSV);
5151
singleFileDrive.onPlug(plug);
5252
singleFileDrive.onUnplug(unplug);
53-
singleFileDrive.begin("data.csv", "DATA", "CSV");
53+
singleFileDrive.begin("data.csv", "Recorded data from the Raspberry Pi Pico.csv");
5454

5555
// Find the last written data
5656
File f = LittleFS.open("data.csv", "r");

libraries/SingleFileDrive/src/SingleFileDrive.cpp

Lines changed: 70 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -53,25 +53,22 @@ void SingleFileDrive::onUnplug(void (*cb)(uint32_t), uint32_t cbData) {
5353
_cbUnplugData = cbData;
5454
}
5555

56-
bool SingleFileDrive::begin(const char *localFile, const char *FILENAME, const char *EXT) {
56+
bool SingleFileDrive::begin(const char *localFile, const char *dosFile) {
5757
if (_started) {
5858
return false;
5959
}
6060
_localFile = strdup(localFile);
61-
_FILENAME = strdup(FILENAME);
62-
_EXT = strdup(EXT);
61+
_dosFile = strdup(dosFile);
6362
_started = true;
6463
return true;
6564
}
6665

6766
void SingleFileDrive::end() {
6867
_started = false;
6968
free(_localFile);
70-
free(_FILENAME);
71-
free(_EXT);
69+
free(_dosFile);
7270
_localFile = nullptr;
73-
_FILENAME = nullptr;
74-
_EXT = nullptr;
71+
_dosFile = nullptr;
7572
}
7673

7774
void SingleFileDrive::bootSector(char buff[512]) {
@@ -94,30 +91,85 @@ void SingleFileDrive::bootSector(char buff[512]) {
9491
buff[0x1ff] = 0xff;
9592
}
9693

94+
static char _toLegalFATChar(char c) {
95+
const char *odds = "!#$%&'()-@^_`{}~";
96+
c = toupper(c);
97+
if (((c >= '0') && (c <= '9')) || ((c >= 'A') && (c <= 'Z')) || strchr(odds, c)) {
98+
return c;
99+
} else {
100+
return '~';
101+
}
102+
}
103+
97104
void SingleFileDrive::directorySector(char buff[512]) {
98105
const uint8_t lbl[] = {
99106
0x50, 0x49, 0x43, 0x4f, 0x44, 0x49, 0x53, 0x4b, 0x20, 0x20, 0x20, 0x08, 0x00, 0x00, 0xac, 0x56,
100107
0x82, 0x55, 0x82, 0x55, 0x00, 0x00, 0xac, 0x56, 0x82, 0x55
101108
}; //, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
102109
memset(buff, 0, 512);
103110
memcpy(buff, lbl, sizeof(lbl));
104-
// space-pad
105-
memset(buff + 32, ' ', 8 + 3);
106-
for (int i = 0; i < 8 && _FILENAME[i]; i++) {
107-
buff[32 + i] = toupper(_FILENAME[i]);
111+
buff += 32; // Skip the just-set label
112+
113+
// Create a legal 11-char UPPERCASE FILENAME WITH 0x20 PAD
114+
char SFN[11];
115+
memset(SFN, ' ', 11);
116+
for (int i = 0; (i < 8) && _dosFile[i] && (_dosFile[i] != '.'); i++) {
117+
SFN[i] = _toLegalFATChar(_dosFile[i]);
118+
}
119+
char *dot = _dosFile + strlen(_dosFile) - 1;
120+
while ((dot >= _dosFile) && (*dot != '.')) {
121+
dot--;
122+
}
123+
if (*dot == '.') {
124+
dot++;
125+
for (int i = 0; (i < 3) && dot[i]; i++) {
126+
SFN[8+i] = _toLegalFATChar(dot[i]);
127+
}
128+
}
129+
uint8_t chksum = 0; // for LFN
130+
for (int i = 0; i < 11; i++) {
131+
chksum = (chksum >> 1) + (chksum << 7) + SFN[i];
132+
}
133+
134+
// Create LFN structure
135+
int entries = (strlen(_dosFile) + 12) / 13; // round up
136+
for (int i = 0; i < entries; i++) {
137+
*buff++ = (entries - i) | (i == 0 ? 0x40 : 0);
138+
const char *partname = _dosFile + 13 * (entries - i - 1);
139+
for (int j=0; j<13; j++) {
140+
uint16_t u;
141+
if (j > (int)strlen(partname)) {
142+
u = 0xffff;
143+
} else {
144+
u = partname[j] & 0xff;
145+
}
146+
*buff++ = u & 0xff;
147+
*buff++ = (u >> 8) & 0xff;
148+
if (j == 4) {
149+
*buff++ = 0x0f; // LFN ATTR
150+
*buff++ = 0;
151+
*buff++ = chksum;
152+
} else if (j == 10) {
153+
*buff++ = 0;
154+
*buff++ = 0;
155+
}
156+
}
108157
}
109-
for (int i = 0; i < 3 && _EXT[i]; i++) {
110-
buff[32 + 8 + i] = toupper(_EXT[i]);
158+
159+
// Create SFN
160+
memset(buff, 0, 32);
161+
for (int i = 0; i < 11; i++) {
162+
buff[i] = SFN[i];
111163
}
112-
buff[32 + 0x0b] = 0x20; // ATTR = Archive
164+
buff[0x0b] = 0x20; // ATTR = Archive
113165
// Ignore creation data/time, etc.
114-
buff[32 + 0x1a] = 0x03; // Starting cluster 3
166+
buff[0x1a] = 0x03; // Starting cluster 3
115167
File f = LittleFS.open(_localFile, "r");
116168
int size = f.size();
117169
f.close();
118-
buff[32 + 0x1c] = size & 255;
119-
buff[32 + 0x1d] = (size >> 8) & 255;
120-
buff[32 + 0x1e] = (size >> 16) & 255; // 16MB or smaller
170+
buff[0x1c] = size & 255;
171+
buff[0x1d] = (size >> 8) & 255;
172+
buff[0x1e] = (size >> 16) & 255; // 16MB or smaller
121173
}
122174

123175
void SingleFileDrive::fatSector(char fat[512]) {

libraries/SingleFileDrive/src/SingleFileDrive.h

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ class SingleFileDrive {
2626
SingleFileDrive();
2727
~SingleFileDrive();
2828

29-
bool begin(const char *localFile, const char *FILENAME, const char *EXT);
29+
bool begin(const char *localFile, const char *dosFile);
3030
void end();
3131

3232
void onDelete(void (*cb)(uint32_t), uint32_t cbData = 0);
@@ -48,8 +48,7 @@ class SingleFileDrive {
4848
private:
4949
bool _started = false;
5050
char *_localFile = nullptr;
51-
char *_FILENAME = nullptr;
52-
char *_EXT = nullptr;
51+
char *_dosFile = nullptr;
5352

5453
void (*_cbDelete)(uint32_t) = nullptr;
5554
uint32_t _cbDeleteData = 0;

0 commit comments

Comments
 (0)