Browse Source

✨ Long filename open/create/write (#23526)

Co-authored-by: Scott Lahteine <github@thinkyhead.com>
GHGiampy 3 years ago
parent
commit
1e246d65ad
No account linked to committer's email address

+ 9
- 19
Marlin/Configuration_adv.h View File

@@ -1525,33 +1525,23 @@
1525 1525
   // LCD's font must contain the characters. Check your selected LCD language.
1526 1526
   //#define UTF_FILENAME_SUPPORT
1527 1527
 
1528
-  // This allows hosts to request long names for files and folders with M33
1529
-  //#define LONG_FILENAME_HOST_SUPPORT
1528
+  //#define LONG_FILENAME_HOST_SUPPORT    // Get the long filename of a file/folder with 'M33 <dosname>' and list long filenames with 'M20 L'
1529
+  //#define LONG_FILENAME_WRITE_SUPPORT   // Create / delete files with long filenames via M28, M30, and Binary Transfer Protocol
1530 1530
 
1531
-  // Enable this option to scroll long filenames in the SD card menu
1532
-  //#define SCROLL_LONG_FILENAMES
1531
+  //#define SCROLL_LONG_FILENAMES         // Scroll long filenames in the SD card menu
1533 1532
 
1534
-  // Leave the heaters on after Stop Print (not recommended!)
1535
-  //#define SD_ABORT_NO_COOLDOWN
1533
+  //#define SD_ABORT_NO_COOLDOWN          // Leave the heaters on after Stop Print (not recommended!)
1536 1534
 
1537 1535
   /**
1538
-   * This option allows you to abort SD printing when any endstop is triggered.
1539
-   * This feature must be enabled with "M540 S1" or from the LCD menu.
1540
-   * To have any effect, endstops must be enabled during SD printing.
1536
+   * Abort SD printing when any endstop is triggered.
1537
+   * This feature is enabled with 'M540 S1' or from the LCD menu.
1538
+   * Endstops must be activated for this option to work.
1541 1539
    */
1542 1540
   //#define SD_ABORT_ON_ENDSTOP_HIT
1543 1541
 
1544
-  /**
1545
-   * This option makes it easier to print the same SD Card file again.
1546
-   * On print completion the LCD Menu will open with the file selected.
1547
-   * You can just click to start the print, or navigate elsewhere.
1548
-   */
1549
-  //#define SD_REPRINT_LAST_SELECTED_FILE
1542
+  //#define SD_REPRINT_LAST_SELECTED_FILE // On print completion open the LCD Menu and select the same file
1550 1543
 
1551
-  /**
1552
-   * Auto-report SdCard status with M27 S<seconds>
1553
-   */
1554
-  //#define AUTO_REPORT_SD_STATUS
1544
+  //#define AUTO_REPORT_SD_STATUS         // Auto-report media status with 'M27 S<seconds>'
1555 1545
 
1556 1546
   /**
1557 1547
    * Support for USB thumb drives using an Arduino USB Host Shield or

+ 2
- 0
Marlin/src/gcode/eeprom/M500-M504.cpp View File

@@ -56,6 +56,8 @@ void GcodeSuite::M502() {
56 56
   /**
57 57
    * M503: print settings currently in memory
58 58
    *
59
+   *   S<bool> : Include / exclude header comments in the output. (Default: S1)
60
+   *
59 61
    * With CONFIGURATION_EMBEDDING:
60 62
    *   C<flag> : Save the full Marlin configuration to SD Card as "mc.zip"
61 63
    */

+ 7
- 1
Marlin/src/gcode/host/M115.cpp View File

@@ -154,6 +154,12 @@ void GcodeSuite::M115() {
154 154
     // LONG_FILENAME_HOST_SUPPORT (M33)
155 155
     cap_line(F("LONG_FILENAME"), ENABLED(LONG_FILENAME_HOST_SUPPORT));
156 156
 
157
+    // LONG_FILENAME_WRITE_SUPPORT (M23, M28, M30...)
158
+    cap_line(F("LFN_WRITE"), ENABLED(LONG_FILENAME_WRITE_SUPPORT));
159
+
160
+    // CUSTOM_FIRMWARE_UPLOAD (M20 F)
161
+    cap_line(F("CUSTOM_FIRMWARE_UPLOAD"), ENABLED(CUSTOM_FIRMWARE_UPLOAD));
162
+
157 163
     // EXTENDED_M20 (M20 L)
158 164
     cap_line(F("EXTENDED_M20"), ENABLED(LONG_FILENAME_HOST_SUPPORT));
159 165
 
@@ -179,7 +185,7 @@ void GcodeSuite::M115() {
179 185
     cap_line(F("MEATPACK"), SERIAL_IMPL.has_feature(port, SerialFeature::MeatPack));
180 186
 
181 187
     // CONFIG_EXPORT
182
-    cap_line(F("CONFIG_EXPORT"), ENABLED(CONFIG_EMBED_AND_SAVE_TO_SD));
188
+    cap_line(F("CONFIG_EXPORT"), ENABLED(CONFIGURATION_EMBEDDING));
183 189
 
184 190
     // Machine Geometry
185 191
     #if ENABLED(M115_GEOMETRY_REPORT)

+ 495
- 89
Marlin/src/sd/SdBaseFile.cpp View File

@@ -89,6 +89,7 @@ bool SdBaseFile::addDirCluster() {
89 89
 }
90 90
 
91 91
 // cache a file's directory entry
92
+// cache the current "dirBlock_" and return the entry at index "dirIndex_"
92 93
 // return pointer to cached entry or null for failure
93 94
 dir_t* SdBaseFile::cacheDirEntry(uint8_t action) {
94 95
   if (!vol_->cacheRawBlock(dirBlock_, action)) return nullptr;
@@ -384,6 +385,20 @@ int8_t SdBaseFile::lsPrintNext(uint8_t flags, uint8_t indent) {
384 385
   return DIR_IS_FILE(&dir) ? 1 : 2;
385 386
 }
386 387
 
388
+/**
389
+ * Calculate a checksum for an 8.3 filename
390
+ *
391
+ * \param name The 8.3 file name to calculate
392
+ *
393
+ * \return The checksum byte
394
+ */
395
+uint8_t lfn_checksum(const uint8_t *name) {
396
+  uint8_t sum = 0;
397
+  for (uint8_t i = 11; i; i--)
398
+    sum = ((sum & 1) << 7) + (sum >> 1) + *name++;
399
+  return sum;
400
+}
401
+
387 402
 // Format directory name field from a 8.3 name string
388 403
 bool SdBaseFile::make83Name(const char *str, uint8_t *name, const char **ptr) {
389 404
   uint8_t n = 7,                      // Max index until a dot is found
@@ -430,6 +445,10 @@ bool SdBaseFile::mkdir(SdBaseFile *parent, const char *path, bool pFlag) {
430 445
   SdBaseFile *sub = &dir1;
431 446
   SdBaseFile *start = parent;
432 447
 
448
+  #if ENABLED(LONG_FILENAME_WRITE_SUPPORT)
449
+    uint8_t dlname[LONG_FILENAME_LENGTH];
450
+  #endif
451
+
433 452
   if (!parent || isOpen()) return false;
434 453
 
435 454
   if (*path == '/') {
@@ -439,28 +458,31 @@ bool SdBaseFile::mkdir(SdBaseFile *parent, const char *path, bool pFlag) {
439 458
       parent = &dir2;
440 459
     }
441 460
   }
442
-  while (1) {
443
-    if (!make83Name(path, dname, &path)) return false;
461
+
462
+  for (;;) {
463
+    if (!TERN(LONG_FILENAME_WRITE_SUPPORT, parsePath(path, dname, dlname, &path), make83Name(path, dname, &path))) return false;
444 464
     while (*path == '/') path++;
445 465
     if (!*path) break;
446
-    if (!sub->open(parent, dname, O_READ)) {
447
-      if (!pFlag || !sub->mkdir(parent, dname))
466
+    if (!sub->open(parent, dname OPTARG(LONG_FILENAME_WRITE_SUPPORT, dlname), O_READ)) {
467
+      if (!pFlag || !sub->mkdir(parent, dname OPTARG(LONG_FILENAME_WRITE_SUPPORT, dlname)))
448 468
         return false;
449 469
     }
450 470
     if (parent != start) parent->close();
451 471
     parent = sub;
452 472
     sub = parent != &dir1 ? &dir1 : &dir2;
453 473
   }
454
-  return mkdir(parent, dname);
474
+  return mkdir(parent, dname OPTARG(LONG_FILENAME_WRITE_SUPPORT, dlname));
455 475
 }
456 476
 
457
-bool SdBaseFile::mkdir(SdBaseFile *parent, const uint8_t dname[11]) {
477
+bool SdBaseFile::mkdir(SdBaseFile *parent, const uint8_t dname[11]
478
+  OPTARG(LONG_FILENAME_WRITE_SUPPORT, const uint8_t dlname[LONG_FILENAME_LENGTH])
479
+) {
458 480
   if (ENABLED(SDCARD_READONLY)) return false;
459 481
 
460 482
   if (!parent->isDir()) return false;
461 483
 
462 484
   // create a normal file
463
-  if (!open(parent, dname, O_CREAT | O_EXCL | O_RDWR)) return false;
485
+  if (!open(parent, dname OPTARG(LONG_FILENAME_WRITE_SUPPORT, dlname), O_CREAT | O_EXCL | O_RDWR)) return false;
464 486
 
465 487
   // convert file to directory
466 488
   flags_ = O_READ;
@@ -578,6 +600,10 @@ bool SdBaseFile::open(SdBaseFile *dirFile, const char *path, uint8_t oflag) {
578 600
   SdBaseFile dir1, dir2;
579 601
   SdBaseFile *parent = dirFile, *sub = &dir1;
580 602
 
603
+  #if ENABLED(LONG_FILENAME_WRITE_SUPPORT)
604
+    uint8_t dlname[LONG_FILENAME_LENGTH];
605
+  #endif
606
+
581 607
   if (!dirFile || isOpen()) return false;
582 608
 
583 609
   if (*path == '/') {                                         // Path starts with '/'
@@ -589,90 +615,244 @@ bool SdBaseFile::open(SdBaseFile *dirFile, const char *path, uint8_t oflag) {
589 615
   }
590 616
 
591 617
   for (;;) {
592
-    if (!make83Name(path, dname, &path)) return false;
618
+    if (!TERN(LONG_FILENAME_WRITE_SUPPORT, parsePath(path, dname, dlname, &path), make83Name(path, dname, &path))) return false;
593 619
     while (*path == '/') path++;
594 620
     if (!*path) break;
595
-    if (!sub->open(parent, dname, O_READ)) return false;
621
+    if (TERN0(LONG_FILENAME_WRITE_SUPPORT, !sub->open(parent, dname, dlname, O_READ))) return false;
596 622
     if (parent != dirFile) parent->close();
597 623
     parent = sub;
598 624
     sub = parent != &dir1 ? &dir1 : &dir2;
599 625
   }
600
-  return open(parent, dname, oflag);
626
+  return open(parent, dname OPTARG(LONG_FILENAME_WRITE_SUPPORT, dlname), oflag);
601 627
 }
602 628
 
603
-// open with filename in dname
604
-bool SdBaseFile::open(SdBaseFile *dirFile, const uint8_t dname[11], uint8_t oflag) {
629
+// open with filename in dname and long filename in dlname
630
+bool SdBaseFile::open(SdBaseFile *dirFile, const uint8_t dname[11]
631
+    OPTARG(LONG_FILENAME_WRITE_SUPPORT, const uint8_t dlname[LONG_FILENAME_LENGTH])
632
+  , uint8_t oflag
633
+) {
605 634
   bool emptyFound = false, fileFound = false;
606
-  uint8_t index;
635
+  uint8_t index = 0;
607 636
   dir_t *p;
608 637
 
609
-  vol_ = dirFile->vol_;
638
+  #if ENABLED(LONG_FILENAME_WRITE_SUPPORT)
639
+    // LFN - Long File Name support
640
+    const bool useLFN = dlname[0] != 0;
641
+    bool lfnFileFound = false;
642
+    vfat_t *pvFat;
643
+    uint8_t emptyCount = 0,
644
+            emptyIndex = 0,
645
+            reqEntriesNum = useLFN ? getLFNEntriesNum((char*)dlname) + 1 : 1,
646
+            lfnNameLength = useLFN ? strlen((char*)dlname) : 0,
647
+            lfnName[LONG_FILENAME_LENGTH],
648
+            lfnSequenceNumber = 0,
649
+            lfnChecksum = 0;
650
+  #endif
610 651
 
652
+  // Rewind this dir
653
+  vol_ = dirFile->vol_;
611 654
   dirFile->rewind();
612
-  // search for file
613 655
 
656
+  // search for file
614 657
   while (dirFile->curPosition_ < dirFile->fileSize_) {
615
-    index = 0xF & (dirFile->curPosition_ >> 5);
616
-    p = dirFile->readDirCache();
617
-    if (!p) return false;
658
+    // Get absolute index position
659
+    index = (dirFile->curPosition_ >> 5) IF_DISABLED(LONG_FILENAME_WRITE_SUPPORT, & 0x0F);
618 660
 
661
+    // Get next entry
662
+    if (!(p = dirFile->readDirCache())) return false;
663
+
664
+    // Check empty status: Is entry empty?
619 665
     if (p->name[0] == DIR_NAME_FREE || p->name[0] == DIR_NAME_DELETED) {
620
-      // remember first empty slot
666
+      // Count the contiguous available entries in which (eventually) fit the new dir entry, if it's a write operation
621 667
       if (!emptyFound) {
622
-        dirBlock_ = dirFile->vol_->cacheBlockNumber();
623
-        dirIndex_ = index;
624
-        emptyFound = true;
668
+        #if ENABLED(LONG_FILENAME_WRITE_SUPPORT)
669
+          if (emptyCount == 0) emptyIndex = index;
670
+          // Incr empty entries counter
671
+          // If found the required empty entries, mark it
672
+          if (++emptyCount == reqEntriesNum) {
673
+            dirBlock_ = dirFile->vol_->cacheBlockNumber();
674
+            dirIndex_ = index & 0xF;
675
+            emptyFound = true;
676
+          }
677
+        #else
678
+          dirBlock_ = dirFile->vol_->cacheBlockNumber();
679
+          dirIndex_ = index;
680
+          emptyFound = true;
681
+        #endif
625 682
       }
626
-      // done if no entries follow
683
+      // Done if no entries follow
627 684
       if (p->name[0] == DIR_NAME_FREE) break;
628 685
     }
629
-    else if (!memcmp(dname, p->name, 11)) {
630
-      fileFound = true;
631
-      break;
686
+    else {  // Entry not empty
687
+      #if ENABLED(LONG_FILENAME_WRITE_SUPPORT)
688
+        // Reset empty counter
689
+        if (!emptyFound) emptyCount = 0;
690
+        // Search for SFN or LFN?
691
+        if (!useLFN) {
692
+          // Check using SFN: file found?
693
+          if (!memcmp(dname, p->name, 11)) {
694
+            fileFound = true;
695
+            break;
696
+          }
697
+        }
698
+        else {
699
+          // Check using LFN: LFN not found? continue search for LFN
700
+          if (!lfnFileFound) {
701
+            // Is this dir a LFN?
702
+            if (isDirLFN(p)) {
703
+              // Get VFat dir entry
704
+              pvFat = (vfat_t *) p;
705
+              // Get checksum from the last entry of the sequence
706
+              if (pvFat->sequenceNumber & 0x40) lfnChecksum = pvFat->checksum;
707
+              // Get LFN sequence number
708
+              lfnSequenceNumber = pvFat->sequenceNumber & 0x1F;
709
+              if WITHIN(lfnSequenceNumber, 1, reqEntriesNum) {
710
+                // Check checksum for all other entries with the starting checksum fetched before
711
+                if (lfnChecksum == pvFat->checksum) {
712
+                  // Set chunk of LFN from VFAT entry into lfnName
713
+                  getLFNName(pvFat, (char *)lfnName, lfnSequenceNumber);
714
+                  // LFN found?
715
+                  if (!strncasecmp((char*)dlname, (char*)lfnName, lfnNameLength)) lfnFileFound = true;
716
+                }
717
+              }
718
+            }
719
+          }
720
+          else {    // Complete LFN found, check for related SFN
721
+            // Check if only the SFN checksum match because the filename may be different due to different truncation methods
722
+            if (!isDirLFN(p) && (lfnChecksum == lfn_checksum(p->name))) {
723
+              fileFound = true;
724
+              break;
725
+            }
726
+            else lfnFileFound = false;    // SFN not valid for the LFN found, reset LFN FileFound
727
+          }
728
+        }
729
+      #else
730
+
731
+        if (!memcmp(dname, p->name, 11)) {
732
+          fileFound = true;
733
+          break;
734
+        }
735
+
736
+      #endif // LONG_FILENAME_WRITE_SUPPORT
632 737
     }
633 738
   }
739
+
634 740
   if (fileFound) {
635 741
     // don't open existing file if O_EXCL
636 742
     if (oflag & O_EXCL) return false;
743
+    TERN_(LONG_FILENAME_WRITE_SUPPORT, index &= 0xF);
637 744
   }
638 745
   else {
639 746
     // don't create unless O_CREAT and O_WRITE
640 747
     if ((oflag & (O_CREAT | O_WRITE)) != (O_CREAT | O_WRITE)) return false;
641
-    if (emptyFound) {
642
-      index = dirIndex_;
643
-      p = cacheDirEntry(SdVolume::CACHE_FOR_WRITE);
644
-      if (!p) return false;
645
-    }
646
-    else {
647
-      if (dirFile->type_ == FAT_FILE_TYPE_ROOT_FIXED) return false;
648 748
 
649
-      // add and zero cluster for dirFile - first cluster is in cache for write
650
-      if (!dirFile->addDirCluster()) return false;
749
+    #if ENABLED(LONG_FILENAME_WRITE_SUPPORT)
651 750
 
652
-      // use first entry in cluster
653
-      p = dirFile->vol_->cache()->dir;
654
-      index = 0;
655
-    }
656
-    // initialize as empty file
657
-    memset(p, 0, sizeof(*p));
658
-    memcpy(p->name, dname, 11);
751
+      // Use bookmark index if found empty entries
752
+      if (emptyFound) index = emptyIndex;
659 753
 
660
-    // set timestamps
661
-    if (dateTime_) {
662
-      // call user date/time function
663
-      dateTime_(&p->creationDate, &p->creationTime);
664
-    }
665
-    else {
666
-      // use default date/time
667
-      p->creationDate = FAT_DEFAULT_DATE;
668
-      p->creationTime = FAT_DEFAULT_TIME;
669
-    }
670
-    p->lastAccessDate = p->creationDate;
671
-    p->lastWriteDate = p->creationDate;
672
-    p->lastWriteTime = p->creationTime;
754
+      // Make room for needed entries
755
+      while (emptyCount < reqEntriesNum) {
756
+        p = dirFile->readDirCache();
757
+        if (!p) break;
758
+        emptyCount++;
759
+      }
760
+      while (emptyCount < reqEntriesNum) {
761
+        if (dirFile->type_ == FAT_FILE_TYPE_ROOT_FIXED) return false;
762
+        // add and zero cluster for dirFile - first cluster is in cache for write
763
+        if (!dirFile->addDirCluster()) return false;
764
+        emptyCount += dirFile->vol_->blocksPerCluster() * 16;
765
+      }
766
+
767
+      // Move to 1st entry to write
768
+      if (!dirFile->seekSet(32 * index)) return false;
769
+
770
+      // Dir entries write loop: [LFN] + SFN(1)
771
+      LOOP_L_N(dirWriteIdx, reqEntriesNum) {
772
+        index = (dirFile->curPosition_ / 32) & 0xF;
773
+        p = dirFile->readDirCache();
774
+        // LFN or SFN Entry?
775
+        if (dirWriteIdx < reqEntriesNum - 1) {
776
+          // Write LFN Entries
777
+          pvFat = (vfat_t *) p;
778
+          // initialize as empty file
779
+          memset(pvFat, 0, sizeof(*pvFat));
780
+          lfnSequenceNumber = (reqEntriesNum - dirWriteIdx - 1) & 0x1F;
781
+          pvFat->attributes = DIR_ATT_LONG_NAME;
782
+          pvFat->checksum = lfn_checksum(dname);
783
+          // Set sequence number and mark as last LFN entry if it's the 1st loop
784
+          pvFat->sequenceNumber = lfnSequenceNumber | (dirWriteIdx == 0 ? 0x40 : 0);
785
+          // Set LFN name block
786
+          setLFNName(pvFat, (char*)dlname, lfnSequenceNumber);
787
+        }
788
+        else {
789
+          // Write SFN Entry
790
+          // initialize as empty file
791
+          memset(p, 0, sizeof(*p));
792
+          memcpy(p->name, dname, 11);
793
+
794
+          // set timestamps
795
+          if (dateTime_) {
796
+            // call user date/time function
797
+            dateTime_(&p->creationDate, &p->creationTime);
798
+          }
799
+          else {
800
+            // use default date/time
801
+            p->creationDate = FAT_DEFAULT_DATE;
802
+            p->creationTime = FAT_DEFAULT_TIME;
803
+          }
804
+          p->lastAccessDate = p->creationDate;
805
+          p->lastWriteDate = p->creationDate;
806
+          p->lastWriteTime = p->creationTime;
807
+        }
808
+
809
+        // write entry to SD
810
+        dirFile->vol_->cacheSetDirty();
811
+        if (!dirFile->vol_->cacheFlush()) return false;
812
+      }
813
+
814
+    #else // !LONG_FILENAME_WRITE_SUPPORT
815
+
816
+      if (emptyFound) {
817
+        index = dirIndex_;
818
+        p = cacheDirEntry(SdVolume::CACHE_FOR_WRITE);
819
+        if (!p) return false;
820
+      }
821
+      else {
822
+        if (dirFile->type_ == FAT_FILE_TYPE_ROOT_FIXED) return false;
823
+
824
+        // add and zero cluster for dirFile - first cluster is in cache for write
825
+        if (!dirFile->addDirCluster()) return false;
826
+
827
+        // use first entry in cluster
828
+        p = dirFile->vol_->cache()->dir;
829
+        index = 0;
830
+      }
831
+
832
+      // initialize as empty file
833
+      memset(p, 0, sizeof(*p));
834
+      memcpy(p->name, dname, 11);
835
+
836
+      // set timestamps
837
+      if (dateTime_) {
838
+        // call user date/time function
839
+        dateTime_(&p->creationDate, &p->creationTime);
840
+      }
841
+      else {
842
+        // use default date/time
843
+        p->creationDate = FAT_DEFAULT_DATE;
844
+        p->creationTime = FAT_DEFAULT_TIME;
845
+      }
846
+
847
+      p->lastAccessDate = p->creationDate;
848
+      p->lastWriteDate = p->creationDate;
849
+      p->lastWriteTime = p->creationTime;
850
+
851
+      // write entry to SD
852
+      if (!dirFile->vol_->cacheFlush()) return false;
853
+
854
+    #endif // !LONG_FILENAME_WRITE_SUPPORT
673 855
 
674
-    // write entry to SD
675
-    if (!dirFile->vol_->cacheFlush()) return false;
676 856
   }
677 857
   // open entry in cache
678 858
   return openCachedEntry(index, oflag);
@@ -808,6 +988,191 @@ bool SdBaseFile::openNext(SdBaseFile *dirFile, uint8_t oflag) {
808 988
   return false;
809 989
 }
810 990
 
991
+#if ENABLED(LONG_FILENAME_WRITE_SUPPORT)
992
+
993
+  /**
994
+   * Check if dir is a long file name entry (LFN)
995
+   *
996
+   * \param[in] dir Parent of this directory will be opened.  Must not be root.
997
+   * \return true if the dir is a long file name entry (LFN)
998
+   */
999
+  bool SdBaseFile::isDirLFN(const dir_t* dir) {
1000
+    if (DIR_IS_LONG_NAME(dir)) {
1001
+      vfat_t *VFAT = (vfat_t*)dir;
1002
+      // Sanity-check the VFAT entry. The first cluster is always set to zero. And the sequence number should be higher than 0
1003
+      if ((VFAT->firstClusterLow == 0) && WITHIN((VFAT->sequenceNumber & 0x1F), 1, MAX_VFAT_ENTRIES)) return true;
1004
+    }
1005
+    return false;
1006
+  }
1007
+
1008
+  /**
1009
+   * Check if dirname string is a long file name (LFN)
1010
+   *
1011
+   * \param[in] dirname The string to check
1012
+   * \return true if the dirname is a long file name (LFN)
1013
+   * \return false if the dirname is a short file name 8.3 (SFN)
1014
+   */
1015
+  bool SdBaseFile::isDirNameLFN(const char *dirname) {
1016
+    uint8_t length = strlen(dirname);
1017
+    uint8_t idx = length;
1018
+    bool dotFound = false;
1019
+    if (idx > 12) return true;            // LFN due to filename length > 12 ("filename.ext")
1020
+    // Check dot(s) position
1021
+    while (idx) {
1022
+      if (dirname[--idx] == '.') {
1023
+        if (!dotFound) {
1024
+          // Last dot (extension) is allowed only
1025
+          // in position [1..8] from start or [0..3] from end for SFN else it's a LFN
1026
+          // A filename starting with "." is a LFN                (eg. ".file" ->in SFN-> "file~1     ")
1027
+          // A filename ending with "." is a SFN (if length <= 9) (eg. "file." ->in SFN-> "file       ")
1028
+          if (idx > 8 || idx == 0 || (length - idx - 1) > 3) return true;   // LFN due to dot extension position
1029
+          dotFound = true;
1030
+        }
1031
+        else {
1032
+          // Found another dot, is a LFN
1033
+          return true;
1034
+        }
1035
+      }
1036
+    }
1037
+    // If no dots found, the filename must be of max 8 characters
1038
+    if ((!dotFound) && length > 8) return true;         // LFN due to max filename (without extension) length
1039
+    return false;
1040
+  }
1041
+
1042
+  /**
1043
+   * Parse path and return 8.3 format and LFN filenames (if the parsed path is a LFN)
1044
+   * The SFN is without dot ("FILENAMEEXT")
1045
+   * The LFN is complete ("Filename.ext")
1046
+   */
1047
+  bool SdBaseFile::parsePath(const char *path, uint8_t *name, uint8_t *lname, const char **ptrNextPath) {
1048
+    // Init randomizer for SFN generation
1049
+    randomSeed(millis());
1050
+    // Parse the LFN
1051
+    uint8_t ilfn = 0;
1052
+    bool lastDotFound = false;
1053
+    const char *pLastDot = 0;
1054
+    const char *lfnpath = path;
1055
+    uint8_t c;
1056
+
1057
+    while (*lfnpath && *lfnpath != '/') {
1058
+      if (ilfn == LONG_FILENAME_LENGTH - 1) return false;                 // Name too long
1059
+      c = *lfnpath++;                                                     // Get char and advance
1060
+      // Fail for illegal characters
1061
+      PGM_P p = PSTR("|<>^+=?/[];:,*\"\\");
1062
+      while (uint8_t b = pgm_read_byte(p++)) if (b == c) return false;    // Check reserved characters
1063
+      if (c < 0x20 || c == 0x7F) return false;                            // Check non-printable characters
1064
+      if (c == '.' && (lfnpath - 1) > path) {                             // Skip dot '.' check in 1st position
1065
+        // Save last dot pointer (skip if starts with '.')
1066
+        pLastDot = lfnpath - 1;
1067
+        lastDotFound = true;
1068
+      }
1069
+      lname[ilfn++] = c;  // Set LFN character
1070
+    }
1071
+    // Terminate LFN
1072
+    lname[ilfn] = 0;
1073
+
1074
+    // Parse/generate 8.3 SFN. Will take
1075
+    // until 8 characters for the filename part
1076
+    // until 3 characters for the extension part (if exists)
1077
+    // Add 4 more characters if name part < 3
1078
+    // Add '~cnt' characters if it's a LFN
1079
+    const bool isLFN = isDirNameLFN((char*)lname);
1080
+
1081
+    uint8_t n = isLFN ? 5 : 7,  // Max index for each component of the file:
1082
+                                // starting with 7 or 5 (if LFN)
1083
+                                // switch to 10 for extension if the last dot is found
1084
+            i = 11;
1085
+    while (i) name[--i] = ' ';            // Set whole FILENAMEEXT to spaces
1086
+    while (*path && *path != '/') {
1087
+      c = *path++;                                                        // Get char and advance
1088
+      // Skip spaces and dots (if it's not the last dot)
1089
+      if (c == ' ') continue;
1090
+      if (c == '.' && (!lastDotFound || (lastDotFound && path < pLastDot))) continue;
1091
+      // Fail for illegal characters
1092
+      PGM_P p = PSTR("|<>^+=?/[];:,*\"\\");
1093
+      while (uint8_t b = pgm_read_byte(p++)) if (b == c) return false;    // Check reserved characters
1094
+      if (c < 0x21 || c == 0x7F) return false;                            // Check non-printable characters
1095
+      // Is last dot?
1096
+      if (c == '.') {
1097
+        // Switch to extension part
1098
+        n = 10;
1099
+        i = 8;
1100
+      }
1101
+      // If in valid range add the character
1102
+      else if (i <= n)                                          // Check size for 8.3 format
1103
+        name[i++] = c + (WITHIN(c, 'a', 'z') ? 'A' - 'a' : 0);  // Uppercase required for 8.3 name
1104
+    }
1105
+    // If it's a LFN then the SFN always need:
1106
+    // - A minimal of 3 characters (otherwise 4 chars are added)
1107
+    // - The '~cnt' at the end
1108
+    if (isLFN) {
1109
+      // Get the 1st free character
1110
+      uint8_t iFree = 0;
1111
+      while (1) if (name[iFree++] == ' ' || iFree == 11) break;
1112
+      iFree--;
1113
+      // Check minimal length
1114
+      if (iFree < 3) {
1115
+        // Append 4 extra characters
1116
+        name[iFree++] = random(0,24) + 'A'; name[iFree++] = random(0,24) + 'A';
1117
+        name[iFree++] = random(0,24) + 'A'; name[iFree++] = random(0,24) + 'A';
1118
+      }
1119
+      // Append '~cnt' characters
1120
+      if (iFree > 5) iFree = 5; // Force the append in the last 3 characters of name part
1121
+      name[iFree++] = '~';
1122
+      name[iFree++] = random(1,9) + '0';
1123
+      name[iFree++] = random(1,9) + '0';
1124
+    }
1125
+
1126
+    // Check if LFN is needed
1127
+    if (!isLFN) lname[0] = 0;   // Zero LFN
1128
+    *ptrNextPath = path;        // Set passed pointer to the end
1129
+    return name[0] != ' ';      // Return true if any name was set
1130
+  }
1131
+
1132
+  /**
1133
+   * Get the LFN filename block from a dir. Get the block in lname at startOffset
1134
+   */
1135
+  void SdBaseFile::getLFNName(vfat_t *pFatDir, char *lname, uint8_t sequenceNumber) {
1136
+    uint8_t startOffset = (sequenceNumber - 1) * FILENAME_LENGTH;
1137
+    LOOP_L_N(i, FILENAME_LENGTH) {
1138
+      const uint16_t utf16_ch = (i >= 11) ? pFatDir->name3[i - 11] : (i >= 5) ? pFatDir->name2[i - 5] : pFatDir->name1[i];
1139
+      #if ENABLED(UTF_FILENAME_SUPPORT)
1140
+        // We can't reconvert to UTF-8 here as UTF-8 is variable-size encoding, but joining LFN blocks
1141
+        // needs static bytes addressing. So here just store full UTF-16LE words to re-convert later.
1142
+        uint16_t idx = (startOffset + i) * 2; // This is fixed as FAT LFN always contain UTF-16LE encoding
1143
+        longFilename[idx] = utf16_ch & 0xFF;
1144
+        longFilename[idx + 1] = (utf16_ch >> 8) & 0xFF;
1145
+      #else
1146
+        // Replace all multibyte characters to '_'
1147
+        lname[startOffset + i] = (utf16_ch > 0xFF) ? '_' : (utf16_ch & 0xFF);
1148
+      #endif
1149
+    }
1150
+  }
1151
+
1152
+  /**
1153
+   * Set the LFN filename block lname to a dir. Put the block based on sequence number
1154
+   */
1155
+  void SdBaseFile::setLFNName(vfat_t *pFatDir, char *lname, uint8_t sequenceNumber) {
1156
+    uint8_t startOffset = (sequenceNumber - 1) * FILENAME_LENGTH;
1157
+    uint8_t nameLength = strlen(lname);
1158
+    LOOP_L_N(i, FILENAME_LENGTH) {
1159
+      uint16_t ch = 0;
1160
+      if ((startOffset + i) < nameLength)
1161
+        ch = lname[startOffset + i];
1162
+      else if ((startOffset + i) > nameLength)
1163
+        ch = 0xFFFF;
1164
+      // Set char
1165
+      if (i < 5)
1166
+        pFatDir->name1[i] = ch;
1167
+      else if (i < 11)
1168
+        pFatDir->name2[i - 5] = ch;
1169
+      else
1170
+        pFatDir->name3[i - 11] = ch;
1171
+    }
1172
+  }
1173
+
1174
+#endif // LONG_FILENAME_WRITE_SUPPORT
1175
+
811 1176
 #if 0
812 1177
 /**
813 1178
  * Open a directory's parent directory.
@@ -1050,20 +1415,6 @@ int16_t SdBaseFile::read(void *buf, uint16_t nbyte) {
1050 1415
 }
1051 1416
 
1052 1417
 /**
1053
- * Calculate a checksum for an 8.3 filename
1054
- *
1055
- * \param name The 8.3 file name to calculate
1056
- *
1057
- * \return The checksum byte
1058
- */
1059
-uint8_t lfn_checksum(const uint8_t *name) {
1060
-  uint8_t sum = 0;
1061
-  for (uint8_t i = 11; i; i--)
1062
-    sum = ((sum & 1) << 7) + (sum >> 1) + *name++;
1063
-  return sum;
1064
-}
1065
-
1066
-/**
1067 1418
  * Read the next entry in a directory.
1068 1419
  *
1069 1420
  * \param[out] dir The dir_t struct that will receive the data.
@@ -1110,30 +1461,40 @@ int8_t SdBaseFile::readDir(dir_t *dir, char *longFilename) {
1110 1461
         if (VFAT->firstClusterLow == 0) {
1111 1462
           const uint8_t seq = VFAT->sequenceNumber & 0x1F;
1112 1463
           if (WITHIN(seq, 1, MAX_VFAT_ENTRIES)) {
1113
-            n = (seq - 1) * (FILENAME_LENGTH);
1114
-            if (n == 0) {
1464
+            if (seq == 1) {
1115 1465
               checksum = VFAT->checksum;
1116 1466
               checksum_error = 0;
1117 1467
             }
1118 1468
             else if (checksum != VFAT->checksum) // orphan detected
1119 1469
               checksum_error = 1;
1120 1470
 
1121
-            LOOP_L_N(i, FILENAME_LENGTH) {
1122
-              const uint16_t utf16_ch = (i >= 11) ? VFAT->name3[i - 11] : (i >= 5) ? VFAT->name2[i - 5] : VFAT->name1[i];
1123
-              #if ENABLED(UTF_FILENAME_SUPPORT)
1124
-                // We can't reconvert to UTF-8 here as UTF-8 is variable-size encoding, but joining LFN blocks
1125
-                // needs static bytes addressing. So here just store full UTF-16LE words to re-convert later.
1126
-                uint16_t idx = (n + i) * 2; // This is fixed as FAT LFN always contain UTF-16LE encoding
1127
-                longFilename[idx] = utf16_ch & 0xFF;
1128
-                longFilename[idx + 1] = (utf16_ch >> 8) & 0xFF;
1129
-              #else
1130
-                // Replace all multibyte characters to '_'
1131
-                longFilename[n + i] = (utf16_ch > 0xFF) ? '_' : (utf16_ch & 0xFF);
1132
-              #endif
1133
-            }
1471
+            #if ENABLED(LONG_FILENAME_WRITE_SUPPORT)
1472
+
1473
+              getLFNName(VFAT, longFilename, seq);  // Get chunk of LFN from VFAT entry
1474
+
1475
+            #else // !LONG_FILENAME_WRITE_SUPPORT
1476
+
1477
+              n = (seq - 1) * (FILENAME_LENGTH);
1478
+
1479
+              LOOP_L_N(i, FILENAME_LENGTH) {
1480
+                const uint16_t utf16_ch = (i >= 11) ? VFAT->name3[i - 11] : (i >= 5) ? VFAT->name2[i - 5] : VFAT->name1[i];
1481
+                #if ENABLED(UTF_FILENAME_SUPPORT)
1482
+                  // We can't reconvert to UTF-8 here as UTF-8 is variable-size encoding, but joining LFN blocks
1483
+                  // needs static bytes addressing. So here just store full UTF-16LE words to re-convert later.
1484
+                  uint16_t idx = (n + i) * 2; // This is fixed as FAT LFN always contain UTF-16LE encoding
1485
+                  longFilename[idx] = utf16_ch & 0xFF;
1486
+                  longFilename[idx + 1] = (utf16_ch >> 8) & 0xFF;
1487
+                #else
1488
+                  // Replace all multibyte characters to '_'
1489
+                  longFilename[n + i] = (utf16_ch > 0xFF) ? '_' : (utf16_ch & 0xFF);
1490
+                #endif
1491
+              }
1492
+
1493
+            #endif // !LONG_FILENAME_WRITE_SUPPORT
1494
+
1134 1495
             // If this VFAT entry is the last one, add a NUL terminator at the end of the string
1135 1496
             if (VFAT->sequenceNumber & 0x40)
1136
-                longFilename[(n + FILENAME_LENGTH) * LONG_FILENAME_CHARSIZE] = '\0';
1497
+              longFilename[LONG_FILENAME_CHARSIZE * TERN(LONG_FILENAME_WRITE_SUPPORT, seq * FILENAME_LENGTH, (n + FILENAME_LENGTH))] = '\0';
1137 1498
           }
1138 1499
         }
1139 1500
       }
@@ -1227,6 +1588,11 @@ bool SdBaseFile::remove() {
1227 1588
   dir_t *d = cacheDirEntry(SdVolume::CACHE_FOR_WRITE);
1228 1589
   if (!d) return false;
1229 1590
 
1591
+  #if ENABLED(LONG_FILENAME_WRITE_SUPPORT)
1592
+    // get SFN checksum before name rewrite (needed for LFN deletion)
1593
+    const uint8_t sfn_checksum = lfn_checksum(d->name);
1594
+  #endif
1595
+
1230 1596
   // mark entry deleted
1231 1597
   d->name[0] = DIR_NAME_DELETED;
1232 1598
 
@@ -1234,8 +1600,48 @@ bool SdBaseFile::remove() {
1234 1600
   type_ = FAT_FILE_TYPE_CLOSED;
1235 1601
 
1236 1602
   // write entry to SD
1237
-  return vol_->cacheFlush();
1238
-  return true;
1603
+  #if DISABLED(LONG_FILENAME_WRITE_SUPPORT)
1604
+
1605
+    return vol_->cacheFlush();
1606
+
1607
+  #else // LONG_FILENAME_WRITE_SUPPORT
1608
+
1609
+    flags_ = 0;
1610
+
1611
+    if (!vol_->cacheFlush()) return false;
1612
+
1613
+    // Check if the entry has a LFN
1614
+    bool lastEntry = false;
1615
+    // loop back to search for any LFN entries related to this file
1616
+    LOOP_S_LE_N(sequenceNumber, 1, MAX_VFAT_ENTRIES) {
1617
+      dirIndex_ = (dirIndex_ - 1) & 0xF;
1618
+      if (dirBlock_ == 0) break;
1619
+      if (dirIndex_ == 0xF) dirBlock_--;
1620
+      dir_t *dir = cacheDirEntry(SdVolume::CACHE_FOR_WRITE);
1621
+      if (!dir) return false;
1622
+
1623
+      // check for valid LFN: not deleted, not top dirs (".", ".."), must be a LFN
1624
+      if (dir->name[0] == DIR_NAME_DELETED || dir->name[0] == '.' || !isDirLFN(dir)) break;
1625
+      // check coherent LFN: checksum and sequenceNumber must match
1626
+      vfat_t* dirlfn = (vfat_t*) dir;
1627
+      if (dirlfn->checksum != sfn_checksum || (dirlfn->sequenceNumber & 0x1F) != sequenceNumber) break;    // orphan entry
1628
+      // is last entry of LFN ?
1629
+      lastEntry = (dirlfn->sequenceNumber & 0x40);
1630
+      // mark as deleted
1631
+      dirlfn->sequenceNumber = DIR_NAME_DELETED;
1632
+      // Flush to SD
1633
+      if (!vol_->cacheFlush()) return false;
1634
+      // exit on last entry of LFN deleted
1635
+      if (lastEntry) break;
1636
+    }
1637
+
1638
+    // Restore current index
1639
+    //if (!seekSet(32UL * dirIndex_)) return false;
1640
+    //dirIndex_ += prevDirIndex;
1641
+
1642
+    return true;
1643
+
1644
+  #endif // LONG_FILENAME_WRITE_SUPPORT
1239 1645
 }
1240 1646
 
1241 1647
 /**

+ 20
- 2
Marlin/src/sd/SdBaseFile.h View File

@@ -377,8 +377,26 @@ class SdBaseFile {
377 377
   dir_t* cacheDirEntry(uint8_t action);
378 378
   int8_t lsPrintNext(uint8_t flags, uint8_t indent);
379 379
   static bool make83Name(const char *str, uint8_t *name, const char **ptr);
380
-  bool mkdir(SdBaseFile *parent, const uint8_t dname[11]);
381
-  bool open(SdBaseFile *dirFile, const uint8_t dname[11], uint8_t oflag);
380
+  bool mkdir(SdBaseFile *parent, const uint8_t dname[11]
381
+    OPTARG(LONG_FILENAME_WRITE_SUPPORT, const uint8_t dlname[LONG_FILENAME_LENGTH])
382
+  );
383
+  bool open(SdBaseFile *dirFile, const uint8_t dname[11]
384
+      OPTARG(LONG_FILENAME_WRITE_SUPPORT, const uint8_t dlname[LONG_FILENAME_LENGTH])
385
+    , uint8_t oflag
386
+  );
382 387
   bool openCachedEntry(uint8_t cacheIndex, uint8_t oflags);
383 388
   dir_t* readDirCache();
389
+
390
+  // Long Filename create/write support
391
+  #if ENABLED(LONG_FILENAME_WRITE_SUPPORT)
392
+    static bool isDirLFN(const dir_t* dir);
393
+    static bool isDirNameLFN(const char *dirname);
394
+    static bool parsePath(const char *str, uint8_t *name, uint8_t *lname, const char **ptr);
395
+    /**
396
+     * Return the number of entries needed in the FAT for this LFN
397
+     */
398
+    static inline uint8_t getLFNEntriesNum(const char *lname) { return (strlen(lname) + 12) / 13; }
399
+    static void getLFNName(vfat_t *vFatDir, char *lname, uint8_t startOffset);
400
+    static void setLFNName(vfat_t *vFatDir, char *lname, uint8_t lfnSequenceNumber);
401
+  #endif
384 402
 };

+ 3
- 3
Marlin/src/sd/cardreader.cpp View File

@@ -328,7 +328,7 @@ void CardReader::printListing(
328 328
         if (includeLongNames) {
329 329
           SERIAL_CHAR(' ');
330 330
           if (prependLong) { SERIAL_ECHO(prependLong); SERIAL_CHAR('/'); }
331
-          SERIAL_ECHO(longFilename[0] ? longFilename : "???");
331
+          SERIAL_ECHO(longFilename[0] ? longFilename : filename);
332 332
         }
333 333
       #endif
334 334
       SERIAL_EOL();
@@ -385,9 +385,9 @@ void CardReader::ls(
385 385
       diveDir.rewind();
386 386
       selectByName(diveDir, segment);
387 387
 
388
-      // Print /LongNamePart to serial output
388
+      // Print /LongNamePart to serial output or the short name if not available
389 389
       SERIAL_CHAR('/');
390
-      SERIAL_ECHO(longFilename[0] ? longFilename : "???");
390
+      SERIAL_ECHO(longFilename[0] ? longFilename : filename);
391 391
 
392 392
       // If the filename was printed then that's it
393 393
       if (!flag.filenameIsDir) break;

+ 45
- 33
buildroot/share/scripts/upload.py View File

@@ -84,21 +84,26 @@ def Upload(source, target, env):
84 84
     #----------------#
85 85
     # File functions #
86 86
     #----------------#
87
-    def _GetFirmwareFiles():
87
+    def _GetFirmwareFiles(UseLongFilenames):
88 88
         if Debug: print('Get firmware files...')
89
-        _Send('M20 F')
89
+        _Send(f"M20 F{'L' if UseLongFilenames else ''}")
90 90
         Responses = _Recv()
91 91
         if len(Responses) < 3 or not any('file list' in r for r in Responses):
92 92
             raise Exception('Error getting firmware files')
93 93
         if Debug: print('OK')
94 94
         return Responses
95 95
 
96
-    def _FilterFirmwareFiles(FirmwareList):
96
+    def _FilterFirmwareFiles(FirmwareList, UseLongFilenames):
97 97
         Firmwares = []
98 98
         for FWFile in FirmwareList:
99
-            if not '/' in FWFile and '.BIN' in FWFile:
100
-                idx = FWFile.index('.BIN')
101
-                Firmwares.append(FWFile[:idx+4])
99
+            # For long filenames take the 3rd column of the firmwares list
100
+            if UseLongFilenames:
101
+                Space = 0
102
+                Space = FWFile.find(' ')
103
+                if Space >= 0: Space = FWFile.find(' ', Space + 1)
104
+                if Space >= 0: FWFile = FWFile[Space + 1:]
105
+            if not '/' in FWFile and '.BIN' in FWFile.upper():
106
+                Firmwares.append(FWFile[:FWFile.upper().index('.BIN') + 4])
102 107
         return Firmwares
103 108
 
104 109
     def _RemoveFirmwareFile(FirmwareFile):
@@ -124,6 +129,8 @@ def Upload(source, target, env):
124 129
     marlin_board_info_name = _GetMarlinEnv(MarlinEnv, 'BOARD_INFO_NAME')
125 130
     marlin_board_custom_build_flags = _GetMarlinEnv(MarlinEnv, 'BOARD_CUSTOM_BUILD_FLAGS')
126 131
     marlin_firmware_bin = _GetMarlinEnv(MarlinEnv, 'FIRMWARE_BIN')
132
+    marlin_long_filename_host_support = _GetMarlinEnv(MarlinEnv, 'LONG_FILENAME_HOST_SUPPORT') is not None
133
+    marlin_longname_write = _GetMarlinEnv(MarlinEnv, 'LONG_FILENAME_WRITE_SUPPORT') is not None
127 134
     marlin_custom_firmware_upload = _GetMarlinEnv(MarlinEnv, 'CUSTOM_FIRMWARE_UPLOAD') is not None
128 135
     marlin_short_build_version = _GetMarlinEnv(MarlinEnv, 'SHORT_BUILD_VERSION')
129 136
     marlin_string_config_h_author = _GetMarlinEnv(MarlinEnv, 'STRING_CONFIG_H_AUTHOR')
@@ -148,6 +155,10 @@ def Upload(source, target, env):
148 155
     # "upload_delete_old_bins": delete all *.bin files in the root of SD Card
149 156
     upload_delete_old_bins = marlin_motherboard in ['BOARD_CREALITY_V4',   'BOARD_CREALITY_V4210', 'BOARD_CREALITY_V423', 'BOARD_CREALITY_V427',
150 157
                                                     'BOARD_CREALITY_V431', 'BOARD_CREALITY_V452',  'BOARD_CREALITY_V453', 'BOARD_CREALITY_V24S1']
158
+    # "upload_random_name": generate a random 8.3 firmware filename to upload
159
+    upload_random_filename = marlin_motherboard in ['BOARD_CREALITY_V4',   'BOARD_CREALITY_V4210', 'BOARD_CREALITY_V423', 'BOARD_CREALITY_V427',
160
+                                                    'BOARD_CREALITY_V431', 'BOARD_CREALITY_V452',  'BOARD_CREALITY_V453', 'BOARD_CREALITY_V24S1'] and not marlin_long_filename_host_support
161
+
151 162
     try:
152 163
 
153 164
         # Start upload job
@@ -156,28 +167,34 @@ def Upload(source, target, env):
156 167
         # Dump some debug info
157 168
         if Debug:
158 169
             print('Upload using:')
159
-            print('---- Marlin --------------------')
160
-            print(f' PIOENV                 : {marlin_pioenv}')
161
-            print(f' SHORT_BUILD_VERSION    : {marlin_short_build_version}')
162
-            print(f' STRING_CONFIG_H_AUTHOR : {marlin_string_config_h_author}')
163
-            print(f' MOTHERBOARD            : {marlin_motherboard}')
164
-            print(f' BOARD_INFO_NAME        : {marlin_board_info_name}')
165
-            print(f' CUSTOM_BUILD_FLAGS     : {marlin_board_custom_build_flags}')
166
-            print(f' FIRMWARE_BIN           : {marlin_firmware_bin}')
167
-            print(f' CUSTOM_FIRMWARE_UPLOAD : {marlin_custom_firmware_upload}')
168
-            print('---- Upload parameters ---------')
169
-            print(f' Source      : {upload_firmware_source_name}')
170
-            print(f' Target      : {upload_firmware_target_name}')
171
-            print(f' Port        : {upload_port} @ {upload_speed} baudrate')
172
-            print(f' Timeout     : {upload_timeout}')
173
-            print(f' Block size  : {upload_blocksize}')
174
-            print(f' Compression : {upload_compression}')
175
-            print(f' Error ratio : {upload_error_ratio}')
176
-            print(f' Test        : {upload_test}')
177
-            print(f' Reset       : {upload_reset}')
178
-            print('--------------------------------')
170
+            print('---- Marlin -----------------------------------')
171
+            print(f' PIOENV                      : {marlin_pioenv}')
172
+            print(f' SHORT_BUILD_VERSION         : {marlin_short_build_version}')
173
+            print(f' STRING_CONFIG_H_AUTHOR      : {marlin_string_config_h_author}')
174
+            print(f' MOTHERBOARD                 : {marlin_motherboard}')
175
+            print(f' BOARD_INFO_NAME             : {marlin_board_info_name}')
176
+            print(f' CUSTOM_BUILD_FLAGS          : {marlin_board_custom_build_flags}')
177
+            print(f' FIRMWARE_BIN                : {marlin_firmware_bin}')
178
+            print(f' LONG_FILENAME_HOST_SUPPORT  : {marlin_long_filename_host_support}')
179
+            print(f' LONG_FILENAME_WRITE_SUPPORT : {marlin_longname_write}')
180
+            print(f' CUSTOM_FIRMWARE_UPLOAD      : {marlin_custom_firmware_upload}')
181
+            print('---- Upload parameters ------------------------')
182
+            print(f' Source                      : {upload_firmware_source_name}')
183
+            print(f' Target                      : {upload_firmware_target_name}')
184
+            print(f' Port                        : {upload_port} @ {upload_speed} baudrate')
185
+            print(f' Timeout                     : {upload_timeout}')
186
+            print(f' Block size                  : {upload_blocksize}')
187
+            print(f' Compression                 : {upload_compression}')
188
+            print(f' Error ratio                 : {upload_error_ratio}')
189
+            print(f' Test                        : {upload_test}')
190
+            print(f' Reset                       : {upload_reset}')
191
+            print('-----------------------------------------------')
179 192
 
180 193
         # Custom implementations based on board parameters
194
+        # Generate a new 8.3 random filename
195
+        if upload_random_filename:
196
+            upload_firmware_target_name = f"fw-{''.join(random.choices('ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789', k=5))}.BIN"
197
+            print(f"Board {marlin_motherboard}: Overriding firmware filename to '{upload_firmware_target_name}'")
181 198
 
182 199
         # Delete all *.bin files on the root of SD Card (if flagged)
183 200
         if upload_delete_old_bins:
@@ -185,11 +202,6 @@ def Upload(source, target, env):
185 202
             if not marlin_custom_firmware_upload:
186 203
                 raise Exception(f"CUSTOM_FIRMWARE_UPLOAD must be enabled in 'Configuration_adv.h' for '{marlin_motherboard}'")
187 204
 
188
-            # Generate a new 8.3 random filename
189
-            # This board remember the last firmware filename and doesn't allow to flash from that filename
190
-            upload_firmware_target_name = f"fw-{''.join(random.choices('ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789', k=5))}.BIN"
191
-            print(f"Board {marlin_motherboard}: Overriding firmware filename to '{upload_firmware_target_name}'")
192
-
193 205
             # Init serial port
194 206
             port = serial.Serial(upload_port, baudrate = upload_speed, write_timeout = 0, timeout = 0.1)
195 207
             port.reset_input_buffer()
@@ -198,13 +210,13 @@ def Upload(source, target, env):
198 210
             _CheckSDCard()
199 211
 
200 212
             # Get firmware files
201
-            FirmwareFiles = _GetFirmwareFiles()
213
+            FirmwareFiles = _GetFirmwareFiles(marlin_long_filename_host_support)
202 214
             if Debug:
203 215
                 for FirmwareFile in FirmwareFiles:
204 216
                     print(f'Found: {FirmwareFile}')
205 217
 
206 218
             # Get all 1st level firmware files (to remove)
207
-            OldFirmwareFiles = _FilterFirmwareFiles(FirmwareFiles[1:len(FirmwareFiles)-2])   # Skip header and footers of list
219
+            OldFirmwareFiles = _FilterFirmwareFiles(FirmwareFiles[1:len(FirmwareFiles)-2], marlin_long_filename_host_support)   # Skip header and footers of list
208 220
             if len(OldFirmwareFiles) == 0:
209 221
                 print('No old firmware files to delete')
210 222
             else:

+ 1
- 1
buildroot/tests/mega2560 View File

@@ -35,7 +35,7 @@ opt_set MOTHERBOARD BOARD_AZTEEG_X3_PRO LCD_LANGUAGE jp_kana DEFAULT_EJERK 10 \
35 35
         EXTRUDERS 5 TEMP_SENSOR_1 1 TEMP_SENSOR_2 5 TEMP_SENSOR_3 20 TEMP_SENSOR_4 1000 TEMP_SENSOR_BED 1
36 36
 opt_enable REPRAP_DISCOUNT_FULL_GRAPHIC_SMART_CONTROLLER LIGHTWEIGHT_UI SHOW_CUSTOM_BOOTSCREEN BOOT_MARLIN_LOGO_SMALL \
37 37
            LCD_SET_PROGRESS_MANUALLY PRINT_PROGRESS_SHOW_DECIMALS SHOW_REMAINING_TIME STATUS_MESSAGE_SCROLLING SCROLL_LONG_FILENAMES \
38
-           SDSUPPORT SDCARD_SORT_ALPHA NO_SD_AUTOSTART USB_FLASH_DRIVE_SUPPORT CANCEL_OBJECTS \
38
+           SDSUPPORT LONG_FILENAME_WRITE_SUPPORT SDCARD_SORT_ALPHA NO_SD_AUTOSTART USB_FLASH_DRIVE_SUPPORT CANCEL_OBJECTS \
39 39
            Z_PROBE_SLED AUTO_BED_LEVELING_UBL UBL_HILBERT_CURVE RESTORE_LEVELING_AFTER_G28 DEBUG_LEVELING_FEATURE G26_MESH_VALIDATION ENABLE_LEVELING_FADE_HEIGHT \
40 40
            EEPROM_SETTINGS EEPROM_CHITCHAT GCODE_MACROS CUSTOM_MENU_MAIN \
41 41
            MULTI_NOZZLE_DUPLICATION CLASSIC_JERK LIN_ADVANCE QUICK_HOME \

Loading…
Cancel
Save