Explorar o código

Merge branch 'main' into dev

gid9798 %!s(int64=2) %!d(string=hai) anos
pai
achega
735a1185d0

+ 9 - 0
fuzzer.c

@@ -46,6 +46,11 @@ PacsFuzzerApp* fuzzer_app_alloc() {
     app->popup = popup_alloc();
     app->popup = popup_alloc();
     view_dispatcher_add_view(app->view_dispatcher, FuzzerViewIDPopup, popup_get_view(app->popup));
     view_dispatcher_add_view(app->view_dispatcher, FuzzerViewIDPopup, popup_get_view(app->popup));
 
 
+    // TextInput
+    app->text_input = text_input_alloc();
+    view_dispatcher_add_view(
+        app->view_dispatcher, FuzzerViewIDTextInput, text_input_get_view(app->text_input));
+
     // Main view
     // Main view
     app->main_view = fuzzer_view_main_alloc();
     app->main_view = fuzzer_view_main_alloc();
     view_dispatcher_add_view(
     view_dispatcher_add_view(
@@ -100,6 +105,10 @@ void fuzzer_app_free(PacsFuzzerApp* app) {
     view_dispatcher_remove_view(app->view_dispatcher, FuzzerViewIDPopup);
     view_dispatcher_remove_view(app->view_dispatcher, FuzzerViewIDPopup);
     popup_free(app->popup);
     popup_free(app->popup);
 
 
+    // TextInput
+    view_dispatcher_remove_view(app->view_dispatcher, FuzzerViewIDTextInput);
+    text_input_free(app->text_input);
+
     scene_manager_free(app->scene_manager);
     scene_manager_free(app->scene_manager);
     view_dispatcher_free(app->view_dispatcher);
     view_dispatcher_free(app->view_dispatcher);
 
 

+ 4 - 0
fuzzer_i.h

@@ -7,6 +7,7 @@
 #include <gui/view_dispatcher.h>
 #include <gui/view_dispatcher.h>
 #include <gui/scene_manager.h>
 #include <gui/scene_manager.h>
 #include <gui/modules/popup.h>
 #include <gui/modules/popup.h>
+#include <gui/modules/text_input.h>
 
 
 #include <dialogs/dialogs.h>
 #include <dialogs/dialogs.h>
 #include <notification/notification_messages.h>
 #include <notification/notification_messages.h>
@@ -23,6 +24,7 @@
 #include "fuzzer_icons.h"
 #include "fuzzer_icons.h"
 
 
 #define FUZZ_TIME_DELAY_MAX (80)
 #define FUZZ_TIME_DELAY_MAX (80)
+#define KEY_NAME_SIZE 22
 
 
 typedef struct {
 typedef struct {
     const char* custom_dict_extension;
     const char* custom_dict_extension;
@@ -41,11 +43,13 @@ typedef struct {
 
 
     Popup* popup;
     Popup* popup;
     DialogsApp* dialogs;
     DialogsApp* dialogs;
+    TextInput* text_input;
     FuzzerViewMain* main_view;
     FuzzerViewMain* main_view;
     FuzzerViewAttack* attack_view;
     FuzzerViewAttack* attack_view;
     FuzzerViewFieldEditor* field_editor_view;
     FuzzerViewFieldEditor* field_editor_view;
 
 
     FuriString* file_path;
     FuriString* file_path;
+    char key_name[KEY_NAME_SIZE + 1];
 
 
     FuzzerState fuzzer_state;
     FuzzerState fuzzer_state;
     FuzzerConsts* fuzzer_const;
     FuzzerConsts* fuzzer_const;

+ 13 - 3
helpers/fuzzer_custom_event.h

@@ -7,11 +7,21 @@ typedef enum {
     FuzzerCustomEventViewMainOk,
     FuzzerCustomEventViewMainOk,
     FuzzerCustomEventViewMainPopupErr,
     FuzzerCustomEventViewMainPopupErr,
 
 
-    FuzzerCustomEventViewAttackBack,
-    FuzzerCustomEventViewAttackOk,
-    // FuzzerCustomEventViewAttackTick, // now not use
     FuzzerCustomEventViewAttackEnd,
     FuzzerCustomEventViewAttackEnd,
 
 
+    FuzzerCustomEventViewAttackExit,
+    FuzzerCustomEventViewAttackRunAttack,
+    FuzzerCustomEventViewAttackPause,
+    FuzzerCustomEventViewAttackIdle, // Setup
+    FuzzerCustomEventViewAttackEmulateCurrent,
+    FuzzerCustomEventViewAttackSave,
+    FuzzerCustomEventViewAttackNextUid,
+    FuzzerCustomEventViewAttackPrevUid,
+
     FuzzerCustomEventViewFieldEditorBack,
     FuzzerCustomEventViewFieldEditorBack,
     FuzzerCustomEventViewFieldEditorOk,
     FuzzerCustomEventViewFieldEditorOk,
+
+    FuzzerCustomEventTextEditResult,
+
+    FuzzerCustomEventPopupClosed,
 } FuzzerCustomEvent;
 } FuzzerCustomEvent;

+ 4 - 1
helpers/fuzzer_types.h

@@ -10,7 +10,9 @@ typedef struct {
 typedef enum {
 typedef enum {
     FuzzerAttackStateOff = 0,
     FuzzerAttackStateOff = 0,
     FuzzerAttackStateIdle,
     FuzzerAttackStateIdle,
-    FuzzerAttackStateRunning,
+    FuzzerAttackStateAttacking,
+    FuzzerAttackStateEmulating,
+    FuzzerAttackStatePause,
     FuzzerAttackStateEnd,
     FuzzerAttackStateEnd,
 
 
 } FuzzerAttackState;
 } FuzzerAttackState;
@@ -23,6 +25,7 @@ typedef enum {
 
 
 typedef enum {
 typedef enum {
     FuzzerViewIDPopup,
     FuzzerViewIDPopup,
+    FuzzerViewIDTextInput,
 
 
     FuzzerViewIDMain,
     FuzzerViewIDMain,
     FuzzerViewIDAttack,
     FuzzerViewIDAttack,

BIN=BIN
icons/DolphinNice_96x59.png


+ 208 - 185
lib/worker/fake_worker.c

@@ -1,4 +1,5 @@
 #include "fake_worker.h"
 #include "fake_worker.h"
+#include "helpers/hardware_worker.h"
 #include "protocol_i.h"
 #include "protocol_i.h"
 
 
 #include <timer.h>
 #include <timer.h>
@@ -8,47 +9,27 @@
 #include <toolbox/stream/buffered_file_stream.h>
 #include <toolbox/stream/buffered_file_stream.h>
 
 
 #define TAG "Fuzzer worker"
 #define TAG "Fuzzer worker"
+#define TOTAL_PROTOCOL_COUNT fuzzer_proto_get_count_of_protocols()
+#define PROTOCOL_KEY_FOLDER EXT_PATH(PROTOCOL_KEY_FOLDER_NAME)
 
 
-#if defined(RFID_125_PROTOCOL)
-
-#include <lib/lfrfid/lfrfid_dict_file.h>
-#include <lib/lfrfid/lfrfid_worker.h>
-#include <lfrfid/protocols/lfrfid_protocols.h>
-
-#else
-
-#include <lib/ibutton/ibutton_worker.h>
-#include <lib/ibutton/ibutton_key.h>
-
-#endif
-
-#include <toolbox/stream/stream.h>
+typedef uint8_t FuzzerWorkerPayload[MAX_PAYLOAD_SIZE];
 
 
 struct FuzzerWorker {
 struct FuzzerWorker {
-#if defined(RFID_125_PROTOCOL)
-    LFRFIDWorker* proto_worker;
-    ProtocolId protocol_id;
-    ProtocolDict* protocols_items;
-#else
-    iButtonWorker* proto_worker;
-    iButtonProtocolId protocol_id; // TODO
-    iButtonProtocols* protocols_items;
-    iButtonKey* key;
-#endif
+    HardwareWorker* hw_worker;
 
 
     const FuzzerProtocol* protocol;
     const FuzzerProtocol* protocol;
-    FuzzerWorkerAttackType attack_type;
-    uint16_t timer_idle_time_ms;
-    uint16_t timer_emu_time_ms;
+    HwProtocolID* suported_proto;
 
 
-    uint8_t payload[MAX_PAYLOAD_SIZE];
-    Stream* uids_stream;
+    FuzzerWorkerPayload payload;
+
+    FuzzerWorkerAttackType attack_type;
     uint16_t index;
     uint16_t index;
-    uint8_t chusen_byte;
+    Stream* uids_stream;
 
 
-    bool treead_running;
     bool in_emu_phase;
     bool in_emu_phase;
     FuriTimer* timer;
     FuriTimer* timer;
+    uint16_t timer_idle_time_ms;
+    uint16_t timer_emu_time_ms;
 
 
     FuzzerWorkerUidChagedCallback tick_callback;
     FuzzerWorkerUidChagedCallback tick_callback;
     void* tick_context;
     void* tick_context;
@@ -57,6 +38,85 @@ struct FuzzerWorker {
     void* end_context;
     void* end_context;
 };
 };
 
 
+static bool fuzzer_worker_set_protocol(FuzzerWorker* instance, FuzzerProtocolsID protocol_index) {
+    if(!(protocol_index < TOTAL_PROTOCOL_COUNT)) {
+        return false;
+    }
+
+    instance->protocol = &fuzzer_proto_items[protocol_index];
+    return hardware_worker_set_protocol_id_by_name(
+        instance->hw_worker, fuzzer_proto_items[protocol_index].name);
+}
+
+static FuzzerProtocolsID
+    fuzzer_worker_is_protocol_valid(FuzzerWorker* instance, HwProtocolID protocol_id) {
+    for(FuzzerProtocolsID i = 0; i < TOTAL_PROTOCOL_COUNT; i++) {
+        if(protocol_id == instance->suported_proto[i]) {
+            return i;
+        }
+    }
+    return TOTAL_PROTOCOL_COUNT;
+}
+
+FuzzerWorkerLoadKeyState fuzzer_worker_load_key_from_file(
+    FuzzerWorker* instance,
+    FuzzerProtocolsID* protocol_index,
+    const char* filename) {
+    furi_assert(instance);
+
+    FuzzerWorkerLoadKeyState res = FuzzerWorkerLoadKeyStateUnsuportedProto;
+    if(!hardware_worker_load_key_from_file(instance->hw_worker, filename)) {
+        FURI_LOG_E(TAG, "Load key file: cant load file");
+        res = FuzzerWorkerLoadKeyStateBadFile;
+    } else {
+        FuzzerProtocolsID loaded_id = fuzzer_worker_is_protocol_valid(
+            instance, hardware_worker_get_protocol_id(instance->hw_worker));
+
+        if(!fuzzer_worker_set_protocol(instance, loaded_id)) {
+            FURI_LOG_E(TAG, "Load key file: Unsuported protocol");
+            res = FuzzerWorkerLoadKeyStateUnsuportedProto;
+        } else {
+            if(*protocol_index != loaded_id) {
+                res = FuzzerWorkerLoadKeyStateDifferentProto;
+            } else {
+                res = FuzzerWorkerLoadKeyStateOk;
+            }
+            *protocol_index = loaded_id;
+
+            hardware_worker_get_protocol_data(
+                instance->hw_worker, &instance->payload[0], MAX_PAYLOAD_SIZE);
+        }
+    }
+
+    return res;
+}
+
+static bool fuzer_worker_make_key_folder() {
+    Storage* storage = furi_record_open(RECORD_STORAGE);
+
+    const bool res = storage_simply_mkdir(storage, PROTOCOL_KEY_FOLDER);
+
+    furi_record_close(RECORD_STORAGE);
+
+    return res;
+}
+
+bool fuzzer_worker_save_key(FuzzerWorker* instance, const char* path) {
+    furi_assert(instance);
+    bool res = false;
+
+    if(!fuzer_worker_make_key_folder()) {
+        FURI_LOG_E(TAG, "Cannot create key folder");
+    } else if(!hardware_worker_save_key(instance->hw_worker, path)) {
+        FURI_LOG_E(TAG, "Cannot save key file");
+    } else {
+        FURI_LOG_D(TAG, "Save key Success");
+        res = true;
+    }
+
+    return res;
+}
+
 static bool fuzzer_worker_load_key(FuzzerWorker* instance, bool next) {
 static bool fuzzer_worker_load_key(FuzzerWorker* instance, bool next) {
     furi_assert(instance);
     furi_assert(instance);
     furi_assert(instance->protocol);
     furi_assert(instance->protocol);
@@ -67,7 +127,11 @@ static bool fuzzer_worker_load_key(FuzzerWorker* instance, bool next) {
     switch(instance->attack_type) {
     switch(instance->attack_type) {
     case FuzzerWorkerAttackTypeDefaultDict:
     case FuzzerWorkerAttackTypeDefaultDict:
         if(next) {
         if(next) {
-            instance->index++;
+            if(instance->index < (protocol->dict.len - 1)) {
+                instance->index++;
+            } else {
+                break;
+            }
         }
         }
         if(instance->index < protocol->dict.len) {
         if(instance->index < protocol->dict.len) {
             memcpy(
             memcpy(
@@ -113,6 +177,7 @@ static bool fuzzer_worker_load_key(FuzzerWorker* instance, bool next) {
             }
             }
             break;
             break;
         }
         }
+        furi_string_free(data_str);
     }
     }
 
 
     break;
     break;
@@ -128,18 +193,51 @@ static bool fuzzer_worker_load_key(FuzzerWorker* instance, bool next) {
     default:
     default:
         break;
         break;
     }
     }
-#if defined(RFID_125_PROTOCOL)
-    protocol_dict_set_data(
-        instance->protocols_items, instance->protocol_id, instance->payload, MAX_PAYLOAD_SIZE);
-#else
-    ibutton_key_set_protocol_id(instance->key, instance->protocol_id);
-    iButtonEditableData data;
-    ibutton_protocols_get_editable_data(instance->protocols_items, instance->key, &data);
-
-    //  TODO  check data.size logic
-    data.size = MAX_PAYLOAD_SIZE;
-    memcpy(data.ptr, instance->payload, MAX_PAYLOAD_SIZE); // data.size);
-#endif
+
+    if(res) {
+        hardware_worker_set_protocol_data(
+            instance->hw_worker, &instance->payload[0], protocol->data_size);
+    }
+
+    return res;
+}
+
+static bool fuzzer_worker_load_previous_key(FuzzerWorker* instance) {
+    furi_assert(instance);
+    furi_assert(instance->protocol);
+    bool res = false;
+
+    const FuzzerProtocol* protocol = instance->protocol;
+
+    switch(instance->attack_type) {
+    case FuzzerWorkerAttackTypeDefaultDict:
+        if(instance->index > 0) {
+            instance->index--;
+            memcpy(
+                instance->payload,
+                &protocol->dict.val[instance->index * protocol->data_size],
+                protocol->data_size);
+            res = true;
+        }
+        break;
+
+    case FuzzerWorkerAttackTypeLoadFile:
+        if(instance->payload[instance->index] != 0x00) {
+            instance->payload[instance->index]--;
+            res = true;
+        }
+
+        break;
+
+    default:
+        break;
+    }
+
+    if(res) {
+        hardware_worker_set_protocol_data(
+            instance->hw_worker, &instance->payload[0], protocol->data_size);
+    }
+
     return res;
     return res;
 }
 }
 
 
@@ -149,13 +247,7 @@ static void fuzzer_worker_on_tick_callback(void* context) {
     FuzzerWorker* instance = context;
     FuzzerWorker* instance = context;
 
 
     if(instance->in_emu_phase) {
     if(instance->in_emu_phase) {
-        if(instance->treead_running) {
-#if defined(RFID_125_PROTOCOL)
-            lfrfid_worker_stop(instance->proto_worker);
-#else
-            ibutton_worker_stop(instance->proto_worker);
-#endif
-        }
+        hardware_worker_stop(instance->hw_worker);
         instance->in_emu_phase = false;
         instance->in_emu_phase = false;
         furi_timer_start(instance->timer, furi_ms_to_ticks(instance->timer_idle_time_ms));
         furi_timer_start(instance->timer, furi_ms_to_ticks(instance->timer_idle_time_ms));
     } else {
     } else {
@@ -165,13 +257,7 @@ static void fuzzer_worker_on_tick_callback(void* context) {
                 instance->end_callback(instance->end_context);
                 instance->end_callback(instance->end_context);
             }
             }
         } else {
         } else {
-            if(instance->treead_running) {
-#if defined(RFID_125_PROTOCOL)
-                lfrfid_worker_emulate_start(instance->proto_worker, instance->protocol_id);
-#else
-                ibutton_worker_emulate_start(instance->proto_worker, instance->key);
-#endif
-            }
+            hardware_worker_emulate_start(instance->hw_worker);
             instance->in_emu_phase = true;
             instance->in_emu_phase = true;
             furi_timer_start(instance->timer, furi_ms_to_ticks(instance->timer_emu_time_ms));
             furi_timer_start(instance->timer, furi_ms_to_ticks(instance->timer_emu_time_ms));
             if(instance->tick_callback) {
             if(instance->tick_callback) {
@@ -190,24 +276,29 @@ void fuzzer_worker_get_current_key(FuzzerWorker* instance, FuzzerPayload* output
     memcpy(output_key->data, instance->payload, instance->protocol->data_size);
     memcpy(output_key->data, instance->payload, instance->protocol->data_size);
 }
 }
 
 
-static void fuzzer_worker_set_protocol(FuzzerWorker* instance, FuzzerProtocolsID protocol_index) {
-    instance->protocol = &fuzzer_proto_items[protocol_index];
+bool fuzzer_worker_next_key(FuzzerWorker* instance) {
+    furi_assert(instance);
+    furi_assert(instance->protocol);
+
+    return fuzzer_worker_load_key(instance, true);
+}
+
+bool fuzzer_worker_previous_key(FuzzerWorker* instance) {
+    furi_assert(instance);
+    furi_assert(instance->protocol);
 
 
-#if defined(RFID_125_PROTOCOL)
-    instance->protocol_id =
-        protocol_dict_get_protocol_by_name(instance->protocols_items, instance->protocol->name);
-#else
-    // TODO iButtonProtocolIdInvalid check
-    instance->protocol_id =
-        ibutton_protocols_get_id_by_name(instance->protocols_items, instance->protocol->name);
-#endif
+    return fuzzer_worker_load_previous_key(instance);
 }
 }
 
 
 bool fuzzer_worker_init_attack_dict(FuzzerWorker* instance, FuzzerProtocolsID protocol_index) {
 bool fuzzer_worker_init_attack_dict(FuzzerWorker* instance, FuzzerProtocolsID protocol_index) {
     furi_assert(instance);
     furi_assert(instance);
 
 
     bool res = false;
     bool res = false;
-    fuzzer_worker_set_protocol(instance, protocol_index);
+
+    if(!fuzzer_worker_set_protocol(instance, protocol_index)) {
+        instance->attack_type = FuzzerWorkerAttackTypeMax;
+        return res;
+    }
 
 
     instance->attack_type = FuzzerWorkerAttackTypeDefaultDict;
     instance->attack_type = FuzzerWorkerAttackTypeDefaultDict;
     instance->index = 0;
     instance->index = 0;
@@ -229,10 +320,15 @@ bool fuzzer_worker_init_attack_file_dict(
     furi_assert(file_path);
     furi_assert(file_path);
 
 
     bool res = false;
     bool res = false;
-    fuzzer_worker_set_protocol(instance, protocol_index);
+
+    if(!fuzzer_worker_set_protocol(instance, protocol_index)) {
+        instance->attack_type = FuzzerWorkerAttackTypeMax;
+        return res;
+    }
 
 
     Storage* storage = furi_record_open(RECORD_STORAGE);
     Storage* storage = furi_record_open(RECORD_STORAGE);
     instance->uids_stream = buffered_file_stream_alloc(storage);
     instance->uids_stream = buffered_file_stream_alloc(storage);
+    furi_record_close(RECORD_STORAGE);
 
 
     if(!buffered_file_stream_open(
     if(!buffered_file_stream_open(
            instance->uids_stream, furi_string_get_cstr(file_path), FSAM_READ, FSOM_OPEN_EXISTING)) {
            instance->uids_stream, furi_string_get_cstr(file_path), FSAM_READ, FSOM_OPEN_EXISTING)) {
@@ -246,7 +342,7 @@ bool fuzzer_worker_init_attack_file_dict(
     if(!fuzzer_worker_load_key(instance, false)) {
     if(!fuzzer_worker_load_key(instance, false)) {
         instance->attack_type = FuzzerWorkerAttackTypeMax;
         instance->attack_type = FuzzerWorkerAttackTypeMax;
         buffered_file_stream_close(instance->uids_stream);
         buffered_file_stream_close(instance->uids_stream);
-        furi_record_close(RECORD_STORAGE);
+        stream_free(instance->uids_stream);
     } else {
     } else {
         res = true;
         res = true;
     }
     }
@@ -262,88 +358,49 @@ bool fuzzer_worker_init_attack_bf_byte(
     furi_assert(instance);
     furi_assert(instance);
 
 
     bool res = false;
     bool res = false;
-    fuzzer_worker_set_protocol(instance, protocol_index);
+    if(!fuzzer_worker_set_protocol(instance, protocol_index)) {
+        instance->attack_type = FuzzerWorkerAttackTypeMax;
+        return res;
+    }
 
 
     instance->attack_type = FuzzerWorkerAttackTypeLoadFile;
     instance->attack_type = FuzzerWorkerAttackTypeLoadFile;
     instance->index = chusen;
     instance->index = chusen;
 
 
     memcpy(instance->payload, new_uid->data, instance->protocol->data_size);
     memcpy(instance->payload, new_uid->data, instance->protocol->data_size);
 
 
-    res = true;
+    hardware_worker_set_protocol_data(
+        instance->hw_worker, &instance->payload[0], instance->protocol->data_size);
 
 
-    return res;
-}
-
-// TODO make it protocol independent
-bool fuzzer_worker_load_key_from_file(
-    FuzzerWorker* instance,
-    FuzzerProtocolsID protocol_index,
-    const char* filename) {
-    furi_assert(instance);
-
-    bool res = false;
-    fuzzer_worker_set_protocol(instance, protocol_index);
-
-#if defined(RFID_125_PROTOCOL)
-    ProtocolId loaded_proto_id = lfrfid_dict_file_load(instance->protocols_items, filename);
-    if(loaded_proto_id == PROTOCOL_NO) {
-        // Err Cant load file
-        FURI_LOG_W(TAG, "Cant load file");
-    } else if(instance->protocol_id != loaded_proto_id) { // Err wrong protocol
-        FURI_LOG_W(TAG, "Wrong protocol");
-        FURI_LOG_W(
-            TAG,
-            "Selected: %s Loaded: %s",
-            instance->protocol->name,
-            protocol_dict_get_name(instance->protocols_items, loaded_proto_id));
-    } else {
-        protocol_dict_get_data(
-            instance->protocols_items, instance->protocol_id, instance->payload, MAX_PAYLOAD_SIZE);
-        res = true;
-    }
-#else
-    if(!ibutton_protocols_load(instance->protocols_items, instance->key, filename)) {
-        // Err Cant load file
-        FURI_LOG_W(TAG, "Cant load file");
-    } else {
-        if(instance->protocol_id != ibutton_key_get_protocol_id(instance->key)) {
-            // Err wrong protocol
-            FURI_LOG_W(TAG, "Wrong protocol");
-            FURI_LOG_W(
-                TAG,
-                "Selected: %s Loaded: %s",
-                instance->protocol->name,
-                ibutton_protocols_get_name(
-                    instance->protocols_items, ibutton_key_get_protocol_id(instance->key)));
-        } else {
-            iButtonEditableData data;
-            ibutton_protocols_get_editable_data(instance->protocols_items, instance->key, &data);
-            memcpy(instance->payload, data.ptr, data.size);
-            res = true;
-        }
-    }
-#endif
-
-    return res;
+    return true;
 }
 }
 
 
 FuzzerWorker* fuzzer_worker_alloc() {
 FuzzerWorker* fuzzer_worker_alloc() {
     FuzzerWorker* instance = malloc(sizeof(FuzzerWorker));
     FuzzerWorker* instance = malloc(sizeof(FuzzerWorker));
 
 
-#if defined(RFID_125_PROTOCOL)
-    instance->protocols_items = protocol_dict_alloc(lfrfid_protocols, LFRFIDProtocolMax);
+    instance->hw_worker = hardware_worker_alloc();
+    hardware_worker_start_thread(instance->hw_worker);
+
+    instance->suported_proto = malloc(sizeof(HwProtocolID) * TOTAL_PROTOCOL_COUNT);
 
 
-    instance->proto_worker = lfrfid_worker_alloc(instance->protocols_items);
-#else
-    instance->protocols_items = ibutton_protocols_alloc();
-    instance->key =
-        ibutton_key_alloc(ibutton_protocols_get_max_data_size(instance->protocols_items));
+    for(uint8_t i = 0; i < TOTAL_PROTOCOL_COUNT; i++) {
+        if(!hardware_worker_set_protocol_id_by_name(
+               instance->hw_worker, fuzzer_proto_items[i].name)) {
+            // Check protocol support
+            FURI_LOG_E(TAG, "Not supported protocol name: %s", fuzzer_proto_items[i].name);
+            furi_crash("Not supported protocol name");
+        } else {
+            instance->suported_proto[i] = hardware_worker_get_protocol_id(instance->hw_worker);
+            FURI_LOG_D(
+                TAG,
+                "%u: %15s Protocol_id: %lu",
+                i + 1,
+                fuzzer_proto_items[i].name,
+                instance->suported_proto[i]);
+        }
+    }
 
 
-    instance->proto_worker = ibutton_worker_alloc(instance->protocols_items);
-#endif
     instance->attack_type = FuzzerWorkerAttackTypeMax;
     instance->attack_type = FuzzerWorkerAttackTypeMax;
     instance->index = 0;
     instance->index = 0;
-    instance->treead_running = false;
     instance->in_emu_phase = false;
     instance->in_emu_phase = false;
 
 
     memset(instance->payload, 0x00, sizeof(instance->payload));
     memset(instance->payload, 0x00, sizeof(instance->payload));
@@ -364,16 +421,10 @@ void fuzzer_worker_free(FuzzerWorker* instance) {
 
 
     furi_timer_free(instance->timer);
     furi_timer_free(instance->timer);
 
 
-#if defined(RFID_125_PROTOCOL)
-    lfrfid_worker_free(instance->proto_worker);
+    free(instance->suported_proto);
 
 
-    protocol_dict_free(instance->protocols_items);
-#else
-    ibutton_worker_free(instance->proto_worker);
-
-    ibutton_key_free(instance->key);
-    ibutton_protocols_free(instance->protocols_items);
-#endif
+    hardware_worker_stop_thread(instance->hw_worker);
+    hardware_worker_free(instance->hw_worker);
 
 
     free(instance);
     free(instance);
 }
 }
@@ -399,25 +450,8 @@ bool fuzzer_worker_start(FuzzerWorker* instance, uint8_t idle_time, uint8_t emu_
             instance->timer_emu_time_ms,
             instance->timer_emu_time_ms,
             instance->timer_idle_time_ms);
             instance->timer_idle_time_ms);
 
 
-        if(!instance->treead_running) {
-#if defined(RFID_125_PROTOCOL)
-            lfrfid_worker_start_thread(instance->proto_worker);
-#else
-            ibutton_worker_start_thread(instance->proto_worker);
-#endif
-            FURI_LOG_D(TAG, "Worker Starting");
-            instance->treead_running = true;
-        } else {
-            FURI_LOG_D(TAG, "Worker UnPaused");
-        }
+        hardware_worker_emulate_start(instance->hw_worker);
 
 
-#if defined(RFID_125_PROTOCOL)
-        // lfrfid_worker_start_thread(instance->proto_worker);
-        lfrfid_worker_emulate_start(instance->proto_worker, instance->protocol_id);
-#else
-        // ibutton_worker_start_thread(instance->proto_worker);
-        ibutton_worker_emulate_start(instance->proto_worker, instance->key);
-#endif
         instance->in_emu_phase = true;
         instance->in_emu_phase = true;
         furi_timer_start(instance->timer, furi_ms_to_ticks(instance->timer_emu_time_ms));
         furi_timer_start(instance->timer, furi_ms_to_ticks(instance->timer_emu_time_ms));
         return true;
         return true;
@@ -425,19 +459,18 @@ bool fuzzer_worker_start(FuzzerWorker* instance, uint8_t idle_time, uint8_t emu_
     return false;
     return false;
 }
 }
 
 
+void fuzzer_worker_start_emulate(FuzzerWorker* instance) {
+    furi_assert(instance);
+
+    hardware_worker_emulate_start(instance->hw_worker);
+}
+
 void fuzzer_worker_pause(FuzzerWorker* instance) {
 void fuzzer_worker_pause(FuzzerWorker* instance) {
     furi_assert(instance);
     furi_assert(instance);
 
 
     furi_timer_stop(instance->timer);
     furi_timer_stop(instance->timer);
 
 
-    if(instance->treead_running) {
-#if defined(RFID_125_PROTOCOL)
-        lfrfid_worker_stop(instance->proto_worker);
-#else
-        ibutton_worker_stop(instance->proto_worker);
-#endif
-        FURI_LOG_D(TAG, "Worker Paused");
-    }
+    hardware_worker_stop(instance->hw_worker);
 }
 }
 
 
 void fuzzer_worker_stop(FuzzerWorker* instance) {
 void fuzzer_worker_stop(FuzzerWorker* instance) {
@@ -445,25 +478,15 @@ void fuzzer_worker_stop(FuzzerWorker* instance) {
 
 
     furi_timer_stop(instance->timer);
     furi_timer_stop(instance->timer);
 
 
-    if(instance->treead_running) {
-#if defined(RFID_125_PROTOCOL)
-        lfrfid_worker_stop(instance->proto_worker);
-        lfrfid_worker_stop_thread(instance->proto_worker);
-#else
-        ibutton_worker_stop(instance->proto_worker);
-        ibutton_worker_stop_thread(instance->proto_worker);
-#endif
-        FURI_LOG_D(TAG, "Worker Stopping");
-        instance->treead_running = false;
-    }
+    hardware_worker_stop(instance->hw_worker);
 
 
     if(instance->attack_type == FuzzerWorkerAttackTypeLoadFileCustomUids) {
     if(instance->attack_type == FuzzerWorkerAttackTypeLoadFileCustomUids) {
         buffered_file_stream_close(instance->uids_stream);
         buffered_file_stream_close(instance->uids_stream);
-        furi_record_close(RECORD_STORAGE);
+        stream_free(instance->uids_stream);
         instance->attack_type = FuzzerWorkerAttackTypeMax;
         instance->attack_type = FuzzerWorkerAttackTypeMax;
     }
     }
 
 
-    // TODO  anything else
+    // TODO anything else
 }
 }
 
 
 void fuzzer_worker_set_uid_chaged_callback(
 void fuzzer_worker_set_uid_chaged_callback(

+ 17 - 2
lib/worker/fake_worker.h

@@ -12,6 +12,14 @@ typedef enum {
     FuzzerWorkerAttackTypeMax,
     FuzzerWorkerAttackTypeMax,
 } FuzzerWorkerAttackType;
 } FuzzerWorkerAttackType;
 
 
+typedef enum {
+    FuzzerWorkerLoadKeyStateBadFile = -2,
+    FuzzerWorkerLoadKeyStateUnsuportedProto,
+    FuzzerWorkerLoadKeyStateOk = 0,
+    FuzzerWorkerLoadKeyStateDifferentProto,
+
+} FuzzerWorkerLoadKeyState;
+
 typedef void (*FuzzerWorkerUidChagedCallback)(void* context);
 typedef void (*FuzzerWorkerUidChagedCallback)(void* context);
 typedef void (*FuzzerWorkerEndCallback)(void* context);
 typedef void (*FuzzerWorkerEndCallback)(void* context);
 
 
@@ -48,6 +56,8 @@ bool fuzzer_worker_start(FuzzerWorker* instance, uint8_t idle_time, uint8_t emu_
  */
  */
 void fuzzer_worker_stop(FuzzerWorker* instance);
 void fuzzer_worker_stop(FuzzerWorker* instance);
 
 
+void fuzzer_worker_start_emulate(FuzzerWorker* instance);
+
 /**
 /**
  * Suspend emulation
  * Suspend emulation
  * 
  * 
@@ -100,6 +110,9 @@ bool fuzzer_worker_init_attack_bf_byte(
  */
  */
 void fuzzer_worker_get_current_key(FuzzerWorker* instance, FuzzerPayload* output_key);
 void fuzzer_worker_get_current_key(FuzzerWorker* instance, FuzzerPayload* output_key);
 
 
+bool fuzzer_worker_next_key(FuzzerWorker* instance);
+bool fuzzer_worker_previous_key(FuzzerWorker* instance);
+
 /**
 /**
  * Load UID from Flipper Format Key file
  * Load UID from Flipper Format Key file
  * 
  * 
@@ -108,11 +121,13 @@ void fuzzer_worker_get_current_key(FuzzerWorker* instance, FuzzerPayload* output
  * @param filename file path to the key file
  * @param filename file path to the key file
  * @return bool True if loading is successful
  * @return bool True if loading is successful
  */
  */
-bool fuzzer_worker_load_key_from_file(
+FuzzerWorkerLoadKeyState fuzzer_worker_load_key_from_file(
     FuzzerWorker* instance,
     FuzzerWorker* instance,
-    FuzzerProtocolsID protocol_index,
+    FuzzerProtocolsID* protocol_index,
     const char* filename);
     const char* filename);
 
 
+bool fuzzer_worker_save_key(FuzzerWorker* instance, const char* path);
+
 /**
 /**
  * Set callback for uid changed
  * Set callback for uid changed
  * 
  * 

+ 198 - 0
lib/worker/helpers/hardware_worker.c

@@ -0,0 +1,198 @@
+#include "hardware_worker.h"
+#include "furi.h"
+
+#if defined(RFID_125_PROTOCOL)
+
+#include <lib/lfrfid/lfrfid_dict_file.h>
+#include <lib/lfrfid/lfrfid_worker.h>
+
+#else
+
+#include <lib/ibutton/ibutton_worker.h>
+#include <lib/ibutton/ibutton_key.h>
+
+#endif
+
+#define TAG "Fuzzer HW worker"
+
+struct HardwareWorker {
+#if defined(RFID_125_PROTOCOL)
+    LFRFIDWorker* proto_worker;
+    ProtocolId protocol_id;
+    ProtocolDict* protocols_items;
+#else
+    iButtonWorker* proto_worker;
+    iButtonProtocolId protocol_id;
+    iButtonProtocols* protocols_items;
+    iButtonKey* key;
+#endif
+};
+
+HardwareWorker* hardware_worker_alloc() {
+    HardwareWorker* instance = malloc(sizeof(HardwareWorker));
+#if defined(RFID_125_PROTOCOL)
+    instance->protocols_items = protocol_dict_alloc(lfrfid_protocols, LFRFIDProtocolMax);
+
+    instance->proto_worker = lfrfid_worker_alloc(instance->protocols_items);
+#else
+    instance->protocols_items = ibutton_protocols_alloc();
+    instance->key =
+        ibutton_key_alloc(ibutton_protocols_get_max_data_size(instance->protocols_items));
+
+    instance->proto_worker = ibutton_worker_alloc(instance->protocols_items);
+#endif
+    return instance;
+}
+
+void hardware_worker_free(HardwareWorker* instance) {
+#if defined(RFID_125_PROTOCOL)
+    lfrfid_worker_free(instance->proto_worker);
+
+    protocol_dict_free(instance->protocols_items);
+#else
+    ibutton_worker_free(instance->proto_worker);
+
+    ibutton_key_free(instance->key);
+    ibutton_protocols_free(instance->protocols_items);
+#endif
+    free(instance);
+}
+
+void hardware_worker_start_thread(HardwareWorker* instance) {
+#if defined(RFID_125_PROTOCOL)
+    lfrfid_worker_start_thread(instance->proto_worker);
+#else
+    ibutton_worker_start_thread(instance->proto_worker);
+#endif
+}
+
+void hardware_worker_stop_thread(HardwareWorker* instance) {
+#if defined(RFID_125_PROTOCOL)
+    lfrfid_worker_stop(instance->proto_worker);
+    lfrfid_worker_stop_thread(instance->proto_worker);
+#else
+    ibutton_worker_stop(instance->proto_worker);
+    ibutton_worker_stop_thread(instance->proto_worker);
+#endif
+}
+
+void hardware_worker_emulate_start(HardwareWorker* instance) {
+#if defined(RFID_125_PROTOCOL)
+    lfrfid_worker_emulate_start(instance->proto_worker, instance->protocol_id);
+#else
+    ibutton_worker_emulate_start(instance->proto_worker, instance->key);
+#endif
+}
+
+void hardware_worker_stop(HardwareWorker* instance) {
+#if defined(RFID_125_PROTOCOL)
+    lfrfid_worker_stop(instance->proto_worker);
+#else
+    ibutton_worker_stop(instance->proto_worker);
+#endif
+}
+
+void hardware_worker_set_protocol_data(
+    HardwareWorker* instance,
+    uint8_t* payload,
+    uint8_t payload_size) {
+#if defined(RFID_125_PROTOCOL)
+    protocol_dict_set_data(
+        instance->protocols_items, instance->protocol_id, payload, payload_size);
+#else
+    ibutton_key_set_protocol_id(instance->key, instance->protocol_id);
+    iButtonEditableData data;
+    ibutton_protocols_get_editable_data(instance->protocols_items, instance->key, &data);
+
+    furi_check(payload_size >= data.size);
+    memcpy(data.ptr, payload, data.size);
+#endif
+}
+
+void hardware_worker_get_protocol_data(
+    HardwareWorker* instance,
+    uint8_t* payload,
+    uint8_t payload_size) {
+#if defined(RFID_125_PROTOCOL)
+    protocol_dict_get_data(
+        instance->protocols_items, instance->protocol_id, payload, payload_size);
+#else
+    iButtonEditableData data;
+    ibutton_protocols_get_editable_data(instance->protocols_items, instance->key, &data);
+    furi_check(payload_size >= data.size);
+    memcpy(payload, data.ptr, data.size);
+#endif
+}
+
+static bool hardware_worker_protocol_is_valid(HardwareWorker* instance) {
+#if defined(RFID_125_PROTOCOL)
+    if(instance->protocol_id != PROTOCOL_NO) {
+        return true;
+    }
+#else
+    if(instance->protocol_id != iButtonProtocolIdInvalid) {
+        return true;
+    }
+#endif
+    return false;
+}
+
+bool hardware_worker_set_protocol_id_by_name(HardwareWorker* instance, const char* protocol_name) {
+#if defined(RFID_125_PROTOCOL)
+    instance->protocol_id =
+        protocol_dict_get_protocol_by_name(instance->protocols_items, protocol_name);
+    return (instance->protocol_id != PROTOCOL_NO);
+#else
+    instance->protocol_id =
+        ibutton_protocols_get_id_by_name(instance->protocols_items, protocol_name);
+    return (instance->protocol_id != iButtonProtocolIdInvalid);
+#endif
+}
+
+HwProtocolID hardware_worker_get_protocol_id(HardwareWorker* instance) {
+    if(hardware_worker_protocol_is_valid(instance)) {
+        return instance->protocol_id;
+    }
+    return -1;
+}
+
+bool hardware_worker_load_key_from_file(HardwareWorker* instance, const char* filename) {
+    bool res = false;
+
+#if defined(RFID_125_PROTOCOL)
+    ProtocolId loaded_proto_id = lfrfid_dict_file_load(instance->protocols_items, filename);
+    if(loaded_proto_id == PROTOCOL_NO) {
+        // Err Cant load file
+        FURI_LOG_W(TAG, "Cant load file");
+    } else {
+        instance->protocol_id = loaded_proto_id;
+        res = true;
+    }
+#else
+
+    if(!ibutton_protocols_load(instance->protocols_items, instance->key, filename)) {
+        // Err Cant load file
+        FURI_LOG_W(TAG, "Cant load file");
+    } else {
+        instance->protocol_id = ibutton_key_get_protocol_id(instance->key);
+        res = true;
+    }
+
+#endif
+
+    return res;
+}
+
+bool hardware_worker_save_key(HardwareWorker* instance, const char* path) {
+    furi_assert(instance);
+    bool res;
+
+#if defined(RFID_125_PROTOCOL)
+    res = lfrfid_dict_file_save(instance->protocols_items, instance->protocol_id, path);
+#else
+
+    res = ibutton_protocols_save(instance->protocols_items, instance->key, path);
+#endif
+
+    return res;
+}

+ 48 - 0
lib/worker/helpers/hardware_worker.h

@@ -0,0 +1,48 @@
+#pragma once
+
+#include <stdint.h>
+#include <stdbool.h>
+
+#if defined(RFID_125_PROTOCOL)
+
+#include <lib/lfrfid/lfrfid_dict_file.h>
+typedef ProtocolId HwProtocolID;
+
+#else
+
+#include <lib/ibutton/protocols/protocol_common.h>
+typedef iButtonProtocolId HwProtocolID;
+
+#endif
+
+typedef struct HardwareWorker HardwareWorker;
+
+HardwareWorker* hardware_worker_alloc();
+
+void hardware_worker_free(HardwareWorker* instance);
+
+void hardware_worker_start_thread(HardwareWorker* instance);
+
+void hardware_worker_stop_thread(HardwareWorker* instance);
+
+void hardware_worker_emulate_start(HardwareWorker* instance);
+
+void hardware_worker_stop(HardwareWorker* instance);
+
+void hardware_worker_set_protocol_data(
+    HardwareWorker* instance,
+    uint8_t* payload,
+    uint8_t payload_size);
+
+void hardware_worker_get_protocol_data(
+    HardwareWorker* instance,
+    uint8_t* payload,
+    uint8_t payload_size);
+
+bool hardware_worker_set_protocol_id_by_name(HardwareWorker* instance, const char* protocol_name);
+
+HwProtocolID hardware_worker_get_protocol_id(HardwareWorker* instance);
+
+bool hardware_worker_load_key_from_file(HardwareWorker* instance, const char* filename);
+
+bool hardware_worker_save_key(HardwareWorker* instance, const char* path);

+ 70 - 70
lib/worker/protocol.c

@@ -152,79 +152,79 @@ const uint8_t uid_list_h10301[][H10301_DATA_SIZE] = {
 
 
 #if defined(RFID_125_PROTOCOL)
 #if defined(RFID_125_PROTOCOL)
 const FuzzerProtocol fuzzer_proto_items[] = {
 const FuzzerProtocol fuzzer_proto_items[] = {
-    [EM4100] =
-        {
-            .name = "EM4100",
-            .data_size = EM4100_DATA_SIZE,
-            .dict =
-                {
-                    .val = (const uint8_t*)&uid_list_em4100,
-                    .len = COUNT_OF(uid_list_em4100),
-                },
-        },
-    [HIDProx] =
-        {
-            .name = "HIDProx",
-            .data_size = HIDProx_DATA_SIZE,
-            .dict =
-                {
-                    .val = (const uint8_t*)&uid_list_hid,
-                    .len = COUNT_OF(uid_list_hid),
-                },
-        },
-    [PAC] =
-        {
-            .name = "PAC/Stanley",
-            .data_size = PAC_DATA_SIZE,
-            .dict =
-                {
-                    .val = (const uint8_t*)&uid_list_pac,
-                    .len = COUNT_OF(uid_list_pac),
-                },
-        },
-    [H10301] =
-        {
-            .name = "H10301",
-            .data_size = H10301_DATA_SIZE,
-            .dict =
-                {
-                    .val = (const uint8_t*)&uid_list_h10301,
-                    .len = COUNT_OF(uid_list_h10301),
-                },
-        },
+    // EM4100
+    {
+        .name = "EM4100",
+        .data_size = EM4100_DATA_SIZE,
+        .dict =
+            {
+                .val = (const uint8_t*)&uid_list_em4100,
+                .len = COUNT_OF(uid_list_em4100),
+            },
+    },
+    // HIDProx
+    {
+        .name = "HIDProx",
+        .data_size = HIDProx_DATA_SIZE,
+        .dict =
+            {
+                .val = (const uint8_t*)&uid_list_hid,
+                .len = COUNT_OF(uid_list_hid),
+            },
+    },
+    // PAC
+    {
+        .name = "PAC/Stanley",
+        .data_size = PAC_DATA_SIZE,
+        .dict =
+            {
+                .val = (const uint8_t*)&uid_list_pac,
+                .len = COUNT_OF(uid_list_pac),
+            },
+    },
+    // H10301
+    {
+        .name = "H10301",
+        .data_size = H10301_DATA_SIZE,
+        .dict =
+            {
+                .val = (const uint8_t*)&uid_list_h10301,
+                .len = COUNT_OF(uid_list_h10301),
+            },
+    },
 };
 };
 #else
 #else
 const FuzzerProtocol fuzzer_proto_items[] = {
 const FuzzerProtocol fuzzer_proto_items[] = {
-    [DS1990] =
-        {
-            .name = "DS1990",
-            .data_size = DS1990_DATA_SIZE,
-            .dict =
-                {
-                    .val = (const uint8_t*)&uid_list_ds1990,
-                    .len = COUNT_OF(uid_list_ds1990),
-                },
-        },
-    [Metakom] =
-        {
-            .name = "Metakom",
-            .data_size = Metakom_DATA_SIZE,
-            .dict =
-                {
-                    .val = (const uint8_t*)&uid_list_metakom,
-                    .len = COUNT_OF(uid_list_metakom),
-                },
-        },
-    [Cyfral] =
-        {
-            .name = "Cyfral",
-            .data_size = Cyfral_DATA_SIZE,
-            .dict =
-                {
-                    .val = (const uint8_t*)&uid_list_cyfral,
-                    .len = COUNT_OF(uid_list_cyfral),
-                },
-        },
+    // DS1990
+    {
+        .name = "DS1990",
+        .data_size = DS1990_DATA_SIZE,
+        .dict =
+            {
+                .val = (const uint8_t*)&uid_list_ds1990,
+                .len = COUNT_OF(uid_list_ds1990),
+            },
+    },
+    // Metakom
+    {
+        .name = "Metakom",
+        .data_size = Metakom_DATA_SIZE,
+        .dict =
+            {
+                .val = (const uint8_t*)&uid_list_metakom,
+                .len = COUNT_OF(uid_list_metakom),
+            },
+    },
+    // Cyfral
+    {
+        .name = "Cyfral",
+        .data_size = Cyfral_DATA_SIZE,
+        .dict =
+            {
+                .val = (const uint8_t*)&uid_list_cyfral,
+                .len = COUNT_OF(uid_list_cyfral),
+            },
+    },
 };
 };
 #endif
 #endif
 
 

+ 10 - 13
lib/worker/protocol.h

@@ -6,18 +6,7 @@
 
 
 typedef struct FuzzerPayload FuzzerPayload;
 typedef struct FuzzerPayload FuzzerPayload;
 
 
-typedef enum {
-#if defined(RFID_125_PROTOCOL)
-    EM4100,
-    HIDProx,
-    PAC,
-    H10301,
-#else
-    DS1990,
-    Metakom,
-    Cyfral,
-#endif
-} FuzzerProtocolsID;
+typedef uint8_t FuzzerProtocolsID;
 
 
 typedef enum {
 typedef enum {
     FuzzerAttackIdDefaultValues = 0,
     FuzzerAttackIdDefaultValues = 0,
@@ -51,8 +40,16 @@ void fuzzer_payload_free(FuzzerPayload*);
  */
  */
 uint8_t fuzzer_proto_get_max_data_size();
 uint8_t fuzzer_proto_get_max_data_size();
 
 
-// TODO add description
+/**
+ * Get recomended/default emulation time
+ * @return Default emulation time
+ */
 uint8_t fuzzer_proto_get_def_emu_time();
 uint8_t fuzzer_proto_get_def_emu_time();
+
+/**
+ * Get recomended/default idle time
+ * @return Default idle time
+ */
 uint8_t fuzzer_proto_get_def_idle_time();
 uint8_t fuzzer_proto_get_def_idle_time();
 
 
 /**
 /**

+ 8 - 0
lib/worker/protocol_i.h

@@ -7,11 +7,19 @@
 #define PROTOCOL_DEF_IDLE_TIME (4)
 #define PROTOCOL_DEF_IDLE_TIME (4)
 #define PROTOCOL_DEF_EMU_TIME (5)
 #define PROTOCOL_DEF_EMU_TIME (5)
 #define PROTOCOL_TIME_DELAY_MIN PROTOCOL_DEF_IDLE_TIME + PROTOCOL_DEF_EMU_TIME
 #define PROTOCOL_TIME_DELAY_MIN PROTOCOL_DEF_IDLE_TIME + PROTOCOL_DEF_EMU_TIME
+
+#define PROTOCOL_KEY_FOLDER_NAME "lfrfid"
+#define PROTOCOL_KEY_EXTENSION ".rfid"
+
 #else
 #else
+
 #define MAX_PAYLOAD_SIZE (8)
 #define MAX_PAYLOAD_SIZE (8)
 #define PROTOCOL_DEF_IDLE_TIME (2)
 #define PROTOCOL_DEF_IDLE_TIME (2)
 #define PROTOCOL_DEF_EMU_TIME (2)
 #define PROTOCOL_DEF_EMU_TIME (2)
 #define PROTOCOL_TIME_DELAY_MIN PROTOCOL_DEF_IDLE_TIME + PROTOCOL_DEF_EMU_TIME
 #define PROTOCOL_TIME_DELAY_MIN PROTOCOL_DEF_IDLE_TIME + PROTOCOL_DEF_EMU_TIME
+
+#define PROTOCOL_KEY_FOLDER_NAME "ibutton"
+#define PROTOCOL_KEY_EXTENSION ".ibtn"
 #endif
 #endif
 
 
 typedef struct ProtoDict ProtoDict;
 typedef struct ProtoDict ProtoDict;

+ 55 - 47
scenes/fuzzer_scene_attack.c

@@ -1,7 +1,7 @@
 #include "../fuzzer_i.h"
 #include "../fuzzer_i.h"
 #include "../helpers/fuzzer_custom_event.h"
 #include "../helpers/fuzzer_custom_event.h"
 
 
-const NotificationSequence sequence_one_green_50_on_blink_blue = {
+const NotificationSequence sequence_one_red_50_on_blink_blue = {
     &message_red_255,
     &message_red_255,
     &message_delay_50,
     &message_delay_50,
     &message_red_0,
     &message_red_0,
@@ -28,35 +28,38 @@ static void fuzzer_scene_attack_set_state(PacsFuzzerApp* app, FuzzerAttackState
     switch(state) {
     switch(state) {
     case FuzzerAttackStateIdle:
     case FuzzerAttackStateIdle:
         notification_message(app->notifications, &sequence_blink_stop);
         notification_message(app->notifications, &sequence_blink_stop);
-        fuzzer_view_attack_pause(app->attack_view);
         break;
         break;
 
 
-    case FuzzerAttackStateRunning:
+    case FuzzerAttackStateAttacking:
+        notification_message(app->notifications, &sequence_blink_start_blue);
+        break;
+    case FuzzerAttackStateEmulating:
         notification_message(app->notifications, &sequence_blink_start_blue);
         notification_message(app->notifications, &sequence_blink_start_blue);
-        fuzzer_view_attack_start(app->attack_view);
         break;
         break;
 
 
     case FuzzerAttackStateEnd:
     case FuzzerAttackStateEnd:
         notification_message(app->notifications, &sequence_blink_stop);
         notification_message(app->notifications, &sequence_blink_stop);
         notification_message(app->notifications, &sequence_single_vibro);
         notification_message(app->notifications, &sequence_single_vibro);
-        fuzzer_view_attack_end(app->attack_view);
         break;
         break;
 
 
     case FuzzerAttackStateOff:
     case FuzzerAttackStateOff:
         notification_message(app->notifications, &sequence_blink_stop);
         notification_message(app->notifications, &sequence_blink_stop);
-        fuzzer_view_attack_stop(app->attack_view);
+        break;
+
+    case FuzzerAttackStatePause:
+        notification_message(app->notifications, &sequence_blink_stop);
         break;
         break;
     }
     }
+
+    fuzzer_view_update_state(app->attack_view, state);
 }
 }
 
 
 void fuzzer_scene_attack_worker_tick_callback(void* context) {
 void fuzzer_scene_attack_worker_tick_callback(void* context) {
     furi_assert(context);
     furi_assert(context);
     PacsFuzzerApp* app = context;
     PacsFuzzerApp* app = context;
 
 
-    notification_message(app->notifications, &sequence_one_green_50_on_blink_blue);
+    notification_message(app->notifications, &sequence_one_red_50_on_blink_blue);
     fuzzer_scene_attack_update_uid(app);
     fuzzer_scene_attack_update_uid(app);
-
-    // view_dispatcher_send_custom_event(app->view_dispatcher, FuzzerCustomEventViewAttackTick);
 }
 }
 
 
 void fuzzer_scene_attack_worker_end_callback(void* context) {
 void fuzzer_scene_attack_worker_end_callback(void* context) {
@@ -100,48 +103,53 @@ bool fuzzer_scene_attack_on_event(void* context, SceneManagerEvent event) {
     bool consumed = false;
     bool consumed = false;
 
 
     if(event.type == SceneManagerEventTypeCustom) {
     if(event.type == SceneManagerEventTypeCustom) {
-        if(event.event == FuzzerCustomEventViewAttackBack) {
-            if(scene_manager_get_scene_state(app->scene_manager, FuzzerSceneAttack) ==
-               FuzzerAttackStateRunning) {
-                // Pause if attack running
-                fuzzer_worker_pause(app->worker);
+        if(event.event == FuzzerCustomEventViewAttackExit) {
+            // Exit
+            fuzzer_worker_stop(app->worker);
+
+            fuzzer_scene_attack_set_state(app, FuzzerAttackStateOff);
+            if(!scene_manager_previous_scene(app->scene_manager)) {
+                scene_manager_stop(app->scene_manager);
+                view_dispatcher_stop(app->view_dispatcher);
+            }
+        } else if(event.event == FuzzerCustomEventViewAttackRunAttack) {
+            if(fuzzer_worker_start(
+                   app->worker,
+                   fuzzer_view_attack_get_time_delay(app->attack_view),
+                   fuzzer_view_attack_get_emu_time(app->attack_view))) {
+                fuzzer_scene_attack_set_state(app, FuzzerAttackStateAttacking);
+            } else {
+                // Error?
+            }
+        } else if(event.event == FuzzerCustomEventViewAttackEmulateCurrent) {
+            fuzzer_worker_start_emulate(app->worker);
+
+            fuzzer_scene_attack_set_state(app, FuzzerAttackStateEmulating);
+        } else if(event.event == FuzzerCustomEventViewAttackPause) {
+            fuzzer_worker_pause(app->worker);
+
+            fuzzer_scene_attack_set_state(app, FuzzerAttackStatePause);
+        } else if(event.event == FuzzerCustomEventViewAttackIdle) {
+            fuzzer_worker_pause(app->worker);
 
 
-                fuzzer_scene_attack_set_state(app, FuzzerAttackStateIdle);
+            fuzzer_scene_attack_set_state(app, FuzzerAttackStateIdle);
+        } else if(event.event == FuzzerCustomEventViewAttackNextUid) {
+            if(fuzzer_worker_next_key(app->worker)) {
+                fuzzer_scene_attack_update_uid(app);
             } else {
             } else {
-                // Exit
-                fuzzer_worker_stop(app->worker);
-
-                fuzzer_scene_attack_set_state(app, FuzzerAttackStateOff);
-                if(!scene_manager_previous_scene(app->scene_manager)) {
-                    scene_manager_stop(app->scene_manager);
-                    view_dispatcher_stop(app->view_dispatcher);
-                }
+                notification_message(app->notifications, &sequence_blink_red_100);
             }
             }
-            consumed = true;
-        } else if(event.event == FuzzerCustomEventViewAttackOk) {
-            if(scene_manager_get_scene_state(app->scene_manager, FuzzerSceneAttack) ==
-               FuzzerAttackStateIdle) {
-                // Start or Continue Attack
-                if(fuzzer_worker_start(
-                       app->worker,
-                       fuzzer_view_attack_get_time_delay(app->attack_view),
-                       fuzzer_view_attack_get_emu_time(app->attack_view))) {
-                    fuzzer_scene_attack_set_state(app, FuzzerAttackStateRunning);
-                } else {
-                    // Error?
-                }
-            } else if(
-                scene_manager_get_scene_state(app->scene_manager, FuzzerSceneAttack) ==
-                FuzzerAttackStateRunning) {
-                // Pause if attack running
-                fuzzer_worker_pause(app->worker);
-
-                fuzzer_scene_attack_set_state(app, FuzzerAttackStateIdle);
+        } else if(event.event == FuzzerCustomEventViewAttackPrevUid) {
+            if(fuzzer_worker_previous_key(app->worker)) {
+                fuzzer_scene_attack_update_uid(app);
+            } else {
+                notification_message(app->notifications, &sequence_blink_red_100);
             }
             }
-            consumed = true;
-            // } else if(event.event == FuzzerCustomEventViewAttackTick) {
-            //     consumed = true;
-        } else if(event.event == FuzzerCustomEventViewAttackEnd) {
+        } else if(event.event == FuzzerCustomEventViewAttackSave) {
+            scene_manager_next_scene(app->scene_manager, FuzzerSceneSaveName);
+        }
+        // Callback from worker
+        else if(event.event == FuzzerCustomEventViewAttackEnd) {
             fuzzer_scene_attack_set_state(app, FuzzerAttackStateEnd);
             fuzzer_scene_attack_set_state(app, FuzzerAttackStateEnd);
             consumed = true;
             consumed = true;
         }
         }

+ 3 - 1
scenes/fuzzer_scene_config.h

@@ -1,3 +1,5 @@
 ADD_SCENE(fuzzer, main, Main)
 ADD_SCENE(fuzzer, main, Main)
 ADD_SCENE(fuzzer, attack, Attack)
 ADD_SCENE(fuzzer, attack, Attack)
-ADD_SCENE(fuzzer, field_editor, FieldEditor)
+ADD_SCENE(fuzzer, field_editor, FieldEditor)
+ADD_SCENE(fuzzer, save_name, SaveName)
+ADD_SCENE(fuzzer, save_success, SaveSuccess)

+ 1 - 0
scenes/fuzzer_scene_field_editor.c

@@ -22,6 +22,7 @@ void fuzzer_scene_field_editor_on_enter(void* context) {
         break;
         break;
 
 
     case FuzzerFieldEditorStateEditingOff:
     case FuzzerFieldEditorStateEditingOff:
+        memset(app->payload->data, 0x00, app->payload->data_size);
         fuzzer_view_field_editor_reset_data(app->field_editor_view, app->payload, false);
         fuzzer_view_field_editor_reset_data(app->field_editor_view, app->payload, false);
         break;
         break;
 
 

+ 21 - 10
scenes/fuzzer_scene_main.c

@@ -2,6 +2,7 @@
 #include "../helpers/fuzzer_custom_event.h"
 #include "../helpers/fuzzer_custom_event.h"
 
 
 #include "../lib/worker/protocol.h"
 #include "../lib/worker/protocol.h"
+#define TAG "Fuzzer main menu"
 
 
 void fuzzer_scene_main_callback(FuzzerCustomEvent event, void* context) {
 void fuzzer_scene_main_callback(FuzzerCustomEvent event, void* context) {
     furi_assert(context);
     furi_assert(context);
@@ -35,7 +36,7 @@ static bool fuzzer_scene_main_load_custom_dict(void* context) {
     return res;
     return res;
 }
 }
 
 
-static bool fuzzer_scene_main_load_key(void* context) {
+static bool fuzzer_scene_main_load_key_dialog(void* context) {
     furi_assert(context);
     furi_assert(context);
     PacsFuzzerApp* app = context;
     PacsFuzzerApp* app = context;
 
 
@@ -134,22 +135,32 @@ bool fuzzer_scene_main_on_event(void* context, SceneManagerEvent event) {
                 break;
                 break;
 
 
             case FuzzerAttackIdLoadFile:
             case FuzzerAttackIdLoadFile:
-                if(!fuzzer_scene_main_load_key(app)) {
+                if(!fuzzer_scene_main_load_key_dialog(app)) {
                     break;
                     break;
                 } else {
                 } else {
-                    if(fuzzer_worker_load_key_from_file(
-                           app->worker,
-                           app->fuzzer_state.proto_index,
-                           furi_string_get_cstr(app->file_path))) {
+                    switch(fuzzer_worker_load_key_from_file(
+                        app->worker,
+                        &app->fuzzer_state.proto_index,
+                        furi_string_get_cstr(app->file_path))) {
+                    case FuzzerWorkerLoadKeyStateOk:
+                    case FuzzerWorkerLoadKeyStateDifferentProto:
                         scene_manager_set_scene_state(
                         scene_manager_set_scene_state(
                             app->scene_manager,
                             app->scene_manager,
                             FuzzerSceneFieldEditor,
                             FuzzerSceneFieldEditor,
                             FuzzerFieldEditorStateEditingOn);
                             FuzzerFieldEditorStateEditingOn);
                         scene_manager_next_scene(app->scene_manager, FuzzerSceneFieldEditor);
                         scene_manager_next_scene(app->scene_manager, FuzzerSceneFieldEditor);
-                        FURI_LOG_I("Scene", "Load ok");
-                    } else {
-                        fuzzer_scene_main_show_error(app, "Unsupported protocol\nor broken file");
-                        FURI_LOG_W("Scene", "Load err");
+                        FURI_LOG_I(TAG, "Load ok");
+                        break;
+
+                    case FuzzerWorkerLoadKeyStateBadFile:
+                        fuzzer_scene_main_show_error(app, "Cant load\nor broken file");
+                        FURI_LOG_E(TAG, "Cant load or broken file");
+                        break;
+
+                    case FuzzerWorkerLoadKeyStateUnsuportedProto:
+                        fuzzer_scene_main_show_error(app, "Unsupported protocol");
+                        FURI_LOG_E(TAG, "Unsupported protocol");
+                        break;
                     }
                     }
                 }
                 }
                 break;
                 break;

+ 67 - 0
scenes/fuzzer_scene_save_name.c

@@ -0,0 +1,67 @@
+#include "../fuzzer_i.h"
+
+#include <toolbox/random_name.h>
+#include <toolbox/path.h>
+
+static void fuzzer_scene_save_name_text_input_callback(void* context) {
+    PacsFuzzerApp* app = context;
+    view_dispatcher_send_custom_event(app->view_dispatcher, FuzzerCustomEventTextEditResult);
+}
+
+void fuzzer_scene_save_name_on_enter(void* context) {
+    PacsFuzzerApp* app = context;
+    TextInput* text_input = app->text_input;
+
+    set_random_name(app->key_name, KEY_NAME_SIZE);
+
+    text_input_set_header_text(text_input, "Name the key");
+    text_input_set_result_callback(
+        text_input,
+        fuzzer_scene_save_name_text_input_callback,
+        app,
+        app->key_name,
+        KEY_NAME_SIZE,
+        false);
+
+    ValidatorIsFile* validator_is_file = validator_is_file_alloc_init(
+        app->fuzzer_const->path_key_folder, app->fuzzer_const->key_extension, app->key_name);
+    text_input_set_validator(text_input, validator_is_file_callback, validator_is_file);
+
+    view_dispatcher_switch_to_view(app->view_dispatcher, FuzzerViewIDTextInput);
+}
+
+bool fuzzer_scene_save_name_on_event(void* context, SceneManagerEvent event) {
+    PacsFuzzerApp* app = context;
+    bool consumed = false;
+
+    if(event.type == SceneManagerEventTypeCustom) {
+        if(event.event == FuzzerCustomEventTextEditResult) {
+            consumed = true;
+            furi_string_printf(
+                app->file_path,
+                "%s/%s%s",
+                app->fuzzer_const->path_key_folder,
+                app->key_name,
+                app->fuzzer_const->key_extension);
+
+            if(fuzzer_worker_save_key(app->worker, furi_string_get_cstr(app->file_path))) {
+                scene_manager_next_scene(app->scene_manager, FuzzerSceneSaveSuccess);
+            } else {
+                scene_manager_previous_scene(app->scene_manager);
+            }
+        }
+    }
+
+    return consumed;
+}
+
+void fuzzer_scene_save_name_on_exit(void* context) {
+    PacsFuzzerApp* app = context;
+    TextInput* text_input = app->text_input;
+
+    void* validator_context = text_input_get_validator_callback_context(text_input);
+    text_input_set_validator(text_input, NULL, NULL);
+    validator_is_file_free((ValidatorIsFile*)validator_context);
+
+    text_input_reset(text_input);
+}

+ 44 - 0
scenes/fuzzer_scene_save_success.c

@@ -0,0 +1,44 @@
+#include "../fuzzer_i.h"
+
+static void fuzzer_scene_save_popup_timeout_callback(void* context) {
+    PacsFuzzerApp* app = context;
+    view_dispatcher_send_custom_event(app->view_dispatcher, FuzzerCustomEventPopupClosed);
+}
+
+void fuzzer_scene_save_success_on_enter(void* context) {
+    PacsFuzzerApp* app = context;
+    Popup* popup = app->popup;
+
+    popup_set_icon(popup, 32, 5, &I_DolphinNice_96x59);
+    popup_set_header(popup, "Saved!", 5, 7, AlignLeft, AlignTop);
+    popup_set_context(popup, app);
+    popup_set_callback(popup, fuzzer_scene_save_popup_timeout_callback);
+    popup_set_timeout(popup, 1500);
+    popup_enable_timeout(popup);
+
+    view_dispatcher_switch_to_view(app->view_dispatcher, FuzzerViewIDPopup);
+}
+
+bool fuzzer_scene_save_success_on_event(void* context, SceneManagerEvent event) {
+    PacsFuzzerApp* app = context;
+    bool consumed = false;
+
+    if((event.type == SceneManagerEventTypeBack) ||
+       ((event.type == SceneManagerEventTypeCustom) &&
+        (event.event == FuzzerCustomEventPopupClosed))) {
+        bool result = scene_manager_search_and_switch_to_previous_scene(
+            app->scene_manager, FuzzerSceneAttack);
+        if(!result) {
+            scene_manager_search_and_switch_to_previous_scene(app->scene_manager, FuzzerSceneMain);
+        }
+        consumed = true;
+    }
+
+    return consumed;
+}
+
+void fuzzer_scene_save_success_on_exit(void* context) {
+    PacsFuzzerApp* app = context;
+
+    popup_reset(app->popup);
+}

+ 6 - 6
todo.md

@@ -2,9 +2,9 @@
 
 
 #### Quality of life
 #### Quality of life
 
 
-- [ ] Make the "Load File" independent of the current protocol
+- [x] Make the "Load File" independent of the current protocol
 - [x] Add pause
 - [x] Add pause
-    - [ ] Switching  UIDs if possible
+    - [x] Switching  UIDs if possible
 - [x] Led and sound Notification
 - [x] Led and sound Notification
     - [x] Led
     - [x] Led
     - [x] Vibro
     - [x] Vibro
@@ -18,7 +18,7 @@
 
 
 - [x] Add `BFCustomerID` attack
 - [x] Add `BFCustomerID` attack
     - [x] Add the ability to select index
     - [x] Add the ability to select index
-- [ ] Save key logic
+- [x] Save key logic
 
 
 ## Code Improvement
 ## Code Improvement
 
 
@@ -36,9 +36,9 @@
     - [x] `UID_MAX_SIZE`
     - [x] `UID_MAX_SIZE`
 - [x] Add pause
 - [x] Add pause
     - [x] Fix `Custom dict` attack when ended
     - [x] Fix `Custom dict` attack when ended
-- [ ] Pause V2
-    - [ ] Save logic
-    - [ ] Switching  UIDs if possible
+- [x] Pause V2
+    - [x] Save logic
+    - [x] Switching  UIDs if possible
 - [ ] Worker
 - [ ] Worker
     - [ ] Use `prtocol_id` instead of protocol name
     - [ ] Use `prtocol_id` instead of protocol name
     - [x] this can be simplified `fuzzer_proto_items`
     - [x] this can be simplified `fuzzer_proto_items`

+ 257 - 146
views/attack.c

@@ -6,7 +6,12 @@
 
 
 #define ATTACK_SCENE_MAX_UID_LENGTH 25
 #define ATTACK_SCENE_MAX_UID_LENGTH 25
 #define UID_MAX_DISPLAYED_LEN (8U)
 #define UID_MAX_DISPLAYED_LEN (8U)
-#define LIFT_RIGHT_OFFSET (3)
+#define LEFT_RIGHT_OFFSET (3U)
+
+#define LINE_1_Y (12U)
+#define LINE_2_Y (24U)
+#define LINE_3_Y (36U)
+#define LINE_4_Y (48U)
 
 
 struct FuzzerViewAttack {
 struct FuzzerViewAttack {
     View* view;
     View* view;
@@ -60,44 +65,11 @@ void fuzzer_view_attack_set_uid(FuzzerViewAttack* view, const FuzzerPayload* uid
         true);
         true);
 }
 }
 
 
-void fuzzer_view_attack_start(FuzzerViewAttack* view) {
+void fuzzer_view_update_state(FuzzerViewAttack* view, FuzzerAttackState state) {
     furi_assert(view);
     furi_assert(view);
 
 
     with_view_model(
     with_view_model(
-        view->view,
-        FuzzerViewAttackModel * model,
-        { model->attack_state = FuzzerAttackStateRunning; },
-        true);
-}
-
-void fuzzer_view_attack_stop(FuzzerViewAttack* view) {
-    furi_assert(view);
-
-    with_view_model(
-        view->view,
-        FuzzerViewAttackModel * model,
-        { model->attack_state = FuzzerAttackStateOff; },
-        true);
-}
-
-void fuzzer_view_attack_pause(FuzzerViewAttack* view) {
-    furi_assert(view);
-
-    with_view_model(
-        view->view,
-        FuzzerViewAttackModel * model,
-        { model->attack_state = FuzzerAttackStateIdle; },
-        true);
-}
-
-void fuzzer_view_attack_end(FuzzerViewAttack* view) {
-    furi_assert(view);
-
-    with_view_model(
-        view->view,
-        FuzzerViewAttackModel * model,
-        { model->attack_state = FuzzerAttackStateEnd; },
-        true);
+        view->view, FuzzerViewAttackModel * model, { model->attack_state = state; }, true);
 }
 }
 
 
 void fuzzer_view_attack_set_callback(
 void fuzzer_view_attack_set_callback(
@@ -110,35 +82,31 @@ void fuzzer_view_attack_set_callback(
     view_attack->context = context;
     view_attack->context = context;
 }
 }
 
 
-void fuzzer_view_attack_draw(Canvas* canvas, FuzzerViewAttackModel* model) {
-    char temp_str[50];
-
-    canvas_clear(canvas);
-    canvas_set_color(canvas, ColorBlack);
-
-    canvas_set_font(canvas, FontPrimary);
-    canvas_draw_str_aligned(canvas, 64, 2, AlignCenter, AlignTop, model->attack_name);
-
+static void
+    fuzzer_view_attack_draw_time_delays_line(Canvas* canvas, FuzzerViewAttackModel* model) {
+    char temp_str[25];
     uint16_t crt;
     uint16_t crt;
+    const uint16_t y = LINE_2_Y;
+
     canvas_set_font(canvas, FontPrimary);
     canvas_set_font(canvas, FontPrimary);
 
 
     if(!model->td_emt_cursor) {
     if(!model->td_emt_cursor) {
         canvas_set_font(canvas, FontSecondary);
         canvas_set_font(canvas, FontSecondary);
         snprintf(temp_str, sizeof(temp_str), "Time delay:");
         snprintf(temp_str, sizeof(temp_str), "Time delay:");
-        canvas_draw_str_aligned(canvas, LIFT_RIGHT_OFFSET, 21, AlignLeft, AlignBottom, temp_str);
+        canvas_draw_str_aligned(canvas, LEFT_RIGHT_OFFSET, y, AlignLeft, AlignBottom, temp_str);
         crt = canvas_string_width(canvas, temp_str);
         crt = canvas_string_width(canvas, temp_str);
 
 
         canvas_set_font(canvas, FontPrimary);
         canvas_set_font(canvas, FontPrimary);
         snprintf(
         snprintf(
             temp_str, sizeof(temp_str), "%d.%d", model->time_delay / 10, model->time_delay % 10);
             temp_str, sizeof(temp_str), "%d.%d", model->time_delay / 10, model->time_delay % 10);
         canvas_draw_str_aligned(
         canvas_draw_str_aligned(
-            canvas, crt + LIFT_RIGHT_OFFSET + 3, 21, AlignLeft, AlignBottom, temp_str);
+            canvas, crt + LEFT_RIGHT_OFFSET + 3, y, AlignLeft, AlignBottom, temp_str);
 
 
         canvas_set_font(canvas, FontSecondary);
         canvas_set_font(canvas, FontSecondary);
         snprintf(
         snprintf(
             temp_str, sizeof(temp_str), "EmT: %d.%d", model->emu_time / 10, model->emu_time % 10);
             temp_str, sizeof(temp_str), "EmT: %d.%d", model->emu_time / 10, model->emu_time % 10);
         canvas_draw_str_aligned(
         canvas_draw_str_aligned(
-            canvas, 128 - LIFT_RIGHT_OFFSET, 21, AlignRight, AlignBottom, temp_str);
+            canvas, 128 - LEFT_RIGHT_OFFSET, y, AlignRight, AlignBottom, temp_str);
     } else {
     } else {
         canvas_set_font(canvas, FontSecondary);
         canvas_set_font(canvas, FontSecondary);
         snprintf(
         snprintf(
@@ -148,138 +116,206 @@ void fuzzer_view_attack_draw(Canvas* canvas, FuzzerViewAttackModel* model) {
             model->time_delay / 10,
             model->time_delay / 10,
             model->time_delay % 10);
             model->time_delay % 10);
 
 
-        canvas_draw_str_aligned(canvas, LIFT_RIGHT_OFFSET, 21, AlignLeft, AlignBottom, temp_str);
+        canvas_draw_str_aligned(canvas, LEFT_RIGHT_OFFSET, y, AlignLeft, AlignBottom, temp_str);
 
 
         canvas_set_font(canvas, FontPrimary);
         canvas_set_font(canvas, FontPrimary);
         snprintf(temp_str, sizeof(temp_str), "%d.%d", model->emu_time / 10, model->emu_time % 10);
         snprintf(temp_str, sizeof(temp_str), "%d.%d", model->emu_time / 10, model->emu_time % 10);
         canvas_draw_str_aligned(
         canvas_draw_str_aligned(
-            canvas, 128 - LIFT_RIGHT_OFFSET, 21, AlignRight, AlignBottom, temp_str);
+            canvas, 128 - LEFT_RIGHT_OFFSET, y, AlignRight, AlignBottom, temp_str);
         crt = canvas_string_width(canvas, temp_str);
         crt = canvas_string_width(canvas, temp_str);
 
 
         canvas_set_font(canvas, FontSecondary);
         canvas_set_font(canvas, FontSecondary);
         snprintf(temp_str, sizeof(temp_str), "Emulation time:");
         snprintf(temp_str, sizeof(temp_str), "Emulation time:");
         canvas_draw_str_aligned(
         canvas_draw_str_aligned(
-            canvas, 128 - LIFT_RIGHT_OFFSET - crt - 3, 21, AlignRight, AlignBottom, temp_str);
+            canvas, 128 - LEFT_RIGHT_OFFSET - crt - 3, y, AlignRight, AlignBottom, temp_str);
+    }
+}
+
+static void fuzzer_view_attack_draw_time_delays_str(Canvas* canvas, FuzzerViewAttackModel* model) {
+    char temp_str[20];
+    uint16_t crt;
+    const uint16_t y = LINE_2_Y;
+
+    canvas_set_font(canvas, FontSecondary);
+    snprintf(
+        temp_str,
+        sizeof(temp_str),
+        "TD: %d.%d Emt: %d.%d",
+        model->time_delay / 10,
+        model->time_delay % 10,
+        model->emu_time / 10,
+        model->emu_time % 10);
+
+    crt = canvas_string_width(canvas, temp_str);
+
+    canvas_draw_str_aligned(
+        canvas, 128 - LEFT_RIGHT_OFFSET - crt, y, AlignLeft, AlignBottom, temp_str);
+}
+
+static void fuzzer_view_attack_draw_idle(Canvas* canvas, FuzzerViewAttackModel* model) {
+    if(model->td_emt_cursor) {
+        elements_button_center(canvas, "Start");
+        elements_button_left(canvas, "EmT -");
+        elements_button_right(canvas, "+ EmT");
+    } else {
+        elements_button_center(canvas, "Start");
+        elements_button_left(canvas, "TD -");
+        elements_button_right(canvas, "+ TD");
+    }
+}
+
+static void fuzzer_view_attack_draw_running(Canvas* canvas, FuzzerViewAttackModel* model) {
+    UNUSED(model);
+    elements_button_left(canvas, "Stop");
+    elements_button_center(canvas, "Pause");
+}
+
+static void fuzzer_view_attack_draw_end(Canvas* canvas, FuzzerViewAttackModel* model) {
+    UNUSED(model);
+    // elements_button_center(canvas, "Restart"); // Reset
+    elements_button_left(canvas, "Exit");
+}
+
+void fuzzer_view_attack_draw(Canvas* canvas, FuzzerViewAttackModel* model) {
+    canvas_clear(canvas);
+    canvas_set_color(canvas, ColorBlack);
+
+    // Header - Attack name
+    canvas_set_font(canvas, FontPrimary);
+    canvas_draw_str_aligned(canvas, 64, LINE_1_Y, AlignCenter, AlignBottom, model->attack_name);
+
+    // Time delays line or Status line
+    switch(model->attack_state) {
+    case FuzzerAttackStateIdle:
+        fuzzer_view_attack_draw_time_delays_line(canvas, model);
+        break;
+
+    case FuzzerAttackStateAttacking:
+        canvas_set_font(canvas, FontPrimary);
+        canvas_draw_str(canvas, LEFT_RIGHT_OFFSET, LINE_2_Y, "Attacking");
+        fuzzer_view_attack_draw_time_delays_str(canvas, model);
+
+        break;
+
+    case FuzzerAttackStateEmulating:
+        canvas_set_font(canvas, FontPrimary);
+        canvas_draw_str_aligned(canvas, 64, LINE_2_Y, AlignCenter, AlignBottom, "Emulating:");
+
+        break;
+
+    case FuzzerAttackStatePause:
+        canvas_set_font(canvas, FontPrimary);
+        canvas_draw_str(canvas, LEFT_RIGHT_OFFSET, LINE_2_Y, "Paused");
+
+        canvas_set_font(canvas, FontSecondary);
+        canvas_draw_icon_ex(canvas, 62, LINE_2_Y - 9, &I_Pin_arrow_up_7x9, IconRotation180);
+        canvas_draw_icon(canvas, 69, LINE_2_Y - 9, &I_Pin_arrow_up_7x9);
+        canvas_draw_str(canvas, 79, LINE_2_Y, "Change UID");
+        break;
+
+    case FuzzerAttackStateEnd:
+        canvas_set_font(canvas, FontPrimary);
+        canvas_draw_str_aligned(canvas, 64, LINE_2_Y, AlignCenter, AlignBottom, "Attack is over");
+
+        break;
+
+    default:
+        break;
     }
     }
 
 
+    // Protocol name
     canvas_set_font(canvas, FontSecondary);
     canvas_set_font(canvas, FontSecondary);
-    canvas_draw_str_aligned(canvas, 64, 26, AlignCenter, AlignTop, model->protocol_name);
+    canvas_draw_str_aligned(canvas, 64, LINE_3_Y, AlignCenter, AlignBottom, model->protocol_name);
 
 
+    // Current UID
     canvas_set_font(canvas, FontPrimary);
     canvas_set_font(canvas, FontPrimary);
     if(128 < canvas_string_width(canvas, furi_string_get_cstr(model->uid_str))) {
     if(128 < canvas_string_width(canvas, furi_string_get_cstr(model->uid_str))) {
         canvas_set_font(canvas, FontSecondary);
         canvas_set_font(canvas, FontSecondary);
     }
     }
     canvas_draw_str_aligned(
     canvas_draw_str_aligned(
-        canvas, 64, 38, AlignCenter, AlignTop, furi_string_get_cstr(model->uid_str));
+        canvas, 64, LINE_4_Y, AlignCenter, AlignBottom, furi_string_get_cstr(model->uid_str));
 
 
+    // Btns
     canvas_set_font(canvas, FontSecondary);
     canvas_set_font(canvas, FontSecondary);
-    if(model->attack_state == FuzzerAttackStateRunning) {
-        elements_button_center(canvas, "Stop");
+    if(model->attack_state == FuzzerAttackStateAttacking ||
+       model->attack_state == FuzzerAttackStateEmulating) {
+        fuzzer_view_attack_draw_running(canvas, model);
     } else if(model->attack_state == FuzzerAttackStateIdle) {
     } else if(model->attack_state == FuzzerAttackStateIdle) {
-        if(model->td_emt_cursor) {
-            elements_button_center(canvas, "Start");
-            elements_button_left(canvas, "EmT -");
-            elements_button_right(canvas, "+ EmT");
-        } else {
-            elements_button_center(canvas, "Start");
-            elements_button_left(canvas, "TD -");
-            elements_button_right(canvas, "+ TD");
-        }
-
+        fuzzer_view_attack_draw_idle(canvas, model);
+    } else if(model->attack_state == FuzzerAttackStatePause) {
+        elements_button_left(canvas, "Back");
+        elements_button_right(canvas, "Save");
+        elements_button_center(canvas, "Emu");
     } else if(model->attack_state == FuzzerAttackStateEnd) {
     } else if(model->attack_state == FuzzerAttackStateEnd) {
-        // elements_button_center(canvas, "Restart"); // Reset
-        elements_button_left(canvas, "Exit");
+        fuzzer_view_attack_draw_end(canvas, model);
     }
     }
 }
 }
 
 
-bool fuzzer_view_attack_input(InputEvent* event, void* context) {
-    furi_assert(context);
-    FuzzerViewAttack* view_attack = context;
-
+static bool fuzzer_view_attack_input_idle(
+    FuzzerViewAttack* view_attack,
+    InputEvent* event,
+    FuzzerViewAttackModel* model) {
     if(event->key == InputKeyBack && event->type == InputTypeShort) {
     if(event->key == InputKeyBack && event->type == InputTypeShort) {
-        view_attack->callback(FuzzerCustomEventViewAttackBack, view_attack->context);
+        view_attack->callback(FuzzerCustomEventViewAttackExit, view_attack->context);
         return true;
         return true;
     } else if(event->key == InputKeyOk && event->type == InputTypeShort) {
     } else if(event->key == InputKeyOk && event->type == InputTypeShort) {
-        view_attack->callback(FuzzerCustomEventViewAttackOk, view_attack->context);
+        view_attack->callback(FuzzerCustomEventViewAttackRunAttack, view_attack->context);
         return true;
         return true;
     } else if(event->key == InputKeyLeft) {
     } else if(event->key == InputKeyLeft) {
-        with_view_model(
-            view_attack->view,
-            FuzzerViewAttackModel * model,
-            {
-                if(model->attack_state == FuzzerAttackStateIdle) {
-                    if(!model->td_emt_cursor) {
-                        // TimeDelay --
-                        if(event->type == InputTypeShort) {
-                            if(model->time_delay > model->time_delay_min) {
-                                model->time_delay--;
-                            }
-                        } else if(event->type == InputTypeLong) {
-                            if((model->time_delay - 10) >= model->time_delay_min) {
-                                model->time_delay -= 10;
-                            } else {
-                                model->time_delay = model->time_delay_min;
-                            }
-                        }
-                    } else {
-                        // EmuTime --
-                        if(event->type == InputTypeShort) {
-                            if(model->emu_time > model->emu_time_min) {
-                                model->emu_time--;
-                            }
-                        } else if(event->type == InputTypeLong) {
-                            if((model->emu_time - 10) >= model->emu_time_min) {
-                                model->emu_time -= 10;
-                            } else {
-                                model->emu_time = model->emu_time_min;
-                            }
-                        }
-                    }
-                } else if(
-                    (model->attack_state == FuzzerAttackStateEnd) &&
-                    (event->type == InputTypeShort)) {
-                    // Exit if Ended
-                    view_attack->callback(FuzzerCustomEventViewAttackBack, view_attack->context);
+        if(!model->td_emt_cursor) {
+            // TimeDelay --
+            if(event->type == InputTypeShort) {
+                if(model->time_delay > model->time_delay_min) {
+                    model->time_delay--;
                 }
                 }
-            },
-            true);
+            } else if(event->type == InputTypeLong || event->type == InputTypeRepeat) {
+                if((model->time_delay - 10) >= model->time_delay_min) {
+                    model->time_delay -= 10;
+                } else {
+                    model->time_delay = model->time_delay_min;
+                }
+            }
+        } else {
+            // EmuTime --
+            if(event->type == InputTypeShort) {
+                if(model->emu_time > model->emu_time_min) {
+                    model->emu_time--;
+                }
+            } else if(event->type == InputTypeLong || event->type == InputTypeRepeat) {
+                if((model->emu_time - 10) >= model->emu_time_min) {
+                    model->emu_time -= 10;
+                } else {
+                    model->emu_time = model->emu_time_min;
+                }
+            }
+        }
         return true;
         return true;
     } else if(event->key == InputKeyRight) {
     } else if(event->key == InputKeyRight) {
-        with_view_model(
-            view_attack->view,
-            FuzzerViewAttackModel * model,
-            {
-                if(model->attack_state == FuzzerAttackStateIdle) {
-                    if(!model->td_emt_cursor) {
-                        // TimeDelay ++
-                        if(event->type == InputTypeShort) {
-                            if(model->time_delay < FUZZ_TIME_DELAY_MAX) {
-                                model->time_delay++;
-                            }
-                        } else if(event->type == InputTypeLong) {
-                            model->time_delay += 10;
-                            if(model->time_delay > FUZZ_TIME_DELAY_MAX) {
-                                model->time_delay = FUZZ_TIME_DELAY_MAX;
-                            }
-                        }
-                    } else {
-                        // EmuTime ++
-                        if(event->type == InputTypeShort) {
-                            if(model->emu_time < FUZZ_TIME_DELAY_MAX) {
-                                model->emu_time++;
-                            }
-                        } else if(event->type == InputTypeLong) {
-                            model->emu_time += 10;
-                            if(model->emu_time > FUZZ_TIME_DELAY_MAX) {
-                                model->emu_time = FUZZ_TIME_DELAY_MAX;
-                            }
-                        }
-                    }
-                } else {
-                    // Nothing
+        if(!model->td_emt_cursor) {
+            // TimeDelay ++
+            if(event->type == InputTypeShort) {
+                if(model->time_delay < FUZZ_TIME_DELAY_MAX) {
+                    model->time_delay++;
                 }
                 }
-            },
-            true);
+            } else if(event->type == InputTypeLong || event->type == InputTypeRepeat) {
+                model->time_delay += 10;
+                if(model->time_delay > FUZZ_TIME_DELAY_MAX) {
+                    model->time_delay = FUZZ_TIME_DELAY_MAX;
+                }
+            }
+        } else {
+            // EmuTime ++
+            if(event->type == InputTypeShort) {
+                if(model->emu_time < FUZZ_TIME_DELAY_MAX) {
+                    model->emu_time++;
+                }
+            } else if(event->type == InputTypeLong || event->type == InputTypeRepeat) {
+                model->emu_time += 10;
+                if(model->emu_time > FUZZ_TIME_DELAY_MAX) {
+                    model->emu_time = FUZZ_TIME_DELAY_MAX;
+                }
+            }
+        }
         return true;
         return true;
     } else if(
     } else if(
         (event->key == InputKeyUp || event->key == InputKeyDown) &&
         (event->key == InputKeyUp || event->key == InputKeyDown) &&
@@ -291,6 +327,81 @@ bool fuzzer_view_attack_input(InputEvent* event, void* context) {
             true);
             true);
         return true;
         return true;
     }
     }
+    return true;
+}
+
+static bool fuzzer_view_attack_input_end(
+    FuzzerViewAttack* view_attack,
+    InputEvent* event,
+    FuzzerViewAttackModel* model) {
+    UNUSED(model);
+    if((event->key == InputKeyBack || event->key == InputKeyLeft) &&
+       event->type == InputTypeShort) {
+        // Exit if Ended
+        view_attack->callback(FuzzerCustomEventViewAttackExit, view_attack->context);
+    }
+    return true;
+}
+
+bool fuzzer_view_attack_input(InputEvent* event, void* context) {
+    furi_assert(context);
+    FuzzerViewAttack* view_attack = context;
+
+    // if(event->key == InputKeyBack && event->type == InputTypeShort) {
+    //     view_attack->callback(FuzzerCustomEventViewAttackBack, view_attack->context);
+    //     return true;
+    // } else if(event->key == InputKeyOk && event->type == InputTypeShort) {
+    //     view_attack->callback(FuzzerCustomEventViewAttackOk, view_attack->context);
+    //     return true;
+    // } else
+    // {
+    with_view_model(
+        view_attack->view,
+        FuzzerViewAttackModel * model,
+        {
+            switch(model->attack_state) {
+            case FuzzerAttackStateIdle:
+                fuzzer_view_attack_input_idle(view_attack, event, model);
+                break;
+
+            case FuzzerAttackStateEnd:
+                fuzzer_view_attack_input_end(view_attack, event, model);
+                break;
+
+            case FuzzerAttackStateAttacking:
+            case FuzzerAttackStateEmulating:
+                if((event->key == InputKeyBack || event->key == InputKeyLeft) &&
+                   event->type == InputTypeShort) {
+                    view_attack->callback(FuzzerCustomEventViewAttackIdle, view_attack->context);
+                } else if(event->key == InputKeyOk && event->type == InputTypeShort) {
+                    view_attack->callback(FuzzerCustomEventViewAttackPause, view_attack->context);
+                }
+                break;
+
+            case FuzzerAttackStatePause:
+                if((event->key == InputKeyBack || event->key == InputKeyLeft) &&
+                   event->type == InputTypeShort) {
+                    view_attack->callback(FuzzerCustomEventViewAttackIdle, view_attack->context);
+                } else if(event->key == InputKeyRight && event->type == InputTypeShort) {
+                    view_attack->callback(FuzzerCustomEventViewAttackSave, view_attack->context);
+                } else if(event->key == InputKeyOk && event->type == InputTypeShort) {
+                    view_attack->callback(
+                        FuzzerCustomEventViewAttackEmulateCurrent, view_attack->context);
+                } else if(event->key == InputKeyUp && event->type == InputTypeShort) {
+                    view_attack->callback(
+                        FuzzerCustomEventViewAttackPrevUid, view_attack->context);
+                } else if(event->key == InputKeyDown && event->type == InputTypeShort) {
+                    view_attack->callback(
+                        FuzzerCustomEventViewAttackNextUid, view_attack->context);
+                }
+                break;
+
+            default:
+                break;
+            }
+        },
+        true);
+    // }
 
 
     return true;
     return true;
 }
 }

+ 1 - 7
views/attack.h

@@ -29,13 +29,7 @@ void fuzzer_view_attack_reset_data(
 
 
 void fuzzer_view_attack_set_uid(FuzzerViewAttack* view, const FuzzerPayload* uid);
 void fuzzer_view_attack_set_uid(FuzzerViewAttack* view, const FuzzerPayload* uid);
 
 
-void fuzzer_view_attack_start(FuzzerViewAttack* view);
-
-void fuzzer_view_attack_stop(FuzzerViewAttack* view);
-
-void fuzzer_view_attack_pause(FuzzerViewAttack* view);
-
-void fuzzer_view_attack_end(FuzzerViewAttack* view);
+void fuzzer_view_update_state(FuzzerViewAttack* view, FuzzerAttackState state);
 
 
 uint8_t fuzzer_view_attack_get_time_delay(FuzzerViewAttack* view);
 uint8_t fuzzer_view_attack_get_time_delay(FuzzerViewAttack* view);