Skip to content

Commit

Permalink
🔧 Reversible file alpha sorting (MarlinFirmware#26130)
Browse files Browse the repository at this point in the history
Co-authored-by: Scott Lahteine <[email protected]>
  • Loading branch information
2 people authored and EvilGremlin committed Oct 26, 2023
1 parent 07ecf33 commit d9d10e2
Show file tree
Hide file tree
Showing 9 changed files with 65 additions and 44 deletions.
5 changes: 3 additions & 2 deletions Marlin/Configuration_adv.h
Original file line number Diff line number Diff line change
Expand Up @@ -1746,9 +1746,10 @@

// SD Card Sorting options
#if ENABLED(SDCARD_SORT_ALPHA)
#define SDSORT_REVERSE false // Default to sorting file names in reverse order.
#define SDSORT_LIMIT 40 // Maximum number of sorted items (10-256). Costs 27 bytes each.
#define FOLDER_SORTING -1 // -1=above 0=none 1=below
#define SDSORT_GCODE false // Allow turning sorting on/off with LCD and M34 G-code.
#define SDSORT_FOLDERS -1 // -1=above 0=none 1=below
#define SDSORT_GCODE false // Enable G-code M34 to set sorting behaviors: M34 S<-1|0|1> F<-1|0|1>
#define SDSORT_USES_RAM false // Pre-allocate a static array for faster pre-sorting.
#define SDSORT_USES_STACK false // Prefer the stack for pre-sorting to give back some SRAM. (Negated by next 2 options.)
#define SDSORT_CACHE_NAMES false // Keep sorted items in RAM longer for speedy performance. Most expensive option.
Expand Down
12 changes: 11 additions & 1 deletion Marlin/src/gcode/sd/M34.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,19 @@

/**
* M34: Set SD Card Sorting Options
*
* S - Default sorting (i.e., SDSORT_REVERSE)
* S-1 - Reverse alpha sorting
* S0 - FID Order (not always newest)
* S1 - Forward alpha sorting
* S2 - Alias for S-1 [deprecated]
*
* F-1 - Folders above files
* F0 - Sort According to 'S'
* F1 - Folders after files
*/
void GcodeSuite::M34() {
if (parser.seen('S')) card.setSortOn(parser.value_bool());
if (parser.seen('S')) card.setSortOn(SortFlag(parser.ushortval('S', TERN(SDSORT_REVERSE, AS_REV, AS_FWD))));
if (parser.seenval('F')) {
const int v = parser.value_long();
card.setSortFolders(v < 0 ? -1 : v > 0 ? 1 : 0);
Expand Down
2 changes: 2 additions & 0 deletions Marlin/src/inc/Changes.h
Original file line number Diff line number Diff line change
Expand Up @@ -661,6 +661,8 @@
#error "Z4_USE_ENDSTOP is obsolete. Instead set Z4_STOP_PIN directly. (e.g., 'Z4_USE_ENDSTOP _ZMAX_' becomes 'Z4_STOP_PIN Z_MAX_PIN')"
#elif defined(INTEGRATED_BABYSTEPPING)
#error "INTEGRATED_BABYSTEPPING is no longer needed and should be removed."
#elif defined(FOLDER_SORTING)
#error "FOLDER_SORTING is now SDSORT_FOLDERS."
#endif

// L64xx stepper drivers have been removed
Expand Down
4 changes: 2 additions & 2 deletions Marlin/src/inc/Conditionals_post.h
Original file line number Diff line number Diff line change
Expand Up @@ -3337,8 +3337,8 @@
#define SDSORT_CACHE_NAMES true
#define SDSORT_CACHE_LPC1768_WARNING 1
#endif
#ifndef FOLDER_SORTING
#define FOLDER_SORTING -1
#ifndef SDSORT_FOLDERS
#define SDSORT_FOLDERS -1
#endif
#ifndef SDSORT_GCODE
#define SDSORT_GCODE false
Expand Down
4 changes: 2 additions & 2 deletions Marlin/src/lcd/e3v2/proui/dwin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -850,7 +850,7 @@ bool DWIN_lcd_sd_status = false;
#if ENABLED(MEDIASORT_MENU_ITEM)
void setMediaSort() {
toggleCheckboxLine(hmiData.mediaSort);
card.setSortOn(hmiData.mediaSort);
card.setSortOn(hmiData.mediaSort ? TERN(SDSORT_REVERSE, AS_REV, AS_FWD) : AS_OFF);
}
#endif

Expand Down Expand Up @@ -1754,7 +1754,7 @@ void dwinSetDataDefaults() {
#endif
#if ENABLED(MEDIASORT_MENU_ITEM)
hmiData.mediaSort = true;
card.setSortOn(true);
card.setSortOn(TERN(SDSORT_REVERSE, AS_REV, AS_FWD));
#endif
hmiData.mediaAutoMount = ENABLED(HAS_SD_EXTENDER);
#if ALL(INDIVIDUAL_AXIS_HOMING_SUBMENU, MESH_BED_LEVELING)
Expand Down
14 changes: 8 additions & 6 deletions Marlin/src/sd/SdBaseFile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1422,11 +1422,13 @@ int16_t SdBaseFile::read(void * const buf, uint16_t nbyte) {
*
* \param[out] dir The dir_t struct that will receive the data.
*
* \return For success readDir() returns the number of bytes read.
* A value of zero will be returned if end of file is reached.
* If an error occurs, readDir() returns -1. Possible errors include
* readDir() called before a directory has been opened, this is not
* a directory file or an I/O error occurred.
* \return For success return a non-zero value (number of bytes read).
* A value of zero will be returned if end of dir is reached.
* If an error occurs, readDir() returns -1. Possible errors:
* - readDir() called on unopened dir
* - not a directory file
* - bad dir entry
* - I/O error
*/
int8_t SdBaseFile::readDir(dir_t * const dir, char * const longFilename) {
int16_t n;
Expand Down Expand Up @@ -1488,7 +1490,7 @@ int8_t SdBaseFile::readDir(dir_t * const dir, char * const longFilename) {
longFilename[idx] = utf16_ch & 0xFF;
longFilename[idx + 1] = (utf16_ch >> 8) & 0xFF;
#else
// Replace all multibyte characters to '_'
// Replace multibyte character with '_'
longFilename[n + i] = (utf16_ch > 0xFF) ? '_' : (utf16_ch & 0xFF);
#endif
}
Expand Down
46 changes: 24 additions & 22 deletions Marlin/src/sd/cardreader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -91,8 +91,8 @@ int16_t CardReader::nrItems = -1;

int16_t CardReader::sort_count;
#if ENABLED(SDSORT_GCODE)
bool CardReader::sort_alpha;
int CardReader::sort_folders;
SortFlag CardReader::sort_alpha;
int8_t CardReader::sort_folders;
//bool CardReader::sort_reverse;
#endif

Expand Down Expand Up @@ -160,8 +160,8 @@ CardReader::CardReader() {
#if ENABLED(SDCARD_SORT_ALPHA)
sort_count = 0;
#if ENABLED(SDSORT_GCODE)
sort_alpha = true;
sort_folders = FOLDER_SORTING;
sort_alpha = TERN(SDSORT_REVERSE, AS_REV, AS_FWD);
sort_folders = SDSORT_FOLDERS;
//sort_reverse = false;
#endif
#endif
Expand Down Expand Up @@ -993,7 +993,7 @@ void CardReader::selectFileByIndex(const int16_t nr) {
if (nr < sort_count) {
strcpy(filename, sortshort[nr]);
strcpy(longFilename, sortnames[nr]);
flag.filenameIsDir = IS_DIR(nr);
TERN_(HAS_FOLDER_SORTING, flag.filenameIsDir = IS_DIR(nr));
setBinFlag(strcmp_P(strrchr(filename, '.'), PSTR(".BIN")) == 0);
return;
}
Expand All @@ -1011,7 +1011,7 @@ void CardReader::selectFileByName(const char * const match) {
if (strcasecmp(match, sortshort[nr]) == 0) {
strcpy(filename, sortshort[nr]);
strcpy(longFilename, sortnames[nr]);
flag.filenameIsDir = IS_DIR(nr);
TERN_(HAS_FOLDER_SORTING, flag.filenameIsDir = IS_DIR(nr));
setBinFlag(strcmp_P(strrchr(filename, '.'), PSTR(".BIN")) == 0);
return;
}
Expand Down Expand Up @@ -1163,7 +1163,7 @@ void CardReader::cdroot() {
* Get the name of a file in the working directory by sort-index
*/
void CardReader::selectFileByIndexSorted(const int16_t nr) {
selectFileByIndex(TERN1(SDSORT_GCODE, sort_alpha) && (nr < sort_count) ? sort_order[nr] : nr);
selectFileByIndex(SortFlag(TERN1(SDSORT_GCODE, sort_alpha != AS_OFF)) && (nr < sort_count) ? sort_order[nr] : nr);
}

#if ENABLED(SDSORT_USES_RAM)
Expand Down Expand Up @@ -1210,7 +1210,7 @@ void CardReader::cdroot() {
int16_t fileCnt = get_num_items();

// Sorting may be turned off
if (TERN0(SDSORT_GCODE, !sort_alpha)) return;
if (TERN0(SDSORT_GCODE, sort_alpha == AS_OFF)) return;

// If there are files, sort up to the limit
if (fileCnt > 0) {
Expand Down Expand Up @@ -1239,9 +1239,9 @@ void CardReader::cdroot() {
// Folder sorting needs 1 bit per entry for flags.
#if HAS_FOLDER_SORTING
#if ENABLED(SDSORT_DYNAMIC_RAM)
isDir = new uint8_t[(fileCnt + 7) >> 3];
isDir = new uint8_t[(fileCnt + 7) >> 3]; // Allocate space with 'new'
#elif ENABLED(SDSORT_USES_STACK)
uint8_t isDir[(fileCnt + 7) >> 3];
uint8_t isDir[(fileCnt + 7) >> 3]; // Use stack in this scope
#endif
#endif

Expand Down Expand Up @@ -1291,18 +1291,18 @@ void CardReader::cdroot() {
const int16_t o2 = sort_order[j + 1];

// Compare names from the array or just the two buffered names
#if ENABLED(SDSORT_USES_RAM)
#define _SORT_CMP_NODIR() (strcasecmp(sortnames[o1], sortnames[o2]) > 0)
#else
#define _SORT_CMP_NODIR() (strcasecmp(name1, name2) > 0)
#endif
auto _sort_cmp_file = [](char * const n1, char * const n2) -> bool {
const bool sort = strcasecmp(n1, n2) > 0;
return (TERN(SDSORT_GCODE, card.sort_alpha == AS_REV, ENABLED(SDSORT_REVERSE))) ? !sort : sort;
};
#define _SORT_CMP_FILE() _sort_cmp_file(TERN(SDSORT_USES_RAM, sortnames[o1], name1), TERN(SDSORT_USES_RAM, sortnames[o2], name2))

#if HAS_FOLDER_SORTING
#if ENABLED(SDSORT_USES_RAM)
// Folder sorting needs an index and bit to test for folder-ness.
#define _SORT_CMP_DIR(fs) (IS_DIR(o1) == IS_DIR(o2) ? _SORT_CMP_NODIR() : IS_DIR(fs > 0 ? o1 : o2))
#define _SORT_CMP_DIR(fs) (IS_DIR(o1) == IS_DIR(o2) ? _SORT_CMP_FILE() : IS_DIR(fs > 0 ? o1 : o2))
#else
#define _SORT_CMP_DIR(fs) ((dir1 == flag.filenameIsDir) ? _SORT_CMP_NODIR() : (fs > 0 ? dir1 : !dir1))
#define _SORT_CMP_DIR(fs) ((dir1 == flag.filenameIsDir) ? _SORT_CMP_FILE() : (fs > 0 ? dir1 : !dir1))
#endif
#endif

Expand All @@ -1318,12 +1318,12 @@ void CardReader::cdroot() {
if (
#if HAS_FOLDER_SORTING
#if ENABLED(SDSORT_GCODE)
sort_folders ? _SORT_CMP_DIR(sort_folders) : _SORT_CMP_NODIR()
sort_folders ? _SORT_CMP_DIR(sort_folders) : _SORT_CMP_FILE()
#else
_SORT_CMP_DIR(FOLDER_SORTING)
_SORT_CMP_DIR(SDSORT_FOLDERS)
#endif
#else
_SORT_CMP_NODIR()
_SORT_CMP_FILE()
#endif
) {
// Reorder the index, indicate that sorting happened
Expand Down Expand Up @@ -1357,12 +1357,14 @@ void CardReader::cdroot() {
#if ENABLED(SDSORT_DYNAMIC_RAM)
sortnames = new char*[1];
sortshort = new char*[1];
isDir = new uint8_t[1];
#endif
selectFileByIndex(0);
SET_SORTNAME(0);
SET_SORTSHORT(0);
isDir[0] = flag.filenameIsDir;
#if ALL(HAS_FOLDER_SORTING, SDSORT_DYNAMIC_RAM)
isDir = new uint8_t[1];
isDir[0] = flag.filenameIsDir;
#endif
#endif
}

Expand Down
16 changes: 10 additions & 6 deletions Marlin/src/sd/cardreader.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,10 @@ extern const char M23_STR[], M24_STR[];
#if ENABLED(SDSORT_DYNAMIC_RAM)
#define SD_RESORT 1
#endif
#if FOLDER_SORTING || ENABLED(SDSORT_GCODE)
#ifndef SDSORT_FOLDERS
#define SDSORT_FOLDERS 0
#endif
#if SDSORT_FOLDERS || ENABLED(SDSORT_GCODE)
#define HAS_FOLDER_SORTING 1
#endif
#endif
Expand Down Expand Up @@ -84,6 +87,7 @@ typedef struct {
} card_flags_t;

enum ListingFlags : uint8_t { LS_LONG_FILENAME, LS_ONLY_BIN, LS_TIMESTAMP };
enum SortFlag : int8_t { AS_REV = -1, AS_OFF, AS_FWD, AS_ALSO_REV };

#if ENABLED(AUTO_REPORT_SD_STATUS)
#include "../libs/autoreport.h"
Expand Down Expand Up @@ -199,8 +203,8 @@ class CardReader {
static void presort();
static void selectFileByIndexSorted(const int16_t nr);
#if ENABLED(SDSORT_GCODE)
FORCE_INLINE static void setSortOn(bool b) { sort_alpha = b; presort(); }
FORCE_INLINE static void setSortFolders(int i) { sort_folders = i; presort(); }
FORCE_INLINE static void setSortOn(const SortFlag f) { sort_alpha = (f == AS_ALSO_REV) ? AS_REV : f; presort(); }
FORCE_INLINE static void setSortFolders(const int8_t i) { sort_folders = i; presort(); }
//FORCE_INLINE static void setSortReverse(bool b) { sort_reverse = b; }
#endif
#else
Expand Down Expand Up @@ -272,12 +276,12 @@ class CardReader {
#if ENABLED(SDCARD_SORT_ALPHA)
static int16_t sort_count; // Count of sorted items in the current directory
#if ENABLED(SDSORT_GCODE)
static bool sort_alpha; // Flag to enable / disable the feature
static int sort_folders; // Folder sorting before/none/after
static SortFlag sort_alpha; // Sorting: REV, OFF, FWD
static int8_t sort_folders; // Folder sorting before/none/after
//static bool sort_reverse; // Flag to enable / disable reverse sorting
#endif

// By default the sort index is static
// By default the sort index is statically allocated
#if ENABLED(SDSORT_DYNAMIC_RAM)
static uint8_t *sort_order;
#else
Expand Down
6 changes: 3 additions & 3 deletions buildroot/tests/STM32F103VE_longer_maple
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,16 @@ set -e

use_example_configs Alfawise/U20
opt_enable BAUD_RATE_GCODE
exec_test $1 $2 "maple CLASSIC_UI U20 config" "$3"
exec_test $1 $2 "Maple - Alfawise U20 - CLASSIC_UI" "$3"

use_example_configs Alfawise/U20
opt_enable BAUD_RATE_GCODE TFT_COLOR_UI
opt_disable TFT_CLASSIC_UI CUSTOM_STATUS_SCREEN_IMAGE
exec_test $1 $2 "maple COLOR_UI U20 config" "$3"
exec_test $1 $2 "Maple - Alfawise U20 - COLOR_UI" "$3"

use_example_configs Alfawise/U20-bltouch
opt_enable BAUD_RATE_GCODE
exec_test $1 $2 "maple BLTouch U20 config"
exec_test $1 $2 "Maple - Alfawise U20 - BLTouch"

# cleanup
restore_configs

0 comments on commit d9d10e2

Please sign in to comment.