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 <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"} |     : notification{"notification"} { | ||||||
|     , sd_ex_api{"sdcard-ex"} |  | ||||||
|     , 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(!file_worker.mkdir(app_folder)) { | ||||||
|     if(fs_result != FSE_OK && fs_result != FSE_EXIST) { |  | ||||||
|         show_file_error_message("Cannot create\napplication 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_init_printf(key_file_name, "%s/%s%s", app_folder, get_key()->get_name(), app_extension); | ||||||
|     string_cat_str(key_file_name, "/"); |     if(!file_worker.remove(string_get_cstr(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); |         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_printf(key_file_name, "%s/%s%s", app_folder, get_key()->get_name(), app_extension); | ||||||
|     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( |     bool res = file_worker.open(string_get_cstr(key_file_name), FSAM_WRITE, FSOM_CREATE_ALWAYS); | ||||||
|         &key_file, 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(!file_worker.write(key_type, 2)) { | ||||||
|         if(key_file.error_id != FSE_OK || write_count != 1) { |             file_worker.close(); | ||||||
|             show_file_error_message("Cannot write\nto key file"); |  | ||||||
|             get_fs_api()->file.close(&key_file); |  | ||||||
|             return false; |             return false; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         const uint8_t byte_text_size = 4; |         if(!file_worker.write_hex(get_key()->get_data(), get_key()->get_type_data_size())) { | ||||||
|         char byte_text[byte_text_size]; |             file_worker.close(); | ||||||
| 
 |  | ||||||
|         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; |             return false; | ||||||
|         } |         } | ||||||
|         } |  | ||||||
|         result = true; |         result = true; | ||||||
|     } else { |  | ||||||
|         show_file_error_message("Cannot create\nnew key file"); |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     get_fs_api()->file.close(&key_file); |     file_worker.close(); | ||||||
|     get_sd_ex_api()->check_error(get_sd_ex_api()->context); |  | ||||||
| 
 | 
 | ||||||
|     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; |     FileWorker file_worker; | ||||||
|     uint16_t read_count; |  | ||||||
| 
 | 
 | ||||||
|     // Open key file
 |     // Open key file
 | ||||||
|     get_fs_api()->file.open(&key_file, string_get_cstr(key_path), FSAM_READ, FSOM_OPEN_EXISTING); |     if(!file_worker.open(string_get_cstr(key_path), FSAM_READ, FSOM_OPEN_EXISTING)) { | ||||||
|     if(key_file.error_id != FSE_OK) { |         file_worker.close(); | ||||||
|         show_file_error_message("Cannot open\nkey file"); |  | ||||||
|         get_fs_api()->file.close(&key_file); |  | ||||||
|         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
 |     // Load type header
 | ||||||
|     read_count = get_fs_api()->file.read(&key_file, byte_text, 1); |     if(!file_worker.read(byte_text, 2)) { | ||||||
|     if(key_file.error_id != FSE_OK || read_count != 1) { |         file_worker.close(); | ||||||
|         show_file_error_message("Cannot read\nkey file"); |  | ||||||
|         get_fs_api()->file.close(&key_file); |  | ||||||
|         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"); |         file_worker.show_error("Cannot parse\nkey file"); | ||||||
|         get_fs_api()->file.close(&key_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++) { |     if(!file_worker.read_hex(key_data, get_key()->get_type_data_size())) { | ||||||
|         // space
 |         get_key()->set_type(old_type); | ||||||
|         read_count = get_fs_api()->file.read(&key_file, byte_text, 1); |         file_worker.close(); | ||||||
|         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; |         return false; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|         // value
 |     file_worker.close(); | ||||||
|         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_type(key_type); |  | ||||||
|     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( |     bool res = file_worker.file_select( | ||||||
|         get_sd_ex_api()->context, |         app_folder, app_extension, get_file_name(), get_file_name_size(), get_key()->get_name()); | ||||||
|         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_init_printf(key_str, "%s/%s%s", app_folder, get_file_name(), app_extension); | ||||||
|         string_cat_str(key_str, "/"); |  | ||||||
|         string_cat_str(key_str, get_file_name()); |  | ||||||
|         string_cat_str(key_str, 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 file_name; | ||||||
|     string_t key_file_name; |  | ||||||
|     bool result = false; |     bool result = false; | ||||||
|  |     FileWorker file_worker; | ||||||
| 
 | 
 | ||||||
|     string_init_set_str(key_file_name, app_folder); |     string_init_printf(file_name, "%s/%s%s", app_folder, get_key()->get_name(), app_extension); | ||||||
|     string_cat_str(key_file_name, "/"); |     result = file_worker.remove(string_get_cstr(file_name)); | ||||||
|     string_cat_str(key_file_name, key->get_name()); |     string_clear(file_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; |     return result; | ||||||
| } | } | ||||||
| @ -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); | ||||||
| }; | }; | ||||||
| @ -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); | ||||||
| }; | }; | ||||||
| @ -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) { | void FileWorker::show_error_internal(const char* error_text) { | ||||||
|     string_set_str(error_string, error_text); |     if(!silent) { | ||||||
|     sd_ex_api.get()->show_error(sd_ex_api.get()->context, string_get_cstr(error_string)); |         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 <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); | ||||||
| }; | }; | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 SG
						SG