Sd Card Test
Source: src/sd_card_test.c · 342 lines · 15 functions.
Module entry points
These functions are exposed to the rest of the project. Open one to follow its decisions and the helpers it calls.
How to read these diagrams
Each function has its own expandable diagram using the actual expressions and statements from this file. Decisions split into True and False; loop bodies are indented. Returns end the function immediately; break and continue follow their C meanings. Arrows between statements show their order when execution reaches them.
These are structural source diagrams before preprocessing, not runtime traces. Conditional compilation and macros remain source expressions. Ternary choices remain inside their statement. Calls are linked separately; callback execution and task scheduling are not inferred.
Functions in this file
browserRoot() · source line 49
static const char *browserRoot(void)
- Exit this function
return browser_source == BROWSER_SPIFFS ? SPIFFS_ROOT : SD_ROOT;
Read original function source
static const char *browserRoot(void)
{
return browser_source == BROWSER_SPIFFS ? SPIFFS_ROOT : SD_ROOT;
}atRoot() · source line 54
static bool atRoot(void)
Calls into this documentation:
- Exit this function
return strcmp(current_path, browserRoot()) == 0;
Read original function source
static bool atRoot(void) { return strcmp(current_path, browserRoot()) == 0; }displayedCount() · source line 55
static unsigned displayedCount(void)
Calls into this documentation:
- Exit this function
return entry_count + (atRoot() ? 0U : 1U);
Read original function source
static unsigned displayedCount(void) { return entry_count + (atRoot() ? 0U : 1U); }sortEntries() · source line 57
static void sortEntries(void)
- FOR loop
unsigned i = 0; i < entry_count; ++iRepeat body while the loop condition permits; then continue below.
- FOR loop
unsigned j = i + 1U; j < entry_count; ++jRepeat body while the loop condition permits; then continue below.
- Decision
strcmp(entries[i].name, entries[j].name) <= 0True- Next loop iteration
continue;
FalseContinue after this decision.
- Execute
directory_entry_t swap = entries[i]; - Execute
entries[i] = entries[j]; - Execute
entries[j] = swap;
Read original function source
static void sortEntries(void)
{
for (unsigned i = 0; i < entry_count; ++i) for (unsigned j = i + 1U; j < entry_count; ++j) {
if (strcmp(entries[i].name, entries[j].name) <= 0) continue;
directory_entry_t swap = entries[i]; entries[i] = entries[j]; entries[j] = swap;
}
}addEntry() · source line 65
static void addEntry(const char *name, uint32_t size, bool directory)
- Execute
const size_t name_length = strlen(name); - Decision
name_length == 0U || name_length > ENTRY_TEXT_CHARSTrue- Exit this function
return;
FalseContinue after this decision.
- FOR loop
unsigned index = 0; index < entry_count; ++indexRepeat body while the loop condition permits; then continue below.
- Decision
strcmp(entries[index].name, name) != 0True- Next loop iteration
continue;
FalseContinue after this decision.
- Execute
entries[index].directory |= directory; - Exit this function
return;
- Decision
entry_count >= MAX_DIRECTORY_ENTRIESTrue- Exit this function
return;
FalseContinue after this decision.
- Execute
directory_entry_t *entry = &entries[entry_count++]; - Execute
memcpy(entry->name, name, name_length + 1U); - Execute
entry->size = size; - Execute
entry->directory = directory;
Read original function source
static void addEntry(const char *name, uint32_t size, bool directory)
{
const size_t name_length = strlen(name);
if (name_length == 0U || name_length > ENTRY_TEXT_CHARS) return;
for (unsigned index = 0; index < entry_count; ++index) {
if (strcmp(entries[index].name, name) != 0) continue;
entries[index].directory |= directory;
return;
}
if (entry_count >= MAX_DIRECTORY_ENTRIES) return;
directory_entry_t *entry = &entries[entry_count++];
memcpy(entry->name, name, name_length + 1U);
entry->size = size;
entry->directory = directory;
}scanSdCard() · source line 81
static void scanSdCard(void)
Calls into this documentation:
- Execute
const esp_err_t space_result = esp_vfs_fat_info(SD_ROOT, &total_bytes, &free_bytes); - Execute
space_ok = space_result == ESP_OK; - Decision
space_okTrue- Execute
snprintf(space_text, sizeof(space_text), "TOTAL %" PRIu64 " MB FREE %" PRIu64 " MB", total_bytes / (1024U * 1024U), free_bytes / (1024U * 1024U));
False- Execute
snprintf(space_text, sizeof(space_text), "SPACE ERROR: %.32s", esp_err_to_name(space_result));
- Execute
errno = 0; - Execute
DIR *directory = opendir(current_path); - Decision
directory == NULLTrue- Execute
snprintf(status_text, sizeof(status_text), "OPEN ERROR: %.34s", strerror(errno)); - Execute
scan_ok = false; - Exit this function
return;
FalseContinue after this decision.
- Execute
int read_error = 0; - FOR loop
;;Repeat body while the loop condition permits; then continue below.
- Execute
errno = 0; - Execute
struct dirent *entry = readdir(directory); - Decision
entry == NULLTrue- Execute
read_error = errno; - Exit enclosing loop or switch
break;
FalseContinue after this decision.
- Decision
strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0True- Next loop iteration
continue;
FalseContinue after this decision.
- Execute
const size_t name_length = strlen(entry->d_name); - Decision
name_length > ENTRY_TEXT_CHARSTrue- Next loop iteration
continue;
FalseContinue after this decision.
- Execute
char path[PATH_MAX]; - Execute
const int written = snprintf(path, sizeof(path), "%s/%s", current_path, entry->d_name); - Decision
written < 0 || (size_t)written >= sizeof(path)True- Next loop iteration
continue;
FalseContinue after this decision.
- Execute
struct stat information; - Decision
stat(path, &information) != 0True- Next loop iteration
continue;
FalseContinue after this decision.
- Execute
addEntry(entry->d_name, (uint32_t)information.st_size, S_ISDIR(information.st_mode));
- Execute
closedir(directory); - Decision
read_error != 0True- Execute
snprintf(status_text, sizeof(status_text), "READ ERROR: %.34s", strerror(read_error)); - Execute
scan_ok = false; - Exit this function
return;
FalseContinue after this decision.
- Execute
sortEntries(); - Execute
snprintf(status_text, sizeof(status_text), "READ OK: %u ITEMS", entry_count); - Execute
scan_ok = true;
Read original function source
static void scanSdCard(void)
{
const esp_err_t space_result = esp_vfs_fat_info(SD_ROOT, &total_bytes, &free_bytes);
space_ok = space_result == ESP_OK;
if (space_ok) {
snprintf(space_text, sizeof(space_text), "TOTAL %" PRIu64 " MB FREE %" PRIu64 " MB",
total_bytes / (1024U * 1024U), free_bytes / (1024U * 1024U));
} else {
snprintf(space_text, sizeof(space_text), "SPACE ERROR: %.32s", esp_err_to_name(space_result));
}
errno = 0;
DIR *directory = opendir(current_path);
if (directory == NULL) {
snprintf(status_text, sizeof(status_text), "OPEN ERROR: %.34s", strerror(errno));
scan_ok = false;
return;
}
int read_error = 0;
for (;;) {
errno = 0;
struct dirent *entry = readdir(directory);
if (entry == NULL) { read_error = errno; break; }
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) continue;
const size_t name_length = strlen(entry->d_name);
if (name_length > ENTRY_TEXT_CHARS) continue;
char path[PATH_MAX];
const int written = snprintf(path, sizeof(path), "%s/%s", current_path, entry->d_name);
if (written < 0 || (size_t)written >= sizeof(path)) continue;
struct stat information;
if (stat(path, &information) != 0) continue;
addEntry(entry->d_name, (uint32_t)information.st_size, S_ISDIR(information.st_mode));
}
closedir(directory);
if (read_error != 0) {
snprintf(status_text, sizeof(status_text), "READ ERROR: %.34s", strerror(read_error));
scan_ok = false;
return;
}
sortEntries();
snprintf(status_text, sizeof(status_text), "READ OK: %u ITEMS", entry_count);
scan_ok = true;
}scanSpiffs() · source line 126
static void scanSpiffs(void)
Calls into this documentation:
- Execute
size_t total = 0, used = 0; - Execute
const esp_err_t space_result = esp_spiffs_info(SPIFFS_PARTITION, &total, &used); - Execute
space_ok = space_result == ESP_OK; - Decision
space_okTrue- Execute
total_bytes = total; - Execute
free_bytes = total >= used ? total - used : 0U; - Execute
snprintf(space_text, sizeof(space_text), "TOTAL %u MB FREE %u MB", (unsigned)(total_bytes / (1024U * 1024U)), (unsigned)(free_bytes / (1024U * 1024U)));
False- Execute
snprintf(space_text, sizeof(space_text), "SPACE ERROR: %.32s", esp_err_to_name(space_result));
- Execute
const char *virtual_path = current_path + strlen(SPIFFS_ROOT); - WHILE loop
*virtual_path == '/'Repeat body while the loop condition permits; then continue below.
- Execute
++virtual_path;
- Execute
const size_t prefix_length = strlen(virtual_path); - Execute
DIR *directory = opendir(SPIFFS_ROOT); - Decision
directory == NULLTrue- Execute
snprintf(status_text, sizeof(status_text), "OPEN ERROR: %.34s", strerror(errno)); - Execute
scan_ok = false; - Exit this function
return;
FalseContinue after this decision.
- Execute
int read_error = 0; - FOR loop
;;Repeat body while the loop condition permits; then continue below.
- Execute
errno = 0; - Execute
struct dirent *entry = readdir(directory); - Decision
entry == NULLTrue- Execute
read_error = errno; - Exit enclosing loop or switch
break;
FalseContinue after this decision.
- Execute
const char *full_name = entry->d_name; - WHILE loop
*full_name == '/'Repeat body while the loop condition permits; then continue below.
- Execute
++full_name;
- Decision
*full_name == '\0'True- Next loop iteration
continue;
FalseContinue after this decision.
- Execute
const char *visible_name = full_name; - Decision
prefix_length != 0UTrue- Decision
strncmp(full_name, virtual_path, prefix_length) != 0 || full_name[prefix_length] != '/'True- Next loop iteration
continue;
FalseContinue after this decision.
- Execute
visible_name = full_name + prefix_length + 1U;
FalseContinue after this decision.
- Execute
const char *separator = strchr(visible_name, '/'); - Decision
separator != NULLTrue- Execute
char directory_name[ENTRY_TEXT_CHARS + 1]; - Execute
const size_t length = (size_t)(separator - visible_name); - Decision
length == 0U || length > ENTRY_TEXT_CHARSTrue- Next loop iteration
continue;
FalseContinue after this decision.
- Execute
memcpy(directory_name, visible_name, length); - Execute
directory_name[length] = '\0'; - Execute
addEntry(directory_name, 0U, true); - Next loop iteration
continue;
FalseContinue after this decision.
- Execute
char path[PATH_MAX]; - Execute
const int written = snprintf(path, sizeof(path), "%s/%s", SPIFFS_ROOT, full_name); - Decision
written < 0 || (size_t)written >= sizeof(path)True- Next loop iteration
continue;
FalseContinue after this decision.
- Execute
struct stat information; - Decision
stat(path, &information) != 0True- Next loop iteration
continue;
FalseContinue after this decision.
- Execute
addEntry(visible_name, (uint32_t)information.st_size, false);
- Execute
closedir(directory); - Decision
read_error != 0True- Execute
snprintf(status_text, sizeof(status_text), "READ ERROR: %.34s", strerror(read_error)); - Execute
scan_ok = false; - Exit this function
return;
FalseContinue after this decision.
- Execute
sortEntries(); - Execute
snprintf(status_text, sizeof(status_text), "READ OK: %u ITEMS", entry_count); - Execute
scan_ok = true;
Read original function source
static void scanSpiffs(void)
{
size_t total = 0, used = 0;
const esp_err_t space_result = esp_spiffs_info(SPIFFS_PARTITION, &total, &used);
space_ok = space_result == ESP_OK;
if (space_ok) {
total_bytes = total;
free_bytes = total >= used ? total - used : 0U;
snprintf(space_text, sizeof(space_text), "TOTAL %u MB FREE %u MB",
(unsigned)(total_bytes / (1024U * 1024U)),
(unsigned)(free_bytes / (1024U * 1024U)));
} else {
snprintf(space_text, sizeof(space_text), "SPACE ERROR: %.32s", esp_err_to_name(space_result));
}
const char *virtual_path = current_path + strlen(SPIFFS_ROOT);
while (*virtual_path == '/') ++virtual_path;
const size_t prefix_length = strlen(virtual_path);
DIR *directory = opendir(SPIFFS_ROOT);
if (directory == NULL) {
snprintf(status_text, sizeof(status_text), "OPEN ERROR: %.34s", strerror(errno));
scan_ok = false;
return;
}
int read_error = 0;
for (;;) {
errno = 0;
struct dirent *entry = readdir(directory);
if (entry == NULL) { read_error = errno; break; }
const char *full_name = entry->d_name;
while (*full_name == '/') ++full_name;
if (*full_name == '\0') continue;
const char *visible_name = full_name;
if (prefix_length != 0U) {
if (strncmp(full_name, virtual_path, prefix_length) != 0 ||
full_name[prefix_length] != '/') continue;
visible_name = full_name + prefix_length + 1U;
}
const char *separator = strchr(visible_name, '/');
if (separator != NULL) {
char directory_name[ENTRY_TEXT_CHARS + 1];
const size_t length = (size_t)(separator - visible_name);
if (length == 0U || length > ENTRY_TEXT_CHARS) continue;
memcpy(directory_name, visible_name, length);
directory_name[length] = '\0';
addEntry(directory_name, 0U, true);
continue;
}
char path[PATH_MAX];
const int written = snprintf(path, sizeof(path), "%s/%s", SPIFFS_ROOT, full_name);
if (written < 0 || (size_t)written >= sizeof(path)) continue;
struct stat information;
if (stat(path, &information) != 0) continue;
addEntry(visible_name, (uint32_t)information.st_size, false);
}
closedir(directory);
if (read_error != 0) {
snprintf(status_text, sizeof(status_text), "READ ERROR: %.34s", strerror(read_error));
scan_ok = false;
return;
}
sortEntries();
snprintf(status_text, sizeof(status_text), "READ OK: %u ITEMS", entry_count);
scan_ok = true;
}scanDirectory() · source line 193
static void scanDirectory(void)
Calls into this documentation:
- Execute
entry_count = selected_entry = 0; - Execute
memset(entries, 0, sizeof(entries)); - Decision
browser_source == BROWSER_SPIFFSTrue- Execute
scanSpiffs();
False- Execute
scanSdCard();
Read original function source
static void scanDirectory(void)
{
entry_count = selected_entry = 0;
memset(entries, 0, sizeof(entries));
if (browser_source == BROWSER_SPIFFS) scanSpiffs();
else scanSdCard();
}goToParent() · source line 201
static void goToParent(void)
Calls into this documentation:
- Execute
char *last_slash = strrchr(current_path, '/'); - Decision
last_slash == NULL || last_slash == current_pathTrue- Exit this function
return;
FalseContinue after this decision.
- Execute
*last_slash = '\0'; - Execute
scanDirectory();
Read original function source
static void goToParent(void)
{
char *last_slash = strrchr(current_path, '/');
if (last_slash == NULL || last_slash == current_path) return;
*last_slash = '\0';
scanDirectory();
}openSelected() · source line 209
static void openSelected(void)
Calls into this documentation:
- Decision
!scan_ok || displayedCount() == 0True- Exit this function
return;
FalseContinue after this decision.
- Decision
!atRoot() && selected_entry == 0True- Execute
goToParent(); - Exit this function
return;
FalseContinue after this decision.
- Execute
const unsigned index = selected_entry - (atRoot() ? 0U : 1U); - Decision
index >= entry_countTrue- Exit this function
return;
FalseContinue after this decision.
- Execute
const directory_entry_t *entry = &entries[index]; - Decision
!entry->directoryTrue- Decision
imageIsSupportedName(entry->name)True- Execute
const int written = snprintf(preview_path, sizeof(preview_path), "%s/%s", current_path, entry->name); - Decision
written >= 0 && (size_t)written < sizeof(preview_path)True- Execute
preview_pending = true; - Execute
snprintf(status_text, sizeof(status_text), "OPENING IMAGE: %.26s", entry->name);
False- Execute
snprintf(status_text, sizeof(status_text), "PATH TOO LONG");
- Exit this function
return;
FalseContinue after this decision.
- Execute
snprintf(status_text, sizeof(status_text), "FILE: %.32s %" PRIu32 " B", entry->name, entry->size); - Exit this function
return;
FalseContinue after this decision.
- Execute
const size_t path_length = strlen(current_path); - Execute
const size_t name_length = strlen(entry->name); - Decision
path_length + 1U + name_length >= sizeof(current_path)True- Execute
snprintf(status_text, sizeof(status_text), "PATH TOO LONG"); - Exit this function
return;
FalseContinue after this decision.
- Execute
current_path[path_length] = '/'; - Execute
memcpy(current_path + path_length + 1U, entry->name, name_length + 1U); - Execute
scanDirectory();
Read original function source
static void openSelected(void)
{
if (!scan_ok || displayedCount() == 0) return;
if (!atRoot() && selected_entry == 0) { goToParent(); return; }
const unsigned index = selected_entry - (atRoot() ? 0U : 1U);
if (index >= entry_count) return;
const directory_entry_t *entry = &entries[index];
if (!entry->directory) {
if (imageIsSupportedName(entry->name)) {
const int written = snprintf(preview_path, sizeof(preview_path), "%s/%s", current_path, entry->name);
if (written >= 0 && (size_t)written < sizeof(preview_path)) {
preview_pending = true;
snprintf(status_text, sizeof(status_text), "OPENING IMAGE: %.26s", entry->name);
} else {
snprintf(status_text, sizeof(status_text), "PATH TOO LONG");
}
return;
}
snprintf(status_text, sizeof(status_text), "FILE: %.32s %" PRIu32 " B", entry->name, entry->size);
return;
}
const size_t path_length = strlen(current_path);
const size_t name_length = strlen(entry->name);
if (path_length + 1U + name_length >= sizeof(current_path)) {
snprintf(status_text, sizeof(status_text), "PATH TOO LONG");
return;
}
current_path[path_length] = '/';
memcpy(current_path + path_length + 1U, entry->name, name_length + 1U);
scanDirectory();
}startSdCardTest() · source line 241
void startSdCardTest(uint32_t now_ms)
Calls into this documentation:
- Execute
(void)now_ms; - Execute
browser_source = TF_CARD_ENABLED ? BROWSER_SD_CARD : BROWSER_SPIFFS; - Execute
strcpy(current_path, browserRoot()); - Execute
running = true; - Execute
waiting_for_centre = true; - Execute
held_direction = 0; - Execute
repeating = false; - Execute
preview_pending = false; - Execute
preview_active = false; - Execute
scanDirectory();
Read original function source
void startSdCardTest(uint32_t now_ms)
{
(void)now_ms;
browser_source = TF_CARD_ENABLED ? BROWSER_SD_CARD : BROWSER_SPIFFS;
strcpy(current_path, browserRoot());
running = true;
waiting_for_centre = true;
held_direction = 0;
repeating = false;
preview_pending = false;
preview_active = false;
scanDirectory();
}stopSdCardTest() · source line 255
void stopSdCardTest(void)
- Execute
running = false; - Execute
preview_pending = false; - Execute
preview_active = false;
Read original function source
void stopSdCardTest(void)
{
running = false;
preview_pending = false;
preview_active = false;
}sdCardTestAction() · source line 262
void sdCardTestAction(bool select, bool toggle_source, uint32_t now_ms)
Calls into this documentation:
- Execute
(void)now_ms; - Decision
TF_CARD_ENABLED && running && toggle_source && !preview_pending && !preview_activeTrue- Execute
browser_source = browser_source == BROWSER_SPIFFS ? BROWSER_SD_CARD : BROWSER_SPIFFS; - Execute
strcpy(current_path, browserRoot()); - Execute
scanDirectory(); - Exit this function
return;
FalseContinue after this decision.
- Decision
running && select && !preview_pending && !preview_activeTrue- Execute
openSelected();
FalseContinue after this decision.
Read original function source
void sdCardTestAction(bool select, bool toggle_source, uint32_t now_ms)
{
(void)now_ms;
if (TF_CARD_ENABLED && running && toggle_source && !preview_pending && !preview_active) {
browser_source = browser_source == BROWSER_SPIFFS ? BROWSER_SD_CARD : BROWSER_SPIFFS;
strcpy(current_path, browserRoot());
scanDirectory();
return;
}
if (running && select && !preview_pending && !preview_active) openSelected();
}updateSdCardTest() · source line 274
void updateSdCardTest(uint16_t *frame, int y_offset, bool joystick_valid, uint32_t now_ms)
Calls into this documentation:
- Decision
!running || !scan_okTrue- Exit this function
return;
FalseContinue after this decision.
- Decision
preview_pendingTrue- Execute
preview_pending = false; - Decision
imageDecodeFile(preview_path, frame)True- Execute
preview_active = true; - Execute
preview_ends_at = now_ms + 2000U;
False- Execute
snprintf(status_text, sizeof(status_text), "IMAGE FAILED: %.28s", preview_path);
- Exit this function
return;
FalseContinue after this decision.
- Decision
preview_activeTrue- Decision
now_ms < preview_ends_atTrue- Exit this function
return;
FalseContinue after this decision.
- Execute
preview_active = false;
FalseContinue after this decision.
- Decision
!joystick_validTrue- Execute
waiting_for_centre = true; - Execute
held_direction = 0; - Exit this function
return;
FalseContinue after this decision.
- Execute
const int magnitude = y_offset < 0 ? -y_offset : y_offset; - Decision
magnitude <= JOYSTICK_RELEASETrue- Execute
waiting_for_centre = false; - Execute
held_direction = 0; - Execute
repeating = false; - Exit this function
return;
FalseContinue after this decision.
- Decision
waiting_for_centre || magnitude < JOYSTICK_ENTERTrue- Exit this function
return;
FalseContinue after this decision.
- Execute
const int direction = y_offset > 0 ? -1 : 1; - Execute
const unsigned count = displayedCount(); - Decision
count == 0True- Exit this function
return;
FalseContinue after this decision.
- Decision
direction == held_direction && now_ms - last_move < (repeating ? REPEAT_MS : REPEAT_START_MS)True- Exit this function
return;
FalseContinue after this decision.
- Execute
repeating = direction == held_direction; - Execute
held_direction = direction; - Execute
last_move = now_ms; - Decision
direction < 0 && selected_entry > 0True- Execute
--selected_entry;
FalseContinue after this decision.
- Decision
direction > 0 && selected_entry + 1U < countTrue- Execute
++selected_entry;
FalseContinue after this decision.
Read original function source
void updateSdCardTest(uint16_t *frame, int y_offset, bool joystick_valid, uint32_t now_ms)
{
if (!running || !scan_ok) return;
if (preview_pending) {
preview_pending = false;
if (imageDecodeFile(preview_path, frame)) {
preview_active = true;
preview_ends_at = now_ms + 2000U;
} else {
snprintf(status_text, sizeof(status_text), "IMAGE FAILED: %.28s", preview_path);
}
return;
}
if (preview_active) {
if (now_ms < preview_ends_at) return;
preview_active = false;
}
if (!joystick_valid) { waiting_for_centre = true; held_direction = 0; return; }
const int magnitude = y_offset < 0 ? -y_offset : y_offset;
if (magnitude <= JOYSTICK_RELEASE) {
waiting_for_centre = false; held_direction = 0; repeating = false; return;
}
if (waiting_for_centre || magnitude < JOYSTICK_ENTER) return;
const int direction = y_offset > 0 ? -1 : 1;
const unsigned count = displayedCount();
if (count == 0) return;
if (direction == held_direction && now_ms - last_move < (repeating ? REPEAT_MS : REPEAT_START_MS)) return;
repeating = direction == held_direction;
held_direction = direction;
last_move = now_ms;
if (direction < 0 && selected_entry > 0) --selected_entry;
if (direction > 0 && selected_entry + 1U < count) ++selected_entry;
}drawSdCardTest() · source line 308
void drawSdCardTest(uint16_t *frame)
Calls into this documentation:
- Decision
!running || frame == NULLTrue- Exit this function
return;
FalseContinue after this decision.
- Decision
preview_activeTrue- Exit this function
return;
FalseContinue after this decision.
- Execute
clear(frame, COLOR_BLACK); - Execute
draw_text_centred(frame, 160, 4, browser_source == BROWSER_SPIFFS ? "SPIFFS BROWSER" : "SD CARD BROWSER", 2, COLOR_CYAN); - Execute
char path_label[27]; - Execute
const char *visible_path = strlen(current_path) > 25U ? current_path + strlen(current_path) - 25U : current_path; - Execute
snprintf(path_label, sizeof(path_label), "PATH: %.20s", visible_path); - Execute
draw_text(frame, 8, 27, path_label, 1, COLOR_WHITE); - Execute
draw_text(frame, 8, 39, status_text, 1, scan_ok ? COLOR_GREEN : COLOR_RED); - Execute
draw_text(frame, 8, 51, space_text, 1, space_ok ? COLOR_LIGHTGRAY : COLOR_RED); - Decision
scan_okTrue- Execute
const unsigned count = displayedCount(); - Execute
const unsigned first = selected_entry / VISIBLE_ENTRIES * VISIBLE_ENTRIES; - FOR loop
unsigned display = first; display < count && display < first + VISIBLE_ENTRIES; ++displayRepeat body while the loop condition permits; then continue below.
- Execute
const bool selected = display == selected_entry; - Execute
const int position = (int)(display - first); - Execute
const int x = position < 5 ? 8 : 166; - Execute
const int y = BROWSER_FIRST_ROW_Y + (position % 5) * BROWSER_ROW_SPACING; - Execute
const bool parent = !atRoot() && display == 0; - Execute
const directory_entry_t *entry = parent ? NULL : &entries[display - (atRoot() ? 0U : 1U)]; - Execute
char label[ENTRY_TEXT_CHARS + 5]; - Decision
parentTrue- Execute
snprintf(label, sizeof(label), "[D] ..");
False- Execute
snprintf(label, sizeof(label), "%s%.18s", entry->directory ? "[D] " : " ", entry->name);
- Execute
draw_text(frame, x, y, label, 1, selected ? COLOR_CYAN : COLOR_YELLOW);
FalseContinue after this decision.
- Execute
draw_text_centred(frame, 160, 156, TF_CARD_ENABLED ? "UP/DOWN A OPEN C SOURCE BOOT BACK" : "UP/DOWN A OPEN BOOT BACK", 1, COLOR_LIGHTGRAY);
Read original function source
void drawSdCardTest(uint16_t *frame)
{
if (!running || frame == NULL) return;
if (preview_active) return;
clear(frame, COLOR_BLACK);
draw_text_centred(frame, 160, 4,
browser_source == BROWSER_SPIFFS ? "SPIFFS BROWSER" : "SD CARD BROWSER",
2, COLOR_CYAN);
char path_label[27];
const char *visible_path = strlen(current_path) > 25U ? current_path + strlen(current_path) - 25U : current_path;
snprintf(path_label, sizeof(path_label), "PATH: %.20s", visible_path);
draw_text(frame, 8, 27, path_label, 1, COLOR_WHITE);
draw_text(frame, 8, 39, status_text, 1, scan_ok ? COLOR_GREEN : COLOR_RED);
draw_text(frame, 8, 51, space_text, 1, space_ok ? COLOR_LIGHTGRAY : COLOR_RED);
if (scan_ok) {
const unsigned count = displayedCount();
const unsigned first = selected_entry / VISIBLE_ENTRIES * VISIBLE_ENTRIES;
for (unsigned display = first; display < count && display < first + VISIBLE_ENTRIES; ++display) {
const bool selected = display == selected_entry;
const int position = (int)(display - first);
const int x = position < 5 ? 8 : 166;
const int y = BROWSER_FIRST_ROW_Y + (position % 5) * BROWSER_ROW_SPACING;
const bool parent = !atRoot() && display == 0;
const directory_entry_t *entry = parent ? NULL : &entries[display - (atRoot() ? 0U : 1U)];
char label[ENTRY_TEXT_CHARS + 5];
if (parent) snprintf(label, sizeof(label), "[D] ..");
else snprintf(label, sizeof(label), "%s%.18s", entry->directory ? "[D] " : " ", entry->name);
draw_text(frame, x, y, label, 1, selected ? COLOR_CYAN : COLOR_YELLOW);
}
}
draw_text_centred(frame, 160, 156,
TF_CARD_ENABLED ? "UP/DOWN A OPEN C SOURCE BOOT BACK" : "UP/DOWN A OPEN BOOT BACK",
1, COLOR_LIGHTGRAY);
}Complete original file, including constants and data
/* Coded By JD(2026) */
#error "Reference listing only — not provided as build-ready source."
#include "sd_card_test.h"
#include "game_graphics.h"
#include "PET2001_font.h"
#include "hardware_features.h"
#include "image_decoder.h"
#include "esp_err.h"
#include "esp_spiffs.h"
#include "esp_vfs_fat.h"
#include <dirent.h>
#include <errno.h>
#include <inttypes.h>
#include <limits.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#define SD_ROOT "/sdcard"
#define SPIFFS_ROOT "/spiffs"
#define SPIFFS_PARTITION "assets"
#define MAX_DIRECTORY_ENTRIES 64
#define ENTRY_TEXT_CHARS 40
#define VISIBLE_ENTRIES 10
#define BROWSER_FIRST_ROW_Y 67
#define BROWSER_ROW_SPACING 15
#define JOYSTICK_ENTER 600
#define JOYSTICK_RELEASE 350
#define REPEAT_START_MS 400U
#define REPEAT_MS 150U
typedef struct {
char name[ENTRY_TEXT_CHARS + 1];
uint32_t size;
bool directory;
} directory_entry_t;
typedef enum { BROWSER_SD_CARD, BROWSER_SPIFFS } browser_source_t;
static directory_entry_t entries[MAX_DIRECTORY_ENTRIES];
static char current_path[PATH_MAX];
static char status_text[48], space_text[48];
static unsigned entry_count, selected_entry;
static uint64_t total_bytes, free_bytes;
static uint32_t last_move;
static int held_direction;
static char preview_path[PATH_MAX];
static uint32_t preview_ends_at;
static bool running, scan_ok, space_ok, waiting_for_centre, repeating, preview_pending, preview_active;
static browser_source_t browser_source;
static const char *browserRoot(void)
{
return browser_source == BROWSER_SPIFFS ? SPIFFS_ROOT : SD_ROOT;
}
static bool atRoot(void) { return strcmp(current_path, browserRoot()) == 0; }
static unsigned displayedCount(void) { return entry_count + (atRoot() ? 0U : 1U); }
static void sortEntries(void)
{
for (unsigned i = 0; i < entry_count; ++i) for (unsigned j = i + 1U; j < entry_count; ++j) {
if (strcmp(entries[i].name, entries[j].name) <= 0) continue;
directory_entry_t swap = entries[i]; entries[i] = entries[j]; entries[j] = swap;
}
}
static void addEntry(const char *name, uint32_t size, bool directory)
{
const size_t name_length = strlen(name);
if (name_length == 0U || name_length > ENTRY_TEXT_CHARS) return;
for (unsigned index = 0; index < entry_count; ++index) {
if (strcmp(entries[index].name, name) != 0) continue;
entries[index].directory |= directory;
return;
}
if (entry_count >= MAX_DIRECTORY_ENTRIES) return;
directory_entry_t *entry = &entries[entry_count++];
memcpy(entry->name, name, name_length + 1U);
entry->size = size;
entry->directory = directory;
}
static void scanSdCard(void)
{
const esp_err_t space_result = esp_vfs_fat_info(SD_ROOT, &total_bytes, &free_bytes);
space_ok = space_result == ESP_OK;
if (space_ok) {
snprintf(space_text, sizeof(space_text), "TOTAL %" PRIu64 " MB FREE %" PRIu64 " MB",
total_bytes / (1024U * 1024U), free_bytes / (1024U * 1024U));
} else {
snprintf(space_text, sizeof(space_text), "SPACE ERROR: %.32s", esp_err_to_name(space_result));
}
errno = 0;
DIR *directory = opendir(current_path);
if (directory == NULL) {
snprintf(status_text, sizeof(status_text), "OPEN ERROR: %.34s", strerror(errno));
scan_ok = false;
return;
}
int read_error = 0;
for (;;) {
errno = 0;
struct dirent *entry = readdir(directory);
if (entry == NULL) { read_error = errno; break; }
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) continue;
const size_t name_length = strlen(entry->d_name);
if (name_length > ENTRY_TEXT_CHARS) continue;
char path[PATH_MAX];
const int written = snprintf(path, sizeof(path), "%s/%s", current_path, entry->d_name);
if (written < 0 || (size_t)written >= sizeof(path)) continue;
struct stat information;
if (stat(path, &information) != 0) continue;
addEntry(entry->d_name, (uint32_t)information.st_size, S_ISDIR(information.st_mode));
}
closedir(directory);
if (read_error != 0) {
snprintf(status_text, sizeof(status_text), "READ ERROR: %.34s", strerror(read_error));
scan_ok = false;
return;
}
sortEntries();
snprintf(status_text, sizeof(status_text), "READ OK: %u ITEMS", entry_count);
scan_ok = true;
}
static void scanSpiffs(void)
{
size_t total = 0, used = 0;
const esp_err_t space_result = esp_spiffs_info(SPIFFS_PARTITION, &total, &used);
space_ok = space_result == ESP_OK;
if (space_ok) {
total_bytes = total;
free_bytes = total >= used ? total - used : 0U;
snprintf(space_text, sizeof(space_text), "TOTAL %u MB FREE %u MB",
(unsigned)(total_bytes / (1024U * 1024U)),
(unsigned)(free_bytes / (1024U * 1024U)));
} else {
snprintf(space_text, sizeof(space_text), "SPACE ERROR: %.32s", esp_err_to_name(space_result));
}
const char *virtual_path = current_path + strlen(SPIFFS_ROOT);
while (*virtual_path == '/') ++virtual_path;
const size_t prefix_length = strlen(virtual_path);
DIR *directory = opendir(SPIFFS_ROOT);
if (directory == NULL) {
snprintf(status_text, sizeof(status_text), "OPEN ERROR: %.34s", strerror(errno));
scan_ok = false;
return;
}
int read_error = 0;
for (;;) {
errno = 0;
struct dirent *entry = readdir(directory);
if (entry == NULL) { read_error = errno; break; }
const char *full_name = entry->d_name;
while (*full_name == '/') ++full_name;
if (*full_name == '\0') continue;
const char *visible_name = full_name;
if (prefix_length != 0U) {
if (strncmp(full_name, virtual_path, prefix_length) != 0 ||
full_name[prefix_length] != '/') continue;
visible_name = full_name + prefix_length + 1U;
}
const char *separator = strchr(visible_name, '/');
if (separator != NULL) {
char directory_name[ENTRY_TEXT_CHARS + 1];
const size_t length = (size_t)(separator - visible_name);
if (length == 0U || length > ENTRY_TEXT_CHARS) continue;
memcpy(directory_name, visible_name, length);
directory_name[length] = '\0';
addEntry(directory_name, 0U, true);
continue;
}
char path[PATH_MAX];
const int written = snprintf(path, sizeof(path), "%s/%s", SPIFFS_ROOT, full_name);
if (written < 0 || (size_t)written >= sizeof(path)) continue;
struct stat information;
if (stat(path, &information) != 0) continue;
addEntry(visible_name, (uint32_t)information.st_size, false);
}
closedir(directory);
if (read_error != 0) {
snprintf(status_text, sizeof(status_text), "READ ERROR: %.34s", strerror(read_error));
scan_ok = false;
return;
}
sortEntries();
snprintf(status_text, sizeof(status_text), "READ OK: %u ITEMS", entry_count);
scan_ok = true;
}
static void scanDirectory(void)
{
entry_count = selected_entry = 0;
memset(entries, 0, sizeof(entries));
if (browser_source == BROWSER_SPIFFS) scanSpiffs();
else scanSdCard();
}
static void goToParent(void)
{
char *last_slash = strrchr(current_path, '/');
if (last_slash == NULL || last_slash == current_path) return;
*last_slash = '\0';
scanDirectory();
}
static void openSelected(void)
{
if (!scan_ok || displayedCount() == 0) return;
if (!atRoot() && selected_entry == 0) { goToParent(); return; }
const unsigned index = selected_entry - (atRoot() ? 0U : 1U);
if (index >= entry_count) return;
const directory_entry_t *entry = &entries[index];
if (!entry->directory) {
if (imageIsSupportedName(entry->name)) {
const int written = snprintf(preview_path, sizeof(preview_path), "%s/%s", current_path, entry->name);
if (written >= 0 && (size_t)written < sizeof(preview_path)) {
preview_pending = true;
snprintf(status_text, sizeof(status_text), "OPENING IMAGE: %.26s", entry->name);
} else {
snprintf(status_text, sizeof(status_text), "PATH TOO LONG");
}
return;
}
snprintf(status_text, sizeof(status_text), "FILE: %.32s %" PRIu32 " B", entry->name, entry->size);
return;
}
const size_t path_length = strlen(current_path);
const size_t name_length = strlen(entry->name);
if (path_length + 1U + name_length >= sizeof(current_path)) {
snprintf(status_text, sizeof(status_text), "PATH TOO LONG");
return;
}
current_path[path_length] = '/';
memcpy(current_path + path_length + 1U, entry->name, name_length + 1U);
scanDirectory();
}
void startSdCardTest(uint32_t now_ms)
{
(void)now_ms;
browser_source = TF_CARD_ENABLED ? BROWSER_SD_CARD : BROWSER_SPIFFS;
strcpy(current_path, browserRoot());
running = true;
waiting_for_centre = true;
held_direction = 0;
repeating = false;
preview_pending = false;
preview_active = false;
scanDirectory();
}
void stopSdCardTest(void)
{
running = false;
preview_pending = false;
preview_active = false;
}
void sdCardTestAction(bool select, bool toggle_source, uint32_t now_ms)
{
(void)now_ms;
if (TF_CARD_ENABLED && running && toggle_source && !preview_pending && !preview_active) {
browser_source = browser_source == BROWSER_SPIFFS ? BROWSER_SD_CARD : BROWSER_SPIFFS;
strcpy(current_path, browserRoot());
scanDirectory();
return;
}
if (running && select && !preview_pending && !preview_active) openSelected();
}
void updateSdCardTest(uint16_t *frame, int y_offset, bool joystick_valid, uint32_t now_ms)
{
if (!running || !scan_ok) return;
if (preview_pending) {
preview_pending = false;
if (imageDecodeFile(preview_path, frame)) {
preview_active = true;
preview_ends_at = now_ms + 2000U;
} else {
snprintf(status_text, sizeof(status_text), "IMAGE FAILED: %.28s", preview_path);
}
return;
}
if (preview_active) {
if (now_ms < preview_ends_at) return;
preview_active = false;
}
if (!joystick_valid) { waiting_for_centre = true; held_direction = 0; return; }
const int magnitude = y_offset < 0 ? -y_offset : y_offset;
if (magnitude <= JOYSTICK_RELEASE) {
waiting_for_centre = false; held_direction = 0; repeating = false; return;
}
if (waiting_for_centre || magnitude < JOYSTICK_ENTER) return;
const int direction = y_offset > 0 ? -1 : 1;
const unsigned count = displayedCount();
if (count == 0) return;
if (direction == held_direction && now_ms - last_move < (repeating ? REPEAT_MS : REPEAT_START_MS)) return;
repeating = direction == held_direction;
held_direction = direction;
last_move = now_ms;
if (direction < 0 && selected_entry > 0) --selected_entry;
if (direction > 0 && selected_entry + 1U < count) ++selected_entry;
}
void drawSdCardTest(uint16_t *frame)
{
if (!running || frame == NULL) return;
if (preview_active) return;
clear(frame, COLOR_BLACK);
drawPet2001TextCentredTransparent(frame, 160, 4,
browser_source == BROWSER_SPIFFS ? "SPIFFS BROWSER" : "SD CARD BROWSER",
COLOR_CYAN, 2);
char path_label[27];
const char *visible_path = strlen(current_path) > 25U ? current_path + strlen(current_path) - 25U : current_path;
snprintf(path_label, sizeof(path_label), "PATH: %.20s", visible_path);
drawPet2001TextTransparent(frame, 8, 27, path_label, COLOR_WHITE, 1);
drawPet2001TextTransparent(frame, 8, 39, status_text, scan_ok ? COLOR_GREEN : COLOR_RED, 1);
drawPet2001TextTransparent(frame, 8, 51, space_text, space_ok ? COLOR_LIGHTGRAY : COLOR_RED, 1);
if (scan_ok) {
const unsigned count = displayedCount();
const unsigned first = selected_entry / VISIBLE_ENTRIES * VISIBLE_ENTRIES;
for (unsigned display = first; display < count && display < first + VISIBLE_ENTRIES; ++display) {
const bool selected = display == selected_entry;
const int position = (int)(display - first);
const int x = position < 5 ? 8 : 166;
const int y = BROWSER_FIRST_ROW_Y + (position % 5) * BROWSER_ROW_SPACING;
const bool parent = !atRoot() && display == 0;
const directory_entry_t *entry = parent ? NULL : &entries[display - (atRoot() ? 0U : 1U)];
char label[ENTRY_TEXT_CHARS + 5];
if (parent) snprintf(label, sizeof(label), "[D] ..");
else snprintf(label, sizeof(label), "%s%.18s", entry->directory ? "[D] " : " ", entry->name);
drawPet2001TextTransparent(frame, x, y, label, selected ? COLOR_CYAN : COLOR_YELLOW, 1);
}
}
drawPet2001TextCentredTransparent(frame, 160, 156,
TF_CARD_ENABLED ? "UP/DOWN A OPEN C SOURCE BOOT BACK" : "UP/DOWN A OPEN BOOT BACK",
COLOR_LIGHTGRAY, 1);
}