SD Card Alpha Sorting
First iteration of alphabetical sorting for SD cards, both slow+efficient and fast+rammy. Option for folders to sort first, last, or not at all.
This commit is contained in:
parent
90eb266139
commit
14187dae6c
@ -290,6 +290,7 @@
|
||||
#define SD_FINISHED_STEPPERRELEASE true //if sd support and the file is finished: disable steppers?
|
||||
#define SD_FINISHED_RELEASECOMMAND "M84 X Y Z E" // You might want to keep the z enabled so your bed stays in place.
|
||||
|
||||
#define SDCARD_SORT_ALPHA // Sort in ASCII order by pre-reading the folder and making a lookup table!
|
||||
#define SDCARD_RATHERRECENTFIRST //reverse file order of sd card menu display. Its sorted practically after the file system block order.
|
||||
// if a file is deleted, it frees a block. hence, the order is not purely chronological. To still have auto0.g accessible, there is again the option to do that.
|
||||
// using:
|
||||
|
@ -111,10 +111,12 @@ uint8_t const SOFT_SPI_SCK_PIN = 13;
|
||||
/**
|
||||
* Defines for long (vfat) filenames
|
||||
*/
|
||||
/** Number of UTF-16 characters per entry */
|
||||
#define FILENAME_LENGTH 13
|
||||
/** Number of VFAT entries used. Every entry has 13 UTF-16 characters */
|
||||
#define MAX_VFAT_ENTRIES (2)
|
||||
/** Total size of the buffer used to store the long filenames */
|
||||
#define LONG_FILENAME_LENGTH (13*MAX_VFAT_ENTRIES+1)
|
||||
#define LONG_FILENAME_LENGTH (FILENAME_LENGTH*MAX_VFAT_ENTRIES+1)
|
||||
#endif // SdFatConfig_h
|
||||
|
||||
|
||||
|
@ -11,6 +11,10 @@
|
||||
|
||||
CardReader::CardReader()
|
||||
{
|
||||
#if SORT_USES_MORE_RAM
|
||||
sortnames = NULL;
|
||||
sort_count = 0;
|
||||
#endif
|
||||
filesize = 0;
|
||||
sdpos = 0;
|
||||
sdprinting = false;
|
||||
@ -55,13 +59,13 @@ void CardReader::lsDive(const char *prepend,SdFile parent)
|
||||
dir_t p;
|
||||
uint8_t cnt=0;
|
||||
|
||||
while (parent.readDir(p, longFilename) > 0)
|
||||
while (parent.readDir(p, diveFilename) > 0)
|
||||
{
|
||||
if( DIR_IS_SUBDIR(&p) && lsAction!=LS_Count && lsAction!=LS_GetFilename) // hence LS_SerialPrint
|
||||
{
|
||||
|
||||
char path[13*2];
|
||||
char lfilename[13];
|
||||
char path[FILENAME_LENGTH*2];
|
||||
char lfilename[FILENAME_LENGTH];
|
||||
createFilename(lfilename,p);
|
||||
|
||||
path[0]=0;
|
||||
@ -87,15 +91,13 @@ void CardReader::lsDive(const char *prepend,SdFile parent)
|
||||
}
|
||||
lsDive(path,dir);
|
||||
//close done automatically by destructor of SdFile
|
||||
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
if (p.name[0] == DIR_NAME_FREE) break;
|
||||
if (p.name[0] == DIR_NAME_DELETED || p.name[0] == '.'|| p.name[0] == '_') continue;
|
||||
if (longFilename[0] != '\0' &&
|
||||
(longFilename[0] == '.' || longFilename[0] == '_')) continue;
|
||||
if (diveFilename[0] != '\0' &&
|
||||
(diveFilename[0] == '.' || diveFilename[0] == '_')) continue;
|
||||
if ( p.name[0] == '.')
|
||||
{
|
||||
if ( p.name[1] != '.')
|
||||
@ -105,7 +107,6 @@ void CardReader::lsDive(const char *prepend,SdFile parent)
|
||||
if (!DIR_IS_FILE_OR_SUBDIR(&p)) continue;
|
||||
filenameIsDir=DIR_IS_SUBDIR(&p);
|
||||
|
||||
|
||||
if(!filenameIsDir)
|
||||
{
|
||||
if(p.name[8]!='G') continue;
|
||||
@ -124,10 +125,8 @@ void CardReader::lsDive(const char *prepend,SdFile parent)
|
||||
}
|
||||
else if(lsAction==LS_GetFilename)
|
||||
{
|
||||
if(cnt==nrFiles)
|
||||
return;
|
||||
if (cnt == nrFiles) return;
|
||||
cnt++;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -136,9 +135,6 @@ void CardReader::lsDive(const char *prepend,SdFile parent)
|
||||
void CardReader::ls()
|
||||
{
|
||||
lsAction=LS_SerialPrint;
|
||||
if(lsAction==LS_Count)
|
||||
nrFiles=0;
|
||||
|
||||
root.rewind();
|
||||
lsDive("",root);
|
||||
}
|
||||
@ -177,6 +173,9 @@ void CardReader::initsd()
|
||||
}
|
||||
workDir=root;
|
||||
curDir=&root;
|
||||
#ifdef SDCARD_SORT_ALPHA
|
||||
presort();
|
||||
#endif
|
||||
/*
|
||||
if(!workDir.openRoot(&volume))
|
||||
{
|
||||
@ -193,8 +192,10 @@ void CardReader::setroot()
|
||||
SERIAL_ECHOLNPGM(MSG_SD_WORKDIR_FAIL);
|
||||
}*/
|
||||
workDir=root;
|
||||
|
||||
curDir=&workDir;
|
||||
#ifdef SDCARD_SORT_ALPHA
|
||||
presort();
|
||||
#endif
|
||||
}
|
||||
void CardReader::release()
|
||||
{
|
||||
@ -235,7 +236,7 @@ void CardReader::getAbsFilename(char *t)
|
||||
while(*t!=0 && cnt< MAXPATHNAMELENGTH)
|
||||
{t++;cnt++;} //crawl counter forward.
|
||||
}
|
||||
if(cnt<MAXPATHNAMELENGTH-13)
|
||||
if(cnt<MAXPATHNAMELENGTH-FILENAME_LENGTH)
|
||||
file.getFilename(t);
|
||||
else
|
||||
t[0]=0;
|
||||
@ -305,7 +306,7 @@ void CardReader::openFile(char* name,bool read, bool replace_current/*=true*/)
|
||||
//SERIAL_ECHO("end :");SERIAL_ECHOLN((int)(dirname_end-name));
|
||||
if(dirname_end>0 && dirname_end>dirname_start)
|
||||
{
|
||||
char subdirname[13];
|
||||
char subdirname[FILENAME_LENGTH];
|
||||
strncpy(subdirname, dirname_start, dirname_end-dirname_start);
|
||||
subdirname[dirname_end-dirname_start]=0;
|
||||
SERIAL_ECHOLN(subdirname);
|
||||
@ -401,7 +402,7 @@ void CardReader::removeFile(char* name)
|
||||
//SERIAL_ECHO("end :");SERIAL_ECHOLN((int)(dirname_end-name));
|
||||
if(dirname_end>0 && dirname_end>dirname_start)
|
||||
{
|
||||
char subdirname[13];
|
||||
char subdirname[FILENAME_LENGTH];
|
||||
strncpy(subdirname, dirname_start, dirname_end-dirname_start);
|
||||
subdirname[dirname_end-dirname_start]=0;
|
||||
SERIAL_ECHOLN(subdirname);
|
||||
@ -439,6 +440,9 @@ void CardReader::removeFile(char* name)
|
||||
SERIAL_PROTOCOLPGM("File deleted:");
|
||||
SERIAL_PROTOCOLLN(fname);
|
||||
sdpos = 0;
|
||||
#ifdef SDCARD_SORT_ALPHA
|
||||
presort();
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -595,21 +599,156 @@ void CardReader::chdir(const char * relpath)
|
||||
workDirParents[0]=*parent;
|
||||
}
|
||||
workDir=newfile;
|
||||
#ifdef SDCARD_SORT_ALPHA
|
||||
presort();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void CardReader::updir()
|
||||
{
|
||||
if(workDirDepth > 0)
|
||||
if (workDirDepth > 0)
|
||||
{
|
||||
--workDirDepth;
|
||||
workDir = workDirParents[0];
|
||||
int d;
|
||||
for (int d = 0; d < workDirDepth; d++)
|
||||
workDirParents[d] = workDirParents[d+1];
|
||||
#ifdef SDCARD_SORT_ALPHA
|
||||
presort();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef SDCARD_SORT_ALPHA
|
||||
|
||||
/**
|
||||
* Get the name of a file in the current directory by sort-index
|
||||
*/
|
||||
void CardReader::getfilename_sorted(const uint8_t nr) {
|
||||
#if SORT_USES_MORE_RAM
|
||||
getfilename(nr < sort_count ? sort_order[nr] : nr);
|
||||
#else
|
||||
getfilename(nr < SORT_LIMIT ? sort_order[nr] : nr);
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 and return filenames from RAM
|
||||
* - Some RAM: Buffer the directory just for this sort
|
||||
*/
|
||||
void CardReader::presort()
|
||||
{
|
||||
#if SORT_USES_MORE_RAM
|
||||
flush_presort();
|
||||
#endif
|
||||
|
||||
uint16_t fileCnt = getnrfilenames();
|
||||
if (fileCnt > 0) {
|
||||
|
||||
if (fileCnt > SORT_LIMIT) fileCnt = SORT_LIMIT;
|
||||
|
||||
#if SORT_USES_MORE_RAM
|
||||
sortnames = malloc(fileCnt * sizeof(char*));
|
||||
sort_count = fileCnt;
|
||||
#elif SORT_USES_RAM
|
||||
char *sortnames[fileCnt];
|
||||
#if FOLDER_SORTING != 0
|
||||
uint8_t isdir[fileCnt];
|
||||
#endif
|
||||
#else
|
||||
char sortname[LONG_FILENAME_LENGTH+1];
|
||||
#endif
|
||||
|
||||
if (fileCnt > 1) {
|
||||
|
||||
// Init sort order [and get filenames]
|
||||
for (int i=0; i<fileCnt; i++) {
|
||||
int byte=i/8, bit=1<<(i%8);
|
||||
sort_order[i] = i;
|
||||
#if SORT_USES_RAM
|
||||
getfilename(i);
|
||||
char *name = diveFilename[0] ? diveFilename : filename;
|
||||
// SERIAL_ECHOPGM("--- ");
|
||||
// SERIAL_ECHOLN(name);
|
||||
sortnames[i] = (char*)malloc(strlen(name) + 1);
|
||||
strcpy(sortnames[i], name);
|
||||
#if FOLDER_SORTING != 0
|
||||
isdir[i] = filenameIsDir;
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
// Bubble Sort
|
||||
for (uint8_t i=fileCnt; --i;) {
|
||||
bool cmp, didSwap = false;
|
||||
for (uint8_t j=0; j<i; ++j) {
|
||||
int s1 = j, s2 = j+1, o1 = sort_order[s1], o2 = sort_order[s2];
|
||||
#if SORT_USES_RAM
|
||||
#if FOLDER_SORTING != 0
|
||||
cmp = (isdir[o1] == isdir[o2]) ? (strcasecmp(sortnames[o1], sortnames[o2]) > 0) : isdir[FOLDER_SORTING > 0 ? o1 : o2];
|
||||
#else
|
||||
cmp = strcasecmp(sortnames[o1], sortnames[o2]) > 0);
|
||||
#endif
|
||||
#else
|
||||
getfilename(o1);
|
||||
#if FOLDER_SORTING != 0
|
||||
bool dir1 = filenameIsDir;
|
||||
#endif
|
||||
char *name = diveFilename[0] ? diveFilename : filename;
|
||||
strcpy(sortname, name);
|
||||
getfilename(o2);
|
||||
name = diveFilename[0] ? diveFilename : filename;
|
||||
#if FOLDER_SORTING != 0
|
||||
cmp = (dir1 == filenameIsDir) ? (strcasecmp(sortname, name) > 0) : (FOLDER_SORTING > 0 ? dir1 : !dir1);
|
||||
#else
|
||||
cmp = strcasecmp(sortname, name) > 0);
|
||||
#endif
|
||||
#endif
|
||||
if (cmp) {
|
||||
// SERIAL_ECHOPGM("Swap ");
|
||||
// SERIAL_ECHOLN(sortnames[o1]);
|
||||
// SERIAL_ECHOPGM(" for ");
|
||||
// SERIAL_ECHOLN(sortnames[o2]);
|
||||
sort_order[s1] = o2;
|
||||
sort_order[s2] = o1;
|
||||
didSwap = true;
|
||||
}
|
||||
}
|
||||
if (!didSwap) break;
|
||||
}
|
||||
|
||||
#if SORT_USES_RAM && !SORT_USES_MORE_RAM
|
||||
for (int i=0; i < fileCnt; ++i) free(sortnames[i]);
|
||||
#endif
|
||||
}
|
||||
else {
|
||||
sort_order[0] = 0;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void CardReader::flush_presort() {
|
||||
#if SORT_USES_MORE_RAM
|
||||
if (sort_count > 0) {
|
||||
for (int i=0; i < sort_count; ++i) {
|
||||
free(sortnames[i]);
|
||||
sort_order[i] = i;
|
||||
}
|
||||
free(sortnames);
|
||||
sortnames = NULL;
|
||||
sort_count = 0;
|
||||
}
|
||||
#else
|
||||
for (int i=SORT_LIMIT; --i;) sort_order[i] = i;
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
void CardReader::printingHasFinished()
|
||||
{
|
||||
|
@ -3,7 +3,11 @@
|
||||
|
||||
#ifdef SDSUPPORT
|
||||
|
||||
#define MAX_DIR_DEPTH 10
|
||||
#define MAX_DIR_DEPTH 10 // Maximum folder depth
|
||||
#define SORT_USES_RAM false // Buffer while sorting, else re-read from SD
|
||||
#define SORT_USES_MORE_RAM false // Always keep the directory in RAM
|
||||
#define SORT_LIMIT 256 // Maximum number of sorted items
|
||||
#define FOLDER_SORTING -1 // -1=above 0=none 1=below
|
||||
|
||||
#include "SdFile.h"
|
||||
enum LsAction {LS_SerialPrint,LS_Count,LS_GetFilename};
|
||||
@ -39,6 +43,12 @@ public:
|
||||
void updir();
|
||||
void setroot();
|
||||
|
||||
#ifdef SDCARD_SORT_ALPHA
|
||||
void presort();
|
||||
void flush_presort();
|
||||
void getfilename_sorted(const uint8_t nr);
|
||||
#endif
|
||||
|
||||
|
||||
FORCE_INLINE bool isFileOpen() { return file.isOpen(); }
|
||||
FORCE_INLINE bool eof() { return sdpos>=filesize ;};
|
||||
@ -51,19 +61,27 @@ public:
|
||||
bool saving;
|
||||
bool logging;
|
||||
bool sdprinting ;
|
||||
bool cardOK ;
|
||||
char filename[13];
|
||||
char longFilename[LONG_FILENAME_LENGTH];
|
||||
bool cardOK;
|
||||
char filename[FILENAME_LENGTH];
|
||||
char diveFilename[LONG_FILENAME_LENGTH];
|
||||
bool filenameIsDir;
|
||||
int lastnr; //last number of the autostart;
|
||||
private:
|
||||
SdFile root,*curDir,workDir,workDirParents[MAX_DIR_DEPTH];
|
||||
uint16_t workDirDepth;
|
||||
#ifdef SDCARD_SORT_ALPHA
|
||||
#if SORT_USES_MORE_RAM
|
||||
uint16_t sort_count;
|
||||
char **sortnames;
|
||||
#else
|
||||
uint8_t sort_order[SORT_LIMIT];
|
||||
#endif
|
||||
#endif
|
||||
Sd2Card card;
|
||||
SdVolume volume;
|
||||
SdFile file;
|
||||
#define SD_PROCEDURE_DEPTH 1
|
||||
#define MAXPATHNAMELENGTH (13*MAX_DIR_DEPTH+MAX_DIR_DEPTH+1)
|
||||
#define MAXPATHNAMELENGTH (FILENAME_LENGTH*MAX_DIR_DEPTH+MAX_DIR_DEPTH+1)
|
||||
uint8_t file_subcall_ctr;
|
||||
uint32_t filespos[SD_PROCEDURE_DEPTH];
|
||||
char filenames[SD_PROCEDURE_DEPTH][MAXPATHNAMELENGTH];
|
||||
|
@ -972,9 +972,9 @@ void lcd_sdcard_menu()
|
||||
card.getWorkDirName();
|
||||
if(card.filename[0]=='/')
|
||||
{
|
||||
#if SDCARDDETECT == -1
|
||||
#if SDCARDDETECT == -1
|
||||
MENU_ITEM(function, LCD_STR_REFRESH MSG_REFRESH, lcd_sd_refresh);
|
||||
#endif
|
||||
#endif
|
||||
}else{
|
||||
MENU_ITEM(function, LCD_STR_FOLDER "..", lcd_sd_updir);
|
||||
}
|
||||
@ -983,16 +983,23 @@ void lcd_sdcard_menu()
|
||||
{
|
||||
if (_menuItemNr == _lineNr)
|
||||
{
|
||||
#ifndef SDCARD_RATHERRECENTFIRST
|
||||
card.getfilename(i);
|
||||
#if defined(SDCARD_RATHERRECENTFIRST) && !defined(SDCARD_SORT_ALPHA)
|
||||
int nr = fileCnt-1-i;
|
||||
#else
|
||||
card.getfilename(fileCnt-1-i);
|
||||
int nr = i;
|
||||
#endif
|
||||
if (card.filenameIsDir)
|
||||
{
|
||||
MENU_ITEM(sddirectory, MSG_CARD_MENU, card.filename, card.longFilename);
|
||||
}else{
|
||||
MENU_ITEM(sdfile, MSG_CARD_MENU, card.filename, card.longFilename);
|
||||
|
||||
#ifdef SDCARD_SORT_ALPHA
|
||||
card.getfilename_sorted(nr);
|
||||
#else
|
||||
card.getfilename(nr);
|
||||
#endif
|
||||
|
||||
if (card.filenameIsDir) {
|
||||
MENU_ITEM(sddirectory, MSG_CARD_MENU, card.filename, card.diveFilename);
|
||||
}
|
||||
else {
|
||||
MENU_ITEM(sdfile, MSG_CARD_MENU, card.filename, card.diveFilename);
|
||||
}
|
||||
}else{
|
||||
MENU_ITEM_DUMMY();
|
||||
@ -1198,7 +1205,7 @@ void lcd_init()
|
||||
#endif // SR_LCD_2W_NL
|
||||
#endif//!NEWPANEL
|
||||
|
||||
#if defined (SDSUPPORT) && defined(SDCARDDETECT) && (SDCARDDETECT > 0)
|
||||
#if defined(SDSUPPORT) && defined(SDCARDDETECT) && (SDCARDDETECT > 0)
|
||||
pinMode(SDCARDDETECT,INPUT);
|
||||
WRITE(SDCARDDETECT, HIGH);
|
||||
lcd_oldcardstatus = IS_SD_INSERTED;
|
||||
|
Loading…
Reference in New Issue
Block a user