diff --git a/sources/Adapters/adv/filesystem/advFileSystem.cpp b/sources/Adapters/adv/filesystem/advFileSystem.cpp index 8179c4628..5997454a3 100644 --- a/sources/Adapters/adv/filesystem/advFileSystem.cpp +++ b/sources/Adapters/adv/filesystem/advFileSystem.cpp @@ -133,7 +133,7 @@ PicoFileType advFileSystem::getFileType(int index) { } void advFileSystem::list(etl::ivector *fileIndexes, const char *filter, - bool subDirOnly, bool includeHidden) { + bool subDirOnly, bool includeHidden, bool sorted) { fileIndexes->clear(); diff --git a/sources/Adapters/adv/filesystem/advFileSystem.h b/sources/Adapters/adv/filesystem/advFileSystem.h index 460249371..78aed8ad8 100644 --- a/sources/Adapters/adv/filesystem/advFileSystem.h +++ b/sources/Adapters/adv/filesystem/advFileSystem.h @@ -51,7 +51,8 @@ class advFileSystem : public FileSystem { virtual FileHandle Open(const char *name, const char *mode) override; virtual bool chdir(const char *path) override; virtual void list(etl::ivector *fileIndexes, const char *filter, - bool subDirOnly, bool includeHidden = false) override; + bool subDirOnly, bool includeHidden = false, + bool sorted = false) override; virtual void getFileName(int index, char *name, int length) override; virtual PicoFileType getFileType(int index) override; virtual bool isParentRoot() override; diff --git a/sources/Adapters/adv/gui/SerialDebugUI.cpp b/sources/Adapters/adv/gui/SerialDebugUI.cpp index 113673d37..0de21ee0c 100644 --- a/sources/Adapters/adv/gui/SerialDebugUI.cpp +++ b/sources/Adapters/adv/gui/SerialDebugUI.cpp @@ -199,7 +199,7 @@ void SerialDebugUI::listFiles(const char *path) { Trace::Error("failed to ls files path:%s", path); } etl::vector fileIndexes; - fs->list(&fileIndexes, "", false); + fs->list(&fileIndexes, "", false, true, true); // No need to actually do the printing below for now as the current debug code // in PicoFileSystem class is already printing all the files fetched when the diff --git a/sources/Adapters/picoTracker/filesystem/picoTrackerFileSystem.cpp b/sources/Adapters/picoTracker/filesystem/picoTrackerFileSystem.cpp index fceebd5ee..d85bdab20 100644 --- a/sources/Adapters/picoTracker/filesystem/picoTrackerFileSystem.cpp +++ b/sources/Adapters/picoTracker/filesystem/picoTrackerFileSystem.cpp @@ -7,7 +7,9 @@ */ #include "picoTrackerFileSystem.h" +#include "Application/Persistency/PersistenceConstants.h" #include "Externals/etl/include/etl/pool.h" +#include "Foundation/Services/MemoryService.h" #include "pico/multicore.h" #include @@ -18,6 +20,38 @@ constexpr uint32_t MAX_OPEN_FILES = 10; static etl::pool filePool; +// compile-time generated map to change uppercase letters to lowercase for +// sorting, leave all other characters unchanged. could be used to also change +// the sort order for other/special characters if needed +static constexpr uint8_t getCharacterSortRank(uint8_t value) { + return (value >= 'A' && value <= 'Z') ? 'a' + (value - 'A') : value; +} + +template +constexpr std::array +MakeSortMap(std::index_sequence) { + return {getCharacterSortRank(static_cast(Indexes))...}; +} + +static constexpr auto kSortMap = MakeSortMap(std::make_index_sequence<256>{}); + +// extracts a uint32 from the first four letters of a string for faster sorting +// by comparing these uint32 values instead of doing string compares +static uint32_t getFileSortKey(char *filename) { + uint32_t sortKey = 0; + uint8_t *value = (uint8_t *)filename; + + for (size_t i = 0; i < 4; i++) { + sortKey <<= 8; + if (*value != '\0') { + sortKey |= kSortMap[*value]; + value++; + } + } + + return sortKey; +} + picoTrackerFileSystem::picoTrackerFileSystem() { // init out access mutex std::lock_guard lock(mutex); @@ -121,9 +155,10 @@ PicoFileType picoTrackerFileSystem::getFileType(int index) { void picoTrackerFileSystem::list(etl::ivector *fileIndexes, const char *filter, bool subDirOnly, - bool includeHidden) { - std::lock_guard lock(mutex); + bool includeHidden, bool sorted) { + std::unique_lock lock(mutex); + uint32_t sortKeys[MAX_FILE_INDEX_SIZE]; fileIndexes->clear(); File cwd; @@ -158,22 +193,65 @@ void picoTrackerFileSystem::list(etl::ivector *fileIndexes, } // filter out "." and files that dont match filter if a filter is given if ((entry.isDirectory() && entry.dirIndex() != 0) || - ((includeHidden || !entry.isHidden()) && matchesFilter)) { - if (subDirOnly) { - if (entry.isDirectory()) { - fileIndexes->push_back(index); - } + (!entry.isHidden() && matchesFilter)) { + bool add = false; + + if (subDirOnly && entry.isDirectory()) { + add = entry.isDirectory(); } else { + add = true; + } + + if (add) { fileIndexes->push_back(index); + entry.getName(buffer, PFILENAME_SIZE); + sortKeys[fileIndexes->size() - 1] = getFileSortKey(buffer); + count++; } // Trace::Log("FILESYSTEM", "[%d] got file: %s", index, buffer); - count++; } else { // Trace::Log("FILESYSTEM", "skipped hidden: %s", buffer); } entry.close(); } + cwd.close(); + lock.unlock(); + + // sort using insertion sort (with an extra step if our keys are identical) + constexpr int total = MAX_PROJECT_NAME_LENGTH + 1; + char currentName[total]; + char prevName[total]; + + for (size_t i = 1; i < count; i++) { + uint32_t key = sortKeys[i]; + int index = fileIndexes->at(i); + size_t j = i; + + while (j > 0) { + bool shouldMove = sortKeys[j - 1] > key; + + if (!shouldMove && sortKeys[j - 1] == key) { + // keys are identical, do a tiebreaker by doing a string compare to + // ensure stable sorting of files with same first 4 letters + getFileName(index, currentName, total); + getFileName(fileIndexes->at(j - 1), prevName, total); + shouldMove = strcasecmp(prevName, currentName) > 0; + } + + if (shouldMove) { + sortKeys[j] = sortKeys[j - 1]; + fileIndexes->at(j) = fileIndexes->at(j - 1); + --j; + } else { + break; + } + } + + sortKeys[j] = key; + fileIndexes->at(j) = index; + } + Trace::Log("FILESYSTEM", "scanned: %d, added file indexes:%d", count, fileIndexes->size()); } @@ -277,21 +355,24 @@ bool picoTrackerFileSystem::CopyFile(const char *srcFilename, auto fSrc = sd.open(srcFilename, O_READ); auto fDest = sd.open(destFilename, O_WRITE | O_CREAT); - int n = 0; - int bufferSize = sizeof(fileBuffer_); + unsigned int n = 0; + const int bufSize = 1024; + char buffer[bufSize]; + while (true) { - n = fSrc.read(fileBuffer_, bufferSize); + n = fSrc.read(buffer, bufSize); // check for read error and only write if no error if (n >= 0) { - fDest.write(fileBuffer_, n); + fDest.write(buffer, n); } else { Trace::Error("Failed to read file: %s", srcFilename); return false; } - if (n < bufferSize) { + if (n < bufSize) { break; } } + fSrc.close(); fDest.close(); return true; diff --git a/sources/Adapters/picoTracker/filesystem/picoTrackerFileSystem.h b/sources/Adapters/picoTracker/filesystem/picoTrackerFileSystem.h index e17a02974..384d952dd 100644 --- a/sources/Adapters/picoTracker/filesystem/picoTrackerFileSystem.h +++ b/sources/Adapters/picoTracker/filesystem/picoTrackerFileSystem.h @@ -31,7 +31,8 @@ class picoTrackerFileSystem : public FileSystem { virtual FileHandle Open(const char *name, const char *mode) override; virtual bool chdir(const char *path) override; virtual void list(etl::ivector *fileIndexes, const char *filter, - bool subDirOnly, bool includeHidden = false) override; + bool subDirOnly, bool includeHidden = false, + bool sorted = false) override; virtual void getFileName(int index, char *name, int length) override; virtual PicoFileType getFileType(int index) override; virtual bool isParentRoot() override; @@ -50,9 +51,6 @@ class picoTrackerFileSystem : public FileSystem { private: SdFs sd; void tolowercase(char *temp); - // buffer needs to be allocated here as too big for allocation as local - // variable on the stack - uint8_t fileBuffer_[512]; }; // Concrete implementation of PI_File for picoTracker diff --git a/sources/Adapters/picoTracker/gui/SerialDebugUI.cpp b/sources/Adapters/picoTracker/gui/SerialDebugUI.cpp index ef7e869a2..b3ab8852a 100644 --- a/sources/Adapters/picoTracker/gui/SerialDebugUI.cpp +++ b/sources/Adapters/picoTracker/gui/SerialDebugUI.cpp @@ -8,6 +8,7 @@ #include "SerialDebugUI.h" #include "Application/Model/Config.h" +#include "Foundation/Services/MemoryService.h" #include "System/FileSystem/FileSystem.h" #include "System/FileSystem/I_File.h" #include "hardware/uart.h" @@ -134,13 +135,14 @@ void SerialDebugUI::listFiles(const char *path) { if (!fs->chdir(path)) { Trace::Error("failed to ls files path:%s", path); } - etl::vector fileIndexes; - fs->list(&fileIndexes, "", false); + + etl::vector fileIndexList; + fs->list(&fileIndexList, "", false, true, true); char name[PFILENAME_SIZE]; - for (size_t i = 0; i < fileIndexes.size(); i++) { - fs->getFileName(fileIndexes[i], name, PFILENAME_SIZE); - if (fs->getFileType(fileIndexes[i]) == PFT_FILE) { + for (size_t i = 0; i < fileIndexList.size(); i++) { + fs->getFileName(fileIndexList[i], name, PFILENAME_SIZE); + if (fs->getFileType(fileIndexList[i]) == PFT_FILE) { printf("[file] %s\n", name); } else { printf("[dir] %s\n", name); @@ -154,7 +156,8 @@ void SerialDebugUI::rmFile(const char *path) { Trace::Error("failed to delete file:%s", path); } else { Trace::Log("SERIALDEBUGUI", "deleted file:%s", path); - char buf[128]; + char buf[256]; + npf_snprintf(buf, sizeof(buf), "deleted:%s\n", path); uart_write_blocking(DEBUG_UART, (uint8_t *)buf, sizeof(buf)); } diff --git a/sources/Application/AppWindow.cpp b/sources/Application/AppWindow.cpp index 187d2c08d..7afae5b24 100644 --- a/sources/Application/AppWindow.cpp +++ b/sources/Application/AppWindow.cpp @@ -152,8 +152,6 @@ AppWindow::AppWindow(I_GUIWindowImp &imp, const char *projectName) // Init all members - _statusLine[0] = 0; - _currentView = nullptr; _closeProject = false; _lastA = 0; diff --git a/sources/Application/AppWindow.h b/sources/Application/AppWindow.h index 278575d30..3084048c8 100644 --- a/sources/Application/AppWindow.h +++ b/sources/Application/AppWindow.h @@ -152,6 +152,8 @@ class AppWindow : public GUIWindow, I_Observer, Status { public: // Static accessor for the animation frame counter static uint32_t GetAnimationFrameCounter() { return animationFrameCounter_; } + + View *getCurrentView() { return _currentView; } }; #endif diff --git a/sources/Application/Audio/AudioFileStreamer.cpp b/sources/Application/Audio/AudioFileStreamer.cpp index 9ebefaaa6..ea34f4802 100644 --- a/sources/Application/Audio/AudioFileStreamer.cpp +++ b/sources/Application/Audio/AudioFileStreamer.cpp @@ -53,13 +53,12 @@ bool AudioFileStreamer::Start(const char *name, int startSample, bool looping) { #endif wav_.Close(); - Trace::Log("", "wave open:%s", name_.c_str()); - auto res = wav_.Open(name_.c_str()); - if (!res) { - Trace::Error("Failed to open streaming of file:%s", name_.c_str()); - mode_ = AFSM_STOPPED; - return false; - } + + Trace::Log("", "wave open:%s", name); + auto res = wav_.Open(name); + Trace::Error("Failed to open streaming of file:%s", name_.c_str()); + mode_ = AFSM_STOPPED; + return false; // Get sample rate information and calculate speed factor fileSampleRate_ = wav_.GetSampleRate(-1); diff --git a/sources/Application/Instruments/SamplePool.cpp b/sources/Application/Instruments/SamplePool.cpp index 03b3956da..a94d18871 100644 --- a/sources/Application/Instruments/SamplePool.cpp +++ b/sources/Application/Instruments/SamplePool.cpp @@ -15,6 +15,7 @@ #include "Externals/etl/include/etl/string.h" #include "Externals/etl/include/etl/string_stream.h" #include "Foundation/Constants/SpecialCharacters.h" +#include "Foundation/Services/MemoryService.h" #include "System/Console/Trace.h" #include "System/FileSystem/FileSystem.h" #include "System/FileSystem/I_File.h" @@ -57,10 +58,9 @@ void SamplePool::Load(const char *projectName) { PROJECT_SAMPLES_DIR); } // First, find all wav files - etl::vector fileIndexes; - fs->list(&fileIndexes, ".wav", false); + fs->list(&fileIndexList_, ".wav", false, false, true); char name[PFILENAME_SIZE]; - uint totalSamples = fileIndexes.size(); + uint totalSamples = fileIndexList_.size(); // store for ui updates importCount = totalSamples; @@ -69,8 +69,8 @@ void SamplePool::Load(const char *projectName) { importIndex = i; importName = name; - fs->getFileName(fileIndexes[i], name, PFILENAME_SIZE); - if (fs->getFileType(fileIndexes[i]) == PFT_FILE) { + fs->getFileName(fileIndexList_[i], name, PFILENAME_SIZE); + if (fs->getFileType(fileIndexList_[i]) == PFT_FILE) { // Check if the filename exceeds the maximum allowed length if (strlen(name) > MAX_INSTRUMENT_FILENAME_LENGTH) { Trace::Error( diff --git a/sources/Application/Instruments/SamplePool.h b/sources/Application/Instruments/SamplePool.h index a39060e70..910c5420c 100644 --- a/sources/Application/Instruments/SamplePool.h +++ b/sources/Application/Instruments/SamplePool.h @@ -61,6 +61,7 @@ class SamplePool : public T_Factory, public Observable { private: etl::vector observers_; + etl::vector fileIndexList_; }; #endif diff --git a/sources/Application/Model/Config.cpp b/sources/Application/Model/Config.cpp index 51f109a7c..c3127a030 100644 --- a/sources/Application/Model/Config.cpp +++ b/sources/Application/Model/Config.cpp @@ -13,6 +13,7 @@ #include "Externals/etl/include/etl/flat_map.h" #include "Externals/etl/include/etl/string.h" #include "Externals/etl/include/etl/string_utilities.h" +#include "Foundation/Services/MemoryService.h" #include "Services/Midi/MidiService.h" #include "System/Console/Trace.h" #include "System/Console/nanoprintf.h" @@ -442,24 +443,25 @@ void Config::ReadColorVariable(PersistencyDocument *doc) { // Process the current element if it's a Color element if (strcmp(doc->ElemName(), "Color") == 0) { // Process Color element - char colorName[64] = {0}; - char colorValue[64] = {0}; + const int colorSize = 64; + char colorName[colorSize]; + char colorValue[colorSize]; // Get the name and value attributes while (doc->NextAttribute()) { if (strcmp(doc->attrname_, "name") == 0) { // Use safer string copy to ensure null-termination size_t len = strlen(doc->attrval_); - if (len >= sizeof(colorName)) { - len = sizeof(colorName) - 1; // Truncate if too long + if (len >= colorSize) { + len = colorSize - 1; // Truncate if too long } memcpy(colorName, doc->attrval_, len); colorName[len] = '\0'; // Ensure null-termination } else if (strcmp(doc->attrname_, "value") == 0) { // Use safer string copy to ensure null-termination size_t len = strlen(doc->attrval_); - if (len >= sizeof(colorValue)) { - len = sizeof(colorValue) - 1; // Truncate if too long + if (len >= colorSize) { + len = colorSize - 1; // Truncate if too long } memcpy(colorValue, doc->attrval_, len); colorValue[len] = '\0'; // Ensure null-termination diff --git a/sources/Application/Persistency/PersistencyService.cpp b/sources/Application/Persistency/PersistencyService.cpp index d8a46696e..c8f11739c 100644 --- a/sources/Application/Persistency/PersistencyService.cpp +++ b/sources/Application/Persistency/PersistencyService.cpp @@ -9,8 +9,8 @@ #include "PersistencyService.h" #include "../Instruments/SamplePool.h" +#include "Foundation/Services/MemoryService.h" #include "Foundation/Services/ServiceRegistry.h" - #include "Foundation/Types/Types.h" #include "Persistent.h" #include "System/Console/Trace.h" @@ -50,10 +50,41 @@ bool PersistencyService::DeleteProject(const char *projectName) { return false; } - if (!DeleteDirectoryContents_(0)) { - Trace::Error("PERSISTENCYSERVICE: Could not delete project contents"); - // attempt to recover to /projects for caller consistency - fs->chdir(".."); + // no checks on the file deletion, since they may not exist and the directory + // deletion will fail later on if they do and cannot be deleted + fs->DeleteFile(PROJECT_DATA_FILE); + fs->DeleteFile(AUTO_SAVE_FILENAME); + + if (!fs->chdir(PROJECT_SAMPLES_DIR)) { + Trace::Error("PERSISTENCYSERVICE: Could not change to project dir"); + return false; + } + + fs->list(&fileIndexList_, ".wav", false); + + // delete all samples + fs->BeginBatch(); + char filename[128]; + for (size_t i = 0; i < fileIndexList_.size(); i++) { + fs->getFileName(fileIndexList_[i], filename, + MAX_PROJECT_SAMPLE_PATH_LENGTH); + if (strcmp(filename, "..") == 0 || strcmp(filename, ".") == 0) { + continue; + } + if (fs->getFileType(fileIndexList_[i]) == PFT_DIR) { + continue; + } + fs->DeleteFile(filename); + }; + fs->EndBatch(); + + if (!fs->chdir("..")) { // up to project dir + Trace::Error("PERSISTENCYSERVICE: Could not change back to project dir"); + return false; + } + + if (!fs->DeleteDir(PROJECT_SAMPLES_DIR)) { + Trace::Error("PERSISTENCYSERVICE: Could not delete the project sample dir"); return false; } @@ -78,13 +109,13 @@ bool PersistencyService::DeleteDirectoryContents_(uint8_t depth) { } while (true) { - fileIndexes_.clear(); - fs->list(&fileIndexes_, "", false, true); + fileIndexList_.clear(); + fs->list(&fileIndexList_, "", false, true); bool foundEntry = false; bool deletedEntry = false; - for (size_t i = 0; i < fileIndexes_.size(); ++i) { - fs->getFileName(fileIndexes_[i], deleteNameBuffer_, + for (size_t i = 0; i < fileIndexList_.size(); ++i) { + fs->getFileName(fileIndexList_[i], deleteNameBuffer_, sizeof(deleteNameBuffer_)); if ((strcmp(deleteNameBuffer_, ".") == 0) || @@ -94,7 +125,7 @@ bool PersistencyService::DeleteDirectoryContents_(uint8_t depth) { foundEntry = true; - const PicoFileType type = fs->getFileType(fileIndexes_[i]); + const PicoFileType type = fs->getFileType(fileIndexList_[i]); if (type == PFT_FILE) { if (!fs->DeleteFile(deleteNameBuffer_)) { Trace::Error("PERSISTENCYSERVICE: Could not delete file: %s", @@ -194,10 +225,11 @@ PersistencyResult PersistencyService::Save(const char *projectName, Trace::Debug("get list of samples to copy from old project: %s", oldProjectName); - fs->list(&fileIndexes_, ".wav", false); + fs->list(&fileIndexList_, ".wav", false); char filenameBuffer[PFILENAME_SIZE]; - for (size_t i = 0; i < fileIndexes_.size(); i++) { - fs->getFileName(fileIndexes_[i], filenameBuffer, sizeof(filenameBuffer)); + for (size_t i = 0; i < fileIndexList_.size(); i++) { + fs->getFileName(fileIndexList_[i], filenameBuffer, + sizeof(filenameBuffer)); // ignore . and .. entries as using *.wav doesnt filter them out if (strcmp(filenameBuffer, ".") == 0 || strcmp(filenameBuffer, "..") == 0) diff --git a/sources/Application/Persistency/PersistencyService.h b/sources/Application/Persistency/PersistencyService.h index 2d6647cef..f5f03835e 100644 --- a/sources/Application/Persistency/PersistencyService.h +++ b/sources/Application/Persistency/PersistencyService.h @@ -68,9 +68,9 @@ class PersistencyService : public Service, bool DeleteDirectoryTree_(const char *dirname, uint8_t depth); // need these as statically allocated buffers as too big for stack - etl::vector fileIndexes_; etl::string pathBufferA; etl::string pathBufferB; + etl::vector fileIndexList_; char deleteNameBuffer_[PFILENAME_SIZE]; }; diff --git a/sources/Application/Views/ImportView.cpp b/sources/Application/Views/ImportView.cpp index 4e0b8dfe8..df2665682 100644 --- a/sources/Application/Views/ImportView.cpp +++ b/sources/Application/Views/ImportView.cpp @@ -11,6 +11,8 @@ #include "Application/Audio/AudioFileStreamer.h" #include "Application/Instruments/SampleInstrument.h" #include "Application/Instruments/SamplePool.h" +#include "Foundation/Services/MemoryService.h" + #include "Application/Views/SampleEditorView.h" #include "Externals/etl/include/etl/string.h" #include "Externals/etl/include/etl/to_string.h" @@ -896,7 +898,7 @@ void ImportView::onConfirmRemoveProjectSample(View &, ModalView &dialog) { } void ImportView::refreshFileIndexList(FileSystem *fs) { - fs->list(&fileIndexList_, ".wav", false); + fs->list(&fileIndexList_, ".wav", false, false, true); if (fs->isCurrentRoot() || inProjectSampleDir_) { for (auto it = fileIndexList_.begin(); it != fileIndexList_.end(); ++it) { diff --git a/sources/Application/Views/ImportView.h b/sources/Application/Views/ImportView.h index fb365333d..55ca77697 100644 --- a/sources/Application/Views/ImportView.h +++ b/sources/Application/Views/ImportView.h @@ -61,6 +61,7 @@ class ImportView : public ScreenView { false; // Flag to track if we're in the project's sample directory FileSystem *pendingDeleteFs_ = nullptr; char pendingDeleteFilename_[PFILENAME_SIZE] = {}; + etl::vector fileIndexList_; }; #endif diff --git a/sources/Application/Views/InstrumentImportView.cpp b/sources/Application/Views/InstrumentImportView.cpp index eda74d003..2b0a1ec45 100644 --- a/sources/Application/Views/InstrumentImportView.cpp +++ b/sources/Application/Views/InstrumentImportView.cpp @@ -11,6 +11,7 @@ #include "Application/AppWindow.h" #include "Application/Instruments/I_Instrument.h" #include "Application/Persistency/PersistencyService.h" +#include "Foundation/Services/MemoryService.h" #include "ModalDialogs/MessageBox.h" #include #include @@ -344,6 +345,6 @@ void InstrumentImportView::setCurrentFolder(FileSystem *fs, const char *name) { // Update list of file indexes in this new dir fileIndexList_.clear(); // Use false for subDirOnly to include both files and directories - fs->list(&fileIndexList_, INSTRUMENT_FILE_EXTENSION, false); + fs->list(&fileIndexList_, INSTRUMENT_FILE_EXTENSION, false, false, true); Trace::Debug("loaded %d files from %s", fileIndexList_.size(), name); } diff --git a/sources/Application/Views/InstrumentImportView.h b/sources/Application/Views/InstrumentImportView.h index ebf309156..ae0eba615 100644 --- a/sources/Application/Views/InstrumentImportView.h +++ b/sources/Application/Views/InstrumentImportView.h @@ -33,10 +33,10 @@ class InstrumentImportView : public ScreenView { private: void onImportSuccess(View &view, ModalView &dialog); + etl::vector fileIndexList_; size_t topIndex_ = 0; size_t currentIndex_ = 0; short selected_ = 0; int toInstrID_ = 0; - etl::vector fileIndexList_; }; #endif diff --git a/sources/Application/Views/SelectProjectView.cpp b/sources/Application/Views/SelectProjectView.cpp index 898bb84f0..46a8d766c 100644 --- a/sources/Application/Views/SelectProjectView.cpp +++ b/sources/Application/Views/SelectProjectView.cpp @@ -12,6 +12,8 @@ #include "Application/Persistency/PersistencyService.h" #include "Application/Utils/DrawUtils.h" #include "Application/Views/ModalDialogs/MessageBox.h" +#include "Foundation/Services/MemoryService.h" + #include "BaseClasses/ViewEvent.h" #include "Foundation/Constants/SpecialCharacters.h" #include "System/System/System.h" @@ -356,7 +358,7 @@ void SelectProjectView::setCurrentFolder() { fileIndexList_.clear(); // Let's read all the directory in the project dir - fs->list(&fileIndexList_, "", true); + fs->list(&fileIndexList_, "", true, true, true); // Filter out "." and ".." along with the hidden default project entry for (auto it = fileIndexList_.begin(); it != fileIndexList_.end();) { diff --git a/sources/Application/Views/SelectProjectView.h b/sources/Application/Views/SelectProjectView.h index 09e8e04ac..dd2f64538 100644 --- a/sources/Application/Views/SelectProjectView.h +++ b/sources/Application/Views/SelectProjectView.h @@ -39,8 +39,8 @@ class SelectProjectView : public ScreenView { size_t topIndex_ = 0; size_t currentIndex_ = 0; char selection_[MAX_PROJECT_NAME_LENGTH + 1]; - etl::vector fileIndexList_; int selectedButton_ = 0; + etl::vector fileIndexList_; void AttemptDeletingSelectedProject(); void AttemptLoadingProject(); diff --git a/sources/Application/Views/ThemeImportView.cpp b/sources/Application/Views/ThemeImportView.cpp index 3df16f95a..fea7611c9 100644 --- a/sources/Application/Views/ThemeImportView.cpp +++ b/sources/Application/Views/ThemeImportView.cpp @@ -12,6 +12,7 @@ #include "Application/Model/Config.h" #include "Application/Persistency/PersistenceConstants.h" #include "Application/Views/ModalDialogs/MessageBox.h" +#include "Foundation/Services/MemoryService.h" #include "ModalDialogs/MessageBox.h" #include "System/Console/Trace.h" #include "System/FileSystem/FileSystem.h" @@ -206,7 +207,7 @@ void ThemeImportView::setCurrentFolder() { isDirty_ = true; // get the directory listing - fs->list(&fileIndexList_, THEME_FILE_EXTENSION, false); + fs->list(&fileIndexList_, THEME_FILE_EXTENSION, false, false, true); // remove directories from listing for (int i = fileIndexList_.size() - 1; i >= 0; i--) { diff --git a/sources/Application/Views/ThemeImportView.h b/sources/Application/Views/ThemeImportView.h index 0f11d4e38..22b7e7129 100644 --- a/sources/Application/Views/ThemeImportView.h +++ b/sources/Application/Views/ThemeImportView.h @@ -34,8 +34,8 @@ class ThemeImportView : public ScreenView { void onImportThemeModalDismiss(View &view, ModalView &dialog); void OpenSelectedItem(); + etl::vector fileIndexList_; size_t topIndex_ = 0; size_t currentIndex_ = 0; - etl::vector fileIndexList_; }; #endif diff --git a/sources/Foundation/Services/MemoryService.h b/sources/Foundation/Services/MemoryService.h new file mode 100644 index 000000000..3781ae9ed --- /dev/null +++ b/sources/Foundation/Services/MemoryService.h @@ -0,0 +1,51 @@ +#pragma once + +#include "Application/AppWindow.h" +#include "Application/Application.h" +#include "Application/Views/ModalDialogs/MessageBox.h" +#include "Externals/etl/include/etl/vector.h" +#include "System/Console/Trace.h" +#include "System/FileSystem/FileSystem.h" + +#define PROTECT_SCRATCH 1 + +#ifdef DEBUG +#define PROTECT_SCRATCH 1 +#endif + +constexpr size_t MEMORYPOOL_SCRATCH_SIZE = 1024; + +class MemoryPool { +private: + static char buffer_[MEMORYPOOL_SCRATCH_SIZE]; +#if PROTECT_SCRATCH + static bool used_; +#endif + +public: + static etl::vector fileIndexList; + + // shared fileIndexList for file operations to avoid needing to allocate on + // the stack in multiple places or have multiple instances of + static void *acquire() { +#if PROTECT_SCRATCH + if (used_) { + Trace::Error("MemoryPool is already in use!"); + Application *app = Application::GetInstance(); + AppWindow *window = (AppWindow *)app->GetWindow(); + MessageBox *mb = + MessageBox::Create(*(window->getCurrentView()), + "MemoryPool is already in use!", MBBF_OK); + window->getCurrentView()->DoModal(mb); + } + used_ = true; +#endif + return buffer_; + } + + static void release() { +#if PROTECT_SCRATCH + used_ = false; +#endif + } +}; \ No newline at end of file diff --git a/sources/System/FileSystem/FileSystem.h b/sources/System/FileSystem/FileSystem.h index 1532b19c0..20fb07ce7 100644 --- a/sources/System/FileSystem/FileSystem.h +++ b/sources/System/FileSystem/FileSystem.h @@ -37,7 +37,8 @@ class FileSystem : public T_Factory { return false; } // Default implementation virtual void list(etl::ivector *fileIndexes, const char *filter, - bool subDirOnly, bool includeHidden = false) = 0; + bool subDirOnly, bool includeHidden = false, + bool sorted = false) = 0; virtual void getFileName(int index, char *name, int length) = 0; virtual PicoFileType getFileType(int index) = 0; virtual bool isParentRoot() = 0; diff --git a/sources/System/io/Status.cpp b/sources/System/io/Status.cpp index 47de82f1f..761b5c91c 100644 --- a/sources/System/io/Status.cpp +++ b/sources/System/io/Status.cpp @@ -8,6 +8,7 @@ */ #include "Status.h" +#include "Foundation/Services/MemoryService.h" #include #include #include