From 6d7d8c7c75e36034f9d8dcfd68090fb0819b0ac4 Mon Sep 17 00:00:00 2001
From: Voinea Dragos <voinea.dragos.alexandru@gmail.com>
Date: Mon, 19 Apr 2021 13:31:30 +0300
Subject: [PATCH 1/7] Do not show hidden/system dirs with M20 L

---
 Firmware/cardreader.cpp | 11 +++++------
 1 file changed, 5 insertions(+), 6 deletions(-)

diff --git a/Firmware/cardreader.cpp b/Firmware/cardreader.cpp
index 673e88c1..6f25330e 100644
--- a/Firmware/cardreader.cpp
+++ b/Firmware/cardreader.cpp
@@ -80,8 +80,12 @@ void CardReader::lsDive(const char *prepend, SdFile parent, const char * const m
 		for (position = parent.curPosition(); parent.readDir(p, longFilename) > 0; position = parent.curPosition()) {
 			if (recursionCnt > MAX_DIR_DEPTH)
 				return;
+			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;
 			else if (DIR_IS_SUBDIR(&p) && lsAction != LS_Count && lsAction != LS_GetFilename) { // If the entry is a directory and the action is LS_SerialPrint
-				
 				// Get the short name for the item, which we know is a folder
 				char lfilename[FILENAME_LENGTH];
 				createFilename(lfilename, p);
@@ -117,11 +121,6 @@ void CardReader::lsDive(const char *prepend, SdFile parent, const char * const m
 					puts_P(PSTR("DIR_EXIT"));
 			}
 			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) {

From b16e1e5235d8aab365ad2b5725049b7e2374dc11 Mon Sep 17 00:00:00 2001
From: Voinea Dragos <voinea.dragos.alexandru@gmail.com>
Date: Mon, 19 Apr 2021 13:54:43 +0300
Subject: [PATCH 2/7] Invert size and LFN in M20 L output

---
 Firmware/cardreader.cpp | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/Firmware/cardreader.cpp b/Firmware/cardreader.cpp
index 6f25330e..c5900a7c 100644
--- a/Firmware/cardreader.cpp
+++ b/Firmware/cardreader.cpp
@@ -133,12 +133,14 @@ void CardReader::lsDive(const char *prepend, SdFile parent, const char * const m
 						createFilename(filename, p);
 						SERIAL_PROTOCOL(prepend);
 						SERIAL_PROTOCOL(filename);
+						
 						MYSERIAL.write(' ');
+						SERIAL_PROTOCOL(p.fileSize);
 						
 						if (lsAction == LS_SerialPrint_LFN)
-							printf_P(PSTR("\"%s\" "), LONGEST_FILENAME);
+							printf_P(PSTR(" \"%s\""), LONGEST_FILENAME);
 						
-						SERIAL_PROTOCOLLN(p.fileSize);
+						SERIAL_PROTOCOLLN();
 						manage_heater();
 						break;
 				

From 2129bcf315dd6a8038c6d0eef0c4c7fa15b06112 Mon Sep 17 00:00:00 2001
From: Voinea Dragos <voinea.dragos.alexandru@gmail.com>
Date: Mon, 19 Apr 2021 14:48:50 +0300
Subject: [PATCH 3/7] M20 T

---
 Firmware/Marlin_main.cpp |  6 +++--
 Firmware/cardreader.cpp  | 53 ++++++++++++++++++++--------------------
 Firmware/cardreader.h    | 18 +++++++++++---
 3 files changed, 44 insertions(+), 33 deletions(-)

diff --git a/Firmware/Marlin_main.cpp b/Firmware/Marlin_main.cpp
index 68933093..f46c6a53 100755
--- a/Firmware/Marlin_main.cpp
+++ b/Firmware/Marlin_main.cpp
@@ -5744,11 +5744,13 @@ if(eSoundMode!=e_SOUND_MODE_SILENT)
     - `L` - Reports ling filenames instead of just short filenames. Requires host software parsing.
     */
     case 20:
+    {
       KEEPALIVE_STATE(NOT_BUSY); // do not send busy messages during listing. Inhibits the output of manage_heater()
       SERIAL_PROTOCOLLNRPGM(_N("Begin file list"));////MSG_BEGIN_FILE_LIST
-      card.ls(code_seen('L'));
+      struct CardReader::ls_param params = {.LFN = code_seen('L'), .timestamp = code_seen('T')};
+      card.ls(params);
       SERIAL_PROTOCOLLNRPGM(_N("End file list"));////MSG_END_FILE_LIST
-      break;
+    } break;
 
     /*!
 	### M21 - Init SD card <a href="https://reprap.org/wiki/G-code#M21:_Initialize_SD_card">M21: Initialize SD card</a>
diff --git a/Firmware/cardreader.cpp b/Firmware/cardreader.cpp
index c5900a7c..37c9ade9 100644
--- a/Firmware/cardreader.cpp
+++ b/Firmware/cardreader.cpp
@@ -61,10 +61,9 @@ char *createFilename(char *buffer,const dir_t &p) //buffer>12characters
 +*   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
-+*   LS_SerialPrint_LFN - Print the full path, long filename and size of each file to serial output
 +*/
 
-void CardReader::lsDive(const char *prepend, SdFile parent, const char * const match/*=NULL*/) {
+void CardReader::lsDive(const char *prepend, SdFile parent, const char * const match/*=NULL*/, LsAction lsAction, struct ls_param *lsParams) {
 	static uint8_t recursionCnt = 0;
 	// RAII incrementer for the recursionCnt
 	class _incrementer
@@ -85,7 +84,7 @@ void CardReader::lsDive(const char *prepend, SdFile parent, const char * const m
 			if (pn0 == DIR_NAME_DELETED || pn0 == '.') continue;
 			if (longFilename[0] == '.') continue;
 			if (!DIR_IS_FILE_OR_SUBDIR(&p) || (p.attributes & DIR_ATT_HIDDEN)) continue;
-			else if (DIR_IS_SUBDIR(&p) && lsAction != LS_Count && lsAction != LS_GetFilename) { // If the entry is a directory and the action is LS_SerialPrint
+			if (DIR_IS_SUBDIR(&p) && lsAction == LS_SerialPrint) { // If the entry is a directory and the action is LS_SerialPrint
 				// Get the short name for the item, which we know is a folder
 				char lfilename[FILENAME_LENGTH];
 				createFilename(lfilename, p);
@@ -103,21 +102,19 @@ void CardReader::lsDive(const char *prepend, SdFile parent, const char * const m
 				// Get a new directory object using the full path
 				// and dive recursively into it.
 				
-				if (lsAction == LS_SerialPrint_LFN)
+				if (lsParams->LFN)
 					printf_P(PSTR("DIR_ENTER: %s \"%s\"\n"), path, longFilename[0] ? longFilename : lfilename);
 				
 				SdFile dir;
 				if (!dir.open(parent, lfilename, O_READ)) {
-					if (lsAction == LS_SerialPrint || lsAction == LS_SerialPrint_LFN) {
-						//SERIAL_ECHO_START();
-						//SERIAL_ECHOPGM(_i("Cannot open subdir"));////MSG_SD_CANT_OPEN_SUBDIR
-						//SERIAL_ECHOLN(lfilename);
-					}
+					//SERIAL_ECHO_START();
+					//SERIAL_ECHOPGM(_i("Cannot open subdir"));////MSG_SD_CANT_OPEN_SUBDIR
+					//SERIAL_ECHOLN(lfilename);
 				}
-				lsDive(path, dir);
+				lsDive(path, dir, NULL, lsAction, lsParams);
 				// close() is done automatically by destructor of SdFile
 				
-				if (lsAction == LS_SerialPrint_LFN)
+				if (lsParams->LFN)
 					puts_P(PSTR("DIR_EXIT"));
 			}
 			else {
@@ -128,7 +125,6 @@ void CardReader::lsDive(const char *prepend, SdFile parent, const char * const m
 						nrFiles++;
 						break;
 					
-					case LS_SerialPrint_LFN:
 					case LS_SerialPrint:
 						createFilename(filename, p);
 						SERIAL_PROTOCOL(prepend);
@@ -137,7 +133,18 @@ void CardReader::lsDive(const char *prepend, SdFile parent, const char * const m
 						MYSERIAL.write(' ');
 						SERIAL_PROTOCOL(p.fileSize);
 						
-						if (lsAction == LS_SerialPrint_LFN)
+						if (lsParams->timestamp)
+						{
+							crmodDate = p.lastWriteDate;
+							crmodTime = p.lastWriteTime;
+							if( crmodDate < p.creationDate || ( crmodDate == p.creationDate && crmodTime < p.creationTime ) ){
+								crmodDate = p.creationDate;
+								crmodTime = p.creationTime;
+							}
+							printf_P(PSTR(" %#lx"), ((uint32_t)crmodDate << 16) | crmodTime);
+						}
+						
+						if (lsParams->LFN)
 							printf_P(PSTR(" \"%s\""), LONGEST_FILENAME);
 						
 						SERIAL_PROTOCOLLN();
@@ -182,14 +189,10 @@ void CardReader::lsDive(const char *prepend, SdFile parent, const char * const m
 		} // while readDir
 }
 
-void CardReader::ls(bool printLFN)
+void CardReader::ls(struct ls_param params)
 {
-  lsAction = printLFN ? LS_SerialPrint_LFN : LS_SerialPrint;
-  //if(lsAction==LS_Count)
-  //nrFiles=0;
-
   root.rewind();
-  lsDive("",root);
+  lsDive("",root, NULL, LS_SerialPrint, &params);
 }
 
 
@@ -696,38 +699,34 @@ void CardReader::closefile(bool store_location)
 void CardReader::getfilename(uint16_t nr, const char * const match/*=NULL*/)
 {
   curDir=&workDir;
-  lsAction=LS_GetFilename;
   nrFiles=nr;
   curDir->rewind();
-  lsDive("",*curDir,match);
+  lsDive("",*curDir,match, LS_GetFilename);
   
 }
 
 void CardReader::getfilename_simple(uint32_t position, const char * const match/*=NULL*/)
 {
 	curDir = &workDir;
-	lsAction = LS_GetFilename;
 	nrFiles = 0;
 	curDir->seekSet(position);
-	lsDive("", *curDir, match);
+	lsDive("", *curDir, match, LS_GetFilename);
 }
 
 void CardReader::getfilename_next(uint32_t position, const char * const match/*=NULL*/)
 {
 	curDir = &workDir;
-	lsAction = LS_GetFilename;
 	nrFiles = 1;
 	curDir->seekSet(position);
-	lsDive("", *curDir, match);
+	lsDive("", *curDir, match, LS_GetFilename);
 }
 
 uint16_t CardReader::getnrfilenames()
 {
   curDir=&workDir;
-  lsAction=LS_Count;
   nrFiles=0;
   curDir->rewind();
-  lsDive("",*curDir);
+  lsDive("",*curDir, NULL, LS_Count);
   //SERIAL_ECHOLN(nrFiles);
   return nrFiles;
 }
diff --git a/Firmware/cardreader.h b/Firmware/cardreader.h
index 47343e23..7eba0f51 100644
--- a/Firmware/cardreader.h
+++ b/Firmware/cardreader.h
@@ -8,12 +8,23 @@
 #define MAX_DIR_DEPTH 6
 
 #include "SdFile.h"
-enum LsAction {LS_SerialPrint,LS_SerialPrint_LFN,LS_Count,LS_GetFilename};
 class CardReader
 {
 public:
   CardReader();
   
+  enum LsAction : uint8_t
+  {
+    LS_SerialPrint,
+    LS_Count,
+    LS_GetFilename,
+  };
+  struct ls_param
+  {
+    bool LFN : 1;
+    bool timestamp : 1;
+  } __attribute__((packed));
+  
   void initsd();
   void write_command(char *buf);
   void write_command_no_newline(char *buf);
@@ -43,7 +54,7 @@ public:
   uint16_t getWorkDirDepth();
   
 
-  void ls(bool printLFN);
+  void ls(struct ls_param params);
   bool chdir(const char * relpath, bool doPresort);
   void updir();
   void setroot(bool doPresort);
@@ -122,12 +133,11 @@ 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.
   char* diveDirName;
 
   bool diveSubfolder (const char *&fileName);
-  void lsDive(const char *prepend, SdFile parent, const char * const match=NULL);
+  void lsDive(const char *prepend, SdFile parent, const char * const match=NULL, LsAction lsAction = LS_GetFilename, struct ls_param *lsParams = NULL);
 #ifdef SDCARD_SORT_ALPHA
   void flush_presort();
 #endif

From 7011014abb9d49bd270a6ea257c8209be7c64969 Mon Sep 17 00:00:00 2001
From: "D.R.racer" <drracer@drracer.eu>
Date: Tue, 20 Apr 2021 06:50:37 +0200
Subject: [PATCH 4/7] Save 16 bytes - pass ls_param by value to functions

... as ls_param is a 1-byte structure it is more conservative to pass it
to functions by value than by a pointer
---
 Firmware/Marlin_main.cpp |  6 ++----
 Firmware/cardreader.cpp  | 14 +++++++-------
 Firmware/cardreader.h    |  6 ++++--
 3 files changed, 13 insertions(+), 13 deletions(-)

diff --git a/Firmware/Marlin_main.cpp b/Firmware/Marlin_main.cpp
index f46c6a53..6c8c423c 100755
--- a/Firmware/Marlin_main.cpp
+++ b/Firmware/Marlin_main.cpp
@@ -5744,13 +5744,11 @@ if(eSoundMode!=e_SOUND_MODE_SILENT)
     - `L` - Reports ling filenames instead of just short filenames. Requires host software parsing.
     */
     case 20:
-    {
       KEEPALIVE_STATE(NOT_BUSY); // do not send busy messages during listing. Inhibits the output of manage_heater()
       SERIAL_PROTOCOLLNRPGM(_N("Begin file list"));////MSG_BEGIN_FILE_LIST
-      struct CardReader::ls_param params = {.LFN = code_seen('L'), .timestamp = code_seen('T')};
-      card.ls(params);
+      card.ls(CardReader::ls_param(code_seen('L'), code_seen('T')));
       SERIAL_PROTOCOLLNRPGM(_N("End file list"));////MSG_END_FILE_LIST
-    } break;
+    break;
 
     /*!
 	### M21 - Init SD card <a href="https://reprap.org/wiki/G-code#M21:_Initialize_SD_card">M21: Initialize SD card</a>
diff --git a/Firmware/cardreader.cpp b/Firmware/cardreader.cpp
index 37c9ade9..a86c49e1 100644
--- a/Firmware/cardreader.cpp
+++ b/Firmware/cardreader.cpp
@@ -63,7 +63,7 @@ char *createFilename(char *buffer,const dir_t &p) //buffer>12characters
 +*   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/*=NULL*/, LsAction lsAction, struct ls_param *lsParams) {
+void CardReader::lsDive(const char *prepend, SdFile parent, const char * const match/*=NULL*/, LsAction lsAction, ls_param lsParams) {
 	static uint8_t recursionCnt = 0;
 	// RAII incrementer for the recursionCnt
 	class _incrementer
@@ -102,7 +102,7 @@ void CardReader::lsDive(const char *prepend, SdFile parent, const char * const m
 				// Get a new directory object using the full path
 				// and dive recursively into it.
 				
-				if (lsParams->LFN)
+				if (lsParams.LFN)
 					printf_P(PSTR("DIR_ENTER: %s \"%s\"\n"), path, longFilename[0] ? longFilename : lfilename);
 				
 				SdFile dir;
@@ -114,7 +114,7 @@ void CardReader::lsDive(const char *prepend, SdFile parent, const char * const m
 				lsDive(path, dir, NULL, lsAction, lsParams);
 				// close() is done automatically by destructor of SdFile
 				
-				if (lsParams->LFN)
+				if (lsParams.LFN)
 					puts_P(PSTR("DIR_EXIT"));
 			}
 			else {
@@ -133,7 +133,7 @@ void CardReader::lsDive(const char *prepend, SdFile parent, const char * const m
 						MYSERIAL.write(' ');
 						SERIAL_PROTOCOL(p.fileSize);
 						
-						if (lsParams->timestamp)
+						if (lsParams.timestamp)
 						{
 							crmodDate = p.lastWriteDate;
 							crmodTime = p.lastWriteTime;
@@ -144,7 +144,7 @@ void CardReader::lsDive(const char *prepend, SdFile parent, const char * const m
 							printf_P(PSTR(" %#lx"), ((uint32_t)crmodDate << 16) | crmodTime);
 						}
 						
-						if (lsParams->LFN)
+						if (lsParams.LFN)
 							printf_P(PSTR(" \"%s\""), LONGEST_FILENAME);
 						
 						SERIAL_PROTOCOLLN();
@@ -189,10 +189,10 @@ void CardReader::lsDive(const char *prepend, SdFile parent, const char * const m
 		} // while readDir
 }
 
-void CardReader::ls(struct ls_param params)
+void CardReader::ls(ls_param params)
 {
   root.rewind();
-  lsDive("",root, NULL, LS_SerialPrint, &params);
+  lsDive("",root, NULL, LS_SerialPrint, params);
 }
 
 
diff --git a/Firmware/cardreader.h b/Firmware/cardreader.h
index 7eba0f51..c6e55428 100644
--- a/Firmware/cardreader.h
+++ b/Firmware/cardreader.h
@@ -23,6 +23,8 @@ public:
   {
     bool LFN : 1;
     bool timestamp : 1;
+    inline ls_param():LFN(0), timestamp(0) { }
+    inline ls_param(bool LFN, bool timestamp):LFN(LFN), timestamp(timestamp) { }
   } __attribute__((packed));
   
   void initsd();
@@ -54,7 +56,7 @@ public:
   uint16_t getWorkDirDepth();
   
 
-  void ls(struct ls_param params);
+  void ls(ls_param params);
   bool chdir(const char * relpath, bool doPresort);
   void updir();
   void setroot(bool doPresort);
@@ -137,7 +139,7 @@ private:
   char* diveDirName;
 
   bool diveSubfolder (const char *&fileName);
-  void lsDive(const char *prepend, SdFile parent, const char * const match=NULL, LsAction lsAction = LS_GetFilename, struct ls_param *lsParams = NULL);
+  void lsDive(const char *prepend, SdFile parent, const char * const match=NULL, LsAction lsAction = LS_GetFilename, ls_param lsParams = ls_param());
 #ifdef SDCARD_SORT_ALPHA
   void flush_presort();
 #endif

From 1c0383c48f21235f1c707b59d8494ea053d9449a Mon Sep 17 00:00:00 2001
From: Alex Voinea <voinea.dragos.alexandru@gmail.com>
Date: Tue, 20 Apr 2021 09:03:22 +0300
Subject: [PATCH 5/7] Add capability report for extended M20

---
 Firmware/Marlin_main.cpp | 1 +
 1 file changed, 1 insertion(+)

diff --git a/Firmware/Marlin_main.cpp b/Firmware/Marlin_main.cpp
index 6c8c423c..ed855fdc 100755
--- a/Firmware/Marlin_main.cpp
+++ b/Firmware/Marlin_main.cpp
@@ -3964,6 +3964,7 @@ static void extended_capabilities_report()
     // AUTOREPORT_POSITION (M114)
     cap_line(PSTR("AUTOREPORT_POSITION"), ENABLED(AUTO_REPORT));
     //@todo Update RepRap cap
+    cap_line(PSTR("EXTENDED_M20"), 1);
 }
 #endif //EXTENDED_CAPABILITIES_REPORT
 

From c4b70b82f35d27e85b2ee54e67fcce06f0a84dcb Mon Sep 17 00:00:00 2001
From: Alex Voinea <voinea.dragos.alexandru@gmail.com>
Date: Tue, 20 Apr 2021 09:13:38 +0300
Subject: [PATCH 6/7] Document M20 T parameter

---
 Firmware/Marlin_main.cpp | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/Firmware/Marlin_main.cpp b/Firmware/Marlin_main.cpp
index ed855fdc..3f8154d8 100755
--- a/Firmware/Marlin_main.cpp
+++ b/Firmware/Marlin_main.cpp
@@ -5740,9 +5740,10 @@ if(eSoundMode!=e_SOUND_MODE_SILENT)
 	### M20 - SD Card file list <a href="https://reprap.org/wiki/G-code#M20:_List_SD_card">M20: List SD card</a>
     #### Usage
     
-        M20 [ L ]
+        M20 [ L | T ]
     #### Parameters
-    - `L` - Reports ling filenames instead of just short filenames. Requires host software parsing.
+    - `T` - Report timestamps as well. The value is one uint32_t encoded as hex. Requires host software parsing (Cap:EXTENDED_M20).
+    - `L` - Reports long filenames instead of just short filenames. Requires host software parsing (Cap:EXTENDED_M20).
     */
     case 20:
       KEEPALIVE_STATE(NOT_BUSY); // do not send busy messages during listing. Inhibits the output of manage_heater()

From 7d82cab1253653d3c33d8b1618454dd9f8349ab6 Mon Sep 17 00:00:00 2001
From: Alex Voinea <voinea.dragos.alexandru@gmail.com>
Date: Tue, 20 Apr 2021 09:16:56 +0300
Subject: [PATCH 7/7] Update doxygen

---
 Firmware/Marlin_main.cpp | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/Firmware/Marlin_main.cpp b/Firmware/Marlin_main.cpp
index 3f8154d8..f391159b 100755
--- a/Firmware/Marlin_main.cpp
+++ b/Firmware/Marlin_main.cpp
@@ -3963,8 +3963,9 @@ static void extended_capabilities_report()
 #endif //FANCHECK and TACH_0 or TACH_1
     // AUTOREPORT_POSITION (M114)
     cap_line(PSTR("AUTOREPORT_POSITION"), ENABLED(AUTO_REPORT));
-    //@todo Update RepRap cap
+    // EXTENDED_M20 (support for L and T parameters)
     cap_line(PSTR("EXTENDED_M20"), 1);
+    //@todo Update RepRap cap
 }
 #endif //EXTENDED_CAPABILITIES_REPORT