1
mirror of https://github.com/DarkFlippers/unleashed-firmware.git synced 2025-12-12 20:49:49 +04:00

Compare commits

..

221 Commits

Author SHA1 Message Date
Methodius
b1a5d4fa8f Zolotaya Korona Online parser added 2024-01-13 17:01:06 +09:00
MX
634e841ce8 upd changelog 2024-01-13 03:24:27 +03:00
MX
924520a9a7 Revert "its time to enable this one"
This reverts commit bc1fdabce4.
2024-01-13 03:24:00 +03:00
Methodius
0d40e57cc8 LF RFID: Write with random password added [ci skip] 2024-01-13 02:39:50 +03:00
MX
81efe25a6e Merge branch 'ofw_dev' into dev 2024-01-12 15:39:40 +03:00
MX
781794f699 Revert "NFC: Skip system dict bug fixed"
This reverts commit 7de861bb4c.
2024-01-12 15:39:00 +03:00
hedger
0789cbdefa assets: checking limits on image size; ufbt: cdb target (#3359)
* scripts: assets: checking limits on image size
* ufbt: added "cdb" target for regenerating; also generating cdb on "vscode_dist"
* fbt: now also creating cdb for vscode_dist

Co-authored-by: あく <alleteam@gmail.com>
2024-01-12 17:58:37 +09:00
Leptopt1los
d289545bf8 NFC: system dict skip when user dict is skipped fix (#3356)
* NFC: system dict skip when user dict is skipped fix
* MFC poller allocator fix (by gornekich)

Co-authored-by: gornekich <n.gorbadey@gmail.com>
2024-01-12 17:41:19 +09:00
MX
e6db0842d4 upd changelog 2024-01-11 21:47:54 +03:00
MX
4b3d6e7332 Add NFC NDEF parser by Willy-JL
510404efe7/applications/main/nfc/plugins/supported_cards/ndef.c
2024-01-11 21:30:17 +03:00
MX
8a3557bc97 merge FuriHal: fix start duration furi_hal_subghz_async_tx 2024-01-11 17:33:29 +03:00
MX
b757544ede Merge branch 'ofw_dev' into dev 2024-01-11 17:10:11 +03:00
Skorpionm
34539cda17 FuriHal: fix start duration furi_hal_subghz_async_tx (#3230)
* FuriHal: fix start duration furi_hal_subghz_async_tx
* FuriHal: add check min duration arr for the first level
* FuriHal: fix conflict dev
* SubGhz: fix unit_test
* FuriHal: subghz internal fix start/stop transmit duration
* Drivers: subghz external fix start/stop transmit duration
* FuriHal: subghz optimization
* SubGhz: fix unit_test subghz
* FuriHal: subghz fix end duration if size == size dma buf
* FuriHal: revert enum values order, remove garbage
* FuriHal: revert one more small bit in subghz
* FuriHal: handle various corner cases in subghz transmission
* FuriHal: cleanup subghz code
* FuriHal: add parenthesis around value in subghz defines
* FuriHal: add packer subghz_async_tx
* FuriHal: more reliable subghz transmission end handling, fixes stuck transmission
* FuriHal: add subghz crutch docs, and rename some defines to conform naming standards
* FuriHal: subghz,  the logic of timers has been changed. disabling the shadow register ARR
* FuriHal: fix subghz off dma irq
* SubGhzExt: fun rename
* FuriHal,SubGhz: fix g0 state on reset, fix incorrect async_tx stop sequence, remove dead code.

Co-authored-by: あく <alleteam@gmail.com>
2024-01-11 16:56:14 +09:00
Augusto Zanellato
0b15fc3807 Fix MyKey production date parsing (#3332)
Co-authored-by: gornekich <n.gorbadey@gmail.com>
Co-authored-by: あく <alleteam@gmail.com>
2024-01-10 22:49:30 +09:00
Leptopt1los
4d99a454fd NFC: parsers minor cleanup (#3329)
* WashCity cards parser cleanup
* umarsh includes cleanup

Co-authored-by: gornekich <n.gorbadey@gmail.com>
Co-authored-by: あく <alleteam@gmail.com>
2024-01-10 20:55:45 +09:00
RebornedBrain
3452fbc351 [FL-3744] [NFC] Ntag success write freeze when not saved card (#3354)
* New scenes for ultralight poller write mode
* Added new button and transition logic for write operation
  For now write is only possible for NTAG21x cards with default password and no AUTHLIM set
* Poller states extended
* Enums and datatypes extended for new poller mode
* Added mode field to poller instance datatype
* New states for poller added in order to implement write mode
* Added new event type for locked cards in order to simplify state flow
* New logic for poller write commands
* Scenes adjustments
* Scenes renamed
* New field added to poller instance
* Now we write in 'page per call' mode
* Now function takes callback return value into account
* Callback will be called only in write mode
* Event type added
* Log adjusted and start page to write set
* Logs added and check in now false at start, then it moves to true
* Now mf_ultralight_poller_handler_request_write_data halts card in case of check failure and stops poller
* All fail events now returns NfcCommandStop callback
* In case of fail we move back properly
* Remove garbage
* Fix moving to previous screen in case of not saved ultralight

Co-authored-by: gornekich <n.gorbadey@gmail.com>
Co-authored-by: あく <alleteam@gmail.com>
2024-01-10 20:46:55 +09:00
hedger
8c04947aa2 ufbt: fixed generated project paths on Windows (#3339) 2024-01-10 20:37:28 +09:00
MX
c85e1305c0 upd changelog 2024-01-08 01:13:22 +03:00
MX
5dfa2fd24c Merge branch 'leptoptilos' into dev 2024-01-08 00:02:55 +03:00
Methodius
71ed7f8e94 NFC parsers false read() positive return fixed pt2 2024-01-08 05:49:58 +09:00
Methodius
4636444464 NFC parsers false read() positive return fixed 2024-01-08 05:39:36 +09:00
MX
e7bf9b4df2 return default update splash screen 2024-01-06 22:35:08 +03:00
MX
f224a8ec57 Merge branch 'leptoptilos' into dev [ci skip] 2024-01-06 21:51:31 +03:00
Methodius
4a47644e64 update ReadMe.md 2024-01-07 03:09:47 +09:00
MX
7af56e8717 upd changelog 2024-01-06 19:39:24 +03:00
MX
526f728f89 Merge branch 'leptoptilos' into dev 2024-01-06 19:35:38 +03:00
Methodius
7de861bb4c NFC: Skip system dict bug fixed 2024-01-06 18:47:35 +09:00
Methodius
4e068ba593 NFC: Set ATQA scene bit numbering changed 2024-01-06 15:38:08 +09:00
MX
0d68fb409c update changelog 2024-01-06 01:47:05 +03:00
MX
b77f41949c Merge branch 'nfc-parsers' into dev 2024-01-06 01:23:48 +03:00
MMX
1d35110a98 Merge pull request #690 from DarkFlippers/leptoptilos
NFC: Add manually MF Classic with custom UID
2024-01-05 22:53:30 +03:00
MMX
e5738ec703 Merge pull request #683 from yaba/nfc-washcity
Added plugin to read WashCity card balance
2024-01-05 22:53:05 +03:00
MMX
942f024db9 Merge pull request #688 from Eczbek/patch-1
Remove weird newline in `applications/ReadMe.md`
2024-01-05 22:52:32 +03:00
MX
a640dfecf8 Merge branch 'dev' into leptoptilos 2024-01-05 22:41:57 +03:00
MX
4fa617b772 Merge branch 'dev' into nfc-parsers 2024-01-05 22:41:47 +03:00
MX
4b95efda49 move hid and snake apps into main repo [ci skip] 2024-01-05 22:41:29 +03:00
Methodius
f6a363e7d2 Fix MyKey production date parsing by augustozanellato (https://github.com/flipperdevices/flipperzero-firmware/pull/3332/files) 2024-01-06 04:36:06 +09:00
Methodius
d2549b3b1a Code cleanup 2024-01-06 03:18:32 +09:00
Methodius
1bae3d19ee Merge remote-tracking branch 'upstream/dev' into nfc-washcity 2024-01-06 03:13:07 +09:00
Methodius
d1df26cc83 NFC: Add manually MF Classic UID desync bug fixed 2024-01-06 00:56:50 +09:00
MX
66756853a6 Merge branch 'dev' into leptoptilos 2024-01-05 18:39:42 +03:00
MX
00d9c60515 subghz use long press to exit transmitter [ci skip]
to avoid unwanted 2 buttons hold condition
holding arrow button and exit causes default button change, which is stays as hidden feature
but this change makes it harder to call it accidentally
2024-01-05 18:39:04 +03:00
MX
e243ca47ba Merge branch 'ofw_dev' into dev 2024-01-05 17:39:24 +03:00
Methodius
941652ec57 NFC App: Generate MF Classic with custom UID added 2024-01-05 22:10:28 +09:00
あく
7eeb60e17e Desktop: fix rpc unlock on pin input screen (#3334) 2024-01-03 17:00:56 +04:00
MX
c8ce5e6ed4 replace icon 2024-01-02 14:10:19 +03:00
MX
1e89197349 Merge branch 'nfc-parsers' into dev 2024-01-02 13:57:17 +03:00
MX
b57f4ce848 Merge branch 'dev' into nfc-parsers 2024-01-02 13:57:06 +03:00
MX
48e80adccc Merge branch 'ofw_dev' into dev 2024-01-02 13:53:42 +03:00
RebornedBrain
d511d76a1b [FL-3678] [FL-3733] [FL-3723] UI refactor (#3323)
* Added new image DolphinSaved_113x58.png for all "saved" pages
* New image DolphinDone_80x58.png added
* Replaced dolphins on all scenes accroding to new UI specs
* New success dolphin image added
* Success scene image replaced
* Changed image and text for update initial scene
* Image and text adjusted for "Original restored" scene
* Removed old DolphinNice_96x59.png image
* New image for LFRFID scene
* Removed unused image
* New UI image added to assets
* Replaced warning dolphin on mf_classic write initial fail scene
* Removed old image
* Changed image on scenes to a new one
* New dolphin mafia image
* Replaced dolphin mafia image to a new one
* Removed DolphinMafia_115x62.png
* New check symbol on completed state for detect_reader
* Adjusted layout elements position
* Removed second switching to popup view in order to achieve control in support callbacks
In general now we show generic scene and after that in on_enter callback we can redefine it for particular protocol
* CardDetected event now also triggers on_event callback
* Now on AuthRequest we throw CardDetected custom event
* Added callback for read_on_event
* Now we show different screen while reading and unlocking
* Fixed missing asstes for some scenes
* Update DolphinMafia_119x62.png
* Adjusted all the scenes with DolphinMafia image
* Scenes with save image adjusted
* Removed unnecessary assets DolphinMafia_119x62.png and DolphinSaved_113x58.png
* All common dolphins moved to Dolphin folder
* Moved DolphinReadingSuccess_59x63.png to Dolphin folder
* Set proper led color for detect and read scenes
* Added new notification sequence for semi_success results
* Use new sequence for semi_success nfc reads
* Different events are now throwed depending on read result
* Added handling of incomplete event for ultralight cards
* Replaced image for iButton scene
* Updated API for f18
* Fixed issue with unlock retry sequence
* Fix after review
* Success notification replaced to semi success in case of incomplete mf classic reading
* New text for read scene
* New read result sound notification logic for mf classic cards

Co-authored-by: あく <alleteam@gmail.com>
Co-authored-by: gornekich <n.gorbadey@gmail.com>
2024-01-02 15:43:46 +09:00
Eczbek
0049b19994 Remove weird newline in applications/ReadMe.md
>:)
2024-01-01 21:56:58 -05:00
MX
56ad7ece69 add new update image for next builds
happy new year !
2023-12-30 03:11:55 +03:00
MX
b48103196f subghz revert previous "fix" and do proper fix
and thanks to Willy-JL !
2023-12-30 02:43:15 +03:00
Methodius
4d5e096437 parsers cleanup 2023-12-30 00:55:13 +09:00
MX
e5ceef3422 Merge branch 'ofw_dev' into dev 2023-12-29 16:57:23 +03:00
gornekich
a7b60bf2a6 MFC emulation fixes (#3324)
* mf classic listener: fix write block
* nfc: go to idle state instead of sleep
* lib nfc: fix documentation
2023-12-29 12:24:20 +09:00
あく
895694c624 Scripts: fix incorrect handling of storage stress test count option (#3321) 2023-12-28 19:15:07 +09:00
Arthur Braghetto
2a0a54a224 Add Samsung AC remotes DB93 and AR-EH04 (#3301)
Co-authored-by: あく <alleteam@gmail.com>
2023-12-28 14:35:01 +09:00
YaBa
35e74c07d1 Added NFC plugin; parser for WashCity cards. (#3319)
* Added NFC plugin; parser for WashCity cards.
* Fixed file to the correct path
* Added demo file Demo_WC_20E.nfc
* Fixed clang format
* fixed typo (balance_cents)
* Removed demo file as requested.

Co-authored-by: あく <alleteam@gmail.com>
2023-12-28 14:23:12 +09:00
ry4000
f6a38352e8 Update mf_classic_dict.nfc (#3314)
This Commit proposes the addition of the 33 Bandai Namco Passport / Sega Aime Card static access keys that has been available to Members of the Flipper Devices Discord Server; for convenience, they are listed in Sector Key A/B order.

Sector 0 Key B has two possible access keys;
019761AA8082 [current Bandai Namco Passports]
574343467632 [Sega Aime Cards / early-edition Bandai Namco Passports]

Co-authored-by: あく <alleteam@gmail.com>
2023-12-28 14:12:13 +09:00
Eric Betts
76ed466eb4 Nfc: HID MFC Plugin (#3312)
Co-authored-by: あく <alleteam@gmail.com>
2023-12-28 14:03:50 +09:00
YaBa
f7e0338a75 Added demo file 2023-12-27 00:48:30 +00:00
YaBa
ca62254ee7 Fixed clang format and typo in var balance_cents 2023-12-26 23:19:48 +00:00
YaBa
a6bfc27530 Added plugin to read WashCity card balance 2023-12-26 21:45:36 +00:00
MX
40cc78489a merge ofw dev 2023-12-27 00:13:37 +03:00
MX
5ef6adb9ea upd changelog 2023-12-26 23:29:24 +03:00
MMX
eef8c0ffa7 Merge pull request #677 from amec0e/dev
Updated infrared assets
2023-12-26 23:25:07 +03:00
MX
528d29b82d upd changelog and ac 2023-12-26 23:24:57 +03:00
MX
5c00a403f6 temp fix for subghz keyboard lock
actually - furi timer is broken :(((((
2023-12-26 22:49:38 +03:00
あく
c9e3f20314 [FL-3429] About: cn,tw,mx certification information (#3318) 2023-12-26 15:12:17 +09:00
あく
0084443ed7 [FL-3727] RPC: reverse input (#3304)
* RPC: reverse input
* Assets: sync protobuf
2023-12-26 14:09:10 +09:00
MX
b84f14386c subghz option to delete old signals on full memory 2023-12-24 03:08:24 +03:00
MX
111750c420 Merge branch 'ofw_dev' into dev 2023-12-22 22:14:20 +03:00
MX
2832160891 change log level 2023-12-22 22:13:28 +03:00
あく
531ba24e9a Rollback #3305 and #3297 fix various rendering issues (#3307)
* Revert "[FL-3729, FL-3730] Gui: fix string width calculation (#3305)"
* Revert "Added UTF-8 support to Flipper Zero canvas API (#3297)"
2023-12-22 11:08:46 +00:00
MX
4d56bb4e44 Revert "testing subghz dynamic limit based on freeheap + RPC"
This reverts commit 77f458fb6e.
2023-12-21 21:42:17 +03:00
MX
92bed4a081 Merge branch 'ofw_dev' into dev 2023-12-21 19:48:12 +03:00
MX
bcadbc6353 Revert "fix broken texts due to usage of utf8, add proper api"
This reverts commit 49b2a4da8a.
2023-12-21 19:45:45 +03:00
あく
1543170f4c [FL-3729, FL-3730] Gui: fix string width calculation (#3305) 2023-12-21 16:43:11 +00:00
amec0e
18194b6bb5 Update audio.ir
New additions
2023-12-21 15:41:32 +00:00
amec0e
cdede67f31 Update fans.ir
New additions
2023-12-21 15:41:08 +00:00
amec0e
c5a76af1dd Update projectors.ir
Updated last checked, no new additions
2023-12-21 15:40:44 +00:00
amec0e
1acbd84b7c Update tv.ir
New additions
2023-12-21 15:39:57 +00:00
MX
271ec6cf97 fix readme 2023-12-21 03:37:44 +03:00
MX
9b156fbdca Merge branch 'nfc-parsers' into dev 2023-12-21 03:36:44 +03:00
MX
de9765fa25 Merge branch 'dev' into nfc-parsers 2023-12-21 03:36:32 +03:00
MMX
abcb9b2f38 Merge pull request #672 from krolchonok/ir-show-number-brute
[IR] change percent on number
2023-12-21 03:35:54 +03:00
MX
eaae5da519 faac rcxt add manually (not tested) 2023-12-21 03:28:53 +03:00
MX
77f458fb6e testing subghz dynamic limit based on freeheap + RPC
by Willy-JL

5cd2d3eabe

e8d9325bec
2023-12-21 03:17:21 +03:00
MX
8fa21c49b2 better subghz history element removal
by Willy-JL

c40755f700
2023-12-21 03:16:16 +03:00
MX
49b2a4da8a fix broken texts due to usage of utf8, add proper api 2023-12-21 03:14:23 +03:00
MX
bc1fdabce4 its time to enable this one 2023-12-20 01:25:35 +03:00
MX
fc6e152fd1 Merge branch 'ofw_dev' into dev 2023-12-19 22:23:17 +03:00
Skorpionm
17b122990f USART Bridge: added support for software control of DE/RE pins (#3280)
* USART Bridge: added support for software control of DE/RE pins
* USART Bridge: fix syntax
* UsbUsartBridge: add TODO ticket
* UsbUsartBridge: add second TODO ticket
* GpioUartBridge: human readable configuration
* GpioUartBridge: rename `Soft DE/RE` to `DE/RE Pin`
* GpioUartBridge: push pull for DE/RE pin

Co-authored-by: あく <alleteam@gmail.com>
2023-12-19 14:13:37 +00:00
MX
78d1507f14 Merge branch 'ofw_dev' into dev 2023-12-19 16:27:23 +03:00
hedger
1e1d9fcb69 ufbt: changed toolchain environment invocation; updated .gitignore for app template (#3300) 2023-12-19 12:43:06 +00:00
gornekich
6f6074dc01 Keys Dict: fix PVS warnings (#3299)
* keys dict: fix PVS warnings
* nfc app: suppress PVS warning
2023-12-19 12:11:35 +00:00
MX
14c6abe4eb Merge branch 'ofw_dev' into dev 2023-12-19 14:01:10 +03:00
Evgeny Stepanischev
25d24f1e4c Added UTF-8 support to Flipper Zero canvas API (#3297)
* Added UTF-8 support to Flipper Zero canvas API
* Add unicode example

Co-authored-by: あく <alleteam@gmail.com>
2023-12-18 18:36:50 +00:00
MX
528d2a7ec3 Merge branch 'ofw_dev' into dev 2023-12-18 21:23:58 +03:00
Andrea Maugeri
7642d67cae NfcDict Refactoring (#3271)
* toolbox(keys_dict): generalize nfc_dict
* nfc: rework nfc app and tests
* toolbox(keys_dict): improve code readability
2023-12-18 14:30:56 +00:00
ushastoe
238187730c [IR] change percent on number
change percent on number in ir brute
2023-12-18 11:41:54 +03:00
Methodius
c53f5aa5b5 Zolotaya Korona transport card parser added 2023-12-17 04:58:07 +09:00
Methodius
ebe1a8f55f parsers cleanup for new api 2023-12-16 23:57:46 +09:00
MX
510f217cd5 Merge branch 'dev' into nfc-parsers 2023-12-16 02:33:09 +03:00
MX
364b334654 fix dict remove dupes 2023-12-16 02:33:00 +03:00
MX
afc2b14578 Merge branch 'dev' into nfc-parsers 2023-12-16 02:31:39 +03:00
MX
db57ff947b Merge branch 'ofw_dev' into dev 2023-12-16 02:16:46 +03:00
MarcelSchm
8c4716f170 added new TV Philips OLED 934/12 (#3293)
Co-authored-by: あく <alleteam@gmail.com>
2023-12-15 19:07:01 +00:00
Gustavo de León
11ac6240d2 Add AC's Carrier 42QG5A580SC and AUX YKR-H/006E (#3284)
* Add Carrier 42QG5A580SC
* Add AC Model: AUX YKR-H/006E
* Cleanup ac.ir
* Infrared: Move Carrier 42QHB12D8S to the back of universal remote
* Infreared: more universal ac cleanup

Co-authored-by: あく <alleteam@gmail.com>
2023-12-15 18:57:08 +00:00
gornekich
d6680d1f75 [FL-3719] NFC Plugins loading rework (#3295)
* nfc app: rework supported cards
* nfc app: supported cards optimization
* nfc app: remove old nfc support implementation
* nfc app: add documentation for supported cards
* nfc app: format sources
* nfc app: fix PVS warnings
* nfc app: one more PVS fix
* nfc app: PVS please stop
* nfc app: add missing documentation

Co-authored-by: あく <alleteam@gmail.com>
2023-12-15 18:09:52 +00:00
gornekich
09540929c3 [FL-3717] MFC emulation fix (#3291)
* mf classic listener: reset state before sleep and after nack
* Fix PVS warnings
* Fix PVS and compiler disagree on builtins

Co-authored-by: あく <alleteam@gmail.com>
2023-12-15 17:51:20 +00:00
Leptopt1los
36aecfbec9 nfc_util: little endian bytes2num functions added (#3287)
* nfc_util_bytes2num_little_endian function added
* f18 target api version sync
* Bump api version

Co-authored-by: hedger <hedger@users.noreply.github.com>
Co-authored-by: あく <alleteam@gmail.com>
2023-12-15 17:05:16 +00:00
Leptopt1los
0af74fb755 Umarsh transport cards parser (#3277)
* Umarsh transport card parser added
* Volna transport cards keys added

Co-authored-by: あく <alleteam@gmail.com>
2023-12-14 14:02:40 +00:00
Augusto Zanellato
78b7310057 Add MyKey parser (#3262)
Co-authored-by: あく <alleteam@gmail.com>
2023-12-14 13:54:58 +00:00
あく
ccbb3a3498 [FL-3715] Update CLI MOTD (#3292)
* Add Carrier 42QG5A580SC

* Cli: update motd

---------

Co-authored-by: Gustavo de León <alfonso.gus.deleon@gmail.com>
2023-12-14 15:14:21 +04:00
MX
2d860b4a22 fix key display newline 2023-12-14 01:14:08 +03:00
MMX
09a66e2285 Merge pull request #668 from noproto/patch-1
Fix edge case -- 0.5% of UIDs got wrong result
2023-12-14 01:06:03 +03:00
MMX
fb17718a5d Merge pull request #667 from sudoAlphaX/patch-1
Fix typo
2023-12-14 01:05:45 +03:00
gornekich
73145e0beb mf classic listener: reset state before sleep and after nack 2023-12-14 00:35:21 +03:00
MX
55b4f87cd7 Merge branch 'ofw_dev' into dev 2023-12-13 20:24:48 +03:00
Nathan N
3dde112676 Fix edge case -- 0.5% of UIDs got wrong result 2023-12-12 11:17:33 -05:00
gornekich
155e4e9fa4 [FL-3706], [FL-3674] NFC NTAG and ISO14443-3b reading fix (#3285)
* mf ultralight poller: reset field after reading tearing flags
* iso14443-3b poller: change cid comparison in ATTRIB cmd

Co-authored-by: あく <alleteam@gmail.com>
2023-12-12 15:24:06 +00:00
あく
90cb1c4f2e FuriHal: RTC register reset API. New factory reset routine that wipes all RTC backup registers content. (#3288) 2023-12-12 19:12:57 +04:00
あく
e2e3663524 FuriHal: various GPIO improvements (#3260)
* FuriHal: add alternative gpio pulls that covers special cases, extra checks and crash on inalid parameters.
* FuriHal: optimize pwr_XX selection in gpio_init
* FuriHal: cleanup gpio param validation crash routine
2023-12-12 14:39:14 +00:00
Skorpionm
7b85e04462 [FL-3681] SubGhz: changed the name of the button when sending RAW to SubGHz (#3275) 2023-12-12 13:59:22 +00:00
Alpha
96412202ba Fix typo
Change Deafult to Default in line 170
2023-12-12 17:04:08 +05:30
MX
0b356c2d6d update changelog 2023-12-11 01:36:54 +03:00
MX
ee9092c8e5 Merge branch 'nfc-parsers' into dev 2023-12-08 00:18:23 +03:00
Methodius
2e7bb26eef Merge branch 'dev' into nfc-parsers 2023-12-07 22:22:29 +09:00
Methodius
5d28939c28 social_moscow parser verification collisions fix 2023-12-07 21:59:27 +09:00
MX
15a29e1483 format 2023-12-07 08:21:32 +03:00
MX
c22b3b57bd Merge branch 'nfc-parsers' into dev 2023-12-07 08:03:43 +03:00
MX
1daa2fa377 Merge branch 'ofw_dev' into dev 2023-12-07 08:03:18 +03:00
Methodius
1b45b8a17d Kazan parser: adult social tariff added 2023-12-06 18:25:47 +09:00
Methodius
e94beff204 Kazan parser: basic tariffs parsing added 2023-12-06 18:08:20 +09:00
gornekich
82baf1e923 [FL-3701] NFC fixes (#3264)
* nfc app: fix unlock with manual password crash
* nfc app: preserve card detected state
* nfc app: fix mf keys scene switch
* nfc app: fix multiple protocol tag detect notification
* nfc plugin: fix retrun in function body in aime parser
* iso14443-3b poller: rework ATTRIB response check
* nfc app: fix navigation after file load failur
* iso14443-3b poller: fix PVS warning
* mfc listener: add crutch in mfc emulation
2023-12-05 22:40:06 +09:00
MX
c416041379 Fix secplus v1 key display issue 2023-12-05 03:40:12 +03:00
MX
10444b943e Fix keeloq decode logic 2023-12-05 03:34:31 +03:00
MMX
2308a54ada Merge pull request #662 from noproto/dev
Add Saflok and MyKey KDFs
2023-12-05 03:33:37 +03:00
MMX
0ed6738a5b Merge pull request #660 from hnlcory/dev
Add keys to mf_classic_dict.nfc
2023-12-05 03:33:03 +03:00
noproto
dc25bfb831 Add Saflok and MyKey KDFs 2023-12-04 16:03:32 -05:00
MX
d6fcb04aa8 Merge branch 'ofw_dev' into dev 2023-12-03 18:42:11 +03:00
MX
9bf8f1015d fix keeloq null pointer if unknown 2023-12-03 18:41:28 +03:00
Honghao Zeng
c477d1321a nfc: m1k-based Aime (non-AIC) card support (#3241)
Co-authored-by: gornekich <n.gorbadey@gmail.com>
2023-12-03 20:00:46 +09:00
MX
ec99b70b38 upd changelog 2023-12-02 23:55:05 +03:00
MX
06a58ebd53 Merge branch 'ofw_dev' into dev [ci skip] 2023-12-02 23:19:43 +03:00
Skorpionm
eb6fe0a4db SubGhz: fix count bit for detect gate_tx protocol (#3253)
Co-authored-by: あく <alleteam@gmail.com>
2023-12-02 16:34:02 +09:00
pborsutzki
c6a14e1a67 Fixed a zero allocation error when reading an iso15693 nfc tag with no additional blocks. (#3229)
Co-authored-by: gornekich <n.gorbadey@gmail.com>
2023-12-02 16:27:58 +09:00
Astra
04cead1fc5 [FL-3620] Add the "remove pairing" button to BLE hid (#3237)
Co-authored-by: あく <alleteam@gmail.com>
2023-12-02 14:03:10 +09:00
Astra
93732865ac [FL-3132] HID app: Add new function key icons (#3236)
* Add new function key icons
* Fix graphical glitches on the buttons

Co-authored-by: あく <alleteam@gmail.com>
2023-12-02 13:52:04 +09:00
RebornedBrain
6a5d63803a [FL-3675] Ntag21x write (#3246)
* New scenes for ultralight poller write mode
* Added new button and transition logic for write operation
   For now write is only possible for NTAG21x cards with default password and no AUTHLIM set
* Poller states extended
* Enums and datatypes extended for new poller mode
* Added mode field to poller instance datatype
* New states for poller added in order to implement write mode
* Added new event type for locked cards in order to simplify state flow
* New logic for poller write commands
* Scenes adjustments
* Scenes renamed
* New field added to poller instance
* Now we write in 'page per call' mode
* Now function takes callback return value into account
* Callback will be called only in write mode
* Event type added
* Log adjusted and start page to write set
* Logs added and check in now false at start, then it moves to true
* Now mf_ultralight_poller_handler_request_write_data halts card in case of check failure and stops poller
* All fail events now returns NfcCommandStop callback
* In case of fail we move back properly
* Remove garbage

Co-authored-by: gornekich <n.gorbadey@gmail.com>
Co-authored-by: あく <alleteam@gmail.com>
2023-12-02 13:45:47 +09:00
MX
159aef022b Fix keeloq decoding order 2023-12-01 22:51:21 +03:00
MX
f7c63c675b upd changelog 2023-12-01 20:38:01 +03:00
MX
99d657fcfb merge fixes 2023-12-01 20:33:14 +03:00
MX
00ceb2cd5d Merge branch 'ofw_dev' into dev 2023-12-01 20:29:16 +03:00
Augusto Zanellato
b51a754fd9 Mifare Classic nested auth support (#3238)
Co-authored-by: Aleksandr Kutuzov <alleteam@gmail.com>
2023-12-01 22:25:53 +09:00
Augusto Zanellato
c1e0d02afc ST25TB poller refining + write support (#3239)
* nfc: st25tb: rework async poller
* nfc: st25tb: introduce sync poller
* nfc: st25tb: add write support
* nfc: st25tb: rewrite poller to use better states
* nfc: st25tb: move to mode request state after success
* nfc: st25tb: minor bug fixes
* type wasn't properly set on ready event
* sending NfcCustomEventPollerFailure on St25tbPollerEventTypeFailure caused poller to being freed and ultimately resulted in a thread crash

Co-authored-by: Aleksandr Kutuzov <alleteam@gmail.com>
2023-12-01 18:42:00 +09:00
hedger
890c9e87ce [FL-3690] Libraries cleanup; u2f crypto rework to use mbedtls (#3234)
* examples: plugins: utilize fal_embedded
* libs: removed fnv1a_hash
* furi: added FURI_PACKED; apps, libs: changed to use FURI_PACKED
* lib: mbedtls: using custom config
* lib: toolbox: removed md5, switched to mbedtls
* targets: f18: link fix
* lib: added mbedtls_cfg.h
* apps: nfc: explicit dependency on libmbedtls
* u2f: reworking to mbedtls
* u2f: replaced sha256 & hmac with mbedtls
* u2f: functional rework using mbedtls
* libs: dropped micro-ecc
* u2f: dropped old implementation
* toolbox: removed sha256 impl
* mcheck() for mbedtls
* libs: removed libmisc; split into smaller libs
* apps: debug: fixed display_test
* apps: include cleanups
* fbt: fixed VERSIONCOMSTR
* furi: added FURI_CHECK_RETURN
* lib: removed qrcode
* cleanup
* fbt: lint_py+format_py: fixed excessive command length
* api: Removed bzero from f7
* api: Removed bzero from f18
* Bump API Symbols

Co-authored-by: Aleksandr Kutuzov <alleteam@gmail.com>
2023-12-01 18:16:48 +09:00
MX
961dd297dd upd changelog 2023-12-01 04:53:11 +03:00
MX
d675563271 sync anims 2023-12-01 04:47:54 +03:00
MX
e027d5c3e8 Add new API function for varitemlist
by Willy-JL

466e1f989f
2023-12-01 04:47:17 +03:00
MX
fdcfd5996b Merge branch 'ofw_dev' into dev 2023-12-01 02:33:41 +03:00
Astra
a849d49c92 [FL-3682] Add the secret door animation (#3233)
Co-authored-by: あく <alleteam@gmail.com>
2023-11-30 18:51:38 +09:00
Evgeny Stepanischev
ff129e524a Allows you to use UCS-2 in canvas_glyph_width (#3226)
* allows you to use UCS-2 in canvas_glyph_width
* Sync API Symbols

Co-authored-by: あく <alleteam@gmail.com>
2023-11-30 18:38:48 +09:00
Cory Parker
05479103a6 Update mf_classic_dict.nfc
Added in new mf classic keys
2023-11-29 21:41:10 -10:00
MX
ff41d262dc add 303.9 frequency 2023-11-29 16:18:20 +03:00
MX
8628a2c6a2 update changelog, release planned for next week 2023-11-29 03:26:05 +03:00
MX
30914678c9 ibutton FIX crash!!!
why??????
2023-11-29 00:20:15 +03:00
MX
c4bf1fe717 use char escaping 2023-11-28 04:39:46 +03:00
MX
1946aaf5e1 upd discord notify 2023-11-28 04:21:43 +03:00
MX
a7b2427007 Merge branch 'ofw_dev' into dev 2023-11-26 19:58:48 +03:00
Astra
f9101d8084 [FL-3686] Mifare Classic fixes (#3221)
* Update Mifare Classic generators to create more accuate data
* Check the transfer buffer validity for NACK
* Fix the AC issues
* CRC errors don't really affect emulation, checking for them isn't worth it
* Make ATQA logic a bit easier to understand
* mf classic: change log level
* mf classic: fix log level

Co-authored-by: gornekich <n.gorbadey@gmail.com>
Co-authored-by: あく <alleteam@gmail.com>
2023-11-26 17:20:49 +09:00
RebornedBrain
1c3cbec661 [FL-3640] NFC: Felica UID emulation (#3190)
* Added basic template of Felica listener
* Raw nfc felica listener functions
* Added functions to setup chip for felica listener
* Cleanup function templates from unnecessary parts
* Removed todo comment
* Updated api versions
* Adjusted chip config for felica
* Set proper chip passive target mode for felica
* Added felica function to unit tests
* Update furi_hal_nfc_felica.c
* Removed duplication

Co-authored-by: gornekich <n.gorbadey@gmail.com>
2023-11-26 17:10:33 +09:00
MX
cea423742b Merge remote-tracking branch 'origin/nfc-parsers' into dev 2023-11-24 02:56:39 +03:00
Methodius
8c5f28d6a0 Kazan parser: Type: abonnement fix 2023-11-23 23:59:06 +09:00
Methodius
6c2e332638 Kazan transport cards parser added 2023-11-23 22:19:26 +09:00
Methodius
5f18532a59 Umarsh code cleanup 2023-11-23 22:11:12 +09:00
Methodius
63f072a819 Umarsh header check rework reworked. Thanks J for idea! 2023-11-22 17:26:10 +09:00
Methodius
df1d6503c0 Umarsh header check rework 2023-11-22 16:15:53 +09:00
MMX
972054b377 Merge pull request #655 from DarkFlippers/nfc-parsers
Nfc parsers
2023-11-22 01:50:54 +03:00
MX
e1c89834df no verbose 2023-11-22 01:48:22 +03:00
assasinfil
c145cad653 Verify card bugfix 2023-11-21 22:34:29 +03:00
Methodius
4261063c99 Metromoney transport card parser added 2023-11-22 00:39:23 +09:00
MX
f8546937c0 fix readme and feature name 2023-11-21 15:59:38 +03:00
assasinfil
3daaea6ecf Added new social card 2023-11-21 15:03:46 +03:00
assasinfil
4a84fbc6e3 UI printing bugfix 2023-11-21 13:12:24 +03:00
assasinfil
d8800e9fe3 Merge branch 'nfc-parsers' of https://github.com/DarkFlippers/unleashed-firmware into nfc-parsers 2023-11-21 11:54:11 +03:00
assasinfil
7fd921227c Fixes 2023-11-21 11:54:06 +03:00
Methodius
b6ad07b47c Umarsh parser: kopecks support added 2023-11-21 13:18:18 +09:00
Methodius
1a21f0e3c9 Umarsh transport cards parser added 2023-11-21 12:21:22 +09:00
MX
c666368446 add simple nfc file name display
temp fix for UI
2023-11-21 01:53:20 +03:00
MX
91d614dd25 add subghz honeywell protocol
by htotoo

https://github.com/Flipper-XFW/Xtreme-Firmware/commits?author=htotoo
2023-11-21 01:00:15 +03:00
MMX
baca59927b Merge pull request #653 from DarkFlippers/nfc-parsers
Nfc parsers
2023-11-20 23:25:52 +03:00
MX
4eb40ce948 fix ibutton info display
by @krolchonok
+ added missing furi_string_free
a378db6663/applications/main/ibutton/scenes/ibutton_scene_info.c
2023-11-20 23:25:29 +03:00
assasinfil
00b1018e9e Fixed validating 2023-11-20 20:46:51 +03:00
assasinfil
9c92338ddf Added social card parser 2023-11-20 19:11:30 +03:00
assasinfil
8188b63522 cleanup 2023-11-20 18:43:23 +03:00
assasinfil
0e172b67eb Layout 1c4 fix 2023-11-20 18:22:17 +03:00
assasinfil
156948ec58 All code added to parser 2023-11-20 17:42:19 +03:00
Methodius
bbdda5a3d7 trying to fix previous fix of timestamp_to_datetime() 2023-11-20 22:19:29 +09:00
Methodius
51a2e638ed trying to fix timestamp_to_datetime() 2023-11-20 22:14:49 +09:00
assasinfil
ffd9d3c218 Added msk transport parser 2023-11-20 14:10:50 +03:00
MX
9513ff5307 fix rgb patch 2023-11-17 02:43:47 +03:00
MX
12e736b283 fix ci, temp workaround for manifest 2023-11-17 02:35:27 +03:00
MX
a64c9534e2 Merge branch 'ofw_dev' into dev 2023-11-17 02:20:27 +03:00
Sergey Gavrilov
4b3e8aba29 [FL-3664] 64k does not enough (#3216)
* Unit tests: add "exists" to furi_record tests
* Unit tests: mu_warn, storage 64k test
* Storage: read/write over 64k
* Unit tests: moar tests for storage r/w for >64k cases
* Apps, libs: replace uint16_t with size_t on storage r/w operations
* Unit tests: better data pattern, subghz: warning if transmission is prohibited

Co-authored-by: あく <alleteam@gmail.com>
2023-11-16 01:39:27 +09:00
hedger
98d5718ec9 fbt: improvements (#3217)
* fbt: changed cdefines & lib handling for external apps; added extra checks for app manifest fields; moved around AppsC generator
* fbt: commandline fixes for spaces in paths
* fbt: fixed stringification for FAP_VERSION
* fbt: Removed excessive quoting for gdb
* docs: update for cdefines; fbt: typo fix
* fbt: enforcing at least 2 components in app version=

Co-authored-by: あく <alleteam@gmail.com>
2023-11-16 01:27:35 +09:00
あく
457aa5331f Various Fixes for 0.95 (#3215)
* FuriHal: retry gauge/charger initialization
* FuriHal: lower logging level for flash known errata
* FuriHal: graceful fail if subghz chip is not working
* Furi: issue stop command even if timer is not active, document timer behavior
2023-11-15 20:11:05 +04:00
Flipper Zelebro
a61b5d4b4c Add Mastercode SubGHz Protocol (#3187)
* Add Mastercode SubGHz Protocol
* Add 2 valid raw files and cleanup code
* Add tests to the two Raw Files
* Remove extra test & delete comments
* Fixes pulse length and shows correct Key

Co-authored-by: FlipperZelebro <flipperzelebro [at] gmail.com>
Co-authored-by: あく <alleteam@gmail.com>
2023-11-15 18:04:45 +09:00
Georgii Surkov
ba074068b0 [FL-3662] Do not remove file when renaming to itself (#3193)
* Do not allow overwriting a file with dir and support renaming file to itself
* Fix operator precedence error
* Add support for storage-specific path equivalence checks
* Fix typo
* Fix updater compilation
* Update Doxygen comments in storage.h

Co-authored-by: Aleksandr Kutuzov <alleteam@gmail.com>
2023-11-15 17:56:13 +09:00
Georgii Surkov
615a147973 [FL-3608] Fix iButton crash on missing file (#3210)
Co-authored-by: あく <alleteam@gmail.com>
2023-11-15 17:45:41 +09:00
gornekich
c00776ca22 [FL-3666] NFC API improvements (#3214)
* drivers: expose st25r3916 driver API
* nfc poller: add start with custom callback
* mf classic: rework sync API with poller custom start
* mf ultralight: rework sync API with poller custom start
* iso14443_3a poller: remove unused col res state
* nfc: rework nfc poller custom start
* mf ultralight: rename sync API
* mf classic: rename sync API
* iso14443-3a: rename sync API
* nfc: remove async prefix in internal functions
* nfc: expose internal API
* nfc: fix sync api include and docs
* targets: fix f18 build
* nfc: rework NfcGenericEventEx type
* nfc poller: add documentation
* iso14443-3a poller: add documentation
* felica poller: add documentation
* iso14443_3b poller: add documentation
* so14443_4a poller: add documentation
* iso14443_4b poller: add documentation
* iso15693 poller: add documentation
* slix poller: add documentation
* mf desfire poller: add documentation
* mf ultralight poller: fix API and add documentation
* mf classic poller: add documentation

Co-authored-by: あく <alleteam@gmail.com>
2023-11-15 17:32:45 +09:00
RebornedBrain
d0b9a3a4ae [NFC] MF Ultralight no pwd polling adjustment (#3207)
* Listener log level changed to Trace
* Show pages count without pwd pages in case of no auth success
* Fixed unit tests

Co-authored-by: gornekich <n.gorbadey@gmail.com>
Co-authored-by: あく <alleteam@gmail.com>
2023-11-15 17:02:35 +09:00
Tobias Jost
dc246ddb09 Fix limited_credit_value having wrong value in mf_desfire_file_settings_parse (#3204)
Co-authored-by: hedger <hedger@users.noreply.github.com>
Co-authored-by: あく <alleteam@gmail.com>
2023-11-15 16:39:29 +09:00
MMX
591a2f2b02 Merge pull request #649 from krolchonok/ibutton_fix
Fix naming in iButton
2023-11-13 20:01:09 +03:00
MX
7aaa847835 Merge branch 'nfcrefactoring' into dev 2023-11-13 20:57:36 +04:00
ushastoe
42101c6594 Revert " applications_user/Flipper-Zero-Privet-Mir-main/"
This reverts commit 3e9ecd2f4f.
2023-11-13 11:27:27 +03:00
ushastoe
3e9ecd2f4f applications_user/Flipper-Zero-Privet-Mir-main/ 2023-11-13 11:26:36 +03:00
ushastoe
725811d7b3 fix [] 2023-11-10 23:29:11 +03:00
ushastoe
a56a52b4bb Fix naming in iButton 2023-11-10 21:15:33 +03:00
558 changed files with 19298 additions and 12932 deletions

View File

@@ -175,3 +175,10 @@ Max butthurt: 12
Min level: 2 Min level: 2
Max level: 3 Max level: 3
Weight: 4 Weight: 4
Name: L2_Secret_door_128x64
Min butthurt: 0
Max butthurt: 12
Min level: 2
Max level: 3
Weight: 4

View File

@@ -462,10 +462,10 @@ index 0000000..68dacda
+ */ + */
+const char* rgb_backlight_get_color_text(uint8_t index); +const char* rgb_backlight_get_color_text(uint8_t index);
\ No newline at end of file \ No newline at end of file
diff --git a/firmware/targets/f7/furi_hal/furi_hal_light.c b/firmware/targets/f7/furi_hal/furi_hal_light.c diff --git a/targets/f7/furi_hal/furi_hal_light.c b/targets/f7/furi_hal/furi_hal_light.c
index 83e1603..45798ca 100644 index 83e1603..45798ca 100644
--- a/firmware/targets/f7/furi_hal/furi_hal_light.c --- a/targets/f7/furi_hal/furi_hal_light.c
+++ b/firmware/targets/f7/furi_hal/furi_hal_light.c +++ b/targets/f7/furi_hal/furi_hal_light.c
@@ -3,6 +3,7 @@ @@ -3,6 +3,7 @@
#include <furi_hal_light.h> #include <furi_hal_light.h>
#include <lp5562.h> #include <lp5562.h>

View File

@@ -45,7 +45,9 @@ steps:
- export FORCE_NO_DIRTY=yes - export FORCE_NO_DIRTY=yes
- export FBT_GIT_SUBMODULE_SHALLOW=1 - export FBT_GIT_SUBMODULE_SHALLOW=1
- wget https://github.com/xMasterX/all-the-plugins/releases/latest/download/all-the-apps-base.tgz - wget https://github.com/xMasterX/all-the-plugins/releases/latest/download/all-the-apps-base.tgz
- tar zxvf all-the-apps-base.tgz - tar zxf all-the-apps-base.tgz
- mkdir -p applications/main/clock_app/resources/apps
- mkdir -p applications/main/clock_app/resources/apps_data
- cp -R base_pack_build/artifacts-base/* applications/main/clock_app/resources/apps/ - cp -R base_pack_build/artifacts-base/* applications/main/clock_app/resources/apps/
- cp -R base_pack_build/apps_data/* applications/main/clock_app/resources/apps_data/ - cp -R base_pack_build/apps_data/* applications/main/clock_app/resources/apps_data/
- rm -rf base_pack_build - rm -rf base_pack_build
@@ -65,7 +67,8 @@ steps:
pull: never pull: never
commands: commands:
- wget https://github.com/xMasterX/all-the-plugins/releases/latest/download/all-the-apps-extra.tgz - wget https://github.com/xMasterX/all-the-plugins/releases/latest/download/all-the-apps-extra.tgz
- tar zxvf all-the-apps-extra.tgz - tar zxf all-the-apps-extra.tgz
- mkdir -p applications/main/clock_app/resources/apps
- cp -R extra_pack_build/artifacts-extra/* applications/main/clock_app/resources/apps/ - cp -R extra_pack_build/artifacts-extra/* applications/main/clock_app/resources/apps/
- rm -rf extra_pack_build - rm -rf extra_pack_build
- export DIST_SUFFIX=${DRONE_TAG}e - export DIST_SUFFIX=${DRONE_TAG}e
@@ -117,7 +120,9 @@ steps:
- rm -f build/f7-firmware-C/toolbox/version.* - rm -f build/f7-firmware-C/toolbox/version.*
- ./fbt COMPACT=1 DEBUG=0 updater_package - ./fbt COMPACT=1 DEBUG=0 updater_package
- wget https://github.com/xMasterX/all-the-plugins/releases/latest/download/all-the-apps-base.tgz - wget https://github.com/xMasterX/all-the-plugins/releases/latest/download/all-the-apps-base.tgz
- tar zxvf all-the-apps-base.tgz - tar zxf all-the-apps-base.tgz
- mkdir -p applications/main/clock_app/resources/apps
- mkdir -p applications/main/clock_app/resources/apps_data
- cp -R base_pack_build/artifacts-base/* applications/main/clock_app/resources/apps/ - cp -R base_pack_build/artifacts-base/* applications/main/clock_app/resources/apps/
- cp -R base_pack_build/apps_data/* applications/main/clock_app/resources/apps_data/ - cp -R base_pack_build/apps_data/* applications/main/clock_app/resources/apps_data/
- rm -rf base_pack_build - rm -rf base_pack_build
@@ -336,9 +341,10 @@ steps:
DISCORD_WEBHOOK: DISCORD_WEBHOOK:
from_secret: dis_release_webhook from_secret: dis_release_webhook
commands: commands:
- wget "https://raw.githubusercontent.com/fieu/discord.sh/e1dc1a7595efad2cad8f072f0b3531c470f5b7c8/discord.sh" - wget "https://raw.githubusercontent.com/fieu/discord.sh/2253303efc0e7211ac2777d2535054cbb872f1e0/discord.sh"
- chmod +x ./discord.sh - chmod +x ./discord.sh
- ./discord.sh --text 'New Unleashed firmware released!\n\nVersion - '${DRONE_TAG}'\n\n[-> Sponsor our project](https://boosty.to/mmxdev)\n\n[[Github - Changelog]](https://github.com/DarkFlippers/unleashed-firmware/releases/tag/'${DRONE_TAG}')\n\n[-How to install firmware-](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/documentation/HowToInstall.md)\n\n[-Download latest extra apps pack-](https://github.com/xMasterX/all-the-plugins/releases/latest)\n\n[-Install FW via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw/'${DRONE_TAG}'/flipper-z-f7-update-'${DRONE_TAG}'.tgz&channel=release-cfw&version='${DRONE_TAG}')\n\n[-Version with only main apps - Install FW via Web Updater-](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)\n\n[-Version without custom animations - Install FW via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw_no_anim/flipper-z-f7-update-'${DRONE_TAG}'n.tgz&channel=release-cfw&version='${DRONE_TAG}'n)\n\n[-Version with RGB patch - only for hardware mod! - Install FW via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-'${DRONE_TAG}'r.tgz&channel=release-cfw&version='${DRONE_TAG}'r)\n\n[-Version with RGB patch - only for hardware mod! - Direct download-](https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-'${DRONE_TAG}'r.tgz)\n\n[-Version with Extra apps - Install FW via Web Updater-](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)' - echo 'New Unleashed firmware released!\n\nVersion - '${DRONE_TAG}'\n\n[-> Sponsor our project](https://boosty.to/mmxdev)\n\n[[Github - Changelog]](https://github.com/DarkFlippers/unleashed-firmware/releases/tag/'${DRONE_TAG}')\n\n[-How to install firmware-](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/documentation/HowToInstall.md)\n\n[-Download latest extra apps pack-](https://github.com/xMasterX/all-the-plugins/releases/latest)\n\n[-Install FW via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw/'${DRONE_TAG}'/flipper-z-f7-update-'${DRONE_TAG}'.tgz&channel=release-cfw&version='${DRONE_TAG}')\n\n[-Version with only main apps - Install FW via Web Updater-](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)\n\n[-Version without custom animations - Install FW via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw_no_anim/flipper-z-f7-update-'${DRONE_TAG}'n.tgz&channel=release-cfw&version='${DRONE_TAG}'n)\n\n[-Version with RGB patch - only for hardware mod! - Install FW via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-'${DRONE_TAG}'r.tgz&channel=release-cfw&version='${DRONE_TAG}'r)\n\n[-Version with RGB patch - only for hardware mod! - Direct download-](https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-'${DRONE_TAG}'r.tgz)\n\n[-Version with Extra apps - Install FW via Web Updater-](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)' > messagedisc.txt
- ./discord.sh --text "$(jq -Rs . <messagedisc.txt | cut -c 2- | rev | cut -c 2- | rev)"
- name: "Send clean build to telegram" - name: "Send clean build to telegram"
image: appleboy/drone-telegram image: appleboy/drone-telegram
@@ -420,7 +426,9 @@ steps:
- export FORCE_NO_DIRTY=yes - export FORCE_NO_DIRTY=yes
- export FBT_GIT_SUBMODULE_SHALLOW=1 - export FBT_GIT_SUBMODULE_SHALLOW=1
- wget https://github.com/xMasterX/all-the-plugins/releases/latest/download/all-the-apps-base.tgz - wget https://github.com/xMasterX/all-the-plugins/releases/latest/download/all-the-apps-base.tgz
- tar zxvf all-the-apps-base.tgz - tar zxf all-the-apps-base.tgz
- mkdir -p applications/main/clock_app/resources/apps
- mkdir -p applications/main/clock_app/resources/apps_data
- cp -R base_pack_build/artifacts-base/* applications/main/clock_app/resources/apps/ - cp -R base_pack_build/artifacts-base/* applications/main/clock_app/resources/apps/
- cp -R base_pack_build/apps_data/* applications/main/clock_app/resources/apps_data/ - cp -R base_pack_build/apps_data/* applications/main/clock_app/resources/apps_data/
- rm -rf base_pack_build - rm -rf base_pack_build
@@ -440,7 +448,8 @@ steps:
pull: never pull: never
commands: commands:
- wget https://github.com/xMasterX/all-the-plugins/releases/latest/download/all-the-apps-extra.tgz - wget https://github.com/xMasterX/all-the-plugins/releases/latest/download/all-the-apps-extra.tgz
- tar zxvf all-the-apps-extra.tgz - tar zxf all-the-apps-extra.tgz
- mkdir -p applications/main/clock_app/resources/apps
- cp -R extra_pack_build/artifacts-extra/* applications/main/clock_app/resources/apps/ - cp -R extra_pack_build/artifacts-extra/* applications/main/clock_app/resources/apps/
- rm -rf extra_pack_build - rm -rf extra_pack_build
- export DIST_SUFFIX=${DRONE_BUILD_NUMBER}e - export DIST_SUFFIX=${DRONE_BUILD_NUMBER}e
@@ -657,9 +666,10 @@ steps:
DISCORD_WEBHOOK: DISCORD_WEBHOOK:
from_secret: dis_dev_webhook from_secret: dis_dev_webhook
commands: commands:
- wget "https://raw.githubusercontent.com/fieu/discord.sh/e1dc1a7595efad2cad8f072f0b3531c470f5b7c8/discord.sh" - wget "https://raw.githubusercontent.com/fieu/discord.sh/2253303efc0e7211ac2777d2535054cbb872f1e0/discord.sh"
- chmod +x ./discord.sh - chmod +x ./discord.sh
- ./discord.sh --text 'Unleashed firmware dev build successful!\n\nBuild - '${DRONE_BUILD_NUMBER}'\n\nCommit - https://github.com/DarkFlippers/unleashed-firmware/commit/'${DRONE_COMMIT_SHA}'\n\n[-> Sponsor our project](https://boosty.to/mmxdev)\n\n[-Version with Extra apps - Install via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-'${DRONE_BUILD_NUMBER}'e.tgz&channel=dev-cfw&version='${DRONE_BUILD_NUMBER}'e)\n\n[-Version with only main apps - Install via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-'${DRONE_BUILD_NUMBER}'c.tgz&channel=dev-cfw&version='${DRONE_BUILD_NUMBER}'c)\n\n[-Version with RGB patch - only for hardware mod! - Install via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-'${DRONE_BUILD_NUMBER}'r.tgz&channel=dev-cfw&version='${DRONE_BUILD_NUMBER}'r)\n\n[-Version with RGB patch - only for hardware mod! - Direct download-](https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-'${DRONE_BUILD_NUMBER}'r.tgz)\n\n[-Install via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw/dev/flipper-z-f7-update-'${DRONE_BUILD_NUMBER}'.tgz&channel=dev-cfw&version='${DRONE_BUILD_NUMBER}')' - echo 'Unleashed firmware dev build successful!\n\nBuild - '${DRONE_BUILD_NUMBER}'\n\nCommit - https://github.com/DarkFlippers/unleashed-firmware/commit/'${DRONE_COMMIT_SHA}'\n\n[-> Sponsor our project](https://boosty.to/mmxdev)\n\n[-Version with Extra apps - Install via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-'${DRONE_BUILD_NUMBER}'e.tgz&channel=dev-cfw&version='${DRONE_BUILD_NUMBER}'e)\n\n[-Version with only main apps - Install via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-'${DRONE_BUILD_NUMBER}'c.tgz&channel=dev-cfw&version='${DRONE_BUILD_NUMBER}'c)\n\n[-Version with RGB patch - only for hardware mod! - Install via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-'${DRONE_BUILD_NUMBER}'r.tgz&channel=dev-cfw&version='${DRONE_BUILD_NUMBER}'r)\n\n[-Version with RGB patch - only for hardware mod! - Direct download-](https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-'${DRONE_BUILD_NUMBER}'r.tgz)\n\n[-Install via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw/dev/flipper-z-f7-update-'${DRONE_BUILD_NUMBER}'.tgz&channel=dev-cfw&version='${DRONE_BUILD_NUMBER}')' > messagedisc.txt
- ./discord.sh --text "$(jq -Rs . <messagedisc.txt | cut -c 2- | rev | cut -c 2- | rev)"
trigger: trigger:
branch: branch:

View File

@@ -1 +1 @@
--ignore-ccache -C gccarm --rules-config .pvsconfig -e lib/cmsis_core -e lib/fatfs -e lib/fnv1a-hash -e lib/FreeRTOS-Kernel -e lib/heatshrink -e lib/libusb_stm32 -e lib/littlefs -e lib/mbedtls -e lib/micro-ecc -e lib/microtar -e lib/mlib -e lib/qrcode -e lib/stm32wb_cmsis -e lib/stm32wb_copro -e lib/stm32wb_hal -e lib/u8g2 -e lib/nanopb -e */arm-none-eabi/* --ignore-ccache -C gccarm --rules-config .pvsconfig -e lib/cmsis_core -e lib/fatfs -e lib/fnv1a-hash -e lib/FreeRTOS-Kernel -e lib/heatshrink -e lib/libusb_stm32 -e lib/littlefs -e lib/mbedtls -e lib/microtar -e lib/mlib -e lib/stm32wb_cmsis -e lib/stm32wb_copro -e lib/stm32wb_hal -e lib/u8g2 -e lib/nanopb -e */arm-none-eabi/*

View File

@@ -1,13 +1,21 @@
## New changes ## New changes
* SubGHz: Add 4 more systems to Add Manually (untested!) * LF RFID: Write T5577 with random password added (clear password via Extra actions) (by @Leptopt1los)
* SubGHz: Add Manually fixes * NFC: Add NFC NDEF parser (by @Willy-JL)
* SubGHz: Added NiceFlor-S to ignore options, removed colons. (by @G2Dolphin | PR #620) * Apps: **Check out Apps updates by following** [this link](https://github.com/xMasterX/all-the-plugins/commits/dev)
* Misc code cleanup * OFW: assets: checking limits on image size; ufbt: cdb target
* RGB: Fix white color on reboot, move settings, add custom color option * OFW: NFC: system dict skip when user dict is skipped fix (replaces our fix)
* **BLE Spam app** updated to latest version (Android, Windows support) (by @Willy-JL) -> (app can be found in builds ` `, `e`, `n`, `r`) * OFW: FuriHal: fix start duration furi_hal_subghz_async_tx
* OFW: Fix double arrows and add proper indication * OFW: NFC: parsers minor cleanup
* OFW: SubGHz: add manually fix 12-bits is 0xFFF (or 0xFF0) CAME/NICE 12-bit * OFW: NFC Ntag success write freeze when not saved card
* OFW: Fix various crashes if debug libraries used * OFW: ufbt: fixed generated project paths on Windows
<br><br>
#### Known NFC post-refactor regressions list:
- Mifare Mini clones reading is broken (original mini working fine) (OFW)
- EMV simple data parser was removed with protocol with refactoring (OFW)
- Option to unlock Slix-L (NFC V) with preset or custom password was removed with refactoring (OFW)
- NFC CLI was removed with refactoring (OFW)
- Current list of affected apps: https://github.com/xMasterX/all-the-plugins/tree/dev/apps_broken_by_last_refactors
- Also in app **Enhanced Sub-GHz Chat** - NFC part was temporarily removed to make app usable, NFC part of the app requires remaking it with new nfc stack
---- ----
@@ -31,8 +39,8 @@
|XMR|(Monero)| `41xUz92suUu1u5Mu4qkrcs52gtfpu9rnZRdBpCJ244KRHf6xXSvVFevdf2cnjS7RAeYr5hn9MsEfxKoFDRSctFjG5fv1Mhn`| |XMR|(Monero)| `41xUz92suUu1u5Mu4qkrcs52gtfpu9rnZRdBpCJ244KRHf6xXSvVFevdf2cnjS7RAeYr5hn9MsEfxKoFDRSctFjG5fv1Mhn`|
|TON||`EQCOqcnYkvzOZUV_9bPE_8oTbOrOF03MnF-VcJyjisTZmpGf`| |TON||`EQCOqcnYkvzOZUV_9bPE_8oTbOrOF03MnF-VcJyjisTZmpGf`|
#### Thanks to our sponsors: #### Thanks to our sponsors who supported project in the past and special thanks to sponsors who supports us on regular basis:
callmezimbra, Quen0n, MERRON, grvpvl (lvpvrg), art_col, ThurstonWaffles, Moneron, UterGrooll, LUCFER, Northpirate, zloepuzo, T.Rat, Alexey B., ionelife, ... ClaraCrazy, Pathfinder [Count Zero cDc], callmezimbra, Quen0n, MERRON, grvpvl (lvpvrg), art_col, ThurstonWaffles, Moneron, UterGrooll, LUCFER, Northpirate, zloepuzo, T.Rat, Alexey B., ionelife, ...
and all other great people who supported our project and me (xMasterX), thanks to you all! and all other great people who supported our project and me (xMasterX), thanks to you all!
@@ -50,7 +58,7 @@ What build I should download and what this name means - `flipper-z-f7-update-(ve
| `c` | ✅ | | | | | `c` | ✅ | | | |
| `n` | | ✅ | | | | `n` | | ✅ | | |
| `e` | ✅ | ✅ | ✅ | | | `e` | ✅ | ✅ | ✅ | |
| `r` | ✅ | ✅ | ✅ | | | `r` | ✅ | ✅ | ✅ | ⚠️ |
This is [hardware mod](https://github.com/quen0n/flipperzero-firmware-rgb#readme), works only on modded flippers! do not install on non modded device! This is [hardware mod](https://github.com/quen0n/flipperzero-firmware-rgb#readme), works only on modded flippers! do not install on non modded device!

View File

@@ -55,7 +55,7 @@
- New frequency analyzer [(by ClusterM)](https://github.com/DarkFlippers/unleashed-firmware/pull/43) - New frequency analyzer [(by ClusterM)](https://github.com/DarkFlippers/unleashed-firmware/pull/43)
- Press OK in frequency analyzer to use detected frequency in Read modes [(by derskythe)](https://github.com/DarkFlippers/unleashed-firmware/pull/77) - Press OK in frequency analyzer to use detected frequency in Read modes [(by derskythe)](https://github.com/DarkFlippers/unleashed-firmware/pull/77)
- Long press OK button in Sub-GHz Frequency analyzer to switch to Read menu [(by derskythe)](https://github.com/DarkFlippers/unleashed-firmware/pull/79) - Long press OK button in Sub-GHz Frequency analyzer to switch to Read menu [(by derskythe)](https://github.com/DarkFlippers/unleashed-firmware/pull/79)
- New option to use timestamps + protocol name when you saving file, instead of random name - Enable in `Radio Settings -> Time in names = ON` - New option to use timestamps + protocol name when you saving file, instead of random name or timestamp only - Enable in `Radio Settings -> Protocol Names = ON`
- Read mode UI improvements (shows time when signal was received) (by @wosk) - Read mode UI improvements (shows time when signal was received) (by @wosk)
- External CC1101 module support (Hardware SPI used) - External CC1101 module support (Hardware SPI used)
- **Hold right in received signal list to delete selected signal** - **Hold right in received signal list to delete selected signal**
@@ -79,7 +79,8 @@
- **NFC/RFID/iButton** - **NFC/RFID/iButton**
* LFRFID/iButton Fuzzer plugins * LFRFID/iButton Fuzzer plugins
* Extra Mifare Classic keys * Extra Mifare Classic keys
* `Add manually` -> Mifare Classic with custom UID * NFC `Add manually` -> Mifare Classic with custom UID
* NFC parsers: Umarsh, Zolotaya Korona, Kazan, Metromoney, Moscow Social Card, Troika (reworked) and [many others](https://github.com/DarkFlippers/unleashed-firmware/tree/dev/applications/main/nfc/plugins/supported_cards)
* Picopass/iClass plugin (now with emulation support!) included in releases * Picopass/iClass plugin (now with emulation support!) included in releases
- **Quality of life & other features** - **Quality of life & other features**
- Customizable Flipper name **Update! Now can be changed in Settings->Desktop** (by @xMasterX and @Willy-JL) - Customizable Flipper name **Update! Now can be changed in Settings->Desktop** (by @xMasterX and @Willy-JL)
@@ -100,25 +101,12 @@ Also check the [changelog in releases](https://github.com/DarkFlippers/unleashed
### Current modified and new Sub-GHz protocols list: ### Current modified and new Sub-GHz protocols list:
Thanks to Official team (to their SubGHz Developer, Skorp) for implementing decoders for these protocols in OFW. Thanks to Official team (to their SubGHz Developer, Skorp) for implementing decoders for these protocols in OFW.
Keeloq [Not ALL systems supported for decode or emulation yet!] - [Supported manufacturers list](https://0bin.net/paste/VwR2lNJY#WH9vnPgvcp7w6zVKucFCuNREKAcOij8KsJ6vqLfMn3b) Keeloq [Not ALL systems supported for decode or emulation!] - [Supported manufacturers list](https://pastes.io/raw/unuj9bhe4m)
Encoders or sending made by @xMasterX: Encoders or sending made by @xMasterX:
- Nero Radio 57bit (+ 56bit encoder improvements) - Nero Radio 57bit (+ 56bit encoder improvements)
- CAME 12bit/24bit encoder fixes (Fixes now merged in OFW) - CAME 12bit/24bit encoder fixes (Fixes now merged in OFW)
- Keeloq: HCS101 - Keeloq: 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 !)
- Keeloq: AN-Motors
- Keeloq: JCM Tech
- Keeloq: MHouse
- Keeloq: Nice Smilo
- Keeloq: DTM Neo
- Keeloq: FAAC RC,XT
- Keeloq: Mutancode
- Keeloq: Normstahl
- Keeloq: Beninca + Allmatic
- Keeloq: Stilmatic
- Keeloq: CAME Space
- Keeloq: Aprimatic (model TR and similar)
- Keeloq: Centurion Nova (thanks Carlos !)
Encoders or sending made by @Eng1n33r(first implementation in Q2 2022) & @xMasterX (current version): Encoders or sending made by @Eng1n33r(first implementation in Q2 2022) & @xMasterX (current version):
- CAME Atomo -> Update! check out new [instructions](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/documentation/SubGHzRemoteProg.md) - CAME Atomo -> Update! check out new [instructions](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/documentation/SubGHzRemoteProg.md)
@@ -140,9 +128,9 @@ The majority of this project is developed and maintained by me, @xMasterX.
I'm unemployed, and the only income I receive is from your donations. I'm unemployed, and the only income I receive is from your donations.
Our team is small and the guys are working on this project as much as they can solely based on the enthusiasm they have for this project and the community. Our team is small and the guys are working on this project as much as they can solely based on the enthusiasm they have for this project and the community.
- @gid9798 - SubGHz, Plugins, many other things - @gid9798 - SubGHz, Plugins, many other things
- @assasinfil - SubGHz protocols - @assasinfil - SubGHz protocols, NFC parsers (working with @Leptopt1los)
- @Svaarich - UI design and animations - @Svaarich - UI design and animations
- @amec0e & @Leptopt1los - Infrared assets - @amec0e & @Leptopt1los (only ACs) - Infrared assets
- Community moderators in Telegram, Discord, and Reddit - Community moderators in Telegram, Discord, and Reddit
- And of course our GitHub community. Your PRs are a very important part of this firmware and open-source development. - And of course our GitHub community. Your PRs are a very important part of this firmware and open-source development.
@@ -167,14 +155,14 @@ You can support us by using links or addresses below:
## Community apps included ## Community apps included
### [🎲 Download Extra plugins for Unleashed](https://github.com/xMasterX/all-the-plugins/releases/latest) ### [🎲 Download Extra plugins for Unleashed](https://github.com/xMasterX/all-the-plugins/releases/latest)
### [List of Extra pack](https://github.com/xMasterX/all-the-plugins/tree/dev#extra-pack) | [List of Base *(Deafult)* pack](https://github.com/xMasterX/all-the-plugins/tree/dev#default-pack) ### [List of Extra pack](https://github.com/xMasterX/all-the-plugins/tree/dev#extra-pack) | [List of Base *(Default)* pack](https://github.com/xMasterX/all-the-plugins/tree/dev#default-pack)
See full list and sources here: [xMasterX/all-the-plugins](https://github.com/xMasterX/all-the-plugins/tree/dev) See full list and sources here: [xMasterX/all-the-plugins](https://github.com/xMasterX/all-the-plugins/tree/dev)
### Official Flipper Zero Apps Catalog [web version](https://lab.flipper.net/apps) or mobile app ### Official Flipper Zero Apps Catalog [web version](https://lab.flipper.net/apps) or mobile app
# Instructions # Instructions
## First lock official docs [docs.flipper.net](https://docs.flipper.net/) ## First look at official docs [docs.flipper.net](https://docs.flipper.net/)
## [How to install](/documentation/HowToInstall.md) - [versions info](/CHANGELOG.md#recommended-update-option---web-updater): `n`,` `,`e`... ## [How to install](/documentation/HowToInstall.md) - [versions info](/CHANGELOG.md#recommended-update-option---web-updater): `n`,` `,`e`...
## Firmware & Development ## Firmware & Development

View File

@@ -172,6 +172,7 @@ Alias("fap_dist", fap_dist)
fap_deploy = distenv.PhonyTarget( fap_deploy = distenv.PhonyTarget(
"fap_deploy", "fap_deploy",
Action(
[ [
[ [
"${PYTHON3}", "${PYTHON3}",
@@ -182,7 +183,8 @@ fap_deploy = distenv.PhonyTarget(
"${SOURCE}", "${SOURCE}",
"/ext/apps", "/ext/apps",
] ]
], ]
),
source=firmware_env.Dir(("${RESOURCES_ROOT}/apps")), source=firmware_env.Dir(("${RESOURCES_ROOT}/apps")),
) )
Depends(fap_deploy, firmware_env["FW_RESOURCES_MANIFEST"]) Depends(fap_deploy, firmware_env["FW_RESOURCES_MANIFEST"])
@@ -276,23 +278,27 @@ distenv.PhonyTarget(
# Linter # Linter
distenv.PhonyTarget( distenv.PhonyTarget(
"lint", "lint",
"${PYTHON3} ${FBT_SCRIPT_DIR}/lint.py check ${LINT_SOURCES}", [["${PYTHON3}", "${FBT_SCRIPT_DIR}/lint.py", "check", "${LINT_SOURCES}"]],
LINT_SOURCES=[n.srcnode() for n in firmware_env["LINT_SOURCES"]], LINT_SOURCES=[n.srcnode() for n in firmware_env["LINT_SOURCES"]],
) )
distenv.PhonyTarget( distenv.PhonyTarget(
"format", "format",
"${PYTHON3} ${FBT_SCRIPT_DIR}/lint.py format ${LINT_SOURCES}", [["${PYTHON3}", "${FBT_SCRIPT_DIR}/lint.py", "format", "${LINT_SOURCES}"]],
LINT_SOURCES=[n.srcnode() for n in firmware_env["LINT_SOURCES"]], LINT_SOURCES=[n.srcnode() for n in firmware_env["LINT_SOURCES"]],
) )
# PY_LINT_SOURCES contains recursively-built modules' SConscript files + application manifests # PY_LINT_SOURCES contains recursively-built modules' SConscript files
# Here we add additional Python files residing in repo root # Here we add additional Python files residing in repo root
firmware_env.Append( firmware_env.Append(
PY_LINT_SOURCES=[ PY_LINT_SOURCES=[
# Py code folders # Py code folders
"site_scons", "site_scons",
"scripts", "scripts",
"applications",
"applications_user",
"assets",
"targets",
# Extra files # Extra files
"SConstruct", "SConstruct",
"firmware.scons", "firmware.scons",
@@ -302,7 +308,10 @@ firmware_env.Append(
black_commandline = "@${PYTHON3} -m black ${PY_BLACK_ARGS} ${PY_LINT_SOURCES}" black_commandline = "@${PYTHON3} -m black ${PY_BLACK_ARGS} ${PY_LINT_SOURCES}"
black_base_args = ["--include", '"\\.scons|\\.py|SConscript|SConstruct"'] black_base_args = [
"--include",
'"(\\.scons|\\.py|SConscript|SConstruct|\\.fam)$"',
]
distenv.PhonyTarget( distenv.PhonyTarget(
"lint_py", "lint_py",
@@ -323,10 +332,14 @@ distenv.PhonyTarget(
) )
# Start Flipper CLI via PySerial's miniterm # Start Flipper CLI via PySerial's miniterm
distenv.PhonyTarget("cli", "${PYTHON3} ${FBT_SCRIPT_DIR}/serial_cli.py -p ${FLIP_PORT}") distenv.PhonyTarget(
"cli", [["${PYTHON3}", "${FBT_SCRIPT_DIR}/serial_cli.py", "-p", "${FLIP_PORT}"]]
)
# Update WiFi devboard firmware # Update WiFi devboard firmware
distenv.PhonyTarget("devboard_flash", "${PYTHON3} ${FBT_SCRIPT_DIR}/wifi_board.py") distenv.PhonyTarget(
"devboard_flash", [["${PYTHON3}", "${FBT_SCRIPT_DIR}/wifi_board.py"]]
)
# Find blackmagic probe # Find blackmagic probe
@@ -356,10 +369,10 @@ vscode_dist = distenv.Install(
) )
distenv.Precious(vscode_dist) distenv.Precious(vscode_dist)
distenv.NoClean(vscode_dist) distenv.NoClean(vscode_dist)
distenv.Alias("vscode_dist", vscode_dist) distenv.Alias("vscode_dist", (vscode_dist, firmware_env["FW_CDB"]))
# Configure shell with build tools # Configure shell with build tools
distenv.PhonyTarget( distenv.PhonyTarget(
"env", "env",
"@echo $( ${FBT_SCRIPT_DIR}/toolchain/fbtenv.sh $)", "@echo $( ${FBT_SCRIPT_DIR.abspath}/toolchain/fbtenv.sh $)",
) )

View File

@@ -40,7 +40,6 @@ Applications for main Flipper menu.
Background services providing system APIs to applications. Background services providing system APIs to applications.
- `applications.h` - Firmware application list header - `applications.h` - Firmware application list header
- `bt` - BLE service and application - `bt` - BLE service and application
- `cli` - Console service and API - `cli` - Console service and API
- `crypto` - Crypto cli tools - `crypto` - Crypto cli tools

View File

@@ -4,7 +4,6 @@ App(
apptype=FlipperAppType.DEBUG, apptype=FlipperAppType.DEBUG,
targets=["f7"], targets=["f7"],
entry_point="accessor_app", entry_point="accessor_app",
cdefines=["APP_ACCESSOR"],
requires=["gui"], requires=["gui"],
stack_size=4 * 1024, stack_size=4 * 1024,
order=40, order=40,

View File

@@ -3,7 +3,6 @@ App(
name="Battery Test", name="Battery Test",
apptype=FlipperAppType.DEBUG, apptype=FlipperAppType.DEBUG,
entry_point="battery_test_app", entry_point="battery_test_app",
cdefines=["APP_BATTERY_TEST"],
requires=[ requires=[
"gui", "gui",
"power", "power",

View File

@@ -3,7 +3,6 @@ App(
name="Blink Test", name="Blink Test",
apptype=FlipperAppType.DEBUG, apptype=FlipperAppType.DEBUG,
entry_point="blink_test_app", entry_point="blink_test_app",
cdefines=["APP_BLINK"],
requires=["gui"], requires=["gui"],
stack_size=1 * 1024, stack_size=1 * 1024,
order=10, order=10,

View File

@@ -3,7 +3,6 @@ App(
name="CCID Debug", name="CCID Debug",
apptype=FlipperAppType.DEBUG, apptype=FlipperAppType.DEBUG,
entry_point="ccid_test_app", entry_point="ccid_test_app",
cdefines=["CCID_TEST"],
requires=[ requires=[
"gui", "gui",
], ],

View File

@@ -13,12 +13,12 @@ struct ISO7816_Command_APDU {
//body //body
uint8_t Lc; uint8_t Lc;
uint8_t Le; uint8_t Le;
} __attribute__((packed)); } FURI_PACKED;
struct ISO7816_Response_APDU { struct ISO7816_Response_APDU {
uint8_t SW1; uint8_t SW1;
uint8_t SW2; uint8_t SW2;
} __attribute__((packed)); } FURI_PACKED;
void iso7816_answer_to_reset(uint8_t* atrBuffer, uint32_t* atrlen); void iso7816_answer_to_reset(uint8_t* atrBuffer, uint32_t* atrlen);
void iso7816_read_command_apdu( void iso7816_read_command_apdu(

View File

@@ -3,7 +3,6 @@ App(
name="Crash Test", name="Crash Test",
apptype=FlipperAppType.DEBUG, apptype=FlipperAppType.DEBUG,
entry_point="crash_test_app", entry_point="crash_test_app",
cdefines=["APP_CRASH_TEST"],
requires=["gui"], requires=["gui"],
stack_size=1 * 1024, stack_size=1 * 1024,
fap_category="Debug", fap_category="Debug",

View File

@@ -3,9 +3,8 @@ App(
name="Display Test", name="Display Test",
apptype=FlipperAppType.DEBUG, apptype=FlipperAppType.DEBUG,
entry_point="display_test_app", entry_point="display_test_app",
cdefines=["APP_DISPLAY_TEST"],
requires=["gui"], requires=["gui"],
fap_libs=["misc"], fap_libs=["u8g2"],
stack_size=1 * 1024, stack_size=1 * 1024,
order=120, order=120,
fap_category="Debug", fap_category="Debug",

View File

@@ -3,7 +3,6 @@ App(
name="File Browser Test", name="File Browser Test",
apptype=FlipperAppType.DEBUG, apptype=FlipperAppType.DEBUG,
entry_point="file_browser_app", entry_point="file_browser_app",
cdefines=["APP_FILE_BROWSER_TEST"],
requires=["gui"], requires=["gui"],
stack_size=2 * 1024, stack_size=2 * 1024,
order=150, order=150,

View File

@@ -3,7 +3,6 @@ App(
name="Keypad Test", name="Keypad Test",
apptype=FlipperAppType.DEBUG, apptype=FlipperAppType.DEBUG,
entry_point="keypad_test_app", entry_point="keypad_test_app",
cdefines=["APP_KEYPAD_TEST"],
requires=["gui"], requires=["gui"],
stack_size=1 * 1024, stack_size=1 * 1024,
order=30, order=30,

View File

@@ -3,7 +3,6 @@ App(
name="Locale Test", name="Locale Test",
apptype=FlipperAppType.DEBUG, apptype=FlipperAppType.DEBUG,
entry_point="locale_test_app", entry_point="locale_test_app",
cdefines=["APP_LOCALE"],
requires=["gui", "locale"], requires=["gui", "locale"],
stack_size=2 * 1024, stack_size=2 * 1024,
order=70, order=70,

View File

@@ -3,7 +3,6 @@ App(
name="Text Box Test", name="Text Box Test",
apptype=FlipperAppType.DEBUG, apptype=FlipperAppType.DEBUG,
entry_point="text_box_test_app", entry_point="text_box_test_app",
cdefines=["APP_TEXT_BOX_TEST"],
requires=["gui"], requires=["gui"],
stack_size=1 * 1024, stack_size=1 * 1024,
order=140, order=140,

View File

@@ -3,7 +3,6 @@ App(
name="UART Echo", name="UART Echo",
apptype=FlipperAppType.DEBUG, apptype=FlipperAppType.DEBUG,
entry_point="uart_echo_app", entry_point="uart_echo_app",
cdefines=["APP_UART_ECHO"],
requires=["gui"], requires=["gui"],
stack_size=2 * 1024, stack_size=2 * 1024,
order=70, order=70,

View File

@@ -3,18 +3,29 @@
#include <furi.h> #include <furi.h>
#include "../minunit.h" #include "../minunit.h"
void test_furi_create_open() { #define TEST_RECORD_NAME "test/holding"
// 1. Create record
uint8_t test_data = 0;
furi_record_create("test/holding", (void*)&test_data);
// 2. Open it void test_furi_create_open() {
void* record = furi_record_open("test/holding"); // Test that record does not exist
mu_check(furi_record_exists(TEST_RECORD_NAME) == false);
// Create record
uint8_t test_data = 0;
furi_record_create(TEST_RECORD_NAME, (void*)&test_data);
// Test that record exists
mu_check(furi_record_exists(TEST_RECORD_NAME) == true);
// Open it
void* record = furi_record_open(TEST_RECORD_NAME);
mu_assert_pointers_eq(record, &test_data); mu_assert_pointers_eq(record, &test_data);
// 3. Close it // Close it
furi_record_close("test/holding"); furi_record_close(TEST_RECORD_NAME);
// 4. Clean up // Clean up
furi_record_destroy("test/holding"); furi_record_destroy(TEST_RECORD_NAME);
// Test that record does not exist
mu_check(furi_record_exists(TEST_RECORD_NAME) == false);
} }

View File

@@ -81,6 +81,7 @@ __attribute__((unused)) static void (*minunit_teardown)(void) = NULL;
void minunit_print_progress(void); void minunit_print_progress(void);
void minunit_print_fail(const char* error); void minunit_print_fail(const char* error);
void minunit_printf_warning(const char* format, ...);
/* Definitions */ /* Definitions */
#define MU_TEST(method_name) static void method_name(void) #define MU_TEST(method_name) static void method_name(void)
@@ -150,6 +151,10 @@ void minunit_print_fail(const char* error);
minunit_end_proc_timer - minunit_proc_timer);) minunit_end_proc_timer - minunit_proc_timer);)
#define MU_EXIT_CODE minunit_fail #define MU_EXIT_CODE minunit_fail
/* Warnings */
#define mu_warn(message) \
MU__SAFE_BLOCK(minunit_printf_warning("%s:%d: %s", __FILE__, __LINE__, message);)
/* Assertions */ /* Assertions */
#define mu_check(test) \ #define mu_check(test) \
MU__SAFE_BLOCK( \ MU__SAFE_BLOCK( \

View File

@@ -7,12 +7,12 @@
#include <nfc/nfc_poller.h> #include <nfc/nfc_poller.h>
#include <nfc/nfc_listener.h> #include <nfc/nfc_listener.h>
#include <nfc/protocols/iso14443_3a/iso14443_3a.h> #include <nfc/protocols/iso14443_3a/iso14443_3a.h>
#include <nfc/protocols/iso14443_3a/iso14443_3a_poller_sync_api.h> #include <nfc/protocols/iso14443_3a/iso14443_3a_poller_sync.h>
#include <nfc/protocols/mf_ultralight/mf_ultralight.h> #include <nfc/protocols/mf_ultralight/mf_ultralight.h>
#include <nfc/protocols/mf_ultralight/mf_ultralight_poller_sync_api.h> #include <nfc/protocols/mf_ultralight/mf_ultralight_poller_sync.h>
#include <nfc/protocols/mf_classic/mf_classic_poller_sync_api.h> #include <nfc/protocols/mf_classic/mf_classic_poller_sync.h>
#include <nfc/helpers/nfc_dict.h> #include <toolbox/keys_dict.h>
#include <nfc/nfc.h> #include <nfc/nfc.h>
#include "../minunit.h" #include "../minunit.h"
@@ -182,8 +182,8 @@ MU_TEST(iso14443_3a_reader) {
Iso14443_3aData iso14443_3a_poller_data = {}; Iso14443_3aData iso14443_3a_poller_data = {};
mu_assert( mu_assert(
iso14443_3a_poller_read(poller, &iso14443_3a_poller_data) == Iso14443_3aErrorNone, iso14443_3a_poller_sync_read(poller, &iso14443_3a_poller_data) == Iso14443_3aErrorNone,
"iso14443_3a_poller_read() failed"); "iso14443_3a_poller_sync_read() failed");
nfc_listener_stop(iso3_listener); nfc_listener_stop(iso3_listener);
mu_assert( mu_assert(
@@ -203,15 +203,26 @@ static void mf_ultralight_reader_test(const char* path) {
NfcDevice* nfc_device = nfc_device_alloc(); NfcDevice* nfc_device = nfc_device_alloc();
mu_assert(nfc_device_load(nfc_device, path), "nfc_device_load() failed\r\n"); mu_assert(nfc_device_load(nfc_device, path), "nfc_device_load() failed\r\n");
NfcListener* mfu_listener = nfc_listener_alloc( MfUltralightData* data =
listener, (MfUltralightData*)nfc_device_get_data(nfc_device, NfcProtocolMfUltralight);
NfcProtocolMfUltralight,
nfc_device_get_data(nfc_device, NfcProtocolMfUltralight)); uint32_t features = mf_ultralight_get_feature_support_set(data->type);
bool pwd_supported =
mf_ultralight_support_feature(features, MfUltralightFeatureSupportPasswordAuth);
uint8_t pwd_num = mf_ultralight_get_pwd_page_num(data->type);
const uint8_t zero_pwd[4] = {0, 0, 0, 0};
if(pwd_supported && !memcmp(data->page[pwd_num].data, zero_pwd, sizeof(zero_pwd))) {
data->pages_read -= 2;
}
NfcListener* mfu_listener = nfc_listener_alloc(listener, NfcProtocolMfUltralight, data);
nfc_listener_start(mfu_listener, NULL, NULL); nfc_listener_start(mfu_listener, NULL, NULL);
MfUltralightData* mfu_data = mf_ultralight_alloc(); MfUltralightData* mfu_data = mf_ultralight_alloc();
MfUltralightError error = mf_ultralight_poller_read_card(poller, mfu_data); MfUltralightError error = mf_ultralight_poller_sync_read_card(poller, mfu_data);
mu_assert(error == MfUltralightErrorNone, "mf_ultralight_poller_read_card() failed"); mu_assert(error == MfUltralightErrorNone, "mf_ultralight_poller_sync_read_card() failed");
nfc_listener_stop(mfu_listener); nfc_listener_stop(mfu_listener);
nfc_listener_free(mfu_listener); nfc_listener_free(mfu_listener);
@@ -259,8 +270,8 @@ MU_TEST(ntag_213_locked_reader) {
nfc_listener_start(mfu_listener, NULL, NULL); nfc_listener_start(mfu_listener, NULL, NULL);
MfUltralightData* mfu_data = mf_ultralight_alloc(); MfUltralightData* mfu_data = mf_ultralight_alloc();
MfUltralightError error = mf_ultralight_poller_read_card(poller, mfu_data); MfUltralightError error = mf_ultralight_poller_sync_read_card(poller, mfu_data);
mu_assert(error == MfUltralightErrorNone, "mf_ultralight_poller_read_card() failed"); mu_assert(error == MfUltralightErrorNone, "mf_ultralight_poller_sync_read_card() failed");
nfc_listener_stop(mfu_listener); nfc_listener_stop(mfu_listener);
nfc_listener_free(mfu_listener); nfc_listener_free(mfu_listener);
@@ -297,8 +308,8 @@ static void mf_ultralight_write() {
MfUltralightData* mfu_data = mf_ultralight_alloc(); MfUltralightData* mfu_data = mf_ultralight_alloc();
// Initial read // Initial read
MfUltralightError error = mf_ultralight_poller_read_card(poller, mfu_data); MfUltralightError error = mf_ultralight_poller_sync_read_card(poller, mfu_data);
mu_assert(error == MfUltralightErrorNone, "mf_ultralight_poller_read_card() failed"); mu_assert(error == MfUltralightErrorNone, "mf_ultralight_poller_sync_read_card() failed");
mu_assert( mu_assert(
mf_ultralight_is_equal(mfu_data, nfc_device_get_data(nfc_device, NfcProtocolMfUltralight)), mf_ultralight_is_equal(mfu_data, nfc_device_get_data(nfc_device, NfcProtocolMfUltralight)),
@@ -310,13 +321,13 @@ static void mf_ultralight_write() {
FURI_LOG_D(TAG, "Writing page %d", i); FURI_LOG_D(TAG, "Writing page %d", i);
furi_hal_random_fill_buf(page.data, sizeof(MfUltralightPage)); furi_hal_random_fill_buf(page.data, sizeof(MfUltralightPage));
mfu_data->page[i] = page; mfu_data->page[i] = page;
error = mf_ultralight_poller_write_page(poller, i, &page); error = mf_ultralight_poller_sync_write_page(poller, i, &page);
mu_assert(error == MfUltralightErrorNone, "mf_ultralight_poller_write_page() failed"); mu_assert(error == MfUltralightErrorNone, "mf_ultralight_poller_sync_write_page() failed");
} }
// Verification read // Verification read
error = mf_ultralight_poller_read_card(poller, mfu_data); error = mf_ultralight_poller_sync_read_card(poller, mfu_data);
mu_assert(error == MfUltralightErrorNone, "mf_ultralight_poller_read_card() failed"); mu_assert(error == MfUltralightErrorNone, "mf_ultralight_poller_sync_read_card() failed");
nfc_listener_stop(mfu_listener); nfc_listener_stop(mfu_listener);
const MfUltralightData* mfu_listener_data = const MfUltralightData* mfu_listener_data =
@@ -344,7 +355,7 @@ static void mf_classic_reader() {
MfClassicBlock block = {}; MfClassicBlock block = {};
MfClassicKey key = {.data = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}}; MfClassicKey key = {.data = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}};
mf_classic_poller_read_block(poller, 0, &key, MfClassicKeyTypeA, &block); mf_classic_poller_sync_read_block(poller, 0, &key, MfClassicKeyTypeA, &block);
nfc_listener_stop(mfc_listener); nfc_listener_stop(mfc_listener);
nfc_listener_free(mfc_listener); nfc_listener_free(mfc_listener);
@@ -372,8 +383,8 @@ static void mf_classic_write() {
furi_hal_random_fill_buf(block_write.data, sizeof(MfClassicBlock)); furi_hal_random_fill_buf(block_write.data, sizeof(MfClassicBlock));
MfClassicKey key = {.data = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}}; MfClassicKey key = {.data = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}};
mf_classic_poller_write_block(poller, 1, &key, MfClassicKeyTypeA, &block_write); mf_classic_poller_sync_write_block(poller, 1, &key, MfClassicKeyTypeA, &block_write);
mf_classic_poller_read_block(poller, 1, &key, MfClassicKeyTypeA, &block_read); mf_classic_poller_sync_read_block(poller, 1, &key, MfClassicKeyTypeA, &block_read);
nfc_listener_stop(mfc_listener); nfc_listener_stop(mfc_listener);
nfc_listener_free(mfc_listener); nfc_listener_free(mfc_listener);
@@ -402,16 +413,18 @@ static void mf_classic_value_block() {
mf_classic_value_to_block(value, 1, &block_write); mf_classic_value_to_block(value, 1, &block_write);
MfClassicError error = MfClassicErrorNone; MfClassicError error = MfClassicErrorNone;
error = mf_classic_poller_write_block(poller, 1, &key, MfClassicKeyTypeA, &block_write); error = mf_classic_poller_sync_write_block(poller, 1, &key, MfClassicKeyTypeA, &block_write);
mu_assert(error == MfClassicErrorNone, "Write failed"); mu_assert(error == MfClassicErrorNone, "Write failed");
int32_t data = 200; int32_t data = 200;
int32_t new_value = 0; int32_t new_value = 0;
error = mf_classic_poller_change_value(poller, 1, &key, MfClassicKeyTypeA, data, &new_value); error =
mf_classic_poller_sync_change_value(poller, 1, &key, MfClassicKeyTypeA, data, &new_value);
mu_assert(error == MfClassicErrorNone, "Value increment failed"); mu_assert(error == MfClassicErrorNone, "Value increment failed");
mu_assert(new_value == value + data, "Value not match"); mu_assert(new_value == value + data, "Value not match");
error = mf_classic_poller_change_value(poller, 1, &key, MfClassicKeyTypeA, -data, &new_value); error =
mf_classic_poller_sync_change_value(poller, 1, &key, MfClassicKeyTypeA, -data, &new_value);
mu_assert(error == MfClassicErrorNone, "Value decrement failed"); mu_assert(error == MfClassicErrorNone, "Value decrement failed");
mu_assert(new_value == value, "Value not match"); mu_assert(new_value == value, "Value not match");
@@ -430,36 +443,36 @@ MU_TEST(mf_classic_dict_test) {
"Remove test dict failed"); "Remove test dict failed");
} }
NfcDict* dict = nfc_dict_alloc( KeysDict* dict = keys_dict_alloc(
NFC_APP_MF_CLASSIC_DICT_UNIT_TEST_PATH, NfcDictModeOpenAlways, sizeof(MfClassicKey)); NFC_APP_MF_CLASSIC_DICT_UNIT_TEST_PATH, KeysDictModeOpenAlways, sizeof(MfClassicKey));
mu_assert(dict != NULL, "nfc_dict_alloc() failed"); mu_assert(dict != NULL, "keys_dict_alloc() failed");
size_t dict_keys_total = nfc_dict_get_total_keys(dict); size_t dict_keys_total = keys_dict_get_total_keys(dict);
mu_assert(dict_keys_total == 0, "nfc_dict_keys_total() failed"); mu_assert(dict_keys_total == 0, "keys_dict_keys_total() failed");
const uint32_t test_key_num = 30; const uint32_t test_key_num = 30;
MfClassicKey* key_arr_ref = malloc(test_key_num * sizeof(MfClassicKey)); MfClassicKey* key_arr_ref = malloc(test_key_num * sizeof(MfClassicKey));
for(size_t i = 0; i < test_key_num; i++) { for(size_t i = 0; i < test_key_num; i++) {
furi_hal_random_fill_buf(key_arr_ref[i].data, sizeof(MfClassicKey)); furi_hal_random_fill_buf(key_arr_ref[i].data, sizeof(MfClassicKey));
mu_assert( mu_assert(
nfc_dict_add_key(dict, key_arr_ref[i].data, sizeof(MfClassicKey)), "add key failed"); keys_dict_add_key(dict, key_arr_ref[i].data, sizeof(MfClassicKey)), "add key failed");
size_t dict_keys_total = nfc_dict_get_total_keys(dict); size_t dict_keys_total = keys_dict_get_total_keys(dict);
mu_assert(dict_keys_total == (i + 1), "nfc_dict_keys_total() failed"); mu_assert(dict_keys_total == (i + 1), "keys_dict_keys_total() failed");
} }
nfc_dict_free(dict); keys_dict_free(dict);
dict = nfc_dict_alloc( dict = keys_dict_alloc(
NFC_APP_MF_CLASSIC_DICT_UNIT_TEST_PATH, NfcDictModeOpenAlways, sizeof(MfClassicKey)); NFC_APP_MF_CLASSIC_DICT_UNIT_TEST_PATH, KeysDictModeOpenAlways, sizeof(MfClassicKey));
mu_assert(dict != NULL, "nfc_dict_alloc() failed"); mu_assert(dict != NULL, "keys_dict_alloc() failed");
dict_keys_total = nfc_dict_get_total_keys(dict); dict_keys_total = keys_dict_get_total_keys(dict);
mu_assert(dict_keys_total == test_key_num, "nfc_dict_keys_total() failed"); mu_assert(dict_keys_total == test_key_num, "keys_dict_keys_total() failed");
MfClassicKey key_dut = {}; MfClassicKey key_dut = {};
size_t key_idx = 0; size_t key_idx = 0;
while(nfc_dict_get_next_key(dict, key_dut.data, sizeof(MfClassicKey))) { while(keys_dict_get_next_key(dict, key_dut.data, sizeof(MfClassicKey))) {
mu_assert( mu_assert(
memcmp(key_arr_ref[key_idx].data, key_dut.data, sizeof(MfClassicKey)) == 0, memcmp(key_arr_ref[key_idx].data, key_dut.data, sizeof(MfClassicKey)) == 0,
"Loaded key data mismatch"); "Loaded key data mismatch");
@@ -471,19 +484,19 @@ MU_TEST(mf_classic_dict_test) {
for(size_t i = 0; i < COUNT_OF(delete_keys_idx); i++) { for(size_t i = 0; i < COUNT_OF(delete_keys_idx); i++) {
MfClassicKey* key = &key_arr_ref[delete_keys_idx[i]]; MfClassicKey* key = &key_arr_ref[delete_keys_idx[i]];
mu_assert( mu_assert(
nfc_dict_is_key_present(dict, key->data, sizeof(MfClassicKey)), keys_dict_is_key_present(dict, key->data, sizeof(MfClassicKey)),
"nfc_dict_is_key_present() failed"); "keys_dict_is_key_present() failed");
mu_assert( mu_assert(
nfc_dict_delete_key(dict, key->data, sizeof(MfClassicKey)), keys_dict_delete_key(dict, key->data, sizeof(MfClassicKey)),
"nfc_dict_delete_key() failed"); "keys_dict_delete_key() failed");
} }
dict_keys_total = nfc_dict_get_total_keys(dict); dict_keys_total = keys_dict_get_total_keys(dict);
mu_assert( mu_assert(
dict_keys_total == test_key_num - COUNT_OF(delete_keys_idx), dict_keys_total == test_key_num - COUNT_OF(delete_keys_idx),
"nfc_dict_keys_total() failed"); "keys_dict_keys_total() failed");
nfc_dict_free(dict); keys_dict_free(dict);
free(key_arr_ref); free(key_arr_ref);
mu_assert( mu_assert(

View File

@@ -455,4 +455,19 @@ NfcError nfc_iso15693_listener_tx_sof(Nfc* instance) {
return NfcErrorNone; return NfcErrorNone;
} }
NfcError nfc_felica_listener_set_sensf_res_data(
Nfc* instance,
const uint8_t* idm,
const uint8_t idm_len,
const uint8_t* pmm,
const uint8_t pmm_len) {
furi_assert(instance);
furi_assert(idm);
furi_assert(pmm);
furi_assert(idm_len == 8);
furi_assert(pmm_len == 8);
return NfcErrorNone;
}
#endif #endif

View File

@@ -0,0 +1,7 @@
Filetype: Flipper SubGhz Key File
Version: 1
Frequency: 433920000
Preset: FuriHalSubGhzPresetOok270Async
Protocol: Mastercode
Bit: 36
Key: 00 00 00 0B 7E 00 3C 08

View File

@@ -0,0 +1,6 @@
Filetype: Flipper SubGhz RAW File
Version: 1
Frequency: 433920000
Preset: FuriHalSubGhzPresetOok270Async
Protocol: RAW
RAW_Data: 10389 -66 405095 -102 207 -106 1165 -130 963739 -1232 899 -2250 2003 -1190 2017 -1202 911 -2256 2021 -1162 2045 -1134 2047 -1164 2047 -1138 2031 -1180 2039 -1182 949 -2190 995 -2214 961 -2228 963 -2198 963 -2214 977 -2212 975 -2210 975 -2208 971 -2200 963 -2210 993 -2184 2075 -1130 2051 -1142 2055 -1136 2047 -1178 965 -2236 933 -2220 975 -2184 999 -2222 967 -2208 969 -2214 979 -2202 2027 -1156 975 -2242 943 -16080 2023 -1162 967 -2220 2057 -1114 2061 -1124 1007 -2242 2025 -1134 2055 -1168 2017 -1138 2075 -1134 2053 -1136 2075 -1130 979 -2214 979 -2174 999 -2182 1001 -2204 977 -2206 1003 -2188 979 -2176 999 -2182 1009 -2176 1009 -2176 1001 -2212 2029 -1116 2091 -1102 2109 -1092 2095 -1126 1001 -2150 1011 -2180 1011 -2180 1009 -2178 1009 -2172 1009 -2166 1001 -2198 2065 -1136 975 -2220 971 -16018 2097 -1166 951 -2240 2009 -1186 2011 -1160 979 -2208 2035 -1134 2053 -1138 2061 -1158 2045 -1152 2029 -1152 2051 -1166 963 -2188 993 -2222 951 -2214 963 -2220 965 -2212 979 -2212 977 -2180 1003 -2202 965 -2218 975 -2216 967 -2188 2061 -1124 2083 -1126 2071 -1130 2059 -1134 993 -2188 979 -2240 947 -2204 979 -2214 971 -2214 973 -2210 971 -2206 2053 -1130 979 -2216 969 -16056 2053 -1134 1001 -2224 2021 -1150 2051 -1154 953 -2240 2045 -1146 2023 -1168 2033 -1144 2065 -1146 2055 -1130 2071 -1160 961 -2192 973 -2190 1005 -2214 975 -2206 967 -2206 975 -2206 967 -2208 975 -2212 967 -2212 979 -2218 977 -2178 2063 -1156 2035 -1160 2061 -1126 2065 -1130 981 -2186 1003 -2210 977 -2208 973 -2202 977 -2200 965 -2248 943 -2206 2039 -1190 941 -48536 65 -7254 263 -68 363 -102 131 -232 263 -264 751 -230 225 -822 397 -634 231 -268 263 -134 267 -64 867 -132 305 -138 67 -100 331 -98 891 -66 455 -66 531 -100 299 -134 897 -98 693 -132 291 -132 333 -98 337 -68 331

View File

@@ -1,27 +1,31 @@
#include "flipper.pb.h"
#include <core/check.h> #include <core/check.h>
#include <core/record.h> #include <core/record.h>
#include "pb_decode.h"
#include <rpc/rpc.h>
#include "rpc/rpc_i.h"
#include "storage.pb.h"
#include "storage/filesystem_api_defines.h"
#include "storage/storage.h"
#include <furi.h> #include <furi.h>
#include "../minunit.h"
#include <stdint.h> #include <stdint.h>
#include <pb.h>
#include <pb_encode.h>
#include <m-list.h>
#include <lib/toolbox/md5_calc.h>
#include <lib/toolbox/path.h>
#include <cli/cli.h>
#include <loader/loader.h>
#include <protobuf_version.h>
#include <FreeRTOS.h> #include <FreeRTOS.h>
#include <semphr.h> #include <semphr.h>
#include <rpc/rpc.h>
#include <rpc/rpc_i.h>
#include <cli/cli.h>
#include <storage/storage.h>
#include <loader/loader.h>
#include <storage/filesystem_api_defines.h>
#include <lib/toolbox/md5_calc.h>
#include <lib/toolbox/path.h>
#include <m-list.h>
#include "../minunit.h"
#include <protobuf_version.h>
#include <pb.h>
#include <pb_encode.h>
#include <pb_decode.h>
#include <storage.pb.h>
#include <flipper.pb.h>
LIST_DEF(MsgList, PB_Main, M_POD_OPLIST) LIST_DEF(MsgList, PB_Main, M_POD_OPLIST)
#define M_OPL_MsgList_t() LIST_OPLIST(MsgList) #define M_OPL_MsgList_t() LIST_OPLIST(MsgList)

View File

@@ -139,7 +139,7 @@ static bool write_file_13DA(Storage* storage, const char* path) {
File* file = storage_file_alloc(storage); File* file = storage_file_alloc(storage);
bool result = false; bool result = false;
if(storage_file_open(file, path, FSAM_WRITE, FSOM_CREATE_ALWAYS)) { if(storage_file_open(file, path, FSAM_WRITE, FSOM_CREATE_ALWAYS)) {
result = storage_file_write(file, "13DA", 4) == 4; result = (storage_file_write(file, "13DA", 4) == 4);
} }
storage_file_close(file); storage_file_close(file);
storage_file_free(file); storage_file_free(file);

View File

@@ -115,6 +115,66 @@ MU_TEST(storage_file_open_close) {
furi_record_close(RECORD_STORAGE); furi_record_close(RECORD_STORAGE);
} }
static bool storage_file_read_write_test(File* file, uint8_t* data, size_t test_size) {
const char* filename = UNIT_TESTS_PATH("storage_chunk.test");
// fill with pattern
for(size_t i = 0; i < test_size; i++) {
data[i] = (i % 113);
}
bool result = false;
do {
if(!storage_file_open(file, filename, FSAM_WRITE, FSOM_CREATE_ALWAYS)) break;
if(test_size != storage_file_write(file, data, test_size)) break;
storage_file_close(file);
// reset data
memset(data, 0, test_size);
if(!storage_file_open(file, filename, FSAM_READ, FSOM_OPEN_EXISTING)) break;
if(test_size != storage_file_read(file, data, test_size)) break;
storage_file_close(file);
// check that data is correct
for(size_t i = 0; i < test_size; i++) {
if(data[i] != (i % 113)) {
break;
}
}
result = true;
} while(false);
return result;
}
MU_TEST(storage_file_read_write_64k) {
Storage* storage = furi_record_open(RECORD_STORAGE);
File* file = storage_file_alloc(storage);
size_t size_1k = 1024;
size_t size_64k = size_1k + size_1k * 63;
size_t size_65k = size_64k + size_1k;
size_t size_max = size_65k + 8;
size_t max_ram_block = memmgr_heap_get_max_free_block();
if(max_ram_block < size_max) {
mu_warn("Not enough RAM for >64k block test");
} else {
uint8_t* data = malloc(size_max);
mu_check(storage_file_read_write_test(file, data, size_1k));
mu_check(storage_file_read_write_test(file, data, size_64k));
mu_check(storage_file_read_write_test(file, data, size_65k));
mu_check(storage_file_read_write_test(file, data, size_max));
free(data);
}
storage_file_free(file);
furi_record_close(RECORD_STORAGE);
}
MU_TEST_SUITE(storage_file) { MU_TEST_SUITE(storage_file) {
storage_file_open_lock_setup(); storage_file_open_lock_setup();
MU_RUN_TEST(storage_file_open_close); MU_RUN_TEST(storage_file_open_close);
@@ -122,6 +182,10 @@ MU_TEST_SUITE(storage_file) {
storage_file_open_lock_teardown(); storage_file_open_lock_teardown();
} }
MU_TEST_SUITE(storage_file_64k) {
MU_RUN_TEST(storage_file_read_write_64k);
}
MU_TEST(storage_dir_open_close) { MU_TEST(storage_dir_open_close) {
Storage* storage = furi_record_open(RECORD_STORAGE); Storage* storage = furi_record_open(RECORD_STORAGE);
File* file; File* file;
@@ -640,6 +704,7 @@ MU_TEST_SUITE(test_md5_calc_suite) {
int run_minunit_test_storage() { int run_minunit_test_storage() {
MU_RUN_SUITE(storage_file); MU_RUN_SUITE(storage_file);
MU_RUN_SUITE(storage_file_64k);
MU_RUN_SUITE(storage_dir); MU_RUN_SUITE(storage_dir);
MU_RUN_SUITE(storage_rename); MU_RUN_SUITE(storage_rename);
MU_RUN_SUITE(test_data_path); MU_RUN_SUITE(test_data_path);

View File

@@ -229,17 +229,17 @@ typedef struct {
size_t pos; size_t pos;
} SubGhzHalAsyncTxTest; } SubGhzHalAsyncTxTest;
#define SUBGHZ_HAL_TEST_DURATION 1 #define SUBGHZ_HAL_TEST_DURATION 3
static LevelDuration subghz_hal_async_tx_test_yield(void* context) { static LevelDuration subghz_hal_async_tx_test_yield(void* context) {
SubGhzHalAsyncTxTest* test = context; SubGhzHalAsyncTxTest* test = context;
bool is_odd = test->pos % 2; bool is_odd = test->pos % 2;
if(test->type == SubGhzHalAsyncTxTestTypeNormal) { if(test->type == SubGhzHalAsyncTxTestTypeNormal) {
if(test->pos < API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL * 8) { if(test->pos < FURI_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL * 8) {
test->pos++; test->pos++;
return level_duration_make(is_odd, SUBGHZ_HAL_TEST_DURATION); return level_duration_make(is_odd, SUBGHZ_HAL_TEST_DURATION);
} else if(test->pos == API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL * 8) { } else if(test->pos == FURI_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL * 8) {
test->pos++; test->pos++;
return level_duration_reset(); return level_duration_reset();
} else { } else {
@@ -249,36 +249,36 @@ static LevelDuration subghz_hal_async_tx_test_yield(void* context) {
if(test->pos == 0) { if(test->pos == 0) {
test->pos++; test->pos++;
return level_duration_make(!is_odd, SUBGHZ_HAL_TEST_DURATION); return level_duration_make(!is_odd, SUBGHZ_HAL_TEST_DURATION);
} else if(test->pos < API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL * 8) { } else if(test->pos < FURI_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL * 8) {
test->pos++; test->pos++;
return level_duration_make(is_odd, SUBGHZ_HAL_TEST_DURATION); return level_duration_make(is_odd, SUBGHZ_HAL_TEST_DURATION);
} else if(test->pos == API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL * 8) { } else if(test->pos == FURI_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL * 8) {
test->pos++; test->pos++;
return level_duration_reset(); return level_duration_reset();
} else { } else {
furi_crash("Yield after reset"); furi_crash("Yield after reset");
} }
} else if(test->type == SubGhzHalAsyncTxTestTypeInvalidMid) { } else if(test->type == SubGhzHalAsyncTxTestTypeInvalidMid) {
if(test->pos == API_HAL_SUBGHZ_ASYNC_TX_BUFFER_HALF / 2) { if(test->pos == FURI_HAL_SUBGHZ_ASYNC_TX_BUFFER_HALF / 2) {
test->pos++; test->pos++;
return level_duration_make(!is_odd, SUBGHZ_HAL_TEST_DURATION); return level_duration_make(!is_odd, SUBGHZ_HAL_TEST_DURATION);
} else if(test->pos < API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL * 8) { } else if(test->pos < FURI_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL * 8) {
test->pos++; test->pos++;
return level_duration_make(is_odd, SUBGHZ_HAL_TEST_DURATION); return level_duration_make(is_odd, SUBGHZ_HAL_TEST_DURATION);
} else if(test->pos == API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL * 8) { } else if(test->pos == FURI_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL * 8) {
test->pos++; test->pos++;
return level_duration_reset(); return level_duration_reset();
} else { } else {
furi_crash("Yield after reset"); furi_crash("Yield after reset");
} }
} else if(test->type == SubGhzHalAsyncTxTestTypeInvalidEnd) { } else if(test->type == SubGhzHalAsyncTxTestTypeInvalidEnd) {
if(test->pos == API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL - 1) { if(test->pos == FURI_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL - 1) {
test->pos++; test->pos++;
return level_duration_make(!is_odd, SUBGHZ_HAL_TEST_DURATION); return level_duration_make(!is_odd, SUBGHZ_HAL_TEST_DURATION);
} else if(test->pos < API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL * 8) { } else if(test->pos < FURI_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL * 8) {
test->pos++; test->pos++;
return level_duration_make(is_odd, SUBGHZ_HAL_TEST_DURATION); return level_duration_make(is_odd, SUBGHZ_HAL_TEST_DURATION);
} else if(test->pos == API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL * 8) { } else if(test->pos == FURI_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL * 8) {
test->pos++; test->pos++;
return level_duration_reset(); return level_duration_reset();
} else { } else {
@@ -292,20 +292,20 @@ static LevelDuration subghz_hal_async_tx_test_yield(void* context) {
furi_crash("Yield after reset"); furi_crash("Yield after reset");
} }
} else if(test->type == SubGhzHalAsyncTxTestTypeResetMid) { } else if(test->type == SubGhzHalAsyncTxTestTypeResetMid) {
if(test->pos < API_HAL_SUBGHZ_ASYNC_TX_BUFFER_HALF / 2) { if(test->pos < FURI_HAL_SUBGHZ_ASYNC_TX_BUFFER_HALF / 2) {
test->pos++; test->pos++;
return level_duration_make(is_odd, SUBGHZ_HAL_TEST_DURATION); return level_duration_make(is_odd, SUBGHZ_HAL_TEST_DURATION);
} else if(test->pos == API_HAL_SUBGHZ_ASYNC_TX_BUFFER_HALF / 2) { } else if(test->pos == FURI_HAL_SUBGHZ_ASYNC_TX_BUFFER_HALF / 2) {
test->pos++; test->pos++;
return level_duration_reset(); return level_duration_reset();
} else { } else {
furi_crash("Yield after reset"); furi_crash("Yield after reset");
} }
} else if(test->type == SubGhzHalAsyncTxTestTypeResetEnd) { } else if(test->type == SubGhzHalAsyncTxTestTypeResetEnd) {
if(test->pos < API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL - 1) { if(test->pos < FURI_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL) {
test->pos++; test->pos++;
return level_duration_make(is_odd, SUBGHZ_HAL_TEST_DURATION); return level_duration_make(is_odd, SUBGHZ_HAL_TEST_DURATION);
} else if(test->pos == API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL - 1) { } else if(test->pos == FURI_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL) {
test->pos++; test->pos++;
return level_duration_reset(); return level_duration_reset();
} else { } else {
@@ -324,6 +324,7 @@ bool subghz_hal_async_tx_test_run(SubGhzHalAsyncTxTestType type) {
furi_hal_subghz_set_frequency_and_path(433920000); furi_hal_subghz_set_frequency_and_path(433920000);
if(!furi_hal_subghz_start_async_tx(subghz_hal_async_tx_test_yield, &test)) { if(!furi_hal_subghz_start_async_tx(subghz_hal_async_tx_test_yield, &test)) {
mu_warn("SubGHZ transmission is prohibited");
return false; return false;
} }
@@ -331,6 +332,8 @@ bool subghz_hal_async_tx_test_run(SubGhzHalAsyncTxTestType type) {
while(!furi_hal_subghz_is_async_tx_complete()) { while(!furi_hal_subghz_is_async_tx_complete()) {
if(furi_hal_cortex_timer_is_expired(timer)) { if(furi_hal_cortex_timer_is_expired(timer)) {
furi_hal_subghz_stop_async_tx();
furi_hal_subghz_sleep();
return false; return false;
} }
furi_delay_ms(10); furi_delay_ms(10);
@@ -652,6 +655,13 @@ MU_TEST(subghz_decoder_kinggates_stylo4k_test) {
"Test decoder " SUBGHZ_PROTOCOL_KINGGATES_STYLO_4K_NAME " error\r\n"); "Test decoder " SUBGHZ_PROTOCOL_KINGGATES_STYLO_4K_NAME " error\r\n");
} }
MU_TEST(subghz_decoder_mastercode_test) {
mu_assert(
subghz_decoder_test(
EXT_PATH("unit_tests/subghz/mastercode_raw.sub"), SUBGHZ_PROTOCOL_MASTERCODE_NAME),
"Test decoder " SUBGHZ_PROTOCOL_MASTERCODE_NAME " error\r\n");
}
//test encoders //test encoders
MU_TEST(subghz_encoder_princeton_test) { MU_TEST(subghz_encoder_princeton_test) {
mu_assert( mu_assert(
@@ -803,6 +813,12 @@ MU_TEST(subghz_encoder_dooya_test) {
"Test encoder " SUBGHZ_PROTOCOL_DOOYA_NAME " error\r\n"); "Test encoder " SUBGHZ_PROTOCOL_DOOYA_NAME " error\r\n");
} }
MU_TEST(subghz_encoder_mastercode_test) {
mu_assert(
subghz_encoder_test(EXT_PATH("unit_tests/subghz/mastercode.sub")),
"Test encoder " SUBGHZ_PROTOCOL_MASTERCODE_NAME " error\r\n");
}
MU_TEST(subghz_random_test) { MU_TEST(subghz_random_test) {
mu_assert(subghz_decode_random_test(TEST_RANDOM_DIR_NAME), "Random test error\r\n"); mu_assert(subghz_decode_random_test(TEST_RANDOM_DIR_NAME), "Random test error\r\n");
} }
@@ -853,6 +869,7 @@ MU_TEST_SUITE(subghz) {
MU_RUN_TEST(subghz_decoder_alutech_at_4n_test); MU_RUN_TEST(subghz_decoder_alutech_at_4n_test);
MU_RUN_TEST(subghz_decoder_nice_one_test); MU_RUN_TEST(subghz_decoder_nice_one_test);
MU_RUN_TEST(subghz_decoder_kinggates_stylo4k_test); MU_RUN_TEST(subghz_decoder_kinggates_stylo4k_test);
MU_RUN_TEST(subghz_decoder_mastercode_test);
MU_RUN_TEST(subghz_encoder_princeton_test); MU_RUN_TEST(subghz_encoder_princeton_test);
MU_RUN_TEST(subghz_encoder_came_test); MU_RUN_TEST(subghz_encoder_came_test);
@@ -879,6 +896,7 @@ MU_TEST_SUITE(subghz) {
MU_RUN_TEST(subghz_encoder_smc5326_test); MU_RUN_TEST(subghz_encoder_smc5326_test);
MU_RUN_TEST(subghz_encoder_holtek_ht12x_test); MU_RUN_TEST(subghz_encoder_holtek_ht12x_test);
MU_RUN_TEST(subghz_encoder_dooya_test); MU_RUN_TEST(subghz_encoder_dooya_test);
MU_RUN_TEST(subghz_encoder_mastercode_test);
MU_RUN_TEST(subghz_random_test); MU_RUN_TEST(subghz_random_test);
subghz_test_deinit(); subghz_test_deinit();

View File

@@ -78,6 +78,16 @@ void minunit_print_fail(const char* str) {
printf(_FURI_LOG_CLR_E "%s\r\n" _FURI_LOG_CLR_RESET, str); printf(_FURI_LOG_CLR_E "%s\r\n" _FURI_LOG_CLR_RESET, str);
} }
void minunit_printf_warning(const char* format, ...) {
FuriString* str = furi_string_alloc();
va_list args;
va_start(args, format);
furi_string_vprintf(str, format, args);
va_end(args);
printf(_FURI_LOG_CLR_W "%s\r\n" _FURI_LOG_CLR_RESET, furi_string_get_cstr(str));
furi_string_free(str);
}
void unit_tests_cli(Cli* cli, FuriString* args, void* context) { void unit_tests_cli(Cli* cli, FuriString* args, void* context) {
UNUSED(cli); UNUSED(cli);
UNUSED(args); UNUSED(args);

View File

@@ -3,7 +3,6 @@ App(
name="USB Mouse", name="USB Mouse",
apptype=FlipperAppType.DEBUG, apptype=FlipperAppType.DEBUG,
entry_point="usb_mouse_app", entry_point="usb_mouse_app",
cdefines=["APP_USB_MOUSE"],
requires=["gui"], requires=["gui"],
stack_size=1 * 1024, stack_size=1 * 1024,
order=60, order=60,

View File

@@ -3,7 +3,6 @@ App(
name="USB Test", name="USB Test",
apptype=FlipperAppType.DEBUG, apptype=FlipperAppType.DEBUG,
entry_point="usb_test_app", entry_point="usb_test_app",
cdefines=["APP_USB_TEST"],
requires=["gui"], requires=["gui"],
stack_size=1 * 1024, stack_size=1 * 1024,
order=50, order=50,

View File

@@ -3,7 +3,6 @@ App(
name="Vibro Test", name="Vibro Test",
apptype=FlipperAppType.DEBUG, apptype=FlipperAppType.DEBUG,
entry_point="vibro_test_app", entry_point="vibro_test_app",
cdefines=["APP_VIBRO_TEST"],
requires=["gui"], requires=["gui"],
stack_size=1 * 1024, stack_size=1 * 1024,
order=20, order=20,

View File

@@ -17,18 +17,18 @@
#define TAG "SubGhzDeviceCc1101Ext" #define TAG "SubGhzDeviceCc1101Ext"
#define SUBGHZ_DEVICE_CC1101_EXT_TX_GPIO &gpio_ext_pb2 #define SUBGHZ_DEVICE_CC1101_EXT_TX_GPIO (&gpio_ext_pb2)
#define SUBGHZ_DEVICE_CC1101_EXT_E07_AMP_GPIO &gpio_ext_pc3 #define SUBGHZ_DEVICE_CC1101_EXT_E07_AMP_GPIO &gpio_ext_pc3
#define SUBGHZ_DEVICE_CC1101_EXT_FORCE_DANGEROUS_RANGE false #define SUBGHZ_DEVICE_CC1101_EXT_FORCE_DANGEROUS_RANGE false
#define SUBGHZ_DEVICE_CC1101_CONFIG_VER 1 #define SUBGHZ_DEVICE_CC1101_CONFIG_VER 1
/* DMA Channels definition */ /* DMA Channels definition */
#define SUBGHZ_DEVICE_CC1101_EXT_DMA DMA2 #define SUBGHZ_DEVICE_CC1101_EXT_DMA (DMA2)
#define SUBGHZ_DEVICE_CC1101_EXT_DMA_CH3_CHANNEL LL_DMA_CHANNEL_3 #define SUBGHZ_DEVICE_CC1101_EXT_DMA_CH3_CHANNEL (LL_DMA_CHANNEL_3)
#define SUBGHZ_DEVICE_CC1101_EXT_DMA_CH4_CHANNEL LL_DMA_CHANNEL_4 #define SUBGHZ_DEVICE_CC1101_EXT_DMA_CH4_CHANNEL (LL_DMA_CHANNEL_4)
#define SUBGHZ_DEVICE_CC1101_EXT_DMA_CH5_CHANNEL LL_DMA_CHANNEL_5 #define SUBGHZ_DEVICE_CC1101_EXT_DMA_CH5_CHANNEL (LL_DMA_CHANNEL_5)
#define SUBGHZ_DEVICE_CC1101_EXT_DMA_CH3_IRQ FuriHalInterruptIdDma2Ch3 #define SUBGHZ_DEVICE_CC1101_EXT_DMA_CH3_IRQ (FuriHalInterruptIdDma2Ch3)
#define SUBGHZ_DEVICE_CC1101_EXT_DMA_CH3_DEF \ #define SUBGHZ_DEVICE_CC1101_EXT_DMA_CH3_DEF \
SUBGHZ_DEVICE_CC1101_EXT_DMA, SUBGHZ_DEVICE_CC1101_EXT_DMA_CH3_CHANNEL SUBGHZ_DEVICE_CC1101_EXT_DMA, SUBGHZ_DEVICE_CC1101_EXT_DMA_CH3_CHANNEL
#define SUBGHZ_DEVICE_CC1101_EXT_DMA_CH4_DEF \ #define SUBGHZ_DEVICE_CC1101_EXT_DMA_CH4_DEF \
@@ -37,10 +37,10 @@
SUBGHZ_DEVICE_CC1101_EXT_DMA, SUBGHZ_DEVICE_CC1101_EXT_DMA_CH5_CHANNEL SUBGHZ_DEVICE_CC1101_EXT_DMA, SUBGHZ_DEVICE_CC1101_EXT_DMA_CH5_CHANNEL
/** Low level buffer dimensions and guard times */ /** Low level buffer dimensions and guard times */
#define SUBGHZ_DEVICE_CC1101_EXT_ASYNC_TX_BUFFER_FULL (256) #define SUBGHZ_DEVICE_CC1101_EXT_ASYNC_TX_BUFFER_FULL (256u)
#define SUBGHZ_DEVICE_CC1101_EXT_ASYNC_TX_BUFFER_HALF \ #define SUBGHZ_DEVICE_CC1101_EXT_ASYNC_TX_BUFFER_HALF \
(SUBGHZ_DEVICE_CC1101_EXT_ASYNC_TX_BUFFER_FULL / 2) (SUBGHZ_DEVICE_CC1101_EXT_ASYNC_TX_BUFFER_FULL / 2)
#define SUBGHZ_DEVICE_CC1101_EXT_ASYNC_TX_GUARD_TIME 999 << 1 #define SUBGHZ_DEVICE_CC1101_EXT_ASYNC_TX_GUARD_TIME (999u >> 1)
/** SubGhz state */ /** SubGhz state */
typedef enum { typedef enum {
@@ -58,13 +58,25 @@ typedef enum {
SubGhzDeviceCC1101ExtRegulationTxRx, /**TxRx*/ SubGhzDeviceCC1101ExtRegulationTxRx, /**TxRx*/
} SubGhzDeviceCC1101ExtRegulation; } SubGhzDeviceCC1101ExtRegulation;
typedef enum {
SubGhzDeviceCC1101ExtAsyncTxMiddlewareStateIdle,
SubGhzDeviceCC1101ExtAsyncTxMiddlewareStateReset,
SubGhzDeviceCC1101ExtAsyncTxMiddlewareStateRun,
} SubGhzDeviceCC1101ExtAsyncTxMiddlewareState;
typedef struct {
SubGhzDeviceCC1101ExtAsyncTxMiddlewareState state;
bool is_odd_level;
uint32_t adder_duration;
} SubGhzDeviceCC1101ExtAsyncTxMiddleware;
typedef struct { typedef struct {
uint32_t* buffer; uint32_t* buffer;
LevelDuration carry_ld;
SubGhzDeviceCC1101ExtCallback callback; SubGhzDeviceCC1101ExtCallback callback;
void* callback_context; void* callback_context;
uint32_t gpio_tx_buff[2]; uint32_t gpio_tx_buff[2];
uint32_t debug_gpio_buff[2]; uint32_t debug_gpio_buff[2];
SubGhzDeviceCC1101ExtAsyncTxMiddleware middleware;
} SubGhzDeviceCC1101ExtAsyncTx; } SubGhzDeviceCC1101ExtAsyncTx;
typedef struct { typedef struct {
@@ -283,8 +295,8 @@ void subghz_device_cc1101_ext_dump_state() {
void subghz_device_cc1101_ext_load_custom_preset(const uint8_t* preset_data) { void subghz_device_cc1101_ext_load_custom_preset(const uint8_t* preset_data) {
//load config //load config
subghz_device_cc1101_ext_reset();
furi_hal_spi_acquire(subghz_device_cc1101_ext->spi_bus_handle); furi_hal_spi_acquire(subghz_device_cc1101_ext->spi_bus_handle);
cc1101_reset(subghz_device_cc1101_ext->spi_bus_handle);
uint32_t i = 0; uint32_t i = 0;
uint8_t pa[8] = {0}; uint8_t pa[8] = {0};
while(preset_data[i]) { while(preset_data[i]) {
@@ -313,8 +325,8 @@ void subghz_device_cc1101_ext_load_custom_preset(const uint8_t* preset_data) {
} }
void subghz_device_cc1101_ext_load_registers(const uint8_t* data) { void subghz_device_cc1101_ext_load_registers(const uint8_t* data) {
subghz_device_cc1101_ext_reset();
furi_hal_spi_acquire(subghz_device_cc1101_ext->spi_bus_handle); furi_hal_spi_acquire(subghz_device_cc1101_ext->spi_bus_handle);
cc1101_reset(subghz_device_cc1101_ext->spi_bus_handle);
uint32_t i = 0; uint32_t i = 0;
while(data[i]) { while(data[i]) {
cc1101_write_reg(subghz_device_cc1101_ext->spi_bus_handle, data[i], data[i + 1]); cc1101_write_reg(subghz_device_cc1101_ext->spi_bus_handle, data[i], data[i + 1]);
@@ -396,6 +408,7 @@ void subghz_device_cc1101_ext_reset() {
furi_hal_gpio_init(subghz_device_cc1101_ext->g0_pin, GpioModeAnalog, GpioPullNo, GpioSpeedLow); furi_hal_gpio_init(subghz_device_cc1101_ext->g0_pin, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
cc1101_switch_to_idle(subghz_device_cc1101_ext->spi_bus_handle); cc1101_switch_to_idle(subghz_device_cc1101_ext->spi_bus_handle);
cc1101_reset(subghz_device_cc1101_ext->spi_bus_handle); cc1101_reset(subghz_device_cc1101_ext->spi_bus_handle);
// Warning: push pull cc1101 clock output on GD0
cc1101_write_reg( cc1101_write_reg(
subghz_device_cc1101_ext->spi_bus_handle, CC1101_IOCFG0, CC1101IocfgHighImpedance); subghz_device_cc1101_ext->spi_bus_handle, CC1101_IOCFG0, CC1101IocfgHighImpedance);
furi_hal_spi_release(subghz_device_cc1101_ext->spi_bus_handle); furi_hal_spi_release(subghz_device_cc1101_ext->spi_bus_handle);
@@ -616,50 +629,91 @@ void subghz_device_cc1101_ext_stop_async_rx() {
furi_hal_gpio_init(subghz_device_cc1101_ext->g0_pin, GpioModeAnalog, GpioPullNo, GpioSpeedLow); furi_hal_gpio_init(subghz_device_cc1101_ext->g0_pin, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
} }
static void subghz_device_cc1101_ext_async_tx_refill(uint32_t* buffer, size_t samples) { void subghz_device_cc1101_ext_async_tx_middleware_idle(
furi_assert(subghz_device_cc1101_ext->state == SubGhzDeviceCC1101ExtStateAsyncTx); SubGhzDeviceCC1101ExtAsyncTxMiddleware* middleware) {
while(samples > 0) { middleware->state = SubGhzDeviceCC1101ExtAsyncTxMiddlewareStateIdle;
bool is_odd = samples % 2; middleware->is_odd_level = false;
LevelDuration ld; middleware->adder_duration = SUBGHZ_DEVICE_CC1101_EXT_ASYNC_TX_GUARD_TIME;
if(level_duration_is_reset(subghz_device_cc1101_ext->async_tx.carry_ld)) {
ld = subghz_device_cc1101_ext->async_tx.callback(
subghz_device_cc1101_ext->async_tx.callback_context);
} else {
ld = subghz_device_cc1101_ext->async_tx.carry_ld;
subghz_device_cc1101_ext->async_tx.carry_ld = level_duration_reset();
} }
if(level_duration_is_wait(ld)) { static inline uint32_t subghz_device_cc1101_ext_async_tx_middleware_get_duration(
*buffer = SUBGHZ_DEVICE_CC1101_EXT_ASYNC_TX_GUARD_TIME; SubGhzDeviceCC1101ExtAsyncTxMiddleware* middleware,
buffer++; SubGhzDeviceCC1101ExtCallback callback) {
samples--; uint32_t ret = 0;
} else if(level_duration_is_reset(ld)) { bool is_level = false;
if(middleware->state == SubGhzDeviceCC1101ExtAsyncTxMiddlewareStateReset) return 0;
while(1) {
LevelDuration ld = callback(subghz_device_cc1101_ext->async_tx.callback_context);
if(level_duration_is_reset(ld)) {
middleware->state = SubGhzDeviceCC1101ExtAsyncTxMiddlewareStateReset;
if(!middleware->is_odd_level) {
return 0;
} else {
return middleware->adder_duration;
}
} else if(level_duration_is_wait(ld)) {
middleware->is_odd_level = !middleware->is_odd_level;
ret = middleware->adder_duration + SUBGHZ_DEVICE_CC1101_EXT_ASYNC_TX_GUARD_TIME;
middleware->adder_duration = 0;
return ret;
}
is_level = level_duration_get_level(ld);
if(middleware->state == SubGhzDeviceCC1101ExtAsyncTxMiddlewareStateIdle) {
if(is_level != middleware->is_odd_level) {
middleware->state = SubGhzDeviceCC1101ExtAsyncTxMiddlewareStateRun;
middleware->is_odd_level = is_level;
middleware->adder_duration = level_duration_get_duration(ld);
return SUBGHZ_DEVICE_CC1101_EXT_ASYNC_TX_GUARD_TIME;
} else {
continue;
}
}
if(middleware->state == SubGhzDeviceCC1101ExtAsyncTxMiddlewareStateRun) {
if(is_level == middleware->is_odd_level) {
middleware->adder_duration += level_duration_get_duration(ld);
continue;
} else {
middleware->is_odd_level = is_level;
ret = middleware->adder_duration;
middleware->adder_duration = level_duration_get_duration(ld);
return ret;
}
}
}
}
static void subghz_device_cc1101_ext_async_tx_refill(uint32_t* buffer, size_t samples) {
furi_assert(subghz_device_cc1101_ext->state == SubGhzDeviceCC1101ExtStateAsyncTx);
while(samples > 0) {
volatile uint32_t duration = subghz_device_cc1101_ext_async_tx_middleware_get_duration(
&subghz_device_cc1101_ext->async_tx.middleware,
subghz_device_cc1101_ext->async_tx.callback);
if(duration == 0) {
*buffer = 0; *buffer = 0;
buffer++; buffer++;
samples--; samples--;
LL_DMA_DisableIT_HT(SUBGHZ_DEVICE_CC1101_EXT_DMA_CH3_DEF); LL_DMA_DisableIT_HT(SUBGHZ_DEVICE_CC1101_EXT_DMA_CH3_DEF);
LL_DMA_DisableIT_TC(SUBGHZ_DEVICE_CC1101_EXT_DMA_CH3_DEF); LL_DMA_DisableIT_TC(SUBGHZ_DEVICE_CC1101_EXT_DMA_CH3_DEF);
if(LL_DMA_IsActiveFlag_HT3(SUBGHZ_DEVICE_CC1101_EXT_DMA)) {
LL_DMA_ClearFlag_HT3(SUBGHZ_DEVICE_CC1101_EXT_DMA);
}
if(LL_DMA_IsActiveFlag_TC3(SUBGHZ_DEVICE_CC1101_EXT_DMA)) {
LL_DMA_ClearFlag_TC3(SUBGHZ_DEVICE_CC1101_EXT_DMA);
}
LL_TIM_EnableIT_UPDATE(TIM17); LL_TIM_EnableIT_UPDATE(TIM17);
break; break;
} else { } else {
bool level = level_duration_get_level(ld); // Lowest possible value is 4us
if(duration < 4) duration = 4;
// Inject guard time if level is incorrect // Divide by 2 since timer resolution is 2us
if(is_odd != level) { // Subtract 1 since we counting from 0
*buffer = SUBGHZ_DEVICE_CC1101_EXT_ASYNC_TX_GUARD_TIME; *buffer = (duration >> 1) - 1;
buffer++;
samples--;
// Special case: prevent buffer overflow if sample is last
if(samples == 0) {
subghz_device_cc1101_ext->async_tx.carry_ld = ld;
break;
}
}
uint32_t duration = level_duration_get_duration(ld);
furi_assert(duration > 0);
*buffer = duration >> 1;
buffer++; buffer++;
samples--; samples--;
} }
@@ -691,12 +745,14 @@ static void subghz_device_cc1101_ext_async_tx_dma_isr() {
static void subghz_device_cc1101_ext_async_tx_timer_isr() { static void subghz_device_cc1101_ext_async_tx_timer_isr() {
if(LL_TIM_IsActiveFlag_UPDATE(TIM17)) { if(LL_TIM_IsActiveFlag_UPDATE(TIM17)) {
if(LL_TIM_GetAutoReload(TIM17) == 0) { if(LL_TIM_GetAutoReload(TIM17) == 0) {
if(subghz_device_cc1101_ext->state == SubGhzDeviceCC1101ExtStateAsyncTx) {
LL_DMA_DisableChannel(SUBGHZ_DEVICE_CC1101_EXT_DMA_CH3_DEF); LL_DMA_DisableChannel(SUBGHZ_DEVICE_CC1101_EXT_DMA_CH3_DEF);
subghz_device_cc1101_ext->state = SubGhzDeviceCC1101ExtStateAsyncTxEnd;
furi_hal_gpio_write(subghz_device_cc1101_ext->g0_pin, false); furi_hal_gpio_write(subghz_device_cc1101_ext->g0_pin, false);
if(subghz_device_cc1101_ext->async_mirror_pin != NULL) if(subghz_device_cc1101_ext->async_mirror_pin != NULL)
furi_hal_gpio_write(subghz_device_cc1101_ext->async_mirror_pin, false); furi_hal_gpio_write(subghz_device_cc1101_ext->async_mirror_pin, false);
LL_TIM_DisableCounter(TIM17); LL_TIM_DisableCounter(TIM17);
subghz_device_cc1101_ext->state = SubGhzDeviceCC1101ExtStateAsyncTxEnd; }
} }
LL_TIM_ClearFlag_UPDATE(TIM17); LL_TIM_ClearFlag_UPDATE(TIM17);
} }
@@ -746,16 +802,18 @@ bool subghz_device_cc1101_ext_start_async_tx(SubGhzDeviceCC1101ExtCallback callb
// Configure TIM // Configure TIM
// Set the timer resolution to 2 us // Set the timer resolution to 2 us
LL_TIM_SetPrescaler(TIM17, (64 << 1) - 1);
LL_TIM_SetCounterMode(TIM17, LL_TIM_COUNTERMODE_UP); LL_TIM_SetCounterMode(TIM17, LL_TIM_COUNTERMODE_UP);
LL_TIM_SetAutoReload(TIM17, 0xFFFF);
LL_TIM_SetClockDivision(TIM17, LL_TIM_CLOCKDIVISION_DIV1); LL_TIM_SetClockDivision(TIM17, LL_TIM_CLOCKDIVISION_DIV1);
LL_TIM_SetAutoReload(TIM17, 500);
LL_TIM_SetPrescaler(TIM17, (64 << 1) - 1);
LL_TIM_SetClockSource(TIM17, LL_TIM_CLOCKSOURCE_INTERNAL); LL_TIM_SetClockSource(TIM17, LL_TIM_CLOCKSOURCE_INTERNAL);
LL_TIM_DisableARRPreload(TIM17); LL_TIM_DisableARRPreload(TIM17);
furi_hal_interrupt_set_isr( furi_hal_interrupt_set_isr(
FuriHalInterruptIdTim1TrgComTim17, subghz_device_cc1101_ext_async_tx_timer_isr, NULL); FuriHalInterruptIdTim1TrgComTim17, subghz_device_cc1101_ext_async_tx_timer_isr, NULL);
subghz_device_cc1101_ext_async_tx_middleware_idle(
&subghz_device_cc1101_ext->async_tx.middleware);
subghz_device_cc1101_ext_async_tx_refill( subghz_device_cc1101_ext_async_tx_refill(
subghz_device_cc1101_ext->async_tx.buffer, SUBGHZ_DEVICE_CC1101_EXT_ASYNC_TX_BUFFER_FULL); subghz_device_cc1101_ext->async_tx.buffer, SUBGHZ_DEVICE_CC1101_EXT_ASYNC_TX_BUFFER_FULL);
@@ -801,7 +859,6 @@ bool subghz_device_cc1101_ext_start_async_tx(SubGhzDeviceCC1101ExtCallback callb
// Start counter // Start counter
LL_TIM_EnableDMAReq_UPDATE(TIM17); LL_TIM_EnableDMAReq_UPDATE(TIM17);
LL_TIM_GenerateEvent_UPDATE(TIM17);
subghz_device_cc1101_ext_tx(); subghz_device_cc1101_ext_tx();
@@ -820,11 +877,15 @@ void subghz_device_cc1101_ext_stop_async_tx() {
subghz_device_cc1101_ext->state == SubGhzDeviceCC1101ExtStateAsyncTx || subghz_device_cc1101_ext->state == SubGhzDeviceCC1101ExtStateAsyncTx ||
subghz_device_cc1101_ext->state == SubGhzDeviceCC1101ExtStateAsyncTxEnd); subghz_device_cc1101_ext->state == SubGhzDeviceCC1101ExtStateAsyncTxEnd);
// Deinitialize GPIO
furi_hal_gpio_write(subghz_device_cc1101_ext->g0_pin, false);
furi_hal_gpio_init(
subghz_device_cc1101_ext->g0_pin, GpioModeAnalog, GpioPullDown, GpioSpeedLow);
// Shutdown radio // Shutdown radio
subghz_device_cc1101_ext_idle(); subghz_device_cc1101_ext_idle();
// Deinitialize Timer // Deinitialize Timer
FURI_CRITICAL_ENTER();
furi_hal_bus_disable(FuriHalBusTIM17); furi_hal_bus_disable(FuriHalBusTIM17);
furi_hal_interrupt_set_isr(FuriHalInterruptIdTim1TrgComTim17, NULL, NULL); furi_hal_interrupt_set_isr(FuriHalInterruptIdTim1TrgComTim17, NULL, NULL);
@@ -833,17 +894,11 @@ void subghz_device_cc1101_ext_stop_async_tx() {
LL_DMA_DisableChannel(SUBGHZ_DEVICE_CC1101_EXT_DMA_CH4_DEF); LL_DMA_DisableChannel(SUBGHZ_DEVICE_CC1101_EXT_DMA_CH4_DEF);
furi_hal_interrupt_set_isr(SUBGHZ_DEVICE_CC1101_EXT_DMA_CH3_IRQ, NULL, NULL); furi_hal_interrupt_set_isr(SUBGHZ_DEVICE_CC1101_EXT_DMA_CH3_IRQ, NULL, NULL);
// Deinitialize GPIO
furi_hal_gpio_write(subghz_device_cc1101_ext->g0_pin, false);
furi_hal_gpio_init(subghz_device_cc1101_ext->g0_pin, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
// Stop debug // Stop debug
if(subghz_device_cc1101_ext_stop_debug()) { if(subghz_device_cc1101_ext_stop_debug()) {
LL_DMA_DisableChannel(SUBGHZ_DEVICE_CC1101_EXT_DMA_CH5_DEF); LL_DMA_DisableChannel(SUBGHZ_DEVICE_CC1101_EXT_DMA_CH5_DEF);
} }
FURI_CRITICAL_EXIT();
free(subghz_device_cc1101_ext->async_tx.buffer); free(subghz_device_cc1101_ext->async_tx.buffer);
subghz_device_cc1101_ext->state = SubGhzDeviceCC1101ExtStateIdle; subghz_device_cc1101_ext->state = SubGhzDeviceCC1101ExtStateIdle;

View File

@@ -14,6 +14,7 @@ App(
entry_point="advanced_plugin1_ep", entry_point="advanced_plugin1_ep",
requires=["example_advanced_plugins"], requires=["example_advanced_plugins"],
sources=["plugin1.c"], sources=["plugin1.c"],
fal_embedded=True,
) )
App( App(
@@ -22,4 +23,5 @@ App(
entry_point="advanced_plugin2_ep", entry_point="advanced_plugin2_ep",
requires=["example_advanced_plugins"], requires=["example_advanced_plugins"],
sources=["plugin2.c"], sources=["plugin2.c"],
fal_embedded=True,
) )

View File

@@ -23,7 +23,10 @@ int32_t example_advanced_plugins_app(void* p) {
PLUGIN_APP_ID, PLUGIN_API_VERSION, composite_api_resolver_get(resolver)); PLUGIN_APP_ID, PLUGIN_API_VERSION, composite_api_resolver_get(resolver));
do { do {
if(plugin_manager_load_all(manager, APP_DATA_PATH("plugins")) != PluginManagerErrorNone) { // For built-in .fals (fal_embedded==True), use APP_ASSETS_PATH
// Otherwise, use APP_DATA_PATH
if(plugin_manager_load_all(manager, APP_ASSETS_PATH("plugins")) !=
PluginManagerErrorNone) {
FURI_LOG_E(TAG, "Failed to load all libs"); FURI_LOG_E(TAG, "Failed to load all libs");
break; break;
} }

View File

@@ -12,12 +12,12 @@ static bool archive_favorites_read_line(File* file, FuriString* str_result) {
bool result = false; bool result = false;
do { do {
uint16_t read_count = storage_file_read(file, buffer, ARCHIVE_FAV_FILE_BUF_LEN); size_t read_count = storage_file_read(file, buffer, ARCHIVE_FAV_FILE_BUF_LEN);
if(storage_file_get_error(file) != FSE_OK) { if(storage_file_get_error(file) != FSE_OK) {
return false; return false;
} }
for(uint16_t i = 0; i < read_count; i++) { for(size_t i = 0; i < read_count; i++) {
if(buffer[i] == '\n') { if(buffer[i] == '\n') {
uint32_t position = storage_file_tell(file); uint32_t position = storage_file_tell(file);
if(storage_file_get_error(file) != FSE_OK) { if(storage_file_get_error(file) != FSE_OK) {

View File

@@ -27,6 +27,7 @@ static const uint32_t baudrate_list[] = {
460800, 460800,
921600, 921600,
}; };
static const char* software_de_re[] = {"None", "4"};
bool gpio_scene_usb_uart_cfg_on_event(void* context, SceneManagerEvent event) { bool gpio_scene_usb_uart_cfg_on_event(void* context, SceneManagerEvent event) {
GpioApp* app = context; GpioApp* app = context;
@@ -84,6 +85,17 @@ static void line_port_cb(VariableItem* item) {
view_dispatcher_send_custom_event(app->view_dispatcher, GpioUsbUartEventConfigSet); view_dispatcher_send_custom_event(app->view_dispatcher, GpioUsbUartEventConfigSet);
} }
static void line_software_de_re_cb(VariableItem* item) {
GpioApp* app = variable_item_get_context(item);
furi_assert(app);
uint8_t index = variable_item_get_current_value_index(item);
variable_item_set_current_value_text(item, software_de_re[index]);
app->usb_uart_cfg->software_de_re = index;
view_dispatcher_send_custom_event(app->view_dispatcher, GpioUsbUartEventConfigSet);
}
static void line_flow_cb(VariableItem* item) { static void line_flow_cb(VariableItem* item) {
GpioApp* app = variable_item_get_context(item); GpioApp* app = variable_item_get_context(item);
furi_assert(app); furi_assert(app);
@@ -155,6 +167,11 @@ void gpio_scene_usb_uart_cfg_on_enter(void* context) {
app->var_item_flow = item; app->var_item_flow = item;
line_ensure_flow_invariant(app); line_ensure_flow_invariant(app);
item = variable_item_list_add(
var_item_list, "DE/RE Pin", COUNT_OF(software_de_re), line_software_de_re_cb, app);
variable_item_set_current_value_index(item, app->usb_uart_cfg->software_de_re);
variable_item_set_current_value_text(item, software_de_re[app->usb_uart_cfg->software_de_re]);
variable_item_list_set_selected_item( variable_item_list_set_selected_item(
var_item_list, scene_manager_get_scene_state(app->scene_manager, GpioAppViewUsbUartCfg)); var_item_list, scene_manager_get_scene_state(app->scene_manager, GpioAppViewUsbUartCfg));

View File

@@ -6,11 +6,16 @@
#include <furi_hal.h> #include <furi_hal.h>
#include <furi_hal_usb_cdc.h> #include <furi_hal_usb_cdc.h>
//TODO: FL-3276 port to new USART API
#include <stm32wbxx_ll_lpuart.h>
#include <stm32wbxx_ll_usart.h>
#define USB_CDC_PKT_LEN CDC_DATA_SZ #define USB_CDC_PKT_LEN CDC_DATA_SZ
#define USB_UART_RX_BUF_SIZE (USB_CDC_PKT_LEN * 5) #define USB_UART_RX_BUF_SIZE (USB_CDC_PKT_LEN * 5)
#define USB_CDC_BIT_DTR (1 << 0) #define USB_CDC_BIT_DTR (1 << 0)
#define USB_CDC_BIT_RTS (1 << 1) #define USB_CDC_BIT_RTS (1 << 1)
#define USB_USART_DE_RE_PIN &gpio_ext_pa4
static const GpioPin* flow_pins[][2] = { static const GpioPin* flow_pins[][2] = {
{&gpio_ext_pa7, &gpio_ext_pa6}, // 2, 3 {&gpio_ext_pa7, &gpio_ext_pa6}, // 2, 3
@@ -247,6 +252,17 @@ static int32_t usb_uart_worker(void* context) {
usb_uart->cfg.flow_pins = usb_uart->cfg_new.flow_pins; usb_uart->cfg.flow_pins = usb_uart->cfg_new.flow_pins;
events |= WorkerEvtCtrlLineSet; events |= WorkerEvtCtrlLineSet;
} }
if(usb_uart->cfg.software_de_re != usb_uart->cfg_new.software_de_re) {
usb_uart->cfg.software_de_re = usb_uart->cfg_new.software_de_re;
if(usb_uart->cfg.software_de_re != 0) {
furi_hal_gpio_write(USB_USART_DE_RE_PIN, true);
furi_hal_gpio_init(
USB_USART_DE_RE_PIN, GpioModeOutputPushPull, GpioPullNo, GpioSpeedMedium);
} else {
furi_hal_gpio_init(
USB_USART_DE_RE_PIN, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
}
}
api_lock_unlock(usb_uart->cfg_lock); api_lock_unlock(usb_uart->cfg_lock);
} }
if(events & WorkerEvtLineCfgSet) { if(events & WorkerEvtLineCfgSet) {
@@ -260,6 +276,8 @@ static int32_t usb_uart_worker(void* context) {
usb_uart_vcp_deinit(usb_uart, usb_uart->cfg.vcp_ch); usb_uart_vcp_deinit(usb_uart, usb_uart->cfg.vcp_ch);
usb_uart_serial_deinit(usb_uart, usb_uart->cfg.uart_ch); usb_uart_serial_deinit(usb_uart, usb_uart->cfg.uart_ch);
furi_hal_gpio_init(USB_USART_DE_RE_PIN, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
if(usb_uart->cfg.flow_pins != 0) { if(usb_uart->cfg.flow_pins != 0) {
furi_hal_gpio_init_simple(flow_pins[usb_uart->cfg.flow_pins - 1][0], GpioModeAnalog); furi_hal_gpio_init_simple(flow_pins[usb_uart->cfg.flow_pins - 1][0], GpioModeAnalog);
furi_hal_gpio_init_simple(flow_pins[usb_uart->cfg.flow_pins - 1][1], GpioModeAnalog); furi_hal_gpio_init_simple(flow_pins[usb_uart->cfg.flow_pins - 1][1], GpioModeAnalog);
@@ -298,7 +316,24 @@ static int32_t usb_uart_tx_thread(void* context) {
if(len > 0) { if(len > 0) {
usb_uart->st.tx_cnt += len; usb_uart->st.tx_cnt += len;
if(usb_uart->cfg.software_de_re != 0)
furi_hal_gpio_write(USB_USART_DE_RE_PIN, false);
furi_hal_uart_tx(usb_uart->cfg.uart_ch, data, len); furi_hal_uart_tx(usb_uart->cfg.uart_ch, data, len);
if(usb_uart->cfg.software_de_re != 0) {
//TODO: FL-3276 port to new USART API
if(usb_uart->cfg.uart_ch == FuriHalUartIdUSART1) {
while(!LL_USART_IsActiveFlag_TC(USART1))
;
} else if(usb_uart->cfg.uart_ch == FuriHalUartIdLPUART1) {
while(!LL_LPUART_IsActiveFlag_TC(LPUART1))
;
}
furi_hal_gpio_write(USB_USART_DE_RE_PIN, true);
}
} }
} }
} }

View File

@@ -11,6 +11,7 @@ typedef struct {
uint8_t flow_pins; uint8_t flow_pins;
uint8_t baudrate_mode; uint8_t baudrate_mode;
uint32_t baudrate; uint32_t baudrate;
uint8_t software_de_re;
} UsbUartConfig; } UsbUartConfig;
typedef struct { typedef struct {

View File

@@ -174,22 +174,21 @@ void ibutton_free(iButton* ibutton) {
free(ibutton); free(ibutton);
} }
bool ibutton_load_key(iButton* ibutton) { bool ibutton_load_key(iButton* ibutton, bool show_error) {
view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewLoading); view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewLoading);
const bool success = ibutton_protocols_load( const bool success = ibutton_protocols_load(
ibutton->protocols, ibutton->key, furi_string_get_cstr(ibutton->file_path)); ibutton->protocols, ibutton->key, furi_string_get_cstr(ibutton->file_path));
if(!success) { if(success) {
dialog_message_show_storage_error(ibutton->dialogs, "Cannot load\nkey file");
} else {
FuriString* tmp = furi_string_alloc(); FuriString* tmp = furi_string_alloc();
path_extract_filename(ibutton->file_path, tmp, true); path_extract_filename(ibutton->file_path, tmp, true);
strncpy(ibutton->key_name, furi_string_get_cstr(tmp), IBUTTON_KEY_NAME_SIZE); strncpy(ibutton->key_name, furi_string_get_cstr(tmp), IBUTTON_KEY_NAME_SIZE);
furi_string_free(tmp); furi_string_free(tmp);
} else if(show_error) {
dialog_message_show_storage_error(ibutton->dialogs, "Cannot load\nkey file");
} }
return success; return success;
@@ -210,7 +209,7 @@ bool ibutton_select_and_load_key(iButton* ibutton) {
if(!dialog_file_browser_show( if(!dialog_file_browser_show(
ibutton->dialogs, ibutton->file_path, ibutton->file_path, &browser_options)) ibutton->dialogs, ibutton->file_path, ibutton->file_path, &browser_options))
break; break;
success = ibutton_load_key(ibutton); success = ibutton_load_key(ibutton, true);
} while(!success); } while(!success);
return success; return success;
@@ -283,7 +282,7 @@ int32_t ibutton_app(void* arg) {
} else { } else {
furi_string_set(ibutton->file_path, (const char*)arg); furi_string_set(ibutton->file_path, (const char*)arg);
key_loaded = ibutton_load_key(ibutton); key_loaded = ibutton_load_key(ibutton, true);
} }
} }

View File

@@ -90,7 +90,7 @@ typedef enum {
} iButtonNotificationMessage; } iButtonNotificationMessage;
bool ibutton_select_and_load_key(iButton* ibutton); bool ibutton_select_and_load_key(iButton* ibutton);
bool ibutton_load_key(iButton* ibutton); bool ibutton_load_key(iButton* ibutton, bool show_error);
bool ibutton_save_key(iButton* ibutton); bool ibutton_save_key(iButton* ibutton);
bool ibutton_delete_key(iButton* ibutton); bool ibutton_delete_key(iButton* ibutton);
void ibutton_reset_key(iButton* ibutton); void ibutton_reset_key(iButton* ibutton);

View File

@@ -43,7 +43,7 @@ bool ibutton_scene_add_value_on_event(void* context, SceneManagerEvent event) {
} else if(event.type == SceneManagerEventTypeBack) { } else if(event.type == SceneManagerEventTypeBack) {
// User cancelled editing, reload the key from storage // User cancelled editing, reload the key from storage
if(scene_manager_has_previous_scene(scene_manager, iButtonSceneSavedKeyMenu)) { if(scene_manager_has_previous_scene(scene_manager, iButtonSceneSavedKeyMenu)) {
if(!ibutton_load_key(ibutton)) { if(!ibutton_load_key(ibutton, true)) {
consumed = scene_manager_search_and_switch_to_previous_scene( consumed = scene_manager_search_and_switch_to_previous_scene(
scene_manager, iButtonSceneStart); scene_manager, iButtonSceneStart);
} }

View File

@@ -9,9 +9,8 @@ void ibutton_scene_delete_success_on_enter(void* context) {
iButton* ibutton = context; iButton* ibutton = context;
Popup* popup = ibutton->popup; Popup* popup = ibutton->popup;
popup_set_icon(popup, 0, 2, &I_DolphinMafia_115x62); popup_set_icon(popup, 0, 2, &I_DolphinMafia_119x62);
popup_set_header(popup, "Deleted", 83, 19, AlignLeft, AlignBottom); popup_set_header(popup, "Deleted", 80, 19, AlignLeft, AlignBottom);
popup_set_callback(popup, ibutton_scene_delete_success_popup_callback); popup_set_callback(popup, ibutton_scene_delete_success_popup_callback);
popup_set_context(popup, ibutton); popup_set_context(popup, ibutton);
popup_set_timeout(popup, 1500); popup_set_timeout(popup, 1500);

View File

@@ -23,15 +23,15 @@ void ibutton_scene_emulate_on_enter(void* context) {
furi_string_printf( furi_string_printf(
tmp, tmp,
"%s\n[%s]", "[%s]\n%s",
furi_string_empty(ibutton->file_path) ? "Unsaved Key" : ibutton->key_name, ibutton_protocols_get_name(ibutton->protocols, ibutton_key_get_protocol_id(key)),
ibutton_protocols_get_name(ibutton->protocols, ibutton_key_get_protocol_id(key))); furi_string_empty(ibutton->file_path) ? "Unsaved Key" : ibutton->key_name);
widget_add_text_box_element( widget_add_text_box_element(
widget, 52, 38, 75, 26, AlignCenter, AlignCenter, furi_string_get_cstr(tmp), true); widget, 52, 30, 75, 40, AlignCenter, AlignCenter, furi_string_get_cstr(tmp), true);
widget_add_string_multiline_element( widget_add_string_multiline_element(
widget, 88, 10, AlignCenter, AlignTop, FontPrimary, "iButton\nemulating"); widget, 88, 5, AlignCenter, AlignTop, FontPrimary, "iButton\nemulating");
ibutton_worker_emulate_set_callback(ibutton->worker, ibutton_scene_emulate_callback, ibutton); ibutton_worker_emulate_set_callback(ibutton->worker, ibutton_scene_emulate_callback, ibutton);
ibutton_worker_emulate_start(ibutton->worker, key); ibutton_worker_emulate_start(ibutton->worker, key);

View File

@@ -8,21 +8,19 @@ void ibutton_scene_info_on_enter(void* context) {
const iButtonProtocolId protocol_id = ibutton_key_get_protocol_id(key); const iButtonProtocolId protocol_id = ibutton_key_get_protocol_id(key);
FuriString* tmp = furi_string_alloc(); FuriString* tmp = furi_string_alloc();
FuriString* keynumber = furi_string_alloc();
ibutton_protocols_render_brief_data(ibutton->protocols, key, keynumber);
furi_string_printf( furi_string_printf(
tmp, tmp,
"\e#%s [%s]\e#", "\e#%s\n[%s]\e#\n%s",
ibutton->key_name, ibutton->key_name,
ibutton_protocols_get_name(ibutton->protocols, protocol_id)); ibutton_protocols_get_name(ibutton->protocols, protocol_id),
furi_string_get_cstr(keynumber));
widget_add_text_box_element( widget_add_text_box_element(
widget, 0, 2, 128, 12, AlignLeft, AlignTop, furi_string_get_cstr(tmp), true); widget, 0, 2, 128, 64, AlignLeft, AlignTop, furi_string_get_cstr(tmp), true);
furi_string_reset(tmp);
ibutton_protocols_render_brief_data(ibutton->protocols, key, tmp);
widget_add_string_multiline_element(
widget, 0, 16, AlignLeft, AlignTop, FontSecondary, furi_string_get_cstr(tmp));
if(ibutton_protocols_get_features(ibutton->protocols, protocol_id) & if(ibutton_protocols_get_features(ibutton->protocols, protocol_id) &
iButtonProtocolFeatureExtData) { iButtonProtocolFeatureExtData) {
@@ -32,6 +30,7 @@ void ibutton_scene_info_on_enter(void* context) {
view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewWidget); view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewWidget);
furi_string_free(tmp); furi_string_free(tmp);
furi_string_free(keynumber);
} }
bool ibutton_scene_info_on_event(void* context, SceneManagerEvent event) { bool ibutton_scene_info_on_event(void* context, SceneManagerEvent event) {

View File

@@ -26,7 +26,7 @@ bool ibutton_scene_rpc_on_event(void* context, SceneManagerEvent event) {
if(event.event == iButtonCustomEventRpcLoadFile) { if(event.event == iButtonCustomEventRpcLoadFile) {
bool result = false; bool result = false;
if(ibutton_load_key(ibutton)) { if(ibutton_load_key(ibutton, false)) {
popup_set_text(popup, ibutton->key_name, 82, 32, AlignCenter, AlignTop); popup_set_text(popup, ibutton->key_name, 82, 32, AlignCenter, AlignTop);
view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewPopup); view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewPopup);

View File

@@ -9,9 +9,8 @@ void ibutton_scene_save_success_on_enter(void* context) {
iButton* ibutton = context; iButton* ibutton = context;
Popup* popup = ibutton->popup; Popup* popup = ibutton->popup;
popup_set_icon(popup, 32, 5, &I_DolphinNice_96x59); popup_set_icon(popup, 36, 5, &I_DolphinSaved_92x58);
popup_set_header(popup, "Saved!", 5, 7, AlignLeft, AlignTop); popup_set_header(popup, "Saved", 15, 19, AlignLeft, AlignBottom);
popup_set_callback(popup, ibutton_scene_save_success_popup_callback); popup_set_callback(popup, ibutton_scene_save_success_popup_callback);
popup_set_context(popup, ibutton); popup_set_context(popup, ibutton);
popup_set_timeout(popup, 1500); popup_set_timeout(popup, 1500);

View File

@@ -42,12 +42,12 @@ void ibutton_scene_write_on_enter(void* context) {
furi_string_printf( furi_string_printf(
tmp, tmp,
"%s\n[%s]", "[%s]\n%s ",
ibutton->key_name, ibutton_protocols_get_name(ibutton->protocols, protocol_id),
ibutton_protocols_get_name(ibutton->protocols, protocol_id)); ibutton->key_name);
widget_add_text_box_element( widget_add_text_box_element(
widget, 52, 38, 75, 26, AlignCenter, AlignCenter, furi_string_get_cstr(tmp), true); widget, 52, 30, 75, 40, AlignCenter, AlignCenter, furi_string_get_cstr(tmp), true);
ibutton_worker_write_set_callback(worker, ibutton_scene_write_callback, ibutton); ibutton_worker_write_set_callback(worker, ibutton_scene_write_callback, ibutton);
@@ -63,7 +63,7 @@ void ibutton_scene_write_on_enter(void* context) {
} }
widget_add_string_multiline_element( widget_add_string_multiline_element(
widget, 88, 10, AlignCenter, AlignTop, FontPrimary, furi_string_get_cstr(tmp)); widget, 88, 5, AlignCenter, AlignTop, FontPrimary, furi_string_get_cstr(tmp));
ibutton_notification_message(ibutton, iButtonNotificationMessageEmulateStart); ibutton_notification_message(ibutton, iButtonNotificationMessageEmulateStart);
view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewWidget); view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewWidget);

View File

@@ -10,7 +10,7 @@ void ibutton_scene_write_success_on_enter(void* context) {
iButton* ibutton = context; iButton* ibutton = context;
Popup* popup = ibutton->popup; Popup* popup = ibutton->popup;
popup_set_icon(popup, 0, 12, &I_iButtonDolphinVerySuccess_108x52); popup_set_icon(popup, 0, 9, &I_iButtonDolphinVerySuccess_92x55);
popup_set_text(popup, "Successfully written!", 40, 12, AlignLeft, AlignBottom); popup_set_text(popup, "Successfully written!", 40, 12, AlignLeft, AlignBottom);
popup_set_callback(popup, ibutton_scene_write_success_popup_callback); popup_set_callback(popup, ibutton_scene_write_success_popup_callback);

View File

@@ -807,3 +807,155 @@ type: raw
frequency: 38000 frequency: 38000
duty_cycle: 0.330000 duty_cycle: 0.330000
data: 6175 7369 602 1570 602 1570 601 1570 573 1598 574 1598 573 1597 574 1597 574 1598 574 525 574 526 573 526 573 527 572 528 571 529 570 529 570 530 568 1603 568 1627 544 1627 544 1627 544 1628 544 554 545 1627 544 1628 544 555 544 555 544 555 544 555 544 554 544 1627 545 555 544 554 545 1627 544 1627 544 1627 544 1627 544 1627 544 1603 568 1602 569 1603 569 530 569 529 570 529 570 529 570 529 570 529 570 529 570 528 570 1601 571 528 570 1602 570 529 570 528 570 1601 570 1600 571 1601 571 528 571 1601 570 528 570 1601 570 1602 570 529 570 529 570 528 570 1601 570 1601 570 1600 571 1601 571 528 570 1601 570 1601 571 528 571 528 571 529 570 528 571 528 570 1601 571 528 571 528 570 1603 570 529 571 1603 570 529 570 1603 570 529 570 1603 570 529 571 1602 571 1603 570 529 571 1603 570 529 571 1603 570 529 571 1603 570 530 570 7370 570 data: 6175 7369 602 1570 602 1570 601 1570 573 1598 574 1598 573 1597 574 1597 574 1598 574 525 574 526 573 526 573 527 572 528 571 529 570 529 570 530 568 1603 568 1627 544 1627 544 1627 544 1628 544 554 545 1627 544 1628 544 555 544 555 544 555 544 555 544 554 544 1627 545 555 544 554 545 1627 544 1627 544 1627 544 1627 544 1627 544 1603 568 1602 569 1603 569 530 569 529 570 529 570 529 570 529 570 529 570 529 570 528 570 1601 571 528 570 1602 570 529 570 528 570 1601 570 1600 571 1601 571 528 571 1601 570 528 570 1601 570 1602 570 529 570 529 570 528 570 1601 570 1601 570 1600 571 1601 571 528 570 1601 570 1601 571 528 571 528 571 529 570 528 571 528 570 1601 571 528 571 528 570 1603 570 529 571 1603 570 529 570 1603 570 529 570 1603 570 529 571 1602 571 1603 570 529 571 1603 570 529 571 1603 570 529 571 1603 570 530 570 7370 570
#
# Model: AUX YKR-H/006E
#
name: Off
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 8957 4502 539 1683 538 1681 540 559 538 559 538 556 541 557 540 1683 538 1682 539 1684 537 1682 539 1681 540 1684 537 1684 537 1682 539 1684 537 558 539 558 539 558 539 558 539 559 538 558 539 1682 539 1681 540 1683 538 557 540 558 539 558 539 557 540 559 538 557 540 557 540 561 536 559 538 558 539 557 540 558 539 558 539 1683 538 558 539 1682 540 557 540 559 538 557 540 557 540 561 536 558 539 559 538 558 539 560 537 557 540 558 539 558 539 558 539 559 538 558 539 1682 539 557 540 558 539 557 540 557 540 558 539 560 537 557 540 557 540 558 539 559 538 557 540 559 538 556 541 558 539 558 539 558 539 556 541 559 538 557 540 558 539 558 539 558 539 557 540 558 539 557 541 557 540 557 540 559 538 558 539 558 539 558 539 558 539 1681 540 557 540 1683 538 558 539 559 538 557 540 559 538 559 538 1683 538 1683 538 1683 538 557 540 558 539 558 540 1683 538 560 563
#
name: Dh
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 8956 4504 536 1684 537 1687 534 559 538 559 538 559 538 560 537 1683 538 1685 536 1682 539 1684 537 1683 538 1684 537 1683 538 1684 537 1683 538 558 539 562 535 559 538 558 539 562 535 560 537 1683 538 1684 537 1683 538 561 536 561 536 561 537 560 537 561 536 558 539 560 537 559 538 560 537 561 536 561 536 563 534 559 538 1684 537 559 538 1684 537 561 536 560 537 560 537 560 537 560 537 560 537 559 538 559 538 559 538 588 509 558 539 559 538 559 538 564 533 1684 537 559 538 560 537 559 538 588 509 563 534 559 538 559 538 558 539 562 535 558 539 561 536 560 537 560 537 559 538 588 509 561 536 560 537 561 536 563 534 561 536 560 537 561 536 1684 537 559 538 559 538 559 538 561 536 560 537 560 537 559 538 561 536 558 539 560 537 1684 537 559 538 1683 538 561 536 561 536 563 534 559 538 558 539 1683 538 1684 537 1684 537 560 537 560 537 1683 538 560 537 560 563
#
name: Cool_hi
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 8957 4502 538 1683 538 1684 537 562 535 560 537 559 538 559 539 1683 538 1682 539 1711 510 1685 536 1683 538 558 539 588 509 557 540 1682 539 557 540 558 539 559 538 559 538 558 539 561 536 1681 540 1682 539 1683 538 559 538 559 538 559 538 561 536 560 537 559 538 558 539 559 538 559 538 559 538 559 538 560 537 560 537 1685 536 560 537 1685 536 560 537 559 538 560 537 560 537 559 538 558 539 559 538 560 537 587 510 562 535 559 538 560 537 557 540 1685 536 559 538 560 537 587 510 588 509 559 538 562 535 560 537 557 540 559 538 557 540 560 537 587 510 560 538 558 539 559 538 559 538 561 536 560 537 560 537 558 540 560 537 559 538 560 537 1683 538 562 535 560 537 559 538 560 537 558 539 559 538 558 539 560 537 560 537 559 538 1683 538 558 539 1684 537 559 538 558 539 559 538 558 539 558 539 1682 539 1684 537 1684 537 1683 538 560 537 558 539 1683 538 1684 564
#
name: Cool_lo
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 8956 4501 539 1682 539 1682 539 559 538 557 540 587 510 558 539 1684 537 1682 539 1682 539 1682 539 1680 541 1682 539 1682 539 1682 539 1681 540 558 539 558 539 558 539 557 540 558 539 557 540 1683 538 1683 538 1683 538 558 539 558 539 558 539 557 540 560 537 560 537 557 540 558 539 557 540 558 539 557 540 560 537 558 539 1681 540 556 541 1684 537 558 539 557 540 559 538 558 539 557 540 558 539 558 539 560 537 559 538 558 539 558 539 557 540 559 538 1685 536 559 538 558 539 587 510 557 540 559 538 559 538 560 537 560 537 558 539 559 538 558 539 559 538 562 535 558 539 557 540 557 540 559 538 559 538 558 539 559 538 558 539 558 539 557 540 1682 539 557 540 558 539 559 538 557 540 558 539 560 537 559 538 557 540 561 536 558 539 1682 539 558 539 1682 539 559 538 557 540 558 539 559 538 559 538 1682 539 1684 537 1683 538 557 540 558 539 558 539 560 537 559 564
#
name: Heat_hi
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 8959 4502 539 1682 539 1682 539 556 541 559 538 558 539 559 538 1680 541 1681 540 1684 537 1683 538 1684 537 557 540 560 537 558 539 1683 538 1682 539 558 539 559 538 559 538 558 539 558 539 1683 538 1681 540 1683 538 559 538 560 537 560 537 559 538 561 536 560 537 559 538 559 538 559 538 559 538 559 538 560 537 557 540 1682 539 557 540 1682 539 559 538 558 539 560 538 558 539 558 539 558 539 558 539 558 539 559 538 559 538 557 540 558 539 558 539 559 538 560 537 1684 537 561 536 557 540 559 538 559 538 557 540 558 539 559 538 560 537 558 539 558 539 559 538 558 539 559 539 559 538 557 540 559 538 557 540 558 540 561 536 558 539 558 539 1683 538 558 539 559 538 557 540 559 538 557 540 558 539 559 538 559 538 558 539 559 538 1681 540 558 539 1684 537 562 535 560 537 559 538 559 538 560 537 1682 539 1682 539 1682 539 1686 535 559 538 1682 539 559 538 1682 565
#
name: Heat_lo
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 8961 4501 539 1681 540 1681 540 559 538 558 539 558 539 557 540 1682 539 1682 539 1681 540 1682 539 1682 539 1683 538 1682 539 1683 538 1682 539 557 540 560 537 558 539 560 537 560 537 558 539 1681 540 1681 540 1682 539 557 540 558 539 561 536 558 539 560 537 558 539 556 541 558 539 558 539 557 540 559 538 558 539 559 538 1684 537 559 538 1682 539 558 539 556 541 559 538 562 535 556 541 558 539 558 539 557 540 558 539 557 540 558 539 559 538 560 537 557 540 557 540 1682 539 558 539 557 540 558 539 559 538 559 538 557 540 558 539 558 539 558 539 558 539 557 540 561 536 558 539 586 511 558 539 557 540 586 511 559 539 556 541 557 540 557 540 1682 539 559 538 558 539 558 539 559 538 558 539 560 537 558 539 559 538 558 539 557 540 1681 540 558 539 1680 541 557 540 557 540 559 538 558 539 559 538 1682 539 1682 539 1682 539 558 539 558 539 1682 539 1682 539 558 565
#
# Model: Carrier 42QG5A580SC
#
name: Off
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 8403 4308 519 1425 518 625 520 1461 485 1427 518 559 517 564 567 1386 518 1429 517 562 597 524 519 1431 517 1430 518 567 516 1426 518 565 517 1429 518 1431 518 1425 519 565 517 1457 485 562 518 1429 519 1424 520 565 516 1426 517 561 519 1430 515 1427 518 563 595 525 518 1428 517 528 489 21085 8424 4296 642 467 568 1400 569 533 569 536 647 1402 569 1395 569 535 568 534 569 1405 566 1397 567 549 568 535 569 1401 567 533 570 1399 571 537 570 534 568 537 567 1397 570 535 571 1401 568 534 567 533 567 1399 567 539 568 1402 569 565 569 532 571 1399 571 1397 569 535 566 1358 489 21085 8401 4318 512 1438 517 573 515 1476 532 1389 516 573 518 609 535 1393 568 1392 516 574 567 528 566 1393 565 1390 516 576 516 1437 517 574 517 1441 516 1445 567 1389 517 578 566 1390 516 578 568 1398 566 1392 515 575 516 1437 516 574 517 1442 567 1425 483 597 515 575 516 1446 514 539 490
#
name: Dh
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 8391 4318 536 1417 518 564 516 1432 517 1428 518 563 518 562 517 1433 516 1428 518 564 519 565 513 1433 517 1430 517 562 518 1463 483 561 517 1430 516 1428 518 563 595 1393 516 559 519 565 514 1425 519 1429 517 569 514 1426 518 566 514 1466 485 562 518 559 518 561 518 1425 519 531 488 21116 8369 4318 569 534 641 1326 640 462 641 465 641 1330 641 1325 639 464 568 534 595 1403 641 1328 641 462 639 467 640 1328 568 536 642 1367 568 531 567 536 569 1398 568 537 542 1432 640 1327 567 532 568 535 567 1400 567 539 567 1399 568 536 568 1402 569 1402 568 1401 567 537 565 1359 487 21117 8392 4294 565 1388 517 575 566 1393 516 1443 514 574 516 573 517 1443 567 1391 515 574 516 577 566 1393 565 1390 566 528 567 1392 516 575 567 1395 565 1390 515 576 568 1391 515 572 566 528 565 1395 565 1393 515 572 564 1391 515 581 563 1389 516 578 513 609 483 576 566 1391 514 538 487
#
name: Cool_hi
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 8424 4292 514 1428 518 559 518 1431 514 1432 517 561 515 566 512 1433 516 1434 516 562 519 562 516 1432 517 1426 518 566 516 1429 517 566 516 1429 518 1428 517 562 516 1467 481 570 517 561 518 565 519 563 515 566 516 567 516 1433 516 1429 514 564 518 561 518 562 519 1428 519 536 562 21009 8478 4280 638 461 641 1328 641 456 642 467 637 1325 643 1326 568 597 568 537 639 1330 641 1327 568 535 640 468 639 1324 568 534 543 1432 566 535 641 494 637 1327 566 532 640 1329 567 1398 640 1327 641 1327 640 1326 642 1328 568 531 568 538 567 1402 567 1406 566 1402 568 532 569 1357 489 21085 8401 4319 565 1394 516 577 567 1396 565 1390 515 574 543 566 516 1441 568 1392 516 575 566 531 566 1393 567 1390 516 576 515 1456 515 579 562 1392 515 1440 515 575 566 1391 516 576 544 569 563 525 516 574 514 606 514 576 514 1439 567 1390 515 575 515 574 570 566 567 1390 515 540 488
#
name: Cool_lo
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 8401 4306 518 1460 483 566 518 1432 517 1430 514 561 516 564 517 1429 518 1430 519 592 485 564 517 1427 517 1432 516 561 545 1430 516 566 518 1462 483 1430 515 568 517 1427 516 566 516 564 517 1430 518 1425 518 559 518 562 518 1426 518 1425 517 593 484 561 516 573 516 1427 517 530 564 21007 8401 4322 642 465 643 1324 643 457 643 458 643 1327 643 1327 641 463 642 461 644 1325 644 1324 643 462 642 483 643 1328 641 460 645 1321 643 458 643 464 641 1325 643 459 644 1327 642 1323 643 461 643 461 642 1347 644 1331 641 463 639 462 641 1327 571 1405 542 1428 670 462 645 1282 562 21007 8402 4317 515 1436 517 606 641 1321 516 1438 517 572 515 578 516 1441 515 1440 516 572 515 575 515 1474 610 1316 515 574 644 1314 516 574 567 1394 640 1316 517 578 639 1319 516 576 517 575 513 1447 641 1315 516 576 515 577 566 1396 514 1440 516 573 518 571 638 456 565 1393 515 538 564
#
name: Heat_hi
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 8399 4307 519 1426 518 560 517 1432 517 1460 485 561 518 558 517 1436 518 1427 518 555 520 561 517 1430 519 1428 516 560 519 1425 518 566 517 1430 516 1432 519 563 519 1425 518 563 515 1429 517 1433 517 1430 516 1432 518 1429 518 568 516 1432 515 1429 519 561 516 567 517 1427 519 533 487 21083 8424 4339 567 536 568 1402 641 493 608 462 545 1431 640 1326 567 532 568 538 640 1331 640 1329 641 536 565 534 641 1329 567 537 639 1329 640 462 642 467 641 1325 568 532 642 1332 640 488 568 534 565 536 566 566 536 567 641 1329 570 535 638 464 569 1401 641 1326 568 534 639 1287 490 21083 8403 4313 567 1390 516 579 514 1441 515 1438 517 575 516 576 568 1393 568 1394 515 606 534 530 515 1441 567 1425 482 576 568 1392 514 578 566 1392 569 1432 515 576 568 1389 517 577 515 1444 515 1437 569 1393 516 1444 566 1389 566 528 567 1394 566 1419 517 575 513 578 515 1440 516 539 490
#
name: Heat_lo
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 8425 4291 542 1401 544 538 542 1404 518 1427 544 534 570 540 516 1434 542 1399 544 539 544 539 541 1403 541 1402 516 570 517 1427 545 566 511 1406 517 1431 542 534 544 1406 543 536 517 567 543 1404 542 1400 595 525 544 1405 542 534 516 1433 516 1428 544 537 544 535 543 1403 542 505 488 21084 8424 4298 566 535 569 1402 567 533 568 537 570 1403 570 1403 565 534 566 537 566 1402 569 1398 569 533 569 538 592 1446 569 535 570 1400 569 567 535 535 568 1437 568 533 568 1398 569 1402 565 533 567 534 569 1403 568 536 569 1402 568 535 567 534 571 1403 568 1415 566 536 571 1362 489 21084 8403 4313 516 1439 517 574 515 1442 515 1441 518 573 516 574 567 1397 514 1440 515 573 516 575 516 1443 515 1439 518 574 516 1440 517 608 535 1396 517 1441 517 579 515 1438 515 576 517 578 568 1390 569 1391 516 575 518 1439 516 573 517 1445 566 1391 516 571 517 572 516 1441 514 543 487
#
# Model: Samsung DB93
#
name: Off
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 667 17837 3089 8903 555 445 578 1411 583 442 554 442 529 468 528 468 554 443 554 442 554 444 552 1441 552 472 523 475 522 1473 522 1473 522 475 547 1447 548 1446 548 1446 548 1446 548 1446 548 449 548 450 547 451 546 474 523 476 521 476 521 476 522 476 521 475 546 451 547 450 548 449 548 449 548 449 548 449 548 449 548 449 548 450 547 450 547 451 546 474 523 474 523 476 521 477 520 477 520 477 521 476 521 476 546 450 547 450 547 450 546 450 547 451 546 451 546 1448 546 1472 522 2982 3005 8963 522 1499 495 502 495 502 495 502 496 501 496 501 521 476 522 475 522 475 522 1472 522 475 522 475 522 1473 521 475 522 1473 522 1499 495 1500 494 1500 496 1499 521 1473 522 475 522 475 522 475 522 475 522 475 522 475 522 476 521 475 522 476 521 476 521 476 521 502 495 503 494 503 495 502 495 501 496 501 521 476 522 476 521 476 521 476 521 476 521 476 521 476 521 476 521 476 521 476 521 476 521 503 494 503 494 503 493 504 494 502 495 502 495 502 520 477 520 2984 3003 8966 519 1475 520 477 520 477 520 477 520 478 519 477 520 478 519 479 518 504 493 1502 493 504 493 503 494 503 494 1501 519 1476 518 1475 519 478 519 1476 518 1476 519 1477 517 1501 493 1503 491 1503 493 1502 493 1502 518 479 518 479 518 479 518 1476 518 1477 517 1477 517 504 493 504 493 504 493 531 466 507 490 1529 467 506 491 529 468 1527 492 1502 492 505 493 1502 492 1502 492 505 492 504 493 504 492 505 492 506 491 532 465 532 465 531 467 530 467 530 467 1528 467 1527 492
#
name: Dh
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 667 17829 3090 8902 556 444 579 1410 584 441 529 468 529 468 554 443 554 442 555 443 553 444 551 1442 551 474 522 475 522 1473 522 475 522 475 547 1448 547 1447 547 1447 547 1447 548 1447 547 449 548 450 547 474 523 474 523 476 521 476 521 476 521 477 521 475 522 476 546 450 547 449 548 449 548 450 547 449 548 450 547 450 547 450 547 451 546 474 523 474 523 474 523 479 518 479 518 479 518 501 496 501 496 477 545 475 522 452 545 474 523 474 523 1472 522 1472 522 1472 522 1472 523 2981 3005 8990 494 1499 495 502 496 501 496 501 496 501 522 475 522 475 522 475 522 475 522 1473 521 475 522 475 522 1473 521 475 522 1473 521 1500 494 1500 495 1499 520 1474 522 1473 522 475 522 475 522 476 521 475 522 476 521 476 521 476 521 476 521 476 521 477 520 503 494 503 494 503 495 502 495 502 520 477 521 476 521 476 521 476 521 476 521 476 521 476 521 476 521 476 521 476 521 476 521 477 520 504 493 503 494 503 494 503 495 502 495 502 495 502 520 477 520 477 520 2984 3003 8966 519 1475 519 477 520 478 519 478 519 478 519 479 518 503 494 505 492 505 492 1503 493 504 493 504 493 504 518 1477 518 1476 518 1476 518 479 518 1477 517 1477 517 1501 493 1504 490 1528 468 1527 468 1527 493 1501 493 504 493 504 493 504 493 1501 493 1502 492 1502 492 504 493 505 491 532 440 556 466 531 467 530 468 530 467 530 467 1527 492 1503 491 505 492 504 493 504 493 505 491 1503 492 505 467 530 492 506 466 557 464 533 464 532 466 1528 467 1528 467 1528 466 1528 491
#
name: Cool_hi
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 667 17831 3089 8903 581 420 578 1411 583 442 529 468 528 468 554 443 554 442 554 443 553 445 551 1443 550 475 521 475 522 1473 522 475 547 449 548 1447 548 1446 548 1446 548 1446 548 1446 548 449 548 449 548 450 547 473 524 476 521 475 522 476 520 476 522 475 522 475 547 449 549 449 548 449 548 449 548 449 548 449 548 449 548 449 548 449 548 450 547 474 523 474 523 476 521 476 521 476 520 477 521 476 546 451 547 450 547 450 547 449 548 450 547 1447 547 1448 546 1448 546 1472 523 2957 3029 8967 518 1477 517 502 495 501 496 501 521 475 522 475 522 475 522 475 522 475 522 1472 522 474 523 475 522 1473 521 475 522 1472 522 1499 495 1500 495 1499 520 1474 522 1473 521 475 522 475 522 475 522 475 522 475 522 475 522 475 522 475 522 476 521 502 495 502 495 503 494 502 496 501 496 501 521 476 522 476 521 475 522 475 522 476 521 476 521 476 521 476 521 476 521 476 521 476 521 476 521 503 494 503 494 503 494 503 495 502 495 502 520 477 520 476 521 476 521 2984 3003 8965 520 1474 521 477 520 477 520 477 520 477 520 477 520 478 519 504 493 504 493 1502 494 503 494 502 495 1500 520 1475 519 1475 519 1475 519 478 519 1475 519 1476 518 1501 493 1502 492 1503 493 1501 494 1501 518 1476 518 478 519 478 519 478 519 1476 518 1476 518 1477 517 504 493 506 491 506 491 506 491 506 492 505 492 504 493 504 518 481 516 1501 493 504 493 504 493 480 517 1501 493 504 493 504 493 504 493 505 491 532 466 531 466 532 466 1528 467 1527 468 1527 492 1502 492
#
name: Cool_lo
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 667 17832 3088 8903 575 448 555 1411 583 441 529 468 529 468 554 443 554 442 554 443 553 444 551 1443 550 474 522 475 522 1473 522 475 522 475 547 1447 548 1446 548 1446 548 1446 548 1447 547 449 548 449 548 474 523 474 523 476 521 476 521 476 521 477 521 475 522 475 547 450 548 449 548 450 547 449 548 449 548 450 547 450 547 449 547 451 546 452 545 474 523 474 523 477 520 478 519 477 519 478 520 477 545 452 545 451 547 451 546 474 523 474 523 1450 544 1472 522 1472 522 1472 523 2981 3005 8990 494 1499 495 502 496 501 496 501 521 476 521 475 522 475 522 475 522 475 522 1473 521 475 522 475 522 1473 521 476 521 1473 521 1500 494 1500 495 1499 495 1499 521 1473 522 475 522 475 522 476 521 475 522 476 521 476 521 476 521 476 521 476 521 477 520 503 494 503 494 503 495 502 495 502 520 477 520 476 521 476 521 476 521 476 521 476 521 476 521 476 521 476 521 476 521 477 520 477 520 478 519 503 493 504 493 504 494 503 494 502 519 478 520 477 520 477 520 2984 3003 8966 519 1475 519 477 520 477 520 478 519 478 519 478 519 503 494 504 493 505 492 1503 493 504 493 504 493 503 519 478 519 1476 518 1476 518 478 519 1476 518 1477 517 1501 493 1504 490 1504 490 1503 493 1502 493 1502 517 480 517 504 493 480 517 1477 517 1502 492 1502 492 504 493 504 493 504 493 531 466 531 466 1529 467 1527 467 1527 492 504 493 1502 492 505 492 504 493 505 492 1503 492 505 492 505 492 505 467 557 464 532 441 556 466 532 466 1528 466 1528 491 1503 491 1503 466
#
name: Heat_hi
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 665 17827 3090 8902 556 444 579 1412 583 441 530 467 529 468 554 442 555 442 554 442 554 444 551 1443 550 473 523 475 522 1472 521 475 523 474 523 1471 549 1446 548 1445 549 1446 548 1445 549 448 549 448 549 449 548 473 523 473 524 475 522 475 522 475 523 475 522 474 548 449 548 449 549 448 549 448 549 448 549 448 549 448 549 449 548 449 548 449 548 450 547 473 524 474 523 476 521 476 521 476 521 476 522 475 547 450 547 450 548 449 548 449 548 1447 547 1447 547 1447 547 1448 546 2957 3030 8962 522 1475 519 501 496 501 497 478 519 500 522 475 522 475 523 474 523 474 523 1472 522 474 523 474 523 1472 522 475 522 1472 522 1499 495 1499 496 1499 496 1498 522 1473 522 474 522 475 522 475 522 474 523 475 522 475 522 475 522 475 522 475 522 475 522 502 495 502 495 502 495 501 496 501 496 501 521 476 522 475 522 475 522 475 522 475 522 475 522 475 522 475 522 475 522 475 522 476 521 476 521 502 495 502 495 502 495 503 495 502 495 501 521 476 521 476 521 2983 3004 8964 521 1474 520 476 521 476 521 477 520 476 521 477 520 477 520 503 494 503 494 1501 494 502 495 502 495 502 520 477 521 1474 520 1474 520 477 520 1474 520 1475 519 1475 519 1500 494 1502 492 1502 493 1501 494 1500 519 478 519 477 520 477 520 1475 519 1475 519 1475 519 478 519 478 519 503 494 505 492 505 492 505 492 1503 493 1502 493 1502 517 1476 518 479 518 479 518 479 518 480 517 479 518 1501 493 504 493 504 493 530 467 531 466 531 467 1527 468 1527 491 1502 493 1501 493
#
name: Heat_lo
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 668 17822 3064 8902 582 442 555 1413 581 442 528 468 529 494 528 469 502 495 527 470 526 471 525 1468 552 446 550 448 549 1446 548 448 549 448 549 1446 548 1448 546 1471 523 1473 521 1473 521 476 521 475 522 475 547 449 548 449 548 449 548 449 548 449 548 449 548 449 548 449 548 450 547 474 523 474 523 474 523 477 520 477 520 477 520 477 521 476 521 476 546 450 548 450 547 474 523 474 523 450 547 474 523 474 523 452 545 474 523 474 523 474 523 1500 494 1499 495 1499 496 1498 522 2955 3032 8963 521 1472 522 475 522 474 523 475 522 475 522 475 522 475 522 475 522 475 522 1499 495 502 495 502 495 1499 496 501 521 1473 522 1473 522 1472 522 1472 522 1473 521 1473 522 475 522 475 522 502 495 502 495 502 495 503 495 501 496 501 496 501 521 476 521 476 521 476 521 476 521 476 521 476 521 476 521 476 521 476 521 476 521 477 520 504 493 503 494 503 494 503 495 502 495 502 495 502 520 477 521 476 521 476 521 476 521 477 520 477 520 477 520 476 521 477 520 2984 3003 8967 518 1502 492 504 493 504 494 503 494 503 494 503 519 478 519 478 519 478 519 1476 518 478 518 479 518 478 519 478 519 1501 493 1501 493 506 491 1504 491 1527 468 1503 492 1526 493 1501 493 1501 494 1501 493 1501 493 504 493 504 493 504 493 1528 466 1529 466 1528 467 529 468 529 493 504 493 504 493 504 493 1502 492 1502 492 1502 467 529 493 1502 491 533 465 532 465 532 465 531 467 530 467 1528 467 530 467 530 467 530 467 530 467 530 467 1527 467 1528 466 1528 466 1529 492
#
# Model: Samsung AR-EH04
#
name: Off
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 663 17769 3057 8901 529 492 527 1434 556 465 529 464 529 464 555 439 555 438 556 438 554 440 553 1435 552 444 549 470 524 1465 524 1466 522 473 522 1467 522 1466 549 1440 549 1440 548 1440 548 445 549 445 549 446 548 447 547 471 523 470 524 471 523 472 522 472 521 473 522 472 522 472 523 472 548 446 549 445 548 446 548 447 547 447 547 446 548 447 547 470 524 471 523 471 523 471 523 471 523 498 496 475 519 498 496 497 498 497 522 472 523 471 524 470 523 471 523 1443 546 1442 547 2947 3023 8935 522 1466 522 471 523 472 522 474 520 498 496 498 496 498 497 497 497 497 523 1466 523 471 523 471 523 1466 523 471 523 1466 522 1467 522 1467 522 1493 495 1493 496 1493 497 497 522 472 523 471 523 471 522 472 522 472 522 472 522 472 522 472 522 472 522 472 522 472 522 472 522 499 495 499 495 499 495 499 495 499 496 498 522 472 523 472 523 471 522 472 522 472 522 472 522 472 522 473 521 473 521 473 521 473 521 473 521 499 495 500 494 499 495 499 496 499 521 2947 3024 8937 521 1467 521 473 521 473 521 472 521 473 521 473 521 473 521 473 521 474 520 1469 520 500 494 500 494 1495 495 500 495 499 520 474 521 1468 520 1468 521 1468 521 1468 521 1468 521 1469 519 1470 518 1495 493 1496 493 500 495 499 520 474 521 1468 520 1469 520 1469 520 473 521 474 520 474 520 475 519 476 518 500 494 502 492 502 491 1497 493 1495 495 499 495 499 520 474 520 475 519 475 519 475 519 475 519 475 519 500 494 501 493 501 493 501 493 501 493 1498 491 1497 519
#
name: Dh
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 665 17760 3083 8875 553 468 527 1432 584 438 555 439 529 464 555 439 555 438 556 438 555 439 553 1435 552 443 550 470 524 1466 523 471 522 473 521 1467 523 1466 549 1439 549 1440 549 1439 549 445 549 445 549 445 549 446 548 470 524 470 524 471 523 472 522 472 522 473 521 472 522 472 547 447 548 445 549 445 549 445 549 445 549 446 548 445 549 445 549 447 547 470 524 471 523 471 523 471 523 473 521 473 521 473 521 473 521 472 523 472 548 446 548 1441 547 1441 548 1441 547 1441 547 2947 3023 8935 522 1466 522 472 522 474 519 475 519 475 519 474 521 473 546 448 547 448 546 1465 523 449 545 448 546 1466 522 471 523 1467 522 1466 522 1493 495 1493 495 1493 496 1493 522 471 523 471 523 471 523 471 523 471 523 471 523 471 523 472 522 472 522 472 522 472 522 472 522 499 495 498 495 499 496 498 496 498 496 498 522 472 523 471 522 472 522 472 522 472 522 472 522 472 522 472 522 472 522 472 522 473 521 473 521 499 495 500 494 500 495 499 495 499 495 498 522 2947 3023 8937 520 1467 521 473 521 473 521 473 521 473 521 473 521 473 521 474 520 474 520 1495 494 500 494 500 495 500 494 1494 520 1468 521 1467 521 473 521 1468 521 1468 520 1469 519 1469 519 1471 517 1495 494 1495 494 1494 520 474 521 473 521 473 520 1468 521 1468 521 1468 520 474 520 475 519 475 519 500 493 500 494 501 493 501 493 501 493 1495 495 1494 520 474 520 474 520 474 520 475 519 1470 518 475 519 476 518 500 493 501 493 501 493 501 493 1497 492 1497 492 1496 493 1495 518
#
name: Cool_hi
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 666 17765 3056 8901 529 492 526 1434 556 465 529 464 530 464 555 438 556 438 555 439 554 440 552 1436 551 443 550 470 524 1465 524 471 523 472 522 1467 521 1467 548 1441 549 1440 548 1440 548 445 549 445 549 445 549 446 548 447 547 470 524 471 523 471 523 473 521 472 522 473 521 473 521 473 522 472 548 446 549 446 548 446 548 447 547 447 547 470 524 447 547 471 523 471 523 471 523 471 523 471 523 475 519 498 496 498 496 498 496 497 498 497 523 1466 524 1443 545 1441 548 1442 546 2947 3023 8935 522 1466 522 472 522 472 522 498 496 498 496 498 496 498 496 498 522 472 523 1466 522 471 523 471 523 1466 523 471 523 1466 523 1467 522 1467 521 1493 496 1493 495 1493 497 497 522 472 523 471 523 471 523 472 522 472 522 472 522 472 522 472 522 472 522 472 522 472 522 473 521 499 495 499 495 499 495 499 496 498 496 498 522 472 522 472 523 472 521 472 522 472 522 472 522 472 522 473 521 473 521 473 521 473 521 474 520 500 494 500 494 500 495 500 495 499 521 2947 3024 8937 520 1467 521 473 521 473 521 473 521 473 521 473 521 473 521 474 520 474 520 1469 520 500 494 500 493 1496 494 1494 495 1494 520 1468 520 473 521 1469 519 1468 521 1469 519 1470 519 1470 519 1495 493 1496 493 1495 495 499 520 474 520 474 520 1469 520 1469 519 1469 519 474 520 475 519 500 494 500 494 500 494 502 492 502 492 502 493 501 493 1496 494 500 519 475 520 475 518 1470 519 475 518 476 518 475 519 476 494 525 493 501 493 501 493 1498 491 1498 491 1497 493 1496 518
#
name: Cool_lo
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 664 17763 3082 8874 553 444 552 1433 583 438 529 464 530 464 555 439 555 438 556 438 555 440 553 1435 552 443 550 470 524 1466 523 472 521 472 522 1467 523 1466 549 1439 549 1439 549 1439 550 444 550 445 549 445 549 447 547 470 524 471 523 471 523 472 522 472 522 473 521 472 523 472 522 471 549 446 549 445 549 446 548 446 548 446 548 446 548 446 548 448 546 471 523 471 523 471 523 471 523 475 519 497 497 474 520 475 520 497 497 496 524 471 524 1465 523 1442 547 1442 547 1442 547 2946 3024 8935 522 1466 522 471 523 474 520 474 520 498 496 474 521 497 522 471 523 471 523 1465 523 471 523 471 523 1466 522 471 523 1466 523 1466 523 1493 495 1493 495 1493 496 1492 523 471 523 471 523 471 523 471 523 471 523 471 523 471 523 472 522 472 522 472 522 472 522 473 521 499 495 499 495 499 496 498 497 498 497 497 523 472 522 471 522 472 522 472 522 472 522 472 522 472 522 472 522 472 522 473 521 473 521 473 521 499 495 499 495 499 496 499 496 498 496 498 522 2946 3024 8937 521 1467 521 472 522 472 522 473 521 473 521 473 521 473 521 474 520 474 520 1495 493 500 494 499 495 499 495 499 496 1493 521 1468 520 473 521 1468 520 1468 521 1468 520 1469 519 1470 518 1495 494 1495 494 1494 495 499 520 473 521 473 521 1468 520 1469 520 1468 521 474 520 474 520 475 519 475 519 476 518 1496 492 1496 494 1495 495 499 520 1469 520 474 520 474 520 474 519 1470 520 474 520 476 518 476 519 477 517 500 494 500 494 503 491 1497 492 1496 493 1495 519 1470 519
#
name: Heat_hi
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 666 17758 3056 8901 528 493 526 1434 580 441 554 439 529 465 554 439 555 439 555 439 554 440 553 1435 552 443 549 470 524 1465 524 472 522 472 521 1467 523 1467 548 1440 549 1440 548 1440 549 445 549 445 549 446 548 446 548 471 523 471 523 471 523 471 523 473 521 473 521 473 521 473 522 472 547 446 549 446 549 445 548 446 548 446 548 446 548 446 548 447 547 471 523 471 523 471 523 471 523 473 521 474 520 473 521 474 521 473 522 473 546 447 548 1441 548 1442 547 1442 547 1442 546 2948 3021 8936 521 1466 522 472 522 498 496 499 494 476 519 476 519 497 497 497 523 472 522 1466 523 471 523 472 522 1466 522 472 522 1466 522 1467 522 1467 522 1493 494 1495 495 1493 522 472 522 472 522 471 522 472 522 472 522 472 522 472 522 472 522 472 522 472 522 472 522 473 521 473 521 499 495 499 495 499 495 499 496 498 521 473 522 472 523 472 522 472 522 472 522 473 521 472 522 472 522 472 522 473 521 473 521 474 520 473 521 499 495 500 494 500 495 499 495 499 521 2947 3023 8938 520 1468 520 473 521 473 521 473 521 473 521 473 521 473 521 474 520 474 520 1495 494 500 494 500 494 500 495 500 494 1494 520 1468 521 474 520 1468 520 1469 520 1468 520 1469 520 1470 518 1496 493 1496 493 1495 495 499 519 475 520 474 520 1468 520 1469 519 1469 519 474 520 475 519 475 519 476 518 500 494 500 494 1497 492 1497 492 1496 493 1495 519 475 519 475 519 475 519 475 519 475 519 1471 518 476 518 500 494 501 493 501 493 501 493 1498 491 1498 491 1496 518 1472 517
#
name: Heat_lo
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 664 17757 3057 8901 528 469 550 1434 556 465 553 440 529 465 554 439 555 439 555 438 555 440 553 1435 552 443 550 470 524 1466 522 472 522 472 521 1467 523 1466 549 1440 549 1440 548 1440 548 445 549 445 549 445 549 446 548 470 524 471 523 471 523 471 523 472 522 472 522 473 522 472 523 472 548 446 549 445 550 445 548 446 548 446 548 446 548 446 548 447 547 470 524 471 523 471 523 471 523 471 523 474 519 474 521 474 521 473 522 473 546 447 547 1441 548 1442 546 1442 547 1442 546 2947 3023 8935 522 1466 522 472 522 498 496 498 495 499 496 498 496 498 521 473 522 471 523 1466 522 471 523 471 522 1466 523 471 523 1467 522 1467 522 1467 521 1493 495 1494 496 1493 521 473 522 472 522 471 523 472 522 471 523 472 522 472 522 472 522 472 522 472 522 472 522 472 522 473 521 499 495 499 495 499 495 499 496 498 522 473 522 472 523 471 522 472 522 472 522 472 522 472 522 472 522 472 522 473 521 473 521 473 521 473 521 499 495 500 494 500 495 499 496 498 522 2947 3023 8937 521 1468 520 473 521 473 521 473 521 473 521 473 521 473 521 474 520 474 520 1470 518 500 494 500 494 500 494 500 494 1494 521 1468 521 473 521 1468 520 1468 520 1469 520 1469 520 1470 518 1495 493 1496 493 1495 494 500 519 475 520 474 520 1469 519 1469 520 1469 519 474 520 474 520 475 519 477 517 500 494 1496 493 1496 493 1496 493 500 495 1495 519 475 518 475 519 475 518 476 518 475 519 1470 519 500 494 500 494 501 493 501 493 501 493 1499 490 1497 493 1497 492 1496 518

View File

@@ -1,7 +1,7 @@
Filetype: IR library file Filetype: IR library file
Version: 1 Version: 1
# Last Updated 1st Sept, 2023 # Last Updated 21st Dec, 2023
# Last Checked 1st Oct, 2023 # Last Checked 21st Dec, 2023
# #
name: Power name: Power
type: parsed type: parsed
@@ -3896,3 +3896,159 @@ type: parsed
protocol: NECext protocol: NECext
address: 80 70 00 00 address: 80 70 00 00
command: C1 3E 00 00 command: C1 3E 00 00
#
name: Power
type: parsed
protocol: NECext
address: 87 7C 00 00
command: C8 37 00 00
#
name: Pause
type: parsed
protocol: NECext
address: 87 7C 00 00
command: 4A B5 00 00
#
name: Play
type: parsed
protocol: NECext
address: 87 7C 00 00
command: 01 FE 00 00
#
name: Prev
type: parsed
protocol: NECext
address: 87 7C 00 00
command: 05 FA 00 00
#
name: Next
type: parsed
protocol: NECext
address: 87 7C 00 00
command: 06 F9 00 00
#
name: Power
type: parsed
protocol: NECext
address: 12 34 00 00
command: 01 FE 00 00
#
name: Vol_up
type: parsed
protocol: NECext
address: 12 34 00 00
command: 0A F5 00 00
#
name: Vol_dn
type: parsed
protocol: NECext
address: 12 34 00 00
command: 0B F4 00 00
#
name: Mute
type: parsed
protocol: NECext
address: 12 34 00 00
command: 09 F6 00 00
#
name: Power
type: parsed
protocol: NEC
address: 01 00 00 00
command: 67 00 00 00
#
name: Next
type: parsed
protocol: NEC
address: 01 00 00 00
command: 38 00 00 00
#
name: Vol_up
type: parsed
protocol: NEC
address: 01 00 00 00
command: 3C 00 00 00
#
name: Vol_dn
type: parsed
protocol: NEC
address: 01 00 00 00
command: 3D 00 00 00
#
name: Mute
type: parsed
protocol: NEC
address: 01 00 00 00
command: 8C 00 00 00
#
name: Prev
type: parsed
protocol: NEC
address: 00 00 00 00
command: 44 00 00 00
#
name: Vol_dn
type: parsed
protocol: NEC
address: 00 00 00 00
command: 07 00 00 00
#
name: Vol_up
type: parsed
protocol: NEC
address: 00 00 00 00
command: 1C 00 00 00
#
name: Power
type: raw
frequency: 38000
duty_cycle: 0.33
data: 9120 4354 714 1539 660 497 633 499 631 500 630 503 628 503 628 503 629 503 628 503 628 1627 628 1627 628 1627 628 1627 628 1626 628 1627 628 1627 628 1627 628 1627 628 503 628 503 628 503 628 503 628 504 627 503 628 504 627 503 628 1627 628 1628 627 1628 627 1627 628 1627 628 1628 627 40094 9038 2194 628
#
name: Prev
type: parsed
protocol: NEC
address: 01 00 00 00
command: 5A 00 00 00
#
name: Next
type: parsed
protocol: NEC
address: 01 00 00 00
command: 5B 00 00 00
#
name: Pause
type: parsed
protocol: NEC
address: 01 00 00 00
command: B2 00 00 00
#
name: Pause
type: parsed
protocol: NECext
address: 00 EF 00 00
command: 11 EE 00 00
#
name: Pause
type: parsed
protocol: NEC
address: 01 00 00 00
command: 2C 00 00 00
#
name: Play
type: parsed
protocol: NEC
address: 01 00 00 00
command: B2 00 00 00
#
name: Play
type: parsed
protocol: NECext
address: 00 EF 00 00
command: 11 EE 00 00
#
name: Play
type: parsed
protocol: NEC
address: 01 00 00 00
command: 2C 00 00 00

View File

@@ -1,7 +1,7 @@
Filetype: IR library file Filetype: IR library file
Version: 1 Version: 1
#Last Updated 1st Oct, 2023 #Last Updated 21st Dec, 2023
#Last Checked 1st Oct, 2023 #Last Checked 21st Dec, 2023
# #
name: Power name: Power
type: raw type: raw
@@ -2103,3 +2103,39 @@ type: raw
frequency: 38000 frequency: 38000
duty_cycle: 0.33 duty_cycle: 0.33
data: 1330 376 1303 376 460 1192 1331 376 1302 376 459 1191 463 1217 462 1218 1331 376 459 1195 484 1219 460 7907 1300 379 1300 380 456 1223 1300 380 1300 380 456 1224 456 1224 456 1224 1299 380 456 1224 456 1224 456 8166 1299 380 1299 380 456 1224 1299 380 1299 380 456 1224 456 1224 455 1224 1299 380 456 1224 455 1224 456 7909 1299 380 1299 380 455 1224 1299 380 1299 380 456 1224 455 1225 455 1225 1298 381 455 1225 454 1225 455 data: 1330 376 1303 376 460 1192 1331 376 1302 376 459 1191 463 1217 462 1218 1331 376 459 1195 484 1219 460 7907 1300 379 1300 380 456 1223 1300 380 1300 380 456 1224 456 1224 456 1224 1299 380 456 1224 456 1224 456 8166 1299 380 1299 380 456 1224 1299 380 1299 380 456 1224 456 1224 455 1224 1299 380 456 1224 455 1224 456 7909 1299 380 1299 380 455 1224 1299 380 1299 380 456 1224 455 1225 455 1225 1298 381 455 1225 454 1225 455
#
name: Power
type: raw
frequency: 38000
duty_cycle: 0.33
data: 9213 4493 613 530 612 529 613 528 614 526 616 1631 615 1631 615 529 613 528 614 1631 615 1631 615 1632 614 1632 614 529 613 528 614 1632 614 1630 616 527 615 1631 615 528 614 1634 611 527 614 527 615 527 615 1630 616 1635 611 528 614 1632 614 527 615 1634 612 1631 615 1632 614 527 615 39730 9215 2228 617 95835 9215 2231 614
#
name: Speed_up
type: parsed
protocol: NEC
address: 30 00 00 00
command: 89 00 00 00
#
name: Timer
type: raw
frequency: 38000
duty_cycle: 0.33
data: 9279 4487 620 526 617 528 614 527 615 527 615 1633 612 1631 614 526 615 527 614 1634 611 1631 614 1633 612 1633 612 529 612 531 610 1633 612 1632 614 528 613 1630 615 1633 613 1632 613 526 615 529 613 527 615 1633 613 1632 614 526 615 526 616 527 615 1632 614 1631 614 1632 614 526 615 39731 9204 2230 614
#
name: Power
type: raw
frequency: 38000
duty_cycle: 0.33
data: 2227 839 757 1597 751 1599 749 829 757 815 750 817 748 814 751 806 749 866 751 836 750 832 754 823 753 819 757 811 754 807 758 799 756 103041 2229 837 749 1605 754 1597 751 826 750 823 753 815 750 812 753 804 751 864 753 834 752 830 756 821 755 818 758 809 756 806 749 808 757 64662 2229 774 749 1558 748 60749 2229 775 748 1559 758
#
name: Speed_up
type: raw
frequency: 38000
duty_cycle: 0.33
data: 2222 843 753 1626 732 1590 758 820 756 817 759 808 757 804 751 806 749 893 724 1603 755 827 748 1595 753 820 756 1578 759 1570 757 827 728 99745 2231 834 752 1601 757 1592 756 821 755 817 759 808 757 805 750 807 758 856 751 1630 728 827 749 1622 726 820 756 1605 732 1570 757 800 755
#
name: Speed_dn
type: raw
frequency: 38000
duty_cycle: 0.33
data: 2221 844 752 1600 758 1591 757 820 756 816 749 817 748 813 752 804 751 1632 758 1596 752 1597 751 1594 754 1585 752 1582 756 806 749 808 757 102464 2224 841 756 1597 751 1599 749 828 748 824 752 815 750 811 754 802 753 1629 750 1604 754 1595 753 1591 757 1583 755 1579 759 804 751 806 749

View File

@@ -1,7 +1,7 @@
Filetype: IR library file Filetype: IR library file
Version: 1 Version: 1
# Last Updated 1st Oct, 2023 # Last Updated 1st Oct, 2023
# Last Checked 1st Oct, 2023 # Last Checked 21st Dec, 2023
# #
# TEMP FIX FOR POWER # TEMP FIX FOR POWER
# #

View File

@@ -1,7 +1,7 @@
Filetype: IR library file Filetype: IR library file
Version: 1 Version: 1
# Last Updated 29th Oct, 2023 # Last Updated 21st Dec, 2023
# Last Checked 29th Oct, 2023 # Last Checked 21st Dec, 2023
# #
name: Power name: Power
type: parsed type: parsed
@@ -2459,3 +2459,9 @@ type: parsed
protocol: NEC protocol: NEC
address: 38 00 00 00 address: 38 00 00 00
command: 1C 00 00 00 command: 1C 00 00 00
#
name: Mute
type: parsed
protocol: NEC
address: 38 00 00 00
command: 04 00 00 00

View File

@@ -4,9 +4,8 @@ void infrared_scene_edit_delete_done_on_enter(void* context) {
InfraredApp* infrared = context; InfraredApp* infrared = context;
Popup* popup = infrared->popup; Popup* popup = infrared->popup;
popup_set_icon(popup, 0, 2, &I_DolphinMafia_115x62); popup_set_icon(popup, 0, 2, &I_DolphinMafia_119x62);
popup_set_header(popup, "Deleted", 83, 19, AlignLeft, AlignBottom); popup_set_header(popup, "Deleted", 80, 19, AlignLeft, AlignBottom);
popup_set_callback(popup, infrared_popup_closed_callback); popup_set_callback(popup, infrared_popup_closed_callback);
popup_set_context(popup, context); popup_set_context(popup, context);
popup_set_timeout(popup, 1500); popup_set_timeout(popup, 1500);

View File

@@ -4,9 +4,8 @@ void infrared_scene_edit_rename_done_on_enter(void* context) {
InfraredApp* infrared = context; InfraredApp* infrared = context;
Popup* popup = infrared->popup; Popup* popup = infrared->popup;
popup_set_icon(popup, 32, 5, &I_DolphinNice_96x59); popup_set_icon(popup, 36, 5, &I_DolphinSaved_92x58);
popup_set_header(popup, "Saved!", 5, 7, AlignLeft, AlignTop); popup_set_header(popup, "Saved", 15, 19, AlignLeft, AlignBottom);
popup_set_callback(popup, infrared_popup_closed_callback); popup_set_callback(popup, infrared_popup_closed_callback);
popup_set_context(popup, context); popup_set_context(popup, context);
popup_set_timeout(popup, 1500); popup_set_timeout(popup, 1500);

View File

@@ -4,12 +4,12 @@ void infrared_scene_learn_done_on_enter(void* context) {
InfraredApp* infrared = context; InfraredApp* infrared = context;
Popup* popup = infrared->popup; Popup* popup = infrared->popup;
popup_set_icon(popup, 32, 5, &I_DolphinNice_96x59);
if(infrared->app_state.is_learning_new_remote) { if(infrared->app_state.is_learning_new_remote) {
popup_set_icon(popup, 48, 6, &I_DolphinDone_80x58);
popup_set_header(popup, "New remote\ncreated!", 0, 0, AlignLeft, AlignTop); popup_set_header(popup, "New remote\ncreated!", 0, 0, AlignLeft, AlignTop);
} else { } else {
popup_set_header(popup, "Saved!", 5, 7, AlignLeft, AlignTop); popup_set_icon(popup, 36, 5, &I_DolphinSaved_92x58);
popup_set_header(popup, "Saved", 15, 19, AlignLeft, AlignBottom);
} }
popup_set_callback(popup, infrared_popup_closed_callback); popup_set_callback(popup, infrared_popup_closed_callback);

View File

@@ -54,11 +54,11 @@ static void infrared_progress_view_draw_callback(Canvas* canvas, void* _model) {
float progress_value = (float)model->progress / model->progress_total; float progress_value = (float)model->progress / model->progress_total;
elements_progress_bar(canvas, x + 4, y + 19, width - 7, progress_value); elements_progress_bar(canvas, x + 4, y + 19, width - 7, progress_value);
uint8_t percent_value = 100 * model->progress / model->progress_total; char number_string[10] = {0};
char percents_string[10] = {0}; snprintf(
snprintf(percents_string, sizeof(percents_string), "%d%%", percent_value); number_string, sizeof(number_string), "%d/%d", model->progress, model->progress_total);
elements_multiline_text_aligned( elements_multiline_text_aligned(
canvas, x + 33, y + 37, AlignCenter, AlignCenter, percents_string); canvas, x + 33, y + 37, AlignCenter, AlignCenter, number_string);
canvas_draw_icon(canvas, x + 14, y + height - 14, &I_Pin_back_arrow_10x8); canvas_draw_icon(canvas, x + 14, y + height - 14, &I_Pin_back_arrow_10x8);
canvas_draw_str(canvas, x + 30, y + height - 6, "= stop"); canvas_draw_str(canvas, x + 30, y + height - 6, "= stop");

View File

@@ -4,27 +4,9 @@
static void lfrfid_clear_t5577_password_and_config_to_EM(LfRfid* app) { static void lfrfid_clear_t5577_password_and_config_to_EM(LfRfid* app) {
Popup* popup = app->popup; Popup* popup = app->popup;
char curr_buf[32] = {}; char curr_buf[32] = {};
//TODO: use .txt file in resources for passwords.
const uint32_t default_passwords[] = { uint8_t default_passwords_len;
0x51243648, 0x000D8787, 0x19920427, 0x50524F58, 0xF9DCEBA0, 0x65857569, 0x05D73B9F, const uint32_t* default_passwords = t5577_get_default_passwords(&default_passwords_len);
0x89A69E60, 0x314159E0, 0xAA55BBBB, 0xA5B4C3D2, 0x1C0B5848, 0x00434343, 0x444E4752,
0x4E457854, 0x44B44CAE, 0x88661858, 0xE9920427, 0x575F4F4B, 0x50520901, 0x20206666,
0x65857569, 0x5469616E, 0x7686962A, 0xC0F5009A, 0x07CEE75D, 0xfeedbeef, 0xdeadc0de,
0x00000000, 0x11111111, 0x22222222, 0x33333333, 0x44444444, 0x55555555, 0x66666666,
0x77777777, 0x88888888, 0x99999999, 0xAAAAAAAA, 0xBBBBBBBB, 0xCCCCCCCC, 0xDDDDDDDD,
0xEEEEEEEE, 0xFFFFFFFF, 0xa0a1a2a3, 0xb0b1b2b3, 0x50415353, 0x00000001, 0x00000002,
0x0000000a, 0x0000000b, 0x01020304, 0x02030405, 0x03040506, 0x04050607, 0x05060708,
0x06070809, 0x0708090A, 0x08090A0B, 0x090A0B0C, 0x0A0B0C0D, 0x0B0C0D0E, 0x0C0D0E0F,
0x01234567, 0x12345678, 0x10000000, 0x20000000, 0x30000000, 0x40000000, 0x50000000,
0x60000000, 0x70000000, 0x80000000, 0x90000000, 0xA0000000, 0xB0000000, 0xC0000000,
0xD0000000, 0xE0000000, 0xF0000000, 0x10101010, 0x01010101, 0x11223344, 0x22334455,
0x33445566, 0x44556677, 0x55667788, 0x66778899, 0x778899AA, 0x8899AABB, 0x99AABBCC,
0xAABBCCDD, 0xBBCCDDEE, 0xCCDDEEFF, 0x0CB7E7FC, 0xFABADA11, 0x87654321, 0x12341234,
0x69696969, 0x12121212, 0x12344321, 0x1234ABCD, 0x11112222, 0x13131313, 0x10041004,
0x31415926, 0xabcd1234, 0x20002000, 0x19721972, 0xaa55aa55, 0x55aa55aa, 0x4f271149,
0x07d7bb0b, 0x9636ef8f, 0xb5f44686, 0x9E3779B9, 0xC6EF3720, 0x7854794A, 0xF1EA5EED,
0x69314718, 0x57721566, 0x93C467E3, 0x27182818, 0x50415353};
const uint8_t default_passwords_len = sizeof(default_passwords) / sizeof(uint32_t);
popup_set_header(popup, "Removing\npassword", 90, 36, AlignCenter, AlignCenter); popup_set_header(popup, "Removing\npassword", 90, 36, AlignCenter, AlignCenter);
popup_set_icon(popup, 0, 3, &I_RFIDDolphinSend_97x61); popup_set_icon(popup, 0, 3, &I_RFIDDolphinSend_97x61);
@@ -55,8 +37,8 @@ void lfrfid_scene_clear_t5577_on_enter(void* context) {
lfrfid_clear_t5577_password_and_config_to_EM(app); lfrfid_clear_t5577_password_and_config_to_EM(app);
notification_message(app->notifications, &sequence_success); notification_message(app->notifications, &sequence_success);
popup_set_header(popup, "Done!", 94, 10, AlignCenter, AlignTop); popup_set_header(popup, "Success!", 75, 10, AlignLeft, AlignTop);
popup_set_icon(popup, 0, 7, &I_RFIDDolphinSuccess_108x57); popup_set_icon(popup, 0, 9, &I_DolphinSuccess_91x55);
popup_set_context(popup, app); popup_set_context(popup, app);
popup_set_callback(popup, lfrfid_popup_timeout_callback); popup_set_callback(popup, lfrfid_popup_timeout_callback);
popup_set_timeout(popup, 1500); popup_set_timeout(popup, 1500);

View File

@@ -6,6 +6,7 @@ ADD_SCENE(lfrfid, exit_confirm, ExitConfirm)
ADD_SCENE(lfrfid, delete_confirm, DeleteConfirm) ADD_SCENE(lfrfid, delete_confirm, DeleteConfirm)
ADD_SCENE(lfrfid, read_key_menu, ReadKeyMenu) ADD_SCENE(lfrfid, read_key_menu, ReadKeyMenu)
ADD_SCENE(lfrfid, write, Write) ADD_SCENE(lfrfid, write, Write)
ADD_SCENE(lfrfid, write_with_pass, WriteWithPass)
ADD_SCENE(lfrfid, write_success, WriteSuccess) ADD_SCENE(lfrfid, write_success, WriteSuccess)
ADD_SCENE(lfrfid, emulate, Emulate) ADD_SCENE(lfrfid, emulate, Emulate)
ADD_SCENE(lfrfid, save_name, SaveName) ADD_SCENE(lfrfid, save_name, SaveName)

View File

@@ -4,8 +4,8 @@ void lfrfid_scene_delete_success_on_enter(void* context) {
LfRfid* app = context; LfRfid* app = context;
Popup* popup = app->popup; Popup* popup = app->popup;
popup_set_icon(popup, 0, 2, &I_DolphinMafia_115x62); popup_set_icon(popup, 0, 2, &I_DolphinMafia_119x62);
popup_set_header(popup, "Deleted", 83, 19, AlignLeft, AlignBottom); popup_set_header(popup, "Deleted", 80, 19, AlignLeft, AlignBottom);
popup_set_context(popup, app); popup_set_context(popup, app);
popup_set_callback(popup, lfrfid_popup_timeout_callback); popup_set_callback(popup, lfrfid_popup_timeout_callback);
popup_set_timeout(popup, 1500); popup_set_timeout(popup, 1500);

View File

@@ -7,8 +7,8 @@ void lfrfid_scene_save_success_on_enter(void* context) {
// Clear state of data enter scene // Clear state of data enter scene
scene_manager_set_scene_state(app->scene_manager, LfRfidSceneSaveData, 0); scene_manager_set_scene_state(app->scene_manager, LfRfidSceneSaveData, 0);
popup_set_icon(popup, 32, 5, &I_DolphinNice_96x59); popup_set_icon(popup, 36, 5, &I_DolphinSaved_92x58);
popup_set_header(popup, "Saved!", 5, 7, AlignLeft, AlignTop); popup_set_header(popup, "Saved", 15, 19, AlignLeft, AlignBottom);
popup_set_context(popup, app); popup_set_context(popup, app);
popup_set_callback(popup, lfrfid_popup_timeout_callback); popup_set_callback(popup, lfrfid_popup_timeout_callback);
popup_set_timeout(popup, 1500); popup_set_timeout(popup, 1500);

View File

@@ -4,6 +4,7 @@
typedef enum { typedef enum {
SubmenuIndexEmulate, SubmenuIndexEmulate,
SubmenuIndexWrite, SubmenuIndexWrite,
SubmenuIndexWriteWithPass,
SubmenuIndexEdit, SubmenuIndexEdit,
SubmenuIndexDelete, SubmenuIndexDelete,
SubmenuIndexInfo, SubmenuIndexInfo,
@@ -23,6 +24,12 @@ void lfrfid_scene_saved_key_menu_on_enter(void* context) {
submenu, "Emulate", SubmenuIndexEmulate, lfrfid_scene_saved_key_menu_submenu_callback, app); submenu, "Emulate", SubmenuIndexEmulate, lfrfid_scene_saved_key_menu_submenu_callback, app);
submenu_add_item( submenu_add_item(
submenu, "Write", SubmenuIndexWrite, lfrfid_scene_saved_key_menu_submenu_callback, app); submenu, "Write", SubmenuIndexWrite, lfrfid_scene_saved_key_menu_submenu_callback, app);
submenu_add_item(
submenu,
"Write with pass",
SubmenuIndexWriteWithPass,
lfrfid_scene_saved_key_menu_submenu_callback,
app);
submenu_add_item( submenu_add_item(
submenu, "Edit", SubmenuIndexEdit, lfrfid_scene_saved_key_menu_submenu_callback, app); submenu, "Edit", SubmenuIndexEdit, lfrfid_scene_saved_key_menu_submenu_callback, app);
submenu_add_item( submenu_add_item(
@@ -48,6 +55,9 @@ bool lfrfid_scene_saved_key_menu_on_event(void* context, SceneManagerEvent event
} else if(event.event == SubmenuIndexWrite) { } else if(event.event == SubmenuIndexWrite) {
scene_manager_next_scene(app->scene_manager, LfRfidSceneWrite); scene_manager_next_scene(app->scene_manager, LfRfidSceneWrite);
consumed = true; consumed = true;
} else if(event.event == SubmenuIndexWriteWithPass) {
scene_manager_next_scene(app->scene_manager, LfRfidSceneWriteWithPass);
consumed = true;
} else if(event.event == SubmenuIndexEdit) { } else if(event.event == SubmenuIndexEdit) {
scene_manager_next_scene(app->scene_manager, LfRfidSceneSaveData); scene_manager_next_scene(app->scene_manager, LfRfidSceneSaveData);
consumed = true; consumed = true;

View File

@@ -57,7 +57,7 @@ bool lfrfid_scene_write_on_event(void* context, SceneManagerEvent event) {
scene_manager_next_scene(app->scene_manager, LfRfidSceneWriteSuccess); scene_manager_next_scene(app->scene_manager, LfRfidSceneWriteSuccess);
consumed = true; consumed = true;
} else if(event.event == LfRfidEventWriteProtocolCannotBeWritten) { } else if(event.event == LfRfidEventWriteProtocolCannotBeWritten) {
popup_set_icon(popup, 72, 17, &I_DolphinCommon_56x48); popup_set_icon(popup, 83, 22, &I_WarningDolphinFlip_45x42);
popup_set_header(popup, "Error", 64, 3, AlignCenter, AlignTop); popup_set_header(popup, "Error", 64, 3, AlignCenter, AlignTop);
popup_set_text(popup, "This protocol\ncannot be written", 3, 17, AlignLeft, AlignTop); popup_set_text(popup, "This protocol\ncannot be written", 3, 17, AlignLeft, AlignTop);
notification_message(app->notifications, &sequence_blink_start_red); notification_message(app->notifications, &sequence_blink_start_red);
@@ -65,7 +65,7 @@ bool lfrfid_scene_write_on_event(void* context, SceneManagerEvent event) {
} else if( } else if(
(event.event == LfRfidEventWriteFobCannotBeWritten) || (event.event == LfRfidEventWriteFobCannotBeWritten) ||
(event.event == LfRfidEventWriteTooLongToWrite)) { (event.event == LfRfidEventWriteTooLongToWrite)) {
popup_set_icon(popup, 72, 17, &I_DolphinCommon_56x48); popup_set_icon(popup, 83, 22, &I_WarningDolphinFlip_45x42);
popup_set_header(popup, "Still trying to write...", 64, 3, AlignCenter, AlignTop); popup_set_header(popup, "Still trying to write...", 64, 3, AlignCenter, AlignTop);
popup_set_text( popup_set_text(
popup, popup,

View File

@@ -4,8 +4,8 @@ void lfrfid_scene_write_success_on_enter(void* context) {
LfRfid* app = context; LfRfid* app = context;
Popup* popup = app->popup; Popup* popup = app->popup;
popup_set_header(popup, "Successfully\nwritten!", 94, 3, AlignCenter, AlignTop); popup_set_header(popup, "Success!", 75, 10, AlignLeft, AlignTop);
popup_set_icon(popup, 0, 6, &I_RFIDDolphinSuccess_108x57); popup_set_icon(popup, 0, 9, &I_DolphinSuccess_91x55);
popup_set_context(popup, app); popup_set_context(popup, app);
popup_set_callback(popup, lfrfid_popup_timeout_callback); popup_set_callback(popup, lfrfid_popup_timeout_callback);
popup_set_timeout(popup, 1500); popup_set_timeout(popup, 1500);

View File

@@ -0,0 +1,94 @@
#include "../lfrfid_i.h"
static void lfrfid_write_with_pass_callback(LFRFIDWorkerWriteResult result, void* context) {
LfRfid* app = context;
uint32_t event = 0;
if(result == LFRFIDWorkerWriteOK) {
event = LfRfidEventWriteOK;
} else if(result == LFRFIDWorkerWriteProtocolCannotBeWritten) {
event = LfRfidEventWriteProtocolCannotBeWritten;
} else if(result == LFRFIDWorkerWriteFobCannotBeWritten) {
event = LfRfidEventWriteFobCannotBeWritten;
} else if(result == LFRFIDWorkerWriteTooLongToWrite) {
event = LfRfidEventWriteTooLongToWrite;
}
view_dispatcher_send_custom_event(app->view_dispatcher, event);
}
void lfrfid_scene_write_with_pass_on_enter(void* context) {
LfRfid* app = context;
Popup* popup = app->popup;
popup_set_header(popup, "Writing", 89, 30, AlignCenter, AlignTop);
if(!furi_string_empty(app->file_name)) {
popup_set_text(popup, furi_string_get_cstr(app->file_name), 89, 43, AlignCenter, AlignTop);
} else {
popup_set_text(
popup,
protocol_dict_get_name(app->dict, app->protocol_id),
89,
43,
AlignCenter,
AlignTop);
}
popup_set_icon(popup, 0, 3, &I_RFIDDolphinSend_97x61);
view_dispatcher_switch_to_view(app->view_dispatcher, LfRfidViewPopup);
size_t size = protocol_dict_get_data_size(app->dict, app->protocol_id);
protocol_dict_get_data(app->dict, app->protocol_id, app->old_key_data, size);
lfrfid_worker_start_thread(app->lfworker);
lfrfid_worker_write_with_pass_start(
app->lfworker, (LFRFIDProtocol)app->protocol_id, lfrfid_write_with_pass_callback, app);
notification_message(app->notifications, &sequence_blink_start_magenta);
}
bool lfrfid_scene_write_with_pass_on_event(void* context, SceneManagerEvent event) {
LfRfid* app = context;
Popup* popup = app->popup;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == LfRfidEventWriteOK) {
notification_message(app->notifications, &sequence_success);
scene_manager_next_scene(app->scene_manager, LfRfidSceneWriteSuccess);
consumed = true;
} else if(event.event == LfRfidEventWriteProtocolCannotBeWritten) {
popup_set_icon(popup, 83, 22, &I_WarningDolphinFlip_45x42);
popup_set_header(popup, "Error", 64, 3, AlignCenter, AlignTop);
popup_set_text(popup, "This protocol\ncannot be written", 3, 17, AlignLeft, AlignTop);
notification_message(app->notifications, &sequence_blink_start_red);
consumed = true;
} else if(
(event.event == LfRfidEventWriteFobCannotBeWritten) ||
(event.event == LfRfidEventWriteTooLongToWrite)) {
popup_set_icon(popup, 83, 22, &I_WarningDolphinFlip_45x42);
popup_set_header(popup, "Still trying to write...", 64, 3, AlignCenter, AlignTop);
popup_set_text(
popup,
"Make sure this\ncard is writable\nand not\nprotected.",
3,
17,
AlignLeft,
AlignTop);
notification_message(app->notifications, &sequence_blink_start_yellow);
consumed = true;
}
}
return consumed;
}
void lfrfid_scene_write_with_pass_on_exit(void* context) {
LfRfid* app = context;
notification_message(app->notifications, &sequence_blink_stop);
popup_reset(app->popup);
lfrfid_worker_stop(app->lfworker);
lfrfid_worker_stop_thread(app->lfworker);
size_t size = protocol_dict_get_data_size(app->dict, app->protocol_id);
protocol_dict_set_data(app->dict, app->protocol_id, app->old_key_data, size);
}

View File

@@ -13,7 +13,7 @@ App(
"!plugins", "!plugins",
"!nfc_cli.c", "!nfc_cli.c",
], ],
fap_libs=["assets"], fap_libs=["assets", "mbedtls"],
fap_icon="icon.png", fap_icon="icon.png",
fap_category="NFC", fap_category="NFC",
) )
@@ -56,6 +56,15 @@ App(
sources=["plugins/supported_cards/troika.c"], sources=["plugins/supported_cards/troika.c"],
) )
App(
appid="social_moscow_parser",
apptype=FlipperAppType.PLUGIN,
entry_point="social_moscow_plugin_ep",
targets=["f7"],
requires=["nfc"],
sources=["plugins/supported_cards/social_moscow.c"],
)
App( App(
appid="plantain_parser", appid="plantain_parser",
apptype=FlipperAppType.PLUGIN, apptype=FlipperAppType.PLUGIN,
@@ -74,6 +83,105 @@ App(
sources=["plugins/supported_cards/two_cities.c"], sources=["plugins/supported_cards/two_cities.c"],
) )
App(
appid="umarsh_parser",
apptype=FlipperAppType.PLUGIN,
entry_point="umarsh_plugin_ep",
targets=["f7"],
requires=["nfc"],
sources=["plugins/supported_cards/umarsh.c"],
)
App(
appid="metromoney_parser",
apptype=FlipperAppType.PLUGIN,
entry_point="metromoney_plugin_ep",
targets=["f7"],
requires=["nfc"],
sources=["plugins/supported_cards/metromoney.c"],
)
App(
appid="kazan_parser",
apptype=FlipperAppType.PLUGIN,
entry_point="kazan_plugin_ep",
targets=["f7"],
requires=["nfc"],
sources=["plugins/supported_cards/kazan.c"],
)
App(
appid="aime_parser",
apptype=FlipperAppType.PLUGIN,
entry_point="aime_plugin_ep",
targets=["f7"],
requires=["nfc"],
sources=["plugins/supported_cards/aime.c"],
)
App(
appid="saflok_parser",
apptype=FlipperAppType.PLUGIN,
entry_point="saflok_plugin_ep",
targets=["f7"],
requires=["nfc"],
sources=["plugins/supported_cards/saflok.c"],
)
App(
appid="mykey_parser",
apptype=FlipperAppType.PLUGIN,
entry_point="mykey_plugin_ep",
targets=["f7"],
requires=["nfc"],
sources=["plugins/supported_cards/mykey.c"],
)
App(
appid="zolotaya_korona_parser",
apptype=FlipperAppType.PLUGIN,
entry_point="zolotaya_korona_plugin_ep",
targets=["f7"],
requires=["nfc"],
sources=["plugins/supported_cards/zolotaya_korona.c"],
)
App(
appid="zolotaya_korona_online_parser",
apptype=FlipperAppType.PLUGIN,
entry_point="zolotaya_korona_online_plugin_ep",
targets=["f7"],
requires=["nfc"],
sources=["plugins/supported_cards/zolotaya_korona_online.c"],
)
App(
appid="hid_parser",
apptype=FlipperAppType.PLUGIN,
entry_point="hid_plugin_ep",
targets=["f7"],
requires=["nfc"],
sources=["plugins/supported_cards/hid.c"],
)
App(
appid="washcity_parser",
apptype=FlipperAppType.PLUGIN,
entry_point="washcity_plugin_ep",
targets=["f7"],
requires=["nfc"],
sources=["plugins/supported_cards/washcity.c"],
)
App(
appid="ndef_parser",
apptype=FlipperAppType.PLUGIN,
entry_point="ndef_plugin_ep",
targets=["f7"],
requires=["nfc"],
sources=["plugins/supported_cards/ndef.c"],
)
App( App(
appid="nfc_start", appid="nfc_start",
targets=["f7"], targets=["f7"],

View File

@@ -1,6 +1,6 @@
#include "mf_user_dict.h" #include "mf_user_dict.h"
#include <nfc/helpers/nfc_dict.h> #include <toolbox/keys_dict.h>
#include <nfc/protocols/mf_classic/mf_classic.h> #include <nfc/protocols/mf_classic/mf_classic.h>
#include <furi/furi.h> #include <furi/furi.h>
@@ -15,22 +15,22 @@ struct MfUserDict {
MfUserDict* mf_user_dict_alloc(size_t max_keys_to_load) { MfUserDict* mf_user_dict_alloc(size_t max_keys_to_load) {
MfUserDict* instance = malloc(sizeof(MfUserDict)); MfUserDict* instance = malloc(sizeof(MfUserDict));
NfcDict* dict = nfc_dict_alloc( KeysDict* dict = keys_dict_alloc(
NFC_APP_MF_CLASSIC_DICT_USER_PATH, NfcDictModeOpenAlways, sizeof(MfClassicKey)); NFC_APP_MF_CLASSIC_DICT_USER_PATH, KeysDictModeOpenAlways, sizeof(MfClassicKey));
furi_assert(dict); furi_assert(dict);
size_t dict_keys_num = nfc_dict_get_total_keys(dict); size_t dict_keys_num = keys_dict_get_total_keys(dict);
instance->keys_num = MIN(max_keys_to_load, dict_keys_num); instance->keys_num = MIN(max_keys_to_load, dict_keys_num);
if(instance->keys_num > 0) { if(instance->keys_num > 0) {
instance->keys_arr = malloc(instance->keys_num * sizeof(MfClassicKey)); instance->keys_arr = malloc(instance->keys_num * sizeof(MfClassicKey));
for(size_t i = 0; i < instance->keys_num; i++) { for(size_t i = 0; i < instance->keys_num; i++) {
bool key_loaded = bool key_loaded =
nfc_dict_get_next_key(dict, instance->keys_arr[i].data, sizeof(MfClassicKey)); keys_dict_get_next_key(dict, instance->keys_arr[i].data, sizeof(MfClassicKey));
furi_assert(key_loaded); furi_assert(key_loaded);
} }
} }
nfc_dict_free(dict); keys_dict_free(dict);
return instance; return instance;
} }
@@ -67,13 +67,13 @@ bool mf_user_dict_delete_key(MfUserDict* instance, uint32_t index) {
furi_assert(index < instance->keys_num); furi_assert(index < instance->keys_num);
furi_assert(instance->keys_arr); furi_assert(instance->keys_arr);
NfcDict* dict = nfc_dict_alloc( KeysDict* dict = keys_dict_alloc(
NFC_APP_MF_CLASSIC_DICT_USER_PATH, NfcDictModeOpenAlways, sizeof(MfClassicKey)); NFC_APP_MF_CLASSIC_DICT_USER_PATH, KeysDictModeOpenAlways, sizeof(MfClassicKey));
furi_assert(dict); furi_assert(dict);
bool key_delete_success = bool key_delete_success =
nfc_dict_delete_key(dict, instance->keys_arr[index].data, sizeof(MfClassicKey)); keys_dict_delete_key(dict, instance->keys_arr[index].data, sizeof(MfClassicKey));
nfc_dict_free(dict); keys_dict_free(dict);
if(key_delete_success) { if(key_delete_success) {
instance->keys_num--; instance->keys_num--;

View File

@@ -1,4 +1,5 @@
#include "nfc_supported_cards.h" #include "nfc_supported_cards.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>
@@ -7,22 +8,72 @@
#include <furi.h> #include <furi.h>
#include <path.h> #include <path.h>
#include <m-array.h>
#define TAG "NfcSupportedCards" #define TAG "NfcSupportedCards"
#define NFC_SUPPORTED_CARDS_PLUGINS_PATH APP_DATA_PATH("plugins") #define NFC_SUPPORTED_CARDS_PLUGINS_PATH APP_DATA_PATH("plugins")
#define NFC_SUPPORTED_CARDS_PLUGIN_SUFFIX "_parser.fal" #define NFC_SUPPORTED_CARDS_PLUGIN_SUFFIX "_parser.fal"
typedef enum {
NfcSupportedCardsPluginFeatureHasVerify = (1U << 0),
NfcSupportedCardsPluginFeatureHasRead = (1U << 1),
NfcSupportedCardsPluginFeatureHasParse = (1U << 2),
} NfcSupportedCardsPluginFeature;
typedef struct {
FuriString* path;
NfcProtocol protocol;
NfcSupportedCardsPluginFeature feature;
} NfcSupportedCardsPluginCache;
ARRAY_DEF(NfcSupportedCardsPluginCache, NfcSupportedCardsPluginCache, M_POD_OPLIST);
typedef enum {
NfcSupportedCardsLoadStateIdle,
NfcSupportedCardsLoadStateInProgress,
NfcSupportedCardsLoadStateSuccess,
NfcSupportedCardsLoadStateFail,
} NfcSupportedCardsLoadState;
typedef struct { typedef struct {
Storage* storage; Storage* storage;
File* directory; File* directory;
FuriString* file_path; FuriString* file_path;
char file_name[256]; char file_name[256];
FlipperApplication* app; FlipperApplication* app;
} NfcSupportedCards; } NfcSupportedCardsLoadContext;
static NfcSupportedCards* nfc_supported_cards_alloc() { struct NfcSupportedCards {
NfcSupportedCardsPluginCache_t plugins_cache_arr;
NfcSupportedCardsLoadState load_state;
NfcSupportedCardsLoadContext* load_context;
};
NfcSupportedCards* nfc_supported_cards_alloc() {
NfcSupportedCards* instance = malloc(sizeof(NfcSupportedCards)); NfcSupportedCards* instance = malloc(sizeof(NfcSupportedCards));
NfcSupportedCardsPluginCache_init(instance->plugins_cache_arr);
return instance;
}
void nfc_supported_cards_free(NfcSupportedCards* instance) {
furi_assert(instance);
NfcSupportedCardsPluginCache_it_t iter;
for(NfcSupportedCardsPluginCache_it(iter, instance->plugins_cache_arr);
!NfcSupportedCardsPluginCache_end_p(iter);
NfcSupportedCardsPluginCache_next(iter)) {
NfcSupportedCardsPluginCache* plugin_cache = NfcSupportedCardsPluginCache_ref(iter);
furi_string_free(plugin_cache->path);
}
NfcSupportedCardsPluginCache_clear(instance->plugins_cache_arr);
free(instance);
}
static NfcSupportedCardsLoadContext* nfc_supported_cards_load_context_alloc() {
NfcSupportedCardsLoadContext* instance = malloc(sizeof(NfcSupportedCardsLoadContext));
instance->storage = furi_record_open(RECORD_STORAGE); instance->storage = furi_record_open(RECORD_STORAGE);
instance->directory = storage_file_alloc(instance->storage); instance->directory = storage_file_alloc(instance->storage);
@@ -35,7 +86,7 @@ static NfcSupportedCards* nfc_supported_cards_alloc() {
return instance; return instance;
} }
static void nfc_supported_cards_free(NfcSupportedCards* instance) { static void nfc_supported_cards_load_context_free(NfcSupportedCardsLoadContext* instance) {
if(instance->app) { if(instance->app) {
flipper_application_free(instance->app); flipper_application_free(instance->app);
} }
@@ -50,7 +101,36 @@ static void nfc_supported_cards_free(NfcSupportedCards* instance) {
} }
static const NfcSupportedCardsPlugin* static const NfcSupportedCardsPlugin*
nfc_supported_cards_get_next_plugin(NfcSupportedCards* instance) { nfc_supported_cards_get_plugin(NfcSupportedCardsLoadContext* instance, FuriString* path) {
furi_assert(instance);
furi_assert(path);
const NfcSupportedCardsPlugin* plugin = NULL;
do {
if(instance->app) flipper_application_free(instance->app);
instance->app = flipper_application_alloc(instance->storage, firmware_api_interface);
if(flipper_application_preload(instance->app, furi_string_get_cstr(path)) !=
FlipperApplicationPreloadStatusSuccess)
break;
if(!flipper_application_is_plugin(instance->app)) break;
if(flipper_application_map_to_memory(instance->app) != FlipperApplicationLoadStatusSuccess)
break;
const FlipperAppPluginDescriptor* descriptor =
flipper_application_plugin_get_descriptor(instance->app);
if(descriptor == NULL) break;
if(strcmp(descriptor->appid, NFC_SUPPORTED_CARD_PLUGIN_APP_ID) != 0) break;
if(descriptor->ep_api_version != NFC_SUPPORTED_CARD_PLUGIN_API_VERSION) break;
plugin = descriptor->entry_point;
} while(false);
return plugin;
}
static const NfcSupportedCardsPlugin*
nfc_supported_cards_get_next_plugin(NfcSupportedCardsLoadContext* instance) {
const NfcSupportedCardsPlugin* plugin = NULL; const NfcSupportedCardsPlugin* plugin = NULL;
do { do {
@@ -65,83 +145,137 @@ static const NfcSupportedCardsPlugin*
path_concat(NFC_SUPPORTED_CARDS_PLUGINS_PATH, instance->file_name, instance->file_path); path_concat(NFC_SUPPORTED_CARDS_PLUGINS_PATH, instance->file_name, instance->file_path);
if(instance->app) flipper_application_free(instance->app); plugin = nfc_supported_cards_get_plugin(instance, instance->file_path);
instance->app = flipper_application_alloc(instance->storage, firmware_api_interface);
if(flipper_application_preload(instance->app, furi_string_get_cstr(instance->file_path)) !=
FlipperApplicationPreloadStatusSuccess)
continue;
if(!flipper_application_is_plugin(instance->app)) continue;
if(flipper_application_map_to_memory(instance->app) != FlipperApplicationLoadStatusSuccess)
continue;
const FlipperAppPluginDescriptor* descriptor =
flipper_application_plugin_get_descriptor(instance->app);
if(descriptor == NULL) continue;
if(strcmp(descriptor->appid, NFC_SUPPORTED_CARD_PLUGIN_APP_ID) != 0) continue;
if(descriptor->ep_api_version != NFC_SUPPORTED_CARD_PLUGIN_API_VERSION) continue;
plugin = descriptor->entry_point;
} while(plugin == NULL); //-V654 } while(plugin == NULL); //-V654
return plugin; return plugin;
} }
bool nfc_supported_cards_read(NfcDevice* device, Nfc* nfc) { void nfc_supported_cards_load_cache(NfcSupportedCards* instance) {
furi_assert(instance);
do {
if((instance->load_state == NfcSupportedCardsLoadStateSuccess) ||
(instance->load_state == NfcSupportedCardsLoadStateFail))
break;
instance->load_context = nfc_supported_cards_load_context_alloc();
while(true) {
const NfcSupportedCardsPlugin* plugin =
nfc_supported_cards_get_next_plugin(instance->load_context);
if(plugin == NULL) break; //-V547
NfcSupportedCardsPluginCache plugin_cache = {}; //-V779
plugin_cache.path = furi_string_alloc_set(instance->load_context->file_path);
plugin_cache.protocol = plugin->protocol;
if(plugin->verify) {
plugin_cache.feature |= NfcSupportedCardsPluginFeatureHasVerify;
}
if(plugin->read) {
plugin_cache.feature |= NfcSupportedCardsPluginFeatureHasRead;
}
if(plugin->parse) {
plugin_cache.feature |= NfcSupportedCardsPluginFeatureHasParse;
}
NfcSupportedCardsPluginCache_push_back(instance->plugins_cache_arr, plugin_cache);
}
nfc_supported_cards_load_context_free(instance->load_context);
size_t plugins_loaded = NfcSupportedCardsPluginCache_size(instance->plugins_cache_arr);
if(plugins_loaded == 0) {
FURI_LOG_D(TAG, "Plugins not found");
instance->load_state = NfcSupportedCardsLoadStateFail;
} else {
FURI_LOG_D(TAG, "Loaded %zu plugins", plugins_loaded);
instance->load_state = NfcSupportedCardsLoadStateSuccess;
}
} while(false);
}
bool nfc_supported_cards_read(NfcSupportedCards* instance, NfcDevice* device, Nfc* nfc) {
furi_assert(instance);
furi_assert(device); furi_assert(device);
furi_assert(nfc); furi_assert(nfc);
bool card_read = false; bool card_read = false;
NfcProtocol protocol = nfc_device_get_protocol(device);
NfcSupportedCards* supported_cards = nfc_supported_cards_alloc();
do { do {
const NfcSupportedCardsPlugin* plugin = if(instance->load_state != NfcSupportedCardsLoadStateSuccess) break;
nfc_supported_cards_get_next_plugin(supported_cards);
if(plugin == NULL) break; //-V547
const NfcProtocol protocol = nfc_device_get_protocol(device); //-V779 instance->load_context = nfc_supported_cards_load_context_alloc();
if(plugin->protocol != protocol) continue;
NfcSupportedCardsPluginCache_it_t iter;
for(NfcSupportedCardsPluginCache_it(iter, instance->plugins_cache_arr);
!NfcSupportedCardsPluginCache_end_p(iter);
NfcSupportedCardsPluginCache_next(iter)) {
NfcSupportedCardsPluginCache* plugin_cache = NfcSupportedCardsPluginCache_ref(iter);
if(plugin_cache->protocol != protocol) continue;
if((plugin_cache->feature & NfcSupportedCardsPluginFeatureHasRead) == 0) continue;
const NfcSupportedCardsPlugin* plugin =
nfc_supported_cards_get_plugin(instance->load_context, plugin_cache->path);
if(plugin == NULL) continue;
if(plugin->verify) { if(plugin->verify) {
if(!plugin->verify(nfc)) continue; if(!plugin->verify(nfc)) continue;
} }
if(plugin->read) { if(plugin->read) {
card_read = plugin->read(nfc, device); if(plugin->read(nfc, device)) {
card_read = true;
break;
}
}
} }
} while(!card_read); nfc_supported_cards_load_context_free(instance->load_context);
} while(false);
nfc_supported_cards_free(supported_cards);
return card_read; return card_read;
} }
bool nfc_supported_cards_parse(const NfcDevice* device, FuriString* parsed_data) { bool nfc_supported_cards_parse(
NfcSupportedCards* instance,
NfcDevice* device,
FuriString* parsed_data) {
furi_assert(instance);
furi_assert(device); furi_assert(device);
furi_assert(parsed_data); furi_assert(parsed_data);
bool parsed = false; bool card_parsed = false;
NfcProtocol protocol = nfc_device_get_protocol(device);
NfcSupportedCards* supported_cards = nfc_supported_cards_alloc();
do { do {
const NfcSupportedCardsPlugin* plugin = if(instance->load_state != NfcSupportedCardsLoadStateSuccess) break;
nfc_supported_cards_get_next_plugin(supported_cards);
if(plugin == NULL) break; //-V547
const NfcProtocol protocol = nfc_device_get_protocol(device); //-V779 instance->load_context = nfc_supported_cards_load_context_alloc();
if(plugin->protocol != protocol) continue;
NfcSupportedCardsPluginCache_it_t iter;
for(NfcSupportedCardsPluginCache_it(iter, instance->plugins_cache_arr);
!NfcSupportedCardsPluginCache_end_p(iter);
NfcSupportedCardsPluginCache_next(iter)) {
NfcSupportedCardsPluginCache* plugin_cache = NfcSupportedCardsPluginCache_ref(iter);
if(plugin_cache->protocol != protocol) continue;
if((plugin_cache->feature & NfcSupportedCardsPluginFeatureHasParse) == 0) continue;
const NfcSupportedCardsPlugin* plugin =
nfc_supported_cards_get_plugin(instance->load_context, plugin_cache->path);
if(plugin == NULL) continue;
if(plugin->parse) { if(plugin->parse) {
parsed = plugin->parse(device, parsed_data); if(plugin->parse(device, parsed_data)) {
card_parsed = true;
break;
}
}
} }
} while(!parsed); nfc_supported_cards_load_context_free(instance->load_context);
} while(false);
nfc_supported_cards_free(supported_cards); return card_parsed;
return parsed;
} }

View File

@@ -15,6 +15,34 @@
extern "C" { extern "C" {
#endif #endif
/**
* @brief NfcSupportedCards opaque type definition.
*/
typedef struct NfcSupportedCards NfcSupportedCards;
/**
* @brief Allocate NfcSupportedCards instance.
*
* @return pointer to allocated NfcSupportedCards instance.
*/
NfcSupportedCards* nfc_supported_cards_alloc();
/**
* @brief Delete an NfcSupportedCards instance
*
* @param[in] instance pointer to instance to be deleted.
*/
void nfc_supported_cards_free(NfcSupportedCards* instance);
/**
* @brief Load plugins information to cache.
*
* @note This function must be called before calling read and parse fanctions.
*
* @param[in, out] instance pointer to NfcSupportedCards instance.
*/
void nfc_supported_cards_load_cache(NfcSupportedCards* instance);
/** /**
* @brief Read the card using a custom procedure. * @brief Read the card using a custom procedure.
* *
@@ -22,13 +50,14 @@ extern "C" {
* try to execute the custom read procedure specified in each. Upon first success, * try to execute the custom read procedure specified in each. Upon first success,
* no further attempts will be made and the function will return. * no further attempts will be made and the function will return.
* *
* @param[in, out] instance pointer to NfcSupportedCards instance.
* @param[in,out] device pointer to a device instance to hold the read data. * @param[in,out] device pointer to a device instance to hold the read data.
* @param[in,out] nfc pointer to an Nfc instance. * @param[in,out] nfc pointer to an Nfc instance.
* @returns true if the card was successfully read, false otherwise. * @returns true if the card was successfully read, false otherwise.
* *
* @see NfcSupportedCardPluginRead for detailed description. * @see NfcSupportedCardPluginRead for detailed description.
*/ */
bool nfc_supported_cards_read(NfcDevice* device, Nfc* nfc); bool nfc_supported_cards_read(NfcSupportedCards* instance, NfcDevice* device, Nfc* nfc);
/** /**
* @brief Parse raw data into human-readable representation. * @brief Parse raw data into human-readable representation.
@@ -37,13 +66,17 @@ bool nfc_supported_cards_read(NfcDevice* device, Nfc* nfc);
* try to parse the data according to each implementation. Upon first success, * try to parse the data according to each implementation. Upon first success,
* no further attempts will be made and the function will return. * no further attempts will be made and the function will return.
* *
* @param[in, out] instance pointer to NfcSupportedCards instance.
* @param[in] device pointer to a device instance holding the data is to be parsed. * @param[in] device pointer to a device instance holding the data is to be parsed.
* @param[out] parsed_data pointer to the string to contain the formatted result. * @param[out] parsed_data pointer to the string to contain the formatted result.
* @returns true if the card was successfully parsed, false otherwise. * @returns true if the card was successfully parsed, false otherwise.
* *
* @see NfcSupportedCardPluginParse for detailed description. * @see NfcSupportedCardPluginParse for detailed description.
*/ */
bool nfc_supported_cards_parse(const NfcDevice* device, FuriString* parsed_data); bool nfc_supported_cards_parse(
NfcSupportedCards* instance,
NfcDevice* device,
FuriString* parsed_data);
#ifdef __cplusplus #ifdef __cplusplus
} }

View File

@@ -67,8 +67,14 @@ static bool nfc_scene_saved_menu_on_event_felica(NfcApp* instance, uint32_t even
return false; return false;
} }
static void nfc_scene_emulate_on_enter_felica(NfcApp* instance) {
const FelicaData* data = nfc_device_get_data(instance->nfc_device, NfcProtocolFelica);
instance->listener = nfc_listener_alloc(instance->nfc, NfcProtocolFelica, data);
nfc_listener_start(instance->listener, NULL, NULL);
}
const NfcProtocolSupportBase nfc_protocol_support_felica = { const NfcProtocolSupportBase nfc_protocol_support_felica = {
.features = NfcProtocolFeatureNone, .features = NfcProtocolFeatureEmulateUid,
.scene_info = .scene_info =
{ {
@@ -102,7 +108,7 @@ const NfcProtocolSupportBase nfc_protocol_support_felica = {
}, },
.scene_emulate = .scene_emulate =
{ {
.on_enter = nfc_protocol_support_common_on_enter_empty, .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,
}, },
}; };

View File

@@ -12,6 +12,7 @@ enum {
SubmenuIndexUnlock = SubmenuIndexCommonMax, SubmenuIndexUnlock = SubmenuIndexCommonMax,
SubmenuIndexUnlockByReader, SubmenuIndexUnlockByReader,
SubmenuIndexUnlockByPassword, SubmenuIndexUnlockByPassword,
SubmenuIndexWrite,
}; };
static void nfc_scene_info_on_enter_mf_ultralight(NfcApp* instance) { static void nfc_scene_info_on_enter_mf_ultralight(NfcApp* instance) {
@@ -51,9 +52,15 @@ static NfcCommand
if(mf_ultralight_event->type == MfUltralightPollerEventTypeReadSuccess) { if(mf_ultralight_event->type == MfUltralightPollerEventTypeReadSuccess) {
nfc_device_set_data( nfc_device_set_data(
instance->nfc_device, NfcProtocolMfUltralight, nfc_poller_get_data(instance->poller)); instance->nfc_device, NfcProtocolMfUltralight, nfc_poller_get_data(instance->poller));
view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventPollerSuccess);
const MfUltralightData* data =
nfc_device_get_data(instance->nfc_device, NfcProtocolMfUltralight);
uint32_t event = (data->pages_read == data->pages_total) ? NfcCustomEventPollerSuccess :
NfcCustomEventPollerIncomplete;
view_dispatcher_send_custom_event(instance->view_dispatcher, event);
return NfcCommandStop; return NfcCommandStop;
} else if(mf_ultralight_event->type == MfUltralightPollerEventTypeAuthRequest) { } else if(mf_ultralight_event->type == MfUltralightPollerEventTypeAuthRequest) {
view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventCardDetected);
nfc_device_set_data( nfc_device_set_data(
instance->nfc_device, NfcProtocolMfUltralight, nfc_poller_get_data(instance->poller)); instance->nfc_device, NfcProtocolMfUltralight, nfc_poller_get_data(instance->poller));
const MfUltralightData* data = const MfUltralightData* data =
@@ -89,10 +96,55 @@ static NfcCommand
return NfcCommandContinue; return NfcCommandContinue;
} }
enum {
NfcSceneMfUltralightReadMenuStateCardSearch,
NfcSceneMfUltralightReadMenuStateCardFound,
};
static void nfc_scene_read_setup_view(NfcApp* instance) {
Popup* popup = instance->popup;
popup_reset(popup);
uint32_t state = scene_manager_get_scene_state(instance->scene_manager, NfcSceneRead);
if(state == NfcSceneMfUltralightReadMenuStateCardSearch) {
popup_set_icon(instance->popup, 0, 8, &I_NFC_manual_60x50);
popup_set_header(instance->popup, "Unlocking", 97, 15, AlignCenter, AlignTop);
popup_set_text(
instance->popup, "Apply card to\nFlipper's back", 97, 27, AlignCenter, AlignTop);
} else {
popup_set_header(instance->popup, "Don't move", 85, 27, AlignCenter, AlignTop);
popup_set_icon(instance->popup, 12, 20, &A_Loading_24);
}
view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewPopup);
}
static void nfc_scene_read_on_enter_mf_ultralight(NfcApp* instance) { static void nfc_scene_read_on_enter_mf_ultralight(NfcApp* instance) {
bool unlocking =
scene_manager_has_previous_scene(instance->scene_manager, NfcSceneMfUltralightUnlockWarn);
uint32_t state = unlocking ? NfcSceneMfUltralightReadMenuStateCardSearch :
NfcSceneMfUltralightReadMenuStateCardFound;
scene_manager_set_scene_state(instance->scene_manager, NfcSceneRead, state);
nfc_scene_read_setup_view(instance);
nfc_poller_start(instance->poller, nfc_scene_read_poller_callback_mf_ultralight, instance); nfc_poller_start(instance->poller, nfc_scene_read_poller_callback_mf_ultralight, instance);
} }
bool nfc_scene_read_on_event_mf_ultralight(NfcApp* instance, uint32_t event) {
if(event == NfcCustomEventCardDetected) {
scene_manager_set_scene_state(
instance->scene_manager, NfcSceneRead, NfcSceneMfUltralightReadMenuStateCardFound);
nfc_scene_read_setup_view(instance);
} else if((event == NfcCustomEventPollerIncomplete)) {
notification_message(instance->notifications, &sequence_semi_success);
scene_manager_next_scene(instance->scene_manager, NfcSceneReadSuccess);
dolphin_deed(DolphinDeedNfcReadSuccess);
}
return true;
}
static void nfc_scene_read_and_saved_menu_on_enter_mf_ultralight(NfcApp* instance) { static void nfc_scene_read_and_saved_menu_on_enter_mf_ultralight(NfcApp* instance) {
Submenu* submenu = instance->submenu; Submenu* submenu = instance->submenu;
@@ -106,6 +158,15 @@ static void nfc_scene_read_and_saved_menu_on_enter_mf_ultralight(NfcApp* instanc
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) {
submenu_add_item(
submenu,
"Write",
SubmenuIndexWrite,
nfc_protocol_support_common_submenu_callback,
instance);
} }
} }
@@ -146,6 +207,9 @@ static bool
if(event == SubmenuIndexUnlock) { if(event == SubmenuIndexUnlock) {
scene_manager_next_scene(instance->scene_manager, NfcSceneMfUltralightUnlockMenu); scene_manager_next_scene(instance->scene_manager, NfcSceneMfUltralightUnlockMenu);
return true; return true;
} else if(event == SubmenuIndexWrite) {
scene_manager_next_scene(instance->scene_manager, NfcSceneMfUltralightWrite);
return true;
} }
return false; return false;
} }
@@ -166,7 +230,7 @@ const NfcProtocolSupportBase nfc_protocol_support_mf_ultralight = {
.scene_read = .scene_read =
{ {
.on_enter = nfc_scene_read_on_enter_mf_ultralight, .on_enter = nfc_scene_read_on_enter_mf_ultralight,
.on_event = nfc_protocol_support_common_on_event_empty, .on_event = nfc_scene_read_on_event_mf_ultralight,
}, },
.scene_read_menu = .scene_read_menu =
{ {

View File

@@ -8,7 +8,6 @@
#include "nfc_protocol_support.h" #include "nfc_protocol_support.h"
#include "nfc/nfc_app_i.h" #include "nfc/nfc_app_i.h"
#include "nfc/helpers/nfc_supported_cards.h"
#include "nfc_protocol_support_defs.h" #include "nfc_protocol_support_defs.h"
#include "nfc_protocol_support_gui_common.h" #include "nfc_protocol_support_gui_common.h"
@@ -147,8 +146,7 @@ static void nfc_protocol_support_scene_more_info_on_exit(NfcApp* instance) {
// SceneRead // SceneRead
static void nfc_protocol_support_scene_read_on_enter(NfcApp* instance) { static void nfc_protocol_support_scene_read_on_enter(NfcApp* instance) {
popup_set_header( popup_set_header(instance->popup, "Don't move", 85, 27, AlignCenter, AlignTop);
instance->popup, "Reading card\nDon't move...", 85, 24, AlignCenter, AlignTop);
popup_set_icon(instance->popup, 12, 23, &A_Loading_24); popup_set_icon(instance->popup, 12, 23, &A_Loading_24);
view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewPopup); view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewPopup);
@@ -157,11 +155,13 @@ static void nfc_protocol_support_scene_read_on_enter(NfcApp* instance) {
instance->protocols_detected[instance->protocols_detected_selected_idx]; instance->protocols_detected[instance->protocols_detected_selected_idx];
instance->poller = nfc_poller_alloc(instance->nfc, protocol); instance->poller = nfc_poller_alloc(instance->nfc, protocol);
view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewPopup);
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[protocol]->scene_read.on_enter(instance);
view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewPopup);
nfc_blink_detect_start(instance); nfc_blink_read_start(instance);
} }
static bool nfc_protocol_support_scene_read_on_event(NfcApp* instance, SceneManagerEvent event) { static bool nfc_protocol_support_scene_read_on_event(NfcApp* instance, SceneManagerEvent event) {
@@ -178,7 +178,8 @@ static bool nfc_protocol_support_scene_read_on_event(NfcApp* instance, SceneMana
} else if(event.event == NfcCustomEventPollerIncomplete) { } else if(event.event == NfcCustomEventPollerIncomplete) {
nfc_poller_stop(instance->poller); nfc_poller_stop(instance->poller);
nfc_poller_free(instance->poller); nfc_poller_free(instance->poller);
bool card_read = nfc_supported_cards_read(instance->nfc_device, instance->nfc); bool card_read = nfc_supported_cards_read(
instance->nfc_supported_cards, instance->nfc_device, instance->nfc);
if(card_read) { if(card_read) {
notification_message(instance->notifications, &sequence_success); notification_message(instance->notifications, &sequence_success);
scene_manager_next_scene(instance->scene_manager, NfcSceneReadSuccess); scene_manager_next_scene(instance->scene_manager, NfcSceneReadSuccess);
@@ -198,6 +199,10 @@ static bool nfc_protocol_support_scene_read_on_event(NfcApp* instance, SceneMana
instance->scene_manager, NfcSceneDetect); instance->scene_manager, NfcSceneDetect);
} }
consumed = true; consumed = true;
} else if(event.event == NfcCustomEventCardDetected) {
const NfcProtocol protocol =
instance->protocols_detected[instance->protocols_detected_selected_idx];
consumed = nfc_protocol_support[protocol]->scene_read.on_event(instance, event.event);
} }
} else if(event.type == SceneManagerEventTypeBack) { } else if(event.type == SceneManagerEventTypeBack) {
nfc_poller_stop(instance->poller); nfc_poller_stop(instance->poller);
@@ -303,7 +308,7 @@ static void nfc_protocol_support_scene_read_success_on_enter(NfcApp* instance) {
Widget* widget = instance->widget; Widget* widget = instance->widget;
FuriString* temp_str = furi_string_alloc(); FuriString* temp_str = furi_string_alloc();
if(nfc_supported_cards_parse(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 {
@@ -579,6 +584,7 @@ static void nfc_protocol_support_scene_emulate_on_enter(NfcApp* instance) {
widget_add_string_element(widget, 90, 13, AlignCenter, AlignTop, FontPrimary, "Emulating"); widget_add_string_element(widget, 90, 13, AlignCenter, AlignTop, FontPrimary, "Emulating");
furi_string_set( furi_string_set(
temp_str, nfc_device_get_name(instance->nfc_device, NfcDeviceNameTypeFull)); temp_str, nfc_device_get_name(instance->nfc_device, NfcDeviceNameTypeFull));
furi_string_cat_printf(temp_str, "\n%s", furi_string_get_cstr(instance->file_name));
} }
widget_add_text_box_element( widget_add_text_box_element(

View File

@@ -29,7 +29,9 @@ static NfcCommand nfc_scene_read_poller_callback_st25tb(NfcGenericEvent event, v
NfcApp* instance = context; NfcApp* instance = context;
const St25tbPollerEvent* st25tb_event = event.event_data; const St25tbPollerEvent* st25tb_event = event.event_data;
if(st25tb_event->type == St25tbPollerEventTypeReady) { if(st25tb_event->type == St25tbPollerEventTypeRequestMode) {
st25tb_event->data->mode_request.mode = St25tbPollerModeRead;
} else if(st25tb_event->type == St25tbPollerEventTypeSuccess) {
nfc_device_set_data( nfc_device_set_data(
instance->nfc_device, NfcProtocolSt25tb, nfc_poller_get_data(instance->poller)); instance->nfc_device, NfcProtocolSt25tb, nfc_poller_get_data(instance->poller));
view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventPollerSuccess); view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventPollerSuccess);

View File

@@ -51,6 +51,7 @@ NfcApp* nfc_app_alloc() {
instance->mf_ul_auth = mf_ultralight_auth_alloc(); instance->mf_ul_auth = mf_ultralight_auth_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();
// Nfc device // Nfc device
instance->nfc_device = nfc_device_alloc(); instance->nfc_device = nfc_device_alloc();
@@ -110,7 +111,6 @@ NfcApp* nfc_app_alloc() {
instance->view_dispatcher, NfcViewWidget, widget_get_view(instance->widget)); instance->view_dispatcher, NfcViewWidget, widget_get_view(instance->widget));
// Dict attack // Dict attack
instance->dict_attack = dict_attack_alloc(); instance->dict_attack = dict_attack_alloc();
view_dispatcher_add_view( view_dispatcher_add_view(
instance->view_dispatcher, NfcViewDictAttack, dict_attack_get_view(instance->dict_attack)); instance->view_dispatcher, NfcViewDictAttack, dict_attack_get_view(instance->dict_attack));
@@ -141,6 +141,7 @@ void nfc_app_free(NfcApp* instance) {
mf_ultralight_auth_free(instance->mf_ul_auth); mf_ultralight_auth_free(instance->mf_ul_auth);
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 device // Nfc device
nfc_device_free(instance->nfc_device); nfc_device_free(instance->nfc_device);
@@ -222,7 +223,7 @@ void nfc_text_store_clear(NfcApp* nfc) {
} }
void nfc_blink_read_start(NfcApp* nfc) { void nfc_blink_read_start(NfcApp* nfc) {
notification_message(nfc->notifications, &sequence_blink_start_cyan); notification_message(nfc->notifications, &sequence_blink_start_yellow);
} }
void nfc_blink_emulate_start(NfcApp* nfc) { void nfc_blink_emulate_start(NfcApp* nfc) {
@@ -230,7 +231,7 @@ void nfc_blink_emulate_start(NfcApp* nfc) {
} }
void nfc_blink_detect_start(NfcApp* nfc) { void nfc_blink_detect_start(NfcApp* nfc) {
notification_message(nfc->notifications, &sequence_blink_start_yellow); notification_message(nfc->notifications, &sequence_blink_start_cyan);
} }
void nfc_blink_stop(NfcApp* nfc) { void nfc_blink_stop(NfcApp* nfc) {
@@ -339,8 +340,10 @@ bool nfc_load_file(NfcApp* instance, FuriString* path, bool show_dialog) {
furi_assert(path); furi_assert(path);
bool result = false; bool result = false;
nfc_supported_cards_load_cache(instance->nfc_supported_cards);
FuriString* load_path = furi_string_alloc(); FuriString* load_path = furi_string_alloc();
if(nfc_has_shadow_file_internal(instance, path)) { if(nfc_has_shadow_file_internal(instance, path)) { //-V1051
nfc_set_shadow_file_path(path, load_path); nfc_set_shadow_file_path(path, load_path);
} else if(furi_string_end_with(path, NFC_APP_SHADOW_EXTENSION)) { } else if(furi_string_end_with(path, NFC_APP_SHADOW_EXTENSION)) {
size_t path_len = furi_string_size(path); size_t path_len = furi_string_size(path);
@@ -400,15 +403,16 @@ bool nfc_load_from_file_select(NfcApp* instance) {
browser_options.base_path = NFC_APP_FOLDER; browser_options.base_path = NFC_APP_FOLDER;
browser_options.hide_dot_files = true; browser_options.hide_dot_files = true;
bool success = false;
do {
// Input events and views are managed by file_browser // Input events and views are managed by file_browser
bool result = 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;
success = nfc_load_file(instance, instance->file_path, true);
} while(!success);
if(result) { return success;
result = nfc_load_file(instance, instance->file_path, true);
}
return result;
} }
void nfc_show_loading_popup(void* context, bool show) { void nfc_show_loading_popup(void* context, bool show) {

View File

@@ -31,6 +31,7 @@
#include "helpers/mf_user_dict.h" #include "helpers/mf_user_dict.h"
#include "helpers/mfkey32_logger.h" #include "helpers/mfkey32_logger.h"
#include "helpers/mf_classic_key_cache.h" #include "helpers/mf_classic_key_cache.h"
#include "helpers/nfc_supported_cards.h"
#include <dialogs/dialogs.h> #include <dialogs/dialogs.h>
#include <storage/storage.h> #include <storage/storage.h>
@@ -51,7 +52,7 @@
#include <nfc/nfc_device.h> #include <nfc/nfc_device.h>
#include <nfc/helpers/nfc_data_generator.h> #include <nfc/helpers/nfc_data_generator.h>
#include <nfc/helpers/nfc_dict.h> #include <toolbox/keys_dict.h>
#include <gui/modules/validators.h> #include <gui/modules/validators.h>
#include <toolbox/path.h> #include <toolbox/path.h>
@@ -79,7 +80,7 @@ typedef enum {
} NfcRpcState; } NfcRpcState;
typedef struct { typedef struct {
NfcDict* dict; KeysDict* dict;
uint8_t sectors_total; uint8_t sectors_total;
uint8_t sectors_read; uint8_t sectors_read;
uint8_t current_sector; uint8_t current_sector;
@@ -132,6 +133,7 @@ struct NfcApp {
Mfkey32Logger* mfkey32_logger; Mfkey32Logger* mfkey32_logger;
MfUserDict* mf_user_dict; MfUserDict* mf_user_dict;
MfClassicKeyCache* mfc_key_cache; MfClassicKeyCache* mfc_key_cache;
NfcSupportedCards* nfc_supported_cards;
NfcDevice* nfc_device; NfcDevice* nfc_device;
Iso14443_3aData* iso14443_3a_edit_data; Iso14443_3aData* iso14443_3a_edit_data;

View File

@@ -0,0 +1,169 @@
#include "nfc_supported_card_plugin.h"
#include <flipper_application/flipper_application.h>
#include <nfc/nfc_device.h>
#include <nfc/helpers/nfc_util.h>
#include <nfc/protocols/mf_classic/mf_classic_poller_sync.h>
#define TAG "Aime"
static const uint64_t aime_key = 0x574343467632;
bool aime_verify(Nfc* nfc) {
bool verified = false;
do {
const uint8_t verify_sector = 0;
uint8_t block_num = mf_classic_get_first_block_num_of_sector(verify_sector);
FURI_LOG_D(TAG, "Verifying sector %u", verify_sector);
MfClassicKey key = {};
nfc_util_num2bytes(aime_key, COUNT_OF(key.data), key.data);
MfClassicAuthContext auth_ctx = {};
MfClassicError error =
mf_classic_poller_sync_auth(nfc, block_num, &key, MfClassicKeyTypeA, &auth_ctx);
if(error != MfClassicErrorNone) {
FURI_LOG_D(TAG, "Failed to read block %u: %d", block_num, error);
break;
}
verified = true;
} while(false);
return verified;
}
static bool aime_read(Nfc* nfc, NfcDevice* device) {
furi_assert(nfc);
furi_assert(device);
bool is_read = false;
MfClassicData* data = mf_classic_alloc();
nfc_device_copy_data(device, NfcProtocolMfClassic, data);
do {
MfClassicType type = MfClassicType1k;
MfClassicError error = mf_classic_poller_sync_detect_type(nfc, &type);
if(error != MfClassicErrorNone) break;
data->type = type;
MfClassicDeviceKeys keys = {};
for(size_t i = 0; i < mf_classic_get_total_sectors_num(data->type); i++) {
nfc_util_num2bytes(aime_key, sizeof(MfClassicKey), keys.key_a[i].data);
FURI_BIT_SET(keys.key_a_mask, i);
nfc_util_num2bytes(aime_key, sizeof(MfClassicKey), keys.key_b[i].data);
FURI_BIT_SET(keys.key_b_mask, i);
}
error = mf_classic_poller_sync_read(nfc, &keys, data);
if(error != MfClassicErrorNone) {
FURI_LOG_W(TAG, "Failed to read data");
break;
}
nfc_device_set_data(device, NfcProtocolMfClassic, data);
is_read = mf_classic_is_card_read(data);
} while(false);
mf_classic_free(data);
return is_read;
}
static bool aime_parse(const NfcDevice* device, FuriString* parsed_data) {
furi_assert(device);
const MfClassicData* data = nfc_device_get_data(device, NfcProtocolMfClassic);
bool parsed = false;
do {
// verify key
MfClassicSectorTrailer* sec_tr = mf_classic_get_sector_trailer_by_sector(data, 0);
uint64_t key = nfc_util_bytes2num(sec_tr->key_a.data, 6);
if(key != aime_key) break;
// Aime Magic is stored at block 1, starts from byte 0, len 4 bytes
const uint8_t* aime_magic = &data->block[1].data[0];
// verify aime magic
if(aime_magic[0] != 'S' || aime_magic[1] != 'B' || aime_magic[2] != 'S' ||
aime_magic[3] != 'D')
break;
// Aime checksum is stored at block 1, starts from byte 13, len 3 bytes
// seems like only old games checks this? e.g., old versions of Chunithm
const uint8_t* aime_checksum = &data->block[1].data[13];
// Aime access code is stored as decimal hex representation in block 2, starts from byte 6, len 10 bytes
const uint8_t* aime_accesscode = &data->block[2].data[6];
char aime_accesscode_str[24 + 1];
snprintf(
aime_accesscode_str,
sizeof(aime_accesscode_str),
"%02x%02x %02x%02x %02x%02x %02x%02x %02x%02x",
aime_accesscode[0],
aime_accesscode[1],
aime_accesscode[2],
aime_accesscode[3],
aime_accesscode[4],
aime_accesscode[5],
aime_accesscode[6],
aime_accesscode[7],
aime_accesscode[8],
aime_accesscode[9]);
// validate decimal hex representation
bool code_is_hex = true;
for(int i = 0; i < 24; i++) {
if(aime_accesscode_str[i] == ' ') continue;
if(aime_accesscode_str[i] < '0' || aime_accesscode_str[i] > '9') {
code_is_hex = false;
break;
}
}
if(!code_is_hex) break;
// Note: Aime access code has some other self-check algorithms that are not public.
// This parser does not try to verify the number.
furi_string_printf(
parsed_data,
"\e#Aime Card\nAccess Code: \n%s\nChecksum: %02X%02X%02X\n",
aime_accesscode_str,
aime_checksum[0],
aime_checksum[1],
aime_checksum[2]);
parsed = true;
} while(false);
return parsed;
}
/* Actual implementation of app<>plugin interface */
static const NfcSupportedCardsPlugin aime_plugin = {
.protocol = NfcProtocolMfClassic,
.verify = aime_verify,
.read = aime_read,
.parse = aime_parse,
};
/* Plugin descriptor to comply with basic plugin specification */
static const FlipperAppPluginDescriptor aime_plugin_descriptor = {
.appid = NFC_SUPPORTED_CARD_PLUGIN_APP_ID,
.ep_api_version = NFC_SUPPORTED_CARD_PLUGIN_API_VERSION,
.entry_point = &aime_plugin,
};
/* Plugin entry point - must return a pointer to const descriptor */
const FlipperAppPluginDescriptor* aime_plugin_ep() {
return &aime_plugin_descriptor;
}

View File

@@ -0,0 +1,153 @@
#include "nfc_supported_card_plugin.h"
#include <flipper_application/flipper_application.h>
#include <nfc/nfc_device.h>
#include <nfc/helpers/nfc_util.h>
#include <nfc/protocols/mf_classic/mf_classic_poller_sync.h>
#define TAG "HID"
static const uint64_t hid_key = 0x484944204953;
bool hid_verify(Nfc* nfc) {
bool verified = false;
do {
const uint8_t verify_sector = 1;
uint8_t block_num = mf_classic_get_first_block_num_of_sector(verify_sector);
FURI_LOG_D(TAG, "Verifying sector %u", verify_sector);
MfClassicKey key = {};
nfc_util_num2bytes(hid_key, COUNT_OF(key.data), key.data);
MfClassicAuthContext auth_ctx = {};
MfClassicError error =
mf_classic_poller_sync_auth(nfc, block_num, &key, MfClassicKeyTypeA, &auth_ctx);
if(error != MfClassicErrorNone) {
FURI_LOG_D(TAG, "Failed to read block %u: %d", block_num, error);
break;
}
verified = true;
} while(false);
return verified;
}
static bool hid_read(Nfc* nfc, NfcDevice* device) {
furi_assert(nfc);
furi_assert(device);
bool is_read = false;
MfClassicData* data = mf_classic_alloc();
nfc_device_copy_data(device, NfcProtocolMfClassic, data);
do {
MfClassicType type = MfClassicType1k;
MfClassicError error = mf_classic_poller_sync_detect_type(nfc, &type);
if(error != MfClassicErrorNone) break;
data->type = type;
MfClassicDeviceKeys keys = {};
for(size_t i = 0; i < mf_classic_get_total_sectors_num(data->type); i++) {
nfc_util_num2bytes(hid_key, sizeof(MfClassicKey), keys.key_a[i].data);
FURI_BIT_SET(keys.key_a_mask, i);
nfc_util_num2bytes(hid_key, sizeof(MfClassicKey), keys.key_b[i].data);
FURI_BIT_SET(keys.key_b_mask, i);
}
error = mf_classic_poller_sync_read(nfc, &keys, data);
if(error != MfClassicErrorNone) {
FURI_LOG_W(TAG, "Failed to read data");
break;
}
nfc_device_set_data(device, NfcProtocolMfClassic, data);
is_read = mf_classic_is_card_read(data);
} while(false);
mf_classic_free(data);
return is_read;
}
static uint8_t get_bit_length(const uint8_t* half_block) {
uint8_t bitLength = 0;
uint32_t* halves = (uint32_t*)half_block;
if(halves[0] == 0) {
uint8_t leading0s = __builtin_clz(REVERSE_BYTES_U32(halves[1]));
bitLength = 31 - leading0s;
} else {
uint8_t leading0s = __builtin_clz(REVERSE_BYTES_U32(halves[0]));
bitLength = 63 - leading0s;
}
return bitLength;
}
static uint64_t get_pacs_bits(const uint8_t* block, uint8_t bitLength) {
// Remove sentinel bit from credential. Byteswapping to handle array of bytes vs 64bit value
uint64_t sentinel = __builtin_bswap64(1ULL << bitLength);
uint64_t swapped = 0;
memcpy(&swapped, block, sizeof(uint64_t));
swapped = __builtin_bswap64(swapped ^ sentinel);
FURI_LOG_D(TAG, "PACS: (%d) %016llx", bitLength, swapped);
return swapped;
}
static bool hid_parse(const NfcDevice* device, FuriString* parsed_data) {
furi_assert(device);
const MfClassicData* data = nfc_device_get_data(device, NfcProtocolMfClassic);
bool parsed = false;
do {
// verify key
const uint8_t verify_sector = 1;
MfClassicSectorTrailer* sec_tr =
mf_classic_get_sector_trailer_by_sector(data, verify_sector);
uint64_t key = nfc_util_bytes2num(sec_tr->key_a.data, 6);
if(key != hid_key) break;
// Currently doesn't support bit length > 63
const uint8_t* credential_block = data->block[5].data + 8;
uint8_t bitLength = get_bit_length(credential_block);
if(bitLength == 0) break;
uint64_t credential = get_pacs_bits(credential_block, bitLength);
if(credential == 0) break;
furi_string_printf(parsed_data, "\e#HID Card\n%dbit\n%llx", bitLength, credential);
parsed = true;
} while(false);
return parsed;
}
/* Actual implementation of app<>plugin interface */
static const NfcSupportedCardsPlugin hid_plugin = {
.protocol = NfcProtocolMfClassic,
.verify = hid_verify,
.read = hid_read,
.parse = hid_parse,
};
/* Plugin descriptor to comply with basic plugin specification */
static const FlipperAppPluginDescriptor hid_plugin_descriptor = {
.appid = NFC_SUPPORTED_CARD_PLUGIN_APP_ID,
.ep_api_version = NFC_SUPPORTED_CARD_PLUGIN_API_VERSION,
.entry_point = &hid_plugin,
};
/* Plugin entry point - must return a pointer to const descriptor */
const FlipperAppPluginDescriptor* hid_plugin_ep() {
return &hid_plugin_descriptor;
}

View File

@@ -0,0 +1,357 @@
/*
* Parser for Kazan transport card (Kazan, Russia).
*
* Copyright 2023 Leptoptilos <leptoptilos@icloud.com>
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "nfc_supported_card_plugin.h"
#include "protocols/mf_classic/mf_classic.h"
#include <flipper_application/flipper_application.h>
#include <nfc/nfc_device.h>
#include <nfc/helpers/nfc_util.h>
#include <nfc/protocols/mf_classic/mf_classic_poller_sync.h>
#include <furi_hal_rtc.h>
#define TAG "Kazan"
typedef struct {
uint64_t a;
uint64_t b;
} MfClassicKeyPair;
static const MfClassicKeyPair kazan_1k_keys_standart[] = {
{.a = 0xFFFFFFFFFFFF, .b = 0xFFFFFFFFFFFF},
{.a = 0xFFFFFFFFFFFF, .b = 0xFFFFFFFFFFFF},
{.a = 0xFFFFFFFFFFFF, .b = 0xFFFFFFFFFFFF},
{.a = 0xFFFFFFFFFFFF, .b = 0xFFFFFFFFFFFF},
{.a = 0xFFFFFFFFFFFF, .b = 0xFFFFFFFFFFFF},
{.a = 0xFFFFFFFFFFFF, .b = 0xFFFFFFFFFFFF},
{.a = 0xFFFFFFFFFFFF, .b = 0xFFFFFFFFFFFF},
{.a = 0xFFFFFFFFFFFF, .b = 0xFFFFFFFFFFFF},
{.a = 0xE954024EE754, .b = 0x0CD464CDC100},
{.a = 0xBC305FE2DA65, .b = 0xCF0EC6ACF2F9},
{.a = 0xF7A545095C49, .b = 0x6862FD600F78},
{.a = 0xFFFFFFFFFFFF, .b = 0xFFFFFFFFFFFF},
{.a = 0xFFFFFFFFFFFF, .b = 0xFFFFFFFFFFFF},
{.a = 0xFFFFFFFFFFFF, .b = 0xFFFFFFFFFFFF},
{.a = 0xFFFFFFFFFFFF, .b = 0xFFFFFFFFFFFF},
{.a = 0xFFFFFFFFFFFF, .b = 0xFFFFFFFFFFFF},
};
static const MfClassicKeyPair kazan_1k_keys_old[] = {
{.a = 0xFFFFFFFFFFFF, .b = 0xFFFFFFFFFFFF},
{.a = 0xFFFFFFFFFFFF, .b = 0xFFFFFFFFFFFF},
{.a = 0xFFFFFFFFFFFF, .b = 0xFFFFFFFFFFFF},
{.a = 0xFFFFFFFFFFFF, .b = 0xFFFFFFFFFFFF},
{.a = 0xFFFFFFFFFFFF, .b = 0xFFFFFFFFFFFF},
{.a = 0xFFFFFFFFFFFF, .b = 0xFFFFFFFFFFFF},
{.a = 0xFFFFFFFFFFFF, .b = 0xFFFFFFFFFFFF},
{.a = 0xFFFFFFFFFFFF, .b = 0xFFFFFFFFFFFF},
{.a = 0x2058EAEE8446, .b = 0xCB9B23815F87},
{.a = 0x492F3744A1DC, .b = 0x6B770AADA274},
{.a = 0xF7A545095C49, .b = 0x6862FD600F78},
{.a = 0xFFFFFFFFFFFF, .b = 0xFFFFFFFFFFFF},
{.a = 0xFFFFFFFFFFFF, .b = 0xFFFFFFFFFFFF},
{.a = 0xFFFFFFFFFFFF, .b = 0xFFFFFFFFFFFF},
{.a = 0xFFFFFFFFFFFF, .b = 0xFFFFFFFFFFFF},
{.a = 0xFFFFFFFFFFFF, .b = 0xFFFFFFFFFFFF},
};
enum SubscriptionType {
SUBSCRIPTION_TYPE_UNKNOWN,
SUBSCRIPTION_TYPE_PURSE,
SUBSCRIPTION_TYPE_ABONNEMENT_BY_TIME,
SUBSCRIPTION_TYPE_ABONNEMENT_BY_TRIPS,
};
enum SubscriptionType get_subscription_type(uint8_t value, FuriString* tariff_name) {
switch(value) {
case 0x51:
furi_string_printf(tariff_name, "Social. Adult");
return SUBSCRIPTION_TYPE_ABONNEMENT_BY_TIME;
case 0x67:
furi_string_printf(tariff_name, "Ground electric transport. 1 month");
return SUBSCRIPTION_TYPE_ABONNEMENT_BY_TIME;
case 0x0F:
furi_string_printf(tariff_name, "Underground only");
return SUBSCRIPTION_TYPE_ABONNEMENT_BY_TRIPS;
case 0x6D:
furi_string_printf(tariff_name, "Tram. 60 minutes. Transfer. 10 trips");
return SUBSCRIPTION_TYPE_ABONNEMENT_BY_TRIPS;
case 0x53:
furi_string_printf(tariff_name, "Standart purse");
return SUBSCRIPTION_TYPE_PURSE;
default:
furi_string_printf(tariff_name, "Unknown");
return SUBSCRIPTION_TYPE_UNKNOWN;
}
}
static bool kazan_verify(Nfc* nfc) {
bool verified = false;
do {
const uint8_t verification_sector_number = 10;
const uint8_t verification_block_number =
mf_classic_get_first_block_num_of_sector(verification_sector_number) + 1;
FURI_LOG_D(TAG, "Verifying sector %u", verification_sector_number);
MfClassicKey key = {0};
nfc_util_num2bytes(
kazan_1k_keys_standart[verification_sector_number].a, COUNT_OF(key.data), key.data);
MfClassicAuthContext auth_context;
MfClassicError error = mf_classic_poller_sync_auth(
nfc, verification_block_number, &key, MfClassicKeyTypeA, &auth_context);
if(error != MfClassicErrorNone) {
FURI_LOG_D(TAG, "Failed to read block %u: %d", verification_block_number, error);
break;
}
verified = true;
} while(false);
return verified;
}
static bool kazan_read(Nfc* nfc, NfcDevice* device) {
furi_assert(nfc);
furi_assert(device);
bool is_read = false;
MfClassicData* data = mf_classic_alloc();
nfc_device_copy_data(device, NfcProtocolMfClassic, data);
do {
MfClassicType type = MfClassicTypeMini;
MfClassicError error = mf_classic_poller_sync_detect_type(nfc, &type);
if(error != MfClassicErrorNone) break;
data->type = type;
if(type != MfClassicType1k) break;
MfClassicDeviceKeys keys = {
.key_a_mask = 0,
.key_b_mask = 0,
};
MfClassicDeviceKeys keys_old = {
.key_a_mask = 0,
.key_b_mask = 0,
};
for(size_t i = 0; i < mf_classic_get_total_sectors_num(data->type); i++) {
nfc_util_num2bytes(
kazan_1k_keys_standart[i].a, sizeof(MfClassicKey), keys.key_a[i].data);
nfc_util_num2bytes(
kazan_1k_keys_old[i].a, sizeof(MfClassicKey), keys_old.key_a[i].data);
FURI_BIT_SET(keys.key_a_mask, i);
FURI_BIT_SET(keys_old.key_a_mask, i);
nfc_util_num2bytes(
kazan_1k_keys_standart[i].b, sizeof(MfClassicKey), keys.key_b[i].data);
nfc_util_num2bytes(
kazan_1k_keys_old[i].b, sizeof(MfClassicKey), keys_old.key_b[i].data);
FURI_BIT_SET(keys.key_b_mask, i);
FURI_BIT_SET(keys_old.key_b_mask, i);
}
error = mf_classic_poller_sync_read(nfc, &keys, data);
if(error != MfClassicErrorNone) {
FURI_LOG_W(TAG, "Failed to read data: standart keys");
break;
}
if(!mf_classic_is_card_read(data)) {
error = mf_classic_poller_sync_read(nfc, &keys_old, data);
if(error != MfClassicErrorNone) {
FURI_LOG_W(TAG, "Failed to read data: old keys");
break;
}
}
nfc_device_set_data(device, NfcProtocolMfClassic, data);
is_read = mf_classic_is_card_read(data);
} while(false);
mf_classic_free(data);
return is_read;
}
static bool kazan_parse(const NfcDevice* device, FuriString* parsed_data) {
furi_assert(device);
const MfClassicData* data = nfc_device_get_data(device, NfcProtocolMfClassic);
bool parsed = false;
do {
const uint8_t verification_sector_number = 10;
const uint8_t ticket_sector_number = 8;
const uint8_t balance_sector_number = 9;
// Verify keys
MfClassicKeyPair keys = {};
const MfClassicSectorTrailer* sec_tr =
mf_classic_get_sector_trailer_by_sector(data, verification_sector_number);
keys.a = nfc_util_bytes2num(sec_tr->key_a.data, COUNT_OF(sec_tr->key_a.data));
if(keys.a != 0xF7A545095C49) {
FURI_LOG_D(TAG, "Parser: Failed to verify key a: %llu", keys.a);
break;
}
// Parse data
uint8_t start_block_num = mf_classic_get_first_block_num_of_sector(ticket_sector_number);
const uint8_t* block_start_ptr = &data->block[start_block_num].data[6];
FuriString* tariff_name = furi_string_alloc();
enum SubscriptionType subscription_type =
get_subscription_type(block_start_ptr[0], tariff_name);
FuriHalRtcDateTime valid_from;
valid_from.year = 2000 + block_start_ptr[1];
valid_from.month = block_start_ptr[2];
valid_from.day = block_start_ptr[3];
FuriHalRtcDateTime valid_to;
valid_to.year = 2000 + block_start_ptr[4];
valid_to.month = block_start_ptr[5];
valid_to.day = block_start_ptr[6];
const uint8_t last_trip_block_number = 2;
block_start_ptr = &data->block[start_block_num + last_trip_block_number].data[1];
FuriHalRtcDateTime last_trip;
last_trip.year = 2000 + block_start_ptr[0];
last_trip.month = block_start_ptr[1];
last_trip.day = block_start_ptr[2];
last_trip.hour = block_start_ptr[3];
last_trip.minute = block_start_ptr[4];
bool is_last_trip_valid = (block_start_ptr[0] | block_start_ptr[1] | block_start_ptr[2]) &&
(last_trip.day < 32 && last_trip.month < 12 &&
last_trip.hour < 24 && last_trip.minute < 60);
start_block_num = mf_classic_get_first_block_num_of_sector(balance_sector_number);
block_start_ptr = &data->block[start_block_num].data[0];
const uint32_t trip_counter = nfc_util_bytes2num_little_endian(block_start_ptr, 4);
size_t uid_len = 0;
const uint8_t* uid = mf_classic_get_uid(data, &uid_len);
const uint32_t card_number = nfc_util_bytes2num_little_endian(uid, 4);
furi_string_cat_printf(
parsed_data, "\e#Kazan transport card\nCard number: %lu\n", card_number);
if(subscription_type == SUBSCRIPTION_TYPE_PURSE) {
furi_string_cat_printf(
parsed_data,
"Type: purse\nBalance: %lu RUR\nBalance valid:\nfrom: %02u.%02u.%u\nto: %02u.%02u.%u",
trip_counter,
valid_from.day,
valid_from.month,
valid_from.year,
valid_to.day,
valid_to.month,
valid_to.year);
}
if(subscription_type == SUBSCRIPTION_TYPE_ABONNEMENT_BY_TRIPS) {
furi_string_cat_printf(
parsed_data,
"Type: abonnement\nTariff: %s\nTrips left: %lu\nCard valid:\nfrom: %02u.%02u.%u\nto: %02u.%02u.%u",
furi_string_get_cstr(tariff_name),
trip_counter,
valid_from.day,
valid_from.month,
valid_from.year,
valid_to.day,
valid_to.month,
valid_to.year);
}
if(subscription_type == SUBSCRIPTION_TYPE_ABONNEMENT_BY_TIME) {
furi_string_cat_printf(
parsed_data,
"Type: abonnement\nTariff: %s\nTotal valid time: %lu days\nCard valid:\nfrom: %02u.%02u.%u\nto: %02u.%02u.%u",
furi_string_get_cstr(tariff_name),
trip_counter,
valid_from.day,
valid_from.month,
valid_from.year,
valid_to.day,
valid_to.month,
valid_to.year);
}
if(subscription_type == SUBSCRIPTION_TYPE_UNKNOWN) {
furi_string_cat_printf(
parsed_data,
"Type: unknown\nTariff: %s\nCounter: %lu\nValid from: %02u.%02u.%u\nValid to: %02u.%02u.%u",
furi_string_get_cstr(tariff_name),
trip_counter,
valid_from.day,
valid_from.month,
valid_from.year,
valid_to.day,
valid_to.month,
valid_to.year);
}
if(is_last_trip_valid) {
furi_string_cat_printf(
parsed_data,
"\nLast trip: %02u.%02u.%u at %02u:%02u",
last_trip.day,
last_trip.month,
last_trip.year,
last_trip.hour,
last_trip.minute);
}
furi_string_free(tariff_name);
parsed = true;
} while(false);
return parsed;
}
/* Actual implementation of app<>plugin interface */
static const NfcSupportedCardsPlugin kazan_plugin = {
.protocol = NfcProtocolMfClassic,
.verify = kazan_verify,
.read = kazan_read,
.parse = kazan_parse,
};
/* Plugin descriptor to comply with basic plugin specification */
static const FlipperAppPluginDescriptor kazan_plugin_descriptor = {
.appid = NFC_SUPPORTED_CARD_PLUGIN_APP_ID,
.ep_api_version = NFC_SUPPORTED_CARD_PLUGIN_API_VERSION,
.entry_point = &kazan_plugin,
};
/* Plugin entry point - must return a pointer to const descriptor */
const FlipperAppPluginDescriptor* kazan_plugin_ep() {
return &kazan_plugin_descriptor;
}

View File

@@ -0,0 +1,189 @@
/*
* Parser for Metromoney card (Georgia).
*
* Copyright 2023 Leptoptilos <leptoptilos@icloud.com>
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "nfc_supported_card_plugin.h"
#include "protocols/mf_classic/mf_classic.h"
#include <flipper_application/flipper_application.h>
#include <nfc/nfc_device.h>
#include <nfc/helpers/nfc_util.h>
#include <nfc/protocols/mf_classic/mf_classic_poller_sync.h>
#define TAG "Metromoney"
typedef struct {
uint64_t a;
uint64_t b;
} MfClassicKeyPair;
static const MfClassicKeyPair metromoney_1k_keys[] = {
{.a = 0x2803BCB0C7E1, .b = 0x4FA9EB49F75E},
{.a = 0x9C616585E26D, .b = 0xD1C71E590D16},
{.a = 0x9C616585E26D, .b = 0xA160FCD5EC4C},
{.a = 0x9C616585E26D, .b = 0xA160FCD5EC4C},
{.a = 0x9C616585E26D, .b = 0xA160FCD5EC4C},
{.a = 0x9C616585E26D, .b = 0xA160FCD5EC4C},
{.a = 0xFFFFFFFFFFFF, .b = 0xFFFFFFFFFFFF},
{.a = 0xFFFFFFFFFFFF, .b = 0xFFFFFFFFFFFF},
{.a = 0x112233445566, .b = 0x361A62F35BC9},
{.a = 0x112233445566, .b = 0x361A62F35BC9},
{.a = 0xFFFFFFFFFFFF, .b = 0xFFFFFFFFFFFF},
{.a = 0xFFFFFFFFFFFF, .b = 0xFFFFFFFFFFFF},
{.a = 0xFFFFFFFFFFFF, .b = 0xFFFFFFFFFFFF},
{.a = 0xFFFFFFFFFFFF, .b = 0xFFFFFFFFFFFF},
{.a = 0xFFFFFFFFFFFF, .b = 0xFFFFFFFFFFFF},
{.a = 0xFFFFFFFFFFFF, .b = 0xFFFFFFFFFFFF},
};
static bool metromoney_verify(Nfc* nfc) {
bool verified = false;
do {
const uint8_t ticket_sector_number = 1;
const uint8_t ticket_block_number =
mf_classic_get_first_block_num_of_sector(ticket_sector_number) + 1;
FURI_LOG_D(TAG, "Verifying sector %u", ticket_sector_number);
MfClassicKey key = {0};
nfc_util_num2bytes(
metromoney_1k_keys[ticket_sector_number].a, COUNT_OF(key.data), key.data);
MfClassicAuthContext auth_context;
MfClassicError error = mf_classic_poller_sync_auth(
nfc, ticket_block_number, &key, MfClassicKeyTypeA, &auth_context);
if(error != MfClassicErrorNone) {
FURI_LOG_D(TAG, "Failed to read block %u: %d", ticket_block_number, error);
break;
}
verified = true;
} while(false);
return verified;
}
static bool metromoney_read(Nfc* nfc, NfcDevice* device) {
furi_assert(nfc);
furi_assert(device);
bool is_read = false;
MfClassicData* data = mf_classic_alloc();
nfc_device_copy_data(device, NfcProtocolMfClassic, data);
do {
MfClassicType type = MfClassicTypeMini;
MfClassicError error = mf_classic_poller_sync_detect_type(nfc, &type);
if(error != MfClassicErrorNone) break;
data->type = type;
if(type != MfClassicType1k) break;
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++) {
nfc_util_num2bytes(metromoney_1k_keys[i].a, sizeof(MfClassicKey), keys.key_a[i].data);
FURI_BIT_SET(keys.key_a_mask, i);
nfc_util_num2bytes(metromoney_1k_keys[i].b, sizeof(MfClassicKey), keys.key_b[i].data);
FURI_BIT_SET(keys.key_b_mask, i);
}
error = mf_classic_poller_sync_read(nfc, &keys, data);
if(error != MfClassicErrorNone) {
FURI_LOG_W(TAG, "Failed to read data");
break;
}
nfc_device_set_data(device, NfcProtocolMfClassic, data);
is_read = mf_classic_is_card_read(data);
} while(false);
mf_classic_free(data);
return is_read;
}
static bool metromoney_parse(const NfcDevice* device, FuriString* parsed_data) {
furi_assert(device);
const MfClassicData* data = nfc_device_get_data(device, NfcProtocolMfClassic);
bool parsed = false;
do {
// Verify key
const uint8_t ticket_sector_number = 1;
const uint8_t ticket_block_number = 1;
const MfClassicSectorTrailer* sec_tr =
mf_classic_get_sector_trailer_by_sector(data, ticket_sector_number);
const uint64_t key = nfc_util_bytes2num(sec_tr->key_a.data, COUNT_OF(sec_tr->key_a.data));
if(key != metromoney_1k_keys[ticket_sector_number].a) break;
// Parse data
const uint8_t start_block_num =
mf_classic_get_first_block_num_of_sector(ticket_sector_number);
const uint8_t* block_start_ptr =
&data->block[start_block_num + ticket_block_number].data[0];
uint32_t balance = nfc_util_bytes2num_little_endian(block_start_ptr, 4);
uint32_t balance_lari = balance / 100;
uint8_t balance_tetri = balance % 100;
size_t uid_len = 0;
const uint8_t* uid = mf_classic_get_uid(data, &uid_len);
uint32_t card_number = nfc_util_bytes2num_little_endian(uid, 4);
furi_string_printf(
parsed_data,
"\e#Metromoney\nCard number: %lu\nBalance: %lu.%02u GEL",
card_number,
balance_lari,
balance_tetri);
parsed = true;
} while(false);
return parsed;
}
/* Actual implementation of app<>plugin interface */
static const NfcSupportedCardsPlugin metromoney_plugin = {
.protocol = NfcProtocolMfClassic,
.verify = metromoney_verify,
.read = metromoney_read,
.parse = metromoney_parse,
};
/* Plugin descriptor to comply with basic plugin specification */
static const FlipperAppPluginDescriptor metromoney_plugin_descriptor = {
.appid = NFC_SUPPORTED_CARD_PLUGIN_APP_ID,
.ep_api_version = NFC_SUPPORTED_CARD_PLUGIN_API_VERSION,
.entry_point = &metromoney_plugin,
};
/* Plugin entry point - must return a pointer to const descriptor */
const FlipperAppPluginDescriptor* metromoney_plugin_ep() {
return &metromoney_plugin_descriptor;
}

View File

@@ -0,0 +1,157 @@
#include "nfc_supported_card_plugin.h"
#include <flipper_application/flipper_application.h>
#include <machine/endian.h>
#include <nfc/protocols/st25tb/st25tb.h>
#define TAG "MyKey"
const uint32_t blankBlock18 = 0x480FCD8F, blankBlock19 = 0x070082C0;
static bool mykey_is_blank(const St25tbData* data) {
return data->blocks[0x18] == blankBlock18 && data->blocks[0x19] == blankBlock19;
}
static bool mykey_has_lockid(const St25tbData* data) {
return (data->blocks[5] & 0xFF) == 0x7F;
}
static bool check_invalid_low_nibble(uint8_t value) {
uint8_t value_lo = value & 0xF;
return value_lo >= 0xA;
}
static bool mykey_get_production_date(
const St25tbData* data,
uint16_t* year_ptr,
uint8_t* month_ptr,
uint8_t* day_ptr) {
uint32_t date_block = data->blocks[8];
uint8_t year = date_block >> 16 & 0xFF;
uint8_t month = date_block >> 8 & 0xFF;
uint8_t day = date_block & 0xFF;
// dates are coded in a peculiar way, the hexadecimal value should in fact be interpreted as a decimal value
// so anything in range A-F is invalid.
if(day > 0x31 || month > 0x12 || day == 0 || month == 0 || year == 0) {
return false;
}
if(check_invalid_low_nibble(day) || check_invalid_low_nibble(month) ||
check_invalid_low_nibble(year) || check_invalid_low_nibble(year >> 4)) {
return false;
}
*year_ptr = year + 0x2000;
*month_ptr = month;
*day_ptr = day;
return true;
}
static bool mykey_parse(const NfcDevice* device, FuriString* parsed_data) {
furi_assert(device);
furi_assert(parsed_data);
const St25tbData* data = nfc_device_get_data(device, NfcProtocolSt25tb);
if(data->type != St25tbType04k && data->type != St25tbTypeX4k) {
FURI_LOG_D(TAG, "bad type");
return false;
}
for(int i = 0; i < 5; i++) {
if(data->blocks[i] != 0xFFFFFFFF) {
FURI_LOG_D(TAG, "bad otp block %d", i);
return false;
}
}
uint16_t mfg_year;
uint8_t mfg_month, mfg_day;
if(!mykey_get_production_date(data, &mfg_year, &mfg_month, &mfg_day)) {
FURI_LOG_D(TAG, "bad mfg date");
return false;
}
if(data->system_otp_block != 0xFEFFFFFF) {
FURI_LOG_D(TAG, "bad sys otp block");
return false;
}
furi_string_cat(parsed_data, "\e#MyKey\n");
if(data->blocks[6] == 0) { // Tag is actually a MyKey but it has been bricked by a reader
furi_string_cat(parsed_data, "\e#Bricked!\nBlock 6 is 0!");
return true;
}
bool is_blank = mykey_is_blank(data);
furi_string_cat_printf(parsed_data, "Serial#: %08lX\n", (uint32_t)__bswap32(data->blocks[7]));
furi_string_cat_printf(parsed_data, "Blank: %s\n", is_blank ? "yes" : "no");
furi_string_cat_printf(parsed_data, "LockID: %s\n", mykey_has_lockid(data) ? "maybe" : "no");
furi_string_cat_printf(
parsed_data, "Prod. date: %02X/%02X/%04X", mfg_day, mfg_month, mfg_year);
if(!is_blank) {
furi_string_cat_printf(
parsed_data, "\nOp. count: %zu\n", (size_t)__bswap32(data->blocks[0x12] & 0xFFFFFF00));
uint32_t block3C = data->blocks[0x3C];
if(block3C == 0xFFFFFFFF) {
furi_string_cat(parsed_data, "No history available!");
} else {
block3C ^= data->blocks[0x07];
uint32_t startingOffset = ((block3C & 0x30000000) >> 28) |
((block3C & 0x00100000) >> 18);
furi_check(startingOffset < 8); //-V547
for(int txnOffset = 8; txnOffset > 0; txnOffset--) {
uint32_t txnBlock =
__bswap32(data->blocks[0x34 + ((startingOffset + txnOffset) % 8)]);
if(txnBlock == 0xFFFFFFFF) {
break;
}
uint8_t day = txnBlock >> 27;
uint8_t month = txnBlock >> 23 & 0xF;
uint16_t year = 2000 + (txnBlock >> 16 & 0x7F);
uint16_t credit = txnBlock & 0xFFFF;
if(txnOffset == 8) {
furi_string_cat_printf(
parsed_data, "Current credit: %d.%02d euros\n", credit / 100, credit % 100);
furi_string_cat(parsed_data, "Op. history (newest first):");
}
furi_string_cat_printf(
parsed_data,
"\n %02d/%02d/%04d %d.%02d",
day,
month,
year,
credit / 100,
credit % 100);
}
}
}
return true;
}
/* Actual implementation of app<>plugin interface */
static const NfcSupportedCardsPlugin mykey_plugin = {
.protocol = NfcProtocolSt25tb,
.verify = NULL,
.read = NULL,
.parse = mykey_parse,
};
/* Plugin descriptor to comply with basic plugin specification */
static const FlipperAppPluginDescriptor mykey_plugin_descriptor = {
.appid = NFC_SUPPORTED_CARD_PLUGIN_APP_ID,
.ep_api_version = NFC_SUPPORTED_CARD_PLUGIN_API_VERSION,
.entry_point = &mykey_plugin,
};
/* Plugin entry point - must return a pointer to const descriptor */
const FlipperAppPluginDescriptor* mykey_plugin_ep() {
return &mykey_plugin_descriptor;
}

View File

@@ -0,0 +1,475 @@
// Parser for NDEF format data
// Supports multiple NDEF messages and records in same tag
// Parsed types: URI (+ Phone, Mail), Text, BT MAC, Contact, WiFi, Empty
// Documentation and sources indicated where relevant
// Made by @Willy-JL
#include "nfc_supported_card_plugin.h"
#include <nfc/helpers/nfc_util.h>
#include <flipper_application/flipper_application.h>
#include <nfc/protocols/mf_ultralight/mf_ultralight.h>
#define TAG "NDEF"
static bool is_text(const uint8_t* buf, size_t len) {
for(size_t i = 0; i < len; i++) {
const char c = buf[i];
if((c < ' ' || c > '~') && c != '\r' && c != '\n') {
return false;
}
}
return true;
}
static void
print_data(FuriString* str, const char* prefix, const uint8_t* buf, size_t len, bool force_hex) {
if(prefix) furi_string_cat_printf(str, "%s: ", prefix);
if(!force_hex && is_text(buf, len)) {
char* tmp = malloc(len + 1);
memcpy(tmp, buf, len);
tmp[len] = '\0';
furi_string_cat_printf(str, "%s", tmp);
free(tmp);
} else {
for(uint8_t i = 0; i < len; i++) {
furi_string_cat_printf(str, "%02X ", buf[i]);
}
}
furi_string_cat(str, "\n");
}
static void parse_ndef_uri(FuriString* str, const uint8_t* payload, uint32_t payload_len) {
// https://learn.adafruit.com/adafruit-pn532-rfid-nfc/ndef#uri-records-0x55-slash-u-607763
const char* prepends[] = {
[0x00] = "",
[0x01] = "http://www.",
[0x02] = "https://www.",
[0x03] = "http://",
[0x04] = "https://",
[0x05] = "tel:",
[0x06] = "mailto:",
[0x07] = "ftp://anonymous:anonymous@",
[0x08] = "ftp://ftp.",
[0x09] = "ftps://",
[0x0A] = "sftp://",
[0x0B] = "smb://",
[0x0C] = "nfs://",
[0x0D] = "ftp://",
[0x0E] = "dav://",
[0x0F] = "news:",
[0x10] = "telnet://",
[0x11] = "imap:",
[0x12] = "rtsp://",
[0x13] = "urn:",
[0x14] = "pop:",
[0x15] = "sip:",
[0x16] = "sips:",
[0x17] = "tftp:",
[0x18] = "btspp://",
[0x19] = "btl2cap://",
[0x1A] = "btgoep://",
[0x1B] = "tcpobex://",
[0x1C] = "irdaobex://",
[0x1D] = "file://",
[0x1E] = "urn:epc:id:",
[0x1F] = "urn:epc:tag:",
[0x20] = "urn:epc:pat:",
[0x21] = "urn:epc:raw:",
[0x22] = "urn:epc:",
[0x23] = "urn:nfc:",
};
const char* prepend = "";
uint8_t prepend_type = payload[0];
if(prepend_type < COUNT_OF(prepends)) {
prepend = prepends[prepend_type];
}
size_t prepend_len = strlen(prepend);
size_t uri_len = prepend_len + (payload_len - 1);
char* const uri_buf = malloc(uri_len);
memcpy(uri_buf, prepend, prepend_len);
memcpy(uri_buf + prepend_len, payload + 1, payload_len - 1);
char* uri = uri_buf;
const char* type = "URI";
if(strncmp(uri, "http", strlen("http")) == 0) {
type = "URL";
} else if(strncmp(uri, "tel:", strlen("tel:")) == 0) {
type = "Phone";
uri += strlen("tel:");
uri_len -= strlen("tel:");
} else if(strncmp(uri, "mailto:", strlen("mailto:")) == 0) {
type = "Mail";
uri += strlen("mailto:");
uri_len -= strlen("mailto:");
}
furi_string_cat_printf(str, "%s\n", type);
print_data(str, NULL, (uint8_t*)uri, uri_len, false);
free(uri_buf);
}
static void parse_ndef_text(FuriString* str, const uint8_t* payload, uint32_t payload_len) {
furi_string_cat(str, "Text\n");
print_data(str, NULL, payload + 3, payload_len - 3, false);
}
static void parse_ndef_bt(FuriString* str, const uint8_t* payload, uint32_t payload_len) {
furi_string_cat(str, "BT MAC\n");
print_data(str, NULL, payload + 2, payload_len - 2, true);
}
static void parse_ndef_vcard(FuriString* str, const uint8_t* payload, uint32_t payload_len) {
char* tmp = malloc(payload_len + 1);
memcpy(tmp, payload, payload_len);
tmp[payload_len] = '\0';
FuriString* fmt = furi_string_alloc_set(tmp);
free(tmp);
furi_string_trim(fmt);
if(furi_string_start_with(fmt, "BEGIN:VCARD")) {
furi_string_right(fmt, furi_string_search_char(fmt, '\n'));
if(furi_string_end_with(fmt, "END:VCARD")) {
furi_string_left(fmt, furi_string_search_rchar(fmt, '\n'));
}
furi_string_trim(fmt);
if(furi_string_start_with(fmt, "VERSION:")) {
furi_string_right(fmt, furi_string_search_char(fmt, '\n'));
furi_string_trim(fmt);
}
}
furi_string_cat(str, "Contact\n");
print_data(str, NULL, (uint8_t*)furi_string_get_cstr(fmt), furi_string_size(fmt), false);
furi_string_free(fmt);
}
static void parse_ndef_wifi(FuriString* str, const uint8_t* payload, uint32_t payload_len) {
// https://android.googlesource.com/platform/packages/apps/Nfc/+/refs/heads/main/src/com/android/nfc/NfcWifiProtectedSetup.java
#define CREDENTIAL_FIELD_ID (0x100E)
#define SSID_FIELD_ID (0x1045)
#define NETWORK_KEY_FIELD_ID (0x1027)
#define AUTH_TYPE_FIELD_ID (0x1003)
#define AUTH_TYPE_EXPECTED_SIZE (2)
#define AUTH_TYPE_OPEN (0x0001)
#define AUTH_TYPE_WPA_PSK (0x0002)
#define AUTH_TYPE_WPA_EAP (0x0008)
#define AUTH_TYPE_WPA2_EAP (0x0010)
#define AUTH_TYPE_WPA2_PSK (0x0020)
#define AUTH_TYPE_WPA_AND_WPA2_PSK (0x0022)
#define MAX_NETWORK_KEY_SIZE_BYTES (64)
size_t i = 0;
while(i < payload_len) {
uint16_t field_id = nfc_util_bytes2num(payload + i, 2);
i += 2;
uint16_t field_len = nfc_util_bytes2num(payload + i, 2);
i += 2;
if(field_id == CREDENTIAL_FIELD_ID) {
furi_string_cat(str, "WiFi\n");
size_t start_position = i;
while(i < start_position + field_len) {
uint16_t cfg_id = nfc_util_bytes2num(payload + i, 2);
i += 2;
uint16_t cfg_len = nfc_util_bytes2num(payload + i, 2);
i += 2;
if(i + cfg_len > start_position + field_len) {
return;
}
switch(cfg_id) {
case SSID_FIELD_ID:
print_data(str, "SSID", payload + i, cfg_len, false);
i += cfg_len;
break;
case NETWORK_KEY_FIELD_ID:
if(cfg_len > MAX_NETWORK_KEY_SIZE_BYTES) {
return;
}
print_data(str, "PWD", payload + i, cfg_len, false);
i += cfg_len;
break;
case AUTH_TYPE_FIELD_ID:
if(cfg_len != AUTH_TYPE_EXPECTED_SIZE) {
return;
}
short auth_type = nfc_util_bytes2num(payload + i, 2);
i += 2;
const char* auth;
switch(auth_type) {
case AUTH_TYPE_OPEN:
auth = "Open";
break;
case AUTH_TYPE_WPA_PSK:
auth = "WPA Personal";
break;
case AUTH_TYPE_WPA_EAP:
auth = "WPA Enterprise";
break;
case AUTH_TYPE_WPA2_EAP:
auth = "WPA2 Enterprise";
break;
case AUTH_TYPE_WPA2_PSK:
auth = "WPA2 Personal";
break;
case AUTH_TYPE_WPA_AND_WPA2_PSK:
auth = "WPA/WPA2 Personal";
break;
default:
auth = "Unknown";
break;
}
print_data(str, "AUTH", (uint8_t*)auth, strlen(auth), false);
break;
default:
i += cfg_len;
break;
}
}
return;
}
i += field_len;
}
}
static void parse_ndef_payload(
FuriString* str,
uint8_t tnf,
const char* type,
uint8_t type_len,
const uint8_t* payload,
uint32_t payload_len) {
if(!payload_len) {
furi_string_cat(str, "Empty\n");
return;
}
switch(tnf) {
case 0x01: // NFC Forum well-known type [NFC RTD]
if(strncmp("U", type, type_len) == 0) {
parse_ndef_uri(str, payload, payload_len);
} else if(strncmp("T", type, type_len) == 0) {
parse_ndef_text(str, payload, payload_len);
} else {
print_data(str, "Well-known type", (uint8_t*)type, type_len, false);
print_data(str, "Payload", payload, payload_len, false);
}
break;
case 0x02: // Media-type [RFC 2046]
if(strncmp("application/vnd.bluetooth.ep.oob", type, type_len) == 0) {
parse_ndef_bt(str, payload, payload_len);
} else if(strncmp("text/vcard", type, type_len) == 0) {
parse_ndef_vcard(str, payload, payload_len);
} else if(strncmp("application/vnd.wfa.wsc", type, type_len) == 0) {
parse_ndef_wifi(str, payload, payload_len);
} else {
print_data(str, "Media Type", (uint8_t*)type, type_len, false);
print_data(str, "Payload", payload, payload_len, false);
}
break;
case 0x00: // Empty
case 0x03: // Absolute URI [RFC 3986]
case 0x04: // NFC Forum external type [NFC RTD]
case 0x05: // Unknown
case 0x06: // Unchanged
case 0x07: // Reserved
default: // Unknown
// Dump data without parsing
print_data(str, "Type name format", &tnf, 1, true);
print_data(str, "Type", (uint8_t*)type, type_len, false);
print_data(str, "Payload", payload, payload_len, false);
break;
}
}
static const uint8_t* parse_ndef_message(
FuriString* str,
size_t message_num,
const uint8_t* cur,
const uint8_t* message_end) {
// NDEF message and record documentation:
// https://developer.nordicsemi.com/nRF_Connect_SDK/doc/latest/nrf/protocols/nfc/index.html#ndef-message-and-record-format
size_t record_num = 0;
bool last_record = false;
while(cur < message_end) {
// Flags and TNF
uint8_t flags_tnf = *cur++;
// Message Begin should only be set on first record
if(record_num++ && flags_tnf & (1 << 7)) break;
// Message End should only be set on last record
if(last_record) break;
if(flags_tnf & (1 << 6)) last_record = true;
// Chunked Flag not supported
if(flags_tnf & (1 << 5)) break;
// Payload Length field of 1 vs 4 bytes
bool short_record = flags_tnf & (1 << 4);
// Is payload ID length and value present
bool id_present = flags_tnf & (1 << 3);
// Type Name Format 3 bit value
uint8_t tnf = flags_tnf & 0b00000111;
// Type Length
uint8_t type_len = *cur++;
// Payload Length
uint32_t payload_len;
if(short_record) {
payload_len = *cur++;
} else {
payload_len = nfc_util_bytes2num(cur, 4);
cur += 4;
}
// ID Length
uint8_t id_len = 0;
if(id_present) {
id_len = *cur++;
}
// Payload Type
char* type = NULL;
if(type_len) {
type = malloc(type_len);
memcpy(type, cur, type_len);
cur += type_len;
}
// Payload ID
cur += id_len;
furi_string_cat_printf(str, "\e*> M:%d R:%d - ", message_num, record_num);
parse_ndef_payload(str, tnf, type, type_len, cur, payload_len);
cur += payload_len;
free(type);
furi_string_trim(str, "\n");
furi_string_cat(str, "\n\n");
}
return cur;
}
static bool ndef_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 {
// Memory layout documentation:
// https://developer.nordicsemi.com/nRF_Connect_SDK/doc/latest/nrfxlib/nfc/doc/type_2_tag.html#id2
// Double check static values layout
// First 4 static reserved pages for UID, internal and lock bytes
// (Not sure if NDEF cata can be found in cards with different layout)
if(data->page[0].data[0] != 0x04) break;
if(data->page[2].data[1] != 0x48) break; // Internal
if(data->page[2].data[2] != 0x00) break; // Lock bytes
if(data->page[2].data[3] != 0x00) break; // ...
if(data->page[3].data[0] != 0xE1) break; // Capability container
if(data->page[3].data[1] != 0x10) break; // ...
// Data content starts here at 5th page
const uint8_t* cur = &data->page[4].data[0];
const uint8_t* end = &data->page[0].data[0] +
(mf_ultralight_get_pages_total(data->type) * MF_ULTRALIGHT_PAGE_SIZE);
size_t message_num = 0;
// Parse as TLV (see docs above)
while(cur < end) {
switch(*cur++) {
case 0x03: { // NDEF message
if(cur >= end) break;
uint16_t len;
if(*cur < 0xFF) { // 1 byte length
len = *cur++;
} else { // 3 byte length (0xFF marker + 2 byte integer)
if(cur + 2 >= end) {
cur = end;
break;
}
len = nfc_util_bytes2num(++cur, 2);
cur += 2;
}
if(cur + len >= end) {
cur = end;
break;
}
if(message_num++ == 0) {
furi_string_printf(
parsed_data,
"\e#NDEF Format Data\nCard type: %s\n",
mf_ultralight_get_device_name(data, NfcDeviceNameTypeFull));
}
const uint8_t* message_end = cur + len;
cur = parse_ndef_message(parsed_data, message_num, cur, message_end);
if(cur != message_end) cur = end;
break;
}
case 0xFE: // TLV end
cur = end;
if(message_num != 0) parsed = true;
break;
case 0x00: // Padding, has no length, skip
break;
case 0x01: // Lock control
case 0x02: // Memory control
case 0xFD: // Proprietary
// We don't care, skip this TLV block
if(cur >= end) break;
if(*cur < 0xFF) { // 1 byte length
cur += *cur + 1; // Shift by TLV length
} else { // 3 byte length (0xFF marker + 2 byte integer)
if(cur + 2 >= end) {
cur = end;
break;
}
cur += nfc_util_bytes2num(cur + 1, 2) + 3; // Shift by TLV length
}
break;
default: // Unknown, bail to avoid problems
cur = end;
break;
}
}
if(parsed) {
furi_string_trim(parsed_data, "\n");
furi_string_cat(parsed_data, "\n");
} else {
furi_string_reset(parsed_data);
}
} while(false);
return parsed;
}
/* Actual implementation of app<>plugin interface */
static const NfcSupportedCardsPlugin ndef_plugin = {
.protocol = NfcProtocolMfUltralight,
.verify = NULL,
.read = NULL,
.parse = ndef_parse,
};
/* Plugin descriptor to comply with basic plugin specification */
static const FlipperAppPluginDescriptor ndef_plugin_descriptor = {
.appid = NFC_SUPPORTED_CARD_PLUGIN_APP_ID,
.ep_api_version = NFC_SUPPORTED_CARD_PLUGIN_API_VERSION,
.entry_point = &ndef_plugin,
};
/* Plugin entry point - must return a pointer to const descriptor */
const FlipperAppPluginDescriptor* ndef_plugin_ep() {
return &ndef_plugin_descriptor;
}

View File

@@ -4,7 +4,7 @@
#include <nfc/nfc_device.h> #include <nfc/nfc_device.h>
#include <nfc/helpers/nfc_util.h> #include <nfc/helpers/nfc_util.h>
#include <nfc/protocols/mf_classic/mf_classic_poller_sync_api.h> #include <nfc/protocols/mf_classic/mf_classic_poller_sync.h>
#define TAG "Plantain" #define TAG "Plantain"
@@ -91,7 +91,7 @@ static bool plantain_verify_type(Nfc* nfc, MfClassicType type) {
MfClassicAuthContext auth_context; MfClassicAuthContext auth_context;
MfClassicError error = MfClassicError error =
mf_classic_poller_auth(nfc, block_num, &key, MfClassicKeyTypeA, &auth_context); mf_classic_poller_sync_auth(nfc, block_num, &key, MfClassicKeyTypeA, &auth_context);
if(error != MfClassicErrorNone) { if(error != MfClassicErrorNone) {
FURI_LOG_D(TAG, "Failed to read block %u: %d", block_num, error); FURI_LOG_D(TAG, "Failed to read block %u: %d", block_num, error);
break; break;
@@ -119,7 +119,7 @@ static bool plantain_read(Nfc* nfc, NfcDevice* device) {
do { do {
MfClassicType type = MfClassicTypeMini; MfClassicType type = MfClassicTypeMini;
MfClassicError error = mf_classic_poller_detect_type(nfc, &type); MfClassicError error = mf_classic_poller_sync_detect_type(nfc, &type);
if(error != MfClassicErrorNone) break; if(error != MfClassicErrorNone) break;
data->type = type; data->type = type;
@@ -134,7 +134,7 @@ static bool plantain_read(Nfc* nfc, NfcDevice* device) {
FURI_BIT_SET(keys.key_b_mask, i); FURI_BIT_SET(keys.key_b_mask, i);
} }
error = mf_classic_poller_read(nfc, &keys, data); error = mf_classic_poller_sync_read(nfc, &keys, data);
if(error != MfClassicErrorNone) { if(error != MfClassicErrorNone) {
FURI_LOG_W(TAG, "Failed to read data"); FURI_LOG_W(TAG, "Failed to read data");
break; break;
@@ -142,7 +142,7 @@ static bool plantain_read(Nfc* nfc, NfcDevice* device) {
nfc_device_set_data(device, NfcProtocolMfClassic, data); nfc_device_set_data(device, NfcProtocolMfClassic, data);
is_read = true; is_read = mf_classic_is_card_read(data);
} while(false); } while(false);
mf_classic_free(data); mf_classic_free(data);

View File

@@ -0,0 +1,174 @@
// From: https://gitee.com/jadenwu/Saflok_KDF/blob/master/saflok.c
// KDF published and reverse engineered by Jaden Wu
// FZ plugin by @noproto
#include "nfc_supported_card_plugin.h"
#include <flipper_application/flipper_application.h>
#include <nfc/nfc_device.h>
#include <nfc/helpers/nfc_util.h>
#include <nfc/protocols/mf_classic/mf_classic_poller_sync.h>
#include <stdint.h>
#define TAG "Saflok"
#define MAGIC_TABLE_SIZE 192
#define KEY_LENGTH 6
#define UID_LENGTH 4
#define CHECK_SECTOR 1
typedef struct {
uint64_t a;
uint64_t b;
} MfClassicKeyPair;
static MfClassicKeyPair saflok_1k_keys[] = {
{.a = 0x000000000000, .b = 0xffffffffffff}, // 000
{.a = 0x2a2c13cc242a, .b = 0xffffffffffff}, // 001
{.a = 0xffffffffffff, .b = 0xffffffffffff}, // 002
{.a = 0xffffffffffff, .b = 0xffffffffffff}, // 003
{.a = 0x000000000000, .b = 0xffffffffffff}, // 004
{.a = 0x000000000000, .b = 0xffffffffffff}, // 005
{.a = 0x000000000000, .b = 0xffffffffffff}, // 006
{.a = 0x000000000000, .b = 0xffffffffffff}, // 007
{.a = 0x000000000000, .b = 0xffffffffffff}, // 008
{.a = 0x000000000000, .b = 0xffffffffffff}, // 009
{.a = 0x000000000000, .b = 0xffffffffffff}, // 010
{.a = 0x000000000000, .b = 0xffffffffffff}, // 011
{.a = 0x000000000000, .b = 0xffffffffffff}, // 012
{.a = 0x000000000000, .b = 0xffffffffffff}, // 013
{.a = 0x000000000000, .b = 0xffffffffffff}, // 014
{.a = 0x000000000000, .b = 0xffffffffffff}, // 015
};
void generate_saflok_key(const uint8_t* uid, uint8_t* key) {
static const uint8_t magic_table[MAGIC_TABLE_SIZE] = {
0x00, 0x00, 0xAA, 0x00, 0x00, 0x00, 0xF0, 0x57, 0xB3, 0x9E, 0xE3, 0xD8, 0x00, 0x00, 0xAA,
0x00, 0x00, 0x00, 0x96, 0x9D, 0x95, 0x4A, 0xC1, 0x57, 0x00, 0x00, 0xAA, 0x00, 0x00, 0x00,
0x8F, 0x43, 0x58, 0x0D, 0x2C, 0x9D, 0x00, 0x00, 0xAA, 0x00, 0x00, 0x00, 0xFF, 0xCC, 0xE0,
0x05, 0x0C, 0x43, 0x00, 0x00, 0xAA, 0x00, 0x00, 0x00, 0x34, 0x1B, 0x15, 0xA6, 0x90, 0xCC,
0x00, 0x00, 0xAA, 0x00, 0x00, 0x00, 0x89, 0x58, 0x56, 0x12, 0xE7, 0x1B, 0x00, 0x00, 0xAA,
0x00, 0x00, 0x00, 0xBB, 0x74, 0xB0, 0x95, 0x36, 0x58, 0x00, 0x00, 0xAA, 0x00, 0x00, 0x00,
0xFB, 0x97, 0xF8, 0x4B, 0x5B, 0x74, 0x00, 0x00, 0xAA, 0x00, 0x00, 0x00, 0xC9, 0xD1, 0x88,
0x35, 0x9F, 0x92, 0x00, 0x00, 0xAA, 0x00, 0x00, 0x00, 0x8F, 0x92, 0xE9, 0x7F, 0x58, 0x97,
0x00, 0x00, 0xAA, 0x00, 0x00, 0x00, 0x16, 0x6C, 0xA2, 0xB0, 0x9F, 0xD1, 0x00, 0x00, 0xAA,
0x00, 0x00, 0x00, 0x27, 0xDD, 0x93, 0x10, 0x1C, 0x6C, 0x00, 0x00, 0xAA, 0x00, 0x00, 0x00,
0xDA, 0x3E, 0x3F, 0xD6, 0x49, 0xDD, 0x00, 0x00, 0xAA, 0x00, 0x00, 0x00, 0x58, 0xDD, 0xED,
0x07, 0x8E, 0x3E, 0x00, 0x00, 0xAA, 0x00, 0x00, 0x00, 0x5C, 0xD0, 0x05, 0xCF, 0xD9, 0x07,
0x00, 0x00, 0xAA, 0x00, 0x00, 0x00, 0x11, 0x8D, 0xD0, 0x01, 0x87, 0xD0};
uint8_t magic_byte = (uid[3] >> 4) + (uid[2] >> 4) + (uid[0] & 0x0F);
uint8_t magickal_index = (magic_byte & 0x0F) * 12 + 11;
uint8_t temp_key[KEY_LENGTH] = {magic_byte, uid[0], uid[1], uid[2], uid[3], magic_byte};
uint8_t carry_sum = 0;
for(int i = KEY_LENGTH - 1; i >= 0; i--, magickal_index--) {
uint16_t keysum = temp_key[i] + magic_table[magickal_index] + carry_sum;
temp_key[i] = (keysum & 0xFF);
carry_sum = keysum >> 8;
}
memcpy(key, temp_key, KEY_LENGTH);
}
static bool saflok_verify(Nfc* nfc) {
bool verified = false;
do {
const uint8_t block_num = mf_classic_get_first_block_num_of_sector(CHECK_SECTOR);
FURI_LOG_D(TAG, "Saflok: Verifying sector %i", CHECK_SECTOR);
MfClassicKey key = {0};
nfc_util_num2bytes(saflok_1k_keys[CHECK_SECTOR].a, COUNT_OF(key.data), key.data);
MfClassicAuthContext auth_context;
MfClassicError error =
mf_classic_poller_sync_auth(nfc, block_num, &key, MfClassicKeyTypeA, &auth_context);
if(error != MfClassicErrorNone) {
FURI_LOG_D(TAG, "Saflok: Failed to read block %u: %d", block_num, error);
break;
}
verified = true;
} while(false);
return verified;
}
static bool saflok_read(Nfc* nfc, NfcDevice* device) {
FURI_LOG_D(TAG, "Entering Saflok KDF");
furi_assert(nfc);
furi_assert(device);
bool is_read = false;
MfClassicData* data = mf_classic_alloc();
nfc_device_copy_data(device, NfcProtocolMfClassic, data);
do {
MfClassicType type = MfClassicType1k;
MfClassicError error = mf_classic_poller_sync_detect_type(nfc, &type);
if(error != MfClassicErrorNone) break;
data->type = type;
size_t uid_len;
const uint8_t* uid = mf_classic_get_uid(data, &uid_len);
FURI_LOG_D(
TAG, "Saflok: UID identified: %02X%02X%02X%02X", uid[0], uid[1], uid[2], uid[3]);
if(uid_len != UID_LENGTH) break;
uint8_t key[KEY_LENGTH];
generate_saflok_key(uid, key);
uint64_t num_key = nfc_util_bytes2num(key, KEY_LENGTH);
FURI_LOG_D(TAG, "Saflok: Key generated for UID: %012llX", num_key);
for(size_t i = 0; i < mf_classic_get_total_sectors_num(data->type); i++) {
if(saflok_1k_keys[i].a == 0x000000000000) {
saflok_1k_keys[i].a = num_key;
}
}
MfClassicDeviceKeys keys = {};
for(size_t i = 0; i < mf_classic_get_total_sectors_num(data->type); i++) {
nfc_util_num2bytes(saflok_1k_keys[i].a, sizeof(MfClassicKey), keys.key_a[i].data);
FURI_BIT_SET(keys.key_a_mask, i);
nfc_util_num2bytes(saflok_1k_keys[i].b, sizeof(MfClassicKey), keys.key_b[i].data);
FURI_BIT_SET(keys.key_b_mask, i);
}
error = mf_classic_poller_sync_read(nfc, &keys, data);
if(error != MfClassicErrorNone) {
FURI_LOG_W(TAG, "Failed to read data");
break;
}
nfc_device_set_data(device, NfcProtocolMfClassic, data);
is_read = mf_classic_is_card_read(data);
} while(false);
mf_classic_free(data);
return is_read;
}
/* Actual implementation of app<>plugin interface */
static const NfcSupportedCardsPlugin saflok_plugin = {
.protocol = NfcProtocolMfClassic,
.verify = saflok_verify,
.read = saflok_read,
// KDF mode
.parse = NULL,
};
/* Plugin descriptor to comply with basic plugin specification */
static const FlipperAppPluginDescriptor saflok_plugin_descriptor = {
.appid = NFC_SUPPORTED_CARD_PLUGIN_APP_ID,
.ep_api_version = NFC_SUPPORTED_CARD_PLUGIN_API_VERSION,
.entry_point = &saflok_plugin,
};
/* Plugin entry point - must return a pointer to const descriptor */
const FlipperAppPluginDescriptor* saflok_plugin_ep() {
return &saflok_plugin_descriptor;
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -4,7 +4,7 @@
#include <nfc/nfc_device.h> #include <nfc/nfc_device.h>
#include <nfc/helpers/nfc_util.h> #include <nfc/helpers/nfc_util.h>
#include <nfc/protocols/mf_classic/mf_classic_poller_sync_api.h> #include <nfc/protocols/mf_classic/mf_classic_poller_sync.h>
#define TAG "TwoCities" #define TAG "TwoCities"
@@ -49,7 +49,7 @@ bool two_cities_verify(Nfc* nfc) {
MfClassicAuthContext auth_ctx = {}; MfClassicAuthContext auth_ctx = {};
MfClassicError error = MfClassicError error =
mf_classic_poller_auth(nfc, block_num, &key, MfClassicKeyTypeA, &auth_ctx); mf_classic_poller_sync_auth(nfc, block_num, &key, MfClassicKeyTypeA, &auth_ctx);
if(error != MfClassicErrorNone) { if(error != MfClassicErrorNone) {
FURI_LOG_D(TAG, "Failed to read block %u: %d", block_num, error); FURI_LOG_D(TAG, "Failed to read block %u: %d", block_num, error);
break; break;
@@ -72,7 +72,7 @@ static bool two_cities_read(Nfc* nfc, NfcDevice* device) {
do { do {
MfClassicType type = MfClassicTypeMini; MfClassicType type = MfClassicTypeMini;
MfClassicError error = mf_classic_poller_detect_type(nfc, &type); MfClassicError error = mf_classic_poller_sync_detect_type(nfc, &type);
if(error != MfClassicErrorNone) break; if(error != MfClassicErrorNone) break;
data->type = type; data->type = type;
@@ -84,7 +84,7 @@ static bool two_cities_read(Nfc* nfc, NfcDevice* device) {
FURI_BIT_SET(keys.key_b_mask, i); FURI_BIT_SET(keys.key_b_mask, i);
} }
error = mf_classic_poller_read(nfc, &keys, data); error = mf_classic_poller_sync_read(nfc, &keys, data);
if(error != MfClassicErrorNone) { if(error != MfClassicErrorNone) {
FURI_LOG_W(TAG, "Failed to read data"); FURI_LOG_W(TAG, "Failed to read data");
break; break;
@@ -92,7 +92,7 @@ static bool two_cities_read(Nfc* nfc, NfcDevice* device) {
nfc_device_set_data(device, NfcProtocolMfClassic, data); nfc_device_set_data(device, NfcProtocolMfClassic, data);
is_read = true; is_read = mf_classic_is_card_read(data);
} while(false); } while(false);
mf_classic_free(data); mf_classic_free(data);

View File

@@ -0,0 +1,154 @@
/*
* Parser for Umarsh card (Russia).
*
* Copyright 2023 Leptoptilos <leptoptilos@icloud.com>
* Thanks https://github.com/krolchonok for the provided dumps and their analysis
*
* Note: All meaningful data is stored in sectors 0, 8 and 12, reading data
* from which is possible only with the B key. The key B for these sectors
* is unique for each card. To get it, you should use a nested attack.
* More info about Umarsh cards: https://github.com/metrodroid/metrodroid/wiki/Umarsh
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "core/core_defines.h"
#include "nfc_supported_card_plugin.h"
#include "protocols/mf_classic/mf_classic.h"
#include <flipper_application/flipper_application.h>
#include <nfc/nfc_device.h>
#include <nfc/helpers/nfc_util.h>
#include <nfc/protocols/mf_classic/mf_classic_poller_sync.h>
#include <furi_hal_rtc.h>
#define TAG "Umarsh"
bool parse_datetime(uint16_t date, FuriHalRtcDateTime* result) {
result->year = 2000 + (date >> 9);
result->month = date >> 5 & 0x0F;
result->day = date & 0x1F;
return (date != 0);
}
static bool umarsh_parse(const NfcDevice* device, FuriString* parsed_data) {
furi_assert(device);
const MfClassicData* data = nfc_device_get_data(device, NfcProtocolMfClassic);
bool parsed = false;
do {
// Verify card type
if(data->type != MfClassicType1k) break;
const uint8_t ticket_sector = 8;
const uint8_t ticket_sector_start_block_number =
mf_classic_get_first_block_num_of_sector(ticket_sector);
// Validate specific for Umarsh ticket sector header
const uint8_t* block_start_ptr = &data->block[ticket_sector_start_block_number].data[0];
const uint32_t header_part_0 = nfc_util_bytes2num(block_start_ptr, 4);
const uint32_t header_part_1 = nfc_util_bytes2num(block_start_ptr + 4, 4);
if((header_part_0 + header_part_1) != 0xFFFFFFFF) break;
// Data parsing from block 1
block_start_ptr = &data->block[ticket_sector_start_block_number + 1].data[0];
const uint16_t expiry_date = nfc_util_bytes2num(block_start_ptr + 1, 2);
const uint8_t region_number = (((block_start_ptr[8] >> 5) & 0x07) << 4) |
(block_start_ptr[12] & 0x0F);
const uint8_t refill_counter = nfc_util_bytes2num(block_start_ptr + 7, 1);
const uint32_t card_number = nfc_util_bytes2num(block_start_ptr + 8, 4) & 0x3FFFFFFF;
if(card_number == 0) break;
// Data parsing from block 2
block_start_ptr = &data->block[ticket_sector_start_block_number + 2].data[0];
const uint16_t valid_to = nfc_util_bytes2num(block_start_ptr, 2);
const uint32_t terminal_number = nfc_util_bytes2num(block_start_ptr + 3, 3);
const uint16_t last_refill_date = nfc_util_bytes2num(block_start_ptr + 6, 2);
const uint16_t balance_rub = (nfc_util_bytes2num(block_start_ptr + 8, 2)) & 0x7FFF;
const uint8_t balance_kop = nfc_util_bytes2num(block_start_ptr + 10, 1) & 0x7F;
FuriHalRtcDateTime expiry_datetime;
bool is_expiry_datetime_valid = parse_datetime(expiry_date, &expiry_datetime);
FuriHalRtcDateTime valid_to_datetime;
bool is_valid_to_datetime_valid = parse_datetime(valid_to, &valid_to_datetime);
FuriHalRtcDateTime last_refill_datetime;
bool is_last_refill_datetime_valid =
parse_datetime(last_refill_date, &last_refill_datetime);
furi_string_cat_printf(
parsed_data,
"\e#Umarsh\nCard number: %lu\nRegion: %02u\nTerminal number: %lu\nRefill counter: %u\nBalance: %u.%02u RUR",
card_number,
region_number,
terminal_number,
refill_counter,
balance_rub,
balance_kop);
if(is_expiry_datetime_valid)
furi_string_cat_printf(
parsed_data,
"\nExpires: %02u.%02u.%u",
expiry_datetime.day,
expiry_datetime.month,
expiry_datetime.year);
if(is_valid_to_datetime_valid)
furi_string_cat_printf(
parsed_data,
"\nValid to: %02u.%02u.%u",
valid_to_datetime.day,
valid_to_datetime.month,
valid_to_datetime.year);
if(is_last_refill_datetime_valid)
furi_string_cat_printf(
parsed_data,
"\nLast refill: %02u.%02u.%u",
last_refill_datetime.day,
last_refill_datetime.month,
last_refill_datetime.year);
parsed = true;
} while(false);
return parsed;
}
/* Actual implementation of app<>plugin interface */
static const NfcSupportedCardsPlugin umarsh_plugin = {
.protocol = NfcProtocolMfClassic,
.verify = NULL,
.read = NULL,
.parse = umarsh_parse,
};
/* Plugin descriptor to comply with basic plugin specification */
static const FlipperAppPluginDescriptor umarsh_plugin_descriptor = {
.appid = NFC_SUPPORTED_CARD_PLUGIN_APP_ID,
.ep_api_version = NFC_SUPPORTED_CARD_PLUGIN_API_VERSION,
.entry_point = &umarsh_plugin,
};
/* Plugin entry point - must return a pointer to const descriptor */
const FlipperAppPluginDescriptor* umarsh_plugin_ep() {
return &umarsh_plugin_descriptor;
}

View File

@@ -0,0 +1,193 @@
/*
* Parser for WashCity MarkItaly Card (Europe).
*
* Copyright 2023 Filipe Polido (YaBaPT) <polido@gmail.com>
*
* Based on MetroMoney by Leptoptilos <leptoptilos@icloud.com>
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "nfc_supported_card_plugin.h"
#include "protocols/mf_classic/mf_classic.h"
#include <flipper_application/flipper_application.h>
#include <nfc/nfc_device.h>
#include <nfc/helpers/nfc_util.h>
#include <nfc/protocols/mf_classic/mf_classic_poller_sync.h>
#define TAG "WashCity"
typedef struct {
uint64_t a;
uint64_t b;
} MfClassicKeyPair;
static const MfClassicKeyPair washcity_1k_keys[] = {
{.a = 0xA0A1A2A3A4A5, .b = 0x010155010100}, // Sector 00
{.a = 0xC78A3D0E1BCD, .b = 0xFFFFFFFFFFFF}, // Sector 01
{.a = 0xC78A3D0E0000, .b = 0xFFFFFFFFFFFF}, // Sector 02
{.a = 0xC78A3D0E0000, .b = 0xFFFFFFFFFFFF}, // Sector 03
{.a = 0xC78A3D0E0000, .b = 0xFFFFFFFFFFFF}, // Sector 04
{.a = 0xC78A3D0E0000, .b = 0xFFFFFFFFFFFF}, // Sector 05
{.a = 0xC78A3D0E0000, .b = 0xFFFFFFFFFFFF}, // Sector 06
{.a = 0xC78A3D0E0000, .b = 0xFFFFFFFFFFFF}, // Sector 07
{.a = 0xC78A3D0E0000, .b = 0xFFFFFFFFFFFF}, // Sector 08
{.a = 0x010155010100, .b = 0xFFFFFFFFFFFF}, // Sector 09
{.a = 0x010155010100, .b = 0xFFFFFFFFFFFF}, // Sector 10
{.a = 0x010155010100, .b = 0xFFFFFFFFFFFF}, // Sector 11
{.a = 0x010155010100, .b = 0xFFFFFFFFFFFF}, // Sector 12
{.a = 0x010155010100, .b = 0xFFFFFFFFFFFF}, // Sector 13
{.a = 0x010155010100, .b = 0xFFFFFFFFFFFF}, // Sector 14
{.a = 0x010155010100, .b = 0xFFFFFFFFFFFF}, // Sector 15
};
static bool washcity_verify(Nfc* nfc) {
bool verified = false;
do {
const uint8_t ticket_sector_number = 0;
const uint8_t ticket_block_number =
mf_classic_get_first_block_num_of_sector(ticket_sector_number) + 1;
FURI_LOG_D(TAG, "Verifying sector %u", ticket_sector_number);
MfClassicKey key = {0};
nfc_util_num2bytes(washcity_1k_keys[ticket_sector_number].a, COUNT_OF(key.data), key.data);
MfClassicAuthContext auth_context;
MfClassicError error = mf_classic_poller_sync_auth(
nfc, ticket_block_number, &key, MfClassicKeyTypeA, &auth_context);
if(error != MfClassicErrorNone) {
FURI_LOG_D(TAG, "Failed to read block %u: %d", ticket_block_number, error);
break;
}
verified = true;
} while(false);
return verified;
}
static bool washcity_read(Nfc* nfc, NfcDevice* device) {
furi_assert(nfc);
furi_assert(device);
bool is_read = false;
MfClassicData* data = mf_classic_alloc();
nfc_device_copy_data(device, NfcProtocolMfClassic, data);
do {
MfClassicType type = MfClassicTypeMini;
MfClassicError error = mf_classic_poller_sync_detect_type(nfc, &type);
if(error != MfClassicErrorNone) break;
data->type = type;
if(type != MfClassicType1k) break;
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++) {
nfc_util_num2bytes(washcity_1k_keys[i].a, sizeof(MfClassicKey), keys.key_a[i].data);
FURI_BIT_SET(keys.key_a_mask, i);
nfc_util_num2bytes(washcity_1k_keys[i].b, sizeof(MfClassicKey), keys.key_b[i].data);
FURI_BIT_SET(keys.key_b_mask, i);
}
error = mf_classic_poller_sync_read(nfc, &keys, data);
if(error != MfClassicErrorNone) {
FURI_LOG_W(TAG, "Failed to read data");
break;
}
nfc_device_set_data(device, NfcProtocolMfClassic, data);
is_read = mf_classic_is_card_read(data);
} while(false);
mf_classic_free(data);
return is_read;
}
static bool washcity_parse(const NfcDevice* device, FuriString* parsed_data) {
furi_assert(device);
const MfClassicData* data = nfc_device_get_data(device, NfcProtocolMfClassic);
bool parsed = false;
do {
// Verify key
const uint8_t ticket_sector_number = 1;
const uint8_t ticket_block_number = 0;
const MfClassicSectorTrailer* sec_tr =
mf_classic_get_sector_trailer_by_sector(data, ticket_sector_number);
const uint64_t key = nfc_util_bytes2num(sec_tr->key_a.data, COUNT_OF(sec_tr->key_a.data));
if(key != washcity_1k_keys[ticket_sector_number].a) break;
// Parse data
const uint8_t start_block_num =
mf_classic_get_first_block_num_of_sector(ticket_sector_number);
const uint8_t* block_start_ptr =
&data->block[start_block_num + ticket_block_number].data[0];
uint32_t balance = nfc_util_bytes2num(block_start_ptr + 2, 2);
uint32_t balance_usd = balance / 100;
uint8_t balance_cents = balance % 100;
size_t uid_len = 0;
const uint8_t* uid = mf_classic_get_uid(data, &uid_len);
// Card Number is printed in HEX (equal to UID)
uint64_t card_number = nfc_util_bytes2num(uid, uid_len);
furi_string_printf(
parsed_data,
"\e#WashCity\nCard number: %0*llX\nBalance: %lu.%02u EUR",
uid_len * 2,
card_number,
balance_usd,
balance_cents);
parsed = true;
} while(false);
return parsed;
}
/* Actual implementation of app<>plugin interface */
static const NfcSupportedCardsPlugin washcity_plugin = {
.protocol = NfcProtocolMfClassic,
.verify = washcity_verify,
.read = washcity_read,
.parse = washcity_parse,
};
/* Plugin descriptor to comply with basic plugin specification */
static const FlipperAppPluginDescriptor washcity_plugin_descriptor = {
.appid = NFC_SUPPORTED_CARD_PLUGIN_APP_ID,
.ep_api_version = NFC_SUPPORTED_CARD_PLUGIN_API_VERSION,
.entry_point = &washcity_plugin,
};
/* Plugin entry point - must return a pointer to const descriptor */
const FlipperAppPluginDescriptor* washcity_plugin_ep() {
return &washcity_plugin_descriptor;
}

View File

@@ -0,0 +1,251 @@
/*
* Parser for Zolotaya Korona card (Russia).
*
* Copyright 2023 Leptoptilos <leptoptilos@icloud.com>
*
* More info about Zolotaya Korona cards: https://github.com/metrodroid/metrodroid/wiki/Zolotaya-Korona
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "furi_hal_rtc.h"
#include "nfc_supported_card_plugin.h"
#include "protocols/mf_classic/mf_classic.h"
#include <flipper_application/flipper_application.h>
#include <nfc/nfc_device.h>
#include <nfc/helpers/nfc_util.h>
#include <nfc/protocols/mf_classic/mf_classic_poller_sync.h>
#define TAG "Zolotaya Korona"
#define TRIP_SECTOR_NUM (4)
#define PURSE_SECTOR_NUM (6)
#define INFO_SECTOR_NUM (15)
// Sector 15 data. Byte [11] contains the mistake. If byte [11] was 0xEF, bytes [1-18] means "ЗАО Золотая Корона"
static const uint8_t info_sector_signature[] = {0xE2, 0x87, 0x80, 0x8E, 0x20, 0x87, 0xAE,
0xAB, 0xAE, 0xF2, 0xA0, 0xEF, 0x20, 0x8A,
0xAE, 0xE0, 0xAE, 0xAD, 0xA0, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00};
#define FURI_HAL_RTC_SECONDS_PER_MINUTE 60
#define FURI_HAL_RTC_SECONDS_PER_HOUR (FURI_HAL_RTC_SECONDS_PER_MINUTE * 60)
#define FURI_HAL_RTC_SECONDS_PER_DAY (FURI_HAL_RTC_SECONDS_PER_HOUR * 24)
#define FURI_HAL_RTC_EPOCH_START_YEAR 1970
void timestamp_to_datetime(uint32_t timestamp, FuriHalRtcDateTime* datetime) {
uint32_t days = timestamp / FURI_HAL_RTC_SECONDS_PER_DAY;
uint32_t seconds_in_day = timestamp % FURI_HAL_RTC_SECONDS_PER_DAY;
datetime->year = FURI_HAL_RTC_EPOCH_START_YEAR;
while(days >= furi_hal_rtc_get_days_per_year(datetime->year)) {
days -= furi_hal_rtc_get_days_per_year(datetime->year);
(datetime->year)++;
}
datetime->month = 1;
while(days >= furi_hal_rtc_get_days_per_month(
furi_hal_rtc_is_leap_year(datetime->year), datetime->month)) {
days -= furi_hal_rtc_get_days_per_month(
furi_hal_rtc_is_leap_year(datetime->year), datetime->month);
(datetime->month)++;
}
datetime->day = days + 1;
datetime->hour = seconds_in_day / FURI_HAL_RTC_SECONDS_PER_HOUR;
datetime->minute =
(seconds_in_day % FURI_HAL_RTC_SECONDS_PER_HOUR) / FURI_HAL_RTC_SECONDS_PER_MINUTE;
datetime->second = seconds_in_day % FURI_HAL_RTC_SECONDS_PER_MINUTE;
}
uint64_t bytes2num_bcd(const uint8_t* src, uint8_t len_bytes, bool* is_bcd) {
furi_assert(src);
furi_assert(len_bytes <= 9);
uint64_t result = 0;
*is_bcd = true;
for(uint8_t i = 0; i < len_bytes; i++) {
if(((src[i] / 16) > 9) || ((src[i] % 16) > 9)) *is_bcd = false;
result *= 10;
result += src[i] / 16;
result *= 10;
result += src[i] % 16;
}
return result;
}
static bool zolotaya_korona_parse(const NfcDevice* device, FuriString* parsed_data) {
furi_assert(device);
const MfClassicData* data = nfc_device_get_data(device, NfcProtocolMfClassic);
bool parsed = false;
do {
// Verify info sector data
const uint8_t start_info_block_number =
mf_classic_get_first_block_num_of_sector(INFO_SECTOR_NUM);
const uint8_t* block_start_ptr = &data->block[start_info_block_number].data[0];
bool verified = true;
for(uint8_t i = 0; i < sizeof(info_sector_signature); i++) {
if(i == 16) {
block_start_ptr = &data->block[start_info_block_number + 1].data[0];
}
if(block_start_ptr[i % 16] != info_sector_signature[i]) {
verified = false;
break;
}
}
if(!verified) break;
// Parse data
// INFO SECTOR
// block 1
const uint8_t region_number = bytes2num_bcd(block_start_ptr + 10, 1, &verified);
// block 2
block_start_ptr = &data->block[start_info_block_number + 2].data[4];
const uint16_t card_number_prefix = bytes2num_bcd(block_start_ptr, 2, &verified);
const uint64_t card_number_postfix = bytes2num_bcd(block_start_ptr + 2, 8, &verified) / 10;
// TRIP SECTOR
const uint8_t start_trip_block_number =
mf_classic_get_first_block_num_of_sector(TRIP_SECTOR_NUM);
// block 0
block_start_ptr = &data->block[start_trip_block_number].data[7];
const uint8_t status = block_start_ptr[0] % 16;
const uint16_t sequence_number = nfc_util_bytes2num(block_start_ptr + 1, 2);
const uint8_t discount_code = nfc_util_bytes2num(block_start_ptr + 3, 1);
// block 1: refill block
block_start_ptr = &data->block[start_trip_block_number + 1].data[1];
const uint16_t refill_machine_id = nfc_util_bytes2num_little_endian(block_start_ptr, 2);
const uint32_t last_refill_timestamp =
nfc_util_bytes2num_little_endian(block_start_ptr + 2, 4);
const uint32_t last_refill_amount =
nfc_util_bytes2num_little_endian(block_start_ptr + 6, 4);
const uint32_t last_refill_amount_rub = last_refill_amount / 100;
const uint8_t last_refill_amount_kop = last_refill_amount % 100;
const uint16_t refill_counter = nfc_util_bytes2num_little_endian(block_start_ptr + 10, 2);
FuriHalRtcDateTime last_refill_datetime = {0};
timestamp_to_datetime(last_refill_timestamp, &last_refill_datetime);
// block 2: trip block
block_start_ptr = &data->block[start_trip_block_number + 2].data[0];
const char validator_first_letter =
nfc_util_bytes2num_little_endian(block_start_ptr + 1, 1);
const uint32_t validator_id = bytes2num_bcd(block_start_ptr + 2, 3, &verified);
const uint32_t last_trip_timestamp =
nfc_util_bytes2num_little_endian(block_start_ptr + 6, 4);
const uint8_t track_number = nfc_util_bytes2num_little_endian(block_start_ptr + 10, 1);
const uint32_t prev_balance = nfc_util_bytes2num_little_endian(block_start_ptr + 11, 4);
const uint32_t prev_balance_rub = prev_balance / 100;
const uint8_t prev_balance_kop = prev_balance % 100;
FuriHalRtcDateTime last_trip_datetime = {0};
timestamp_to_datetime(last_trip_timestamp, &last_trip_datetime);
// PARSE DATA FROM PURSE SECTOR
const uint8_t start_purse_block_number =
mf_classic_get_first_block_num_of_sector(PURSE_SECTOR_NUM);
block_start_ptr = &data->block[start_purse_block_number].data[0];
// block 0
const uint32_t balance = nfc_util_bytes2num_little_endian(block_start_ptr, 4);
uint32_t balance_rub = balance / 100;
uint8_t balance_kop = balance % 100;
furi_string_cat_printf(
parsed_data,
"\e#Zolotaya korona\nCard number: %u%015llu\nRegion: %u\nBalance: %lu.%02u RUR\nPrev. balance: %lu.%02u RUR",
card_number_prefix,
card_number_postfix,
region_number,
balance_rub,
balance_kop,
prev_balance_rub,
prev_balance_kop);
furi_string_cat_printf(
parsed_data,
"\nLast refill amount: %lu.%02u RUR\nRefill counter: %u\nLast refill: %u.%02u.%02u %02u:%02u\nRefill machine id: %u",
last_refill_amount_rub,
last_refill_amount_kop,
refill_counter,
last_refill_datetime.day,
last_refill_datetime.month,
last_refill_datetime.year,
last_refill_datetime.hour,
last_refill_datetime.minute,
refill_machine_id);
furi_string_cat_printf(
parsed_data,
"\nLast trip: %u.%02u.%02u %02u:%02u\nTrack number: %u\nValidator: %c%06lu",
last_trip_datetime.day,
last_trip_datetime.month,
last_trip_datetime.year,
last_trip_datetime.hour,
last_trip_datetime.minute,
track_number,
validator_first_letter,
validator_id);
if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) {
furi_string_cat_printf(
parsed_data,
"\nStatus: %u\nSequence num: %u\nDiscount code: %u",
status,
sequence_number,
discount_code);
}
parsed = true;
} while(false);
return parsed;
}
/* Actual implementation of app<>plugin interface */
static const NfcSupportedCardsPlugin zolotaya_korona_plugin = {
.protocol = NfcProtocolMfClassic,
.verify = NULL,
.read = NULL,
.parse = zolotaya_korona_parse,
};
/* Plugin descriptor to comply with basic plugin specification */
static const FlipperAppPluginDescriptor zolotaya_korona_plugin_descriptor = {
.appid = NFC_SUPPORTED_CARD_PLUGIN_APP_ID,
.ep_api_version = NFC_SUPPORTED_CARD_PLUGIN_API_VERSION,
.entry_point = &zolotaya_korona_plugin,
};
/* Plugin entry point - must return a pointer to const descriptor */
const FlipperAppPluginDescriptor* zolotaya_korona_plugin_ep() {
return &zolotaya_korona_plugin_descriptor;
}

View File

@@ -0,0 +1,166 @@
/*
* Parser for Zolotaya Korona Online card (Russia).
* Tariffs research by DNZ1393
*
* Copyright 2023 Leptoptilos <leptoptilos@icloud.com>
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "furi_hal_rtc.h"
#include "nfc_supported_card_plugin.h"
#include "protocols/mf_classic/mf_classic.h"
#include <flipper_application/flipper_application.h>
#include <nfc/nfc_device.h>
#include <nfc/helpers/nfc_util.h>
#include <nfc/protocols/mf_classic/mf_classic_poller_sync.h>
#define TAG "Zolotaya Korona Online"
#define TRIP_SECTOR_NUM (4)
#define INFO_SECTOR_NUM (15)
uint64_t bytes2num_bcd(const uint8_t* src, uint8_t len_bytes, bool* is_bcd) {
furi_assert(src);
furi_assert(len_bytes <= 9);
uint64_t result = 0;
*is_bcd = true;
for(uint8_t i = 0; i < len_bytes; i++) {
if(((src[i] / 16) > 9) || ((src[i] % 16) > 9)) *is_bcd = false;
result *= 10;
result += src[i] / 16;
result *= 10;
result += src[i] % 16;
}
return result;
}
bool parse_online_card_tariff(uint16_t tariff_num, FuriString* tariff_name) {
bool tariff_parsed = false;
switch(tariff_num) {
case 0x0100:
furi_string_set_str(tariff_name, "Standart (online)");
tariff_parsed = true;
break;
case 0x0101:
case 0x0121:
furi_string_set_str(tariff_name, "Standart (airtag)");
tariff_parsed = true;
break;
case 0x0401:
furi_string_set_str(tariff_name, "Student (50%% discount)");
tariff_parsed = true;
break;
case 0x0402:
furi_string_set_str(tariff_name, "Student (travel)");
tariff_parsed = true;
break;
case 0x0002:
furi_string_set_str(tariff_name, "School (50%% discount)");
tariff_parsed = true;
break;
case 0x0505:
furi_string_set_str(tariff_name, "Social (large families)");
tariff_parsed = true;
break;
case 0x0528:
furi_string_set_str(tariff_name, "Social (handicapped)");
tariff_parsed = true;
break;
default:
furi_string_set_str(tariff_name, "Unknown");
tariff_parsed = false;
break;
}
return tariff_parsed;
}
static bool zolotaya_korona_online_parse(const NfcDevice* device, FuriString* parsed_data) {
furi_assert(device);
const MfClassicData* data = nfc_device_get_data(device, NfcProtocolMfClassic);
bool parsed = false;
do {
// Verify info sector data (card number prefix)
const uint8_t start_trip_block_number =
mf_classic_get_first_block_num_of_sector(TRIP_SECTOR_NUM);
const uint8_t start_info_block_number =
mf_classic_get_first_block_num_of_sector(INFO_SECTOR_NUM);
const uint8_t* block_start_ptr = &data->block[start_info_block_number].data[3];
// Validate card number
bool is_bcd;
const uint16_t card_number_prefix = bytes2num_bcd(block_start_ptr, 2, &is_bcd);
if(!is_bcd) break;
if(card_number_prefix != 9643) break;
const uint64_t card_number_postfix = bytes2num_bcd(block_start_ptr + 2, 8, &is_bcd) / 10;
if(!is_bcd) break;
// Parse data
FuriString* tariff_name = furi_string_alloc();
block_start_ptr = &data->block[start_info_block_number].data[1];
const uint16_t tariff = nfc_util_bytes2num(block_start_ptr, 2);
parse_online_card_tariff(tariff, tariff_name);
block_start_ptr = &data->block[start_trip_block_number].data[0];
const uint8_t region_number = nfc_util_bytes2num(block_start_ptr, 1);
furi_string_cat_printf(
parsed_data,
"\e#Zolotaya korona\nCard number: %u%015llu\nTariff: %02X.%02X: %s\nRegion: %u\n",
card_number_prefix,
card_number_postfix,
tariff / 256,
tariff % 256,
furi_string_get_cstr(tariff_name),
region_number);
furi_string_free(tariff_name);
parsed = true;
} while(false);
return parsed;
}
/* Actual implementation of app<>plugin interface */
static const NfcSupportedCardsPlugin zolotaya_korona_online_plugin = {
.protocol = NfcProtocolMfClassic,
.verify = NULL,
.read = NULL,
.parse = zolotaya_korona_online_parse,
};
/* Plugin descriptor to comply with basic plugin specification */
static const FlipperAppPluginDescriptor zolotaya_korona_online_plugin_descriptor = {
.appid = NFC_SUPPORTED_CARD_PLUGIN_APP_ID,
.ep_api_version = NFC_SUPPORTED_CARD_PLUGIN_API_VERSION,
.entry_point = &zolotaya_korona_online_plugin,
};
/* Plugin entry point - must return a pointer to const descriptor */
const FlipperAppPluginDescriptor* zolotaya_korona_online_plugin_ep() {
return &zolotaya_korona_online_plugin_descriptor;
}

View File

@@ -3823,3 +3823,45 @@ F72CD208FDF9
2158E314C3DF 2158E314C3DF
# 1k WALDORF ASTORIA # 1k WALDORF ASTORIA
011C6CF459E8 011C6CF459E8
# 1k HAWAII HOTEL
2CAD8A83DF28
555D8BBC2D3E
78DF1176C8FD
ADC169F922CB
# Volgograd (Russia) Volna transport cards keys
2B787A063D5D
D37C8F1793F7
# Bandai Namco Passport [fka Banapassport] / Sega Aime Card
6090D00632F5
019761AA8082
574343467632
A99164400748
62742819AD7C
CC5075E42BA1
B9DF35A0814C
8AF9C718F23D
58CD5C3673CB
FC80E88EB88C
7A3CDAD7C023
30424C029001
024E4E44001F
ECBBFA57C6AD
4757698143BD
1D30972E6485
F8526D1A8D6D
1300EC8C7E80
F80A65A87FFA
DEB06ED4AF8E
4AD96BF28190
000390014D41
0800F9917CB0
730050555253
4146D4A956C4
131157FBB126
E69DD9015A43
337237F254D5
9A8389F32FBF
7B8FB4A7100B
C8382A233993
7B304F2A12A6
FC9418BF788B

View File

@@ -24,6 +24,10 @@ ADD_SCENE(nfc, field, Field)
ADD_SCENE(nfc, retry_confirm, RetryConfirm) ADD_SCENE(nfc, retry_confirm, RetryConfirm)
ADD_SCENE(nfc, exit_confirm, ExitConfirm) ADD_SCENE(nfc, exit_confirm, ExitConfirm)
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)

View File

@@ -10,8 +10,8 @@ void nfc_scene_delete_success_on_enter(void* context) {
// Setup view // Setup view
Popup* popup = nfc->popup; Popup* popup = nfc->popup;
popup_set_icon(popup, 0, 2, &I_DolphinMafia_115x62); popup_set_icon(popup, 0, 2, &I_DolphinMafia_119x62);
popup_set_header(popup, "Deleted", 83, 19, AlignLeft, AlignBottom); popup_set_header(popup, "Deleted", 80, 19, AlignLeft, AlignBottom);
popup_set_timeout(popup, 1500); popup_set_timeout(popup, 1500);
popup_set_context(popup, nfc); popup_set_context(popup, nfc);
popup_set_callback(popup, nfc_scene_delete_success_popup_callback); popup_set_callback(popup, nfc_scene_delete_success_popup_callback);

View File

@@ -17,8 +17,9 @@ void nfc_scene_detect_on_enter(void* context) {
// Setup view // Setup view
popup_reset(instance->popup); popup_reset(instance->popup);
popup_set_header(instance->popup, "Reading", 97, 15, AlignCenter, AlignTop);
popup_set_text( popup_set_text(
instance->popup, "Apply card to\nFlipper's back", 97, 24, AlignCenter, AlignTop); instance->popup, "Apply card to\nFlipper's back", 97, 27, AlignCenter, AlignTop);
popup_set_icon(instance->popup, 0, 8, &I_NFC_manual_60x50); popup_set_icon(instance->popup, 0, 8, &I_NFC_manual_60x50);
view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewPopup); view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewPopup);
@@ -37,6 +38,7 @@ bool nfc_scene_detect_on_event(void* context, SceneManagerEvent event) {
if(event.type == SceneManagerEventTypeCustom) { if(event.type == SceneManagerEventTypeCustom) {
if(event.event == NfcCustomEventWorkerExit) { if(event.event == NfcCustomEventWorkerExit) {
if(instance->protocols_detected_num > 1) { if(instance->protocols_detected_num > 1) {
notification_message(instance->notifications, &sequence_single_vibro);
scene_manager_next_scene(instance->scene_manager, NfcSceneSelectProtocol); scene_manager_next_scene(instance->scene_manager, NfcSceneSelectProtocol);
} else { } else {
scene_manager_next_scene(instance->scene_manager, NfcSceneRead); scene_manager_next_scene(instance->scene_manager, NfcSceneRead);

View File

@@ -12,9 +12,8 @@ void nfc_scene_exit_confirm_on_enter(void* context) {
dialog_ex_set_left_button_text(dialog_ex, "Exit"); dialog_ex_set_left_button_text(dialog_ex, "Exit");
dialog_ex_set_right_button_text(dialog_ex, "Stay"); dialog_ex_set_right_button_text(dialog_ex, "Stay");
dialog_ex_set_header(dialog_ex, "Exit to NFC Menu?", 64, 11, AlignCenter, AlignTop); dialog_ex_set_header(dialog_ex, "Exit to NFC Menu?", 64, 0, AlignCenter, AlignTop);
dialog_ex_set_text( dialog_ex_set_text(dialog_ex, "All unsaved data will be lost", 64, 12, AlignCenter, AlignTop);
dialog_ex, "All unsaved data\nwill be lost!", 64, 25, AlignCenter, AlignTop);
dialog_ex_set_context(dialog_ex, nfc); dialog_ex_set_context(dialog_ex, nfc);
dialog_ex_set_result_callback(dialog_ex, nfc_scene_exit_confirm_dialog_callback); dialog_ex_set_result_callback(dialog_ex, nfc_scene_exit_confirm_dialog_callback);

View File

@@ -45,11 +45,7 @@ bool nfc_scene_extra_actions_on_event(void* context, SceneManagerEvent event) {
if(event.type == SceneManagerEventTypeCustom) { if(event.type == SceneManagerEventTypeCustom) {
if(event.event == SubmenuIndexMfClassicKeys) { if(event.event == SubmenuIndexMfClassicKeys) {
if(nfc_dict_check_presence(NFC_APP_MF_CLASSIC_DICT_USER_PATH)) {
scene_manager_next_scene(instance->scene_manager, NfcSceneMfClassicKeys); scene_manager_next_scene(instance->scene_manager, NfcSceneMfClassicKeys);
} else {
scene_manager_previous_scene(instance->scene_manager);
}
consumed = true; consumed = true;
} else if(event.event == SubmenuIndexMfUltralightUnlock) { } else if(event.event == SubmenuIndexMfUltralightUnlock) {
mf_ultralight_auth_reset(instance->mf_ul_auth); mf_ultralight_auth_reset(instance->mf_ul_auth);

View File

@@ -41,7 +41,8 @@ NfcCommand nfc_dict_attack_worker_callback(NfcGenericEvent event, void* context)
instance->view_dispatcher, NfcCustomEventDictAttackDataUpdate); instance->view_dispatcher, NfcCustomEventDictAttackDataUpdate);
} else if(mfc_event->type == MfClassicPollerEventTypeRequestKey) { } else if(mfc_event->type == MfClassicPollerEventTypeRequestKey) {
MfClassicKey key = {}; MfClassicKey key = {};
if(nfc_dict_get_next_key(instance->nfc_dict_context.dict, key.data, sizeof(MfClassicKey))) { if(keys_dict_get_next_key(
instance->nfc_dict_context.dict, key.data, sizeof(MfClassicKey))) {
mfc_event->data->key_request_data.key = key; mfc_event->data->key_request_data.key = key;
mfc_event->data->key_request_data.key_provided = true; mfc_event->data->key_request_data.key_provided = true;
instance->nfc_dict_context.dict_keys_current++; instance->nfc_dict_context.dict_keys_current++;
@@ -60,7 +61,7 @@ NfcCommand nfc_dict_attack_worker_callback(NfcGenericEvent event, void* context)
view_dispatcher_send_custom_event( view_dispatcher_send_custom_event(
instance->view_dispatcher, NfcCustomEventDictAttackDataUpdate); instance->view_dispatcher, NfcCustomEventDictAttackDataUpdate);
} else if(mfc_event->type == MfClassicPollerEventTypeNextSector) { } else if(mfc_event->type == MfClassicPollerEventTypeNextSector) {
nfc_dict_rewind(instance->nfc_dict_context.dict); keys_dict_rewind(instance->nfc_dict_context.dict);
instance->nfc_dict_context.dict_keys_current = 0; instance->nfc_dict_context.dict_keys_current = 0;
instance->nfc_dict_context.current_sector = instance->nfc_dict_context.current_sector =
mfc_event->data->next_sector_data.current_sector; mfc_event->data->next_sector_data.current_sector;
@@ -79,7 +80,7 @@ NfcCommand nfc_dict_attack_worker_callback(NfcGenericEvent event, void* context)
view_dispatcher_send_custom_event( view_dispatcher_send_custom_event(
instance->view_dispatcher, NfcCustomEventDictAttackDataUpdate); instance->view_dispatcher, NfcCustomEventDictAttackDataUpdate);
} else if(mfc_event->type == MfClassicPollerEventTypeKeyAttackStop) { } else if(mfc_event->type == MfClassicPollerEventTypeKeyAttackStop) {
nfc_dict_rewind(instance->nfc_dict_context.dict); keys_dict_rewind(instance->nfc_dict_context.dict);
instance->nfc_dict_context.is_key_attack = false; instance->nfc_dict_context.is_key_attack = false;
instance->nfc_dict_context.dict_keys_current = 0; instance->nfc_dict_context.dict_keys_current = 0;
view_dispatcher_send_custom_event( view_dispatcher_send_custom_event(
@@ -124,15 +125,15 @@ static void nfc_scene_mf_classic_dict_attack_prepare_view(NfcApp* instance) {
scene_manager_get_scene_state(instance->scene_manager, NfcSceneMfClassicDictAttack); scene_manager_get_scene_state(instance->scene_manager, NfcSceneMfClassicDictAttack);
if(state == DictAttackStateUserDictInProgress) { if(state == DictAttackStateUserDictInProgress) {
do { do {
if(!nfc_dict_check_presence(NFC_APP_MF_CLASSIC_DICT_USER_PATH)) { if(!keys_dict_check_presence(NFC_APP_MF_CLASSIC_DICT_USER_PATH)) {
state = DictAttackStateSystemDictInProgress; state = DictAttackStateSystemDictInProgress;
break; break;
} }
instance->nfc_dict_context.dict = nfc_dict_alloc( instance->nfc_dict_context.dict = keys_dict_alloc(
NFC_APP_MF_CLASSIC_DICT_USER_PATH, NfcDictModeOpenAlways, sizeof(MfClassicKey)); NFC_APP_MF_CLASSIC_DICT_USER_PATH, KeysDictModeOpenAlways, sizeof(MfClassicKey));
if(nfc_dict_get_total_keys(instance->nfc_dict_context.dict) == 0) { if(keys_dict_get_total_keys(instance->nfc_dict_context.dict) == 0) {
nfc_dict_free(instance->nfc_dict_context.dict); keys_dict_free(instance->nfc_dict_context.dict);
state = DictAttackStateSystemDictInProgress; state = DictAttackStateSystemDictInProgress;
break; break;
} }
@@ -141,13 +142,13 @@ static void nfc_scene_mf_classic_dict_attack_prepare_view(NfcApp* instance) {
} while(false); } while(false);
} }
if(state == DictAttackStateSystemDictInProgress) { if(state == DictAttackStateSystemDictInProgress) {
instance->nfc_dict_context.dict = nfc_dict_alloc( instance->nfc_dict_context.dict = keys_dict_alloc(
NFC_APP_MF_CLASSIC_DICT_SYSTEM_PATH, NfcDictModeOpenExisting, sizeof(MfClassicKey)); NFC_APP_MF_CLASSIC_DICT_SYSTEM_PATH, KeysDictModeOpenExisting, sizeof(MfClassicKey));
dict_attack_set_header(instance->dict_attack, "MF Classic System Dictionary"); dict_attack_set_header(instance->dict_attack, "MF Classic System Dictionary");
} }
instance->nfc_dict_context.dict_keys_total = instance->nfc_dict_context.dict_keys_total =
nfc_dict_get_total_keys(instance->nfc_dict_context.dict); keys_dict_get_total_keys(instance->nfc_dict_context.dict);
dict_attack_set_total_dict_keys( dict_attack_set_total_dict_keys(
instance->dict_attack, instance->nfc_dict_context.dict_keys_total); instance->dict_attack, instance->nfc_dict_context.dict_keys_total);
instance->nfc_dict_context.dict_keys_current = 0; instance->nfc_dict_context.dict_keys_current = 0;
@@ -174,6 +175,16 @@ void nfc_scene_mf_classic_dict_attack_on_enter(void* context) {
nfc_poller_start(instance->poller, nfc_dict_attack_worker_callback, instance); nfc_poller_start(instance->poller, nfc_dict_attack_worker_callback, instance);
} }
static void nfc_scene_mf_classic_dict_attack_notify_read(NfcApp* instance) {
const MfClassicData* mfc_data = nfc_poller_get_data(instance->poller);
bool is_card_fully_read = mf_classic_is_card_read(mfc_data);
if(is_card_fully_read) {
notification_message(instance->notifications, &sequence_success);
} else {
notification_message(instance->notifications, &sequence_semi_success);
}
}
bool nfc_scene_mf_classic_dict_attack_on_event(void* context, SceneManagerEvent event) { bool nfc_scene_mf_classic_dict_attack_on_event(void* context, SceneManagerEvent event) {
NfcApp* instance = context; NfcApp* instance = context;
bool consumed = false; bool consumed = false;
@@ -185,7 +196,7 @@ bool nfc_scene_mf_classic_dict_attack_on_event(void* context, SceneManagerEvent
if(state == DictAttackStateUserDictInProgress) { if(state == DictAttackStateUserDictInProgress) {
nfc_poller_stop(instance->poller); nfc_poller_stop(instance->poller);
nfc_poller_free(instance->poller); nfc_poller_free(instance->poller);
nfc_dict_free(instance->nfc_dict_context.dict); keys_dict_free(instance->nfc_dict_context.dict);
scene_manager_set_scene_state( scene_manager_set_scene_state(
instance->scene_manager, instance->scene_manager,
NfcSceneMfClassicDictAttack, NfcSceneMfClassicDictAttack,
@@ -195,7 +206,7 @@ bool nfc_scene_mf_classic_dict_attack_on_event(void* context, SceneManagerEvent
nfc_poller_start(instance->poller, nfc_dict_attack_worker_callback, instance); nfc_poller_start(instance->poller, nfc_dict_attack_worker_callback, instance);
consumed = true; consumed = true;
} else { } else {
notification_message(instance->notifications, &sequence_success); nfc_scene_mf_classic_dict_attack_notify_read(instance);
scene_manager_next_scene(instance->scene_manager, NfcSceneReadSuccess); scene_manager_next_scene(instance->scene_manager, NfcSceneReadSuccess);
dolphin_deed(DolphinDeedNfcReadSuccess); dolphin_deed(DolphinDeedNfcReadSuccess);
consumed = true; consumed = true;
@@ -215,7 +226,7 @@ bool nfc_scene_mf_classic_dict_attack_on_event(void* context, SceneManagerEvent
if(instance->nfc_dict_context.is_card_present) { if(instance->nfc_dict_context.is_card_present) {
nfc_poller_stop(instance->poller); nfc_poller_stop(instance->poller);
nfc_poller_free(instance->poller); nfc_poller_free(instance->poller);
nfc_dict_free(instance->nfc_dict_context.dict); keys_dict_free(instance->nfc_dict_context.dict);
scene_manager_set_scene_state( scene_manager_set_scene_state(
instance->scene_manager, instance->scene_manager,
NfcSceneMfClassicDictAttack, NfcSceneMfClassicDictAttack,
@@ -224,13 +235,13 @@ bool nfc_scene_mf_classic_dict_attack_on_event(void* context, SceneManagerEvent
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);
} else { } else {
notification_message(instance->notifications, &sequence_success); nfc_scene_mf_classic_dict_attack_notify_read(instance);
scene_manager_next_scene(instance->scene_manager, NfcSceneReadSuccess); scene_manager_next_scene(instance->scene_manager, NfcSceneReadSuccess);
dolphin_deed(DolphinDeedNfcReadSuccess); dolphin_deed(DolphinDeedNfcReadSuccess);
} }
consumed = true; consumed = true;
} else if(state == DictAttackStateSystemDictInProgress) { } else if(state == DictAttackStateSystemDictInProgress) {
notification_message(instance->notifications, &sequence_success); nfc_scene_mf_classic_dict_attack_notify_read(instance);
scene_manager_next_scene(instance->scene_manager, NfcSceneReadSuccess); scene_manager_next_scene(instance->scene_manager, NfcSceneReadSuccess);
dolphin_deed(DolphinDeedNfcReadSuccess); dolphin_deed(DolphinDeedNfcReadSuccess);
consumed = true; consumed = true;
@@ -253,7 +264,7 @@ void nfc_scene_mf_classic_dict_attack_on_exit(void* context) {
scene_manager_set_scene_state( scene_manager_set_scene_state(
instance->scene_manager, NfcSceneMfClassicDictAttack, DictAttackStateUserDictInProgress); instance->scene_manager, NfcSceneMfClassicDictAttack, DictAttackStateUserDictInProgress);
nfc_dict_free(instance->nfc_dict_context.dict); keys_dict_free(instance->nfc_dict_context.dict);
instance->nfc_dict_context.current_sector = 0; instance->nfc_dict_context.current_sector = 0;
instance->nfc_dict_context.sectors_total = 0; instance->nfc_dict_context.sectors_total = 0;

Some files were not shown because too many files have changed in this diff Show More