From 6901445592dd9e9b164b00b4ff432f0e89f63511 Mon Sep 17 00:00:00 2001
From: Scott Lahteine <sourcetree@thinkyhead.com>
Date: Wed, 26 Nov 2014 07:17:47 -0800
Subject: [PATCH] Improvements, more SORT_USES_MORE_RAM

With this option, always keeps the dir in RAM, doubling as a cache for
getfilename. A board with only 8K of SRAM is cutting it very close.
---
 Marlin/cardreader.cpp | 137 +++++++++++++++++++++---------------------
 Marlin/cardreader.h   |  18 +++---
 Marlin/ultralcd.cpp   |   4 +-
 3 files changed, 78 insertions(+), 81 deletions(-)

diff --git a/Marlin/cardreader.cpp b/Marlin/cardreader.cpp
index 10a4e6b1cf..5d465f47a4 100644
--- a/Marlin/cardreader.cpp
+++ b/Marlin/cardreader.cpp
@@ -11,8 +11,7 @@
 
 CardReader::CardReader()
 {
-  #if defined(SDCARD_SORT_ALPHA) && SORT_USES_MORE_RAM
-   sortnames = NULL;
+  #ifdef SDCARD_SORT_ALPHA
    sort_count = 0;
   #endif
    filesize = 0;
@@ -37,19 +36,15 @@ CardReader::CardReader()
   autostart_atmillis=millis()+5000;
 }
 
-char *createFilename(char *buffer,const dir_t &p) //buffer>12characters
+char *createFilename(char *buffer, const dir_t &p) //buffer>12characters
 {
   char *pos=buffer;
-  for (uint8_t i = 0; i < 11; i++) 
-  {
-    if (p.name[i] == ' ')continue;
-    if (i == 8) 
-    {
-      *pos++='.';
-    }
-    *pos++=p.name[i];
+  for (uint8_t i = 0; i < 11; i++) {
+    if (p.name[i] == ' ') continue;
+    if (i == 8) *pos++ = '.';
+    *pos++ = p.name[i];
   }
-  *pos++=0;
+  *pos++ = 0;
   return buffer;
 }
 
@@ -59,7 +54,7 @@ void  CardReader::lsDive(const char *prepend,SdFile parent)
   dir_t p;
   uint8_t cnt=0;
  
-  while (parent.readDir(p, diveFilename) > 0)
+  while (parent.readDir(p, longFilename) > 0)
   {
     if( DIR_IS_SUBDIR(&p) && lsAction!=LS_Count && lsAction!=LS_GetFilename) // hence LS_SerialPrint
     {
@@ -96,8 +91,8 @@ void  CardReader::lsDive(const char *prepend,SdFile parent)
     {
       if (p.name[0] == DIR_NAME_FREE) break;
       if (p.name[0] == DIR_NAME_DELETED || p.name[0] == '.'|| p.name[0] == '_') continue;
-      if (diveFilename[0] != '\0' &&
-          (diveFilename[0] == '.' || diveFilename[0] == '_')) continue;
+      if (longFilename[0] != '\0' &&
+          (longFilename[0] == '.' || longFilename[0] == '_')) continue;
       if ( p.name[0] == '.')
       {
         if ( p.name[1] != '.')
@@ -556,21 +551,20 @@ void CardReader::closefile(bool store_location)
   
 }
 
-void CardReader::getfilename(const uint8_t nr)
+void CardReader::getfilename(const uint16_t nr)
 {
-  #if defined(SDCARD_SORT_ALPHA) && SORT_USES_MORE_RAM
+  #if defined(SDCARD_SORT_ALPHA) && SORT_USES_RAM && SORT_USES_MORE_RAM
     if (nr < sort_count) {
-      strcpy(diveFilename, sortnames[nr]);
+      strcpy(longFilename, sortnames[nr]);
+      filenameIsDir = isDir[nr];
       return;
     }
   #endif
-
   curDir=&workDir;
   lsAction=LS_GetFilename;
   nrFiles=nr;
   curDir->rewind();
   lsDive("",*curDir);
-  
 }
 
 uint16_t CardReader::getnrfilenames()
@@ -631,12 +625,8 @@ void CardReader::updir()
 /**
  * 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
+void CardReader::getfilename_sorted(const uint16_t nr) {
+  getfilename(nr < sort_count ? sort_order[nr] : nr);
 }
 
 /**
@@ -656,68 +646,73 @@ void CardReader::presort()
 
     if (fileCnt > SORT_LIMIT) fileCnt = SORT_LIMIT;
 
-    #if SORT_USES_MORE_RAM
-      sortnames = (char**)malloc(fileCnt * sizeof(char*));
-      sort_count = fileCnt;
-    #elif SORT_USES_RAM
-      char *sortnames[fileCnt];
-      #if FOLDER_SORTING != 0
-        uint8_t isdir[fileCnt];
+    #if SORT_USES_RAM
+      #if SORT_USES_MORE_RAM
+        sortnames = (char**)calloc(fileCnt, sizeof(char*));
+      #else
+        char *sortnames[fileCnt];
       #endif
     #else
-      char sortname[LONG_FILENAME_LENGTH+1];
+      char name1[LONG_FILENAME_LENGTH+1];
     #endif
 
+    #if FOLDER_SORTING != 0
+      #if SORT_USES_RAM && SORT_USES_MORE_RAM
+        isDir = (uint8_t*)calloc(fileCnt, sizeof(uint8_t));
+      #else
+        uint8_t isDir[fileCnt];
+      #endif
+    #endif
+
+    sort_count = fileCnt;
+    sort_order = new uint8_t[fileCnt];
+
     if (fileCnt > 1) {
 
-      // Init sort order [and get filenames]
-      for (int i=0; i<fileCnt; i++) {
-        int byte=i/8, bit=1<<(i%8);
+      // Init sort order. If using RAM then read all filenames now.
+      for (uint16_t i=0; i<fileCnt; i++) {
         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);
+          sortnames[i] = strdup(longFilename[0] ? longFilename : filename);
+          // char out[30];
+          // sprintf_P(out, PSTR("---- %i %s %s"), i, filenameIsDir ? "D" : " ", sortnames[i]);
+          // SERIAL_ECHOLN(out);
           #if FOLDER_SORTING != 0
-            isdir[i] = filenameIsDir;
+            isDir[i] = filenameIsDir;
           #endif
         #endif
       }
 
       // Bubble Sort
-      for (uint8_t i=fileCnt; --i;) {
+      for (uint16_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];
+        for (uint16_t j=0; j<i; ++j) {
+          uint16_t 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];
+              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);
+              cmp = strcasecmp(sortnames[o1], sortnames[o2]) > 0;
             #endif
           #else
             getfilename(o1);
+            strcpy(name1, longFilename[0] ? longFilename : filename);
             #if FOLDER_SORTING != 0
               bool dir1 = filenameIsDir;
             #endif
-            char *name = diveFilename[0] ? diveFilename : filename;
-            strcpy(sortname, name);
             getfilename(o2);
-            name = diveFilename[0] ? diveFilename : filename;
+            char *name2 = longFilename[0] ? longFilename : filename;
             #if FOLDER_SORTING != 0
-              cmp = (dir1 == filenameIsDir) ? (strcasecmp(sortname, name) > 0) : (FOLDER_SORTING > 0 ? dir1 : !dir1);
+              cmp = (dir1 == filenameIsDir) ? (strcasecmp(name1, name2) > 0) : (FOLDER_SORTING > 0 ? dir1 : !dir1);
             #else
-              cmp = strcasecmp(sortname, name) > 0);
+              cmp = strcasecmp(name1, name2) > 0;
             #endif
           #endif
           if (cmp) {
-            // SERIAL_ECHOPGM("Swap ");
-            // SERIAL_ECHOLN(sortnames[o1]);
-            // SERIAL_ECHOPGM(" for ");
-            // SERIAL_ECHOLN(sortnames[o2]);
+            // char out[LONG_FILENAME_LENGTH*2+20];
+            // sprintf_P(out, PSTR("Swap %i %s for %i %s"), o1, sortnames[o1], o2, sortnames[o2]);
+            // SERIAL_ECHOLN(out);
             sort_order[s1] = o2;
             sort_order[s2] = o1;
             didSwap = true;
@@ -727,30 +722,32 @@ void CardReader::presort()
       }
 
       #if SORT_USES_RAM && !SORT_USES_MORE_RAM
-        for (int i=0; i < fileCnt; ++i) free(sortnames[i]);
+        for (uint16_t i=0; i<fileCnt; ++i) free(sortnames[i]);
       #endif
     }
     else {
       sort_order[0] = 0;
+      #if SORT_USES_RAM && SORT_USES_MORE_RAM
+        sortnames = (char**)malloc(sizeof(char*));
+        isDir = (uint8_t*)malloc(sizeof(uint8_t));
+        getfilename(0);
+        sortnames[0] = strdup(longFilename[0] ? longFilename : filename);
+        isDir[0] = filenameIsDir;
+      #endif
     }
 
   }
 }
 
 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;
-      }
+  if (sort_count > 0) {
+    #if SORT_USES_RAM && SORT_USES_MORE_RAM
+      for (uint8_t i=0; i<sort_count; ++i) free(sortnames[i]);
       free(sortnames);
-      sortnames = NULL;
-      sort_count = 0;
-    }
-  #else
-    for (int i=SORT_LIMIT; --i;) sort_order[i] = i;
-  #endif
+    #endif
+    delete sort_order;
+    sort_count = 0;
+  }
 }
 
 #endif // SDCARD_SORT_ALPHA
diff --git a/Marlin/cardreader.h b/Marlin/cardreader.h
index 1d8d1b1fb6..6a74fe0661 100644
--- a/Marlin/cardreader.h
+++ b/Marlin/cardreader.h
@@ -6,7 +6,7 @@
 #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 SORT_LIMIT 64             // Maximum number of sorted items
 #define FOLDER_SORTING -1         // -1=above  0=none  1=below
 
 #include "SdFile.h"
@@ -32,7 +32,7 @@ public:
   void getStatus();
   void printingHasFinished();
 
-  void getfilename(const uint8_t nr);
+  void getfilename(const uint16_t nr);
   uint16_t getnrfilenames();
   
   void getAbsFilename(char *t);
@@ -46,7 +46,7 @@ public:
 #ifdef SDCARD_SORT_ALPHA
   void presort();
   void flush_presort();
-  void getfilename_sorted(const uint8_t nr);
+  void getfilename_sorted(const uint16_t nr);
 #endif
 
 
@@ -60,21 +60,21 @@ public:
 public:
   bool saving;
   bool logging;
-  bool sdprinting ;  
+  bool sdprinting;
   bool cardOK;
   char filename[FILENAME_LENGTH];
-  char diveFilename[LONG_FILENAME_LENGTH];
+  char longFilename[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
+  uint16_t sort_count;
+  uint8_t *sort_order;
   #if SORT_USES_MORE_RAM
-    uint16_t sort_count;
     char **sortnames;
-  #else
-    uint8_t sort_order[SORT_LIMIT];
+    uint8_t *isDir;
   #endif
 #endif
   Sd2Card card;
@@ -93,7 +93,7 @@ private:
   bool autostart_stilltocheck; //the sd start is delayed, because otherwise the serial cannot answer fast enought to make contact with the hostsoftware.
   
   LsAction lsAction; //stored for recursion.
-  int16_t nrFiles; //counter for the files in the current directory and recycled as position counter for getting the nrFiles'th name in the directory.
+  uint16_t nrFiles; //counter for the files in the current directory and recycled as position counter for getting the nrFiles'th name in the directory.
   char* diveDirName;
   void lsDive(const char *prepend,SdFile parent);
 };
diff --git a/Marlin/ultralcd.cpp b/Marlin/ultralcd.cpp
index 981efdb606..f131499b94 100644
--- a/Marlin/ultralcd.cpp
+++ b/Marlin/ultralcd.cpp
@@ -970,10 +970,10 @@ void lcd_sdcard_menu()
             #endif
 
             if (card.filenameIsDir) {
-              MENU_ITEM(sddirectory, MSG_CARD_MENU, card.filename, card.diveFilename);
+              MENU_ITEM(sddirectory, MSG_CARD_MENU, card.filename, card.longFilename);
             }
             else {
-              MENU_ITEM(sdfile, MSG_CARD_MENU, card.filename, card.diveFilename);
+              MENU_ITEM(sdfile, MSG_CARD_MENU, card.filename, card.longFilename);
             }
         }else{
             MENU_ITEM_DUMMY();