Explored rooms

Source: src/dungeon_explored.c · 202 lines · 20 functions.

Module responsibility

Tracks which map cells have been visited and persists the exploration record with validation and checksums.

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

updateChecksum() · source line 18
static void updateChecksum(uint32_t *checksum, uint8_t value)
  1. Execute*checksum = (*checksum ^ value) * CHECKSUM_PRIME;
Read original function source
static void updateChecksum(uint32_t *checksum, uint8_t value) { *checksum = (*checksum ^ value) * CHECKSUM_PRIME; }
updateChecksumU32() · source line 19
static void updateChecksumU32(uint32_t *checksum, uint32_t value)

Calls into this documentation:

  1. ExecuteupdateChecksum(checksum, (uint8_t)value);
  2. ExecuteupdateChecksum(checksum, (uint8_t)(value >> 8));
  3. ExecuteupdateChecksum(checksum, (uint8_t)(value >> 16));
  4. ExecuteupdateChecksum(checksum, (uint8_t)(value >> 24));
Read original function source
static void updateChecksumU32(uint32_t *checksum, uint32_t value)
{
    updateChecksum(checksum, (uint8_t)value); updateChecksum(checksum, (uint8_t)(value >> 8));
    updateChecksum(checksum, (uint8_t)(value >> 16)); updateChecksum(checksum, (uint8_t)(value >> 24));
}
writeByte() · source line 25
static bool writeByte(FILE *file, uint8_t value, DungeonExploredResult *result)
  1. Decisionfwrite(&value, 1, 1, file) != 1U
    True
    1. Exit this functionreturn false;
    False

    Continue after this decision.

  2. Execute++result->bytes;
  3. Exit this functionreturn true;
Read original function source
static bool writeByte(FILE *file, uint8_t value, DungeonExploredResult *result)
{
    if (fwrite(&value, 1, 1, file) != 1U) return false;
    ++result->bytes;
    return true;
}
writeU16() · source line 32
static bool writeU16(FILE *file, uint16_t value, DungeonExploredResult *result)

Calls into this documentation:

  1. Exit this functionreturn writeByte(file, (uint8_t)value, result) && writeByte(file, (uint8_t)(value >> 8), result);
Read original function source
static bool writeU16(FILE *file, uint16_t value, DungeonExploredResult *result)
{
    return writeByte(file, (uint8_t)value, result) && writeByte(file, (uint8_t)(value >> 8), result);
}
writeU32() · source line 37
static bool writeU32(FILE *file, uint32_t value, DungeonExploredResult *result)

Calls into this documentation:

  1. Exit this functionreturn writeByte(file, (uint8_t)value, result) && writeByte(file, (uint8_t)(value >> 8), result) && writeByte(file, (uint8_t)(value >> 16), result) && writeByte(file, (uint8_t)(value >> 24), result);
Read original function source
static bool writeU32(FILE *file, uint32_t value, DungeonExploredResult *result)
{
    return writeByte(file, (uint8_t)value, result) && writeByte(file, (uint8_t)(value >> 8), result) &&
           writeByte(file, (uint8_t)(value >> 16), result) && writeByte(file, (uint8_t)(value >> 24), result);
}
readByte() · source line 43
static bool readByte(FILE *file, uint8_t *value, DungeonExploredResult *result)
  1. Decisionfread(value, 1, 1, file) != 1U
    True
    1. Exit this functionreturn false;
    False

    Continue after this decision.

  2. Execute++result->bytes;
  3. Exit this functionreturn true;
Read original function source
static bool readByte(FILE *file, uint8_t *value, DungeonExploredResult *result)
{
    if (fread(value, 1, 1, file) != 1U) return false;
    ++result->bytes;
    return true;
}
readU16() · source line 50
static bool readU16(FILE *file, uint16_t *value, DungeonExploredResult *result)

Calls into this documentation:

  1. Executeuint8_t low, high;
  2. Decision!readByte(file, &low, result) || !readByte(file, &high, result)
    True
    1. Exit this functionreturn false;
    False

    Continue after this decision.

  3. Execute*value = (uint16_t)low | ((uint16_t)high << 8);
  4. Exit this functionreturn true;
Read original function source
static bool readU16(FILE *file, uint16_t *value, DungeonExploredResult *result)
{
    uint8_t low, high;
    if (!readByte(file, &low, result) || !readByte(file, &high, result)) return false;
    *value = (uint16_t)low | ((uint16_t)high << 8);
    return true;
}
readU32() · source line 58
static bool readU32(FILE *file, uint32_t *value, DungeonExploredResult *result)

Calls into this documentation:

  1. Executeuint8_t b0, b1, b2, b3;
  2. Decision!readByte(file, &b0, result) || !readByte(file, &b1, result) || !readByte(file, &b2, result) || !readByte(file, &b3, result)
    True
    1. Exit this functionreturn false;
    False

    Continue after this decision.

  3. Execute*value = (uint32_t)b0 | ((uint32_t)b1 << 8) | ((uint32_t)b2 << 16) | ((uint32_t)b3 << 24);
  4. Exit this functionreturn true;
Read original function source
static bool readU32(FILE *file, uint32_t *value, DungeonExploredResult *result)
{
    uint8_t b0, b1, b2, b3;
    if (!readByte(file, &b0, result) || !readByte(file, &b1, result) || !readByte(file, &b2, result) || !readByte(file, &b3, result)) return false;
    *value = (uint32_t)b0 | ((uint32_t)b1 << 8) | ((uint32_t)b2 << 16) | ((uint32_t)b3 << 24);
    return true;
}
validPosition() · source line 66
static bool validPosition(const DungeonPosition *position)
  1. Exit this functionreturn position != NULL && position->floor < DUNGEON_FLOOR_COUNT && position->x < DUNGEON_WIDTH && position->y < DUNGEON_HEIGHT;
Read original function source
static bool validPosition(const DungeonPosition *position)
{
    return position != NULL && position->floor < DUNGEON_FLOOR_COUNT && position->x < DUNGEON_WIDTH && position->y < DUNGEON_HEIGHT;
}
exploredIndex() · source line 71
static uint16_t exploredIndex(uint8_t x, uint8_t y)
  1. Exit this functionreturn (uint16_t)y * DUNGEON_WIDTH + x;
Read original function source
static uint16_t exploredIndex(uint8_t x, uint8_t y) { return (uint16_t)y * DUNGEON_WIDTH + x; }
setExplored() · source line 73
static void setExplored(const DungeonPosition *position)

Calls into this documentation:

  1. Executeconst uint16_t index = exploredIndex(position->x, position->y);
  2. Executeexplored[position->floor][index / 8U] |= (uint8_t)(1U << (index % 8U));
Read original function source
static void setExplored(const DungeonPosition *position)
{
    const uint16_t index = exploredIndex(position->x, position->y);
    explored[position->floor][index / 8U] |= (uint8_t)(1U << (index % 8U));
}
calculateChecksum() · source line 79
static uint32_t calculateChecksum(uint32_t world_checksum)

Calls into this documentation:

  1. Executeuint32_t checksum = CHECKSUM_START;
  2. ExecuteupdateChecksumU32(&checksum, world_checksum);
  3. FOR loopunsigned floor = 0; floor < DUNGEON_FLOOR_COUNT; ++floor

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

    1. FOR loopunsigned index = 0; index < EXPLORED_BYTES_PER_FLOOR; ++index

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

      1. ExecuteupdateChecksum(&checksum, explored[floor][index]);
  4. Exit this functionreturn checksum;
Read original function source
static uint32_t calculateChecksum(uint32_t world_checksum)
{
    uint32_t checksum = CHECKSUM_START;
    updateChecksumU32(&checksum, world_checksum);
    for (unsigned floor = 0; floor < DUNGEON_FLOOR_COUNT; ++floor)
        for (unsigned index = 0; index < EXPLORED_BYTES_PER_FLOOR; ++index)
            updateChecksum(&checksum, explored[floor][index]);
    return checksum;
}
saveExplored() · source line 89
static bool saveExplored(DungeonExploredResult *result)

Calls into this documentation:

  1. Executememset(result, 0, sizeof(*result));
  2. Executeresult->error = DUNGEON_EXPLORED_OPEN_WRITE_FAILED;
  3. Executeerrno = 0;
  4. ExecuteFILE *file = fopen(EXPLORED_FILE, "wb");
  5. Decisionfile == NULL
    True
      1. Executeresult->errno_value = errno;
      2. Exit this functionreturn false;
    False

    Continue after this decision.

  6. Executeconst uint8_t magic[] = { 'M', 'E', 'X', 'P' };
  7. Executeconst uint32_t world_checksum = getDungeonWorldChecksum();
  8. Executeconst uint32_t checksum = calculateChecksum(world_checksum);
  9. Executebool written = fwrite(magic, 1, sizeof(magic), file) == sizeof(magic);
  10. Executeresult->bytes += written ? sizeof(magic) : 0U;
  11. Executewritten = written && writeU16(file, EXPLORED_VERSION, result) && writeU16(file, EXPLORED_PAYLOAD_SIZE, result) && writeU32(file, checksum, result) && writeU32(file, world_checksum, result) && fwrite(explored, 1, EXPLORED_BYTES, file) == EXPLORED_BYTES;
  12. Decisionwritten
    True
    1. Executeresult->bytes += EXPLORED_BYTES;
    False

    Continue after this decision.

  13. Executeconst int close_result = fclose(file);
  14. Decision!written || close_result != 0 || result->bytes != EXPLORED_FILE_SIZE
    True
      1. Executeresult->error = DUNGEON_EXPLORED_WRITE_FAILED;
      2. Executeresult->errno_value = errno;
      3. Exit this functionreturn false;
    False

    Continue after this decision.

  15. Executeresult->error = DUNGEON_EXPLORED_OK;
  16. Exit this functionreturn true;
Read original function source
static bool saveExplored(DungeonExploredResult *result)
{
    memset(result, 0, sizeof(*result));
    result->error = DUNGEON_EXPLORED_OPEN_WRITE_FAILED;
    errno = 0;
    FILE *file = fopen(EXPLORED_FILE, "wb");
    if (file == NULL) { result->errno_value = errno; return false; }
    const uint8_t magic[] = { 'M', 'E', 'X', 'P' };
    const uint32_t world_checksum = getDungeonWorldChecksum();
    const uint32_t checksum = calculateChecksum(world_checksum);
    bool written = fwrite(magic, 1, sizeof(magic), file) == sizeof(magic);
    result->bytes += written ? sizeof(magic) : 0U;
    written = written && writeU16(file, EXPLORED_VERSION, result) && writeU16(file, EXPLORED_PAYLOAD_SIZE, result) &&
              writeU32(file, checksum, result) && writeU32(file, world_checksum, result) &&
              fwrite(explored, 1, EXPLORED_BYTES, file) == EXPLORED_BYTES;
    if (written) result->bytes += EXPLORED_BYTES;
    const int close_result = fclose(file);
    if (!written || close_result != 0 || result->bytes != EXPLORED_FILE_SIZE) { result->error = DUNGEON_EXPLORED_WRITE_FAILED; result->errno_value = errno; return false; }
    result->error = DUNGEON_EXPLORED_OK;
    return true;
}
loadExplored() · source line 111
static bool loadExplored(DungeonExploredResult *result)

Calls into this documentation:

  1. Executememset(result, 0, sizeof(*result));
  2. Executeresult->error = DUNGEON_EXPLORED_OPEN_READ_FAILED;
  3. ExecuteFILE *file = fopen(EXPLORED_FILE, "rb");
  4. Decisionfile == NULL
    True
    1. Exit this functionreturn false;
    False

    Continue after this decision.

  5. Decisionfseek(file, 0L, SEEK_END) != 0 || ftell(file) != (long)EXPLORED_FILE_SIZE
    True
      1. Executeresult->error = DUNGEON_EXPLORED_BAD_FILE_SIZE;
      2. Executefclose(file);
      3. Exit this functionreturn false;
    False

    Continue after this decision.

  6. Executerewind(file);
  7. Executeuint8_t magic[4];
  8. Executeuint16_t version, payload;
  9. Executeuint32_t checksum, world_checksum;
  10. Executebool read = fread(magic, 1, sizeof(magic), file) == sizeof(magic);
  11. Executeresult->bytes += read ? sizeof(magic) : 0U;
  12. Executeread = read && readU16(file, &version, result) && readU16(file, &payload, result) && readU32(file, &checksum, result) && readU32(file, &world_checksum, result) && fread(explored, 1, EXPLORED_BYTES, file) == EXPLORED_BYTES;
  13. Decisionread
    True
    1. Executeresult->bytes += EXPLORED_BYTES;
    False

    Continue after this decision.

  14. Executefclose(file);
  15. Decision!read || result->bytes != EXPLORED_FILE_SIZE
    True
      1. Executeresult->error = DUNGEON_EXPLORED_READ_FAILED;
      2. Exit this functionreturn false;
    False

    Continue after this decision.

  16. Decisionmemcmp(magic, "MEXP", sizeof(magic)) != 0
    True
      1. Executeresult->error = DUNGEON_EXPLORED_BAD_SIGNATURE;
      2. Exit this functionreturn false;
    False

    Continue after this decision.

  17. Decisionversion != EXPLORED_VERSION
    True
      1. Executeresult->error = DUNGEON_EXPLORED_BAD_VERSION;
      2. Exit this functionreturn false;
    False

    Continue after this decision.

  18. Decisionpayload != EXPLORED_PAYLOAD_SIZE
    True
      1. Executeresult->error = DUNGEON_EXPLORED_BAD_FILE_SIZE;
      2. Exit this functionreturn false;
    False

    Continue after this decision.

  19. Decisionworld_checksum != getDungeonWorldChecksum()
    True
      1. Executeresult->error = DUNGEON_EXPLORED_WRONG_DUNGEON;
      2. Exit this functionreturn false;
    False

    Continue after this decision.

  20. Decisionchecksum != calculateChecksum(world_checksum)
    True
      1. Executeresult->error = DUNGEON_EXPLORED_CHECKSUM_FAILED;
      2. Exit this functionreturn false;
    False

    Continue after this decision.

  21. Executeresult->error = DUNGEON_EXPLORED_OK;
  22. Exit this functionreturn true;
Read original function source
static bool loadExplored(DungeonExploredResult *result)
{
    memset(result, 0, sizeof(*result));
    result->error = DUNGEON_EXPLORED_OPEN_READ_FAILED;
    FILE *file = fopen(EXPLORED_FILE, "rb");
    if (file == NULL) return false;
    if (fseek(file, 0L, SEEK_END) != 0 || ftell(file) != (long)EXPLORED_FILE_SIZE) { result->error = DUNGEON_EXPLORED_BAD_FILE_SIZE; fclose(file); return false; }
    rewind(file);
    uint8_t magic[4]; uint16_t version, payload; uint32_t checksum, world_checksum;
    bool read = fread(magic, 1, sizeof(magic), file) == sizeof(magic);
    result->bytes += read ? sizeof(magic) : 0U;
    read = read && readU16(file, &version, result) && readU16(file, &payload, result) && readU32(file, &checksum, result) && readU32(file, &world_checksum, result) &&
           fread(explored, 1, EXPLORED_BYTES, file) == EXPLORED_BYTES;
    if (read) result->bytes += EXPLORED_BYTES;
    fclose(file);
    if (!read || result->bytes != EXPLORED_FILE_SIZE) { result->error = DUNGEON_EXPLORED_READ_FAILED; return false; }
    if (memcmp(magic, "MEXP", sizeof(magic)) != 0) { result->error = DUNGEON_EXPLORED_BAD_SIGNATURE; return false; }
    if (version != EXPLORED_VERSION) { result->error = DUNGEON_EXPLORED_BAD_VERSION; return false; }
    if (payload != EXPLORED_PAYLOAD_SIZE) { result->error = DUNGEON_EXPLORED_BAD_FILE_SIZE; return false; }
    if (world_checksum != getDungeonWorldChecksum()) { result->error = DUNGEON_EXPLORED_WRONG_DUNGEON; return false; }
    if (checksum != calculateChecksum(world_checksum)) { result->error = DUNGEON_EXPLORED_CHECKSUM_FAILED; return false; }
    result->error = DUNGEON_EXPLORED_OK;
    return true;
}
initDungeonExplored() · source line 136
void initDungeonExplored(void)
  1. Executememset(explored, 0, sizeof(explored));
Read original function source
void initDungeonExplored(void) { memset(explored, 0, sizeof(explored)); }
prepareDungeonExplored() · source line 138
bool prepareDungeonExplored(const DungeonPosition *position, DungeonExploredResult *result)

Calls into this documentation:

  1. Decisionresult == NULL || !validPosition(position)
    True
      1. Decisionresult != NULL
        True
          1. Executememset(result, 0, sizeof(*result));
          2. Executeresult->error = DUNGEON_EXPLORED_INVALID_POSITION;
        False

        Continue after this decision.

      2. Exit this functionreturn false;
    False

    Continue after this decision.

  2. DecisionloadExplored(result)
    True
      1. DecisionisDungeonExplored(position->floor, position->x, position->y)
        True
        1. Exit this functionreturn true;
        False

        Continue after this decision.

      2. ExecutesetExplored(position);
      3. Exit this functionreturn saveExplored(result);
    False

    Continue after this decision.

  3. Decisionresult->error != DUNGEON_EXPLORED_OPEN_READ_FAILED
    True
    1. Exit this functionreturn false;
    False

    Continue after this decision.

  4. ExecuteinitDungeonExplored();
  5. ExecutesetExplored(position);
  6. Decision!saveExplored(result)
    True
    1. Exit this functionreturn false;
    False

    Continue after this decision.

  7. Executeresult->created = true;
  8. Exit this functionreturn true;
Read original function source
bool prepareDungeonExplored(const DungeonPosition *position, DungeonExploredResult *result)
{
    if (result == NULL || !validPosition(position)) {
        if (result != NULL) { memset(result, 0, sizeof(*result)); result->error = DUNGEON_EXPLORED_INVALID_POSITION; }
        return false;
    }
    if (loadExplored(result)) {
        if (isDungeonExplored(position->floor, position->x, position->y)) return true;
        setExplored(position);
        return saveExplored(result);
    }
    if (result->error != DUNGEON_EXPLORED_OPEN_READ_FAILED) return false;
    initDungeonExplored();
    setExplored(position);
    if (!saveExplored(result)) return false;
    result->created = true;
    return true;
}
resetDungeonExplored() · source line 157
bool resetDungeonExplored(const DungeonPosition *position, DungeonExploredResult *result)

Calls into this documentation:

  1. Decisionresult == NULL || !validPosition(position)
    True
      1. Decisionresult != NULL
        True
          1. Executememset(result, 0, sizeof(*result));
          2. Executeresult->error = DUNGEON_EXPLORED_INVALID_POSITION;
        False

        Continue after this decision.

      2. Exit this functionreturn false;
    False

    Continue after this decision.

  2. ExecuteinitDungeonExplored();
  3. ExecutesetExplored(position);
  4. Exit this functionreturn saveExplored(result);
Read original function source
bool resetDungeonExplored(const DungeonPosition *position, DungeonExploredResult *result)
{
    if (result == NULL || !validPosition(position)) {
        if (result != NULL) { memset(result, 0, sizeof(*result)); result->error = DUNGEON_EXPLORED_INVALID_POSITION; }
        return false;
    }
    initDungeonExplored();
    setExplored(position);
    return saveExplored(result);
}
markDungeonExplored() · source line 168
bool markDungeonExplored(const DungeonPosition *position, DungeonExploredResult *result)

Calls into this documentation:

  1. Decisionresult == NULL || !validPosition(position)
    True
      1. Decisionresult != NULL
        True
          1. Executememset(result, 0, sizeof(*result));
          2. Executeresult->error = DUNGEON_EXPLORED_INVALID_POSITION;
        False

        Continue after this decision.

      2. Exit this functionreturn false;
    False

    Continue after this decision.

  2. DecisionisDungeonExplored(position->floor, position->x, position->y)
    True
      1. Executememset(result, 0, sizeof(*result));
      2. Executeresult->error = DUNGEON_EXPLORED_OK;
      3. Exit this functionreturn true;
    False

    Continue after this decision.

  3. ExecutesetExplored(position);
  4. Exit this functionreturn saveExplored(result);
Read original function source
bool markDungeonExplored(const DungeonPosition *position, DungeonExploredResult *result)
{
    if (result == NULL || !validPosition(position)) {
        if (result != NULL) { memset(result, 0, sizeof(*result)); result->error = DUNGEON_EXPLORED_INVALID_POSITION; }
        return false;
    }
    if (isDungeonExplored(position->floor, position->x, position->y)) { memset(result, 0, sizeof(*result)); result->error = DUNGEON_EXPLORED_OK; return true; }
    setExplored(position);
    return saveExplored(result);
}
isDungeonExplored() · source line 179
bool isDungeonExplored(uint8_t floor, uint8_t x, uint8_t y)

Calls into this documentation:

  1. Decisionfloor >= DUNGEON_FLOOR_COUNT || x >= DUNGEON_WIDTH || y >= DUNGEON_HEIGHT
    True
    1. Exit this functionreturn false;
    False

    Continue after this decision.

  2. Executeconst uint16_t index = exploredIndex(x, y);
  3. Exit this functionreturn (explored[floor][index / 8U] & (uint8_t)(1U << (index % 8U))) != 0U;
Read original function source
bool isDungeonExplored(uint8_t floor, uint8_t x, uint8_t y)
{
    if (floor >= DUNGEON_FLOOR_COUNT || x >= DUNGEON_WIDTH || y >= DUNGEON_HEIGHT) return false;
    const uint16_t index = exploredIndex(x, y);
    return (explored[floor][index / 8U] & (uint8_t)(1U << (index % 8U))) != 0U;
}
dungeonExploredErrorText() · source line 186
const char *dungeonExploredErrorText(DungeonExploredError error)
  1. Select a caseerror

    Enter matching case; follow source fall-through until a break, return or end.

      1. Case / labelcase DUNGEON_EXPLORED_OK:
      2. Exit this functionreturn "OK";
      3. Case / labelcase DUNGEON_EXPLORED_OPEN_READ_FAILED:
      4. Exit this functionreturn "Cannot open explored map";
      5. Case / labelcase DUNGEON_EXPLORED_OPEN_WRITE_FAILED:
      6. Exit this functionreturn "Cannot create explored map";
      7. Case / labelcase DUNGEON_EXPLORED_READ_FAILED:
      8. Exit this functionreturn "Explored map read failed";
      9. Case / labelcase DUNGEON_EXPLORED_WRITE_FAILED:
      10. Exit this functionreturn "Explored map save failed";
      11. Case / labelcase DUNGEON_EXPLORED_BAD_SIGNATURE:
      12. Exit this functionreturn "Bad explored map signature";
      13. Case / labelcase DUNGEON_EXPLORED_BAD_VERSION:
      14. Exit this functionreturn "Bad explored map version";
      15. Case / labelcase DUNGEON_EXPLORED_BAD_FILE_SIZE:
      16. Exit this functionreturn "Bad explored map size";
      17. Case / labelcase DUNGEON_EXPLORED_CHECKSUM_FAILED:
      18. Exit this functionreturn "Explored map checksum failed";
      19. Case / labelcase DUNGEON_EXPLORED_WRONG_DUNGEON:
      20. Exit this functionreturn "Explored map is for another dungeon";
      21. Case / labelcase DUNGEON_EXPLORED_INVALID_POSITION:
      22. Exit this functionreturn "Bad explored map position";
      23. Case / labeldefault:
      24. Exit this functionreturn "Explored map error";
Read original function source
const char *dungeonExploredErrorText(DungeonExploredError error)
{
    switch (error) {
        case DUNGEON_EXPLORED_OK: return "OK";
        case DUNGEON_EXPLORED_OPEN_READ_FAILED: return "Cannot open explored map";
        case DUNGEON_EXPLORED_OPEN_WRITE_FAILED: return "Cannot create explored map";
        case DUNGEON_EXPLORED_READ_FAILED: return "Explored map read failed";
        case DUNGEON_EXPLORED_WRITE_FAILED: return "Explored map save failed";
        case DUNGEON_EXPLORED_BAD_SIGNATURE: return "Bad explored map signature";
        case DUNGEON_EXPLORED_BAD_VERSION: return "Bad explored map version";
        case DUNGEON_EXPLORED_BAD_FILE_SIZE: return "Bad explored map size";
        case DUNGEON_EXPLORED_CHECKSUM_FAILED: return "Explored map checksum failed";
        case DUNGEON_EXPLORED_WRONG_DUNGEON: return "Explored map is for another dungeon";
        case DUNGEON_EXPLORED_INVALID_POSITION: return "Bad explored map position";
        default: return "Explored map error";
    }
}
Complete original file, including constants and data
/* Coded By JD(2026) */
#error "Reference listing only — not provided as build-ready source."
#include "dungeon_explored.h"
#include "dungeon_storage.h"
#include <errno.h>
#include <stdio.h>
#include <string.h>

#define EXPLORED_FILE "/spiffs/dungeon_explored.dat"
#define EXPLORED_VERSION 2U
#define EXPLORED_BYTES_PER_FLOOR ((DUNGEON_WIDTH * DUNGEON_HEIGHT) / 8U)
#define EXPLORED_BYTES (EXPLORED_BYTES_PER_FLOOR * DUNGEON_FLOOR_COUNT)
#define EXPLORED_PAYLOAD_SIZE (4U + EXPLORED_BYTES)
#define EXPLORED_FILE_SIZE (12U + EXPLORED_PAYLOAD_SIZE)
#define CHECKSUM_START 2166136261UL
#define CHECKSUM_PRIME 16777619UL

static uint8_t explored[DUNGEON_FLOOR_COUNT][EXPLORED_BYTES_PER_FLOOR];

static void updateChecksum(uint32_t *checksum, uint8_t value) { *checksum = (*checksum ^ value) * CHECKSUM_PRIME; }
static void updateChecksumU32(uint32_t *checksum, uint32_t value)
{
    updateChecksum(checksum, (uint8_t)value); updateChecksum(checksum, (uint8_t)(value >> 8));
    updateChecksum(checksum, (uint8_t)(value >> 16)); updateChecksum(checksum, (uint8_t)(value >> 24));
}

static bool writeByte(FILE *file, uint8_t value, DungeonExploredResult *result)
{
    if (fwrite(&value, 1, 1, file) != 1U) return false;
    ++result->bytes;
    return true;
}

static bool writeU16(FILE *file, uint16_t value, DungeonExploredResult *result)
{
    return writeByte(file, (uint8_t)value, result) && writeByte(file, (uint8_t)(value >> 8), result);
}

static bool writeU32(FILE *file, uint32_t value, DungeonExploredResult *result)
{
    return writeByte(file, (uint8_t)value, result) && writeByte(file, (uint8_t)(value >> 8), result) &&
           writeByte(file, (uint8_t)(value >> 16), result) && writeByte(file, (uint8_t)(value >> 24), result);
}

static bool readByte(FILE *file, uint8_t *value, DungeonExploredResult *result)
{
    if (fread(value, 1, 1, file) != 1U) return false;
    ++result->bytes;
    return true;
}

static bool readU16(FILE *file, uint16_t *value, DungeonExploredResult *result)
{
    uint8_t low, high;
    if (!readByte(file, &low, result) || !readByte(file, &high, result)) return false;
    *value = (uint16_t)low | ((uint16_t)high << 8);
    return true;
}

static bool readU32(FILE *file, uint32_t *value, DungeonExploredResult *result)
{
    uint8_t b0, b1, b2, b3;
    if (!readByte(file, &b0, result) || !readByte(file, &b1, result) || !readByte(file, &b2, result) || !readByte(file, &b3, result)) return false;
    *value = (uint32_t)b0 | ((uint32_t)b1 << 8) | ((uint32_t)b2 << 16) | ((uint32_t)b3 << 24);
    return true;
}

static bool validPosition(const DungeonPosition *position)
{
    return position != NULL && position->floor < DUNGEON_FLOOR_COUNT && position->x < DUNGEON_WIDTH && position->y < DUNGEON_HEIGHT;
}

static uint16_t exploredIndex(uint8_t x, uint8_t y) { return (uint16_t)y * DUNGEON_WIDTH + x; }

static void setExplored(const DungeonPosition *position)
{
    const uint16_t index = exploredIndex(position->x, position->y);
    explored[position->floor][index / 8U] |= (uint8_t)(1U << (index % 8U));
}

static uint32_t calculateChecksum(uint32_t world_checksum)
{
    uint32_t checksum = CHECKSUM_START;
    updateChecksumU32(&checksum, world_checksum);
    for (unsigned floor = 0; floor < DUNGEON_FLOOR_COUNT; ++floor)
        for (unsigned index = 0; index < EXPLORED_BYTES_PER_FLOOR; ++index)
            updateChecksum(&checksum, explored[floor][index]);
    return checksum;
}

static bool saveExplored(DungeonExploredResult *result)
{
    memset(result, 0, sizeof(*result));
    result->error = DUNGEON_EXPLORED_OPEN_WRITE_FAILED;
    errno = 0;
    FILE *file = fopen(EXPLORED_FILE, "wb");
    if (file == NULL) { result->errno_value = errno; return false; }
    const uint8_t magic[] = { 'M', 'E', 'X', 'P' };
    const uint32_t world_checksum = getDungeonWorldChecksum();
    const uint32_t checksum = calculateChecksum(world_checksum);
    bool written = fwrite(magic, 1, sizeof(magic), file) == sizeof(magic);
    result->bytes += written ? sizeof(magic) : 0U;
    written = written && writeU16(file, EXPLORED_VERSION, result) && writeU16(file, EXPLORED_PAYLOAD_SIZE, result) &&
              writeU32(file, checksum, result) && writeU32(file, world_checksum, result) &&
              fwrite(explored, 1, EXPLORED_BYTES, file) == EXPLORED_BYTES;
    if (written) result->bytes += EXPLORED_BYTES;
    const int close_result = fclose(file);
    if (!written || close_result != 0 || result->bytes != EXPLORED_FILE_SIZE) { result->error = DUNGEON_EXPLORED_WRITE_FAILED; result->errno_value = errno; return false; }
    result->error = DUNGEON_EXPLORED_OK;
    return true;
}

static bool loadExplored(DungeonExploredResult *result)
{
    memset(result, 0, sizeof(*result));
    result->error = DUNGEON_EXPLORED_OPEN_READ_FAILED;
    FILE *file = fopen(EXPLORED_FILE, "rb");
    if (file == NULL) return false;
    if (fseek(file, 0L, SEEK_END) != 0 || ftell(file) != (long)EXPLORED_FILE_SIZE) { result->error = DUNGEON_EXPLORED_BAD_FILE_SIZE; fclose(file); return false; }
    rewind(file);
    uint8_t magic[4]; uint16_t version, payload; uint32_t checksum, world_checksum;
    bool read = fread(magic, 1, sizeof(magic), file) == sizeof(magic);
    result->bytes += read ? sizeof(magic) : 0U;
    read = read && readU16(file, &version, result) && readU16(file, &payload, result) && readU32(file, &checksum, result) && readU32(file, &world_checksum, result) &&
           fread(explored, 1, EXPLORED_BYTES, file) == EXPLORED_BYTES;
    if (read) result->bytes += EXPLORED_BYTES;
    fclose(file);
    if (!read || result->bytes != EXPLORED_FILE_SIZE) { result->error = DUNGEON_EXPLORED_READ_FAILED; return false; }
    if (memcmp(magic, "MEXP", sizeof(magic)) != 0) { result->error = DUNGEON_EXPLORED_BAD_SIGNATURE; return false; }
    if (version != EXPLORED_VERSION) { result->error = DUNGEON_EXPLORED_BAD_VERSION; return false; }
    if (payload != EXPLORED_PAYLOAD_SIZE) { result->error = DUNGEON_EXPLORED_BAD_FILE_SIZE; return false; }
    if (world_checksum != getDungeonWorldChecksum()) { result->error = DUNGEON_EXPLORED_WRONG_DUNGEON; return false; }
    if (checksum != calculateChecksum(world_checksum)) { result->error = DUNGEON_EXPLORED_CHECKSUM_FAILED; return false; }
    result->error = DUNGEON_EXPLORED_OK;
    return true;
}

void initDungeonExplored(void) { memset(explored, 0, sizeof(explored)); }

bool prepareDungeonExplored(const DungeonPosition *position, DungeonExploredResult *result)
{
    if (result == NULL || !validPosition(position)) {
        if (result != NULL) { memset(result, 0, sizeof(*result)); result->error = DUNGEON_EXPLORED_INVALID_POSITION; }
        return false;
    }
    if (loadExplored(result)) {
        if (isDungeonExplored(position->floor, position->x, position->y)) return true;
        setExplored(position);
        return saveExplored(result);
    }
    if (result->error != DUNGEON_EXPLORED_OPEN_READ_FAILED) return false;
    initDungeonExplored();
    setExplored(position);
    if (!saveExplored(result)) return false;
    result->created = true;
    return true;
}

bool resetDungeonExplored(const DungeonPosition *position, DungeonExploredResult *result)
{
    if (result == NULL || !validPosition(position)) {
        if (result != NULL) { memset(result, 0, sizeof(*result)); result->error = DUNGEON_EXPLORED_INVALID_POSITION; }
        return false;
    }
    initDungeonExplored();
    setExplored(position);
    return saveExplored(result);
}

bool markDungeonExplored(const DungeonPosition *position, DungeonExploredResult *result)
{
    if (result == NULL || !validPosition(position)) {
        if (result != NULL) { memset(result, 0, sizeof(*result)); result->error = DUNGEON_EXPLORED_INVALID_POSITION; }
        return false;
    }
    if (isDungeonExplored(position->floor, position->x, position->y)) { memset(result, 0, sizeof(*result)); result->error = DUNGEON_EXPLORED_OK; return true; }
    setExplored(position);
    return saveExplored(result);
}

bool isDungeonExplored(uint8_t floor, uint8_t x, uint8_t y)
{
    if (floor >= DUNGEON_FLOOR_COUNT || x >= DUNGEON_WIDTH || y >= DUNGEON_HEIGHT) return false;
    const uint16_t index = exploredIndex(x, y);
    return (explored[floor][index / 8U] & (uint8_t)(1U << (index % 8U))) != 0U;
}

const char *dungeonExploredErrorText(DungeonExploredError error)
{
    switch (error) {
        case DUNGEON_EXPLORED_OK: return "OK";
        case DUNGEON_EXPLORED_OPEN_READ_FAILED: return "Cannot open explored map";
        case DUNGEON_EXPLORED_OPEN_WRITE_FAILED: return "Cannot create explored map";
        case DUNGEON_EXPLORED_READ_FAILED: return "Explored map read failed";
        case DUNGEON_EXPLORED_WRITE_FAILED: return "Explored map save failed";
        case DUNGEON_EXPLORED_BAD_SIGNATURE: return "Bad explored map signature";
        case DUNGEON_EXPLORED_BAD_VERSION: return "Bad explored map version";
        case DUNGEON_EXPLORED_BAD_FILE_SIZE: return "Bad explored map size";
        case DUNGEON_EXPLORED_CHECKSUM_FAILED: return "Explored map checksum failed";
        case DUNGEON_EXPLORED_WRONG_DUNGEON: return "Explored map is for another dungeon";
        case DUNGEON_EXPLORED_INVALID_POSITION: return "Bad explored map position";
        default: return "Explored map error";
    }
}