Просмотр исходного кода

[FL-1307] iButton key files: ASCII encoding and .ibtn extension (#493)

* GUI module submenu: fix documentation
* GUI module submenu: add submenu_set_selected_item fn
* App iButton: use submenu_set_selected_item to store and set selected item in submenu
* App iButton: swap write and emulate in "saved key menu" scene
* App iButton: file select can now switch to the previous selected file
* App iButton: swap write and emulate indexes in "saved key menu" scene
* Gui module file_select: work with separate extension
* iButton app: separate file managment, file error handling
* SD card api: custom error message
* iButton app: better file error handling

Co-authored-by: あく <alleteam@gmail.com>
SG 4 лет назад
Родитель
Сommit
7e6a97c3a3

+ 47 - 25
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);
 }

+ 204 - 0
applications/ibutton/ibutton-app.cpp

@@ -3,6 +3,9 @@
 #include <callback-connector.h>
 #include <m-string.h>
 
+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;
 }

+ 9 - 0
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<Scene> 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);
 };

+ 1 - 11
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();

+ 1 - 27
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<uint8_t>(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;
     }
 

+ 1 - 33
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<iButtonKeyType>(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();

+ 1 - 1
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;
 

+ 29 - 2
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;
             }
         }
     }

+ 2 - 1
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);

+ 1 - 1
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);

+ 1 - 0
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