Просмотр исходного кода

Merge pull request #3 from derskythe/feat/existing-file-two-bytes

Feat/existing file two bytes
Der Skythe 3 лет назад
Родитель
Сommit
63f1bd5425

+ 1 - 1
application.fam

@@ -7,7 +7,7 @@ App(
     requires=["gui","dialogs"],
     stack_size=2 * 1024,
     order=11,
-    fap_icon="subbrute_10px.png",
+    fap_icon="images/subbrute_10px.png",
     fap_category="Tools",
     fap_icon_assets="images",
 )

+ 59 - 0
helpers/gui_top_buttons.c

@@ -0,0 +1,59 @@
+#include "gui_top_buttons.h"
+
+void elements_button_top_left(Canvas* canvas, const char* str) {
+    const Icon* icon = &I_ButtonUp_7x4;
+
+    const uint8_t button_height = 12;
+    const uint8_t vertical_offset = 3;
+    const uint8_t horizontal_offset = 3;
+    const uint8_t string_width = canvas_string_width(canvas, str);
+    const uint8_t icon_h_offset = 3;
+    const uint8_t icon_width_with_offset = icon_get_width(icon) + icon_h_offset;
+    const uint8_t icon_v_offset = icon_get_height(icon) + vertical_offset;
+    const uint8_t button_width = string_width + horizontal_offset * 2 + icon_width_with_offset;
+
+    const uint8_t x = 0;
+    const uint8_t y = 0 + button_height;
+
+    uint8_t line_x = x + button_width;
+    uint8_t line_y = y - button_height;
+    canvas_draw_box(canvas, x, line_y, button_width, button_height);
+    canvas_draw_line(canvas, line_x + 0, line_y, line_x + 0, y - 1);
+    canvas_draw_line(canvas, line_x + 1, line_y, line_x + 1, y - 2);
+    canvas_draw_line(canvas, line_x + 2, line_y, line_x + 2, y - 3);
+
+    canvas_invert_color(canvas);
+    canvas_draw_icon(canvas, x + horizontal_offset, y - icon_v_offset, icon);
+    canvas_draw_str(
+        canvas, x + horizontal_offset + icon_width_with_offset, y - vertical_offset, str);
+    canvas_invert_color(canvas);
+}
+
+void elements_button_top_right(Canvas* canvas, const char* str) {
+    const Icon* icon = &I_ButtonDown_7x4;
+
+    const uint8_t button_height = 12;
+    const uint8_t vertical_offset = 3;
+    const uint8_t horizontal_offset = 3;
+    const uint8_t string_width = canvas_string_width(canvas, str);
+    const uint8_t icon_h_offset = 3;
+    const uint8_t icon_width_with_offset = icon_get_width(icon) + icon_h_offset;
+    const uint8_t icon_v_offset = icon_get_height(icon) + vertical_offset + 1;
+    const uint8_t button_width = string_width + horizontal_offset * 2 + icon_width_with_offset;
+
+    const uint8_t x = canvas_width(canvas);
+    const uint8_t y = 0 + button_height;
+
+    uint8_t line_x = x - button_width;
+    uint8_t line_y = y - button_height;
+    canvas_draw_box(canvas, line_x, line_y, button_width, button_height);
+    canvas_draw_line(canvas, line_x - 1, line_y, line_x - 1, y - 1);
+    canvas_draw_line(canvas, line_x - 2, line_y, line_x - 2, y - 2);
+    canvas_draw_line(canvas, line_x - 3, line_y, line_x - 3, y - 3);
+
+    canvas_invert_color(canvas);
+    canvas_draw_str(canvas, x - button_width + horizontal_offset, y - vertical_offset, str);
+    canvas_draw_icon(
+        canvas, x - horizontal_offset - icon_get_width(icon), y - icon_v_offset, icon);
+    canvas_invert_color(canvas);
+}

+ 21 - 0
helpers/gui_top_buttons.h

@@ -0,0 +1,21 @@
+#pragma once
+
+#include <input/input.h>
+#include <gui/elements.h>
+#include <gui/icon.h>
+#include <gui/icon_animation.h>
+#include <assets_icons.h>
+
+/**
+ * Thanks to the author of metronome
+ * @param canvas
+ * @param str
+ */
+void elements_button_top_left(Canvas* canvas, const char* str);
+
+/**
+ * Thanks to the author of metronome
+ * @param canvas
+ * @param str
+ */
+void elements_button_top_right(Canvas* canvas, const char* str);

+ 19 - 9
helpers/subbrute_worker.c

@@ -96,8 +96,11 @@ bool subbrute_worker_init_default_attack(
     instance->te = protocol->te;
     instance->repeat = protocol->repeat + extra_repeats;
     instance->load_index = 0;
-    instance->file_key = NULL;
-    instance->max_value = subbrute_protocol_calc_max_value(instance->attack, instance->bits);
+    instance->file_key = 0;
+    instance->two_bytes = false;
+
+    instance->max_value =
+        subbrute_protocol_calc_max_value(instance->attack, instance->bits, instance->two_bytes);
 
     instance->initiated = true;
     instance->state = SubBruteWorkerStateReady;
@@ -122,9 +125,10 @@ bool subbrute_worker_init_file_attack(
     SubBruteWorker* instance,
     uint64_t step,
     uint8_t load_index,
-    const char* file_key,
+    uint64_t file_key,
     SubBruteProtocol* protocol,
-    uint8_t extra_repeats) {
+    uint8_t extra_repeats,
+    bool two_bytes) {
     furi_assert(instance);
 
     if(instance->worker_running) {
@@ -142,7 +146,10 @@ bool subbrute_worker_init_file_attack(
     instance->load_index = load_index;
     instance->repeat = protocol->repeat + extra_repeats;
     instance->file_key = file_key;
-    instance->max_value = subbrute_protocol_calc_max_value(instance->attack, instance->bits);
+    instance->two_bytes = two_bytes;
+
+    instance->max_value =
+        subbrute_protocol_calc_max_value(instance->attack, instance->bits, instance->two_bytes);
 
     instance->initiated = true;
     instance->state = SubBruteWorkerStateReady;
@@ -150,14 +157,15 @@ bool subbrute_worker_init_file_attack(
 #ifdef FURI_DEBUG
     FURI_LOG_I(
         TAG,
-        "subbrute_worker_init_file_attack: %s, bits: %d, preset: %s, file: %s, te: %d, repeat: %d, max_value: %lld",
+        "subbrute_worker_init_file_attack: %s, bits: %d, preset: %s, file: %s, te: %d, repeat: %d, max_value: %lld, key: %llX",
         subbrute_protocol_name(instance->attack),
         instance->bits,
         subbrute_protocol_preset(instance->preset),
         subbrute_protocol_file(instance->file),
         instance->te,
         instance->repeat,
-        instance->max_value);
+        instance->max_value,
+        instance->file_key);
 #endif
 
     return true;
@@ -244,7 +252,8 @@ bool subbrute_worker_transmit_current_key(SubBruteWorker* instance, uint64_t ste
             instance->te,
             instance->repeat,
             instance->load_index,
-            instance->file_key);
+            instance->file_key,
+            instance->two_bytes);
     } else {
         subbrute_protocol_default_payload(
             stream, step, instance->bits, instance->te, instance->repeat);
@@ -373,7 +382,8 @@ int32_t subbrute_worker_thread(void* context) {
                 instance->te,
                 instance->repeat,
                 instance->load_index,
-                instance->file_key);
+                instance->file_key,
+                instance->two_bytes);
         } else {
             subbrute_protocol_default_payload(
                 stream, instance->step, instance->bits, instance->te, instance->repeat);

+ 3 - 2
helpers/subbrute_worker.h

@@ -28,9 +28,10 @@ bool subbrute_worker_init_file_attack(
     SubBruteWorker* instance,
     uint64_t step,
     uint8_t load_index,
-    const char* file_key,
+    uint64_t file_key,
     SubBruteProtocol* protocol,
-    uint8_t extra_repeats);
+    uint8_t extra_repeats,
+    bool two_bytes);
 bool subbrute_worker_start(SubBruteWorker* instance);
 void subbrute_worker_stop(SubBruteWorker* instance);
 bool subbrute_worker_transmit_current_key(SubBruteWorker* instance, uint64_t step);

+ 2 - 1
helpers/subbrute_worker_private.h

@@ -31,8 +31,9 @@ struct SubBruteWorker {
     uint8_t te;
     uint8_t repeat;
     uint8_t load_index; // Index of group to bruteforce in loaded file
-    const char* file_key;
+    uint64_t file_key;
     uint64_t max_value; // Max step
+    bool two_bytes;
 
     // Manual transmit
     uint32_t last_time_tx_data;

+ 0 - 0
subbrute_10px.png → images/subbrute_10px.png


+ 11 - 11
scenes/subbrute_scene_load_file.c

@@ -3,13 +3,6 @@
 
 #define TAG "SubBruteSceneLoadFile"
 
-//void subbrute_scene_load_file_callback(SubBruteCustomEvent event, void* context) {
-////    furi_assert(context);
-////
-////    SubBruteState* instance = (SubBruteState*)context;
-////    view_dispatcher_send_custom_event(instance->view_dispatcher, event);
-//}
-
 void subbrute_scene_load_file_on_enter(void* context) {
     furi_assert(context);
     SubBruteState* instance = (SubBruteState*)context;
@@ -24,8 +17,14 @@ void subbrute_scene_load_file_on_enter(void* context) {
     dialog_file_browser_set_basic_options(&browser_options, SUBBRUTE_FILE_EXT, &I_sub1_10px);
 
     SubBruteFileResult load_result = SubBruteFileResultUnknown;
+    // TODO: DELETE IT
+#ifdef SUBBRUTE_FAST_TRACK
+    bool res = true;
+    furi_string_printf(load_path, "%s", "/ext/subghz/princeton.sub");
+#else
     bool res =
         dialog_file_browser_show(instance->dialogs, load_path, app_folder, &browser_options);
+#endif
 #ifdef FURI_DEBUG
     FURI_LOG_D(
         TAG,
@@ -44,11 +43,12 @@ void subbrute_scene_load_file_on_enter(void* context) {
             if(load_result == SubBruteFileResultOk) {
                 if(!subbrute_worker_init_file_attack(
                        instance->worker,
-                       instance->device->key_index,
-                       instance->device->load_index,
-                       instance->device->file_key,
+                       instance->device->current_step,
+                       instance->device->bit_index,
+                       instance->device->key_from_file,
                        instance->device->file_protocol_info,
-                       extra_repeats)) {
+                       extra_repeats,
+                       instance->device->two_bytes)) {
                     furi_crash("Invalid attack set!");
                 }
                 // Ready to run!

+ 24 - 7
scenes/subbrute_scene_load_select.c

@@ -20,7 +20,8 @@ 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->file_key);
+    subbrute_main_view_set_index(
+        view, 7, true, instance->device->two_bytes, instance->device->key_from_file);
 
     view_dispatcher_switch_to_view(instance->view_dispatcher, instance->current_view);
 }
@@ -38,21 +39,37 @@ bool subbrute_scene_load_select_on_event(void* context, SceneManagerEvent event)
 
     if(event.type == SceneManagerEventTypeCustom) {
         if(event.event == SubBruteCustomEventTypeIndexSelected) {
-            instance->device->load_index = subbrute_main_view_get_index(instance->view_main);
+            /*#ifdef FURI_DEBUG && !SUBBRUTE_FAST_TRACK
+            view_dispatcher_stop(instance->view_dispatcher);
+            consumed = true;
+#else*/
+            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->device->max_value = subbrute_protocol_calc_max_value(
+                instance->device->attack,
+                instance->device->bit_index,
+                instance->device->two_bytes);
 
             if(!subbrute_worker_init_file_attack(
                    instance->worker,
-                   instance->device->key_index,
-                   instance->device->load_index,
-                   instance->device->file_key,
+                   instance->device->current_step,
+                   instance->device->bit_index,
+                   instance->device->key_from_file,
                    instance->device->file_protocol_info,
-                   extra_repeats)) {
+                   extra_repeats,
+                   instance->device->two_bytes)) {
                 furi_crash("Invalid attack set!");
             }
             scene_manager_next_scene(instance->scene_manager, SubBruteSceneSetupAttack);
+            /*#endif*/
             consumed = true;
-        }
+        } /* else if(event.event == SubBruteCustomEventTypeChangeStepUp) {
+            instance->device->two_bytes = true;
+        } else if(event.event == SubBruteCustomEventTypeChangeStepDown) {
+            instance->device->two_bytes = false;
+        }*/
     } else if(event.type == SceneManagerEventTypeBack) {
         if(!scene_manager_search_and_switch_to_previous_scene(
                instance->scene_manager, SubBruteSceneStart)) {

+ 4 - 4
scenes/subbrute_scene_run_attack.c

@@ -45,7 +45,7 @@ void subbrute_scene_run_attack_on_enter(void* context) {
         instance->worker, subbrute_scene_run_attack_device_state_changed, instance);
 
     if(!subbrute_worker_is_running(instance->worker)) {
-        subbrute_worker_set_step(instance->worker, instance->device->key_index);
+        subbrute_worker_set_step(instance->worker, instance->device->current_step);
         if(!subbrute_worker_start(instance->worker)) {
             view_dispatcher_send_custom_event(
                 instance->view_dispatcher, SubBruteCustomEventTypeError);
@@ -64,7 +64,7 @@ bool subbrute_scene_run_attack_on_event(void* context, SceneManagerEvent event)
 
     if(event.type == SceneManagerEventTypeCustom) {
         uint64_t step = subbrute_worker_get_step(instance->worker);
-        instance->device->key_index = step;
+        instance->device->current_step = step;
         subbrute_attack_view_set_current_step(view, step);
 
         if(event.event == SubBruteCustomEventTypeTransmitFinished) {
@@ -89,12 +89,12 @@ bool subbrute_scene_run_attack_on_event(void* context, SceneManagerEvent event)
             scene_manager_search_and_switch_to_previous_scene(
                 instance->scene_manager, SubBruteSceneSetupAttack);
         } else if(event.event == SubBruteCustomEventTypeUpdateView) {
-            //subbrute_attack_view_set_current_step(view, instance->device->key_index);
+            //subbrute_attack_view_set_current_step(view, instance->device->current_step);
         }
         consumed = true;
     } else if(event.type == SceneManagerEventTypeTick) {
         uint64_t step = subbrute_worker_get_step(instance->worker);
-        instance->device->key_index = step;
+        instance->device->current_step = step;
         subbrute_attack_view_set_current_step(view, step);
 
         consumed = true;

+ 7 - 7
scenes/subbrute_scene_setup_attack.c

@@ -37,14 +37,14 @@ void subbrute_scene_setup_attack_on_enter(void* context) {
         instance->worker, subbrute_scene_setup_attack_device_state_changed, context);
     if(subbrute_worker_is_running(instance->worker)) {
         subbrute_worker_stop(instance->worker);
-        instance->device->key_index = subbrute_worker_get_step(instance->worker);
+        instance->device->current_step = subbrute_worker_get_step(instance->worker);
     }
 
     subbrute_attack_view_init_values(
         view,
         instance->device->attack,
         instance->device->max_value,
-        instance->device->key_index,
+        instance->device->current_step,
         false,
         instance->device->extra_repeats);
 
@@ -77,7 +77,7 @@ bool subbrute_scene_setup_attack_on_event(void* context, SceneManagerEvent event
                 view,
                 instance->device->attack,
                 instance->device->max_value,
-                instance->device->key_index,
+                instance->device->current_step,
                 false,
                 instance->device->extra_repeats);
             scene_manager_next_scene(instance->scene_manager, SubBruteSceneSaveName);
@@ -86,7 +86,7 @@ bool subbrute_scene_setup_attack_on_event(void* context, SceneManagerEvent event
                 view,
                 instance->device->attack,
                 instance->device->max_value,
-                instance->device->key_index,
+                instance->device->current_step,
                 false,
                 instance->device->extra_repeats);
             scene_manager_next_scene(instance->scene_manager, SubBruteSceneStart);
@@ -99,7 +99,7 @@ bool subbrute_scene_setup_attack_on_event(void* context, SceneManagerEvent event
                 // Blink
                 notification_message(instance->notifications, &sequence_blink_green_100);
                 subbrute_worker_transmit_current_key(
-                    instance->worker, instance->device->key_index);
+                    instance->worker, instance->device->current_step);
                 // Stop
                 notification_message(instance->notifications, &sequence_blink_stop);
             }
@@ -128,9 +128,9 @@ bool subbrute_scene_setup_attack_on_event(void* context, SceneManagerEvent event
         consumed = true;
     } else if(event.type == SceneManagerEventTypeTick) {
         if(subbrute_worker_is_running(instance->worker)) {
-            instance->device->key_index = subbrute_worker_get_step(instance->worker);
+            instance->device->current_step = subbrute_worker_get_step(instance->worker);
         }
-        subbrute_attack_view_set_current_step(view, instance->device->key_index);
+        subbrute_attack_view_set_current_step(view, instance->device->current_step);
         consumed = true;
     }
 

+ 7 - 2
scenes/subbrute_scene_start.c

@@ -23,9 +23,14 @@ void subbrute_scene_start_on_enter(void* context) {
 
     instance->current_view = SubBruteViewMain;
     subbrute_main_view_set_callback(view, subbrute_scene_start_callback, instance);
-    subbrute_main_view_set_index(view, instance->device->attack, false, NULL);
+    subbrute_main_view_set_index(view, instance->device->attack, false, instance->device->two_bytes, 0);
 
     view_dispatcher_switch_to_view(instance->view_dispatcher, instance->current_view);
+
+    // TODO: DELETE IT
+#ifdef SUBBRUTE_FAST_TRACK
+    scene_manager_next_scene(instance->scene_manager, SubBruteSceneLoadFile);
+#endif
 }
 
 void subbrute_scene_start_on_exit(void* context) {
@@ -57,7 +62,7 @@ bool subbrute_scene_start_on_event(void* context, SceneManagerEvent event) {
                (!subbrute_worker_init_default_attack(
                    instance->worker,
                    attack,
-                   instance->device->key_index,
+                   instance->device->current_step,
                    instance->device->protocol_info,
                    instance->device->extra_repeats))) {
                 furi_crash("Invalid attack set!");

+ 149 - 60
subbrute_device.c

@@ -12,7 +12,7 @@
 SubBruteDevice* subbrute_device_alloc() {
     SubBruteDevice* instance = malloc(sizeof(SubBruteDevice));
 
-    instance->key_index = 0;
+    instance->current_step = 0;
 
     instance->protocol_info = NULL;
     instance->file_protocol_info = NULL;
@@ -23,7 +23,7 @@ SubBruteDevice* subbrute_device_alloc() {
         instance->environment, (void*)&subghz_protocol_registry);
 
 #ifdef FURI_DEBUG
-    subbrute_device_attack_set_default_values(instance, SubBruteAttackCAME12bit433);
+    subbrute_device_attack_set_default_values(instance, SubBruteAttackLoadFile);
 #else
     subbrute_device_attack_set_default_values(instance, SubBruteAttackCAME12bit433);
 #endif
@@ -51,32 +51,32 @@ void subbrute_device_free(SubBruteDevice* instance) {
 
 uint64_t subbrute_device_add_step(SubBruteDevice* instance, int8_t step) {
     if(step > 0) {
-        if((instance->key_index + step) - instance->max_value == 1) {
-            instance->key_index = 0x00;
+        if((instance->current_step + step) - instance->max_value == 1) {
+            instance->current_step = 0x00;
         } else {
-            uint64_t value = instance->key_index + step;
+            uint64_t value = instance->current_step + step;
             if(value == instance->max_value) {
-                instance->key_index = value;
+                instance->current_step = value;
             } else {
-                instance->key_index = value % instance->max_value;
+                instance->current_step = value % instance->max_value;
             }
         }
     } else {
-        if(instance->key_index + step == 0) {
-            instance->key_index = 0x00;
-        } else if(instance->key_index == 0) {
-            instance->key_index = instance->max_value;
+        if(instance->current_step + step == 0) {
+            instance->current_step = 0x00;
+        } else if(instance->current_step == 0) {
+            instance->current_step = instance->max_value;
         } else {
-            uint64_t value = ((instance->key_index + step) + instance->max_value);
+            uint64_t value = ((instance->current_step + step) + instance->max_value);
             if(value == instance->max_value) {
-                instance->key_index = value;
+                instance->current_step = value;
             } else {
-                instance->key_index = value % instance->max_value;
+                instance->current_step = value % instance->max_value;
             }
         }
     }
 
-    return instance->key_index;
+    return instance->current_step;
 }
 
 bool subbrute_device_save_file(SubBruteDevice* instance, const char* dev_file_name) {
@@ -101,19 +101,20 @@ bool subbrute_device_save_file(SubBruteDevice* instance, const char* dev_file_na
                 instance->file_protocol_info->frequency,
                 instance->file_protocol_info->preset,
                 instance->file_protocol_info->file,
-                instance->key_index,
+                instance->current_step,
                 instance->file_protocol_info->bits,
                 instance->file_protocol_info->te,
                 instance->file_protocol_info->repeat,
-                instance->load_index,
-                instance->file_key);
+                instance->bit_index,
+                instance->key_from_file,
+                instance->two_bytes);
         } else {
             subbrute_protocol_default_generate_file(
                 stream,
                 instance->protocol_info->frequency,
                 instance->protocol_info->preset,
                 instance->protocol_info->file,
-                instance->key_index,
+                instance->current_step,
                 instance->protocol_info->bits,
                 instance->protocol_info->te,
                 instance->protocol_info->repeat);
@@ -174,8 +175,8 @@ SubBruteFileResult subbrute_device_attack_set(
             protocol_check_result = SubBruteFileResultOk;
 
             // Calc max value
-            instance->max_value =
-                subbrute_protocol_calc_max_value(instance->attack, instance->protocol_info->bits);
+            instance->max_value = subbrute_protocol_calc_max_value(
+                instance->attack, instance->protocol_info->bits, instance->two_bytes);
         }
 #ifdef FURI_DEBUG
         bits = instance->protocol_info->bits;
@@ -189,8 +190,8 @@ SubBruteFileResult subbrute_device_attack_set(
         protocol_check_result = SubBruteFileResultOk;
 
         // Calc max value
-        instance->max_value =
-            subbrute_protocol_calc_max_value(instance->attack, instance->file_protocol_info->bits);
+        instance->max_value = subbrute_protocol_calc_max_value(
+            instance->attack, instance->file_protocol_info->bits, instance->two_bytes);
 #ifdef FURI_DEBUG
         bits = instance->file_protocol_info->bits;
         te = instance->file_protocol_info->te;
@@ -257,25 +258,24 @@ uint8_t subbrute_device_load_from_file(SubBruteDevice* instance, const char* fil
         }
 
         // Frequency
-        if(flipper_format_read_uint32(fff_data_file, "Frequency", &temp_data32, 1)) {
-            instance->file_protocol_info->frequency = temp_data32;
-            if(!furi_hal_subghz_is_tx_allowed(instance->file_protocol_info->frequency)) {
-                result = SubBruteFileResultFrequencyNotAllowed;
-                break;
-            }
-        } else {
+        if(!flipper_format_read_uint32(fff_data_file, "Frequency", &temp_data32, 1)) {
             FURI_LOG_E(TAG, "Missing or incorrect Frequency");
             result = SubBruteFileResultMissingOrIncorrectFrequency;
             break;
         }
+        instance->file_protocol_info->frequency = temp_data32;
+        if(!furi_hal_subghz_is_tx_allowed(instance->file_protocol_info->frequency)) {
+            result = SubBruteFileResultFrequencyNotAllowed;
+            break;
+        }
 
         // Preset
         if(!flipper_format_read_string(fff_data_file, "Preset", temp_str)) {
             FURI_LOG_E(TAG, "Preset FAIL");
             result = SubBruteFileResultPresetInvalid;
-        } else {
-            instance->file_protocol_info->preset = subbrute_protocol_convert_preset(temp_str);
+            break;
         }
+        instance->file_protocol_info->preset = subbrute_protocol_convert_preset(temp_str);
 
         const char* protocol_file = NULL;
         // Protocol
@@ -283,13 +283,12 @@ uint8_t subbrute_device_load_from_file(SubBruteDevice* instance, const char* fil
             FURI_LOG_E(TAG, "Missing Protocol");
             result = SubBruteFileResultMissingProtocol;
             break;
-        } else {
-            instance->file_protocol_info->file = subbrute_protocol_file_protocol_name(temp_str);
-            protocol_file = subbrute_protocol_file(instance->file_protocol_info->file);
+        }
+        instance->file_protocol_info->file = subbrute_protocol_file_protocol_name(temp_str);
+        protocol_file = subbrute_protocol_file(instance->file_protocol_info->file);
 #ifdef FURI_DEBUG
-            FURI_LOG_D(TAG, "Protocol: %s", protocol_file);
+        FURI_LOG_D(TAG, "Protocol: %s", protocol_file);
 #endif
-        }
 
         instance->decoder_result = subghz_receiver_search_decoder_base_by_name(
             instance->receiver, furi_string_get_cstr(temp_str));
@@ -307,9 +306,7 @@ uint8_t subbrute_device_load_from_file(SubBruteDevice* instance, const char* fil
             break;
         }
 #ifdef FURI_DEBUG
-        else {
-            FURI_LOG_D(TAG, "Decoder: %s", instance->decoder_result->protocol->name);
-        }
+        FURI_LOG_D(TAG, "Decoder: %s", instance->decoder_result->protocol->name);
 #endif
 
         // Bit
@@ -317,28 +314,120 @@ uint8_t subbrute_device_load_from_file(SubBruteDevice* instance, const char* fil
             FURI_LOG_E(TAG, "Missing or incorrect Bit");
             result = SubBruteFileResultMissingOrIncorrectBit;
             break;
-        } else {
-            instance->file_protocol_info->bits = temp_data32;
+        }
+        instance->file_protocol_info->bits = temp_data32;
 #ifdef FURI_DEBUG
-            FURI_LOG_D(TAG, "Bit: %d", instance->file_protocol_info->bits);
+        FURI_LOG_D(TAG, "Bit: %d", instance->file_protocol_info->bits);
 #endif
-        }
 
+        // TODO: Delete this
         // Key
-        if(!flipper_format_read_string(fff_data_file, "Key", temp_str)) {
-            FURI_LOG_E(TAG, "Missing or incorrect Key");
+        //         if(!flipper_format_read_string(fff_data_file, "Key", temp_str)) {
+        //             FURI_LOG_E(TAG, "Missing or incorrect Key");
+        //             result = SubBruteFileResultMissingOrIncorrectKey;
+        //             break;
+        //         } else {
+        //             snprintf(
+        //                 instance->current_key_from_file,
+        //                 sizeof(instance->current_key_from_file),
+        //                 "%s",
+        //                 furi_string_get_cstr(temp_str));
+        // #ifdef FURI_DEBUG
+        //             FURI_LOG_D(TAG, "Key: %s", instance->current_key_from_file);
+        // #endif
+        //         }
+        //
+        //         flipper_format_rewind(fff_data_file);
+
+        uint8_t key_data[sizeof(uint64_t)] = {0};
+        if(!flipper_format_read_hex(fff_data_file, "Key", key_data, sizeof(uint64_t))) {
+            FURI_LOG_E(TAG, "Missing Key");
             result = SubBruteFileResultMissingOrIncorrectKey;
             break;
-        } else {
-            snprintf(
-                instance->file_key,
-                sizeof(instance->file_key),
-                "%s",
-                furi_string_get_cstr(temp_str));
-#ifdef FURI_DEBUG
-            FURI_LOG_D(TAG, "Key: %s", instance->file_key);
-#endif
         }
+        uint64_t data = 0;
+        for(uint8_t i = 0; i < sizeof(uint64_t); i++) {
+            data = (data << 8) | key_data[i];
+        }
+#if FURI_DEBUG
+        FURI_LOG_D(TAG, "Key: %.16llX", data);
+#endif
+        instance->key_from_file = data;
+
+        //         uint16_t add_value = 0x0001;
+        //         uint8_t bit_index = 7;
+        //         bool two_bytes = true;
+        //         uint8_t p[8];
+        //         for(int i = 0; i < 8; i++) {
+        //             p[i] = (uint8_t)(instance->key_from_file >> 8 * (7 - i)) & 0xFF;
+        //         }
+        //         uint16_t num = two_bytes ? (p[bit_index - 1] << 8) | p[bit_index] : p[bit_index];
+        //         FURI_LOG_D(TAG, "num: 0x%04X", num);
+        //         num += add_value;
+        //         FURI_LOG_D(TAG, "num added: 0x%04X", num);
+        //         uint8_t low_byte = num & (0xff);
+        //         uint8_t high_byte = (num >> 8) & 0xff;
+
+        //         data = 0;
+        //         for(uint8_t i = 0; i < sizeof(uint64_t); i++) {
+        //             if(i == bit_index - 1 && two_bytes) {
+        //                 data = (data << 8) | high_byte;
+        //                 data = (data << 8) | low_byte;
+        //                 i++;
+        //             } else if(i == bit_index) {
+        //                 data = (data << 8) | low_byte;
+        //             } else {
+        //                 data = (data << 8) | p[i];
+        //             }
+        //         }
+        // #if FURI_DEBUG
+        //         furi_string_printf(temp_str, "Key: %lX", (uint32_t)(data & 0xFFFFFFFF));
+        //         FURI_LOG_D(
+        //             TAG, "H: 0x%02X, L: 0x%02X, %s", high_byte, low_byte, furi_string_get_cstr(temp_str));
+        // #endif
+        // uint8_t key_data[sizeof(uint64_t)] = {0};
+        // if(!flipper_format_read_hex(fff_data_file, "Key", key_data, sizeof(uint64_t))) {
+        //     FURI_LOG_E(TAG, "Missing Key");
+        //     result = SubBruteFileResultMissingOrIncorrectKey;
+        //     break;
+        // }
+        // uint64_t data = 0;
+        // for(uint8_t i = 0; i < sizeof(uint64_t); i++) {
+        //     data = (data << 8) | key_data[i];
+        // }
+        // instance->key_from_file = data;
+
+        // uint16_t add_value = 0x0001;
+        // uint8_t bit_index = 7;
+        // bool two_bytes = true;
+
+        // uint8_t p[8];
+        // for(int i = 0; i < 8; i++) {
+        //     p[i] = (uint8_t)(instance->key_from_file >> 8 * (7 - i)) & 0xFF;
+        // }
+        // uint16_t num = two_bytes ? (p[bit_index - 1] << 8) | p[bit_index] : p[bit_index];
+        // FURI_LOG_D(TAG, "num: 0x%04X", num);
+        // num += add_value;
+        // FURI_LOG_D(TAG, "num added: 0x%04X", num);
+        // uint8_t low_byte = num & (0xff);
+        // uint8_t high_byte = (num >> 8) & 0xff;
+
+        // data = 0;
+        // for(uint8_t i = 0; i < sizeof(uint64_t); i++) {
+        //     if(i == bit_index - 1 && two_bytes) {
+        //         data = (data << 8) | high_byte;
+        //         data = (data << 8) | low_byte;
+        //         i++;
+        //     } else if(i == bit_index) {
+        //         data = (data << 8) | low_byte;
+        //     } else {
+        //         data = (data << 8) | p[i];
+        //     }
+        // }
+
+        // furi_string_printf(temp_str, "Key: %lX", (uint32_t)(data & 0xFFFFFFFF));
+        // FURI_LOG_D(
+        //     TAG, "H: 0x%02X, L: 0x%02X, %s", high_byte, low_byte, furi_string_get_cstr(temp_str));
 
         // TE
         if(!flipper_format_read_uint32(fff_data_file, "TE", &temp_data32, 1)) {
@@ -394,15 +483,15 @@ void subbrute_device_attack_set_default_values(
     FURI_LOG_D(TAG, "subbrute_device_attack_set_default_values");
 #endif
     instance->attack = default_attack;
-    instance->key_index = 0x00;
-    instance->load_index = 0x00;
+    instance->current_step = 0x00;
+    instance->bit_index = 0x00;
     instance->extra_repeats = 0;
+    instance->two_bytes = false;
     memset(instance->current_key, 0, sizeof(instance->current_key));
 
     if(default_attack != SubBruteAttackLoadFile) {
-        memset(instance->file_key, 0, sizeof(instance->file_key));
-
-        instance->max_value = (uint64_t)0x00;
+        instance->max_value = subbrute_protocol_calc_max_value(
+            instance->attack, instance->bit_index, instance->two_bytes);
     }
 }
 

+ 6 - 4
subbrute_device.h

@@ -36,9 +36,7 @@ typedef struct {
     SubBruteProtocol* file_protocol_info;
 
     // Current step
-    uint64_t key_index;
-    // Index of group to bruteforce in loaded file
-    uint8_t load_index;
+    uint64_t current_step;
 
     // SubGhz
     SubGhzReceiver* receiver;
@@ -52,7 +50,11 @@ typedef struct {
 
     // Loaded info for attack type
     char current_key[SUBBRUTE_PAYLOAD_SIZE];
-    char file_key[SUBBRUTE_MAX_LEN_NAME];
+    uint64_t key_from_file;
+    uint64_t current_key_from_file;
+    bool two_bytes;
+    // Index of group to bruteforce in loaded file
+    uint8_t bit_index;
 } SubBruteDevice;
 
 SubBruteDevice* subbrute_device_alloc();

+ 5 - 1
subbrute_i.h

@@ -16,7 +16,7 @@
 #include <gui/modules/widget.h>
 #include <gui/modules/loading.h>
 
-#include <SubGHz_Bruteforcer_icons.h>
+#include "SubGHz_Bruteforcer_icons.h"
 
 #include <dialogs/dialogs.h>
 
@@ -29,6 +29,10 @@
 #include "views/subbrute_attack_view.h"
 #include "views/subbrute_main_view.h"
 
+#ifdef FURI_DEBUG
+//#define SUBBRUTE_FAST_TRACK false
+#endif
+
 typedef enum {
     SubBruteViewNone,
     SubBruteViewMain,

+ 92 - 47
subbrute_protocols.c

@@ -25,6 +25,17 @@ const SubBruteProtocol subbrute_protocol_came_12bit_307 = {
     .preset = FuriHalSubGhzPresetOok650Async,
     .file = CAMEFileProtocol};
 
+/**
+ * CAME 12bit 315MHz
+ */
+const SubBruteProtocol subbrute_protocol_came_12bit_315 = {
+    .frequency = 315000000,
+    .bits = 12,
+    .te = 0,
+    .repeat = 3,
+    .preset = FuriHalSubGhzPresetOok650Async,
+    .file = CAMEFileProtocol};
+
 /**
  * CAME 12bit 433MHz
  */
@@ -243,6 +254,7 @@ const SubBruteProtocol subbrute_protocol_load_file =
 static const char* subbrute_protocol_names[] = {
     [SubBruteAttackCAME12bit303] = "CAME 12bit 303MHz",
     [SubBruteAttackCAME12bit307] = "CAME 12bit 307MHz",
+    [SubBruteAttackCAME12bit315] = "CAME 12bit 315MHz",
     [SubBruteAttackCAME12bit433] = "CAME 12bit 433MHz",
     [SubBruteAttackCAME12bit868] = "CAME 12bit 868MHz",
     [SubBruteAttackNICE12bit433] = "NICE 12bit 433MHz",
@@ -279,6 +291,7 @@ static const char* subbrute_protocol_presets[] = {
 const SubBruteProtocol* subbrute_protocol_registry[] = {
     [SubBruteAttackCAME12bit303] = &subbrute_protocol_came_12bit_303,
     [SubBruteAttackCAME12bit307] = &subbrute_protocol_came_12bit_307,
+    [SubBruteAttackCAME12bit315] = &subbrute_protocol_came_12bit_315,
     [SubBruteAttackCAME12bit433] = &subbrute_protocol_came_12bit_433,
     [SubBruteAttackCAME12bit868] = &subbrute_protocol_came_12bit_868,
     [SubBruteAttackNICE12bit433] = &subbrute_protocol_nice_12bit_433,
@@ -366,27 +379,74 @@ SubBruteFileProtocol subbrute_protocol_file_protocol_name(FuriString* name) {
     return UnknownFileProtocol;
 }
 
-void subbrute_protocol_default_payload(
-    Stream* stream,
+void subbrute_protocol_create_candidate_for_existing_file(
+    FuriString* candidate,
     uint64_t step,
-    uint8_t bits,
-    uint8_t te,
-    uint8_t repeat) {
-    FuriString* candidate = furi_string_alloc_set_str("                       ");
+    uint8_t bit_index,
+    uint64_t file_key,
+    bool two_bytes) {
+    uint8_t p[8];
+    for(int i = 0; i < 8; i++) {
+        p[i] = (uint8_t)(file_key >> 8 * (7 - i)) & 0xFF;
+    }
+    uint8_t low_byte = step & (0xff);
+    uint8_t high_byte = (step >> 8) & 0xff;
+
+    size_t size = sizeof(uint64_t);
+    for(uint8_t i = 0; i < size; i++) {
+        if(i == bit_index - 1 && two_bytes) {
+            furi_string_cat_printf(candidate, "%02X %02X", high_byte, low_byte);
+            i++;
+        } else if(i == bit_index) {
+            furi_string_cat_printf(candidate, "%02X", low_byte);
+        } else if(p[i] != 0) {
+            furi_string_cat_printf(candidate, "%02X", p[i]);
+        } else {
+            furi_string_cat_printf(candidate, "%s", "00");
+        }
+
+        if(i < size - 1) {
+            furi_string_push_back(candidate, ' ');
+        }
+    }
+
+#ifdef FURI_DEBUG
+    FURI_LOG_D(TAG, "file candidate: %s, step: %lld", furi_string_get_cstr(candidate), step);
+#endif
+}
 
-    FuriString* buffer = furi_string_alloc_printf("%16llX", step);
-    int j = 0;
-    for(uint8_t i = 0; i < 16; i++) {
-        if(furi_string_get_char(buffer, i) != ' ') {
-            furi_string_set_char(candidate, i + j, furi_string_get_char(buffer, i));
+void subbrute_protocol_create_candidate_for_default(FuriString* candidate, uint64_t step) {
+    uint8_t p[8];
+    for(int i = 0; i < 8; i++) {
+        p[i] = (uint8_t)(step >> 8 * (7 - i)) & 0xFF;
+    }
+
+    size_t size = sizeof(uint64_t);
+    for(uint8_t i = 0; i < size; i++) {
+        if(p[i] != 0) {
+            furi_string_cat_printf(candidate, "%02X", p[i]);
         } else {
-            furi_string_set_char(candidate, i + j, '0');
+            furi_string_cat_printf(candidate, "%s", "00");
         }
-        if(i % 2 != 0) {
-            j++;
+
+        if(i < size - 1) {
+            furi_string_push_back(candidate, ' ');
         }
     }
-    furi_string_free(buffer);
+
+#ifdef FURI_DEBUG
+    FURI_LOG_D(TAG, "candidate: %s, step: %lld", furi_string_get_cstr(candidate), step);
+#endif
+}
+
+void subbrute_protocol_default_payload(
+    Stream* stream,
+    uint64_t step,
+    uint8_t bits,
+    uint8_t te,
+    uint8_t repeat) {
+    FuriString* candidate = furi_string_alloc();
+    subbrute_protocol_create_candidate_for_default(candidate, step);
 
 #ifdef FURI_DEBUG
     FURI_LOG_D(
@@ -420,13 +480,12 @@ void subbrute_protocol_file_payload(
     uint8_t bits,
     uint8_t te,
     uint8_t repeat,
-    uint8_t load_index,
-    const char* file_key) {
+    uint8_t bit_index,
+    uint64_t file_key,
+    bool two_bytes) {
     FuriString* candidate = furi_string_alloc();
-    char subbrute_payload_byte[4];
-    furi_string_set_str(candidate, file_key);
-    snprintf(subbrute_payload_byte, 4, "%02X ", (uint8_t)step);
-    furi_string_replace_at(candidate, load_index * 3, 3, subbrute_payload_byte);
+    subbrute_protocol_create_candidate_for_existing_file(
+        candidate, step, bit_index, file_key, two_bytes);
 
 #ifdef FURI_DEBUG
     FURI_LOG_D(
@@ -464,21 +523,8 @@ void subbrute_protocol_default_generate_file(
     uint8_t bits,
     uint8_t te,
     uint8_t repeat) {
-    FuriString* candidate = furi_string_alloc_set_str("                       ");
-
-    FuriString* buffer = furi_string_alloc_printf("%16llX", step);
-    int j = 0;
-    for(uint8_t i = 0; i < 16; i++) {
-        if(furi_string_get_char(buffer, i) != ' ') {
-            furi_string_set_char(candidate, i + j, furi_string_get_char(buffer, i));
-        } else {
-            furi_string_set_char(candidate, i + j, '0');
-        }
-        if(i % 2 != 0) {
-            j++;
-        }
-    }
-    furi_string_free(buffer);
+    FuriString* candidate = furi_string_alloc();
+    subbrute_protocol_create_candidate_for_default(candidate, step);
 
 #ifdef FURI_DEBUG
     FURI_LOG_D(TAG, "candidate: %s, step: %lld", furi_string_get_cstr(candidate), step);
@@ -520,17 +566,15 @@ void subbrute_protocol_file_generate_file(
     uint8_t bits,
     uint8_t te,
     uint8_t repeat,
-    uint8_t load_index,
-    const char* file_key) {
+    uint8_t bit_index,
+    uint64_t file_key,
+    bool two_bytes) {
     FuriString* candidate = furi_string_alloc();
-    char subbrute_payload_byte[4];
-    furi_string_set_str(candidate, file_key);
-    snprintf(subbrute_payload_byte, 4, "%02X ", (uint8_t)step);
-    furi_string_replace_at(candidate, load_index * 3, 3, subbrute_payload_byte);
+    // char subbrute_payload_byte[8];
+    //furi_string_set_str(candidate, file_key);
+    subbrute_protocol_create_candidate_for_existing_file(
+        candidate, step, bit_index, file_key, two_bytes);
 
-#ifdef FURI_DEBUG
-    FURI_LOG_D(TAG, "candidate: %s, step: %lld", furi_string_get_cstr(candidate), step);
-#endif
     stream_clean(stream);
     if(te) {
         stream_write_format(
@@ -558,10 +602,11 @@ void subbrute_protocol_file_generate_file(
     furi_string_free(candidate);
 }
 
-uint64_t subbrute_protocol_calc_max_value(SubBruteAttacks attack_type, uint8_t bits) {
+uint64_t
+    subbrute_protocol_calc_max_value(SubBruteAttacks attack_type, uint8_t bits, bool two_bytes) {
     uint64_t max_value;
     if(attack_type == SubBruteAttackLoadFile) {
-        max_value = 0xFF;
+        max_value = two_bytes ? 0xFFFF : 0xFF;
     } else {
         FuriString* max_value_s;
         max_value_s = furi_string_alloc();

+ 9 - 5
subbrute_protocols.h

@@ -26,6 +26,7 @@ typedef enum {
 typedef enum {
     SubBruteAttackCAME12bit303,
     SubBruteAttackCAME12bit307,
+    SubBruteAttackCAME12bit315,
     SubBruteAttackCAME12bit433,
     SubBruteAttackCAME12bit868,
     SubBruteAttackNICE12bit433,
@@ -78,8 +79,9 @@ void subbrute_protocol_file_payload(
     uint8_t bits,
     uint8_t te,
     uint8_t repeat,
-    uint8_t load_index,
-    const char* file_key);
+    uint8_t bit_index,
+    uint64_t file_key,
+    bool two_bytes);
 void subbrute_protocol_default_generate_file(
     Stream* stream,
     uint32_t frequency,
@@ -98,6 +100,8 @@ void subbrute_protocol_file_generate_file(
     uint8_t bits,
     uint8_t te,
     uint8_t repeat,
-    uint8_t load_index,
-    const char* file_key);
-uint64_t subbrute_protocol_calc_max_value(SubBruteAttacks attack_type, uint8_t bits);
+    uint8_t bit_index,
+    uint64_t file_key,
+    bool two_bytes);
+uint64_t
+    subbrute_protocol_calc_max_value(SubBruteAttacks attack_type, uint8_t bits, bool two_bytes);

+ 22 - 71
views/subbrute_attack_view.c

@@ -1,6 +1,7 @@
 #include "subbrute_attack_view.h"
 #include "../subbrute_i.h"
 #include "../subbrute_protocols.h"
+#include "../helpers/gui_top_buttons.h"
 
 #include <input/input.h>
 #include <gui/elements.h>
@@ -255,74 +256,6 @@ void subbrute_attack_view_exit(void* context) {
         false);
 }
 
-/**
- * Thanks to the author of metronome
- * @param canvas
- * @param str
- */
-void elements_button_top_left(Canvas* canvas, const char* str) {
-    const Icon* icon = &I_ButtonUp_7x4;
-
-    const uint8_t button_height = 12;
-    const uint8_t vertical_offset = 3;
-    const uint8_t horizontal_offset = 3;
-    const uint8_t string_width = canvas_string_width(canvas, str);
-    const uint8_t icon_h_offset = 3;
-    const uint8_t icon_width_with_offset = icon_get_width(icon) + icon_h_offset;
-    const uint8_t icon_v_offset = icon_get_height(icon) + vertical_offset;
-    const uint8_t button_width = string_width + horizontal_offset * 2 + icon_width_with_offset;
-
-    const uint8_t x = 0;
-    const uint8_t y = 0 + button_height;
-
-    uint8_t line_x = x + button_width;
-    uint8_t line_y = y - button_height;
-    canvas_draw_box(canvas, x, line_y, button_width, button_height);
-    canvas_draw_line(canvas, line_x + 0, line_y, line_x + 0, y - 1);
-    canvas_draw_line(canvas, line_x + 1, line_y, line_x + 1, y - 2);
-    canvas_draw_line(canvas, line_x + 2, line_y, line_x + 2, y - 3);
-
-    canvas_invert_color(canvas);
-    canvas_draw_icon(canvas, x + horizontal_offset, y - icon_v_offset, icon);
-    canvas_draw_str(
-        canvas, x + horizontal_offset + icon_width_with_offset, y - vertical_offset, str);
-    canvas_invert_color(canvas);
-}
-
-/**
- * Thanks to the author of metronome
- * @param canvas
- * @param str
- */
-void elements_button_top_right(Canvas* canvas, const char* str) {
-    const Icon* icon = &I_ButtonDown_7x4;
-
-    const uint8_t button_height = 12;
-    const uint8_t vertical_offset = 3;
-    const uint8_t horizontal_offset = 3;
-    const uint8_t string_width = canvas_string_width(canvas, str);
-    const uint8_t icon_h_offset = 3;
-    const uint8_t icon_width_with_offset = icon_get_width(icon) + icon_h_offset;
-    const uint8_t icon_v_offset = icon_get_height(icon) + vertical_offset + 1;
-    const uint8_t button_width = string_width + horizontal_offset * 2 + icon_width_with_offset;
-
-    const uint8_t x = canvas_width(canvas);
-    const uint8_t y = 0 + button_height;
-
-    uint8_t line_x = x - button_width;
-    uint8_t line_y = y - button_height;
-    canvas_draw_box(canvas, line_x, line_y, button_width, button_height);
-    canvas_draw_line(canvas, line_x - 1, line_y, line_x - 1, y - 1);
-    canvas_draw_line(canvas, line_x - 2, line_y, line_x - 2, y - 2);
-    canvas_draw_line(canvas, line_x - 3, line_y, line_x - 3, y - 3);
-
-    canvas_invert_color(canvas);
-    canvas_draw_str(canvas, x - button_width + horizontal_offset, y - vertical_offset, str);
-    canvas_draw_icon(
-        canvas, x - horizontal_offset - icon_get_width(icon), y - icon_v_offset, icon);
-    canvas_invert_color(canvas);
-}
-
 void subbrute_attack_view_draw(Canvas* canvas, void* context) {
     furi_assert(context);
     SubBruteAttackViewModel* model = (SubBruteAttackViewModel*)context;
@@ -339,9 +272,27 @@ void subbrute_attack_view_draw(Canvas* canvas, void* context) {
     }
 
     // Current Step / Max value
-    canvas_set_font(canvas, FontBigNumbers);
-    snprintf(buffer, sizeof(buffer), "%04d/%04d", (int)model->current_step, (int)model->max_value);
-    canvas_draw_str_aligned(canvas, 64, 17, AlignCenter, AlignTop, buffer);
+    const uint8_t y_frequency = 17;
+    if(model->max_value > 9999) {
+        canvas_set_font(canvas, FontBigNumbers);
+        snprintf(buffer, sizeof(buffer), "%05d/", (int)model->current_step);
+        canvas_draw_str_aligned(canvas, 5, y_frequency, AlignLeft, AlignTop, buffer);
+
+        // Second part with another font, because BigNumber is out of screen bounds
+        canvas_set_font(canvas, FontPrimary);
+        snprintf(buffer, sizeof(buffer), "%05d", (int)model->max_value);
+        canvas_draw_str_aligned(canvas, 107, y_frequency + 13, AlignRight, AlignBottom, buffer);
+    } else if(model->max_value <= 0xFF) {
+        canvas_set_font(canvas, FontBigNumbers);
+        snprintf(
+            buffer, sizeof(buffer), "%03d/%03d", (int)model->current_step, (int)model->max_value);
+        canvas_draw_str_aligned(canvas, 64, y_frequency, AlignCenter, AlignTop, buffer);
+    } else {
+        canvas_set_font(canvas, FontBigNumbers);
+        snprintf(
+            buffer, sizeof(buffer), "%04d/%04d", (int)model->current_step, (int)model->max_value);
+        canvas_draw_str_aligned(canvas, 64, y_frequency, AlignCenter, AlignTop, buffer);
+    }
     canvas_set_font(canvas, FontSecondary);
 
     memset(buffer, 0, sizeof(buffer));

+ 143 - 84
views/subbrute_main_view.c

@@ -1,6 +1,7 @@
 #include "subbrute_main_view.h"
 #include "../subbrute_i.h"
 #include "../subbrute_protocols.h"
+#include "../helpers/gui_top_buttons.h"
 
 #include <input/input.h>
 #include <gui/elements.h>
@@ -10,6 +11,15 @@
 #define TAG "SubBruteMainView"
 
 #define ITEMS_ON_SCREEN 3
+#define ITEMS_INTERVAL 1
+#define ITEM_WIDTH 14
+#define ITEM_Y 27
+#define ITEM_HEIGHT 13
+#define TEXT_X 6
+#define TEXT_Y 37
+#define TEXT_INTERVAL 3
+#define TEXT_WIDTH 12
+#define ITEM_FRAME_RADIUS 2
 
 struct SubBruteMainView {
     View* view;
@@ -17,7 +27,8 @@ struct SubBruteMainView {
     void* context;
     uint8_t index;
     bool is_select_byte;
-    const char* key_field;
+    bool two_bytes;
+    uint64_t key_from_file;
     uint8_t extra_repeats;
     uint8_t window_position;
 };
@@ -27,7 +38,8 @@ typedef struct {
     uint8_t extra_repeats;
     uint8_t window_position;
     bool is_select_byte;
-    const char* key_field;
+    bool two_bytes;
+    uint64_t key_from_file;
 } SubBruteMainViewModel;
 
 void subbrute_main_view_set_callback(
@@ -41,84 +53,105 @@ void subbrute_main_view_set_callback(
     instance->context = context;
 }
 
-FuriString* center_displayed_key(const char* key_cstr, uint8_t index) {
-    uint8_t str_index = (index * 3);
-
-    char display_menu[] = {
-        'X', 'X', ' ', 'X', 'X', ' ', '<', 'X', 'X', '>', ' ', 'X', 'X', ' ', 'X', 'X', '\0'};
-
-    if(key_cstr != NULL) {
-        if(index > 1) {
-            display_menu[0] = key_cstr[str_index - 6];
-            display_menu[1] = key_cstr[str_index - 5];
-        } else {
-            display_menu[0] = ' ';
-            display_menu[1] = ' ';
-        }
-
-        if(index > 0) {
-            display_menu[3] = key_cstr[str_index - 3];
-            display_menu[4] = key_cstr[str_index - 2];
-        } else {
-            display_menu[3] = ' ';
-            display_menu[4] = ' ';
-        }
-
-        display_menu[7] = key_cstr[str_index];
-        display_menu[8] = key_cstr[str_index + 1];
-
-        if((str_index + 4) <= (uint8_t)strlen(key_cstr)) {
-            display_menu[11] = key_cstr[str_index + 3];
-            display_menu[12] = key_cstr[str_index + 4];
-        } else {
-            display_menu[11] = ' ';
-            display_menu[12] = ' ';
-        }
-
-        if((str_index + 8) <= (uint8_t)strlen(key_cstr)) {
-            display_menu[14] = key_cstr[str_index + 6];
-            display_menu[15] = key_cstr[str_index + 7];
+void subbrute_main_view_center_displayed_key(
+    Canvas* canvas,
+    uint64_t key,
+    uint8_t index,
+    bool two_bytes) {
+    uint8_t text_x = TEXT_X;
+    uint8_t item_x = TEXT_X - ITEMS_INTERVAL;
+    canvas_set_font(canvas, FontSecondary);
+
+    for(int i = 0; i < 8; i++) {
+        char current_value[3] = {0};
+        uint8_t byte_value = (uint8_t)(key >> 8 * (7 - i)) & 0xFF;
+        snprintf(current_value, sizeof(current_value), "%02X", byte_value);
+
+        // For two bytes we need to select prev location
+        if(!two_bytes && i == index) {
+            canvas_set_color(canvas, ColorBlack);
+            canvas_draw_rbox(
+                canvas, item_x - 1, ITEM_Y, ITEM_WIDTH + 1, ITEM_HEIGHT, ITEM_FRAME_RADIUS);
+            canvas_set_color(canvas, ColorWhite);
+            canvas_draw_str(canvas, text_x, TEXT_Y, current_value);
+        } else if(two_bytes && (i == index || i == index - 1)) {
+            if(i == index) {
+                canvas_set_color(canvas, ColorBlack);
+                canvas_draw_rbox(
+                    canvas,
+                    item_x - ITEMS_INTERVAL - ITEM_WIDTH - 1,
+                    ITEM_Y,
+                    ITEM_WIDTH * 2 + ITEMS_INTERVAL * 2 + 1,
+                    ITEM_HEIGHT,
+                    ITEM_FRAME_RADIUS);
+
+                canvas_set_color(canvas, ColorWhite);
+                canvas_draw_str(canvas, text_x, TEXT_Y, current_value);
+
+                // Redraw prev element with white
+                memset(current_value, 0, sizeof(current_value));
+                byte_value = (uint8_t)(key >> 8 * (7 - i + 1)) & 0xFF;
+                snprintf(current_value, sizeof(current_value), "%02X", byte_value);
+                canvas_draw_str(
+                    canvas, text_x - (TEXT_WIDTH + TEXT_INTERVAL), TEXT_Y, current_value);
+            } else {
+                canvas_set_color(canvas, ColorWhite);
+                canvas_draw_str(canvas, text_x, TEXT_Y, current_value);
+            }
         } else {
-            display_menu[14] = ' ';
-            display_menu[15] = ' ';
+            canvas_set_color(canvas, ColorBlack);
+            canvas_draw_str(canvas, text_x, TEXT_Y, current_value);
         }
+        text_x = text_x + TEXT_WIDTH + TEXT_INTERVAL;
+        item_x = item_x + ITEM_WIDTH + ITEMS_INTERVAL;
     }
-    return furi_string_alloc_set(display_menu);
+
+    // Return normal color
+    canvas_set_color(canvas, ColorBlack);
 }
 
 void subbrute_main_view_draw(Canvas* canvas, SubBruteMainViewModel* model) {
-    // 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, "Sub-GHz BruteForcer 3.2");
-    canvas_invert_color(canvas);
-
     uint16_t screen_width = canvas_width(canvas);
     uint16_t screen_height = canvas_height(canvas);
 
     if(model->is_select_byte) {
 #ifdef FURI_DEBUG
-        //FURI_LOG_D(TAG, "key_field: %s", model->key_field);
+        //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_draw_str_aligned(canvas, 64, 26, AlignCenter, AlignTop, msg_index);
-
-        FuriString* menu_items;
-
-        menu_items = center_displayed_key(model->key_field, model->index);
+        //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, 40, AlignCenter, AlignTop, furi_string_get_cstr(menu_items));
+            canvas, 64, 17, AlignCenter, AlignTop, "Please select values to calc:");
 
-        elements_button_center(canvas, "Select");
-        elements_button_left(canvas, "<");
-        elements_button_right(canvas, ">");
+        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));
 
-        furi_string_reset(menu_items);
-        furi_string_free(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");
+        }
     } 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, "Sub-GHz BruteForcer 3.3");
+        canvas_invert_color(canvas);
+
         // Menu
         canvas_set_color(canvas, ColorBlack);
         canvas_set_font(canvas, FontSecondary);
@@ -257,18 +290,34 @@ bool subbrute_main_view_input(InputEvent* event, void* context) {
                 }
             }
         }
-    } else {
-        if(event->key == InputKeyLeft && is_short) {
-            if(instance->index > 0) {
+    } 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 && is_short) {
+            consumed = true;
+        } else if(event->key == InputKeyRight) {
             if(instance->index < 7) {
                 instance->index++;
             }
             updated = true;
-        } else if(event->key == InputKeyOk && is_short) {
+            consumed = 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;
+            consumed = true;
+        } else if(event->key == InputKeyOk) {
             instance->callback(SubBruteCustomEventTypeIndexSelected, instance->context);
             consumed = true;
             updated = true;
@@ -282,8 +331,9 @@ bool subbrute_main_view_input(InputEvent* event, void* context) {
             {
                 model->index = instance->index;
                 model->window_position = instance->window_position;
-                model->key_field = instance->key_field;
+                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);
@@ -318,24 +368,25 @@ SubBruteMainView* subbrute_main_view_alloc() {
     view_set_enter_callback(instance->view, subbrute_main_view_enter);
     view_set_exit_callback(instance->view, subbrute_main_view_exit);
 
+    instance->index = 0;
+    instance->window_position = 0;
+    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,
         {
-            model->index = 0;
-            model->window_position = 0;
-            model->key_field = NULL;
-            model->is_select_byte = false;
-            model->extra_repeats = 0;
+            model->index = instance->index;
+            model->window_position = instance->window_position;
+            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);
 
-    instance->index = 0;
-    instance->window_position = 0;
-    instance->key_field = NULL;
-    instance->is_select_byte = false;
-    instance->extra_repeats = 0;
-
     return instance;
 }
 
@@ -355,14 +406,16 @@ void subbrute_main_view_set_index(
     SubBruteMainView* instance,
     uint8_t idx,
     bool is_select_byte,
-    const char* key_field) {
+    bool two_bytes,
+    uint64_t key_from_file) {
     furi_assert(instance);
     furi_assert(idx < SubBruteAttackTotalCount);
 #ifdef FURI_DEBUG
-    FURI_LOG_I(TAG, "Set index: %d, IS_SELECT_BYTE: %d", idx, is_select_byte);
+    FURI_LOG_I(TAG, "Set index: %d, is_select_byte: %d", idx, is_select_byte);
 #endif
     instance->is_select_byte = is_select_byte;
-    instance->key_field = key_field;
+    instance->two_bytes = two_bytes;
+    instance->key_from_file = key_from_file;
     instance->index = idx;
     instance->window_position = idx;
 
@@ -386,8 +439,9 @@ void subbrute_main_view_set_index(
         {
             model->index = instance->index;
             model->window_position = instance->window_position;
-            model->key_field = instance->key_field;
+            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);
@@ -401,4 +455,9 @@ SubBruteAttacks subbrute_main_view_get_index(SubBruteMainView* instance) {
 uint8_t subbrute_main_view_get_extra_repeats(SubBruteMainView* instance) {
     furi_assert(instance);
     return instance->extra_repeats;
+}
+
+bool subbrute_main_view_get_two_bytes(SubBruteMainView* instance) {
+    furi_assert(instance);
+    return instance->two_bytes;
 }

+ 3 - 1
views/subbrute_main_view.h

@@ -21,9 +21,11 @@ void subbrute_main_view_set_index(
     SubBruteMainView* instance,
     uint8_t idx,
     bool is_select_byte,
-    const char* key_field);
+    bool two_bytes,
+    uint64_t file_key);
 SubBruteAttacks subbrute_main_view_get_index(SubBruteMainView* instance);
 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);