[FL-1791] Flipper file format (#740)
* Lib: new flipper file format library * Lib: flipper file format cpp wrapper * Storage: simple function for remove file and check error * iButton app: remove file worker, use new flipper file format instead * Dialogs: storage error message * Storage: simple function for mkdir and check error * iButton app: error messages * Libs: update makefile * RFID app: remove file worker, use new flipper file format instead * Flipper File: library documentation Co-authored-by: Aleksandr Kutuzov <alleteam@gmail.com>
This commit is contained in:
		
							parent
							
								
									e0c1928fde
								
							
						
					
					
						commit
						c8b36dd406
					
				| @ -60,3 +60,14 @@ DialogMessageButton dialog_message_show(DialogsApp* context, const DialogMessage | |||||||
| 
 | 
 | ||||||
|     return return_data.dialog_value; |     return return_data.dialog_value; | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | /****************** Storage error ******************/ | ||||||
|  | 
 | ||||||
|  | void dialog_message_show_storage_error(DialogsApp* context, const char* error_text) { | ||||||
|  |     DialogMessage* message = dialog_message_alloc(); | ||||||
|  |     dialog_message_set_text(message, error_text, 88, 32, AlignCenter, AlignCenter); | ||||||
|  |     dialog_message_set_icon(message, &I_SDQuestion_35x43, 5, 6); | ||||||
|  |     dialog_message_set_buttons(message, "Back", NULL, NULL); | ||||||
|  |     dialog_message_show(context, message); | ||||||
|  |     dialog_message_free(message); | ||||||
|  | } | ||||||
| @ -123,6 +123,13 @@ void dialog_message_set_buttons( | |||||||
|  */ |  */ | ||||||
| DialogMessageButton dialog_message_show(DialogsApp* context, const DialogMessage* message); | DialogMessageButton dialog_message_show(DialogsApp* context, const DialogMessage* message); | ||||||
| 
 | 
 | ||||||
|  | /**
 | ||||||
|  |  * Show SD error message (with question sign) | ||||||
|  |  * @param context  | ||||||
|  |  * @param error_text  | ||||||
|  |  */ | ||||||
|  | void dialog_message_show_storage_error(DialogsApp* context, const char* error_text); | ||||||
|  | 
 | ||||||
| #ifdef __cplusplus | #ifdef __cplusplus | ||||||
| } | } | ||||||
| #endif | #endif | ||||||
| @ -2,11 +2,12 @@ | |||||||
| #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-cpp.h> | #include <toolbox/path.h> | ||||||
| #include <lib/toolbox/path.h> | #include <toolbox/flipper-file-cpp.h> | ||||||
| 
 | 
 | ||||||
| const char* iButtonApp::app_folder = "/any/ibutton"; | const char* iButtonApp::app_folder = "/any/ibutton"; | ||||||
| const char* iButtonApp::app_extension = ".ibtn"; | const char* iButtonApp::app_extension = ".ibtn"; | ||||||
|  | const char* iButtonApp::app_filetype = "Flipper iButton key"; | ||||||
| 
 | 
 | ||||||
| void iButtonApp::run(void* args) { | void iButtonApp::run(void* args) { | ||||||
|     iButtonEvent event; |     iButtonEvent event; | ||||||
| @ -37,7 +38,9 @@ void iButtonApp::run(void* args) { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| iButtonApp::iButtonApp() | iButtonApp::iButtonApp() | ||||||
|     : notification{"notification"} { |     : notification{"notification"} | ||||||
|  |     , storage{"storage"} | ||||||
|  |     , dialogs{"dialogs"} { | ||||||
|     furi_hal_power_insomnia_enter(); |     furi_hal_power_insomnia_enter(); | ||||||
|     key_worker = new KeyWorker(&ibutton_gpio); |     key_worker = new KeyWorker(&ibutton_gpio); | ||||||
| } | } | ||||||
| @ -188,102 +191,90 @@ bool iButtonApp::save_key(const char* key_name) { | |||||||
|     // Create ibutton directory if necessary
 |     // Create ibutton directory if necessary
 | ||||||
|     make_app_folder(); |     make_app_folder(); | ||||||
| 
 | 
 | ||||||
|     FileWorkerCpp file_worker; |     FlipperFileCpp file(storage); | ||||||
|     string_t key_file_name; |     string_t key_file_name; | ||||||
|     bool result = false; |     bool result = false; | ||||||
|  |     string_init(key_file_name); | ||||||
| 
 | 
 | ||||||
|     // First remove key if it was saved
 |     do { | ||||||
|     string_init_printf(key_file_name, "%s/%s%s", app_folder, get_key()->get_name(), app_extension); |         // First remove key if it was saved (we rename the key)
 | ||||||
|     if(!file_worker.remove(string_get_cstr(key_file_name))) { |         if(!delete_key()) break; | ||||||
|         string_clear(key_file_name); |  | ||||||
|         return false; |  | ||||||
|     }; |  | ||||||
| 
 | 
 | ||||||
|     // Save the key
 |         // Save the key
 | ||||||
|     get_key()->set_name(key_name); |         key.set_name(key_name); | ||||||
|     string_printf(key_file_name, "%s/%s%s", app_folder, get_key()->get_name(), app_extension); |  | ||||||
| 
 | 
 | ||||||
|     bool res = file_worker.open(string_get_cstr(key_file_name), FSAM_WRITE, FSOM_CREATE_ALWAYS); |         // Set full file name, for new key
 | ||||||
|  |         string_printf(key_file_name, "%s/%s%s", app_folder, key.get_name(), app_extension); | ||||||
|  | 
 | ||||||
|  |         // Open file for write
 | ||||||
|  |         if(!file.new_write(string_get_cstr(key_file_name))) break; | ||||||
|  | 
 | ||||||
|  |         // Write header
 | ||||||
|  |         if(!file.write_header_cstr(iButtonApp::app_filetype, 1)) break; | ||||||
|  | 
 | ||||||
|  |         // Write key type
 | ||||||
|  |         if(!file.write_comment_cstr("Key type can be Cyfral, Dallas or Metakom")) break; | ||||||
|  |         const char* key_type = key.get_key_type_string_by_type(key.get_key_type()); | ||||||
|  |         if(!file.write_string_cstr("Key type", key_type)) break; | ||||||
|  | 
 | ||||||
|  |         // Write data
 | ||||||
|  |         if(!file.write_comment_cstr( | ||||||
|  |                "Data size for Cyfral is 2, for Metakom is 4, for Dallas is 8")) | ||||||
|  |             break; | ||||||
|  | 
 | ||||||
|  |         if(!file.write_hex_array("Data", key.get_data(), key.get_type_data_size())) break; | ||||||
|  |         result = true; | ||||||
|  | 
 | ||||||
|  |     } while(false); | ||||||
|  | 
 | ||||||
|  |     file.close(); | ||||||
|     string_clear(key_file_name); |     string_clear(key_file_name); | ||||||
| 
 | 
 | ||||||
|     if(res) { |     if(!result) { | ||||||
|         // type header
 |         dialog_message_show_storage_error(dialogs, "Cannot save\nkey file"); | ||||||
|         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; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         if(!file_worker.write(key_type, 2)) { |  | ||||||
|             file_worker.close(); |  | ||||||
|             return false; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         if(!file_worker.write_hex(get_key()->get_data(), get_key()->get_type_data_size())) { |  | ||||||
|             file_worker.close(); |  | ||||||
|             return false; |  | ||||||
|         } |  | ||||||
|         result = true; |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     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) { | ||||||
|     FileWorkerCpp file_worker; |     FlipperFileCpp file(storage); | ||||||
|  |     bool result = false; | ||||||
|  |     string_t data; | ||||||
|  |     string_init(data); | ||||||
| 
 | 
 | ||||||
|     // Open key file
 |     do { | ||||||
|     if(!file_worker.open(string_get_cstr(key_path), FSAM_READ, FSOM_OPEN_EXISTING)) { |         if(!file.open_read(string_get_cstr(key_path))) break; | ||||||
|         file_worker.close(); | 
 | ||||||
|         return false; |         // header
 | ||||||
|  |         uint32_t version; | ||||||
|  |         if(!file.read_header(data, &version)) break; | ||||||
|  |         if(string_cmp_str(data, iButtonApp::app_filetype) != 0) break; | ||||||
|  |         if(version != 1) break; | ||||||
|  | 
 | ||||||
|  |         // key type
 | ||||||
|  |         iButtonKeyType type; | ||||||
|  |         if(!file.read_string("Key type", data)) break; | ||||||
|  |         if(!key.get_key_type_by_type_string(string_get_cstr(data), &type)) break; | ||||||
|  | 
 | ||||||
|  |         // key data
 | ||||||
|  |         uint8_t key_data[IBUTTON_KEY_DATA_SIZE] = {0}; | ||||||
|  |         if(!file.read_hex_array("Data", key_data, key.get_type_data_size_by_type(type))) break; | ||||||
|  | 
 | ||||||
|  |         key.set_type(type); | ||||||
|  |         key.set_data(key_data, IBUTTON_KEY_DATA_SIZE); | ||||||
|  | 
 | ||||||
|  |         result = true; | ||||||
|  |     } while(false); | ||||||
|  | 
 | ||||||
|  |     file.close(); | ||||||
|  |     string_clear(data); | ||||||
|  | 
 | ||||||
|  |     if(!result) { | ||||||
|  |         dialog_message_show_storage_error(dialogs, "Cannot load\nkey file"); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     const uint8_t byte_text_size = 4; |     return result; | ||||||
|     char byte_text[byte_text_size] = {0, 0, 0, 0}; |  | ||||||
| 
 |  | ||||||
|     // 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) { |  | ||||||
|         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 { |  | ||||||
|         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); |  | ||||||
| 
 |  | ||||||
|     uint8_t key_data[IBUTTON_KEY_DATA_SIZE] = {0, 0, 0, 0, 0, 0, 0, 0}; |  | ||||||
|     if(!file_worker.read_hex(key_data, get_key()->get_type_data_size())) { |  | ||||||
|         get_key()->set_type(old_type); |  | ||||||
|         file_worker.close(); |  | ||||||
|         return false; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     file_worker.close(); |  | ||||||
|     get_key()->set_data(key_data, IBUTTON_KEY_DATA_SIZE); |  | ||||||
| 
 |  | ||||||
|     return true; |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool iButtonApp::load_key(const char* key_name) { | bool iButtonApp::load_key(const char* key_name) { | ||||||
| @ -303,11 +294,15 @@ bool iButtonApp::load_key(const char* key_name) { | |||||||
| 
 | 
 | ||||||
| bool iButtonApp::load_key() { | bool iButtonApp::load_key() { | ||||||
|     bool result = false; |     bool result = false; | ||||||
|     FileWorkerCpp file_worker; |  | ||||||
| 
 | 
 | ||||||
|     // Input events and views are managed by file_select
 |     // Input events and views are managed by file_select
 | ||||||
|     bool res = file_worker.file_select( |     bool res = dialog_file_select_show( | ||||||
|         app_folder, app_extension, get_file_name(), get_file_name_size(), get_key()->get_name()); |         dialogs, | ||||||
|  |         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; | ||||||
| @ -328,16 +323,16 @@ bool iButtonApp::load_key() { | |||||||
| bool iButtonApp::delete_key() { | bool iButtonApp::delete_key() { | ||||||
|     string_t file_name; |     string_t file_name; | ||||||
|     bool result = false; |     bool result = false; | ||||||
|     FileWorkerCpp file_worker; |  | ||||||
| 
 | 
 | ||||||
|     string_init_printf(file_name, "%s/%s%s", app_folder, get_key()->get_name(), app_extension); |     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)); |     result = storage_simply_remove(storage, string_get_cstr(file_name)); | ||||||
|     string_clear(file_name); |     string_clear(file_name); | ||||||
| 
 | 
 | ||||||
|     return result; |     return result; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void iButtonApp::make_app_folder() { | void iButtonApp::make_app_folder() { | ||||||
|     FileWorkerCpp file_worker; |     if(!storage_simply_mkdir(storage, app_folder)) { | ||||||
|     file_worker.mkdir(app_folder); |         dialog_message_show_storage_error(dialogs, "Cannot create\napp folder"); | ||||||
|  |     } | ||||||
| } | } | ||||||
| @ -30,6 +30,8 @@ | |||||||
| #include "ibutton-key.h" | #include "ibutton-key.h" | ||||||
| 
 | 
 | ||||||
| #include <notification/notification-messages.h> | #include <notification/notification-messages.h> | ||||||
|  | #include <storage/storage.h> | ||||||
|  | #include <dialogs/dialogs.h> | ||||||
| 
 | 
 | ||||||
| #include <record-controller.hpp> | #include <record-controller.hpp> | ||||||
| 
 | 
 | ||||||
| @ -126,6 +128,8 @@ private: | |||||||
|     iButtonKey key; |     iButtonKey key; | ||||||
| 
 | 
 | ||||||
|     RecordController<NotificationApp> notification; |     RecordController<NotificationApp> notification; | ||||||
|  |     RecordController<Storage> storage; | ||||||
|  |     RecordController<DialogsApp> dialogs; | ||||||
| 
 | 
 | ||||||
|     static const uint8_t file_name_size = 100; |     static const uint8_t file_name_size = 100; | ||||||
|     char file_name[file_name_size]; |     char file_name[file_name_size]; | ||||||
| @ -135,6 +139,7 @@ private: | |||||||
| 
 | 
 | ||||||
|     static const char* app_folder; |     static const char* app_folder; | ||||||
|     static const char* app_extension; |     static const char* app_extension; | ||||||
|  |     static const char* app_filetype; | ||||||
| 
 | 
 | ||||||
|     bool load_key_data(string_t key_path); |     bool load_key_data(string_t key_path); | ||||||
|     void make_app_folder(); |     void make_app_folder(); | ||||||
|  | |||||||
| @ -22,21 +22,7 @@ uint8_t* iButtonKey::get_data() { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| uint8_t iButtonKey::get_type_data_size() { | uint8_t iButtonKey::get_type_data_size() { | ||||||
|     uint8_t size = 0; |     return get_type_data_size_by_type(type); | ||||||
| 
 |  | ||||||
|     switch(type) { |  | ||||||
|     case iButtonKeyType::KeyCyfral: |  | ||||||
|         size = 2; |  | ||||||
|         break; |  | ||||||
|     case iButtonKeyType::KeyMetakom: |  | ||||||
|         size = 4; |  | ||||||
|         break; |  | ||||||
|     case iButtonKeyType::KeyDallas: |  | ||||||
|         size = 8; |  | ||||||
|         break; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     return size; |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void iButtonKey::set_name(const char* _name) { | void iButtonKey::set_name(const char* _name) { | ||||||
| @ -55,5 +41,55 @@ iButtonKeyType iButtonKey::get_key_type() { | |||||||
|     return type; |     return type; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | const char* iButtonKey::get_key_type_string_by_type(iButtonKeyType key_type) { | ||||||
|  |     switch(key_type) { | ||||||
|  |     case iButtonKeyType::KeyCyfral: | ||||||
|  |         return "Cyfral"; | ||||||
|  |         break; | ||||||
|  |     case iButtonKeyType::KeyMetakom: | ||||||
|  |         return "Metakom"; | ||||||
|  |         break; | ||||||
|  |     case iButtonKeyType::KeyDallas: | ||||||
|  |         return "Dallas"; | ||||||
|  |         break; | ||||||
|  |     default: | ||||||
|  |         furi_crash("Invalid iButton type"); | ||||||
|  |         return ""; | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool iButtonKey::get_key_type_by_type_string(const char* type_string, iButtonKeyType* key_type) { | ||||||
|  |     if(strcmp(type_string, get_key_type_string_by_type(iButtonKeyType::KeyCyfral)) == 0) { | ||||||
|  |         *key_type = iButtonKeyType::KeyCyfral; | ||||||
|  |     } else if(strcmp(type_string, get_key_type_string_by_type(iButtonKeyType::KeyMetakom)) == 0) { | ||||||
|  |         *key_type = iButtonKeyType::KeyMetakom; | ||||||
|  |     } else if(strcmp(type_string, get_key_type_string_by_type(iButtonKeyType::KeyDallas)) == 0) { | ||||||
|  |         *key_type = iButtonKeyType::KeyDallas; | ||||||
|  |     } else { | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | uint8_t iButtonKey::get_type_data_size_by_type(iButtonKeyType key_type) { | ||||||
|  |     uint8_t size = 0; | ||||||
|  | 
 | ||||||
|  |     switch(key_type) { | ||||||
|  |     case iButtonKeyType::KeyCyfral: | ||||||
|  |         size = 2; | ||||||
|  |         break; | ||||||
|  |     case iButtonKeyType::KeyMetakom: | ||||||
|  |         size = 4; | ||||||
|  |         break; | ||||||
|  |     case iButtonKeyType::KeyDallas: | ||||||
|  |         size = 8; | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return size; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| iButtonKey::iButtonKey() { | iButtonKey::iButtonKey() { | ||||||
| } | } | ||||||
|  | |||||||
| @ -17,6 +17,10 @@ public: | |||||||
|     void set_type(iButtonKeyType key_type); |     void set_type(iButtonKeyType key_type); | ||||||
|     iButtonKeyType get_key_type(); |     iButtonKeyType get_key_type(); | ||||||
| 
 | 
 | ||||||
|  |     const char* get_key_type_string_by_type(iButtonKeyType key_type); | ||||||
|  |     bool get_key_type_by_type_string(const char* type_string, iButtonKeyType* key_type); | ||||||
|  |     uint8_t get_type_data_size_by_type(iButtonKeyType key_type); | ||||||
|  | 
 | ||||||
|     iButtonKey(); |     iButtonKey(); | ||||||
| 
 | 
 | ||||||
| private: | private: | ||||||
|  | |||||||
| @ -16,15 +16,18 @@ | |||||||
| #include "scene/lfrfid-app-scene-delete-confirm.h" | #include "scene/lfrfid-app-scene-delete-confirm.h" | ||||||
| #include "scene/lfrfid-app-scene-delete-success.h" | #include "scene/lfrfid-app-scene-delete-success.h" | ||||||
| 
 | 
 | ||||||
| #include <file-worker-cpp.h> |  | ||||||
| #include <lib/toolbox/path.h> | #include <lib/toolbox/path.h> | ||||||
|  | #include <lib/toolbox/flipper-file-cpp.h> | ||||||
| 
 | 
 | ||||||
| const char* LfRfidApp::app_folder = "/any/lfrfid"; | const char* LfRfidApp::app_folder = "/any/lfrfid"; | ||||||
| const char* LfRfidApp::app_extension = ".rfid"; | const char* LfRfidApp::app_extension = ".rfid"; | ||||||
|  | const char* LfRfidApp::app_filetype = "Flipper RFID key"; | ||||||
| 
 | 
 | ||||||
| LfRfidApp::LfRfidApp() | LfRfidApp::LfRfidApp() | ||||||
|     : scene_controller{this} |     : scene_controller{this} | ||||||
|     , notification{"notification"} |     , notification{"notification"} | ||||||
|  |     , storage{"storage"} | ||||||
|  |     , dialogs{"dialogs"} | ||||||
|     , text_store(40) { |     , text_store(40) { | ||||||
|     furi_hal_power_insomnia_enter(); |     furi_hal_power_insomnia_enter(); | ||||||
| } | } | ||||||
| @ -77,20 +80,20 @@ bool LfRfidApp::save_key(RfidKey* key) { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool LfRfidApp::load_key_from_file_select(bool need_restore) { | bool LfRfidApp::load_key_from_file_select(bool need_restore) { | ||||||
|     FileWorkerCpp file_worker; |  | ||||||
|     TextStore* filename_ts = new TextStore(64); |     TextStore* filename_ts = new TextStore(64); | ||||||
|     bool result; |     bool result = false; | ||||||
| 
 | 
 | ||||||
|     if(need_restore) { |     if(need_restore) { | ||||||
|         result = file_worker.file_select( |         result = dialog_file_select_show( | ||||||
|  |             dialogs, | ||||||
|             app_folder, |             app_folder, | ||||||
|             app_extension, |             app_extension, | ||||||
|             filename_ts->text, |             filename_ts->text, | ||||||
|             filename_ts->text_size, |             filename_ts->text_size, | ||||||
|             worker.key.get_name()); |             worker.key.get_name()); | ||||||
|     } else { |     } else { | ||||||
|         result = file_worker.file_select( |         result = dialog_file_select_show( | ||||||
|             app_folder, app_extension, filename_ts->text, filename_ts->text_size, NULL); |             dialogs, app_folder, app_extension, filename_ts->text, filename_ts->text_size, NULL); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     if(result) { |     if(result) { | ||||||
| @ -105,86 +108,87 @@ bool LfRfidApp::load_key_from_file_select(bool need_restore) { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool LfRfidApp::delete_key(RfidKey* key) { | bool LfRfidApp::delete_key(RfidKey* key) { | ||||||
|     FileWorkerCpp file_worker; |  | ||||||
|     string_t file_name; |     string_t file_name; | ||||||
|     bool result = false; |     bool result = false; | ||||||
| 
 | 
 | ||||||
|     string_init_printf(file_name, "%s/%s%s", app_folder, key->get_name(), app_extension); |     string_init_printf(file_name, "%s/%s%s", app_folder, key->get_name(), app_extension); | ||||||
|     result = file_worker.remove(string_get_cstr(file_name)); |     result = storage_simply_remove(storage, string_get_cstr(file_name)); | ||||||
|     string_clear(file_name); |     string_clear(file_name); | ||||||
| 
 | 
 | ||||||
|     return result; |     return result; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool LfRfidApp::load_key_data(const char* path, RfidKey* key) { | bool LfRfidApp::load_key_data(const char* path, RfidKey* key) { | ||||||
|     FileWorkerCpp file_worker; |     FlipperFileCpp file(storage); | ||||||
|     bool result = false; |     bool result = false; | ||||||
|  |     string_t str_result; | ||||||
|  |     string_init(str_result); | ||||||
| 
 | 
 | ||||||
|     bool res = file_worker.open(path, FSAM_READ, FSOM_OPEN_EXISTING); |     do { | ||||||
|  |         if(!file.open_read(path)) break; | ||||||
| 
 | 
 | ||||||
|     if(res) { |         // header
 | ||||||
|         string_t str_result; |         uint32_t version; | ||||||
|         string_init(str_result); |         if(!file.read_header(str_result, &version)) break; | ||||||
|  |         if(string_cmp_str(str_result, app_filetype) != 0) break; | ||||||
|  |         if(version != 1) break; | ||||||
| 
 | 
 | ||||||
|         do { |         // key type
 | ||||||
|             RfidKey loaded_key; |         LfrfidKeyType type; | ||||||
|             LfrfidKeyType loaded_type; |         RfidKey loaded_key; | ||||||
| 
 | 
 | ||||||
|             // load type
 |         if(!file.read_string("Key type", str_result)) break; | ||||||
|             if(!file_worker.read_until(str_result, ' ')) break; |         if(!lfrfid_key_get_string_type(string_get_cstr(str_result), &type)) break; | ||||||
|             if(!lfrfid_key_get_string_type(string_get_cstr(str_result), &loaded_type)) { |         loaded_key.set_type(type); | ||||||
|                 file_worker.show_error("Cannot parse\nfile"); |  | ||||||
|                 break; |  | ||||||
|             } |  | ||||||
|             loaded_key.set_type(loaded_type); |  | ||||||
| 
 | 
 | ||||||
|             // load data
 |         // key data
 | ||||||
|             uint8_t tmp_data[loaded_key.get_type_data_count()]; |         uint8_t key_data[loaded_key.get_type_data_count()] = {}; | ||||||
|             if(!file_worker.read_hex(tmp_data, loaded_key.get_type_data_count())) break; |         if(!file.read_hex_array("Data", key_data, loaded_key.get_type_data_count())) break; | ||||||
|             loaded_key.set_data(tmp_data, loaded_key.get_type_data_count()); |         loaded_key.set_data(key_data, loaded_key.get_type_data_count()); | ||||||
| 
 | 
 | ||||||
|             *key = loaded_key; |  | ||||||
|             result = true; |  | ||||||
|         } while(0); |  | ||||||
| 
 |  | ||||||
|         // load name
 |  | ||||||
|         path_extract_filename_no_ext(path, str_result); |         path_extract_filename_no_ext(path, str_result); | ||||||
|         key->set_name(string_get_cstr(str_result)); |         loaded_key.set_name(string_get_cstr(str_result)); | ||||||
| 
 | 
 | ||||||
|         string_clear(str_result); |         *key = loaded_key; | ||||||
|  |         result = true; | ||||||
|  |     } while(0); | ||||||
|  | 
 | ||||||
|  |     file.close(); | ||||||
|  |     string_clear(str_result); | ||||||
|  | 
 | ||||||
|  |     if(!result) { | ||||||
|  |         dialog_message_show_storage_error(dialogs, "Cannot load\nkey file"); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     file_worker.close(); |  | ||||||
| 
 |  | ||||||
|     return result; |     return result; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool LfRfidApp::save_key_data(const char* path, RfidKey* key) { | bool LfRfidApp::save_key_data(const char* path, RfidKey* key) { | ||||||
|     FileWorkerCpp file_worker; |     FlipperFileCpp file(storage); | ||||||
|     bool result = false; |     bool result = false; | ||||||
| 
 | 
 | ||||||
|     bool res = file_worker.open(path, FSAM_WRITE, FSOM_CREATE_ALWAYS); |     do { | ||||||
|  |         if(!file.new_write(path)) break; | ||||||
|  |         if(!file.write_header_cstr(app_filetype, 1)) break; | ||||||
|  |         if(!file.write_comment_cstr("Key type can be EM4100, H10301 or I40134")) break; | ||||||
|  |         if(!file.write_string_cstr("Key type", lfrfid_key_get_type_string(key->get_type()))) break; | ||||||
|  |         if(!file.write_comment_cstr("Data size for EM4100 is 5, for H10301 is 3, for I40134 is 3")) | ||||||
|  |             break; | ||||||
|  |         if(!file.write_hex_array("Data", key->get_data(), key->get_type_data_count())) break; | ||||||
|  |         result = true; | ||||||
|  |     } while(0); | ||||||
| 
 | 
 | ||||||
|     if(res) { |     file.close(); | ||||||
|         do { |  | ||||||
|             // type header
 |  | ||||||
|             const char* key_type = lfrfid_key_get_type_string(key->get_type()); |  | ||||||
|             char delimeter = ' '; |  | ||||||
| 
 | 
 | ||||||
|             if(!file_worker.write(key_type, strlen(key_type))) break; |     if(!result) { | ||||||
|             if(!file_worker.write(&delimeter)) break; |         dialog_message_show_storage_error(dialogs, "Cannot save\nkey file"); | ||||||
|             if(!file_worker.write_hex(key->get_data(), key->get_type_data_count())) break; |  | ||||||
| 
 |  | ||||||
|             result = true; |  | ||||||
|         } while(0); |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     file_worker.close(); |  | ||||||
| 
 |  | ||||||
|     return result; |     return result; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void LfRfidApp::make_app_folder() { | void LfRfidApp::make_app_folder() { | ||||||
|     FileWorkerCpp file_worker; |     if(!storage_simply_mkdir(storage, app_folder)) { | ||||||
|     file_worker.mkdir(app_folder); |         dialog_message_show_storage_error(dialogs, "Cannot create\napp folder"); | ||||||
|  |     } | ||||||
| } | } | ||||||
| @ -16,6 +16,8 @@ | |||||||
| #include "view/container-vm.h" | #include "view/container-vm.h" | ||||||
| 
 | 
 | ||||||
| #include <notification/notification-messages.h> | #include <notification/notification-messages.h> | ||||||
|  | #include <storage/storage.h> | ||||||
|  | #include <dialogs/dialogs.h> | ||||||
| 
 | 
 | ||||||
| #include "helpers/rfid-worker.h" | #include "helpers/rfid-worker.h" | ||||||
| 
 | 
 | ||||||
| @ -63,6 +65,8 @@ public: | |||||||
|     LfRfidApp(); |     LfRfidApp(); | ||||||
| 
 | 
 | ||||||
|     RecordController<NotificationApp> notification; |     RecordController<NotificationApp> notification; | ||||||
|  |     RecordController<Storage> storage; | ||||||
|  |     RecordController<DialogsApp> dialogs; | ||||||
| 
 | 
 | ||||||
|     RfidWorker worker; |     RfidWorker worker; | ||||||
| 
 | 
 | ||||||
| @ -72,6 +76,7 @@ public: | |||||||
| 
 | 
 | ||||||
|     static const char* app_folder; |     static const char* app_folder; | ||||||
|     static const char* app_extension; |     static const char* app_extension; | ||||||
|  |     static const char* app_filetype; | ||||||
| 
 | 
 | ||||||
|     bool save_key(RfidKey* key); |     bool save_key(RfidKey* key); | ||||||
|     bool load_key_from_file_select(bool need_restore); |     bool load_key_from_file_select(bool need_restore); | ||||||
|  | |||||||
| @ -380,4 +380,16 @@ void storage_file_free(File* file) { | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     free(file); |     free(file); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool storage_simply_remove(Storage* storage, const char* path) { | ||||||
|  |     FS_Error result; | ||||||
|  |     result = storage_common_remove(storage, path); | ||||||
|  |     return result == FSE_OK || result == FSE_NOT_EXIST; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool storage_simply_mkdir(Storage* storage, const char* path) { | ||||||
|  |     FS_Error result; | ||||||
|  |     result = storage_common_mkdir(storage, path); | ||||||
|  |     return result == FSE_OK || result == FSE_EXIST; | ||||||
| } | } | ||||||
| @ -230,6 +230,24 @@ FS_Error storage_sd_info(Storage* api, SDInfo* info); | |||||||
|  */ |  */ | ||||||
| FS_Error storage_sd_status(Storage* api); | FS_Error storage_sd_status(Storage* api); | ||||||
| 
 | 
 | ||||||
|  | /***************** Simplified Functions ******************/ | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Removes a file/directory from the repository, the directory must be empty and the file/directory must not be open | ||||||
|  |  * @param storage pointer to the api | ||||||
|  |  * @param path  | ||||||
|  |  * @return true on success or if file/dir is not exist | ||||||
|  |  */ | ||||||
|  | bool storage_simply_remove(Storage* storage, const char* path); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Creates a directory | ||||||
|  |  * @param storage  | ||||||
|  |  * @param path  | ||||||
|  |  * @return true on success or if directory is already exist | ||||||
|  |  */ | ||||||
|  | bool storage_simply_mkdir(Storage* storage, const char* path); | ||||||
|  | 
 | ||||||
| #ifdef __cplusplus | #ifdef __cplusplus | ||||||
| } | } | ||||||
| #endif | #endif | ||||||
| @ -108,6 +108,7 @@ CPP_SOURCES		+= $(wildcard $(LIB_DIR)/app-scened-template/*/*.cpp) | |||||||
| 
 | 
 | ||||||
| # Toolbox
 | # Toolbox
 | ||||||
| C_SOURCES		+= $(wildcard $(LIB_DIR)/toolbox/*.c) | C_SOURCES		+= $(wildcard $(LIB_DIR)/toolbox/*.c) | ||||||
|  | CPP_SOURCES		+= $(wildcard $(LIB_DIR)/toolbox/*.cpp) | ||||||
| 
 | 
 | ||||||
| # USB Stack
 | # USB Stack
 | ||||||
| CFLAGS			+= -I$(LIB_DIR)/libusb_stm32/inc | CFLAGS			+= -I$(LIB_DIR)/libusb_stm32/inc | ||||||
|  | |||||||
							
								
								
									
										72
									
								
								lib/toolbox/flipper-file-cpp.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										72
									
								
								lib/toolbox/flipper-file-cpp.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,72 @@ | |||||||
|  | #include "flipper-file-cpp.h" | ||||||
|  | 
 | ||||||
|  | FlipperFileCpp::FlipperFileCpp(Storage* storage) { | ||||||
|  |     file = flipper_file_alloc(storage); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | FlipperFileCpp::~FlipperFileCpp() { | ||||||
|  |     flipper_file_free(file); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool FlipperFileCpp::open_read(const char* filename) { | ||||||
|  |     return flipper_file_open_read(file, filename); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool FlipperFileCpp::new_write(const char* filename) { | ||||||
|  |     return flipper_file_new_write(file, filename); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool FlipperFileCpp::close() { | ||||||
|  |     return flipper_file_close(file); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool FlipperFileCpp::read_header(string_t filetype, uint32_t* version) { | ||||||
|  |     return flipper_file_read_header(file, filetype, version); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool FlipperFileCpp::write_header(string_t filetype, const uint32_t version) { | ||||||
|  |     return flipper_file_write_header(file, filetype, version); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool FlipperFileCpp::write_header_cstr(const char* filetype, const uint32_t version) { | ||||||
|  |     return flipper_file_write_header_cstr(file, filetype, version); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool FlipperFileCpp::read_string(const char* key, string_t data) { | ||||||
|  |     return flipper_file_read_string(file, key, data); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool FlipperFileCpp::write_string(const char* key, string_t data) { | ||||||
|  |     return flipper_file_write_string(file, key, data); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool FlipperFileCpp::write_string_cstr(const char* key, const char* data) { | ||||||
|  |     return flipper_file_write_string_cstr(file, key, data); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool FlipperFileCpp::read_uint32(const char* key, uint32_t* data) { | ||||||
|  |     return flipper_file_read_uint32(file, key, data); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool FlipperFileCpp::write_uint32(const char* key, const uint32_t data) { | ||||||
|  |     return flipper_file_write_uint32(file, key, data); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool FlipperFileCpp::write_comment(string_t data) { | ||||||
|  |     return flipper_file_write_comment(file, data); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool FlipperFileCpp::write_comment_cstr(const char* data) { | ||||||
|  |     return flipper_file_write_comment_cstr(file, data); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool FlipperFileCpp::write_hex_array( | ||||||
|  |     const char* key, | ||||||
|  |     const uint8_t* data, | ||||||
|  |     const uint16_t data_size) { | ||||||
|  |     return flipper_file_write_hex_array(file, key, data, data_size); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool FlipperFileCpp::read_hex_array(const char* key, uint8_t* data, const uint16_t data_size) { | ||||||
|  |     return flipper_file_read_hex_array(file, key, data, data_size); | ||||||
|  | } | ||||||
							
								
								
									
										41
									
								
								lib/toolbox/flipper-file-cpp.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								lib/toolbox/flipper-file-cpp.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,41 @@ | |||||||
|  | #pragma once | ||||||
|  | #include "flipper-file.h" | ||||||
|  | 
 | ||||||
|  | class FlipperFileCpp { | ||||||
|  | private: | ||||||
|  |     FlipperFile* file; | ||||||
|  | 
 | ||||||
|  | public: | ||||||
|  |     FlipperFileCpp(Storage* storage); | ||||||
|  |     ~FlipperFileCpp(); | ||||||
|  | 
 | ||||||
|  |     bool open_read(const char* filename); | ||||||
|  | 
 | ||||||
|  |     bool new_write(const char* filename); | ||||||
|  | 
 | ||||||
|  |     bool close(); | ||||||
|  | 
 | ||||||
|  |     bool read_header(string_t filetype, uint32_t* version); | ||||||
|  | 
 | ||||||
|  |     bool write_header(string_t filetype, const uint32_t version); | ||||||
|  | 
 | ||||||
|  |     bool write_header_cstr(const char* filetype, const uint32_t version); | ||||||
|  | 
 | ||||||
|  |     bool read_string(const char* key, string_t data); | ||||||
|  | 
 | ||||||
|  |     bool write_string(const char* key, string_t data); | ||||||
|  | 
 | ||||||
|  |     bool write_string_cstr(const char* key, const char* data); | ||||||
|  | 
 | ||||||
|  |     bool read_uint32(const char* key, uint32_t* data); | ||||||
|  | 
 | ||||||
|  |     bool write_uint32(const char* key, const uint32_t data); | ||||||
|  | 
 | ||||||
|  |     bool write_comment(string_t data); | ||||||
|  | 
 | ||||||
|  |     bool write_comment_cstr(const char* data); | ||||||
|  | 
 | ||||||
|  |     bool write_hex_array(const char* key, const uint8_t* data, const uint16_t data_size); | ||||||
|  | 
 | ||||||
|  |     bool read_hex_array(const char* key, uint8_t* data, const uint16_t data_size); | ||||||
|  | }; | ||||||
							
								
								
									
										464
									
								
								lib/toolbox/flipper-file.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										464
									
								
								lib/toolbox/flipper-file.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,464 @@ | |||||||
|  | #include <furi.h> | ||||||
|  | #include "flipper-file.h" | ||||||
|  | #include <toolbox/hex.h> | ||||||
|  | #include <inttypes.h> | ||||||
|  | 
 | ||||||
|  | struct FlipperFile { | ||||||
|  |     File* file; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | const char* flipper_file_filetype_key = "Filetype"; | ||||||
|  | const char* flipper_file_version_key = "Version"; | ||||||
|  | const char flipper_file_eoln = '\n'; | ||||||
|  | const char flipper_file_eolr = '\r'; | ||||||
|  | const char flipper_file_delimiter = ':'; | ||||||
|  | const char flipper_file_comment = '#'; | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Writes data to a file as a hexadecimal array. | ||||||
|  |  * @param file  | ||||||
|  |  * @param data  | ||||||
|  |  * @param data_size  | ||||||
|  |  * @return true on success write  | ||||||
|  |  */ | ||||||
|  | bool flipper_file_write_hex_internal(File* file, const uint8_t* data, const uint16_t data_size) { | ||||||
|  |     const uint8_t byte_text_size = 3; | ||||||
|  |     char byte_text[byte_text_size]; | ||||||
|  | 
 | ||||||
|  |     bool result = true; | ||||||
|  |     uint16_t bytes_written; | ||||||
|  |     for(uint8_t i = 0; i < data_size; i++) { | ||||||
|  |         snprintf(byte_text, byte_text_size, "%02X", data[i]); | ||||||
|  | 
 | ||||||
|  |         if(i != 0) { | ||||||
|  |             // space
 | ||||||
|  |             const char space = ' '; | ||||||
|  |             bytes_written = storage_file_write(file, &space, sizeof(space)); | ||||||
|  |             if(bytes_written != sizeof(space)) { | ||||||
|  |                 result = false; | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         bytes_written = storage_file_write(file, &byte_text, strlen(byte_text)); | ||||||
|  |         if(bytes_written != strlen(byte_text)) { | ||||||
|  |             result = false; | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return result; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Reads a valid key from a file as a string. | ||||||
|  |  * After reading, the rw pointer will be on the flipper_file_delimiter symbol. | ||||||
|  |  * Optimized not to read comments and values into RAM. | ||||||
|  |  * @param file  | ||||||
|  |  * @param key  | ||||||
|  |  * @return true on success read  | ||||||
|  |  */ | ||||||
|  | bool flipper_file_read_valid_key(File* file, string_t key) { | ||||||
|  |     string_clean(key); | ||||||
|  |     bool found = false; | ||||||
|  |     bool error = false; | ||||||
|  |     const uint8_t buffer_size = 32; | ||||||
|  |     uint8_t buffer[buffer_size]; | ||||||
|  |     bool accumulate = true; | ||||||
|  |     bool new_line = true; | ||||||
|  | 
 | ||||||
|  |     while(true) { | ||||||
|  |         uint16_t bytes_were_read = storage_file_read(file, buffer, buffer_size); | ||||||
|  |         if(bytes_were_read == 0) break; | ||||||
|  | 
 | ||||||
|  |         for(uint16_t i = 0; i < bytes_were_read; i++) { | ||||||
|  |             if(buffer[i] == flipper_file_eoln) { | ||||||
|  |                 // EOL found, clean data, start accumulating data and set the new_line flag
 | ||||||
|  |                 string_clean(key); | ||||||
|  |                 accumulate = true; | ||||||
|  |                 new_line = true; | ||||||
|  |             } else if(buffer[i] == flipper_file_eolr) { | ||||||
|  |                 // Ignore
 | ||||||
|  |             } else if(buffer[i] == flipper_file_comment && new_line) { | ||||||
|  |                 // if there is a comment character and we are at the beginning of a new line
 | ||||||
|  |                 // do not accumulate comment data and reset the new_line flag
 | ||||||
|  |                 accumulate = false; | ||||||
|  |                 new_line = false; | ||||||
|  |             } else if(buffer[i] == flipper_file_delimiter) { | ||||||
|  |                 if(new_line) { | ||||||
|  |                     // we are on a "new line" and found the delimiter
 | ||||||
|  |                     // this can only be if we have previously found some kind of key, so
 | ||||||
|  |                     // clear the data, set the flag that we no longer want to accumulate data
 | ||||||
|  |                     // and reset the new_line flag
 | ||||||
|  |                     string_clean(key); | ||||||
|  |                     accumulate = false; | ||||||
|  |                     new_line = false; | ||||||
|  |                 } else { | ||||||
|  |                     // parse the delimiter only if we are accumulating data
 | ||||||
|  |                     if(accumulate) { | ||||||
|  |                         // we found the delimiter, move the rw pointer to the correct location
 | ||||||
|  |                         // and signal that we have found something
 | ||||||
|  |                         // TODO negative seek
 | ||||||
|  |                         uint64_t position = storage_file_tell(file); | ||||||
|  |                         position = position - bytes_were_read + i; | ||||||
|  |                         if(!storage_file_seek(file, position, true)) { | ||||||
|  |                             error = true; | ||||||
|  |                             break; | ||||||
|  |                         } | ||||||
|  | 
 | ||||||
|  |                         found = true; | ||||||
|  |                         break; | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             } else { | ||||||
|  |                 // just new symbol, reset the new_line flag
 | ||||||
|  |                 new_line = false; | ||||||
|  |                 if(accumulate) { | ||||||
|  |                     // and accumulate data if we want
 | ||||||
|  |                     string_push_back(key, buffer[i]); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if(found || error) break; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return found; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Sets rw pointer to the data after the key | ||||||
|  |  * @param file  | ||||||
|  |  * @param key  | ||||||
|  |  * @return true if key was found  | ||||||
|  |  */ | ||||||
|  | bool flipper_file_seek_to_key(File* file, const char* key) { | ||||||
|  |     bool found = false; | ||||||
|  |     string_t readed_key; | ||||||
|  | 
 | ||||||
|  |     string_init(readed_key); | ||||||
|  | 
 | ||||||
|  |     // TODO optimize this to search from a stored rw pointer
 | ||||||
|  |     if(storage_file_seek(file, 0, true)) { | ||||||
|  |         while(!storage_file_eof(file)) { | ||||||
|  |             if(flipper_file_read_valid_key(file, readed_key)) { | ||||||
|  |                 if(string_cmp_str(readed_key, key) == 0) { | ||||||
|  |                     uint64_t position = storage_file_tell(file); | ||||||
|  |                     if(!storage_file_seek(file, position + 2, true)) break; | ||||||
|  | 
 | ||||||
|  |                     found = true; | ||||||
|  |                     break; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     string_clear(readed_key); | ||||||
|  | 
 | ||||||
|  |     return found; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Reads data as a string from the stored rw pointer to the \r or \n symbol position | ||||||
|  |  * @param file  | ||||||
|  |  * @param str_result  | ||||||
|  |  * @return true on success read | ||||||
|  |  */ | ||||||
|  | bool flipper_file_read_until(File* file, string_t str_result) { | ||||||
|  |     string_clean(str_result); | ||||||
|  |     const uint8_t buffer_size = 32; | ||||||
|  |     uint8_t buffer[buffer_size]; | ||||||
|  | 
 | ||||||
|  |     do { | ||||||
|  |         uint16_t bytes_were_read = storage_file_read(file, buffer, buffer_size); | ||||||
|  |         if(bytes_were_read == 0) break; | ||||||
|  | 
 | ||||||
|  |         bool result = false; | ||||||
|  |         bool error = false; | ||||||
|  |         for(uint16_t i = 0; i < bytes_were_read; i++) { | ||||||
|  |             if(buffer[i] == flipper_file_eoln) { | ||||||
|  |                 // TODO negative seek
 | ||||||
|  |                 uint64_t position = storage_file_tell(file); | ||||||
|  |                 position = position - bytes_were_read + i; | ||||||
|  |                 if(!storage_file_seek(file, position, true)) { | ||||||
|  |                     error = true; | ||||||
|  |                     break; | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 result = true; | ||||||
|  |                 break; | ||||||
|  |             } else if(buffer[i] == flipper_file_eolr) { | ||||||
|  |                 // Ignore
 | ||||||
|  |             } else { | ||||||
|  |                 string_push_back(str_result, buffer[i]); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if(result || error) { | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |     } while(true); | ||||||
|  | 
 | ||||||
|  |     return string_size(str_result) != 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Reads single hexadecimal data from a file to byte | ||||||
|  |  * @param file  | ||||||
|  |  * @param byte  | ||||||
|  |  * @return bool  | ||||||
|  |  */ | ||||||
|  | bool flipper_file_read_hex_byte(File* file, uint8_t* byte) { | ||||||
|  |     uint8_t hi_nibble_value, low_nibble_value; | ||||||
|  |     uint8_t text[3]; | ||||||
|  |     bool result = false; | ||||||
|  | 
 | ||||||
|  |     uint16_t bytes_were_read = storage_file_read(file, text, 3); | ||||||
|  |     if(bytes_were_read >= 2) { | ||||||
|  |         if(text[0] != ' ') { | ||||||
|  |             if(hex_char_to_hex_nibble(text[0], &hi_nibble_value) && | ||||||
|  |                hex_char_to_hex_nibble(text[1], &low_nibble_value)) { | ||||||
|  |                 *byte = (hi_nibble_value << 4) | low_nibble_value; | ||||||
|  |                 result = true; | ||||||
|  |             } | ||||||
|  |         } else { | ||||||
|  |             if(hex_char_to_hex_nibble(text[1], &hi_nibble_value) && | ||||||
|  |                hex_char_to_hex_nibble(text[2], &low_nibble_value)) { | ||||||
|  |                 *byte = (hi_nibble_value << 4) | low_nibble_value; | ||||||
|  |                 result = true; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return result; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | FlipperFile* flipper_file_alloc(Storage* storage) { | ||||||
|  |     FlipperFile* flipper_file = malloc(sizeof(FlipperFile)); | ||||||
|  |     flipper_file->file = storage_file_alloc(storage); | ||||||
|  |     return flipper_file; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void flipper_file_free(FlipperFile* flipper_file) { | ||||||
|  |     furi_assert(flipper_file); | ||||||
|  |     if(storage_file_is_open(flipper_file->file)) { | ||||||
|  |         storage_file_close(flipper_file->file); | ||||||
|  |     } | ||||||
|  |     storage_file_free(flipper_file->file); | ||||||
|  |     free(flipper_file); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool flipper_file_open_read(FlipperFile* flipper_file, const char* filename) { | ||||||
|  |     furi_assert(flipper_file); | ||||||
|  |     bool result = storage_file_open(flipper_file->file, filename, FSAM_READ, FSOM_OPEN_EXISTING); | ||||||
|  |     return result; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool flipper_file_new_write(FlipperFile* flipper_file, const char* filename) { | ||||||
|  |     furi_assert(flipper_file); | ||||||
|  |     bool result = storage_file_open(flipper_file->file, filename, FSAM_WRITE, FSOM_CREATE_ALWAYS); | ||||||
|  |     return result; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool flipper_file_close(FlipperFile* flipper_file) { | ||||||
|  |     furi_assert(flipper_file); | ||||||
|  |     if(storage_file_is_open(flipper_file->file)) { | ||||||
|  |         return storage_file_close(flipper_file->file); | ||||||
|  |     } | ||||||
|  |     return true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool flipper_file_read_header(FlipperFile* flipper_file, string_t filetype, uint32_t* version) { | ||||||
|  |     bool result = false; | ||||||
|  |     do { | ||||||
|  |         result = flipper_file_read_string(flipper_file, flipper_file_filetype_key, filetype); | ||||||
|  |         if(!result) break; | ||||||
|  |         result = flipper_file_read_uint32(flipper_file, flipper_file_version_key, version); | ||||||
|  |         if(!result) break; | ||||||
|  |     } while(false); | ||||||
|  | 
 | ||||||
|  |     return result; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool flipper_file_write_header( | ||||||
|  |     FlipperFile* flipper_file, | ||||||
|  |     string_t filetype, | ||||||
|  |     const uint32_t version) { | ||||||
|  |     bool result = false; | ||||||
|  |     do { | ||||||
|  |         result = flipper_file_write_string(flipper_file, flipper_file_filetype_key, filetype); | ||||||
|  |         if(!result) break; | ||||||
|  |         result = flipper_file_write_uint32(flipper_file, flipper_file_version_key, version); | ||||||
|  |         if(!result) break; | ||||||
|  |     } while(false); | ||||||
|  | 
 | ||||||
|  |     return result; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool flipper_file_write_header_cstr( | ||||||
|  |     FlipperFile* flipper_file, | ||||||
|  |     const char* filetype, | ||||||
|  |     const uint32_t version) { | ||||||
|  |     bool result = false; | ||||||
|  |     string_t value; | ||||||
|  |     string_init_set(value, filetype); | ||||||
|  |     result = flipper_file_write_header(flipper_file, value, version); | ||||||
|  |     string_clear(value); | ||||||
|  |     return result; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool flipper_file_read_string(FlipperFile* flipper_file, const char* key, string_t data) { | ||||||
|  |     furi_assert(flipper_file); | ||||||
|  | 
 | ||||||
|  |     bool result = false; | ||||||
|  |     if(flipper_file_seek_to_key(flipper_file->file, key)) { | ||||||
|  |         if(flipper_file_read_until(flipper_file->file, data)) { | ||||||
|  |             result = true; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     return result; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool flipper_file_write_string(FlipperFile* flipper_file, const char* key, string_t data) { | ||||||
|  |     furi_assert(flipper_file); | ||||||
|  | 
 | ||||||
|  |     bool result = false; | ||||||
|  |     do { | ||||||
|  |         uint16_t bytes_written; | ||||||
|  |         bytes_written = storage_file_write(flipper_file->file, key, strlen(key)); | ||||||
|  |         if(bytes_written != strlen(key)) break; | ||||||
|  | 
 | ||||||
|  |         const char delimiter_buffer[2] = {flipper_file_delimiter, ' '}; | ||||||
|  |         bytes_written = | ||||||
|  |             storage_file_write(flipper_file->file, delimiter_buffer, sizeof(delimiter_buffer)); | ||||||
|  |         if(bytes_written != sizeof(delimiter_buffer)) break; | ||||||
|  | 
 | ||||||
|  |         bytes_written = | ||||||
|  |             storage_file_write(flipper_file->file, string_get_cstr(data), string_size(data)); | ||||||
|  |         if(bytes_written != string_size(data)) break; | ||||||
|  | 
 | ||||||
|  |         bytes_written = | ||||||
|  |             storage_file_write(flipper_file->file, &flipper_file_eoln, sizeof(flipper_file_eoln)); | ||||||
|  |         if(bytes_written != sizeof(flipper_file_eoln)) break; | ||||||
|  | 
 | ||||||
|  |         result = true; | ||||||
|  |     } while(false); | ||||||
|  | 
 | ||||||
|  |     return result; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool flipper_file_write_string_cstr(FlipperFile* flipper_file, const char* key, const char* data) { | ||||||
|  |     bool result = false; | ||||||
|  |     string_t value; | ||||||
|  |     string_init_set(value, data); | ||||||
|  |     result = flipper_file_write_string(flipper_file, key, value); | ||||||
|  |     string_clear(value); | ||||||
|  |     return result; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool flipper_file_read_uint32(FlipperFile* flipper_file, const char* key, uint32_t* data) { | ||||||
|  |     bool result = false; | ||||||
|  |     string_t value; | ||||||
|  |     string_init(value); | ||||||
|  | 
 | ||||||
|  |     result = flipper_file_read_string(flipper_file, key, value); | ||||||
|  |     if(result) { | ||||||
|  |         int ret = sscanf(string_get_cstr(value), "%" PRIu32, data); | ||||||
|  |         if(ret != 1) result = false; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     string_clear(value); | ||||||
|  |     return result; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool flipper_file_write_uint32(FlipperFile* flipper_file, const char* key, const uint32_t data) { | ||||||
|  |     bool result = false; | ||||||
|  |     string_t value; | ||||||
|  |     string_init_printf(value, "%" PRIu32, data); | ||||||
|  |     result = flipper_file_write_string(flipper_file, key, value); | ||||||
|  |     string_clear(value); | ||||||
|  |     return result; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool flipper_file_write_comment(FlipperFile* flipper_file, string_t data) { | ||||||
|  |     furi_assert(flipper_file); | ||||||
|  | 
 | ||||||
|  |     bool result = false; | ||||||
|  |     do { | ||||||
|  |         uint16_t bytes_written; | ||||||
|  |         const char comment_buffer[2] = {flipper_file_comment, ' '}; | ||||||
|  |         bytes_written = | ||||||
|  |             storage_file_write(flipper_file->file, comment_buffer, sizeof(comment_buffer)); | ||||||
|  |         if(bytes_written != sizeof(comment_buffer)) break; | ||||||
|  | 
 | ||||||
|  |         bytes_written = | ||||||
|  |             storage_file_write(flipper_file->file, string_get_cstr(data), string_size(data)); | ||||||
|  |         if(bytes_written != string_size(data)) break; | ||||||
|  | 
 | ||||||
|  |         bytes_written = | ||||||
|  |             storage_file_write(flipper_file->file, &flipper_file_eoln, sizeof(flipper_file_eoln)); | ||||||
|  |         if(bytes_written != sizeof(flipper_file_eoln)) break; | ||||||
|  | 
 | ||||||
|  |         result = true; | ||||||
|  |     } while(false); | ||||||
|  | 
 | ||||||
|  |     return result; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool flipper_file_write_comment_cstr(FlipperFile* flipper_file, const char* data) { | ||||||
|  |     bool result = false; | ||||||
|  |     string_t value; | ||||||
|  |     string_init_set(value, data); | ||||||
|  |     result = flipper_file_write_comment(flipper_file, value); | ||||||
|  |     string_clear(value); | ||||||
|  |     return result; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool flipper_file_write_hex_array( | ||||||
|  |     FlipperFile* flipper_file, | ||||||
|  |     const char* key, | ||||||
|  |     const uint8_t* data, | ||||||
|  |     const uint16_t data_size) { | ||||||
|  |     furi_assert(flipper_file); | ||||||
|  | 
 | ||||||
|  |     bool result = false; | ||||||
|  |     do { | ||||||
|  |         uint16_t bytes_written; | ||||||
|  |         bytes_written = storage_file_write(flipper_file->file, key, strlen(key)); | ||||||
|  |         if(bytes_written != strlen(key)) break; | ||||||
|  | 
 | ||||||
|  |         const char delimiter_buffer[2] = {flipper_file_delimiter, ' '}; | ||||||
|  |         bytes_written = | ||||||
|  |             storage_file_write(flipper_file->file, delimiter_buffer, sizeof(delimiter_buffer)); | ||||||
|  |         if(bytes_written != sizeof(delimiter_buffer)) break; | ||||||
|  | 
 | ||||||
|  |         if(!flipper_file_write_hex_internal(flipper_file->file, data, data_size)) break; | ||||||
|  | 
 | ||||||
|  |         bytes_written = | ||||||
|  |             storage_file_write(flipper_file->file, &flipper_file_eoln, sizeof(flipper_file_eoln)); | ||||||
|  |         if(bytes_written != sizeof(flipper_file_eoln)) break; | ||||||
|  | 
 | ||||||
|  |         result = true; | ||||||
|  |     } while(false); | ||||||
|  | 
 | ||||||
|  |     return result; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool flipper_file_read_hex_array( | ||||||
|  |     FlipperFile* flipper_file, | ||||||
|  |     const char* key, | ||||||
|  |     uint8_t* data, | ||||||
|  |     const uint16_t data_size) { | ||||||
|  |     furi_assert(flipper_file); | ||||||
|  | 
 | ||||||
|  |     bool result = false; | ||||||
|  |     if(flipper_file_seek_to_key(flipper_file->file, key)) { | ||||||
|  |         result = true; | ||||||
|  |         for(uint16_t i = 0; i < data_size; i++) { | ||||||
|  |             if(!flipper_file_read_hex_byte(flipper_file->file, &data[i])) { | ||||||
|  |                 result = false; | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     return result; | ||||||
|  | } | ||||||
							
								
								
									
										267
									
								
								lib/toolbox/flipper-file.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										267
									
								
								lib/toolbox/flipper-file.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,267 @@ | |||||||
|  | /**
 | ||||||
|  |  * @file flipper-file.h | ||||||
|  |  * Flipper File Format helper library. | ||||||
|  |  *  | ||||||
|  |  * Flipper File Format is a fairly simple format for storing data in a file. | ||||||
|  |  *  | ||||||
|  |  * Flipper file structure: | ||||||
|  |  *  | ||||||
|  |  * ~~~~~~~~~~~~~~~~~~~~~ | ||||||
|  |  * # Commentary | ||||||
|  |  * Field name: field value | ||||||
|  |  * ~~~~~~~~~~~~~~~~~~~~~ | ||||||
|  |  *  | ||||||
|  |  * Lines starting with the # character are ignored (considered as comments). The separator between the name of the value and the value itself is the string ": ". | ||||||
|  |  * | ||||||
|  |  * Currently supported types: | ||||||
|  |  *  | ||||||
|  |  * ~~~~~~~~~~~~~~~~~~~~~ | ||||||
|  |  * String: text | ||||||
|  |  * Uint32: 1 | ||||||
|  |  * Hex Array: A4 B3 C2 D1 12 FF | ||||||
|  |  * ~~~~~~~~~~~~~~~~~~~~~ | ||||||
|  |  *  | ||||||
|  |  * End of line is LF when writing, but CR is supported when reading. | ||||||
|  |  *  | ||||||
|  |  * The library is designed in such a way that comments and field values are completely ignored when searching for keys, that is, they do not consume memory. | ||||||
|  |  *  | ||||||
|  |  * File example:  | ||||||
|  |  *  | ||||||
|  |  * ~~~~~~~~~~~~~~~~~~~~~ | ||||||
|  |  * Filetype: Flipper Test File | ||||||
|  |  * Version: 1 | ||||||
|  |  * # Just test file | ||||||
|  |  * String: String value | ||||||
|  |  * UINT: 1234 | ||||||
|  |  * Hex Array: 00 01 FF A3 | ||||||
|  |  * ~~~~~~~~~~~~~~~~~~~~~ | ||||||
|  |  *  | ||||||
|  |  * Writing: | ||||||
|  |  *  | ||||||
|  |  * ~~~~~~~~~~~~~~~~~~~~~ | ||||||
|  |  * FlipperFile file = flipper_file_alloc(storage); | ||||||
|  |  *  | ||||||
|  |  * do { | ||||||
|  |  *     const uint32_t version = 1; | ||||||
|  |  *     const char* string_value = "String value"; | ||||||
|  |  *     const uint32_t uint32_value = 1234; | ||||||
|  |  *     const uint16_t array_size = 4; | ||||||
|  |  *     const uint8_t* array[array_size] = {0x00, 0x01, 0xFF, 0xA3}; | ||||||
|  |  *      | ||||||
|  |  *     if(!flipper_file_new_write(file, "/ext/flipper_file_test")) break; | ||||||
|  |  *     if(!flipper_file_write_header_cstr(file, "Flipper Test File", version)) break; | ||||||
|  |  *     if(!flipper_file_write_comment_cstr(file, "Just test file")) break; | ||||||
|  |  *     if(!flipper_file_write_string_cstr(file, "String", string_value)) break; | ||||||
|  |  *     if(!flipper_file_flipper_file_write_uint32(file, "UINT", uint32_value)) break; | ||||||
|  |  *     if(!flipper_file_write_hex_array(file, "Hex Array", array, array_size)) break; | ||||||
|  |  *      | ||||||
|  |  *     // signal that the file was written successfully
 | ||||||
|  |  * } while(0); | ||||||
|  |  *  | ||||||
|  |  * flipper_file_close(file); | ||||||
|  |  * flipper_file_free(file); | ||||||
|  |  * ~~~~~~~~~~~~~~~~~~~~~ | ||||||
|  |  *  | ||||||
|  |  * Reading: | ||||||
|  |  *  | ||||||
|  |  * ~~~~~~~~~~~~~~~~~~~~~ | ||||||
|  |  * FlipperFile file = flipper_file_alloc(storage); | ||||||
|  |  *  | ||||||
|  |  * do { | ||||||
|  |  *     uint32_t version = 1; | ||||||
|  |  *     string_t file_type; | ||||||
|  |  *     string_t string_value; | ||||||
|  |  *     uint32_t uint32_value = 1; | ||||||
|  |  *     uint16_t array_size = 4; | ||||||
|  |  *     uint8_t* array[array_size] = {0}; | ||||||
|  |  *     string_init(file_type); | ||||||
|  |  *     string_init(string_value); | ||||||
|  |  *      | ||||||
|  |  *     if(!flipper_file_open_read(file, "/ext/flipper_file_test")) break; | ||||||
|  |  *     if(!flipper_file_read_header(file, file_type, &version)) break; | ||||||
|  |  *     if(!flipper_file_read_string(file, "String", string_value)) break; | ||||||
|  |  *     if(!flipper_file_read_uint32(file, "UINT", &uint32_value)) break; | ||||||
|  |  *     if(!flipper_file_read_hex_array(file, "Hex Array", array, array_size)) break; | ||||||
|  |  *      | ||||||
|  |  *     // signal that the file was read successfully
 | ||||||
|  |  * } while(0); | ||||||
|  |  *  | ||||||
|  |  * flipper_file_close(file); | ||||||
|  |  * flipper_file_free(file); | ||||||
|  |  * ~~~~~~~~~~~~~~~~~~~~~ | ||||||
|  |  *  | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #pragma once | ||||||
|  | #include <stdint.h> | ||||||
|  | #include <mlib/m-string.h> | ||||||
|  | #include <storage/storage.h> | ||||||
|  | 
 | ||||||
|  | #ifdef __cplusplus | ||||||
|  | extern "C" { | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | /** FlipperFile type anonymous structure. */ | ||||||
|  | typedef struct FlipperFile FlipperFile; | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Allocate FlipperFile. | ||||||
|  |  * @param storage storage api | ||||||
|  |  * @return FlipperFile* Pointer to a FlipperFile instance | ||||||
|  |  */ | ||||||
|  | FlipperFile* flipper_file_alloc(Storage* storage); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Free FlipperFile. | ||||||
|  |  * @param flipper_file Pointer to a FlipperFile instance | ||||||
|  |  */ | ||||||
|  | void flipper_file_free(FlipperFile* flipper_file); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Open file for reading. | ||||||
|  |  * @param flipper_file Pointer to a FlipperFile instance | ||||||
|  |  * @param filename File name and path | ||||||
|  |  * @return True on success | ||||||
|  |  */ | ||||||
|  | bool flipper_file_open_read(FlipperFile* flipper_file, const char* filename); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Open file for writing. Creates a new file, or deletes the contents of the file if it already exists. | ||||||
|  |  * @param flipper_file Pointer to a FlipperFile instance | ||||||
|  |  * @param filename File name and path | ||||||
|  |  * @return True on success | ||||||
|  |  */ | ||||||
|  | bool flipper_file_new_write(FlipperFile* flipper_file, const char* filename); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Close the file. | ||||||
|  |  * @param flipper_file Pointer to a FlipperFile instance | ||||||
|  |  * @return True on success | ||||||
|  |  */ | ||||||
|  | bool flipper_file_close(FlipperFile* flipper_file); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Read the header (file type and version) from the file. | ||||||
|  |  * @param flipper_file Pointer to a FlipperFile instance | ||||||
|  |  * @param filetype File type string | ||||||
|  |  * @param version Version Value | ||||||
|  |  * @return True on success | ||||||
|  |  */ | ||||||
|  | bool flipper_file_read_header(FlipperFile* flipper_file, string_t filetype, uint32_t* version); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Write the header (file type and version) to the file. | ||||||
|  |  * @param flipper_file Pointer to a FlipperFile instance | ||||||
|  |  * @param filetype File type string | ||||||
|  |  * @param version Version Value | ||||||
|  |  * @return True on success | ||||||
|  |  */ | ||||||
|  | bool flipper_file_write_header( | ||||||
|  |     FlipperFile* flipper_file, | ||||||
|  |     string_t filetype, | ||||||
|  |     const uint32_t version); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Write the header (file type and version) to the file. Plain C string version. | ||||||
|  |  * @param flipper_file Pointer to a FlipperFile instance | ||||||
|  |  * @param filetype File type string | ||||||
|  |  * @param version Version Value | ||||||
|  |  * @return True on success | ||||||
|  |  */ | ||||||
|  | bool flipper_file_write_header_cstr( | ||||||
|  |     FlipperFile* flipper_file, | ||||||
|  |     const char* filetype, | ||||||
|  |     const uint32_t version); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Read a string from a file by Key | ||||||
|  |  * @param flipper_file Pointer to a FlipperFile instance | ||||||
|  |  * @param key Key | ||||||
|  |  * @param data Value | ||||||
|  |  * @return True on success | ||||||
|  |  */ | ||||||
|  | bool flipper_file_read_string(FlipperFile* flipper_file, const char* key, string_t data); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Write key and string to file. | ||||||
|  |  * @param flipper_file Pointer to a FlipperFile instance | ||||||
|  |  * @param key Key | ||||||
|  |  * @param data Value | ||||||
|  |  * @return True on success | ||||||
|  |  */ | ||||||
|  | bool flipper_file_write_string(FlipperFile* flipper_file, const char* key, string_t data); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Write key and string to file. Plain C string version. | ||||||
|  |  * @param flipper_file Pointer to a FlipperFile instance | ||||||
|  |  * @param key Key | ||||||
|  |  * @param data Value | ||||||
|  |  * @return True on success | ||||||
|  |  */ | ||||||
|  | bool flipper_file_write_string_cstr(FlipperFile* flipper_file, const char* key, const char* data); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Read uint32 from a file by Key | ||||||
|  |  * @param flipper_file Pointer to a FlipperFile instance | ||||||
|  |  * @param key Key | ||||||
|  |  * @param data Value | ||||||
|  |  * @return True on success | ||||||
|  |  */ | ||||||
|  | bool flipper_file_read_uint32(FlipperFile* flipper_file, const char* key, uint32_t* data); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Write key and uint32 to file. | ||||||
|  |  * @param flipper_file Pointer to a FlipperFile instance | ||||||
|  |  * @param key Key | ||||||
|  |  * @param data Value | ||||||
|  |  * @return True on success | ||||||
|  |  */ | ||||||
|  | bool flipper_file_write_uint32(FlipperFile* flipper_file, const char* key, const uint32_t data); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Write comment to file. | ||||||
|  |  * @param flipper_file Pointer to a FlipperFile instance | ||||||
|  |  * @param data Comment text | ||||||
|  |  * @return True on success | ||||||
|  |  */ | ||||||
|  | bool flipper_file_write_comment(FlipperFile* flipper_file, string_t data); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Write comment to file. Plain C string version. | ||||||
|  |  * @param flipper_file Pointer to a FlipperFile instance | ||||||
|  |  * @param data Comment text | ||||||
|  |  * @return True on success | ||||||
|  |  */ | ||||||
|  | bool flipper_file_write_comment_cstr(FlipperFile* flipper_file, const char* data); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Read hex array from a file by Key | ||||||
|  |  * @param flipper_file Pointer to a FlipperFile instance | ||||||
|  |  * @param key Key | ||||||
|  |  * @param data Value | ||||||
|  |  * @param data_size Value size | ||||||
|  |  * @return True on success | ||||||
|  |  */ | ||||||
|  | bool flipper_file_read_hex_array( | ||||||
|  |     FlipperFile* flipper_file, | ||||||
|  |     const char* key, | ||||||
|  |     uint8_t* data, | ||||||
|  |     const uint16_t data_size); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Write key and hex array to file. | ||||||
|  |  * @param flipper_file Pointer to a FlipperFile instance | ||||||
|  |  * @param key Key | ||||||
|  |  * @param data Value | ||||||
|  |  * @param data_size Value size | ||||||
|  |  * @return True on success | ||||||
|  |  */ | ||||||
|  | bool flipper_file_write_hex_array( | ||||||
|  |     FlipperFile* flipper_file, | ||||||
|  |     const char* key, | ||||||
|  |     const uint8_t* data, | ||||||
|  |     const uint16_t data_size); | ||||||
|  | 
 | ||||||
|  | #ifdef __cplusplus | ||||||
|  | } | ||||||
|  | #endif | ||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 SG
						SG