From ed34dfa1c6738a1bc40cb9da934817fa574e6d77 Mon Sep 17 00:00:00 2001 From: Leptopt1los <53914086+Leptopt1los@users.noreply.github.com> Date: Wed, 7 Feb 2024 01:53:42 +0900 Subject: [PATCH 1/7] NFC: MF Classic parsers read() fix (dictionary attack skip) (#3355) * NFC: MF Classic lib: MfClassicErrorPartialRead added * unnecessary imports removed * MfClassicError refactor by @gornekich Co-authored-by: gornekich --------- Co-authored-by: gornekich --- .../main/nfc/plugins/supported_cards/aime.c | 2 +- .../main/nfc/plugins/supported_cards/hid.c | 2 +- .../nfc/plugins/supported_cards/plantain.c | 4 ++-- .../main/nfc/plugins/supported_cards/troika.c | 4 ++-- .../nfc/plugins/supported_cards/two_cities.c | 4 ++-- lib/nfc/protocols/mf_classic/mf_classic.h | 1 + .../mf_classic/mf_classic_poller_sync.c | 21 ++++++++----------- 7 files changed, 18 insertions(+), 20 deletions(-) diff --git a/applications/main/nfc/plugins/supported_cards/aime.c b/applications/main/nfc/plugins/supported_cards/aime.c index df1e7e077..50052f0d1 100644 --- a/applications/main/nfc/plugins/supported_cards/aime.c +++ b/applications/main/nfc/plugins/supported_cards/aime.c @@ -60,7 +60,7 @@ static bool aime_read(Nfc* nfc, NfcDevice* device) { } error = mf_classic_poller_sync_read(nfc, &keys, data); - if(error != MfClassicErrorNone) { + if(error == MfClassicErrorNotPresent) { FURI_LOG_W(TAG, "Failed to read data"); break; } diff --git a/applications/main/nfc/plugins/supported_cards/hid.c b/applications/main/nfc/plugins/supported_cards/hid.c index 66ced4d0c..b6b0a406f 100644 --- a/applications/main/nfc/plugins/supported_cards/hid.c +++ b/applications/main/nfc/plugins/supported_cards/hid.c @@ -60,7 +60,7 @@ static bool hid_read(Nfc* nfc, NfcDevice* device) { } error = mf_classic_poller_sync_read(nfc, &keys, data); - if(error != MfClassicErrorNone) { + if(error == MfClassicErrorNotPresent) { FURI_LOG_W(TAG, "Failed to read data"); break; } diff --git a/applications/main/nfc/plugins/supported_cards/plantain.c b/applications/main/nfc/plugins/supported_cards/plantain.c index a21e1cd41..b7e7497f7 100644 --- a/applications/main/nfc/plugins/supported_cards/plantain.c +++ b/applications/main/nfc/plugins/supported_cards/plantain.c @@ -135,14 +135,14 @@ static bool plantain_read(Nfc* nfc, NfcDevice* device) { } error = mf_classic_poller_sync_read(nfc, &keys, data); - if(error != MfClassicErrorNone) { + if(error != MfClassicErrorNotPresent) { FURI_LOG_W(TAG, "Failed to read data"); break; } nfc_device_set_data(device, NfcProtocolMfClassic, data); - is_read = true; + is_read = (error == MfClassicErrorNone); } while(false); mf_classic_free(data); diff --git a/applications/main/nfc/plugins/supported_cards/troika.c b/applications/main/nfc/plugins/supported_cards/troika.c index 7cf1e4dd8..e3dfb703c 100644 --- a/applications/main/nfc/plugins/supported_cards/troika.c +++ b/applications/main/nfc/plugins/supported_cards/troika.c @@ -137,14 +137,14 @@ static bool troika_read(Nfc* nfc, NfcDevice* device) { } error = mf_classic_poller_sync_read(nfc, &keys, data); - if(error != MfClassicErrorNone) { + if(error == MfClassicErrorNotPresent) { FURI_LOG_W(TAG, "Failed to read data"); break; } nfc_device_set_data(device, NfcProtocolMfClassic, data); - is_read = true; + is_read = (error == MfClassicErrorNone); } while(false); mf_classic_free(data); diff --git a/applications/main/nfc/plugins/supported_cards/two_cities.c b/applications/main/nfc/plugins/supported_cards/two_cities.c index 1748d372d..e9309f719 100644 --- a/applications/main/nfc/plugins/supported_cards/two_cities.c +++ b/applications/main/nfc/plugins/supported_cards/two_cities.c @@ -85,14 +85,14 @@ static bool two_cities_read(Nfc* nfc, NfcDevice* device) { } error = mf_classic_poller_sync_read(nfc, &keys, data); - if(error != MfClassicErrorNone) { + if(error != MfClassicErrorNotPresent) { FURI_LOG_W(TAG, "Failed to read data"); break; } nfc_device_set_data(device, NfcProtocolMfClassic, data); - is_read = true; + is_read = (error == MfClassicErrorNone); } while(false); mf_classic_free(data); diff --git a/lib/nfc/protocols/mf_classic/mf_classic.h b/lib/nfc/protocols/mf_classic/mf_classic.h index 146e6a6f1..755c457d1 100644 --- a/lib/nfc/protocols/mf_classic/mf_classic.h +++ b/lib/nfc/protocols/mf_classic/mf_classic.h @@ -39,6 +39,7 @@ typedef enum { MfClassicErrorNotPresent, MfClassicErrorProtocol, MfClassicErrorAuth, + MfClassicErrorPartialRead, MfClassicErrorTimeout, } MfClassicError; diff --git a/lib/nfc/protocols/mf_classic/mf_classic_poller_sync.c b/lib/nfc/protocols/mf_classic/mf_classic_poller_sync.c index 69954452a..8566a8612 100644 --- a/lib/nfc/protocols/mf_classic/mf_classic_poller_sync.c +++ b/lib/nfc/protocols/mf_classic/mf_classic_poller_sync.c @@ -475,19 +475,16 @@ MfClassicError nfc_poller_stop(poller); - if(poller_context.error != MfClassicErrorNone) { - error = poller_context.error; - } else { - const MfClassicData* mfc_data = nfc_poller_get_data(poller); - uint8_t sectors_read = 0; - uint8_t keys_found = 0; + const MfClassicData* mfc_data = nfc_poller_get_data(poller); + uint8_t sectors_read = 0; + uint8_t keys_found = 0; - mf_classic_get_read_sectors_and_keys(mfc_data, §ors_read, &keys_found); - if((sectors_read > 0) || (keys_found > 0)) { - mf_classic_copy(data, mfc_data); - } else { - error = MfClassicErrorNotPresent; - } + mf_classic_get_read_sectors_and_keys(mfc_data, §ors_read, &keys_found); + if((sectors_read == 0) && (keys_found == 0)) { + error = MfClassicErrorNotPresent; + } else { + mf_classic_copy(data, mfc_data); + error = mf_classic_is_card_read(mfc_data) ? MfClassicErrorNone : MfClassicErrorPartialRead; } nfc_poller_free(poller); From e0782966d4276ae1f71bd3d902d377aa1ab37ed1 Mon Sep 17 00:00:00 2001 From: Astra <93453568+Astrrra@users.noreply.github.com> Date: Tue, 6 Feb 2024 21:29:17 +0400 Subject: [PATCH 2/7] [FL-3661] Troika layout fixes (#3365) * Add support for different troika layouts * Display additional data if debug is enabled in settings * Support for layout 2, where there's no balance * nfc app plugins: fix mfc read error processing * nfc app: clean up troika plugin * nfc app: troika parser more clean up --------- Co-authored-by: gornekich Co-authored-by: hedger --- .../nfc/plugins/supported_cards/plantain.c | 2 +- .../main/nfc/plugins/supported_cards/troika.c | 175 ++++++++++++++++-- .../nfc/plugins/supported_cards/two_cities.c | 2 +- 3 files changed, 165 insertions(+), 14 deletions(-) diff --git a/applications/main/nfc/plugins/supported_cards/plantain.c b/applications/main/nfc/plugins/supported_cards/plantain.c index b7e7497f7..ba8f34295 100644 --- a/applications/main/nfc/plugins/supported_cards/plantain.c +++ b/applications/main/nfc/plugins/supported_cards/plantain.c @@ -135,7 +135,7 @@ static bool plantain_read(Nfc* nfc, NfcDevice* device) { } error = mf_classic_poller_sync_read(nfc, &keys, data); - if(error != MfClassicErrorNotPresent) { + if(error == MfClassicErrorNotPresent) { FURI_LOG_W(TAG, "Failed to read data"); break; } diff --git a/applications/main/nfc/plugins/supported_cards/troika.c b/applications/main/nfc/plugins/supported_cards/troika.c index e3dfb703c..20bd685ee 100644 --- a/applications/main/nfc/plugins/supported_cards/troika.c +++ b/applications/main/nfc/plugins/supported_cards/troika.c @@ -5,6 +5,7 @@ #include #include #include +#include "furi_hal_rtc.h" #define TAG "Troika" @@ -18,6 +19,19 @@ typedef struct { uint32_t data_sector; } TroikaCardConfig; +typedef enum { + TroikaLayoutUnknown = 0x0, + TroikaLayout2 = 0x2, + TroikaLayoutE = 0xE, +} TroikaLayout; + +typedef enum { + TroikaSublayoutUnknown = 0x0, + TroikaSublayout3 = 0x3, + TroikaSublayout5 = 0x5, + TroikaSublayout6 = 0x6, +} TroikaSubLayout; + static const MfClassicKeyPair troika_1k_keys[] = { {.a = 0xa0a1a2a3a4a5, .b = 0xfbf225dc5d58}, {.a = 0xa82607b01c0d, .b = 0x2910989b6880}, @@ -67,7 +81,7 @@ static bool troika_get_card_config(TroikaCardConfig* config, MfClassicType type) config->data_sector = 8; config->keys = troika_1k_keys; } else if(type == MfClassicType4k) { - config->data_sector = 4; + config->data_sector = 8; // Further testing needed config->keys = troika_4k_keys; } else { success = false; @@ -76,6 +90,126 @@ static bool troika_get_card_config(TroikaCardConfig* config, MfClassicType type) return success; } +static TroikaLayout troika_get_layout(const MfClassicData* data, uint8_t start_block_num) { + furi_assert(data); + + // Layout is stored in byte 6 of block, length 4 bits (bits 52 - 55), second nibble. + const uint8_t* layout_ptr = &data->block[start_block_num].data[6]; + const uint8_t layout = (*layout_ptr & 0x0F); + + TroikaLayout result = TroikaLayoutUnknown; + switch(layout) { + case TroikaLayout2: + case TroikaLayoutE: + result = layout; + break; + default: + // If debug is enabled - pass the actual layout value for the debug text + if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { + return layout; + } else { + return TroikaLayoutUnknown; + } + } + + return result; +} + +static TroikaSubLayout troika_get_sub_layout(const MfClassicData* data, uint8_t start_block_num) { + furi_assert(data); + + // Sublayout is stored in byte 7 (bits 56 - 60) of block, length 5 bits (first nibble and one bit from second nibble) + const uint8_t* sub_layout_ptr = &data->block[start_block_num].data[7]; + const uint8_t sub_layout = (*sub_layout_ptr & 0x3F) >> 3; + + TroikaSubLayout result = TroikaSublayoutUnknown; + switch(sub_layout) { + case TroikaSublayout3: + case TroikaSublayout5: + case TroikaSublayout6: + result = sub_layout; + break; + default: + // If debug is enabled - pass the actual sublayout value for the debug text + if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { + return sub_layout; + } else { + return TroikaSublayoutUnknown; + } + } + + return result; +} + +static bool troika_has_balance(TroikaLayout layout, TroikaSubLayout sub_layout) { + UNUSED(sub_layout); + // Layout 0x2 has no balance + + if(layout == TroikaLayout2) { + return false; + } + + return true; +} + +static uint16_t troika_get_balance( + const MfClassicData* data, + uint8_t start_block_num, + TroikaLayout layout, + TroikaSubLayout sub_layout) { + furi_assert(data); + + // In layout 0x3 balance in bits 188:209 ( from sector start, length 22). + // In layout 0x5 balance in bits 165:185 ( from sector start, length 20). + + uint32_t balance = 0; + uint8_t balance_data_offset = 0; + bool supported_layout = false; + + if(layout == TroikaLayoutE && sub_layout == TroikaSublayout3) { + balance_data_offset = 7; + supported_layout = true; + } else if(layout == TroikaLayoutE && sub_layout == TroikaSublayout5) { + balance_data_offset = 4; + supported_layout = true; + } + + if(supported_layout) { + const uint8_t* temp_ptr = &data->block[start_block_num + 1].data[balance_data_offset]; + balance |= (temp_ptr[0] & 0x3) << 18; + balance |= temp_ptr[1] << 10; + balance |= temp_ptr[2] << 2; + balance |= (temp_ptr[3] & 0xC0) >> 6; + } + + return balance / 100; +} + +static uint32_t troika_get_number( + const MfClassicData* data, + uint8_t start_block_num, + TroikaLayout layout, + TroikaSubLayout sub_layout) { + furi_assert(data); + UNUSED(sub_layout); + + if(layout == TroikaLayoutE || layout == TroikaLayout2) { + const uint8_t* temp_ptr = &data->block[start_block_num].data[2]; + + uint32_t number = 0; + for(size_t i = 1; i < 5; i++) { + number <<= 8; + number |= temp_ptr[i]; + } + number >>= 4; + number |= (temp_ptr[0] & 0xf) << 28; + + return number; + } else { + return 0; + } +} + static bool troika_verify_type(Nfc* nfc, MfClassicType type) { bool verified = false; @@ -171,22 +305,39 @@ static bool troika_parse(const NfcDevice* device, FuriString* parsed_data) { const uint64_t key = nfc_util_bytes2num(sec_tr->key_a.data, COUNT_OF(sec_tr->key_a.data)); if(key != cfg.keys[cfg.data_sector].a) break; - // Parse data + // Get the block number of the block that contains the data const uint8_t start_block_num = mf_classic_get_first_block_num_of_sector(cfg.data_sector); - const uint8_t* temp_ptr = &data->block[start_block_num + 1].data[5]; - uint16_t balance = ((temp_ptr[0] << 8) | temp_ptr[1]) / 25; - temp_ptr = &data->block[start_block_num].data[2]; + // Get layout, sublayout, balance and number + TroikaLayout layout = troika_get_layout(data, start_block_num); + TroikaSubLayout sub_layout = troika_get_sub_layout(data, start_block_num); - uint32_t number = 0; - for(size_t i = 1; i < 5; i++) { - number <<= 8; - number |= temp_ptr[i]; + if(!furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { + // If debug is enabled - proceed even if layout or sublayout is unknown, that will make collecting data easier + if(layout == TroikaLayoutUnknown || sub_layout == TroikaSublayoutUnknown) break; + } + + uint32_t number = troika_get_number(data, start_block_num, layout, sub_layout); + + furi_string_printf(parsed_data, "\e#Troika\nNum: %lu", number); + + if(troika_has_balance(layout, sub_layout) || + furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { + uint16_t balance = troika_get_balance(data, start_block_num, layout, sub_layout); + furi_string_cat_printf(parsed_data, "\nBalance: %u RUR", balance); + } else { + furi_string_cat_printf(parsed_data, "\nBalance: Not available"); + } + + if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { + furi_string_cat_printf( + parsed_data, + "\nLayout: %02x\nSublayout: %02x\nData Block: %u", + layout, + sub_layout, + start_block_num); } - number >>= 4; - number |= (temp_ptr[0] & 0xf) << 28; - furi_string_printf(parsed_data, "\e#Troika\nNum: %lu\nBalance: %u RUR", number, balance); parsed = true; } while(false); diff --git a/applications/main/nfc/plugins/supported_cards/two_cities.c b/applications/main/nfc/plugins/supported_cards/two_cities.c index e9309f719..7e29cd085 100644 --- a/applications/main/nfc/plugins/supported_cards/two_cities.c +++ b/applications/main/nfc/plugins/supported_cards/two_cities.c @@ -85,7 +85,7 @@ static bool two_cities_read(Nfc* nfc, NfcDevice* device) { } error = mf_classic_poller_sync_read(nfc, &keys, data); - if(error != MfClassicErrorNotPresent) { + if(error == MfClassicErrorNotPresent) { FURI_LOG_W(TAG, "Failed to read data"); break; } From c736c3f3f09b09db7df80af0964ad058572b1499 Mon Sep 17 00:00:00 2001 From: Tolly Hill Date: Tue, 6 Feb 2024 17:34:54 +0000 Subject: [PATCH 3/7] NFC: Display unread Mifare Classic bytes as question marks (#3384) * NFC: Display unread Mifare Classic bytes as ?? * Apply patch from @gornekich --------- Co-authored-by: gornekich --- .../mf_classic/mf_classic_render.c | 43 +++++++++++++++++-- 1 file changed, 39 insertions(+), 4 deletions(-) diff --git a/applications/main/nfc/helpers/protocol_support/mf_classic/mf_classic_render.c b/applications/main/nfc/helpers/protocol_support/mf_classic/mf_classic_render.c index 5bd4a6b6d..0382b3333 100644 --- a/applications/main/nfc/helpers/protocol_support/mf_classic/mf_classic_render.c +++ b/applications/main/nfc/helpers/protocol_support/mf_classic/mf_classic_render.c @@ -18,13 +18,48 @@ void nfc_render_mf_classic_info( furi_string_cat_printf(str, "\nSectors Read: %u/%u", sectors_read, sectors_total); } +static void + mf_classic_render_raw_data(const uint8_t* data, size_t size, bool data_read, FuriString* str) { + furi_assert((size % 2) == 0); + + for(size_t i = 0; i < size; i += 2) { + if(data_read) { + furi_string_cat_printf(str, "%02X%02X ", data[i], data[i + 1]); + } else { + furi_string_cat_printf(str, "???? "); + } + } +} + +static void + mf_classic_render_block(const MfClassicData* data, uint8_t block_num, FuriString* str) { + if(mf_classic_is_sector_trailer(block_num)) { + uint8_t sec_num = mf_classic_get_sector_by_block(block_num); + MfClassicSectorTrailer* sec_tr = mf_classic_get_sector_trailer_by_sector(data, sec_num); + + // Render key A + bool key_read = mf_classic_is_key_found(data, sec_num, MfClassicKeyTypeA); + mf_classic_render_raw_data(sec_tr->key_a.data, sizeof(MfClassicKey), key_read, str); + + // Render access bits + bool access_bits_read = mf_classic_is_block_read(data, block_num); + mf_classic_render_raw_data( + sec_tr->access_bits.data, sizeof(MfClassicAccessBits), access_bits_read, str); + + // Render key B + key_read = mf_classic_is_key_found(data, sec_num, MfClassicKeyTypeB); + mf_classic_render_raw_data(sec_tr->key_b.data, sizeof(MfClassicKey), key_read, str); + } else { + const uint8_t* block_data = data->block[block_num].data; + bool block_read = mf_classic_is_block_read(data, block_num); + mf_classic_render_raw_data(block_data, sizeof(MfClassicBlock), block_read, str); + } +} + void nfc_render_mf_classic_dump(const MfClassicData* data, FuriString* str) { uint16_t total_blocks = mf_classic_get_total_block_num(data->type); for(size_t i = 0; i < total_blocks; i++) { - for(size_t j = 0; j < sizeof(MfClassicBlock); j += 2) { - furi_string_cat_printf( - str, "%02X%02X ", data->block[i].data[j], data->block[i].data[j + 1]); - } + mf_classic_render_block(data, i, str); } } From 2c784d3feae51ef01089c816ce73b782cf580df3 Mon Sep 17 00:00:00 2001 From: MMX <10697207+xMasterX@users.noreply.github.com> Date: Tue, 6 Feb 2024 20:42:06 +0300 Subject: [PATCH 4/7] Fix nfc_protocol_support_scene_save_name_on_event crash (#3418) * Add Clearing of detected protocols list in nfc_scene_start * Apply patch --------- Co-authored-by: hedger --- .../main/nfc/helpers/protocol_support/nfc_protocol_support.c | 3 +-- applications/main/nfc/scenes/nfc_scene_start.c | 2 ++ 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.c b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.c index c87ee613f..da75142ba 100644 --- a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.c +++ b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.c @@ -521,8 +521,7 @@ static bool scene_manager_has_previous_scene(instance->scene_manager, NfcSceneSetType) ? DolphinDeedNfcAddSave : DolphinDeedNfcSave); - const NfcProtocol protocol = - instance->protocols_detected[instance->protocols_detected_selected_idx]; + const NfcProtocol protocol = nfc_device_get_protocol(instance->nfc_device); consumed = nfc_protocol_support[protocol]->scene_save_name.on_event( instance, event.event); } else { diff --git a/applications/main/nfc/scenes/nfc_scene_start.c b/applications/main/nfc/scenes/nfc_scene_start.c index c923226fc..e8774b4aa 100644 --- a/applications/main/nfc/scenes/nfc_scene_start.c +++ b/applications/main/nfc/scenes/nfc_scene_start.c @@ -24,6 +24,8 @@ void nfc_scene_start_on_enter(void* context) { furi_string_reset(nfc->file_name); nfc_device_clear(nfc->nfc_device); iso14443_3a_reset(nfc->iso14443_3a_edit_data); + // Reset detected protocols list + nfc_app_reset_detected_protocols(nfc); submenu_add_item(submenu, "Read", SubmenuIndexRead, nfc_scene_start_submenu_callback, nfc); submenu_add_item( From a83e421d7133bb224d81cd8c412387532a31e1b3 Mon Sep 17 00:00:00 2001 From: Jeremy Cooper Date: Tue, 6 Feb 2024 09:47:44 -0800 Subject: [PATCH 5/7] Add an NFC parser for the San Francisco Bay Area "Clipper" transit card. (#3344) * Add an NFC parser for the San Francisco Bay Area "Clipper" transit card. * Add more agencies and stations, decode vehicle ids, refactor. Add more agencies and station names using data from local research and: * Metrodroid project (GPLv3): https://github.com/metrodroid/metrodroid * Farebot (GPLv3): https://github.com/codebutler/farebot --------- Co-authored-by: gornekich Co-authored-by: hedger --- applications/main/nfc/application.fam | 9 + .../nfc/plugins/supported_cards/clipper.c | 621 ++++++++++++++++++ 2 files changed, 630 insertions(+) create mode 100644 applications/main/nfc/plugins/supported_cards/clipper.c diff --git a/applications/main/nfc/application.fam b/applications/main/nfc/application.fam index b92d0ebf1..27cb1e9ee 100644 --- a/applications/main/nfc/application.fam +++ b/applications/main/nfc/application.fam @@ -110,6 +110,15 @@ App( sources=["plugins/supported_cards/umarsh.c"], ) +App( + appid="clipper_parser", + apptype=FlipperAppType.PLUGIN, + entry_point="clipper_plugin_ep", + targets=["f7"], + requires=["nfc"], + sources=["plugins/supported_cards/clipper.c"], +) + App( appid="hid_parser", apptype=FlipperAppType.PLUGIN, diff --git a/applications/main/nfc/plugins/supported_cards/clipper.c b/applications/main/nfc/plugins/supported_cards/clipper.c new file mode 100644 index 000000000..c76fb1cdc --- /dev/null +++ b/applications/main/nfc/plugins/supported_cards/clipper.c @@ -0,0 +1,621 @@ +/* + * clipper.c - Parser for Clipper cards (San Francisco, California). + * + * Based on research, some of which dates to 2007! + * + * Copyright 2024 Jeremy Cooper + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include "nfc_supported_card_plugin.h" + +#include +#include +#include +#include +#include +#include + +// +// Table of application ids observed in the wild, and their sources. +// +static const struct { + const MfDesfireApplicationId app; + const char* type; +} clipper_types[] = { + // Application advertised on classic, plastic cards. + {.app = {.data = {0x90, 0x11, 0xf2}}, .type = "Card"}, + // Application advertised on a mobile device. + {.app = {.data = {0x91, 0x11, 0xf2}}, .type = "Mobile Device"}, +}; +static const size_t kNumCardTypes = sizeof(clipper_types) / sizeof(clipper_types[0]); + +struct IdMapping_struct { + uint16_t id; + const char* name; +}; +typedef struct IdMapping_struct IdMapping; + +#define COUNT(_array) sizeof(_array) / sizeof(_array[0]) + +// +// Known transportation agencies and their identifiers. +// +static const IdMapping agency_names[] = { + {.id = 0x0001, .name = "AC Transit"}, + {.id = 0x0004, .name = "BART"}, + {.id = 0x0006, .name = "Caltrain"}, + {.id = 0x0008, .name = "CCTA"}, + {.id = 0x000b, .name = "GGT"}, + {.id = 0x000f, .name = "SamTrans"}, + {.id = 0x0011, .name = "VTA"}, + {.id = 0x0012, .name = "Muni"}, + {.id = 0x0019, .name = "GG Ferry"}, + {.id = 0x001b, .name = "SF Bay Ferry"}, +}; +static const size_t kNumAgencies = COUNT(agency_names); + +// +// Known station names for various agencies. +// +static const IdMapping bart_zones[] = { + {.id = 0x0001, .name = "Colma"}, + {.id = 0x0002, .name = "Daly City"}, + {.id = 0x0003, .name = "Balboa Park"}, + {.id = 0x0004, .name = "Glen Park"}, + {.id = 0x0005, .name = "24th St Mission"}, + {.id = 0x0006, .name = "16th St Mission"}, + {.id = 0x0007, .name = "Civic Center/UN Plaza"}, + {.id = 0x0008, .name = "Powell St"}, + {.id = 0x0009, .name = "Montgomery St"}, + {.id = 0x000a, .name = "Embarcadero"}, + {.id = 0x000b, .name = "West Oakland"}, + {.id = 0x000c, .name = "12th St/Oakland City Center"}, + {.id = 0x000d, .name = "19th St/Oakland"}, + {.id = 0x000e, .name = "MacArthur"}, + {.id = 0x000f, .name = "Rockridge"}, + {.id = 0x0010, .name = "Orinda"}, + {.id = 0x0011, .name = "Lafayette"}, + {.id = 0x0012, .name = "Walnut Creek"}, + {.id = 0x0013, .name = "Pleasant Hill/Contra Costa Centre"}, + {.id = 0x0014, .name = "Concord"}, + {.id = 0x0015, .name = "North Concord/Martinez"}, + {.id = 0x0016, .name = "Pittsburg/Bay Point"}, + {.id = 0x0017, .name = "Ashby"}, + {.id = 0x0018, .name = "Downtown Berkeley"}, + {.id = 0x0019, .name = "North Berkeley"}, + {.id = 0x001a, .name = "El Cerrito Plaza"}, + {.id = 0x001b, .name = "El Cerrito Del Norte"}, + {.id = 0x001c, .name = "Richmond"}, + {.id = 0x001d, .name = "Lake Merrit"}, + {.id = 0x001e, .name = "Fruitvale"}, + {.id = 0x001f, .name = "Coliseum"}, + {.id = 0x0021, .name = "San Leandro"}, + {.id = 0x0022, .name = "Hayward"}, + {.id = 0x0023, .name = "South Hayward"}, + {.id = 0x0024, .name = "Union City"}, + {.id = 0x0025, .name = "Fremont"}, + {.id = 0x0026, .name = "Daly City(2)?"}, + {.id = 0x0027, .name = "Dublin/Pleasanton"}, + {.id = 0x0028, .name = "South San Francisco"}, + {.id = 0x0029, .name = "San Bruno"}, + {.id = 0x002a, .name = "SFO Airport"}, + {.id = 0x002b, .name = "Millbrae"}, + {.id = 0x002c, .name = "West Dublin/Pleasanton"}, + {.id = 0x002d, .name = "OAK Airport"}, + {.id = 0x002e, .name = "Warm Springs/South Fremont"}, +}; +static const size_t kNumBARTZones = COUNT(bart_zones); + +static const IdMapping muni_zones[] = { + {.id = 0x0000, .name = "City Street"}, + {.id = 0x0005, .name = "Embarcadero"}, + {.id = 0x0006, .name = "Montgomery"}, + {.id = 0x0007, .name = "Powell"}, + {.id = 0x0008, .name = "Civic Center"}, + {.id = 0x0009, .name = "Van Ness"}, // Guessed + {.id = 0x000a, .name = "Church"}, + {.id = 0x000b, .name = "Castro"}, + {.id = 0x000c, .name = "Forest Hill"}, // Guessed + {.id = 0x000d, .name = "West Portal"}, +}; +static const size_t kNumMUNIZones = COUNT(muni_zones); + +static const IdMapping actransit_zones[] = { + {.id = 0x0000, .name = "City Street"}, +}; +static const size_t kNumACTransitZones = COUNT(actransit_zones); + +// +// Full agency+zone mapping. +// +static const struct { + uint16_t agency_id; + const IdMapping* zone_map; + size_t zone_count; +} agency_zone_map[] = { + {.agency_id = 0x0001, .zone_map = actransit_zones, .zone_count = kNumACTransitZones}, + {.agency_id = 0x0004, .zone_map = bart_zones, .zone_count = kNumBARTZones}, + {.agency_id = 0x0012, .zone_map = muni_zones, .zone_count = kNumMUNIZones}}; +static const size_t kNumAgencyZoneMaps = COUNT(agency_zone_map); + +// File ids of important files on the card. +static const MfDesfireFileId clipper_ecash_file_id = 2; +static const MfDesfireFileId clipper_histidx_file_id = 6; +static const MfDesfireFileId clipper_identity_file_id = 8; +static const MfDesfireFileId clipper_history_file_id = 14; + +struct ClipperCardInfo_struct { + uint32_t serial_number; + uint16_t counter; + uint16_t last_txn_id; + uint32_t last_updated_tm_1900; + uint16_t last_terminal_id; + int16_t balance_cents; +}; +typedef struct ClipperCardInfo_struct ClipperCardInfo; + +// Forward declarations for helper functions. +static void furi_string_cat_timestamp( + FuriString* str, + const char* date_hdr, + const char* time_hdr, + uint32_t tmst_1900); +static void epoch_1900_datetime_to_furi(uint32_t seconds, FuriHalRtcDateTime* out); +static bool get_file_contents( + const MfDesfireApplication* app, + const MfDesfireFileId* id, + MfDesfireFileType type, + size_t min_size, + const uint8_t** out); +static bool decode_id_file(const uint8_t* ef8_data, ClipperCardInfo* info); +static bool decode_cash_file(const uint8_t* ef2_data, ClipperCardInfo* info); +static bool get_map_item(uint16_t id, const IdMapping* map, size_t sz, const char** out); +static bool get_agency_zone_name(uint16_t agency_id, uint16_t zone_id, const char** out); +static void + decode_usd(int16_t amount_cents, bool* out_is_negative, int16_t* out_usd, uint16_t* out_cents); +static bool dump_ride_history( + const uint8_t* index_file, + const uint8_t* history_file, + size_t len, + FuriString* parsed_data); +static bool dump_ride_event(const uint8_t* record, FuriString* parsed_data); + +// Unmarshal a 32-bit integer, big endian, unsigned +static inline uint32_t get_u32be(const uint8_t* field) { + return nfc_util_bytes2num(field, 4); +} + +// Unmarshal a 16-bit integer, big endian, unsigned +static uint16_t get_u16be(const uint8_t* field) { + return nfc_util_bytes2num(field, 2); +} + +// Unmarshal a 16-bit integer, big endian, signed, two's-complement +static int16_t get_i16be(const uint8_t* field) { + uint16_t raw = get_u16be(field); + if(raw > 0x7fff) + return -((uint32_t)0x10000 - raw); + else + return raw; +} + +static bool clipper_parse(const NfcDevice* device, FuriString* parsed_data) { + furi_assert(device); + furi_assert(parsed_data); + + bool parsed = false; + + do { + const MfDesfireData* data = nfc_device_get_data(device, NfcProtocolMfDesfire); + + const MfDesfireApplication* app = NULL; + const char* device_description = NULL; + + for(size_t i = 0; i < kNumCardTypes; i++) { + app = mf_desfire_get_application(data, &clipper_types[i].app); + device_description = clipper_types[i].type; + if(app != NULL) break; + } + + // If no matching application was found, abort this parser. + if(app == NULL) break; + + ClipperCardInfo info; + const uint8_t* id_data; + if(!get_file_contents( + app, &clipper_identity_file_id, MfDesfireFileTypeStandard, 5, &id_data)) + break; + if(!decode_id_file(id_data, &info)) break; + + const uint8_t* cash_data; + if(!get_file_contents(app, &clipper_ecash_file_id, MfDesfireFileTypeBackup, 32, &cash_data)) + break; + if(!decode_cash_file(cash_data, &info)) break; + + int16_t balance_usd; + uint16_t balance_cents; + bool _balance_is_negative; + decode_usd(info.balance_cents, &_balance_is_negative, &balance_usd, &balance_cents); + + furi_string_cat_printf( + parsed_data, + "\e#Clipper\n" + "Serial: %" PRIu32 "\n" + "Balance: $%d.%02u\n" + "Type: %s\n" + "\e#Last Update\n", + info.serial_number, + balance_usd, + balance_cents, + device_description); + if(info.last_updated_tm_1900 != 0) + furi_string_cat_timestamp( + parsed_data, "Date: ", "\nTime: ", info.last_updated_tm_1900); + else + furi_string_cat_str(parsed_data, "Never"); + furi_string_cat_printf( + parsed_data, + "\nTerminal: 0x%04x\n" + "Transaction Id: %u\n" + "Counter: %u\n", + info.last_terminal_id, + info.last_txn_id, + info.counter); + + const uint8_t *history_index, *history; + + if(!get_file_contents( + app, &clipper_histidx_file_id, MfDesfireFileTypeBackup, 16, &history_index)) + break; + if(!get_file_contents( + app, &clipper_history_file_id, MfDesfireFileTypeStandard, 512, &history)) + break; + + if(!dump_ride_history(history_index, history, 512, parsed_data)) break; + + parsed = true; + } while(false); + + return parsed; +} + +static bool get_file_contents( + const MfDesfireApplication* app, + const MfDesfireFileId* id, + MfDesfireFileType type, + size_t min_size, + const uint8_t** out) { + const MfDesfireFileSettings* settings = mf_desfire_get_file_settings(app, id); + if(settings == NULL) return false; + if(settings->type != type) return false; + + const MfDesfireFileData* file_data = mf_desfire_get_file_data(app, id); + if(file_data == NULL) return false; + + if(simple_array_get_count(file_data->data) < min_size) return false; + + *out = simple_array_cget_data(file_data->data); + + return true; +} + +static bool decode_id_file(const uint8_t* ef8_data, ClipperCardInfo* info) { + // Identity file (8) + // + // Byte view + // + // 0 1 2 3 4 5 6 7 8 + // +----+----.----.----.----+----.----.----+ + // 0x00 | uk | card_id | unknown | + // +----+----.----.----.----+----.----.----+ + // 0x08 | unknown | + // +----.----.----.----.----.----.----.----+ + // 0x10 ... + // + // + // Field Datatype Description + // ----- -------- ----------- + // uk ?8?? Unknown, 8-bit byte + // card_id U32BE Card identifier + // + info->serial_number = nfc_util_bytes2num(&ef8_data[1], 4); + return true; +} + +static bool decode_cash_file(const uint8_t* ef2_data, ClipperCardInfo* info) { + // ECash file (2) + // + // Byte view + // + // 0 1 2 3 4 5 6 7 8 + // +----.----+----.----+----.----.----.----+ + // 0x00 | unk00 | counter | timestamp_1900 | + // +----.----+----.----+----.----.----.----+ + // 0x08 | term_id | unk01 | + // +----.----+----.----+----.----.----.----+ + // 0x10 | txn_id | balance | unknown | + // +----.----+----.----+----.----.----.----+ + // 0x18 | unknown | + // +---------------------------------------+ + // + // Field Datatype Description + // ----- -------- ----------- + // unk00 U8[2] Unknown bytes + // counter U16BE Unknown, appears to be a counter + // timestamp_1900 U32BE Timestamp of last transaction, in seconds + // since 1900-01-01 GMT. + // unk01 U8[6] Unknown bytes + // txn_id U16BE Id of last transaction. + // balance S16BE Card cash balance, in cents. + // Cards can obtain negative balances in this + // system, so balances are signed integers. + // Maximum card balance is therefore + // $327.67. + // unk02 U8[12] Unknown bytes. + // + info->counter = get_u16be(&ef2_data[2]); + info->last_updated_tm_1900 = get_u32be(&ef2_data[4]); + info->last_terminal_id = get_u16be(&ef2_data[8]); + info->last_txn_id = get_u16be(&ef2_data[0x10]); + info->balance_cents = get_i16be(&ef2_data[0x12]); + return true; +} + +static bool dump_ride_history( + const uint8_t* index_file, + const uint8_t* history_file, + size_t len, + FuriString* parsed_data) { + static const size_t kRideRecordSize = 0x20; + + for(size_t i = 0; i < 16; i++) { + uint8_t record_num = index_file[i]; + if(record_num == 0xff) break; + + size_t record_offset = record_num * kRideRecordSize; + + if(record_offset + kRideRecordSize > len) break; + + const uint8_t* record = &history_file[record_offset]; + if(!dump_ride_event(record, parsed_data)) break; + } + + return true; +} + +static bool dump_ride_event(const uint8_t* record, FuriString* parsed_data) { + // Ride record + // + // 0 1 2 3 4 5 6 7 8 + // +----+----+----.----+----.----+----.----+ + // 0x00 |0x10| ? | agency | ? | fare | + // +----.----+----.----+----.----.----.----+ + // 0x08 | ? | vehicle | time_on | + // +----.----.----.----+----.----+----.----+ + // 0x10 | time_off | zone_on | zone_off| + // +----+----.----.----.----+----+----+----+ + // 0x18 | ? | ? | ? | ? | ? | + // +----+----.----.----.----+----+----+----+ + // + // Field Datatype Description + // ----- -------- ----------- + // agency U16BE Transportation agency identifier. + // Known ids: + // 1 == AC Transit + // 4 == BART + // 18 == SF MUNI + // fare I16BE Fare deducted, in cents. + // vehicle U16BE Vehicle id (0 == not provided) + // time_on U32BE Boarding time, in seconds since 1900-01-01 GMT. + // time_off U32BE Off-boarding time, if present, in seconds + // since 1900-01-01 GMT. Set to zero if no offboard + // has been recorded. + // zone_on U16BE Id of boarding zone or station. Agency-specific. + // zone_off U16BE Id of offboarding zone or station. Agency- + // specific. + if(record[0] != 0x10) return false; + + uint16_t agency_id = get_u16be(&record[2]); + if(agency_id == 0) + // Likely empty record. Skip. + return false; + const char* agency_name; + bool ok = get_map_item(agency_id, agency_names, kNumAgencies, &agency_name); + if(!ok) agency_name = "Unknown"; + + uint16_t vehicle_id = get_u16be(&record[0x0a]); + + int16_t fare_raw_cents = get_i16be(&record[6]); + bool _fare_is_negative; + int16_t fare_usd; + uint16_t fare_cents; + decode_usd(fare_raw_cents, &_fare_is_negative, &fare_usd, &fare_cents); + + uint32_t time_on_raw = get_u32be(&record[0x0c]); + uint32_t time_off_raw = get_u32be(&record[0x10]); + uint16_t zone_id_on = get_u16be(&record[0x14]); + uint16_t zone_id_off = get_u16be(&record[0x16]); + + const char *zone_on, *zone_off; + if(!get_agency_zone_name(agency_id, zone_id_on, &zone_on)) { + zone_on = "Unknown"; + } + if(!get_agency_zone_name(agency_id, zone_id_off, &zone_off)) { + zone_off = "Unknown"; + } + + furi_string_cat_str(parsed_data, "\e#Ride Record\n"); + furi_string_cat_timestamp(parsed_data, "Date: ", "\nTime: ", time_on_raw); + furi_string_cat_printf( + parsed_data, + "\n" + "Fare: $%d.%02u\n" + "Agency: %s (%04x)\n" + "On: %s (%04x)\n", + fare_usd, + fare_cents, + agency_name, + agency_id, + zone_on, + zone_id_on); + if(vehicle_id != 0) { + furi_string_cat_printf(parsed_data, "Vehicle id: %d\n", vehicle_id); + } + if(time_off_raw != 0) { + furi_string_cat_printf(parsed_data, "Off: %s (%04x)\n", zone_off, zone_id_off); + furi_string_cat_timestamp(parsed_data, "Date Off: ", "\nTime Off: ", time_off_raw); + furi_string_cat_str(parsed_data, "\n"); + } + + return true; +} + +static bool get_map_item(uint16_t id, const IdMapping* map, size_t sz, const char** out) { + for(size_t i = 0; i < sz; i++) { + if(map[i].id == id) { + *out = map[i].name; + return true; + } + } + + return false; +} + +static bool get_agency_zone_name(uint16_t agency_id, uint16_t zone_id, const char** out) { + for(size_t i = 0; i < kNumAgencyZoneMaps; i++) { + if(agency_zone_map[i].agency_id == agency_id) { + return get_map_item( + zone_id, agency_zone_map[i].zone_map, agency_zone_map[i].zone_count, out); + } + } + + return false; +} + +// Split a balance/fare amount from raw cents to dollars and cents portion, +// automatically adjusting the cents portion so that it is always positive, +// for easier display. +static void + decode_usd(int16_t amount_cents, bool* out_is_negative, int16_t* out_usd, uint16_t* out_cents) { + *out_usd = amount_cents / 100; + + if(amount_cents >= 0) { + *out_is_negative = false; + *out_cents = amount_cents % 100; + } else { + *out_is_negative = true; + *out_cents = (amount_cents * -1) % 100; + } +} + +// Decode a raw 1900-based timestamp and append a human-readable form to a +// FuriString. +static void furi_string_cat_timestamp( + FuriString* str, + const char* date_hdr, + const char* time_hdr, + uint32_t tmst_1900) { + FuriHalRtcDateTime tm; + + epoch_1900_datetime_to_furi(tmst_1900, &tm); + + FuriString* date_str = furi_string_alloc(); + locale_format_date(date_str, &tm, locale_get_date_format(), "-"); + + FuriString* time_str = furi_string_alloc(); + locale_format_time(time_str, &tm, locale_get_time_format(), true); + + furi_string_cat_printf( + str, + "%s%s%s%s (UTC)", + date_hdr, + furi_string_get_cstr(date_str), + time_hdr, + furi_string_get_cstr(time_str)); + + furi_string_free(date_str); + furi_string_free(time_str); +} + +// Convert a "1900"-based timestamp to Furi time, assuming a UTC/GMT timezone. +static void epoch_1900_datetime_to_furi(uint32_t seconds, FuriHalRtcDateTime* out) { + uint16_t year, month, day, hour, minute, second; + + // Calculate absolute number of days elapsed since the 1900 epoch + // and save the residual for the time within the day. + uint32_t absolute_days = seconds / 86400; + uint32_t seconds_within_day = seconds % 86400; + + // Calculate day of the week. + // January 1, 1900 was a Monday ("day of week" = 1) + uint8_t dow = (absolute_days + 1) % 7; + + // + // Compute the date by simply marching through time in as large chunks + // as possible. + // + + for(year = 1900;; year++) { + uint16_t year_days = furi_hal_rtc_get_days_per_year(year); + if(absolute_days >= year_days) + absolute_days -= year_days; + else + break; + } + + bool is_leap = furi_hal_rtc_is_leap_year(year); + + for(month = 1;; month++) { + uint8_t days_in_month = furi_hal_rtc_get_days_per_month(is_leap, month); + if(absolute_days >= days_in_month) + absolute_days -= days_in_month; + else + break; + } + + day = absolute_days + 1; + hour = seconds_within_day / 3600; + uint16_t sub_hour = seconds_within_day % 3600; + minute = sub_hour / 60; + second = sub_hour % 60; + + out->year = year; + out->month = month; + out->day = day; + out->hour = hour; + out->minute = minute; + out->second = second; + out->weekday = dow; +} + +/* Actual implementation of app<>plugin interface */ +static const NfcSupportedCardsPlugin clipper_plugin = { + .protocol = NfcProtocolMfDesfire, + .verify = NULL, + .read = NULL, + .parse = clipper_parse, +}; + +/* Plugin descriptor to comply with basic plugin specification */ +static const FlipperAppPluginDescriptor clipper_plugin_descriptor = { + .appid = NFC_SUPPORTED_CARD_PLUGIN_APP_ID, + .ep_api_version = NFC_SUPPORTED_CARD_PLUGIN_API_VERSION, + .entry_point = &clipper_plugin, +}; + +/* Plugin entry point - must return a pointer to const descriptor */ +const FlipperAppPluginDescriptor* clipper_plugin_ep() { + return &clipper_plugin_descriptor; +} From 9a77dbec568efaf48574df940c6f4671cc3132fa Mon Sep 17 00:00:00 2001 From: RebornedBrain <138568282+RebornedBrain@users.noreply.github.com> Date: Tue, 6 Feb 2024 20:57:06 +0300 Subject: [PATCH 6/7] [FL-3678] NFC UI refactor (#3382) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Added new image DolphinSaved_113x58.png for all "saved" pages * New image DolphinDone_80x58.png added * Replaced dolphins on all scenes accroding to new UI specs * New success dolphin image added * Success scene image replaced * Changed image and text for update initial scene * Image and text adjusted for "Original restored" scene * Removed old DolphinNice_96x59.png image * New image for LFRFID scene * Removed unused image * New UI image added to assets * Replaced warning dolphin on mf_classic write initial fail scene * Removed old image * Changed image on scenes to a new one * New dolphin mafia image * Replaced dolphin mafia image to a new one * Removed DolphinMafia_115x62.png * New check symbol on completed state for detect_reader * Adjusted layout elements position * Removed second switching to popup view in order to achieve control in support callbacks In general now we show generic scene and after that in on_enter callback we can redefine it for particular protocol * CardDetected event now also triggers on_event callback * Now on AuthRequest we throw CardDetected custom event * Added callback for read_on_event * Now we show different screen while reading and unlocking * Fixed missing asstes for some scenes * Update DolphinMafia_119x62.png * Adjusted all the scenes with DolphinMafia image * Scenes with save image adjusted * Removed unnecessary assets DolphinMafia_119x62.png and DolphinSaved_113x58.png * All common dolphins moved to Dolphin folder * Moved DolphinReadingSuccess_59x63.png to Dolphin folder * Set proper led color for detect and read scenes * Added new notification sequence for semi_success results * Use new sequence for semi_success nfc reads * Different events are now throwed depending on read result * Added handling of incomplete event for ultralight cards * Replaced image for iButton scene * Updated API for f18 * Fixed issue with unlock retry sequence * Fix after review * Success notification replaced to semi success in case of incomplete mf classic reading * New text for read scene * New read result sound notification logic for mf classic cards * Change MIFARE name accroding to new requirements * New QR code image for MFKey app * Update nfc_scene_mf_classic_mfkey_complete.c scene according to new UI requirements * Update detect_reader.c and check_big_20x17.png * New nfc save confirm scene added * Implemented new flow for 'Detect Reader button' after partial mf classic read according to new UI * UID for 15693 tags now shown on the new line * Fix nfc unit tests * Revert "Fix nfc unit tests" This reverts commit 685ed6bfad1980e42098a8bbe366de5b8b4cfd09. * Rolled back all Mifare renamings in library files * Revert "Change MIFARE name accroding to new requirements" This reverts commit cfb974dc1f5bff1d46a0483741b2b8f4726cdda3. * Now Mifare word is changed only on the app level without changes to lib level * Filename or "Unsaved + CardType" is now showed for saved cards during emulation * Headers added to Write scenes * Reordered menu items accrding to new spec * Filename will be printed for saved tag in info scene * New info render format for 14443_3a cards * New info render format for 14443_3b cards * New info render format for 14443_4a cards * New info render format for iso15693 cards. Also More_Info scene added to display Memory data * New info render format for slix cards. Also More_Info scene added to display Memory data * Fixed "Mifare" word for desfire cards * Aligned text and replaced dolphin image on emulate scene * Fixed Mifare caption after QA * Realigned emulation scene and fixed replaced Mifare to MIFARE --------- Co-authored-by: あく Co-authored-by: gornekich --- .../helpers/protocol_support/felica/felica.c | 1 + .../iso14443_3a/iso14443_3a.c | 2 + .../iso14443_3a/iso14443_3a_render.c | 10 +++- .../iso14443_3a/iso14443_3a_render.h | 2 + .../iso14443_3b/iso14443_3b.c | 1 + .../iso14443_3b/iso14443_3b_render.c | 6 +- .../iso14443_4a/iso14443_4a.c | 1 + .../iso14443_4a/iso14443_4a_render.c | 5 +- .../iso14443_4b/iso14443_4b.c | 1 + .../protocol_support/iso15693_3/iso15693_3.c | 22 ++++++- .../iso15693_3/iso15693_3_render.c | 56 ++++++++++-------- .../iso15693_3/iso15693_3_render.h | 2 + .../protocol_support/mf_classic/mf_classic.c | 1 + .../protocol_support/mf_desfire/mf_desfire.c | 3 + .../mf_ultralight/mf_ultralight.c | 2 + .../protocol_support/nfc_protocol_support.c | 10 ++-- .../nfc/helpers/protocol_support/slix/slix.c | 21 ++++++- .../protocol_support/slix/slix_render.c | 12 ++-- .../protocol_support/slix/slix_render.h | 1 + .../helpers/protocol_support/st25tb/st25tb.c | 1 + applications/main/nfc/nfc_app.c | 9 +++ applications/main/nfc/nfc_app_i.h | 2 + .../nfc/scenes/nfc_scene_select_protocol.c | 2 + .../icons/NFC/NFC_dolphin_emulation_47x61.png | Bin 1541 -> 0 bytes .../icons/NFC/NFC_dolphin_emulation_51x64.png | Bin 0 -> 1591 bytes 25 files changed, 126 insertions(+), 47 deletions(-) delete mode 100644 assets/icons/NFC/NFC_dolphin_emulation_47x61.png create mode 100644 assets/icons/NFC/NFC_dolphin_emulation_51x64.png diff --git a/applications/main/nfc/helpers/protocol_support/felica/felica.c b/applications/main/nfc/helpers/protocol_support/felica/felica.c index f9c849121..6e7aa2d8a 100644 --- a/applications/main/nfc/helpers/protocol_support/felica/felica.c +++ b/applications/main/nfc/helpers/protocol_support/felica/felica.c @@ -13,6 +13,7 @@ static void nfc_scene_info_on_enter_felica(NfcApp* instance) { const FelicaData* data = nfc_device_get_data(device, NfcProtocolFelica); FuriString* temp_str = furi_string_alloc(); + nfc_append_filename_string_when_present(instance, temp_str); furi_string_cat_printf( temp_str, "\e#%s\n", nfc_device_get_name(device, NfcDeviceNameTypeFull)); nfc_render_felica_info(data, NfcProtocolFormatTypeFull, temp_str); diff --git a/applications/main/nfc/helpers/protocol_support/iso14443_3a/iso14443_3a.c b/applications/main/nfc/helpers/protocol_support/iso14443_3a/iso14443_3a.c index c0d502d03..05db74a4e 100644 --- a/applications/main/nfc/helpers/protocol_support/iso14443_3a/iso14443_3a.c +++ b/applications/main/nfc/helpers/protocol_support/iso14443_3a/iso14443_3a.c @@ -13,6 +13,8 @@ static void nfc_scene_info_on_enter_iso14443_3a(NfcApp* instance) { const Iso14443_3aData* data = nfc_device_get_data(device, NfcProtocolIso14443_3a); FuriString* temp_str = furi_string_alloc(); + nfc_append_filename_string_when_present(instance, temp_str); + furi_string_cat_printf( temp_str, "\e#%s\n", nfc_device_get_name(device, NfcDeviceNameTypeFull)); nfc_render_iso14443_3a_info(data, NfcProtocolFormatTypeFull, temp_str); diff --git a/applications/main/nfc/helpers/protocol_support/iso14443_3a/iso14443_3a_render.c b/applications/main/nfc/helpers/protocol_support/iso14443_3a/iso14443_3a_render.c index 7306f1072..810242fbc 100644 --- a/applications/main/nfc/helpers/protocol_support/iso14443_3a/iso14443_3a_render.c +++ b/applications/main/nfc/helpers/protocol_support/iso14443_3a/iso14443_3a_render.c @@ -6,13 +6,17 @@ void nfc_render_iso14443_3a_format_bytes(FuriString* str, const uint8_t* const d } } +void nfc_render_iso14443_tech_type(const Iso14443_3aData* data, FuriString* str) { + const char iso_type = iso14443_3a_supports_iso14443_4(data) ? '4' : '3'; + furi_string_cat_printf(str, "Tech: ISO 14443-%c (NFC-A)\n", iso_type); +} + void nfc_render_iso14443_3a_info( const Iso14443_3aData* data, NfcProtocolFormatType format_type, FuriString* str) { if(format_type == NfcProtocolFormatTypeFull) { - const char iso_type = iso14443_3a_supports_iso14443_4(data) ? '4' : '3'; - furi_string_cat_printf(str, "ISO 14443-%c (NFC-A)\n", iso_type); + nfc_render_iso14443_tech_type(data, str); } nfc_render_iso14443_3a_brief(data, str); @@ -30,5 +34,5 @@ void nfc_render_iso14443_3a_brief(const Iso14443_3aData* data, FuriString* str) void nfc_render_iso14443_3a_extra(const Iso14443_3aData* data, FuriString* str) { furi_string_cat_printf(str, "\nATQA: %02X %02X ", data->atqa[1], data->atqa[0]); - furi_string_cat_printf(str, "SAK: %02X", data->sak); + furi_string_cat_printf(str, "\nSAK: %02X", data->sak); } diff --git a/applications/main/nfc/helpers/protocol_support/iso14443_3a/iso14443_3a_render.h b/applications/main/nfc/helpers/protocol_support/iso14443_3a/iso14443_3a_render.h index 14b91d221..34e347aa3 100644 --- a/applications/main/nfc/helpers/protocol_support/iso14443_3a/iso14443_3a_render.h +++ b/applications/main/nfc/helpers/protocol_support/iso14443_3a/iso14443_3a_render.h @@ -9,6 +9,8 @@ void nfc_render_iso14443_3a_info( NfcProtocolFormatType format_type, FuriString* str); +void nfc_render_iso14443_tech_type(const Iso14443_3aData* data, FuriString* str); + void nfc_render_iso14443_3a_format_bytes(FuriString* str, const uint8_t* const data, size_t size); void nfc_render_iso14443_3a_brief(const Iso14443_3aData* data, FuriString* str); diff --git a/applications/main/nfc/helpers/protocol_support/iso14443_3b/iso14443_3b.c b/applications/main/nfc/helpers/protocol_support/iso14443_3b/iso14443_3b.c index fee231846..a71a65753 100644 --- a/applications/main/nfc/helpers/protocol_support/iso14443_3b/iso14443_3b.c +++ b/applications/main/nfc/helpers/protocol_support/iso14443_3b/iso14443_3b.c @@ -13,6 +13,7 @@ static void nfc_scene_info_on_enter_iso14443_3b(NfcApp* instance) { const Iso14443_3bData* data = nfc_device_get_data(device, NfcProtocolIso14443_3b); FuriString* temp_str = furi_string_alloc(); + nfc_append_filename_string_when_present(instance, temp_str); furi_string_cat_printf( temp_str, "\e#%s\n", nfc_device_get_name(device, NfcDeviceNameTypeFull)); nfc_render_iso14443_3b_info(data, NfcProtocolFormatTypeFull, temp_str); diff --git a/applications/main/nfc/helpers/protocol_support/iso14443_3b/iso14443_3b_render.c b/applications/main/nfc/helpers/protocol_support/iso14443_3b/iso14443_3b_render.c index 2e81d57a4..ec7efa84f 100644 --- a/applications/main/nfc/helpers/protocol_support/iso14443_3b/iso14443_3b_render.c +++ b/applications/main/nfc/helpers/protocol_support/iso14443_3b/iso14443_3b_render.c @@ -6,7 +6,7 @@ void nfc_render_iso14443_3b_info( FuriString* str) { if(format_type == NfcProtocolFormatTypeFull) { const char iso_type = iso14443_3b_supports_iso14443_4(data) ? '4' : '3'; - furi_string_cat_printf(str, "ISO 14443-%c (NFC-B)\n", iso_type); + furi_string_cat_printf(str, "Tech: ISO 14443-%c (NFC-B)\n", iso_type); } furi_string_cat_printf(str, "UID:"); @@ -20,7 +20,7 @@ void nfc_render_iso14443_3b_info( if(format_type != NfcProtocolFormatTypeFull) return; - furi_string_cat_printf(str, "\n\e#Protocol info\n"); + furi_string_cat_printf(str, "\n::::::::::::::::[Protocol info]:::::::::::::::\n"); if(iso14443_3b_supports_bit_rate(data, Iso14443_3bBitRateBoth106Kbit)) { furi_string_cat(str, "Bit rate PICC <-> PCD:\n 106 kBit/s supported\n"); @@ -68,7 +68,7 @@ void nfc_render_iso14443_3b_info( iso14443_3b_supports_frame_option(data, Iso14443_3bFrameOptionCid) ? "" : "not "; furi_string_cat_printf(str, "CID: %ssupported", cid_support_str); - furi_string_cat_printf(str, "\n\e#Application data\nRaw:"); + furi_string_cat_printf(str, "\n::::::::::::[Application data]::::::::::::\nRaw:"); size_t app_data_size; const uint8_t* app_data = iso14443_3b_get_application_data(data, &app_data_size); diff --git a/applications/main/nfc/helpers/protocol_support/iso14443_4a/iso14443_4a.c b/applications/main/nfc/helpers/protocol_support/iso14443_4a/iso14443_4a.c index 0a3a592e1..5bd38975d 100644 --- a/applications/main/nfc/helpers/protocol_support/iso14443_4a/iso14443_4a.c +++ b/applications/main/nfc/helpers/protocol_support/iso14443_4a/iso14443_4a.c @@ -14,6 +14,7 @@ static void nfc_scene_info_on_enter_iso14443_4a(NfcApp* instance) { const Iso14443_4aData* data = nfc_device_get_data(device, NfcProtocolIso14443_4a); FuriString* temp_str = furi_string_alloc(); + nfc_append_filename_string_when_present(instance, temp_str); furi_string_cat_printf( temp_str, "\e#%s\n", nfc_device_get_name(device, NfcDeviceNameTypeFull)); nfc_render_iso14443_4a_info(data, NfcProtocolFormatTypeFull, temp_str); diff --git a/applications/main/nfc/helpers/protocol_support/iso14443_4a/iso14443_4a_render.c b/applications/main/nfc/helpers/protocol_support/iso14443_4a/iso14443_4a_render.c index a963e744b..4ff07d596 100644 --- a/applications/main/nfc/helpers/protocol_support/iso14443_4a/iso14443_4a_render.c +++ b/applications/main/nfc/helpers/protocol_support/iso14443_4a/iso14443_4a_render.c @@ -14,11 +14,12 @@ void nfc_render_iso14443_4a_info( } void nfc_render_iso14443_4a_brief(const Iso14443_4aData* data, FuriString* str) { + nfc_render_iso14443_tech_type(iso14443_4a_get_base_data(data), str); nfc_render_iso14443_3a_brief(iso14443_4a_get_base_data(data), str); } void nfc_render_iso14443_4a_extra(const Iso14443_4aData* data, FuriString* str) { - furi_string_cat_printf(str, "\n\e#Protocol info\n"); + furi_string_cat_printf(str, "\n::::::::::::::::[Protocol info]:::::::::::::::\n"); if(iso14443_4a_supports_bit_rate(data, Iso14443_4aBitRateBoth106Kbit)) { furi_string_cat(str, "Bit rate PICC <-> PCD:\n 106 kBit/s supported\n"); @@ -72,7 +73,7 @@ void nfc_render_iso14443_4a_extra(const Iso14443_4aData* data, FuriString* str) const uint8_t* hist_bytes = iso14443_4a_get_historical_bytes(data, &hist_bytes_count); if(hist_bytes_count > 0) { - furi_string_cat_printf(str, "\n\e#Historical bytes\nRaw:"); + furi_string_cat_printf(str, "\n:::::::::::::[Historical bytes]:::::::::::::\nRaw:"); for(size_t i = 0; i < hist_bytes_count; ++i) { furi_string_cat_printf(str, " %02X", hist_bytes[i]); diff --git a/applications/main/nfc/helpers/protocol_support/iso14443_4b/iso14443_4b.c b/applications/main/nfc/helpers/protocol_support/iso14443_4b/iso14443_4b.c index a0c70a22e..ee49f2a42 100644 --- a/applications/main/nfc/helpers/protocol_support/iso14443_4b/iso14443_4b.c +++ b/applications/main/nfc/helpers/protocol_support/iso14443_4b/iso14443_4b.c @@ -14,6 +14,7 @@ static void nfc_scene_info_on_enter_iso14443_4b(NfcApp* instance) { const Iso14443_4bData* data = nfc_device_get_data(device, NfcProtocolIso14443_4b); FuriString* temp_str = furi_string_alloc(); + nfc_append_filename_string_when_present(instance, temp_str); furi_string_cat_printf( temp_str, "\e#%s\n", nfc_device_get_name(device, NfcDeviceNameTypeFull)); nfc_render_iso14443_4b_info(data, NfcProtocolFormatTypeFull, temp_str); diff --git a/applications/main/nfc/helpers/protocol_support/iso15693_3/iso15693_3.c b/applications/main/nfc/helpers/protocol_support/iso15693_3/iso15693_3.c index 7f861a032..d645fa3bb 100644 --- a/applications/main/nfc/helpers/protocol_support/iso15693_3/iso15693_3.c +++ b/applications/main/nfc/helpers/protocol_support/iso15693_3/iso15693_3.c @@ -14,16 +14,30 @@ static void nfc_scene_info_on_enter_iso15693_3(NfcApp* instance) { const Iso15693_3Data* data = nfc_device_get_data(device, NfcProtocolIso15693_3); FuriString* temp_str = furi_string_alloc(); + nfc_append_filename_string_when_present(instance, temp_str); furi_string_cat_printf( temp_str, "\e#%s\n", nfc_device_get_name(device, NfcDeviceNameTypeFull)); nfc_render_iso15693_3_info(data, NfcProtocolFormatTypeFull, temp_str); + widget_reset(instance->widget); widget_add_text_scroll_element( instance->widget, 0, 0, 128, 64, furi_string_get_cstr(temp_str)); furi_string_free(temp_str); } +static void nfc_scene_more_info_on_enter_iso15693_3(NfcApp* instance) { + const NfcDevice* device = instance->nfc_device; + const Iso15693_3Data* data = nfc_device_get_data(device, NfcProtocolIso15693_3); + + FuriString* temp_str = furi_string_alloc(); + nfc_render_iso15693_3_system_info(data, temp_str); + + widget_add_text_scroll_element( + instance->widget, 0, 0, 128, 64, furi_string_get_cstr(temp_str)); + furi_string_free(temp_str); +} + static NfcCommand nfc_scene_read_poller_callback_iso15693_3(NfcGenericEvent event, void* context) { furi_assert(event.protocol == NfcProtocolIso15693_3); @@ -104,13 +118,19 @@ static bool nfc_scene_saved_menu_on_event_iso15693_3(NfcApp* instance, uint32_t } const NfcProtocolSupportBase nfc_protocol_support_iso15693_3 = { - .features = NfcProtocolFeatureEmulateFull | NfcProtocolFeatureEditUid, + .features = NfcProtocolFeatureEmulateFull | NfcProtocolFeatureEditUid | + NfcProtocolFeatureMoreInfo, .scene_info = { .on_enter = nfc_scene_info_on_enter_iso15693_3, .on_event = nfc_protocol_support_common_on_event_empty, }, + .scene_more_info = + { + .on_enter = nfc_scene_more_info_on_enter_iso15693_3, + .on_event = nfc_protocol_support_common_on_event_empty, + }, .scene_read = { .on_enter = nfc_scene_read_on_enter_iso15693_3, diff --git a/applications/main/nfc/helpers/protocol_support/iso15693_3/iso15693_3_render.c b/applications/main/nfc/helpers/protocol_support/iso15693_3/iso15693_3_render.c index bb2ab92d3..07b96d701 100644 --- a/applications/main/nfc/helpers/protocol_support/iso15693_3/iso15693_3_render.c +++ b/applications/main/nfc/helpers/protocol_support/iso15693_3/iso15693_3_render.c @@ -36,32 +36,7 @@ void nfc_render_iso15693_3_brief(const Iso15693_3Data* data, FuriString* str) { } } -void nfc_render_iso15693_3_extra(const Iso15693_3Data* data, FuriString* str) { - furi_string_cat(str, "\n\e#General info\n"); - if(data->system_info.flags & ISO15693_3_SYSINFO_FLAG_DSFID) { - furi_string_cat_printf(str, "DSFID: %02X\n", data->system_info.ic_ref); - } - - if(data->system_info.flags & ISO15693_3_SYSINFO_FLAG_AFI) { - furi_string_cat_printf(str, "AFI: %02X\n", data->system_info.afi); - } - - if(data->system_info.flags & ISO15693_3_SYSINFO_FLAG_IC_REF) { - furi_string_cat_printf(str, "IC Reference: %02X\n", data->system_info.ic_ref); - } - - furi_string_cat(str, "\e#Lock bits\n"); - - if(data->system_info.flags & ISO15693_3_SYSINFO_FLAG_DSFID) { - furi_string_cat_printf( - str, "DSFID: %s locked\n", data->settings.lock_bits.dsfid ? "" : "not"); - } - - if(data->system_info.flags & ISO15693_3_SYSINFO_FLAG_AFI) { - furi_string_cat_printf( - str, "AFI: %s locked\n", data->settings.lock_bits.dsfid ? "" : "not"); - } - +void nfc_render_iso15693_3_system_info(const Iso15693_3Data* data, FuriString* str) { if(data->system_info.flags & ISO15693_3_SYSINFO_FLAG_MEMORY) { furi_string_cat(str, "\e#Memory data\n\e*--------------------\n"); @@ -88,5 +63,34 @@ void nfc_render_iso15693_3_extra(const Iso15693_3Data* data, FuriString* str) { "(Data is too big. Showing only the first %u bytes.)", display_block_count * block_size); } + } else { + furi_string_cat(str, "\e#No available data\n"); + } +} + +void nfc_render_iso15693_3_extra(const Iso15693_3Data* data, FuriString* str) { + furi_string_cat(str, "\n::::::::::::::::[General info]:::::::::::::::::\n"); + if(data->system_info.flags & ISO15693_3_SYSINFO_FLAG_DSFID) { + furi_string_cat_printf(str, "DSFID: %02X\n", data->system_info.ic_ref); + } + + if(data->system_info.flags & ISO15693_3_SYSINFO_FLAG_AFI) { + furi_string_cat_printf(str, "AFI: %02X\n", data->system_info.afi); + } + + if(data->system_info.flags & ISO15693_3_SYSINFO_FLAG_IC_REF) { + furi_string_cat_printf(str, "IC Reference: %02X\n", data->system_info.ic_ref); + } + + furi_string_cat(str, ":::::::::::::::::::[Lock bits]::::::::::::::::::::\n"); + + if(data->system_info.flags & ISO15693_3_SYSINFO_FLAG_DSFID) { + furi_string_cat_printf( + str, "DSFID: %s locked\n", data->settings.lock_bits.dsfid ? "" : "not"); + } + + if(data->system_info.flags & ISO15693_3_SYSINFO_FLAG_AFI) { + furi_string_cat_printf( + str, "AFI: %s locked\n", data->settings.lock_bits.dsfid ? "" : "not"); } } diff --git a/applications/main/nfc/helpers/protocol_support/iso15693_3/iso15693_3_render.h b/applications/main/nfc/helpers/protocol_support/iso15693_3/iso15693_3_render.h index d531fd2eb..87100102a 100644 --- a/applications/main/nfc/helpers/protocol_support/iso15693_3/iso15693_3_render.h +++ b/applications/main/nfc/helpers/protocol_support/iso15693_3/iso15693_3_render.h @@ -12,3 +12,5 @@ void nfc_render_iso15693_3_info( void nfc_render_iso15693_3_brief(const Iso15693_3Data* data, FuriString* str); void nfc_render_iso15693_3_extra(const Iso15693_3Data* data, FuriString* str); + +void nfc_render_iso15693_3_system_info(const Iso15693_3Data* data, FuriString* str); diff --git a/applications/main/nfc/helpers/protocol_support/mf_classic/mf_classic.c b/applications/main/nfc/helpers/protocol_support/mf_classic/mf_classic.c index 7feeccf22..6cba77250 100644 --- a/applications/main/nfc/helpers/protocol_support/mf_classic/mf_classic.c +++ b/applications/main/nfc/helpers/protocol_support/mf_classic/mf_classic.c @@ -21,6 +21,7 @@ static void nfc_scene_info_on_enter_mf_classic(NfcApp* instance) { const MfClassicData* data = nfc_device_get_data(device, NfcProtocolMfClassic); FuriString* temp_str = furi_string_alloc(); + nfc_append_filename_string_when_present(instance, temp_str); furi_string_cat_printf( temp_str, "\e#%s\n", nfc_device_get_name(device, NfcDeviceNameTypeFull)); furi_string_replace(temp_str, "Mifare", "MIFARE"); diff --git a/applications/main/nfc/helpers/protocol_support/mf_desfire/mf_desfire.c b/applications/main/nfc/helpers/protocol_support/mf_desfire/mf_desfire.c index bc05c2a4c..ef51d98e0 100644 --- a/applications/main/nfc/helpers/protocol_support/mf_desfire/mf_desfire.c +++ b/applications/main/nfc/helpers/protocol_support/mf_desfire/mf_desfire.c @@ -14,8 +14,10 @@ static void nfc_scene_info_on_enter_mf_desfire(NfcApp* instance) { const MfDesfireData* data = nfc_device_get_data(device, NfcProtocolMfDesfire); FuriString* temp_str = furi_string_alloc(); + nfc_append_filename_string_when_present(instance, temp_str); furi_string_cat_printf( temp_str, "\e#%s\n", nfc_device_get_name(device, NfcDeviceNameTypeFull)); + furi_string_replace(temp_str, "Mifare", "MIFARE"); nfc_render_mf_desfire_info(data, NfcProtocolFormatTypeFull, temp_str); widget_add_text_scroll_element( @@ -56,6 +58,7 @@ static void nfc_scene_read_success_on_enter_mf_desfire(NfcApp* instance) { FuriString* temp_str = furi_string_alloc(); furi_string_cat_printf( temp_str, "\e#%s\n", nfc_device_get_name(device, NfcDeviceNameTypeFull)); + furi_string_replace(temp_str, "Mifare", "MIFARE"); nfc_render_mf_desfire_info(data, NfcProtocolFormatTypeShort, temp_str); widget_add_text_scroll_element( diff --git a/applications/main/nfc/helpers/protocol_support/mf_ultralight/mf_ultralight.c b/applications/main/nfc/helpers/protocol_support/mf_ultralight/mf_ultralight.c index 4a8d4d744..3efa032bc 100644 --- a/applications/main/nfc/helpers/protocol_support/mf_ultralight/mf_ultralight.c +++ b/applications/main/nfc/helpers/protocol_support/mf_ultralight/mf_ultralight.c @@ -20,6 +20,8 @@ static void nfc_scene_info_on_enter_mf_ultralight(NfcApp* instance) { const MfUltralightData* data = nfc_device_get_data(device, NfcProtocolMfUltralight); FuriString* temp_str = furi_string_alloc(); + nfc_append_filename_string_when_present(instance, temp_str); + furi_string_cat_printf( temp_str, "\e#%s\n", nfc_device_get_name(device, NfcDeviceNameTypeFull)); nfc_render_mf_ultralight_info(data, NfcProtocolFormatTypeFull, temp_str); diff --git a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.c b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.c index da75142ba..a770df1e5 100644 --- a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.c +++ b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.c @@ -564,11 +564,11 @@ static void nfc_protocol_support_scene_emulate_on_enter(NfcApp* instance) { FuriString* temp_str = furi_string_alloc(); const NfcProtocol protocol = nfc_device_get_protocol(instance->nfc_device); - widget_add_icon_element(widget, 0, 3, &I_NFC_dolphin_emulation_47x61); + widget_add_icon_element(widget, 0, 3, &I_NFC_dolphin_emulation_51x64); if(nfc_protocol_support_has_feature(protocol, NfcProtocolFeatureEmulateUid)) { widget_add_string_element( - widget, 90, 13, AlignCenter, AlignTop, FontPrimary, "Emulating UID"); + widget, 90, 26, AlignCenter, AlignCenter, FontPrimary, "Emulating UID"); size_t uid_len; const uint8_t* uid = nfc_device_get_uid(instance->nfc_device, &uid_len); @@ -580,7 +580,8 @@ static void nfc_protocol_support_scene_emulate_on_enter(NfcApp* instance) { furi_string_trim(temp_str); } else { - widget_add_string_element(widget, 90, 13, AlignCenter, AlignTop, FontPrimary, "Emulating"); + widget_add_string_element( + widget, 90, 26, AlignCenter, AlignCenter, FontPrimary, "Emulating"); if(!furi_string_empty(instance->file_name)) { furi_string_set(temp_str, instance->file_name); } else { @@ -588,11 +589,12 @@ static void nfc_protocol_support_scene_emulate_on_enter(NfcApp* instance) { temp_str, "Unsaved\n%s", nfc_device_get_name(instance->nfc_device, NfcDeviceNameTypeFull)); + furi_string_replace_str(temp_str, "Mifare", "MIFARE"); } } widget_add_text_box_element( - widget, 56, 28, 71, 25, AlignCenter, AlignTop, furi_string_get_cstr(temp_str), false); + widget, 56, 33, 71, 25, AlignCenter, AlignTop, furi_string_get_cstr(temp_str), false); furi_string_free(temp_str); diff --git a/applications/main/nfc/helpers/protocol_support/slix/slix.c b/applications/main/nfc/helpers/protocol_support/slix/slix.c index ad858a75f..8480f8810 100644 --- a/applications/main/nfc/helpers/protocol_support/slix/slix.c +++ b/applications/main/nfc/helpers/protocol_support/slix/slix.c @@ -14,16 +14,30 @@ static void nfc_scene_info_on_enter_slix(NfcApp* instance) { const SlixData* data = nfc_device_get_data(device, NfcProtocolSlix); FuriString* temp_str = furi_string_alloc(); + nfc_append_filename_string_when_present(instance, temp_str); furi_string_cat_printf( temp_str, "\e#%s\n", nfc_device_get_name(device, NfcDeviceNameTypeFull)); nfc_render_slix_info(data, NfcProtocolFormatTypeFull, temp_str); + widget_reset(instance->widget); widget_add_text_scroll_element( instance->widget, 0, 0, 128, 64, furi_string_get_cstr(temp_str)); furi_string_free(temp_str); } +static void nfc_scene_more_info_on_enter_slix(NfcApp* instance) { + const NfcDevice* device = instance->nfc_device; + const SlixData* data = nfc_device_get_data(device, NfcProtocolSlix); + + FuriString* temp_str = furi_string_alloc(); + nfc_render_iso15693_3_system_info(slix_get_base_data(data), temp_str); + + widget_add_text_scroll_element( + instance->widget, 0, 0, 128, 64, furi_string_get_cstr(temp_str)); + furi_string_free(temp_str); +} + static NfcCommand nfc_scene_read_poller_callback_slix(NfcGenericEvent event, void* context) { furi_assert(event.protocol == NfcProtocolSlix); @@ -101,13 +115,18 @@ static bool nfc_scene_saved_menu_on_event_slix(NfcApp* instance, uint32_t event) } const NfcProtocolSupportBase nfc_protocol_support_slix = { - .features = NfcProtocolFeatureEmulateFull, + .features = NfcProtocolFeatureEmulateFull | NfcProtocolFeatureMoreInfo, .scene_info = { .on_enter = nfc_scene_info_on_enter_slix, .on_event = nfc_protocol_support_common_on_event_empty, }, + .scene_more_info = + { + .on_enter = nfc_scene_more_info_on_enter_slix, + .on_event = nfc_protocol_support_common_on_event_empty, + }, .scene_read = { .on_enter = nfc_scene_read_on_enter_slix, diff --git a/applications/main/nfc/helpers/protocol_support/slix/slix_render.c b/applications/main/nfc/helpers/protocol_support/slix/slix_render.c index 80f953db9..1be460194 100644 --- a/applications/main/nfc/helpers/protocol_support/slix/slix_render.c +++ b/applications/main/nfc/helpers/protocol_support/slix/slix_render.c @@ -1,14 +1,12 @@ #include "slix_render.h" -#include "../iso15693_3/iso15693_3_render.h" - void nfc_render_slix_info(const SlixData* data, NfcProtocolFormatType format_type, FuriString* str) { nfc_render_iso15693_3_brief(slix_get_base_data(data), str); if(format_type != NfcProtocolFormatTypeFull) return; const SlixType slix_type = slix_get_type(data); - furi_string_cat(str, "\n\e#Passwords\n"); + furi_string_cat(str, "\n::::::::::::::::::[Passwords]:::::::::::::::::\n"); static const char* slix_password_names[] = { "Read", @@ -25,7 +23,7 @@ void nfc_render_slix_info(const SlixData* data, NfcProtocolFormatType format_typ } } - furi_string_cat(str, "\e#Lock bits\n"); + furi_string_cat(str, ":::::::::::::::::::[Lock bits]::::::::::::::::::::\n"); if(slix_type_has_features(slix_type, SLIX_TYPE_FEATURE_EAS)) { furi_string_cat_printf( @@ -38,7 +36,7 @@ void nfc_render_slix_info(const SlixData* data, NfcProtocolFormatType format_typ const SlixProtection protection = data->system_info.protection; - furi_string_cat(str, "\e#Page protection\n"); + furi_string_cat(str, "::::::::::::[Page protection]::::::::::::\n"); furi_string_cat_printf(str, "Pointer: H >= %02X\n", protection.pointer); const char* rh = (protection.condition & SLIX_PP_CONDITION_RH) ? "" : "un"; @@ -52,12 +50,12 @@ void nfc_render_slix_info(const SlixData* data, NfcProtocolFormatType format_typ } if(slix_type_has_features(slix_type, SLIX_TYPE_FEATURE_PRIVACY)) { - furi_string_cat(str, "\e#Privacy\n"); + furi_string_cat(str, "::::::::::::::::::::[Privacy]::::::::::::::::::::::\n"); furi_string_cat_printf(str, "Privacy mode: %sabled\n", data->privacy ? "en" : "dis"); } if(slix_type_has_features(slix_type, SLIX_TYPE_FEATURE_SIGNATURE)) { - furi_string_cat(str, "\e#Signature\n"); + furi_string_cat(str, ":::::::::::::::::::[Signature]::::::::::::::::::\n"); for(uint32_t i = 0; i < 4; ++i) { furi_string_cat_printf(str, "%02X ", data->signature[i]); } diff --git a/applications/main/nfc/helpers/protocol_support/slix/slix_render.h b/applications/main/nfc/helpers/protocol_support/slix/slix_render.h index 98ae6dc97..bfc216382 100644 --- a/applications/main/nfc/helpers/protocol_support/slix/slix_render.h +++ b/applications/main/nfc/helpers/protocol_support/slix/slix_render.h @@ -3,5 +3,6 @@ #include #include "../nfc_protocol_support_render_common.h" +#include "../iso15693_3/iso15693_3_render.h" void nfc_render_slix_info(const SlixData* data, NfcProtocolFormatType format_type, FuriString* str); diff --git a/applications/main/nfc/helpers/protocol_support/st25tb/st25tb.c b/applications/main/nfc/helpers/protocol_support/st25tb/st25tb.c index e22af48b3..8f5826dc4 100644 --- a/applications/main/nfc/helpers/protocol_support/st25tb/st25tb.c +++ b/applications/main/nfc/helpers/protocol_support/st25tb/st25tb.c @@ -13,6 +13,7 @@ static void nfc_scene_info_on_enter_st25tb(NfcApp* instance) { const St25tbData* data = nfc_device_get_data(device, NfcProtocolSt25tb); FuriString* temp_str = furi_string_alloc(); + nfc_append_filename_string_when_present(instance, temp_str); furi_string_cat_printf( temp_str, "\e#%s\n", nfc_device_get_name(device, NfcDeviceNameTypeFull)); nfc_render_st25tb_info(data, NfcProtocolFormatTypeFull, temp_str); diff --git a/applications/main/nfc/nfc_app.c b/applications/main/nfc/nfc_app.c index 183f49895..29c407b28 100644 --- a/applications/main/nfc/nfc_app.c +++ b/applications/main/nfc/nfc_app.c @@ -445,6 +445,15 @@ void nfc_app_reset_detected_protocols(NfcApp* instance) { instance->protocols_detected_num = 0; } +void nfc_append_filename_string_when_present(NfcApp* instance, FuriString* string) { + furi_assert(instance); + furi_assert(string); + + if(!furi_string_empty(instance->file_name)) { + furi_string_cat_printf(string, "Name:%s\n", furi_string_get_cstr(instance->file_name)); + } +} + static bool nfc_is_hal_ready() { if(furi_hal_nfc_is_hal_ready() != FuriHalNfcErrorNone) { // No connection to the chip, show an error screen diff --git a/applications/main/nfc/nfc_app_i.h b/applications/main/nfc/nfc_app_i.h index 943d722f8..324ed6a5d 100644 --- a/applications/main/nfc/nfc_app_i.h +++ b/applications/main/nfc/nfc_app_i.h @@ -192,3 +192,5 @@ void nfc_make_app_folder(NfcApp* instance); void nfc_app_set_detected_protocols(NfcApp* instance, const NfcProtocol* types, uint32_t count); void nfc_app_reset_detected_protocols(NfcApp* instance); + +void nfc_append_filename_string_when_present(NfcApp* instance, FuriString* string); diff --git a/applications/main/nfc/scenes/nfc_scene_select_protocol.c b/applications/main/nfc/scenes/nfc_scene_select_protocol.c index 7a5d12521..52b2664ec 100644 --- a/applications/main/nfc/scenes/nfc_scene_select_protocol.c +++ b/applications/main/nfc/scenes/nfc_scene_select_protocol.c @@ -29,6 +29,8 @@ void nfc_scene_select_protocol_on_enter(void* context) { "%s %s", prefix, nfc_device_get_protocol_name(instance->protocols_detected[i])); + + furi_string_replace_str(temp_str, "Mifare", "MIFARE"); submenu_add_item( submenu, furi_string_get_cstr(temp_str), diff --git a/assets/icons/NFC/NFC_dolphin_emulation_47x61.png b/assets/icons/NFC/NFC_dolphin_emulation_47x61.png deleted file mode 100644 index 1783531285bed514517fc0821501e718359dd765..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1541 zcmaJ>eM}Q)7{3ZaLqs>76Ex1V5{uLJ`hl&zQLyxiR4J5Flue0VuU9zXKDZvVFu$in zjZPV|k*WB>lBo=wab{s;0v4vrHgq6yL6|@s=rj(*ZALe`w+OgD#xD2qyib0=-}CW4 z_wKW%tO^gC8wNp8xH$>4fiD6cy*LQG!v-3sLy!oej7F>3XoNYQby0aF1bI7aN-wPT zSzjw@m}hD^wN~8M!%5Suc^yp$% z;LAJvZZ<7U;$}9n&swkday_9{j$6k|(n<`UWz^qKx;6aE!&&NN(Msv}w)zef+FQ1w zPdX)Tbt_@>_j_ z1g4yN$-3nhg@rScIaE?HPo@{A*oop?Lg$pk$HB2)6bR6yfWuxok8z`3y<}7u1$MxV zNZ?V3kgJ!xNGj7}g^esv!dkgfMko{tSgVthPF&?syKrI|tWv0yh!WgdnNo|Y)TmMi zk6jWFkaxOJ8D8>lEl2;>9^cDOUul8V6b|{|}|<3A9_V zkuUT8Phq#ch$9gj>1GRf0_>e@Q6LnD8hH{ISl-UEdE_v6wo9ijB}kYxrRx(fq|eo5E&zRs*rRh@+=LHR*h1V=c1idZ;b1lJeL)dauJXW z64={+?e(||3{b$F7+$cL7=MxhGtYzJp6B{075o?>)?~ZM@Am^U<4XHBa7ryUV+Omo z^NSB}*9I*Vo3i}=cBw9McHOY`cJjO|@#%;8vhG*t42yEl!M7sbnHFlhs_u%cu{BLw zhaGH~)>1i`aAdfxzb-yDYp8axDf-aUrDb=dji35@J+L(5s-gM3W`<^_YQ(SZsXOZ| z+MW^|z2V5B)@@rgjw-Cq(dw%_ar$zq^bB!0SN!Wu`o1~8yPWDctj)SpoN(-94Cmev zzx{^qT>j%Hhc_onj|mkQqEx+SO=({jC(e}(-Mu#TV*2AZM;fP!-l&6{t3IlprLTNH zu7%|!`|gA}!?~;;lRx5Kde1<}Db&?0+p;8mU68o-2TkKA6-h@we_Yd@SR^HGPk zTw<~x)L!ghDi?=TUtd5z8164@t6%c>#|vtn#`%LIpusI7pJ(MpaIQm;*_49SCT!aE E4{AO|uK)l5 diff --git a/assets/icons/NFC/NFC_dolphin_emulation_51x64.png b/assets/icons/NFC/NFC_dolphin_emulation_51x64.png new file mode 100644 index 0000000000000000000000000000000000000000..ad5646d16459cbeba7b19f63b5afb6db21f819f3 GIT binary patch literal 1591 zcmeAS@N?(olHy`uVBq!ia0vp^#z5@A!3-oDPVO&fU|`hGbaoE#baqxKD9TUE%t>Wn zsJOLu;zqB-0U~YtrCpCM4C8q8pg=t^Am{4A9?$45g^1P8lh%4VPrama=gz8H4)^VR zdhhHSCog`m;0MdT#z$8UDtZ(M9nAlA?)<}o2keWJuFq`?Y;#-_y8ETa_0Lb=2pjt_ zI;fus+I`iEp<}YeBXuUbctM8qv&-&gelUM_I3zQHDW`m~4oe{8FLA4@3db5GzXbC> z;xoAFxtr|~qsG^wdPXnDcli#UJLmB~Vm7E=_q^GVeTL&Z?S_?$y?Fz?FHN+1JvS?I ztB&`ryDA+|K5AUPs4)3=;fxg~fwHotjeqPJ#6BM_yF4*F^z^Oh=kp6~v4q}24xJX@vryZ0+8WTx0Eg`4^s_!c;)W@LI)6{QAO z`Gq7`WhYyvDB0U7*i={n4aiL`NmQuF&B-gas<2f8n`;GRgM{^!6u?SKvTc#AbGt^BsFfdXux715BHZ@62OEx#qQ7|$vGS)XV)HkryH8ip^Ftsu@R)7K} zpoK*#X;wilZcyuhJX@uVl9B=|ef{$Ca=mh6z5JqdeM3u2OML?)eIp}XpbFjM%Dj@q z3f;V7Wta&rsl~}fnFS@8`FRQ;6BCp2OG|8(fR2UuBDVl;Y+f-mq<~?jUy)d#Z>VPg z@)b;>uP=V3xw&xF#U(+h2=`(&xHzP;AXPsowK%`DC>a=cY04n03ap%qQWHz^i$e1A zb6^1(kda@KU!0L&pkQRGXQH413OGX}19QDxJtGq%GX)b%(z*Q}aq-dQ%X3O>pW3rIp+Qpv^9+MVV!(DQ-pixeDL_vC72l7DJro zLG`BKc8d{Cz4}1M=!2piDH*_ofN2ZFgr{pD2c9!h^MKi*2$=3;z1JuL^9);nPl)UP z|Nnu^&_kE&fCZSXr;B4q#jQQlIQf_qd0c5+MXOX$CUrGc&h{NFs!LOA8_nM2jgySt!IBFjn)|4o^*9%oVS*X zZ0??}n8{i*U;Ri9Uic<}|l_6;w1%IPg#Ybbe;UU!j&K6%UWKMy2+v zd+;yhlYZ_kTP_hFZXj~h;=H7@?t!?7<+Cq)1TE_njXLwt{|dW`N6ZgNp{aEbcu&4{ z?-7XAk9w%%Qq`HH9p(6A`#fReDXS{?wu*1&4cg@tlrG*P{m)}d-osblSZ)cuotM$v zPwW5H$pSs;NycL(D`2Zwc+K;@CAtDnm{r-UW| Dsjf_~ literal 0 HcmV?d00001 From 9d6f51484a11ae71ab758693e2bc0bcb53ae9384 Mon Sep 17 00:00:00 2001 From: RebornedBrain <138568282+RebornedBrain@users.noreply.github.com> Date: Tue, 6 Feb 2024 21:20:57 +0300 Subject: [PATCH 7/7] [FL-3755] Rework more info scene for Ultralight cards (#3391) * Changed event handler signature. Now we put whole SceneManagerEvent not only custom event. * Changed signature and implementation of common on_event callback * Changes required due to event signature adjustment * Reset widget on exit from more info scene * Enum for more info scene states for ultralight cards * New implementation of more info logic added * Check simplified * Update nfc_protocol_support.c --------- Co-authored-by: gornekich --- .../helpers/protocol_support/felica/felica.c | 4 +- .../iso14443_3a/iso14443_3a.c | 4 +- .../iso14443_3b/iso14443_3b.c | 6 +- .../iso14443_3b/iso14443_3b_i.h | 2 +- .../iso14443_4a/iso14443_4a.c | 4 +- .../iso14443_4b/iso14443_4b.c | 6 +- .../protocol_support/iso15693_3/iso15693_3.c | 4 +- .../protocol_support/mf_classic/mf_classic.c | 35 +++--- .../mf_ultralight/mf_ultralight.c | 106 ++++++++++++++---- .../mf_ultralight/mf_ultralight_render.c | 3 +- .../protocol_support/nfc_protocol_support.c | 26 ++--- .../nfc_protocol_support_base.h | 5 +- .../nfc_protocol_support_gui_common.c | 4 +- .../nfc_protocol_support_gui_common.h | 3 +- .../nfc/helpers/protocol_support/slix/slix.c | 4 +- .../helpers/protocol_support/st25tb/st25tb.c | 4 +- 16 files changed, 143 insertions(+), 77 deletions(-) diff --git a/applications/main/nfc/helpers/protocol_support/felica/felica.c b/applications/main/nfc/helpers/protocol_support/felica/felica.c index 6e7aa2d8a..affa33b86 100644 --- a/applications/main/nfc/helpers/protocol_support/felica/felica.c +++ b/applications/main/nfc/helpers/protocol_support/felica/felica.c @@ -59,8 +59,8 @@ static void nfc_scene_read_success_on_enter_felica(NfcApp* instance) { furi_string_free(temp_str); } -static bool nfc_scene_saved_menu_on_event_felica(NfcApp* instance, uint32_t event) { - if(event == SubmenuIndexCommonEdit) { +static bool nfc_scene_saved_menu_on_event_felica(NfcApp* instance, SceneManagerEvent event) { + if(event.type == SceneManagerEventTypeCustom && event.event == SubmenuIndexCommonEdit) { scene_manager_next_scene(instance->scene_manager, NfcSceneSetUid); return true; } diff --git a/applications/main/nfc/helpers/protocol_support/iso14443_3a/iso14443_3a.c b/applications/main/nfc/helpers/protocol_support/iso14443_3a/iso14443_3a.c index 05db74a4e..99e211301 100644 --- a/applications/main/nfc/helpers/protocol_support/iso14443_3a/iso14443_3a.c +++ b/applications/main/nfc/helpers/protocol_support/iso14443_3a/iso14443_3a.c @@ -97,8 +97,8 @@ static void nfc_scene_emulate_on_enter_iso14443_3a(NfcApp* instance) { instance->listener, nfc_scene_emulate_listener_callback_iso14443_3a, instance); } -static bool nfc_scene_read_menu_on_event_iso14443_3a(NfcApp* instance, uint32_t event) { - if(event == SubmenuIndexCommonEmulate) { +static bool nfc_scene_read_menu_on_event_iso14443_3a(NfcApp* instance, SceneManagerEvent event) { + if(event.type == SceneManagerEventTypeCustom && event.event == SubmenuIndexCommonEmulate) { scene_manager_next_scene(instance->scene_manager, NfcSceneEmulate); return true; } diff --git a/applications/main/nfc/helpers/protocol_support/iso14443_3b/iso14443_3b.c b/applications/main/nfc/helpers/protocol_support/iso14443_3b/iso14443_3b.c index a71a65753..43b541111 100644 --- a/applications/main/nfc/helpers/protocol_support/iso14443_3b/iso14443_3b.c +++ b/applications/main/nfc/helpers/protocol_support/iso14443_3b/iso14443_3b.c @@ -60,8 +60,8 @@ static void nfc_scene_read_success_on_enter_iso14443_3b(NfcApp* instance) { furi_string_free(temp_str); } -bool nfc_scene_saved_menu_on_event_iso14443_3b_common(NfcApp* instance, uint32_t event) { - if(event == SubmenuIndexCommonEdit) { +bool nfc_scene_saved_menu_on_event_iso14443_3b_common(NfcApp* instance, SceneManagerEvent event) { + if(event.type == SceneManagerEventTypeCustom && event.event == SubmenuIndexCommonEdit) { scene_manager_next_scene(instance->scene_manager, NfcSceneSetUid); return true; } @@ -69,7 +69,7 @@ bool nfc_scene_saved_menu_on_event_iso14443_3b_common(NfcApp* instance, uint32_t return false; } -static bool nfc_scene_saved_menu_on_event_iso14443_3b(NfcApp* instance, uint32_t event) { +static bool nfc_scene_saved_menu_on_event_iso14443_3b(NfcApp* instance, SceneManagerEvent event) { return nfc_scene_saved_menu_on_event_iso14443_3b_common(instance, event); } diff --git a/applications/main/nfc/helpers/protocol_support/iso14443_3b/iso14443_3b_i.h b/applications/main/nfc/helpers/protocol_support/iso14443_3b/iso14443_3b_i.h index 53fe6b392..6c7c2a0bc 100644 --- a/applications/main/nfc/helpers/protocol_support/iso14443_3b/iso14443_3b_i.h +++ b/applications/main/nfc/helpers/protocol_support/iso14443_3b/iso14443_3b_i.h @@ -4,4 +4,4 @@ #include "iso14443_3b.h" -bool nfc_scene_saved_menu_on_event_iso14443_3b_common(NfcApp* instance, uint32_t event); +bool nfc_scene_saved_menu_on_event_iso14443_3b_common(NfcApp* instance, SceneManagerEvent event); diff --git a/applications/main/nfc/helpers/protocol_support/iso14443_4a/iso14443_4a.c b/applications/main/nfc/helpers/protocol_support/iso14443_4a/iso14443_4a.c index 5bd38975d..17435ccd4 100644 --- a/applications/main/nfc/helpers/protocol_support/iso14443_4a/iso14443_4a.c +++ b/applications/main/nfc/helpers/protocol_support/iso14443_4a/iso14443_4a.c @@ -100,8 +100,8 @@ static void nfc_scene_emulate_on_enter_iso14443_4a(NfcApp* instance) { instance->listener, nfc_scene_emulate_listener_callback_iso14443_4a, instance); } -static bool nfc_scene_read_menu_on_event_iso14443_4a(NfcApp* instance, uint32_t event) { - if(event == SubmenuIndexCommonEmulate) { +static bool nfc_scene_read_menu_on_event_iso14443_4a(NfcApp* instance, SceneManagerEvent event) { + if(event.type == SceneManagerEventTypeCustom && event.event == SubmenuIndexCommonEmulate) { scene_manager_next_scene(instance->scene_manager, NfcSceneEmulate); return true; } diff --git a/applications/main/nfc/helpers/protocol_support/iso14443_4b/iso14443_4b.c b/applications/main/nfc/helpers/protocol_support/iso14443_4b/iso14443_4b.c index ee49f2a42..8038e0491 100644 --- a/applications/main/nfc/helpers/protocol_support/iso14443_4b/iso14443_4b.c +++ b/applications/main/nfc/helpers/protocol_support/iso14443_4b/iso14443_4b.c @@ -65,8 +65,8 @@ static void nfc_scene_saved_menu_on_enter_iso14443_4b(NfcApp* instance) { UNUSED(instance); } -static bool nfc_scene_read_menu_on_event_iso14443_4b(NfcApp* instance, uint32_t event) { - if(event == SubmenuIndexCommonEmulate) { +static bool nfc_scene_read_menu_on_event_iso14443_4b(NfcApp* instance, SceneManagerEvent event) { + if(event.type == SceneManagerEventTypeCustom && event.event == SubmenuIndexCommonEmulate) { scene_manager_next_scene(instance->scene_manager, NfcSceneEmulate); return true; } @@ -74,7 +74,7 @@ static bool nfc_scene_read_menu_on_event_iso14443_4b(NfcApp* instance, uint32_t return false; } -static bool nfc_scene_saved_menu_on_event_iso14443_4b(NfcApp* instance, uint32_t event) { +static bool nfc_scene_saved_menu_on_event_iso14443_4b(NfcApp* instance, SceneManagerEvent event) { return nfc_scene_saved_menu_on_event_iso14443_3b_common(instance, event); } diff --git a/applications/main/nfc/helpers/protocol_support/iso15693_3/iso15693_3.c b/applications/main/nfc/helpers/protocol_support/iso15693_3/iso15693_3.c index d645fa3bb..7efd102f1 100644 --- a/applications/main/nfc/helpers/protocol_support/iso15693_3/iso15693_3.c +++ b/applications/main/nfc/helpers/protocol_support/iso15693_3/iso15693_3.c @@ -108,8 +108,8 @@ static void nfc_scene_emulate_on_enter_iso15693_3(NfcApp* instance) { instance->listener, nfc_scene_emulate_listener_callback_iso15693_3, instance); } -static bool nfc_scene_saved_menu_on_event_iso15693_3(NfcApp* instance, uint32_t event) { - if(event == SubmenuIndexCommonEdit) { +static bool nfc_scene_saved_menu_on_event_iso15693_3(NfcApp* instance, SceneManagerEvent event) { + if(event.type == SceneManagerEventTypeCustom && event.event == SubmenuIndexCommonEdit) { scene_manager_next_scene(instance->scene_manager, NfcSceneSetUid); return true; } diff --git a/applications/main/nfc/helpers/protocol_support/mf_classic/mf_classic.c b/applications/main/nfc/helpers/protocol_support/mf_classic/mf_classic.c index 6cba77250..8ac4a2c6a 100644 --- a/applications/main/nfc/helpers/protocol_support/mf_classic/mf_classic.c +++ b/applications/main/nfc/helpers/protocol_support/mf_classic/mf_classic.c @@ -100,8 +100,9 @@ static void nfc_scene_read_on_enter_mf_classic(NfcApp* instance) { nfc_poller_start(instance->poller, nfc_scene_read_poller_callback_mf_classic, instance); } -static bool nfc_scene_read_on_event_mf_classic(NfcApp* instance, uint32_t event) { - if(event == NfcCustomEventPollerIncomplete) { +static bool nfc_scene_read_on_event_mf_classic(NfcApp* instance, SceneManagerEvent event) { + if(event.type == SceneManagerEventTypeCustom && + event.event == NfcCustomEventPollerIncomplete) { scene_manager_next_scene(instance->scene_manager, NfcSceneMfClassicDictAttack); } @@ -171,8 +172,8 @@ static void nfc_scene_emulate_on_enter_mf_classic(NfcApp* instance) { nfc_listener_start(instance->listener, NULL, NULL); } -static bool nfc_scene_read_menu_on_event_mf_classic(NfcApp* instance, uint32_t event) { - if(event == SubmenuIndexDetectReader) { +static bool nfc_scene_read_menu_on_event_mf_classic(NfcApp* instance, SceneManagerEvent event) { + if(event.type == SceneManagerEventTypeCustom && event.event == SubmenuIndexDetectReader) { scene_manager_next_scene(instance->scene_manager, NfcSceneSaveConfirm); dolphin_deed(DolphinDeedNfcDetectReader); return true; @@ -181,27 +182,29 @@ static bool nfc_scene_read_menu_on_event_mf_classic(NfcApp* instance, uint32_t e return false; } -static bool nfc_scene_saved_menu_on_event_mf_classic(NfcApp* instance, uint32_t event) { +static bool nfc_scene_saved_menu_on_event_mf_classic(NfcApp* instance, SceneManagerEvent event) { bool consumed = false; - if(event == SubmenuIndexDetectReader) { - scene_manager_next_scene(instance->scene_manager, NfcSceneMfClassicDetectReader); - consumed = true; - } else if(event == SubmenuIndexWrite) { - scene_manager_next_scene(instance->scene_manager, NfcSceneMfClassicWriteInitial); - consumed = true; - } else if(event == SubmenuIndexUpdate) { - scene_manager_next_scene(instance->scene_manager, NfcSceneMfClassicUpdateInitial); - consumed = true; + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == SubmenuIndexDetectReader) { + scene_manager_next_scene(instance->scene_manager, NfcSceneMfClassicDetectReader); + consumed = true; + } else if(event.event == SubmenuIndexWrite) { + scene_manager_next_scene(instance->scene_manager, NfcSceneMfClassicWriteInitial); + consumed = true; + } else if(event.event == SubmenuIndexUpdate) { + scene_manager_next_scene(instance->scene_manager, NfcSceneMfClassicUpdateInitial); + consumed = true; + } } return consumed; } -static bool nfc_scene_save_name_on_event_mf_classic(NfcApp* instance, uint32_t event) { +static bool nfc_scene_save_name_on_event_mf_classic(NfcApp* instance, SceneManagerEvent event) { bool consumed = false; - if(event == NfcCustomEventTextInputDone) { + if(event.type == SceneManagerEventTypeCustom && event.event == NfcCustomEventTextInputDone) { mf_classic_key_cache_save( instance->mfc_key_cache, nfc_device_get_data(instance->nfc_device, NfcProtocolMfClassic)); diff --git a/applications/main/nfc/helpers/protocol_support/mf_ultralight/mf_ultralight.c b/applications/main/nfc/helpers/protocol_support/mf_ultralight/mf_ultralight.c index 3efa032bc..f0a46dd87 100644 --- a/applications/main/nfc/helpers/protocol_support/mf_ultralight/mf_ultralight.c +++ b/applications/main/nfc/helpers/protocol_support/mf_ultralight/mf_ultralight.c @@ -2,6 +2,7 @@ #include "mf_ultralight_render.h" #include +#include #include "nfc/nfc_app_i.h" @@ -15,6 +16,11 @@ enum { SubmenuIndexWrite, }; +enum { + NfcSceneMoreInfoStateASCII, + NfcSceneMoreInfoStateRawData, +}; + static void nfc_scene_info_on_enter_mf_ultralight(NfcApp* instance) { const NfcDevice* device = instance->nfc_device; const MfUltralightData* data = nfc_device_get_data(device, NfcProtocolMfUltralight); @@ -37,11 +43,62 @@ static void nfc_scene_more_info_on_enter_mf_ultralight(NfcApp* instance) { const MfUltralightData* mfu = nfc_device_get_data(device, NfcProtocolMfUltralight); furi_string_reset(instance->text_box_store); - nfc_render_mf_ultralight_dump(mfu, instance->text_box_store); + uint32_t scene_state = + scene_manager_get_scene_state(instance->scene_manager, NfcSceneMoreInfo); - text_box_set_font(instance->text_box, TextBoxFontHex); - text_box_set_text(instance->text_box, furi_string_get_cstr(instance->text_box_store)); - view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewTextBox); + if(scene_state == NfcSceneMoreInfoStateASCII) { + pretty_format_bytes_hex_canonical( + instance->text_box_store, + MF_ULTRALIGHT_PAGE_SIZE, + PRETTY_FORMAT_FONT_MONOSPACE, + (uint8_t*)mfu->page, + mfu->pages_read * MF_ULTRALIGHT_PAGE_SIZE); + + widget_add_text_scroll_element( + instance->widget, 0, 0, 128, 48, furi_string_get_cstr(instance->text_box_store)); + widget_add_button_element( + instance->widget, + GuiButtonTypeRight, + "Raw Data", + nfc_protocol_support_common_widget_callback, + instance); + + widget_add_button_element( + instance->widget, + GuiButtonTypeLeft, + "Info", + nfc_protocol_support_common_widget_callback, + instance); + } else if(scene_state == NfcSceneMoreInfoStateRawData) { + nfc_render_mf_ultralight_dump(mfu, instance->text_box_store); + widget_add_text_scroll_element( + instance->widget, 0, 0, 128, 48, furi_string_get_cstr(instance->text_box_store)); + + widget_add_button_element( + instance->widget, + GuiButtonTypeLeft, + "ASCII", + nfc_protocol_support_common_widget_callback, + instance); + } +} + +static bool nfc_scene_more_info_on_event_mf_ultralight(NfcApp* instance, SceneManagerEvent event) { + bool consumed = false; + + if((event.type == SceneManagerEventTypeCustom && event.event == GuiButtonTypeLeft) || + (event.type == SceneManagerEventTypeBack)) { + scene_manager_set_scene_state( + instance->scene_manager, NfcSceneMoreInfo, NfcSceneMoreInfoStateASCII); + scene_manager_previous_scene(instance->scene_manager); + consumed = true; + } else if(event.type == SceneManagerEventTypeCustom && event.event == GuiButtonTypeRight) { + scene_manager_set_scene_state( + instance->scene_manager, NfcSceneMoreInfo, NfcSceneMoreInfoStateRawData); + scene_manager_next_scene(instance->scene_manager, NfcSceneMoreInfo); + consumed = true; + } + return consumed; } static NfcCommand @@ -134,15 +191,17 @@ static void nfc_scene_read_on_enter_mf_ultralight(NfcApp* instance) { nfc_poller_start(instance->poller, nfc_scene_read_poller_callback_mf_ultralight, instance); } -bool nfc_scene_read_on_event_mf_ultralight(NfcApp* instance, uint32_t event) { - if(event == NfcCustomEventCardDetected) { - scene_manager_set_scene_state( - instance->scene_manager, NfcSceneRead, NfcSceneMfUltralightReadMenuStateCardFound); - nfc_scene_read_setup_view(instance); - } else if((event == NfcCustomEventPollerIncomplete)) { - notification_message(instance->notifications, &sequence_semi_success); - scene_manager_next_scene(instance->scene_manager, NfcSceneReadSuccess); - dolphin_deed(DolphinDeedNfcReadSuccess); +bool nfc_scene_read_on_event_mf_ultralight(NfcApp* instance, SceneManagerEvent event) { + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == NfcCustomEventCardDetected) { + scene_manager_set_scene_state( + instance->scene_manager, NfcSceneRead, NfcSceneMfUltralightReadMenuStateCardFound); + nfc_scene_read_setup_view(instance); + } else if((event.event == NfcCustomEventPollerIncomplete)) { + notification_message(instance->notifications, &sequence_semi_success); + scene_manager_next_scene(instance->scene_manager, NfcSceneReadSuccess); + dolphin_deed(DolphinDeedNfcReadSuccess); + } } return true; } @@ -204,14 +263,17 @@ static void nfc_scene_emulate_on_enter_mf_ultralight(NfcApp* instance) { nfc_listener_start(instance->listener, NULL, NULL); } -static bool - nfc_scene_read_and_saved_menu_on_event_mf_ultralight(NfcApp* instance, uint32_t event) { - if(event == SubmenuIndexUnlock) { - scene_manager_next_scene(instance->scene_manager, NfcSceneMfUltralightUnlockMenu); - return true; - } else if(event == SubmenuIndexWrite) { - scene_manager_next_scene(instance->scene_manager, NfcSceneMfUltralightWrite); - return true; +static bool nfc_scene_read_and_saved_menu_on_event_mf_ultralight( + NfcApp* instance, + SceneManagerEvent event) { + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == SubmenuIndexUnlock) { + scene_manager_next_scene(instance->scene_manager, NfcSceneMfUltralightUnlockMenu); + return true; + } else if(event.event == SubmenuIndexWrite) { + scene_manager_next_scene(instance->scene_manager, NfcSceneMfUltralightWrite); + return true; + } } return false; } @@ -227,7 +289,7 @@ const NfcProtocolSupportBase nfc_protocol_support_mf_ultralight = { .scene_more_info = { .on_enter = nfc_scene_more_info_on_enter_mf_ultralight, - .on_event = nfc_protocol_support_common_on_event_empty, + .on_event = nfc_scene_more_info_on_event_mf_ultralight, }, .scene_read = { diff --git a/applications/main/nfc/helpers/protocol_support/mf_ultralight/mf_ultralight_render.c b/applications/main/nfc/helpers/protocol_support/mf_ultralight/mf_ultralight_render.c index 5296f4807..1bc508adc 100644 --- a/applications/main/nfc/helpers/protocol_support/mf_ultralight/mf_ultralight_render.c +++ b/applications/main/nfc/helpers/protocol_support/mf_ultralight/mf_ultralight_render.c @@ -36,10 +36,11 @@ void nfc_render_mf_ultralight_info( } void nfc_render_mf_ultralight_dump(const MfUltralightData* data, FuriString* str) { + furi_string_cat_printf(str, "\e*"); for(size_t i = 0; i < data->pages_read; i++) { const uint8_t* page_data = data->page[i].data; for(size_t j = 0; j < MF_ULTRALIGHT_PAGE_SIZE; j += 2) { - furi_string_cat_printf(str, "%02X%02X ", page_data[j], page_data[j + 1]); + furi_string_cat_printf(str, " %02X%02X", page_data[j], page_data[j + 1]); } } } diff --git a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.c b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.c index a770df1e5..37a9199df 100644 --- a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.c +++ b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.c @@ -131,16 +131,15 @@ static bool nfc_protocol_support_scene_more_info_on_event(NfcApp* instance, SceneManagerEvent event) { bool consumed = false; - if(event.type == SceneManagerEventTypeCustom) { - const NfcProtocol protocol = nfc_device_get_protocol(instance->nfc_device); - consumed = nfc_protocol_support[protocol]->scene_more_info.on_event(instance, event.event); - } + const NfcProtocol protocol = nfc_device_get_protocol(instance->nfc_device); + consumed = nfc_protocol_support[protocol]->scene_more_info.on_event(instance, event); return consumed; } static void nfc_protocol_support_scene_more_info_on_exit(NfcApp* instance) { text_box_reset(instance->text_box); + widget_reset(instance->widget); furi_string_reset(instance->text_box_store); } @@ -188,8 +187,7 @@ static bool nfc_protocol_support_scene_read_on_event(NfcApp* instance, SceneMana } else { const NfcProtocol protocol = instance->protocols_detected[instance->protocols_detected_selected_idx]; - consumed = - nfc_protocol_support[protocol]->scene_read.on_event(instance, event.event); + consumed = nfc_protocol_support[protocol]->scene_read.on_event(instance, event); } } else if(event.event == NfcCustomEventPollerFailure) { nfc_poller_stop(instance->poller); @@ -202,7 +200,7 @@ static bool nfc_protocol_support_scene_read_on_event(NfcApp* instance, SceneMana } else if(event.event == NfcCustomEventCardDetected) { const NfcProtocol protocol = instance->protocols_detected[instance->protocols_detected_selected_idx]; - consumed = nfc_protocol_support[protocol]->scene_read.on_event(instance, event.event); + consumed = nfc_protocol_support[protocol]->scene_read.on_event(instance, event); } } else if(event.type == SceneManagerEventTypeBack) { nfc_poller_stop(instance->poller); @@ -287,8 +285,7 @@ static bool consumed = true; } else { const NfcProtocol protocol = nfc_device_get_protocol(instance->nfc_device); - consumed = - nfc_protocol_support[protocol]->scene_read_menu.on_event(instance, event.event); + consumed = nfc_protocol_support[protocol]->scene_read_menu.on_event(instance, event); } } else if(event.type == SceneManagerEventTypeBack) { @@ -456,8 +453,7 @@ static bool consumed = true; } else { const NfcProtocol protocol = nfc_device_get_protocol(instance->nfc_device); - consumed = - nfc_protocol_support[protocol]->scene_saved_menu.on_event(instance, event.event); + consumed = nfc_protocol_support[protocol]->scene_saved_menu.on_event(instance, event); } } else if(event.type == SceneManagerEventTypeBack) { @@ -521,9 +517,11 @@ static bool scene_manager_has_previous_scene(instance->scene_manager, NfcSceneSetType) ? DolphinDeedNfcAddSave : DolphinDeedNfcSave); - const NfcProtocol protocol = nfc_device_get_protocol(instance->nfc_device); - consumed = nfc_protocol_support[protocol]->scene_save_name.on_event( - instance, event.event); + + const NfcProtocol protocol = + instance->protocols_detected[instance->protocols_detected_selected_idx]; + consumed = + nfc_protocol_support[protocol]->scene_save_name.on_event(instance, event); } else { consumed = scene_manager_search_and_switch_to_previous_scene( instance->scene_manager, NfcSceneStart); diff --git a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_base.h b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_base.h index 69a6d34d2..eec736ca2 100644 --- a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_base.h +++ b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_base.h @@ -7,6 +7,7 @@ #include #include "../../nfc_app.h" +#include "../../nfc_app_i.h" /** * @brief Scene entry handler. @@ -19,10 +20,10 @@ typedef void (*NfcProtocolSupportOnEnter)(NfcApp* instance); * @brief Scene event handler. * * @param[in,out] instance pointer to the NFC application instance. - * @param[in] event custom event that has occurred. + * @param[in] event scene manager event that has occurred. * @returns true if the event was handled, false otherwise. */ -typedef bool (*NfcProtocolSupportOnEvent)(NfcApp* instance, uint32_t event); +typedef bool (*NfcProtocolSupportOnEvent)(NfcApp* instance, SceneManagerEvent event); /** * @brief Abstract scene interface. diff --git a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_gui_common.c b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_gui_common.c index f3a855125..8c38f8475 100644 --- a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_gui_common.c +++ b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_gui_common.c @@ -35,8 +35,8 @@ void nfc_protocol_support_common_on_enter_empty(NfcApp* instance) { UNUSED(instance); } -bool nfc_protocol_support_common_on_event_empty(NfcApp* instance, uint32_t event) { +bool nfc_protocol_support_common_on_event_empty(NfcApp* instance, SceneManagerEvent event) { UNUSED(instance); UNUSED(event); - return true; + return event.type != SceneManagerEventTypeBack; } diff --git a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_gui_common.h b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_gui_common.h index 40ba40c8e..3230f1a7e 100644 --- a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_gui_common.h +++ b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_gui_common.h @@ -7,6 +7,7 @@ #include #include "nfc/nfc_app.h" +#include "nfc/nfc_app_i.h" /** * @brief Common submenu indices. @@ -82,4 +83,4 @@ void nfc_protocol_support_common_on_enter_empty(NfcApp* instance); * @param[in] event custom event type that has occurred. * @returns always true. */ -bool nfc_protocol_support_common_on_event_empty(NfcApp* instance, uint32_t event); +bool nfc_protocol_support_common_on_event_empty(NfcApp* instance, SceneManagerEvent event); diff --git a/applications/main/nfc/helpers/protocol_support/slix/slix.c b/applications/main/nfc/helpers/protocol_support/slix/slix.c index 8480f8810..35592eaa1 100644 --- a/applications/main/nfc/helpers/protocol_support/slix/slix.c +++ b/applications/main/nfc/helpers/protocol_support/slix/slix.c @@ -105,8 +105,8 @@ static void nfc_scene_emulate_on_enter_slix(NfcApp* instance) { nfc_listener_start(instance->listener, nfc_scene_emulate_listener_callback_slix, instance); } -static bool nfc_scene_saved_menu_on_event_slix(NfcApp* instance, uint32_t event) { - if(event == SubmenuIndexCommonEdit) { +static bool nfc_scene_saved_menu_on_event_slix(NfcApp* instance, SceneManagerEvent event) { + if(event.type == SceneManagerEventTypeCustom && event.event == SubmenuIndexCommonEdit) { scene_manager_next_scene(instance->scene_manager, NfcSceneSetUid); return true; } diff --git a/applications/main/nfc/helpers/protocol_support/st25tb/st25tb.c b/applications/main/nfc/helpers/protocol_support/st25tb/st25tb.c index 8f5826dc4..0305d614c 100644 --- a/applications/main/nfc/helpers/protocol_support/st25tb/st25tb.c +++ b/applications/main/nfc/helpers/protocol_support/st25tb/st25tb.c @@ -61,8 +61,8 @@ static void nfc_scene_read_success_on_enter_st25tb(NfcApp* instance) { furi_string_free(temp_str); } -static bool nfc_scene_saved_menu_on_event_st25tb(NfcApp* instance, uint32_t event) { - if(event == SubmenuIndexCommonEdit) { +static bool nfc_scene_saved_menu_on_event_st25tb(NfcApp* instance, SceneManagerEvent event) { + if(event.type == SceneManagerEventTypeCustom && event.event == SubmenuIndexCommonEdit) { scene_manager_next_scene(instance->scene_manager, NfcSceneSetUid); return true; }