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
This commit is contained in:
		
							parent
							
								
									22e1ecb642
								
							
						
					
					
						commit
						dce3665f63
					
				| @ -2,6 +2,7 @@ | ||||
| #include <stdarg.h> | ||||
| #include <callback-connector.h> | ||||
| #include <m-string.h> | ||||
| #include <file-worker.h> | ||||
| 
 | ||||
| const char* iButtonApp::app_folder = "ibutton"; | ||||
| const char* iButtonApp::app_extension = ".ibtn"; | ||||
| @ -33,9 +34,7 @@ void iButtonApp::run(void* args) { | ||||
| } | ||||
| 
 | ||||
| iButtonApp::iButtonApp() | ||||
|     : fs_api{"sdcard"} | ||||
|     , sd_ex_api{"sdcard-ex"} | ||||
|     , notification{"notification"} { | ||||
|     : notification{"notification"} { | ||||
|     api_hal_power_insomnia_enter(); | ||||
|     key_worker = new KeyWorker(&ibutton_gpio); | ||||
| 
 | ||||
| @ -123,14 +122,6 @@ iButtonKey* iButtonApp::get_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() { | ||||
|     return file_name; | ||||
| } | ||||
| @ -225,158 +216,105 @@ void iButtonApp::generate_random_name(char* name, uint8_t max_name_size) { | ||||
| } | ||||
| 
 | ||||
| // 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; | ||||
|     FileWorker file_worker; | ||||
|     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"); | ||||
|     if(!file_worker.mkdir(app_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_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); | ||||
|         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); | ||||
|     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); | ||||
| 
 | ||||
|     if(res) { | ||||
|         // type header
 | ||||
|         const char* key_type = "E"; | ||||
|         const char* key_type = "E "; | ||||
| 
 | ||||
|         switch(get_key()->get_key_type()) { | ||||
|         case iButtonKeyType::KeyCyfral: | ||||
|             key_type = "C"; | ||||
|             key_type = "C "; | ||||
|             break; | ||||
|         case iButtonKeyType::KeyDallas: | ||||
|             key_type = "D"; | ||||
|             key_type = "D "; | ||||
|             break; | ||||
|         case iButtonKeyType::KeyMetakom: | ||||
|             key_type = "M"; | ||||
|             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); | ||||
|         if(!file_worker.write(key_type, 2)) { | ||||
|             file_worker.close(); | ||||
|             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; | ||||
|     } 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; | ||||
| } | ||||
| 
 | ||||
| bool iButtonApp::load_key_data(string_t key_path) { | ||||
|     File key_file; | ||||
|     uint16_t read_count; | ||||
|     FileWorker file_worker; | ||||
| 
 | ||||
|     // 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; | ||||
|     } | ||||
| 
 | ||||
|     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); | ||||
|     // Load type header
 | ||||
|     if(!file_worker.read(byte_text, 2)) { | ||||
|         file_worker.close(); | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     iButtonKeyType key_type = iButtonKeyType::KeyCyfral; | ||||
|     if(strcmp(byte_text, "C") == 0) { | ||||
|     if(strcmp(byte_text, "C ") == 0) { | ||||
|         key_type = iButtonKeyType::KeyCyfral; | ||||
|     } else if(strcmp(byte_text, "M") == 0) { | ||||
|     } else if(strcmp(byte_text, "M ") == 0) { | ||||
|         key_type = iButtonKeyType::KeyMetakom; | ||||
|     } else if(strcmp(byte_text, "D") == 0) { | ||||
|     } 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); | ||||
|         file_worker.show_error("Cannot parse\nkey file"); | ||||
|         file_worker.close(); | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     iButtonKeyType old_type = get_key()->get_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}; | ||||
|     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); | ||||
| 
 | ||||
|     return true; | ||||
| @ -406,24 +344,17 @@ bool iButtonApp::load_key(const char* key_name) { | ||||
| 
 | ||||
| bool iButtonApp::load_key() { | ||||
|     bool result = false; | ||||
|     FileWorker file_worker; | ||||
| 
 | ||||
|     // 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) { | ||||
|         string_t key_str; | ||||
| 
 | ||||
|         // 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); | ||||
|         if(result) { | ||||
| @ -432,22 +363,17 @@ bool iButtonApp::load_key() { | ||||
|         string_clear(key_str); | ||||
|     } | ||||
| 
 | ||||
|     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; | ||||
|     string_t file_name; | ||||
|     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; | ||||
| } | ||||
| @ -90,8 +90,6 @@ public: | ||||
|     char* get_text_store(); | ||||
|     uint8_t get_text_store_size(); | ||||
| 
 | ||||
|     SdCard_Api* get_sd_ex_api(); | ||||
|     FS_Api* get_fs_api(); | ||||
|     char* get_file_name(); | ||||
|     uint8_t get_file_name_size(); | ||||
| 
 | ||||
| @ -132,8 +130,6 @@ private: | ||||
| 
 | ||||
|     iButtonKey key; | ||||
| 
 | ||||
|     RecordController<FS_Api> fs_api; | ||||
|     RecordController<SdCard_Api> sd_ex_api; | ||||
|     RecordController<NotificationApp> notification; | ||||
| 
 | ||||
|     static const uint8_t file_name_size = 100; | ||||
| @ -145,6 +141,5 @@ private: | ||||
|     static const char* app_folder; | ||||
|     static const char* app_extension; | ||||
| 
 | ||||
|     void show_file_error_message(const char* error_text); | ||||
|     bool load_key_data(string_t key_path); | ||||
| }; | ||||
| @ -22,7 +22,6 @@ void notification_message_block(NotificationApp* app, const NotificationSequence | ||||
|         .sequence = sequence, | ||||
|         .back_event = osEventFlagsNew(NULL)}; | ||||
|     furi_check(osMessageQueuePut(app->queue, &m, 0, osWaitForever) == osOK); | ||||
|     osThreadYield(); | ||||
|     osEventFlagsWait(m.back_event, NOTIFICATION_EVENT_COMPLETE, osFlagsWaitAny, osWaitForever); | ||||
|     osEventFlagsDelete(m.back_event); | ||||
| }; | ||||
| @ -33,7 +32,6 @@ void notification_internal_message_block( | ||||
|     NotificationAppMessage m = { | ||||
|         .type = InternalLayerMessage, .sequence = sequence, .back_event = osEventFlagsNew(NULL)}; | ||||
|     furi_check(osMessageQueuePut(app->queue, &m, 0, osWaitForever) == osOK); | ||||
|     osThreadYield(); | ||||
|     osEventFlagsWait(m.back_event, NOTIFICATION_EVENT_COMPLETE, osFlagsWaitAny, osWaitForever); | ||||
|     osEventFlagsDelete(m.back_event); | ||||
| }; | ||||
| @ -1,20 +1,19 @@ | ||||
| #include "file-worker.h" | ||||
| 
 | ||||
| FileWorker::FileWorker() | ||||
| FileWorker::FileWorker(bool _silent) | ||||
|     : fs_api{"sdcard"} | ||||
|     , sd_ex_api{"sdcard-ex"} { | ||||
|     string_init(error_string); | ||||
|     silent = _silent; | ||||
| } | ||||
| 
 | ||||
| FileWorker::~FileWorker() { | ||||
|     string_clear(error_string); | ||||
| } | ||||
| 
 | ||||
| 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); | ||||
| 
 | ||||
|     if(!result) { | ||||
|         show_error_message("Cannot open\nfile"); | ||||
|         show_error_internal("Cannot open\nfile"); | ||||
|         close(); | ||||
|         return false; | ||||
|     } | ||||
| @ -32,7 +31,7 @@ bool FileWorker::mkdir(const char* dirname) { | ||||
|     FS_Error fs_result = fs_api.get()->common.mkdir(dirname); | ||||
| 
 | ||||
|     if(fs_result != FSE_OK && fs_result != FSE_EXIST) { | ||||
|         show_error_message("Cannot create\nfolder"); | ||||
|         show_error_internal("Cannot create\nfolder"); | ||||
|         return false; | ||||
|     }; | ||||
| 
 | ||||
| @ -42,19 +41,197 @@ bool FileWorker::mkdir(const char* dirname) { | ||||
| bool FileWorker::remove(const char* filename) { | ||||
|     FS_Error fs_result = fs_api.get()->common.remove(filename); | ||||
|     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 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() { | ||||
|     sd_ex_api.get()->check_error(sd_ex_api.get()->context); | ||||
|     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; | ||||
| } | ||||
| @ -4,24 +4,148 @@ | ||||
| #include <filesystem-api.h> | ||||
| #include <m-string.h> | ||||
| 
 | ||||
| /**
 | ||||
|  * @brief File operations helper class. | ||||
|  * Automatically opens API records, shows error message on error. | ||||
|  */ | ||||
| class FileWorker { | ||||
| public: | ||||
|     FileWorker(); | ||||
|     FileWorker(bool silent = false); | ||||
|     ~FileWorker(); | ||||
| 
 | ||||
|     RecordController<FS_Api> fs_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); | ||||
| 
 | ||||
|     /**
 | ||||
|      * @brief Close file | ||||
|      *  | ||||
|      * @return true on success | ||||
|      */ | ||||
|     bool close(); | ||||
| 
 | ||||
|     /**
 | ||||
|      * @brief Creates a directory. Doesn't show error if directory exist.  | ||||
|      *  | ||||
|      * @param dirname  | ||||
|      * @return true on success | ||||
|      */ | ||||
|     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); | ||||
| 
 | ||||
|     /**
 | ||||
|      * @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: | ||||
|     File file; | ||||
|     bool silent; | ||||
| 
 | ||||
|     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); | ||||
| }; | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 SG
						SG