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

feat: add save repeat count to app

DerSkythe пре 2 година
родитељ
комит
579f5d7d32

+ 4 - 4
helpers/subbrute_worker.c

@@ -85,7 +85,7 @@ bool subbrute_worker_init_default_attack(
     SubBruteAttacks attack_type,
     uint64_t step,
     const SubBruteProtocol* protocol,
-    uint8_t extra_repeats) {
+    uint8_t repeats) {
     furi_assert(instance);
 
     if(instance->worker_running) {
@@ -100,7 +100,7 @@ bool subbrute_worker_init_default_attack(
     instance->step = step;
     instance->bits = protocol->bits;
     instance->te = protocol->te;
-    instance->repeat = protocol->repeat + extra_repeats;
+    instance->repeat = repeats;
     instance->load_index = 0;
     instance->file_key = 0;
     instance->two_bytes = false;
@@ -133,7 +133,7 @@ bool subbrute_worker_init_file_attack(
     uint8_t load_index,
     uint64_t file_key,
     SubBruteProtocol* protocol,
-    uint8_t extra_repeats,
+    uint8_t repeats,
     bool two_bytes) {
     furi_assert(instance);
 
@@ -150,7 +150,7 @@ bool subbrute_worker_init_file_attack(
     instance->bits = protocol->bits;
     instance->te = protocol->te;
     instance->load_index = load_index;
-    instance->repeat = protocol->repeat + extra_repeats;
+    instance->repeat = repeats;
     instance->file_key = file_key;
     instance->two_bytes = two_bytes;
 

+ 2 - 2
helpers/subbrute_worker.h

@@ -24,14 +24,14 @@ bool subbrute_worker_init_default_attack(
     SubBruteAttacks attack_type,
     uint64_t step,
     const SubBruteProtocol* protocol,
-    uint8_t extra_repeats);
+    uint8_t repeats);
 bool subbrute_worker_init_file_attack(
     SubBruteWorker* instance,
     uint64_t step,
     uint8_t load_index,
     uint64_t file_key,
     SubBruteProtocol* protocol,
-    uint8_t extra_repeats,
+    uint8_t repeats,
     bool two_bytes);
 bool subbrute_worker_start(SubBruteWorker* instance);
 void subbrute_worker_stop(SubBruteWorker* instance);

+ 7 - 3
scenes/subbrute_scene_load_file.c

@@ -36,10 +36,13 @@ void subbrute_scene_load_file_on_enter(void* context) {
         load_result =
             subbrute_device_load_from_file(instance->device, furi_string_get_cstr(load_path));
         if(load_result == SubBruteFileResultOk) {
-            uint8_t extra_repeats = subbrute_main_view_get_extra_repeats(instance->view_main);
+            instance->settings->last_index = SubBruteAttackLoadFile;
+            subbrute_settings_set_repeats(
+                instance->settings, subbrute_main_view_get_extra_repeats(instance->view_main));
+            uint8_t extra_repeats = subbrute_settings_get_current_repeats(instance->settings);
 
             load_result = subbrute_device_attack_set(
-                instance->device, SubBruteAttackLoadFile, extra_repeats);
+                instance->device, instance->settings->last_index, extra_repeats);
             if(load_result == SubBruteFileResultOk) {
                 if(!subbrute_worker_init_file_attack(
                        instance->worker,
@@ -58,6 +61,7 @@ void subbrute_scene_load_file_on_enter(void* context) {
         }
 
         if(load_result == SubBruteFileResultOk) {
+            subbrute_settings_save(instance->settings);
             scene_manager_next_scene(instance->scene_manager, SubBruteSceneLoadSelect);
         } else {
             FURI_LOG_E(TAG, "Returned error: %d", load_result);
@@ -88,4 +92,4 @@ bool subbrute_scene_load_file_on_event(void* context, SceneManagerEvent event) {
     UNUSED(context);
     UNUSED(event);
     return false;
-}
+}

+ 15 - 4
scenes/subbrute_scene_load_select.c

@@ -21,7 +21,12 @@ void subbrute_scene_load_select_on_enter(void* context) {
     instance->current_view = SubBruteViewMain;
     subbrute_main_view_set_callback(view, subbrute_scene_load_select_callback, instance);
     subbrute_main_view_set_index(
-        view, 7, true, instance->device->two_bytes, instance->device->key_from_file);
+        view,
+        7,
+        instance->settings->repeat_values,
+        true,
+        instance->device->two_bytes,
+        instance->device->key_from_file);
 
     view_dispatcher_switch_to_view(instance->view_dispatcher, instance->current_view);
 }
@@ -46,7 +51,12 @@ bool subbrute_scene_load_select_on_event(void* context, SceneManagerEvent event)
             instance->device->current_step = 0;
             instance->device->bit_index = subbrute_main_view_get_index(instance->view_main);
             instance->device->two_bytes = subbrute_main_view_get_two_bytes(instance->view_main);
-            uint8_t extra_repeats = subbrute_main_view_get_extra_repeats(instance->view_main);
+
+            instance->settings->last_index = instance->device->attack;
+            subbrute_settings_set_repeats(
+                instance->settings, subbrute_main_view_get_extra_repeats(instance->view_main));
+            uint8_t total_repeats = subbrute_settings_get_current_repeats(instance->settings);
+
             instance->device->max_value = subbrute_protocol_calc_max_value(
                 instance->device->attack,
                 instance->device->bit_index,
@@ -58,10 +68,11 @@ bool subbrute_scene_load_select_on_event(void* context, SceneManagerEvent event)
                    instance->device->bit_index,
                    instance->device->key_from_file,
                    instance->device->file_protocol_info,
-                   extra_repeats,
+                   total_repeats,
                    instance->device->two_bytes)) {
                 furi_crash("Invalid attack set!");
             }
+            subbrute_settings_save(instance->settings);
             scene_manager_next_scene(instance->scene_manager, SubBruteSceneSetupAttack);
             /*#endif*/
             consumed = true;
@@ -79,4 +90,4 @@ bool subbrute_scene_load_select_on_event(void* context, SceneManagerEvent event)
     }
 
     return consumed;
-}
+}

+ 3 - 3
scenes/subbrute_scene_setup_extra.c

@@ -12,7 +12,7 @@
 
 enum SubBruteVarListIndex {
     SubBruteVarListIndexTimeDelay,
-    SubBruteVarListIndexRepeat_or_OnExtra,
+    SubBruteVarListIndexRepeatOrOnExtra,
     SubBruteVarListIndexTe,
 };
 
@@ -255,7 +255,7 @@ static void setup_extra_enter_callback(void* context, uint32_t index) {
     furi_assert(context);
     SubBruteState* instance = context;
 
-    if(index == SubBruteVarListIndexRepeat_or_OnExtra) {
+    if(index == SubBruteVarListIndexRepeatOrOnExtra) {
         subbrute_scene_setup_extra_init_var_list(instance, true);
     }
 }
@@ -278,4 +278,4 @@ bool subbrute_scene_setup_extra_on_event(void* context, SceneManagerEvent event)
     UNUSED(context);
     UNUSED(event);
     return false;
-}
+}

+ 22 - 5
scenes/subbrute_scene_start.c

@@ -20,8 +20,16 @@ void subbrute_scene_start_on_enter(void* context) {
 
     instance->current_view = SubBruteViewMain;
     subbrute_main_view_set_callback(view, subbrute_scene_start_callback, instance);
+
+    instance->device->attack = instance->settings->last_index;
+
     subbrute_main_view_set_index(
-        view, instance->device->attack, false, instance->device->two_bytes, 0);
+        view,
+        instance->settings->last_index,
+        instance->settings->repeat_values,
+        false,
+        instance->device->two_bytes,
+        0);
 
     view_dispatcher_switch_to_view(instance->view_dispatcher, instance->current_view);
 }
@@ -44,19 +52,23 @@ bool subbrute_scene_start_on_event(void* context, SceneManagerEvent event) {
             event.event == SubBruteCustomEventTypeLoadFile ? "true" : "false");
 #endif
         if(event.event == SubBruteCustomEventTypeMenuSelected) {
-            SubBruteAttacks attack = subbrute_main_view_get_index(instance->view_main);
-            uint8_t extra_repeats = subbrute_main_view_get_extra_repeats(instance->view_main);
+            instance->settings->last_index = subbrute_main_view_get_index(instance->view_main);
+            subbrute_settings_set_repeats(
+                instance->settings, subbrute_main_view_get_extra_repeats(instance->view_main));
+            uint8_t total_repeats = subbrute_settings_get_current_repeats(instance->settings);
 
-            if((subbrute_device_attack_set(instance->device, attack, extra_repeats) !=
+            if((subbrute_device_attack_set(
+                    instance->device, instance->settings->last_index, total_repeats) !=
                 SubBruteFileResultOk) ||
                (!subbrute_worker_init_default_attack(
                    instance->worker,
-                   attack,
+                   instance->settings->last_index,
                    instance->device->current_step,
                    instance->device->protocol_info,
                    instance->device->extra_repeats))) {
                 furi_crash("Invalid attack set!");
             }
+            subbrute_settings_save(instance->settings);
             scene_manager_next_scene(instance->scene_manager, SubBruteSceneSetupAttack);
 
             consumed = true;
@@ -69,6 +81,11 @@ bool subbrute_scene_start_on_event(void* context, SceneManagerEvent event) {
         }
     } else if(event.type == SceneManagerEventTypeBack) {
         //exit app
+        instance->settings->last_index = subbrute_main_view_get_index(instance->view_main);
+        subbrute_settings_set_repeats(
+            instance->settings, subbrute_main_view_get_extra_repeats(instance->view_main));
+        subbrute_settings_save(instance->settings);
+
         scene_manager_stop(instance->scene_manager);
         view_dispatcher_stop(instance->view_dispatcher);
         consumed = true;

+ 5 - 1
subbrute.c

@@ -105,6 +105,8 @@ SubBruteState* subbrute_alloc() {
         SubBruteViewAttack,
         subbrute_attack_view_get_view(instance->view_attack));
 
+    instance->settings = subbrute_settings_alloc();
+    subbrute_settings_load(instance->settings);
     //instance->flipper_format = flipper_format_string_alloc();
     //instance->environment = subghz_environment_alloc();
 
@@ -126,9 +128,11 @@ void subbrute_free(SubBruteState* instance) {
 
     // SubBruteDevice
     subbrute_device_free(instance->device);
-
     subghz_devices_deinit();
 
+    //subbrute_settings_save(instance->settings);
+    subbrute_settings_free(instance->settings);
+
     // Notifications
     notification_message(instance->notifications, &sequence_blink_stop);
     furi_record_close(RECORD_NOTIFICATION);

+ 4 - 1
subbrute_i.h

@@ -26,11 +26,12 @@
 
 #include "subbrute.h"
 #include "subbrute_device.h"
+#include "subbrute_settings.h"
 #include "helpers/subbrute_worker.h"
 #include "views/subbrute_attack_view.h"
 #include "views/subbrute_main_view.h"
 
-#define SUBBRUTEFORCER_VER "Sub-GHz BruteForcer 3.8"
+#define SUBBRUTEFORCER_VER "Sub-GHz BruteForcer 3.9"
 
 #ifdef FURI_DEBUG
 //#define SUBBRUTE_FAST_TRACK false
@@ -77,6 +78,8 @@ struct SubBruteState {
     SubBruteDevice* device;
     // SubBruteWorker
     SubBruteWorker* worker;
+    // Last used settings
+    SubBruteSettings* settings;
 };
 
 void subbrute_show_loading_popup(void* context, bool show);

+ 3 - 1
subbrute_protocols.h

@@ -5,6 +5,8 @@
 #include <core/string.h>
 #include <toolbox/stream/stream.h>
 
+#define SUBBRUTE_PROTOCOL_MAX_REPEATS 9
+
 typedef enum {
     CAMEFileProtocol,
     NICEFileProtocol,
@@ -123,4 +125,4 @@ void subbrute_protocol_file_generate_file(
     uint64_t file_key,
     bool two_bytes);
 uint64_t
-    subbrute_protocol_calc_max_value(SubBruteAttacks attack_type, uint8_t bits, bool two_bytes);
+    subbrute_protocol_calc_max_value(SubBruteAttacks attack_type, uint8_t bits, bool two_bytes);

+ 147 - 0
subbrute_settings.c

@@ -0,0 +1,147 @@
+#include "subbrute_settings.h"
+#include "subbrute_i.h"
+
+#define TAG "SubBruteSettings"
+
+#define SUBBRUTE_SETTINGS_FILE_TYPE "Sub-GHz BruteForcer Settings File"
+#define SUBBRUTE_SETTINGS_FILE_VERSION 1
+#define SUBBRUTE_SETTINGS_PATH EXT_PATH("subghz-bruteforcer.settings")
+
+#define SUBBRUTE_FIELD_LAST_INDEX "LastIndex"
+#define SUBBRUTE_FIELD_REPEAT_VALUES "RepeatValue"
+
+SubBruteSettings* subbrute_settings_alloc(void) {
+    SubBruteSettings* instance = malloc(sizeof(SubBruteSettings));
+    return instance;
+}
+
+void subbrute_settings_free(SubBruteSettings* instance) {
+    furi_assert(instance);
+    free(instance);
+}
+
+void subbrute_settings_load(SubBruteSettings* instance) {
+    furi_assert(instance);
+
+    Storage* storage = furi_record_open(RECORD_STORAGE);
+    FlipperFormat* fff_data_file = flipper_format_file_alloc(storage);
+
+    uint32_t temp_last_index = 0;
+    uint8_t temp_repeat_values[SubBruteAttackTotalCount] = {0};
+    bool was_read_last_index = false;
+    bool was_read_repeat_values = false;
+
+    if(FSE_OK == storage_sd_status(storage) && SUBBRUTE_SETTINGS_PATH &&
+       flipper_format_file_open_existing(fff_data_file, SUBBRUTE_SETTINGS_PATH)) {
+        was_read_last_index = flipper_format_read_uint32(
+            fff_data_file, SUBBRUTE_FIELD_LAST_INDEX, (uint32_t*)&temp_last_index, 1);
+        was_read_repeat_values = flipper_format_read_hex(
+            fff_data_file,
+            SUBBRUTE_FIELD_REPEAT_VALUES,
+            temp_repeat_values,
+            SubBruteAttackTotalCount);
+    } else {
+        FURI_LOG_E(TAG, "Error open file %s", SUBBRUTE_SETTINGS_PATH);
+    }
+
+    if(was_read_last_index && temp_last_index < SubBruteAttackTotalCount) {
+        instance->last_index = temp_last_index;
+    } else {
+        FURI_LOG_W(TAG, "Last used index not found or can't be used!");
+        instance->last_index = (uint32_t)SubBruteAttackCAME12bit433;
+    }
+    if(was_read_repeat_values) {
+        for(size_t i = 0; i < SubBruteAttackTotalCount; i++) {
+            uint8_t protocol_count = subbrute_protocol_repeats_count(i);
+            uint8_t max_protocol_count = protocol_count * 3;
+            if(temp_repeat_values[i] < protocol_count) {
+                instance->repeat_values[i] = protocol_count;
+            } else if(temp_repeat_values[i] > max_protocol_count) {
+                instance->repeat_values[i] = max_protocol_count;
+            } else {
+                instance->repeat_values[i] = temp_repeat_values[i];
+            }
+        }
+    } else {
+        FURI_LOG_W(TAG, "Last used repeat values can't be used!");
+        for(size_t i = 0; i < SubBruteAttackTotalCount; i++) {
+            instance->repeat_values[i] = subbrute_protocol_repeats_count(i);
+        }
+    }
+
+    flipper_format_file_close(fff_data_file);
+    flipper_format_free(fff_data_file);
+    furi_record_close(RECORD_STORAGE);
+}
+
+bool subbrute_settings_save(SubBruteSettings* instance) {
+    furi_assert(instance);
+
+    bool saved = false;
+    Storage* storage = furi_record_open(RECORD_STORAGE);
+    FlipperFormat* file = flipper_format_file_alloc(storage);
+
+    do {
+        if(FSE_OK != storage_sd_status(storage)) {
+            break;
+        }
+        // Open file
+        if(!flipper_format_file_open_always(file, SUBBRUTE_SETTINGS_PATH)) {
+            break;
+        }
+        // Write header
+        if(!flipper_format_write_header_cstr(
+               file, SUBBRUTE_SETTINGS_FILE_TYPE, SUBBRUTE_SETTINGS_FILE_VERSION)) {
+            break;
+        }
+        if(!flipper_format_insert_or_update_uint32(
+               file, SUBBRUTE_FIELD_LAST_INDEX, &instance->last_index, 1)) {
+            break;
+        }
+
+        if(!flipper_format_insert_or_update_hex(
+               file,
+               SUBBRUTE_FIELD_REPEAT_VALUES,
+               instance->repeat_values,
+               SubBruteAttackTotalCount)) {
+            break;
+        }
+        saved = true;
+        break;
+    } while(true);
+
+    if(!saved) {
+        FURI_LOG_E(TAG, "Error save file %s", SUBBRUTE_SETTINGS_PATH);
+    }
+
+    flipper_format_file_close(file);
+    flipper_format_free(file);
+    furi_record_close(RECORD_STORAGE);
+
+    return saved;
+}
+
+void subbrute_settings_set_value(SubBruteSettings* instance, SubBruteAttacks index, uint8_t value) {
+    furi_assert(instance);
+
+    instance->repeat_values[index] = value;
+}
+uint8_t subbrute_settings_get_value(SubBruteSettings* instance, SubBruteAttacks index) {
+    furi_assert(instance);
+
+    return instance->repeat_values[index];
+}
+
+void subbrute_settings_set_repeats(SubBruteSettings* instance, const uint8_t* repeated_values) {
+    furi_assert(instance);
+
+    for(size_t i = 0; i < SubBruteAttackTotalCount; i++) {
+        instance->repeat_values[i] = repeated_values[i];
+    }
+}
+
+uint8_t subbrute_settings_get_current_repeats(SubBruteSettings* instance) {
+    furi_assert(instance);
+
+    return instance->repeat_values[instance->last_index];
+}

+ 21 - 0
subbrute_settings.h

@@ -0,0 +1,21 @@
+#pragma once
+
+#include <furi_hal.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <storage/storage.h>
+#include "subbrute_protocols.h"
+
+typedef struct {
+    uint8_t repeat_values[SubBruteAttackTotalCount];
+    uint32_t last_index;
+} SubBruteSettings;
+
+SubBruteSettings* subbrute_settings_alloc(void);
+void subbrute_settings_free(SubBruteSettings* instance);
+void subbrute_settings_load(SubBruteSettings* instance);
+bool subbrute_settings_save(SubBruteSettings* instance);
+void subbrute_settings_set_value(SubBruteSettings* instance, SubBruteAttacks index, uint8_t value);
+uint8_t subbrute_settings_get_value(SubBruteSettings* instance, SubBruteAttacks index);
+void subbrute_settings_set_repeats(SubBruteSettings* instance, const uint8_t* repeated_values);
+uint8_t subbrute_settings_get_current_repeats(SubBruteSettings* instance);

+ 4 - 4
views/subbrute_attack_view.c

@@ -24,7 +24,7 @@ typedef struct {
     SubBruteAttacks attack_type;
     uint64_t max_value;
     uint64_t current_step;
-    uint8_t extra_repeats;
+    uint8_t repeat_count;
     bool is_attacking;
     IconAnimation* icon;
 } SubBruteAttackViewModel;
@@ -234,7 +234,7 @@ void subbrute_attack_view_init_values(
             model->attack_type = index;
             model->current_step = current_step;
             model->is_attacking = is_attacking;
-            model->extra_repeats = extra_repeats;
+            model->repeat_count = extra_repeats;
             if(is_attacking) {
                 icon_animation_start(model->icon);
             } else {
@@ -306,7 +306,7 @@ void subbrute_attack_view_draw(Canvas* canvas, void* context) {
             buffer,
             sizeof(buffer),
             "x%d",
-            model->extra_repeats); // + subbrute_protocol_repeats_count(model->attack_type));
+            model->repeat_count); // + subbrute_protocol_repeats_count(model->attack_type));
         canvas_draw_str_aligned(canvas, 60, 6, AlignCenter, AlignCenter, buffer);
 
         elements_button_left(canvas, "-1");
@@ -333,7 +333,7 @@ void subbrute_attack_view_draw(Canvas* canvas, void* context) {
             buffer,
             sizeof(buffer),
             "x%d",
-            model->extra_repeats); // + subbrute_protocol_repeats_count(model->attack_type));
+            model->repeat_count); // + subbrute_protocol_repeats_count(model->attack_type));
         canvas_draw_str(canvas, 4, y - 8, buffer);
         canvas_draw_str(canvas, 4, y - 1, "repeats");
 

+ 228 - 192
views/subbrute_main_view.c

@@ -27,13 +27,13 @@ struct SubBruteMainView {
     bool is_select_byte;
     bool two_bytes;
     uint64_t key_from_file;
-    uint8_t extra_repeats;
+    uint8_t repeat_values[SubBruteAttackTotalCount];
     uint8_t window_position;
 };
 
 typedef struct {
     uint8_t index;
-    uint8_t extra_repeats;
+    uint8_t repeat_values[SubBruteAttackTotalCount];
     uint8_t window_position;
     bool is_select_byte;
     bool two_bytes;
@@ -108,217 +108,252 @@ void subbrute_main_view_center_displayed_key(
     canvas_set_color(canvas, ColorBlack);
 }
 
-void subbrute_main_view_draw(Canvas* canvas, SubBruteMainViewModel* model) {
-    uint16_t screen_width = canvas_width(canvas);
-    uint16_t screen_height = canvas_height(canvas);
-
-    if(model->is_select_byte) {
+void subbrute_main_view_draw_is_byte_selected(Canvas* canvas, SubBruteMainViewModel* model) {
 #ifdef FURI_DEBUG
-        //FURI_LOG_D(TAG, "key_from_file: %s", model->key_from_file);
+    //FURI_LOG_D(TAG, "key_from_file: %s", model->key_from_file);
 #endif
-        //char msg_index[18];
-        //snprintf(msg_index, sizeof(msg_index), "Field index: %d", model->index);
-        canvas_set_font(canvas, FontSecondary);
-        canvas_draw_str_aligned(
-            canvas, 64, 17, AlignCenter, AlignTop, "Please select values to calc:");
-
-        subbrute_main_view_center_displayed_key(
-            canvas, model->key_from_file, model->index, model->two_bytes);
-        //const char* line = furi_string_get_cstr(menu_items);
-        //canvas_set_font(canvas, FontSecondary);
-        //canvas_draw_str_aligned(
-        //    canvas, 64, 37, AlignCenter, AlignTop, furi_string_get_cstr(menu_items));
-
-        elements_button_center(canvas, "Select");
-        if(model->index > 0) {
-            elements_button_left(canvas, " ");
-        }
-        if(model->index < 7) {
-            elements_button_right(canvas, " ");
-        }
-        // Switch to another mode
-        if(model->two_bytes) {
-            elements_button_top_left(canvas, "One byte");
-        } else {
-            elements_button_top_left(canvas, "Two bytes");
-        }
+    //char msg_index[18];
+    //snprintf(msg_index, sizeof(msg_index), "Field index: %d", model->index);
+    canvas_set_font(canvas, FontSecondary);
+    canvas_draw_str_aligned(
+        canvas, 64, 17, AlignCenter, AlignTop, "Please select values to calc:");
+
+    subbrute_main_view_center_displayed_key(
+        canvas, model->key_from_file, model->index, model->two_bytes);
+    //const char* line = furi_string_get_cstr(menu_items);
+    //canvas_set_font(canvas, FontSecondary);
+    //canvas_draw_str_aligned(
+    //    canvas, 64, 37, AlignCenter, AlignTop, furi_string_get_cstr(menu_items));
+
+    elements_button_center(canvas, "Select");
+    if(model->index > 0) {
+        elements_button_left(canvas, " ");
+    }
+    if(model->index < 7) {
+        elements_button_right(canvas, " ");
+    }
+    // Switch to another mode
+    if(model->two_bytes) {
+        elements_button_top_left(canvas, "One byte");
     } else {
-        // Title
-        canvas_set_font(canvas, FontPrimary);
-        canvas_draw_box(canvas, 0, 0, canvas_width(canvas), STATUS_BAR_Y_SHIFT);
-        canvas_invert_color(canvas);
-        canvas_draw_str_aligned(canvas, 64, 3, AlignCenter, AlignTop, SUBBRUTEFORCER_VER);
-        canvas_invert_color(canvas);
-
-        // Menu
-        canvas_set_color(canvas, ColorBlack);
-        canvas_set_font(canvas, FontSecondary);
-        const uint8_t item_height = 16;
+        elements_button_top_left(canvas, "Two bytes");
+    }
+}
+
+void subbrute_main_view_draw_is_ordinary_selected(Canvas* canvas, SubBruteMainViewModel* model) {
+    uint16_t screen_width = canvas_width(canvas);
+    uint16_t screen_height = canvas_height(canvas);
+
+    // Title
+    canvas_set_font(canvas, FontPrimary);
+    canvas_draw_box(canvas, 0, 0, canvas_width(canvas), STATUS_BAR_Y_SHIFT);
+    canvas_invert_color(canvas);
+    canvas_draw_str_aligned(canvas, 64, 3, AlignCenter, AlignTop, SUBBRUTEFORCER_VER);
+    canvas_invert_color(canvas);
+
+    // Menu
+    canvas_set_color(canvas, ColorBlack);
+    canvas_set_font(canvas, FontSecondary);
+    const uint8_t item_height = 16;
+    const uint8_t string_height_offset = 9;
 
 #ifdef FURI_DEBUG
-        //FURI_LOG_D(TAG, "window_position: %d, index: %d", model->window_position, model->index);
+    //FURI_LOG_D(TAG, "window_position: %d, index: %d", model->window_position, model->index);
 #endif
-        for(uint8_t position = 0; position < SubBruteAttackTotalCount; ++position) {
-            uint8_t item_position = position - model->window_position;
-
-            if(item_position < ITEMS_ON_SCREEN) {
-                if(model->index == position) {
-                    canvas_draw_str_aligned(
-                        canvas,
-                        3,
-                        9 + (item_position * item_height) + STATUS_BAR_Y_SHIFT,
-                        AlignLeft,
-                        AlignCenter,
-                        subbrute_protocol_name(position));
-
-                    if(model->extra_repeats > 0) {
+    for(size_t position = 0; position < SubBruteAttackTotalCount; ++position) {
+        uint8_t item_position = position - model->window_position;
+
+        if(item_position < ITEMS_ON_SCREEN) {
+            if(model->index == position) {
+                canvas_draw_str_aligned(
+                    canvas,
+                    3,
+                    string_height_offset + (item_position * item_height) + STATUS_BAR_Y_SHIFT,
+                    AlignLeft,
+                    AlignCenter,
+                    subbrute_protocol_name(position));
+
+                elements_frame(
+                    canvas, 1, 1 + (item_position * item_height) + STATUS_BAR_Y_SHIFT, 124, 15);
+            } else {
+                canvas_draw_str_aligned(
+                    canvas,
+                    4,
+                    string_height_offset + (item_position * item_height) + STATUS_BAR_Y_SHIFT,
+                    AlignLeft,
+                    AlignCenter,
+                    subbrute_protocol_name(position));
+            }
+
+            uint8_t current_repeat_count = model->repeat_values[position];
+            uint8_t min_repeat_count = subbrute_protocol_repeats_count(position);
+
+            if(current_repeat_count > min_repeat_count) {
 #ifdef FW_ORIGIN_Official
-                        canvas_set_font(canvas, FontSecondary);
+                canvas_set_font(canvas, FontSecondary);
 #else
-                        canvas_set_font(canvas, FontBatteryPercent);
+                canvas_set_font(canvas, FontBatteryPercent);
 #endif
-                        char buffer[10];
-                        snprintf(
-                            buffer,
-                            sizeof(buffer),
-                            "x%d",
-                            model->extra_repeats + subbrute_protocol_repeats_count(model->index));
-                        uint8_t temp_x_offset_repeats = 18;
-                        if(model->extra_repeats + subbrute_protocol_repeats_count(model->index) <
-                           10) {
-                            temp_x_offset_repeats = 15;
-                        }
-                        canvas_draw_str_aligned(
-                            canvas,
-                            screen_width - temp_x_offset_repeats,
-                            9 + (item_position * item_height) + STATUS_BAR_Y_SHIFT,
-                            AlignLeft,
-                            AlignCenter,
-                            buffer);
-                        canvas_set_font(canvas, FontSecondary);
-                    }
-
-                    elements_frame(
-                        canvas, 1, 1 + (item_position * item_height) + STATUS_BAR_Y_SHIFT, 124, 15);
-                } else {
-                    canvas_draw_str_aligned(
-                        canvas,
-                        4,
-                        9 + (item_position * item_height) + STATUS_BAR_Y_SHIFT,
-                        AlignLeft,
-                        AlignCenter,
-                        subbrute_protocol_name(position));
-                }
+                char buffer[10];
+                snprintf(buffer, sizeof(buffer), "x%d", current_repeat_count);
+                uint8_t temp_x_offset_repeats =
+                    current_repeat_count <= SUBBRUTE_PROTOCOL_MAX_REPEATS ? 15 : 18;
+
+                canvas_draw_str_aligned(
+                    canvas,
+                    screen_width - temp_x_offset_repeats,
+                    string_height_offset + (item_position * item_height) + STATUS_BAR_Y_SHIFT,
+                    AlignLeft,
+                    AlignCenter,
+                    buffer);
+                canvas_set_font(canvas, FontSecondary);
             }
         }
+    }
+
+    elements_scrollbar_pos(
+        canvas,
+        screen_width,
+        STATUS_BAR_Y_SHIFT + 2,
+        screen_height - STATUS_BAR_Y_SHIFT,
+        model->index,
+        SubBruteAttackTotalCount);
+}
+
+void subbrute_main_view_draw(Canvas* canvas, SubBruteMainViewModel* model) {
+    if(model->is_select_byte) {
+        subbrute_main_view_draw_is_byte_selected(canvas, model);
+    } else {
+        subbrute_main_view_draw_is_ordinary_selected(canvas, model);
+    }
+}
+
+bool subbrute_main_view_input_file_protocol(InputEvent* event, SubBruteMainView* instance) {
+    bool updated = false;
+    if(event->key == InputKeyLeft) {
+        if((instance->index > 0 && !instance->two_bytes) ||
+           (instance->two_bytes && instance->index > 1)) {
+            instance->index--;
+        }
+        updated = true;
+    } else if(event->key == InputKeyRight) {
+        if(instance->index < 7) {
+            instance->index++;
+        }
+        updated = true;
+    } else if(event->key == InputKeyUp) {
+        instance->two_bytes = !instance->two_bytes;
+        // Because index is changing
+        if(instance->two_bytes && instance->index < 7) {
+            instance->index++;
+        }
+        // instance->callback(
+        //     instance->two_bytes ? SubBruteCustomEventTypeChangeStepUp :
+        //                           SubBruteCustomEventTypeChangeStepDown,
+        //     instance->context);
+
+        updated = true;
+    } else if(event->key == InputKeyOk) {
+        instance->callback(SubBruteCustomEventTypeIndexSelected, instance->context);
+        updated = true;
+    }
+    return updated;
+}
+
+bool subbrute_main_view_input_ordinary_protocol(
+    InputEvent* event,
+    SubBruteMainView* instance,
+    bool is_short) {
+    const uint8_t min_value = 0;
+    const uint8_t correct_total = SubBruteAttackTotalCount - 1;
+    uint8_t index = instance->index;
+    uint8_t min_repeats = subbrute_protocol_repeats_count(index);
+    uint8_t max_repeats = min_repeats * 3;
+    uint8_t current_repeats = instance->repeat_values[index];
+
+    bool updated = false;
+    if(event->key == InputKeyUp && is_short) {
+        if(index == min_value) {
+            instance->index = correct_total;
+        } else {
+            instance->index = CLAMP(index - 1, correct_total, min_value);
+        }
+        //instance->repeat_values = 0;
+        updated = true;
+    } else if(event->key == InputKeyDown && is_short) {
+        if(index == correct_total) {
+            instance->index = min_value;
+        } else {
+            instance->index = CLAMP(index + 1, correct_total, min_value);
+        }
+        //instance->repeat_values = 0;
+        updated = true;
+    } else if(event->key == InputKeyLeft && is_short) {
+        instance->repeat_values[index] = CLAMP(current_repeats - 1, max_repeats, min_repeats);
+
+        updated = true;
+    } else if(event->key == InputKeyRight && is_short) {
+        instance->repeat_values[index] = CLAMP(current_repeats + 1, max_repeats, min_repeats);
+
+        updated = true;
+    } else if(event->key == InputKeyOk && is_short) {
+        if(index == SubBruteAttackLoadFile) {
+            instance->callback(SubBruteCustomEventTypeLoadFile, instance->context);
+        } else {
+            instance->callback(SubBruteCustomEventTypeMenuSelected, instance->context);
+        }
+        updated = true;
+    }
+
+    if(updated) {
+        instance->window_position = instance->index;
+        if(instance->window_position > 0) {
+            instance->window_position -= 1;
+        }
 
-        elements_scrollbar_pos(
-            canvas,
-            screen_width,
-            STATUS_BAR_Y_SHIFT + 2,
-            screen_height - STATUS_BAR_Y_SHIFT,
-            model->index,
-            SubBruteAttackTotalCount);
+        if(SubBruteAttackTotalCount <= ITEMS_ON_SCREEN) {
+            instance->window_position = 0;
+        } else {
+            if(instance->window_position >= (SubBruteAttackTotalCount - ITEMS_ON_SCREEN)) {
+                instance->window_position = (SubBruteAttackTotalCount - ITEMS_ON_SCREEN);
+            }
+        }
     }
+
+    return updated;
 }
 
 bool subbrute_main_view_input(InputEvent* event, void* context) {
     furi_assert(event);
     furi_assert(context);
 
+    SubBruteMainView* instance = context;
+
     if(event->key == InputKeyBack && event->type == InputTypeShort) {
 #ifdef FURI_DEBUG
         FURI_LOG_I(TAG, "InputKey: BACK");
 #endif
+        instance->callback(SubBruteCustomEventTypeBackPressed, instance->context);
         return false;
     }
 
-    SubBruteMainView* instance = context;
 #ifdef FURI_DEBUG
-    FURI_LOG_D(TAG, "InputKey: %d, extra_repeats: %d", event->key, instance->extra_repeats);
+    FURI_LOG_D(
+        TAG,
+        "InputKey: %d, extra_repeats: %d",
+        event->key,
+        instance->repeat_values[instance->index]);
 #endif
-    const uint8_t min_value = 0;
-    const uint8_t correct_total = SubBruteAttackTotalCount - 1;
-    uint8_t max_repeats = 14 - subbrute_protocol_repeats_count(instance->index);
 
     bool updated = false;
     bool is_short = (event->type == InputTypeShort) || (event->type == InputTypeRepeat);
 
-    if(!instance->is_select_byte) {
-        if(event->key == InputKeyUp && is_short) {
-            if(instance->index == min_value) {
-                instance->index = correct_total;
-            } else {
-                instance->index = CLAMP(instance->index - 1, correct_total, min_value);
-            }
-            instance->extra_repeats = 0;
-            updated = true;
-        } else if(event->key == InputKeyDown && is_short) {
-            if(instance->index == correct_total) {
-                instance->index = min_value;
-            } else {
-                instance->index = CLAMP(instance->index + 1, correct_total, min_value);
-            }
-            instance->extra_repeats = 0;
-            updated = true;
-        } else if(event->key == InputKeyLeft && is_short) {
-            instance->extra_repeats = CLAMP(instance->extra_repeats - 1, max_repeats, 0);
-
-            updated = true;
-        } else if(event->key == InputKeyRight && is_short) {
-            instance->extra_repeats = CLAMP(instance->extra_repeats + 1, max_repeats, 0);
-
-            updated = true;
-        } else if(event->key == InputKeyOk && is_short) {
-            if(instance->index == SubBruteAttackLoadFile) {
-                instance->callback(SubBruteCustomEventTypeLoadFile, instance->context);
-            } else {
-                instance->callback(SubBruteCustomEventTypeMenuSelected, instance->context);
-            }
-            updated = true;
-        }
-        if(updated) {
-            instance->window_position = instance->index;
-            if(instance->window_position > 0) {
-                instance->window_position -= 1;
-            }
-
-            if(SubBruteAttackTotalCount <= ITEMS_ON_SCREEN) {
-                instance->window_position = 0;
-            } else {
-                if(instance->window_position >= (SubBruteAttackTotalCount - ITEMS_ON_SCREEN)) {
-                    instance->window_position = (SubBruteAttackTotalCount - ITEMS_ON_SCREEN);
-                }
-            }
-        }
-    } else if(is_short) {
-        if(event->key == InputKeyLeft) {
-            if((instance->index > 0 && !instance->two_bytes) ||
-               (instance->two_bytes && instance->index > 1)) {
-                instance->index--;
-            }
-            updated = true;
-        } else if(event->key == InputKeyRight) {
-            if(instance->index < 7) {
-                instance->index++;
-            }
-            updated = true;
-        } else if(event->key == InputKeyUp) {
-            instance->two_bytes = !instance->two_bytes;
-            // Because index is changing
-            if(instance->two_bytes && instance->index < 7) {
-                instance->index++;
-            }
-            // instance->callback(
-            //     instance->two_bytes ? SubBruteCustomEventTypeChangeStepUp :
-            //                           SubBruteCustomEventTypeChangeStepDown,
-            //     instance->context);
-
-            updated = true;
-        } else if(event->key == InputKeyOk) {
-            instance->callback(SubBruteCustomEventTypeIndexSelected, instance->context);
-            updated = true;
+    if(instance->is_select_byte) {
+        if(is_short) {
+            updated = subbrute_main_view_input_file_protocol(event, instance);
         }
+    } else {
+        updated = subbrute_main_view_input_ordinary_protocol(event, instance, is_short);
     }
 
     if(updated) {
@@ -331,7 +366,7 @@ bool subbrute_main_view_input(InputEvent* event, void* context) {
                 model->key_from_file = instance->key_from_file;
                 model->is_select_byte = instance->is_select_byte;
                 model->two_bytes = instance->two_bytes;
-                model->extra_repeats = instance->extra_repeats;
+                model->repeat_values[model->index] = instance->repeat_values[instance->index];
             },
             true);
     }
@@ -362,7 +397,7 @@ SubBruteMainView* subbrute_main_view_alloc() {
     instance->key_from_file = 0;
     instance->is_select_byte = false;
     instance->two_bytes = false;
-    instance->extra_repeats = 0;
+
     with_view_model(
         instance->view,
         SubBruteMainViewModel * model,
@@ -372,7 +407,6 @@ SubBruteMainView* subbrute_main_view_alloc() {
             model->key_from_file = instance->key_from_file;
             model->is_select_byte = instance->is_select_byte;
             model->two_bytes = instance->two_bytes;
-            model->extra_repeats = instance->extra_repeats;
         },
         true);
 
@@ -394,6 +428,7 @@ View* subbrute_main_view_get_view(SubBruteMainView* instance) {
 void subbrute_main_view_set_index(
     SubBruteMainView* instance,
     uint8_t idx,
+    const uint8_t* repeats,
     bool is_select_byte,
     bool two_bytes,
     uint64_t key_from_file) {
@@ -402,6 +437,9 @@ void subbrute_main_view_set_index(
 #ifdef FURI_DEBUG
     FURI_LOG_I(TAG, "Set index: %d, is_select_byte: %d", idx, is_select_byte);
 #endif
+    for(size_t i = 0; i < SubBruteAttackTotalCount; i++) {
+        instance->repeat_values[i] = repeats[i];
+    }
     instance->is_select_byte = is_select_byte;
     instance->two_bytes = two_bytes;
     instance->key_from_file = key_from_file;
@@ -413,12 +451,8 @@ void subbrute_main_view_set_index(
             instance->window_position -= 1;
         }
 
-        if(SubBruteAttackTotalCount <= ITEMS_ON_SCREEN) {
-            instance->window_position = 0;
-        } else {
-            if(instance->window_position >= (SubBruteAttackTotalCount - ITEMS_ON_SCREEN)) {
-                instance->window_position = (SubBruteAttackTotalCount - ITEMS_ON_SCREEN);
-            }
+        if(instance->window_position >= (SubBruteAttackTotalCount - ITEMS_ON_SCREEN)) {
+            instance->window_position = (SubBruteAttackTotalCount - ITEMS_ON_SCREEN);
         }
     }
 
@@ -431,7 +465,9 @@ void subbrute_main_view_set_index(
             model->key_from_file = instance->key_from_file;
             model->is_select_byte = instance->is_select_byte;
             model->two_bytes = instance->two_bytes;
-            model->extra_repeats = instance->extra_repeats;
+            for(size_t i = 0; i < SubBruteAttackTotalCount; i++) {
+                model->repeat_values[i] = repeats[i];
+            }
         },
         true);
 }
@@ -441,9 +477,9 @@ SubBruteAttacks subbrute_main_view_get_index(SubBruteMainView* instance) {
     return instance->index;
 }
 
-uint8_t subbrute_main_view_get_extra_repeats(SubBruteMainView* instance) {
+const uint8_t* subbrute_main_view_get_extra_repeats(SubBruteMainView* instance) {
     furi_assert(instance);
-    return instance->extra_repeats;
+    return instance->repeat_values;
 }
 
 bool subbrute_main_view_get_two_bytes(SubBruteMainView* instance) {

+ 4 - 3
views/subbrute_main_view.h

@@ -20,13 +20,14 @@ View* subbrute_main_view_get_view(SubBruteMainView* instance);
 void subbrute_main_view_set_index(
     SubBruteMainView* instance,
     uint8_t idx,
+    const uint8_t* repeats,
     bool is_select_byte,
     bool two_bytes,
-    uint64_t file_key);
+    uint64_t key_from_file);
 SubBruteAttacks subbrute_main_view_get_index(SubBruteMainView* instance);
-uint8_t subbrute_main_view_get_extra_repeats(SubBruteMainView* instance);
+const uint8_t* subbrute_main_view_get_extra_repeats(SubBruteMainView* instance);
 bool subbrute_main_view_get_two_bytes(SubBruteMainView* instance);
 void subbrute_attack_view_enter(void* context);
 void subbrute_attack_view_exit(void* context);
 bool subbrute_attack_view_input(InputEvent* event, void* context);
-void subbrute_attack_view_draw(Canvas* canvas, void* context);
+void subbrute_attack_view_draw(Canvas* canvas, void* context);