diff --git a/applications/main/nfc/helpers/protocol_support/mf_plus/mf_plus.c b/applications/main/nfc/helpers/protocol_support/mf_plus/mf_plus.c index a020c9bbd..d6abba3b3 100644 --- a/applications/main/nfc/helpers/protocol_support/mf_plus/mf_plus.c +++ b/applications/main/nfc/helpers/protocol_support/mf_plus/mf_plus.c @@ -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 = { diff --git a/applications/main/nfc/plugins/supported_cards/skylanders.c b/applications/main/nfc/plugins/supported_cards/skylanders.c index fca1c3185..6c199f114 100644 --- a/applications/main/nfc/plugins/supported_cards/skylanders.c +++ b/applications/main/nfc/plugins/supported_cards/skylanders.c @@ -5,11 +5,54 @@ #include #include #include +#include #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)); diff --git a/applications/main/nfc/resources/nfc/assets/skylanders.nfc b/applications/main/nfc/resources/nfc/assets/skylanders.nfc new file mode 100644 index 000000000..58e6d5276 --- /dev/null +++ b/applications/main/nfc/resources/nfc/assets/skylanders.nfc @@ -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 diff --git a/applications/main/nfc/scenes/nfc_scene_exit_confirm.c b/applications/main/nfc/scenes/nfc_scene_exit_confirm.c index 7c4a3d19d..d62ee2d8e 100644 --- a/applications/main/nfc/scenes/nfc_scene_exit_confirm.c +++ b/applications/main/nfc/scenes/nfc_scene_exit_confirm.c @@ -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)); diff --git a/lib/nfc/protocols/mf_plus/mf_plus.c b/lib/nfc/protocols/mf_plus/mf_plus.c index 98b7cd82a..32d8d6c59 100644 --- a/lib/nfc/protocols/mf_plus/mf_plus.c +++ b/lib/nfc/protocols/mf_plus/mf_plus.c @@ -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; } \ No newline at end of file diff --git a/lib/nfc/protocols/mf_plus/mf_plus.h b/lib/nfc/protocols/mf_plus/mf_plus.h index 828d1c070..31559ffdc 100644 --- a/lib/nfc/protocols/mf_plus/mf_plus.h +++ b/lib/nfc/protocols/mf_plus/mf_plus.h @@ -2,13 +2,11 @@ #include -#include - #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; diff --git a/lib/nfc/protocols/mf_plus/mf_plus_i.c b/lib/nfc/protocols/mf_plus/mf_plus_i.c index c248d2568..d5fe5be82 100644 --- a/lib/nfc/protocols/mf_plus/mf_plus_i.c +++ b/lib/nfc/protocols/mf_plus/mf_plus_i.c @@ -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; -} \ No newline at end of file + return success; +} diff --git a/lib/nfc/protocols/mf_plus/mf_plus_i.h b/lib/nfc/protocols/mf_plus/mf_plus_i.h index 8ced4bdd0..1b80030f9 100644 --- a/lib/nfc/protocols/mf_plus/mf_plus_i.h +++ b/lib/nfc/protocols/mf_plus/mf_plus_i.h @@ -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); diff --git a/lib/nfc/protocols/mf_plus/mf_plus_poller.c b/lib/nfc/protocols/mf_plus/mf_plus_poller.c index a218229c5..c93ec9e67 100644 --- a/lib/nfc/protocols/mf_plus/mf_plus_poller.c +++ b/lib/nfc/protocols/mf_plus/mf_plus_poller.c @@ -1,4 +1,5 @@ #include "mf_plus_poller_i.h" +#include "mf_plus_i.h" #include @@ -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 = { diff --git a/lib/nfc/protocols/mf_plus/mf_plus_poller_i.c b/lib/nfc/protocols/mf_plus/mf_plus_poller_i.c index 1a4298b67..cab906f1d 100644 --- a/lib/nfc/protocols/mf_plus/mf_plus_poller_i.c +++ b/lib/nfc/protocols/mf_plus/mf_plus_poller_i.c @@ -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; } diff --git a/lib/nfc/protocols/mf_plus/mf_plus_poller_i.h b/lib/nfc/protocols/mf_plus/mf_plus_poller_i.h index 79f46b8d8..c7b547a84 100644 --- a/lib/nfc/protocols/mf_plus/mf_plus_poller_i.h +++ b/lib/nfc/protocols/mf_plus/mf_plus_poller_i.h @@ -18,6 +18,8 @@ typedef enum { typedef enum { MfPlusPollerStateIdle, MfPlusPollerStateReadVersion, + MfPlusPollerStateParseVersion, + MfPlusPollerStateParseIso4, MfPlusPollerStateReadFailed, MfPlusPollerStateReadSuccess,