World storage
Source: src/dungeon_storage.c · 286 lines · 18 functions.
Module responsibility
Loads and saves generated worlds, serialises positions and cells, validates checksums and deletes run data.
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
deleteDungeonDataFiles() · source line 29
bool deleteDungeonDataFiles(DungeonDeleteResult *result)
- Execute
static const char *const files[] = { DUNGEON_FILE, DUNGEON_PLAYER_FILE, DUNGEON_EXPLORED_FILE, DUNGEON_HISTORY_FILE, }; - Decision
result == NULLTrue- Exit this function
return false;
FalseContinue after this decision.
- Execute
memset(result, 0, sizeof(*result)); - Execute
result->error = DUNGEON_DELETE_OK; - FOR loop
unsigned index = 0U; index < sizeof(files) / sizeof(files[0]); ++indexRepeat body while the loop condition permits; then continue below.
- Execute
errno = 0; - Decision
remove(files[index]) == 0True- Execute
++result->deleted;
False- Decision
errno == ENOENTTrue- Execute
++result->missing;
False- Execute
result->error = DUNGEON_DELETE_FAILED; - Execute
result->errno_value = errno; - Execute
result->failed_path = files[index]; - Exit this function
return false;
- Exit this function
return true;
Read original function source
bool deleteDungeonDataFiles(DungeonDeleteResult *result)
{
static const char *const files[] = {
DUNGEON_FILE,
DUNGEON_PLAYER_FILE,
DUNGEON_EXPLORED_FILE,
DUNGEON_HISTORY_FILE,
};
if (result == NULL) return false;
memset(result, 0, sizeof(*result));
result->error = DUNGEON_DELETE_OK;
for (unsigned index = 0U; index < sizeof(files) / sizeof(files[0]); ++index) {
errno = 0;
if (remove(files[index]) == 0) {
++result->deleted;
/* ESP_LOGI("dungeon_storage", "DELETE %s OK", files[index]); */
} else if (errno == ENOENT) {
++result->missing;
/* ESP_LOGI("dungeon_storage", "DELETE %s MISSING", files[index]); */
} else {
result->error = DUNGEON_DELETE_FAILED;
result->errno_value = errno;
result->failed_path = files[index];
/* ESP_LOGI("dungeon_storage", "DELETE %s FAILED errno=%d", files[index], errno); */
return false;
}
}
/* ESP_LOGI("dungeon_storage", "DELETE COMPLETE deleted=%u missing=%u", (unsigned)result->deleted, (unsigned)result->missing); */
return true;
}prepareNewDungeonWorldStorage() · source line 60
void prepareNewDungeonWorldStorage(void)
- Execute
loaded_version = DUNGEON_FILE_VERSION; - Execute
memset(file_tail, 0, sizeof(file_tail));
Read original function source
void prepareNewDungeonWorldStorage(void)
{
loaded_version = DUNGEON_FILE_VERSION;
memset(file_tail, 0, sizeof(file_tail));
}readByte() · source line 66
static bool readByte(FILE *file, uint8_t *value, DungeonStorageResult *result)
- Decision
fread(value, 1, 1, file) != 1UTrue- Exit this function
return false;
FalseContinue after this decision.
- Execute
++result->bytes; - Exit this function
return true;
Read original function source
static bool readByte(FILE *file, uint8_t *value, DungeonStorageResult *result)
{
if (fread(value, 1, 1, file) != 1U) return false;
++result->bytes;
return true;
}readU16() · source line 73
static bool readU16(FILE *file, uint16_t *value, DungeonStorageResult *result)
- Execute
uint8_t bytes[2]; - Decision
fread(bytes, 1, sizeof(bytes), file) != sizeof(bytes)True- Exit this function
return false;
FalseContinue after this decision.
- Execute
result->bytes += sizeof(bytes); - Execute
*value = (uint16_t)bytes[0] | ((uint16_t)bytes[1] << 8); - Exit this function
return true;
Read original function source
static bool readU16(FILE *file, uint16_t *value, DungeonStorageResult *result)
{
uint8_t bytes[2];
if (fread(bytes, 1, sizeof(bytes), file) != sizeof(bytes)) return false;
result->bytes += sizeof(bytes);
*value = (uint16_t)bytes[0] | ((uint16_t)bytes[1] << 8);
return true;
}readU32() · source line 82
static bool readU32(FILE *file, uint32_t *value, DungeonStorageResult *result)
- Execute
uint8_t bytes[4]; - Decision
fread(bytes, 1, sizeof(bytes), file) != sizeof(bytes)True- Exit this function
return false;
FalseContinue after this decision.
- Execute
result->bytes += sizeof(bytes); - Execute
*value = (uint32_t)bytes[0] | ((uint32_t)bytes[1] << 8) | ((uint32_t)bytes[2] << 16) | ((uint32_t)bytes[3] << 24); - Exit this function
return true;
Read original function source
static bool readU32(FILE *file, uint32_t *value, DungeonStorageResult *result)
{
uint8_t bytes[4];
if (fread(bytes, 1, sizeof(bytes), file) != sizeof(bytes)) return false;
result->bytes += sizeof(bytes);
*value = (uint32_t)bytes[0] | ((uint32_t)bytes[1] << 8) | ((uint32_t)bytes[2] << 16) | ((uint32_t)bytes[3] << 24);
return true;
}writeByte() · source line 91
static bool writeByte(FILE *file, uint8_t value, DungeonStorageResult *result)
- Decision
fwrite(&value, 1, 1, file) != 1UTrue- Exit this function
return false;
FalseContinue after this decision.
- Execute
++result->bytes; - Exit this function
return true;
Read original function source
static bool writeByte(FILE *file, uint8_t value, DungeonStorageResult *result)
{
if (fwrite(&value, 1, 1, file) != 1U) return false;
++result->bytes;
return true;
}writeU16() · source line 98
static bool writeU16(FILE *file, uint16_t value, DungeonStorageResult *result)
Calls into this documentation:
- Exit this function
return 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, DungeonStorageResult *result)
{
return writeByte(file, (uint8_t)value, result) && writeByte(file, (uint8_t)(value >> 8), result);
}writeU32() · source line 103
static bool writeU32(FILE *file, uint32_t value, DungeonStorageResult *result)
Calls into this documentation:
- Exit this function
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);
Read original function source
static bool writeU32(FILE *file, uint32_t value, DungeonStorageResult *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);
}updateChecksum() · source line 109
static void updateChecksum(uint32_t *checksum, uint8_t value)
- Execute
*checksum ^= value; - Execute
*checksum *= CHECKSUM_PRIME;
Read original function source
static void updateChecksum(uint32_t *checksum, uint8_t value)
{
*checksum ^= value;
*checksum *= CHECKSUM_PRIME;
}readPayloadByte() · source line 115
static bool readPayloadByte(FILE *file, uint8_t *value, DungeonStorageResult *result, uint32_t *checksum)
Calls into this documentation:
- Exit this function
return readByte(file, value, result) ? (updateChecksum(checksum, *value), true) : false;
Read original function source
static bool readPayloadByte(FILE *file, uint8_t *value, DungeonStorageResult *result, uint32_t *checksum)
{
return readByte(file, value, result) ? (updateChecksum(checksum, *value), true) : false;
}writePayloadByte() · source line 120
static bool writePayloadByte(FILE *file, uint8_t value, DungeonStorageResult *result, uint32_t *checksum)
Calls into this documentation:
- Decision
!writeByte(file, value, result)True- Exit this function
return false;
FalseContinue after this decision.
- Execute
updateChecksum(checksum, value); - Exit this function
return true;
Read original function source
static bool writePayloadByte(FILE *file, uint8_t value, DungeonStorageResult *result, uint32_t *checksum)
{
if (!writeByte(file, value, result)) return false;
updateChecksum(checksum, value);
return true;
}writePayloadU16() · source line 127
static bool writePayloadU16(FILE *file, uint16_t value, DungeonStorageResult *result, uint32_t *checksum)
Calls into this documentation:
- Exit this function
return writePayloadByte(file, (uint8_t)value, result, checksum) && writePayloadByte(file, (uint8_t)(value >> 8), result, checksum);
Read original function source
static bool writePayloadU16(FILE *file, uint16_t value, DungeonStorageResult *result, uint32_t *checksum)
{
return writePayloadByte(file, (uint8_t)value, result, checksum) && writePayloadByte(file, (uint8_t)(value >> 8), result, checksum);
}readPayloadU16() · source line 132
static bool readPayloadU16(FILE *file, uint16_t *value, DungeonStorageResult *result, uint32_t *checksum)
Calls into this documentation:
- Decision
!readU16(file, value, result)True- Exit this function
return false;
FalseContinue after this decision.
- Execute
updateChecksum(checksum, (uint8_t)(*value & 0xFFU)); - Execute
updateChecksum(checksum, (uint8_t)(*value >> 8)); - Exit this function
return true;
Read original function source
static bool readPayloadU16(FILE *file, uint16_t *value, DungeonStorageResult *result, uint32_t *checksum)
{
if (!readU16(file, value, result)) return false;
updateChecksum(checksum, (uint8_t)(*value & 0xFFU));
updateChecksum(checksum, (uint8_t)(*value >> 8));
return true;
}readPosition() · source line 140
static bool readPosition(FILE *file, DungeonPosition *position, DungeonStorageResult *result, uint32_t *checksum)
Calls into this documentation:
- Exit this function
return readPayloadByte(file, &position->floor, result, checksum) && readPayloadByte(file, &position->x, result, checksum) && readPayloadByte(file, &position->y, result, checksum);
Read original function source
static bool readPosition(FILE *file, DungeonPosition *position, DungeonStorageResult *result, uint32_t *checksum)
{
return readPayloadByte(file, &position->floor, result, checksum) && readPayloadByte(file, &position->x, result, checksum) && readPayloadByte(file, &position->y, result, checksum);
}loadDungeonWorld() · source line 145
bool loadDungeonWorld(DungeonStorageResult *result)
Calls into this documentation:
- Decision
result == NULLTrue- Exit this function
return false;
FalseContinue after this decision.
- Execute
memset(result, 0, sizeof(*result)); - Execute
result->error = DUNGEON_STORAGE_OPEN_READ_FAILED; - Execute
FILE *file = fopen(DUNGEON_FILE, "rb"); - Decision
file == NULLTrue- Exit this function
return false;
FalseContinue after this decision.
- Decision
fseek(file, 0L, SEEK_END) != 0True- Jump to label
goto read_failed;
FalseContinue after this decision.
- Execute
const long file_size = ftell(file); - Decision
file_size != (long)LEGACY_FILE_SIZE && file_size != (long)FILE_SIZETrue- Execute
result->error = DUNGEON_STORAGE_BAD_FILE_SIZE; - Jump to label
goto done;
FalseContinue after this decision.
- Execute
rewind(file); - Execute
uint8_t magic[4], floors, width, height, record_size; - Execute
uint16_t version; - Execute
uint32_t payload_size, stored_checksum; - Decision
fread(magic, 1, sizeof(magic), file) != sizeof(magic)True- Jump to label
goto read_failed;
FalseContinue after this decision.
- Execute
result->bytes += sizeof(magic); - Decision
!readU16(file, &version, result) || !readByte(file, &floors, result) || !readByte(file, &width, result) || !readByte(file, &height, result) || !readByte(file, &record_size, result) || !readU32(file, &payload_size, result) || !readU32(file, &stored_checksum, result)True- Jump to label
goto read_failed;
FalseContinue after this decision.
- Decision
memcmp(magic, "MAZE", sizeof(magic)) != 0True- Execute
result->error = DUNGEON_STORAGE_BAD_SIGNATURE; - Jump to label
goto done;
FalseContinue after this decision.
- Decision
version != DUNGEON_LEGACY_VERSION && version != DUNGEON_FILE_VERSIONTrue- Execute
result->error = DUNGEON_STORAGE_BAD_VERSION; - Jump to label
goto done;
FalseContinue after this decision.
- Decision
floors != DUNGEON_FLOOR_COUNT || width != DUNGEON_WIDTH || height != DUNGEON_HEIGHT || record_size != CELL_RECORD_SIZETrue- Execute
result->error = DUNGEON_STORAGE_BAD_DIMENSIONS; - Jump to label
goto done;
FalseContinue after this decision.
- Execute
const bool legacy = version == DUNGEON_LEGACY_VERSION && payload_size == WORLD_PAYLOAD_SIZE && file_size == (long)LEGACY_FILE_SIZE; - Execute
const bool version2 = version == DUNGEON_FILE_VERSION && payload_size == WORLD_PAYLOAD_SIZE + CONFIG_RECORD_SIZE + RESERVED_SIZE && file_size == (long)FILE_SIZE; - Decision
!legacy && !version2True- Execute
result->error = DUNGEON_STORAGE_BAD_FILE_SIZE; - Jump to label
goto done;
FalseContinue after this decision.
- Execute
initDungeonMap(); - Execute
initDungeonConfig(); - Execute
DungeonWorld *world = getDungeonWorld(); - Execute
uint32_t checksum = CHECKSUM_START; - Execute
bool read = readPosition(file, &world->entrance, result, &checksum) && readPosition(file, &world->exit, result, &checksum); - FOR loop
unsigned index = 0; read && index < DUNGEON_FLOOR_COUNT - 1U; ++indexRepeat body while the loop condition permits; then continue below.
- Execute
read = readPosition(file, &world->stairLinks[index].upper, result, &checksum) && readPosition(file, &world->stairLinks[index].lower, result, &checksum);
- FOR loop
unsigned floor = 0; read && floor < DUNGEON_FLOOR_COUNT; ++floorRepeat body while the loop condition permits; then continue below.
- FOR loop
unsigned y = 0; read && y < DUNGEON_HEIGHT; ++yRepeat body while the loop condition permits; then continue below.
- FOR loop
unsigned x = 0; read && x < DUNGEON_WIDTH; ++xRepeat body while the loop condition permits; then continue below.
- Execute
DungeonCell *cell = &world->floors[floor].cells[y][x]; - Execute
read = readPayloadByte(file, &cell->exits, result, &checksum) && readPayloadByte(file, &cell->tileType, result, &checksum) && readPayloadByte(file, &cell->variant, result, &checksum) && readPayloadByte(file, &cell->explored, result, &checksum) && readPayloadU16(file, &cell->eventFlags, result, &checksum) && readPayloadU16(file, &cell->stateFlags, result, &checksum);
- FOR loop
unsigned index = 0; read && version2 && index < TAIL_SIZE; ++indexRepeat body while the loop condition permits; then continue below.
- Execute
read = readPayloadByte(file, &file_tail[index], result, &checksum);
- Decision
!read || result->bytes != (uint32_t)file_sizeTrue- Jump to label
goto read_failed;
FalseContinue after this decision.
- Decision
checksum != stored_checksumTrue- Execute
result->error = DUNGEON_STORAGE_CHECKSUM_FAILED; - Jump to label
goto done;
FalseContinue after this decision.
- Execute
loaded_version = version; - Execute
result->error = DUNGEON_STORAGE_OK; - Execute
fclose(file); - Exit this function
return true; - Execute
read_failed: result->error = DUNGEON_STORAGE_READ_FAILED; - Execute
done: fclose(file); - Exit this function
return false;
Read original function source
bool loadDungeonWorld(DungeonStorageResult *result)
{
if (result == NULL) return false;
memset(result, 0, sizeof(*result));
result->error = DUNGEON_STORAGE_OPEN_READ_FAILED;
FILE *file = fopen(DUNGEON_FILE, "rb");
if (file == NULL) return false;
if (fseek(file, 0L, SEEK_END) != 0) goto read_failed;
const long file_size = ftell(file);
if (file_size != (long)LEGACY_FILE_SIZE && file_size != (long)FILE_SIZE) { result->error = DUNGEON_STORAGE_BAD_FILE_SIZE; goto done; }
rewind(file);
uint8_t magic[4], floors, width, height, record_size;
uint16_t version;
uint32_t payload_size, stored_checksum;
if (fread(magic, 1, sizeof(magic), file) != sizeof(magic)) goto read_failed;
result->bytes += sizeof(magic);
if (!readU16(file, &version, result) || !readByte(file, &floors, result) || !readByte(file, &width, result) || !readByte(file, &height, result) || !readByte(file, &record_size, result) || !readU32(file, &payload_size, result) || !readU32(file, &stored_checksum, result)) goto read_failed;
if (memcmp(magic, "MAZE", sizeof(magic)) != 0) { result->error = DUNGEON_STORAGE_BAD_SIGNATURE; goto done; }
if (version != DUNGEON_LEGACY_VERSION && version != DUNGEON_FILE_VERSION) { result->error = DUNGEON_STORAGE_BAD_VERSION; goto done; }
if (floors != DUNGEON_FLOOR_COUNT || width != DUNGEON_WIDTH || height != DUNGEON_HEIGHT || record_size != CELL_RECORD_SIZE) { result->error = DUNGEON_STORAGE_BAD_DIMENSIONS; goto done; }
const bool legacy = version == DUNGEON_LEGACY_VERSION && payload_size == WORLD_PAYLOAD_SIZE && file_size == (long)LEGACY_FILE_SIZE;
const bool version2 = version == DUNGEON_FILE_VERSION && payload_size == WORLD_PAYLOAD_SIZE + CONFIG_RECORD_SIZE + RESERVED_SIZE && file_size == (long)FILE_SIZE;
if (!legacy && !version2) { result->error = DUNGEON_STORAGE_BAD_FILE_SIZE; goto done; }
initDungeonMap();
initDungeonConfig();
DungeonWorld *world = getDungeonWorld();
uint32_t checksum = CHECKSUM_START;
bool read = readPosition(file, &world->entrance, result, &checksum) && readPosition(file, &world->exit, result, &checksum);
for (unsigned index = 0; read && index < DUNGEON_FLOOR_COUNT - 1U; ++index) read = readPosition(file, &world->stairLinks[index].upper, result, &checksum) && readPosition(file, &world->stairLinks[index].lower, result, &checksum);
for (unsigned floor = 0; read && floor < DUNGEON_FLOOR_COUNT; ++floor) for (unsigned y = 0; read && y < DUNGEON_HEIGHT; ++y) for (unsigned x = 0; read && x < DUNGEON_WIDTH; ++x) {
DungeonCell *cell = &world->floors[floor].cells[y][x];
read = readPayloadByte(file, &cell->exits, result, &checksum) && readPayloadByte(file, &cell->tileType, result, &checksum) && readPayloadByte(file, &cell->variant, result, &checksum) && readPayloadByte(file, &cell->explored, result, &checksum) && readPayloadU16(file, &cell->eventFlags, result, &checksum) && readPayloadU16(file, &cell->stateFlags, result, &checksum);
}
for (unsigned index = 0; read && version2 && index < TAIL_SIZE; ++index) read = readPayloadByte(file, &file_tail[index], result, &checksum);
if (!read || result->bytes != (uint32_t)file_size) goto read_failed;
if (checksum != stored_checksum) { result->error = DUNGEON_STORAGE_CHECKSUM_FAILED; goto done; }
loaded_version = version;
result->error = DUNGEON_STORAGE_OK;
fclose(file);
return true;
read_failed:
result->error = DUNGEON_STORAGE_READ_FAILED;
done:
fclose(file);
return false;
}saveDungeonWorld() · source line 193
bool saveDungeonWorld(DungeonStorageResult *result)
Calls into this documentation:
- Decision
result == NULL || (loaded_version != DUNGEON_LEGACY_VERSION && loaded_version != DUNGEON_FILE_VERSION)True- Exit this function
return false;
FalseContinue after this decision.
- Execute
memset(result, 0, sizeof(*result)); - Execute
result->error = DUNGEON_STORAGE_OPEN_WRITE_FAILED; - Execute
errno = 0; - Execute
FILE *file = fopen(DUNGEON_FILE, "wb"); - Decision
file == NULLTrue- Execute
result->errno_value = errno; - Exit this function
return false;
FalseContinue after this decision.
- Execute
const bool version2 = loaded_version == DUNGEON_FILE_VERSION; - Execute
const uint32_t payload_size = WORLD_PAYLOAD_SIZE + (version2 ? TAIL_SIZE : 0U); - Execute
const uint32_t file_size = HEADER_SIZE + payload_size; - Execute
const uint8_t magic[] = { 'M', 'A', 'Z', 'E' }; - Execute
const DungeonWorld *world = getDungeonWorld(); - Execute
uint32_t checksum = CHECKSUM_START; - Execute
bool written = fwrite(magic, 1, sizeof(magic), file) == sizeof(magic); - Execute
result->bytes += written ? sizeof(magic) : 0U; - Execute
written = written && writeU16(file, loaded_version, result) && writeByte(file, DUNGEON_FLOOR_COUNT, result) && writeByte(file, DUNGEON_WIDTH, result) && writeByte(file, DUNGEON_HEIGHT, result) && writeByte(file, CELL_RECORD_SIZE, result) && writeU32(file, payload_size, result) && writeU32(file, 0U, result); - Execute
const long checksum_offset = 14L; - Execute
const DungeonPosition *positions[] = { &world->entrance, &world->exit }; - FOR loop
unsigned index = 0; written && index < sizeof(positions) / sizeof(positions[0]); ++indexRepeat body while the loop condition permits; then continue below.
- Execute
written = writePayloadByte(file, positions[index]->floor, result, &checksum) && writePayloadByte(file, positions[index]->x, result, &checksum) && writePayloadByte(file, positions[index]->y, result, &checksum);
- FOR loop
unsigned index = 0; written && index < DUNGEON_FLOOR_COUNT - 1U; ++indexRepeat body while the loop condition permits; then continue below.
- Execute
const DungeonStairLink *link = &world->stairLinks[index]; - Execute
written = writePayloadByte(file, link->upper.floor, result, &checksum) && writePayloadByte(file, link->upper.x, result, &checksum) && writePayloadByte(file, link->upper.y, result, &checksum) && writePayloadByte(file, link->lower.floor, result, &checksum) && writePayloadByte(file, link->lower.x, result, &checksum) && writePayloadByte(file, link->lower.y, result, &checksum);
- FOR loop
unsigned floor = 0; written && floor < DUNGEON_FLOOR_COUNT; ++floorRepeat body while the loop condition permits; then continue below.
- FOR loop
unsigned y = 0; written && y < DUNGEON_HEIGHT; ++yRepeat body while the loop condition permits; then continue below.
- FOR loop
unsigned x = 0; written && x < DUNGEON_WIDTH; ++xRepeat body while the loop condition permits; then continue below.
- Execute
const DungeonCell *cell = &world->floors[floor].cells[y][x]; - Execute
written = writePayloadByte(file, cell->exits, result, &checksum) && writePayloadByte(file, cell->tileType, result, &checksum) && writePayloadByte(file, cell->variant, result, &checksum) && writePayloadByte(file, cell->explored, result, &checksum) && writePayloadU16(file, cell->eventFlags, result, &checksum) && writePayloadU16(file, cell->stateFlags, result, &checksum);
- FOR loop
unsigned index = 0; written && version2 && index < TAIL_SIZE; ++indexRepeat body while the loop condition permits; then continue below.
- Execute
written = writePayloadByte(file, file_tail[index], result, &checksum);
- Decision
written && fseek(file, checksum_offset, SEEK_SET) == 0True- Execute
uint8_t bytes[] = { (uint8_t)checksum, (uint8_t)(checksum >> 8), (uint8_t)(checksum >> 16), (uint8_t)(checksum >> 24) }; - Execute
written = fwrite(bytes, 1, sizeof(bytes), file) == sizeof(bytes);
False- Execute
written = false;
- Execute
const int close_result = fclose(file); - Decision
!written || close_result != 0 || result->bytes != file_sizeTrue- Execute
result->error = DUNGEON_STORAGE_WRITE_FAILED; - Execute
result->errno_value = errno; - Exit this function
return false;
FalseContinue after this decision.
- Execute
result->error = DUNGEON_STORAGE_OK; - Exit this function
return true;
Read original function source
bool saveDungeonWorld(DungeonStorageResult *result)
{
if (result == NULL || (loaded_version != DUNGEON_LEGACY_VERSION && loaded_version != DUNGEON_FILE_VERSION)) return false;
memset(result, 0, sizeof(*result));
result->error = DUNGEON_STORAGE_OPEN_WRITE_FAILED;
errno = 0;
FILE *file = fopen(DUNGEON_FILE, "wb");
if (file == NULL) { result->errno_value = errno; return false; }
const bool version2 = loaded_version == DUNGEON_FILE_VERSION;
const uint32_t payload_size = WORLD_PAYLOAD_SIZE + (version2 ? TAIL_SIZE : 0U);
const uint32_t file_size = HEADER_SIZE + payload_size;
const uint8_t magic[] = { 'M', 'A', 'Z', 'E' };
const DungeonWorld *world = getDungeonWorld();
uint32_t checksum = CHECKSUM_START;
bool written = fwrite(magic, 1, sizeof(magic), file) == sizeof(magic);
result->bytes += written ? sizeof(magic) : 0U;
written = written && writeU16(file, loaded_version, result) && writeByte(file, DUNGEON_FLOOR_COUNT, result) && writeByte(file, DUNGEON_WIDTH, result) && writeByte(file, DUNGEON_HEIGHT, result) && writeByte(file, CELL_RECORD_SIZE, result) && writeU32(file, payload_size, result) && writeU32(file, 0U, result);
const long checksum_offset = 14L;
const DungeonPosition *positions[] = { &world->entrance, &world->exit };
for (unsigned index = 0; written && index < sizeof(positions) / sizeof(positions[0]); ++index)
written = writePayloadByte(file, positions[index]->floor, result, &checksum) && writePayloadByte(file, positions[index]->x, result, &checksum) && writePayloadByte(file, positions[index]->y, result, &checksum);
for (unsigned index = 0; written && index < DUNGEON_FLOOR_COUNT - 1U; ++index) {
const DungeonStairLink *link = &world->stairLinks[index];
written = writePayloadByte(file, link->upper.floor, result, &checksum) && writePayloadByte(file, link->upper.x, result, &checksum) && writePayloadByte(file, link->upper.y, result, &checksum) && writePayloadByte(file, link->lower.floor, result, &checksum) && writePayloadByte(file, link->lower.x, result, &checksum) && writePayloadByte(file, link->lower.y, result, &checksum);
}
for (unsigned floor = 0; written && floor < DUNGEON_FLOOR_COUNT; ++floor) for (unsigned y = 0; written && y < DUNGEON_HEIGHT; ++y) for (unsigned x = 0; written && x < DUNGEON_WIDTH; ++x) {
const DungeonCell *cell = &world->floors[floor].cells[y][x];
written = writePayloadByte(file, cell->exits, result, &checksum) && writePayloadByte(file, cell->tileType, result, &checksum) && writePayloadByte(file, cell->variant, result, &checksum) && writePayloadByte(file, cell->explored, result, &checksum) && writePayloadU16(file, cell->eventFlags, result, &checksum) && writePayloadU16(file, cell->stateFlags, result, &checksum);
}
for (unsigned index = 0; written && version2 && index < TAIL_SIZE; ++index) written = writePayloadByte(file, file_tail[index], result, &checksum);
if (written && fseek(file, checksum_offset, SEEK_SET) == 0) {
uint8_t bytes[] = { (uint8_t)checksum, (uint8_t)(checksum >> 8), (uint8_t)(checksum >> 16), (uint8_t)(checksum >> 24) };
written = fwrite(bytes, 1, sizeof(bytes), file) == sizeof(bytes);
} else written = false;
const int close_result = fclose(file);
if (!written || close_result != 0 || result->bytes != file_size) {
result->error = DUNGEON_STORAGE_WRITE_FAILED;
result->errno_value = errno;
return false;
}
result->error = DUNGEON_STORAGE_OK;
return true;
}getDungeonWorldChecksum() · source line 237
uint32_t getDungeonWorldChecksum(void)
Calls into this documentation:
- Execute
const DungeonWorld *world = getDungeonWorld(); - Execute
uint32_t checksum = CHECKSUM_START; - Execute
const DungeonPosition *positions[] = { &world->entrance, &world->exit }; - FOR loop
unsigned index = 0; index < sizeof(positions) / sizeof(positions[0]); ++indexRepeat body while the loop condition permits; then continue below.
- Execute
updateChecksum(&checksum, positions[index]->floor); - Execute
updateChecksum(&checksum, positions[index]->x); - Execute
updateChecksum(&checksum, positions[index]->y);
- FOR loop
unsigned index = 0; index < DUNGEON_FLOOR_COUNT - 1U; ++indexRepeat body while the loop condition permits; then continue below.
- Execute
const DungeonStairLink *link = &world->stairLinks[index]; - Execute
const DungeonPosition *stairs[] = { &link->upper, &link->lower }; - FOR loop
unsigned position = 0; position < sizeof(stairs) / sizeof(stairs[0]); ++positionRepeat body while the loop condition permits; then continue below.
- Execute
updateChecksum(&checksum, stairs[position]->floor); - Execute
updateChecksum(&checksum, stairs[position]->x); - Execute
updateChecksum(&checksum, stairs[position]->y);
- FOR loop
unsigned floor = 0; floor < DUNGEON_FLOOR_COUNT; ++floorRepeat body while the loop condition permits; then continue below.
- FOR loop
unsigned y = 0; y < DUNGEON_HEIGHT; ++yRepeat body while the loop condition permits; then continue below.
- FOR loop
unsigned x = 0; x < DUNGEON_WIDTH; ++xRepeat body while the loop condition permits; then continue below.
- Execute
const DungeonCell *cell = &world->floors[floor].cells[y][x]; - Execute
updateChecksum(&checksum, cell->exits); - Execute
updateChecksum(&checksum, cell->tileType); - Execute
updateChecksum(&checksum, cell->variant); - Execute
updateChecksum(&checksum, (uint8_t)(cell->eventFlags & 0xFFU)); - Execute
updateChecksum(&checksum, (uint8_t)(cell->eventFlags >> 8));
- Exit this function
return checksum;
Read original function source
uint32_t getDungeonWorldChecksum(void)
{
const DungeonWorld *world = getDungeonWorld();
uint32_t checksum = CHECKSUM_START;
const DungeonPosition *positions[] = { &world->entrance, &world->exit };
for (unsigned index = 0; index < sizeof(positions) / sizeof(positions[0]); ++index) {
updateChecksum(&checksum, positions[index]->floor);
updateChecksum(&checksum, positions[index]->x);
updateChecksum(&checksum, positions[index]->y);
}
for (unsigned index = 0; index < DUNGEON_FLOOR_COUNT - 1U; ++index) {
const DungeonStairLink *link = &world->stairLinks[index];
const DungeonPosition *stairs[] = { &link->upper, &link->lower };
for (unsigned position = 0; position < sizeof(stairs) / sizeof(stairs[0]); ++position) {
updateChecksum(&checksum, stairs[position]->floor);
updateChecksum(&checksum, stairs[position]->x);
updateChecksum(&checksum, stairs[position]->y);
}
}
for (unsigned floor = 0; floor < DUNGEON_FLOOR_COUNT; ++floor) {
for (unsigned y = 0; y < DUNGEON_HEIGHT; ++y) {
for (unsigned x = 0; x < DUNGEON_WIDTH; ++x) {
const DungeonCell *cell = &world->floors[floor].cells[y][x];
updateChecksum(&checksum, cell->exits);
updateChecksum(&checksum, cell->tileType);
updateChecksum(&checksum, cell->variant);
updateChecksum(&checksum, (uint8_t)(cell->eventFlags & 0xFFU));
updateChecksum(&checksum, (uint8_t)(cell->eventFlags >> 8));
}
}
}
return checksum;
}dungeonStorageErrorText() · source line 271
const char *dungeonStorageErrorText(DungeonStorageError error)
- Select a case
errorEnter matching case; follow source fall-through until a break, return or end.
- Case / label
case DUNGEON_STORAGE_OK: - Exit this function
return "OK"; - Case / label
case DUNGEON_STORAGE_OPEN_READ_FAILED: - Exit this function
return "Cannot open file"; - Case / label
case DUNGEON_STORAGE_READ_FAILED: - Exit this function
return "Read failed"; - Case / label
case DUNGEON_STORAGE_OPEN_WRITE_FAILED: - Exit this function
return "Cannot create file"; - Case / label
case DUNGEON_STORAGE_WRITE_FAILED: - Exit this function
return "File save failed"; - Case / label
case DUNGEON_STORAGE_BAD_SIGNATURE: - Exit this function
return "Bad signature"; - Case / label
case DUNGEON_STORAGE_BAD_VERSION: - Exit this function
return "Bad version"; - Case / label
case DUNGEON_STORAGE_BAD_DIMENSIONS: - Exit this function
return "Bad dimensions"; - Case / label
case DUNGEON_STORAGE_BAD_FILE_SIZE: - Exit this function
return "Bad file size"; - Case / label
case DUNGEON_STORAGE_CHECKSUM_FAILED: - Exit this function
return "Checksum failed"; - Case / label
default: - Exit this function
return "Unknown error";
Read original function source
const char *dungeonStorageErrorText(DungeonStorageError error)
{
switch (error) {
case DUNGEON_STORAGE_OK: return "OK";
case DUNGEON_STORAGE_OPEN_READ_FAILED: return "Cannot open file";
case DUNGEON_STORAGE_READ_FAILED: return "Read failed";
case DUNGEON_STORAGE_OPEN_WRITE_FAILED: return "Cannot create file";
case DUNGEON_STORAGE_WRITE_FAILED: return "File save failed";
case DUNGEON_STORAGE_BAD_SIGNATURE: return "Bad signature";
case DUNGEON_STORAGE_BAD_VERSION: return "Bad version";
case DUNGEON_STORAGE_BAD_DIMENSIONS: return "Bad dimensions";
case DUNGEON_STORAGE_BAD_FILE_SIZE: return "Bad file size";
case DUNGEON_STORAGE_CHECKSUM_FAILED: return "Checksum failed";
default: return "Unknown error";
}
}Complete original file, including constants and data
/* Coded By JD(2026) */
#error "Reference listing only — not provided as build-ready source."
#include "dungeon_storage.h"
#include "dungeon_config.h"
#include "dungeon_map.h"
#include <errno.h>
#include "esp_log.h"
#include <stdio.h>
#include <string.h>
#define DUNGEON_FILE "/spiffs/dungeon.dat"
#define DUNGEON_PLAYER_FILE "/spiffs/dungeon_player.dat"
#define DUNGEON_EXPLORED_FILE "/spiffs/dungeon_explored.dat"
#define DUNGEON_HISTORY_FILE "/spiffs/dungeon_highscores.dat"
#define DUNGEON_LEGACY_VERSION 1U
#define DUNGEON_FILE_VERSION 2U
#define CELL_RECORD_SIZE 8U
#define WORLD_PAYLOAD_SIZE (6U + ((DUNGEON_FLOOR_COUNT - 1U) * 6U) + (DUNGEON_FLOOR_COUNT * DUNGEON_WIDTH * DUNGEON_HEIGHT * CELL_RECORD_SIZE))
#define CONFIG_RECORD_SIZE 159U
#define RESERVED_SIZE 128U
#define TAIL_SIZE (CONFIG_RECORD_SIZE + RESERVED_SIZE)
#define HEADER_SIZE 18U
#define LEGACY_FILE_SIZE (HEADER_SIZE + WORLD_PAYLOAD_SIZE)
#define FILE_SIZE (LEGACY_FILE_SIZE + CONFIG_RECORD_SIZE + RESERVED_SIZE)
#define CHECKSUM_START 2166136261UL
#define CHECKSUM_PRIME 16777619UL
static uint8_t file_tail[TAIL_SIZE];
static uint16_t loaded_version;
bool deleteDungeonDataFiles(DungeonDeleteResult *result)
{
static const char *const files[] = {
DUNGEON_FILE,
DUNGEON_PLAYER_FILE,
DUNGEON_EXPLORED_FILE,
DUNGEON_HISTORY_FILE,
};
if (result == NULL) return false;
memset(result, 0, sizeof(*result));
result->error = DUNGEON_DELETE_OK;
for (unsigned index = 0U; index < sizeof(files) / sizeof(files[0]); ++index) {
errno = 0;
if (remove(files[index]) == 0) {
++result->deleted;
/* ESP_LOGI("dungeon_storage", "DELETE %s OK", files[index]); */
} else if (errno == ENOENT) {
++result->missing;
/* ESP_LOGI("dungeon_storage", "DELETE %s MISSING", files[index]); */
} else {
result->error = DUNGEON_DELETE_FAILED;
result->errno_value = errno;
result->failed_path = files[index];
/* ESP_LOGI("dungeon_storage", "DELETE %s FAILED errno=%d", files[index], errno); */
return false;
}
}
/* ESP_LOGI("dungeon_storage", "DELETE COMPLETE deleted=%u missing=%u", (unsigned)result->deleted, (unsigned)result->missing); */
return true;
}
void prepareNewDungeonWorldStorage(void)
{
loaded_version = DUNGEON_FILE_VERSION;
memset(file_tail, 0, sizeof(file_tail));
}
static bool readByte(FILE *file, uint8_t *value, DungeonStorageResult *result)
{
if (fread(value, 1, 1, file) != 1U) return false;
++result->bytes;
return true;
}
static bool readU16(FILE *file, uint16_t *value, DungeonStorageResult *result)
{
uint8_t bytes[2];
if (fread(bytes, 1, sizeof(bytes), file) != sizeof(bytes)) return false;
result->bytes += sizeof(bytes);
*value = (uint16_t)bytes[0] | ((uint16_t)bytes[1] << 8);
return true;
}
static bool readU32(FILE *file, uint32_t *value, DungeonStorageResult *result)
{
uint8_t bytes[4];
if (fread(bytes, 1, sizeof(bytes), file) != sizeof(bytes)) return false;
result->bytes += sizeof(bytes);
*value = (uint32_t)bytes[0] | ((uint32_t)bytes[1] << 8) | ((uint32_t)bytes[2] << 16) | ((uint32_t)bytes[3] << 24);
return true;
}
static bool writeByte(FILE *file, uint8_t value, DungeonStorageResult *result)
{
if (fwrite(&value, 1, 1, file) != 1U) return false;
++result->bytes;
return true;
}
static bool writeU16(FILE *file, uint16_t value, DungeonStorageResult *result)
{
return writeByte(file, (uint8_t)value, result) && writeByte(file, (uint8_t)(value >> 8), result);
}
static bool writeU32(FILE *file, uint32_t value, DungeonStorageResult *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 void updateChecksum(uint32_t *checksum, uint8_t value)
{
*checksum ^= value;
*checksum *= CHECKSUM_PRIME;
}
static bool readPayloadByte(FILE *file, uint8_t *value, DungeonStorageResult *result, uint32_t *checksum)
{
return readByte(file, value, result) ? (updateChecksum(checksum, *value), true) : false;
}
static bool writePayloadByte(FILE *file, uint8_t value, DungeonStorageResult *result, uint32_t *checksum)
{
if (!writeByte(file, value, result)) return false;
updateChecksum(checksum, value);
return true;
}
static bool writePayloadU16(FILE *file, uint16_t value, DungeonStorageResult *result, uint32_t *checksum)
{
return writePayloadByte(file, (uint8_t)value, result, checksum) && writePayloadByte(file, (uint8_t)(value >> 8), result, checksum);
}
static bool readPayloadU16(FILE *file, uint16_t *value, DungeonStorageResult *result, uint32_t *checksum)
{
if (!readU16(file, value, result)) return false;
updateChecksum(checksum, (uint8_t)(*value & 0xFFU));
updateChecksum(checksum, (uint8_t)(*value >> 8));
return true;
}
static bool readPosition(FILE *file, DungeonPosition *position, DungeonStorageResult *result, uint32_t *checksum)
{
return readPayloadByte(file, &position->floor, result, checksum) && readPayloadByte(file, &position->x, result, checksum) && readPayloadByte(file, &position->y, result, checksum);
}
bool loadDungeonWorld(DungeonStorageResult *result)
{
if (result == NULL) return false;
memset(result, 0, sizeof(*result));
result->error = DUNGEON_STORAGE_OPEN_READ_FAILED;
FILE *file = fopen(DUNGEON_FILE, "rb");
if (file == NULL) return false;
if (fseek(file, 0L, SEEK_END) != 0) goto read_failed;
const long file_size = ftell(file);
if (file_size != (long)LEGACY_FILE_SIZE && file_size != (long)FILE_SIZE) { result->error = DUNGEON_STORAGE_BAD_FILE_SIZE; goto done; }
rewind(file);
uint8_t magic[4], floors, width, height, record_size;
uint16_t version;
uint32_t payload_size, stored_checksum;
if (fread(magic, 1, sizeof(magic), file) != sizeof(magic)) goto read_failed;
result->bytes += sizeof(magic);
if (!readU16(file, &version, result) || !readByte(file, &floors, result) || !readByte(file, &width, result) || !readByte(file, &height, result) || !readByte(file, &record_size, result) || !readU32(file, &payload_size, result) || !readU32(file, &stored_checksum, result)) goto read_failed;
if (memcmp(magic, "MAZE", sizeof(magic)) != 0) { result->error = DUNGEON_STORAGE_BAD_SIGNATURE; goto done; }
if (version != DUNGEON_LEGACY_VERSION && version != DUNGEON_FILE_VERSION) { result->error = DUNGEON_STORAGE_BAD_VERSION; goto done; }
if (floors != DUNGEON_FLOOR_COUNT || width != DUNGEON_WIDTH || height != DUNGEON_HEIGHT || record_size != CELL_RECORD_SIZE) { result->error = DUNGEON_STORAGE_BAD_DIMENSIONS; goto done; }
const bool legacy = version == DUNGEON_LEGACY_VERSION && payload_size == WORLD_PAYLOAD_SIZE && file_size == (long)LEGACY_FILE_SIZE;
const bool version2 = version == DUNGEON_FILE_VERSION && payload_size == WORLD_PAYLOAD_SIZE + CONFIG_RECORD_SIZE + RESERVED_SIZE && file_size == (long)FILE_SIZE;
if (!legacy && !version2) { result->error = DUNGEON_STORAGE_BAD_FILE_SIZE; goto done; }
initDungeonMap();
initDungeonConfig();
DungeonWorld *world = getDungeonWorld();
uint32_t checksum = CHECKSUM_START;
bool read = readPosition(file, &world->entrance, result, &checksum) && readPosition(file, &world->exit, result, &checksum);
for (unsigned index = 0; read && index < DUNGEON_FLOOR_COUNT - 1U; ++index) read = readPosition(file, &world->stairLinks[index].upper, result, &checksum) && readPosition(file, &world->stairLinks[index].lower, result, &checksum);
for (unsigned floor = 0; read && floor < DUNGEON_FLOOR_COUNT; ++floor) for (unsigned y = 0; read && y < DUNGEON_HEIGHT; ++y) for (unsigned x = 0; read && x < DUNGEON_WIDTH; ++x) {
DungeonCell *cell = &world->floors[floor].cells[y][x];
read = readPayloadByte(file, &cell->exits, result, &checksum) && readPayloadByte(file, &cell->tileType, result, &checksum) && readPayloadByte(file, &cell->variant, result, &checksum) && readPayloadByte(file, &cell->explored, result, &checksum) && readPayloadU16(file, &cell->eventFlags, result, &checksum) && readPayloadU16(file, &cell->stateFlags, result, &checksum);
}
for (unsigned index = 0; read && version2 && index < TAIL_SIZE; ++index) read = readPayloadByte(file, &file_tail[index], result, &checksum);
if (!read || result->bytes != (uint32_t)file_size) goto read_failed;
if (checksum != stored_checksum) { result->error = DUNGEON_STORAGE_CHECKSUM_FAILED; goto done; }
loaded_version = version;
result->error = DUNGEON_STORAGE_OK;
fclose(file);
return true;
read_failed:
result->error = DUNGEON_STORAGE_READ_FAILED;
done:
fclose(file);
return false;
}
bool saveDungeonWorld(DungeonStorageResult *result)
{
if (result == NULL || (loaded_version != DUNGEON_LEGACY_VERSION && loaded_version != DUNGEON_FILE_VERSION)) return false;
memset(result, 0, sizeof(*result));
result->error = DUNGEON_STORAGE_OPEN_WRITE_FAILED;
errno = 0;
FILE *file = fopen(DUNGEON_FILE, "wb");
if (file == NULL) { result->errno_value = errno; return false; }
const bool version2 = loaded_version == DUNGEON_FILE_VERSION;
const uint32_t payload_size = WORLD_PAYLOAD_SIZE + (version2 ? TAIL_SIZE : 0U);
const uint32_t file_size = HEADER_SIZE + payload_size;
const uint8_t magic[] = { 'M', 'A', 'Z', 'E' };
const DungeonWorld *world = getDungeonWorld();
uint32_t checksum = CHECKSUM_START;
bool written = fwrite(magic, 1, sizeof(magic), file) == sizeof(magic);
result->bytes += written ? sizeof(magic) : 0U;
written = written && writeU16(file, loaded_version, result) && writeByte(file, DUNGEON_FLOOR_COUNT, result) && writeByte(file, DUNGEON_WIDTH, result) && writeByte(file, DUNGEON_HEIGHT, result) && writeByte(file, CELL_RECORD_SIZE, result) && writeU32(file, payload_size, result) && writeU32(file, 0U, result);
const long checksum_offset = 14L;
const DungeonPosition *positions[] = { &world->entrance, &world->exit };
for (unsigned index = 0; written && index < sizeof(positions) / sizeof(positions[0]); ++index)
written = writePayloadByte(file, positions[index]->floor, result, &checksum) && writePayloadByte(file, positions[index]->x, result, &checksum) && writePayloadByte(file, positions[index]->y, result, &checksum);
for (unsigned index = 0; written && index < DUNGEON_FLOOR_COUNT - 1U; ++index) {
const DungeonStairLink *link = &world->stairLinks[index];
written = writePayloadByte(file, link->upper.floor, result, &checksum) && writePayloadByte(file, link->upper.x, result, &checksum) && writePayloadByte(file, link->upper.y, result, &checksum) && writePayloadByte(file, link->lower.floor, result, &checksum) && writePayloadByte(file, link->lower.x, result, &checksum) && writePayloadByte(file, link->lower.y, result, &checksum);
}
for (unsigned floor = 0; written && floor < DUNGEON_FLOOR_COUNT; ++floor) for (unsigned y = 0; written && y < DUNGEON_HEIGHT; ++y) for (unsigned x = 0; written && x < DUNGEON_WIDTH; ++x) {
const DungeonCell *cell = &world->floors[floor].cells[y][x];
written = writePayloadByte(file, cell->exits, result, &checksum) && writePayloadByte(file, cell->tileType, result, &checksum) && writePayloadByte(file, cell->variant, result, &checksum) && writePayloadByte(file, cell->explored, result, &checksum) && writePayloadU16(file, cell->eventFlags, result, &checksum) && writePayloadU16(file, cell->stateFlags, result, &checksum);
}
for (unsigned index = 0; written && version2 && index < TAIL_SIZE; ++index) written = writePayloadByte(file, file_tail[index], result, &checksum);
if (written && fseek(file, checksum_offset, SEEK_SET) == 0) {
uint8_t bytes[] = { (uint8_t)checksum, (uint8_t)(checksum >> 8), (uint8_t)(checksum >> 16), (uint8_t)(checksum >> 24) };
written = fwrite(bytes, 1, sizeof(bytes), file) == sizeof(bytes);
} else written = false;
const int close_result = fclose(file);
if (!written || close_result != 0 || result->bytes != file_size) {
result->error = DUNGEON_STORAGE_WRITE_FAILED;
result->errno_value = errno;
return false;
}
result->error = DUNGEON_STORAGE_OK;
return true;
}
uint32_t getDungeonWorldChecksum(void)
{
const DungeonWorld *world = getDungeonWorld();
uint32_t checksum = CHECKSUM_START;
const DungeonPosition *positions[] = { &world->entrance, &world->exit };
for (unsigned index = 0; index < sizeof(positions) / sizeof(positions[0]); ++index) {
updateChecksum(&checksum, positions[index]->floor);
updateChecksum(&checksum, positions[index]->x);
updateChecksum(&checksum, positions[index]->y);
}
for (unsigned index = 0; index < DUNGEON_FLOOR_COUNT - 1U; ++index) {
const DungeonStairLink *link = &world->stairLinks[index];
const DungeonPosition *stairs[] = { &link->upper, &link->lower };
for (unsigned position = 0; position < sizeof(stairs) / sizeof(stairs[0]); ++position) {
updateChecksum(&checksum, stairs[position]->floor);
updateChecksum(&checksum, stairs[position]->x);
updateChecksum(&checksum, stairs[position]->y);
}
}
for (unsigned floor = 0; floor < DUNGEON_FLOOR_COUNT; ++floor) {
for (unsigned y = 0; y < DUNGEON_HEIGHT; ++y) {
for (unsigned x = 0; x < DUNGEON_WIDTH; ++x) {
const DungeonCell *cell = &world->floors[floor].cells[y][x];
updateChecksum(&checksum, cell->exits);
updateChecksum(&checksum, cell->tileType);
updateChecksum(&checksum, cell->variant);
updateChecksum(&checksum, (uint8_t)(cell->eventFlags & 0xFFU));
updateChecksum(&checksum, (uint8_t)(cell->eventFlags >> 8));
}
}
}
return checksum;
}
const char *dungeonStorageErrorText(DungeonStorageError error)
{
switch (error) {
case DUNGEON_STORAGE_OK: return "OK";
case DUNGEON_STORAGE_OPEN_READ_FAILED: return "Cannot open file";
case DUNGEON_STORAGE_READ_FAILED: return "Read failed";
case DUNGEON_STORAGE_OPEN_WRITE_FAILED: return "Cannot create file";
case DUNGEON_STORAGE_WRITE_FAILED: return "File save failed";
case DUNGEON_STORAGE_BAD_SIGNATURE: return "Bad signature";
case DUNGEON_STORAGE_BAD_VERSION: return "Bad version";
case DUNGEON_STORAGE_BAD_DIMENSIONS: return "Bad dimensions";
case DUNGEON_STORAGE_BAD_FILE_SIZE: return "Bad file size";
case DUNGEON_STORAGE_CHECKSUM_FAILED: return "Checksum failed";
default: return "Unknown error";
}
}