From bf0b8063a7848c95ed493ee86f102429d5b29fa6 Mon Sep 17 00:00:00 2001 From: Derek Jamison Date: Thu, 23 Mar 2023 09:48:30 -0400 Subject: [PATCH] save/load social preferences. --- .../rock_paper_scissors/rock_paper_scissors.c | 167 +++++++++++++++--- .../rock_paper_scissors/rock_paper_scissors.h | 20 +++ 2 files changed, 161 insertions(+), 26 deletions(-) diff --git a/subghz/plugins/rock_paper_scissors/rock_paper_scissors.c b/subghz/plugins/rock_paper_scissors/rock_paper_scissors.c index 357fdf2..9719044 100644 --- a/subghz/plugins/rock_paper_scissors/rock_paper_scissors.c +++ b/subghz/plugins/rock_paper_scissors/rock_paper_scissors.c @@ -1408,6 +1408,139 @@ static void remote_games_remove(GameContext* game_context, GameEvent* game_event } } +// This is a helper method that creates the game directory if it does not exist. +// @param storage pointer to a Storage. +static void ensure_dir_exists(Storage* storage) { + // If apps_data directory doesn't exist, create it. + if(!storage_dir_exists(storage, RPS_APPS_DATA_FOLDER)) { + FURI_LOG_I(TAG, "Creating directory: %s", RPS_APPS_DATA_FOLDER); + storage_simply_mkdir(storage, RPS_APPS_DATA_FOLDER); + } else { + FURI_LOG_I(TAG, "Directory exists: %s", RPS_APPS_DATA_FOLDER); + } + + // If rock_paper_scissors directory doesn't exist, create it. + if(!storage_dir_exists(storage, RPS_GAME_FOLDER)) { + FURI_LOG_I(TAG, "Creating directory: %s", RPS_GAME_FOLDER); + storage_simply_mkdir(storage, RPS_GAME_FOLDER); + } else { + FURI_LOG_I(TAG, "Directory exists: %s", RPS_GAME_FOLDER); + } +} + +// Saves a local contact to the file system. +// @param game_context pointer to a GameContext. +static void save_local_contact(GameContext* game_context) { + if(furi_mutex_acquire(game_context->mutex, FuriWaitForever) != FuriStatusOk) { + return; + } + + Storage* storage = furi_record_open(RECORD_STORAGE); + ensure_dir_exists(storage); + FURI_LOG_I(TAG, "Saving social: %s", furi_string_get_cstr(game_context->data->local_contact)); + + File* social_file = storage_file_alloc(storage); + if(storage_file_open(social_file, RPS_SOCIAL_PATH, FSAM_WRITE, FSOM_OPEN_ALWAYS)) { + FURI_LOG_T(TAG, "Opened file: %s", RPS_SOCIAL_PATH); + int offset = + (KEYBOARD_MAX_LEN + 2) * furi_string_get_char(game_context->data->local_contact, 0); + if(offset < 0) { + offset = 0; + } + + int file_size = storage_file_size(social_file); + if(file_size < offset) { + storage_file_seek(social_file, file_size, true); + for(int i = file_size; i < offset; i++) { + storage_file_write(social_file, "\0", 1); + } + } + + storage_file_seek(social_file, offset, true); + if(!storage_file_write( + social_file, + furi_string_get_cstr(game_context->data->local_contact), + furi_string_size(game_context->data->local_contact))) { + FURI_LOG_E(TAG, "Failed to write to file."); + } + storage_file_write(social_file, "\n", 1); + + storage_file_seek(social_file, 0, true); + storage_file_write( + social_file, + furi_string_get_cstr(game_context->data->local_contact), + furi_string_size(game_context->data->local_contact)); + storage_file_write(social_file, "\n", 1); + } else { + FURI_LOG_E(TAG, "Failed to open file: %s", RPS_SOCIAL_PATH); + } + + storage_file_close(social_file); + storage_file_free(social_file); + furi_record_close(RECORD_STORAGE); + + furi_mutex_release(game_context->mutex); +} + +// Loads a local contact from the file system. +// @param index index of the contact to load. +// @param skip_first_char true if the first character should be skipped. +// @param buffer pointer to a FuriString. +static void load_social_data(int index, bool skip_first_char, FuriString* buffer) { + Storage* storage = furi_record_open(RECORD_STORAGE); + + File* social_file = storage_file_alloc(storage); + if(storage_file_open(social_file, RPS_SOCIAL_PATH, FSAM_READ, FSOM_OPEN_EXISTING)) { + char ch; + FURI_LOG_T(TAG, "Opened file: %s", RPS_SOCIAL_PATH); + int offset = (KEYBOARD_MAX_LEN + 2) * index; + + if(skip_first_char) { + offset++; // Skip first character when filling keyboard buffer. + } + + int file_size = storage_file_size(social_file); + if(file_size > offset) { + storage_file_seek(social_file, offset, true); + furi_string_reset(buffer); + while(storage_file_read(social_file, &ch, 1) && !storage_file_eof(social_file)) { + if(ch == '\n' || ch == '\0') { + break; + } + furi_string_push_back(buffer, ch); + } + } + } else { + FURI_LOG_E(TAG, "Failed to open file: %s", RPS_SOCIAL_PATH); + } + + storage_file_close(social_file); + storage_file_free(social_file); + furi_record_close(RECORD_STORAGE); +} + +// Fills the keyboard data array with the previously saved social information. +// @param game_context pointer to a GameContext. +static void load_keyboard_data(GameContext* game_context) { + load_social_data( + contact_list[game_context->data->social_line][0], true, game_context->data->keyboard_data); +} + +// Loads the initial local contact from the file system. +// @param game_context pointer to a GameContext. +static void load_local_contact(GameContext* game_context) { + load_social_data(0, false, game_context->data->local_contact); + if(furi_string_size(game_context->data->local_contact) > 0) { + char ch = furi_string_get_char(game_context->data->local_contact, 0); + for(int i = 0; i < (int)COUNT_OF(contact_list); i++) { + if(contact_list[i][0] == ch) { + game_context->data->social_line = i; + break; + } + } + } +} + // Saves a game result to the file system. // @param game_context pointer to a GameContext. static void save_result(GameContext* game_context) { @@ -1438,27 +1571,13 @@ static void save_result(GameContext* game_context) { FURI_LOG_I(TAG, "Saving result: %s", furi_string_get_cstr(game_context->data->buffer)); Storage* storage = furi_record_open(RECORD_STORAGE); + ensure_dir_exists(storage); + File* games_file = storage_file_alloc(storage); - // If apps_data directory doesn't exist, create it. - if(!storage_dir_exists(storage, RPS_APPS_DATA_FOLDER)) { - FURI_LOG_I(TAG, "Creating directory: %s", RPS_APPS_DATA_FOLDER); - storage_simply_mkdir(storage, RPS_APPS_DATA_FOLDER); - } else { - FURI_LOG_I(TAG, "Directory exists: %s", RPS_APPS_DATA_FOLDER); - } - - // If rock_paper_scissors directory doesn't exist, create it. - if(!storage_dir_exists(storage, RPS_GAME_FOLDER)) { - FURI_LOG_I(TAG, "Creating directory: %s", RPS_GAME_FOLDER); - storage_simply_mkdir(storage, RPS_GAME_FOLDER); - } else { - FURI_LOG_I(TAG, "Directory exists: %s", RPS_GAME_FOLDER); - } - // Append contents to ending of games.txt (create if doesn't exist) if(storage_file_open(games_file, RPS_GAME_PATH, FSAM_WRITE, FSOM_OPEN_APPEND)) { - FURI_LOG_E(TAG, "Opened file: %s", RPS_GAME_PATH); + FURI_LOG_T(TAG, "Opened file: %s", RPS_GAME_PATH); if(!storage_file_write( games_file, furi_string_get_cstr(game_context->data->buffer), @@ -1654,6 +1773,7 @@ int32_t rock_paper_scissors_app(void* p) { game_context->data->keyboard_data = furi_string_alloc(); load_player_stats(game_context); + load_local_contact(game_context); // Queue for events game_context->queue = furi_message_queue_alloc(8, sizeof(GameEvent)); @@ -1947,10 +2067,7 @@ int32_t rock_paper_scissors_app(void* p) { "%c%s", contact_list[game_context->data->social_line][0], furi_string_get_cstr(game_context->data->keyboard_data)); - FURI_LOG_I( - TAG, - "Changed contact info to '%s'", - furi_string_get_cstr(game_context->data->local_contact)); + save_local_contact(game_context); game_context->data->screen_state = ScreenMainMenu; } break; @@ -2003,17 +2120,15 @@ int32_t rock_paper_scissors_app(void* p) { case InputKeyOk: if(game_context->data->social_line == 0) { furi_string_set(game_context->data->local_contact, CONTACT_INFO_NONE); - FURI_LOG_I( - TAG, - "Changed contact info to '%s'", - furi_string_get_cstr(game_context->data->local_contact)); + save_local_contact(game_context); game_context->data->screen_state = ScreenMainMenu; } else { furi_string_set( game_context->data->keyboard_heading, contact_list[game_context->data->social_line] + 1); - game_context->data->keyboard_row = 0; + game_context->data->keyboard_row = 3; game_context->data->keyboard_col = 13; + load_keyboard_data(game_context); game_context->data->screen_state = ScreenEditMessage; } break; diff --git a/subghz/plugins/rock_paper_scissors/rock_paper_scissors.h b/subghz/plugins/rock_paper_scissors/rock_paper_scissors.h index 713acfe..6b70178 100644 --- a/subghz/plugins/rock_paper_scissors/rock_paper_scissors.h +++ b/subghz/plugins/rock_paper_scissors/rock_paper_scissors.h @@ -20,6 +20,8 @@ "rock_paper_scissors" #define RPS_GAME_FILE_NAME "games.txt" #define RPS_GAME_PATH RPS_GAME_FOLDER "/" RPS_GAME_FILE_NAME +#define RPS_SOCIAL_FILE_NAME "social.me" +#define RPS_SOCIAL_PATH RPS_GAME_FOLDER "/" RPS_SOCIAL_FILE_NAME // This is sent at the beginning of all RF messages. NOTE: It must end with the ':' character. #define RPS_GAME_NAME "RPS:" @@ -459,6 +461,24 @@ static void remote_games_previous(GameContext* game_context); static void remote_games_add(GameContext* game_context, GameEvent* game_event); static void remote_games_remove(GameContext* game_context, GameEvent* game_event); +// This is a helper method that creates the game directory if it does not exist. +// @param storage pointer to a Storage. +static void ensure_dir_exists(Storage* storage); + +// Saves a local contact to the file system. +// @param game_context pointer to a GameContext. +static void save_local_contact(GameContext* game_context); + +// Loads a local contact from the file system. +// @param index index of the contact to load. +// @param skip_first_char true if the first character should be skipped. +// @param buffer pointer to a FuriString. +static void load_social_data(int index, bool skip_first_char, FuriString* buffer); + +// Fills the keyboard data array with the previously saved social information. +// @param game_context pointer to a GameContext. +static void load_keyboard_data(GameContext* game_context); + // Saves a game result to the file system. // @param game_context pointer to a GameContext. static void save_result(GameContext* game_context);