Merge branch 'flipperdevices:dev' into dev

This commit is contained in:
Eng1n33r
2022-04-20 16:34:24 +03:00
committed by GitHub
94 changed files with 1926 additions and 1738 deletions

View File

@@ -76,7 +76,7 @@ jobs:
with: with:
run: | run: |
set -e set -e
make assets_manifest make assets_rebuild assets_manifest
git diff --quiet || ( echo "Assets recompilation required."; exit 255 ) git diff --quiet || ( echo "Assets recompilation required."; exit 255 )
- name: 'Build the firmware in docker' - name: 'Build the firmware in docker'

View File

@@ -79,7 +79,6 @@ ifeq ($(FORCE), 1)
endif endif
@$(MAKE) -C $(PROJECT_ROOT)/firmware -j$(NPROCS) flash @$(MAKE) -C $(PROJECT_ROOT)/firmware -j$(NPROCS) flash
.PHONY: updater .PHONY: updater
updater: updater:
@$(MAKE) -C $(PROJECT_ROOT)/firmware -j$(NPROCS) RAM_EXEC=1 all @$(MAKE) -C $(PROJECT_ROOT)/firmware -j$(NPROCS) RAM_EXEC=1 all
@@ -97,14 +96,16 @@ updater_package_bin: firmware_all updater
@$(PROJECT_ROOT)/scripts/dist.py copy -t $(TARGET) -p firmware updater -s $(DIST_SUFFIX) --bundlever "$(VERSION_STRING)" @$(PROJECT_ROOT)/scripts/dist.py copy -t $(TARGET) -p firmware updater -s $(DIST_SUFFIX) --bundlever "$(VERSION_STRING)"
.PHONY: updater_package .PHONY: updater_package
updater_package: firmware_all updater updater_package: firmware_all updater assets_manifest
@$(PROJECT_ROOT)/scripts/dist.py copy -t $(TARGET) -p firmware updater -s $(DIST_SUFFIX) -a assets/resources --bundlever "$(VERSION_STRING)" @$(PROJECT_ROOT)/scripts/dist.py copy -t $(TARGET) -p firmware updater -s $(DIST_SUFFIX) -r $(PROJECT_ROOT)/assets/resources --bundlever "$(VERSION_STRING)"
.PHONY: assets_manifest .PHONY: assets_manifest
assets_manifest: assets_manifest:
@$(MAKE) -C $(PROJECT_ROOT)/assets clean @$(MAKE) -C $(PROJECT_ROOT)/assets manifest
@$(MAKE) -C $(PROJECT_ROOT)/assets
@$(PROJECT_ROOT)/scripts/assets.py manifest assets/resources .PHONY: assets_rebuild
assets_rebuild:
@$(MAKE) -C $(PROJECT_ROOT)/assets clean all
.PHONY: flash_radio .PHONY: flash_radio
flash_radio: flash_radio:
@@ -124,8 +125,8 @@ flash_radio_fus:
.PHONY: flash_radio_fus_please_i_m_not_going_to_complain .PHONY: flash_radio_fus_please_i_m_not_going_to_complain
flash_radio_fus_please_i_m_not_going_to_complain: flash_radio_fus_please_i_m_not_going_to_complain:
@$(PROJECT_ROOT)/scripts/flash.py core2fus 0x080EC000 --statement=AGREE_TO_LOOSE_FLIPPER_FEATURES_THAT_USES_CRYPTO_ENCLAVE $(COPRO_DIR)/stm32wb5x_FUS_fw_for_fus_0_5_3.bin @$(PROJECT_ROOT)/scripts/flash.py core2fus 0x080EC000 --statement=AGREE_TO_LOSE_FLIPPER_FEATURES_THAT_USE_CRYPTO_ENCLAVE $(COPRO_DIR)/stm32wb5x_FUS_fw_for_fus_0_5_3.bin
@$(PROJECT_ROOT)/scripts/flash.py core2fus 0x080EC000 --statement=AGREE_TO_LOOSE_FLIPPER_FEATURES_THAT_USES_CRYPTO_ENCLAVE $(COPRO_DIR)/stm32wb5x_FUS_fw.bin @$(PROJECT_ROOT)/scripts/flash.py core2fus 0x080EC000 --statement=AGREE_TO_LOSE_FLIPPER_FEATURES_THAT_USE_CRYPTO_ENCLAVE $(COPRO_DIR)/stm32wb5x_FUS_fw.bin
@$(PROJECT_ROOT)/scripts/ob.py set @$(PROJECT_ROOT)/scripts/ob.py set
.PHONY: lint .PHONY: lint

View File

@@ -43,6 +43,7 @@ extern int32_t usb_test_app(void* p);
extern int32_t vibro_test_app(void* p); extern int32_t vibro_test_app(void* p);
extern int32_t bt_hid_app(void* p); extern int32_t bt_hid_app(void* p);
extern int32_t battery_test_app(void* p); extern int32_t battery_test_app(void* p);
extern int32_t text_box_test_app(void* p);
// Plugins // Plugins
extern int32_t music_player_app(void* p); extern int32_t music_player_app(void* p);
@@ -304,6 +305,10 @@ const FlipperApplication FLIPPER_DEBUG_APPS[] = {
#ifdef APP_BATTERY_TEST #ifdef APP_BATTERY_TEST
{.app = battery_test_app, .name = "Battery Test", .stack_size = 1024, .icon = NULL}, {.app = battery_test_app, .name = "Battery Test", .stack_size = 1024, .icon = NULL},
#endif #endif
#ifdef APP_TEXT_BOX_TEST
{.app = text_box_test_app, .name = "Text Box Test", .stack_size = 1024, .icon = NULL},
#endif
}; };
const size_t FLIPPER_DEBUG_APPS_COUNT = COUNT_OF(FLIPPER_DEBUG_APPS); const size_t FLIPPER_DEBUG_APPS_COUNT = COUNT_OF(FLIPPER_DEBUG_APPS);

View File

@@ -165,6 +165,12 @@ CFLAGS += -DAPP_DISPLAY_TEST
SRV_GUI = 1 SRV_GUI = 1
endif endif
APP_TEXT_BOX_TEST ?= 0
ifeq ($(APP_TEXT_BOX_TEST), 1)
CFLAGS += -DAPP_TEXT_BOX_TEST
SRV_GUI = 1
endif
APP_BATTERY_TEST ?= 0 APP_BATTERY_TEST ?= 0
ifeq ($(APP_BATTERY_TEST), 1) ifeq ($(APP_BATTERY_TEST), 1)
CFLAGS += -DAPP_BATTERY_TEST CFLAGS += -DAPP_BATTERY_TEST

View File

@@ -33,7 +33,8 @@ void archive_scene_delete_on_enter(void* context) {
char delete_str[64]; char delete_str[64];
snprintf(delete_str, sizeof(delete_str), "\e#Delete %s?\e#", name); snprintf(delete_str, sizeof(delete_str), "\e#Delete %s?\e#", name);
widget_add_text_box_element(app->widget, 0, 0, 128, 23, AlignCenter, AlignCenter, delete_str); widget_add_text_box_element(
app->widget, 0, 0, 128, 23, AlignCenter, AlignCenter, delete_str, false);
view_dispatcher_switch_to_view(app->view_dispatcher, ArchiveViewWidget); view_dispatcher_switch_to_view(app->view_dispatcher, ArchiveViewWidget);
} }

View File

@@ -0,0 +1,126 @@
#include <furi.h>
#include <gui/gui.h>
#include <input/input.h>
#include <gui/elements.h>
#define TAG "TextBoxTest"
static void text_box_center_top_secondary_128x22(Canvas* canvas) {
canvas_draw_frame(canvas, 0, 0, 128, 22);
elements_text_box(canvas, 0, 0, 128, 22, AlignCenter, AlignTop, "secondary font test", false);
}
static void text_box_right_bottom_bold_128x22(Canvas* canvas) {
canvas_draw_frame(canvas, 0, 0, 128, 22);
elements_text_box(
canvas, 0, 0, 128, 22, AlignRight, AlignBottom, "\e#Bold font test\e#", false);
}
static void text_box_left_center_mixed_80x50(Canvas* canvas) {
canvas_draw_frame(canvas, 0, 0, 80, 50);
elements_text_box(
canvas,
0,
0,
80,
50,
AlignLeft,
AlignCenter,
"\e#Never\e# gonna give you up\n\e!Never\e! gonna let you down",
false);
}
static void text_box_center_center_secondary_110x44(Canvas* canvas) {
canvas_draw_frame(canvas, 4, 20, 110, 30);
elements_text_box(
canvas,
4,
20,
110,
30,
AlignCenter,
AlignCenter,
"Loooooooooooooo0000000ooong file name from happy 100500 Flipper 0wners",
true);
}
static void (*text_box_test_render[])(Canvas* canvas) = {
text_box_center_top_secondary_128x22,
text_box_right_bottom_bold_128x22,
text_box_left_center_mixed_80x50,
text_box_center_center_secondary_110x44,
};
typedef struct {
uint32_t idx;
} TextBoxTestState;
static void text_box_test_render_callback(Canvas* canvas, void* ctx) {
TextBoxTestState* state = acquire_mutex((ValueMutex*)ctx, 25);
canvas_clear(canvas);
text_box_test_render[state->idx](canvas);
release_mutex((ValueMutex*)ctx, state);
}
static void text_box_test_input_callback(InputEvent* input_event, void* ctx) {
osMessageQueueId_t event_queue = ctx;
osMessageQueuePut(event_queue, input_event, 0, osWaitForever);
}
int32_t text_box_test_app(void* p) {
osMessageQueueId_t event_queue = osMessageQueueNew(32, sizeof(InputEvent), NULL);
furi_check(event_queue);
TextBoxTestState _state = {.idx = 0};
ValueMutex state_mutex;
if(!init_mutex(&state_mutex, &_state, sizeof(TextBoxTestState))) {
FURI_LOG_E(TAG, "Cannot create mutex");
return 0;
}
ViewPort* view_port = view_port_alloc();
view_port_draw_callback_set(view_port, text_box_test_render_callback, &state_mutex);
view_port_input_callback_set(view_port, text_box_test_input_callback, event_queue);
// Open GUI and register view_port
Gui* gui = furi_record_open("gui");
gui_add_view_port(gui, view_port, GuiLayerFullscreen);
uint32_t test_renders_num = SIZEOF_ARRAY(text_box_test_render);
InputEvent event;
while(osMessageQueueGet(event_queue, &event, NULL, osWaitForever) == osOK) {
TextBoxTestState* state = acquire_mutex_block(&state_mutex);
if(event.type == InputTypeShort) {
if(event.key == InputKeyRight) {
if(state->idx < test_renders_num - 1) {
state->idx++;
}
} else if(event.key == InputKeyLeft) {
if(state->idx > 0) {
state->idx--;
}
} else if(event.key == InputKeyBack) {
release_mutex(&state_mutex, state);
break;
}
}
release_mutex(&state_mutex, state);
view_port_update(view_port);
}
// remove & free all stuff created by app
gui_remove_view_port(gui, view_port);
view_port_free(view_port);
osMessageQueueDelete(event_queue);
delete_mutex(&state_mutex);
furi_record_close("gui");
return 0;
}

View File

@@ -547,7 +547,8 @@ void elements_text_box(
uint8_t height, uint8_t height,
Align horizontal, Align horizontal,
Align vertical, Align vertical,
const char* text) { const char* text,
bool strip_to_dots) {
furi_assert(canvas); furi_assert(canvas);
ElementTextBoxLine line[ELEMENTS_MAX_LINES_NUM]; ElementTextBoxLine line[ELEMENTS_MAX_LINES_NUM];
@@ -571,6 +572,7 @@ void elements_text_box(
uint8_t total_height_default = 0; uint8_t total_height_default = 0;
uint16_t i = 0; uint16_t i = 0;
bool full_text_processed = false; bool full_text_processed = false;
uint16_t dots_width = canvas_string_width(canvas, "...");
canvas_set_font(canvas, FontSecondary); canvas_set_font(canvas, FontSecondary);
@@ -663,31 +665,29 @@ void elements_text_box(
} }
// Set vertical alignment for all lines // Set vertical alignment for all lines
if(full_text_processed) { if(total_height_default < height) {
if(total_height_default < height) { if(vertical == AlignTop) {
if(vertical == AlignTop) {
line[0].y = y + line[0].height;
} else if(vertical == AlignCenter) {
line[0].y = y + line[0].height + (height - total_height_default) / 2;
} else if(vertical == AlignBottom) {
line[0].y = y + line[0].height + (height - total_height_default);
}
if(line_num > 1) {
for(uint8_t i = 1; i < line_num; i++) {
line[i].y = line[i - 1].y + line[i - 1].leading_default;
}
}
} else if(line_num > 1) {
uint8_t free_pixel_num = height - total_height_min;
uint8_t fill_pixel = 0;
uint8_t j = 1;
line[0].y = y + line[0].height; line[0].y = y + line[0].height;
while(fill_pixel < free_pixel_num) { } else if(vertical == AlignCenter) {
line[j].y = line[j - 1].y + line[j - 1].leading_min + 1; line[0].y = y + line[0].height + (height - total_height_default) / 2;
fill_pixel++; } else if(vertical == AlignBottom) {
j = j % (line_num - 1) + 1; line[0].y = y + line[0].height + (height - total_height_default);
}
if(line_num > 1) {
for(uint8_t i = 1; i < line_num; i++) {
line[i].y = line[i - 1].y + line[i - 1].leading_default;
} }
} }
} else if(line_num > 1) {
uint8_t free_pixel_num = height - total_height_min;
uint8_t fill_pixel = 0;
uint8_t j = 1;
line[0].y = y + line[0].height;
while(fill_pixel < free_pixel_num) {
line[j].y = line[j - 1].y + line[j - 1].leading_min + 1;
fill_pixel++;
j = j % (line_num - 1) + 1;
}
} }
// Draw line by line // Draw line by line
@@ -733,6 +733,13 @@ void elements_text_box(
canvas_draw_glyph(canvas, line[i].x, line[i].y, line[i].text[j]); canvas_draw_glyph(canvas, line[i].x, line[i].y, line[i].text[j]);
canvas_invert_color(canvas); canvas_invert_color(canvas);
} else { } else {
if((i == line_num - 1) && strip_to_dots) {
uint8_t next_symbol_width = canvas_glyph_width(canvas, line[i].text[j]);
if(line[i].x + next_symbol_width + dots_width > x + width) {
canvas_draw_str(canvas, line[i].x, line[i].y, "...");
break;
}
}
canvas_draw_glyph(canvas, line[i].x, line[i].y, line[i].text[j]); canvas_draw_glyph(canvas, line[i].x, line[i].y, line[i].text[j]);
} }
line[i].x += canvas_glyph_width(canvas, line[i].text[j]); line[i].x += canvas_glyph_width(canvas, line[i].text[j]);

View File

@@ -194,17 +194,18 @@ void elements_string_fit_width(Canvas* canvas, string_t string, uint8_t width);
/** Draw text box element /** Draw text box element
* *
* @param canvas Canvas instance * @param canvas Canvas instance
* @param x x coordinate * @param x x coordinate
* @param y y coordinate * @param y y coordinate
* @param width width to fit text * @param width width to fit text
* @param height height to fit text * @param height height to fit text
* @param horizontal Align instance * @param horizontal Align instance
* @param vertical Align instance * @param vertical Align instance
* @param[in] text Formatted text. The following formats are available: * @param[in] text Formatted text. The following formats are available:
* "\e#Bold text\e#" - bold font is used * "\e#Bold text\e#" - bold font is used
* "\e*Monospaced text\e*" - monospaced font is used * "\e*Monospaced text\e*" - monospaced font is used
* "\e#Inversed text\e#" - white text on black background * "\e#Inversed text\e#" - white text on black background
* @param strip_to_dots Strip text to ... if does not fit to width
*/ */
void elements_text_box( void elements_text_box(
Canvas* canvas, Canvas* canvas,
@@ -214,7 +215,8 @@ void elements_text_box(
uint8_t height, uint8_t height,
Align horizontal, Align horizontal,
Align vertical, Align vertical,
const char* text); const char* text,
bool strip_to_dots);
#ifdef __cplusplus #ifdef __cplusplus
} }

7
applications/gui/modules/widget.c Executable file → Normal file
View File

@@ -154,10 +154,11 @@ void widget_add_text_box_element(
uint8_t height, uint8_t height,
Align horizontal, Align horizontal,
Align vertical, Align vertical,
const char* text) { const char* text,
bool strip_to_dots) {
furi_assert(widget); furi_assert(widget);
WidgetElement* text_box_element = WidgetElement* text_box_element = widget_element_text_box_create(
widget_element_text_box_create(x, y, width, height, horizontal, vertical, text); x, y, width, height, horizontal, vertical, text, strip_to_dots);
widget_add_element(widget, text_box_element); widget_add_element(widget, text_box_element);
} }

View File

@@ -81,17 +81,18 @@ void widget_add_string_element(
/** Add Text Box Element /** Add Text Box Element
* *
* @param widget Widget instance * @param widget Widget instance
* @param x x coordinate * @param x x coordinate
* @param y y coordinate * @param y y coordinate
* @param width width to fit text * @param width width to fit text
* @param height height to fit text * @param height height to fit text
* @param horizontal Align instance * @param horizontal Align instance
* @param vertical Align instance * @param vertical Align instance
* @param[in] text Formatted text. The following formats are available: * @param[in] text Formatted text. The following formats are available:
* "\e#Bold text\e#" - bold font is used * "\e#Bold text\e#" - bold font is used
* "\e*Monospaced text\e*" - monospaced font is used * "\e*Monospaced text\e*" - monospaced font is used
* "\e#Inversed text\e#" - white text on black background * "\e#Inversed text\e#" - white text on black background
* @param strip_to_dots Strip text to ... if does not fit to width
*/ */
void widget_add_text_box_element( void widget_add_text_box_element(
Widget* widget, Widget* widget,
@@ -101,7 +102,8 @@ void widget_add_text_box_element(
uint8_t height, uint8_t height,
Align horizontal, Align horizontal,
Align vertical, Align vertical,
const char* text); const char* text,
bool strip_to_dots);
/** Add Button Element /** Add Button Element
* *

View File

@@ -60,7 +60,8 @@ WidgetElement* widget_element_text_box_create(
uint8_t height, uint8_t height,
Align horizontal, Align horizontal,
Align vertical, Align vertical,
const char* text); const char* text,
bool strip_to_dots);
/** Create button element */ /** Create button element */
WidgetElement* widget_element_button_create( WidgetElement* widget_element_button_create(

View File

@@ -10,6 +10,7 @@ typedef struct {
Align horizontal; Align horizontal;
Align vertical; Align vertical;
string_t text; string_t text;
bool strip_to_dots;
} GuiTextBoxModel; } GuiTextBoxModel;
static void gui_text_box_draw(Canvas* canvas, WidgetElement* element) { static void gui_text_box_draw(Canvas* canvas, WidgetElement* element) {
@@ -26,7 +27,8 @@ static void gui_text_box_draw(Canvas* canvas, WidgetElement* element) {
model->height, model->height,
model->horizontal, model->horizontal,
model->vertical, model->vertical,
string_get_cstr(model->text)); string_get_cstr(model->text),
model->strip_to_dots);
} }
} }
@@ -46,7 +48,8 @@ WidgetElement* widget_element_text_box_create(
uint8_t height, uint8_t height,
Align horizontal, Align horizontal,
Align vertical, Align vertical,
const char* text) { const char* text,
bool strip_to_dots) {
furi_assert(text); furi_assert(text);
// Allocate and init model // Allocate and init model
@@ -58,6 +61,7 @@ WidgetElement* widget_element_text_box_create(
model->horizontal = horizontal; model->horizontal = horizontal;
model->vertical = vertical; model->vertical = vertical;
string_init_set_str(model->text, text); string_init_set_str(model->text, text);
model->strip_to_dots = strip_to_dots;
// Allocate and init Element // Allocate and init Element
WidgetElement* gui_string = malloc(sizeof(WidgetElement)); WidgetElement* gui_string = malloc(sizeof(WidgetElement));

View File

@@ -21,7 +21,7 @@ void iButtonSceneDeleteConfirm::on_enter(iButtonApp* app) {
app->set_text_store("\e#Delete %s?\e#", ibutton_key_get_name_p(key)); app->set_text_store("\e#Delete %s?\e#", ibutton_key_get_name_p(key));
widget_add_text_box_element( widget_add_text_box_element(
widget, 0, 0, 128, 27, AlignCenter, AlignCenter, app->get_text_store()); widget, 0, 0, 128, 27, AlignCenter, AlignCenter, app->get_text_store(), false);
widget_add_button_element(widget, GuiButtonTypeLeft, "Cancel", widget_callback, app); widget_add_button_element(widget, GuiButtonTypeLeft, "Cancel", widget_callback, app);
widget_add_button_element(widget, GuiButtonTypeRight, "Delete", widget_callback, app); widget_add_button_element(widget, GuiButtonTypeRight, "Delete", widget_callback, app);

View File

@@ -9,7 +9,7 @@ void iButtonSceneInfo::on_enter(iButtonApp* app) {
app->set_text_store("%s", ibutton_key_get_name_p(key)); app->set_text_store("%s", ibutton_key_get_name_p(key));
widget_add_text_box_element( widget_add_text_box_element(
widget, 0, 0, 128, 28, AlignCenter, AlignCenter, app->get_text_store()); widget, 0, 0, 128, 28, AlignCenter, AlignCenter, app->get_text_store(), false);
switch(ibutton_key_get_type(key)) { switch(ibutton_key_get_type(key)) {
case iButtonKeyDS1990: case iButtonKeyDS1990:

View File

@@ -23,9 +23,9 @@ void InfraredAppSceneEdit::on_enter(InfraredApp* app) {
InfraredAppViewManager* view_manager = app->get_view_manager(); InfraredAppViewManager* view_manager = app->get_view_manager();
Submenu* submenu = view_manager->get_submenu(); Submenu* submenu = view_manager->get_submenu();
submenu_add_item(submenu, "Add Key", SubmenuIndexAddKey, submenu_callback, app); submenu_add_item(submenu, "Add Button", SubmenuIndexAddKey, submenu_callback, app);
submenu_add_item(submenu, "Rename Key", SubmenuIndexRenameKey, submenu_callback, app); submenu_add_item(submenu, "Rename Button", SubmenuIndexRenameKey, submenu_callback, app);
submenu_add_item(submenu, "Delete Key", SubmenuIndexDeleteKey, submenu_callback, app); submenu_add_item(submenu, "Delete Button", SubmenuIndexDeleteKey, submenu_callback, app);
submenu_add_item(submenu, "Rename Remote", SubmenuIndexRenameRemote, submenu_callback, app); submenu_add_item(submenu, "Rename Remote", SubmenuIndexRenameRemote, submenu_callback, app);
submenu_add_item(submenu, "Delete Remote", SubmenuIndexDeleteRemote, submenu_callback, app); submenu_add_item(submenu, "Delete Remote", SubmenuIndexDeleteRemote, submenu_callback, app);
submenu_set_selected_item(submenu, submenu_item_selected); submenu_set_selected_item(submenu, submenu_item_selected);

View File

@@ -51,7 +51,7 @@ void InfraredAppSceneEditDelete::on_enter(InfraredApp* app) {
dialog_ex_set_text(dialog_ex, app->get_text_store(0), 64, 31, AlignCenter, AlignCenter); dialog_ex_set_text(dialog_ex, app->get_text_store(0), 64, 31, AlignCenter, AlignCenter);
dialog_ex_set_icon(dialog_ex, 0, 0, NULL); dialog_ex_set_icon(dialog_ex, 0, 0, NULL);
dialog_ex_set_left_button_text(dialog_ex, "Back"); dialog_ex_set_left_button_text(dialog_ex, "Cancel");
dialog_ex_set_right_button_text(dialog_ex, "Delete"); dialog_ex_set_right_button_text(dialog_ex, "Delete");
dialog_ex_set_result_callback(dialog_ex, dialog_result_callback); dialog_ex_set_result_callback(dialog_ex, dialog_result_callback);
dialog_ex_set_context(dialog_ex, app); dialog_ex_set_context(dialog_ex, app);

View File

@@ -16,8 +16,9 @@ void InfraredAppSceneEditKeySelect::on_enter(InfraredApp* app) {
Submenu* submenu = view_manager->get_submenu(); Submenu* submenu = view_manager->get_submenu();
int item_number = 0; int item_number = 0;
const char* header = const char* header = app->get_edit_action() == InfraredApp::EditAction::Rename ?
app->get_edit_action() == InfraredApp::EditAction::Rename ? "Rename key:" : "Delete key:"; "Rename Button:" :
"Delete Button:";
submenu_set_header(submenu, header); submenu_set_header(submenu, header);
auto remote_manager = app->get_remote_manager(); auto remote_manager = app->get_remote_manager();

View File

@@ -14,7 +14,7 @@ void InfraredAppSceneEditRename::on_enter(InfraredApp* app) {
strncpy(buffer_str, button_name.c_str(), max_len); strncpy(buffer_str, button_name.c_str(), max_len);
buffer_str[max_len + 1] = 0; buffer_str[max_len + 1] = 0;
enter_name_length = max_len; enter_name_length = max_len;
text_input_set_header_text(text_input, "Name the key"); text_input_set_header_text(text_input, "Name the button");
} else { } else {
auto remote_name = remote_manager->get_remote_name(); auto remote_name = remote_manager->get_remote_name();
strncpy(app->get_text_store(0), remote_name.c_str(), app->get_text_store_size()); strncpy(app->get_text_store(0), remote_name.c_str(), app->get_text_store_size());

View File

@@ -20,7 +20,7 @@ void InfraredAppSceneLearnEnterName::on_enter(InfraredApp* app) {
app->set_text_store(0, "RAW_%d", raw_signal.timings_cnt); app->set_text_store(0, "RAW_%d", raw_signal.timings_cnt);
} }
text_input_set_header_text(text_input, "Name the key"); text_input_set_header_text(text_input, "Name the button");
text_input_set_result_callback( text_input_set_result_callback(
text_input, text_input,
InfraredApp::text_input_callback, InfraredApp::text_input_callback,

View File

@@ -3,19 +3,19 @@
bool nfc_custom_event_callback(void* context, uint32_t event) { bool nfc_custom_event_callback(void* context, uint32_t event) {
furi_assert(context); furi_assert(context);
Nfc* nfc = (Nfc*)context; Nfc* nfc = context;
return scene_manager_handle_custom_event(nfc->scene_manager, event); return scene_manager_handle_custom_event(nfc->scene_manager, event);
} }
bool nfc_back_event_callback(void* context) { bool nfc_back_event_callback(void* context) {
furi_assert(context); furi_assert(context);
Nfc* nfc = (Nfc*)context; Nfc* nfc = context;
return scene_manager_handle_back_event(nfc->scene_manager); return scene_manager_handle_back_event(nfc->scene_manager);
} }
void nfc_tick_event_callback(void* context) { void nfc_tick_event_callback(void* context) {
furi_assert(context); furi_assert(context);
Nfc* nfc = (Nfc*)context; Nfc* nfc = context;
scene_manager_handle_tick_event(nfc->scene_manager); scene_manager_handle_tick_event(nfc->scene_manager);
} }

View File

@@ -16,40 +16,35 @@ static void nfc_cli_print_usage() {
} }
} }
void nfc_cli_detect(Cli* cli, string_t args) { static void nfc_cli_detect(Cli* cli, string_t args) {
// Check if nfc worker is not busy // Check if nfc worker is not busy
if(furi_hal_nfc_is_busy()) { if(furi_hal_nfc_is_busy()) {
printf("Nfc is busy\r\n"); printf("Nfc is busy\r\n");
return; return;
} }
rfalNfcDevice* dev_list;
uint8_t dev_cnt = 0; FuriHalNfcDevData dev_data = {};
bool cmd_exit = false; bool cmd_exit = false;
furi_hal_nfc_exit_sleep(); furi_hal_nfc_exit_sleep();
printf("Detecting nfc...\r\nPress Ctrl+C to abort\r\n"); printf("Detecting nfc...\r\nPress Ctrl+C to abort\r\n");
while(!cmd_exit) { while(!cmd_exit) {
cmd_exit |= cli_cmd_interrupt_received(cli); cmd_exit |= cli_cmd_interrupt_received(cli);
cmd_exit |= furi_hal_nfc_detect(&dev_list, &dev_cnt, 400, true); if(furi_hal_nfc_detect(&dev_data, 400)) {
if(dev_cnt > 0) { printf("found: %s ", nfc_get_dev_type(dev_data.type));
printf("Found %d devices\r\n", dev_cnt); printf("UID length: %d, UID:", dev_data.uid_len);
for(uint8_t i = 0; i < dev_cnt; i++) { for(size_t i = 0; i < dev_data.uid_len; i++) {
printf("%d found: %s ", i + 1, nfc_get_rfal_type(dev_list[i].type)); printf("%02X", dev_data.uid[i]);
if(dev_list[i].type == RFAL_NFC_LISTEN_TYPE_NFCA) {
printf("type: %s, ", nfc_get_nfca_type(dev_list[i].dev.nfca.type));
}
printf("UID length: %d, UID:", dev_list[i].nfcidLen);
for(uint8_t j = 0; j < dev_list[i].nfcidLen; j++) {
printf("%02X", dev_list[i].nfcid[j]);
}
printf("\r\n");
} }
printf("\r\n");
break;
} }
furi_hal_nfc_sleep();
osDelay(50); osDelay(50);
} }
furi_hal_nfc_deactivate(); furi_hal_nfc_sleep();
} }
void nfc_cli_emulate(Cli* cli, string_t args) { static void nfc_cli_emulate(Cli* cli, string_t args) {
// Check if nfc worker is not busy // Check if nfc worker is not busy
if(furi_hal_nfc_is_busy()) { if(furi_hal_nfc_is_busy()) {
printf("Nfc is busy\r\n"); printf("Nfc is busy\r\n");
@@ -60,26 +55,25 @@ void nfc_cli_emulate(Cli* cli, string_t args) {
printf("Emulating NFC-A Type: T2T UID: 36 9C E7 B1 0A C1 34 SAK: 00 ATQA: 00/44\r\n"); printf("Emulating NFC-A Type: T2T UID: 36 9C E7 B1 0A C1 34 SAK: 00 ATQA: 00/44\r\n");
printf("Press Ctrl+C to abort\r\n"); printf("Press Ctrl+C to abort\r\n");
NfcDeviceCommonData params = { FuriHalNfcDevData params = {
.uid = {0x36, 0x9C, 0xe7, 0xb1, 0x0A, 0xC1, 0x34}, .uid = {0x36, 0x9C, 0xe7, 0xb1, 0x0A, 0xC1, 0x34},
.uid_len = 7, .uid_len = 7,
.atqa = {0x44, 0x00}, .atqa = {0x44, 0x00},
.sak = 0x00, .sak = 0x00,
.device = NfcDeviceNfca, .type = FuriHalNfcTypeA,
.protocol = NfcDeviceProtocolMifareUl,
}; };
while(!cli_cmd_interrupt_received(cli)) { while(!cli_cmd_interrupt_received(cli)) {
if(furi_hal_nfc_listen(params.uid, params.uid_len, params.atqa, params.sak, false, 100)) { if(furi_hal_nfc_listen(params.uid, params.uid_len, params.atqa, params.sak, false, 100)) {
printf("Reader detected\r\n"); printf("Reader detected\r\n");
furi_hal_nfc_deactivate(); furi_hal_nfc_sleep();
} }
osDelay(50); osDelay(50);
} }
furi_hal_nfc_deactivate(); furi_hal_nfc_sleep();
} }
void nfc_cli_field(Cli* cli, string_t args) { static void nfc_cli_field(Cli* cli, string_t args) {
// Check if nfc worker is not busy // Check if nfc worker is not busy
if(furi_hal_nfc_is_busy()) { if(furi_hal_nfc_is_busy()) {
printf("Nfc is busy\r\n"); printf("Nfc is busy\r\n");
@@ -97,7 +91,7 @@ void nfc_cli_field(Cli* cli, string_t args) {
} }
furi_hal_nfc_field_off(); furi_hal_nfc_field_off();
furi_hal_nfc_deactivate(); furi_hal_nfc_sleep();
} }
static void nfc_cli(Cli* cli, string_t args, void* context) { static void nfc_cli(Cli* cli, string_t args, void* context) {

View File

@@ -41,31 +41,31 @@ static void nfc_device_prepare_format_string(NfcDevice* dev, string_t format_str
static bool nfc_device_parse_format_string(NfcDevice* dev, string_t format_string) { static bool nfc_device_parse_format_string(NfcDevice* dev, string_t format_string) {
if(string_start_with_str_p(format_string, "UID")) { if(string_start_with_str_p(format_string, "UID")) {
dev->format = NfcDeviceSaveFormatUid; dev->format = NfcDeviceSaveFormatUid;
dev->dev_data.nfc_data.protocol = NfcDeviceProtocolUnknown; dev->dev_data.protocol = NfcDeviceProtocolUnknown;
return true; return true;
} }
if(string_start_with_str_p(format_string, "Bank card")) { if(string_start_with_str_p(format_string, "Bank card")) {
dev->format = NfcDeviceSaveFormatBankCard; dev->format = NfcDeviceSaveFormatBankCard;
dev->dev_data.nfc_data.protocol = NfcDeviceProtocolEMV; dev->dev_data.protocol = NfcDeviceProtocolEMV;
return true; return true;
} }
// Check Mifare Ultralight types // Check Mifare Ultralight types
for(MfUltralightType type = MfUltralightTypeUnknown; type < MfUltralightTypeNum; type++) { for(MfUltralightType type = MfUltralightTypeUnknown; type < MfUltralightTypeNum; type++) {
if(string_start_with_str_p(format_string, nfc_mf_ul_type(type, true))) { if(string_equal_str_p(format_string, nfc_mf_ul_type(type, true))) {
dev->format = NfcDeviceSaveFormatMifareUl; dev->format = NfcDeviceSaveFormatMifareUl;
dev->dev_data.nfc_data.protocol = NfcDeviceProtocolMifareUl; dev->dev_data.protocol = NfcDeviceProtocolMifareUl;
dev->dev_data.mf_ul_data.type = type; dev->dev_data.mf_ul_data.type = type;
return true; return true;
} }
} }
if(string_start_with_str_p(format_string, "Mifare Classic")) { if(string_start_with_str_p(format_string, "Mifare Classic")) {
dev->format = NfcDeviceSaveFormatMifareClassic; dev->format = NfcDeviceSaveFormatMifareClassic;
dev->dev_data.nfc_data.protocol = NfcDeviceProtocolMifareClassic; dev->dev_data.protocol = NfcDeviceProtocolMifareClassic;
return true; return true;
} }
if(string_start_with_str_p(format_string, "Mifare DESFire")) { if(string_start_with_str_p(format_string, "Mifare DESFire")) {
dev->format = NfcDeviceSaveFormatMifareDesfire; dev->format = NfcDeviceSaveFormatMifareDesfire;
dev->dev_data.nfc_data.protocol = NfcDeviceProtocolMifareDesfire; dev->dev_data.protocol = NfcDeviceProtocolMifareDesfire;
return true; return true;
} }
return false; return false;
@@ -73,7 +73,7 @@ static bool nfc_device_parse_format_string(NfcDevice* dev, string_t format_strin
static bool nfc_device_save_mifare_ul_data(FlipperFormat* file, NfcDevice* dev) { static bool nfc_device_save_mifare_ul_data(FlipperFormat* file, NfcDevice* dev) {
bool saved = false; bool saved = false;
MifareUlData* data = &dev->dev_data.mf_ul_data; MfUltralightData* data = &dev->dev_data.mf_ul_data;
string_t temp_str; string_t temp_str;
string_init(temp_str); string_init(temp_str);
@@ -122,7 +122,7 @@ static bool nfc_device_save_mifare_ul_data(FlipperFormat* file, NfcDevice* dev)
bool nfc_device_load_mifare_ul_data(FlipperFormat* file, NfcDevice* dev) { bool nfc_device_load_mifare_ul_data(FlipperFormat* file, NfcDevice* dev) {
bool parsed = false; bool parsed = false;
MifareUlData* data = &dev->dev_data.mf_ul_data; MfUltralightData* data = &dev->dev_data.mf_ul_data;
string_t temp_str; string_t temp_str;
string_init(temp_str); string_init(temp_str);
@@ -548,7 +548,7 @@ bool nfc_device_load_mifare_df_data(FlipperFormat* file, NfcDevice* dev) {
static bool nfc_device_save_bank_card_data(FlipperFormat* file, NfcDevice* dev) { static bool nfc_device_save_bank_card_data(FlipperFormat* file, NfcDevice* dev) {
bool saved = false; bool saved = false;
NfcEmvData* data = &dev->dev_data.emv_data; EmvData* data = &dev->dev_data.emv_data;
uint32_t data_temp = 0; uint32_t data_temp = 0;
do { do {
@@ -577,8 +577,8 @@ static bool nfc_device_save_bank_card_data(FlipperFormat* file, NfcDevice* dev)
bool nfc_device_load_bank_card_data(FlipperFormat* file, NfcDevice* dev) { bool nfc_device_load_bank_card_data(FlipperFormat* file, NfcDevice* dev) {
bool parsed = false; bool parsed = false;
NfcEmvData* data = &dev->dev_data.emv_data; EmvData* data = &dev->dev_data.emv_data;
memset(data, 0, sizeof(NfcEmvData)); memset(data, 0, sizeof(EmvData));
uint32_t data_cnt = 0; uint32_t data_cnt = 0;
string_t temp_str; string_t temp_str;
string_init(temp_str); string_init(temp_str);
@@ -700,7 +700,7 @@ static bool nfc_device_save_file(
bool saved = false; bool saved = false;
FlipperFormat* file = flipper_format_file_alloc(dev->storage); FlipperFormat* file = flipper_format_file_alloc(dev->storage);
NfcDeviceCommonData* data = &dev->dev_data.nfc_data; FuriHalNfcDevData* data = &dev->dev_data.nfc_data;
string_t temp_str; string_t temp_str;
string_init(temp_str); string_init(temp_str);
@@ -758,7 +758,7 @@ bool nfc_device_save_shadow(NfcDevice* dev, const char* dev_name) {
static bool nfc_device_load_data(NfcDevice* dev, string_t path) { static bool nfc_device_load_data(NfcDevice* dev, string_t path) {
bool parsed = false; bool parsed = false;
FlipperFormat* file = flipper_format_file_alloc(dev->storage); FlipperFormat* file = flipper_format_file_alloc(dev->storage);
NfcDeviceCommonData* data = &dev->dev_data.nfc_data; FuriHalNfcDevData* data = &dev->dev_data.nfc_data;
uint32_t data_cnt = 0; uint32_t data_cnt = 0;
string_t temp_str; string_t temp_str;
string_init(temp_str); string_init(temp_str);
@@ -789,6 +789,7 @@ static bool nfc_device_load_data(NfcDevice* dev, string_t path) {
if(!nfc_device_parse_format_string(dev, temp_str)) break; if(!nfc_device_parse_format_string(dev, temp_str)) break;
// Read and parse UID, ATQA and SAK // Read and parse UID, ATQA and SAK
if(!flipper_format_get_value_count(file, "UID", &data_cnt)) break; if(!flipper_format_get_value_count(file, "UID", &data_cnt)) break;
if(!(data_cnt == 4 || data_cnt == 7)) break;
data->uid_len = data_cnt; data->uid_len = data_cnt;
if(!flipper_format_read_hex(file, "UID", data->uid, data->uid_len)) break; if(!flipper_format_read_hex(file, "UID", data->uid, data->uid_len)) break;
if(!flipper_format_read_hex(file, "ATQA", data->atqa, 2)) break; if(!flipper_format_read_hex(file, "ATQA", data->atqa, 2)) break;
@@ -863,7 +864,7 @@ bool nfc_file_select(NfcDevice* dev) {
} }
void nfc_device_data_clear(NfcDeviceData* dev_data) { void nfc_device_data_clear(NfcDeviceData* dev_data) {
if(dev_data->nfc_data.protocol == NfcDeviceProtocolMifareDesfire) { if(dev_data->protocol == NfcDeviceProtocolMifareDesfire) {
mf_df_clear(&dev_data->mf_df_data); mf_df_clear(&dev_data->mf_df_data);
} }
} }

View File

@@ -5,6 +5,8 @@
#include <storage/storage.h> #include <storage/storage.h>
#include <dialogs/dialogs.h> #include <dialogs/dialogs.h>
#include <furi_hal_nfc.h>
#include <lib/nfc_protocols/emv.h>
#include <lib/nfc_protocols/mifare_ultralight.h> #include <lib/nfc_protocols/mifare_ultralight.h>
#include <lib/nfc_protocols/mifare_classic.h> #include <lib/nfc_protocols/mifare_classic.h>
#include <lib/nfc_protocols/mifare_desfire.h> #include <lib/nfc_protocols/mifare_desfire.h>
@@ -17,13 +19,6 @@
#define NFC_APP_EXTENSION ".nfc" #define NFC_APP_EXTENSION ".nfc"
#define NFC_APP_SHADOW_EXTENSION ".shd" #define NFC_APP_SHADOW_EXTENSION ".shd"
typedef enum {
NfcDeviceNfca,
NfcDeviceNfcb,
NfcDeviceNfcf,
NfcDeviceNfcv,
} NfcDeviceType;
typedef enum { typedef enum {
NfcDeviceProtocolUnknown, NfcDeviceProtocolUnknown,
NfcDeviceProtocolEMV, NfcDeviceProtocolEMV,
@@ -40,38 +35,18 @@ typedef enum {
NfcDeviceSaveFormatMifareDesfire, NfcDeviceSaveFormatMifareDesfire,
} NfcDeviceSaveFormat; } NfcDeviceSaveFormat;
typedef struct {
uint8_t uid_len;
uint8_t uid[10];
uint8_t atqa[2];
uint8_t sak;
NfcDeviceType device;
NfcProtocol protocol;
} NfcDeviceCommonData;
typedef struct {
char name[32];
uint8_t aid[16];
uint16_t aid_len;
uint8_t number[10];
uint8_t number_len;
uint8_t exp_mon;
uint8_t exp_year;
uint16_t country_code;
uint16_t currency_code;
} NfcEmvData;
typedef struct { typedef struct {
uint8_t data[NFC_READER_DATA_MAX_SIZE]; uint8_t data[NFC_READER_DATA_MAX_SIZE];
uint16_t size; uint16_t size;
} NfcReaderRequestData; } NfcReaderRequestData;
typedef struct { typedef struct {
NfcDeviceCommonData nfc_data; FuriHalNfcDevData nfc_data;
NfcProtocol protocol;
NfcReaderRequestData reader_data; NfcReaderRequestData reader_data;
union { union {
NfcEmvData emv_data; EmvData emv_data;
MifareUlData mf_ul_data; MfUltralightData mf_ul_data;
MfClassicData mf_classic_data; MfClassicData mf_classic_data;
MifareDesfireData mf_df_data; MifareDesfireData mf_df_data;
}; };

View File

@@ -40,7 +40,7 @@ struct Nfc {
NotificationApp* notifications; NotificationApp* notifications;
SceneManager* scene_manager; SceneManager* scene_manager;
NfcDevice* dev; NfcDevice* dev;
NfcDeviceCommonData dev_edit_data; FuriHalNfcDevData dev_edit_data;
char text_store[NFC_TEXT_STORE_SIZE + 1]; char text_store[NFC_TEXT_STORE_SIZE + 1];
string_t text_box_store; string_t text_box_store;

View File

@@ -1,48 +1,14 @@
#include "nfc_types.h" #include "nfc_types.h"
const char* nfc_get_rfal_type(rfalNfcDevType type) { const char* nfc_get_dev_type(FuriHalNfcType type) {
if(type == RFAL_NFC_LISTEN_TYPE_NFCA) { if(type == FuriHalNfcTypeA) {
return "NFC-A"; return "NFC-A";
} else if(type == RFAL_NFC_LISTEN_TYPE_NFCB) { } else if(type == FuriHalNfcTypeB) {
return "NFC-B"; return "NFC-B";
} else if(type == RFAL_NFC_LISTEN_TYPE_NFCF) { } else if(type == FuriHalNfcTypeF) {
return "NFC-F"; return "NFC-F";
} else if(type == RFAL_NFC_LISTEN_TYPE_NFCV) { } else if(type == FuriHalNfcTypeV) {
return "NFC-V"; return "NFC-V";
} else if(type == RFAL_NFC_LISTEN_TYPE_ST25TB) {
return "NFC-ST25TB";
} else if(type == RFAL_NFC_LISTEN_TYPE_AP2P) {
return "NFC-AP2P";
} else {
return "Unknown";
}
}
const char* nfc_get_dev_type(NfcDeviceType type) {
if(type == NfcDeviceNfca) {
return "NFC-A";
} else if(type == NfcDeviceNfcb) {
return "NFC-B";
} else if(type == NfcDeviceNfcf) {
return "NFC-F";
} else if(type == NfcDeviceNfcv) {
return "NFC-V";
} else {
return "Unknown";
}
}
const char* nfc_get_nfca_type(rfalNfcaListenDeviceType type) {
if(type == RFAL_NFCA_T1T) {
return "T1T";
} else if(type == RFAL_NFCA_T2T) {
return "T2T";
} else if(type == RFAL_NFCA_T4T) {
return "T4T";
} else if(type == RFAL_NFCA_NFCDEP) {
return "NFCDEP";
} else if(type == RFAL_NFCA_T4T_NFCDEP) {
return "T4T_NFCDEP";
} else { } else {
return "Unknown"; return "Unknown";
} }

View File

@@ -1,16 +1,8 @@
#pragma once #pragma once
#include "st_errno.h" #include "nfc_device.h"
#include "rfal_nfc.h"
#include <gui/view_dispatcher.h> const char* nfc_get_dev_type(FuriHalNfcType type);
#include "nfc_worker.h"
const char* nfc_get_rfal_type(rfalNfcDevType type);
const char* nfc_get_dev_type(NfcDeviceType type);
const char* nfc_get_nfca_type(rfalNfcaListenDeviceType type);
const char* nfc_guess_protocol(NfcProtocol protocol); const char* nfc_guess_protocol(NfcProtocol protocol);

View File

@@ -2,7 +2,8 @@
#include <furi_hal.h> #include <furi_hal.h>
#include <lib/nfc_protocols/nfc_util.h> #include <lib/nfc_protocols/nfc_util.h>
#include <lib/nfc_protocols/emv_decoder.h> #include <lib/nfc_protocols/emv.h>
#include <lib/nfc_protocols/mifare_common.h>
#include <lib/nfc_protocols/mifare_ultralight.h> #include <lib/nfc_protocols/mifare_ultralight.h>
#include <lib/nfc_protocols/mifare_classic.h> #include <lib/nfc_protocols/mifare_classic.h>
#include <lib/nfc_protocols/mifare_desfire.h> #include <lib/nfc_protocols/mifare_desfire.h>
@@ -94,22 +95,20 @@ int32_t nfc_worker_task(void* context) {
nfc_worker_emulate(nfc_worker); nfc_worker_emulate(nfc_worker);
} else if(nfc_worker->state == NfcWorkerStateReadEMVApp) { } else if(nfc_worker->state == NfcWorkerStateReadEMVApp) {
nfc_worker_read_emv_app(nfc_worker); nfc_worker_read_emv_app(nfc_worker);
} else if(nfc_worker->state == NfcWorkerStateReadEMV) { } else if(nfc_worker->state == NfcWorkerStateReadEMVData) {
nfc_worker_read_emv(nfc_worker); nfc_worker_read_emv(nfc_worker);
} else if(nfc_worker->state == NfcWorkerStateEmulateApdu) { } else if(nfc_worker->state == NfcWorkerStateEmulateApdu) {
nfc_worker_emulate_apdu(nfc_worker); nfc_worker_emulate_apdu(nfc_worker);
} else if(nfc_worker->state == NfcWorkerStateReadMifareUl) { } else if(nfc_worker->state == NfcWorkerStateReadMifareUltralight) {
nfc_worker_read_mifare_ul(nfc_worker); nfc_worker_read_mifare_ultralight(nfc_worker);
} else if(nfc_worker->state == NfcWorkerStateEmulateMifareUl) { } else if(nfc_worker->state == NfcWorkerStateEmulateMifareUltralight) {
nfc_worker_emulate_mifare_ul(nfc_worker); nfc_worker_emulate_mifare_ul(nfc_worker);
} else if(nfc_worker->state == NfcWorkerStateReadMifareClassic) { } else if(nfc_worker->state == NfcWorkerStateReadMifareClassic) {
nfc_worker_mifare_classic_dict_attack(nfc_worker); nfc_worker_mifare_classic_dict_attack(nfc_worker);
} else if(nfc_worker->state == NfcWorkerStateReadMifareDesfire) { } else if(nfc_worker->state == NfcWorkerStateReadMifareDesfire) {
nfc_worker_read_mifare_desfire(nfc_worker); nfc_worker_read_mifare_desfire(nfc_worker);
} else if(nfc_worker->state == NfcWorkerStateField) {
nfc_worker_field(nfc_worker);
} }
furi_hal_nfc_deactivate(); furi_hal_nfc_sleep();
nfc_worker_change_state(nfc_worker, NfcWorkerStateReady); nfc_worker_change_state(nfc_worker, NfcWorkerStateReady);
furi_hal_power_insomnia_exit(); furi_hal_power_insomnia_exit();
@@ -117,579 +116,225 @@ int32_t nfc_worker_task(void* context) {
} }
void nfc_worker_detect(NfcWorker* nfc_worker) { void nfc_worker_detect(NfcWorker* nfc_worker) {
rfalNfcDevice* dev_list;
rfalNfcDevice* dev;
uint8_t dev_cnt;
nfc_device_data_clear(nfc_worker->dev_data); nfc_device_data_clear(nfc_worker->dev_data);
NfcDeviceCommonData* result = &nfc_worker->dev_data->nfc_data; NfcDeviceData* dev_data = nfc_worker->dev_data;
FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data;
while(nfc_worker->state == NfcWorkerStateDetect) { while(nfc_worker->state == NfcWorkerStateDetect) {
if(furi_hal_nfc_detect(&dev_list, &dev_cnt, 1000, true)) { if(furi_hal_nfc_detect(nfc_data, 1000)) {
// Process first found device // Process first found device
dev = &dev_list[0]; if(nfc_data->type == FuriHalNfcTypeA) {
result->uid_len = dev->nfcidLen; if(mf_ul_check_card_type(nfc_data->atqa[0], nfc_data->atqa[1], nfc_data->sak)) {
memcpy(result->uid, dev->nfcid, dev->nfcidLen); dev_data->protocol = NfcDeviceProtocolMifareUl;
if(dev->type == RFAL_NFC_LISTEN_TYPE_NFCA) {
result->device = NfcDeviceNfca;
result->atqa[0] = dev->dev.nfca.sensRes.anticollisionInfo;
result->atqa[1] = dev->dev.nfca.sensRes.platformInfo;
result->sak = dev->dev.nfca.selRes.sak;
if(mf_ul_check_card_type(
dev->dev.nfca.sensRes.anticollisionInfo,
dev->dev.nfca.sensRes.platformInfo,
dev->dev.nfca.selRes.sak)) {
result->protocol = NfcDeviceProtocolMifareUl;
} else if(mf_classic_check_card_type( } else if(mf_classic_check_card_type(
dev->dev.nfca.sensRes.anticollisionInfo, nfc_data->atqa[0], nfc_data->atqa[1], nfc_data->sak)) {
dev->dev.nfca.sensRes.platformInfo, dev_data->protocol = NfcDeviceProtocolMifareClassic;
dev->dev.nfca.selRes.sak)) {
result->protocol = NfcDeviceProtocolMifareClassic;
} else if(mf_df_check_card_type( } else if(mf_df_check_card_type(
dev->dev.nfca.sensRes.anticollisionInfo, nfc_data->atqa[0], nfc_data->atqa[1], nfc_data->sak)) {
dev->dev.nfca.sensRes.platformInfo, dev_data->protocol = NfcDeviceProtocolMifareDesfire;
dev->dev.nfca.selRes.sak)) { } else if(nfc_data->interface == FuriHalNfcInterfaceIsoDep) {
result->protocol = NfcDeviceProtocolMifareDesfire; dev_data->protocol = NfcDeviceProtocolEMV;
} else if(dev->rfInterface == RFAL_NFC_INTERFACE_ISODEP) {
result->protocol = NfcDeviceProtocolEMV;
} else { } else {
result->protocol = NfcDeviceProtocolUnknown; dev_data->protocol = NfcDeviceProtocolUnknown;
} }
} else if(dev->type == RFAL_NFC_LISTEN_TYPE_NFCB) {
result->device = NfcDeviceNfcb;
} else if(dev->type == RFAL_NFC_LISTEN_TYPE_NFCF) {
result->device = NfcDeviceNfcf;
} else if(dev->type == RFAL_NFC_LISTEN_TYPE_NFCV) {
result->device = NfcDeviceNfcv;
} }
// Notify caller and exit // Notify caller and exit
if(nfc_worker->callback) { if(nfc_worker->callback) {
nfc_worker->callback(NfcWorkerEventSuccess, nfc_worker->context); nfc_worker->callback(NfcWorkerEventSuccess, nfc_worker->context);
} }
break; break;
} }
furi_hal_nfc_sleep();
osDelay(100); osDelay(100);
} }
} }
bool nfc_worker_emulate_uid_callback(
uint8_t* buff_rx,
uint16_t buff_rx_len,
uint8_t* buff_tx,
uint16_t* buff_tx_len,
uint32_t* data_type,
void* context) {
furi_assert(context);
NfcWorker* nfc_worker = context;
NfcReaderRequestData* reader_data = &nfc_worker->dev_data->reader_data;
reader_data->size = buff_rx_len / 8;
if(reader_data->size > 0) {
memcpy(reader_data->data, buff_rx, reader_data->size);
if(nfc_worker->callback) {
nfc_worker->callback(NfcWorkerEventSuccess, nfc_worker->context);
}
}
return true;
}
void nfc_worker_emulate(NfcWorker* nfc_worker) { void nfc_worker_emulate(NfcWorker* nfc_worker) {
NfcDeviceCommonData* data = &nfc_worker->dev_data->nfc_data; FuriHalNfcTxRxContext tx_rx = {};
FuriHalNfcDevData* data = &nfc_worker->dev_data->nfc_data;
NfcReaderRequestData* reader_data = &nfc_worker->dev_data->reader_data;
while(nfc_worker->state == NfcWorkerStateEmulate) { while(nfc_worker->state == NfcWorkerStateEmulate) {
furi_hal_nfc_emulate_nfca( if(furi_hal_nfc_listen(data->uid, data->uid_len, data->atqa, data->sak, true, 100)) {
data->uid, if(furi_hal_nfc_tx_rx(&tx_rx, 100)) {
data->uid_len, reader_data->size = tx_rx.rx_bits / 8;
data->atqa, if(reader_data->size > 0) {
data->sak, memcpy(reader_data->data, tx_rx.rx_data, reader_data->size);
nfc_worker_emulate_uid_callback, if(nfc_worker->callback) {
nfc_worker, nfc_worker->callback(NfcWorkerEventSuccess, nfc_worker->context);
1000); }
}
} else {
FURI_LOG_E(TAG, "Failed to get reader commands");
}
}
} }
} }
void nfc_worker_read_emv_app(NfcWorker* nfc_worker) { void nfc_worker_read_emv_app(NfcWorker* nfc_worker) {
ReturnCode err; FuriHalNfcTxRxContext tx_rx = {};
rfalNfcDevice* dev_list;
EmvApplication emv_app = {}; 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; NfcDeviceData* result = nfc_worker->dev_data;
FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data;
nfc_device_data_clear(result); nfc_device_data_clear(result);
while(nfc_worker->state == NfcWorkerStateReadEMVApp) { while(nfc_worker->state == NfcWorkerStateReadEMVApp) {
memset(&emv_app, 0, sizeof(emv_app)); if(furi_hal_nfc_detect(nfc_data, 1000)) {
if(furi_hal_nfc_detect(&dev_list, &dev_cnt, 1000, false)) {
// Card was found. Check that it supports EMV // Card was found. Check that it supports EMV
if(dev_list[0].rfInterface == RFAL_NFC_INTERFACE_ISODEP) { if(nfc_data->interface == FuriHalNfcInterfaceIsoDep) {
result->nfc_data.uid_len = dev_list[0].dev.nfca.nfcId1Len; result->protocol = NfcDeviceProtocolEMV;
result->nfc_data.atqa[0] = dev_list[0].dev.nfca.sensRes.anticollisionInfo; if(emv_search_application(&tx_rx, &emv_app)) {
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_D(TAG, "Send select PPSE command");
tx_len = emv_prepare_select_ppse(tx_buff);
err = furi_hal_nfc_data_exchange(tx_buff, tx_len, &rx_buff, &rx_len, false);
if(err != ERR_NONE) {
FURI_LOG_D(TAG, "Error during selection PPSE request: %d", err);
furi_hal_nfc_deactivate();
continue;
}
FURI_LOG_D(TAG, "Select PPSE response received. Start parsing response");
if(emv_decode_ppse_response(rx_buff, *rx_len, &emv_app)) {
FURI_LOG_D(TAG, "Select PPSE responce parced");
// Notify caller and exit // Notify caller and exit
result->emv_data.aid_len = emv_app.aid_len; result->emv_data.aid_len = emv_app.aid_len;
memcpy(result->emv_data.aid, emv_app.aid, emv_app.aid_len); memcpy(result->emv_data.aid, emv_app.aid, emv_app.aid_len);
if(nfc_worker->callback) { if(nfc_worker->callback) {
nfc_worker->callback(NfcWorkerEventSuccess, nfc_worker->context); nfc_worker->callback(NfcWorkerEventSuccess, nfc_worker->context);
} }
break;
} else {
FURI_LOG_D(TAG, "Can't find pay application");
furi_hal_nfc_deactivate();
continue;
} }
} else { } else {
// Can't find EMV card
FURI_LOG_W(TAG, "Card doesn't support EMV"); FURI_LOG_W(TAG, "Card doesn't support EMV");
furi_hal_nfc_deactivate();
} }
} else { } else {
// Can't find EMV card
FURI_LOG_D(TAG, "Can't find any cards"); FURI_LOG_D(TAG, "Can't find any cards");
furi_hal_nfc_deactivate();
} }
furi_hal_nfc_sleep();
osDelay(20); osDelay(20);
} }
} }
void nfc_worker_read_emv(NfcWorker* nfc_worker) { void nfc_worker_read_emv(NfcWorker* nfc_worker) {
ReturnCode err; FuriHalNfcTxRxContext tx_rx = {};
rfalNfcDevice* dev_list;
EmvApplication emv_app = {}; 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; NfcDeviceData* result = nfc_worker->dev_data;
FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data;
nfc_device_data_clear(result); nfc_device_data_clear(result);
while(nfc_worker->state == NfcWorkerStateReadEMV) { while(nfc_worker->state == NfcWorkerStateReadEMVData) {
memset(&emv_app, 0, sizeof(emv_app)); if(furi_hal_nfc_detect(nfc_data, 1000)) {
if(furi_hal_nfc_detect(&dev_list, &dev_cnt, 1000, false)) {
// Card was found. Check that it supports EMV // Card was found. Check that it supports EMV
if(dev_list[0].rfInterface == RFAL_NFC_INTERFACE_ISODEP) { if(nfc_data->interface == FuriHalNfcInterfaceIsoDep) {
result->nfc_data.uid_len = dev_list[0].dev.nfca.nfcId1Len; result->protocol = NfcDeviceProtocolEMV;
result->nfc_data.atqa[0] = dev_list[0].dev.nfca.sensRes.anticollisionInfo; if(emv_read_bank_card(&tx_rx, &emv_app)) {
result->nfc_data.atqa[1] = dev_list[0].dev.nfca.sensRes.platformInfo; result->emv_data.number_len = emv_app.card_number_len;
result->nfc_data.sak = dev_list[0].dev.nfca.selRes.sak; memcpy(
memcpy( result->emv_data.number, emv_app.card_number, result->emv_data.number_len);
result->nfc_data.uid, dev_list[0].dev.nfca.nfcId1, result->nfc_data.uid_len);
result->nfc_data.protocol = NfcDeviceProtocolEMV;
FURI_LOG_D(TAG, "Send select PPSE command");
tx_len = emv_prepare_select_ppse(tx_buff);
err = furi_hal_nfc_data_exchange(tx_buff, tx_len, &rx_buff, &rx_len, false);
if(err != ERR_NONE) {
FURI_LOG_D(TAG, "Error during selection PPSE request: %d", err);
furi_hal_nfc_deactivate();
continue;
}
FURI_LOG_D(TAG, "Select PPSE response received. Start parsing response");
if(emv_decode_ppse_response(rx_buff, *rx_len, &emv_app)) {
FURI_LOG_D(TAG, "Select PPSE responce parced");
result->emv_data.aid_len = emv_app.aid_len; result->emv_data.aid_len = emv_app.aid_len;
memcpy(result->emv_data.aid, emv_app.aid, emv_app.aid_len); memcpy(result->emv_data.aid, emv_app.aid, emv_app.aid_len);
} else { if(emv_app.name_found) {
FURI_LOG_D(TAG, "Can't find pay application"); memcpy(result->emv_data.name, emv_app.name, sizeof(emv_app.name));
furi_hal_nfc_deactivate(); }
continue; if(emv_app.exp_month) {
result->emv_data.exp_mon = emv_app.exp_month;
result->emv_data.exp_year = emv_app.exp_year;
}
if(emv_app.country_code) {
result->emv_data.country_code = emv_app.country_code;
}
if(emv_app.currency_code) {
result->emv_data.currency_code = emv_app.currency_code;
}
// Notify caller and exit
if(nfc_worker->callback) {
nfc_worker->callback(NfcWorkerEventSuccess, nfc_worker->context);
}
break;
} }
FURI_LOG_D(TAG, "Starting application ..."); } else {
tx_len = emv_prepare_select_app(tx_buff, &emv_app); FURI_LOG_W(TAG, "Card doesn't support EMV");
err = furi_hal_nfc_data_exchange(tx_buff, tx_len, &rx_buff, &rx_len, false); }
if(err != ERR_NONE) { } else {
FURI_LOG_D(TAG, "Error during application selection request: %d", err); FURI_LOG_D(TAG, "Can't find any cards");
furi_hal_nfc_deactivate(); }
continue; furi_hal_nfc_sleep();
} osDelay(20);
FURI_LOG_D(TAG, "Select application response received. Start parsing response"); }
if(emv_decode_select_app_response(rx_buff, *rx_len, &emv_app)) { }
FURI_LOG_D(TAG, "Card name: %s", emv_app.name);
memcpy(result->emv_data.name, emv_app.name, sizeof(emv_app.name)); void nfc_worker_emulate_apdu(NfcWorker* nfc_worker) {
} else if(emv_app.pdol.size > 0) { FuriHalNfcTxRxContext tx_rx = {};
FURI_LOG_D(TAG, "Can't find card name, but PDOL is present."); FuriHalNfcDevData params = {
} else { .uid = {0xCF, 0x72, 0xd4, 0x40},
FURI_LOG_D(TAG, "Can't find card name or PDOL"); .uid_len = 4,
furi_hal_nfc_deactivate(); .atqa = {0x00, 0x04},
continue; .sak = 0x20,
} .type = FuriHalNfcTypeA,
FURI_LOG_D(TAG, "Starting Get Processing Options command ..."); };
tx_len = emv_prepare_get_proc_opt(tx_buff, &emv_app);
err = furi_hal_nfc_data_exchange(tx_buff, tx_len, &rx_buff, &rx_len, false); while(nfc_worker->state == NfcWorkerStateEmulateApdu) {
if(err != ERR_NONE) { if(furi_hal_nfc_listen(params.uid, params.uid_len, params.atqa, params.sak, false, 300)) {
FURI_LOG_D(TAG, "Error during Get Processing Options command: %d", err); FURI_LOG_D(TAG, "POS terminal detected");
furi_hal_nfc_deactivate(); if(emv_card_emulation(&tx_rx)) {
continue; FURI_LOG_D(TAG, "EMV card emulated");
} }
if(emv_decode_get_proc_opt(rx_buff, *rx_len, &emv_app)) { } else {
FURI_LOG_D(TAG, "Card number parsed"); FURI_LOG_D(TAG, "Can't find reader");
result->emv_data.number_len = emv_app.card_number_len; }
memcpy(result->emv_data.number, emv_app.card_number, emv_app.card_number_len); furi_hal_nfc_sleep();
osDelay(20);
}
}
void nfc_worker_read_mifare_ultralight(NfcWorker* nfc_worker) {
FuriHalNfcTxRxContext tx_rx = {};
MfUltralightReader reader = {};
MfUltralightData data = {};
NfcDeviceData* result = nfc_worker->dev_data;
FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data;
while(nfc_worker->state == NfcWorkerStateReadMifareUltralight) {
if(furi_hal_nfc_detect(nfc_data, 300)) {
if(nfc_data->type == FuriHalNfcTypeA &&
mf_ul_check_card_type(nfc_data->atqa[0], nfc_data->atqa[1], nfc_data->sak)) {
FURI_LOG_D(TAG, "Found Mifare Ultralight tag. Start reading");
if(mf_ul_read_card(&tx_rx, &reader, &data)) {
result->protocol = NfcDeviceProtocolMifareUl;
result->mf_ul_data = data;
// Notify caller and exit // Notify caller and exit
if(nfc_worker->callback) { if(nfc_worker->callback) {
nfc_worker->callback(NfcWorkerEventSuccess, nfc_worker->context); nfc_worker->callback(NfcWorkerEventSuccess, nfc_worker->context);
} }
break; break;
} else { } else {
// Mastercard doesn't give PAN / card number as GPO response FURI_LOG_D(TAG, "Failed reading Mifare Ultralight");
// Iterate over all files found in application
bool pan_found = false;
for(uint8_t i = 0; (i < emv_app.afl.size) && !pan_found; i += 4) {
uint8_t sfi = emv_app.afl.data[i] >> 3;
uint8_t record_start = emv_app.afl.data[i + 1];
uint8_t record_end = emv_app.afl.data[i + 2];
// Iterate over all records in file
for(uint8_t record = record_start; record <= record_end; ++record) {
tx_len = emv_prepare_read_sfi_record(tx_buff, sfi, record);
err = furi_hal_nfc_data_exchange(
tx_buff, tx_len, &rx_buff, &rx_len, false);
if(err != ERR_NONE) {
FURI_LOG_D(
TAG,
"Error reading application sfi %d, record %d",
sfi,
record);
}
if(emv_decode_read_sfi_record(rx_buff, *rx_len, &emv_app)) {
pan_found = true;
break;
}
}
}
if(pan_found) {
FURI_LOG_D(TAG, "Card PAN found");
result->emv_data.number_len = emv_app.card_number_len;
memcpy(
result->emv_data.number,
emv_app.card_number,
result->emv_data.number_len);
if(emv_app.exp_month) {
result->emv_data.exp_mon = emv_app.exp_month;
result->emv_data.exp_year = emv_app.exp_year;
}
if(emv_app.country_code) {
result->emv_data.country_code = emv_app.country_code;
}
if(emv_app.currency_code) {
result->emv_data.currency_code = emv_app.currency_code;
}
// Notify caller and exit
if(nfc_worker->callback) {
nfc_worker->callback(NfcWorkerEventSuccess, nfc_worker->context);
}
break;
} else {
FURI_LOG_D(TAG, "Can't read card number");
}
furi_hal_nfc_deactivate();
} }
} else { } else {
// Can't find EMV card FURI_LOG_W(TAG, "Tag is not Mifare Ultralight");
FURI_LOG_W(TAG, "Card doesn't support EMV");
furi_hal_nfc_deactivate();
}
} else {
// Can't find EMV card
FURI_LOG_D(TAG, "Can't find any cards");
furi_hal_nfc_deactivate();
}
osDelay(20);
}
}
void nfc_worker_emulate_apdu(NfcWorker* nfc_worker) {
ReturnCode err;
uint8_t tx_buff[255] = {};
uint16_t tx_len = 0;
uint8_t* rx_buff;
uint16_t* rx_len;
NfcDeviceCommonData params = {
.uid = {0xCF, 0x72, 0xd4, 0x40},
.uid_len = 4,
.atqa = {0x00, 0x04},
.sak = 0x20,
.device = NfcDeviceNfca,
.protocol = NfcDeviceProtocolEMV,
};
// Test RX data
const uint8_t debug_rx[] = {
0xba, 0x0b, 0xba, 0xba, 0x20, 0x00, 0x02, 0x28, 0xde, 0xad, 0xbe, 0xef, 0x00, 0xca, 0xca,
0xca, 0xfe, 0xfa, 0xce, 0x14, 0x88, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88,
0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10, 0xba,
0x0b, 0xba, 0xba, 0x20, 0x00, 0x02, 0x28, 0xde, 0xad, 0xbe, 0xef, 0x00, 0xca, 0xca, 0xca,
0xfe, 0xfa, 0xce, 0x14, 0x88, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99,
0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10, 0xba, 0x0b,
0xba, 0xba, 0x20, 0x00, 0x02, 0x28, 0xde, 0xad, 0xbe, 0xef, 0x00, 0xca, 0xca, 0xca, 0xfe,
0xfa, 0xce, 0x14, 0x88, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa,
0xbb, 0xcc, 0xdd, 0xee, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10, 0xba, 0x0b, 0xba,
0xba, 0x20, 0x00, 0x02, 0x28, 0xde, 0xad, 0xbe, 0xef, 0x00, 0xca, 0xca, 0xca, 0xfe, 0xfa,
0xce, 0x14, 0x88, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb,
0xcc, 0xdd, 0xee, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10, 0xba, 0x0b, 0xba, 0xba,
0x20, 0x00, 0x02, 0x28, 0xde, 0xad, 0xbe, 0xef, 0x00, 0xca, 0xca, 0xca, 0xfe, 0xfa, 0xce,
0x14, 0x88, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc,
0xdd, 0xee, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10, 0xba, 0x0b, 0xba, 0xba, 0x20,
0x00, 0x02, 0x28, 0xde, 0xad, 0xbe, 0xef, 0x00, 0xca, 0xca, 0xca, 0xfe, 0xfa, 0xce, 0x14,
0x88, 0x00};
// Test TX data
const uint8_t debug_tx[] = {
0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xff, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32,
0x10, 0x14, 0x88, 0x02, 0x28, 0x00, 0x00, 0xca, 0xca, 0x00, 0xc0, 0xc0, 0x00, 0xde, 0xad,
0xbe, 0xef, 0xce, 0xee, 0xec, 0xca, 0xfe, 0xba, 0xba, 0xb0, 0xb0, 0xac, 0xdc, 0x11, 0x12,
0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xff, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10,
0x14, 0x88, 0x02, 0x28, 0x00, 0x00, 0xca, 0xca, 0x00, 0xc0, 0xc0, 0x00, 0xde, 0xad, 0xbe,
0xef, 0xce, 0xee, 0xec, 0xca, 0xfe, 0xba, 0xba, 0xb0, 0xb0, 0xac, 0xdc, 0x11, 0x12, 0x34,
0x56, 0x78, 0x9a, 0xbc, 0xde, 0xff, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10, 0x14,
0x88, 0x02, 0x28, 0x00, 0x00, 0xca, 0xca, 0x00, 0xc0, 0xc0, 0x00, 0xde, 0xad, 0xbe, 0xef,
0xce, 0xee, 0xec, 0xca, 0xfe, 0xba, 0xba, 0xb0, 0xb0, 0xac, 0xdc, 0x11, 0x12, 0x34, 0x56,
0x78, 0x9a, 0xbc, 0xde, 0xff, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10, 0x14, 0x88,
0x02, 0x28, 0x00, 0x00, 0xca, 0xca, 0x00, 0xc0, 0xc0, 0x00, 0xde, 0xad, 0xbe, 0xef, 0xce,
0xee, 0xec, 0xca, 0xfe, 0xba, 0xba, 0xb0, 0xb0, 0xac, 0xdc, 0x11, 0x12, 0x34, 0x56, 0x78,
0x9a, 0xbc, 0xde, 0xff, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10, 0x14, 0x88, 0x02,
0x28, 0x00, 0x00, 0xca, 0xca, 0x00, 0xc0, 0xc0, 0x00, 0xde, 0xad, 0xbe, 0xef, 0xce, 0xee,
0xec, 0xca, 0xfe, 0xba, 0xba, 0xb0, 0xb0, 0xac, 0xdc, 0x11, 0x12, 0x34, 0x56, 0x78, 0x9a,
0xbc, 0xde, 0xff, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10, 0x14, 0x88, 0x02, 0x28,
0x00, 0x00};
while(nfc_worker->state == NfcWorkerStateEmulateApdu) {
if(furi_hal_nfc_listen(params.uid, params.uid_len, params.atqa, params.sak, false, 300)) {
FURI_LOG_D(TAG, "POS terminal detected");
// Read data from POS terminal
err = furi_hal_nfc_data_exchange(NULL, 0, &rx_buff, &rx_len, false);
if(err == ERR_NONE) {
FURI_LOG_D(TAG, "Received Select PPSE");
} else {
FURI_LOG_D(TAG, "Error in 1st data exchange: select PPSE");
furi_hal_nfc_deactivate();
continue;
}
FURI_LOG_D(TAG, "Transive SELECT PPSE ANS");
tx_len = emv_select_ppse_ans(tx_buff);
err = furi_hal_nfc_data_exchange(tx_buff, tx_len, &rx_buff, &rx_len, false);
if(err == ERR_NONE) {
FURI_LOG_D(TAG, "Received Select APP");
} else {
FURI_LOG_D(TAG, "Error in 2nd data exchange: select APP");
furi_hal_nfc_deactivate();
continue;
}
FURI_LOG_D(TAG, "Transive SELECT APP ANS");
tx_len = emv_select_app_ans(tx_buff);
err = furi_hal_nfc_data_exchange(tx_buff, tx_len, &rx_buff, &rx_len, false);
if(err == ERR_NONE) {
FURI_LOG_D(TAG, "Received PDOL");
} else {
FURI_LOG_D(TAG, "Error in 3rd data exchange: receive PDOL");
furi_hal_nfc_deactivate();
continue;
}
FURI_LOG_D(TAG, "Transive PDOL ANS");
tx_len = emv_get_proc_opt_ans(tx_buff);
err = furi_hal_nfc_data_exchange(tx_buff, tx_len, &rx_buff, &rx_len, false);
if(err == ERR_NONE) {
FURI_LOG_D(TAG, "Transive PDOL ANS");
} else {
FURI_LOG_D(TAG, "Error in 4rd data exchange: Transive PDOL ANS");
furi_hal_nfc_deactivate();
continue;
}
if(*rx_len != sizeof(debug_rx) || memcmp(rx_buff, debug_rx, sizeof(debug_rx))) {
FURI_LOG_D(TAG, "Failed long message test");
} else {
FURI_LOG_D(TAG, "Correct debug message received");
tx_len = sizeof(debug_tx);
err = furi_hal_nfc_data_exchange(
(uint8_t*)debug_tx, tx_len, &rx_buff, &rx_len, false);
if(err == ERR_NONE) {
FURI_LOG_D(TAG, "Transive Debug message");
}
}
furi_hal_nfc_deactivate();
} else {
FURI_LOG_D(TAG, "Can't find reader");
}
osDelay(20);
}
}
void nfc_worker_read_mifare_ul(NfcWorker* nfc_worker) {
ReturnCode err;
rfalNfcDevice* dev_list;
uint8_t dev_cnt = 0;
uint8_t tx_buff[255] = {};
uint16_t tx_len = 0;
uint8_t* rx_buff;
uint16_t* rx_len;
MifareUlDevice mf_ul_read;
NfcDeviceData* result = nfc_worker->dev_data;
nfc_device_data_clear(result);
while(nfc_worker->state == NfcWorkerStateReadMifareUl) {
furi_hal_nfc_deactivate();
memset(&mf_ul_read, 0, sizeof(mf_ul_read));
if(furi_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_D(TAG, "Found Mifare Ultralight tag. Reading tag version");
tx_len = mf_ul_prepare_get_version(tx_buff);
err = furi_hal_nfc_data_exchange(tx_buff, tx_len, &rx_buff, &rx_len, false);
if(err == ERR_NONE) {
mf_ul_parse_get_version_response(rx_buff, &mf_ul_read);
FURI_LOG_D(
TAG,
"Mifare Ultralight Type: %d, Pages: %d",
mf_ul_read.data.type,
mf_ul_read.pages_to_read);
FURI_LOG_D(TAG, "Reading signature ...");
tx_len = mf_ul_prepare_read_signature(tx_buff);
if(furi_hal_nfc_data_exchange(tx_buff, tx_len, &rx_buff, &rx_len, false)) {
FURI_LOG_D(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);
}
} else if(err == ERR_TIMEOUT) {
FURI_LOG_D(
TAG,
"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
furi_hal_nfc_deactivate();
if(!furi_hal_nfc_detect(&dev_list, &dev_cnt, 300, false)) {
FURI_LOG_D(TAG, "Lost connection. Restarting search");
continue;
}
} else {
FURI_LOG_D(
TAG, "Error getting Mifare Ultralight version. Error code: %d", err);
continue;
}
if(mf_ul_read.support_fast_read) {
FURI_LOG_D(TAG, "Reading pages ...");
tx_len = mf_ul_prepare_fast_read(tx_buff, 0x00, mf_ul_read.pages_to_read - 1);
if(furi_hal_nfc_data_exchange(tx_buff, tx_len, &rx_buff, &rx_len, false)) {
FURI_LOG_D(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_D(TAG, "Reading 3 counters ...");
for(uint8_t i = 0; i < 3; i++) {
tx_len = mf_ul_prepare_read_cnt(tx_buff, i);
if(furi_hal_nfc_data_exchange(tx_buff, tx_len, &rx_buff, &rx_len, false)) {
FURI_LOG_W(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_D(TAG, "Checking tearing flags ...");
for(uint8_t i = 0; i < 3; i++) {
tx_len = mf_ul_prepare_check_tearing(tx_buff, i);
if(furi_hal_nfc_data_exchange(tx_buff, tx_len, &rx_buff, &rx_len, false)) {
FURI_LOG_D(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_D(TAG, "Reading pages %d - %d ...", page, page + 3);
tx_len = mf_ul_prepare_read(tx_buff, page);
if(furi_hal_nfc_data_exchange(tx_buff, tx_len, &rx_buff, &rx_len, false)) {
FURI_LOG_D(TAG, "Read pages %d - %d failed", page, page + 3);
continue;
} else {
mf_ul_parse_read_response(rx_buff, page, &mf_ul_read);
}
}
}
// Fill result data
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;
result->nfc_data.protocol = NfcDeviceProtocolMifareUl;
memcpy(
result->nfc_data.uid, dev_list[0].dev.nfca.nfcId1, result->nfc_data.uid_len);
result->mf_ul_data = mf_ul_read.data;
// Notify caller and exit
if(nfc_worker->callback) {
nfc_worker->callback(NfcWorkerEventSuccess, nfc_worker->context);
}
break;
} else {
FURI_LOG_W(TAG, "Tag does not support Mifare Ultralight");
} }
} else { } else {
FURI_LOG_D(TAG, "Can't find any tags"); FURI_LOG_D(TAG, "Can't find any tags");
} }
furi_hal_nfc_sleep();
osDelay(100); osDelay(100);
} }
} }
void nfc_worker_emulate_mifare_ul(NfcWorker* nfc_worker) { void nfc_worker_emulate_mifare_ul(NfcWorker* nfc_worker) {
NfcDeviceCommonData* nfc_common = &nfc_worker->dev_data->nfc_data; FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data;
MifareUlDevice mf_ul_emulate; MfUltralightEmulator emulator = {};
mf_ul_prepare_emulation(&mf_ul_emulate, &nfc_worker->dev_data->mf_ul_data); mf_ul_prepare_emulation(&emulator, &nfc_worker->dev_data->mf_ul_data);
while(nfc_worker->state == NfcWorkerStateEmulateMifareUl) { while(nfc_worker->state == NfcWorkerStateEmulateMifareUltralight) {
furi_hal_nfc_emulate_nfca( furi_hal_nfc_emulate_nfca(
nfc_common->uid, nfc_data->uid,
nfc_common->uid_len, nfc_data->uid_len,
nfc_common->atqa, nfc_data->atqa,
nfc_common->sak, nfc_data->sak,
mf_ul_prepare_emulation_response, mf_ul_prepare_emulation_response,
&mf_ul_emulate, &emulator,
5000); 5000);
// Check if data was modified // Check if data was modified
if(mf_ul_emulate.data_changed) { if(emulator.data_changed) {
nfc_worker->dev_data->mf_ul_data = mf_ul_emulate.data; nfc_worker->dev_data->mf_ul_data = emulator.data;
if(nfc_worker->callback) { if(nfc_worker->callback) {
nfc_worker->callback(NfcWorkerEventSuccess, nfc_worker->context); nfc_worker->callback(NfcWorkerEventSuccess, nfc_worker->context);
} }
mf_ul_emulate.data_changed = false; emulator.data_changed = false;
} }
} }
} }
void nfc_worker_mifare_classic_dict_attack(NfcWorker* nfc_worker) { void nfc_worker_mifare_classic_dict_attack(NfcWorker* nfc_worker) {
furi_assert(nfc_worker->callback); furi_assert(nfc_worker->callback);
rfalNfcDevice* dev_list;
rfalNfcDevice* dev;
NfcDeviceCommonData* nfc_common;
uint8_t dev_cnt = 0;
FuriHalNfcTxRxContext tx_rx_ctx = {}; FuriHalNfcTxRxContext tx_rx_ctx = {};
MfClassicAuthContext auth_ctx = {}; MfClassicAuthContext auth_ctx = {};
MfClassicReader reader = {}; MfClassicReader reader = {};
@@ -697,6 +342,7 @@ void nfc_worker_mifare_classic_dict_attack(NfcWorker* nfc_worker) {
uint16_t curr_sector = 0; uint16_t curr_sector = 0;
uint8_t total_sectors = 0; uint8_t total_sectors = 0;
NfcWorkerEvent event; NfcWorkerEvent event;
FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data;
// Open dictionary // Open dictionary
nfc_worker->dict_stream = file_stream_alloc(nfc_worker->storage); nfc_worker->dict_stream = file_stream_alloc(nfc_worker->storage);
@@ -710,14 +356,13 @@ void nfc_worker_mifare_classic_dict_attack(NfcWorker* nfc_worker) {
// Detect Mifare Classic card // Detect Mifare Classic card
while(nfc_worker->state == NfcWorkerStateReadMifareClassic) { while(nfc_worker->state == NfcWorkerStateReadMifareClassic) {
if(furi_hal_nfc_detect(&dev_list, &dev_cnt, 300, false)) { if(furi_hal_nfc_detect(nfc_data, 300)) {
dev = &dev_list[0];
if(mf_classic_get_type( if(mf_classic_get_type(
dev->nfcid, nfc_data->uid,
dev->nfcidLen, nfc_data->uid_len,
dev->dev.nfca.sensRes.anticollisionInfo, nfc_data->atqa[0],
dev->dev.nfca.sensRes.platformInfo, nfc_data->atqa[1],
dev->dev.nfca.selRes.sak, nfc_data->sak,
&reader)) { &reader)) {
total_sectors = mf_classic_get_total_sectors_num(&reader); total_sectors = mf_classic_get_total_sectors_num(&reader);
if(reader.type == MfClassicType1k) { if(reader.type == MfClassicType1k) {
@@ -745,7 +390,7 @@ void nfc_worker_mifare_classic_dict_attack(NfcWorker* nfc_worker) {
mf_classic_auth_init_context(&auth_ctx, reader.cuid, curr_sector); mf_classic_auth_init_context(&auth_ctx, reader.cuid, curr_sector);
bool sector_key_found = false; bool sector_key_found = false;
while(nfc_mf_classic_dict_get_next_key(nfc_worker->dict_stream, &curr_key)) { while(nfc_mf_classic_dict_get_next_key(nfc_worker->dict_stream, &curr_key)) {
furi_hal_nfc_deactivate(); furi_hal_nfc_sleep();
if(furi_hal_nfc_activate_nfca(300, &reader.cuid)) { if(furi_hal_nfc_activate_nfca(300, &reader.cuid)) {
if(!card_found_notified) { if(!card_found_notified) {
if(reader.type == MfClassicType1k) { if(reader.type == MfClassicType1k) {
@@ -817,15 +462,8 @@ void nfc_worker_mifare_classic_dict_attack(NfcWorker* nfc_worker) {
uint8_t sectors_read = uint8_t sectors_read =
mf_classic_read_card(&tx_rx_ctx, &reader, &nfc_worker->dev_data->mf_classic_data); mf_classic_read_card(&tx_rx_ctx, &reader, &nfc_worker->dev_data->mf_classic_data);
if(sectors_read) { if(sectors_read) {
dev = &dev_list[0];
nfc_common = &nfc_worker->dev_data->nfc_data;
nfc_common->uid_len = dev->dev.nfca.nfcId1Len;
nfc_common->atqa[0] = dev->dev.nfca.sensRes.anticollisionInfo;
nfc_common->atqa[1] = dev->dev.nfca.sensRes.platformInfo;
nfc_common->sak = dev->dev.nfca.selRes.sak;
nfc_common->protocol = NfcDeviceProtocolMifareClassic;
memcpy(nfc_common->uid, dev->dev.nfca.nfcId1, nfc_common->uid_len);
event = NfcWorkerEventSuccess; event = NfcWorkerEventSuccess;
nfc_worker->dev_data->protocol = NfcDeviceProtocolMifareClassic;
FURI_LOG_I(TAG, "Successfully read %d sectors", sectors_read); FURI_LOG_I(TAG, "Successfully read %d sectors", sectors_read);
} else { } else {
event = NfcWorkerEventFail; event = NfcWorkerEventFail;
@@ -838,42 +476,8 @@ void nfc_worker_mifare_classic_dict_attack(NfcWorker* nfc_worker) {
stream_free(nfc_worker->dict_stream); stream_free(nfc_worker->dict_stream);
} }
ReturnCode nfc_exchange_full(
uint8_t* tx_buff,
uint16_t tx_len,
uint8_t* rx_buff,
uint16_t rx_cap,
uint16_t* rx_len) {
ReturnCode err;
uint8_t* part_buff;
uint16_t* part_len;
err = furi_hal_nfc_data_exchange(tx_buff, tx_len, &part_buff, &part_len, false);
if(*part_len > rx_cap) {
return ERR_OVERRUN;
}
memcpy(rx_buff, part_buff, *part_len);
*rx_len = *part_len;
while(err == ERR_NONE && rx_buff[0] == 0xAF) {
err = furi_hal_nfc_data_exchange(rx_buff, 1, &part_buff, &part_len, false);
if(*part_len > rx_cap - *rx_len) {
return ERR_OVERRUN;
}
if(*part_len == 0) {
return ERR_PROTO;
}
memcpy(rx_buff + *rx_len, part_buff + 1, *part_len - 1);
*rx_buff = *part_buff;
*rx_len += *part_len - 1;
}
return err;
}
void nfc_worker_read_mifare_desfire(NfcWorker* nfc_worker) { void nfc_worker_read_mifare_desfire(NfcWorker* nfc_worker) {
ReturnCode err; ReturnCode err;
rfalNfcDevice* dev_list;
uint8_t dev_cnt = 0;
uint8_t tx_buff[64] = {}; uint8_t tx_buff[64] = {};
uint16_t tx_len = 0; uint16_t tx_len = 0;
uint8_t rx_buff[512] = {}; uint8_t rx_buff[512] = {};
@@ -881,19 +485,17 @@ void nfc_worker_read_mifare_desfire(NfcWorker* nfc_worker) {
NfcDeviceData* result = nfc_worker->dev_data; NfcDeviceData* result = nfc_worker->dev_data;
nfc_device_data_clear(result); nfc_device_data_clear(result);
MifareDesfireData* data = &result->mf_df_data; MifareDesfireData* data = &result->mf_df_data;
FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data;
while(nfc_worker->state == NfcWorkerStateReadMifareDesfire) { while(nfc_worker->state == NfcWorkerStateReadMifareDesfire) {
furi_hal_nfc_deactivate(); furi_hal_nfc_sleep();
if(!furi_hal_nfc_detect(&dev_list, &dev_cnt, 300, false)) { if(!furi_hal_nfc_detect(nfc_data, 300)) {
osDelay(100); osDelay(100);
continue; continue;
} }
memset(data, 0, sizeof(MifareDesfireData)); memset(data, 0, sizeof(MifareDesfireData));
if(dev_list[0].type != RFAL_NFC_LISTEN_TYPE_NFCA || if(nfc_data->type != FuriHalNfcTypeA ||
!mf_df_check_card_type( !mf_df_check_card_type(nfc_data->atqa[0], nfc_data->atqa[1], nfc_data->sak)) {
dev_list[0].dev.nfca.sensRes.anticollisionInfo,
dev_list[0].dev.nfca.sensRes.platformInfo,
dev_list[0].dev.nfca.selRes.sak)) {
FURI_LOG_D(TAG, "Tag is not DESFire"); FURI_LOG_D(TAG, "Tag is not DESFire");
osDelay(100); osDelay(100);
continue; continue;
@@ -901,18 +503,11 @@ void nfc_worker_read_mifare_desfire(NfcWorker* nfc_worker) {
FURI_LOG_D(TAG, "Found DESFire tag"); FURI_LOG_D(TAG, "Found DESFire tag");
// Fill non-DESFire result data result->protocol = NfcDeviceProtocolMifareDesfire;
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;
result->nfc_data.device = NfcDeviceNfca;
result->nfc_data.protocol = NfcDeviceProtocolMifareDesfire;
memcpy(result->nfc_data.uid, dev_list[0].dev.nfca.nfcId1, result->nfc_data.uid_len);
// Get DESFire version // Get DESFire version
tx_len = mf_df_prepare_get_version(tx_buff); tx_len = mf_df_prepare_get_version(tx_buff);
err = nfc_exchange_full(tx_buff, tx_len, rx_buff, sizeof(rx_buff), &rx_len); err = furi_hal_nfc_exchange_full(tx_buff, tx_len, rx_buff, sizeof(rx_buff), &rx_len);
if(err != ERR_NONE) { if(err != ERR_NONE) {
FURI_LOG_W(TAG, "Bad exchange getting version, err: %d", err); FURI_LOG_W(TAG, "Bad exchange getting version, err: %d", err);
continue; continue;
@@ -923,7 +518,7 @@ void nfc_worker_read_mifare_desfire(NfcWorker* nfc_worker) {
} }
tx_len = mf_df_prepare_get_free_memory(tx_buff); tx_len = mf_df_prepare_get_free_memory(tx_buff);
err = nfc_exchange_full(tx_buff, tx_len, rx_buff, sizeof(rx_buff), &rx_len); err = furi_hal_nfc_exchange_full(tx_buff, tx_len, rx_buff, sizeof(rx_buff), &rx_len);
if(err == ERR_NONE) { if(err == ERR_NONE) {
data->free_memory = malloc(sizeof(MifareDesfireFreeMemory)); data->free_memory = malloc(sizeof(MifareDesfireFreeMemory));
memset(data->free_memory, 0, sizeof(MifareDesfireFreeMemory)); memset(data->free_memory, 0, sizeof(MifareDesfireFreeMemory));
@@ -935,7 +530,7 @@ void nfc_worker_read_mifare_desfire(NfcWorker* nfc_worker) {
} }
tx_len = mf_df_prepare_get_key_settings(tx_buff); tx_len = mf_df_prepare_get_key_settings(tx_buff);
err = nfc_exchange_full(tx_buff, tx_len, rx_buff, sizeof(rx_buff), &rx_len); err = furi_hal_nfc_exchange_full(tx_buff, tx_len, rx_buff, sizeof(rx_buff), &rx_len);
if(err != ERR_NONE) { if(err != ERR_NONE) {
FURI_LOG_D(TAG, "Bad exchange getting key settings, err: %d", err); FURI_LOG_D(TAG, "Bad exchange getting key settings, err: %d", err);
} else { } else {
@@ -951,7 +546,8 @@ void nfc_worker_read_mifare_desfire(NfcWorker* nfc_worker) {
&data->master_key_settings->key_version_head; &data->master_key_settings->key_version_head;
for(uint8_t key_id = 0; key_id < data->master_key_settings->max_keys; key_id++) { for(uint8_t key_id = 0; key_id < data->master_key_settings->max_keys; key_id++) {
tx_len = mf_df_prepare_get_key_version(tx_buff, key_id); tx_len = mf_df_prepare_get_key_version(tx_buff, key_id);
err = nfc_exchange_full(tx_buff, tx_len, rx_buff, sizeof(rx_buff), &rx_len); err =
furi_hal_nfc_exchange_full(tx_buff, tx_len, rx_buff, sizeof(rx_buff), &rx_len);
if(err != ERR_NONE) { if(err != ERR_NONE) {
FURI_LOG_W(TAG, "Bad exchange getting key version, err: %d", err); FURI_LOG_W(TAG, "Bad exchange getting key version, err: %d", err);
continue; continue;
@@ -970,7 +566,7 @@ void nfc_worker_read_mifare_desfire(NfcWorker* nfc_worker) {
} }
tx_len = mf_df_prepare_get_application_ids(tx_buff); tx_len = mf_df_prepare_get_application_ids(tx_buff);
err = nfc_exchange_full(tx_buff, tx_len, rx_buff, sizeof(rx_buff), &rx_len); err = furi_hal_nfc_exchange_full(tx_buff, tx_len, rx_buff, sizeof(rx_buff), &rx_len);
if(err != ERR_NONE) { if(err != ERR_NONE) {
FURI_LOG_W(TAG, "Bad exchange getting application IDs, err: %d", err); FURI_LOG_W(TAG, "Bad exchange getting application IDs, err: %d", err);
} else { } else {
@@ -981,13 +577,13 @@ void nfc_worker_read_mifare_desfire(NfcWorker* nfc_worker) {
for(MifareDesfireApplication* app = data->app_head; app; app = app->next) { for(MifareDesfireApplication* app = data->app_head; app; app = app->next) {
tx_len = mf_df_prepare_select_application(tx_buff, app->id); tx_len = mf_df_prepare_select_application(tx_buff, app->id);
err = nfc_exchange_full(tx_buff, tx_len, rx_buff, sizeof(rx_buff), &rx_len); err = furi_hal_nfc_exchange_full(tx_buff, tx_len, rx_buff, sizeof(rx_buff), &rx_len);
if(!mf_df_parse_select_application_response(rx_buff, rx_len)) { if(!mf_df_parse_select_application_response(rx_buff, rx_len)) {
FURI_LOG_W(TAG, "Bad exchange selecting application, err: %d", err); FURI_LOG_W(TAG, "Bad exchange selecting application, err: %d", err);
continue; continue;
} }
tx_len = mf_df_prepare_get_key_settings(tx_buff); tx_len = mf_df_prepare_get_key_settings(tx_buff);
err = nfc_exchange_full(tx_buff, tx_len, rx_buff, sizeof(rx_buff), &rx_len); err = furi_hal_nfc_exchange_full(tx_buff, tx_len, rx_buff, sizeof(rx_buff), &rx_len);
if(err != ERR_NONE) { if(err != ERR_NONE) {
FURI_LOG_W(TAG, "Bad exchange getting key settings, err: %d", err); FURI_LOG_W(TAG, "Bad exchange getting key settings, err: %d", err);
} else { } else {
@@ -1002,7 +598,8 @@ void nfc_worker_read_mifare_desfire(NfcWorker* nfc_worker) {
MifareDesfireKeyVersion** key_version_head = &app->key_settings->key_version_head; MifareDesfireKeyVersion** key_version_head = &app->key_settings->key_version_head;
for(uint8_t key_id = 0; key_id < app->key_settings->max_keys; key_id++) { for(uint8_t key_id = 0; key_id < app->key_settings->max_keys; key_id++) {
tx_len = mf_df_prepare_get_key_version(tx_buff, key_id); tx_len = mf_df_prepare_get_key_version(tx_buff, key_id);
err = nfc_exchange_full(tx_buff, tx_len, rx_buff, sizeof(rx_buff), &rx_len); err = furi_hal_nfc_exchange_full(
tx_buff, tx_len, rx_buff, sizeof(rx_buff), &rx_len);
if(err != ERR_NONE) { if(err != ERR_NONE) {
FURI_LOG_W(TAG, "Bad exchange getting key version, err: %d", err); FURI_LOG_W(TAG, "Bad exchange getting key version, err: %d", err);
continue; continue;
@@ -1021,7 +618,7 @@ void nfc_worker_read_mifare_desfire(NfcWorker* nfc_worker) {
} }
tx_len = mf_df_prepare_get_file_ids(tx_buff); tx_len = mf_df_prepare_get_file_ids(tx_buff);
err = nfc_exchange_full(tx_buff, tx_len, rx_buff, sizeof(rx_buff), &rx_len); err = furi_hal_nfc_exchange_full(tx_buff, tx_len, rx_buff, sizeof(rx_buff), &rx_len);
if(err != ERR_NONE) { if(err != ERR_NONE) {
FURI_LOG_W(TAG, "Bad exchange getting file IDs, err: %d", err); FURI_LOG_W(TAG, "Bad exchange getting file IDs, err: %d", err);
} else { } else {
@@ -1032,7 +629,8 @@ void nfc_worker_read_mifare_desfire(NfcWorker* nfc_worker) {
for(MifareDesfireFile* file = app->file_head; file; file = file->next) { for(MifareDesfireFile* file = app->file_head; file; file = file->next) {
tx_len = mf_df_prepare_get_file_settings(tx_buff, file->id); tx_len = mf_df_prepare_get_file_settings(tx_buff, file->id);
err = nfc_exchange_full(tx_buff, tx_len, rx_buff, sizeof(rx_buff), &rx_len); err =
furi_hal_nfc_exchange_full(tx_buff, tx_len, rx_buff, sizeof(rx_buff), &rx_len);
if(err != ERR_NONE) { if(err != ERR_NONE) {
FURI_LOG_W(TAG, "Bad exchange getting file settings, err: %d", err); FURI_LOG_W(TAG, "Bad exchange getting file settings, err: %d", err);
continue; continue;
@@ -1054,7 +652,8 @@ void nfc_worker_read_mifare_desfire(NfcWorker* nfc_worker) {
tx_len = mf_df_prepare_read_records(tx_buff, file->id, 0, 0); tx_len = mf_df_prepare_read_records(tx_buff, file->id, 0, 0);
break; break;
} }
err = nfc_exchange_full(tx_buff, tx_len, rx_buff, sizeof(rx_buff), &rx_len); err =
furi_hal_nfc_exchange_full(tx_buff, tx_len, rx_buff, sizeof(rx_buff), &rx_len);
if(err != ERR_NONE) { if(err != ERR_NONE) {
FURI_LOG_W(TAG, "Bad exchange reading file %d, err: %d", file->id, err); FURI_LOG_W(TAG, "Bad exchange reading file %d, err: %d", file->id, err);
continue; continue;
@@ -1073,11 +672,3 @@ void nfc_worker_read_mifare_desfire(NfcWorker* nfc_worker) {
break; break;
} }
} }
void nfc_worker_field(NfcWorker* nfc_worker) {
furi_hal_nfc_field_on();
while(nfc_worker->state == NfcWorkerStateField) {
osDelay(50);
}
furi_hal_nfc_field_off();
}

View File

@@ -13,11 +13,11 @@ typedef enum {
NfcWorkerStateDetect, NfcWorkerStateDetect,
NfcWorkerStateEmulate, NfcWorkerStateEmulate,
NfcWorkerStateReadEMVApp, NfcWorkerStateReadEMVApp,
NfcWorkerStateReadEMV, NfcWorkerStateReadEMVData,
NfcWorkerStateEmulateApdu, NfcWorkerStateEmulateApdu,
NfcWorkerStateField, NfcWorkerStateField,
NfcWorkerStateReadMifareUl, NfcWorkerStateReadMifareUltralight,
NfcWorkerStateEmulateMifareUl, NfcWorkerStateEmulateMifareUltralight,
NfcWorkerStateReadMifareClassic, NfcWorkerStateReadMifareClassic,
NfcWorkerStateReadMifareDesfire, NfcWorkerStateReadMifareDesfire,
// Transition // Transition

View File

@@ -4,19 +4,8 @@
#include "nfc_worker.h" #include "nfc_worker.h"
#include <furi.h> #include <furi.h>
#include <stdbool.h>
#include <lib/toolbox/stream/file_stream.h> #include <lib/toolbox/stream/file_stream.h>
#include <rfal_analogConfig.h>
#include <rfal_rf.h>
#include <rfal_nfc.h>
#include <rfal_nfca.h>
#include <rfal_nfcb.h>
#include <rfal_nfcf.h>
#include <rfal_nfcv.h>
#include <st25r3916.h>
#include <st25r3916_irq.h>
struct NfcWorker { struct NfcWorker {
FuriThread* thread; FuriThread* thread;
Storage* storage; Storage* storage;
@@ -44,9 +33,7 @@ void nfc_worker_detect(NfcWorker* nfc_worker);
void nfc_worker_emulate(NfcWorker* nfc_worker); void nfc_worker_emulate(NfcWorker* nfc_worker);
void nfc_worker_field(NfcWorker* nfc_worker); void nfc_worker_read_mifare_ultralight(NfcWorker* nfc_worker);
void nfc_worker_read_mifare_ul(NfcWorker* nfc_worker);
void nfc_worker_mifare_classic_dict_attack(NfcWorker* nfc_worker); void nfc_worker_mifare_classic_dict_attack(NfcWorker* nfc_worker);

View File

@@ -17,7 +17,7 @@ void nfc_scene_card_menu_on_enter(void* context) {
Nfc* nfc = context; Nfc* nfc = context;
Submenu* submenu = nfc->submenu; Submenu* submenu = nfc->submenu;
if(nfc->dev->dev_data.nfc_data.protocol > NfcDeviceProtocolUnknown) { if(nfc->dev->dev_data.protocol > NfcDeviceProtocolUnknown) {
submenu_add_item( submenu_add_item(
submenu, submenu,
"Run Compatible App", "Run Compatible App",
@@ -49,13 +49,13 @@ bool nfc_scene_card_menu_on_event(void* context, SceneManagerEvent event) {
if(event.event == SubmenuIndexRunApp) { if(event.event == SubmenuIndexRunApp) {
scene_manager_set_scene_state( scene_manager_set_scene_state(
nfc->scene_manager, NfcSceneCardMenu, SubmenuIndexRunApp); nfc->scene_manager, NfcSceneCardMenu, SubmenuIndexRunApp);
if(nfc->dev->dev_data.nfc_data.protocol == NfcDeviceProtocolMifareUl) { if(nfc->dev->dev_data.protocol == NfcDeviceProtocolMifareUl) {
scene_manager_next_scene(nfc->scene_manager, NfcSceneReadMifareUl); scene_manager_next_scene(nfc->scene_manager, NfcSceneReadMifareUl);
} else if(nfc->dev->dev_data.nfc_data.protocol == NfcDeviceProtocolMifareDesfire) { } else if(nfc->dev->dev_data.protocol == NfcDeviceProtocolMifareDesfire) {
scene_manager_next_scene(nfc->scene_manager, NfcSceneReadMifareDesfire); scene_manager_next_scene(nfc->scene_manager, NfcSceneReadMifareDesfire);
} else if(nfc->dev->dev_data.nfc_data.protocol == NfcDeviceProtocolEMV) { } else if(nfc->dev->dev_data.protocol == NfcDeviceProtocolEMV) {
scene_manager_next_scene(nfc->scene_manager, NfcSceneReadEmvApp); scene_manager_next_scene(nfc->scene_manager, NfcSceneReadEmvApp);
} else if(nfc->dev->dev_data.nfc_data.protocol == NfcDeviceProtocolMifareClassic) { } else if(nfc->dev->dev_data.protocol == NfcDeviceProtocolMifareClassic) {
scene_manager_next_scene(nfc->scene_manager, NfcSceneReadMifareClassic); scene_manager_next_scene(nfc->scene_manager, NfcSceneReadMifareClassic);
} }
consumed = true; consumed = true;

View File

@@ -2,7 +2,6 @@ ADD_SCENE(nfc, start, Start)
ADD_SCENE(nfc, read_card, ReadCard) ADD_SCENE(nfc, read_card, ReadCard)
ADD_SCENE(nfc, read_card_success, ReadCardSuccess) ADD_SCENE(nfc, read_card_success, ReadCardSuccess)
ADD_SCENE(nfc, card_menu, CardMenu) ADD_SCENE(nfc, card_menu, CardMenu)
ADD_SCENE(nfc, not_implemented, NotImplemented)
ADD_SCENE(nfc, emulate_uid, EmulateUid) ADD_SCENE(nfc, emulate_uid, EmulateUid)
ADD_SCENE(nfc, save_name, SaveName) ADD_SCENE(nfc, save_name, SaveName)
ADD_SCENE(nfc, save_success, SaveSuccess) ADD_SCENE(nfc, save_success, SaveSuccess)

View File

@@ -6,13 +6,13 @@ enum SubmenuDebugIndex {
}; };
void nfc_scene_debug_submenu_callback(void* context, uint32_t index) { void nfc_scene_debug_submenu_callback(void* context, uint32_t index) {
Nfc* nfc = (Nfc*)context; Nfc* nfc = context;
view_dispatcher_send_custom_event(nfc->view_dispatcher, index); view_dispatcher_send_custom_event(nfc->view_dispatcher, index);
} }
void nfc_scene_debug_on_enter(void* context) { void nfc_scene_debug_on_enter(void* context) {
Nfc* nfc = (Nfc*)context; Nfc* nfc = context;
Submenu* submenu = nfc->submenu; Submenu* submenu = nfc->submenu;
submenu_add_item( submenu_add_item(
@@ -28,7 +28,7 @@ void nfc_scene_debug_on_enter(void* context) {
} }
bool nfc_scene_debug_on_event(void* context, SceneManagerEvent event) { bool nfc_scene_debug_on_event(void* context, SceneManagerEvent event) {
Nfc* nfc = (Nfc*)context; Nfc* nfc = context;
bool consumed = false; bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) { if(event.type == SceneManagerEventTypeCustom) {
@@ -48,7 +48,7 @@ bool nfc_scene_debug_on_event(void* context, SceneManagerEvent event) {
} }
void nfc_scene_debug_on_exit(void* context) { void nfc_scene_debug_on_exit(void* context) {
Nfc* nfc = (Nfc*)context; Nfc* nfc = context;
submenu_reset(nfc->submenu); submenu_reset(nfc->submenu);
} }

View File

@@ -1,29 +1,29 @@
#include "../nfc_i.h" #include "../nfc_i.h"
void nfc_scene_delete_widget_callback(GuiButtonType result, InputType type, void* context) { void nfc_scene_delete_widget_callback(GuiButtonType result, InputType type, void* context) {
Nfc* nfc = (Nfc*)context; Nfc* nfc = context;
if(type == InputTypeShort) { if(type == InputTypeShort) {
view_dispatcher_send_custom_event(nfc->view_dispatcher, result); view_dispatcher_send_custom_event(nfc->view_dispatcher, result);
} }
} }
void nfc_scene_delete_on_enter(void* context) { void nfc_scene_delete_on_enter(void* context) {
Nfc* nfc = (Nfc*)context; Nfc* nfc = context;
// Setup Custom Widget view // Setup Custom Widget view
char delete_str[64]; char temp_str[64];
snprintf(delete_str, sizeof(delete_str), "\e#Delete %s?\e#", nfc->dev->dev_name); snprintf(temp_str, sizeof(temp_str), "\e#Delete %s?\e#", nfc->dev->dev_name);
widget_add_text_box_element(nfc->widget, 0, 0, 128, 23, AlignCenter, AlignCenter, delete_str); widget_add_text_box_element(
nfc->widget, 0, 0, 128, 23, AlignCenter, AlignCenter, temp_str, false);
widget_add_button_element( widget_add_button_element(
nfc->widget, GuiButtonTypeLeft, "Back", nfc_scene_delete_widget_callback, nfc); nfc->widget, GuiButtonTypeLeft, "Back", nfc_scene_delete_widget_callback, nfc);
widget_add_button_element( widget_add_button_element(
nfc->widget, GuiButtonTypeRight, "Delete", nfc_scene_delete_widget_callback, nfc); nfc->widget, GuiButtonTypeRight, "Delete", nfc_scene_delete_widget_callback, nfc);
char uid_str[32]; FuriHalNfcDevData* data = &nfc->dev->dev_data.nfc_data;
NfcDeviceCommonData* data = &nfc->dev->dev_data.nfc_data;
if(data->uid_len == 4) { if(data->uid_len == 4) {
snprintf( snprintf(
uid_str, temp_str,
sizeof(uid_str), sizeof(temp_str),
"UID: %02X %02X %02X %02X", "UID: %02X %02X %02X %02X",
data->uid[0], data->uid[0],
data->uid[1], data->uid[1],
@@ -31,8 +31,8 @@ void nfc_scene_delete_on_enter(void* context) {
data->uid[3]); data->uid[3]);
} else if(data->uid_len == 7) { } else if(data->uid_len == 7) {
snprintf( snprintf(
uid_str, temp_str,
sizeof(uid_str), sizeof(temp_str),
"UID: %02X %02X %02X %02X %02X %02X %02X", "UID: %02X %02X %02X %02X %02X %02X %02X",
data->uid[0], data->uid[0],
data->uid[1], data->uid[1],
@@ -42,12 +42,13 @@ void nfc_scene_delete_on_enter(void* context) {
data->uid[5], data->uid[5],
data->uid[6]); data->uid[6]);
} }
widget_add_string_element(nfc->widget, 64, 23, AlignCenter, AlignTop, FontSecondary, uid_str); widget_add_string_element(nfc->widget, 64, 23, AlignCenter, AlignTop, FontSecondary, temp_str);
const char* protocol_name = NULL; const char* protocol_name = NULL;
if(data->protocol == NfcDeviceProtocolEMV) { NfcProtocol protocol = nfc->dev->dev_data.protocol;
protocol_name = nfc_guess_protocol(data->protocol); if(protocol == NfcDeviceProtocolEMV) {
} else if(data->protocol == NfcDeviceProtocolMifareUl) { protocol_name = nfc_guess_protocol(protocol);
} else if(protocol == NfcDeviceProtocolMifareUl) {
protocol_name = nfc_mf_ul_type(nfc->dev->dev_data.mf_ul_data.type, false); protocol_name = nfc_mf_ul_type(nfc->dev->dev_data.mf_ul_data.type, false);
} }
if(protocol_name) { if(protocol_name) {
@@ -56,18 +57,17 @@ void nfc_scene_delete_on_enter(void* context) {
} }
// TODO change dinamically // TODO change dinamically
widget_add_string_element(nfc->widget, 118, 33, AlignRight, AlignTop, FontSecondary, "NFC-A"); widget_add_string_element(nfc->widget, 118, 33, AlignRight, AlignTop, FontSecondary, "NFC-A");
char sak_str[16]; snprintf(temp_str, sizeof(temp_str), "SAK: %02X", data->sak);
snprintf(sak_str, sizeof(sak_str), "SAK: %02X", data->sak); widget_add_string_element(nfc->widget, 10, 43, AlignLeft, AlignTop, FontSecondary, temp_str);
widget_add_string_element(nfc->widget, 10, 43, AlignLeft, AlignTop, FontSecondary, sak_str); snprintf(temp_str, sizeof(temp_str), "ATQA: %02X%02X", data->atqa[0], data->atqa[1]);
char atqa_str[16]; widget_add_string_element(nfc->widget, 118, 43, AlignRight, AlignTop, FontSecondary, temp_str);
snprintf(atqa_str, sizeof(atqa_str), "ATQA: %02X%02X", data->atqa[0], data->atqa[1]);
widget_add_string_element(nfc->widget, 118, 43, AlignRight, AlignTop, FontSecondary, atqa_str);
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget);
} }
bool nfc_scene_delete_on_event(void* context, SceneManagerEvent event) { bool nfc_scene_delete_on_event(void* context, SceneManagerEvent event) {
Nfc* nfc = (Nfc*)context; Nfc* nfc = context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) { if(event.type == SceneManagerEventTypeCustom) {
if(event.event == GuiButtonTypeLeft) { if(event.event == GuiButtonTypeLeft) {
@@ -79,14 +79,14 @@ bool nfc_scene_delete_on_event(void* context, SceneManagerEvent event) {
scene_manager_search_and_switch_to_previous_scene( scene_manager_search_and_switch_to_previous_scene(
nfc->scene_manager, NfcSceneStart); nfc->scene_manager, NfcSceneStart);
} }
return true; consumed = true;
} }
} }
return false; return consumed;
} }
void nfc_scene_delete_on_exit(void* context) { void nfc_scene_delete_on_exit(void* context) {
Nfc* nfc = (Nfc*)context; Nfc* nfc = context;
widget_reset(nfc->widget); widget_reset(nfc->widget);
} }

View File

@@ -1,12 +1,12 @@
#include "../nfc_i.h" #include "../nfc_i.h"
void nfc_scene_delete_success_popup_callback(void* context) { void nfc_scene_delete_success_popup_callback(void* context) {
Nfc* nfc = (Nfc*)context; Nfc* nfc = context;
view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventViewExit); view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventViewExit);
} }
void nfc_scene_delete_success_on_enter(void* context) { void nfc_scene_delete_success_on_enter(void* context) {
Nfc* nfc = (Nfc*)context; Nfc* nfc = context;
// Setup view // Setup view
Popup* popup = nfc->popup; Popup* popup = nfc->popup;
@@ -20,27 +20,21 @@ void nfc_scene_delete_success_on_enter(void* context) {
} }
bool nfc_scene_delete_success_on_event(void* context, SceneManagerEvent event) { bool nfc_scene_delete_success_on_event(void* context, SceneManagerEvent event) {
Nfc* nfc = (Nfc*)context; Nfc* nfc = context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) { if(event.type == SceneManagerEventTypeCustom) {
if(event.event == NfcCustomEventViewExit) { if(event.event == NfcCustomEventViewExit) {
return scene_manager_search_and_switch_to_previous_scene( consumed = scene_manager_search_and_switch_to_previous_scene(
nfc->scene_manager, NfcSceneStart); nfc->scene_manager, NfcSceneStart);
} }
} }
return false; return consumed;
} }
void nfc_scene_delete_success_on_exit(void* context) { void nfc_scene_delete_success_on_exit(void* context) {
Nfc* nfc = (Nfc*)context; Nfc* nfc = context;
// Clear view // Clear view
Popup* popup = nfc->popup; popup_reset(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);
} }

View File

@@ -36,19 +36,19 @@ void nfc_scene_device_info_on_enter(void* context) {
(nfc->dev->format == NfcDeviceSaveFormatBankCard); (nfc->dev->format == NfcDeviceSaveFormatBankCard);
// Setup Custom Widget view // Setup Custom Widget view
widget_add_text_box_element( widget_add_text_box_element(
nfc->widget, 0, 0, 128, 22, AlignCenter, AlignTop, nfc->dev->dev_name); nfc->widget, 0, 0, 128, 22, AlignCenter, AlignTop, nfc->dev->dev_name, false);
widget_add_button_element( widget_add_button_element(
nfc->widget, GuiButtonTypeLeft, "Back", nfc_scene_device_info_widget_callback, nfc); nfc->widget, GuiButtonTypeLeft, "Back", nfc_scene_device_info_widget_callback, nfc);
if(data_display_supported) { if(data_display_supported) {
widget_add_button_element( widget_add_button_element(
nfc->widget, GuiButtonTypeRight, "Data", nfc_scene_device_info_widget_callback, nfc); nfc->widget, GuiButtonTypeRight, "Data", nfc_scene_device_info_widget_callback, nfc);
} }
char uid_str[32]; char temp_str[32];
NfcDeviceCommonData* data = &nfc->dev->dev_data.nfc_data; FuriHalNfcDevData* data = &nfc->dev->dev_data.nfc_data;
if(data->uid_len == 4) { if(data->uid_len == 4) {
snprintf( snprintf(
uid_str, temp_str,
sizeof(uid_str), sizeof(temp_str),
"UID: %02X %02X %02X %02X", "UID: %02X %02X %02X %02X",
data->uid[0], data->uid[0],
data->uid[1], data->uid[1],
@@ -56,8 +56,8 @@ void nfc_scene_device_info_on_enter(void* context) {
data->uid[3]); data->uid[3]);
} else if(data->uid_len == 7) { } else if(data->uid_len == 7) {
snprintf( snprintf(
uid_str, temp_str,
sizeof(uid_str), sizeof(temp_str),
"UID: %02X %02X %02X %02X %02X %02X %02X", "UID: %02X %02X %02X %02X %02X %02X %02X",
data->uid[0], data->uid[0],
data->uid[1], data->uid[1],
@@ -67,15 +67,15 @@ void nfc_scene_device_info_on_enter(void* context) {
data->uid[5], data->uid[5],
data->uid[6]); data->uid[6]);
} }
widget_add_string_element(nfc->widget, 64, 21, AlignCenter, AlignTop, FontSecondary, uid_str); widget_add_string_element(nfc->widget, 64, 21, AlignCenter, AlignTop, FontSecondary, temp_str);
const char* protocol_name = NULL; const char* protocol_name = NULL;
if(data->protocol == NfcDeviceProtocolEMV || NfcProtocol protocol = nfc->dev->dev_data.protocol;
data->protocol == NfcDeviceProtocolMifareDesfire) { if(protocol == NfcDeviceProtocolEMV || protocol == NfcDeviceProtocolMifareDesfire) {
protocol_name = nfc_guess_protocol(data->protocol); protocol_name = nfc_guess_protocol(protocol);
} else if(data->protocol == NfcDeviceProtocolMifareUl) { } else if(protocol == NfcDeviceProtocolMifareUl) {
protocol_name = nfc_mf_ul_type(nfc->dev->dev_data.mf_ul_data.type, false); protocol_name = nfc_mf_ul_type(nfc->dev->dev_data.mf_ul_data.type, false);
} else if(data->protocol == NfcDeviceProtocolMifareClassic) { } else if(protocol == NfcDeviceProtocolMifareClassic) {
protocol_name = nfc_mf_classic_type(nfc->dev->dev_data.mf_classic_data.type); protocol_name = nfc_mf_classic_type(nfc->dev->dev_data.mf_classic_data.type);
} }
if(protocol_name) { if(protocol_name) {
@@ -84,12 +84,10 @@ void nfc_scene_device_info_on_enter(void* context) {
} }
// TODO change dinamically // TODO change dinamically
widget_add_string_element(nfc->widget, 118, 32, AlignRight, AlignTop, FontSecondary, "NFC-A"); widget_add_string_element(nfc->widget, 118, 32, AlignRight, AlignTop, FontSecondary, "NFC-A");
char sak_str[16]; snprintf(temp_str, sizeof(temp_str), "SAK: %02X", data->sak);
snprintf(sak_str, sizeof(sak_str), "SAK: %02X", data->sak); widget_add_string_element(nfc->widget, 10, 42, AlignLeft, AlignTop, FontSecondary, temp_str);
widget_add_string_element(nfc->widget, 10, 42, AlignLeft, AlignTop, FontSecondary, sak_str); snprintf(temp_str, sizeof(temp_str), "ATQA: %02X%02X", data->atqa[0], data->atqa[1]);
char atqa_str[16]; widget_add_string_element(nfc->widget, 118, 42, AlignRight, AlignTop, FontSecondary, temp_str);
snprintf(atqa_str, sizeof(atqa_str), "ATQA: %02X%02X", data->atqa[0], data->atqa[1]);
widget_add_string_element(nfc->widget, 118, 42, AlignRight, AlignTop, FontSecondary, atqa_str);
// Setup Data View // Setup Data View
if(nfc->dev->format == NfcDeviceSaveFormatUid) { if(nfc->dev->format == NfcDeviceSaveFormatUid) {
@@ -99,7 +97,7 @@ void nfc_scene_device_info_on_enter(void* context) {
dialog_ex_set_context(dialog_ex, nfc); dialog_ex_set_context(dialog_ex, nfc);
dialog_ex_set_result_callback(dialog_ex, nfc_scene_device_info_dialog_callback); dialog_ex_set_result_callback(dialog_ex, nfc_scene_device_info_dialog_callback);
} else if(nfc->dev->format == NfcDeviceSaveFormatMifareUl) { } else if(nfc->dev->format == NfcDeviceSaveFormatMifareUl) {
MifareUlData* mf_ul_data = &nfc->dev->dev_data.mf_ul_data; MfUltralightData* mf_ul_data = &nfc->dev->dev_data.mf_ul_data;
TextBox* text_box = nfc->text_box; TextBox* text_box = nfc->text_box;
text_box_set_font(text_box, TextBoxFontHex); text_box_set_font(text_box, TextBoxFontHex);
for(uint16_t i = 0; i < mf_ul_data->data_size; i += 2) { for(uint16_t i = 0; i < mf_ul_data->data_size; i += 2) {
@@ -130,7 +128,7 @@ void nfc_scene_device_info_on_enter(void* context) {
widget_add_string_element( widget_add_string_element(
nfc->widget, 64, 17, AlignCenter, AlignBottom, FontSecondary, nfc->text_store); nfc->widget, 64, 17, AlignCenter, AlignBottom, FontSecondary, nfc->text_store);
} else if(nfc->dev->format == NfcDeviceSaveFormatBankCard) { } else if(nfc->dev->format == NfcDeviceSaveFormatBankCard) {
NfcEmvData* emv_data = &nfc->dev->dev_data.emv_data; EmvData* emv_data = &nfc->dev->dev_data.emv_data;
BankCard* bank_card = nfc->bank_card; BankCard* bank_card = nfc->bank_card;
bank_card_set_name(bank_card, emv_data->name); bank_card_set_name(bank_card, emv_data->name);
bank_card_set_number(bank_card, emv_data->number, emv_data->number_len); bank_card_set_number(bank_card, emv_data->number, emv_data->number_len);
@@ -212,21 +210,16 @@ bool nfc_scene_device_info_on_event(void* context, SceneManagerEvent event) {
} }
void nfc_scene_device_info_on_exit(void* context) { void nfc_scene_device_info_on_exit(void* context) {
Nfc* nfc = (Nfc*)context; Nfc* nfc = context;
// Clear Custom Widget // Clear views
widget_reset(nfc->widget); widget_reset(nfc->widget);
if(nfc->dev->format == NfcDeviceSaveFormatUid) { if(nfc->dev->format == NfcDeviceSaveFormatUid) {
// Clear Dialog dialog_ex_reset(nfc->dialog_ex);
DialogEx* dialog_ex = nfc->dialog_ex;
dialog_ex_reset(dialog_ex);
} else if(nfc->dev->format == NfcDeviceSaveFormatMifareUl) { } else if(nfc->dev->format == NfcDeviceSaveFormatMifareUl) {
// Clear TextBox
text_box_reset(nfc->text_box); text_box_reset(nfc->text_box);
string_reset(nfc->text_box_store); string_reset(nfc->text_box_store);
} else if(nfc->dev->format == NfcDeviceSaveFormatBankCard) { } else if(nfc->dev->format == NfcDeviceSaveFormatBankCard) {
// Clear Bank Card
bank_card_clear(nfc->bank_card); bank_card_clear(nfc->bank_card);
} }
} }

View File

@@ -1,36 +1,34 @@
#include "../nfc_i.h" #include "../nfc_i.h"
void nfc_scene_emulate_apdu_sequence_on_enter(void* context) { void nfc_scene_emulate_apdu_sequence_on_enter(void* context) {
Nfc* nfc = (Nfc*)context; Nfc* nfc = context;
// Setup view // Setup view
Popup* popup = nfc->popup; Popup* popup = nfc->popup;
popup_set_header(popup, "Run APDU reader", 64, 31, AlignCenter, AlignTop); popup_set_header(popup, "Run APDU reader", 64, 31, AlignCenter, AlignTop);
// Setup and start worker // Setup and start worker
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup); view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup);
nfc_worker_start(nfc->worker, NfcWorkerStateEmulateApdu, &nfc->dev->dev_data, NULL, nfc); nfc_worker_start(nfc->worker, NfcWorkerStateEmulateApdu, &nfc->dev->dev_data, NULL, nfc);
} }
bool nfc_scene_emulate_apdu_sequence_on_event(void* context, SceneManagerEvent event) { bool nfc_scene_emulate_apdu_sequence_on_event(void* context, SceneManagerEvent event) {
Nfc* nfc = (Nfc*)context; Nfc* nfc = context;
bool consumed = false;
if(event.type == SceneManagerEventTypeTick) { if(event.type == SceneManagerEventTypeTick) {
notification_message(nfc->notifications, &sequence_blink_blue_10); notification_message(nfc->notifications, &sequence_blink_blue_10);
return true; consumed = true;
} }
return false;
return consumed;
} }
void nfc_scene_emulate_apdu_sequence_on_exit(void* context) { void nfc_scene_emulate_apdu_sequence_on_exit(void* context) {
Nfc* nfc = (Nfc*)context; Nfc* nfc = context;
// Stop worker // Stop worker
nfc_worker_stop(nfc->worker); nfc_worker_stop(nfc->worker);
// Clear view // Clear view
Popup* popup = nfc->popup; popup_reset(nfc->popup);
popup_reset(popup);
} }

View File

@@ -5,13 +5,14 @@
#define NFC_MF_UL_DATA_CHANGED (1UL) #define NFC_MF_UL_DATA_CHANGED (1UL)
void nfc_emulate_mifare_ul_worker_callback(NfcWorkerEvent event, void* context) { void nfc_emulate_mifare_ul_worker_callback(NfcWorkerEvent event, void* context) {
Nfc* nfc = (Nfc*)context; Nfc* nfc = context;
scene_manager_set_scene_state( scene_manager_set_scene_state(
nfc->scene_manager, NfcSceneEmulateMifareUl, NFC_MF_UL_DATA_CHANGED); nfc->scene_manager, NfcSceneEmulateMifareUl, NFC_MF_UL_DATA_CHANGED);
} }
void nfc_scene_emulate_mifare_ul_on_enter(void* context) { void nfc_scene_emulate_mifare_ul_on_enter(void* context) {
Nfc* nfc = (Nfc*)context; Nfc* nfc = context;
DOLPHIN_DEED(DolphinDeedNfcEmulate); DOLPHIN_DEED(DolphinDeedNfcEmulate);
// Setup view // Setup view
@@ -26,14 +27,14 @@ void nfc_scene_emulate_mifare_ul_on_enter(void* context) {
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup); view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup);
nfc_worker_start( nfc_worker_start(
nfc->worker, nfc->worker,
NfcWorkerStateEmulateMifareUl, NfcWorkerStateEmulateMifareUltralight,
&nfc->dev->dev_data, &nfc->dev->dev_data,
nfc_emulate_mifare_ul_worker_callback, nfc_emulate_mifare_ul_worker_callback,
nfc); nfc);
} }
bool nfc_scene_emulate_mifare_ul_on_event(void* context, SceneManagerEvent event) { bool nfc_scene_emulate_mifare_ul_on_event(void* context, SceneManagerEvent event) {
Nfc* nfc = (Nfc*)context; Nfc* nfc = context;
bool consumed = false; bool consumed = false;
if(event.type == SceneManagerEventTypeTick) { if(event.type == SceneManagerEventTypeTick) {
@@ -55,11 +56,8 @@ bool nfc_scene_emulate_mifare_ul_on_event(void* context, SceneManagerEvent event
} }
void nfc_scene_emulate_mifare_ul_on_exit(void* context) { void nfc_scene_emulate_mifare_ul_on_exit(void* context) {
Nfc* nfc = (Nfc*)context; Nfc* nfc = context;
// Clear view // Clear view
Popup* popup = nfc->popup; popup_reset(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);
} }

View File

@@ -1,6 +1,8 @@
#include "../nfc_i.h" #include "../nfc_i.h"
#include <dolphin/dolphin.h> #include <dolphin/dolphin.h>
#define NFC_SCENE_EMULATE_UID_LOG_SIZE_MAX (200)
enum { enum {
NfcSceneEmulateUidStateWidget, NfcSceneEmulateUidStateWidget,
NfcSceneEmulateUidStateTextBox, NfcSceneEmulateUidStateTextBox,
@@ -28,7 +30,7 @@ void nfc_emulate_uid_textbox_callback(void* context) {
// Add widget with device name or inform that data received // Add widget with device name or inform that data received
static void nfc_scene_emulate_uid_widget_config(Nfc* nfc, bool data_received) { static void nfc_scene_emulate_uid_widget_config(Nfc* nfc, bool data_received) {
NfcDeviceCommonData* data = &nfc->dev->dev_data.nfc_data; FuriHalNfcDevData* data = &nfc->dev->dev_data.nfc_data;
Widget* widget = nfc->widget; Widget* widget = nfc->widget;
widget_reset(widget); widget_reset(widget);
string_t info_str; string_t info_str;
@@ -45,7 +47,7 @@ static void nfc_scene_emulate_uid_widget_config(Nfc* nfc, bool data_received) {
} }
string_strim(info_str); string_strim(info_str);
widget_add_text_box_element( widget_add_text_box_element(
widget, 56, 43, 70, 21, AlignLeft, AlignTop, string_get_cstr(info_str)); widget, 56, 43, 70, 21, AlignLeft, AlignTop, string_get_cstr(info_str), true);
string_clear(info_str); string_clear(info_str);
if(data_received) { if(data_received) {
widget_add_button_element( widget_add_button_element(
@@ -95,13 +97,15 @@ bool nfc_scene_emulate_uid_on_event(void* context, SceneManagerEvent event) {
nfc_scene_emulate_uid_widget_config(nfc, true); nfc_scene_emulate_uid_widget_config(nfc, true);
} }
// Update TextBox data // Update TextBox data
string_cat_printf(nfc->text_box_store, "R:"); if(string_size(nfc->text_box_store) < NFC_SCENE_EMULATE_UID_LOG_SIZE_MAX) {
for(uint16_t i = 0; i < reader_data->size; i++) { string_cat_printf(nfc->text_box_store, "R:");
string_cat_printf(nfc->text_box_store, " %02X", reader_data->data[i]); for(uint16_t i = 0; i < reader_data->size; i++) {
string_cat_printf(nfc->text_box_store, " %02X", reader_data->data[i]);
}
string_push_back(nfc->text_box_store, '\n');
text_box_set_text(nfc->text_box, string_get_cstr(nfc->text_box_store));
} }
string_push_back(nfc->text_box_store, '\n');
memset(reader_data, 0, sizeof(NfcReaderRequestData)); memset(reader_data, 0, sizeof(NfcReaderRequestData));
text_box_set_text(nfc->text_box, string_get_cstr(nfc->text_box_store));
consumed = true; consumed = true;
} else if(event.event == GuiButtonTypeCenter && state == NfcSceneEmulateUidStateWidget) { } else if(event.event == GuiButtonTypeCenter && state == NfcSceneEmulateUidStateWidget) {
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewTextBox); view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewTextBox);

View File

@@ -1,7 +1,7 @@
#include "../nfc_i.h" #include "../nfc_i.h"
void nfc_scene_field_on_enter(void* context) { void nfc_scene_field_on_enter(void* context) {
Nfc* nfc = (Nfc*)context; Nfc* nfc = context;
furi_hal_nfc_field_on(); furi_hal_nfc_field_on();
@@ -23,12 +23,9 @@ bool nfc_scene_field_on_event(void* context, SceneManagerEvent event) {
} }
void nfc_scene_field_on_exit(void* context) { void nfc_scene_field_on_exit(void* context) {
Nfc* nfc = (Nfc*)context; Nfc* nfc = context;
notification_internal_message(nfc->notifications, &sequence_reset_blue);
Popup* popup = nfc->popup;
popup_reset(popup);
furi_hal_nfc_field_off(); furi_hal_nfc_field_off();
notification_internal_message(nfc->notifications, &sequence_reset_blue);
popup_reset(nfc->popup);
} }

View File

@@ -1,7 +1,7 @@
#include "../nfc_i.h" #include "../nfc_i.h"
void nfc_scene_file_select_on_enter(void* context) { void nfc_scene_file_select_on_enter(void* context) {
Nfc* nfc = (Nfc*)context; Nfc* nfc = context;
// Process file_select return // Process file_select return
if(nfc_file_select(nfc->dev)) { if(nfc_file_select(nfc->dev)) {
scene_manager_next_scene(nfc->scene_manager, NfcSceneSavedMenu); scene_manager_next_scene(nfc->scene_manager, NfcSceneSavedMenu);

View File

@@ -18,13 +18,13 @@ MifareDesfireApplication* nfc_scene_mifare_desfire_app_get_app(Nfc* nfc) {
} }
void nfc_scene_mifare_desfire_app_submenu_callback(void* context, uint32_t index) { void nfc_scene_mifare_desfire_app_submenu_callback(void* context, uint32_t index) {
Nfc* nfc = (Nfc*)context; Nfc* nfc = context;
view_dispatcher_send_custom_event(nfc->view_dispatcher, index); view_dispatcher_send_custom_event(nfc->view_dispatcher, index);
} }
void nfc_scene_mifare_desfire_app_on_enter(void* context) { void nfc_scene_mifare_desfire_app_on_enter(void* context) {
Nfc* nfc = (Nfc*)context; Nfc* nfc = context;
Submenu* submenu = nfc->submenu; Submenu* submenu = nfc->submenu;
MifareDesfireApplication* app = nfc_scene_mifare_desfire_app_get_app(nfc); MifareDesfireApplication* app = nfc_scene_mifare_desfire_app_get_app(nfc);
if(!app) { if(!app) {
@@ -73,7 +73,8 @@ void nfc_scene_mifare_desfire_app_on_enter(void* context) {
} }
bool nfc_scene_mifare_desfire_app_on_event(void* context, SceneManagerEvent event) { bool nfc_scene_mifare_desfire_app_on_event(void* context, SceneManagerEvent event) {
Nfc* nfc = (Nfc*)context; Nfc* nfc = context;
bool consumed = false;
uint32_t state = scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMifareDesfireApp); uint32_t state = scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMifareDesfireApp);
if(event.type == SceneManagerEventTypeCustom) { if(event.type == SceneManagerEventTypeCustom) {
@@ -96,24 +97,24 @@ bool nfc_scene_mifare_desfire_app_on_event(void* context, SceneManagerEvent even
text_box_set_text(text_box, string_get_cstr(nfc->text_box_store)); text_box_set_text(text_box, string_get_cstr(nfc->text_box_store));
scene_manager_set_scene_state(nfc->scene_manager, NfcSceneMifareDesfireApp, state | 1); scene_manager_set_scene_state(nfc->scene_manager, NfcSceneMifareDesfireApp, state | 1);
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewTextBox); view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewTextBox);
return true; consumed = true;
} else if(event.type == SceneManagerEventTypeBack) { } else if(event.type == SceneManagerEventTypeBack) {
if(state & 1) { if(state & 1) {
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu); view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu);
scene_manager_set_scene_state( scene_manager_set_scene_state(
nfc->scene_manager, NfcSceneMifareDesfireApp, state & ~1); nfc->scene_manager, NfcSceneMifareDesfireApp, state & ~1);
return true; consumed = true;
} }
} }
return false; return consumed;
} }
void nfc_scene_mifare_desfire_app_on_exit(void* context) { void nfc_scene_mifare_desfire_app_on_exit(void* context) {
Nfc* nfc = (Nfc*)context; Nfc* nfc = context;
// Clear views
text_box_reset(nfc->text_box); text_box_reset(nfc->text_box);
string_reset(nfc->text_box_store); string_reset(nfc->text_box_store);
submenu_reset(nfc->submenu); submenu_reset(nfc->submenu);
} }

View File

@@ -19,7 +19,7 @@ void nfc_scene_mifare_desfire_data_submenu_callback(void* context, uint32_t inde
} }
void nfc_scene_mifare_desfire_data_on_enter(void* context) { void nfc_scene_mifare_desfire_data_on_enter(void* context) {
Nfc* nfc = (Nfc*)context; Nfc* nfc = context;
Submenu* submenu = nfc->submenu; Submenu* submenu = nfc->submenu;
uint32_t state = scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMifareDesfireData); uint32_t state = scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMifareDesfireData);
MifareDesfireData* data = &nfc->dev->dev_data.mf_df_data; MifareDesfireData* data = &nfc->dev->dev_data.mf_df_data;
@@ -61,7 +61,8 @@ void nfc_scene_mifare_desfire_data_on_enter(void* context) {
} }
bool nfc_scene_mifare_desfire_data_on_event(void* context, SceneManagerEvent event) { bool nfc_scene_mifare_desfire_data_on_event(void* context, SceneManagerEvent event) {
Nfc* nfc = (Nfc*)context; Nfc* nfc = context;
bool consumed = false;
uint32_t state = scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMifareDesfireData); uint32_t state = scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMifareDesfireData);
MifareDesfireData* data = &nfc->dev->dev_data.mf_df_data; MifareDesfireData* data = &nfc->dev->dev_data.mf_df_data;
@@ -76,7 +77,7 @@ bool nfc_scene_mifare_desfire_data_on_event(void* context, SceneManagerEvent eve
nfc->scene_manager, nfc->scene_manager,
NfcSceneMifareDesfireData, NfcSceneMifareDesfireData,
MifareDesfireDataStateItem + SubmenuIndexCardInfo); MifareDesfireDataStateItem + SubmenuIndexCardInfo);
return true; consumed = true;
} else { } else {
uint16_t index = event.event - SubmenuIndexDynamic; uint16_t index = event.event - SubmenuIndexDynamic;
scene_manager_set_scene_state( scene_manager_set_scene_state(
@@ -84,25 +85,25 @@ bool nfc_scene_mifare_desfire_data_on_event(void* context, SceneManagerEvent eve
scene_manager_set_scene_state( scene_manager_set_scene_state(
nfc->scene_manager, NfcSceneMifareDesfireApp, index << 1); nfc->scene_manager, NfcSceneMifareDesfireApp, index << 1);
scene_manager_next_scene(nfc->scene_manager, NfcSceneMifareDesfireApp); scene_manager_next_scene(nfc->scene_manager, NfcSceneMifareDesfireApp);
return true; consumed = true;
} }
} else if(event.type == SceneManagerEventTypeBack) { } else if(event.type == SceneManagerEventTypeBack) {
if(state >= MifareDesfireDataStateItem) { if(state >= MifareDesfireDataStateItem) {
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu); view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu);
scene_manager_set_scene_state( scene_manager_set_scene_state(
nfc->scene_manager, NfcSceneMifareDesfireData, MifareDesfireDataStateMenu); nfc->scene_manager, NfcSceneMifareDesfireData, MifareDesfireDataStateMenu);
return true; consumed = true;
} }
} }
return false; return consumed;
} }
void nfc_scene_mifare_desfire_data_on_exit(void* context) { void nfc_scene_mifare_desfire_data_on_exit(void* context) {
Nfc* nfc = (Nfc*)context; Nfc* nfc = context;
// Clear views
text_box_reset(nfc->text_box); text_box_reset(nfc->text_box);
string_reset(nfc->text_box_store); string_reset(nfc->text_box_store);
submenu_reset(nfc->submenu); submenu_reset(nfc->submenu);
} }

View File

@@ -5,13 +5,13 @@ enum SubmenuIndex {
}; };
void nfc_scene_mifare_desfire_menu_submenu_callback(void* context, uint32_t index) { void nfc_scene_mifare_desfire_menu_submenu_callback(void* context, uint32_t index) {
Nfc* nfc = (Nfc*)context; Nfc* nfc = context;
view_dispatcher_send_custom_event(nfc->view_dispatcher, index); view_dispatcher_send_custom_event(nfc->view_dispatcher, index);
} }
void nfc_scene_mifare_desfire_menu_on_enter(void* context) { void nfc_scene_mifare_desfire_menu_on_enter(void* context) {
Nfc* nfc = (Nfc*)context; Nfc* nfc = context;
Submenu* submenu = nfc->submenu; Submenu* submenu = nfc->submenu;
submenu_add_item( submenu_add_item(
@@ -24,7 +24,8 @@ void nfc_scene_mifare_desfire_menu_on_enter(void* context) {
} }
bool nfc_scene_mifare_desfire_menu_on_event(void* context, SceneManagerEvent event) { bool nfc_scene_mifare_desfire_menu_on_event(void* context, SceneManagerEvent event) {
Nfc* nfc = (Nfc*)context; Nfc* nfc = context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) { if(event.type == SceneManagerEventTypeCustom) {
if(event.event == SubmenuIndexSave) { if(event.event == SubmenuIndexSave) {
@@ -34,15 +35,16 @@ bool nfc_scene_mifare_desfire_menu_on_event(void* context, SceneManagerEvent eve
// Clear device name // Clear device name
nfc_device_set_name(nfc->dev, ""); nfc_device_set_name(nfc->dev, "");
scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveName); scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveName);
return true; consumed = true;
} }
} }
return false; return consumed;
} }
void nfc_scene_mifare_desfire_menu_on_exit(void* context) { void nfc_scene_mifare_desfire_menu_on_exit(void* context) {
Nfc* nfc = (Nfc*)context; Nfc* nfc = context;
// Clear view
submenu_reset(nfc->submenu); submenu_reset(nfc->submenu);
} }

View File

@@ -6,13 +6,13 @@ enum SubmenuIndex {
}; };
void nfc_scene_mifare_ul_menu_submenu_callback(void* context, uint32_t index) { void nfc_scene_mifare_ul_menu_submenu_callback(void* context, uint32_t index) {
Nfc* nfc = (Nfc*)context; Nfc* nfc = context;
view_dispatcher_send_custom_event(nfc->view_dispatcher, index); view_dispatcher_send_custom_event(nfc->view_dispatcher, index);
} }
void nfc_scene_mifare_ul_menu_on_enter(void* context) { void nfc_scene_mifare_ul_menu_on_enter(void* context) {
Nfc* nfc = (Nfc*)context; Nfc* nfc = context;
Submenu* submenu = nfc->submenu; Submenu* submenu = nfc->submenu;
submenu_add_item( submenu_add_item(
@@ -26,7 +26,8 @@ void nfc_scene_mifare_ul_menu_on_enter(void* context) {
} }
bool nfc_scene_mifare_ul_menu_on_event(void* context, SceneManagerEvent event) { bool nfc_scene_mifare_ul_menu_on_event(void* context, SceneManagerEvent event) {
Nfc* nfc = (Nfc*)context; Nfc* nfc = context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) { if(event.type == SceneManagerEventTypeCustom) {
if(event.event == SubmenuIndexSave) { if(event.event == SubmenuIndexSave) {
@@ -36,23 +37,24 @@ bool nfc_scene_mifare_ul_menu_on_event(void* context, SceneManagerEvent event) {
// Clear device name // Clear device name
nfc_device_set_name(nfc->dev, ""); nfc_device_set_name(nfc->dev, "");
scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveName); scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveName);
return true; consumed = true;
} else if(event.event == SubmenuIndexEmulate) { } else if(event.event == SubmenuIndexEmulate) {
scene_manager_set_scene_state( scene_manager_set_scene_state(
nfc->scene_manager, NfcSceneMifareUlMenu, SubmenuIndexEmulate); nfc->scene_manager, NfcSceneMifareUlMenu, SubmenuIndexEmulate);
scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateMifareUl); scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateMifareUl);
return true; consumed = true;
} }
} else if(event.type == SceneManagerEventTypeBack) { } else if(event.type == SceneManagerEventTypeBack) {
return scene_manager_search_and_switch_to_previous_scene( consumed =
nfc->scene_manager, NfcSceneStart); scene_manager_search_and_switch_to_previous_scene(nfc->scene_manager, NfcSceneStart);
} }
return false; return consumed;
} }
void nfc_scene_mifare_ul_menu_on_exit(void* context) { void nfc_scene_mifare_ul_menu_on_exit(void* context) {
Nfc* nfc = (Nfc*)context; Nfc* nfc = context;
// Clear view
submenu_reset(nfc->submenu); submenu_reset(nfc->submenu);
} }

View File

@@ -1,42 +0,0 @@
#include "../nfc_i.h"
void nfc_scene_not_implemented_dialog_callback(DialogExResult result, void* context) {
Nfc* nfc = (Nfc*)context;
view_dispatcher_send_custom_event(nfc->view_dispatcher, result);
}
void nfc_scene_not_implemented_on_enter(void* context) {
Nfc* nfc = (Nfc*)context;
// TODO Set data from worker
DialogEx* dialog_ex = nfc->dialog_ex;
dialog_ex_set_left_button_text(dialog_ex, "Back");
dialog_ex_set_header(dialog_ex, "Not implemented", 60, 24, AlignCenter, AlignCenter);
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->view_dispatcher, NfcViewDialogEx);
}
bool nfc_scene_not_implemented_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);
}
}
return false;
}
void nfc_scene_not_implemented_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_left_button_text(dialog_ex, NULL);
dialog_ex_set_result_callback(dialog_ex, NULL);
dialog_ex_set_context(dialog_ex, NULL);
}

View File

@@ -2,12 +2,12 @@
#include <dolphin/dolphin.h> #include <dolphin/dolphin.h>
void nfc_read_card_worker_callback(NfcWorkerEvent event, void* context) { void nfc_read_card_worker_callback(NfcWorkerEvent event, void* context) {
Nfc* nfc = (Nfc*)context; Nfc* nfc = context;
view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventWorkerExit); view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventWorkerExit);
} }
void nfc_scene_read_card_on_enter(void* context) { void nfc_scene_read_card_on_enter(void* context) {
Nfc* nfc = (Nfc*)context; Nfc* nfc = context;
DOLPHIN_DEED(DolphinDeedNfcRead); DOLPHIN_DEED(DolphinDeedNfcRead);
// Setup view // Setup view
@@ -22,29 +22,26 @@ void nfc_scene_read_card_on_enter(void* context) {
} }
bool nfc_scene_read_card_on_event(void* context, SceneManagerEvent event) { bool nfc_scene_read_card_on_event(void* context, SceneManagerEvent event) {
Nfc* nfc = (Nfc*)context; Nfc* nfc = context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) { if(event.type == SceneManagerEventTypeCustom) {
if(event.event == NfcCustomEventWorkerExit) { if(event.event == NfcCustomEventWorkerExit) {
scene_manager_next_scene(nfc->scene_manager, NfcSceneReadCardSuccess); scene_manager_next_scene(nfc->scene_manager, NfcSceneReadCardSuccess);
return true; consumed = true;
} }
} else if(event.type == SceneManagerEventTypeTick) { } else if(event.type == SceneManagerEventTypeTick) {
notification_message(nfc->notifications, &sequence_blink_blue_10); notification_message(nfc->notifications, &sequence_blink_blue_10);
return true; consumed = true;
} }
return false; return consumed;
} }
void nfc_scene_read_card_on_exit(void* context) { void nfc_scene_read_card_on_exit(void* context) {
Nfc* nfc = (Nfc*)context; Nfc* nfc = context;
// Stop worker // Stop worker
nfc_worker_stop(nfc->worker); nfc_worker_stop(nfc->worker);
// Clear view // Clear view
Popup* popup = nfc->popup; popup_reset(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);
} }

View File

@@ -7,13 +7,15 @@ void nfc_scene_read_card_success_widget_callback(
void* context) { void* context) {
furi_assert(context); furi_assert(context);
Nfc* nfc = context; Nfc* nfc = context;
if(type == InputTypeShort) { if(type == InputTypeShort) {
view_dispatcher_send_custom_event(nfc->view_dispatcher, result); view_dispatcher_send_custom_event(nfc->view_dispatcher, result);
} }
} }
void nfc_scene_read_card_success_on_enter(void* context) { void nfc_scene_read_card_success_on_enter(void* context) {
Nfc* nfc = (Nfc*)context; Nfc* nfc = context;
string_t data_str; string_t data_str;
string_t uid_str; string_t uid_str;
string_init(data_str); string_init(data_str);
@@ -24,9 +26,9 @@ void nfc_scene_read_card_success_on_enter(void* context) {
notification_message(nfc->notifications, &sequence_success); notification_message(nfc->notifications, &sequence_success);
// Setup view // Setup view
NfcDeviceCommonData* data = &nfc->dev->dev_data.nfc_data; FuriHalNfcDevData* data = &nfc->dev->dev_data.nfc_data;
Widget* widget = nfc->widget; Widget* widget = nfc->widget;
string_set_str(data_str, nfc_get_dev_type(data->device)); string_set_str(data_str, nfc_get_dev_type(data->type));
string_set_str(uid_str, "UID:"); string_set_str(uid_str, "UID:");
for(uint8_t i = 0; i < data->uid_len; i++) { for(uint8_t i = 0; i < data->uid_len; i++) {
string_cat_printf(uid_str, " %02X", data->uid[i]); string_cat_printf(uid_str, " %02X", data->uid[i]);
@@ -34,7 +36,7 @@ void nfc_scene_read_card_success_on_enter(void* context) {
widget_add_button_element( widget_add_button_element(
widget, GuiButtonTypeLeft, "Retry", nfc_scene_read_card_success_widget_callback, nfc); widget, GuiButtonTypeLeft, "Retry", nfc_scene_read_card_success_widget_callback, nfc);
if(data->device == NfcDeviceNfca) { if(data->type == FuriHalNfcTypeA) {
widget_add_button_element( widget_add_button_element(
widget, GuiButtonTypeRight, "More", nfc_scene_read_card_success_widget_callback, nfc); widget, GuiButtonTypeRight, "More", nfc_scene_read_card_success_widget_callback, nfc);
widget_add_icon_element(widget, 8, 13, &I_Medium_chip_22x21); widget_add_icon_element(widget, 8, 13, &I_Medium_chip_22x21);
@@ -44,7 +46,7 @@ void nfc_scene_read_card_success_on_enter(void* context) {
string_printf( string_printf(
data_str, data_str,
"%s\nATQA: %02X%02X SAK: %02X", "%s\nATQA: %02X%02X SAK: %02X",
nfc_guess_protocol(data->protocol), nfc_guess_protocol(nfc->dev->dev_data.protocol),
data->atqa[0], data->atqa[0],
data->atqa[1], data->atqa[1],
data->sak); data->sak);
@@ -66,14 +68,14 @@ void nfc_scene_read_card_success_on_enter(void* context) {
} }
bool nfc_scene_read_card_success_on_event(void* context, SceneManagerEvent event) { bool nfc_scene_read_card_success_on_event(void* context, SceneManagerEvent event) {
Nfc* nfc = (Nfc*)context; Nfc* nfc = context;
NfcDeviceCommonData* data = &nfc->dev->dev_data.nfc_data; FuriHalNfcDevData* data = &nfc->dev->dev_data.nfc_data;
bool consumed = false; bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) { if(event.type == SceneManagerEventTypeCustom) {
if(event.event == GuiButtonTypeLeft) { if(event.event == GuiButtonTypeLeft) {
consumed = scene_manager_previous_scene(nfc->scene_manager); consumed = scene_manager_previous_scene(nfc->scene_manager);
} else if(data->device == NfcDeviceNfca && event.event == GuiButtonTypeRight) { } else if(data->type == FuriHalNfcTypeA && event.event == GuiButtonTypeRight) {
// Clear device name // Clear device name
nfc_device_set_name(nfc->dev, ""); nfc_device_set_name(nfc->dev, "");
scene_manager_next_scene(nfc->scene_manager, NfcSceneCardMenu); scene_manager_next_scene(nfc->scene_manager, NfcSceneCardMenu);
@@ -84,6 +86,8 @@ bool nfc_scene_read_card_success_on_event(void* context, SceneManagerEvent event
} }
void nfc_scene_read_card_success_on_exit(void* context) { void nfc_scene_read_card_success_on_exit(void* context) {
Nfc* nfc = (Nfc*)context; Nfc* nfc = context;
// Clear view
widget_reset(nfc->widget); widget_reset(nfc->widget);
} }

View File

@@ -2,12 +2,12 @@
#include <dolphin/dolphin.h> #include <dolphin/dolphin.h>
void nfc_read_emv_app_worker_callback(NfcWorkerEvent event, void* context) { void nfc_read_emv_app_worker_callback(NfcWorkerEvent event, void* context) {
Nfc* nfc = (Nfc*)context; Nfc* nfc = context;
view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventWorkerExit); view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventWorkerExit);
} }
void nfc_scene_read_emv_app_on_enter(void* context) { void nfc_scene_read_emv_app_on_enter(void* context) {
Nfc* nfc = (Nfc*)context; Nfc* nfc = context;
DOLPHIN_DEED(DolphinDeedNfcRead); DOLPHIN_DEED(DolphinDeedNfcRead);
// Setup view // Setup view
@@ -26,31 +26,30 @@ void nfc_scene_read_emv_app_on_enter(void* context) {
} }
bool nfc_scene_read_emv_app_on_event(void* context, SceneManagerEvent event) { bool nfc_scene_read_emv_app_on_event(void* context, SceneManagerEvent event) {
Nfc* nfc = (Nfc*)context; Nfc* nfc = context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) { if(event.type == SceneManagerEventTypeCustom) {
if(event.event == NfcCustomEventWorkerExit) { if(event.event == NfcCustomEventWorkerExit) {
scene_manager_set_scene_state( scene_manager_set_scene_state(
nfc->scene_manager, NfcSceneReadEmvAppSuccess, NFC_SEND_NOTIFICATION_TRUE); nfc->scene_manager, NfcSceneReadEmvAppSuccess, NFC_SEND_NOTIFICATION_TRUE);
scene_manager_next_scene(nfc->scene_manager, NfcSceneReadEmvAppSuccess); scene_manager_next_scene(nfc->scene_manager, NfcSceneReadEmvAppSuccess);
return true; consumed = true;
} }
} else if(event.type == SceneManagerEventTypeTick) { } else if(event.type == SceneManagerEventTypeTick) {
notification_message(nfc->notifications, &sequence_blink_blue_10); notification_message(nfc->notifications, &sequence_blink_blue_10);
return true; consumed = true;
} }
return false;
return consumed;
} }
void nfc_scene_read_emv_app_on_exit(void* context) { void nfc_scene_read_emv_app_on_exit(void* context) {
Nfc* nfc = (Nfc*)context; Nfc* nfc = context;
// Stop worker // Stop worker
nfc_worker_stop(nfc->worker); nfc_worker_stop(nfc->worker);
// Clear view // Clear view
Popup* popup = nfc->popup; popup_reset(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);
} }

View File

@@ -2,27 +2,38 @@
#include "../helpers/nfc_emv_parser.h" #include "../helpers/nfc_emv_parser.h"
#include <dolphin/dolphin.h> #include <dolphin/dolphin.h>
#define NFC_SCENE_READ_SUCCESS_SHIFT " " void nfc_scene_read_emv_app_widget_callback(GuiButtonType result, InputType type, void* context) {
Nfc* nfc = context;
void nfc_scene_read_emv_app_success_dialog_callback(DialogExResult result, void* context) { if(type == InputTypeShort) {
Nfc* nfc = (Nfc*)context; view_dispatcher_send_custom_event(nfc->view_dispatcher, result);
}
view_dispatcher_send_custom_event(nfc->view_dispatcher, result);
} }
void nfc_scene_read_emv_app_success_on_enter(void* context) { void nfc_scene_read_emv_app_success_on_enter(void* context) {
Nfc* nfc = (Nfc*)context; Nfc* nfc = context;
DOLPHIN_DEED(DolphinDeedNfcReadSuccess); DOLPHIN_DEED(DolphinDeedNfcReadSuccess);
// Setup view // Setup view
NfcDeviceCommonData* nfc_data = &nfc->dev->dev_data.nfc_data; FuriHalNfcDevData* nfc_data = &nfc->dev->dev_data.nfc_data;
NfcEmvData* emv_data = &nfc->dev->dev_data.emv_data; EmvData* emv_data = &nfc->dev->dev_data.emv_data;
DialogEx* dialog_ex = nfc->dialog_ex; Widget* widget = nfc->widget;
dialog_ex_set_left_button_text(dialog_ex, "Retry"); widget_add_button_element(
dialog_ex_set_right_button_text(dialog_ex, "Run app"); widget, GuiButtonTypeLeft, "Retry", nfc_scene_read_emv_app_widget_callback, nfc);
dialog_ex_set_header(dialog_ex, "Found EMV App", 36, 8, AlignLeft, AlignCenter); widget_add_button_element(
dialog_ex_set_icon(dialog_ex, 8, 13, &I_Medium_chip_22x21); widget, GuiButtonTypeRight, "Run app", nfc_scene_read_emv_app_widget_callback, nfc);
// Display UID and AID widget_add_string_element(widget, 36, 5, AlignLeft, AlignTop, FontPrimary, "Found EMV App");
widget_add_icon_element(widget, 8, 5, &I_Medium_chip_22x21);
// Display UID
string_t temp_str;
string_init_printf(temp_str, "UID:");
for(size_t i = 0; i < nfc_data->uid_len; i++) {
string_cat_printf(temp_str, " %02X", nfc_data->uid[i]);
}
widget_add_string_element(
widget, 36, 18, AlignLeft, AlignTop, FontSecondary, string_get_cstr(temp_str));
string_reset(temp_str);
// Display application
string_printf(temp_str, "App: ");
string_t aid; string_t aid;
string_init(aid); string_init(aid);
bool aid_found = bool aid_found =
@@ -32,19 +43,11 @@ void nfc_scene_read_emv_app_success_on_enter(void* context) {
string_cat_printf(aid, "%02X", emv_data->aid[i]); string_cat_printf(aid, "%02X", emv_data->aid[i]);
} }
} }
nfc_text_store_set( string_cat(temp_str, aid);
nfc, widget_add_string_element(
NFC_SCENE_READ_SUCCESS_SHIFT "UID: %02X %02X %02X %02X \n" NFC_SCENE_READ_SUCCESS_SHIFT widget, 7, 29, AlignLeft, AlignTop, FontSecondary, string_get_cstr(temp_str));
"Application:\n%s", string_clear(temp_str);
nfc_data->uid[0],
nfc_data->uid[1],
nfc_data->uid[2],
nfc_data->uid[3],
string_get_cstr(aid));
string_clear(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);
// Send notification // Send notification
if(scene_manager_get_scene_state(nfc->scene_manager, NfcSceneReadEmvAppSuccess) == if(scene_manager_get_scene_state(nfc->scene_manager, NfcSceneReadEmvAppSuccess) ==
@@ -54,32 +57,27 @@ void nfc_scene_read_emv_app_success_on_enter(void* context) {
nfc->scene_manager, NfcSceneReadEmvAppSuccess, NFC_SEND_NOTIFICATION_FALSE); nfc->scene_manager, NfcSceneReadEmvAppSuccess, NFC_SEND_NOTIFICATION_FALSE);
} }
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewDialogEx); view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget);
} }
bool nfc_scene_read_emv_app_success_on_event(void* context, SceneManagerEvent event) { bool nfc_scene_read_emv_app_success_on_event(void* context, SceneManagerEvent event) {
Nfc* nfc = (Nfc*)context; Nfc* nfc = context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) { if(event.type == SceneManagerEventTypeCustom) {
if(event.event == DialogExResultLeft) { if(event.event == GuiButtonTypeLeft) {
return scene_manager_previous_scene(nfc->scene_manager); consumed = scene_manager_previous_scene(nfc->scene_manager);
} else if(event.event == DialogExResultRight) { } else if(event.event == GuiButtonTypeRight) {
scene_manager_next_scene(nfc->scene_manager, NfcSceneRunEmvAppConfirm); scene_manager_next_scene(nfc->scene_manager, NfcSceneRunEmvAppConfirm);
return true; consumed = true;
} }
} }
return false; return consumed;
} }
void nfc_scene_read_emv_app_success_on_exit(void* context) { void nfc_scene_read_emv_app_success_on_exit(void* context) {
Nfc* nfc = (Nfc*)context; Nfc* nfc = context;
DialogEx* dialog_ex = nfc->dialog_ex; // Clear views
dialog_ex_set_header(dialog_ex, NULL, 0, 0, AlignCenter, AlignCenter); widget_reset(nfc->widget);
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);
} }

View File

@@ -2,12 +2,12 @@
#include <dolphin/dolphin.h> #include <dolphin/dolphin.h>
void nfc_read_emv_data_worker_callback(NfcWorkerEvent event, void* context) { void nfc_read_emv_data_worker_callback(NfcWorkerEvent event, void* context) {
Nfc* nfc = (Nfc*)context; Nfc* nfc = context;
view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventWorkerExit); view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventWorkerExit);
} }
void nfc_scene_read_emv_data_on_enter(void* context) { void nfc_scene_read_emv_data_on_enter(void* context) {
Nfc* nfc = (Nfc*)context; Nfc* nfc = context;
DOLPHIN_DEED(DolphinDeedNfcRead); DOLPHIN_DEED(DolphinDeedNfcRead);
// Setup view // Setup view
@@ -21,38 +21,35 @@ void nfc_scene_read_emv_data_on_enter(void* context) {
// Start worker // Start worker
nfc_worker_start( nfc_worker_start(
nfc->worker, nfc->worker,
NfcWorkerStateReadEMV, NfcWorkerStateReadEMVData,
&nfc->dev->dev_data, &nfc->dev->dev_data,
nfc_read_emv_data_worker_callback, nfc_read_emv_data_worker_callback,
nfc); nfc);
} }
bool nfc_scene_read_emv_data_on_event(void* context, SceneManagerEvent event) { bool nfc_scene_read_emv_data_on_event(void* context, SceneManagerEvent event) {
Nfc* nfc = (Nfc*)context; Nfc* nfc = context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) { if(event.type == SceneManagerEventTypeCustom) {
if(event.event == NfcCustomEventWorkerExit) { if(event.event == NfcCustomEventWorkerExit) {
scene_manager_set_scene_state( scene_manager_set_scene_state(
nfc->scene_manager, NfcSceneReadEmvDataSuccess, NFC_SEND_NOTIFICATION_TRUE); nfc->scene_manager, NfcSceneReadEmvDataSuccess, NFC_SEND_NOTIFICATION_TRUE);
scene_manager_next_scene(nfc->scene_manager, NfcSceneReadEmvDataSuccess); scene_manager_next_scene(nfc->scene_manager, NfcSceneReadEmvDataSuccess);
return true; consumed = true;
} }
} else if(event.type == SceneManagerEventTypeTick) { } else if(event.type == SceneManagerEventTypeTick) {
notification_message(nfc->notifications, &sequence_blink_blue_10); notification_message(nfc->notifications, &sequence_blink_blue_10);
return true; consumed = true;
} }
return false; return consumed;
} }
void nfc_scene_read_emv_data_on_exit(void* context) { void nfc_scene_read_emv_data_on_exit(void* context) {
Nfc* nfc = (Nfc*)context; Nfc* nfc = context;
// Stop worker // Stop worker
nfc_worker_stop(nfc->worker); nfc_worker_stop(nfc->worker);
// Clear view // Clear view
Popup* popup = nfc->popup; popup_reset(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);
} }

View File

@@ -6,16 +6,16 @@ void nfc_scene_read_emv_data_success_widget_callback(
GuiButtonType result, GuiButtonType result,
InputType type, InputType type,
void* context) { void* context) {
Nfc* nfc = (Nfc*)context; Nfc* nfc = context;
if(type == InputTypeShort) { if(type == InputTypeShort) {
view_dispatcher_send_custom_event(nfc->view_dispatcher, result); view_dispatcher_send_custom_event(nfc->view_dispatcher, result);
} }
} }
void nfc_scene_read_emv_data_success_on_enter(void* context) { void nfc_scene_read_emv_data_success_on_enter(void* context) {
Nfc* nfc = (Nfc*)context; Nfc* nfc = context;
NfcEmvData* emv_data = &nfc->dev->dev_data.emv_data; EmvData* emv_data = &nfc->dev->dev_data.emv_data;
NfcDeviceCommonData* nfc_data = &nfc->dev->dev_data.nfc_data; FuriHalNfcDevData* nfc_data = &nfc->dev->dev_data.nfc_data;
DOLPHIN_DEED(DolphinDeedNfcReadSuccess); DOLPHIN_DEED(DolphinDeedNfcReadSuccess);
// Setup Custom Widget view // Setup Custom Widget view
@@ -78,25 +78,23 @@ void nfc_scene_read_emv_data_success_on_enter(void* context) {
string_clear(disp_currency); string_clear(disp_currency);
} }
string_clear(currency_name); string_clear(currency_name);
char temp_str[32];
// Add ATQA // Add ATQA
char atqa_str[16]; snprintf(temp_str, sizeof(temp_str), "ATQA: %02X%02X", nfc_data->atqa[0], nfc_data->atqa[1]);
snprintf(atqa_str, sizeof(atqa_str), "ATQA: %02X%02X", nfc_data->atqa[0], nfc_data->atqa[1]); widget_add_string_element(nfc->widget, 121, 32, AlignRight, AlignTop, FontSecondary, temp_str);
widget_add_string_element(nfc->widget, 121, 32, AlignRight, AlignTop, FontSecondary, atqa_str);
// Add UID // Add UID
char uid_str[32];
snprintf( snprintf(
uid_str, temp_str,
sizeof(uid_str), sizeof(temp_str),
"UID: %02X %02X %02X %02X", "UID: %02X %02X %02X %02X",
nfc_data->uid[0], nfc_data->uid[0],
nfc_data->uid[1], nfc_data->uid[1],
nfc_data->uid[2], nfc_data->uid[2],
nfc_data->uid[3]); nfc_data->uid[3]);
widget_add_string_element(nfc->widget, 7, 42, AlignLeft, AlignTop, FontSecondary, uid_str); widget_add_string_element(nfc->widget, 7, 42, AlignLeft, AlignTop, FontSecondary, temp_str);
// Add SAK // Add SAK
char sak_str[16]; snprintf(temp_str, sizeof(temp_str), "SAK: %02X", nfc_data->sak);
snprintf(sak_str, sizeof(sak_str), "SAK: %02X", nfc_data->sak); widget_add_string_element(nfc->widget, 121, 42, AlignRight, AlignTop, FontSecondary, temp_str);
widget_add_string_element(nfc->widget, 121, 42, AlignRight, AlignTop, FontSecondary, sak_str);
// Add expiration date // Add expiration date
if(emv_data->exp_mon) { if(emv_data->exp_mon) {
char exp_str[16]; char exp_str[16];
@@ -117,28 +115,30 @@ void nfc_scene_read_emv_data_success_on_enter(void* context) {
} }
bool nfc_scene_read_emv_data_success_on_event(void* context, SceneManagerEvent event) { bool nfc_scene_read_emv_data_success_on_event(void* context, SceneManagerEvent event) {
Nfc* nfc = (Nfc*)context; Nfc* nfc = context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) { if(event.type == SceneManagerEventTypeCustom) {
if(event.event == GuiButtonTypeLeft) { if(event.event == GuiButtonTypeLeft) {
return scene_manager_search_and_switch_to_previous_scene( consumed = scene_manager_search_and_switch_to_previous_scene(
nfc->scene_manager, NfcSceneReadEmvAppSuccess); nfc->scene_manager, NfcSceneReadEmvAppSuccess);
} else if(event.event == GuiButtonTypeRight) { } else if(event.event == GuiButtonTypeRight) {
// Clear device name // Clear device name
nfc_device_set_name(nfc->dev, ""); nfc_device_set_name(nfc->dev, "");
nfc->dev->format = NfcDeviceSaveFormatBankCard; nfc->dev->format = NfcDeviceSaveFormatBankCard;
scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveName); scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveName);
return true; consumed = true;
} }
} else if(event.type == SceneManagerEventTypeBack) { } else if(event.type == SceneManagerEventTypeBack) {
return scene_manager_search_and_switch_to_previous_scene( consumed = scene_manager_search_and_switch_to_previous_scene(
nfc->scene_manager, NfcSceneReadEmvAppSuccess); nfc->scene_manager, NfcSceneReadEmvAppSuccess);
} }
return false; return consumed;
} }
void nfc_scene_read_emv_data_success_on_exit(void* context) { void nfc_scene_read_emv_data_success_on_exit(void* context) {
Nfc* nfc = (Nfc*)context; Nfc* nfc = context;
// Clear view
widget_reset(nfc->widget); widget_reset(nfc->widget);
} }

View File

@@ -2,12 +2,12 @@
#include <dolphin/dolphin.h> #include <dolphin/dolphin.h>
void nfc_read_mifare_desfire_worker_callback(NfcWorkerEvent event, void* context) { void nfc_read_mifare_desfire_worker_callback(NfcWorkerEvent event, void* context) {
Nfc* nfc = (Nfc*)context; Nfc* nfc = context;
view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventWorkerExit); view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventWorkerExit);
} }
void nfc_scene_read_mifare_desfire_on_enter(void* context) { void nfc_scene_read_mifare_desfire_on_enter(void* context) {
Nfc* nfc = (Nfc*)context; Nfc* nfc = context;
DOLPHIN_DEED(DolphinDeedNfcRead); DOLPHIN_DEED(DolphinDeedNfcRead);
// Setup view // Setup view
@@ -26,31 +26,28 @@ void nfc_scene_read_mifare_desfire_on_enter(void* context) {
} }
bool nfc_scene_read_mifare_desfire_on_event(void* context, SceneManagerEvent event) { bool nfc_scene_read_mifare_desfire_on_event(void* context, SceneManagerEvent event) {
Nfc* nfc = (Nfc*)context; Nfc* nfc = context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) { if(event.type == SceneManagerEventTypeCustom) {
if(event.event == NfcCustomEventWorkerExit) { if(event.event == NfcCustomEventWorkerExit) {
notification_message(nfc->notifications, &sequence_success); notification_message(nfc->notifications, &sequence_success);
scene_manager_next_scene(nfc->scene_manager, NfcSceneReadMifareDesfireSuccess); scene_manager_next_scene(nfc->scene_manager, NfcSceneReadMifareDesfireSuccess);
return true; consumed = true;
} }
} else if(event.type == SceneManagerEventTypeTick) { } else if(event.type == SceneManagerEventTypeTick) {
notification_message(nfc->notifications, &sequence_blink_blue_10); notification_message(nfc->notifications, &sequence_blink_blue_10);
DOLPHIN_DEED(DolphinDeedNfcReadSuccess); DOLPHIN_DEED(DolphinDeedNfcReadSuccess);
return true; consumed = true;
} }
return false; return consumed;
} }
void nfc_scene_read_mifare_desfire_on_exit(void* context) { void nfc_scene_read_mifare_desfire_on_exit(void* context) {
Nfc* nfc = (Nfc*)context; Nfc* nfc = context;
// Stop worker // Stop worker
nfc_worker_stop(nfc->worker); nfc_worker_stop(nfc->worker);
// Clear view // Clear view
Popup* popup = nfc->popup; popup_reset(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);
} }

View File

@@ -9,13 +9,13 @@ enum {
}; };
void nfc_scene_read_mifare_desfire_success_dialog_callback(DialogExResult result, void* context) { void nfc_scene_read_mifare_desfire_success_dialog_callback(DialogExResult result, void* context) {
Nfc* nfc = (Nfc*)context; Nfc* nfc = context;
view_dispatcher_send_custom_event(nfc->view_dispatcher, result); view_dispatcher_send_custom_event(nfc->view_dispatcher, result);
} }
void nfc_scene_read_mifare_desfire_success_on_enter(void* context) { void nfc_scene_read_mifare_desfire_success_on_enter(void* context) {
Nfc* nfc = (Nfc*)context; Nfc* nfc = context;
MifareDesfireData* data = &nfc->dev->dev_data.mf_df_data; MifareDesfireData* data = &nfc->dev->dev_data.mf_df_data;
DialogEx* dialog_ex = nfc->dialog_ex; DialogEx* dialog_ex = nfc->dialog_ex;
@@ -67,9 +67,9 @@ void nfc_scene_read_mifare_desfire_success_on_enter(void* context) {
bool nfc_scene_read_mifare_desfire_success_on_event(void* context, SceneManagerEvent event) { bool nfc_scene_read_mifare_desfire_success_on_event(void* context, SceneManagerEvent event) {
Nfc* nfc = context; Nfc* nfc = context;
bool consumed = false;
uint32_t state = uint32_t state =
scene_manager_get_scene_state(nfc->scene_manager, NfcSceneReadMifareDesfireSuccess); scene_manager_get_scene_state(nfc->scene_manager, NfcSceneReadMifareDesfireSuccess);
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) { if(event.type == SceneManagerEventTypeCustom) {
if(state == ReadMifareDesfireSuccessStateShowUID && event.event == DialogExResultLeft) { if(state == ReadMifareDesfireSuccessStateShowUID && event.event == DialogExResultLeft) {
@@ -98,9 +98,8 @@ bool nfc_scene_read_mifare_desfire_success_on_event(void* context, SceneManagerE
} }
void nfc_scene_read_mifare_desfire_success_on_exit(void* context) { void nfc_scene_read_mifare_desfire_success_on_exit(void* context) {
Nfc* nfc = (Nfc*)context; Nfc* nfc = context;
// Clean dialog // Clean dialog
DialogEx* dialog_ex = nfc->dialog_ex; dialog_ex_reset(nfc->dialog_ex);
dialog_ex_reset(dialog_ex);
} }

View File

@@ -19,7 +19,7 @@ void nfc_scene_read_mifare_ul_on_enter(void* context) {
// Start worker // Start worker
nfc_worker_start( nfc_worker_start(
nfc->worker, nfc->worker,
NfcWorkerStateReadMifareUl, NfcWorkerStateReadMifareUltralight,
&nfc->dev->dev_data, &nfc->dev->dev_data,
nfc_read_mifare_ul_worker_callback, nfc_read_mifare_ul_worker_callback,
nfc); nfc);
@@ -43,6 +43,7 @@ bool nfc_scene_read_mifare_ul_on_event(void* context, SceneManagerEvent event) {
void nfc_scene_read_mifare_ul_on_exit(void* context) { void nfc_scene_read_mifare_ul_on_exit(void* context) {
Nfc* nfc = context; Nfc* nfc = context;
// Stop worker // Stop worker
nfc_worker_stop(nfc->worker); nfc_worker_stop(nfc->worker);
// Clear view // Clear view

View File

@@ -9,21 +9,21 @@ enum {
}; };
void nfc_scene_read_mifare_ul_success_dialog_callback(DialogExResult result, void* context) { void nfc_scene_read_mifare_ul_success_dialog_callback(DialogExResult result, void* context) {
Nfc* nfc = (Nfc*)context; Nfc* nfc = context;
view_dispatcher_send_custom_event(nfc->view_dispatcher, result); view_dispatcher_send_custom_event(nfc->view_dispatcher, result);
} }
void nfc_scene_read_mifare_ul_success_on_enter(void* context) { void nfc_scene_read_mifare_ul_success_on_enter(void* context) {
Nfc* nfc = (Nfc*)context; Nfc* nfc = context;
DOLPHIN_DEED(DolphinDeedNfcReadSuccess); DOLPHIN_DEED(DolphinDeedNfcReadSuccess);
// Send notification // Send notification
notification_message(nfc->notifications, &sequence_success); notification_message(nfc->notifications, &sequence_success);
// Setup dialog view // Setup dialog view
NfcDeviceCommonData* data = &nfc->dev->dev_data.nfc_data; FuriHalNfcDevData* data = &nfc->dev->dev_data.nfc_data;
MifareUlData* mf_ul_data = &nfc->dev->dev_data.mf_ul_data; MfUltralightData* mf_ul_data = &nfc->dev->dev_data.mf_ul_data;
DialogEx* dialog_ex = nfc->dialog_ex; DialogEx* dialog_ex = nfc->dialog_ex;
dialog_ex_set_left_button_text(dialog_ex, "Retry"); dialog_ex_set_left_button_text(dialog_ex, "Retry");
dialog_ex_set_right_button_text(dialog_ex, "More"); dialog_ex_set_right_button_text(dialog_ex, "More");
@@ -69,9 +69,9 @@ void nfc_scene_read_mifare_ul_success_on_enter(void* context) {
bool nfc_scene_read_mifare_ul_success_on_event(void* context, SceneManagerEvent event) { bool nfc_scene_read_mifare_ul_success_on_event(void* context, SceneManagerEvent event) {
Nfc* nfc = context; Nfc* nfc = context;
bool consumed = false;
uint32_t state = uint32_t state =
scene_manager_get_scene_state(nfc->scene_manager, NfcSceneReadMifareUlSuccess); scene_manager_get_scene_state(nfc->scene_manager, NfcSceneReadMifareUlSuccess);
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) { if(event.type == SceneManagerEventTypeCustom) {
if(state == ReadMifareUlStateShowUID && event.event == DialogExResultLeft) { if(state == ReadMifareUlStateShowUID && event.event == DialogExResultLeft) {
@@ -99,14 +99,10 @@ bool nfc_scene_read_mifare_ul_success_on_event(void* context, SceneManagerEvent
} }
void nfc_scene_read_mifare_ul_success_on_exit(void* context) { void nfc_scene_read_mifare_ul_success_on_exit(void* context) {
Nfc* nfc = (Nfc*)context; Nfc* nfc = context;
// Clean dialog // Clean views
DialogEx* dialog_ex = nfc->dialog_ex; dialog_ex_reset(nfc->dialog_ex);
dialog_ex_reset(dialog_ex); text_box_reset(nfc->text_box);
// Clean TextBox
TextBox* text_box = nfc->text_box;
text_box_reset(text_box);
string_reset(nfc->text_box_store); string_reset(nfc->text_box_store);
} }

View File

@@ -1,12 +1,12 @@
#include "../nfc_i.h" #include "../nfc_i.h"
void nfc_scene_restore_original_popup_callback(void* context) { void nfc_scene_restore_original_popup_callback(void* context) {
Nfc* nfc = (Nfc*)context; Nfc* nfc = context;
view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventViewExit); view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventViewExit);
} }
void nfc_scene_restore_original_on_enter(void* context) { void nfc_scene_restore_original_on_enter(void* context) {
Nfc* nfc = (Nfc*)context; Nfc* nfc = context;
// Setup view // Setup view
Popup* popup = nfc->popup; Popup* popup = nfc->popup;
@@ -20,7 +20,7 @@ void nfc_scene_restore_original_on_enter(void* context) {
} }
bool nfc_scene_restore_original_on_event(void* context, SceneManagerEvent event) { bool nfc_scene_restore_original_on_event(void* context, SceneManagerEvent event) {
Nfc* nfc = (Nfc*)context; Nfc* nfc = context;
bool consumed = false; bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) { if(event.type == SceneManagerEventTypeCustom) {
@@ -32,15 +32,8 @@ bool nfc_scene_restore_original_on_event(void* context, SceneManagerEvent event)
} }
void nfc_scene_restore_original_on_exit(void* context) { void nfc_scene_restore_original_on_exit(void* context) {
Nfc* nfc = (Nfc*)context; Nfc* nfc = context;
// Clear view // Clear view
Popup* popup = nfc->popup; popup_reset(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);
} }

View File

@@ -1,15 +1,13 @@
#include "../nfc_i.h" #include "../nfc_i.h"
#define NFC_SCENE_READ_SUCCESS_SHIFT " "
void nfc_scene_run_emv_app_confirm_dialog_callback(DialogExResult result, void* context) { void nfc_scene_run_emv_app_confirm_dialog_callback(DialogExResult result, void* context) {
Nfc* nfc = (Nfc*)context; Nfc* nfc = context;
view_dispatcher_send_custom_event(nfc->view_dispatcher, result); view_dispatcher_send_custom_event(nfc->view_dispatcher, result);
} }
void nfc_scene_run_emv_app_confirm_on_enter(void* context) { void nfc_scene_run_emv_app_confirm_on_enter(void* context) {
Nfc* nfc = (Nfc*)context; Nfc* nfc = context;
DialogEx* dialog_ex = nfc->dialog_ex; DialogEx* dialog_ex = nfc->dialog_ex;
dialog_ex_set_left_button_text(dialog_ex, "Back"); dialog_ex_set_left_button_text(dialog_ex, "Back");
@@ -29,28 +27,23 @@ void nfc_scene_run_emv_app_confirm_on_enter(void* context) {
} }
bool nfc_scene_run_emv_app_confirm_on_event(void* context, SceneManagerEvent event) { bool nfc_scene_run_emv_app_confirm_on_event(void* context, SceneManagerEvent event) {
Nfc* nfc = (Nfc*)context; Nfc* nfc = context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) { if(event.type == SceneManagerEventTypeCustom) {
if(event.event == DialogExResultLeft) { if(event.event == DialogExResultLeft) {
return scene_manager_previous_scene(nfc->scene_manager); consumed = scene_manager_previous_scene(nfc->scene_manager);
} else if(event.event == DialogExResultRight) { } else if(event.event == DialogExResultRight) {
scene_manager_next_scene(nfc->scene_manager, NfcSceneReadEmvData); scene_manager_next_scene(nfc->scene_manager, NfcSceneReadEmvData);
return true; consumed = true;
} }
} }
return false; return consumed;
} }
void nfc_scene_run_emv_app_confirm_on_exit(void* context) { void nfc_scene_run_emv_app_confirm_on_exit(void* context) {
Nfc* nfc = (Nfc*)context; Nfc* nfc = context;
DialogEx* dialog_ex = nfc->dialog_ex; // Clean view
dialog_ex_set_header(dialog_ex, NULL, 0, 0, AlignCenter, AlignCenter); dialog_ex_reset(nfc->dialog_ex);
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);
} }

View File

@@ -3,13 +3,13 @@
#include <gui/modules/validators.h> #include <gui/modules/validators.h>
void nfc_scene_save_name_text_input_callback(void* context) { void nfc_scene_save_name_text_input_callback(void* context) {
Nfc* nfc = (Nfc*)context; Nfc* nfc = context;
view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventTextInputDone); view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventTextInputDone);
} }
void nfc_scene_save_name_on_enter(void* context) { void nfc_scene_save_name_on_enter(void* context) {
Nfc* nfc = (Nfc*)context; Nfc* nfc = context;
// Setup view // Setup view
TextInput* text_input = nfc->text_input; TextInput* text_input = nfc->text_input;
@@ -37,7 +37,8 @@ void nfc_scene_save_name_on_enter(void* context) {
} }
bool nfc_scene_save_name_on_event(void* context, SceneManagerEvent event) { bool nfc_scene_save_name_on_event(void* context, SceneManagerEvent event) {
Nfc* nfc = (Nfc*)context; Nfc* nfc = context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) { if(event.type == SceneManagerEventTypeCustom) {
if(event.event == NfcCustomEventTextInputDone) { if(event.event == NfcCustomEventTextInputDone) {
@@ -50,18 +51,18 @@ bool nfc_scene_save_name_on_event(void* context, SceneManagerEvent event) {
strlcpy(nfc->dev->dev_name, nfc->text_store, strlen(nfc->text_store) + 1); strlcpy(nfc->dev->dev_name, nfc->text_store, strlen(nfc->text_store) + 1);
if(nfc_device_save(nfc->dev, nfc->text_store)) { if(nfc_device_save(nfc->dev, nfc->text_store)) {
scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveSuccess); scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveSuccess);
return true; consumed = true;
} else { } else {
return scene_manager_search_and_switch_to_previous_scene( consumed = scene_manager_search_and_switch_to_previous_scene(
nfc->scene_manager, NfcSceneStart); nfc->scene_manager, NfcSceneStart);
} }
} }
} }
return false; return consumed;
} }
void nfc_scene_save_name_on_exit(void* context) { void nfc_scene_save_name_on_exit(void* context) {
Nfc* nfc = (Nfc*)context; Nfc* nfc = context;
// Clear view // Clear view
void* validator_context = text_input_get_validator_callback_context(nfc->text_input); void* validator_context = text_input_get_validator_callback_context(nfc->text_input);

View File

@@ -2,12 +2,12 @@
#include <dolphin/dolphin.h> #include <dolphin/dolphin.h>
void nfc_scene_save_success_popup_callback(void* context) { void nfc_scene_save_success_popup_callback(void* context) {
Nfc* nfc = (Nfc*)context; Nfc* nfc = context;
view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventViewExit); view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventViewExit);
} }
void nfc_scene_save_success_on_enter(void* context) { void nfc_scene_save_success_on_enter(void* context) {
Nfc* nfc = (Nfc*)context; Nfc* nfc = context;
DOLPHIN_DEED(DolphinDeedNfcSave); DOLPHIN_DEED(DolphinDeedNfcSave);
// Setup view // Setup view
@@ -22,7 +22,7 @@ void nfc_scene_save_success_on_enter(void* context) {
} }
bool nfc_scene_save_success_on_event(void* context, SceneManagerEvent event) { bool nfc_scene_save_success_on_event(void* context, SceneManagerEvent event) {
Nfc* nfc = (Nfc*)context; Nfc* nfc = context;
bool consumed = false; bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) { if(event.type == SceneManagerEventTypeCustom) {
@@ -47,15 +47,8 @@ bool nfc_scene_save_success_on_event(void* context, SceneManagerEvent event) {
} }
void nfc_scene_save_success_on_exit(void* context) { void nfc_scene_save_success_on_exit(void* context) {
Nfc* nfc = (Nfc*)context; Nfc* nfc = context;
// Clear view // Clear view
Popup* popup = nfc->popup; popup_reset(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);
} }

View File

@@ -9,13 +9,13 @@ enum SubmenuIndex {
}; };
void nfc_scene_saved_menu_submenu_callback(void* context, uint32_t index) { void nfc_scene_saved_menu_submenu_callback(void* context, uint32_t index) {
Nfc* nfc = (Nfc*)context; Nfc* nfc = context;
view_dispatcher_send_custom_event(nfc->view_dispatcher, index); view_dispatcher_send_custom_event(nfc->view_dispatcher, index);
} }
void nfc_scene_saved_menu_on_enter(void* context) { void nfc_scene_saved_menu_on_enter(void* context) {
Nfc* nfc = (Nfc*)context; Nfc* nfc = context;
Submenu* submenu = nfc->submenu; Submenu* submenu = nfc->submenu;
if(nfc->dev->format == NfcDeviceSaveFormatUid || if(nfc->dev->format == NfcDeviceSaveFormatUid ||
@@ -56,7 +56,7 @@ void nfc_scene_saved_menu_on_enter(void* context) {
} }
bool nfc_scene_saved_menu_on_event(void* context, SceneManagerEvent event) { bool nfc_scene_saved_menu_on_event(void* context, SceneManagerEvent event) {
Nfc* nfc = (Nfc*)context; Nfc* nfc = context;
bool consumed = false; bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) { if(event.type == SceneManagerEventTypeCustom) {
@@ -92,7 +92,7 @@ bool nfc_scene_saved_menu_on_event(void* context, SceneManagerEvent event) {
} }
void nfc_scene_saved_menu_on_exit(void* context) { void nfc_scene_saved_menu_on_exit(void* context) {
Nfc* nfc = (Nfc*)context; Nfc* nfc = context;
submenu_reset(nfc->submenu); submenu_reset(nfc->submenu);
} }

View File

@@ -1,13 +1,13 @@
#include "../nfc_i.h" #include "../nfc_i.h"
void nfc_scene_set_atqa_byte_input_callback(void* context) { void nfc_scene_set_atqa_byte_input_callback(void* context) {
Nfc* nfc = (Nfc*)context; Nfc* nfc = context;
view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventByteInputDone); view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventByteInputDone);
} }
void nfc_scene_set_atqa_on_enter(void* context) { void nfc_scene_set_atqa_on_enter(void* context) {
Nfc* nfc = (Nfc*)context; Nfc* nfc = context;
// Setup view // Setup view
ByteInput* byte_input = nfc->byte_input; ByteInput* byte_input = nfc->byte_input;
@@ -23,19 +23,20 @@ void nfc_scene_set_atqa_on_enter(void* context) {
} }
bool nfc_scene_set_atqa_on_event(void* context, SceneManagerEvent event) { bool nfc_scene_set_atqa_on_event(void* context, SceneManagerEvent event) {
Nfc* nfc = (Nfc*)context; Nfc* nfc = context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) { if(event.type == SceneManagerEventTypeCustom) {
if(event.event == NfcCustomEventByteInputDone) { if(event.event == NfcCustomEventByteInputDone) {
scene_manager_next_scene(nfc->scene_manager, NfcSceneSetUid); scene_manager_next_scene(nfc->scene_manager, NfcSceneSetUid);
return true; consumed = true;
} }
} }
return false; return consumed;
} }
void nfc_scene_set_atqa_on_exit(void* context) { void nfc_scene_set_atqa_on_exit(void* context) {
Nfc* nfc = (Nfc*)context; Nfc* nfc = context;
// Clear view // Clear view
byte_input_set_result_callback(nfc->byte_input, NULL, NULL, NULL, NULL, 0); byte_input_set_result_callback(nfc->byte_input, NULL, NULL, NULL, NULL, 0);

View File

@@ -1,13 +1,13 @@
#include "../nfc_i.h" #include "../nfc_i.h"
void nfc_scene_set_sak_byte_input_callback(void* context) { void nfc_scene_set_sak_byte_input_callback(void* context) {
Nfc* nfc = (Nfc*)context; Nfc* nfc = context;
view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventByteInputDone); view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventByteInputDone);
} }
void nfc_scene_set_sak_on_enter(void* context) { void nfc_scene_set_sak_on_enter(void* context) {
Nfc* nfc = (Nfc*)context; Nfc* nfc = context;
// Setup view // Setup view
ByteInput* byte_input = nfc->byte_input; ByteInput* byte_input = nfc->byte_input;
@@ -23,19 +23,20 @@ void nfc_scene_set_sak_on_enter(void* context) {
} }
bool nfc_scene_set_sak_on_event(void* context, SceneManagerEvent event) { bool nfc_scene_set_sak_on_event(void* context, SceneManagerEvent event) {
Nfc* nfc = (Nfc*)context; Nfc* nfc = context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) { if(event.type == SceneManagerEventTypeCustom) {
if(event.event == NfcCustomEventByteInputDone) { if(event.event == NfcCustomEventByteInputDone) {
scene_manager_next_scene(nfc->scene_manager, NfcSceneSetAtqua); scene_manager_next_scene(nfc->scene_manager, NfcSceneSetAtqua);
return true; consumed = true;
} }
} }
return false; return consumed;
} }
void nfc_scene_set_sak_on_exit(void* context) { void nfc_scene_set_sak_on_exit(void* context) {
Nfc* nfc = (Nfc*)context; Nfc* nfc = context;
// Clear view // Clear view
byte_input_set_result_callback(nfc->byte_input, NULL, NULL, NULL, NULL, 0); byte_input_set_result_callback(nfc->byte_input, NULL, NULL, NULL, NULL, 0);

View File

@@ -6,13 +6,13 @@ enum SubmenuIndex {
}; };
void nfc_scene_set_type_submenu_callback(void* context, uint32_t index) { void nfc_scene_set_type_submenu_callback(void* context, uint32_t index) {
Nfc* nfc = (Nfc*)context; Nfc* nfc = context;
view_dispatcher_send_custom_event(nfc->view_dispatcher, index); view_dispatcher_send_custom_event(nfc->view_dispatcher, index);
} }
void nfc_scene_set_type_on_enter(void* context) { void nfc_scene_set_type_on_enter(void* context) {
Nfc* nfc = (Nfc*)context; Nfc* nfc = context;
Submenu* submenu = nfc->submenu; Submenu* submenu = nfc->submenu;
// Clear device name // Clear device name
nfc_device_set_name(nfc->dev, ""); nfc_device_set_name(nfc->dev, "");
@@ -24,26 +24,27 @@ void nfc_scene_set_type_on_enter(void* context) {
} }
bool nfc_scene_set_type_on_event(void* context, SceneManagerEvent event) { bool nfc_scene_set_type_on_event(void* context, SceneManagerEvent event) {
Nfc* nfc = (Nfc*)context; Nfc* nfc = context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) { if(event.type == SceneManagerEventTypeCustom) {
if(event.event == SubmenuIndexNFCA7) { if(event.event == SubmenuIndexNFCA7) {
nfc->dev->dev_data.nfc_data.uid_len = 7; nfc->dev->dev_data.nfc_data.uid_len = 7;
nfc->dev->format = NfcDeviceSaveFormatUid; nfc->dev->format = NfcDeviceSaveFormatUid;
scene_manager_next_scene(nfc->scene_manager, NfcSceneSetSak); scene_manager_next_scene(nfc->scene_manager, NfcSceneSetSak);
return true; consumed = true;
} else if(event.event == SubmenuIndexNFCA4) { } else if(event.event == SubmenuIndexNFCA4) {
nfc->dev->dev_data.nfc_data.uid_len = 4; nfc->dev->dev_data.nfc_data.uid_len = 4;
nfc->dev->format = NfcDeviceSaveFormatUid; nfc->dev->format = NfcDeviceSaveFormatUid;
scene_manager_next_scene(nfc->scene_manager, NfcSceneSetSak); scene_manager_next_scene(nfc->scene_manager, NfcSceneSetSak);
return true; consumed = true;
} }
} }
return false; return consumed;
} }
void nfc_scene_set_type_on_exit(void* context) { void nfc_scene_set_type_on_exit(void* context) {
Nfc* nfc = (Nfc*)context; Nfc* nfc = context;
submenu_reset(nfc->submenu); submenu_reset(nfc->submenu);
} }

View File

@@ -2,13 +2,13 @@
#include <dolphin/dolphin.h> #include <dolphin/dolphin.h>
void nfc_scene_set_uid_byte_input_callback(void* context) { void nfc_scene_set_uid_byte_input_callback(void* context) {
Nfc* nfc = (Nfc*)context; Nfc* nfc = context;
view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventByteInputDone); view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventByteInputDone);
} }
void nfc_scene_set_uid_on_enter(void* context) { void nfc_scene_set_uid_on_enter(void* context) {
Nfc* nfc = (Nfc*)context; Nfc* nfc = context;
// Setup view // Setup view
ByteInput* byte_input = nfc->byte_input; ByteInput* byte_input = nfc->byte_input;
@@ -26,19 +26,20 @@ void nfc_scene_set_uid_on_enter(void* context) {
bool nfc_scene_set_uid_on_event(void* context, SceneManagerEvent event) { bool nfc_scene_set_uid_on_event(void* context, SceneManagerEvent event) {
Nfc* nfc = (Nfc*)context; Nfc* nfc = (Nfc*)context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) { if(event.type == SceneManagerEventTypeCustom) {
if(event.event == NfcCustomEventByteInputDone) { if(event.event == NfcCustomEventByteInputDone) {
DOLPHIN_DEED(DolphinDeedNfcAdd); DOLPHIN_DEED(DolphinDeedNfcAdd);
scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveName); scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveName);
return true; consumed = true;
} }
} }
return false; return consumed;
} }
void nfc_scene_set_uid_on_exit(void* context) { void nfc_scene_set_uid_on_exit(void* context) {
Nfc* nfc = (Nfc*)context; Nfc* nfc = context;
// Clear view // Clear view
byte_input_set_result_callback(nfc->byte_input, NULL, NULL, NULL, NULL, 0); byte_input_set_result_callback(nfc->byte_input, NULL, NULL, NULL, NULL, 0);

View File

@@ -9,13 +9,13 @@ enum SubmenuIndex {
}; };
void nfc_scene_start_submenu_callback(void* context, uint32_t index) { void nfc_scene_start_submenu_callback(void* context, uint32_t index) {
Nfc* nfc = (Nfc*)context; Nfc* nfc = context;
view_dispatcher_send_custom_event(nfc->view_dispatcher, index); view_dispatcher_send_custom_event(nfc->view_dispatcher, index);
} }
void nfc_scene_start_on_enter(void* context) { void nfc_scene_start_on_enter(void* context) {
Nfc* nfc = (Nfc*)context; Nfc* nfc = context;
Submenu* submenu = nfc->submenu; Submenu* submenu = nfc->submenu;
submenu_add_item( submenu_add_item(
@@ -43,7 +43,7 @@ void nfc_scene_start_on_enter(void* context) {
} }
bool nfc_scene_start_on_event(void* context, SceneManagerEvent event) { bool nfc_scene_start_on_event(void* context, SceneManagerEvent event) {
Nfc* nfc = (Nfc*)context; Nfc* nfc = context;
bool consumed = false; bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) { if(event.type == SceneManagerEventTypeCustom) {
@@ -70,7 +70,7 @@ bool nfc_scene_start_on_event(void* context, SceneManagerEvent event) {
} }
void nfc_scene_start_on_exit(void* context) { void nfc_scene_start_on_exit(void* context) {
Nfc* nfc = (Nfc*)context; Nfc* nfc = context;
submenu_reset(nfc->submenu); submenu_reset(nfc->submenu);
} }

View File

@@ -24,7 +24,7 @@ void subghz_scene_delete_raw_on_enter(void* context) {
char delete_str[64]; char delete_str[64];
snprintf(delete_str, sizeof(delete_str), "\e#Delete %s?\e#", subghz->file_name); snprintf(delete_str, sizeof(delete_str), "\e#Delete %s?\e#", subghz->file_name);
widget_add_text_box_element( widget_add_text_box_element(
subghz->widget, 0, 0, 128, 23, AlignCenter, AlignCenter, delete_str); subghz->widget, 0, 0, 128, 23, AlignCenter, AlignCenter, delete_str, false);
widget_add_string_element( widget_add_string_element(
subghz->widget, 38, 25, AlignLeft, AlignTop, FontSecondary, "RAW signal"); subghz->widget, 38, 25, AlignLeft, AlignTop, FontSecondary, "RAW signal");

View File

@@ -236,7 +236,15 @@ void subghz_read_raw_draw(Canvas* canvas, SubGhzReadRAWModel* model) {
elements_button_center(canvas, "Send"); elements_button_center(canvas, "Send");
elements_button_right(canvas, "More"); elements_button_right(canvas, "More");
elements_text_box( elements_text_box(
canvas, 4, 12, 110, 44, AlignCenter, AlignCenter, string_get_cstr(model->file_name)); canvas,
4,
12,
110,
44,
AlignCenter,
AlignCenter,
string_get_cstr(model->file_name),
true);
break; break;
case SubGhzReadRAWStatusTX: case SubGhzReadRAWStatusTX:

View File

@@ -19,7 +19,7 @@ static const char* update_task_stage_descr[] = {
[UpdateTaskStageRadioCommit] = "Applying radio stack", [UpdateTaskStageRadioCommit] = "Applying radio stack",
[UpdateTaskStageLfsBackup] = "Backing up LFS", [UpdateTaskStageLfsBackup] = "Backing up LFS",
[UpdateTaskStageLfsRestore] = "Restoring LFS", [UpdateTaskStageLfsRestore] = "Restoring LFS",
[UpdateTaskStageAssetsUpdate] = "Updating assets", [UpdateTaskStageResourcesUpdate] = "Updating resources",
[UpdateTaskStageCompleted] = "Completed!", [UpdateTaskStageCompleted] = "Completed!",
[UpdateTaskStageError] = "Error", [UpdateTaskStageError] = "Error",
}; };

View File

@@ -23,7 +23,7 @@ typedef enum {
UpdateTaskStageRadioCommit, UpdateTaskStageRadioCommit,
UpdateTaskStageLfsBackup, UpdateTaskStageLfsBackup,
UpdateTaskStageLfsRestore, UpdateTaskStageLfsRestore,
UpdateTaskStageAssetsUpdate, UpdateTaskStageResourcesUpdate,
UpdateTaskStageCompleted, UpdateTaskStageCompleted,
UpdateTaskStageError, UpdateTaskStageError,
} UpdateTaskStage; } UpdateTaskStage;

View File

@@ -166,14 +166,13 @@ static bool update_task_post_update(UpdateTask* update_task) {
.total_files = 0, .total_files = 0,
.processed_files = 0, .processed_files = 0,
}; };
update_task_set_progress(update_task, UpdateTaskStageAssetsUpdate, 0); update_task_set_progress(update_task, UpdateTaskStageResourcesUpdate, 0);
path_concat( path_concat(
string_get_cstr(update_task->update_path), string_get_cstr(update_task->update_path),
string_get_cstr(update_task->manifest->resource_bundle), string_get_cstr(update_task->manifest->resource_bundle),
file_path); file_path);
update_task_set_progress(update_task, UpdateTaskStageProgress, 0);
TarArchive* archive = tar_archive_alloc(update_task->storage); TarArchive* archive = tar_archive_alloc(update_task->storage);
tar_archive_set_file_callback(archive, update_task_resource_unpack_cb, &progress); tar_archive_set_file_callback(archive, update_task_resource_unpack_cb, &progress);
success = tar_archive_open(archive, string_get_cstr(file_path), TAR_OPEN_MODE_READ); success = tar_archive_open(archive, string_get_cstr(file_path), TAR_OPEN_MODE_READ);

View File

@@ -3,11 +3,11 @@ PROJECT_ROOT = $(abspath $(dir $(abspath $(firstword $(MAKEFILE_LIST))))..)
include $(PROJECT_ROOT)/assets/assets.mk include $(PROJECT_ROOT)/assets/assets.mk
.PHONY: all .PHONY: all
all: icons protobuf dolphin all: icons protobuf dolphin manifest
$(ASSETS): $(ASSETS_SOURCES) $(ASSETS_COMPILLER) $(ASSETS): $(ASSETS_SOURCES) $(ASSETS_COMPILER)
@echo "\tASSETS\t\t" $@ @echo "\tASSETS\t\t" $@
@$(ASSETS_COMPILLER) icons "$(ASSETS_SOURCE_DIR)" "$(ASSETS_COMPILED_DIR)" @$(ASSETS_COMPILER) icons "$(ASSETS_SOURCE_DIR)" "$(ASSETS_COMPILED_DIR)"
.PHONY: icons .PHONY: icons
icons: $(ASSETS) icons: $(ASSETS)
@@ -22,11 +22,15 @@ protobuf: $(PROTOBUF)
$(DOLPHIN_EXTERNAL_OUTPUT_DIR): $(DOLPHIN_SOURCE_DIR) $(DOLPHIN_EXTERNAL_OUTPUT_DIR): $(DOLPHIN_SOURCE_DIR)
@echo "\tDOLPHIN blocking" @echo "\tDOLPHIN blocking"
@$(ASSETS_COMPILLER) dolphin -s dolphin_blocking "$(DOLPHIN_SOURCE_DIR)/blocking" "$(DOLPHIN_INTERNAL_OUTPUT_DIR)" @$(ASSETS_COMPILER) dolphin -s dolphin_blocking "$(DOLPHIN_SOURCE_DIR)/blocking" "$(DOLPHIN_INTERNAL_OUTPUT_DIR)"
@echo "\tDOLPHIN internal" @echo "\tDOLPHIN internal"
@$(ASSETS_COMPILLER) dolphin -s dolphin_internal "$(DOLPHIN_SOURCE_DIR)/internal" "$(DOLPHIN_INTERNAL_OUTPUT_DIR)" @$(ASSETS_COMPILER) dolphin -s dolphin_internal "$(DOLPHIN_SOURCE_DIR)/internal" "$(DOLPHIN_INTERNAL_OUTPUT_DIR)"
@echo "\tDOLPHIN external" @echo "\tDOLPHIN external"
@$(ASSETS_COMPILLER) dolphin "$(DOLPHIN_SOURCE_DIR)/external" "$(DOLPHIN_EXTERNAL_OUTPUT_DIR)" @$(ASSETS_COMPILER) dolphin "$(DOLPHIN_SOURCE_DIR)/external" "$(DOLPHIN_EXTERNAL_OUTPUT_DIR)"
.PHONY: manifest
manifest:
$(ASSETS_COMPILER) manifest $(RESOURCES_DIR)
.PHONY: dolphin .PHONY: dolphin
dolphin: $(DOLPHIN_EXTERNAL_OUTPUT_DIR) dolphin: $(DOLPHIN_EXTERNAL_OUTPUT_DIR)

View File

@@ -1,11 +1,15 @@
ASSETS_DIR := $(PROJECT_ROOT)/assets ASSETS_DIR := $(PROJECT_ROOT)/assets
ASSETS_COMPILLER := $(PROJECT_ROOT)/scripts/assets.py ASSETS_COMPILER := $(PROJECT_ROOT)/scripts/assets.py
ASSETS_COMPILED_DIR := $(ASSETS_DIR)/compiled ASSETS_COMPILED_DIR := $(ASSETS_DIR)/compiled
ASSETS_SOURCE_DIR := $(ASSETS_DIR)/icons ASSETS_SOURCE_DIR := $(ASSETS_DIR)/icons
ASSETS_SOURCES += $(shell find $(ASSETS_SOURCE_DIR) -type f -iname '*.png' -or -iname 'frame_rate') ASSETS_SOURCES += $(shell find $(ASSETS_SOURCE_DIR) -type f -iname '*.png' -or -iname 'frame_rate')
ASSETS += $(ASSETS_COMPILED_DIR)/assets_icons.c ASSETS += $(ASSETS_COMPILED_DIR)/assets_icons.c
RESOURCES_DIR := $(ASSETS_DIR)/resources
RESOURCES_MANIFEST := $(RESOURCES_DIR)/Manifest
RESOURCES_FILES := $(shell find $(RESOURCES_DIR) ! -name Manifest -type f)
DOLPHIN_SOURCE_DIR := $(ASSETS_DIR)/dolphin DOLPHIN_SOURCE_DIR := $(ASSETS_DIR)/dolphin
DOLPHIN_INTERNAL_OUTPUT_DIR := $(ASSETS_COMPILED_DIR) DOLPHIN_INTERNAL_OUTPUT_DIR := $(ASSETS_COMPILED_DIR)
DOLPHIN_EXTERNAL_OUTPUT_DIR := $(ASSETS_DIR)/resources/dolphin DOLPHIN_EXTERNAL_OUTPUT_DIR := $(ASSETS_DIR)/resources/dolphin

240
assets/resources/Manifest Normal file
View File

@@ -0,0 +1,240 @@
V:0
T:1650389893
D:badusb
D:dolphin
D:infrared
D:nfc
D:subghz
D:u2f
F:bb8ffef2d052f171760ce3dc5220cbad:1591:badusb/demo_macos.txt
F:e538ad2ce5a06ec45e1b5b24824901b1:1552:badusb/demo_windows.txt
D:dolphin/L1_Boxing_128x64
D:dolphin/L1_Cry_128x64
D:dolphin/L1_Furippa1_128x64
D:dolphin/L1_Laptop_128x51
D:dolphin/L1_Leaving_sad_128x64
D:dolphin/L1_Mad_fist_128x64
D:dolphin/L1_Read_books_128x64
D:dolphin/L1_Recording_128x51
D:dolphin/L1_Sleep_128x64
D:dolphin/L1_Waves_128x50
D:dolphin/L2_Furippa2_128x64
D:dolphin/L2_Hacking_pc_128x64
D:dolphin/L2_Soldering_128x64
D:dolphin/L3_Furippa3_128x64
D:dolphin/L3_Hijack_radio_128x64
D:dolphin/L3_Lab_research_128x54
F:d1148ab5354eaf4fa7f959589d840932:1563:dolphin/manifest.txt
F:d37be8444102ec5cde5fe3a85d55b57d:481:dolphin/L1_Boxing_128x64/frame_0.bm
F:54fb07443bc153ded9589b74d23b4263:461:dolphin/L1_Boxing_128x64/frame_1.bm
F:e007afe130d699c715b99ce8e5b407bd:531:dolphin/L1_Boxing_128x64/frame_2.bm
F:a999a9a6c76c66158f1aa5ccb56de7c9:437:dolphin/L1_Boxing_128x64/frame_3.bm
F:ec6af9cb451ab16c0fa62e95e8134b49:459:dolphin/L1_Boxing_128x64/frame_4.bm
F:2aa0c1e7bf1131b9dc172aa595ec01f2:450:dolphin/L1_Boxing_128x64/frame_5.bm
F:bbc8f750d17d156438c5cfe1122ec7f4:442:dolphin/L1_Boxing_128x64/frame_6.bm
F:f6e51ada3e3285e330714dab5b4277dd:418:dolphin/L1_Boxing_128x64/meta.txt
F:ab33a6f37209541f3db938d1cfe1706f:889:dolphin/L1_Cry_128x64/frame_0.bm
F:1b3fdeb404af0f7402caa5a5e091a8f8:911:dolphin/L1_Cry_128x64/frame_1.bm
F:4db644b173af72f3d371d2bd81f76b05:910:dolphin/L1_Cry_128x64/frame_2.bm
F:cd4c0ef67a8e514edecd9600242db068:923:dolphin/L1_Cry_128x64/frame_3.bm
F:ee02e9589e0714d3e2bc0d93aa294ccb:894:dolphin/L1_Cry_128x64/frame_4.bm
F:7703a7d9745d13b45d73ce4b86b4cdc8:940:dolphin/L1_Cry_128x64/frame_5.bm
F:ee6de6a0ed903317c4948cb445e0a9a8:915:dolphin/L1_Cry_128x64/frame_6.bm
F:a3892e45826c66f48d3d64fb81521446:934:dolphin/L1_Cry_128x64/frame_7.bm
F:680b12cc4dad722d6583b7e710bfc297:516:dolphin/L1_Cry_128x64/meta.txt
F:4911eaa7cb84ced19e5dea2af51b91a5:294:dolphin/L1_Furippa1_128x64/frame_0.bm
F:5669bee57c7b3d93a1665dd87fd5372a:325:dolphin/L1_Furippa1_128x64/frame_1.bm
F:80b48a77682b853e6236cd1c89083e6f:465:dolphin/L1_Furippa1_128x64/frame_10.bm
F:9d8ea10bf3d3831cb4a94957dc0b41c6:698:dolphin/L1_Furippa1_128x64/frame_11.bm
F:2dbb3125ea63550906fba8f7ec7b1da3:541:dolphin/L1_Furippa1_128x64/frame_12.bm
F:6a06b718957dca9caa63a4f3baa73abb:584:dolphin/L1_Furippa1_128x64/frame_13.bm
F:5450bf16c3d2fceaf5e6ea585b7ef7c1:610:dolphin/L1_Furippa1_128x64/frame_14.bm
F:535c0eca62703eb7df36f17334a6191b:719:dolphin/L1_Furippa1_128x64/frame_15.bm
F:7c03af85ade9b791755f3a4d106c2b7c:458:dolphin/L1_Furippa1_128x64/frame_16.bm
F:41b8fea16ad8705f4594e6119eade395:400:dolphin/L1_Furippa1_128x64/frame_17.bm
F:2db7fd3da5208a8e41902ae27cf41702:333:dolphin/L1_Furippa1_128x64/frame_18.bm
F:7e47428442e0f04959fc6afde979936e:351:dolphin/L1_Furippa1_128x64/frame_2.bm
F:0eb187078f169d7a852e97ecf430aea0:324:dolphin/L1_Furippa1_128x64/frame_3.bm
F:967c402971a442a5bf28eba804bb3ff4:387:dolphin/L1_Furippa1_128x64/frame_4.bm
F:175cb930fba0fc86f54a3a109b741708:390:dolphin/L1_Furippa1_128x64/frame_5.bm
F:f8c3ee1ab657549d1d00c1c72d8d2ff5:407:dolphin/L1_Furippa1_128x64/frame_6.bm
F:4911eaa7cb84ced19e5dea2af51b91a5:294:dolphin/L1_Furippa1_128x64/frame_7.bm
F:8f649ff34b224f4e564644a4494c54ed:283:dolphin/L1_Furippa1_128x64/frame_8.bm
F:3ec3c40d26bf8d3e691b1335d20d4ec0:312:dolphin/L1_Furippa1_128x64/frame_9.bm
F:ebe088426d184cf6651288accd21add6:241:dolphin/L1_Furippa1_128x64/meta.txt
F:d02fdfd1a3b89da00d2acf32bd09da80:555:dolphin/L1_Laptop_128x51/frame_0.bm
F:7e29ea503d41023fa3895d15458f106d:557:dolphin/L1_Laptop_128x51/frame_1.bm
F:eb55e0629de873f537d8412ced528eb4:560:dolphin/L1_Laptop_128x51/frame_2.bm
F:1516472ab3c140dd5bd4d089caa44747:556:dolphin/L1_Laptop_128x51/frame_3.bm
F:61172f89cf0a17bd7f978edccdeed166:560:dolphin/L1_Laptop_128x51/frame_4.bm
F:9d54913928c7e9477b6b8a43f3767621:554:dolphin/L1_Laptop_128x51/frame_5.bm
F:5243d6272bbb213e9c17af07ee011402:553:dolphin/L1_Laptop_128x51/frame_6.bm
F:aa68e0f28f117891ba0f4d7613224fc6:560:dolphin/L1_Laptop_128x51/frame_7.bm
F:9ef1935ab29fe70bbc517f4b602547d7:403:dolphin/L1_Laptop_128x51/meta.txt
F:6ce34e62c5bf4764a4163101afe63e60:514:dolphin/L1_Leaving_sad_128x64/frame_0.bm
F:19a0e0c518d222d91d24b8712ab6bb80:526:dolphin/L1_Leaving_sad_128x64/frame_1.bm
F:837bfb424c8d8a3bfbda7d6a28ba5a5c:316:dolphin/L1_Leaving_sad_128x64/frame_10.bm
F:1a69b6f63a96e0958837ea8b21db3966:294:dolphin/L1_Leaving_sad_128x64/frame_11.bm
F:c3ea827593a4563d544dfb7e99d73885:322:dolphin/L1_Leaving_sad_128x64/frame_12.bm
F:1e3842669191fe9599f830ac133e0751:542:dolphin/L1_Leaving_sad_128x64/frame_2.bm
F:9161660e6827bd776a15eefa2a8add19:557:dolphin/L1_Leaving_sad_128x64/frame_3.bm
F:d01a79fdb4f84397d82bf9927aeb71e0:488:dolphin/L1_Leaving_sad_128x64/frame_4.bm
F:316e30ef319c080fab2a79c21e526319:469:dolphin/L1_Leaving_sad_128x64/frame_5.bm
F:09a812d59b60b5fe7724057daa14ad60:499:dolphin/L1_Leaving_sad_128x64/frame_6.bm
F:9eb07b76cc864a0ce2918d68e41d4500:486:dolphin/L1_Leaving_sad_128x64/frame_7.bm
F:cf8c4cc4abbd700b096037b7ebfd0e31:403:dolphin/L1_Leaving_sad_128x64/frame_8.bm
F:889728ded689203aa82193e573912d18:317:dolphin/L1_Leaving_sad_128x64/frame_9.bm
F:2bff1f09ad1e9059a60e08990ca1d414:477:dolphin/L1_Leaving_sad_128x64/meta.txt
F:c31a882e95ed5c69fd63226db2188710:520:dolphin/L1_Mad_fist_128x64/frame_0.bm
F:740326828f6ba6e29373943ba835e77f:540:dolphin/L1_Mad_fist_128x64/frame_1.bm
F:0c9693dda040fd73ca6d773a10924bd8:542:dolphin/L1_Mad_fist_128x64/frame_10.bm
F:425c1d101debd1e9502db2628640b704:505:dolphin/L1_Mad_fist_128x64/frame_11.bm
F:aa576f7dbd14ec682f6c50314165fb14:501:dolphin/L1_Mad_fist_128x64/frame_12.bm
F:712335eabefb8c7bb7fb2f4301419c10:500:dolphin/L1_Mad_fist_128x64/frame_13.bm
F:b6e11711ea4dcc2e64f267d888f91baf:515:dolphin/L1_Mad_fist_128x64/frame_2.bm
F:61bdd22a2b1e67efe093b6acf7dfadce:538:dolphin/L1_Mad_fist_128x64/frame_3.bm
F:20ae06a3ce7a07656e578edb024e2b3f:512:dolphin/L1_Mad_fist_128x64/frame_4.bm
F:45cf2bd55365a7328df39fe98a496cc9:519:dolphin/L1_Mad_fist_128x64/frame_5.bm
F:4b8840eebb3a4a1ead69a7130816047e:524:dolphin/L1_Mad_fist_128x64/frame_6.bm
F:0de4497a5fbf80cc93e523465c5e3122:515:dolphin/L1_Mad_fist_128x64/frame_7.bm
F:32d8ddeb19bfa415fe283666b1e323a2:517:dolphin/L1_Mad_fist_128x64/frame_8.bm
F:a42a0578c2d0411500fb3485a3beb536:526:dolphin/L1_Mad_fist_128x64/frame_9.bm
F:10a521c78168a5928c859494e2a61cd2:349:dolphin/L1_Mad_fist_128x64/meta.txt
F:61565b7be9a69a60ce2dbae0273df347:653:dolphin/L1_Read_books_128x64/frame_0.bm
F:cf5a2d423540e3af37e789d70c9c1fbf:653:dolphin/L1_Read_books_128x64/frame_1.bm
F:c91935861979d024e6637b8810889878:650:dolphin/L1_Read_books_128x64/frame_2.bm
F:0c007a30f396f3e7a0ded2b24080357d:646:dolphin/L1_Read_books_128x64/frame_3.bm
F:323a52816dd79d6d3186f451e26e06ad:650:dolphin/L1_Read_books_128x64/frame_4.bm
F:494f27958f4cea9b94d09cf27725c5cd:652:dolphin/L1_Read_books_128x64/frame_5.bm
F:a6a7491fe80255e1745c9f293da52805:646:dolphin/L1_Read_books_128x64/frame_6.bm
F:238497e6643fd491cd6002e98c615c05:647:dolphin/L1_Read_books_128x64/frame_7.bm
F:300651e8f53d9a29ae38d4b9292c73cf:643:dolphin/L1_Read_books_128x64/frame_8.bm
F:3d9568deeff646b677092902a98f9ceb:325:dolphin/L1_Read_books_128x64/meta.txt
F:2aba555567ab70cff003ded4138c6721:663:dolphin/L1_Recording_128x51/frame_0.bm
F:8456c6e86825957e5662e2f08eb6c116:657:dolphin/L1_Recording_128x51/frame_1.bm
F:2e4a1aca5afa5a6ab254884210875eb4:629:dolphin/L1_Recording_128x51/frame_10.bm
F:9f1cf96598e3d935879b1d0c97705778:659:dolphin/L1_Recording_128x51/frame_11.bm
F:409abfeca974e5649affcd1faafea988:628:dolphin/L1_Recording_128x51/frame_2.bm
F:66b2a5abf05acbf79f9943e01b8b8cec:654:dolphin/L1_Recording_128x51/frame_3.bm
F:d55c5ed28c2ff48f42ab30b420d64fa3:662:dolphin/L1_Recording_128x51/frame_4.bm
F:2ce12d8cfdd953c9dadb9459c580a320:622:dolphin/L1_Recording_128x51/frame_5.bm
F:da631e3837fcdf3ee9e6abdf17fb764b:664:dolphin/L1_Recording_128x51/frame_6.bm
F:604a7cdac2491c9bc2e88b9e91c99dcc:626:dolphin/L1_Recording_128x51/frame_7.bm
F:fc94649dc98244dd9a0ab7fe62721d3c:663:dolphin/L1_Recording_128x51/frame_8.bm
F:b2475ab8ee26cbd9a403ee603520bd35:661:dolphin/L1_Recording_128x51/frame_9.bm
F:a7c2b3b420706712149cc2426c68df4f:219:dolphin/L1_Recording_128x51/meta.txt
F:9858fd34b55cebcb9be50c5710212a13:580:dolphin/L1_Sleep_128x64/frame_0.bm
F:e47ef8c846083b8fde028b1724861444:589:dolphin/L1_Sleep_128x64/frame_1.bm
F:9749bd05b47fd07cc3a41ab201f86bf4:582:dolphin/L1_Sleep_128x64/frame_2.bm
F:edf11266b20b846ace622e41cd36906b:597:dolphin/L1_Sleep_128x64/frame_3.bm
F:8fbb96a9d809d85fa6bad931fe4e6fe2:510:dolphin/L1_Sleep_128x64/meta.txt
F:283b41f1b2c581c510ff176293b7288a:443:dolphin/L1_Waves_128x50/frame_0.bm
F:c9fc5127e1d8a4217b6b177716725ba0:448:dolphin/L1_Waves_128x50/frame_1.bm
F:8e0797bf26d5d8d3cbeb99798c222b80:463:dolphin/L1_Waves_128x50/frame_2.bm
F:da02b1deb3119b31f2b8f182d5bf3242:472:dolphin/L1_Waves_128x50/frame_3.bm
F:8e6fb4133acbda7e5bb9adad0aed306c:620:dolphin/L1_Waves_128x50/meta.txt
F:be80d2fa903e3250b69c063a1eef0621:350:dolphin/L2_Furippa2_128x64/frame_0.bm
F:9e628f5e154f12d6c57b13befed1f5f6:385:dolphin/L2_Furippa2_128x64/frame_1.bm
F:80b48a77682b853e6236cd1c89083e6f:465:dolphin/L2_Furippa2_128x64/frame_10.bm
F:9d8ea10bf3d3831cb4a94957dc0b41c6:698:dolphin/L2_Furippa2_128x64/frame_11.bm
F:2dbb3125ea63550906fba8f7ec7b1da3:541:dolphin/L2_Furippa2_128x64/frame_12.bm
F:6a06b718957dca9caa63a4f3baa73abb:584:dolphin/L2_Furippa2_128x64/frame_13.bm
F:5450bf16c3d2fceaf5e6ea585b7ef7c1:610:dolphin/L2_Furippa2_128x64/frame_14.bm
F:e3c92103f403857b502081d3b058e53a:740:dolphin/L2_Furippa2_128x64/frame_15.bm
F:432669d796bbf7be1d14f5b7db036a92:533:dolphin/L2_Furippa2_128x64/frame_16.bm
F:53485c6b465c80a1ce8ddf03c4976039:451:dolphin/L2_Furippa2_128x64/frame_17.bm
F:333b75b16c088428a28259c931630fb9:397:dolphin/L2_Furippa2_128x64/frame_18.bm
F:ed02d68380382361f3f01cbf01d13b0c:402:dolphin/L2_Furippa2_128x64/frame_2.bm
F:b0ba042d7b60dc5681182b1d4005f0a2:374:dolphin/L2_Furippa2_128x64/frame_3.bm
F:518a84fa5a4e9e7f84246d5d82e87f15:440:dolphin/L2_Furippa2_128x64/frame_4.bm
F:9b7b0ae6f4f55d30cb43b0465216aa25:449:dolphin/L2_Furippa2_128x64/frame_5.bm
F:03b153949b0dae2efe1fc5f0dc57a0ef:466:dolphin/L2_Furippa2_128x64/frame_6.bm
F:be80d2fa903e3250b69c063a1eef0621:350:dolphin/L2_Furippa2_128x64/frame_7.bm
F:a8433f451cf3efc4ce2fb04a38c1f84f:319:dolphin/L2_Furippa2_128x64/frame_8.bm
F:d32a11bf9779d57191c1e59fe69cf83d:317:dolphin/L2_Furippa2_128x64/frame_9.bm
F:ebe088426d184cf6651288accd21add6:241:dolphin/L2_Furippa2_128x64/meta.txt
F:af4ec0085c29732085c51b18dc97bc27:543:dolphin/L2_Hacking_pc_128x64/frame_0.bm
F:eb141965fb6fb9f8b28766bac92abe1a:545:dolphin/L2_Hacking_pc_128x64/frame_1.bm
F:f7b33d3541dab08aaf4e8375e262b982:548:dolphin/L2_Hacking_pc_128x64/frame_2.bm
F:03634d90c54fd235aa76c0f9f794c80b:608:dolphin/L2_Hacking_pc_128x64/frame_3.bm
F:4c77406302f3fb74f8bdba568097082a:609:dolphin/L2_Hacking_pc_128x64/frame_4.bm
F:b0d1783358094534ac95b3455124d5fe:409:dolphin/L2_Hacking_pc_128x64/meta.txt
F:584c92e6fb15e99389b84d567e6d4d02:699:dolphin/L2_Soldering_128x64/frame_0.bm
F:3fa01b93460379204b6d14f43573b4f3:688:dolphin/L2_Soldering_128x64/frame_1.bm
F:6fad29757d4b7231b1d0ec53d0529b45:699:dolphin/L2_Soldering_128x64/frame_10.bm
F:e82c83e5a03abf4f6a1efd0a0f1ca33a:689:dolphin/L2_Soldering_128x64/frame_2.bm
F:7f9f310e22ef85af225dd1aefa2c47ba:689:dolphin/L2_Soldering_128x64/frame_3.bm
F:1ff31af6f90f07c0cdfa3283f52a5adc:693:dolphin/L2_Soldering_128x64/frame_4.bm
F:1a8f25aff949860cc6ffc79b4f48d5dd:696:dolphin/L2_Soldering_128x64/frame_5.bm
F:dbaa75feb8aebaf9b1cc5201c29952b8:712:dolphin/L2_Soldering_128x64/frame_6.bm
F:ee356bd981fba90c402d8e08d3015792:732:dolphin/L2_Soldering_128x64/frame_7.bm
F:09d5c5a685df606562d407bb9dac798e:705:dolphin/L2_Soldering_128x64/frame_8.bm
F:5451816e73bad029b3b9f3f55d294582:698:dolphin/L2_Soldering_128x64/frame_9.bm
F:c38ffad11987faf5ba6e363ead705e78:319:dolphin/L2_Soldering_128x64/meta.txt
F:2e083023ab65d1f99bba71f9aae6db9a:398:dolphin/L3_Furippa3_128x64/frame_0.bm
F:8cf20e07d84fd6a1157ba932beca70ea:438:dolphin/L3_Furippa3_128x64/frame_1.bm
F:018344c951691b7b1d77c1c6729d3e42:559:dolphin/L3_Furippa3_128x64/frame_10.bm
F:07008e2508064ab7a8467802472a9803:728:dolphin/L3_Furippa3_128x64/frame_11.bm
F:2dbb3125ea63550906fba8f7ec7b1da3:541:dolphin/L3_Furippa3_128x64/frame_12.bm
F:6a06b718957dca9caa63a4f3baa73abb:584:dolphin/L3_Furippa3_128x64/frame_13.bm
F:5450bf16c3d2fceaf5e6ea585b7ef7c1:610:dolphin/L3_Furippa3_128x64/frame_14.bm
F:e333224a4bed87b606df57a252ed4887:741:dolphin/L3_Furippa3_128x64/frame_15.bm
F:a20a6abfbd66fc3f92c66adacc4444a3:559:dolphin/L3_Furippa3_128x64/frame_16.bm
F:c1e051dce6b90e4f69b4792d0356a6b3:492:dolphin/L3_Furippa3_128x64/frame_17.bm
F:377f3621507c6590120cbc1c8ca92999:445:dolphin/L3_Furippa3_128x64/frame_18.bm
F:81f09c0fcd2bddb8a107a199e7149230:463:dolphin/L3_Furippa3_128x64/frame_2.bm
F:ed7fd1ada1070493462c1899f7372baf:424:dolphin/L3_Furippa3_128x64/frame_3.bm
F:e5fb2cdc4e08d6abff3191d37a1007ed:499:dolphin/L3_Furippa3_128x64/frame_4.bm
F:923a05250e5a93c7db7bbbf48448d164:504:dolphin/L3_Furippa3_128x64/frame_5.bm
F:1e9628db28a9a908c4a4b24cb16c5d20:521:dolphin/L3_Furippa3_128x64/frame_6.bm
F:2e083023ab65d1f99bba71f9aae6db9a:398:dolphin/L3_Furippa3_128x64/frame_7.bm
F:f1ec6e12daba9490f9e2e0e308ae3f83:419:dolphin/L3_Furippa3_128x64/frame_8.bm
F:106997120ad4cd23bd51e6f26bd7d74d:435:dolphin/L3_Furippa3_128x64/frame_9.bm
F:ebe088426d184cf6651288accd21add6:241:dolphin/L3_Furippa3_128x64/meta.txt
F:42970030123b2468984785fea7c60318:524:dolphin/L3_Hijack_radio_128x64/frame_0.bm
F:491c6d8ef21e48ca0f6b5fbd269c820b:527:dolphin/L3_Hijack_radio_128x64/frame_1.bm
F:ff499c8716c5f7fc1110a5ee82bda20c:550:dolphin/L3_Hijack_radio_128x64/frame_10.bm
F:ee39d82d6efc6a6992d19b6d75a6c509:572:dolphin/L3_Hijack_radio_128x64/frame_11.bm
F:5d14e8cb9d67bf8f597d6c749d07a135:539:dolphin/L3_Hijack_radio_128x64/frame_12.bm
F:9461004b75a34a36097668159c4cabe6:579:dolphin/L3_Hijack_radio_128x64/frame_13.bm
F:c925d4b1dff9c81463944cf930d7da8d:526:dolphin/L3_Hijack_radio_128x64/frame_2.bm
F:f98ed80cfab3a94b580be81654401c89:529:dolphin/L3_Hijack_radio_128x64/frame_3.bm
F:97ba548c27732be9e05fb8f7be8204ce:571:dolphin/L3_Hijack_radio_128x64/frame_4.bm
F:524932eb2391057fc1dea7237c7086e3:574:dolphin/L3_Hijack_radio_128x64/frame_5.bm
F:8eb9672f719926ac9c4c158575f388cd:524:dolphin/L3_Hijack_radio_128x64/frame_6.bm
F:7ca93fbab93bc278d4a11089d624a07b:655:dolphin/L3_Hijack_radio_128x64/frame_7.bm
F:37b4368f0b7235f3a7347bf499541666:645:dolphin/L3_Hijack_radio_128x64/frame_8.bm
F:ea9c3d7bab4756c2916369d5e130fa71:611:dolphin/L3_Hijack_radio_128x64/frame_9.bm
F:8583743f18a12ff647d3478e7aebdad6:230:dolphin/L3_Hijack_radio_128x64/meta.txt
F:f5f02a9df03bba734bdb7ed3297795f0:611:dolphin/L3_Lab_research_128x54/frame_0.bm
F:8f9655ad286464159443922d00e45620:614:dolphin/L3_Lab_research_128x54/frame_1.bm
F:7793b1bc107d4ea2e311e92dc16bf946:576:dolphin/L3_Lab_research_128x54/frame_10.bm
F:f24b8409f9dc770f3845424fe0ab489e:585:dolphin/L3_Lab_research_128x54/frame_11.bm
F:4ea93c4482dac43f40b67cc308f21e6d:571:dolphin/L3_Lab_research_128x54/frame_12.bm
F:cf3bb68dc78c568db22f37057a9fdd66:615:dolphin/L3_Lab_research_128x54/frame_13.bm
F:79719219aaebc95ea525def9173cabf5:618:dolphin/L3_Lab_research_128x54/frame_2.bm
F:05572cfd756704acd6ce9d6c15d03fc0:608:dolphin/L3_Lab_research_128x54/frame_3.bm
F:a26604a0d5427d5cf62a7a911a68b16c:615:dolphin/L3_Lab_research_128x54/frame_4.bm
F:9edc345fe53017970f93dc680818e63e:618:dolphin/L3_Lab_research_128x54/frame_5.bm
F:cf3bb68dc78c568db22f37057a9fdd66:615:dolphin/L3_Lab_research_128x54/frame_6.bm
F:5442895c85f769349288aa3df0990f9d:585:dolphin/L3_Lab_research_128x54/frame_7.bm
F:33b8fde22f34ef556b64b77164bc19b0:578:dolphin/L3_Lab_research_128x54/frame_8.bm
F:f267f0654781049ca323b11bb4375519:581:dolphin/L3_Lab_research_128x54/frame_9.bm
F:41106c0cbc5144f151b2b2d3daaa0527:727:dolphin/L3_Lab_research_128x54/meta.txt
D:infrared/assets
F:5b16e1a59daf3ef1d0fc95b3b5596d67:74300:infrared/assets/tv.ir
D:nfc/assets
F:c6826a621d081d68309e4be424d3d974:4715:nfc/assets/aid.nfc
F:86efbebdf41bb6bf15cc51ef88f069d5:2565:nfc/assets/country_code.nfc
F:41b4f08774249014cb8d3dffa5f5c07d:1757:nfc/assets/currency_code.nfc
F:c60e862919731b0bd538a1001bbc1098:17453:nfc/assets/mf_classic_dict.nfc
D:subghz/assets
F:dda1ef895b8a25fde57c874feaaef997:650:subghz/assets/came_atomo
F:610a0ffa2479a874f2060eb2348104c5:2712:subghz/assets/keeloq_mfcodes
F:9214f9c10463b746a27e82ce0b96e040:465:subghz/assets/keeloq_mfcodes_user
F:653bd8d349055a41e1152e557d4a52d3:202:subghz/assets/nice_flor_s
F:00e967e5c558e44a0651bb821d5cf1d0:414:subghz/assets/setting_frequency_analyzer_user
F:16e8c7cb4a13f26ea55b2b0a59f9cc7a:554:subghz/assets/setting_user
D:u2f/assets
F:7e11e688e39034bbb9d88410044795e1:365:u2f/assets/cert.der
F:f60b88c20ed479ed9684e249f7134618:264:u2f/assets/cert_key.u2f

View File

@@ -48,13 +48,12 @@ void furi_hal_nfc_exit_sleep() {
rfalLowPowerModeStop(); rfalLowPowerModeStop();
} }
bool furi_hal_nfc_detect( bool furi_hal_nfc_detect(FuriHalNfcDevData* nfc_data, uint32_t timeout) {
rfalNfcDevice** dev_list, furi_assert(nfc_data);
uint8_t* dev_cnt,
uint32_t timeout, rfalNfcDevice* dev_list = NULL;
bool deactivate) { uint8_t dev_cnt = 0;
furi_assert(dev_list); bool detected = false;
furi_assert(dev_cnt);
rfalLowPowerModeStop(); rfalLowPowerModeStop();
rfalNfcState state = rfalNfcGetState(); rfalNfcState state = rfalNfcGetState();
@@ -77,9 +76,13 @@ bool furi_hal_nfc_detect(
uint32_t start = DWT->CYCCNT; uint32_t start = DWT->CYCCNT;
rfalNfcDiscover(&params); rfalNfcDiscover(&params);
while(state != RFAL_NFC_STATE_ACTIVATED) { while(true) {
rfalNfcWorker(); rfalNfcWorker();
state = rfalNfcGetState(); state = rfalNfcGetState();
if(state == RFAL_NFC_STATE_ACTIVATED) {
detected = true;
break;
}
FURI_LOG_T(TAG, "Current state %d", state); FURI_LOG_T(TAG, "Current state %d", state);
if(state == RFAL_NFC_STATE_POLL_ACTIVATION) { if(state == RFAL_NFC_STATE_POLL_ACTIVATION) {
start = DWT->CYCCNT; start = DWT->CYCCNT;
@@ -91,16 +94,42 @@ bool furi_hal_nfc_detect(
if(DWT->CYCCNT - start > timeout * clocks_in_ms) { if(DWT->CYCCNT - start > timeout * clocks_in_ms) {
rfalNfcDeactivate(true); rfalNfcDeactivate(true);
FURI_LOG_T(TAG, "Timeout"); FURI_LOG_T(TAG, "Timeout");
return false; break;
} }
osThreadYield(); osThreadYield();
} }
rfalNfcGetDevicesFound(dev_list, dev_cnt); rfalNfcGetDevicesFound(&dev_list, &dev_cnt);
if(deactivate) { if(detected) {
rfalNfcDeactivate(false); if(dev_list[0].type == RFAL_NFC_LISTEN_TYPE_NFCA) {
rfalLowPowerModeStart(); nfc_data->type = FuriHalNfcTypeA;
nfc_data->atqa[0] = dev_list[0].dev.nfca.sensRes.anticollisionInfo;
nfc_data->atqa[1] = dev_list[0].dev.nfca.sensRes.platformInfo;
nfc_data->sak = dev_list[0].dev.nfca.selRes.sak;
uint8_t* cuid_start = dev_list[0].nfcid;
if(dev_list[0].nfcidLen == 7) {
cuid_start = &dev_list[0].nfcid[3];
}
nfc_data->cuid = (cuid_start[0] << 24) | (cuid_start[1] << 16) | (cuid_start[2] << 8) |
(cuid_start[3]);
} else if(dev_list[0].type == RFAL_NFC_LISTEN_TYPE_NFCB) {
nfc_data->type = FuriHalNfcTypeB;
} else if(dev_list[0].type == RFAL_NFC_LISTEN_TYPE_NFCF) {
nfc_data->type = FuriHalNfcTypeF;
} else if(dev_list[0].type == RFAL_NFC_LISTEN_TYPE_NFCV) {
nfc_data->type = FuriHalNfcTypeV;
}
if(dev_list[0].rfInterface == RFAL_NFC_INTERFACE_RF) {
nfc_data->interface = FuriHalNfcInterfaceRf;
} else if(dev_list[0].rfInterface == RFAL_NFC_INTERFACE_ISODEP) {
nfc_data->interface = FuriHalNfcInterfaceIsoDep;
} else if(dev_list[0].rfInterface == RFAL_NFC_INTERFACE_NFCDEP) {
nfc_data->interface = FuriHalNfcInterfaceNfcDep;
}
nfc_data->uid_len = dev_list[0].nfcidLen;
memcpy(nfc_data->uid, dev_list[0].nfcid, nfc_data->uid_len);
} }
return true;
return detected;
} }
bool furi_hal_nfc_activate_nfca(uint32_t timeout, uint32_t* cuid) { bool furi_hal_nfc_activate_nfca(uint32_t timeout, uint32_t* cuid) {
@@ -326,12 +355,6 @@ bool furi_hal_nfc_emulate_nfca(
return true; return true;
} }
bool furi_hal_nfc_get_first_frame(uint8_t** rx_buff, uint16_t** rx_len) {
ReturnCode ret =
rfalNfcDataExchangeStart(NULL, 0, rx_buff, rx_len, 0, RFAL_TXRX_FLAGS_DEFAULT);
return ret == ERR_NONE;
}
ReturnCode furi_hal_nfc_data_exchange( ReturnCode furi_hal_nfc_data_exchange(
uint8_t* tx_buff, uint8_t* tx_buff,
uint16_t tx_len, uint16_t tx_len,
@@ -370,6 +393,22 @@ ReturnCode furi_hal_nfc_data_exchange(
return ret; return ret;
} }
static uint32_t furi_hal_nfc_tx_rx_get_flag(FuriHalNfcTxRxType type) {
uint32_t flags = 0;
if(type == FuriHalNfcTxRxTypeRxNoCrc) {
flags = RFAL_TXRX_FLAGS_CRC_RX_KEEP;
} else if(type == FuriHalNfcTxRxTypeRxKeepPar) {
flags = RFAL_TXRX_FLAGS_CRC_TX_MANUAL | RFAL_TXRX_FLAGS_CRC_RX_KEEP |
RFAL_TXRX_FLAGS_PAR_RX_KEEP;
} else if(type == FuriHalNfcTxRxTypeRaw) {
flags = RFAL_TXRX_FLAGS_CRC_TX_MANUAL | RFAL_TXRX_FLAGS_CRC_RX_KEEP |
RFAL_TXRX_FLAGS_PAR_RX_KEEP | RFAL_TXRX_FLAGS_PAR_TX_NONE;
}
return flags;
}
static uint16_t furi_hal_nfc_data_and_parity_to_bitstream( static uint16_t furi_hal_nfc_data_and_parity_to_bitstream(
uint8_t* data, uint8_t* data,
uint16_t len, uint16_t len,
@@ -420,8 +459,8 @@ uint16_t furi_hal_nfc_bitstream_to_data_and_parity(
return curr_byte; return curr_byte;
} }
bool furi_hal_nfc_tx_rx(FuriHalNfcTxRxContext* tx_rx_ctx) { bool furi_hal_nfc_tx_rx(FuriHalNfcTxRxContext* tx_rx, uint16_t timeout_ms) {
furi_assert(tx_rx_ctx); furi_assert(tx_rx);
ReturnCode ret; ReturnCode ret;
rfalNfcState state = RFAL_NFC_STATE_ACTIVATED; rfalNfcState state = RFAL_NFC_STATE_ACTIVATED;
@@ -431,26 +470,18 @@ bool furi_hal_nfc_tx_rx(FuriHalNfcTxRxContext* tx_rx_ctx) {
uint16_t* temp_rx_bits = NULL; uint16_t* temp_rx_bits = NULL;
// Prepare data for FIFO if necessary // Prepare data for FIFO if necessary
if(tx_rx_ctx->tx_rx_type == FURI_HAL_NFC_TXRX_RAW) { uint32_t flags = furi_hal_nfc_tx_rx_get_flag(tx_rx->tx_rx_type);
if(tx_rx->tx_rx_type == FuriHalNfcTxRxTypeRaw) {
temp_tx_bits = furi_hal_nfc_data_and_parity_to_bitstream( temp_tx_bits = furi_hal_nfc_data_and_parity_to_bitstream(
tx_rx_ctx->tx_data, tx_rx_ctx->tx_bits / 8, tx_rx_ctx->tx_parity, temp_tx_buff); tx_rx->tx_data, tx_rx->tx_bits / 8, tx_rx->tx_parity, temp_tx_buff);
ret = rfalNfcDataExchangeCustomStart( ret = rfalNfcDataExchangeCustomStart(
temp_tx_buff, temp_tx_buff, temp_tx_bits, &temp_rx_buff, &temp_rx_bits, RFAL_FWT_NONE, flags);
temp_tx_bits,
&temp_rx_buff,
&temp_rx_bits,
RFAL_FWT_NONE,
tx_rx_ctx->tx_rx_type);
} else { } else {
ret = rfalNfcDataExchangeCustomStart( ret = rfalNfcDataExchangeCustomStart(
tx_rx_ctx->tx_data, tx_rx->tx_data, tx_rx->tx_bits, &temp_rx_buff, &temp_rx_bits, RFAL_FWT_NONE, flags);
tx_rx_ctx->tx_bits,
&temp_rx_buff,
&temp_rx_bits,
RFAL_FWT_NONE,
tx_rx_ctx->tx_rx_type);
} }
if(ret != ERR_NONE) { if(ret != ERR_NONE) {
FURI_LOG_E(TAG, "Failed to start data exchange");
return false; return false;
} }
uint32_t start = DWT->CYCCNT; uint32_t start = DWT->CYCCNT;
@@ -459,28 +490,64 @@ bool furi_hal_nfc_tx_rx(FuriHalNfcTxRxContext* tx_rx_ctx) {
state = rfalNfcGetState(); state = rfalNfcGetState();
ret = rfalNfcDataExchangeGetStatus(); ret = rfalNfcDataExchangeGetStatus();
if(ret == ERR_BUSY) { if(ret == ERR_BUSY) {
if(DWT->CYCCNT - start > 4 * clocks_in_ms) { if(DWT->CYCCNT - start > timeout_ms * clocks_in_ms) {
FURI_LOG_D(TAG, "Timeout during data exchange");
return false; return false;
} }
continue; continue;
} else { } else {
start = DWT->CYCCNT; start = DWT->CYCCNT;
} }
taskYIELD(); osThreadYield();
} }
if(tx_rx_ctx->tx_rx_type == FURI_HAL_NFC_TXRX_RAW) { if(tx_rx->tx_rx_type == FuriHalNfcTxRxTypeRaw) {
tx_rx_ctx->rx_bits = tx_rx->rx_bits = 8 * furi_hal_nfc_bitstream_to_data_and_parity(
8 * furi_hal_nfc_bitstream_to_data_and_parity( temp_rx_buff, *temp_rx_bits, tx_rx->rx_data, tx_rx->rx_parity);
temp_rx_buff, *temp_rx_bits, tx_rx_ctx->rx_data, tx_rx_ctx->rx_parity);
} else { } else {
memcpy(tx_rx_ctx->rx_data, temp_rx_buff, *temp_rx_bits / 8); memcpy(tx_rx->rx_data, temp_rx_buff, MIN(*temp_rx_bits / 8, FURI_HAL_NFC_DATA_BUFF_SIZE));
tx_rx->rx_bits = *temp_rx_bits;
} }
return true; return true;
} }
void furi_hal_nfc_deactivate() { ReturnCode furi_hal_nfc_exchange_full(
uint8_t* tx_buff,
uint16_t tx_len,
uint8_t* rx_buff,
uint16_t rx_cap,
uint16_t* rx_len) {
ReturnCode err;
uint8_t* part_buff;
uint16_t* part_len_bits;
uint16_t part_len_bytes;
err = furi_hal_nfc_data_exchange(tx_buff, tx_len, &part_buff, &part_len_bits, false);
part_len_bytes = *part_len_bits / 8;
if(part_len_bytes > rx_cap) {
return ERR_OVERRUN;
}
memcpy(rx_buff, part_buff, part_len_bytes);
*rx_len = part_len_bytes;
while(err == ERR_NONE && rx_buff[0] == 0xAF) {
err = furi_hal_nfc_data_exchange(rx_buff, 1, &part_buff, &part_len_bits, false);
part_len_bytes = *part_len_bits / 8;
if(part_len_bytes > rx_cap - *rx_len) {
return ERR_OVERRUN;
}
if(part_len_bytes == 0) {
return ERR_PROTO;
}
memcpy(rx_buff + *rx_len, part_buff + 1, part_len_bytes - 1);
*rx_buff = *part_buff;
*rx_len += part_len_bytes - 1;
}
return err;
}
void furi_hal_nfc_sleep() {
rfalNfcDeactivate(false); rfalNfcDeactivate(false);
rfalLowPowerModeStart(); rfalLowPowerModeStart();
} }

View File

@@ -15,32 +15,31 @@ extern "C" {
#endif #endif
#define FURI_HAL_NFC_UID_MAX_LEN 10 #define FURI_HAL_NFC_UID_MAX_LEN 10
#define FURI_HAL_NFC_DATA_BUFF_SIZE (64) #define FURI_HAL_NFC_DATA_BUFF_SIZE (256)
#define FURI_HAL_NFC_PARITY_BUFF_SIZE (FURI_HAL_NFC_DATA_BUFF_SIZE / 8) #define FURI_HAL_NFC_PARITY_BUFF_SIZE (FURI_HAL_NFC_DATA_BUFF_SIZE / 8)
#define FURI_HAL_NFC_TXRX_DEFAULT \ #define FURI_HAL_NFC_TXRX_DEFAULT \
((uint32_t)RFAL_TXRX_FLAGS_CRC_TX_AUTO | (uint32_t)RFAL_TXRX_FLAGS_CRC_RX_REMV | \ ((uint32_t)RFAL_TXRX_FLAGS_CRC_TX_AUTO | (uint32_t)RFAL_TXRX_FLAGS_CRC_RX_REMV | \
(uint32_t)RFAL_TXRX_FLAGS_NFCIP1_OFF | (uint32_t)RFAL_TXRX_FLAGS_AGC_ON | \ (uint32_t)RFAL_TXRX_FLAGS_PAR_RX_REMV | (uint32_t)RFAL_TXRX_FLAGS_PAR_TX_AUTO)
(uint32_t)RFAL_TXRX_FLAGS_PAR_RX_REMV | (uint32_t)RFAL_TXRX_FLAGS_PAR_TX_AUTO | \
(uint32_t)RFAL_TXRX_FLAGS_NFCV_FLAG_AUTO)
#define FURI_HAL_NFC_TX_DEFAULT_RX_NO_CRC \ #define FURI_HAL_NFC_TX_DEFAULT_RX_NO_CRC \
((uint32_t)RFAL_TXRX_FLAGS_CRC_TX_AUTO | (uint32_t)RFAL_TXRX_FLAGS_CRC_RX_KEEP | \ ((uint32_t)RFAL_TXRX_FLAGS_CRC_TX_AUTO | (uint32_t)RFAL_TXRX_FLAGS_CRC_RX_KEEP | \
(uint32_t)RFAL_TXRX_FLAGS_NFCIP1_OFF | (uint32_t)RFAL_TXRX_FLAGS_AGC_ON | \ (uint32_t)RFAL_TXRX_FLAGS_PAR_RX_REMV | (uint32_t)RFAL_TXRX_FLAGS_PAR_TX_AUTO)
(uint32_t)RFAL_TXRX_FLAGS_PAR_RX_REMV | (uint32_t)RFAL_TXRX_FLAGS_PAR_TX_AUTO | \
(uint32_t)RFAL_TXRX_FLAGS_NFCV_FLAG_AUTO)
#define FURI_HAL_NFC_TXRX_WITH_PAR \ #define FURI_HAL_NFC_TXRX_WITH_PAR \
((uint32_t)RFAL_TXRX_FLAGS_CRC_TX_MANUAL | (uint32_t)RFAL_TXRX_FLAGS_CRC_RX_KEEP | \ ((uint32_t)RFAL_TXRX_FLAGS_CRC_TX_MANUAL | (uint32_t)RFAL_TXRX_FLAGS_CRC_RX_KEEP | \
(uint32_t)RFAL_TXRX_FLAGS_NFCIP1_OFF | (uint32_t)RFAL_TXRX_FLAGS_AGC_ON | \ (uint32_t)RFAL_TXRX_FLAGS_PAR_RX_KEEP | (uint32_t)RFAL_TXRX_FLAGS_PAR_TX_AUTO)
(uint32_t)RFAL_TXRX_FLAGS_PAR_RX_KEEP | (uint32_t)RFAL_TXRX_FLAGS_PAR_TX_AUTO | \
(uint32_t)RFAL_TXRX_FLAGS_NFCV_FLAG_AUTO)
#define FURI_HAL_NFC_TXRX_RAW \ #define FURI_HAL_NFC_TXRX_RAW \
((uint32_t)RFAL_TXRX_FLAGS_CRC_TX_MANUAL | (uint32_t)RFAL_TXRX_FLAGS_CRC_RX_KEEP | \ ((uint32_t)RFAL_TXRX_FLAGS_CRC_TX_MANUAL | (uint32_t)RFAL_TXRX_FLAGS_CRC_RX_KEEP | \
(uint32_t)RFAL_TXRX_FLAGS_NFCIP1_OFF | (uint32_t)RFAL_TXRX_FLAGS_AGC_ON | \ (uint32_t)RFAL_TXRX_FLAGS_PAR_RX_KEEP | (uint32_t)RFAL_TXRX_FLAGS_PAR_TX_NONE)
(uint32_t)RFAL_TXRX_FLAGS_PAR_RX_KEEP | (uint32_t)RFAL_TXRX_FLAGS_PAR_TX_NONE | \
(uint32_t)RFAL_TXRX_FLAGS_NFCV_FLAG_AUTO) typedef enum {
FuriHalNfcTxRxTypeDefault,
FuriHalNfcTxRxTypeRxNoCrc,
FuriHalNfcTxRxTypeRxKeepPar,
FuriHalNfcTxRxTypeRaw,
} FuriHalNfcTxRxType;
typedef bool (*FuriHalNfcEmulateCallback)( typedef bool (*FuriHalNfcEmulateCallback)(
uint8_t* buff_rx, uint8_t* buff_rx,
@@ -50,6 +49,29 @@ typedef bool (*FuriHalNfcEmulateCallback)(
uint32_t* flags, uint32_t* flags,
void* context); void* context);
typedef enum {
FuriHalNfcTypeA,
FuriHalNfcTypeB,
FuriHalNfcTypeF,
FuriHalNfcTypeV,
} FuriHalNfcType;
typedef enum {
FuriHalNfcInterfaceRf,
FuriHalNfcInterfaceIsoDep,
FuriHalNfcInterfaceNfcDep,
} FuriHalNfcInterface;
typedef struct {
FuriHalNfcType type;
FuriHalNfcInterface interface;
uint8_t uid_len;
uint8_t uid[10];
uint32_t cuid;
uint8_t atqa[2];
uint8_t sak;
} FuriHalNfcDevData;
typedef struct { typedef struct {
uint8_t tx_data[FURI_HAL_NFC_DATA_BUFF_SIZE]; uint8_t tx_data[FURI_HAL_NFC_DATA_BUFF_SIZE];
uint8_t tx_parity[FURI_HAL_NFC_PARITY_BUFF_SIZE]; uint8_t tx_parity[FURI_HAL_NFC_PARITY_BUFF_SIZE];
@@ -57,7 +79,7 @@ typedef struct {
uint8_t rx_data[FURI_HAL_NFC_DATA_BUFF_SIZE]; uint8_t rx_data[FURI_HAL_NFC_DATA_BUFF_SIZE];
uint8_t rx_parity[FURI_HAL_NFC_PARITY_BUFF_SIZE]; uint8_t rx_parity[FURI_HAL_NFC_PARITY_BUFF_SIZE];
uint16_t rx_bits; uint16_t rx_bits;
uint32_t tx_rx_type; FuriHalNfcTxRxType tx_rx_type;
} FuriHalNfcTxRxContext; } FuriHalNfcTxRxContext;
/** Init nfc /** Init nfc
@@ -95,11 +117,7 @@ void furi_hal_nfc_exit_sleep();
* *
* @return true on success * @return true on success
*/ */
bool furi_hal_nfc_detect( bool furi_hal_nfc_detect(FuriHalNfcDevData* nfc_data, uint32_t timeout);
rfalNfcDevice** dev_list,
uint8_t* dev_cnt,
uint32_t timeout,
bool deactivate);
/** Activate NFC-A tag /** Activate NFC-A tag
* *
@@ -138,15 +156,6 @@ bool furi_hal_nfc_emulate_nfca(
void* context, void* context,
uint32_t timeout); uint32_t timeout);
/** Get first command from reader after activation in emulation mode
*
* @param rx_buff pointer to receive buffer
* @param rx_len receive buffer length
*
* @return true on success
*/
bool furi_hal_nfc_get_first_frame(uint8_t** rx_buff, uint16_t** rx_len);
/** NFC data exchange /** NFC data exchange
* *
* @param tx_buff transmit buffer * @param tx_buff transmit buffer
@@ -170,11 +179,28 @@ ReturnCode furi_hal_nfc_data_exchange(
* *
* @return true on success * @return true on success
*/ */
bool furi_hal_nfc_tx_rx(FuriHalNfcTxRxContext* tx_rx_ctx); bool furi_hal_nfc_tx_rx(FuriHalNfcTxRxContext* tx_rx, uint16_t timeout_ms);
/** NFC data full exhange
*
* @param tx_buff transmit buffer
* @param tx_len transmit buffer length
* @param rx_buff receive buffer
* @param rx_cap receive buffer capacity
* @param rx_len receive buffer length
*
* @return ST ReturnCode
*/
ReturnCode furi_hal_nfc_exchange_full(
uint8_t* tx_buff,
uint16_t tx_len,
uint8_t* rx_buff,
uint16_t rx_cap,
uint16_t* rx_len);
/** NFC deactivate and start sleep /** NFC deactivate and start sleep
*/ */
void furi_hal_nfc_deactivate(); void furi_hal_nfc_sleep();
void furi_hal_nfc_stop(); void furi_hal_nfc_stop();

View File

@@ -3015,7 +3015,7 @@ ReturnCode rfalIsoDepGetApduTransceiveStatus(void) {
} }
/* Update output param rxLen */ /* Update output param rxLen */
*gIsoDep.APDUParam.rxLen = gIsoDep.APDURxPos; *gIsoDep.APDUParam.rxLen = gIsoDep.APDURxPos * 8;
/* Wait for following I-Block or APDU TxRx has finished */ /* Wait for following I-Block or APDU TxRx has finished */
return ((ret == ERR_AGAIN) ? ERR_BUSY : ERR_NONE); return ((ret == ERR_AGAIN) ? ERR_BUSY : ERR_NONE);

View File

@@ -725,9 +725,14 @@ ReturnCode rfalNfcDataExchangeCustomStart(
{ {
/*******************************************************************************/ /*******************************************************************************/
case RFAL_NFC_INTERFACE_RF: case RFAL_NFC_INTERFACE_RF:
ctx.rxBuf = gNfcDev.rxBuf.rfBuf, ctx.rxBufLen = sizeof(gNfcDev.rxBuf.rfBuf), ctx.rxBuf = gNfcDev.rxBuf.rfBuf;
ctx.rxRcvdLen = &gNfcDev.rxLen, ctx.txBuf = txData, ctx.txBufLen = txDataLen, ctx.rxBufLen = 8 * sizeof(gNfcDev.rxBuf.rfBuf);
ctx.flags = flags, ctx.fwt = fwt, *rxData = (uint8_t*)gNfcDev.rxBuf.rfBuf; ctx.rxRcvdLen = &gNfcDev.rxLen;
ctx.txBuf = txData;
ctx.txBufLen = txDataLen;
ctx.flags = flags;
ctx.fwt = fwt;
*rxData = (uint8_t*)gNfcDev.rxBuf.rfBuf;
*rvdLen = (uint16_t*)&gNfcDev.rxLen; *rvdLen = (uint16_t*)&gNfcDev.rxLen;
err = rfalStartTransceive(&ctx); err = rfalStartTransceive(&ctx);
break; break;
@@ -736,13 +741,14 @@ ReturnCode rfalNfcDataExchangeCustomStart(
/*******************************************************************************/ /*******************************************************************************/
case RFAL_NFC_INTERFACE_ISODEP: { case RFAL_NFC_INTERFACE_ISODEP: {
rfalIsoDepApduTxRxParam isoDepTxRx; rfalIsoDepApduTxRxParam isoDepTxRx;
uint16_t tx_bytes = txDataLen / 8;
if(txDataLen > sizeof(gNfcDev.txBuf.isoDepBuf.apdu)) { if(tx_bytes > sizeof(gNfcDev.txBuf.isoDepBuf.apdu)) {
return ERR_NOMEM; return ERR_NOMEM;
} }
if(txDataLen > 0U) { if(tx_bytes > 0U) {
ST_MEMCPY((uint8_t*)gNfcDev.txBuf.isoDepBuf.apdu, txData, txDataLen); ST_MEMCPY((uint8_t*)gNfcDev.txBuf.isoDepBuf.apdu, txData, tx_bytes);
} }
isoDepTxRx.DID = RFAL_ISODEP_NO_DID; isoDepTxRx.DID = RFAL_ISODEP_NO_DID;
@@ -751,7 +757,7 @@ ReturnCode rfalNfcDataExchangeCustomStart(
isoDepTxRx.dFWT = gNfcDev.activeDev->proto.isoDep.info.dFWT; isoDepTxRx.dFWT = gNfcDev.activeDev->proto.isoDep.info.dFWT;
isoDepTxRx.FWT = gNfcDev.activeDev->proto.isoDep.info.FWT; isoDepTxRx.FWT = gNfcDev.activeDev->proto.isoDep.info.FWT;
isoDepTxRx.txBuf = &gNfcDev.txBuf.isoDepBuf; isoDepTxRx.txBuf = &gNfcDev.txBuf.isoDepBuf;
isoDepTxRx.txBufLen = txDataLen; isoDepTxRx.txBufLen = tx_bytes;
isoDepTxRx.rxBuf = &gNfcDev.rxBuf.isoDepBuf; isoDepTxRx.rxBuf = &gNfcDev.rxBuf.isoDepBuf;
isoDepTxRx.rxLen = &gNfcDev.rxLen; isoDepTxRx.rxLen = &gNfcDev.rxLen;
isoDepTxRx.tmpBuf = &gNfcDev.tmpBuf.isoDepBuf; isoDepTxRx.tmpBuf = &gNfcDev.tmpBuf.isoDepBuf;

View File

@@ -1,4 +1,8 @@
#include "emv_decoder.h" #include "emv.h"
#include <furi/common_defines.h>
#define TAG "Emv"
const PDOLValue pdol_term_info = {0x9F59, {0xC8, 0x80, 0x00}}; // Terminal transaction information const PDOLValue pdol_term_info = {0x9F59, {0xC8, 0x80, 0x00}}; // Terminal transaction information
const PDOLValue pdol_term_type = {0x9F5A, {0x00}}; // Terminal transaction type const PDOLValue pdol_term_type = {0x9F5A, {0x00}}; // Terminal transaction type
@@ -69,19 +73,6 @@ static bool emv_decode_search_tag_u16_r(uint16_t tag, uint8_t* buff, uint16_t* i
return false; return false;
} }
uint16_t emv_prepare_select_ppse(uint8_t* dest) {
const uint8_t emv_select_ppse[] = {
0x00, 0xA4, // SELECT ppse
0x04, 0x00, // P1:By name, P2: empty
0x0e, // Lc: Data length
0x32, 0x50, 0x41, 0x59, 0x2e, 0x53, 0x59, // Data string:
0x53, 0x2e, 0x44, 0x44, 0x46, 0x30, 0x31, // 2PAY.SYS.DDF01 (PPSE)
0x00 // Le
};
memcpy(dest, emv_select_ppse, sizeof(emv_select_ppse));
return sizeof(emv_select_ppse);
}
bool emv_decode_ppse_response(uint8_t* buff, uint16_t len, EmvApplication* app) { bool emv_decode_ppse_response(uint8_t* buff, uint16_t len, EmvApplication* app) {
uint16_t i = 0; uint16_t i = 0;
bool app_aid_found = false; bool app_aid_found = false;
@@ -89,7 +80,7 @@ bool emv_decode_ppse_response(uint8_t* buff, uint16_t len, EmvApplication* app)
while(i < len) { while(i < len) {
if(buff[i] == EMV_TAG_APP_TEMPLATE) { if(buff[i] == EMV_TAG_APP_TEMPLATE) {
uint8_t app_len = buff[++i]; uint8_t app_len = buff[++i];
for(uint16_t j = i; j < i + app_len; j++) { for(uint16_t j = i; j < MIN(i + app_len, len - 1); j++) {
if(buff[j] == EMV_TAG_AID) { if(buff[j] == EMV_TAG_AID) {
app_aid_found = true; app_aid_found = true;
app->aid_len = buff[j + 1]; app->aid_len = buff[j + 1];
@@ -105,7 +96,59 @@ bool emv_decode_ppse_response(uint8_t* buff, uint16_t len, EmvApplication* app)
return app_aid_found; return app_aid_found;
} }
uint16_t emv_prepare_select_app(uint8_t* dest, EmvApplication* app) { bool emv_select_ppse(FuriHalNfcTxRxContext* tx_rx, EmvApplication* app) {
bool app_aid_found = false;
const uint8_t emv_select_ppse_cmd[] = {
0x00, 0xA4, // SELECT ppse
0x04, 0x00, // P1:By name, P2: empty
0x0e, // Lc: Data length
0x32, 0x50, 0x41, 0x59, 0x2e, 0x53, 0x59, // Data string:
0x53, 0x2e, 0x44, 0x44, 0x46, 0x30, 0x31, // 2PAY.SYS.DDF01 (PPSE)
0x00 // Le
};
memcpy(tx_rx->tx_data, emv_select_ppse_cmd, sizeof(emv_select_ppse_cmd));
tx_rx->tx_bits = sizeof(emv_select_ppse_cmd) * 8;
tx_rx->tx_rx_type = FuriHalNfcTxRxTypeDefault;
FURI_LOG_D(TAG, "Send select PPSE");
if(furi_hal_nfc_tx_rx(tx_rx, 300)) {
if(emv_decode_ppse_response(tx_rx->rx_data, tx_rx->rx_bits / 8, app)) {
app_aid_found = true;
} else {
FURI_LOG_E(TAG, "Failed to parse application");
}
} else {
FURI_LOG_E(TAG, "Failed select PPSE");
}
return app_aid_found;
}
static bool emv_decode_select_app_response(uint8_t* buff, uint16_t len, EmvApplication* app) {
uint16_t i = 0;
bool decode_success = false;
while(i < len) {
if(buff[i] == EMV_TAG_CARD_NAME) {
uint8_t name_len = buff[i + 1];
emv_parse_TLV((uint8_t*)app->name, buff, &i);
app->name[name_len] = '\0';
app->name_found = true;
decode_success = true;
} else if(((buff[i] << 8) | buff[i + 1]) == EMV_TAG_PDOL) {
i++;
app->pdol.size = emv_parse_TLV(app->pdol.data, buff, &i);
decode_success = true;
}
i++;
}
return decode_success;
}
bool emv_select_app(FuriHalNfcTxRxContext* tx_rx, EmvApplication* app) {
bool select_app_success = false;
const uint8_t emv_select_header[] = { const uint8_t emv_select_header[] = {
0x00, 0x00,
0xA4, // SELECT application 0xA4, // SELECT application
@@ -113,33 +156,29 @@ uint16_t emv_prepare_select_app(uint8_t* dest, EmvApplication* app) {
0x00 // P1:By name, P2:First or only occurence 0x00 // P1:By name, P2:First or only occurence
}; };
uint16_t size = sizeof(emv_select_header); uint16_t size = sizeof(emv_select_header);
// Copy header // Copy header
memcpy(dest, emv_select_header, size); memcpy(tx_rx->tx_data, emv_select_header, size);
// Copy AID // Copy AID
dest[size++] = app->aid_len; tx_rx->tx_data[size++] = app->aid_len;
memcpy(&dest[size], app->aid, app->aid_len); memcpy(&tx_rx->tx_data[size], app->aid, app->aid_len);
size += app->aid_len; size += app->aid_len;
dest[size++] = 0; tx_rx->tx_data[size++] = 0x00;
return size; tx_rx->tx_bits = size * 8;
} tx_rx->tx_rx_type = FuriHalNfcTxRxTypeDefault;
bool emv_decode_select_app_response(uint8_t* buff, uint16_t len, EmvApplication* app) { FURI_LOG_D(TAG, "Start application");
uint16_t i = 0; if(furi_hal_nfc_tx_rx(tx_rx, 300)) {
bool found_name = false; if(emv_decode_select_app_response(tx_rx->rx_data, tx_rx->rx_bits / 8, app)) {
select_app_success = true;
while(i < len) { } else {
if(buff[i] == EMV_TAG_CARD_NAME) { FURI_LOG_E(TAG, "Failed to read PAN or PDOL");
uint8_t name_len = buff[i + 1];
emv_parse_TLV((uint8_t*)app->name, buff, &i);
app->name[name_len] = '\0';
found_name = true;
} else if(((buff[i] << 8) | buff[i + 1]) == EMV_TAG_PDOL) {
i++;
app->pdol.size = emv_parse_TLV(app->pdol.data, buff, &i);
} }
i++; } else {
FURI_LOG_E(TAG, "Failed to start application");
} }
return found_name;
return select_app_success;
} }
static uint16_t emv_prepare_pdol(APDU* dest, APDU* src) { static uint16_t emv_prepare_pdol(APDU* dest, APDU* src) {
@@ -175,53 +214,56 @@ static uint16_t emv_prepare_pdol(APDU* dest, APDU* src) {
return dest->size; return dest->size;
} }
uint16_t emv_prepare_get_proc_opt(uint8_t* dest, EmvApplication* app) { static bool emv_decode_get_proc_opt(uint8_t* buff, uint16_t len, EmvApplication* app) {
// Get processing option header bool card_num_read = false;
const uint8_t emv_gpo_header[] = {0x80, 0xA8, 0x00, 0x00};
uint16_t size = sizeof(emv_gpo_header);
// Copy header
memcpy(dest, emv_gpo_header, size);
APDU pdol_data = {0, {0}};
// Prepare and copy pdol parameters
emv_prepare_pdol(&pdol_data, &app->pdol);
dest[size++] = 0x02 + pdol_data.size;
dest[size++] = 0x83;
dest[size++] = pdol_data.size;
memcpy(dest + size, pdol_data.data, pdol_data.size);
size += pdol_data.size;
dest[size++] = 0;
return size;
}
bool emv_decode_get_proc_opt(uint8_t* buff, uint16_t len, EmvApplication* app) {
for(uint16_t i = 0; i < len; i++) { for(uint16_t i = 0; i < len; i++) {
if(buff[i] == EMV_TAG_CARD_NUM) { if(buff[i] == EMV_TAG_CARD_NUM) {
app->card_number_len = 8; app->card_number_len = 8;
memcpy(app->card_number, &buff[i + 2], app->card_number_len); memcpy(app->card_number, &buff[i + 2], app->card_number_len);
return true; card_num_read = true;
} else if(buff[i] == EMV_TAG_AFL) { } else if(buff[i] == EMV_TAG_AFL) {
app->afl.size = emv_parse_TLV(app->afl.data, buff, &i); app->afl.size = emv_parse_TLV(app->afl.data, buff, &i);
} }
} }
return false;
return card_num_read;
} }
uint16_t emv_prepare_read_sfi_record(uint8_t* dest, uint8_t sfi, uint8_t record_num) { static bool emv_get_processing_options(FuriHalNfcTxRxContext* tx_rx, EmvApplication* app) {
const uint8_t sfi_param = (sfi << 3) | (1 << 2); bool card_num_read = false;
const uint8_t emv_sfi_header[] = { const uint8_t emv_gpo_header[] = {0x80, 0xA8, 0x00, 0x00};
0x00, uint16_t size = sizeof(emv_gpo_header);
0xB2, // READ RECORD
record_num, // Copy header
sfi_param, // P1:record_number and P2:SFI memcpy(tx_rx->tx_data, emv_gpo_header, size);
0x00 // Le APDU pdol_data = {0, {0}};
}; // Prepare and copy pdol parameters
uint16_t size = sizeof(emv_sfi_header); emv_prepare_pdol(&pdol_data, &app->pdol);
memcpy(dest, emv_sfi_header, size); tx_rx->tx_data[size++] = 0x02 + pdol_data.size;
return size; tx_rx->tx_data[size++] = 0x83;
tx_rx->tx_data[size++] = pdol_data.size;
memcpy(tx_rx->tx_data + size, pdol_data.data, pdol_data.size);
size += pdol_data.size;
tx_rx->tx_data[size++] = 0;
tx_rx->tx_bits = size * 8;
tx_rx->tx_rx_type = FuriHalNfcTxRxTypeDefault;
FURI_LOG_D(TAG, "Get proccessing options");
if(furi_hal_nfc_tx_rx(tx_rx, 300)) {
if(emv_decode_get_proc_opt(tx_rx->rx_data, tx_rx->rx_bits / 8, app)) {
card_num_read = true;
}
} else {
FURI_LOG_E(TAG, "Failed to get processing options");
}
return card_num_read;
} }
bool emv_decode_read_sfi_record(uint8_t* buff, uint16_t len, EmvApplication* app) { static bool emv_decode_read_sfi_record(uint8_t* buff, uint16_t len, EmvApplication* app) {
bool pan_parsed = false; bool pan_parsed = false;
for(uint16_t i = 0; i < len; i++) { for(uint16_t i = 0; i < len; i++) {
if(buff[i] == EMV_TAG_PAN) { if(buff[i] == EMV_TAG_PAN) {
if(buff[i + 1] == 8 || buff[i + 1] == 10) { if(buff[i + 1] == 8 || buff[i + 1] == 10) {
@@ -240,20 +282,118 @@ bool emv_decode_read_sfi_record(uint8_t* buff, uint16_t len, EmvApplication* app
i += 2; i += 2;
} }
} }
return pan_parsed; return pan_parsed;
} }
uint16_t emv_select_ppse_ans(uint8_t* buff) { static bool emv_read_sfi_record(
memcpy(buff, select_ppse_ans, sizeof(select_ppse_ans)); FuriHalNfcTxRxContext* tx_rx,
return sizeof(select_ppse_ans); EmvApplication* app,
uint8_t sfi,
uint8_t record_num) {
bool card_num_read = false;
uint8_t sfi_param = (sfi << 3) | (1 << 2);
uint8_t emv_sfi_header[] = {
0x00,
0xB2, // READ RECORD
record_num, // P1:record_number
sfi_param, // P2:SFI
0x00 // Le
};
memcpy(tx_rx->tx_data, emv_sfi_header, sizeof(emv_sfi_header));
tx_rx->tx_bits = sizeof(emv_sfi_header) * 8;
tx_rx->tx_rx_type = FuriHalNfcTxRxTypeDefault;
if(furi_hal_nfc_tx_rx(tx_rx, 300)) {
if(emv_decode_read_sfi_record(tx_rx->rx_data, tx_rx->rx_bits / 8, app)) {
card_num_read = true;
}
} else {
FURI_LOG_E(TAG, "Failed to read SFI record %d", record_num);
}
return card_num_read;
} }
uint16_t emv_select_app_ans(uint8_t* buff) { static bool emv_read_files(FuriHalNfcTxRxContext* tx_rx, EmvApplication* app) {
memcpy(buff, select_app_ans, sizeof(select_app_ans)); bool card_num_read = false;
return sizeof(select_app_ans);
if(app->afl.size == 0) {
return false;
}
FURI_LOG_D(TAG, "Search PAN in SFI");
// Iterate through all files
for(size_t i = 0; i < app->afl.size; i += 4) {
uint8_t sfi = app->afl.data[i] >> 3;
uint8_t record_start = app->afl.data[i + 1];
uint8_t record_end = app->afl.data[i + 2];
// Iterate through all records in file
for(uint8_t record = record_start; record <= record_end; ++record) {
card_num_read |= emv_read_sfi_record(tx_rx, app, sfi, record);
}
}
return card_num_read;
} }
uint16_t emv_get_proc_opt_ans(uint8_t* buff) { bool emv_search_application(FuriHalNfcTxRxContext* tx_rx, EmvApplication* emv_app) {
memcpy(buff, pdol_ans, sizeof(pdol_ans)); furi_assert(tx_rx);
return sizeof(pdol_ans); furi_assert(emv_app);
memset(emv_app, 0, sizeof(EmvApplication));
return emv_select_ppse(tx_rx, emv_app);
}
bool emv_read_bank_card(FuriHalNfcTxRxContext* tx_rx, EmvApplication* emv_app) {
furi_assert(tx_rx);
furi_assert(emv_app);
bool card_num_read = false;
memset(emv_app, 0, sizeof(EmvApplication));
do {
if(!emv_select_ppse(tx_rx, emv_app)) break;
if(!emv_select_app(tx_rx, emv_app)) break;
if(emv_get_processing_options(tx_rx, emv_app)) {
card_num_read = true;
} else {
card_num_read = emv_read_files(tx_rx, emv_app);
}
} while(false);
return card_num_read;
}
bool emv_card_emulation(FuriHalNfcTxRxContext* tx_rx) {
furi_assert(tx_rx);
bool emulation_complete = false;
memset(tx_rx, 0, sizeof(FuriHalNfcTxRxContext));
do {
FURI_LOG_D(TAG, "Read select PPSE command");
if(!furi_hal_nfc_tx_rx(tx_rx, 300)) break;
memcpy(tx_rx->tx_data, select_ppse_ans, sizeof(select_ppse_ans));
tx_rx->tx_bits = sizeof(select_ppse_ans) * 8;
tx_rx->tx_rx_type = FuriHalNfcTxRxTypeDefault;
FURI_LOG_D(TAG, "Send select PPSE answer and read select App command");
if(!furi_hal_nfc_tx_rx(tx_rx, 300)) break;
memcpy(tx_rx->tx_data, select_app_ans, sizeof(select_app_ans));
tx_rx->tx_bits = sizeof(select_app_ans) * 8;
tx_rx->tx_rx_type = FuriHalNfcTxRxTypeDefault;
FURI_LOG_D(TAG, "Send select App answer and read get PDOL command");
if(!furi_hal_nfc_tx_rx(tx_rx, 300)) break;
memcpy(tx_rx->tx_data, pdol_ans, sizeof(pdol_ans));
tx_rx->tx_bits = sizeof(pdol_ans) * 8;
tx_rx->tx_rx_type = FuriHalNfcTxRxTypeDefault;
FURI_LOG_D(TAG, "Send get PDOL answer");
if(!furi_hal_nfc_tx_rx(tx_rx, 300)) break;
emulation_complete = true;
} while(false);
return emulation_complete;
} }

87
lib/nfc_protocols/emv.h Executable file
View File

@@ -0,0 +1,87 @@
#pragma once
#include <furi_hal_nfc.h>
#define MAX_APDU_LEN 255
#define EMV_TAG_APP_TEMPLATE 0x61
#define EMV_TAG_AID 0x4F
#define EMV_TAG_PRIORITY 0x87
#define EMV_TAG_PDOL 0x9F38
#define EMV_TAG_CARD_NAME 0x50
#define EMV_TAG_FCI 0xBF0C
#define EMV_TAG_LOG_CTRL 0x9F4D
#define EMV_TAG_CARD_NUM 0x57
#define EMV_TAG_PAN 0x5A
#define EMV_TAG_AFL 0x94
#define EMV_TAG_EXP_DATE 0x5F24
#define EMV_TAG_COUNTRY_CODE 0x5F28
#define EMV_TAG_CURRENCY_CODE 0x9F42
#define EMV_TAG_CARDHOLDER_NAME 0x5F20
typedef struct {
char name[32];
uint8_t aid[16];
uint16_t aid_len;
uint8_t number[10];
uint8_t number_len;
uint8_t exp_mon;
uint8_t exp_year;
uint16_t country_code;
uint16_t currency_code;
} EmvData;
typedef struct {
uint16_t tag;
uint8_t data[];
} PDOLValue;
typedef struct {
uint8_t size;
uint8_t data[MAX_APDU_LEN];
} APDU;
typedef struct {
uint8_t priority;
uint8_t aid[16];
uint8_t aid_len;
char name[32];
bool name_found;
uint8_t card_number[10];
uint8_t card_number_len;
uint8_t exp_month;
uint8_t exp_year;
uint16_t country_code;
uint16_t currency_code;
APDU pdol;
APDU afl;
} EmvApplication;
/** Read bank card data
* @note Search EMV Application, start it, try to read AID, PAN, card name,
* expiration date, currency and country codes
*
* @param tx_rx FuriHalNfcTxRxContext instance
* @param emv_app EmvApplication instance
*
* @return true on success
*/
bool emv_read_bank_card(FuriHalNfcTxRxContext* tx_rx, EmvApplication* emv_app);
/** Search for EMV Application
*
* @param tx_rx FuriHalNfcTxRxContext instance
* @param emv_app EmvApplication instance
*
* @return true on success
*/
bool emv_search_application(FuriHalNfcTxRxContext* tx_rx, EmvApplication* emv_app);
/** Emulate bank card
* @note Answer to application selection and PDOL
*
* @param tx_rx FuriHalNfcTxRxContext instance
*
* @return true on success
*/
bool emv_card_emulation(FuriHalNfcTxRxContext* tx_rx);

View File

@@ -1,67 +0,0 @@
#pragma once
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#define MAX_APDU_LEN 255
#define EMV_TAG_APP_TEMPLATE 0x61
#define EMV_TAG_AID 0x4F
#define EMV_TAG_PRIORITY 0x87
#define EMV_TAG_PDOL 0x9F38
#define EMV_TAG_CARD_NAME 0x50
#define EMV_TAG_FCI 0xBF0C
#define EMV_TAG_LOG_CTRL 0x9F4D
#define EMV_TAG_CARD_NUM 0x57
#define EMV_TAG_PAN 0x5A
#define EMV_TAG_AFL 0x94
#define EMV_TAG_EXP_DATE 0x5F24
#define EMV_TAG_COUNTRY_CODE 0x5F28
#define EMV_TAG_CURRENCY_CODE 0x9F42
#define EMV_TAG_CARDHOLDER_NAME 0x5F20
typedef struct {
uint16_t tag;
uint8_t data[];
} PDOLValue;
extern const PDOLValue* const pdol_values[];
typedef struct {
uint8_t size;
uint8_t data[MAX_APDU_LEN];
} APDU;
typedef struct {
uint8_t priority;
uint8_t aid[16];
uint8_t aid_len;
char name[32];
uint8_t card_number[10];
uint8_t card_number_len;
uint8_t exp_month;
uint8_t exp_year;
uint16_t country_code;
uint16_t currency_code;
APDU pdol;
APDU afl;
} EmvApplication;
/* Terminal emulation */
uint16_t emv_prepare_select_ppse(uint8_t* dest);
bool emv_decode_ppse_response(uint8_t* buff, uint16_t len, EmvApplication* app);
uint16_t emv_prepare_select_app(uint8_t* dest, EmvApplication* app);
bool emv_decode_select_app_response(uint8_t* buff, uint16_t len, EmvApplication* app);
uint16_t emv_prepare_get_proc_opt(uint8_t* dest, EmvApplication* app);
bool emv_decode_get_proc_opt(uint8_t* buff, uint16_t len, EmvApplication* app);
uint16_t emv_prepare_read_sfi_record(uint8_t* dest, uint8_t sfi, uint8_t record_num);
bool emv_decode_read_sfi_record(uint8_t* buff, uint16_t len, EmvApplication* app);
/* Card emulation */
uint16_t emv_select_ppse_ans(uint8_t* buff);
uint16_t emv_select_app_ans(uint8_t* buff);
uint16_t emv_get_proc_opt_ans(uint8_t* buff);

View File

@@ -116,17 +116,15 @@ static bool mf_classic_auth(
tx_rx->tx_data[0] = MF_CLASSIC_AUTH_KEY_B_CMD; tx_rx->tx_data[0] = MF_CLASSIC_AUTH_KEY_B_CMD;
} }
tx_rx->tx_data[1] = block; tx_rx->tx_data[1] = block;
tx_rx->tx_rx_type = FURI_HAL_NFC_TX_DEFAULT_RX_NO_CRC; tx_rx->tx_rx_type = FuriHalNfcTxRxTypeRxNoCrc;
tx_rx->tx_bits = 2 * 8; tx_rx->tx_bits = 2 * 8;
if(!furi_hal_nfc_tx_rx(tx_rx)) break; if(!furi_hal_nfc_tx_rx(tx_rx, 4)) break;
uint32_t nt = (uint32_t)nfc_util_bytes2num(tx_rx->rx_data, 4); uint32_t nt = (uint32_t)nfc_util_bytes2num(tx_rx->rx_data, 4);
crypto1_init(crypto, key); crypto1_init(crypto, key);
crypto1_word(crypto, nt ^ cuid, 0); crypto1_word(crypto, nt ^ cuid, 0);
uint8_t nr[4] = {}; uint8_t nr[4] = {};
// uint8_t parity = 0;
nfc_util_num2bytes(prng_successor(DWT->CYCCNT, 32), 4, nr); nfc_util_num2bytes(prng_successor(DWT->CYCCNT, 32), 4, nr);
// uint8_t nr_ar[8] = {};
for(uint8_t i = 0; i < 4; i++) { for(uint8_t i = 0; i < 4; i++) {
tx_rx->tx_data[i] = crypto1_byte(crypto, nr[i], 0) ^ nr[i]; tx_rx->tx_data[i] = crypto1_byte(crypto, nr[i], 0) ^ nr[i];
tx_rx->tx_parity[0] |= tx_rx->tx_parity[0] |=
@@ -140,9 +138,9 @@ static bool mf_classic_auth(
(((crypto1_filter(crypto->odd) ^ nfc_util_odd_parity8(nt & 0xff)) & 0x01) (((crypto1_filter(crypto->odd) ^ nfc_util_odd_parity8(nt & 0xff)) & 0x01)
<< (7 - i)); << (7 - i));
} }
tx_rx->tx_rx_type = FURI_HAL_NFC_TXRX_RAW; tx_rx->tx_rx_type = FuriHalNfcTxRxTypeRaw;
tx_rx->tx_bits = 8 * 8; tx_rx->tx_bits = 8 * 8;
if(!furi_hal_nfc_tx_rx(tx_rx)) break; if(!furi_hal_nfc_tx_rx(tx_rx, 4)) break;
if(tx_rx->rx_bits == 32) { if(tx_rx->rx_bits == 32) {
crypto1_word(crypto, 0, 0); crypto1_word(crypto, 0, 0);
auth_success = true; auth_success = true;
@@ -178,7 +176,7 @@ bool mf_classic_auth_attempt(
} }
if(need_halt) { if(need_halt) {
furi_hal_nfc_deactivate(); furi_hal_nfc_sleep();
furi_hal_nfc_activate_nfca(300, &auth_ctx->cuid); furi_hal_nfc_activate_nfca(300, &auth_ctx->cuid);
} }
@@ -220,9 +218,9 @@ bool mf_classic_read_block(
((crypto1_filter(crypto->odd) ^ nfc_util_odd_parity8(plain_cmd[i])) & 0x01) << (7 - i); ((crypto1_filter(crypto->odd) ^ nfc_util_odd_parity8(plain_cmd[i])) & 0x01) << (7 - i);
} }
tx_rx->tx_bits = 4 * 9; tx_rx->tx_bits = 4 * 9;
tx_rx->tx_rx_type = FURI_HAL_NFC_TXRX_RAW; tx_rx->tx_rx_type = FuriHalNfcTxRxTypeRaw;
if(furi_hal_nfc_tx_rx(tx_rx)) { if(furi_hal_nfc_tx_rx(tx_rx, 4)) {
if(tx_rx->rx_bits == 8 * 18) { if(tx_rx->rx_bits == 8 * 18) {
for(uint8_t i = 0; i < 18; i++) { for(uint8_t i = 0; i < 18; i++) {
block->value[i] = crypto1_byte(crypto, 0, 0) ^ tx_rx->rx_data[i]; block->value[i] = crypto1_byte(crypto, 0, 0) ^ tx_rx->rx_data[i];
@@ -248,7 +246,7 @@ bool mf_classic_read_sector(
uint8_t first_block; uint8_t first_block;
bool sector_read = false; bool sector_read = false;
furi_hal_nfc_deactivate(); furi_hal_nfc_sleep();
do { do {
// Activate card // Activate card
if(!furi_hal_nfc_activate_nfca(200, &cuid)) break; if(!furi_hal_nfc_activate_nfca(200, &cuid)) break;

View File

@@ -0,0 +1,17 @@
#include "mifare_common.h"
MifareType mifare_common_get_type(uint8_t ATQA0, uint8_t ATQA1, uint8_t SAK) {
MifareType type = MifareTypeUnknown;
if((ATQA0 == 0x44) && (ATQA1 == 0x00) && (SAK == 0x00)) {
type = MifareTypeUltralight;
} else if(
((ATQA0 == 0x44 || ATQA0 == 0x04) && (SAK == 0x08)) ||
((ATQA0 == 0x42 || ATQA0 == 0x02) && (SAK == 0x18))) {
type = MifareTypeClassic;
} else if(ATQA0 == 0x44 && ATQA1 == 0x03 && SAK == 0x20) {
type = MifareTypeDesfire;
}
return type;
}

View File

@@ -0,0 +1,12 @@
#pragma once
#include <stdint.h>
typedef enum {
MifareTypeUnknown,
MifareTypeUltralight,
MifareTypeClassic,
MifareTypeDesfire,
} MifareType;
MifareType mifare_common_get_type(uint8_t ATQA0, uint8_t ATQA1, uint8_t SAK);

View File

@@ -1,6 +1,7 @@
#include "mifare_ultralight.h" #include "mifare_ultralight.h"
#include <furi.h> #include <furi.h>
#include <furi_hal_nfc.h>
#define TAG "MfUltralight"
bool mf_ul_check_card_type(uint8_t ATQA0, uint8_t ATQA1, uint8_t SAK) { bool mf_ul_check_card_type(uint8_t ATQA0, uint8_t ATQA1, uint8_t SAK) {
if((ATQA0 == 0x44) && (ATQA1 == 0x00) && (SAK == 0x00)) { if((ATQA0 == 0x44) && (ATQA1 == 0x00) && (SAK == 0x00)) {
@@ -9,187 +10,204 @@ bool mf_ul_check_card_type(uint8_t ATQA0, uint8_t ATQA1, uint8_t SAK) {
return false; return false;
} }
uint16_t mf_ul_prepare_get_version(uint8_t* dest) { static void mf_ul_set_default_version(MfUltralightReader* reader, MfUltralightData* data) {
dest[0] = MF_UL_GET_VERSION_CMD; data->type = MfUltralightTypeUnknown;
return 1; reader->pages_to_read = 16;
reader->support_fast_read = false;
} }
void mf_ul_parse_get_version_response(uint8_t* buff, MifareUlDevice* mf_ul_read) { bool mf_ultralight_read_version(
MfUltralightVersion* version = (MfUltralightVersion*)buff; FuriHalNfcTxRxContext* tx_rx,
memcpy(&mf_ul_read->data.version, version, sizeof(MfUltralightVersion)); MfUltralightReader* reader,
if(version->storage_size == 0x0B || version->storage_size == 0x00) { MfUltralightData* data) {
mf_ul_read->data.type = MfUltralightTypeUL11; bool version_read = false;
mf_ul_read->pages_to_read = 20;
mf_ul_read->support_fast_read = true; do {
} else if(version->storage_size == 0x0E) { FURI_LOG_D(TAG, "Reading version");
mf_ul_read->data.type = MfUltralightTypeUL21; tx_rx->tx_data[0] = MF_UL_GET_VERSION_CMD;
mf_ul_read->pages_to_read = 41; tx_rx->tx_bits = 8;
mf_ul_read->support_fast_read = true; tx_rx->tx_rx_type = FuriHalNfcTxRxTypeDefault;
} else if(version->storage_size == 0x0F) { if(!furi_hal_nfc_tx_rx(tx_rx, 4)) {
mf_ul_read->data.type = MfUltralightTypeNTAG213; FURI_LOG_D(TAG, "Failed reading version");
mf_ul_read->pages_to_read = 45; mf_ul_set_default_version(reader, data);
mf_ul_read->support_fast_read = false; furi_hal_nfc_sleep();
} else if(version->storage_size == 0x11) { furi_hal_nfc_activate_nfca(300, NULL);
mf_ul_read->data.type = MfUltralightTypeNTAG215; break;
mf_ul_read->pages_to_read = 135; }
mf_ul_read->support_fast_read = false; MfUltralightVersion* version = (MfUltralightVersion*)tx_rx->rx_data;
} else if(version->storage_size == 0x13) { data->version = *version;
mf_ul_read->data.type = MfUltralightTypeNTAG216; if(version->storage_size == 0x0B || version->storage_size == 0x00) {
mf_ul_read->pages_to_read = 231; data->type = MfUltralightTypeUL11;
mf_ul_read->support_fast_read = false; reader->pages_to_read = 20;
reader->support_fast_read = true;
} else if(version->storage_size == 0x0E) {
data->type = MfUltralightTypeUL21;
reader->pages_to_read = 41;
reader->support_fast_read = true;
} else if(version->storage_size == 0x0F) {
data->type = MfUltralightTypeNTAG213;
reader->pages_to_read = 45;
reader->support_fast_read = false;
} else if(version->storage_size == 0x11) {
data->type = MfUltralightTypeNTAG215;
reader->pages_to_read = 135;
reader->support_fast_read = false;
} else if(version->storage_size == 0x13) {
data->type = MfUltralightTypeNTAG216;
reader->pages_to_read = 231;
reader->support_fast_read = false;
} else {
mf_ul_set_default_version(reader, data);
break;
}
version_read = true;
} while(false);
return version_read;
}
bool mf_ultralight_read_pages(
FuriHalNfcTxRxContext* tx_rx,
MfUltralightReader* reader,
MfUltralightData* data) {
uint8_t pages_read_cnt = 0;
for(size_t i = 0; i < reader->pages_to_read; i += 4) {
FURI_LOG_D(TAG, "Reading pages %d - %d", i, i + 3);
tx_rx->tx_data[0] = MF_UL_READ_CMD;
tx_rx->tx_data[1] = i;
tx_rx->tx_bits = 16;
tx_rx->tx_rx_type = FuriHalNfcTxRxTypeDefault;
if(!furi_hal_nfc_tx_rx(tx_rx, 4)) {
FURI_LOG_D(TAG, "Failed to read pages %d - %d", i, i + 3);
break;
}
if(i + 4 <= reader->pages_to_read) {
pages_read_cnt = 4;
} else {
pages_read_cnt = reader->pages_to_read - reader->pages_read;
}
reader->pages_read += pages_read_cnt;
data->data_size = reader->pages_read * 4;
memcpy(&data->data[i * 4], tx_rx->rx_data, pages_read_cnt * 4);
}
return reader->pages_read == reader->pages_to_read;
}
bool mf_ultralight_fast_read_pages(
FuriHalNfcTxRxContext* tx_rx,
MfUltralightReader* reader,
MfUltralightData* data) {
FURI_LOG_D(TAG, "Reading pages 0 - %d", reader->pages_to_read);
tx_rx->tx_data[0] = MF_UL_FAST_READ_CMD;
tx_rx->tx_data[1] = 0;
tx_rx->tx_data[2] = reader->pages_to_read - 1;
tx_rx->tx_bits = 24;
tx_rx->tx_rx_type = FuriHalNfcTxRxTypeDefault;
if(furi_hal_nfc_tx_rx(tx_rx, 20)) {
reader->pages_read = reader->pages_to_read;
data->data_size = reader->pages_read * 4;
memcpy(data->data, tx_rx->rx_data, data->data_size);
} else { } else {
mf_ul_set_default_version(mf_ul_read); FURI_LOG_D(TAG, "Failed to read pages 0 - %d", reader->pages_to_read);
}
}
void mf_ul_set_default_version(MifareUlDevice* mf_ul_read) {
mf_ul_read->data.type = MfUltralightTypeUnknown;
mf_ul_read->pages_to_read = 16;
mf_ul_read->support_fast_read = false;
}
uint16_t mf_ul_prepare_read(uint8_t* dest, uint8_t start_page) {
dest[0] = MF_UL_READ_CMD;
dest[1] = start_page;
return 2;
}
void mf_ul_parse_read_response(uint8_t* buff, uint16_t page_addr, MifareUlDevice* mf_ul_read) {
uint8_t pages_read = 4;
uint8_t page_read_count = mf_ul_read->pages_read + pages_read;
if(page_read_count > mf_ul_read->pages_to_read) {
pages_read -= page_read_count - mf_ul_read->pages_to_read;
}
mf_ul_read->pages_read += pages_read;
mf_ul_read->data.data_size = mf_ul_read->pages_read * 4;
memcpy(&mf_ul_read->data.data[page_addr * 4], buff, pages_read * 4);
}
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_read = end_page - start_page + 1;
mf_ul_read->data.data_size = mf_ul_read->pages_read * 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_READ_SIG;
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;
}
void mf_ul_prepare_emulation(MifareUlDevice* mf_ul_emulate, MifareUlData* data) {
mf_ul_emulate->data = *data;
mf_ul_emulate->auth_data = NULL;
mf_ul_emulate->data_changed = false;
mf_ul_emulate->comp_write_cmd_started = false;
if(data->version.storage_size == 0) {
mf_ul_emulate->data.type = MfUltralightTypeUnknown;
mf_ul_emulate->support_fast_read = false;
} else if(data->version.storage_size == 0x0B) {
mf_ul_emulate->data.type = MfUltralightTypeUL11;
mf_ul_emulate->support_fast_read = true;
} else if(data->version.storage_size == 0x0E) {
mf_ul_emulate->data.type = MfUltralightTypeUL21;
mf_ul_emulate->support_fast_read = true;
} else if(data->version.storage_size == 0x0F) {
mf_ul_emulate->data.type = MfUltralightTypeNTAG213;
mf_ul_emulate->support_fast_read = true;
} else if(data->version.storage_size == 0x11) {
mf_ul_emulate->data.type = MfUltralightTypeNTAG215;
mf_ul_emulate->support_fast_read = true;
} else if(data->version.storage_size == 0x13) {
mf_ul_emulate->data.type = MfUltralightTypeNTAG216;
mf_ul_emulate->support_fast_read = true;
} }
if(mf_ul_emulate->data.type >= MfUltralightTypeNTAG213) { return reader->pages_read == reader->pages_to_read;
uint16_t pwd_page = (data->data_size / 4) - 2;
mf_ul_emulate->auth_data = (MifareUlAuthData*)&data->data[pwd_page * 4];
}
} }
void mf_ul_protect_auth_data_on_read_command( bool mf_ultralight_read_signature(FuriHalNfcTxRxContext* tx_rx, MfUltralightData* data) {
bool signature_read = false;
FURI_LOG_D(TAG, "Reading signature");
tx_rx->tx_data[0] = MF_UL_READ_SIG;
tx_rx->tx_data[1] = 0;
tx_rx->tx_bits = 16;
tx_rx->tx_rx_type = FuriHalNfcTxRxTypeDefault;
if(furi_hal_nfc_tx_rx(tx_rx, 7)) {
memcpy(data->signature, tx_rx->rx_data, sizeof(data->signature));
signature_read = true;
} else {
FURI_LOG_D(TAG, "Failed redaing signature");
}
return signature_read;
}
bool mf_ultralight_read_counters(FuriHalNfcTxRxContext* tx_rx, MfUltralightData* data) {
uint8_t counter_read = 0;
FURI_LOG_D(TAG, "Reading counters");
for(size_t i = 0; i < 3; i++) {
tx_rx->tx_data[0] = MF_UL_READ_CNT;
tx_rx->rx_data[1] = i;
tx_rx->tx_bits = 16;
tx_rx->tx_rx_type = FuriHalNfcTxRxTypeDefault;
if(!furi_hal_nfc_tx_rx(tx_rx, 4)) {
FURI_LOG_D(TAG, "Failed to read %d counter", i);
break;
}
data->counter[i] = (tx_rx->rx_data[2] << 16) | (tx_rx->rx_data[1] << 8) |
tx_rx->rx_data[0];
counter_read++;
}
return counter_read == 2;
}
bool mf_ultralight_read_tearing_flags(FuriHalNfcTxRxContext* tx_rx, MfUltralightData* data) {
uint8_t flag_read = 0;
FURI_LOG_D(TAG, "Reading tearing flags");
for(size_t i = 0; i < 3; i++) {
tx_rx->tx_data[0] = MF_UL_CHECK_TEARING;
tx_rx->rx_data[1] = i;
tx_rx->tx_bits = 16;
tx_rx->tx_rx_type = FuriHalNfcTxRxTypeDefault;
if(!furi_hal_nfc_tx_rx(tx_rx, 4)) {
FURI_LOG_D(TAG, "Failed to read %d tearing flag", i);
break;
}
data->tearing[i] = tx_rx->rx_data[0];
flag_read++;
}
return flag_read == 2;
}
bool mf_ul_read_card(
FuriHalNfcTxRxContext* tx_rx,
MfUltralightReader* reader,
MfUltralightData* data) {
furi_assert(tx_rx);
furi_assert(reader);
furi_assert(data);
bool card_read = false;
// Read Mifare Ultralight version
if(mf_ultralight_read_version(tx_rx, reader, data)) {
// Read Signature
mf_ultralight_read_signature(tx_rx, data);
}
// Read data blocks
if(reader->support_fast_read) {
mf_ultralight_read_counters(tx_rx, data);
mf_ultralight_read_tearing_flags(tx_rx, data);
}
card_read = mf_ultralight_read_pages(tx_rx, reader, data);
return card_read;
}
// TODO rework
static void mf_ul_protect_auth_data_on_read_command(
uint8_t* tx_buff, uint8_t* tx_buff,
uint8_t start_page, uint8_t start_page,
uint8_t end_page, uint8_t end_page,
MifareUlDevice* mf_ul_emulate) { MfUltralightEmulator* emulator) {
if(mf_ul_emulate->data.type >= MfUltralightTypeNTAG213) { if(emulator->data.type >= MfUltralightTypeNTAG213) {
uint8_t pwd_page = (mf_ul_emulate->data.data_size / 4) - 2; uint8_t pwd_page = (emulator->data.data_size / 4) - 2;
uint8_t pack_page = pwd_page + 1; uint8_t pack_page = pwd_page + 1;
if((start_page <= pwd_page) && (end_page >= pwd_page)) { if((start_page <= pwd_page) && (end_page >= pwd_page)) {
memset(&tx_buff[(pwd_page - start_page) * 4], 0, 4); memset(&tx_buff[(pwd_page - start_page) * 4], 0, 4);
@@ -200,6 +218,31 @@ void mf_ul_protect_auth_data_on_read_command(
} }
} }
void mf_ul_prepare_emulation(MfUltralightEmulator* emulator, MfUltralightData* data) {
emulator->data = *data;
emulator->auth_data = NULL;
emulator->data_changed = false;
emulator->comp_write_cmd_started = false;
if(data->type == MfUltralightTypeUnknown) {
emulator->support_fast_read = false;
} else if(data->type == MfUltralightTypeUL11) {
emulator->support_fast_read = true;
} else if(data->type == MfUltralightTypeUL21) {
emulator->support_fast_read = true;
} else if(data->type == MfUltralightTypeNTAG213) {
emulator->support_fast_read = false;
} else if(data->type == MfUltralightTypeNTAG215) {
emulator->support_fast_read = false;
} else if(data->type == MfUltralightTypeNTAG216) {
emulator->support_fast_read = false;
}
if(data->type >= MfUltralightTypeNTAG213) {
uint16_t pwd_page = (data->data_size / 4) - 2;
emulator->auth_data = (MfUltralightAuth*)&data->data[pwd_page * 4];
}
}
bool mf_ul_prepare_emulation_response( bool mf_ul_prepare_emulation_response(
uint8_t* buff_rx, uint8_t* buff_rx,
uint16_t buff_rx_len, uint16_t buff_rx_len,
@@ -208,30 +251,30 @@ bool mf_ul_prepare_emulation_response(
uint32_t* data_type, uint32_t* data_type,
void* context) { void* context) {
furi_assert(context); furi_assert(context);
MifareUlDevice* mf_ul_emulate = context; MfUltralightEmulator* emulator = context;
uint8_t cmd = buff_rx[0]; uint8_t cmd = buff_rx[0];
uint16_t page_num = mf_ul_emulate->data.data_size / 4; uint16_t page_num = emulator->data.data_size / 4;
uint16_t tx_bytes = 0; uint16_t tx_bytes = 0;
uint16_t tx_bits = 0; uint16_t tx_bits = 0;
bool command_parsed = false; bool command_parsed = false;
// Check composite commands // Check composite commands
if(mf_ul_emulate->comp_write_cmd_started) { if(emulator->comp_write_cmd_started) {
// Compatibility write is the only one composit command // Compatibility write is the only one composit command
if(buff_rx_len == 16) { if(buff_rx_len == 16) {
memcpy(&mf_ul_emulate->data.data[mf_ul_emulate->comp_write_page_addr * 4], buff_rx, 4); memcpy(&emulator->data.data[emulator->comp_write_page_addr * 4], buff_rx, 4);
mf_ul_emulate->data_changed = true; emulator->data_changed = true;
// Send ACK message // Send ACK message
buff_tx[0] = 0x0A; buff_tx[0] = 0x0A;
tx_bits = 4; tx_bits = 4;
*data_type = FURI_HAL_NFC_TXRX_RAW; *data_type = FURI_HAL_NFC_TXRX_RAW;
command_parsed = true; command_parsed = true;
} }
mf_ul_emulate->comp_write_cmd_started = false; emulator->comp_write_cmd_started = false;
} else if(cmd == MF_UL_GET_VERSION_CMD) { } else if(cmd == MF_UL_GET_VERSION_CMD) {
if(mf_ul_emulate->data.type != MfUltralightTypeUnknown) { if(emulator->data.type != MfUltralightTypeUnknown) {
tx_bytes = sizeof(mf_ul_emulate->data.version); tx_bytes = sizeof(emulator->data.version);
memcpy(buff_tx, &mf_ul_emulate->data.version, tx_bytes); memcpy(buff_tx, &emulator->data.version, tx_bytes);
*data_type = FURI_HAL_NFC_TXRX_DEFAULT; *data_type = FURI_HAL_NFC_TXRX_DEFAULT;
command_parsed = true; command_parsed = true;
} }
@@ -242,28 +285,24 @@ bool mf_ul_prepare_emulation_response(
if(start_page + 4 > page_num) { if(start_page + 4 > page_num) {
// Handle roll-over mechanism // Handle roll-over mechanism
uint8_t end_pages_num = page_num - start_page; uint8_t end_pages_num = page_num - start_page;
memcpy(buff_tx, &mf_ul_emulate->data.data[start_page * 4], end_pages_num * 4); memcpy(buff_tx, &emulator->data.data[start_page * 4], end_pages_num * 4);
memcpy( memcpy(&buff_tx[end_pages_num * 4], emulator->data.data, (4 - end_pages_num) * 4);
&buff_tx[end_pages_num * 4],
mf_ul_emulate->data.data,
(4 - end_pages_num) * 4);
} else { } else {
memcpy(buff_tx, &mf_ul_emulate->data.data[start_page * 4], tx_bytes); memcpy(buff_tx, &emulator->data.data[start_page * 4], tx_bytes);
} }
mf_ul_protect_auth_data_on_read_command( mf_ul_protect_auth_data_on_read_command(
buff_tx, start_page, (start_page + 4), mf_ul_emulate); buff_tx, start_page, (start_page + 4), emulator);
*data_type = FURI_HAL_NFC_TXRX_DEFAULT; *data_type = FURI_HAL_NFC_TXRX_DEFAULT;
command_parsed = true; command_parsed = true;
} }
} else if(cmd == MF_UL_FAST_READ_CMD) { } else if(cmd == MF_UL_FAST_READ_CMD) {
if(mf_ul_emulate->support_fast_read) { if(emulator->support_fast_read) {
uint8_t start_page = buff_rx[1]; uint8_t start_page = buff_rx[1];
uint8_t end_page = buff_rx[2]; uint8_t end_page = buff_rx[2];
if((start_page < page_num) && (end_page < page_num) && (start_page < (end_page + 1))) { if((start_page < page_num) && (end_page < page_num) && (start_page < (end_page + 1))) {
tx_bytes = ((end_page + 1) - start_page) * 4; tx_bytes = ((end_page + 1) - start_page) * 4;
memcpy(buff_tx, &mf_ul_emulate->data.data[start_page * 4], tx_bytes); memcpy(buff_tx, &emulator->data.data[start_page * 4], tx_bytes);
mf_ul_protect_auth_data_on_read_command( mf_ul_protect_auth_data_on_read_command(buff_tx, start_page, end_page, emulator);
buff_tx, start_page, end_page, mf_ul_emulate);
*data_type = FURI_HAL_NFC_TXRX_DEFAULT; *data_type = FURI_HAL_NFC_TXRX_DEFAULT;
command_parsed = true; command_parsed = true;
} }
@@ -271,8 +310,8 @@ bool mf_ul_prepare_emulation_response(
} else if(cmd == MF_UL_WRITE) { } else if(cmd == MF_UL_WRITE) {
uint8_t write_page = buff_rx[1]; uint8_t write_page = buff_rx[1];
if((write_page > 1) && (write_page < page_num - 2)) { if((write_page > 1) && (write_page < page_num - 2)) {
memcpy(&mf_ul_emulate->data.data[write_page * 4], &buff_rx[2], 4); memcpy(&emulator->data.data[write_page * 4], &buff_rx[2], 4);
mf_ul_emulate->data_changed = true; emulator->data_changed = true;
// ACK // ACK
buff_tx[0] = 0x0A; buff_tx[0] = 0x0A;
tx_bits = 4; tx_bits = 4;
@@ -282,8 +321,8 @@ bool mf_ul_prepare_emulation_response(
} else if(cmd == MF_UL_COMP_WRITE) { } else if(cmd == MF_UL_COMP_WRITE) {
uint8_t write_page = buff_rx[1]; uint8_t write_page = buff_rx[1];
if((write_page > 1) && (write_page < page_num - 2)) { if((write_page > 1) && (write_page < page_num - 2)) {
mf_ul_emulate->comp_write_cmd_started = true; emulator->comp_write_cmd_started = true;
mf_ul_emulate->comp_write_page_addr = write_page; emulator->comp_write_page_addr = write_page;
// ACK // ACK
buff_tx[0] = 0x0A; buff_tx[0] = 0x0A;
tx_bits = 4; tx_bits = 4;
@@ -293,9 +332,9 @@ bool mf_ul_prepare_emulation_response(
} else if(cmd == MF_UL_READ_CNT) { } else if(cmd == MF_UL_READ_CNT) {
uint8_t cnt_num = buff_rx[1]; uint8_t cnt_num = buff_rx[1];
if(cnt_num < 3) { if(cnt_num < 3) {
buff_tx[0] = mf_ul_emulate->data.counter[cnt_num] >> 16; buff_tx[0] = emulator->data.counter[cnt_num] >> 16;
buff_tx[1] = mf_ul_emulate->data.counter[cnt_num] >> 8; buff_tx[1] = emulator->data.counter[cnt_num] >> 8;
buff_tx[2] = mf_ul_emulate->data.counter[cnt_num]; buff_tx[2] = emulator->data.counter[cnt_num];
tx_bytes = 3; tx_bytes = 3;
*data_type = FURI_HAL_NFC_TXRX_DEFAULT; *data_type = FURI_HAL_NFC_TXRX_DEFAULT;
command_parsed = true; command_parsed = true;
@@ -303,9 +342,9 @@ bool mf_ul_prepare_emulation_response(
} else if(cmd == MF_UL_INC_CNT) { } else if(cmd == MF_UL_INC_CNT) {
uint8_t cnt_num = buff_rx[1]; uint8_t cnt_num = buff_rx[1];
uint32_t inc = (buff_rx[2] | (buff_rx[3] << 8) | (buff_rx[4] << 16)); uint32_t inc = (buff_rx[2] | (buff_rx[3] << 8) | (buff_rx[4] << 16));
if((cnt_num < 3) && (mf_ul_emulate->data.counter[cnt_num] + inc < 0x00FFFFFF)) { if((cnt_num < 3) && (emulator->data.counter[cnt_num] + inc < 0x00FFFFFF)) {
mf_ul_emulate->data.counter[cnt_num] += inc; emulator->data.counter[cnt_num] += inc;
mf_ul_emulate->data_changed = true; emulator->data_changed = true;
// ACK // ACK
buff_tx[0] = 0x0A; buff_tx[0] = 0x0A;
tx_bits = 4; tx_bits = 4;
@@ -313,14 +352,14 @@ bool mf_ul_prepare_emulation_response(
command_parsed = true; command_parsed = true;
} }
} else if(cmd == MF_UL_AUTH) { } else if(cmd == MF_UL_AUTH) {
if(mf_ul_emulate->data.type >= MfUltralightTypeNTAG213) { if(emulator->data.type >= MfUltralightTypeNTAG213) {
if(memcmp(&buff_rx[1], mf_ul_emulate->auth_data->pwd, 4) == 0) { if(memcmp(&buff_rx[1], emulator->auth_data->pwd, 4) == 0) {
buff_tx[0] = mf_ul_emulate->auth_data->pack.raw[0]; buff_tx[0] = emulator->auth_data->pack.raw[0];
buff_tx[1] = mf_ul_emulate->auth_data->pack.raw[1]; buff_tx[1] = emulator->auth_data->pack.raw[1];
tx_bytes = 2; tx_bytes = 2;
*data_type = FURI_HAL_NFC_TXRX_DEFAULT; *data_type = FURI_HAL_NFC_TXRX_DEFAULT;
command_parsed = true; command_parsed = true;
} else if(!mf_ul_emulate->auth_data->pack.value) { } else if(!emulator->auth_data->pack.value) {
buff_tx[0] = 0x80; buff_tx[0] = 0x80;
buff_tx[1] = 0x80; buff_tx[1] = 0x80;
tx_bytes = 2; tx_bytes = 2;
@@ -331,15 +370,15 @@ bool mf_ul_prepare_emulation_response(
} else if(cmd == MF_UL_READ_SIG) { } else if(cmd == MF_UL_READ_SIG) {
// Check 2nd byte = 0x00 - RFU // Check 2nd byte = 0x00 - RFU
if(buff_rx[1] == 0x00) { if(buff_rx[1] == 0x00) {
tx_bytes = sizeof(mf_ul_emulate->data.signature); tx_bytes = sizeof(emulator->data.signature);
memcpy(buff_tx, mf_ul_emulate->data.signature, tx_bytes); memcpy(buff_tx, emulator->data.signature, tx_bytes);
*data_type = FURI_HAL_NFC_TXRX_DEFAULT; *data_type = FURI_HAL_NFC_TXRX_DEFAULT;
command_parsed = true; command_parsed = true;
} }
} else if(cmd == MF_UL_CHECK_TEARING) { } else if(cmd == MF_UL_CHECK_TEARING) {
uint8_t cnt_num = buff_rx[1]; uint8_t cnt_num = buff_rx[1];
if(cnt_num < 3) { if(cnt_num < 3) {
buff_tx[0] = mf_ul_emulate->data.tearing[cnt_num]; buff_tx[0] = emulator->data.tearing[cnt_num];
tx_bytes = 1; tx_bytes = 1;
*data_type = FURI_HAL_NFC_TXRX_DEFAULT; *data_type = FURI_HAL_NFC_TXRX_DEFAULT;
command_parsed = true; command_parsed = true;

View File

@@ -1,8 +1,6 @@
#pragma once #pragma once
#include <stdint.h> #include <furi_hal_nfc.h>
#include <stdbool.h>
#include <string.h>
#define MF_UL_MAX_DUMP_SIZE 1024 #define MF_UL_MAX_DUMP_SIZE 1024
@@ -62,7 +60,7 @@ typedef struct {
uint8_t tearing[3]; uint8_t tearing[3];
uint16_t data_size; uint16_t data_size;
uint8_t data[MF_UL_MAX_DUMP_SIZE]; uint8_t data[MF_UL_MAX_DUMP_SIZE];
} MifareUlData; } MfUltralightData;
typedef struct { typedef struct {
uint8_t pwd[4]; uint8_t pwd[4];
@@ -70,52 +68,53 @@ typedef struct {
uint8_t raw[2]; uint8_t raw[2];
uint16_t value; uint16_t value;
} pack; } pack;
} MifareUlAuthData; } MfUltralightAuth;
typedef struct { typedef struct {
uint8_t pages_to_read; uint8_t pages_to_read;
uint8_t pages_read; uint8_t pages_read;
bool support_fast_read; bool support_fast_read;
} MfUltralightReader;
typedef struct {
MfUltralightData data;
bool support_fast_read;
bool data_changed; bool data_changed;
MifareUlData data;
MifareUlAuthData* auth_data;
bool comp_write_cmd_started; bool comp_write_cmd_started;
uint8_t comp_write_page_addr; uint8_t comp_write_page_addr;
} MifareUlDevice; MfUltralightAuth* auth_data;
} MfUltralightEmulator;
bool mf_ul_check_card_type(uint8_t ATQA0, uint8_t ATQA1, uint8_t SAK); 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); bool mf_ultralight_read_version(
void mf_ul_parse_get_version_response(uint8_t* buff, MifareUlDevice* mf_ul_read); FuriHalNfcTxRxContext* tx_rx,
void mf_ul_set_default_version(MifareUlDevice* mf_ul_read); MfUltralightReader* reader,
MfUltralightData* data);
uint16_t mf_ul_prepare_read_signature(uint8_t* dest); bool mf_ultralight_read_pages(
void mf_ul_parse_read_signature_response(uint8_t* buff, MifareUlDevice* mf_ul_read); FuriHalNfcTxRxContext* tx_rx,
MfUltralightReader* reader,
MfUltralightData* data);
uint16_t mf_ul_prepare_read_cnt(uint8_t* dest, uint8_t cnt_index); bool mf_ultralight_fast_read_pages(
void mf_ul_parse_read_cnt_response(uint8_t* buff, uint8_t cnt_index, MifareUlDevice* mf_ul_read); FuriHalNfcTxRxContext* tx_rx,
MfUltralightReader* reader,
MfUltralightData* data);
uint16_t mf_ul_prepare_inc_cnt(uint8_t* dest, uint8_t cnt_index, uint32_t value); bool mf_ultralight_read_signature(FuriHalNfcTxRxContext* tx_rx, MfUltralightData* data);
uint16_t mf_ul_prepare_check_tearing(uint8_t* dest, uint8_t cnt_index); bool mf_ultralight_read_counters(FuriHalNfcTxRxContext* tx_rx, MfUltralightData* data);
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); bool mf_ultralight_read_tearing_flags(FuriHalNfcTxRxContext* tx_rx, MfUltralightData* data);
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); bool mf_ul_read_card(
void mf_ul_parse_fast_read_response( FuriHalNfcTxRxContext* tx_rx,
uint8_t* buff, MfUltralightReader* reader,
uint8_t start_page, MfUltralightData* data);
uint8_t end_page,
MifareUlDevice* mf_ul_read);
uint16_t mf_ul_prepare_write(uint8_t* dest, uint16_t page_addr, uint32_t data); void mf_ul_prepare_emulation(MfUltralightEmulator* emulator, MfUltralightData* data);
void mf_ul_prepare_emulation(MifareUlDevice* mf_ul_emulate, MifareUlData* data);
bool mf_ul_prepare_emulation_response( bool mf_ul_prepare_emulation_response(
uint8_t* buff_rx, uint8_t* buff_rx,
uint16_t buff_rx_len, uint16_t buff_rx_len,

View File

@@ -13,7 +13,7 @@
#define MANIFEST_KEY_RADIO_ADDRESS "Radio address" #define MANIFEST_KEY_RADIO_ADDRESS "Radio address"
#define MANIFEST_KEY_RADIO_VERSION "Radio version" #define MANIFEST_KEY_RADIO_VERSION "Radio version"
#define MANIFEST_KEY_RADIO_CRC "Radio CRC" #define MANIFEST_KEY_RADIO_CRC "Radio CRC"
#define MANIFEST_KEY_ASSETS_FILE "Assets" #define MANIFEST_KEY_ASSETS_FILE "Resources"
UpdateManifest* update_manifest_alloc() { UpdateManifest* update_manifest_alloc() {
UpdateManifest* update_manifest = malloc(sizeof(UpdateManifest)); UpdateManifest* update_manifest = malloc(sizeof(UpdateManifest));

View File

@@ -86,7 +86,6 @@ $(OBJ_DIR)/upload: $(OBJ_DIR)/$(PROJECT).bin
dfu-util -d 0483:df11 -D $(OBJ_DIR)/$(PROJECT).bin -a 0 -s $(FLASH_ADDRESS) $(DFU_OPTIONS) dfu-util -d 0483:df11 -D $(OBJ_DIR)/$(PROJECT).bin -a 0 -s $(FLASH_ADDRESS) $(DFU_OPTIONS)
touch $@ touch $@
.PHONY: flash .PHONY: flash
flash: $(OBJ_DIR)/flash flash: $(OBJ_DIR)/flash

View File

@@ -203,15 +203,12 @@ class Main(App):
manifest_file = os.path.join(directory_path, "Manifest") manifest_file = os.path.join(directory_path, "Manifest")
old_manifest = Manifest() old_manifest = Manifest()
if os.path.exists(manifest_file): if os.path.exists(manifest_file):
self.logger.info( self.logger.info("old manifest is present, loading for compare")
f"old manifest is present, loading for compare and removing file"
)
old_manifest.load(manifest_file) old_manifest.load(manifest_file)
os.unlink(manifest_file)
self.logger.info(f'Creating new Manifest for directory "{directory_path}"') self.logger.info(f'Creating new Manifest for directory "{directory_path}"')
new_manifest = Manifest() new_manifest = Manifest()
new_manifest.create(directory_path) new_manifest.create(directory_path)
new_manifest.save(manifest_file)
self.logger.info(f"Comparing new manifest with old") self.logger.info(f"Comparing new manifest with old")
only_in_old, changed, only_in_new = Manifest.compare(old_manifest, new_manifest) only_in_old, changed, only_in_new = Manifest.compare(old_manifest, new_manifest)
for record in only_in_old: for record in only_in_old:
@@ -220,6 +217,12 @@ class Main(App):
self.logger.info(f"Changed: {record}") self.logger.info(f"Changed: {record}")
for record in only_in_new: for record in only_in_new:
self.logger.info(f"Only in new: {record}") self.logger.info(f"Only in new: {record}")
if any((only_in_old, changed, only_in_new)):
self.logger.warning("Manifests are different, updating")
new_manifest.save(manifest_file)
else:
self.logger.info("Manifest is up-to-date!")
self.logger.info(f"Complete") self.logger.info(f"Complete")
return 0 return 0

View File

@@ -18,7 +18,7 @@ class Main(App):
self.parser_copy.add_argument("-t", dest="target", required=True) self.parser_copy.add_argument("-t", dest="target", required=True)
self.parser_copy.add_argument("-p", dest="projects", nargs="+", required=True) self.parser_copy.add_argument("-p", dest="projects", nargs="+", required=True)
self.parser_copy.add_argument("-s", dest="suffix", required=True) self.parser_copy.add_argument("-s", dest="suffix", required=True)
self.parser_copy.add_argument("-a", dest="assets", required=False) self.parser_copy.add_argument("-r", dest="resources", required=False)
self.parser_copy.add_argument( self.parser_copy.add_argument(
"--bundlever", "--bundlever",
dest="version", dest="version",
@@ -79,16 +79,16 @@ class Main(App):
self.args.version, self.args.version,
"-t", "-t",
self.args.target, self.args.target,
"-dfu", "--dfu",
self.get_dist_filepath(self.get_project_filename("firmware", "dfu")), self.get_dist_filepath(self.get_project_filename("firmware", "dfu")),
"-stage", "--stage",
self.get_dist_filepath(self.get_project_filename("updater", "bin")), self.get_dist_filepath(self.get_project_filename("updater", "bin")),
] ]
if self.args.assets: if self.args.resources:
bundle_args.extend( bundle_args.extend(
( (
"-a", "-r",
self.args.assets, self.args.resources,
) )
) )
self.logger.info( self.logger.info(

View File

@@ -8,7 +8,7 @@ import os
from flipper.app import App from flipper.app import App
from flipper.cube import CubeProgrammer from flipper.cube import CubeProgrammer
STATEMENT = "AGREE_TO_LOOSE_FLIPPER_FEATURES_THAT_USES_CRYPTO_ENCLAVE" STATEMENT = "AGREE_TO_LOSE_FLIPPER_FEATURES_THAT_USE_CRYPTO_ENCLAVE"
class Main(App): class Main(App):

View File

@@ -18,10 +18,10 @@ class ManifestRecord:
def toLine(self): def toLine(self):
raise NotImplementedError raise NotImplementedError
def _unpack(self, manifest, key, type): def _unpack(self, manifest, key, nodetype):
key, value = manifest.readline().split(":", 1) key, value = manifest.readline().split(":", 1)
assert key == key assert key == key
return type(value) return nodetype(value)
MANIFEST_TAGS_RECORDS = {} MANIFEST_TAGS_RECORDS = {}
@@ -94,7 +94,7 @@ class ManifestRecordFile(ManifestRecord):
@staticmethod @staticmethod
def fromLine(line): def fromLine(line):
data = line.split(":", 3) data = line.split(":", 3)
return ManifestRecordFile(data[2], data[0], data[1]) return ManifestRecordFile(data[2], data[0], int(data[1]))
def toLine(self): def toLine(self):
return f"{self.tag}:{self.md5}:{self.size}:{self.path}\n" return f"{self.tag}:{self.md5}:{self.size}:{self.path}\n"
@@ -133,7 +133,7 @@ class Manifest:
def addFile(self, path, md5, size): def addFile(self, path, md5, size):
self.records.append(ManifestRecordFile(path, md5, size)) self.records.append(ManifestRecordFile(path, md5, size))
def create(self, directory_path): def create(self, directory_path, ignore_files=["Manifest"]):
for root, dirs, files in os.walk(directory_path): for root, dirs, files in os.walk(directory_path):
relative_root = root.replace(directory_path, "", 1) relative_root = root.replace(directory_path, "", 1)
if relative_root.startswith("/"): if relative_root.startswith("/"):
@@ -141,13 +141,16 @@ class Manifest:
# process directories # process directories
for dir in dirs: for dir in dirs:
relative_dir_path = os.path.join(relative_root, dir) relative_dir_path = os.path.join(relative_root, dir)
self.logger.info(f'Adding directory: "{relative_dir_path}"') self.logger.debug(f'Adding directory: "{relative_dir_path}"')
self.addDirectory(relative_dir_path) self.addDirectory(relative_dir_path)
# Process files # Process files
for file in files: for file in files:
relative_file_path = os.path.join(relative_root, file) relative_file_path = os.path.join(relative_root, file)
if file in ignore_files:
self.logger.info(f'Skipping file "{relative_file_path}"')
continue
full_file_path = os.path.join(root, file) full_file_path = os.path.join(root, file)
self.logger.info(f'Adding file: "{relative_file_path}"') self.logger.debug(f'Adding file: "{relative_file_path}"')
self.addFile( self.addFile(
relative_file_path, relative_file_path,
file_md5(full_file_path), file_md5(full_file_path),
@@ -155,7 +158,7 @@ class Manifest:
) )
def toFsTree(self): def toFsTree(self):
root = FsNode("", FsNode.Type.Directory) root = FsNode("", FsNode.NodeType.Directory)
for record in self.records: for record in self.records:
if isinstance(record, ManifestRecordDirectory): if isinstance(record, ManifestRecordDirectory):
root.addDirectory(record.path) root.addDirectory(record.path)

View File

@@ -3,13 +3,13 @@ from collections import OrderedDict
class FsNode: class FsNode:
class Type(Enum): class NodeType(Enum):
File = 0 File = 0
Directory = 1 Directory = 1
def __init__(self, name: str, type: "FsNode.Type", **kwargs): def __init__(self, name: str, nodetype: "FsNode.Type", **kwargs):
self.name = name self.name = name
self.type = type self.nodetype = nodetype
self.data = kwargs self.data = kwargs
self.parent = None self.parent = None
self.children = OrderedDict() self.children = OrderedDict()
@@ -25,7 +25,7 @@ class FsNode:
parent_node = self.traverse(fragments) parent_node = self.traverse(fragments)
if not parent_node: if not parent_node:
raise Exception(f"No parent node found for: {path}") raise Exception(f"No parent node found for: {path}")
parent_node.addChild(FsNode(name, FsNode.Type.Directory)) parent_node.addChild(FsNode(name, FsNode.NodeType.Directory))
def addFile(self, path, md5, size): def addFile(self, path, md5, size):
fragments = path.split("/") fragments = path.split("/")
@@ -34,7 +34,7 @@ class FsNode:
parent_node = self.traverse(fragments) parent_node = self.traverse(fragments)
if not parent_node: if not parent_node:
raise Exception(f"No parent node found for: {path}") raise Exception(f"No parent node found for: {path}")
parent_node.addChild(FsNode(name, FsNode.Type.File, md5=md5, size=size)) parent_node.addChild(FsNode(name, FsNode.NodeType.File, md5=md5, size=size))
def getChild(self, name): def getChild(self, name):
return self.children[name] return self.children[name]
@@ -58,19 +58,37 @@ class FsNode:
def dump(self): def dump(self):
ret = {} ret = {}
ret["name"] = (self.name,) ret["name"] = (self.name,)
ret["type"] = (self.type,) ret["type"] = (self.nodetype,)
ret["path"] = (self.getPath(),) ret["path"] = (self.getPath(),)
if len(self.children): if len(self.children):
ret["children"] = [node.dump() for node in self.children.values()] ret["children"] = [node.dump() for node in self.children.values()]
return ret return ret
def walk_nodes(node: FsNode):
yield node
for child in node.children.values():
yield from walk_nodes(child)
# Returns filenames: [only_in_left], [changed], [only_in_right]
def compare_fs_trees(left: FsNode, right: FsNode): def compare_fs_trees(left: FsNode, right: FsNode):
# import pprint # import pprint
# pprint.pprint(left.dump()) # pprint.pprint(left.dump())
# pprint.pprint(right.dump()) # pprint.pprint(right.dump())
left_dict = dict((node.getPath(), node) for node in walk_nodes(left))
right_dict = dict((node.getPath(), node) for node in walk_nodes(right))
only_in_left = [] left_names = set(left_dict.keys())
changed = [] right_names = set(right_dict.keys())
only_in_right = [] common_names = left_names.intersection(right_names)
return [], [], []
return (
list(left_names - right_names),
list(
name
for name in common_names
if left_dict[name].data != right_dict[name].data
),
list(right_names - left_names),
)

View File

@@ -10,8 +10,12 @@ import tarfile
class Main(App): class Main(App):
UPDATE_MANIFEST_NAME = "update.fuf"
# No compression, plain tar # No compression, plain tar
ASSET_TAR_MODE = "w:" RESOURCE_TAR_MODE = "w:"
RESOURCE_TAR_FORMAT = tarfile.USTAR_FORMAT
RESOURCE_FILE_NAME = "resources.tar"
def init(self): def init(self):
self.subparsers = self.parser.add_subparsers(help="sub-command help") self.subparsers = self.parser.add_subparsers(help="sub-command help")
@@ -24,17 +28,17 @@ class Main(App):
self.parser_generate.add_argument("-d", dest="directory", required=True) self.parser_generate.add_argument("-d", dest="directory", required=True)
self.parser_generate.add_argument("-v", dest="version", required=True) self.parser_generate.add_argument("-v", dest="version", required=True)
self.parser_generate.add_argument("-t", dest="target", required=True) self.parser_generate.add_argument("-t", dest="target", required=True)
self.parser_generate.add_argument("-dfu", dest="dfu", required=False) self.parser_generate.add_argument("--dfu", dest="dfu", required=False)
self.parser_generate.add_argument("-a", dest="assets", required=False) self.parser_generate.add_argument("-r", dest="resources", required=False)
self.parser_generate.add_argument("-stage", dest="stage", required=True) self.parser_generate.add_argument("--stage", dest="stage", required=True)
self.parser_generate.add_argument( self.parser_generate.add_argument(
"-radio", dest="radiobin", default="", required=False "--radio", dest="radiobin", default="", required=False
) )
self.parser_generate.add_argument( self.parser_generate.add_argument(
"-radioaddr", dest="radioaddr", required=False "--radioaddr", dest="radioaddr", required=False
) )
self.parser_generate.add_argument( self.parser_generate.add_argument(
"-radiover", dest="radioversion", required=False "--radiover", dest="radioversion", required=False
) )
self.parser_generate.set_defaults(func=self.generate) self.parser_generate.set_defaults(func=self.generate)
@@ -43,7 +47,7 @@ class Main(App):
stage_basename = basename(self.args.stage) stage_basename = basename(self.args.stage)
dfu_basename = basename(self.args.dfu) dfu_basename = basename(self.args.dfu)
radiobin_basename = basename(self.args.radiobin) radiobin_basename = basename(self.args.radiobin)
assets_basename = "" resources_basename = ""
if not exists(self.args.directory): if not exists(self.args.directory):
os.makedirs(self.args.directory) os.makedirs(self.args.directory)
@@ -54,10 +58,10 @@ class Main(App):
shutil.copyfile( shutil.copyfile(
self.args.radiobin, join(self.args.directory, radiobin_basename) self.args.radiobin, join(self.args.directory, radiobin_basename)
) )
if self.args.assets: if self.args.resources:
assets_basename = "assets.tar" resources_basename = self.RESOURCE_FILE_NAME
self.package_assets( self.package_resources(
self.args.assets, join(self.args.directory, assets_basename) self.args.resources, join(self.args.directory, resources_basename)
) )
file = FlipperFormatFile() file = FlipperFormatFile()
@@ -75,14 +79,14 @@ class Main(App):
file.writeKey("Radio CRC", self.int2ffhex(self.crc(self.args.radiobin))) file.writeKey("Radio CRC", self.int2ffhex(self.crc(self.args.radiobin)))
else: else:
file.writeKey("Radio CRC", self.int2ffhex(0)) file.writeKey("Radio CRC", self.int2ffhex(0))
file.writeKey("Assets", assets_basename) file.writeKey("Resources", resources_basename)
file.save(join(self.args.directory, "update.fuf")) file.save(join(self.args.directory, self.UPDATE_MANIFEST_NAME))
return 0 return 0
def package_assets(self, srcdir: str, dst_name: str): def package_resources(self, srcdir: str, dst_name: str):
with tarfile.open( with tarfile.open(
dst_name, self.ASSET_TAR_MODE, format=tarfile.USTAR_FORMAT dst_name, self.RESOURCE_TAR_MODE, format=self.RESOURCE_TAR_FORMAT
) as tarball: ) as tarball:
tarball.add(srcdir, arcname="") tarball.add(srcdir, arcname="")