From 769ab2aef2f73a1bbbb1d9c8e08e9ae64db71576 Mon Sep 17 00:00:00 2001 From: Albert Kharisov Date: Thu, 22 Jul 2021 03:07:00 +0300 Subject: [PATCH] [FL-1489] IRDA: move to FileWorker (#594) * [FL-1489] IRDA: move to FileWorker, fixes * Use FileWorker * Use file_select to select remotes * Fix some crashes * Add RAW parsing restrictions * Remove excess scene (LearnDoneAfter) * Move all file system logic to standalone object --- applications/gui/modules/button_panel.c | 4 + applications/irda/irda-app-brute-force.cpp | 53 ++-- applications/irda/irda-app-brute-force.hpp | 8 +- applications/irda/irda-app-event.hpp | 1 - applications/irda/irda-app-file-parser.cpp | 244 +++++++++++++----- applications/irda/irda-app-file-parser.hpp | 36 ++- applications/irda/irda-app-remote-manager.cpp | 200 ++++---------- applications/irda/irda-app-remote-manager.hpp | 7 +- applications/irda/irda-app-view-manager.cpp | 7 + applications/irda/irda-app-view-manager.hpp | 1 + applications/irda/irda-app.cpp | 14 +- applications/irda/irda-app.hpp | 4 +- .../scene/irda-app-scene-learn-done-after.cpp | 32 --- .../irda/scene/irda-app-scene-learn-done.cpp | 6 +- .../scene/irda-app-scene-learn-success.cpp | 11 +- .../irda/scene/irda-app-scene-remote-list.cpp | 71 +---- .../irda/scene/irda-app-scene-start.cpp | 1 + .../scene/irda-app-scene-universal-common.cpp | 35 +-- applications/irda/scene/irda-app-scene.hpp | 11 +- applications/sd-filesystem/sd-filesystem.c | 13 +- lib/app-scened-template/file-worker-cpp.cpp | 21 +- lib/app-scened-template/file-worker-cpp.h | 45 +++- lib/app-scened-template/file-worker.c | 102 +++++++- lib/app-scened-template/file-worker.h | 51 +++- lib/common-api/sd-card-api.h | 4 +- 25 files changed, 591 insertions(+), 391 deletions(-) delete mode 100644 applications/irda/scene/irda-app-scene-learn-done-after.cpp diff --git a/applications/gui/modules/button_panel.c b/applications/gui/modules/button_panel.c index 472319a7..7bb5cf05 100644 --- a/applications/gui/modules/button_panel.c +++ b/applications/gui/modules/button_panel.c @@ -137,6 +137,10 @@ void button_panel_clean(ButtonPanel* button_panel) { *button_item = NULL; } } + model->reserve_x = 0; + model->reserve_y = 0; + LabelList_clean(model->labels); + ButtonMatrix_clean(model->button_matrix); return true; }); } diff --git a/applications/irda/irda-app-brute-force.cpp b/applications/irda/irda-app-brute-force.cpp index 21491e76..8709dce6 100644 --- a/applications/irda/irda-app-brute-force.cpp +++ b/applications/irda/irda-app-brute-force.cpp @@ -1,4 +1,8 @@ #include "irda-app-brute-force.hpp" +#include "irda/irda-app-file-parser.hpp" +#include "m-string.h" +#include +#include void IrdaAppBruteForce::add_record(int index, const char* name) { records[name].index = index; @@ -7,43 +11,51 @@ void IrdaAppBruteForce::add_record(int index, const char* name) { bool IrdaAppBruteForce::calculate_messages() { bool fs_res = false; - fs_res = file_parser.get_fs_api().file.open( - &file, universal_db_filename, FSAM_READ, FSOM_OPEN_EXISTING); + furi_assert(!file_parser); + + file_parser = std::make_unique(); + fs_res = file_parser->open_irda_file_read(universal_db_filename); if(!fs_res) { - file_parser.get_sd_api().show_error(file_parser.get_sd_api().context, "Can't open file"); + file_parser.reset(nullptr); return false; } - file_parser.reset(); while(1) { - auto message = file_parser.read_signal(&file); - if(!message) break; - auto element = records.find(message->name); + auto file_signal = file_parser->read_signal(); + if(!file_signal) break; + + auto element = records.find(file_signal->name); if(element != records.cend()) { ++element->second.amount; } } - file_parser.get_fs_api().file.close(&file); + file_parser->close(); + file_parser.reset(nullptr); return true; } void IrdaAppBruteForce::stop_bruteforce() { + furi_assert((current_record.size())); + if(current_record.size()) { - file_parser.get_fs_api().file.close(&file); + furi_assert(file_parser); current_record.clear(); + file_parser->close(); + file_parser.reset(nullptr); } } // TODO: [FL-1418] replace with timer-chained consequence of messages. bool IrdaAppBruteForce::send_next_bruteforce(void) { furi_assert(current_record.size()); + furi_assert(file_parser); std::unique_ptr file_signal; do { - file_signal = file_parser.read_signal(&file); + file_signal = file_parser->read_signal(); } while(file_signal && current_record.compare(file_signal->name)); if(file_signal) { @@ -53,25 +65,26 @@ bool IrdaAppBruteForce::send_next_bruteforce(void) { } bool IrdaAppBruteForce::start_bruteforce(int index, int& record_amount) { - file_parser.reset(); + bool result = false; + record_amount = 0; + for(const auto& it : records) { if(it.second.index == index) { record_amount = it.second.amount; - current_record = it.first; + if(record_amount) { + current_record = it.first; + } break; } } if(record_amount) { - bool fs_res = file_parser.get_fs_api().file.open( - &file, universal_db_filename, FSAM_READ, FSOM_OPEN_EXISTING); - if(fs_res) { - return true; - } else { - file_parser.get_sd_api().show_error( - file_parser.get_sd_api().context, "Can't open file"); + file_parser = std::make_unique(); + result = file_parser->open_irda_file_read(universal_db_filename); + if(!result) { + (void)file_parser.reset(nullptr); } } - return false; + return result; } diff --git a/applications/irda/irda-app-brute-force.hpp b/applications/irda/irda-app-brute-force.hpp index 62b8f34e..a7794301 100644 --- a/applications/irda/irda-app-brute-force.hpp +++ b/applications/irda/irda-app-brute-force.hpp @@ -2,13 +2,13 @@ #include "furi/check.h" #include #include "irda-app-file-parser.hpp" - +#include class IrdaAppBruteForce { const char* universal_db_filename; - IrdaAppFileParser file_parser; File file; std::string current_record; + std::unique_ptr file_parser; typedef struct { int index; @@ -30,8 +30,6 @@ public: void add_record(int index, const char* name); IrdaAppBruteForce(const char* filename) : universal_db_filename (filename) {} - ~IrdaAppBruteForce() { - stop_bruteforce(); - } + ~IrdaAppBruteForce() {} }; diff --git a/applications/irda/irda-app-event.hpp b/applications/irda/irda-app-event.hpp index fd0df16c..eb4848fa 100644 --- a/applications/irda/irda-app-event.hpp +++ b/applications/irda/irda-app-event.hpp @@ -15,7 +15,6 @@ public: TextEditDone, PopupTimer, ButtonPanelPressed, - ButtonPanelPopupBackPressed, }; union { diff --git a/applications/irda/irda-app-file-parser.cpp b/applications/irda/irda-app-file-parser.cpp index e0e274e5..288100ae 100644 --- a/applications/irda/irda-app-file-parser.cpp +++ b/applications/irda/irda-app-file-parser.cpp @@ -1,70 +1,139 @@ #include "irda-app-file-parser.hpp" +#include "furi/check.h" #include "irda-app-remote-manager.hpp" #include "irda-app-signal.h" +#include "m-string.h" +#include #include #include #include +#include #include #include +#include uint32_t const IrdaAppFileParser::max_line_length = ((9 + 1) * 512 + 100); +const char* IrdaAppFileParser::irda_directory = "/irda"; +const char* IrdaAppFileParser::irda_extension = ".ir"; +uint32_t const IrdaAppFileParser::max_raw_timings_in_signal = 512; -std::unique_ptr IrdaAppFileParser::read_signal(File* file) { - while(1) { - auto str = getline(file); - if(str.empty()) return nullptr; +bool IrdaAppFileParser::open_irda_file_read(const char* name) { + std::string full_filename; + if(name[0] != '/') + full_filename = make_full_name(name); + else + full_filename = name; - auto message = parse_signal(str); - if(!message.get()) { - message = parse_signal_raw(str); - } - if(message) return message; - } + return file_worker.open(full_filename.c_str(), FSAM_READ, FSOM_OPEN_EXISTING); } -bool IrdaAppFileParser::store_signal(File* file, const IrdaAppSignal& signal, const char* name) { - char* content = new char[max_line_length]; +bool IrdaAppFileParser::open_irda_file_write(const char* name) { + std::string dirname(irda_directory); + auto full_filename = make_full_name(name); + + if(!file_worker.mkdir(dirname.c_str())) return false; + + return file_worker.open(full_filename.c_str(), FSAM_WRITE, FSOM_CREATE_ALWAYS); +} + +size_t IrdaAppFileParser::stringify_message( + const IrdaAppSignal& signal, + const char* name, + char* buf, + size_t buf_size) { + auto message = signal.get_message(); + auto protocol = message.protocol; size_t written = 0; - if(!signal.is_raw()) { - auto message = signal.get_message(); - auto protocol = message.protocol; + written += sniprintf( + buf, + buf_size, + "%.31s %.31s A:%0*lX C:%0*lX\n", + name, + irda_get_protocol_name(protocol), + irda_get_protocol_address_length(protocol), + message.address, + irda_get_protocol_command_length(protocol), + message.command); - sniprintf( - content, - max_line_length, - "%.31s %.31s A:%0*lX C:%0*lX\n", - name, - irda_get_protocol_name(protocol), - irda_get_protocol_address_length(protocol), - message.address, - irda_get_protocol_command_length(protocol), - message.command); - written = strlen(content); - } else { - int duty_cycle = 100 * IRDA_COMMON_DUTY_CYCLE; - written += sniprintf( - &content[written], - max_line_length - written, - "%.31s RAW F:%d DC:%d", - name, - IRDA_COMMON_CARRIER_FREQUENCY, - duty_cycle); - - auto& raw_signal = signal.get_raw_signal(); - for(size_t i = 0; i < raw_signal.timings_cnt; ++i) { - written += sniprintf( - &content[written], max_line_length - written, " %ld", raw_signal.timings[i]); - furi_assert(written <= max_line_length); - } - written += snprintf(&content[written], max_line_length - written, "\n"); + furi_assert(written < buf_size); + if(written >= buf_size) { + written = 0; } - furi_assert(written < max_line_length); - size_t write_count = 0; - write_count = get_fs_api().file.write(file, content, written); - delete[] content; - return (file->error_id == FSE_OK) && (write_count == written); + return written; +} + +size_t IrdaAppFileParser::stringify_raw_signal( + const IrdaAppSignal& signal, + const char* name, + char* buf, + size_t buf_size) { + size_t written = 0; + int duty_cycle = 100 * IRDA_COMMON_DUTY_CYCLE; + written += sniprintf( + &buf[written], + max_line_length - written, + "%.31s RAW F:%d DC:%d", + name, + IRDA_COMMON_CARRIER_FREQUENCY, + duty_cycle); + + auto& raw_signal = signal.get_raw_signal(); + for(size_t i = 0; i < raw_signal.timings_cnt; ++i) { + written += sniprintf(&buf[written], buf_size - written, " %ld", raw_signal.timings[i]); + if(written > buf_size) { + return false; + } + } + written += snprintf(&buf[written], buf_size - written, "\n"); + + furi_assert(written < buf_size); + if(written >= buf_size) { + written = 0; + } + + return written; +} + +bool IrdaAppFileParser::save_signal(const IrdaAppSignal& signal, const char* name) { + char* buf = new char[max_line_length]; + size_t buf_cnt = 0; + bool write_result = false; + + if(signal.is_raw()) { + buf_cnt = stringify_raw_signal(signal, name, buf, max_line_length); + } else { + buf_cnt = stringify_message(signal, name, buf, max_line_length); + } + + if(buf_cnt) { + write_result = file_worker.write(buf, buf_cnt); + } + delete[] buf; + return write_result; +} + +std::unique_ptr IrdaAppFileParser::read_signal(void) { + string_t line; + string_init(line); + string_reserve(line, max_line_length); + std::unique_ptr file_signal; + + while(!file_signal && + file_worker.read_until_buffered(line, file_buf, &file_buf_cnt, sizeof(file_buf))) { + if(string_empty_p(line)) { + continue; + } + auto c_str = string_get_cstr(line); + file_signal = parse_signal(c_str); + if(!file_signal) { + file_signal = parse_signal_raw(c_str); + } + } + string_clear(line); + + return file_signal; } std::unique_ptr @@ -128,7 +197,6 @@ const char* find_first_not_of(const char* str, char symbol) { std::unique_ptr IrdaAppFileParser::parse_signal_raw(const std::string& string) const { - char protocol_name[32]; uint32_t frequency; uint32_t duty_cycle; int str_len = string.size(); @@ -136,14 +204,10 @@ std::unique_ptr auto irda_file_signal = std::make_unique(); int parsed = std::sscanf( - str.data(), - "%31s %31s F:%ld DC:%ld", - irda_file_signal->name, - protocol_name, - &frequency, - &duty_cycle); + str.data(), "%31s RAW F:%ld DC:%ld", irda_file_signal->name, &frequency, &duty_cycle); - if(parsed != 4) { + if((parsed != 3) || (frequency > 42000) || (frequency < 32000) || (duty_cycle == 0) || + (duty_cycle >= 100)) { return nullptr; } @@ -152,9 +216,8 @@ std::unique_ptr header_len = sniprintf( dummy, sizeof(dummy), - "%.31s %.31s F:%ld DC:%ld", + "%.31s RAW F:%ld DC:%ld", irda_file_signal->name, - protocol_name, frequency, duty_cycle); @@ -162,39 +225,94 @@ std::unique_ptr str.remove_prefix(header_len); /* move allocated timings into raw signal object */ - IrdaAppSignal::RawSignal raw_signal = {.timings_cnt = 0, .timings = new uint32_t[500]}; + IrdaAppSignal::RawSignal raw_signal = { + .timings_cnt = 0, .timings = new uint32_t[max_raw_timings_in_signal]}; bool result = false; while(!str.empty()) { char buf[10]; size_t index = str.find_first_not_of(' ', 1); if(index == std::string_view::npos) { - result = true; break; } str.remove_prefix(index); parsed = std::sscanf(str.data(), "%9s", buf); if(parsed != 1) { + result = false; + furi_assert(0); break; } str.remove_prefix(strlen(buf)); int value = atoi(buf); if(value <= 0) { + result = false; + furi_assert(0); break; } raw_signal.timings[raw_signal.timings_cnt] = value; ++raw_signal.timings_cnt; - if(raw_signal.timings_cnt >= 500) { + result = true; + if(raw_signal.timings_cnt >= max_raw_timings_in_signal) { + result = false; + furi_assert(0); break; } } if(result) { - irda_file_signal->signal.set_raw_signal(raw_signal.timings, raw_signal.timings_cnt); + /* copy timings instead of moving them to occupy less than max_raw_timings_in_signal */ + irda_file_signal->signal.copy_raw_signal(raw_signal.timings, raw_signal.timings_cnt); } else { (void)irda_file_signal.release(); - delete[] raw_signal.timings; } + delete[] raw_signal.timings; return irda_file_signal; } + +bool IrdaAppFileParser::is_irda_file_exist(const char* name, bool* exist) { + std::string full_path = make_full_name(name); + return file_worker.is_file_exist(full_path.c_str(), exist); +} + +std::string IrdaAppFileParser::make_full_name(const std::string& remote_name) const { + return std::string("") + irda_directory + "/" + remote_name + irda_extension; +} + +std::string IrdaAppFileParser::make_name(const std::string& full_name) const { + std::string str(full_name, full_name.find_last_of('/') + 1, full_name.size()); + str.erase(str.find_last_of('.')); + + return str; +} + +bool IrdaAppFileParser::remove_irda_file(const char* name) { + std::string full_filename = make_full_name(name); + return file_worker.remove(full_filename.c_str()); +} + +bool IrdaAppFileParser::rename_irda_file(const char* old_name, const char* new_name) { + std::string old_filename = make_full_name(old_name); + std::string new_filename = make_full_name(new_name); + return file_worker.rename(old_filename.c_str(), new_filename.c_str()); +} + +bool IrdaAppFileParser::close() { + return file_worker.close(); +} + +bool IrdaAppFileParser::check_errors() { + return file_worker.check_errors(); +} + +std::string IrdaAppFileParser::file_select(const char* selected) { + TextStore* filename_ts = new TextStore(128); + bool result; + + result = file_worker.file_select( + irda_directory, irda_extension, filename_ts->text, filename_ts->text_size, selected); + + delete filename_ts; + + return result ? std::string(filename_ts->text) : std::string(); +} diff --git a/applications/irda/irda-app-file-parser.hpp b/applications/irda/irda-app-file-parser.hpp index 86a69a72..1195f8c3 100644 --- a/applications/irda/irda-app-file-parser.hpp +++ b/applications/irda/irda-app-file-parser.hpp @@ -1,26 +1,44 @@ #pragma once #include #include -#include "irda-app-remote-manager.hpp" +#include +#include "irda-app-signal.h" -class IrdaAppFileParser : public FileReader { +class IrdaAppFileParser { public: typedef struct { char name[32]; IrdaAppSignal signal; } IrdaFileSignal; - IrdaAppFileParser() { - /* Assume we can save max 512 samples */ - set_max_line_length(max_line_length); - } + bool open_irda_file_read(const char* filename); + bool open_irda_file_write(const char* filename); + bool is_irda_file_exist(const char* filename, bool* exist); + bool rename_irda_file(const char* filename, const char* newname); + bool remove_irda_file(const char* name); + bool close(); + bool check_errors(); - std::unique_ptr read_signal(File* file); - bool store_signal(File* file, const IrdaAppSignal& signal, const char* name); + std::unique_ptr read_signal(); + bool save_signal(const IrdaAppSignal& signal, const char* name); + std::string file_select(const char* selected); + + std::string make_name(const std::string& full_name) const; private: - static const uint32_t max_line_length; + size_t stringify_message(const IrdaAppSignal& signal, const char* name, char* content, size_t content_len); + size_t stringify_raw_signal(const IrdaAppSignal& signal, const char* name, char* content, size_t content_len); std::unique_ptr parse_signal(const std::string& str) const; std::unique_ptr parse_signal_raw(const std::string& str) const; + std::string make_full_name(const std::string& name) const; + + static const char* irda_directory; + static const char* irda_extension; + static const uint32_t max_line_length; + static uint32_t const max_raw_timings_in_signal; + + FileWorkerCpp file_worker; + char file_buf[128]; + size_t file_buf_cnt = 0; }; diff --git a/applications/irda/irda-app-remote-manager.cpp b/applications/irda/irda-app-remote-manager.cpp index aedd39ef..3536b074 100644 --- a/applications/irda/irda-app-remote-manager.cpp +++ b/applications/irda/irda-app-remote-manager.cpp @@ -5,32 +5,29 @@ #include "gui/modules/button_menu.h" #include "irda.h" #include +#include #include #include #include "irda-app-file-parser.hpp" -const char* IrdaAppRemoteManager::irda_directory = "irda"; -const char* IrdaAppRemoteManager::irda_extension = ".ir"; static const std::string default_remote_name = "remote"; -static bool find_string(const std::vector& strings, const std::string& match_string) { - for(const auto& string : strings) { - if(!string.compare(match_string)) return true; - } - return false; -} +std::string IrdaAppRemoteManager::find_vacant_remote_name(const std::string& name) { + IrdaAppFileParser file_parser; + bool exist = true; -static std::string - find_vacant_name(const std::vector& strings, const std::string& name) { - // if suggested name is occupied, try another one (name2, name3, etc) - if(find_string(strings, name)) { - int i = 1; - while(find_string(strings, name + std::to_string(++i))) - ; - return name + std::to_string(i); - } else { + if(!file_parser.is_irda_file_exist(name.c_str(), &exist)) { + return std::string(); + } else if(!exist) { return name; } + + uint32_t i = 1; + /* if suggested name is occupied, try another one (name2, name3, etc) */ + while(file_parser.is_irda_file_exist((name + std::to_string(++i)).c_str(), &exist) && exist) + ; + + return !exist ? name + std::to_string(i) : std::string(); } bool IrdaAppRemoteManager::add_button(const char* button_name, const IrdaAppSignal& signal) { @@ -43,11 +40,10 @@ bool IrdaAppRemoteManager::add_remote_with_button( const IrdaAppSignal& signal) { furi_check(button_name != nullptr); - std::vector remote_list; - bool result = get_remote_list(remote_list); - if(!result) return false; - - auto new_name = find_vacant_name(remote_list, default_remote_name); + auto new_name = find_vacant_remote_name(default_remote_name); + if(new_name.empty()) { + return false; + } remote = std::make_unique(new_name); return add_button(button_name, signal); @@ -73,29 +69,17 @@ const IrdaAppSignal& IrdaAppRemoteManager::get_button_data(size_t index) const { return buttons.at(index).signal; } -std::string IrdaAppRemoteManager::make_full_name(const std::string& remote_name) const { - return std::string("/") + irda_directory + "/" + remote_name + irda_extension; -} - -std::string IrdaAppRemoteManager::make_remote_name(const std::string& full_name) const { - std::string str(full_name, full_name.find_last_of('/') + 1, full_name.size()); - str.erase(str.find_last_of('.')); - - return str; -} - bool IrdaAppRemoteManager::delete_remote() { - FS_Error fs_res; + bool result; IrdaAppFileParser file_parser; - fs_res = file_parser.get_fs_api().common.remove(make_full_name(remote->name).c_str()); - if(fs_res != FSE_OK) { - file_parser.get_sd_api().show_error( - file_parser.get_sd_api().context, "Error deleting file"); - return false; - } + result = file_parser.remove_irda_file(remote->name.c_str()); + reset_remote(); + return result; +} + +void IrdaAppRemoteManager::reset_remote() { remote.reset(); - return true; } bool IrdaAppRemoteManager::delete_button(uint32_t index) { @@ -111,12 +95,11 @@ std::string IrdaAppRemoteManager::get_button_name(uint32_t index) { furi_check(remote.get() != nullptr); auto& buttons = remote->buttons; furi_check(index < buttons.size()); - return buttons[index].name; + return buttons[index].name.c_str(); } std::string IrdaAppRemoteManager::get_remote_name() { - furi_check(remote.get() != nullptr); - return remote->name; + return remote ? remote->name : std::string(); } int IrdaAppRemoteManager::find_remote_name(const std::vector& strings) { @@ -134,22 +117,21 @@ bool IrdaAppRemoteManager::rename_remote(const char* str) { furi_check(str != nullptr); furi_check(remote.get() != nullptr); - if(!remote->name.compare(str)) return true; - - std::vector remote_list; - bool result = get_remote_list(remote_list); - if(!result) return false; - - auto new_name = find_vacant_name(remote_list, str); - IrdaAppFileParser file_parser; - FS_Error fs_err = file_parser.get_fs_api().common.rename( - make_full_name(remote->name).c_str(), make_full_name(new_name).c_str()); - remote->name = new_name; - if(fs_err != FSE_OK) { - file_parser.get_sd_api().show_error( - file_parser.get_sd_api().context, "Error renaming\nremote file"); + if(!remote->name.compare(str)) { + return true; } - return fs_err == FSE_OK; + + auto new_name = find_vacant_remote_name(str); + if(new_name.empty()) { + return false; + } + + IrdaAppFileParser file_parser; + bool result = file_parser.rename_irda_file(remote->name.c_str(), new_name.c_str()); + + remote->name = new_name; + + return result; } bool IrdaAppRemoteManager::rename_button(uint32_t index, const char* str) { @@ -167,115 +149,45 @@ size_t IrdaAppRemoteManager::get_number_of_buttons() { } bool IrdaAppRemoteManager::store(void) { - File file; - std::string dirname(std::string("/") + irda_directory); - IrdaAppFileParser file_parser; - FS_Error fs_err = file_parser.get_fs_api().common.mkdir(dirname.c_str()); - if((fs_err != FSE_OK) && (fs_err != FSE_EXIST)) { - file_parser.get_sd_api().show_error( - file_parser.get_sd_api().context, "Can't create directory"); - return false; - } + bool result = true; - bool res = file_parser.get_fs_api().file.open( - &file, make_full_name(remote->name).c_str(), FSAM_WRITE, FSOM_CREATE_ALWAYS); - - if(!res) { - file_parser.get_sd_api().show_error( - file_parser.get_sd_api().context, "Cannot create\nnew remote file"); + if(!file_parser.open_irda_file_write(remote->name.c_str())) { return false; } for(const auto& button : remote->buttons) { - bool result = file_parser.store_signal(&file, button.signal, button.name.c_str()); + bool result = file_parser.save_signal(button.signal, button.name.c_str()); if(!result) { - file_parser.get_sd_api().show_error( - file_parser.get_sd_api().context, "Cannot write\nto key file"); - file_parser.get_fs_api().file.close(&file); - return false; + result = false; + break; } } - file_parser.get_fs_api().file.close(&file); - file_parser.get_sd_api().check_error(file_parser.get_sd_api().context); + file_parser.close(); - return true; + return result; } -bool IrdaAppRemoteManager::get_remote_list(std::vector& remote_names) const { - bool fs_res = false; - char name[128]; - File dir; - std::string dirname(std::string("/") + irda_directory); - remote_names.clear(); - - IrdaAppFileParser file_parser; - fs_res = file_parser.get_fs_api().dir.open(&dir, dirname.c_str()); - if(!fs_res) { - if(!check_fs()) { - file_parser.get_sd_api().show_error( - file_parser.get_sd_api().context, "Cannot open\napplication directory"); - return false; - } else { - return true; // SD ok, but no files written yet - } - } - - while(file_parser.get_fs_api().dir.read(&dir, nullptr, name, sizeof(name)) && strlen(name)) { - std::string filename(name); - auto extension_index = filename.rfind(irda_extension); - if((extension_index == std::string::npos) || - (extension_index + strlen(irda_extension) != filename.size())) { - continue; - } - remote_names.push_back(filename.erase(extension_index)); - } - file_parser.get_fs_api().dir.close(&dir); - - return true; -} - -bool IrdaAppRemoteManager::load(const std::string& name_arg, bool fullpath) { +bool IrdaAppRemoteManager::load(const std::string& name) { bool fs_res = false; IrdaAppFileParser file_parser; - File file; - std::string full_filename; - std::string remote_name; - if(fullpath) { - full_filename = name_arg; - remote_name = make_remote_name(name_arg); - } else { - full_filename = make_full_name(name_arg); - remote_name = name_arg; - } - - fs_res = file_parser.get_fs_api().file.open( - &file, full_filename.c_str(), FSAM_READ, FSOM_OPEN_EXISTING); + fs_res = file_parser.open_irda_file_read(name.c_str()); if(!fs_res) { - file_parser.get_sd_api().show_error( - file_parser.get_sd_api().context, "Error opening file"); return false; } - remote = std::make_unique(remote_name); + remote = std::make_unique(name); while(1) { - auto file_signal = file_parser.read_signal(&file); - if(!file_signal.get()) break; + auto file_signal = file_parser.read_signal(); + if(!file_signal) { + break; + } remote->buttons.emplace_back(file_signal->name, file_signal->signal); } - file_parser.get_fs_api().file.close(&file); + file_parser.close(); return true; } - -bool IrdaAppRemoteManager::check_fs() const { - // TODO: [FL-1431] Add return value to file_parser.get_sd_api().check_error() and replace get_fs_info(). - IrdaAppFileParser file_parser; - auto fs_err = file_parser.get_fs_api().common.get_fs_info(nullptr, nullptr); - if(fs_err != FSE_OK) - file_parser.get_sd_api().show_error(file_parser.get_sd_api().context, "SD card not found"); - return fs_err == FSE_OK; -} diff --git a/applications/irda/irda-app-remote-manager.hpp b/applications/irda/irda-app-remote-manager.hpp index dd193074..1d835ca7 100644 --- a/applications/irda/irda-app-remote-manager.hpp +++ b/applications/irda/irda-app-remote-manager.hpp @@ -9,7 +9,6 @@ #include #include "irda-app-signal.h" - class IrdaAppRemoteButton { friend class IrdaAppRemoteManager; std::string name; @@ -49,8 +48,8 @@ public: int find_remote_name(const std::vector& strings); bool rename_button(uint32_t index, const char* str); bool rename_remote(const char* str); + std::string find_vacant_remote_name(const std::string& name); - bool get_remote_list(std::vector& remote_names) const; std::vector get_button_list() const; std::string get_button_name(uint32_t index); std::string get_remote_name(); @@ -58,9 +57,9 @@ public: const IrdaAppSignal& get_button_data(size_t index) const; bool delete_button(uint32_t index); bool delete_remote(); + void reset_remote(); bool store(); - bool load(const std::string& name, bool fullpath = false); - bool check_fs() const; + bool load(const std::string& name); }; diff --git a/applications/irda/irda-app-view-manager.cpp b/applications/irda/irda-app-view-manager.cpp index 822b66ab..cc2371f7 100644 --- a/applications/irda/irda-app-view-manager.cpp +++ b/applications/irda/irda-app-view-manager.cpp @@ -1,6 +1,7 @@ #include "furi.h" #include "gui/modules/button_panel.h" #include "irda-app.hpp" +#include "irda/irda-app-event.hpp" #include IrdaAppViewManager::IrdaAppViewManager() { @@ -98,6 +99,12 @@ osMessageQueueId_t IrdaAppViewManager::get_event_queue() { return event_queue; } +void IrdaAppViewManager::clear_events() { + IrdaAppEvent event; + while(osMessageQueueGet(event_queue, &event, NULL, 0) == osOK) + ; +} + void IrdaAppViewManager::receive_event(IrdaAppEvent* event) { if(osMessageQueueGet(event_queue, event, NULL, 100) != osOK) { event->type = IrdaAppEvent::Type::Tick; diff --git a/applications/irda/irda-app-view-manager.hpp b/applications/irda/irda-app-view-manager.hpp index 90511acd..4569a104 100644 --- a/applications/irda/irda-app-view-manager.hpp +++ b/applications/irda/irda-app-view-manager.hpp @@ -28,6 +28,7 @@ public: void receive_event(IrdaAppEvent* event); void send_event(IrdaAppEvent* event); + void clear_events(); DialogEx* get_dialog_ex(); Submenu* get_submenu(); diff --git a/applications/irda/irda-app.cpp b/applications/irda/irda-app.cpp index 29e1d281..1e2de687 100644 --- a/applications/irda/irda-app.cpp +++ b/applications/irda/irda-app.cpp @@ -1,4 +1,5 @@ #include "irda-app.hpp" +#include "irda/irda-app-file-parser.hpp" #include #include #include @@ -12,12 +13,16 @@ int32_t IrdaApp::run(void* args) { bool exit = false; if(args) { - const char* remote_name = static_cast(args); - bool result = remote_manager.load(std::string(remote_name), true); + std::string remote_name; + { + IrdaAppFileParser file_parser; + remote_name = file_parser.make_name(static_cast(args)); + } + bool result = remote_manager.load(remote_name); if(result) { current_scene = IrdaApp::Scene::Remote; } else { - printf("Failed to load remote \'%s\'\r\n", remote_name); + printf("Failed to load remote \'%s\'\r\n", remote_name.c_str()); return -1; } } @@ -65,6 +70,7 @@ void IrdaApp::switch_to_next_scene_without_saving(Scene next_scene) { scenes[current_scene]->on_exit(this); current_scene = next_scene; scenes[current_scene]->on_enter(this); + view_manager.clear_events(); } } @@ -93,6 +99,7 @@ void IrdaApp::search_and_switch_to_previous_scene(const std::initializer_liston_exit(this); current_scene = previous_scene; scenes[current_scene]->on_enter(this); + view_manager.clear_events(); } } @@ -106,6 +113,7 @@ bool IrdaApp::switch_to_previous_scene(uint8_t count) { scenes[current_scene]->on_exit(this); current_scene = previous_scene; scenes[current_scene]->on_enter(this); + view_manager.clear_events(); return false; } diff --git a/applications/irda/irda-app.hpp b/applications/irda/irda-app.hpp index 31637e7a..0562aefb 100644 --- a/applications/irda/irda-app.hpp +++ b/applications/irda/irda-app.hpp @@ -2,7 +2,7 @@ #include #include #include -#include "irda/scene/irda-app-scene.hpp" +#include "scene/irda-app-scene.hpp" #include "irda-app-event.hpp" #include "scene/irda-app-scene.hpp" #include "irda-app-view-manager.hpp" @@ -34,7 +34,6 @@ public: LearnSuccess, LearnEnterName, LearnDone, - LearnDoneAfter, Remote, RemoteList, Edit, @@ -126,7 +125,6 @@ private: {Scene::LearnSuccess, new IrdaAppSceneLearnSuccess()}, {Scene::LearnEnterName, new IrdaAppSceneLearnEnterName()}, {Scene::LearnDone, new IrdaAppSceneLearnDone()}, - {Scene::LearnDoneAfter, new IrdaAppSceneLearnDoneAfter()}, {Scene::Remote, new IrdaAppSceneRemote()}, {Scene::RemoteList, new IrdaAppSceneRemoteList()}, {Scene::Edit, new IrdaAppSceneEdit()}, diff --git a/applications/irda/scene/irda-app-scene-learn-done-after.cpp b/applications/irda/scene/irda-app-scene-learn-done-after.cpp deleted file mode 100644 index b7ff57d6..00000000 --- a/applications/irda/scene/irda-app-scene-learn-done-after.cpp +++ /dev/null @@ -1,32 +0,0 @@ -#include "../irda-app.hpp" -#include - -void IrdaAppSceneLearnDoneAfter::on_enter(IrdaApp* app) { - auto view_manager = app->get_view_manager(); - auto popup = view_manager->get_popup(); - - popup_set_icon(popup, 0, 30, &I_IrdaSendShort_128x34); - popup_set_text( - popup, "Get ready!\nPoint flipper at target.", 64, 16, AlignCenter, AlignCenter); - - popup_set_callback(popup, IrdaApp::popup_callback); - popup_set_context(popup, app); - popup_set_timeout(popup, 1500); - popup_enable_timeout(popup); - - view_manager->switch_to(IrdaAppViewManager::ViewType::Popup); -} - -bool IrdaAppSceneLearnDoneAfter::on_event(IrdaApp* app, IrdaAppEvent* event) { - bool consumed = false; - - if(event->type == IrdaAppEvent::Type::PopupTimer) { - app->switch_to_next_scene(IrdaApp::Scene::Remote); - consumed = true; - } - - return consumed; -} - -void IrdaAppSceneLearnDoneAfter::on_exit(IrdaApp* app) { -} diff --git a/applications/irda/scene/irda-app-scene-learn-done.cpp b/applications/irda/scene/irda-app-scene-learn-done.cpp index f411af5d..a9bd2049 100644 --- a/applications/irda/scene/irda-app-scene-learn-done.cpp +++ b/applications/irda/scene/irda-app-scene-learn-done.cpp @@ -24,11 +24,7 @@ bool IrdaAppSceneLearnDone::on_event(IrdaApp* app, IrdaAppEvent* event) { bool consumed = false; if(event->type == IrdaAppEvent::Type::PopupTimer) { - if(app->get_learn_new_remote()) { - app->switch_to_next_scene(IrdaApp::Scene::LearnDoneAfter); - } else { - app->switch_to_next_scene(IrdaApp::Scene::Remote); - } + app->switch_to_next_scene(IrdaApp::Scene::Remote); consumed = true; } diff --git a/applications/irda/scene/irda-app-scene-learn-success.cpp b/applications/irda/scene/irda-app-scene-learn-success.cpp index 74e8db74..4ade76df 100644 --- a/applications/irda/scene/irda-app-scene-learn-success.cpp +++ b/applications/irda/scene/irda-app-scene-learn-success.cpp @@ -1,5 +1,7 @@ #include "../irda-app.hpp" #include "irda.h" +#include "../irda-app-file-parser.hpp" +#include static void dialog_result_callback(DialogExResult result, void* context) { auto app = static_cast(context); @@ -61,15 +63,18 @@ bool IrdaAppSceneLearnSuccess::on_event(IrdaApp* app, IrdaAppEvent* event) { signal.transmit(); break; } - case DialogExResultRight: - auto remote_manager = app->get_remote_manager(); - if(remote_manager->check_fs()) { + case DialogExResultRight: { + IrdaAppFileParser file_parser; + if(file_parser.check_errors()) { app->switch_to_next_scene(IrdaApp::Scene::LearnEnterName); } else { app->switch_to_previous_scene(); } break; } + default: + break; + } } return consumed; diff --git a/applications/irda/scene/irda-app-scene-remote-list.cpp b/applications/irda/scene/irda-app-scene-remote-list.cpp index aacc537f..d2b56e48 100644 --- a/applications/irda/scene/irda-app-scene-remote-list.cpp +++ b/applications/irda/scene/irda-app-scene-remote-list.cpp @@ -1,75 +1,30 @@ #include "../irda-app.hpp" - -typedef enum { - SubmenuIndexPlus = -1, -} SubmenuIndex; - -static void submenu_callback(void* context, uint32_t index) { - IrdaApp* app = static_cast(context); - IrdaAppEvent event; - - event.type = IrdaAppEvent::Type::MenuSelected; - event.payload.menu_index = index; - - app->get_view_manager()->send_event(&event); -} +#include "irda/irda-app-event.hpp" void IrdaAppSceneRemoteList::on_enter(IrdaApp* app) { - IrdaAppViewManager* view_manager = app->get_view_manager(); - Submenu* submenu = view_manager->get_submenu(); + IrdaAppFileParser file_parser; + bool success = false; auto remote_manager = app->get_remote_manager(); - int i = 0; + auto last_selected_remote = remote_manager->get_remote_name(); + auto selected_file = file_parser.file_select( + last_selected_remote.size() ? last_selected_remote.c_str() : nullptr); + if(!selected_file.empty()) { + if(remote_manager->load(selected_file)) { + app->switch_to_next_scene(IrdaApp::Scene::Remote); + success = true; + } + } - bool result = remote_manager->get_remote_list(remote_names); - if(!result) { + if(!success) { app->switch_to_previous_scene(); - return; } - - for(auto& name : remote_names) { - submenu_add_item(submenu, name.c_str(), i++, submenu_callback, app); - } - submenu_add_item( - submenu, " +", SubmenuIndexPlus, submenu_callback, app); - - if((SubmenuIndex)submenu_item_selected == SubmenuIndexPlus) { - submenu_set_selected_item(submenu, submenu_item_selected); - } else { - int remote_index = remote_manager->find_remote_name(remote_names); - submenu_set_selected_item(submenu, (remote_index >= 0) ? remote_index : 0); - } - - submenu_item_selected = 0; - view_manager->switch_to(IrdaAppViewManager::ViewType::Submenu); } bool IrdaAppSceneRemoteList::on_event(IrdaApp* app, IrdaAppEvent* event) { bool consumed = false; - if(event->type == IrdaAppEvent::Type::MenuSelected) { - switch(event->payload.menu_index) { - case SubmenuIndexPlus: - app->set_learn_new_remote(true); - app->switch_to_next_scene(IrdaApp::Scene::Learn); - submenu_item_selected = event->payload.menu_index; - break; - default: - auto remote_manager = app->get_remote_manager(); - bool result = remote_manager->load(remote_names.at(event->payload.menu_index)); - if(result) { - app->switch_to_next_scene(IrdaApp::Scene::Remote); - } - consumed = true; - break; - } - } - return consumed; } void IrdaAppSceneRemoteList::on_exit(IrdaApp* app) { - IrdaAppViewManager* view_manager = app->get_view_manager(); - Submenu* submenu = view_manager->get_submenu(); - - submenu_clean(submenu); } diff --git a/applications/irda/scene/irda-app-scene-start.cpp b/applications/irda/scene/irda-app-scene-start.cpp index 27f40ad3..6d9e77f8 100644 --- a/applications/irda/scene/irda-app-scene-start.cpp +++ b/applications/irda/scene/irda-app-scene-start.cpp @@ -61,5 +61,6 @@ void IrdaAppSceneStart::on_exit(IrdaApp* app) { IrdaAppViewManager* view_manager = app->get_view_manager(); Submenu* submenu = view_manager->get_submenu(); + app->get_remote_manager()->reset_remote(); submenu_clean(submenu); } diff --git a/applications/irda/scene/irda-app-scene-universal-common.cpp b/applications/irda/scene/irda-app-scene-universal-common.cpp index 53c8a246..88e7cf34 100644 --- a/applications/irda/scene/irda-app-scene-universal-common.cpp +++ b/applications/irda/scene/irda-app-scene-universal-common.cpp @@ -28,7 +28,7 @@ static bool irda_popup_brut_input_callback(InputEvent* event, void* context) { consumed = true; IrdaAppEvent irda_event; - irda_event.type = IrdaAppEvent::Type::ButtonPanelPopupBackPressed; + irda_event.type = IrdaAppEvent::Type::Back; app->get_view_manager()->send_event(&irda_event); } @@ -58,8 +58,8 @@ void IrdaAppSceneUniversalCommon::progress_popup(IrdaApp* app) { bool IrdaAppSceneUniversalCommon::on_event(IrdaApp* app, IrdaAppEvent* event) { bool consumed = false; - if(event->type == IrdaAppEvent::Type::Tick) { - if(brute_force_started) { + if(brute_force_started) { + if(event->type == IrdaAppEvent::Type::Tick) { auto view_manager = app->get_view_manager(); IrdaAppEvent tick_event = {.type = IrdaAppEvent::Type::Tick}; view_manager->send_event(&tick_event); @@ -70,26 +70,27 @@ bool IrdaAppSceneUniversalCommon::on_event(IrdaApp* app, IrdaAppEvent* event) { brute_force_started = false; remove_popup(app); } + consumed = true; + } else if(event->type == IrdaAppEvent::Type::Back) { + brute_force_started = false; + brute_force.stop_bruteforce(); + remove_popup(app); + consumed = true; } - consumed = true; - } - - if(event->type == IrdaAppEvent::Type::ButtonPanelPopupBackPressed) { - consumed = true; - brute_force_started = false; - brute_force.stop_bruteforce(); - remove_popup(app); - } else if(event->type == IrdaAppEvent::Type::ButtonPanelPressed) { - int record_amount = 0; - if(brute_force.start_bruteforce(event->payload.menu_index, record_amount)) { - if(record_amount > 0) { + } else { + if(event->type == IrdaAppEvent::Type::ButtonPanelPressed) { + int record_amount = 0; + if(brute_force.start_bruteforce(event->payload.menu_index, record_amount)) { brute_force_started = true; show_popup(app, record_amount); + } else { + app->switch_to_previous_scene(); } - } else { + consumed = true; + } else if(event->type == IrdaAppEvent::Type::Back) { app->switch_to_previous_scene(); + consumed = true; } - consumed = true; } return consumed; diff --git a/applications/irda/scene/irda-app-scene.hpp b/applications/irda/scene/irda-app-scene.hpp index 524c0671..a90f5f49 100644 --- a/applications/irda/scene/irda-app-scene.hpp +++ b/applications/irda/scene/irda-app-scene.hpp @@ -65,13 +65,6 @@ public: void on_exit(IrdaApp* app) final; }; -class IrdaAppSceneLearnDoneAfter : public IrdaAppScene { -public: - void on_enter(IrdaApp* app) final; - bool on_event(IrdaApp* app, IrdaAppEvent* event) final; - void on_exit(IrdaApp* app) final; -}; - class IrdaAppSceneRemote : public IrdaAppScene { public: void on_enter(IrdaApp* app) final; @@ -155,14 +148,14 @@ protected: class IrdaAppSceneUniversalTV : public IrdaAppSceneUniversalCommon { public: void on_enter(IrdaApp* app) final; - IrdaAppSceneUniversalTV() : IrdaAppSceneUniversalCommon("/irda/universal/tv.ir") {} + IrdaAppSceneUniversalTV() : IrdaAppSceneUniversalCommon("/assets/ext/irda/tv.ir") {} ~IrdaAppSceneUniversalTV() {} }; class IrdaAppSceneUniversalAudio : public IrdaAppSceneUniversalCommon { public: void on_enter(IrdaApp* app) final; - IrdaAppSceneUniversalAudio() : IrdaAppSceneUniversalCommon("/irda/universal/audio.ir") {} + IrdaAppSceneUniversalAudio() : IrdaAppSceneUniversalCommon("/assets/ext/irda/audio.ir") {} ~IrdaAppSceneUniversalAudio() {} }; diff --git a/applications/sd-filesystem/sd-filesystem.c b/applications/sd-filesystem/sd-filesystem.c index 9c9d273b..124c796c 100644 --- a/applications/sd-filesystem/sd-filesystem.c +++ b/applications/sd-filesystem/sd-filesystem.c @@ -42,7 +42,7 @@ typedef struct { const char* extension; char* result; uint8_t result_size; - char* selected_filename; + const char* selected_filename; } SdAppFileSelectData; typedef struct { @@ -64,7 +64,7 @@ bool sd_api_file_select( const char* extension, char* result, uint8_t result_size, - char* selected_filename); + const char* selected_filename); void sd_api_check_error(SdApp* sd_app); void sd_api_show_error(SdApp* sd_app, const char* error_text); @@ -435,7 +435,7 @@ bool sd_api_file_select( const char* extension, char* result, uint8_t result_size, - char* selected_filename) { + const char* selected_filename) { bool retval = false; SdAppEvent message = { @@ -629,7 +629,12 @@ void free_view_holder(SdApp* sd_app) { } void app_reset_state(SdApp* sd_app) { - view_holder_stop(sd_app->view_holder); + _fs_lock(&sd_app->info); + if(sd_app->view_holder) { + view_holder_stop(sd_app->view_holder); + } + _fs_unlock(&sd_app->info); + free_view_holder(sd_app); string_set_str(sd_app->text_holder, ""); sd_app->sd_app_state = SdAppStateBackground; diff --git a/lib/app-scened-template/file-worker-cpp.cpp b/lib/app-scened-template/file-worker-cpp.cpp index 22912002..616cb30c 100644 --- a/lib/app-scened-template/file-worker-cpp.cpp +++ b/lib/app-scened-template/file-worker-cpp.cpp @@ -62,7 +62,24 @@ bool FileWorkerCpp::file_select( const char* extension, char* result, uint8_t result_size, - char* selected_filename) { + const char* selected_filename) { return file_worker_file_select( file_worker, path, extension, result, result_size, selected_filename); -} \ No newline at end of file +} + +bool FileWorkerCpp::read_until_buffered(string_t str_result, char* file_buf, size_t* file_buf_cnt, size_t max_length, char separator) { + return file_worker_read_until_buffered(file_worker, str_result, file_buf, file_buf_cnt, max_length, separator); +} + +bool FileWorkerCpp::is_file_exist(const char* filename, bool* exist) { + return file_worker_is_file_exist(file_worker, filename, exist); +} + +bool FileWorkerCpp::rename(const char* old_path, const char* new_path) { + return file_worker_rename(file_worker, old_path, new_path); +} + +bool FileWorkerCpp::check_errors() { + return file_worker_check_errors(file_worker); +} + diff --git a/lib/app-scened-template/file-worker-cpp.h b/lib/app-scened-template/file-worker-cpp.h index b7a6f7e0..6b5b732b 100644 --- a/lib/app-scened-template/file-worker-cpp.h +++ b/lib/app-scened-template/file-worker-cpp.h @@ -128,7 +128,50 @@ public: const char* extension, char* result, uint8_t result_size, - char* selected_filename); + const char* selected_filename); + + /** + * @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 included in the result. + * + * @param result + * @param file_buf + * @param file_buf_cnt + * @param max_length + * @param separator + * @return true on success + */ + bool read_until_buffered(string_t str_result, char* file_buf, size_t* file_buf_cnt, size_t max_length, char separator = '\n'); + + /** + * @brief Check whether file exist or not + * + * @param file_worker FileWorker instance + * @param filename + * @param exist - flag to show file exist + * @return true on success + */ + bool is_file_exist( + const char* filename, + bool* exist); + + /** + * @brief Rename file or directory + * + * @param old_filename + * @param new_filename + * @return true on success + */ + bool rename( + const char* old_path, + const char* new_path); + + /** + * @brief Check errors + * + * @return true if no errors + */ + bool check_errors(); private: FileWorker* file_worker; diff --git a/lib/app-scened-template/file-worker.c b/lib/app-scened-template/file-worker.c index c85f751a..674ddab8 100644 --- a/lib/app-scened-template/file-worker.c +++ b/lib/app-scened-template/file-worker.c @@ -1,4 +1,5 @@ #include "file-worker.h" +#include "m-string.h" #include #include #include @@ -8,6 +9,8 @@ struct FileWorker { SdCard_Api* sd_ex_api; bool silent; File file; + char file_buf[48]; + size_t file_buf_cnt; }; bool file_worker_check_common_errors(FileWorker* file_worker); @@ -25,6 +28,7 @@ FileWorker* file_worker_alloc(bool _silent) { file_worker->silent = _silent; file_worker->fs_api = furi_record_open("sdcard"); file_worker->sd_ex_api = furi_record_open("sdcard-ex"); + file_worker->file_buf_cnt = 0; return file_worker; } @@ -215,14 +219,17 @@ bool file_worker_file_select( const char* extension, char* result, uint8_t result_size, - char* selected_filename) { + const 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; + /* TODO: [FL-1431] Add return value to file_parser.get_sd_api().check_error() and replace get_fs_info(). */ + FS_Error fs_err = file_worker->fs_api->common.get_fs_info(NULL, NULL); + if(fs_err != FSE_OK) + file_worker->sd_ex_api->show_error(file_worker->sd_ex_api->context, "SD card not found"); + return fs_err == FSE_OK; } void file_worker_show_error_internal(FileWorker* file_worker, const char* error_text) { @@ -277,4 +284,91 @@ bool file_worker_seek_internal(FileWorker* file_worker, uint64_t position, bool } return true; -} \ No newline at end of file +} + +bool file_worker_read_until_buffered(FileWorker* file_worker, string_t str_result, char* file_buf, size_t* file_buf_cnt, size_t file_buf_size, char separator) { + furi_assert(string_capacity(str_result) > 0); + furi_assert(file_buf_size <= 512); /* fs_api->file.read now supports up to 512 bytes reading at a time */ + + string_clean(str_result); + size_t newline_index = 0; + bool found_eol = false; + bool max_length_exceeded = false; + size_t max_length = string_capacity(str_result) - 1; + + while(1) { + if(*file_buf_cnt > 0) { + size_t end_index = 0; + char* endline_ptr = (char*)memchr(file_buf, separator, *file_buf_cnt); + newline_index = endline_ptr - file_buf; + + if(endline_ptr == 0) { + end_index = *file_buf_cnt; + } else if(newline_index < *file_buf_cnt) { + end_index = newline_index + 1; + found_eol = true; + } else { + furi_assert(0); + } + + if (max_length && (string_size(str_result) + end_index > max_length)) + max_length_exceeded = true; + + if (!max_length_exceeded) { + for (size_t i = 0; i < end_index; ++i) { + string_push_back(str_result, file_buf[i]); + } + } + + memmove(file_buf, &file_buf[end_index], *file_buf_cnt - end_index); + *file_buf_cnt = *file_buf_cnt - end_index; + if(found_eol) break; + } + + *file_buf_cnt += + file_worker->fs_api->file.read(&file_worker->file, &file_buf[*file_buf_cnt], file_buf_size - *file_buf_cnt); + if(file_worker->file.error_id != FSE_OK) { + file_worker_show_error_internal(file_worker, "Cannot read\nfile"); + string_clear(str_result); + *file_buf_cnt = 0; + break; + } + if(*file_buf_cnt == 0) { + break; // end of reading + } + } + + if (max_length_exceeded) + string_clear(str_result); + + return string_size(str_result) || *file_buf_cnt; +} + +bool file_worker_rename(FileWorker* file_worker, const char* old_path, const char* new_path) { + FS_Error fs_result = file_worker->fs_api->common.rename(old_path, new_path); + + if(fs_result != FSE_OK && fs_result != FSE_EXIST) { + file_worker_show_error_internal(file_worker, "Cannot rename\n file/directory"); + return false; + } + + return file_worker_check_common_errors(file_worker); +} + +bool file_worker_check_errors(FileWorker* file_worker) { + return file_worker_check_common_errors(file_worker); +} + +bool file_worker_is_file_exist( + FileWorker* file_worker, + const char* filename, + bool* exist) { + + File file; + *exist = file_worker->fs_api->file.open(&file, filename, FSAM_READ, FSOM_OPEN_EXISTING); + if (*exist) + file_worker->fs_api->file.close(&file); + + return file_worker_check_common_errors(file_worker); +} + diff --git a/lib/app-scened-template/file-worker.h b/lib/app-scened-template/file-worker.h index 4831743b..48c1c483 100644 --- a/lib/app-scened-template/file-worker.h +++ b/lib/app-scened-template/file-worker.h @@ -163,8 +163,55 @@ bool file_worker_file_select( const char* extension, char* result, uint8_t result_size, - char* selected_filename); + const char* selected_filename); + +/** + * @brief Reads data from a file until separator or EOF is found. + * The separator is included in the result. + * + * @param file_worker FileWorker instance + * @param str_result + * @param file_buf + * @param file_buf_cnt + * @param max_length + * @param separator + * @return true on success + */ +bool file_worker_read_until_buffered(FileWorker* file_worker, string_t str_result, char* file_buf, size_t* file_buf_cnt, size_t max_length, char separator); + +/** + * @brief Check whether file exist or not + * + * @param file_worker FileWorker instance + * @param filename + * @param exist - flag to show file exist + * @return true on success + */ +bool file_worker_is_file_exist( + FileWorker* file_worker, + const char* filename, + bool* exist); + +/** + * @brief Rename file or directory + * + * @param file_worker FileWorker instance + * @param old_filename + * @param new_filename + * @return true on success + */ +bool file_worker_rename(FileWorker* file_worker, + const char* old_path, + const char* new_path); + +/** + * @brief Check errors + * + * @param file_worker FileWorker instance + * @return true on success + */ +bool file_worker_check_errors(FileWorker* file_worker); #ifdef __cplusplus } -#endif \ No newline at end of file +#endif diff --git a/lib/common-api/sd-card-api.h b/lib/common-api/sd-card-api.h index c4b312eb..d7bd611d 100644 --- a/lib/common-api/sd-card-api.h +++ b/lib/common-api/sd-card-api.h @@ -15,11 +15,11 @@ typedef struct { const char* extension, char* result, uint8_t result_size, - char* selected_filename); + const char* selected_filename); void (*check_error)(SdApp* context); void (*show_error)(SdApp* context, const char* error_text); } SdCard_Api; #ifdef __cplusplus } -#endif \ No newline at end of file +#endif