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)
  1. Exit this functionreturn 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:

  1. Exit this functionreturn 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:

  1. Exit this functionreturn 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)
  1. FOR loopunsigned i = 0; i < entry_count; ++i

    Repeat body while the loop condition permits; then continue below.

    1. FOR loopunsigned j = i + 1U; j < entry_count; ++j

      Repeat body while the loop condition permits; then continue below.

        1. Decisionstrcmp(entries[i].name, entries[j].name) <= 0
          True
          1. Next loop iterationcontinue;
          False

          Continue after this decision.

        2. Executedirectory_entry_t swap = entries[i];
        3. Executeentries[i] = entries[j];
        4. Executeentries[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)
  1. Executeconst size_t name_length = strlen(name);
  2. Decisionname_length == 0U || name_length > ENTRY_TEXT_CHARS
    True
    1. Exit this functionreturn;
    False

    Continue after this decision.

  3. FOR loopunsigned index = 0; index < entry_count; ++index

    Repeat body while the loop condition permits; then continue below.

      1. Decisionstrcmp(entries[index].name, name) != 0
        True
        1. Next loop iterationcontinue;
        False

        Continue after this decision.

      2. Executeentries[index].directory |= directory;
      3. Exit this functionreturn;
  4. Decisionentry_count >= MAX_DIRECTORY_ENTRIES
    True
    1. Exit this functionreturn;
    False

    Continue after this decision.

  5. Executedirectory_entry_t *entry = &entries[entry_count++];
  6. Executememcpy(entry->name, name, name_length + 1U);
  7. Executeentry->size = size;
  8. Executeentry->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:

  1. Executeconst esp_err_t space_result = esp_vfs_fat_info(SD_ROOT, &total_bytes, &free_bytes);
  2. Executespace_ok = space_result == ESP_OK;
  3. Decisionspace_ok
    True
      1. Executesnprintf(space_text, sizeof(space_text), "TOTAL %" PRIu64 " MB FREE %" PRIu64 " MB", total_bytes / (1024U * 1024U), free_bytes / (1024U * 1024U));
    False
      1. Executesnprintf(space_text, sizeof(space_text), "SPACE ERROR: %.32s", esp_err_to_name(space_result));
  4. Executeerrno = 0;
  5. ExecuteDIR *directory = opendir(current_path);
  6. Decisiondirectory == NULL
    True
      1. Executesnprintf(status_text, sizeof(status_text), "OPEN ERROR: %.34s", strerror(errno));
      2. Executescan_ok = false;
      3. Exit this functionreturn;
    False

    Continue after this decision.

  7. Executeint read_error = 0;
  8. FOR loop;;

    Repeat body while the loop condition permits; then continue below.

      1. Executeerrno = 0;
      2. Executestruct dirent *entry = readdir(directory);
      3. Decisionentry == NULL
        True
          1. Executeread_error = errno;
          2. Exit enclosing loop or switchbreak;
        False

        Continue after this decision.

      4. Decisionstrcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0
        True
        1. Next loop iterationcontinue;
        False

        Continue after this decision.

      5. Executeconst size_t name_length = strlen(entry->d_name);
      6. Decisionname_length > ENTRY_TEXT_CHARS
        True
        1. Next loop iterationcontinue;
        False

        Continue after this decision.

      7. Executechar path[PATH_MAX];
      8. Executeconst int written = snprintf(path, sizeof(path), "%s/%s", current_path, entry->d_name);
      9. Decisionwritten < 0 || (size_t)written >= sizeof(path)
        True
        1. Next loop iterationcontinue;
        False

        Continue after this decision.

      10. Executestruct stat information;
      11. Decisionstat(path, &information) != 0
        True
        1. Next loop iterationcontinue;
        False

        Continue after this decision.

      12. ExecuteaddEntry(entry->d_name, (uint32_t)information.st_size, S_ISDIR(information.st_mode));
  9. Executeclosedir(directory);
  10. Decisionread_error != 0
    True
      1. Executesnprintf(status_text, sizeof(status_text), "READ ERROR: %.34s", strerror(read_error));
      2. Executescan_ok = false;
      3. Exit this functionreturn;
    False

    Continue after this decision.

  11. ExecutesortEntries();
  12. Executesnprintf(status_text, sizeof(status_text), "READ OK: %u ITEMS", entry_count);
  13. Executescan_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:

  1. Executesize_t total = 0, used = 0;
  2. Executeconst esp_err_t space_result = esp_spiffs_info(SPIFFS_PARTITION, &total, &used);
  3. Executespace_ok = space_result == ESP_OK;
  4. Decisionspace_ok
    True
      1. Executetotal_bytes = total;
      2. Executefree_bytes = total >= used ? total - used : 0U;
      3. Executesnprintf(space_text, sizeof(space_text), "TOTAL %u MB FREE %u MB", (unsigned)(total_bytes / (1024U * 1024U)), (unsigned)(free_bytes / (1024U * 1024U)));
    False
      1. Executesnprintf(space_text, sizeof(space_text), "SPACE ERROR: %.32s", esp_err_to_name(space_result));
  5. Executeconst char *virtual_path = current_path + strlen(SPIFFS_ROOT);
  6. WHILE loop*virtual_path == '/'

    Repeat body while the loop condition permits; then continue below.

    1. Execute++virtual_path;
  7. Executeconst size_t prefix_length = strlen(virtual_path);
  8. ExecuteDIR *directory = opendir(SPIFFS_ROOT);
  9. Decisiondirectory == NULL
    True
      1. Executesnprintf(status_text, sizeof(status_text), "OPEN ERROR: %.34s", strerror(errno));
      2. Executescan_ok = false;
      3. Exit this functionreturn;
    False

    Continue after this decision.

  10. Executeint read_error = 0;
  11. FOR loop;;

    Repeat body while the loop condition permits; then continue below.

      1. Executeerrno = 0;
      2. Executestruct dirent *entry = readdir(directory);
      3. Decisionentry == NULL
        True
          1. Executeread_error = errno;
          2. Exit enclosing loop or switchbreak;
        False

        Continue after this decision.

      4. Executeconst char *full_name = entry->d_name;
      5. WHILE loop*full_name == '/'

        Repeat body while the loop condition permits; then continue below.

        1. Execute++full_name;
      6. Decision*full_name == '\0'
        True
        1. Next loop iterationcontinue;
        False

        Continue after this decision.

      7. Executeconst char *visible_name = full_name;
      8. Decisionprefix_length != 0U
        True
          1. Decisionstrncmp(full_name, virtual_path, prefix_length) != 0 || full_name[prefix_length] != '/'
            True
            1. Next loop iterationcontinue;
            False

            Continue after this decision.

          2. Executevisible_name = full_name + prefix_length + 1U;
        False

        Continue after this decision.

      9. Executeconst char *separator = strchr(visible_name, '/');
      10. Decisionseparator != NULL
        True
          1. Executechar directory_name[ENTRY_TEXT_CHARS + 1];
          2. Executeconst size_t length = (size_t)(separator - visible_name);
          3. Decisionlength == 0U || length > ENTRY_TEXT_CHARS
            True
            1. Next loop iterationcontinue;
            False

            Continue after this decision.

          4. Executememcpy(directory_name, visible_name, length);
          5. Executedirectory_name[length] = '\0';
          6. ExecuteaddEntry(directory_name, 0U, true);
          7. Next loop iterationcontinue;
        False

        Continue after this decision.

      11. Executechar path[PATH_MAX];
      12. Executeconst int written = snprintf(path, sizeof(path), "%s/%s", SPIFFS_ROOT, full_name);
      13. Decisionwritten < 0 || (size_t)written >= sizeof(path)
        True
        1. Next loop iterationcontinue;
        False

        Continue after this decision.

      14. Executestruct stat information;
      15. Decisionstat(path, &information) != 0
        True
        1. Next loop iterationcontinue;
        False

        Continue after this decision.

      16. ExecuteaddEntry(visible_name, (uint32_t)information.st_size, false);
  12. Executeclosedir(directory);
  13. Decisionread_error != 0
    True
      1. Executesnprintf(status_text, sizeof(status_text), "READ ERROR: %.34s", strerror(read_error));
      2. Executescan_ok = false;
      3. Exit this functionreturn;
    False

    Continue after this decision.

  14. ExecutesortEntries();
  15. Executesnprintf(status_text, sizeof(status_text), "READ OK: %u ITEMS", entry_count);
  16. Executescan_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:

  1. Executeentry_count = selected_entry = 0;
  2. Executememset(entries, 0, sizeof(entries));
  3. Decisionbrowser_source == BROWSER_SPIFFS
    True
    1. ExecutescanSpiffs();
    False
    1. ExecutescanSdCard();
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:

  1. Executechar *last_slash = strrchr(current_path, '/');
  2. Decisionlast_slash == NULL || last_slash == current_path
    True
    1. Exit this functionreturn;
    False

    Continue after this decision.

  3. Execute*last_slash = '\0';
  4. ExecutescanDirectory();
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:

  1. Decision!scan_ok || displayedCount() == 0
    True
    1. Exit this functionreturn;
    False

    Continue after this decision.

  2. Decision!atRoot() && selected_entry == 0
    True
      1. ExecutegoToParent();
      2. Exit this functionreturn;
    False

    Continue after this decision.

  3. Executeconst unsigned index = selected_entry - (atRoot() ? 0U : 1U);
  4. Decisionindex >= entry_count
    True
    1. Exit this functionreturn;
    False

    Continue after this decision.

  5. Executeconst directory_entry_t *entry = &entries[index];
  6. Decision!entry->directory
    True
      1. DecisionimageIsSupportedName(entry->name)
        True
          1. Executeconst int written = snprintf(preview_path, sizeof(preview_path), "%s/%s", current_path, entry->name);
          2. Decisionwritten >= 0 && (size_t)written < sizeof(preview_path)
            True
              1. Executepreview_pending = true;
              2. Executesnprintf(status_text, sizeof(status_text), "OPENING IMAGE: %.26s", entry->name);
            False
              1. Executesnprintf(status_text, sizeof(status_text), "PATH TOO LONG");
          3. Exit this functionreturn;
        False

        Continue after this decision.

      2. Executesnprintf(status_text, sizeof(status_text), "FILE: %.32s %" PRIu32 " B", entry->name, entry->size);
      3. Exit this functionreturn;
    False

    Continue after this decision.

  7. Executeconst size_t path_length = strlen(current_path);
  8. Executeconst size_t name_length = strlen(entry->name);
  9. Decisionpath_length + 1U + name_length >= sizeof(current_path)
    True
      1. Executesnprintf(status_text, sizeof(status_text), "PATH TOO LONG");
      2. Exit this functionreturn;
    False

    Continue after this decision.

  10. Executecurrent_path[path_length] = '/';
  11. Executememcpy(current_path + path_length + 1U, entry->name, name_length + 1U);
  12. ExecutescanDirectory();
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:

  1. Execute(void)now_ms;
  2. Executebrowser_source = TF_CARD_ENABLED ? BROWSER_SD_CARD : BROWSER_SPIFFS;
  3. Executestrcpy(current_path, browserRoot());
  4. Executerunning = true;
  5. Executewaiting_for_centre = true;
  6. Executeheld_direction = 0;
  7. Executerepeating = false;
  8. Executepreview_pending = false;
  9. Executepreview_active = false;
  10. ExecutescanDirectory();
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)
  1. Executerunning = false;
  2. Executepreview_pending = false;
  3. Executepreview_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:

  1. Execute(void)now_ms;
  2. DecisionTF_CARD_ENABLED && running && toggle_source && !preview_pending && !preview_active
    True
      1. Executebrowser_source = browser_source == BROWSER_SPIFFS ? BROWSER_SD_CARD : BROWSER_SPIFFS;
      2. Executestrcpy(current_path, browserRoot());
      3. ExecutescanDirectory();
      4. Exit this functionreturn;
    False

    Continue after this decision.

  3. Decisionrunning && select && !preview_pending && !preview_active
    True
    1. ExecuteopenSelected();
    False

    Continue 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:

  1. Decision!running || !scan_ok
    True
    1. Exit this functionreturn;
    False

    Continue after this decision.

  2. Decisionpreview_pending
    True
      1. Executepreview_pending = false;
      2. DecisionimageDecodeFile(preview_path, frame)
        True
          1. Executepreview_active = true;
          2. Executepreview_ends_at = now_ms + 2000U;
        False
          1. Executesnprintf(status_text, sizeof(status_text), "IMAGE FAILED: %.28s", preview_path);
      3. Exit this functionreturn;
    False

    Continue after this decision.

  3. Decisionpreview_active
    True
      1. Decisionnow_ms < preview_ends_at
        True
        1. Exit this functionreturn;
        False

        Continue after this decision.

      2. Executepreview_active = false;
    False

    Continue after this decision.

  4. Decision!joystick_valid
    True
      1. Executewaiting_for_centre = true;
      2. Executeheld_direction = 0;
      3. Exit this functionreturn;
    False

    Continue after this decision.

  5. Executeconst int magnitude = y_offset < 0 ? -y_offset : y_offset;
  6. Decisionmagnitude <= JOYSTICK_RELEASE
    True
      1. Executewaiting_for_centre = false;
      2. Executeheld_direction = 0;
      3. Executerepeating = false;
      4. Exit this functionreturn;
    False

    Continue after this decision.

  7. Decisionwaiting_for_centre || magnitude < JOYSTICK_ENTER
    True
    1. Exit this functionreturn;
    False

    Continue after this decision.

  8. Executeconst int direction = y_offset > 0 ? -1 : 1;
  9. Executeconst unsigned count = displayedCount();
  10. Decisioncount == 0
    True
    1. Exit this functionreturn;
    False

    Continue after this decision.

  11. Decisiondirection == held_direction && now_ms - last_move < (repeating ? REPEAT_MS : REPEAT_START_MS)
    True
    1. Exit this functionreturn;
    False

    Continue after this decision.

  12. Executerepeating = direction == held_direction;
  13. Executeheld_direction = direction;
  14. Executelast_move = now_ms;
  15. Decisiondirection < 0 && selected_entry > 0
    True
    1. Execute--selected_entry;
    False

    Continue after this decision.

  16. Decisiondirection > 0 && selected_entry + 1U < count
    True
    1. Execute++selected_entry;
    False

    Continue 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:

  1. Decision!running || frame == NULL
    True
    1. Exit this functionreturn;
    False

    Continue after this decision.

  2. Decisionpreview_active
    True
    1. Exit this functionreturn;
    False

    Continue after this decision.

  3. Executeclear(frame, COLOR_BLACK);
  4. Executedraw_text_centred(frame, 160, 4, browser_source == BROWSER_SPIFFS ? "SPIFFS BROWSER" : "SD CARD BROWSER", 2, COLOR_CYAN);
  5. Executechar path_label[27];
  6. Executeconst char *visible_path = strlen(current_path) > 25U ? current_path + strlen(current_path) - 25U : current_path;
  7. Executesnprintf(path_label, sizeof(path_label), "PATH: %.20s", visible_path);
  8. Executedraw_text(frame, 8, 27, path_label, 1, COLOR_WHITE);
  9. Executedraw_text(frame, 8, 39, status_text, 1, scan_ok ? COLOR_GREEN : COLOR_RED);
  10. Executedraw_text(frame, 8, 51, space_text, 1, space_ok ? COLOR_LIGHTGRAY : COLOR_RED);
  11. Decisionscan_ok
    True
      1. Executeconst unsigned count = displayedCount();
      2. Executeconst unsigned first = selected_entry / VISIBLE_ENTRIES * VISIBLE_ENTRIES;
      3. FOR loopunsigned display = first; display < count && display < first + VISIBLE_ENTRIES; ++display

        Repeat body while the loop condition permits; then continue below.

          1. Executeconst bool selected = display == selected_entry;
          2. Executeconst int position = (int)(display - first);
          3. Executeconst int x = position < 5 ? 8 : 166;
          4. Executeconst int y = BROWSER_FIRST_ROW_Y + (position % 5) * BROWSER_ROW_SPACING;
          5. Executeconst bool parent = !atRoot() && display == 0;
          6. Executeconst directory_entry_t *entry = parent ? NULL : &entries[display - (atRoot() ? 0U : 1U)];
          7. Executechar label[ENTRY_TEXT_CHARS + 5];
          8. Decisionparent
            True
            1. Executesnprintf(label, sizeof(label), "[D] ..");
            False
            1. Executesnprintf(label, sizeof(label), "%s%.18s", entry->directory ? "[D] " : " ", entry->name);
          9. Executedraw_text(frame, x, y, label, 1, selected ? COLOR_CYAN : COLOR_YELLOW);
    False

    Continue after this decision.

  12. Executedraw_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);
}