|
@@ -7,10 +7,19 @@
|
|
|
|
|
|
#ifdef SDSUPPORT
|
|
|
|
|
|
+#define LONGEST_FILENAME (longFilename[0] ? longFilename : filename)
|
|
|
|
|
|
|
|
|
CardReader::CardReader()
|
|
|
{
|
|
|
+ #ifdef SDCARD_SORT_ALPHA
|
|
|
+ sort_count = 0;
|
|
|
+ #if SDSORT_GCODE
|
|
|
+ sort_alpha = true;
|
|
|
+ sort_folders = FOLDER_SORTING;
|
|
|
+
|
|
|
+ #endif
|
|
|
+ #endif
|
|
|
filesize = 0;
|
|
|
sdpos = 0;
|
|
|
sdprinting = false;
|
|
@@ -50,94 +59,98 @@ char *createFilename(char *buffer,const dir_t &p)
|
|
|
}
|
|
|
|
|
|
|
|
|
-void CardReader::lsDive(const char *prepend, SdFile parent, const char * const match)
|
|
|
-{
|
|
|
- dir_t p;
|
|
|
- uint8_t cnt=0;
|
|
|
-
|
|
|
- while (parent.readDir(p, longFilename) > 0)
|
|
|
- {
|
|
|
- if( DIR_IS_SUBDIR(&p) && lsAction!=LS_Count && lsAction!=LS_GetFilename)
|
|
|
- {
|
|
|
-
|
|
|
- char path[13*2];
|
|
|
- char lfilename[13];
|
|
|
- createFilename(lfilename,p);
|
|
|
-
|
|
|
- path[0]=0;
|
|
|
- if(strlen(prepend)==0)
|
|
|
- {
|
|
|
- strcat(path,"/");
|
|
|
- }
|
|
|
- strcat(path,prepend);
|
|
|
- strcat(path,lfilename);
|
|
|
- strcat(path,"/");
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
- SdFile dir;
|
|
|
- if(!dir.open(parent,lfilename, O_READ))
|
|
|
- {
|
|
|
- if(lsAction==LS_SerialPrint)
|
|
|
- {
|
|
|
- SERIAL_ECHO_START;
|
|
|
- SERIAL_ECHORPGM(MSG_SD_CANT_ENTER_SUBDIR);
|
|
|
- SERIAL_ECHOLN(lfilename);
|
|
|
- }
|
|
|
- }
|
|
|
- lsDive(path,dir);
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- char pn0 = p.name[0];
|
|
|
- if (pn0 == DIR_NAME_FREE) break;
|
|
|
- if (pn0 == DIR_NAME_DELETED || pn0 == '.' || pn0 == '_') continue;
|
|
|
- char lf0 = longFilename[0];
|
|
|
- if (lf0 == '.' || lf0 == '_') continue;
|
|
|
-
|
|
|
- if (!DIR_IS_FILE_OR_SUBDIR(&p)) continue;
|
|
|
-
|
|
|
- if ((p.attributes & (DIR_ATT_HIDDEN | DIR_ATT_SYSTEM)) != 0) continue;
|
|
|
- filenameIsDir=DIR_IS_SUBDIR(&p);
|
|
|
-
|
|
|
-
|
|
|
- if(!filenameIsDir)
|
|
|
- {
|
|
|
- if(p.name[8]!='G') continue;
|
|
|
- if(p.name[9]=='~') continue;
|
|
|
- }
|
|
|
-
|
|
|
- createFilename(filename,p);
|
|
|
- if(lsAction==LS_SerialPrint)
|
|
|
- {
|
|
|
- SERIAL_PROTOCOL(prepend);
|
|
|
- SERIAL_PROTOCOLLN(filename);
|
|
|
- }
|
|
|
- else if(lsAction==LS_Count)
|
|
|
- {
|
|
|
- nrFiles++;
|
|
|
- }
|
|
|
- else if(lsAction==LS_GetFilename)
|
|
|
- {
|
|
|
- if (match != NULL) {
|
|
|
- if (strcasecmp(match, filename) == 0) return;
|
|
|
- }
|
|
|
- else if (cnt == nrFiles) return;
|
|
|
- cnt++;
|
|
|
-
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
+
|
|
|
+* Dive into a folder and recurse depth-first to perform a pre-set operation lsAction:
|
|
|
+* LS_Count - Add +1 to nrFiles for every file within the parent
|
|
|
+* LS_GetFilename - Get the filename of the file indexed by nrFiles
|
|
|
+* LS_SerialPrint - Print the full path and size of each file to serial output
|
|
|
+*/
|
|
|
+
|
|
|
+void CardReader::lsDive(const char *prepend, SdFile parent, const char * const match) {
|
|
|
+ dir_t p;
|
|
|
+ uint8_t cnt = 0;
|
|
|
+
|
|
|
+
|
|
|
+ while (parent.readDir(p, longFilename) > 0) {
|
|
|
+
|
|
|
+
|
|
|
+ if (DIR_IS_SUBDIR(&p) && lsAction != LS_Count && lsAction != LS_GetFilename) {
|
|
|
+
|
|
|
+
|
|
|
+ char lfilename[FILENAME_LENGTH];
|
|
|
+ createFilename(lfilename, p);
|
|
|
+
|
|
|
+
|
|
|
+ bool prepend_is_empty = (prepend[0] == '\0');
|
|
|
+ int len = (prepend_is_empty ? 1 : strlen(prepend)) + strlen(lfilename) + 1 + 1;
|
|
|
+ char path[len];
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ strcpy(path, prepend_is_empty ? "/" : prepend);
|
|
|
+ strcat(path, lfilename);
|
|
|
+ strcat(path, "/");
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ SdFile dir;
|
|
|
+ if (!dir.open(parent, lfilename, O_READ)) {
|
|
|
+ if (lsAction == LS_SerialPrint) {
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ }
|
|
|
+ }
|
|
|
+ lsDive(path, dir);
|
|
|
+
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ uint8_t pn0 = p.name[0];
|
|
|
+ if (pn0 == DIR_NAME_FREE) break;
|
|
|
+ if (pn0 == DIR_NAME_DELETED || pn0 == '.') continue;
|
|
|
+ if (longFilename[0] == '.') continue;
|
|
|
+
|
|
|
+ if (!DIR_IS_FILE_OR_SUBDIR(&p) || (p.attributes & DIR_ATT_HIDDEN)) continue;
|
|
|
+
|
|
|
+ filenameIsDir = DIR_IS_SUBDIR(&p);
|
|
|
+
|
|
|
+ if (!filenameIsDir && (p.name[8] != 'G' || p.name[9] == '~')) continue;
|
|
|
+
|
|
|
+ switch (lsAction) {
|
|
|
+ case LS_Count:
|
|
|
+ nrFiles++;
|
|
|
+ break;
|
|
|
+
|
|
|
+ case LS_SerialPrint:
|
|
|
+ createFilename(filename, p);
|
|
|
+ SERIAL_PROTOCOL(prepend);
|
|
|
+ SERIAL_PROTOCOL(filename);
|
|
|
+
|
|
|
+ SERIAL_PROTOCOLLN(p.fileSize);
|
|
|
+ break;
|
|
|
+
|
|
|
+ case LS_GetFilename:
|
|
|
+ createFilename(filename, p);
|
|
|
+ if (match != NULL) {
|
|
|
+ if (strcasecmp(match, filename) == 0) return;
|
|
|
+ }
|
|
|
+ else if (cnt == nrFiles) return;
|
|
|
+ cnt++;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
void CardReader::ls()
|
|
|
{
|
|
|
lsAction=LS_SerialPrint;
|
|
|
- if(lsAction==LS_Count)
|
|
|
- nrFiles=0;
|
|
|
+
|
|
|
+
|
|
|
|
|
|
root.rewind();
|
|
|
lsDive("",root);
|
|
@@ -185,6 +198,9 @@ void CardReader::initsd()
|
|
|
}
|
|
|
workDir=root;
|
|
|
curDir=&root;
|
|
|
+#ifdef SDCARD_SORT_ALPHA
|
|
|
+ presort();
|
|
|
+#endif
|
|
|
|
|
|
if(!workDir.openRoot(&volume))
|
|
|
{
|
|
@@ -203,6 +219,9 @@ void CardReader::setroot()
|
|
|
workDir=root;
|
|
|
|
|
|
curDir=&workDir;
|
|
|
+ #ifdef SDCARD_SORT_ALPHA
|
|
|
+ presort();
|
|
|
+ #endif
|
|
|
}
|
|
|
void CardReader::release()
|
|
|
{
|
|
@@ -215,6 +234,9 @@ void CardReader::startFileprint()
|
|
|
if(cardOK)
|
|
|
{
|
|
|
sdprinting = true;
|
|
|
+ #ifdef SDCARD_SORT_ALPHA
|
|
|
+ flush_presort();
|
|
|
+ #endif
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -449,6 +471,9 @@ void CardReader::removeFile(char* name)
|
|
|
SERIAL_PROTOCOLPGM("File deleted:");
|
|
|
SERIAL_PROTOCOLLN(fname);
|
|
|
sdpos = 0;
|
|
|
+ #ifdef SDCARD_SORT_ALPHA
|
|
|
+ presort();
|
|
|
+ #endif
|
|
|
}
|
|
|
else
|
|
|
{
|
|
@@ -641,6 +666,241 @@ void CardReader::updir()
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+#ifdef SDCARD_SORT_ALPHA
|
|
|
+
|
|
|
+
|
|
|
+* Get the name of a file in the current directory by sort-index
|
|
|
+*/
|
|
|
+void CardReader::getfilename_sorted(const uint16_t nr) {
|
|
|
+ getfilename(
|
|
|
+ #if SDSORT_GCODE
|
|
|
+ sort_alpha &&
|
|
|
+ #endif
|
|
|
+ (nr < sort_count) ? sort_order[nr] : nr
|
|
|
+ );
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+* Read all the files and produce a sort key
|
|
|
+*
|
|
|
+* We can do this in 3 ways...
|
|
|
+* - Minimal RAM: Read two filenames at a time sorting along...
|
|
|
+* - Some RAM: Buffer the directory just for this sort
|
|
|
+* - Most RAM: Buffer the directory and return filenames from RAM
|
|
|
+*/
|
|
|
+void CardReader::presort() {
|
|
|
+
|
|
|
+ #if SDSORT_GCODE
|
|
|
+ if (!sort_alpha) return;
|
|
|
+ #endif
|
|
|
+
|
|
|
+
|
|
|
+ flush_presort();
|
|
|
+
|
|
|
+
|
|
|
+ uint16_t fileCnt = getnrfilenames();
|
|
|
+ if (fileCnt > 0) {
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ if (fileCnt > SDSORT_LIMIT) fileCnt = SDSORT_LIMIT;
|
|
|
+
|
|
|
+
|
|
|
+ #if SDSORT_DYNAMIC_RAM
|
|
|
+ sort_order = new uint8_t[fileCnt];
|
|
|
+ #endif
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ #if SDSORT_USES_RAM
|
|
|
+
|
|
|
+
|
|
|
+ #if SDSORT_CACHE_NAMES
|
|
|
+ #if SDSORT_DYNAMIC_RAM
|
|
|
+ sortshort = new char*[fileCnt];
|
|
|
+ sortnames = new char*[fileCnt];
|
|
|
+ #endif
|
|
|
+ #elif SDSORT_USES_STACK
|
|
|
+ char sortnames[fileCnt][LONG_FILENAME_LENGTH];
|
|
|
+ #endif
|
|
|
+
|
|
|
+
|
|
|
+ #if HAS_FOLDER_SORTING
|
|
|
+ #if SDSORT_DYNAMIC_RAM
|
|
|
+ isDir = new uint8_t[(fileCnt + 7) >> 3];
|
|
|
+ #elif SDSORT_USES_STACK
|
|
|
+ uint8_t isDir[(fileCnt + 7) >> 3];
|
|
|
+ #endif
|
|
|
+ #endif
|
|
|
+
|
|
|
+ #else
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ char name1[LONG_FILENAME_LENGTH + 1];
|
|
|
+
|
|
|
+ #endif
|
|
|
+
|
|
|
+ if (fileCnt > 1) {
|
|
|
+ SERIAL_ECHOLNPGM("Sort order:");
|
|
|
+
|
|
|
+ for (uint16_t i = 0; i < fileCnt; i++) {
|
|
|
+ sort_order[i] = i;
|
|
|
+
|
|
|
+ #if SDSORT_USES_RAM
|
|
|
+ getfilename(i);
|
|
|
+ #if SDSORT_DYNAMIC_RAM
|
|
|
+
|
|
|
+ sortnames[i] = strdup(LONGEST_FILENAME);
|
|
|
+ #if SDSORT_CACHE_NAMES
|
|
|
+
|
|
|
+
|
|
|
+ sortshort[i] = strdup(filename);
|
|
|
+ #endif
|
|
|
+ #else
|
|
|
+
|
|
|
+ strcpy(sortnames[i], LONGEST_FILENAME);
|
|
|
+ #if SDSORT_CACHE_NAMES
|
|
|
+ strcpy(sortshort[i], filename);
|
|
|
+ #endif
|
|
|
+ #endif
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ #if HAS_FOLDER_SORTING
|
|
|
+ const uint16_t bit = i & 0x07, ind = i >> 3;
|
|
|
+ if (bit == 0) isDir[ind] = 0x00;
|
|
|
+ if (filenameIsDir) isDir[ind] |= _BV(bit);
|
|
|
+ #endif
|
|
|
+ #endif
|
|
|
+ }
|
|
|
+
|
|
|
+ SERIAL_ECHOPGM("FILE_CNT:");
|
|
|
+ MYSERIAL.println(int(fileCnt));
|
|
|
+
|
|
|
+
|
|
|
+ for (uint16_t i = fileCnt; --i;) {
|
|
|
+ bool didSwap = false;
|
|
|
+ MYSERIAL.println(int(i));
|
|
|
+ for (uint16_t j = 0; j < i; ++j) {
|
|
|
+ const uint16_t o1 = sort_order[j], o2 = sort_order[j + 1];
|
|
|
+
|
|
|
+
|
|
|
+ #if SDSORT_USES_RAM
|
|
|
+ #define _SORT_CMP_NODIR() (strcasecmp(sortnames[o1], sortnames[o2]) > 0)
|
|
|
+ #else
|
|
|
+ #define _SORT_CMP_NODIR() (strcasecmp(name1, name2) > 0)
|
|
|
+ #endif
|
|
|
+
|
|
|
+ #if HAS_FOLDER_SORTING
|
|
|
+ #if SDSORT_USES_RAM
|
|
|
+
|
|
|
+ const uint8_t ind1 = o1 >> 3, bit1 = o1 & 0x07,
|
|
|
+ ind2 = o2 >> 3, bit2 = o2 & 0x07;
|
|
|
+ #define _SORT_CMP_DIR(fs) \
|
|
|
+ (((isDir[ind1] & _BV(bit1)) != 0) == ((isDir[ind2] & _BV(bit2)) != 0) \
|
|
|
+ ? _SORT_CMP_NODIR() \
|
|
|
+ : (isDir[fs > 0 ? ind1 : ind2] & (fs > 0 ? _BV(bit1) : _BV(bit2))) != 0)
|
|
|
+ #else
|
|
|
+ #define _SORT_CMP_DIR(fs) ((dir1 == filenameIsDir) ? _SORT_CMP_NODIR() : (fs > 0 ? dir1 : !dir1))
|
|
|
+ #endif
|
|
|
+ #endif
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ #if !SDSORT_USES_RAM
|
|
|
+ SERIAL_ECHOPGM("o1 & o2: ");
|
|
|
+ MYSERIAL.println(int(o1));
|
|
|
+ MYSERIAL.println(int(o2));
|
|
|
+
|
|
|
+ getfilename(o1);
|
|
|
+ strcpy(name1, LONGEST_FILENAME);
|
|
|
+ #if HAS_FOLDER_SORTING
|
|
|
+ bool dir1 = filenameIsDir;
|
|
|
+ #endif
|
|
|
+ getfilename(o2);
|
|
|
+ char *name2 = LONGEST_FILENAME;
|
|
|
+
|
|
|
+ SERIAL_ECHOLNPGM("Names:");
|
|
|
+ MYSERIAL.println(name1);
|
|
|
+ MYSERIAL.println(name2);
|
|
|
+ #endif
|
|
|
+
|
|
|
+
|
|
|
+ if (
|
|
|
+ #if HAS_FOLDER_SORTING
|
|
|
+ #if SDSORT_GCODE
|
|
|
+ sort_folders ? _SORT_CMP_DIR(sort_folders) : _SORT_CMP_NODIR()
|
|
|
+ #else
|
|
|
+ _SORT_CMP_DIR(FOLDER_SORTING)
|
|
|
+ #endif
|
|
|
+ #else
|
|
|
+ _SORT_CMP_NODIR()
|
|
|
+ #endif
|
|
|
+ ) {
|
|
|
+ sort_order[j] = o2;
|
|
|
+ sort_order[j + 1] = o1;
|
|
|
+ didSwap = true;
|
|
|
+ SERIAL_ECHOLNPGM("Did swap");
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (!didSwap) break;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ #if (SDSORT_USES_RAM && !SDSORT_CACHE_NAMES)
|
|
|
+ #if SDSORT_DYNAMIC_RAM
|
|
|
+ for (uint16_t i = 0; i < fileCnt; ++i) free(sortnames[i]);
|
|
|
+ #if HAS_FOLDER_SORTING
|
|
|
+ free(isDir);
|
|
|
+ #endif
|
|
|
+ #endif
|
|
|
+ #endif
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ sort_order[0] = 0;
|
|
|
+ #if (SDSORT_USES_RAM && SDSORT_CACHE_NAMES)
|
|
|
+ getfilename(0);
|
|
|
+ #if SDSORT_DYNAMIC_RAM
|
|
|
+ sortnames = new char*[1];
|
|
|
+ sortnames[0] = strdup(LONGEST_FILENAME);
|
|
|
+ sortshort = new char*[1];
|
|
|
+ sortshort[0] = strdup(filename);
|
|
|
+ isDir = new uint8_t[1];
|
|
|
+ #else
|
|
|
+ strcpy(sortnames[0], LONGEST_FILENAME);
|
|
|
+ strcpy(sortshort[0], filename);
|
|
|
+ #endif
|
|
|
+ isDir[0] = filenameIsDir ? 0x01 : 0x00;
|
|
|
+ #endif
|
|
|
+ }
|
|
|
+
|
|
|
+ sort_count = fileCnt;
|
|
|
+ }
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+void CardReader::flush_presort() {
|
|
|
+ if (sort_count > 0) {
|
|
|
+ #if SDSORT_DYNAMIC_RAM
|
|
|
+ delete sort_order;
|
|
|
+ #if SDSORT_CACHE_NAMES
|
|
|
+ for (uint8_t i = 0; i < sort_count; ++i) {
|
|
|
+ free(sortshort[i]);
|
|
|
+ free(sortnames[i]);
|
|
|
+ }
|
|
|
+ delete sortshort;
|
|
|
+ delete sortnames;
|
|
|
+ #endif
|
|
|
+ #endif
|
|
|
+ sort_count = 0;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+#endif
|
|
|
+
|
|
|
+
|
|
|
|
|
|
void CardReader::printingHasFinished()
|
|
|
{
|
|
@@ -664,6 +924,9 @@ void CardReader::printingHasFinished()
|
|
|
enquecommand_P(PSTR(SD_FINISHED_RELEASECOMMAND));
|
|
|
}
|
|
|
autotempShutdown();
|
|
|
+ #ifdef SDCARD_SORT_ALPHA
|
|
|
+ presort();
|
|
|
+ #endif
|
|
|
}
|
|
|
}
|
|
|
|