[FL-1371] New LF-RFID app. Second encounter. (#547)
* File worker: file operations helper. * Notification app: removed yield * File worker: write operations, calls to system file widgets * App ibutton: use file worker * Lfrfid: generic key loading, add path helper and hex conversion to lib * FileWorker: plain C verison * FileWorker: add to lib.mk * FileWorker: add to C sources, instead of CPP * Lfrfid: save scene * App lfrfid: add key scene, saved key menu * App lfrfid: saved key info scene * App lfrfid: delete key scene
This commit is contained in:
		
							parent
							
								
									e8211226f3
								
							
						
					
					
						commit
						7a13391b2b
					
				
							
								
								
									
										10
									
								
								applications/ibutton/ibutton-app.cpp
									
									
									
									
									
										
										
										Executable file → Normal file
									
								
							
							
						
						
									
										10
									
								
								applications/ibutton/ibutton-app.cpp
									
									
									
									
									
										
										
										Executable file → Normal file
									
								
							| @ -2,7 +2,7 @@ | ||||
| #include <stdarg.h> | ||||
| #include <callback-connector.h> | ||||
| #include <m-string.h> | ||||
| #include <file-worker.h> | ||||
| #include <file-worker-cpp.h> | ||||
| 
 | ||||
| const char* iButtonApp::app_folder = "ibutton"; | ||||
| const char* iButtonApp::app_extension = ".ibtn"; | ||||
| @ -217,7 +217,7 @@ void iButtonApp::generate_random_name(char* name, uint8_t max_name_size) { | ||||
| 
 | ||||
| // file managment
 | ||||
| bool iButtonApp::save_key(const char* key_name) { | ||||
|     FileWorker file_worker; | ||||
|     FileWorkerCpp file_worker; | ||||
|     string_t key_file_name; | ||||
|     bool result = false; | ||||
| 
 | ||||
| @ -274,7 +274,7 @@ bool iButtonApp::save_key(const char* key_name) { | ||||
| } | ||||
| 
 | ||||
| bool iButtonApp::load_key_data(string_t key_path) { | ||||
|     FileWorker file_worker; | ||||
|     FileWorkerCpp file_worker; | ||||
| 
 | ||||
|     // Open key file
 | ||||
|     if(!file_worker.open(string_get_cstr(key_path), FSAM_READ, FSOM_OPEN_EXISTING)) { | ||||
| @ -344,7 +344,7 @@ bool iButtonApp::load_key(const char* key_name) { | ||||
| 
 | ||||
| bool iButtonApp::load_key() { | ||||
|     bool result = false; | ||||
|     FileWorker file_worker; | ||||
|     FileWorkerCpp file_worker; | ||||
| 
 | ||||
|     // Input events and views are managed by file_select
 | ||||
|     bool res = file_worker.file_select( | ||||
| @ -369,7 +369,7 @@ bool iButtonApp::load_key() { | ||||
| bool iButtonApp::delete_key() { | ||||
|     string_t file_name; | ||||
|     bool result = false; | ||||
|     FileWorker file_worker; | ||||
|     FileWorkerCpp file_worker; | ||||
| 
 | ||||
|     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)); | ||||
|  | ||||
| @ -1,4 +1,5 @@ | ||||
| #include "key-info.h" | ||||
| #include <string.h> | ||||
| 
 | ||||
| const char* lfrfid_key_get_type_string(LfrfidKeyType type) { | ||||
|     switch(type) { | ||||
| @ -16,6 +17,22 @@ const char* lfrfid_key_get_type_string(LfrfidKeyType type) { | ||||
|     return "Unknown"; | ||||
| } | ||||
| 
 | ||||
| bool lfrfid_key_get_string_type(const char* string, LfrfidKeyType* type) { | ||||
|     bool result = true; | ||||
| 
 | ||||
|     if(strcmp("EM4100", string) == 0) { | ||||
|         *type = LfrfidKeyType::KeyEM4100; | ||||
|     } else if(strcmp("H10301", string) == 0) { | ||||
|         *type = LfrfidKeyType::KeyH10301; | ||||
|     } else if(strcmp("I40134", string) == 0) { | ||||
|         *type = LfrfidKeyType::KeyI40134; | ||||
|     } else { | ||||
|         result = false; | ||||
|     } | ||||
| 
 | ||||
|     return result; | ||||
| } | ||||
| 
 | ||||
| uint8_t lfrfid_key_get_type_data_count(LfrfidKeyType type) { | ||||
|     switch(type) { | ||||
|     case LfrfidKeyType::KeyEM4100: | ||||
|  | ||||
| @ -11,4 +11,5 @@ enum class LfrfidKeyType : uint8_t { | ||||
| }; | ||||
| 
 | ||||
| const char* lfrfid_key_get_type_string(LfrfidKeyType type); | ||||
| bool lfrfid_key_get_string_type(const char* string, LfrfidKeyType* type); | ||||
| uint8_t lfrfid_key_get_type_data_count(LfrfidKeyType type); | ||||
| @ -1,12 +1,9 @@ | ||||
| #include "rfid-key.h" | ||||
| #include <furi/check.h> | ||||
| #include <string.h> | ||||
| 
 | ||||
| RfidKey::RfidKey() { | ||||
|     data.fill(0); | ||||
| 
 | ||||
|     for(uint8_t i = 0; i < (LFRFID_KEY_NAME_SIZE + 1); i++) { | ||||
|         name[i] = 0; | ||||
|     } | ||||
|     clear(); | ||||
| } | ||||
| 
 | ||||
| RfidKey::~RfidKey() { | ||||
| @ -16,18 +13,22 @@ void RfidKey::set_type(LfrfidKeyType _type) { | ||||
|     type = _type; | ||||
| } | ||||
| 
 | ||||
| void RfidKey::set_data(uint8_t* _data, const uint8_t _data_size) { | ||||
| void RfidKey::set_data(const uint8_t* _data, const uint8_t _data_size) { | ||||
|     furi_assert(_data_size <= data.size()); | ||||
|     for(uint8_t i = 0; i < _data_size; i++) { | ||||
|         data[i] = _data[i]; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void RfidKey::set_name(const char* _name) { | ||||
|     strlcpy(name, _name, get_name_length()); | ||||
| } | ||||
| 
 | ||||
| LfrfidKeyType RfidKey::get_type() { | ||||
|     return type; | ||||
| } | ||||
| 
 | ||||
| uint8_t* RfidKey::get_data() { | ||||
| const uint8_t* RfidKey::get_data() { | ||||
|     return &data[0]; | ||||
| } | ||||
| 
 | ||||
| @ -42,3 +43,23 @@ const uint8_t RfidKey::get_type_data_count() { | ||||
| char* RfidKey::get_name() { | ||||
|     return name; | ||||
| } | ||||
| 
 | ||||
| uint8_t RfidKey::get_name_length() { | ||||
|     return LFRFID_KEY_NAME_SIZE; | ||||
| } | ||||
| 
 | ||||
| void RfidKey::clear() { | ||||
|     set_name(""); | ||||
|     set_type(LfrfidKeyType::KeyEM4100); | ||||
|     data.fill(0); | ||||
| } | ||||
| 
 | ||||
| RfidKey& RfidKey::operator=(const RfidKey& rhs) { | ||||
|     if(this == &rhs) return *this; | ||||
| 
 | ||||
|     set_type(rhs.type); | ||||
|     set_name(rhs.name); | ||||
|     set_data(&rhs.data[0], get_type_data_count()); | ||||
| 
 | ||||
|     return *this; | ||||
| } | ||||
|  | ||||
| @ -8,15 +8,17 @@ public: | ||||
|     ~RfidKey(); | ||||
| 
 | ||||
|     void set_type(LfrfidKeyType type); | ||||
|     void set_data(uint8_t* data, const uint8_t data_size); | ||||
|     void set_data(const uint8_t* data, const uint8_t data_size); | ||||
|     void set_name(const char* name); | ||||
| 
 | ||||
|     LfrfidKeyType get_type(); | ||||
|     uint8_t* get_data(); | ||||
| 
 | ||||
|     const uint8_t* get_data(); | ||||
|     const char* get_type_text(); | ||||
|     const uint8_t get_type_data_count(); | ||||
| 
 | ||||
|     char* get_name(); | ||||
|     uint8_t get_name_length(); | ||||
|     void clear(); | ||||
|     RfidKey& operator=(const RfidKey& rhs); | ||||
| 
 | ||||
| private: | ||||
|     std::array<uint8_t, LFRFID_KEY_SIZE> data; | ||||
|  | ||||
| @ -111,7 +111,7 @@ void RfidWriter::write_reset() { | ||||
|     write_bit(0); | ||||
| } | ||||
| 
 | ||||
| void RfidWriter::write_em(uint8_t em_data[5]) { | ||||
| void RfidWriter::write_em(const uint8_t em_data[5]) { | ||||
|     ProtocolEMMarin em_card; | ||||
|     uint64_t em_encoded_data; | ||||
|     em_card.encode(em_data, 5, reinterpret_cast<uint8_t*>(&em_encoded_data), sizeof(uint64_t)); | ||||
| @ -125,7 +125,7 @@ void RfidWriter::write_em(uint8_t em_data[5]) { | ||||
|     __enable_irq(); | ||||
| } | ||||
| 
 | ||||
| void RfidWriter::write_hid(uint8_t hid_data[3]) { | ||||
| void RfidWriter::write_hid(const uint8_t hid_data[3]) { | ||||
|     ProtocolHID10301 hid_card; | ||||
|     uint32_t card_data[3]; | ||||
|     hid_card.encode(hid_data, 3, reinterpret_cast<uint8_t*>(&card_data), sizeof(card_data) * 3); | ||||
|  | ||||
| @ -7,8 +7,8 @@ public: | ||||
|     ~RfidWriter(); | ||||
|     void start(); | ||||
|     void stop(); | ||||
|     void write_em(uint8_t em_data[5]); | ||||
|     void write_hid(uint8_t hid_data[3]); | ||||
|     void write_em(const uint8_t em_data[5]); | ||||
|     void write_hid(const uint8_t hid_data[3]); | ||||
| 
 | ||||
| private: | ||||
|     void write_gap(uint32_t gap_time); | ||||
|  | ||||
| @ -1,9 +1,9 @@ | ||||
| #include "lfrfid-app.h" | ||||
| 
 | ||||
| // app enter function
 | ||||
| extern "C" int32_t lfrfid_app(void* p) { | ||||
| extern "C" int32_t lfrfid_app(void* args) { | ||||
|     LfRfidApp* app = new LfRfidApp(); | ||||
|     app->run(); | ||||
|     app->run(args); | ||||
|     delete app; | ||||
| 
 | ||||
|     return 0; | ||||
|  | ||||
| @ -7,6 +7,20 @@ | ||||
| #include "scene/lfrfid-app-scene-write-success.h" | ||||
| #include "scene/lfrfid-app-scene-emulate.h" | ||||
| #include "scene/lfrfid-app-scene-save-name.h" | ||||
| #include "scene/lfrfid-app-scene-save-success.h" | ||||
| #include "scene/lfrfid-app-scene-select-key.h" | ||||
| #include "scene/lfrfid-app-scene-saved-key-menu.h" | ||||
| #include "scene/lfrfid-app-scene-save-data.h" | ||||
| #include "scene/lfrfid-app-scene-save-type.h" | ||||
| #include "scene/lfrfid-app-scene-saved-info.h" | ||||
| #include "scene/lfrfid-app-scene-delete-confirm.h" | ||||
| #include "scene/lfrfid-app-scene-delete-success.h" | ||||
| 
 | ||||
| #include <file-worker-cpp.h> | ||||
| #include <path.h> | ||||
| 
 | ||||
| const char* LfRfidApp::app_folder = "lfrfid"; | ||||
| const char* LfRfidApp::app_extension = ".rfid"; | ||||
| 
 | ||||
| LfRfidApp::LfRfidApp() | ||||
|     : scene_controller{this} | ||||
| @ -24,7 +38,14 @@ LfRfidApp::~LfRfidApp() { | ||||
|     api_hal_power_insomnia_exit(); | ||||
| } | ||||
| 
 | ||||
| void LfRfidApp::run() { | ||||
| void LfRfidApp::run(void* _args) { | ||||
|     const char* args = reinterpret_cast<const char*>(_args); | ||||
| 
 | ||||
|     if(strlen(args)) { | ||||
|         load_key_data(args, &worker.key); | ||||
|         scene_controller.add_scene(SceneType::Emulate, new LfRfidAppSceneEmulate()); | ||||
|         scene_controller.process(100, SceneType::Emulate); | ||||
|     } else { | ||||
|         scene_controller.add_scene(SceneType::Start, new LfRfidAppSceneStart()); | ||||
|         scene_controller.add_scene(SceneType::Read, new LfRfidAppSceneRead()); | ||||
|         scene_controller.add_scene(SceneType::ReadSuccess, new LfRfidAppSceneReadSuccess()); | ||||
| @ -33,5 +54,140 @@ void LfRfidApp::run() { | ||||
|         scene_controller.add_scene(SceneType::WriteSuccess, new LfRfidAppSceneWriteSuccess()); | ||||
|         scene_controller.add_scene(SceneType::Emulate, new LfRfidAppSceneEmulate()); | ||||
|         scene_controller.add_scene(SceneType::SaveName, new LfRfidAppSceneSaveName()); | ||||
|         scene_controller.add_scene(SceneType::SaveSuccess, new LfRfidAppSceneSaveSuccess()); | ||||
|         scene_controller.add_scene(SceneType::SelectKey, new LfRfidAppSceneSelectKey()); | ||||
|         scene_controller.add_scene(SceneType::SavedKeyMenu, new LfRfidAppSceneSavedKeyMenu()); | ||||
|         scene_controller.add_scene(SceneType::SaveData, new LfRfidAppSceneSaveData()); | ||||
|         scene_controller.add_scene(SceneType::SaveType, new LfRfidAppSceneSaveType()); | ||||
|         scene_controller.add_scene(SceneType::SavedInfo, new LfRfidAppSceneSavedInfo()); | ||||
|         scene_controller.add_scene(SceneType::DeleteConfirm, new LfRfidAppSceneDeleteConfirm()); | ||||
|         scene_controller.add_scene(SceneType::DeleteSuccess, new LfRfidAppSceneDeleteSuccess()); | ||||
|         scene_controller.process(100); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| bool LfRfidApp::save_key(RfidKey* key) { | ||||
|     string_t file_name; | ||||
|     bool result = false; | ||||
| 
 | ||||
|     make_app_folder(); | ||||
| 
 | ||||
|     string_init_printf(file_name, "%s/%s%s", app_folder, key->get_name(), app_extension); | ||||
|     result = save_key_data(string_get_cstr(file_name), key); | ||||
|     string_clear(file_name); | ||||
| 
 | ||||
|     return result; | ||||
| } | ||||
| 
 | ||||
| bool LfRfidApp::load_key_from_file_select(bool need_restore) { | ||||
|     FileWorkerCpp file_worker; | ||||
|     TextStore* filename_ts = new TextStore(64); | ||||
|     bool result; | ||||
| 
 | ||||
|     if(need_restore) { | ||||
|         result = file_worker.file_select( | ||||
|             app_folder, | ||||
|             app_extension, | ||||
|             filename_ts->text, | ||||
|             filename_ts->text_size, | ||||
|             worker.key.get_name()); | ||||
|     } else { | ||||
|         result = file_worker.file_select( | ||||
|             app_folder, app_extension, filename_ts->text, filename_ts->text_size, NULL); | ||||
|     } | ||||
| 
 | ||||
|     if(result) { | ||||
|         string_t key_str; | ||||
|         string_init_printf(key_str, "%s/%s%s", app_folder, filename_ts->text, app_extension); | ||||
|         result = load_key_data(string_get_cstr(key_str), &worker.key); | ||||
|         string_clear(key_str); | ||||
|     } | ||||
| 
 | ||||
|     delete filename_ts; | ||||
|     return result; | ||||
| } | ||||
| 
 | ||||
| bool LfRfidApp::delete_key(RfidKey* key) { | ||||
|     FileWorkerCpp file_worker; | ||||
|     string_t file_name; | ||||
|     bool result = false; | ||||
| 
 | ||||
|     string_init_printf(file_name, "%s/%s%s", app_folder, key->get_name(), app_extension); | ||||
|     result = file_worker.remove(string_get_cstr(file_name)); | ||||
|     string_clear(file_name); | ||||
| 
 | ||||
|     return result; | ||||
| } | ||||
| 
 | ||||
| bool LfRfidApp::load_key_data(const char* path, RfidKey* key) { | ||||
|     FileWorkerCpp file_worker; | ||||
|     bool result = false; | ||||
| 
 | ||||
|     bool res = file_worker.open(path, FSAM_READ, FSOM_OPEN_EXISTING); | ||||
| 
 | ||||
|     if(res) { | ||||
|         string_t str_result; | ||||
|         string_init(str_result); | ||||
| 
 | ||||
|         do { | ||||
|             RfidKey loaded_key; | ||||
|             LfrfidKeyType loaded_type; | ||||
| 
 | ||||
|             // load type
 | ||||
|             if(!file_worker.read_until(str_result, ' ')) break; | ||||
|             if(!lfrfid_key_get_string_type(string_get_cstr(str_result), &loaded_type)) { | ||||
|                 file_worker.show_error("Cannot parse\nfile"); | ||||
|                 break; | ||||
|             } | ||||
|             loaded_key.set_type(loaded_type); | ||||
| 
 | ||||
|             // load data
 | ||||
|             uint8_t tmp_data[loaded_key.get_type_data_count()]; | ||||
|             if(!file_worker.read_hex(tmp_data, loaded_key.get_type_data_count())) break; | ||||
|             loaded_key.set_data(tmp_data, loaded_key.get_type_data_count()); | ||||
| 
 | ||||
|             *key = loaded_key; | ||||
|             result = true; | ||||
|         } while(0); | ||||
| 
 | ||||
|         // load name
 | ||||
|         path_extract_filename_no_ext(path, str_result); | ||||
|         key->set_name(string_get_cstr(str_result)); | ||||
| 
 | ||||
|         string_clear(str_result); | ||||
|     } | ||||
| 
 | ||||
|     file_worker.close(); | ||||
| 
 | ||||
|     return result; | ||||
| } | ||||
| 
 | ||||
| bool LfRfidApp::save_key_data(const char* path, RfidKey* key) { | ||||
|     FileWorkerCpp file_worker; | ||||
|     bool result = false; | ||||
| 
 | ||||
|     bool res = file_worker.open(path, FSAM_WRITE, FSOM_CREATE_ALWAYS); | ||||
| 
 | ||||
|     if(res) { | ||||
|         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(!file_worker.write(&delimeter)) break; | ||||
|             if(!file_worker.write_hex(key->get_data(), key->get_type_data_count())) break; | ||||
| 
 | ||||
|             result = true; | ||||
|         } while(0); | ||||
|     } | ||||
| 
 | ||||
|     file_worker.close(); | ||||
| 
 | ||||
|     return result; | ||||
| } | ||||
| 
 | ||||
| void LfRfidApp::make_app_folder() { | ||||
|     FileWorkerCpp file_worker; | ||||
|     file_worker.mkdir(app_folder); | ||||
| } | ||||
| @ -38,6 +38,14 @@ public: | ||||
|         WriteSuccess, | ||||
|         Emulate, | ||||
|         SaveName, | ||||
|         SaveSuccess, | ||||
|         SelectKey, | ||||
|         SavedKeyMenu, | ||||
|         SaveData, | ||||
|         SaveType, | ||||
|         SavedInfo, | ||||
|         DeleteConfirm, | ||||
|         DeleteSuccess, | ||||
|     }; | ||||
| 
 | ||||
|     class Event { | ||||
| @ -63,5 +71,18 @@ public: | ||||
|     RfidWorker worker; | ||||
| 
 | ||||
|     TextStore text_store; | ||||
|     void run(); | ||||
| 
 | ||||
|     void run(void* args); | ||||
| 
 | ||||
|     static const char* app_folder; | ||||
|     static const char* app_extension; | ||||
| 
 | ||||
|     bool save_key(RfidKey* key); | ||||
|     bool load_key_from_file_select(bool need_restore); | ||||
|     bool delete_key(RfidKey* key); | ||||
| 
 | ||||
|     bool load_key_data(const char* path, RfidKey* key); | ||||
|     bool save_key_data(const char* path, RfidKey* key); | ||||
| 
 | ||||
|     void make_app_folder(); | ||||
| }; | ||||
| @ -0,0 +1,99 @@ | ||||
| #include "lfrfid-app-scene-delete-confirm.h" | ||||
| #include "../view/elements/button-element.h" | ||||
| #include "../view/elements/icon-element.h" | ||||
| #include "../view/elements/string-element.h" | ||||
| 
 | ||||
| void LfRfidAppSceneDeleteConfirm::on_enter(LfRfidApp* app, bool need_restore) { | ||||
|     string_init(string_data); | ||||
|     string_init(string_decrypted); | ||||
|     string_init(string_header); | ||||
| 
 | ||||
|     auto container = app->view_controller.get<ContainerVM>(); | ||||
| 
 | ||||
|     auto button = container->add<ButtonElement>(); | ||||
|     button->set_type(ButtonElement::Type::Left, "Back"); | ||||
|     button->set_callback(app, LfRfidAppSceneDeleteConfirm::back_callback); | ||||
| 
 | ||||
|     button = container->add<ButtonElement>(); | ||||
|     button->set_type(ButtonElement::Type::Right, "Delete"); | ||||
|     button->set_callback(app, LfRfidAppSceneDeleteConfirm::delete_callback); | ||||
| 
 | ||||
|     auto line_1 = container->add<StringElement>(); | ||||
|     auto line_2 = container->add<StringElement>(); | ||||
|     auto line_3 = container->add<StringElement>(); | ||||
|     auto line_4 = container->add<StringElement>(); | ||||
| 
 | ||||
|     RfidKey& key = app->worker.key; | ||||
|     const uint8_t* data = key.get_data(); | ||||
| 
 | ||||
|     for(uint8_t i = 0; i < key.get_type_data_count(); i++) { | ||||
|         if(i != 0) { | ||||
|             string_cat_printf(string_data, " "); | ||||
|         } | ||||
|         string_cat_printf(string_data, "%02X", data[i]); | ||||
|     } | ||||
| 
 | ||||
|     string_printf(string_header, "Delete %s?", key.get_name()); | ||||
|     line_1->set_text( | ||||
|         string_get_cstr(string_header), 64, 19, AlignCenter, AlignBottom, FontPrimary); | ||||
|     line_2->set_text( | ||||
|         string_get_cstr(string_data), 64, 29, AlignCenter, AlignBottom, FontSecondary); | ||||
| 
 | ||||
|     switch(key.get_type()) { | ||||
|     case LfrfidKeyType::KeyEM4100: | ||||
|         string_printf( | ||||
|             string_decrypted, "%03u,%05u", data[2], (uint16_t)((data[3] << 8) | (data[4]))); | ||||
| 
 | ||||
|         break; | ||||
|     case LfrfidKeyType::KeyH10301: | ||||
|     case LfrfidKeyType::KeyI40134: | ||||
|         string_printf( | ||||
|             string_decrypted, "FC: %u    ID: %u", data[0], (uint16_t)((data[1] << 8) | (data[2]))); | ||||
|         break; | ||||
|     } | ||||
|     line_3->set_text( | ||||
|         string_get_cstr(string_decrypted), 64, 39, AlignCenter, AlignBottom, FontSecondary); | ||||
| 
 | ||||
|     line_4->set_text( | ||||
|         lfrfid_key_get_type_string(key.get_type()), | ||||
|         64, | ||||
|         49, | ||||
|         AlignCenter, | ||||
|         AlignBottom, | ||||
|         FontSecondary); | ||||
| 
 | ||||
|     app->view_controller.switch_to<ContainerVM>(); | ||||
| } | ||||
| 
 | ||||
| bool LfRfidAppSceneDeleteConfirm::on_event(LfRfidApp* app, LfRfidApp::Event* event) { | ||||
|     bool consumed = false; | ||||
| 
 | ||||
|     if(event->type == LfRfidApp::EventType::Next) { | ||||
|         app->delete_key(&app->worker.key); | ||||
|         app->scene_controller.switch_to_next_scene(LfRfidApp::SceneType::DeleteSuccess); | ||||
|         consumed = true; | ||||
|     } | ||||
| 
 | ||||
|     return consumed; | ||||
| } | ||||
| 
 | ||||
| void LfRfidAppSceneDeleteConfirm::on_exit(LfRfidApp* app) { | ||||
|     app->view_controller.get<ContainerVM>()->clean(); | ||||
|     string_clear(string_data); | ||||
|     string_clear(string_decrypted); | ||||
|     string_clear(string_header); | ||||
| } | ||||
| 
 | ||||
| void LfRfidAppSceneDeleteConfirm::back_callback(void* context) { | ||||
|     LfRfidApp* app = static_cast<LfRfidApp*>(context); | ||||
|     LfRfidApp::Event event; | ||||
|     event.type = LfRfidApp::EventType::Back; | ||||
|     app->view_controller.send_event(&event); | ||||
| } | ||||
| 
 | ||||
| void LfRfidAppSceneDeleteConfirm::delete_callback(void* context) { | ||||
|     LfRfidApp* app = static_cast<LfRfidApp*>(context); | ||||
|     LfRfidApp::Event event; | ||||
|     event.type = LfRfidApp::EventType::Next; | ||||
|     app->view_controller.send_event(&event); | ||||
| } | ||||
							
								
								
									
										17
									
								
								applications/lfrfid/scene/lfrfid-app-scene-delete-confirm.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								applications/lfrfid/scene/lfrfid-app-scene-delete-confirm.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,17 @@ | ||||
| #pragma once | ||||
| #include "../lfrfid-app.h" | ||||
| 
 | ||||
| class LfRfidAppSceneDeleteConfirm : public GenericScene<LfRfidApp> { | ||||
| public: | ||||
|     void on_enter(LfRfidApp* app, bool need_restore) final; | ||||
|     bool on_event(LfRfidApp* app, LfRfidApp::Event* event) final; | ||||
|     void on_exit(LfRfidApp* app) final; | ||||
| 
 | ||||
| private: | ||||
|     static void back_callback(void* context); | ||||
|     static void delete_callback(void* context); | ||||
| 
 | ||||
|     string_t string_header; | ||||
|     string_t string_data; | ||||
|     string_t string_decrypted; | ||||
| }; | ||||
| @ -0,0 +1,38 @@ | ||||
| #include "lfrfid-app-scene-delete-success.h" | ||||
| 
 | ||||
| void LfRfidAppSceneDeleteSuccess::on_enter(LfRfidApp* app, bool need_restore) { | ||||
|     auto popup = app->view_controller.get<PopupVM>(); | ||||
| 
 | ||||
|     popup->set_icon(0, 2, I_DolphinMafia_115x62); | ||||
|     popup->set_text("Deleted", 83, 19, AlignLeft, AlignBottom); | ||||
|     popup->set_context(app); | ||||
|     popup->set_callback(LfRfidAppSceneDeleteSuccess::timeout_callback); | ||||
|     popup->set_timeout(1500); | ||||
|     popup->enable_timeout(); | ||||
| 
 | ||||
|     app->view_controller.switch_to<PopupVM>(); | ||||
| } | ||||
| 
 | ||||
| bool LfRfidAppSceneDeleteSuccess::on_event(LfRfidApp* app, LfRfidApp::Event* event) { | ||||
|     bool consumed = false; | ||||
| 
 | ||||
|     if(event->type == LfRfidApp::EventType::Back) { | ||||
|         app->scene_controller.search_and_switch_to_previous_scene( | ||||
|             {LfRfidApp::SceneType::SelectKey}); | ||||
| 
 | ||||
|         consumed = true; | ||||
|     } | ||||
| 
 | ||||
|     return consumed; | ||||
| } | ||||
| 
 | ||||
| void LfRfidAppSceneDeleteSuccess::on_exit(LfRfidApp* app) { | ||||
|     app->view_controller.get<PopupVM>()->clean(); | ||||
| } | ||||
| 
 | ||||
| void LfRfidAppSceneDeleteSuccess::timeout_callback(void* context) { | ||||
|     LfRfidApp* app = static_cast<LfRfidApp*>(context); | ||||
|     LfRfidApp::Event event; | ||||
|     event.type = LfRfidApp::EventType::Back; | ||||
|     app->view_controller.send_event(&event); | ||||
| } | ||||
							
								
								
									
										12
									
								
								applications/lfrfid/scene/lfrfid-app-scene-delete-success.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								applications/lfrfid/scene/lfrfid-app-scene-delete-success.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,12 @@ | ||||
| #pragma once | ||||
| #include "../lfrfid-app.h" | ||||
| 
 | ||||
| class LfRfidAppSceneDeleteSuccess : public GenericScene<LfRfidApp> { | ||||
| public: | ||||
|     void on_enter(LfRfidApp* app, bool need_restore) final; | ||||
|     bool on_event(LfRfidApp* app, LfRfidApp::Event* event) final; | ||||
|     void on_exit(LfRfidApp* app) final; | ||||
| 
 | ||||
| private: | ||||
|     static void timeout_callback(void* context); | ||||
| }; | ||||
| @ -3,7 +3,7 @@ | ||||
| void LfRfidAppSceneEmulate::on_enter(LfRfidApp* app, bool need_restore) { | ||||
|     string_init(data_string); | ||||
| 
 | ||||
|     uint8_t* data = app->worker.key.get_data(); | ||||
|     const uint8_t* data = app->worker.key.get_data(); | ||||
| 
 | ||||
|     for(uint8_t i = 0; i < app->worker.key.get_type_data_count(); i++) { | ||||
|         string_cat_printf(data_string, "%02X", data[i]); | ||||
| @ -11,8 +11,12 @@ void LfRfidAppSceneEmulate::on_enter(LfRfidApp* app, bool need_restore) { | ||||
| 
 | ||||
|     auto popup = app->view_controller.get<PopupVM>(); | ||||
| 
 | ||||
|     popup->set_header("Emulating", 90, 34, AlignCenter, AlignTop); | ||||
|     popup->set_text(string_get_cstr(data_string), 90, 48, AlignCenter, AlignTop); | ||||
|     popup->set_header("LF emulating", 89, 30, AlignCenter, AlignTop); | ||||
|     if(strlen(app->worker.key.get_name())) { | ||||
|         popup->set_text(app->worker.key.get_name(), 89, 43, AlignCenter, AlignTop); | ||||
|     } else { | ||||
|         popup->set_text(string_get_cstr(data_string), 89, 43, AlignCenter, AlignTop); | ||||
|     } | ||||
|     popup->set_icon(0, 4, I_RFIDDolphinSend_98x60); | ||||
| 
 | ||||
|     app->view_controller.switch_to<PopupVM>(); | ||||
|  | ||||
| @ -32,7 +32,7 @@ void LfRfidAppSceneReadSuccess::on_enter(LfRfidApp* app, bool need_restore) { | ||||
|     auto line_2_value = container->add<StringElement>(); | ||||
|     auto line_3_value = container->add<StringElement>(); | ||||
| 
 | ||||
|     uint8_t* data = app->worker.key.get_data(); | ||||
|     const uint8_t* data = app->worker.key.get_data(); | ||||
| 
 | ||||
|     switch(app->worker.key.get_type()) { | ||||
|     case LfrfidKeyType::KeyEM4100: | ||||
|  | ||||
							
								
								
									
										58
									
								
								applications/lfrfid/scene/lfrfid-app-scene-save-data.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										58
									
								
								applications/lfrfid/scene/lfrfid-app-scene-save-data.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,58 @@ | ||||
| #include "lfrfid-app-scene-save-data.h" | ||||
| 
 | ||||
| static void print_buffer(const uint8_t* buffer) { | ||||
|     for(uint8_t i = 0; i < LFRFID_KEY_SIZE; i++) { | ||||
|         printf("%02X", buffer[i]); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void LfRfidAppSceneSaveData::on_enter(LfRfidApp* app, bool need_restore) { | ||||
|     auto byte_input = app->view_controller.get<ByteInputVM>(); | ||||
|     RfidKey& key = app->worker.key; | ||||
| 
 | ||||
|     printf("k: "); | ||||
|     print_buffer(key.get_data()); | ||||
|     printf(" o: "); | ||||
|     print_buffer(old_key_data); | ||||
|     printf(" n: "); | ||||
|     print_buffer(new_key_data); | ||||
|     printf("\r\n"); | ||||
|     if(need_restore) printf("restored\r\n"); | ||||
| 
 | ||||
|     if(need_restore) { | ||||
|         key.set_data(old_key_data, key.get_type_data_count()); | ||||
|     } else { | ||||
|         memcpy(old_key_data, key.get_data(), key.get_type_data_count()); | ||||
|     } | ||||
| 
 | ||||
|     memcpy(new_key_data, key.get_data(), key.get_type_data_count()); | ||||
|     byte_input->set_header_text("Enter the data in hex"); | ||||
| 
 | ||||
|     byte_input->set_result_callback( | ||||
|         save_callback, NULL, app, new_key_data, app->worker.key.get_type_data_count()); | ||||
| 
 | ||||
|     app->view_controller.switch_to<ByteInputVM>(); | ||||
| } | ||||
| 
 | ||||
| bool LfRfidAppSceneSaveData::on_event(LfRfidApp* app, LfRfidApp::Event* event) { | ||||
|     bool consumed = false; | ||||
|     RfidKey& key = app->worker.key; | ||||
| 
 | ||||
|     if(event->type == LfRfidApp::EventType::Next) { | ||||
|         key.set_data(new_key_data, key.get_type_data_count()); | ||||
|         app->scene_controller.switch_to_next_scene(LfRfidApp::SceneType::SaveName); | ||||
|     } | ||||
| 
 | ||||
|     return consumed; | ||||
| } | ||||
| 
 | ||||
| void LfRfidAppSceneSaveData::on_exit(LfRfidApp* app) { | ||||
|     app->view_controller.get<ByteInputVM>()->clean(); | ||||
| } | ||||
| 
 | ||||
| void LfRfidAppSceneSaveData::save_callback(void* context, uint8_t* bytes, uint8_t bytes_count) { | ||||
|     LfRfidApp* app = static_cast<LfRfidApp*>(context); | ||||
|     LfRfidApp::Event event; | ||||
|     event.type = LfRfidApp::EventType::Next; | ||||
|     app->view_controller.send_event(&event); | ||||
| } | ||||
							
								
								
									
										33
									
								
								applications/lfrfid/scene/lfrfid-app-scene-save-data.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								applications/lfrfid/scene/lfrfid-app-scene-save-data.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,33 @@ | ||||
| #pragma once | ||||
| #include "../lfrfid-app.h" | ||||
| 
 | ||||
| class LfRfidAppSceneSaveData : public GenericScene<LfRfidApp> { | ||||
| public: | ||||
|     void on_enter(LfRfidApp* app, bool need_restore) final; | ||||
|     bool on_event(LfRfidApp* app, LfRfidApp::Event* event) final; | ||||
|     void on_exit(LfRfidApp* app) final; | ||||
| 
 | ||||
| private: | ||||
|     static void save_callback(void* context, uint8_t* bytes, uint8_t bytes_count); | ||||
|     uint8_t old_key_data[LFRFID_KEY_SIZE] = { | ||||
|         0xAA, | ||||
|         0xAA, | ||||
|         0xAA, | ||||
|         0xAA, | ||||
|         0xAA, | ||||
|         0xAA, | ||||
|         0xAA, | ||||
|         0xAA, | ||||
|     }; | ||||
| 
 | ||||
|     uint8_t new_key_data[LFRFID_KEY_SIZE] = { | ||||
|         0xBB, | ||||
|         0xBB, | ||||
|         0xBB, | ||||
|         0xBB, | ||||
|         0xBB, | ||||
|         0xBB, | ||||
|         0xBB, | ||||
|         0xBB, | ||||
|     }; | ||||
| }; | ||||
| @ -14,7 +14,7 @@ void LfRfidAppSceneSaveName::on_enter(LfRfidApp* app, bool need_restore) { | ||||
|     text_input->set_header_text("Name the card"); | ||||
| 
 | ||||
|     text_input->set_result_callback( | ||||
|         save_callback, app, app->text_store.text, LFRFID_KEY_NAME_SIZE); | ||||
|         save_callback, app, app->text_store.text, app->worker.key.get_name_length()); | ||||
| 
 | ||||
|     app->view_controller.switch_to<TextInputVM>(); | ||||
| } | ||||
| @ -23,14 +23,18 @@ bool LfRfidAppSceneSaveName::on_event(LfRfidApp* app, LfRfidApp::Event* event) { | ||||
|     bool consumed = false; | ||||
| 
 | ||||
|     if(event->type == LfRfidApp::EventType::Next) { | ||||
|         /*if(app->save_key(app->get_text_store())) {
 | ||||
|             app->switch_to_next_scene(iButtonApp::Scene::SceneSaveSuccess); | ||||
|         if(strlen(app->worker.key.get_name())) { | ||||
|             app->delete_key(&app->worker.key); | ||||
|         } | ||||
| 
 | ||||
|         app->worker.key.set_name(app->text_store.text); | ||||
| 
 | ||||
|         if(app->save_key(&app->worker.key)) { | ||||
|             app->scene_controller.switch_to_next_scene(LfRfidApp::SceneType::SaveSuccess); | ||||
|         } else { | ||||
|             app->search_and_switch_to_previous_scene( | ||||
|                 {iButtonApp::Scene::SceneReadedKeyMenu, | ||||
|                  iButtonApp::Scene::SceneSavedKeyMenu, | ||||
|                  iButtonApp::Scene::SceneAddType}); | ||||
|         }*/ | ||||
|             app->scene_controller.search_and_switch_to_previous_scene( | ||||
|                 {LfRfidApp::SceneType::ReadedMenu}); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     return consumed; | ||||
|  | ||||
							
								
								
									
										46
									
								
								applications/lfrfid/scene/lfrfid-app-scene-save-success.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								applications/lfrfid/scene/lfrfid-app-scene-save-success.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,46 @@ | ||||
| #include "lfrfid-app-scene-save-success.h" | ||||
| 
 | ||||
| void LfRfidAppSceneSaveSuccess::on_enter(LfRfidApp* app, bool need_restore) { | ||||
|     auto popup = app->view_controller.get<PopupVM>(); | ||||
| 
 | ||||
|     popup->set_icon(32, 5, I_DolphinNice_96x59); | ||||
|     popup->set_text("Saved!", 13, 22, AlignLeft, AlignBottom); | ||||
|     popup->set_context(app); | ||||
|     popup->set_callback(LfRfidAppSceneSaveSuccess::timeout_callback); | ||||
|     popup->set_timeout(1500); | ||||
|     popup->enable_timeout(); | ||||
| 
 | ||||
|     app->view_controller.switch_to<PopupVM>(); | ||||
| } | ||||
| 
 | ||||
| bool LfRfidAppSceneSaveSuccess::on_event(LfRfidApp* app, LfRfidApp::Event* event) { | ||||
|     bool consumed = false; | ||||
| 
 | ||||
|     if(event->type == LfRfidApp::EventType::Back) { | ||||
|         bool result = app->scene_controller.has_previous_scene( | ||||
|             {LfRfidApp::SceneType::ReadedMenu, LfRfidApp::SceneType::SelectKey}); | ||||
| 
 | ||||
|         if(result) { | ||||
|             app->scene_controller.search_and_switch_to_previous_scene( | ||||
|                 {LfRfidApp::SceneType::ReadedMenu, LfRfidApp::SceneType::SelectKey}); | ||||
|         } else { | ||||
|             app->scene_controller.search_and_switch_to_another_scene( | ||||
|                 {LfRfidApp::SceneType::SaveType}, LfRfidApp::SceneType::SelectKey); | ||||
|         } | ||||
| 
 | ||||
|         consumed = true; | ||||
|     } | ||||
| 
 | ||||
|     return consumed; | ||||
| } | ||||
| 
 | ||||
| void LfRfidAppSceneSaveSuccess::on_exit(LfRfidApp* app) { | ||||
|     app->view_controller.get<PopupVM>()->clean(); | ||||
| } | ||||
| 
 | ||||
| void LfRfidAppSceneSaveSuccess::timeout_callback(void* context) { | ||||
|     LfRfidApp* app = static_cast<LfRfidApp*>(context); | ||||
|     LfRfidApp::Event event; | ||||
|     event.type = LfRfidApp::EventType::Back; | ||||
|     app->view_controller.send_event(&event); | ||||
| } | ||||
							
								
								
									
										12
									
								
								applications/lfrfid/scene/lfrfid-app-scene-save-success.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								applications/lfrfid/scene/lfrfid-app-scene-save-success.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,12 @@ | ||||
| #pragma once | ||||
| #include "../lfrfid-app.h" | ||||
| 
 | ||||
| class LfRfidAppSceneSaveSuccess : public GenericScene<LfRfidApp> { | ||||
| public: | ||||
|     void on_enter(LfRfidApp* app, bool need_restore) final; | ||||
|     bool on_event(LfRfidApp* app, LfRfidApp::Event* event) final; | ||||
|     void on_exit(LfRfidApp* app) final; | ||||
| 
 | ||||
| private: | ||||
|     static void timeout_callback(void* context); | ||||
| }; | ||||
							
								
								
									
										46
									
								
								applications/lfrfid/scene/lfrfid-app-scene-save-type.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								applications/lfrfid/scene/lfrfid-app-scene-save-type.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,46 @@ | ||||
| #include "lfrfid-app-scene-save-type.h" | ||||
| 
 | ||||
| void LfRfidAppSceneSaveType::on_enter(LfRfidApp* app, bool need_restore) { | ||||
|     auto submenu = app->view_controller.get<SubmenuVM>(); | ||||
| 
 | ||||
|     for(uint8_t i = 0; i <= static_cast<uint8_t>(LfrfidKeyType::KeyI40134); i++) { | ||||
|         submenu->add_item( | ||||
|             lfrfid_key_get_type_string(static_cast<LfrfidKeyType>(i)), i, submenu_callback, app); | ||||
|     } | ||||
| 
 | ||||
|     if(need_restore) { | ||||
|         submenu->set_selected_item(submenu_item_selected); | ||||
|     } | ||||
| 
 | ||||
|     app->view_controller.switch_to<SubmenuVM>(); | ||||
| 
 | ||||
|     // clear key name
 | ||||
|     app->worker.key.set_name(""); | ||||
| } | ||||
| 
 | ||||
| bool LfRfidAppSceneSaveType::on_event(LfRfidApp* app, LfRfidApp::Event* event) { | ||||
|     bool consumed = false; | ||||
| 
 | ||||
|     if(event->type == LfRfidApp::EventType::MenuSelected) { | ||||
|         submenu_item_selected = event->payload.menu_index; | ||||
|         app->worker.key.set_type(static_cast<LfrfidKeyType>(event->payload.menu_index)); | ||||
|         app->scene_controller.switch_to_next_scene(LfRfidApp::SceneType::SaveData); | ||||
|         consumed = true; | ||||
|     } | ||||
| 
 | ||||
|     return consumed; | ||||
| } | ||||
| 
 | ||||
| void LfRfidAppSceneSaveType::on_exit(LfRfidApp* app) { | ||||
|     app->view_controller.get<SubmenuVM>()->clean(); | ||||
| } | ||||
| 
 | ||||
| void LfRfidAppSceneSaveType::submenu_callback(void* context, uint32_t index) { | ||||
|     LfRfidApp* app = static_cast<LfRfidApp*>(context); | ||||
|     LfRfidApp::Event event; | ||||
| 
 | ||||
|     event.type = LfRfidApp::EventType::MenuSelected; | ||||
|     event.payload.menu_index = index; | ||||
| 
 | ||||
|     app->view_controller.send_event(&event); | ||||
| } | ||||
							
								
								
									
										13
									
								
								applications/lfrfid/scene/lfrfid-app-scene-save-type.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								applications/lfrfid/scene/lfrfid-app-scene-save-type.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,13 @@ | ||||
| #pragma once | ||||
| #include "../lfrfid-app.h" | ||||
| 
 | ||||
| class LfRfidAppSceneSaveType : public GenericScene<LfRfidApp> { | ||||
| public: | ||||
|     void on_enter(LfRfidApp* app, bool need_restore) final; | ||||
|     bool on_event(LfRfidApp* app, LfRfidApp::Event* event) final; | ||||
|     void on_exit(LfRfidApp* app) final; | ||||
| 
 | ||||
| private: | ||||
|     static void submenu_callback(void* context, uint32_t index); | ||||
|     uint32_t submenu_item_selected = 0; | ||||
| }; | ||||
							
								
								
									
										77
									
								
								applications/lfrfid/scene/lfrfid-app-scene-saved-info.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										77
									
								
								applications/lfrfid/scene/lfrfid-app-scene-saved-info.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,77 @@ | ||||
| #include "lfrfid-app-scene-saved-info.h" | ||||
| #include "../view/elements/button-element.h" | ||||
| #include "../view/elements/icon-element.h" | ||||
| #include "../view/elements/string-element.h" | ||||
| 
 | ||||
| void LfRfidAppSceneSavedInfo::on_enter(LfRfidApp* app, bool need_restore) { | ||||
|     string_init(string_data); | ||||
|     string_init(string_decrypted); | ||||
| 
 | ||||
|     auto container = app->view_controller.get<ContainerVM>(); | ||||
| 
 | ||||
|     auto button = container->add<ButtonElement>(); | ||||
|     button->set_type(ButtonElement::Type::Left, "Back"); | ||||
|     button->set_callback(app, LfRfidAppSceneSavedInfo::back_callback); | ||||
| 
 | ||||
|     auto line_1 = container->add<StringElement>(); | ||||
|     auto line_2 = container->add<StringElement>(); | ||||
|     auto line_3 = container->add<StringElement>(); | ||||
|     auto line_4 = container->add<StringElement>(); | ||||
| 
 | ||||
|     RfidKey& key = app->worker.key; | ||||
|     const uint8_t* data = key.get_data(); | ||||
| 
 | ||||
|     for(uint8_t i = 0; i < key.get_type_data_count(); i++) { | ||||
|         if(i != 0) { | ||||
|             string_cat_printf(string_data, " "); | ||||
|         } | ||||
|         string_cat_printf(string_data, "%02X", data[i]); | ||||
|     } | ||||
| 
 | ||||
|     line_1->set_text(key.get_name(), 64, 17, AlignCenter, AlignBottom, FontSecondary); | ||||
|     line_2->set_text(string_get_cstr(string_data), 64, 29, AlignCenter, AlignBottom, FontPrimary); | ||||
| 
 | ||||
|     switch(key.get_type()) { | ||||
|     case LfrfidKeyType::KeyEM4100: | ||||
|         string_printf( | ||||
|             string_decrypted, "%03u,%05u", data[2], (uint16_t)((data[3] << 8) | (data[4]))); | ||||
| 
 | ||||
|         break; | ||||
|     case LfrfidKeyType::KeyH10301: | ||||
|     case LfrfidKeyType::KeyI40134: | ||||
|         string_printf( | ||||
|             string_decrypted, "FC: %u    ID: %u", data[0], (uint16_t)((data[1] << 8) | (data[2]))); | ||||
|         break; | ||||
|     } | ||||
|     line_3->set_text( | ||||
|         string_get_cstr(string_decrypted), 64, 39, AlignCenter, AlignBottom, FontSecondary); | ||||
| 
 | ||||
|     line_4->set_text( | ||||
|         lfrfid_key_get_type_string(key.get_type()), | ||||
|         64, | ||||
|         49, | ||||
|         AlignCenter, | ||||
|         AlignBottom, | ||||
|         FontSecondary); | ||||
| 
 | ||||
|     app->view_controller.switch_to<ContainerVM>(); | ||||
| } | ||||
| 
 | ||||
| bool LfRfidAppSceneSavedInfo::on_event(LfRfidApp* app, LfRfidApp::Event* event) { | ||||
|     bool consumed = false; | ||||
| 
 | ||||
|     return consumed; | ||||
| } | ||||
| 
 | ||||
| void LfRfidAppSceneSavedInfo::on_exit(LfRfidApp* app) { | ||||
|     app->view_controller.get<ContainerVM>()->clean(); | ||||
|     string_clear(string_data); | ||||
|     string_clear(string_decrypted); | ||||
| } | ||||
| 
 | ||||
| void LfRfidAppSceneSavedInfo::back_callback(void* context) { | ||||
|     LfRfidApp* app = static_cast<LfRfidApp*>(context); | ||||
|     LfRfidApp::Event event; | ||||
|     event.type = LfRfidApp::EventType::Back; | ||||
|     app->view_controller.send_event(&event); | ||||
| } | ||||
							
								
								
									
										15
									
								
								applications/lfrfid/scene/lfrfid-app-scene-saved-info.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								applications/lfrfid/scene/lfrfid-app-scene-saved-info.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,15 @@ | ||||
| #pragma once | ||||
| #include "../lfrfid-app.h" | ||||
| 
 | ||||
| class LfRfidAppSceneSavedInfo : public GenericScene<LfRfidApp> { | ||||
| public: | ||||
|     void on_enter(LfRfidApp* app, bool need_restore) final; | ||||
|     bool on_event(LfRfidApp* app, LfRfidApp::Event* event) final; | ||||
|     void on_exit(LfRfidApp* app) final; | ||||
| 
 | ||||
| private: | ||||
|     static void back_callback(void* context); | ||||
| 
 | ||||
|     string_t string_data; | ||||
|     string_t string_decrypted; | ||||
| }; | ||||
| @ -0,0 +1,67 @@ | ||||
| #include "lfrfid-app-scene-saved-key-menu.h" | ||||
| 
 | ||||
| typedef enum { | ||||
|     SubmenuEmulate, | ||||
|     SubmenuWrite, | ||||
|     SubmenuEdit, | ||||
|     SubmenuDelete, | ||||
|     SubmenuInfo, | ||||
| } SubmenuIndex; | ||||
| 
 | ||||
| void LfRfidAppSceneSavedKeyMenu::on_enter(LfRfidApp* app, bool need_restore) { | ||||
|     auto submenu = app->view_controller.get<SubmenuVM>(); | ||||
| 
 | ||||
|     submenu->add_item("Emulate", SubmenuEmulate, submenu_callback, app); | ||||
|     submenu->add_item("Write", SubmenuWrite, submenu_callback, app); | ||||
|     submenu->add_item("Edit", SubmenuEdit, submenu_callback, app); | ||||
|     submenu->add_item("Delete", SubmenuDelete, submenu_callback, app); | ||||
|     submenu->add_item("Info", SubmenuInfo, submenu_callback, app); | ||||
| 
 | ||||
|     if(need_restore) { | ||||
|         submenu->set_selected_item(submenu_item_selected); | ||||
|     } | ||||
| 
 | ||||
|     app->view_controller.switch_to<SubmenuVM>(); | ||||
| } | ||||
| 
 | ||||
| bool LfRfidAppSceneSavedKeyMenu::on_event(LfRfidApp* app, LfRfidApp::Event* event) { | ||||
|     bool consumed = false; | ||||
| 
 | ||||
|     if(event->type == LfRfidApp::EventType::MenuSelected) { | ||||
|         submenu_item_selected = event->payload.menu_index; | ||||
|         switch(event->payload.menu_index) { | ||||
|         case SubmenuEmulate: | ||||
|             app->scene_controller.switch_to_next_scene(LfRfidApp::SceneType::Emulate); | ||||
|             break; | ||||
|         case SubmenuWrite: | ||||
|             app->scene_controller.switch_to_next_scene(LfRfidApp::SceneType::Write); | ||||
|             break; | ||||
|         case SubmenuEdit: | ||||
|             app->scene_controller.switch_to_next_scene(LfRfidApp::SceneType::SaveData); | ||||
|             break; | ||||
|         case SubmenuDelete: | ||||
|             app->scene_controller.switch_to_next_scene(LfRfidApp::SceneType::DeleteConfirm); | ||||
|             break; | ||||
|         case SubmenuInfo: | ||||
|             app->scene_controller.switch_to_next_scene(LfRfidApp::SceneType::SavedInfo); | ||||
|             break; | ||||
|         } | ||||
|         consumed = true; | ||||
|     } | ||||
| 
 | ||||
|     return consumed; | ||||
| } | ||||
| 
 | ||||
| void LfRfidAppSceneSavedKeyMenu::on_exit(LfRfidApp* app) { | ||||
|     app->view_controller.get<SubmenuVM>()->clean(); | ||||
| } | ||||
| 
 | ||||
| void LfRfidAppSceneSavedKeyMenu::submenu_callback(void* context, uint32_t index) { | ||||
|     LfRfidApp* app = static_cast<LfRfidApp*>(context); | ||||
|     LfRfidApp::Event event; | ||||
| 
 | ||||
|     event.type = LfRfidApp::EventType::MenuSelected; | ||||
|     event.payload.menu_index = index; | ||||
| 
 | ||||
|     app->view_controller.send_event(&event); | ||||
| } | ||||
							
								
								
									
										13
									
								
								applications/lfrfid/scene/lfrfid-app-scene-saved-key-menu.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								applications/lfrfid/scene/lfrfid-app-scene-saved-key-menu.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,13 @@ | ||||
| #pragma once | ||||
| #include "../lfrfid-app.h" | ||||
| 
 | ||||
| class LfRfidAppSceneSavedKeyMenu : public GenericScene<LfRfidApp> { | ||||
| public: | ||||
|     void on_enter(LfRfidApp* app, bool need_restore) final; | ||||
|     bool on_event(LfRfidApp* app, LfRfidApp::Event* event) final; | ||||
|     void on_exit(LfRfidApp* app) final; | ||||
| 
 | ||||
| private: | ||||
|     static void submenu_callback(void* context, uint32_t index); | ||||
|     uint32_t submenu_item_selected = 0; | ||||
| }; | ||||
							
								
								
									
										18
									
								
								applications/lfrfid/scene/lfrfid-app-scene-select-key.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								applications/lfrfid/scene/lfrfid-app-scene-select-key.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,18 @@ | ||||
| #include "lfrfid-app-scene-select-key.h" | ||||
| 
 | ||||
| void LfRfidAppSceneSelectKey::on_enter(LfRfidApp* app, bool need_restore) { | ||||
|     if(app->load_key_from_file_select(need_restore)) { | ||||
|         app->scene_controller.switch_to_next_scene(LfRfidApp::SceneType::SavedKeyMenu); | ||||
|     } else { | ||||
|         app->scene_controller.switch_to_previous_scene(); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| bool LfRfidAppSceneSelectKey::on_event(LfRfidApp* app, LfRfidApp::Event* event) { | ||||
|     bool consumed = false; | ||||
| 
 | ||||
|     return consumed; | ||||
| } | ||||
| 
 | ||||
| void LfRfidAppSceneSelectKey::on_exit(LfRfidApp* app) { | ||||
| } | ||||
							
								
								
									
										9
									
								
								applications/lfrfid/scene/lfrfid-app-scene-select-key.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								applications/lfrfid/scene/lfrfid-app-scene-select-key.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,9 @@ | ||||
| #pragma once | ||||
| #include "../lfrfid-app.h" | ||||
| 
 | ||||
| class LfRfidAppSceneSelectKey : public GenericScene<LfRfidApp> { | ||||
| public: | ||||
|     void on_enter(LfRfidApp* app, bool need_restore) final; | ||||
|     bool on_event(LfRfidApp* app, LfRfidApp::Event* event) final; | ||||
|     void on_exit(LfRfidApp* app) final; | ||||
| }; | ||||
| @ -16,7 +16,11 @@ void LfRfidAppSceneStart::on_enter(LfRfidApp* app, bool need_restore) { | ||||
|     if(need_restore) { | ||||
|         submenu->set_selected_item(submenu_item_selected); | ||||
|     } | ||||
| 
 | ||||
|     app->view_controller.switch_to<SubmenuVM>(); | ||||
| 
 | ||||
|     // clear key
 | ||||
|     app->worker.key.clear(); | ||||
| } | ||||
| 
 | ||||
| bool LfRfidAppSceneStart::on_event(LfRfidApp* app, LfRfidApp::Event* event) { | ||||
| @ -29,8 +33,10 @@ bool LfRfidAppSceneStart::on_event(LfRfidApp* app, LfRfidApp::Event* event) { | ||||
|             app->scene_controller.switch_to_next_scene(LfRfidApp::SceneType::Read); | ||||
|             break; | ||||
|         case SubmenuSaved: | ||||
|             app->scene_controller.switch_to_next_scene(LfRfidApp::SceneType::SelectKey); | ||||
|             break; | ||||
|         case SubmenuAddManually: | ||||
|             app->scene_controller.switch_to_next_scene(LfRfidApp::SceneType::SaveType); | ||||
|             break; | ||||
|         } | ||||
|         consumed = true; | ||||
|  | ||||
| @ -4,7 +4,7 @@ void LfRfidAppSceneWrite::on_enter(LfRfidApp* app, bool need_restore) { | ||||
|     card_not_supported = false; | ||||
|     string_init(data_string); | ||||
| 
 | ||||
|     uint8_t* data = app->worker.key.get_data(); | ||||
|     const uint8_t* data = app->worker.key.get_data(); | ||||
| 
 | ||||
|     for(uint8_t i = 0; i < app->worker.key.get_type_data_count(); i++) { | ||||
|         string_cat_printf(data_string, "%02X", data[i]); | ||||
| @ -12,8 +12,12 @@ void LfRfidAppSceneWrite::on_enter(LfRfidApp* app, bool need_restore) { | ||||
| 
 | ||||
|     auto popup = app->view_controller.get<PopupVM>(); | ||||
| 
 | ||||
|     popup->set_header("Writing", 90, 34, AlignCenter, AlignTop); | ||||
|     popup->set_text(string_get_cstr(data_string), 90, 48, AlignCenter, AlignTop); | ||||
|     popup->set_header("LF writing", 89, 30, AlignCenter, AlignTop); | ||||
|     if(strlen(app->worker.key.get_name())) { | ||||
|         popup->set_text(app->worker.key.get_name(), 89, 43, AlignCenter, AlignTop); | ||||
|     } else { | ||||
|         popup->set_text(string_get_cstr(data_string), 89, 43, AlignCenter, AlignTop); | ||||
|     } | ||||
|     popup->set_icon(0, 4, I_RFIDDolphinSend_98x60); | ||||
| 
 | ||||
|     app->view_controller.switch_to<PopupVM>(); | ||||
|  | ||||
							
								
								
									
										68
									
								
								lib/app-scened-template/file-worker-cpp.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										68
									
								
								lib/app-scened-template/file-worker-cpp.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,68 @@ | ||||
| #include "file-worker-cpp.h" | ||||
| #include <hex.h> | ||||
| 
 | ||||
| FileWorkerCpp::FileWorkerCpp(bool _silent) { | ||||
|     file_worker = file_worker_alloc(_silent); | ||||
| } | ||||
| 
 | ||||
| FileWorkerCpp::~FileWorkerCpp() { | ||||
|     file_worker_free(file_worker); | ||||
| } | ||||
| 
 | ||||
| bool FileWorkerCpp::open(const char* filename, FS_AccessMode access_mode, FS_OpenMode open_mode) { | ||||
|     return file_worker_open(file_worker, filename, access_mode, open_mode); | ||||
| } | ||||
| 
 | ||||
| bool FileWorkerCpp::close() { | ||||
|     return file_worker_close(file_worker); | ||||
| } | ||||
| 
 | ||||
| bool FileWorkerCpp::mkdir(const char* dirname) { | ||||
|     return file_worker_mkdir(file_worker, dirname); | ||||
| } | ||||
| 
 | ||||
| bool FileWorkerCpp::remove(const char* filename) { | ||||
|     return file_worker_remove(file_worker, filename); | ||||
| } | ||||
| 
 | ||||
| bool FileWorkerCpp::read(void* buffer, uint16_t bytes_to_read) { | ||||
|     return file_worker_read(file_worker, buffer, bytes_to_read); | ||||
| } | ||||
| 
 | ||||
| bool FileWorkerCpp::read_until(string_t str_result, char separator) { | ||||
|     return file_worker_read_until(file_worker, str_result, separator); | ||||
| } | ||||
| 
 | ||||
| bool FileWorkerCpp::read_hex(uint8_t* buffer, uint16_t bytes_to_read) { | ||||
|     return file_worker_read_hex(file_worker, buffer, bytes_to_read); | ||||
| } | ||||
| 
 | ||||
| bool FileWorkerCpp::tell(uint64_t* position) { | ||||
|     return file_worker_tell(file_worker, position); | ||||
| } | ||||
| 
 | ||||
| bool FileWorkerCpp::seek(uint64_t position, bool from_start) { | ||||
|     return file_worker_seek(file_worker, position, from_start); | ||||
| } | ||||
| 
 | ||||
| bool FileWorkerCpp::write(const void* buffer, uint16_t bytes_to_write) { | ||||
|     return file_worker_write(file_worker, buffer, bytes_to_write); | ||||
| } | ||||
| 
 | ||||
| bool FileWorkerCpp::write_hex(const uint8_t* buffer, uint16_t bytes_to_write) { | ||||
|     return file_worker_write_hex(file_worker, buffer, bytes_to_write); | ||||
| } | ||||
| 
 | ||||
| void FileWorkerCpp::show_error(const char* error_text) { | ||||
|     file_worker_show_error(file_worker, error_text); | ||||
| } | ||||
| 
 | ||||
| bool FileWorkerCpp::file_select( | ||||
|     const char* path, | ||||
|     const char* extension, | ||||
|     char* result, | ||||
|     uint8_t result_size, | ||||
|     char* selected_filename) { | ||||
|     return file_worker_file_select( | ||||
|         file_worker, path, extension, result, result_size, selected_filename); | ||||
| } | ||||
							
								
								
									
										135
									
								
								lib/app-scened-template/file-worker-cpp.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										135
									
								
								lib/app-scened-template/file-worker-cpp.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,135 @@ | ||||
| #pragma once | ||||
| #include "file-worker.h" | ||||
| 
 | ||||
| /**
 | ||||
|  * @brief File operations helper class. | ||||
|  * Automatically opens API records, shows error message on error. | ||||
|  */ | ||||
| class FileWorkerCpp { | ||||
| public: | ||||
|     FileWorkerCpp(bool silent = false); | ||||
|     ~FileWorkerCpp(); | ||||
| 
 | ||||
|     /**
 | ||||
|      * @brief Open file | ||||
|      *  | ||||
|      * @param filename  | ||||
|      * @param access_mode  | ||||
|      * @param open_mode  | ||||
|      * @return true on success | ||||
|      */ | ||||
|     bool open(const char* filename, FS_AccessMode access_mode, FS_OpenMode open_mode); | ||||
| 
 | ||||
|     /**
 | ||||
|      * @brief Close file | ||||
|      *  | ||||
|      * @return true on success | ||||
|      */ | ||||
|     bool close(); | ||||
| 
 | ||||
|     /**
 | ||||
|      * @brief Creates a directory. Doesn't show error if directory exist.  | ||||
|      *  | ||||
|      * @param dirname  | ||||
|      * @return true on success | ||||
|      */ | ||||
|     bool mkdir(const char* dirname); | ||||
| 
 | ||||
|     /**
 | ||||
|      * @brief Removes the file. Doesn't show error if file doesn't exist. | ||||
|      *  | ||||
|      * @param filename  | ||||
|      * @return true on success   | ||||
|      */ | ||||
|     bool remove(const char* filename); | ||||
| 
 | ||||
|     /**
 | ||||
|      * @brief Reads data from a file. | ||||
|      *  | ||||
|      * @param buffer  | ||||
|      * @param bytes_to_read  | ||||
|      * @return true on success   | ||||
|      */ | ||||
|     bool read(void* buffer, uint16_t bytes_to_read = 1); | ||||
| 
 | ||||
|     /**
 | ||||
|      * @brief Reads data from a file until separator or EOF is found.  | ||||
|      * Moves seek pointer to the next symbol after the separator. The separator is not included in the result. | ||||
|      *  | ||||
|      * @param result  | ||||
|      * @param separator  | ||||
|      * @return true on success   | ||||
|      */ | ||||
|     bool read_until(string_t result, char separator = '\n'); | ||||
| 
 | ||||
|     /**
 | ||||
|      * @brief Reads data in hexadecimal space-delimited format. For example "AF FF" in a file - [175, 255] in a read buffer. | ||||
|      *  | ||||
|      * @param buffer  | ||||
|      * @param bytes_to_read  | ||||
|      * @return true on success   | ||||
|      */ | ||||
|     bool read_hex(uint8_t* buffer, uint16_t bytes_to_read = 1); | ||||
| 
 | ||||
|     /**
 | ||||
|      * @brief Read seek pointer value | ||||
|      *  | ||||
|      * @param position  | ||||
|      * @return true on success   | ||||
|      */ | ||||
|     bool tell(uint64_t* position); | ||||
| 
 | ||||
|     /**
 | ||||
|      * @brief Set seek pointer value | ||||
|      *  | ||||
|      * @param position  | ||||
|      * @param from_start  | ||||
|      * @return true on success   | ||||
|      */ | ||||
|     bool seek(uint64_t position, bool from_start); | ||||
| 
 | ||||
|     /**
 | ||||
|      * @brief Write data to file. | ||||
|      *  | ||||
|      * @param buffer  | ||||
|      * @param bytes_to_write  | ||||
|      * @return true on success   | ||||
|      */ | ||||
|     bool write(const void* buffer, uint16_t bytes_to_write = 1); | ||||
| 
 | ||||
|     /**
 | ||||
|      * @brief Write data to file in hexadecimal space-delimited format. For example [175, 255] in a write buffer - "AF FF" in a file. | ||||
|      *  | ||||
|      * @param buffer  | ||||
|      * @param bytes_to_write  | ||||
|      * @return true on success   | ||||
|      */ | ||||
|     bool write_hex(const uint8_t* buffer, uint16_t bytes_to_write = 1); | ||||
| 
 | ||||
|     /**
 | ||||
|      * @brief Show system file error message | ||||
|      *  | ||||
|      * @param error_text  | ||||
|      */ | ||||
|     void show_error(const char* error_text); | ||||
| 
 | ||||
|     /**
 | ||||
|      * @brief Show system file select widget | ||||
|      *  | ||||
|      * @param path  | ||||
|      * @param extension  | ||||
|      * @param result  | ||||
|      * @param result_size  | ||||
|      * @param selected_filename  | ||||
|      * @return true if file was selected | ||||
|      */ | ||||
|     bool file_select( | ||||
|         const char* path, | ||||
|         const char* extension, | ||||
|         char* result, | ||||
|         uint8_t result_size, | ||||
|         char* selected_filename); | ||||
| 
 | ||||
| private: | ||||
|     FileWorker* file_worker; | ||||
| }; | ||||
							
								
								
									
										280
									
								
								lib/app-scened-template/file-worker.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										280
									
								
								lib/app-scened-template/file-worker.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,280 @@ | ||||
| #include "file-worker.h" | ||||
| #include <hex.h> | ||||
| #include <sd-card-api.h> | ||||
| #include <furi.h> | ||||
| 
 | ||||
| struct FileWorker { | ||||
|     FS_Api* fs_api; | ||||
|     SdCard_Api* sd_ex_api; | ||||
|     bool silent; | ||||
|     File file; | ||||
| }; | ||||
| 
 | ||||
| bool file_worker_check_common_errors(FileWorker* file_worker); | ||||
| void file_worker_show_error_internal(FileWorker* file_worker, const char* error_text); | ||||
| bool file_worker_read_internal(FileWorker* file_worker, void* buffer, uint16_t bytes_to_read); | ||||
| bool file_worker_write_internal( | ||||
|     FileWorker* file_worker, | ||||
|     const void* buffer, | ||||
|     uint16_t bytes_to_write); | ||||
| bool file_worker_tell_internal(FileWorker* file_worker, uint64_t* position); | ||||
| bool file_worker_seek_internal(FileWorker* file_worker, uint64_t position, bool from_start); | ||||
| 
 | ||||
| FileWorker* file_worker_alloc(bool _silent) { | ||||
|     FileWorker* file_worker = malloc(sizeof(FileWorker)); | ||||
|     file_worker->silent = _silent; | ||||
|     file_worker->fs_api = furi_record_open("sdcard"); | ||||
|     file_worker->sd_ex_api = furi_record_open("sdcard-ex"); | ||||
| 
 | ||||
|     return file_worker; | ||||
| } | ||||
| 
 | ||||
| void file_worker_free(FileWorker* file_worker) { | ||||
|     furi_record_close("sdcard"); | ||||
|     furi_record_close("sdcard-ex"); | ||||
|     free(file_worker); | ||||
| } | ||||
| 
 | ||||
| bool file_worker_open( | ||||
|     FileWorker* file_worker, | ||||
|     const char* filename, | ||||
|     FS_AccessMode access_mode, | ||||
|     FS_OpenMode open_mode) { | ||||
|     bool result = | ||||
|         file_worker->fs_api->file.open(&file_worker->file, filename, access_mode, open_mode); | ||||
| 
 | ||||
|     if(!result) { | ||||
|         file_worker_show_error_internal(file_worker, "Cannot open\nfile"); | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     return file_worker_check_common_errors(file_worker); | ||||
| } | ||||
| 
 | ||||
| bool file_worker_close(FileWorker* file_worker) { | ||||
|     file_worker->fs_api->file.close(&file_worker->file); | ||||
| 
 | ||||
|     return file_worker_check_common_errors(file_worker); | ||||
| } | ||||
| 
 | ||||
| bool file_worker_mkdir(FileWorker* file_worker, const char* dirname) { | ||||
|     FS_Error fs_result = file_worker->fs_api->common.mkdir(dirname); | ||||
| 
 | ||||
|     if(fs_result != FSE_OK && fs_result != FSE_EXIST) { | ||||
|         file_worker_show_error_internal(file_worker, "Cannot create\nfolder"); | ||||
|         return false; | ||||
|     }; | ||||
| 
 | ||||
|     return file_worker_check_common_errors(file_worker); | ||||
| } | ||||
| 
 | ||||
| bool file_worker_remove(FileWorker* file_worker, const char* filename) { | ||||
|     FS_Error fs_result = file_worker->fs_api->common.remove(filename); | ||||
|     if(fs_result != FSE_OK && fs_result != FSE_NOT_EXIST) { | ||||
|         file_worker_show_error_internal(file_worker, "Cannot remove\nold file"); | ||||
|         return false; | ||||
|     }; | ||||
| 
 | ||||
|     return file_worker_check_common_errors(file_worker); | ||||
| } | ||||
| 
 | ||||
| bool file_worker_read(FileWorker* file_worker, void* buffer, uint16_t bytes_to_read) { | ||||
|     if(!file_worker_read_internal(file_worker, buffer, bytes_to_read)) { | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     return file_worker_check_common_errors(file_worker); | ||||
| } | ||||
| 
 | ||||
| bool file_worker_read_until(FileWorker* file_worker, 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 = | ||||
|             file_worker->fs_api->file.read(&file_worker->file, buffer, buffer_size); | ||||
|         if(file_worker->file.error_id != FSE_OK) { | ||||
|             file_worker_show_error_internal(file_worker, "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(!file_worker_tell_internal(file_worker, &position)) { | ||||
|                     return false; | ||||
|                 } | ||||
| 
 | ||||
|                 position = position - read_count + i + 1; | ||||
| 
 | ||||
|                 if(!file_worker_seek_internal(file_worker, position, true)) { | ||||
|                     return false; | ||||
|                 } | ||||
| 
 | ||||
|                 result = true; | ||||
|                 break; | ||||
|             } else { | ||||
|                 string_push_back(str_result, buffer[i]); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         if(result || read_count == 0) { | ||||
|             break; | ||||
|         } | ||||
|     } while(true); | ||||
| 
 | ||||
|     return file_worker_check_common_errors(file_worker); | ||||
| } | ||||
| 
 | ||||
| bool file_worker_read_hex(FileWorker* file_worker, uint8_t* buffer, uint16_t bytes_to_read) { | ||||
|     uint8_t hi_nibble_value, low_nibble_value; | ||||
|     uint8_t text[2]; | ||||
| 
 | ||||
|     for(uint8_t i = 0; i < bytes_to_read; i++) { | ||||
|         if(i != 0) { | ||||
|             // space
 | ||||
|             if(!file_worker_read_internal(file_worker, text, 1)) { | ||||
|                 return false; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         // actual data
 | ||||
|         if(!file_worker_read_internal(file_worker, text, 2)) { | ||||
|             return false; | ||||
|         } | ||||
| 
 | ||||
|         // convert hex value to byte
 | ||||
|         if(hex_char_to_hex_nibble(text[0], &hi_nibble_value) && | ||||
|            hex_char_to_hex_nibble(text[1], &low_nibble_value)) { | ||||
|             buffer[i] = (hi_nibble_value << 4) | low_nibble_value; | ||||
|         } else { | ||||
|             file_worker_show_error_internal(file_worker, "Cannot parse\nfile"); | ||||
|             return false; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     return file_worker_check_common_errors(file_worker); | ||||
| } | ||||
| 
 | ||||
| bool file_worker_tell(FileWorker* file_worker, uint64_t* position) { | ||||
|     if(!file_worker_tell_internal(file_worker, position)) { | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     return file_worker_check_common_errors(file_worker); | ||||
| } | ||||
| 
 | ||||
| bool file_worker_seek(FileWorker* file_worker, uint64_t position, bool from_start) { | ||||
|     if(!file_worker_seek_internal(file_worker, position, from_start)) { | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     return file_worker_check_common_errors(file_worker); | ||||
| } | ||||
| 
 | ||||
| bool file_worker_write(FileWorker* file_worker, const void* buffer, uint16_t bytes_to_write) { | ||||
|     if(!file_worker_write_internal(file_worker, buffer, bytes_to_write)) { | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     return file_worker_check_common_errors(file_worker); | ||||
| } | ||||
| 
 | ||||
| bool file_worker_write_hex(FileWorker* file_worker, 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(!file_worker_write_internal(file_worker, space, 1)) { | ||||
|                 return false; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         if(!file_worker_write_internal(file_worker, byte_text, 2)) { | ||||
|             return false; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     return file_worker_check_common_errors(file_worker); | ||||
| } | ||||
| 
 | ||||
| void file_worker_show_error(FileWorker* file_worker, const char* error_text) { | ||||
|     file_worker->sd_ex_api->show_error(file_worker->sd_ex_api->context, error_text); | ||||
| } | ||||
| 
 | ||||
| bool file_worker_file_select( | ||||
|     FileWorker* file_worker, | ||||
|     const char* path, | ||||
|     const char* extension, | ||||
|     char* result, | ||||
|     uint8_t result_size, | ||||
|     char* selected_filename) { | ||||
|     return file_worker->sd_ex_api->file_select( | ||||
|         file_worker->sd_ex_api->context, path, extension, result, result_size, selected_filename); | ||||
| } | ||||
| 
 | ||||
| bool file_worker_check_common_errors(FileWorker* file_worker) { | ||||
|     file_worker->sd_ex_api->check_error(file_worker->sd_ex_api->context); | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| void file_worker_show_error_internal(FileWorker* file_worker, const char* error_text) { | ||||
|     if(!file_worker->silent) { | ||||
|         file_worker_show_error(file_worker, error_text); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| bool file_worker_read_internal(FileWorker* file_worker, void* buffer, uint16_t bytes_to_read) { | ||||
|     uint16_t read_count = | ||||
|         file_worker->fs_api->file.read(&file_worker->file, buffer, bytes_to_read); | ||||
| 
 | ||||
|     if(file_worker->file.error_id != FSE_OK || read_count != bytes_to_read) { | ||||
|         file_worker_show_error_internal(file_worker, "Cannot read\nfile"); | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| bool file_worker_write_internal( | ||||
|     FileWorker* file_worker, | ||||
|     const void* buffer, | ||||
|     uint16_t bytes_to_write) { | ||||
|     uint16_t write_count = | ||||
|         file_worker->fs_api->file.write(&file_worker->file, buffer, bytes_to_write); | ||||
| 
 | ||||
|     if(file_worker->file.error_id != FSE_OK || write_count != bytes_to_write) { | ||||
|         file_worker_show_error_internal(file_worker, "Cannot write\nto file"); | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| bool file_worker_tell_internal(FileWorker* file_worker, uint64_t* position) { | ||||
|     *position = file_worker->fs_api->file.tell(&file_worker->file); | ||||
| 
 | ||||
|     if(file_worker->file.error_id != FSE_OK) { | ||||
|         file_worker_show_error_internal(file_worker, "Cannot tell\nfile offset"); | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| bool file_worker_seek_internal(FileWorker* file_worker, uint64_t position, bool from_start) { | ||||
|     file_worker->fs_api->file.seek(&file_worker->file, position, from_start); | ||||
|     if(file_worker->file.error_id != FSE_OK) { | ||||
|         file_worker_show_error_internal(file_worker, "Cannot seek\nfile"); | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     return true; | ||||
| } | ||||
| @ -1,237 +0,0 @@ | ||||
| #include "file-worker.h" | ||||
| 
 | ||||
| FileWorker::FileWorker(bool _silent) | ||||
|     : fs_api{"sdcard"} | ||||
|     , sd_ex_api{"sdcard-ex"} { | ||||
|     silent = _silent; | ||||
| } | ||||
| 
 | ||||
| FileWorker::~FileWorker() { | ||||
| } | ||||
| 
 | ||||
| bool FileWorker::open(const char* filename, FS_AccessMode access_mode, FS_OpenMode open_mode) { | ||||
|     bool result = fs_api.get()->file.open(&file, filename, access_mode, open_mode); | ||||
| 
 | ||||
|     if(!result) { | ||||
|         show_error_internal("Cannot open\nfile"); | ||||
|         close(); | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     return check_common_errors(); | ||||
| } | ||||
| 
 | ||||
| bool FileWorker::close() { | ||||
|     fs_api.get()->file.close(&file); | ||||
| 
 | ||||
|     return check_common_errors(); | ||||
| } | ||||
| 
 | ||||
| bool FileWorker::mkdir(const char* dirname) { | ||||
|     FS_Error fs_result = fs_api.get()->common.mkdir(dirname); | ||||
| 
 | ||||
|     if(fs_result != FSE_OK && fs_result != FSE_EXIST) { | ||||
|         show_error_internal("Cannot create\nfolder"); | ||||
|         return false; | ||||
|     }; | ||||
| 
 | ||||
|     return check_common_errors(); | ||||
| } | ||||
| 
 | ||||
| bool FileWorker::remove(const char* filename) { | ||||
|     FS_Error fs_result = fs_api.get()->common.remove(filename); | ||||
|     if(fs_result != FSE_OK && fs_result != FSE_NOT_EXIST) { | ||||
|         show_error_internal("Cannot remove\nold file"); | ||||
|         return false; | ||||
|     }; | ||||
| 
 | ||||
|     return check_common_errors(); | ||||
| } | ||||
| 
 | ||||
| bool FileWorker::read(void* buffer, uint16_t bytes_to_read) { | ||||
|     if(!read_internal(buffer, bytes_to_read)) { | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     return check_common_errors(); | ||||
| } | ||||
| 
 | ||||
| bool FileWorker::read_until(string_t str_result, char separator) { | ||||
|     string_clean(str_result); | ||||
|     const uint8_t buffer_size = 32; | ||||
|     uint8_t buffer[buffer_size]; | ||||
| 
 | ||||
|     do { | ||||
|         uint16_t read_count = fs_api.get()->file.read(&file, buffer, buffer_size); | ||||
|         if(file.error_id != FSE_OK) { | ||||
|             show_error_internal("Cannot read\nfile"); | ||||
|             return false; | ||||
|         } | ||||
| 
 | ||||
|         bool result = false; | ||||
|         for(uint16_t i = 0; i < read_count; i++) { | ||||
|             if(buffer[i] == separator) { | ||||
|                 uint64_t position; | ||||
|                 if(!tell_internal(&position)) { | ||||
|                     return false; | ||||
|                 } | ||||
| 
 | ||||
|                 position = position - read_count + i + 1; | ||||
| 
 | ||||
|                 if(!seek_internal(position, true)) { | ||||
|                     return false; | ||||
|                 } | ||||
| 
 | ||||
|                 result = true; | ||||
|                 break; | ||||
|             } else { | ||||
|                 string_push_back(str_result, buffer[i]); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         if(result || read_count == 0) { | ||||
|             break; | ||||
|         } | ||||
|     } while(true); | ||||
| 
 | ||||
|     return check_common_errors(); | ||||
| } | ||||
| 
 | ||||
| bool FileWorker::read_hex(uint8_t* buffer, uint16_t bytes_to_read) { | ||||
|     uint8_t text[2]; | ||||
| 
 | ||||
|     for(uint8_t i = 0; i < bytes_to_read; i++) { | ||||
|         if(i != 0) { | ||||
|             // space
 | ||||
|             if(!read_internal(text, 1)) { | ||||
|                 return false; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         // actual data
 | ||||
|         if(!read_internal(text, 2)) { | ||||
|             return false; | ||||
|         } | ||||
| 
 | ||||
|         // convert hex value to byte
 | ||||
|         buffer[i] = strtol(reinterpret_cast<char*>(text), NULL, 16); | ||||
|     } | ||||
| 
 | ||||
|     return check_common_errors(); | ||||
| } | ||||
| 
 | ||||
| bool FileWorker::tell(uint64_t* position) { | ||||
|     if(!tell_internal(position)) { | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     return check_common_errors(); | ||||
| } | ||||
| 
 | ||||
| bool FileWorker::seek(uint64_t position, bool from_start) { | ||||
|     if(!seek_internal(position, from_start)) { | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     return check_common_errors(); | ||||
| } | ||||
| 
 | ||||
| bool FileWorker::write(const void* buffer, uint16_t bytes_to_write) { | ||||
|     if(!write_internal(buffer, bytes_to_write)) { | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     return check_common_errors(); | ||||
| } | ||||
| 
 | ||||
| bool FileWorker::write_hex(const uint8_t* buffer, uint16_t bytes_to_write) { | ||||
|     const uint8_t byte_text_size = 3; | ||||
|     char byte_text[byte_text_size]; | ||||
| 
 | ||||
|     for(uint8_t i = 0; i < bytes_to_write; i++) { | ||||
|         sniprintf(byte_text, byte_text_size, "%02X", buffer[i]); | ||||
| 
 | ||||
|         if(i != 0) { | ||||
|             // space
 | ||||
|             const char* space = " "; | ||||
|             if(!write_internal(space, 1)) { | ||||
|                 return false; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         if(!write_internal(byte_text, 2)) { | ||||
|             return false; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     return check_common_errors(); | ||||
| } | ||||
| 
 | ||||
| void FileWorker::show_error(const char* error_text) { | ||||
|     sd_ex_api.get()->show_error(sd_ex_api.get()->context, error_text); | ||||
| } | ||||
| 
 | ||||
| bool FileWorker::file_select( | ||||
|     const char* path, | ||||
|     const char* extension, | ||||
|     char* result, | ||||
|     uint8_t result_size, | ||||
|     char* selected_filename) { | ||||
|     return sd_ex_api.get()->file_select( | ||||
|         sd_ex_api.get()->context, path, extension, result, result_size, selected_filename); | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| bool FileWorker::check_common_errors() { | ||||
|     sd_ex_api.get()->check_error(sd_ex_api.get()->context); | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| void FileWorker::show_error_internal(const char* error_text) { | ||||
|     if(!silent) { | ||||
|         show_error(error_text); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| bool FileWorker::read_internal(void* buffer, uint16_t bytes_to_read) { | ||||
|     uint16_t read_count = fs_api.get()->file.read(&file, buffer, bytes_to_read); | ||||
| 
 | ||||
|     if(file.error_id != FSE_OK || read_count != bytes_to_read) { | ||||
|         show_error_internal("Cannot read\nfile"); | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| bool FileWorker::write_internal(const void* buffer, uint16_t bytes_to_write) { | ||||
|     uint16_t write_count = fs_api.get()->file.write(&file, buffer, bytes_to_write); | ||||
| 
 | ||||
|     if(file.error_id != FSE_OK || write_count != bytes_to_write) { | ||||
|         show_error_internal("Cannot write\nto file"); | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| bool FileWorker::tell_internal(uint64_t* position) { | ||||
|     *position = fs_api.get()->file.tell(&file); | ||||
| 
 | ||||
|     if(file.error_id != FSE_OK) { | ||||
|         show_error_internal("Cannot tell\nfile offset"); | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| bool FileWorker::seek_internal(uint64_t position, bool from_start) { | ||||
|     fs_api.get()->file.seek(&file, position, from_start); | ||||
|     if(file.error_id != FSE_OK) { | ||||
|         show_error_internal("Cannot seek\nfile"); | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     return true; | ||||
| } | ||||
| @ -1,127 +1,155 @@ | ||||
| #pragma once | ||||
| #include "record-controller.hpp" | ||||
| #include <sd-card-api.h> | ||||
| #include <filesystem-api.h> | ||||
| #include <m-string.h> | ||||
| #include <filesystem-api.h> | ||||
| 
 | ||||
| #ifdef __cplusplus | ||||
| extern "C" { | ||||
| #endif | ||||
| 
 | ||||
| /**
 | ||||
|  * @brief File operations helper class. | ||||
|  * Automatically opens API records, shows error message on error. | ||||
|  */ | ||||
| class FileWorker { | ||||
| public: | ||||
|     FileWorker(bool silent = false); | ||||
|     ~FileWorker(); | ||||
| typedef struct FileWorker FileWorker; | ||||
| 
 | ||||
|     RecordController<FS_Api> fs_api; | ||||
|     RecordController<SdCard_Api> sd_ex_api; | ||||
| /**
 | ||||
|  * @brief Allocate FileWorker | ||||
|  *  | ||||
|  * @param silent do not show errors except from file_worker_show_error fn | ||||
|  * @return FileWorker*  | ||||
|  */ | ||||
| FileWorker* file_worker_alloc(bool silent); | ||||
| 
 | ||||
| /**
 | ||||
|  * @brief free FileWorker | ||||
|  *  | ||||
|  * @param file_worker  | ||||
|  */ | ||||
| void file_worker_free(FileWorker* file_worker); | ||||
| 
 | ||||
| /**
 | ||||
|  * @brief Open file | ||||
|  *  | ||||
|  * @param file_worker FileWorker instance  | ||||
|  * @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 file_worker_open( | ||||
|     FileWorker* file_worker, | ||||
|     const char* filename, | ||||
|     FS_AccessMode access_mode, | ||||
|     FS_OpenMode open_mode); | ||||
| 
 | ||||
| /**
 | ||||
|  * @brief Close file | ||||
|  *  | ||||
|  * @param file_worker FileWorker instance  | ||||
|  * @return true on success | ||||
|  */ | ||||
|     bool close(); | ||||
| bool file_worker_close(FileWorker* file_worker); | ||||
| 
 | ||||
| /**
 | ||||
|  * @brief Creates a directory. Doesn't show error if directory exist.  | ||||
|  *  | ||||
|  * @param file_worker FileWorker instance  | ||||
|  * @param dirname  | ||||
|  * @return true on success | ||||
|  */ | ||||
|     bool mkdir(const char* dirname); | ||||
| bool file_worker_mkdir(FileWorker* file_worker, const char* dirname); | ||||
| 
 | ||||
| /**
 | ||||
|  * @brief Removes the file. Doesn't show error if file doesn't exist. | ||||
|  *  | ||||
|  * @param file_worker FileWorker instance  | ||||
|  * @param filename  | ||||
|  * @return true on success   | ||||
|  */ | ||||
|     bool remove(const char* filename); | ||||
| bool file_worker_remove(FileWorker* file_worker, const char* filename); | ||||
| 
 | ||||
| /**
 | ||||
|  * @brief Reads data from a file. | ||||
|  *  | ||||
|  * @param file_worker FileWorker instance  | ||||
|  * @param buffer  | ||||
|  * @param bytes_to_read  | ||||
|  * @return true on success   | ||||
|  */ | ||||
|     bool read(void* buffer, uint16_t bytes_to_read = 1); | ||||
| bool file_worker_read(FileWorker* file_worker, void* buffer, uint16_t bytes_to_read); | ||||
| 
 | ||||
| /**
 | ||||
|  * @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 file_worker FileWorker instance  | ||||
|  * @param result  | ||||
|  * @param separator  | ||||
|  * @return true on success   | ||||
|  */ | ||||
|     bool read_until(string_t result, char separator = '\n'); | ||||
| bool file_worker_read_until(FileWorker* file_worker, string_t result, char separator); | ||||
| 
 | ||||
| /**
 | ||||
|  * @brief Reads data in hexadecimal space-delimited format. For example "AF FF" in a file - [175, 255] in a read buffer. | ||||
|  *  | ||||
|  * @param file_worker FileWorker instance  | ||||
|  * @param buffer  | ||||
|  * @param bytes_to_read  | ||||
|  * @return true on success   | ||||
|  */ | ||||
|     bool read_hex(uint8_t* buffer, uint16_t bytes_to_read = 1); | ||||
| bool file_worker_read_hex(FileWorker* file_worker, uint8_t* buffer, uint16_t bytes_to_read); | ||||
| 
 | ||||
| /**
 | ||||
|  * @brief Read seek pointer value | ||||
|  *  | ||||
|  * @param file_worker FileWorker instance  | ||||
|  * @param position  | ||||
|  * @return true on success   | ||||
|  */ | ||||
|     bool tell(uint64_t* position); | ||||
| bool file_worker_tell(FileWorker* file_worker, uint64_t* position); | ||||
| 
 | ||||
| /**
 | ||||
|  * @brief Set seek pointer value | ||||
|  *  | ||||
|  * @param file_worker FileWorker instance  | ||||
|  * @param position  | ||||
|  * @param from_start  | ||||
|  * @return true on success   | ||||
|  */ | ||||
|     bool seek(uint64_t position, bool from_start); | ||||
| bool file_worker_seek(FileWorker* file_worker, uint64_t position, bool from_start); | ||||
| 
 | ||||
| /**
 | ||||
|  * @brief Write data to file. | ||||
|  *  | ||||
|  * @param file_worker FileWorker instance  | ||||
|  * @param buffer  | ||||
|  * @param bytes_to_write  | ||||
|  * @return true on success   | ||||
|  */ | ||||
|     bool write(const void* buffer, uint16_t bytes_to_write = 1); | ||||
| bool file_worker_write(FileWorker* file_worker, const void* buffer, uint16_t bytes_to_write); | ||||
| 
 | ||||
| /**
 | ||||
|  * @brief Write data to file in hexadecimal space-delimited format. For example [175, 255] in a write buffer - "AF FF" in a file. | ||||
|  *  | ||||
|  * @param file_worker FileWorker instance  | ||||
|  * @param buffer  | ||||
|  * @param bytes_to_write  | ||||
|  * @return true on success   | ||||
|  */ | ||||
|     bool write_hex(const uint8_t* buffer, uint16_t bytes_to_write = 1); | ||||
| bool file_worker_write_hex(FileWorker* file_worker, const uint8_t* buffer, uint16_t bytes_to_write); | ||||
| 
 | ||||
| /**
 | ||||
|  * @brief Show system file error message | ||||
|  *  | ||||
|  * @param file_worker FileWorker instance  | ||||
|  * @param error_text  | ||||
|  */ | ||||
|     void show_error(const char* error_text); | ||||
| void file_worker_show_error(FileWorker* file_worker, const char* error_text); | ||||
| 
 | ||||
| /**
 | ||||
|  * @brief Show system file select widget | ||||
|  *  | ||||
|  * @param file_worker FileWorker instance  | ||||
|  * @param path  | ||||
|  * @param extension  | ||||
|  * @param result  | ||||
| @ -129,23 +157,14 @@ public: | ||||
|  * @param selected_filename  | ||||
|  * @return true if file was selected | ||||
|  */ | ||||
|     bool file_select( | ||||
| bool file_worker_file_select( | ||||
|     FileWorker* file_worker, | ||||
|     const char* path, | ||||
|     const char* extension, | ||||
|     char* result, | ||||
|     uint8_t result_size, | ||||
|     char* selected_filename); | ||||
| 
 | ||||
| private: | ||||
|     File file; | ||||
|     bool silent; | ||||
| 
 | ||||
|     bool check_common_errors(); | ||||
| 
 | ||||
|     void show_error_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); | ||||
| }; | ||||
| #ifdef __cplusplus | ||||
| } | ||||
| #endif | ||||
| @ -2,7 +2,7 @@ | ||||
| #include <forward_list> | ||||
| #include <initializer_list> | ||||
| 
 | ||||
| #define GENERIC_SCENE_ENUM_VALUES Uninitalized, Exit, Start | ||||
| #define GENERIC_SCENE_ENUM_VALUES Exit, Start | ||||
| #define GENERIC_EVENT_ENUM_VALUES Tick, Back | ||||
| 
 | ||||
| /**
 | ||||
| @ -54,35 +54,100 @@ public: | ||||
|      *  | ||||
|      * @param scene_index_list list of scene indexes to which you want to switch | ||||
|      */ | ||||
|     void search_and_switch_to_previous_scene( | ||||
|     bool search_and_switch_to_previous_scene( | ||||
|         const std::initializer_list<typename TApp::SceneType>& scene_index_list) { | ||||
|         auto previous_scene_index = TApp::SceneType::Start; | ||||
|         auto previous_scene_index = TApp::SceneType::Exit; | ||||
|         bool scene_found = false; | ||||
|         bool result = false; | ||||
| 
 | ||||
|         while(!scene_found) { | ||||
|             previous_scene_index = get_previous_scene_index(); | ||||
|             for(const auto& element : scene_index_list) { | ||||
|                 if(previous_scene_index == element) { | ||||
|                     scene_found = true; | ||||
|                     result = true; | ||||
|                     break; | ||||
|                 } | ||||
| 
 | ||||
|                 if(previous_scene_index == TApp::SceneType::Exit) { | ||||
|                     scene_found = true; | ||||
|                     break; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         if(result) { | ||||
|             switch_to_scene(previous_scene_index, true); | ||||
|         } | ||||
| 
 | ||||
|         return result; | ||||
|     } | ||||
| 
 | ||||
|     bool search_and_switch_to_another_scene( | ||||
|         const std::initializer_list<typename TApp::SceneType>& scene_index_list, | ||||
|         typename TApp::SceneType scene_index) { | ||||
|         auto previous_scene_index = TApp::SceneType::Exit; | ||||
|         bool scene_found = false; | ||||
|         bool result = false; | ||||
| 
 | ||||
|         while(!scene_found) { | ||||
|             previous_scene_index = get_previous_scene_index(); | ||||
|             for(const auto& element : scene_index_list) { | ||||
|                 if(previous_scene_index == element) { | ||||
|                     scene_found = true; | ||||
|                     result = true; | ||||
|                     break; | ||||
|                 } | ||||
| 
 | ||||
|                 if(previous_scene_index == TApp::SceneType::Exit) { | ||||
|                     scene_found = true; | ||||
|                     break; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         if(result) { | ||||
|             switch_to_scene(scene_index, true); | ||||
|         } | ||||
| 
 | ||||
|         return result; | ||||
|     } | ||||
| 
 | ||||
|     bool | ||||
|     has_previous_scene(const std::initializer_list<typename TApp::SceneType>& scene_index_list) { | ||||
|         bool result = false; | ||||
| 
 | ||||
|         for(auto const& previous_element : previous_scenes_list) { | ||||
|             for(const auto& element : scene_index_list) { | ||||
|                 if(previous_element == element) { | ||||
|                     result = true; | ||||
|                     break; | ||||
|                 } | ||||
| 
 | ||||
|                 if(previous_element == TApp::SceneType::Exit) { | ||||
|                     break; | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             if(result) break; | ||||
|         } | ||||
| 
 | ||||
|         return result; | ||||
|     } | ||||
| 
 | ||||
|     /**
 | ||||
|      * @brief Start application main cycle | ||||
|      *  | ||||
|      * @param tick_length_ms tick event length in milliseconds | ||||
|      */ | ||||
|     void process(uint32_t tick_length_ms = 100) { | ||||
|     void process( | ||||
|         uint32_t tick_length_ms = 100, | ||||
|         typename TApp::SceneType start_scene_index = TApp::SceneType::Start) { | ||||
|         typename TApp::Event event; | ||||
|         bool consumed; | ||||
|         bool exit = false; | ||||
| 
 | ||||
|         current_scene_index = TApp::SceneType::Start; | ||||
|         current_scene_index = start_scene_index; | ||||
|         scenes[current_scene_index]->on_enter(app, false); | ||||
| 
 | ||||
|         while(!exit) { | ||||
| @ -124,7 +189,7 @@ public: | ||||
|      */ | ||||
|     SceneController(TApp* app_pointer) { | ||||
|         app = app_pointer; | ||||
|         current_scene_index = TApp::SceneType::Uninitalized; | ||||
|         current_scene_index = TApp::SceneType::Exit; | ||||
|     } | ||||
| 
 | ||||
|     /**
 | ||||
|  | ||||
| @ -1,4 +1,5 @@ | ||||
| #include "args.h" | ||||
| #include "hex.h" | ||||
| 
 | ||||
| size_t args_get_first_word_length(string_t args) { | ||||
|     size_t ws = string_search_char(args, ' '); | ||||
| @ -27,28 +28,13 @@ bool args_read_string_and_trim(string_t args, string_t word) { | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| bool args_char_to_hex_nibble(char c, uint8_t* nibble) { | ||||
|     if((c >= '0' && c <= '9') || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f')) { | ||||
|         if((c >= '0' && c <= '9')) { | ||||
|             *nibble = c - '0'; | ||||
|         } else if((c >= 'A' && c <= 'F')) { | ||||
|             *nibble = c - 'A' + 10; | ||||
|         } else { | ||||
|             *nibble = c - 'a' + 10; | ||||
|         } | ||||
|         return true; | ||||
|     } else { | ||||
|         return false; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| bool args_char_to_hex(char hi_nibble, char low_nibble, uint8_t* byte) { | ||||
|     uint8_t hi_nibble_value = 0; | ||||
|     uint8_t low_nibble_value = 0; | ||||
|     bool result = false; | ||||
| 
 | ||||
|     if(args_char_to_hex_nibble(hi_nibble, &hi_nibble_value)) { | ||||
|         if(args_char_to_hex_nibble(low_nibble, &low_nibble_value)) { | ||||
|     if(hex_char_to_hex_nibble(hi_nibble, &hi_nibble_value)) { | ||||
|         if(hex_char_to_hex_nibble(low_nibble, &low_nibble_value)) { | ||||
|             result = true; | ||||
|             *byte = (hi_nibble_value << 4) | low_nibble_value; | ||||
|         } | ||||
|  | ||||
| @ -46,15 +46,6 @@ size_t args_get_first_word_length(string_t args); | ||||
|  */ | ||||
| size_t args_length(string_t args); | ||||
| 
 | ||||
| /**
 | ||||
|  * @brief Convert ASCII hex value to nibble  | ||||
|  *  | ||||
|  * @param c ASCII character | ||||
|  * @param nibble nibble pointer, output | ||||
|  * @return bool conversion status | ||||
|  */ | ||||
| bool args_char_to_hex_nibble(char c, uint8_t* nibble); | ||||
| 
 | ||||
| /**
 | ||||
|  * @brief Convert ASCII hex values to byte | ||||
|  *  | ||||
|  | ||||
							
								
								
									
										16
									
								
								lib/args/hex.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								lib/args/hex.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,16 @@ | ||||
| #include "hex.h" | ||||
| 
 | ||||
| bool hex_char_to_hex_nibble(char c, uint8_t* nibble) { | ||||
|     if((c >= '0' && c <= '9') || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f')) { | ||||
|         if((c >= '0' && c <= '9')) { | ||||
|             *nibble = c - '0'; | ||||
|         } else if((c >= 'A' && c <= 'F')) { | ||||
|             *nibble = c - 'A' + 10; | ||||
|         } else { | ||||
|             *nibble = c - 'a' + 10; | ||||
|         } | ||||
|         return true; | ||||
|     } else { | ||||
|         return false; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										20
									
								
								lib/args/hex.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								lib/args/hex.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,20 @@ | ||||
| #pragma once | ||||
| #include "stdint.h" | ||||
| #include "stdbool.h" | ||||
| 
 | ||||
| #ifdef __cplusplus | ||||
| extern "C" { | ||||
| #endif | ||||
| 
 | ||||
| /**
 | ||||
|  * @brief Convert ASCII hex value to nibble  | ||||
|  *  | ||||
|  * @param c ASCII character | ||||
|  * @param nibble nibble pointer, output | ||||
|  * @return bool conversion status | ||||
|  */ | ||||
| bool hex_char_to_hex_nibble(char c, uint8_t* nibble); | ||||
| 
 | ||||
| #ifdef __cplusplus | ||||
| } | ||||
| #endif | ||||
							
								
								
									
										20
									
								
								lib/args/path.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								lib/args/path.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,20 @@ | ||||
| #include "path.h" | ||||
| 
 | ||||
| void path_extract_filename_no_ext(const char* path, string_t filename) { | ||||
|     string_set(filename, path); | ||||
| 
 | ||||
|     size_t start_position = string_search_rchar(filename, '/'); | ||||
|     size_t end_position = string_search_rchar(filename, '.'); | ||||
| 
 | ||||
|     if(start_position == STRING_FAILURE) { | ||||
|         start_position = 0; | ||||
|     } else { | ||||
|         start_position += 1; | ||||
|     } | ||||
| 
 | ||||
|     if(end_position == STRING_FAILURE) { | ||||
|         end_position = string_size(filename); | ||||
|     } | ||||
| 
 | ||||
|     string_mid(filename, start_position, end_position - start_position); | ||||
| } | ||||
							
								
								
									
										18
									
								
								lib/args/path.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								lib/args/path.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,18 @@ | ||||
| #pragma once | ||||
| #include "m-string.h" | ||||
| 
 | ||||
| #ifdef __cplusplus | ||||
| extern "C" { | ||||
| #endif | ||||
| 
 | ||||
| /**
 | ||||
|  * @brief Extract filename without extension from path. | ||||
|  *  | ||||
|  * @param path path string | ||||
|  * @param filename output filename string. Must be initialized before. | ||||
|  */ | ||||
| void path_extract_filename_no_ext(const char* path, string_t filename); | ||||
| 
 | ||||
| #ifdef __cplusplus | ||||
| } | ||||
| #endif | ||||
| @ -105,5 +105,6 @@ C_SOURCES		+= $(wildcard $(LIB_DIR)/fl_subghz/*/*.c) | ||||
| 
 | ||||
| #scened app template lib
 | ||||
| CFLAGS			+= -I$(LIB_DIR)/app-scened-template | ||||
| C_SOURCES		+= $(wildcard $(LIB_DIR)/app-scened-template/*.c) | ||||
| CPP_SOURCES		+= $(wildcard $(LIB_DIR)/app-scened-template/*.cpp) | ||||
| CPP_SOURCES		+= $(wildcard $(LIB_DIR)/app-scened-template/*/*.cpp) | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 SG
						SG