Merge remote-tracking branch 'OFW/astra/3746-mfp-detect' into nfc_mfplus

This commit is contained in:
MX 2024-06-06 00:55:54 +03:00
commit 05457b41e4
No known key found for this signature in database
GPG key ID: 7CCC66B7DBDD1C83
11 changed files with 644 additions and 1077 deletions

View file

@ -26,23 +26,26 @@ static void nfc_scene_info_on_enter_mf_plus(NfcApp* instance) {
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));
FURI_LOG_D(
"MFP",
"Read success: %s",
nfc_device_get_name(instance->nfc_device, NfcDeviceNameTypeFull));
view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventPollerSuccess);
return NfcCommandStop;
command = NfcCommandStop;
} else if(mf_plus_event->type == MfPlusPollerEventTypeReadFailed) {
view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventPollerFailure);
command = NfcCommandStop;
}
return NfcCommandContinue;
return command;
}
static void nfc_scene_read_on_enter_mf_plus(NfcApp* instance) {
@ -76,7 +79,7 @@ static void nfc_scene_emulate_on_enter_mf_plus(NfcApp* instance) {
}
const NfcProtocolSupportBase nfc_protocol_support_mf_plus = {
.features = NfcProtocolFeatureMoreInfo,
.features = NfcProtocolFeatureEmulateUid,
.scene_info =
{

View file

@ -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));

View 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

View file

@ -28,13 +28,9 @@ 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) &&
(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneReadMenu) ||
scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSavedMenu))) {
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};
consumed = scene_manager_search_and_switch_to_previous_scene_one_of(
nfc->scene_manager, possible_scenes, COUNT_OF(possible_scenes));

View file

@ -60,6 +60,7 @@ MfPlusData* mf_plus_alloc(void) {
void mf_plus_free(MfPlusData* data) {
furi_check(data);
furi_string_free(data->device_name);
iso14443_4a_free(data->iso14443_4a_data);
free(data);
@ -67,9 +68,10 @@ void mf_plus_free(MfPlusData* data) {
void mf_plus_reset(MfPlusData* data) {
furi_check(data);
iso14443_4a_reset(data->iso14443_4a_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;
@ -78,8 +80,8 @@ void mf_plus_reset(MfPlusData* data) {
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);
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;
@ -92,10 +94,9 @@ bool mf_plus_verify(MfPlusData* data, const FuriString* device_type) {
}
bool mf_plus_load(MfPlusData* data, FlipperFormat* ff, uint32_t version) {
furi_assert(data);
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;
@ -109,10 +110,9 @@ bool mf_plus_load(MfPlusData* data, FlipperFormat* ff, uint32_t version) {
}
bool mf_plus_save(const MfPlusData* data, FlipperFormat* ff) {
furi_assert(data);
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;
@ -127,10 +127,10 @@ bool mf_plus_save(const MfPlusData* data, FlipperFormat* ff) {
}
bool mf_plus_is_equal(const MfPlusData* data, const MfPlusData* other) {
furi_assert(data);
furi_assert(other);
bool equal = false;
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;
@ -146,44 +146,35 @@ bool mf_plus_is_equal(const MfPlusData* data, const MfPlusData* other) {
const char* mf_plus_get_device_name(const MfPlusData* data, NfcDeviceNameType name_type) {
furi_check(data);
FuriString* full_name = furi_string_alloc();
const char* name = NULL;
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");
}
do {
if(name_type == NfcDeviceNameTypeFull) {
furi_string_reset(data->device_name);
furi_string_cat_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]);
name = furi_string_get_cstr(data->device_name);
FURI_LOG_D("Mifare Plus", "Full name: %s", name);
} else if(name_type == NfcDeviceNameTypeShort) {
name = "Mifare Plus";
} else {
break;
}
} while(false);
furi_string_free(full_name);
FURI_LOG_D("Mifare Plus", "Name: %s", name);
return name;
return furi_string_get_cstr(data->device_name);
}
const uint8_t* mf_plus_get_uid(const MfPlusData* data, size_t* uid_len) {
furi_assert(data);
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_assert(data);
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;
}

View file

@ -2,13 +2,11 @@
#include <lib/nfc/protocols/iso14443_4a/iso14443_4a.h>
#include <lib/toolbox/simple_array.h>
#ifdef __cplusplus
extern "C" {
#endif
#define MF_PLUS_UID_SIZE (7)
#define MF_PLUS_UID_SIZE_MAX (7)
#define MF_PLUS_BATCH_SIZE (5)
#define MF_PLUS_CMD_GET_VERSION (0x60)
@ -71,7 +69,7 @@ typedef struct {
uint8_t sw_storage;
uint8_t sw_proto;
uint8_t uid[MF_PLUS_UID_SIZE];
uint8_t uid[MF_PLUS_UID_SIZE_MAX];
uint8_t batch[MF_PLUS_BATCH_SIZE];
uint8_t prod_week;
uint8_t prod_year;

View file

@ -8,44 +8,233 @@
#define MF_PLUS_FFF_CARD_TYPE_KEY "Card Type"
#define MF_PLUS_FFF_MEMORY_SIZE_KEY "Memory Size"
bool mf_plus_version_parse(MfPlusVersion* data, const BitBuffer* buf) {
#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;
}
bool mf_plus_security_level_parse(MfPlusSecurityLevel* data, const BitBuffer* buf) {
const bool can_parse = bit_buffer_get_size_bytes(buf) == sizeof(MfPlusSecurityLevel);
if(can_parse) {
bit_buffer_write_bytes(buf, data, sizeof(MfPlusSecurityLevel));
}
return can_parse;
}
bool mf_plus_type_parse(MfPlusType* data, const BitBuffer* buf) {
const bool can_parse = bit_buffer_get_size_bytes(buf) == sizeof(MfPlusType);
if(can_parse) {
bit_buffer_write_bytes(buf, data, sizeof(MfPlusType));
}
return can_parse;
}
bool mf_plus_size_parse(MfPlusSize* data, const BitBuffer* buf) {
const bool can_parse = bit_buffer_get_size_bytes(buf) == sizeof(MfPlusSize);
if(can_parse) {
bit_buffer_write_bytes(buf, data, sizeof(MfPlusSize));
}
return can_parse;
return can_parse ? MfPlusErrorNone : MfPlusErrorProtocol;
}
bool mf_plus_version_load(MfPlusVersion* data, FlipperFormat* ff) {
@ -152,10 +341,11 @@ bool mf_plus_security_level_save(const MfPlusSecurityLevel* data, FlipperFormat*
break;
}
flipper_format_write_string(ff, MF_PLUS_FFF_SECURITY_LEVEL_KEY, security_level_string);
bool success =
flipper_format_write_string(ff, MF_PLUS_FFF_SECURITY_LEVEL_KEY, security_level_string);
furi_string_free(security_level_string);
return true;
return success;
}
bool mf_plus_type_save(const MfPlusType* data, FlipperFormat* ff) {
@ -185,10 +375,10 @@ bool mf_plus_type_save(const MfPlusType* data, FlipperFormat* ff) {
break;
}
flipper_format_write_string(ff, MF_PLUS_FFF_CARD_TYPE_KEY, type_string);
bool success = flipper_format_write_string(ff, MF_PLUS_FFF_CARD_TYPE_KEY, type_string);
furi_string_free(type_string);
return true;
return success;
}
bool mf_plus_size_save(const MfPlusSize* data, FlipperFormat* ff) {
@ -209,8 +399,8 @@ bool mf_plus_size_save(const MfPlusSize* data, FlipperFormat* ff) {
break;
}
flipper_format_write_string(ff, MF_PLUS_FFF_MEMORY_SIZE_KEY, size_string);
bool success = flipper_format_write_string(ff, MF_PLUS_FFF_MEMORY_SIZE_KEY, size_string);
furi_string_free(size_string);
return true;
}
return success;
}

View file

@ -4,13 +4,13 @@
#define MF_PLUS_FFF_PICC_PREFIX "PICC"
bool mf_plus_version_parse(MfPlusVersion* data, const BitBuffer* buf);
MfPlusError mf_plus_get_type_from_version(
const Iso14443_4aData* iso14443_4a_data,
MfPlusData* mf_plus_data);
bool mf_plus_security_level_parse(MfPlusSecurityLevel* data, const BitBuffer* buf);
MfPlusError mf_plus_get_type_from_iso4(const Iso14443_4aData* iso4_data, MfPlusData* mf_plus_data);
bool mf_plus_type_parse(MfPlusType* data, const BitBuffer* buf);
bool mf_plus_size_parse(MfPlusSize* data, const BitBuffer* buf);
MfPlusError mf_plus_version_parse(MfPlusVersion* data, const BitBuffer* buf);
bool mf_plus_version_load(MfPlusVersion* data, FlipperFormat* ff);

View file

@ -1,4 +1,5 @@
#include "mf_plus_poller_i.h"
#include "mf_plus_i.h"
#include <nfc/protocols/nfc_poller_base.h>
@ -9,13 +10,6 @@
#define MF_PLUS_BUF_SIZE (64U)
#define MF_PLUS_RESULT_BUF_SIZE (512U)
const char* mf_plus_ats_t1_tk_values[] = {
"\xC1\x05\x2F\x2F\x00\x35\xC7", // Mifare Plus S
"\xC1\x05\x2F\x2F\x01\xBC\xD6", // Mifare Plus X
"\xC1\x05\x2F\x2F\x00\xF6\xD1", // Mifare Plus SE
"\xC1\x05\x2F\x2F\x01\xF6\xD1", // Mifare Plus SE
};
typedef NfcCommand (*MfPlusPollerReadHandler)(MfPlusPoller* instance);
const MfPlusData* mf_plus_poller_get_data(MfPlusPoller* instance) {
@ -24,205 +18,10 @@ const MfPlusData* mf_plus_poller_get_data(MfPlusPoller* instance) {
return instance->data;
}
bool mf_plus_poller_get_type_from_iso4(const Iso14443_4aData* iso4_data, MfPlusData* mf_plus_data) {
furi_assert(iso4_data);
furi_assert(mf_plus_data);
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");
return true;
} 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");
return true;
} 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");
return true;
} else {
FURI_LOG_D(TAG, "Sak 08 but no known Mifare Plus type");
return false;
}
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");
return true;
} 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");
return true;
} else {
FURI_LOG_D(TAG, "Sak 18 but no known Mifare Plus type");
return false;
}
case 0x20:
if(memcmp(
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
mf_plus_data->type = MfPlusTypeS;
mf_plus_data->security_level = MfPlusSecurityLevel3;
if(iso4_data->iso14443_3a_data->atqa[1] == 0x04) {
// Mifare Plus S 2K SL3
mf_plus_data->size = MfPlusSize2K;
FURI_LOG_D(TAG, "Mifare Plus S 2K SL3");
} else if(iso4_data->iso14443_3a_data->atqa[1] == 0x02) {
// Mifare Plus S 4K SL3
mf_plus_data->size = MfPlusSize4K;
FURI_LOG_D(TAG, "Mifare Plus S 4K SL3");
} else {
FURI_LOG_D(TAG, "Sak 20 but no known Mifare Plus type (S)");
return false;
}
return true;
} else if(
memcmp(
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;
if(iso4_data->iso14443_3a_data->atqa[1] == 0x04) {
mf_plus_data->size = MfPlusSize2K;
FURI_LOG_D(TAG, "Mifare Plus X 2K SL3");
} else if(iso4_data->iso14443_3a_data->atqa[1] == 0x02) {
mf_plus_data->size = MfPlusSize4K;
FURI_LOG_D(TAG, "Mifare Plus X 4K SL3");
} else {
FURI_LOG_D(TAG, "Sak 20 but no known Mifare Plus type (X)");
return false;
}
return true;
} else {
FURI_LOG_D(TAG, "Sak 20 but no known Mifare Plus type");
return false;
}
}
FURI_LOG_D(TAG, "No known Mifare Plus type");
return false;
}
static bool mf_plus_poller_detect_type(MfPlusPoller* instance) {
furi_assert(instance);
bool detected = false;
const Iso14443_4aData* iso14443_4a_data =
iso14443_4a_poller_get_data(instance->iso14443_4a_poller);
const MfPlusError error = mf_plus_poller_read_version(instance, &instance->data->version);
if(error == MfPlusErrorNone) {
FURI_LOG_D(TAG, "Read version success: %d", error);
if(instance->data->version.hw_major == 0x02 || instance->data->version.hw_major == 0x82) {
detected = true;
if(iso14443_4a_data->iso14443_3a_data->sak == 0x10) {
// Mifare Plus 2K SL2
instance->data->type = MfPlusTypePlus;
instance->data->size = MfPlusSize2K;
instance->data->security_level = MfPlusSecurityLevel2;
} else if(iso14443_4a_data->iso14443_3a_data->sak == 0x11) {
// Mifare Plus 4K SL3
instance->data->type = MfPlusTypePlus;
instance->data->size = MfPlusSize4K;
instance->data->security_level = MfPlusSecurityLevel3;
} else {
// Mifare Plus EV1/EV2
// Revision
switch(instance->data->version.hw_major) {
case 0x11:
instance->data->type = MfPlusTypeEV1;
break;
case 0x22:
instance->data->type = MfPlusTypeEV2;
break;
default:
instance->data->type = MfPlusTypeUnknown;
break;
}
// Storage size
switch(instance->data->version.hw_storage) {
case 0x16:
instance->data->size = MfPlusSize2K;
break;
case 0x18:
instance->data->size = MfPlusSize4K;
break;
default:
instance->data->size = MfPlusSizeUnknown;
break;
}
// Security level
if(iso14443_4a_data->iso14443_3a_data->sak == 0x20) {
// Mifare Plus EV1/2 SL3
instance->data->security_level = MfPlusSecurityLevel3;
} else {
// Mifare Plus EV1/2 SL1
instance->data->security_level = MfPlusSecurityLevel1;
}
}
}
} else {
FURI_LOG_D(TAG, "Read version error: %d", error);
detected = mf_plus_poller_get_type_from_iso4(iso14443_4a_data, instance->data);
}
return detected;
}
MfPlusPoller* mf_plus_poller_alloc(Iso14443_4aPoller* iso14443_4a_poller) {
furi_assert(iso14443_4a_poller);
MfPlusPoller* instance = malloc(sizeof(MfPlusPoller));
furi_assert(instance);
instance->iso14443_4a_poller = iso14443_4a_poller;
@ -259,10 +58,40 @@ static NfcCommand mf_plus_poller_handler_idle(MfPlusPoller* instance) {
}
static NfcCommand mf_plus_poller_handler_read_version(MfPlusPoller* instance) {
bool success = mf_plus_poller_detect_type(instance);
if(success) {
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;
}
@ -271,26 +100,35 @@ static NfcCommand mf_plus_poller_handler_read_version(MfPlusPoller* instance) {
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,
};
@ -307,13 +145,12 @@ static void mf_plus_poller_set_callback(
}
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;
furi_assert(instance);
const Iso14443_4aPollerEvent* iso14443_4a_event = event.event_data;
furi_assert(iso14443_4a_event);
NfcCommand command = NfcCommandContinue;
@ -340,21 +177,27 @@ void mf_plus_poller_free(MfPlusPoller* 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;
furi_assert(instance);
Iso14443_4aPollerEvent* iso14443_4a_event = event.event_data;
furi_assert(iso14443_4a_event);
bool detected = false;
MfPlusError error = MfPlusErrorUnknown;
if(iso14443_4a_event->type == Iso14443_4aPollerEventTypeReady) {
detected = mf_plus_poller_detect_type(instance);
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 detected;
return (error == MfPlusErrorNone);
}
const NfcPollerBase mf_plus_poller = {

View file

@ -19,8 +19,10 @@ MfPlusError mf_plus_process_error(Iso14443_4aError error) {
}
}
MfPlusError
mf_plus_send_chunk(MfPlusPoller* instance, const BitBuffer* tx_buffer, BitBuffer* rx_buffer) {
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);
@ -28,46 +30,30 @@ MfPlusError
furi_assert(tx_buffer);
furi_assert(rx_buffer);
MfPlusError error = MfPlusErrorNone;
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);
do {
Iso14443_4aError iso14443_4a_error = iso14443_4a_poller_send_block(
instance->iso14443_4a_poller, tx_buffer, instance->rx_buffer);
if(error == MfPlusErrorNone) {
bit_buffer_copy(rx_buffer, instance->rx_buffer);
}
if(iso14443_4a_error != Iso14443_4aErrorNone) {
error = mf_plus_process_error(iso14443_4a_error);
break;
}
bit_buffer_reset(instance->tx_buffer);
if(bit_buffer_get_size_bytes(instance->rx_buffer) > sizeof(uint8_t)) {
bit_buffer_copy_right(rx_buffer, instance->rx_buffer, sizeof(uint8_t));
} else {
bit_buffer_reset(rx_buffer);
}
} while(false);
bit_buffer_reset(instance->tx_buffer);
return error;
}
MfPlusError mf_plus_poller_read_version(MfPlusPoller* instance, MfPlusVersion* data) {
furi_assert(instance);
furi_check(instance);
bit_buffer_reset(instance->input_buffer);
bit_buffer_append_byte(instance->input_buffer, MF_PLUS_CMD_GET_VERSION);
MfPlusError error;
do {
error = mf_plus_send_chunk(instance, instance->input_buffer, instance->result_buffer);
if(error != MfPlusErrorNone) break;
if(!mf_plus_version_parse(data, instance->result_buffer)) {
error = MfPlusErrorProtocol;
}
} while(false);
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;
}

View file

@ -18,6 +18,8 @@ typedef enum {
typedef enum {
MfPlusPollerStateIdle,
MfPlusPollerStateReadVersion,
MfPlusPollerStateParseVersion,
MfPlusPollerStateParseIso4,
MfPlusPollerStateReadFailed,
MfPlusPollerStateReadSuccess,