From a02ba763cddcd7905ea4042879a6dbea64de400c Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Thu, 3 Aug 2023 03:02:05 +0200 Subject: [PATCH] Create mass storage images on flipper --- .../external/mass_storage/mass_storage_app.c | 23 +++ .../mass_storage/mass_storage_app_i.h | 21 +++ .../scenes/mass_storage_scene_config.h | 2 + .../scenes/mass_storage_scene_create_image.c | 172 ++++++++++++++++++ .../mass_storage_scene_create_image_name.c | 52 ++++++ .../scenes/mass_storage_scene_start.c | 8 + 6 files changed, 278 insertions(+) create mode 100644 applications/external/mass_storage/scenes/mass_storage_scene_create_image.c create mode 100644 applications/external/mass_storage/scenes/mass_storage_scene_create_image_name.c diff --git a/applications/external/mass_storage/mass_storage_app.c b/applications/external/mass_storage/mass_storage_app.c index eb10b2928..7e711ee85 100644 --- a/applications/external/mass_storage/mass_storage_app.c +++ b/applications/external/mass_storage/mass_storage_app.c @@ -31,6 +31,9 @@ MassStorageApp* mass_storage_app_alloc(char* arg) { furi_string_set_str(app->file_path, MASS_STORAGE_APP_PATH_FOLDER); } + app->create_image_size = 100; + app->create_size_unit = SizeUnitMb; + app->gui = furi_record_open(RECORD_GUI); app->fs_api = furi_record_open(RECORD_STORAGE); app->notifications = furi_record_open(RECORD_NOTIFICATION); @@ -55,10 +58,24 @@ MassStorageApp* mass_storage_app_alloc(char* arg) { MassStorageAppViewWork, mass_storage_get_view(app->mass_storage_view)); + app->var_item_list = variable_item_list_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, + MassStorageAppViewVarItemList, + variable_item_list_get_view(app->var_item_list)); + app->submenu = submenu_alloc(); view_dispatcher_add_view( app->view_dispatcher, MassStorageAppViewSubmenu, submenu_get_view(app->submenu)); + app->text_input = text_input_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, MassStorageAppViewTextInput, text_input_get_view(app->text_input)); + + app->popup = popup_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, MassStorageAppViewPopup, popup_get_view(app->popup)); + view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); scene_manager_set_scene_state( @@ -78,8 +95,14 @@ void mass_storage_app_free(MassStorageApp* app) { // Views view_dispatcher_remove_view(app->view_dispatcher, MassStorageAppViewWork); mass_storage_free(app->mass_storage_view); + view_dispatcher_remove_view(app->view_dispatcher, MassStorageAppViewVarItemList); + variable_item_list_free(app->var_item_list); view_dispatcher_remove_view(app->view_dispatcher, MassStorageAppViewSubmenu); submenu_free(app->submenu); + view_dispatcher_remove_view(app->view_dispatcher, MassStorageAppViewTextInput); + text_input_free(app->text_input); + view_dispatcher_remove_view(app->view_dispatcher, MassStorageAppViewPopup); + popup_free(app->popup); // View dispatcher view_dispatcher_free(app->view_dispatcher); diff --git a/applications/external/mass_storage/mass_storage_app_i.h b/applications/external/mass_storage/mass_storage_app_i.h index f2ddeaa41..7601dce8c 100644 --- a/applications/external/mass_storage/mass_storage_app_i.h +++ b/applications/external/mass_storage/mass_storage_app_i.h @@ -11,13 +11,24 @@ #include #include #include +#include #include +#include +#include #include #include "views/mass_storage_view.h" #define MASS_STORAGE_APP_PATH_FOLDER STORAGE_APP_DATA_PATH_PREFIX #define MASS_STORAGE_FILE_NAME_LEN 40 +typedef enum { + SizeUnitBytes, + SizeUnitKb, + SizeUnitMb, + SizeUnitGb, + SizeUnitCount, +} SizeUnit; + struct MassStorageApp { Gui* gui; Storage* fs_api; @@ -27,7 +38,14 @@ struct MassStorageApp { DialogsApp* dialogs; Widget* widget; MassStorage* mass_storage_view; + VariableItemList* var_item_list; Submenu* submenu; + TextInput* text_input; + Popup* popup; + + uint32_t create_image_size; + SizeUnit create_size_unit; + char create_name[MASS_STORAGE_FILE_NAME_LEN]; FuriString* file_path; File* file; @@ -39,5 +57,8 @@ struct MassStorageApp { typedef enum { MassStorageAppViewError, MassStorageAppViewWork, + MassStorageAppViewVarItemList, MassStorageAppViewSubmenu, + MassStorageAppViewTextInput, + MassStorageAppViewPopup, } MassStorageAppView; diff --git a/applications/external/mass_storage/scenes/mass_storage_scene_config.h b/applications/external/mass_storage/scenes/mass_storage_scene_config.h index f7fe889e3..ff0418fdf 100644 --- a/applications/external/mass_storage/scenes/mass_storage_scene_config.h +++ b/applications/external/mass_storage/scenes/mass_storage_scene_config.h @@ -1,3 +1,5 @@ ADD_SCENE(mass_storage, start, Start) ADD_SCENE(mass_storage, file_select, FileSelect) ADD_SCENE(mass_storage, work, Work) +ADD_SCENE(mass_storage, create_image, CreateImage) +ADD_SCENE(mass_storage, create_image_name, CreateImageName) diff --git a/applications/external/mass_storage/scenes/mass_storage_scene_create_image.c b/applications/external/mass_storage/scenes/mass_storage_scene_create_image.c new file mode 100644 index 000000000..2e27fe601 --- /dev/null +++ b/applications/external/mass_storage/scenes/mass_storage_scene_create_image.c @@ -0,0 +1,172 @@ +#include "../mass_storage_app_i.h" +#include + +enum VarItemListIndex { + VarItemListIndexImageSize, + VarItemListIndexSizeUnit, + VarItemListIndexName, + VarItemListIndexCreate, +}; + +void mass_storage_scene_create_image_var_item_list_callback(void* context, uint32_t index) { + MassStorageApp* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, index); +} + +const uint32_t image_size_values[] = {1, 2, 4, 8, 12, 16, 20, 25, 30, 35, 40, + 45, 50, 69, 75, 100, 150, 200, 250, 500, 750}; +static void mass_storage_scene_create_image_image_size_changed(VariableItem* item) { + MassStorageApp* app = variable_item_get_context(item); + app->create_image_size = image_size_values[variable_item_get_current_value_index(item)]; + char str[4]; + snprintf(str, sizeof(str), "%lu", app->create_image_size); + variable_item_set_current_value_text(item, str); +} + +const char* const size_unit_names[] = { + [SizeUnitBytes] = "Bytes", + [SizeUnitKb] = "Kb", + [SizeUnitMb] = "Mb", + [SizeUnitGb] = "Gb", +}; +static void mass_storage_scene_create_image_size_unit_changed(VariableItem* item) { + MassStorageApp* app = variable_item_get_context(item); + app->create_size_unit = variable_item_get_current_value_index(item); + variable_item_set_current_value_text(item, size_unit_names[app->create_size_unit]); +} + +void mass_storage_scene_create_image_on_enter(void* context) { + MassStorageApp* app = context; + VariableItemList* var_item_list = app->var_item_list; + VariableItem* item; + uint8_t value_index; + + value_index = + value_index_uint32(app->create_image_size, image_size_values, COUNT_OF(image_size_values)); + app->create_image_size = image_size_values[value_index]; + char str[4]; + snprintf(str, sizeof(str), "%lu", app->create_image_size); + item = variable_item_list_add( + var_item_list, + "Image Size", + COUNT_OF(image_size_values), + mass_storage_scene_create_image_image_size_changed, + app); + variable_item_set_current_value_index(item, value_index); + variable_item_set_current_value_text(item, str); + + app->create_size_unit = CLAMP(app->create_size_unit, SizeUnitCount - 1, 0); + item = variable_item_list_add( + var_item_list, + "Size Unit", + SizeUnitCount, + mass_storage_scene_create_image_size_unit_changed, + app); + variable_item_set_current_value_index(item, app->create_size_unit); + variable_item_set_current_value_text(item, size_unit_names[app->create_size_unit]); + + item = variable_item_list_add(var_item_list, "Name", 0, NULL, app); + variable_item_set_current_value_text(item, app->create_name); + + variable_item_list_add(var_item_list, "Create", 0, NULL, app); + + variable_item_list_set_enter_callback( + var_item_list, mass_storage_scene_create_image_var_item_list_callback, app); + + variable_item_list_set_selected_item( + var_item_list, + scene_manager_get_scene_state(app->scene_manager, MassStorageSceneCreateImage)); + + view_dispatcher_switch_to_view(app->view_dispatcher, MassStorageAppViewVarItemList); +} + +static void popup_callback_ok(void* context) { + MassStorageApp* app = context; + scene_manager_previous_scene(app->scene_manager); +} + +static void popup_callback_error(void* context) { + MassStorageApp* app = context; + view_dispatcher_switch_to_view(app->view_dispatcher, MassStorageAppViewVarItemList); +} + +bool mass_storage_scene_create_image_on_event(void* context, SceneManagerEvent event) { + MassStorageApp* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + scene_manager_set_scene_state( + app->scene_manager, MassStorageSceneCreateImage, event.event); + consumed = true; + switch(event.event) { + case VarItemListIndexName: + scene_manager_next_scene(app->scene_manager, MassStorageSceneCreateImageName); + break; + case VarItemListIndexCreate: { + popup_set_header(app->popup, "Creating Image...", 64, 32, AlignCenter, AlignCenter); + popup_set_text(app->popup, "", 0, 0, AlignLeft, AlignBottom); + popup_set_callback(app->popup, NULL); + popup_set_context(app->popup, NULL); + popup_set_timeout(app->popup, 0); + popup_disable_timeout(app->popup); + view_dispatcher_switch_to_view(app->view_dispatcher, MassStorageAppViewPopup); + + bool default_name = !strnlen(app->create_name, sizeof(app->create_name)); + if(default_name) { + snprintf( + app->create_name, + sizeof(app->create_name), + "%lu%s", + app->create_image_size, + size_unit_names[app->create_size_unit]); + } + furi_string_printf(app->file_path, APP_DATA_PATH("%s.img"), app->create_name); + + app->file = storage_file_alloc(app->fs_api); + const char* error = NULL; + if(storage_file_open( + app->file, furi_string_get_cstr(app->file_path), FSAM_WRITE, FSOM_CREATE_NEW)) { + uint64_t size = app->create_image_size; + for(size_t i = app->create_size_unit; i > 0; i--) size *= 1024; + if(!storage_file_expand(app->file, size)) { + error = "Can't allocate data"; + } + } else { + if(storage_file_exists(app->fs_api, furi_string_get_cstr(app->file_path))) { + error = "File already exists"; + } else { + error = "Can't open file"; + } + } + storage_file_free(app->file); + + if(error) { + popup_set_header( + app->popup, "Error Creating Image!", 64, 26, AlignCenter, AlignCenter); + popup_set_text(app->popup, error, 64, 40, AlignCenter, AlignCenter); + popup_set_callback(app->popup, popup_callback_error); + } else { + popup_set_header(app->popup, "Image Created!", 64, 32, AlignCenter, AlignCenter); + popup_set_text(app->popup, "", 0, 0, AlignLeft, AlignBottom); + popup_set_callback(app->popup, popup_callback_ok); + } + popup_set_context(app->popup, app); + popup_set_timeout(app->popup, 0); + popup_disable_timeout(app->popup); + view_dispatcher_switch_to_view(app->view_dispatcher, MassStorageAppViewPopup); + + if(default_name) strcpy(app->create_name, ""); + break; + } + default: + break; + } + } + + return consumed; +} + +void mass_storage_scene_create_image_on_exit(void* context) { + MassStorageApp* app = context; + variable_item_list_reset(app->var_item_list); +} diff --git a/applications/external/mass_storage/scenes/mass_storage_scene_create_image_name.c b/applications/external/mass_storage/scenes/mass_storage_scene_create_image_name.c new file mode 100644 index 000000000..8915900f2 --- /dev/null +++ b/applications/external/mass_storage/scenes/mass_storage_scene_create_image_name.c @@ -0,0 +1,52 @@ +#include "../mass_storage_app_i.h" + +enum TextInputIndex { + TextInputResultOk, +}; + +static void mass_storage_scene_create_image_name_text_input_callback(void* context) { + MassStorageApp* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, TextInputResultOk); +} + +void mass_storage_scene_create_image_name_on_enter(void* context) { + MassStorageApp* app = context; + TextInput* text_input = app->text_input; + + text_input_set_header_text(text_input, "Leave empty for default"); + + text_input_set_minimum_length(text_input, 0); + + text_input_set_result_callback( + text_input, + mass_storage_scene_create_image_name_text_input_callback, + app, + app->create_name, + sizeof(app->create_name), + false); + + view_dispatcher_switch_to_view(app->view_dispatcher, MassStorageAppViewTextInput); +} + +bool mass_storage_scene_create_image_name_on_event(void* context, SceneManagerEvent event) { + MassStorageApp* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + consumed = true; + switch(event.event) { + case TextInputResultOk: + scene_manager_previous_scene(app->scene_manager); + break; + default: + break; + } + } + + return consumed; +} + +void mass_storage_scene_create_image_name_on_exit(void* context) { + MassStorageApp* app = context; + text_input_reset(app->text_input); +} diff --git a/applications/external/mass_storage/scenes/mass_storage_scene_start.c b/applications/external/mass_storage/scenes/mass_storage_scene_start.c index 15ddb16ec..57afc1130 100644 --- a/applications/external/mass_storage/scenes/mass_storage_scene_start.c +++ b/applications/external/mass_storage/scenes/mass_storage_scene_start.c @@ -17,6 +17,14 @@ void mass_storage_scene_start_on_enter(void* context) { mass_storage_scene_start_submenu_callback, app); + submenu_add_item( + submenu, + "Create Image", + MassStorageSceneCreateImage, + mass_storage_scene_start_submenu_callback, + app); + scene_manager_set_scene_state(app->scene_manager, MassStorageSceneCreateImage, 0); + submenu_set_header(submenu, "USB Mass Storage"); submenu_set_selected_item( submenu, scene_manager_get_scene_state(app->scene_manager, MassStorageSceneStart));