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

[FL-1547], [FL-1500] NFC app v1 (#593)

* nfc: remove mifare read debug view and scene
* nfc: change mifare ultralight data structure
* mifare_ultralight: add more commands
* nfc: add emulate mifare ul scene
* nfc: rework data structures, remove debug scenes and views
* nfc: add read emv scenes
* nfc: mifare emulation wip
* nfc cli: increase detecting time
* nfc: save nfc files with new format
* nfc: store Mifare Ultralight
* nfc: start loading mifare ultralight
* nfc: add delete scenes
* nfc: add edit UID and name
* nfc: finish parsing uid and mifare ul data
* nfc: delete success fix
* gui_widget: introduce GuiWidget
* gui_widget: add string element
* gui_widget: add button element
* gui_widget: move free elements into gui_widget
* nfc: rework info scene with GuiWidget
* nfc: rework device info scene
* nfc: rework delete scene gui
* nfc: add compatible script support
* nfc: rework emv reading scenes
* nfc: rework bank card save
* nfc: add bank card custom view
* gui_widget: add icon element
* nfc: add icon to bank card
* nfc: start worker after switching view

Co-authored-by: あく <alleteam@gmail.com>
gornekich 4 лет назад
Родитель
Сommit
49af516ec7
64 измененных файлов с 2189 добавлено и 1060 удалено
  1. 84 0
      applications/nfc/gui_widget/gui_element_button.c
  2. 24 0
      applications/nfc/gui_widget/gui_element_button.h
  3. 21 0
      applications/nfc/gui_widget/gui_element_i.h
  4. 51 0
      applications/nfc/gui_widget/gui_element_icon.c
  5. 13 0
      applications/nfc/gui_widget/gui_element_icon.h
  6. 72 0
      applications/nfc/gui_widget/gui_element_string.c
  7. 21 0
      applications/nfc/gui_widget/gui_element_string.h
  8. 141 0
      applications/nfc/gui_widget/gui_widget.c
  9. 76 0
      applications/nfc/gui_widget/gui_widget.h
  10. 37 62
      applications/nfc/nfc.c
  11. 3 3
      applications/nfc/nfc_cli.c
  12. 344 56
      applications/nfc/nfc_device.c
  13. 27 13
      applications/nfc/nfc_device.h
  14. 16 0
      applications/nfc/nfc_device_i.h
  15. 13 17
      applications/nfc/nfc_i.h
  16. 1 20
      applications/nfc/nfc_types.h
  17. 187 65
      applications/nfc/nfc_worker.c
  18. 4 12
      applications/nfc/nfc_worker.h
  19. 7 3
      applications/nfc/nfc_worker_i.h
  20. 17 10
      applications/nfc/scenes/nfc_scene_card_menu.c
  21. 9 5
      applications/nfc/scenes/nfc_scene_config.h
  22. 0 14
      applications/nfc/scenes/nfc_scene_debug_detect.c
  23. 0 14
      applications/nfc/scenes/nfc_scene_debug_emulate.c
  24. 0 72
      applications/nfc/scenes/nfc_scene_debug_menu.c
  25. 0 14
      applications/nfc/scenes/nfc_scene_debug_read_emv.c
  26. 0 14
      applications/nfc/scenes/nfc_scene_debug_read_mifare_ul.c
  27. 95 0
      applications/nfc/scenes/nfc_scene_delete.c
  28. 47 0
      applications/nfc/scenes/nfc_scene_delete_success.c
  29. 185 0
      applications/nfc/scenes/nfc_scene_device_info.c
  30. 60 0
      applications/nfc/scenes/nfc_scene_emulate_mifare_ul.c
  31. 6 8
      applications/nfc/scenes/nfc_scene_emulate_uid.c
  32. 1 1
      applications/nfc/scenes/nfc_scene_file_select.c
  33. 4 3
      applications/nfc/scenes/nfc_scene_mifare_ul_menu.c
  34. 2 2
      applications/nfc/scenes/nfc_scene_not_implemented.c
  35. 7 10
      applications/nfc/scenes/nfc_scene_read_card.c
  36. 5 5
      applications/nfc/scenes/nfc_scene_read_card_success.c
  37. 54 0
      applications/nfc/scenes/nfc_scene_read_emv_app.c
  38. 69 0
      applications/nfc/scenes/nfc_scene_read_emv_app_success.c
  39. 57 0
      applications/nfc/scenes/nfc_scene_read_emv_data.c
  40. 89 0
      applications/nfc/scenes/nfc_scene_read_emv_data_success.c
  41. 9 8
      applications/nfc/scenes/nfc_scene_read_mifare_ul.c
  42. 10 16
      applications/nfc/scenes/nfc_scene_read_mifare_ul_success.c
  43. 56 0
      applications/nfc/scenes/nfc_scene_run_emv_app_confirm.c
  44. 8 6
      applications/nfc/scenes/nfc_scene_save_name.c
  45. 2 3
      applications/nfc/scenes/nfc_scene_save_success.c
  46. 9 7
      applications/nfc/scenes/nfc_scene_saved_menu.c
  47. 3 3
      applications/nfc/scenes/nfc_scene_scripts_menu.c
  48. 8 4
      applications/nfc/scenes/nfc_scene_set_atqa.c
  49. 8 3
      applications/nfc/scenes/nfc_scene_set_sak.c
  50. 6 4
      applications/nfc/scenes/nfc_scene_set_type.c
  51. 4 4
      applications/nfc/scenes/nfc_scene_set_uid.c
  52. 3 8
      applications/nfc/scenes/nfc_scene_start.c
  53. 65 0
      applications/nfc/views/bank_card.c
  54. 23 0
      applications/nfc/views/bank_card.h
  55. 0 142
      applications/nfc/views/nfc_detect.c
  56. 0 12
      applications/nfc/views/nfc_detect.h
  57. 0 79
      applications/nfc/views/nfc_emulate.c
  58. 0 12
      applications/nfc/views/nfc_emulate.h
  59. 0 133
      applications/nfc/views/nfc_emv.c
  60. 0 12
      applications/nfc/views/nfc_emv.h
  61. 0 162
      applications/nfc/views/nfc_mifare_ul.c
  62. 0 12
      applications/nfc/views/nfc_mifare_ul.h
  63. 83 3
      lib/nfc_protocols/mifare_ultralight.c
  64. 43 4
      lib/nfc_protocols/mifare_ultralight.h

+ 84 - 0
applications/nfc/gui_widget/gui_element_button.c

@@ -0,0 +1,84 @@
+#include "gui_element_i.h"
+#include "gui_element_button.h"
+#include "gui_widget.h"
+#include <gui/elements.h>
+#include <m-string.h>
+
+typedef struct {
+    GuiButtonType button_type;
+    string_t text;
+    ButtonCallback callback;
+    void* context;
+} GuiButtonModel;
+
+static void gui_button_draw(Canvas* canvas, GuiElement* element) {
+    furi_assert(canvas);
+    furi_assert(element);
+    GuiButtonModel* model = element->model;
+
+    canvas_set_color(canvas, ColorBlack);
+    canvas_set_font(canvas, FontSecondary);
+
+    if(model->button_type == GuiButtonTypeLeft) {
+        elements_button_left(canvas, string_get_cstr(model->text));
+    } else if(model->button_type == GuiButtonTypeRight) {
+        elements_button_right(canvas, string_get_cstr(model->text));
+    } else if(model->button_type == GuiButtonTypeCenter) {
+        elements_button_center(canvas, string_get_cstr(model->text));
+    }
+}
+
+static bool gui_button_input(InputEvent* event, GuiElement* element) {
+    GuiButtonModel* model = element->model;
+    bool consumed = false;
+
+    if((event->type == InputTypeShort) && model->callback) {
+        if((model->button_type == GuiButtonTypeLeft) && (event->key == InputKeyLeft)) {
+            model->callback(model->button_type, model->context);
+            consumed = true;
+        } else if((model->button_type == GuiButtonTypeRight) && (event->key == InputKeyRight)) {
+            model->callback(model->button_type, model->context);
+            consumed = true;
+        } else if((model->button_type == GuiButtonTypeCenter) && (event->key == InputKeyOk)) {
+            model->callback(model->button_type, model->context);
+            consumed = true;
+        }
+    }
+
+    return consumed;
+}
+
+static void gui_button_free(GuiElement* gui_button) {
+    furi_assert(gui_button);
+
+    GuiButtonModel* model = gui_button->model;
+    if(gui_button->parent != NULL) {
+        // TODO deattach element
+    }
+    string_clear(model->text);
+    free(gui_button->model);
+    free(gui_button);
+}
+
+GuiElement* gui_button_create(
+    GuiButtonType button_type,
+    const char* text,
+    ButtonCallback callback,
+    void* context) {
+    // Allocate and init model
+    GuiButtonModel* model = furi_alloc(sizeof(GuiButtonModel));
+    model->button_type = button_type;
+    model->callback = callback;
+    model->context = context;
+    string_init_set_str(model->text, text);
+
+    // Allocate and init Element
+    GuiElement* gui_button = furi_alloc(sizeof(GuiElement));
+    gui_button->parent = NULL;
+    gui_button->input = gui_button_input;
+    gui_button->draw = gui_button_draw;
+    gui_button->free = gui_button_free;
+    gui_button->model = model;
+
+    return gui_button;
+}

+ 24 - 0
applications/nfc/gui_widget/gui_element_button.h

@@ -0,0 +1,24 @@
+#pragma once
+#include <stdint.h>
+
+typedef struct GuiElement GuiElement;
+
+typedef enum {
+    GuiButtonTypeLeft,
+    GuiButtonTypeCenter,
+    GuiButtonTypeRight,
+} GuiButtonType;
+
+typedef void (*ButtonCallback)(GuiButtonType button, void* context);
+
+/** Allocate Button Element
+ * @param button_type GuiButtonType instance
+ * @param text text on allocated button
+ * @param callback ButtonCallback instance
+ * @param context pointer to context
+ */
+GuiElement* gui_button_create(
+    GuiButtonType button_type,
+    const char* text,
+    ButtonCallback callback,
+    void* context);

+ 21 - 0
applications/nfc/gui_widget/gui_element_i.h

@@ -0,0 +1,21 @@
+#pragma once
+#include <furi.h>
+#include <gui/view.h>
+
+typedef struct GuiElement GuiElement;
+typedef struct GuiWidget GuiWidget;
+
+struct GuiElement {
+    // generic draw and input callbacks
+    void (*draw)(Canvas* canvas, GuiElement* element);
+    bool (*input)(InputEvent* event, GuiElement* element);
+
+    // free callback
+    void (*free)(GuiElement* element);
+
+    // generic model holder
+    void* model;
+
+    // pointer to widget that hold our element
+    GuiWidget* parent;
+};

+ 51 - 0
applications/nfc/gui_widget/gui_element_icon.c

@@ -0,0 +1,51 @@
+#include "gui_element_i.h"
+#include "gui_element_icon.h"
+#include "gui_widget.h"
+
+#include <m-string.h>
+
+typedef struct {
+    uint8_t x;
+    uint8_t y;
+    const Icon* icon;
+} GuiIconModel;
+
+static void gui_icon_draw(Canvas* canvas, GuiElement* element) {
+    furi_assert(canvas);
+    furi_assert(element);
+    GuiIconModel* model = element->model;
+
+    if(model->icon) {
+        canvas_draw_icon(canvas, model->x, model->y, model->icon);
+    }
+}
+
+static void gui_icon_free(GuiElement* gui_icon) {
+    furi_assert(gui_icon);
+
+    if(gui_icon->parent != NULL) {
+        // TODO deattach element
+    }
+    free(gui_icon->model);
+    free(gui_icon);
+}
+
+GuiElement* gui_icon_create(uint8_t x, uint8_t y, const Icon* icon) {
+    furi_assert(icon);
+
+    // Allocate and init model
+    GuiIconModel* model = furi_alloc(sizeof(GuiIconModel));
+    model->x = x;
+    model->y = y;
+    model->icon = icon;
+
+    // Allocate and init Element
+    GuiElement* gui_icon = furi_alloc(sizeof(GuiElement));
+    gui_icon->parent = NULL;
+    gui_icon->input = NULL;
+    gui_icon->draw = gui_icon_draw;
+    gui_icon->free = gui_icon_free;
+    gui_icon->model = model;
+
+    return gui_icon;
+}

+ 13 - 0
applications/nfc/gui_widget/gui_element_icon.h

@@ -0,0 +1,13 @@
+#pragma once
+#include <stdint.h>
+#include <gui/canvas.h>
+
+typedef struct GuiElement GuiElement;
+
+/** Allocate GuiElement element
+ * @param x - x coordinate
+ * @param y - y coordinate
+ * @param icon Icon instance
+ * @return GuiElement instance
+ */
+GuiElement* gui_icon_create(uint8_t x, uint8_t y, const Icon* icon);

+ 72 - 0
applications/nfc/gui_widget/gui_element_string.c

@@ -0,0 +1,72 @@
+#include "gui_element_i.h"
+#include "gui_element_string.h"
+#include "gui_widget.h"
+
+#include <m-string.h>
+
+typedef struct {
+    uint8_t x;
+    uint8_t y;
+    Align horizontal;
+    Align vertical;
+    Font font;
+    string_t text;
+} GuiStringModel;
+
+static void gui_string_draw(Canvas* canvas, GuiElement* element) {
+    furi_assert(canvas);
+    furi_assert(element);
+    GuiStringModel* model = element->model;
+
+    if(string_size(model->text)) {
+        canvas_set_font(canvas, model->font);
+        canvas_draw_str_aligned(
+            canvas,
+            model->x,
+            model->y,
+            model->horizontal,
+            model->vertical,
+            string_get_cstr(model->text));
+    }
+}
+
+static void gui_string_free(GuiElement* gui_string) {
+    furi_assert(gui_string);
+
+    GuiStringModel* model = gui_string->model;
+    if(gui_string->parent != NULL) {
+        // TODO deattach element
+    }
+    string_clear(model->text);
+    free(gui_string->model);
+    free(gui_string);
+}
+
+GuiElement* gui_string_create(
+    uint8_t x,
+    uint8_t y,
+    Align horizontal,
+    Align vertical,
+    Font font,
+    const char* text) {
+    furi_assert(text);
+
+    // Allocate and init model
+    GuiStringModel* model = furi_alloc(sizeof(GuiStringModel));
+    model->x = x;
+    model->y = y;
+    model->horizontal = horizontal;
+    model->vertical = vertical;
+    model->font = font;
+    string_init_set_str(model->text, text);
+
+    // Allocate and init Element
+    GuiElement* gui_string = furi_alloc(sizeof(GuiElement));
+    gui_string->parent = NULL;
+    gui_string->input = NULL;
+    gui_string->draw = gui_string_draw;
+    gui_string->free = gui_string_free;
+    gui_string->model = model;
+
+    return gui_string;
+}

+ 21 - 0
applications/nfc/gui_widget/gui_element_string.h

@@ -0,0 +1,21 @@
+#pragma once
+#include <stdint.h>
+#include <gui/canvas.h>
+
+typedef struct GuiElement GuiElement;
+
+/** Allocate GuiElement element
+ * @param x - x coordinate
+ * @param y - y coordinate
+ * @param horizontal - Align instance
+ * @param vertical - Align instance
+ * @param font Font instance
+ * @return GuiElement instance
+ */
+GuiElement* gui_string_create(
+    uint8_t x,
+    uint8_t y,
+    Align horizontal,
+    Align vertical,
+    Font font,
+    const char* text);

+ 141 - 0
applications/nfc/gui_widget/gui_widget.c

@@ -0,0 +1,141 @@
+#include <furi.h>
+#include "gui_element_i.h"
+#include "gui_widget.h"
+
+#define MAX_GUI_ELEMENTS 8
+
+struct GuiWidget {
+    View* view;
+    void* context;
+};
+
+// TODO rework with M-LIB container
+typedef struct {
+    GuiElement* element[MAX_GUI_ELEMENTS];
+} GuiWidgetModel;
+
+static void gui_widget_view_draw_callback(Canvas* canvas, void* _model) {
+    GuiWidgetModel* model = _model;
+    canvas_clear(canvas);
+
+    for(uint8_t i = 0; i < MAX_GUI_ELEMENTS; i++) {
+        if(model->element[i] != NULL) {
+            if(model->element[i]->draw != NULL) {
+                model->element[i]->draw(canvas, model->element[i]);
+            }
+        }
+    };
+}
+
+static bool gui_widget_view_input_callback(InputEvent* event, void* context) {
+    GuiWidget* gui_widget = context;
+    bool consumed = false;
+
+    with_view_model(
+        gui_widget->view, (GuiWidgetModel * model) {
+            for(uint8_t i = 0; i < MAX_GUI_ELEMENTS; i++) {
+                if(model->element[i] != NULL) {
+                    if(model->element[i]->input != NULL) {
+                        consumed = model->element[i]->input(event, model->element[i]);
+                    }
+                }
+            };
+            return true;
+        });
+
+    return consumed;
+}
+
+GuiWidget* gui_widget_alloc() {
+    GuiWidget* gui_widget = furi_alloc(sizeof(GuiWidget));
+    gui_widget->view = view_alloc();
+    view_set_context(gui_widget->view, gui_widget);
+    view_allocate_model(gui_widget->view, ViewModelTypeLocking, sizeof(GuiWidgetModel));
+    view_set_draw_callback(gui_widget->view, gui_widget_view_draw_callback);
+    view_set_input_callback(gui_widget->view, gui_widget_view_input_callback);
+
+    with_view_model(
+        gui_widget->view, (GuiWidgetModel * model) {
+            for(uint8_t i = 0; i < MAX_GUI_ELEMENTS; i++) {
+                model->element[i] = NULL;
+            };
+            return true;
+        });
+
+    return gui_widget;
+}
+
+void gui_widget_free(GuiWidget* gui_widget) {
+    furi_assert(gui_widget);
+    gui_widget_clear(gui_widget);
+    view_free(gui_widget->view);
+    free(gui_widget);
+}
+
+void gui_widget_clear(GuiWidget* gui_widget) {
+    furi_assert(gui_widget);
+
+    with_view_model(
+        gui_widget->view, (GuiWidgetModel * model) {
+            for(uint8_t i = 0; i < MAX_GUI_ELEMENTS; i++) {
+                if(model->element[i]) {
+                    furi_assert(model->element[i]->free);
+                    model->element[i]->free(model->element[i]);
+                    model->element[i] = NULL;
+                }
+            };
+            return true;
+        });
+}
+
+View* gui_widget_get_view(GuiWidget* gui_widget) {
+    furi_assert(gui_widget);
+    return gui_widget->view;
+}
+
+void gui_widget_add_element(GuiWidget* gui_widget, GuiElement* element) {
+    furi_assert(gui_widget);
+    with_view_model(
+        gui_widget->view, (GuiWidgetModel * model) {
+            // add element to first null position
+            for(uint8_t i = 0; i < MAX_GUI_ELEMENTS; i++) {
+                if(model->element[i] == NULL) {
+                    model->element[i] = element;
+                    element->parent = gui_widget;
+                    break;
+                }
+            };
+            return true;
+        });
+}
+
+void gui_widget_add_string_element(
+    GuiWidget* gui_widget,
+    uint8_t x,
+    uint8_t y,
+    Align horizontal,
+    Align vertical,
+    Font font,
+    const char* text) {
+    furi_assert(gui_widget);
+    GuiElement* string_element = gui_string_create(x, y, horizontal, vertical, font, text);
+    gui_widget_add_element(gui_widget, string_element);
+}
+
+void gui_widget_add_button_element(
+    GuiWidget* gui_widget,
+    GuiButtonType button_type,
+    const char* text,
+    ButtonCallback callback,
+    void* context) {
+    furi_assert(gui_widget);
+    GuiElement* button_element = gui_button_create(button_type, text, callback, context);
+    gui_widget_add_element(gui_widget, button_element);
+}
+
+void gui_widget_add_icon_element(GuiWidget* gui_widget, uint8_t x, uint8_t y, const Icon* icon) {
+    furi_assert(gui_widget);
+    furi_assert(icon);
+    GuiElement* icon_element = gui_icon_create(x, y, icon);
+    gui_widget_add_element(gui_widget, icon_element);
+}

+ 76 - 0
applications/nfc/gui_widget/gui_widget.h

@@ -0,0 +1,76 @@
+#pragma once
+#include <gui/view.h>
+#include "gui_element_string.h"
+#include "gui_element_button.h"
+#include "gui_element_icon.h"
+
+typedef struct GuiWidget GuiWidget;
+typedef struct GuiElement GuiElement;
+
+/** Allocate Gui Widget that holds Gui Elements
+ * @return GuiWidget instance
+ */
+GuiWidget* gui_widget_alloc();
+
+/** Free Gui Widget
+ * @note this function free Gui Elements
+ * @param gui_widget GuiWidget instance
+ */
+void gui_widget_free(GuiWidget* gui_widget);
+
+/** Clear Gui Widget
+ * @param gui_widget GuiWidget instance
+ */
+void gui_widget_clear(GuiWidget* gui_widget);
+
+/** Get Gui Widget view
+ * @param gui_widget GuiWidget instance
+ * @return View instance
+ */
+View* gui_widget_get_view(GuiWidget* gui_widget);
+
+/** Add generic Gui Elements to Gui Widget
+ * @param gui_widget GuiWidget instance
+ * @param element GuiElement element
+ */
+void gui_widget_add_element(GuiWidget* gui_widget, GuiElement* element);
+
+/** Add String Element
+ * @param gui_widget GuiWidget instance
+ * @param x - x coordinate
+ * @param y - y coordinate
+ * @param horizontal - Align instance
+ * @param vertical - Align instance
+ * @param font Font instance
+ * @return GuiElement instance
+ */
+void gui_widget_add_string_element(
+    GuiWidget* gui_widget,
+    uint8_t x,
+    uint8_t y,
+    Align horizontal,
+    Align vertical,
+    Font font,
+    const char* text);
+
+/** Add Button Element
+ * @param gui_widget GuiWidget instance
+ * @param button_type GuiButtonType instance
+ * @param text text on allocated button
+ * @param callback ButtonCallback instance
+ * @param context pointer to context
+ */
+void gui_widget_add_button_element(
+    GuiWidget* gui_widget,
+    GuiButtonType button_type,
+    const char* text,
+    ButtonCallback callback,
+    void* context);
+
+/** Add Icon Element
+ * @param gui_widget GuiWidget instance
+ * @param x - x coordinate
+ * @param y - y coordinate
+ * @param icon Icon instance
+ */
+void gui_widget_add_icon_element(GuiWidget* gui_widget, uint8_t x, uint8_t y, const Icon* icon);

+ 37 - 62
applications/nfc/nfc.c

@@ -22,78 +22,61 @@ void nfc_tick_event_callback(void* context) {
 Nfc* nfc_alloc() {
     Nfc* nfc = furi_alloc(sizeof(Nfc));
 
-    nfc->nfc_common.worker = nfc_worker_alloc();
-    nfc->nfc_common.view_dispatcher = view_dispatcher_alloc();
+    nfc->worker = nfc_worker_alloc();
+    nfc->view_dispatcher = view_dispatcher_alloc();
     nfc->scene_manager = scene_manager_alloc(&nfc_scene_handlers, nfc);
-    view_dispatcher_enable_queue(nfc->nfc_common.view_dispatcher);
-    view_dispatcher_set_event_callback_context(nfc->nfc_common.view_dispatcher, nfc);
-    view_dispatcher_set_custom_event_callback(
-        nfc->nfc_common.view_dispatcher, nfc_custom_event_callback);
+    view_dispatcher_enable_queue(nfc->view_dispatcher);
+    view_dispatcher_set_event_callback_context(nfc->view_dispatcher, nfc);
+    view_dispatcher_set_custom_event_callback(nfc->view_dispatcher, nfc_custom_event_callback);
     view_dispatcher_set_navigation_event_callback(
-        nfc->nfc_common.view_dispatcher, nfc_navigation_event_callback);
-    view_dispatcher_set_tick_event_callback(
-        nfc->nfc_common.view_dispatcher, nfc_tick_event_callback, 300);
+        nfc->view_dispatcher, nfc_navigation_event_callback);
+    view_dispatcher_set_tick_event_callback(nfc->view_dispatcher, nfc_tick_event_callback, 100);
 
     // Open GUI record
     nfc->gui = furi_record_open("gui");
-    view_dispatcher_attach_to_gui(
-        nfc->nfc_common.view_dispatcher, nfc->gui, ViewDispatcherTypeFullscreen);
+    view_dispatcher_attach_to_gui(nfc->view_dispatcher, nfc->gui, ViewDispatcherTypeFullscreen);
 
     // Open Notification record
     nfc->notifications = furi_record_open("notification");
 
     // Submenu
     nfc->submenu = submenu_alloc();
-    view_dispatcher_add_view(
-        nfc->nfc_common.view_dispatcher, NfcViewMenu, submenu_get_view(nfc->submenu));
+    view_dispatcher_add_view(nfc->view_dispatcher, NfcViewMenu, submenu_get_view(nfc->submenu));
 
     // Dialog
     nfc->dialog_ex = dialog_ex_alloc();
     view_dispatcher_add_view(
-        nfc->nfc_common.view_dispatcher, NfcViewDialogEx, dialog_ex_get_view(nfc->dialog_ex));
+        nfc->view_dispatcher, NfcViewDialogEx, dialog_ex_get_view(nfc->dialog_ex));
 
     // Popup
     nfc->popup = popup_alloc();
-    view_dispatcher_add_view(
-        nfc->nfc_common.view_dispatcher, NfcViewPopup, popup_get_view(nfc->popup));
+    view_dispatcher_add_view(nfc->view_dispatcher, NfcViewPopup, popup_get_view(nfc->popup));
 
     // Text Input
     nfc->text_input = text_input_alloc();
     view_dispatcher_add_view(
-        nfc->nfc_common.view_dispatcher, NfcViewTextInput, text_input_get_view(nfc->text_input));
+        nfc->view_dispatcher, NfcViewTextInput, text_input_get_view(nfc->text_input));
 
     // Byte Input
     nfc->byte_input = byte_input_alloc();
     view_dispatcher_add_view(
-        nfc->nfc_common.view_dispatcher, NfcViewByteInput, byte_input_get_view(nfc->byte_input));
+        nfc->view_dispatcher, NfcViewByteInput, byte_input_get_view(nfc->byte_input));
 
     // TextBox
     nfc->text_box = text_box_alloc();
     view_dispatcher_add_view(
-        nfc->nfc_common.view_dispatcher, NfcViewTextBox, text_box_get_view(nfc->text_box));
+        nfc->view_dispatcher, NfcViewTextBox, text_box_get_view(nfc->text_box));
     string_init(nfc->text_box_store);
 
-    // Detect
-    nfc->nfc_detect = nfc_detect_alloc(&nfc->nfc_common);
-    view_dispatcher_add_view(
-        nfc->nfc_common.view_dispatcher, NfcViewDetect, nfc_detect_get_view(nfc->nfc_detect));
-
-    // Emulate
-    nfc->nfc_emulate = nfc_emulate_alloc(&nfc->nfc_common);
+    // Custom Widget
+    nfc->widget = gui_widget_alloc();
     view_dispatcher_add_view(
-        nfc->nfc_common.view_dispatcher, NfcViewEmulate, nfc_emulate_get_view(nfc->nfc_emulate));
+        nfc->view_dispatcher, NfcViewWidget, gui_widget_get_view(nfc->widget));
 
-    // EMV
-    nfc->nfc_emv = nfc_emv_alloc(&nfc->nfc_common);
+    // Bank Card
+    nfc->bank_card = bank_card_alloc();
     view_dispatcher_add_view(
-        nfc->nfc_common.view_dispatcher, NfcViewEmv, nfc_emv_get_view(nfc->nfc_emv));
-
-    // Mifare Ultralight
-    nfc->nfc_mifare_ul = nfc_mifare_ul_alloc(&nfc->nfc_common);
-    view_dispatcher_add_view(
-        nfc->nfc_common.view_dispatcher,
-        NfcViewMifareUl,
-        nfc_mifare_ul_get_view(nfc->nfc_mifare_ul));
+        nfc->view_dispatcher, NfcViewBankCard, bank_card_get_view(nfc->bank_card));
 
     return nfc;
 }
@@ -102,52 +85,44 @@ void nfc_free(Nfc* nfc) {
     furi_assert(nfc);
 
     // Submenu
-    view_dispatcher_remove_view(nfc->nfc_common.view_dispatcher, NfcViewMenu);
+    view_dispatcher_remove_view(nfc->view_dispatcher, NfcViewMenu);
     submenu_free(nfc->submenu);
 
     // DialogEx
-    view_dispatcher_remove_view(nfc->nfc_common.view_dispatcher, NfcViewDialogEx);
+    view_dispatcher_remove_view(nfc->view_dispatcher, NfcViewDialogEx);
     dialog_ex_free(nfc->dialog_ex);
 
     // Popup
-    view_dispatcher_remove_view(nfc->nfc_common.view_dispatcher, NfcViewPopup);
+    view_dispatcher_remove_view(nfc->view_dispatcher, NfcViewPopup);
     popup_free(nfc->popup);
 
     // TextInput
-    view_dispatcher_remove_view(nfc->nfc_common.view_dispatcher, NfcViewTextInput);
+    view_dispatcher_remove_view(nfc->view_dispatcher, NfcViewTextInput);
     text_input_free(nfc->text_input);
 
     // ByteInput
-    view_dispatcher_remove_view(nfc->nfc_common.view_dispatcher, NfcViewByteInput);
+    view_dispatcher_remove_view(nfc->view_dispatcher, NfcViewByteInput);
     byte_input_free(nfc->byte_input);
 
     // TextBox
-    view_dispatcher_remove_view(nfc->nfc_common.view_dispatcher, NfcViewTextBox);
+    view_dispatcher_remove_view(nfc->view_dispatcher, NfcViewTextBox);
     text_box_free(nfc->text_box);
     string_clear(nfc->text_box_store);
 
-    // Detect
-    view_dispatcher_remove_view(nfc->nfc_common.view_dispatcher, NfcViewDetect);
-    nfc_detect_free(nfc->nfc_detect);
-
-    // Emulate
-    view_dispatcher_remove_view(nfc->nfc_common.view_dispatcher, NfcViewEmulate);
-    nfc_emulate_free(nfc->nfc_emulate);
-
-    // EMV
-    view_dispatcher_remove_view(nfc->nfc_common.view_dispatcher, NfcViewEmv);
-    nfc_emv_free(nfc->nfc_emv);
+    // Custom Widget
+    view_dispatcher_remove_view(nfc->view_dispatcher, NfcViewWidget);
+    gui_widget_free(nfc->widget);
 
-    // Mifare Ultralight
-    view_dispatcher_remove_view(nfc->nfc_common.view_dispatcher, NfcViewMifareUl);
-    nfc_mifare_ul_free(nfc->nfc_mifare_ul);
+    // Bank Card
+    view_dispatcher_remove_view(nfc->view_dispatcher, NfcViewBankCard);
+    bank_card_free(nfc->bank_card);
 
     // Worker
-    nfc_worker_stop(nfc->nfc_common.worker);
-    nfc_worker_free(nfc->nfc_common.worker);
+    nfc_worker_stop(nfc->worker);
+    nfc_worker_free(nfc->worker);
 
     // View Dispatcher
-    view_dispatcher_free(nfc->nfc_common.view_dispatcher);
+    view_dispatcher_free(nfc->view_dispatcher);
 
     // Scene Manager
     scene_manager_free(nfc->scene_manager);
@@ -167,13 +142,13 @@ int32_t nfc_task(void* p) {
     Nfc* nfc = nfc_alloc();
 
     // Check argument and run corresponding scene
-    if(p && nfc_device_load(&nfc->device, p)) {
+    if(p && nfc_device_load(&nfc->dev, p)) {
         scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateUid);
     } else {
         scene_manager_next_scene(nfc->scene_manager, NfcSceneStart);
     }
 
-    view_dispatcher_run(nfc->nfc_common.view_dispatcher);
+    view_dispatcher_run(nfc->view_dispatcher);
 
     nfc_free(nfc);
 

+ 3 - 3
applications/nfc/nfc_cli.c

@@ -24,7 +24,7 @@ void nfc_cli_detect(Cli* cli, string_t args, void* context) {
     printf("Detecting nfc...\r\nPress Ctrl+C to abort\r\n");
     while(!cmd_exit) {
         cmd_exit |= cli_cmd_interrupt_received(cli);
-        cmd_exit |= api_hal_nfc_detect(&dev_list, &dev_cnt, 200, true);
+        cmd_exit |= api_hal_nfc_detect(&dev_list, &dev_cnt, 400, true);
         if(dev_cnt > 0) {
             printf("Found %d devices\r\n", dev_cnt);
             for(uint8_t i = 0; i < dev_cnt; i++) {
@@ -56,13 +56,13 @@ void nfc_cli_emulate(Cli* cli, string_t args, void* context) {
     printf("Emulating NFC-A Type: T2T UID: CF72D440 SAK: 20 ATQA: 00/04\r\n");
     printf("Press Ctrl+C to abort\r\n");
 
-    NfcDeviceData params = {
+    NfcDeviceCommomData params = {
         .uid = {0x36, 0x9C, 0xe7, 0xb1, 0x0A, 0xC1, 0x34},
         .uid_len = 7,
         .atqa = {0x44, 0x00},
         .sak = 0x00,
         .device = NfcDeviceNfca,
-        .protocol = NfcDeviceProtocolMfUltralight,
+        .protocol = NfcDeviceProtocolMifareUl,
     };
 
     while(!cli_cmd_interrupt_received(cli)) {

+ 344 - 56
applications/nfc/nfc_device.c

@@ -1,13 +1,246 @@
-#include "nfc_device.h"
+#include "nfc_device_i.h"
 
 #include <file-worker.h>
 #include <path.h>
+#include <hex.h>
 
 #define NFC_DEVICE_MAX_DATA_LEN 14
 
 static const char* nfc_app_folder = "nfc";
 static const char* nfc_app_extension = ".nfc";
 
+static bool nfc_device_read_hex(string_t str, uint8_t* buff, uint16_t len) {
+    string_strim(str);
+    uint8_t nibble_high = 0;
+    uint8_t nibble_low = 0;
+    bool parsed = true;
+
+    for(uint16_t i = 0; i < len; i++) {
+        if(hex_char_to_hex_nibble(string_get_char(str, 0), &nibble_high) &&
+           hex_char_to_hex_nibble(string_get_char(str, 1), &nibble_low)) {
+            buff[i] = (nibble_high << 4) | nibble_low;
+            string_right(str, 3);
+        } else {
+            parsed = false;
+            break;
+        }
+    }
+    return parsed;
+}
+
+uint16_t nfc_device_prepare_format_string(NfcDevice* dev, string_t format_string) {
+    if(dev->format == NfcDeviceSaveFormatUid) {
+        string_set_str(format_string, "UID\n");
+    } else if(dev->format == NfcDeviceSaveFormatBankCard) {
+        string_set_str(format_string, "Bank card\n");
+    } else if(dev->format == NfcDeviceSaveFormatMifareUl) {
+        string_set_str(format_string, "Mifare Ultralight\n");
+    } else {
+        string_set_str(format_string, "Unknown\n");
+    }
+    return string_size(format_string);
+}
+
+bool nfc_device_parse_format_string(NfcDevice* dev, string_t format_string) {
+    if(string_start_with_str_p(format_string, "UID")) {
+        dev->format = NfcDeviceSaveFormatUid;
+        dev->dev_data.nfc_data.protocol = NfcDeviceProtocolUnknown;
+        return true;
+    } else if(string_start_with_str_p(format_string, "Bank card")) {
+        dev->format = NfcDeviceSaveFormatBankCard;
+        dev->dev_data.nfc_data.protocol = NfcDeviceProtocolEMV;
+        return true;
+    } else if(string_start_with_str_p(format_string, "Mifare Ultralight")) {
+        dev->format = NfcDeviceSaveFormatMifareUl;
+        dev->dev_data.nfc_data.protocol = NfcDeviceProtocolMifareUl;
+        return true;
+    }
+    return false;
+}
+
+uint16_t nfc_device_prepare_uid_string(NfcDevice* dev, string_t uid_string) {
+    NfcDeviceCommomData* uid_data = &dev->dev_data.nfc_data;
+    string_printf(uid_string, "UID len: %02X UID: ", dev->dev_data.nfc_data.uid_len);
+    for(uint8_t i = 0; i < uid_data->uid_len; i++) {
+        string_cat_printf(uid_string, "%02X ", uid_data->uid[i]);
+    }
+    string_cat_printf(
+        uid_string,
+        "ATQA: %02X %02X SAK: %02X\n",
+        uid_data->atqa[0],
+        uid_data->atqa[1],
+        uid_data->sak);
+    return string_size(uid_string);
+}
+
+bool nfc_device_parse_uid_string(NfcDevice* dev, string_t uid_string) {
+    NfcDeviceCommomData* uid_data = &dev->dev_data.nfc_data;
+    bool parsed = false;
+
+    do {
+        // strlen("UID len: ") = 9
+        string_right(uid_string, 9);
+        if(!nfc_device_read_hex(uid_string, &uid_data->uid_len, 1)) {
+            break;
+        }
+        // strlen("UID: ") = 5
+        string_right(uid_string, 5);
+        if(!nfc_device_read_hex(uid_string, uid_data->uid, uid_data->uid_len)) {
+            break;
+        }
+        // strlen("ATQA: ") = 6
+        string_right(uid_string, 6);
+        if(!nfc_device_read_hex(uid_string, uid_data->atqa, 2)) {
+            break;
+        }
+        // strlen("SAK: ") = 5
+        string_right(uid_string, 5);
+        if(!nfc_device_read_hex(uid_string, &uid_data->sak, 1)) {
+            break;
+        }
+        parsed = true;
+    } while(0);
+
+    return parsed;
+}
+
+uint16_t nfc_device_prepare_mifare_ul_string(NfcDevice* dev, string_t mifare_ul_string) {
+    MifareUlData* data = &dev->dev_data.mf_ul_data;
+    string_printf(mifare_ul_string, "Signature:");
+    for(uint8_t i = 0; i < sizeof(data->signature); i++) {
+        string_cat_printf(mifare_ul_string, " %02X", data->signature[i]);
+    }
+    string_cat_printf(mifare_ul_string, "\nVersion:");
+    uint8_t* version = (uint8_t*)&data->version;
+    for(uint8_t i = 0; i < sizeof(data->version); i++) {
+        string_cat_printf(mifare_ul_string, " %02X", version[i]);
+    }
+    for(uint8_t i = 0; i < 3; i++) {
+        string_cat_printf(
+            mifare_ul_string,
+            "\nCounter %d: %lu Tearing flag %d: %02X",
+            i,
+            data->counter[i],
+            i,
+            data->tearing[i]);
+    }
+    string_cat_printf(mifare_ul_string, "\nData size: %d\n", data->data_size);
+    for(uint16_t i = 0; i < data->data_size; i += 4) {
+        string_cat_printf(
+            mifare_ul_string,
+            "%02X %02X %02X %02X\n",
+            data->data[i],
+            data->data[i + 1],
+            data->data[i + 2],
+            data->data[i + 3]);
+    }
+    return string_size(mifare_ul_string);
+}
+
+bool nfc_device_parse_mifare_ul_string(NfcDevice* dev, string_t mifare_ul_string) {
+    MifareUlData* data = &dev->dev_data.mf_ul_data;
+    uint16_t tearing_tmp = 0;
+    uint16_t cnt_num = 0;
+    size_t ws = 0;
+    int res = 0;
+    bool parsed = false;
+
+    do {
+        // strlen("Signature: ") = 11
+        string_right(mifare_ul_string, 11);
+        if(!nfc_device_read_hex(mifare_ul_string, data->signature, sizeof(data->signature))) {
+            break;
+        }
+        // strlen("Version: ") = 9
+        string_right(mifare_ul_string, 9);
+        if(!nfc_device_read_hex(
+               mifare_ul_string, (uint8_t*)&data->version, sizeof(data->version))) {
+            break;
+        }
+        string_strim(mifare_ul_string);
+        // Read counters and tearing flags
+        for(uint8_t i = 0; i < 3; i++) {
+            res = sscanf(
+                string_get_cstr(mifare_ul_string),
+                "Counter %hX: %lu Tearing flag %hX: %02hX",
+                &cnt_num,
+                &data->counter[i],
+                &cnt_num,
+                &tearing_tmp);
+            if(res != 4) {
+                break;
+            }
+            data->tearing[i] = tearing_tmp;
+            ws = string_search_char(mifare_ul_string, '\n');
+            string_right(mifare_ul_string, ws + 1);
+        }
+        // Read data size
+        res = sscanf(string_get_cstr(mifare_ul_string), "Data size: %hu", &data->data_size);
+        if(res != 1) {
+            break;
+        }
+        ws = string_search_char(mifare_ul_string, '\n');
+        string_right(mifare_ul_string, ws + 1);
+        // Read data
+        for(uint16_t i = 0; i < data->data_size; i += 4) {
+            if(!nfc_device_read_hex(mifare_ul_string, &data->data[i], 4)) {
+                break;
+            }
+        }
+        parsed = true;
+    } while(0);
+
+    return parsed;
+}
+
+uint16_t nfc_device_prepare_bank_card_string(NfcDevice* dev, string_t bank_card_string) {
+    NfcEmvData* data = &dev->dev_data.emv_data;
+    string_printf(bank_card_string, "AID len: %d, AID:", data->aid_len);
+    for(uint8_t i = 0; i < data->aid_len; i++) {
+        string_cat_printf(bank_card_string, " %02X", data->aid[i]);
+    }
+    string_cat_printf(bank_card_string, "\nName: %s\nNumber:", data->name);
+    for(uint8_t i = 0; i < sizeof(data->number); i++) {
+        string_cat_printf(bank_card_string, " %02X", data->number[i]);
+    }
+    return string_size(bank_card_string);
+}
+
+bool nfc_device_parse_bank_card_string(NfcDevice* dev, string_t bank_card_string) {
+    NfcEmvData* data = &dev->dev_data.emv_data;
+    bool parsed = false;
+    int res = 0;
+    memset(data, 0, sizeof(NfcEmvData));
+
+    do {
+        res = sscanf(string_get_cstr(bank_card_string), "AID len: %hu", &data->aid_len);
+        if(res != 1) {
+            break;
+        }
+        // strlen("AID len: ") = 9
+        string_right(bank_card_string, 9);
+        size_t ws = string_search_char(bank_card_string, ':');
+        string_right(bank_card_string, ws + 1);
+        if(!nfc_device_read_hex(bank_card_string, data->aid, data->aid_len)) {
+            break;
+        }
+        res = sscanf(string_get_cstr(bank_card_string), "Name: %s\n", data->name);
+        if(res != 1) {
+            break;
+        }
+        ws = string_search_char(bank_card_string, '\n');
+        string_right(bank_card_string, ws + 1);
+        // strlen("Number: ") = 8
+        string_right(bank_card_string, 8);
+        if(!nfc_device_read_hex(bank_card_string, data->number, sizeof(data->number))) {
+            break;
+        }
+        parsed = true;
+    } while(0);
+
+    return parsed;
+}
+
 void nfc_device_set_name(NfcDevice* dev, const char* name) {
     furi_assert(dev);
 
@@ -19,37 +252,52 @@ bool nfc_device_save(NfcDevice* dev, const char* dev_name) {
 
     FileWorker* file_worker = file_worker_alloc(false);
     string_t dev_file_name;
+    string_init(dev_file_name);
+    string_t temp_str;
+    string_init(temp_str);
+    uint16_t string_len = 0;
 
-    // Create nfc directory if necessary
-    if(!file_worker_mkdir(file_worker, nfc_app_folder)) {
-        return false;
-    };
-
-    // First remove nfc device file if it was saved
-    string_init_printf(dev_file_name, "%s/%s%s", nfc_app_folder, dev_name, nfc_app_extension);
-    if(!file_worker_remove(file_worker, string_get_cstr(dev_file_name))) {
-        string_clear(dev_file_name);
-        return false;
-    };
-
-    // Prepare buffer to write
-    uint8_t buff[NFC_DEVICE_MAX_DATA_LEN];
-    buff[0] = dev->data.uid_len;
-    memcpy(&buff[1], dev->data.uid, dev->data.uid_len);
-    memcpy(&buff[dev->data.uid_len + 1], dev->data.atqa, 2);
-    buff[dev->data.uid_len + 3] = dev->data.sak;
-
-    // Save nfc device
-    bool res = file_worker_open(
-        file_worker, string_get_cstr(dev_file_name), FSAM_WRITE, FSOM_CREATE_ALWAYS);
-    string_clear(dev_file_name);
-    if(res) {
-        // Write UID length
-        if(!file_worker_write_hex(file_worker, buff, dev->data.uid_len + 4)) {
-            file_worker_close(file_worker);
-            return false;
+    do {
+        // Create nfc directory if necessary
+        if(!file_worker_mkdir(file_worker, nfc_app_folder)) {
+            break;
+        };
+        // First remove nfc device file if it was saved
+        string_printf(dev_file_name, "%s/%s%s", nfc_app_folder, dev_name, nfc_app_extension);
+        if(!file_worker_remove(file_worker, string_get_cstr(dev_file_name))) {
+            break;
+        };
+        // Open file
+        if(!file_worker_open(
+               file_worker, string_get_cstr(dev_file_name), FSAM_WRITE, FSOM_CREATE_ALWAYS)) {
+            break;
         }
-    }
+        // Prepare and write format name on 1st line
+        string_len = nfc_device_prepare_format_string(dev, temp_str);
+        if(!file_worker_write(file_worker, string_get_cstr(temp_str), string_len)) {
+            break;
+        }
+        // Prepare and write UID data on 2nd line
+        string_len = nfc_device_prepare_uid_string(dev, temp_str);
+        if(!file_worker_write(file_worker, string_get_cstr(temp_str), string_len)) {
+            break;
+        }
+        // Save more data if necessary
+        if(dev->format == NfcDeviceSaveFormatMifareUl) {
+            string_len = nfc_device_prepare_mifare_ul_string(dev, temp_str);
+            if(!file_worker_write(file_worker, string_get_cstr(temp_str), string_len)) {
+                break;
+            }
+        } else if(dev->format == NfcDeviceSaveFormatBankCard) {
+            string_len = nfc_device_prepare_bank_card_string(dev, temp_str);
+            if(!file_worker_write(file_worker, string_get_cstr(temp_str), string_len)) {
+                break;
+            }
+        }
+    } while(0);
+
+    string_clear(temp_str);
+    string_clear(dev_file_name);
     file_worker_close(file_worker);
     file_worker_free(file_worker);
 
@@ -57,34 +305,52 @@ bool nfc_device_save(NfcDevice* dev, const char* dev_name) {
 }
 
 static bool nfc_device_load_data(FileWorker* file_worker, string_t path, NfcDevice* dev) {
-    // Open key file
-    if(!file_worker_open(file_worker, string_get_cstr(path), FSAM_READ, FSOM_OPEN_EXISTING)) {
-        return false;
-    }
-
-    uint8_t buff[NFC_DEVICE_MAX_DATA_LEN] = {};
-
-    // Load first byte - UID length
-    if(!file_worker_read_hex(file_worker, buff, 1)) {
-        return false;
-    }
-    // Read space
-    uint8_t space = 0;
-    if(!file_worker_read(file_worker, &space, 1)) {
-        return false;
-    }
+    string_t temp_string;
+    string_init(temp_string);
+    bool parsed = false;
 
-    // Load other data
-    if(!file_worker_read_hex(file_worker, &buff[1], buff[0] + 3)) {
-        return false;
-    }
+    do {
+        // Open key file
+        if(!file_worker_open(file_worker, string_get_cstr(path), FSAM_READ, FSOM_OPEN_EXISTING)) {
+            break;
+        }
+        // Read and parse format from 1st line
+        if(!file_worker_read_until(file_worker, temp_string, '\n')) {
+            break;
+        }
+        if(!nfc_device_parse_format_string(dev, temp_string)) {
+            break;
+        }
+        // Read and parse UID data from 2nd line
+        if(!file_worker_read_until(file_worker, temp_string, '\n')) {
+            break;
+        }
+        if(!nfc_device_parse_uid_string(dev, temp_string)) {
+            break;
+        }
+        // Parse other data
+        if(dev->format == NfcDeviceSaveFormatMifareUl) {
+            // Read until EOF
+            if(!file_worker_read_until(file_worker, temp_string, 0x05)) {
+                break;
+            }
+            if(!nfc_device_parse_mifare_ul_string(dev, temp_string)) {
+                break;
+            }
+        } else if(dev->format == NfcDeviceSaveFormatBankCard) {
+            // Read until EOF
+            if(!file_worker_read_until(file_worker, temp_string, 0x05)) {
+                break;
+            }
+            if(!nfc_device_parse_bank_card_string(dev, temp_string)) {
+                break;
+            }
+        }
+        parsed = true;
+    } while(0);
 
-    // Set loaded data
-    dev->data.uid_len = buff[0];
-    memcpy(dev->data.uid, &buff[1], dev->data.uid_len);
-    memcpy(dev->data.atqa, &buff[dev->data.uid_len + 1], 2);
-    dev->data.sak = buff[dev->data.uid_len + 3];
-    return true;
+    string_clear(temp_string);
+    return parsed;
 }
 
 bool nfc_device_load(NfcDevice* dev, const char* file_path) {
@@ -137,3 +403,25 @@ bool nfc_file_select(NfcDevice* dev) {
 
     return res;
 }
+
+void nfc_device_clear(NfcDevice* dev) {
+    furi_assert(dev);
+
+    memset(&dev->dev_data, 0, sizeof(dev->dev_data));
+    nfc_device_set_name(dev, "");
+    dev->format = NfcDeviceSaveFormatUid;
+}
+
+bool nfc_device_delete(NfcDevice* dev) {
+    furi_assert(dev);
+
+    bool result = false;
+    FileWorker* file_worker = file_worker_alloc(false);
+    string_t file_path;
+    string_init_printf(file_path, "%s/%s%s", nfc_app_folder, dev->dev_name, nfc_app_extension);
+    result = file_worker_remove(file_worker, string_get_cstr(file_path));
+    string_clear(file_path);
+    file_worker_close(file_worker);
+    file_worker_free(file_worker);
+    return result;
+}

+ 27 - 13
applications/nfc/nfc_device.h

@@ -3,11 +3,11 @@
 #include <stdint.h>
 #include <stdbool.h>
 
+#include "mifare_ultralight.h"
+
 #define NFC_DEV_NAME_MAX_LEN 22
 #define NFC_FILE_NAME_MAX_LEN 120
 
-#define NFC_MIFARE_UL_MAX_SIZE 256
-
 typedef enum {
     NfcDeviceNfca,
     NfcDeviceNfcb,
@@ -18,9 +18,15 @@ typedef enum {
 typedef enum {
     NfcDeviceProtocolUnknown,
     NfcDeviceProtocolEMV,
-    NfcDeviceProtocolMfUltralight,
+    NfcDeviceProtocolMifareUl,
 } NfcProtocol;
 
+typedef enum {
+    NfcDeviceSaveFormatUid,
+    NfcDeviceSaveFormatBankCard,
+    NfcDeviceSaveFormatMifareUl,
+} NfcDeviceSaveFormat;
+
 typedef struct {
     uint8_t uid_len;
     uint8_t uid[10];
@@ -28,27 +34,31 @@ typedef struct {
     uint8_t sak;
     NfcDeviceType device;
     NfcProtocol protocol;
-} NfcDeviceData;
+} NfcDeviceCommomData;
 
 typedef struct {
-    NfcDeviceData nfc_data;
     char name[32];
+    uint8_t aid[16];
+    uint16_t aid_len;
     uint8_t number[8];
+    uint8_t exp_mon;
+    uint16_t exp_year;
+    char cardholder[32];
 } NfcEmvData;
 
 typedef struct {
-    NfcDeviceData nfc_data;
-    uint8_t full_dump[NFC_MIFARE_UL_MAX_SIZE];
-    uint16_t dump_size;
-    // TODO delete with debug view
-    uint8_t man_block[12];
-    uint8_t otp[4];
-} NfcMifareUlData;
+    NfcDeviceCommomData nfc_data;
+    union {
+        NfcEmvData emv_data;
+        MifareUlData mf_ul_data;
+    };
+} NfcDeviceData;
 
 typedef struct {
-    NfcDeviceData data;
+    NfcDeviceData dev_data;
     char dev_name[NFC_DEV_NAME_MAX_LEN];
     char file_name[NFC_FILE_NAME_MAX_LEN];
+    NfcDeviceSaveFormat format;
 } NfcDevice;
 
 void nfc_device_set_name(NfcDevice* dev, const char* name);
@@ -58,3 +68,7 @@ bool nfc_device_save(NfcDevice* dev, const char* dev_name);
 bool nfc_device_load(NfcDevice* dev, const char* file_path);
 
 bool nfc_file_select(NfcDevice* dev);
+
+void nfc_device_clear(NfcDevice* dev);
+
+bool nfc_device_delete(NfcDevice* dev);

+ 16 - 0
applications/nfc/nfc_device_i.h

@@ -0,0 +1,16 @@
+#pragma once
+
+#include "nfc_device.h"
+#include <m-string.h>
+
+uint16_t nfc_device_prepare_format_string(NfcDevice* dev, string_t format_string);
+bool nfc_device_parse_format_string(NfcDevice* dev, string_t format_string);
+
+uint16_t nfc_device_prepare_uid_string(NfcDevice* dev, string_t uid_string);
+bool nfc_device_parse_uid_string(NfcDevice* dev, string_t uid_string);
+
+uint16_t nfc_device_prepare_mifare_ul_string(NfcDevice* dev, string_t mifare_ul_string);
+bool nfc_device_parse_mifare_ul_string(NfcDevice* dev, string_t mifare_ul_string);
+
+uint16_t nfc_device_prepare_bank_card_string(NfcDevice* dev, string_t bank_card_string);
+bool nfc_device_parse_bank_card_string(NfcDevice* dev, string_t bank_card_string);

+ 13 - 17
applications/nfc/nfc_i.h

@@ -21,31 +21,27 @@
 #include <gui/modules/byte_input.h>
 #include <gui/modules/text_box.h>
 
-#include <nfc/scenes/nfc_scene.h>
+#include "views/bank_card.h"
+
+#include "gui_widget/gui_widget.h"
+#include "gui_widget/gui_element_string.h"
+#include "gui_widget/gui_element_button.h"
 
-#include "views/nfc_detect.h"
-#include "views/nfc_emulate.h"
-#include "views/nfc_emv.h"
-#include "views/nfc_mifare_ul.h"
+#include <nfc/scenes/nfc_scene.h>
 
 #define NFC_TEXT_STORE_SIZE 128
 
 struct Nfc {
-    NfcCommon nfc_common;
+    NfcWorker* worker;
+    ViewDispatcher* view_dispatcher;
     Gui* gui;
     NotificationApp* notifications;
     SceneManager* scene_manager;
-    NfcDevice device;
+    NfcDevice dev;
 
     char text_store[NFC_TEXT_STORE_SIZE + 1];
     string_t text_box_store;
 
-    // Nfc Views
-    NfcDetect* nfc_detect;
-    NfcEmulate* nfc_emulate;
-    NfcEmv* nfc_emv;
-    NfcMifareUl* nfc_mifare_ul;
-
     // Common Views
     Submenu* submenu;
     DialogEx* dialog_ex;
@@ -53,6 +49,8 @@ struct Nfc {
     TextInput* text_input;
     ByteInput* byte_input;
     TextBox* text_box;
+    GuiWidget* widget;
+    BankCard* bank_card;
 };
 
 typedef enum {
@@ -62,10 +60,8 @@ typedef enum {
     NfcViewTextInput,
     NfcViewByteInput,
     NfcViewTextBox,
-    NfcViewDetect,
-    NfcViewEmulate,
-    NfcViewEmv,
-    NfcViewMifareUl,
+    NfcViewWidget,
+    NfcViewBankCard,
 } NfcView;
 
 Nfc* nfc_alloc();

+ 1 - 20
applications/nfc/nfc_types.h

@@ -6,25 +6,6 @@
 #include <gui/view_dispatcher.h>
 #include "nfc_worker.h"
 
-typedef struct {
-    NfcWorker* worker;
-    ViewDispatcher* view_dispatcher;
-    NfcWorkerResult worker_result;
-} NfcCommon;
-
-typedef enum {
-    NfcEventDetect,
-    NfcEventEmv,
-    NfcEventMifareUl,
-} NfcEvent;
-
-typedef enum {
-    NfcSubmenuDetect,
-    NfcSubmenuEmulate,
-    NfcSubmenuEMV,
-    NfcSubmenuMifareUl,
-} NfcSubmenu;
-
 static inline const char* nfc_get_dev_type(rfalNfcDevType type) {
     if(type == RFAL_NFC_LISTEN_TYPE_NFCA) {
         return "NFC-A may be:";
@@ -62,7 +43,7 @@ static inline const char* nfc_get_nfca_type(rfalNfcaListenDeviceType type) {
 static inline const char* nfc_get_protocol(NfcProtocol protocol) {
     if(protocol == NfcDeviceProtocolEMV) {
         return "EMV bank card";
-    } else if(protocol == NfcDeviceProtocolMfUltralight) {
+    } else if(protocol == NfcDeviceProtocolMifareUl) {
         return "Mifare Ultralight";
     } else {
         return "Unrecognized";

+ 187 - 65
applications/nfc/nfc_worker.c

@@ -39,26 +39,21 @@ ReturnCode nfc_worker_get_error(NfcWorker* nfc_worker) {
     return nfc_worker->error;
 }
 
-void nfc_worker_set_emulation_params(NfcWorker* nfc_worker, NfcDeviceData* data) {
-    furi_assert(nfc_worker);
-    furi_assert(data);
-
-    nfc_worker->emulate_params = *data;
-}
-
 void nfc_worker_start(
     NfcWorker* nfc_worker,
     NfcWorkerState state,
-    NfcWorkerResult* result_dest,
+    NfcDeviceData* dev_data,
     NfcWorkerCallback callback,
     void* context) {
     furi_assert(nfc_worker);
-    furi_assert(nfc_worker->state == NfcWorkerStateReady);
-    furi_assert(result_dest);
+    furi_assert(dev_data);
+    while(nfc_worker->state != NfcWorkerStateReady) {
+        osDelay(10);
+    }
 
     nfc_worker->callback = callback;
     nfc_worker->context = context;
-    nfc_worker->last_result = result_dest;
+    nfc_worker->dev_data = dev_data;
     nfc_worker_change_state(nfc_worker, state);
     nfc_worker->thread = osThreadNew(nfc_worker_task, nfc_worker, &nfc_worker->thread_attr);
 }
@@ -88,12 +83,16 @@ void nfc_worker_task(void* context) {
         nfc_worker_detect(nfc_worker);
     } else if(nfc_worker->state == NfcWorkerStateEmulate) {
         nfc_worker_emulate(nfc_worker);
+    } else if(nfc_worker->state == NfcWorkerStateReadEMVApp) {
+        nfc_worker_read_emv_app(nfc_worker);
     } else if(nfc_worker->state == NfcWorkerStateReadEMV) {
         nfc_worker_read_emv(nfc_worker);
     } else if(nfc_worker->state == NfcWorkerStateEmulateEMV) {
         nfc_worker_emulate_emv(nfc_worker);
-    } else if(nfc_worker->state == NfcWorkerStateReadMfUltralight) {
-        nfc_worker_read_mf_ultralight(nfc_worker);
+    } else if(nfc_worker->state == NfcWorkerStateReadMifareUl) {
+        nfc_worker_read_mifare_ul(nfc_worker);
+    } else if(nfc_worker->state == NfcWorkerStateEmulateMifareUl) {
+        nfc_worker_emulate_mifare_ul(nfc_worker);
     } else if(nfc_worker->state == NfcWorkerStateField) {
         nfc_worker_field(nfc_worker);
     }
@@ -107,7 +106,7 @@ void nfc_worker_detect(NfcWorker* nfc_worker) {
     rfalNfcDevice* dev_list;
     rfalNfcDevice* dev;
     uint8_t dev_cnt;
-    NfcDeviceData* result = &nfc_worker->last_result->nfc_detect_data;
+    NfcDeviceCommomData* result = &nfc_worker->dev_data->nfc_data;
 
     while(nfc_worker->state == NfcWorkerStateDetect) {
         if(api_hal_nfc_detect(&dev_list, &dev_cnt, 1000, true)) {
@@ -124,11 +123,12 @@ void nfc_worker_detect(NfcWorker* nfc_worker) {
                        dev->dev.nfca.sensRes.anticollisionInfo,
                        dev->dev.nfca.sensRes.platformInfo,
                        dev->dev.nfca.selRes.sak)) {
-                    result->protocol = NfcDeviceProtocolMfUltralight;
+                    result->protocol = NfcDeviceProtocolMifareUl;
+                } else if(dev->rfInterface == RFAL_NFC_INTERFACE_ISODEP) {
+                    result->protocol = NfcDeviceProtocolEMV;
                 } else {
                     result->protocol = NfcDeviceProtocolUnknown;
                 }
-
             } else if(dev->type == RFAL_NFC_LISTEN_TYPE_NFCB) {
                 result->device = NfcDeviceNfcb;
             } else if(dev->type == RFAL_NFC_LISTEN_TYPE_NFCF) {
@@ -147,15 +147,77 @@ void nfc_worker_detect(NfcWorker* nfc_worker) {
 }
 
 void nfc_worker_emulate(NfcWorker* nfc_worker) {
-    NfcDeviceData* param = &nfc_worker->emulate_params;
+    NfcDeviceCommomData* data = &nfc_worker->dev_data->nfc_data;
     while(nfc_worker->state == NfcWorkerStateEmulate) {
-        if(api_hal_nfc_listen(param->uid, param->uid_len, param->atqa, param->sak, 100)) {
+        if(api_hal_nfc_listen(data->uid, data->uid_len, data->atqa, data->sak, 100)) {
             FURI_LOG_I(NFC_WORKER_TAG, "Reader detected");
         }
         osDelay(10);
     }
 }
 
+void nfc_worker_read_emv_app(NfcWorker* nfc_worker) {
+    ReturnCode err;
+    rfalNfcDevice* dev_list;
+    EmvApplication emv_app = {};
+    uint8_t dev_cnt = 0;
+    uint8_t tx_buff[255] = {};
+    uint16_t tx_len = 0;
+    uint8_t* rx_buff;
+    uint16_t* rx_len;
+    NfcDeviceData* result = nfc_worker->dev_data;
+
+    while(nfc_worker->state == NfcWorkerStateReadEMVApp) {
+        memset(&emv_app, 0, sizeof(emv_app));
+        if(api_hal_nfc_detect(&dev_list, &dev_cnt, 1000, false)) {
+            // Card was found. Check that it supports EMV
+            if(dev_list[0].rfInterface == RFAL_NFC_INTERFACE_ISODEP) {
+                result->nfc_data.uid_len = dev_list[0].dev.nfca.nfcId1Len;
+                result->nfc_data.atqa[0] = dev_list[0].dev.nfca.sensRes.anticollisionInfo;
+                result->nfc_data.atqa[1] = dev_list[0].dev.nfca.sensRes.platformInfo;
+                result->nfc_data.sak = dev_list[0].dev.nfca.selRes.sak;
+                memcpy(
+                    result->nfc_data.uid, dev_list[0].dev.nfca.nfcId1, result->nfc_data.uid_len);
+                result->nfc_data.protocol = NfcDeviceProtocolEMV;
+
+                FURI_LOG_I(NFC_WORKER_TAG, "Send select PPSE command");
+                tx_len = emv_prepare_select_ppse(tx_buff);
+                err = api_hal_nfc_data_exchange(tx_buff, tx_len, &rx_buff, &rx_len, false);
+                if(err != ERR_NONE) {
+                    FURI_LOG_E(NFC_WORKER_TAG, "Error during selection PPSE request: %d", err);
+                    api_hal_nfc_deactivate();
+                    continue;
+                }
+                FURI_LOG_I(
+                    NFC_WORKER_TAG, "Select PPSE response received. Start parsing response");
+                if(emv_decode_ppse_response(rx_buff, *rx_len, &emv_app)) {
+                    FURI_LOG_I(NFC_WORKER_TAG, "Select PPSE responce parced");
+                    // Notify caller and exit
+                    result->emv_data.aid_len = emv_app.aid_len;
+                    memcpy(result->emv_data.aid, emv_app.aid, emv_app.aid_len);
+                    if(nfc_worker->callback) {
+                        nfc_worker->callback(nfc_worker->context);
+                    }
+                    break;
+                } else {
+                    FURI_LOG_E(NFC_WORKER_TAG, "Can't find pay application");
+                    api_hal_nfc_deactivate();
+                    continue;
+                }
+            } else {
+                // Can't find EMV card
+                FURI_LOG_W(NFC_WORKER_TAG, "Card doesn't support EMV");
+                api_hal_nfc_deactivate();
+            }
+        } else {
+            // Can't find EMV card
+            FURI_LOG_W(NFC_WORKER_TAG, "Can't find any cards");
+            api_hal_nfc_deactivate();
+        }
+        osDelay(20);
+    }
+}
+
 void nfc_worker_read_emv(NfcWorker* nfc_worker) {
     ReturnCode err;
     rfalNfcDevice* dev_list;
@@ -165,13 +227,21 @@ void nfc_worker_read_emv(NfcWorker* nfc_worker) {
     uint16_t tx_len = 0;
     uint8_t* rx_buff;
     uint16_t* rx_len;
-    NfcEmvData* result = &nfc_worker->last_result->nfc_emv_data;
+    NfcDeviceData* result = nfc_worker->dev_data;
 
     while(nfc_worker->state == NfcWorkerStateReadEMV) {
         memset(&emv_app, 0, sizeof(emv_app));
         if(api_hal_nfc_detect(&dev_list, &dev_cnt, 1000, false)) {
             // Card was found. Check that it supports EMV
             if(dev_list[0].rfInterface == RFAL_NFC_INTERFACE_ISODEP) {
+                result->nfc_data.uid_len = dev_list[0].dev.nfca.nfcId1Len;
+                result->nfc_data.atqa[0] = dev_list[0].dev.nfca.sensRes.anticollisionInfo;
+                result->nfc_data.atqa[1] = dev_list[0].dev.nfca.sensRes.platformInfo;
+                result->nfc_data.sak = dev_list[0].dev.nfca.selRes.sak;
+                memcpy(
+                    result->nfc_data.uid, dev_list[0].dev.nfca.nfcId1, result->nfc_data.uid_len);
+                result->nfc_data.protocol = NfcDeviceProtocolEMV;
+
                 FURI_LOG_I(NFC_WORKER_TAG, "Send select PPSE command");
                 tx_len = emv_prepare_select_ppse(tx_buff);
                 err = api_hal_nfc_data_exchange(tx_buff, tx_len, &rx_buff, &rx_len, false);
@@ -203,7 +273,7 @@ void nfc_worker_read_emv(NfcWorker* nfc_worker) {
                     "Select application response received. Start parsing response");
                 if(emv_decode_select_app_response(rx_buff, *rx_len, &emv_app)) {
                     FURI_LOG_I(NFC_WORKER_TAG, "Card name: %s", emv_app.name);
-                    memcpy(result->name, emv_app.name, sizeof(emv_app.name));
+                    memcpy(result->emv_data.name, emv_app.name, sizeof(emv_app.name));
                 } else {
                     FURI_LOG_E(NFC_WORKER_TAG, "Can't read card name");
                     api_hal_nfc_deactivate();
@@ -220,7 +290,8 @@ void nfc_worker_read_emv(NfcWorker* nfc_worker) {
                 }
                 if(emv_decode_get_proc_opt(rx_buff, *rx_len, &emv_app)) {
                     FURI_LOG_I(NFC_WORKER_TAG, "Card number parsed");
-                    memcpy(result->number, emv_app.card_number, sizeof(emv_app.card_number));
+                    memcpy(
+                        result->emv_data.number, emv_app.card_number, sizeof(emv_app.card_number));
                     // Notify caller and exit
                     if(nfc_worker->callback) {
                         nfc_worker->callback(nfc_worker->context);
@@ -255,7 +326,10 @@ void nfc_worker_read_emv(NfcWorker* nfc_worker) {
                     }
                     if(pan_found) {
                         FURI_LOG_I(NFC_WORKER_TAG, "Card PAN found");
-                        memcpy(result->number, emv_app.card_number, sizeof(emv_app.card_number));
+                        memcpy(
+                            result->emv_data.number,
+                            emv_app.card_number,
+                            sizeof(emv_app.card_number));
                         // Notify caller and exit
                         if(nfc_worker->callback) {
                             nfc_worker->callback(nfc_worker->context);
@@ -286,7 +360,7 @@ void nfc_worker_emulate_emv(NfcWorker* nfc_worker) {
     uint16_t tx_len = 0;
     uint8_t* rx_buff;
     uint16_t* rx_len;
-    NfcDeviceData params = {
+    NfcDeviceCommomData params = {
         .uid = {0xCF, 0x72, 0xd4, 0x40},
         .uid_len = 4,
         .atqa = {0x00, 0x04},
@@ -343,7 +417,7 @@ void nfc_worker_emulate_emv(NfcWorker* nfc_worker) {
     }
 }
 
-void nfc_worker_read_mf_ultralight(NfcWorker* nfc_worker) {
+void nfc_worker_read_mifare_ul(NfcWorker* nfc_worker) {
     ReturnCode err;
     rfalNfcDevice* dev_list;
     uint8_t dev_cnt = 0;
@@ -351,21 +425,20 @@ void nfc_worker_read_mf_ultralight(NfcWorker* nfc_worker) {
     uint16_t tx_len = 0;
     uint8_t* rx_buff;
     uint16_t* rx_len;
-    MfUltralightRead mf_ul_read;
-    NfcMifareUlData* result = &nfc_worker->last_result->nfc_mifare_ul_data;
+    MifareUlDevice mf_ul_read;
+    NfcDeviceData* result = nfc_worker->dev_data;
 
-    while(nfc_worker->state == NfcWorkerStateReadMfUltralight) {
+    while(nfc_worker->state == NfcWorkerStateReadMifareUl) {
         api_hal_nfc_deactivate();
         memset(&mf_ul_read, 0, sizeof(mf_ul_read));
-        if(api_hal_nfc_detect(&dev_list, &dev_cnt, 1000, false)) {
+        if(api_hal_nfc_detect(&dev_list, &dev_cnt, 300, false)) {
             if(dev_list[0].type == RFAL_NFC_LISTEN_TYPE_NFCA &&
                mf_ul_check_card_type(
                    dev_list[0].dev.nfca.sensRes.anticollisionInfo,
                    dev_list[0].dev.nfca.sensRes.platformInfo,
                    dev_list[0].dev.nfca.selRes.sak)) {
                 // Get Mifare Ultralight version
-                FURI_LOG_I(
-                    NFC_WORKER_TAG, "Found Mifare Ultralight tag. Trying to get tag version");
+                FURI_LOG_I(NFC_WORKER_TAG, "Found Mifare Ultralight tag. Reading tag version");
                 tx_len = mf_ul_prepare_get_version(tx_buff);
                 err = api_hal_nfc_data_exchange(tx_buff, tx_len, &rx_buff, &rx_len, false);
                 if(err == ERR_NONE) {
@@ -378,7 +451,7 @@ void nfc_worker_read_mf_ultralight(NfcWorker* nfc_worker) {
                 } else if(err == ERR_TIMEOUT) {
                     FURI_LOG_W(
                         NFC_WORKER_TAG,
-                        "Card doesn't respond to GET VERSION command. Reinit card and set default read parameters");
+                        "Card doesn't respond to GET VERSION command. Setting default read parameters");
                     err = ERR_NONE;
                     mf_ul_set_default_version(&mf_ul_read);
                     // Reinit device
@@ -395,37 +468,58 @@ void nfc_worker_read_mf_ultralight(NfcWorker* nfc_worker) {
                     continue;
                 }
 
-                // Dump Mifare Ultralight card
-                FURI_LOG_I(NFC_WORKER_TAG, "Trying to read pages");
                 if(mf_ul_read.support_fast_read) {
-                    // Read card with FAST_READ command
+                    FURI_LOG_I(NFC_WORKER_TAG, "Reading pages ...");
                     tx_len = mf_ul_prepare_fast_read(tx_buff, 0x00, mf_ul_read.pages_to_read - 1);
-                    err = api_hal_nfc_data_exchange(tx_buff, tx_len, &rx_buff, &rx_len, false);
-                    if(err == ERR_NONE) {
-                        FURI_LOG_I(
-                            NFC_WORKER_TAG,
-                            "Fast read pages %d - %d succeed",
-                            0,
-                            mf_ul_read.pages_to_read - 1);
-                        memcpy(mf_ul_read.dump, rx_buff, mf_ul_read.pages_to_read * 4);
-                        mf_ul_read.pages_readed = mf_ul_read.pages_to_read;
-                    } else {
-                        FURI_LOG_E(NFC_WORKER_TAG, "Fast read failed");
+                    if(api_hal_nfc_data_exchange(tx_buff, tx_len, &rx_buff, &rx_len, false)) {
+                        FURI_LOG_E(NFC_WORKER_TAG, "Failed reading pages");
                         continue;
+                    } else {
+                        mf_ul_parse_fast_read_response(
+                            rx_buff, 0x00, mf_ul_read.pages_to_read - 1, &mf_ul_read);
+                    }
+
+                    FURI_LOG_I(NFC_WORKER_TAG, "Reading signature ...");
+                    tx_len = mf_ul_prepare_read_signature(tx_buff);
+                    if(api_hal_nfc_data_exchange(tx_buff, tx_len, &rx_buff, &rx_len, false)) {
+                        FURI_LOG_W(NFC_WORKER_TAG, "Failed reading signature");
+                        memset(mf_ul_read.data.signature, 0, sizeof(mf_ul_read.data.signature));
+                    } else {
+                        mf_ul_parse_read_signature_response(rx_buff, &mf_ul_read);
+                    }
+
+                    FURI_LOG_I(NFC_WORKER_TAG, "Reading 3 counters ...");
+                    for(uint8_t i = 0; i < 3; i++) {
+                        tx_len = mf_ul_prepare_read_cnt(tx_buff, i);
+                        if(api_hal_nfc_data_exchange(tx_buff, tx_len, &rx_buff, &rx_len, false)) {
+                            FURI_LOG_W(NFC_WORKER_TAG, "Failed reading Counter %d", i);
+                            mf_ul_read.data.counter[i] = 0;
+                        } else {
+                            mf_ul_parse_read_cnt_response(rx_buff, i, &mf_ul_read);
+                        }
+                    }
+
+                    FURI_LOG_I(NFC_WORKER_TAG, "Checking tearing flags ...");
+                    for(uint8_t i = 0; i < 3; i++) {
+                        tx_len = mf_ul_prepare_check_tearing(tx_buff, i);
+                        if(api_hal_nfc_data_exchange(tx_buff, tx_len, &rx_buff, &rx_len, false)) {
+                            FURI_LOG_E(NFC_WORKER_TAG, "Error checking tearing flag %d", i);
+                            mf_ul_read.data.tearing[i] = MF_UL_TEARING_FLAG_DEFAULT;
+                        } else {
+                            mf_ul_parse_check_tearing_response(rx_buff, i, &mf_ul_read);
+                        }
                     }
                 } else {
                     // READ card with READ command (4 pages at a time)
                     for(uint8_t page = 0; page < mf_ul_read.pages_to_read; page += 4) {
+                        FURI_LOG_I(NFC_WORKER_TAG, "Reading pages %d - %d ...", page, page + 3);
                         tx_len = mf_ul_prepare_read(tx_buff, page);
-                        err = api_hal_nfc_data_exchange(tx_buff, tx_len, &rx_buff, &rx_len, false);
-                        if(err == ERR_NONE) {
-                            FURI_LOG_I(
-                                NFC_WORKER_TAG, "Read pages %d - %d succeed", page, page + 3);
-                            memcpy(&mf_ul_read.dump[page * 4], rx_buff, 4 * 4);
-                            mf_ul_read.pages_readed += 4;
-                        } else {
-                            FURI_LOG_W(
+                        if(api_hal_nfc_data_exchange(tx_buff, tx_len, &rx_buff, &rx_len, false)) {
+                            FURI_LOG_E(
                                 NFC_WORKER_TAG, "Read pages %d - %d failed", page, page + 3);
+                            continue;
+                        } else {
+                            mf_ul_parse_read_response(rx_buff, page, &mf_ul_read);
                         }
                     }
                 }
@@ -435,20 +529,11 @@ void nfc_worker_read_mf_ultralight(NfcWorker* nfc_worker) {
                 result->nfc_data.atqa[0] = dev_list[0].dev.nfca.sensRes.anticollisionInfo;
                 result->nfc_data.atqa[1] = dev_list[0].dev.nfca.sensRes.platformInfo;
                 result->nfc_data.sak = dev_list[0].dev.nfca.selRes.sak;
+                result->nfc_data.protocol = NfcDeviceProtocolMifareUl;
                 memcpy(
                     result->nfc_data.uid, dev_list[0].dev.nfca.nfcId1, result->nfc_data.uid_len);
-                memcpy(result->man_block, mf_ul_read.dump, 4 * 3);
-                memcpy(result->otp, &mf_ul_read.dump[4 * 3], 4);
-                result->dump_size = mf_ul_read.pages_readed * 4;
-                memcpy(result->full_dump, mf_ul_read.dump, result->dump_size);
-
-                for(uint8_t i = 0; i < mf_ul_read.pages_readed * 4; i += 4) {
-                    printf("Page %2d: ", i / 4);
-                    for(uint8_t j = 0; j < 4; j++) {
-                        printf("%02X ", mf_ul_read.dump[i + j]);
-                    }
-                    printf("\r\n");
-                }
+                result->mf_ul_data = mf_ul_read.data;
+
                 // Notify caller and exit
                 if(nfc_worker->callback) {
                     nfc_worker->callback(nfc_worker->context);
@@ -464,6 +549,43 @@ void nfc_worker_read_mf_ultralight(NfcWorker* nfc_worker) {
     }
 }
 
+void nfc_worker_emulate_mifare_ul(NfcWorker* nfc_worker) {
+    ReturnCode err;
+    uint8_t tx_buff[255] = {};
+    uint16_t tx_len = 0;
+    uint8_t* rx_buff;
+    uint16_t* rx_len;
+    NfcDeviceData* data = nfc_worker->dev_data;
+
+    while(nfc_worker->state == NfcWorkerStateEmulateMifareUl) {
+        if(api_hal_nfc_listen(
+               data->nfc_data.uid,
+               data->nfc_data.uid_len,
+               data->nfc_data.atqa,
+               data->nfc_data.sak,
+               1000)) {
+            FURI_LOG_I(NFC_WORKER_TAG, "Hello my dudes");
+            // Prepare version answer
+            tx_len = sizeof(data->mf_ul_data.version);
+            memcpy(tx_buff, &data->mf_ul_data.version, tx_len);
+            err = api_hal_nfc_data_exchange(tx_buff, tx_len, &rx_buff, &rx_len, false);
+            if(err == ERR_NONE) {
+                FURI_LOG_I(NFC_WORKER_TAG, "Received 1st message:");
+                for(uint16_t i = 0; i < *rx_len; i++) {
+                    printf("%02X ", rx_buff[i]);
+                }
+                printf("\r\n");
+            } else {
+                FURI_LOG_E(NFC_WORKER_TAG, "Error in 1st data exchange: select PPSE");
+                api_hal_nfc_deactivate();
+                continue;
+            }
+        }
+        FURI_LOG_W(NFC_WORKER_TAG, "Hello my dudes");
+        osDelay(10);
+    }
+}
+
 void nfc_worker_field(NfcWorker* nfc_worker) {
     api_hal_nfc_field_on();
     while(nfc_worker->state == NfcWorkerStateField) {

+ 4 - 12
applications/nfc/nfc_worker.h

@@ -2,14 +2,6 @@
 
 #include "nfc_device.h"
 
-typedef struct {
-    union {
-        NfcDeviceData nfc_detect_data;
-        NfcEmvData nfc_emv_data;
-        NfcMifareUlData nfc_mifare_ul_data;
-    };
-} NfcWorkerResult;
-
 typedef struct NfcWorker NfcWorker;
 
 typedef enum {
@@ -20,10 +12,12 @@ typedef enum {
     // Main worker states
     NfcWorkerStateDetect,
     NfcWorkerStateEmulate,
+    NfcWorkerStateReadEMVApp,
     NfcWorkerStateReadEMV,
     NfcWorkerStateEmulateEMV,
     NfcWorkerStateField,
-    NfcWorkerStateReadMfUltralight,
+    NfcWorkerStateReadMifareUl,
+    NfcWorkerStateEmulateMifareUl,
     // Transition
     NfcWorkerStateStop,
 } NfcWorkerState;
@@ -36,14 +30,12 @@ NfcWorkerState nfc_worker_get_state(NfcWorker* nfc_worker);
 
 ReturnCode nfc_worker_get_error(NfcWorker* nfc_worker);
 
-void nfc_worker_set_emulation_params(NfcWorker* nfc_worker, NfcDeviceData* data);
-
 void nfc_worker_free(NfcWorker* nfc_worker);
 
 void nfc_worker_start(
     NfcWorker* nfc_worker,
     NfcWorkerState state,
-    NfcWorkerResult* result_dest,
+    NfcDeviceData* dev_data,
     NfcWorkerCallback callback,
     void* context);
 

+ 7 - 3
applications/nfc/nfc_worker_i.h

@@ -20,10 +20,10 @@ struct NfcWorker {
     osThreadAttr_t thread_attr;
     osThreadId_t thread;
 
-    NfcWorkerResult* last_result;
+    NfcDeviceData* dev_data;
+
     NfcWorkerCallback callback;
     void* context;
-    NfcDeviceData emulate_params;
 
     NfcWorkerState state;
     ReturnCode error;
@@ -33,6 +33,8 @@ void nfc_worker_change_state(NfcWorker* nfc_worker, NfcWorkerState state);
 
 void nfc_worker_task(void* context);
 
+void nfc_worker_read_emv_app(NfcWorker* nfc_worker);
+
 void nfc_worker_read_emv(NfcWorker* nfc_worker);
 
 void nfc_worker_emulate_emv(NfcWorker* nfc_worker);
@@ -43,4 +45,6 @@ void nfc_worker_emulate(NfcWorker* nfc_worker);
 
 void nfc_worker_field(NfcWorker* nfc_worker);
 
-void nfc_worker_read_mf_ultralight(NfcWorker* nfc_worker);
+void nfc_worker_read_mifare_ul(NfcWorker* nfc_worker);
+
+void nfc_worker_emulate_mifare_ul(NfcWorker* nfc_worker);

+ 17 - 10
applications/nfc/scenes/nfc_scene_card_menu.c

@@ -10,19 +10,21 @@ enum SubmenuIndex {
 void nfc_scene_card_menu_submenu_callback(void* context, uint32_t index) {
     Nfc* nfc = (Nfc*)context;
 
-    view_dispatcher_send_custom_event(nfc->nfc_common.view_dispatcher, index);
+    view_dispatcher_send_custom_event(nfc->view_dispatcher, index);
 }
 
 const void nfc_scene_card_menu_on_enter(void* context) {
     Nfc* nfc = (Nfc*)context;
     Submenu* submenu = nfc->submenu;
 
-    submenu_add_item(
-        submenu,
-        "Run compatible app",
-        SubmenuIndexRunApp,
-        nfc_scene_card_menu_submenu_callback,
-        nfc);
+    if(nfc->dev.dev_data.nfc_data.protocol > NfcDeviceProtocolUnknown) {
+        submenu_add_item(
+            submenu,
+            "Run compatible app",
+            SubmenuIndexRunApp,
+            nfc_scene_card_menu_submenu_callback,
+            nfc);
+    }
     submenu_add_item(
         submenu,
         "Additional reading scripts",
@@ -36,7 +38,7 @@ const void nfc_scene_card_menu_on_enter(void* context) {
     submenu_set_selected_item(
         nfc->submenu, scene_manager_get_scene_state(nfc->scene_manager, NfcSceneCardMenu));
 
-    view_dispatcher_switch_to_view(nfc->nfc_common.view_dispatcher, NfcViewMenu);
+    view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu);
 }
 
 const bool nfc_scene_card_menu_on_event(void* context, SceneManagerEvent event) {
@@ -46,12 +48,16 @@ const bool nfc_scene_card_menu_on_event(void* context, SceneManagerEvent event)
         if(event.event == SubmenuIndexRunApp) {
             scene_manager_set_scene_state(
                 nfc->scene_manager, NfcSceneCardMenu, SubmenuIndexRunApp);
-            scene_manager_next_scene(nfc->scene_manager, NfcSceneNotImplemented);
+            if(nfc->dev.dev_data.nfc_data.protocol == NfcDeviceProtocolMifareUl) {
+                scene_manager_next_scene(nfc->scene_manager, NfcSceneReadMifareUl);
+            } else if(nfc->dev.dev_data.nfc_data.protocol == NfcDeviceProtocolEMV) {
+                scene_manager_next_scene(nfc->scene_manager, NfcSceneReadEmvApp);
+            }
             return true;
         } else if(event.event == SubmenuIndexChooseScript) {
             scene_manager_set_scene_state(
                 nfc->scene_manager, NfcSceneCardMenu, SubmenuIndexChooseScript);
-            scene_manager_next_scene(nfc->scene_manager, NfcSceneNotImplemented);
+            scene_manager_next_scene(nfc->scene_manager, NfcSceneScriptsMenu);
             return true;
         } else if(event.event == SubmenuIndexEmulate) {
             scene_manager_set_scene_state(
@@ -60,6 +66,7 @@ const bool nfc_scene_card_menu_on_event(void* context, SceneManagerEvent event)
             return true;
         } else if(event.event == SubmenuIndexSave) {
             scene_manager_set_scene_state(nfc->scene_manager, NfcSceneCardMenu, SubmenuIndexSave);
+            nfc->dev.format = NfcDeviceSaveFormatUid;
             scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveName);
             return true;
         }

+ 9 - 5
applications/nfc/scenes/nfc_scene_config.h

@@ -16,8 +16,12 @@ ADD_SCENE(nfc, scripts_menu, ScriptsMenu)
 ADD_SCENE(nfc, read_mifare_ul, ReadMifareUl)
 ADD_SCENE(nfc, read_mifare_ul_success, ReadMifareUlSuccess)
 ADD_SCENE(nfc, mifare_ul_menu, MifareUlMenu)
-ADD_SCENE(nfc, debug_menu, DebugMenu)
-ADD_SCENE(nfc, debug_detect, DebugDetect)
-ADD_SCENE(nfc, debug_emulate, DebugEmulate)
-ADD_SCENE(nfc, debug_read_emv, DebugReadEmv)
-ADD_SCENE(nfc, debug_read_mifare_ul, DebugReadMifareUl)
+ADD_SCENE(nfc, emulate_mifare_ul, EmulateMifareUl)
+ADD_SCENE(nfc, read_emv_app, ReadEmvApp)
+ADD_SCENE(nfc, read_emv_app_success, ReadEmvAppSuccess)
+ADD_SCENE(nfc, device_info, DeviceInfo)
+ADD_SCENE(nfc, delete, Delete)
+ADD_SCENE(nfc, delete_success, DeleteSuccess)
+ADD_SCENE(nfc, run_emv_app_confirm, RunEmvAppConfirm)
+ADD_SCENE(nfc, read_emv_data, ReadEmvData)
+ADD_SCENE(nfc, read_emv_data_success, ReadEmvDataSuccess)

+ 0 - 14
applications/nfc/scenes/nfc_scene_debug_detect.c

@@ -1,14 +0,0 @@
-#include "../nfc_i.h"
-
-const void nfc_scene_debug_detect_on_enter(void* context) {
-    Nfc* nfc = (Nfc*)context;
-
-    view_dispatcher_switch_to_view(nfc->nfc_common.view_dispatcher, NfcViewDetect);
-}
-
-const bool nfc_scene_debug_detect_on_event(void* context, SceneManagerEvent event) {
-    return false;
-}
-
-const void nfc_scene_debug_detect_on_exit(void* context) {
-}

+ 0 - 14
applications/nfc/scenes/nfc_scene_debug_emulate.c

@@ -1,14 +0,0 @@
-#include "../nfc_i.h"
-
-const void nfc_scene_debug_emulate_on_enter(void* context) {
-    Nfc* nfc = (Nfc*)context;
-
-    view_dispatcher_switch_to_view(nfc->nfc_common.view_dispatcher, NfcViewEmulate);
-}
-
-const bool nfc_scene_debug_emulate_on_event(void* context, SceneManagerEvent event) {
-    return false;
-}
-
-const void nfc_scene_debug_emulate_on_exit(void* context) {
-}

+ 0 - 72
applications/nfc/scenes/nfc_scene_debug_menu.c

@@ -1,72 +0,0 @@
-#include "../nfc_i.h"
-
-enum SubmenuIndex {
-    SubmenuIndexDetect,
-    SubmenuIndexEmulate,
-    SubmenuIndexReadEmv,
-    SubmenuIndexReadMifareUl,
-};
-
-void nfc_scene_debug_menu_submenu_callback(void* context, uint32_t index) {
-    Nfc* nfc = (Nfc*)context;
-
-    view_dispatcher_send_custom_event(nfc->nfc_common.view_dispatcher, index);
-}
-
-const void nfc_scene_debug_menu_on_enter(void* context) {
-    Nfc* nfc = (Nfc*)context;
-    Submenu* submenu = nfc->submenu;
-
-    submenu_add_item(
-        submenu, "Detect", SubmenuIndexDetect, nfc_scene_debug_menu_submenu_callback, nfc);
-    submenu_add_item(
-        submenu, "Emulate", SubmenuIndexEmulate, nfc_scene_debug_menu_submenu_callback, nfc);
-    submenu_add_item(
-        submenu, "Read EMV", SubmenuIndexReadEmv, nfc_scene_debug_menu_submenu_callback, nfc);
-    submenu_add_item(
-        submenu,
-        "Read Mifare Ultralight",
-        SubmenuIndexReadMifareUl,
-        nfc_scene_debug_menu_submenu_callback,
-        nfc);
-    submenu_set_selected_item(
-        nfc->submenu, scene_manager_get_scene_state(nfc->scene_manager, NfcSceneDebugMenu));
-
-    view_dispatcher_switch_to_view(nfc->nfc_common.view_dispatcher, NfcViewMenu);
-}
-
-const bool nfc_scene_debug_menu_on_event(void* context, SceneManagerEvent event) {
-    Nfc* nfc = (Nfc*)context;
-
-    if(event.type == SceneManagerEventTypeCustom) {
-        if(event.event == SubmenuIndexDetect) {
-            scene_manager_set_scene_state(
-                nfc->scene_manager, NfcSceneDebugMenu, SubmenuIndexDetect);
-            scene_manager_next_scene(nfc->scene_manager, NfcSceneDebugDetect);
-            return true;
-        } else if(event.event == SubmenuIndexEmulate) {
-            scene_manager_set_scene_state(
-                nfc->scene_manager, NfcSceneDebugMenu, SubmenuIndexEmulate);
-            scene_manager_next_scene(nfc->scene_manager, NfcSceneDebugEmulate);
-            return true;
-        } else if(event.event == SubmenuIndexReadEmv) {
-            scene_manager_set_scene_state(
-                nfc->scene_manager, NfcSceneDebugMenu, SubmenuIndexReadEmv);
-            scene_manager_next_scene(nfc->scene_manager, NfcSceneDebugReadEmv);
-            return true;
-        } else if(event.event == SubmenuIndexReadMifareUl) {
-            scene_manager_set_scene_state(
-                nfc->scene_manager, NfcSceneDebugMenu, SubmenuIndexReadMifareUl);
-            scene_manager_next_scene(nfc->scene_manager, NfcSceneDebugReadMifareUl);
-            return true;
-        }
-    }
-
-    return false;
-}
-
-const void nfc_scene_debug_menu_on_exit(void* context) {
-    Nfc* nfc = (Nfc*)context;
-
-    submenu_clean(nfc->submenu);
-}

+ 0 - 14
applications/nfc/scenes/nfc_scene_debug_read_emv.c

@@ -1,14 +0,0 @@
-#include "../nfc_i.h"
-
-const void nfc_scene_debug_read_emv_on_enter(void* context) {
-    Nfc* nfc = (Nfc*)context;
-
-    view_dispatcher_switch_to_view(nfc->nfc_common.view_dispatcher, NfcViewEmv);
-}
-
-const bool nfc_scene_debug_read_emv_on_event(void* context, SceneManagerEvent event) {
-    return false;
-}
-
-const void nfc_scene_debug_read_emv_on_exit(void* context) {
-}

+ 0 - 14
applications/nfc/scenes/nfc_scene_debug_read_mifare_ul.c

@@ -1,14 +0,0 @@
-#include "../nfc_i.h"
-
-const void nfc_scene_debug_read_mifare_ul_on_enter(void* context) {
-    Nfc* nfc = (Nfc*)context;
-
-    view_dispatcher_switch_to_view(nfc->nfc_common.view_dispatcher, NfcViewMifareUl);
-}
-
-const bool nfc_scene_debug_read_mifare_ul_on_event(void* context, SceneManagerEvent event) {
-    return false;
-}
-
-const void nfc_scene_debug_read_mifare_ul_on_exit(void* context) {
-}

+ 95 - 0
applications/nfc/scenes/nfc_scene_delete.c

@@ -0,0 +1,95 @@
+#include "../nfc_i.h"
+
+void nfc_scene_delete_widget_callback(GuiButtonType result, void* context) {
+    Nfc* nfc = (Nfc*)context;
+
+    view_dispatcher_send_custom_event(nfc->view_dispatcher, result);
+}
+
+void nfc_scene_delete_on_enter(void* context) {
+    Nfc* nfc = (Nfc*)context;
+
+    // Setup Custom Widget view
+    char delete_str[64];
+    snprintf(delete_str, sizeof(delete_str), "Delete %s", nfc->dev.dev_name);
+    gui_widget_add_string_element(
+        nfc->widget, 64, 6, AlignCenter, AlignTop, FontPrimary, delete_str);
+    gui_widget_add_button_element(
+        nfc->widget, GuiButtonTypeLeft, "Back", nfc_scene_delete_widget_callback, nfc);
+    gui_widget_add_button_element(
+        nfc->widget, GuiButtonTypeRight, "Delete", nfc_scene_delete_widget_callback, nfc);
+    char uid_str[32];
+    NfcDeviceCommomData* data = &nfc->dev.dev_data.nfc_data;
+    if(data->uid_len == 4) {
+        snprintf(
+            uid_str,
+            sizeof(uid_str),
+            "UID: %02X %02X %02X %02X",
+            data->uid[0],
+            data->uid[1],
+            data->uid[2],
+            data->uid[3]);
+    } else if(data->uid_len == 7) {
+        snprintf(
+            uid_str,
+            sizeof(uid_str),
+            "UID: %02X %02X %02X %02X %02X %02X %02X",
+            data->uid[0],
+            data->uid[1],
+            data->uid[2],
+            data->uid[3],
+            data->uid[4],
+            data->uid[5],
+            data->uid[6]);
+    }
+    gui_widget_add_string_element(
+        nfc->widget, 64, 21, AlignCenter, AlignTop, FontSecondary, uid_str);
+
+    if(data->protocol > NfcDeviceProtocolUnknown) {
+        gui_widget_add_string_element(
+            nfc->widget,
+            10,
+            32,
+            AlignLeft,
+            AlignTop,
+            FontSecondary,
+            nfc_get_protocol(data->protocol));
+    }
+    // TODO change dinamically
+    gui_widget_add_string_element(
+        nfc->widget, 118, 32, AlignRight, AlignTop, FontSecondary, "NFC-A");
+    char sak_str[16];
+    snprintf(sak_str, sizeof(sak_str), "SAK: %02X", data->sak);
+    gui_widget_add_string_element(
+        nfc->widget, 10, 42, AlignLeft, AlignTop, FontSecondary, sak_str);
+    char atqa_str[16];
+    snprintf(atqa_str, sizeof(atqa_str), "ATQA: %02X%02X", data->atqa[0], data->atqa[1]);
+    gui_widget_add_string_element(
+        nfc->widget, 118, 42, AlignRight, AlignTop, FontSecondary, atqa_str);
+
+    view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget);
+}
+
+const bool nfc_scene_delete_on_event(void* context, SceneManagerEvent event) {
+    Nfc* nfc = (Nfc*)context;
+
+    if(event.type == SceneManagerEventTypeCustom) {
+        if(event.event == GuiButtonTypeLeft) {
+            return scene_manager_previous_scene(nfc->scene_manager);
+        } else if(event.event == GuiButtonTypeRight) {
+            if(nfc_device_delete(&nfc->dev)) {
+                scene_manager_next_scene(nfc->scene_manager, NfcSceneDeleteSuccess);
+            } else {
+                scene_manager_search_previous_scene(nfc->scene_manager, NfcSceneStart);
+            }
+            return true;
+        }
+    }
+    return false;
+}
+
+const void nfc_scene_delete_on_exit(void* context) {
+    Nfc* nfc = (Nfc*)context;
+
+    gui_widget_clear(nfc->widget);
+}

+ 47 - 0
applications/nfc/scenes/nfc_scene_delete_success.c

@@ -0,0 +1,47 @@
+#include "../nfc_i.h"
+
+#define SCENE_SAVE_SUCCESS_CUSTOM_EVENT (0UL)
+
+void nfc_scene_delete_success_popup_callback(void* context) {
+    Nfc* nfc = (Nfc*)context;
+    view_dispatcher_send_custom_event(nfc->view_dispatcher, SCENE_SAVE_SUCCESS_CUSTOM_EVENT);
+}
+
+const void nfc_scene_delete_success_on_enter(void* context) {
+    Nfc* nfc = (Nfc*)context;
+
+    // Setup view
+    Popup* popup = nfc->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, nfc);
+    popup_set_callback(popup, nfc_scene_delete_success_popup_callback);
+    popup_enable_timeout(popup);
+    view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup);
+}
+
+const bool nfc_scene_delete_success_on_event(void* context, SceneManagerEvent event) {
+    Nfc* nfc = (Nfc*)context;
+
+    if(event.type == SceneManagerEventTypeCustom) {
+        if(event.event == SCENE_SAVE_SUCCESS_CUSTOM_EVENT) {
+            return scene_manager_search_previous_scene(nfc->scene_manager, NfcSceneStart);
+        }
+    }
+    return false;
+}
+
+const void nfc_scene_delete_success_on_exit(void* context) {
+    Nfc* nfc = (Nfc*)context;
+
+    // Clear view
+    Popup* popup = nfc->popup;
+    popup_set_header(popup, NULL, 0, 0, AlignCenter, AlignBottom);
+    popup_set_text(popup, NULL, 0, 0, AlignCenter, AlignTop);
+    popup_set_icon(popup, 0, 0, NULL);
+    popup_set_callback(popup, NULL);
+    popup_set_context(popup, NULL);
+    popup_set_timeout(popup, 0);
+    popup_disable_timeout(popup);
+}

+ 185 - 0
applications/nfc/scenes/nfc_scene_device_info.c

@@ -0,0 +1,185 @@
+#include "../nfc_i.h"
+
+#define NFC_SCENE_DEVICE_INFO_TEXTBOX_CUSTOM_EVENT (0UL)
+
+enum {
+    NfcSceneDeviceInfoUid,
+    NfcSceneDeviceInfoData,
+};
+
+void nfc_scene_device_info_widget_callback(GuiButtonType result, void* context) {
+    Nfc* nfc = (Nfc*)context;
+
+    view_dispatcher_send_custom_event(nfc->view_dispatcher, result);
+}
+
+void nfc_scene_device_info_dialog_callback(DialogExResult result, void* context) {
+    Nfc* nfc = (Nfc*)context;
+
+    view_dispatcher_send_custom_event(nfc->view_dispatcher, result);
+}
+
+void nfc_scene_device_info_text_box_callback(void* context) {
+    Nfc* nfc = (Nfc*)context;
+
+    view_dispatcher_send_custom_event(
+        nfc->view_dispatcher, NFC_SCENE_DEVICE_INFO_TEXTBOX_CUSTOM_EVENT);
+}
+
+void nfc_scene_device_info_on_enter(void* context) {
+    Nfc* nfc = (Nfc*)context;
+
+    // Setup Custom Widget view
+    gui_widget_add_string_element(
+        nfc->widget, 64, 6, AlignCenter, AlignTop, FontSecondary, nfc->dev.dev_name);
+    gui_widget_add_button_element(
+        nfc->widget, GuiButtonTypeLeft, "Back", nfc_scene_device_info_widget_callback, nfc);
+    gui_widget_add_button_element(
+        nfc->widget, GuiButtonTypeRight, "Data", nfc_scene_device_info_widget_callback, nfc);
+    char uid_str[32];
+    NfcDeviceCommomData* data = &nfc->dev.dev_data.nfc_data;
+    if(data->uid_len == 4) {
+        snprintf(
+            uid_str,
+            sizeof(uid_str),
+            "UID: %02X %02X %02X %02X",
+            data->uid[0],
+            data->uid[1],
+            data->uid[2],
+            data->uid[3]);
+    } else if(data->uid_len == 7) {
+        snprintf(
+            uid_str,
+            sizeof(uid_str),
+            "UID: %02X %02X %02X %02X %02X %02X %02X",
+            data->uid[0],
+            data->uid[1],
+            data->uid[2],
+            data->uid[3],
+            data->uid[4],
+            data->uid[5],
+            data->uid[6]);
+    }
+    gui_widget_add_string_element(
+        nfc->widget, 64, 21, AlignCenter, AlignTop, FontSecondary, uid_str);
+
+    if(data->protocol > NfcDeviceProtocolUnknown) {
+        gui_widget_add_string_element(
+            nfc->widget,
+            10,
+            32,
+            AlignLeft,
+            AlignTop,
+            FontSecondary,
+            nfc_get_protocol(data->protocol));
+    }
+    // TODO change dinamically
+    gui_widget_add_string_element(
+        nfc->widget, 118, 32, AlignRight, AlignTop, FontSecondary, "NFC-A");
+    char sak_str[16];
+    snprintf(sak_str, sizeof(sak_str), "SAK: %02X", data->sak);
+    gui_widget_add_string_element(
+        nfc->widget, 10, 42, AlignLeft, AlignTop, FontSecondary, sak_str);
+    char atqa_str[16];
+    snprintf(atqa_str, sizeof(atqa_str), "ATQA: %02X%02X", data->atqa[0], data->atqa[1]);
+    gui_widget_add_string_element(
+        nfc->widget, 118, 42, AlignRight, AlignTop, FontSecondary, atqa_str);
+
+    // Setup Data View
+    if(nfc->dev.format == NfcDeviceSaveFormatUid) {
+        DialogEx* dialog_ex = nfc->dialog_ex;
+        dialog_ex_set_left_button_text(dialog_ex, "Back");
+        dialog_ex_set_text(dialog_ex, "No data", 64, 32, AlignCenter, AlignCenter);
+        dialog_ex_set_context(dialog_ex, nfc);
+        dialog_ex_set_result_callback(dialog_ex, nfc_scene_device_info_dialog_callback);
+    } else if(nfc->dev.format == NfcDeviceSaveFormatMifareUl) {
+        MifareUlData* mf_ul_data = (MifareUlData*)&nfc->dev.dev_data.mf_ul_data;
+        TextBox* text_box = nfc->text_box;
+        text_box_set_context(text_box, nfc);
+        text_box_set_exit_callback(text_box, nfc_scene_device_info_text_box_callback);
+        text_box_set_font(text_box, TextBoxFontHex);
+        for(uint16_t i = 0; i < mf_ul_data->data_size; i += 2) {
+            if(!(i % 8) && i) {
+                string_push_back(nfc->text_box_store, '\n');
+            }
+            string_cat_printf(
+                nfc->text_box_store, "%02X%02X ", mf_ul_data->data[i], mf_ul_data->data[i + 1]);
+        }
+        text_box_set_text(text_box, string_get_cstr(nfc->text_box_store));
+    } else if(nfc->dev.format == NfcDeviceSaveFormatBankCard) {
+        NfcEmvData* emv_data = &nfc->dev.dev_data.emv_data;
+        BankCard* bank_card = nfc->bank_card;
+        bank_card_set_name(bank_card, emv_data->name);
+        bank_card_set_number(bank_card, emv_data->number);
+        if(!strcmp(emv_data->name, "")) {
+            bank_card_set_cardholder_name(bank_card, emv_data->cardholder);
+        }
+        if(emv_data->exp_mon) {
+            bank_card_set_exp_date(bank_card, emv_data->exp_mon, emv_data->exp_year);
+        }
+    }
+    scene_manager_set_scene_state(nfc->scene_manager, NfcSceneDeviceInfo, NfcSceneDeviceInfoUid);
+    view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget);
+}
+
+const bool nfc_scene_device_info_on_event(void* context, SceneManagerEvent event) {
+    Nfc* nfc = (Nfc*)context;
+    bool consumed = false;
+    uint32_t state = scene_manager_get_scene_state(nfc->scene_manager, NfcSceneDeviceInfo);
+
+    if(event.type == SceneManagerEventTypeCustom) {
+        if((state == NfcSceneDeviceInfoUid) && (event.event == GuiButtonTypeLeft)) {
+            consumed = scene_manager_previous_scene(nfc->scene_manager);
+        } else if((state == NfcSceneDeviceInfoUid) && (event.event == GuiButtonTypeRight)) {
+            if(nfc->dev.format == NfcDeviceSaveFormatUid) {
+                scene_manager_set_scene_state(
+                    nfc->scene_manager, NfcSceneDeviceInfo, NfcSceneDeviceInfoData);
+                view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewDialogEx);
+                consumed = true;
+            } else if(nfc->dev.format == NfcDeviceSaveFormatMifareUl) {
+                scene_manager_set_scene_state(
+                    nfc->scene_manager, NfcSceneDeviceInfo, NfcSceneDeviceInfoData);
+                view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewTextBox);
+                consumed = true;
+            } else if(nfc->dev.format == NfcDeviceSaveFormatBankCard) {
+                scene_manager_set_scene_state(
+                    nfc->scene_manager, NfcSceneDeviceInfo, NfcSceneDeviceInfoData);
+                view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewBankCard);
+                consumed = true;
+            }
+        } else if(state == NfcSceneDeviceInfoData) {
+            scene_manager_set_scene_state(
+                nfc->scene_manager, NfcSceneDeviceInfo, NfcSceneDeviceInfoUid);
+            view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget);
+            consumed = true;
+        }
+    }
+    return consumed;
+}
+
+const void nfc_scene_device_info_on_exit(void* context) {
+    Nfc* nfc = (Nfc*)context;
+
+    // Clear Custom Widget
+    gui_widget_clear(nfc->widget);
+
+    if(nfc->dev.format == NfcDeviceSaveFormatUid) {
+        // Clear Dialog
+        DialogEx* dialog_ex = nfc->dialog_ex;
+        dialog_ex_set_header(dialog_ex, NULL, 0, 0, AlignCenter, AlignCenter);
+        dialog_ex_set_text(dialog_ex, NULL, 0, 0, AlignCenter, AlignTop);
+        dialog_ex_set_icon(dialog_ex, 0, 0, NULL);
+        dialog_ex_set_left_button_text(dialog_ex, NULL);
+        dialog_ex_set_right_button_text(dialog_ex, NULL);
+        dialog_ex_set_center_button_text(dialog_ex, NULL);
+        dialog_ex_set_result_callback(dialog_ex, NULL);
+        dialog_ex_set_context(dialog_ex, NULL);
+    } else if(nfc->dev.format == NfcDeviceSaveFormatMifareUl) {
+        // Clear TextBox
+        text_box_clean(nfc->text_box);
+        string_clean(nfc->text_box_store);
+    } else if(nfc->dev.format == NfcDeviceSaveFormatBankCard) {
+        // Clear Bank Card
+        bank_card_clear(nfc->bank_card);
+    }
+}

+ 60 - 0
applications/nfc/scenes/nfc_scene_emulate_mifare_ul.c

@@ -0,0 +1,60 @@
+#include "../nfc_i.h"
+
+const void nfc_scene_emulate_mifare_ul_on_enter(void* context) {
+    Nfc* nfc = (Nfc*)context;
+
+    // Setup view
+    Popup* popup = nfc->popup;
+    NfcDeviceCommomData* data = &nfc->dev.dev_data.nfc_data;
+
+    if(strcmp(nfc->dev.dev_name, "")) {
+        nfc_text_store_set(nfc, "%s", nfc->dev.dev_name);
+    } else if(data->uid_len == 4) {
+        nfc_text_store_set(
+            nfc, "%02X %02X %02X %02X", data->uid[0], data->uid[1], data->uid[2], data->uid[3]);
+    } else if(data->uid_len == 7) {
+        nfc_text_store_set(
+            nfc,
+            "%02X %02X %02X %02X\n%02X %02X %02X",
+            data->uid[0],
+            data->uid[1],
+            data->uid[2],
+            data->uid[3],
+            data->uid[4],
+            data->uid[5],
+            data->uid[6]);
+    }
+
+    popup_set_icon(popup, 0, 3, &I_RFIDDolphinSend_97x61);
+    popup_set_header(popup, "Emulating Mf Ul", 56, 31, AlignLeft, AlignTop);
+    popup_set_text(popup, nfc->text_store, 56, 43, AlignLeft, AlignTop);
+
+    // Setup and start worker
+
+    view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup);
+    nfc_worker_start(nfc->worker, NfcWorkerStateEmulateMifareUl, &nfc->dev.dev_data, NULL, nfc);
+}
+
+const bool nfc_scene_emulate_mifare_ul_on_event(void* context, SceneManagerEvent event) {
+    Nfc* nfc = (Nfc*)context;
+    bool consumed = false;
+
+    if(event.type == SceneManagerEventTypeTick) {
+        notification_message(nfc->notifications, &sequence_blink_blue_10);
+        consumed = true;
+    }
+    return consumed;
+}
+
+const void nfc_scene_emulate_mifare_ul_on_exit(void* context) {
+    Nfc* nfc = (Nfc*)context;
+
+    // Stop worker
+    nfc_worker_stop(nfc->worker);
+
+    // Clear view
+    Popup* popup = nfc->popup;
+    popup_set_header(popup, NULL, 0, 0, AlignCenter, AlignBottom);
+    popup_set_text(popup, NULL, 0, 0, AlignCenter, AlignTop);
+    popup_set_icon(popup, 0, 0, NULL);
+}

+ 6 - 8
applications/nfc/scenes/nfc_scene_emulate_uid.c

@@ -5,10 +5,10 @@ const void nfc_scene_emulate_uid_on_enter(void* context) {
 
     // Setup view
     Popup* popup = nfc->popup;
-    NfcDeviceData* data = &nfc->device.data;
+    NfcDeviceCommomData* data = &nfc->dev.dev_data.nfc_data;
 
-    if(strcmp(nfc->device.dev_name, "")) {
-        nfc_text_store_set(nfc, "%s", nfc->device.dev_name);
+    if(strcmp(nfc->dev.dev_name, "")) {
+        nfc_text_store_set(nfc, "%s", nfc->dev.dev_name);
     } else if(data->uid_len == 4) {
         nfc_text_store_set(
             nfc, "%02X %02X %02X %02X", data->uid[0], data->uid[1], data->uid[2], data->uid[3]);
@@ -31,10 +31,8 @@ const void nfc_scene_emulate_uid_on_enter(void* context) {
 
     // Setup and start worker
 
-    nfc_worker_set_emulation_params(nfc->nfc_common.worker, data);
-    nfc_worker_start(
-        nfc->nfc_common.worker, NfcWorkerStateEmulate, &nfc->nfc_common.worker_result, NULL, nfc);
-    view_dispatcher_switch_to_view(nfc->nfc_common.view_dispatcher, NfcViewPopup);
+    view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup);
+    nfc_worker_start(nfc->worker, NfcWorkerStateEmulate, &nfc->dev.dev_data, NULL, nfc);
 }
 
 const bool nfc_scene_emulate_uid_on_event(void* context, SceneManagerEvent event) {
@@ -51,7 +49,7 @@ const void nfc_scene_emulate_uid_on_exit(void* context) {
     Nfc* nfc = (Nfc*)context;
 
     // Stop worker
-    nfc_worker_stop(nfc->nfc_common.worker);
+    nfc_worker_stop(nfc->worker);
 
     // Clear view
     Popup* popup = nfc->popup;

+ 1 - 1
applications/nfc/scenes/nfc_scene_file_select.c

@@ -3,7 +3,7 @@
 const void nfc_scene_file_select_on_enter(void* context) {
     Nfc* nfc = (Nfc*)context;
     // Process file_select return
-    if(nfc_file_select(&nfc->device)) {
+    if(nfc_file_select(&nfc->dev)) {
         scene_manager_next_scene(nfc->scene_manager, NfcSceneSavedMenu);
     } else {
         scene_manager_search_previous_scene(nfc->scene_manager, NfcSceneStart);

+ 4 - 3
applications/nfc/scenes/nfc_scene_mifare_ul_menu.c

@@ -8,7 +8,7 @@ enum SubmenuIndex {
 void nfc_scene_mifare_ul_menu_submenu_callback(void* context, uint32_t index) {
     Nfc* nfc = (Nfc*)context;
 
-    view_dispatcher_send_custom_event(nfc->nfc_common.view_dispatcher, index);
+    view_dispatcher_send_custom_event(nfc->view_dispatcher, index);
 }
 
 const void nfc_scene_mifare_ul_menu_on_enter(void* context) {
@@ -22,7 +22,7 @@ const void nfc_scene_mifare_ul_menu_on_enter(void* context) {
     submenu_set_selected_item(
         nfc->submenu, scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMifareUlMenu));
 
-    view_dispatcher_switch_to_view(nfc->nfc_common.view_dispatcher, NfcViewMenu);
+    view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu);
 }
 
 const bool nfc_scene_mifare_ul_menu_on_event(void* context, SceneManagerEvent event) {
@@ -32,7 +32,8 @@ const bool nfc_scene_mifare_ul_menu_on_event(void* context, SceneManagerEvent ev
         if(event.event == SubmenuIndexSave) {
             scene_manager_set_scene_state(
                 nfc->scene_manager, NfcSceneMifareUlMenu, SubmenuIndexSave);
-            scene_manager_next_scene(nfc->scene_manager, NfcSceneNotImplemented);
+            nfc->dev.format = NfcDeviceSaveFormatMifareUl;
+            scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveName);
             return true;
         } else if(event.event == SubmenuIndexEmulate) {
             scene_manager_set_scene_state(

+ 2 - 2
applications/nfc/scenes/nfc_scene_not_implemented.c

@@ -3,7 +3,7 @@
 void nfc_scene_not_implemented_dialog_callback(DialogExResult result, void* context) {
     Nfc* nfc = (Nfc*)context;
 
-    view_dispatcher_send_custom_event(nfc->nfc_common.view_dispatcher, result);
+    view_dispatcher_send_custom_event(nfc->view_dispatcher, result);
 }
 
 const void nfc_scene_not_implemented_on_enter(void* context) {
@@ -16,7 +16,7 @@ const void nfc_scene_not_implemented_on_enter(void* context) {
     dialog_ex_set_context(dialog_ex, nfc);
     dialog_ex_set_result_callback(dialog_ex, nfc_scene_not_implemented_dialog_callback);
 
-    view_dispatcher_switch_to_view(nfc->nfc_common.view_dispatcher, NfcViewDialogEx);
+    view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewDialogEx);
 }
 
 const bool nfc_scene_not_implemented_on_event(void* context, SceneManagerEvent event) {

+ 7 - 10
applications/nfc/scenes/nfc_scene_read_card.c

@@ -1,8 +1,10 @@
 #include "../nfc_i.h"
 
+#define NFC_READ_CARD_CUSTOM_EVENT (0UL)
+
 void nfc_read_card_worker_callback(void* context) {
     Nfc* nfc = (Nfc*)context;
-    view_dispatcher_send_custom_event(nfc->nfc_common.view_dispatcher, NfcEventDetect);
+    view_dispatcher_send_custom_event(nfc->view_dispatcher, NFC_READ_CARD_CUSTOM_EVENT);
 }
 
 const void nfc_scene_read_card_on_enter(void* context) {
@@ -14,21 +16,16 @@ const void nfc_scene_read_card_on_enter(void* context) {
     popup_set_icon(popup, 0, 3, &I_RFIDDolphinReceive_97x61);
 
     // Start worker
+    view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup);
     nfc_worker_start(
-        nfc->nfc_common.worker,
-        NfcWorkerStateDetect,
-        &nfc->nfc_common.worker_result,
-        nfc_read_card_worker_callback,
-        nfc);
-    view_dispatcher_switch_to_view(nfc->nfc_common.view_dispatcher, NfcViewPopup);
+        nfc->worker, NfcWorkerStateDetect, &nfc->dev.dev_data, nfc_read_card_worker_callback, nfc);
 }
 
 const bool nfc_scene_read_card_on_event(void* context, SceneManagerEvent event) {
     Nfc* nfc = (Nfc*)context;
 
     if(event.type == SceneManagerEventTypeCustom) {
-        if(event.event == NfcEventDetect) {
-            nfc->device.data = nfc->nfc_common.worker_result.nfc_detect_data;
+        if(event.event == NFC_READ_CARD_CUSTOM_EVENT) {
             scene_manager_next_scene(nfc->scene_manager, NfcSceneReadCardSuccess);
             return true;
         }
@@ -43,7 +40,7 @@ const void nfc_scene_read_card_on_exit(void* context) {
     Nfc* nfc = (Nfc*)context;
 
     // Stop worker
-    nfc_worker_stop(nfc->nfc_common.worker);
+    nfc_worker_stop(nfc->worker);
 
     // Clear view
     Popup* popup = nfc->popup;

+ 5 - 5
applications/nfc/scenes/nfc_scene_read_card_success.c

@@ -5,20 +5,20 @@
 void nfc_scene_read_card_success_dialog_callback(DialogExResult result, void* context) {
     Nfc* nfc = (Nfc*)context;
 
-    view_dispatcher_send_custom_event(nfc->nfc_common.view_dispatcher, result);
+    view_dispatcher_send_custom_event(nfc->view_dispatcher, result);
 }
 
-const void nfc_scene_read_card_success_on_enter(void* context) {
+void nfc_scene_read_card_success_on_enter(void* context) {
     Nfc* nfc = (Nfc*)context;
 
     // Clear device name
-    nfc_device_set_name(&nfc->device, "");
+    nfc_device_set_name(&nfc->dev, "");
 
     // Send notification
     notification_message(nfc->notifications, &sequence_success);
 
     // Setup view
-    NfcDeviceData* data = (NfcDeviceData*)&nfc->nfc_common.worker_result;
+    NfcDeviceCommomData* data = (NfcDeviceCommomData*)&nfc->dev.dev_data.nfc_data;
     DialogEx* dialog_ex = nfc->dialog_ex;
     dialog_ex_set_left_button_text(dialog_ex, "Retry");
     dialog_ex_set_right_button_text(dialog_ex, "More");
@@ -60,7 +60,7 @@ const void nfc_scene_read_card_success_on_enter(void* context) {
     dialog_ex_set_context(dialog_ex, nfc);
     dialog_ex_set_result_callback(dialog_ex, nfc_scene_read_card_success_dialog_callback);
 
-    view_dispatcher_switch_to_view(nfc->nfc_common.view_dispatcher, NfcViewDialogEx);
+    view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewDialogEx);
 }
 
 const bool nfc_scene_read_card_success_on_event(void* context, SceneManagerEvent event) {

+ 54 - 0
applications/nfc/scenes/nfc_scene_read_emv_app.c

@@ -0,0 +1,54 @@
+#include "../nfc_i.h"
+
+#define NFC_READ_EMV_APP_CUSTOM_EVENT (0UL)
+
+void nfc_read_emv_app_worker_callback(void* context) {
+    Nfc* nfc = (Nfc*)context;
+    view_dispatcher_send_custom_event(nfc->view_dispatcher, NFC_READ_EMV_APP_CUSTOM_EVENT);
+}
+
+const void nfc_scene_read_emv_app_on_enter(void* context) {
+    Nfc* nfc = (Nfc*)context;
+
+    // Setup view
+    Popup* popup = nfc->popup;
+    popup_set_header(popup, "Reading\nbank card", 70, 34, AlignLeft, AlignTop);
+    popup_set_icon(popup, 0, 3, &I_RFIDDolphinReceive_97x61);
+
+    view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup);
+    // Start worker
+    nfc_worker_start(
+        nfc->worker,
+        NfcWorkerStateReadEMVApp,
+        &nfc->dev.dev_data,
+        nfc_read_emv_app_worker_callback,
+        nfc);
+}
+
+const bool nfc_scene_read_emv_app_on_event(void* context, SceneManagerEvent event) {
+    Nfc* nfc = (Nfc*)context;
+
+    if(event.type == SceneManagerEventTypeCustom) {
+        if(event.event == NFC_READ_EMV_APP_CUSTOM_EVENT) {
+            scene_manager_next_scene(nfc->scene_manager, NfcSceneReadEmvAppSuccess);
+            return true;
+        }
+    } else if(event.type == SceneManagerEventTypeTick) {
+        notification_message(nfc->notifications, &sequence_blink_blue_10);
+        return true;
+    }
+    return false;
+}
+
+const void nfc_scene_read_emv_app_on_exit(void* context) {
+    Nfc* nfc = (Nfc*)context;
+
+    // Stop worker
+    nfc_worker_stop(nfc->worker);
+
+    // Clear view
+    Popup* popup = nfc->popup;
+    popup_set_header(popup, NULL, 0, 0, AlignCenter, AlignBottom);
+    popup_set_text(popup, NULL, 0, 0, AlignCenter, AlignTop);
+    popup_set_icon(popup, 0, 0, NULL);
+}

+ 69 - 0
applications/nfc/scenes/nfc_scene_read_emv_app_success.c

@@ -0,0 +1,69 @@
+#include "../nfc_i.h"
+
+#define NFC_SCENE_READ_SUCCESS_SHIFT "              "
+
+void nfc_scene_read_emv_app_success_dialog_callback(DialogExResult result, void* context) {
+    Nfc* nfc = (Nfc*)context;
+
+    view_dispatcher_send_custom_event(nfc->view_dispatcher, result);
+}
+
+void nfc_scene_read_emv_app_success_on_enter(void* context) {
+    Nfc* nfc = (Nfc*)context;
+
+    // Setup view
+    NfcDeviceCommomData* nfc_data = &nfc->dev.dev_data.nfc_data;
+    NfcEmvData* emv_data = &nfc->dev.dev_data.emv_data;
+    DialogEx* dialog_ex = nfc->dialog_ex;
+    dialog_ex_set_left_button_text(dialog_ex, "Retry");
+    dialog_ex_set_right_button_text(dialog_ex, "Run app");
+    dialog_ex_set_header(dialog_ex, "Found EMV App", 36, 8, AlignLeft, AlignCenter);
+    dialog_ex_set_icon(dialog_ex, 8, 13, &I_Medium_chip_22x21);
+    // Display UID and AID
+    string_t aid;
+    string_init_printf(aid, "AID:");
+    for(uint8_t i = 0; i < emv_data->aid_len; i++) {
+        string_cat_printf(aid, " %02X", emv_data->aid[i]);
+    }
+    nfc_text_store_set(
+        nfc,
+        NFC_SCENE_READ_SUCCESS_SHIFT "UID: %02X %02X %02X %02X \n\n%s",
+        nfc_data->uid[0],
+        nfc_data->uid[1],
+        nfc_data->uid[2],
+        nfc_data->uid[3],
+        string_get_cstr(aid));
+    string_clear(aid);
+    dialog_ex_set_text(dialog_ex, nfc->text_store, 8, 16, AlignLeft, AlignTop);
+    dialog_ex_set_context(dialog_ex, nfc);
+    dialog_ex_set_result_callback(dialog_ex, nfc_scene_read_emv_app_success_dialog_callback);
+
+    view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewDialogEx);
+}
+
+const bool nfc_scene_read_emv_app_success_on_event(void* context, SceneManagerEvent event) {
+    Nfc* nfc = (Nfc*)context;
+
+    if(event.type == SceneManagerEventTypeCustom) {
+        if(event.event == DialogExResultLeft) {
+            return scene_manager_previous_scene(nfc->scene_manager);
+        } else if(event.event == DialogExResultRight) {
+            scene_manager_next_scene(nfc->scene_manager, NfcSceneRunEmvAppConfirm);
+            return true;
+        }
+    }
+    return false;
+}
+
+const void nfc_scene_read_emv_app_success_on_exit(void* context) {
+    Nfc* nfc = (Nfc*)context;
+
+    DialogEx* dialog_ex = nfc->dialog_ex;
+    dialog_ex_set_header(dialog_ex, NULL, 0, 0, AlignCenter, AlignCenter);
+    dialog_ex_set_text(dialog_ex, NULL, 0, 0, AlignCenter, AlignTop);
+    dialog_ex_set_icon(dialog_ex, 0, 0, NULL);
+    dialog_ex_set_left_button_text(dialog_ex, NULL);
+    dialog_ex_set_right_button_text(dialog_ex, NULL);
+    dialog_ex_set_result_callback(dialog_ex, NULL);
+    dialog_ex_set_context(dialog_ex, NULL);
+}

+ 57 - 0
applications/nfc/scenes/nfc_scene_read_emv_data.c

@@ -0,0 +1,57 @@
+#include "../nfc_i.h"
+
+#define NFC_READ_EMV_DATA_CUSTOM_EVENT (0UL)
+
+void nfc_read_emv_data_worker_callback(void* context) {
+    Nfc* nfc = (Nfc*)context;
+    view_dispatcher_send_custom_event(nfc->view_dispatcher, NFC_READ_EMV_DATA_CUSTOM_EVENT);
+}
+
+const void nfc_scene_read_emv_data_on_enter(void* context) {
+    Nfc* nfc = (Nfc*)context;
+
+    // Setup view
+    Popup* popup = nfc->popup;
+    popup_set_header(popup, "Reading\nbank card", 70, 34, AlignLeft, AlignTop);
+    popup_set_icon(popup, 0, 3, &I_RFIDDolphinReceive_97x61);
+
+    view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup);
+    // Start worker
+    nfc_worker_start(
+        nfc->worker,
+        NfcWorkerStateReadEMV,
+        &nfc->dev.dev_data,
+        nfc_read_emv_data_worker_callback,
+        nfc);
+}
+
+const bool nfc_scene_read_emv_data_on_event(void* context, SceneManagerEvent event) {
+    Nfc* nfc = (Nfc*)context;
+
+    if(event.type == SceneManagerEventTypeCustom) {
+        if(event.event == NFC_READ_EMV_DATA_CUSTOM_EVENT) {
+            scene_manager_next_scene(nfc->scene_manager, NfcSceneReadEmvDataSuccess);
+            return true;
+        }
+    } else if(event.type == SceneManagerEventTypeTick) {
+        notification_message(nfc->notifications, &sequence_blink_blue_10);
+        return true;
+    }
+    return false;
+}
+
+const void nfc_scene_read_emv_data_on_exit(void* context) {
+    Nfc* nfc = (Nfc*)context;
+
+    // Stop worker
+    nfc_worker_stop(nfc->worker);
+
+    // Send notification
+    notification_message(nfc->notifications, &sequence_success);
+
+    // Clear view
+    Popup* popup = nfc->popup;
+    popup_set_header(popup, NULL, 0, 0, AlignCenter, AlignBottom);
+    popup_set_text(popup, NULL, 0, 0, AlignCenter, AlignTop);
+    popup_set_icon(popup, 0, 0, NULL);
+}

+ 89 - 0
applications/nfc/scenes/nfc_scene_read_emv_data_success.c

@@ -0,0 +1,89 @@
+#include "../nfc_i.h"
+
+void nfc_scene_read_emv_data_success_widget_callback(GuiButtonType result, void* context) {
+    Nfc* nfc = (Nfc*)context;
+
+    view_dispatcher_send_custom_event(nfc->view_dispatcher, result);
+}
+
+void nfc_scene_read_emv_data_success_on_enter(void* context) {
+    Nfc* nfc = (Nfc*)context;
+    NfcEmvData* emv_data = &nfc->dev.dev_data.emv_data;
+    NfcDeviceCommomData* nfc_data = &nfc->dev.dev_data.nfc_data;
+
+    // Clear device name
+    nfc_device_set_name(&nfc->dev, "");
+
+    // Setup Custom Widget view
+    gui_widget_add_button_element(
+        nfc->widget,
+        GuiButtonTypeLeft,
+        "Back",
+        nfc_scene_read_emv_data_success_widget_callback,
+        nfc);
+    gui_widget_add_button_element(
+        nfc->widget,
+        GuiButtonTypeRight,
+        "Save",
+        nfc_scene_read_emv_data_success_widget_callback,
+        nfc);
+    gui_widget_add_string_element(
+        nfc->widget, 64, 3, AlignCenter, AlignTop, FontSecondary, nfc->dev.dev_data.emv_data.name);
+    char pan_str[32];
+    snprintf(
+        pan_str,
+        sizeof(pan_str),
+        "%02X%02X %02X%02X %02X%02X %02X%02X",
+        emv_data->number[0],
+        emv_data->number[1],
+        emv_data->number[2],
+        emv_data->number[3],
+        emv_data->number[4],
+        emv_data->number[5],
+        emv_data->number[6],
+        emv_data->number[7]);
+    gui_widget_add_string_element(
+        nfc->widget, 64, 13, AlignCenter, AlignTop, FontSecondary, pan_str);
+    char atqa_str[16];
+    snprintf(atqa_str, sizeof(atqa_str), "ATQA: %02X%02X", nfc_data->atqa[0], nfc_data->atqa[1]);
+    gui_widget_add_string_element(
+        nfc->widget, 121, 32, AlignRight, AlignTop, FontSecondary, atqa_str);
+    char uid_str[32];
+    snprintf(
+        uid_str,
+        sizeof(uid_str),
+        "UID: %02X %02X %02X %02X",
+        nfc_data->uid[0],
+        nfc_data->uid[1],
+        nfc_data->uid[2],
+        nfc_data->uid[3]);
+    gui_widget_add_string_element(nfc->widget, 7, 42, AlignLeft, AlignTop, FontSecondary, uid_str);
+    char sak_str[16];
+    snprintf(sak_str, sizeof(sak_str), "SAK: %02X", nfc_data->sak);
+    gui_widget_add_string_element(
+        nfc->widget, 121, 42, AlignRight, AlignTop, FontSecondary, sak_str);
+
+    view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget);
+}
+
+const bool nfc_scene_read_emv_data_success_on_event(void* context, SceneManagerEvent event) {
+    Nfc* nfc = (Nfc*)context;
+
+    if(event.type == SceneManagerEventTypeCustom) {
+        if(event.event == GuiButtonTypeLeft) {
+            return scene_manager_search_previous_scene(
+                nfc->scene_manager, NfcSceneReadEmvAppSuccess);
+        } else if(event.event == GuiButtonTypeRight) {
+            nfc->dev.format = NfcDeviceSaveFormatBankCard;
+            scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveName);
+            return true;
+        }
+    }
+    return false;
+}
+
+const void nfc_scene_read_emv_data_success_on_exit(void* context) {
+    Nfc* nfc = (Nfc*)context;
+
+    gui_widget_clear(nfc->widget);
+}

+ 9 - 8
applications/nfc/scenes/nfc_scene_read_mifare_ul.c

@@ -1,8 +1,10 @@
 #include "../nfc_i.h"
 
+#define NFC_READ_MIFARE_UL_CUSTOM_EVENT (0UL)
+
 void nfc_read_mifare_ul_worker_callback(void* context) {
     Nfc* nfc = (Nfc*)context;
-    view_dispatcher_send_custom_event(nfc->nfc_common.view_dispatcher, NfcEventMifareUl);
+    view_dispatcher_send_custom_event(nfc->view_dispatcher, NFC_READ_MIFARE_UL_CUSTOM_EVENT);
 }
 
 const void nfc_scene_read_mifare_ul_on_enter(void* context) {
@@ -13,22 +15,21 @@ const void nfc_scene_read_mifare_ul_on_enter(void* context) {
     popup_set_header(popup, "Detecting\nultralight", 70, 34, AlignLeft, AlignTop);
     popup_set_icon(popup, 0, 3, &I_RFIDDolphinReceive_97x61);
 
+    view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup);
     // Start worker
     nfc_worker_start(
-        nfc->nfc_common.worker,
-        NfcWorkerStateReadMfUltralight,
-        &nfc->nfc_common.worker_result,
+        nfc->worker,
+        NfcWorkerStateReadMifareUl,
+        &nfc->dev.dev_data,
         nfc_read_mifare_ul_worker_callback,
         nfc);
-    view_dispatcher_switch_to_view(nfc->nfc_common.view_dispatcher, NfcViewPopup);
 }
 
 const bool nfc_scene_read_mifare_ul_on_event(void* context, SceneManagerEvent event) {
     Nfc* nfc = (Nfc*)context;
 
     if(event.type == SceneManagerEventTypeCustom) {
-        if(event.event == NfcEventMifareUl) {
-            nfc->device.data = nfc->nfc_common.worker_result.nfc_detect_data;
+        if(event.event == NFC_READ_MIFARE_UL_CUSTOM_EVENT) {
             scene_manager_next_scene(nfc->scene_manager, NfcSceneReadMifareUlSuccess);
             return true;
         }
@@ -43,7 +44,7 @@ const void nfc_scene_read_mifare_ul_on_exit(void* context) {
     Nfc* nfc = (Nfc*)context;
 
     // Stop worker
-    nfc_worker_stop(nfc->nfc_common.worker);
+    nfc_worker_stop(nfc->worker);
 
     // Clear view
     Popup* popup = nfc->popup;

+ 10 - 16
applications/nfc/scenes/nfc_scene_read_mifare_ul_success.c

@@ -11,28 +11,26 @@ enum {
 void nfc_scene_read_mifare_ul_success_dialog_callback(DialogExResult result, void* context) {
     Nfc* nfc = (Nfc*)context;
 
-    view_dispatcher_send_custom_event(nfc->nfc_common.view_dispatcher, result);
+    view_dispatcher_send_custom_event(nfc->view_dispatcher, result);
 }
 
 void nfc_scene_read_mifare_ul_success_text_box_callback(void* context) {
     Nfc* nfc = (Nfc*)context;
 
-    view_dispatcher_send_custom_event(
-        nfc->nfc_common.view_dispatcher, NFC_SCENE_READ_MF_UL_CUSTOM_EVENT);
+    view_dispatcher_send_custom_event(nfc->view_dispatcher, NFC_SCENE_READ_MF_UL_CUSTOM_EVENT);
 }
 
 const void nfc_scene_read_mifare_ul_success_on_enter(void* context) {
     Nfc* nfc = (Nfc*)context;
 
     // Clear device name
-    nfc_device_set_name(&nfc->device, "");
+    nfc_device_set_name(&nfc->dev, "");
 
     // Send notification
     notification_message(nfc->notifications, &sequence_success);
 
     // Setup dialog view
-    NfcDeviceData* data =
-        (NfcDeviceData*)&nfc->nfc_common.worker_result.nfc_mifare_ul_data.nfc_data;
+    NfcDeviceCommomData* data = (NfcDeviceCommomData*)&nfc->dev.dev_data.nfc_data;
     DialogEx* dialog_ex = nfc->dialog_ex;
     dialog_ex_set_left_button_text(dialog_ex, "Retry");
     dialog_ex_set_right_button_text(dialog_ex, "More");
@@ -59,27 +57,23 @@ const void nfc_scene_read_mifare_ul_success_on_enter(void* context) {
     dialog_ex_set_result_callback(dialog_ex, nfc_scene_read_mifare_ul_success_dialog_callback);
 
     // Setup TextBox view
-    NfcMifareUlData* mf_ul_data =
-        (NfcMifareUlData*)&nfc->nfc_common.worker_result.nfc_mifare_ul_data;
+    MifareUlData* mf_ul_data = (MifareUlData*)&nfc->dev.dev_data.mf_ul_data;
     TextBox* text_box = nfc->text_box;
     text_box_set_context(text_box, nfc);
     text_box_set_exit_callback(text_box, nfc_scene_read_mifare_ul_success_text_box_callback);
     text_box_set_font(text_box, TextBoxFontHex);
-    for(uint16_t i = 0; i < mf_ul_data->dump_size; i += 2) {
+    for(uint16_t i = 0; i < mf_ul_data->data_size; i += 2) {
         if(!(i % 8) && i) {
             string_push_back(nfc->text_box_store, '\n');
         }
         string_cat_printf(
-            nfc->text_box_store,
-            "%02X%02X ",
-            mf_ul_data->full_dump[i],
-            mf_ul_data->full_dump[i + 1]);
+            nfc->text_box_store, "%02X%02X ", mf_ul_data->data[i], mf_ul_data->data[i + 1]);
     }
     text_box_set_text(text_box, string_get_cstr(nfc->text_box_store));
 
     scene_manager_set_scene_state(
         nfc->scene_manager, NfcSceneReadMifareUlSuccess, ReadMifareUlStateShowUID);
-    view_dispatcher_switch_to_view(nfc->nfc_common.view_dispatcher, NfcViewDialogEx);
+    view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewDialogEx);
 }
 
 const bool nfc_scene_read_mifare_ul_success_on_event(void* context, SceneManagerEvent event) {
@@ -101,7 +95,7 @@ const bool nfc_scene_read_mifare_ul_success_on_event(void* context, SceneManager
             (scene_manager_get_scene_state(nfc->scene_manager, NfcSceneReadMifareUlSuccess) ==
              ReadMifareUlStateShowUID) &&
             (event.event == DialogExResultCenter)) {
-            view_dispatcher_switch_to_view(nfc->nfc_common.view_dispatcher, NfcViewTextBox);
+            view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewTextBox);
             scene_manager_set_scene_state(
                 nfc->scene_manager, NfcSceneReadMifareUlSuccess, ReadMifareUlStateShowData);
             return true;
@@ -109,7 +103,7 @@ const bool nfc_scene_read_mifare_ul_success_on_event(void* context, SceneManager
             (scene_manager_get_scene_state(nfc->scene_manager, NfcSceneReadMifareUlSuccess) ==
              ReadMifareUlStateShowData) &&
             (event.event == NFC_SCENE_READ_MF_UL_CUSTOM_EVENT)) {
-            view_dispatcher_switch_to_view(nfc->nfc_common.view_dispatcher, NfcViewDialogEx);
+            view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewDialogEx);
             scene_manager_set_scene_state(
                 nfc->scene_manager, NfcSceneReadMifareUlSuccess, ReadMifareUlStateShowUID);
             return true;

+ 56 - 0
applications/nfc/scenes/nfc_scene_run_emv_app_confirm.c

@@ -0,0 +1,56 @@
+#include "../nfc_i.h"
+
+#define NFC_SCENE_READ_SUCCESS_SHIFT "              "
+
+void nfc_scene_run_emv_app_confirm_dialog_callback(DialogExResult result, void* context) {
+    Nfc* nfc = (Nfc*)context;
+
+    view_dispatcher_send_custom_event(nfc->view_dispatcher, result);
+}
+
+void nfc_scene_run_emv_app_confirm_on_enter(void* context) {
+    Nfc* nfc = (Nfc*)context;
+
+    DialogEx* dialog_ex = nfc->dialog_ex;
+    dialog_ex_set_left_button_text(dialog_ex, "Back");
+    dialog_ex_set_right_button_text(dialog_ex, "Run");
+    dialog_ex_set_header(dialog_ex, "Run EMV app?", 64, 8, AlignCenter, AlignCenter);
+    dialog_ex_set_text(
+        dialog_ex,
+        "It will try to run card's app\nand detect unencrypred\ndata",
+        64,
+        18,
+        AlignCenter,
+        AlignTop);
+    dialog_ex_set_context(dialog_ex, nfc);
+    dialog_ex_set_result_callback(dialog_ex, nfc_scene_run_emv_app_confirm_dialog_callback);
+
+    view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewDialogEx);
+}
+
+const bool nfc_scene_run_emv_app_confirm_on_event(void* context, SceneManagerEvent event) {
+    Nfc* nfc = (Nfc*)context;
+
+    if(event.type == SceneManagerEventTypeCustom) {
+        if(event.event == DialogExResultLeft) {
+            return scene_manager_previous_scene(nfc->scene_manager);
+        } else if(event.event == DialogExResultRight) {
+            scene_manager_next_scene(nfc->scene_manager, NfcSceneReadEmvData);
+            return true;
+        }
+    }
+    return false;
+}
+
+const void nfc_scene_run_emv_app_confirm_on_exit(void* context) {
+    Nfc* nfc = (Nfc*)context;
+
+    DialogEx* dialog_ex = nfc->dialog_ex;
+    dialog_ex_set_header(dialog_ex, NULL, 0, 0, AlignCenter, AlignCenter);
+    dialog_ex_set_text(dialog_ex, NULL, 0, 0, AlignCenter, AlignTop);
+    dialog_ex_set_icon(dialog_ex, 0, 0, NULL);
+    dialog_ex_set_left_button_text(dialog_ex, NULL);
+    dialog_ex_set_right_button_text(dialog_ex, NULL);
+    dialog_ex_set_result_callback(dialog_ex, NULL);
+    dialog_ex_set_context(dialog_ex, NULL);
+}

+ 8 - 6
applications/nfc/scenes/nfc_scene_save_name.c

@@ -5,8 +5,7 @@
 void nfc_scene_save_name_text_input_callback(void* context) {
     Nfc* nfc = (Nfc*)context;
 
-    view_dispatcher_send_custom_event(
-        nfc->nfc_common.view_dispatcher, SCENE_SAVE_NAME_CUSTOM_EVENT);
+    view_dispatcher_send_custom_event(nfc->view_dispatcher, SCENE_SAVE_NAME_CUSTOM_EVENT);
 }
 
 const void nfc_scene_save_name_on_enter(void* context) {
@@ -14,7 +13,10 @@ const void nfc_scene_save_name_on_enter(void* context) {
 
     // Setup view
     TextInput* text_input = nfc->text_input;
-    nfc_text_store_clear(nfc);
+    if(nfc->dev.dev_name) {
+        nfc_device_delete(&nfc->dev);
+    }
+    nfc_text_store_set(nfc, nfc->dev.dev_name);
     text_input_set_header_text(text_input, "Name the card");
     text_input_set_result_callback(
         text_input,
@@ -22,7 +24,7 @@ const void nfc_scene_save_name_on_enter(void* context) {
         nfc,
         nfc->text_store,
         sizeof(nfc->text_store));
-    view_dispatcher_switch_to_view(nfc->nfc_common.view_dispatcher, NfcViewTextInput);
+    view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewTextInput);
 }
 
 const bool nfc_scene_save_name_on_event(void* context, SceneManagerEvent event) {
@@ -30,8 +32,8 @@ const bool nfc_scene_save_name_on_event(void* context, SceneManagerEvent event)
 
     if(event.type == SceneManagerEventTypeCustom) {
         if(event.event == SCENE_SAVE_NAME_CUSTOM_EVENT) {
-            memcpy(&nfc->device.dev_name, nfc->text_store, strlen(nfc->text_store));
-            if(nfc_device_save(&nfc->device, nfc->text_store)) {
+            memcpy(&nfc->dev.dev_name, nfc->text_store, strlen(nfc->text_store));
+            if(nfc_device_save(&nfc->dev, nfc->text_store)) {
                 scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveSuccess);
                 return true;
             } else {

+ 2 - 3
applications/nfc/scenes/nfc_scene_save_success.c

@@ -4,8 +4,7 @@
 
 void nfc_scene_save_success_popup_callback(void* context) {
     Nfc* nfc = (Nfc*)context;
-    view_dispatcher_send_custom_event(
-        nfc->nfc_common.view_dispatcher, SCENE_SAVE_SUCCESS_CUSTOM_EVENT);
+    view_dispatcher_send_custom_event(nfc->view_dispatcher, SCENE_SAVE_SUCCESS_CUSTOM_EVENT);
 }
 
 const void nfc_scene_save_success_on_enter(void* context) {
@@ -19,7 +18,7 @@ const void nfc_scene_save_success_on_enter(void* context) {
     popup_set_context(popup, nfc);
     popup_set_callback(popup, nfc_scene_save_success_popup_callback);
     popup_enable_timeout(popup);
-    view_dispatcher_switch_to_view(nfc->nfc_common.view_dispatcher, NfcViewPopup);
+    view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup);
 }
 
 const bool nfc_scene_save_success_on_event(void* context, SceneManagerEvent event) {

+ 9 - 7
applications/nfc/scenes/nfc_scene_saved_menu.c

@@ -10,15 +10,17 @@ enum SubmenuIndex {
 void nfc_scene_saved_menu_submenu_callback(void* context, uint32_t index) {
     Nfc* nfc = (Nfc*)context;
 
-    view_dispatcher_send_custom_event(nfc->nfc_common.view_dispatcher, index);
+    view_dispatcher_send_custom_event(nfc->view_dispatcher, index);
 }
 
 const void nfc_scene_saved_menu_on_enter(void* context) {
     Nfc* nfc = (Nfc*)context;
     Submenu* submenu = nfc->submenu;
 
-    submenu_add_item(
-        submenu, "Emulate", SubmenuIndexEmulate, nfc_scene_saved_menu_submenu_callback, nfc);
+    if(nfc->dev.format != NfcDeviceSaveFormatBankCard) {
+        submenu_add_item(
+            submenu, "Emulate", SubmenuIndexEmulate, nfc_scene_saved_menu_submenu_callback, nfc);
+    }
     submenu_add_item(
         submenu, "Edit UID and name", SubmenuIndexEdit, nfc_scene_saved_menu_submenu_callback, nfc);
     submenu_add_item(
@@ -28,7 +30,7 @@ const void nfc_scene_saved_menu_on_enter(void* context) {
     submenu_set_selected_item(
         nfc->submenu, scene_manager_get_scene_state(nfc->scene_manager, NfcSceneSavedMenu));
 
-    view_dispatcher_switch_to_view(nfc->nfc_common.view_dispatcher, NfcViewMenu);
+    view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu);
 }
 
 const bool nfc_scene_saved_menu_on_event(void* context, SceneManagerEvent event) {
@@ -42,16 +44,16 @@ const bool nfc_scene_saved_menu_on_event(void* context, SceneManagerEvent event)
             return true;
         } else if(event.event == SubmenuIndexEdit) {
             scene_manager_set_scene_state(nfc->scene_manager, NfcSceneSavedMenu, SubmenuIndexEdit);
-            scene_manager_next_scene(nfc->scene_manager, NfcSceneNotImplemented);
+            scene_manager_next_scene(nfc->scene_manager, NfcSceneSetUid);
             return true;
         } else if(event.event == SubmenuIndexDelete) {
             scene_manager_set_scene_state(
                 nfc->scene_manager, NfcSceneSavedMenu, SubmenuIndexDelete);
-            scene_manager_next_scene(nfc->scene_manager, NfcSceneNotImplemented);
+            scene_manager_next_scene(nfc->scene_manager, NfcSceneDelete);
             return true;
         } else if(event.event == SubmenuIndexInfo) {
             scene_manager_set_scene_state(nfc->scene_manager, NfcSceneSavedMenu, SubmenuIndexInfo);
-            scene_manager_next_scene(nfc->scene_manager, NfcSceneNotImplemented);
+            scene_manager_next_scene(nfc->scene_manager, NfcSceneDeviceInfo);
             return true;
         }
     }

+ 3 - 3
applications/nfc/scenes/nfc_scene_scripts_menu.c

@@ -8,7 +8,7 @@ enum SubmenuIndex {
 void nfc_scene_scripts_menu_submenu_callback(void* context, uint32_t index) {
     Nfc* nfc = (Nfc*)context;
 
-    view_dispatcher_send_custom_event(nfc->nfc_common.view_dispatcher, index);
+    view_dispatcher_send_custom_event(nfc->view_dispatcher, index);
 }
 
 const void nfc_scene_scripts_menu_on_enter(void* context) {
@@ -29,7 +29,7 @@ const void nfc_scene_scripts_menu_on_enter(void* context) {
         nfc);
     submenu_set_selected_item(
         nfc->submenu, scene_manager_get_scene_state(nfc->scene_manager, NfcSceneScriptsMenu));
-    view_dispatcher_switch_to_view(nfc->nfc_common.view_dispatcher, NfcViewMenu);
+    view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu);
 }
 
 const bool nfc_scene_scripts_menu_on_event(void* context, SceneManagerEvent event) {
@@ -39,7 +39,7 @@ const bool nfc_scene_scripts_menu_on_event(void* context, SceneManagerEvent even
         if(event.event == SubmenuIndexBankCard) {
             scene_manager_set_scene_state(
                 nfc->scene_manager, NfcSceneScriptsMenu, SubmenuIndexBankCard);
-            scene_manager_next_scene(nfc->scene_manager, NfcSceneNotImplemented);
+            scene_manager_next_scene(nfc->scene_manager, NfcSceneReadEmvApp);
             return true;
         } else if(event.event == SubmenuIndexMifareUltralight) {
             scene_manager_set_scene_state(

+ 8 - 4
applications/nfc/scenes/nfc_scene_set_atqa.c

@@ -5,8 +5,7 @@
 void nfc_scene_set_atqa_byte_input_callback(void* context) {
     Nfc* nfc = (Nfc*)context;
 
-    view_dispatcher_send_custom_event(
-        nfc->nfc_common.view_dispatcher, SCENE_SET_ATQA_CUSTOM_EVENT);
+    view_dispatcher_send_custom_event(nfc->view_dispatcher, SCENE_SET_ATQA_CUSTOM_EVENT);
 }
 
 const void nfc_scene_set_atqa_on_enter(void* context) {
@@ -16,8 +15,13 @@ const void nfc_scene_set_atqa_on_enter(void* context) {
     ByteInput* byte_input = nfc->byte_input;
     byte_input_set_header_text(byte_input, "Enter atqa in hex");
     byte_input_set_result_callback(
-        byte_input, nfc_scene_set_atqa_byte_input_callback, NULL, nfc, nfc->device.data.atqa, 2);
-    view_dispatcher_switch_to_view(nfc->nfc_common.view_dispatcher, NfcViewByteInput);
+        byte_input,
+        nfc_scene_set_atqa_byte_input_callback,
+        NULL,
+        nfc,
+        nfc->dev.dev_data.nfc_data.atqa,
+        2);
+    view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewByteInput);
 }
 
 const bool nfc_scene_set_atqa_on_event(void* context, SceneManagerEvent event) {

+ 8 - 3
applications/nfc/scenes/nfc_scene_set_sak.c

@@ -5,7 +5,7 @@
 void nfc_scene_set_sak_byte_input_callback(void* context) {
     Nfc* nfc = (Nfc*)context;
 
-    view_dispatcher_send_custom_event(nfc->nfc_common.view_dispatcher, SCENE_SET_SAK_CUSTOM_EVENT);
+    view_dispatcher_send_custom_event(nfc->view_dispatcher, SCENE_SET_SAK_CUSTOM_EVENT);
 }
 
 const void nfc_scene_set_sak_on_enter(void* context) {
@@ -15,8 +15,13 @@ const void nfc_scene_set_sak_on_enter(void* context) {
     ByteInput* byte_input = nfc->byte_input;
     byte_input_set_header_text(byte_input, "Enter SAK in hex");
     byte_input_set_result_callback(
-        byte_input, nfc_scene_set_sak_byte_input_callback, NULL, nfc, &nfc->device.data.sak, 1);
-    view_dispatcher_switch_to_view(nfc->nfc_common.view_dispatcher, NfcViewByteInput);
+        byte_input,
+        nfc_scene_set_sak_byte_input_callback,
+        NULL,
+        nfc,
+        &nfc->dev.dev_data.nfc_data.sak,
+        1);
+    view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewByteInput);
 }
 
 const bool nfc_scene_set_sak_on_event(void* context, SceneManagerEvent event) {

+ 6 - 4
applications/nfc/scenes/nfc_scene_set_type.c

@@ -8,7 +8,7 @@ enum SubmenuIndex {
 void nfc_scene_set_type_submenu_callback(void* context, uint32_t index) {
     Nfc* nfc = (Nfc*)context;
 
-    view_dispatcher_send_custom_event(nfc->nfc_common.view_dispatcher, index);
+    view_dispatcher_send_custom_event(nfc->view_dispatcher, index);
 }
 
 const void nfc_scene_set_type_on_enter(void* context) {
@@ -19,7 +19,7 @@ const void nfc_scene_set_type_on_enter(void* context) {
         submenu, "NFC-A 7-bytes UID", SubmenuIndexNFCA7, nfc_scene_set_type_submenu_callback, nfc);
     submenu_add_item(
         submenu, "NFC-A 4-bytes UID", SubmenuIndexNFCA4, nfc_scene_set_type_submenu_callback, nfc);
-    view_dispatcher_switch_to_view(nfc->nfc_common.view_dispatcher, NfcViewMenu);
+    view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu);
 }
 
 const bool nfc_scene_set_type_on_event(void* context, SceneManagerEvent event) {
@@ -27,11 +27,13 @@ const bool nfc_scene_set_type_on_event(void* context, SceneManagerEvent event) {
 
     if(event.type == SceneManagerEventTypeCustom) {
         if(event.event == SubmenuIndexNFCA7) {
-            nfc->device.data.uid_len = 7;
+            nfc->dev.dev_data.nfc_data.uid_len = 7;
+            nfc->dev.format = NfcDeviceSaveFormatUid;
             scene_manager_next_scene(nfc->scene_manager, NfcSceneSetSak);
             return true;
         } else if(event.event == SubmenuIndexNFCA4) {
-            nfc->device.data.uid_len = 4;
+            nfc->dev.dev_data.nfc_data.uid_len = 4;
+            nfc->dev.format = NfcDeviceSaveFormatUid;
             scene_manager_next_scene(nfc->scene_manager, NfcSceneSetSak);
             return true;
         }

+ 4 - 4
applications/nfc/scenes/nfc_scene_set_uid.c

@@ -5,7 +5,7 @@
 void nfc_scene_set_uid_byte_input_callback(void* context) {
     Nfc* nfc = (Nfc*)context;
 
-    view_dispatcher_send_custom_event(nfc->nfc_common.view_dispatcher, SCENE_SET_UID_CUSTOM_EVENT);
+    view_dispatcher_send_custom_event(nfc->view_dispatcher, SCENE_SET_UID_CUSTOM_EVENT);
 }
 
 const void nfc_scene_set_uid_on_enter(void* context) {
@@ -19,9 +19,9 @@ const void nfc_scene_set_uid_on_enter(void* context) {
         nfc_scene_set_uid_byte_input_callback,
         NULL,
         nfc,
-        nfc->device.data.uid,
-        nfc->device.data.uid_len);
-    view_dispatcher_switch_to_view(nfc->nfc_common.view_dispatcher, NfcViewByteInput);
+        nfc->dev.dev_data.nfc_data.uid,
+        nfc->dev.dev_data.nfc_data.uid_len);
+    view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewByteInput);
 }
 
 const bool nfc_scene_set_uid_on_event(void* context, SceneManagerEvent event) {

+ 3 - 8
applications/nfc/scenes/nfc_scene_start.c

@@ -5,13 +5,12 @@ enum SubmenuIndex {
     SubmenuIndexRunScript,
     SubmenuIndexSaved,
     SubmenuIndexAddManualy,
-    SubmenuIndexDebug,
 };
 
 void nfc_scene_start_submenu_callback(void* context, uint32_t index) {
     Nfc* nfc = (Nfc*)context;
 
-    view_dispatcher_send_custom_event(nfc->nfc_common.view_dispatcher, index);
+    view_dispatcher_send_custom_event(nfc->view_dispatcher, index);
 }
 
 const void nfc_scene_start_on_enter(void* context) {
@@ -30,11 +29,11 @@ const void nfc_scene_start_on_enter(void* context) {
         submenu, "Saved cards", SubmenuIndexSaved, nfc_scene_start_submenu_callback, nfc);
     submenu_add_item(
         submenu, "Add manually", SubmenuIndexAddManualy, nfc_scene_start_submenu_callback, nfc);
-    submenu_add_item(submenu, "Debug", SubmenuIndexDebug, nfc_scene_start_submenu_callback, nfc);
     submenu_set_selected_item(
         submenu, scene_manager_get_scene_state(nfc->scene_manager, NfcSceneStart));
 
-    view_dispatcher_switch_to_view(nfc->nfc_common.view_dispatcher, NfcViewMenu);
+    nfc_device_clear(&nfc->dev);
+    view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu);
 }
 
 const bool nfc_scene_start_on_event(void* context, SceneManagerEvent event) {
@@ -59,10 +58,6 @@ const bool nfc_scene_start_on_event(void* context, SceneManagerEvent event) {
                 nfc->scene_manager, NfcSceneStart, SubmenuIndexAddManualy);
             scene_manager_next_scene(nfc->scene_manager, NfcSceneSetType);
             return true;
-        } else if(event.event == SubmenuIndexDebug) {
-            scene_manager_set_scene_state(nfc->scene_manager, NfcSceneStart, SubmenuIndexDebug);
-            scene_manager_next_scene(nfc->scene_manager, NfcSceneDebugMenu);
-            return true;
         }
     }
     return false;

+ 65 - 0
applications/nfc/views/bank_card.c

@@ -0,0 +1,65 @@
+#include "bank_card.h"
+#include "../gui_widget/gui_widget.h"
+#include <m-string.h>
+
+struct BankCard {
+    GuiWidget* widget;
+};
+
+BankCard* bank_card_alloc() {
+    BankCard* bank_card = furi_alloc(sizeof(BankCard));
+    bank_card->widget = gui_widget_alloc();
+    return bank_card;
+}
+
+void bank_card_free(BankCard* bank_card) {
+    furi_assert(bank_card);
+    gui_widget_free(bank_card->widget);
+    free(bank_card);
+}
+
+View* bank_card_get_view(BankCard* bank_card) {
+    furi_assert(bank_card);
+    return gui_widget_get_view(bank_card->widget);
+}
+
+void bank_card_clear(BankCard* bank_card) {
+    furi_assert(bank_card);
+    gui_widget_clear(bank_card->widget);
+}
+
+void bank_card_set_name(BankCard* bank_card, char* name) {
+    furi_assert(bank_card);
+    furi_assert(name);
+    gui_widget_add_string_element(
+        bank_card->widget, 64, 6, AlignCenter, AlignTop, FontSecondary, name);
+}
+
+void bank_card_set_number(BankCard* bank_card, uint8_t* number) {
+    furi_assert(bank_card);
+    furi_assert(number);
+    string_t num_str;
+    string_init(num_str);
+    for(uint8_t i = 0; i < 8; i += 2) {
+        string_cat_printf(num_str, "%02X%02X ", number[i], number[i + 1]);
+    }
+    gui_widget_add_string_element(
+        bank_card->widget, 25, 22, AlignLeft, AlignTop, FontSecondary, string_get_cstr(num_str));
+    gui_widget_add_icon_element(bank_card->widget, 6, 20, &I_EMV_Chip_14x11);
+    string_clear(num_str);
+}
+
+void bank_card_set_exp_date(BankCard* bank_card, uint8_t mon, uint16_t year) {
+    furi_assert(bank_card);
+    char exp_date_str[16];
+    snprintf(exp_date_str, sizeof(exp_date_str), "Exp: %02d/%02d", mon, year % 100);
+    gui_widget_add_string_element(
+        bank_card->widget, 122, 54, AlignRight, AlignBottom, FontSecondary, exp_date_str);
+}
+
+void bank_card_set_cardholder_name(BankCard* bank_card, char* name) {
+    furi_assert(bank_card);
+    furi_assert(name);
+    gui_widget_add_string_element(
+        bank_card->widget, 6, 37, AlignLeft, AlignTop, FontSecondary, name);
+}

+ 23 - 0
applications/nfc/views/bank_card.h

@@ -0,0 +1,23 @@
+#pragma once
+#include <stdint.h>
+#include <gui/view.h>
+
+typedef struct BankCard BankCard;
+
+typedef void (*BankCardBackCallback)(void);
+
+BankCard* bank_card_alloc();
+
+void bank_card_free(BankCard* bank_card);
+
+void bank_card_clear(BankCard* bank_card);
+
+View* bank_card_get_view(BankCard* bank_card);
+
+void bank_card_set_name(BankCard* bank_card, char* name);
+
+void bank_card_set_number(BankCard* bank_card, uint8_t* number);
+
+void bank_card_set_exp_date(BankCard* bank_card, uint8_t mon, uint16_t year);
+
+void bank_card_set_cardholder_name(BankCard* bank_card, char* name);

+ 0 - 142
applications/nfc/views/nfc_detect.c

@@ -1,142 +0,0 @@
-#include "nfc_detect.h"
-
-#include <furi.h>
-#include <api-hal.h>
-#include <input/input.h>
-
-#include "../nfc_i.h"
-
-struct NfcDetect {
-    NfcCommon* nfc_common;
-    View* view;
-};
-
-typedef struct {
-    bool found;
-    NfcDeviceData data;
-} NfcDetectModel;
-
-void nfc_detect_draw(Canvas* canvas, NfcDetectModel* model) {
-    char buffer[32];
-    canvas_clear(canvas);
-    canvas_set_font(canvas, FontPrimary);
-    if(model->found) {
-        canvas_draw_str(canvas, 0, 12, "Found");
-        canvas_draw_str(canvas, 32, 12, nfc_get_dev_type(model->data.device));
-        canvas_set_font(canvas, FontSecondary);
-        if(model->data.protocol != NfcDeviceProtocolUnknown) {
-            canvas_draw_str(canvas, 0, 22, nfc_get_protocol(model->data.protocol));
-        }
-        // Display UID
-        for(uint8_t i = 0; i < model->data.uid_len; i++) {
-            snprintf(buffer + (i * 2), sizeof(buffer) - (i * 2), "%02X", model->data.uid[i]);
-            buffer[model->data.uid_len * 2] = 0;
-        }
-        canvas_draw_str(canvas, 0, 32, "UID: ");
-        canvas_draw_str(canvas, 22, 32, buffer);
-        // Display ATQA and SAK
-        snprintf(
-            buffer,
-            sizeof(buffer),
-            "ATQA: %02X %02X   SAK: %02X",
-            model->data.atqa[1],
-            model->data.atqa[0],
-            model->data.sak);
-        canvas_draw_str(canvas, 0, 42, buffer);
-    } else {
-        canvas_draw_str(canvas, 0, 12, "Searching");
-        canvas_set_font(canvas, FontSecondary);
-        canvas_draw_str(canvas, 2, 22, "Place card to the back");
-    }
-}
-
-bool nfc_detect_input(InputEvent* event, void* context) {
-    if(event->key == InputKeyBack) {
-        return false;
-    }
-    return true;
-}
-
-void nfc_detect_worker_callback(void* context) {
-    furi_assert(context);
-
-    NfcDetect* nfc_detect = (NfcDetect*)context;
-    view_dispatcher_send_custom_event(nfc_detect->nfc_common->view_dispatcher, NfcEventDetect);
-}
-
-bool nfc_detect_view_custom(uint32_t event, void* context) {
-    furi_assert(context);
-
-    NfcDetect* nfc_detect = (NfcDetect*)context;
-    if(event == NfcEventDetect) {
-        NfcDeviceData* data = (NfcDeviceData*)&nfc_detect->nfc_common->worker_result;
-
-        with_view_model(
-            nfc_detect->view, (NfcDetectModel * model) {
-                model->found = true;
-                model->data = *data;
-                return true;
-            });
-        // TODO add and configure next view model
-        return false;
-    }
-
-    return false;
-}
-
-void nfc_detect_enter(void* context) {
-    furi_assert(context);
-
-    NfcDetect* nfc_detect = (NfcDetect*)context;
-    with_view_model(
-        nfc_detect->view, (NfcDetectModel * model) {
-            model->found = false;
-            model->data.protocol = NfcDeviceProtocolUnknown;
-            return true;
-        });
-    nfc_worker_start(
-        nfc_detect->nfc_common->worker,
-        NfcWorkerStateDetect,
-        &nfc_detect->nfc_common->worker_result,
-        nfc_detect_worker_callback,
-        nfc_detect);
-}
-
-void nfc_detect_exit(void* context) {
-    furi_assert(context);
-
-    NfcDetect* nfc_detect = (NfcDetect*)context;
-    nfc_worker_stop(nfc_detect->nfc_common->worker);
-}
-
-NfcDetect* nfc_detect_alloc(NfcCommon* nfc_common) {
-    furi_assert(nfc_common);
-
-    NfcDetect* nfc_detect = furi_alloc(sizeof(NfcDetect));
-    nfc_detect->nfc_common = nfc_common;
-
-    // View allocation and configuration
-    nfc_detect->view = view_alloc();
-    view_allocate_model(nfc_detect->view, ViewModelTypeLockFree, sizeof(NfcDetectModel));
-    view_set_context(nfc_detect->view, nfc_detect);
-    view_set_draw_callback(nfc_detect->view, (ViewDrawCallback)nfc_detect_draw);
-    view_set_input_callback(nfc_detect->view, nfc_detect_input);
-    view_set_custom_callback(nfc_detect->view, nfc_detect_view_custom);
-    view_set_enter_callback(nfc_detect->view, nfc_detect_enter);
-    view_set_exit_callback(nfc_detect->view, nfc_detect_exit);
-
-    return nfc_detect;
-}
-
-void nfc_detect_free(NfcDetect* nfc_detect) {
-    furi_assert(nfc_detect);
-
-    view_free(nfc_detect->view);
-    free(nfc_detect);
-}
-
-View* nfc_detect_get_view(NfcDetect* nfc_detect) {
-    furi_assert(nfc_detect);
-
-    return nfc_detect->view;
-}

+ 0 - 12
applications/nfc/views/nfc_detect.h

@@ -1,12 +0,0 @@
-#pragma once
-
-#include <gui/view.h>
-#include "../nfc_types.h"
-
-typedef struct NfcDetect NfcDetect;
-
-NfcDetect* nfc_detect_alloc(NfcCommon* nfc_common);
-
-void nfc_detect_free(NfcDetect* nfc_detect);
-
-View* nfc_detect_get_view(NfcDetect* nfc_detect);

+ 0 - 79
applications/nfc/views/nfc_emulate.c

@@ -1,79 +0,0 @@
-#include "nfc_emulate.h"
-
-#include <furi.h>
-#include <api-hal.h>
-#include <input/input.h>
-
-#include "../nfc_i.h"
-
-struct NfcEmulate {
-    NfcCommon* nfc_common;
-    View* view;
-};
-
-void nfc_emulate_draw(Canvas* canvas, void* model) {
-    canvas_clear(canvas);
-    canvas_set_font(canvas, FontPrimary);
-    canvas_draw_str(canvas, 0, 12, "Emulating NFC-A");
-    canvas_set_font(canvas, FontSecondary);
-    canvas_draw_str(canvas, 2, 22, "Type: T2T");
-    canvas_draw_str(canvas, 2, 32, "UID length: 7");
-    canvas_draw_str(canvas, 2, 42, "UID: 36 9C E7 B1 0A C1 34");
-    canvas_draw_str(canvas, 2, 52, "SAK: 00 ATQA: 00/44");
-}
-
-bool nfc_emulate_input(InputEvent* event, void* context) {
-    if(event->key == InputKeyBack) {
-        return false;
-    }
-    return true;
-}
-
-void nfc_emulate_enter(void* context) {
-    furi_assert(context);
-
-    NfcEmulate* nfc_emulate = (NfcEmulate*)context;
-    nfc_worker_start(
-        nfc_emulate->nfc_common->worker,
-        NfcWorkerStateEmulate,
-        &nfc_emulate->nfc_common->worker_result,
-        NULL,
-        NULL);
-}
-
-void nfc_emulate_exit(void* context) {
-    furi_assert(context);
-
-    NfcEmulate* nfc_emulate = (NfcEmulate*)context;
-    nfc_worker_stop(nfc_emulate->nfc_common->worker);
-}
-
-NfcEmulate* nfc_emulate_alloc(NfcCommon* nfc_common) {
-    furi_assert(nfc_common);
-
-    NfcEmulate* nfc_emulate = furi_alloc(sizeof(NfcEmulate));
-    nfc_emulate->nfc_common = nfc_common;
-
-    // View allocation and configuration
-    nfc_emulate->view = view_alloc();
-    view_set_context(nfc_emulate->view, nfc_emulate);
-    view_set_draw_callback(nfc_emulate->view, (ViewDrawCallback)nfc_emulate_draw);
-    view_set_input_callback(nfc_emulate->view, nfc_emulate_input);
-    view_set_enter_callback(nfc_emulate->view, nfc_emulate_enter);
-    view_set_exit_callback(nfc_emulate->view, nfc_emulate_exit);
-
-    return nfc_emulate;
-}
-
-void nfc_emulate_free(NfcEmulate* nfc_emulate) {
-    furi_assert(nfc_emulate);
-
-    view_free(nfc_emulate->view);
-    free(nfc_emulate);
-}
-
-View* nfc_emulate_get_view(NfcEmulate* nfc_emulate) {
-    furi_assert(nfc_emulate);
-
-    return nfc_emulate->view;
-}

+ 0 - 12
applications/nfc/views/nfc_emulate.h

@@ -1,12 +0,0 @@
-#pragma once
-
-#include <gui/view.h>
-#include "../nfc_types.h"
-
-typedef struct NfcEmulate NfcEmulate;
-
-NfcEmulate* nfc_emulate_alloc(NfcCommon* nfc_common);
-
-void nfc_emulate_free(NfcEmulate* nfc_emulate);
-
-View* nfc_emulate_get_view(NfcEmulate* nfc_emulate);

+ 0 - 133
applications/nfc/views/nfc_emv.c

@@ -1,133 +0,0 @@
-#include "nfc_emv.h"
-
-#include <furi.h>
-#include <api-hal.h>
-#include <input/input.h>
-
-#include "../nfc_i.h"
-
-struct NfcEmv {
-    NfcCommon* nfc_common;
-    View* view;
-};
-
-typedef struct {
-    bool found;
-    NfcEmvData emv_data;
-} NfcEmvModel;
-
-void nfc_emv_draw(Canvas* canvas, NfcEmvModel* model) {
-    char buffer[32];
-    canvas_clear(canvas);
-    canvas_set_font(canvas, FontPrimary);
-    if(model->found) {
-        canvas_draw_str(canvas, 0, 12, "Found EMV card");
-        canvas_set_font(canvas, FontSecondary);
-        canvas_draw_str(canvas, 2, 22, "Type:");
-        snprintf(buffer, sizeof(buffer), "%s", model->emv_data.name);
-        canvas_draw_str(canvas, 2, 32, buffer);
-        snprintf(buffer, sizeof(buffer), "Number:\n");
-        canvas_draw_str(canvas, 2, 42, buffer);
-        uint8_t card_num_len = sizeof(model->emv_data.number);
-        for(uint8_t i = 0; i < card_num_len; i++) {
-            snprintf(
-                buffer + (i * 2), sizeof(buffer) - (i * 2), "%02X", model->emv_data.number[i]);
-        }
-        buffer[card_num_len * 2] = 0;
-        canvas_draw_str(canvas, 2, 52, buffer);
-    } else {
-        canvas_draw_str(canvas, 0, 12, "Searching");
-        canvas_set_font(canvas, FontSecondary);
-        canvas_draw_str(canvas, 2, 22, "Place card to the back");
-    }
-}
-
-bool nfc_emv_input(InputEvent* event, void* context) {
-    if(event->key == InputKeyBack) {
-        return false;
-    }
-    return true;
-}
-
-void nfc_emv_worker_callback(void* context) {
-    furi_assert(context);
-
-    NfcEmv* nfc_emv = (NfcEmv*)context;
-    view_dispatcher_send_custom_event(nfc_emv->nfc_common->view_dispatcher, NfcEventEmv);
-}
-
-bool nfc_emv_custom(uint32_t event, void* context) {
-    furi_assert(context);
-
-    NfcEmv* nfc_emv = (NfcEmv*)context;
-    if(event == NfcEventEmv) {
-        NfcEmvData* data = (NfcEmvData*)&nfc_emv->nfc_common->worker_result;
-
-        with_view_model(
-            nfc_emv->view, (NfcEmvModel * model) {
-                model->found = true;
-                model->emv_data = *data;
-                return true;
-            });
-        // TODO add and configure next view model
-        return true;
-    }
-
-    return false;
-}
-
-void nfc_emv_enter(void* context) {
-    furi_assert(context);
-
-    NfcEmv* nfc_emv = (NfcEmv*)context;
-    with_view_model(
-        nfc_emv->view, (NfcEmvModel * model) {
-            model->found = false;
-            return true;
-        });
-    nfc_worker_start(
-        nfc_emv->nfc_common->worker,
-        NfcWorkerStateReadEMV,
-        &nfc_emv->nfc_common->worker_result,
-        nfc_emv_worker_callback,
-        nfc_emv);
-}
-
-void nfc_emv_exit(void* context) {
-    furi_assert(context);
-
-    NfcEmv* nfc_emv = (NfcEmv*)context;
-    nfc_worker_stop(nfc_emv->nfc_common->worker);
-}
-
-NfcEmv* nfc_emv_alloc(NfcCommon* nfc_common) {
-    furi_assert(nfc_common);
-
-    NfcEmv* nfc_emv = furi_alloc(sizeof(NfcEmv));
-    nfc_emv->nfc_common = nfc_common;
-
-    // View allocation and configuration
-    nfc_emv->view = view_alloc();
-    view_allocate_model(nfc_emv->view, ViewModelTypeLockFree, sizeof(NfcEmvModel));
-    view_set_context(nfc_emv->view, nfc_emv);
-    view_set_draw_callback(nfc_emv->view, (ViewDrawCallback)nfc_emv_draw);
-    view_set_input_callback(nfc_emv->view, nfc_emv_input);
-    view_set_custom_callback(nfc_emv->view, nfc_emv_custom);
-    view_set_enter_callback(nfc_emv->view, nfc_emv_enter);
-    view_set_exit_callback(nfc_emv->view, nfc_emv_exit);
-
-    return nfc_emv;
-}
-
-void nfc_emv_free(NfcEmv* nfc_emv) {
-    furi_assert(nfc_emv);
-
-    view_free(nfc_emv->view);
-    free(nfc_emv);
-}
-
-View* nfc_emv_get_view(NfcEmv* nfc_emv) {
-    furi_assert(nfc_emv);
-
-    return nfc_emv->view;
-}

+ 0 - 12
applications/nfc/views/nfc_emv.h

@@ -1,12 +0,0 @@
-#pragma once
-
-#include <gui/view.h>
-#include "../nfc_types.h"
-
-typedef struct NfcEmv NfcEmv;
-
-NfcEmv* nfc_emv_alloc(NfcCommon* nfc_common);
-
-void nfc_emv_free(NfcEmv* nfc_emv);
-
-View* nfc_emv_get_view(NfcEmv* nfc_emv);

+ 0 - 162
applications/nfc/views/nfc_mifare_ul.c

@@ -1,162 +0,0 @@
-#include "nfc_mifare_ul.h"
-
-#include <furi.h>
-#include <api-hal.h>
-#include <input/input.h>
-
-#include "../nfc_i.h"
-
-struct NfcMifareUl {
-    NfcCommon* nfc_common;
-    View* view;
-};
-
-typedef struct {
-    bool found;
-    NfcMifareUlData nfc_mf_ul_data;
-} NfcMifareUlModel;
-
-void nfc_mifare_ul_draw(Canvas* canvas, NfcMifareUlModel* model) {
-    char buffer[32];
-    canvas_clear(canvas);
-    canvas_set_font(canvas, FontPrimary);
-    if(model->found) {
-        canvas_draw_str(canvas, 0, 12, "Found Mifare Ultralight");
-        canvas_set_font(canvas, FontSecondary);
-        canvas_draw_str(canvas, 2, 22, "UID:");
-        for(uint8_t i = 0; i < model->nfc_mf_ul_data.nfc_data.uid_len; i++) {
-            snprintf(
-                buffer + (i * 2),
-                sizeof(buffer) - (i * 2),
-                "%02X",
-                model->nfc_mf_ul_data.nfc_data.uid[i]);
-        }
-        buffer[model->nfc_mf_ul_data.nfc_data.uid_len * 2] = 0;
-        canvas_draw_str(canvas, 18, 22, buffer);
-
-        uint8_t man_bl_size = sizeof(model->nfc_mf_ul_data.man_block);
-        canvas_draw_str(canvas, 2, 32, "Manufacturer block:");
-        for(uint8_t i = 0; i < man_bl_size / 2; i++) {
-            snprintf(
-                buffer + (i * 2),
-                sizeof(buffer) - (i * 2),
-                "%02X",
-                model->nfc_mf_ul_data.man_block[i]);
-        }
-        buffer[man_bl_size] = 0;
-        canvas_draw_str(canvas, 2, 42, buffer);
-
-        for(uint8_t i = 0; i < man_bl_size / 2; i++) {
-            snprintf(
-                buffer + (i * 2),
-                sizeof(buffer) - (i * 2),
-                "%02X",
-                model->nfc_mf_ul_data.man_block[man_bl_size / 2 + i]);
-        }
-        buffer[man_bl_size] = 0;
-        canvas_draw_str(canvas, 2, 52, buffer);
-
-        canvas_draw_str(canvas, 2, 62, "OTP: ");
-        for(uint8_t i = 0; i < sizeof(model->nfc_mf_ul_data.otp); i++) {
-            snprintf(
-                buffer + (i * 2), sizeof(buffer) - (i * 2), "%02X", model->nfc_mf_ul_data.otp[i]);
-        }
-        buffer[sizeof(model->nfc_mf_ul_data.otp) * 2] = 0;
-        canvas_draw_str(canvas, 22, 62, buffer);
-    } else {
-        canvas_draw_str(canvas, 0, 12, "Searching");
-        canvas_set_font(canvas, FontSecondary);
-        canvas_draw_str(canvas, 2, 22, "Place card to the back");
-    }
-}
-
-bool nfc_mifare_ul_input(InputEvent* event, void* context) {
-    if(event->key == InputKeyBack) {
-        return false;
-    }
-    return true;
-}
-
-void nfc_mifare_ul_worker_callback(void* context) {
-    furi_assert(context);
-
-    NfcMifareUl* nfc_mifare_ul = (NfcMifareUl*)context;
-    view_dispatcher_send_custom_event(
-        nfc_mifare_ul->nfc_common->view_dispatcher, NfcEventMifareUl);
-}
-
-bool nfc_mifare_ul_custom(uint32_t event, void* context) {
-    furi_assert(context);
-
-    NfcMifareUl* nfc_mifare_ul = (NfcMifareUl*)context;
-    if(event == NfcEventMifareUl) {
-        NfcMifareUlData* data = (NfcMifareUlData*)&nfc_mifare_ul->nfc_common->worker_result;
-
-        with_view_model(
-            nfc_mifare_ul->view, (NfcMifareUlModel * model) {
-                model->found = true;
-                model->nfc_mf_ul_data = *data;
-                return true;
-            });
-        // TODO add and configure next view model
-        return true;
-    }
-
-    return false;
-}
-
-void nfc_mifare_ul_enter(void* context) {
-    furi_assert(context);
-
-    NfcMifareUl* nfc_mifare_ul = (NfcMifareUl*)context;
-    with_view_model(
-        nfc_mifare_ul->view, (NfcMifareUlModel * m) {
-            m->found = false;
-            return true;
-        });
-    nfc_worker_start(
-        nfc_mifare_ul->nfc_common->worker,
-        NfcWorkerStateReadMfUltralight,
-        &nfc_mifare_ul->nfc_common->worker_result,
-        nfc_mifare_ul_worker_callback,
-        nfc_mifare_ul);
-}
-
-void nfc_mifare_ul_exit(void* context) {
-    furi_assert(context);
-
-    NfcMifareUl* nfc_mifare_ul = (NfcMifareUl*)context;
-    nfc_worker_stop(nfc_mifare_ul->nfc_common->worker);
-}
-
-NfcMifareUl* nfc_mifare_ul_alloc(NfcCommon* nfc_common) {
-    furi_assert(nfc_common);
-
-    NfcMifareUl* nfc_mifare_ul = furi_alloc(sizeof(NfcMifareUl));
-    nfc_mifare_ul->nfc_common = nfc_common;
-
-    // View allocation and configuration
-    nfc_mifare_ul->view = view_alloc();
-    view_allocate_model(nfc_mifare_ul->view, ViewModelTypeLockFree, sizeof(NfcMifareUlModel));
-    view_set_context(nfc_mifare_ul->view, nfc_mifare_ul);
-    view_set_draw_callback(nfc_mifare_ul->view, (ViewDrawCallback)nfc_mifare_ul_draw);
-    view_set_input_callback(nfc_mifare_ul->view, nfc_mifare_ul_input);
-    view_set_custom_callback(nfc_mifare_ul->view, nfc_mifare_ul_custom);
-    view_set_enter_callback(nfc_mifare_ul->view, nfc_mifare_ul_enter);
-    view_set_exit_callback(nfc_mifare_ul->view, nfc_mifare_ul_exit);
-
-    return nfc_mifare_ul;
-}
-
-void nfc_mifare_ul_free(NfcMifareUl* nfc_mifare_ul) {
-    furi_assert(nfc_mifare_ul);
-
-    view_free(nfc_mifare_ul->view);
-    free(nfc_mifare_ul);
-}
-
-View* nfc_mifare_ul_get_view(NfcMifareUl* nfc_mifare_ul) {
-    furi_assert(nfc_mifare_ul);
-
-    return nfc_mifare_ul->view;
-}

+ 0 - 12
applications/nfc/views/nfc_mifare_ul.h

@@ -1,12 +0,0 @@
-#pragma once
-
-#include <gui/view.h>
-#include "../nfc_types.h"
-
-typedef struct NfcMifareUl NfcMifareUl;
-
-NfcMifareUl* nfc_mifare_ul_alloc(NfcCommon* nfc_common);
-
-void nfc_mifare_ul_free(NfcMifareUl* nfc_mifare_ul);
-
-View* nfc_mifare_ul_get_view(NfcMifareUl* nfc_mifare_ul);

+ 83 - 3
lib/nfc_protocols/mifare_ultralight.c

@@ -12,8 +12,9 @@ uint16_t mf_ul_prepare_get_version(uint8_t* dest) {
     return 1;
 }
 
-void mf_ul_parse_get_version_response(uint8_t* buff, MfUltralightRead* mf_ul_read) {
+void mf_ul_parse_get_version_response(uint8_t* buff, MifareUlDevice* mf_ul_read) {
     MfUltralightVersion* version = (MfUltralightVersion*) buff;
+    memcpy(&mf_ul_read->data.version, version, sizeof(MfUltralightVersion));
     if(version->storage_size == 0x0B || version->storage_size == 0x00) {
         mf_ul_read->type = MfUltralightTypeUL11;
         mf_ul_read->pages_to_read = 20;
@@ -39,9 +40,9 @@ void mf_ul_parse_get_version_response(uint8_t* buff, MfUltralightRead* mf_ul_rea
     }
 }
 
-void mf_ul_set_default_version(MfUltralightRead* mf_ul_read) {
+void mf_ul_set_default_version(MifareUlDevice* mf_ul_read) {
     mf_ul_read->type = MfUltralightTypeUnknown;
-    mf_ul_read->pages_to_read = 20;
+    mf_ul_read->pages_to_read = 16;
     mf_ul_read->support_fast_read = false;
 }
 
@@ -51,9 +52,88 @@ uint16_t mf_ul_prepare_read(uint8_t* dest, uint8_t start_page) {
     return 2;
 }
 
+void mf_ul_parse_read_response(uint8_t* buff, uint16_t page_addr, MifareUlDevice* mf_ul_read) {
+    mf_ul_read->pages_readed += 4;
+    mf_ul_read->data.data_size = mf_ul_read->pages_readed * 4;
+    memcpy(&mf_ul_read->data.data[page_addr * 4], buff, 16);
+}
+
 uint16_t mf_ul_prepare_fast_read(uint8_t* dest, uint8_t start_page, uint8_t end_page) {
     dest[0] = MF_UL_FAST_READ_CMD;
     dest[1] = start_page;
     dest[2] = end_page;
     return 3;
 }
+
+void mf_ul_parse_fast_read_response(uint8_t* buff, uint8_t start_page, uint8_t end_page, MifareUlDevice* mf_ul_read) {
+    mf_ul_read->pages_readed = end_page - start_page + 1;
+    mf_ul_read->data.data_size = mf_ul_read->pages_readed * 4;
+    memcpy(mf_ul_read->data.data, buff, mf_ul_read->data.data_size);
+}
+
+uint16_t mf_ul_prepare_read_signature(uint8_t* dest) {
+    dest[0] = MF_UL_CHECK_TEARING;
+    dest[1] = 0;
+    return 2;
+}
+
+void mf_ul_parse_read_signature_response(uint8_t* buff, MifareUlDevice* mf_ul_read) {
+    memcpy(mf_ul_read->data.signature, buff, sizeof(mf_ul_read->data.signature));
+}
+
+uint16_t mf_ul_prepare_read_cnt(uint8_t* dest, uint8_t cnt_index) {
+    if(cnt_index > 2) {
+        return 0;
+    }
+    dest[0] = MF_UL_READ_CNT;
+    dest[1] = cnt_index;
+    return 2;
+}
+
+void mf_ul_parse_read_cnt_response(uint8_t* buff, uint8_t cnt_index, MifareUlDevice* mf_ul_read) {
+    // Reverse LSB sequence
+    if(cnt_index < 3) {
+        mf_ul_read->data.counter[cnt_index] = (buff[2] << 16) | (buff[1] << 8) | (buff[0]);
+    }
+}
+
+uint16_t mf_ul_prepare_inc_cnt(uint8_t* dest, uint8_t cnt_index, uint32_t value) {
+    if(cnt_index > 2) {
+        return 0;
+    }
+    dest[0] = MF_UL_INC_CNT;
+    dest[1] = cnt_index;
+    dest[2] = (uint8_t) value;
+    dest[3] = (uint8_t) (value >> 8);
+    dest[4] = (uint8_t) (value >> 16);
+    dest[5] = 0;
+    return 6;
+}
+
+uint16_t mf_ul_prepare_check_tearing(uint8_t* dest, uint8_t cnt_index) {
+    if(cnt_index > 2) {
+        return 0;
+    }
+    dest[0] = MF_UL_CHECK_TEARING;
+    dest[1] = cnt_index;
+    return 2;
+}
+
+void mf_ul_parse_check_tearing_response(uint8_t* buff, uint8_t cnt_index, MifareUlDevice* mf_ul_read) {
+    if(cnt_index < 2) {
+        mf_ul_read->data.tearing[cnt_index] = buff[0];
+    }
+}
+
+uint16_t mf_ul_prepare_write(uint8_t* dest, uint16_t page_addr, uint32_t data) {
+    if(page_addr < 2) {
+        return 0;
+    }
+    dest[0] = MF_UL_WRITE;
+    dest[1] = page_addr;
+    dest[2] = (uint8_t) (data >> 24);
+    dest[3] = (uint8_t) (data >> 16);
+    dest[4] = (uint8_t) (data >> 8);
+    dest[5] = (uint8_t) data;
+    return 6;
+}

+ 43 - 4
lib/nfc_protocols/mifare_ultralight.h

@@ -4,9 +4,22 @@
 #include <stdbool.h>
 #include <string.h>
 
+#define MF_UL_MAX_DUMP_SIZE 255
+
+#define MF_UL_TEARING_FLAG_DEFAULT (0xBD)
+
 #define MF_UL_GET_VERSION_CMD (0x60)
 #define MF_UL_READ_CMD (0x30)
 #define MF_UL_FAST_READ_CMD (0x3A)
+#define MF_UL_WRITE (0xA2)
+#define MF_UL_COMP_WRITE (0xA0)
+#define MF_UL_READ_CNT (0x39)
+#define MF_UL_INC_CNT (0xA5)
+#define MF_UL_AUTH (0x1B)
+#define MF_UL_READ_SIG (0x3C)
+#define MF_UL_CHECK_TEARING (0x3E)
+#define MF_UL_READ_VCSL (0x4B)
+
 
 typedef enum {
     MfUltralightTypeUnknown,
@@ -38,19 +51,45 @@ typedef struct {
     uint8_t  otp[4];
 } MfUltralightManufacturerBlock;
 
+typedef struct {
+    MfUltralightVersion version;
+    uint8_t signature[32];
+    uint32_t counter[3];
+    uint8_t tearing[3];
+    uint16_t data_size;
+    uint8_t data[MF_UL_MAX_DUMP_SIZE];
+} MifareUlData;
+
 typedef struct {
     MfUltralightType type;
     uint8_t pages_to_read;
     uint8_t pages_readed;
     bool support_fast_read;
-    uint8_t dump[255];
-} MfUltralightRead;
+    MifareUlData data;
+} MifareUlDevice;
 
 bool mf_ul_check_card_type(uint8_t ATQA0, uint8_t ATQA1, uint8_t SAK);
 
 uint16_t mf_ul_prepare_get_version(uint8_t* dest);
-void mf_ul_parse_get_version_response(uint8_t* buff, MfUltralightRead* mf_ul_read);
-void mf_ul_set_default_version(MfUltralightRead* mf_ul_read);
+void mf_ul_parse_get_version_response(uint8_t* buff, MifareUlDevice* mf_ul_read);
+void mf_ul_set_default_version(MifareUlDevice* mf_ul_read);
+
+uint16_t mf_ul_prepare_read_signature(uint8_t* dest);
+void mf_ul_parse_read_signature_response(uint8_t* buff, MifareUlDevice* mf_ul_read);
+
+uint16_t mf_ul_prepare_read_cnt(uint8_t* dest, uint8_t cnt_index);
+void mf_ul_parse_read_cnt_response(uint8_t* buff, uint8_t cnt_index, MifareUlDevice* mf_ul_read);
+
+uint16_t mf_ul_prepare_inc_cnt(uint8_t* dest, uint8_t cnt_index, uint32_t value);
+
+uint16_t mf_ul_prepare_check_tearing(uint8_t* dest, uint8_t cnt_index);
+void mf_ul_parse_check_tearing_response(uint8_t* buff, uint8_t cnt_index, MifareUlDevice* mf_ul_read);
 
 uint16_t mf_ul_prepare_read(uint8_t* dest, uint8_t start_page);
+void mf_ul_parse_read_response(uint8_t* buff, uint16_t page_addr, MifareUlDevice* mf_ul_read);
+
 uint16_t mf_ul_prepare_fast_read(uint8_t* dest, uint8_t start_page, uint8_t end_page);
+void mf_ul_parse_fast_read_response(uint8_t* buff, uint8_t start_page, uint8_t end_page, MifareUlDevice* mf_ul_read);
+
+uint16_t mf_ul_prepare_write(uint8_t* dest, uint16_t page_addr, uint32_t data);
+