From f1a7e0ba96b6e04a8e743ed6889ff741b3903bef Mon Sep 17 00:00:00 2001 From: Orphey <15167344+frostmorn@users.noreply.github.com> Date: Wed, 20 Mar 2024 22:27:43 +0000 Subject: [PATCH 1/3] sdBrowserMenu + spiffsBrowserMenu -> fileBrowserMenu --- firmware/keira/src/apps/launcher.cpp | 97 +++++++++++--------------- firmware/keira/src/apps/launcher.h | 7 +- sdk/lib/lilka/src/lilka/filesystem.cpp | 83 ++++++++++++++++++++-- sdk/lib/lilka/src/lilka/filesystem.h | 8 ++- sdk/lib/lilka/src/lilka/fsdefs.h | 18 +++++ sdk/lib/lilka/src/lilka/sdcard.h | 17 ++--- 6 files changed, 150 insertions(+), 80 deletions(-) create mode 100644 sdk/lib/lilka/src/lilka/fsdefs.h diff --git a/firmware/keira/src/apps/launcher.cpp b/firmware/keira/src/apps/launcher.cpp index 4829832b..f025f5a6 100644 --- a/firmware/keira/src/apps/launcher.cpp +++ b/firmware/keira/src/apps/launcher.cpp @@ -53,9 +53,9 @@ void LauncherApp::run() { if (index == 0) { appsMenu(); } else if (index == 1) { - sdBrowserMenu("/"); + fileBrowserMenu("/", FB_SD); } else if (index == 2) { - spiffsBrowserMenu(); + fileBrowserMenu("/", FB_SPIFFS); } else if (index == 3) { // dev_menu(); devMenu(); @@ -127,20 +127,39 @@ const uint16_t get_file_color(const String& filename) { return lilka::display.color565(200, 200, 200); } } - -void LauncherApp::sdBrowserMenu(String path) { - if (!lilka::sdcard.available()) { - alert("Помилка", "SD-карта не знайдена"); - } - size_t _numEntries = lilka::sdcard.getEntryCount(path); - if (_numEntries == 0) { - alert("Помилка", "Директорія пуста або сталася помилка читання директорії"); +void LauncherApp::fileBrowserMenu(String path, int fbrowserType) { + size_t _numEntries = 0; + if (fbrowserType == FB_SD) { + if (!lilka::sdcard.available()) { + alert("Помилка", "SD-карта не знайдена"); + return; + } + size_t _numEntries = lilka::sdcard.getEntryCount(path); + if (_numEntries == 0) { + alert("Помилка", "Директорія пуста або сталася помилка читання директорії"); + return; + } + } else if (fbrowserType == FB_SPIFFS) { + if (!lilka::filesystem.available()) { + alert("Помилка", "SPIFFS не підтримується"); + return; + } + size_t _numEntries = lilka::filesystem.getEntryCount(path); + if (_numEntries == 0) { + alert("Помилка", "Директорія пуста або сталася помилка читання директорії"); + return; + } + } else { + alert("Помилка", "Цей тип носія не підтримується"); return; } lilka::Entry* entries = new lilka::Entry[_numEntries]; + int numEntries = -1; + + if (fbrowserType == FB_SD) numEntries = lilka::sdcard.listDir(path, entries); + else numEntries = lilka::filesystem.listDir(path, entries); - int numEntries = lilka::sdcard.listDir(path, entries); std::unique_ptr entriesPtr(entries); // Так як listDir має повертати -1 в разі помилки @@ -150,7 +169,12 @@ void LauncherApp::sdBrowserMenu(String path) { return; } - lilka::Menu menu("SD: " + path); + String menuLabel; + if (fbrowserType == FB_SD) menuLabel = "SD: " + path; + else menuLabel = "SPIFFS: " + path; + + lilka::Menu menu(menuLabel); + for (int i = 0; i < numEntries; i++) { String filename = entries[i].name; const menu_icon_t* icon = @@ -169,57 +193,14 @@ void LauncherApp::sdBrowserMenu(String path) { if (index != -1) { if (index >= numEntries - 1) break; if (entries[index].type == lilka::EntryType::ENT_DIRECTORY) { - sdBrowserMenu(path + entries[index].name + "/"); + fileBrowserMenu(path + entries[index].name + "/", fbrowserType); } else { - selectFile(lilka::sdcard.abspath(path + entries[index].name)); + if (fbrowserType == FB_SD) selectFile(lilka::sdcard.abspath(path + entries[index].name)); + else selectFile(lilka::filesystem.abspath(path + entries[index].name)); } } taskYIELD(); } - - return; -} - -void LauncherApp::spiffsBrowserMenu() { - if (!lilka::filesystem.available()) { - alert("Помилка", "SPIFFS не підтримується"); - return; - } - - String filenames - [32]; // TODO - allocate dynamically (increasing to 64 causes task stack overflow which is limited by ARDUINO_LOOP_STACK_SIZE) - int numEntries = lilka::filesystem.readdir(filenames); - - if (numEntries == -1) { - alert("Помилка", "Не вдалося прочитати корінь SPIFFS"); - return; - } - const menu_icon_t* icons[32]; - uint16_t colors[32]; - for (int i = 0; i < numEntries; i++) { - icons[i] = get_file_icon(filenames[i]); - colors[i] = get_file_color(filenames[i]); - } - filenames[numEntries++] = "<< Назад"; - icons[numEntries - 1] = 0; - colors[numEntries - 1] = 0; - - lilka::Menu menu("SPIFFS"); - for (int i = 0; i < numEntries; i++) { - menu.addItem(filenames[i], icons[i], colors[i]); - } - while (1) { - menu.update(); - menu.draw(canvas); - queueDraw(); - int16_t index = menu.getSelectedIndex(); - if (index != -1) { - if (index == numEntries - 1) { - return; - } - selectFile(lilka::filesystem.abspath(filenames[index])); - } - } } void LauncherApp::selectFile(String path) { diff --git a/firmware/keira/src/apps/launcher.h b/firmware/keira/src/apps/launcher.h index f918af31..8327b9b0 100644 --- a/firmware/keira/src/apps/launcher.h +++ b/firmware/keira/src/apps/launcher.h @@ -12,6 +12,10 @@ typedef struct { { \ name, []() { return new className(); } \ } +typedef enum { + FB_SD, + FB_SPIFFS, +} FileBrowserType; class LauncherApp : public App { public: @@ -20,8 +24,7 @@ class LauncherApp : public App { private: void run() override; void appsMenu(); - void sdBrowserMenu(String path); - void spiffsBrowserMenu(); + void fileBrowserMenu(String path, int fbrowserType); void devMenu(); void settingsMenu(); diff --git a/sdk/lib/lilka/src/lilka/filesystem.cpp b/sdk/lib/lilka/src/lilka/filesystem.cpp index 57ae437a..ef691155 100644 --- a/sdk/lib/lilka/src/lilka/filesystem.cpp +++ b/sdk/lib/lilka/src/lilka/filesystem.cpp @@ -6,7 +6,7 @@ namespace lilka { -Filesystem::Filesystem() : _available(false), _filesystem(NULL) { +Filesystem::Filesystem() : _available(false), fs(NULL) { } void Filesystem::begin() { @@ -18,15 +18,59 @@ void Filesystem::begin() { serial_log("SPIFFS ok"); _available = true; } - _filesystem = &SPIFFS; + fs = &SPIFFS; } bool Filesystem::available() { return _available; } +int Filesystem::listDir(String path, Entry entries[]) { + while (!path.equals("/") && path.endsWith("/")) { + // Strip trailing slashes, unless it's the root directory + path.remove(path.length() - 1); + } + File root = fs->open(path); + if (!root) { + serial_err("listDir: failed to open directory: %s", path.c_str()); + return -1; + } + if (!root.isDirectory()) { + serial_err("listDir: not a directory: %s", path.c_str()); + return -1; + } + + int i = 0; + File file = root.openNextFile(); + while (file) { + entries[i].name = file.name(); + if (file.isDirectory()) { + entries[i].type = ENT_DIRECTORY; + } else { + entries[i].type = ENT_FILE; + } + entries[i].size = file.size(); + file.close(); + file = root.openNextFile(); + i++; + } + root.close(); + // Sort filenames, but keep directories first + qsort(entries, i, sizeof(Entry), [](const void* a, const void* b) -> int { + const Entry* ea = static_cast(a); + const Entry* eb = static_cast(b); + if (ea->type == ENT_DIRECTORY && eb->type != ENT_DIRECTORY) { + return -1; + } else if (ea->type != ENT_DIRECTORY && eb->type == ENT_DIRECTORY) { + return 1; + } + return ea->name.compareTo(eb->name); + }); + return i; +} + int Filesystem::readdir(String filenames[]) { - File _root = _filesystem->open("/"); + File _root = fs->open("/"); int count = 0; File file = _root.openNextFile(); while (file) { @@ -50,7 +94,7 @@ int Filesystem::readdir(String filenames[]) { } int Filesystem::readdir(String filenames[], String extension) { - File _root = _filesystem->open("/"); + File _root = fs->open("/"); int count = 0; File file = _root.openNextFile(); while (file) { @@ -70,6 +114,37 @@ int Filesystem::readdir(String filenames[], String extension) { return count; } +size_t Filesystem::getEntryCount(String path) { + size_t countFiles = 0; + + while (!path.equals("/") && path.endsWith("/")) { + // Strip trailing slashes, unless it's the root directory + path.remove(path.length() - 1); + } + File root = fs->open(path); + // Below we assume if folder can't be open then it has zero files + // Btw we will show this error using serial + if (!root) { + serial_err("%s:%d:getEntryCount: failed to open directory: %s", __FILE__, __LINE__, path.c_str()); + return 0; + } + if (!root.isDirectory()) { + serial_err("%s:%d:getEntryCount: not a directory: %s", __FILE__, __LINE__, path.c_str()); + return 0; + } + + File file = root.openNextFile(); + + while (file) { + file = root.openNextFile(); + countFiles++; + } + + root.close(); + + return countFiles; +} + String Filesystem::abspath(String filename) { return String(LILKA_FSROOT) + "/" + filename; } diff --git a/sdk/lib/lilka/src/lilka/filesystem.h b/sdk/lib/lilka/src/lilka/filesystem.h index 2e7fedef..b5e7796b 100644 --- a/sdk/lib/lilka/src/lilka/filesystem.h +++ b/sdk/lib/lilka/src/lilka/filesystem.h @@ -2,20 +2,22 @@ #define LILKA_FILESYSTEM_H #include - +#include "fsdefs.h" namespace lilka { - class Filesystem { public: Filesystem(); void begin(); bool available(); + + int listDir(String path, Entry entries[]); int readdir(String filenames[]); int readdir(String filenames[], String extension); + size_t getEntryCount(String path); String abspath(String filename); private: - FS* _filesystem; + FS* fs; bool _available; }; diff --git a/sdk/lib/lilka/src/lilka/fsdefs.h b/sdk/lib/lilka/src/lilka/fsdefs.h new file mode 100644 index 00000000..d354e5f2 --- /dev/null +++ b/sdk/lib/lilka/src/lilka/fsdefs.h @@ -0,0 +1,18 @@ +#pragma once +/* + This file should be a place to store common for all filesystems + parts +*/ +// definitions required for SPIFFS and SDCard Browsers +namespace lilka { +typedef enum { + ENT_FILE, + ENT_DIRECTORY, +} EntryType; + +typedef struct { + String name; + EntryType type; + size_t size; +} Entry; +} // namespace lilka \ No newline at end of file diff --git a/sdk/lib/lilka/src/lilka/sdcard.h b/sdk/lib/lilka/src/lilka/sdcard.h index d3d70735..55924822 100644 --- a/sdk/lib/lilka/src/lilka/sdcard.h +++ b/sdk/lib/lilka/src/lilka/sdcard.h @@ -4,30 +4,21 @@ #include #include "config.h" - +#include "fsdefs.h" namespace lilka { -typedef enum { - ENT_FILE, - ENT_DIRECTORY, -} EntryType; - -typedef struct { - String name; - EntryType type; - size_t size; -} Entry; - class SDCard { public: SDCard(); void begin(); bool available(); - SDFS* fs; int listDir(String path, Entry entries[]); size_t getEntryCount(String path); String abspath(String path); + +private: + SDFS* fs; }; extern SDCard sdcard; From 6101759b3db025491f518d81240e3cf8f4877f55 Mon Sep 17 00:00:00 2001 From: Orphey <15167344+frostmorn@users.noreply.github.com> Date: Wed, 20 Mar 2024 22:40:45 +0000 Subject: [PATCH 2/3] Remove unused code parts, make filesystem.h implementation closer to sdcard.h --- sdk/lib/lilka/src/lilka/filesystem.cpp | 45 -------------------------- sdk/lib/lilka/src/lilka/filesystem.h | 2 -- 2 files changed, 47 deletions(-) diff --git a/sdk/lib/lilka/src/lilka/filesystem.cpp b/sdk/lib/lilka/src/lilka/filesystem.cpp index ef691155..e06589bf 100644 --- a/sdk/lib/lilka/src/lilka/filesystem.cpp +++ b/sdk/lib/lilka/src/lilka/filesystem.cpp @@ -69,51 +69,6 @@ int Filesystem::listDir(String path, Entry entries[]) { return i; } -int Filesystem::readdir(String filenames[]) { - File _root = fs->open("/"); - int count = 0; - File file = _root.openNextFile(); - while (file) { - if (file.isDirectory()) { - file.close(); - file = _root.openNextFile(); - continue; - } - filenames[count++] = file.name(); - file.close(); - file = _root.openNextFile(); - } - _root.close(); - // Sort filenames - qsort(filenames, count, sizeof(String), [](const void* a, const void* b) -> int { - const String* ea = static_cast(a); - const String* eb = static_cast(b); - return ea->compareTo(*eb); - }); - return count; -} - -int Filesystem::readdir(String filenames[], String extension) { - File _root = fs->open("/"); - int count = 0; - File file = _root.openNextFile(); - while (file) { - if (file.isDirectory()) { - file.close(); - file = _root.openNextFile(); - continue; - } - String name = file.name(); - if (name.endsWith(extension)) { - filenames[count++] = name; - } - file.close(); - file = _root.openNextFile(); - } - _root.close(); - return count; -} - size_t Filesystem::getEntryCount(String path) { size_t countFiles = 0; diff --git a/sdk/lib/lilka/src/lilka/filesystem.h b/sdk/lib/lilka/src/lilka/filesystem.h index b5e7796b..5b047355 100644 --- a/sdk/lib/lilka/src/lilka/filesystem.h +++ b/sdk/lib/lilka/src/lilka/filesystem.h @@ -11,8 +11,6 @@ class Filesystem { bool available(); int listDir(String path, Entry entries[]); - int readdir(String filenames[]); - int readdir(String filenames[], String extension); size_t getEntryCount(String path); String abspath(String filename); From 8bb7861cf5928dc903abc452709a7b2d77babfb2 Mon Sep 17 00:00:00 2001 From: Orphey <15167344+frostmorn@users.noreply.github.com> Date: Thu, 21 Mar 2024 02:14:23 +0000 Subject: [PATCH 3/3] fix shadow variable _numEntries --- firmware/keira/src/apps/launcher.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/firmware/keira/src/apps/launcher.cpp b/firmware/keira/src/apps/launcher.cpp index a2a2324b..4ddbbc87 100644 --- a/firmware/keira/src/apps/launcher.cpp +++ b/firmware/keira/src/apps/launcher.cpp @@ -139,7 +139,7 @@ void LauncherApp::fileBrowserMenu(String path, int fbrowserType) { alert("Помилка", "SD-карта не знайдена"); return; } - size_t _numEntries = lilka::sdcard.getEntryCount(path); + _numEntries = lilka::sdcard.getEntryCount(path); if (_numEntries == 0) { alert("Помилка", "Директорія пуста або сталася помилка читання директорії"); return; @@ -149,7 +149,7 @@ void LauncherApp::fileBrowserMenu(String path, int fbrowserType) { alert("Помилка", "SPIFFS не підтримується"); return; } - size_t _numEntries = lilka::filesystem.getEntryCount(path); + _numEntries = lilka::filesystem.getEntryCount(path); if (_numEntries == 0) { alert("Помилка", "Директорія пуста або сталася помилка читання директорії"); return;