#include "dolphin_state.h" #include #include #include #define DOLPHIN_STORE_KEY "/int/dolphin.state" #define DOLPHIN_STORE_HEADER_MAGIC 0xD0 #define DOLPHIN_STORE_HEADER_VERSION 0x01 #define DOLPHIN_LVL_THRESHOLD 20.0f typedef struct { uint8_t magic; uint8_t version; uint8_t checksum; uint8_t flags; uint32_t timestamp; } DolphinStoreHeader; typedef struct { uint32_t limit_ibutton; uint32_t limit_nfc; uint32_t limit_ir; uint32_t limit_rfid; uint32_t flags; uint32_t icounter; uint32_t butthurt; uint64_t timestamp; } DolphinStoreData; typedef struct { DolphinStoreHeader header; DolphinStoreData data; } DolphinStore; struct DolphinState { Storage* fs_api; DolphinStoreData data; bool dirty; }; DolphinState* dolphin_state_alloc() { DolphinState* dolphin_state = furi_alloc(sizeof(DolphinState)); dolphin_state->fs_api = furi_record_open("storage"); return dolphin_state; } void dolphin_state_free(DolphinState* dolphin_state) { furi_record_close("storage"); free(dolphin_state); } bool dolphin_state_save(DolphinState* dolphin_state) { if(!dolphin_state->dirty) { return true; } FURI_LOG_I("dolphin-state", "State is dirty, saving to \"%s\"", DOLPHIN_STORE_KEY); DolphinStore store; // Calculate checksum uint8_t* source = (uint8_t*)&dolphin_state->data; uint8_t checksum = 0; for(size_t i = 0; i < sizeof(DolphinStoreData); i++) { checksum += source[i]; } // Set header store.header.magic = DOLPHIN_STORE_HEADER_MAGIC; store.header.version = DOLPHIN_STORE_HEADER_VERSION; store.header.checksum = checksum; store.header.flags = 0; store.header.timestamp = 0; // Set data store.data = dolphin_state->data; // Store File* file = storage_file_alloc(dolphin_state->fs_api); bool save_result = storage_file_open(file, DOLPHIN_STORE_KEY, FSAM_WRITE, FSOM_CREATE_ALWAYS); if(save_result) { uint16_t bytes_count = storage_file_write(file, &store, sizeof(DolphinStore)); if(bytes_count != sizeof(DolphinStore)) { save_result = false; } } if(!save_result) { FURI_LOG_E( "dolphin-state", "Save failed. Storage returned: %s", storage_file_get_error_desc(file)); } storage_file_close(file); storage_file_free(file); dolphin_state->dirty = !save_result; FURI_LOG_I("dolphin-state", "Saved"); return save_result; } bool dolphin_state_load(DolphinState* dolphin_state) { DolphinStore store; // Read Dolphin State Store FURI_LOG_I("dolphin-state", "Loading state from \"%s\"", DOLPHIN_STORE_KEY); File* file = storage_file_alloc(dolphin_state->fs_api); bool load_result = storage_file_open(file, DOLPHIN_STORE_KEY, FSAM_READ, FSOM_OPEN_EXISTING); if(!load_result) { FURI_LOG_E( "dolphin-state", "Load failed. Storage returned: %s", storage_file_get_error_desc(file)); } else { uint16_t bytes_count = storage_file_read(file, &store, sizeof(DolphinStore)); if(bytes_count != sizeof(DolphinStore)) { load_result = false; } } if(!load_result) { FURI_LOG_E("dolphin-state", "DolphinStore size mismatch"); } else { if(store.header.magic == DOLPHIN_STORE_HEADER_MAGIC && store.header.version == DOLPHIN_STORE_HEADER_VERSION) { FURI_LOG_I( "dolphin-state", "Magic(%d) and Version(%d) match", store.header.magic, store.header.version); uint8_t checksum = 0; const uint8_t* source = (const uint8_t*)&store.data; for(size_t i = 0; i < sizeof(DolphinStoreData); i++) { checksum += source[i]; } if(store.header.checksum == checksum) { FURI_LOG_I("dolphin-state", "Checksum(%d) match", store.header.checksum); dolphin_state->data = store.data; } else { FURI_LOG_E( "dolphin-state", "Checksum(%d != %d) mismatch", store.header.checksum, checksum); load_result = false; } } else { FURI_LOG_E( "dolphin-state", "Magic(%d != %d) or Version(%d != %d) mismatch", store.header.magic, DOLPHIN_STORE_HEADER_MAGIC, store.header.version, DOLPHIN_STORE_HEADER_VERSION); load_result = false; } } storage_file_close(file); storage_file_free(file); dolphin_state->dirty = !load_result; return load_result; } void dolphin_state_clear(DolphinState* dolphin_state) { memset(&dolphin_state->data, 0, sizeof(DolphinStoreData)); } uint64_t dolphin_state_timestamp() { RTC_TimeTypeDef time; RTC_DateTypeDef date; struct tm current; HAL_RTC_GetTime(&hrtc, &time, RTC_FORMAT_BIN); HAL_RTC_GetDate(&hrtc, &date, RTC_FORMAT_BIN); current.tm_year = date.Year + 100; current.tm_mday = date.Date; current.tm_mon = date.Month - 1; current.tm_hour = time.Hours; current.tm_min = time.Minutes; current.tm_sec = time.Seconds; return mktime(¤t); } void dolphin_state_on_deed(DolphinState* dolphin_state, DolphinDeed deed) { const DolphinDeedWeight* deed_weight = dolphin_deed_weight(deed); int32_t icounter = dolphin_state->data.icounter + deed_weight->icounter; int32_t butthurt = dolphin_state->data.butthurt; if(icounter >= 0) { dolphin_state->data.icounter = icounter; dolphin_state->data.butthurt = MAX(butthurt - deed_weight->icounter, 0); dolphin_state->data.timestamp = dolphin_state_timestamp(); } dolphin_state->dirty = true; } void dolphin_state_butthurted(DolphinState* dolphin_state) { dolphin_state->data.butthurt++; dolphin_state->data.timestamp = dolphin_state_timestamp(); dolphin_state->dirty = true; } uint32_t dolphin_state_get_icounter(DolphinState* dolphin_state) { return dolphin_state->data.icounter; } uint32_t dolphin_state_get_butthurt(DolphinState* dolphin_state) { return dolphin_state->data.butthurt; } uint64_t dolphin_state_get_timestamp(DolphinState* dolphin_state) { return dolphin_state->data.timestamp; } uint32_t dolphin_state_get_level(uint32_t icounter) { return 0.5f + sqrtf(1.0f + 8.0f * ((float)icounter / DOLPHIN_LVL_THRESHOLD)) / 2.0f; } uint32_t dolphin_state_xp_to_levelup(uint32_t icounter, uint32_t level, bool remaining) { return (DOLPHIN_LVL_THRESHOLD * level * (level + 1) / 2) - (remaining ? icounter : 0); }