diff --git a/applications/applications.c b/applications/applications.c index 71fb513bd..c932f86ce 100644 --- a/applications/applications.c +++ b/applications/applications.c @@ -440,6 +440,14 @@ const FlipperApplication FLIPPER_DEBUG_APPS[] = { .flags = FlipperApplicationFlagDefault}, #endif +#ifdef APP_SCENED + {.app = scened_app, + .name = "Templated Scene", + .stack_size = 1024, + .icon = NULL, + .flags = FlipperApplicationFlagDefault}, +#endif + #ifdef APP_LF_RFID {.app = lfrfid_debug_app, .name = "LF-RFID Debug", diff --git a/applications/bad_usb/bad_usb_app.c b/applications/bad_usb/bad_usb_app.c index 65ccc575b..eb647e004 100644 --- a/applications/bad_usb/bad_usb_app.c +++ b/applications/bad_usb/bad_usb_app.c @@ -1,5 +1,4 @@ #include "bad_usb_app_i.h" -#include "m-string.h" #include #include #include @@ -23,13 +22,33 @@ static void bad_usb_app_tick_event_callback(void* context) { scene_manager_handle_tick_event(app->scene_manager); } +static bool bad_usb_check_assets() { + Storage* fs_api = furi_record_open("storage"); + + File* dir = storage_file_alloc(fs_api); + bool ret = false; + + if(storage_dir_open(dir, BAD_USB_APP_PATH_FOLDER)) { + ret = true; + } + + storage_dir_close(dir); + storage_file_free(dir); + + furi_record_close("storage"); + + return ret; +} + BadUsbApp* bad_usb_app_alloc(char* arg) { BadUsbApp* app = malloc(sizeof(BadUsbApp)); - string_init(app->file_path); - if(arg != NULL) { - string_set_str(app->file_path, arg); + string_t filename; + string_init(filename); + path_extract_filename_no_ext(arg, filename); + strncpy(app->file_name, string_get_cstr(filename), BAD_USB_FILE_NAME_LEN); + string_clear(filename); } app->gui = furi_record_open("gui"); @@ -64,11 +83,13 @@ BadUsbApp* bad_usb_app_alloc(char* arg) { app->error = BadUsbAppErrorCloseRpc; scene_manager_next_scene(app->scene_manager, BadUsbSceneError); } else { - if(!string_empty_p(app->file_path)) { + if(*app->file_name != '\0') { scene_manager_next_scene(app->scene_manager, BadUsbSceneWork); - } else { - string_set_str(app->file_path, BAD_USB_APP_PATH_FOLDER); + } else if(bad_usb_check_assets()) { scene_manager_next_scene(app->scene_manager, BadUsbSceneFileSelect); + } else { + app->error = BadUsbAppErrorNoFiles; + scene_manager_next_scene(app->scene_manager, BadUsbSceneError); } } @@ -96,8 +117,6 @@ void bad_usb_app_free(BadUsbApp* app) { furi_record_close("notification"); furi_record_close("dialogs"); - string_clear(app->file_path); - free(app); } diff --git a/applications/bad_usb/bad_usb_app_i.h b/applications/bad_usb/bad_usb_app_i.h index c82419e0f..67f5816b8 100644 --- a/applications/bad_usb/bad_usb_app_i.h +++ b/applications/bad_usb/bad_usb_app_i.h @@ -16,6 +16,7 @@ #define BAD_USB_APP_PATH_FOLDER "/any/badusb" #define BAD_USB_APP_EXTENSION ".txt" +#define BAD_USB_FILE_NAME_LEN 40 typedef enum { BadUsbAppErrorNoFiles, @@ -31,7 +32,7 @@ struct BadUsbApp { Widget* widget; BadUsbAppError error; - string_t file_path; + char file_name[BAD_USB_FILE_NAME_LEN + 1]; BadUsb* bad_usb_view; BadUsbScript* bad_usb_script; }; diff --git a/applications/bad_usb/scenes/bad_usb_scene_file_select.c b/applications/bad_usb/scenes/bad_usb_scene_file_select.c index 1e6ba895a..82f03bab0 100644 --- a/applications/bad_usb/scenes/bad_usb_scene_file_select.c +++ b/applications/bad_usb/scenes/bad_usb_scene_file_select.c @@ -5,16 +5,14 @@ static bool bad_usb_file_select(BadUsbApp* bad_usb) { furi_assert(bad_usb); - // Input events and views are managed by file_browser - bool res = dialog_file_browser_show( + // Input events and views are managed by file_select + bool res = dialog_file_select_show( bad_usb->dialogs, - bad_usb->file_path, - bad_usb->file_path, + BAD_USB_APP_PATH_FOLDER, BAD_USB_APP_EXTENSION, - true, - &I_badusb_10px, - true); - + bad_usb->file_name, + sizeof(bad_usb->file_name), + NULL); return res; } diff --git a/applications/bad_usb/scenes/bad_usb_scene_work.c b/applications/bad_usb/scenes/bad_usb_scene_work.c index 516cbde3a..a3a46803a 100644 --- a/applications/bad_usb/scenes/bad_usb_scene_work.c +++ b/applications/bad_usb/scenes/bad_usb_scene_work.c @@ -2,8 +2,6 @@ #include "../bad_usb_app_i.h" #include "../views/bad_usb_view.h" #include "furi_hal.h" -#include "m-string.h" -#include "toolbox/path.h" void bad_usb_scene_work_ok_callback(InputType type, void* context) { furi_assert(context); @@ -30,9 +28,10 @@ void bad_usb_scene_work_on_enter(void* context) { string_t file_name; string_init(file_name); - path_extract_filename(app->file_path, file_name, true); - bad_usb_set_file_name(app->bad_usb_view, string_get_cstr(file_name)); - app->bad_usb_script = bad_usb_script_open(app->file_path); + bad_usb_set_file_name(app->bad_usb_view, app->file_name); + string_printf( + file_name, "%s/%s%s", BAD_USB_APP_PATH_FOLDER, app->file_name, BAD_USB_APP_EXTENSION); + app->bad_usb_script = bad_usb_script_open(file_name); string_clear(file_name); diff --git a/applications/bad_usb/views/bad_usb_view.c b/applications/bad_usb/views/bad_usb_view.c index 430885df3..5b6fe6e89 100644 --- a/applications/bad_usb/views/bad_usb_view.c +++ b/applications/bad_usb/views/bad_usb_view.c @@ -2,8 +2,6 @@ #include "../bad_usb_script.h" #include -#define MAX_NAME_LEN 64 - struct BadUsb { View* view; BadUsbOkCallback callback; @@ -11,7 +9,7 @@ struct BadUsb { }; typedef struct { - char file_name[MAX_NAME_LEN]; + char* file_name; BadUsbState state; uint8_t anim_frame; } BadUsbModel; @@ -151,11 +149,11 @@ void bad_usb_set_ok_callback(BadUsb* bad_usb, BadUsbOkCallback callback, void* c }); } -void bad_usb_set_file_name(BadUsb* bad_usb, const char* name) { +void bad_usb_set_file_name(BadUsb* bad_usb, char* name) { furi_assert(name); with_view_model( bad_usb->view, (BadUsbModel * model) { - strncpy(model->file_name, name, MAX_NAME_LEN); + model->file_name = name; return true; }); } diff --git a/applications/bad_usb/views/bad_usb_view.h b/applications/bad_usb/views/bad_usb_view.h index 80a47e2ca..f5a8a0fab 100755 --- a/applications/bad_usb/views/bad_usb_view.h +++ b/applications/bad_usb/views/bad_usb_view.h @@ -14,6 +14,6 @@ View* bad_usb_get_view(BadUsb* bad_usb); void bad_usb_set_ok_callback(BadUsb* bad_usb, BadUsbOkCallback callback, void* context); -void bad_usb_set_file_name(BadUsb* bad_usb, const char* name); +void bad_usb_set_file_name(BadUsb* bad_usb, char* name); void bad_usb_set_state(BadUsb* bad_usb, BadUsbState* st); diff --git a/applications/dialogs/dialogs.c b/applications/dialogs/dialogs.c index 8929dc116..cf4a2ad68 100644 --- a/applications/dialogs/dialogs.c +++ b/applications/dialogs/dialogs.c @@ -1,7 +1,6 @@ -#include "dialogs/dialogs_message.h" #include "dialogs_i.h" #include "dialogs_api_lock.h" -#include "dialogs_module_file_browser.h" +#include "dialogs_module_file_select.h" #include "dialogs_module_message.h" static DialogsApp* dialogs_app_alloc() { @@ -14,9 +13,9 @@ static DialogsApp* dialogs_app_alloc() { static void dialogs_app_process_message(DialogsApp* app, DialogsAppMessage* message) { UNUSED(app); switch(message->command) { - case DialogsAppCommandFileBrowser: + case DialogsAppCommandFileOpen: message->return_data->bool_value = - dialogs_app_process_module_file_browser(&message->data->file_browser); + dialogs_app_process_module_file_select(&message->data->file_select); break; case DialogsAppCommandDialog: message->return_data->dialog_value = diff --git a/applications/dialogs/dialogs.h b/applications/dialogs/dialogs.h index 536060565..9c71c0989 100644 --- a/applications/dialogs/dialogs.h +++ b/applications/dialogs/dialogs.h @@ -1,7 +1,6 @@ #pragma once #include #include -#include "m-string.h" #ifdef __cplusplus extern "C" { @@ -11,27 +10,25 @@ extern "C" { typedef struct DialogsApp DialogsApp; -/****************** FILE BROWSER ******************/ +/****************** FILE SELECT ******************/ /** - * Shows and processes the file browser dialog + * Shows and processes the file selection dialog * @param context api pointer - * @param result_path selected file path string pointer - * @param path preselected file path string pointer + * @param path path to directory * @param extension file extension to be offered for selection - * @param skip_assets true - do not show assets folders - * @param icon file icon pointer, NULL for default icon - * @param hide_ext true - hide extensions for files + * @param selected_filename buffer where the selected filename will be saved + * @param selected_filename_size and the size of this buffer + * @param preselected_filename filename to be preselected * @return bool whether a file was selected */ -bool dialog_file_browser_show( +bool dialog_file_select_show( DialogsApp* context, - string_ptr result_path, - string_ptr path, + const char* path, const char* extension, - bool skip_assets, - const Icon* icon, - bool hide_ext); + char* result, + uint8_t result_size, + const char* preselected_filename); /****************** MESSAGE ******************/ diff --git a/applications/dialogs/dialogs_api.c b/applications/dialogs/dialogs_api.c index fab3a5eae..c4efb287d 100644 --- a/applications/dialogs/dialogs_api.c +++ b/applications/dialogs/dialogs_api.c @@ -1,36 +1,31 @@ -#include "dialogs/dialogs_message.h" #include "dialogs_i.h" #include "dialogs_api_lock.h" -#include "m-string.h" -/****************** File browser ******************/ +/****************** File select ******************/ -bool dialog_file_browser_show( +bool dialog_file_select_show( DialogsApp* context, - string_ptr result_path, - string_ptr path, + const char* path, const char* extension, - bool skip_assets, - const Icon* icon, - bool hide_ext) { + char* result, + uint8_t result_size, + const char* preselected_filename) { FuriApiLock lock = API_LOCK_INIT_LOCKED(); furi_check(lock != NULL); DialogsAppData data = { - .file_browser = { + .file_select = { + .path = path, .extension = extension, - .result_path = result_path, - .file_icon = icon, - .hide_ext = hide_ext, - .skip_assets = skip_assets, - .preselected_filename = path, - + .result = result, + .result_size = result_size, + .preselected_filename = preselected_filename, }}; DialogsAppReturn return_data; DialogsAppMessage message = { .lock = lock, - .command = DialogsAppCommandFileBrowser, + .command = DialogsAppCommandFileOpen, .data = &data, .return_data = &return_data, }; diff --git a/applications/dialogs/dialogs_message.h b/applications/dialogs/dialogs_message.h index ccfbdece5..d7b5fabf4 100644 --- a/applications/dialogs/dialogs_message.h +++ b/applications/dialogs/dialogs_message.h @@ -2,27 +2,25 @@ #include #include "dialogs_i.h" #include "dialogs_api_lock.h" -#include "m-string.h" #ifdef __cplusplus extern "C" { #endif typedef struct { + const char* path; const char* extension; - bool skip_assets; - bool hide_ext; - const Icon* file_icon; - string_ptr result_path; - string_ptr preselected_filename; -} DialogsAppMessageDataFileBrowser; + char* result; + uint8_t result_size; + const char* preselected_filename; +} DialogsAppMessageDataFileSelect; typedef struct { const DialogMessage* message; } DialogsAppMessageDataDialog; typedef union { - DialogsAppMessageDataFileBrowser file_browser; + DialogsAppMessageDataFileSelect file_select; DialogsAppMessageDataDialog dialog; } DialogsAppData; @@ -32,7 +30,7 @@ typedef union { } DialogsAppReturn; typedef enum { - DialogsAppCommandFileBrowser, + DialogsAppCommandFileOpen, DialogsAppCommandDialog, } DialogsAppCommand; diff --git a/applications/dialogs/dialogs_module_file_browser.c b/applications/dialogs/dialogs_module_file_browser.c deleted file mode 100644 index ecd0ca79b..000000000 --- a/applications/dialogs/dialogs_module_file_browser.c +++ /dev/null @@ -1,59 +0,0 @@ -#include "dialogs_i.h" -#include "dialogs_api_lock.h" -#include "gui/modules/file_browser.h" - -typedef struct { - FuriApiLock lock; - bool result; -} DialogsAppFileBrowserContext; - -static void dialogs_app_file_browser_back_callback(void* context) { - furi_assert(context); - DialogsAppFileBrowserContext* file_browser_context = context; - file_browser_context->result = false; - API_LOCK_UNLOCK(file_browser_context->lock); -} - -static void dialogs_app_file_browser_callback(void* context) { - furi_assert(context); - DialogsAppFileBrowserContext* file_browser_context = context; - file_browser_context->result = true; - API_LOCK_UNLOCK(file_browser_context->lock); -} - -bool dialogs_app_process_module_file_browser(const DialogsAppMessageDataFileBrowser* data) { - bool ret = false; - Gui* gui = furi_record_open("gui"); - - DialogsAppFileBrowserContext* file_browser_context = - malloc(sizeof(DialogsAppFileBrowserContext)); - file_browser_context->lock = API_LOCK_INIT_LOCKED(); - - ViewHolder* view_holder = view_holder_alloc(); - view_holder_attach_to_gui(view_holder, gui); - view_holder_set_back_callback( - view_holder, dialogs_app_file_browser_back_callback, file_browser_context); - - FileBrowser* file_browser = file_browser_alloc(data->result_path); - file_browser_set_callback( - file_browser, dialogs_app_file_browser_callback, file_browser_context); - file_browser_configure( - file_browser, data->extension, data->skip_assets, data->file_icon, data->hide_ext); - file_browser_start(file_browser, data->preselected_filename); - - view_holder_set_view(view_holder, file_browser_get_view(file_browser)); - view_holder_start(view_holder); - API_LOCK_WAIT_UNTIL_UNLOCK(file_browser_context->lock); - - ret = file_browser_context->result; - - view_holder_stop(view_holder); - view_holder_free(view_holder); - file_browser_stop(file_browser); - file_browser_free(file_browser); - API_LOCK_FREE(file_browser_context->lock); - free(file_browser_context); - furi_record_close("gui"); - - return ret; -} diff --git a/applications/dialogs/dialogs_module_file_select.c b/applications/dialogs/dialogs_module_file_select.c new file mode 100644 index 000000000..f133c00a0 --- /dev/null +++ b/applications/dialogs/dialogs_module_file_select.c @@ -0,0 +1,59 @@ +#include "dialogs_i.h" +#include "dialogs_api_lock.h" +#include + +typedef struct { + FuriApiLock lock; + bool result; +} DialogsAppFileSelectContext; + +static void dialogs_app_file_select_back_callback(void* context) { + furi_assert(context); + DialogsAppFileSelectContext* file_select_context = context; + file_select_context->result = false; + API_LOCK_UNLOCK(file_select_context->lock); +} + +static void dialogs_app_file_select_callback(bool result, void* context) { + furi_assert(context); + DialogsAppFileSelectContext* file_select_context = context; + file_select_context->result = result; + API_LOCK_UNLOCK(file_select_context->lock); +} + +bool dialogs_app_process_module_file_select(const DialogsAppMessageDataFileSelect* data) { + bool ret = false; + Gui* gui = furi_record_open("gui"); + + DialogsAppFileSelectContext* file_select_context = malloc(sizeof(DialogsAppFileSelectContext)); + file_select_context->lock = API_LOCK_INIT_LOCKED(); + + ViewHolder* view_holder = view_holder_alloc(); + view_holder_attach_to_gui(view_holder, gui); + view_holder_set_back_callback( + view_holder, dialogs_app_file_select_back_callback, file_select_context); + + FileSelect* file_select = file_select_alloc(); + file_select_set_callback(file_select, dialogs_app_file_select_callback, file_select_context); + file_select_set_filter(file_select, data->path, data->extension); + file_select_set_result_buffer(file_select, data->result, data->result_size); + file_select_init(file_select); + if(data->preselected_filename != NULL) { + file_select_set_selected_file(file_select, data->preselected_filename); + } + + view_holder_set_view(view_holder, file_select_get_view(file_select)); + view_holder_start(view_holder); + API_LOCK_WAIT_UNTIL_UNLOCK(file_select_context->lock); + + ret = file_select_context->result; + + view_holder_stop(view_holder); + view_holder_free(view_holder); + file_select_free(file_select); + API_LOCK_FREE(file_select_context->lock); + free(file_select_context); + furi_record_close("gui"); + + return ret; +} diff --git a/applications/dialogs/dialogs_module_file_browser.h b/applications/dialogs/dialogs_module_file_select.h similarity index 54% rename from applications/dialogs/dialogs_module_file_browser.h rename to applications/dialogs/dialogs_module_file_select.h index b6cbdf6a5..749fe9c1d 100644 --- a/applications/dialogs/dialogs_module_file_browser.h +++ b/applications/dialogs/dialogs_module_file_select.h @@ -5,7 +5,7 @@ extern "C" { #endif -bool dialogs_app_process_module_file_browser(const DialogsAppMessageDataFileBrowser* data); +bool dialogs_app_process_module_file_select(const DialogsAppMessageDataFileSelect* data); #ifdef __cplusplus } diff --git a/applications/gui/modules/file_select.c b/applications/gui/modules/file_select.c new file mode 100644 index 000000000..14541196b --- /dev/null +++ b/applications/gui/modules/file_select.c @@ -0,0 +1,475 @@ +#include "file_select.h" +#include +#include +#include + +#define FILENAME_COUNT 4 + +struct FileSelect { + // public + View* view; + Storage* fs_api; + const char* path; + const char* extension; + + bool init_completed; + + FileSelectCallback callback; + void* context; + + char* buffer; + uint8_t buffer_size; +}; + +typedef struct { + string_t filename[FILENAME_COUNT]; + uint8_t position; + + uint16_t first_file_index; + uint16_t file_count; + +} FileSelectModel; + +bool file_select_fill_strings(FileSelect* file_select); +bool file_select_fill_count(FileSelect* file_select); +static bool file_select_init_inner(FileSelect* file_select); + +static void file_select_draw_callback(Canvas* canvas, void* _model) { + FileSelectModel* model = _model; + + string_t string_buff; + const uint8_t item_height = 16; + const uint8_t item_width = 123; + const uint8_t text_max_width = 115; + + canvas_clear(canvas); + canvas_set_font(canvas, FontSecondary); + + if(model->file_count) { + for(uint8_t i = 0; i < MIN(FILENAME_COUNT, model->file_count); i++) { + if(i == model->position) { + canvas_set_color(canvas, ColorBlack); + canvas_draw_box(canvas, 0, (i * item_height) + 1, item_width, item_height - 2); + + canvas_set_color(canvas, ColorWhite); + canvas_draw_dot(canvas, 0, (i * item_height) + 1); + canvas_draw_dot(canvas, 0, (i * item_height) + item_height - 2); + canvas_draw_dot(canvas, item_width - 1, (i * item_height) + 1); + canvas_draw_dot(canvas, item_width - 1, (i * item_height) + item_height - 2); + } else { + canvas_set_color(canvas, ColorBlack); + } + + string_init_set(string_buff, model->filename[i]); + elements_string_fit_width(canvas, string_buff, text_max_width); + canvas_draw_str( + canvas, 6, (i * item_height) + item_height - 4, string_get_cstr(string_buff)); + + string_clear(string_buff); + } + } else { + canvas_draw_str(canvas, 6, item_height, "Empty folder"); + } + elements_scrollbar(canvas, model->first_file_index + model->position, model->file_count); +} + +static bool file_select_input_callback(InputEvent* event, void* context) { + FileSelect* file_select = (FileSelect*)context; + bool consumed = false; + + if((event->type == InputTypeShort) | (event->type == InputTypeRepeat)) { + if(!file_select->init_completed) { + if(!file_select_init_inner(file_select)) { + file_select->callback(false, file_select->context); + } + } else if(event->key == InputKeyUp) { + with_view_model( + file_select->view, (FileSelectModel * model) { + if(model->position == 0) { + if(model->first_file_index == 0) { + // wrap + int16_t max_first_file_index = model->file_count - FILENAME_COUNT; + model->position = MIN(FILENAME_COUNT - 1, model->file_count - 1); + model->first_file_index = + max_first_file_index < 0 ? 0 : max_first_file_index; + } else { + model->first_file_index--; + } + } else if(model->position == 1) { + if(model->first_file_index == 0) { + model->position--; + } else { + model->first_file_index--; + } + } else { + model->position--; + } + return true; + }); + consumed = true; + if(!file_select_fill_strings(file_select)) { + file_select->callback(false, file_select->context); + } + } else if(event->key == InputKeyDown) { + with_view_model( + file_select->view, (FileSelectModel * model) { + uint16_t max_first_file_index = model->file_count > FILENAME_COUNT ? + model->file_count - FILENAME_COUNT : + 0; + + if(model->position >= MIN(FILENAME_COUNT - 1, model->file_count - 1)) { + if(model->first_file_index >= max_first_file_index) { + // wrap + model->position = 0; + model->first_file_index = 0; + } else { + model->first_file_index++; + } + } else if(model->position >= (FILENAME_COUNT - 2)) { + if(model->first_file_index >= max_first_file_index) { + model->position++; + } else { + model->first_file_index++; + } + } else { + model->position++; + } + return true; + }); + consumed = true; + if(!file_select_fill_strings(file_select)) { + file_select->callback(false, file_select->context); + } + } else if(event->key == InputKeyOk) { + if(file_select->callback != NULL) { + size_t files = 0; + if(file_select->buffer) { + with_view_model( + file_select->view, (FileSelectModel * model) { + files = model->file_count; + strlcpy( + file_select->buffer, + string_get_cstr(model->filename[model->position]), + file_select->buffer_size); + + return false; + }); + }; + if(files > 0) { + file_select->callback(true, file_select->context); + } + } + consumed = true; + } + } + + return consumed; +} + +static bool file_select_init_inner(FileSelect* file_select) { + bool result = false; + if(file_select->path && file_select->extension && file_select->fs_api) { + if(file_select_fill_count(file_select)) { + if(file_select_fill_strings(file_select)) { + file_select->init_completed = true; + result = true; + } + } + } + + return result; +} + +FileSelect* file_select_alloc() { + FileSelect* file_select = malloc(sizeof(FileSelect)); + file_select->view = view_alloc(); + file_select->fs_api = furi_record_open("storage"); + + view_set_context(file_select->view, file_select); + view_allocate_model(file_select->view, ViewModelTypeLockFree, sizeof(FileSelectModel)); + view_set_draw_callback(file_select->view, file_select_draw_callback); + view_set_input_callback(file_select->view, file_select_input_callback); + + with_view_model( + file_select->view, (FileSelectModel * model) { + for(uint8_t i = 0; i < FILENAME_COUNT; i++) { + string_init(model->filename[i]); + } + + model->first_file_index = 0; + model->file_count = 0; + return false; + }); + + return file_select; +} + +void file_select_free(FileSelect* file_select) { + furi_assert(file_select); + with_view_model( + file_select->view, (FileSelectModel * model) { + for(uint8_t i = 0; i < FILENAME_COUNT; i++) { + string_clear(model->filename[i]); + } + return false; + }); + view_free(file_select->view); + free(file_select); + furi_record_close("storage"); +} + +View* file_select_get_view(FileSelect* file_select) { + furi_assert(file_select); + return file_select->view; +} + +void file_select_set_callback(FileSelect* file_select, FileSelectCallback callback, void* context) { + file_select->context = context; + file_select->callback = callback; +} + +void file_select_set_filter(FileSelect* file_select, const char* path, const char* extension) { + furi_assert(file_select); + file_select->path = path; + file_select->extension = extension; +} + +void file_select_set_result_buffer(FileSelect* file_select, char* buffer, uint8_t buffer_size) { + file_select->buffer = buffer; + file_select->buffer_size = buffer_size; + + if(file_select->buffer) { + strlcpy(file_select->buffer, "", file_select->buffer_size); + } +} + +bool file_select_init(FileSelect* file_select) { + if(!file_select_init_inner(file_select)) { + file_select->callback(false, file_select->context); + return false; + } else { + return true; + } +} + +static bool filter_file(FileSelect* file_select, FileInfo* file_info, char* name) { + bool result = false; + + if(!(file_info->flags & FSF_DIRECTORY)) { + if(strcmp(file_select->extension, "*") == 0) { + result = true; + } else if(strstr(name, file_select->extension) != NULL) { + result = true; + } + } + + return result; +} + +bool file_select_fill_strings(FileSelect* file_select) { + furi_assert(file_select); + furi_assert(file_select->fs_api); + furi_assert(file_select->path); + furi_assert(file_select->extension); + + FileInfo file_info; + File* directory = storage_file_alloc(file_select->fs_api); + + uint8_t string_counter = 0; + uint16_t file_counter = 0; + const uint8_t name_length = 100; + char* name = malloc(name_length); + uint16_t first_file_index = 0; + + with_view_model( + file_select->view, (FileSelectModel * model) { + first_file_index = model->first_file_index; + return false; + }); + + if(!storage_dir_open(directory, file_select->path)) { + storage_dir_close(directory); + storage_file_free(directory); + free(name); + return true; + } + + while(1) { + if(!storage_dir_read(directory, &file_info, name, name_length)) { + break; + } + + if(storage_file_get_error(directory) == FSE_OK) { + if(filter_file(file_select, &file_info, name)) { + if(file_counter >= first_file_index) { + with_view_model( + file_select->view, (FileSelectModel * model) { + string_set_str(model->filename[string_counter], name); + + if(strcmp(file_select->extension, "*") != 0) { + string_replace_all_str( + model->filename[string_counter], file_select->extension, ""); + } + + return true; + }); + string_counter++; + + if(string_counter >= FILENAME_COUNT) { + break; + } + } + file_counter++; + } + } else { + storage_dir_close(directory); + storage_file_free(directory); + free(name); + return false; + } + } + + storage_dir_close(directory); + storage_file_free(directory); + free(name); + return true; +} + +bool file_select_fill_count(FileSelect* file_select) { + furi_assert(file_select); + furi_assert(file_select->fs_api); + furi_assert(file_select->path); + furi_assert(file_select->extension); + + FileInfo file_info; + File* directory = storage_file_alloc(file_select->fs_api); + + uint16_t file_counter = 0; + const uint8_t name_length = 100; + char* name = malloc(name_length); + + if(!storage_dir_open(directory, file_select->path)) { + storage_dir_close(directory); + storage_file_free(directory); + free(name); + return true; + } + + while(1) { + if(!storage_dir_read(directory, &file_info, name, name_length)) { + break; + } + + if(storage_file_get_error(directory) == FSE_OK) { + if(filter_file(file_select, &file_info, name)) { + file_counter++; + } + } else { + storage_dir_close(directory); + storage_file_free(directory); + free(name); + return false; + } + } + + with_view_model( + file_select->view, (FileSelectModel * model) { + model->file_count = file_counter; + return false; + }); + + storage_dir_close(directory); + storage_file_free(directory); + free(name); + return true; +} + +void file_select_set_selected_file_internal(FileSelect* file_select, const char* filename) { + furi_assert(file_select); + furi_assert(filename); + furi_assert(file_select->fs_api); + furi_assert(file_select->path); + furi_assert(file_select->extension); + + if(strlen(filename) == 0) return; + + FileInfo file_info; + File* directory = storage_file_alloc(file_select->fs_api); + + const uint8_t name_length = 100; + char* name = malloc(name_length); + uint16_t file_position = 0; + bool file_found = false; + + string_t filename_str; + string_init_set_str(filename_str, filename); + if(strcmp(file_select->extension, "*") != 0) { + string_cat_str(filename_str, file_select->extension); + } + + if(!storage_dir_open(directory, file_select->path)) { + string_clear(filename_str); + storage_dir_close(directory); + storage_file_free(directory); + free(name); + return; + } + + while(1) { + if(!storage_dir_read(directory, &file_info, name, name_length)) { + break; + } + + if(storage_file_get_error(directory) == FSE_OK) { + if(filter_file(file_select, &file_info, name)) { + if(strcmp(string_get_cstr(filename_str), name) == 0) { + file_found = true; + break; + } + + file_position++; + } + } else { + string_clear(filename_str); + storage_dir_close(directory); + storage_file_free(directory); + free(name); + return; + } + } + + if(file_found) { + with_view_model( + file_select->view, (FileSelectModel * model) { + uint16_t max_first_file_index = + model->file_count > FILENAME_COUNT ? model->file_count - FILENAME_COUNT : 0; + + model->first_file_index = file_position; + + if(model->first_file_index > 0) { + model->first_file_index -= 1; + } + + if(model->first_file_index >= max_first_file_index) { + model->first_file_index = max_first_file_index; + } + + model->position = file_position - model->first_file_index; + + return true; + }); + } + + string_clear(filename_str); + storage_dir_close(directory); + storage_file_free(directory); + free(name); +} + +void file_select_set_selected_file(FileSelect* file_select, const char* filename) { + file_select_set_selected_file_internal(file_select, filename); + + if(!file_select_fill_strings(file_select)) { + file_select->callback(false, file_select->context); + } +} diff --git a/applications/gui/modules/file_select.h b/applications/gui/modules/file_select.h new file mode 100644 index 000000000..ed3d5b60c --- /dev/null +++ b/applications/gui/modules/file_select.h @@ -0,0 +1,31 @@ +/** + * @file file_select.h + * GUI: FileSelect view module API + */ + +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct FileSelect FileSelect; + +typedef void (*FileSelectCallback)(bool result, void* context); + +FileSelect* file_select_alloc(); + +void file_select_free(FileSelect* file_select); +View* file_select_get_view(FileSelect* file_select); + +void file_select_set_callback(FileSelect* file_select, FileSelectCallback callback, void* context); +void file_select_set_filter(FileSelect* file_select, const char* path, const char* extension); +void file_select_set_result_buffer(FileSelect* file_select, char* buffer, uint8_t buffer_size); +bool file_select_init(FileSelect* file_select); +void file_select_set_selected_file(FileSelect* file_select, const char* filename); + +#ifdef __cplusplus +} +#endif diff --git a/applications/gui/modules/validators.c b/applications/gui/modules/validators.c index 546423d02..242dbe682 100644 --- a/applications/gui/modules/validators.c +++ b/applications/gui/modules/validators.c @@ -3,7 +3,7 @@ #include "applications/storage/storage.h" struct ValidatorIsFile { - char* app_path_folder; + const char* app_path_folder; const char* app_extension; char* current_name; }; @@ -40,7 +40,7 @@ ValidatorIsFile* validator_is_file_alloc_init( const char* current_name) { ValidatorIsFile* instance = malloc(sizeof(ValidatorIsFile)); - instance->app_path_folder = strdup(app_path_folder); + instance->app_path_folder = app_path_folder; instance->app_extension = app_extension; instance->current_name = strdup(current_name); @@ -49,7 +49,6 @@ ValidatorIsFile* validator_is_file_alloc_init( void validator_is_file_free(ValidatorIsFile* instance) { furi_assert(instance); - free(instance->app_path_folder); free(instance->current_name); free(instance); } diff --git a/applications/ibutton/ibutton.c b/applications/ibutton/ibutton.c index b1b5bf62f..a38f077f3 100644 --- a/applications/ibutton/ibutton.c +++ b/applications/ibutton/ibutton.c @@ -1,8 +1,7 @@ #include "ibutton.h" -#include "assets_icons.h" #include "ibutton_i.h" #include "ibutton/scenes/ibutton_scene.h" -#include "m-string.h" + #include #include @@ -86,8 +85,6 @@ void ibutton_tick_event_callback(void* context) { iButton* ibutton_alloc() { iButton* ibutton = malloc(sizeof(iButton)); - string_init(ibutton->file_path); - ibutton->scene_manager = scene_manager_alloc(&ibutton_scene_handlers, ibutton); ibutton->view_dispatcher = view_dispatcher_alloc(); @@ -179,28 +176,49 @@ void ibutton_free(iButton* ibutton) { ibutton_worker_free(ibutton->key_worker); ibutton_key_free(ibutton->key); - string_clear(ibutton->file_path); - free(ibutton); } bool ibutton_file_select(iButton* ibutton) { - bool success = dialog_file_browser_show( + bool success = dialog_file_select_show( ibutton->dialogs, - ibutton->file_path, - ibutton->file_path, + IBUTTON_APP_FOLDER, IBUTTON_APP_EXTENSION, - true, - &I_ibutt_10px, - true); + ibutton->file_name, + IBUTTON_FILE_NAME_SIZE, + ibutton_key_get_name_p(ibutton->key)); if(success) { - success = ibutton_load_key_data(ibutton, ibutton->file_path); + string_t key_str; + string_init_printf( + key_str, "%s/%s%s", IBUTTON_APP_FOLDER, ibutton->file_name, IBUTTON_APP_EXTENSION); + success = ibutton_load_key_data(ibutton, key_str); + + if(success) { + ibutton_key_set_name(ibutton->key, ibutton->file_name); + } + + string_clear(key_str); } return success; } +bool ibutton_load_key(iButton* ibutton, const char* key_name) { + string_t key_path; + string_init_set_str(key_path, key_name); + + const bool success = ibutton_load_key_data(ibutton, key_path); + + if(success) { + path_extract_filename_no_ext(key_name, key_path); + ibutton_key_set_name(ibutton->key, string_get_cstr(key_path)); + } + + string_clear(key_path); + return success; +} + bool ibutton_save_key(iButton* ibutton, const char* key_name) { // Create ibutton directory if necessary ibutton_make_app_folder(ibutton); @@ -208,22 +226,27 @@ bool ibutton_save_key(iButton* ibutton, const char* key_name) { FlipperFormat* file = flipper_format_file_alloc(ibutton->storage); iButtonKey* key = ibutton->key; + string_t key_file_name; bool result = false; + string_init(key_file_name); do { // First remove key if it was saved (we rename the key) - ibutton_delete_key(ibutton); + if(!ibutton_delete_key(ibutton)) break; + + // Save the key + ibutton_key_set_name(key, key_name); // Set full file name, for new key - if(string_end_with_str_p(ibutton->file_path, IBUTTON_APP_EXTENSION)) { - size_t filename_start = string_search_rchar(ibutton->file_path, '/'); - string_left(ibutton->file_path, filename_start); - } - - string_cat_printf(ibutton->file_path, "/%s%s", key_name, IBUTTON_APP_EXTENSION); + string_printf( + key_file_name, + "%s/%s%s", + IBUTTON_APP_FOLDER, + ibutton_key_get_name_p(key), + IBUTTON_APP_EXTENSION); // Open file for write - if(!flipper_format_file_open_always(file, string_get_cstr(ibutton->file_path))) break; + if(!flipper_format_file_open_always(file, string_get_cstr(key_file_name))) break; // Write header if(!flipper_format_write_header_cstr(file, IBUTTON_APP_FILE_TYPE, 1)) break; @@ -248,6 +271,8 @@ bool ibutton_save_key(iButton* ibutton, const char* key_name) { flipper_format_free(file); + string_clear(key_file_name); + if(!result) { dialog_message_show_storage_error(ibutton->dialogs, "Cannot save\nkey file"); } @@ -256,8 +281,17 @@ bool ibutton_save_key(iButton* ibutton, const char* key_name) { } bool ibutton_delete_key(iButton* ibutton) { + string_t file_name; bool result = false; - result = storage_simply_remove(ibutton->storage, string_get_cstr(ibutton->file_path)); + + string_init_printf( + file_name, + "%s/%s%s", + IBUTTON_APP_FOLDER, + ibutton_key_get_name_p(ibutton->key), + IBUTTON_APP_EXTENSION); + result = storage_simply_remove(ibutton->storage, string_get_cstr(file_name)); + string_clear(file_name); return result; } @@ -301,17 +335,8 @@ int32_t ibutton_app(void* p) { ibutton_make_app_folder(ibutton); - bool key_loaded = false; - - if(p) { - string_set_str(ibutton->file_path, (const char*)p); - if(ibutton_load_key_data(ibutton, ibutton->file_path)) { - key_loaded = true; - // TODO: Display an error if the key from p could not be loaded - } - } - - if(key_loaded) { + if(p && ibutton_load_key(ibutton, (const char*)p)) { + // TODO: Display an error if the key from p could not be loaded scene_manager_next_scene(ibutton->scene_manager, iButtonSceneEmulate); } else { scene_manager_next_scene(ibutton->scene_manager, iButtonSceneStart); diff --git a/applications/ibutton/ibutton_i.h b/applications/ibutton/ibutton_i.h index e66712bee..36857fd64 100644 --- a/applications/ibutton/ibutton_i.h +++ b/applications/ibutton/ibutton_i.h @@ -41,7 +41,7 @@ struct iButton { iButtonWorker* key_worker; iButtonKey* key; - string_t file_path; + char file_name[IBUTTON_FILE_NAME_SIZE]; char text_store[IBUTTON_TEXT_STORE_SIZE + 1]; Submenu* submenu; @@ -74,6 +74,7 @@ typedef enum { } iButtonNotificationMessage; bool ibutton_file_select(iButton* ibutton); +bool ibutton_load_key(iButton* ibutton, const char* key_name); bool ibutton_save_key(iButton* ibutton, const char* key_name); bool ibutton_delete_key(iButton* ibutton); void ibutton_text_store_set(iButton* ibutton, const char* text, ...); diff --git a/applications/ibutton/scenes/ibutton_scene_add_type.c b/applications/ibutton/scenes/ibutton_scene_add_type.c index 273330e71..db129295a 100644 --- a/applications/ibutton/scenes/ibutton_scene_add_type.c +++ b/applications/ibutton/scenes/ibutton_scene_add_type.c @@ -1,5 +1,4 @@ #include "../ibutton_i.h" -#include "m-string.h" enum SubmenuIndex { SubmenuIndexCyfral, @@ -45,7 +44,7 @@ bool ibutton_scene_add_type_on_event(void* context, SceneManagerEvent event) { furi_crash("Unknown key type"); } - string_set_str(ibutton->file_path, IBUTTON_APP_FOLDER); + ibutton_key_set_name(key, ""); ibutton_key_clear_data(key); scene_manager_next_scene(ibutton->scene_manager, iButtonSceneAddValue); } diff --git a/applications/ibutton/scenes/ibutton_scene_delete_confirm.c b/applications/ibutton/scenes/ibutton_scene_delete_confirm.c index 51f1f2794..73ea97cc6 100644 --- a/applications/ibutton/scenes/ibutton_scene_delete_confirm.c +++ b/applications/ibutton/scenes/ibutton_scene_delete_confirm.c @@ -1,5 +1,4 @@ #include "../ibutton_i.h" -#include static void ibutton_scene_delete_confirm_widget_callback( GuiButtonType result, @@ -17,11 +16,7 @@ void ibutton_scene_delete_confirm_on_enter(void* context) { iButtonKey* key = ibutton->key; const uint8_t* key_data = ibutton_key_get_data_p(key); - string_t key_name; - string_init(key_name); - path_extract_filename(ibutton->file_path, key_name, true); - - ibutton_text_store_set(ibutton, "\e#Delete %s?\e#", string_get_cstr(key_name)); + ibutton_text_store_set(ibutton, "\e#Delete %s?\e#", ibutton_key_get_name_p(key)); widget_add_text_box_element( widget, 0, 0, 128, 27, AlignCenter, AlignCenter, ibutton->text_store, false); widget_add_button_element( @@ -67,8 +62,6 @@ void ibutton_scene_delete_confirm_on_enter(void* context) { widget, 64, 33, AlignCenter, AlignBottom, FontSecondary, ibutton->text_store); view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewWidget); - - string_clear(key_name); } bool ibutton_scene_delete_confirm_on_event(void* context, SceneManagerEvent event) { diff --git a/applications/ibutton/scenes/ibutton_scene_emulate.c b/applications/ibutton/scenes/ibutton_scene_emulate.c index 590223979..8ffe73b66 100644 --- a/applications/ibutton/scenes/ibutton_scene_emulate.c +++ b/applications/ibutton/scenes/ibutton_scene_emulate.c @@ -1,6 +1,5 @@ #include "../ibutton_i.h" #include -#include static void ibutton_scene_emulate_callback(void* context, bool emulated) { iButton* ibutton = context; @@ -16,19 +15,14 @@ void ibutton_scene_emulate_on_enter(void* context) { iButtonKey* key = ibutton->key; const uint8_t* key_data = ibutton_key_get_data_p(key); - - string_t key_name; - string_init(key_name); - if(string_end_with_str_p(ibutton->file_path, IBUTTON_APP_EXTENSION)) { - path_extract_filename(ibutton->file_path, key_name, true); - } + const char* key_name = ibutton_key_get_name_p(key); uint8_t line_count = 2; DOLPHIN_DEED(DolphinDeedIbuttonEmulate); // check that stored key has name - if(!string_empty_p(key_name)) { - ibutton_text_store_set(ibutton, "emulating\n%s", string_get_cstr(key_name)); + if(strcmp(key_name, "") != 0) { + ibutton_text_store_set(ibutton, "emulating\n%s", key_name); line_count = 2; } else { // if not, show key data @@ -83,8 +77,6 @@ void ibutton_scene_emulate_on_enter(void* context) { ibutton_worker_emulate_set_callback( ibutton->key_worker, ibutton_scene_emulate_callback, ibutton); ibutton_worker_emulate_start(ibutton->key_worker, key); - - string_clear(key_name); } bool ibutton_scene_emulate_on_event(void* context, SceneManagerEvent event) { diff --git a/applications/ibutton/scenes/ibutton_scene_info.c b/applications/ibutton/scenes/ibutton_scene_info.c index bd364ada8..5b0af1d8f 100644 --- a/applications/ibutton/scenes/ibutton_scene_info.c +++ b/applications/ibutton/scenes/ibutton_scene_info.c @@ -1,5 +1,4 @@ #include "../ibutton_i.h" -#include void ibutton_scene_info_on_enter(void* context) { iButton* ibutton = context; @@ -8,11 +7,7 @@ void ibutton_scene_info_on_enter(void* context) { const uint8_t* key_data = ibutton_key_get_data_p(key); - string_t key_name; - string_init(key_name); - path_extract_filename(ibutton->file_path, key_name, true); - - ibutton_text_store_set(ibutton, "%s", string_get_cstr(key_name)); + ibutton_text_store_set(ibutton, "%s", ibutton_key_get_name_p(key)); widget_add_text_box_element( widget, 0, 0, 128, 28, AlignCenter, AlignCenter, ibutton->text_store, false); @@ -51,8 +46,6 @@ void ibutton_scene_info_on_enter(void* context) { widget, 64, 35, AlignCenter, AlignBottom, FontPrimary, ibutton->text_store); view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewWidget); - - string_clear(key_name); } bool ibutton_scene_info_on_event(void* context, SceneManagerEvent event) { diff --git a/applications/ibutton/scenes/ibutton_scene_read.c b/applications/ibutton/scenes/ibutton_scene_read.c index 0cc0a8dff..a25f27e60 100644 --- a/applications/ibutton/scenes/ibutton_scene_read.c +++ b/applications/ibutton/scenes/ibutton_scene_read.c @@ -18,7 +18,7 @@ void ibutton_scene_read_on_enter(void* context) { popup_set_icon(popup, 0, 5, &I_DolphinWait_61x59); view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewPopup); - string_set_str(ibutton->file_path, IBUTTON_APP_FOLDER); + ibutton_key_set_name(key, ""); ibutton_worker_read_set_callback(worker, ibutton_scene_read_callback, ibutton); ibutton_worker_read_start(worker, key); diff --git a/applications/ibutton/scenes/ibutton_scene_save_name.c b/applications/ibutton/scenes/ibutton_scene_save_name.c index 6caf5d2d5..b1baf6afc 100644 --- a/applications/ibutton/scenes/ibutton_scene_save_name.c +++ b/applications/ibutton/scenes/ibutton_scene_save_name.c @@ -1,7 +1,5 @@ #include "../ibutton_i.h" -#include "m-string.h" #include -#include static void ibutton_scene_save_name_text_input_callback(void* context) { iButton* ibutton = context; @@ -12,17 +10,13 @@ void ibutton_scene_save_name_on_enter(void* context) { iButton* ibutton = context; TextInput* text_input = ibutton->text_input; - string_t key_name; - string_init(key_name); - if(string_end_with_str_p(ibutton->file_path, IBUTTON_APP_EXTENSION)) { - path_extract_filename(ibutton->file_path, key_name, true); - } + const char* key_name = ibutton_key_get_name_p(ibutton->key); + const bool key_name_is_empty = !strcmp(key_name, ""); - const bool key_name_is_empty = string_empty_p(key_name); if(key_name_is_empty) { set_random_name(ibutton->text_store, IBUTTON_TEXT_STORE_SIZE); } else { - ibutton_text_store_set(ibutton, "%s", string_get_cstr(key_name)); + ibutton_text_store_set(ibutton, "%s", key_name); } text_input_set_header_text(text_input, "Name the key"); @@ -34,19 +28,11 @@ void ibutton_scene_save_name_on_enter(void* context) { IBUTTON_KEY_NAME_SIZE, key_name_is_empty); - string_t folder_path; - string_init(folder_path); - - path_extract_dirname(string_get_cstr(ibutton->file_path), folder_path); - - ValidatorIsFile* validator_is_file = validator_is_file_alloc_init( - string_get_cstr(folder_path), IBUTTON_APP_EXTENSION, string_get_cstr(key_name)); + ValidatorIsFile* validator_is_file = + validator_is_file_alloc_init(IBUTTON_APP_FOLDER, IBUTTON_APP_EXTENSION, key_name); text_input_set_validator(text_input, validator_is_file_callback, validator_is_file); view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewTextInput); - - string_clear(key_name); - string_clear(folder_path); } bool ibutton_scene_save_name_on_event(void* context, SceneManagerEvent event) { diff --git a/applications/ibutton/scenes/ibutton_scene_start.c b/applications/ibutton/scenes/ibutton_scene_start.c index cc8af9839..a3141f5ad 100644 --- a/applications/ibutton/scenes/ibutton_scene_start.c +++ b/applications/ibutton/scenes/ibutton_scene_start.c @@ -36,7 +36,6 @@ bool ibutton_scene_start_on_event(void* context, SceneManagerEvent event) { if(event.event == SubmenuIndexRead) { scene_manager_next_scene(ibutton->scene_manager, iButtonSceneRead); } else if(event.event == SubmenuIndexSaved) { - string_set_str(ibutton->file_path, IBUTTON_APP_FOLDER); scene_manager_next_scene(ibutton->scene_manager, iButtonSceneSelectKey); } else if(event.event == SubmenuIndexAdd) { scene_manager_next_scene(ibutton->scene_manager, iButtonSceneAddType); diff --git a/applications/ibutton/scenes/ibutton_scene_write.c b/applications/ibutton/scenes/ibutton_scene_write.c index 4ce19408a..35e45d83b 100644 --- a/applications/ibutton/scenes/ibutton_scene_write.c +++ b/applications/ibutton/scenes/ibutton_scene_write.c @@ -1,6 +1,4 @@ #include "../ibutton_i.h" -#include "m-string.h" -#include "toolbox/path.h" typedef enum { iButtonSceneWriteStateDefault, @@ -19,18 +17,13 @@ void ibutton_scene_write_on_enter(void* context) { iButtonWorker* worker = ibutton->key_worker; const uint8_t* key_data = ibutton_key_get_data_p(key); - - string_t key_name; - string_init(key_name); - if(string_end_with_str_p(ibutton->file_path, IBUTTON_APP_EXTENSION)) { - path_extract_filename(ibutton->file_path, key_name, true); - } + const char* key_name = ibutton_key_get_name_p(key); uint8_t line_count = 2; // check that stored key has name - if(!string_empty_p(key_name)) { - ibutton_text_store_set(ibutton, "writing\n%s", string_get_cstr(key_name)); + if(strcmp(key_name, "") != 0) { + ibutton_text_store_set(ibutton, "writing\n%s", key_name); line_count = 2; } else { // if not, show key data @@ -86,8 +79,6 @@ void ibutton_scene_write_on_enter(void* context) { ibutton_worker_write_set_callback(worker, ibutton_scene_write_callback, ibutton); ibutton_worker_write_start(worker, key); - - string_clear(key_name); } bool ibutton_scene_write_on_event(void* context, SceneManagerEvent event) { diff --git a/applications/infrared/infrared_app.cpp b/applications/infrared/infrared_app.cpp index 1ac859d10..5dc3b311d 100644 --- a/applications/infrared/infrared_app.cpp +++ b/applications/infrared/infrared_app.cpp @@ -1,5 +1,4 @@ #include "infrared_app.h" -#include "m-string.h" #include #include #include @@ -13,18 +12,20 @@ int32_t InfraredApp::run(void* args) { bool exit = false; if(args) { - string_t path; - string_init_set_str(path, (char*)args); - if(string_end_with_str_p(path, InfraredApp::infrared_extension)) { - bool result = remote_manager.load(path); + std::string path = static_cast(args); + std::string remote_name(path, path.find_last_of('/') + 1, path.size()); + auto last_dot = remote_name.find_last_of('.'); + if(last_dot != std::string::npos) { + remote_name.erase(last_dot); + path.erase(path.find_last_of('/')); + bool result = remote_manager.load(path, remote_name); if(result) { current_scene = InfraredApp::Scene::Remote; } else { - printf("Failed to load remote \'%s\'\r\n", string_get_cstr(path)); + printf("Failed to load remote \'%s\'\r\n", remote_name.c_str()); return -1; } } - string_clear(path); } scenes[current_scene]->on_enter(this); @@ -50,7 +51,6 @@ int32_t InfraredApp::run(void* args) { InfraredApp::InfraredApp() { furi_check(InfraredAppRemoteManager::max_button_name_length < get_text_store_size()); - string_init_set_str(file_path, InfraredApp::infrared_directory); notification = static_cast(furi_record_open("notification")); dialogs = static_cast(furi_record_open("dialogs")); infrared_worker = infrared_worker_alloc(); @@ -60,7 +60,6 @@ InfraredApp::~InfraredApp() { infrared_worker_free(infrared_worker); furi_record_close("notification"); furi_record_close("dialogs"); - string_clear(file_path); for(auto& [key, scene] : scenes) delete scene; } diff --git a/applications/infrared/infrared_app.h b/applications/infrared/infrared_app.h index 1cb8b6617..e0db7c51b 100644 --- a/applications/infrared/infrared_app.h +++ b/applications/infrared/infrared_app.h @@ -251,8 +251,6 @@ public: /** Main class destructor, deinitializes all critical objects */ ~InfraredApp(); - string_t file_path; - /** Path to Infrared directory */ static constexpr const char* infrared_directory = "/any/infrared"; /** Infrared files extension (remote files and universal databases) */ diff --git a/applications/infrared/infrared_app_remote_manager.cpp b/applications/infrared/infrared_app_remote_manager.cpp index faeccb39e..4fd645532 100644 --- a/applications/infrared/infrared_app_remote_manager.cpp +++ b/applications/infrared/infrared_app_remote_manager.cpp @@ -1,5 +1,3 @@ -#include "m-string.h" -#include "storage/filesystem_api_defines.h" #include #include "infrared_app_remote_manager.h" #include "infrared/helpers/infrared_parser.h" @@ -13,58 +11,44 @@ #include #include #include "infrared_app.h" -#include static const char* default_remote_name = "remote"; -void InfraredAppRemoteManager::find_vacant_remote_name(string_t name, string_t path) { +std::string InfraredAppRemoteManager::make_full_name( + const std::string& path, + const std::string& remote_name) const { + return std::string("") + path + "/" + remote_name + InfraredApp::infrared_extension; +} + +std::string InfraredAppRemoteManager::find_vacant_remote_name(const std::string& name) { + std::string result_name; Storage* storage = static_cast(furi_record_open("storage")); - string_t base_path; - string_init_set(base_path, path); + FS_Error error = storage_common_stat( + storage, make_full_name(InfraredApp::infrared_directory, name).c_str(), NULL); - if(string_end_with_str_p(base_path, InfraredApp::infrared_extension)) { - size_t filename_start = string_search_rchar(base_path, '/'); - string_left(base_path, filename_start); - } - - string_printf( - base_path, - "%s/%s%s", - string_get_cstr(path), - string_get_cstr(name), - InfraredApp::infrared_extension); - - FS_Error error = storage_common_stat(storage, string_get_cstr(base_path), NULL); - - if(error == FSE_OK) { + if(error == FSE_NOT_EXIST) { + result_name = name; + } else if(error != FSE_OK) { + result_name = std::string(); + } else { /* if suggested name is occupied, try another one (name2, name3, etc) */ - size_t dot = string_search_rchar(base_path, '.'); - string_left(base_path, dot); - - string_t path_temp; - string_init(path_temp); - uint32_t i = 1; + std::string new_name; do { - string_printf( - path_temp, - "%s%u%s", - string_get_cstr(base_path), - ++i, - InfraredApp::infrared_extension); - error = storage_common_stat(storage, string_get_cstr(path_temp), NULL); + new_name = make_full_name(InfraredApp::infrared_directory, name + std::to_string(++i)); + error = storage_common_stat(storage, new_name.c_str(), NULL); } while(error == FSE_OK); - string_clear(path_temp); - if(error == FSE_NOT_EXIST) { - string_cat_printf(name, "%u", i); + result_name = name + std::to_string(i); + } else { + result_name = std::string(); } } - string_clear(base_path); furi_record_close("storage"); + return result_name; } bool InfraredAppRemoteManager::add_button(const char* button_name, const InfraredAppSignal& signal) { @@ -77,23 +61,12 @@ bool InfraredAppRemoteManager::add_remote_with_button( const InfraredAppSignal& signal) { furi_check(button_name != nullptr); - string_t new_name; - string_init_set_str(new_name, default_remote_name); - - string_t new_path; - string_init_set_str(new_path, InfraredApp::infrared_directory); - - find_vacant_remote_name(new_name, new_path); - - string_cat_printf( - new_path, "/%s%s", string_get_cstr(new_name), InfraredApp::infrared_extension); - - remote = std::make_unique(new_path); - remote->name = std::string(string_get_cstr(new_name)); - - string_clear(new_path); - string_clear(new_name); + auto new_name = find_vacant_remote_name(default_remote_name); + if(new_name.empty()) { + return false; + } + remote = std::make_unique(InfraredApp::infrared_directory, new_name); return add_button(button_name, signal); } @@ -120,7 +93,8 @@ const InfraredAppSignal& InfraredAppRemoteManager::get_button_data(size_t index) bool InfraredAppRemoteManager::delete_remote() { Storage* storage = static_cast(furi_record_open("storage")); - FS_Error error = storage_common_remove(storage, string_get_cstr(remote->path)); + FS_Error error = + storage_common_remove(storage, make_full_name(remote->path, remote->name).c_str()); reset_remote(); furi_record_close("storage"); @@ -154,33 +128,22 @@ std::string InfraredAppRemoteManager::get_remote_name() { bool InfraredAppRemoteManager::rename_remote(const char* str) { furi_check(str != nullptr); furi_check(remote.get() != nullptr); - furi_check(!string_empty_p(remote->path)); if(!remote->name.compare(str)) { return true; } - string_t new_name; - string_init_set_str(new_name, str); - find_vacant_remote_name(new_name, remote->path); - - string_t new_path; - string_init_set(new_path, remote->path); - if(string_end_with_str_p(new_path, InfraredApp::infrared_extension)) { - size_t filename_start = string_search_rchar(new_path, '/'); - string_left(new_path, filename_start); + auto new_name = find_vacant_remote_name(str); + if(new_name.empty()) { + return false; } - string_cat_printf( - new_path, "/%s%s", string_get_cstr(new_name), InfraredApp::infrared_extension); Storage* storage = static_cast(furi_record_open("storage")); - FS_Error error = - storage_common_rename(storage, string_get_cstr(remote->path), string_get_cstr(new_path)); - remote->name = std::string(string_get_cstr(new_name)); - - string_clear(new_name); - string_clear(new_path); + std::string old_filename = make_full_name(remote->path, remote->name); + std::string new_filename = make_full_name(remote->path, new_name); + FS_Error error = storage_common_rename(storage, old_filename.c_str(), new_filename.c_str()); + remote->name = new_name; furi_record_close("storage"); return (error == FSE_OK || error == FSE_EXIST); @@ -208,8 +171,10 @@ bool InfraredAppRemoteManager::store(void) { FlipperFormat* ff = flipper_format_file_alloc(storage); - FURI_LOG_I("RemoteManager", "store file: \'%s\'", string_get_cstr(remote->path)); - result = flipper_format_file_open_always(ff, string_get_cstr(remote->path)); + FURI_LOG_I( + "RemoteManager", "store file: \'%s\'", make_full_name(remote->path, remote->name).c_str()); + result = + flipper_format_file_open_always(ff, make_full_name(remote->path, remote->name).c_str()); if(result) { result = flipper_format_write_header_cstr(ff, "IR signals file", 1); } @@ -227,13 +192,13 @@ bool InfraredAppRemoteManager::store(void) { return result; } -bool InfraredAppRemoteManager::load(string_t path) { +bool InfraredAppRemoteManager::load(const std::string& path, const std::string& remote_name) { bool result = false; Storage* storage = static_cast(furi_record_open("storage")); FlipperFormat* ff = flipper_format_file_alloc(storage); - FURI_LOG_I("RemoteManager", "load file: \'%s\'", string_get_cstr(path)); - result = flipper_format_file_open_existing(ff, string_get_cstr(path)); + FURI_LOG_I("RemoteManager", "load file: \'%s\'", make_full_name(path, remote_name).c_str()); + result = flipper_format_file_open_existing(ff, make_full_name(path, remote_name).c_str()); if(result) { string_t header; string_init(header); @@ -245,14 +210,7 @@ bool InfraredAppRemoteManager::load(string_t path) { string_clear(header); } if(result) { - string_t new_name; - string_init(new_name); - - remote = std::make_unique(path); - path_extract_filename(path, new_name, true); - remote->name = std::string(string_get_cstr(new_name)); - - string_clear(new_name); + remote = std::make_unique(path, remote_name); InfraredAppSignal signal; std::string signal_name; while(infrared_parser_read_signal(ff, signal, signal_name)) { diff --git a/applications/infrared/infrared_app_remote_manager.h b/applications/infrared/infrared_app_remote_manager.h index b6f0b170f..31557d54b 100644 --- a/applications/infrared/infrared_app_remote_manager.h +++ b/applications/infrared/infrared_app_remote_manager.h @@ -8,7 +8,6 @@ #include "infrared_app_signal.h" -#include "m-string.h" #include #include @@ -61,19 +60,17 @@ class InfraredAppRemote { /** Name of remote */ std::string name; /** Path to remote file */ - string_t path; + std::string path; public: /** Initialize new remote * * @param path - remote file path + * @param name - new remote name */ - InfraredAppRemote(string_t file_path) { - string_init_set(path, file_path); - } - - ~InfraredAppRemote() { - string_clear(path); + InfraredAppRemote(const std::string& path, const std::string& name) + : name(name) + , path(path) { } }; @@ -81,6 +78,12 @@ public: class InfraredAppRemoteManager { /** Remote instance. There can be 1 remote loaded at a time. */ std::unique_ptr remote; + /** Make full name from remote name + * + * @param remote_name name of remote + * @retval full name of remote on disk + */ + std::string make_full_name(const std::string& path, const std::string& remote_name) const; public: /** Restriction to button name length. Buttons larger are ignored. */ @@ -122,9 +125,9 @@ public: * incremented digit(2,3,4,etc) added to name and check repeated. * * @param name - suggested remote name - * @param path - remote file path + * @retval garanteed free remote name, prefixed with suggested */ - void find_vacant_remote_name(string_t name, string_t path); + std::string find_vacant_remote_name(const std::string& name); /** Get button list * @@ -182,8 +185,8 @@ public: /** Load data from disk into current remote * - * @param path - path to remote file + * @param name - name of remote to load * @retval true if success, false otherwise */ - bool load(string_t path); + bool load(const std::string& path, const std::string& name); }; diff --git a/applications/infrared/scene/infrared_app_scene_edit_rename.cpp b/applications/infrared/scene/infrared_app_scene_edit_rename.cpp index 761da49f3..dc63c64b1 100644 --- a/applications/infrared/scene/infrared_app_scene_edit_rename.cpp +++ b/applications/infrared/scene/infrared_app_scene_edit_rename.cpp @@ -1,6 +1,4 @@ #include "../infrared_app.h" -#include "m-string.h" -#include "toolbox/path.h" void InfraredAppSceneEditRename::on_enter(InfraredApp* app) { InfraredAppViewManager* view_manager = app->get_view_manager(); @@ -23,18 +21,9 @@ void InfraredAppSceneEditRename::on_enter(InfraredApp* app) { enter_name_length = InfraredAppRemoteManager::max_remote_name_length; text_input_set_header_text(text_input, "Name the remote"); - string_t folder_path; - string_init(folder_path); - - if(string_end_with_str_p(app->file_path, InfraredApp::infrared_extension)) { - path_extract_dirname(string_get_cstr(app->file_path), folder_path); - } - ValidatorIsFile* validator_is_file = validator_is_file_alloc_init( - string_get_cstr(folder_path), app->infrared_extension, remote_name.c_str()); + app->infrared_directory, app->infrared_extension, remote_name.c_str()); text_input_set_validator(text_input, validator_is_file_callback, validator_is_file); - - string_clear(folder_path); } text_input_set_result_callback( diff --git a/applications/infrared/scene/infrared_app_scene_remote_list.cpp b/applications/infrared/scene/infrared_app_scene_remote_list.cpp index c72acb6e8..f59ff3e91 100644 --- a/applications/infrared/scene/infrared_app_scene_remote_list.cpp +++ b/applications/infrared/scene/infrared_app_scene_remote_list.cpp @@ -1,5 +1,4 @@ #include "../infrared_app.h" -#include "assets_icons.h" #include "infrared/infrared_app_event.h" #include @@ -9,6 +8,11 @@ void InfraredAppSceneRemoteList::on_enter(InfraredApp* app) { bool result = false; bool file_select_result; auto remote_manager = app->get_remote_manager(); + auto last_selected_remote = remote_manager->get_remote_name(); + const char* last_selected_remote_name = + last_selected_remote.size() ? last_selected_remote.c_str() : nullptr; + auto filename_ts = + std::make_unique(InfraredAppRemoteManager::max_remote_name_length); DialogsApp* dialogs = app->get_dialogs(); InfraredAppViewManager* view_manager = app->get_view_manager(); @@ -16,17 +20,16 @@ void InfraredAppSceneRemoteList::on_enter(InfraredApp* app) { button_menu_reset(button_menu); view_manager->switch_to(InfraredAppViewManager::ViewId::ButtonMenu); - file_select_result = dialog_file_browser_show( + file_select_result = dialog_file_select_show( dialogs, - app->file_path, - app->file_path, + InfraredApp::infrared_directory, InfraredApp::infrared_extension, - true, - &I_ir_10px, - true); + filename_ts->text, + filename_ts->text_size, + last_selected_remote_name); if(file_select_result) { - if(remote_manager->load(app->file_path)) { + if(remote_manager->load(InfraredApp::infrared_directory, std::string(filename_ts->text))) { app->switch_to_next_scene(InfraredApp::Scene::Remote); result = true; } diff --git a/applications/infrared/scene/infrared_app_scene_start.cpp b/applications/infrared/scene/infrared_app_scene_start.cpp index 5efdce7a0..c42ab9fed 100644 --- a/applications/infrared/scene/infrared_app_scene_start.cpp +++ b/applications/infrared/scene/infrared_app_scene_start.cpp @@ -26,8 +26,6 @@ void InfraredAppSceneStart::on_enter(InfraredApp* app) { submenu, "Learn New Remote", SubmenuIndexLearnNewRemote, submenu_callback, app); submenu_add_item(submenu, "Saved Remotes", SubmenuIndexSavedRemotes, submenu_callback, app); submenu_set_selected_item(submenu, submenu_item_selected); - - string_set_str(app->file_path, InfraredApp::infrared_directory); submenu_item_selected = 0; view_manager->switch_to(InfraredAppViewManager::ViewId::Submenu); diff --git a/applications/lfrfid/lfrfid_app.cpp b/applications/lfrfid/lfrfid_app.cpp index 4027a07ea..c2274234b 100644 --- a/applications/lfrfid/lfrfid_app.cpp +++ b/applications/lfrfid/lfrfid_app.cpp @@ -1,7 +1,4 @@ #include "lfrfid_app.h" -#include "assets_icons.h" -#include "furi/common_defines.h" -#include "m-string.h" #include "scene/lfrfid_app_scene_start.h" #include "scene/lfrfid_app_scene_read.h" #include "scene/lfrfid_app_scene_read_success.h" @@ -34,11 +31,9 @@ LfRfidApp::LfRfidApp() , storage{"storage"} , dialogs{"dialogs"} , text_store(40) { - string_init_set_str(file_path, app_folder); } LfRfidApp::~LfRfidApp() { - string_clear(file_path); } void LfRfidApp::run(void* _args) { @@ -47,8 +42,7 @@ void LfRfidApp::run(void* _args) { make_app_folder(); if(strlen(args)) { - string_set_str(file_path, args); - load_key_data(file_path, &worker.key); + load_key_data(args, &worker.key); scene_controller.add_scene(SceneType::Emulate, new LfRfidAppSceneEmulate()); scene_controller.process(100, SceneType::Emulate); } else { @@ -75,49 +69,65 @@ void LfRfidApp::run(void* _args) { } bool LfRfidApp::save_key(RfidKey* key) { + string_t file_name; bool result = false; make_app_folder(); - if(string_end_with_str_p(file_path, app_extension)) { - size_t filename_start = string_search_rchar(file_path, '/'); - string_left(file_path, filename_start); - } + 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); - string_cat_printf(file_path, "/%s%s", key->get_name(), app_extension); - - result = save_key_data(file_path, key); return result; } bool LfRfidApp::load_key_from_file_select(bool need_restore) { - if(!need_restore) { - string_set_str(file_path, app_folder); - } + TextStore* filename_ts = new TextStore(64); + bool result = false; - bool result = dialog_file_browser_show( - dialogs, file_path, file_path, app_extension, true, &I_125_10px, true); + if(need_restore) { + result = dialog_file_select_show( + dialogs, + app_folder, + app_extension, + filename_ts->text, + filename_ts->text_size, + worker.key.get_name()); + } else { + result = dialog_file_select_show( + dialogs, app_folder, app_extension, filename_ts->text, filename_ts->text_size, NULL); + } if(result) { - result = load_key_data(file_path, &worker.key); + 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) { - UNUSED(key); - return storage_simply_remove(storage, string_get_cstr(file_path)); + string_t file_name; + bool result = false; + + string_init_printf(file_name, "%s/%s%s", app_folder, key->get_name(), app_extension); + result = storage_simply_remove(storage, string_get_cstr(file_name)); + string_clear(file_name); + + return result; } -bool LfRfidApp::load_key_data(string_t path, RfidKey* key) { +bool LfRfidApp::load_key_data(const char* path, RfidKey* key) { FlipperFormat* file = flipper_format_file_alloc(storage); bool result = false; string_t str_result; string_init(str_result); do { - if(!flipper_format_file_open_existing(file, string_get_cstr(path))) break; + if(!flipper_format_file_open_existing(file, path)) break; // header uint32_t version; @@ -139,7 +149,7 @@ bool LfRfidApp::load_key_data(string_t path, RfidKey* key) { break; loaded_key.set_data(key_data, loaded_key.get_type_data_count()); - path_extract_filename(path, str_result, true); + path_extract_filename_no_ext(path, str_result); loaded_key.set_name(string_get_cstr(str_result)); *key = loaded_key; @@ -156,12 +166,12 @@ bool LfRfidApp::load_key_data(string_t path, RfidKey* key) { return result; } -bool LfRfidApp::save_key_data(string_t path, RfidKey* key) { +bool LfRfidApp::save_key_data(const char* path, RfidKey* key) { FlipperFormat* file = flipper_format_file_alloc(storage); bool result = false; do { - if(!flipper_format_file_open_always(file, string_get_cstr(path))) break; + if(!flipper_format_file_open_always(file, path)) break; if(!flipper_format_write_header_cstr(file, app_filetype, 1)) break; if(!flipper_format_write_comment_cstr(file, "Key type can be EM4100, H10301 or I40134")) break; diff --git a/applications/lfrfid/lfrfid_app.h b/applications/lfrfid/lfrfid_app.h index 3f8209a1d..dddfb753a 100644 --- a/applications/lfrfid/lfrfid_app.h +++ b/applications/lfrfid/lfrfid_app.h @@ -1,5 +1,4 @@ #pragma once -#include "m-string.h" #include #include @@ -77,8 +76,6 @@ public: TextStore text_store; - string_t file_path; - void run(void* args); static const char* app_folder; @@ -89,8 +86,8 @@ public: bool load_key_from_file_select(bool need_restore); bool delete_key(RfidKey* key); - bool load_key_data(string_t path, RfidKey* key); - bool save_key_data(string_t path, RfidKey* key); + bool load_key_data(const char* path, RfidKey* key); + bool save_key_data(const char* path, RfidKey* key); void make_app_folder(); }; diff --git a/applications/lfrfid/scene/lfrfid_app_scene_save_name.cpp b/applications/lfrfid/scene/lfrfid_app_scene_save_name.cpp index d7ba2c9ed..d460724e2 100644 --- a/applications/lfrfid/scene/lfrfid_app_scene_save_name.cpp +++ b/applications/lfrfid/scene/lfrfid_app_scene_save_name.cpp @@ -1,14 +1,11 @@ #include "lfrfid_app_scene_save_name.h" -#include "m-string.h" #include -#include void LfRfidAppSceneSaveName::on_enter(LfRfidApp* app, bool /* need_restore */) { const char* key_name = app->worker.key.get_name(); bool key_name_empty = !strcmp(key_name, ""); if(key_name_empty) { - string_set_str(app->file_path, app->app_folder); set_random_name(app->text_store.text, app->text_store.text_size); } else { app->text_store.set("%s", key_name); @@ -24,17 +21,10 @@ void LfRfidAppSceneSaveName::on_enter(LfRfidApp* app, bool /* need_restore */) { app->worker.key.get_name_length(), key_name_empty); - string_t folder_path; - string_init(folder_path); - - path_extract_dirname(string_get_cstr(app->file_path), folder_path); - ValidatorIsFile* validator_is_file = - validator_is_file_alloc_init(string_get_cstr(folder_path), app->app_extension, key_name); + validator_is_file_alloc_init(app->app_folder, app->app_extension, key_name); text_input->set_validator(validator_is_file_callback, validator_is_file); - string_clear(folder_path); - app->view_controller.switch_to(); } diff --git a/applications/music_player/music_player.c b/applications/music_player/music_player.c index 9b5dda0fa..6dfef4920 100644 --- a/applications/music_player/music_player.c +++ b/applications/music_player/music_player.c @@ -1,5 +1,3 @@ -#include "assets_icons.h" -#include "m-string.h" #include #include @@ -300,23 +298,23 @@ int32_t music_player_app(void* p) { if(p) { string_cat_str(file_path, p); } else { - string_set_str(file_path, MUSIC_PLAYER_APP_PATH_FOLDER); - + char file_name[256] = {0}; DialogsApp* dialogs = furi_record_open("dialogs"); - bool res = dialog_file_browser_show( + bool res = dialog_file_select_show( dialogs, - file_path, - file_path, + MUSIC_PLAYER_APP_PATH_FOLDER, MUSIC_PLAYER_APP_EXTENSION, - true, - &I_music_10px, - false); - + file_name, + 255, + NULL); furi_record_close("dialogs"); if(!res) { FURI_LOG_E(TAG, "No file selected"); break; } + string_cat_str(file_path, MUSIC_PLAYER_APP_PATH_FOLDER); + string_cat_str(file_path, "/"); + string_cat_str(file_path, file_name); } if(!music_player_worker_load(music_player->worker, string_get_cstr(file_path))) { diff --git a/applications/nfc/nfc_device.c b/applications/nfc/nfc_device.c index ca3fc45aa..63f0c3ccb 100644 --- a/applications/nfc/nfc_device.c +++ b/applications/nfc/nfc_device.c @@ -1,6 +1,4 @@ #include "nfc_device.h" -#include "assets_icons.h" -#include "m-string.h" #include "nfc_types.h" #include @@ -16,7 +14,6 @@ NfcDevice* nfc_device_alloc() { NfcDevice* nfc_dev = malloc(sizeof(NfcDevice)); nfc_dev->storage = furi_record_open("storage"); nfc_dev->dialogs = furi_record_open("dialogs"); - string_init(nfc_dev->load_path); return nfc_dev; } @@ -25,7 +22,6 @@ void nfc_device_free(NfcDevice* nfc_dev) { nfc_device_clear(nfc_dev); furi_record_close("storage"); furi_record_close("dialogs"); - string_clear(nfc_dev->load_path); free(nfc_dev); } @@ -734,24 +730,11 @@ void nfc_device_set_name(NfcDevice* dev, const char* name) { strlcpy(dev->dev_name, name, NFC_DEV_NAME_MAX_LEN); } -static void nfc_device_get_path_without_ext(string_t orig_path, string_t shadow_path) { - // TODO: this won't work if there is ".nfc" anywhere in the path other than - // at the end - size_t ext_start = string_search_str(orig_path, NFC_APP_EXTENSION); - string_set_n(shadow_path, orig_path, 0, ext_start); -} - -static void nfc_device_get_shadow_path(string_t orig_path, string_t shadow_path) { - nfc_device_get_path_without_ext(orig_path, shadow_path); - string_cat_printf(shadow_path, "%s", NFC_APP_SHADOW_EXTENSION); -} - static bool nfc_device_save_file( NfcDevice* dev, const char* dev_name, const char* folder, - const char* extension, - bool use_load_path) { + const char* extension) { furi_assert(dev); bool saved = false; @@ -761,19 +744,10 @@ static bool nfc_device_save_file( string_init(temp_str); do { - if(use_load_path && !string_empty_p(dev->load_path)) { - // Get directory name - path_extract_dirname(string_get_cstr(dev->load_path), temp_str); - // Create nfc directory if necessary - if(!storage_simply_mkdir(dev->storage, string_get_cstr(temp_str))) break; - // Make path to file to save - string_cat_printf(temp_str, "/%s%s", dev_name, extension); - } else { - // Create nfc directory if necessary - if(!storage_simply_mkdir(dev->storage, NFC_APP_FOLDER)) break; - // First remove nfc device file if it was saved - string_printf(temp_str, "%s/%s%s", folder, dev_name, extension); - } + // Create nfc directory if necessary + if(!storage_simply_mkdir(dev->storage, NFC_APP_FOLDER)) break; + // First remove nfc device file if it was saved + string_printf(temp_str, "%s/%s%s", folder, dev_name, extension); // Open file if(!flipper_format_file_open_always(file, string_get_cstr(temp_str))) break; // Write header @@ -812,12 +786,12 @@ static bool nfc_device_save_file( } bool nfc_device_save(NfcDevice* dev, const char* dev_name) { - return nfc_device_save_file(dev, dev_name, NFC_APP_FOLDER, NFC_APP_EXTENSION, true); + return nfc_device_save_file(dev, dev_name, NFC_APP_FOLDER, NFC_APP_EXTENSION); } bool nfc_device_save_shadow(NfcDevice* dev, const char* dev_name) { dev->shadow_file_exist = true; - return nfc_device_save_file(dev, dev_name, NFC_APP_FOLDER, NFC_APP_SHADOW_EXTENSION, true); + return nfc_device_save_file(dev, dev_name, NFC_APP_FOLDER, NFC_APP_SHADOW_EXTENSION); } static bool nfc_device_load_data(NfcDevice* dev, string_t path) { @@ -831,7 +805,9 @@ static bool nfc_device_load_data(NfcDevice* dev, string_t path) { do { // Check existance of shadow file - nfc_device_get_shadow_path(path, temp_str); + size_t ext_start = string_search_str(path, NFC_APP_EXTENSION); + string_set_n(temp_str, path, 0, ext_start); + string_cat_printf(temp_str, "%s", NFC_APP_SHADOW_EXTENSION); dev->shadow_file_exist = storage_common_stat(dev->storage, string_get_cstr(temp_str), NULL) == FSE_OK; // Open shadow file if it exists. If not - open original @@ -888,16 +864,15 @@ bool nfc_device_load(NfcDevice* dev, const char* file_path) { furi_assert(file_path); // Load device data - string_set_str(dev->load_path, file_path); - bool dev_load = nfc_device_load_data(dev, dev->load_path); + string_t path; + string_init_set_str(path, file_path); + bool dev_load = nfc_device_load_data(dev, path); if(dev_load) { // Set device name - string_t filename; - string_init(filename); - path_extract_filename_no_ext(file_path, filename); - nfc_device_set_name(dev, string_get_cstr(filename)); - string_clear(filename); + path_extract_filename_no_ext(file_path, path); + nfc_device_set_name(dev, string_get_cstr(path)); } + string_clear(path); return dev_load; } @@ -905,19 +880,23 @@ bool nfc_device_load(NfcDevice* dev, const char* file_path) { bool nfc_file_select(NfcDevice* dev) { furi_assert(dev); - // Input events and views are managed by file_browser - bool res = dialog_file_browser_show( - dev->dialogs, dev->load_path, dev->load_path, NFC_APP_EXTENSION, true, &I_Nfc_10px, true); + // Input events and views are managed by file_select + bool res = dialog_file_select_show( + dev->dialogs, + NFC_APP_FOLDER, + NFC_APP_EXTENSION, + dev->file_name, + sizeof(dev->file_name), + dev->dev_name); if(res) { - string_t filename; - string_init(filename); - path_extract_filename(dev->load_path, filename, true); - strncpy(dev->dev_name, string_get_cstr(filename), NFC_DEV_NAME_MAX_LEN); - res = nfc_device_load_data(dev, dev->load_path); + string_t dev_str; + // Get key file path + string_init_printf(dev_str, "%s/%s%s", NFC_APP_FOLDER, dev->file_name, NFC_APP_EXTENSION); + res = nfc_device_load_data(dev, dev_str); if(res) { - nfc_device_set_name(dev, dev->dev_name); + nfc_device_set_name(dev, dev->file_name); } - string_clear(filename); + string_clear(dev_str); } return res; @@ -935,10 +914,9 @@ void nfc_device_clear(NfcDevice* dev) { nfc_device_data_clear(&dev->dev_data); memset(&dev->dev_data, 0, sizeof(dev->dev_data)); dev->format = NfcDeviceSaveFormatUid; - string_set_str(dev->load_path, NFC_APP_FOLDER); } -bool nfc_device_delete(NfcDevice* dev, bool use_load_path) { +bool nfc_device_delete(NfcDevice* dev) { furi_assert(dev); bool deleted = false; @@ -947,20 +925,12 @@ bool nfc_device_delete(NfcDevice* dev, bool use_load_path) { do { // Delete original file - if(use_load_path && !string_empty_p(dev->load_path)) { - string_set(file_path, dev->load_path); - } else { - string_printf(file_path, "%s/%s%s", NFC_APP_FOLDER, dev->dev_name, NFC_APP_EXTENSION); - } + string_init_printf(file_path, "%s/%s%s", NFC_APP_FOLDER, dev->dev_name, NFC_APP_EXTENSION); if(!storage_simply_remove(dev->storage, string_get_cstr(file_path))) break; // Delete shadow file if it exists if(dev->shadow_file_exist) { - if(use_load_path && !string_empty_p(dev->load_path)) { - nfc_device_get_shadow_path(dev->load_path, file_path); - } else { - string_printf( - file_path, "%s/%s%s", NFC_APP_FOLDER, dev->dev_name, NFC_APP_SHADOW_EXTENSION); - } + string_printf( + file_path, "%s/%s%s", NFC_APP_FOLDER, dev->dev_name, NFC_APP_SHADOW_EXTENSION); if(!storage_simply_remove(dev->storage, string_get_cstr(file_path))) break; } deleted = true; @@ -974,29 +944,19 @@ bool nfc_device_delete(NfcDevice* dev, bool use_load_path) { return deleted; } -bool nfc_device_restore(NfcDevice* dev, bool use_load_path) { +bool nfc_device_restore(NfcDevice* dev) { furi_assert(dev); furi_assert(dev->shadow_file_exist); bool restored = false; string_t path; - string_init(path); - do { - if(use_load_path && !string_empty_p(dev->load_path)) { - nfc_device_get_shadow_path(dev->load_path, path); - } else { - string_printf( - path, "%s/%s%s", NFC_APP_FOLDER, dev->dev_name, NFC_APP_SHADOW_EXTENSION); - } + string_init_printf( + path, "%s/%s%s", NFC_APP_FOLDER, dev->dev_name, NFC_APP_SHADOW_EXTENSION); if(!storage_simply_remove(dev->storage, string_get_cstr(path))) break; dev->shadow_file_exist = false; - if(use_load_path && !string_empty_p(dev->load_path)) { - string_set(path, dev->load_path); - } else { - string_printf(path, "%s/%s%s", NFC_APP_FOLDER, dev->dev_name, NFC_APP_EXTENSION); - } + string_printf(path, "%s/%s%s", NFC_APP_FOLDER, dev->dev_name, NFC_APP_EXTENSION); if(!nfc_device_load_data(dev, path)) break; restored = true; } while(0); diff --git a/applications/nfc/nfc_device.h b/applications/nfc/nfc_device.h index 400f6c363..215f637fa 100644 --- a/applications/nfc/nfc_device.h +++ b/applications/nfc/nfc_device.h @@ -12,6 +12,7 @@ #include #define NFC_DEV_NAME_MAX_LEN 22 +#define NFC_FILE_NAME_MAX_LEN 120 #define NFC_READER_DATA_MAX_SIZE 64 #define NFC_APP_FOLDER "/any/nfc" @@ -56,7 +57,7 @@ typedef struct { DialogsApp* dialogs; NfcDeviceData dev_data; char dev_name[NFC_DEV_NAME_MAX_LEN + 1]; - string_t load_path; + char file_name[NFC_FILE_NAME_MAX_LEN]; NfcDeviceSaveFormat format; bool shadow_file_exist; } NfcDevice; @@ -79,6 +80,6 @@ void nfc_device_data_clear(NfcDeviceData* dev); void nfc_device_clear(NfcDevice* dev); -bool nfc_device_delete(NfcDevice* dev, bool use_load_path); +bool nfc_device_delete(NfcDevice* dev); -bool nfc_device_restore(NfcDevice* dev, bool use_load_path); +bool nfc_device_restore(NfcDevice* dev); diff --git a/applications/nfc/scenes/nfc_scene_delete.c b/applications/nfc/scenes/nfc_scene_delete.c index 1946b9290..e8ba3e440 100755 --- a/applications/nfc/scenes/nfc_scene_delete.c +++ b/applications/nfc/scenes/nfc_scene_delete.c @@ -73,7 +73,7 @@ bool nfc_scene_delete_on_event(void* context, SceneManagerEvent event) { if(event.event == GuiButtonTypeLeft) { return scene_manager_previous_scene(nfc->scene_manager); } else if(event.event == GuiButtonTypeRight) { - if(nfc_device_delete(nfc->dev, true)) { + if(nfc_device_delete(nfc->dev)) { scene_manager_next_scene(nfc->scene_manager, NfcSceneDeleteSuccess); } else { scene_manager_search_and_switch_to_previous_scene( diff --git a/applications/nfc/scenes/nfc_scene_save_name.c b/applications/nfc/scenes/nfc_scene_save_name.c index d5e05472a..e95c97eb4 100755 --- a/applications/nfc/scenes/nfc_scene_save_name.c +++ b/applications/nfc/scenes/nfc_scene_save_name.c @@ -1,8 +1,6 @@ #include "../nfc_i.h" -#include "m-string.h" #include #include -#include void nfc_scene_save_name_text_input_callback(void* context) { Nfc* nfc = context; @@ -31,22 +29,11 @@ void nfc_scene_save_name_on_enter(void* context) { NFC_DEV_NAME_MAX_LEN, dev_name_empty); - string_t folder_path; - string_init(folder_path); - - if(string_end_with_str_p(nfc->dev->load_path, NFC_APP_EXTENSION)) { - path_extract_dirname(string_get_cstr(nfc->dev->load_path), folder_path); - } else { - string_set_str(folder_path, NFC_APP_FOLDER); - } - - ValidatorIsFile* validator_is_file = validator_is_file_alloc_init( - string_get_cstr(folder_path), NFC_APP_EXTENSION, nfc->dev->dev_name); + ValidatorIsFile* validator_is_file = + validator_is_file_alloc_init(NFC_APP_FOLDER, NFC_APP_EXTENSION, nfc->dev->dev_name); text_input_set_validator(text_input, validator_is_file_callback, validator_is_file); view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewTextInput); - - string_clear(folder_path); } bool nfc_scene_save_name_on_event(void* context, SceneManagerEvent event) { @@ -56,7 +43,7 @@ bool nfc_scene_save_name_on_event(void* context, SceneManagerEvent event) { if(event.type == SceneManagerEventTypeCustom) { if(event.event == NfcCustomEventTextInputDone) { if(strcmp(nfc->dev->dev_name, "")) { - nfc_device_delete(nfc->dev, true); + nfc_device_delete(nfc->dev); } if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSetUid)) { nfc->dev->dev_data.nfc_data = nfc->dev_edit_data; diff --git a/applications/nfc/scenes/nfc_scene_save_success.c b/applications/nfc/scenes/nfc_scene_save_success.c index 5c15a509a..985897a6d 100644 --- a/applications/nfc/scenes/nfc_scene_save_success.c +++ b/applications/nfc/scenes/nfc_scene_save_success.c @@ -30,6 +30,9 @@ bool nfc_scene_save_success_on_event(void* context, SceneManagerEvent event) { if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneCardMenu)) { consumed = scene_manager_search_and_switch_to_previous_scene( nfc->scene_manager, NfcSceneCardMenu); + } else if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSetType)) { + consumed = scene_manager_search_and_switch_to_another_scene( + nfc->scene_manager, NfcSceneFileSelect); } else if(scene_manager_has_previous_scene( nfc->scene_manager, NfcSceneMifareDesfireMenu)) { consumed = scene_manager_search_and_switch_to_previous_scene( diff --git a/applications/nfc/scenes/nfc_scene_saved_menu.c b/applications/nfc/scenes/nfc_scene_saved_menu.c index f2b2dea37..1b435ccd1 100644 --- a/applications/nfc/scenes/nfc_scene_saved_menu.c +++ b/applications/nfc/scenes/nfc_scene_saved_menu.c @@ -78,7 +78,7 @@ bool nfc_scene_saved_menu_on_event(void* context, SceneManagerEvent event) { scene_manager_next_scene(nfc->scene_manager, NfcSceneDeviceInfo); consumed = true; } else if(event.event == SubmenuIndexRestoreOriginal) { - if(!nfc_device_restore(nfc->dev, true)) { + if(!nfc_device_restore(nfc->dev)) { scene_manager_search_and_switch_to_previous_scene( nfc->scene_manager, NfcSceneStart); } else { diff --git a/applications/nfc/scenes/nfc_scene_set_type.c b/applications/nfc/scenes/nfc_scene_set_type.c index 0fe63424f..0dbb4f7ea 100755 --- a/applications/nfc/scenes/nfc_scene_set_type.c +++ b/applications/nfc/scenes/nfc_scene_set_type.c @@ -1,5 +1,4 @@ #include "../nfc_i.h" -#include "m-string.h" enum SubmenuIndex { SubmenuIndexNFCA4, @@ -17,7 +16,6 @@ void nfc_scene_set_type_on_enter(void* context) { Submenu* submenu = nfc->submenu; // Clear device name nfc_device_set_name(nfc->dev, ""); - string_set_str(nfc->dev->load_path, ""); submenu_add_item( submenu, "NFC-A 7-bytes UID", SubmenuIndexNFCA7, nfc_scene_set_type_submenu_callback, nfc); submenu_add_item( diff --git a/applications/scened_app_example/scene/scened_app_scene_byte_input.cpp b/applications/scened_app_example/scene/scened_app_scene_byte_input.cpp new file mode 100644 index 000000000..200252ad6 --- /dev/null +++ b/applications/scened_app_example/scene/scened_app_scene_byte_input.cpp @@ -0,0 +1,35 @@ +#include "scened_app_scene_byte_input.h" + +void ScenedAppSceneByteInput::on_enter(ScenedApp* app, bool /* need_restore */) { + ByteInputVM* byte_input = app->view_controller; + auto callback = cbc::obtain_connector(this, &ScenedAppSceneByteInput::result_callback); + + byte_input->set_result_callback(callback, NULL, app, data, 4); + byte_input->set_header_text("Enter the key"); + + app->view_controller.switch_to(); +} + +bool ScenedAppSceneByteInput::on_event(ScenedApp* app, ScenedApp::Event* event) { + bool consumed = false; + + if(event->type == ScenedApp::EventType::ByteEditResult) { + app->scene_controller.switch_to_previous_scene(); + consumed = true; + } + + return consumed; +} + +void ScenedAppSceneByteInput::on_exit(ScenedApp* app) { + app->view_controller.get()->clean(); +} + +void ScenedAppSceneByteInput::result_callback(void* context) { + ScenedApp* app = static_cast(context); + ScenedApp::Event event; + + event.type = ScenedApp::EventType::ByteEditResult; + + app->view_controller.send_event(&event); +} diff --git a/applications/scened_app_example/scene/scened_app_scene_byte_input.h b/applications/scened_app_example/scene/scened_app_scene_byte_input.h new file mode 100644 index 000000000..0f0b02acb --- /dev/null +++ b/applications/scened_app_example/scene/scened_app_scene_byte_input.h @@ -0,0 +1,19 @@ +#pragma once +#include "../scened_app.h" + +class ScenedAppSceneByteInput : public GenericScene { +public: + void on_enter(ScenedApp* app, bool need_restore) final; + bool on_event(ScenedApp* app, ScenedApp::Event* event) final; + void on_exit(ScenedApp* app) final; + +private: + void result_callback(void* context); + + uint8_t data[4] = { + 0x01, + 0xA2, + 0xF4, + 0xD3, + }; +}; diff --git a/applications/scened_app_example/scene/scened_app_scene_start.cpp b/applications/scened_app_example/scene/scened_app_scene_start.cpp new file mode 100644 index 000000000..5538962ba --- /dev/null +++ b/applications/scened_app_example/scene/scened_app_scene_start.cpp @@ -0,0 +1,47 @@ +#include "scened_app_scene_start.h" + +typedef enum { + SubmenuByteInput, +} SubmenuIndex; + +void ScenedAppSceneStart::on_enter(ScenedApp* app, bool need_restore) { + auto submenu = app->view_controller.get(); + auto callback = cbc::obtain_connector(this, &ScenedAppSceneStart::submenu_callback); + + submenu->add_item("Byte Input", SubmenuByteInput, callback, app); + + if(need_restore) { + submenu->set_selected_item(submenu_item_selected); + } + app->view_controller.switch_to(); +} + +bool ScenedAppSceneStart::on_event(ScenedApp* app, ScenedApp::Event* event) { + bool consumed = false; + + if(event->type == ScenedApp::EventType::MenuSelected) { + submenu_item_selected = event->payload.menu_index; + switch(event->payload.menu_index) { + case SubmenuByteInput: + app->scene_controller.switch_to_next_scene(ScenedApp::SceneType::ByteInputScene); + break; + } + consumed = true; + } + + return consumed; +} + +void ScenedAppSceneStart::on_exit(ScenedApp* app) { + app->view_controller.get()->clean(); +} + +void ScenedAppSceneStart::submenu_callback(void* context, uint32_t index) { + ScenedApp* app = static_cast(context); + ScenedApp::Event event; + + event.type = ScenedApp::EventType::MenuSelected; + event.payload.menu_index = index; + + app->view_controller.send_event(&event); +} diff --git a/applications/scened_app_example/scene/scened_app_scene_start.h b/applications/scened_app_example/scene/scened_app_scene_start.h new file mode 100644 index 000000000..8324a20fd --- /dev/null +++ b/applications/scened_app_example/scene/scened_app_scene_start.h @@ -0,0 +1,13 @@ +#pragma once +#include "../scened_app.h" + +class ScenedAppSceneStart : public GenericScene { +public: + void on_enter(ScenedApp* app, bool need_restore) final; + bool on_event(ScenedApp* app, ScenedApp::Event* event) final; + void on_exit(ScenedApp* app) final; + +private: + void submenu_callback(void* context, uint32_t index); + uint32_t submenu_item_selected = 0; +}; diff --git a/applications/scened_app_example/scened_app.cpp b/applications/scened_app_example/scened_app.cpp new file mode 100644 index 000000000..64040b22d --- /dev/null +++ b/applications/scened_app_example/scened_app.cpp @@ -0,0 +1,20 @@ +#include "scened_app.h" +#include "scene/scened_app_scene_start.h" +#include "scene/scened_app_scene_byte_input.h" + +ScenedApp::ScenedApp() + : scene_controller{this} + , text_store{128} + , notification{"notification"} { +} + +ScenedApp::~ScenedApp() { +} + +void ScenedApp::run() { + scene_controller.add_scene(SceneType::Start, new ScenedAppSceneStart()); + scene_controller.add_scene(SceneType::ByteInputScene, new ScenedAppSceneByteInput()); + + notification_message(notification, &sequence_blink_green_10); + scene_controller.process(100); +} diff --git a/applications/scened_app_example/scened_app.h b/applications/scened_app_example/scened_app.h new file mode 100644 index 000000000..c6cecb477 --- /dev/null +++ b/applications/scened_app_example/scened_app.h @@ -0,0 +1,47 @@ +#pragma once +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +#include + +class ScenedApp { +public: + enum class EventType : uint8_t { + GENERIC_EVENT_ENUM_VALUES, + MenuSelected, + ByteEditResult, + }; + + enum class SceneType : uint8_t { + GENERIC_SCENE_ENUM_VALUES, + ByteInputScene, + }; + + class Event { + public: + union { + int32_t menu_index; + } payload; + + EventType type; + }; + + SceneController, ScenedApp> scene_controller; + TextStore text_store; + ViewController view_controller; + RecordController notification; + + ~ScenedApp(); + ScenedApp(); + + void run(); +}; diff --git a/applications/scened_app_example/scened_app_launcher.cpp b/applications/scened_app_example/scened_app_launcher.cpp new file mode 100644 index 000000000..3d0bdfcaf --- /dev/null +++ b/applications/scened_app_example/scened_app_launcher.cpp @@ -0,0 +1,11 @@ +#include "scened_app.h" + +// app enter function +extern "C" int32_t scened_app(void* p) { + UNUSED(p); + ScenedApp* app = new ScenedApp(); + app->run(); + delete app; + + return 0; +} diff --git a/applications/subghz/scenes/subghz_scene_delete.c b/applications/subghz/scenes/subghz_scene_delete.c index 43151de20..83d6d4751 100644 --- a/applications/subghz/scenes/subghz_scene_delete.c +++ b/applications/subghz/scenes/subghz_scene_delete.c @@ -49,7 +49,7 @@ bool subghz_scene_delete_on_event(void* context, SceneManagerEvent event) { SubGhz* subghz = context; if(event.type == SceneManagerEventTypeCustom) { if(event.event == SubGhzCustomEventSceneDelete) { - string_set(subghz->file_path_tmp, subghz->file_path); + strncpy(subghz->file_path_tmp, subghz->file_path, SUBGHZ_MAX_LEN_NAME); if(subghz_delete_file(subghz)) { scene_manager_next_scene(subghz->scene_manager, SubGhzSceneDeleteSuccess); } else { diff --git a/applications/subghz/scenes/subghz_scene_delete_raw.c b/applications/subghz/scenes/subghz_scene_delete_raw.c index a20968d5b..03f0eb81b 100644 --- a/applications/subghz/scenes/subghz_scene_delete_raw.c +++ b/applications/subghz/scenes/subghz_scene_delete_raw.c @@ -24,7 +24,7 @@ void subghz_scene_delete_raw_on_enter(void* context) { char delete_str[SUBGHZ_MAX_LEN_NAME + 16]; string_t file_name; string_init(file_name); - path_extract_filename(subghz->file_path, file_name, true); + path_extract_filename_no_ext(subghz->file_path, file_name); snprintf(delete_str, sizeof(delete_str), "\e#Delete %s?\e#", string_get_cstr(file_name)); string_clear(file_name); @@ -61,7 +61,7 @@ bool subghz_scene_delete_raw_on_event(void* context, SceneManagerEvent event) { SubGhz* subghz = context; if(event.type == SceneManagerEventTypeCustom) { if(event.event == SubGhzCustomEventSceneDeleteRAW) { - string_set(subghz->file_path_tmp, subghz->file_path); + strncpy(subghz->file_path_tmp, subghz->file_path, SUBGHZ_MAX_LEN_NAME); if(subghz_delete_file(subghz)) { scene_manager_next_scene(subghz->scene_manager, SubGhzSceneDeleteSuccess); } else { diff --git a/applications/subghz/scenes/subghz_scene_more_raw.c b/applications/subghz/scenes/subghz_scene_more_raw.c index a5bade927..54bd08158 100644 --- a/applications/subghz/scenes/subghz_scene_more_raw.c +++ b/applications/subghz/scenes/subghz_scene_more_raw.c @@ -45,7 +45,7 @@ bool subghz_scene_more_raw_on_event(void* context, SceneManagerEvent event) { scene_manager_next_scene(subghz->scene_manager, SubGhzSceneDeleteRAW); return true; } else if(event.event == SubmenuIndexEdit) { - string_reset(subghz->file_path_tmp); + memset(subghz->file_path_tmp, 0, sizeof(subghz->file_path_tmp)); scene_manager_set_scene_state( subghz->scene_manager, SubGhzSceneMoreRAW, SubmenuIndexEdit); scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSaveName); diff --git a/applications/subghz/scenes/subghz_scene_read_raw.c b/applications/subghz/scenes/subghz_scene_read_raw.c index 628a5f1f0..5323bfe58 100644 --- a/applications/subghz/scenes/subghz_scene_read_raw.c +++ b/applications/subghz/scenes/subghz_scene_read_raw.c @@ -25,7 +25,7 @@ bool subghz_scene_read_raw_update_filename(SubGhz* subghz) { break; } - string_set(subghz->file_path, temp_str); + strncpy(subghz->file_path, string_get_cstr(temp_str), SUBGHZ_MAX_LEN_NAME); ret = true; } while(false); @@ -75,13 +75,13 @@ void subghz_scene_read_raw_on_enter(void* context) { subghz_read_raw_set_status(subghz->subghz_read_raw, SubGhzReadRAWStatusIDLE, ""); break; case SubGhzRxKeyStateRAWLoad: - path_extract_filename(subghz->file_path, file_name, true); + path_extract_filename_no_ext(subghz->file_path, file_name); subghz_read_raw_set_status( subghz->subghz_read_raw, SubGhzReadRAWStatusLoadKeyTX, string_get_cstr(file_name)); subghz->txrx->rx_key_state = SubGhzRxKeyStateIDLE; break; case SubGhzRxKeyStateRAWSave: - path_extract_filename(subghz->file_path, file_name, true); + path_extract_filename_no_ext(subghz->file_path, file_name); subghz_read_raw_set_status( subghz->subghz_read_raw, SubGhzReadRAWStatusSaveKey, string_get_cstr(file_name)); subghz->txrx->rx_key_state = SubGhzRxKeyStateIDLE; @@ -296,7 +296,7 @@ bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) { subghz->state_notifications = SubGhzNotificationStateRx; subghz->txrx->rx_key_state = SubGhzRxKeyStateAddKey; } else { - string_set_str(subghz->error_str, "Function requires\nan SD card."); + string_set(subghz->error_str, "Function requires\nan SD card."); scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowError); } } diff --git a/applications/subghz/scenes/subghz_scene_save_name.c b/applications/subghz/scenes/subghz_scene_save_name.c index 95fc19388..617709cc7 100644 --- a/applications/subghz/scenes/subghz_scene_save_name.c +++ b/applications/subghz/scenes/subghz_scene_save_name.c @@ -1,6 +1,4 @@ #include "../subghz_i.h" -#include "m-string.h" -#include "subghz/types.h" #include #include "../helpers/subghz_custom_event.h" #include @@ -22,49 +20,45 @@ void subghz_scene_save_name_on_enter(void* context) { bool dev_name_empty = false; string_t file_name; - string_t dir_name; string_init(file_name); - string_init(dir_name); - if(!subghz_path_is_file(subghz->file_path)) { + if(!strcmp(subghz->file_path, "")) { char file_name_buf[SUBGHZ_MAX_LEN_NAME] = {0}; set_random_name(file_name_buf, SUBGHZ_MAX_LEN_NAME); - string_set_str(file_name, file_name_buf); - string_set_str(subghz->file_path, SUBGHZ_APP_FOLDER); + string_set(file_name, file_name_buf); + strncpy(subghz->file_dir, SUBGHZ_APP_FOLDER, SUBGHZ_MAX_LEN_NAME); //highlighting the entire filename by default dev_name_empty = true; } else { - string_set(subghz->file_path_tmp, subghz->file_path); - path_extract_dirname(string_get_cstr(subghz->file_path), dir_name); - path_extract_filename(subghz->file_path, file_name, true); + strncpy(subghz->file_path_tmp, subghz->file_path, SUBGHZ_MAX_LEN_NAME); + path_extract_dirname(subghz->file_path, file_name); + strncpy(subghz->file_dir, string_get_cstr(file_name), SUBGHZ_MAX_LEN_NAME); + path_extract_filename_no_ext(subghz->file_path, file_name); if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneReadRAW) != SubGhzCustomEventManagerNoSet) { subghz_get_next_name_file(subghz, SUBGHZ_MAX_LEN_NAME); - path_extract_filename(subghz->file_path, file_name, true); + path_extract_filename_no_ext(subghz->file_path, file_name); if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneReadRAW) == SubGhzCustomEventManagerSetRAW) { dev_name_empty = true; } } - string_set(subghz->file_path, dir_name); } - - strncpy(subghz->file_name_tmp, string_get_cstr(file_name), SUBGHZ_MAX_LEN_NAME); + strncpy(subghz->file_path, string_get_cstr(file_name), SUBGHZ_MAX_LEN_NAME); text_input_set_header_text(text_input, "Name signal"); text_input_set_result_callback( text_input, subghz_scene_save_name_text_input_callback, subghz, - subghz->file_name_tmp, + subghz->file_path, MAX_TEXT_INPUT_LEN, // buffer size dev_name_empty); - ValidatorIsFile* validator_is_file = validator_is_file_alloc_init( - string_get_cstr(subghz->file_path), SUBGHZ_APP_EXTENSION, NULL); + ValidatorIsFile* validator_is_file = + validator_is_file_alloc_init(subghz->file_dir, SUBGHZ_APP_EXTENSION, NULL); text_input_set_validator(text_input, validator_is_file_callback, validator_is_file); string_clear(file_name); - string_clear(dir_name); view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdTextInput); } @@ -72,15 +66,18 @@ void subghz_scene_save_name_on_enter(void* context) { bool subghz_scene_save_name_on_event(void* context, SceneManagerEvent event) { SubGhz* subghz = context; if(event.type == SceneManagerEventTypeBack) { - string_set(subghz->file_path, subghz->file_path_tmp); + strncpy(subghz->file_path, subghz->file_path_tmp, SUBGHZ_MAX_LEN_NAME); scene_manager_previous_scene(subghz->scene_manager); return true; } else if(event.type == SceneManagerEventTypeCustom) { if(event.event == SubGhzCustomEventSceneSaveName) { - if(strcmp(subghz->file_name_tmp, "")) { - string_cat_printf( - subghz->file_path, "/%s%s", subghz->file_name_tmp, SUBGHZ_APP_EXTENSION); - if(subghz_path_is_file(subghz->file_path_tmp)) { + if(strcmp(subghz->file_path, "")) { + string_t temp_str; + string_init_printf( + temp_str, "%s/%s%s", subghz->file_dir, subghz->file_path, SUBGHZ_APP_EXTENSION); + strncpy(subghz->file_path, string_get_cstr(temp_str), SUBGHZ_MAX_LEN_NAME); + string_clear(temp_str); + if(strcmp(subghz->file_path_tmp, "")) { if(!subghz_rename_file(subghz)) { return false; } @@ -88,7 +85,7 @@ bool subghz_scene_save_name_on_event(void* context, SceneManagerEvent event) { if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneSetType) != SubGhzCustomEventManagerNoSet) { subghz_save_protocol_to_file( - subghz, subghz->txrx->fff_data, string_get_cstr(subghz->file_path)); + subghz, subghz->txrx->fff_data, subghz->file_path); scene_manager_set_scene_state( subghz->scene_manager, SubGhzSceneSetType, @@ -98,14 +95,13 @@ bool subghz_scene_save_name_on_event(void* context, SceneManagerEvent event) { subghz, subghz_history_get_raw_data( subghz->txrx->history, subghz->txrx->idx_menu_chosen), - string_get_cstr(subghz->file_path)); + subghz->file_path); } } if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneReadRAW) != SubGhzCustomEventManagerNoSet) { - subghz_protocol_raw_gen_fff_data( - subghz->txrx->fff_data, string_get_cstr(subghz->file_path)); + subghz_protocol_raw_gen_fff_data(subghz->txrx->fff_data, subghz->file_path); scene_manager_set_scene_state( subghz->scene_manager, SubGhzSceneReadRAW, SubGhzCustomEventManagerNoSet); } else { @@ -115,7 +111,7 @@ bool subghz_scene_save_name_on_event(void* context, SceneManagerEvent event) { scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSaveSuccess); return true; } else { - string_set_str(subghz->error_str, "No name file"); + string_set(subghz->error_str, "No name file"); scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowErrorSub); return true; } diff --git a/applications/subghz/scenes/subghz_scene_transmitter.c b/applications/subghz/scenes/subghz_scene_transmitter.c index b8b22749c..38ab84994 100644 --- a/applications/subghz/scenes/subghz_scene_transmitter.c +++ b/applications/subghz/scenes/subghz_scene_transmitter.c @@ -94,7 +94,7 @@ bool subghz_scene_transmitter_on_event(void* context, SceneManagerEvent event) { subghz->scene_manager, SubGhzSceneStart); return true; } else if(event.event == SubGhzCustomEventViewTransmitterError) { - string_set_str(subghz->error_str, "Protocol not found"); + string_set(subghz->error_str, "Protocol not found"); scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowErrorSub); } } else if(event.type == SceneManagerEventTypeTick) { diff --git a/applications/subghz/subghz.c b/applications/subghz/subghz.c index 0fb7b5f53..99c9db452 100644 --- a/applications/subghz/subghz.c +++ b/applications/subghz/subghz.c @@ -1,7 +1,5 @@ /* Abandon hope, all ye who enter here. */ -#include "m-string.h" -#include "subghz/types.h" #include "subghz_i.h" #include @@ -26,9 +24,6 @@ void subghz_tick_event_callback(void* context) { SubGhz* subghz_alloc() { SubGhz* subghz = malloc(sizeof(SubGhz)); - string_init(subghz->file_path); - string_init(subghz->file_path_tmp); - // GUI subghz->gui = furi_record_open("gui"); @@ -256,9 +251,9 @@ void subghz_free(SubGhz* subghz) { furi_record_close("notification"); subghz->notifications = NULL; - // Path strings - string_clear(subghz->file_path); - string_clear(subghz->file_path_tmp); + // About birds + furi_assert(subghz->file_path[SUBGHZ_MAX_LEN_NAME] == 0); + furi_assert(subghz->file_path_tmp[SUBGHZ_MAX_LEN_NAME] == 0); // The rest free(subghz); @@ -275,7 +270,7 @@ int32_t subghz_app(void* p) { // Check argument and run corresponding scene if(p) { if(subghz_key_load(subghz, p)) { - string_set_str(subghz->file_path, p); + strncpy(subghz->file_path, p, SUBGHZ_MAX_LEN_NAME); if((!strcmp(subghz->txrx->decoder_result->protocol->name, "RAW"))) { //Load Raw TX @@ -291,13 +286,12 @@ int32_t subghz_app(void* p) { view_dispatcher_stop(subghz->view_dispatcher); } } else { - string_set_str(subghz->file_path, SUBGHZ_APP_FOLDER); if(load_database) { scene_manager_next_scene(subghz->scene_manager, SubGhzSceneStart); } else { scene_manager_set_scene_state( subghz->scene_manager, SubGhzSceneShowError, SubGhzCustomEventManagerSet); - string_set_str( + string_set( subghz->error_str, "No SD card or\ndatabase found.\nSome app function\nmay be reduced."); scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowError); diff --git a/applications/subghz/subghz_cli.c b/applications/subghz/subghz_cli.c index 6c92a5d6b..f919e1cf2 100644 --- a/applications/subghz/subghz_cli.c +++ b/applications/subghz/subghz_cli.c @@ -303,7 +303,7 @@ void subghz_cli_command_decode_raw(Cli* cli, string_t args, void* context) { UNUSED(context); string_t file_name; string_init(file_name); - string_set_str(file_name, "/any/subghz/test.sub"); + string_set(file_name, "/any/subghz/test.sub"); Storage* storage = furi_record_open("storage"); FlipperFormat* fff_data_file = flipper_format_file_alloc(storage); diff --git a/applications/subghz/subghz_history.c b/applications/subghz/subghz_history.c index a8f86eecb..cb30726ba 100644 --- a/applications/subghz/subghz_history.c +++ b/applications/subghz/subghz_history.c @@ -169,14 +169,14 @@ bool subghz_history_add_to_history( break; } if(!strcmp(string_get_cstr(instance->tmp_string), "KeeLoq")) { - string_set_str(instance->tmp_string, "KL "); + string_set(instance->tmp_string, "KL "); if(!flipper_format_read_string(item->flipper_string, "Manufacture", text)) { FURI_LOG_E(TAG, "Missing Protocol"); break; } string_cat(instance->tmp_string, text); } else if(!strcmp(string_get_cstr(instance->tmp_string), "Star Line")) { - string_set_str(instance->tmp_string, "SL "); + string_set(instance->tmp_string, "SL "); if(!flipper_format_read_string(item->flipper_string, "Manufacture", text)) { FURI_LOG_E(TAG, "Missing Protocol"); break; diff --git a/applications/subghz/subghz_i.c b/applications/subghz/subghz_i.c index 5439fd2a9..690e85f74 100644 --- a/applications/subghz/subghz_i.c +++ b/applications/subghz/subghz_i.c @@ -1,8 +1,5 @@ #include "subghz_i.h" -#include "assets_icons.h" -#include "m-string.h" -#include "subghz/types.h" #include #include #include @@ -48,11 +45,11 @@ void subghz_get_frequency_modulation(SubGhz* subghz, string_t frequency, string_ if(modulation != NULL) { if(subghz->txrx->preset == FuriHalSubGhzPresetOok650Async || subghz->txrx->preset == FuriHalSubGhzPresetOok270Async) { - string_set_str(modulation, "AM"); + string_set(modulation, "AM"); } else if( subghz->txrx->preset == FuriHalSubGhzPreset2FSKDev238Async || subghz->txrx->preset == FuriHalSubGhzPreset2FSKDev476Async) { - string_set_str(modulation, "FM"); + string_set(modulation, "FM"); } else { furi_crash("SugGhz: Modulation is incorrect."); } @@ -194,9 +191,8 @@ void subghz_tx_stop(SubGhz* subghz) { //if protocol dynamic then we save the last upload if((subghz->txrx->decoder_result->protocol->type == SubGhzProtocolTypeDynamic) && - (subghz_path_is_file(subghz->file_path))) { - subghz_save_protocol_to_file( - subghz, subghz->txrx->fff_data, string_get_cstr(subghz->file_path)); + (strcmp(subghz->file_path, ""))) { + subghz_save_protocol_to_file(subghz, subghz->txrx->fff_data, subghz->file_path); } subghz_idle(subghz); notification_message(subghz->notifications, &sequence_reset_red); @@ -338,10 +334,10 @@ bool subghz_get_next_name_file(SubGhz* subghz, uint8_t max_len) { bool res = false; - if(subghz_path_is_file(subghz->file_path)) { + if(strcmp(subghz->file_path, "")) { //get the name of the next free file - path_extract_filename(subghz->file_path, file_name, true); - path_extract_dirname(string_get_cstr(subghz->file_path), file_path); + path_extract_filename_no_ext(subghz->file_path, file_name); + path_extract_dirname(subghz->file_path, file_path); storage_get_next_filename( storage, @@ -357,7 +353,7 @@ bool subghz_get_next_name_file(SubGhz* subghz, uint8_t max_len) { string_get_cstr(file_path), string_get_cstr(file_name), SUBGHZ_APP_EXTENSION); - string_set(subghz->file_path, temp_str); + strncpy(subghz->file_path, string_get_cstr(temp_str), SUBGHZ_MAX_LEN_NAME); res = true; } @@ -417,17 +413,19 @@ bool subghz_load_protocol_from_file(SubGhz* subghz) { string_init(file_path); // Input events and views are managed by file_select - bool res = dialog_file_browser_show( + bool res = dialog_file_select_show( subghz->dialogs, - subghz->file_path, - subghz->file_path, + SUBGHZ_APP_FOLDER, SUBGHZ_APP_EXTENSION, - true, - &I_sub1_10px, - true); + subghz->file_path, + sizeof(subghz->file_path), + NULL); if(res) { - res = subghz_key_load(subghz, string_get_cstr(subghz->file_path)); + string_printf( + file_path, "%s/%s%s", SUBGHZ_APP_FOLDER, subghz->file_path, SUBGHZ_APP_EXTENSION); + strncpy(subghz->file_path, string_get_cstr(file_path), SUBGHZ_MAX_LEN_NAME); + res = subghz_key_load(subghz, subghz->file_path); } string_clear(file_path); @@ -441,9 +439,9 @@ bool subghz_rename_file(SubGhz* subghz) { Storage* storage = furi_record_open("storage"); - if(string_cmp(subghz->file_path_tmp, subghz->file_path)) { - FS_Error fs_result = storage_common_rename( - storage, string_get_cstr(subghz->file_path_tmp), string_get_cstr(subghz->file_path)); + if(strcmp(subghz->file_path_tmp, subghz->file_path)) { + FS_Error fs_result = + storage_common_rename(storage, subghz->file_path_tmp, subghz->file_path); if(fs_result != FSE_OK) { dialog_message_show_storage_error(subghz->dialogs, "Cannot rename\n file/directory"); @@ -459,7 +457,7 @@ bool subghz_delete_file(SubGhz* subghz) { furi_assert(subghz); Storage* storage = furi_record_open("storage"); - bool result = storage_simply_remove(storage, string_get_cstr(subghz->file_path_tmp)); + bool result = storage_simply_remove(storage, subghz->file_path_tmp); furi_record_close("storage"); subghz_file_name_clear(subghz); @@ -469,12 +467,8 @@ bool subghz_delete_file(SubGhz* subghz) { void subghz_file_name_clear(SubGhz* subghz) { furi_assert(subghz); - string_set_str(subghz->file_path, SUBGHZ_APP_FOLDER); - string_reset(subghz->file_path_tmp); -} - -bool subghz_path_is_file(string_t path) { - return string_end_with_str_p(path, SUBGHZ_APP_EXTENSION); + memset(subghz->file_path, 0, sizeof(subghz->file_path)); + memset(subghz->file_path_tmp, 0, sizeof(subghz->file_path_tmp)); } uint32_t subghz_random_serial(void) { diff --git a/applications/subghz/subghz_i.h b/applications/subghz/subghz_i.h index accb79c00..b1a932836 100644 --- a/applications/subghz/subghz_i.h +++ b/applications/subghz/subghz_i.h @@ -36,7 +36,7 @@ #include #include -#define SUBGHZ_MAX_LEN_NAME 64 +#define SUBGHZ_MAX_LEN_NAME 250 /** SubGhzNotification state */ typedef enum { @@ -128,9 +128,10 @@ struct SubGhz { ByteInput* byte_input; Widget* widget; DialogsApp* dialogs; - string_t file_path; - string_t file_path_tmp; - char file_name_tmp[SUBGHZ_MAX_LEN_NAME]; + char file_path[SUBGHZ_MAX_LEN_NAME + 1]; + char file_path_tmp[SUBGHZ_MAX_LEN_NAME + 1]; + //ToDo you can get rid of it, you need to refactor text input to return the path to the folder + char file_dir[SUBGHZ_MAX_LEN_NAME + 1]; SubGhzNotificationState state_notifications; SubGhzViewReceiver* subghz_receiver; @@ -182,6 +183,5 @@ bool subghz_load_protocol_from_file(SubGhz* subghz); bool subghz_rename_file(SubGhz* subghz); bool subghz_delete_file(SubGhz* subghz); void subghz_file_name_clear(SubGhz* subghz); -bool subghz_path_is_file(string_t path); uint32_t subghz_random_serial(void); void subghz_hopper_update(SubGhz* subghz); diff --git a/applications/subghz/views/receiver.c b/applications/subghz/views/receiver.c index 866d82726..7b19cbcf2 100644 --- a/applications/subghz/views/receiver.c +++ b/applications/subghz/views/receiver.c @@ -111,9 +111,9 @@ void subghz_view_receiver_add_data_statusbar( furi_assert(subghz_receiver); with_view_model( subghz_receiver->view, (SubGhzViewReceiverModel * model) { - string_set_str(model->frequency_str, frequency_str); - string_set_str(model->preset_str, preset_str); - string_set_str(model->history_stat_str, history_stat_str); + string_set(model->frequency_str, frequency_str); + string_set(model->preset_str, preset_str); + string_set(model->history_stat_str, history_stat_str); return true; }); } diff --git a/applications/subghz/views/subghz_read_raw.c b/applications/subghz/views/subghz_read_raw.c index a4807d775..ff3ba45a3 100644 --- a/applications/subghz/views/subghz_read_raw.c +++ b/applications/subghz/views/subghz_read_raw.c @@ -46,8 +46,8 @@ void subghz_read_raw_add_data_statusbar( furi_assert(instance); with_view_model( instance->view, (SubGhzReadRAWModel * model) { - string_set_str(model->frequency_str, frequency_str); - string_set_str(model->preset_str, preset_str); + string_set(model->frequency_str, frequency_str); + string_set(model->preset_str, preset_str); return true; }); } @@ -372,7 +372,7 @@ bool subghz_read_raw_input(InputEvent* event, void* context) { model->satus = SubGhzReadRAWStatusStart; model->rssi_history_end = false; model->ind_write = 0; - string_set_str(model->sample_write, "0 spl."); + string_set(model->sample_write, "0 spl."); string_reset(model->file_name); instance->callback(SubGhzCustomEventViewReadRAWErase, instance->context); } @@ -424,7 +424,7 @@ void subghz_read_raw_set_status( model->rssi_history_end = false; model->ind_write = 0; string_reset(model->file_name); - string_set_str(model->sample_write, "0 spl."); + string_set(model->sample_write, "0 spl."); return true; }); break; @@ -441,8 +441,8 @@ void subghz_read_raw_set_status( model->satus = SubGhzReadRAWStatusLoadKeyIDLE; model->rssi_history_end = false; model->ind_write = 0; - string_set_str(model->file_name, file_name); - string_set_str(model->sample_write, "RAW"); + string_set(model->file_name, file_name); + string_set(model->sample_write, "RAW"); return true; }); break; @@ -451,8 +451,8 @@ void subghz_read_raw_set_status( instance->view, (SubGhzReadRAWModel * model) { model->satus = SubGhzReadRAWStatusLoadKeyIDLE; if(!model->ind_write) { - string_set_str(model->file_name, file_name); - string_set_str(model->sample_write, "RAW"); + string_set(model->file_name, file_name); + string_set(model->sample_write, "RAW"); } else { string_reset(model->file_name); } diff --git a/applications/subghz/views/transmitter.c b/applications/subghz/views/transmitter.c index 3cbcf098a..3113e31f6 100644 --- a/applications/subghz/views/transmitter.c +++ b/applications/subghz/views/transmitter.c @@ -36,9 +36,9 @@ void subghz_view_transmitter_add_data_to_show( furi_assert(subghz_transmitter); with_view_model( subghz_transmitter->view, (SubGhzViewTransmitterModel * model) { - string_set_str(model->key_str, key_str); - string_set_str(model->frequency_str, frequency_str); - string_set_str(model->preset_str, preset_str); + string_set(model->key_str, key_str); + string_set(model->frequency_str, frequency_str); + string_set(model->preset_str, preset_str); model->show_button = show_button; return true; }); diff --git a/assets/icons/Archive/music_10px.png b/assets/icons/Archive/music_10px.png deleted file mode 100644 index d41eb0db8..000000000 Binary files a/assets/icons/Archive/music_10px.png and /dev/null differ diff --git a/lib/one_wire/ibutton/ibutton_key.c b/lib/one_wire/ibutton/ibutton_key.c index 2c0f7fa26..c6d4466f0 100644 --- a/lib/one_wire/ibutton/ibutton_key.c +++ b/lib/one_wire/ibutton/ibutton_key.c @@ -4,6 +4,7 @@ struct iButtonKey { uint8_t data[IBUTTON_KEY_DATA_SIZE]; + char name[IBUTTON_KEY_NAME_SIZE]; iButtonKeyType type; }; @@ -41,6 +42,14 @@ uint8_t ibutton_key_get_data_size(iButtonKey* key) { return ibutton_key_get_size_by_type(key->type); } +void ibutton_key_set_name(iButtonKey* key, const char* name) { + strlcpy(key->name, name, IBUTTON_KEY_NAME_SIZE); +} + +const char* ibutton_key_get_name_p(iButtonKey* key) { + return key->name; +} + void ibutton_key_set_type(iButtonKey* key, iButtonKeyType key_type) { key->type = key_type; } diff --git a/lib/one_wire/ibutton/ibutton_key.h b/lib/one_wire/ibutton/ibutton_key.h index f66537d7e..8d6732bcb 100644 --- a/lib/one_wire/ibutton/ibutton_key.h +++ b/lib/one_wire/ibutton/ibutton_key.h @@ -68,6 +68,20 @@ const uint8_t* ibutton_key_get_data_p(iButtonKey* key); */ uint8_t ibutton_key_get_data_size(iButtonKey* key); +/** + * Set key name + * @param key + * @param name + */ +void ibutton_key_set_name(iButtonKey* key, const char* name); + +/** + * Get pointer to key name + * @param key + * @return const char* + */ +const char* ibutton_key_get_name_p(iButtonKey* key); + /** * Set key type * @param key diff --git a/lib/toolbox/path.c b/lib/toolbox/path.c index a99e57d1f..4fd042e41 100644 --- a/lib/toolbox/path.c +++ b/lib/toolbox/path.c @@ -19,20 +19,6 @@ void path_extract_filename_no_ext(const char* path, string_t filename) { string_mid(filename, start_position, end_position - start_position); } -void path_extract_filename(string_t path, string_t name, bool trim_ext) { - size_t filename_start = string_search_rchar(path, '/'); - if(filename_start > 0) { - filename_start++; - string_set_n(name, path, filename_start, string_size(path) - filename_start); - } - if(trim_ext) { - size_t dot = string_search_rchar(name, '.'); - if(dot > 0) { - string_left(name, dot); - } - } -} - static inline void path_cleanup(string_t path) { string_strim(path); while(string_end_with_str_p(path, "/")) { diff --git a/lib/toolbox/path.h b/lib/toolbox/path.h index 76e501cc1..0de63bb2b 100644 --- a/lib/toolbox/path.h +++ b/lib/toolbox/path.h @@ -14,15 +14,6 @@ extern "C" { */ void path_extract_filename_no_ext(const char* path, string_t filename); -/** - * @brief Extract filename string from path. - * - * @param path path string - * @param filename output filename string. Must be initialized before. - * @param trim_ext true - get filename without extension - */ -void path_extract_filename(string_t path, string_t filename, bool trim_ext); - /** * @brief Extract last path component *