Completion history
Source: src/dungeon_history.c · 240 lines · 23 functions.
Module responsibility
Stores, sorts and validates completed runs for the Dungeon high-score and history screens.
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 15
static void updateChecksum(uint32_t *checksum, uint8_t value)
- Execute
*checksum = (*checksum ^ value) * CHECKSUM_PRIME;
Read original function source
static void updateChecksum(uint32_t *checksum, uint8_t value) { *checksum = (*checksum ^ value) * CHECKSUM_PRIME; }updateChecksumU16() · source line 16
static void updateChecksumU16(uint32_t *checksum, uint16_t value)
Calls into this documentation:
- Execute
updateChecksum(checksum, (uint8_t)value); - Execute
updateChecksum(checksum, (uint8_t)(value >> 8));
Read original function source
static void updateChecksumU16(uint32_t *checksum, uint16_t value) { updateChecksum(checksum, (uint8_t)value); updateChecksum(checksum, (uint8_t)(value >> 8)); }updateChecksumU32() · source line 17
static void updateChecksumU32(uint32_t *checksum, uint32_t value)
Calls into this documentation:
- Execute
updateChecksum(checksum, (uint8_t)value); - Execute
updateChecksum(checksum, (uint8_t)(value >> 8)); - Execute
updateChecksum(checksum, (uint8_t)(value >> 16)); - Execute
updateChecksum(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 23
static bool writeByte(FILE *file, uint8_t value, DungeonHistoryResult *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, DungeonHistoryResult *result)
{
if (fwrite(&value, 1, 1, file) != 1U) return false;
++result->bytes;
return true;
}writeU16() · source line 30
static bool writeU16(FILE *file, uint16_t value, DungeonHistoryResult *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, DungeonHistoryResult *result)
{
return writeByte(file, (uint8_t)value, result) && writeByte(file, (uint8_t)(value >> 8), result);
}writeU32() · source line 35
static bool writeU32(FILE *file, uint32_t value, DungeonHistoryResult *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, DungeonHistoryResult *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 41
static bool readByte(FILE *file, uint8_t *value, DungeonHistoryResult *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, DungeonHistoryResult *result)
{
if (fread(value, 1, 1, file) != 1U) return false;
++result->bytes;
return true;
}readU16() · source line 48
static bool readU16(FILE *file, uint16_t *value, DungeonHistoryResult *result)
Calls into this documentation:
- Execute
uint8_t low, high; - Decision
!readByte(file, &low, result) || !readByte(file, &high, result)True- Exit this function
return false;
FalseContinue after this decision.
- Execute
*value = (uint16_t)low | ((uint16_t)high << 8); - Exit this function
return true;
Read original function source
static bool readU16(FILE *file, uint16_t *value, DungeonHistoryResult *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 56
static bool readU32(FILE *file, uint32_t *value, DungeonHistoryResult *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, DungeonHistoryResult *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;
}updateRecordChecksum() · source line 65
static void updateRecordChecksum(uint32_t *checksum, const DungeonHistoryRecord *record)
Calls into this documentation:
- Execute
updateChecksumU32(checksum, record->gold); - Execute
updateChecksumU32(checksum, record->moves); - Execute
updateChecksumU32(checksum, record->timePlayedSeconds); - Execute
updateChecksumU32(checksum, record->startedAtEpoch); - Execute
updateChecksumU32(checksum, record->finishedAtEpoch); - Execute
updateChecksumU32(checksum, record->experience); - Execute
updateChecksumU16(checksum, record->level); - Execute
updateChecksumU16(checksum, record->attack); - Execute
updateChecksumU16(checksum, record->defence); - Execute
updateChecksumU16(checksum, record->keys); - Execute
updateChecksumU16(checksum, record->healthPotions); - Execute
updateChecksumU16(checksum, record->unspentStatPoints); - Execute
updateChecksumU16(checksum, record->shopPurchaseCount); - Execute
updateChecksum(checksum, record->commentId); - Execute
updateChecksum(checksum, 0U);
Read original function source
static void updateRecordChecksum(uint32_t *checksum, const DungeonHistoryRecord *record)
{
updateChecksumU32(checksum, record->gold); updateChecksumU32(checksum, record->moves);
updateChecksumU32(checksum, record->timePlayedSeconds); updateChecksumU32(checksum, record->startedAtEpoch);
updateChecksumU32(checksum, record->finishedAtEpoch); updateChecksumU32(checksum, record->experience);
updateChecksumU16(checksum, record->level); updateChecksumU16(checksum, record->attack);
updateChecksumU16(checksum, record->defence); updateChecksumU16(checksum, record->keys);
updateChecksumU16(checksum, record->healthPotions); updateChecksumU16(checksum, record->unspentStatPoints);
updateChecksumU16(checksum, record->shopPurchaseCount); updateChecksum(checksum, record->commentId);
updateChecksum(checksum, 0U);
}calculateChecksum() · source line 77
static uint32_t calculateChecksum(void)
Calls into this documentation:
- Execute
uint32_t checksum = CHECKSUM_START; - Execute
updateChecksum(&checksum, record_count); - FOR loop
unsigned index = 0; index < DUNGEON_HISTORY_MAX_RECORDS; ++indexRepeat body while the loop condition permits; then continue below.
- Execute
updateRecordChecksum(&checksum, &records[index]);
- Exit this function
return checksum;
Read original function source
static uint32_t calculateChecksum(void)
{
uint32_t checksum = CHECKSUM_START;
updateChecksum(&checksum, record_count);
for (unsigned index = 0; index < DUNGEON_HISTORY_MAX_RECORDS; ++index) updateRecordChecksum(&checksum, &records[index]);
return checksum;
}writeRecord() · source line 85
static bool writeRecord(FILE *file, const DungeonHistoryRecord *record, DungeonHistoryResult *result)
Calls into this documentation:
- Exit this function
return writeU32(file, record->gold, result) && writeU32(file, record->moves, result) && writeU32(file, record->timePlayedSeconds, result) && writeU32(file, record->startedAtEpoch, result) && writeU32(file, record->finishedAtEpoch, result) && writeU32(file, record->experience, result) && writeU16(file, record->level, result) && writeU16(file, record->attack, result) && writeU16(file, record->defence, result) && writeU16(file, record->keys, result) && writeU16(file, record->healthPotions, result) && writeU16(file, record->unspentStatPoints, result) && writeU16(file, record->shopPurchaseCount, result) && writeByte(file, record->commentId, result) && writeByte(file, 0U, result);
Read original function source
static bool writeRecord(FILE *file, const DungeonHistoryRecord *record, DungeonHistoryResult *result)
{
return writeU32(file, record->gold, result) && writeU32(file, record->moves, result) &&
writeU32(file, record->timePlayedSeconds, result) && writeU32(file, record->startedAtEpoch, result) &&
writeU32(file, record->finishedAtEpoch, result) && writeU32(file, record->experience, result) &&
writeU16(file, record->level, result) && writeU16(file, record->attack, result) &&
writeU16(file, record->defence, result) && writeU16(file, record->keys, result) &&
writeU16(file, record->healthPotions, result) && writeU16(file, record->unspentStatPoints, result) &&
writeU16(file, record->shopPurchaseCount, result) && writeByte(file, record->commentId, result) &&
writeByte(file, 0U, result);
}readRecord() · source line 97
static bool readRecord(FILE *file, DungeonHistoryRecord *record, DungeonHistoryResult *result)
Calls into this documentation:
- Execute
uint8_t reserved; - Exit this function
return readU32(file, &record->gold, result) && readU32(file, &record->moves, result) && readU32(file, &record->timePlayedSeconds, result) && readU32(file, &record->startedAtEpoch, result) && readU32(file, &record->finishedAtEpoch, result) && readU32(file, &record->experience, result) && readU16(file, &record->level, result) && readU16(file, &record->attack, result) && readU16(file, &record->defence, result) && readU16(file, &record->keys, result) && readU16(file, &record->healthPotions, result) && readU16(file, &record->unspentStatPoints, result) && readU16(file, &record->shopPurchaseCount, result) && readByte(file, &record->commentId, result) && readByte(file, &reserved, result);
Read original function source
static bool readRecord(FILE *file, DungeonHistoryRecord *record, DungeonHistoryResult *result)
{
uint8_t reserved;
return readU32(file, &record->gold, result) && readU32(file, &record->moves, result) &&
readU32(file, &record->timePlayedSeconds, result) && readU32(file, &record->startedAtEpoch, result) &&
readU32(file, &record->finishedAtEpoch, result) && readU32(file, &record->experience, result) &&
readU16(file, &record->level, result) && readU16(file, &record->attack, result) &&
readU16(file, &record->defence, result) && readU16(file, &record->keys, result) &&
readU16(file, &record->healthPotions, result) && readU16(file, &record->unspentStatPoints, result) &&
readU16(file, &record->shopPurchaseCount, result) && readByte(file, &record->commentId, result) &&
readByte(file, &reserved, result);
}betterRecord() · source line 110
static bool betterRecord(const DungeonHistoryRecord *left, const DungeonHistoryRecord *right)
- Decision
left->gold != right->goldTrue- Exit this function
return left->gold > right->gold;
FalseContinue after this decision.
- Decision
left->moves != right->movesTrue- Exit this function
return left->moves < right->moves;
FalseContinue after this decision.
- Exit this function
return left->timePlayedSeconds < right->timePlayedSeconds;
Read original function source
static bool betterRecord(const DungeonHistoryRecord *left, const DungeonHistoryRecord *right)
{
if (left->gold != right->gold) return left->gold > right->gold;
if (left->moves != right->moves) return left->moves < right->moves;
return left->timePlayedSeconds < right->timePlayedSeconds;
}sortRecords() · source line 117
static void sortRecords(void)
Calls into this documentation:
- FOR loop
unsigned index = 1; index < record_count; ++indexRepeat body while the loop condition permits; then continue below.
- Execute
DungeonHistoryRecord value = records[index]; - Execute
unsigned position = index; - WHILE loop
position > 0U && betterRecord(&value, &records[position - 1U])Repeat body while the loop condition permits; then continue below.
- Execute
records[position] = records[position - 1U]; - Execute
--position;
- Execute
records[position] = value;
Read original function source
static void sortRecords(void)
{
for (unsigned index = 1; index < record_count; ++index) {
DungeonHistoryRecord value = records[index];
unsigned position = index;
while (position > 0U && betterRecord(&value, &records[position - 1U])) {
records[position] = records[position - 1U];
--position;
}
records[position] = value;
}
}sameCompletion() · source line 130
static bool sameCompletion(const DungeonHistoryRecord *record, const DungeonPlayerState *player)
- Exit this function
return record->gold == player->gold && record->moves == player->moves && record->timePlayedSeconds == player->timePlayedSeconds && record->startedAtEpoch == player->startedAtEpoch && record->finishedAtEpoch == player->finishedAtEpoch;
Read original function source
static bool sameCompletion(const DungeonHistoryRecord *record, const DungeonPlayerState *player)
{
return record->gold == player->gold && record->moves == player->moves &&
record->timePlayedSeconds == player->timePlayedSeconds &&
record->startedAtEpoch == player->startedAtEpoch &&
record->finishedAtEpoch == player->finishedAtEpoch;
}saveHistory() · source line 138
static bool saveHistory(DungeonHistoryResult *result)
Calls into this documentation:
- Execute
memset(result, 0, sizeof(*result)); - Execute
result->error = DUNGEON_HISTORY_OPEN_WRITE_FAILED; - Execute
FILE *file = fopen(HISTORY_FILE, "wb"); - Decision
file == NULLTrue- Exit this function
return false;
FalseContinue after this decision.
- Execute
const uint8_t magic[] = { 'D', 'H', 'I', 'S' }; - Execute
const uint8_t reserved[] = { 0U, 0U, 0U }; - Execute
bool written = fwrite(magic, 1, sizeof(magic), file) == sizeof(magic); - Execute
result->bytes += written ? sizeof(magic) : 0U; - Execute
written = written && writeU16(file, HISTORY_VERSION, result) && writeU16(file, HISTORY_RECORD_SIZE, result) && writeByte(file, record_count, result) && fwrite(reserved, 1, sizeof(reserved), file) == sizeof(reserved); - Decision
writtenTrue- Execute
result->bytes += sizeof(reserved);
FalseContinue after this decision.
- Execute
written = written && writeU32(file, calculateChecksum(), result); - FOR loop
unsigned index = 0; written && index < DUNGEON_HISTORY_MAX_RECORDS; ++indexRepeat body while the loop condition permits; then continue below.
- Execute
written = writeRecord(file, &records[index], result);
- Execute
fclose(file); - Decision
!written || result->bytes != HISTORY_FILE_SIZETrue- Execute
result->error = DUNGEON_HISTORY_WRITE_FAILED; - Exit this function
return false;
FalseContinue after this decision.
- Execute
result->error = DUNGEON_HISTORY_OK; - Exit this function
return true;
Read original function source
static bool saveHistory(DungeonHistoryResult *result)
{
memset(result, 0, sizeof(*result));
result->error = DUNGEON_HISTORY_OPEN_WRITE_FAILED;
FILE *file = fopen(HISTORY_FILE, "wb");
if (file == NULL) return false;
const uint8_t magic[] = { 'D', 'H', 'I', 'S' };
const uint8_t reserved[] = { 0U, 0U, 0U };
bool written = fwrite(magic, 1, sizeof(magic), file) == sizeof(magic);
result->bytes += written ? sizeof(magic) : 0U;
written = written && writeU16(file, HISTORY_VERSION, result) && writeU16(file, HISTORY_RECORD_SIZE, result) &&
writeByte(file, record_count, result) && fwrite(reserved, 1, sizeof(reserved), file) == sizeof(reserved);
if (written) result->bytes += sizeof(reserved);
written = written && writeU32(file, calculateChecksum(), result);
for (unsigned index = 0; written && index < DUNGEON_HISTORY_MAX_RECORDS; ++index)
written = writeRecord(file, &records[index], result);
fclose(file);
if (!written || result->bytes != HISTORY_FILE_SIZE) { result->error = DUNGEON_HISTORY_WRITE_FAILED; return false; }
result->error = DUNGEON_HISTORY_OK;
return true;
}initDungeonHistory() · source line 160
void initDungeonHistory(void)
- Execute
memset(records, 0, sizeof(records)); - Execute
record_count = 0U;
Read original function source
void initDungeonHistory(void) { memset(records, 0, sizeof(records)); record_count = 0U; }loadDungeonHistory() · source line 162
bool loadDungeonHistory(DungeonHistoryResult *result)
Calls into this documentation:
- Decision
result == NULLTrue- Exit this function
return false;
FalseContinue after this decision.
- Execute
initDungeonHistory(); - Execute
memset(result, 0, sizeof(*result)); - Execute
result->error = DUNGEON_HISTORY_OPEN_READ_FAILED; - Execute
FILE *file = fopen(HISTORY_FILE, "rb"); - Decision
file == NULLTrue- Exit this function
return false;
FalseContinue after this decision.
- Decision
fseek(file, 0L, SEEK_END) != 0 || ftell(file) != (long)HISTORY_FILE_SIZETrue- Execute
result->error = DUNGEON_HISTORY_BAD_FILE; - Execute
fclose(file); - Exit this function
return false;
FalseContinue after this decision.
- Execute
rewind(file); - Execute
uint8_t magic[4], count, reserved[3]; - Execute
uint16_t version, record_size; - Execute
uint32_t stored_checksum; - Execute
bool read = fread(magic, 1, sizeof(magic), file) == sizeof(magic); - Execute
result->bytes += read ? sizeof(magic) : 0U; - Execute
read = read && readU16(file, &version, result) && readU16(file, &record_size, result) && readByte(file, &count, result) && fread(reserved, 1, sizeof(reserved), file) == sizeof(reserved); - Decision
readTrue- Execute
result->bytes += sizeof(reserved);
FalseContinue after this decision.
- Execute
read = read && readU32(file, &stored_checksum, result); - FOR loop
unsigned index = 0; read && index < DUNGEON_HISTORY_MAX_RECORDS; ++indexRepeat body while the loop condition permits; then continue below.
- Execute
read = readRecord(file, &records[index], result);
- Execute
fclose(file); - Decision
!read || result->bytes != HISTORY_FILE_SIZETrue- Execute
initDungeonHistory(); - Execute
result->error = DUNGEON_HISTORY_READ_FAILED; - Exit this function
return false;
FalseContinue after this decision.
- Decision
memcmp(magic, "DHIS", sizeof(magic)) != 0 || version != HISTORY_VERSION || record_size != HISTORY_RECORD_SIZE || count > DUNGEON_HISTORY_MAX_RECORDSTrue- Execute
initDungeonHistory(); - Execute
result->error = DUNGEON_HISTORY_BAD_FILE; - Exit this function
return false;
FalseContinue after this decision.
- Execute
record_count = count; - Decision
stored_checksum != calculateChecksum()True- Execute
initDungeonHistory(); - Execute
result->error = DUNGEON_HISTORY_CHECKSUM_FAILED; - Exit this function
return false;
FalseContinue after this decision.
- Execute
sortRecords(); - Execute
result->error = DUNGEON_HISTORY_OK; - Exit this function
return true;
Read original function source
bool loadDungeonHistory(DungeonHistoryResult *result)
{
if (result == NULL) return false;
initDungeonHistory();
memset(result, 0, sizeof(*result));
result->error = DUNGEON_HISTORY_OPEN_READ_FAILED;
FILE *file = fopen(HISTORY_FILE, "rb");
if (file == NULL) return false;
if (fseek(file, 0L, SEEK_END) != 0 || ftell(file) != (long)HISTORY_FILE_SIZE) { result->error = DUNGEON_HISTORY_BAD_FILE; fclose(file); return false; }
rewind(file);
uint8_t magic[4], count, reserved[3];
uint16_t version, record_size;
uint32_t stored_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, &record_size, result) &&
readByte(file, &count, result) && fread(reserved, 1, sizeof(reserved), file) == sizeof(reserved);
if (read) result->bytes += sizeof(reserved);
read = read && readU32(file, &stored_checksum, result);
for (unsigned index = 0; read && index < DUNGEON_HISTORY_MAX_RECORDS; ++index) read = readRecord(file, &records[index], result);
fclose(file);
if (!read || result->bytes != HISTORY_FILE_SIZE) { initDungeonHistory(); result->error = DUNGEON_HISTORY_READ_FAILED; return false; }
if (memcmp(magic, "DHIS", sizeof(magic)) != 0 || version != HISTORY_VERSION || record_size != HISTORY_RECORD_SIZE || count > DUNGEON_HISTORY_MAX_RECORDS) {
initDungeonHistory(); result->error = DUNGEON_HISTORY_BAD_FILE; return false;
}
record_count = count;
if (stored_checksum != calculateChecksum()) { initDungeonHistory(); result->error = DUNGEON_HISTORY_CHECKSUM_FAILED; return false; }
sortRecords();
result->error = DUNGEON_HISTORY_OK;
return true;
}recordDungeonCompletion() · source line 194
bool recordDungeonCompletion(const DungeonPlayerState *player, DungeonHistoryResult *result, uint8_t *rank)
Calls into this documentation:
- Decision
player == NULL || result == NULL || rank == NULLTrue- Exit this function
return false;
FalseContinue after this decision.
- Execute
*rank = 0U; - Decision
!loadDungeonHistory(result)True- Decision
result->error != DUNGEON_HISTORY_OPEN_READ_FAILEDTrue- Exit this function
return false;
FalseContinue after this decision.
- Execute
initDungeonHistory(); - Execute
result->created = true;
FalseContinue after this decision.
- FOR loop
unsigned index = 0; index < record_count; ++indexRepeat body while the loop condition permits; then continue below.
- Decision
sameCompletion(&records[index], player)True- Execute
*rank = (uint8_t)(index + 1U); - Execute
result->error = DUNGEON_HISTORY_OK; - Exit this function
return true;
FalseContinue after this decision.
- Execute
DungeonHistoryRecord record; - Execute
memset(&record, 0, sizeof(record)); - Execute
record.gold = player->gold; - Execute
record.moves = player->moves; - Execute
record.timePlayedSeconds = player->timePlayedSeconds; - Execute
record.startedAtEpoch = player->startedAtEpoch; - Execute
record.finishedAtEpoch = player->finishedAtEpoch; - Execute
record.experience = player->experience; - Execute
record.level = player->level; - Execute
record.attack = player->attack; - Execute
record.defence = player->defence; - Execute
record.keys = player->keys; - Execute
record.healthPotions = player->healthPotions; - Execute
record.unspentStatPoints = player->unspentStatPoints; - Execute
record.shopPurchaseCount = player->shopPurchaseCount; - Decision
record_count < DUNGEON_HISTORY_MAX_RECORDSTrue- Execute
records[record_count++] = record;
False- Decision
betterRecord(&record, &records[record_count - 1U])True- Execute
records[record_count - 1U] = record;
False- Execute
result->error = DUNGEON_HISTORY_OK; - Exit this function
return true;
- Execute
sortRecords(); - FOR loop
unsigned index = 0; index < record_count; ++indexRepeat body while the loop condition permits; then continue below.
- Decision
sameCompletion(&records[index], player)True- Execute
*rank = (uint8_t)(index + 1U); - Exit enclosing loop or switch
break;
FalseContinue after this decision.
- Execute
const bool created = result->created; - Decision
!saveHistory(result)True- Exit this function
return false;
FalseContinue after this decision.
- Execute
result->created = created; - Exit this function
return true;
Read original function source
bool recordDungeonCompletion(const DungeonPlayerState *player, DungeonHistoryResult *result, uint8_t *rank)
{
if (player == NULL || result == NULL || rank == NULL) return false;
*rank = 0U;
if (!loadDungeonHistory(result)) {
if (result->error != DUNGEON_HISTORY_OPEN_READ_FAILED) return false;
initDungeonHistory();
result->created = true;
}
for (unsigned index = 0; index < record_count; ++index) {
if (sameCompletion(&records[index], player)) { *rank = (uint8_t)(index + 1U); result->error = DUNGEON_HISTORY_OK; return true; }
}
DungeonHistoryRecord record;
memset(&record, 0, sizeof(record));
record.gold = player->gold; record.moves = player->moves; record.timePlayedSeconds = player->timePlayedSeconds;
record.startedAtEpoch = player->startedAtEpoch; record.finishedAtEpoch = player->finishedAtEpoch;
record.experience = player->experience; record.level = player->level; record.attack = player->attack;
record.defence = player->defence; record.keys = player->keys; record.healthPotions = player->healthPotions;
record.unspentStatPoints = player->unspentStatPoints; record.shopPurchaseCount = player->shopPurchaseCount;
if (record_count < DUNGEON_HISTORY_MAX_RECORDS) records[record_count++] = record;
else if (betterRecord(&record, &records[record_count - 1U])) records[record_count - 1U] = record;
else { result->error = DUNGEON_HISTORY_OK; return true; }
sortRecords();
for (unsigned index = 0; index < record_count; ++index)
if (sameCompletion(&records[index], player)) { *rank = (uint8_t)(index + 1U); break; }
const bool created = result->created;
if (!saveHistory(result)) return false;
result->created = created;
return true;
}dungeonHistoryCount() · source line 225
uint8_t dungeonHistoryCount(void)
- Exit this function
return record_count;
Read original function source
uint8_t dungeonHistoryCount(void) { return record_count; }dungeonHistoryRecord() · source line 226
const DungeonHistoryRecord *dungeonHistoryRecord(uint8_t index)
- Exit this function
return index < record_count ? &records[index] : NULL;
Read original function source
const DungeonHistoryRecord *dungeonHistoryRecord(uint8_t index) { return index < record_count ? &records[index] : NULL; }dungeonHistoryErrorText() · source line 228
const char *dungeonHistoryErrorText(DungeonHistoryError error)
- Select a case
errorEnter matching case; follow source fall-through until a break, return or end.
- Case / label
case DUNGEON_HISTORY_OK: - Exit this function
return "OK"; - Case / label
case DUNGEON_HISTORY_OPEN_READ_FAILED: - Exit this function
return "No completions yet"; - Case / label
case DUNGEON_HISTORY_OPEN_WRITE_FAILED: - Exit this function
return "Cannot create history"; - Case / label
case DUNGEON_HISTORY_READ_FAILED: - Exit this function
return "History read failed"; - Case / label
case DUNGEON_HISTORY_WRITE_FAILED: - Exit this function
return "History save failed"; - Case / label
case DUNGEON_HISTORY_BAD_FILE: - Exit this function
return "Bad history file"; - Case / label
case DUNGEON_HISTORY_CHECKSUM_FAILED: - Exit this function
return "History checksum failed"; - Case / label
default: - Exit this function
return "History error";
Read original function source
const char *dungeonHistoryErrorText(DungeonHistoryError error)
{
switch (error) {
case DUNGEON_HISTORY_OK: return "OK";
case DUNGEON_HISTORY_OPEN_READ_FAILED: return "No completions yet";
case DUNGEON_HISTORY_OPEN_WRITE_FAILED: return "Cannot create history";
case DUNGEON_HISTORY_READ_FAILED: return "History read failed";
case DUNGEON_HISTORY_WRITE_FAILED: return "History save failed";
case DUNGEON_HISTORY_BAD_FILE: return "Bad history file";
case DUNGEON_HISTORY_CHECKSUM_FAILED: return "History checksum failed";
default: return "History error";
}
}Complete original file, including constants and data
/* Coded By JD(2026) */
#error "Reference listing only — not provided as build-ready source."
#include "dungeon_history.h"
#include <stdio.h>
#include <string.h>
#define HISTORY_FILE "/spiffs/dungeon_highscores.dat"
#define HISTORY_VERSION 1U
#define HISTORY_RECORD_SIZE 40U
#define HISTORY_FILE_SIZE 216U
#define CHECKSUM_START 2166136261UL
#define CHECKSUM_PRIME 16777619UL
static DungeonHistoryRecord records[DUNGEON_HISTORY_MAX_RECORDS];
static uint8_t record_count;
static void updateChecksum(uint32_t *checksum, uint8_t value) { *checksum = (*checksum ^ value) * CHECKSUM_PRIME; }
static void updateChecksumU16(uint32_t *checksum, uint16_t value) { updateChecksum(checksum, (uint8_t)value); updateChecksum(checksum, (uint8_t)(value >> 8)); }
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, DungeonHistoryResult *result)
{
if (fwrite(&value, 1, 1, file) != 1U) return false;
++result->bytes;
return true;
}
static bool writeU16(FILE *file, uint16_t value, DungeonHistoryResult *result)
{
return writeByte(file, (uint8_t)value, result) && writeByte(file, (uint8_t)(value >> 8), result);
}
static bool writeU32(FILE *file, uint32_t value, DungeonHistoryResult *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, DungeonHistoryResult *result)
{
if (fread(value, 1, 1, file) != 1U) return false;
++result->bytes;
return true;
}
static bool readU16(FILE *file, uint16_t *value, DungeonHistoryResult *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, DungeonHistoryResult *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 void updateRecordChecksum(uint32_t *checksum, const DungeonHistoryRecord *record)
{
updateChecksumU32(checksum, record->gold); updateChecksumU32(checksum, record->moves);
updateChecksumU32(checksum, record->timePlayedSeconds); updateChecksumU32(checksum, record->startedAtEpoch);
updateChecksumU32(checksum, record->finishedAtEpoch); updateChecksumU32(checksum, record->experience);
updateChecksumU16(checksum, record->level); updateChecksumU16(checksum, record->attack);
updateChecksumU16(checksum, record->defence); updateChecksumU16(checksum, record->keys);
updateChecksumU16(checksum, record->healthPotions); updateChecksumU16(checksum, record->unspentStatPoints);
updateChecksumU16(checksum, record->shopPurchaseCount); updateChecksum(checksum, record->commentId);
updateChecksum(checksum, 0U);
}
static uint32_t calculateChecksum(void)
{
uint32_t checksum = CHECKSUM_START;
updateChecksum(&checksum, record_count);
for (unsigned index = 0; index < DUNGEON_HISTORY_MAX_RECORDS; ++index) updateRecordChecksum(&checksum, &records[index]);
return checksum;
}
static bool writeRecord(FILE *file, const DungeonHistoryRecord *record, DungeonHistoryResult *result)
{
return writeU32(file, record->gold, result) && writeU32(file, record->moves, result) &&
writeU32(file, record->timePlayedSeconds, result) && writeU32(file, record->startedAtEpoch, result) &&
writeU32(file, record->finishedAtEpoch, result) && writeU32(file, record->experience, result) &&
writeU16(file, record->level, result) && writeU16(file, record->attack, result) &&
writeU16(file, record->defence, result) && writeU16(file, record->keys, result) &&
writeU16(file, record->healthPotions, result) && writeU16(file, record->unspentStatPoints, result) &&
writeU16(file, record->shopPurchaseCount, result) && writeByte(file, record->commentId, result) &&
writeByte(file, 0U, result);
}
static bool readRecord(FILE *file, DungeonHistoryRecord *record, DungeonHistoryResult *result)
{
uint8_t reserved;
return readU32(file, &record->gold, result) && readU32(file, &record->moves, result) &&
readU32(file, &record->timePlayedSeconds, result) && readU32(file, &record->startedAtEpoch, result) &&
readU32(file, &record->finishedAtEpoch, result) && readU32(file, &record->experience, result) &&
readU16(file, &record->level, result) && readU16(file, &record->attack, result) &&
readU16(file, &record->defence, result) && readU16(file, &record->keys, result) &&
readU16(file, &record->healthPotions, result) && readU16(file, &record->unspentStatPoints, result) &&
readU16(file, &record->shopPurchaseCount, result) && readByte(file, &record->commentId, result) &&
readByte(file, &reserved, result);
}
static bool betterRecord(const DungeonHistoryRecord *left, const DungeonHistoryRecord *right)
{
if (left->gold != right->gold) return left->gold > right->gold;
if (left->moves != right->moves) return left->moves < right->moves;
return left->timePlayedSeconds < right->timePlayedSeconds;
}
static void sortRecords(void)
{
for (unsigned index = 1; index < record_count; ++index) {
DungeonHistoryRecord value = records[index];
unsigned position = index;
while (position > 0U && betterRecord(&value, &records[position - 1U])) {
records[position] = records[position - 1U];
--position;
}
records[position] = value;
}
}
static bool sameCompletion(const DungeonHistoryRecord *record, const DungeonPlayerState *player)
{
return record->gold == player->gold && record->moves == player->moves &&
record->timePlayedSeconds == player->timePlayedSeconds &&
record->startedAtEpoch == player->startedAtEpoch &&
record->finishedAtEpoch == player->finishedAtEpoch;
}
static bool saveHistory(DungeonHistoryResult *result)
{
memset(result, 0, sizeof(*result));
result->error = DUNGEON_HISTORY_OPEN_WRITE_FAILED;
FILE *file = fopen(HISTORY_FILE, "wb");
if (file == NULL) return false;
const uint8_t magic[] = { 'D', 'H', 'I', 'S' };
const uint8_t reserved[] = { 0U, 0U, 0U };
bool written = fwrite(magic, 1, sizeof(magic), file) == sizeof(magic);
result->bytes += written ? sizeof(magic) : 0U;
written = written && writeU16(file, HISTORY_VERSION, result) && writeU16(file, HISTORY_RECORD_SIZE, result) &&
writeByte(file, record_count, result) && fwrite(reserved, 1, sizeof(reserved), file) == sizeof(reserved);
if (written) result->bytes += sizeof(reserved);
written = written && writeU32(file, calculateChecksum(), result);
for (unsigned index = 0; written && index < DUNGEON_HISTORY_MAX_RECORDS; ++index)
written = writeRecord(file, &records[index], result);
fclose(file);
if (!written || result->bytes != HISTORY_FILE_SIZE) { result->error = DUNGEON_HISTORY_WRITE_FAILED; return false; }
result->error = DUNGEON_HISTORY_OK;
return true;
}
void initDungeonHistory(void) { memset(records, 0, sizeof(records)); record_count = 0U; }
bool loadDungeonHistory(DungeonHistoryResult *result)
{
if (result == NULL) return false;
initDungeonHistory();
memset(result, 0, sizeof(*result));
result->error = DUNGEON_HISTORY_OPEN_READ_FAILED;
FILE *file = fopen(HISTORY_FILE, "rb");
if (file == NULL) return false;
if (fseek(file, 0L, SEEK_END) != 0 || ftell(file) != (long)HISTORY_FILE_SIZE) { result->error = DUNGEON_HISTORY_BAD_FILE; fclose(file); return false; }
rewind(file);
uint8_t magic[4], count, reserved[3];
uint16_t version, record_size;
uint32_t stored_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, &record_size, result) &&
readByte(file, &count, result) && fread(reserved, 1, sizeof(reserved), file) == sizeof(reserved);
if (read) result->bytes += sizeof(reserved);
read = read && readU32(file, &stored_checksum, result);
for (unsigned index = 0; read && index < DUNGEON_HISTORY_MAX_RECORDS; ++index) read = readRecord(file, &records[index], result);
fclose(file);
if (!read || result->bytes != HISTORY_FILE_SIZE) { initDungeonHistory(); result->error = DUNGEON_HISTORY_READ_FAILED; return false; }
if (memcmp(magic, "DHIS", sizeof(magic)) != 0 || version != HISTORY_VERSION || record_size != HISTORY_RECORD_SIZE || count > DUNGEON_HISTORY_MAX_RECORDS) {
initDungeonHistory(); result->error = DUNGEON_HISTORY_BAD_FILE; return false;
}
record_count = count;
if (stored_checksum != calculateChecksum()) { initDungeonHistory(); result->error = DUNGEON_HISTORY_CHECKSUM_FAILED; return false; }
sortRecords();
result->error = DUNGEON_HISTORY_OK;
return true;
}
bool recordDungeonCompletion(const DungeonPlayerState *player, DungeonHistoryResult *result, uint8_t *rank)
{
if (player == NULL || result == NULL || rank == NULL) return false;
*rank = 0U;
if (!loadDungeonHistory(result)) {
if (result->error != DUNGEON_HISTORY_OPEN_READ_FAILED) return false;
initDungeonHistory();
result->created = true;
}
for (unsigned index = 0; index < record_count; ++index) {
if (sameCompletion(&records[index], player)) { *rank = (uint8_t)(index + 1U); result->error = DUNGEON_HISTORY_OK; return true; }
}
DungeonHistoryRecord record;
memset(&record, 0, sizeof(record));
record.gold = player->gold; record.moves = player->moves; record.timePlayedSeconds = player->timePlayedSeconds;
record.startedAtEpoch = player->startedAtEpoch; record.finishedAtEpoch = player->finishedAtEpoch;
record.experience = player->experience; record.level = player->level; record.attack = player->attack;
record.defence = player->defence; record.keys = player->keys; record.healthPotions = player->healthPotions;
record.unspentStatPoints = player->unspentStatPoints; record.shopPurchaseCount = player->shopPurchaseCount;
if (record_count < DUNGEON_HISTORY_MAX_RECORDS) records[record_count++] = record;
else if (betterRecord(&record, &records[record_count - 1U])) records[record_count - 1U] = record;
else { result->error = DUNGEON_HISTORY_OK; return true; }
sortRecords();
for (unsigned index = 0; index < record_count; ++index)
if (sameCompletion(&records[index], player)) { *rank = (uint8_t)(index + 1U); break; }
const bool created = result->created;
if (!saveHistory(result)) return false;
result->created = created;
return true;
}
uint8_t dungeonHistoryCount(void) { return record_count; }
const DungeonHistoryRecord *dungeonHistoryRecord(uint8_t index) { return index < record_count ? &records[index] : NULL; }
const char *dungeonHistoryErrorText(DungeonHistoryError error)
{
switch (error) {
case DUNGEON_HISTORY_OK: return "OK";
case DUNGEON_HISTORY_OPEN_READ_FAILED: return "No completions yet";
case DUNGEON_HISTORY_OPEN_WRITE_FAILED: return "Cannot create history";
case DUNGEON_HISTORY_READ_FAILED: return "History read failed";
case DUNGEON_HISTORY_WRITE_FAILED: return "History save failed";
case DUNGEON_HISTORY_BAD_FILE: return "Bad history file";
case DUNGEON_HISTORY_CHECKSUM_FAILED: return "History checksum failed";
default: return "History error";
}
}