Преглед изворни кода

File worker helper. Slightly optimized iButton app code size. (#545)

* File worker: file operations helper
* Notification app: removed yield
* File worker: write operations, calls to system file widgets
* App ibutton: use file worker
* App ibutton: small save fix, forgotten byte
SG пре 4 година
родитељ
комит
dce3665f63

+ 44 - 118
applications/ibutton/ibutton-app.cpp

@@ -2,6 +2,7 @@
 #include <stdarg.h>
 #include <stdarg.h>
 #include <callback-connector.h>
 #include <callback-connector.h>
 #include <m-string.h>
 #include <m-string.h>
+#include <file-worker.h>
 
 
 const char* iButtonApp::app_folder = "ibutton";
 const char* iButtonApp::app_folder = "ibutton";
 const char* iButtonApp::app_extension = ".ibtn";
 const char* iButtonApp::app_extension = ".ibtn";
@@ -33,9 +34,7 @@ void iButtonApp::run(void* args) {
 }
 }
 
 
 iButtonApp::iButtonApp()
 iButtonApp::iButtonApp()
-    : fs_api{"sdcard"}
-    , sd_ex_api{"sdcard-ex"}
-    , notification{"notification"} {
+    : notification{"notification"} {
     api_hal_power_insomnia_enter();
     api_hal_power_insomnia_enter();
     key_worker = new KeyWorker(&ibutton_gpio);
     key_worker = new KeyWorker(&ibutton_gpio);
 
 
@@ -123,14 +122,6 @@ iButtonKey* iButtonApp::get_key() {
     return &key;
     return &key;
 }
 }
 
 
-SdCard_Api* iButtonApp::get_sd_ex_api() {
-    return sd_ex_api;
-}
-
-FS_Api* iButtonApp::get_fs_api() {
-    return fs_api;
-}
-
 char* iButtonApp::get_file_name() {
 char* iButtonApp::get_file_name() {
     return file_name;
     return file_name;
 }
 }
@@ -225,158 +216,105 @@ void iButtonApp::generate_random_name(char* name, uint8_t max_name_size) {
 }
 }
 
 
 // file managment
 // 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) {
 bool iButtonApp::save_key(const char* key_name) {
-    File key_file;
+    FileWorker file_worker;
     string_t key_file_name;
     string_t key_file_name;
     bool result = false;
     bool result = false;
-    FS_Error fs_result;
-    uint16_t write_count;
 
 
     // Create ibutton directory if necessary
     // 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");
+    if(!file_worker.mkdir(app_folder)) {
         return false;
         return false;
     };
     };
 
 
     // First remove key if it was saved
     // 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_init_printf(key_file_name, "%s/%s%s", app_folder, get_key()->get_name(), app_extension);
+    if(!file_worker.remove(string_get_cstr(key_file_name))) {
         string_clear(key_file_name);
         string_clear(key_file_name);
-        show_file_error_message("Cannot remove\nold key file");
         return false;
         return false;
     };
     };
 
 
     // Save the key
     // Save the key
     get_key()->set_name(key_name);
     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);
+    string_printf(key_file_name, "%s/%s%s", app_folder, get_key()->get_name(), app_extension);
 
 
-    bool res = get_fs_api()->file.open(
-        &key_file, string_get_cstr(key_file_name), FSAM_WRITE, FSOM_CREATE_ALWAYS);
+    bool res = file_worker.open(string_get_cstr(key_file_name), FSAM_WRITE, FSOM_CREATE_ALWAYS);
     string_clear(key_file_name);
     string_clear(key_file_name);
 
 
     if(res) {
     if(res) {
         // type header
         // type header
-        const char* key_type = "E";
+        const char* key_type = "E ";
 
 
         switch(get_key()->get_key_type()) {
         switch(get_key()->get_key_type()) {
         case iButtonKeyType::KeyCyfral:
         case iButtonKeyType::KeyCyfral:
-            key_type = "C";
+            key_type = "C ";
             break;
             break;
         case iButtonKeyType::KeyDallas:
         case iButtonKeyType::KeyDallas:
-            key_type = "D";
+            key_type = "D ";
             break;
             break;
         case iButtonKeyType::KeyMetakom:
         case iButtonKeyType::KeyMetakom:
-            key_type = "M";
+            key_type = "M ";
             break;
             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);
+        if(!file_worker.write(key_type, 2)) {
+            file_worker.close();
             return false;
             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;
-            }
+        if(!file_worker.write_hex(get_key()->get_data(), get_key()->get_type_data_size())) {
+            file_worker.close();
+            return false;
         }
         }
         result = true;
         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);
+    file_worker.close();
 
 
     return result;
     return result;
 }
 }
 
 
 bool iButtonApp::load_key_data(string_t key_path) {
 bool iButtonApp::load_key_data(string_t key_path) {
-    File key_file;
-    uint16_t read_count;
+    FileWorker file_worker;
 
 
     // Open key file
     // Open key file
-    get_fs_api()->file.open(&key_file, string_get_cstr(key_path), FSAM_READ, FSOM_OPEN_EXISTING);
-    if(key_file.error_id != FSE_OK) {
-        show_file_error_message("Cannot open\nkey file");
-        get_fs_api()->file.close(&key_file);
+    if(!file_worker.open(string_get_cstr(key_path), FSAM_READ, FSOM_OPEN_EXISTING)) {
+        file_worker.close();
         return false;
         return false;
     }
     }
 
 
     const uint8_t byte_text_size = 4;
     const uint8_t byte_text_size = 4;
     char byte_text[byte_text_size] = {0, 0, 0, 0};
     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);
+    // Load type header
+    if(!file_worker.read(byte_text, 2)) {
+        file_worker.close();
         return false;
         return false;
     }
     }
 
 
     iButtonKeyType key_type = iButtonKeyType::KeyCyfral;
     iButtonKeyType key_type = iButtonKeyType::KeyCyfral;
-    if(strcmp(byte_text, "C") == 0) {
+    if(strcmp(byte_text, "C ") == 0) {
         key_type = iButtonKeyType::KeyCyfral;
         key_type = iButtonKeyType::KeyCyfral;
-    } else if(strcmp(byte_text, "M") == 0) {
+    } else if(strcmp(byte_text, "M ") == 0) {
         key_type = iButtonKeyType::KeyMetakom;
         key_type = iButtonKeyType::KeyMetakom;
-    } else if(strcmp(byte_text, "D") == 0) {
+    } else if(strcmp(byte_text, "D ") == 0) {
         key_type = iButtonKeyType::KeyDallas;
         key_type = iButtonKeyType::KeyDallas;
     } else {
     } else {
-        show_file_error_message("Cannot parse\nkey file");
-        get_fs_api()->file.close(&key_file);
+        file_worker.show_error("Cannot parse\nkey file");
+        file_worker.close();
         return false;
         return false;
     }
     }
 
 
+    iButtonKeyType old_type = get_key()->get_key_type();
     get_key()->set_type(key_type);
     get_key()->set_type(key_type);
 
 
-    // load data
     uint8_t key_data[IBUTTON_KEY_DATA_SIZE] = {0, 0, 0, 0, 0, 0, 0, 0};
     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);
+    if(!file_worker.read_hex(key_data, get_key()->get_type_data_size())) {
+        get_key()->set_type(old_type);
+        file_worker.close();
+        return false;
     }
     }
 
 
-    get_fs_api()->file.close(&key_file);
-
-    get_key()->set_type(key_type);
+    file_worker.close();
     get_key()->set_data(key_data, IBUTTON_KEY_DATA_SIZE);
     get_key()->set_data(key_data, IBUTTON_KEY_DATA_SIZE);
 
 
     return true;
     return true;
@@ -406,24 +344,17 @@ bool iButtonApp::load_key(const char* key_name) {
 
 
 bool iButtonApp::load_key() {
 bool iButtonApp::load_key() {
     bool result = false;
     bool result = false;
+    FileWorker file_worker;
 
 
     // Input events and views are managed by file_select
     // 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());
+    bool res = file_worker.file_select(
+        app_folder, app_extension, get_file_name(), get_file_name_size(), get_key()->get_name());
 
 
     if(res) {
     if(res) {
         string_t key_str;
         string_t key_str;
 
 
         // Get key file path
         // 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);
+        string_init_printf(key_str, "%s/%s%s", app_folder, get_file_name(), app_extension);
 
 
         result = load_key_data(key_str);
         result = load_key_data(key_str);
         if(result) {
         if(result) {
@@ -432,22 +363,17 @@ bool iButtonApp::load_key() {
         string_clear(key_str);
         string_clear(key_str);
     }
     }
 
 
-    get_sd_ex_api()->check_error(get_sd_ex_api()->context);
-
     return result;
     return result;
 }
 }
 
 
 bool iButtonApp::delete_key() {
 bool iButtonApp::delete_key() {
-    iButtonKey* key = get_key();
-    string_t key_file_name;
+    string_t file_name;
     bool result = false;
     bool result = false;
+    FileWorker file_worker;
 
 
-    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);
+    string_init_printf(file_name, "%s/%s%s", app_folder, get_key()->get_name(), app_extension);
+    result = file_worker.remove(string_get_cstr(file_name));
+    string_clear(file_name);
 
 
     return result;
     return result;
 }
 }

+ 0 - 5
applications/ibutton/ibutton-app.h

@@ -90,8 +90,6 @@ public:
     char* get_text_store();
     char* get_text_store();
     uint8_t get_text_store_size();
     uint8_t get_text_store_size();
 
 
-    SdCard_Api* get_sd_ex_api();
-    FS_Api* get_fs_api();
     char* get_file_name();
     char* get_file_name();
     uint8_t get_file_name_size();
     uint8_t get_file_name_size();
 
 
@@ -132,8 +130,6 @@ private:
 
 
     iButtonKey key;
     iButtonKey key;
 
 
-    RecordController<FS_Api> fs_api;
-    RecordController<SdCard_Api> sd_ex_api;
     RecordController<NotificationApp> notification;
     RecordController<NotificationApp> notification;
 
 
     static const uint8_t file_name_size = 100;
     static const uint8_t file_name_size = 100;
@@ -145,6 +141,5 @@ private:
     static const char* app_folder;
     static const char* app_folder;
     static const char* app_extension;
     static const char* app_extension;
 
 
-    void show_file_error_message(const char* error_text);
     bool load_key_data(string_t key_path);
     bool load_key_data(string_t key_path);
 };
 };

+ 0 - 2
applications/notification/notification-app-api.c

@@ -22,7 +22,6 @@ void notification_message_block(NotificationApp* app, const NotificationSequence
         .sequence = sequence,
         .sequence = sequence,
         .back_event = osEventFlagsNew(NULL)};
         .back_event = osEventFlagsNew(NULL)};
     furi_check(osMessageQueuePut(app->queue, &m, 0, osWaitForever) == osOK);
     furi_check(osMessageQueuePut(app->queue, &m, 0, osWaitForever) == osOK);
-    osThreadYield();
     osEventFlagsWait(m.back_event, NOTIFICATION_EVENT_COMPLETE, osFlagsWaitAny, osWaitForever);
     osEventFlagsWait(m.back_event, NOTIFICATION_EVENT_COMPLETE, osFlagsWaitAny, osWaitForever);
     osEventFlagsDelete(m.back_event);
     osEventFlagsDelete(m.back_event);
 };
 };
@@ -33,7 +32,6 @@ void notification_internal_message_block(
     NotificationAppMessage m = {
     NotificationAppMessage m = {
         .type = InternalLayerMessage, .sequence = sequence, .back_event = osEventFlagsNew(NULL)};
         .type = InternalLayerMessage, .sequence = sequence, .back_event = osEventFlagsNew(NULL)};
     furi_check(osMessageQueuePut(app->queue, &m, 0, osWaitForever) == osOK);
     furi_check(osMessageQueuePut(app->queue, &m, 0, osWaitForever) == osOK);
-    osThreadYield();
     osEventFlagsWait(m.back_event, NOTIFICATION_EVENT_COMPLETE, osFlagsWaitAny, osWaitForever);
     osEventFlagsWait(m.back_event, NOTIFICATION_EVENT_COMPLETE, osFlagsWaitAny, osWaitForever);
     osEventFlagsDelete(m.back_event);
     osEventFlagsDelete(m.back_event);
 };
 };

+ 186 - 9
lib/app-scened-template/file-worker.cpp

@@ -1,20 +1,19 @@
 #include "file-worker.h"
 #include "file-worker.h"
 
 
-FileWorker::FileWorker()
+FileWorker::FileWorker(bool _silent)
     : fs_api{"sdcard"}
     : fs_api{"sdcard"}
     , sd_ex_api{"sdcard-ex"} {
     , sd_ex_api{"sdcard-ex"} {
-    string_init(error_string);
+    silent = _silent;
 }
 }
 
 
 FileWorker::~FileWorker() {
 FileWorker::~FileWorker() {
-    string_clear(error_string);
 }
 }
 
 
 bool FileWorker::open(const char* filename, FS_AccessMode access_mode, FS_OpenMode open_mode) {
 bool FileWorker::open(const char* filename, FS_AccessMode access_mode, FS_OpenMode open_mode) {
     bool result = fs_api.get()->file.open(&file, filename, access_mode, open_mode);
     bool result = fs_api.get()->file.open(&file, filename, access_mode, open_mode);
 
 
     if(!result) {
     if(!result) {
-        show_error_message("Cannot open\nfile");
+        show_error_internal("Cannot open\nfile");
         close();
         close();
         return false;
         return false;
     }
     }
@@ -32,7 +31,7 @@ bool FileWorker::mkdir(const char* dirname) {
     FS_Error fs_result = fs_api.get()->common.mkdir(dirname);
     FS_Error fs_result = fs_api.get()->common.mkdir(dirname);
 
 
     if(fs_result != FSE_OK && fs_result != FSE_EXIST) {
     if(fs_result != FSE_OK && fs_result != FSE_EXIST) {
-        show_error_message("Cannot create\nfolder");
+        show_error_internal("Cannot create\nfolder");
         return false;
         return false;
     };
     };
 
 
@@ -42,19 +41,197 @@ bool FileWorker::mkdir(const char* dirname) {
 bool FileWorker::remove(const char* filename) {
 bool FileWorker::remove(const char* filename) {
     FS_Error fs_result = fs_api.get()->common.remove(filename);
     FS_Error fs_result = fs_api.get()->common.remove(filename);
     if(fs_result != FSE_OK && fs_result != FSE_NOT_EXIST) {
     if(fs_result != FSE_OK && fs_result != FSE_NOT_EXIST) {
-        show_error_message("Cannot remove\nold file");
+        show_error_internal("Cannot remove\nold file");
         return false;
         return false;
     };
     };
 
 
     return check_common_errors();
     return check_common_errors();
 }
 }
 
 
+bool FileWorker::read(void* buffer, uint16_t bytes_to_read) {
+    if(!read_internal(buffer, bytes_to_read)) {
+        return false;
+    }
+
+    return check_common_errors();
+}
+
+bool FileWorker::read_until(string_t str_result, char separator) {
+    string_clean(str_result);
+    const uint8_t buffer_size = 32;
+    uint8_t buffer[buffer_size];
+
+    do {
+        uint16_t read_count = fs_api.get()->file.read(&file, buffer, buffer_size);
+        if(file.error_id != FSE_OK) {
+            show_error_internal("Cannot read\nfile");
+            return false;
+        }
+
+        bool result = false;
+        for(uint16_t i = 0; i < read_count; i++) {
+            if(buffer[i] == separator) {
+                uint64_t position;
+                if(!tell_internal(&position)) {
+                    return false;
+                }
+
+                position = position - read_count + i + 1;
+
+                if(!seek_internal(position, true)) {
+                    return false;
+                }
+
+                result = true;
+                break;
+            } else {
+                string_push_back(str_result, buffer[i]);
+            }
+        }
+
+        if(result || read_count == 0) {
+            break;
+        }
+    } while(true);
+
+    return check_common_errors();
+}
+
+bool FileWorker::read_hex(uint8_t* buffer, uint16_t bytes_to_read) {
+    uint8_t text[2];
+
+    for(uint8_t i = 0; i < bytes_to_read; i++) {
+        if(i != 0) {
+            // space
+            if(!read_internal(text, 1)) {
+                return false;
+            }
+        }
+
+        // actual data
+        if(!read_internal(text, 2)) {
+            return false;
+        }
+
+        // convert hex value to byte
+        buffer[i] = strtol(reinterpret_cast<char*>(text), NULL, 16);
+    }
+
+    return check_common_errors();
+}
+
+bool FileWorker::tell(uint64_t* position) {
+    if(!tell_internal(position)) {
+        return false;
+    }
+
+    return check_common_errors();
+}
+
+bool FileWorker::seek(uint64_t position, bool from_start) {
+    if(!seek_internal(position, from_start)) {
+        return false;
+    }
+
+    return check_common_errors();
+}
+
+bool FileWorker::write(const void* buffer, uint16_t bytes_to_write) {
+    if(!write_internal(buffer, bytes_to_write)) {
+        return false;
+    }
+
+    return check_common_errors();
+}
+
+bool FileWorker::write_hex(const uint8_t* buffer, uint16_t bytes_to_write) {
+    const uint8_t byte_text_size = 3;
+    char byte_text[byte_text_size];
+
+    for(uint8_t i = 0; i < bytes_to_write; i++) {
+        sniprintf(byte_text, byte_text_size, "%02X", buffer[i]);
+
+        if(i != 0) {
+            // space
+            const char* space = " ";
+            if(!write_internal(space, 1)) {
+                return false;
+            }
+        }
+
+        if(!write_internal(byte_text, 2)) {
+            return false;
+        }
+    }
+
+    return check_common_errors();
+}
+
+void FileWorker::show_error(const char* error_text) {
+    sd_ex_api.get()->show_error(sd_ex_api.get()->context, error_text);
+}
+
+bool FileWorker::file_select(
+    const char* path,
+    const char* extension,
+    char* result,
+    uint8_t result_size,
+    char* selected_filename) {
+    return sd_ex_api.get()->file_select(
+        sd_ex_api.get()->context, path, extension, result, result_size, selected_filename);
+
+}
+
 bool FileWorker::check_common_errors() {
 bool FileWorker::check_common_errors() {
     sd_ex_api.get()->check_error(sd_ex_api.get()->context);
     sd_ex_api.get()->check_error(sd_ex_api.get()->context);
     return true;
     return true;
 }
 }
 
 
-void FileWorker::show_error_message(const char* error_text) {
-    string_set_str(error_string, error_text);
-    sd_ex_api.get()->show_error(sd_ex_api.get()->context, string_get_cstr(error_string));
+void FileWorker::show_error_internal(const char* error_text) {
+    if(!silent) {
+        show_error(error_text);
+    }
 }
 }
+
+bool FileWorker::read_internal(void* buffer, uint16_t bytes_to_read) {
+    uint16_t read_count = fs_api.get()->file.read(&file, buffer, bytes_to_read);
+
+    if(file.error_id != FSE_OK || read_count != bytes_to_read) {
+        show_error_internal("Cannot read\nfile");
+        return false;
+    }
+
+    return true;
+}
+
+bool FileWorker::write_internal(const void* buffer, uint16_t bytes_to_write) {
+    uint16_t write_count = fs_api.get()->file.write(&file, buffer, bytes_to_write);
+
+    if(file.error_id != FSE_OK || write_count != bytes_to_write) {
+        show_error_internal("Cannot write\nto file");
+        return false;
+    }
+
+    return true;
+}
+
+bool FileWorker::tell_internal(uint64_t* position) {
+    *position = fs_api.get()->file.tell(&file);
+
+    if(file.error_id != FSE_OK) {
+        show_error_internal("Cannot tell\nfile offset");
+        return false;
+    }
+
+    return true;
+}
+
+bool FileWorker::seek_internal(uint64_t position, bool from_start) {
+    fs_api.get()->file.seek(&file, position, from_start);
+    if(file.error_id != FSE_OK) {
+        show_error_internal("Cannot seek\nfile");
+        return false;
+    }
+
+    return true;
+}

+ 127 - 3
lib/app-scened-template/file-worker.h

@@ -4,24 +4,148 @@
 #include <filesystem-api.h>
 #include <filesystem-api.h>
 #include <m-string.h>
 #include <m-string.h>
 
 
+/**
+ * @brief File operations helper class.
+ * Automatically opens API records, shows error message on error.
+ */
 class FileWorker {
 class FileWorker {
 public:
 public:
-    FileWorker();
+    FileWorker(bool silent = false);
     ~FileWorker();
     ~FileWorker();
 
 
     RecordController<FS_Api> fs_api;
     RecordController<FS_Api> fs_api;
     RecordController<SdCard_Api> sd_ex_api;
     RecordController<SdCard_Api> sd_ex_api;
 
 
+    /**
+     * @brief Open file
+     * 
+     * @param filename 
+     * @param access_mode 
+     * @param open_mode 
+     * @return true on success
+     */
     bool open(const char* filename, FS_AccessMode access_mode, FS_OpenMode open_mode);
     bool open(const char* filename, FS_AccessMode access_mode, FS_OpenMode open_mode);
+
+    /**
+     * @brief Close file
+     * 
+     * @return true on success
+     */
     bool close();
     bool close();
 
 
+    /**
+     * @brief Creates a directory. Doesn't show error if directory exist. 
+     * 
+     * @param dirname 
+     * @return true on success
+     */
     bool mkdir(const char* dirname);
     bool mkdir(const char* dirname);
+
+    /**
+     * @brief Removes the file. Doesn't show error if file doesn't exist.
+     * 
+     * @param filename 
+     * @return true on success  
+     */
     bool remove(const char* filename);
     bool remove(const char* filename);
 
 
+    /**
+     * @brief Reads data from a file.
+     * 
+     * @param buffer 
+     * @param bytes_to_read 
+     * @return true on success  
+     */
+    bool read(void* buffer, uint16_t bytes_to_read = 1);
+
+    /**
+     * @brief Reads data from a file until separator or EOF is found. 
+     * Moves seek pointer to the next symbol after the separator. The separator is not included in the result.
+     * 
+     * @param result 
+     * @param separator 
+     * @return true on success  
+     */
+    bool read_until(string_t result, char separator = '\n');
+
+    /**
+     * @brief Reads data in hexadecimal space-delimited format. For example "AF FF" in a file - [175, 255] in a read buffer.
+     * 
+     * @param buffer 
+     * @param bytes_to_read 
+     * @return true on success  
+     */
+    bool read_hex(uint8_t* buffer, uint16_t bytes_to_read = 1);
+
+    /**
+     * @brief Read seek pointer value
+     * 
+     * @param position 
+     * @return true on success  
+     */
+    bool tell(uint64_t* position);
+
+    /**
+     * @brief Set seek pointer value
+     * 
+     * @param position 
+     * @param from_start 
+     * @return true on success  
+     */
+    bool seek(uint64_t position, bool from_start);
+
+    /**
+     * @brief Write data to file.
+     * 
+     * @param buffer 
+     * @param bytes_to_write 
+     * @return true on success  
+     */
+    bool write(const void* buffer, uint16_t bytes_to_write = 1);
+
+    /**
+     * @brief Write data to file in hexadecimal space-delimited format. For example [175, 255] in a write buffer - "AF FF" in a file.
+     * 
+     * @param buffer 
+     * @param bytes_to_write 
+     * @return true on success  
+     */
+    bool write_hex(const uint8_t* buffer, uint16_t bytes_to_write = 1);
+
+    /**
+     * @brief Show system file error message
+     * 
+     * @param error_text 
+     */
+    void show_error(const char* error_text);
+
+    /**
+     * @brief Show system file select widget
+     * 
+     * @param path 
+     * @param extension 
+     * @param result 
+     * @param result_size 
+     * @param selected_filename 
+     * @return true if file was selected
+     */
+    bool file_select(
+        const char* path,
+        const char* extension,
+        char* result,
+        uint8_t result_size,
+        char* selected_filename);
+
 private:
 private:
     File file;
     File file;
+    bool silent;
 
 
     bool check_common_errors();
     bool check_common_errors();
-    void show_error_message(const char* error_text);
-    string_t error_string;
+
+    void show_error_internal(const char* error_text);
+
+    bool read_internal(void* buffer, uint16_t bytes_to_read = 1);
+    bool write_internal(const void* buffer, uint16_t bytes_to_write = 1);
+    bool tell_internal(uint64_t* position);
+    bool seek_internal(uint64_t position, bool from_start);
 };
 };