mirror of
https://github.com/DarkFlippers/unleashed-firmware.git
synced 2025-12-12 04:34:43 +04:00
Merge branch 'dev' into release
This commit is contained in:
@@ -6,9 +6,9 @@
|
|||||||
How to [install firmware](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/documentation/HowToInstall.md)?
|
How to [install firmware](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/documentation/HowToInstall.md)?
|
||||||
|
|
||||||
### Install FW via Web Updater:
|
### Install FW via Web Updater:
|
||||||
[Default](https://lab.flipper.net/?url=https://unleashedflip.com/fw/dev/flipper-z-f7-update-(buildnum).tgz&channel=dev-cfw&version=(buildnum)) > ` `
|
[Default](https://lab.flipper.net/?url=https://unleashedflip.com/fw/dev/flipper-z-f7-update-(buildnum).tgz&target=f7&channel=dev-cfw&version=(buildnum)) > ` `
|
||||||
[Extra apps](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-(buildnum)e.tgz&channel=dev-cfw&version=(buildnum)e) > `e`
|
[Extra apps](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-(buildnum)e.tgz&target=f7&channel=dev-cfw&version=(buildnum)e) > `e`
|
||||||
[No apps](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-(buildnum)c.tgz&channel=dev-cfw&version=(buildnum)c) > `c`
|
[No apps](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-(buildnum)c.tgz&target=f7&channel=dev-cfw&version=(buildnum)c) > `c`
|
||||||
What ` `, `e`, `c` means? -> [versions info](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/CHANGELOG.md#what-e---c-means-what-i-need-to-download-if-i-dont-want-to-use-web-updater)
|
What ` `, `e`, `c` means? -> [versions info](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/CHANGELOG.md#what-e---c-means-what-i-need-to-download-if-i-dont-want-to-use-web-updater)
|
||||||
### Direct tgz download links:
|
### Direct tgz download links:
|
||||||
[Default](https://unleashedflip.com/fw/dev/flipper-z-f7-update-(buildnum).tgz) > ` ` - [Extra apps](https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-(buildnum)e.tgz) > `e` - [No apps](https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-(buildnum)c.tgz) > `c`
|
[Default](https://unleashedflip.com/fw/dev/flipper-z-f7-update-(buildnum).tgz) > ` ` - [Extra apps](https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-(buildnum)e.tgz) > `e` - [No apps](https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-(buildnum)c.tgz) > `c`
|
||||||
|
|||||||
@@ -7,9 +7,9 @@
|
|||||||
How to [install firmware](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/documentation/HowToInstall.md)?
|
How to [install firmware](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/documentation/HowToInstall.md)?
|
||||||
|
|
||||||
**Install FW via Web Updater:**
|
**Install FW via Web Updater:**
|
||||||
[Default](https://lab.flipper.net/?url=https://unleashedflip.com/fw/dev/flipper-z-f7-update-(buildnum).tgz&channel=dev-cfw&version=(buildnum)) > ` `
|
[Default](https://lab.flipper.net/?url=https://unleashedflip.com/fw/dev/flipper-z-f7-update-(buildnum).tgz&target=f7&channel=dev-cfw&version=(buildnum)) > ` `
|
||||||
[Extra apps](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-(buildnum)e.tgz&channel=dev-cfw&version=(buildnum)e) > `e`
|
[Extra apps](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-(buildnum)e.tgz&target=f7&channel=dev-cfw&version=(buildnum)e) > `e`
|
||||||
[No apps](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-(buildnum)c.tgz&channel=dev-cfw&version=(buildnum)c) > `c`
|
[No apps](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-(buildnum)c.tgz&target=f7&channel=dev-cfw&version=(buildnum)c) > `c`
|
||||||
|
|
||||||
What ` `, `e`, `c` means? -> [versions info](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/CHANGELOG.md#what-e---c-means-what-i-need-to-download-if-i-dont-want-to-use-web-updater)
|
What ` `, `e`, `c` means? -> [versions info](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/CHANGELOG.md#what-e---c-means-what-i-need-to-download-if-i-dont-want-to-use-web-updater)
|
||||||
|
|
||||||
|
|||||||
@@ -6,9 +6,9 @@
|
|||||||
How to [install firmware](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/documentation/HowToInstall.md)?
|
How to [install firmware](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/documentation/HowToInstall.md)?
|
||||||
|
|
||||||
### Install FW via Web Updater:
|
### Install FW via Web Updater:
|
||||||
[Default](https://lab.flipper.net/?url=https://unleashedflip.com/fw/(releasever)/flipper-z-f7-update-(releasever).tgz&channel=release-cfw&version=(releasever)) > ` `
|
[Default](https://lab.flipper.net/?url=https://unleashedflip.com/fw/(releasever)/flipper-z-f7-update-(releasever).tgz&target=f7&channel=release-cfw&version=(releasever)) > ` `
|
||||||
[Extra apps](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-(releasever)e.tgz&channel=release-cfw&version=(releasever)e) > `e`
|
[Extra apps](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-(releasever)e.tgz&target=f7&channel=release-cfw&version=(releasever)e) > `e`
|
||||||
[No apps](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-(releasever)c.tgz&channel=release-cfw&version=(releasever)c) > `c`
|
[No apps](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-(releasever)c.tgz&target=f7&channel=release-cfw&version=(releasever)c) > `c`
|
||||||
What ` `, `e`, `c` means? -> [versions info](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/CHANGELOG.md#what-e---c-means-what-i-need-to-download-if-i-dont-want-to-use-web-updater)
|
What ` `, `e`, `c` means? -> [versions info](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/CHANGELOG.md#what-e---c-means-what-i-need-to-download-if-i-dont-want-to-use-web-updater)
|
||||||
### Direct tgz download links:
|
### Direct tgz download links:
|
||||||
[Default](https://unleashedflip.com/fw/(releasever)/flipper-z-f7-update-(releasever).tgz) > ` ` - [Extra apps](https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-(releasever)e.tgz) > `e` - [No apps](https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-(releasever)c.tgz) > `c`
|
[Default](https://unleashedflip.com/fw/(releasever)/flipper-z-f7-update-(releasever).tgz) > ` ` - [Extra apps](https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-(releasever)e.tgz) > `e` - [No apps](https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-(releasever)c.tgz) > `c`
|
||||||
|
|||||||
@@ -7,9 +7,9 @@
|
|||||||
How to [install firmware](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/documentation/HowToInstall.md)?
|
How to [install firmware](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/documentation/HowToInstall.md)?
|
||||||
|
|
||||||
**Install FW via Web Updater:**
|
**Install FW via Web Updater:**
|
||||||
[Default](https://lab.flipper.net/?url=https://unleashedflip.com/fw/(releasever)/flipper-z-f7-update-(releasever).tgz&channel=release-cfw&version=(releasever)) > ` `
|
[Default](https://lab.flipper.net/?url=https://unleashedflip.com/fw/(releasever)/flipper-z-f7-update-(releasever).tgz&target=f7&channel=release-cfw&version=(releasever)) > ` `
|
||||||
[Extra apps](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-(releasever)e.tgz&channel=release-cfw&version=(releasever)e) > `e`
|
[Extra apps](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-(releasever)e.tgz&target=f7&channel=release-cfw&version=(releasever)e) > `e`
|
||||||
[No apps](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-(releasever)c.tgz&channel=release-cfw&version=(releasever)c) > `c`
|
[No apps](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-(releasever)c.tgz&target=f7&channel=release-cfw&version=(releasever)c) > `c`
|
||||||
|
|
||||||
What ` `, `e`, `c` means? -> [versions info](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/CHANGELOG.md#what-e---c-means-what-i-need-to-download-if-i-dont-want-to-use-web-updater)
|
What ` `, `e`, `c` means? -> [versions info](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/CHANGELOG.md#what-e---c-means-what-i-need-to-download-if-i-dont-want-to-use-web-updater)
|
||||||
|
|
||||||
|
|||||||
@@ -83,11 +83,11 @@ steps:
|
|||||||
- ls -laS artifacts-extra-apps/f7-update-${DRONE_TAG}e
|
- ls -laS artifacts-extra-apps/f7-update-${DRONE_TAG}e
|
||||||
- sed -i 's/(version)/'${DRONE_TAG}'/g' CHANGELOG.md
|
- sed -i 's/(version)/'${DRONE_TAG}'/g' CHANGELOG.md
|
||||||
- echo '# Install FW via Web Updater:' >> CHANGELOG.md
|
- echo '# Install FW via Web Updater:' >> CHANGELOG.md
|
||||||
- echo '### [Default](https://lab.flipper.net/?url=https://unleashedflip.com/fw/${DRONE_TAG}/flipper-z-f7-update-'${DRONE_TAG}'.tgz&channel=release-cfw&version='${DRONE_TAG}') > ` `' >> CHANGELOG.md
|
- echo '### [Default](https://lab.flipper.net/?url=https://unleashedflip.com/fw/${DRONE_TAG}/flipper-z-f7-update-'${DRONE_TAG}'.tgz&target=f7&channel=release-cfw&version='${DRONE_TAG}') > ` `' >> CHANGELOG.md
|
||||||
- echo '' >> CHANGELOG.md
|
- echo '' >> CHANGELOG.md
|
||||||
- echo '### [Extra apps](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-'${DRONE_TAG}'e.tgz&channel=release-cfw&version='${DRONE_TAG}'e) > `e`' >> CHANGELOG.md
|
- echo '### [Extra apps](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-'${DRONE_TAG}'e.tgz&target=f7&channel=release-cfw&version='${DRONE_TAG}'e) > `e`' >> CHANGELOG.md
|
||||||
- echo '' >> CHANGELOG.md
|
- echo '' >> CHANGELOG.md
|
||||||
- echo '### [No apps](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-'${DRONE_TAG}'c.tgz&channel=release-cfw&version='${DRONE_TAG}'c) > `c`' >> CHANGELOG.md
|
- echo '### [No apps](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-'${DRONE_TAG}'c.tgz&target=f7&channel=release-cfw&version='${DRONE_TAG}'c) > `c`' >> CHANGELOG.md
|
||||||
environment:
|
environment:
|
||||||
FBT_TOOLS_CUSTOM_LINK:
|
FBT_TOOLS_CUSTOM_LINK:
|
||||||
from_secret: fbt_link
|
from_secret: fbt_link
|
||||||
|
|||||||
144
CHANGELOG.md
144
CHANGELOG.md
@@ -1,93 +1,65 @@
|
|||||||
## Main changes
|
## Main changes
|
||||||
- Current API: 86.0
|
- Current API: 86.0
|
||||||
**WARNING! After install of this version your Desktop (fav apps) and LCD & Notifications settings will be reset to default values, please configure them again after this update!** (this is required due to big updates on that parts and config struct changes)
|
* SubGHz: **Roger (static 28 bit) with add manually support** (by @xMasterX & @mishamyte)
|
||||||
* SubGHz: Add **Feron** protocol (static 32 bit) **full support** (by @xMasterX)
|
* SubGHz: **V2 Phoenix full support** (button switch, add manually, counter decrypt/encrypt) (by @xMasterX & @RocketGod-git, original code by @Skorpionm)
|
||||||
* SubGHz: Add **Revers RB2 / RB2M Protocol** (static 64 bit) **full support** with add manually (by @xMasterX)
|
* SubGHz: **Keeloq: Add support for - Motorline (with add manually support), Rosh, Pecinin, Rossi, Merlin, Steelmate** (by @xMasterX & @RocketGod-git)
|
||||||
* SubGHz: **Fix Hollarm protocol with more verification**
|
* SubGHz: **Nero Radio static parse** and display more data
|
||||||
* SubGHz: **Fix GangQi protocol** (by @DoberBit and @mishamyte (who spent 2 weeks on this))
|
* SubGHz: Reduce less popular freqs in default hopper preset, **make it faster**
|
||||||
* SubGHz: **Came Atomo button hold simulation with full cycle** simulation (to allow proper pairing with receiver)
|
* SubGHz: **Marantec protocol implement CRC verification display and Add manually support** (by @xMasterX & @li0ard, original code by @Skorpionm)
|
||||||
* SubGHz: Add **Prastel (42bit static code)** support (OFW PR 4178 by @pmazzini)
|
* SubGHz: **Keeloq: Comunello - add manually support**
|
||||||
* Desktop: **Add support for Favorite App - Ok Long** (Warning! Old favourites apps list will be reset!) (PR #886 | by @DrEverr)
|
* iButton: **TM01x Dallas write support** (PR #899 | by @Leptopt1los)
|
||||||
* Display: **LCD Color Inversion** (Settings - LCD and Notifications - LCD Inversion) (PR #887 #893 | by @Dmitry422)
|
* SubGHz: Rename and **extend Alarms, Sensors, Cars ignore options** (Alarms: Hollarm, GangQi | Cars: Kia, Starline, ScherKhan | Sensors: Magellan, Honeywell, Honeywell WDB (doorbells), Legrand (doorbells), Feron (RGB lights))
|
||||||
* Display: **Night Shift Feature** (dimming backlight in selected time interval) (PR #885 #896 | by @Dmitry422)
|
* SubGHz: V2 Phoenix show counter value (upd: see above, now decrypted)
|
||||||
* Display: **Сombining RGB Backlight mod** (by @quen0n) and original backlight support **in one firmware** (+ Rainbow/Wave effect (based on @Willy-JL idea)) (PR #877 #881 #890 | by @Dmitry422) - (**To enable RGB Backlight support go into Notifications settings**)
|
* SubGHz: **Add Keeloq IronLogic (aka IL100) smart clone remote copiers support** (thanks to Vitaly for RAWs)
|
||||||
* NFC: Use default UL/UL-C pwd/key as default value for key input (PR #891 | by @mishamyte)
|
* SubGHz: **Fix CAME 24bit decoder**
|
||||||
* OFW: LFRFID - **EM4305 support**
|
* SubGHz: Add 462.750 MHz & 868.46 MHz to default subghz freqs list
|
||||||
* OFW: **Universal IR signal selection**
|
* SubGHz: **Tune Holtek HT12x to decode Holtek only** and not conflict with came 12bit
|
||||||
* OFW: **BadUSB: Mouse control**
|
* SubGHz: Fix Rename scene bug, that was replacing file name with random name when Rename is opened then closed then opened again
|
||||||
* OFW: **Pinning of settings options**
|
* Display: Backlight option "always on" and RGB bug removed (PR #900 | by @Dmitry422)
|
||||||
* OFW: NFC app now can launch MFKey32
|
* NFC: Ultralight C - Attempt of authentication with default key (PR #898 | by @mishamyte)
|
||||||
* OFW: BadUSB arbitrary key combinations
|
* System: Loader - Fix misplaced ApplicationBeforeLoad events (PR #905 | by @WillyJL)
|
||||||
* OFW PR 4136: BadUSB: Full USB/BLE parameter customization, UI improvements, and more (by @Willy-JL)
|
* OFW PR 4210: Infrared: Add text scroll to remote buttons (by @956MB)
|
||||||
* OFW: NFC - Added naming for DESFire cards + fix MF3ICD40 cards unable to be read
|
* NFC:
|
||||||
* Apps: Add **FindMyFlipper to system apps and allow autostart** on system boot [app by @MatthewKuKanich](https://github.com/MatthewKuKanich/FindMyFlipper) and autoloader by @Willy-JL - to use app please check how to add keys in [app repo](https://github.com/MatthewKuKanich/FindMyFlipper)
|
- **NFC Type 4 support + many other improvements** (by @WillyJL)
|
||||||
* README Update: Enhanced Visuals & Navigation (PR #871 #872 | by @m-xim)
|
- New Type 4 Tag (NDEF on NTAG4xx / MIFARE DESFire) protocol, full support
|
||||||
* Docs: Update FAQ.md (PR #865 | by @mi-lrn)
|
- New NTAG4xx (NTAG413 DNA / NTAG424 DNA) protocol, only detection and basic info support
|
||||||
* Input: **Vibro on Button press option** (PR #867 | by @Dmitry422)
|
- NDEF parsing plugin supports Type 4 Tag protocol
|
||||||
* Power: **Option to limit battery charging** (suppress charging on selected charge level) (PR #867 | by @Dmitry422) (idea and example by @oltenxyz)
|
- Show more version info for MIFARE Plus cards
|
||||||
|
- Improve detection/verification of MIFARE DESFire and MIFARE Plus SE
|
||||||
|
- Improve navigation for MIFARE Classic Update from / Write to Initial Card
|
||||||
|
- Refactor Write code for MIFARE Ultralight/Classic in NFC app helpers
|
||||||
|
- Cleanup event handling in NFC app
|
||||||
|
- NFC app uses a bit less RAM because of previous 2 points
|
||||||
|
- Refactor NXP Native Commands to share between protocols (used by MIFARE DESFire, MIFARE Plus, NTAG4xx)
|
||||||
|
- MIFARE DESFire poller API can now switch between native and ISO7816-wrapped commands
|
||||||
|
- Expand ISO14443-4A API with listener (emulation) support for sending responses to reader (except I-block chaining)
|
||||||
|
- Exposed some APIs for apps to use that were meant to be public:
|
||||||
|
- ISO14443-3A listener (emulation)
|
||||||
|
- ISO15693-3 device (data), poller (reading), listener (emulation)
|
||||||
|
- Cleanup/reorder protocol definitions for tidyness
|
||||||
|
- Ventra ULEV1 parser (by @hazardousvoltage)
|
||||||
|
- CSC Service Works parser (by @zinongli)
|
||||||
|
- Philips Sonicare parser (by @Sil333033)
|
||||||
|
- SmartRider parser (by @jaylikesbunda)
|
||||||
* Apps: **Check out more Apps updates and fixes by following** [this link](https://github.com/xMasterX/all-the-plugins/commits/dev)
|
* Apps: **Check out more Apps updates and fixes by following** [this link](https://github.com/xMasterX/all-the-plugins/commits/dev)
|
||||||
## Other changes
|
## Other changes
|
||||||
* SubGHz: Move hardcoded extra modulations to user config - uncomment them in setting_user.example and remove .example from filename
|
* BadUSB: Fix modifier keys with HOLD/RELEASE commands (by @WillyJL)
|
||||||
* SubGHz: Various bugfixes and experimental options (rolling counter overflow) (by @xMasterX)
|
* Docs: Update doorhan programming instructions (by @li0ard)
|
||||||
* Anims: Disable winter anims
|
* FuriHalSerial: Fix RXFNE interrupt hang, aka freezing with UART output when Expansion Modules are enabled (by @WillyJL)
|
||||||
* NFC: mfclassic poller fix early key reuse in dictionary attack state machine (by @noproto)
|
* Expansion: add is_connected api (by @HaxSam & @WillyJL)
|
||||||
* OFW: RC fixes
|
* RFID 125khz: Fix strange bug with LCD backlight going off after doing "Write"
|
||||||
* OFW: Desktop: Fix freeze on boot if PIN set
|
* GUI: Added `submenu_remove_item()` to API, was needed for NFC Type 4 related changes (by @WillyJL)
|
||||||
* OFW PR 4189: USB-UART bridge fix (by @portasynthinca3)
|
* SubGHz: Fix possible frequency analyzer deadlock when holding Ok (by @WillyJL)
|
||||||
* OFW: FBT: Fix for Python 3.13
|
* RFID 125khz: Add DEZ10 representation to EM410X (by @realcatgirly)
|
||||||
* OFW: sdk: bump API to force re-upload for the catalog
|
* OFW PR 4205: fix sample durations when using external CC1101 (by @Aerosnail)
|
||||||
* OFW: SDK: Fix missing RECORD_CLI define
|
* OFW PR 4206: Stop JS PWM on exit (by @portasynthinca3)
|
||||||
* OFW: Fix NULL dereference in CLI completions
|
* OFW PR 4212: Fixed inverted logic condition in subghz chat cli (by @GameLord2011)
|
||||||
* OFW PR 4181: vcp, cli: Handle Tx/Rx events before Connect/Disconnect + extra fixes (by @portasynthinca3)
|
* NFC: Fix clipper date timestamp (PR #903 | by @luu176)
|
||||||
* OFW: BLE: Slightly increase mfg_data size
|
* Desktop: DEBUG - fix desktop anim switch override by favourite apps
|
||||||
* OFW: fbt: Deterministic STARTUP order & additional checks
|
* CLI: Various fixes (by @WillyJL)
|
||||||
* OFW: JS: Update and fix docs, fix Number.toString() with decimals
|
* BadUSB: Fix key combos main keys being case sensitive (by @WillyJL)
|
||||||
* OFW: New JS value destructuring
|
* System: log level none after update
|
||||||
* OFW: Docs: Fix doxygen references from PR 4168
|
* Docs: Some updates on subghz remotes programming
|
||||||
* OFW: BLE advertising improvements
|
|
||||||
* OFW: **New CLI architecture**
|
|
||||||
* OFW: **CLI autocomplete and other sugar**
|
|
||||||
* OFW: CLI commands in fals and threads
|
|
||||||
* OFW: cli: fixed `free_blocks` command
|
|
||||||
* OFW: docs: badusb arbitrary modkey chains
|
|
||||||
* OFW: Separate cli_shell into toolbox
|
|
||||||
* OFW: Move JS modules to new arg parser
|
|
||||||
* OFW: Application chaining
|
|
||||||
* OFW: Fix DWARF dead code elimination and linking
|
|
||||||
* OFW: NFC: Fix crash on ISO15693-3 save when memory is empty or cannot be read
|
|
||||||
* OFW: Reduced ieee754 parser size
|
|
||||||
* OFW: Added Doom animation (by @doomwastaken)
|
|
||||||
* OFW PR 4133: add nfc apdu cli command back (by @leommxj)
|
|
||||||
* OFW: NFC: Support DESFire Transaction MAC file type (by @Willy-JL)
|
|
||||||
* OFW: NFC: Fix NDEF parser for MIFARE Classic (by @Willy-JL)
|
|
||||||
* OFW: GUI: Fix widget text scroll with 256+ lines (by @Willy-JL)
|
|
||||||
* OFW: Infrared: Fix universals sending (by @Willy-JL)
|
|
||||||
* OFW: HID Ble: increased stack and improvements (by @doomwastaken)
|
|
||||||
* OFW: Stricter constness for const data (by @hedger)
|
|
||||||
* OFW PR 4017: Alarm improvements: Snooze, timeouts, and dismissing from the locked state (by @Astrrra)
|
|
||||||
* OFW: fix: flipper detected before it was rebooted
|
|
||||||
* OFW: NFC: FeliCa Protocol Expose Read Block API and Allow Specifying Service
|
|
||||||
* OFW: LFRFID: Fix Detection Conflict Between Securakey and Noralsy Format (by @zinongli)
|
|
||||||
* OFW: Stdio API improvements
|
|
||||||
* OFW: GUI: Widget view extra options for JS
|
|
||||||
* OFW: Update heap implementation
|
|
||||||
* OFW: Updated Button Panel
|
|
||||||
* OFW: UART framing mode selection
|
|
||||||
* OFW: gpio: clear irq status before calling user handler
|
|
||||||
* OFW: Fix 5V on GPIO
|
|
||||||
* OFW: Fixed repeat in subghz tx_from_file command
|
|
||||||
* OFW: LFRFID: Noralsy Format/Brand
|
|
||||||
* OFW: Faster di card reading
|
|
||||||
* OFW: vscode: disabled auto-update for clangd since correct version is in the toolchain
|
|
||||||
* OFW: Furi, USB, BLE, Debug: various bug fixes and improvements
|
|
||||||
* OFW: EventLoop unsubscribe fix
|
|
||||||
* OFW: nfc: Enable MFUL sync poller to be provided with passwords
|
|
||||||
* OFW: ST25TB poller mode check
|
|
||||||
* OFW: JS features & bugfixes (SDK 0.2) **Existing Widget JS module was removed and replaced with new ofw gui/widget module, old apps using widget may be incompatible now!**
|
|
||||||
* OFW: Infrared: increase max carrier limit
|
|
||||||
* OFW: Ensure that `furi_record_create` is passed a non-NULL data pointer
|
|
||||||
* OFW: Update mbedtls & expose AES
|
|
||||||
* OFW: Add the Showtime animation
|
|
||||||
<br><br>
|
<br><br>
|
||||||
#### Known NFC post-refactor regressions list:
|
#### Known NFC post-refactor regressions list:
|
||||||
- Mifare Mini clones reading is broken (original mini working fine) (OFW)
|
- Mifare Mini clones reading is broken (original mini working fine) (OFW)
|
||||||
|
|||||||
15
ReadMe.md
15
ReadMe.md
@@ -80,6 +80,7 @@ Before getting started:
|
|||||||
> - FAAC SLH, BFT Mitto / Somfy Telis / Nice Flor S / CAME Atomo, etc. manual creation with programming new remote into receiver (use button 0xF for BFT Mitto, 0x8 (Prog) on Somfy Telis, (right arrow button for other protocols))
|
> - FAAC SLH, BFT Mitto / Somfy Telis / Nice Flor S / CAME Atomo, etc. manual creation with programming new remote into receiver (use button 0xF for BFT Mitto, 0x8 (Prog) on Somfy Telis, (right arrow button for other protocols))
|
||||||
> - Debug mode counter increase settings (+1 → +5, +10, default: +1)
|
> - Debug mode counter increase settings (+1 → +5, +10, default: +1)
|
||||||
> - Debug PIN output settings for protocol development
|
> - Debug PIN output settings for protocol development
|
||||||
|
> - Ignore options - Alarms: Hollarm, GangQi | Cars: Kia, Starline, ScherKhan | Sensors: Magellan, Honeywell, Honeywell WDB (doorbells), Legrand (doorbells), Feron (RGB lights)
|
||||||
> </details>
|
> </details>
|
||||||
|
|
||||||
> <details>
|
> <details>
|
||||||
@@ -126,7 +127,7 @@ Before getting started:
|
|||||||
> - Battery percentage display with different styles `Settings -> Desktop -> Battery View`
|
> - Battery percentage display with different styles `Settings -> Desktop -> Battery View`
|
||||||
> - More games in Dummy Mode → click or hold any of arrow buttons
|
> - More games in Dummy Mode → click or hold any of arrow buttons
|
||||||
> - Lock device with pin (or regular lock if pin not set) by holding UP button on main screen [(by an4tur0r)](https://github.com/DarkFlippers/unleashed-firmware/pull/107)
|
> - Lock device with pin (or regular lock if pin not set) by holding UP button on main screen [(by an4tur0r)](https://github.com/DarkFlippers/unleashed-firmware/pull/107)
|
||||||
> - **BadKB** plugin [(by Willy-JL, ClaraCrazy, XFW contributors)](https://github.com/Flipper-XFW/Xtreme-Firmware/tree/dev/applications/main/bad_kb) - (See in Applications → Tools) - (aka BadUSB via Bluetooth)
|
> - **BadKB** (BadUSB) [(by Willy-JL, ClaraCrazy, XFW contributors)](https://github.com/Flipper-XFW/Xtreme-Firmware/tree/dev/applications/main/bad_kb) - (Integrated into BadUSB app now!) - (aka BadUSB via Bluetooth)
|
||||||
> - BadUSB → Keyboard layouts [(by rien > dummy-decoy)](https://github.com/dummy-decoy/flipperzero-firmware/tree/dummy_decoy/bad_usb_keyboard_layout)
|
> - BadUSB → Keyboard layouts [(by rien > dummy-decoy)](https://github.com/dummy-decoy/flipperzero-firmware/tree/dummy_decoy/bad_usb_keyboard_layout)
|
||||||
> - Custom community plugins and games added + all known working apps can be downloaded in extra pack in every release
|
> - Custom community plugins and games added + all known working apps can be downloaded in extra pack in every release
|
||||||
> - Other small fixes and changes throughout
|
> - Other small fixes and changes throughout
|
||||||
@@ -155,9 +156,10 @@ Thanks to Official team (to their SubGHz Developer, Skorp) for implementing supp
|
|||||||
> | Came_Space | FAAC_RC,XT | Kingates_Stylo4k | Pantera | Tomahawk_TZ-9030 |
|
> | Came_Space | FAAC_RC,XT | Kingates_Stylo4k | Pantera | Tomahawk_TZ-9030 |
|
||||||
> | Cenmax | FAAC_SLH | KGB/Subaru | Pantera_CLK | Tomahawk_Z,X_3-5 |
|
> | Cenmax | FAAC_SLH | KGB/Subaru | Pantera_CLK | Tomahawk_Z,X_3-5 |
|
||||||
> | Cenmax_St-5 | Faraon | Leopard | Pantera_XS/Jaguar | ZX-730-750-1055 |
|
> | Cenmax_St-5 | Faraon | Leopard | Pantera_XS/Jaguar | ZX-730-750-1055 |
|
||||||
> | Cenmax_St-7 | Genius_Bravo | Magic_1 | Partisan_RX | |
|
> | Cenmax_St-7 | Genius_Bravo | Magic_1 | Partisan_RX | IL-100(Smart) |
|
||||||
> | Centurion | Gibidi | Magic_2 | Reff | |
|
> | Centurion | Gibidi | Magic_2 | Reff | Merlin |
|
||||||
> | Monarch | Jolly Motors | Magic_3 | Sheriff | |
|
> | Monarch | Jolly Motors | Magic_3 | Sheriff | Steelmate |
|
||||||
|
> | Motorline | Rosh | Pecinin | Rossi | |
|
||||||
> </details>
|
> </details>
|
||||||
<br/>
|
<br/>
|
||||||
|
|
||||||
@@ -165,6 +167,9 @@ Thanks to Official team (to their SubGHz Developer, Skorp) for implementing supp
|
|||||||
<summary><code><strong>Decoders/Encoders or emulation (+ programming mode) support made by @xMasterX</strong></code></summary>
|
<summary><code><strong>Decoders/Encoders or emulation (+ programming mode) support made by @xMasterX</strong></code></summary>
|
||||||
<br/>
|
<br/>
|
||||||
|
|
||||||
|
- Roger (static 28 bit) with add manually support (by @xMasterX & @mishamyte)
|
||||||
|
- V2 Phoenix (Phox) (dynamic 52 bit) (by @xMasterX & @RocketGod-git)
|
||||||
|
- Marantec (static 49 bit) (add manually support and CRC verify) (by @xMasterX & @li0ard)
|
||||||
- Feron (static 32 bit)
|
- Feron (static 32 bit)
|
||||||
- ReversRB2 / RB2M (static 64 bit) with add manually support
|
- ReversRB2 / RB2M (static 64 bit) with add manually support
|
||||||
- Marantec24 (static 24 bit) with add manually support
|
- Marantec24 (static 24 bit) with add manually support
|
||||||
@@ -173,7 +178,7 @@ Thanks to Official team (to their SubGHz Developer, Skorp) for implementing supp
|
|||||||
- Hay21 (dynamic 21 bit) with button parsing
|
- Hay21 (dynamic 21 bit) with button parsing
|
||||||
- Nero Radio 57bit (+ 56bit support)
|
- Nero Radio 57bit (+ 56bit support)
|
||||||
- CAME 12bit/24bit encoder fixes (Fixes are now merged in OFW)
|
- CAME 12bit/24bit encoder fixes (Fixes are now merged in OFW)
|
||||||
- Keeloq: Dea Mio, Genius Bravo, GSN, HCS101, AN-Motors, JCM Tech, MHouse, Nice Smilo, DTM Neo, FAAC RC,XT, Mutancode, Normstahl, Beninca + Allmatic, Stilmatic, CAME Space, Aprimatic (model TR and similar), Centurion Nova (thanks Carlos !), Hormann EcoStar, Novoferm, Sommer, Monarch (thanks @ashphx !), Jolly Motors (thanks @pkooiman !)
|
- Keeloq: Dea Mio, Genius Bravo, GSN, HCS101, AN-Motors, JCM Tech, MHouse, Nice Smilo, DTM Neo, FAAC RC,XT, Mutancode, Normstahl, Beninca + Allmatic, Stilmatic, CAME Space, Aprimatic (model TR and similar), Centurion Nova (thanks Carlos !), Hormann EcoStar, Novoferm, Sommer, Monarch (thanks @ashphx !), Jolly Motors (thanks @pkooiman !), IL-100(Smart) (thx Vitaly for RAWs), Motorline (with add manually support), Rosh, Pecinin, Rossi, Merlin, Steelmate (thanks @RocketGod-git)
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
|
|||||||
@@ -612,11 +612,13 @@ void subghz_device_cc1101_ext_start_async_rx(
|
|||||||
furi_hal_bus_enable(FuriHalBusTIM17);
|
furi_hal_bus_enable(FuriHalBusTIM17);
|
||||||
|
|
||||||
// Configure TIM
|
// Configure TIM
|
||||||
|
LL_TIM_InitTypeDef TIM_InitStruct = {0};
|
||||||
//Set the timer resolution to 2 us
|
//Set the timer resolution to 2 us
|
||||||
LL_TIM_SetPrescaler(TIM17, (64 << 1) - 1);
|
TIM_InitStruct.Prescaler = (64 << 1) - 1;
|
||||||
LL_TIM_SetCounterMode(TIM17, LL_TIM_COUNTERMODE_UP);
|
TIM_InitStruct.CounterMode = LL_TIM_COUNTERMODE_UP;
|
||||||
LL_TIM_SetAutoReload(TIM17, 0xFFFF);
|
TIM_InitStruct.Autoreload = 0xFFFF;
|
||||||
LL_TIM_SetClockDivision(TIM17, LL_TIM_CLOCKDIVISION_DIV1);
|
TIM_InitStruct.ClockDivision = LL_TIM_CLOCKDIVISION_DIV1;
|
||||||
|
LL_TIM_Init(TIM17, &TIM_InitStruct);
|
||||||
|
|
||||||
// Timer: advanced
|
// Timer: advanced
|
||||||
LL_TIM_SetClockSource(TIM17, LL_TIM_CLOCKSOURCE_INTERNAL);
|
LL_TIM_SetClockSource(TIM17, LL_TIM_CLOCKSOURCE_INTERNAL);
|
||||||
|
|||||||
@@ -50,13 +50,20 @@ bool ducky_is_line_end(const char chr) {
|
|||||||
return (chr == ' ') || (chr == '\0') || (chr == '\r') || (chr == '\n');
|
return (chr == ' ') || (chr == '\0') || (chr == '\r') || (chr == '\n');
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16_t ducky_get_keycode(BadUsbScript* bad_usb, const char* param, bool accept_chars) {
|
uint16_t ducky_get_keycode(BadUsbScript* bad_usb, const char* param, bool accept_modifiers) {
|
||||||
uint16_t keycode = ducky_get_keycode_by_name(param);
|
uint16_t keycode = ducky_get_keycode_by_name(param);
|
||||||
if(keycode != HID_KEYBOARD_NONE) {
|
if(keycode != HID_KEYBOARD_NONE) {
|
||||||
return keycode;
|
return keycode;
|
||||||
}
|
}
|
||||||
|
|
||||||
if((accept_chars) && (strlen(param) > 0)) {
|
if(accept_modifiers) {
|
||||||
|
uint16_t keycode = ducky_get_modifier_keycode_by_name(param);
|
||||||
|
if(keycode != HID_KEYBOARD_NONE) {
|
||||||
|
return keycode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(strlen(param) > 0) {
|
||||||
return BADUSB_ASCII_TO_KEY(bad_usb, param[0]) & 0xFF;
|
return BADUSB_ASCII_TO_KEY(bad_usb, param[0]) & 0xFF;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
@@ -213,9 +220,7 @@ static int32_t ducky_parse_line(BadUsbScript* bad_usb, FuriString* line) {
|
|||||||
|
|
||||||
// Main key
|
// Main key
|
||||||
char next_char = *line_cstr;
|
char next_char = *line_cstr;
|
||||||
uint16_t main_key = ducky_get_keycode_by_name(line_cstr);
|
key = modifiers | ducky_get_keycode(bad_usb, line_cstr, false);
|
||||||
if(!main_key && next_char) main_key = BADUSB_ASCII_TO_KEY(bad_usb, next_char);
|
|
||||||
key = modifiers | main_key;
|
|
||||||
|
|
||||||
if(key == 0 && next_char) ducky_error(bad_usb, "No keycode defined for %s", line_cstr);
|
if(key == 0 && next_char) ducky_error(bad_usb, "No keycode defined for %s", line_cstr);
|
||||||
|
|
||||||
|
|||||||
@@ -92,7 +92,7 @@ static int32_t ducky_fnc_sysrq(BadUsbScript* bad_usb, const char* line, int32_t
|
|||||||
UNUSED(param);
|
UNUSED(param);
|
||||||
|
|
||||||
line = &line[ducky_get_command_len(line) + 1];
|
line = &line[ducky_get_command_len(line) + 1];
|
||||||
uint16_t key = ducky_get_keycode(bad_usb, line, true);
|
uint16_t key = ducky_get_keycode(bad_usb, line, false);
|
||||||
bad_usb->hid->kb_press(bad_usb->hid_inst, KEY_MOD_LEFT_ALT | HID_KEYBOARD_PRINT_SCREEN);
|
bad_usb->hid->kb_press(bad_usb->hid_inst, KEY_MOD_LEFT_ALT | HID_KEYBOARD_PRINT_SCREEN);
|
||||||
bad_usb->hid->kb_press(bad_usb->hid_inst, key);
|
bad_usb->hid->kb_press(bad_usb->hid_inst, key);
|
||||||
bad_usb->hid->release_all(bad_usb->hid_inst);
|
bad_usb->hid->release_all(bad_usb->hid_inst);
|
||||||
@@ -196,7 +196,7 @@ static int32_t ducky_fnc_globe(BadUsbScript* bad_usb, const char* line, int32_t
|
|||||||
UNUSED(param);
|
UNUSED(param);
|
||||||
|
|
||||||
line = &line[ducky_get_command_len(line) + 1];
|
line = &line[ducky_get_command_len(line) + 1];
|
||||||
uint16_t key = ducky_get_keycode(bad_usb, line, true);
|
uint16_t key = ducky_get_keycode(bad_usb, line, false);
|
||||||
if(key == HID_KEYBOARD_NONE) {
|
if(key == HID_KEYBOARD_NONE) {
|
||||||
return ducky_error(bad_usb, "No keycode defined for %s", line);
|
return ducky_error(bad_usb, "No keycode defined for %s", line);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ struct BadUsbScript {
|
|||||||
size_t string_print_pos;
|
size_t string_print_pos;
|
||||||
};
|
};
|
||||||
|
|
||||||
uint16_t ducky_get_keycode(BadUsbScript* bad_usb, const char* param, bool accept_chars);
|
uint16_t ducky_get_keycode(BadUsbScript* bad_usb, const char* param, bool accept_modifiers);
|
||||||
|
|
||||||
uint32_t ducky_get_command_len(const char* line);
|
uint32_t ducky_get_command_len(const char* line);
|
||||||
|
|
||||||
@@ -58,6 +58,8 @@ bool ducky_is_line_end(const char chr);
|
|||||||
|
|
||||||
uint16_t ducky_get_next_modifier_keycode_by_name(const char** param);
|
uint16_t ducky_get_next_modifier_keycode_by_name(const char** param);
|
||||||
|
|
||||||
|
uint16_t ducky_get_modifier_keycode_by_name(const char* param);
|
||||||
|
|
||||||
uint16_t ducky_get_keycode_by_name(const char* param);
|
uint16_t ducky_get_keycode_by_name(const char* param);
|
||||||
|
|
||||||
uint16_t ducky_get_media_keycode_by_name(const char* param);
|
uint16_t ducky_get_media_keycode_by_name(const char* param);
|
||||||
|
|||||||
@@ -131,6 +131,18 @@ uint16_t ducky_get_next_modifier_keycode_by_name(const char** param) {
|
|||||||
return HID_KEYBOARD_NONE;
|
return HID_KEYBOARD_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint16_t ducky_get_modifier_keycode_by_name(const char* param) {
|
||||||
|
for(size_t i = 0; i < COUNT_OF(ducky_modifier_keys); i++) {
|
||||||
|
size_t key_cmd_len = strlen(ducky_modifier_keys[i].name);
|
||||||
|
if((strncmp(param, ducky_modifier_keys[i].name, key_cmd_len) == 0) &&
|
||||||
|
(ducky_is_line_end(param[key_cmd_len]))) {
|
||||||
|
return ducky_modifier_keys[i].keycode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return HID_KEYBOARD_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
uint16_t ducky_get_keycode_by_name(const char* param) {
|
uint16_t ducky_get_keycode_by_name(const char* param) {
|
||||||
for(size_t i = 0; i < COUNT_OF(ducky_keys); i++) {
|
for(size_t i = 0; i < COUNT_OF(ducky_keys); i++) {
|
||||||
size_t key_cmd_len = strlen(ducky_keys[i].name);
|
size_t key_cmd_len = strlen(ducky_keys[i].name);
|
||||||
|
|||||||
@@ -41,8 +41,7 @@ void lfrfid_scene_read_success_on_enter(void* context) {
|
|||||||
furi_string_cat_printf(display_text, "\n%s", furi_string_get_cstr(rendered_data));
|
furi_string_cat_printf(display_text, "\n%s", furi_string_get_cstr(rendered_data));
|
||||||
furi_string_free(rendered_data);
|
furi_string_free(rendered_data);
|
||||||
|
|
||||||
widget_add_text_box_element(
|
widget_add_text_scroll_element(widget, 0, 16, 128, 35, furi_string_get_cstr(display_text));
|
||||||
widget, 0, 16, 128, 52, AlignLeft, AlignTop, furi_string_get_cstr(display_text), true);
|
|
||||||
widget_add_button_element(widget, GuiButtonTypeLeft, "Retry", lfrfid_widget_callback, app);
|
widget_add_button_element(widget, GuiButtonTypeLeft, "Retry", lfrfid_widget_callback, app);
|
||||||
widget_add_button_element(widget, GuiButtonTypeRight, "More", lfrfid_widget_callback, app);
|
widget_add_button_element(widget, GuiButtonTypeRight, "More", lfrfid_widget_callback, app);
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
#include "gallagher/gallagher_util.h"
|
#include "gallagher/gallagher_util.h"
|
||||||
#include "mosgortrans/mosgortrans_util.h"
|
#include "mosgortrans/mosgortrans_util.h"
|
||||||
|
#include "../nfc_app_i.h"
|
||||||
|
#include "../helpers/protocol_support/nfc_protocol_support_gui_common.h"
|
||||||
|
#include "../helpers/protocol_support/nfc_protocol_support_unlock_helper.h"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* A list of app's private functions and objects to expose for plugins.
|
* A list of app's private functions and objects to expose for plugins.
|
||||||
@@ -22,4 +25,20 @@ static constexpr auto nfc_app_api_table = sort(create_array_t<sym_entry>(
|
|||||||
(FuriString * str,
|
(FuriString * str,
|
||||||
const char* name,
|
const char* name,
|
||||||
uint8_t prefix_separator_cnt,
|
uint8_t prefix_separator_cnt,
|
||||||
uint8_t suffix_separator_cnt))));
|
uint8_t suffix_separator_cnt)),
|
||||||
|
API_METHOD(
|
||||||
|
nfc_append_filename_string_when_present,
|
||||||
|
void,
|
||||||
|
(NfcApp * instance, FuriString* string)),
|
||||||
|
API_METHOD(nfc_protocol_support_common_submenu_callback, void, (void* context, uint32_t index)),
|
||||||
|
API_METHOD(
|
||||||
|
nfc_protocol_support_common_widget_callback,
|
||||||
|
void,
|
||||||
|
(GuiButtonType result, InputType type, void* context)),
|
||||||
|
API_METHOD(nfc_protocol_support_common_on_enter_empty, void, (NfcApp * instance)),
|
||||||
|
API_METHOD(
|
||||||
|
nfc_protocol_support_common_on_event_empty,
|
||||||
|
bool,
|
||||||
|
(NfcApp * instance, SceneManagerEvent event)),
|
||||||
|
API_METHOD(nfc_unlock_helper_setup_from_state, void, (NfcApp * instance)),
|
||||||
|
API_METHOD(nfc_unlock_helper_card_detected_handler, void, (NfcApp * instance))));
|
||||||
|
|||||||
@@ -18,6 +18,205 @@ App(
|
|||||||
fap_category="NFC",
|
fap_category="NFC",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Protocol support plugins
|
||||||
|
|
||||||
|
App(
|
||||||
|
appid="nfc_iso14443_3a",
|
||||||
|
targets=["f7"],
|
||||||
|
apptype=FlipperAppType.PLUGIN,
|
||||||
|
entry_point="nfc_iso14443_3a_ep",
|
||||||
|
requires=["nfc"],
|
||||||
|
sources=[
|
||||||
|
"helpers/protocol_support/iso14443_3a/*.c",
|
||||||
|
],
|
||||||
|
fal_embedded=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
App(
|
||||||
|
appid="nfc_iso14443_3b",
|
||||||
|
targets=["f7"],
|
||||||
|
apptype=FlipperAppType.PLUGIN,
|
||||||
|
entry_point="nfc_iso14443_3b_ep",
|
||||||
|
requires=["nfc"],
|
||||||
|
sources=[
|
||||||
|
"helpers/protocol_support/iso14443_3b/*.c",
|
||||||
|
],
|
||||||
|
fal_embedded=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
App(
|
||||||
|
appid="nfc_iso14443_4a",
|
||||||
|
targets=["f7"],
|
||||||
|
apptype=FlipperAppType.PLUGIN,
|
||||||
|
entry_point="nfc_iso14443_4a_ep",
|
||||||
|
requires=["nfc"],
|
||||||
|
sources=[
|
||||||
|
"helpers/protocol_support/iso14443_4a/*.c",
|
||||||
|
"helpers/protocol_support/iso14443_3a/*.c",
|
||||||
|
],
|
||||||
|
fal_embedded=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
App(
|
||||||
|
appid="nfc_iso14443_4b",
|
||||||
|
targets=["f7"],
|
||||||
|
apptype=FlipperAppType.PLUGIN,
|
||||||
|
entry_point="nfc_iso14443_4b_ep",
|
||||||
|
requires=["nfc"],
|
||||||
|
sources=[
|
||||||
|
"helpers/protocol_support/iso14443_4b/*.c",
|
||||||
|
"helpers/protocol_support/iso14443_3b/*.c",
|
||||||
|
],
|
||||||
|
fal_embedded=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
App(
|
||||||
|
appid="nfc_iso15693_3",
|
||||||
|
targets=["f7"],
|
||||||
|
apptype=FlipperAppType.PLUGIN,
|
||||||
|
entry_point="nfc_iso15693_3_ep",
|
||||||
|
requires=["nfc"],
|
||||||
|
sources=[
|
||||||
|
"helpers/protocol_support/iso15693_3/*.c",
|
||||||
|
],
|
||||||
|
fal_embedded=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
App(
|
||||||
|
appid="nfc_felica",
|
||||||
|
targets=["f7"],
|
||||||
|
apptype=FlipperAppType.PLUGIN,
|
||||||
|
entry_point="nfc_felica_ep",
|
||||||
|
requires=["nfc"],
|
||||||
|
sources=[
|
||||||
|
"helpers/protocol_support/felica/*.c",
|
||||||
|
"helpers/felica_*.c",
|
||||||
|
],
|
||||||
|
fal_embedded=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
App(
|
||||||
|
appid="nfc_mf_ultralight",
|
||||||
|
targets=["f7"],
|
||||||
|
apptype=FlipperAppType.PLUGIN,
|
||||||
|
entry_point="nfc_mf_ultralight_ep",
|
||||||
|
requires=["nfc"],
|
||||||
|
sources=[
|
||||||
|
"helpers/protocol_support/mf_ultralight/*.c",
|
||||||
|
"helpers/protocol_support/iso14443_3a/*.c",
|
||||||
|
"helpers/mf_ultralight_*.c",
|
||||||
|
],
|
||||||
|
fap_libs=["mbedtls"],
|
||||||
|
fal_embedded=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
App(
|
||||||
|
appid="nfc_mf_classic",
|
||||||
|
targets=["f7"],
|
||||||
|
apptype=FlipperAppType.PLUGIN,
|
||||||
|
entry_point="nfc_mf_classic_ep",
|
||||||
|
requires=["nfc"],
|
||||||
|
sources=[
|
||||||
|
"helpers/protocol_support/mf_classic/*.c",
|
||||||
|
"helpers/protocol_support/iso14443_3a/*.c",
|
||||||
|
"helpers/mf_classic_*.c",
|
||||||
|
],
|
||||||
|
fal_embedded=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
App(
|
||||||
|
appid="nfc_mf_plus",
|
||||||
|
targets=["f7"],
|
||||||
|
apptype=FlipperAppType.PLUGIN,
|
||||||
|
entry_point="nfc_mf_plus_ep",
|
||||||
|
requires=["nfc"],
|
||||||
|
sources=[
|
||||||
|
"helpers/protocol_support/mf_plus/*.c",
|
||||||
|
"helpers/protocol_support/iso14443_4a/*.c",
|
||||||
|
"helpers/protocol_support/iso14443_3a/*.c",
|
||||||
|
],
|
||||||
|
fal_embedded=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
App(
|
||||||
|
appid="nfc_mf_desfire",
|
||||||
|
targets=["f7"],
|
||||||
|
apptype=FlipperAppType.PLUGIN,
|
||||||
|
entry_point="nfc_mf_desfire_ep",
|
||||||
|
requires=["nfc"],
|
||||||
|
sources=[
|
||||||
|
"helpers/protocol_support/mf_desfire/*.c",
|
||||||
|
"helpers/protocol_support/iso14443_4a/*.c",
|
||||||
|
"helpers/protocol_support/iso14443_3a/*.c",
|
||||||
|
],
|
||||||
|
fal_embedded=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
App(
|
||||||
|
appid="nfc_slix",
|
||||||
|
targets=["f7"],
|
||||||
|
apptype=FlipperAppType.PLUGIN,
|
||||||
|
entry_point="nfc_slix_ep",
|
||||||
|
requires=["nfc"],
|
||||||
|
sources=[
|
||||||
|
"helpers/protocol_support/slix/*.c",
|
||||||
|
"helpers/protocol_support/iso15693_3/*.c",
|
||||||
|
],
|
||||||
|
fal_embedded=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
App(
|
||||||
|
appid="nfc_st25tb",
|
||||||
|
targets=["f7"],
|
||||||
|
apptype=FlipperAppType.PLUGIN,
|
||||||
|
entry_point="nfc_st25tb_ep",
|
||||||
|
requires=["nfc"],
|
||||||
|
sources=[
|
||||||
|
"helpers/protocol_support/st25tb/*.c",
|
||||||
|
],
|
||||||
|
fal_embedded=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
App(
|
||||||
|
appid="nfc_ntag4xx",
|
||||||
|
targets=["f7"],
|
||||||
|
apptype=FlipperAppType.PLUGIN,
|
||||||
|
entry_point="nfc_ntag4xx_ep",
|
||||||
|
requires=["nfc"],
|
||||||
|
sources=[
|
||||||
|
"helpers/protocol_support/ntag4xx/*.c",
|
||||||
|
"helpers/protocol_support/iso14443_4a/*.c",
|
||||||
|
"helpers/protocol_support/iso14443_3a/*.c",
|
||||||
|
],
|
||||||
|
fal_embedded=True,
|
||||||
|
)
|
||||||
|
App(
|
||||||
|
appid="nfc_type_4_tag",
|
||||||
|
targets=["f7"],
|
||||||
|
apptype=FlipperAppType.PLUGIN,
|
||||||
|
entry_point="nfc_type_4_tag_ep",
|
||||||
|
requires=["nfc"],
|
||||||
|
sources=[
|
||||||
|
"helpers/protocol_support/type_4_tag/*.c",
|
||||||
|
"helpers/protocol_support/iso14443_4a/*.c",
|
||||||
|
"helpers/protocol_support/iso14443_3a/*.c",
|
||||||
|
],
|
||||||
|
fal_embedded=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
App(
|
||||||
|
appid="nfc_emv",
|
||||||
|
targets=["f7"],
|
||||||
|
apptype=FlipperAppType.PLUGIN,
|
||||||
|
entry_point="nfc_emv_ep",
|
||||||
|
requires=["nfc"],
|
||||||
|
sources=[
|
||||||
|
"helpers/protocol_support/emv/*.c",
|
||||||
|
],
|
||||||
|
fal_embedded=True,
|
||||||
|
)
|
||||||
|
|
||||||
# Parser plugins
|
# Parser plugins
|
||||||
|
|
||||||
App(
|
App(
|
||||||
@@ -29,6 +228,15 @@ App(
|
|||||||
sources=["plugins/supported_cards/all_in_one.c"],
|
sources=["plugins/supported_cards/all_in_one.c"],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
App(
|
||||||
|
appid="smartrider_parser",
|
||||||
|
apptype=FlipperAppType.PLUGIN,
|
||||||
|
entry_point="smartrider_plugin_ep",
|
||||||
|
targets=["f7"],
|
||||||
|
requires=["nfc"],
|
||||||
|
sources=["plugins/supported_cards/smartrider.c"],
|
||||||
|
)
|
||||||
|
|
||||||
App(
|
App(
|
||||||
appid="microel_parser",
|
appid="microel_parser",
|
||||||
apptype=FlipperAppType.PLUGIN,
|
apptype=FlipperAppType.PLUGIN,
|
||||||
@@ -254,6 +462,7 @@ App(
|
|||||||
requires=["nfc"],
|
requires=["nfc"],
|
||||||
sources=["plugins/supported_cards/ndef.c"],
|
sources=["plugins/supported_cards/ndef.c"],
|
||||||
)
|
)
|
||||||
|
|
||||||
App(
|
App(
|
||||||
appid="ndef_mfc_parser",
|
appid="ndef_mfc_parser",
|
||||||
apptype=FlipperAppType.PLUGIN,
|
apptype=FlipperAppType.PLUGIN,
|
||||||
@@ -274,6 +483,16 @@ App(
|
|||||||
sources=["plugins/supported_cards/ndef.c"],
|
sources=["plugins/supported_cards/ndef.c"],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
App(
|
||||||
|
appid="ndef_t4t_parser",
|
||||||
|
apptype=FlipperAppType.PLUGIN,
|
||||||
|
cdefines=[("NDEF_PROTO", "NDEF_PROTO_T4T")],
|
||||||
|
entry_point="ndef_plugin_ep",
|
||||||
|
targets=["f7"],
|
||||||
|
requires=["nfc"],
|
||||||
|
sources=["plugins/supported_cards/ndef.c"],
|
||||||
|
)
|
||||||
|
|
||||||
App(
|
App(
|
||||||
appid="itso_parser",
|
appid="itso_parser",
|
||||||
apptype=FlipperAppType.PLUGIN,
|
apptype=FlipperAppType.PLUGIN,
|
||||||
@@ -320,6 +539,33 @@ App(
|
|||||||
sources=["plugins/supported_cards/disney_infinity.c"],
|
sources=["plugins/supported_cards/disney_infinity.c"],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
App(
|
||||||
|
appid="sonicare_parser",
|
||||||
|
apptype=FlipperAppType.PLUGIN,
|
||||||
|
entry_point="sonicare_plugin_ep",
|
||||||
|
targets=["f7"],
|
||||||
|
requires=["nfc"],
|
||||||
|
sources=["plugins/supported_cards/sonicare.c"],
|
||||||
|
)
|
||||||
|
|
||||||
|
App(
|
||||||
|
appid="csc_parser",
|
||||||
|
apptype=FlipperAppType.PLUGIN,
|
||||||
|
entry_point="csc_plugin_ep",
|
||||||
|
targets=["f7"],
|
||||||
|
requires=["nfc"],
|
||||||
|
sources=["plugins/supported_cards/csc.c"],
|
||||||
|
)
|
||||||
|
|
||||||
|
App(
|
||||||
|
appid="ventra_parser",
|
||||||
|
apptype=FlipperAppType.PLUGIN,
|
||||||
|
entry_point="ventra_plugin_ep",
|
||||||
|
targets=["f7"],
|
||||||
|
requires=["nfc"],
|
||||||
|
sources=["plugins/supported_cards/ventra.c"],
|
||||||
|
)
|
||||||
|
|
||||||
App(
|
App(
|
||||||
appid="cli_nfc",
|
appid="cli_nfc",
|
||||||
targets=["f7"],
|
targets=["f7"],
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ typedef enum {
|
|||||||
NfcCustomEventCardLost,
|
NfcCustomEventCardLost,
|
||||||
|
|
||||||
NfcCustomEventViewExit,
|
NfcCustomEventViewExit,
|
||||||
|
NfcCustomEventRetry,
|
||||||
NfcCustomEventWorkerExit,
|
NfcCustomEventWorkerExit,
|
||||||
NfcCustomEventWorkerUpdate,
|
NfcCustomEventWorkerUpdate,
|
||||||
NfcCustomEventWrongCard,
|
NfcCustomEventWrongCard,
|
||||||
@@ -30,4 +31,6 @@ typedef enum {
|
|||||||
NfcCustomEventPollerFailure,
|
NfcCustomEventPollerFailure,
|
||||||
|
|
||||||
NfcCustomEventListenerUpdate,
|
NfcCustomEventListenerUpdate,
|
||||||
|
|
||||||
|
NfcCustomEventEmulationTimeExpired,
|
||||||
} NfcCustomEvent;
|
} NfcCustomEvent;
|
||||||
|
|||||||
@@ -1,11 +1,9 @@
|
|||||||
#include "nfc_supported_cards.h"
|
#include "nfc_supported_cards.h"
|
||||||
#include "../api/nfc_app_api_interface.h"
|
|
||||||
|
|
||||||
#include "../plugins/supported_cards/nfc_supported_card_plugin.h"
|
#include "../plugins/supported_cards/nfc_supported_card_plugin.h"
|
||||||
|
|
||||||
#include <flipper_application/flipper_application.h>
|
#include <flipper_application/flipper_application.h>
|
||||||
#include <flipper_application/plugins/plugin_manager.h>
|
#include <flipper_application/plugins/plugin_manager.h>
|
||||||
#include <flipper_application/plugins/composite_resolver.h>
|
|
||||||
#include <loader/firmware_api/firmware_api.h>
|
#include <loader/firmware_api/firmware_api.h>
|
||||||
|
|
||||||
#include <furi.h>
|
#include <furi.h>
|
||||||
@@ -52,12 +50,9 @@ struct NfcSupportedCards {
|
|||||||
NfcSupportedCardsLoadContext* load_context;
|
NfcSupportedCardsLoadContext* load_context;
|
||||||
};
|
};
|
||||||
|
|
||||||
NfcSupportedCards* nfc_supported_cards_alloc(void) {
|
NfcSupportedCards* nfc_supported_cards_alloc(CompositeApiResolver* api_resolver) {
|
||||||
NfcSupportedCards* instance = malloc(sizeof(NfcSupportedCards));
|
NfcSupportedCards* instance = malloc(sizeof(NfcSupportedCards));
|
||||||
|
instance->api_resolver = api_resolver;
|
||||||
instance->api_resolver = composite_api_resolver_alloc();
|
|
||||||
composite_api_resolver_add(instance->api_resolver, firmware_api_interface);
|
|
||||||
composite_api_resolver_add(instance->api_resolver, nfc_application_api_interface);
|
|
||||||
|
|
||||||
NfcSupportedCardsPluginCache_init(instance->plugins_cache_arr);
|
NfcSupportedCardsPluginCache_init(instance->plugins_cache_arr);
|
||||||
|
|
||||||
@@ -76,7 +71,6 @@ void nfc_supported_cards_free(NfcSupportedCards* instance) {
|
|||||||
}
|
}
|
||||||
NfcSupportedCardsPluginCache_clear(instance->plugins_cache_arr);
|
NfcSupportedCardsPluginCache_clear(instance->plugins_cache_arr);
|
||||||
|
|
||||||
composite_api_resolver_free(instance->api_resolver);
|
|
||||||
free(instance);
|
free(instance);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <core/string.h>
|
#include <core/string.h>
|
||||||
|
#include <flipper_application/plugins/composite_resolver.h>
|
||||||
|
|
||||||
#include <nfc/nfc.h>
|
#include <nfc/nfc.h>
|
||||||
#include <nfc/nfc_device.h>
|
#include <nfc/nfc_device.h>
|
||||||
@@ -25,7 +26,7 @@ typedef struct NfcSupportedCards NfcSupportedCards;
|
|||||||
*
|
*
|
||||||
* @return pointer to allocated NfcSupportedCards instance.
|
* @return pointer to allocated NfcSupportedCards instance.
|
||||||
*/
|
*/
|
||||||
NfcSupportedCards* nfc_supported_cards_alloc(void);
|
NfcSupportedCards* nfc_supported_cards_alloc(CompositeApiResolver* api_resolver);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Delete an NfcSupportedCards instance
|
* @brief Delete an NfcSupportedCards instance
|
||||||
|
|||||||
@@ -128,4 +128,11 @@ const NfcProtocolSupportBase nfc_protocol_support_emv = {
|
|||||||
.on_enter = nfc_protocol_support_common_on_enter_empty,
|
.on_enter = nfc_protocol_support_common_on_enter_empty,
|
||||||
.on_event = nfc_protocol_support_common_on_event_empty,
|
.on_event = nfc_protocol_support_common_on_event_empty,
|
||||||
},
|
},
|
||||||
|
.scene_write =
|
||||||
|
{
|
||||||
|
.on_enter = nfc_protocol_support_common_on_enter_empty,
|
||||||
|
.on_event = nfc_protocol_support_common_on_event_empty,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
NFC_PROTOCOL_SUPPORT_PLUGIN(emv, NfcProtocolEmv);
|
||||||
|
|||||||
@@ -133,15 +133,6 @@ static void nfc_scene_read_success_on_enter_felica(NfcApp* instance) {
|
|||||||
furi_string_free(temp_str);
|
furi_string_free(temp_str);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool nfc_scene_saved_menu_on_event_felica(NfcApp* instance, SceneManagerEvent event) {
|
|
||||||
if(event.type == SceneManagerEventTypeCustom && event.event == SubmenuIndexCommonEdit) {
|
|
||||||
scene_manager_next_scene(instance->scene_manager, NfcSceneSetUid);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void nfc_scene_emulate_on_enter_felica(NfcApp* instance) {
|
static void nfc_scene_emulate_on_enter_felica(NfcApp* instance) {
|
||||||
const FelicaData* data = nfc_device_get_data(instance->nfc_device, NfcProtocolFelica);
|
const FelicaData* data = nfc_device_get_data(instance->nfc_device, NfcProtocolFelica);
|
||||||
instance->listener = nfc_listener_alloc(instance->nfc, NfcProtocolFelica, data);
|
instance->listener = nfc_listener_alloc(instance->nfc, NfcProtocolFelica, data);
|
||||||
@@ -201,7 +192,7 @@ const NfcProtocolSupportBase nfc_protocol_support_felica = {
|
|||||||
.scene_saved_menu =
|
.scene_saved_menu =
|
||||||
{
|
{
|
||||||
.on_enter = nfc_protocol_support_common_on_enter_empty,
|
.on_enter = nfc_protocol_support_common_on_enter_empty,
|
||||||
.on_event = nfc_scene_saved_menu_on_event_felica,
|
.on_event = nfc_protocol_support_common_on_event_empty,
|
||||||
},
|
},
|
||||||
.scene_save_name =
|
.scene_save_name =
|
||||||
{
|
{
|
||||||
@@ -213,4 +204,11 @@ const NfcProtocolSupportBase nfc_protocol_support_felica = {
|
|||||||
.on_enter = nfc_scene_emulate_on_enter_felica,
|
.on_enter = nfc_scene_emulate_on_enter_felica,
|
||||||
.on_event = nfc_protocol_support_common_on_event_empty,
|
.on_event = nfc_protocol_support_common_on_event_empty,
|
||||||
},
|
},
|
||||||
|
.scene_write =
|
||||||
|
{
|
||||||
|
.on_enter = nfc_protocol_support_common_on_enter_empty,
|
||||||
|
.on_event = nfc_protocol_support_common_on_event_empty,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
NFC_PROTOCOL_SUPPORT_PLUGIN(felica, NfcProtocolFelica);
|
||||||
|
|||||||
@@ -67,21 +67,22 @@ static NfcCommand
|
|||||||
furi_assert(event.protocol == NfcProtocolIso14443_3a);
|
furi_assert(event.protocol == NfcProtocolIso14443_3a);
|
||||||
furi_assert(event.event_data);
|
furi_assert(event.event_data);
|
||||||
|
|
||||||
NfcApp* nfc = context;
|
NfcApp* instance = context;
|
||||||
Iso14443_3aListenerEvent* iso14443_3a_event = event.event_data;
|
Iso14443_3aListenerEvent* iso14443_3a_event = event.event_data;
|
||||||
|
|
||||||
if(iso14443_3a_event->type == Iso14443_3aListenerEventTypeReceivedStandardFrame) {
|
if(iso14443_3a_event->type == Iso14443_3aListenerEventTypeReceivedStandardFrame) {
|
||||||
if(furi_string_size(nfc->text_box_store) < NFC_LOG_SIZE_MAX) {
|
if(furi_string_size(instance->text_box_store) < NFC_LOG_SIZE_MAX) {
|
||||||
furi_string_cat_printf(nfc->text_box_store, "R:");
|
furi_string_cat_printf(instance->text_box_store, "R:");
|
||||||
for(size_t i = 0; i < bit_buffer_get_size_bytes(iso14443_3a_event->data->buffer);
|
for(size_t i = 0; i < bit_buffer_get_size_bytes(iso14443_3a_event->data->buffer);
|
||||||
i++) {
|
i++) {
|
||||||
furi_string_cat_printf(
|
furi_string_cat_printf(
|
||||||
nfc->text_box_store,
|
instance->text_box_store,
|
||||||
" %02X",
|
" %02X",
|
||||||
bit_buffer_get_byte(iso14443_3a_event->data->buffer, i));
|
bit_buffer_get_byte(iso14443_3a_event->data->buffer, i));
|
||||||
}
|
}
|
||||||
furi_string_push_back(nfc->text_box_store, '\n');
|
furi_string_push_back(instance->text_box_store, '\n');
|
||||||
view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventListenerUpdate);
|
view_dispatcher_send_custom_event(
|
||||||
|
instance->view_dispatcher, NfcCustomEventListenerUpdate);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -97,15 +98,6 @@ static void nfc_scene_emulate_on_enter_iso14443_3a(NfcApp* instance) {
|
|||||||
instance->listener, nfc_scene_emulate_listener_callback_iso14443_3a, instance);
|
instance->listener, nfc_scene_emulate_listener_callback_iso14443_3a, instance);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool nfc_scene_read_menu_on_event_iso14443_3a(NfcApp* instance, SceneManagerEvent event) {
|
|
||||||
if(event.type == SceneManagerEventTypeCustom && event.event == SubmenuIndexCommonEmulate) {
|
|
||||||
scene_manager_next_scene(instance->scene_manager, NfcSceneEmulate);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const NfcProtocolSupportBase nfc_protocol_support_iso14443_3a = {
|
const NfcProtocolSupportBase nfc_protocol_support_iso14443_3a = {
|
||||||
.features = NfcProtocolFeatureEmulateUid | NfcProtocolFeatureEditUid,
|
.features = NfcProtocolFeatureEmulateUid | NfcProtocolFeatureEditUid,
|
||||||
|
|
||||||
@@ -122,7 +114,7 @@ const NfcProtocolSupportBase nfc_protocol_support_iso14443_3a = {
|
|||||||
.scene_read_menu =
|
.scene_read_menu =
|
||||||
{
|
{
|
||||||
.on_enter = nfc_protocol_support_common_on_enter_empty,
|
.on_enter = nfc_protocol_support_common_on_enter_empty,
|
||||||
.on_event = nfc_scene_read_menu_on_event_iso14443_3a,
|
.on_event = nfc_protocol_support_common_on_event_empty,
|
||||||
},
|
},
|
||||||
.scene_read_success =
|
.scene_read_success =
|
||||||
{
|
{
|
||||||
@@ -144,4 +136,11 @@ const NfcProtocolSupportBase nfc_protocol_support_iso14443_3a = {
|
|||||||
.on_enter = nfc_scene_emulate_on_enter_iso14443_3a,
|
.on_enter = nfc_scene_emulate_on_enter_iso14443_3a,
|
||||||
.on_event = nfc_protocol_support_common_on_event_empty,
|
.on_event = nfc_protocol_support_common_on_event_empty,
|
||||||
},
|
},
|
||||||
|
.scene_write =
|
||||||
|
{
|
||||||
|
.on_enter = nfc_protocol_support_common_on_enter_empty,
|
||||||
|
.on_event = nfc_protocol_support_common_on_event_empty,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
NFC_PROTOCOL_SUPPORT_PLUGIN(iso14443_3a, NfcProtocolIso14443_3a);
|
||||||
|
|||||||
@@ -60,19 +60,6 @@ static void nfc_scene_read_success_on_enter_iso14443_3b(NfcApp* instance) {
|
|||||||
furi_string_free(temp_str);
|
furi_string_free(temp_str);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool nfc_scene_saved_menu_on_event_iso14443_3b_common(NfcApp* instance, SceneManagerEvent event) {
|
|
||||||
if(event.type == SceneManagerEventTypeCustom && event.event == SubmenuIndexCommonEdit) {
|
|
||||||
scene_manager_next_scene(instance->scene_manager, NfcSceneSetUid);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool nfc_scene_saved_menu_on_event_iso14443_3b(NfcApp* instance, SceneManagerEvent event) {
|
|
||||||
return nfc_scene_saved_menu_on_event_iso14443_3b_common(instance, event);
|
|
||||||
}
|
|
||||||
|
|
||||||
const NfcProtocolSupportBase nfc_protocol_support_iso14443_3b = {
|
const NfcProtocolSupportBase nfc_protocol_support_iso14443_3b = {
|
||||||
.features = NfcProtocolFeatureNone,
|
.features = NfcProtocolFeatureNone,
|
||||||
|
|
||||||
@@ -99,7 +86,7 @@ const NfcProtocolSupportBase nfc_protocol_support_iso14443_3b = {
|
|||||||
.scene_saved_menu =
|
.scene_saved_menu =
|
||||||
{
|
{
|
||||||
.on_enter = nfc_protocol_support_common_on_enter_empty,
|
.on_enter = nfc_protocol_support_common_on_enter_empty,
|
||||||
.on_event = nfc_scene_saved_menu_on_event_iso14443_3b,
|
.on_event = nfc_protocol_support_common_on_event_empty,
|
||||||
},
|
},
|
||||||
.scene_save_name =
|
.scene_save_name =
|
||||||
{
|
{
|
||||||
@@ -111,4 +98,11 @@ const NfcProtocolSupportBase nfc_protocol_support_iso14443_3b = {
|
|||||||
.on_enter = nfc_protocol_support_common_on_enter_empty,
|
.on_enter = nfc_protocol_support_common_on_enter_empty,
|
||||||
.on_event = nfc_protocol_support_common_on_event_empty,
|
.on_event = nfc_protocol_support_common_on_event_empty,
|
||||||
},
|
},
|
||||||
|
.scene_write =
|
||||||
|
{
|
||||||
|
.on_enter = nfc_protocol_support_common_on_enter_empty,
|
||||||
|
.on_event = nfc_protocol_support_common_on_event_empty,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
NFC_PROTOCOL_SUPPORT_PLUGIN(iso14443_3b, NfcProtocolIso14443_3b);
|
||||||
|
|||||||
@@ -1,7 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <nfc/protocols/iso14443_3b/iso14443_3b.h>
|
|
||||||
|
|
||||||
#include "iso14443_3b.h"
|
|
||||||
|
|
||||||
bool nfc_scene_saved_menu_on_event_iso14443_3b_common(NfcApp* instance, SceneManagerEvent event);
|
|
||||||
@@ -70,21 +70,22 @@ NfcCommand nfc_scene_emulate_listener_callback_iso14443_4a(NfcGenericEvent event
|
|||||||
furi_assert(event.protocol == NfcProtocolIso14443_4a);
|
furi_assert(event.protocol == NfcProtocolIso14443_4a);
|
||||||
furi_assert(event.event_data);
|
furi_assert(event.event_data);
|
||||||
|
|
||||||
NfcApp* nfc = context;
|
NfcApp* instance = context;
|
||||||
Iso14443_4aListenerEvent* iso14443_4a_event = event.event_data;
|
Iso14443_4aListenerEvent* iso14443_4a_event = event.event_data;
|
||||||
|
|
||||||
if(iso14443_4a_event->type == Iso14443_4aListenerEventTypeReceivedData) {
|
if(iso14443_4a_event->type == Iso14443_4aListenerEventTypeReceivedData) {
|
||||||
if(furi_string_size(nfc->text_box_store) < NFC_LOG_SIZE_MAX) {
|
if(furi_string_size(instance->text_box_store) < NFC_LOG_SIZE_MAX) {
|
||||||
furi_string_cat_printf(nfc->text_box_store, "R:");
|
furi_string_cat_printf(instance->text_box_store, "R:");
|
||||||
for(size_t i = 0; i < bit_buffer_get_size_bytes(iso14443_4a_event->data->buffer);
|
for(size_t i = 0; i < bit_buffer_get_size_bytes(iso14443_4a_event->data->buffer);
|
||||||
i++) {
|
i++) {
|
||||||
furi_string_cat_printf(
|
furi_string_cat_printf(
|
||||||
nfc->text_box_store,
|
instance->text_box_store,
|
||||||
" %02X",
|
" %02X",
|
||||||
bit_buffer_get_byte(iso14443_4a_event->data->buffer, i));
|
bit_buffer_get_byte(iso14443_4a_event->data->buffer, i));
|
||||||
}
|
}
|
||||||
furi_string_push_back(nfc->text_box_store, '\n');
|
furi_string_push_back(instance->text_box_store, '\n');
|
||||||
view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventListenerUpdate);
|
view_dispatcher_send_custom_event(
|
||||||
|
instance->view_dispatcher, NfcCustomEventListenerUpdate);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -100,15 +101,6 @@ static void nfc_scene_emulate_on_enter_iso14443_4a(NfcApp* instance) {
|
|||||||
instance->listener, nfc_scene_emulate_listener_callback_iso14443_4a, instance);
|
instance->listener, nfc_scene_emulate_listener_callback_iso14443_4a, instance);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool nfc_scene_read_menu_on_event_iso14443_4a(NfcApp* instance, SceneManagerEvent event) {
|
|
||||||
if(event.type == SceneManagerEventTypeCustom && event.event == SubmenuIndexCommonEmulate) {
|
|
||||||
scene_manager_next_scene(instance->scene_manager, NfcSceneEmulate);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const NfcProtocolSupportBase nfc_protocol_support_iso14443_4a = {
|
const NfcProtocolSupportBase nfc_protocol_support_iso14443_4a = {
|
||||||
.features = NfcProtocolFeatureEmulateUid | NfcProtocolFeatureEditUid,
|
.features = NfcProtocolFeatureEmulateUid | NfcProtocolFeatureEditUid,
|
||||||
|
|
||||||
@@ -125,7 +117,7 @@ const NfcProtocolSupportBase nfc_protocol_support_iso14443_4a = {
|
|||||||
.scene_read_menu =
|
.scene_read_menu =
|
||||||
{
|
{
|
||||||
.on_enter = nfc_protocol_support_common_on_enter_empty,
|
.on_enter = nfc_protocol_support_common_on_enter_empty,
|
||||||
.on_event = nfc_scene_read_menu_on_event_iso14443_4a,
|
.on_event = nfc_protocol_support_common_on_event_empty,
|
||||||
},
|
},
|
||||||
.scene_read_success =
|
.scene_read_success =
|
||||||
{
|
{
|
||||||
@@ -147,4 +139,11 @@ const NfcProtocolSupportBase nfc_protocol_support_iso14443_4a = {
|
|||||||
.on_enter = nfc_scene_emulate_on_enter_iso14443_4a,
|
.on_enter = nfc_scene_emulate_on_enter_iso14443_4a,
|
||||||
.on_event = nfc_protocol_support_common_on_event_empty,
|
.on_event = nfc_protocol_support_common_on_event_empty,
|
||||||
},
|
},
|
||||||
|
.scene_write =
|
||||||
|
{
|
||||||
|
.on_enter = nfc_protocol_support_common_on_enter_empty,
|
||||||
|
.on_event = nfc_protocol_support_common_on_event_empty,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
NFC_PROTOCOL_SUPPORT_PLUGIN(iso14443_4a, NfcProtocolIso14443_4a);
|
||||||
|
|||||||
@@ -7,7 +7,6 @@
|
|||||||
|
|
||||||
#include "../nfc_protocol_support_common.h"
|
#include "../nfc_protocol_support_common.h"
|
||||||
#include "../nfc_protocol_support_gui_common.h"
|
#include "../nfc_protocol_support_gui_common.h"
|
||||||
#include "../iso14443_3b/iso14443_3b_i.h"
|
|
||||||
|
|
||||||
static void nfc_scene_info_on_enter_iso14443_4b(NfcApp* instance) {
|
static void nfc_scene_info_on_enter_iso14443_4b(NfcApp* instance) {
|
||||||
const NfcDevice* device = instance->nfc_device;
|
const NfcDevice* device = instance->nfc_device;
|
||||||
@@ -61,23 +60,6 @@ static void nfc_scene_read_success_on_enter_iso14443_4b(NfcApp* instance) {
|
|||||||
furi_string_free(temp_str);
|
furi_string_free(temp_str);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void nfc_scene_saved_menu_on_enter_iso14443_4b(NfcApp* instance) {
|
|
||||||
UNUSED(instance);
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool nfc_scene_read_menu_on_event_iso14443_4b(NfcApp* instance, SceneManagerEvent event) {
|
|
||||||
if(event.type == SceneManagerEventTypeCustom && event.event == SubmenuIndexCommonEmulate) {
|
|
||||||
scene_manager_next_scene(instance->scene_manager, NfcSceneEmulate);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool nfc_scene_saved_menu_on_event_iso14443_4b(NfcApp* instance, SceneManagerEvent event) {
|
|
||||||
return nfc_scene_saved_menu_on_event_iso14443_3b_common(instance, event);
|
|
||||||
}
|
|
||||||
|
|
||||||
const NfcProtocolSupportBase nfc_protocol_support_iso14443_4b = {
|
const NfcProtocolSupportBase nfc_protocol_support_iso14443_4b = {
|
||||||
.features = NfcProtocolFeatureNone,
|
.features = NfcProtocolFeatureNone,
|
||||||
|
|
||||||
@@ -94,7 +76,7 @@ const NfcProtocolSupportBase nfc_protocol_support_iso14443_4b = {
|
|||||||
.scene_read_menu =
|
.scene_read_menu =
|
||||||
{
|
{
|
||||||
.on_enter = nfc_protocol_support_common_on_enter_empty,
|
.on_enter = nfc_protocol_support_common_on_enter_empty,
|
||||||
.on_event = nfc_scene_read_menu_on_event_iso14443_4b,
|
.on_event = nfc_protocol_support_common_on_event_empty,
|
||||||
},
|
},
|
||||||
.scene_read_success =
|
.scene_read_success =
|
||||||
{
|
{
|
||||||
@@ -103,8 +85,8 @@ const NfcProtocolSupportBase nfc_protocol_support_iso14443_4b = {
|
|||||||
},
|
},
|
||||||
.scene_saved_menu =
|
.scene_saved_menu =
|
||||||
{
|
{
|
||||||
.on_enter = nfc_scene_saved_menu_on_enter_iso14443_4b,
|
.on_enter = nfc_protocol_support_common_on_enter_empty,
|
||||||
.on_event = nfc_scene_saved_menu_on_event_iso14443_4b,
|
.on_event = nfc_protocol_support_common_on_event_empty,
|
||||||
},
|
},
|
||||||
.scene_save_name =
|
.scene_save_name =
|
||||||
{
|
{
|
||||||
@@ -116,4 +98,11 @@ const NfcProtocolSupportBase nfc_protocol_support_iso14443_4b = {
|
|||||||
.on_enter = nfc_protocol_support_common_on_enter_empty,
|
.on_enter = nfc_protocol_support_common_on_enter_empty,
|
||||||
.on_event = nfc_protocol_support_common_on_event_empty,
|
.on_event = nfc_protocol_support_common_on_event_empty,
|
||||||
},
|
},
|
||||||
|
.scene_write =
|
||||||
|
{
|
||||||
|
.on_enter = nfc_protocol_support_common_on_enter_empty,
|
||||||
|
.on_event = nfc_protocol_support_common_on_event_empty,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
NFC_PROTOCOL_SUPPORT_PLUGIN(iso14443_4b, NfcProtocolIso14443_4b);
|
||||||
|
|||||||
@@ -80,20 +80,21 @@ static NfcCommand
|
|||||||
furi_assert(event.protocol == NfcProtocolIso15693_3);
|
furi_assert(event.protocol == NfcProtocolIso15693_3);
|
||||||
furi_assert(event.event_data);
|
furi_assert(event.event_data);
|
||||||
|
|
||||||
NfcApp* nfc = context;
|
NfcApp* instance = context;
|
||||||
Iso15693_3ListenerEvent* iso15693_3_event = event.event_data;
|
Iso15693_3ListenerEvent* iso15693_3_event = event.event_data;
|
||||||
|
|
||||||
if(iso15693_3_event->type == Iso15693_3ListenerEventTypeCustomCommand) {
|
if(iso15693_3_event->type == Iso15693_3ListenerEventTypeCustomCommand) {
|
||||||
if(furi_string_size(nfc->text_box_store) < NFC_LOG_SIZE_MAX) {
|
if(furi_string_size(instance->text_box_store) < NFC_LOG_SIZE_MAX) {
|
||||||
furi_string_cat_printf(nfc->text_box_store, "R:");
|
furi_string_cat_printf(instance->text_box_store, "R:");
|
||||||
for(size_t i = 0; i < bit_buffer_get_size_bytes(iso15693_3_event->data->buffer); i++) {
|
for(size_t i = 0; i < bit_buffer_get_size_bytes(iso15693_3_event->data->buffer); i++) {
|
||||||
furi_string_cat_printf(
|
furi_string_cat_printf(
|
||||||
nfc->text_box_store,
|
instance->text_box_store,
|
||||||
" %02X",
|
" %02X",
|
||||||
bit_buffer_get_byte(iso15693_3_event->data->buffer, i));
|
bit_buffer_get_byte(iso15693_3_event->data->buffer, i));
|
||||||
}
|
}
|
||||||
furi_string_push_back(nfc->text_box_store, '\n');
|
furi_string_push_back(instance->text_box_store, '\n');
|
||||||
view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventListenerUpdate);
|
view_dispatcher_send_custom_event(
|
||||||
|
instance->view_dispatcher, NfcCustomEventListenerUpdate);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -108,15 +109,6 @@ static void nfc_scene_emulate_on_enter_iso15693_3(NfcApp* instance) {
|
|||||||
instance->listener, nfc_scene_emulate_listener_callback_iso15693_3, instance);
|
instance->listener, nfc_scene_emulate_listener_callback_iso15693_3, instance);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool nfc_scene_saved_menu_on_event_iso15693_3(NfcApp* instance, SceneManagerEvent event) {
|
|
||||||
if(event.type == SceneManagerEventTypeCustom && event.event == SubmenuIndexCommonEdit) {
|
|
||||||
scene_manager_next_scene(instance->scene_manager, NfcSceneSetUid);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const NfcProtocolSupportBase nfc_protocol_support_iso15693_3 = {
|
const NfcProtocolSupportBase nfc_protocol_support_iso15693_3 = {
|
||||||
.features = NfcProtocolFeatureEmulateFull | NfcProtocolFeatureEditUid |
|
.features = NfcProtocolFeatureEmulateFull | NfcProtocolFeatureEditUid |
|
||||||
NfcProtocolFeatureMoreInfo,
|
NfcProtocolFeatureMoreInfo,
|
||||||
@@ -149,7 +141,7 @@ const NfcProtocolSupportBase nfc_protocol_support_iso15693_3 = {
|
|||||||
.scene_saved_menu =
|
.scene_saved_menu =
|
||||||
{
|
{
|
||||||
.on_enter = nfc_protocol_support_common_on_enter_empty,
|
.on_enter = nfc_protocol_support_common_on_enter_empty,
|
||||||
.on_event = nfc_scene_saved_menu_on_event_iso15693_3,
|
.on_event = nfc_protocol_support_common_on_event_empty,
|
||||||
},
|
},
|
||||||
.scene_save_name =
|
.scene_save_name =
|
||||||
{
|
{
|
||||||
@@ -161,4 +153,11 @@ const NfcProtocolSupportBase nfc_protocol_support_iso15693_3 = {
|
|||||||
.on_enter = nfc_scene_emulate_on_enter_iso15693_3,
|
.on_enter = nfc_scene_emulate_on_enter_iso15693_3,
|
||||||
.on_event = nfc_protocol_support_common_on_event_empty,
|
.on_event = nfc_protocol_support_common_on_event_empty,
|
||||||
},
|
},
|
||||||
|
.scene_write =
|
||||||
|
{
|
||||||
|
.on_enter = nfc_protocol_support_common_on_enter_empty,
|
||||||
|
.on_event = nfc_protocol_support_common_on_event_empty,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
NFC_PROTOCOL_SUPPORT_PLUGIN(iso15693_3, NfcProtocolIso15693_3);
|
||||||
|
|||||||
@@ -12,10 +12,9 @@
|
|||||||
|
|
||||||
enum {
|
enum {
|
||||||
SubmenuIndexDetectReader = SubmenuIndexCommonMax,
|
SubmenuIndexDetectReader = SubmenuIndexCommonMax,
|
||||||
SubmenuIndexWrite,
|
|
||||||
SubmenuIndexUpdate,
|
|
||||||
SubmenuIndexDictAttack,
|
SubmenuIndexDictAttack,
|
||||||
SubmenuIndexCrackNonces,
|
SubmenuIndexCrackNonces,
|
||||||
|
SubmenuIndexUpdate,
|
||||||
};
|
};
|
||||||
|
|
||||||
static void nfc_scene_info_on_enter_mf_classic(NfcApp* instance) {
|
static void nfc_scene_info_on_enter_mf_classic(NfcApp* instance) {
|
||||||
@@ -115,6 +114,9 @@ static void nfc_scene_read_menu_on_enter_mf_classic(NfcApp* instance) {
|
|||||||
Submenu* submenu = instance->submenu;
|
Submenu* submenu = instance->submenu;
|
||||||
const MfClassicData* data = nfc_device_get_data(instance->nfc_device, NfcProtocolMfClassic);
|
const MfClassicData* data = nfc_device_get_data(instance->nfc_device, NfcProtocolMfClassic);
|
||||||
|
|
||||||
|
// Doesn't make sense to show "Write to Initial Card" right after reading
|
||||||
|
submenu_remove_item(submenu, SubmenuIndexCommonWrite);
|
||||||
|
|
||||||
if(!mf_classic_is_card_read(data)) {
|
if(!mf_classic_is_card_read(data)) {
|
||||||
submenu_add_item(
|
submenu_add_item(
|
||||||
submenu,
|
submenu,
|
||||||
@@ -160,6 +162,8 @@ static void nfc_scene_saved_menu_on_enter_mf_classic(NfcApp* instance) {
|
|||||||
Submenu* submenu = instance->submenu;
|
Submenu* submenu = instance->submenu;
|
||||||
const MfClassicData* data = nfc_device_get_data(instance->nfc_device, NfcProtocolMfClassic);
|
const MfClassicData* data = nfc_device_get_data(instance->nfc_device, NfcProtocolMfClassic);
|
||||||
|
|
||||||
|
submenu_change_item_label(submenu, SubmenuIndexCommonWrite, "Write to Initial Card");
|
||||||
|
|
||||||
if(!mf_classic_is_card_read(data)) {
|
if(!mf_classic_is_card_read(data)) {
|
||||||
submenu_add_item(
|
submenu_add_item(
|
||||||
submenu,
|
submenu,
|
||||||
@@ -175,12 +179,6 @@ static void nfc_scene_saved_menu_on_enter_mf_classic(NfcApp* instance) {
|
|||||||
nfc_protocol_support_common_submenu_callback,
|
nfc_protocol_support_common_submenu_callback,
|
||||||
instance);
|
instance);
|
||||||
}
|
}
|
||||||
submenu_add_item(
|
|
||||||
submenu,
|
|
||||||
"Write to Initial Card",
|
|
||||||
SubmenuIndexWrite,
|
|
||||||
nfc_protocol_support_common_submenu_callback,
|
|
||||||
instance);
|
|
||||||
|
|
||||||
submenu_add_item(
|
submenu_add_item(
|
||||||
submenu,
|
submenu,
|
||||||
@@ -215,9 +213,6 @@ static bool nfc_scene_read_menu_on_event_mf_classic(NfcApp* instance, SceneManag
|
|||||||
scene_manager_next_scene(instance->scene_manager, NfcSceneMfClassicDictAttack);
|
scene_manager_next_scene(instance->scene_manager, NfcSceneMfClassicDictAttack);
|
||||||
}
|
}
|
||||||
consumed = true;
|
consumed = true;
|
||||||
} else if(event.event == SubmenuIndexCommonEdit) {
|
|
||||||
scene_manager_next_scene(instance->scene_manager, NfcSceneSetUid);
|
|
||||||
consumed = true;
|
|
||||||
} else if(event.event == SubmenuIndexCrackNonces) {
|
} else if(event.event == SubmenuIndexCrackNonces) {
|
||||||
scene_manager_set_scene_state(
|
scene_manager_set_scene_state(
|
||||||
instance->scene_manager, NfcSceneSaveConfirm, NfcSceneSaveConfirmStateCrackNonces);
|
instance->scene_manager, NfcSceneSaveConfirm, NfcSceneSaveConfirmStateCrackNonces);
|
||||||
@@ -236,18 +231,15 @@ static bool nfc_scene_saved_menu_on_event_mf_classic(NfcApp* instance, SceneMana
|
|||||||
if(event.event == SubmenuIndexDetectReader) {
|
if(event.event == SubmenuIndexDetectReader) {
|
||||||
scene_manager_next_scene(instance->scene_manager, NfcSceneMfClassicDetectReader);
|
scene_manager_next_scene(instance->scene_manager, NfcSceneMfClassicDetectReader);
|
||||||
consumed = true;
|
consumed = true;
|
||||||
} else if(event.event == SubmenuIndexWrite) {
|
|
||||||
scene_manager_next_scene(instance->scene_manager, NfcSceneMfClassicWriteInitial);
|
|
||||||
consumed = true;
|
|
||||||
} else if(event.event == SubmenuIndexUpdate) {
|
|
||||||
scene_manager_next_scene(instance->scene_manager, NfcSceneMfClassicUpdateInitial);
|
|
||||||
consumed = true;
|
|
||||||
} else if(event.event == SubmenuIndexDictAttack) {
|
} else if(event.event == SubmenuIndexDictAttack) {
|
||||||
if(!scene_manager_search_and_switch_to_previous_scene(
|
if(!scene_manager_search_and_switch_to_previous_scene(
|
||||||
instance->scene_manager, NfcSceneMfClassicDictAttack)) {
|
instance->scene_manager, NfcSceneMfClassicDictAttack)) {
|
||||||
scene_manager_next_scene(instance->scene_manager, NfcSceneMfClassicDictAttack);
|
scene_manager_next_scene(instance->scene_manager, NfcSceneMfClassicDictAttack);
|
||||||
}
|
}
|
||||||
consumed = true;
|
consumed = true;
|
||||||
|
} else if(event.event == SubmenuIndexUpdate) {
|
||||||
|
scene_manager_next_scene(instance->scene_manager, NfcSceneMfClassicUpdateInitial);
|
||||||
|
consumed = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -267,8 +259,71 @@ static bool nfc_scene_save_name_on_event_mf_classic(NfcApp* instance, SceneManag
|
|||||||
return consumed;
|
return consumed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static NfcCommand
|
||||||
|
nfc_scene_write_poller_callback_mf_classic(NfcGenericEvent event, void* context) {
|
||||||
|
furi_assert(event.protocol == NfcProtocolMfClassic);
|
||||||
|
|
||||||
|
NfcApp* instance = context;
|
||||||
|
MfClassicPollerEvent* mfc_event = event.event_data;
|
||||||
|
NfcCommand command = NfcCommandContinue;
|
||||||
|
const MfClassicData* write_data =
|
||||||
|
nfc_device_get_data(instance->nfc_device, NfcProtocolMfClassic);
|
||||||
|
|
||||||
|
if(mfc_event->type == MfClassicPollerEventTypeCardDetected) {
|
||||||
|
furi_string_reset(instance->text_box_store);
|
||||||
|
view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventCardDetected);
|
||||||
|
} else if(mfc_event->type == MfClassicPollerEventTypeCardLost) {
|
||||||
|
furi_string_set(instance->text_box_store, "Use the source\ncard only");
|
||||||
|
view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventCardLost);
|
||||||
|
} else if(mfc_event->type == MfClassicPollerEventTypeRequestMode) {
|
||||||
|
const MfClassicData* tag_data = nfc_poller_get_data(instance->poller);
|
||||||
|
if(iso14443_3a_is_equal(tag_data->iso14443_3a_data, write_data->iso14443_3a_data)) {
|
||||||
|
mfc_event->data->poller_mode.mode = MfClassicPollerModeWrite;
|
||||||
|
} else {
|
||||||
|
furi_string_set(
|
||||||
|
instance->text_box_store, "Use source card!\nTo write blanks\nuse NFC Magic app");
|
||||||
|
view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventWrongCard);
|
||||||
|
command = NfcCommandStop;
|
||||||
|
}
|
||||||
|
} else if(mfc_event->type == MfClassicPollerEventTypeRequestSectorTrailer) {
|
||||||
|
uint8_t sector = mfc_event->data->sec_tr_data.sector_num;
|
||||||
|
uint8_t sec_tr = mf_classic_get_sector_trailer_num_by_sector(sector);
|
||||||
|
if(mf_classic_is_block_read(write_data, sec_tr)) {
|
||||||
|
mfc_event->data->sec_tr_data.sector_trailer = write_data->block[sec_tr];
|
||||||
|
mfc_event->data->sec_tr_data.sector_trailer_provided = true;
|
||||||
|
} else {
|
||||||
|
mfc_event->data->sec_tr_data.sector_trailer_provided = false;
|
||||||
|
}
|
||||||
|
} else if(mfc_event->type == MfClassicPollerEventTypeRequestWriteBlock) {
|
||||||
|
uint8_t block_num = mfc_event->data->write_block_data.block_num;
|
||||||
|
if(mf_classic_is_block_read(write_data, block_num)) {
|
||||||
|
mfc_event->data->write_block_data.write_block = write_data->block[block_num];
|
||||||
|
mfc_event->data->write_block_data.write_block_provided = true;
|
||||||
|
} else {
|
||||||
|
mfc_event->data->write_block_data.write_block_provided = false;
|
||||||
|
}
|
||||||
|
} else if(mfc_event->type == MfClassicPollerEventTypeSuccess) {
|
||||||
|
furi_string_reset(instance->text_box_store);
|
||||||
|
view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventPollerSuccess);
|
||||||
|
command = NfcCommandStop;
|
||||||
|
} else if(mfc_event->type == MfClassicPollerEventTypeFail) {
|
||||||
|
furi_string_set(instance->text_box_store, "Not all sectors\nwere written\ncorrectly");
|
||||||
|
view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventPollerFailure);
|
||||||
|
command = NfcCommandStop;
|
||||||
|
}
|
||||||
|
|
||||||
|
return command;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void nfc_scene_write_on_enter_mf_classic(NfcApp* instance) {
|
||||||
|
instance->poller = nfc_poller_alloc(instance->nfc, NfcProtocolMfClassic);
|
||||||
|
nfc_poller_start(instance->poller, nfc_scene_write_poller_callback_mf_classic, instance);
|
||||||
|
furi_string_set(instance->text_box_store, "Use the source\ncard only");
|
||||||
|
}
|
||||||
|
|
||||||
const NfcProtocolSupportBase nfc_protocol_support_mf_classic = {
|
const NfcProtocolSupportBase nfc_protocol_support_mf_classic = {
|
||||||
.features = NfcProtocolFeatureEmulateFull | NfcProtocolFeatureMoreInfo,
|
.features = NfcProtocolFeatureEmulateFull | NfcProtocolFeatureMoreInfo |
|
||||||
|
NfcProtocolFeatureWrite,
|
||||||
|
|
||||||
.scene_info =
|
.scene_info =
|
||||||
{
|
{
|
||||||
@@ -310,4 +365,11 @@ const NfcProtocolSupportBase nfc_protocol_support_mf_classic = {
|
|||||||
.on_enter = nfc_scene_emulate_on_enter_mf_classic,
|
.on_enter = nfc_scene_emulate_on_enter_mf_classic,
|
||||||
.on_event = nfc_protocol_support_common_on_event_empty,
|
.on_event = nfc_protocol_support_common_on_event_empty,
|
||||||
},
|
},
|
||||||
|
.scene_write =
|
||||||
|
{
|
||||||
|
.on_enter = nfc_scene_write_on_enter_mf_classic,
|
||||||
|
.on_event = nfc_protocol_support_common_on_event_empty,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
NFC_PROTOCOL_SUPPORT_PLUGIN(mf_classic, NfcProtocolMfClassic);
|
||||||
|
|||||||
@@ -124,4 +124,11 @@ const NfcProtocolSupportBase nfc_protocol_support_mf_desfire = {
|
|||||||
.on_enter = nfc_scene_emulate_on_enter_mf_desfire,
|
.on_enter = nfc_scene_emulate_on_enter_mf_desfire,
|
||||||
.on_event = nfc_protocol_support_common_on_event_empty,
|
.on_event = nfc_protocol_support_common_on_event_empty,
|
||||||
},
|
},
|
||||||
|
.scene_write =
|
||||||
|
{
|
||||||
|
.on_enter = nfc_protocol_support_common_on_enter_empty,
|
||||||
|
.on_event = nfc_protocol_support_common_on_event_empty,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
NFC_PROTOCOL_SUPPORT_PLUGIN(mf_desfire, NfcProtocolMfDesfire);
|
||||||
|
|||||||
@@ -25,6 +25,20 @@ static void nfc_scene_info_on_enter_mf_plus(NfcApp* instance) {
|
|||||||
|
|
||||||
furi_string_free(temp_str);
|
furi_string_free(temp_str);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void nfc_scene_more_info_on_enter_mf_plus(NfcApp* instance) {
|
||||||
|
const NfcDevice* device = instance->nfc_device;
|
||||||
|
const MfPlusData* data = nfc_device_get_data(device, NfcProtocolMfPlus);
|
||||||
|
|
||||||
|
furi_string_reset(instance->text_box_store);
|
||||||
|
nfc_render_mf_plus_data(data, instance->text_box_store);
|
||||||
|
|
||||||
|
text_box_set_font(instance->text_box, TextBoxFontHex);
|
||||||
|
text_box_set_text(instance->text_box, furi_string_get_cstr(instance->text_box_store));
|
||||||
|
|
||||||
|
view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewTextBox);
|
||||||
|
}
|
||||||
|
|
||||||
static NfcCommand nfc_scene_read_poller_callback_mf_plus(NfcGenericEvent event, void* context) {
|
static NfcCommand nfc_scene_read_poller_callback_mf_plus(NfcGenericEvent event, void* context) {
|
||||||
furi_assert(context);
|
furi_assert(context);
|
||||||
furi_assert(event.protocol == NfcProtocolMfPlus);
|
furi_assert(event.protocol == NfcProtocolMfPlus);
|
||||||
@@ -78,7 +92,7 @@ static void nfc_scene_emulate_on_enter_mf_plus(NfcApp* instance) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const NfcProtocolSupportBase nfc_protocol_support_mf_plus = {
|
const NfcProtocolSupportBase nfc_protocol_support_mf_plus = {
|
||||||
.features = NfcProtocolFeatureEmulateUid,
|
.features = NfcProtocolFeatureEmulateUid | NfcProtocolFeatureMoreInfo,
|
||||||
|
|
||||||
.scene_info =
|
.scene_info =
|
||||||
{
|
{
|
||||||
@@ -87,7 +101,7 @@ const NfcProtocolSupportBase nfc_protocol_support_mf_plus = {
|
|||||||
},
|
},
|
||||||
.scene_more_info =
|
.scene_more_info =
|
||||||
{
|
{
|
||||||
.on_enter = nfc_protocol_support_common_on_enter_empty,
|
.on_enter = nfc_scene_more_info_on_enter_mf_plus,
|
||||||
.on_event = nfc_protocol_support_common_on_event_empty,
|
.on_event = nfc_protocol_support_common_on_event_empty,
|
||||||
},
|
},
|
||||||
.scene_read =
|
.scene_read =
|
||||||
@@ -120,4 +134,11 @@ const NfcProtocolSupportBase nfc_protocol_support_mf_plus = {
|
|||||||
.on_enter = nfc_scene_emulate_on_enter_mf_plus,
|
.on_enter = nfc_scene_emulate_on_enter_mf_plus,
|
||||||
.on_event = nfc_protocol_support_common_on_event_empty,
|
.on_event = nfc_protocol_support_common_on_event_empty,
|
||||||
},
|
},
|
||||||
|
.scene_write =
|
||||||
|
{
|
||||||
|
.on_enter = nfc_protocol_support_common_on_enter_empty,
|
||||||
|
.on_event = nfc_protocol_support_common_on_event_empty,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
NFC_PROTOCOL_SUPPORT_PLUGIN(mf_plus, NfcProtocolMfPlus);
|
||||||
|
|||||||
@@ -15,7 +15,21 @@ void nfc_render_mf_plus_info(
|
|||||||
}
|
}
|
||||||
|
|
||||||
void nfc_render_mf_plus_data(const MfPlusData* data, FuriString* str) {
|
void nfc_render_mf_plus_data(const MfPlusData* data, FuriString* str) {
|
||||||
|
MfPlusVersion empty_version = {0};
|
||||||
|
if(memcmp(&data->version, &empty_version, sizeof(MfPlusVersion)) == 0) {
|
||||||
|
const char* device_name = mf_plus_get_device_name(data, NfcDeviceNameTypeFull);
|
||||||
|
if(data->type == MfPlusTypeUnknown || data->size == MfPlusSizeUnknown ||
|
||||||
|
data->security_level == MfPlusSecurityLevelUnknown) {
|
||||||
|
furi_string_cat_printf(str, "This %s", device_name);
|
||||||
|
furi_string_replace(str, " Unknown", "");
|
||||||
|
} else {
|
||||||
|
furi_string_cat(str, device_name);
|
||||||
|
}
|
||||||
|
furi_string_replace(str, "Mifare", "MIFARE");
|
||||||
|
furi_string_cat(str, " does not support the GetVersion command, extra info unavailable\n");
|
||||||
|
} else {
|
||||||
nfc_render_mf_plus_version(&data->version, str);
|
nfc_render_mf_plus_version(&data->version, str);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void nfc_render_mf_plus_version(const MfPlusVersion* data, FuriString* str) {
|
void nfc_render_mf_plus_version(const MfPlusVersion* data, FuriString* str) {
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ enum {
|
|||||||
SubmenuIndexUnlock = SubmenuIndexCommonMax,
|
SubmenuIndexUnlock = SubmenuIndexCommonMax,
|
||||||
SubmenuIndexUnlockByReader,
|
SubmenuIndexUnlockByReader,
|
||||||
SubmenuIndexUnlockByPassword,
|
SubmenuIndexUnlockByPassword,
|
||||||
SubmenuIndexWrite,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
@@ -182,24 +181,22 @@ static void nfc_scene_read_and_saved_menu_on_enter_mf_ultralight(NfcApp* instanc
|
|||||||
|
|
||||||
const MfUltralightData* data =
|
const MfUltralightData* data =
|
||||||
nfc_device_get_data(instance->nfc_device, NfcProtocolMfUltralight);
|
nfc_device_get_data(instance->nfc_device, NfcProtocolMfUltralight);
|
||||||
|
bool is_locked = !mf_ultralight_is_all_data_read(data);
|
||||||
|
|
||||||
if(!mf_ultralight_is_all_data_read(data)) {
|
if(is_locked ||
|
||||||
|
(data->type != MfUltralightTypeNTAG213 && data->type != MfUltralightTypeNTAG215 &&
|
||||||
|
data->type != MfUltralightTypeNTAG216 && data->type != MfUltralightTypeUL11 &&
|
||||||
|
data->type != MfUltralightTypeUL21 && data->type != MfUltralightTypeOrigin)) {
|
||||||
|
submenu_remove_item(submenu, SubmenuIndexCommonWrite);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(is_locked) {
|
||||||
submenu_add_item(
|
submenu_add_item(
|
||||||
submenu,
|
submenu,
|
||||||
"Unlock",
|
"Unlock",
|
||||||
SubmenuIndexUnlock,
|
SubmenuIndexUnlock,
|
||||||
nfc_protocol_support_common_submenu_callback,
|
nfc_protocol_support_common_submenu_callback,
|
||||||
instance);
|
instance);
|
||||||
} else if(
|
|
||||||
data->type == MfUltralightTypeNTAG213 || data->type == MfUltralightTypeNTAG215 ||
|
|
||||||
data->type == MfUltralightTypeNTAG216 || data->type == MfUltralightTypeUL11 ||
|
|
||||||
data->type == MfUltralightTypeUL21 || data->type == MfUltralightTypeOrigin) {
|
|
||||||
submenu_add_item(
|
|
||||||
submenu,
|
|
||||||
"Write",
|
|
||||||
SubmenuIndexWrite,
|
|
||||||
nfc_protocol_support_common_submenu_callback,
|
|
||||||
instance);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -252,19 +249,57 @@ static bool nfc_scene_read_and_saved_menu_on_event_mf_ultralight(
|
|||||||
NfcSceneMfUltralightUnlockMenu;
|
NfcSceneMfUltralightUnlockMenu;
|
||||||
scene_manager_next_scene(instance->scene_manager, next_scene);
|
scene_manager_next_scene(instance->scene_manager, next_scene);
|
||||||
consumed = true;
|
consumed = true;
|
||||||
} else if(event.event == SubmenuIndexWrite) {
|
|
||||||
scene_manager_next_scene(instance->scene_manager, NfcSceneMfUltralightWrite);
|
|
||||||
consumed = true;
|
|
||||||
} else if(event.event == SubmenuIndexCommonEdit) {
|
|
||||||
scene_manager_next_scene(instance->scene_manager, NfcSceneSetUid);
|
|
||||||
consumed = true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return consumed;
|
return consumed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static NfcCommand
|
||||||
|
nfc_scene_write_poller_callback_mf_ultralight(NfcGenericEvent event, void* context) {
|
||||||
|
furi_assert(event.protocol == NfcProtocolMfUltralight);
|
||||||
|
|
||||||
|
NfcApp* instance = context;
|
||||||
|
MfUltralightPollerEvent* mf_ultralight_event = event.event_data;
|
||||||
|
NfcCommand command = NfcCommandContinue;
|
||||||
|
|
||||||
|
if(mf_ultralight_event->type == MfUltralightPollerEventTypeRequestMode) {
|
||||||
|
mf_ultralight_event->data->poller_mode = MfUltralightPollerModeWrite;
|
||||||
|
furi_string_reset(instance->text_box_store);
|
||||||
|
view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventCardDetected);
|
||||||
|
} else if(mf_ultralight_event->type == MfUltralightPollerEventTypeAuthRequest) {
|
||||||
|
mf_ultralight_event->data->auth_context.skip_auth = true;
|
||||||
|
} else if(mf_ultralight_event->type == MfUltralightPollerEventTypeRequestWriteData) {
|
||||||
|
mf_ultralight_event->data->write_data =
|
||||||
|
nfc_device_get_data(instance->nfc_device, NfcProtocolMfUltralight);
|
||||||
|
} else if(mf_ultralight_event->type == MfUltralightPollerEventTypeCardMismatch) {
|
||||||
|
furi_string_set(instance->text_box_store, "Card of the same\ntype should be\n presented");
|
||||||
|
view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventWrongCard);
|
||||||
|
command = NfcCommandStop;
|
||||||
|
} else if(mf_ultralight_event->type == MfUltralightPollerEventTypeCardLocked) {
|
||||||
|
furi_string_set(
|
||||||
|
instance->text_box_store, "Card protected by\npassword, AUTH0\nor lock bits");
|
||||||
|
view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventPollerFailure);
|
||||||
|
command = NfcCommandStop;
|
||||||
|
} else if(mf_ultralight_event->type == MfUltralightPollerEventTypeWriteFail) {
|
||||||
|
command = NfcCommandStop;
|
||||||
|
} else if(mf_ultralight_event->type == MfUltralightPollerEventTypeWriteSuccess) {
|
||||||
|
furi_string_reset(instance->text_box_store);
|
||||||
|
view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventPollerSuccess);
|
||||||
|
command = NfcCommandStop;
|
||||||
|
}
|
||||||
|
|
||||||
|
return command;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void nfc_scene_write_on_enter_mf_ultralight(NfcApp* instance) {
|
||||||
|
instance->poller = nfc_poller_alloc(instance->nfc, NfcProtocolMfUltralight);
|
||||||
|
nfc_poller_start(instance->poller, nfc_scene_write_poller_callback_mf_ultralight, instance);
|
||||||
|
furi_string_set(instance->text_box_store, "Apply the initial\ncard only");
|
||||||
|
}
|
||||||
|
|
||||||
const NfcProtocolSupportBase nfc_protocol_support_mf_ultralight = {
|
const NfcProtocolSupportBase nfc_protocol_support_mf_ultralight = {
|
||||||
.features = NfcProtocolFeatureEmulateFull | NfcProtocolFeatureMoreInfo,
|
.features = NfcProtocolFeatureEmulateFull | NfcProtocolFeatureMoreInfo |
|
||||||
|
NfcProtocolFeatureWrite,
|
||||||
|
|
||||||
.scene_info =
|
.scene_info =
|
||||||
{
|
{
|
||||||
@@ -306,4 +341,11 @@ const NfcProtocolSupportBase nfc_protocol_support_mf_ultralight = {
|
|||||||
.on_enter = nfc_scene_emulate_on_enter_mf_ultralight,
|
.on_enter = nfc_scene_emulate_on_enter_mf_ultralight,
|
||||||
.on_event = nfc_protocol_support_common_on_event_empty,
|
.on_event = nfc_protocol_support_common_on_event_empty,
|
||||||
},
|
},
|
||||||
|
.scene_write =
|
||||||
|
{
|
||||||
|
.on_enter = nfc_scene_write_on_enter_mf_ultralight,
|
||||||
|
.on_event = nfc_protocol_support_common_on_event_empty,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
NFC_PROTOCOL_SUPPORT_PLUGIN(mf_ultralight, NfcProtocolMfUltralight);
|
||||||
|
|||||||
@@ -9,9 +9,13 @@
|
|||||||
|
|
||||||
#include "nfc/nfc_app_i.h"
|
#include "nfc/nfc_app_i.h"
|
||||||
|
|
||||||
#include "nfc_protocol_support_defs.h"
|
#include "nfc_protocol_support_base.h"
|
||||||
#include "nfc_protocol_support_gui_common.h"
|
#include "nfc_protocol_support_gui_common.h"
|
||||||
|
|
||||||
|
#include <flipper_application/plugins/plugin_manager.h>
|
||||||
|
|
||||||
|
#define TAG "NfcProtocolSupport"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Common scene entry handler.
|
* @brief Common scene entry handler.
|
||||||
*
|
*
|
||||||
@@ -46,6 +50,147 @@ typedef struct {
|
|||||||
|
|
||||||
static const NfcProtocolSupportCommonSceneBase nfc_protocol_support_scenes[];
|
static const NfcProtocolSupportCommonSceneBase nfc_protocol_support_scenes[];
|
||||||
|
|
||||||
|
const NfcProtocolSupportBase nfc_protocol_support_empty = {
|
||||||
|
.features = NfcProtocolFeatureNone,
|
||||||
|
|
||||||
|
.scene_info =
|
||||||
|
{
|
||||||
|
.on_enter = nfc_protocol_support_common_on_enter_empty,
|
||||||
|
.on_event = nfc_protocol_support_common_on_event_empty,
|
||||||
|
},
|
||||||
|
.scene_more_info =
|
||||||
|
{
|
||||||
|
.on_enter = nfc_protocol_support_common_on_enter_empty,
|
||||||
|
.on_event = nfc_protocol_support_common_on_event_empty,
|
||||||
|
},
|
||||||
|
.scene_read =
|
||||||
|
{
|
||||||
|
.on_enter = nfc_protocol_support_common_on_enter_empty,
|
||||||
|
.on_event = nfc_protocol_support_common_on_event_empty,
|
||||||
|
},
|
||||||
|
.scene_read_menu =
|
||||||
|
{
|
||||||
|
.on_enter = nfc_protocol_support_common_on_enter_empty,
|
||||||
|
.on_event = nfc_protocol_support_common_on_event_empty,
|
||||||
|
},
|
||||||
|
.scene_read_success =
|
||||||
|
{
|
||||||
|
.on_enter = nfc_protocol_support_common_on_enter_empty,
|
||||||
|
.on_event = nfc_protocol_support_common_on_event_empty,
|
||||||
|
},
|
||||||
|
.scene_saved_menu =
|
||||||
|
{
|
||||||
|
.on_enter = nfc_protocol_support_common_on_enter_empty,
|
||||||
|
.on_event = nfc_protocol_support_common_on_event_empty,
|
||||||
|
},
|
||||||
|
.scene_save_name =
|
||||||
|
{
|
||||||
|
.on_enter = nfc_protocol_support_common_on_enter_empty,
|
||||||
|
.on_event = nfc_protocol_support_common_on_event_empty,
|
||||||
|
},
|
||||||
|
.scene_emulate =
|
||||||
|
{
|
||||||
|
.on_enter = nfc_protocol_support_common_on_enter_empty,
|
||||||
|
.on_event = nfc_protocol_support_common_on_event_empty,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
struct NfcProtocolSupport {
|
||||||
|
NfcProtocol protocol;
|
||||||
|
PluginManager* plugin_manager;
|
||||||
|
const NfcProtocolSupportBase* base;
|
||||||
|
};
|
||||||
|
|
||||||
|
const char* nfc_protocol_support_plugin_names[NfcProtocolNum] = {
|
||||||
|
[NfcProtocolIso14443_3a] = "iso14443_3a",
|
||||||
|
[NfcProtocolIso14443_3b] = "iso14443_3b",
|
||||||
|
[NfcProtocolIso14443_4a] = "iso14443_4a",
|
||||||
|
[NfcProtocolIso14443_4b] = "iso14443_4b",
|
||||||
|
[NfcProtocolIso15693_3] = "iso15693_3",
|
||||||
|
[NfcProtocolFelica] = "felica",
|
||||||
|
[NfcProtocolMfUltralight] = "mf_ultralight",
|
||||||
|
[NfcProtocolMfClassic] = "mf_classic",
|
||||||
|
[NfcProtocolMfPlus] = "mf_plus",
|
||||||
|
[NfcProtocolMfDesfire] = "mf_desfire",
|
||||||
|
[NfcProtocolSlix] = "slix",
|
||||||
|
[NfcProtocolSt25tb] = "st25tb",
|
||||||
|
[NfcProtocolNtag4xx] = "ntag4xx",
|
||||||
|
[NfcProtocolType4Tag] = "type_4_tag",
|
||||||
|
[NfcProtocolEmv] = "emv",
|
||||||
|
/* Add new protocol support plugin names here */
|
||||||
|
};
|
||||||
|
|
||||||
|
void nfc_protocol_support_alloc(NfcProtocol protocol, void* context) {
|
||||||
|
furi_assert(context);
|
||||||
|
|
||||||
|
NfcApp* instance = context;
|
||||||
|
|
||||||
|
NfcProtocolSupport* protocol_support = malloc(sizeof(NfcProtocolSupport));
|
||||||
|
protocol_support->protocol = protocol;
|
||||||
|
|
||||||
|
const char* protocol_name = nfc_protocol_support_plugin_names[protocol];
|
||||||
|
FuriString* plugin_path =
|
||||||
|
furi_string_alloc_printf(APP_ASSETS_PATH("plugins/nfc_%s.fal"), protocol_name);
|
||||||
|
FURI_LOG_D(TAG, "Loading %s", furi_string_get_cstr(plugin_path));
|
||||||
|
|
||||||
|
protocol_support->plugin_manager = plugin_manager_alloc(
|
||||||
|
NFC_PROTOCOL_SUPPORT_PLUGIN_APP_ID,
|
||||||
|
NFC_PROTOCOL_SUPPORT_PLUGIN_API_VERSION,
|
||||||
|
composite_api_resolver_get(instance->api_resolver));
|
||||||
|
do {
|
||||||
|
if(plugin_manager_load_single(
|
||||||
|
protocol_support->plugin_manager, furi_string_get_cstr(plugin_path)) !=
|
||||||
|
PluginManagerErrorNone) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
const NfcProtocolSupportPlugin* plugin =
|
||||||
|
plugin_manager_get_ep(protocol_support->plugin_manager, 0);
|
||||||
|
|
||||||
|
if(plugin->protocol != protocol) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
protocol_support->base = plugin->base;
|
||||||
|
} while(false);
|
||||||
|
if(!protocol_support->base) {
|
||||||
|
protocol_support->base = &nfc_protocol_support_empty;
|
||||||
|
plugin_manager_free(protocol_support->plugin_manager);
|
||||||
|
protocol_support->plugin_manager = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
furi_string_free(plugin_path);
|
||||||
|
|
||||||
|
instance->protocol_support = protocol_support;
|
||||||
|
}
|
||||||
|
|
||||||
|
void nfc_protocol_support_free(void* context) {
|
||||||
|
furi_assert(context);
|
||||||
|
|
||||||
|
NfcApp* instance = context;
|
||||||
|
|
||||||
|
if(instance->protocol_support->plugin_manager) {
|
||||||
|
plugin_manager_free(instance->protocol_support->plugin_manager);
|
||||||
|
}
|
||||||
|
free(instance->protocol_support);
|
||||||
|
instance->protocol_support = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const NfcProtocolSupportBase*
|
||||||
|
nfc_protocol_support_get(NfcProtocol protocol, void* context) {
|
||||||
|
furi_assert(context);
|
||||||
|
|
||||||
|
NfcApp* instance = context;
|
||||||
|
|
||||||
|
if(instance->protocol_support && instance->protocol_support->protocol != protocol) {
|
||||||
|
nfc_protocol_support_free(instance);
|
||||||
|
}
|
||||||
|
if(!instance->protocol_support) {
|
||||||
|
nfc_protocol_support_alloc(protocol, instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
return instance->protocol_support->base;
|
||||||
|
}
|
||||||
|
|
||||||
// Interface functions
|
// Interface functions
|
||||||
void nfc_protocol_support_on_enter(NfcProtocolSupportScene scene, void* context) {
|
void nfc_protocol_support_on_enter(NfcProtocolSupportScene scene, void* context) {
|
||||||
furi_assert(scene < NfcProtocolSupportSceneCount);
|
furi_assert(scene < NfcProtocolSupportSceneCount);
|
||||||
@@ -74,17 +219,23 @@ void nfc_protocol_support_on_exit(NfcProtocolSupportScene scene, void* context)
|
|||||||
nfc_protocol_support_scenes[scene].on_exit(instance);
|
nfc_protocol_support_scenes[scene].on_exit(instance);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool nfc_protocol_support_has_feature(NfcProtocol protocol, NfcProtocolFeature feature) {
|
bool nfc_protocol_support_has_feature(
|
||||||
return nfc_protocol_support[protocol]->features & feature;
|
NfcProtocol protocol,
|
||||||
|
void* context,
|
||||||
|
NfcProtocolFeature feature) {
|
||||||
|
furi_assert(context);
|
||||||
|
|
||||||
|
NfcApp* instance = context;
|
||||||
|
return nfc_protocol_support_get(protocol, instance)->features & feature;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Common scene handlers
|
// Common scene handlers
|
||||||
// SceneInfo
|
// SceneInfo
|
||||||
static void nfc_protocol_support_scene_info_on_enter(NfcApp* instance) {
|
static void nfc_protocol_support_scene_info_on_enter(NfcApp* instance) {
|
||||||
const NfcProtocol protocol = nfc_device_get_protocol(instance->nfc_device);
|
const NfcProtocol protocol = nfc_device_get_protocol(instance->nfc_device);
|
||||||
nfc_protocol_support[protocol]->scene_info.on_enter(instance);
|
nfc_protocol_support_get(protocol, instance)->scene_info.on_enter(instance);
|
||||||
|
|
||||||
if(nfc_protocol_support_has_feature(protocol, NfcProtocolFeatureMoreInfo)) {
|
if(nfc_protocol_support_has_feature(protocol, instance, NfcProtocolFeatureMoreInfo)) {
|
||||||
widget_add_button_element(
|
widget_add_button_element(
|
||||||
instance->widget,
|
instance->widget,
|
||||||
GuiButtonTypeRight,
|
GuiButtonTypeRight,
|
||||||
@@ -124,7 +275,7 @@ static void nfc_protocol_support_scene_info_on_exit(NfcApp* instance) {
|
|||||||
// SceneMoreInfo
|
// SceneMoreInfo
|
||||||
static void nfc_protocol_support_scene_more_info_on_enter(NfcApp* instance) {
|
static void nfc_protocol_support_scene_more_info_on_enter(NfcApp* instance) {
|
||||||
const NfcProtocol protocol = nfc_device_get_protocol(instance->nfc_device);
|
const NfcProtocol protocol = nfc_device_get_protocol(instance->nfc_device);
|
||||||
nfc_protocol_support[protocol]->scene_more_info.on_enter(instance);
|
nfc_protocol_support_get(protocol, instance)->scene_more_info.on_enter(instance);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
@@ -132,7 +283,8 @@ static bool
|
|||||||
bool consumed = false;
|
bool consumed = false;
|
||||||
|
|
||||||
const NfcProtocol protocol = nfc_device_get_protocol(instance->nfc_device);
|
const NfcProtocol protocol = nfc_device_get_protocol(instance->nfc_device);
|
||||||
consumed = nfc_protocol_support[protocol]->scene_more_info.on_event(instance, event);
|
consumed =
|
||||||
|
nfc_protocol_support_get(protocol, instance)->scene_more_info.on_event(instance, event);
|
||||||
|
|
||||||
return consumed;
|
return consumed;
|
||||||
}
|
}
|
||||||
@@ -157,7 +309,7 @@ static void nfc_protocol_support_scene_read_on_enter(NfcApp* instance) {
|
|||||||
//nfc_supported_cards_load_cache(instance->nfc_supported_cards);
|
//nfc_supported_cards_load_cache(instance->nfc_supported_cards);
|
||||||
|
|
||||||
// Start poller with the appropriate callback
|
// Start poller with the appropriate callback
|
||||||
nfc_protocol_support[protocol]->scene_read.on_enter(instance);
|
nfc_protocol_support_get(protocol, instance)->scene_read.on_enter(instance);
|
||||||
|
|
||||||
nfc_blink_read_start(instance);
|
nfc_blink_read_start(instance);
|
||||||
}
|
}
|
||||||
@@ -186,7 +338,8 @@ static bool nfc_protocol_support_scene_read_on_event(NfcApp* instance, SceneMana
|
|||||||
} else {
|
} else {
|
||||||
const NfcProtocol protocol =
|
const NfcProtocol protocol =
|
||||||
nfc_detected_protocols_get_selected(instance->detected_protocols);
|
nfc_detected_protocols_get_selected(instance->detected_protocols);
|
||||||
consumed = nfc_protocol_support[protocol]->scene_read.on_event(instance, event);
|
consumed = nfc_protocol_support_get(protocol, instance)
|
||||||
|
->scene_read.on_event(instance, event);
|
||||||
}
|
}
|
||||||
} else if(event.event == NfcCustomEventPollerFailure) {
|
} else if(event.event == NfcCustomEventPollerFailure) {
|
||||||
nfc_poller_stop(instance->poller);
|
nfc_poller_stop(instance->poller);
|
||||||
@@ -199,7 +352,8 @@ static bool nfc_protocol_support_scene_read_on_event(NfcApp* instance, SceneMana
|
|||||||
} else if(event.event == NfcCustomEventCardDetected) {
|
} else if(event.event == NfcCustomEventCardDetected) {
|
||||||
const NfcProtocol protocol =
|
const NfcProtocol protocol =
|
||||||
nfc_detected_protocols_get_selected(instance->detected_protocols);
|
nfc_detected_protocols_get_selected(instance->detected_protocols);
|
||||||
consumed = nfc_protocol_support[protocol]->scene_read.on_event(instance, event);
|
consumed =
|
||||||
|
nfc_protocol_support_get(protocol, instance)->scene_read.on_event(instance, event);
|
||||||
}
|
}
|
||||||
} else if(event.type == SceneManagerEventTypeBack) {
|
} else if(event.type == SceneManagerEventTypeBack) {
|
||||||
nfc_poller_stop(instance->poller);
|
nfc_poller_stop(instance->poller);
|
||||||
@@ -241,7 +395,7 @@ static void nfc_protocol_support_scene_read_menu_on_enter(NfcApp* instance) {
|
|||||||
instance);
|
instance);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(nfc_protocol_support_has_feature(protocol, NfcProtocolFeatureEmulateUid)) {
|
if(nfc_protocol_support_has_feature(protocol, instance, NfcProtocolFeatureEmulateUid)) {
|
||||||
submenu_add_item(
|
submenu_add_item(
|
||||||
submenu,
|
submenu,
|
||||||
"Emulate UID",
|
"Emulate UID",
|
||||||
@@ -249,7 +403,7 @@ static void nfc_protocol_support_scene_read_menu_on_enter(NfcApp* instance) {
|
|||||||
nfc_protocol_support_common_submenu_callback,
|
nfc_protocol_support_common_submenu_callback,
|
||||||
instance);
|
instance);
|
||||||
|
|
||||||
} else if(nfc_protocol_support_has_feature(protocol, NfcProtocolFeatureEmulateFull)) {
|
} else if(nfc_protocol_support_has_feature(protocol, instance, NfcProtocolFeatureEmulateFull)) {
|
||||||
submenu_add_item(
|
submenu_add_item(
|
||||||
submenu,
|
submenu,
|
||||||
"Emulate",
|
"Emulate",
|
||||||
@@ -258,7 +412,16 @@ static void nfc_protocol_support_scene_read_menu_on_enter(NfcApp* instance) {
|
|||||||
instance);
|
instance);
|
||||||
}
|
}
|
||||||
|
|
||||||
nfc_protocol_support[protocol]->scene_read_menu.on_enter(instance);
|
if(nfc_protocol_support_has_feature(protocol, instance, NfcProtocolFeatureWrite)) {
|
||||||
|
submenu_add_item(
|
||||||
|
submenu,
|
||||||
|
"Write",
|
||||||
|
SubmenuIndexCommonWrite,
|
||||||
|
nfc_protocol_support_common_submenu_callback,
|
||||||
|
instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
nfc_protocol_support_get(protocol, instance)->scene_read_menu.on_enter(instance);
|
||||||
|
|
||||||
submenu_add_item(
|
submenu_add_item(
|
||||||
submenu,
|
submenu,
|
||||||
@@ -291,9 +454,17 @@ static bool
|
|||||||
dolphin_deed(DolphinDeedNfcEmulate);
|
dolphin_deed(DolphinDeedNfcEmulate);
|
||||||
scene_manager_next_scene(instance->scene_manager, NfcSceneEmulate);
|
scene_manager_next_scene(instance->scene_manager, NfcSceneEmulate);
|
||||||
consumed = true;
|
consumed = true;
|
||||||
|
} else if(event.event == SubmenuIndexCommonWrite) {
|
||||||
|
dolphin_deed(DolphinDeedNfcEmulate);
|
||||||
|
scene_manager_next_scene(instance->scene_manager, NfcSceneWrite);
|
||||||
|
consumed = true;
|
||||||
|
} else if(event.event == SubmenuIndexCommonEdit) {
|
||||||
|
scene_manager_next_scene(instance->scene_manager, NfcSceneSetUid);
|
||||||
|
consumed = true;
|
||||||
} else {
|
} else {
|
||||||
const NfcProtocol protocol = nfc_device_get_protocol(instance->nfc_device);
|
const NfcProtocol protocol = nfc_device_get_protocol(instance->nfc_device);
|
||||||
consumed = nfc_protocol_support[protocol]->scene_read_menu.on_event(instance, event);
|
consumed = nfc_protocol_support_get(protocol, instance)
|
||||||
|
->scene_read_menu.on_event(instance, event);
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if(event.type == SceneManagerEventTypeBack) {
|
} else if(event.type == SceneManagerEventTypeBack) {
|
||||||
@@ -312,13 +483,17 @@ static void nfc_protocol_support_scene_read_saved_menu_on_exit(NfcApp* instance)
|
|||||||
static void nfc_protocol_support_scene_read_success_on_enter(NfcApp* instance) {
|
static void nfc_protocol_support_scene_read_success_on_enter(NfcApp* instance) {
|
||||||
Widget* widget = instance->widget;
|
Widget* widget = instance->widget;
|
||||||
|
|
||||||
|
popup_set_header(instance->popup, "Parsing", 85, 27, AlignCenter, AlignTop);
|
||||||
|
popup_set_icon(instance->popup, 12, 23, &A_Loading_24);
|
||||||
|
view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewPopup);
|
||||||
|
|
||||||
FuriString* temp_str = furi_string_alloc();
|
FuriString* temp_str = furi_string_alloc();
|
||||||
if(nfc_supported_cards_parse(instance->nfc_supported_cards, instance->nfc_device, temp_str)) {
|
if(nfc_supported_cards_parse(instance->nfc_supported_cards, instance->nfc_device, temp_str)) {
|
||||||
widget_add_text_scroll_element(
|
widget_add_text_scroll_element(
|
||||||
instance->widget, 0, 0, 128, 52, furi_string_get_cstr(temp_str));
|
instance->widget, 0, 0, 128, 52, furi_string_get_cstr(temp_str));
|
||||||
} else {
|
} else {
|
||||||
const NfcProtocol protocol = nfc_device_get_protocol(instance->nfc_device);
|
const NfcProtocol protocol = nfc_device_get_protocol(instance->nfc_device);
|
||||||
nfc_protocol_support[protocol]->scene_read_success.on_enter(instance);
|
nfc_protocol_support_get(protocol, instance)->scene_read_success.on_enter(instance);
|
||||||
}
|
}
|
||||||
|
|
||||||
furi_string_free(temp_str);
|
furi_string_free(temp_str);
|
||||||
@@ -366,7 +541,7 @@ static void nfc_protocol_support_scene_saved_menu_on_enter(NfcApp* instance) {
|
|||||||
Submenu* submenu = instance->submenu;
|
Submenu* submenu = instance->submenu;
|
||||||
|
|
||||||
// Header submenu items
|
// Header submenu items
|
||||||
if(nfc_protocol_support_has_feature(protocol, NfcProtocolFeatureEmulateUid)) {
|
if(nfc_protocol_support_has_feature(protocol, instance, NfcProtocolFeatureEmulateUid)) {
|
||||||
submenu_add_item(
|
submenu_add_item(
|
||||||
submenu,
|
submenu,
|
||||||
"Emulate UID",
|
"Emulate UID",
|
||||||
@@ -374,7 +549,7 @@ static void nfc_protocol_support_scene_saved_menu_on_enter(NfcApp* instance) {
|
|||||||
nfc_protocol_support_common_submenu_callback,
|
nfc_protocol_support_common_submenu_callback,
|
||||||
instance);
|
instance);
|
||||||
|
|
||||||
} else if(nfc_protocol_support_has_feature(protocol, NfcProtocolFeatureEmulateFull)) {
|
} else if(nfc_protocol_support_has_feature(protocol, instance, NfcProtocolFeatureEmulateFull)) {
|
||||||
submenu_add_item(
|
submenu_add_item(
|
||||||
submenu,
|
submenu,
|
||||||
"Emulate",
|
"Emulate",
|
||||||
@@ -383,7 +558,16 @@ static void nfc_protocol_support_scene_saved_menu_on_enter(NfcApp* instance) {
|
|||||||
instance);
|
instance);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(nfc_protocol_support_has_feature(protocol, NfcProtocolFeatureEditUid)) {
|
if(nfc_protocol_support_has_feature(protocol, instance, NfcProtocolFeatureWrite)) {
|
||||||
|
submenu_add_item(
|
||||||
|
submenu,
|
||||||
|
"Write",
|
||||||
|
SubmenuIndexCommonWrite,
|
||||||
|
nfc_protocol_support_common_submenu_callback,
|
||||||
|
instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(nfc_protocol_support_has_feature(protocol, instance, NfcProtocolFeatureEditUid)) {
|
||||||
submenu_add_item(
|
submenu_add_item(
|
||||||
submenu,
|
submenu,
|
||||||
"Edit UID",
|
"Edit UID",
|
||||||
@@ -393,7 +577,7 @@ static void nfc_protocol_support_scene_saved_menu_on_enter(NfcApp* instance) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Protocol-dependent menu items
|
// Protocol-dependent menu items
|
||||||
nfc_protocol_support[protocol]->scene_saved_menu.on_enter(instance);
|
nfc_protocol_support_get(protocol, instance)->scene_saved_menu.on_enter(instance);
|
||||||
|
|
||||||
// Trailer submenu items
|
// Trailer submenu items
|
||||||
if(nfc_has_shadow_file(instance)) {
|
if(nfc_has_shadow_file(instance)) {
|
||||||
@@ -456,12 +640,19 @@ static bool
|
|||||||
dolphin_deed(is_added ? DolphinDeedNfcAddEmulate : DolphinDeedNfcEmulate);
|
dolphin_deed(is_added ? DolphinDeedNfcAddEmulate : DolphinDeedNfcEmulate);
|
||||||
scene_manager_next_scene(instance->scene_manager, NfcSceneEmulate);
|
scene_manager_next_scene(instance->scene_manager, NfcSceneEmulate);
|
||||||
consumed = true;
|
consumed = true;
|
||||||
|
} else if(event.event == SubmenuIndexCommonWrite) {
|
||||||
|
const bool is_added =
|
||||||
|
scene_manager_has_previous_scene(instance->scene_manager, NfcSceneSetType);
|
||||||
|
dolphin_deed(is_added ? DolphinDeedNfcAddEmulate : DolphinDeedNfcEmulate);
|
||||||
|
scene_manager_next_scene(instance->scene_manager, NfcSceneWrite);
|
||||||
|
consumed = true;
|
||||||
} else if(event.event == SubmenuIndexCommonEdit) {
|
} else if(event.event == SubmenuIndexCommonEdit) {
|
||||||
scene_manager_next_scene(instance->scene_manager, NfcSceneSetUid);
|
scene_manager_next_scene(instance->scene_manager, NfcSceneSetUid);
|
||||||
consumed = true;
|
consumed = true;
|
||||||
} else {
|
} else {
|
||||||
const NfcProtocol protocol = nfc_device_get_protocol(instance->nfc_device);
|
const NfcProtocol protocol = nfc_device_get_protocol(instance->nfc_device);
|
||||||
consumed = nfc_protocol_support[protocol]->scene_saved_menu.on_event(instance, event);
|
consumed = nfc_protocol_support_get(protocol, instance)
|
||||||
|
->scene_saved_menu.on_event(instance, event);
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if(event.type == SceneManagerEventTypeBack) {
|
} else if(event.type == SceneManagerEventTypeBack) {
|
||||||
@@ -480,8 +671,18 @@ static void nfc_protocol_support_scene_save_name_on_enter(NfcApp* instance) {
|
|||||||
bool name_is_empty = furi_string_empty(instance->file_name);
|
bool name_is_empty = furi_string_empty(instance->file_name);
|
||||||
if(name_is_empty) {
|
if(name_is_empty) {
|
||||||
furi_string_set(instance->file_path, NFC_APP_FOLDER);
|
furi_string_set(instance->file_path, NFC_APP_FOLDER);
|
||||||
name_generator_make_auto_basic(
|
FuriString* prefix = furi_string_alloc();
|
||||||
instance->text_store, NFC_TEXT_STORE_SIZE, NFC_APP_FILENAME_PREFIX);
|
furi_string_set(prefix, nfc_device_get_name(instance->nfc_device, NfcDeviceNameTypeFull));
|
||||||
|
furi_string_replace(prefix, "Mifare", "MF");
|
||||||
|
furi_string_replace(prefix, " Classic", "C"); // MFC
|
||||||
|
furi_string_replace(prefix, "Desfire", "Des"); // MF Des
|
||||||
|
furi_string_replace(prefix, "Ultralight", "UL"); // MF UL
|
||||||
|
furi_string_replace(prefix, " Plus", "+"); // NTAG I2C+
|
||||||
|
furi_string_replace(prefix, " (Unknown)", "");
|
||||||
|
furi_string_replace_all(prefix, " ", "_");
|
||||||
|
name_generator_make_auto(
|
||||||
|
instance->text_store, NFC_TEXT_STORE_SIZE, furi_string_get_cstr(prefix));
|
||||||
|
furi_string_free(prefix);
|
||||||
furi_string_set(folder_path, NFC_APP_FOLDER);
|
furi_string_set(folder_path, NFC_APP_FOLDER);
|
||||||
} else {
|
} else {
|
||||||
nfc_text_store_set(instance, "%s", furi_string_get_cstr(instance->file_name));
|
nfc_text_store_set(instance, "%s", furi_string_get_cstr(instance->file_name));
|
||||||
@@ -527,8 +728,8 @@ static bool
|
|||||||
DolphinDeedNfcSave);
|
DolphinDeedNfcSave);
|
||||||
|
|
||||||
const NfcProtocol protocol = nfc_device_get_protocol(instance->nfc_device);
|
const NfcProtocol protocol = nfc_device_get_protocol(instance->nfc_device);
|
||||||
consumed =
|
consumed = nfc_protocol_support_get(protocol, instance)
|
||||||
nfc_protocol_support[protocol]->scene_save_name.on_event(instance, event);
|
->scene_save_name.on_event(instance, event);
|
||||||
} else {
|
} else {
|
||||||
consumed = scene_manager_search_and_switch_to_previous_scene(
|
consumed = scene_manager_search_and_switch_to_previous_scene(
|
||||||
instance->scene_manager, NfcSceneStart);
|
instance->scene_manager, NfcSceneStart);
|
||||||
@@ -570,9 +771,9 @@ static void nfc_protocol_support_scene_emulate_on_enter(NfcApp* instance) {
|
|||||||
FuriString* temp_str = furi_string_alloc();
|
FuriString* temp_str = furi_string_alloc();
|
||||||
const NfcProtocol protocol = nfc_device_get_protocol(instance->nfc_device);
|
const NfcProtocol protocol = nfc_device_get_protocol(instance->nfc_device);
|
||||||
|
|
||||||
widget_add_icon_element(widget, 0, 3, &I_NFC_dolphin_emulation_51x64);
|
widget_add_icon_element(widget, 0, 0, &I_NFC_dolphin_emulation_51x64);
|
||||||
|
|
||||||
if(nfc_protocol_support_has_feature(protocol, NfcProtocolFeatureEmulateUid)) {
|
if(nfc_protocol_support_has_feature(protocol, instance, NfcProtocolFeatureEmulateUid)) {
|
||||||
widget_add_string_element(
|
widget_add_string_element(
|
||||||
widget, 90, 26, AlignCenter, AlignCenter, FontPrimary, "Emulating UID");
|
widget, 90, 26, AlignCenter, AlignCenter, FontPrimary, "Emulating UID");
|
||||||
|
|
||||||
@@ -613,7 +814,7 @@ static void nfc_protocol_support_scene_emulate_on_enter(NfcApp* instance) {
|
|||||||
furi_string_reset(instance->text_box_store);
|
furi_string_reset(instance->text_box_store);
|
||||||
|
|
||||||
// instance->listener is allocated in the respective on_enter() handler
|
// instance->listener is allocated in the respective on_enter() handler
|
||||||
nfc_protocol_support[protocol]->scene_emulate.on_enter(instance);
|
nfc_protocol_support_get(protocol, instance)->scene_emulate.on_enter(instance);
|
||||||
|
|
||||||
scene_manager_set_scene_state(
|
scene_manager_set_scene_state(
|
||||||
instance->scene_manager, NfcSceneEmulate, NfcSceneEmulateStateWidget);
|
instance->scene_manager, NfcSceneEmulate, NfcSceneEmulateStateWidget);
|
||||||
@@ -692,6 +893,191 @@ static void nfc_protocol_support_scene_emulate_on_exit(NfcApp* instance) {
|
|||||||
nfc_blink_stop(instance);
|
nfc_blink_stop(instance);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SceneWrite
|
||||||
|
/**
|
||||||
|
* @brief Current view displayed on the write scene.
|
||||||
|
*
|
||||||
|
* The emulation scene has five states, some protocols may not use all states.
|
||||||
|
* Protocol handles poller events, when scene state needs to change it should
|
||||||
|
* fill text_box_store with a short caption (when applicable) before sending
|
||||||
|
* the relevant view dispatcher event.
|
||||||
|
*/
|
||||||
|
enum {
|
||||||
|
NfcSceneWriteStateSearching, /**< Ask user to touch the card. Event: on_enter, CardLost. Needs caption. */
|
||||||
|
NfcSceneWriteStateWriting, /**< Ask not to move while writing. Event: CardDetected. No caption. */
|
||||||
|
NfcSceneWriteStateSuccess, /**< Card written successfully. Event: PollerSuccess. No caption. */
|
||||||
|
NfcSceneWriteStateFailure, /**< An error is displayed. Event: PollerFailure. Needs caption. */
|
||||||
|
NfcSceneWriteStateWrongCard, /**< Wrong card was presented. Event: WrongCard. Needs caption. */
|
||||||
|
};
|
||||||
|
|
||||||
|
static void nfc_protocol_support_scene_write_popup_callback(void* context) {
|
||||||
|
NfcApp* instance = context;
|
||||||
|
view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventViewExit);
|
||||||
|
}
|
||||||
|
|
||||||
|
void nfc_protocol_support_scene_write_widget_callback(
|
||||||
|
GuiButtonType result,
|
||||||
|
InputType type,
|
||||||
|
void* context) {
|
||||||
|
NfcApp* instance = context;
|
||||||
|
if(type == InputTypeShort && result == GuiButtonTypeLeft) {
|
||||||
|
view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventRetry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void nfc_protocol_support_scene_write_setup_view(NfcApp* instance) {
|
||||||
|
Popup* popup = instance->popup;
|
||||||
|
Widget* widget = instance->widget;
|
||||||
|
popup_reset(popup);
|
||||||
|
widget_reset(widget);
|
||||||
|
uint32_t state = scene_manager_get_scene_state(instance->scene_manager, NfcSceneWrite);
|
||||||
|
NfcView view = NfcViewPopup;
|
||||||
|
|
||||||
|
if(state == NfcSceneWriteStateSearching) {
|
||||||
|
popup_set_header(popup, "Writing", 95, 20, AlignCenter, AlignCenter);
|
||||||
|
popup_set_text(
|
||||||
|
popup,
|
||||||
|
furi_string_get_cstr(instance->text_box_store),
|
||||||
|
95,
|
||||||
|
38,
|
||||||
|
AlignCenter,
|
||||||
|
AlignCenter);
|
||||||
|
popup_set_icon(popup, 0, 8, &I_NFC_manual_60x50);
|
||||||
|
} else if(state == NfcSceneWriteStateWriting) {
|
||||||
|
popup_set_header(popup, "Writing\nDon't move...", 52, 32, AlignLeft, AlignCenter);
|
||||||
|
popup_set_icon(popup, 12, 23, &A_Loading_24);
|
||||||
|
} else if(state == NfcSceneWriteStateSuccess) {
|
||||||
|
popup_set_header(popup, "Successfully\nwritten!", 126, 2, AlignRight, AlignTop);
|
||||||
|
popup_set_icon(popup, 0, 9, &I_DolphinSuccess_91x55);
|
||||||
|
popup_set_timeout(popup, 1500);
|
||||||
|
popup_set_context(popup, instance);
|
||||||
|
popup_set_callback(popup, nfc_protocol_support_scene_write_popup_callback);
|
||||||
|
popup_enable_timeout(popup);
|
||||||
|
} else if(state == NfcSceneWriteStateFailure) {
|
||||||
|
view = NfcViewWidget;
|
||||||
|
widget_add_string_element(
|
||||||
|
widget, 7, 4, AlignLeft, AlignTop, FontPrimary, "Writing gone wrong!");
|
||||||
|
widget_add_string_multiline_element(
|
||||||
|
widget,
|
||||||
|
7,
|
||||||
|
17,
|
||||||
|
AlignLeft,
|
||||||
|
AlignTop,
|
||||||
|
FontSecondary,
|
||||||
|
furi_string_get_cstr(instance->text_box_store));
|
||||||
|
widget_add_icon_element(widget, 83, 22, &I_WarningDolphinFlip_45x42);
|
||||||
|
widget_add_button_element(
|
||||||
|
widget,
|
||||||
|
GuiButtonTypeLeft,
|
||||||
|
"Retry",
|
||||||
|
nfc_protocol_support_scene_write_widget_callback,
|
||||||
|
instance);
|
||||||
|
} else if(state == NfcSceneWriteStateWrongCard) {
|
||||||
|
view = NfcViewWidget;
|
||||||
|
widget_add_string_element(widget, 3, 4, AlignLeft, AlignTop, FontPrimary, "Wrong card!");
|
||||||
|
widget_add_string_multiline_element(
|
||||||
|
widget,
|
||||||
|
4,
|
||||||
|
17,
|
||||||
|
AlignLeft,
|
||||||
|
AlignTop,
|
||||||
|
FontSecondary,
|
||||||
|
furi_string_get_cstr(instance->text_box_store));
|
||||||
|
widget_add_icon_element(widget, 83, 22, &I_WarningDolphinFlip_45x42);
|
||||||
|
widget_add_button_element(
|
||||||
|
widget,
|
||||||
|
GuiButtonTypeLeft,
|
||||||
|
"Retry",
|
||||||
|
nfc_protocol_support_scene_write_widget_callback,
|
||||||
|
instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
view_dispatcher_switch_to_view(instance->view_dispatcher, view);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void nfc_protocol_support_scene_write_on_enter(NfcApp* instance) {
|
||||||
|
scene_manager_set_scene_state(
|
||||||
|
instance->scene_manager, NfcSceneWrite, NfcSceneWriteStateSearching);
|
||||||
|
furi_string_reset(instance->text_box_store);
|
||||||
|
|
||||||
|
const NfcProtocol protocol = nfc_device_get_protocol(instance->nfc_device);
|
||||||
|
|
||||||
|
// instance->poller is allocated in the respective on_enter() handler
|
||||||
|
nfc_protocol_support_get(protocol, instance)->scene_write.on_enter(instance);
|
||||||
|
|
||||||
|
nfc_protocol_support_scene_write_setup_view(instance);
|
||||||
|
nfc_blink_emulate_start(instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool nfc_protocol_support_scene_write_on_event(NfcApp* instance, SceneManagerEvent event) {
|
||||||
|
bool consumed = false;
|
||||||
|
|
||||||
|
if(event.type == SceneManagerEventTypeCustom) {
|
||||||
|
uint32_t new_state = -1;
|
||||||
|
bool stop_poller = false;
|
||||||
|
|
||||||
|
if(event.event == NfcCustomEventCardDetected) {
|
||||||
|
new_state = NfcSceneWriteStateWriting;
|
||||||
|
consumed = true;
|
||||||
|
} else if(event.event == NfcCustomEventCardLost) {
|
||||||
|
new_state = NfcSceneWriteStateSearching;
|
||||||
|
consumed = true;
|
||||||
|
} else if(event.event == NfcCustomEventPollerSuccess) {
|
||||||
|
dolphin_deed(DolphinDeedNfcSave);
|
||||||
|
notification_message(instance->notifications, &sequence_success);
|
||||||
|
new_state = NfcSceneWriteStateSuccess;
|
||||||
|
stop_poller = true;
|
||||||
|
consumed = true;
|
||||||
|
} else if(event.event == NfcCustomEventPollerFailure) {
|
||||||
|
notification_message(instance->notifications, &sequence_error);
|
||||||
|
new_state = NfcSceneWriteStateFailure;
|
||||||
|
stop_poller = true;
|
||||||
|
consumed = true;
|
||||||
|
} else if(event.event == NfcCustomEventWrongCard) {
|
||||||
|
notification_message(instance->notifications, &sequence_error);
|
||||||
|
new_state = NfcSceneWriteStateWrongCard;
|
||||||
|
stop_poller = true;
|
||||||
|
consumed = true;
|
||||||
|
} else if(event.event == NfcCustomEventViewExit) {
|
||||||
|
scene_manager_previous_scene(instance->scene_manager);
|
||||||
|
consumed = true;
|
||||||
|
} else if(event.event == NfcCustomEventRetry) {
|
||||||
|
nfc_protocol_support_scenes[NfcProtocolSupportSceneWrite].on_exit(instance);
|
||||||
|
nfc_protocol_support_scenes[NfcProtocolSupportSceneWrite].on_enter(instance);
|
||||||
|
consumed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(stop_poller) {
|
||||||
|
if(instance->poller) {
|
||||||
|
nfc_poller_stop(instance->poller);
|
||||||
|
nfc_poller_free(instance->poller);
|
||||||
|
instance->poller = NULL;
|
||||||
|
}
|
||||||
|
nfc_blink_stop(instance);
|
||||||
|
}
|
||||||
|
if(new_state != (uint32_t)-1) {
|
||||||
|
scene_manager_set_scene_state(instance->scene_manager, NfcSceneWrite, new_state);
|
||||||
|
nfc_protocol_support_scene_write_setup_view(instance);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return consumed;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void nfc_protocol_support_scene_write_on_exit(NfcApp* instance) {
|
||||||
|
if(instance->poller) {
|
||||||
|
nfc_poller_stop(instance->poller);
|
||||||
|
nfc_poller_free(instance->poller);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear view
|
||||||
|
popup_reset(instance->popup);
|
||||||
|
widget_reset(instance->widget);
|
||||||
|
furi_string_reset(instance->text_box_store);
|
||||||
|
|
||||||
|
nfc_blink_stop(instance);
|
||||||
|
}
|
||||||
|
|
||||||
static void nfc_protocol_support_scene_rpc_on_enter(NfcApp* instance) {
|
static void nfc_protocol_support_scene_rpc_on_enter(NfcApp* instance) {
|
||||||
UNUSED(instance);
|
UNUSED(instance);
|
||||||
}
|
}
|
||||||
@@ -709,7 +1095,7 @@ static void nfc_protocol_support_scene_rpc_setup_ui_and_emulate(NfcApp* instance
|
|||||||
nfc_blink_emulate_start(instance);
|
nfc_blink_emulate_start(instance);
|
||||||
|
|
||||||
const NfcProtocol protocol = nfc_device_get_protocol(instance->nfc_device);
|
const NfcProtocol protocol = nfc_device_get_protocol(instance->nfc_device);
|
||||||
nfc_protocol_support[protocol]->scene_emulate.on_enter(instance);
|
nfc_protocol_support_get(protocol, instance)->scene_emulate.on_enter(instance);
|
||||||
|
|
||||||
instance->rpc_state = NfcRpcStateEmulating;
|
instance->rpc_state = NfcRpcStateEmulating;
|
||||||
}
|
}
|
||||||
@@ -807,6 +1193,12 @@ static const NfcProtocolSupportCommonSceneBase
|
|||||||
.on_event = nfc_protocol_support_scene_emulate_on_event,
|
.on_event = nfc_protocol_support_scene_emulate_on_event,
|
||||||
.on_exit = nfc_protocol_support_scene_emulate_on_exit,
|
.on_exit = nfc_protocol_support_scene_emulate_on_exit,
|
||||||
},
|
},
|
||||||
|
[NfcProtocolSupportSceneWrite] =
|
||||||
|
{
|
||||||
|
.on_enter = nfc_protocol_support_scene_write_on_enter,
|
||||||
|
.on_event = nfc_protocol_support_scene_write_on_event,
|
||||||
|
.on_exit = nfc_protocol_support_scene_write_on_exit,
|
||||||
|
},
|
||||||
[NfcProtocolSupportSceneRpc] =
|
[NfcProtocolSupportSceneRpc] =
|
||||||
{
|
{
|
||||||
.on_enter = nfc_protocol_support_scene_rpc_on_enter,
|
.on_enter = nfc_protocol_support_scene_rpc_on_enter,
|
||||||
|
|||||||
@@ -40,7 +40,7 @@
|
|||||||
*
|
*
|
||||||
* | Filename | Explanation |
|
* | Filename | Explanation |
|
||||||
* |:-----------------------|:------------|
|
* |:-----------------------|:------------|
|
||||||
* | protocol_name.h | Interface structure declaration used in `nfc_protocol_support_defs.c`. |
|
* | protocol_name.h | Interface structure declaration. |
|
||||||
* | protocol_name.c | Protocol-specific scene implemenatations and definitions. |
|
* | protocol_name.c | Protocol-specific scene implemenatations and definitions. |
|
||||||
* | protocol_name_render.h | Protocol-specific rendering (formatting) functions. Used for converting protocol data into textual descriptions. |
|
* | protocol_name_render.h | Protocol-specific rendering (formatting) functions. Used for converting protocol data into textual descriptions. |
|
||||||
* | protocol_name_render.c | Implementations for functions declared in `protocol_name_render.h`.|
|
* | protocol_name_render.c | Implementations for functions declared in `protocol_name_render.h`.|
|
||||||
@@ -65,8 +65,13 @@
|
|||||||
*
|
*
|
||||||
* After completing the protocol support, it must be registered within the application in order for it to be usable.
|
* After completing the protocol support, it must be registered within the application in order for it to be usable.
|
||||||
*
|
*
|
||||||
* In nfc_protocol_support_defs.c, include the `protocol_name.h` file and add a new entry in the `nfc_protocol_support[]`
|
* In `protocol_name.c`, add `NFC_PROTOCOL_SUPPORT_PLUGIN(protocol_name, NfcProtocolName)` at the bottom,
|
||||||
* array under the appropriate index.
|
* below the `NfcProtocolSupportBase` structure definition.
|
||||||
|
*
|
||||||
|
* In `application.fam`, add a new entry for the plugin, following the other examples.
|
||||||
|
*
|
||||||
|
* In nfc_protocol_support.c, add a new entry in the `nfc_protocol_support_plugin_names[]`
|
||||||
|
* array under the appropriate index with the name of the plugin (without the `nfc_` prefix).
|
||||||
*
|
*
|
||||||
* ## Done!
|
* ## Done!
|
||||||
*
|
*
|
||||||
@@ -80,6 +85,10 @@
|
|||||||
|
|
||||||
#include "nfc_protocol_support_common.h"
|
#include "nfc_protocol_support_common.h"
|
||||||
|
|
||||||
|
typedef struct NfcProtocolSupport NfcProtocolSupport;
|
||||||
|
|
||||||
|
void nfc_protocol_support_free(void* context);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Abstract interface for on_enter() scene handler.
|
* @brief Abstract interface for on_enter() scene handler.
|
||||||
*
|
*
|
||||||
@@ -113,4 +122,7 @@ bool nfc_protocol_support_on_event(
|
|||||||
*/
|
*/
|
||||||
void nfc_protocol_support_on_exit(NfcProtocolSupportScene scene, void* context);
|
void nfc_protocol_support_on_exit(NfcProtocolSupportScene scene, void* context);
|
||||||
|
|
||||||
bool nfc_protocol_support_has_feature(NfcProtocol protocol, NfcProtocolFeature feature);
|
bool nfc_protocol_support_has_feature(
|
||||||
|
NfcProtocol protocol,
|
||||||
|
void* context,
|
||||||
|
NfcProtocolFeature feature);
|
||||||
|
|||||||
@@ -9,6 +9,8 @@
|
|||||||
#include "../../nfc_app.h"
|
#include "../../nfc_app.h"
|
||||||
#include "../../nfc_app_i.h"
|
#include "../../nfc_app_i.h"
|
||||||
|
|
||||||
|
#include <lib/flipper_application/flipper_application.h>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Scene entry handler.
|
* @brief Scene entry handler.
|
||||||
*
|
*
|
||||||
@@ -114,4 +116,47 @@ typedef struct {
|
|||||||
* It is responsible for creating a listener and for handling its events.
|
* It is responsible for creating a listener and for handling its events.
|
||||||
*/
|
*/
|
||||||
NfcProtocolSupportSceneBase scene_emulate;
|
NfcProtocolSupportSceneBase scene_emulate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Handlers for protocol-specific write scene.
|
||||||
|
*
|
||||||
|
* This scene is activated when a write operation is in progress.
|
||||||
|
* It is responsible for creating a poller, handling its events and
|
||||||
|
* displaying short captions for what is happening.
|
||||||
|
*/
|
||||||
|
NfcProtocolSupportSceneBase scene_write;
|
||||||
} NfcProtocolSupportBase;
|
} NfcProtocolSupportBase;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Unique string identifier for protocol support plugins.
|
||||||
|
*/
|
||||||
|
#define NFC_PROTOCOL_SUPPORT_PLUGIN_APP_ID "NfcProtocolSupportPlugin"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Currently supported plugin API version.
|
||||||
|
*/
|
||||||
|
#define NFC_PROTOCOL_SUPPORT_PLUGIN_API_VERSION 1
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Protocol support plugin interface.
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
NfcProtocol protocol; /**< Identifier of the protocol this plugin implements. */
|
||||||
|
const NfcProtocolSupportBase* base; /**< Pointer to the protocol support interface. */
|
||||||
|
} NfcProtocolSupportPlugin;
|
||||||
|
|
||||||
|
#define NFC_PROTOCOL_SUPPORT_PLUGIN(name, protocol) \
|
||||||
|
static const NfcProtocolSupportPlugin nfc_protocol_support_##name##_desc = { \
|
||||||
|
protocol, \
|
||||||
|
&nfc_protocol_support_##name, \
|
||||||
|
}; \
|
||||||
|
\
|
||||||
|
static const FlipperAppPluginDescriptor plugin_descriptor_##name = { \
|
||||||
|
.appid = NFC_PROTOCOL_SUPPORT_PLUGIN_APP_ID, \
|
||||||
|
.ep_api_version = NFC_PROTOCOL_SUPPORT_PLUGIN_API_VERSION, \
|
||||||
|
.entry_point = &nfc_protocol_support_##name##_desc, \
|
||||||
|
}; \
|
||||||
|
\
|
||||||
|
const FlipperAppPluginDescriptor* nfc_##name##_ep(void) { \
|
||||||
|
return &plugin_descriptor_##name; \
|
||||||
|
}
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ typedef enum {
|
|||||||
NfcProtocolFeatureEmulateFull = 1UL << 1, /**< Complete emulation is supported. */
|
NfcProtocolFeatureEmulateFull = 1UL << 1, /**< Complete emulation is supported. */
|
||||||
NfcProtocolFeatureEditUid = 1UL << 2, /**< UID editing is supported. */
|
NfcProtocolFeatureEditUid = 1UL << 2, /**< UID editing is supported. */
|
||||||
NfcProtocolFeatureMoreInfo = 1UL << 3, /**< More information is provided. */
|
NfcProtocolFeatureMoreInfo = 1UL << 3, /**< More information is provided. */
|
||||||
|
NfcProtocolFeatureWrite = 1UL << 4, /**< Writing to real card is supported. */
|
||||||
} NfcProtocolFeature;
|
} NfcProtocolFeature;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -30,6 +31,7 @@ typedef enum {
|
|||||||
NfcProtocolSupportSceneSavedMenu, /**< Menu for the card that was loaded from file. */
|
NfcProtocolSupportSceneSavedMenu, /**< Menu for the card that was loaded from file. */
|
||||||
NfcProtocolSupportSceneSaveName, /**< Shown when saving or renaming a file. */
|
NfcProtocolSupportSceneSaveName, /**< Shown when saving or renaming a file. */
|
||||||
NfcProtocolSupportSceneEmulate, /**< Shown when emulating a card. */
|
NfcProtocolSupportSceneEmulate, /**< Shown when emulating a card. */
|
||||||
|
NfcProtocolSupportSceneWrite, /**< Shown when writing to a card. */
|
||||||
NfcProtocolSupportSceneRpc, /**< Shown in remote-controlled (RPC) mode. */
|
NfcProtocolSupportSceneRpc, /**< Shown in remote-controlled (RPC) mode. */
|
||||||
|
|
||||||
NfcProtocolSupportSceneCount, /**< Special value equal to total scene count. Internal use. */
|
NfcProtocolSupportSceneCount, /**< Special value equal to total scene count. Internal use. */
|
||||||
|
|||||||
@@ -1,49 +0,0 @@
|
|||||||
/**
|
|
||||||
* @file nfc_protocol_support_defs.c
|
|
||||||
* @brief Application-level protocol support definitions.
|
|
||||||
*
|
|
||||||
* This file is to be modified whenever support for
|
|
||||||
* a new protocol is to be added.
|
|
||||||
*/
|
|
||||||
#include "nfc_protocol_support_base.h"
|
|
||||||
|
|
||||||
#include <nfc/protocols/nfc_protocol.h>
|
|
||||||
|
|
||||||
#include "iso14443_3a/iso14443_3a.h"
|
|
||||||
#include "iso14443_3b/iso14443_3b.h"
|
|
||||||
#include "iso14443_4a/iso14443_4a.h"
|
|
||||||
#include "iso14443_4b/iso14443_4b.h"
|
|
||||||
#include "iso15693_3/iso15693_3.h"
|
|
||||||
#include "felica/felica.h"
|
|
||||||
#include "mf_ultralight/mf_ultralight.h"
|
|
||||||
#include "mf_classic/mf_classic.h"
|
|
||||||
#include "mf_plus/mf_plus.h"
|
|
||||||
#include "mf_desfire/mf_desfire.h"
|
|
||||||
#include "emv/emv.h"
|
|
||||||
#include "slix/slix.h"
|
|
||||||
#include "st25tb/st25tb.h"
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Array of pointers to concrete protocol support implementations.
|
|
||||||
*
|
|
||||||
* When adding support for a new protocol, add it to the end of this array
|
|
||||||
* under its respective index.
|
|
||||||
*
|
|
||||||
* @see nfc_protocol.h
|
|
||||||
*/
|
|
||||||
const NfcProtocolSupportBase* nfc_protocol_support[NfcProtocolNum] = {
|
|
||||||
[NfcProtocolIso14443_3a] = &nfc_protocol_support_iso14443_3a,
|
|
||||||
[NfcProtocolIso14443_3b] = &nfc_protocol_support_iso14443_3b,
|
|
||||||
[NfcProtocolIso14443_4a] = &nfc_protocol_support_iso14443_4a,
|
|
||||||
[NfcProtocolIso14443_4b] = &nfc_protocol_support_iso14443_4b,
|
|
||||||
[NfcProtocolIso15693_3] = &nfc_protocol_support_iso15693_3,
|
|
||||||
[NfcProtocolFelica] = &nfc_protocol_support_felica,
|
|
||||||
[NfcProtocolMfUltralight] = &nfc_protocol_support_mf_ultralight,
|
|
||||||
[NfcProtocolMfClassic] = &nfc_protocol_support_mf_classic,
|
|
||||||
[NfcProtocolMfPlus] = &nfc_protocol_support_mf_plus,
|
|
||||||
[NfcProtocolMfDesfire] = &nfc_protocol_support_mf_desfire,
|
|
||||||
[NfcProtocolSlix] = &nfc_protocol_support_slix,
|
|
||||||
[NfcProtocolSt25tb] = &nfc_protocol_support_st25tb,
|
|
||||||
[NfcProtocolEmv] = &nfc_protocol_support_emv,
|
|
||||||
/* Add new protocol support implementations here */
|
|
||||||
};
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
/**
|
|
||||||
* @file nfc_protocol_support_defs.h
|
|
||||||
* @brief Application-level protocol support declarations.
|
|
||||||
*/
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "nfc_protocol_support_base.h"
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Declaraion of array of pointers to protocol support implementations.
|
|
||||||
*/
|
|
||||||
extern const NfcProtocolSupportBase* nfc_protocol_support[];
|
|
||||||
@@ -26,9 +26,9 @@ void nfc_protocol_support_common_byte_input_done_callback(void* context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void nfc_protocol_support_common_text_input_done_callback(void* context) {
|
void nfc_protocol_support_common_text_input_done_callback(void* context) {
|
||||||
NfcApp* nfc = context;
|
NfcApp* instance = context;
|
||||||
|
|
||||||
view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventTextInputDone);
|
view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventTextInputDone);
|
||||||
}
|
}
|
||||||
|
|
||||||
void nfc_protocol_support_common_on_enter_empty(NfcApp* instance) {
|
void nfc_protocol_support_common_on_enter_empty(NfcApp* instance) {
|
||||||
|
|||||||
@@ -15,6 +15,7 @@
|
|||||||
enum {
|
enum {
|
||||||
SubmenuIndexCommonSave, /**< Save menu option. */
|
SubmenuIndexCommonSave, /**< Save menu option. */
|
||||||
SubmenuIndexCommonEmulate, /**< Emulate menu option. */
|
SubmenuIndexCommonEmulate, /**< Emulate menu option. */
|
||||||
|
SubmenuIndexCommonWrite, /**< Write menu option. */
|
||||||
SubmenuIndexCommonEdit, /**< Edit menu option. */
|
SubmenuIndexCommonEdit, /**< Edit menu option. */
|
||||||
SubmenuIndexCommonInfo, /**< Info menu option. */
|
SubmenuIndexCommonInfo, /**< Info menu option. */
|
||||||
SubmenuIndexCommonRename, /**< Rename menu option. */
|
SubmenuIndexCommonRename, /**< Rename menu option. */
|
||||||
@@ -23,6 +24,10 @@ enum {
|
|||||||
SubmenuIndexCommonMax, /**< Special value, internal use. */
|
SubmenuIndexCommonMax, /**< Special value, internal use. */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Common submenu callback.
|
* @brief Common submenu callback.
|
||||||
*
|
*
|
||||||
@@ -84,3 +89,7 @@ void nfc_protocol_support_common_on_enter_empty(NfcApp* instance);
|
|||||||
* @returns always true.
|
* @returns always true.
|
||||||
*/
|
*/
|
||||||
bool nfc_protocol_support_common_on_event_empty(NfcApp* instance, SceneManagerEvent event);
|
bool nfc_protocol_support_common_on_event_empty(NfcApp* instance, SceneManagerEvent event);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|||||||
@@ -5,5 +5,13 @@ typedef enum {
|
|||||||
NfcSceneReadMenuStateCardFound,
|
NfcSceneReadMenuStateCardFound,
|
||||||
} NfcSceneUnlockReadState;
|
} NfcSceneUnlockReadState;
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
void nfc_unlock_helper_setup_from_state(NfcApp* instance);
|
void nfc_unlock_helper_setup_from_state(NfcApp* instance);
|
||||||
void nfc_unlock_helper_card_detected_handler(NfcApp* instance);
|
void nfc_unlock_helper_card_detected_handler(NfcApp* instance);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|||||||
140
applications/main/nfc/helpers/protocol_support/ntag4xx/ntag4xx.c
Normal file
140
applications/main/nfc/helpers/protocol_support/ntag4xx/ntag4xx.c
Normal file
@@ -0,0 +1,140 @@
|
|||||||
|
#include "ntag4xx.h"
|
||||||
|
#include "ntag4xx_render.h"
|
||||||
|
|
||||||
|
#include <nfc/protocols/ntag4xx/ntag4xx_poller.h>
|
||||||
|
|
||||||
|
#include "nfc/nfc_app_i.h"
|
||||||
|
|
||||||
|
#include "../nfc_protocol_support_common.h"
|
||||||
|
#include "../nfc_protocol_support_gui_common.h"
|
||||||
|
#include "../iso14443_4a/iso14443_4a_i.h"
|
||||||
|
|
||||||
|
static void nfc_scene_info_on_enter_ntag4xx(NfcApp* instance) {
|
||||||
|
const NfcDevice* device = instance->nfc_device;
|
||||||
|
const Ntag4xxData* data = nfc_device_get_data(device, NfcProtocolNtag4xx);
|
||||||
|
|
||||||
|
FuriString* temp_str = furi_string_alloc();
|
||||||
|
nfc_append_filename_string_when_present(instance, temp_str);
|
||||||
|
furi_string_cat_printf(
|
||||||
|
temp_str, "\e#%s\n", nfc_device_get_name(device, NfcDeviceNameTypeFull));
|
||||||
|
nfc_render_ntag4xx_info(data, NfcProtocolFormatTypeFull, temp_str);
|
||||||
|
|
||||||
|
widget_add_text_scroll_element(
|
||||||
|
instance->widget, 0, 0, 128, 64, furi_string_get_cstr(temp_str));
|
||||||
|
|
||||||
|
furi_string_free(temp_str);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void nfc_scene_more_info_on_enter_ntag4xx(NfcApp* instance) {
|
||||||
|
const NfcDevice* device = instance->nfc_device;
|
||||||
|
const Ntag4xxData* data = nfc_device_get_data(device, NfcProtocolNtag4xx);
|
||||||
|
|
||||||
|
furi_string_reset(instance->text_box_store);
|
||||||
|
nfc_render_ntag4xx_data(data, instance->text_box_store);
|
||||||
|
|
||||||
|
text_box_set_font(instance->text_box, TextBoxFontHex);
|
||||||
|
text_box_set_text(instance->text_box, furi_string_get_cstr(instance->text_box_store));
|
||||||
|
|
||||||
|
view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewTextBox);
|
||||||
|
}
|
||||||
|
|
||||||
|
static NfcCommand nfc_scene_read_poller_callback_ntag4xx(NfcGenericEvent event, void* context) {
|
||||||
|
furi_assert(event.protocol == NfcProtocolNtag4xx);
|
||||||
|
|
||||||
|
NfcCommand command = NfcCommandContinue;
|
||||||
|
|
||||||
|
NfcApp* instance = context;
|
||||||
|
const Ntag4xxPollerEvent* ntag4xx_event = event.event_data;
|
||||||
|
|
||||||
|
if(ntag4xx_event->type == Ntag4xxPollerEventTypeReadSuccess) {
|
||||||
|
nfc_device_set_data(
|
||||||
|
instance->nfc_device, NfcProtocolNtag4xx, nfc_poller_get_data(instance->poller));
|
||||||
|
view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventPollerSuccess);
|
||||||
|
command = NfcCommandStop;
|
||||||
|
} else if(ntag4xx_event->type == Ntag4xxPollerEventTypeReadFailed) {
|
||||||
|
command = NfcCommandReset;
|
||||||
|
}
|
||||||
|
|
||||||
|
return command;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void nfc_scene_read_on_enter_ntag4xx(NfcApp* instance) {
|
||||||
|
nfc_poller_start(instance->poller, nfc_scene_read_poller_callback_ntag4xx, instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void nfc_scene_read_success_on_enter_ntag4xx(NfcApp* instance) {
|
||||||
|
const NfcDevice* device = instance->nfc_device;
|
||||||
|
const Ntag4xxData* data = nfc_device_get_data(device, NfcProtocolNtag4xx);
|
||||||
|
|
||||||
|
FuriString* temp_str = furi_string_alloc();
|
||||||
|
furi_string_cat_printf(
|
||||||
|
temp_str, "\e#%s\n", nfc_device_get_name(device, NfcDeviceNameTypeFull));
|
||||||
|
nfc_render_ntag4xx_info(data, NfcProtocolFormatTypeShort, temp_str);
|
||||||
|
|
||||||
|
widget_add_text_scroll_element(
|
||||||
|
instance->widget, 0, 0, 128, 52, furi_string_get_cstr(temp_str));
|
||||||
|
|
||||||
|
furi_string_free(temp_str);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void nfc_scene_emulate_on_enter_ntag4xx(NfcApp* instance) {
|
||||||
|
const Iso14443_4aData* iso14443_4a_data =
|
||||||
|
nfc_device_get_data(instance->nfc_device, NfcProtocolIso14443_4a);
|
||||||
|
|
||||||
|
instance->listener =
|
||||||
|
nfc_listener_alloc(instance->nfc, NfcProtocolIso14443_4a, iso14443_4a_data);
|
||||||
|
nfc_listener_start(
|
||||||
|
instance->listener, nfc_scene_emulate_listener_callback_iso14443_4a, instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
const NfcProtocolSupportBase nfc_protocol_support_ntag4xx = {
|
||||||
|
.features = NfcProtocolFeatureEmulateUid | NfcProtocolFeatureMoreInfo,
|
||||||
|
|
||||||
|
.scene_info =
|
||||||
|
{
|
||||||
|
.on_enter = nfc_scene_info_on_enter_ntag4xx,
|
||||||
|
.on_event = nfc_protocol_support_common_on_event_empty,
|
||||||
|
},
|
||||||
|
.scene_more_info =
|
||||||
|
{
|
||||||
|
.on_enter = nfc_scene_more_info_on_enter_ntag4xx,
|
||||||
|
.on_event = nfc_protocol_support_common_on_event_empty,
|
||||||
|
},
|
||||||
|
.scene_read =
|
||||||
|
{
|
||||||
|
.on_enter = nfc_scene_read_on_enter_ntag4xx,
|
||||||
|
.on_event = nfc_protocol_support_common_on_event_empty,
|
||||||
|
},
|
||||||
|
.scene_read_menu =
|
||||||
|
{
|
||||||
|
.on_enter = nfc_protocol_support_common_on_enter_empty,
|
||||||
|
.on_event = nfc_protocol_support_common_on_event_empty,
|
||||||
|
},
|
||||||
|
.scene_read_success =
|
||||||
|
{
|
||||||
|
.on_enter = nfc_scene_read_success_on_enter_ntag4xx,
|
||||||
|
.on_event = nfc_protocol_support_common_on_event_empty,
|
||||||
|
},
|
||||||
|
.scene_saved_menu =
|
||||||
|
{
|
||||||
|
.on_enter = nfc_protocol_support_common_on_enter_empty,
|
||||||
|
.on_event = nfc_protocol_support_common_on_event_empty,
|
||||||
|
},
|
||||||
|
.scene_save_name =
|
||||||
|
{
|
||||||
|
.on_enter = nfc_protocol_support_common_on_enter_empty,
|
||||||
|
.on_event = nfc_protocol_support_common_on_event_empty,
|
||||||
|
},
|
||||||
|
.scene_emulate =
|
||||||
|
{
|
||||||
|
.on_enter = nfc_scene_emulate_on_enter_ntag4xx,
|
||||||
|
.on_event = nfc_protocol_support_common_on_event_empty,
|
||||||
|
},
|
||||||
|
.scene_write =
|
||||||
|
{
|
||||||
|
.on_enter = nfc_protocol_support_common_on_enter_empty,
|
||||||
|
.on_event = nfc_protocol_support_common_on_event_empty,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
NFC_PROTOCOL_SUPPORT_PLUGIN(ntag4xx, NfcProtocolNtag4xx);
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "../nfc_protocol_support_base.h"
|
||||||
|
|
||||||
|
extern const NfcProtocolSupportBase nfc_protocol_support_ntag4xx;
|
||||||
@@ -0,0 +1,110 @@
|
|||||||
|
#include "ntag4xx_render.h"
|
||||||
|
|
||||||
|
#include "../iso14443_4a/iso14443_4a_render.h"
|
||||||
|
|
||||||
|
void nfc_render_ntag4xx_info(
|
||||||
|
const Ntag4xxData* data,
|
||||||
|
NfcProtocolFormatType format_type,
|
||||||
|
FuriString* str) {
|
||||||
|
nfc_render_iso14443_4a_brief(ntag4xx_get_base_data(data), str);
|
||||||
|
|
||||||
|
const Ntag4xxType type = ntag4xx_get_type_from_version(&data->version);
|
||||||
|
if(type >= Ntag4xxTypeUnknown) {
|
||||||
|
furi_string_cat(str, "Memory Size: unknown");
|
||||||
|
} else {
|
||||||
|
size_t size_cc = 32;
|
||||||
|
size_t size_ndef = 0;
|
||||||
|
size_t size_proprietary = 0;
|
||||||
|
bool has_tagtamper = false;
|
||||||
|
switch(type) {
|
||||||
|
case Ntag4xxType413DNA:
|
||||||
|
size_ndef = 128;
|
||||||
|
size_proprietary = 0;
|
||||||
|
break;
|
||||||
|
case Ntag4xxType424DNATT:
|
||||||
|
has_tagtamper = true;
|
||||||
|
/* fall-through */
|
||||||
|
case Ntag4xxType424DNA:
|
||||||
|
size_ndef = 256;
|
||||||
|
size_proprietary = 128;
|
||||||
|
break;
|
||||||
|
case Ntag4xxType426QDNATT:
|
||||||
|
has_tagtamper = true;
|
||||||
|
/* fall-through */
|
||||||
|
case Ntag4xxType426QDNA:
|
||||||
|
size_ndef = 768;
|
||||||
|
size_proprietary = 128;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
furi_string_cat_printf(
|
||||||
|
str, "\nMemory Size: %zu bytes\n", size_cc + size_ndef + size_proprietary);
|
||||||
|
furi_string_cat_printf(str, "Usable NDEF Size: %zu bytes\n", size_ndef - sizeof(uint16_t));
|
||||||
|
furi_string_cat_printf(str, "Capability Cont.: %zu bytes\n", size_cc);
|
||||||
|
if(size_proprietary) {
|
||||||
|
furi_string_cat_printf(str, "Proprietary File: %zu bytes\n", size_proprietary);
|
||||||
|
}
|
||||||
|
furi_string_cat_printf(str, "TagTamper: %ssupported", has_tagtamper ? "" : "not ");
|
||||||
|
}
|
||||||
|
|
||||||
|
if(format_type != NfcProtocolFormatTypeFull) return;
|
||||||
|
|
||||||
|
furi_string_cat(str, "\n\e#ISO14443-4 data");
|
||||||
|
nfc_render_iso14443_4a_extra(ntag4xx_get_base_data(data), str);
|
||||||
|
}
|
||||||
|
|
||||||
|
void nfc_render_ntag4xx_data(const Ntag4xxData* data, FuriString* str) {
|
||||||
|
nfc_render_ntag4xx_version(&data->version, str);
|
||||||
|
}
|
||||||
|
|
||||||
|
void nfc_render_ntag4xx_version(const Ntag4xxVersion* data, FuriString* str) {
|
||||||
|
furi_string_cat_printf(
|
||||||
|
str,
|
||||||
|
"%02x:%02x:%02x:%02x:%02x:%02x:%02x\n",
|
||||||
|
data->uid[0],
|
||||||
|
data->uid[1],
|
||||||
|
data->uid[2],
|
||||||
|
data->uid[3],
|
||||||
|
data->uid[4],
|
||||||
|
data->uid[5],
|
||||||
|
data->uid[6]);
|
||||||
|
furi_string_cat_printf(
|
||||||
|
str,
|
||||||
|
"hw %02x type %02x sub %02x\n"
|
||||||
|
" maj %02x min %02x\n"
|
||||||
|
" size %02x proto %02x\n",
|
||||||
|
data->hw_vendor,
|
||||||
|
data->hw_type,
|
||||||
|
data->hw_subtype,
|
||||||
|
data->hw_major,
|
||||||
|
data->hw_minor,
|
||||||
|
data->hw_storage,
|
||||||
|
data->hw_proto);
|
||||||
|
furi_string_cat_printf(
|
||||||
|
str,
|
||||||
|
"sw %02x type %02x sub %02x\n"
|
||||||
|
" maj %02x min %02x\n"
|
||||||
|
" size %02x proto %02x\n",
|
||||||
|
data->sw_vendor,
|
||||||
|
data->sw_type,
|
||||||
|
data->sw_subtype,
|
||||||
|
data->sw_major,
|
||||||
|
data->sw_minor,
|
||||||
|
data->sw_storage,
|
||||||
|
data->sw_proto);
|
||||||
|
furi_string_cat_printf(
|
||||||
|
str,
|
||||||
|
"batch %02x:%02x:%02x:%02x:%01x\n"
|
||||||
|
"week %d year %d\n"
|
||||||
|
"fab key %02x id %02x\n",
|
||||||
|
data->batch[0],
|
||||||
|
data->batch[1],
|
||||||
|
data->batch[2],
|
||||||
|
data->batch[3],
|
||||||
|
data->batch_extra,
|
||||||
|
data->prod_week,
|
||||||
|
data->prod_year,
|
||||||
|
(data->fab_key_4b << 1) | (data->fab_key_1b),
|
||||||
|
data->optional.fab_key_id);
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <nfc/protocols/ntag4xx/ntag4xx.h>
|
||||||
|
|
||||||
|
#include "../nfc_protocol_support_render_common.h"
|
||||||
|
|
||||||
|
void nfc_render_ntag4xx_info(
|
||||||
|
const Ntag4xxData* data,
|
||||||
|
NfcProtocolFormatType format_type,
|
||||||
|
FuriString* str);
|
||||||
|
|
||||||
|
void nfc_render_ntag4xx_data(const Ntag4xxData* data, FuriString* str);
|
||||||
|
|
||||||
|
void nfc_render_ntag4xx_version(const Ntag4xxVersion* data, FuriString* str);
|
||||||
@@ -78,20 +78,21 @@ static NfcCommand nfc_scene_emulate_listener_callback_slix(NfcGenericEvent event
|
|||||||
furi_assert(event.protocol == NfcProtocolSlix);
|
furi_assert(event.protocol == NfcProtocolSlix);
|
||||||
furi_assert(event.event_data);
|
furi_assert(event.event_data);
|
||||||
|
|
||||||
NfcApp* nfc = context;
|
NfcApp* instance = context;
|
||||||
SlixListenerEvent* slix_event = event.event_data;
|
SlixListenerEvent* slix_event = event.event_data;
|
||||||
|
|
||||||
if(slix_event->type == SlixListenerEventTypeCustomCommand) {
|
if(slix_event->type == SlixListenerEventTypeCustomCommand) {
|
||||||
if(furi_string_size(nfc->text_box_store) < NFC_LOG_SIZE_MAX) {
|
if(furi_string_size(instance->text_box_store) < NFC_LOG_SIZE_MAX) {
|
||||||
furi_string_cat_printf(nfc->text_box_store, "R:");
|
furi_string_cat_printf(instance->text_box_store, "R:");
|
||||||
for(size_t i = 0; i < bit_buffer_get_size_bytes(slix_event->data->buffer); i++) {
|
for(size_t i = 0; i < bit_buffer_get_size_bytes(slix_event->data->buffer); i++) {
|
||||||
furi_string_cat_printf(
|
furi_string_cat_printf(
|
||||||
nfc->text_box_store,
|
instance->text_box_store,
|
||||||
" %02X",
|
" %02X",
|
||||||
bit_buffer_get_byte(slix_event->data->buffer, i));
|
bit_buffer_get_byte(slix_event->data->buffer, i));
|
||||||
}
|
}
|
||||||
furi_string_push_back(nfc->text_box_store, '\n');
|
furi_string_push_back(instance->text_box_store, '\n');
|
||||||
view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventListenerUpdate);
|
view_dispatcher_send_custom_event(
|
||||||
|
instance->view_dispatcher, NfcCustomEventListenerUpdate);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -105,15 +106,6 @@ static void nfc_scene_emulate_on_enter_slix(NfcApp* instance) {
|
|||||||
nfc_listener_start(instance->listener, nfc_scene_emulate_listener_callback_slix, instance);
|
nfc_listener_start(instance->listener, nfc_scene_emulate_listener_callback_slix, instance);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool nfc_scene_saved_menu_on_event_slix(NfcApp* instance, SceneManagerEvent event) {
|
|
||||||
if(event.type == SceneManagerEventTypeCustom && event.event == SubmenuIndexCommonEdit) {
|
|
||||||
scene_manager_next_scene(instance->scene_manager, NfcSceneSetUid);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const NfcProtocolSupportBase nfc_protocol_support_slix = {
|
const NfcProtocolSupportBase nfc_protocol_support_slix = {
|
||||||
.features = NfcProtocolFeatureEmulateFull | NfcProtocolFeatureMoreInfo,
|
.features = NfcProtocolFeatureEmulateFull | NfcProtocolFeatureMoreInfo,
|
||||||
|
|
||||||
@@ -145,7 +137,7 @@ const NfcProtocolSupportBase nfc_protocol_support_slix = {
|
|||||||
.scene_saved_menu =
|
.scene_saved_menu =
|
||||||
{
|
{
|
||||||
.on_enter = nfc_protocol_support_common_on_enter_empty,
|
.on_enter = nfc_protocol_support_common_on_enter_empty,
|
||||||
.on_event = nfc_scene_saved_menu_on_event_slix,
|
.on_event = nfc_protocol_support_common_on_event_empty,
|
||||||
},
|
},
|
||||||
.scene_save_name =
|
.scene_save_name =
|
||||||
{
|
{
|
||||||
@@ -157,4 +149,11 @@ const NfcProtocolSupportBase nfc_protocol_support_slix = {
|
|||||||
.on_enter = nfc_scene_emulate_on_enter_slix,
|
.on_enter = nfc_scene_emulate_on_enter_slix,
|
||||||
.on_event = nfc_protocol_support_common_on_event_empty,
|
.on_event = nfc_protocol_support_common_on_event_empty,
|
||||||
},
|
},
|
||||||
|
.scene_write =
|
||||||
|
{
|
||||||
|
.on_enter = nfc_protocol_support_common_on_enter_empty,
|
||||||
|
.on_event = nfc_protocol_support_common_on_event_empty,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
NFC_PROTOCOL_SUPPORT_PLUGIN(slix, NfcProtocolSlix);
|
||||||
|
|||||||
@@ -61,15 +61,6 @@ static void nfc_scene_read_success_on_enter_st25tb(NfcApp* instance) {
|
|||||||
furi_string_free(temp_str);
|
furi_string_free(temp_str);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool nfc_scene_saved_menu_on_event_st25tb(NfcApp* instance, SceneManagerEvent event) {
|
|
||||||
if(event.type == SceneManagerEventTypeCustom && event.event == SubmenuIndexCommonEdit) {
|
|
||||||
scene_manager_next_scene(instance->scene_manager, NfcSceneSetUid);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const NfcProtocolSupportBase nfc_protocol_support_st25tb = {
|
const NfcProtocolSupportBase nfc_protocol_support_st25tb = {
|
||||||
.features = NfcProtocolFeatureNone,
|
.features = NfcProtocolFeatureNone,
|
||||||
|
|
||||||
@@ -96,7 +87,7 @@ const NfcProtocolSupportBase nfc_protocol_support_st25tb = {
|
|||||||
.scene_saved_menu =
|
.scene_saved_menu =
|
||||||
{
|
{
|
||||||
.on_enter = nfc_protocol_support_common_on_enter_empty,
|
.on_enter = nfc_protocol_support_common_on_enter_empty,
|
||||||
.on_event = nfc_scene_saved_menu_on_event_st25tb,
|
.on_event = nfc_protocol_support_common_on_event_empty,
|
||||||
},
|
},
|
||||||
.scene_save_name =
|
.scene_save_name =
|
||||||
{
|
{
|
||||||
@@ -108,4 +99,11 @@ const NfcProtocolSupportBase nfc_protocol_support_st25tb = {
|
|||||||
.on_enter = nfc_protocol_support_common_on_enter_empty,
|
.on_enter = nfc_protocol_support_common_on_enter_empty,
|
||||||
.on_event = nfc_protocol_support_common_on_event_empty,
|
.on_event = nfc_protocol_support_common_on_event_empty,
|
||||||
},
|
},
|
||||||
|
.scene_write =
|
||||||
|
{
|
||||||
|
.on_enter = nfc_protocol_support_common_on_enter_empty,
|
||||||
|
.on_event = nfc_protocol_support_common_on_event_empty,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
NFC_PROTOCOL_SUPPORT_PLUGIN(st25tb, NfcProtocolSt25tb);
|
||||||
|
|||||||
@@ -0,0 +1,260 @@
|
|||||||
|
#include "type_4_tag.h"
|
||||||
|
#include "type_4_tag_render.h"
|
||||||
|
|
||||||
|
#include <nfc/protocols/type_4_tag/type_4_tag_poller.h>
|
||||||
|
#include <nfc/protocols/type_4_tag/type_4_tag_listener.h>
|
||||||
|
#include <toolbox/pretty_format.h>
|
||||||
|
|
||||||
|
#include "nfc/nfc_app_i.h"
|
||||||
|
|
||||||
|
#include "../nfc_protocol_support_common.h"
|
||||||
|
#include "../nfc_protocol_support_gui_common.h"
|
||||||
|
|
||||||
|
enum {
|
||||||
|
NfcSceneMoreInfoStateASCII,
|
||||||
|
NfcSceneMoreInfoStateRawData,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void nfc_scene_info_on_enter_type_4_tag(NfcApp* instance) {
|
||||||
|
const NfcDevice* device = instance->nfc_device;
|
||||||
|
const Type4TagData* data = nfc_device_get_data(device, NfcProtocolType4Tag);
|
||||||
|
|
||||||
|
FuriString* temp_str = furi_string_alloc();
|
||||||
|
nfc_append_filename_string_when_present(instance, temp_str);
|
||||||
|
furi_string_cat_printf(
|
||||||
|
temp_str, "\e#%s\n", nfc_device_get_name(device, NfcDeviceNameTypeFull));
|
||||||
|
nfc_render_type_4_tag_info(data, NfcProtocolFormatTypeFull, temp_str);
|
||||||
|
|
||||||
|
widget_add_text_scroll_element(
|
||||||
|
instance->widget, 0, 0, 128, 52, furi_string_get_cstr(temp_str));
|
||||||
|
|
||||||
|
furi_string_free(temp_str);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void nfc_scene_more_info_on_enter_type_4_tag(NfcApp* instance) {
|
||||||
|
const NfcDevice* device = instance->nfc_device;
|
||||||
|
const Type4TagData* data = nfc_device_get_data(device, NfcProtocolType4Tag);
|
||||||
|
|
||||||
|
furi_string_reset(instance->text_box_store);
|
||||||
|
uint32_t scene_state =
|
||||||
|
scene_manager_get_scene_state(instance->scene_manager, NfcSceneMoreInfo);
|
||||||
|
|
||||||
|
if(scene_state == NfcSceneMoreInfoStateASCII) {
|
||||||
|
if(simple_array_get_count(data->ndef_data) == 0) {
|
||||||
|
furi_string_cat_str(instance->text_box_store, "No NDEF data to show");
|
||||||
|
} else {
|
||||||
|
pretty_format_bytes_hex_canonical(
|
||||||
|
instance->text_box_store,
|
||||||
|
TYPE_4_TAG_RENDER_BYTES_PER_LINE,
|
||||||
|
PRETTY_FORMAT_FONT_MONOSPACE,
|
||||||
|
simple_array_cget_data(data->ndef_data),
|
||||||
|
simple_array_get_count(data->ndef_data));
|
||||||
|
}
|
||||||
|
|
||||||
|
widget_add_text_scroll_element(
|
||||||
|
instance->widget, 0, 0, 128, 48, furi_string_get_cstr(instance->text_box_store));
|
||||||
|
widget_add_button_element(
|
||||||
|
instance->widget,
|
||||||
|
GuiButtonTypeRight,
|
||||||
|
"Raw Data",
|
||||||
|
nfc_protocol_support_common_widget_callback,
|
||||||
|
instance);
|
||||||
|
|
||||||
|
widget_add_button_element(
|
||||||
|
instance->widget,
|
||||||
|
GuiButtonTypeLeft,
|
||||||
|
"Info",
|
||||||
|
nfc_protocol_support_common_widget_callback,
|
||||||
|
instance);
|
||||||
|
} else if(scene_state == NfcSceneMoreInfoStateRawData) {
|
||||||
|
nfc_render_type_4_tag_dump(data, instance->text_box_store);
|
||||||
|
widget_add_text_scroll_element(
|
||||||
|
instance->widget, 0, 0, 128, 48, furi_string_get_cstr(instance->text_box_store));
|
||||||
|
|
||||||
|
widget_add_button_element(
|
||||||
|
instance->widget,
|
||||||
|
GuiButtonTypeLeft,
|
||||||
|
"ASCII",
|
||||||
|
nfc_protocol_support_common_widget_callback,
|
||||||
|
instance);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool nfc_scene_more_info_on_event_type_4_tag(NfcApp* instance, SceneManagerEvent event) {
|
||||||
|
bool consumed = false;
|
||||||
|
|
||||||
|
if((event.type == SceneManagerEventTypeCustom && event.event == GuiButtonTypeLeft) ||
|
||||||
|
(event.type == SceneManagerEventTypeBack)) {
|
||||||
|
scene_manager_set_scene_state(
|
||||||
|
instance->scene_manager, NfcSceneMoreInfo, NfcSceneMoreInfoStateASCII);
|
||||||
|
scene_manager_previous_scene(instance->scene_manager);
|
||||||
|
consumed = true;
|
||||||
|
} else if(event.type == SceneManagerEventTypeCustom && event.event == GuiButtonTypeRight) {
|
||||||
|
scene_manager_set_scene_state(
|
||||||
|
instance->scene_manager, NfcSceneMoreInfo, NfcSceneMoreInfoStateRawData);
|
||||||
|
scene_manager_next_scene(instance->scene_manager, NfcSceneMoreInfo);
|
||||||
|
consumed = true;
|
||||||
|
}
|
||||||
|
return consumed;
|
||||||
|
}
|
||||||
|
|
||||||
|
static NfcCommand nfc_scene_read_poller_callback_type_4_tag(NfcGenericEvent event, void* context) {
|
||||||
|
furi_assert(event.protocol == NfcProtocolType4Tag);
|
||||||
|
|
||||||
|
NfcCommand command = NfcCommandContinue;
|
||||||
|
|
||||||
|
NfcApp* instance = context;
|
||||||
|
const Type4TagPollerEvent* type_4_tag_event = event.event_data;
|
||||||
|
|
||||||
|
if(type_4_tag_event->type == Type4TagPollerEventTypeReadSuccess) {
|
||||||
|
nfc_device_set_data(
|
||||||
|
instance->nfc_device, NfcProtocolType4Tag, nfc_poller_get_data(instance->poller));
|
||||||
|
view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventPollerSuccess);
|
||||||
|
command = NfcCommandStop;
|
||||||
|
} else if(type_4_tag_event->type == Type4TagPollerEventTypeReadFailed) {
|
||||||
|
command = NfcCommandReset;
|
||||||
|
}
|
||||||
|
|
||||||
|
return command;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void nfc_scene_read_on_enter_type_4_tag(NfcApp* instance) {
|
||||||
|
nfc_poller_start(instance->poller, nfc_scene_read_poller_callback_type_4_tag, instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void nfc_scene_read_success_on_enter_type_4_tag(NfcApp* instance) {
|
||||||
|
const NfcDevice* device = instance->nfc_device;
|
||||||
|
const Type4TagData* data = nfc_device_get_data(device, NfcProtocolType4Tag);
|
||||||
|
|
||||||
|
FuriString* temp_str = furi_string_alloc();
|
||||||
|
furi_string_cat_printf(
|
||||||
|
temp_str, "\e#%s\n", nfc_device_get_name(device, NfcDeviceNameTypeFull));
|
||||||
|
nfc_render_type_4_tag_info(data, NfcProtocolFormatTypeShort, temp_str);
|
||||||
|
|
||||||
|
widget_add_text_scroll_element(
|
||||||
|
instance->widget, 0, 0, 128, 52, furi_string_get_cstr(temp_str));
|
||||||
|
|
||||||
|
furi_string_free(temp_str);
|
||||||
|
}
|
||||||
|
|
||||||
|
static NfcCommand
|
||||||
|
nfc_scene_emulate_listener_callback_type_4_tag(NfcGenericEvent event, void* context) {
|
||||||
|
furi_assert(event.protocol == NfcProtocolType4Tag);
|
||||||
|
|
||||||
|
NfcApp* instance = context;
|
||||||
|
Type4TagListenerEvent* type_4_tag_event = event.event_data;
|
||||||
|
|
||||||
|
if(type_4_tag_event->type == Type4TagListenerEventTypeCustomCommand) {
|
||||||
|
if(furi_string_size(instance->text_box_store) < NFC_LOG_SIZE_MAX) {
|
||||||
|
furi_string_cat_printf(instance->text_box_store, "R:");
|
||||||
|
for(size_t i = 0; i < bit_buffer_get_size_bytes(type_4_tag_event->data->buffer); i++) {
|
||||||
|
furi_string_cat_printf(
|
||||||
|
instance->text_box_store,
|
||||||
|
" %02X",
|
||||||
|
bit_buffer_get_byte(type_4_tag_event->data->buffer, i));
|
||||||
|
}
|
||||||
|
furi_string_push_back(instance->text_box_store, '\n');
|
||||||
|
view_dispatcher_send_custom_event(
|
||||||
|
instance->view_dispatcher, NfcCustomEventListenerUpdate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return NfcCommandContinue;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void nfc_scene_emulate_on_enter_type_4_tag(NfcApp* instance) {
|
||||||
|
const Type4TagData* data = nfc_device_get_data(instance->nfc_device, NfcProtocolType4Tag);
|
||||||
|
|
||||||
|
instance->listener = nfc_listener_alloc(instance->nfc, NfcProtocolType4Tag, data);
|
||||||
|
nfc_listener_start(
|
||||||
|
instance->listener, nfc_scene_emulate_listener_callback_type_4_tag, instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
static NfcCommand
|
||||||
|
nfc_scene_write_poller_callback_type_4_tag(NfcGenericEvent event, void* context) {
|
||||||
|
furi_assert(event.protocol == NfcProtocolType4Tag);
|
||||||
|
|
||||||
|
NfcApp* instance = context;
|
||||||
|
Type4TagPollerEvent* type_4_tag_event = event.event_data;
|
||||||
|
NfcCommand command = NfcCommandContinue;
|
||||||
|
|
||||||
|
if(type_4_tag_event->type == Type4TagPollerEventTypeRequestMode) {
|
||||||
|
type_4_tag_event->data->poller_mode.mode = Type4TagPollerModeWrite;
|
||||||
|
type_4_tag_event->data->poller_mode.data =
|
||||||
|
nfc_device_get_data(instance->nfc_device, NfcProtocolType4Tag);
|
||||||
|
furi_string_reset(instance->text_box_store);
|
||||||
|
view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventCardDetected);
|
||||||
|
} else if(type_4_tag_event->type == Type4TagPollerEventTypeWriteFail) {
|
||||||
|
const char* error_str = type_4_tag_event->data->error == Type4TagErrorCardLocked ?
|
||||||
|
"Card does not\nallow writing\nnew data" :
|
||||||
|
"Failed to\nwrite new data";
|
||||||
|
furi_string_set(instance->text_box_store, error_str);
|
||||||
|
view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventPollerFailure);
|
||||||
|
command = NfcCommandStop;
|
||||||
|
} else if(type_4_tag_event->type == Type4TagPollerEventTypeWriteSuccess) {
|
||||||
|
furi_string_reset(instance->text_box_store);
|
||||||
|
view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventPollerSuccess);
|
||||||
|
command = NfcCommandStop;
|
||||||
|
}
|
||||||
|
|
||||||
|
return command;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void nfc_scene_write_on_enter_type_4_tag(NfcApp* instance) {
|
||||||
|
instance->poller = nfc_poller_alloc(instance->nfc, NfcProtocolType4Tag);
|
||||||
|
nfc_poller_start(instance->poller, nfc_scene_write_poller_callback_type_4_tag, instance);
|
||||||
|
furi_string_set(instance->text_box_store, "Apply card\nto the back");
|
||||||
|
}
|
||||||
|
|
||||||
|
const NfcProtocolSupportBase nfc_protocol_support_type_4_tag = {
|
||||||
|
.features = NfcProtocolFeatureEmulateFull | NfcProtocolFeatureMoreInfo |
|
||||||
|
NfcProtocolFeatureWrite,
|
||||||
|
|
||||||
|
.scene_info =
|
||||||
|
{
|
||||||
|
.on_enter = nfc_scene_info_on_enter_type_4_tag,
|
||||||
|
.on_event = nfc_protocol_support_common_on_event_empty,
|
||||||
|
},
|
||||||
|
.scene_more_info =
|
||||||
|
{
|
||||||
|
.on_enter = nfc_scene_more_info_on_enter_type_4_tag,
|
||||||
|
.on_event = nfc_scene_more_info_on_event_type_4_tag,
|
||||||
|
},
|
||||||
|
.scene_read =
|
||||||
|
{
|
||||||
|
.on_enter = nfc_scene_read_on_enter_type_4_tag,
|
||||||
|
.on_event = nfc_protocol_support_common_on_event_empty,
|
||||||
|
},
|
||||||
|
.scene_read_menu =
|
||||||
|
{
|
||||||
|
.on_enter = nfc_protocol_support_common_on_enter_empty,
|
||||||
|
.on_event = nfc_protocol_support_common_on_event_empty,
|
||||||
|
},
|
||||||
|
.scene_read_success =
|
||||||
|
{
|
||||||
|
.on_enter = nfc_scene_read_success_on_enter_type_4_tag,
|
||||||
|
.on_event = nfc_protocol_support_common_on_event_empty,
|
||||||
|
},
|
||||||
|
.scene_saved_menu =
|
||||||
|
{
|
||||||
|
.on_enter = nfc_protocol_support_common_on_enter_empty,
|
||||||
|
.on_event = nfc_protocol_support_common_on_event_empty,
|
||||||
|
},
|
||||||
|
.scene_save_name =
|
||||||
|
{
|
||||||
|
.on_enter = nfc_protocol_support_common_on_enter_empty,
|
||||||
|
.on_event = nfc_protocol_support_common_on_event_empty,
|
||||||
|
},
|
||||||
|
.scene_emulate =
|
||||||
|
{
|
||||||
|
.on_enter = nfc_scene_emulate_on_enter_type_4_tag,
|
||||||
|
.on_event = nfc_protocol_support_common_on_event_empty,
|
||||||
|
},
|
||||||
|
.scene_write =
|
||||||
|
{
|
||||||
|
.on_enter = nfc_scene_write_on_enter_type_4_tag,
|
||||||
|
.on_event = nfc_protocol_support_common_on_event_empty,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
NFC_PROTOCOL_SUPPORT_PLUGIN(type_4_tag, NfcProtocolType4Tag);
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "../nfc_protocol_support_base.h"
|
||||||
|
|
||||||
|
extern const NfcProtocolSupportBase nfc_protocol_support_type_4_tag;
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
#include "type_4_tag_render.h"
|
||||||
|
|
||||||
|
#include "../iso14443_4a/iso14443_4a_render.h"
|
||||||
|
|
||||||
|
void nfc_render_type_4_tag_info(
|
||||||
|
const Type4TagData* data,
|
||||||
|
NfcProtocolFormatType format_type,
|
||||||
|
FuriString* str) {
|
||||||
|
nfc_render_iso14443_4a_brief(type_4_tag_get_base_data(data), str);
|
||||||
|
|
||||||
|
furi_string_cat(str, "\n:::::::::::::::[Stored NDEF]:::::::::::::::\n");
|
||||||
|
furi_string_cat_printf(str, "Current NDEF Size: %lu", simple_array_get_count(data->ndef_data));
|
||||||
|
|
||||||
|
if(data->is_tag_specific) {
|
||||||
|
furi_string_cat(str, "\n::::::::::::::::::[Tag Specs]::::::::::::::::::\n");
|
||||||
|
furi_string_cat_printf(
|
||||||
|
str,
|
||||||
|
"Card: %s\n",
|
||||||
|
furi_string_empty(data->platform_name) ? "unknown" :
|
||||||
|
furi_string_get_cstr(data->platform_name));
|
||||||
|
furi_string_cat_printf(
|
||||||
|
str, "T4T Mapping Version: %u.%u\n", data->t4t_version.major, data->t4t_version.minor);
|
||||||
|
furi_string_cat_printf(str, "NDEF File ID: %04X\n", data->ndef_file_id);
|
||||||
|
furi_string_cat_printf(str, "Max NDEF Size: %u\n", data->ndef_max_len);
|
||||||
|
furi_string_cat_printf(
|
||||||
|
str, "APDU Sizes: R:%u W:%u\n", data->chunk_max_read, data->chunk_max_write);
|
||||||
|
furi_string_cat_printf(
|
||||||
|
str,
|
||||||
|
"Read Lock: %02X%s\n",
|
||||||
|
data->ndef_read_lock,
|
||||||
|
data->ndef_read_lock == 0 ? " (unlocked)" : "");
|
||||||
|
furi_string_cat_printf(
|
||||||
|
str,
|
||||||
|
"Write Lock: %02X%s",
|
||||||
|
data->ndef_write_lock,
|
||||||
|
data->ndef_write_lock == 0 ? " (unlocked)" : "");
|
||||||
|
}
|
||||||
|
|
||||||
|
if(format_type != NfcProtocolFormatTypeFull) return;
|
||||||
|
|
||||||
|
furi_string_cat(str, "\n\e#ISO14443-4 data");
|
||||||
|
nfc_render_iso14443_4a_extra(type_4_tag_get_base_data(data), str);
|
||||||
|
}
|
||||||
|
|
||||||
|
void nfc_render_type_4_tag_dump(const Type4TagData* data, FuriString* str) {
|
||||||
|
size_t ndef_len = simple_array_get_count(data->ndef_data);
|
||||||
|
if(ndef_len == 0) {
|
||||||
|
furi_string_cat_str(str, "No NDEF data to show");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const uint8_t* ndef_data = simple_array_cget_data(data->ndef_data);
|
||||||
|
furi_string_cat_printf(str, "\e*");
|
||||||
|
for(size_t i = 0; i < ndef_len; i += TYPE_4_TAG_RENDER_BYTES_PER_LINE) {
|
||||||
|
const uint8_t* line_data = &ndef_data[i];
|
||||||
|
for(size_t j = 0; j < TYPE_4_TAG_RENDER_BYTES_PER_LINE; j += 2) {
|
||||||
|
furi_string_cat_printf(str, " %02X%02X", line_data[j], line_data[j + 1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <nfc/protocols/type_4_tag/type_4_tag.h>
|
||||||
|
|
||||||
|
#include "../nfc_protocol_support_render_common.h"
|
||||||
|
|
||||||
|
#define TYPE_4_TAG_RENDER_BYTES_PER_LINE (4U)
|
||||||
|
|
||||||
|
void nfc_render_type_4_tag_info(
|
||||||
|
const Type4TagData* data,
|
||||||
|
NfcProtocolFormatType format_type,
|
||||||
|
FuriString* str);
|
||||||
|
|
||||||
|
void nfc_render_type_4_tag_dump(const Type4TagData* data, FuriString* str);
|
||||||
@@ -1,7 +1,9 @@
|
|||||||
#include "nfc_app_i.h"
|
#include "nfc_app_i.h"
|
||||||
|
#include "api/nfc_app_api_interface.h"
|
||||||
#include "helpers/protocol_support/nfc_protocol_support.h"
|
#include "helpers/protocol_support/nfc_protocol_support.h"
|
||||||
|
|
||||||
#include <dolphin/dolphin.h>
|
#include <dolphin/dolphin.h>
|
||||||
|
#include <loader/firmware_api/firmware_api.h>
|
||||||
|
|
||||||
bool nfc_custom_event_callback(void* context, uint32_t event) {
|
bool nfc_custom_event_callback(void* context, uint32_t event) {
|
||||||
furi_assert(context);
|
furi_assert(context);
|
||||||
@@ -49,12 +51,16 @@ NfcApp* nfc_app_alloc(void) {
|
|||||||
|
|
||||||
instance->nfc = nfc_alloc();
|
instance->nfc = nfc_alloc();
|
||||||
|
|
||||||
|
instance->api_resolver = composite_api_resolver_alloc();
|
||||||
|
composite_api_resolver_add(instance->api_resolver, firmware_api_interface);
|
||||||
|
composite_api_resolver_add(instance->api_resolver, nfc_application_api_interface);
|
||||||
|
|
||||||
instance->detected_protocols = nfc_detected_protocols_alloc();
|
instance->detected_protocols = nfc_detected_protocols_alloc();
|
||||||
instance->felica_auth = felica_auth_alloc();
|
instance->felica_auth = felica_auth_alloc();
|
||||||
instance->mf_ul_auth = mf_ultralight_auth_alloc();
|
instance->mf_ul_auth = mf_ultralight_auth_alloc();
|
||||||
instance->slix_unlock = slix_unlock_alloc();
|
instance->slix_unlock = slix_unlock_alloc();
|
||||||
instance->mfc_key_cache = mf_classic_key_cache_alloc();
|
instance->mfc_key_cache = mf_classic_key_cache_alloc();
|
||||||
instance->nfc_supported_cards = nfc_supported_cards_alloc();
|
instance->nfc_supported_cards = nfc_supported_cards_alloc(instance->api_resolver);
|
||||||
|
|
||||||
// Nfc device
|
// Nfc device
|
||||||
instance->nfc_device = nfc_device_alloc();
|
instance->nfc_device = nfc_device_alloc();
|
||||||
@@ -148,6 +154,9 @@ void nfc_app_free(NfcApp* instance) {
|
|||||||
slix_unlock_free(instance->slix_unlock);
|
slix_unlock_free(instance->slix_unlock);
|
||||||
mf_classic_key_cache_free(instance->mfc_key_cache);
|
mf_classic_key_cache_free(instance->mfc_key_cache);
|
||||||
nfc_supported_cards_free(instance->nfc_supported_cards);
|
nfc_supported_cards_free(instance->nfc_supported_cards);
|
||||||
|
if(instance->protocol_support) {
|
||||||
|
nfc_protocol_support_free(instance);
|
||||||
|
}
|
||||||
|
|
||||||
// Nfc device
|
// Nfc device
|
||||||
nfc_device_free(instance->nfc_device);
|
nfc_device_free(instance->nfc_device);
|
||||||
@@ -415,6 +424,11 @@ bool nfc_load_from_file_select(NfcApp* instance) {
|
|||||||
if(!dialog_file_browser_show(
|
if(!dialog_file_browser_show(
|
||||||
instance->dialogs, instance->file_path, instance->file_path, &browser_options))
|
instance->dialogs, instance->file_path, instance->file_path, &browser_options))
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
nfc_show_loading_popup(instance, true);
|
||||||
|
nfc_supported_cards_load_cache(instance->nfc_supported_cards);
|
||||||
|
nfc_show_loading_popup(instance, false);
|
||||||
|
|
||||||
success = nfc_load_file(instance, instance->file_path, true);
|
success = nfc_load_file(instance, instance->file_path, true);
|
||||||
} while(!success);
|
} while(!success);
|
||||||
|
|
||||||
@@ -464,7 +478,7 @@ static bool nfc_is_hal_ready(void) {
|
|||||||
static void nfc_show_initial_scene_for_device(NfcApp* nfc) {
|
static void nfc_show_initial_scene_for_device(NfcApp* nfc) {
|
||||||
NfcProtocol prot = nfc_device_get_protocol(nfc->nfc_device);
|
NfcProtocol prot = nfc_device_get_protocol(nfc->nfc_device);
|
||||||
uint32_t scene = nfc_protocol_support_has_feature(
|
uint32_t scene = nfc_protocol_support_has_feature(
|
||||||
prot, NfcProtocolFeatureEmulateFull | NfcProtocolFeatureEmulateUid) ?
|
prot, nfc, NfcProtocolFeatureEmulateFull | NfcProtocolFeatureEmulateUid) ?
|
||||||
NfcSceneEmulate :
|
NfcSceneEmulate :
|
||||||
NfcSceneSavedMenu;
|
NfcSceneSavedMenu;
|
||||||
// Load plugins (parsers) in case if we are in the saved menu
|
// Load plugins (parsers) in case if we are in the saved menu
|
||||||
@@ -523,11 +537,6 @@ int32_t nfc_app(void* p) {
|
|||||||
} else {
|
} else {
|
||||||
view_dispatcher_attach_to_gui(
|
view_dispatcher_attach_to_gui(
|
||||||
nfc->view_dispatcher, nfc->gui, ViewDispatcherTypeFullscreen);
|
nfc->view_dispatcher, nfc->gui, ViewDispatcherTypeFullscreen);
|
||||||
// Load plugins (parsers) one time in case if we running app normally
|
|
||||||
nfc_show_loading_popup(nfc, true);
|
|
||||||
nfc_supported_cards_load_cache(nfc->nfc_supported_cards);
|
|
||||||
nfc_show_loading_popup(nfc, false);
|
|
||||||
// Switch to the initial scene
|
|
||||||
scene_manager_next_scene(nfc->scene_manager, NfcSceneStart);
|
scene_manager_next_scene(nfc->scene_manager, NfcSceneStart);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -32,10 +32,12 @@
|
|||||||
#include "helpers/mfkey32_logger.h"
|
#include "helpers/mfkey32_logger.h"
|
||||||
#include "helpers/nfc_emv_parser.h"
|
#include "helpers/nfc_emv_parser.h"
|
||||||
#include "helpers/mf_classic_key_cache.h"
|
#include "helpers/mf_classic_key_cache.h"
|
||||||
|
#include "helpers/protocol_support/nfc_protocol_support.h"
|
||||||
#include "helpers/nfc_supported_cards.h"
|
#include "helpers/nfc_supported_cards.h"
|
||||||
#include "helpers/felica_auth.h"
|
#include "helpers/felica_auth.h"
|
||||||
#include "helpers/slix_unlock.h"
|
#include "helpers/slix_unlock.h"
|
||||||
|
|
||||||
|
#include <flipper_application/plugins/composite_resolver.h>
|
||||||
#include <loader/loader.h>
|
#include <loader/loader.h>
|
||||||
#include <dialogs/dialogs.h>
|
#include <dialogs/dialogs.h>
|
||||||
#include <storage/storage.h>
|
#include <storage/storage.h>
|
||||||
@@ -149,6 +151,8 @@ struct NfcApp {
|
|||||||
Mfkey32Logger* mfkey32_logger;
|
Mfkey32Logger* mfkey32_logger;
|
||||||
MfUserDict* mf_user_dict;
|
MfUserDict* mf_user_dict;
|
||||||
MfClassicKeyCache* mfc_key_cache;
|
MfClassicKeyCache* mfc_key_cache;
|
||||||
|
CompositeApiResolver* api_resolver;
|
||||||
|
NfcProtocolSupport* protocol_support;
|
||||||
NfcSupportedCards* nfc_supported_cards;
|
NfcSupportedCards* nfc_supported_cards;
|
||||||
|
|
||||||
NfcDevice* nfc_device;
|
NfcDevice* nfc_device;
|
||||||
@@ -176,6 +180,10 @@ typedef enum {
|
|||||||
NfcSceneSaveConfirmStateCrackNonces,
|
NfcSceneSaveConfirmStateCrackNonces,
|
||||||
} NfcSceneSaveConfirmState;
|
} NfcSceneSaveConfirmState;
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
int32_t nfc_task(void* p);
|
int32_t nfc_task(void* p);
|
||||||
|
|
||||||
void nfc_text_store_set(NfcApp* nfc, const char* text, ...);
|
void nfc_text_store_set(NfcApp* nfc, const char* text, ...);
|
||||||
@@ -213,3 +221,7 @@ void nfc_make_app_folder(NfcApp* instance);
|
|||||||
void nfc_append_filename_string_when_present(NfcApp* instance, FuriString* string);
|
void nfc_append_filename_string_when_present(NfcApp* instance, FuriString* string);
|
||||||
|
|
||||||
void nfc_app_run_external(NfcApp* nfc, const char* app_path);
|
void nfc_app_run_external(NfcApp* nfc, const char* app_path);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|||||||
@@ -7,7 +7,6 @@
|
|||||||
#include <lib/nfc/nfc_poller.h>
|
#include <lib/nfc/nfc_poller.h>
|
||||||
#include <lib/nfc/protocols/iso14443_4a/iso14443_4a_poller.h>
|
#include <lib/nfc/protocols/iso14443_4a/iso14443_4a_poller.h>
|
||||||
#include <lib/nfc/protocols/iso14443_4b/iso14443_4b_poller.h>
|
#include <lib/nfc/protocols/iso14443_4b/iso14443_4b_poller.h>
|
||||||
#include <lib/nfc/protocols/iso15693_3/iso15693_3_poller.h>
|
|
||||||
#include <toolbox/pipe.h>
|
#include <toolbox/pipe.h>
|
||||||
|
|
||||||
#include <furi_hal_nfc.h>
|
#include <furi_hal_nfc.h>
|
||||||
@@ -15,13 +14,12 @@
|
|||||||
#define FLAG_EVENT (1 << 10)
|
#define FLAG_EVENT (1 << 10)
|
||||||
|
|
||||||
#define NFC_MAX_BUFFER_SIZE (256)
|
#define NFC_MAX_BUFFER_SIZE (256)
|
||||||
#define NFC_BASE_PROTOCOL_MAX (3)
|
#define NFC_BASE_PROTOCOL_MAX (2)
|
||||||
#define POLLER_DONE (1 << 0)
|
#define POLLER_DONE (1 << 0)
|
||||||
#define POLLER_ERR (1 << 1)
|
#define POLLER_ERR (1 << 1)
|
||||||
static NfcProtocol BASE_PROTOCOL[NFC_BASE_PROTOCOL_MAX] = {
|
static NfcProtocol BASE_PROTOCOL[NFC_BASE_PROTOCOL_MAX] = {
|
||||||
NfcProtocolIso14443_4a,
|
NfcProtocolIso14443_4a,
|
||||||
NfcProtocolIso14443_4b,
|
NfcProtocolIso14443_4b};
|
||||||
NfcProtocolIso15693_3};
|
|
||||||
typedef struct ApduContext {
|
typedef struct ApduContext {
|
||||||
BitBuffer* tx_buffer;
|
BitBuffer* tx_buffer;
|
||||||
BitBuffer* rx_buffer;
|
BitBuffer* rx_buffer;
|
||||||
@@ -88,19 +86,6 @@ static NfcCommand trx_callback(NfcGenericEvent event, void* context) {
|
|||||||
furi_thread_flags_set(apdu_context->thread_id, POLLER_ERR);
|
furi_thread_flags_set(apdu_context->thread_id, POLLER_ERR);
|
||||||
return NfcCommandStop;
|
return NfcCommandStop;
|
||||||
}
|
}
|
||||||
} else if(NfcProtocolIso15693_3 == event.protocol) {
|
|
||||||
Iso15693_3Error err = iso15693_3_poller_send_frame(
|
|
||||||
event.instance,
|
|
||||||
apdu_context->tx_buffer,
|
|
||||||
apdu_context->rx_buffer,
|
|
||||||
ISO15693_3_FDT_POLL_FC);
|
|
||||||
if(Iso15693_3ErrorNone == err) {
|
|
||||||
furi_thread_flags_set(apdu_context->thread_id, POLLER_DONE);
|
|
||||||
return NfcCommandContinue;
|
|
||||||
} else {
|
|
||||||
furi_thread_flags_set(apdu_context->thread_id, POLLER_ERR);
|
|
||||||
return NfcCommandStop;
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
// should never reach here
|
// should never reach here
|
||||||
furi_crash("Unknown protocol");
|
furi_crash("Unknown protocol");
|
||||||
|
|||||||
@@ -548,7 +548,7 @@ static void furi_string_cat_timestamp(
|
|||||||
const char* time_hdr,
|
const char* time_hdr,
|
||||||
uint32_t tmst_1900) {
|
uint32_t tmst_1900) {
|
||||||
DateTime tm;
|
DateTime tm;
|
||||||
|
tmst_1900 -= 2208988800; // Clipper uses epoch from 1900, not 1970.
|
||||||
datetime_timestamp_to_datetime(tmst_1900, &tm);
|
datetime_timestamp_to_datetime(tmst_1900, &tm);
|
||||||
|
|
||||||
FuriString* date_str = furi_string_alloc();
|
FuriString* date_str = furi_string_alloc();
|
||||||
|
|||||||
149
applications/main/nfc/plugins/supported_cards/csc.c
Normal file
149
applications/main/nfc/plugins/supported_cards/csc.c
Normal file
@@ -0,0 +1,149 @@
|
|||||||
|
/*
|
||||||
|
* Parser for CSC Service Works Reloadable Cash Card (US)
|
||||||
|
* Date created 2024/5/26
|
||||||
|
* Zinong Li
|
||||||
|
* Discord @torron0483
|
||||||
|
* Github @zinongli
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "nfc_supported_card_plugin.h"
|
||||||
|
#include <flipper_application.h>
|
||||||
|
#include <nfc/protocols/mf_classic/mf_classic_poller_sync.h>
|
||||||
|
#include <bit_lib.h>
|
||||||
|
#include <furi/core/string.h>
|
||||||
|
|
||||||
|
#include <nfc/nfc.h>
|
||||||
|
#include <nfc/nfc_device.h>
|
||||||
|
|
||||||
|
#define TAG "CSC"
|
||||||
|
|
||||||
|
bool csc_parse(const NfcDevice* device, FuriString* parsed_data) {
|
||||||
|
furi_assert(device);
|
||||||
|
const MfClassicData* data = nfc_device_get_data(device, NfcProtocolMfClassic);
|
||||||
|
bool parsed = false;
|
||||||
|
|
||||||
|
do {
|
||||||
|
if(data->type != MfClassicType1k) break; // Check card type
|
||||||
|
|
||||||
|
// Verify memory format (checksum is later)
|
||||||
|
const uint8_t refill_block_num = 2;
|
||||||
|
const uint8_t current_balance_block_num = 4;
|
||||||
|
const uint8_t current_balance_copy_block_num = 8;
|
||||||
|
|
||||||
|
const uint8_t* current_balance_block_start_ptr =
|
||||||
|
&data->block[current_balance_block_num].data[0];
|
||||||
|
const uint8_t* current_balance_copy_block_start_ptr =
|
||||||
|
&data->block[current_balance_copy_block_num].data[0];
|
||||||
|
|
||||||
|
uint32_t current_balance_and_times =
|
||||||
|
bit_lib_bytes_to_num_le(current_balance_block_start_ptr, 4);
|
||||||
|
uint32_t current_balance_and_times_copy =
|
||||||
|
bit_lib_bytes_to_num_le(current_balance_copy_block_start_ptr, 4);
|
||||||
|
|
||||||
|
// Failed verification if balance != backup
|
||||||
|
if(current_balance_and_times != current_balance_and_times_copy) {
|
||||||
|
FURI_LOG_D(TAG, "Backup verification failed");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Even if balance = 0, e.g. new card, refilled times can't be zero
|
||||||
|
if(current_balance_and_times == 0 || current_balance_and_times_copy == 0) {
|
||||||
|
FURI_LOG_D(TAG, "Value bytes empty");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse data
|
||||||
|
const uint8_t card_lives_block_num = 9;
|
||||||
|
const uint8_t refill_sign_block_num = 13;
|
||||||
|
|
||||||
|
const uint8_t* refilled_balance_block_start_ptr = &data->block[refill_block_num].data[9];
|
||||||
|
const uint8_t* refill_times_block_start_ptr = &data->block[refill_block_num].data[5];
|
||||||
|
const uint8_t* card_lives_block_start_ptr = &data->block[card_lives_block_num].data[0];
|
||||||
|
const uint8_t* refill_sign_block_start_ptr = &data->block[refill_sign_block_num].data[0];
|
||||||
|
|
||||||
|
uint32_t refilled_balance = bit_lib_bytes_to_num_le(refilled_balance_block_start_ptr, 2);
|
||||||
|
uint32_t refilled_balance_dollar = refilled_balance / 100;
|
||||||
|
uint8_t refilled_balance_cent = refilled_balance % 100;
|
||||||
|
|
||||||
|
uint32_t current_balance = bit_lib_bytes_to_num_le(current_balance_block_start_ptr, 2);
|
||||||
|
uint32_t current_balance_dollar = current_balance / 100;
|
||||||
|
uint8_t current_balance_cent = current_balance % 100;
|
||||||
|
|
||||||
|
// How many times it can still be used
|
||||||
|
uint32_t card_lives = bit_lib_bytes_to_num_le(card_lives_block_start_ptr, 2);
|
||||||
|
|
||||||
|
uint32_t refill_times = bit_lib_bytes_to_num_le(refill_times_block_start_ptr, 2);
|
||||||
|
// This is zero when you buy the card. but after refilling it, the refilling machine will leave a non-zero signature here
|
||||||
|
uint64_t refill_sign = bit_lib_bytes_to_num_le(refill_sign_block_start_ptr, 8);
|
||||||
|
|
||||||
|
size_t uid_len = 0;
|
||||||
|
const uint8_t* uid = mf_classic_get_uid(data, &uid_len);
|
||||||
|
uint32_t card_uid = bit_lib_bytes_to_num_le(uid, 4);
|
||||||
|
|
||||||
|
// Last byte of refill block is checksum
|
||||||
|
const uint8_t* checksum_block = data->block[refill_block_num].data;
|
||||||
|
uint8_t xor_result = 0;
|
||||||
|
for(size_t i = 0; i < 16; ++i) {
|
||||||
|
xor_result ^= checksum_block[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
if(refill_sign == 0 && refill_times == 1) {
|
||||||
|
// New cards don't comply to checksum but refill time should be once
|
||||||
|
furi_string_printf(
|
||||||
|
parsed_data,
|
||||||
|
"\e#CSC Service Works\n"
|
||||||
|
"UID: %lu\n"
|
||||||
|
"New Card\n"
|
||||||
|
"Card Value: %lu.%02u USD\n"
|
||||||
|
"Card Usages Left: %lu",
|
||||||
|
card_uid,
|
||||||
|
refilled_balance_dollar,
|
||||||
|
refilled_balance_cent,
|
||||||
|
card_lives);
|
||||||
|
} else {
|
||||||
|
if(xor_result != 0) {
|
||||||
|
FURI_LOG_D(TAG, "Checksum failed");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
furi_string_printf(
|
||||||
|
parsed_data,
|
||||||
|
"\e#CSC Service Works\n"
|
||||||
|
"UID: %lu\n"
|
||||||
|
"Balance: %lu.%02u USD\n"
|
||||||
|
"Last Top-up: %lu.%02u USD\n"
|
||||||
|
"Top-up Count: %lu\n"
|
||||||
|
"Card Usages Left: %lu",
|
||||||
|
card_uid,
|
||||||
|
current_balance_dollar,
|
||||||
|
current_balance_cent,
|
||||||
|
refilled_balance_dollar,
|
||||||
|
refilled_balance_cent,
|
||||||
|
refill_times,
|
||||||
|
card_lives);
|
||||||
|
}
|
||||||
|
|
||||||
|
parsed = true;
|
||||||
|
} while(false);
|
||||||
|
|
||||||
|
return parsed;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Actual implementation of app<>plugin interface */
|
||||||
|
static const NfcSupportedCardsPlugin csc_plugin = {
|
||||||
|
.protocol = NfcProtocolMfClassic,
|
||||||
|
.verify = NULL,
|
||||||
|
.read = NULL,
|
||||||
|
.parse = csc_parse,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Plugin descriptor to comply with basic plugin specification */
|
||||||
|
static const FlipperAppPluginDescriptor csc_plugin_descriptor = {
|
||||||
|
.appid = NFC_SUPPORTED_CARD_PLUGIN_APP_ID,
|
||||||
|
.ep_api_version = NFC_SUPPORTED_CARD_PLUGIN_API_VERSION,
|
||||||
|
.entry_point = &csc_plugin,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Plugin entry point - must return a pointer to const descriptor */
|
||||||
|
const FlipperAppPluginDescriptor* csc_plugin_ep(void) {
|
||||||
|
return &csc_plugin_descriptor;
|
||||||
|
}
|
||||||
@@ -199,6 +199,6 @@ static const FlipperAppPluginDescriptor emv_plugin_descriptor = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/* Plugin entry point - must return a pointer to const descriptor */
|
/* Plugin entry point - must return a pointer to const descriptor */
|
||||||
const FlipperAppPluginDescriptor* emv_plugin_ep() {
|
const FlipperAppPluginDescriptor* emv_plugin_ep(void) {
|
||||||
return &emv_plugin_descriptor;
|
return &emv_plugin_descriptor;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -223,7 +223,7 @@ static bool kazan_read(Nfc* nfc, NfcDevice* device) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!mf_classic_is_card_read(data)) {
|
if(error == MfClassicErrorPartialRead) {
|
||||||
error = mf_classic_poller_sync_read(nfc, &keys_v2, data);
|
error = mf_classic_poller_sync_read(nfc, &keys_v2, data);
|
||||||
if(error == MfClassicErrorNotPresent) {
|
if(error == MfClassicErrorNotPresent) {
|
||||||
FURI_LOG_W(TAG, "Failed to read data: keys_v1");
|
FURI_LOG_W(TAG, "Failed to read data: keys_v1");
|
||||||
@@ -231,7 +231,7 @@ static bool kazan_read(Nfc* nfc, NfcDevice* device) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!mf_classic_is_card_read(data)) {
|
if(error == MfClassicErrorPartialRead) {
|
||||||
error = mf_classic_poller_sync_read(nfc, &keys_v3, data);
|
error = mf_classic_poller_sync_read(nfc, &keys_v3, data);
|
||||||
if(error == MfClassicErrorNotPresent) {
|
if(error == MfClassicErrorNotPresent) {
|
||||||
FURI_LOG_W(TAG, "Failed to read data: keys_v3");
|
FURI_LOG_W(TAG, "Failed to read data: keys_v3");
|
||||||
@@ -241,7 +241,7 @@ static bool kazan_read(Nfc* nfc, NfcDevice* device) {
|
|||||||
|
|
||||||
nfc_device_set_data(device, NfcProtocolMfClassic, data);
|
nfc_device_set_data(device, NfcProtocolMfClassic, data);
|
||||||
|
|
||||||
is_read = mf_classic_is_card_read(data);
|
is_read = (error == MfClassicErrorNone);
|
||||||
} while(false);
|
} while(false);
|
||||||
|
|
||||||
mf_classic_free(data);
|
mf_classic_free(data);
|
||||||
@@ -409,6 +409,6 @@ static const FlipperAppPluginDescriptor kazan_plugin_descriptor = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/* Plugin entry point - must return a pointer to const descriptor */
|
/* Plugin entry point - must return a pointer to const descriptor */
|
||||||
const FlipperAppPluginDescriptor* kazan_plugin_ep() {
|
const FlipperAppPluginDescriptor* kazan_plugin_ep(void) {
|
||||||
return &kazan_plugin_descriptor;
|
return &kazan_plugin_descriptor;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -185,6 +185,6 @@ static const FlipperAppPluginDescriptor metromoney_plugin_descriptor = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/* Plugin entry point - must return a pointer to const descriptor */
|
/* Plugin entry point - must return a pointer to const descriptor */
|
||||||
const FlipperAppPluginDescriptor* metromoney_plugin_ep() {
|
const FlipperAppPluginDescriptor* metromoney_plugin_ep(void) {
|
||||||
return &metromoney_plugin_descriptor;
|
return &metromoney_plugin_descriptor;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -199,7 +199,7 @@ static bool mizip_parse(const NfcDevice* device, FuriString* parsed_data) {
|
|||||||
MfClassicSectorTrailer* sec_tr =
|
MfClassicSectorTrailer* sec_tr =
|
||||||
mf_classic_get_sector_trailer_by_sector(data, cfg.verify_sector);
|
mf_classic_get_sector_trailer_by_sector(data, cfg.verify_sector);
|
||||||
uint64_t key = bit_lib_bytes_to_num_be(sec_tr->key_b.data, 6);
|
uint64_t key = bit_lib_bytes_to_num_be(sec_tr->key_b.data, 6);
|
||||||
if(key != cfg.keys[cfg.verify_sector].b) return false;
|
if(key != cfg.keys[cfg.verify_sector].b) break;
|
||||||
|
|
||||||
//Get UID
|
//Get UID
|
||||||
uint8_t uid[UID_LENGTH];
|
uint8_t uid[UID_LENGTH];
|
||||||
|
|||||||
@@ -20,6 +20,7 @@
|
|||||||
#include <nfc/protocols/mf_ultralight/mf_ultralight.h>
|
#include <nfc/protocols/mf_ultralight/mf_ultralight.h>
|
||||||
#include <nfc/protocols/mf_classic/mf_classic.h>
|
#include <nfc/protocols/mf_classic/mf_classic.h>
|
||||||
#include <nfc/protocols/slix/slix.h>
|
#include <nfc/protocols/slix/slix.h>
|
||||||
|
#include <nfc/protocols/type_4_tag/type_4_tag.h>
|
||||||
|
|
||||||
#include <bit_lib.h>
|
#include <bit_lib.h>
|
||||||
#include <toolbox/pretty_format.h>
|
#include <toolbox/pretty_format.h>
|
||||||
@@ -31,7 +32,8 @@
|
|||||||
#define NDEF_PROTO_UL (1)
|
#define NDEF_PROTO_UL (1)
|
||||||
#define NDEF_PROTO_MFC (2)
|
#define NDEF_PROTO_MFC (2)
|
||||||
#define NDEF_PROTO_SLIX (3)
|
#define NDEF_PROTO_SLIX (3)
|
||||||
#define NDEF_PROTO_TOTAL (4)
|
#define NDEF_PROTO_T4T (4)
|
||||||
|
#define NDEF_PROTO_TOTAL (5)
|
||||||
|
|
||||||
#ifndef NDEF_PROTO
|
#ifndef NDEF_PROTO
|
||||||
#error Must specify what protocol to use with NDEF_PROTO define!
|
#error Must specify what protocol to use with NDEF_PROTO define!
|
||||||
@@ -43,7 +45,7 @@
|
|||||||
#define NDEF_TITLE(device, parsed_data) \
|
#define NDEF_TITLE(device, parsed_data) \
|
||||||
furi_string_printf( \
|
furi_string_printf( \
|
||||||
parsed_data, \
|
parsed_data, \
|
||||||
"\e#NDEF Format Data\nCard type: %s\n", \
|
"\e#NDEF Format Data\nCard: %s\n", \
|
||||||
nfc_device_get_name(device, NfcDeviceNameTypeFull))
|
nfc_device_get_name(device, NfcDeviceNameTypeFull))
|
||||||
|
|
||||||
// ---=== structures ===---
|
// ---=== structures ===---
|
||||||
@@ -151,6 +153,11 @@ typedef struct {
|
|||||||
const uint8_t* start;
|
const uint8_t* start;
|
||||||
size_t size;
|
size_t size;
|
||||||
} slix;
|
} slix;
|
||||||
|
#elif NDEF_PROTO == NDEF_PROTO_T4T
|
||||||
|
struct {
|
||||||
|
const uint8_t* data;
|
||||||
|
size_t size;
|
||||||
|
} t4t;
|
||||||
#endif
|
#endif
|
||||||
} Ndef;
|
} Ndef;
|
||||||
|
|
||||||
@@ -230,6 +237,13 @@ static bool ndef_get(Ndef* ndef, size_t pos, size_t len, void* buf) {
|
|||||||
memcpy(buf, ndef->slix.start + pos, len);
|
memcpy(buf, ndef->slix.start + pos, len);
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
#elif NDEF_PROTO == NDEF_PROTO_T4T
|
||||||
|
|
||||||
|
// Memory space is contiguous, simply need to remap to data pointer
|
||||||
|
if(pos + len > ndef->t4t.size) return false;
|
||||||
|
memcpy(buf, ndef->t4t.data + pos, len);
|
||||||
|
return true;
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
UNUSED(ndef);
|
UNUSED(ndef);
|
||||||
@@ -1039,6 +1053,44 @@ static bool ndef_slix_parse(const NfcDevice* device, FuriString* parsed_data) {
|
|||||||
return parsed > 0;
|
return parsed > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#elif NDEF_PROTO == NDEF_PROTO_T4T
|
||||||
|
|
||||||
|
static bool ndef_t4t_parse(const NfcDevice* device, FuriString* parsed_data) {
|
||||||
|
furi_assert(device);
|
||||||
|
furi_assert(parsed_data);
|
||||||
|
|
||||||
|
const Type4TagData* data = nfc_device_get_data(device, NfcProtocolType4Tag);
|
||||||
|
size_t data_start = 0;
|
||||||
|
size_t data_size = simple_array_get_count(data->ndef_data);
|
||||||
|
|
||||||
|
NDEF_TITLE(device, parsed_data);
|
||||||
|
|
||||||
|
furi_string_replace(parsed_data, "Card: ", "Protocol: ");
|
||||||
|
if(data->is_tag_specific && !furi_string_empty(data->platform_name)) {
|
||||||
|
furi_string_cat_printf(
|
||||||
|
parsed_data, "Card: %s\n", furi_string_get_cstr(data->platform_name));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ndef ndef = {
|
||||||
|
.output = parsed_data,
|
||||||
|
.t4t =
|
||||||
|
{
|
||||||
|
.data = data_size == 0 ? NULL : simple_array_cget_data(data->ndef_data),
|
||||||
|
.size = data_size,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
size_t parsed = ndef_parse_message(&ndef, data_start, data_size - data_start, 1, false);
|
||||||
|
|
||||||
|
if(parsed) {
|
||||||
|
furi_string_trim(parsed_data, "\n");
|
||||||
|
furi_string_cat(parsed_data, "\n");
|
||||||
|
} else {
|
||||||
|
furi_string_reset(parsed_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
return parsed > 0;
|
||||||
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// ---=== boilerplate ===---
|
// ---=== boilerplate ===---
|
||||||
@@ -1056,6 +1108,9 @@ static const NfcSupportedCardsPlugin ndef_plugin = {
|
|||||||
#elif NDEF_PROTO == NDEF_PROTO_SLIX
|
#elif NDEF_PROTO == NDEF_PROTO_SLIX
|
||||||
.parse = ndef_slix_parse,
|
.parse = ndef_slix_parse,
|
||||||
.protocol = NfcProtocolSlix,
|
.protocol = NfcProtocolSlix,
|
||||||
|
#elif NDEF_PROTO == NDEF_PROTO_T4T
|
||||||
|
.parse = ndef_t4t_parse,
|
||||||
|
.protocol = NfcProtocolType4Tag,
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -247,7 +247,7 @@ static bool plantain_parse(const NfcDevice* device, FuriString* parsed_data) {
|
|||||||
// Print card number with 4-digit groups. "3" in "3078" denotes a ticket type "3 - full ticket", will differ on discounted cards.
|
// Print card number with 4-digit groups. "3" in "3078" denotes a ticket type "3 - full ticket", will differ on discounted cards.
|
||||||
furi_string_cat_printf(parsed_data, "Number: ");
|
furi_string_cat_printf(parsed_data, "Number: ");
|
||||||
FuriString* card_number_s = furi_string_alloc();
|
FuriString* card_number_s = furi_string_alloc();
|
||||||
furi_string_cat_printf(card_number_s, "%llu", card_number);
|
furi_string_cat_printf(card_number_s, "%lld", card_number);
|
||||||
FuriString* tmp_s = furi_string_alloc_set_str("9643 3078 ");
|
FuriString* tmp_s = furi_string_alloc_set_str("9643 3078 ");
|
||||||
for(uint8_t i = 0; i < 24; i += 4) {
|
for(uint8_t i = 0; i < 24; i += 4) {
|
||||||
for(uint8_t j = 0; j < 4; j++) {
|
for(uint8_t j = 0; j < 4; j++) {
|
||||||
|
|||||||
334
applications/main/nfc/plugins/supported_cards/smartrider.c
Normal file
334
applications/main/nfc/plugins/supported_cards/smartrider.c
Normal file
@@ -0,0 +1,334 @@
|
|||||||
|
#include "nfc_supported_card_plugin.h"
|
||||||
|
#include <bit_lib.h>
|
||||||
|
#include <flipper_application.h>
|
||||||
|
#include <furi.h>
|
||||||
|
#include <nfc/protocols/mf_classic/mf_classic_poller_sync.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#define MAX_TRIPS 10
|
||||||
|
#define TAG "SmartRider"
|
||||||
|
#define MAX_BLOCKS 64
|
||||||
|
#define MAX_DATE_ITERATIONS 366
|
||||||
|
|
||||||
|
static const uint8_t STANDARD_KEYS[3][6] = {
|
||||||
|
{0x20, 0x31, 0xD1, 0xE5, 0x7A, 0x3B},
|
||||||
|
{0x4C, 0xA6, 0x02, 0x9F, 0x94, 0x73},
|
||||||
|
{0x19, 0x19, 0x53, 0x98, 0xE3, 0x2F}};
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint32_t timestamp;
|
||||||
|
uint16_t cost;
|
||||||
|
uint16_t transaction_number;
|
||||||
|
uint16_t journey_number;
|
||||||
|
char route[5];
|
||||||
|
uint8_t tap_on : 1;
|
||||||
|
uint8_t block;
|
||||||
|
} __attribute__((packed)) TripData;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint32_t balance;
|
||||||
|
uint16_t issued_days;
|
||||||
|
uint16_t expiry_days;
|
||||||
|
uint16_t purchase_cost;
|
||||||
|
uint16_t auto_load_threshold;
|
||||||
|
uint16_t auto_load_value;
|
||||||
|
char card_serial_number[11];
|
||||||
|
uint8_t token;
|
||||||
|
TripData trips[MAX_TRIPS];
|
||||||
|
uint8_t trip_count;
|
||||||
|
} __attribute__((packed)) SmartRiderData;
|
||||||
|
|
||||||
|
static const char* const CONCESSION_TYPES[] = {
|
||||||
|
"Pre-issue",
|
||||||
|
"Standard Fare",
|
||||||
|
"Student",
|
||||||
|
NULL,
|
||||||
|
"Tertiary",
|
||||||
|
NULL,
|
||||||
|
"Seniors",
|
||||||
|
"Health Care",
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
"PTA Staff",
|
||||||
|
"Pensioner",
|
||||||
|
"Free Travel"};
|
||||||
|
|
||||||
|
static inline const char* get_concession_type(uint8_t token) {
|
||||||
|
return (token <= 0x10) ? CONCESSION_TYPES[token] : "Unknown";
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool authenticate_and_read(
|
||||||
|
Nfc* nfc,
|
||||||
|
uint8_t sector,
|
||||||
|
const uint8_t* key,
|
||||||
|
MfClassicKeyType key_type,
|
||||||
|
MfClassicBlock* block_data) {
|
||||||
|
MfClassicKey mf_key;
|
||||||
|
memcpy(mf_key.data, key, 6);
|
||||||
|
uint8_t block = mf_classic_get_first_block_num_of_sector(sector);
|
||||||
|
|
||||||
|
if(mf_classic_poller_sync_auth(nfc, block, &mf_key, key_type, NULL) != MfClassicErrorNone) {
|
||||||
|
FURI_LOG_D(TAG, "Authentication failed for sector %d key type %d", sector, key_type);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(mf_classic_poller_sync_read_block(nfc, block, &mf_key, key_type, block_data) !=
|
||||||
|
MfClassicErrorNone) {
|
||||||
|
FURI_LOG_D(TAG, "Read failed for sector %d", sector);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool smartrider_verify(Nfc* nfc) {
|
||||||
|
furi_assert(nfc);
|
||||||
|
MfClassicBlock block_data;
|
||||||
|
|
||||||
|
for(int i = 0; i < 3; i++) {
|
||||||
|
if(!authenticate_and_read(
|
||||||
|
nfc,
|
||||||
|
i * 6,
|
||||||
|
STANDARD_KEYS[i],
|
||||||
|
i % 2 == 0 ? MfClassicKeyTypeA : MfClassicKeyTypeB,
|
||||||
|
&block_data) ||
|
||||||
|
memcmp(block_data.data, STANDARD_KEYS[i], 6) != 0) {
|
||||||
|
FURI_LOG_D(TAG, "Authentication or key mismatch for key %d", i);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FURI_LOG_I(TAG, "SmartRider card verified");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool
|
||||||
|
parse_trip_data(const MfClassicBlock* block_data, TripData* trip, uint8_t block_number) {
|
||||||
|
trip->timestamp = bit_lib_bytes_to_num_le(block_data->data + 3, 4);
|
||||||
|
trip->tap_on = (block_data->data[7] & 0x10) == 0x10;
|
||||||
|
memcpy(trip->route, block_data->data + 8, 4);
|
||||||
|
trip->route[4] = '\0';
|
||||||
|
trip->cost = bit_lib_bytes_to_num_le(block_data->data + 13, 2);
|
||||||
|
trip->transaction_number = bit_lib_bytes_to_num_le(block_data->data, 2);
|
||||||
|
trip->journey_number = bit_lib_bytes_to_num_le(block_data->data + 2, 2);
|
||||||
|
trip->block = block_number;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool smartrider_read(Nfc* nfc, NfcDevice* device) {
|
||||||
|
furi_assert(nfc);
|
||||||
|
furi_assert(device);
|
||||||
|
MfClassicData* data = mf_classic_alloc();
|
||||||
|
nfc_device_copy_data(device, NfcProtocolMfClassic, data);
|
||||||
|
|
||||||
|
MfClassicType type;
|
||||||
|
if(mf_classic_poller_sync_detect_type(nfc, &type) != MfClassicErrorNone ||
|
||||||
|
type != MfClassicType1k) {
|
||||||
|
mf_classic_free(data);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
data->type = type;
|
||||||
|
|
||||||
|
MfClassicDeviceKeys keys = {.key_a_mask = 0, .key_b_mask = 0};
|
||||||
|
for(size_t i = 0; i < mf_classic_get_total_sectors_num(data->type); i++) {
|
||||||
|
memcpy(keys.key_a[i].data, STANDARD_KEYS[i == 0 ? 0 : 1], sizeof(STANDARD_KEYS[0]));
|
||||||
|
if(i > 0) {
|
||||||
|
memcpy(keys.key_b[i].data, STANDARD_KEYS[2], sizeof(STANDARD_KEYS[0]));
|
||||||
|
FURI_BIT_SET(keys.key_b_mask, i);
|
||||||
|
}
|
||||||
|
FURI_BIT_SET(keys.key_a_mask, i);
|
||||||
|
}
|
||||||
|
|
||||||
|
MfClassicError error = mf_classic_poller_sync_read(nfc, &keys, data);
|
||||||
|
if(error != MfClassicErrorNone) {
|
||||||
|
FURI_LOG_W(TAG, "Failed to read data");
|
||||||
|
mf_classic_free(data);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
nfc_device_set_data(device, NfcProtocolMfClassic, data);
|
||||||
|
mf_classic_free(data);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool is_leap_year(uint16_t year) {
|
||||||
|
return (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void calculate_date(uint32_t timestamp, char* date_str, size_t date_str_size) {
|
||||||
|
uint32_t seconds_since_2000 = timestamp;
|
||||||
|
uint32_t days_since_2000 = seconds_since_2000 / 86400;
|
||||||
|
uint16_t year = 2000;
|
||||||
|
uint8_t month = 1;
|
||||||
|
uint16_t day = 1;
|
||||||
|
|
||||||
|
static const uint16_t days_in_month[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
|
||||||
|
|
||||||
|
while(days_since_2000 >= (is_leap_year(year) ? 366 : 365)) {
|
||||||
|
days_since_2000 -= (is_leap_year(year) ? 366 : 365);
|
||||||
|
year++;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(month = 0; month < 12; month++) {
|
||||||
|
uint16_t dim = days_in_month[month];
|
||||||
|
if(month == 1 && is_leap_year(year)) {
|
||||||
|
dim++;
|
||||||
|
}
|
||||||
|
if(days_since_2000 < dim) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
days_since_2000 -= dim;
|
||||||
|
}
|
||||||
|
|
||||||
|
day = days_since_2000 + 1;
|
||||||
|
month++; // Adjust month to 1-based
|
||||||
|
|
||||||
|
if(date_str_size > 0) {
|
||||||
|
size_t written = 0;
|
||||||
|
written += snprintf(date_str + written, date_str_size - written, "%02u", day);
|
||||||
|
if(written < date_str_size - 1) {
|
||||||
|
written += snprintf(date_str + written, date_str_size - written, "/");
|
||||||
|
}
|
||||||
|
if(written < date_str_size - 1) {
|
||||||
|
written += snprintf(date_str + written, date_str_size - written, "%02u", month);
|
||||||
|
}
|
||||||
|
if(written < date_str_size - 1) {
|
||||||
|
written += snprintf(date_str + written, date_str_size - written, "/");
|
||||||
|
}
|
||||||
|
if(written < date_str_size - 1) {
|
||||||
|
snprintf(date_str + written, date_str_size - written, "%02u", year % 100);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// If the buffer size is 0, do nothing
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool smartrider_parse(const NfcDevice* device, FuriString* parsed_data) {
|
||||||
|
furi_assert(device);
|
||||||
|
furi_assert(parsed_data);
|
||||||
|
const MfClassicData* data = nfc_device_get_data(device, NfcProtocolMfClassic);
|
||||||
|
SmartRiderData sr_data = {0};
|
||||||
|
|
||||||
|
if(data->type != MfClassicType1k) {
|
||||||
|
FURI_LOG_E(TAG, "Invalid card type");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const MfClassicSectorTrailer* sec_tr = mf_classic_get_sector_trailer_by_sector(data, 0);
|
||||||
|
if(!sec_tr || memcmp(sec_tr->key_a.data, STANDARD_KEYS[0], 6) != 0) {
|
||||||
|
FURI_LOG_E(TAG, "Key verification failed for sector 0");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const uint8_t required_blocks[] = {14, 4, 5, 1, 52, 50, 0};
|
||||||
|
for(size_t i = 0; i < COUNT_OF(required_blocks); i++) {
|
||||||
|
if(required_blocks[i] >= MAX_BLOCKS ||
|
||||||
|
!mf_classic_is_block_read(data, required_blocks[i])) {
|
||||||
|
FURI_LOG_E(TAG, "Required block %d is not read or out of range", required_blocks[i]);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sr_data.balance = bit_lib_bytes_to_num_le(data->block[14].data + 7, 2);
|
||||||
|
sr_data.issued_days = bit_lib_bytes_to_num_le(data->block[4].data + 16, 2);
|
||||||
|
sr_data.expiry_days = bit_lib_bytes_to_num_le(data->block[4].data + 18, 2);
|
||||||
|
sr_data.auto_load_threshold = bit_lib_bytes_to_num_le(data->block[4].data + 20, 2);
|
||||||
|
sr_data.auto_load_value = bit_lib_bytes_to_num_le(data->block[4].data + 22, 2);
|
||||||
|
sr_data.token = data->block[5].data[8];
|
||||||
|
sr_data.purchase_cost = bit_lib_bytes_to_num_le(data->block[0].data + 14, 2);
|
||||||
|
|
||||||
|
snprintf(
|
||||||
|
sr_data.card_serial_number,
|
||||||
|
sizeof(sr_data.card_serial_number),
|
||||||
|
"%02X%02X%02X%02X%02X",
|
||||||
|
data->block[1].data[6],
|
||||||
|
data->block[1].data[7],
|
||||||
|
data->block[1].data[8],
|
||||||
|
data->block[1].data[9],
|
||||||
|
data->block[1].data[10]);
|
||||||
|
|
||||||
|
for(uint8_t block_number = 40; block_number <= 52 && sr_data.trip_count < MAX_TRIPS;
|
||||||
|
block_number++) {
|
||||||
|
if((block_number != 43 && block_number != 47 && block_number != 51) &&
|
||||||
|
mf_classic_is_block_read(data, block_number) &&
|
||||||
|
parse_trip_data(
|
||||||
|
&data->block[block_number], &sr_data.trips[sr_data.trip_count], block_number)) {
|
||||||
|
sr_data.trip_count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sort trips by timestamp (descending order)
|
||||||
|
for(uint8_t i = 0; i < sr_data.trip_count - 1; i++) {
|
||||||
|
for(uint8_t j = 0; j < sr_data.trip_count - i - 1; j++) {
|
||||||
|
if(sr_data.trips[j].timestamp < sr_data.trips[j + 1].timestamp) {
|
||||||
|
TripData temp = sr_data.trips[j];
|
||||||
|
sr_data.trips[j] = sr_data.trips[j + 1];
|
||||||
|
sr_data.trips[j + 1] = temp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
furi_string_printf(
|
||||||
|
parsed_data,
|
||||||
|
"\e#SmartRider\nBalance: $%lu.%02lu\nConcession: %s\nSerial: %s%s\n"
|
||||||
|
"Total Cost: $%u.%02u\nAuto-Load: $%u.%02u/$%u.%02u\n\e#Tag On/Off History\n",
|
||||||
|
sr_data.balance / 100,
|
||||||
|
sr_data.balance % 100,
|
||||||
|
get_concession_type(sr_data.token),
|
||||||
|
memcmp(sr_data.card_serial_number, "00", 2) == 0 ? "SR0" : "",
|
||||||
|
memcmp(sr_data.card_serial_number, "00", 2) == 0 ? sr_data.card_serial_number + 2 :
|
||||||
|
sr_data.card_serial_number,
|
||||||
|
sr_data.purchase_cost / 100,
|
||||||
|
sr_data.purchase_cost % 100,
|
||||||
|
sr_data.auto_load_threshold / 100,
|
||||||
|
sr_data.auto_load_threshold % 100,
|
||||||
|
sr_data.auto_load_value / 100,
|
||||||
|
sr_data.auto_load_value % 100);
|
||||||
|
|
||||||
|
for(uint8_t i = 0; i < sr_data.trip_count; i++) {
|
||||||
|
char date_str[9];
|
||||||
|
calculate_date(sr_data.trips[i].timestamp, date_str, sizeof(date_str));
|
||||||
|
|
||||||
|
uint32_t cost = sr_data.trips[i].cost;
|
||||||
|
if(cost > 0) {
|
||||||
|
furi_string_cat_printf(
|
||||||
|
parsed_data,
|
||||||
|
"%s %c $%lu.%02lu %s\n",
|
||||||
|
date_str,
|
||||||
|
sr_data.trips[i].tap_on ? '+' : '-',
|
||||||
|
cost / 100,
|
||||||
|
cost % 100,
|
||||||
|
sr_data.trips[i].route);
|
||||||
|
} else {
|
||||||
|
furi_string_cat_printf(
|
||||||
|
parsed_data,
|
||||||
|
"%s %c %s\n",
|
||||||
|
date_str,
|
||||||
|
sr_data.trips[i].tap_on ? '+' : '-',
|
||||||
|
sr_data.trips[i].route);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
static const NfcSupportedCardsPlugin smartrider_plugin = {
|
||||||
|
.protocol = NfcProtocolMfClassic,
|
||||||
|
.verify = smartrider_verify,
|
||||||
|
.read = smartrider_read,
|
||||||
|
.parse = smartrider_parse,
|
||||||
|
};
|
||||||
|
|
||||||
|
__attribute__((used)) const FlipperAppPluginDescriptor* smartrider_plugin_ep() {
|
||||||
|
static const FlipperAppPluginDescriptor plugin_descriptor = {
|
||||||
|
.appid = NFC_SUPPORTED_CARD_PLUGIN_APP_ID,
|
||||||
|
.ep_api_version = NFC_SUPPORTED_CARD_PLUGIN_API_VERSION,
|
||||||
|
.entry_point = &smartrider_plugin,
|
||||||
|
};
|
||||||
|
return &plugin_descriptor;
|
||||||
|
}
|
||||||
|
|
||||||
|
// made with love by jay candel <3
|
||||||
111
applications/main/nfc/plugins/supported_cards/sonicare.c
Normal file
111
applications/main/nfc/plugins/supported_cards/sonicare.c
Normal file
@@ -0,0 +1,111 @@
|
|||||||
|
// Parser for Philips Sonicare toothbrush heads.
|
||||||
|
// Made by @Sil333033
|
||||||
|
// Thanks to Cyrill Künzi for this research! https://kuenzi.dev/toothbrush/
|
||||||
|
|
||||||
|
#include "nfc_supported_card_plugin.h"
|
||||||
|
|
||||||
|
#include <flipper_application/flipper_application.h>
|
||||||
|
#include <nfc/protocols/mf_ultralight/mf_ultralight.h>
|
||||||
|
|
||||||
|
#define TAG "Sonicare"
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
SonicareHeadWhite,
|
||||||
|
SonicareHeadBlack,
|
||||||
|
SonicareHeadUnkown,
|
||||||
|
} SonicareHead;
|
||||||
|
|
||||||
|
static SonicareHead sonicare_get_head_type(const MfUltralightData* data) {
|
||||||
|
// data.page[34].data got 4 bytes
|
||||||
|
// 31:32:31:34 for black (not sure)
|
||||||
|
// 31:31:31:31 for white (not sure)
|
||||||
|
// the data should be in here based on the research, but i cant find it.
|
||||||
|
// page 34 byte 0 is always 0x30 for the white brushes i have, so i guess thats white
|
||||||
|
// TODO: Get a black brush and test this
|
||||||
|
|
||||||
|
if(data->page[34].data[0] == 0x30) {
|
||||||
|
return SonicareHeadWhite;
|
||||||
|
} else {
|
||||||
|
return SonicareHeadUnkown;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t sonicare_get_seconds_brushed(const MfUltralightData* data) {
|
||||||
|
uint32_t seconds_brushed = 0;
|
||||||
|
|
||||||
|
seconds_brushed += data->page[36].data[0];
|
||||||
|
seconds_brushed += data->page[36].data[1] << 8;
|
||||||
|
|
||||||
|
return seconds_brushed;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool sonicare_parse(const NfcDevice* device, FuriString* parsed_data) {
|
||||||
|
furi_assert(device);
|
||||||
|
furi_assert(parsed_data);
|
||||||
|
|
||||||
|
const MfUltralightData* data = nfc_device_get_data(device, NfcProtocolMfUltralight);
|
||||||
|
|
||||||
|
bool parsed = false;
|
||||||
|
|
||||||
|
do {
|
||||||
|
// Check for NDEF link match
|
||||||
|
const char* test = "philips.com/nfcbrushheadtap";
|
||||||
|
// Data is a array of arrays, cast to char array and compare
|
||||||
|
if(strncmp(test, (const char*)&data->page[5].data[3], strlen(test)) != 0) {
|
||||||
|
FURI_LOG_D(TAG, "Not a Philips Sonicare head");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
const SonicareHead head_type = sonicare_get_head_type(data);
|
||||||
|
const uint32_t seconds_brushed = sonicare_get_seconds_brushed(data);
|
||||||
|
|
||||||
|
FuriString* head_type_str = furi_string_alloc();
|
||||||
|
|
||||||
|
switch(head_type) {
|
||||||
|
case SonicareHeadWhite:
|
||||||
|
furi_string_printf(head_type_str, "White");
|
||||||
|
break;
|
||||||
|
case SonicareHeadBlack:
|
||||||
|
furi_string_printf(head_type_str, "Black");
|
||||||
|
break;
|
||||||
|
case SonicareHeadUnkown:
|
||||||
|
default:
|
||||||
|
furi_string_printf(head_type_str, "Unknown");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
furi_string_printf(
|
||||||
|
parsed_data,
|
||||||
|
"\e#Philips Sonicare head\nColor: %s\nTime brushed: %02.0f:%02.0f:%02ld\n",
|
||||||
|
furi_string_get_cstr(head_type_str),
|
||||||
|
floor(seconds_brushed / 3600),
|
||||||
|
floor((seconds_brushed / 60) % 60),
|
||||||
|
seconds_brushed % 60);
|
||||||
|
|
||||||
|
furi_string_free(head_type_str);
|
||||||
|
|
||||||
|
parsed = true;
|
||||||
|
} while(false);
|
||||||
|
|
||||||
|
return parsed;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Actual implementation of app<>plugin interface */
|
||||||
|
static const NfcSupportedCardsPlugin sonicare_plugin = {
|
||||||
|
.protocol = NfcProtocolMfUltralight,
|
||||||
|
.verify = NULL,
|
||||||
|
.read = NULL,
|
||||||
|
.parse = sonicare_parse,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Plugin descriptor to comply with basic plugin specification */
|
||||||
|
static const FlipperAppPluginDescriptor sonicare_plugin_descriptor = {
|
||||||
|
.appid = NFC_SUPPORTED_CARD_PLUGIN_APP_ID,
|
||||||
|
.ep_api_version = NFC_SUPPORTED_CARD_PLUGIN_API_VERSION,
|
||||||
|
.entry_point = &sonicare_plugin,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Plugin entry point - must return a pointer to const descriptor */
|
||||||
|
const FlipperAppPluginDescriptor* sonicare_plugin_ep(void) {
|
||||||
|
return &sonicare_plugin_descriptor;
|
||||||
|
}
|
||||||
@@ -112,7 +112,7 @@ static bool two_cities_parse(const NfcDevice* device, FuriString* parsed_data) {
|
|||||||
// Verify key
|
// Verify key
|
||||||
MfClassicSectorTrailer* sec_tr = mf_classic_get_sector_trailer_by_sector(data, 4);
|
MfClassicSectorTrailer* sec_tr = mf_classic_get_sector_trailer_by_sector(data, 4);
|
||||||
uint64_t key = bit_lib_bytes_to_num_be(sec_tr->key_a.data, 6);
|
uint64_t key = bit_lib_bytes_to_num_be(sec_tr->key_a.data, 6);
|
||||||
if(key != two_cities_4k_keys[4].a) return false;
|
if(key != two_cities_4k_keys[4].a) break;
|
||||||
|
|
||||||
// =====
|
// =====
|
||||||
// PLANTAIN
|
// PLANTAIN
|
||||||
|
|||||||
292
applications/main/nfc/plugins/supported_cards/ventra.c
Normal file
292
applications/main/nfc/plugins/supported_cards/ventra.c
Normal file
@@ -0,0 +1,292 @@
|
|||||||
|
// Parser for CTA Ventra Ultralight cards
|
||||||
|
// Made by @hazardousvoltage
|
||||||
|
// Based on my own research, with...
|
||||||
|
// Credit to https://www.lenrek.net/experiments/compass-tickets/ & MetroDroid project for underlying info
|
||||||
|
//
|
||||||
|
// This parser can decode the paper single-use and single/multi-day paper passes using Ultralight EV1
|
||||||
|
// The plastic cards are DESFire and fully locked down, not much useful info extractable
|
||||||
|
// TODO:
|
||||||
|
// - Sort the duplicate/rare ticket types
|
||||||
|
// - Database of stop IDs for trains? Buses there's just too damn many, but you can find them here:
|
||||||
|
// https://data.cityofchicago.org/Transportation/CTA-Bus-Stops-kml/84eu-buny/about_data
|
||||||
|
// - Generalize to handle all known Cubic Nextfare Ultralight systems? Anyone wants to send me specimen dumps, hit me up on Discord.
|
||||||
|
|
||||||
|
#include "nfc_supported_card_plugin.h"
|
||||||
|
|
||||||
|
#include <flipper_application/flipper_application.h>
|
||||||
|
#include <nfc/protocols/mf_ultralight/mf_ultralight.h>
|
||||||
|
#include "datetime.h"
|
||||||
|
#include <furi_hal.h>
|
||||||
|
|
||||||
|
#define TAG "Ventra"
|
||||||
|
|
||||||
|
DateTime ventra_exp_date = {0}, ventra_validity_date = {0};
|
||||||
|
uint8_t ventra_high_seq = 0, ventra_cur_blk = 0, ventra_mins_active = 0;
|
||||||
|
|
||||||
|
uint32_t time_now() {
|
||||||
|
return furi_hal_rtc_get_timestamp();
|
||||||
|
}
|
||||||
|
|
||||||
|
static DateTime dt_delta(DateTime dt, uint8_t delta_days) {
|
||||||
|
// returns shifted DateTime, from initial DateTime and time offset in seconds
|
||||||
|
DateTime dt_shifted = {0};
|
||||||
|
datetime_timestamp_to_datetime(
|
||||||
|
datetime_datetime_to_timestamp(&dt) - (uint64_t)delta_days * 86400, &dt_shifted);
|
||||||
|
return dt_shifted;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
static long dt_diff(DateTime dta, DateTime dtb) {
|
||||||
|
// returns difference in seconds between two DateTimes
|
||||||
|
long diff;
|
||||||
|
diff = datetime_datetime_to_timestamp(&dta) - datetime_datetime_to_timestamp(&dtb);
|
||||||
|
return diff;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Card is expired if:
|
||||||
|
// - Hard expiration date passed (90 days from purchase, encoded in product record)
|
||||||
|
// - Soft expiration date passed:
|
||||||
|
// - For passes, n days after first use
|
||||||
|
// - For tickets, 2 hours after first use
|
||||||
|
// Calculating these is dumber than it needs to be, see xact record parser.
|
||||||
|
bool isExpired(void) {
|
||||||
|
uint32_t ts_hard_exp = datetime_datetime_to_timestamp(&ventra_exp_date);
|
||||||
|
uint32_t ts_soft_exp = datetime_datetime_to_timestamp(&ventra_validity_date);
|
||||||
|
uint32_t ts_now = time_now();
|
||||||
|
return (ts_now >= ts_hard_exp || ts_now > ts_soft_exp);
|
||||||
|
}
|
||||||
|
|
||||||
|
static FuriString* ventra_parse_xact(const MfUltralightData* data, uint8_t blk, bool is_pass) {
|
||||||
|
FuriString* ventra_xact_str = furi_string_alloc();
|
||||||
|
uint16_t ts = data->page[blk].data[0] | data->page[blk].data[1] << 8;
|
||||||
|
uint8_t tran_type = ts & 0x1F;
|
||||||
|
ts >>= 5;
|
||||||
|
uint8_t day = data->page[blk].data[2];
|
||||||
|
uint32_t work = data->page[blk + 1].data[0] | data->page[blk + 1].data[1] << 8 |
|
||||||
|
data->page[blk + 1].data[2] << 16;
|
||||||
|
uint8_t seq = work & 0x7F;
|
||||||
|
uint16_t exp = (work >> 7) & 0x7FF;
|
||||||
|
uint8_t exp_day = data->page[blk + 2].data[0];
|
||||||
|
uint16_t locus = data->page[blk + 2].data[1] | data->page[blk + 2].data[2] << 8;
|
||||||
|
uint8_t line = data->page[blk + 2].data[3];
|
||||||
|
|
||||||
|
// This computes the block timestamp, based on the card expiration date and delta from it
|
||||||
|
DateTime dt = dt_delta(ventra_exp_date, day);
|
||||||
|
dt.hour = (ts & 0x7FF) / 60;
|
||||||
|
dt.minute = (ts & 0x7FF) % 60;
|
||||||
|
|
||||||
|
// If sequence is 0, block isn't used yet (new card with only one active block, typically the first one.
|
||||||
|
// Otherwise, the block with higher sequence is the latest transaction, and the other block is prior transaction.
|
||||||
|
// Not necessarily in that order on the card. We need the latest data to compute validity and pretty-print them
|
||||||
|
// in reverse chrono. So this mess sets some globals as to which block is current, computes the validity times, etc.
|
||||||
|
if(seq == 0) {
|
||||||
|
furi_string_printf(ventra_xact_str, "-- EMPTY --");
|
||||||
|
return (ventra_xact_str);
|
||||||
|
}
|
||||||
|
if(seq > ventra_high_seq) {
|
||||||
|
ventra_high_seq = seq;
|
||||||
|
ventra_cur_blk = blk;
|
||||||
|
ventra_mins_active = data->page[blk + 1].data[3];
|
||||||
|
// Figure out the soft expiration. For passes it's easy, the readers update the "exp" field in the transaction record.
|
||||||
|
// Tickets, not so much, readers don't update "exp", but each xact record has "minutes since last tap" which is
|
||||||
|
// updated and carried forward. That, plus transaction timestamp, gives the expiration time.
|
||||||
|
if(tran_type == 6) { // Furthermore, purchase transactions set bogus expiration dates
|
||||||
|
if(is_pass) {
|
||||||
|
ventra_validity_date = dt_delta(ventra_exp_date, exp_day);
|
||||||
|
ventra_validity_date.hour = (exp & 0x7FF) / 60;
|
||||||
|
ventra_validity_date.minute = (exp & 0x7FF) % 60;
|
||||||
|
} else {
|
||||||
|
uint32_t validity_ts = datetime_datetime_to_timestamp(&dt);
|
||||||
|
validity_ts += (120 - ventra_mins_active) * 60;
|
||||||
|
datetime_timestamp_to_datetime(validity_ts, &ventra_validity_date);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Type 0 = Purchase, 1 = Train ride, 2 = Bus ride
|
||||||
|
// TODO: Check PACE and see if it uses a different line code
|
||||||
|
char linemap[3] = "PTB";
|
||||||
|
char* xact_fmt = (line == 2) ? "%c %5d %04d-%02d-%02d %02d:%02d" :
|
||||||
|
"%c %04X %04d-%02d-%02d %02d:%02d";
|
||||||
|
// I like a nice concise display showing all the relevant infos without having to scroll...
|
||||||
|
// Line StopID DateTime
|
||||||
|
furi_string_printf(
|
||||||
|
ventra_xact_str,
|
||||||
|
xact_fmt,
|
||||||
|
(line < 3) ? linemap[line] : '?',
|
||||||
|
locus,
|
||||||
|
dt.year,
|
||||||
|
dt.month,
|
||||||
|
dt.day,
|
||||||
|
dt.hour,
|
||||||
|
dt.minute);
|
||||||
|
return (ventra_xact_str);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool ventra_parse(const NfcDevice* device, FuriString* parsed_data) {
|
||||||
|
furi_assert(device);
|
||||||
|
furi_assert(parsed_data);
|
||||||
|
|
||||||
|
const MfUltralightData* data = nfc_device_get_data(device, NfcProtocolMfUltralight);
|
||||||
|
|
||||||
|
bool parsed = false;
|
||||||
|
|
||||||
|
do {
|
||||||
|
// This test can probably be improved -- it matches every Ventra I've seen, but will also match others
|
||||||
|
// in the same family. Or maybe we just generalize this parser.
|
||||||
|
if(data->page[4].data[0] != 0x0A || data->page[4].data[1] != 4 ||
|
||||||
|
data->page[4].data[2] != 0 || data->page[6].data[0] != 0 ||
|
||||||
|
data->page[6].data[1] != 0 || data->page[6].data[2] != 0) {
|
||||||
|
FURI_LOG_D(TAG, "Not Ventra Ultralight");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse the product record, display interesting data & extract info needed to parse transaction blocks
|
||||||
|
// Had this in its own function, ended up just setting a bunch of shitty globals, so inlined it instead.
|
||||||
|
FuriString* ventra_prod_str = furi_string_alloc();
|
||||||
|
uint8_t otp = data->page[3].data[0];
|
||||||
|
uint8_t prod_code = data->page[5].data[2];
|
||||||
|
bool is_pass = false;
|
||||||
|
switch(prod_code) {
|
||||||
|
case 2:
|
||||||
|
case 0x1F: // Only ever seen one of these, it parses like a Single
|
||||||
|
furi_string_cat_printf(ventra_prod_str, "Single");
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
case 0x3F:
|
||||||
|
is_pass = true;
|
||||||
|
furi_string_cat_printf(ventra_prod_str, "1-Day");
|
||||||
|
break;
|
||||||
|
case 4: // Last I checked, 3 day passes only available at airport TVMs & social service agencies
|
||||||
|
is_pass = true;
|
||||||
|
furi_string_cat_printf(ventra_prod_str, "3-Day");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
is_pass =
|
||||||
|
true; // There are some card types I don't know what they are, but they parse like a pass, not a ticket.
|
||||||
|
furi_string_cat_printf(ventra_prod_str, "0x%02X", data->page[5].data[2]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t date_y = data->page[4].data[3] | (data->page[5].data[0] << 8);
|
||||||
|
uint8_t date_d = date_y & 0x1F;
|
||||||
|
uint8_t date_m = (date_y >> 5) & 0x0F;
|
||||||
|
date_y >>= 9;
|
||||||
|
date_y += 2000;
|
||||||
|
ventra_exp_date.day = date_d;
|
||||||
|
ventra_exp_date.month = date_m;
|
||||||
|
ventra_exp_date.year = date_y;
|
||||||
|
ventra_validity_date = ventra_exp_date; // Until we know otherwise
|
||||||
|
|
||||||
|
// Parse the transaction blocks. This sets a few sloppy globals, but it's too complex and repetitive to inline.
|
||||||
|
FuriString* ventra_xact_str1 = ventra_parse_xact(data, 8, is_pass);
|
||||||
|
FuriString* ventra_xact_str2 = ventra_parse_xact(data, 12, is_pass);
|
||||||
|
|
||||||
|
uint8_t card_state = 1;
|
||||||
|
uint8_t rides_left = 0;
|
||||||
|
|
||||||
|
char* card_states[5] = {"???", "NEW", "ACT", "USED", "EXP"};
|
||||||
|
|
||||||
|
if(ventra_high_seq > 1) card_state = 2;
|
||||||
|
// On "ticket" product, the OTP bits mark off rides used. Bit 0 seems to be unused, the next 3 are set as rides are used.
|
||||||
|
// Some, not all, readers will set the high bits to 0x7 when a card is tapped after it's expired or depleted. Have not
|
||||||
|
// seen other combinations, but if we do, we'll make a nice ???. 1-day passes set the OTP bit 1 on first use. 3-day
|
||||||
|
// passes do not. But we don't really care, since they don't matter on passes, unless you're trying to rollback one.
|
||||||
|
if(!is_pass) {
|
||||||
|
switch(otp) {
|
||||||
|
case 0:
|
||||||
|
rides_left = 3;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
card_state = 2;
|
||||||
|
rides_left = 2;
|
||||||
|
break;
|
||||||
|
case 6:
|
||||||
|
card_state = 2;
|
||||||
|
rides_left = 1;
|
||||||
|
break;
|
||||||
|
case 0x0E:
|
||||||
|
case 0x7E:
|
||||||
|
card_state = 3;
|
||||||
|
rides_left = 0;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
card_state = 0;
|
||||||
|
rides_left = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(isExpired()) {
|
||||||
|
card_state = 4;
|
||||||
|
rides_left = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
furi_string_printf(
|
||||||
|
parsed_data,
|
||||||
|
"\e#Ventra %s (%s)\n",
|
||||||
|
furi_string_get_cstr(ventra_prod_str),
|
||||||
|
card_states[card_state]);
|
||||||
|
|
||||||
|
furi_string_cat_printf(
|
||||||
|
parsed_data,
|
||||||
|
"Exp: %04d-%02d-%02d %02d:%02d\n",
|
||||||
|
ventra_validity_date.year,
|
||||||
|
ventra_validity_date.month,
|
||||||
|
ventra_validity_date.day,
|
||||||
|
ventra_validity_date.hour,
|
||||||
|
ventra_validity_date.minute);
|
||||||
|
|
||||||
|
if(rides_left) {
|
||||||
|
furi_string_cat_printf(parsed_data, "Rides left: %d\n", rides_left);
|
||||||
|
}
|
||||||
|
|
||||||
|
furi_string_cat_printf(
|
||||||
|
parsed_data,
|
||||||
|
"%s\n",
|
||||||
|
furi_string_get_cstr(ventra_cur_blk == 8 ? ventra_xact_str1 : ventra_xact_str2));
|
||||||
|
|
||||||
|
furi_string_cat_printf(
|
||||||
|
parsed_data,
|
||||||
|
"%s\n",
|
||||||
|
furi_string_get_cstr(ventra_cur_blk == 8 ? ventra_xact_str2 : ventra_xact_str1));
|
||||||
|
|
||||||
|
furi_string_cat_printf(
|
||||||
|
parsed_data, "TVM ID: %02X%02X\n", data->page[7].data[1], data->page[7].data[0]);
|
||||||
|
furi_string_cat_printf(parsed_data, "Tx count: %d\n", ventra_high_seq);
|
||||||
|
furi_string_cat_printf(
|
||||||
|
parsed_data,
|
||||||
|
"Hard Expiry: %04d-%02d-%02d",
|
||||||
|
ventra_exp_date.year,
|
||||||
|
ventra_exp_date.month,
|
||||||
|
ventra_exp_date.day);
|
||||||
|
|
||||||
|
furi_string_free(ventra_prod_str);
|
||||||
|
furi_string_free(ventra_xact_str1);
|
||||||
|
furi_string_free(ventra_xact_str2);
|
||||||
|
|
||||||
|
parsed = true;
|
||||||
|
} while(false);
|
||||||
|
|
||||||
|
return parsed;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Actual implementation of app<>plugin interface */
|
||||||
|
static const NfcSupportedCardsPlugin ventra_plugin = {
|
||||||
|
.protocol = NfcProtocolMfUltralight,
|
||||||
|
.verify = NULL,
|
||||||
|
.read = NULL,
|
||||||
|
.parse = ventra_parse,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Plugin descriptor to comply with basic plugin specification */
|
||||||
|
static const FlipperAppPluginDescriptor ventra_plugin_descriptor = {
|
||||||
|
.appid = NFC_SUPPORTED_CARD_PLUGIN_APP_ID,
|
||||||
|
.ep_api_version = NFC_SUPPORTED_CARD_PLUGIN_API_VERSION,
|
||||||
|
.entry_point = &ventra_plugin,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Plugin entry point - must return a pointer to const descriptor */
|
||||||
|
const FlipperAppPluginDescriptor* ventra_plugin_ep(void) {
|
||||||
|
return &ventra_plugin_descriptor;
|
||||||
|
}
|
||||||
@@ -209,6 +209,6 @@ static const FlipperAppPluginDescriptor zolotaya_korona_plugin_descriptor = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/* Plugin entry point - must return a pointer to const descriptor */
|
/* Plugin entry point - must return a pointer to const descriptor */
|
||||||
const FlipperAppPluginDescriptor* zolotaya_korona_plugin_ep() {
|
const FlipperAppPluginDescriptor* zolotaya_korona_plugin_ep(void) {
|
||||||
return &zolotaya_korona_plugin_descriptor;
|
return &zolotaya_korona_plugin_descriptor;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -139,6 +139,6 @@ static const FlipperAppPluginDescriptor zolotaya_korona_online_plugin_descriptor
|
|||||||
};
|
};
|
||||||
|
|
||||||
/* Plugin entry point - must return a pointer to const descriptor */
|
/* Plugin entry point - must return a pointer to const descriptor */
|
||||||
const FlipperAppPluginDescriptor* zolotaya_korona_online_plugin_ep() {
|
const FlipperAppPluginDescriptor* zolotaya_korona_online_plugin_ep(void) {
|
||||||
return &zolotaya_korona_online_plugin_descriptor;
|
return &zolotaya_korona_online_plugin_descriptor;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ ADD_SCENE(nfc, extra_actions, ExtraActions)
|
|||||||
ADD_SCENE(nfc, read_success, ReadSuccess)
|
ADD_SCENE(nfc, read_success, ReadSuccess)
|
||||||
ADD_SCENE(nfc, read_menu, ReadMenu)
|
ADD_SCENE(nfc, read_menu, ReadMenu)
|
||||||
ADD_SCENE(nfc, emulate, Emulate)
|
ADD_SCENE(nfc, emulate, Emulate)
|
||||||
|
ADD_SCENE(nfc, write, Write)
|
||||||
ADD_SCENE(nfc, rpc, Rpc)
|
ADD_SCENE(nfc, rpc, Rpc)
|
||||||
ADD_SCENE(nfc, debug, Debug)
|
ADD_SCENE(nfc, debug, Debug)
|
||||||
ADD_SCENE(nfc, field, Field)
|
ADD_SCENE(nfc, field, Field)
|
||||||
@@ -25,10 +26,6 @@ ADD_SCENE(nfc, retry_confirm, RetryConfirm)
|
|||||||
ADD_SCENE(nfc, exit_confirm, ExitConfirm)
|
ADD_SCENE(nfc, exit_confirm, ExitConfirm)
|
||||||
ADD_SCENE(nfc, save_confirm, SaveConfirm)
|
ADD_SCENE(nfc, save_confirm, SaveConfirm)
|
||||||
|
|
||||||
ADD_SCENE(nfc, mf_ultralight_write, MfUltralightWrite)
|
|
||||||
ADD_SCENE(nfc, mf_ultralight_write_success, MfUltralightWriteSuccess)
|
|
||||||
ADD_SCENE(nfc, mf_ultralight_write_fail, MfUltralightWriteFail)
|
|
||||||
ADD_SCENE(nfc, mf_ultralight_wrong_card, MfUltralightWrongCard)
|
|
||||||
ADD_SCENE(nfc, mf_ultralight_unlock_menu, MfUltralightUnlockMenu)
|
ADD_SCENE(nfc, mf_ultralight_unlock_menu, MfUltralightUnlockMenu)
|
||||||
ADD_SCENE(nfc, mf_ultralight_unlock_warn, MfUltralightUnlockWarn)
|
ADD_SCENE(nfc, mf_ultralight_unlock_warn, MfUltralightUnlockWarn)
|
||||||
ADD_SCENE(nfc, mf_ultralight_key_input, MfUltralightKeyInput)
|
ADD_SCENE(nfc, mf_ultralight_key_input, MfUltralightKeyInput)
|
||||||
@@ -48,10 +45,6 @@ ADD_SCENE(nfc, mf_classic_mfkey_complete, MfClassicMfkeyComplete)
|
|||||||
ADD_SCENE(nfc, mf_classic_update_initial, MfClassicUpdateInitial)
|
ADD_SCENE(nfc, mf_classic_update_initial, MfClassicUpdateInitial)
|
||||||
ADD_SCENE(nfc, mf_classic_update_initial_success, MfClassicUpdateInitialSuccess)
|
ADD_SCENE(nfc, mf_classic_update_initial_success, MfClassicUpdateInitialSuccess)
|
||||||
ADD_SCENE(nfc, mf_classic_update_initial_wrong_card, MfClassicUpdateInitialWrongCard)
|
ADD_SCENE(nfc, mf_classic_update_initial_wrong_card, MfClassicUpdateInitialWrongCard)
|
||||||
ADD_SCENE(nfc, mf_classic_write_initial, MfClassicWriteInitial)
|
|
||||||
ADD_SCENE(nfc, mf_classic_write_initial_success, MfClassicWriteInitialSuccess)
|
|
||||||
ADD_SCENE(nfc, mf_classic_write_initial_fail, MfClassicWriteInitialFail)
|
|
||||||
ADD_SCENE(nfc, mf_classic_write_initial_wrong_card, MfClassicWriteInitialWrongCard)
|
|
||||||
|
|
||||||
ADD_SCENE(nfc, mf_classic_keys, MfClassicKeys)
|
ADD_SCENE(nfc, mf_classic_keys, MfClassicKeys)
|
||||||
ADD_SCENE(nfc, mf_classic_keys_list, MfClassicKeysList)
|
ADD_SCENE(nfc, mf_classic_keys_list, MfClassicKeysList)
|
||||||
|
|||||||
@@ -16,6 +16,10 @@ void nfc_scene_detect_scan_callback(NfcScannerEvent event, void* context) {
|
|||||||
void nfc_scene_detect_on_enter(void* context) {
|
void nfc_scene_detect_on_enter(void* context) {
|
||||||
NfcApp* instance = context;
|
NfcApp* instance = context;
|
||||||
|
|
||||||
|
nfc_show_loading_popup(instance, true);
|
||||||
|
nfc_supported_cards_load_cache(instance->nfc_supported_cards);
|
||||||
|
nfc_show_loading_popup(instance, false);
|
||||||
|
|
||||||
// Setup view
|
// Setup view
|
||||||
popup_reset(instance->popup);
|
popup_reset(instance->popup);
|
||||||
popup_set_header(instance->popup, "Reading", 97, 15, AlignCenter, AlignTop);
|
popup_set_header(instance->popup, "Reading", 97, 15, AlignCenter, AlignTop);
|
||||||
|
|||||||
@@ -236,7 +236,6 @@ void nfc_scene_mf_classic_dict_attack_on_enter(void* context) {
|
|||||||
dict_attack_set_card_state(instance->dict_attack, true);
|
dict_attack_set_card_state(instance->dict_attack, true);
|
||||||
view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewDictAttack);
|
view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewDictAttack);
|
||||||
nfc_blink_read_start(instance);
|
nfc_blink_read_start(instance);
|
||||||
notification_message(instance->notifications, &sequence_display_backlight_enforce_on);
|
|
||||||
|
|
||||||
instance->poller = nfc_poller_alloc(instance->nfc, NfcProtocolMfClassic);
|
instance->poller = nfc_poller_alloc(instance->nfc, NfcProtocolMfClassic);
|
||||||
nfc_poller_start(instance->poller, nfc_dict_attack_worker_callback, instance);
|
nfc_poller_start(instance->poller, nfc_dict_attack_worker_callback, instance);
|
||||||
@@ -392,5 +391,4 @@ void nfc_scene_mf_classic_dict_attack_on_exit(void* context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
nfc_blink_stop(instance);
|
nfc_blink_stop(instance);
|
||||||
notification_message(instance->notifications, &sequence_display_backlight_enforce_auto);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -47,6 +47,9 @@ bool nfc_scene_mf_classic_update_initial_wrong_card_on_event(
|
|||||||
if(event.event == GuiButtonTypeLeft) {
|
if(event.event == GuiButtonTypeLeft) {
|
||||||
consumed = scene_manager_previous_scene(instance->scene_manager);
|
consumed = scene_manager_previous_scene(instance->scene_manager);
|
||||||
}
|
}
|
||||||
|
} else if(event.type == SceneManagerEventTypeBack) {
|
||||||
|
consumed = scene_manager_search_and_switch_to_previous_scene(
|
||||||
|
instance->scene_manager, NfcSceneSavedMenu);
|
||||||
}
|
}
|
||||||
return consumed;
|
return consumed;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,148 +0,0 @@
|
|||||||
#include "../nfc_app_i.h"
|
|
||||||
|
|
||||||
#include <nfc/protocols/mf_classic/mf_classic_poller.h>
|
|
||||||
|
|
||||||
enum {
|
|
||||||
NfcSceneMfClassicWriteInitialStateCardSearch,
|
|
||||||
NfcSceneMfClassicWriteInitialStateCardFound,
|
|
||||||
};
|
|
||||||
|
|
||||||
NfcCommand
|
|
||||||
nfc_scene_mf_classic_write_initial_worker_callback(NfcGenericEvent event, void* context) {
|
|
||||||
furi_assert(context);
|
|
||||||
furi_assert(event.event_data);
|
|
||||||
furi_assert(event.protocol == NfcProtocolMfClassic);
|
|
||||||
|
|
||||||
NfcCommand command = NfcCommandContinue;
|
|
||||||
NfcApp* instance = context;
|
|
||||||
MfClassicPollerEvent* mfc_event = event.event_data;
|
|
||||||
const MfClassicData* write_data =
|
|
||||||
nfc_device_get_data(instance->nfc_device, NfcProtocolMfClassic);
|
|
||||||
|
|
||||||
if(mfc_event->type == MfClassicPollerEventTypeCardDetected) {
|
|
||||||
view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventCardDetected);
|
|
||||||
} else if(mfc_event->type == MfClassicPollerEventTypeCardLost) {
|
|
||||||
view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventCardLost);
|
|
||||||
} else if(mfc_event->type == MfClassicPollerEventTypeRequestMode) {
|
|
||||||
const MfClassicData* tag_data = nfc_poller_get_data(instance->poller);
|
|
||||||
if(iso14443_3a_is_equal(tag_data->iso14443_3a_data, write_data->iso14443_3a_data)) {
|
|
||||||
mfc_event->data->poller_mode.mode = MfClassicPollerModeWrite;
|
|
||||||
} else {
|
|
||||||
view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventWrongCard);
|
|
||||||
command = NfcCommandStop;
|
|
||||||
}
|
|
||||||
} else if(mfc_event->type == MfClassicPollerEventTypeRequestSectorTrailer) {
|
|
||||||
uint8_t sector = mfc_event->data->sec_tr_data.sector_num;
|
|
||||||
uint8_t sec_tr = mf_classic_get_sector_trailer_num_by_sector(sector);
|
|
||||||
if(mf_classic_is_block_read(write_data, sec_tr)) {
|
|
||||||
mfc_event->data->sec_tr_data.sector_trailer = write_data->block[sec_tr];
|
|
||||||
mfc_event->data->sec_tr_data.sector_trailer_provided = true;
|
|
||||||
} else {
|
|
||||||
mfc_event->data->sec_tr_data.sector_trailer_provided = false;
|
|
||||||
}
|
|
||||||
} else if(mfc_event->type == MfClassicPollerEventTypeRequestWriteBlock) {
|
|
||||||
uint8_t block_num = mfc_event->data->write_block_data.block_num;
|
|
||||||
if(mf_classic_is_block_read(write_data, block_num)) {
|
|
||||||
mfc_event->data->write_block_data.write_block = write_data->block[block_num];
|
|
||||||
mfc_event->data->write_block_data.write_block_provided = true;
|
|
||||||
} else {
|
|
||||||
mfc_event->data->write_block_data.write_block_provided = false;
|
|
||||||
}
|
|
||||||
} else if(mfc_event->type == MfClassicPollerEventTypeSuccess) {
|
|
||||||
view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventPollerSuccess);
|
|
||||||
command = NfcCommandStop;
|
|
||||||
} else if(mfc_event->type == MfClassicPollerEventTypeFail) {
|
|
||||||
view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventPollerFailure);
|
|
||||||
command = NfcCommandStop;
|
|
||||||
}
|
|
||||||
return command;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void nfc_scene_mf_classic_write_initial_setup_view(NfcApp* instance) {
|
|
||||||
Popup* popup = instance->popup;
|
|
||||||
popup_reset(popup);
|
|
||||||
uint32_t state =
|
|
||||||
scene_manager_get_scene_state(instance->scene_manager, NfcSceneMfClassicWriteInitial);
|
|
||||||
|
|
||||||
if(state == NfcSceneMfClassicWriteInitialStateCardSearch) {
|
|
||||||
popup_set_header(instance->popup, "Writing", 95, 20, AlignCenter, AlignCenter);
|
|
||||||
popup_set_text(
|
|
||||||
instance->popup, "Use the source\ncard only", 95, 38, AlignCenter, AlignCenter);
|
|
||||||
popup_set_icon(instance->popup, 0, 8, &I_NFC_manual_60x50);
|
|
||||||
} else {
|
|
||||||
popup_set_header(popup, "Writing\nDon't move...", 52, 32, AlignLeft, AlignCenter);
|
|
||||||
popup_set_icon(popup, 12, 23, &A_Loading_24);
|
|
||||||
}
|
|
||||||
|
|
||||||
view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewPopup);
|
|
||||||
}
|
|
||||||
|
|
||||||
void nfc_scene_mf_classic_write_initial_on_enter(void* context) {
|
|
||||||
NfcApp* instance = context;
|
|
||||||
dolphin_deed(DolphinDeedNfcEmulate);
|
|
||||||
|
|
||||||
scene_manager_set_scene_state(
|
|
||||||
instance->scene_manager,
|
|
||||||
NfcSceneMfClassicWriteInitial,
|
|
||||||
NfcSceneMfClassicWriteInitialStateCardSearch);
|
|
||||||
nfc_scene_mf_classic_write_initial_setup_view(instance);
|
|
||||||
|
|
||||||
// Setup and start worker
|
|
||||||
instance->poller = nfc_poller_alloc(instance->nfc, NfcProtocolMfClassic);
|
|
||||||
nfc_poller_start(
|
|
||||||
instance->poller, nfc_scene_mf_classic_write_initial_worker_callback, instance);
|
|
||||||
|
|
||||||
nfc_blink_emulate_start(instance);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool nfc_scene_mf_classic_write_initial_on_event(void* context, SceneManagerEvent event) {
|
|
||||||
NfcApp* instance = context;
|
|
||||||
bool consumed = false;
|
|
||||||
|
|
||||||
if(event.type == SceneManagerEventTypeCustom) {
|
|
||||||
if(event.event == NfcCustomEventCardDetected) {
|
|
||||||
scene_manager_set_scene_state(
|
|
||||||
instance->scene_manager,
|
|
||||||
NfcSceneMfClassicWriteInitial,
|
|
||||||
NfcSceneMfClassicWriteInitialStateCardFound);
|
|
||||||
nfc_scene_mf_classic_write_initial_setup_view(instance);
|
|
||||||
consumed = true;
|
|
||||||
} else if(event.event == NfcCustomEventCardLost) {
|
|
||||||
scene_manager_set_scene_state(
|
|
||||||
instance->scene_manager,
|
|
||||||
NfcSceneMfClassicWriteInitial,
|
|
||||||
NfcSceneMfClassicWriteInitialStateCardSearch);
|
|
||||||
nfc_scene_mf_classic_write_initial_setup_view(instance);
|
|
||||||
consumed = true;
|
|
||||||
} else if(event.event == NfcCustomEventWrongCard) {
|
|
||||||
scene_manager_next_scene(
|
|
||||||
instance->scene_manager, NfcSceneMfClassicWriteInitialWrongCard);
|
|
||||||
consumed = true;
|
|
||||||
} else if(event.event == NfcCustomEventPollerSuccess) {
|
|
||||||
scene_manager_next_scene(
|
|
||||||
instance->scene_manager, NfcSceneMfClassicWriteInitialSuccess);
|
|
||||||
consumed = true;
|
|
||||||
} else if(event.event == NfcCustomEventPollerFailure) {
|
|
||||||
scene_manager_next_scene(instance->scene_manager, NfcSceneMfClassicWriteInitialFail);
|
|
||||||
consumed = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return consumed;
|
|
||||||
}
|
|
||||||
|
|
||||||
void nfc_scene_mf_classic_write_initial_on_exit(void* context) {
|
|
||||||
NfcApp* instance = context;
|
|
||||||
|
|
||||||
nfc_poller_stop(instance->poller);
|
|
||||||
nfc_poller_free(instance->poller);
|
|
||||||
|
|
||||||
scene_manager_set_scene_state(
|
|
||||||
instance->scene_manager,
|
|
||||||
NfcSceneMfClassicWriteInitial,
|
|
||||||
NfcSceneMfClassicWriteInitialStateCardSearch);
|
|
||||||
// Clear view
|
|
||||||
popup_reset(instance->popup);
|
|
||||||
|
|
||||||
nfc_blink_stop(instance);
|
|
||||||
}
|
|
||||||
@@ -1,62 +0,0 @@
|
|||||||
#include "../nfc_app_i.h"
|
|
||||||
|
|
||||||
void nfc_scene_mf_classic_write_initial_fail_widget_callback(
|
|
||||||
GuiButtonType result,
|
|
||||||
InputType type,
|
|
||||||
void* context) {
|
|
||||||
NfcApp* instance = context;
|
|
||||||
if(type == InputTypeShort) {
|
|
||||||
view_dispatcher_send_custom_event(instance->view_dispatcher, result);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void nfc_scene_mf_classic_write_initial_fail_on_enter(void* context) {
|
|
||||||
NfcApp* instance = context;
|
|
||||||
Widget* widget = instance->widget;
|
|
||||||
|
|
||||||
notification_message(instance->notifications, &sequence_error);
|
|
||||||
|
|
||||||
widget_add_icon_element(widget, 83, 22, &I_WarningDolphinFlip_45x42);
|
|
||||||
widget_add_string_element(
|
|
||||||
widget, 7, 4, AlignLeft, AlignTop, FontPrimary, "Writing gone wrong!");
|
|
||||||
widget_add_string_multiline_element(
|
|
||||||
widget,
|
|
||||||
7,
|
|
||||||
17,
|
|
||||||
AlignLeft,
|
|
||||||
AlignTop,
|
|
||||||
FontSecondary,
|
|
||||||
"Not all sectors\nwere written\ncorrectly.");
|
|
||||||
|
|
||||||
widget_add_button_element(
|
|
||||||
widget,
|
|
||||||
GuiButtonTypeLeft,
|
|
||||||
"Finish",
|
|
||||||
nfc_scene_mf_classic_write_initial_fail_widget_callback,
|
|
||||||
instance);
|
|
||||||
|
|
||||||
// Setup and start worker
|
|
||||||
view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewWidget);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool nfc_scene_mf_classic_write_initial_fail_on_event(void* context, SceneManagerEvent event) {
|
|
||||||
NfcApp* instance = context;
|
|
||||||
bool consumed = false;
|
|
||||||
|
|
||||||
if(event.type == SceneManagerEventTypeCustom) {
|
|
||||||
if(event.event == GuiButtonTypeLeft) {
|
|
||||||
consumed = scene_manager_search_and_switch_to_previous_scene(
|
|
||||||
instance->scene_manager, NfcSceneSavedMenu);
|
|
||||||
}
|
|
||||||
} else if(event.type == SceneManagerEventTypeBack) {
|
|
||||||
consumed = scene_manager_search_and_switch_to_previous_scene(
|
|
||||||
instance->scene_manager, NfcSceneSavedMenu);
|
|
||||||
}
|
|
||||||
return consumed;
|
|
||||||
}
|
|
||||||
|
|
||||||
void nfc_scene_mf_classic_write_initial_fail_on_exit(void* context) {
|
|
||||||
NfcApp* instance = context;
|
|
||||||
|
|
||||||
widget_reset(instance->widget);
|
|
||||||
}
|
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
#include "../nfc_app_i.h"
|
|
||||||
|
|
||||||
void nfc_scene_mf_classic_write_initial_success_popup_callback(void* context) {
|
|
||||||
NfcApp* instance = context;
|
|
||||||
view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventViewExit);
|
|
||||||
}
|
|
||||||
|
|
||||||
void nfc_scene_mf_classic_write_initial_success_on_enter(void* context) {
|
|
||||||
NfcApp* instance = context;
|
|
||||||
dolphin_deed(DolphinDeedNfcSave);
|
|
||||||
|
|
||||||
notification_message(instance->notifications, &sequence_success);
|
|
||||||
|
|
||||||
Popup* popup = instance->popup;
|
|
||||||
popup_set_header(popup, "Success!", 75, 10, AlignLeft, AlignTop);
|
|
||||||
popup_set_icon(popup, 0, 9, &I_DolphinSuccess_91x55);
|
|
||||||
popup_set_timeout(popup, 1500);
|
|
||||||
popup_set_context(popup, instance);
|
|
||||||
popup_set_callback(popup, nfc_scene_mf_classic_write_initial_success_popup_callback);
|
|
||||||
popup_enable_timeout(popup);
|
|
||||||
|
|
||||||
view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewPopup);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool nfc_scene_mf_classic_write_initial_success_on_event(void* context, SceneManagerEvent event) {
|
|
||||||
NfcApp* instance = context;
|
|
||||||
bool consumed = false;
|
|
||||||
|
|
||||||
if(event.type == SceneManagerEventTypeCustom) {
|
|
||||||
if(event.event == NfcCustomEventViewExit) {
|
|
||||||
consumed = scene_manager_search_and_switch_to_previous_scene(
|
|
||||||
instance->scene_manager, NfcSceneSavedMenu);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return consumed;
|
|
||||||
}
|
|
||||||
|
|
||||||
void nfc_scene_mf_classic_write_initial_success_on_exit(void* context) {
|
|
||||||
NfcApp* instance = context;
|
|
||||||
|
|
||||||
// Clear view
|
|
||||||
popup_reset(instance->popup);
|
|
||||||
}
|
|
||||||
@@ -1,57 +0,0 @@
|
|||||||
#include "../nfc_app_i.h"
|
|
||||||
|
|
||||||
void nfc_scene_mf_classic_write_initial_wrong_card_widget_callback(
|
|
||||||
GuiButtonType result,
|
|
||||||
InputType type,
|
|
||||||
void* context) {
|
|
||||||
NfcApp* instance = context;
|
|
||||||
if(type == InputTypeShort) {
|
|
||||||
view_dispatcher_send_custom_event(instance->view_dispatcher, result);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void nfc_scene_mf_classic_write_initial_wrong_card_on_enter(void* context) {
|
|
||||||
NfcApp* instance = context;
|
|
||||||
Widget* widget = instance->widget;
|
|
||||||
|
|
||||||
notification_message(instance->notifications, &sequence_error);
|
|
||||||
|
|
||||||
widget_add_icon_element(widget, 83, 22, &I_WarningDolphinFlip_45x42);
|
|
||||||
widget_add_string_element(
|
|
||||||
widget, 3, 4, AlignLeft, AlignTop, FontPrimary, "Use The Source Card!");
|
|
||||||
widget_add_string_multiline_element(
|
|
||||||
widget,
|
|
||||||
4,
|
|
||||||
17,
|
|
||||||
AlignLeft,
|
|
||||||
AlignTop,
|
|
||||||
FontSecondary,
|
|
||||||
"Go to NFC Magic\napp if you want to\nwrite blanks");
|
|
||||||
widget_add_button_element(
|
|
||||||
widget,
|
|
||||||
GuiButtonTypeLeft,
|
|
||||||
"Retry",
|
|
||||||
nfc_scene_mf_classic_write_initial_wrong_card_widget_callback,
|
|
||||||
instance);
|
|
||||||
|
|
||||||
// Setup and start worker
|
|
||||||
view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewWidget);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool nfc_scene_mf_classic_write_initial_wrong_card_on_event(void* context, SceneManagerEvent event) {
|
|
||||||
NfcApp* instance = context;
|
|
||||||
bool consumed = false;
|
|
||||||
|
|
||||||
if(event.type == SceneManagerEventTypeCustom) {
|
|
||||||
if(event.event == GuiButtonTypeLeft) {
|
|
||||||
consumed = scene_manager_previous_scene(instance->scene_manager);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return consumed;
|
|
||||||
}
|
|
||||||
|
|
||||||
void nfc_scene_mf_classic_write_initial_wrong_card_on_exit(void* context) {
|
|
||||||
NfcApp* instance = context;
|
|
||||||
|
|
||||||
widget_reset(instance->widget);
|
|
||||||
}
|
|
||||||
@@ -1,120 +0,0 @@
|
|||||||
#include "../nfc_app_i.h"
|
|
||||||
|
|
||||||
#include <nfc/protocols/mf_ultralight/mf_ultralight_poller.h>
|
|
||||||
|
|
||||||
enum {
|
|
||||||
NfcSceneMfUltralightWriteStateCardSearch,
|
|
||||||
NfcSceneMfUltralightWriteStateCardFound,
|
|
||||||
};
|
|
||||||
|
|
||||||
NfcCommand nfc_scene_mf_ultralight_write_worker_callback(NfcGenericEvent event, void* context) {
|
|
||||||
furi_assert(context);
|
|
||||||
furi_assert(event.event_data);
|
|
||||||
furi_assert(event.protocol == NfcProtocolMfUltralight);
|
|
||||||
|
|
||||||
NfcCommand command = NfcCommandContinue;
|
|
||||||
NfcApp* instance = context;
|
|
||||||
MfUltralightPollerEvent* mfu_event = event.event_data;
|
|
||||||
|
|
||||||
if(mfu_event->type == MfUltralightPollerEventTypeRequestMode) {
|
|
||||||
mfu_event->data->poller_mode = MfUltralightPollerModeWrite;
|
|
||||||
view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventCardDetected);
|
|
||||||
} else if(mfu_event->type == MfUltralightPollerEventTypeAuthRequest) {
|
|
||||||
mfu_event->data->auth_context.skip_auth = true;
|
|
||||||
} else if(mfu_event->type == MfUltralightPollerEventTypeRequestWriteData) {
|
|
||||||
mfu_event->data->write_data =
|
|
||||||
nfc_device_get_data(instance->nfc_device, NfcProtocolMfUltralight);
|
|
||||||
} else if(mfu_event->type == MfUltralightPollerEventTypeCardMismatch) {
|
|
||||||
view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventWrongCard);
|
|
||||||
command = NfcCommandStop;
|
|
||||||
} else if(mfu_event->type == MfUltralightPollerEventTypeCardLocked) {
|
|
||||||
view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventPollerFailure);
|
|
||||||
command = NfcCommandStop;
|
|
||||||
} else if(mfu_event->type == MfUltralightPollerEventTypeWriteFail) {
|
|
||||||
command = NfcCommandStop;
|
|
||||||
} else if(mfu_event->type == MfUltralightPollerEventTypeWriteSuccess) {
|
|
||||||
view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventPollerSuccess);
|
|
||||||
command = NfcCommandStop;
|
|
||||||
}
|
|
||||||
return command;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void nfc_scene_mf_ultralight_write_setup_view(NfcApp* instance) {
|
|
||||||
Popup* popup = instance->popup;
|
|
||||||
popup_reset(popup);
|
|
||||||
uint32_t state =
|
|
||||||
scene_manager_get_scene_state(instance->scene_manager, NfcSceneMfUltralightWrite);
|
|
||||||
|
|
||||||
if(state == NfcSceneMfUltralightWriteStateCardSearch) {
|
|
||||||
popup_set_header(instance->popup, "Writing", 95, 20, AlignCenter, AlignCenter);
|
|
||||||
popup_set_text(
|
|
||||||
instance->popup, "Apply the initial\ncard only", 95, 38, AlignCenter, AlignCenter);
|
|
||||||
popup_set_icon(instance->popup, 0, 8, &I_NFC_manual_60x50);
|
|
||||||
} else {
|
|
||||||
popup_set_header(popup, "Writing\nDon't move...", 52, 32, AlignLeft, AlignCenter);
|
|
||||||
popup_set_icon(popup, 12, 23, &A_Loading_24);
|
|
||||||
}
|
|
||||||
|
|
||||||
view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewPopup);
|
|
||||||
}
|
|
||||||
|
|
||||||
void nfc_scene_mf_ultralight_write_on_enter(void* context) {
|
|
||||||
NfcApp* instance = context;
|
|
||||||
dolphin_deed(DolphinDeedNfcEmulate);
|
|
||||||
|
|
||||||
scene_manager_set_scene_state(
|
|
||||||
instance->scene_manager,
|
|
||||||
NfcSceneMfUltralightWrite,
|
|
||||||
NfcSceneMfUltralightWriteStateCardSearch);
|
|
||||||
nfc_scene_mf_ultralight_write_setup_view(instance);
|
|
||||||
|
|
||||||
// Setup and start worker
|
|
||||||
FURI_LOG_D("WMFU", "Card searching...");
|
|
||||||
instance->poller = nfc_poller_alloc(instance->nfc, NfcProtocolMfUltralight);
|
|
||||||
nfc_poller_start(instance->poller, nfc_scene_mf_ultralight_write_worker_callback, instance);
|
|
||||||
|
|
||||||
nfc_blink_emulate_start(instance);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool nfc_scene_mf_ultralight_write_on_event(void* context, SceneManagerEvent event) {
|
|
||||||
NfcApp* instance = context;
|
|
||||||
bool consumed = false;
|
|
||||||
|
|
||||||
if(event.type == SceneManagerEventTypeCustom) {
|
|
||||||
if(event.event == NfcCustomEventCardDetected) {
|
|
||||||
scene_manager_set_scene_state(
|
|
||||||
instance->scene_manager,
|
|
||||||
NfcSceneMfUltralightWrite,
|
|
||||||
NfcSceneMfUltralightWriteStateCardFound);
|
|
||||||
nfc_scene_mf_ultralight_write_setup_view(instance);
|
|
||||||
consumed = true;
|
|
||||||
} else if(event.event == NfcCustomEventWrongCard) {
|
|
||||||
scene_manager_next_scene(instance->scene_manager, NfcSceneMfUltralightWrongCard);
|
|
||||||
consumed = true;
|
|
||||||
} else if(event.event == NfcCustomEventPollerSuccess) {
|
|
||||||
scene_manager_next_scene(instance->scene_manager, NfcSceneMfUltralightWriteSuccess);
|
|
||||||
consumed = true;
|
|
||||||
} else if(event.event == NfcCustomEventPollerFailure) {
|
|
||||||
scene_manager_next_scene(instance->scene_manager, NfcSceneMfUltralightWriteFail);
|
|
||||||
consumed = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return consumed;
|
|
||||||
}
|
|
||||||
|
|
||||||
void nfc_scene_mf_ultralight_write_on_exit(void* context) {
|
|
||||||
NfcApp* instance = context;
|
|
||||||
|
|
||||||
nfc_poller_stop(instance->poller);
|
|
||||||
nfc_poller_free(instance->poller);
|
|
||||||
|
|
||||||
scene_manager_set_scene_state(
|
|
||||||
instance->scene_manager,
|
|
||||||
NfcSceneMfUltralightWrite,
|
|
||||||
NfcSceneMfUltralightWriteStateCardSearch);
|
|
||||||
// Clear view
|
|
||||||
popup_reset(instance->popup);
|
|
||||||
|
|
||||||
nfc_blink_stop(instance);
|
|
||||||
}
|
|
||||||
@@ -1,67 +0,0 @@
|
|||||||
#include "../nfc_app_i.h"
|
|
||||||
|
|
||||||
void nfc_scene_mf_ultralight_write_fail_widget_callback(
|
|
||||||
GuiButtonType result,
|
|
||||||
InputType type,
|
|
||||||
void* context) {
|
|
||||||
NfcApp* instance = context;
|
|
||||||
if(type == InputTypeShort) {
|
|
||||||
view_dispatcher_send_custom_event(instance->view_dispatcher, result);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void nfc_scene_mf_ultralight_write_fail_on_enter(void* context) {
|
|
||||||
NfcApp* instance = context;
|
|
||||||
Widget* widget = instance->widget;
|
|
||||||
|
|
||||||
notification_message(instance->notifications, &sequence_error);
|
|
||||||
|
|
||||||
widget_add_icon_element(widget, 83, 22, &I_WarningDolphinFlip_45x42);
|
|
||||||
widget_add_string_element(
|
|
||||||
widget, 7, 4, AlignLeft, AlignTop, FontPrimary, "Writing gone wrong!");
|
|
||||||
widget_add_string_multiline_element(
|
|
||||||
widget,
|
|
||||||
7,
|
|
||||||
17,
|
|
||||||
AlignLeft,
|
|
||||||
AlignTop,
|
|
||||||
FontSecondary,
|
|
||||||
"Card protected by\npassword, AUTH0\nor lock bits");
|
|
||||||
|
|
||||||
widget_add_button_element(
|
|
||||||
widget,
|
|
||||||
GuiButtonTypeLeft,
|
|
||||||
"Finish",
|
|
||||||
nfc_scene_mf_ultralight_write_fail_widget_callback,
|
|
||||||
instance);
|
|
||||||
|
|
||||||
// Setup and start worker
|
|
||||||
view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewWidget);
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool nfc_scene_mf_ultralight_write_fail_move_to_back_scene(const NfcApp* const instance) {
|
|
||||||
bool was_saved = scene_manager_has_previous_scene(instance->scene_manager, NfcSceneSavedMenu);
|
|
||||||
uint32_t scene_id = was_saved ? NfcSceneSavedMenu : NfcSceneReadMenu;
|
|
||||||
|
|
||||||
return scene_manager_search_and_switch_to_previous_scene(instance->scene_manager, scene_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool nfc_scene_mf_ultralight_write_fail_on_event(void* context, SceneManagerEvent event) {
|
|
||||||
NfcApp* instance = context;
|
|
||||||
bool consumed = false;
|
|
||||||
|
|
||||||
if(event.type == SceneManagerEventTypeCustom) {
|
|
||||||
if(event.event == GuiButtonTypeLeft) {
|
|
||||||
consumed = nfc_scene_mf_ultralight_write_fail_move_to_back_scene(instance);
|
|
||||||
}
|
|
||||||
} else if(event.type == SceneManagerEventTypeBack) {
|
|
||||||
consumed = nfc_scene_mf_ultralight_write_fail_move_to_back_scene(instance);
|
|
||||||
}
|
|
||||||
return consumed;
|
|
||||||
}
|
|
||||||
|
|
||||||
void nfc_scene_mf_ultralight_write_fail_on_exit(void* context) {
|
|
||||||
NfcApp* instance = context;
|
|
||||||
|
|
||||||
widget_reset(instance->widget);
|
|
||||||
}
|
|
||||||
@@ -1,46 +0,0 @@
|
|||||||
#include "../nfc_app_i.h"
|
|
||||||
|
|
||||||
void nfc_scene_mf_ultralight_write_success_popup_callback(void* context) {
|
|
||||||
NfcApp* instance = context;
|
|
||||||
view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventViewExit);
|
|
||||||
}
|
|
||||||
|
|
||||||
void nfc_scene_mf_ultralight_write_success_on_enter(void* context) {
|
|
||||||
NfcApp* instance = context;
|
|
||||||
dolphin_deed(DolphinDeedNfcSave);
|
|
||||||
|
|
||||||
notification_message(instance->notifications, &sequence_success);
|
|
||||||
|
|
||||||
Popup* popup = instance->popup;
|
|
||||||
popup_set_icon(popup, 48, 6, &I_DolphinDone_80x58);
|
|
||||||
popup_set_header(popup, "Successfully\nwritten", 5, 22, AlignLeft, AlignBottom);
|
|
||||||
popup_set_timeout(popup, 1500);
|
|
||||||
popup_set_context(popup, instance);
|
|
||||||
popup_set_callback(popup, nfc_scene_mf_ultralight_write_success_popup_callback);
|
|
||||||
popup_enable_timeout(popup);
|
|
||||||
|
|
||||||
view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewPopup);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool nfc_scene_mf_ultralight_write_success_on_event(void* context, SceneManagerEvent event) {
|
|
||||||
NfcApp* instance = context;
|
|
||||||
bool consumed = false;
|
|
||||||
|
|
||||||
if(event.type == SceneManagerEventTypeCustom) {
|
|
||||||
if(event.event == NfcCustomEventViewExit) {
|
|
||||||
bool was_saved =
|
|
||||||
scene_manager_has_previous_scene(instance->scene_manager, NfcSceneSavedMenu);
|
|
||||||
|
|
||||||
consumed = scene_manager_search_and_switch_to_previous_scene(
|
|
||||||
instance->scene_manager, was_saved ? NfcSceneSavedMenu : NfcSceneReadSuccess);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return consumed;
|
|
||||||
}
|
|
||||||
|
|
||||||
void nfc_scene_mf_ultralight_write_success_on_exit(void* context) {
|
|
||||||
NfcApp* instance = context;
|
|
||||||
|
|
||||||
// Clear view
|
|
||||||
popup_reset(instance->popup);
|
|
||||||
}
|
|
||||||
@@ -1,58 +0,0 @@
|
|||||||
#include "../nfc_app_i.h"
|
|
||||||
|
|
||||||
void nfc_scene_mf_ultralight_wrong_card_widget_callback(
|
|
||||||
GuiButtonType result,
|
|
||||||
InputType type,
|
|
||||||
void* context) {
|
|
||||||
NfcApp* instance = context;
|
|
||||||
if(type == InputTypeShort) {
|
|
||||||
view_dispatcher_send_custom_event(instance->view_dispatcher, result);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void nfc_scene_mf_ultralight_wrong_card_on_enter(void* context) {
|
|
||||||
NfcApp* instance = context;
|
|
||||||
Widget* widget = instance->widget;
|
|
||||||
|
|
||||||
notification_message(instance->notifications, &sequence_error);
|
|
||||||
|
|
||||||
widget_add_icon_element(widget, 83, 22, &I_WarningDolphinFlip_45x42);
|
|
||||||
widget_add_string_element(
|
|
||||||
widget, 3, 4, AlignLeft, AlignTop, FontPrimary, "This is wrong card");
|
|
||||||
widget_add_string_multiline_element(
|
|
||||||
widget,
|
|
||||||
4,
|
|
||||||
17,
|
|
||||||
AlignLeft,
|
|
||||||
AlignTop,
|
|
||||||
FontSecondary,
|
|
||||||
"Card of the same\ntype should be\n presented");
|
|
||||||
//"Data management\nis only possible\nwith card of same type");
|
|
||||||
widget_add_button_element(
|
|
||||||
widget,
|
|
||||||
GuiButtonTypeLeft,
|
|
||||||
"Retry",
|
|
||||||
nfc_scene_mf_ultralight_wrong_card_widget_callback,
|
|
||||||
instance);
|
|
||||||
|
|
||||||
// Setup and start worker
|
|
||||||
view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewWidget);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool nfc_scene_mf_ultralight_wrong_card_on_event(void* context, SceneManagerEvent event) {
|
|
||||||
NfcApp* instance = context;
|
|
||||||
bool consumed = false;
|
|
||||||
|
|
||||||
if(event.type == SceneManagerEventTypeCustom) {
|
|
||||||
if(event.event == GuiButtonTypeLeft) {
|
|
||||||
consumed = scene_manager_previous_scene(instance->scene_manager);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return consumed;
|
|
||||||
}
|
|
||||||
|
|
||||||
void nfc_scene_mf_ultralight_wrong_card_on_exit(void* context) {
|
|
||||||
NfcApp* instance = context;
|
|
||||||
|
|
||||||
widget_reset(instance->widget);
|
|
||||||
}
|
|
||||||
@@ -1,6 +1,12 @@
|
|||||||
#include "../helpers/protocol_support/nfc_protocol_support.h"
|
#include "../helpers/protocol_support/nfc_protocol_support.h"
|
||||||
|
#include "../nfc_app_i.h"
|
||||||
|
|
||||||
void nfc_scene_read_on_enter(void* context) {
|
void nfc_scene_read_on_enter(void* context) {
|
||||||
|
NfcApp* instance = context;
|
||||||
|
nfc_show_loading_popup(instance, true);
|
||||||
|
nfc_supported_cards_load_cache(instance->nfc_supported_cards);
|
||||||
|
nfc_show_loading_popup(instance, false);
|
||||||
|
|
||||||
nfc_protocol_support_on_enter(NfcProtocolSupportSceneRead, context);
|
nfc_protocol_support_on_enter(NfcProtocolSupportSceneRead, context);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -56,37 +56,25 @@ bool nfc_scene_start_on_event(void* context, SceneManagerEvent event) {
|
|||||||
bool consumed = false;
|
bool consumed = false;
|
||||||
|
|
||||||
if(event.type == SceneManagerEventTypeCustom) {
|
if(event.type == SceneManagerEventTypeCustom) {
|
||||||
|
consumed = true;
|
||||||
if(event.event == SubmenuIndexRead) {
|
if(event.event == SubmenuIndexRead) {
|
||||||
scene_manager_set_scene_state(nfc->scene_manager, NfcSceneStart, SubmenuIndexRead);
|
|
||||||
scene_manager_next_scene(nfc->scene_manager, NfcSceneDetect);
|
scene_manager_next_scene(nfc->scene_manager, NfcSceneDetect);
|
||||||
dolphin_deed(DolphinDeedNfcRead);
|
dolphin_deed(DolphinDeedNfcRead);
|
||||||
consumed = true;
|
|
||||||
} else if(event.event == SubmenuIndexDetectReader) {
|
} else if(event.event == SubmenuIndexDetectReader) {
|
||||||
scene_manager_set_scene_state(
|
|
||||||
nfc->scene_manager, NfcSceneStart, SubmenuIndexDetectReader);
|
|
||||||
scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicDetectReader);
|
scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicDetectReader);
|
||||||
consumed = true;
|
|
||||||
} else if(event.event == SubmenuIndexSaved) {
|
} else if(event.event == SubmenuIndexSaved) {
|
||||||
// Save the scene state explicitly in each branch, so that
|
|
||||||
// if the user cancels loading a file, the Saved menu item
|
|
||||||
// is properly reselected.
|
|
||||||
scene_manager_set_scene_state(nfc->scene_manager, NfcSceneStart, SubmenuIndexSaved);
|
|
||||||
scene_manager_next_scene(nfc->scene_manager, NfcSceneFileSelect);
|
scene_manager_next_scene(nfc->scene_manager, NfcSceneFileSelect);
|
||||||
consumed = true;
|
|
||||||
} else if(event.event == SubmenuIndexExtraAction) {
|
} else if(event.event == SubmenuIndexExtraAction) {
|
||||||
scene_manager_set_scene_state(
|
|
||||||
nfc->scene_manager, NfcSceneStart, SubmenuIndexExtraAction);
|
|
||||||
scene_manager_next_scene(nfc->scene_manager, NfcSceneExtraActions);
|
scene_manager_next_scene(nfc->scene_manager, NfcSceneExtraActions);
|
||||||
consumed = true;
|
|
||||||
} else if(event.event == SubmenuIndexAddManually) {
|
} else if(event.event == SubmenuIndexAddManually) {
|
||||||
scene_manager_set_scene_state(
|
|
||||||
nfc->scene_manager, NfcSceneStart, SubmenuIndexAddManually);
|
|
||||||
scene_manager_next_scene(nfc->scene_manager, NfcSceneSetType);
|
scene_manager_next_scene(nfc->scene_manager, NfcSceneSetType);
|
||||||
consumed = true;
|
|
||||||
} else if(event.event == SubmenuIndexDebug) {
|
} else if(event.event == SubmenuIndexDebug) {
|
||||||
scene_manager_set_scene_state(nfc->scene_manager, NfcSceneStart, SubmenuIndexDebug);
|
|
||||||
scene_manager_next_scene(nfc->scene_manager, NfcSceneDebug);
|
scene_manager_next_scene(nfc->scene_manager, NfcSceneDebug);
|
||||||
consumed = true;
|
} else {
|
||||||
|
consumed = false;
|
||||||
|
}
|
||||||
|
if(consumed) {
|
||||||
|
scene_manager_set_scene_state(nfc->scene_manager, NfcSceneStart, event.event);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return consumed;
|
return consumed;
|
||||||
|
|||||||
13
applications/main/nfc/scenes/nfc_scene_write.c
Normal file
13
applications/main/nfc/scenes/nfc_scene_write.c
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
#include "../helpers/protocol_support/nfc_protocol_support.h"
|
||||||
|
|
||||||
|
void nfc_scene_write_on_enter(void* context) {
|
||||||
|
nfc_protocol_support_on_enter(NfcProtocolSupportSceneWrite, context);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool nfc_scene_write_on_event(void* context, SceneManagerEvent event) {
|
||||||
|
return nfc_protocol_support_on_event(NfcProtocolSupportSceneWrite, context, event);
|
||||||
|
}
|
||||||
|
|
||||||
|
void nfc_scene_write_on_exit(void* context) {
|
||||||
|
nfc_protocol_support_on_exit(NfcProtocolSupportSceneWrite, context);
|
||||||
|
}
|
||||||
@@ -74,16 +74,20 @@ typedef enum {
|
|||||||
SetTypeSomfyTelis,
|
SetTypeSomfyTelis,
|
||||||
SetTypeANMotorsAT4,
|
SetTypeANMotorsAT4,
|
||||||
SetTypeAlutechAT4N,
|
SetTypeAlutechAT4N,
|
||||||
|
SetTypePhoenix_V2_433,
|
||||||
SetTypeHCS101_433_92,
|
SetTypeHCS101_433_92,
|
||||||
SetTypeDoorHan_315_00,
|
SetTypeDoorHan_315_00,
|
||||||
SetTypeDoorHan_433_92,
|
SetTypeDoorHan_433_92,
|
||||||
SetTypeBeninca433,
|
SetTypeBeninca433,
|
||||||
SetTypeBeninca868,
|
SetTypeBeninca868,
|
||||||
|
SetTypeComunello433,
|
||||||
|
SetTypeComunello868,
|
||||||
SetTypeAllmatic433,
|
SetTypeAllmatic433,
|
||||||
SetTypeAllmatic868,
|
SetTypeAllmatic868,
|
||||||
SetTypeCenturion433,
|
SetTypeCenturion433,
|
||||||
SetTypeMonarch433,
|
SetTypeMonarch433,
|
||||||
SetTypeJollyMotors433,
|
SetTypeJollyMotors433,
|
||||||
|
SetTypeMotorline433,
|
||||||
SetTypeSommer_FM_434,
|
SetTypeSommer_FM_434,
|
||||||
SetTypeSommer_FM_868,
|
SetTypeSommer_FM_868,
|
||||||
SetTypeSommer_FM238_434,
|
SetTypeSommer_FM238_434,
|
||||||
@@ -124,6 +128,9 @@ typedef enum {
|
|||||||
SetTypeHollarm_433,
|
SetTypeHollarm_433,
|
||||||
SetTypeReversRB2_433,
|
SetTypeReversRB2_433,
|
||||||
SetTypeMarantec24_868,
|
SetTypeMarantec24_868,
|
||||||
|
SetTypeMarantec_433,
|
||||||
|
SetTypeMarantec_868,
|
||||||
|
SetTypeRoger_433,
|
||||||
SetTypeLinear_300_00,
|
SetTypeLinear_300_00,
|
||||||
// SetTypeNeroSketch, //Deleted in OFW
|
// SetTypeNeroSketch, //Deleted in OFW
|
||||||
// SetTypeNeroRadio, //Deleted in OFW
|
// SetTypeNeroRadio, //Deleted in OFW
|
||||||
|
|||||||
@@ -118,7 +118,8 @@ static int32_t subghz_frequency_analyzer_worker_thread(void* context) {
|
|||||||
for(size_t i = 0; i < subghz_setting_get_frequency_count(instance->setting); i++) {
|
for(size_t i = 0; i < subghz_setting_get_frequency_count(instance->setting); i++) {
|
||||||
uint32_t current_frequency = subghz_setting_get_frequency(instance->setting, i);
|
uint32_t current_frequency = subghz_setting_get_frequency(instance->setting, i);
|
||||||
if(furi_hal_subghz_is_frequency_valid(current_frequency) &&
|
if(furi_hal_subghz_is_frequency_valid(current_frequency) &&
|
||||||
(((current_frequency != 467750000) && (current_frequency != 464000000)) &&
|
(((current_frequency != 462750000) && (current_frequency != 467750000) &&
|
||||||
|
(current_frequency != 464000000)) &&
|
||||||
(current_frequency <= 920000000))) {
|
(current_frequency <= 920000000))) {
|
||||||
furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz);
|
furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz);
|
||||||
cc1101_switch_to_idle(&furi_hal_spi_bus_handle_subghz);
|
cc1101_switch_to_idle(&furi_hal_spi_bus_handle_subghz);
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
#include <lib/subghz/protocols/secplus_v1.h>
|
#include <lib/subghz/protocols/secplus_v1.h>
|
||||||
#include <lib/subghz/protocols/secplus_v2.h>
|
#include <lib/subghz/protocols/secplus_v2.h>
|
||||||
#include <lib/subghz/protocols/nice_flor_s.h>
|
#include <lib/subghz/protocols/nice_flor_s.h>
|
||||||
|
#include <lib/subghz/protocols/marantec.h>
|
||||||
|
|
||||||
#include <flipper_format/flipper_format_i.h>
|
#include <flipper_format/flipper_format_i.h>
|
||||||
#include <lib/toolbox/stream/stream.h>
|
#include <lib/toolbox/stream/stream.h>
|
||||||
@@ -383,6 +384,34 @@ bool subghz_txrx_gen_secplus_v1_protocol(
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool subghz_txrx_gen_phoenix_v2_protocol(
|
||||||
|
void* context,
|
||||||
|
const char* preset_name,
|
||||||
|
uint32_t frequency,
|
||||||
|
uint32_t serial,
|
||||||
|
uint16_t cnt) {
|
||||||
|
SubGhzTxRx* txrx = context;
|
||||||
|
|
||||||
|
bool res = false;
|
||||||
|
|
||||||
|
txrx->transmitter =
|
||||||
|
subghz_transmitter_alloc_init(txrx->environment, SUBGHZ_PROTOCOL_PHOENIX_V2_NAME);
|
||||||
|
subghz_txrx_set_preset(txrx, preset_name, frequency, NULL, 0);
|
||||||
|
|
||||||
|
if(txrx->transmitter && subghz_protocol_phoenix_v2_create_data(
|
||||||
|
subghz_transmitter_get_protocol_instance(txrx->transmitter),
|
||||||
|
txrx->fff_data,
|
||||||
|
serial,
|
||||||
|
cnt,
|
||||||
|
txrx->preset)) {
|
||||||
|
res = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
subghz_transmitter_free(txrx->transmitter);
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
void subghz_txrx_gen_serial_gangqi(uint64_t* result_key) {
|
void subghz_txrx_gen_serial_gangqi(uint64_t* result_key) {
|
||||||
uint64_t randkey = (uint64_t)rand();
|
uint64_t randkey = (uint64_t)rand();
|
||||||
uint16_t serial = (uint16_t)((randkey) & 0xFFFF);
|
uint16_t serial = (uint16_t)((randkey) & 0xFFFF);
|
||||||
@@ -395,3 +424,27 @@ void subghz_txrx_gen_serial_gangqi(uint64_t* result_key) {
|
|||||||
// serial | const_and_button
|
// serial | const_and_button
|
||||||
*result_key = (serial << 18) | (const_and_button << 10) | (bytesum << 2);
|
*result_key = (serial << 18) | (const_and_button << 10) | (bytesum << 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void subghz_txrx_gen_key_marantec(uint64_t* result_key) {
|
||||||
|
uint64_t randkey = (uint64_t)rand();
|
||||||
|
uint32_t serial = (uint32_t)((randkey) & 0xFFFFF);
|
||||||
|
// 0x130 is the constant
|
||||||
|
// 0x4 is the button code
|
||||||
|
// 0x86 is the serial constant
|
||||||
|
// serial is random value that we pre generate above
|
||||||
|
// At the end we will put the crc sum
|
||||||
|
uint64_t full_key_no_crc = (uint64_t)((uint64_t)0x130 << 40 | (uint64_t)serial << 20 |
|
||||||
|
(uint64_t)0x4 << 16 | (uint64_t)0x86 << 8);
|
||||||
|
|
||||||
|
uint8_t tdata[6] = {
|
||||||
|
full_key_no_crc >> 48,
|
||||||
|
full_key_no_crc >> 40,
|
||||||
|
full_key_no_crc >> 32,
|
||||||
|
full_key_no_crc >> 24,
|
||||||
|
full_key_no_crc >> 16,
|
||||||
|
full_key_no_crc >> 8};
|
||||||
|
|
||||||
|
uint8_t crc = subghz_protocol_marantec_crc8(tdata, sizeof(tdata));
|
||||||
|
|
||||||
|
*result_key = ((full_key_no_crc >> 8) << 8) | crc;
|
||||||
|
}
|
||||||
|
|||||||
@@ -115,6 +115,13 @@ bool subghz_txrx_gen_came_atomo_protocol(
|
|||||||
uint32_t serial,
|
uint32_t serial,
|
||||||
uint16_t cnt);
|
uint16_t cnt);
|
||||||
|
|
||||||
|
bool subghz_txrx_gen_phoenix_v2_protocol(
|
||||||
|
void* context,
|
||||||
|
const char* preset_name,
|
||||||
|
uint32_t frequency,
|
||||||
|
uint32_t serial,
|
||||||
|
uint16_t cnt);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate data SecPlus v2 protocol
|
* Generate data SecPlus v2 protocol
|
||||||
*
|
*
|
||||||
@@ -153,3 +160,10 @@ bool subghz_txrx_gen_secplus_v1_protocol(
|
|||||||
* @return uint64_t if success
|
* @return uint64_t if success
|
||||||
*/
|
*/
|
||||||
void subghz_txrx_gen_serial_gangqi(uint64_t* result_key);
|
void subghz_txrx_gen_serial_gangqi(uint64_t* result_key);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate key for Marantec protocol
|
||||||
|
*
|
||||||
|
* @param result_key Pointer to a uint64_t where the key will be stored
|
||||||
|
*/
|
||||||
|
void subghz_txrx_gen_key_marantec(uint64_t* result_key);
|
||||||
|
|||||||
@@ -1,62 +1,69 @@
|
|||||||
Filetype: Flipper SubGhz Keystore File
|
Filetype: Flipper SubGhz Keystore File
|
||||||
Version: 0
|
Version: 0
|
||||||
Encryption: 1
|
Encryption: 1
|
||||||
IV: 46 75 72 69 20 63 68 65 63 6B 20 4F 77 4F 21 3F
|
IV: 46 75 72 72 79 20 52 6F 63 6B 65 74 21 21 21 30
|
||||||
796353C129CC2B688FE158D36E82001F7450D58DD763BCF4D6FA1CE6C3D598EA
|
05176EEFAC177FE261FE3EB5C8E103BE7CF9F2FEB32BDD6BB63D22EE9C17B9D2
|
||||||
1E7DDEB3B54A42B8993C32AF209CBCC9A1137CE334449F016B993D673EB15C69
|
B645E3CAC0D5E26891249D326BCEB09850E4FB8F8E86A466E97E83437A9E0041
|
||||||
F9CB2DDC4E3D45694292C7DE45F1DC0BD74235B36F624AF39E8C3D211A713408
|
AA4255FFA1ADE8FB840F80A93F8F1A2D1E39051131D24DE7258D66A8CF2066CF
|
||||||
538F46EF4250801434FEA469EE07E8C8BF71C6179442718A05455BB501D797C4
|
13ACA390FD5254B024084D5D1F41B8DDF5304FF00C3C85A9C26CD13A7A268654
|
||||||
F2BF384DBF7F828E025F020E47D1D637B497FB470444F0C6F9DC67C6830EDDE8A26A6EE89A321D3924D9099895AB2EA1
|
4CFBF498D5E2C85496985E83D91B0F4229A925E16A90C6712750032C3699EE0AA5D04123E579B6121573FC61766E89AD
|
||||||
2CA8A8A4866D5C2B715B520F641B41355A81BA73170842233806D8AA3E3F3D62
|
93DADC2AE4235470E171E0E85D24D04A84C37187284C38D1CBB48666FDA8CD6C
|
||||||
80CF5DEF931CF902EE602319F7CD506D42E5FBC06ABBF9F05D474C8E8A2AAC4E
|
DB13D8CCC0CB07685F29F33AE07DA2FD14C2AE4F4D001DB88465D5CFE8CFDAA9
|
||||||
15CB465F7A646226C987BB4928E92A61F350ADCDDE355B717730ADDD1B738950
|
E51CD1B5074B63D26E274218A0AB3B2E435454EE094DCA5679F35477658A72F9
|
||||||
86B7597ED3EDF6826124ABD7AE419197DC4A93FA064179ACAD853EC670F93995
|
10AFD5FD9C296E67EDD9504A60BA9EF84556F40213DEC4DE44F99B088BCC6A57
|
||||||
28263A286F1ADA0E851E8A27AFD7706CF3513D8A24D41CF7E24A925FE3D86305
|
EF7AA55F6A473DE093D648240D5FCEB05F8B3295DC37B3E83239A4AF320CD688
|
||||||
AF29A08AB877F12681706D71B82B8E2447DA18EBF9731EF3CE91FA5ECAC4E98F8FDC817E0F67C3D7348DD2AF128F1E62
|
A22892E71B9D0D7FAF92B27C724E76C4A6824DBE5F083F1006D11E42D153C4AC98D0A11C6A8D62F5921A24ECC7437485
|
||||||
A675C5BB6AC41336B5F5A27FC062FBF30DC39B1E5C498F0FD823261C3177FA58416C5402554742370CB0DAFD2895F6F8
|
7A25416E390D81DA68A59C3BA30D4B7FC8269B5E0DAF77CA3A857B6F478A050585918485AEE72D375F02D177CB296E31
|
||||||
0073790C26BD0224F5560161A4D4C4F8E05498E995FD2BF12EA9926A5127E9A5
|
94004BA0BB1E47965E60025949EF4CC2738C463F57C97FD2A89C76CCCDEA5397
|
||||||
235ADFE253B51260DCF5C66F9CB9602B91A0C57BE14D1BE1E11EB309643B29DB
|
111CB1C19863A0165521D974F838CE718DA07948A8D9A8A7490E75032A62ECA2
|
||||||
F9C2E3A926AAA108F474075F0B4EDFF3135492B1529EAB150CF78E748BF36E17
|
17B6E27C69FA002F6CF23D719DFE595140BEFA5083D12E774CF89E2CED53D68D
|
||||||
50F442CC9D90B9FF8C786245EC442B911A28A04D829D0E45D7D2D4C5A3F5B864
|
73311E0FF8ABB3E9461AD14A4F52791647A50E2102D3B74188A73C35BC14EB55
|
||||||
9EA00CD21C0BE1BB15A0CAD31097A44CAF42868F259E8ED25F361986C26161514FA85D78F3889B4185D2A1484C1DA88C
|
54E15840A6A6DCA85275E38E4218EE2B539E9E468E24C49428DA363C955C5FC81ACEE79EEB941B83EE4147A0817043BD
|
||||||
52DFE7D9052F29071922876723C30CF6FE6247D6439590C5C7C0493120ACA096
|
7D0FBB417B99B3C6AB18C7B2DC82582D2DCD1E10515028874E73254188F7FEE9
|
||||||
D5AF176D59779A92F6EFB34F271D76D60780C3EA83306326E0119CD3B0417687
|
3F6E89BBCC133B85945234A8201539ECD8796909CC81FE67673F8DE1ECA63045
|
||||||
C4CC4E589CCCEC609B4DE39F926F83CCFFCC966871B9A998514910A3F59E86FB56C476DFFB4181E3D22E86A760C4C137
|
39554C0DC1C3694FAAFF65537FF710D9593B7B461E011FC39D014F253F0432533A40276D8259AFD8C957A378237D574F
|
||||||
50A64FEDA64B61A5C123906426EF726D98AC70C15F38245A931B5649F0944930679513D9862091275F10A804EBB610DE
|
E60F6CD7063B85F0F20ACB7E7A42B03DE4A9F6CCA54CB7F036AFA23A27D3E9E006BD523E5356260AA78206D9276E6E57
|
||||||
E7938C4F32E571582D73F855ACC40FF0153EFBAB6D184F2DBE8EAC63C4276D92
|
9EB252EDA9352B966EC4F053D5B013772361D2AD4B217EF33F46A5CEC97A00F3
|
||||||
7667993F35F1363C0A3AEB5222F07F91904B3F6FA375BF062B269B09706764CB
|
AA6773E79BC6D76314BB523FDF203358E01ECB2BBCF3B5DD1EBD043663C74B05
|
||||||
9DF764EE4A70D331BA8870F4D27C3E90E811A5E306BC72701A99A0377AC7B189
|
29B29A50F3F27F4D8C7B0FADA98CC004A7871078DAD1CBAC4846862C3DF82E02
|
||||||
324D0FBC096A489983C45E82B2745A83A3725A87D2D2CA676A521075065D5047
|
6E3A479D4334FF05606899B0383116125056A316621B279F904A02B842918C59
|
||||||
8EA1A30AE08CEBD03A52EB2512C7C69CB824E0B9BE900E25F15FAB17ADBB1188
|
3991732015F4A213E9912E34AC92515D88010C07DA0B118AD6F64A05DC38D2C5
|
||||||
CE379182103FC4E0442745F6F202AB6ED8EBC051B5F5537916938D9DB8FCD6BE
|
550B1866F7493C75812DF85DDADC38AF21D9B58189E4EE99A021328523881A9D
|
||||||
752BD22AA37D030C60E48CB57309AF631682AC4A0D67C3A7D1130EB056717FB6
|
77960CA031D28362586100F17DF94FF4E7D6EFAFAF23952887F9DF0507825A99
|
||||||
9ECFE7D24BF25BD543E1EAE8116D95C110BD4514EE279BCA71234865EB9166BD
|
01E6FC89E97B7729BF4D1ED8041F69005181BF3639F939C5833B009E96B9F2F7
|
||||||
14B3FA8704BA3B284C65F1A6FD114E53B883E12EAFB24B574F84585BD20157DA
|
D1CC7C536706ECFC5826C8933135D2B110996F1CB13388A702B8453DA40E40AD
|
||||||
D026E1E2CA70E33291482EEFCE11123540BBF591D400C92CEBB8CAF99A9DE882
|
B64D2F1E1A80E6DAB92283A512B40DB7FFC519F394AA94CC86C8532F69949723
|
||||||
88E618EF5E76D1F5A60926D48D3D58FF67B4A92DE6EECA271EDAFC2419AE787B
|
6399409A0AC0298DEDA76037C83042FC0870132CFF7F82E54AD0966BE16AC882
|
||||||
74E14AD8ADE2BC575048325D1B3990669CDB35D840169B913043DAC938862AE4
|
D310536FA78F95BB0B408676990AA937117717BADE9D3B975C0ECE10FB586A1B
|
||||||
BEB5388B6A7D5C9EF65BDD7577D1DD654B7FAECE5A4CF0937BB7D0C0C5494CD0
|
A8149C0581DCC291D037E96EF321DB6214BD7CB25F1696226A9FE750AA23B334
|
||||||
761356D494E3C947CBDCA887A30071A675FE1BE6E77FC9016DB9B5659B7FF9E7
|
BA3BEBD564D8F571202CD6FE89BC33F89C8E01C03AE0814F2BEF37C33CE874B4
|
||||||
7503725DFC7212F5F719AFC9DF29F07321511BE4896E12D1D10C68BB07C6483A2CE5FFDFD074CFC279E42E6DD860C496
|
88CD81AC7605A7F6EFF85FD62C65E0C9945335CFC085B92B27B69648C6E5BF6B
|
||||||
BD03D78399DE44449AEE9F00EBCABBF419EDEA1701B9979A97D57AECB5139D1E
|
8057C7CB5071DFFFAE4804FD9EC1EC1D3F54D06514906A34B17F6B6CB45A9D473992DF6BC8A9F9E146E39D6163209CC6
|
||||||
E80EB84DF9DA2E34B78F50D6488A30F8EFD11E7C6DBEB7CFEDE83BE5CA86B6DF
|
9ABC8814C8FD1AB254374150177616F5C7B43049473C84329BEC855578B96002
|
||||||
9332A130138F2AC11A12030CF43EFF77E7CABE761EDE14748112E13267496CE9
|
8BCA39A498B00245C71D94E3160CEE8ACA5BEB18AE0AD64A385AFCC018E99744
|
||||||
E657F3C95EEF0AF92A5C49F66BD9C053A82493C7D6267F1E7C038473AE488116
|
5AD75C51CA5AE5FA9BBC6A41576C745F265CC28FC4DA2AD230B6692CF151FD61
|
||||||
6F37491FFE130F90B77D7E5EA4AA75A1DB0CA3644F68B6502DFC302DDB80367B
|
E86092E04CD72D874A92DE838035E811E75E411049C0A7BD0FE2AA9C802BE5AB
|
||||||
3B37C5CCDD510873628D92B352907FEFF0AC2B38C751C2E46C3B97C3E365972B
|
CE70ADB22E85747FDC064F0B5974385CD57D41D376CE1C7490C1BEC8A3FC5A7A
|
||||||
A4C845187BEC75669234EE07B839DCFF618678D2EAA5596350F0936A400099A5
|
8F096E0A11682DB315825213D3DB5D725555C1CDF444169EB919E47E0F0FA6F7
|
||||||
2C961EF4F2454771B2646ECBAA1D0B7DAA2FD8ECC7228037A36E24FE30C43ABAA446C1B5968C0E2DD141C55557A4CCB8
|
AD9C9A694D807BA77E5A54B248A88B55000757203D931506255BF8F4215C00D3
|
||||||
C4E0D43E9670C2B91F6AF03D60F216625CC19C697331BC443194D2BB88E042DF
|
F0E804B6C6B6E91916CB73EB44FB2D1992400BC90ED8B22DF5D038317588341207D74E08C00E529DF2CF2A64F2C7C0FF
|
||||||
3DC5584F43061AF79907D6342DF3344435B5AA6277B33D7DC56C429D1EB81BC4
|
72212FCEED35E9C3A176B67DCDB84B284F4DFDCD0ECE8D3F6089C58C2B8A616C
|
||||||
D9186791E907CBB9EF26EDDAEA9F0DBC8D24E213E55942ED5E1790B5A55F8758
|
000F9F746BFB47FC10B23E3F08C2A84BCB3870D0C5AE974472849699884BC929
|
||||||
2B54CB727BBFD8567543448D2D24B3865329F89936D3B035ED2185BA88F1DF2E
|
7B8F9AB04E5F86D6DDCF6164A25EA927788A03F57977FC5C55E1D565279B09C4
|
||||||
A08F0D881AF001E52FF2CF9D232A9A566EB1900B351AFEEFEE666BEC40588F64
|
0E9CDCD07D1D4F1429E59F81B524960A75F19A464798C7E822E52728AC83784A
|
||||||
46A11CA89BA1998A247275EEDF504DAFC5F97B41DFF4943F531A9F8F43DF19C5
|
F2DE2B108A1476BB6F85DD3CCB0F0527627B45179092BA7A56D5971490E3875C
|
||||||
EF70A3858E84B13DF606317381B2E9DFFC346E96AF7C1DF0001586B8BF35808F
|
7F307358D988FEA12648739F58DD249EBDF0B1C44B73BA547C50EB576C071DAE
|
||||||
38EAF18DB8096C7427EBD36CBC5B0E945A3286278CC0227EB056F7ACB9E450D4
|
2DFBA988592CEF3B62A76183DBA727E734359B89F53AFF3160441EF8709FC633
|
||||||
28278D1DAA263E9A45ED17F67F6B6B0CF00F8C0F58F86C8161F8D4266FE556CB
|
57F7DC38DDC87C19CE956BC44C638DEF34D814A7BAB0AC8AD61855143FD984FD
|
||||||
0B0C79FDD7C9EA31FAA4AA829EDDD2A3453C05A74F5B53BBEAE83E1F4913FD1C18BD235D14D06D9E567DDB273E4C1F2A
|
A8AADB687251FA6AC2BBC8EF1E3FA621893293DFBD8C1D07971BF82F22A00DC3
|
||||||
7D663A93AE1B9A2E00E944B92838DED3376E09C5179C8F3037B2EAE9E7326C2A
|
65AEA1EE34E316C769E551AC2309D07FC2ED92EA044674E3A99CD7B543C730EB
|
||||||
D64EC2C7BD8CFA152368DF6BB75D66EF24EAA9C864A1386184B793C0585D82BC
|
968ECC790E5590E7EB22AFD3546C28F4EB87EA4CEE35F72DDFE7153F74611EAA
|
||||||
51D8EB188E833891CCD15919FAC8FE56ACAB1007699F4FDCB53A6DDAB02E5CAA
|
0F937930D4E1BDF0B729277CF94A47064BCB959938C70CDB3AC3C65DA68DA1FB
|
||||||
650866D34DECD1D1F3559EFD8D2A4C1DB51C005F5932608CB6062B384D7A1F59C8E3FBF2C0A5AEFFB631D7B88A630AAB
|
A8AB66375D59E112104CD81B819D618BE43D6A6F159BAD35583653EF3547D25D
|
||||||
|
A81D5DE2102F05D50750DC37C26E9C9502FA89EF98A2EB1EA546EE48C628E9C4
|
||||||
|
EAFDE0A8936AF8EF718027937BC17CEF691E570996B403CF4762240D267EB305
|
||||||
|
C48686348F0A94B07BC60AB825C1A0791C20DBBDD7DAE0ED47E8A7FBD9334EACF8E33DCEC36963E87929260DF769520B
|
||||||
|
493D53BD7BB2B3E081AE793A3BADB3AB0F33C95B83677715D6DE2922F2BEC892
|
||||||
|
63FFD3D8CAB980E45D49253A69C99A6813CBE6013992EFBC862173BAD0E26373
|
||||||
|
2EF88F43C5A76EC87E02B780585B10957F4EA386F96710FAB98BC2C1E214DBFA
|
||||||
|
A021CFA0E72AADFD75BC67FBE9345082B0A8B31782E933E81196F84B1797D83E8B2F81E1CF5C3F026D11B9DFC95222E2
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ bool subghz_scene_frequency_analyzer_on_event(void* context, SceneManagerEvent e
|
|||||||
} else if(event.event == SubGhzCustomEventViewFreqAnalOkLong) {
|
} else if(event.event == SubGhzCustomEventViewFreqAnalOkLong) {
|
||||||
// Don't need to save, we already saved on short event (and on exit event too)
|
// Don't need to save, we already saved on short event (and on exit event too)
|
||||||
subghz_rx_key_state_set(subghz, SubGhzRxKeyStateIDLE);
|
subghz_rx_key_state_set(subghz, SubGhzRxKeyStateIDLE);
|
||||||
scene_manager_set_scene_state(subghz->scene_manager, SubGhzSceneStart, 10);
|
scene_manager_previous_scene(subghz->scene_manager); // Stops the worker
|
||||||
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneReceiver);
|
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneReceiver);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,9 +8,9 @@ enum SubGhzSettingIndex {
|
|||||||
SubGhzSettingIndexHopping,
|
SubGhzSettingIndexHopping,
|
||||||
SubGhzSettingIndexModulation,
|
SubGhzSettingIndexModulation,
|
||||||
SubGhzSettingIndexBinRAW,
|
SubGhzSettingIndexBinRAW,
|
||||||
SubGhzSettingIndexIgnoreStarline,
|
|
||||||
SubGhzSettingIndexIgnoreCars,
|
SubGhzSettingIndexIgnoreCars,
|
||||||
SubGhzSettingIndexIgnoreMagellan,
|
SubGhzSettingIndexIgnoreAlarms,
|
||||||
|
SubGhzSettingIndexIgnoreSensors,
|
||||||
SubGhzSettingIndexIgnorePrinceton,
|
SubGhzSettingIndexIgnorePrinceton,
|
||||||
SubGhzSettingIndexIgnoreNiceFlorS,
|
SubGhzSettingIndexIgnoreNiceFlorS,
|
||||||
SubGhzSettingIndexDeleteOldSignals,
|
SubGhzSettingIndexDeleteOldSignals,
|
||||||
@@ -301,16 +301,16 @@ static inline bool subghz_scene_receiver_config_ignore_filter_get_index(
|
|||||||
return READ_BIT(filter, flag) > 0;
|
return READ_BIT(filter, flag) > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void subghz_scene_receiver_config_set_starline(VariableItem* item) {
|
static void subghz_scene_receiver_config_set_cars(VariableItem* item) {
|
||||||
subghz_scene_receiver_config_set_ignore_filter(item, SubGhzProtocolFlag_StarLine);
|
subghz_scene_receiver_config_set_ignore_filter(item, SubGhzProtocolFlag_Cars);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void subghz_scene_receiver_config_set_auto_alarms(VariableItem* item) {
|
static void subghz_scene_receiver_config_set_alarms(VariableItem* item) {
|
||||||
subghz_scene_receiver_config_set_ignore_filter(item, SubGhzProtocolFlag_AutoAlarms);
|
subghz_scene_receiver_config_set_ignore_filter(item, SubGhzProtocolFlag_Alarms);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void subghz_scene_receiver_config_set_magellan(VariableItem* item) {
|
static void subghz_scene_receiver_config_set_sensors(VariableItem* item) {
|
||||||
subghz_scene_receiver_config_set_ignore_filter(item, SubGhzProtocolFlag_Magellan);
|
subghz_scene_receiver_config_set_ignore_filter(item, SubGhzProtocolFlag_Sensors);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void subghz_scene_receiver_config_set_princeton(VariableItem* item) {
|
static void subghz_scene_receiver_config_set_princeton(VariableItem* item) {
|
||||||
@@ -445,39 +445,39 @@ void subghz_scene_receiver_config_on_enter(void* context) {
|
|||||||
|
|
||||||
if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneReadRAW) !=
|
if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneReadRAW) !=
|
||||||
SubGhzCustomEventManagerSet) {
|
SubGhzCustomEventManagerSet) {
|
||||||
item = variable_item_list_add(
|
|
||||||
subghz->variable_item_list,
|
|
||||||
"Ignore Starline",
|
|
||||||
COMBO_BOX_COUNT,
|
|
||||||
subghz_scene_receiver_config_set_starline,
|
|
||||||
subghz);
|
|
||||||
|
|
||||||
value_index = subghz_scene_receiver_config_ignore_filter_get_index(
|
|
||||||
subghz->ignore_filter, SubGhzProtocolFlag_StarLine);
|
|
||||||
variable_item_set_current_value_index(item, value_index);
|
|
||||||
variable_item_set_current_value_text(item, combobox_text[value_index]);
|
|
||||||
|
|
||||||
item = variable_item_list_add(
|
item = variable_item_list_add(
|
||||||
subghz->variable_item_list,
|
subghz->variable_item_list,
|
||||||
"Ignore Cars",
|
"Ignore Cars",
|
||||||
COMBO_BOX_COUNT,
|
COMBO_BOX_COUNT,
|
||||||
subghz_scene_receiver_config_set_auto_alarms,
|
subghz_scene_receiver_config_set_cars,
|
||||||
subghz);
|
subghz);
|
||||||
|
|
||||||
value_index = subghz_scene_receiver_config_ignore_filter_get_index(
|
value_index = subghz_scene_receiver_config_ignore_filter_get_index(
|
||||||
subghz->ignore_filter, SubGhzProtocolFlag_AutoAlarms);
|
subghz->ignore_filter, SubGhzProtocolFlag_Cars);
|
||||||
variable_item_set_current_value_index(item, value_index);
|
variable_item_set_current_value_index(item, value_index);
|
||||||
variable_item_set_current_value_text(item, combobox_text[value_index]);
|
variable_item_set_current_value_text(item, combobox_text[value_index]);
|
||||||
|
|
||||||
item = variable_item_list_add(
|
item = variable_item_list_add(
|
||||||
subghz->variable_item_list,
|
subghz->variable_item_list,
|
||||||
"Ignore Magellan",
|
"Ignore Alarms",
|
||||||
COMBO_BOX_COUNT,
|
COMBO_BOX_COUNT,
|
||||||
subghz_scene_receiver_config_set_magellan,
|
subghz_scene_receiver_config_set_alarms,
|
||||||
subghz);
|
subghz);
|
||||||
|
|
||||||
value_index = subghz_scene_receiver_config_ignore_filter_get_index(
|
value_index = subghz_scene_receiver_config_ignore_filter_get_index(
|
||||||
subghz->ignore_filter, SubGhzProtocolFlag_Magellan);
|
subghz->ignore_filter, SubGhzProtocolFlag_Alarms);
|
||||||
|
variable_item_set_current_value_index(item, value_index);
|
||||||
|
variable_item_set_current_value_text(item, combobox_text[value_index]);
|
||||||
|
|
||||||
|
item = variable_item_list_add(
|
||||||
|
subghz->variable_item_list,
|
||||||
|
"Ignore Sensors",
|
||||||
|
COMBO_BOX_COUNT,
|
||||||
|
subghz_scene_receiver_config_set_sensors,
|
||||||
|
subghz);
|
||||||
|
|
||||||
|
value_index = subghz_scene_receiver_config_ignore_filter_get_index(
|
||||||
|
subghz->ignore_filter, SubGhzProtocolFlag_Sensors);
|
||||||
variable_item_set_current_value_index(item, value_index);
|
variable_item_set_current_value_index(item, value_index);
|
||||||
variable_item_set_current_value_text(item, combobox_text[value_index]);
|
variable_item_set_current_value_text(item, combobox_text[value_index]);
|
||||||
|
|
||||||
|
|||||||
@@ -93,6 +93,9 @@ void subghz_scene_save_name_on_enter(void* context) {
|
|||||||
bool subghz_scene_save_name_on_event(void* context, SceneManagerEvent event) {
|
bool subghz_scene_save_name_on_event(void* context, SceneManagerEvent event) {
|
||||||
SubGhz* subghz = context;
|
SubGhz* subghz = context;
|
||||||
if(event.type == SceneManagerEventTypeBack) {
|
if(event.type == SceneManagerEventTypeBack) {
|
||||||
|
// Set file path to default
|
||||||
|
furi_string_set(subghz->file_path, SUBGHZ_APP_FOLDER);
|
||||||
|
//
|
||||||
if(!(strcmp(subghz->file_name_tmp, "") == 0) ||
|
if(!(strcmp(subghz->file_name_tmp, "") == 0) ||
|
||||||
scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneReadRAW) !=
|
scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneReadRAW) !=
|
||||||
SubGhzCustomEventManagerNoSet) {
|
SubGhzCustomEventManagerNoSet) {
|
||||||
@@ -106,8 +109,6 @@ bool subghz_scene_save_name_on_event(void* context, SceneManagerEvent event) {
|
|||||||
} else {
|
} else {
|
||||||
scene_manager_previous_scene(subghz->scene_manager);
|
scene_manager_previous_scene(subghz->scene_manager);
|
||||||
}
|
}
|
||||||
// Set file path to default
|
|
||||||
furi_string_set(subghz->file_path, SUBGHZ_APP_FOLDER);
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
} else if(event.type == SceneManagerEventTypeCustom) {
|
} else if(event.type == SceneManagerEventTypeCustom) {
|
||||||
|
|||||||
@@ -20,13 +20,18 @@ static const char* submenu_names[SetTypeMAX] = {
|
|||||||
[SetTypeSomfyTelis] = "Somfy Telis 433MHz",
|
[SetTypeSomfyTelis] = "Somfy Telis 433MHz",
|
||||||
[SetTypeANMotorsAT4] = "AN-Motors AT4 433MHz",
|
[SetTypeANMotorsAT4] = "AN-Motors AT4 433MHz",
|
||||||
[SetTypeAlutechAT4N] = "Alutech AT4N 433MHz",
|
[SetTypeAlutechAT4N] = "Alutech AT4N 433MHz",
|
||||||
|
[SetTypeRoger_433] = "Roger 433MHz",
|
||||||
|
[SetTypePhoenix_V2_433] = "V2 Phoenix 433MHz",
|
||||||
[SetTypeHCS101_433_92] = "KL: HCS101 433MHz",
|
[SetTypeHCS101_433_92] = "KL: HCS101 433MHz",
|
||||||
[SetTypeDoorHan_315_00] = "KL: DoorHan 315MHz",
|
[SetTypeDoorHan_315_00] = "KL: DoorHan 315MHz",
|
||||||
[SetTypeDoorHan_433_92] = "KL: DoorHan 433MHz",
|
[SetTypeDoorHan_433_92] = "KL: DoorHan 433MHz",
|
||||||
[SetTypeBeninca433] = "KL: Beninca 433MHz",
|
[SetTypeBeninca433] = "KL: Beninca 433MHz",
|
||||||
[SetTypeBeninca868] = "KL: Beninca 868MHz",
|
[SetTypeBeninca868] = "KL: Beninca 868MHz",
|
||||||
|
[SetTypeComunello433] = "KL: Comunello 433MHz",
|
||||||
|
[SetTypeComunello868] = "KL: Comunello 868MHz",
|
||||||
[SetTypeAllmatic433] = "KL: Allmatic 433MHz",
|
[SetTypeAllmatic433] = "KL: Allmatic 433MHz",
|
||||||
[SetTypeAllmatic868] = "KL: Allmatic 868MHz",
|
[SetTypeAllmatic868] = "KL: Allmatic 868MHz",
|
||||||
|
[SetTypeMotorline433] = "KL: Motorline 433MHz",
|
||||||
[SetTypeCenturion433] = "KL: Centurion 433MHz",
|
[SetTypeCenturion433] = "KL: Centurion 433MHz",
|
||||||
[SetTypeMonarch433] = "KL: Monarch 433MHz",
|
[SetTypeMonarch433] = "KL: Monarch 433MHz",
|
||||||
[SetTypeJollyMotors433] = "KL: Jolly Mot. 433MHz",
|
[SetTypeJollyMotors433] = "KL: Jolly Mot. 433MHz",
|
||||||
@@ -69,6 +74,8 @@ static const char* submenu_names[SetTypeMAX] = {
|
|||||||
[SetTypeHollarm_433] = "Hollarm 433MHz",
|
[SetTypeHollarm_433] = "Hollarm 433MHz",
|
||||||
[SetTypeReversRB2_433] = "Revers RB2 433MHz",
|
[SetTypeReversRB2_433] = "Revers RB2 433MHz",
|
||||||
[SetTypeMarantec24_868] = "Marantec24 868MHz",
|
[SetTypeMarantec24_868] = "Marantec24 868MHz",
|
||||||
|
[SetTypeMarantec_433] = "Marantec 433MHz",
|
||||||
|
[SetTypeMarantec_868] = "Marantec 868MHz",
|
||||||
[SetTypeBETT_433] = "BETT 433MHz",
|
[SetTypeBETT_433] = "BETT 433MHz",
|
||||||
[SetTypeLinear_300_00] = "Linear 300MHz",
|
[SetTypeLinear_300_00] = "Linear 300MHz",
|
||||||
// [SetTypeNeroSketch] = "Nero Sketch", // Deleted in OFW
|
// [SetTypeNeroSketch] = "Nero Sketch", // Deleted in OFW
|
||||||
@@ -108,6 +115,7 @@ typedef enum {
|
|||||||
GenNiceFlorS,
|
GenNiceFlorS,
|
||||||
GenSecPlus1,
|
GenSecPlus1,
|
||||||
GenSecPlus2,
|
GenSecPlus2,
|
||||||
|
GenPhoenixV2,
|
||||||
} GenType;
|
} GenType;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
@@ -166,6 +174,10 @@ typedef struct {
|
|||||||
uint8_t btn;
|
uint8_t btn;
|
||||||
uint32_t cnt;
|
uint32_t cnt;
|
||||||
} sec_plus_2;
|
} sec_plus_2;
|
||||||
|
struct {
|
||||||
|
uint32_t serial;
|
||||||
|
uint16_t cnt;
|
||||||
|
} phoenix_v2;
|
||||||
};
|
};
|
||||||
} GenInfo;
|
} GenInfo;
|
||||||
|
|
||||||
@@ -190,6 +202,9 @@ bool subghz_scene_set_type_on_event(void* context, SceneManagerEvent event) {
|
|||||||
uint64_t gangqi_key;
|
uint64_t gangqi_key;
|
||||||
subghz_txrx_gen_serial_gangqi(&gangqi_key);
|
subghz_txrx_gen_serial_gangqi(&gangqi_key);
|
||||||
|
|
||||||
|
uint64_t marantec_key;
|
||||||
|
subghz_txrx_gen_key_marantec(&marantec_key);
|
||||||
|
|
||||||
GenInfo gen_info = {0};
|
GenInfo gen_info = {0};
|
||||||
switch(event.event) {
|
switch(event.event) {
|
||||||
case SetTypePricenton433:
|
case SetTypePricenton433:
|
||||||
@@ -272,6 +287,16 @@ bool subghz_scene_set_type_on_event(void* context, SceneManagerEvent event) {
|
|||||||
.data.bits = 24,
|
.data.bits = 24,
|
||||||
.data.te = 0};
|
.data.te = 0};
|
||||||
break;
|
break;
|
||||||
|
case SetTypeRoger_433:
|
||||||
|
gen_info = (GenInfo){
|
||||||
|
.type = GenData,
|
||||||
|
.mod = "AM650",
|
||||||
|
.freq = 433920000,
|
||||||
|
.data.name = SUBGHZ_PROTOCOL_ROGER_NAME,
|
||||||
|
.data.key = (key & 0xFFFF000) | 0x0000101, // button code 0x1 and (crc?) is 0x01
|
||||||
|
.data.bits = 28,
|
||||||
|
.data.te = 0};
|
||||||
|
break;
|
||||||
case SetTypeLinear_300_00:
|
case SetTypeLinear_300_00:
|
||||||
gen_info = (GenInfo){
|
gen_info = (GenInfo){
|
||||||
.type = GenData,
|
.type = GenData,
|
||||||
@@ -358,6 +383,28 @@ bool subghz_scene_set_type_on_event(void* context, SceneManagerEvent event) {
|
|||||||
.data.bits = 24,
|
.data.bits = 24,
|
||||||
.data.te = 0};
|
.data.te = 0};
|
||||||
break;
|
break;
|
||||||
|
case SetTypeMarantec_433:
|
||||||
|
gen_info = (GenInfo){
|
||||||
|
.type = GenData,
|
||||||
|
.mod = "AM650",
|
||||||
|
.freq = 433920000,
|
||||||
|
.data.name =
|
||||||
|
SUBGHZ_PROTOCOL_MARANTEC_NAME, // Button code is 0x4 and crc sum to the end
|
||||||
|
.data.key = marantec_key,
|
||||||
|
.data.bits = 49,
|
||||||
|
.data.te = 0};
|
||||||
|
break;
|
||||||
|
case SetTypeMarantec_868:
|
||||||
|
gen_info = (GenInfo){
|
||||||
|
.type = GenData,
|
||||||
|
.mod = "AM650",
|
||||||
|
.freq = 868350000,
|
||||||
|
.data.name =
|
||||||
|
SUBGHZ_PROTOCOL_MARANTEC_NAME, // Button code is 0x4 and crc sum to the end
|
||||||
|
.data.key = marantec_key,
|
||||||
|
.data.bits = 49,
|
||||||
|
.data.te = 0};
|
||||||
|
break;
|
||||||
case SetTypeFaacSLH_433:
|
case SetTypeFaacSLH_433:
|
||||||
gen_info = (GenInfo){
|
gen_info = (GenInfo){
|
||||||
.type = GenFaacSLH,
|
.type = GenFaacSLH,
|
||||||
@@ -400,6 +447,26 @@ bool subghz_scene_set_type_on_event(void* context, SceneManagerEvent event) {
|
|||||||
.keeloq.cnt = 0x05,
|
.keeloq.cnt = 0x05,
|
||||||
.keeloq.manuf = "Beninca"};
|
.keeloq.manuf = "Beninca"};
|
||||||
break;
|
break;
|
||||||
|
case SetTypeComunello433:
|
||||||
|
gen_info = (GenInfo){
|
||||||
|
.type = GenKeeloq,
|
||||||
|
.mod = "AM650",
|
||||||
|
.freq = 433920000,
|
||||||
|
.keeloq.serial = key & 0x00FFFFFF,
|
||||||
|
.keeloq.btn = 0x08,
|
||||||
|
.keeloq.cnt = 0x05,
|
||||||
|
.keeloq.manuf = "Comunello"};
|
||||||
|
break;
|
||||||
|
case SetTypeComunello868:
|
||||||
|
gen_info = (GenInfo){
|
||||||
|
.type = GenKeeloq,
|
||||||
|
.mod = "AM650",
|
||||||
|
.freq = 868460000,
|
||||||
|
.keeloq.serial = key & 0x00FFFFFF,
|
||||||
|
.keeloq.btn = 0x08,
|
||||||
|
.keeloq.cnt = 0x05,
|
||||||
|
.keeloq.manuf = "Comunello"};
|
||||||
|
break;
|
||||||
case SetTypeAllmatic433:
|
case SetTypeAllmatic433:
|
||||||
gen_info = (GenInfo){
|
gen_info = (GenInfo){
|
||||||
.type = GenKeeloq,
|
.type = GenKeeloq,
|
||||||
@@ -585,16 +652,16 @@ bool subghz_scene_set_type_on_event(void* context, SceneManagerEvent event) {
|
|||||||
.type = GenCameAtomo,
|
.type = GenCameAtomo,
|
||||||
.mod = "AM650",
|
.mod = "AM650",
|
||||||
.freq = 433920000,
|
.freq = 433920000,
|
||||||
.keeloq.serial = (key & 0x0FFFFFFF) | 0x10000000,
|
.came_atomo.serial = (key & 0x0FFFFFFF) | 0x10000000,
|
||||||
.keeloq.cnt = 0x03};
|
.came_atomo.cnt = 0x03};
|
||||||
break;
|
break;
|
||||||
case SetTypeCameAtomo868:
|
case SetTypeCameAtomo868:
|
||||||
gen_info = (GenInfo){
|
gen_info = (GenInfo){
|
||||||
.type = GenCameAtomo,
|
.type = GenCameAtomo,
|
||||||
.mod = "AM650",
|
.mod = "AM650",
|
||||||
.freq = 868350000,
|
.freq = 868350000,
|
||||||
.keeloq.serial = (key & 0x0FFFFFFF) | 0x10000000,
|
.came_atomo.serial = (key & 0x0FFFFFFF) | 0x10000000,
|
||||||
.keeloq.cnt = 0x03};
|
.came_atomo.cnt = 0x03};
|
||||||
break;
|
break;
|
||||||
case SetTypeBFTMitto:
|
case SetTypeBFTMitto:
|
||||||
gen_info = (GenInfo){
|
gen_info = (GenInfo){
|
||||||
@@ -625,6 +692,16 @@ bool subghz_scene_set_type_on_event(void* context, SceneManagerEvent event) {
|
|||||||
.somfy_telis.btn = 0x02,
|
.somfy_telis.btn = 0x02,
|
||||||
.somfy_telis.cnt = 0x03};
|
.somfy_telis.cnt = 0x03};
|
||||||
break;
|
break;
|
||||||
|
case SetTypeMotorline433:
|
||||||
|
gen_info = (GenInfo){
|
||||||
|
.type = GenKeeloq,
|
||||||
|
.mod = "AM650",
|
||||||
|
.freq = 433920000,
|
||||||
|
.keeloq.serial = key & 0x0FFFFFFF,
|
||||||
|
.keeloq.btn = 0x01,
|
||||||
|
.keeloq.cnt = 0x03,
|
||||||
|
.keeloq.manuf = "Motorline"};
|
||||||
|
break;
|
||||||
case SetTypeDoorHan_433_92:
|
case SetTypeDoorHan_433_92:
|
||||||
gen_info = (GenInfo){
|
gen_info = (GenInfo){
|
||||||
.type = GenKeeloq,
|
.type = GenKeeloq,
|
||||||
@@ -820,6 +897,14 @@ bool subghz_scene_set_type_on_event(void* context, SceneManagerEvent event) {
|
|||||||
.sec_plus_2.btn = 0x68,
|
.sec_plus_2.btn = 0x68,
|
||||||
.sec_plus_2.cnt = 0xE500000};
|
.sec_plus_2.cnt = 0xE500000};
|
||||||
break;
|
break;
|
||||||
|
case SetTypePhoenix_V2_433:
|
||||||
|
gen_info = (GenInfo){
|
||||||
|
.type = GenPhoenixV2,
|
||||||
|
.mod = "AM650",
|
||||||
|
.freq = 433920000,
|
||||||
|
.phoenix_v2.serial = (key & 0x0FFFFFFF) | 0xB0000000,
|
||||||
|
.phoenix_v2.cnt = 0x025D};
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
furi_crash("Not implemented");
|
furi_crash("Not implemented");
|
||||||
break;
|
break;
|
||||||
@@ -927,6 +1012,14 @@ bool subghz_scene_set_type_on_event(void* context, SceneManagerEvent event) {
|
|||||||
gen_info.sec_plus_2.btn,
|
gen_info.sec_plus_2.btn,
|
||||||
gen_info.sec_plus_2.cnt);
|
gen_info.sec_plus_2.cnt);
|
||||||
break;
|
break;
|
||||||
|
case GenPhoenixV2:
|
||||||
|
generated_protocol = subghz_txrx_gen_phoenix_v2_protocol(
|
||||||
|
subghz->txrx,
|
||||||
|
gen_info.mod,
|
||||||
|
gen_info.freq,
|
||||||
|
gen_info.phoenix_v2.serial,
|
||||||
|
gen_info.phoenix_v2.cnt);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
furi_crash("Not implemented");
|
furi_crash("Not implemented");
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -1099,7 +1099,7 @@ static void subghz_cli_command_chat(PipeSide* pipe, FuriString* args) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(!cli_is_pipe_broken_or_is_etx_next_char(pipe)) {
|
if(cli_is_pipe_broken_or_is_etx_next_char(pipe)) {
|
||||||
printf("\r\n");
|
printf("\r\n");
|
||||||
chat_event.event = SubGhzChatEventUserExit;
|
chat_event.event = SubGhzChatEventUserExit;
|
||||||
subghz_chat_worker_put_event_chat(subghz_chat, &chat_event);
|
subghz_chat_worker_put_event_chat(subghz_chat, &chat_event);
|
||||||
|
|||||||
@@ -255,9 +255,9 @@ bool subghz_frequency_analyzer_input(InputEvent* event, void* context) {
|
|||||||
need_redraw = true;
|
need_redraw = true;
|
||||||
}
|
}
|
||||||
} else if(
|
} else if(
|
||||||
(event->type != InputTypeRelease && event->type != InputTypeRepeat) &&
|
(event->type == InputTypeShort || event->type == InputTypeLong) &&
|
||||||
event->key == InputKeyOk) {
|
event->key == InputKeyOk) {
|
||||||
need_redraw = true;
|
need_redraw = false;
|
||||||
bool updated = false;
|
bool updated = false;
|
||||||
uint32_t frequency_to_save;
|
uint32_t frequency_to_save;
|
||||||
with_view_model(
|
with_view_model(
|
||||||
@@ -286,21 +286,19 @@ bool subghz_frequency_analyzer_input(InputEvent* event, void* context) {
|
|||||||
instance->worker, frequency_candidate);
|
instance->worker, frequency_candidate);
|
||||||
if(frequency_candidate > 0 && frequency_candidate != model->frequency_to_save) {
|
if(frequency_candidate > 0 && frequency_candidate != model->frequency_to_save) {
|
||||||
model->frequency_to_save = frequency_candidate;
|
model->frequency_to_save = frequency_candidate;
|
||||||
|
frequency_to_save = frequency_candidate;
|
||||||
updated = true;
|
updated = true;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
true);
|
false);
|
||||||
|
|
||||||
if(updated) {
|
if(updated) {
|
||||||
instance->callback(SubGhzCustomEventViewFreqAnalOkShort, instance->context);
|
instance->callback(SubGhzCustomEventViewFreqAnalOkShort, instance->context);
|
||||||
}
|
}
|
||||||
|
|
||||||
// First the device receives short, then when user release button we get long
|
// If it was a long press also send a second event
|
||||||
if(event->type == InputTypeLong && frequency_to_save > 0) {
|
if(event->type == InputTypeLong && frequency_to_save > 0) {
|
||||||
// Stop worker
|
// Worker stopped on app thread instead of GUI thread when switching scene in callback
|
||||||
if(subghz_frequency_analyzer_worker_is_running(instance->worker)) {
|
|
||||||
subghz_frequency_analyzer_worker_stop(instance->worker);
|
|
||||||
}
|
|
||||||
|
|
||||||
instance->callback(SubGhzCustomEventViewFreqAnalOkLong, instance->context);
|
instance->callback(SubGhzCustomEventViewFreqAnalOkLong, instance->context);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -73,10 +73,11 @@ bool desktop_main_input_callback(InputEvent* event, void* context) {
|
|||||||
} else if(event->key == InputKeyOk) {
|
} else if(event->key == InputKeyOk) {
|
||||||
if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) {
|
if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) {
|
||||||
main_view->callback(DesktopAnimationEventNewIdleAnimation, main_view->context);
|
main_view->callback(DesktopAnimationEventNewIdleAnimation, main_view->context);
|
||||||
}
|
} else {
|
||||||
main_view->callback(DesktopMainEventOpenFavoriteOkLong, main_view->context);
|
main_view->callback(DesktopMainEventOpenFavoriteOkLong, main_view->context);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
if(event->type == InputTypeShort) {
|
if(event->type == InputTypeShort) {
|
||||||
if(event->key == InputKeyOk) {
|
if(event->key == InputKeyOk) {
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ typedef enum {
|
|||||||
ExpansionStateDisabled,
|
ExpansionStateDisabled,
|
||||||
ExpansionStateEnabled,
|
ExpansionStateEnabled,
|
||||||
ExpansionStateRunning,
|
ExpansionStateRunning,
|
||||||
|
ExpansionStateConnectionEstablished,
|
||||||
} ExpansionState;
|
} ExpansionState;
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
@@ -27,10 +28,13 @@ typedef enum {
|
|||||||
ExpansionMessageTypeReloadSettings,
|
ExpansionMessageTypeReloadSettings,
|
||||||
ExpansionMessageTypeModuleConnected,
|
ExpansionMessageTypeModuleConnected,
|
||||||
ExpansionMessageTypeModuleDisconnected,
|
ExpansionMessageTypeModuleDisconnected,
|
||||||
|
ExpansionMessageTypeConnectionEstablished,
|
||||||
|
ExpansionMessageTypeIsConnected,
|
||||||
} ExpansionMessageType;
|
} ExpansionMessageType;
|
||||||
|
|
||||||
typedef union {
|
typedef union {
|
||||||
FuriHalSerialId serial_id;
|
FuriHalSerialId serial_id;
|
||||||
|
bool* is_connected;
|
||||||
} ExpansionMessageData;
|
} ExpansionMessageData;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
@@ -67,13 +71,21 @@ static void expansion_detect_callback(void* context) {
|
|||||||
UNUSED(status);
|
UNUSED(status);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void expansion_worker_callback(void* context) {
|
static void expansion_worker_callback(void* context, ExpansionWorkerCallbackReason reason) {
|
||||||
furi_assert(context);
|
furi_assert(context);
|
||||||
Expansion* instance = context;
|
Expansion* instance = context;
|
||||||
|
|
||||||
ExpansionMessage message = {
|
ExpansionMessage message;
|
||||||
.type = ExpansionMessageTypeModuleDisconnected,
|
switch(reason) {
|
||||||
.api_lock = NULL, // Not locking the API here to avoid a deadlock
|
case ExpansionWorkerCallbackReasonExit:
|
||||||
|
message.type = ExpansionMessageTypeModuleDisconnected;
|
||||||
|
message.api_lock = NULL; // Not locking the API here to avoid a deadlock
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ExpansionWorkerCallbackReasonConnected:
|
||||||
|
message.type = ExpansionMessageTypeConnectionEstablished;
|
||||||
|
message.api_lock = api_lock_alloc_locked();
|
||||||
|
break;
|
||||||
};
|
};
|
||||||
|
|
||||||
const FuriStatus status = furi_message_queue_put(instance->queue, &message, FuriWaitForever);
|
const FuriStatus status = furi_message_queue_put(instance->queue, &message, FuriWaitForever);
|
||||||
@@ -106,7 +118,9 @@ static void
|
|||||||
UNUSED(data);
|
UNUSED(data);
|
||||||
if(instance->state == ExpansionStateDisabled) {
|
if(instance->state == ExpansionStateDisabled) {
|
||||||
return;
|
return;
|
||||||
} else if(instance->state == ExpansionStateRunning) {
|
} else if(
|
||||||
|
instance->state == ExpansionStateRunning ||
|
||||||
|
instance->state == ExpansionStateConnectionEstablished) {
|
||||||
expansion_worker_stop(instance->worker);
|
expansion_worker_stop(instance->worker);
|
||||||
expansion_worker_free(instance->worker);
|
expansion_worker_free(instance->worker);
|
||||||
} else {
|
} else {
|
||||||
@@ -124,7 +138,9 @@ static void expansion_control_handler_set_listen_serial(
|
|||||||
if(instance->state != ExpansionStateDisabled && instance->serial_id == data->serial_id) {
|
if(instance->state != ExpansionStateDisabled && instance->serial_id == data->serial_id) {
|
||||||
return;
|
return;
|
||||||
|
|
||||||
} else if(instance->state == ExpansionStateRunning) {
|
} else if(
|
||||||
|
instance->state == ExpansionStateRunning ||
|
||||||
|
instance->state == ExpansionStateConnectionEstablished) {
|
||||||
expansion_worker_stop(instance->worker);
|
expansion_worker_stop(instance->worker);
|
||||||
expansion_worker_free(instance->worker);
|
expansion_worker_free(instance->worker);
|
||||||
|
|
||||||
@@ -182,7 +198,8 @@ static void expansion_control_handler_module_disconnected(
|
|||||||
Expansion* instance,
|
Expansion* instance,
|
||||||
const ExpansionMessageData* data) {
|
const ExpansionMessageData* data) {
|
||||||
UNUSED(data);
|
UNUSED(data);
|
||||||
if(instance->state != ExpansionStateRunning) {
|
if(instance->state != ExpansionStateRunning &&
|
||||||
|
instance->state != ExpansionStateConnectionEstablished) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -192,6 +209,23 @@ static void expansion_control_handler_module_disconnected(
|
|||||||
instance->serial_id, expansion_detect_callback, instance);
|
instance->serial_id, expansion_detect_callback, instance);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void expansion_control_handler_connection_established(
|
||||||
|
Expansion* instance,
|
||||||
|
const ExpansionMessageData* data) {
|
||||||
|
UNUSED(data);
|
||||||
|
if(instance->state != ExpansionStateRunning &&
|
||||||
|
instance->state != ExpansionStateConnectionEstablished) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
instance->state = ExpansionStateConnectionEstablished;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
expansion_control_handler_is_connected(Expansion* instance, const ExpansionMessageData* data) {
|
||||||
|
*data->is_connected = instance->state == ExpansionStateConnectionEstablished;
|
||||||
|
}
|
||||||
|
|
||||||
typedef void (*ExpansionControlHandler)(Expansion*, const ExpansionMessageData*);
|
typedef void (*ExpansionControlHandler)(Expansion*, const ExpansionMessageData*);
|
||||||
|
|
||||||
static const ExpansionControlHandler expansion_control_handlers[] = {
|
static const ExpansionControlHandler expansion_control_handlers[] = {
|
||||||
@@ -201,6 +235,8 @@ static const ExpansionControlHandler expansion_control_handlers[] = {
|
|||||||
[ExpansionMessageTypeReloadSettings] = expansion_control_handler_reload_settings,
|
[ExpansionMessageTypeReloadSettings] = expansion_control_handler_reload_settings,
|
||||||
[ExpansionMessageTypeModuleConnected] = expansion_control_handler_module_connected,
|
[ExpansionMessageTypeModuleConnected] = expansion_control_handler_module_connected,
|
||||||
[ExpansionMessageTypeModuleDisconnected] = expansion_control_handler_module_disconnected,
|
[ExpansionMessageTypeModuleDisconnected] = expansion_control_handler_module_disconnected,
|
||||||
|
[ExpansionMessageTypeConnectionEstablished] = expansion_control_handler_connection_established,
|
||||||
|
[ExpansionMessageTypeIsConnected] = expansion_control_handler_is_connected,
|
||||||
};
|
};
|
||||||
|
|
||||||
static int32_t expansion_control(void* context) {
|
static int32_t expansion_control(void* context) {
|
||||||
@@ -295,6 +331,22 @@ void expansion_disable(Expansion* instance) {
|
|||||||
api_lock_wait_unlock_and_free(message.api_lock);
|
api_lock_wait_unlock_and_free(message.api_lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool expansion_is_connected(Expansion* instance) {
|
||||||
|
furi_check(instance);
|
||||||
|
bool is_connected;
|
||||||
|
|
||||||
|
ExpansionMessage message = {
|
||||||
|
.type = ExpansionMessageTypeIsConnected,
|
||||||
|
.data.is_connected = &is_connected,
|
||||||
|
.api_lock = api_lock_alloc_locked(),
|
||||||
|
};
|
||||||
|
|
||||||
|
furi_message_queue_put(instance->queue, &message, FuriWaitForever);
|
||||||
|
api_lock_wait_unlock_and_free(message.api_lock);
|
||||||
|
|
||||||
|
return is_connected;
|
||||||
|
}
|
||||||
|
|
||||||
void expansion_set_listen_serial(Expansion* instance, FuriHalSerialId serial_id) {
|
void expansion_set_listen_serial(Expansion* instance, FuriHalSerialId serial_id) {
|
||||||
furi_check(instance);
|
furi_check(instance);
|
||||||
furi_check(serial_id < FuriHalSerialIdMax);
|
furi_check(serial_id < FuriHalSerialIdMax);
|
||||||
|
|||||||
@@ -50,6 +50,15 @@ void expansion_enable(Expansion* instance);
|
|||||||
*/
|
*/
|
||||||
void expansion_disable(Expansion* instance);
|
void expansion_disable(Expansion* instance);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Check if an expansion module is connected.
|
||||||
|
*
|
||||||
|
* @param[in,out] instance pointer to the Expansion instance.
|
||||||
|
*
|
||||||
|
* @returns true if the module is connected and initialized, false otherwise.
|
||||||
|
*/
|
||||||
|
bool expansion_is_connected(Expansion* instance);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Enable support for expansion modules on designated serial port.
|
* @brief Enable support for expansion modules on designated serial port.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -35,7 +35,8 @@ typedef enum {
|
|||||||
ExpansionWorkerFlagError = 1 << 2,
|
ExpansionWorkerFlagError = 1 << 2,
|
||||||
} ExpansionWorkerFlag;
|
} ExpansionWorkerFlag;
|
||||||
|
|
||||||
#define EXPANSION_ALL_FLAGS (ExpansionWorkerFlagData | ExpansionWorkerFlagStop)
|
#define EXPANSION_ALL_FLAGS \
|
||||||
|
(ExpansionWorkerFlagData | ExpansionWorkerFlagStop | ExpansionWorkerFlagError)
|
||||||
|
|
||||||
struct ExpansionWorker {
|
struct ExpansionWorker {
|
||||||
FuriThread* thread;
|
FuriThread* thread;
|
||||||
@@ -225,6 +226,7 @@ static bool expansion_worker_handle_state_handshake(
|
|||||||
|
|
||||||
if(furi_hal_serial_is_baud_rate_supported(instance->serial_handle, baud_rate)) {
|
if(furi_hal_serial_is_baud_rate_supported(instance->serial_handle, baud_rate)) {
|
||||||
instance->state = ExpansionWorkerStateConnected;
|
instance->state = ExpansionWorkerStateConnected;
|
||||||
|
instance->callback(instance->cb_context, ExpansionWorkerCallbackReasonConnected);
|
||||||
// Send response at previous baud rate
|
// Send response at previous baud rate
|
||||||
if(!expansion_worker_send_status_response(instance, ExpansionFrameErrorNone)) break;
|
if(!expansion_worker_send_status_response(instance, ExpansionFrameErrorNone)) break;
|
||||||
furi_hal_serial_set_br(instance->serial_handle, baud_rate);
|
furi_hal_serial_set_br(instance->serial_handle, baud_rate);
|
||||||
@@ -360,6 +362,8 @@ static int32_t expansion_worker(void* context) {
|
|||||||
expansion_worker_state_machine(instance);
|
expansion_worker_state_machine(instance);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
furi_hal_serial_async_rx_stop(instance->serial_handle);
|
||||||
|
|
||||||
if(instance->state == ExpansionWorkerStateRpcActive) {
|
if(instance->state == ExpansionWorkerStateRpcActive) {
|
||||||
expansion_worker_rpc_session_close(instance);
|
expansion_worker_rpc_session_close(instance);
|
||||||
}
|
}
|
||||||
@@ -371,7 +375,7 @@ static int32_t expansion_worker(void* context) {
|
|||||||
|
|
||||||
// Do not invoke worker callback on user-requested exit
|
// Do not invoke worker callback on user-requested exit
|
||||||
if((instance->exit_reason != ExpansionWorkerExitReasonUser) && (instance->callback != NULL)) {
|
if((instance->exit_reason != ExpansionWorkerExitReasonUser) && (instance->callback != NULL)) {
|
||||||
instance->callback(instance->cb_context);
|
instance->callback(instance->cb_context, ExpansionWorkerCallbackReasonExit);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
@@ -17,14 +17,20 @@
|
|||||||
*/
|
*/
|
||||||
typedef struct ExpansionWorker ExpansionWorker;
|
typedef struct ExpansionWorker ExpansionWorker;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
ExpansionWorkerCallbackReasonExit,
|
||||||
|
ExpansionWorkerCallbackReasonConnected,
|
||||||
|
} ExpansionWorkerCallbackReason;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Worker callback type.
|
* @brief Worker callback type.
|
||||||
*
|
*
|
||||||
* @see expansion_worker_set_callback()
|
* @see expansion_worker_set_callback()
|
||||||
*
|
*
|
||||||
* @param[in,out] context pointer to a user-defined object.
|
* @param[in,out] context pointer to a user-defined object.
|
||||||
|
* @param[in] reason reason for the callback.
|
||||||
*/
|
*/
|
||||||
typedef void (*ExpansionWorkerCallback)(void* context);
|
typedef void (*ExpansionWorkerCallback)(void* context, ExpansionWorkerCallbackReason reason);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Create an expansion worker instance.
|
* @brief Create an expansion worker instance.
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <m-array.h>
|
#include <m-array.h>
|
||||||
|
|
||||||
|
#define SCROLL_INTERVAL (333)
|
||||||
#define ITEM_FIRST_OFFSET 17
|
#define ITEM_FIRST_OFFSET 17
|
||||||
#define ITEM_NEXT_OFFSET 4
|
#define ITEM_NEXT_OFFSET 4
|
||||||
#define ITEM_HEIGHT 14
|
#define ITEM_HEIGHT 14
|
||||||
@@ -35,13 +36,56 @@ typedef struct {
|
|||||||
ButtonMenuItemArray_t items;
|
ButtonMenuItemArray_t items;
|
||||||
size_t position;
|
size_t position;
|
||||||
const char* header;
|
const char* header;
|
||||||
|
size_t scroll_counter;
|
||||||
|
FuriTimer* scroll_timer;
|
||||||
} ButtonMenuModel;
|
} ButtonMenuModel;
|
||||||
|
|
||||||
|
static void button_menu_draw_text(
|
||||||
|
Canvas* canvas,
|
||||||
|
uint8_t item_x,
|
||||||
|
uint8_t item_y,
|
||||||
|
const char* text,
|
||||||
|
bool selected,
|
||||||
|
ButtonMenuModel* model) {
|
||||||
|
FuriString* disp_str;
|
||||||
|
disp_str = furi_string_alloc_set(text);
|
||||||
|
bool draw_static = true;
|
||||||
|
|
||||||
|
if(selected) {
|
||||||
|
size_t text_width = canvas_string_width(canvas, furi_string_get_cstr(disp_str));
|
||||||
|
if(text_width >= ITEM_WIDTH - 8) {
|
||||||
|
elements_scrollable_text_line(
|
||||||
|
canvas,
|
||||||
|
item_x + 4,
|
||||||
|
item_y + ITEM_HEIGHT - 4,
|
||||||
|
ITEM_WIDTH - 8,
|
||||||
|
disp_str,
|
||||||
|
model->scroll_counter,
|
||||||
|
false);
|
||||||
|
draw_static = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(draw_static) {
|
||||||
|
elements_string_fit_width(canvas, disp_str, ITEM_WIDTH - 6);
|
||||||
|
canvas_draw_str_aligned(
|
||||||
|
canvas,
|
||||||
|
item_x + (ITEM_WIDTH / 2),
|
||||||
|
item_y + (ITEM_HEIGHT / 2),
|
||||||
|
AlignCenter,
|
||||||
|
AlignCenter,
|
||||||
|
furi_string_get_cstr(disp_str));
|
||||||
|
}
|
||||||
|
|
||||||
|
furi_string_free(disp_str);
|
||||||
|
}
|
||||||
|
|
||||||
static void button_menu_draw_control_button(
|
static void button_menu_draw_control_button(
|
||||||
Canvas* canvas,
|
Canvas* canvas,
|
||||||
uint8_t item_position,
|
uint8_t item_position,
|
||||||
const char* text,
|
const char* text,
|
||||||
bool selected) {
|
bool selected,
|
||||||
|
ButtonMenuModel* model) {
|
||||||
furi_assert(canvas);
|
furi_assert(canvas);
|
||||||
furi_assert(text);
|
furi_assert(text);
|
||||||
|
|
||||||
@@ -54,20 +98,16 @@ static void button_menu_draw_control_button(
|
|||||||
elements_slightly_rounded_box(canvas, item_x, item_y, ITEM_WIDTH, ITEM_HEIGHT);
|
elements_slightly_rounded_box(canvas, item_x, item_y, ITEM_WIDTH, ITEM_HEIGHT);
|
||||||
canvas_set_color(canvas, ColorWhite);
|
canvas_set_color(canvas, ColorWhite);
|
||||||
}
|
}
|
||||||
canvas_draw_str_aligned(
|
|
||||||
canvas,
|
button_menu_draw_text(canvas, item_x, item_y, text, selected, model);
|
||||||
item_x + (ITEM_WIDTH / 2),
|
|
||||||
item_y + (ITEM_HEIGHT / 2),
|
|
||||||
AlignCenter,
|
|
||||||
AlignCenter,
|
|
||||||
text);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void button_menu_draw_common_button(
|
static void button_menu_draw_common_button(
|
||||||
Canvas* canvas,
|
Canvas* canvas,
|
||||||
uint8_t item_position,
|
uint8_t item_position,
|
||||||
const char* text,
|
const char* text,
|
||||||
bool selected) {
|
bool selected,
|
||||||
|
ButtonMenuModel* model) {
|
||||||
furi_assert(canvas);
|
furi_assert(canvas);
|
||||||
furi_assert(text);
|
furi_assert(text);
|
||||||
|
|
||||||
@@ -83,19 +123,7 @@ static void button_menu_draw_common_button(
|
|||||||
canvas_draw_rframe(canvas, item_x, item_y, ITEM_WIDTH, ITEM_HEIGHT, 5);
|
canvas_draw_rframe(canvas, item_x, item_y, ITEM_WIDTH, ITEM_HEIGHT, 5);
|
||||||
}
|
}
|
||||||
|
|
||||||
FuriString* disp_str;
|
button_menu_draw_text(canvas, item_x, item_y, text, selected, model);
|
||||||
disp_str = furi_string_alloc_set(text);
|
|
||||||
elements_string_fit_width(canvas, disp_str, ITEM_WIDTH - 6);
|
|
||||||
|
|
||||||
canvas_draw_str_aligned(
|
|
||||||
canvas,
|
|
||||||
item_x + (ITEM_WIDTH / 2),
|
|
||||||
item_y + (ITEM_HEIGHT / 2),
|
|
||||||
AlignCenter,
|
|
||||||
AlignCenter,
|
|
||||||
furi_string_get_cstr(disp_str));
|
|
||||||
|
|
||||||
furi_string_free(disp_str);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void button_menu_view_draw_callback(Canvas* canvas, void* _model) {
|
static void button_menu_view_draw_callback(Canvas* canvas, void* _model) {
|
||||||
@@ -120,9 +148,17 @@ static void button_menu_view_draw_callback(Canvas* canvas, void* _model) {
|
|||||||
if(model->header) {
|
if(model->header) {
|
||||||
FuriString* disp_str;
|
FuriString* disp_str;
|
||||||
disp_str = furi_string_alloc_set(model->header);
|
disp_str = furi_string_alloc_set(model->header);
|
||||||
elements_string_fit_width(canvas, disp_str, ITEM_WIDTH - 6);
|
size_t header_width = canvas_string_width(canvas, furi_string_get_cstr(disp_str));
|
||||||
|
|
||||||
|
if(header_width >= ITEM_WIDTH - 8) {
|
||||||
|
elements_scrollable_text_line(
|
||||||
|
canvas, 3, 13, ITEM_WIDTH - 8, disp_str, model->scroll_counter, false);
|
||||||
|
} else {
|
||||||
|
elements_string_fit_width(canvas, disp_str, ITEM_WIDTH - 8);
|
||||||
canvas_draw_str_aligned(
|
canvas_draw_str_aligned(
|
||||||
canvas, 32, 10, AlignCenter, AlignCenter, furi_string_get_cstr(disp_str));
|
canvas, 32, 10, AlignCenter, AlignCenter, furi_string_get_cstr(disp_str));
|
||||||
|
}
|
||||||
|
|
||||||
furi_string_free(disp_str);
|
furi_string_free(disp_str);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -137,13 +173,15 @@ static void button_menu_view_draw_callback(Canvas* canvas, void* _model) {
|
|||||||
canvas,
|
canvas,
|
||||||
item_position % BUTTONS_PER_SCREEN,
|
item_position % BUTTONS_PER_SCREEN,
|
||||||
ButtonMenuItemArray_cref(it)->label,
|
ButtonMenuItemArray_cref(it)->label,
|
||||||
(item_position == model->position));
|
(item_position == model->position),
|
||||||
|
model);
|
||||||
} else if(ButtonMenuItemArray_cref(it)->type == ButtonMenuItemTypeCommon) {
|
} else if(ButtonMenuItemArray_cref(it)->type == ButtonMenuItemTypeCommon) {
|
||||||
button_menu_draw_common_button(
|
button_menu_draw_common_button(
|
||||||
canvas,
|
canvas,
|
||||||
item_position % BUTTONS_PER_SCREEN,
|
item_position % BUTTONS_PER_SCREEN,
|
||||||
ButtonMenuItemArray_cref(it)->label,
|
ButtonMenuItemArray_cref(it)->label,
|
||||||
(item_position == model->position));
|
(item_position == model->position),
|
||||||
|
model);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -158,8 +196,10 @@ static void button_menu_process_up(ButtonMenu* button_menu) {
|
|||||||
{
|
{
|
||||||
if(model->position > 0) {
|
if(model->position > 0) {
|
||||||
model->position--;
|
model->position--;
|
||||||
|
model->scroll_counter = 0;
|
||||||
} else {
|
} else {
|
||||||
model->position = ButtonMenuItemArray_size(model->items) - 1;
|
model->position = ButtonMenuItemArray_size(model->items) - 1;
|
||||||
|
model->scroll_counter = 0;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
true);
|
true);
|
||||||
@@ -174,8 +214,10 @@ static void button_menu_process_down(ButtonMenu* button_menu) {
|
|||||||
{
|
{
|
||||||
if(model->position < (ButtonMenuItemArray_size(model->items) - 1)) {
|
if(model->position < (ButtonMenuItemArray_size(model->items) - 1)) {
|
||||||
model->position++;
|
model->position++;
|
||||||
|
model->scroll_counter = 0;
|
||||||
} else {
|
} else {
|
||||||
model->position = 0;
|
model->position = 0;
|
||||||
|
model->scroll_counter = 0;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
true);
|
true);
|
||||||
@@ -193,8 +235,10 @@ static void button_menu_process_right(ButtonMenu* button_menu) {
|
|||||||
position_candidate -= position_candidate % BUTTONS_PER_SCREEN;
|
position_candidate -= position_candidate % BUTTONS_PER_SCREEN;
|
||||||
if(position_candidate < (ButtonMenuItemArray_size(model->items))) {
|
if(position_candidate < (ButtonMenuItemArray_size(model->items))) {
|
||||||
model->position = position_candidate;
|
model->position = position_candidate;
|
||||||
|
model->scroll_counter = 0;
|
||||||
} else {
|
} else {
|
||||||
model->position = 0;
|
model->position = 0;
|
||||||
|
model->scroll_counter = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -217,6 +261,7 @@ static void button_menu_process_left(ButtonMenu* button_menu) {
|
|||||||
};
|
};
|
||||||
position_candidate -= position_candidate % BUTTONS_PER_SCREEN;
|
position_candidate -= position_candidate % BUTTONS_PER_SCREEN;
|
||||||
model->position = position_candidate;
|
model->position = position_candidate;
|
||||||
|
model->scroll_counter = 0;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
true);
|
true);
|
||||||
@@ -314,6 +359,7 @@ void button_menu_reset(ButtonMenu* button_menu) {
|
|||||||
ButtonMenuItemArray_reset(model->items);
|
ButtonMenuItemArray_reset(model->items);
|
||||||
model->position = 0;
|
model->position = 0;
|
||||||
model->header = NULL;
|
model->header = NULL;
|
||||||
|
model->scroll_counter = 0;
|
||||||
},
|
},
|
||||||
true);
|
true);
|
||||||
}
|
}
|
||||||
@@ -351,6 +397,12 @@ ButtonMenuItem* button_menu_add_item(
|
|||||||
return item;
|
return item;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void button_menu_process_timer_callback(void* context) {
|
||||||
|
ButtonMenu* button_menu = context;
|
||||||
|
with_view_model(
|
||||||
|
button_menu->view, ButtonMenuModel * model, { model->scroll_counter++; }, true);
|
||||||
|
}
|
||||||
|
|
||||||
ButtonMenu* button_menu_alloc(void) {
|
ButtonMenu* button_menu_alloc(void) {
|
||||||
ButtonMenu* button_menu = malloc(sizeof(ButtonMenu));
|
ButtonMenu* button_menu = malloc(sizeof(ButtonMenu));
|
||||||
button_menu->view = view_alloc();
|
button_menu->view = view_alloc();
|
||||||
@@ -367,6 +419,10 @@ ButtonMenu* button_menu_alloc(void) {
|
|||||||
ButtonMenuItemArray_init(model->items);
|
ButtonMenuItemArray_init(model->items);
|
||||||
model->position = 0;
|
model->position = 0;
|
||||||
model->header = NULL;
|
model->header = NULL;
|
||||||
|
model->scroll_counter = 0;
|
||||||
|
model->scroll_timer = furi_timer_alloc(
|
||||||
|
button_menu_process_timer_callback, FuriTimerTypePeriodic, button_menu);
|
||||||
|
furi_timer_start(model->scroll_timer, SCROLL_INTERVAL);
|
||||||
},
|
},
|
||||||
true);
|
true);
|
||||||
|
|
||||||
@@ -380,7 +436,11 @@ void button_menu_free(ButtonMenu* button_menu) {
|
|||||||
with_view_model(
|
with_view_model(
|
||||||
button_menu->view,
|
button_menu->view,
|
||||||
ButtonMenuModel * model,
|
ButtonMenuModel * model,
|
||||||
{ ButtonMenuItemArray_clear(model->items); },
|
{
|
||||||
|
ButtonMenuItemArray_clear(model->items);
|
||||||
|
furi_timer_stop(model->scroll_timer);
|
||||||
|
furi_timer_free(model->scroll_timer);
|
||||||
|
},
|
||||||
true);
|
true);
|
||||||
view_free(button_menu->view);
|
view_free(button_menu->view);
|
||||||
free(button_menu);
|
free(button_menu);
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
struct Submenu {
|
struct Submenu {
|
||||||
View* view;
|
View* view;
|
||||||
|
|
||||||
FuriTimer* locked_timer;
|
FuriTimer* locked_timer;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -19,6 +20,7 @@ typedef struct {
|
|||||||
};
|
};
|
||||||
void* callback_context;
|
void* callback_context;
|
||||||
bool has_extended_events;
|
bool has_extended_events;
|
||||||
|
|
||||||
bool locked;
|
bool locked;
|
||||||
FuriString* locked_message;
|
FuriString* locked_message;
|
||||||
} SubmenuItem;
|
} SubmenuItem;
|
||||||
@@ -68,6 +70,7 @@ typedef struct {
|
|||||||
FuriString* header;
|
FuriString* header;
|
||||||
size_t position;
|
size_t position;
|
||||||
size_t window_position;
|
size_t window_position;
|
||||||
|
|
||||||
bool locked_message_visible;
|
bool locked_message_visible;
|
||||||
bool is_vertical;
|
bool is_vertical;
|
||||||
} SubmenuModel;
|
} SubmenuModel;
|
||||||
@@ -76,9 +79,9 @@ static void submenu_process_up(Submenu* submenu);
|
|||||||
static void submenu_process_down(Submenu* submenu);
|
static void submenu_process_down(Submenu* submenu);
|
||||||
static void submenu_process_ok(Submenu* submenu, InputType input_type);
|
static void submenu_process_ok(Submenu* submenu, InputType input_type);
|
||||||
|
|
||||||
static size_t submenu_items_on_screen(bool header, bool vertical) {
|
static size_t submenu_items_on_screen(SubmenuModel* model) {
|
||||||
size_t res = (vertical) ? 8 : 4;
|
size_t res = (model->is_vertical) ? 8 : 4;
|
||||||
return (header) ? res - 1 : res;
|
return (furi_string_empty(model->header)) ? res : res - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void submenu_view_draw_callback(Canvas* canvas, void* _model) {
|
static void submenu_view_draw_callback(Canvas* canvas, void* _model) {
|
||||||
@@ -101,9 +104,9 @@ static void submenu_view_draw_callback(Canvas* canvas, void* _model) {
|
|||||||
for(SubmenuItemArray_it(it, model->items); !SubmenuItemArray_end_p(it);
|
for(SubmenuItemArray_it(it, model->items); !SubmenuItemArray_end_p(it);
|
||||||
SubmenuItemArray_next(it)) {
|
SubmenuItemArray_next(it)) {
|
||||||
const size_t item_position = position - model->window_position;
|
const size_t item_position = position - model->window_position;
|
||||||
const size_t items_on_screen =
|
const size_t items_on_screen = submenu_items_on_screen(model);
|
||||||
submenu_items_on_screen(!furi_string_empty(model->header), model->is_vertical);
|
uint8_t y_offset = furi_string_empty(model->header) ? 0 : item_height;
|
||||||
uint8_t y_offset = furi_string_empty(model->header) ? 0 : 16;
|
bool is_locked = SubmenuItemArray_cref(it)->locked;
|
||||||
|
|
||||||
if(item_position < items_on_screen) {
|
if(item_position < items_on_screen) {
|
||||||
if(position == model->position) {
|
if(position == model->position) {
|
||||||
@@ -119,7 +122,7 @@ static void submenu_view_draw_callback(Canvas* canvas, void* _model) {
|
|||||||
canvas_set_color(canvas, ColorBlack);
|
canvas_set_color(canvas, ColorBlack);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(SubmenuItemArray_cref(it)->locked) {
|
if(is_locked) {
|
||||||
canvas_draw_icon(
|
canvas_draw_icon(
|
||||||
canvas,
|
canvas,
|
||||||
item_width - 10,
|
item_width - 10,
|
||||||
@@ -127,10 +130,8 @@ static void submenu_view_draw_callback(Canvas* canvas, void* _model) {
|
|||||||
&I_Lock_7x8);
|
&I_Lock_7x8);
|
||||||
}
|
}
|
||||||
|
|
||||||
FuriString* disp_str;
|
FuriString* disp_str = furi_string_alloc_set(SubmenuItemArray_cref(it)->label);
|
||||||
disp_str = furi_string_alloc_set(SubmenuItemArray_cref(it)->label);
|
elements_string_fit_width(canvas, disp_str, item_width - (is_locked ? 21 : 11));
|
||||||
elements_string_fit_width(
|
|
||||||
canvas, disp_str, item_width - (SubmenuItemArray_cref(it)->locked ? 21 : 11));
|
|
||||||
|
|
||||||
canvas_draw_str(
|
canvas_draw_str(
|
||||||
canvas,
|
canvas,
|
||||||
@@ -161,25 +162,14 @@ static void submenu_view_draw_callback(Canvas* canvas, void* _model) {
|
|||||||
|
|
||||||
canvas_draw_rframe(canvas, frame_x, frame_y, frame_width, frame_height, 3);
|
canvas_draw_rframe(canvas, frame_x, frame_y, frame_width, frame_height, 3);
|
||||||
canvas_draw_rframe(canvas, frame_x + 1, frame_y + 1, frame_width - 2, frame_height - 2, 2);
|
canvas_draw_rframe(canvas, frame_x + 1, frame_y + 1, frame_width - 2, frame_height - 2, 2);
|
||||||
if(model->is_vertical) {
|
|
||||||
elements_multiline_text_aligned(
|
elements_multiline_text_aligned(
|
||||||
canvas,
|
canvas,
|
||||||
32,
|
(model->is_vertical) ? 32 : 84,
|
||||||
42,
|
(model->is_vertical) ? 42 : 32,
|
||||||
AlignCenter,
|
AlignCenter,
|
||||||
AlignCenter,
|
AlignCenter,
|
||||||
furi_string_get_cstr(
|
furi_string_get_cstr(
|
||||||
SubmenuItemArray_get(model->items, model->position)->locked_message));
|
SubmenuItemArray_get(model->items, model->position)->locked_message));
|
||||||
} else {
|
|
||||||
elements_multiline_text_aligned(
|
|
||||||
canvas,
|
|
||||||
84,
|
|
||||||
32,
|
|
||||||
AlignCenter,
|
|
||||||
AlignCenter,
|
|
||||||
furi_string_get_cstr(
|
|
||||||
SubmenuItemArray_get(model->items, model->position)->locked_message));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -195,8 +185,7 @@ static bool submenu_view_input_callback(InputEvent* event, void* context) {
|
|||||||
{ locked_message_visible = model->locked_message_visible; },
|
{ locked_message_visible = model->locked_message_visible; },
|
||||||
false);
|
false);
|
||||||
|
|
||||||
if((!(event->type == InputTypePress) && !(event->type == InputTypeRelease)) &&
|
if(locked_message_visible && (event->type == InputTypeShort || event->type == InputTypeLong)) {
|
||||||
locked_message_visible) {
|
|
||||||
with_view_model(
|
with_view_model(
|
||||||
submenu->view, SubmenuModel * model, { model->locked_message_visible = false; }, true);
|
submenu->view, SubmenuModel * model, { model->locked_message_visible = false; }, true);
|
||||||
consumed = true;
|
consumed = true;
|
||||||
@@ -303,6 +292,9 @@ void submenu_add_lockable_item(
|
|||||||
SubmenuItem* item = NULL;
|
SubmenuItem* item = NULL;
|
||||||
furi_check(label);
|
furi_check(label);
|
||||||
furi_check(submenu);
|
furi_check(submenu);
|
||||||
|
if(locked) {
|
||||||
|
furi_check(locked_message);
|
||||||
|
}
|
||||||
|
|
||||||
with_view_model(
|
with_view_model(
|
||||||
submenu->view,
|
submenu->view,
|
||||||
@@ -366,6 +358,25 @@ void submenu_change_item_label(Submenu* submenu, uint32_t index, const char* lab
|
|||||||
true);
|
true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void submenu_remove_item(Submenu* submenu, uint32_t index) {
|
||||||
|
furi_check(submenu);
|
||||||
|
|
||||||
|
with_view_model(
|
||||||
|
submenu->view,
|
||||||
|
SubmenuModel * model,
|
||||||
|
{
|
||||||
|
SubmenuItemArray_it_t it;
|
||||||
|
for(SubmenuItemArray_it(it, model->items); !SubmenuItemArray_end_p(it);
|
||||||
|
SubmenuItemArray_next(it)) {
|
||||||
|
if(index == SubmenuItemArray_cref(it)->index) {
|
||||||
|
SubmenuItemArray_remove(model->items, it);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
true);
|
||||||
|
}
|
||||||
|
|
||||||
void submenu_reset(Submenu* submenu) {
|
void submenu_reset(Submenu* submenu) {
|
||||||
furi_check(submenu);
|
furi_check(submenu);
|
||||||
view_set_orientation(submenu->view, ViewOrientationHorizontal);
|
view_set_orientation(submenu->view, ViewOrientationHorizontal);
|
||||||
@@ -431,8 +442,7 @@ void submenu_set_selected_item(Submenu* submenu, uint32_t index) {
|
|||||||
model->window_position -= 1;
|
model->window_position -= 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
const size_t items_on_screen =
|
const size_t items_on_screen = submenu_items_on_screen(model);
|
||||||
submenu_items_on_screen(!furi_string_empty(model->header), model->is_vertical);
|
|
||||||
|
|
||||||
if(items_size <= items_on_screen) {
|
if(items_size <= items_on_screen) {
|
||||||
model->window_position = 0;
|
model->window_position = 0;
|
||||||
@@ -451,8 +461,7 @@ void submenu_process_up(Submenu* submenu) {
|
|||||||
submenu->view,
|
submenu->view,
|
||||||
SubmenuModel * model,
|
SubmenuModel * model,
|
||||||
{
|
{
|
||||||
const size_t items_on_screen =
|
const size_t items_on_screen = submenu_items_on_screen(model);
|
||||||
submenu_items_on_screen(!furi_string_empty(model->header), model->is_vertical);
|
|
||||||
const size_t items_size = SubmenuItemArray_size(model->items);
|
const size_t items_size = SubmenuItemArray_size(model->items);
|
||||||
|
|
||||||
if(model->position > 0) {
|
if(model->position > 0) {
|
||||||
@@ -475,8 +484,7 @@ void submenu_process_down(Submenu* submenu) {
|
|||||||
submenu->view,
|
submenu->view,
|
||||||
SubmenuModel * model,
|
SubmenuModel * model,
|
||||||
{
|
{
|
||||||
const size_t items_on_screen =
|
const size_t items_on_screen = submenu_items_on_screen(model);
|
||||||
submenu_items_on_screen(!furi_string_empty(model->header), model->is_vertical);
|
|
||||||
const size_t items_size = SubmenuItemArray_size(model->items);
|
const size_t items_size = SubmenuItemArray_size(model->items);
|
||||||
|
|
||||||
if(model->position < items_size - 1) {
|
if(model->position < items_size - 1) {
|
||||||
@@ -504,7 +512,8 @@ void submenu_process_ok(Submenu* submenu, InputType input_type) {
|
|||||||
if(model->position < items_size) {
|
if(model->position < items_size) {
|
||||||
item = SubmenuItemArray_get(model->items, model->position);
|
item = SubmenuItemArray_get(model->items, model->position);
|
||||||
}
|
}
|
||||||
if(item && item->locked) {
|
if(item && item->locked &&
|
||||||
|
(input_type == InputTypeShort || input_type == InputTypeLong)) {
|
||||||
model->locked_message_visible = true;
|
model->locked_message_visible = true;
|
||||||
furi_timer_start(submenu->locked_timer, furi_kernel_get_tick_frequency() * 3);
|
furi_timer_start(submenu->locked_timer, furi_kernel_get_tick_frequency() * 3);
|
||||||
}
|
}
|
||||||
@@ -540,11 +549,9 @@ void submenu_set_header(Submenu* submenu, const char* header) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void submenu_set_orientation(Submenu* submenu, ViewOrientation orientation) {
|
void submenu_set_orientation(Submenu* submenu, ViewOrientation orientation) {
|
||||||
furi_assert(submenu);
|
furi_check(submenu);
|
||||||
const bool is_vertical =
|
const bool is_vertical = orientation == ViewOrientationVertical ||
|
||||||
(orientation == ViewOrientationVertical || orientation == ViewOrientationVerticalFlip) ?
|
orientation == ViewOrientationVerticalFlip;
|
||||||
true :
|
|
||||||
false;
|
|
||||||
|
|
||||||
view_set_orientation(submenu->view, orientation);
|
view_set_orientation(submenu->view, orientation);
|
||||||
|
|
||||||
@@ -558,8 +565,7 @@ void submenu_set_orientation(Submenu* submenu, ViewOrientation orientation) {
|
|||||||
// Need if _set_orientation is called after _set_selected_item
|
// Need if _set_orientation is called after _set_selected_item
|
||||||
size_t position = model->position;
|
size_t position = model->position;
|
||||||
const size_t items_size = SubmenuItemArray_size(model->items);
|
const size_t items_size = SubmenuItemArray_size(model->items);
|
||||||
const size_t items_on_screen =
|
const size_t items_on_screen = submenu_items_on_screen(model);
|
||||||
submenu_items_on_screen(!furi_string_empty(model->header), model->is_vertical);
|
|
||||||
|
|
||||||
if(position >= items_size) {
|
if(position >= items_size) {
|
||||||
position = 0;
|
position = 0;
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user