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

Merge pull request #5 from frux-c/refractor_save

Refractor save and add view saved tags
Abel Chaka пре 2 година
родитељ
комит
c6adcd5ffd

BIN
assets/res/TDS_1_9_Standard.pdf


+ 5 - 5
scenes/uhf_scene_config.h

@@ -5,11 +5,11 @@ ADD_SCENE(uhf, read_tag_success, ReadTagSuccess)
 ADD_SCENE(uhf, card_menu, CardMenu)
 ADD_SCENE(uhf, save_name, SaveName)
 ADD_SCENE(uhf, save_success, SaveSuccess)
-// ADD_SCENE(uhf, saved_menu, SavedMenu)
-// ADD_SCENE(uhf, file_select, FileSelect)
-// ADD_SCENE(uhf, device_info, DeviceInfo)
-// ADD_SCENE(uhf, delete, Delete)
-// ADD_SCENE(uhf, delete_success, DeleteSuccess)
+ADD_SCENE(uhf, saved_menu, SavedMenu)
+ADD_SCENE(uhf, file_select, FileSelect)
+ADD_SCENE(uhf, device_info, DeviceInfo)
+ADD_SCENE(uhf, delete, Delete)
+ADD_SCENE(uhf, delete_success, DeleteSuccess)
 // ADD_SCENE(uhf, write_card, WriteCard)
 // ADD_SCENE(uhf, write_card_success, WriteCardSuccess)
 // ADD_SCENE(uhf, read_factory_success, ReadFactorySuccess)

+ 50 - 0
scenes/uhf_scene_delete.c

@@ -0,0 +1,50 @@
+#include "../uhf_app_i.h"
+
+void uhf_scene_delete_widget_callback(GuiButtonType result, InputType type, void* context) {
+    UHFApp* uhf_app = context;
+    if(type == InputTypeShort) {
+        view_dispatcher_send_custom_event(uhf_app->view_dispatcher, result);
+    }
+}
+
+void uhf_scene_delete_on_enter(void* context) {
+    UHFApp* uhf_app = context;
+
+    // Setup Custom Widget view
+    char temp_str[64];
+    snprintf(temp_str, sizeof(temp_str), "\e#Delete %s?\e#", uhf_app->uhf_device->dev_name);
+    widget_add_text_box_element(
+        uhf_app->widget, 0, 0, 128, 23, AlignCenter, AlignCenter, temp_str, false);
+    widget_add_button_element(
+        uhf_app->widget, GuiButtonTypeLeft, "Back", uhf_scene_delete_widget_callback, uhf_app);
+    widget_add_button_element(
+        uhf_app->widget, GuiButtonTypeRight, "Delete", uhf_scene_delete_widget_callback, uhf_app);
+
+    view_dispatcher_switch_to_view(uhf_app->view_dispatcher, UHFViewWidget);
+}
+
+bool uhf_scene_delete_on_event(void* context, SceneManagerEvent event) {
+    UHFApp* uhf_app = context;
+    bool consumed = false;
+
+    if(event.type == SceneManagerEventTypeCustom) {
+        if(event.event == GuiButtonTypeLeft) {
+            return scene_manager_previous_scene(uhf_app->scene_manager);
+        } else if(event.event == GuiButtonTypeRight) {
+            if(uhf_device_delete(uhf_app->uhf_device, true)) {
+                scene_manager_next_scene(uhf_app->scene_manager, UHFSceneDeleteSuccess);
+            } else {
+                scene_manager_search_and_switch_to_previous_scene(
+                    uhf_app->scene_manager, UHFSceneStart);
+            }
+            consumed = true;
+        }
+    }
+    return consumed;
+}
+
+void uhf_scene_delete_on_exit(void* context) {
+    UHFApp* uhf_app = context;
+
+    widget_reset(uhf_app->widget);
+}

+ 40 - 0
scenes/uhf_scene_delete_success.c

@@ -0,0 +1,40 @@
+#include "../uhf_app_i.h"
+
+void uhf_scene_delete_success_popup_callback(void* context) {
+    UHFApp* uhf_app = context;
+    view_dispatcher_send_custom_event(uhf_app->view_dispatcher, UHFCustomEventViewExit);
+}
+
+void uhf_scene_delete_success_on_enter(void* context) {
+    UHFApp* uhf_app = context;
+
+    // Setup view
+    Popup* popup = uhf_app->popup;
+    popup_set_icon(popup, 0, 2, &I_DolphinMafia_115x62);
+    popup_set_header(popup, "Deleted", 83, 19, AlignLeft, AlignBottom);
+    popup_set_timeout(popup, 1500);
+    popup_set_context(popup, uhf_app);
+    popup_set_callback(popup, uhf_scene_delete_success_popup_callback);
+    popup_enable_timeout(popup);
+    view_dispatcher_switch_to_view(uhf_app->view_dispatcher, UHFViewPopup);
+}
+
+bool uhf_scene_delete_success_on_event(void* context, SceneManagerEvent event) {
+    UHFApp* uhf_app = context;
+    bool consumed = false;
+
+    if(event.type == SceneManagerEventTypeCustom) {
+        if(event.event == UHFCustomEventViewExit) {
+            consumed = scene_manager_search_and_switch_to_previous_scene(
+                uhf_app->scene_manager, UHFSceneStart);
+        }
+    }
+    return consumed;
+}
+
+void uhf_scene_delete_success_on_exit(void* context) {
+    UHFApp* uhf_app = context;
+
+    // Clear view
+    popup_reset(uhf_app->popup);
+}

+ 133 - 0
scenes/uhf_scene_device_info.c

@@ -0,0 +1,133 @@
+#include "../uhf_app_i.h"
+#include <dolphin/dolphin.h>
+
+typedef enum { EPC_INFO, TID_INFO, USER_INFO } UHFTagInfo;
+
+static UHFTagInfo current_info;
+
+char* get_current_bank_info_str() {
+    switch(current_info) {
+    case EPC_INFO:
+        return "EPC Bank";
+    case TID_INFO:
+        return "TID Bank";
+    case USER_INFO:
+        return "User Bank";
+    }
+    return "";
+}
+
+char* get_next_bank_info_str() {
+    switch(current_info) {
+    case EPC_INFO:
+        current_info = TID_INFO;
+        return "TID";
+    case TID_INFO:
+        current_info = USER_INFO;
+        return "USER";
+    case USER_INFO:
+        current_info = EPC_INFO;
+        return "EPC";
+    }
+    return "";
+}
+
+void uhf_scene_device_info_widget_callback(GuiButtonType result, InputType type, void* context) {
+    UHFApp* uhf_app = context;
+    if(type == InputTypeShort) {
+        view_dispatcher_send_custom_event(uhf_app->view_dispatcher, result);
+    }
+}
+
+void change_view_on_event(UHFApp* uhf_app) {
+    UHFTag* uhf_tag = uhf_app->uhf_device->uhf_tag;
+    FuriString* furi_temp_str;
+    furi_temp_str = furi_string_alloc();
+    char* temp_str;
+    size_t length;
+
+    widget_reset(uhf_app->widget);
+    widget_add_string_element(
+        uhf_app->widget, 64, 5, AlignCenter, AlignCenter, FontPrimary, get_current_bank_info_str());
+
+    switch(current_info) {
+    case EPC_INFO:
+        temp_str = convertToHexString(uhf_tag->epc, uhf_tag->epc_length);
+        length = uhf_tag->epc_length;
+        break;
+    case TID_INFO:
+        temp_str = convertToHexString(uhf_tag->tid, uhf_tag->tid_length);
+        length = uhf_tag->tid_length;
+        break;
+    case USER_INFO:
+        temp_str = convertToHexString(uhf_tag->user, uhf_tag->user_length);
+        length = uhf_tag->user_length;
+        break;
+    default:
+        temp_str = NULL;
+        length = 0;
+        break;
+    }
+
+    furi_string_cat_printf(furi_temp_str, "Length: %d bytes", length);
+
+    widget_add_string_element(
+        uhf_app->widget,
+        3,
+        12,
+        AlignLeft,
+        AlignTop,
+        FontKeyboard,
+        furi_string_get_cstr(furi_temp_str));
+
+    widget_add_string_multiline_element(
+        uhf_app->widget, 3, 24, AlignLeft, AlignTop, FontBatteryPercent, temp_str);
+
+    widget_add_button_element(
+        uhf_app->widget,
+        GuiButtonTypeRight,
+        get_next_bank_info_str(),
+        uhf_scene_device_info_widget_callback,
+        uhf_app);
+
+    widget_add_button_element(
+        uhf_app->widget, GuiButtonTypeLeft, "Back", uhf_scene_device_info_widget_callback, uhf_app);
+
+    furi_string_free(furi_temp_str);
+    free(temp_str);
+}
+
+void uhf_scene_device_info_on_enter(void* context) {
+    UHFApp* uhf_app = context;
+    current_info = EPC_INFO;
+    dolphin_deed(DolphinDeedNfcReadSuccess);
+    change_view_on_event(uhf_app);
+    view_dispatcher_switch_to_view(uhf_app->view_dispatcher, UHFViewWidget);
+}
+
+bool uhf_scene_device_info_on_event(void* context, SceneManagerEvent event) {
+    UHFApp* uhf_app = context;
+    bool consumed = false;
+    if(event.type == SceneManagerEventTypeTick) return false;
+    if(event.type == SceneManagerEventTypeCustom) {
+        if(event.event == GuiButtonTypeLeft) {
+            consumed = scene_manager_previous_scene(uhf_app->scene_manager);
+        } else if(event.event == GuiButtonTypeRight) {
+            change_view_on_event(uhf_app);
+        } else if(event.event == UHFCustomEventViewExit) {
+            view_dispatcher_switch_to_view(uhf_app->view_dispatcher, UHFViewWidget);
+            consumed = true;
+        }
+    } else if(event.type == SceneManagerEventTypeBack) {
+        scene_manager_previous_scene(uhf_app->scene_manager);
+        consumed = true;
+    }
+    return consumed;
+}
+
+void uhf_scene_device_info_on_exit(void* context) {
+    UHFApp* uhf_app = context;
+
+    // Clear views
+    widget_reset(uhf_app->widget);
+}

+ 23 - 0
scenes/uhf_scene_file_select.c

@@ -0,0 +1,23 @@
+#include "../uhf_app_i.h"
+
+void uhf_scene_file_select_on_enter(void* context) {
+    UHFApp* uhf_app = context;
+    // Process file_select return
+    uhf_device_set_loading_callback(uhf_app->uhf_device, uhf_show_loading_popup, uhf_app);
+    if(uhf_file_select(uhf_app->uhf_device)) {
+        scene_manager_next_scene(uhf_app->scene_manager, UHFSceneSavedMenu);
+    } else {
+        scene_manager_search_and_switch_to_previous_scene(uhf_app->scene_manager, UHFSceneStart);
+    }
+    uhf_device_set_loading_callback(uhf_app->uhf_device, NULL, uhf_app);
+}
+
+bool uhf_scene_file_select_on_event(void* context, SceneManagerEvent event) {
+    UNUSED(context);
+    UNUSED(event);
+    return false;
+}
+
+void uhf_scene_file_select_on_exit(void* context) {
+    UNUSED(context);
+}

+ 0 - 1
scenes/uhf_scene_read_tag.c

@@ -29,7 +29,6 @@ void uhf_scene_read_tag_on_enter(void* ctx) {
 }
 
 bool uhf_scene_read_tag_on_event(void* ctx, SceneManagerEvent event) {
-    // UNUSED(ctx);
     UHFApp* uhf_app = ctx;
     bool consumed = false;
     if(event.event == UHFCustomEventWorkerExit) {

+ 15 - 28
scenes/uhf_scene_read_tag_success.c

@@ -1,4 +1,5 @@
 #include "../uhf_app_i.h"
+#include <dolphin/dolphin.h>
 
 void uhf_read_tag_success_worker_callback(UHFWorkerEvent event, void* ctx) {
     UNUSED(event);
@@ -16,9 +17,12 @@ void uhf_scene_read_card_success_widget_callback(GuiButtonType result, InputType
 
 void uhf_scene_read_tag_success_on_enter(void* ctx) {
     UHFApp* uhf_app = ctx;
+    UHFTag* uhf_tag = uhf_app->worker->uhf_tag;
 
-    const uint8_t* read_data =
-        uhf_response_data_get_uhf_data(uhf_app->worker->response_data, 0)->data;
+    dolphin_deed(DolphinDeedNfcReadSuccess);
+
+    // Send notification
+    notification_message(uhf_app->notifications, &sequence_success);
 
     widget_add_string_element(
         uhf_app->widget, 32, 5, AlignLeft, AlignCenter, FontPrimary, "Read Success");
@@ -31,32 +35,13 @@ void uhf_scene_read_tag_success_on_enter(void* ctx) {
     widget_add_string_element(
         uhf_app->widget, 3, 32, AlignLeft, AlignCenter, FontPrimary, "EPC :");
 
-    widget_add_string_element(
-        uhf_app->widget,
-        26,
-        19,
-        AlignLeft,
-        AlignCenter,
-        FontKeyboard,
-        convertToHexString(read_data + 6, 2));
-    widget_add_string_element(
-        uhf_app->widget,
-        96,
-        19,
-        AlignLeft,
-        AlignCenter,
-        FontKeyboard,
-        convertToHexString(read_data + 20, 2));
-
+    char* pc = convertToHexString(uhf_tag->pc, 2);
+    widget_add_string_element(uhf_app->widget, 26, 19, AlignLeft, AlignCenter, FontKeyboard, pc);
+    char* crc = convertToHexString(uhf_tag->crc, 2);
+    widget_add_string_element(uhf_app->widget, 96, 19, AlignLeft, AlignCenter, FontKeyboard, crc);
+    char* epc = convertToHexString(uhf_tag->epc + 2, uhf_tag->epc_length - 2);
     widget_add_string_multiline_element(
-        uhf_app->widget,
-        34,
-        29,
-        AlignLeft,
-        AlignTop,
-        FontKeyboard,
-        convertToHexString(read_data + 8, 12));
-
+        uhf_app->widget, 34, 29, AlignLeft, AlignTop, FontKeyboard, epc);
     widget_add_button_element(
         uhf_app->widget,
         GuiButtonTypeRight,
@@ -69,8 +54,10 @@ void uhf_scene_read_tag_success_on_enter(void* ctx) {
         "Exit",
         uhf_scene_read_card_success_widget_callback,
         uhf_app);
-
     view_dispatcher_switch_to_view(uhf_app->view_dispatcher, UHFViewWidget);
+    free(pc);
+    free(crc);
+    free(epc);
 }
 
 bool uhf_scene_read_tag_success_on_event(void* ctx, SceneManagerEvent event) {

+ 11 - 10
scenes/uhf_scene_save_name.c

@@ -3,9 +3,6 @@
 #include <gui/modules/validators.h>
 #include <toolbox/path.h>
 
-#define UHF_DEV_NAME_MAX_LEN 22
-#define UHF_APP_EXTENSION ".uhf"
-
 void uhf_scene_save_name_text_input_callback(void* context) {
     UHFApp* uhf_app = context;
 
@@ -30,12 +27,12 @@ void uhf_scene_save_name_on_enter(void* context) {
     FuriString* folder_path;
     folder_path = furi_string_alloc_set(STORAGE_APP_DATA_PATH_PREFIX);
 
-    // if(furi_string_end_with(uhf_app->dev->load_path, UHF_APP_EXTENSION)) {
-    //     path_extract_dirname(furi_string_get_cstr(uhf_app->dev->load_path), folder_path);
-    // }
+    if(furi_string_end_with(uhf_app->uhf_device->load_path, UHF_APP_EXTENSION)) {
+        path_extract_dirname(furi_string_get_cstr(uhf_app->uhf_device->load_path), folder_path);
+    }
 
-    ValidatorIsFile* validator_is_file =
-        validator_is_file_alloc_init(furi_string_get_cstr(folder_path), UHF_APP_EXTENSION, "test");
+    ValidatorIsFile* validator_is_file = validator_is_file_alloc_init(
+        furi_string_get_cstr(folder_path), UHF_APP_EXTENSION, uhf_app->uhf_device->dev_name);
     text_input_set_validator(text_input, validator_is_file_callback, validator_is_file);
 
     view_dispatcher_switch_to_view(uhf_app->view_dispatcher, UHFViewTextInput);
@@ -45,11 +42,15 @@ void uhf_scene_save_name_on_enter(void* context) {
 
 bool uhf_scene_save_name_on_event(void* context, SceneManagerEvent event) {
     UHFApp* uhf_app = context;
-    UHFResponseData* uhf_data_save = uhf_app->worker->response_data;
     bool consumed = false;
+
     if(event.type == SceneManagerEventTypeCustom) {
         if(event.event == UHFCustomEventTextInputDone) {
-            if(uhf_save_read_data(uhf_data_save, uhf_app->storage, uhf_app->text_store)) {
+            strlcpy(
+                uhf_app->uhf_device->dev_name,
+                uhf_app->text_store,
+                strlen(uhf_app->text_store) + 1);
+            if(uhf_device_save(uhf_app->uhf_device, uhf_app->text_store)) {
                 scene_manager_next_scene(uhf_app->scene_manager, UHFSceneSaveSuccess);
                 consumed = true;
             } else {

+ 60 - 0
scenes/uhf_scene_saved_menu.c

@@ -0,0 +1,60 @@
+#include "../uhf_app_i.h"
+
+enum SubmenuIndex {
+    SubmenuIndexDelete,
+    SubmenuIndexInfo,
+    SubmenuIndexWrite,
+};
+
+void uhf_scene_saved_menu_submenu_callback(void* context, uint32_t index) {
+    UHFApp* uhf_app = context;
+
+    view_dispatcher_send_custom_event(uhf_app->view_dispatcher, index);
+}
+
+void uhf_scene_saved_menu_on_enter(void* context) {
+    UHFApp* uhf_app = context;
+    Submenu* submenu = uhf_app->submenu;
+
+    submenu_add_item(
+        submenu, "Delete", SubmenuIndexDelete, uhf_scene_saved_menu_submenu_callback, uhf_app);
+    submenu_add_item(
+        submenu, "Info", SubmenuIndexInfo, uhf_scene_saved_menu_submenu_callback, uhf_app);
+    submenu_add_item(
+        submenu, "Write", SubmenuIndexWrite, uhf_scene_saved_menu_submenu_callback, uhf_app);
+
+    submenu_set_selected_item(
+        uhf_app->submenu,
+        scene_manager_get_scene_state(uhf_app->scene_manager, UHFSceneSavedMenu));
+
+    view_dispatcher_switch_to_view(uhf_app->view_dispatcher, UHFViewMenu);
+}
+
+bool uhf_scene_saved_menu_on_event(void* context, SceneManagerEvent event) {
+    UHFApp* uhf_app = context;
+    bool consumed = false;
+
+    if(event.type == SceneManagerEventTypeCustom) {
+        scene_manager_set_scene_state(uhf_app->scene_manager, UHFSceneSavedMenu, event.event);
+
+        if(event.event == SubmenuIndexDelete) {
+            scene_manager_next_scene(uhf_app->scene_manager, UHFSceneDelete);
+            consumed = true;
+        } else if(event.event == SubmenuIndexInfo) {
+            scene_manager_next_scene(uhf_app->scene_manager, UHFSceneDeviceInfo);
+            consumed = true;
+        }
+        // } else if(event.event == SubmenuIndexWrite) {
+        //     scene_manager_next_scene(uhf_app->scene_manager, UHFSceneWriteCard);
+        //     consumed = true;
+        // }
+    }
+
+    return consumed;
+}
+
+void uhf_scene_saved_menu_on_exit(void* context) {
+    UHFApp* uhf_app = context;
+
+    submenu_reset(uhf_app->submenu);
+}

+ 7 - 7
scenes/uhf_scene_start.c

@@ -31,14 +31,14 @@ bool uhf_scene_start_on_event(void* ctx, SceneManagerEvent event) {
             scene_manager_set_scene_state(uhf_app->scene_manager, UHFSceneStart, SubmenuIndexRead);
             scene_manager_next_scene(uhf_app->scene_manager, UHFSceneReadTag);
             consumed = true;
+        } else if(event.event == SubmenuIndexSaved) {
+            // Explicitly save state so that the correct item is
+            // reselected if the user cancels loading a file.
+            scene_manager_set_scene_state(
+                uhf_app->scene_manager, UHFSceneStart, SubmenuIndexSaved);
+            scene_manager_next_scene(uhf_app->scene_manager, UHFSceneFileSelect);
+            consumed = true;
         }
-        //else if(event.event == SubmenuIndexSaved) {
-        // Explicitly save state so that the correct item is
-        // reselected if the user cancels loading a file.
-        // scene_manager_set_scene_state(
-        //     uhf_app->scene_manager, UHFSceneStart, SubmenuIndexSaved);
-        // scene_manager_next_scene(uhf_app->scene_manager, UHFSceneFileSelect);
-        // consumed = true;
         // } else if(event.event == SubmenuIndexEliteDictAttack) {
         //     scene_manager_set_scene_state(
         //         uhf_app->scene_manager, UHFSceneStart, SubmenuIndexEliteDictAttack);

+ 8 - 0
scenes/uhf_scene_verify.c

@@ -116,6 +116,14 @@ bool uhf_scene_verify_on_event(void* ctx, SceneManagerEvent event) {
                     AlignCenter,
                     FontPrimary,
                     "No UHF Module found");
+                widget_add_string_multiline_element(
+                    uhf_app->widget,
+                    64,
+                    30,
+                    AlignCenter,
+                    AlignCenter,
+                    FontSecondary,
+                    "Please connect your module.\nPlease refer to the frux-c/uhf_rfid for help.");
                 widget_add_button_element(
                     uhf_app->widget,
                     GuiButtonTypeLeft,

+ 27 - 92
uhf_app.c

@@ -1,98 +1,22 @@
 #include "uhf_app_i.h"
 
-static const char* uhf_file_header = "Flipper UHF device";
-static const uint32_t uhf_file_version = 1;
-static const uint8_t bank_data_start = 20;
-static const uint8_t bank_data_length = 16;
-
-// empty callback
-void empty_rx_callback(UartIrqEvent event, uint8_t data, void* ctx) {
-    UNUSED(event);
-    UNUSED(data);
-    UNUSED(ctx);
-}
-
 char* convertToHexString(const uint8_t* array, size_t length) {
     if(array == NULL || length == 0) {
-        return NULL;
-    }
-
-    // Each byte takes 3 characters in the hex representation (2 characters + space), plus 1 for the null terminator
-    size_t hexLength = (length * 3) + 1;
-
-    char* hexArray = (char*)malloc(hexLength * sizeof(char));
-    if(hexArray == NULL) {
-        return NULL;
+        return " ";
     }
+    FuriString* temp_str = furi_string_alloc();
 
-    size_t index = 0;
     for(size_t i = 0; i < length; i++) {
-        index += snprintf(&hexArray[index], hexLength - index, "%02x ", array[i]);
-    }
-
-    hexArray[hexLength - 1] = '\0';
-
-    return hexArray;
-}
-
-bool uhf_save_read_data(UHFResponseData* uhf_response_data, Storage* storage, const char* filename) {
-    if(!storage_dir_exists(storage, UHF_APPS_DATA_FOLDER)) {
-        storage_simply_mkdir(storage, UHF_APPS_DATA_FOLDER);
-    }
-    if(!storage_dir_exists(storage, UHF_APPS_STORAGE_FOLDER)) {
-        storage_simply_mkdir(storage, UHF_APPS_STORAGE_FOLDER);
-    }
-
-    FlipperFormat* file = flipper_format_file_alloc(storage);
-    FuriString* temp_str = furi_string_alloc();
-    // set file name
-    furi_string_cat_printf(
-        temp_str, "%s/%s%s", UHF_APPS_STORAGE_FOLDER, filename, UHF_FILE_EXTENSION);
-    // open file
-    if(!flipper_format_file_open_always(file, furi_string_get_cstr(temp_str))) return false;
-    // write header
-    if(!flipper_format_write_header_cstr(file, uhf_file_header, uhf_file_version)) return false;
-    // write rfu data to file
-    UHFData* rfu_data = uhf_response_data_get_uhf_data(uhf_response_data, 1);
-    if(rfu_data->length) {
-        if(!flipper_format_write_hex(
-               file, "RFU", rfu_data->data + bank_data_start, bank_data_length))
-            return false;
-    } else {
-        if(!flipper_format_write_hex(file, "RFU", UHF_BANK_DOES_NOT_EXIST, 1)) return false;
+        furi_string_cat_printf(temp_str, "%02X ", array[i]);
     }
+    const char* furi_str = furi_string_get_cstr(temp_str);
 
-    // write epc data to file
-    UHFData* epc_data = uhf_response_data_get_uhf_data(uhf_response_data, 2);
-    if(epc_data->length) {
-        if(!flipper_format_write_hex(
-               file, "EPC", epc_data->data + bank_data_start, bank_data_length))
-            return false;
-    } else {
-        if(!flipper_format_write_hex(file, "EPC", UHF_BANK_DOES_NOT_EXIST, 1)) return false;
-    }
+    size_t str_len = strlen(furi_str);
+    char* str = (char*)malloc(sizeof(char) * str_len);
 
-    // write tid data to file
-    UHFData* tid_data = uhf_response_data_get_uhf_data(uhf_response_data, 3);
-    if(tid_data->length) {
-        if(!flipper_format_write_hex(
-               file, "TID", tid_data->data + bank_data_start, bank_data_length))
-            return false;
-    } else {
-        if(!flipper_format_write_hex(file, "TID", UHF_BANK_DOES_NOT_EXIST, 1)) return false;
-    }
-    // write user data to file
-    UHFData* user_data = uhf_response_data_get_uhf_data(uhf_response_data, 4);
-    if(user_data->length) {
-        if(!flipper_format_write_hex(
-               file, "USER", user_data->data + bank_data_start, bank_data_length))
-            return false;
-    } else {
-        if(!flipper_format_write_hex(file, "USER", UHF_BANK_DOES_NOT_EXIST, 1)) return false;
-    }
+    memcpy(str, furi_str, str_len);
     furi_string_free(temp_str);
-    flipper_format_free(file);
-    return true;
+    return str;
 }
 
 bool uhf_custom_event_callback(void* ctx, uint32_t event) {
@@ -115,7 +39,6 @@ void uhf_tick_event_callback(void* ctx) {
 
 UHFApp* uhf_alloc() {
     UHFApp* uhf_app = (UHFApp*)malloc(sizeof(UHFApp));
-    uhf_app->worker = (UHFWorker*)uhf_worker_alloc();
     uhf_app->view_dispatcher = view_dispatcher_alloc();
     uhf_app->scene_manager = scene_manager_alloc(&uhf_scene_handlers, uhf_app);
     view_dispatcher_enable_queue(uhf_app->view_dispatcher);
@@ -131,8 +54,16 @@ UHFApp* uhf_alloc() {
     view_dispatcher_attach_to_gui(
         uhf_app->view_dispatcher, uhf_app->gui, ViewDispatcherTypeFullscreen);
 
-    // Storage
-    uhf_app->storage = furi_record_open(RECORD_STORAGE);
+    //worker
+    uhf_app->worker = uhf_worker_alloc();
+
+    // device
+    uhf_app->uhf_device = uhf_device_alloc();
+
+    UHFTag* uhf_tag = uhf_tag_alloc();
+    // point tag object to worker
+    uhf_app->worker->uhf_tag = uhf_tag;
+    uhf_app->uhf_device->uhf_tag = uhf_tag;
 
     // Open Notification record
     uhf_app->notifications = furi_record_open(RECORD_NOTIFICATION);
@@ -192,6 +123,9 @@ void uhf_free(UHFApp* uhf_app) {
     uhf_worker_stop(uhf_app->worker);
     uhf_worker_free(uhf_app->worker);
 
+    // Tag
+    uhf_tag_free(uhf_app->worker->uhf_tag);
+
     // View Dispatcher
     view_dispatcher_free(uhf_app->view_dispatcher);
 
@@ -202,9 +136,8 @@ void uhf_free(UHFApp* uhf_app) {
     furi_record_close(RECORD_GUI);
     uhf_app->gui = NULL;
 
-    // Storage
-    furi_record_close(RECORD_STORAGE);
-    uhf_app->storage = NULL;
+    // UHFDevice
+    uhf_device_free(uhf_app->uhf_device);
 
     // Notifications
     furi_record_close(RECORD_NOTIFICATION);
@@ -251,16 +184,18 @@ int32_t uhf_app_main(void* ctx) {
     UNUSED(ctx);
     UHFApp* uhf_app = uhf_alloc();
 
+    furi_hal_uart_resume(FuriHalUartIdUSART1);
+
     // enable 5v pin
     furi_hal_power_enable_otg();
+
     scene_manager_next_scene(uhf_app->scene_manager, UHFSceneVerify);
     view_dispatcher_run(uhf_app->view_dispatcher);
 
     // disable 5v pin
     furi_hal_power_disable_otg();
 
-    // set uart callback to none
-    furi_hal_uart_set_irq_cb(FuriHalUartIdUSART1, empty_rx_callback, NULL);
+    furi_hal_uart_suspend(FuriHalUartIdUSART1);
 
     // exit app
     uhf_free(uhf_app);

+ 11 - 12
uhf_app_i.h

@@ -14,24 +14,24 @@
 
 #include <input/input.h>
 
+#include "uhf_app.h"
+#include "uhf_worker.h"
+#include "uhf_device.h"
 #include "scenes/uhf_scene.h"
 
 #include <storage/storage.h>
-// #include <lib/toolbox/path.h>
+#include <lib/toolbox/path.h>
 #include <toolbox/path.h>
 #include <flipper_format/flipper_format.h>
 
-#include "uhf_app.h"
-#include "uhf_worker.h"
 #include <uhf_rfid_icons.h>
 
-#define UHF_BANK_DOES_NOT_EXIST (uint8_t[]){0xFF}
 #define UHF_TEXT_STORE_SIZE 128
-#define UHF_APPS_DATA_FOLDER EXT_PATH("apps_data")
-#define UHF_APPS_STORAGE_FOLDER \
-    UHF_APPS_DATA_FOLDER "/"    \
-                         "uhf_rfid"
-#define UHF_FILE_EXTENSION ".uhf"
+// #define UHF_APPS_DATA_FOLDER EXT_PATH("apps_data")
+// #define UHF_APPS_STORAGE_FOLDER
+//     UHF_APPS_DATA_FOLDER "/"
+//                          "uhf_rfid"
+// #define UHF_FILE_EXTENSION ".uhf"
 
 enum UHFCustomEvent {
     // Reserve first 100 events for button types and indexes, starting from 0
@@ -55,11 +55,10 @@ struct UHFApp {
     Gui* gui;
     NotificationApp* notifications;
     SceneManager* scene_manager;
-    Storage* storage;
-
+    // Storage* storage;
+    UHFDevice* uhf_device;
     char text_store[UHF_TEXT_STORE_SIZE + 1];
     FuriString* text_box_store;
-
     // Common Views
     Submenu* submenu;
     Popup* popup;

+ 7 - 0
uhf_cmd.h

@@ -15,6 +15,13 @@ typedef enum{
     NOTIFICATION_FRAME
 }UHFFrameType;
 
+typedef enum{
+    RFU_BANK,
+    EPC_BANK,
+    TID_BANK,
+    USER_BANK
+}UHFBank;
+
 typedef enum{
     CHINA_900MHZ = 1,
     CHINA_800MHZ = 4,

+ 61 - 1
uhf_data.c

@@ -1,8 +1,8 @@
-#include <furi.h>
 #include "uhf_data.h"
 
 UHFData* uhf_data_alloc() {
     UHFData* uhf_data = (UHFData*)malloc(sizeof(UHFData));
+    uhf_data->word_length = 0;
     uhf_data->length = 0;
     uhf_data->start = false;
     uhf_data->end = false;
@@ -34,6 +34,23 @@ void uhf_data_reset(UHFData* uhf_data) {
     uhf_data->next = NULL;
 }
 
+uint8_t uhf_data_calculate_checksum(UHFData* uhf_data) {
+    // CheckSum8 Modulo 256
+    // Sum of Bytes % 256
+    uint8_t sum_val = 0x00;
+    size_t length = uhf_data->length - 2;
+    for(size_t i = 1; i < length; i++) {
+        sum_val += uhf_data->data[i];
+    }
+    return sum_val % 256;
+}
+
+bool uhf_data_verfiy_checksum(UHFData* uhf_data) {
+    uint8_t data_checksum = uhf_data->data[uhf_data->length - 2];
+    uint8_t actual_checksum = uhf_data_calculate_checksum(uhf_data);
+    return data_checksum == actual_checksum;
+}
+
 void uhf_data_free(UHFData* uhf_data) {
     if(uhf_data == NULL) return;
     while(uhf_data != NULL) {
@@ -85,4 +102,47 @@ void uhf_response_data_reset(UHFResponseData* uhf_response_data) {
 void uhf_response_data_free(UHFResponseData* uhf_response_data) {
     uhf_data_free(uhf_response_data->head);
     free(uhf_response_data);
+}
+
+UHFTag* uhf_tag_alloc() {
+    UHFTag* uhf_tag = (UHFTag*)malloc(sizeof(UHFTag));
+    return uhf_tag;
+}
+
+void uhf_tag_set_epc(UHFTag* uhf_tag, uint8_t* data, size_t length) {
+    memcpy(uhf_tag->crc, data, 2);
+    data += 2;
+    memcpy(uhf_tag->pc, data, 2);
+    memcpy(uhf_tag->epc, data, length);
+    uhf_tag->epc_length = length;
+}
+
+void uhf_tag_reset(UHFTag* uhf_tag) {
+    for(int i = 0; i < 2; i++) {
+        uhf_tag->crc[i] = 0;
+        uhf_tag->pc[i] = 0;
+        uhf_tag->xpc[i] = 0;
+    }
+    for(int i = 0; i < MAX_BANK_SIZE; i++) {
+        uhf_tag->epc[i] = 0;
+        uhf_tag->tid[i] = 0;
+        uhf_tag->user[i] = 0;
+    }
+    uhf_tag->epc_length = 0;
+    uhf_tag->tid_length = 0;
+    uhf_tag->user_length = 0;
+}
+
+void uhf_tag_set_tid(UHFTag* uhf_tag, uint8_t* data, size_t length) {
+    memcpy(uhf_tag->tid, data, length);
+    uhf_tag->tid_length = length;
+}
+
+void uhf_tag_set_user(UHFTag* uhf_tag, uint8_t* data, size_t length) {
+    memcpy(uhf_tag->user, data, length);
+    uhf_tag->user_length = length;
+}
+
+void uhf_tag_free(UHFTag* uhf_tag) {
+    free(uhf_tag);
 }

+ 35 - 3
uhf_data.h

@@ -2,11 +2,16 @@
 
 #include <stdint.h>
 #include <stdlib.h>
+#include <string.h>
+#include <stdbool.h>
 
-#define MAX_DATA_SIZE 64
+#define MAX_DATA_SIZE 128
+#define MAX_BANK_SIZE 64
+typedef unsigned int uint;
 
 typedef struct UHFData {
     uint8_t data[MAX_DATA_SIZE];
+    size_t word_length;
     size_t length;
     bool start;
     bool end;
@@ -17,16 +22,43 @@ typedef struct UHFResponseData {
     UHFData* head;
     UHFData* tail;
     size_t size;
-
 } UHFResponseData;
 
+typedef struct UHFTag {
+    // RESERVED BANK (RFU) (00)
+    uint8_t kill_pwd[2]; // 0x00-0x10
+    uint8_t access_pwd[2]; // 0x10-0x20
+    // EPC Bank
+    uint8_t crc[2]; // 0x00-0x10
+    uint8_t pc[2]; // 0x10-0x20
+    uint8_t epc[MAX_BANK_SIZE]; // 0x20-0x210
+    size_t epc_length;
+    uint8_t xpc[2]; // 0x210-0x21F
+    size_t xpc_length;
+    // TID Bank
+    uint8_t tid[MAX_BANK_SIZE]; // 0x00-END
+    size_t tid_length;
+    // USER Bank
+    uint8_t user[MAX_BANK_SIZE]; // 0x00-END
+    size_t user_length;
+} UHFTag;
+
 UHFData* uhf_data_alloc();
 int uhf_data_append(UHFData* uhf_data, uint8_t data);
 void uhf_data_reset(UHFData* uhf_data);
+uint8_t uhf_data_calculate_checksum(UHFData* uhf_data);
+bool uhf_data_verfiy_checksum(UHFData* uhf_data);
 void uhf_data_free(UHFData* uhf_data);
 
 UHFResponseData* uhf_response_data_alloc();
 UHFData* uhf_response_data_add_new_uhf_data(UHFResponseData* uhf_response_data);
 UHFData* uhf_response_data_get_uhf_data(UHFResponseData* uhf_response_data, uint index);
 void uhf_response_data_reset(UHFResponseData* uhf_response_data);
-void uhf_response_data_free(UHFResponseData* uhf_response_data);
+void uhf_response_data_free(UHFResponseData* uhf_response_data);
+
+UHFTag* uhf_tag_alloc();
+void uhf_tag_reset(UHFTag* uhf_tag);
+void uhf_tag_set_epc(UHFTag* uhf_tag, uint8_t* data, size_t length);
+void uhf_tag_set_tid(UHFTag* uhf_tag, uint8_t* data, size_t length);
+void uhf_tag_set_user(UHFTag* uhf_tag, uint8_t* data, size_t length);
+void uhf_tag_free(UHFTag* uhf_tag);

+ 325 - 0
uhf_device.c

@@ -0,0 +1,325 @@
+#include "uhf_device.h"
+
+#include <toolbox/path.h>
+#include <flipper_format/flipper_format.h>
+#include <uhf_rfid_icons.h>
+
+#define TAG "UHFDevice"
+
+static const char* uhf_file_header = "Flipper UHF RFID device";
+static const uint32_t uhf_file_version = 1;
+// static const uint8_t bank_data_start = 20;
+// static const uint8_t bank_data_length = 16;
+
+UHFDevice* uhf_device_alloc() {
+    UHFDevice* uhf_device = malloc(sizeof(UHFDevice));
+    uhf_device->storage = furi_record_open(RECORD_STORAGE);
+    uhf_device->dialogs = furi_record_open(RECORD_DIALOGS);
+    uhf_device->load_path = furi_string_alloc();
+    return uhf_device;
+}
+
+void uhf_device_set_name(UHFDevice* dev, const char* name) {
+    furi_assert(dev);
+
+    strlcpy(dev->dev_name, name, UHF_DEV_NAME_MAX_LEN);
+}
+
+static bool uhf_device_save_file(
+    UHFDevice* dev,
+    const char* dev_name,
+    const char* folder,
+    const char* extension,
+    bool use_load_path) {
+    furi_assert(dev);
+
+    UHFTag* uhf_tag = dev->uhf_tag;
+    bool saved = false;
+    FlipperFormat* file = flipper_format_file_alloc(dev->storage);
+    FuriString* temp_str;
+    temp_str = furi_string_alloc();
+    do {
+        if(use_load_path && !furi_string_empty(dev->load_path)) {
+            // Get directory name
+            path_extract_dirname(furi_string_get_cstr(dev->load_path), temp_str);
+            // Make path to file to save
+            furi_string_cat_printf(temp_str, "/%s%s", dev_name, extension);
+        } else {
+            // First remove uhf device file if it was saved
+            furi_string_printf(temp_str, "%s/%s%s", folder, dev_name, extension);
+        }
+        // Open file
+        if(!flipper_format_file_open_always(file, furi_string_get_cstr(temp_str))) break;
+
+        // Write header
+        if(!flipper_format_write_header_cstr(file, uhf_file_header, uhf_file_version)) break;
+
+        // Reserved bank might be added
+        // todo : maybe
+        uint32_t temp_arr[1];
+        // write epc
+        temp_arr[0] = uhf_tag->epc_length;
+        if(!flipper_format_write_uint32(file, UHF_EPC_BANK_LENGTH_LABEL, temp_arr, 1)) break;
+        if(!flipper_format_write_hex(file, UHF_EPC_BANK_LABEL, uhf_tag->epc, uhf_tag->epc_length))
+            break;
+        // write tid
+        temp_arr[0] = uhf_tag->tid_length;
+        if(!flipper_format_write_uint32(file, UHF_TID_BANK_LENGTH_LABEL, temp_arr, 1)) break;
+        if(!flipper_format_write_hex(file, UHF_TID_BANK_LABEL, uhf_tag->tid, uhf_tag->tid_length))
+            break;
+        // write user
+        temp_arr[0] = uhf_tag->user_length;
+        if(!flipper_format_write_uint32(file, UHF_USER_BANK_LENGTH_LABEL, temp_arr, 1)) break;
+        if(!flipper_format_write_hex(
+               file, UHF_USER_BANK_LABEL, uhf_tag->user, uhf_tag->user_length))
+            break;
+        saved = true;
+    } while(0);
+
+    if(!saved) {
+        dialog_message_show_storage_error(dev->dialogs, "Can not save\nfile");
+    }
+    furi_string_free(temp_str);
+    flipper_format_free(file);
+    return saved;
+}
+
+bool uhf_device_save(UHFDevice* dev, const char* dev_name) {
+    return uhf_device_save_file(
+        dev, dev_name, STORAGE_APP_DATA_PATH_PREFIX, UHF_APP_EXTENSION, true);
+
+    return false;
+}
+// uncomment
+
+static bool uhf_device_load_data(UHFDevice* dev, FuriString* path, bool show_dialog) {
+    bool parsed = false;
+    FlipperFormat* file = flipper_format_file_alloc(dev->storage);
+    // UHFResponseData* uhf_response_data = dev->dev_data;
+    FuriString* temp_str;
+    temp_str = furi_string_alloc();
+    bool deprecated_version = false;
+    UHFTag* uhf_tag = dev->uhf_tag;
+    uhf_tag_reset(uhf_tag);
+    uint32_t temp_arr[1];
+    if(dev->loading_cb) {
+        dev->loading_cb(dev->loading_cb_ctx, true);
+    }
+
+    do {
+        if(!flipper_format_file_open_existing(file, furi_string_get_cstr(path))) break;
+
+        // Read and verify file header
+        uint32_t version = 0;
+        if(!flipper_format_read_header(file, temp_str, &version)) break;
+        if(furi_string_cmp_str(temp_str, uhf_file_header) || (version != uhf_file_version)) {
+            deprecated_version = true;
+            break;
+        }
+        // read epc
+        if(!flipper_format_read_uint32(file, UHF_EPC_BANK_LENGTH_LABEL, temp_arr, 1)) break;
+        uhf_tag->epc_length = temp_arr[0];
+        if(!flipper_format_read_hex(file, UHF_EPC_BANK_LABEL, uhf_tag->epc, uhf_tag->epc_length))
+            break;
+
+        // read tid
+        if(!flipper_format_read_uint32(file, UHF_TID_BANK_LENGTH_LABEL, temp_arr, 1)) break;
+        uhf_tag->tid_length = temp_arr[0];
+        if(!flipper_format_read_hex(file, UHF_TID_BANK_LABEL, uhf_tag->tid, uhf_tag->tid_length))
+            break;
+
+        // read user
+        if(!flipper_format_read_uint32(file, UHF_USER_BANK_LENGTH_LABEL, temp_arr, 1)) break;
+        uhf_tag->user_length = temp_arr[0];
+        if(!flipper_format_read_hex(file, UHF_USER_BANK_LABEL, uhf_tag->user, uhf_tag->user_length))
+            break;
+
+        parsed = true;
+    } while(false);
+
+    if(dev->loading_cb) {
+        dev->loading_cb(dev->loading_cb_ctx, false);
+    }
+
+    if((!parsed) && (show_dialog)) {
+        if(deprecated_version) {
+            dialog_message_show_storage_error(dev->dialogs, "File format deprecated");
+        } else {
+            dialog_message_show_storage_error(dev->dialogs, "Can not parse\nfile");
+        }
+    }
+
+    furi_string_free(temp_str);
+    flipper_format_free(file);
+
+    return parsed;
+}
+
+// void picopass_device_clear(UHFDevice* dev) {
+//     furi_assert(dev);
+
+//     picopass_device_data_clear(&dev->dev_data);
+//     memset(&dev->dev_data, 0, sizeof(dev->dev_data));
+//     dev->format = PicopassDeviceSaveFormatHF;
+//     furi_string_reset(dev->load_path);
+// }
+
+void uhf_device_free(UHFDevice* uhf_dev) {
+    furi_assert(uhf_dev);
+    furi_record_close(RECORD_STORAGE);
+    furi_record_close(RECORD_DIALOGS);
+    furi_string_free(uhf_dev->load_path);
+    free(uhf_dev);
+}
+
+bool uhf_file_select(UHFDevice* dev) {
+    furi_assert(dev);
+
+    FuriString* uhf_app_folder;
+    uhf_app_folder = furi_string_alloc_set(STORAGE_APP_DATA_PATH_PREFIX);
+
+    DialogsFileBrowserOptions browser_options;
+    dialog_file_browser_set_basic_options(&browser_options, UHF_APP_EXTENSION, &I_Nfc_10px);
+    browser_options.base_path = STORAGE_APP_DATA_PATH_PREFIX;
+
+    bool res =
+        dialog_file_browser_show(dev->dialogs, dev->load_path, uhf_app_folder, &browser_options);
+
+    furi_string_free(uhf_app_folder);
+    if(res) {
+        FuriString* filename;
+        filename = furi_string_alloc();
+        path_extract_filename(dev->load_path, filename, true);
+        strncpy(dev->dev_name, furi_string_get_cstr(filename), UHF_DEV_NAME_MAX_LEN);
+        res = uhf_device_load_data(dev, dev->load_path, true);
+        if(res) {
+            uhf_device_set_name(dev, dev->dev_name);
+        }
+        furi_string_free(filename);
+    }
+
+    return res;
+}
+
+// void uhf_device_data_clear(UHFDevice* dev_data) {
+//     for(size_t i = 0; i < PICOPASS_MAX_APP_LIMIT; i++) {
+//         memset(dev_data->AA1[i].data, 0, sizeof(dev_data->AA1[i].data));
+//     }
+//     dev_data->pacs.legacy = false;
+//     dev_data->pacs.se_enabled = false;
+//     dev_data->pacs.elite_kdf = false;
+//     dev_data->pacs.pin_length = 0;
+// }
+
+bool uhf_device_delete(UHFDevice* dev, bool use_load_path) {
+    furi_assert(dev);
+
+    bool deleted = false;
+    FuriString* file_path;
+    file_path = furi_string_alloc();
+
+    do {
+        // Delete original file
+        if(use_load_path && !furi_string_empty(dev->load_path)) {
+            furi_string_set(file_path, dev->load_path);
+        } else {
+            furi_string_printf(file_path, APP_DATA_PATH("%s%s"), dev->dev_name, UHF_APP_EXTENSION);
+        }
+        if(!storage_simply_remove(dev->storage, furi_string_get_cstr(file_path))) break;
+        deleted = true;
+    } while(0);
+
+    if(!deleted) {
+        dialog_message_show_storage_error(dev->dialogs, "Can not remove file");
+    }
+
+    furi_string_free(file_path);
+    return deleted;
+}
+
+void uhf_device_set_loading_callback(UHFDevice* dev, UHFLoadingCallback callback, void* context) {
+    furi_assert(dev);
+
+    dev->loading_cb = callback;
+    dev->loading_cb_ctx = context;
+}
+
+// ReturnCode picopass_device_decrypt(uint8_t* enc_data, uint8_t* dec_data) {
+//     uint8_t key[32] = {0};
+//     memcpy(key, picopass_iclass_decryptionkey, sizeof(picopass_iclass_decryptionkey));
+//     mbedtls_des3_context ctx;
+//     mbedtls_des3_init(&ctx);
+//     mbedtls_des3_set2key_dec(&ctx, key);
+//     mbedtls_des3_crypt_ecb(&ctx, enc_data, dec_data);
+//     mbedtls_des3_free(&ctx);
+//     return ERR_NONE;
+// }
+
+// ReturnCode picopass_device_parse_credential(PicopassBlock* AA1, PicopassPacs* pacs) {
+//     ReturnCode err;
+
+//     pacs->biometrics = AA1[6].data[4];
+//     pacs->pin_length = AA1[6].data[6] & 0x0F;
+//     pacs->encryption = AA1[6].data[7];
+
+//     if(pacs->encryption == PicopassDeviceEncryption3DES) {
+//         FURI_LOG_D(TAG, "3DES Encrypted");
+//         err = picopass_device_decrypt(AA1[7].data, pacs->credential);
+//         if(err != ERR_NONE) {
+//             FURI_LOG_E(TAG, "decrypt error %d", err);
+//             return err;
+//         }
+
+//         err = picopass_device_decrypt(AA1[8].data, pacs->pin0);
+//         if(err != ERR_NONE) {
+//             FURI_LOG_E(TAG, "decrypt error %d", err);
+//             return err;
+//         }
+
+//         err = picopass_device_decrypt(AA1[9].data, pacs->pin1);
+//         if(err != ERR_NONE) {
+//             FURI_LOG_E(TAG, "decrypt error %d", err);
+//             return err;
+//         }
+//     } else if(pacs->encryption == PicopassDeviceEncryptionNone) {
+//         FURI_LOG_D(TAG, "No Encryption");
+//         memcpy(pacs->credential, AA1[7].data, PICOPASS_BLOCK_LEN);
+//         memcpy(pacs->pin0, AA1[8].data, PICOPASS_BLOCK_LEN);
+//         memcpy(pacs->pin1, AA1[9].data, PICOPASS_BLOCK_LEN);
+//     } else if(pacs->encryption == PicopassDeviceEncryptionDES) {
+//         FURI_LOG_D(TAG, "DES Encrypted");
+//     } else {
+//         FURI_LOG_D(TAG, "Unknown encryption");
+//     }
+
+//     pacs->sio = (AA1[10].data[0] == 0x30); // rough check
+
+//     return ERR_NONE;
+// }
+
+// ReturnCode picopass_device_parse_wiegand(uint8_t* data, PicopassWiegandRecord* record) {
+//     uint32_t* halves = (uint32_t*)data;
+//     if(halves[0] == 0) {
+//         uint8_t leading0s = __builtin_clz(REVERSE_BYTES_U32(halves[1]));
+//         record->bitLength = 31 - leading0s;
+//     } else {
+//         uint8_t leading0s = __builtin_clz(REVERSE_BYTES_U32(halves[0]));
+//         record->bitLength = 63 - leading0s;
+//     }
+//     FURI_LOG_D(TAG, "bitLength: %d", record->bitLength);
+
+//     if(record->bitLength == 26) {
+//         uint8_t* v4 = data + 4;
+//         uint32_t bot = v4[3] | (v4[2] << 8) | (v4[1] << 16) | (v4[0] << 24);
+
+//         record->CardNumber = (bot >> 1) & 0xFFFF;
+//         record->FacilityCode = (bot >> 17) & 0xFF;
+//         FURI_LOG_D(TAG, "FC: %u CN: %u", record->FacilityCode, record->CardNumber);
+//         record->valid = true;
+//     } else {
+//         record->CardNumber = 0;
+//         record->FacilityCode = 0;
+//         record->valid = false;
+//     }
+//     return ERR_NONE;
+// }

+ 65 - 0
uhf_device.h

@@ -0,0 +1,65 @@
+#pragma once
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <storage/storage.h>
+#include <dialogs/dialogs.h>
+#include <mbedtls/des.h>
+#include "uhf_data.h"
+
+// #include "rfal_picopass.h"
+
+#define UHF_DEV_NAME_MAX_LEN 22
+// #define PICOPASS_READER_DATA_MAX_SIZE 64
+// #define PICOPASS_BLOCK_LEN 8
+// #define PICOPASS_MAX_APP_LIMIT 32
+#define UHF_BANK_DOES_NOT_EXIST                                                                   \
+    (uint8_t[]) {                                                                                 \
+        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, \
+            0xFF                                                                                  \
+    }
+
+#define UHF_EPC_BANK_LENGTH_LABEL "EPC_LENGTH"
+#define UHF_TID_BANK_LENGTH_LABEL "TID_LENGTH"
+#define UHF_USER_BANK_LENGTH_LABEL "USER_LENGTH"
+#define UHF_RFU_BANK_LABEL "RFU"
+#define UHF_EPC_BANK_LABEL "EPC"
+#define UHF_TID_BANK_LABEL "TID"
+#define UHF_USER_BANK_LABEL "USER"
+
+#define UHF_APP_EXTENSION ".uhf"
+// #define PICOPASS_APP_SHADOW_EXTENSION ".pas"
+
+typedef void (*UHFLoadingCallback)(void* context, bool state);
+
+typedef struct {
+    Storage* storage;
+    DialogsApp* dialogs;
+    // UHFResponseData* dev_data;
+    UHFTag* uhf_tag;
+    char dev_name[UHF_DEV_NAME_MAX_LEN + 1];
+    FuriString* load_path;
+    UHFLoadingCallback loading_cb;
+    void* loading_cb_ctx;
+} UHFDevice;
+
+UHFDevice* uhf_device_alloc();
+
+void uhf_device_free(UHFDevice* uhf_dev);
+
+void uhf_device_set_name(UHFDevice* dev, const char* name);
+
+bool uhf_device_save(UHFDevice* dev, const char* dev_name);
+
+bool uhf_file_select(UHFDevice* dev);
+
+// void uhf_device_data_clear(PicopassDeviceData* dev_data);
+
+void uhf_device_clear(UHFDevice* dev);
+
+bool uhf_device_delete(UHFDevice* dev, bool use_load_path);
+
+void uhf_device_set_loading_callback(UHFDevice* dev, UHFLoadingCallback callback, void* context);
+
+// ReturnCode uhf_device_parse_credential(PicopassBlock* AA1, PicopassPacs* pacs);
+// ReturnCode uhf_device_parse_wiegand(uint8_t* data, PicopassWiegandRecord* record);

+ 124 - 95
uhf_worker.c

@@ -2,28 +2,12 @@
 #include "uhf_cmd.h"
 
 #define CB_DELAY 50
-// inner functions
-uint8_t calculate_checksum(UHFData* uhf_data) {
-    // CheckSum8 Modulo 256
-    // Sum of Bytes % 256
-    uint8_t sum_val = 0x00;
-    size_t length = uhf_data->length - 2;
-    for(size_t i = 1; i < length; i++) {
-        sum_val += uhf_data->data[i];
-    }
-    return sum_val % 256;
-}
-bool validate_checksum(UHFData* uhf_data) {
-    uint8_t data_checksum = uhf_data->data[uhf_data->length - 2];
-    uint8_t actual_checksum = calculate_checksum(uhf_data);
-    return data_checksum == actual_checksum;
-}
+
 // uart callback functions
 void module_rx_callback(UartIrqEvent event, uint8_t data, void* ctx) {
     UNUSED(event);
     UHFData* uhf_data = ctx;
     uhf_data_append(uhf_data, data);
-    // FURI_LOG_E("module_rx_callback", "%02x", data);
 }
 
 // yrm100 module commands
@@ -48,14 +32,77 @@ UHFWorkerEvent verify_module_connected(UHFWorker* uhf_worker) {
     furi_hal_uart_set_irq_cb(FuriHalUartIdUSART1, module_rx_callback, manufacturer);
     furi_hal_uart_tx(FuriHalUartIdUSART1, CMD_MANUFACTURERS.cmd, CMD_MANUFACTURERS.length);
     furi_delay_ms(CB_DELAY);
-    // FURI_LOG_E("log", "done sending tx");
+    // verify that we received all data
     if(!hardware_version->end || !software_version->end || !manufacturer->end) {
         return UHFWorkerEventFail;
     }
+    // verify all data was received correctly
+    if(!uhf_data_verfiy_checksum(hardware_version) ||
+       !uhf_data_verfiy_checksum(software_version) || !uhf_data_verfiy_checksum(manufacturer))
+        return UHFWorkerEventFail;
+
     return UHFWorkerEventSuccess;
 }
 
+static uint8_t get_epc_length_in_bits(uint8_t pc) {
+    uint8_t epc_length = pc;
+    epc_length >>= 3;
+    return (uint8_t)epc_length * 16; // x-words * 16 bits
+}
+
+static bool send_set_select_command(UHFData* selected_tag, UHFBank bank) {
+    bool success = false;
+    // Set select
+    UHFData* select_cmd = uhf_data_alloc();
+    select_cmd->start = true;
+    select_cmd->length = CMD_SET_SELECT_PARAMETER.length;
+    memcpy((void*)&select_cmd->data, (void*)&CMD_SET_SELECT_PARAMETER.cmd[0], select_cmd->length);
+    // set select param
+    size_t mask_length_bits = (size_t)get_epc_length_in_bits(selected_tag->data[6]);
+    size_t mask_length_bytes = (size_t)mask_length_bits / 8;
+    select_cmd->data[5] = bank; // 0x00=rfu, 0x01=epc, 0x10=tid, 0x11=user
+    // set ptr
+    select_cmd->data[9] = 0x20; // epc data begins after 0x20
+    // set mask length
+    select_cmd->data[10] = mask_length_bits;
+    // set mask starting position
+    select_cmd->length = 12;
+    // set mask
+    for(size_t i = 0; i < mask_length_bytes; i++) {
+        uhf_data_append(select_cmd, selected_tag->data[8 + i]);
+    }
+    uhf_data_append(select_cmd, 0x00); // add checksum section
+    uhf_data_append(select_cmd, FRAME_END); // command end
+    // add checksum
+    select_cmd->data[select_cmd->length - 2] = uhf_data_calculate_checksum(select_cmd);
+    UHFData* select_response = uhf_data_alloc();
+    furi_hal_uart_set_irq_cb(FuriHalUartIdUSART1, module_rx_callback, select_response);
+    furi_hal_uart_tx(FuriHalUartIdUSART1, select_cmd->data, select_cmd->length);
+    furi_delay_ms(CB_DELAY);
+
+    success = select_response->data[5] == 0x00;
+
+    uhf_data_free(select_cmd);
+    uhf_data_free(select_response);
+
+    return success;
+}
+
+static bool read_bank(UHFData* read_bank_cmd, UHFData* response_bank, UHFBank bank) {
+    furi_hal_uart_set_irq_cb(FuriHalUartIdUSART1, module_rx_callback, response_bank);
+    read_bank_cmd->data[9] = bank;
+    read_bank_cmd->data[read_bank_cmd->length - 2] = uhf_data_calculate_checksum(read_bank_cmd);
+    uhf_data_reset(response_bank);
+    furi_hal_uart_tx(FuriHalUartIdUSART1, read_bank_cmd->data, read_bank_cmd->length);
+    furi_delay_ms(CB_DELAY);
+    return response_bank->data[2] == read_bank_cmd->data[2];
+}
+
 UHFWorkerEvent read_single_card(UHFWorker* uhf_worker) {
+    // debug
+    // FuriString* temp_str;
+    // temp_str = furi_string_alloc();
+    // e-debug
     UHFResponseData* uhf_response_data = uhf_worker->response_data;
     uhf_response_data_reset(uhf_response_data);
     UHFData* raw_read_data = uhf_response_data_get_uhf_data(uhf_response_data, 0);
@@ -77,27 +124,15 @@ UHFWorkerEvent read_single_card(UHFWorker* uhf_worker) {
                 break; // read success
         }
     }
-    // Set select
-    UHFData* select_cmd = uhf_data_alloc();
-    select_cmd->length = CMD_SET_SELECT_PARAMETER.length;
-    memcpy((void*)&select_cmd->data, (void*)&CMD_SET_SELECT_PARAMETER.cmd[0], select_cmd->length);
-    // set select param
-    select_cmd->data[5] = 0x01; // 0x00=rfu, 0x01=epc, 0x10=tid, 0x11=user
-    // set ptr
-    select_cmd->data[9] = 0x20;
-    // set mask
-    for(int i = 0; i < 12; i++) // 96 bits, 12 bytes
-    {
-        select_cmd->data[12 + i] = raw_read_data->data[8 + i];
-    }
-    // checksum
-    select_cmd->data[select_cmd->length - 2] = calculate_checksum(select_cmd);
-    UHFData* select_response = uhf_response_data_add_new_uhf_data(uhf_response_data);
-    furi_hal_uart_set_irq_cb(FuriHalUartIdUSART1, module_rx_callback, select_response);
-    furi_hal_uart_tx(FuriHalUartIdUSART1, select_cmd->data, select_cmd->length);
-    furi_delay_ms(CB_DELAY);
 
-    if(select_response->data[5] != 0x00) return UHFWorkerEventFail;
+    // todo : rfu ?
+    UHFTag* uhf_tag = uhf_worker->uhf_tag;
+    uhf_tag_reset(uhf_tag);
+
+    // add to tag object
+    UHFData* raw_bank_data = uhf_data_alloc();
+    size_t epc_length = (size_t)get_epc_length_in_bits(raw_read_data->data[6]) / 8;
+    size_t offset = (size_t)(8 + epc_length);
 
     UHFData* read_bank_cmd = uhf_data_alloc();
     read_bank_cmd->length = CMD_READ_LABEL_DATA_STORAGE.length;
@@ -106,69 +141,63 @@ UHFWorkerEvent read_single_card(UHFWorker* uhf_worker) {
         (void*)&CMD_READ_LABEL_DATA_STORAGE.cmd[0],
         read_bank_cmd->length);
 
-    // int retry = 10;
-    // read rfu bank
-    UHFData* rfu_bank = select_response;
-    furi_hal_uart_set_irq_cb(FuriHalUartIdUSART1, module_rx_callback, rfu_bank);
-    read_bank_cmd->data[9] = 0x00;
-    read_bank_cmd->data[read_bank_cmd->length - 2] = calculate_checksum(read_bank_cmd);
-    uhf_data_reset(rfu_bank);
-    furi_hal_uart_tx(FuriHalUartIdUSART1, read_bank_cmd->data, read_bank_cmd->length);
-    furi_delay_ms(CB_DELAY);
-    if(rfu_bank->data[2] != read_bank_cmd->data[2]) {
-        uhf_data_reset(rfu_bank);
-    }
+    if(!send_set_select_command(raw_read_data, EPC_BANK)) return UHFWorkerEventFail;
 
-    // read epc bank
-    UHFData* epc_bank = uhf_response_data_add_new_uhf_data(uhf_response_data);
-    furi_hal_uart_set_irq_cb(FuriHalUartIdUSART1, module_rx_callback, epc_bank);
-    read_bank_cmd->data[9] = 0x01;
-    read_bank_cmd->data[read_bank_cmd->length - 2] = calculate_checksum(read_bank_cmd);
-    uhf_data_reset(epc_bank);
-    furi_hal_uart_tx(FuriHalUartIdUSART1, read_bank_cmd->data, read_bank_cmd->length);
-    furi_delay_ms(CB_DELAY);
-    if(epc_bank->data[2] != read_bank_cmd->data[2]) {
-        uhf_data_reset(epc_bank);
-    }
-
-    // read tid bank
-    UHFData* tid_bank = uhf_response_data_add_new_uhf_data(uhf_response_data);
-    furi_hal_uart_set_irq_cb(FuriHalUartIdUSART1, module_rx_callback, tid_bank);
-    read_bank_cmd->data[9] = 0x02;
-    read_bank_cmd->data[read_bank_cmd->length - 2] = calculate_checksum(read_bank_cmd);
-    uhf_data_reset(tid_bank);
-    furi_hal_uart_tx(FuriHalUartIdUSART1, read_bank_cmd->data, read_bank_cmd->length);
-    furi_delay_ms(CB_DELAY);
-    if(tid_bank->data[2] != read_bank_cmd->data[2]) {
-        uhf_data_reset(tid_bank);
-    }
-
-    // read user bank
-    UHFData* user_bank = uhf_response_data_add_new_uhf_data(uhf_response_data);
-    furi_hal_uart_set_irq_cb(FuriHalUartIdUSART1, module_rx_callback, user_bank);
-    read_bank_cmd->data[9] = 0x03;
-    read_bank_cmd->data[read_bank_cmd->length - 2] = calculate_checksum(read_bank_cmd);
-    uhf_data_reset(user_bank);
-    furi_hal_uart_tx(FuriHalUartIdUSART1, read_bank_cmd->data, read_bank_cmd->length);
-    furi_delay_ms(CB_DELAY);
-    if(user_bank->data[2] != read_bank_cmd->data[2]) {
-        uhf_data_reset(user_bank);
-    }
+    int retry = 3;
+    do {
+        if(read_bank(read_bank_cmd, raw_bank_data, EPC_BANK)) {
+            uhf_tag_set_epc(uhf_tag, raw_bank_data->data + offset, epc_length + 2);
+            FURI_LOG_E("TAG", "epc read");
+            break;
+        }
 
-    // for(int i = 0; i < (int)user_bank->length; i++) {
-    //     FURI_LOG_E("user_bank", "data[%d]=%02x", i, user_bank->data[i]);
+    } while(retry--);
+    // // debug
+    // furi_string_reset(temp_str);
+    // for(size_t i = 0; i < raw_bank_data->length; i++) {
+    //     furi_string_cat_printf(temp_str, "%02x ", raw_bank_data->data[i]);
     // }
-    // for(int i = 0; i < (int)rfu_bank->length; i++) {
-    //     FURI_LOG_E("rfu_bank", "data[%d]=%02x", i, rfu_bank->data[i]);
-    // }
-    // for(int i = 0; i < (int)epc_bank->length; i++) {
-    //     FURI_LOG_E("epc_bank", "data[%d]=%02x", i, epc_bank->data[i]);
+    // FURI_LOG_E("TAG", "data = %s", furi_string_get_cstr(temp_str));
+    // // e-debug
+    uhf_data_reset(raw_bank_data);
+    retry = 3;
+    do {
+        if(read_bank(read_bank_cmd, raw_bank_data, TID_BANK)) {
+            uhf_tag_set_tid(uhf_tag, raw_bank_data->data + offset, 16);
+            break;
+        }
+    } while(retry--);
+    // // debug
+    // furi_string_reset(temp_str);
+    // for(size_t i = 0; i < raw_bank_data->length; i++) {
+    //     furi_string_cat_printf(temp_str, "%02x ", raw_bank_data->data[i]);
     // }
-    // for(int i = 0; i < (int)tid_bank->length; i++) {
-    //     FURI_LOG_E("tid_bank", "data[%d]=%02x", i, tid_bank->data[i]);
+    // FURI_LOG_E("TAG", "data = %s", furi_string_get_cstr(temp_str));
+    // // e-debug
+    uhf_data_reset(raw_bank_data);
+    retry = 3;
+    if(raw_read_data->data[6] & 0x04) {
+        do {
+            if(read_bank(read_bank_cmd, raw_bank_data, USER_BANK)) {
+                uhf_tag_set_user(uhf_tag, raw_bank_data->data + offset, 16);
+                break;
+            }
+        } while(retry--);
+    }
+    // // debug
+    // furi_string_reset(temp_str);
+    // for(size_t i = 0; i < raw_bank_data->length; i++) {
+    //     furi_string_cat_printf(temp_str, "%02x ", raw_bank_data->data[i]);
     // }
-    uhf_data_free(select_cmd);
+    // FURI_LOG_E("TAG", "data = %s", furi_string_get_cstr(temp_str));
+    // // e-debug
+    uhf_data_reset(raw_bank_data);
+    uhf_data_free(raw_bank_data);
     uhf_data_free(read_bank_cmd);
+    // debug
+    // furi_string_free(temp_str);
+    // e-debug
+
     return UHFWorkerEventSuccess;
 }
 

+ 2 - 3
uhf_worker.h

@@ -31,6 +31,7 @@ typedef void (*UHFWorkerCallback)(UHFWorkerEvent event, void* ctx);
 typedef struct UHFWorker {
     FuriThread* thread;
     UHFResponseData* response_data;
+    UHFTag* uhf_tag;
     UHFWorkerCallback callback;
     UHFWorkerState state;
     void* ctx;
@@ -45,6 +46,4 @@ void uhf_worker_start(
     UHFWorkerCallback callback,
     void* ctx);
 void uhf_worker_stop(UHFWorker* uhf_worker);
-void uhf_worker_free(UHFWorker* uhf_worker);
-uint8_t calculate_checksum(UHFData* uhf_data);
-bool validate_checksum(UHFData* uhf_data);
+void uhf_worker_free(UHFWorker* uhf_worker);