Resource files

Source: src/dungeon_files.c · 90 lines · 2 functions.

Module responsibility

Initialises the Dungeon resource folder and installs required files when they are missing.

See how this module fits into the complete Dungeon system →

Module entry points

These functions are exposed to the rest of the Dungeon system. Open one to follow its decisions and helper calls.

How to read these diagrams

Each function has an expandable structural diagram derived from the current C source. Decisions split into True and False, loop bodies are indented, and arrows show statement order.

These are source diagrams before preprocessing, not runtime traces. Callback execution, task scheduling and macro expansion are not inferred.

Functions in this file

copyIfMissing() · source line 24
static bool copyIfMissing(const DungeonFileMigration *migration)
  1. ExecuteFILE *destination = fopen(migration->new_path, "rb");
  2. Decisiondestination != NULL
    True
      1. Executefclose(destination);
      2. Exit this functionreturn true;
    False

    Continue after this decision.

  3. ExecuteFILE *source = fopen(migration->old_path, "rb");
  4. Decisionsource == NULL
    True
    1. Exit this functionreturn true;
    False

    Continue after this decision.

  5. Executechar temporary_path[48];
  6. Executeconst int path_length = snprintf(temporary_path, sizeof(temporary_path), "%s.tmp", migration->new_path);
  7. Decisionpath_length <= 0 || (size_t)path_length >= sizeof(temporary_path)
    True
      1. Executefclose(source);
      2. Exit this functionreturn false;
    False

    Continue after this decision.

  8. Executedestination = fopen(temporary_path, "wb");
  9. Decisiondestination == NULL
    True
      1. Executefclose(source);
      2. Exit this functionreturn false;
    False

    Continue after this decision.

  10. Executebool copied = true;
  11. Executeuint8_t buffer[256];
  12. Executesize_t count;
  13. WHILE loop(count = fread(buffer, 1, sizeof(buffer), source)) > 0U

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

      1. Decisionfwrite(buffer, 1, count, destination) != count
        True
          1. Executecopied = false;
          2. Exit enclosing loop or switchbreak;
        False

        Continue after this decision.

  14. Decisionferror(source) != 0
    True
    1. Executecopied = false;
    False

    Continue after this decision.

  15. Executefclose(destination);
  16. Executefclose(source);
  17. Decision!copied
    True
      1. Executeremove(temporary_path);
      2. Exit this functionreturn false;
    False

    Continue after this decision.

  18. Decisionrename(temporary_path, migration->new_path) != 0
    True
      1. Executeremove(temporary_path);
      2. Exit this functionreturn false;
    False

    Continue after this decision.

  19. ExecuteESP_LOGI(TAG, "Copied %s to internal storage", migration->old_path);
  20. Exit this functionreturn copied;
Read original function source
static bool copyIfMissing(const DungeonFileMigration *migration)
{
    FILE *destination = fopen(migration->new_path, "rb");
    if (destination != NULL) {
        fclose(destination);
        return true;
    }

    FILE *source = fopen(migration->old_path, "rb");
    if (source == NULL) return true;
    char temporary_path[48];
    const int path_length = snprintf(temporary_path, sizeof(temporary_path), "%s.tmp", migration->new_path);
    if (path_length <= 0 || (size_t)path_length >= sizeof(temporary_path)) {
        fclose(source);
        return false;
    }
    destination = fopen(temporary_path, "wb");
    if (destination == NULL) {
        fclose(source);
        return false;
    }

    bool copied = true;
    uint8_t buffer[256];
    size_t count;
    while ((count = fread(buffer, 1, sizeof(buffer), source)) > 0U) {
        if (fwrite(buffer, 1, count, destination) != count) {
            copied = false;
            break;
        }
    }
    if (ferror(source) != 0) copied = false;
    fclose(destination);
    fclose(source);
    if (!copied) {
        remove(temporary_path);
        return false;
    }
    if (rename(temporary_path, migration->new_path) != 0) {
        remove(temporary_path);
        return false;
    }
    ESP_LOGI(TAG, "Copied %s to internal storage", migration->old_path);
    return copied;
}
dungeonFilesInitialize() · source line 70
bool dungeonFilesInitialize(bool sd_available)

Calls into this documentation:

  1. Executeconst esp_vfs_spiffs_conf_t config = { .base_path = DUNGEON_DATA_MOUNT, .partition_label = DUNGEON_DATA_PARTITION, .max_files = 5, .format_if_mount_failed = false, };
  2. Executeconst esp_err_t result = esp_vfs_spiffs_register(&config);
  3. Decisionresult != ESP_OK
    True
      1. ExecuteESP_LOGE(TAG, "SPIFFS mount failed: %s", esp_err_to_name(result));
      2. Exit this functionreturn false;
    False

    Continue after this decision.

  4. Decision!sd_available
    True
    1. Exit this functionreturn true;
    False

    Continue after this decision.

  5. FOR loopunsigned index = 0U; index < sizeof(migrations) / sizeof(migrations[0]); ++index

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

    1. Decision!copyIfMissing(&migrations[index])
      True
        1. ExecuteESP_LOGE(TAG, "Could not migrate %s", migrations[index].old_path);
        2. Exit this functionreturn false;
      False

      Continue after this decision.

  6. Exit this functionreturn true;
Read original function source
bool dungeonFilesInitialize(bool sd_available)
{
    const esp_vfs_spiffs_conf_t config = {
        .base_path = DUNGEON_DATA_MOUNT,
        .partition_label = DUNGEON_DATA_PARTITION,
        .max_files = 5,
        .format_if_mount_failed = false,
    };
    const esp_err_t result = esp_vfs_spiffs_register(&config);
    if (result != ESP_OK) {
        ESP_LOGE(TAG, "SPIFFS mount failed: %s", esp_err_to_name(result));
        return false;
    }
    if (!sd_available) return true;
    for (unsigned index = 0U; index < sizeof(migrations) / sizeof(migrations[0]); ++index)
        if (!copyIfMissing(&migrations[index])) {
            ESP_LOGE(TAG, "Could not migrate %s", migrations[index].old_path);
            return false;
        }
    return true;
}
Complete original file, including constants and data
/* Coded By JD(2026) */
#error "Reference listing only — not provided as build-ready source."
#include "dungeon_files.h"
#include "esp_log.h"
#include "esp_spiffs.h"
#include <stdio.h>
#include <stdint.h>

#define DUNGEON_DATA_MOUNT "/spiffs"
#define DUNGEON_DATA_PARTITION "assets"

static const char *TAG = "dungeon_files";

typedef struct {
    const char *old_path;
    const char *new_path;
} DungeonFileMigration;

static const DungeonFileMigration migrations[] = {
    { "/sdcard/dungeon/dungeon.dat", DUNGEON_DATA_MOUNT "/dungeon.dat" },
    { "/sdcard/dungeon/player.dat", DUNGEON_DATA_MOUNT "/dungeon_player.dat" },
    { "/sdcard/dungeon/explored.dat", DUNGEON_DATA_MOUNT "/dungeon_explored.dat" },
    { "/sdcard/dungeon/highscores.dat", DUNGEON_DATA_MOUNT "/dungeon_highscores.dat" },
};

static bool copyIfMissing(const DungeonFileMigration *migration)
{
    FILE *destination = fopen(migration->new_path, "rb");
    if (destination != NULL) {
        fclose(destination);
        return true;
    }

    FILE *source = fopen(migration->old_path, "rb");
    if (source == NULL) return true;
    char temporary_path[48];
    const int path_length = snprintf(temporary_path, sizeof(temporary_path), "%s.tmp", migration->new_path);
    if (path_length <= 0 || (size_t)path_length >= sizeof(temporary_path)) {
        fclose(source);
        return false;
    }
    destination = fopen(temporary_path, "wb");
    if (destination == NULL) {
        fclose(source);
        return false;
    }

    bool copied = true;
    uint8_t buffer[256];
    size_t count;
    while ((count = fread(buffer, 1, sizeof(buffer), source)) > 0U) {
        if (fwrite(buffer, 1, count, destination) != count) {
            copied = false;
            break;
        }
    }
    if (ferror(source) != 0) copied = false;
    fclose(destination);
    fclose(source);
    if (!copied) {
        remove(temporary_path);
        return false;
    }
    if (rename(temporary_path, migration->new_path) != 0) {
        remove(temporary_path);
        return false;
    }
    ESP_LOGI(TAG, "Copied %s to internal storage", migration->old_path);
    return copied;
}

bool dungeonFilesInitialize(bool sd_available)
{
    const esp_vfs_spiffs_conf_t config = {
        .base_path = DUNGEON_DATA_MOUNT,
        .partition_label = DUNGEON_DATA_PARTITION,
        .max_files = 5,
        .format_if_mount_failed = false,
    };
    const esp_err_t result = esp_vfs_spiffs_register(&config);
    if (result != ESP_OK) {
        ESP_LOGE(TAG, "SPIFFS mount failed: %s", esp_err_to_name(result));
        return false;
    }
    if (!sd_available) return true;
    for (unsigned index = 0U; index < sizeof(migrations) / sizeof(migrations[0]); ++index)
        if (!copyIfMissing(&migrations[index])) {
            ESP_LOGE(TAG, "Could not migrate %s", migrations[index].old_path);
            return false;
        }
    return true;
}