[FL-1919] NFC rework with Flipper File Format (#756)
* nfc: allocate nfc device on heap * nfc: rework save with flipper file format * nfc: rework nfc device load with flipper file * nfc: save AID length and data * nfc: remove file worker usage * nfc: format sources * nfc: rework with flipper file format addons * assets: update EMV resources with flipper file format * nfc: rework EMV resources parsing with new file format * assets: fix EMV AID file format * nfc: fix nfc_device usage Co-authored-by: あく <alleteam@gmail.com>
This commit is contained in:
		
							parent
							
								
									54dc16134d
								
							
						
					
					
						commit
						90d450368c
					
				@ -1,49 +1,80 @@
 | 
				
			|||||||
#include "nfc_emv_parser.h"
 | 
					#include "nfc_emv_parser.h"
 | 
				
			||||||
 | 
					#include <lib/flipper_file/flipper_file.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <file-worker.h>
 | 
					static const char* nfc_resources_header = "Flipper EMV resources";
 | 
				
			||||||
 | 
					static const uint32_t nfc_resources_file_version = 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static bool
 | 
					static bool nfc_emv_parser_search_data(
 | 
				
			||||||
    nfc_emv_parser_get_value(const char* file_path, string_t key, char delimiter, string_t value) {
 | 
					    Storage* storage,
 | 
				
			||||||
    bool found = false;
 | 
					    const char* file_name,
 | 
				
			||||||
    FileWorker* file_worker = file_worker_alloc(true);
 | 
					    string_t key,
 | 
				
			||||||
 | 
					    string_t data) {
 | 
				
			||||||
 | 
					    bool parsed = false;
 | 
				
			||||||
 | 
					    FlipperFile* file = flipper_file_alloc(storage);
 | 
				
			||||||
 | 
					    string_t temp_str;
 | 
				
			||||||
 | 
					    string_init(temp_str);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if(file_worker_open(file_worker, file_path, FSAM_READ, FSOM_OPEN_EXISTING)) {
 | 
					    do {
 | 
				
			||||||
        if(file_worker_get_value_from_key(file_worker, key, delimiter, value)) {
 | 
					        // Open file
 | 
				
			||||||
            found = true;
 | 
					        if(!flipper_file_open_existing(file, file_name)) break;
 | 
				
			||||||
        }
 | 
					        // Read file header and version
 | 
				
			||||||
    }
 | 
					        uint32_t version = 0;
 | 
				
			||||||
 | 
					        if(!flipper_file_read_header(file, temp_str, &version)) break;
 | 
				
			||||||
 | 
					        if(string_cmp_str(temp_str, nfc_resources_header) ||
 | 
				
			||||||
 | 
					           (version != nfc_resources_file_version))
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					        if(!flipper_file_read_string(file, string_get_cstr(key), data)) break;
 | 
				
			||||||
 | 
					        parsed = true;
 | 
				
			||||||
 | 
					    } while(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    file_worker_close(file_worker);
 | 
					    string_clear(temp_str);
 | 
				
			||||||
    file_worker_free(file_worker);
 | 
					    flipper_file_free(file);
 | 
				
			||||||
    return found;
 | 
					    return parsed;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
bool nfc_emv_parser_get_aid_name(uint8_t* aid, uint8_t aid_len, string_t aid_name) {
 | 
					bool nfc_emv_parser_get_aid_name(
 | 
				
			||||||
    bool result = false;
 | 
					    Storage* storage,
 | 
				
			||||||
 | 
					    uint8_t* aid,
 | 
				
			||||||
 | 
					    uint8_t aid_len,
 | 
				
			||||||
 | 
					    string_t aid_name) {
 | 
				
			||||||
 | 
					    furi_assert(storage);
 | 
				
			||||||
 | 
					    bool parsed = false;
 | 
				
			||||||
    string_t key;
 | 
					    string_t key;
 | 
				
			||||||
    string_init(key);
 | 
					    string_init(key);
 | 
				
			||||||
    for(uint8_t i = 0; i < aid_len; i++) {
 | 
					    for(uint8_t i = 0; i < aid_len; i++) {
 | 
				
			||||||
        string_cat_printf(key, "%02X", aid[i]);
 | 
					        string_cat_printf(key, "%02X", aid[i]);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    result = nfc_emv_parser_get_value("/ext/nfc/emv/aid.nfc", key, ' ', aid_name);
 | 
					    if(nfc_emv_parser_search_data(storage, "/ext/nfc/emv/aid.nfc", key, aid_name)) {
 | 
				
			||||||
 | 
					        parsed = true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    string_clear(key);
 | 
					    string_clear(key);
 | 
				
			||||||
    return result;
 | 
					    return parsed;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
bool nfc_emv_parser_get_country_name(uint16_t country_code, string_t country_name) {
 | 
					bool nfc_emv_parser_get_country_name(
 | 
				
			||||||
    bool result = false;
 | 
					    Storage* storage,
 | 
				
			||||||
 | 
					    uint16_t country_code,
 | 
				
			||||||
 | 
					    string_t country_name) {
 | 
				
			||||||
 | 
					    bool parsed = false;
 | 
				
			||||||
    string_t key;
 | 
					    string_t key;
 | 
				
			||||||
    string_init_printf(key, "%04X", country_code);
 | 
					    string_init_printf(key, "%04X", country_code);
 | 
				
			||||||
    result = nfc_emv_parser_get_value("/ext/nfc/emv/country_code.nfc", key, ' ', country_name);
 | 
					    if(nfc_emv_parser_search_data(storage, "/ext/nfc/emv/country_code.nfc", key, country_name)) {
 | 
				
			||||||
 | 
					        parsed = true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    string_clear(key);
 | 
					    string_clear(key);
 | 
				
			||||||
    return result;
 | 
					    return parsed;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
bool nfc_emv_parser_get_currency_name(uint16_t currency_code, string_t currency_name) {
 | 
					bool nfc_emv_parser_get_currency_name(
 | 
				
			||||||
    bool result = false;
 | 
					    Storage* storage,
 | 
				
			||||||
 | 
					    uint16_t currency_code,
 | 
				
			||||||
 | 
					    string_t currency_name) {
 | 
				
			||||||
 | 
					    bool parsed = false;
 | 
				
			||||||
    string_t key;
 | 
					    string_t key;
 | 
				
			||||||
    string_init_printf(key, "%04X", currency_code);
 | 
					    string_init_printf(key, "%04X", currency_code);
 | 
				
			||||||
    result = nfc_emv_parser_get_value("/ext/nfc/emv/currency_code.nfc", key, ' ', currency_name);
 | 
					    if(nfc_emv_parser_search_data(storage, "/ext/nfc/emv/currency_code.nfc", key, currency_name)) {
 | 
				
			||||||
 | 
					        parsed = true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    string_clear(key);
 | 
					    string_clear(key);
 | 
				
			||||||
    return result;
 | 
					    return parsed;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -3,25 +3,39 @@
 | 
				
			|||||||
#include <stdint.h>
 | 
					#include <stdint.h>
 | 
				
			||||||
#include <stdbool.h>
 | 
					#include <stdbool.h>
 | 
				
			||||||
#include <m-string.h>
 | 
					#include <m-string.h>
 | 
				
			||||||
 | 
					#include <storage/storage.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** Get EMV application name by number
 | 
					/** Get EMV application name by number
 | 
				
			||||||
 | 
					 * @param storage Storage instance
 | 
				
			||||||
 * @param aid - AID number array
 | 
					 * @param aid - AID number array
 | 
				
			||||||
 * @param aid_len - AID length
 | 
					 * @param aid_len - AID length
 | 
				
			||||||
 * @param aid_name - string to keep AID name
 | 
					 * @param aid_name - string to keep AID name
 | 
				
			||||||
 * @return - true if AID found, false otherwies
 | 
					 * @return - true if AID found, false otherwies
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
bool nfc_emv_parser_get_aid_name(uint8_t* aid, uint8_t aid_len, string_t aid_name);
 | 
					bool nfc_emv_parser_get_aid_name(
 | 
				
			||||||
 | 
					    Storage* storage,
 | 
				
			||||||
 | 
					    uint8_t* aid,
 | 
				
			||||||
 | 
					    uint8_t aid_len,
 | 
				
			||||||
 | 
					    string_t aid_name);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** Get country name by country code
 | 
					/** Get country name by country code
 | 
				
			||||||
 | 
					 * @param storage Storage instance
 | 
				
			||||||
 * @param country_code - ISO 3166 country code
 | 
					 * @param country_code - ISO 3166 country code
 | 
				
			||||||
 * @param country_name - string to keep country name
 | 
					 * @param country_name - string to keep country name
 | 
				
			||||||
 * @return - true if country found, false otherwies
 | 
					 * @return - true if country found, false otherwies
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
bool nfc_emv_parser_get_country_name(uint16_t country_code, string_t country_name);
 | 
					bool nfc_emv_parser_get_country_name(
 | 
				
			||||||
 | 
					    Storage* storage,
 | 
				
			||||||
 | 
					    uint16_t country_code,
 | 
				
			||||||
 | 
					    string_t country_name);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** Get currency name by currency code
 | 
					/** Get currency name by currency code
 | 
				
			||||||
 | 
					 * @param storage Storage instance
 | 
				
			||||||
 * @param currency_code - ISO 3166 currency code
 | 
					 * @param currency_code - ISO 3166 currency code
 | 
				
			||||||
 * @param currency_name - string to keep currency name
 | 
					 * @param currency_name - string to keep currency name
 | 
				
			||||||
 * @return - true if currency found, false otherwies
 | 
					 * @return - true if currency found, false otherwies
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
bool nfc_emv_parser_get_currency_name(uint16_t currency_code, string_t currency_name);
 | 
					bool nfc_emv_parser_get_currency_name(
 | 
				
			||||||
 | 
					    Storage* storage,
 | 
				
			||||||
 | 
					    uint16_t currency_code,
 | 
				
			||||||
 | 
					    string_t currency_name);
 | 
				
			||||||
 | 
				
			|||||||
@ -31,6 +31,9 @@ Nfc* nfc_alloc() {
 | 
				
			|||||||
    view_dispatcher_set_navigation_event_callback(nfc->view_dispatcher, nfc_back_event_callback);
 | 
					    view_dispatcher_set_navigation_event_callback(nfc->view_dispatcher, nfc_back_event_callback);
 | 
				
			||||||
    view_dispatcher_set_tick_event_callback(nfc->view_dispatcher, nfc_tick_event_callback, 100);
 | 
					    view_dispatcher_set_tick_event_callback(nfc->view_dispatcher, nfc_tick_event_callback, 100);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Nfc device
 | 
				
			||||||
 | 
					    nfc->dev = nfc_device_alloc();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Open GUI record
 | 
					    // Open GUI record
 | 
				
			||||||
    nfc->gui = furi_record_open("gui");
 | 
					    nfc->gui = furi_record_open("gui");
 | 
				
			||||||
    view_dispatcher_attach_to_gui(nfc->view_dispatcher, nfc->gui, ViewDispatcherTypeFullscreen);
 | 
					    view_dispatcher_attach_to_gui(nfc->view_dispatcher, nfc->gui, ViewDispatcherTypeFullscreen);
 | 
				
			||||||
@ -82,6 +85,9 @@ Nfc* nfc_alloc() {
 | 
				
			|||||||
void nfc_free(Nfc* nfc) {
 | 
					void nfc_free(Nfc* nfc) {
 | 
				
			||||||
    furi_assert(nfc);
 | 
					    furi_assert(nfc);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Nfc device
 | 
				
			||||||
 | 
					    nfc_device_free(nfc->dev);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Submenu
 | 
					    // Submenu
 | 
				
			||||||
    view_dispatcher_remove_view(nfc->view_dispatcher, NfcViewMenu);
 | 
					    view_dispatcher_remove_view(nfc->view_dispatcher, NfcViewMenu);
 | 
				
			||||||
    submenu_free(nfc->submenu);
 | 
					    submenu_free(nfc->submenu);
 | 
				
			||||||
@ -154,8 +160,8 @@ int32_t nfc_app(void* p) {
 | 
				
			|||||||
    char* args = p;
 | 
					    char* args = p;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Check argument and run corresponding scene
 | 
					    // Check argument and run corresponding scene
 | 
				
			||||||
    if((*args != '\0') && nfc_device_load(&nfc->dev, p)) {
 | 
					    if((*args != '\0') && nfc_device_load(nfc->dev, p)) {
 | 
				
			||||||
        if(nfc->dev.format == NfcDeviceSaveFormatMifareUl) {
 | 
					        if(nfc->dev->format == NfcDeviceSaveFormatMifareUl) {
 | 
				
			||||||
            scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateMifareUl);
 | 
					            scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateMifareUl);
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
            scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateUid);
 | 
					            scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateUid);
 | 
				
			||||||
 | 
				
			|||||||
@ -1,45 +1,38 @@
 | 
				
			|||||||
#include "nfc_device_i.h"
 | 
					#include "nfc_device.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <file-worker.h>
 | 
					 | 
				
			||||||
#include <lib/toolbox/path.h>
 | 
					#include <lib/toolbox/path.h>
 | 
				
			||||||
#include <lib/toolbox/hex.h>
 | 
					#include <lib/flipper_file/flipper_file.h>
 | 
				
			||||||
 | 
					 | 
				
			||||||
#define NFC_DEVICE_MAX_DATA_LEN 14
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
static const char* nfc_app_folder = "/any/nfc";
 | 
					static const char* nfc_app_folder = "/any/nfc";
 | 
				
			||||||
static const char* nfc_app_extension = ".nfc";
 | 
					static const char* nfc_app_extension = ".nfc";
 | 
				
			||||||
static const char* nfc_app_shadow_extension = ".shd";
 | 
					static const char* nfc_app_shadow_extension = ".shd";
 | 
				
			||||||
 | 
					static const char* nfc_file_header = "Flipper NFC device";
 | 
				
			||||||
 | 
					static const uint32_t nfc_file_version = 2;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static bool nfc_device_read_hex(string_t str, uint8_t* buff, uint16_t len, uint8_t delim_len) {
 | 
					NfcDevice* nfc_device_alloc() {
 | 
				
			||||||
    string_strim(str);
 | 
					    NfcDevice* nfc_dev = furi_alloc(sizeof(NfcDevice));
 | 
				
			||||||
    uint8_t nibble_high = 0;
 | 
					    nfc_dev->storage = furi_record_open("storage");
 | 
				
			||||||
    uint8_t nibble_low = 0;
 | 
					    nfc_dev->dialogs = furi_record_open("dialogs");
 | 
				
			||||||
    bool parsed = true;
 | 
					    return nfc_dev;
 | 
				
			||||||
 | 
					 | 
				
			||||||
    for(uint16_t i = 0; i < len; i++) {
 | 
					 | 
				
			||||||
        if(hex_char_to_hex_nibble(string_get_char(str, 0), &nibble_high) &&
 | 
					 | 
				
			||||||
           hex_char_to_hex_nibble(string_get_char(str, 1), &nibble_low)) {
 | 
					 | 
				
			||||||
            buff[i] = (nibble_high << 4) | nibble_low;
 | 
					 | 
				
			||||||
            string_right(str, delim_len + 2);
 | 
					 | 
				
			||||||
        } else {
 | 
					 | 
				
			||||||
            parsed = false;
 | 
					 | 
				
			||||||
            break;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    return parsed;
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
uint16_t nfc_device_prepare_format_string(NfcDevice* dev, string_t format_string) {
 | 
					void nfc_device_free(NfcDevice* nfc_dev) {
 | 
				
			||||||
 | 
					    furi_assert(nfc_dev);
 | 
				
			||||||
 | 
					    furi_record_close("storage");
 | 
				
			||||||
 | 
					    furi_record_close("dialogs");
 | 
				
			||||||
 | 
					    free(nfc_dev);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void nfc_device_prepare_format_string(NfcDevice* dev, string_t format_string) {
 | 
				
			||||||
    if(dev->format == NfcDeviceSaveFormatUid) {
 | 
					    if(dev->format == NfcDeviceSaveFormatUid) {
 | 
				
			||||||
        string_set_str(format_string, "UID\n");
 | 
					        string_set_str(format_string, "UID");
 | 
				
			||||||
    } else if(dev->format == NfcDeviceSaveFormatBankCard) {
 | 
					    } else if(dev->format == NfcDeviceSaveFormatBankCard) {
 | 
				
			||||||
        string_set_str(format_string, "Bank card\n");
 | 
					        string_set_str(format_string, "Bank card");
 | 
				
			||||||
    } else if(dev->format == NfcDeviceSaveFormatMifareUl) {
 | 
					    } else if(dev->format == NfcDeviceSaveFormatMifareUl) {
 | 
				
			||||||
        string_set_str(format_string, "Mifare Ultralight\n");
 | 
					        string_set_str(format_string, "Mifare Ultralight");
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
        string_set_str(format_string, "Unknown\n");
 | 
					        string_set_str(format_string, "Unknown");
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    return string_size(format_string);
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
bool nfc_device_parse_format_string(NfcDevice* dev, string_t format_string) {
 | 
					bool nfc_device_parse_format_string(NfcDevice* dev, string_t format_string) {
 | 
				
			||||||
@ -59,228 +52,166 @@ bool nfc_device_parse_format_string(NfcDevice* dev, string_t format_string) {
 | 
				
			|||||||
    return false;
 | 
					    return false;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
uint16_t nfc_device_prepare_uid_string(NfcDevice* dev, string_t uid_string) {
 | 
					static bool nfc_device_save_mifare_ul_data(FlipperFile* file, NfcDevice* dev) {
 | 
				
			||||||
    NfcDeviceCommonData* uid_data = &dev->dev_data.nfc_data;
 | 
					    bool saved = false;
 | 
				
			||||||
    string_printf(uid_string, "UID len: %02X UID: ", dev->dev_data.nfc_data.uid_len);
 | 
					 | 
				
			||||||
    for(uint8_t i = 0; i < uid_data->uid_len; i++) {
 | 
					 | 
				
			||||||
        string_cat_printf(uid_string, "%02X ", uid_data->uid[i]);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    string_cat_printf(
 | 
					 | 
				
			||||||
        uid_string,
 | 
					 | 
				
			||||||
        "ATQA: %02X %02X SAK: %02X\n",
 | 
					 | 
				
			||||||
        uid_data->atqa[0],
 | 
					 | 
				
			||||||
        uid_data->atqa[1],
 | 
					 | 
				
			||||||
        uid_data->sak);
 | 
					 | 
				
			||||||
    return string_size(uid_string);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
bool nfc_device_parse_uid_string(NfcDevice* dev, string_t uid_string) {
 | 
					 | 
				
			||||||
    NfcDeviceCommonData* uid_data = &dev->dev_data.nfc_data;
 | 
					 | 
				
			||||||
    bool parsed = false;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    do {
 | 
					 | 
				
			||||||
        // strlen("UID len: ") = 9
 | 
					 | 
				
			||||||
        string_right(uid_string, 9);
 | 
					 | 
				
			||||||
        if(!nfc_device_read_hex(uid_string, &uid_data->uid_len, 1, 1)) {
 | 
					 | 
				
			||||||
            break;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        // strlen("UID: ") = 5
 | 
					 | 
				
			||||||
        string_right(uid_string, 5);
 | 
					 | 
				
			||||||
        if(!nfc_device_read_hex(uid_string, uid_data->uid, uid_data->uid_len, 1)) {
 | 
					 | 
				
			||||||
            break;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        // strlen("ATQA: ") = 6
 | 
					 | 
				
			||||||
        string_right(uid_string, 6);
 | 
					 | 
				
			||||||
        if(!nfc_device_read_hex(uid_string, uid_data->atqa, 2, 1)) {
 | 
					 | 
				
			||||||
            break;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        // strlen("SAK: ") = 5
 | 
					 | 
				
			||||||
        string_right(uid_string, 5);
 | 
					 | 
				
			||||||
        if(!nfc_device_read_hex(uid_string, &uid_data->sak, 1, 1)) {
 | 
					 | 
				
			||||||
            break;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        parsed = true;
 | 
					 | 
				
			||||||
    } while(0);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    return parsed;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
uint16_t nfc_device_prepare_mifare_ul_string(NfcDevice* dev, string_t mifare_ul_string) {
 | 
					 | 
				
			||||||
    MifareUlData* data = &dev->dev_data.mf_ul_data;
 | 
					    MifareUlData* data = &dev->dev_data.mf_ul_data;
 | 
				
			||||||
    string_printf(mifare_ul_string, "Signature:");
 | 
					    string_t temp_str;
 | 
				
			||||||
    for(uint8_t i = 0; i < sizeof(data->signature); i++) {
 | 
					    string_init(temp_str);
 | 
				
			||||||
        string_cat_printf(mifare_ul_string, " %02X", data->signature[i]);
 | 
					
 | 
				
			||||||
    }
 | 
					    // Save Mifare Ultralight specific data
 | 
				
			||||||
    string_cat_printf(mifare_ul_string, "\nVersion:");
 | 
					    do {
 | 
				
			||||||
    uint8_t* version = (uint8_t*)&data->version;
 | 
					        if(!flipper_file_write_comment_cstr(file, "Mifare Ultralight specific data")) break;
 | 
				
			||||||
    for(uint8_t i = 0; i < sizeof(data->version); i++) {
 | 
					        if(!flipper_file_write_hex(file, "Signature", data->signature, sizeof(data->signature)))
 | 
				
			||||||
        string_cat_printf(mifare_ul_string, " %02X", version[i]);
 | 
					            break;
 | 
				
			||||||
    }
 | 
					        if(!flipper_file_write_hex(
 | 
				
			||||||
 | 
					               file, "Mifare version", (uint8_t*)&data->version, sizeof(data->version)))
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					        // Write conters and tearing flags data
 | 
				
			||||||
 | 
					        bool counters_saved = true;
 | 
				
			||||||
        for(uint8_t i = 0; i < 3; i++) {
 | 
					        for(uint8_t i = 0; i < 3; i++) {
 | 
				
			||||||
        string_cat_printf(
 | 
					            string_printf(temp_str, "Counter %d", i);
 | 
				
			||||||
            mifare_ul_string,
 | 
					            if(!flipper_file_write_uint32(file, string_get_cstr(temp_str), &data->counter[i], 1)) {
 | 
				
			||||||
            "\nCounter %d: %lu Tearing flag %d: %02X",
 | 
					                counters_saved = false;
 | 
				
			||||||
            i,
 | 
					                break;
 | 
				
			||||||
            data->counter[i],
 | 
					 | 
				
			||||||
            i,
 | 
					 | 
				
			||||||
            data->tearing[i]);
 | 
					 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
    string_cat_printf(mifare_ul_string, "\nData size: %d\n", data->data_size);
 | 
					            string_printf(temp_str, "Tearing %d", i);
 | 
				
			||||||
 | 
					            if(!flipper_file_write_hex(file, string_get_cstr(temp_str), &data->tearing[i], 1)) {
 | 
				
			||||||
 | 
					                counters_saved = false;
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if(!counters_saved) break;
 | 
				
			||||||
 | 
					        // Write pages data
 | 
				
			||||||
 | 
					        uint32_t pages_total = data->data_size / 4;
 | 
				
			||||||
 | 
					        if(!flipper_file_write_uint32(file, "Pages total", &pages_total, 1)) break;
 | 
				
			||||||
 | 
					        bool pages_saved = true;
 | 
				
			||||||
        for(uint16_t i = 0; i < data->data_size; i += 4) {
 | 
					        for(uint16_t i = 0; i < data->data_size; i += 4) {
 | 
				
			||||||
        string_cat_printf(
 | 
					            string_printf(temp_str, "Page %d", i / 4);
 | 
				
			||||||
            mifare_ul_string,
 | 
					            if(!flipper_file_write_hex(file, string_get_cstr(temp_str), &data->data[i], 4)) {
 | 
				
			||||||
            "%02X %02X %02X %02X\n",
 | 
					                pages_saved = false;
 | 
				
			||||||
            data->data[i],
 | 
					                break;
 | 
				
			||||||
            data->data[i + 1],
 | 
					 | 
				
			||||||
            data->data[i + 2],
 | 
					 | 
				
			||||||
            data->data[i + 3]);
 | 
					 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
    return string_size(mifare_ul_string);
 | 
					        }
 | 
				
			||||||
 | 
					        if(!pages_saved) break;
 | 
				
			||||||
 | 
					        saved = true;
 | 
				
			||||||
 | 
					    } while(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    string_clear(temp_str);
 | 
				
			||||||
 | 
					    return saved;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
bool nfc_device_parse_mifare_ul_string(NfcDevice* dev, string_t mifare_ul_string) {
 | 
					bool nfc_device_load_mifare_ul_data(FlipperFile* file, NfcDevice* dev) {
 | 
				
			||||||
    MifareUlData* data = &dev->dev_data.mf_ul_data;
 | 
					 | 
				
			||||||
    uint16_t tearing_tmp = 0;
 | 
					 | 
				
			||||||
    uint16_t cnt_num = 0;
 | 
					 | 
				
			||||||
    size_t ws = 0;
 | 
					 | 
				
			||||||
    int res = 0;
 | 
					 | 
				
			||||||
    bool parsed = false;
 | 
					    bool parsed = false;
 | 
				
			||||||
 | 
					    MifareUlData* data = &dev->dev_data.mf_ul_data;
 | 
				
			||||||
 | 
					    string_t temp_str;
 | 
				
			||||||
 | 
					    string_init(temp_str);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    do {
 | 
					    do {
 | 
				
			||||||
        // strlen("Signature: ") = 11
 | 
					        // Read signature
 | 
				
			||||||
        string_right(mifare_ul_string, 11);
 | 
					        if(!flipper_file_read_hex(file, "Signature", data->signature, sizeof(data->signature)))
 | 
				
			||||||
        if(!nfc_device_read_hex(mifare_ul_string, data->signature, sizeof(data->signature), 1)) {
 | 
					 | 
				
			||||||
            break;
 | 
					            break;
 | 
				
			||||||
        }
 | 
					        // Read Mifare version
 | 
				
			||||||
        // strlen("Version: ") = 9
 | 
					        if(!flipper_file_read_hex(
 | 
				
			||||||
        string_right(mifare_ul_string, 9);
 | 
					               file, "Mifare version", (uint8_t*)&data->version, sizeof(data->version)))
 | 
				
			||||||
        if(!nfc_device_read_hex(
 | 
					 | 
				
			||||||
               mifare_ul_string, (uint8_t*)&data->version, sizeof(data->version), 1)) {
 | 
					 | 
				
			||||||
            break;
 | 
					            break;
 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        string_strim(mifare_ul_string);
 | 
					 | 
				
			||||||
        // Read counters and tearing flags
 | 
					        // Read counters and tearing flags
 | 
				
			||||||
 | 
					        bool counters_parsed = true;
 | 
				
			||||||
        for(uint8_t i = 0; i < 3; i++) {
 | 
					        for(uint8_t i = 0; i < 3; i++) {
 | 
				
			||||||
            res = sscanf(
 | 
					            string_printf(temp_str, "Counter %d", i);
 | 
				
			||||||
                string_get_cstr(mifare_ul_string),
 | 
					            if(!flipper_file_read_uint32(file, string_get_cstr(temp_str), &data->counter[i], 1)) {
 | 
				
			||||||
                "Counter %hX: %lu Tearing flag %hX: %02hX",
 | 
					                counters_parsed = false;
 | 
				
			||||||
                &cnt_num,
 | 
					 | 
				
			||||||
                &data->counter[i],
 | 
					 | 
				
			||||||
                &cnt_num,
 | 
					 | 
				
			||||||
                &tearing_tmp);
 | 
					 | 
				
			||||||
            if(res != 4) {
 | 
					 | 
				
			||||||
                break;
 | 
					                break;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            data->tearing[i] = tearing_tmp;
 | 
					            string_printf(temp_str, "Tearing %d", i);
 | 
				
			||||||
            ws = string_search_char(mifare_ul_string, '\n');
 | 
					            if(!flipper_file_read_hex(file, string_get_cstr(temp_str), &data->tearing[i], 1)) {
 | 
				
			||||||
            string_right(mifare_ul_string, ws + 1);
 | 
					                counters_parsed = false;
 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        // Read data size
 | 
					 | 
				
			||||||
        res = sscanf(string_get_cstr(mifare_ul_string), "Data size: %hu", &data->data_size);
 | 
					 | 
				
			||||||
        if(res != 1) {
 | 
					 | 
				
			||||||
            break;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        ws = string_search_char(mifare_ul_string, '\n');
 | 
					 | 
				
			||||||
        string_right(mifare_ul_string, ws + 1);
 | 
					 | 
				
			||||||
        // Read data
 | 
					 | 
				
			||||||
        for(uint16_t i = 0; i < data->data_size; i += 4) {
 | 
					 | 
				
			||||||
            if(!nfc_device_read_hex(mifare_ul_string, &data->data[i], 4, 1)) {
 | 
					 | 
				
			||||||
                break;
 | 
					                break;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					        if(!counters_parsed) break;
 | 
				
			||||||
 | 
					        // Read pages
 | 
				
			||||||
 | 
					        uint32_t pages = 0;
 | 
				
			||||||
 | 
					        if(!flipper_file_read_uint32(file, "Pages total", &pages, 1)) break;
 | 
				
			||||||
 | 
					        data->data_size = pages * 4;
 | 
				
			||||||
 | 
					        bool pages_parsed = true;
 | 
				
			||||||
 | 
					        for(uint16_t i = 0; i < pages; i++) {
 | 
				
			||||||
 | 
					            string_printf(temp_str, "Page %d", i);
 | 
				
			||||||
 | 
					            if(!flipper_file_read_hex(file, string_get_cstr(temp_str), &data->data[i * 4], 4)) {
 | 
				
			||||||
 | 
					                pages_parsed = false;
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if(!pages_parsed) break;
 | 
				
			||||||
        parsed = true;
 | 
					        parsed = true;
 | 
				
			||||||
    } while(0);
 | 
					    } while(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    string_clear(temp_str);
 | 
				
			||||||
    return parsed;
 | 
					    return parsed;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
uint16_t nfc_device_prepare_bank_card_string(NfcDevice* dev, string_t bank_card_string) {
 | 
					static bool nfc_device_save_bank_card_data(FlipperFile* file, NfcDevice* dev) {
 | 
				
			||||||
 | 
					    bool saved = false;
 | 
				
			||||||
    NfcEmvData* data = &dev->dev_data.emv_data;
 | 
					    NfcEmvData* data = &dev->dev_data.emv_data;
 | 
				
			||||||
    string_printf(bank_card_string, "AID len: %d, AID:", data->aid_len);
 | 
					    uint32_t data_temp = 0;
 | 
				
			||||||
    for(uint8_t i = 0; i < data->aid_len; i++) {
 | 
					
 | 
				
			||||||
        string_cat_printf(bank_card_string, " %02X", data->aid[i]);
 | 
					    do {
 | 
				
			||||||
    }
 | 
					        // Write Bank card specific data
 | 
				
			||||||
    string_cat_printf(
 | 
					        if(!flipper_file_write_comment_cstr(file, "Bank card specific data")) break;
 | 
				
			||||||
        bank_card_string, "\nName: %s\nNumber len: %d\nNumber:", data->name, data->number_len);
 | 
					        if(!flipper_file_write_hex(file, "AID", data->aid, data->aid_len)) break;
 | 
				
			||||||
    for(uint8_t i = 0; i < data->number_len; i++) {
 | 
					        if(!flipper_file_write_string_cstr(file, "Name", data->name)) break;
 | 
				
			||||||
        string_cat_printf(bank_card_string, " %02X", data->number[i]);
 | 
					        if(!flipper_file_write_hex(file, "Number", data->number, data->number_len)) break;
 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
        if(data->exp_mon) {
 | 
					        if(data->exp_mon) {
 | 
				
			||||||
        string_cat_printf(
 | 
					            uint8_t exp_data[2] = {data->exp_mon, data->exp_year};
 | 
				
			||||||
            bank_card_string, "\nExp date: %02X/%02X", data->exp_mon, data->exp_year);
 | 
					            if(!flipper_file_write_hex(file, "Exp data", exp_data, sizeof(exp_data))) break;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        if(data->country_code) {
 | 
					        if(data->country_code) {
 | 
				
			||||||
        string_cat_printf(bank_card_string, "\nCountry code: %04X", data->country_code);
 | 
					            data_temp = data->country_code;
 | 
				
			||||||
 | 
					            if(!flipper_file_write_uint32(file, "Country code", &data_temp, 1)) break;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        if(data->currency_code) {
 | 
					        if(data->currency_code) {
 | 
				
			||||||
        string_cat_printf(bank_card_string, "\nCurrency code: %04X", data->currency_code);
 | 
					            data_temp = data->currency_code;
 | 
				
			||||||
 | 
					            if(!flipper_file_write_uint32(file, "Currency code", &data_temp, 1)) break;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    return string_size(bank_card_string);
 | 
					        saved = true;
 | 
				
			||||||
 | 
					    } while(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return saved;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
bool nfc_device_parse_bank_card_string(NfcDevice* dev, string_t bank_card_string) {
 | 
					bool nfc_device_load_bank_card_data(FlipperFile* file, NfcDevice* dev) {
 | 
				
			||||||
    NfcEmvData* data = &dev->dev_data.emv_data;
 | 
					 | 
				
			||||||
    bool parsed = false;
 | 
					    bool parsed = false;
 | 
				
			||||||
    int res = 0;
 | 
					    NfcEmvData* data = &dev->dev_data.emv_data;
 | 
				
			||||||
    uint8_t code[2] = {};
 | 
					 | 
				
			||||||
    memset(data, 0, sizeof(NfcEmvData));
 | 
					    memset(data, 0, sizeof(NfcEmvData));
 | 
				
			||||||
 | 
					    uint32_t data_cnt = 0;
 | 
				
			||||||
 | 
					    string_t temp_str;
 | 
				
			||||||
 | 
					    string_init(temp_str);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    do {
 | 
					    do {
 | 
				
			||||||
        res = sscanf(string_get_cstr(bank_card_string), "AID len: %hu", &data->aid_len);
 | 
					        // Load essential data
 | 
				
			||||||
        if(res != 1) {
 | 
					        if(!flipper_file_get_value_count(file, "AID", &data_cnt)) break;
 | 
				
			||||||
            break;
 | 
					        data->aid_len = data_cnt;
 | 
				
			||||||
        }
 | 
					        if(!flipper_file_read_hex(file, "AID", data->aid, data->aid_len)) break;
 | 
				
			||||||
        // strlen("AID len: ") = 9
 | 
					        if(!flipper_file_read_string(file, "Name", temp_str)) break;
 | 
				
			||||||
        string_right(bank_card_string, 9);
 | 
					        strlcpy(data->name, string_get_cstr(temp_str), sizeof(data->name));
 | 
				
			||||||
        size_t ws = string_search_char(bank_card_string, ':');
 | 
					        if(!flipper_file_get_value_count(file, "Number", &data_cnt)) break;
 | 
				
			||||||
        string_right(bank_card_string, ws + 1);
 | 
					        data->number_len = data_cnt;
 | 
				
			||||||
        if(!nfc_device_read_hex(bank_card_string, data->aid, data->aid_len, 1)) {
 | 
					        if(!flipper_file_read_hex(file, "Number", data->number, data->number_len)) break;
 | 
				
			||||||
            break;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        res = sscanf(string_get_cstr(bank_card_string), "Name: %s\n", data->name);
 | 
					 | 
				
			||||||
        if(res != 1) {
 | 
					 | 
				
			||||||
            break;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        ws = string_search_char(bank_card_string, '\n');
 | 
					 | 
				
			||||||
        string_right(bank_card_string, ws + 1);
 | 
					 | 
				
			||||||
        res = sscanf(string_get_cstr(bank_card_string), "Number len: %hhu", &data->number_len);
 | 
					 | 
				
			||||||
        if(res != 1) {
 | 
					 | 
				
			||||||
            break;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        ws = string_search_char(bank_card_string, '\n');
 | 
					 | 
				
			||||||
        string_right(bank_card_string, ws + 1);
 | 
					 | 
				
			||||||
        // strlen("Number: ") = 8
 | 
					 | 
				
			||||||
        string_right(bank_card_string, 8);
 | 
					 | 
				
			||||||
        if(!nfc_device_read_hex(bank_card_string, data->number, data->number_len, 1)) {
 | 
					 | 
				
			||||||
            break;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        parsed = true;
 | 
					        parsed = true;
 | 
				
			||||||
        // Check expiration date presence
 | 
					        // Load optional data
 | 
				
			||||||
        ws = string_search_str(bank_card_string, "Exp date: ");
 | 
					        uint8_t exp_data[2] = {};
 | 
				
			||||||
        if(ws != STRING_FAILURE) {
 | 
					        if(flipper_file_read_hex(file, "Exp data", exp_data, 2)) {
 | 
				
			||||||
            // strlen("Exp date: ") = 10
 | 
					            data->exp_mon = exp_data[0];
 | 
				
			||||||
            string_right(bank_card_string, 10);
 | 
					            data->exp_year = exp_data[1];
 | 
				
			||||||
            nfc_device_read_hex(bank_card_string, &data->exp_mon, 1, 1);
 | 
					 | 
				
			||||||
            nfc_device_read_hex(bank_card_string, &data->exp_year, 1, 1);
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        // Check country code presence
 | 
					        if(flipper_file_read_uint32(file, "Country code", &data_cnt, 1)) {
 | 
				
			||||||
        ws = string_search_str(bank_card_string, "Country code: ");
 | 
					            data->country_code = data_cnt;
 | 
				
			||||||
        if(ws != STRING_FAILURE) {
 | 
					 | 
				
			||||||
            // strlen("Country code: ") = 14
 | 
					 | 
				
			||||||
            string_right(bank_card_string, 14);
 | 
					 | 
				
			||||||
            nfc_device_read_hex(bank_card_string, code, 2, 0);
 | 
					 | 
				
			||||||
            data->country_code = code[0] << 8 | code[1];
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        // Check currency code presence
 | 
					        if(flipper_file_read_uint32(file, "Currency code", &data_cnt, 1)) {
 | 
				
			||||||
        ws = string_search_str(bank_card_string, "Currency code: ");
 | 
					            data->currency_code = data_cnt;
 | 
				
			||||||
        if(ws != STRING_FAILURE) {
 | 
					 | 
				
			||||||
            // strlen("Currency code: ") = 15
 | 
					 | 
				
			||||||
            string_right(bank_card_string, 15);
 | 
					 | 
				
			||||||
            nfc_device_read_hex(bank_card_string, code, 2, 0);
 | 
					 | 
				
			||||||
            data->currency_code = code[0] << 8 | code[1];
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    } while(0);
 | 
					    } while(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    string_clear(temp_str);
 | 
				
			||||||
    return parsed;
 | 
					    return parsed;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -297,58 +228,49 @@ static bool nfc_device_save_file(
 | 
				
			|||||||
    const char* extension) {
 | 
					    const char* extension) {
 | 
				
			||||||
    furi_assert(dev);
 | 
					    furi_assert(dev);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    FileWorker* file_worker = file_worker_alloc(false);
 | 
					    bool saved = false;
 | 
				
			||||||
    string_t dev_file_name;
 | 
					    FlipperFile* file = flipper_file_alloc(dev->storage);
 | 
				
			||||||
    string_init(dev_file_name);
 | 
					    NfcDeviceCommonData* data = &dev->dev_data.nfc_data;
 | 
				
			||||||
    string_t temp_str;
 | 
					    string_t temp_str;
 | 
				
			||||||
    string_init(temp_str);
 | 
					    string_init(temp_str);
 | 
				
			||||||
    uint16_t string_len = 0;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    do {
 | 
					    do {
 | 
				
			||||||
        // Create nfc directory if necessary
 | 
					        // Create nfc directory if necessary
 | 
				
			||||||
        if(!file_worker_mkdir(file_worker, nfc_app_folder)) {
 | 
					        if(!storage_simply_mkdir(dev->storage, nfc_app_folder)) break;
 | 
				
			||||||
            break;
 | 
					 | 
				
			||||||
        };
 | 
					 | 
				
			||||||
        // First remove nfc device file if it was saved
 | 
					        // First remove nfc device file if it was saved
 | 
				
			||||||
        string_printf(dev_file_name, "%s/%s%s", folder, dev_name, extension);
 | 
					        string_printf(temp_str, "%s/%s%s", folder, dev_name, extension);
 | 
				
			||||||
        if(!file_worker_remove(file_worker, string_get_cstr(dev_file_name))) {
 | 
					 | 
				
			||||||
            break;
 | 
					 | 
				
			||||||
        };
 | 
					 | 
				
			||||||
        // Open file
 | 
					        // Open file
 | 
				
			||||||
        if(!file_worker_open(
 | 
					        if(!flipper_file_open_always(file, string_get_cstr(temp_str))) break;
 | 
				
			||||||
               file_worker, string_get_cstr(dev_file_name), FSAM_WRITE, FSOM_CREATE_ALWAYS)) {
 | 
					        // Write header
 | 
				
			||||||
 | 
					        if(!flipper_file_write_header_cstr(file, nfc_file_header, nfc_file_version)) break;
 | 
				
			||||||
 | 
					        // Write nfc device type
 | 
				
			||||||
 | 
					        if(!flipper_file_write_comment_cstr(
 | 
				
			||||||
 | 
					               file, "Nfc device type can be UID, Mifare Ultralight, Bank card"))
 | 
				
			||||||
            break;
 | 
					            break;
 | 
				
			||||||
        }
 | 
					        nfc_device_prepare_format_string(dev, temp_str);
 | 
				
			||||||
        // Prepare and write format name on 1st line
 | 
					        if(!flipper_file_write_string(file, "Device type", temp_str)) break;
 | 
				
			||||||
        string_len = nfc_device_prepare_format_string(dev, temp_str);
 | 
					        // Write UID, ATQA, SAK
 | 
				
			||||||
        if(!file_worker_write(file_worker, string_get_cstr(temp_str), string_len)) {
 | 
					        if(!flipper_file_write_comment_cstr(file, "UID, ATQA and SAK are common for all formats"))
 | 
				
			||||||
            break;
 | 
					            break;
 | 
				
			||||||
        }
 | 
					        if(!flipper_file_write_hex(file, "UID", data->uid, data->uid_len)) break;
 | 
				
			||||||
        // Prepare and write UID data on 2nd line
 | 
					        if(!flipper_file_write_hex(file, "ATQA", data->atqa, 2)) break;
 | 
				
			||||||
        string_len = nfc_device_prepare_uid_string(dev, temp_str);
 | 
					        if(!flipper_file_write_hex(file, "SAK", &data->sak, 1)) break;
 | 
				
			||||||
        if(!file_worker_write(file_worker, string_get_cstr(temp_str), string_len)) {
 | 
					 | 
				
			||||||
            break;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        // Save more data if necessary
 | 
					        // Save more data if necessary
 | 
				
			||||||
        if(dev->format == NfcDeviceSaveFormatMifareUl) {
 | 
					        if(dev->format == NfcDeviceSaveFormatMifareUl) {
 | 
				
			||||||
            string_len = nfc_device_prepare_mifare_ul_string(dev, temp_str);
 | 
					            if(!nfc_device_save_mifare_ul_data(file, dev)) break;
 | 
				
			||||||
            if(!file_worker_write(file_worker, string_get_cstr(temp_str), string_len)) {
 | 
					 | 
				
			||||||
                break;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        } else if(dev->format == NfcDeviceSaveFormatBankCard) {
 | 
					        } else if(dev->format == NfcDeviceSaveFormatBankCard) {
 | 
				
			||||||
            string_len = nfc_device_prepare_bank_card_string(dev, temp_str);
 | 
					            if(!nfc_device_save_bank_card_data(file, dev)) break;
 | 
				
			||||||
            if(!file_worker_write(file_worker, string_get_cstr(temp_str), string_len)) {
 | 
					 | 
				
			||||||
                break;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					        saved = true;
 | 
				
			||||||
    } while(0);
 | 
					    } while(0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if(!saved) {
 | 
				
			||||||
 | 
					        dialog_message_show_storage_error(dev->dialogs, "Can not save\nkey file");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    string_clear(temp_str);
 | 
					    string_clear(temp_str);
 | 
				
			||||||
    string_clear(dev_file_name);
 | 
					    flipper_file_close(file);
 | 
				
			||||||
    file_worker_close(file_worker);
 | 
					    flipper_file_free(file);
 | 
				
			||||||
    file_worker_free(file_worker);
 | 
					    return saved;
 | 
				
			||||||
 | 
					 | 
				
			||||||
    return true;
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
bool nfc_device_save(NfcDevice* dev, const char* dev_name) {
 | 
					bool nfc_device_save(NfcDevice* dev, const char* dev_name) {
 | 
				
			||||||
@ -360,73 +282,64 @@ bool nfc_device_save_shadow(NfcDevice* dev, const char* dev_name) {
 | 
				
			|||||||
    return nfc_device_save_file(dev, dev_name, nfc_app_folder, nfc_app_shadow_extension);
 | 
					    return nfc_device_save_file(dev, dev_name, nfc_app_folder, nfc_app_shadow_extension);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static bool nfc_device_load_data(FileWorker* file_worker, string_t path, NfcDevice* dev) {
 | 
					static bool nfc_device_load_data(NfcDevice* dev, string_t path) {
 | 
				
			||||||
    string_t temp_string;
 | 
					 | 
				
			||||||
    string_init(temp_string);
 | 
					 | 
				
			||||||
    bool parsed = false;
 | 
					    bool parsed = false;
 | 
				
			||||||
 | 
					    FlipperFile* file = flipper_file_alloc(dev->storage);
 | 
				
			||||||
 | 
					    NfcDeviceCommonData* data = &dev->dev_data.nfc_data;
 | 
				
			||||||
 | 
					    uint32_t data_cnt = 0;
 | 
				
			||||||
 | 
					    string_t temp_str;
 | 
				
			||||||
 | 
					    string_init(temp_str);
 | 
				
			||||||
 | 
					    bool depricated_version = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    do {
 | 
					    do {
 | 
				
			||||||
        // Check existance of shadow file
 | 
					        // Check existance of shadow file
 | 
				
			||||||
        size_t ext_start = string_search_str(path, nfc_app_extension);
 | 
					        size_t ext_start = string_search_str(path, nfc_app_extension);
 | 
				
			||||||
        string_set_n(temp_string, path, 0, ext_start);
 | 
					        string_set_n(temp_str, path, 0, ext_start);
 | 
				
			||||||
        string_cat_printf(temp_string, "%s", nfc_app_shadow_extension);
 | 
					        string_cat_printf(temp_str, "%s", nfc_app_shadow_extension);
 | 
				
			||||||
        if(!file_worker_is_file_exist(
 | 
					        dev->shadow_file_exist =
 | 
				
			||||||
               file_worker, string_get_cstr(temp_string), &dev->shadow_file_exist)) {
 | 
					            storage_common_stat(dev->storage, string_get_cstr(temp_str), NULL) == FSE_OK;
 | 
				
			||||||
            break;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        // Open shadow file if it exists. If not - open original
 | 
					        // Open shadow file if it exists. If not - open original
 | 
				
			||||||
        if(dev->shadow_file_exist) {
 | 
					        if(dev->shadow_file_exist) {
 | 
				
			||||||
            if(!file_worker_open(
 | 
					            if(!flipper_file_open_existing(file, string_get_cstr(temp_str))) break;
 | 
				
			||||||
                   file_worker, string_get_cstr(temp_string), FSAM_READ, FSOM_OPEN_EXISTING)) {
 | 
					 | 
				
			||||||
                break;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
            if(!file_worker_open(
 | 
					            if(!flipper_file_open_existing(file, string_get_cstr(path))) break;
 | 
				
			||||||
                   file_worker, string_get_cstr(path), FSAM_READ, FSOM_OPEN_EXISTING)) {
 | 
					        }
 | 
				
			||||||
                break;
 | 
					        // Read and verify file header
 | 
				
			||||||
            }
 | 
					        uint32_t version = 0;
 | 
				
			||||||
        }
 | 
					        if(!flipper_file_read_header(file, temp_str, &version)) break;
 | 
				
			||||||
 | 
					        if(string_cmp_str(temp_str, nfc_file_header) || (version != nfc_file_version)) {
 | 
				
			||||||
        // Read and parse format from 1st line
 | 
					            depricated_version = true;
 | 
				
			||||||
        if(!file_worker_read_until(file_worker, temp_string, '\n')) {
 | 
					 | 
				
			||||||
            break;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        if(!nfc_device_parse_format_string(dev, temp_string)) {
 | 
					 | 
				
			||||||
            break;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        // Read and parse UID data from 2nd line
 | 
					 | 
				
			||||||
        if(!file_worker_read_until(file_worker, temp_string, '\n')) {
 | 
					 | 
				
			||||||
            break;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        if(!nfc_device_parse_uid_string(dev, temp_string)) {
 | 
					 | 
				
			||||||
            break;
 | 
					            break;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					        // Read Nfc device type
 | 
				
			||||||
 | 
					        if(!flipper_file_read_string(file, "Device type", temp_str)) break;
 | 
				
			||||||
 | 
					        if(!nfc_device_parse_format_string(dev, temp_str)) break;
 | 
				
			||||||
 | 
					        // Read and parse UID, ATQA and SAK
 | 
				
			||||||
 | 
					        if(!flipper_file_get_value_count(file, "UID", &data_cnt)) break;
 | 
				
			||||||
 | 
					        data->uid_len = data_cnt;
 | 
				
			||||||
 | 
					        if(!flipper_file_read_hex(file, "UID", data->uid, data->uid_len)) break;
 | 
				
			||||||
 | 
					        if(!flipper_file_read_hex(file, "ATQA", data->atqa, 2)) break;
 | 
				
			||||||
 | 
					        if(!flipper_file_read_hex(file, "SAK", &data->sak, 1)) break;
 | 
				
			||||||
        // Parse other data
 | 
					        // Parse other data
 | 
				
			||||||
        if(dev->format == NfcDeviceSaveFormatMifareUl) {
 | 
					        if(dev->format == NfcDeviceSaveFormatMifareUl) {
 | 
				
			||||||
            // Read until EOF
 | 
					            if(!nfc_device_load_mifare_ul_data(file, dev)) break;
 | 
				
			||||||
            if(!file_worker_read_until(file_worker, temp_string, 0x05)) {
 | 
					 | 
				
			||||||
                break;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            if(!nfc_device_parse_mifare_ul_string(dev, temp_string)) {
 | 
					 | 
				
			||||||
                break;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        } else if(dev->format == NfcDeviceSaveFormatBankCard) {
 | 
					        } else if(dev->format == NfcDeviceSaveFormatBankCard) {
 | 
				
			||||||
            // Read until EOF
 | 
					            if(!nfc_device_load_bank_card_data(file, dev)) break;
 | 
				
			||||||
            if(!file_worker_read_until(file_worker, temp_string, 0x05)) {
 | 
					 | 
				
			||||||
                break;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            if(!nfc_device_parse_bank_card_string(dev, temp_string)) {
 | 
					 | 
				
			||||||
                break;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        parsed = true;
 | 
					        parsed = true;
 | 
				
			||||||
    } while(0);
 | 
					    } while(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if(!parsed) {
 | 
					    if(!parsed) {
 | 
				
			||||||
        file_worker_show_error(file_worker, "Can not parse\nfile");
 | 
					        if(depricated_version) {
 | 
				
			||||||
 | 
					            dialog_message_show_storage_error(dev->dialogs, "File format depricated");
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            dialog_message_show_storage_error(dev->dialogs, "Can not parse\nfile");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    string_clear(temp_string);
 | 
					    string_clear(temp_str);
 | 
				
			||||||
 | 
					    flipper_file_close(file);
 | 
				
			||||||
 | 
					    flipper_file_free(file);
 | 
				
			||||||
    return parsed;
 | 
					    return parsed;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -434,19 +347,16 @@ bool nfc_device_load(NfcDevice* dev, const char* file_path) {
 | 
				
			|||||||
    furi_assert(dev);
 | 
					    furi_assert(dev);
 | 
				
			||||||
    furi_assert(file_path);
 | 
					    furi_assert(file_path);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    FileWorker* file_worker = file_worker_alloc(false);
 | 
					 | 
				
			||||||
    // Load device data
 | 
					    // Load device data
 | 
				
			||||||
    string_t path;
 | 
					    string_t path;
 | 
				
			||||||
    string_init_set_str(path, file_path);
 | 
					    string_init_set_str(path, file_path);
 | 
				
			||||||
    bool dev_load = nfc_device_load_data(file_worker, path, dev);
 | 
					    bool dev_load = nfc_device_load_data(dev, path);
 | 
				
			||||||
    if(dev_load) {
 | 
					    if(dev_load) {
 | 
				
			||||||
        // Set device name
 | 
					        // Set device name
 | 
				
			||||||
        path_extract_filename_no_ext(file_path, path);
 | 
					        path_extract_filename_no_ext(file_path, path);
 | 
				
			||||||
        nfc_device_set_name(dev, string_get_cstr(path));
 | 
					        nfc_device_set_name(dev, string_get_cstr(path));
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    string_clear(path);
 | 
					    string_clear(path);
 | 
				
			||||||
    file_worker_close(file_worker);
 | 
					 | 
				
			||||||
    file_worker_free(file_worker);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return dev_load;
 | 
					    return dev_load;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -454,10 +364,9 @@ bool nfc_device_load(NfcDevice* dev, const char* file_path) {
 | 
				
			|||||||
bool nfc_file_select(NfcDevice* dev) {
 | 
					bool nfc_file_select(NfcDevice* dev) {
 | 
				
			||||||
    furi_assert(dev);
 | 
					    furi_assert(dev);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    FileWorker* file_worker = file_worker_alloc(false);
 | 
					 | 
				
			||||||
    // Input events and views are managed by file_select
 | 
					    // Input events and views are managed by file_select
 | 
				
			||||||
    bool res = file_worker_file_select(
 | 
					    bool res = dialog_file_select_show(
 | 
				
			||||||
        file_worker,
 | 
					        dev->dialogs,
 | 
				
			||||||
        nfc_app_folder,
 | 
					        nfc_app_folder,
 | 
				
			||||||
        nfc_app_extension,
 | 
					        nfc_app_extension,
 | 
				
			||||||
        dev->file_name,
 | 
					        dev->file_name,
 | 
				
			||||||
@ -465,18 +374,14 @@ bool nfc_file_select(NfcDevice* dev) {
 | 
				
			|||||||
        dev->dev_name);
 | 
					        dev->dev_name);
 | 
				
			||||||
    if(res) {
 | 
					    if(res) {
 | 
				
			||||||
        string_t dev_str;
 | 
					        string_t dev_str;
 | 
				
			||||||
 | 
					 | 
				
			||||||
        // Get key file path
 | 
					        // Get key file path
 | 
				
			||||||
        string_init_printf(dev_str, "%s/%s%s", nfc_app_folder, dev->file_name, nfc_app_extension);
 | 
					        string_init_printf(dev_str, "%s/%s%s", nfc_app_folder, dev->file_name, nfc_app_extension);
 | 
				
			||||||
 | 
					        res = nfc_device_load_data(dev, dev_str);
 | 
				
			||||||
        res = nfc_device_load_data(file_worker, dev_str, dev);
 | 
					 | 
				
			||||||
        if(res) {
 | 
					        if(res) {
 | 
				
			||||||
            nfc_device_set_name(dev, dev->file_name);
 | 
					            nfc_device_set_name(dev, dev->file_name);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        string_clear(dev_str);
 | 
					        string_clear(dev_str);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    file_worker_close(file_worker);
 | 
					 | 
				
			||||||
    file_worker_free(file_worker);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return res;
 | 
					    return res;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -491,61 +396,48 @@ void nfc_device_clear(NfcDevice* dev) {
 | 
				
			|||||||
bool nfc_device_delete(NfcDevice* dev) {
 | 
					bool nfc_device_delete(NfcDevice* dev) {
 | 
				
			||||||
    furi_assert(dev);
 | 
					    furi_assert(dev);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    bool result = true;
 | 
					    bool deleted = false;
 | 
				
			||||||
    FileWorker* file_worker = file_worker_alloc(false);
 | 
					 | 
				
			||||||
    string_t file_path;
 | 
					    string_t file_path;
 | 
				
			||||||
 | 
					    string_init(file_path);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    do {
 | 
					    do {
 | 
				
			||||||
        // Delete original file
 | 
					        // Delete original file
 | 
				
			||||||
        string_init_printf(file_path, "%s/%s%s", nfc_app_folder, dev->dev_name, nfc_app_extension);
 | 
					        string_init_printf(file_path, "%s/%s%s", nfc_app_folder, dev->dev_name, nfc_app_extension);
 | 
				
			||||||
        if(!file_worker_remove(file_worker, string_get_cstr(file_path))) {
 | 
					        if(!storage_simply_remove(dev->storage, string_get_cstr(file_path))) break;
 | 
				
			||||||
            result = false;
 | 
					 | 
				
			||||||
            break;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        // Delete shadow file if it exists
 | 
					        // Delete shadow file if it exists
 | 
				
			||||||
        if(dev->shadow_file_exist) {
 | 
					        if(dev->shadow_file_exist) {
 | 
				
			||||||
            string_clean(file_path);
 | 
					 | 
				
			||||||
            string_printf(
 | 
					            string_printf(
 | 
				
			||||||
                file_path, "%s/%s%s", nfc_app_folder, dev->dev_name, nfc_app_shadow_extension);
 | 
					                file_path, "%s/%s%s", nfc_app_folder, dev->dev_name, nfc_app_shadow_extension);
 | 
				
			||||||
            if(!file_worker_remove(file_worker, string_get_cstr(file_path))) {
 | 
					            if(!storage_simply_remove(dev->storage, string_get_cstr(file_path))) break;
 | 
				
			||||||
                result = false;
 | 
					 | 
				
			||||||
                break;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					        deleted = true;
 | 
				
			||||||
    } while(0);
 | 
					    } while(0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if(!deleted) {
 | 
				
			||||||
 | 
					        dialog_message_show_storage_error(dev->dialogs, "Can not remove file");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    string_clear(file_path);
 | 
					    string_clear(file_path);
 | 
				
			||||||
    file_worker_close(file_worker);
 | 
					    return deleted;
 | 
				
			||||||
    file_worker_free(file_worker);
 | 
					 | 
				
			||||||
    return result;
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
bool nfc_device_restore(NfcDevice* dev) {
 | 
					bool nfc_device_restore(NfcDevice* dev) {
 | 
				
			||||||
    furi_assert(dev);
 | 
					    furi_assert(dev);
 | 
				
			||||||
    furi_assert(dev->shadow_file_exist);
 | 
					    furi_assert(dev->shadow_file_exist);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    bool result = true;
 | 
					    bool restored = false;
 | 
				
			||||||
    FileWorker* file_worker = file_worker_alloc(false);
 | 
					 | 
				
			||||||
    string_t path;
 | 
					    string_t path;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    do {
 | 
					    do {
 | 
				
			||||||
        string_init_printf(
 | 
					        string_init_printf(
 | 
				
			||||||
            path, "%s/%s%s", nfc_app_folder, dev->dev_name, nfc_app_shadow_extension);
 | 
					            path, "%s/%s%s", nfc_app_folder, dev->dev_name, nfc_app_shadow_extension);
 | 
				
			||||||
        if(!file_worker_remove(file_worker, string_get_cstr(path))) {
 | 
					        if(!storage_simply_remove(dev->storage, string_get_cstr(path))) break;
 | 
				
			||||||
            result = false;
 | 
					 | 
				
			||||||
            break;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        dev->shadow_file_exist = false;
 | 
					        dev->shadow_file_exist = false;
 | 
				
			||||||
        string_clean(path);
 | 
					 | 
				
			||||||
        string_printf(path, "%s/%s%s", nfc_app_folder, dev->dev_name, nfc_app_extension);
 | 
					        string_printf(path, "%s/%s%s", nfc_app_folder, dev->dev_name, nfc_app_extension);
 | 
				
			||||||
        if(!nfc_device_load_data(file_worker, path, dev)) {
 | 
					        if(!nfc_device_load_data(dev, path)) break;
 | 
				
			||||||
            result = false;
 | 
					        restored = true;
 | 
				
			||||||
            break;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    } while(0);
 | 
					    } while(0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    string_clear(path);
 | 
					    string_clear(path);
 | 
				
			||||||
    file_worker_close(file_worker);
 | 
					    return restored;
 | 
				
			||||||
    file_worker_free(file_worker);
 | 
					 | 
				
			||||||
    return result;
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -2,6 +2,8 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
#include <stdint.h>
 | 
					#include <stdint.h>
 | 
				
			||||||
#include <stdbool.h>
 | 
					#include <stdbool.h>
 | 
				
			||||||
 | 
					#include <storage/storage.h>
 | 
				
			||||||
 | 
					#include <dialogs/dialogs.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "mifare_ultralight.h"
 | 
					#include "mifare_ultralight.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -57,6 +59,8 @@ typedef struct {
 | 
				
			|||||||
} NfcDeviceData;
 | 
					} NfcDeviceData;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
typedef struct {
 | 
					typedef struct {
 | 
				
			||||||
 | 
					    Storage* storage;
 | 
				
			||||||
 | 
					    DialogsApp* dialogs;
 | 
				
			||||||
    NfcDeviceData dev_data;
 | 
					    NfcDeviceData dev_data;
 | 
				
			||||||
    char dev_name[NFC_DEV_NAME_MAX_LEN + 1];
 | 
					    char dev_name[NFC_DEV_NAME_MAX_LEN + 1];
 | 
				
			||||||
    char file_name[NFC_FILE_NAME_MAX_LEN];
 | 
					    char file_name[NFC_FILE_NAME_MAX_LEN];
 | 
				
			||||||
@ -64,6 +68,10 @@ typedef struct {
 | 
				
			|||||||
    bool shadow_file_exist;
 | 
					    bool shadow_file_exist;
 | 
				
			||||||
} NfcDevice;
 | 
					} NfcDevice;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					NfcDevice* nfc_device_alloc();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void nfc_device_free(NfcDevice* nfc_dev);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void nfc_device_set_name(NfcDevice* dev, const char* name);
 | 
					void nfc_device_set_name(NfcDevice* dev, const char* name);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
bool nfc_device_save(NfcDevice* dev, const char* dev_name);
 | 
					bool nfc_device_save(NfcDevice* dev, const char* dev_name);
 | 
				
			||||||
 | 
				
			|||||||
@ -1,16 +0,0 @@
 | 
				
			|||||||
#pragma once
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include "nfc_device.h"
 | 
					 | 
				
			||||||
#include <m-string.h>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
uint16_t nfc_device_prepare_format_string(NfcDevice* dev, string_t format_string);
 | 
					 | 
				
			||||||
bool nfc_device_parse_format_string(NfcDevice* dev, string_t format_string);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
uint16_t nfc_device_prepare_uid_string(NfcDevice* dev, string_t uid_string);
 | 
					 | 
				
			||||||
bool nfc_device_parse_uid_string(NfcDevice* dev, string_t uid_string);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
uint16_t nfc_device_prepare_mifare_ul_string(NfcDevice* dev, string_t mifare_ul_string);
 | 
					 | 
				
			||||||
bool nfc_device_parse_mifare_ul_string(NfcDevice* dev, string_t mifare_ul_string);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
uint16_t nfc_device_prepare_bank_card_string(NfcDevice* dev, string_t bank_card_string);
 | 
					 | 
				
			||||||
bool nfc_device_parse_bank_card_string(NfcDevice* dev, string_t bank_card_string);
 | 
					 | 
				
			||||||
@ -36,7 +36,7 @@ struct Nfc {
 | 
				
			|||||||
    Gui* gui;
 | 
					    Gui* gui;
 | 
				
			||||||
    NotificationApp* notifications;
 | 
					    NotificationApp* notifications;
 | 
				
			||||||
    SceneManager* scene_manager;
 | 
					    SceneManager* scene_manager;
 | 
				
			||||||
    NfcDevice dev;
 | 
					    NfcDevice* dev;
 | 
				
			||||||
    NfcDeviceCommonData dev_edit_data;
 | 
					    NfcDeviceCommonData dev_edit_data;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    char text_store[NFC_TEXT_STORE_SIZE + 1];
 | 
					    char text_store[NFC_TEXT_STORE_SIZE + 1];
 | 
				
			||||||
 | 
				
			|||||||
@ -248,6 +248,8 @@ void nfc_worker_read_emv(NfcWorker* nfc_worker) {
 | 
				
			|||||||
                    NFC_WORKER_TAG, "Select PPSE response received. Start parsing response");
 | 
					                    NFC_WORKER_TAG, "Select PPSE response received. Start parsing response");
 | 
				
			||||||
                if(emv_decode_ppse_response(rx_buff, *rx_len, &emv_app)) {
 | 
					                if(emv_decode_ppse_response(rx_buff, *rx_len, &emv_app)) {
 | 
				
			||||||
                    FURI_LOG_I(NFC_WORKER_TAG, "Select PPSE responce parced");
 | 
					                    FURI_LOG_I(NFC_WORKER_TAG, "Select PPSE responce parced");
 | 
				
			||||||
 | 
					                    result->emv_data.aid_len = emv_app.aid_len;
 | 
				
			||||||
 | 
					                    memcpy(result->emv_data.aid, emv_app.aid, emv_app.aid_len);
 | 
				
			||||||
                } else {
 | 
					                } else {
 | 
				
			||||||
                    FURI_LOG_E(NFC_WORKER_TAG, "Can't find pay application");
 | 
					                    FURI_LOG_E(NFC_WORKER_TAG, "Can't find pay application");
 | 
				
			||||||
                    furi_hal_nfc_deactivate();
 | 
					                    furi_hal_nfc_deactivate();
 | 
				
			||||||
 | 
				
			|||||||
@ -17,7 +17,7 @@ void nfc_scene_card_menu_on_enter(void* context) {
 | 
				
			|||||||
    Nfc* nfc = (Nfc*)context;
 | 
					    Nfc* nfc = (Nfc*)context;
 | 
				
			||||||
    Submenu* submenu = nfc->submenu;
 | 
					    Submenu* submenu = nfc->submenu;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if(nfc->dev.dev_data.nfc_data.protocol > NfcDeviceProtocolUnknown) {
 | 
					    if(nfc->dev->dev_data.nfc_data.protocol > NfcDeviceProtocolUnknown) {
 | 
				
			||||||
        submenu_add_item(
 | 
					        submenu_add_item(
 | 
				
			||||||
            submenu,
 | 
					            submenu,
 | 
				
			||||||
            "Run compatible app",
 | 
					            "Run compatible app",
 | 
				
			||||||
@ -48,9 +48,9 @@ bool nfc_scene_card_menu_on_event(void* context, SceneManagerEvent event) {
 | 
				
			|||||||
        if(event.event == SubmenuIndexRunApp) {
 | 
					        if(event.event == SubmenuIndexRunApp) {
 | 
				
			||||||
            scene_manager_set_scene_state(
 | 
					            scene_manager_set_scene_state(
 | 
				
			||||||
                nfc->scene_manager, NfcSceneCardMenu, SubmenuIndexRunApp);
 | 
					                nfc->scene_manager, NfcSceneCardMenu, SubmenuIndexRunApp);
 | 
				
			||||||
            if(nfc->dev.dev_data.nfc_data.protocol == NfcDeviceProtocolMifareUl) {
 | 
					            if(nfc->dev->dev_data.nfc_data.protocol == NfcDeviceProtocolMifareUl) {
 | 
				
			||||||
                scene_manager_next_scene(nfc->scene_manager, NfcSceneReadMifareUl);
 | 
					                scene_manager_next_scene(nfc->scene_manager, NfcSceneReadMifareUl);
 | 
				
			||||||
            } else if(nfc->dev.dev_data.nfc_data.protocol == NfcDeviceProtocolEMV) {
 | 
					            } else if(nfc->dev->dev_data.nfc_data.protocol == NfcDeviceProtocolEMV) {
 | 
				
			||||||
                scene_manager_next_scene(nfc->scene_manager, NfcSceneReadEmvApp);
 | 
					                scene_manager_next_scene(nfc->scene_manager, NfcSceneReadEmvApp);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            return true;
 | 
					            return true;
 | 
				
			||||||
@ -66,7 +66,7 @@ bool nfc_scene_card_menu_on_event(void* context, SceneManagerEvent event) {
 | 
				
			|||||||
            return true;
 | 
					            return true;
 | 
				
			||||||
        } else if(event.event == SubmenuIndexSave) {
 | 
					        } else if(event.event == SubmenuIndexSave) {
 | 
				
			||||||
            scene_manager_set_scene_state(nfc->scene_manager, NfcSceneCardMenu, SubmenuIndexSave);
 | 
					            scene_manager_set_scene_state(nfc->scene_manager, NfcSceneCardMenu, SubmenuIndexSave);
 | 
				
			||||||
            nfc->dev.format = NfcDeviceSaveFormatUid;
 | 
					            nfc->dev->format = NfcDeviceSaveFormatUid;
 | 
				
			||||||
            scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveName);
 | 
					            scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveName);
 | 
				
			||||||
            return true;
 | 
					            return true;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
				
			|||||||
@ -12,14 +12,14 @@ void nfc_scene_delete_on_enter(void* context) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    // Setup Custom Widget view
 | 
					    // Setup Custom Widget view
 | 
				
			||||||
    char delete_str[64];
 | 
					    char delete_str[64];
 | 
				
			||||||
    snprintf(delete_str, sizeof(delete_str), "\e#Delete %s\e#", nfc->dev.dev_name);
 | 
					    snprintf(delete_str, sizeof(delete_str), "\e#Delete %s\e#", nfc->dev->dev_name);
 | 
				
			||||||
    widget_add_text_box_element(nfc->widget, 0, 0, 128, 24, AlignCenter, AlignCenter, delete_str);
 | 
					    widget_add_text_box_element(nfc->widget, 0, 0, 128, 24, AlignCenter, AlignCenter, delete_str);
 | 
				
			||||||
    widget_add_button_element(
 | 
					    widget_add_button_element(
 | 
				
			||||||
        nfc->widget, GuiButtonTypeLeft, "Back", nfc_scene_delete_widget_callback, nfc);
 | 
					        nfc->widget, GuiButtonTypeLeft, "Back", nfc_scene_delete_widget_callback, nfc);
 | 
				
			||||||
    widget_add_button_element(
 | 
					    widget_add_button_element(
 | 
				
			||||||
        nfc->widget, GuiButtonTypeRight, "Delete", nfc_scene_delete_widget_callback, nfc);
 | 
					        nfc->widget, GuiButtonTypeRight, "Delete", nfc_scene_delete_widget_callback, nfc);
 | 
				
			||||||
    char uid_str[32];
 | 
					    char uid_str[32];
 | 
				
			||||||
    NfcDeviceCommonData* data = &nfc->dev.dev_data.nfc_data;
 | 
					    NfcDeviceCommonData* data = &nfc->dev->dev_data.nfc_data;
 | 
				
			||||||
    if(data->uid_len == 4) {
 | 
					    if(data->uid_len == 4) {
 | 
				
			||||||
        snprintf(
 | 
					        snprintf(
 | 
				
			||||||
            uid_str,
 | 
					            uid_str,
 | 
				
			||||||
@ -73,7 +73,7 @@ bool nfc_scene_delete_on_event(void* context, SceneManagerEvent event) {
 | 
				
			|||||||
        if(event.event == GuiButtonTypeLeft) {
 | 
					        if(event.event == GuiButtonTypeLeft) {
 | 
				
			||||||
            return scene_manager_previous_scene(nfc->scene_manager);
 | 
					            return scene_manager_previous_scene(nfc->scene_manager);
 | 
				
			||||||
        } else if(event.event == GuiButtonTypeRight) {
 | 
					        } else if(event.event == GuiButtonTypeRight) {
 | 
				
			||||||
            if(nfc_device_delete(&nfc->dev)) {
 | 
					            if(nfc_device_delete(nfc->dev)) {
 | 
				
			||||||
                scene_manager_next_scene(nfc->scene_manager, NfcSceneDeleteSuccess);
 | 
					                scene_manager_next_scene(nfc->scene_manager, NfcSceneDeleteSuccess);
 | 
				
			||||||
            } else {
 | 
					            } else {
 | 
				
			||||||
                scene_manager_search_and_switch_to_previous_scene(
 | 
					                scene_manager_search_and_switch_to_previous_scene(
 | 
				
			||||||
 | 
				
			|||||||
@ -1,4 +1,5 @@
 | 
				
			|||||||
#include "../nfc_i.h"
 | 
					#include "../nfc_i.h"
 | 
				
			||||||
 | 
					#include "../helpers/nfc_emv_parser.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define NFC_SCENE_DEVICE_INFO_BACK_EVENT (0UL)
 | 
					#define NFC_SCENE_DEVICE_INFO_BACK_EVENT (0UL)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -36,13 +37,13 @@ void nfc_scene_device_info_on_enter(void* context) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    // Setup Custom Widget view
 | 
					    // Setup Custom Widget view
 | 
				
			||||||
    widget_add_text_box_element(
 | 
					    widget_add_text_box_element(
 | 
				
			||||||
        nfc->widget, 0, 0, 128, 24, AlignCenter, AlignCenter, nfc->dev.dev_name);
 | 
					        nfc->widget, 0, 0, 128, 24, AlignCenter, AlignCenter, nfc->dev->dev_name);
 | 
				
			||||||
    widget_add_button_element(
 | 
					    widget_add_button_element(
 | 
				
			||||||
        nfc->widget, GuiButtonTypeLeft, "Back", nfc_scene_device_info_widget_callback, nfc);
 | 
					        nfc->widget, GuiButtonTypeLeft, "Back", nfc_scene_device_info_widget_callback, nfc);
 | 
				
			||||||
    widget_add_button_element(
 | 
					    widget_add_button_element(
 | 
				
			||||||
        nfc->widget, GuiButtonTypeRight, "Data", nfc_scene_device_info_widget_callback, nfc);
 | 
					        nfc->widget, GuiButtonTypeRight, "Data", nfc_scene_device_info_widget_callback, nfc);
 | 
				
			||||||
    char uid_str[32];
 | 
					    char uid_str[32];
 | 
				
			||||||
    NfcDeviceCommonData* data = &nfc->dev.dev_data.nfc_data;
 | 
					    NfcDeviceCommonData* data = &nfc->dev->dev_data.nfc_data;
 | 
				
			||||||
    if(data->uid_len == 4) {
 | 
					    if(data->uid_len == 4) {
 | 
				
			||||||
        snprintf(
 | 
					        snprintf(
 | 
				
			||||||
            uid_str,
 | 
					            uid_str,
 | 
				
			||||||
@ -87,14 +88,14 @@ void nfc_scene_device_info_on_enter(void* context) {
 | 
				
			|||||||
    widget_add_string_element(nfc->widget, 118, 42, AlignRight, AlignTop, FontSecondary, atqa_str);
 | 
					    widget_add_string_element(nfc->widget, 118, 42, AlignRight, AlignTop, FontSecondary, atqa_str);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Setup Data View
 | 
					    // Setup Data View
 | 
				
			||||||
    if(nfc->dev.format == NfcDeviceSaveFormatUid) {
 | 
					    if(nfc->dev->format == NfcDeviceSaveFormatUid) {
 | 
				
			||||||
        DialogEx* dialog_ex = nfc->dialog_ex;
 | 
					        DialogEx* dialog_ex = nfc->dialog_ex;
 | 
				
			||||||
        dialog_ex_set_left_button_text(dialog_ex, "Back");
 | 
					        dialog_ex_set_left_button_text(dialog_ex, "Back");
 | 
				
			||||||
        dialog_ex_set_text(dialog_ex, "No data", 64, 32, AlignCenter, AlignCenter);
 | 
					        dialog_ex_set_text(dialog_ex, "No data", 64, 32, AlignCenter, AlignCenter);
 | 
				
			||||||
        dialog_ex_set_context(dialog_ex, nfc);
 | 
					        dialog_ex_set_context(dialog_ex, nfc);
 | 
				
			||||||
        dialog_ex_set_result_callback(dialog_ex, nfc_scene_device_info_dialog_callback);
 | 
					        dialog_ex_set_result_callback(dialog_ex, nfc_scene_device_info_dialog_callback);
 | 
				
			||||||
    } else if(nfc->dev.format == NfcDeviceSaveFormatMifareUl) {
 | 
					    } else if(nfc->dev->format == NfcDeviceSaveFormatMifareUl) {
 | 
				
			||||||
        MifareUlData* mf_ul_data = (MifareUlData*)&nfc->dev.dev_data.mf_ul_data;
 | 
					        MifareUlData* mf_ul_data = &nfc->dev->dev_data.mf_ul_data;
 | 
				
			||||||
        TextBox* text_box = nfc->text_box;
 | 
					        TextBox* text_box = nfc->text_box;
 | 
				
			||||||
        text_box_set_context(text_box, nfc);
 | 
					        text_box_set_context(text_box, nfc);
 | 
				
			||||||
        text_box_set_exit_callback(text_box, nfc_scene_device_info_text_box_callback);
 | 
					        text_box_set_exit_callback(text_box, nfc_scene_device_info_text_box_callback);
 | 
				
			||||||
@ -107,8 +108,8 @@ void nfc_scene_device_info_on_enter(void* context) {
 | 
				
			|||||||
                nfc->text_box_store, "%02X%02X ", mf_ul_data->data[i], mf_ul_data->data[i + 1]);
 | 
					                nfc->text_box_store, "%02X%02X ", mf_ul_data->data[i], mf_ul_data->data[i + 1]);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        text_box_set_text(text_box, string_get_cstr(nfc->text_box_store));
 | 
					        text_box_set_text(text_box, string_get_cstr(nfc->text_box_store));
 | 
				
			||||||
    } else if(nfc->dev.format == NfcDeviceSaveFormatBankCard) {
 | 
					    } else if(nfc->dev->format == NfcDeviceSaveFormatBankCard) {
 | 
				
			||||||
        NfcEmvData* emv_data = &nfc->dev.dev_data.emv_data;
 | 
					        NfcEmvData* emv_data = &nfc->dev->dev_data.emv_data;
 | 
				
			||||||
        BankCard* bank_card = nfc->bank_card;
 | 
					        BankCard* bank_card = nfc->bank_card;
 | 
				
			||||||
        bank_card_set_name(bank_card, emv_data->name);
 | 
					        bank_card_set_name(bank_card, emv_data->name);
 | 
				
			||||||
        bank_card_set_number(bank_card, emv_data->number, emv_data->number_len);
 | 
					        bank_card_set_number(bank_card, emv_data->number, emv_data->number_len);
 | 
				
			||||||
@ -116,12 +117,29 @@ void nfc_scene_device_info_on_enter(void* context) {
 | 
				
			|||||||
        if(emv_data->exp_mon) {
 | 
					        if(emv_data->exp_mon) {
 | 
				
			||||||
            bank_card_set_exp_date(bank_card, emv_data->exp_mon, emv_data->exp_year);
 | 
					            bank_card_set_exp_date(bank_card, emv_data->exp_mon, emv_data->exp_year);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					        string_t display_str;
 | 
				
			||||||
 | 
					        string_init(display_str);
 | 
				
			||||||
        if(emv_data->country_code) {
 | 
					        if(emv_data->country_code) {
 | 
				
			||||||
            bank_card_set_country_name(bank_card, emv_data->country_code);
 | 
					            string_t country_name;
 | 
				
			||||||
 | 
					            string_init(country_name);
 | 
				
			||||||
 | 
					            if(nfc_emv_parser_get_country_name(
 | 
				
			||||||
 | 
					                   nfc->dev->storage, emv_data->country_code, country_name)) {
 | 
				
			||||||
 | 
					                string_printf(display_str, "Reg:%s", string_get_cstr(country_name));
 | 
				
			||||||
 | 
					                bank_card_set_country_name(bank_card, string_get_cstr(display_str));
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            string_clear(country_name);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        if(emv_data->currency_code) {
 | 
					        if(emv_data->currency_code) {
 | 
				
			||||||
            bank_card_set_currency_name(bank_card, emv_data->currency_code);
 | 
					            string_t currency_name;
 | 
				
			||||||
 | 
					            string_init(currency_name);
 | 
				
			||||||
 | 
					            if(nfc_emv_parser_get_currency_name(
 | 
				
			||||||
 | 
					                   nfc->dev->storage, emv_data->country_code, currency_name)) {
 | 
				
			||||||
 | 
					                string_printf(display_str, "Cur:%s", string_get_cstr(currency_name));
 | 
				
			||||||
 | 
					                bank_card_set_currency_name(bank_card, string_get_cstr(display_str));
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					            string_clear(currency_name);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        string_clear(display_str);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    scene_manager_set_scene_state(nfc->scene_manager, NfcSceneDeviceInfo, NfcSceneDeviceInfoUid);
 | 
					    scene_manager_set_scene_state(nfc->scene_manager, NfcSceneDeviceInfo, NfcSceneDeviceInfoUid);
 | 
				
			||||||
    view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget);
 | 
					    view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget);
 | 
				
			||||||
@ -136,17 +154,17 @@ bool nfc_scene_device_info_on_event(void* context, SceneManagerEvent event) {
 | 
				
			|||||||
        if((state == NfcSceneDeviceInfoUid) && (event.event == GuiButtonTypeLeft)) {
 | 
					        if((state == NfcSceneDeviceInfoUid) && (event.event == GuiButtonTypeLeft)) {
 | 
				
			||||||
            consumed = scene_manager_previous_scene(nfc->scene_manager);
 | 
					            consumed = scene_manager_previous_scene(nfc->scene_manager);
 | 
				
			||||||
        } else if((state == NfcSceneDeviceInfoUid) && (event.event == GuiButtonTypeRight)) {
 | 
					        } else if((state == NfcSceneDeviceInfoUid) && (event.event == GuiButtonTypeRight)) {
 | 
				
			||||||
            if(nfc->dev.format == NfcDeviceSaveFormatUid) {
 | 
					            if(nfc->dev->format == NfcDeviceSaveFormatUid) {
 | 
				
			||||||
                scene_manager_set_scene_state(
 | 
					                scene_manager_set_scene_state(
 | 
				
			||||||
                    nfc->scene_manager, NfcSceneDeviceInfo, NfcSceneDeviceInfoData);
 | 
					                    nfc->scene_manager, NfcSceneDeviceInfo, NfcSceneDeviceInfoData);
 | 
				
			||||||
                view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewDialogEx);
 | 
					                view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewDialogEx);
 | 
				
			||||||
                consumed = true;
 | 
					                consumed = true;
 | 
				
			||||||
            } else if(nfc->dev.format == NfcDeviceSaveFormatMifareUl) {
 | 
					            } else if(nfc->dev->format == NfcDeviceSaveFormatMifareUl) {
 | 
				
			||||||
                scene_manager_set_scene_state(
 | 
					                scene_manager_set_scene_state(
 | 
				
			||||||
                    nfc->scene_manager, NfcSceneDeviceInfo, NfcSceneDeviceInfoData);
 | 
					                    nfc->scene_manager, NfcSceneDeviceInfo, NfcSceneDeviceInfoData);
 | 
				
			||||||
                view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewTextBox);
 | 
					                view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewTextBox);
 | 
				
			||||||
                consumed = true;
 | 
					                consumed = true;
 | 
				
			||||||
            } else if(nfc->dev.format == NfcDeviceSaveFormatBankCard) {
 | 
					            } else if(nfc->dev->format == NfcDeviceSaveFormatBankCard) {
 | 
				
			||||||
                scene_manager_set_scene_state(
 | 
					                scene_manager_set_scene_state(
 | 
				
			||||||
                    nfc->scene_manager, NfcSceneDeviceInfo, NfcSceneDeviceInfoData);
 | 
					                    nfc->scene_manager, NfcSceneDeviceInfo, NfcSceneDeviceInfoData);
 | 
				
			||||||
                view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewBankCard);
 | 
					                view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewBankCard);
 | 
				
			||||||
@ -168,7 +186,7 @@ void nfc_scene_device_info_on_exit(void* context) {
 | 
				
			|||||||
    // Clear Custom Widget
 | 
					    // Clear Custom Widget
 | 
				
			||||||
    widget_clear(nfc->widget);
 | 
					    widget_clear(nfc->widget);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if(nfc->dev.format == NfcDeviceSaveFormatUid) {
 | 
					    if(nfc->dev->format == NfcDeviceSaveFormatUid) {
 | 
				
			||||||
        // Clear Dialog
 | 
					        // Clear Dialog
 | 
				
			||||||
        DialogEx* dialog_ex = nfc->dialog_ex;
 | 
					        DialogEx* dialog_ex = nfc->dialog_ex;
 | 
				
			||||||
        dialog_ex_set_header(dialog_ex, NULL, 0, 0, AlignCenter, AlignCenter);
 | 
					        dialog_ex_set_header(dialog_ex, NULL, 0, 0, AlignCenter, AlignCenter);
 | 
				
			||||||
@ -179,11 +197,11 @@ void nfc_scene_device_info_on_exit(void* context) {
 | 
				
			|||||||
        dialog_ex_set_center_button_text(dialog_ex, NULL);
 | 
					        dialog_ex_set_center_button_text(dialog_ex, NULL);
 | 
				
			||||||
        dialog_ex_set_result_callback(dialog_ex, NULL);
 | 
					        dialog_ex_set_result_callback(dialog_ex, NULL);
 | 
				
			||||||
        dialog_ex_set_context(dialog_ex, NULL);
 | 
					        dialog_ex_set_context(dialog_ex, NULL);
 | 
				
			||||||
    } else if(nfc->dev.format == NfcDeviceSaveFormatMifareUl) {
 | 
					    } else if(nfc->dev->format == NfcDeviceSaveFormatMifareUl) {
 | 
				
			||||||
        // Clear TextBox
 | 
					        // Clear TextBox
 | 
				
			||||||
        text_box_clean(nfc->text_box);
 | 
					        text_box_clean(nfc->text_box);
 | 
				
			||||||
        string_clean(nfc->text_box_store);
 | 
					        string_clean(nfc->text_box_store);
 | 
				
			||||||
    } else if(nfc->dev.format == NfcDeviceSaveFormatBankCard) {
 | 
					    } else if(nfc->dev->format == NfcDeviceSaveFormatBankCard) {
 | 
				
			||||||
        // Clear Bank Card
 | 
					        // Clear Bank Card
 | 
				
			||||||
        bank_card_clear(nfc->bank_card);
 | 
					        bank_card_clear(nfc->bank_card);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
				
			|||||||
@ -11,7 +11,7 @@ void nfc_scene_emulate_apdu_sequence_on_enter(void* context) {
 | 
				
			|||||||
    // Setup and start worker
 | 
					    // Setup and start worker
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup);
 | 
					    view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup);
 | 
				
			||||||
    nfc_worker_start(nfc->worker, NfcWorkerStateEmulateApdu, &nfc->dev.dev_data, NULL, nfc);
 | 
					    nfc_worker_start(nfc->worker, NfcWorkerStateEmulateApdu, &nfc->dev->dev_data, NULL, nfc);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
bool nfc_scene_emulate_apdu_sequence_on_event(void* context, SceneManagerEvent event) {
 | 
					bool nfc_scene_emulate_apdu_sequence_on_event(void* context, SceneManagerEvent event) {
 | 
				
			||||||
 | 
				
			|||||||
@ -14,8 +14,8 @@ void nfc_scene_emulate_mifare_ul_on_enter(void* context) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    // Setup view
 | 
					    // Setup view
 | 
				
			||||||
    Popup* popup = nfc->popup;
 | 
					    Popup* popup = nfc->popup;
 | 
				
			||||||
    if(strcmp(nfc->dev.dev_name, "")) {
 | 
					    if(strcmp(nfc->dev->dev_name, "")) {
 | 
				
			||||||
        nfc_text_store_set(nfc, "%s", nfc->dev.dev_name);
 | 
					        nfc_text_store_set(nfc, "%s", nfc->dev->dev_name);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    popup_set_icon(popup, 0, 3, &I_RFIDDolphinSend_97x61);
 | 
					    popup_set_icon(popup, 0, 3, &I_RFIDDolphinSend_97x61);
 | 
				
			||||||
    popup_set_header(popup, "Emulating\nMf Ultralight", 56, 31, AlignLeft, AlignTop);
 | 
					    popup_set_header(popup, "Emulating\nMf Ultralight", 56, 31, AlignLeft, AlignTop);
 | 
				
			||||||
@ -25,7 +25,7 @@ void nfc_scene_emulate_mifare_ul_on_enter(void* context) {
 | 
				
			|||||||
    nfc_worker_start(
 | 
					    nfc_worker_start(
 | 
				
			||||||
        nfc->worker,
 | 
					        nfc->worker,
 | 
				
			||||||
        NfcWorkerStateEmulateMifareUl,
 | 
					        NfcWorkerStateEmulateMifareUl,
 | 
				
			||||||
        &nfc->dev.dev_data,
 | 
					        &nfc->dev->dev_data,
 | 
				
			||||||
        nfc_emulate_mifare_ul_worker_callback,
 | 
					        nfc_emulate_mifare_ul_worker_callback,
 | 
				
			||||||
        nfc);
 | 
					        nfc);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -45,7 +45,7 @@ bool nfc_scene_emulate_mifare_ul_on_event(void* context, SceneManagerEvent event
 | 
				
			|||||||
           NFC_MF_UL_DATA_CHANGED) {
 | 
					           NFC_MF_UL_DATA_CHANGED) {
 | 
				
			||||||
            scene_manager_set_scene_state(
 | 
					            scene_manager_set_scene_state(
 | 
				
			||||||
                nfc->scene_manager, NfcSceneEmulateMifareUl, NFC_MF_UL_DATA_NOT_CHANGED);
 | 
					                nfc->scene_manager, NfcSceneEmulateMifareUl, NFC_MF_UL_DATA_NOT_CHANGED);
 | 
				
			||||||
            nfc_device_save_shadow(&nfc->dev, nfc->dev.dev_name);
 | 
					            nfc_device_save_shadow(nfc->dev, nfc->dev->dev_name);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        consumed = false;
 | 
					        consumed = false;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
				
			|||||||
@ -5,10 +5,10 @@ void nfc_scene_emulate_uid_on_enter(void* context) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    // Setup view
 | 
					    // Setup view
 | 
				
			||||||
    Popup* popup = nfc->popup;
 | 
					    Popup* popup = nfc->popup;
 | 
				
			||||||
    NfcDeviceCommonData* data = &nfc->dev.dev_data.nfc_data;
 | 
					    NfcDeviceCommonData* data = &nfc->dev->dev_data.nfc_data;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if(strcmp(nfc->dev.dev_name, "")) {
 | 
					    if(strcmp(nfc->dev->dev_name, "")) {
 | 
				
			||||||
        nfc_text_store_set(nfc, "%s", nfc->dev.dev_name);
 | 
					        nfc_text_store_set(nfc, "%s", nfc->dev->dev_name);
 | 
				
			||||||
    } else if(data->uid_len == 4) {
 | 
					    } else if(data->uid_len == 4) {
 | 
				
			||||||
        nfc_text_store_set(
 | 
					        nfc_text_store_set(
 | 
				
			||||||
            nfc, "%02X %02X %02X %02X", data->uid[0], data->uid[1], data->uid[2], data->uid[3]);
 | 
					            nfc, "%02X %02X %02X %02X", data->uid[0], data->uid[1], data->uid[2], data->uid[3]);
 | 
				
			||||||
@ -32,7 +32,7 @@ void nfc_scene_emulate_uid_on_enter(void* context) {
 | 
				
			|||||||
    // Setup and start worker
 | 
					    // Setup and start worker
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup);
 | 
					    view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup);
 | 
				
			||||||
    nfc_worker_start(nfc->worker, NfcWorkerStateEmulate, &nfc->dev.dev_data, NULL, nfc);
 | 
					    nfc_worker_start(nfc->worker, NfcWorkerStateEmulate, &nfc->dev->dev_data, NULL, nfc);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
bool nfc_scene_emulate_uid_on_event(void* context, SceneManagerEvent event) {
 | 
					bool nfc_scene_emulate_uid_on_event(void* context, SceneManagerEvent event) {
 | 
				
			||||||
 | 
				
			|||||||
@ -3,7 +3,7 @@
 | 
				
			|||||||
void nfc_scene_file_select_on_enter(void* context) {
 | 
					void nfc_scene_file_select_on_enter(void* context) {
 | 
				
			||||||
    Nfc* nfc = (Nfc*)context;
 | 
					    Nfc* nfc = (Nfc*)context;
 | 
				
			||||||
    // Process file_select return
 | 
					    // Process file_select return
 | 
				
			||||||
    if(nfc_file_select(&nfc->dev)) {
 | 
					    if(nfc_file_select(nfc->dev)) {
 | 
				
			||||||
        scene_manager_next_scene(nfc->scene_manager, NfcSceneSavedMenu);
 | 
					        scene_manager_next_scene(nfc->scene_manager, NfcSceneSavedMenu);
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
        scene_manager_search_and_switch_to_previous_scene(nfc->scene_manager, NfcSceneStart);
 | 
					        scene_manager_search_and_switch_to_previous_scene(nfc->scene_manager, NfcSceneStart);
 | 
				
			||||||
 | 
				
			|||||||
@ -32,9 +32,9 @@ bool nfc_scene_mifare_ul_menu_on_event(void* context, SceneManagerEvent event) {
 | 
				
			|||||||
        if(event.event == SubmenuIndexSave) {
 | 
					        if(event.event == SubmenuIndexSave) {
 | 
				
			||||||
            scene_manager_set_scene_state(
 | 
					            scene_manager_set_scene_state(
 | 
				
			||||||
                nfc->scene_manager, NfcSceneMifareUlMenu, SubmenuIndexSave);
 | 
					                nfc->scene_manager, NfcSceneMifareUlMenu, SubmenuIndexSave);
 | 
				
			||||||
            nfc->dev.format = NfcDeviceSaveFormatMifareUl;
 | 
					            nfc->dev->format = NfcDeviceSaveFormatMifareUl;
 | 
				
			||||||
            // Clear device name
 | 
					            // Clear device name
 | 
				
			||||||
            nfc_device_set_name(&nfc->dev, "");
 | 
					            nfc_device_set_name(nfc->dev, "");
 | 
				
			||||||
            scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveName);
 | 
					            scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveName);
 | 
				
			||||||
            return true;
 | 
					            return true;
 | 
				
			||||||
        } else if(event.event == SubmenuIndexEmulate) {
 | 
					        } else if(event.event == SubmenuIndexEmulate) {
 | 
				
			||||||
 | 
				
			|||||||
@ -18,7 +18,7 @@ void nfc_scene_read_card_on_enter(void* context) {
 | 
				
			|||||||
    // Start worker
 | 
					    // Start worker
 | 
				
			||||||
    view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup);
 | 
					    view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup);
 | 
				
			||||||
    nfc_worker_start(
 | 
					    nfc_worker_start(
 | 
				
			||||||
        nfc->worker, NfcWorkerStateDetect, &nfc->dev.dev_data, nfc_read_card_worker_callback, nfc);
 | 
					        nfc->worker, NfcWorkerStateDetect, &nfc->dev->dev_data, nfc_read_card_worker_callback, nfc);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
bool nfc_scene_read_card_on_event(void* context, SceneManagerEvent event) {
 | 
					bool nfc_scene_read_card_on_event(void* context, SceneManagerEvent event) {
 | 
				
			||||||
 | 
				
			|||||||
@ -15,7 +15,7 @@ void nfc_scene_read_card_success_on_enter(void* context) {
 | 
				
			|||||||
    notification_message(nfc->notifications, &sequence_success);
 | 
					    notification_message(nfc->notifications, &sequence_success);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Setup view
 | 
					    // Setup view
 | 
				
			||||||
    NfcDeviceCommonData* data = (NfcDeviceCommonData*)&nfc->dev.dev_data.nfc_data;
 | 
					    NfcDeviceCommonData* data = &nfc->dev->dev_data.nfc_data;
 | 
				
			||||||
    DialogEx* dialog_ex = nfc->dialog_ex;
 | 
					    DialogEx* dialog_ex = nfc->dialog_ex;
 | 
				
			||||||
    dialog_ex_set_left_button_text(dialog_ex, "Retry");
 | 
					    dialog_ex_set_left_button_text(dialog_ex, "Retry");
 | 
				
			||||||
    dialog_ex_set_right_button_text(dialog_ex, "More");
 | 
					    dialog_ex_set_right_button_text(dialog_ex, "More");
 | 
				
			||||||
@ -68,7 +68,7 @@ bool nfc_scene_read_card_success_on_event(void* context, SceneManagerEvent event
 | 
				
			|||||||
            return scene_manager_previous_scene(nfc->scene_manager);
 | 
					            return scene_manager_previous_scene(nfc->scene_manager);
 | 
				
			||||||
        } else if(event.event == DialogExResultRight) {
 | 
					        } else if(event.event == DialogExResultRight) {
 | 
				
			||||||
            // Clear device name
 | 
					            // Clear device name
 | 
				
			||||||
            nfc_device_set_name(&nfc->dev, "");
 | 
					            nfc_device_set_name(nfc->dev, "");
 | 
				
			||||||
            scene_manager_next_scene(nfc->scene_manager, NfcSceneCardMenu);
 | 
					            scene_manager_next_scene(nfc->scene_manager, NfcSceneCardMenu);
 | 
				
			||||||
            return true;
 | 
					            return true;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
				
			|||||||
@ -20,7 +20,7 @@ void nfc_scene_read_emv_app_on_enter(void* context) {
 | 
				
			|||||||
    nfc_worker_start(
 | 
					    nfc_worker_start(
 | 
				
			||||||
        nfc->worker,
 | 
					        nfc->worker,
 | 
				
			||||||
        NfcWorkerStateReadEMVApp,
 | 
					        NfcWorkerStateReadEMVApp,
 | 
				
			||||||
        &nfc->dev.dev_data,
 | 
					        &nfc->dev->dev_data,
 | 
				
			||||||
        nfc_read_emv_app_worker_callback,
 | 
					        nfc_read_emv_app_worker_callback,
 | 
				
			||||||
        nfc);
 | 
					        nfc);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -13,8 +13,8 @@ void nfc_scene_read_emv_app_success_on_enter(void* context) {
 | 
				
			|||||||
    Nfc* nfc = (Nfc*)context;
 | 
					    Nfc* nfc = (Nfc*)context;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Setup view
 | 
					    // Setup view
 | 
				
			||||||
    NfcDeviceCommonData* nfc_data = &nfc->dev.dev_data.nfc_data;
 | 
					    NfcDeviceCommonData* nfc_data = &nfc->dev->dev_data.nfc_data;
 | 
				
			||||||
    NfcEmvData* emv_data = &nfc->dev.dev_data.emv_data;
 | 
					    NfcEmvData* emv_data = &nfc->dev->dev_data.emv_data;
 | 
				
			||||||
    DialogEx* dialog_ex = nfc->dialog_ex;
 | 
					    DialogEx* dialog_ex = nfc->dialog_ex;
 | 
				
			||||||
    dialog_ex_set_left_button_text(dialog_ex, "Retry");
 | 
					    dialog_ex_set_left_button_text(dialog_ex, "Retry");
 | 
				
			||||||
    dialog_ex_set_right_button_text(dialog_ex, "Run app");
 | 
					    dialog_ex_set_right_button_text(dialog_ex, "Run app");
 | 
				
			||||||
@ -23,7 +23,8 @@ void nfc_scene_read_emv_app_success_on_enter(void* context) {
 | 
				
			|||||||
    // Display UID and AID
 | 
					    // Display UID and AID
 | 
				
			||||||
    string_t aid;
 | 
					    string_t aid;
 | 
				
			||||||
    string_init(aid);
 | 
					    string_init(aid);
 | 
				
			||||||
    bool aid_found = nfc_emv_parser_get_aid_name(emv_data->aid, emv_data->aid_len, aid);
 | 
					    bool aid_found =
 | 
				
			||||||
 | 
					        nfc_emv_parser_get_aid_name(nfc->dev->storage, emv_data->aid, emv_data->aid_len, aid);
 | 
				
			||||||
    if(!aid_found) {
 | 
					    if(!aid_found) {
 | 
				
			||||||
        for(uint8_t i = 0; i < emv_data->aid_len; i++) {
 | 
					        for(uint8_t i = 0; i < emv_data->aid_len; i++) {
 | 
				
			||||||
            string_cat_printf(aid, "%02X", emv_data->aid[i]);
 | 
					            string_cat_printf(aid, "%02X", emv_data->aid[i]);
 | 
				
			||||||
 | 
				
			|||||||
@ -17,12 +17,12 @@ void nfc_scene_read_emv_data_on_enter(void* context) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup);
 | 
					    view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup);
 | 
				
			||||||
    // Clear emv data
 | 
					    // Clear emv data
 | 
				
			||||||
    memset(&nfc->dev.dev_data.emv_data, 0, sizeof(nfc->dev.dev_data.emv_data));
 | 
					    memset(&nfc->dev->dev_data.emv_data, 0, sizeof(nfc->dev->dev_data.emv_data));
 | 
				
			||||||
    // Start worker
 | 
					    // Start worker
 | 
				
			||||||
    nfc_worker_start(
 | 
					    nfc_worker_start(
 | 
				
			||||||
        nfc->worker,
 | 
					        nfc->worker,
 | 
				
			||||||
        NfcWorkerStateReadEMV,
 | 
					        NfcWorkerStateReadEMV,
 | 
				
			||||||
        &nfc->dev.dev_data,
 | 
					        &nfc->dev->dev_data,
 | 
				
			||||||
        nfc_read_emv_data_worker_callback,
 | 
					        nfc_read_emv_data_worker_callback,
 | 
				
			||||||
        nfc);
 | 
					        nfc);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -13,8 +13,8 @@ void nfc_scene_read_emv_data_success_widget_callback(
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
void nfc_scene_read_emv_data_success_on_enter(void* context) {
 | 
					void nfc_scene_read_emv_data_success_on_enter(void* context) {
 | 
				
			||||||
    Nfc* nfc = (Nfc*)context;
 | 
					    Nfc* nfc = (Nfc*)context;
 | 
				
			||||||
    NfcEmvData* emv_data = &nfc->dev.dev_data.emv_data;
 | 
					    NfcEmvData* emv_data = &nfc->dev->dev_data.emv_data;
 | 
				
			||||||
    NfcDeviceCommonData* nfc_data = &nfc->dev.dev_data.nfc_data;
 | 
					    NfcDeviceCommonData* nfc_data = &nfc->dev->dev_data.nfc_data;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Setup Custom Widget view
 | 
					    // Setup Custom Widget view
 | 
				
			||||||
    // Add frame
 | 
					    // Add frame
 | 
				
			||||||
@ -34,7 +34,7 @@ void nfc_scene_read_emv_data_success_on_enter(void* context) {
 | 
				
			|||||||
        nfc);
 | 
					        nfc);
 | 
				
			||||||
    // Add card name
 | 
					    // Add card name
 | 
				
			||||||
    widget_add_string_element(
 | 
					    widget_add_string_element(
 | 
				
			||||||
        nfc->widget, 64, 3, AlignCenter, AlignTop, FontSecondary, nfc->dev.dev_data.emv_data.name);
 | 
					        nfc->widget, 64, 3, AlignCenter, AlignTop, FontSecondary, nfc->dev->dev_data.emv_data.name);
 | 
				
			||||||
    // Add cad number
 | 
					    // Add cad number
 | 
				
			||||||
    string_t pan_str;
 | 
					    string_t pan_str;
 | 
				
			||||||
    string_init(pan_str);
 | 
					    string_init(pan_str);
 | 
				
			||||||
@ -49,7 +49,7 @@ void nfc_scene_read_emv_data_success_on_enter(void* context) {
 | 
				
			|||||||
    string_t country_name;
 | 
					    string_t country_name;
 | 
				
			||||||
    string_init(country_name);
 | 
					    string_init(country_name);
 | 
				
			||||||
    if((emv_data->country_code) &&
 | 
					    if((emv_data->country_code) &&
 | 
				
			||||||
       nfc_emv_parser_get_country_name(emv_data->country_code, country_name)) {
 | 
					       nfc_emv_parser_get_country_name(nfc->dev->storage, emv_data->country_code, country_name)) {
 | 
				
			||||||
        string_t disp_country;
 | 
					        string_t disp_country;
 | 
				
			||||||
        string_init_printf(disp_country, "Reg:%s", country_name);
 | 
					        string_init_printf(disp_country, "Reg:%s", country_name);
 | 
				
			||||||
        widget_add_string_element(
 | 
					        widget_add_string_element(
 | 
				
			||||||
@ -61,7 +61,8 @@ void nfc_scene_read_emv_data_success_on_enter(void* context) {
 | 
				
			|||||||
    string_t currency_name;
 | 
					    string_t currency_name;
 | 
				
			||||||
    string_init(currency_name);
 | 
					    string_init(currency_name);
 | 
				
			||||||
    if((emv_data->currency_code) &&
 | 
					    if((emv_data->currency_code) &&
 | 
				
			||||||
       nfc_emv_parser_get_currency_name(emv_data->currency_code, currency_name)) {
 | 
					       nfc_emv_parser_get_currency_name(
 | 
				
			||||||
 | 
					           nfc->dev->storage, emv_data->currency_code, currency_name)) {
 | 
				
			||||||
        string_t disp_currency;
 | 
					        string_t disp_currency;
 | 
				
			||||||
        string_init_printf(disp_currency, "Cur:%s", currency_name);
 | 
					        string_init_printf(disp_currency, "Cur:%s", currency_name);
 | 
				
			||||||
        widget_add_string_element(
 | 
					        widget_add_string_element(
 | 
				
			||||||
@ -122,8 +123,8 @@ bool nfc_scene_read_emv_data_success_on_event(void* context, SceneManagerEvent e
 | 
				
			|||||||
                nfc->scene_manager, NfcSceneReadEmvAppSuccess);
 | 
					                nfc->scene_manager, NfcSceneReadEmvAppSuccess);
 | 
				
			||||||
        } else if(event.event == GuiButtonTypeRight) {
 | 
					        } else if(event.event == GuiButtonTypeRight) {
 | 
				
			||||||
            // Clear device name
 | 
					            // Clear device name
 | 
				
			||||||
            nfc_device_set_name(&nfc->dev, "");
 | 
					            nfc_device_set_name(nfc->dev, "");
 | 
				
			||||||
            nfc->dev.format = NfcDeviceSaveFormatBankCard;
 | 
					            nfc->dev->format = NfcDeviceSaveFormatBankCard;
 | 
				
			||||||
            scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveName);
 | 
					            scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveName);
 | 
				
			||||||
            return true;
 | 
					            return true;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
				
			|||||||
@ -20,7 +20,7 @@ void nfc_scene_read_mifare_ul_on_enter(void* context) {
 | 
				
			|||||||
    nfc_worker_start(
 | 
					    nfc_worker_start(
 | 
				
			||||||
        nfc->worker,
 | 
					        nfc->worker,
 | 
				
			||||||
        NfcWorkerStateReadMifareUl,
 | 
					        NfcWorkerStateReadMifareUl,
 | 
				
			||||||
        &nfc->dev.dev_data,
 | 
					        &nfc->dev->dev_data,
 | 
				
			||||||
        nfc_read_mifare_ul_worker_callback,
 | 
					        nfc_read_mifare_ul_worker_callback,
 | 
				
			||||||
        nfc);
 | 
					        nfc);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -27,7 +27,7 @@ void nfc_scene_read_mifare_ul_success_on_enter(void* context) {
 | 
				
			|||||||
    notification_message(nfc->notifications, &sequence_success);
 | 
					    notification_message(nfc->notifications, &sequence_success);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Setup dialog view
 | 
					    // Setup dialog view
 | 
				
			||||||
    NfcDeviceCommonData* data = (NfcDeviceCommonData*)&nfc->dev.dev_data.nfc_data;
 | 
					    NfcDeviceCommonData* data = &nfc->dev->dev_data.nfc_data;
 | 
				
			||||||
    DialogEx* dialog_ex = nfc->dialog_ex;
 | 
					    DialogEx* dialog_ex = nfc->dialog_ex;
 | 
				
			||||||
    dialog_ex_set_left_button_text(dialog_ex, "Retry");
 | 
					    dialog_ex_set_left_button_text(dialog_ex, "Retry");
 | 
				
			||||||
    dialog_ex_set_right_button_text(dialog_ex, "More");
 | 
					    dialog_ex_set_right_button_text(dialog_ex, "More");
 | 
				
			||||||
@ -54,7 +54,7 @@ void nfc_scene_read_mifare_ul_success_on_enter(void* context) {
 | 
				
			|||||||
    dialog_ex_set_result_callback(dialog_ex, nfc_scene_read_mifare_ul_success_dialog_callback);
 | 
					    dialog_ex_set_result_callback(dialog_ex, nfc_scene_read_mifare_ul_success_dialog_callback);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Setup TextBox view
 | 
					    // Setup TextBox view
 | 
				
			||||||
    MifareUlData* mf_ul_data = (MifareUlData*)&nfc->dev.dev_data.mf_ul_data;
 | 
					    MifareUlData* mf_ul_data = &nfc->dev->dev_data.mf_ul_data;
 | 
				
			||||||
    TextBox* text_box = nfc->text_box;
 | 
					    TextBox* text_box = nfc->text_box;
 | 
				
			||||||
    text_box_set_context(text_box, nfc);
 | 
					    text_box_set_context(text_box, nfc);
 | 
				
			||||||
    text_box_set_exit_callback(text_box, nfc_scene_read_mifare_ul_success_text_box_callback);
 | 
					    text_box_set_exit_callback(text_box, nfc_scene_read_mifare_ul_success_text_box_callback);
 | 
				
			||||||
 | 
				
			|||||||
@ -15,11 +15,11 @@ void nfc_scene_save_name_on_enter(void* context) {
 | 
				
			|||||||
    // Setup view
 | 
					    // Setup view
 | 
				
			||||||
    TextInput* text_input = nfc->text_input;
 | 
					    TextInput* text_input = nfc->text_input;
 | 
				
			||||||
    bool dev_name_empty = false;
 | 
					    bool dev_name_empty = false;
 | 
				
			||||||
    if(!strcmp(nfc->dev.dev_name, "")) {
 | 
					    if(!strcmp(nfc->dev->dev_name, "")) {
 | 
				
			||||||
        set_random_name(nfc->text_store, sizeof(nfc->text_store));
 | 
					        set_random_name(nfc->text_store, sizeof(nfc->text_store));
 | 
				
			||||||
        dev_name_empty = true;
 | 
					        dev_name_empty = true;
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
        nfc_text_store_set(nfc, nfc->dev.dev_name);
 | 
					        nfc_text_store_set(nfc, nfc->dev->dev_name);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    text_input_set_header_text(text_input, "Name the card");
 | 
					    text_input_set_header_text(text_input, "Name the card");
 | 
				
			||||||
    text_input_set_result_callback(
 | 
					    text_input_set_result_callback(
 | 
				
			||||||
@ -37,14 +37,14 @@ bool nfc_scene_save_name_on_event(void* context, SceneManagerEvent event) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    if(event.type == SceneManagerEventTypeCustom) {
 | 
					    if(event.type == SceneManagerEventTypeCustom) {
 | 
				
			||||||
        if(event.event == SCENE_SAVE_NAME_CUSTOM_EVENT) {
 | 
					        if(event.event == SCENE_SAVE_NAME_CUSTOM_EVENT) {
 | 
				
			||||||
            if(strcmp(nfc->dev.dev_name, "")) {
 | 
					            if(strcmp(nfc->dev->dev_name, "")) {
 | 
				
			||||||
                nfc_device_delete(&nfc->dev);
 | 
					                nfc_device_delete(nfc->dev);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSetUid)) {
 | 
					            if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSetUid)) {
 | 
				
			||||||
                nfc->dev.dev_data.nfc_data = nfc->dev_edit_data;
 | 
					                nfc->dev->dev_data.nfc_data = nfc->dev_edit_data;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            strlcpy(nfc->dev.dev_name, nfc->text_store, strlen(nfc->text_store) + 1);
 | 
					            strlcpy(nfc->dev->dev_name, nfc->text_store, strlen(nfc->text_store) + 1);
 | 
				
			||||||
            if(nfc_device_save(&nfc->dev, nfc->text_store)) {
 | 
					            if(nfc_device_save(nfc->dev, nfc->text_store)) {
 | 
				
			||||||
                scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveSuccess);
 | 
					                scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveSuccess);
 | 
				
			||||||
                return true;
 | 
					                return true;
 | 
				
			||||||
            } else {
 | 
					            } else {
 | 
				
			||||||
 | 
				
			|||||||
@ -18,7 +18,7 @@ void nfc_scene_saved_menu_on_enter(void* context) {
 | 
				
			|||||||
    Nfc* nfc = (Nfc*)context;
 | 
					    Nfc* nfc = (Nfc*)context;
 | 
				
			||||||
    Submenu* submenu = nfc->submenu;
 | 
					    Submenu* submenu = nfc->submenu;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if(nfc->dev.format != NfcDeviceSaveFormatBankCard) {
 | 
					    if(nfc->dev->format != NfcDeviceSaveFormatBankCard) {
 | 
				
			||||||
        submenu_add_item(
 | 
					        submenu_add_item(
 | 
				
			||||||
            submenu, "Emulate", SubmenuIndexEmulate, nfc_scene_saved_menu_submenu_callback, nfc);
 | 
					            submenu, "Emulate", SubmenuIndexEmulate, nfc_scene_saved_menu_submenu_callback, nfc);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@ -30,7 +30,7 @@ void nfc_scene_saved_menu_on_enter(void* context) {
 | 
				
			|||||||
        submenu, "Info", SubmenuIndexInfo, nfc_scene_saved_menu_submenu_callback, nfc);
 | 
					        submenu, "Info", SubmenuIndexInfo, nfc_scene_saved_menu_submenu_callback, nfc);
 | 
				
			||||||
    submenu_set_selected_item(
 | 
					    submenu_set_selected_item(
 | 
				
			||||||
        nfc->submenu, scene_manager_get_scene_state(nfc->scene_manager, NfcSceneSavedMenu));
 | 
					        nfc->submenu, scene_manager_get_scene_state(nfc->scene_manager, NfcSceneSavedMenu));
 | 
				
			||||||
    if(nfc->dev.shadow_file_exist) {
 | 
					    if(nfc->dev->shadow_file_exist) {
 | 
				
			||||||
        submenu_add_item(
 | 
					        submenu_add_item(
 | 
				
			||||||
            submenu,
 | 
					            submenu,
 | 
				
			||||||
            "Restore original",
 | 
					            "Restore original",
 | 
				
			||||||
@ -49,7 +49,7 @@ bool nfc_scene_saved_menu_on_event(void* context, SceneManagerEvent event) {
 | 
				
			|||||||
    if(event.type == SceneManagerEventTypeCustom) {
 | 
					    if(event.type == SceneManagerEventTypeCustom) {
 | 
				
			||||||
        scene_manager_set_scene_state(nfc->scene_manager, NfcSceneSavedMenu, event.event);
 | 
					        scene_manager_set_scene_state(nfc->scene_manager, NfcSceneSavedMenu, event.event);
 | 
				
			||||||
        if(event.event == SubmenuIndexEmulate) {
 | 
					        if(event.event == SubmenuIndexEmulate) {
 | 
				
			||||||
            if(nfc->dev.format == NfcDeviceSaveFormatMifareUl) {
 | 
					            if(nfc->dev->format == NfcDeviceSaveFormatMifareUl) {
 | 
				
			||||||
                scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateMifareUl);
 | 
					                scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateMifareUl);
 | 
				
			||||||
            } else {
 | 
					            } else {
 | 
				
			||||||
                scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateUid);
 | 
					                scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateUid);
 | 
				
			||||||
@ -65,7 +65,7 @@ bool nfc_scene_saved_menu_on_event(void* context, SceneManagerEvent event) {
 | 
				
			|||||||
            scene_manager_next_scene(nfc->scene_manager, NfcSceneDeviceInfo);
 | 
					            scene_manager_next_scene(nfc->scene_manager, NfcSceneDeviceInfo);
 | 
				
			||||||
            consumed = true;
 | 
					            consumed = true;
 | 
				
			||||||
        } else if(event.event == SubmenuIndexRestoreOriginal) {
 | 
					        } else if(event.event == SubmenuIndexRestoreOriginal) {
 | 
				
			||||||
            if(!nfc_device_restore(&nfc->dev)) {
 | 
					            if(!nfc_device_restore(nfc->dev)) {
 | 
				
			||||||
                scene_manager_search_and_switch_to_previous_scene(
 | 
					                scene_manager_search_and_switch_to_previous_scene(
 | 
				
			||||||
                    nfc->scene_manager, NfcSceneStart);
 | 
					                    nfc->scene_manager, NfcSceneStart);
 | 
				
			||||||
            } else {
 | 
					            } else {
 | 
				
			||||||
 | 
				
			|||||||
@ -19,7 +19,7 @@ void nfc_scene_set_atqa_on_enter(void* context) {
 | 
				
			|||||||
        nfc_scene_set_atqa_byte_input_callback,
 | 
					        nfc_scene_set_atqa_byte_input_callback,
 | 
				
			||||||
        NULL,
 | 
					        NULL,
 | 
				
			||||||
        nfc,
 | 
					        nfc,
 | 
				
			||||||
        nfc->dev.dev_data.nfc_data.atqa,
 | 
					        nfc->dev->dev_data.nfc_data.atqa,
 | 
				
			||||||
        2);
 | 
					        2);
 | 
				
			||||||
    view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewByteInput);
 | 
					    view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewByteInput);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -19,7 +19,7 @@ void nfc_scene_set_sak_on_enter(void* context) {
 | 
				
			|||||||
        nfc_scene_set_sak_byte_input_callback,
 | 
					        nfc_scene_set_sak_byte_input_callback,
 | 
				
			||||||
        NULL,
 | 
					        NULL,
 | 
				
			||||||
        nfc,
 | 
					        nfc,
 | 
				
			||||||
        &nfc->dev.dev_data.nfc_data.sak,
 | 
					        &nfc->dev->dev_data.nfc_data.sak,
 | 
				
			||||||
        1);
 | 
					        1);
 | 
				
			||||||
    view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewByteInput);
 | 
					    view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewByteInput);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -15,7 +15,7 @@ void nfc_scene_set_type_on_enter(void* context) {
 | 
				
			|||||||
    Nfc* nfc = (Nfc*)context;
 | 
					    Nfc* nfc = (Nfc*)context;
 | 
				
			||||||
    Submenu* submenu = nfc->submenu;
 | 
					    Submenu* submenu = nfc->submenu;
 | 
				
			||||||
    // Clear device name
 | 
					    // Clear device name
 | 
				
			||||||
    nfc_device_set_name(&nfc->dev, "");
 | 
					    nfc_device_set_name(nfc->dev, "");
 | 
				
			||||||
    submenu_add_item(
 | 
					    submenu_add_item(
 | 
				
			||||||
        submenu, "NFC-A 7-bytes UID", SubmenuIndexNFCA7, nfc_scene_set_type_submenu_callback, nfc);
 | 
					        submenu, "NFC-A 7-bytes UID", SubmenuIndexNFCA7, nfc_scene_set_type_submenu_callback, nfc);
 | 
				
			||||||
    submenu_add_item(
 | 
					    submenu_add_item(
 | 
				
			||||||
@ -28,13 +28,13 @@ bool nfc_scene_set_type_on_event(void* context, SceneManagerEvent event) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    if(event.type == SceneManagerEventTypeCustom) {
 | 
					    if(event.type == SceneManagerEventTypeCustom) {
 | 
				
			||||||
        if(event.event == SubmenuIndexNFCA7) {
 | 
					        if(event.event == SubmenuIndexNFCA7) {
 | 
				
			||||||
            nfc->dev.dev_data.nfc_data.uid_len = 7;
 | 
					            nfc->dev->dev_data.nfc_data.uid_len = 7;
 | 
				
			||||||
            nfc->dev.format = NfcDeviceSaveFormatUid;
 | 
					            nfc->dev->format = NfcDeviceSaveFormatUid;
 | 
				
			||||||
            scene_manager_next_scene(nfc->scene_manager, NfcSceneSetSak);
 | 
					            scene_manager_next_scene(nfc->scene_manager, NfcSceneSetSak);
 | 
				
			||||||
            return true;
 | 
					            return true;
 | 
				
			||||||
        } else if(event.event == SubmenuIndexNFCA4) {
 | 
					        } else if(event.event == SubmenuIndexNFCA4) {
 | 
				
			||||||
            nfc->dev.dev_data.nfc_data.uid_len = 4;
 | 
					            nfc->dev->dev_data.nfc_data.uid_len = 4;
 | 
				
			||||||
            nfc->dev.format = NfcDeviceSaveFormatUid;
 | 
					            nfc->dev->format = NfcDeviceSaveFormatUid;
 | 
				
			||||||
            scene_manager_next_scene(nfc->scene_manager, NfcSceneSetSak);
 | 
					            scene_manager_next_scene(nfc->scene_manager, NfcSceneSetSak);
 | 
				
			||||||
            return true;
 | 
					            return true;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
				
			|||||||
@ -14,7 +14,7 @@ void nfc_scene_set_uid_on_enter(void* context) {
 | 
				
			|||||||
    // Setup view
 | 
					    // Setup view
 | 
				
			||||||
    ByteInput* byte_input = nfc->byte_input;
 | 
					    ByteInput* byte_input = nfc->byte_input;
 | 
				
			||||||
    byte_input_set_header_text(byte_input, "Enter uid in hex");
 | 
					    byte_input_set_header_text(byte_input, "Enter uid in hex");
 | 
				
			||||||
    nfc->dev_edit_data = nfc->dev.dev_data.nfc_data;
 | 
					    nfc->dev_edit_data = nfc->dev->dev_data.nfc_data;
 | 
				
			||||||
    byte_input_set_result_callback(
 | 
					    byte_input_set_result_callback(
 | 
				
			||||||
        byte_input,
 | 
					        byte_input,
 | 
				
			||||||
        nfc_scene_set_uid_byte_input_callback,
 | 
					        nfc_scene_set_uid_byte_input_callback,
 | 
				
			||||||
 | 
				
			|||||||
@ -34,7 +34,7 @@ void nfc_scene_start_on_enter(void* context) {
 | 
				
			|||||||
    submenu_set_selected_item(
 | 
					    submenu_set_selected_item(
 | 
				
			||||||
        submenu, scene_manager_get_scene_state(nfc->scene_manager, NfcSceneStart));
 | 
					        submenu, scene_manager_get_scene_state(nfc->scene_manager, NfcSceneStart));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    nfc_device_clear(&nfc->dev);
 | 
					    nfc_device_clear(nfc->dev);
 | 
				
			||||||
    view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu);
 | 
					    view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -67,42 +67,14 @@ void bank_card_set_exp_date(BankCard* bank_card, uint8_t mon, uint8_t year) {
 | 
				
			|||||||
        bank_card->widget, 122, 54, AlignRight, AlignBottom, FontSecondary, exp_date_str);
 | 
					        bank_card->widget, 122, 54, AlignRight, AlignBottom, FontSecondary, exp_date_str);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void bank_card_set_country_name(BankCard* bank_card, uint16_t country_code) {
 | 
					void bank_card_set_country_name(BankCard* bank_card, const char* country_name) {
 | 
				
			||||||
    furi_assert(bank_card);
 | 
					    furi_assert(bank_card);
 | 
				
			||||||
    string_t country_name;
 | 
					 | 
				
			||||||
    string_init(country_name);
 | 
					 | 
				
			||||||
    if(nfc_emv_parser_get_country_name(country_code, country_name)) {
 | 
					 | 
				
			||||||
        string_t disp_country;
 | 
					 | 
				
			||||||
        string_init_printf(disp_country, "Reg:%s", country_name);
 | 
					 | 
				
			||||||
    widget_add_string_element(
 | 
					    widget_add_string_element(
 | 
				
			||||||
            bank_card->widget,
 | 
					        bank_card->widget, 120, 18, AlignRight, AlignTop, FontSecondary, country_name);
 | 
				
			||||||
            120,
 | 
					 | 
				
			||||||
            18,
 | 
					 | 
				
			||||||
            AlignRight,
 | 
					 | 
				
			||||||
            AlignTop,
 | 
					 | 
				
			||||||
            FontSecondary,
 | 
					 | 
				
			||||||
            string_get_cstr(disp_country));
 | 
					 | 
				
			||||||
        string_clear(disp_country);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    string_clear(country_name);
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void bank_card_set_currency_name(BankCard* bank_card, uint16_t currency_code) {
 | 
					void bank_card_set_currency_name(BankCard* bank_card, const char* currency_name) {
 | 
				
			||||||
    furi_assert(bank_card);
 | 
					    furi_assert(bank_card);
 | 
				
			||||||
    string_t currency_name;
 | 
					 | 
				
			||||||
    string_init(currency_name);
 | 
					 | 
				
			||||||
    if(nfc_emv_parser_get_currency_name(currency_code, currency_name)) {
 | 
					 | 
				
			||||||
        string_t disp_currency;
 | 
					 | 
				
			||||||
        string_init_printf(disp_currency, "Cur:%s", currency_name);
 | 
					 | 
				
			||||||
    widget_add_string_element(
 | 
					    widget_add_string_element(
 | 
				
			||||||
            bank_card->widget,
 | 
					        bank_card->widget, 31, 18, AlignLeft, AlignTop, FontSecondary, currency_name);
 | 
				
			||||||
            31,
 | 
					 | 
				
			||||||
            18,
 | 
					 | 
				
			||||||
            AlignLeft,
 | 
					 | 
				
			||||||
            AlignTop,
 | 
					 | 
				
			||||||
            FontSecondary,
 | 
					 | 
				
			||||||
            string_get_cstr(disp_currency));
 | 
					 | 
				
			||||||
        string_clear(disp_currency);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    string_clear(currency_name);
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -21,6 +21,6 @@ void bank_card_set_number(BankCard* bank_card, uint8_t* number, uint8_t len);
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
void bank_card_set_exp_date(BankCard* bank_card, uint8_t mon, uint8_t year);
 | 
					void bank_card_set_exp_date(BankCard* bank_card, uint8_t mon, uint8_t year);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void bank_card_set_country_name(BankCard* bank_card, uint16_t country_code);
 | 
					void bank_card_set_country_name(BankCard* bank_card, const char* country_name);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void bank_card_set_currency_name(BankCard* bank_card, uint16_t currency_code);
 | 
					void bank_card_set_currency_name(BankCard* bank_card, const char* currency_name);
 | 
				
			||||||
 | 
				
			|||||||
@ -1,148 +1,151 @@
 | 
				
			|||||||
A00000000305076010 VISA ELO Credit
 | 
					Filetype: Flipper EMV resources
 | 
				
			||||||
A0000000031010 VISA Debit/Credit (Classic)
 | 
					Version: 1
 | 
				
			||||||
A000000003101001 VISA Credit
 | 
					# EMV Application ID code: Application ID name
 | 
				
			||||||
A000000003101002 VISA Debit
 | 
					A00000000305076010: VISA ELO Credit
 | 
				
			||||||
A0000000032010 VISA Electron
 | 
					A0000000031010: VISA Debit/Credit (Classic)
 | 
				
			||||||
A0000000032020 VISA
 | 
					A000000003101001: VISA Credit
 | 
				
			||||||
A0000000033010 VISA Interlink
 | 
					A000000003101002: VISA Debit
 | 
				
			||||||
A0000000034010 VISA Specific
 | 
					A0000000032010: VISA Electron
 | 
				
			||||||
A0000000035010 VISA Specific
 | 
					A0000000032020: VISA
 | 
				
			||||||
A0000000036010 Domestic Visa Cash
 | 
					A0000000033010: VISA Interlink
 | 
				
			||||||
A0000000036020 International Visa Cash
 | 
					A0000000034010: VISA Specific
 | 
				
			||||||
A0000000038002 VISA Auth EMV-CAP (DPA)
 | 
					A0000000035010: VISA Specific
 | 
				
			||||||
A0000000038010 VISA Plus
 | 
					A0000000036010: Domestic Visa Cash
 | 
				
			||||||
A0000000039010 VISA Loyalty
 | 
					A0000000036020: International Visa Cash
 | 
				
			||||||
A000000003999910 VISA Proprietary ATM
 | 
					A0000000038002: VISA Auth EMV-CAP (DPA)
 | 
				
			||||||
A00000000401 MasterCard PayPass
 | 
					A0000000038010: VISA Plus
 | 
				
			||||||
A0000000041010 MasterCard Global
 | 
					A0000000039010: VISA Loyalty
 | 
				
			||||||
A00000000410101213 MasterCard Credit
 | 
					A000000003999910: VISA Proprietary ATM
 | 
				
			||||||
A00000000410101215 MasterCard Credit
 | 
					A00000000401: MasterCard PayPass
 | 
				
			||||||
A0000000042010 MasterCard Specific
 | 
					A0000000041010: MasterCard Global
 | 
				
			||||||
A0000000043010 MasterCard Specific
 | 
					A00000000410101213: MasterCard Credit
 | 
				
			||||||
A0000000043060 Maestro (Debit)
 | 
					A00000000410101215: MasterCard Credit
 | 
				
			||||||
A000000004306001 Maestro (Debit)
 | 
					A0000000042010: MasterCard Specific
 | 
				
			||||||
A0000000044010 MasterCard Specific
 | 
					A0000000043010: MasterCard Specific
 | 
				
			||||||
A0000000045010 MasterCard Specific
 | 
					A0000000043060: Maestro (Debit)
 | 
				
			||||||
A0000000046000 Cirrus
 | 
					A000000004306001: Maestro (Debit)
 | 
				
			||||||
A0000000048002 SecureCode EMV-CAP
 | 
					A0000000044010: MasterCard Specific
 | 
				
			||||||
A0000000049999 MasterCard PayPass
 | 
					A0000000045010: MasterCard Specific
 | 
				
			||||||
A0000000050001 Maestro UK
 | 
					A0000000046000: Cirrus
 | 
				
			||||||
A0000000050002 Solo
 | 
					A0000000048002: SecureCode EMV-CAP
 | 
				
			||||||
A00000002401 Self Service
 | 
					A0000000049999: MasterCard PayPass
 | 
				
			||||||
A000000025 American Express
 | 
					A0000000050001: Maestro UK
 | 
				
			||||||
A0000000250000 American Express
 | 
					A0000000050002: Solo
 | 
				
			||||||
A00000002501 American Express
 | 
					A00000002401: Self Service
 | 
				
			||||||
A000000025010402 American Express
 | 
					A000000025: American Express
 | 
				
			||||||
A000000025010701 ExpressPay
 | 
					A0000000250000: American Express
 | 
				
			||||||
A000000025010801 American Express
 | 
					A00000002501: American Express
 | 
				
			||||||
A0000000291010 Link / American Express
 | 
					A000000025010402: American Express
 | 
				
			||||||
A0000000421010 Cartes Bancaire EMV Card
 | 
					A000000025010701: ExpressPay
 | 
				
			||||||
A0000000426010 Apple Pay
 | 
					A000000025010801: American Express
 | 
				
			||||||
A00000006510 JCB
 | 
					A0000000291010: Link / American Express
 | 
				
			||||||
A0000000651010 JCB J Smart Credit
 | 
					A0000000421010: Cartes Bancaire EMV Card
 | 
				
			||||||
A00000006900 Moneo
 | 
					A0000000426010: Apple Pay
 | 
				
			||||||
A000000077010000021000000000003B Visa AEPN
 | 
					A00000006510: JCB
 | 
				
			||||||
A000000098 Debit Card
 | 
					A0000000651010: JCB J Smart Credit
 | 
				
			||||||
A0000000980848 Debit Card
 | 
					A00000006900: Moneo
 | 
				
			||||||
A0000001211010 Dankort VISA GEM Vision
 | 
					A000000077010000021000000000003B: Visa AEPN
 | 
				
			||||||
A0000001410001 PagoBANCOMAT
 | 
					A000000098: Debit Card
 | 
				
			||||||
A0000001523010 Discover, Pulse D Pas
 | 
					A0000000980848: Debit Card
 | 
				
			||||||
A0000001524010 Discover
 | 
					A0000001211010: Dankort VISA GEM Vision
 | 
				
			||||||
A0000001544442 Banricompras Debito
 | 
					A0000001410001: PagoBANCOMAT
 | 
				
			||||||
A000000172950001 BAROC Taiwan
 | 
					A0000001523010: Discover, Pulse D Pas
 | 
				
			||||||
A0000002281010 SPAN (M/Chip)
 | 
					A0000001524010: Discover
 | 
				
			||||||
A0000002282010 SPAN (VIS)
 | 
					A0000001544442: Banricompras Debito
 | 
				
			||||||
A0000002771010 INTERAC
 | 
					A000000172950001: BAROC Taiwan
 | 
				
			||||||
A00000031510100528 Currence PuC
 | 
					A0000002281010: SPAN (M/Chip)
 | 
				
			||||||
A0000003156020 Chipknip
 | 
					A0000002282010: SPAN (VIS)
 | 
				
			||||||
A0000003591010028001 Girocard EAPS
 | 
					A0000002771010: INTERAC
 | 
				
			||||||
A0000003710001 InterSwitch Verve Card
 | 
					A00000031510100528: Currence PuC
 | 
				
			||||||
A0000004540010 Etranzact Genesis Card
 | 
					A0000003156020: Chipknip
 | 
				
			||||||
A0000004540011 Etranzact Genesis Card 2
 | 
					A0000003591010028001: Girocard EAPS
 | 
				
			||||||
A0000004766C GOOGLE_PAYMENT
 | 
					A0000003710001: InterSwitch Verve Card
 | 
				
			||||||
A0000005241010 RuPay
 | 
					A0000004540010: Etranzact Genesis Card
 | 
				
			||||||
A0000006723010 TROY chip credit card
 | 
					A0000004540011: Etranzact Genesis Card 2
 | 
				
			||||||
A0000006723020 TROY chip debit card
 | 
					A0000004766C: GOOGLE_PAYMENT
 | 
				
			||||||
A0000007705850 XTRAPOWER
 | 
					A0000005241010: RuPay
 | 
				
			||||||
B012345678 Maestro TEST
 | 
					A0000006723010: TROY chip credit card
 | 
				
			||||||
D27600002545500100 Girocard
 | 
					A0000006723020: TROY chip debit card
 | 
				
			||||||
D5780000021010 Bankaxept
 | 
					A0000007705850: XTRAPOWER
 | 
				
			||||||
F0000000030001 BRADESCO
 | 
					B012345678: Maestro TEST
 | 
				
			||||||
A000000003000000 (VISA) Card Manager
 | 
					D27600002545500100: Girocard
 | 
				
			||||||
A000000003534441 Schlumberger SD
 | 
					D5780000021010: Bankaxept
 | 
				
			||||||
A0000000035350 Security Domain
 | 
					F0000000030001: BRADESCO
 | 
				
			||||||
A000000003535041 Security Domain
 | 
					A000000003000000: (VISA) Card Manager
 | 
				
			||||||
A0000000040000 MasterCard Card Manager
 | 
					A000000003534441: Schlumberger SD
 | 
				
			||||||
A000000018434D Gemplus card manager
 | 
					A0000000035350: Security Domain
 | 
				
			||||||
A000000018434D00 Gemplus Security Domain
 | 
					A000000003535041: Security Domain
 | 
				
			||||||
A0000000960200 Proton WISD
 | 
					A0000000040000: MasterCard Card Manager
 | 
				
			||||||
A0000001510000 Global Platform SD
 | 
					A000000018434D: Gemplus card manager
 | 
				
			||||||
A00000015153504341534400 CASD_AID
 | 
					A000000018434D00: Gemplus Security Domain
 | 
				
			||||||
A000000476A010 GSD_MANAGER_AID
 | 
					A0000000960200: Proton WISD
 | 
				
			||||||
A000000476A110 GSD_MANAGER_AID
 | 
					A0000001510000: Global Platform SD
 | 
				
			||||||
315041592E5359532E4444463031 Visa PSE 
 | 
					A00000015153504341534400: CASD_AID
 | 
				
			||||||
325041592E5359532E4444463031 Visa PPSE
 | 
					A000000476A010: GSD_MANAGER_AID
 | 
				
			||||||
A0000000042203 MasterCard Specific
 | 
					A000000476A110: GSD_MANAGER_AID
 | 
				
			||||||
A0000000045555 APDULogger
 | 
					315041592E5359532E4444463031: Visa PSE 
 | 
				
			||||||
A0000000090001FF44FF1289 Orange
 | 
					325041592E5359532E4444463031: Visa PPSE
 | 
				
			||||||
A0000000101030 Maestro-CH
 | 
					A0000000042203: MasterCard Specific
 | 
				
			||||||
A00000001800 Gemplus
 | 
					A0000000045555: APDULogger
 | 
				
			||||||
A0000000181001 gemplus util packages
 | 
					A0000000090001FF44FF1289: Orange
 | 
				
			||||||
A000000025010104 American Express
 | 
					A0000000101030: Maestro-CH
 | 
				
			||||||
A00000002949034010100001 HSBC
 | 
					A00000001800: Gemplus
 | 
				
			||||||
A00000002949282010100000 Barclay
 | 
					A0000000181001: gemplus util packages
 | 
				
			||||||
A00000005945430100 Girocard Electronic Cash
 | 
					A000000025010104: American Express
 | 
				
			||||||
A0000000980840 Visa Common Debit
 | 
					A00000002949034010100001: HSBC
 | 
				
			||||||
A0000001570010 AMEX
 | 
					A00000002949282010100000: Barclay
 | 
				
			||||||
A0000001570020 MasterCard
 | 
					A00000005945430100: Girocard Electronic Cash
 | 
				
			||||||
A0000001570021 Maestro
 | 
					A0000000980840: Visa Common Debit
 | 
				
			||||||
A0000001570022 Maestro
 | 
					A0000001570010: AMEX
 | 
				
			||||||
A0000001570023 CASH
 | 
					A0000001570020: MasterCard
 | 
				
			||||||
A0000001570030 VISA
 | 
					A0000001570021: Maestro
 | 
				
			||||||
A0000001570031 VISA
 | 
					A0000001570022: Maestro
 | 
				
			||||||
A0000001570040 JCB
 | 
					A0000001570023: CASH
 | 
				
			||||||
A0000001570050 Postcard
 | 
					A0000001570030: VISA
 | 
				
			||||||
A0000001570051 Postcard
 | 
					A0000001570031: VISA
 | 
				
			||||||
A0000001570100 MCard
 | 
					A0000001570040: JCB
 | 
				
			||||||
A0000001570104 MyOne
 | 
					A0000001570050: Postcard
 | 
				
			||||||
A000000157010C WIRCard
 | 
					A0000001570051: Postcard
 | 
				
			||||||
A000000157010D Power Card
 | 
					A0000001570100: MCard
 | 
				
			||||||
A0000001574443 DINERS CLUB
 | 
					A0000001570104: MyOne
 | 
				
			||||||
A0000001574444 Supercard Plus
 | 
					A000000157010C: WIRCard
 | 
				
			||||||
A00000022820101010 SPAN
 | 
					A000000157010D: Power Card
 | 
				
			||||||
A000000308000010000100 ID-ONE PIV BIO
 | 
					A0000001574443: DINERS CLUB
 | 
				
			||||||
A0000003241010 Discover Zip
 | 
					A0000001574444: Supercard Plus
 | 
				
			||||||
A000000333010101 UnionPay Debit
 | 
					A00000022820101010: SPAN
 | 
				
			||||||
A000000333010102 UnionPay Credit
 | 
					A000000308000010000100: ID-ONE PIV BIO
 | 
				
			||||||
A000000333010103 UnionPay Quasi Credit
 | 
					A0000003241010: Discover Zip
 | 
				
			||||||
A000000333010106 UnionPay Electronic Cash
 | 
					A000000333010101: UnionPay Debit
 | 
				
			||||||
A000000333010108 U.S. UnionPay Common Debit
 | 
					A000000333010102: UnionPay Credit
 | 
				
			||||||
A000000337102000 Classic
 | 
					A000000333010103: UnionPay Quasi Credit
 | 
				
			||||||
A000000337101001 Prepaye Online
 | 
					A000000333010106: UnionPay Electronic Cash
 | 
				
			||||||
A000000337102001 Prepaye Possibile Offiline
 | 
					A000000333010108: U.S. UnionPay Common Debit
 | 
				
			||||||
A000000337601001 Porte Monnaie Electronique
 | 
					A000000337102000: Classic
 | 
				
			||||||
A0000006581010 MIR Credit
 | 
					A000000337101001: Prepaye Online
 | 
				
			||||||
A0000006581011 MIR Credit
 | 
					A000000337102001: Prepaye Possibile Offiline
 | 
				
			||||||
A0000006582010 MIR Debit
 | 
					A000000337601001: Porte Monnaie Electronique
 | 
				
			||||||
D040000001000002 Paylife Quick IEP
 | 
					A0000006581010: MIR Credit
 | 
				
			||||||
D040000002000002 RFU
 | 
					A0000006581011: MIR Credit
 | 
				
			||||||
D040000003000002 POS
 | 
					A0000006582010: MIR Debit
 | 
				
			||||||
D040000004000002 ATM
 | 
					D040000001000002: Paylife Quick IEP
 | 
				
			||||||
D04000000B000002 Retail
 | 
					D040000002000002: RFU
 | 
				
			||||||
D04000000C000002 Bank_Data
 | 
					D040000003000002: POS
 | 
				
			||||||
D04000000D000002 Shopping
 | 
					D040000004000002: ATM
 | 
				
			||||||
D040000013000001 DF_UNI_Kepler1
 | 
					D04000000B000002: Retail
 | 
				
			||||||
D040000013000001 DF_Schüler1
 | 
					D04000000C000002: Bank_Data
 | 
				
			||||||
D040000013000002 DF_UNI_Kepler2
 | 
					D04000000D000002: Shopping
 | 
				
			||||||
D040000013000002 DF_Schüler2
 | 
					D040000013000001: DF_UNI_Kepler1
 | 
				
			||||||
D040000014000001 DF_Mensa
 | 
					D040000013000001: DF_Schüler1
 | 
				
			||||||
D040000015000001 DF_UNI_Ausweis
 | 
					D040000013000002: DF_UNI_Kepler2
 | 
				
			||||||
D040000015000001 DF_Ausweis
 | 
					D040000013000002: DF_Schüler2
 | 
				
			||||||
D0400000190001 EMV ATM Maestro
 | 
					D040000014000001: DF_Mensa
 | 
				
			||||||
D0400000190002 EMV POS Maestro
 | 
					D040000015000001: DF_UNI_Ausweis
 | 
				
			||||||
D0400000190003 EMV ATM MasterCard
 | 
					D040000015000001: DF_Ausweis
 | 
				
			||||||
D0400000190004 EMV POS MasterCard
 | 
					D0400000190001: EMV ATM Maestro
 | 
				
			||||||
D276000025 Girocard
 | 
					D0400000190002: EMV POS Maestro
 | 
				
			||||||
D27600002547410100 Girocard ATM
 | 
					D0400000190003: EMV ATM MasterCard
 | 
				
			||||||
D7560000010101 Reka Card
 | 
					D0400000190004: EMV POS MasterCard
 | 
				
			||||||
D7560000300101 M Budget
 | 
					D276000025: Girocard
 | 
				
			||||||
 | 
					D27600002547410100: Girocard ATM
 | 
				
			||||||
 | 
					D7560000010101: Reka Card
 | 
				
			||||||
 | 
					D7560000300101: M Budget
 | 
				
			||||||
@ -1,249 +1,252 @@
 | 
				
			|||||||
0004 AFG
 | 
					Filetype: Flipper EMV resources
 | 
				
			||||||
0008 ALB
 | 
					Version: 1
 | 
				
			||||||
0010 ATA
 | 
					# EMV country code: country name
 | 
				
			||||||
0012 DZA
 | 
					0004: AFG
 | 
				
			||||||
0016 ASM
 | 
					0008: ALB
 | 
				
			||||||
0020 AND
 | 
					0010: ATA
 | 
				
			||||||
0024 AGO
 | 
					0012: DZA
 | 
				
			||||||
0028 ATG
 | 
					0016: ASM
 | 
				
			||||||
0031 AZE
 | 
					0020: AND
 | 
				
			||||||
0032 ARG
 | 
					0024: AGO
 | 
				
			||||||
0036 AUS
 | 
					0028: ATG
 | 
				
			||||||
0040 AUT
 | 
					0031: AZE
 | 
				
			||||||
0044 BHS
 | 
					0032: ARG
 | 
				
			||||||
0048 BHR
 | 
					0036: AUS
 | 
				
			||||||
0050 BGD
 | 
					0040: AUT
 | 
				
			||||||
0051 ARM
 | 
					0044: BHS
 | 
				
			||||||
0052 BRB
 | 
					0048: BHR
 | 
				
			||||||
0056 BEL
 | 
					0050: BGD
 | 
				
			||||||
0060 BMU
 | 
					0051: ARM
 | 
				
			||||||
0064 BTN
 | 
					0052: BRB
 | 
				
			||||||
0068 BOL
 | 
					0056: BEL
 | 
				
			||||||
0070 BIH
 | 
					0060: BMU
 | 
				
			||||||
0072 BWA
 | 
					0064: BTN
 | 
				
			||||||
0074 BVT
 | 
					0068: BOL
 | 
				
			||||||
0076 BRA
 | 
					0070: BIH
 | 
				
			||||||
0084 BLZ
 | 
					0072: BWA
 | 
				
			||||||
0086 IOT
 | 
					0074: BVT
 | 
				
			||||||
0090 SLB
 | 
					0076: BRA
 | 
				
			||||||
0092 VGB
 | 
					0084: BLZ
 | 
				
			||||||
0096 BRN
 | 
					0086: IOT
 | 
				
			||||||
0100 BGR
 | 
					0090: SLB
 | 
				
			||||||
0104 MMR
 | 
					0092: VGB
 | 
				
			||||||
0108 BDI
 | 
					0096: BRN
 | 
				
			||||||
0112 BLR
 | 
					0100: BGR
 | 
				
			||||||
0116 KHM
 | 
					0104: MMR
 | 
				
			||||||
0120 CMR
 | 
					0108: BDI
 | 
				
			||||||
0124 CAN
 | 
					0112: BLR
 | 
				
			||||||
0132 CPV
 | 
					0116: KHM
 | 
				
			||||||
0136 CYM
 | 
					0120: CMR
 | 
				
			||||||
0140 CAF
 | 
					0124: CAN
 | 
				
			||||||
0144 LKA
 | 
					0132: CPV
 | 
				
			||||||
0148 TCD
 | 
					0136: CYM
 | 
				
			||||||
0152 CHL
 | 
					0140: CAF
 | 
				
			||||||
0156 CHN
 | 
					0144: LKA
 | 
				
			||||||
0158 TWN
 | 
					0148: TCD
 | 
				
			||||||
0162 CXR
 | 
					0152: CHL
 | 
				
			||||||
0166 CCK
 | 
					0156: CHN
 | 
				
			||||||
0170 COL
 | 
					0158: TWN
 | 
				
			||||||
0174 COM
 | 
					0162: CXR
 | 
				
			||||||
0175 MYT
 | 
					0166: CCK
 | 
				
			||||||
0178 COG
 | 
					0170: COL
 | 
				
			||||||
0180 COD
 | 
					0174: COM
 | 
				
			||||||
0184 COK
 | 
					0175: MYT
 | 
				
			||||||
0188 CRI
 | 
					0178: COG
 | 
				
			||||||
0191 HRV
 | 
					0180: COD
 | 
				
			||||||
0192 CUB
 | 
					0184: COK
 | 
				
			||||||
0196 CYP
 | 
					0188: CRI
 | 
				
			||||||
0203 CZE
 | 
					0191: HRV
 | 
				
			||||||
0204 BEN
 | 
					0192: CUB
 | 
				
			||||||
0208 DNK
 | 
					0196: CYP
 | 
				
			||||||
0212 DMA
 | 
					0203: CZE
 | 
				
			||||||
0214 DOM
 | 
					0204: BEN
 | 
				
			||||||
0218 ECU
 | 
					0208: DNK
 | 
				
			||||||
0222 SLV
 | 
					0212: DMA
 | 
				
			||||||
0226 GNQ
 | 
					0214: DOM
 | 
				
			||||||
0231 ETH
 | 
					0218: ECU
 | 
				
			||||||
0232 ERI
 | 
					0222: SLV
 | 
				
			||||||
0233 EST
 | 
					0226: GNQ
 | 
				
			||||||
0234 FRO
 | 
					0231: ETH
 | 
				
			||||||
0238 FLK
 | 
					0232: ERI
 | 
				
			||||||
0239 SGS
 | 
					0233: EST
 | 
				
			||||||
0242 FJI
 | 
					0234: FRO
 | 
				
			||||||
0246 FIN
 | 
					0238: FLK
 | 
				
			||||||
0248 ALA
 | 
					0239: SGS
 | 
				
			||||||
0250 FRA
 | 
					0242: FJI
 | 
				
			||||||
0254 GUF
 | 
					0246: FIN
 | 
				
			||||||
0258 PYF
 | 
					0248: ALA
 | 
				
			||||||
0260 ATF
 | 
					0250: FRA
 | 
				
			||||||
0262 DJI
 | 
					0254: GUF
 | 
				
			||||||
0266 GAB
 | 
					0258: PYF
 | 
				
			||||||
0268 GEO
 | 
					0260: ATF
 | 
				
			||||||
0270 GMB
 | 
					0262: DJI
 | 
				
			||||||
0275 PSE
 | 
					0266: GAB
 | 
				
			||||||
0276 DEU
 | 
					0268: GEO
 | 
				
			||||||
0288 GHA
 | 
					0270: GMB
 | 
				
			||||||
0292 GIB
 | 
					0275: PSE
 | 
				
			||||||
0296 KIR
 | 
					0276: DEU
 | 
				
			||||||
0300 GRC
 | 
					0288: GHA
 | 
				
			||||||
0304 GRL
 | 
					0292: GIB
 | 
				
			||||||
0308 GRD
 | 
					0296: KIR
 | 
				
			||||||
0312 GLP
 | 
					0300: GRC
 | 
				
			||||||
0316 GUM
 | 
					0304: GRL
 | 
				
			||||||
0320 GTM
 | 
					0308: GRD
 | 
				
			||||||
0324 GIN
 | 
					0312: GLP
 | 
				
			||||||
0328 GUY
 | 
					0316: GUM
 | 
				
			||||||
0332 HTI
 | 
					0320: GTM
 | 
				
			||||||
0334 HMD
 | 
					0324: GIN
 | 
				
			||||||
0336 VAT
 | 
					0328: GUY
 | 
				
			||||||
0340 HND
 | 
					0332: HTI
 | 
				
			||||||
0344 HKG
 | 
					0334: HMD
 | 
				
			||||||
0348 HUN
 | 
					0336: VAT
 | 
				
			||||||
0352 ISL
 | 
					0340: HND
 | 
				
			||||||
0356 IND
 | 
					0344: HKG
 | 
				
			||||||
0360 IDN
 | 
					0348: HUN
 | 
				
			||||||
0364 IRN
 | 
					0352: ISL
 | 
				
			||||||
0368 IRQ
 | 
					0356: IND
 | 
				
			||||||
0372 IRL
 | 
					0360: IDN
 | 
				
			||||||
0376 ISR
 | 
					0364: IRN
 | 
				
			||||||
0380 ITA
 | 
					0368: IRQ
 | 
				
			||||||
0384 CIV
 | 
					0372: IRL
 | 
				
			||||||
0388 JAM
 | 
					0376: ISR
 | 
				
			||||||
0392 JPN
 | 
					0380: ITA
 | 
				
			||||||
0398 KAZ
 | 
					0384: CIV
 | 
				
			||||||
0400 JOR
 | 
					0388: JAM
 | 
				
			||||||
0404 KEN
 | 
					0392: JPN
 | 
				
			||||||
0408 PRK
 | 
					0398: KAZ
 | 
				
			||||||
0410 KOR
 | 
					0400: JOR
 | 
				
			||||||
0414 KWT
 | 
					0404: KEN
 | 
				
			||||||
0417 KGZ
 | 
					0408: PRK
 | 
				
			||||||
0418 LAO
 | 
					0410: KOR
 | 
				
			||||||
0422 LBN
 | 
					0414: KWT
 | 
				
			||||||
0426 LSO
 | 
					0417: KGZ
 | 
				
			||||||
0428 LVA
 | 
					0418: LAO
 | 
				
			||||||
0430 LBR
 | 
					0422: LBN
 | 
				
			||||||
0434 LBY
 | 
					0426: LSO
 | 
				
			||||||
0438 LIE
 | 
					0428: LVA
 | 
				
			||||||
0440 LTU
 | 
					0430: LBR
 | 
				
			||||||
0442 LUX
 | 
					0434: LBY
 | 
				
			||||||
0446 MAC
 | 
					0438: LIE
 | 
				
			||||||
0450 MDG
 | 
					0440: LTU
 | 
				
			||||||
0454 MWI
 | 
					0442: LUX
 | 
				
			||||||
0458 MYS
 | 
					0446: MAC
 | 
				
			||||||
0462 MDV
 | 
					0450: MDG
 | 
				
			||||||
0466 MLI
 | 
					0454: MWI
 | 
				
			||||||
0470 MLT
 | 
					0458: MYS
 | 
				
			||||||
0474 MTQ
 | 
					0462: MDV
 | 
				
			||||||
0478 MRT
 | 
					0466: MLI
 | 
				
			||||||
0480 MUS
 | 
					0470: MLT
 | 
				
			||||||
0484 MEX
 | 
					0474: MTQ
 | 
				
			||||||
0492 MCO
 | 
					0478: MRT
 | 
				
			||||||
0496 MNG
 | 
					0480: MUS
 | 
				
			||||||
0498 MDA
 | 
					0484: MEX
 | 
				
			||||||
0499 MNE
 | 
					0492: MCO
 | 
				
			||||||
0500 MSR
 | 
					0496: MNG
 | 
				
			||||||
0504 MAR
 | 
					0498: MDA
 | 
				
			||||||
0508 MOZ
 | 
					0499: MNE
 | 
				
			||||||
0512 OMN
 | 
					0500: MSR
 | 
				
			||||||
0516 NAM
 | 
					0504: MAR
 | 
				
			||||||
0520 NRU
 | 
					0508: MOZ
 | 
				
			||||||
0524 NPL
 | 
					0512: OMN
 | 
				
			||||||
0528 NLD
 | 
					0516: NAM
 | 
				
			||||||
0531 CUW
 | 
					0520: NRU
 | 
				
			||||||
0533 ABW
 | 
					0524: NPL
 | 
				
			||||||
0534 SXM
 | 
					0528: NLD
 | 
				
			||||||
0535 BES
 | 
					0531: CUW
 | 
				
			||||||
0540 NCL
 | 
					0533: ABW
 | 
				
			||||||
0548 VUT
 | 
					0534: SXM
 | 
				
			||||||
0554 NZL
 | 
					0535: BES
 | 
				
			||||||
0558 NIC
 | 
					0540: NCL
 | 
				
			||||||
0562 NER
 | 
					0548: VUT
 | 
				
			||||||
0566 NGA
 | 
					0554: NZL
 | 
				
			||||||
0570 NIU
 | 
					0558: NIC
 | 
				
			||||||
0574 NFK
 | 
					0562: NER
 | 
				
			||||||
0578 NOR
 | 
					0566: NGA
 | 
				
			||||||
0580 MNP
 | 
					0570: NIU
 | 
				
			||||||
0581 UMI
 | 
					0574: NFK
 | 
				
			||||||
0583 FSM
 | 
					0578: NOR
 | 
				
			||||||
0584 MHL
 | 
					0580: MNP
 | 
				
			||||||
0585 PLW
 | 
					0581: UMI
 | 
				
			||||||
0586 PAK
 | 
					0583: FSM
 | 
				
			||||||
0591 PAN
 | 
					0584: MHL
 | 
				
			||||||
0598 PNG
 | 
					0585: PLW
 | 
				
			||||||
0600 PRY
 | 
					0586: PAK
 | 
				
			||||||
0604 PER
 | 
					0591: PAN
 | 
				
			||||||
0608 PHL
 | 
					0598: PNG
 | 
				
			||||||
0612 PCN
 | 
					0600: PRY
 | 
				
			||||||
0616 POL
 | 
					0604: PER
 | 
				
			||||||
0620 PRT
 | 
					0608: PHL
 | 
				
			||||||
0624 GNB
 | 
					0612: PCN
 | 
				
			||||||
0626 TLS
 | 
					0616: POL
 | 
				
			||||||
0630 PRI
 | 
					0620: PRT
 | 
				
			||||||
0634 QAT
 | 
					0624: GNB
 | 
				
			||||||
0638 REU
 | 
					0626: TLS
 | 
				
			||||||
0642 ROU
 | 
					0630: PRI
 | 
				
			||||||
0643 RUS
 | 
					0634: QAT
 | 
				
			||||||
0646 RWA
 | 
					0638: REU
 | 
				
			||||||
0652 BLM
 | 
					0642: ROU
 | 
				
			||||||
0654 SHN
 | 
					0643: RUS
 | 
				
			||||||
0659 KNA
 | 
					0646: RWA
 | 
				
			||||||
0660 AIA
 | 
					0652: BLM
 | 
				
			||||||
0662 LCA
 | 
					0654: SHN
 | 
				
			||||||
0663 MAF
 | 
					0659: KNA
 | 
				
			||||||
0666 SPM
 | 
					0660: AIA
 | 
				
			||||||
0670 VCT
 | 
					0662: LCA
 | 
				
			||||||
0674 SMR
 | 
					0663: MAF
 | 
				
			||||||
0678 STP
 | 
					0666: SPM
 | 
				
			||||||
0682 SAU
 | 
					0670: VCT
 | 
				
			||||||
0686 SEN
 | 
					0674: SMR
 | 
				
			||||||
0688 SRB
 | 
					0678: STP
 | 
				
			||||||
0690 SYC
 | 
					0682: SAU
 | 
				
			||||||
0694 SLE
 | 
					0686: SEN
 | 
				
			||||||
0702 SGP
 | 
					0688: SRB
 | 
				
			||||||
0703 SVK
 | 
					0690: SYC
 | 
				
			||||||
0704 VNM
 | 
					0694: SLE
 | 
				
			||||||
0705 SVN
 | 
					0702: SGP
 | 
				
			||||||
0706 SOM
 | 
					0703: SVK
 | 
				
			||||||
0710 ZAF
 | 
					0704: VNM
 | 
				
			||||||
0716 ZWE
 | 
					0705: SVN
 | 
				
			||||||
0724 ESP
 | 
					0706: SOM
 | 
				
			||||||
0728 SSD
 | 
					0710: ZAF
 | 
				
			||||||
0729 SDN
 | 
					0716: ZWE
 | 
				
			||||||
0732 ESH
 | 
					0724: ESP
 | 
				
			||||||
0740 SUR
 | 
					0728: SSD
 | 
				
			||||||
0744 SJM
 | 
					0729: SDN
 | 
				
			||||||
0748 SWZ
 | 
					0732: ESH
 | 
				
			||||||
0752 SWE
 | 
					0740: SUR
 | 
				
			||||||
0756 CHE
 | 
					0744: SJM
 | 
				
			||||||
0760 SYR
 | 
					0748: SWZ
 | 
				
			||||||
0762 TJK
 | 
					0752: SWE
 | 
				
			||||||
0764 THA
 | 
					0756: CHE
 | 
				
			||||||
0768 TGO
 | 
					0760: SYR
 | 
				
			||||||
0772 TKL
 | 
					0762: TJK
 | 
				
			||||||
0776 TON
 | 
					0764: THA
 | 
				
			||||||
0780 TTO
 | 
					0768: TGO
 | 
				
			||||||
0784 ARE
 | 
					0772: TKL
 | 
				
			||||||
0788 TUN
 | 
					0776: TON
 | 
				
			||||||
0792 TUR
 | 
					0780: TTO
 | 
				
			||||||
0795 TKM
 | 
					0784: ARE
 | 
				
			||||||
0796 TCA
 | 
					0788: TUN
 | 
				
			||||||
0798 TUV
 | 
					0792: TUR
 | 
				
			||||||
0800 UGA
 | 
					0795: TKM
 | 
				
			||||||
0804 UKR
 | 
					0796: TCA
 | 
				
			||||||
0807 MKD
 | 
					0798: TUV
 | 
				
			||||||
0818 EGY
 | 
					0800: UGA
 | 
				
			||||||
0826 GBR
 | 
					0804: UKR
 | 
				
			||||||
0831 GGY
 | 
					0807: MKD
 | 
				
			||||||
0832 JEY
 | 
					0818: EGY
 | 
				
			||||||
0833 IMN
 | 
					0826: GBR
 | 
				
			||||||
0834 TZA
 | 
					0831: GGY
 | 
				
			||||||
0840 USA
 | 
					0832: JEY
 | 
				
			||||||
0850 VIR
 | 
					0833: IMN
 | 
				
			||||||
0854 BFA
 | 
					0834: TZA
 | 
				
			||||||
0858 URY
 | 
					0840: USA
 | 
				
			||||||
0860 UZB
 | 
					0850: VIR
 | 
				
			||||||
0862 VEN
 | 
					0854: BFA
 | 
				
			||||||
0876 WLF
 | 
					0858: URY
 | 
				
			||||||
0882 WSM
 | 
					0860: UZB
 | 
				
			||||||
0887 YEM
 | 
					0862: VEN
 | 
				
			||||||
0894 ZMB
 | 
					0876: WLF
 | 
				
			||||||
 | 
					0882: WSM
 | 
				
			||||||
 | 
					0887: YEM
 | 
				
			||||||
 | 
					0894: ZMB
 | 
				
			||||||
@ -1,168 +1,171 @@
 | 
				
			|||||||
0997 USN
 | 
					Filetype: Flipper EMV resources
 | 
				
			||||||
0994 XSU
 | 
					Version: 1
 | 
				
			||||||
0990 CLF
 | 
					# EMV currency code: currency name
 | 
				
			||||||
0986 BRL
 | 
					0997: USN
 | 
				
			||||||
0985 PLN
 | 
					0994: XSU
 | 
				
			||||||
0984 BOV
 | 
					0990: CLF
 | 
				
			||||||
0981 GEL
 | 
					0986: BRL
 | 
				
			||||||
0980 UAH
 | 
					0985: PLN
 | 
				
			||||||
0979 MXV
 | 
					0984: BOV
 | 
				
			||||||
0978 EUR
 | 
					0981: GEL
 | 
				
			||||||
0977 BAM
 | 
					0980: UAH
 | 
				
			||||||
0976 CDF
 | 
					0979: MXV
 | 
				
			||||||
0975 BGN
 | 
					0978: EUR
 | 
				
			||||||
0973 AOA
 | 
					0977: BAM
 | 
				
			||||||
0972 TJS
 | 
					0976: CDF
 | 
				
			||||||
0971 AFN
 | 
					0975: BGN
 | 
				
			||||||
0970 COU
 | 
					0973: AOA
 | 
				
			||||||
0969 MGA
 | 
					0972: TJS
 | 
				
			||||||
0968 SRD
 | 
					0971: AFN
 | 
				
			||||||
0967 ZMW
 | 
					0970: COU
 | 
				
			||||||
0965 XUA
 | 
					0969: MGA
 | 
				
			||||||
0960 XDR
 | 
					0968: SRD
 | 
				
			||||||
0953 XPF
 | 
					0967: ZMW
 | 
				
			||||||
0952 XOF
 | 
					0965: XUA
 | 
				
			||||||
0951 XCD
 | 
					0960: XDR
 | 
				
			||||||
0950 XAF
 | 
					0953: XPF
 | 
				
			||||||
0949 TRY
 | 
					0952: XOF
 | 
				
			||||||
0948 CHW
 | 
					0951: XCD
 | 
				
			||||||
0947 CHE
 | 
					0950: XAF
 | 
				
			||||||
0946 RON
 | 
					0949: TRY
 | 
				
			||||||
0944 AZN
 | 
					0948: CHW
 | 
				
			||||||
0943 MZN
 | 
					0947: CHE
 | 
				
			||||||
0941 RSD
 | 
					0946: RON
 | 
				
			||||||
0940 UYI
 | 
					0944: AZN
 | 
				
			||||||
0938 SDG
 | 
					0943: MZN
 | 
				
			||||||
0937 VEF
 | 
					0941: RSD
 | 
				
			||||||
0936 GHS
 | 
					0940: UYI
 | 
				
			||||||
0934 TMT
 | 
					0938: SDG
 | 
				
			||||||
0933 BYN
 | 
					0937: VEF
 | 
				
			||||||
0932 ZWL
 | 
					0936: GHS
 | 
				
			||||||
0931 CUC
 | 
					0934: TMT
 | 
				
			||||||
0930 STN
 | 
					0933: BYN
 | 
				
			||||||
0929 MRU
 | 
					0932: ZWL
 | 
				
			||||||
0901 TWD
 | 
					0931: CUC
 | 
				
			||||||
0886 YER
 | 
					0930: STN
 | 
				
			||||||
0882 WST
 | 
					0929: MRU
 | 
				
			||||||
0860 UZS
 | 
					0901: TWD
 | 
				
			||||||
0858 UYU
 | 
					0886: YER
 | 
				
			||||||
0840 USD
 | 
					0882: WST
 | 
				
			||||||
0834 TZS
 | 
					0860: UZS
 | 
				
			||||||
0826 GBP
 | 
					0858: UYU
 | 
				
			||||||
0818 EGP
 | 
					0840: USD
 | 
				
			||||||
0807 MKD
 | 
					0834: TZS
 | 
				
			||||||
0800 UGX
 | 
					0826: GBP
 | 
				
			||||||
0788 TND
 | 
					0818: EGP
 | 
				
			||||||
0784 AED
 | 
					0807: MKD
 | 
				
			||||||
0780 TTD
 | 
					0800: UGX
 | 
				
			||||||
0776 TOP
 | 
					0788: TND
 | 
				
			||||||
0764 THB
 | 
					0784: AED
 | 
				
			||||||
0760 SYP
 | 
					0780: TTD
 | 
				
			||||||
0756 CHF
 | 
					0776: TOP
 | 
				
			||||||
0752 SEK
 | 
					0764: THB
 | 
				
			||||||
0748 SZL
 | 
					0760: SYP
 | 
				
			||||||
0728 SSP
 | 
					0756: CHF
 | 
				
			||||||
0710 ZAR
 | 
					0752: SEK
 | 
				
			||||||
0706 SOS
 | 
					0748: SZL
 | 
				
			||||||
0704 VND
 | 
					0728: SSP
 | 
				
			||||||
0702 SGD
 | 
					0710: ZAR
 | 
				
			||||||
0694 SLL
 | 
					0706: SOS
 | 
				
			||||||
0690 SCR
 | 
					0704: VND
 | 
				
			||||||
0682 SAR
 | 
					0702: SGD
 | 
				
			||||||
0654 SHP
 | 
					0694: SLL
 | 
				
			||||||
0646 RWF
 | 
					0690: SCR
 | 
				
			||||||
0643 RUB
 | 
					0682: SAR
 | 
				
			||||||
0634 QAR
 | 
					0654: SHP
 | 
				
			||||||
0608 PHP
 | 
					0646: RWF
 | 
				
			||||||
0604 PEN
 | 
					0643: RUB
 | 
				
			||||||
0600 PYG
 | 
					0634: QAR
 | 
				
			||||||
0598 PGK
 | 
					0608: PHP
 | 
				
			||||||
0590 PAB
 | 
					0604: PEN
 | 
				
			||||||
0586 PKR
 | 
					0600: PYG
 | 
				
			||||||
0578 NOK
 | 
					0598: PGK
 | 
				
			||||||
0566 NGN
 | 
					0590: PAB
 | 
				
			||||||
0558 NIO
 | 
					0586: PKR
 | 
				
			||||||
0554 NZD
 | 
					0578: NOK
 | 
				
			||||||
0548 VUV
 | 
					0566: NGN
 | 
				
			||||||
0533 AWG
 | 
					0558: NIO
 | 
				
			||||||
0532 ANG
 | 
					0554: NZD
 | 
				
			||||||
0524 NPR
 | 
					0548: VUV
 | 
				
			||||||
0516 NAD
 | 
					0533: AWG
 | 
				
			||||||
0512 OMR
 | 
					0532: ANG
 | 
				
			||||||
0504 MAD
 | 
					0524: NPR
 | 
				
			||||||
0498 MDL
 | 
					0516: NAD
 | 
				
			||||||
0496 MNT
 | 
					0512: OMR
 | 
				
			||||||
0484 MXN
 | 
					0504: MAD
 | 
				
			||||||
0480 MUR
 | 
					0498: MDL
 | 
				
			||||||
0462 MVR
 | 
					0496: MNT
 | 
				
			||||||
0458 MYR
 | 
					0484: MXN
 | 
				
			||||||
0454 MWK
 | 
					0480: MUR
 | 
				
			||||||
0446 MOP
 | 
					0462: MVR
 | 
				
			||||||
0434 LYD
 | 
					0458: MYR
 | 
				
			||||||
0430 LRD
 | 
					0454: MWK
 | 
				
			||||||
0426 LSL
 | 
					0446: MOP
 | 
				
			||||||
0422 LBP
 | 
					0434: LYD
 | 
				
			||||||
0418 LAK
 | 
					0430: LRD
 | 
				
			||||||
0417 KGS
 | 
					0426: LSL
 | 
				
			||||||
0414 KWD
 | 
					0422: LBP
 | 
				
			||||||
0410 KRW
 | 
					0418: LAK
 | 
				
			||||||
0408 KPW
 | 
					0417: KGS
 | 
				
			||||||
0404 KES
 | 
					0414: KWD
 | 
				
			||||||
0400 JOD
 | 
					0410: KRW
 | 
				
			||||||
0398 KZT
 | 
					0408: KPW
 | 
				
			||||||
0392 JPY
 | 
					0404: KES
 | 
				
			||||||
0388 JMD
 | 
					0400: JOD
 | 
				
			||||||
0376 ILS
 | 
					0398: KZT
 | 
				
			||||||
0368 IQD
 | 
					0392: JPY
 | 
				
			||||||
0364 IRR
 | 
					0388: JMD
 | 
				
			||||||
0360 IDR
 | 
					0376: ILS
 | 
				
			||||||
0356 INR
 | 
					0368: IQD
 | 
				
			||||||
0352 ISK
 | 
					0364: IRR
 | 
				
			||||||
0348 HUF
 | 
					0360: IDR
 | 
				
			||||||
0344 HKD
 | 
					0356: INR
 | 
				
			||||||
0340 HNL
 | 
					0352: ISK
 | 
				
			||||||
0332 HTG
 | 
					0348: HUF
 | 
				
			||||||
0328 GYD
 | 
					0344: HKD
 | 
				
			||||||
0324 GNF
 | 
					0340: HNL
 | 
				
			||||||
0320 GTQ
 | 
					0332: HTG
 | 
				
			||||||
0292 GIP
 | 
					0328: GYD
 | 
				
			||||||
0270 GMD
 | 
					0324: GNF
 | 
				
			||||||
0262 DJF
 | 
					0320: GTQ
 | 
				
			||||||
0242 FJD
 | 
					0292: GIP
 | 
				
			||||||
0238 FKP
 | 
					0270: GMD
 | 
				
			||||||
0232 ERN
 | 
					0262: DJF
 | 
				
			||||||
0230 ETB
 | 
					0242: FJD
 | 
				
			||||||
0222 SVC
 | 
					0238: FKP
 | 
				
			||||||
0214 DOP
 | 
					0232: ERN
 | 
				
			||||||
0208 DKK
 | 
					0230: ETB
 | 
				
			||||||
0203 CZK
 | 
					0222: SVC
 | 
				
			||||||
0192 CUP
 | 
					0214: DOP
 | 
				
			||||||
0191 HRK
 | 
					0208: DKK
 | 
				
			||||||
0188 CRC
 | 
					0203: CZK
 | 
				
			||||||
0174 KMF
 | 
					0192: CUP
 | 
				
			||||||
0170 COP
 | 
					0191: HRK
 | 
				
			||||||
0156 CNY
 | 
					0188: CRC
 | 
				
			||||||
0152 CLP
 | 
					0174: KMF
 | 
				
			||||||
0144 LKR
 | 
					0170: COP
 | 
				
			||||||
0136 KYD
 | 
					0156: CNY
 | 
				
			||||||
0132 CVE
 | 
					0152: CLP
 | 
				
			||||||
0124 CAD
 | 
					0144: LKR
 | 
				
			||||||
0116 KHR
 | 
					0136: KYD
 | 
				
			||||||
0108 BIF
 | 
					0132: CVE
 | 
				
			||||||
0104 MMK
 | 
					0124: CAD
 | 
				
			||||||
0096 BND
 | 
					0116: KHR
 | 
				
			||||||
0090 SBD
 | 
					0108: BIF
 | 
				
			||||||
0084 BZD
 | 
					0104: MMK
 | 
				
			||||||
0072 BWP
 | 
					0096: BND
 | 
				
			||||||
0068 BOB
 | 
					0090: SBD
 | 
				
			||||||
0064 BTN
 | 
					0084: BZD
 | 
				
			||||||
0060 BMD
 | 
					0072: BWP
 | 
				
			||||||
0052 BBD
 | 
					0068: BOB
 | 
				
			||||||
0051 AMD
 | 
					0064: BTN
 | 
				
			||||||
0050 BDT
 | 
					0060: BMD
 | 
				
			||||||
0048 BHD
 | 
					0052: BBD
 | 
				
			||||||
0044 BSD
 | 
					0051: AMD
 | 
				
			||||||
0036 AUD
 | 
					0050: BDT
 | 
				
			||||||
0032 ARS
 | 
					0048: BHD
 | 
				
			||||||
0012 DZD
 | 
					0044: BSD
 | 
				
			||||||
0008 ALL
 | 
					0036: AUD
 | 
				
			||||||
 | 
					0032: ARS
 | 
				
			||||||
 | 
					0012: DZD
 | 
				
			||||||
 | 
					0008: ALL
 | 
				
			||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user