Преглед изворни кода

Mass Storage: Port changes from firmware

- Bigger image creation allowed
- Icon rename like firmware
- Select .iso files aswell
- Auto unlock USB profile
- Cosmetic changes to main view
Willy-JL пре 1 година
родитељ
комит
a0ede19e45

+ 1 - 0
mass_storage/.gitsubtree

@@ -1 +1,2 @@
+https://github.com/xMasterX/all-the-plugins dev base_pack/mass_storage
 https://github.com/flipperdevices/flipperzero-good-faps dev mass_storage
 https://github.com/flipperdevices/flipperzero-good-faps dev mass_storage

+ 1 - 1
mass_storage/application.fam

@@ -10,7 +10,7 @@ App(
     stack_size=2 * 1024,
     stack_size=2 * 1024,
     fap_description="Implements a mass storage device over USB for disk images",
     fap_description="Implements a mass storage device over USB for disk images",
     fap_version="1.3",
     fap_version="1.3",
-    fap_icon="assets/mass_storage_10px.png",
+    fap_icon="assets/floppydisk_10px.png",
     fap_icon_assets="assets",
     fap_icon_assets="assets",
     fap_category="USB",
     fap_category="USB",
 )
 )

+ 0 - 0
mass_storage/assets/mass_storage_10px.png → mass_storage/assets/floppydisk_10px.png


+ 26 - 5
mass_storage/mass_storage_app.c

@@ -46,6 +46,25 @@ MassStorageApp* mass_storage_app_alloc(char* arg) {
     app->fs_api = furi_record_open(RECORD_STORAGE);
     app->fs_api = furi_record_open(RECORD_STORAGE);
     app->dialogs = furi_record_open(RECORD_DIALOGS);
     app->dialogs = furi_record_open(RECORD_DIALOGS);
 
 
+    app->create_image_size = (uint8_t)-1;
+    SDInfo sd_info;
+    if(storage_sd_info(app->fs_api, &sd_info) == FSE_OK) {
+        switch(sd_info.fs_type) {
+        case FST_FAT12:
+            app->create_image_max = 16LL * 1024 * 1024;
+            break;
+        case FST_FAT16:
+            app->create_image_max = 2LL * 1024 * 1024 * 1024;
+            break;
+        case FST_FAT32:
+            app->create_image_max = 4LL * 1024 * 1024 * 1024;
+            break;
+        default:
+            app->create_image_max = 0;
+            break;
+        }
+    }
+
     app->view_dispatcher = view_dispatcher_alloc();
     app->view_dispatcher = view_dispatcher_alloc();
     view_dispatcher_enable_queue(app->view_dispatcher);
     view_dispatcher_enable_queue(app->view_dispatcher);
 
 
@@ -83,14 +102,14 @@ MassStorageApp* mass_storage_app_alloc(char* arg) {
     view_dispatcher_add_view(
     view_dispatcher_add_view(
         app->view_dispatcher, MassStorageAppViewWidget, widget_get_view(app->widget));
         app->view_dispatcher, MassStorageAppViewWidget, widget_get_view(app->widget));
 
 
+    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);
     view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen);
 
 
     if(storage_file_exists(app->fs_api, furi_string_get_cstr(app->file_path))) {
     if(storage_file_exists(app->fs_api, furi_string_get_cstr(app->file_path))) {
-        if(!furi_hal_usb_is_locked()) {
-            scene_manager_next_scene(app->scene_manager, MassStorageSceneWork);
-        } else {
-            scene_manager_next_scene(app->scene_manager, MassStorageSceneUsbLocked);
-        }
+        scene_manager_next_scene(app->scene_manager, MassStorageSceneWork);
     } else {
     } else {
         scene_manager_next_scene(app->scene_manager, MassStorageSceneStart);
         scene_manager_next_scene(app->scene_manager, MassStorageSceneStart);
     }
     }
@@ -107,12 +126,14 @@ void mass_storage_app_free(MassStorageApp* app) {
     view_dispatcher_remove_view(app->view_dispatcher, MassStorageAppViewStart);
     view_dispatcher_remove_view(app->view_dispatcher, MassStorageAppViewStart);
     view_dispatcher_remove_view(app->view_dispatcher, MassStorageAppViewLoading);
     view_dispatcher_remove_view(app->view_dispatcher, MassStorageAppViewLoading);
     view_dispatcher_remove_view(app->view_dispatcher, MassStorageAppViewWidget);
     view_dispatcher_remove_view(app->view_dispatcher, MassStorageAppViewWidget);
+    view_dispatcher_remove_view(app->view_dispatcher, MassStorageAppViewPopup);
 
 
     mass_storage_free(app->mass_storage_view);
     mass_storage_free(app->mass_storage_view);
     text_input_free(app->text_input);
     text_input_free(app->text_input);
     variable_item_list_free(app->variable_item_list);
     variable_item_list_free(app->variable_item_list);
     loading_free(app->loading);
     loading_free(app->loading);
     widget_free(app->widget);
     widget_free(app->widget);
+    popup_free(app->popup);
 
 
     // View dispatcher
     // View dispatcher
     view_dispatcher_free(app->view_dispatcher);
     view_dispatcher_free(app->view_dispatcher);

+ 6 - 5
mass_storage/mass_storage_app_i.h

@@ -14,6 +14,7 @@
 #include <gui/modules/text_input.h>
 #include <gui/modules/text_input.h>
 #include <gui/modules/loading.h>
 #include <gui/modules/loading.h>
 #include <gui/modules/widget.h>
 #include <gui/modules/widget.h>
+#include <gui/modules/popup.h>
 #include <storage/storage.h>
 #include <storage/storage.h>
 #include "views/mass_storage_view.h"
 #include "views/mass_storage_view.h"
 #include <mass_storage_icons.h>
 #include <mass_storage_icons.h>
@@ -28,6 +29,7 @@ struct MassStorageApp {
     ViewDispatcher* view_dispatcher;
     ViewDispatcher* view_dispatcher;
     SceneManager* scene_manager;
     SceneManager* scene_manager;
     Widget* widget;
     Widget* widget;
+    Popup* popup;
     DialogsApp* dialogs;
     DialogsApp* dialogs;
     TextInput* text_input;
     TextInput* text_input;
     VariableItemList* variable_item_list;
     VariableItemList* variable_item_list;
@@ -40,8 +42,9 @@ struct MassStorageApp {
     FuriMutex* usb_mutex;
     FuriMutex* usb_mutex;
     MassStorageUsb* usb;
     MassStorageUsb* usb;
 
 
-    char new_file_name[MASS_STORAGE_FILE_NAME_LEN + 1];
-    uint32_t new_file_size;
+    uint64_t create_image_max;
+    uint8_t create_image_size;
+    char create_image_name[MASS_STORAGE_FILE_NAME_LEN];
 
 
     uint32_t bytes_read, bytes_written;
     uint32_t bytes_read, bytes_written;
 };
 };
@@ -52,6 +55,7 @@ typedef enum {
     MassStorageAppViewWork,
     MassStorageAppViewWork,
     MassStorageAppViewLoading,
     MassStorageAppViewLoading,
     MassStorageAppViewWidget,
     MassStorageAppViewWidget,
+    MassStorageAppViewPopup,
 } MassStorageAppView;
 } MassStorageAppView;
 
 
 enum MassStorageCustomEvent {
 enum MassStorageCustomEvent {
@@ -59,9 +63,6 @@ enum MassStorageCustomEvent {
     MassStorageCustomEventReserved = 100,
     MassStorageCustomEventReserved = 100,
 
 
     MassStorageCustomEventEject,
     MassStorageCustomEventEject,
-    MassStorageCustomEventFileSelect,
-    MassStorageCustomEventNewImage,
-    MassStorageCustomEventNameInput,
 };
 };
 
 
 void mass_storage_app_show_loading_popup(MassStorageApp* app, bool show);
 void mass_storage_app_show_loading_popup(MassStorageApp* app, bool show);

+ 2 - 1
mass_storage/scenes/mass_storage_scene_config.h

@@ -1,5 +1,6 @@
 ADD_SCENE(mass_storage, start, Start)
 ADD_SCENE(mass_storage, start, Start)
 ADD_SCENE(mass_storage, file_select, FileSelect)
 ADD_SCENE(mass_storage, file_select, FileSelect)
 ADD_SCENE(mass_storage, work, Work)
 ADD_SCENE(mass_storage, work, Work)
-ADD_SCENE(mass_storage, file_name, FileName)
+ADD_SCENE(mass_storage, create_image, CreateImage)
+ADD_SCENE(mass_storage, create_image_name, CreateImageName)
 ADD_SCENE(mass_storage, usb_locked, UsbLocked)
 ADD_SCENE(mass_storage, usb_locked, UsbLocked)

+ 193 - 0
mass_storage/scenes/mass_storage_scene_create_image.c

@@ -0,0 +1,193 @@
+#include "../mass_storage_app_i.h"
+#include <lib/toolbox/value_index.h>
+
+enum VarItemListIndex {
+    VarItemListIndexImageSize,
+    VarItemListIndexImageName,
+    VarItemListIndexCreateImage,
+};
+
+void mass_storage_scene_create_image_variable_item_list_callback(void* context, uint32_t index) {
+    MassStorageApp* app = context;
+    view_dispatcher_send_custom_event(app->view_dispatcher, index);
+}
+
+static const struct {
+    char* name;
+    uint64_t value;
+} image_sizes[] = {
+    {"1MB", 1LL * 1024 * 1024},
+    {"2MB", 2LL * 1024 * 1024},
+    {"4MB", 4LL * 1024 * 1024},
+    {"8MB", 8LL * 1024 * 1024},
+    {"16MB", 16LL * 1024 * 1024},
+    {"32MB", 32LL * 1024 * 1024},
+    {"64MB", 64LL * 1024 * 1024},
+    {"128MB", 128LL * 1024 * 1024},
+    {"256MB", 256LL * 1024 * 1024},
+    {"512MB", 512LL * 1024 * 1024},
+    {"1GB", 1LL * 1024 * 1024 * 1024},
+    {"2GB", 2LL * 1024 * 1024 * 1024},
+    {"4GB", 4LL * 1024 * 1024 * 1024},
+    {"8GB", 8LL * 1024 * 1024 * 1024},
+    {"16GB", 16LL * 1024 * 1024 * 1024},
+    {"32GB", 32LL * 1024 * 1024 * 1024},
+    {"64GB", 64LL * 1024 * 1024 * 1024},
+    {"128GB", 128LL * 1024 * 1024 * 1024},
+    {"256GB", 256LL * 1024 * 1024 * 1024},
+    {"512GB", 512LL * 1024 * 1024 * 1024},
+};
+static void mass_storage_scene_create_image_image_size_changed(VariableItem* item) {
+    MassStorageApp* app = variable_item_get_context(item);
+    app->create_image_size = variable_item_get_current_value_index(item);
+    variable_item_set_current_value_text(item, image_sizes[app->create_image_size].name);
+}
+
+void mass_storage_scene_create_image_on_enter(void* context) {
+    MassStorageApp* app = context;
+    VariableItemList* variable_item_list = app->variable_item_list;
+    VariableItem* item;
+
+    uint8_t size_count = COUNT_OF(image_sizes);
+    if(app->create_image_max) {
+        for(size_t i = 1; i < size_count; i++) {
+            if(image_sizes[i].value > app->create_image_max) {
+                size_count = i;
+                break;
+            }
+        }
+    }
+    if(app->create_image_size == (uint8_t)-1) {
+        app->create_image_size = CLAMP(7, size_count - 2, 0); // 7 = 128MB
+    }
+    item = variable_item_list_add(
+        variable_item_list,
+        "Image Size",
+        size_count,
+        mass_storage_scene_create_image_image_size_changed,
+        app);
+    variable_item_set_current_value_index(item, app->create_image_size);
+    variable_item_set_current_value_text(item, image_sizes[app->create_image_size].name);
+
+    item = variable_item_list_add(variable_item_list, "Image Name", 0, NULL, app);
+    variable_item_set_current_value_text(item, app->create_image_name);
+
+    variable_item_list_add(variable_item_list, "Create Image", 0, NULL, app);
+
+    variable_item_list_set_enter_callback(
+        variable_item_list, mass_storage_scene_create_image_variable_item_list_callback, app);
+
+    variable_item_list_set_header(variable_item_list, "Create Disk Image");
+
+    variable_item_list_set_selected_item(
+        variable_item_list,
+        scene_manager_get_scene_state(app->scene_manager, MassStorageSceneCreateImage));
+
+    view_dispatcher_switch_to_view(app->view_dispatcher, MassStorageAppViewStart);
+}
+
+static void popup_callback_ok(void* context) {
+    MassStorageApp* app = context;
+    scene_manager_set_scene_state(
+        app->scene_manager, MassStorageSceneStart, MassStorageSceneFileSelect);
+    scene_manager_previous_scene(app->scene_manager);
+    scene_manager_next_scene(app->scene_manager, MassStorageSceneFileSelect);
+}
+
+static void popup_callback_error(void* context) {
+    MassStorageApp* app = context;
+    view_dispatcher_switch_to_view(app->view_dispatcher, MassStorageAppViewStart);
+}
+
+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 VarItemListIndexImageName:
+            scene_manager_next_scene(app->scene_manager, MassStorageSceneCreateImageName);
+            break;
+        case VarItemListIndexCreateImage: {
+            mass_storage_app_show_loading_popup(app, true);
+            const char* name = strnlen(app->create_image_name, sizeof(app->create_image_name)) ?
+                                   app->create_image_name :
+                                   image_sizes[app->create_image_size].name;
+            furi_string_printf(
+                app->file_path,
+                "%s/%s%s",
+                MASS_STORAGE_APP_PATH_FOLDER,
+                name,
+                MASS_STORAGE_APP_EXTENSION);
+
+            app->file = storage_file_alloc(app->fs_api);
+            const char* error = NULL;
+            bool success = false;
+
+            do {
+                if(!storage_file_open(
+                       app->file,
+                       furi_string_get_cstr(app->file_path),
+                       FSAM_READ | FSAM_WRITE,
+                       FSOM_CREATE_NEW))
+                    break;
+
+                uint64_t size = image_sizes[app->create_image_size].value;
+                if(size == app->create_image_max) size--;
+                if(!storage_file_expand(app->file, size)) break;
+
+                // Format as exFAT
+                error = "Image formatting failed";
+                if(storage_virtual_init(app->fs_api, app->file) != FSE_OK) {
+                    if(storage_virtual_quit(app->fs_api) != FSE_OK) break;
+                    if(storage_virtual_init(app->fs_api, app->file) != FSE_OK) break;
+                }
+                if(storage_virtual_format(app->fs_api) == FSE_OK) {
+                    success = true;
+                    error = NULL;
+                }
+                storage_virtual_quit(app->fs_api);
+            } while(false);
+
+            if(!success) {
+                error = storage_file_get_error_desc(app->file);
+                FS_Error error = storage_file_get_error(app->file);
+                storage_file_close(app->file);
+                if(error != FSE_EXIST) {
+                    storage_common_remove(app->fs_api, furi_string_get_cstr(app->file_path));
+                }
+            }
+            storage_file_free(app->file);
+            mass_storage_app_show_loading_popup(app, false);
+
+            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);
+            break;
+        }
+        default:
+            break;
+        }
+    }
+
+    return consumed;
+}
+
+void mass_storage_scene_create_image_on_exit(void* context) {
+    MassStorageApp* app = context;
+    variable_item_list_reset(app->variable_item_list);
+}

+ 52 - 0
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, "Image name, empty = 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_image_name,
+        sizeof(app->create_image_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);
+}

+ 0 - 87
mass_storage/scenes/mass_storage_scene_file_name.c

@@ -1,87 +0,0 @@
-#include "../mass_storage_app_i.h"
-
-#define WRITE_BUF_LEN 4096
-
-static void mass_storage_file_name_text_callback(void* context) {
-    furi_assert(context);
-
-    MassStorageApp* app = context;
-    view_dispatcher_send_custom_event(app->view_dispatcher, MassStorageCustomEventNameInput);
-}
-
-static bool mass_storage_create_image(Storage* storage, const char* file_path, uint32_t size) {
-    FURI_LOG_I("TAG", "Creating image %s, len:%lu", file_path, size);
-    File* file = storage_file_alloc(storage);
-
-    bool success = false;
-    uint8_t* buffer = malloc(WRITE_BUF_LEN);
-    do {
-        if(!storage_file_open(file, file_path, FSAM_WRITE, FSOM_CREATE_ALWAYS)) break;
-        if(!storage_file_seek(file, size, true)) break;
-        if(!storage_file_seek(file, 0, true)) break;
-        // Zero out first 4k - partition table and adjacent data
-        if(!storage_file_write(file, buffer, WRITE_BUF_LEN)) break;
-
-        success = true;
-    } while(false);
-
-    free(buffer);
-    storage_file_close(file);
-    storage_file_free(file);
-    return success;
-}
-
-void mass_storage_scene_file_name_on_enter(void* context) {
-    MassStorageApp* app = context;
-
-    text_input_set_header_text(app->text_input, "Enter image name");
-    ValidatorIsFile* validator_is_file =
-        validator_is_file_alloc_init(MASS_STORAGE_APP_PATH_FOLDER, MASS_STORAGE_APP_EXTENSION, "");
-    text_input_set_validator(app->text_input, validator_is_file_callback, validator_is_file);
-
-    text_input_set_result_callback(
-        app->text_input,
-        mass_storage_file_name_text_callback,
-        app,
-        app->new_file_name,
-        MASS_STORAGE_FILE_NAME_LEN,
-        true);
-    view_dispatcher_switch_to_view(app->view_dispatcher, MassStorageAppViewTextInput);
-}
-
-bool mass_storage_scene_file_name_on_event(void* context, SceneManagerEvent event) {
-    UNUSED(event);
-    MassStorageApp* app = context;
-
-    bool consumed = false;
-
-    if(event.type == SceneManagerEventTypeCustom) {
-        if(event.event == MassStorageCustomEventNameInput) {
-            mass_storage_app_show_loading_popup(app, true);
-            furi_string_printf(
-                app->file_path,
-                "%s/%s%s",
-                MASS_STORAGE_APP_PATH_FOLDER,
-                app->new_file_name,
-                MASS_STORAGE_APP_EXTENSION);
-            if(mass_storage_create_image(
-                   app->fs_api, furi_string_get_cstr(app->file_path), app->new_file_size)) {
-                if(!furi_hal_usb_is_locked()) {
-                    scene_manager_next_scene(app->scene_manager, MassStorageSceneWork);
-                } else {
-                    scene_manager_next_scene(app->scene_manager, MassStorageSceneUsbLocked);
-                }
-            } // TODO: error message screen
-        }
-    }
-    return consumed;
-}
-
-void mass_storage_scene_file_name_on_exit(void* context) {
-    UNUSED(context);
-    MassStorageApp* app = context;
-    void* validator_context = text_input_get_validator_callback_context(app->text_input);
-    text_input_set_validator(app->text_input, NULL, NULL);
-    validator_is_file_free(validator_context);
-    text_input_reset(app->text_input);
-}

+ 2 - 6
mass_storage/scenes/mass_storage_scene_file_select.c

@@ -6,7 +6,7 @@ static bool mass_storage_file_select(MassStorageApp* mass_storage) {
 
 
     DialogsFileBrowserOptions browser_options;
     DialogsFileBrowserOptions browser_options;
     dialog_file_browser_set_basic_options(
     dialog_file_browser_set_basic_options(
-        &browser_options, MASS_STORAGE_APP_EXTENSION, &I_mass_storage_10px);
+        &browser_options, MASS_STORAGE_APP_EXTENSION "|.iso", &I_floppydisk_10px);
     browser_options.base_path = MASS_STORAGE_APP_PATH_FOLDER;
     browser_options.base_path = MASS_STORAGE_APP_PATH_FOLDER;
     browser_options.hide_ext = false;
     browser_options.hide_ext = false;
 
 
@@ -20,11 +20,7 @@ void mass_storage_scene_file_select_on_enter(void* context) {
     MassStorageApp* mass_storage = context;
     MassStorageApp* mass_storage = context;
 
 
     if(mass_storage_file_select(mass_storage)) {
     if(mass_storage_file_select(mass_storage)) {
-        if(!furi_hal_usb_is_locked()) {
-            scene_manager_next_scene(mass_storage->scene_manager, MassStorageSceneWork);
-        } else {
-            scene_manager_next_scene(mass_storage->scene_manager, MassStorageSceneUsbLocked);
-        }
+        scene_manager_next_scene(mass_storage->scene_manager, MassStorageSceneWork);
     } else {
     } else {
         scene_manager_previous_scene(mass_storage->scene_manager);
         scene_manager_previous_scene(mass_storage->scene_manager);
     }
     }

+ 31 - 42
mass_storage/scenes/mass_storage_scene_start.c

@@ -1,54 +1,32 @@
 #include "../mass_storage_app_i.h"
 #include "../mass_storage_app_i.h"
 
 
-static const struct {
-    char* name;
-    uint32_t value;
-} image_size[] = {
-    {"1.44M", 1440 * 1024},
-    {"2M", 2 * 1024 * 1024},
-    {"4M", 4 * 1024 * 1024},
-    {"8M", 8 * 1024 * 1024},
-    {"16M", 16 * 1024 * 1024},
-    {"32M", 32 * 1024 * 1024},
-    {"64M", 64 * 1024 * 1024},
-    {"128M", 128 * 1024 * 1024},
-    {"256M", 256 * 1024 * 1024},
-    {"512M", 512 * 1024 * 1024},
-    {"700M", 700 * 1024 * 1024},
-    {"1G", 1024 * 1024 * 1024},
-    {"2G", 2u * 1024 * 1024 * 1024},
+enum VarItemListIndex {
+    VarItemListIndexSelectDiskImage,
+    VarItemListIndexCreateDiskImage,
 };
 };
 
 
-static void mass_storage_item_select(void* context, uint32_t index) {
+static void mass_storage_scene_start_variable_item_list_callback(void* context, uint32_t index) {
     MassStorageApp* app = context;
     MassStorageApp* app = context;
-    if(index == 0) {
-        view_dispatcher_send_custom_event(app->view_dispatcher, MassStorageCustomEventFileSelect);
-    } else {
-        view_dispatcher_send_custom_event(app->view_dispatcher, MassStorageCustomEventNewImage);
-    }
-}
-
-static void mass_storage_image_size(VariableItem* item) {
-    MassStorageApp* app = variable_item_get_context(item);
-    uint8_t index = variable_item_get_current_value_index(item);
-    variable_item_set_current_value_text(item, image_size[index].name);
-    app->new_file_size = image_size[index].value;
+    view_dispatcher_send_custom_event(app->view_dispatcher, index);
 }
 }
 
 
 void mass_storage_scene_start_on_enter(void* context) {
 void mass_storage_scene_start_on_enter(void* context) {
     MassStorageApp* app = context;
     MassStorageApp* app = context;
+    VariableItemList* variable_item_list = app->variable_item_list;
+
+    variable_item_list_add(variable_item_list, "Select Disk Image", 0, NULL, app);
 
 
-    VariableItem* item =
-        variable_item_list_add(app->variable_item_list, "Select disk image", 0, NULL, NULL);
+    variable_item_list_add(variable_item_list, "Create Disk Image", 0, NULL, app);
 
 
-    item = variable_item_list_add(
-        app->variable_item_list, "New image", COUNT_OF(image_size), mass_storage_image_size, app);
+    variable_item_list_set_enter_callback(
+        variable_item_list, mass_storage_scene_start_variable_item_list_callback, app);
 
 
-    variable_item_list_set_enter_callback(app->variable_item_list, mass_storage_item_select, app);
+    variable_item_list_set_header(variable_item_list, "USB Mass Storage");
+
+    variable_item_list_set_selected_item(
+        variable_item_list,
+        scene_manager_get_scene_state(app->scene_manager, MassStorageSceneStart));
 
 
-    variable_item_set_current_value_index(item, 2);
-    variable_item_set_current_value_text(item, image_size[2].name);
-    app->new_file_size = image_size[2].value;
     view_dispatcher_switch_to_view(app->view_dispatcher, MassStorageAppViewStart);
     view_dispatcher_switch_to_view(app->view_dispatcher, MassStorageAppViewStart);
 }
 }
 
 
@@ -57,14 +35,25 @@ bool mass_storage_scene_start_on_event(void* context, SceneManagerEvent event) {
     UNUSED(event);
     UNUSED(event);
     MassStorageApp* app = context;
     MassStorageApp* app = context;
 
 
+    bool consumed = false;
+
     if(event.type == SceneManagerEventTypeCustom) {
     if(event.type == SceneManagerEventTypeCustom) {
-        if(event.event == MassStorageCustomEventFileSelect) {
+        scene_manager_set_scene_state(app->scene_manager, MassStorageSceneStart, event.event);
+        consumed = true;
+        switch(event.event) {
+        case VarItemListIndexSelectDiskImage:
             scene_manager_next_scene(app->scene_manager, MassStorageSceneFileSelect);
             scene_manager_next_scene(app->scene_manager, MassStorageSceneFileSelect);
-        } else if(event.event == MassStorageCustomEventNewImage) {
-            scene_manager_next_scene(app->scene_manager, MassStorageSceneFileName);
+            break;
+        case VarItemListIndexCreateDiskImage:
+            scene_manager_set_scene_state(app->scene_manager, MassStorageSceneCreateImage, 0);
+            scene_manager_next_scene(app->scene_manager, MassStorageSceneCreateImage);
+            break;
+        default:
+            break;
         }
         }
     }
     }
-    return false;
+
+    return consumed;
 }
 }
 
 
 void mass_storage_scene_start_on_exit(void* context) {
 void mass_storage_scene_start_on_exit(void* context) {

+ 1 - 0
mass_storage/scenes/mass_storage_scene_work.c

@@ -109,6 +109,7 @@ void mass_storage_scene_work_on_enter(void* context) {
         .eject = file_eject,
         .eject = file_eject,
     };
     };
 
 
+    furi_hal_usb_unlock();
     app->usb = mass_storage_usb_start(furi_string_get_cstr(file_name), fn);
     app->usb = mass_storage_usb_start(furi_string_get_cstr(file_name), fn);
 
 
     furi_string_free(file_name);
     furi_string_free(file_name);

+ 10 - 10
mass_storage/views/mass_storage_view.c

@@ -19,9 +19,9 @@ static void append_suffixed_byte_count(FuriString* string, uint32_t count) {
     } else if(count < 1024 * 1024) {
     } else if(count < 1024 * 1024) {
         furi_string_cat_printf(string, "%luK", count / 1024);
         furi_string_cat_printf(string, "%luK", count / 1024);
     } else if(count < 1024 * 1024 * 1024) {
     } else if(count < 1024 * 1024 * 1024) {
-        furi_string_cat_printf(string, "%.3fM", (double)count / (1024 * 1024));
+        furi_string_cat_printf(string, "%.1fM", (double)count / (1024 * 1024));
     } else {
     } else {
-        furi_string_cat_printf(string, "%.3fG", (double)count / (1024 * 1024 * 1024));
+        furi_string_cat_printf(string, "%.1fG", (double)count / (1024 * 1024 * 1024));
     }
     }
 }
 }
 
 
@@ -34,28 +34,28 @@ static void mass_storage_draw_callback(Canvas* canvas, void* _model) {
     canvas_draw_str_aligned(
     canvas_draw_str_aligned(
         canvas, canvas_width(canvas) / 2, 0, AlignCenter, AlignTop, "USB Mass Storage");
         canvas, canvas_width(canvas) / 2, 0, AlignCenter, AlignTop, "USB Mass Storage");
 
 
-    canvas_set_font(canvas, FontSecondary);
+    canvas_set_font(canvas, FontBatteryPercent);
     elements_string_fit_width(canvas, model->file_name, 89 - 2);
     elements_string_fit_width(canvas, model->file_name, 89 - 2);
     canvas_draw_str_aligned(
     canvas_draw_str_aligned(
-        canvas, 50, 23, AlignCenter, AlignBottom, furi_string_get_cstr(model->file_name));
+        canvas, 92, 24, AlignRight, AlignBottom, furi_string_get_cstr(model->file_name));
 
 
     furi_string_set_str(model->status_string, "R:");
     furi_string_set_str(model->status_string, "R:");
     append_suffixed_byte_count(model->status_string, model->bytes_read);
     append_suffixed_byte_count(model->status_string, model->bytes_read);
     if(model->read_speed) {
     if(model->read_speed) {
-        furi_string_cat_str(model->status_string, "; ");
+        furi_string_cat_str(model->status_string, "/");
         append_suffixed_byte_count(model->status_string, model->read_speed);
         append_suffixed_byte_count(model->status_string, model->read_speed);
-        furi_string_cat_str(model->status_string, "ps");
+        furi_string_cat_str(model->status_string, "s");
     }
     }
-    canvas_draw_str(canvas, 12, 34, furi_string_get_cstr(model->status_string));
+    canvas_draw_str(canvas, 14, 34, furi_string_get_cstr(model->status_string));
 
 
     furi_string_set_str(model->status_string, "W:");
     furi_string_set_str(model->status_string, "W:");
     append_suffixed_byte_count(model->status_string, model->bytes_written);
     append_suffixed_byte_count(model->status_string, model->bytes_written);
     if(model->write_speed) {
     if(model->write_speed) {
-        furi_string_cat_str(model->status_string, "; ");
+        furi_string_cat_str(model->status_string, "/");
         append_suffixed_byte_count(model->status_string, model->write_speed);
         append_suffixed_byte_count(model->status_string, model->write_speed);
-        furi_string_cat_str(model->status_string, "ps");
+        furi_string_cat_str(model->status_string, "s");
     }
     }
-    canvas_draw_str(canvas, 12, 44, furi_string_get_cstr(model->status_string));
+    canvas_draw_str(canvas, 14, 43, furi_string_get_cstr(model->status_string));
 }
 }
 
 
 MassStorage* mass_storage_alloc() {
 MassStorage* mass_storage_alloc() {