diff --git a/applications/gui/modules/file_select.c b/applications/gui/modules/file_select.c index f9e1d2cd..a4a0db08 100644 --- a/applications/gui/modules/file_select.c +++ b/applications/gui/modules/file_select.c @@ -142,15 +142,16 @@ static bool file_select_input_callback(InputEvent* event, void* context) { } } else if(event->key == InputKeyOk) { if(file_select->callback != NULL) { - const char* result; - with_view_model( - file_select->view, (FileSelectModel * model) { - result = string_get_cstr(model->filename[model->position]); - return false; - }); - if(file_select->buffer) { - strlcpy(file_select->buffer, result, file_select->buffer_size); + with_view_model( + file_select->view, (FileSelectModel * model) { + strlcpy( + file_select->buffer, + string_get_cstr(model->filename[model->position]), + file_select->buffer_size); + + return false; + }); }; file_select->callback(true, file_select->context); @@ -312,6 +313,14 @@ bool file_select_fill_strings(FileSelect* file_select) { with_view_model( file_select->view, (FileSelectModel * model) { string_set_str(model->filename[string_counter], name); + + if(strcmp(file_select->extension, "*") != 0) { + string_replace_all_str( + model->filename[string_counter], + file_select->extension, + ""); + } + return true; }); string_counter++; @@ -408,15 +417,23 @@ void file_select_set_selected_file_internal(FileSelect* file_select, const char* const uint8_t name_length = 100; char* name = calloc(name_length, sizeof(char)); - uint16_t file_position = 0; - if(name == NULL) { return; } + uint16_t file_position = 0; + bool file_found = false; + + string_t filename_str; + string_init_set_str(filename_str, filename); + if(strcmp(file_select->extension, "*") != 0) { + string_cat_str(filename_str, file_select->extension); + } + result = dir_api->open(&directory, file_select->path); if(!result) { + string_clear(filename_str); dir_api->close(&directory); free(name); return; @@ -432,13 +449,15 @@ void file_select_set_selected_file_internal(FileSelect* file_select, const char* if(result) { if(directory.error_id == FSE_OK) { if(filter_file(file_select, &file_info, name)) { - if(strcmp(filename, name) == 0) { + if(strcmp(string_get_cstr(filename_str), name) == 0) { + file_found = true; break; } file_position++; } } else { + string_clear(filename_str); dir_api->close(&directory); free(name); return; @@ -446,26 +465,29 @@ void file_select_set_selected_file_internal(FileSelect* file_select, const char* } } - with_view_model( - file_select->view, (FileSelectModel * model) { - uint16_t max_first_file_index = - model->file_count > FILENAME_COUNT ? model->file_count - FILENAME_COUNT : 0; + if(file_found) { + with_view_model( + file_select->view, (FileSelectModel * model) { + uint16_t max_first_file_index = + model->file_count > FILENAME_COUNT ? model->file_count - FILENAME_COUNT : 0; - model->first_file_index = file_position; + model->first_file_index = file_position; - if(model->first_file_index > 0) { - model->first_file_index -= 1; - } + if(model->first_file_index > 0) { + model->first_file_index -= 1; + } - if(model->first_file_index >= max_first_file_index) { - model->first_file_index = max_first_file_index; - } + if(model->first_file_index >= max_first_file_index) { + model->first_file_index = max_first_file_index; + } - model->position = file_position - model->first_file_index; + model->position = file_position - model->first_file_index; - return true; - }); + return true; + }); + } + string_clear(filename_str); dir_api->close(&directory); free(name); } diff --git a/applications/ibutton/ibutton-app.cpp b/applications/ibutton/ibutton-app.cpp index 5d8e0297..ead57e43 100644 --- a/applications/ibutton/ibutton-app.cpp +++ b/applications/ibutton/ibutton-app.cpp @@ -3,6 +3,9 @@ #include #include +const char* iButtonApp::app_folder = "ibutton"; +const char* iButtonApp::app_extension = ".ibtn"; + void iButtonApp::run(void) { iButtonEvent event; bool consumed; @@ -381,4 +384,205 @@ void iButtonApp::generate_random_name(char* name, uint8_t max_name_size) { name, max_name_size, "%s_%s", prefix[rand() % prefix_size], suffix[rand() % suffix_size]); // to upper name[0] = name[0] - 0x20; +} + +// file managment +void iButtonApp::show_file_error_message(const char* error_text) { + set_text_store(error_text); + get_sd_ex_api()->show_error(get_sd_ex_api()->context, get_text_store()); +} + +bool iButtonApp::save_key(const char* key_name) { + File key_file; + string_t key_file_name; + bool result = false; + FS_Error fs_result; + uint16_t write_count; + + // Create ibutton directory if necessary + fs_result = get_fs_api()->common.mkdir(app_folder); + if(fs_result != FSE_OK && fs_result != FSE_EXIST) { + show_file_error_message("Cannot create\napplication folder"); + return false; + }; + + // First remove key if it was saved + string_init_set_str(key_file_name, app_folder); + string_cat_str(key_file_name, "/"); + string_cat_str(key_file_name, get_key()->get_name()); + string_cat_str(key_file_name, app_extension); + fs_result = get_fs_api()->common.remove(string_get_cstr(key_file_name)); + if(fs_result != FSE_OK && fs_result != FSE_NOT_EXIST) { + string_clear(key_file_name); + show_file_error_message("Cannot remove\nold key file"); + return false; + }; + + // Save the key + get_key()->set_name(key_name); + string_set_str(key_file_name, app_folder); + string_cat_str(key_file_name, "/"); + string_cat_str(key_file_name, get_key()->get_name()); + string_cat_str(key_file_name, app_extension); + + bool res = get_fs_api()->file.open( + &key_file, string_get_cstr(key_file_name), FSAM_WRITE, FSOM_CREATE_ALWAYS); + string_clear(key_file_name); + + if(res) { + // type header + const char* key_type = "E"; + + switch(get_key()->get_key_type()) { + case iButtonKeyType::KeyCyfral: + key_type = "C"; + break; + case iButtonKeyType::KeyDallas: + key_type = "D"; + break; + case iButtonKeyType::KeyMetakom: + key_type = "M"; + break; + } + + write_count = get_fs_api()->file.write(&key_file, key_type, 1); + if(key_file.error_id != FSE_OK || write_count != 1) { + show_file_error_message("Cannot write\nto key file"); + get_fs_api()->file.close(&key_file); + return false; + } + + const uint8_t byte_text_size = 4; + char byte_text[byte_text_size]; + + for(uint8_t i = 0; i < get_key()->get_type_data_size(); i++) { + sniprintf(byte_text, byte_text_size, " %02X", get_key()->get_data()[i]); + write_count = get_fs_api()->file.write(&key_file, byte_text, 3); + if(key_file.error_id != FSE_OK || write_count != 3) { + show_file_error_message("Cannot write\nto key file"); + get_fs_api()->file.close(&key_file); + return false; + } + } + result = true; + } else { + show_file_error_message("Cannot create\nnew key file"); + } + + get_fs_api()->file.close(&key_file); + get_sd_ex_api()->check_error(get_sd_ex_api()->context); + + return result; +} + +bool iButtonApp::load_key() { + bool result = false; + + // Input events and views are managed by file_select + bool res = get_sd_ex_api()->file_select( + get_sd_ex_api()->context, + app_folder, + app_extension, + get_file_name(), + get_file_name_size(), + get_key()->get_name()); + + if(res) { + string_t key_str; + File key_file; + uint16_t read_count; + + // Get key file path + string_init_set_str(key_str, app_folder); + string_cat_str(key_str, "/"); + string_cat_str(key_str, get_file_name()); + string_cat_str(key_str, app_extension); + + // Open key file + get_fs_api()->file.open( + &key_file, string_get_cstr(key_str), FSAM_READ, FSOM_OPEN_EXISTING); + string_clear(key_str); + + if(key_file.error_id != FSE_OK) { + show_file_error_message("Cannot open\nkey file"); + get_fs_api()->file.close(&key_file); + return false; + } + + const uint8_t byte_text_size = 4; + char byte_text[byte_text_size] = {0, 0, 0, 0}; + + // load type header + read_count = get_fs_api()->file.read(&key_file, byte_text, 1); + if(key_file.error_id != FSE_OK || read_count != 1) { + show_file_error_message("Cannot read\nkey file"); + get_fs_api()->file.close(&key_file); + return false; + } + + iButtonKeyType key_type = iButtonKeyType::KeyCyfral; + if(strcmp(byte_text, "C") == 0) { + key_type = iButtonKeyType::KeyCyfral; + } else if(strcmp(byte_text, "M") == 0) { + key_type = iButtonKeyType::KeyMetakom; + } else if(strcmp(byte_text, "D") == 0) { + key_type = iButtonKeyType::KeyDallas; + } else { + show_file_error_message("Cannot parse\nkey file"); + get_fs_api()->file.close(&key_file); + return false; + } + + get_key()->set_type(key_type); + + // load data + uint8_t key_data[IBUTTON_KEY_DATA_SIZE] = {0, 0, 0, 0, 0, 0, 0, 0}; + for(uint8_t i = 0; i < get_key()->get_type_data_size(); i++) { + // space + read_count = get_fs_api()->file.read(&key_file, byte_text, 1); + if(key_file.error_id != FSE_OK || read_count != 1) { + show_file_error_message("Cannot read\nkey file"); + get_fs_api()->file.close(&key_file); + return false; + } + + // value + read_count = get_fs_api()->file.read(&key_file, byte_text, 2); + if(key_file.error_id != FSE_OK || read_count != 2) { + show_file_error_message("Cannot read\nkey file"); + get_fs_api()->file.close(&key_file); + return false; + } + + // convert hex value to byte + key_data[i] = strtol(byte_text, NULL, 16); + } + + get_fs_api()->file.close(&key_file); + + get_key()->set_name(get_file_name()); + get_key()->set_type(key_type); + get_key()->set_data(key_data, IBUTTON_KEY_DATA_SIZE); + + result = true; + } + + get_sd_ex_api()->check_error(get_sd_ex_api()->context); + + return result; +} + +bool iButtonApp::delete_key() { + iButtonKey* key = get_key(); + string_t key_file_name; + bool result = false; + + string_init_set_str(key_file_name, app_folder); + string_cat_str(key_file_name, "/"); + string_cat_str(key_file_name, key->get_name()); + string_cat_str(key_file_name, app_extension); + result = (get_fs_api()->common.remove(string_get_cstr(key_file_name)) == FSE_OK); + string_clear(key_file_name); + + return result; } \ No newline at end of file diff --git a/applications/ibutton/ibutton-app.h b/applications/ibutton/ibutton-app.h index 48db20f8..9d79601a 100644 --- a/applications/ibutton/ibutton-app.h +++ b/applications/ibutton/ibutton-app.h @@ -115,6 +115,10 @@ public: void generate_random_name(char* name, uint8_t max_name_size); + bool save_key(const char* key_name); + bool load_key(); + bool delete_key(); + private: std::list previous_scenes_list = {Scene::SceneExit}; Scene current_scene = Scene::SceneStart; @@ -162,4 +166,9 @@ private: bool read_hex_byte(string_t arg, uint8_t* byte); void print_key_data(void); + + static const char* app_folder; + static const char* app_extension; + + void show_file_error_message(const char* error_text); }; \ No newline at end of file diff --git a/applications/ibutton/scene/ibutton-scene-delete-confirm.cpp b/applications/ibutton/scene/ibutton-scene-delete-confirm.cpp index 84ba94b5..e7430fd7 100644 --- a/applications/ibutton/scene/ibutton-scene-delete-confirm.cpp +++ b/applications/ibutton/scene/ibutton-scene-delete-confirm.cpp @@ -55,18 +55,8 @@ bool iButtonSceneDeleteConfirm::on_event(iButtonApp* app, iButtonEvent* event) { if(event->type == iButtonEvent::Type::EventTypeDialogResult) { if(event->payload.dialog_result == DialogExResultRight) { - iButtonKey* key = app->get_key(); - string_t key_file_name; - string_init_set_str(key_file_name, "ibutton/"); - string_cat_str(key_file_name, key->get_name()); - bool res = - (app->get_fs_api()->common.remove(string_get_cstr(key_file_name)) == FSE_OK); - string_clear(key_file_name); - if(res) { + if(app->delete_key()) { app->switch_to_next_scene(iButtonApp::Scene::SceneDeleteSuccess); - } else { - // TODO error file path - // app->switch_to_next_scene(iButtonApp::Scene::SceneDeleteFail); } } else { app->switch_to_previous_scene(); diff --git a/applications/ibutton/scene/ibutton-scene-save-name.cpp b/applications/ibutton/scene/ibutton-scene-save-name.cpp index 46b6df7c..1bb614cf 100644 --- a/applications/ibutton/scene/ibutton-scene-save-name.cpp +++ b/applications/ibutton/scene/ibutton-scene-save-name.cpp @@ -31,40 +31,14 @@ bool iButtonSceneSaveName::on_event(iButtonApp* app, iButtonEvent* event) { bool consumed = false; if(event->type == iButtonEvent::Type::EventTypeTextEditResult) { - iButtonKey* key = app->get_key(); - File key_file; - string_t key_file_name; - - // Create ibutton directory if necessary - app->get_fs_api()->common.mkdir("ibutton"); - - // First remove key if it was saved - string_init_set_str(key_file_name, "ibutton/"); - string_cat_str(key_file_name, key->get_name()); - app->get_fs_api()->common.remove(string_get_cstr(key_file_name)); - - // Save the key - key->set_name(app->get_text_store()); - string_set_str(key_file_name, "ibutton/"); - string_cat_str(key_file_name, app->get_text_store()); - uint8_t key_data[IBUTTON_KEY_DATA_SIZE + 1]; - key_data[0] = static_cast(key->get_key_type()); - memcpy(key_data + 1, key->get_data(), IBUTTON_KEY_DATA_SIZE); - bool res = app->get_fs_api()->file.open( - &key_file, string_get_cstr(key_file_name), FSAM_WRITE, FSOM_CREATE_ALWAYS); - // TODO process file system errors from file system service - if(res) { - res = app->get_fs_api()->file.write(&key_file, key_data, IBUTTON_KEY_DATA_SIZE + 1); - res = app->get_fs_api()->file.close(&key_file); + if(app->save_key(app->get_text_store())) { app->switch_to_next_scene(iButtonApp::Scene::SceneSaveSuccess); } else { - app->get_sd_ex_api()->check_error(app->get_sd_ex_api()->context); app->search_and_switch_to_previous_scene( {iButtonApp::Scene::SceneReadedKeyMenu, iButtonApp::Scene::SceneSavedKeyMenu, iButtonApp::Scene::SceneAddType}); } - string_clear(key_file_name); consumed = true; } diff --git a/applications/ibutton/scene/ibutton-scene-select-key.cpp b/applications/ibutton/scene/ibutton-scene-select-key.cpp index 69b33ab8..b4f5db33 100644 --- a/applications/ibutton/scene/ibutton-scene-select-key.cpp +++ b/applications/ibutton/scene/ibutton-scene-select-key.cpp @@ -4,40 +4,8 @@ #include "../ibutton-key.h" void iButtonSceneSelectKey::on_enter(iButtonApp* app) { - // Input events and views are managed by file_select - bool res = app->get_sd_ex_api()->file_select( - app->get_sd_ex_api()->context, - "ibutton", - "*", - app->get_file_name(), - app->get_file_name_size(), - app->get_key()->get_name()); - // Process file_select return - if(res) { - // Get key file path - string_t key_str; - string_init_set_str(key_str, "ibutton/"); - string_cat_str(key_str, app->get_file_name()); - - // Read data from file - File key_file; - uint8_t key_data[IBUTTON_KEY_DATA_SIZE + 1] = {}; - // TODO process false result from file system service - app->get_fs_api()->file.open( - &key_file, string_get_cstr(key_str), FSAM_READ, FSOM_OPEN_EXISTING); - app->get_fs_api()->file.read(&key_file, key_data, IBUTTON_KEY_DATA_SIZE + 1); - app->get_fs_api()->file.close(&key_file); - string_clear(key_str); - - // Set key data - iButtonKeyType key_type = static_cast(key_data[0]); - if(key_type > iButtonKeyType::KeyMetakom) { - app->switch_to_next_scene(iButtonApp::Scene::SceneStart); - } - app->get_key()->set_name(app->get_file_name()); - app->get_key()->set_type(key_type); - app->get_key()->set_data(key_data + 1, IBUTTON_KEY_DATA_SIZE); + if(app->load_key()) { app->switch_to_next_scene(iButtonApp::Scene::SceneSavedKeyMenu); } else { app->switch_to_previous_scene(); diff --git a/applications/sd-filesystem/sd-filesystem-api.c b/applications/sd-filesystem/sd-filesystem-api.c index 26dceaf1..6b6c9fb9 100644 --- a/applications/sd-filesystem/sd-filesystem-api.c +++ b/applications/sd-filesystem/sd-filesystem-api.c @@ -268,7 +268,7 @@ uint16_t fs_file_read(File* file, void* buff, uint16_t const bytes_to_read) { } // Write data to the file -uint16_t fs_file_write(File* file, void* buff, uint16_t const bytes_to_write) { +uint16_t fs_file_write(File* file, const void* buff, uint16_t const bytes_to_write) { FileData* filedata = NULL; uint16_t bytes_written = 0; diff --git a/applications/sd-filesystem/sd-filesystem.c b/applications/sd-filesystem/sd-filesystem.c index 29c70736..f62a53c9 100644 --- a/applications/sd-filesystem/sd-filesystem.c +++ b/applications/sd-filesystem/sd-filesystem.c @@ -34,6 +34,7 @@ typedef enum { SdAppEventTypeEject, SdAppEventTypeFileSelect, SdAppEventTypeCheckError, + SdAppEventTypeShowError, } SdAppEventType; typedef struct { @@ -52,6 +53,7 @@ typedef struct { SdAppEventType type; union { SdAppFileSelectData file_select_data; + const char* error_text; } payload; } SdAppEvent; @@ -64,6 +66,7 @@ bool sd_api_file_select( uint8_t result_size, char* selected_filename); void sd_api_check_error(SdApp* sd_app); +void sd_api_show_error(SdApp* sd_app, const char* error_text); /******************* Allocators *******************/ @@ -125,6 +128,8 @@ SdApp* sd_app_alloc() { sd_app->sd_card_api.context = sd_app; sd_app->sd_card_api.file_select = sd_api_file_select; sd_app->sd_card_api.check_error = sd_api_check_error; + sd_app->sd_card_api.show_error = sd_api_show_error; + sd_app->sd_app_state = SdAppStateBackground; string_init(sd_app->text_holder); @@ -455,6 +460,7 @@ bool sd_api_file_select( break; } } + if(!retval) { sd_api_check_error(sd_app); } @@ -467,6 +473,11 @@ void sd_api_check_error(SdApp* sd_app) { furi_check(osMessageQueuePut(sd_app->event_queue, &message, 0, osWaitForever) == osOK); } +void sd_api_show_error(SdApp* sd_app, const char* error_text) { + SdAppEvent message = {.type = SdAppEventTypeShowError, .payload.error_text = error_text}; + furi_check(osMessageQueuePut(sd_app->event_queue, &message, 0, osWaitForever) == osOK); +} + /******************* View callbacks *******************/ void app_view_back_callback(void* context) { @@ -904,11 +915,16 @@ int32_t sd_filesystem(void* p) { dialog_ex_set_left_button_text(dialog, "Back"); if(sd_app->info.status == SD_NO_CARD) { dialog_ex_set_text( - dialog, "SD card\nnot found", 64, y_1_line, AlignLeft, AlignCenter); + dialog, + "SD card\nnot found", + 88, + y_1_line, + AlignCenter, + AlignCenter); dialog_ex_set_icon(dialog, 5, 6, I_SDQuestion_35x43); } else { dialog_ex_set_text( - dialog, "SD card\nerror", 64, y_1_line, AlignLeft, AlignCenter); + dialog, "SD card\nerror", 88, y_1_line, AlignCenter, AlignCenter); dialog_ex_set_icon(dialog, 5, 10, I_SDError_43x35); } sd_app->sd_app_state = SdAppStateCheckError; @@ -916,6 +932,17 @@ int32_t sd_filesystem(void* p) { } } break; + case SdAppEventTypeShowError: + if(try_to_alloc_view_holder(sd_app, gui)) { + DialogEx* dialog = alloc_and_attach_dialog(sd_app); + dialog_ex_set_left_button_text(dialog, "Back"); + dialog_ex_set_text( + dialog, event.payload.error_text, 88, y_1_line, AlignCenter, AlignCenter); + dialog_ex_set_icon(dialog, 5, 6, I_SDQuestion_35x43); + sd_app->sd_app_state = SdAppStateShowError; + view_holder_start(sd_app->view_holder); + } + break; } } } diff --git a/applications/sd-filesystem/sd-filesystem.h b/applications/sd-filesystem/sd-filesystem.h index 0170ef83..3a4da79c 100644 --- a/applications/sd-filesystem/sd-filesystem.h +++ b/applications/sd-filesystem/sd-filesystem.h @@ -87,6 +87,7 @@ typedef enum { SdAppStateEjected, SdAppStateFileSelect, SdAppStateCheckError, + SdAppStateShowError, } SdAppState; struct SdApp { @@ -115,7 +116,7 @@ SDError _fs_status(SdFsInfo* fs_info); bool fs_file_open(File* file, const char* path, FS_AccessMode access_mode, FS_OpenMode open_mode); bool fs_file_close(File* file); uint16_t fs_file_read(File* file, void* buff, uint16_t bytes_to_read); -uint16_t fs_file_write(File* file, void* buff, uint16_t bytes_to_write); +uint16_t fs_file_write(File* file, const void* buff, uint16_t bytes_to_write); bool fs_file_seek(File* file, uint32_t offset, bool from_start); uint64_t fs_file_tell(File* file); bool fs_file_truncate(File* file); diff --git a/lib/common-api/filesystem-api.h b/lib/common-api/filesystem-api.h index 15091292..c63f0d0e 100644 --- a/lib/common-api/filesystem-api.h +++ b/lib/common-api/filesystem-api.h @@ -174,7 +174,7 @@ typedef struct { bool (*open)(File* file, const char* path, FS_AccessMode access_mode, FS_OpenMode open_mode); bool (*close)(File* file); uint16_t (*read)(File* file, void* buff, uint16_t bytes_to_read); - uint16_t (*write)(File* file, void* buff, uint16_t bytes_to_write); + uint16_t (*write)(File* file, const void* buff, uint16_t bytes_to_write); bool (*seek)(File* file, uint32_t offset, bool from_start); uint64_t (*tell)(File* file); bool (*truncate)(File* file); diff --git a/lib/common-api/sd-card-api.h b/lib/common-api/sd-card-api.h index d7488ffa..c4b312eb 100644 --- a/lib/common-api/sd-card-api.h +++ b/lib/common-api/sd-card-api.h @@ -17,6 +17,7 @@ typedef struct { uint8_t result_size, char* selected_filename); void (*check_error)(SdApp* context); + void (*show_error)(SdApp* context, const char* error_text); } SdCard_Api; #ifdef __cplusplus