mirror of
https://github.com/DarkFlippers/unleashed-firmware
synced 2024-11-26 06:20:21 +00:00
[FL-3746] Mifare Plus detection support (#3607)
* Initial MFPlus draft * Proper detection (WIP) * Mifare Plus detection done * Bump F18 API * Alloc takes no arguments * Fixes from code review * Remove leftover logging * Remove stray reminder comment * Review changes and extra logging * Fix atqa detection * Fix incorrect comparison * ATQA byte swap fix * mf plus: code clean up * mf plus: remove unused code * mf plus: fix read fail event handling * mf plus: fix return error codes * mf plus: handle load and save errors * mf plus: assert -> check in public API funxtion * Bump API Symbols version * Fix wrong feature mask * Skylanders plugin separation * Fix navigation * Fix info box size Co-authored-by: gornekich <n.gorbadey@gmail.com> Co-authored-by: あく <alleteam@gmail.com>
This commit is contained in:
parent
6d8b050eda
commit
cf8c82c451
23 changed files with 1655 additions and 744 deletions
124
applications/main/nfc/helpers/protocol_support/mf_plus/mf_plus.c
Normal file
124
applications/main/nfc/helpers/protocol_support/mf_plus/mf_plus.c
Normal file
|
@ -0,0 +1,124 @@
|
|||
#include "mf_plus.h"
|
||||
#include "mf_plus_render.h"
|
||||
|
||||
#include <nfc/protocols/mf_plus/mf_plus_poller.h>
|
||||
|
||||
#include "nfc/nfc_app_i.h"
|
||||
|
||||
#include "../nfc_protocol_support_common.h"
|
||||
#include "../nfc_protocol_support_gui_common.h"
|
||||
#include "../iso14443_4a/iso14443_4a_i.h"
|
||||
|
||||
static void nfc_scene_info_on_enter_mf_plus(NfcApp* instance) {
|
||||
const NfcDevice* device = instance->nfc_device;
|
||||
const MfPlusData* data = nfc_device_get_data(device, NfcProtocolMfPlus);
|
||||
|
||||
FuriString* temp_str = furi_string_alloc();
|
||||
nfc_append_filename_string_when_present(instance, temp_str);
|
||||
furi_string_cat_printf(
|
||||
temp_str, "\e#%s\n", nfc_device_get_name(device, NfcDeviceNameTypeFull));
|
||||
furi_string_replace(temp_str, "Mifare", "MIFARE");
|
||||
nfc_render_mf_plus_info(data, NfcProtocolFormatTypeFull, temp_str);
|
||||
|
||||
widget_add_text_scroll_element(
|
||||
instance->widget, 0, 0, 128, 64, furi_string_get_cstr(temp_str));
|
||||
|
||||
furi_string_free(temp_str);
|
||||
}
|
||||
static NfcCommand nfc_scene_read_poller_callback_mf_plus(NfcGenericEvent event, void* context) {
|
||||
furi_assert(context);
|
||||
furi_assert(event.protocol == NfcProtocolMfPlus);
|
||||
furi_assert(event.event_data);
|
||||
|
||||
NfcApp* instance = context;
|
||||
const MfPlusPollerEvent* mf_plus_event = event.event_data;
|
||||
|
||||
NfcCommand command = NfcCommandContinue;
|
||||
|
||||
if(mf_plus_event->type == MfPlusPollerEventTypeReadSuccess) {
|
||||
nfc_device_set_data(
|
||||
instance->nfc_device, NfcProtocolMfPlus, nfc_poller_get_data(instance->poller));
|
||||
view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventPollerSuccess);
|
||||
command = NfcCommandStop;
|
||||
} else if(mf_plus_event->type == MfPlusPollerEventTypeReadFailed) {
|
||||
view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventPollerFailure);
|
||||
command = NfcCommandStop;
|
||||
}
|
||||
|
||||
return command;
|
||||
}
|
||||
|
||||
static void nfc_scene_read_on_enter_mf_plus(NfcApp* instance) {
|
||||
nfc_poller_start(instance->poller, nfc_scene_read_poller_callback_mf_plus, instance);
|
||||
}
|
||||
|
||||
static void nfc_scene_read_success_on_enter_mf_plus(NfcApp* instance) {
|
||||
const NfcDevice* device = instance->nfc_device;
|
||||
const MfPlusData* data = nfc_device_get_data(device, NfcProtocolMfPlus);
|
||||
|
||||
FuriString* temp_str = furi_string_alloc();
|
||||
furi_string_cat_printf(
|
||||
temp_str, "\e#%s\n", nfc_device_get_name(device, NfcDeviceNameTypeFull));
|
||||
furi_string_replace(temp_str, "Mifare", "MIFARE");
|
||||
nfc_render_mf_plus_info(data, NfcProtocolFormatTypeShort, temp_str);
|
||||
|
||||
widget_add_text_scroll_element(
|
||||
instance->widget, 0, 0, 128, 52, furi_string_get_cstr(temp_str));
|
||||
|
||||
furi_string_free(temp_str);
|
||||
}
|
||||
|
||||
static void nfc_scene_emulate_on_enter_mf_plus(NfcApp* instance) {
|
||||
const Iso14443_4aData* iso14443_4a_data =
|
||||
nfc_device_get_data(instance->nfc_device, NfcProtocolIso14443_4a);
|
||||
|
||||
instance->listener =
|
||||
nfc_listener_alloc(instance->nfc, NfcProtocolIso14443_4a, iso14443_4a_data);
|
||||
nfc_listener_start(
|
||||
instance->listener, nfc_scene_emulate_listener_callback_iso14443_4a, instance);
|
||||
}
|
||||
|
||||
const NfcProtocolSupportBase nfc_protocol_support_mf_plus = {
|
||||
.features = NfcProtocolFeatureEmulateUid,
|
||||
|
||||
.scene_info =
|
||||
{
|
||||
.on_enter = nfc_scene_info_on_enter_mf_plus,
|
||||
.on_event = nfc_protocol_support_common_on_event_empty,
|
||||
},
|
||||
.scene_more_info =
|
||||
{
|
||||
.on_enter = nfc_protocol_support_common_on_enter_empty,
|
||||
.on_event = nfc_protocol_support_common_on_event_empty,
|
||||
},
|
||||
.scene_read =
|
||||
{
|
||||
.on_enter = nfc_scene_read_on_enter_mf_plus,
|
||||
.on_event = nfc_protocol_support_common_on_event_empty,
|
||||
},
|
||||
.scene_read_menu =
|
||||
{
|
||||
.on_enter = nfc_protocol_support_common_on_enter_empty,
|
||||
.on_event = nfc_protocol_support_common_on_event_empty,
|
||||
},
|
||||
.scene_read_success =
|
||||
{
|
||||
.on_enter = nfc_scene_read_success_on_enter_mf_plus,
|
||||
.on_event = nfc_protocol_support_common_on_event_empty,
|
||||
},
|
||||
.scene_saved_menu =
|
||||
{
|
||||
.on_enter = nfc_protocol_support_common_on_enter_empty,
|
||||
.on_event = nfc_protocol_support_common_on_event_empty,
|
||||
},
|
||||
.scene_save_name =
|
||||
{
|
||||
.on_enter = nfc_protocol_support_common_on_enter_empty,
|
||||
.on_event = nfc_protocol_support_common_on_event_empty,
|
||||
},
|
||||
.scene_emulate =
|
||||
{
|
||||
.on_enter = nfc_scene_emulate_on_enter_mf_plus,
|
||||
.on_event = nfc_protocol_support_common_on_event_empty,
|
||||
},
|
||||
};
|
|
@ -0,0 +1,5 @@
|
|||
#pragma once
|
||||
|
||||
#include "../nfc_protocol_support_base.h"
|
||||
|
||||
extern const NfcProtocolSupportBase nfc_protocol_support_mf_plus;
|
|
@ -0,0 +1,67 @@
|
|||
#include "mf_plus_render.h"
|
||||
|
||||
#include "../iso14443_4a/iso14443_4a_render.h"
|
||||
|
||||
void nfc_render_mf_plus_info(
|
||||
const MfPlusData* data,
|
||||
NfcProtocolFormatType format_type,
|
||||
FuriString* str) {
|
||||
nfc_render_iso14443_4a_brief(mf_plus_get_base_data(data), str);
|
||||
|
||||
if(format_type != NfcProtocolFormatTypeFull) return;
|
||||
|
||||
furi_string_cat(str, "\n\e#ISO14443-4 data");
|
||||
nfc_render_iso14443_4a_extra(mf_plus_get_base_data(data), str);
|
||||
}
|
||||
|
||||
void nfc_render_mf_plus_data(const MfPlusData* data, FuriString* str) {
|
||||
nfc_render_mf_plus_version(&data->version, str);
|
||||
}
|
||||
|
||||
void nfc_render_mf_plus_version(const MfPlusVersion* data, FuriString* str) {
|
||||
furi_string_cat_printf(
|
||||
str,
|
||||
"%02x:%02x:%02x:%02x:%02x:%02x:%02x\n",
|
||||
data->uid[0],
|
||||
data->uid[1],
|
||||
data->uid[2],
|
||||
data->uid[3],
|
||||
data->uid[4],
|
||||
data->uid[5],
|
||||
data->uid[6]);
|
||||
furi_string_cat_printf(
|
||||
str,
|
||||
"hw %02x type %02x sub %02x\n"
|
||||
" maj %02x min %02x\n"
|
||||
" size %02x proto %02x\n",
|
||||
data->hw_vendor,
|
||||
data->hw_type,
|
||||
data->hw_subtype,
|
||||
data->hw_major,
|
||||
data->hw_minor,
|
||||
data->hw_storage,
|
||||
data->hw_proto);
|
||||
furi_string_cat_printf(
|
||||
str,
|
||||
"sw %02x type %02x sub %02x\n"
|
||||
" maj %02x min %02x\n"
|
||||
" size %02x proto %02x\n",
|
||||
data->sw_vendor,
|
||||
data->sw_type,
|
||||
data->sw_subtype,
|
||||
data->sw_major,
|
||||
data->sw_minor,
|
||||
data->sw_storage,
|
||||
data->sw_proto);
|
||||
furi_string_cat_printf(
|
||||
str,
|
||||
"batch %02x:%02x:%02x:%02x:%02x\n"
|
||||
"week %d year %d\n",
|
||||
data->batch[0],
|
||||
data->batch[1],
|
||||
data->batch[2],
|
||||
data->batch[3],
|
||||
data->batch[4],
|
||||
data->prod_week,
|
||||
data->prod_year);
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
#pragma once
|
||||
|
||||
#include <nfc/protocols/mf_plus/mf_plus.h>
|
||||
|
||||
#include "../nfc_protocol_support_render_common.h"
|
||||
|
||||
void nfc_render_mf_plus_info(
|
||||
const MfPlusData* data,
|
||||
NfcProtocolFormatType format_type,
|
||||
FuriString* str);
|
||||
|
||||
void nfc_render_mf_plus_data(const MfPlusData* data, FuriString* str);
|
||||
|
||||
void nfc_render_mf_plus_version(const MfPlusVersion* data, FuriString* str);
|
|
@ -17,6 +17,7 @@
|
|||
#include "felica/felica.h"
|
||||
#include "mf_ultralight/mf_ultralight.h"
|
||||
#include "mf_classic/mf_classic.h"
|
||||
#include "mf_plus/mf_plus.h"
|
||||
#include "mf_desfire/mf_desfire.h"
|
||||
#include "slix/slix.h"
|
||||
#include "st25tb/st25tb.h"
|
||||
|
@ -38,6 +39,7 @@ const NfcProtocolSupportBase* nfc_protocol_support[NfcProtocolNum] = {
|
|||
[NfcProtocolFelica] = &nfc_protocol_support_felica,
|
||||
[NfcProtocolMfUltralight] = &nfc_protocol_support_mf_ultralight,
|
||||
[NfcProtocolMfClassic] = &nfc_protocol_support_mf_classic,
|
||||
[NfcProtocolMfPlus] = &nfc_protocol_support_mf_plus,
|
||||
[NfcProtocolMfDesfire] = &nfc_protocol_support_mf_desfire,
|
||||
[NfcProtocolSlix] = &nfc_protocol_support_slix,
|
||||
[NfcProtocolSt25tb] = &nfc_protocol_support_st25tb,
|
||||
|
|
|
@ -5,11 +5,54 @@
|
|||
#include <nfc/nfc_device.h>
|
||||
#include <bit_lib/bit_lib.h>
|
||||
#include <nfc/protocols/mf_classic/mf_classic_poller_sync.h>
|
||||
#include <flipper_format/flipper_format.h>
|
||||
|
||||
#define TAG "Skylanders"
|
||||
|
||||
static const uint64_t skylanders_key = 0x4b0b20107ccb;
|
||||
|
||||
static const char* nfc_resources_header = "Flipper NFC resources";
|
||||
static const uint32_t nfc_resources_file_version = 1;
|
||||
|
||||
static bool skylanders_search_data(
|
||||
Storage* storage,
|
||||
const char* file_name,
|
||||
FuriString* key,
|
||||
FuriString* data) {
|
||||
bool parsed = false;
|
||||
FlipperFormat* file = flipper_format_file_alloc(storage);
|
||||
FuriString* temp_str;
|
||||
temp_str = furi_string_alloc();
|
||||
|
||||
do {
|
||||
// Open file
|
||||
if(!flipper_format_file_open_existing(file, file_name)) break;
|
||||
// Read file header and version
|
||||
uint32_t version = 0;
|
||||
if(!flipper_format_read_header(file, temp_str, &version)) break;
|
||||
if(furi_string_cmp_str(temp_str, nfc_resources_header) ||
|
||||
(version != nfc_resources_file_version))
|
||||
break;
|
||||
if(!flipper_format_read_string(file, furi_string_get_cstr(key), data)) break;
|
||||
parsed = true;
|
||||
} while(false);
|
||||
|
||||
furi_string_free(temp_str);
|
||||
flipper_format_free(file);
|
||||
return parsed;
|
||||
}
|
||||
|
||||
bool skylanders_get_name(Storage* storage, uint16_t id, FuriString* name) {
|
||||
bool parsed = false;
|
||||
FuriString* key;
|
||||
key = furi_string_alloc_printf("%04X", id);
|
||||
if(skylanders_search_data(storage, EXT_PATH("nfc/assets/skylanders.nfc"), key, name)) {
|
||||
parsed = true;
|
||||
}
|
||||
furi_string_free(key);
|
||||
return parsed;
|
||||
}
|
||||
|
||||
bool skylanders_verify(Nfc* nfc) {
|
||||
bool verified = false;
|
||||
|
||||
|
@ -75,742 +118,6 @@ static bool skylanders_read(Nfc* nfc, NfcDevice* device) {
|
|||
return is_read;
|
||||
}
|
||||
|
||||
static uint8_t fill_name(const uint16_t id, FuriString* name) {
|
||||
// USED RESEARCH FROM https://github.com/silicontrip/SkyReader/blob/master/toynames.cpp#L15C1-L163C1
|
||||
// AND https://github.com/bettse/Solarbreeze/blob/master/Solarbreeze/ThePoster.swift#L438C1-L681C1
|
||||
switch(id) {
|
||||
case 0x0000:
|
||||
furi_string_cat_printf(name, "Whirlwind");
|
||||
break;
|
||||
case 0x0001:
|
||||
furi_string_cat_printf(name, "Sonic Boom");
|
||||
break;
|
||||
case 0x0002:
|
||||
furi_string_cat_printf(name, "Warnado");
|
||||
break;
|
||||
case 0x0003:
|
||||
furi_string_cat_printf(name, "Lightning Rod");
|
||||
break;
|
||||
case 0x0004:
|
||||
case 0x0194:
|
||||
furi_string_cat_printf(name, "Bash");
|
||||
break;
|
||||
case 0x0005:
|
||||
furi_string_cat_printf(name, "Terrafin");
|
||||
break;
|
||||
case 0x0006:
|
||||
furi_string_cat_printf(name, "Dino-Rang");
|
||||
break;
|
||||
case 0x0007:
|
||||
furi_string_cat_printf(name, "Prism Break");
|
||||
break;
|
||||
case 0x0008:
|
||||
furi_string_cat_printf(name, "Sunburn");
|
||||
break;
|
||||
case 0x0009:
|
||||
furi_string_cat_printf(name, "Eruptor");
|
||||
break;
|
||||
case 0x000A:
|
||||
furi_string_cat_printf(name, "Ignitor");
|
||||
break;
|
||||
case 0x000B:
|
||||
furi_string_cat_printf(name, "Flameslinger");
|
||||
break;
|
||||
case 0x000C:
|
||||
furi_string_cat_printf(name, "Zap");
|
||||
break;
|
||||
case 0x000D:
|
||||
furi_string_cat_printf(name, "Wham-Shell");
|
||||
break;
|
||||
case 0x000E:
|
||||
furi_string_cat_printf(name, "Gill Grunt");
|
||||
break;
|
||||
case 0x000F:
|
||||
furi_string_cat_printf(name, "Slam Bam");
|
||||
break;
|
||||
case 0x0010:
|
||||
case 0x01A0:
|
||||
furi_string_cat_printf(name, "Spyro");
|
||||
break;
|
||||
case 0x0011:
|
||||
furi_string_cat_printf(name, "Voodood");
|
||||
break;
|
||||
case 0x0012:
|
||||
furi_string_cat_printf(name, "Double Trouble");
|
||||
break;
|
||||
case 0x0013:
|
||||
case 0x01A3:
|
||||
furi_string_cat_printf(name, "Trigger Happy");
|
||||
break;
|
||||
case 0x0014:
|
||||
furi_string_cat_printf(name, "Drobot");
|
||||
break;
|
||||
case 0x0015:
|
||||
furi_string_cat_printf(name, "Drill Sergeant");
|
||||
break;
|
||||
case 0x0016:
|
||||
furi_string_cat_printf(name, "Boomer");
|
||||
break;
|
||||
case 0x0017:
|
||||
furi_string_cat_printf(name, "Wrecking Ball");
|
||||
break;
|
||||
case 0x0018:
|
||||
furi_string_cat_printf(name, "Camo");
|
||||
break;
|
||||
case 0x0019:
|
||||
furi_string_cat_printf(name, "Zook");
|
||||
break;
|
||||
case 0x001A:
|
||||
furi_string_cat_printf(name, "Stealth Elf");
|
||||
break;
|
||||
case 0x001B:
|
||||
furi_string_cat_printf(name, "Stump Smash");
|
||||
break;
|
||||
case 0x001C:
|
||||
furi_string_cat_printf(name, "Dark Spyro");
|
||||
break;
|
||||
case 0x001D:
|
||||
furi_string_cat_printf(name, "Hex");
|
||||
break;
|
||||
case 0x001E:
|
||||
case 0x01AE:
|
||||
furi_string_cat_printf(name, "Chop Chop");
|
||||
break;
|
||||
case 0x001F:
|
||||
furi_string_cat_printf(name, "Ghost Roaster");
|
||||
break;
|
||||
case 0x0020:
|
||||
furi_string_cat_printf(name, "Cynder");
|
||||
break;
|
||||
case 0x0064:
|
||||
furi_string_cat_printf(name, "Jet Vac");
|
||||
break;
|
||||
case 0x0065:
|
||||
furi_string_cat_printf(name, "Swarm");
|
||||
break;
|
||||
case 0x0066:
|
||||
furi_string_cat_printf(name, "Crusher");
|
||||
break;
|
||||
case 0x0067:
|
||||
furi_string_cat_printf(name, "Flashwing");
|
||||
break;
|
||||
case 0x0068:
|
||||
furi_string_cat_printf(name, "Hot Head");
|
||||
break;
|
||||
case 0x0069:
|
||||
furi_string_cat_printf(name, "Hot Dog");
|
||||
break;
|
||||
case 0x006A:
|
||||
furi_string_cat_printf(name, "Chill");
|
||||
break;
|
||||
case 0x006B:
|
||||
furi_string_cat_printf(name, "Thumpback");
|
||||
break;
|
||||
case 0x006C:
|
||||
furi_string_cat_printf(name, "Pop Fizz");
|
||||
break;
|
||||
case 0x006D:
|
||||
furi_string_cat_printf(name, "Ninjini");
|
||||
break;
|
||||
case 0x006E:
|
||||
furi_string_cat_printf(name, "Bouncer");
|
||||
break;
|
||||
case 0x006F:
|
||||
furi_string_cat_printf(name, "Sprocket");
|
||||
break;
|
||||
case 0x0070:
|
||||
furi_string_cat_printf(name, "Tree Rex");
|
||||
break;
|
||||
case 0x0071:
|
||||
furi_string_cat_printf(name, "Shroomboom");
|
||||
break;
|
||||
case 0x0072:
|
||||
furi_string_cat_printf(name, "Eye-Brawl");
|
||||
break;
|
||||
case 0x0073:
|
||||
furi_string_cat_printf(name, "Fright Rider");
|
||||
break;
|
||||
case 0x00C8:
|
||||
furi_string_cat_printf(name, "Anvil Rain");
|
||||
break;
|
||||
case 0x00C9:
|
||||
furi_string_cat_printf(name, "Treasure Chest");
|
||||
break;
|
||||
case 0x00CA:
|
||||
furi_string_cat_printf(name, "Healing Elixer");
|
||||
break;
|
||||
case 0x00CB:
|
||||
furi_string_cat_printf(name, "Ghost Swords");
|
||||
break;
|
||||
case 0x00CC:
|
||||
furi_string_cat_printf(name, "Time Twister");
|
||||
break;
|
||||
case 0x00CD:
|
||||
furi_string_cat_printf(name, "Sky-Iron Shield");
|
||||
break;
|
||||
case 0x00CE:
|
||||
furi_string_cat_printf(name, "Winged Boots");
|
||||
break;
|
||||
case 0x00CF:
|
||||
furi_string_cat_printf(name, "Sparx Dragonfly");
|
||||
break;
|
||||
case 0x00D0:
|
||||
furi_string_cat_printf(name, "Dragonfire Cannon");
|
||||
break;
|
||||
case 0x00D1:
|
||||
furi_string_cat_printf(name, "Scorpion Striker Catapult");
|
||||
break;
|
||||
case 0x00D2:
|
||||
furi_string_cat_printf(name, "Trap - Magic");
|
||||
break;
|
||||
case 0x00D3:
|
||||
furi_string_cat_printf(name, "Trap - Water");
|
||||
break;
|
||||
case 0x00D4:
|
||||
furi_string_cat_printf(name, "Trap - Air");
|
||||
break;
|
||||
case 0x00D5:
|
||||
furi_string_cat_printf(name, "Trap - Undead");
|
||||
break;
|
||||
case 0x00D6:
|
||||
furi_string_cat_printf(name, "Trap - Tech");
|
||||
break;
|
||||
case 0x00D7:
|
||||
furi_string_cat_printf(name, "Trap - Fire");
|
||||
break;
|
||||
case 0x00D8:
|
||||
furi_string_cat_printf(name, "Trap - Earth");
|
||||
break;
|
||||
case 0x00D9:
|
||||
furi_string_cat_printf(name, "Trap - Life");
|
||||
break;
|
||||
case 0x00DA:
|
||||
furi_string_cat_printf(name, "Trap - Light");
|
||||
break;
|
||||
case 0x00DB:
|
||||
furi_string_cat_printf(name, "Trap - Dark");
|
||||
break;
|
||||
case 0x00DC:
|
||||
furi_string_cat_printf(name, "Trap - Kaos");
|
||||
break;
|
||||
case 0x00E6:
|
||||
furi_string_cat_printf(name, "Hand Of Fate");
|
||||
break;
|
||||
case 0x00E7:
|
||||
furi_string_cat_printf(name, "Piggy Bank");
|
||||
break;
|
||||
case 0x00E8:
|
||||
furi_string_cat_printf(name, "Rocket Ram");
|
||||
break;
|
||||
case 0x00E9:
|
||||
furi_string_cat_printf(name, "Tiki Speaky");
|
||||
break;
|
||||
case 0x00EB:
|
||||
furi_string_cat_printf(name, "Imaginite Mystery Chest");
|
||||
break;
|
||||
case 0x012C:
|
||||
furi_string_cat_printf(name, "Dragons Peak");
|
||||
break;
|
||||
case 0x012D:
|
||||
furi_string_cat_printf(name, "Empire of Ice");
|
||||
break;
|
||||
case 0x012E:
|
||||
furi_string_cat_printf(name, "Pirate Seas");
|
||||
break;
|
||||
case 0x012F:
|
||||
furi_string_cat_printf(name, "Darklight Crypt");
|
||||
break;
|
||||
case 0x0130:
|
||||
furi_string_cat_printf(name, "Volcanic Vault");
|
||||
break;
|
||||
case 0x0131:
|
||||
furi_string_cat_printf(name, "Mirror Of Mystery");
|
||||
break;
|
||||
case 0x0132:
|
||||
furi_string_cat_printf(name, "Nightmare Express");
|
||||
break;
|
||||
case 0x0133:
|
||||
furi_string_cat_printf(name, "Sunscraper Spire");
|
||||
break;
|
||||
case 0x0134:
|
||||
furi_string_cat_printf(name, "Midnight Museum");
|
||||
break;
|
||||
case 0x01C2:
|
||||
furi_string_cat_printf(name, "Gusto");
|
||||
break;
|
||||
case 0x01C3:
|
||||
furi_string_cat_printf(name, "Thunderbolt");
|
||||
break;
|
||||
case 0x01C4:
|
||||
furi_string_cat_printf(name, "Fling Kong");
|
||||
break;
|
||||
case 0x01C5:
|
||||
furi_string_cat_printf(name, "Blades");
|
||||
break;
|
||||
case 0x01C6:
|
||||
furi_string_cat_printf(name, "Wallop");
|
||||
break;
|
||||
case 0x01C7:
|
||||
furi_string_cat_printf(name, "Head Rush");
|
||||
break;
|
||||
case 0x01C8:
|
||||
furi_string_cat_printf(name, "Fist Bump");
|
||||
break;
|
||||
case 0x01C9:
|
||||
furi_string_cat_printf(name, "Rocky Roll");
|
||||
break;
|
||||
case 0x01CA:
|
||||
furi_string_cat_printf(name, "Wildfire");
|
||||
break;
|
||||
case 0x01CB:
|
||||
furi_string_cat_printf(name, "Ka Boom");
|
||||
break;
|
||||
case 0x01CC:
|
||||
furi_string_cat_printf(name, "Trail Blazer");
|
||||
break;
|
||||
case 0x01CD:
|
||||
furi_string_cat_printf(name, "Torch");
|
||||
break;
|
||||
case 0x01CE:
|
||||
furi_string_cat_printf(name, "Snap Shot");
|
||||
break;
|
||||
case 0x01CF:
|
||||
furi_string_cat_printf(name, "Lob Star");
|
||||
break;
|
||||
case 0x01D0:
|
||||
furi_string_cat_printf(name, "Flip Wreck");
|
||||
break;
|
||||
case 0x01D1:
|
||||
furi_string_cat_printf(name, "Echo");
|
||||
break;
|
||||
case 0x01D2:
|
||||
furi_string_cat_printf(name, "Blastermind");
|
||||
break;
|
||||
case 0x01D3:
|
||||
furi_string_cat_printf(name, "Enigma");
|
||||
break;
|
||||
case 0x01D4:
|
||||
furi_string_cat_printf(name, "Deja Vu");
|
||||
break;
|
||||
case 0x01D5:
|
||||
furi_string_cat_printf(name, "Cobra Cadabra");
|
||||
break;
|
||||
case 0x01D6:
|
||||
furi_string_cat_printf(name, "Jawbreaker");
|
||||
break;
|
||||
case 0x01D7:
|
||||
furi_string_cat_printf(name, "Gearshift");
|
||||
break;
|
||||
case 0x01D8:
|
||||
furi_string_cat_printf(name, "Chopper");
|
||||
break;
|
||||
case 0x01D9:
|
||||
furi_string_cat_printf(name, "Tread Head");
|
||||
break;
|
||||
case 0x01DA:
|
||||
furi_string_cat_printf(name, "Bushwhack");
|
||||
break;
|
||||
case 0x01DB:
|
||||
furi_string_cat_printf(name, "Tuff Luck");
|
||||
break;
|
||||
case 0x01DC:
|
||||
furi_string_cat_printf(name, "Food Fight");
|
||||
break;
|
||||
case 0x01DD:
|
||||
furi_string_cat_printf(name, "High Five");
|
||||
break;
|
||||
case 0x01DE:
|
||||
furi_string_cat_printf(name, "Krypt King");
|
||||
break;
|
||||
case 0x01DF:
|
||||
furi_string_cat_printf(name, "Short Cut");
|
||||
break;
|
||||
case 0x01E0:
|
||||
furi_string_cat_printf(name, "Bat Spin");
|
||||
break;
|
||||
case 0x01E1:
|
||||
furi_string_cat_printf(name, "Funny Bone");
|
||||
break;
|
||||
case 0x01E2:
|
||||
furi_string_cat_printf(name, "Knight light");
|
||||
break;
|
||||
case 0x01E3:
|
||||
furi_string_cat_printf(name, "Spotlight");
|
||||
break;
|
||||
case 0x01E4:
|
||||
furi_string_cat_printf(name, "Knight Mare");
|
||||
break;
|
||||
case 0x01E5:
|
||||
furi_string_cat_printf(name, "Blackout");
|
||||
break;
|
||||
case 0x01F6:
|
||||
furi_string_cat_printf(name, "Bop");
|
||||
break;
|
||||
case 0x01F7:
|
||||
furi_string_cat_printf(name, "Spry");
|
||||
break;
|
||||
case 0x01F8:
|
||||
furi_string_cat_printf(name, "Hijinx");
|
||||
break;
|
||||
case 0x01F9:
|
||||
furi_string_cat_printf(name, "Terrabite");
|
||||
break;
|
||||
case 0x01FA:
|
||||
furi_string_cat_printf(name, "Breeze");
|
||||
break;
|
||||
case 0x01FB:
|
||||
furi_string_cat_printf(name, "Weeruptor");
|
||||
break;
|
||||
case 0x01FC:
|
||||
furi_string_cat_printf(name, "Pet Vac");
|
||||
break;
|
||||
case 0x01FD:
|
||||
furi_string_cat_printf(name, "Small Fry");
|
||||
break;
|
||||
case 0x01FE:
|
||||
furi_string_cat_printf(name, "Drobit");
|
||||
break;
|
||||
case 0x0202:
|
||||
furi_string_cat_printf(name, "Gill Runt");
|
||||
break;
|
||||
case 0x0207:
|
||||
furi_string_cat_printf(name, "Trigger Snappy");
|
||||
break;
|
||||
case 0x020E:
|
||||
furi_string_cat_printf(name, "Whisper Elf");
|
||||
break;
|
||||
case 0x021C:
|
||||
furi_string_cat_printf(name, "Barkley");
|
||||
break;
|
||||
case 0x021D:
|
||||
furi_string_cat_printf(name, "Thumpling");
|
||||
break;
|
||||
case 0x021E:
|
||||
furi_string_cat_printf(name, "Mini Jini");
|
||||
break;
|
||||
case 0x021F:
|
||||
furi_string_cat_printf(name, "Eye Small");
|
||||
break;
|
||||
case 0x0259:
|
||||
furi_string_cat_printf(name, "King Pen");
|
||||
break;
|
||||
case 0x0265:
|
||||
furi_string_cat_printf(name, "Golden Queen");
|
||||
break;
|
||||
case 0x02AD:
|
||||
furi_string_cat_printf(name, "Fire Acorn");
|
||||
break;
|
||||
case 0x03E8:
|
||||
furi_string_cat_printf(name, "(Boom) Jet");
|
||||
break;
|
||||
case 0x03E9:
|
||||
furi_string_cat_printf(name, "(Free) Ranger");
|
||||
break;
|
||||
case 0x03EA:
|
||||
furi_string_cat_printf(name, "(Rubble) Rouser");
|
||||
break;
|
||||
case 0x03EB:
|
||||
furi_string_cat_printf(name, "(Doom) Stone");
|
||||
break;
|
||||
case 0x03EC:
|
||||
furi_string_cat_printf(name, "Blast Zone");
|
||||
break;
|
||||
case 0x03ED:
|
||||
furi_string_cat_printf(name, "(Fire) Kraken");
|
||||
break;
|
||||
case 0x03EE:
|
||||
furi_string_cat_printf(name, "(Stink) Bomb");
|
||||
break;
|
||||
case 0x03EF:
|
||||
furi_string_cat_printf(name, "(Grilla) Drilla");
|
||||
break;
|
||||
case 0x03F0:
|
||||
furi_string_cat_printf(name, "(Hoot) Loop");
|
||||
break;
|
||||
case 0x03F1:
|
||||
furi_string_cat_printf(name, "(Trap) Shadow");
|
||||
break;
|
||||
case 0x03F2:
|
||||
furi_string_cat_printf(name, "(Magna) Charge");
|
||||
break;
|
||||
case 0x03F3:
|
||||
furi_string_cat_printf(name, "(Spy) Rise");
|
||||
break;
|
||||
case 0x03F4:
|
||||
furi_string_cat_printf(name, "(Night) Shift");
|
||||
break;
|
||||
case 0x03F5:
|
||||
furi_string_cat_printf(name, "(Rattle) Shake");
|
||||
break;
|
||||
case 0x03F6:
|
||||
furi_string_cat_printf(name, "(Freeze) Blade");
|
||||
break;
|
||||
case 0x03F7:
|
||||
furi_string_cat_printf(name, "Wash Buckler");
|
||||
break;
|
||||
case 0x07D0:
|
||||
furi_string_cat_printf(name, "Boom (Jet)");
|
||||
break;
|
||||
case 0x07D1:
|
||||
furi_string_cat_printf(name, "Free (Ranger)");
|
||||
break;
|
||||
case 0x07D2:
|
||||
furi_string_cat_printf(name, "Rubble (Rouser)");
|
||||
break;
|
||||
case 0x07D3:
|
||||
furi_string_cat_printf(name, "Doom (Stone)");
|
||||
break;
|
||||
case 0x07D4:
|
||||
furi_string_cat_printf(name, "Blast Zone (Head)");
|
||||
break;
|
||||
case 0x07D5:
|
||||
furi_string_cat_printf(name, "Fire (Kraken)");
|
||||
break;
|
||||
case 0x07D6:
|
||||
furi_string_cat_printf(name, "Stink (Bomb)");
|
||||
break;
|
||||
case 0x07D7:
|
||||
furi_string_cat_printf(name, "Grilla (Drilla)");
|
||||
break;
|
||||
case 0x07D8:
|
||||
furi_string_cat_printf(name, "Hoot (Loop)");
|
||||
break;
|
||||
case 0x07D9:
|
||||
furi_string_cat_printf(name, "Trap (Shadow)");
|
||||
break;
|
||||
case 0x07DA:
|
||||
furi_string_cat_printf(name, "Magna (Charge)");
|
||||
break;
|
||||
case 0x07DB:
|
||||
furi_string_cat_printf(name, "Spy (Rise)");
|
||||
break;
|
||||
case 0x07DC:
|
||||
furi_string_cat_printf(name, "Night (Shift)");
|
||||
break;
|
||||
case 0x07DD:
|
||||
furi_string_cat_printf(name, "Rattle (Shake)");
|
||||
break;
|
||||
case 0x07DE:
|
||||
furi_string_cat_printf(name, "Freeze (Blade)");
|
||||
break;
|
||||
case 0x07DF:
|
||||
furi_string_cat_printf(name, "Wash Buckler (Head)");
|
||||
break;
|
||||
case 0x0BB8:
|
||||
furi_string_cat_printf(name, "Scratch");
|
||||
break;
|
||||
case 0x0BB9:
|
||||
furi_string_cat_printf(name, "Pop Thorn");
|
||||
break;
|
||||
case 0x0BBA:
|
||||
furi_string_cat_printf(name, "Slobber Tooth");
|
||||
break;
|
||||
case 0x0BBB:
|
||||
furi_string_cat_printf(name, "Scorp");
|
||||
break;
|
||||
case 0x0BBC:
|
||||
furi_string_cat_printf(name, "Fryno");
|
||||
break;
|
||||
case 0x0BBD:
|
||||
furi_string_cat_printf(name, "Smolderdash");
|
||||
break;
|
||||
case 0x0BBE:
|
||||
furi_string_cat_printf(name, "Bumble Blast");
|
||||
break;
|
||||
case 0x0BBF:
|
||||
furi_string_cat_printf(name, "Zoo Lou");
|
||||
break;
|
||||
case 0x0BC0:
|
||||
furi_string_cat_printf(name, "Dune Bug");
|
||||
break;
|
||||
case 0x0BC1:
|
||||
furi_string_cat_printf(name, "Star Strike");
|
||||
break;
|
||||
case 0x0BC2:
|
||||
furi_string_cat_printf(name, "Countdown");
|
||||
break;
|
||||
case 0x0BC3:
|
||||
furi_string_cat_printf(name, "Wind Up");
|
||||
break;
|
||||
case 0x0BC4:
|
||||
furi_string_cat_printf(name, "Roller Brawl");
|
||||
break;
|
||||
case 0x0BC5:
|
||||
furi_string_cat_printf(name, "Grim Creeper");
|
||||
break;
|
||||
case 0x0BC6:
|
||||
furi_string_cat_printf(name, "Rip Tide");
|
||||
break;
|
||||
case 0x0BC7:
|
||||
furi_string_cat_printf(name, "Punk Shock");
|
||||
break;
|
||||
case 0x0C80:
|
||||
furi_string_cat_printf(name, "Battle Hammer");
|
||||
break;
|
||||
case 0x0C81:
|
||||
furi_string_cat_printf(name, "Sky Diamond");
|
||||
break;
|
||||
case 0x0C82:
|
||||
furi_string_cat_printf(name, "Platinum Sheep");
|
||||
break;
|
||||
case 0x0C83:
|
||||
furi_string_cat_printf(name, "Groove Machine");
|
||||
break;
|
||||
case 0x0C84:
|
||||
furi_string_cat_printf(name, "UFO Hat");
|
||||
break;
|
||||
case 0x0C94:
|
||||
furi_string_cat_printf(name, "Jet Stream");
|
||||
break;
|
||||
case 0x0C95:
|
||||
furi_string_cat_printf(name, "Tomb Buggy");
|
||||
break;
|
||||
case 0x0C96:
|
||||
furi_string_cat_printf(name, "Reef Ripper");
|
||||
break;
|
||||
case 0x0C97:
|
||||
furi_string_cat_printf(name, "Burn Cycle");
|
||||
break;
|
||||
case 0x0C98:
|
||||
furi_string_cat_printf(name, "Hot Streak");
|
||||
break;
|
||||
case 0x0C99:
|
||||
furi_string_cat_printf(name, "Shark Tank");
|
||||
break;
|
||||
case 0x0C9A:
|
||||
furi_string_cat_printf(name, "Thump Truck");
|
||||
break;
|
||||
case 0x0C9B:
|
||||
furi_string_cat_printf(name, "Crypt Crusher");
|
||||
break;
|
||||
case 0x0C9C:
|
||||
furi_string_cat_printf(name, "Stealth Stinger");
|
||||
break;
|
||||
case 0x0C9F:
|
||||
furi_string_cat_printf(name, "Dive Bomber");
|
||||
break;
|
||||
case 0x0CA0:
|
||||
furi_string_cat_printf(name, "Sky Slicer");
|
||||
break;
|
||||
case 0x0CA1:
|
||||
furi_string_cat_printf(name, "Clown Cruiser");
|
||||
break;
|
||||
case 0x0CA2:
|
||||
furi_string_cat_printf(name, "Gold Rusher");
|
||||
break;
|
||||
case 0x0CA3:
|
||||
furi_string_cat_printf(name, "Shield Striker");
|
||||
break;
|
||||
case 0x0CA4:
|
||||
furi_string_cat_printf(name, "Sun Runner");
|
||||
break;
|
||||
case 0x0CA5:
|
||||
furi_string_cat_printf(name, "Sea Shadow");
|
||||
break;
|
||||
case 0x0CA6:
|
||||
furi_string_cat_printf(name, "Splatter Splasher");
|
||||
break;
|
||||
case 0x0CA7:
|
||||
furi_string_cat_printf(name, "Soda Skimmer");
|
||||
break;
|
||||
case 0x0CA8:
|
||||
furi_string_cat_printf(name, "Barrel Blaster");
|
||||
break;
|
||||
case 0x0CA9:
|
||||
furi_string_cat_printf(name, "Buzz Wing");
|
||||
break;
|
||||
case 0x0CE4:
|
||||
furi_string_cat_printf(name, "Sheep Wreck Island");
|
||||
break;
|
||||
case 0x0CE5:
|
||||
furi_string_cat_printf(name, "Tower of Time");
|
||||
break;
|
||||
case 0x0CE6:
|
||||
furi_string_cat_printf(name, "Fiery Forge");
|
||||
break;
|
||||
case 0x0CE7:
|
||||
furi_string_cat_printf(name, "Arkeyan Crossbow");
|
||||
break;
|
||||
case 0x0D48:
|
||||
furi_string_cat_printf(name, "Fiesta");
|
||||
break;
|
||||
case 0x0D49:
|
||||
furi_string_cat_printf(name, "High Volt");
|
||||
break;
|
||||
case 0x0D4A:
|
||||
furi_string_cat_printf(name, "Splat");
|
||||
break;
|
||||
case 0x0D4E:
|
||||
furi_string_cat_printf(name, "Stormblade");
|
||||
break;
|
||||
case 0x0D53:
|
||||
furi_string_cat_printf(name, "Smash It");
|
||||
break;
|
||||
case 0x0D54:
|
||||
furi_string_cat_printf(name, "Spitfire");
|
||||
break;
|
||||
case 0x0D55:
|
||||
furi_string_cat_printf(name, "Hurricane Jet-Vac");
|
||||
break;
|
||||
case 0x0D56:
|
||||
furi_string_cat_printf(name, "Double Dare Trigger Happy");
|
||||
break;
|
||||
case 0x0D57:
|
||||
furi_string_cat_printf(name, "Super Shot Stealth Elf");
|
||||
break;
|
||||
case 0x0D58:
|
||||
furi_string_cat_printf(name, "Shark Shooter Terrafin");
|
||||
break;
|
||||
case 0x0D59:
|
||||
furi_string_cat_printf(name, "Bone Bash Roller Brawl");
|
||||
break;
|
||||
case 0x0D5C:
|
||||
furi_string_cat_printf(name, "Big Bubble Pop Fizz");
|
||||
break;
|
||||
case 0x0D5D:
|
||||
furi_string_cat_printf(name, "Lava Lance Eruptor");
|
||||
break;
|
||||
case 0x0D5E:
|
||||
furi_string_cat_printf(name, "Deep Dive Gill Grunt");
|
||||
break;
|
||||
case 0x0D5F:
|
||||
furi_string_cat_printf(name, "Turbo Charge Donkey Kong");
|
||||
break;
|
||||
case 0x0D60:
|
||||
furi_string_cat_printf(name, "Hammer Slam Bowser");
|
||||
break;
|
||||
case 0x0D61:
|
||||
furi_string_cat_printf(name, "Dive-Clops");
|
||||
break;
|
||||
case 0x0D62:
|
||||
furi_string_cat_printf(name, "Astroblast");
|
||||
break;
|
||||
case 0x0D63:
|
||||
furi_string_cat_printf(name, "Nightfall");
|
||||
break;
|
||||
case 0x0D64:
|
||||
furi_string_cat_printf(name, "Thrillipede");
|
||||
break;
|
||||
case 0x0DAC:
|
||||
furi_string_cat_printf(name, "Sky Trophy");
|
||||
break;
|
||||
case 0x0DAD:
|
||||
furi_string_cat_printf(name, "Land Trophy");
|
||||
break;
|
||||
case 0x0DAE:
|
||||
furi_string_cat_printf(name, "Sea Trophy");
|
||||
break;
|
||||
case 0x0DAF:
|
||||
furi_string_cat_printf(name, "Kaos Trophy");
|
||||
break;
|
||||
default:
|
||||
furi_string_cat_printf(name, "Unknown");
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool skylanders_parse(const NfcDevice* device, FuriString* parsed_data) {
|
||||
furi_assert(device);
|
||||
|
||||
|
@ -830,7 +137,11 @@ static bool skylanders_parse(const NfcDevice* device, FuriString* parsed_data) {
|
|||
const uint16_t id = (uint16_t)*data->block[1].data;
|
||||
if(id == 0) break;
|
||||
|
||||
bool success = fill_name(id, name);
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
|
||||
bool success = skylanders_get_name(storage, id, name);
|
||||
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
if(!success) break;
|
||||
|
||||
furi_string_printf(parsed_data, "\e#Skylanders\n%s", furi_string_get_cstr(name));
|
||||
|
|
247
applications/main/nfc/resources/nfc/assets/skylanders.nfc
Normal file
247
applications/main/nfc/resources/nfc/assets/skylanders.nfc
Normal file
|
@ -0,0 +1,247 @@
|
|||
Filetype: Flipper NFC resources
|
||||
Version: 1
|
||||
# ID: Name
|
||||
0000: Whirlwind
|
||||
0001: Sonic Boom
|
||||
0002: Warnado
|
||||
0003: Lightning Rod
|
||||
0004: Bash
|
||||
0194: Bash
|
||||
0005: Terrafin
|
||||
0006: Dino-Rang
|
||||
0007: Prism Break
|
||||
0008: Sunburn
|
||||
0009: Eruptor
|
||||
000A: Ignitor
|
||||
000B: Flameslinger
|
||||
000C: Zap
|
||||
000D: Wham-Shell
|
||||
000E: Gill Grunt
|
||||
000F: Slam Bam
|
||||
0010: Spyro
|
||||
01A0: Spyro
|
||||
0011: Voodood
|
||||
0012: Double Trouble
|
||||
0013: Trigger Happy
|
||||
01A3: Trigger Happy
|
||||
0014: Drobot
|
||||
0015: Drill Sergeant
|
||||
0016: Boomer
|
||||
0017: Wrecking Ball
|
||||
0018: Camo
|
||||
0019: Zook
|
||||
001A: Stealth Elf
|
||||
001B: Stump Smash
|
||||
001C: Dark Spyro
|
||||
001D: Hex
|
||||
001E: Chop Chop
|
||||
01AE: Chop Chop
|
||||
001F: Ghost Roaster
|
||||
0020: Cynder
|
||||
0064: Jet Vac
|
||||
0065: Swarm
|
||||
0066: Crusher
|
||||
0067: Flashwing
|
||||
0068: Hot Head
|
||||
0069: Hot Dog
|
||||
006A: Chill
|
||||
006B: Thumpback
|
||||
006C: Pop Fizz
|
||||
006D: Ninjini
|
||||
006E: Bouncer
|
||||
006F: Sprocket
|
||||
0070: Tree Rex
|
||||
0071: Shroomboom
|
||||
0072: Eye-Brawl
|
||||
0073: Fright Rider
|
||||
00C8: Anvil Rain
|
||||
00C9: Treasure Chest
|
||||
00CA: Healing Elixer
|
||||
00CB: Ghost Swords
|
||||
00CC: Time Twister
|
||||
00CD: Sky-Iron Shield
|
||||
00CE: Winged Boots
|
||||
00CF: Sparx Dragonfly
|
||||
00D0: Dragonfire Cannon
|
||||
00D1: Scorpion Striker Catapult
|
||||
00D2: Trap - Magic
|
||||
00D3: Trap - Water
|
||||
00D4: Trap - Air
|
||||
00D5: Trap - Undead
|
||||
00D6: Trap - Tech
|
||||
00D7: Trap - Fire
|
||||
00D8: Trap - Earth
|
||||
00D9: Trap - Life
|
||||
00DA: Trap - Light
|
||||
00DB: Trap - Dark
|
||||
00DC: Trap - Kaos
|
||||
00E6: Hand Of Fate
|
||||
00E7: Piggy Bank
|
||||
00E8: Rocket Ram
|
||||
00E9: Tiki Speaky
|
||||
00EB: Imaginite Mystery Chest
|
||||
012C: Dragons Peak
|
||||
012D: Empire of Ice
|
||||
012E: Pirate Seas
|
||||
012F: Darklight Crypt
|
||||
0130: Volcanic Vault
|
||||
0131: Mirror Of Mystery
|
||||
0132: Nightmare Express
|
||||
0133: Sunscraper Spire
|
||||
0134: Midnight Museum
|
||||
01C2: Gusto
|
||||
01C3: Thunderbolt
|
||||
01C4: Fling Kong
|
||||
01C5: Blades
|
||||
01C6: Wallop
|
||||
01C7: Head Rush
|
||||
01C8: Fist Bump
|
||||
01C9: Rocky Roll
|
||||
01CA: Wildfire
|
||||
01CB: Ka Boom
|
||||
01CC: Trail Blazer
|
||||
01CD: Torch
|
||||
01CE: Snap Shot
|
||||
01CF: Lob Star
|
||||
01D0: Flip Wreck
|
||||
01D1: Echo
|
||||
01D2: Blastermind
|
||||
01D3: Enigma
|
||||
01D4: Deja Vu
|
||||
01D5: Cobra Cadabra
|
||||
01D6: Jawbreaker
|
||||
01D7: Gearshift
|
||||
01D8: Chopper
|
||||
01D9: Tread Head
|
||||
01DA: Bushwhack
|
||||
01DB: Tuff Luck
|
||||
01DC: Food Fight
|
||||
01DD: High Five
|
||||
01DE: Krypt King
|
||||
01DF: Short Cut
|
||||
01E0: Bat Spin
|
||||
01E1: Funny Bone
|
||||
01E2: Knight light
|
||||
01E3: Spotlight
|
||||
01E4: Knight Mare
|
||||
01E5: Blackout
|
||||
01F6: Bop
|
||||
01F7: Spry
|
||||
01F8: Hijinx
|
||||
01F9: Terrabite
|
||||
01FA: Breeze
|
||||
01FB: Weeruptor
|
||||
01FC: Pet Vac
|
||||
01FD: Small Fry
|
||||
01FE: Drobit
|
||||
0202: Gill Runt
|
||||
0207: Trigger Snappy
|
||||
020E: Whisper Elf
|
||||
021C: Barkley
|
||||
021D: Thumpling
|
||||
021E: Mini Jini
|
||||
021F: Eye Small
|
||||
0259: King Pen
|
||||
0265: Golden Queen
|
||||
02AD: Fire Acorn
|
||||
03E8: (Boom) Jet
|
||||
03E9: (Free) Ranger
|
||||
03EA: (Rubble) Rouser
|
||||
03EB: (Doom) Stone
|
||||
03EC: Blast Zone
|
||||
03ED: (Fire) Kraken
|
||||
03EE: (Stink) Bomb
|
||||
03EF: (Grilla) Drilla
|
||||
03F0: (Hoot) Loop
|
||||
03F1: (Trap) Shadow
|
||||
03F2: (Magna) Charge
|
||||
03F3: (Spy) Rise
|
||||
03F4: (Night) Shift
|
||||
03F5: (Rattle) Shake
|
||||
03F6: (Freeze) Blade
|
||||
03F7: Wash Buckler
|
||||
07D0: Boom (Jet)
|
||||
07D1: Free (Ranger)
|
||||
07D2: Rubble (Rouser)
|
||||
07D3: Doom (Stone)
|
||||
07D4: Blast Zone (Head)
|
||||
07D5: Fire (Kraken)
|
||||
07D6: Stink (Bomb)
|
||||
07D7: Grilla (Drilla)
|
||||
07D8: Hoot (Loop)
|
||||
07D9: Trap (Shadow)
|
||||
07DA: Magna (Charge)
|
||||
07DB: Spy (Rise)
|
||||
07DC: Night (Shift)
|
||||
07DD: Rattle (Shake)
|
||||
07DE: Freeze (Blade)
|
||||
07DF: Wash Buckler (Head)
|
||||
0BB8: Scratch
|
||||
0BB9: Pop Thorn
|
||||
0BBA: Slobber Tooth
|
||||
0BBB: Scorp
|
||||
0BBC: Fryno
|
||||
0BBD: Smolderdash
|
||||
0BBE: Bumble Blast
|
||||
0BBF: Zoo Lou
|
||||
0BC0: Dune Bug
|
||||
0BC1: Star Strike
|
||||
0BC2: Countdown
|
||||
0BC3: Wind Up
|
||||
0BC4: Roller Brawl
|
||||
0BC5: Grim Creeper
|
||||
0BC6: Rip Tide
|
||||
0BC7: Punk Shock
|
||||
0C80: Battle Hammer
|
||||
0C81: Sky Diamond
|
||||
0C82: Platinum Sheep
|
||||
0C83: Groove Machine
|
||||
0C84: UFO Hat
|
||||
0C94: Jet Stream
|
||||
0C95: Tomb Buggy
|
||||
0C96: Reef Ripper
|
||||
0C97: Burn Cycle
|
||||
0C98: Hot Streak
|
||||
0C99: Shark Tank
|
||||
0C9A: Thump Truck
|
||||
0C9B: Crypt Crusher
|
||||
0C9C: Stealth Stinger
|
||||
0C9F: Dive Bomber
|
||||
0CA0: Sky Slicer
|
||||
0CA1: Clown Cruiser
|
||||
0CA2: Gold Rusher
|
||||
0CA3: Shield Striker
|
||||
0CA4: Sun Runner
|
||||
0CA5: Sea Shadow
|
||||
0CA6: Splatter Splasher
|
||||
0CA7: Soda Skimmer
|
||||
0CA8: Barrel Blaster
|
||||
0CA9: Buzz Wing
|
||||
0CE4: Sheep Wreck Island
|
||||
0CE5: Tower of Time
|
||||
0CE6: Fiery Forge
|
||||
0CE7: Arkeyan Crossbow
|
||||
0D48: Fiesta
|
||||
0D49: High Volt
|
||||
0D4A: Splat
|
||||
0D4E: Stormblade
|
||||
0D53: Smash It
|
||||
0D54: Spitfire
|
||||
0D55: Hurricane Jet-Vac
|
||||
0D56: Double Dare Trigger Happy
|
||||
0D57: Super Shot Stealth Elf
|
||||
0D58: Shark Shooter Terrafin
|
||||
0D59: Bone Bash Roller Brawl
|
||||
0D5C: Big Bubble Pop Fizz
|
||||
0D5D: Lava Lance Eruptor
|
||||
0D5E: Deep Dive Gill Grunt
|
||||
0D5F: Turbo Charge Donkey Kong
|
||||
0D60: Hammer Slam Bowser
|
||||
0D61: Dive-Clops
|
||||
0D62: Astroblast
|
||||
0D63: Nightfall
|
||||
0D64: Thrillipede
|
||||
0DAC: Sky Trophy
|
||||
0DAD: Land Trophy
|
||||
0DAE: Sea Trophy
|
||||
0DAF: Kaos Trophy
|
|
@ -28,11 +28,7 @@ bool nfc_scene_exit_confirm_on_event(void* context, SceneManagerEvent event) {
|
|||
if(event.event == DialogExResultRight) {
|
||||
consumed = scene_manager_previous_scene(nfc->scene_manager);
|
||||
} else if(event.event == DialogExResultLeft) {
|
||||
if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSelectProtocol)) {
|
||||
consumed = scene_manager_search_and_switch_to_previous_scene(
|
||||
nfc->scene_manager, NfcSceneSelectProtocol);
|
||||
} else if(
|
||||
scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneMfClassicDictAttack) &&
|
||||
if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneMfClassicDictAttack) &&
|
||||
(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneReadMenu) ||
|
||||
scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSavedMenu))) {
|
||||
const uint32_t possible_scenes[] = {NfcSceneReadMenu, NfcSceneSavedMenu};
|
||||
|
|
|
@ -21,6 +21,7 @@ env.Append(
|
|||
File("protocols/iso14443_4b/iso14443_4b.h"),
|
||||
File("protocols/mf_ultralight/mf_ultralight.h"),
|
||||
File("protocols/mf_classic/mf_classic.h"),
|
||||
File("protocols/mf_plus/mf_plus.h"),
|
||||
File("protocols/mf_desfire/mf_desfire.h"),
|
||||
File("protocols/slix/slix.h"),
|
||||
File("protocols/st25tb/st25tb.h"),
|
||||
|
@ -32,6 +33,7 @@ env.Append(
|
|||
File("protocols/iso14443_4b/iso14443_4b_poller.h"),
|
||||
File("protocols/mf_ultralight/mf_ultralight_poller.h"),
|
||||
File("protocols/mf_classic/mf_classic_poller.h"),
|
||||
File("protocols/mf_plus/mf_plus_poller.h"),
|
||||
File("protocols/mf_desfire/mf_desfire_poller.h"),
|
||||
File("protocols/slix/slix_poller.h"),
|
||||
File("protocols/st25tb/st25tb_poller.h"),
|
||||
|
|
180
lib/nfc/protocols/mf_plus/mf_plus.c
Normal file
180
lib/nfc/protocols/mf_plus/mf_plus.c
Normal file
|
@ -0,0 +1,180 @@
|
|||
#include "mf_plus_i.h"
|
||||
|
||||
#include <bit_lib/bit_lib.h>
|
||||
#include <furi.h>
|
||||
|
||||
#define MF_PLUS_PROTOCOL_NAME "Mifare Plus"
|
||||
|
||||
static const char* mf_plus_type_strings[] = {
|
||||
[MfPlusTypeS] = "Plus S",
|
||||
[MfPlusTypeX] = "Plus X",
|
||||
[MfPlusTypeSE] = "Plus SE",
|
||||
[MfPlusTypeEV1] = "Plus EV1",
|
||||
[MfPlusTypeEV2] = "Plus EV2",
|
||||
[MfPlusTypePlus] = "Plus",
|
||||
[MfPlusTypeUnknown] = "Unknown",
|
||||
};
|
||||
|
||||
static const char* mf_plus_size_strings[] = {
|
||||
[MfPlusSize1K] = "1K",
|
||||
[MfPlusSize2K] = "2K",
|
||||
[MfPlusSize4K] = "4K",
|
||||
[MfPlusSizeUnknown] = "Unknown",
|
||||
};
|
||||
|
||||
static const char* mf_plus_security_level_strings[] = {
|
||||
[MfPlusSecurityLevel0] = "SL0",
|
||||
[MfPlusSecurityLevel1] = "SL1",
|
||||
[MfPlusSecurityLevel2] = "SL2",
|
||||
[MfPlusSecurityLevel3] = "SL3",
|
||||
[MfPlusSecurityLevelUnknown] = "Unknown",
|
||||
};
|
||||
|
||||
const NfcDeviceBase nfc_device_mf_plus = {
|
||||
.protocol_name = MF_PLUS_PROTOCOL_NAME,
|
||||
.alloc = (NfcDeviceAlloc)mf_plus_alloc,
|
||||
.free = (NfcDeviceFree)mf_plus_free,
|
||||
.reset = (NfcDeviceReset)mf_plus_reset,
|
||||
.copy = (NfcDeviceCopy)mf_plus_copy,
|
||||
.verify = (NfcDeviceVerify)mf_plus_verify,
|
||||
.load = (NfcDeviceLoad)mf_plus_load,
|
||||
.save = (NfcDeviceSave)mf_plus_save,
|
||||
.is_equal = (NfcDeviceEqual)mf_plus_is_equal,
|
||||
.get_name = (NfcDeviceGetName)mf_plus_get_device_name,
|
||||
.get_uid = (NfcDeviceGetUid)mf_plus_get_uid,
|
||||
.set_uid = (NfcDeviceSetUid)mf_plus_set_uid,
|
||||
.get_base_data = (NfcDeviceGetBaseData)mf_plus_get_base_data,
|
||||
};
|
||||
|
||||
MfPlusData* mf_plus_alloc(void) {
|
||||
MfPlusData* data = malloc(sizeof(MfPlusData));
|
||||
data->device_name = furi_string_alloc();
|
||||
data->iso14443_4a_data = iso14443_4a_alloc();
|
||||
|
||||
data->type = MfPlusTypeUnknown;
|
||||
data->security_level = MfPlusSecurityLevelUnknown;
|
||||
data->size = MfPlusSizeUnknown;
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
void mf_plus_free(MfPlusData* data) {
|
||||
furi_check(data);
|
||||
|
||||
furi_string_free(data->device_name);
|
||||
iso14443_4a_free(data->iso14443_4a_data);
|
||||
free(data);
|
||||
}
|
||||
|
||||
void mf_plus_reset(MfPlusData* data) {
|
||||
furi_check(data);
|
||||
|
||||
iso14443_4a_reset(data->iso14443_4a_data);
|
||||
memset(&data->version, 0, sizeof(data->version));
|
||||
furi_string_reset(data->device_name);
|
||||
data->type = MfPlusTypeUnknown;
|
||||
data->security_level = MfPlusSecurityLevelUnknown;
|
||||
data->size = MfPlusSizeUnknown;
|
||||
}
|
||||
|
||||
void mf_plus_copy(MfPlusData* data, const MfPlusData* other) {
|
||||
furi_check(data);
|
||||
furi_check(other);
|
||||
|
||||
iso14443_4a_copy(data->iso14443_4a_data, other->iso14443_4a_data);
|
||||
data->version = other->version;
|
||||
data->type = other->type;
|
||||
data->security_level = other->security_level;
|
||||
data->size = other->size;
|
||||
}
|
||||
|
||||
bool mf_plus_verify(MfPlusData* data, const FuriString* device_type) {
|
||||
UNUSED(data);
|
||||
return furi_string_equal_str(device_type, MF_PLUS_PROTOCOL_NAME);
|
||||
}
|
||||
|
||||
bool mf_plus_load(MfPlusData* data, FlipperFormat* ff, uint32_t version) {
|
||||
furi_check(data);
|
||||
|
||||
bool success = false;
|
||||
do {
|
||||
if(!iso14443_4a_load(data->iso14443_4a_data, ff, version)) break;
|
||||
if(!mf_plus_version_load(&data->version, ff)) break;
|
||||
if(!mf_plus_type_load(&data->type, ff)) break;
|
||||
if(!mf_plus_security_level_load(&data->security_level, ff)) break;
|
||||
if(!mf_plus_size_load(&data->size, ff)) break;
|
||||
success = true;
|
||||
} while(false);
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
bool mf_plus_save(const MfPlusData* data, FlipperFormat* ff) {
|
||||
furi_check(data);
|
||||
|
||||
bool success = false;
|
||||
do {
|
||||
if(!iso14443_4a_save(data->iso14443_4a_data, ff)) break;
|
||||
if(!flipper_format_write_comment_cstr(ff, MF_PLUS_PROTOCOL_NAME " specific data")) break;
|
||||
if(!mf_plus_version_save(&data->version, ff)) break;
|
||||
if(!mf_plus_type_save(&data->type, ff)) break;
|
||||
if(!mf_plus_security_level_save(&data->security_level, ff)) break;
|
||||
if(!mf_plus_size_save(&data->size, ff)) break;
|
||||
success = true;
|
||||
} while(false);
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
bool mf_plus_is_equal(const MfPlusData* data, const MfPlusData* other) {
|
||||
furi_check(data);
|
||||
furi_check(other);
|
||||
|
||||
bool equal = false;
|
||||
do {
|
||||
if(!iso14443_4a_is_equal(data->iso14443_4a_data, other->iso14443_4a_data)) break;
|
||||
if(memcmp(&data->version, &other->version, sizeof(data->version)) != 0) break;
|
||||
if(data->security_level != other->security_level) break;
|
||||
if(data->type != other->type) break;
|
||||
if(data->size != other->size) break;
|
||||
equal = true;
|
||||
} while(false);
|
||||
|
||||
return equal;
|
||||
}
|
||||
|
||||
const char* mf_plus_get_device_name(const MfPlusData* data, NfcDeviceNameType name_type) {
|
||||
furi_check(data);
|
||||
|
||||
if(name_type == NfcDeviceNameTypeFull) {
|
||||
furi_string_printf(
|
||||
data->device_name,
|
||||
"Mifare %s %s %s",
|
||||
mf_plus_type_strings[data->type], // Includes "Plus" for regular Mifare Plus cards
|
||||
mf_plus_size_strings[data->size],
|
||||
mf_plus_security_level_strings[data->security_level]);
|
||||
} else if(name_type == NfcDeviceNameTypeShort) {
|
||||
furi_string_set_str(data->device_name, MF_PLUS_PROTOCOL_NAME);
|
||||
} else {
|
||||
furi_crash("Unexpected name type");
|
||||
}
|
||||
|
||||
return furi_string_get_cstr(data->device_name);
|
||||
}
|
||||
|
||||
const uint8_t* mf_plus_get_uid(const MfPlusData* data, size_t* uid_len) {
|
||||
furi_check(data);
|
||||
|
||||
return iso14443_4a_get_uid(data->iso14443_4a_data, uid_len);
|
||||
}
|
||||
|
||||
bool mf_plus_set_uid(MfPlusData* data, const uint8_t* uid, size_t uid_len) {
|
||||
furi_check(data);
|
||||
|
||||
return iso14443_4a_set_uid(data->iso14443_4a_data, uid, uid_len);
|
||||
}
|
||||
Iso14443_4aData* mf_plus_get_base_data(const MfPlusData* data) {
|
||||
furi_check(data);
|
||||
|
||||
return data->iso14443_4a_data;
|
||||
}
|
115
lib/nfc/protocols/mf_plus/mf_plus.h
Normal file
115
lib/nfc/protocols/mf_plus/mf_plus.h
Normal file
|
@ -0,0 +1,115 @@
|
|||
#pragma once
|
||||
|
||||
#include <lib/nfc/protocols/iso14443_4a/iso14443_4a.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define MF_PLUS_UID_SIZE_MAX (7)
|
||||
#define MF_PLUS_BATCH_SIZE (5)
|
||||
|
||||
#define MF_PLUS_CMD_GET_VERSION (0x60)
|
||||
|
||||
typedef enum {
|
||||
MfPlusErrorNone,
|
||||
MfPlusErrorUnknown,
|
||||
MfPlusErrorNotPresent,
|
||||
MfPlusErrorProtocol,
|
||||
MfPlusErrorAuth,
|
||||
MfPlusErrorPartialRead,
|
||||
MfPlusErrorTimeout,
|
||||
} MfPlusError;
|
||||
|
||||
typedef enum {
|
||||
MfPlusTypePlus,
|
||||
MfPlusTypeEV1,
|
||||
MfPlusTypeEV2,
|
||||
MfPlusTypeS,
|
||||
MfPlusTypeSE,
|
||||
MfPlusTypeX,
|
||||
|
||||
MfPlusTypeUnknown,
|
||||
MfPlusTypeNum,
|
||||
} MfPlusType;
|
||||
|
||||
typedef enum {
|
||||
MfPlusSize1K,
|
||||
MfPlusSize2K,
|
||||
MfPlusSize4K,
|
||||
|
||||
MfPlusSizeUnknown,
|
||||
MfPlusSizeNum,
|
||||
} MfPlusSize;
|
||||
|
||||
typedef enum {
|
||||
MfPlusSecurityLevel0,
|
||||
MfPlusSecurityLevel1,
|
||||
MfPlusSecurityLevel2,
|
||||
MfPlusSecurityLevel3,
|
||||
|
||||
MfPlusSecurityLevelUnknown,
|
||||
MfPlusSecurityLevelNum,
|
||||
} MfPlusSecurityLevel;
|
||||
|
||||
typedef struct {
|
||||
uint8_t hw_vendor;
|
||||
uint8_t hw_type;
|
||||
uint8_t hw_subtype;
|
||||
uint8_t hw_major;
|
||||
uint8_t hw_minor;
|
||||
uint8_t hw_storage;
|
||||
uint8_t hw_proto;
|
||||
|
||||
uint8_t sw_vendor;
|
||||
uint8_t sw_type;
|
||||
uint8_t sw_subtype;
|
||||
uint8_t sw_major;
|
||||
uint8_t sw_minor;
|
||||
uint8_t sw_storage;
|
||||
uint8_t sw_proto;
|
||||
|
||||
uint8_t uid[MF_PLUS_UID_SIZE_MAX];
|
||||
uint8_t batch[MF_PLUS_BATCH_SIZE];
|
||||
uint8_t prod_week;
|
||||
uint8_t prod_year;
|
||||
} MfPlusVersion;
|
||||
|
||||
typedef struct {
|
||||
Iso14443_4aData* iso14443_4a_data;
|
||||
MfPlusVersion version;
|
||||
MfPlusType type;
|
||||
MfPlusSize size;
|
||||
MfPlusSecurityLevel security_level;
|
||||
FuriString* device_name;
|
||||
} MfPlusData;
|
||||
|
||||
extern const NfcDeviceBase nfc_device_mf_plus;
|
||||
|
||||
MfPlusData* mf_plus_alloc(void);
|
||||
|
||||
void mf_plus_free(MfPlusData* data);
|
||||
|
||||
void mf_plus_reset(MfPlusData* data);
|
||||
|
||||
void mf_plus_copy(MfPlusData* data, const MfPlusData* other);
|
||||
|
||||
bool mf_plus_verify(MfPlusData* data, const FuriString* device_type);
|
||||
|
||||
bool mf_plus_load(MfPlusData* data, FlipperFormat* ff, uint32_t version);
|
||||
|
||||
bool mf_plus_save(const MfPlusData* data, FlipperFormat* ff);
|
||||
|
||||
bool mf_plus_is_equal(const MfPlusData* data, const MfPlusData* other);
|
||||
|
||||
const char* mf_plus_get_device_name(const MfPlusData* data, NfcDeviceNameType name_type);
|
||||
|
||||
const uint8_t* mf_plus_get_uid(const MfPlusData* data, size_t* uid_len);
|
||||
|
||||
bool mf_plus_set_uid(MfPlusData* data, const uint8_t* uid, size_t uid_len);
|
||||
|
||||
Iso14443_4aData* mf_plus_get_base_data(const MfPlusData* data);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
406
lib/nfc/protocols/mf_plus/mf_plus_i.c
Normal file
406
lib/nfc/protocols/mf_plus/mf_plus_i.c
Normal file
|
@ -0,0 +1,406 @@
|
|||
#include "mf_plus_i.h"
|
||||
|
||||
#define MF_PLUS_FFF_VERSION_KEY \
|
||||
MF_PLUS_FFF_PICC_PREFIX " " \
|
||||
"Version"
|
||||
|
||||
#define MF_PLUS_FFF_SECURITY_LEVEL_KEY "Security Level"
|
||||
#define MF_PLUS_FFF_CARD_TYPE_KEY "Card Type"
|
||||
#define MF_PLUS_FFF_MEMORY_SIZE_KEY "Memory Size"
|
||||
|
||||
#define TAG "MfPlus"
|
||||
|
||||
const uint8_t mf_plus_ats_t1_tk_values[][7] = {
|
||||
{0xC1, 0x05, 0x2F, 0x2F, 0x00, 0x35, 0xC7}, // Mifare Plus S
|
||||
{0xC1, 0x05, 0x2F, 0x2F, 0x01, 0xBC, 0xD6}, // Mifare Plus X
|
||||
{0xC1, 0x05, 0x2F, 0x2F, 0x00, 0xF6, 0xD1}, // Mifare Plus SE
|
||||
{0xC1, 0x05, 0x2F, 0x2F, 0x01, 0xF6, 0xD1}, // Mifare Plus SE
|
||||
};
|
||||
|
||||
MfPlusError mf_plus_get_type_from_version(
|
||||
const Iso14443_4aData* iso14443_4a_data,
|
||||
MfPlusData* mf_plus_data) {
|
||||
furi_assert(iso14443_4a_data);
|
||||
furi_assert(mf_plus_data);
|
||||
|
||||
MfPlusError error = MfPlusErrorProtocol;
|
||||
|
||||
if(mf_plus_data->version.hw_major == 0x02 || mf_plus_data->version.hw_major == 0x82) {
|
||||
error = MfPlusErrorNone;
|
||||
if(iso14443_4a_data->iso14443_3a_data->sak == 0x10) {
|
||||
// Mifare Plus 2K SL2
|
||||
mf_plus_data->type = MfPlusTypePlus;
|
||||
mf_plus_data->size = MfPlusSize2K;
|
||||
mf_plus_data->security_level = MfPlusSecurityLevel2;
|
||||
FURI_LOG_D(TAG, "Mifare Plus 2K SL2");
|
||||
} else if(iso14443_4a_data->iso14443_3a_data->sak == 0x11) {
|
||||
// Mifare Plus 4K SL3
|
||||
mf_plus_data->type = MfPlusTypePlus;
|
||||
mf_plus_data->size = MfPlusSize4K;
|
||||
mf_plus_data->security_level = MfPlusSecurityLevel3;
|
||||
FURI_LOG_D(TAG, "Mifare Plus 4K SL3");
|
||||
} else {
|
||||
// Mifare Plus EV1/EV2
|
||||
|
||||
// Revision
|
||||
switch(mf_plus_data->version.hw_major) {
|
||||
case 0x11:
|
||||
mf_plus_data->type = MfPlusTypeEV1;
|
||||
FURI_LOG_D(TAG, "Mifare Plus EV1");
|
||||
break;
|
||||
case 0x22:
|
||||
mf_plus_data->type = MfPlusTypeEV2;
|
||||
FURI_LOG_D(TAG, "Mifare Plus EV2");
|
||||
break;
|
||||
default:
|
||||
mf_plus_data->type = MfPlusTypeUnknown;
|
||||
FURI_LOG_D(TAG, "Unknown Mifare Plus EV type");
|
||||
break;
|
||||
}
|
||||
|
||||
// Storage size
|
||||
switch(mf_plus_data->version.hw_storage) {
|
||||
case 0x16:
|
||||
mf_plus_data->size = MfPlusSize2K;
|
||||
FURI_LOG_D(TAG, "2K");
|
||||
break;
|
||||
case 0x18:
|
||||
mf_plus_data->size = MfPlusSize4K;
|
||||
FURI_LOG_D(TAG, "4K");
|
||||
break;
|
||||
default:
|
||||
mf_plus_data->size = MfPlusSizeUnknown;
|
||||
FURI_LOG_D(TAG, "Unknown storage size");
|
||||
break;
|
||||
}
|
||||
|
||||
// Security level
|
||||
if(iso14443_4a_data->iso14443_3a_data->sak == 0x20) {
|
||||
// Mifare Plus EV1/2 SL3
|
||||
mf_plus_data->security_level = MfPlusSecurityLevel3;
|
||||
FURI_LOG_D(TAG, "Miare Plus EV1/2 SL3");
|
||||
} else {
|
||||
// Mifare Plus EV1/2 SL1
|
||||
mf_plus_data->security_level = MfPlusSecurityLevel1;
|
||||
FURI_LOG_D(TAG, "Miare Plus EV1/2 SL1");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
MfPlusError
|
||||
mf_plus_get_type_from_iso4(const Iso14443_4aData* iso4_data, MfPlusData* mf_plus_data) {
|
||||
furi_assert(iso4_data);
|
||||
furi_assert(mf_plus_data);
|
||||
|
||||
MfPlusError error = MfPlusErrorProtocol;
|
||||
|
||||
switch(iso4_data->iso14443_3a_data->sak) {
|
||||
case 0x08:
|
||||
if(memcmp(
|
||||
simple_array_get_data(iso4_data->ats_data.t1_tk),
|
||||
mf_plus_ats_t1_tk_values[0],
|
||||
simple_array_get_count(iso4_data->ats_data.t1_tk)) == 0) {
|
||||
// Mifare Plus S 2K SL1
|
||||
mf_plus_data->type = MfPlusTypeS;
|
||||
mf_plus_data->size = MfPlusSize2K;
|
||||
mf_plus_data->security_level = MfPlusSecurityLevel1;
|
||||
|
||||
FURI_LOG_D(TAG, "Mifare Plus S 2K SL1");
|
||||
error = MfPlusErrorNone;
|
||||
} else if(
|
||||
memcmp(
|
||||
simple_array_get_data(iso4_data->ats_data.t1_tk),
|
||||
mf_plus_ats_t1_tk_values[1],
|
||||
simple_array_get_count(iso4_data->ats_data.t1_tk)) == 0) {
|
||||
// Mifare Plus X 2K SL1
|
||||
mf_plus_data->type = MfPlusTypeX;
|
||||
mf_plus_data->size = MfPlusSize2K;
|
||||
mf_plus_data->security_level = MfPlusSecurityLevel1;
|
||||
|
||||
FURI_LOG_D(TAG, "Mifare Plus X 2K SL1");
|
||||
error = MfPlusErrorNone;
|
||||
} else if(
|
||||
memcmp(
|
||||
simple_array_get_data(iso4_data->ats_data.t1_tk),
|
||||
mf_plus_ats_t1_tk_values[2],
|
||||
simple_array_get_count(iso4_data->ats_data.t1_tk)) == 0 ||
|
||||
memcmp(
|
||||
simple_array_get_data(iso4_data->ats_data.t1_tk),
|
||||
mf_plus_ats_t1_tk_values[3],
|
||||
simple_array_get_count(iso4_data->ats_data.t1_tk)) == 0) {
|
||||
// Mifare Plus SE 1K SL1
|
||||
mf_plus_data->type = MfPlusTypeSE;
|
||||
mf_plus_data->size = MfPlusSize1K;
|
||||
mf_plus_data->security_level = MfPlusSecurityLevel1;
|
||||
|
||||
FURI_LOG_D(TAG, "Mifare Plus SE 1K SL1");
|
||||
error = MfPlusErrorNone;
|
||||
} else {
|
||||
FURI_LOG_D(TAG, "Sak 08 but no known Mifare Plus type");
|
||||
}
|
||||
|
||||
break;
|
||||
case 0x18:
|
||||
if(memcmp(
|
||||
simple_array_get_data(iso4_data->ats_data.t1_tk),
|
||||
mf_plus_ats_t1_tk_values[0],
|
||||
simple_array_get_count(iso4_data->ats_data.t1_tk)) == 0) {
|
||||
// Mifare Plus S 4K SL1
|
||||
mf_plus_data->type = MfPlusTypeS;
|
||||
mf_plus_data->size = MfPlusSize4K;
|
||||
mf_plus_data->security_level = MfPlusSecurityLevel1;
|
||||
|
||||
FURI_LOG_D(TAG, "Mifare Plus S 4K SL1");
|
||||
error = MfPlusErrorNone;
|
||||
} else if(
|
||||
memcmp(
|
||||
simple_array_get_data(iso4_data->ats_data.t1_tk),
|
||||
mf_plus_ats_t1_tk_values[1],
|
||||
simple_array_get_count(iso4_data->ats_data.t1_tk)) == 0) {
|
||||
// Mifare Plus X 4K SL1
|
||||
mf_plus_data->type = MfPlusTypeX;
|
||||
mf_plus_data->size = MfPlusSize4K;
|
||||
mf_plus_data->security_level = MfPlusSecurityLevel1;
|
||||
|
||||
FURI_LOG_D(TAG, "Mifare Plus X 4K SL1");
|
||||
error = MfPlusErrorNone;
|
||||
} else {
|
||||
FURI_LOG_D(TAG, "Sak 18 but no known Mifare Plus type");
|
||||
}
|
||||
|
||||
break;
|
||||
case 0x20:
|
||||
if(memcmp(
|
||||
simple_array_get_data(iso4_data->ats_data.t1_tk),
|
||||
mf_plus_ats_t1_tk_values[0],
|
||||
simple_array_get_count(iso4_data->ats_data.t1_tk)) == 0) {
|
||||
// Mifare Plus S 2/4K SL3
|
||||
FURI_LOG_D(TAG, "Mifare Plus S SL3");
|
||||
mf_plus_data->type = MfPlusTypeS;
|
||||
mf_plus_data->security_level = MfPlusSecurityLevel3;
|
||||
|
||||
if((iso4_data->iso14443_3a_data->atqa[0] & 0x0F) == 0x04) {
|
||||
// Mifare Plus S 2K SL3
|
||||
mf_plus_data->size = MfPlusSize2K;
|
||||
|
||||
FURI_LOG_D(TAG, "Mifare Plus S 2K SL3");
|
||||
error = MfPlusErrorNone;
|
||||
} else if((iso4_data->iso14443_3a_data->atqa[0] & 0x0F) == 0x02) {
|
||||
// Mifare Plus S 4K SL3
|
||||
mf_plus_data->size = MfPlusSize4K;
|
||||
|
||||
FURI_LOG_D(TAG, "Mifare Plus S 4K SL3");
|
||||
error = MfPlusErrorNone;
|
||||
} else {
|
||||
FURI_LOG_D(TAG, "Sak 20 but no known Mifare Plus type (S)");
|
||||
}
|
||||
} else if(
|
||||
memcmp(
|
||||
simple_array_get_data(iso4_data->ats_data.t1_tk),
|
||||
mf_plus_ats_t1_tk_values[1],
|
||||
simple_array_get_count(iso4_data->ats_data.t1_tk)) == 0) {
|
||||
mf_plus_data->type = MfPlusTypeX;
|
||||
mf_plus_data->security_level = MfPlusSecurityLevel3;
|
||||
FURI_LOG_D(TAG, "Mifare Plus X SL3");
|
||||
|
||||
if((iso4_data->iso14443_3a_data->atqa[0] & 0x0F) == 0x04) {
|
||||
mf_plus_data->size = MfPlusSize2K;
|
||||
|
||||
FURI_LOG_D(TAG, "Mifare Plus X 2K SL3");
|
||||
error = MfPlusErrorNone;
|
||||
} else if((iso4_data->iso14443_3a_data->atqa[0] & 0x0F) == 0x02) {
|
||||
mf_plus_data->size = MfPlusSize4K;
|
||||
|
||||
FURI_LOG_D(TAG, "Mifare Plus X 4K SL3");
|
||||
error = MfPlusErrorNone;
|
||||
} else {
|
||||
FURI_LOG_D(TAG, "Sak 20 but no known Mifare Plus type (X)");
|
||||
}
|
||||
} else {
|
||||
FURI_LOG_D(TAG, "Sak 20 but no known Mifare Plus type");
|
||||
}
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
MfPlusError mf_plus_version_parse(MfPlusVersion* data, const BitBuffer* buf) {
|
||||
const bool can_parse = bit_buffer_get_size_bytes(buf) == sizeof(MfPlusVersion);
|
||||
|
||||
if(can_parse) {
|
||||
bit_buffer_write_bytes(buf, data, sizeof(MfPlusVersion));
|
||||
}
|
||||
|
||||
return can_parse ? MfPlusErrorNone : MfPlusErrorProtocol;
|
||||
}
|
||||
|
||||
bool mf_plus_version_load(MfPlusVersion* data, FlipperFormat* ff) {
|
||||
return flipper_format_read_hex(
|
||||
ff, MF_PLUS_FFF_VERSION_KEY, (uint8_t*)data, sizeof(MfPlusVersion));
|
||||
}
|
||||
|
||||
bool mf_plus_security_level_load(MfPlusSecurityLevel* data, FlipperFormat* ff) {
|
||||
FuriString* security_level_string = furi_string_alloc();
|
||||
flipper_format_read_string(ff, MF_PLUS_FFF_SECURITY_LEVEL_KEY, security_level_string);
|
||||
|
||||
// Take the last character of the string
|
||||
char security_level_char = furi_string_get_char(
|
||||
security_level_string, furi_string_utf8_length(security_level_string) - 1);
|
||||
|
||||
switch(security_level_char) {
|
||||
case '0':
|
||||
*data = MfPlusSecurityLevel0;
|
||||
break;
|
||||
case '1':
|
||||
*data = MfPlusSecurityLevel1;
|
||||
break;
|
||||
case '2':
|
||||
*data = MfPlusSecurityLevel2;
|
||||
break;
|
||||
case '3':
|
||||
*data = MfPlusSecurityLevel3;
|
||||
break;
|
||||
default:
|
||||
*data = MfPlusSecurityLevelUnknown;
|
||||
break;
|
||||
}
|
||||
|
||||
furi_string_free(security_level_string);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool mf_plus_type_load(MfPlusType* data, FlipperFormat* ff) {
|
||||
FuriString* type_string = furi_string_alloc();
|
||||
flipper_format_read_string(ff, MF_PLUS_FFF_CARD_TYPE_KEY, type_string);
|
||||
|
||||
if(furi_string_equal_str(type_string, "Mifare Plus")) {
|
||||
*data = MfPlusTypePlus;
|
||||
} else if(furi_string_equal_str(type_string, "Mifare Plus X")) {
|
||||
*data = MfPlusTypeX;
|
||||
} else if(furi_string_equal_str(type_string, "Mifare Plus S")) {
|
||||
*data = MfPlusTypeS;
|
||||
} else if(furi_string_equal_str(type_string, "Mifare Plus SE")) {
|
||||
*data = MfPlusTypeSE;
|
||||
} else if(furi_string_equal_str(type_string, "Mifare Plus EV1")) {
|
||||
*data = MfPlusTypeEV1;
|
||||
} else if(furi_string_equal_str(type_string, "Mifare Plus EV2")) {
|
||||
*data = MfPlusTypeEV2;
|
||||
} else {
|
||||
*data = MfPlusTypeUnknown;
|
||||
}
|
||||
|
||||
furi_string_free(type_string);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool mf_plus_size_load(MfPlusSize* data, FlipperFormat* ff) {
|
||||
FuriString* size_string = furi_string_alloc();
|
||||
flipper_format_read_string(ff, MF_PLUS_FFF_MEMORY_SIZE_KEY, size_string);
|
||||
|
||||
if(furi_string_equal_str(size_string, "1K")) {
|
||||
*data = MfPlusSize1K;
|
||||
} else if(furi_string_equal_str(size_string, "2K")) {
|
||||
*data = MfPlusSize2K;
|
||||
} else if(furi_string_equal_str(size_string, "4K")) {
|
||||
*data = MfPlusSize4K;
|
||||
} else {
|
||||
*data = MfPlusSizeUnknown;
|
||||
}
|
||||
|
||||
furi_string_free(size_string);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool mf_plus_version_save(const MfPlusVersion* data, FlipperFormat* ff) {
|
||||
return flipper_format_write_hex(
|
||||
ff, MF_PLUS_FFF_VERSION_KEY, (const uint8_t*)data, sizeof(MfPlusVersion));
|
||||
}
|
||||
|
||||
bool mf_plus_security_level_save(const MfPlusSecurityLevel* data, FlipperFormat* ff) {
|
||||
FuriString* security_level_string = furi_string_alloc();
|
||||
|
||||
switch(*data) {
|
||||
case MfPlusSecurityLevel0:
|
||||
furi_string_cat(security_level_string, "SL0");
|
||||
break;
|
||||
case MfPlusSecurityLevel1:
|
||||
furi_string_cat(security_level_string, "SL1");
|
||||
break;
|
||||
case MfPlusSecurityLevel2:
|
||||
furi_string_cat(security_level_string, "SL2");
|
||||
break;
|
||||
case MfPlusSecurityLevel3:
|
||||
furi_string_cat(security_level_string, "SL3");
|
||||
break;
|
||||
default:
|
||||
furi_string_cat(security_level_string, "Unknown");
|
||||
break;
|
||||
}
|
||||
|
||||
bool success =
|
||||
flipper_format_write_string(ff, MF_PLUS_FFF_SECURITY_LEVEL_KEY, security_level_string);
|
||||
furi_string_free(security_level_string);
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
bool mf_plus_type_save(const MfPlusType* data, FlipperFormat* ff) {
|
||||
FuriString* type_string = furi_string_alloc();
|
||||
|
||||
switch(*data) {
|
||||
case MfPlusTypePlus:
|
||||
furi_string_cat(type_string, "Mifare Plus");
|
||||
break;
|
||||
case MfPlusTypeX:
|
||||
furi_string_cat(type_string, "Mifare Plus X");
|
||||
break;
|
||||
case MfPlusTypeS:
|
||||
furi_string_cat(type_string, "Mifare Plus S");
|
||||
break;
|
||||
case MfPlusTypeSE:
|
||||
furi_string_cat(type_string, "Mifare Plus SE");
|
||||
break;
|
||||
case MfPlusTypeEV1:
|
||||
furi_string_cat(type_string, "Mifare Plus EV1");
|
||||
break;
|
||||
case MfPlusTypeEV2:
|
||||
furi_string_cat(type_string, "Mifare Plus EV2");
|
||||
break;
|
||||
default:
|
||||
furi_string_cat(type_string, "Unknown");
|
||||
break;
|
||||
}
|
||||
|
||||
bool success = flipper_format_write_string(ff, MF_PLUS_FFF_CARD_TYPE_KEY, type_string);
|
||||
furi_string_free(type_string);
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
bool mf_plus_size_save(const MfPlusSize* data, FlipperFormat* ff) {
|
||||
FuriString* size_string = furi_string_alloc();
|
||||
|
||||
switch(*data) {
|
||||
case MfPlusSize1K:
|
||||
furi_string_cat(size_string, "1K");
|
||||
break;
|
||||
case MfPlusSize2K:
|
||||
furi_string_cat(size_string, "2K");
|
||||
break;
|
||||
case MfPlusSize4K:
|
||||
furi_string_cat(size_string, "4K");
|
||||
break;
|
||||
default:
|
||||
furi_string_cat(size_string, "Unknown");
|
||||
break;
|
||||
}
|
||||
|
||||
bool success = flipper_format_write_string(ff, MF_PLUS_FFF_MEMORY_SIZE_KEY, size_string);
|
||||
furi_string_free(size_string);
|
||||
|
||||
return success;
|
||||
}
|
29
lib/nfc/protocols/mf_plus/mf_plus_i.h
Normal file
29
lib/nfc/protocols/mf_plus/mf_plus_i.h
Normal file
|
@ -0,0 +1,29 @@
|
|||
#pragma once
|
||||
|
||||
#include "mf_plus.h"
|
||||
|
||||
#define MF_PLUS_FFF_PICC_PREFIX "PICC"
|
||||
|
||||
MfPlusError mf_plus_get_type_from_version(
|
||||
const Iso14443_4aData* iso14443_4a_data,
|
||||
MfPlusData* mf_plus_data);
|
||||
|
||||
MfPlusError mf_plus_get_type_from_iso4(const Iso14443_4aData* iso4_data, MfPlusData* mf_plus_data);
|
||||
|
||||
MfPlusError mf_plus_version_parse(MfPlusVersion* data, const BitBuffer* buf);
|
||||
|
||||
bool mf_plus_version_load(MfPlusVersion* data, FlipperFormat* ff);
|
||||
|
||||
bool mf_plus_security_level_load(MfPlusSecurityLevel* data, FlipperFormat* ff);
|
||||
|
||||
bool mf_plus_type_load(MfPlusType* data, FlipperFormat* ff);
|
||||
|
||||
bool mf_plus_size_load(MfPlusSize* data, FlipperFormat* ff);
|
||||
|
||||
bool mf_plus_version_save(const MfPlusVersion* data, FlipperFormat* ff);
|
||||
|
||||
bool mf_plus_security_level_save(const MfPlusSecurityLevel* data, FlipperFormat* ff);
|
||||
|
||||
bool mf_plus_type_save(const MfPlusType* data, FlipperFormat* ff);
|
||||
|
||||
bool mf_plus_size_save(const MfPlusSize* data, FlipperFormat* ff);
|
210
lib/nfc/protocols/mf_plus/mf_plus_poller.c
Normal file
210
lib/nfc/protocols/mf_plus/mf_plus_poller.c
Normal file
|
@ -0,0 +1,210 @@
|
|||
#include "mf_plus_poller_i.h"
|
||||
#include "mf_plus_i.h"
|
||||
|
||||
#include <nfc/protocols/nfc_poller_base.h>
|
||||
|
||||
#include <furi.h>
|
||||
|
||||
#define TAG "MfPlusPoller"
|
||||
|
||||
#define MF_PLUS_BUF_SIZE (64U)
|
||||
#define MF_PLUS_RESULT_BUF_SIZE (512U)
|
||||
|
||||
typedef NfcCommand (*MfPlusPollerReadHandler)(MfPlusPoller* instance);
|
||||
|
||||
const MfPlusData* mf_plus_poller_get_data(MfPlusPoller* instance) {
|
||||
furi_assert(instance);
|
||||
|
||||
return instance->data;
|
||||
}
|
||||
|
||||
MfPlusPoller* mf_plus_poller_alloc(Iso14443_4aPoller* iso14443_4a_poller) {
|
||||
furi_assert(iso14443_4a_poller);
|
||||
|
||||
MfPlusPoller* instance = malloc(sizeof(MfPlusPoller));
|
||||
|
||||
instance->iso14443_4a_poller = iso14443_4a_poller;
|
||||
|
||||
instance->data = mf_plus_alloc();
|
||||
|
||||
instance->tx_buffer = bit_buffer_alloc(MF_PLUS_BUF_SIZE);
|
||||
instance->rx_buffer = bit_buffer_alloc(MF_PLUS_BUF_SIZE);
|
||||
instance->input_buffer = bit_buffer_alloc(MF_PLUS_BUF_SIZE);
|
||||
instance->result_buffer = bit_buffer_alloc(MF_PLUS_RESULT_BUF_SIZE);
|
||||
|
||||
instance->general_event.protocol = NfcProtocolMfPlus;
|
||||
instance->general_event.event_data = &instance->mfp_event;
|
||||
instance->general_event.instance = instance;
|
||||
|
||||
instance->mfp_event.data = &instance->mfp_event_data;
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
static NfcCommand mf_plus_poller_handler_idle(MfPlusPoller* instance) {
|
||||
furi_assert(instance);
|
||||
|
||||
bit_buffer_reset(instance->input_buffer);
|
||||
bit_buffer_reset(instance->result_buffer);
|
||||
bit_buffer_reset(instance->tx_buffer);
|
||||
bit_buffer_reset(instance->rx_buffer);
|
||||
|
||||
iso14443_4a_copy(
|
||||
instance->data->iso14443_4a_data,
|
||||
iso14443_4a_poller_get_data(instance->iso14443_4a_poller));
|
||||
|
||||
instance->state = MfPlusPollerStateReadVersion;
|
||||
return NfcCommandContinue;
|
||||
}
|
||||
|
||||
static NfcCommand mf_plus_poller_handler_read_version(MfPlusPoller* instance) {
|
||||
MfPlusError error = mf_plus_poller_read_version(instance, &instance->data->version);
|
||||
if(error == MfPlusErrorNone) {
|
||||
instance->state = MfPlusPollerStateParseVersion;
|
||||
} else {
|
||||
instance->state = MfPlusPollerStateParseIso4;
|
||||
}
|
||||
|
||||
return NfcCommandContinue;
|
||||
}
|
||||
|
||||
static NfcCommand mf_plus_poller_handler_parse_version(MfPlusPoller* instance) {
|
||||
furi_assert(instance);
|
||||
|
||||
MfPlusError error = mf_plus_get_type_from_version(
|
||||
iso14443_4a_poller_get_data(instance->iso14443_4a_poller), instance->data);
|
||||
if(error == MfPlusErrorNone) {
|
||||
instance->state = MfPlusPollerStateReadSuccess;
|
||||
} else {
|
||||
instance->error = error;
|
||||
instance->state = MfPlusPollerStateReadFailed;
|
||||
}
|
||||
|
||||
return NfcCommandContinue;
|
||||
}
|
||||
|
||||
static NfcCommand mf_plus_poller_handler_parse_iso4(MfPlusPoller* instance) {
|
||||
furi_assert(instance);
|
||||
|
||||
MfPlusError error = mf_plus_get_type_from_iso4(
|
||||
iso14443_4a_poller_get_data(instance->iso14443_4a_poller), instance->data);
|
||||
if(error == MfPlusErrorNone) {
|
||||
instance->state = MfPlusPollerStateReadSuccess;
|
||||
} else {
|
||||
instance->error = error;
|
||||
instance->state = MfPlusPollerStateReadFailed;
|
||||
}
|
||||
|
||||
return NfcCommandContinue;
|
||||
}
|
||||
|
||||
static NfcCommand mf_plus_poller_handler_read_failed(MfPlusPoller* instance) {
|
||||
furi_assert(instance);
|
||||
|
||||
FURI_LOG_D(TAG, "Read failed");
|
||||
iso14443_4a_poller_halt(instance->iso14443_4a_poller);
|
||||
|
||||
instance->mfp_event.type = MfPlusPollerEventTypeReadFailed;
|
||||
instance->mfp_event.data->error = instance->error;
|
||||
NfcCommand command = instance->callback(instance->general_event, instance->context);
|
||||
instance->state = MfPlusPollerStateIdle;
|
||||
|
||||
return command;
|
||||
}
|
||||
|
||||
static NfcCommand mf_plus_poller_handler_read_success(MfPlusPoller* instance) {
|
||||
furi_assert(instance);
|
||||
|
||||
FURI_LOG_D(TAG, "Read success");
|
||||
iso14443_4a_poller_halt(instance->iso14443_4a_poller);
|
||||
|
||||
instance->mfp_event.type = MfPlusPollerEventTypeReadSuccess;
|
||||
NfcCommand command = instance->callback(instance->general_event, instance->context);
|
||||
|
||||
return command;
|
||||
}
|
||||
|
||||
static const MfPlusPollerReadHandler mf_plus_poller_read_handler[MfPlusPollerStateNum] = {
|
||||
[MfPlusPollerStateIdle] = mf_plus_poller_handler_idle,
|
||||
[MfPlusPollerStateReadVersion] = mf_plus_poller_handler_read_version,
|
||||
[MfPlusPollerStateParseVersion] = mf_plus_poller_handler_parse_version,
|
||||
[MfPlusPollerStateParseIso4] = mf_plus_poller_handler_parse_iso4,
|
||||
[MfPlusPollerStateReadFailed] = mf_plus_poller_handler_read_failed,
|
||||
[MfPlusPollerStateReadSuccess] = mf_plus_poller_handler_read_success,
|
||||
};
|
||||
|
||||
static void mf_plus_poller_set_callback(
|
||||
MfPlusPoller* instance,
|
||||
NfcGenericCallback callback,
|
||||
void* context) {
|
||||
furi_assert(instance);
|
||||
furi_assert(callback);
|
||||
|
||||
instance->callback = callback;
|
||||
instance->context = context;
|
||||
}
|
||||
|
||||
static NfcCommand mf_plus_poller_run(NfcGenericEvent event, void* context) {
|
||||
furi_assert(context);
|
||||
furi_assert(event.protocol = NfcProtocolIso14443_4a);
|
||||
furi_assert(event.event_data);
|
||||
|
||||
MfPlusPoller* instance = context;
|
||||
const Iso14443_4aPollerEvent* iso14443_4a_event = event.event_data;
|
||||
|
||||
NfcCommand command = NfcCommandContinue;
|
||||
|
||||
if(iso14443_4a_event->type == Iso14443_4aPollerEventTypeReady) {
|
||||
command = mf_plus_poller_read_handler[instance->state](instance);
|
||||
} else if(iso14443_4a_event->type == Iso14443_4aPollerEventTypeError) {
|
||||
instance->mfp_event.type = MfPlusPollerEventTypeReadFailed;
|
||||
command = instance->callback(instance->general_event, instance->context);
|
||||
}
|
||||
|
||||
return command;
|
||||
}
|
||||
|
||||
void mf_plus_poller_free(MfPlusPoller* instance) {
|
||||
furi_assert(instance);
|
||||
furi_assert(instance->data);
|
||||
|
||||
bit_buffer_free(instance->tx_buffer);
|
||||
bit_buffer_free(instance->rx_buffer);
|
||||
bit_buffer_free(instance->input_buffer);
|
||||
bit_buffer_free(instance->result_buffer);
|
||||
mf_plus_free(instance->data);
|
||||
free(instance);
|
||||
}
|
||||
|
||||
static bool mf_plus_poller_detect(NfcGenericEvent event, void* context) {
|
||||
furi_assert(context);
|
||||
furi_assert(event.protocol = NfcProtocolIso14443_4a);
|
||||
furi_assert(event.event_data);
|
||||
|
||||
MfPlusPoller* instance = context;
|
||||
Iso14443_4aPollerEvent* iso14443_4a_event = event.event_data;
|
||||
|
||||
MfPlusError error = MfPlusErrorUnknown;
|
||||
|
||||
if(iso14443_4a_event->type == Iso14443_4aPollerEventTypeReady) {
|
||||
error = mf_plus_poller_read_version(instance, &instance->data->version);
|
||||
if(error == MfPlusErrorNone) {
|
||||
error = mf_plus_get_type_from_version(
|
||||
iso14443_4a_poller_get_data(instance->iso14443_4a_poller), instance->data);
|
||||
} else {
|
||||
error = mf_plus_get_type_from_iso4(
|
||||
iso14443_4a_poller_get_data(instance->iso14443_4a_poller), instance->data);
|
||||
}
|
||||
}
|
||||
|
||||
return (error == MfPlusErrorNone);
|
||||
}
|
||||
|
||||
const NfcPollerBase mf_plus_poller = {
|
||||
.alloc = (NfcPollerAlloc)mf_plus_poller_alloc,
|
||||
.free = (NfcPollerFree)mf_plus_poller_free,
|
||||
.set_callback = (NfcPollerSetCallback)mf_plus_poller_set_callback,
|
||||
.run = (NfcPollerRun)mf_plus_poller_run,
|
||||
.detect = (NfcPollerDetect)mf_plus_poller_detect,
|
||||
.get_data = (NfcPollerGetData)mf_plus_poller_get_data,
|
||||
};
|
55
lib/nfc/protocols/mf_plus/mf_plus_poller.h
Normal file
55
lib/nfc/protocols/mf_plus/mf_plus_poller.h
Normal file
|
@ -0,0 +1,55 @@
|
|||
#pragma once
|
||||
|
||||
#include "mf_plus.h"
|
||||
|
||||
#include <lib/nfc/protocols/iso14443_4a/iso14443_4a.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief MIFARE Plus poller opaque type definition.
|
||||
*/
|
||||
typedef struct MfPlusPoller MfPlusPoller;
|
||||
|
||||
/**
|
||||
* @brief Enumeration of possible MfPlus poller event types.
|
||||
*/
|
||||
|
||||
typedef enum {
|
||||
MfPlusPollerEventTypeReadSuccess, /**< Card was read successfully. */
|
||||
MfPlusPollerEventTypeReadFailed, /**< Poller failed to read the card. */
|
||||
} MfPlusPollerEventType;
|
||||
|
||||
/**
|
||||
* @brief MIFARE Plus poller event data.
|
||||
*/
|
||||
typedef union {
|
||||
MfPlusError error; /**< Error code indicating card reading fail reason. */
|
||||
} MfPlusPollerEventData;
|
||||
|
||||
/**
|
||||
* @brief MIFARE Plus poller event structure.
|
||||
*
|
||||
* Upon emission of an event, an instance of this struct will be passed to the callback.
|
||||
*/
|
||||
typedef struct {
|
||||
MfPlusPollerEventType type; /**< Type of emitted event. */
|
||||
MfPlusPollerEventData* data; /**< Pointer to event specific data. */
|
||||
} MfPlusPollerEvent;
|
||||
|
||||
/**
|
||||
* @brief Read MfPlus card version.
|
||||
*
|
||||
* Must ONLY be used inside the callback function.
|
||||
*
|
||||
* @param[in, out] instance pointer to the instance to be used in the transaction.
|
||||
* @param[out] data pointer to the MfPlusVersion structure to be filled with version data.
|
||||
* @return MfPlusErrorNone on success, an error code on failure.
|
||||
*/
|
||||
MfPlusError mf_plus_poller_read_version(MfPlusPoller* instance, MfPlusVersion* data);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
5
lib/nfc/protocols/mf_plus/mf_plus_poller_defs.h
Normal file
5
lib/nfc/protocols/mf_plus/mf_plus_poller_defs.h
Normal file
|
@ -0,0 +1,5 @@
|
|||
#pragma once
|
||||
|
||||
#include <nfc/protocols/nfc_poller_base.h>
|
||||
|
||||
extern const NfcPollerBase mf_plus_poller;
|
59
lib/nfc/protocols/mf_plus/mf_plus_poller_i.c
Normal file
59
lib/nfc/protocols/mf_plus/mf_plus_poller_i.c
Normal file
|
@ -0,0 +1,59 @@
|
|||
#include "mf_plus_poller_i.h"
|
||||
|
||||
#include <furi.h>
|
||||
|
||||
#include "mf_plus_i.h"
|
||||
|
||||
#define TAG "MfPlusPoller"
|
||||
|
||||
MfPlusError mf_plus_process_error(Iso14443_4aError error) {
|
||||
switch(error) {
|
||||
case Iso14443_4aErrorNone:
|
||||
return MfPlusErrorNone;
|
||||
case Iso14443_4aErrorNotPresent:
|
||||
return MfPlusErrorNotPresent;
|
||||
case Iso14443_4aErrorTimeout:
|
||||
return MfPlusErrorTimeout;
|
||||
default:
|
||||
return MfPlusErrorProtocol;
|
||||
}
|
||||
}
|
||||
|
||||
MfPlusError mf_plus_poller_send_chunk(
|
||||
MfPlusPoller* instance,
|
||||
const BitBuffer* tx_buffer,
|
||||
BitBuffer* rx_buffer) {
|
||||
furi_assert(instance);
|
||||
furi_assert(instance->iso14443_4a_poller);
|
||||
furi_assert(instance->tx_buffer);
|
||||
furi_assert(instance->rx_buffer);
|
||||
furi_assert(tx_buffer);
|
||||
furi_assert(rx_buffer);
|
||||
|
||||
Iso14443_4aError iso14443_4a_error = iso14443_4a_poller_send_block(
|
||||
instance->iso14443_4a_poller, tx_buffer, instance->rx_buffer);
|
||||
MfPlusError error = mf_plus_process_error(iso14443_4a_error);
|
||||
|
||||
if(error == MfPlusErrorNone) {
|
||||
bit_buffer_copy(rx_buffer, instance->rx_buffer);
|
||||
}
|
||||
|
||||
bit_buffer_reset(instance->tx_buffer);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
MfPlusError mf_plus_poller_read_version(MfPlusPoller* instance, MfPlusVersion* data) {
|
||||
furi_check(instance);
|
||||
|
||||
bit_buffer_reset(instance->input_buffer);
|
||||
bit_buffer_append_byte(instance->input_buffer, MF_PLUS_CMD_GET_VERSION);
|
||||
|
||||
MfPlusError error =
|
||||
mf_plus_poller_send_chunk(instance, instance->input_buffer, instance->result_buffer);
|
||||
if(error == MfPlusErrorNone) {
|
||||
error = mf_plus_version_parse(data, instance->result_buffer);
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
56
lib/nfc/protocols/mf_plus/mf_plus_poller_i.h
Normal file
56
lib/nfc/protocols/mf_plus/mf_plus_poller_i.h
Normal file
|
@ -0,0 +1,56 @@
|
|||
#pragma once
|
||||
|
||||
#include "mf_plus_poller.h"
|
||||
|
||||
#include <lib/nfc/protocols/iso14443_4a/iso14443_4a_poller_i.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define MF_PLUS_FWT_FC (60000)
|
||||
|
||||
typedef enum {
|
||||
MfPlusCardStateDetected,
|
||||
MfPlusCardStateLost,
|
||||
} MfPlusCardState;
|
||||
|
||||
typedef enum {
|
||||
MfPlusPollerStateIdle,
|
||||
MfPlusPollerStateReadVersion,
|
||||
MfPlusPollerStateParseVersion,
|
||||
MfPlusPollerStateParseIso4,
|
||||
MfPlusPollerStateReadFailed,
|
||||
MfPlusPollerStateReadSuccess,
|
||||
|
||||
MfPlusPollerStateNum,
|
||||
} MfPlusPollerState;
|
||||
|
||||
struct MfPlusPoller {
|
||||
Iso14443_4aPoller* iso14443_4a_poller;
|
||||
|
||||
MfPlusData* data;
|
||||
MfPlusPollerState state;
|
||||
|
||||
BitBuffer* tx_buffer;
|
||||
BitBuffer* rx_buffer;
|
||||
BitBuffer* input_buffer;
|
||||
BitBuffer* result_buffer;
|
||||
|
||||
MfPlusError error;
|
||||
NfcGenericEvent general_event;
|
||||
MfPlusPollerEvent mfp_event;
|
||||
MfPlusPollerEventData mfp_event_data;
|
||||
NfcGenericCallback callback;
|
||||
void* context;
|
||||
};
|
||||
|
||||
MfPlusError mf_plus_process_error(Iso14443_4aError error);
|
||||
|
||||
MfPlusPoller* mf_plus_poller_alloc(Iso14443_4aPoller* iso14443_4a_poller);
|
||||
|
||||
void mf_plus_poller_free(MfPlusPoller* instance);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
|
@ -20,6 +20,7 @@
|
|||
#include <nfc/protocols/felica/felica.h>
|
||||
#include <nfc/protocols/mf_ultralight/mf_ultralight.h>
|
||||
#include <nfc/protocols/mf_classic/mf_classic.h>
|
||||
#include <nfc/protocols/mf_plus/mf_plus.h>
|
||||
#include <nfc/protocols/mf_desfire/mf_desfire.h>
|
||||
#include <nfc/protocols/slix/slix_device_defs.h>
|
||||
#include <nfc/protocols/st25tb/st25tb.h>
|
||||
|
@ -39,6 +40,7 @@ const NfcDeviceBase* nfc_devices[NfcProtocolNum] = {
|
|||
[NfcProtocolFelica] = &nfc_device_felica,
|
||||
[NfcProtocolMfUltralight] = &nfc_device_mf_ultralight,
|
||||
[NfcProtocolMfClassic] = &nfc_device_mf_classic,
|
||||
[NfcProtocolMfPlus] = &nfc_device_mf_plus,
|
||||
[NfcProtocolMfDesfire] = &nfc_device_mf_desfire,
|
||||
[NfcProtocolSlix] = &nfc_device_slix,
|
||||
[NfcProtocolSt25tb] = &nfc_device_st25tb,
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#include <nfc/protocols/felica/felica_poller_defs.h>
|
||||
#include <nfc/protocols/mf_ultralight/mf_ultralight_poller_defs.h>
|
||||
#include <nfc/protocols/mf_classic/mf_classic_poller_defs.h>
|
||||
#include <nfc/protocols/mf_plus/mf_plus_poller_defs.h>
|
||||
#include <nfc/protocols/mf_desfire/mf_desfire_poller_defs.h>
|
||||
#include <nfc/protocols/slix/slix_poller_defs.h>
|
||||
#include <nfc/protocols/st25tb/st25tb_poller_defs.h>
|
||||
|
@ -21,6 +22,7 @@ const NfcPollerBase* nfc_pollers_api[NfcProtocolNum] = {
|
|||
[NfcProtocolFelica] = &nfc_poller_felica,
|
||||
[NfcProtocolMfUltralight] = &mf_ultralight_poller,
|
||||
[NfcProtocolMfClassic] = &mf_classic_poller,
|
||||
[NfcProtocolMfPlus] = &mf_plus_poller,
|
||||
[NfcProtocolMfDesfire] = &mf_desfire_poller,
|
||||
[NfcProtocolSlix] = &nfc_poller_slix,
|
||||
/* Add new pollers here */
|
||||
|
|
|
@ -61,6 +61,7 @@ static const NfcProtocol nfc_protocol_iso14443_3b_children_protocol[] = {
|
|||
/** List of ISO14443-4A child protocols. */
|
||||
static const NfcProtocol nfc_protocol_iso14443_4a_children_protocol[] = {
|
||||
NfcProtocolMfDesfire,
|
||||
NfcProtocolMfPlus,
|
||||
};
|
||||
|
||||
/** List of ISO115693-3 child protocols. */
|
||||
|
@ -128,6 +129,12 @@ static const NfcProtocolTreeNode nfc_protocol_nodes[NfcProtocolNum] = {
|
|||
.children_num = 0,
|
||||
.children_protocol = NULL,
|
||||
},
|
||||
[NfcProtocolMfPlus] =
|
||||
{
|
||||
.parent_protocol = NfcProtocolIso14443_4a,
|
||||
.children_num = 0,
|
||||
.children_protocol = NULL,
|
||||
},
|
||||
[NfcProtocolMfDesfire] =
|
||||
{
|
||||
.parent_protocol = NfcProtocolIso14443_4a,
|
||||
|
|
|
@ -184,6 +184,7 @@ typedef enum {
|
|||
NfcProtocolFelica,
|
||||
NfcProtocolMfUltralight,
|
||||
NfcProtocolMfClassic,
|
||||
NfcProtocolMfPlus,
|
||||
NfcProtocolMfDesfire,
|
||||
NfcProtocolSlix,
|
||||
NfcProtocolSt25tb,
|
||||
|
|
|
@ -156,6 +156,8 @@ Header,+,lib/nfc/protocols/mf_classic/mf_classic_poller.h,,
|
|||
Header,+,lib/nfc/protocols/mf_classic/mf_classic_poller_sync.h,,
|
||||
Header,+,lib/nfc/protocols/mf_desfire/mf_desfire.h,,
|
||||
Header,+,lib/nfc/protocols/mf_desfire/mf_desfire_poller.h,,
|
||||
Header,+,lib/nfc/protocols/mf_plus/mf_plus.h,,
|
||||
Header,+,lib/nfc/protocols/mf_plus/mf_plus_poller.h,,
|
||||
Header,+,lib/nfc/protocols/mf_ultralight/mf_ultralight.h,,
|
||||
Header,+,lib/nfc/protocols/mf_ultralight/mf_ultralight_listener.h,,
|
||||
Header,+,lib/nfc/protocols/mf_ultralight/mf_ultralight_poller.h,,
|
||||
|
@ -2531,6 +2533,19 @@ Function,+,mf_desfire_save,_Bool,"const MfDesfireData*, FlipperFormat*"
|
|||
Function,+,mf_desfire_send_chunks,MfDesfireError,"MfDesfirePoller*, const BitBuffer*, BitBuffer*"
|
||||
Function,+,mf_desfire_set_uid,_Bool,"MfDesfireData*, const uint8_t*, size_t"
|
||||
Function,+,mf_desfire_verify,_Bool,"MfDesfireData*, const FuriString*"
|
||||
Function,+,mf_plus_alloc,MfPlusData*,
|
||||
Function,+,mf_plus_copy,void,"MfPlusData*, const MfPlusData*"
|
||||
Function,+,mf_plus_free,void,MfPlusData*
|
||||
Function,+,mf_plus_get_base_data,Iso14443_4aData*,const MfPlusData*
|
||||
Function,+,mf_plus_get_device_name,const char*,"const MfPlusData*, NfcDeviceNameType"
|
||||
Function,+,mf_plus_get_uid,const uint8_t*,"const MfPlusData*, size_t*"
|
||||
Function,+,mf_plus_is_equal,_Bool,"const MfPlusData*, const MfPlusData*"
|
||||
Function,+,mf_plus_load,_Bool,"MfPlusData*, FlipperFormat*, uint32_t"
|
||||
Function,+,mf_plus_poller_read_version,MfPlusError,"MfPlusPoller*, MfPlusVersion*"
|
||||
Function,+,mf_plus_reset,void,MfPlusData*
|
||||
Function,+,mf_plus_save,_Bool,"const MfPlusData*, FlipperFormat*"
|
||||
Function,+,mf_plus_set_uid,_Bool,"MfPlusData*, const uint8_t*, size_t"
|
||||
Function,+,mf_plus_verify,_Bool,"MfPlusData*, const FuriString*"
|
||||
Function,+,mf_ultralight_alloc,MfUltralightData*,
|
||||
Function,+,mf_ultralight_copy,void,"MfUltralightData*, const MfUltralightData*"
|
||||
Function,+,mf_ultralight_detect_protocol,_Bool,const Iso14443_3aData*
|
||||
|
@ -3843,6 +3858,7 @@ Variable,+,message_vibro_on,const NotificationMessage,
|
|||
Variable,-,nfc_device_felica,const NfcDeviceBase,
|
||||
Variable,-,nfc_device_mf_classic,const NfcDeviceBase,
|
||||
Variable,-,nfc_device_mf_desfire,const NfcDeviceBase,
|
||||
Variable,-,nfc_device_mf_plus,const NfcDeviceBase,
|
||||
Variable,-,nfc_device_mf_ultralight,const NfcDeviceBase,
|
||||
Variable,-,nfc_device_st25tb,const NfcDeviceBase,
|
||||
Variable,+,sequence_audiovisual_alert,const NotificationSequence,
|
||||
|
|
|
Loading…
Reference in a new issue