From bc519747d905dc72b643a25335c7b61dec9acb7a Mon Sep 17 00:00:00 2001 From: 0xchocolate <0xchocolate@users.noreply.github.com> Date: Wed, 10 Aug 2022 17:03:51 -0700 Subject: [PATCH] Squashed commit of the following: MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 1d50a5981e818e6033512fb6bd633a181aef9539 Author: hedger Date: Tue Aug 9 18:57:11 2022 +0300 [FL-2059] Storage fixes for handling empty files (#1563) * storage: fixed handling of zero-length files * docs: added ReadMe.md for vscode workspace * rpc: storage: improved empty file & error handling in write handler * docs: markdown fix * docs: typo fixes Co-authored-by: SG Co-authored-by: あく commit 01eb92db0695fe73f8866580af36cc03362d297c Author: Astra <93453568+Astrrra@users.noreply.github.com> Date: Tue Aug 9 18:45:52 2022 +0300 Mifare Classic emulation fixes (#1566) * Add fix for field * More small bugfixes * Clean up commit ca23d0c207a2ac2f52885686e61f893d332c515f Author: MX <10697207+xMasterX@users.noreply.github.com> Date: Tue Aug 9 15:16:09 2022 +0300 fix building updater package (#1564) commit a7316e7822900f1c4adb806b7b73b36f01937aa6 Author: Oleg K Date: Sun Aug 7 19:08:00 2022 +0300 Added support for Samsung TVs using address 07. (#1509) * Added support for Samsung TVs using address 07. Samsung QEXX, UEXX * Recompile assets and update manifest Co-authored-by: あく commit 416cce9ffc9819422940978cbc66476150aec900 Author: Skorpionm <85568270+Skorpionm@users.noreply.github.com> Date: Sun Aug 7 19:56:45 2022 +0400 [FL-2718, FL-2719] SubGhz: add protocol BERNER / ELKA / TEDSEN / TELETASTER / Doitrand / Marantec / Phoenix V2 (static mode) / Phox (static mode), fix Princeton (#1516) * SubGhz: add protocol marantec * SubGhz: add protocol BERNER / ELKA / TEDSEN / TELETASTER * SubGhz: add protocol Doitrand * SubGhz: delete debug * SubGhz: add protocol Phoenix V2 (static mode) * SubGhz: fix serial decode Phoenix V2 * SubGhz: fix Princeton, display serial number and button on boot * SubGhz: fix Bett decoder and fix unit_test * SubGhz: update test_random_raw for unit_test Co-authored-by: あく commit 81b404aafa3ead93c1496de696bef5c99b21d6fa Author: Mayco Date: Sun Aug 7 17:40:09 2022 +0200 Add ability to type a space with the input keyboard by long-pressing "_" (#1550) Co-authored-by: Mayco Co-authored-by: あく commit 173c94156d4c6644046ea782eb6ac7e1b18033a4 Author: gornekich Date: Sun Aug 7 18:33:14 2022 +0300 NFC: Add Skylanders support (#1553) * nfc: add skylanders support * nfc: format sources Co-authored-by: あく commit f3d7d7dba085f4e676c1d901122fb021a3553644 Author: Peter Seprus Date: Sun Aug 7 17:28:04 2022 +0200 Extend random name generator (#1551) * Extend random name generator * Format sources Co-authored-by: あく commit 67a9753f78d95d1158cbb09aa8548c82069dcf5b Author: Astra <93453568+Astrrra@users.noreply.github.com> Date: Sun Aug 7 18:18:39 2022 +0300 Add a Mifare Classic info screen to parser output (#1504) * Add the info screen * Oops, don't dupe the points! * Show ATQA and SAK, remove the back label * And the dolphin doesn't need to be imported anymore * Correct UX to the approved one Co-authored-by: gornekich commit 9ffcc52ada215be47a1921875ada955d3420e0be Author: Vitaliya Chumakova Date: Sun Aug 7 18:09:00 2022 +0300 Mifare Ultralight authentication (#1365) * mifare ultralight auth prototype * it works! * Reference source * use countof * rework everything * oops forgot scenes * build: revert changes in manifest, stack size * build: fix buid, format sources * nfc: update unlock ultralight GUI * nfc: fix byte input header * nfc: add new scenes for locked ultralight * nfc: add data read to ultralights * nfc: add unlock option in mf ultralight menu * nfc: add data read init in ultralight generation * nfc: lin sources, fix unlocked save * nfc: format python sources * nfc: clean up Co-authored-by: gornekich commit d147190d6173f427bbedce988cb28eff22ca692e Author: Georgii Surkov <37121527+gsurkov@users.noreply.github.com> Date: Fri Aug 5 18:38:20 2022 +0300 [FL-2717] Fix unexpected behaviour when opening a remote from outside (#1538) * Fix unexpected behaviour when opening remote from outside * Same fix for editing button name * Exit application correctly if remote was deleted * Remove duplicate function from ibutton * Use COUNT_OF macro Co-authored-by: あく commit 55b4ff8e0c23ba64a3c82b8f7736b20d2022f295 Author: Mewa Date: Fri Aug 5 17:12:13 2022 +0200 Documentation: fixed outdated naming (#1518) * Documentation: fixed outdated naming: changed outdated naming in assets/dolphin/ReadMe. `essential -> blocking` * Updated naming convencion in assets/ReadMe file and assets/dolphin/ReadMe file Co-authored-by: あく commit 040558c8f989aa36a50f86276f847dfb73cfa8e2 Author: TQMatvey <77576395+TQMatvey@users.noreply.github.com> Date: Thu Aug 4 19:18:34 2022 +0700 PicoPass: Fix Card Detection Visuals (#1511) Co-authored-by: あく commit 8a370d70dc15ee90892d55232e9a3709c39cf8b6 Author: Georgii Surkov <37121527+gsurkov@users.noreply.github.com> Date: Thu Aug 4 15:11:01 2022 +0300 [FL-2700] Fix IR hangup with short signals (#1535) * Do not use infrared worker callback for notifications * Remove tx callback * Port Infrared notifications to hardware blinker * Move all blink message definitions to notification_messages.h * Fix potential hangup after leaving debug scene commit 4460010e76fba3626e0ef2c7e3e5c8149dda25c3 Author: SG Date: Thu Aug 4 14:34:04 2022 +1000 Archive: fix null dereference #1531 (#1532) commit 4c499d9045d0cda866b1820c731c63df3fb47092 Author: HexPandaa <47880094+HexPandaa@users.noreply.github.com> Date: Wed Aug 3 19:13:06 2022 +0200 Fix directory name in lib readme (#1528) Co-authored-by: あく commit 3ee93e1a820fa0401b4de49de0e1050b83e53399 Author: Fedor Indutny <238531+indutny@users.noreply.github.com> Date: Wed Aug 3 10:07:35 2022 -0700 nfc: make dict attack more interactive (#1462) * nfc: deduplify dict attack worker code * nfc: make dict attack more interactive Co-authored-by: gornekich Co-authored-by: あく commit 284c56718bf808f8346a2e48fd26a9d8ee865e9d Author: Ryan Murphy <57873842+fork-bombed@users.noreply.github.com> Date: Wed Aug 3 18:00:17 2022 +0100 NFC: Edit UID feature (#1513) * Added option to edit UID in saved NFC files * Fixed bug with saved filename * Only show for data that can't be read Co-authored-by: あく commit 135fbd294bcc9e67eda18230df7547c1ef347645 Author: Georgii Surkov <37121527+gsurkov@users.noreply.github.com> Date: Wed Aug 3 19:43:14 2022 +0300 [FL-2693] RW buffered streams (#1523) * Add write methods for stream cache * Fix logical error * Implement write cache for buffered file streams * Minor code refactoring * Less ugly code * Better read() implementation * Intermediate implementation * Fix logical error * Code cleanup * Update FFF comments * Fix logical error * Github: rsync with delete Co-authored-by: あく commit 6499597586c9e5aff76bf4d58f8389c4d2fca0ee Author: hedger Date: Wed Aug 3 19:32:31 2022 +0300 vscode: initial development configuration (#1520) * vscode: initial development configuration; fbt: `vscode_dist` target for deploying vscode config * vscode: fixed fbt blackmagic command Co-authored-by: あく commit 51f5641c5efa9a00f491d57574a4e7254add22a7 Author: Žiga Deisinger <41201503+zigad@users.noreply.github.com> Date: Wed Aug 3 18:18:48 2022 +0200 FIX: Fixed inconsistencies between texts (#1496) * FIX: Fixed inconsistencies between texts: I have changed inconsistencies. Sometimes there was a missing capital letter and sometimes there was dot instead of exclamation mark and so on. No other changes were made. I have made the correction based on how other text looks on Fliper and for headers most texts use Pascal Case. * FIX: Fixed inconsistencies between texts: Found 2 more texts with inconsistencies. Co-authored-by: あく commit bc34689ed6e6a8c2c757be2da01984814468537a Author: SG Date: Thu Aug 4 02:00:17 2022 +1000 Make printf great again (#1438) * Printf lib: wrap *printf* functions * Printf lib, FW: drop sprintf. Dolphin: dump timestamp as is, wo asctime. * FW: remove sniprintf, wrap assert functions * Printf lib: wrap putc, puts, putchar * Printf: a working but not thread-safe concept. * Poorly wrap fflush * stdglue: buffers * Core: thread local buffers * Core: move stdglue to thread api, add ability to get FuriThread instance of current thread. * RPC tests: replace sprintf with snprintf * Applications: use new stdout api * Printf lib: wrap more printf-like and stdout functions * Documentation * Apps: snprintf size fixes Co-authored-by: あく commit eed4296890f7bb24c0b7f7627e9fcbcc695dd10a Author: SG Date: Thu Aug 4 01:47:10 2022 +1000 MPU Hal (#1492) * Furi HAL: memory protection unit * Core: prohibit NULL dereferencing, even for reads. * Applications: fix NULL dereference * Core: stack protection by MPU * MPU: stack region alignment * Apps: fix null pointer dereferences * Threads: fix non-null arg check * Desktop settings: fix null pointer dereference * Core: documented null-check hack * Fix null dereference issues * Apps: args check * Core: naming fixes * format code * Core: remove NONNULL specifier * FurHal: move MPU initialization to begining, fix enum naming Co-authored-by: あく commit 4a6477aaa87ed35388d27ffbedb8f3d19a032461 Author: SG Date: Wed Aug 3 22:01:38 2022 +1000 Core, logs: removed tag concatenation (#1524) * Core, logs: removed tag concatenation * Logs: remove unused fn * Logs: remove allocation commit 93a4b9c4a9ee35372e95eae7f96fffd42a2a809e Author: Max Andreev Date: Tue Aug 2 17:05:31 2022 +0300 [FL-2649] Drop Docker in CI/CD (#1412) * enable sparseCheckout, moving github actions from docker to raw shell * fix missing known_hosts while setting ssh priv key * fix build.yml * add ssh key to upload just in time * fixing rsync syntax * fix build.yml * try to fix build.yml again * testing rsync * test rsync again * add linters * add Black Python linter to submodules * add Black submodule * add working python linter target, dirty file list * up toolchain to version 4 * up toolchain to ver 5 * up toolchain version to 6 * fbt: using black 22.6.0 * remove Black submodule, up toolchain to ver 7 * fbt: added lint_py, format_py targets * add pvs_studio workflow * fix pvs_studio segfault * fix pvs_studio command * fix pvs_studio command 2 * show env before run pvs_studio * try to debug pvs_studio * try to strace pvs_studio.. * Add FBT_TOOLCHAIN_PATH, MacOS Rosseta check, and ignore non-x86_64 linux architectures * prevent redownloading toolchain on github-runners * fix toolchain download exitcode * add strace to debug pvs_studio segfault * disable strace to catch full code dump * Add './fbt cli' target to access Flipper CLI via PySerial * remove pvs_studio from this PR * removing clang-format from toolchain due errors * make source easy, and fix some mistakes found by @hedger * Add check_submodules workflow, some fixes * fixing mistakes Co-authored-by: hedger Co-authored-by: hedger commit a1637e9216e98396938debd7ccb4e15cbcb41eb1 Author: hedger Date: Tue Aug 2 16:46:43 2022 +0300 fbt fixes & improvements (#1490) * fbt: minimal USB flash mode; scripts: faster storage.py with larger chunks * fbt: fixed creation of temporary file nodes confusing scons * docs: removed refs to --with-updater * fbt: removed splashscreen from minimal update package * fbt: renamed dist arguments for consistency * docs: fixed updater_debug target * fbt: separate target for generating compilation_database.json without building the code. * fbt: added `jflash` target for programming over JLink probe; refactored usb flashing targets * fbt: building updater_app in unit_tests configuration * fbt: fixed reset behavior after flashing with J-Link * fbt: generating .map file for firmware binary & external apps * fbt/core: moved library contents before apps code Co-authored-by: あく commit 1e732830ec23be2a9acff42c240609d53bed275a Author: hedger Date: Tue Aug 2 16:29:16 2022 +0300 ci: check for uncommited changes after build (#1461) * ci: check for uncommited changes after build * assets: updated Manifest to match sources * ci: simplified uncommited files check * resources: Updated Manifest * fbt: always rebuild manifest Co-authored-by: あく commit 01afb289c0dd968193ec6f8abf6282a327fa4e7e Author: Georgii Surkov <37121527+gsurkov@users.noreply.github.com> Date: Tue Aug 2 16:17:37 2022 +0300 [FL-2713] Buffered file streams fix (#1515) * Fix incorrect buffered stream behaviour * Add specific tests * Make the test fail on wrong behaviour * Better names Co-authored-by: あく commit f9745b4141570e6dbc919ac5a63ed98acb9483c7 Author: あく Date: Tue Aug 2 21:54:12 2022 +0900 [FL-2705] App RPC Bug Fixes and redesign (#1491) * Rpc: remove callback timer * Rpc: simplify rpc event callback * Rpc: migrate to new confirmation schema * Rpc: migrate to new confirmation schema part2: finalize ibutton and rfid * Rpc: migrate to new confirmation schema part3: finallize nfc and fix id in load * Rpc: hardened sequencing check * Rpc: migrate to new confirmation schema part4: finalize subghz * iButton: properly handle exit * Nfc: correct sequence for rpc exit send * Rpc: fix review issues and nfc exit message * Rpc: more logging and condition race fix in confirmation * Rpc: migrate to new confirmation schema part5: finalize infrared * Rpc: more logging commit f9386b2649c022fde75572046ceb51198a4a5317 Author: SG Date: Tue Aug 2 21:24:42 2022 +1000 Assets: unused assets removed (#1514) commit 4da6eba395652de8266ecd6a2bb63012517efd85 Author: Skorpionm <85568270+Skorpionm@users.noreply.github.com> Date: Mon Aug 1 16:24:21 2022 +0400 [FL-2706, FL-2709] SubGhz: checking saved key files for length (#1485) * [FL-2706] SubGhz: checking saved key files for length * SubGhz: fix RAW file upload error * [FL-2709] GubGhz: RAW screen fix Co-authored-by: あく commit 84550d58784637fb46d91c8087a635350e84db5b Author: hedger Date: Sun Jul 31 02:48:55 2022 +0300 [FL-2654] Updater: retrying pre-boot SD card mount multiple times (#1402) * Updater: retrying pre-boot SD card mount multiple times * Updater: added delay before retrying SD card mount on early boot Co-authored-by: あく commit 712a48b5db166a401cae9f6ca986d5aaeca7a1a5 Author: MX <10697207+xMasterX@users.noreply.github.com> Date: Sun Jul 31 02:34:38 2022 +0300 Fix typo in subghz (#1467) * fix typo across subghz Co-authored-by: あく Co-authored-by: Aleksandr Kutuzov commit 4c39dcbe0c40c89f92df93d8b572799e39e603e1 Author: Anna Prosvetova Date: Sun Jul 31 01:14:16 2022 +0200 ☦️ Rpc: fix backup commands responses (#1502) commit c40e8811d68e9f4b8f603ae5d5826b814521014d Author: gornekich Date: Thu Jul 28 15:34:28 2022 +0300 [FL-2701], [FL-2702], [FL-2699] NFC fixes (#1478) * nfc: change read scene views * nfc: rework return after save success * nfc: add fallback to read UID of unrecognized iso14443-3 * nfc: show mifare desfire on read success * nfc: add restore original confirm scene * nfc: fix icon name * nfc: clear 6 bit in SAK to emulate 14443-4 uids * nfc: don't change original sak commit b6e52e979d07dbd17de80614df6fa224fcb9af33 Author: TQMatvey <77576395+TQMatvey@users.noreply.github.com> Date: Thu Jul 28 16:48:45 2022 +0700 Infrared.c: Dont Close GUI 2 times (#1477) GUI was being closed 2 times upon exit, this PR fixes that by removing 1 of those cases commit c7772060657afc8ee5a160ea3ed62246d989c136 Author: gornekich Date: Tue Jul 26 20:28:37 2022 +0300 NFC: fix navigation from menu scenes #1459 commit 80a7de80783150b6f1aae7a7426ed982b5221d43 Author: hedger Date: Tue Jul 26 20:13:09 2022 +0300 updater: fixed dolphin level not being migrated (#1458) commit 9c59bcd7763cbe9b0132b0f2698543e2dfb11623 Author: gornekich Date: Tue Jul 26 18:30:49 2022 +0300 [FL-2605] NFC new design (#1364) * nfc: add new read scene * lib: refactore nfc library * mifare desfire: add read card fuction * lib nfc: add auto read worker * nfc: add supported cards * nfc: add mifare classic read success scene * nfc: add troyka support * submodule: update protobuf * nfc: mifare classic keys cache * nfc: rework mifare classic key cache * Correct spelling * nfc: add user dictionary * nfc: introduce block read map in fff * nfc: rework dict attack * nfc: improve dict attack * nfc: rework mifare classic format * nfc: rework MFC read with Reader * nfc: add gui for MFC read success scene * nfc: fix dict attack view gui * nfc: add retry and exit confirm scenes * nfc: add retry and exit scenes navigation * nfc: check user dictionary * nfc: remove unused scenes * nfc: rename functions in nfc worker * nfc: rename mf_classic_dict_attack -> dict_attack * nfc: change scenes names * nfc: remove scene tick events * nfc: rework dict calls with buffer streams * nfc: fix notifications * nfc: fix mf desfire navigation * nfc: remove notification from mf classic read success * nfc: fix read sectors calculation * nfc: add fallback for unknown card * nfc: show file name while emulating * nfc: fix build * nfc: fix memory leak * nfc: fix desfire read * nfc: add no dict found navigation * nfc: add read views * nfc: update card fix * nfc: fix access bytes save * nfc: add exit and retry confirm to mf ultralight read success * nfc: introduce detect reader * nfc: change record open arg to macros * nfc: fix start from archive Co-authored-by: Astra Co-authored-by: あく commit ec19c11dbe4a81147947cc3a8dd2d182374ed67a Author: Skorpionm <85568270+Skorpionm@users.noreply.github.com> Date: Tue Jul 26 18:16:59 2022 +0400 [FL-2669] SubGhz: add support for loading custom presets (#1398) * SubGhz: load custom -preset * SubGhz: fix error prt=0 * SubGhz: load custom preset * SubGhz: code refactoring to support custom preset * SubGhz: add custom presert refactoring * SubGhz: fix alloc history alloc preset * SubGhz: fix error load file * SubGhz: fix start custom preset * SubGhz: fix delete custom preset * SubGhz: add description Custom_preset_data for CC1101 * SubGhz: debug logging and buffer size rounding Co-authored-by: あく Co-authored-by: Aleksandr Kutuzov commit ed7db336c1ce537ee86af5df6b9512299e323db8 Author: Skorpionm <85568270+Skorpionm@users.noreply.github.com> Date: Tue Jul 26 16:58:07 2022 +0400 [FL-2684, FL-2685] bugfix subghz (#1446) * [FL-2684] SubGhz: fix incorrect CAME TWICE protocol definition * [FL-2685] SubGhz: fix the recorded RAW signal is deleted when trying to transmit on a prohibited frequency Co-authored-by: あく commit 3fa5e18c5a2c17fc666c2b767d90187bb5f30c3a Author: hedger Date: Tue Jul 26 15:44:03 2022 +0300 [FL-2692, FL-2604, FL-2632] New first start sequence (#1456) * desktop: restored automatic power off & manual power off on slideshow view; assets: added frames for new first start sequence; docs: added info on slideshow compilation * desktop: restarting long timer on OK button release Co-authored-by: あく commit 056446dfed68931b9997cd3d7600f655809243c4 Author: hedger Date: Tue Jul 26 15:21:51 2022 +0300 [FL-2675] /int space reservation (#1448) * storage: added global #defines for /int, /ext & /any * storage: introduced PATH_EXT, PATH_INT& PATH_ANY macros * core apps: moved hardcoded config files names to separate headers; prefixed them with "."; updater: added file name migration to new naming convention on backup extraction * storage: fixed storage_merge_recursive handling of complex directory structures; storage_move_to_sd: changed data migration logic to all non-dot files & all folders * core: added macro aliases for core record names * Bumped protobuf commit pointer * storage: reserved 5 pages in /int; denying write&creation of non-dot files when running out of free space Co-authored-by: あく commit 52a83fc929f125d24136e017661c671d21caac17 Author: Georgii Surkov <37121527+gsurkov@users.noreply.github.com> Date: Tue Jul 26 14:46:14 2022 +0300 [FL-2688] Fix incorrect remote renaming behaviour #1455 commit 05b816429c6a1801e26d74467be65da81d44428d Author: TijnMertens <110020473+TijnMertens@users.noreply.github.com> Date: Tue Jul 26 11:13:04 2022 +0200 Minor grammar and typo fix (#1454) commit e03b102af220d7cd0697e57838b8370b1fb60155 Author: MX <10697207+xMasterX@users.noreply.github.com> Date: Tue Jul 26 02:09:04 2022 +0300 Fix git submodules update called anyways (#1450) Ignoring FBT_NO_SYNC commit 27b698f081ecbda04e9337ba5b9ee7abae8cdb4e Author: Szymon Lisowiec Date: Mon Jul 25 23:31:34 2022 +0200 fbt: Fixed build for users with space in username (#1437) Co-authored-by: あく Co-authored-by: hedger commit d085af31c3ed9155058d6b5ea49f279d73eab541 Author: AlexeyOplachko <45398541+AlexeyOplachko@users.noreply.github.com> Date: Mon Jul 25 23:34:49 2022 +0300 Fixing a typo in Bug Report Issue Template (#1449) commit cd77b93f26fd05996dc0a2381eaf23c37ef5e394 Author: Eric Betts Date: Mon Jul 25 08:36:38 2022 -0700 Picopass: dump full card, extract some details (#1408) * Dump entire picopass card * Capture more iClass details * facility code bugfix Co-authored-by: あく commit f8e0ec42c53b7b0710d17f5efcd264c8b4c94927 Author: Yukai Li Date: Mon Jul 25 09:21:05 2022 -0600 nfc: NTAG203 support (#1383) * nfc: Fix original MFUL feature flags * nfc: Add NTAG203 read support * nfc: Update emulation lock byte handling for NTAG203 * nfc: Add NTAG203 counter emulation support * nfc: Add NTAG203 tag generator * nfc: Fix NTAG203 emulating GET_VERSION * nfc: Fix MFUL version reading * nfc: Complete NTAG203 counter emulation behavior * nfc: Complete NTAG203 emulation * nfc: Remove unnecessary init in MFUL emulation * nfc: Add notes about MFUL type enum Co-authored-by: gornekich Co-authored-by: あく commit 30820b83b5d6738c0867b11860ccbf92f8530183 Author: Nikolay Minaylov Date: Mon Jul 25 17:46:42 2022 +0300 [FL-2464, FL-2466] RFID, ibutton text fix #1413 Co-authored-by: あく commit ac60d1808a055be65ca42ad64f89d3c721de8772 Author: Wyatt Neal Date: Mon Jul 25 10:35:02 2022 -0400 fixing typos, satus -> status (#1422) Co-authored-by: あく commit d80edba8916210248cb2f0107f872cddc1dceb95 Author: Nikolay Minaylov Date: Mon Jul 25 17:16:45 2022 +0300 RPC App: state message and GUI update (#1423) * RPC App: state message and GUI update * Protobuf submodule update Co-authored-by: SG Co-authored-by: あく commit f1cb95655cdb6cb6ca7ea9251f85d79cbc4b1e94 Author: Kowlin <10947836+Kowlin@users.noreply.github.com> Date: Mon Jul 25 15:11:24 2022 +0200 Port over Issue templates to new YML format (#1433) * WIP Push * Add feature request label to template * Punctuation helps * rename feature request and add in enhancement * wording * Add in extra markdown explanations Co-authored-by: SG Co-authored-by: あく commit f5d6a8084b2ff90608cf9fee65185b31f2080993 Author: Nikolay Minaylov Date: Mon Jul 25 15:11:34 2022 +0300 [FL-2668] GUI message screens update #1428 Co-authored-by: SG Co-authored-by: あく commit 3ee592cae724ca2823c96be4070cbc8ae0001e4f Author: Ruben van Baarle Date: Mon Jul 25 13:48:06 2022 +0200 Fix SubGHz chat immediately closing #1440 It's just an if condition which should've been inverted Co-authored-by: あく commit c22d66590e86ba27f99b79332f0ea1fd46e8ec83 Author: Georgii Surkov <37121527+gsurkov@users.noreply.github.com> Date: Mon Jul 25 14:23:47 2022 +0300 [FL-2682] Allow spaces in file names #1444 commit 7c49f604f986f13790051822c72ae7de1730827c Author: Zoë Prosvetova <109866245+ZoeMeetAgain@users.noreply.github.com> Date: Sat Jul 23 18:33:39 2022 +0200 Fix toolchain typos (#1435) commit 253b98c8f014e4fa91a129eaba3a986f91ac5b12 Author: ESurge Date: Fri Jul 22 20:35:14 2022 -0700 Added condition to cli "log" command to end if serial terminal is disconnected. (#1425) * Added condition to cli "log" command. If USB is disconnected while "log" command is running, it will end the command. * Reverted change on cli_commands.c Added condition in cli.c for cli_cmd_interrupt_received function to react appropriately when serial terminal is disconnected. Added condition in subghz_cli.c for subghz chat cmd to exit gracefully when serial terminal is disconnected. * Formatting Co-authored-by: あく commit 16e598b2c08bf3156370f15fef5b5dd002780f68 Author: Georgii Surkov <37121527+gsurkov@users.noreply.github.com> Date: Fri Jul 22 19:00:25 2022 +0300 [FL-2655, FL-2650] Buffered file streams (#1424) * Initial buffered file stream implementation * Fix logical errors * Fix more logical errors * Minimally working implementation * Adapt infrared unit tests for buffered streams * Increase read buffer size from 512 to 1K * Correct naming and formatting * More code improvements * Allow passing access and open modes for buffered streams * Implement tests for buffered streams * Better file and method names * Add comments and correct formatting * Use buffered streams in Infrared * Fix compilation error commit ec57dd310a6ad9657c08d143bd82a66da532dc78 Author: adisbladis Date: Wed Jul 20 20:48:10 2022 +0800 fbt: Respect SOURCE_DATE_EPOCH when setting build date (#1421) * fbt: using SOURCE_DATE_EPOCH from environment for build timestamp (if set) Co-authored-by: hedger commit e3c7201a2065677ad3c96f0bb1f47f7b58cdb819 Author: あく Date: Wed Jul 20 13:56:33 2022 +0300 Furi: core refactoring and CMSIS removal part 2 (#1410) * Furi: rename and move core * Furi: drop CMSIS_OS header and unused api, partially refactor and cleanup the rest * Furi: CMSIS_OS drop and refactoring. * Furi: refactoring, remove cmsis legacy * Furi: fix incorrect assert on queue deallocation, cleanup timer * Furi: improve delay api, get rid of floats * hal: dropped furi_hal_crc * Furi: move DWT based delay to cortex HAL * Furi: update core documentation Co-authored-by: hedger commit f9c2287ea7dabb0e8275d9a635c4b941516f59bc Author: Dom Date: Sun Jul 17 03:09:47 2022 -0700 Update ReadMe.md (#1409) - Minor correction in grammar for ReadMe. Co-authored-by: あく commit 73711c75e630badf275eb0f11cfc4391ac3d2566 Author: t0m1o1 <94725994+t0m1o1@users.noreply.github.com> Date: Sun Jul 17 10:56:47 2022 +0100 Update bad_usb_script.c to fix incorrect ALT key const #1406 Co-authored-by: あく commit e7c3da1da9bbbd454b1a863e336dce3fbd5e6553 Author: Skorpionm <85568270+Skorpionm@users.noreply.github.com> Date: Sun Jul 17 13:45:21 2022 +0400 [FL-2658, FL-2657] SubGhz: add new protocol (IronLogic, Comunello, Sommer(fsk476), Normstahl, KEY, EcoStar, Gibidi, Mutancode) (#1404) * Subghz: fix cli no load keeloq_mfcodes_user * SubGhz: add new protocol (IronLogic, Comunello, Sommer(fsk476), Normstahl, KEY, EcoStar, Gibidi, Mutancode) * SubGhz: fix syntax * SubGhz: fix error build Co-authored-by: あく commit 7741a19244ce3af206c6434fc922f1340a96e547 Author: SG Date: Sun Jul 17 12:34:13 2022 +0300 Better crash handling (#1397) * Core: correct ISR flag check on crash, dump heap and stack info on crash * Core: crash, show task name * Core crash: optimization Co-authored-by: あく commit 80629de01ecc6cee5a811ba647479117f17b8374 Author: Georgii Surkov <37121527+gsurkov@users.noreply.github.com> Date: Sun Jul 17 12:21:56 2022 +0300 [FL-2601] Move Infrared unit test data to assets (#1396) * Move samsung raw data to assets * Add more assets and fix bugs * Clean up code * Implement all raw data as assets * Remove input data from old test files * Better signal names * Better file opening logic * Implement loading parsed data from files * Move most of RC5 tests into assets * Add more test cases * Add more test cases * Eliminate RUN_DECODER macro * Better code structure * Implement run_encoder function * More encoder tests * Move all encoder tests to assets * Move all test data to assets * Normalise function names * Rename code files * Uncomment other tests * Swich test order to avoid weird memory leaks * UnitTests: cleanup output and redirect it into stdout * UnitTests: selectable tests and better reporting Co-authored-by: あく commit 877c5c812236337756bd8dba8694ac0ccf53da66 Author: Nikolay Minaylov Date: Sun Jul 17 10:41:16 2022 +0300 [FL-1962, FL-2464, FL-2465, FL-2466, FL-2560, FL-2637, FL-2595] Ibutton, Infrared, LfRFID GUI fixes (#1392) * Ibutton, Infrared, LfRFID GUI fixes * Loading screens update Co-authored-by: あく commit edc6ca0c8f3cd66ccfa2a8b0bf102bb1828d388e Author: Eric Betts Date: Sat Jul 16 23:13:51 2022 -0700 Log MFC nonces for use with mfkey32v2 (#1390) Co-authored-by: あく commit c29ab50016655d57304e9ffd358a2923cc85faa1 Author: Eric Betts Date: Sat Jul 16 23:01:19 2022 -0700 Calculate picopass CRC dynamically (#1389) Co-authored-by: あく --- .github/CODEOWNERS | 2 +- .github/ISSUE_TEMPLATE/01_bug_report.yml | 46 + .github/ISSUE_TEMPLATE/02_enhancements.yml | 21 + .github/ISSUE_TEMPLATE/03_feature_request.yml | 24 + .github/ISSUE_TEMPLATE/bug_report.md | 30 - .github/ISSUE_TEMPLATE/config.yml | 5 + .github/ISSUE_TEMPLATE/discuss-issue.md | 12 - .github/ISSUE_TEMPLATE/feature_request.md | 20 - .github/ISSUE_TEMPLATE/in-progress.md | 10 - .github/ISSUE_TEMPLATE/need-help.md | 10 - .github/workflows/build.yml | 115 +- .github/workflows/build_toolchain.yml | 46 - .github/workflows/check_submodules.yml | 52 +- .github/workflows/lint_c.yml | 24 +- .github/workflows/lint_python.yml | 14 +- .github/workflows/reindex.yml | 7 +- .vscode/.gitignore | 4 + .vscode/ReadMe.md | 17 + .vscode/example/c_cpp_properties.json | 32 + .vscode/example/launch.json | 87 ++ .vscode/example/settings.json | 22 + .vscode/example/tasks.json | 103 ++ .vscode/extensions.json | 15 + ReadMe.md | 4 +- SConstruct | 114 +- applications/about/about.c | 16 +- applications/archive/archive.c | 4 +- applications/archive/helpers/archive_apps.c | 17 +- .../archive/helpers/archive_browser.c | 6 +- .../archive/helpers/archive_browser.h | 15 +- .../archive/helpers/archive_favorites.c | 28 +- .../archive/helpers/archive_favorites.h | 4 +- applications/archive/helpers/archive_files.c | 8 +- .../archive/scenes/archive_scene_browser.c | 29 +- .../archive/scenes/archive_scene_rename.c | 6 +- applications/bad_usb/bad_usb_app.c | 15 +- applications/bad_usb/bad_usb_app_i.h | 3 +- applications/bad_usb/bad_usb_script.c | 2 +- .../bad_usb/scenes/bad_usb_scene_error.c | 16 +- applications/bt/bt_cli.c | 26 +- applications/bt/bt_debug_app/bt_debug_app.c | 6 +- applications/bt/bt_hid_app/bt_hid.c | 16 +- applications/bt/bt_service/bt.c | 17 +- applications/bt/bt_service/bt.h | 2 + applications/bt/bt_service/bt_keys_filename.h | 3 + applications/bt/bt_service/bt_keys_storage.c | 3 +- applications/bt/bt_service/bt_keys_storage.h | 1 + applications/bt/bt_settings.c | 3 +- applications/bt/bt_settings.h | 2 + .../bt/bt_settings_app/bt_settings_app.c | 8 +- .../bt_settings_scene_forget_dev_confirm.c | 4 +- applications/bt/bt_settings_filename.h | 3 + applications/cli/cli.c | 16 +- applications/cli/cli.h | 2 + applications/cli/cli_commands.c | 12 +- applications/cli/cli_i.h | 2 +- applications/cli/cli_vcp.c | 3 +- applications/crypto/crypto_cli.c | 4 +- applications/debug_tools/blink_test.c | 8 +- .../debug_tools/display_test/display_test.c | 4 +- .../file_browser_test/file_browser_app.c | 10 +- .../scenes/file_browser_scene_start.c | 8 +- applications/debug_tools/keypad_test.c | 14 +- applications/debug_tools/text_box_test.c | 4 +- applications/debug_tools/uart_echo.c | 8 +- applications/debug_tools/usb_mouse.c | 2 +- applications/debug_tools/usb_test.c | 4 +- applications/debug_tools/vibro_test.c | 8 +- .../desktop/animations/animation_manager.c | 54 +- .../desktop/animations/animation_storage.c | 12 +- .../animations/views/bubble_animation_view.c | 25 +- applications/desktop/desktop.c | 27 +- .../desktop_settings/desktop_settings.h | 6 +- .../desktop_settings/desktop_settings_app.c | 6 +- .../desktop_settings_filename.h | 3 + .../desktop_settings_scene_pin_setup_done.c | 4 +- applications/desktop/helpers/pin_lock.c | 16 +- applications/desktop/helpers/slideshow.c | 4 +- .../desktop/helpers/slideshow_filename.h | 3 + .../desktop/scenes/desktop_scene_debug.c | 4 +- .../desktop/scenes/desktop_scene_locked.c | 4 +- .../desktop/scenes/desktop_scene_pin_input.c | 4 +- .../desktop/scenes/desktop_scene_slideshow.c | 14 +- applications/desktop/views/desktop_events.h | 1 + .../desktop/views/desktop_view_debug.c | 19 +- .../desktop/views/desktop_view_lock_menu.c | 2 +- .../desktop/views/desktop_view_slideshow.c | 31 +- .../desktop/views/desktop_view_slideshow.h | 3 + applications/dialogs/dialogs.c | 2 +- applications/dialogs/dialogs.h | 2 + .../dialogs/dialogs_module_file_browser.c | 4 +- applications/dialogs/dialogs_module_message.c | 4 +- applications/dolphin/dolphin.c | 2 +- applications/dolphin/dolphin.h | 2 + applications/dolphin/helpers/dolphin_state.c | 5 +- .../dolphin/helpers/dolphin_state_filename.h | 3 + applications/dolphin/passport/passport.c | 8 +- applications/extapps.scons | 2 + applications/gpio/gpio_app.c | 8 +- .../scenes/gpio_scene_usb_uart_close_rpc.c | 26 +- applications/gpio/usb_uart_bridge.c | 16 +- applications/gpio/views/gpio_test.c | 2 +- applications/gui/gui.c | 4 +- applications/gui/gui.h | 2 + applications/gui/modules/button_menu.c | 20 +- .../gui/modules/file_browser_worker.c | 18 +- applications/gui/modules/text_input.c | 6 +- applications/gui/modules/validators.c | 4 +- applications/gui/modules/validators.h | 2 +- applications/ibutton/ibutton.c | 96 +- applications/ibutton/ibutton_cli.c | 4 +- applications/ibutton/ibutton_custom_event.h | 2 + applications/ibutton/ibutton_i.h | 7 +- .../scenes/ibutton_scene_exit_confirm.c | 2 +- .../ibutton/scenes/ibutton_scene_read.c | 2 +- .../scenes/ibutton_scene_retry_confirm.c | 2 +- .../ibutton/scenes/ibutton_scene_rpc.c | 57 +- .../ibutton/scenes/ibutton_scene_save_name.c | 4 +- .../scenes/ibutton_scene_save_success.c | 4 +- .../scenes/ibutton_scene_write_success.c | 4 +- applications/infrared/infrared.c | 106 +- applications/infrared/infrared_brute_force.c | 10 +- applications/infrared/infrared_cli.c | 12 +- applications/infrared/infrared_custom_event.h | 6 + applications/infrared/infrared_i.h | 8 +- applications/infrared/infrared_remote.c | 12 +- .../common/infrared_scene_universal_common.c | 5 +- .../infrared/scenes/infrared_scene_ask_back.c | 6 +- .../scenes/infrared_scene_ask_retry.c | 4 +- .../infrared/scenes/infrared_scene_debug.c | 1 + .../scenes/infrared_scene_edit_delete.c | 6 +- .../scenes/infrared_scene_edit_delete_done.c | 6 +- .../scenes/infrared_scene_edit_rename_done.c | 5 +- .../infrared/scenes/infrared_scene_learn.c | 7 +- .../scenes/infrared_scene_learn_done.c | 5 +- .../scenes/infrared_scene_learn_success.c | 7 +- .../infrared/scenes/infrared_scene_remote.c | 6 +- .../infrared/scenes/infrared_scene_rpc.c | 44 +- .../scenes/infrared_scene_universal_tv.c | 2 +- applications/input/input.c | 7 +- applications/input/input.h | 2 + applications/lfrfid/lfrfid_app.cpp | 41 +- applications/lfrfid/lfrfid_app.h | 2 + applications/lfrfid/lfrfid_cli.cpp | 2 +- .../lfrfid/scene/lfrfid_app_scene_emulate.cpp | 12 - .../scene/lfrfid_app_scene_exit_confirm.cpp | 4 +- .../scene/lfrfid_app_scene_retry_confirm.cpp | 4 +- .../lfrfid/scene/lfrfid_app_scene_rpc.cpp | 37 +- .../lfrfid/scene/lfrfid_app_scene_write.cpp | 12 +- applications/loader/loader.c | 15 +- applications/loader/loader.h | 2 + applications/music_player/music_player.c | 21 +- applications/music_player/music_player_cli.c | 8 +- .../music_player/music_player_worker.c | 8 +- applications/nfc/application.fam | 2 +- applications/nfc/helpers/nfc_custom_event.h | 3 + applications/nfc/helpers/nfc_emv_parser.c | 7 +- applications/nfc/helpers/nfc_generators.c | 48 +- .../nfc/helpers/nfc_mf_classic_dict.c | 53 - .../nfc/helpers/nfc_mf_classic_dict.h | 15 - applications/nfc/nfc.c | 142 +-- applications/nfc/nfc_cli.c | 9 +- applications/nfc/nfc_i.h | 13 +- applications/nfc/nfc_worker.c | 708 ----------- applications/nfc/nfc_worker_i.h | 57 - applications/nfc/scenes/nfc_scene_card_menu.c | 90 -- applications/nfc/scenes/nfc_scene_config.h | 60 +- .../nfc/scenes/nfc_scene_detect_reader.c | 143 +++ .../nfc/scenes/nfc_scene_device_info.c | 2 +- .../nfc/scenes/nfc_scene_dict_not_found.c | 7 +- .../nfc/scenes/nfc_scene_emulate_uid.c | 9 +- ...success.c => nfc_scene_emv_read_success.c} | 36 +- .../nfc/scenes/nfc_scene_exit_confirm.c | 47 + .../nfc/scenes/nfc_scene_extra_actions.c | 57 + .../scenes/nfc_scene_mf_classic_dict_attack.c | 170 +++ ...assic.c => nfc_scene_mf_classic_emulate.c} | 29 +- .../nfc/scenes/nfc_scene_mf_classic_info.c | 72 ++ .../nfc/scenes/nfc_scene_mf_classic_keys.c | 59 + .../scenes/nfc_scene_mf_classic_keys_add.c | 57 + ...sic_menu.c => nfc_scene_mf_classic_menu.c} | 36 +- .../nfc_scene_mf_classic_read_success.c | 104 ++ ...sfire_app.c => nfc_scene_mf_desfire_app.c} | 36 +- ...ire_data.c => nfc_scene_mf_desfire_data.c} | 32 +- ...ire_menu.c => nfc_scene_mf_desfire_menu.c} | 15 +- ....c => nfc_scene_mf_desfire_read_success.c} | 59 +- ...ul.c => nfc_scene_mf_ultralight_emulate.c} | 29 +- .../nfc_scene_mf_ultralight_key_input.c | 44 + .../nfc/scenes/nfc_scene_mf_ultralight_menu.c | 74 ++ .../nfc_scene_mf_ultralight_read_auth.c | 107 ++ ...nfc_scene_mf_ultralight_read_auth_result.c | 98 ++ .../nfc_scene_mf_ultralight_read_success.c | 124 ++ .../nfc_scene_mf_ultralight_unlock_menu.c | 70 ++ .../nfc_scene_mf_ultralight_unlock_warn.c | 45 + .../nfc/scenes/nfc_scene_mifare_ul_menu.c | 60 - applications/nfc/scenes/nfc_scene_read.c | 111 ++ applications/nfc/scenes/nfc_scene_read_card.c | 51 - .../nfc/scenes/nfc_scene_read_card_success.c | 20 +- .../nfc/scenes/nfc_scene_read_emv_app.c | 58 - .../scenes/nfc_scene_read_emv_app_success.c | 83 -- .../nfc/scenes/nfc_scene_read_emv_data.c | 59 - .../scenes/nfc_scene_read_mifare_classic.c | 96 -- .../scenes/nfc_scene_read_mifare_desfire.c | 56 - .../nfc/scenes/nfc_scene_read_mifare_ul.c | 54 - .../scenes/nfc_scene_read_mifare_ul_success.c | 108 -- .../nfc/scenes/nfc_scene_restore_original.c | 8 +- .../nfc_scene_restore_original_confirm.c | 53 + .../nfc/scenes/nfc_scene_retry_confirm.c | 47 + applications/nfc/scenes/nfc_scene_rpc.c | 66 +- .../scenes/nfc_scene_run_emv_app_confirm.c | 49 - .../nfc/scenes/nfc_scene_save_success.c | 12 +- .../nfc/scenes/nfc_scene_saved_menu.c | 37 +- .../nfc/scenes/nfc_scene_scripts_menu.c | 82 -- applications/nfc/scenes/nfc_scene_set_uid.c | 12 +- applications/nfc/scenes/nfc_scene_start.c | 23 +- applications/nfc/views/dict_attack.c | 207 ++-- applications/nfc/views/dict_attack.h | 29 +- applications/notification/notification.h | 2 + applications/notification/notification_app.c | 12 +- applications/notification/notification_app.h | 3 +- .../notification/notification_messages.c | 48 + .../notification/notification_messages.h | 9 + .../notification/notification_settings_app.c | 8 +- .../notification_settings_filename.h | 3 + applications/picopass/picopass.c | 8 +- applications/picopass/picopass_device.c | 68 +- applications/picopass/picopass_device.h | 16 +- applications/picopass/picopass_worker.c | 58 +- .../scenes/picopass_scene_read_card.c | 2 +- .../scenes/picopass_scene_read_card_success.c | 13 +- .../power/battery_test_app/battery_test_app.c | 14 +- applications/power/power_cli.c | 6 +- applications/power/power_service/power.c | 12 +- applications/power/power_service/power.h | 2 + .../power_settings_app/power_settings_app.c | 10 +- .../scenes/power_settings_scene_power_off.c | 2 +- applications/rpc/rpc.c | 4 +- applications/rpc/rpc.h | 2 + applications/rpc/rpc_app.c | 180 ++- applications/rpc/rpc_app.h | 10 +- applications/rpc/rpc_gui.c | 8 +- applications/rpc/rpc_storage.c | 139 +-- applications/rpc/rpc_system.c | 8 +- applications/snake_game/snake_game.c | 4 +- applications/storage/storage.c | 8 +- applications/storage/storage.h | 15 +- applications/storage/storage_cli.c | 126 +- applications/storage/storage_external_api.c | 26 +- applications/storage/storage_internal_api.c | 9 +- applications/storage/storage_processing.c | 34 +- applications/storage/storage_test_app.c | 32 +- applications/storage/storages/storage_ext.c | 25 +- applications/storage/storages/storage_int.c | 41 +- .../storage_move_to_sd/application.fam | 3 +- .../storage_move_to_sd/storage_move_to_sd.c | 114 +- .../scenes/storage_settings_scene_benchmark.c | 14 +- .../storage_settings_scene_factory_reset.c | 2 +- .../storage_settings_scene_format_confirm.c | 14 +- .../storage_settings_scene_formatting.c | 5 +- .../storage_settings_scene_internal_info.c | 5 +- .../scenes/storage_settings_scene_sd_info.c | 10 +- .../storage_settings_scene_unmount_confirm.c | 14 +- .../scenes/storage_settings_scene_unmounted.c | 12 +- .../storage_settings/storage_settings.c | 12 +- .../subghz/helpers/subghz_custom_event.h | 5 + applications/subghz/helpers/subghz_types.h | 13 + .../subghz/scenes/subghz_scene_need_saving.c | 8 +- .../subghz/scenes/subghz_scene_read_raw.c | 17 +- .../subghz/scenes/subghz_scene_receiver.c | 22 +- .../scenes/subghz_scene_receiver_config.c | 73 +- .../scenes/subghz_scene_receiver_info.c | 21 +- applications/subghz/scenes/subghz_scene_rpc.c | 73 +- .../subghz/scenes/subghz_scene_save_name.c | 4 +- .../subghz/scenes/subghz_scene_set_type.c | 88 +- .../scenes/subghz_scene_show_error_sub.c | 4 +- .../subghz/scenes/subghz_scene_transmitter.c | 2 +- applications/subghz/subghz.c | 126 +- applications/subghz/subghz_cli.c | 33 +- applications/subghz/subghz_history.c | 30 +- applications/subghz/subghz_history.h | 13 +- applications/subghz/subghz_i.c | 135 ++- applications/subghz/subghz_i.h | 19 +- applications/subghz/subghz_setting.c | 206 +++- applications/subghz/subghz_setting.h | 22 + applications/subghz/views/subghz_read_raw.c | 82 +- applications/subghz/views/subghz_read_raw.h | 2 +- .../subghz/views/subghz_test_static.c | 14 +- applications/system/system_settings.c | 4 +- applications/u2f/scenes/u2f_scene_error.c | 16 +- applications/u2f/u2f_app.c | 8 +- applications/u2f/u2f_data.c | 38 +- .../flipper_format_string_test.c | 6 +- .../flipper_format/flipper_format_test.c | 50 +- .../unit_tests/infrared/infrared_test.c | 6 +- applications/unit_tests/nfc/nfc_test.c | 8 +- applications/unit_tests/rpc/rpc_test.c | 105 +- .../unit_tests/storage/dirwalk_test.c | 20 +- .../unit_tests/storage/storage_test.c | 82 +- applications/unit_tests/stream/stream_test.c | 61 +- applications/unit_tests/subghz/subghz_test.c | 152 ++- applications/unit_tests/test_index.c | 12 +- applications/updater/cli/updater_cli.c | 16 +- applications/updater/updater.c | 16 +- applications/updater/util/update_task.c | 4 +- .../updater/util/update_task_worker_backup.c | 13 +- assets/ReadMe.md | 12 +- assets/SConscript | 5 +- assets/dolphin/ReadMe.md | 4 +- assets/icons/Archive/ble_10px.png | Bin 301 -> 0 bytes .../icons/BLE/BLE_HID/Ble_connected_38x34.png | Bin 3694 -> 0 bytes .../BLE/BLE_HID/Ble_disconnected_24x34.png | Bin 3656 -> 0 bytes .../ActiveConnection_50x64.png} | Bin 3593 -> 3842 bytes .../Dolphin/DolphinFirstStart0_70x53.png | Bin 624 -> 0 bytes .../Dolphin/DolphinFirstStart1_59x53.png | Bin 558 -> 0 bytes .../Dolphin/DolphinFirstStart2_59x51.png | Bin 574 -> 0 bytes .../Dolphin/DolphinFirstStart3_57x48.png | Bin 527 -> 0 bytes .../Dolphin/DolphinFirstStart4_67x53.png | Bin 556 -> 0 bytes .../Dolphin/DolphinFirstStart5_54x49.png | Bin 1721 -> 0 bytes .../Dolphin/DolphinFirstStart6_58x54.png | Bin 553 -> 0 bytes assets/icons/Dolphin/DolphinOkay_41x43.png | Bin 423 -> 0 bytes .../icons/Infrared/InfraredLearn_128x64.png | Bin 4161 -> 0 bytes .../Infrared/InfraredSendShort_128x34.png | Bin 4009 -> 0 bytes assets/icons/Infrared/InfraredSend_128x64.png | Bin 4219 -> 0 bytes assets/icons/Interface/DoorLocked_10x56.png | Bin 372 -> 0 bytes .../icons/Interface/PassportBottom_128x17.png | Bin 364 -> 0 bytes assets/icons/Interface/PassportLeft_6x47.png | Bin 298 -> 0 bytes .../icons/MainMenu/Bluetooth_14/frame_01.png | Bin 100 -> 0 bytes .../icons/MainMenu/Bluetooth_14/frame_02.png | Bin 99 -> 0 bytes .../icons/MainMenu/Bluetooth_14/frame_03.png | Bin 99 -> 0 bytes .../icons/MainMenu/Bluetooth_14/frame_04.png | Bin 96 -> 0 bytes .../icons/MainMenu/Bluetooth_14/frame_05.png | Bin 97 -> 0 bytes .../icons/MainMenu/Bluetooth_14/frame_06.png | Bin 99 -> 0 bytes assets/icons/MainMenu/Bluetooth_14/frame_rate | 1 - assets/icons/MainMenu/Games_14/frame_01.png | Bin 96 -> 0 bytes assets/icons/MainMenu/Games_14/frame_02.png | Bin 99 -> 0 bytes assets/icons/MainMenu/Games_14/frame_03.png | Bin 105 -> 0 bytes assets/icons/MainMenu/Games_14/frame_04.png | Bin 104 -> 0 bytes assets/icons/MainMenu/Games_14/frame_05.png | Bin 105 -> 0 bytes assets/icons/MainMenu/Games_14/frame_06.png | Bin 102 -> 0 bytes assets/icons/MainMenu/Games_14/frame_07.png | Bin 101 -> 0 bytes assets/icons/MainMenu/Games_14/frame_08.png | Bin 102 -> 0 bytes assets/icons/MainMenu/Games_14/frame_09.png | Bin 100 -> 0 bytes assets/icons/MainMenu/Games_14/frame_rate | 1 - .../icons/MainMenu/Passport_14/frame_01.png | Bin 98 -> 0 bytes .../icons/MainMenu/Passport_14/frame_02.png | Bin 81 -> 0 bytes .../icons/MainMenu/Passport_14/frame_03.png | Bin 86 -> 0 bytes .../icons/MainMenu/Passport_14/frame_04.png | Bin 89 -> 0 bytes .../icons/MainMenu/Passport_14/frame_05.png | Bin 89 -> 0 bytes .../icons/MainMenu/Passport_14/frame_06.png | Bin 94 -> 0 bytes .../icons/MainMenu/Passport_14/frame_07.png | Bin 97 -> 0 bytes .../icons/MainMenu/Passport_14/frame_08.png | Bin 97 -> 0 bytes .../icons/MainMenu/Passport_14/frame_09.png | Bin 99 -> 0 bytes .../icons/MainMenu/Passport_14/frame_10.png | Bin 98 -> 0 bytes assets/icons/MainMenu/Passport_14/frame_rate | 1 - assets/icons/MainMenu/Power_14/frame_01.png | Bin 326 -> 0 bytes assets/icons/MainMenu/Power_14/frame_rate | 1 - .../icons/MainMenu/Tamagotchi_14/frame_01.png | Bin 106 -> 0 bytes .../icons/MainMenu/Tamagotchi_14/frame_02.png | Bin 106 -> 0 bytes .../icons/MainMenu/Tamagotchi_14/frame_03.png | Bin 106 -> 0 bytes .../icons/MainMenu/Tamagotchi_14/frame_04.png | Bin 106 -> 0 bytes .../icons/MainMenu/Tamagotchi_14/frame_05.png | Bin 106 -> 0 bytes .../icons/MainMenu/Tamagotchi_14/frame_06.png | Bin 106 -> 0 bytes .../icons/MainMenu/Tamagotchi_14/frame_rate | 1 - assets/icons/NFC/NFC_manual.png | Bin 0 -> 3804 bytes assets/icons/NFC/Reader_detect.png | Bin 0 -> 3771 bytes assets/icons/NFC/Restoring.png | Bin 0 -> 3794 bytes assets/icons/StatusBar/BadUsb_9x8.png | Bin 83 -> 0 bytes assets/icons/StatusBar/Battery_19x8.png | Bin 81 -> 0 bytes assets/icons/StatusBar/PlaceholderL_11x13.png | Bin 312 -> 0 bytes assets/icons/StatusBar/PlaceholderR_30x13.png | Bin 318 -> 0 bytes assets/protobuf | 2 +- assets/resources/Manifest | 8 +- assets/resources/infrared/assets/tv.ir | 36 + assets/resources/subghz/assets/setting_user | 27 +- assets/slideshow/first_start/frame_00.png | Bin 0 -> 602 bytes assets/slideshow/first_start/frame_01.png | Bin 0 -> 558 bytes assets/slideshow/first_start/frame_02.png | Bin 0 -> 562 bytes assets/slideshow/first_start/frame_03.png | Bin 0 -> 595 bytes assets/slideshow/first_start/frame_04.png | Bin 0 -> 548 bytes assets/slideshow/first_start/frame_05.png | Bin 0 -> 579 bytes assets/unit_tests/subghz/bett.sub | 7 + assets/unit_tests/subghz/bett_raw.sub | 8 + assets/unit_tests/subghz/doitrand.sub | 7 + assets/unit_tests/subghz/doitrand_raw.sub | 15 + assets/unit_tests/subghz/marantec.sub | 7 + assets/unit_tests/subghz/marantec_raw.sub | 14 + assets/unit_tests/subghz/phoenix_v2.sub | 7 + assets/unit_tests/subghz/phoenix_v2_raw.sub | 9 + assets/unit_tests/subghz/test_random_raw.sub | 20 + debug/fw.jflash | 90 ++ documentation/OTA.md | 9 +- documentation/fbt.md | 39 +- fbt | 14 +- fbt.cmd | 5 +- fbt_options.py | 1 + firmware.scons | 21 +- firmware/targets/f7/Inc/FreeRTOSConfig.h | 6 +- firmware/targets/f7/Src/dfu.c | 2 +- firmware/targets/f7/Src/update.c | 28 +- firmware/targets/f7/fatfs/stm32_adafruit_sd.c | 4 + firmware/targets/f7/fatfs/stm32_adafruit_sd.h | 1 + firmware/targets/f7/furi_hal/furi_hal.c | 13 +- .../targets/f7/furi_hal/furi_hal_interrupt.c | 19 + firmware/targets/f7/furi_hal/furi_hal_mpu.c | 66 ++ firmware/targets/f7/furi_hal/furi_hal_nfc.c | 7 +- .../targets/f7/furi_hal/furi_hal_subghz.c | 53 +- firmware/targets/f7/stm32wb55xx_flash.ld | 1 + .../targets/furi_hal_include/furi_hal_mpu.h | 86 ++ .../targets/furi_hal_include/furi_hal_nfc.h | 2 +- .../furi_hal_include/furi_hal_subghz.h | 11 +- furi/core/common_defines.h | 8 + furi/core/log.c | 44 +- furi/core/log.h | 30 +- furi/core/memmgr.c | 6 +- furi/core/stdglue.c | 102 -- furi/core/stdglue.h | 36 - furi/core/thread.c | 90 +- furi/core/thread.h | 35 + furi/furi.c | 1 - furi/furi.h | 1 - lib/ReadMe.md | 2 +- lib/SConscript | 9 +- lib/ST25RFAL002/include/rfal_picopass.h | 4 - lib/flipper_format/flipper_format.h | 8 +- lib/infrared/worker/infrared_worker.c | 4 +- lib/loclass.scons | 2 - lib/mbedtls.scons | 8 +- lib/misc.scons | 2 - lib/nfc/SConscript | 16 + lib/nfc/helpers/mf_classic_dict.c | 148 +++ lib/nfc/helpers/mf_classic_dict.h | 28 + .../nfc/helpers/nfc_debug_pcap.c | 2 +- .../nfc/helpers/nfc_debug_pcap.h | 0 {applications => lib}/nfc/nfc_device.c | 308 ++++- {applications => lib}/nfc/nfc_device.h | 24 +- {applications => lib}/nfc/nfc_types.c | 2 + {applications => lib}/nfc/nfc_types.h | 0 lib/nfc/nfc_worker.c | 570 +++++++++ {applications => lib}/nfc/nfc_worker.h | 41 +- lib/nfc/nfc_worker_i.h | 51 + lib/nfc/parsers/nfc_supported_card.c | 13 + lib/nfc/parsers/nfc_supported_card.h | 27 + lib/nfc/parsers/troyka_parser.c | 71 ++ lib/nfc/parsers/troyka_parser.h | 9 + .../protocols}/crypto1.c | 0 .../protocols}/crypto1.h | 0 lib/{nfc_protocols => nfc/protocols}/emv.c | 0 lib/{nfc_protocols => nfc/protocols}/emv.h | 0 .../protocols}/mifare_classic.c | 350 ++++-- .../protocols}/mifare_classic.h | 69 +- .../protocols}/mifare_common.c | 3 +- .../protocols}/mifare_common.h | 0 .../protocols}/mifare_desfire.c | 172 +++ .../protocols}/mifare_desfire.h | 4 + .../protocols}/mifare_ultralight.c | 380 +++++- .../protocols}/mifare_ultralight.h | 48 + .../protocols}/nfc_util.c | 0 .../protocols}/nfc_util.h | 0 lib/{nfc_protocols => nfc/protocols}/nfca.c | 0 lib/{nfc_protocols => nfc/protocols}/nfca.h | 0 lib/print/SConscript | 107 ++ lib/print/printf_tiny.c | 1037 +++++++++++++++++ lib/print/printf_tiny.h | 103 ++ lib/print/wrappers.c | 74 ++ lib/subghz/blocks/generic.c | 57 +- lib/subghz/blocks/generic.h | 16 +- lib/subghz/protocols/base.c | 6 +- lib/subghz/protocols/base.h | 6 +- lib/subghz/protocols/bett.c | 357 ++++++ lib/subghz/protocols/bett.h | 107 ++ lib/subghz/protocols/came.c | 29 +- lib/subghz/protocols/came.h | 6 +- lib/subghz/protocols/came_atomo.c | 19 +- lib/subghz/protocols/came_atomo.h | 6 +- lib/subghz/protocols/came_twee.c | 27 +- lib/subghz/protocols/came_twee.h | 6 +- lib/subghz/protocols/chamberlain_code.c | 25 +- lib/subghz/protocols/chamberlain_code.h | 6 +- lib/subghz/protocols/doitrand.c | 356 ++++++ lib/subghz/protocols/doitrand.h | 107 ++ lib/subghz/protocols/faac_slh.c | 21 +- lib/subghz/protocols/faac_slh.h | 6 +- lib/subghz/protocols/gate_tx.c | 25 +- lib/subghz/protocols/gate_tx.h | 6 +- lib/subghz/protocols/holtek.c | 27 +- lib/subghz/protocols/holtek.h | 6 +- lib/subghz/protocols/hormann.c | 25 +- lib/subghz/protocols/hormann.h | 6 +- lib/subghz/protocols/ido.c | 18 +- lib/subghz/protocols/ido.h | 6 +- lib/subghz/protocols/keeloq.c | 23 +- lib/subghz/protocols/keeloq.h | 12 +- lib/subghz/protocols/kia.c | 22 +- lib/subghz/protocols/kia.h | 6 +- lib/subghz/protocols/linear.c | 25 +- lib/subghz/protocols/linear.h | 6 +- lib/subghz/protocols/marantec.c | 393 +++++++ lib/subghz/protocols/marantec.h | 107 ++ lib/subghz/protocols/megacode.c | 27 +- lib/subghz/protocols/megacode.h | 6 +- lib/subghz/protocols/nero_radio.c | 27 +- lib/subghz/protocols/nero_radio.h | 6 +- lib/subghz/protocols/nero_sketch.c | 27 +- lib/subghz/protocols/nero_sketch.h | 6 +- lib/subghz/protocols/nice_flo.c | 29 +- lib/subghz/protocols/nice_flo.h | 6 +- lib/subghz/protocols/nice_flor_s.c | 21 +- lib/subghz/protocols/nice_flor_s.h | 6 +- lib/subghz/protocols/phoenix_v2.c | 339 ++++++ lib/subghz/protocols/phoenix_v2.h | 107 ++ lib/subghz/protocols/power_smart.c | 25 +- lib/subghz/protocols/power_smart.h | 6 +- lib/subghz/protocols/princeton.c | 41 +- lib/subghz/protocols/princeton.h | 6 +- lib/subghz/protocols/raw.c | 38 +- lib/subghz/protocols/raw.h | 14 +- lib/subghz/protocols/registry.c | 3 +- lib/subghz/protocols/registry.h | 4 + lib/subghz/protocols/scher_khan.c | 5 +- lib/subghz/protocols/scher_khan.h | 6 +- lib/subghz/protocols/secplus_v1.c | 25 +- lib/subghz/protocols/secplus_v1.h | 6 +- lib/subghz/protocols/secplus_v2.c | 25 +- lib/subghz/protocols/secplus_v2.h | 12 +- lib/subghz/protocols/somfy_keytis.c | 11 +- lib/subghz/protocols/somfy_keytis.h | 6 +- lib/subghz/protocols/somfy_telis.c | 19 +- lib/subghz/protocols/somfy_telis.h | 6 +- lib/subghz/protocols/star_line.c | 11 +- lib/subghz/protocols/star_line.h | 6 +- lib/subghz/subghz_file_encoder_worker.c | 4 +- lib/subghz/subghz_keystore.c | 16 +- lib/subghz/subghz_worker.c | 5 +- lib/subghz/types.h | 8 +- lib/toolbox/dir_walk.c | 2 +- lib/toolbox/path.c | 10 +- lib/toolbox/random_name.c | 19 +- lib/toolbox/saved_struct.c | 8 +- lib/toolbox/stream/buffered_file_stream.c | 135 ++- lib/toolbox/stream/buffered_file_stream.h | 14 +- lib/toolbox/stream/file_stream.c | 5 +- lib/toolbox/stream/stream_cache.c | 23 +- lib/toolbox/stream/stream_cache.h | 17 + lib/toolbox/tar/tar_archive.c | 44 +- lib/toolbox/tar/tar_archive.h | 7 +- lib/update_util/lfs_backup.c | 34 +- lib/update_util/update_manifest.c | 4 +- lib/update_util/update_manifest.h | 3 +- lib/update_util/update_operation.c | 24 +- scripts/ReadMe.md | 13 + scripts/flipper/storage.py | 11 +- scripts/selfupdate.py | 11 +- scripts/serial_cli.py | 14 + scripts/storage.py | 11 +- scripts/toolchain/fbtenv.cmd | 12 +- scripts/toolchain/fbtenv.sh | 219 +++- scripts/toolchain/unix-toolchain-download.sh | 135 --- site_scons/site_tools/fbt_apps.py | 14 +- site_scons/site_tools/fbt_assets.py | 8 +- site_scons/site_tools/fbt_dist.py | 37 +- site_scons/site_tools/fbt_version.py | 2 +- site_scons/site_tools/fwbin.py | 2 +- site_scons/site_tools/jflash.py | 27 + site_scons/site_tools/sconsmodular.py | 5 +- 563 files changed, 12367 insertions(+), 5175 deletions(-) create mode 100644 .github/ISSUE_TEMPLATE/01_bug_report.yml create mode 100644 .github/ISSUE_TEMPLATE/02_enhancements.yml create mode 100644 .github/ISSUE_TEMPLATE/03_feature_request.yml delete mode 100644 .github/ISSUE_TEMPLATE/bug_report.md create mode 100644 .github/ISSUE_TEMPLATE/config.yml delete mode 100644 .github/ISSUE_TEMPLATE/discuss-issue.md delete mode 100644 .github/ISSUE_TEMPLATE/feature_request.md delete mode 100644 .github/ISSUE_TEMPLATE/in-progress.md delete mode 100644 .github/ISSUE_TEMPLATE/need-help.md delete mode 100644 .github/workflows/build_toolchain.yml create mode 100644 .vscode/.gitignore create mode 100644 .vscode/ReadMe.md create mode 100644 .vscode/example/c_cpp_properties.json create mode 100644 .vscode/example/launch.json create mode 100644 .vscode/example/settings.json create mode 100644 .vscode/example/tasks.json create mode 100644 .vscode/extensions.json create mode 100644 applications/bt/bt_service/bt_keys_filename.h create mode 100644 applications/bt/bt_settings_filename.h create mode 100644 applications/desktop/desktop_settings/desktop_settings_filename.h create mode 100644 applications/desktop/helpers/slideshow_filename.h create mode 100644 applications/dolphin/helpers/dolphin_state_filename.h delete mode 100644 applications/nfc/helpers/nfc_mf_classic_dict.c delete mode 100644 applications/nfc/helpers/nfc_mf_classic_dict.h mode change 100755 => 100644 applications/nfc/nfc_i.h delete mode 100644 applications/nfc/nfc_worker.c delete mode 100644 applications/nfc/nfc_worker_i.h delete mode 100755 applications/nfc/scenes/nfc_scene_card_menu.c create mode 100644 applications/nfc/scenes/nfc_scene_detect_reader.c rename applications/nfc/scenes/{nfc_scene_read_emv_data_success.c => nfc_scene_emv_read_success.c} (78%) mode change 100755 => 100644 create mode 100644 applications/nfc/scenes/nfc_scene_exit_confirm.c create mode 100644 applications/nfc/scenes/nfc_scene_extra_actions.c create mode 100644 applications/nfc/scenes/nfc_scene_mf_classic_dict_attack.c rename applications/nfc/scenes/{nfc_scene_emulate_mifare_classic.c => nfc_scene_mf_classic_emulate.c} (57%) create mode 100644 applications/nfc/scenes/nfc_scene_mf_classic_info.c create mode 100644 applications/nfc/scenes/nfc_scene_mf_classic_keys.c create mode 100644 applications/nfc/scenes/nfc_scene_mf_classic_keys_add.c rename applications/nfc/scenes/{nfc_scene_mifare_classic_menu.c => nfc_scene_mf_classic_menu.c} (52%) create mode 100644 applications/nfc/scenes/nfc_scene_mf_classic_read_success.c rename applications/nfc/scenes/{nfc_scene_mifare_desfire_app.c => nfc_scene_mf_desfire_app.c} (73%) rename applications/nfc/scenes/{nfc_scene_mifare_desfire_data.c => nfc_scene_mf_desfire_data.c} (74%) rename applications/nfc/scenes/{nfc_scene_mifare_desfire_menu.c => nfc_scene_mf_desfire_menu.c} (61%) rename applications/nfc/scenes/{nfc_scene_read_mifare_desfire_success.c => nfc_scene_mf_desfire_read_success.c} (55%) rename applications/nfc/scenes/{nfc_scene_emulate_mifare_ul.c => nfc_scene_mf_ultralight_emulate.c} (56%) create mode 100644 applications/nfc/scenes/nfc_scene_mf_ultralight_key_input.c create mode 100644 applications/nfc/scenes/nfc_scene_mf_ultralight_menu.c create mode 100644 applications/nfc/scenes/nfc_scene_mf_ultralight_read_auth.c create mode 100644 applications/nfc/scenes/nfc_scene_mf_ultralight_read_auth_result.c create mode 100644 applications/nfc/scenes/nfc_scene_mf_ultralight_read_success.c create mode 100644 applications/nfc/scenes/nfc_scene_mf_ultralight_unlock_menu.c create mode 100644 applications/nfc/scenes/nfc_scene_mf_ultralight_unlock_warn.c delete mode 100755 applications/nfc/scenes/nfc_scene_mifare_ul_menu.c create mode 100644 applications/nfc/scenes/nfc_scene_read.c delete mode 100755 applications/nfc/scenes/nfc_scene_read_card.c delete mode 100755 applications/nfc/scenes/nfc_scene_read_emv_app.c delete mode 100755 applications/nfc/scenes/nfc_scene_read_emv_app_success.c delete mode 100755 applications/nfc/scenes/nfc_scene_read_emv_data.c delete mode 100644 applications/nfc/scenes/nfc_scene_read_mifare_classic.c delete mode 100644 applications/nfc/scenes/nfc_scene_read_mifare_desfire.c delete mode 100755 applications/nfc/scenes/nfc_scene_read_mifare_ul.c delete mode 100755 applications/nfc/scenes/nfc_scene_read_mifare_ul_success.c create mode 100644 applications/nfc/scenes/nfc_scene_restore_original_confirm.c create mode 100644 applications/nfc/scenes/nfc_scene_retry_confirm.c delete mode 100755 applications/nfc/scenes/nfc_scene_run_emv_app_confirm.c delete mode 100755 applications/nfc/scenes/nfc_scene_scripts_menu.c create mode 100644 applications/notification/notification_settings_filename.h mode change 100755 => 100644 applications/storage_settings/scenes/storage_settings_scene_unmounted.c delete mode 100644 assets/icons/Archive/ble_10px.png delete mode 100644 assets/icons/BLE/BLE_HID/Ble_connected_38x34.png delete mode 100644 assets/icons/BLE/BLE_HID/Ble_disconnected_24x34.png rename assets/icons/{PIN/Pin_cell_13x13.png => Common/ActiveConnection_50x64.png} (79%) delete mode 100644 assets/icons/Dolphin/DolphinFirstStart0_70x53.png delete mode 100644 assets/icons/Dolphin/DolphinFirstStart1_59x53.png delete mode 100644 assets/icons/Dolphin/DolphinFirstStart2_59x51.png delete mode 100644 assets/icons/Dolphin/DolphinFirstStart3_57x48.png delete mode 100644 assets/icons/Dolphin/DolphinFirstStart4_67x53.png delete mode 100644 assets/icons/Dolphin/DolphinFirstStart5_54x49.png delete mode 100644 assets/icons/Dolphin/DolphinFirstStart6_58x54.png delete mode 100644 assets/icons/Dolphin/DolphinOkay_41x43.png delete mode 100644 assets/icons/Infrared/InfraredLearn_128x64.png delete mode 100644 assets/icons/Infrared/InfraredSendShort_128x34.png delete mode 100644 assets/icons/Infrared/InfraredSend_128x64.png delete mode 100644 assets/icons/Interface/DoorLocked_10x56.png delete mode 100644 assets/icons/Interface/PassportBottom_128x17.png delete mode 100644 assets/icons/Interface/PassportLeft_6x47.png delete mode 100644 assets/icons/MainMenu/Bluetooth_14/frame_01.png delete mode 100644 assets/icons/MainMenu/Bluetooth_14/frame_02.png delete mode 100644 assets/icons/MainMenu/Bluetooth_14/frame_03.png delete mode 100644 assets/icons/MainMenu/Bluetooth_14/frame_04.png delete mode 100644 assets/icons/MainMenu/Bluetooth_14/frame_05.png delete mode 100644 assets/icons/MainMenu/Bluetooth_14/frame_06.png delete mode 100644 assets/icons/MainMenu/Bluetooth_14/frame_rate delete mode 100644 assets/icons/MainMenu/Games_14/frame_01.png delete mode 100644 assets/icons/MainMenu/Games_14/frame_02.png delete mode 100644 assets/icons/MainMenu/Games_14/frame_03.png delete mode 100644 assets/icons/MainMenu/Games_14/frame_04.png delete mode 100644 assets/icons/MainMenu/Games_14/frame_05.png delete mode 100644 assets/icons/MainMenu/Games_14/frame_06.png delete mode 100644 assets/icons/MainMenu/Games_14/frame_07.png delete mode 100644 assets/icons/MainMenu/Games_14/frame_08.png delete mode 100644 assets/icons/MainMenu/Games_14/frame_09.png delete mode 100644 assets/icons/MainMenu/Games_14/frame_rate delete mode 100644 assets/icons/MainMenu/Passport_14/frame_01.png delete mode 100644 assets/icons/MainMenu/Passport_14/frame_02.png delete mode 100644 assets/icons/MainMenu/Passport_14/frame_03.png delete mode 100644 assets/icons/MainMenu/Passport_14/frame_04.png delete mode 100644 assets/icons/MainMenu/Passport_14/frame_05.png delete mode 100644 assets/icons/MainMenu/Passport_14/frame_06.png delete mode 100644 assets/icons/MainMenu/Passport_14/frame_07.png delete mode 100644 assets/icons/MainMenu/Passport_14/frame_08.png delete mode 100644 assets/icons/MainMenu/Passport_14/frame_09.png delete mode 100644 assets/icons/MainMenu/Passport_14/frame_10.png delete mode 100644 assets/icons/MainMenu/Passport_14/frame_rate delete mode 100644 assets/icons/MainMenu/Power_14/frame_01.png delete mode 100644 assets/icons/MainMenu/Power_14/frame_rate delete mode 100644 assets/icons/MainMenu/Tamagotchi_14/frame_01.png delete mode 100644 assets/icons/MainMenu/Tamagotchi_14/frame_02.png delete mode 100644 assets/icons/MainMenu/Tamagotchi_14/frame_03.png delete mode 100644 assets/icons/MainMenu/Tamagotchi_14/frame_04.png delete mode 100644 assets/icons/MainMenu/Tamagotchi_14/frame_05.png delete mode 100644 assets/icons/MainMenu/Tamagotchi_14/frame_06.png delete mode 100644 assets/icons/MainMenu/Tamagotchi_14/frame_rate create mode 100644 assets/icons/NFC/NFC_manual.png create mode 100644 assets/icons/NFC/Reader_detect.png create mode 100644 assets/icons/NFC/Restoring.png delete mode 100644 assets/icons/StatusBar/BadUsb_9x8.png delete mode 100644 assets/icons/StatusBar/Battery_19x8.png delete mode 100644 assets/icons/StatusBar/PlaceholderL_11x13.png delete mode 100644 assets/icons/StatusBar/PlaceholderR_30x13.png create mode 100644 assets/slideshow/first_start/frame_00.png create mode 100644 assets/slideshow/first_start/frame_01.png create mode 100644 assets/slideshow/first_start/frame_02.png create mode 100644 assets/slideshow/first_start/frame_03.png create mode 100644 assets/slideshow/first_start/frame_04.png create mode 100644 assets/slideshow/first_start/frame_05.png create mode 100644 assets/unit_tests/subghz/bett.sub create mode 100644 assets/unit_tests/subghz/bett_raw.sub create mode 100644 assets/unit_tests/subghz/doitrand.sub create mode 100644 assets/unit_tests/subghz/doitrand_raw.sub create mode 100644 assets/unit_tests/subghz/marantec.sub create mode 100644 assets/unit_tests/subghz/marantec_raw.sub create mode 100644 assets/unit_tests/subghz/phoenix_v2.sub create mode 100644 assets/unit_tests/subghz/phoenix_v2_raw.sub create mode 100644 debug/fw.jflash create mode 100644 firmware/targets/f7/furi_hal/furi_hal_mpu.c create mode 100644 firmware/targets/furi_hal_include/furi_hal_mpu.h delete mode 100644 furi/core/stdglue.c delete mode 100644 furi/core/stdglue.h create mode 100644 lib/nfc/SConscript create mode 100644 lib/nfc/helpers/mf_classic_dict.c create mode 100644 lib/nfc/helpers/mf_classic_dict.h rename {applications => lib}/nfc/helpers/nfc_debug_pcap.c (98%) rename {applications => lib}/nfc/helpers/nfc_debug_pcap.h (100%) rename {applications => lib}/nfc/nfc_device.c (77%) rename {applications => lib}/nfc/nfc_device.h (78%) rename {applications => lib}/nfc/nfc_types.c (96%) rename {applications => lib}/nfc/nfc_types.h (100%) create mode 100644 lib/nfc/nfc_worker.c rename {applications => lib}/nfc/nfc_worker.h (54%) create mode 100644 lib/nfc/nfc_worker_i.h create mode 100644 lib/nfc/parsers/nfc_supported_card.c create mode 100644 lib/nfc/parsers/nfc_supported_card.h create mode 100644 lib/nfc/parsers/troyka_parser.c create mode 100644 lib/nfc/parsers/troyka_parser.h rename lib/{nfc_protocols => nfc/protocols}/crypto1.c (100%) rename lib/{nfc_protocols => nfc/protocols}/crypto1.h (100%) rename lib/{nfc_protocols => nfc/protocols}/emv.c (100%) rename lib/{nfc_protocols => nfc/protocols}/emv.h (100%) rename lib/{nfc_protocols => nfc/protocols}/mifare_classic.c (69%) rename lib/{nfc_protocols => nfc/protocols}/mifare_classic.h (55%) rename lib/{nfc_protocols => nfc/protocols}/mifare_common.c (80%) rename lib/{nfc_protocols => nfc/protocols}/mifare_common.h (100%) rename lib/{nfc_protocols => nfc/protocols}/mifare_desfire.c (62%) rename lib/{nfc_protocols => nfc/protocols}/mifare_desfire.h (98%) rename lib/{nfc_protocols => nfc/protocols}/mifare_ultralight.c (84%) rename lib/{nfc_protocols => nfc/protocols}/mifare_ultralight.h (78%) rename lib/{nfc_protocols => nfc/protocols}/nfc_util.c (100%) rename lib/{nfc_protocols => nfc/protocols}/nfc_util.h (100%) rename lib/{nfc_protocols => nfc/protocols}/nfca.c (100%) rename lib/{nfc_protocols => nfc/protocols}/nfca.h (100%) create mode 100644 lib/print/SConscript create mode 100644 lib/print/printf_tiny.c create mode 100644 lib/print/printf_tiny.h create mode 100644 lib/print/wrappers.c create mode 100644 lib/subghz/protocols/bett.c create mode 100644 lib/subghz/protocols/bett.h create mode 100644 lib/subghz/protocols/doitrand.c create mode 100644 lib/subghz/protocols/doitrand.h create mode 100644 lib/subghz/protocols/marantec.c create mode 100644 lib/subghz/protocols/marantec.h create mode 100644 lib/subghz/protocols/phoenix_v2.c create mode 100644 lib/subghz/protocols/phoenix_v2.h create mode 100644 scripts/serial_cli.py delete mode 100755 scripts/toolchain/unix-toolchain-download.sh create mode 100644 site_scons/site_tools/jflash.py diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index da0cfd2c0..71acb5f14 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -76,7 +76,7 @@ /lib/microtar/ @skotopes @DrZlo13 @hedger /lib/mlib/ @skotopes @DrZlo13 @hedger /lib/nanopb/ @skotopes @DrZlo13 @hedger -/lib/nfc_protocols/ @skotopes @DrZlo13 @hedger @gornekich +/lib/nfc/ @skotopes @DrZlo13 @hedger @gornekich /lib/one_wire/ @skotopes @DrZlo13 @hedger /lib/qrcode/ @skotopes @DrZlo13 @hedger /lib/subghz/ @skotopes @DrZlo13 @hedger @Skorpionm diff --git a/.github/ISSUE_TEMPLATE/01_bug_report.yml b/.github/ISSUE_TEMPLATE/01_bug_report.yml new file mode 100644 index 000000000..844d62c48 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/01_bug_report.yml @@ -0,0 +1,46 @@ +name: Bug report +description: File a bug reports regarding the firmware. +labels: ['bug'] +body: +- type: markdown + attributes: + value: | + Thank you for taking the time to fill out an issue, this template is meant for any issues related to the Flipper Zero firmware. + If you require help with the Flipper zero and its firmware, we ask that you join [our forum](https://forum.flipperzero.one) +- type: textarea + id: description + attributes: + label: Describe the bug. + description: "A clear and concise description of what the bug is." + validations: + required: true +- type: textarea + id: repro + attributes: + label: Reproduction + description: "How can this bug be reproduced?" + placeholder: | + 1. Switch on... + 2. Press button '....' + 3. Wait for the moon phase + 4. It burns + validations: + required: true +- type: input + id: target + attributes: + label: Target + description: Specify the target + # Target seems to be largely ignored by outside sources. +- type: textarea + id: logs + attributes: + label: Logs + description: Attach your debug logs here + render: Text + # Avoid rendering as Markdown here. +- type: textarea + id: anything-else + attributes: + label: Anything else? + description: Let us know if you have anything else to share. diff --git a/.github/ISSUE_TEMPLATE/02_enhancements.yml b/.github/ISSUE_TEMPLATE/02_enhancements.yml new file mode 100644 index 000000000..1768e193c --- /dev/null +++ b/.github/ISSUE_TEMPLATE/02_enhancements.yml @@ -0,0 +1,21 @@ +name: Enhancements +description: Suggest improvements for any existing functionality within the firmware. +body: +- type: markdown + attributes: + value: | + Thank you for taking the time to fill out an issue. This template is meant for feature requests and improvements to already existing functionality. + If you require help with the Flipper zero and its firmware, we ask that you join [our forum](https://forum.flipperzero.one) +- type: textarea + id: proposal + attributes: + label: "Describe the enhancement you're suggesting." + description: | + Feel free to describe in as much detail as you wish. + validations: + required: true +- type: textarea + id: anything-else + attributes: + label: Anything else? + description: Let us know if you have anything else to share. diff --git a/.github/ISSUE_TEMPLATE/03_feature_request.yml b/.github/ISSUE_TEMPLATE/03_feature_request.yml new file mode 100644 index 000000000..2af114547 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/03_feature_request.yml @@ -0,0 +1,24 @@ +name: Feature Request +description: For feature requests regarding the firmware. +labels: ['feature request'] +body: +- type: markdown + attributes: + value: | + Thank you for taking the time to fill out an issue, this template is meant for any feature suggestions. + If you require help with the Flipper zero and its firmware, we ask that you join [our forum](https://forum.flipperzero.one) +- type: textarea + id: proposal + attributes: + label: "Description of the feature you're suggesting." + description: | + Please describe your feature request in as many details as possible. + - Describe what it should do. + - Note whetever it is to extend existing functionality or introduce new functionality. + validations: + required: true +- type: textarea + id: anything-else + attributes: + label: Anything else? + description: Let us know if you have anything else to share. diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md deleted file mode 100644 index cf337499e..000000000 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ /dev/null @@ -1,30 +0,0 @@ ---- -name: Bug report -about: Create a report to help us improve -title: '' -labels: bug -assignees: '' - ---- - -**Describe the bug** -A clear and concise description of what the bug is. - -**To Reproduce** -Steps to reproduce the behavior: -1. Switch on... -2. Press button '....' -3. Wait for the moon phase -4. It burns - -**Expected behavior** -A clear and concise description of what you expected to happen. - -**Logs** -Add debug logs - -**Target** -Specify the target - -**Additional context** -Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 000000000..55ff9d29e --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,5 @@ +blank_issues_enabled: true +contact_links: + - name: Need help? + url: https://forum.flipperzero.one + about: For any question regarding on how to use the Flipper Zero and its firmware. diff --git a/.github/ISSUE_TEMPLATE/discuss-issue.md b/.github/ISSUE_TEMPLATE/discuss-issue.md deleted file mode 100644 index d036533d0..000000000 --- a/.github/ISSUE_TEMPLATE/discuss-issue.md +++ /dev/null @@ -1,12 +0,0 @@ ---- -name: Discuss issue -about: Start discussion about improvements -title: '' -labels: discussion -assignees: '' - ---- - -# What are you want to add or change - -# What questions do you have diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md deleted file mode 100644 index e301d68ce..000000000 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ /dev/null @@ -1,20 +0,0 @@ ---- -name: Feature request -about: Suggest an idea for this project -title: '' -labels: feature request -assignees: '' - ---- - -**Is your feature request related to a problem? Please describe.** -A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] - -**Describe the solution you'd like** -A clear and concise description of what you want to happen. - -**Describe alternatives you've considered** -A clear and concise description of any alternative solutions or features you've considered. - -**Additional context** -Add any other context or screenshots about the feature request here. diff --git a/.github/ISSUE_TEMPLATE/in-progress.md b/.github/ISSUE_TEMPLATE/in-progress.md deleted file mode 100644 index e8cf0332c..000000000 --- a/.github/ISSUE_TEMPLATE/in-progress.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -name: In progress -about: When you start doing your big deal -title: '' -labels: in progress -assignees: '' - ---- - -Shortly (or not) describe what are you will do diff --git a/.github/ISSUE_TEMPLATE/need-help.md b/.github/ISSUE_TEMPLATE/need-help.md deleted file mode 100644 index c7b1e2863..000000000 --- a/.github/ISSUE_TEMPLATE/need-help.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -name: Need help -about: Ask the community for help if you confused and can't figure out something -title: '' -labels: need help -assignees: '' - ---- - -# Describe you problem here diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index d3ebb9ad6..a1ae875a8 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -15,11 +15,8 @@ env: jobs: main: - runs-on: [self-hosted,FlipperZero] + runs-on: [self-hosted,FlipperZeroShell] steps: - - name: 'Cleanup workspace' - uses: AutoModality/action-clean@v1 - - name: 'Decontaminate previous build leftovers' run: | if [ -d .git ] @@ -32,12 +29,8 @@ jobs: uses: actions/checkout@v2 with: fetch-depth: 0 - submodules: true ref: ${{ github.event.pull_request.head.sha }} - - name: 'Build docker image' - uses: ./.github/actions/docker - - name: 'Make artifacts directory' run: | test -d artifacts && rm -rf artifacts || true @@ -71,40 +64,38 @@ jobs: run: | tar czpf artifacts/flipper-z-any-scripts-${{steps.names.outputs.suffix}}.tgz scripts - - name: 'Build the firmware in docker' - uses: ./.github/actions/docker - with: - run: | - set -e - for TARGET in ${TARGETS} - do - ./fbt TARGET_HW=`echo ${TARGET} | sed 's/f//'` --with-updater updater_package ${{ startsWith(github.ref, 'refs/tags') && 'DEBUG=0 COMPACT=1' || '' }} - done + - name: 'Build the firmware' + run: | + set -e + for TARGET in ${TARGETS} + do + FBT_TOOLCHAIN_PATH=/opt ./fbt TARGET_HW=`echo ${TARGET} | sed 's/f//'` updater_package ${{ startsWith(github.ref, 'refs/tags') && 'DEBUG=0 COMPACT=1' || '' }} + done - name: 'Move upload files' if: ${{ !github.event.pull_request.head.repo.fork }} - uses: ./.github/actions/docker - with: - run: | - set -e - for TARGET in ${TARGETS} - do - mv dist/${TARGET}-*/* artifacts/ - done + run: | + set -e + for TARGET in ${TARGETS} + do + mv dist/${TARGET}-*/* artifacts/ + done - name: 'Bundle self-update package' if: ${{ !github.event.pull_request.head.repo.fork }} - uses: ./.github/actions/docker - with: - run: | - set -e - for UPDATEBUNDLE in artifacts/*/ - do - BUNDLE_NAME=`echo $UPDATEBUNDLE | cut -d'/' -f2` - echo Packaging ${BUNDLE_NAME} - tar czpf artifacts/flipper-z-${BUNDLE_NAME}.tgz -C artifacts ${BUNDLE_NAME} - rm -rf artifacts/${BUNDLE_NAME} - done + run: | + set -e + for UPDATEBUNDLE in artifacts/*/ + do + BUNDLE_NAME=`echo $UPDATEBUNDLE | cut -d'/' -f2` + echo Packaging ${BUNDLE_NAME} + tar czpf artifacts/flipper-z-${BUNDLE_NAME}.tgz -C artifacts ${BUNDLE_NAME} + rm -rf artifacts/${BUNDLE_NAME} + done + + - name: "Check for uncommited changes" + run: | + git diff --exit-code - name: 'Bundle resources' if: ${{ !github.event.pull_request.head.repo.fork }} @@ -113,29 +104,23 @@ jobs: - name: 'Bundle core2 firmware' if: ${{ !github.event.pull_request.head.repo.fork }} - uses: ./.github/actions/docker - with: - run: | - ./fbt copro_dist - tar czpf artifacts/flipper-z-any-core2_firmware-${{steps.names.outputs.suffix}}.tgz -C assets core2_firmware + run: | + FBT_TOOLCHAIN_PATH=/opt ./fbt copro_dist + tar czpf artifacts/flipper-z-any-core2_firmware-${{steps.names.outputs.suffix}}.tgz -C assets core2_firmware - name: 'Upload artifacts to update server' if: ${{ !github.event.pull_request.head.repo.fork }} - uses: burnett01/rsync-deployments@5.1 - with: - switches: -avzP --delete --mkpath - path: artifacts/ - remote_path: "${{ secrets.RSYNC_DEPLOY_BASE_PATH }}${{steps.names.outputs.artifacts-path}}/" - remote_host: ${{ secrets.RSYNC_DEPLOY_HOST }} - remote_port: ${{ secrets.RSYNC_DEPLOY_PORT }} - remote_user: ${{ secrets.RSYNC_DEPLOY_USER }} - remote_key: ${{ secrets.RSYNC_DEPLOY_KEY }} + run: | + echo "${{ secrets.RSYNC_DEPLOY_KEY }}" > deploy_key; + chmod 600 ./deploy_key; + rsync -avzP --delete --mkpath \ + -e 'ssh -p ${{ secrets.RSYNC_DEPLOY_PORT }} -i ./deploy_key' \ + artifacts/ ${{ secrets.RSYNC_DEPLOY_USER }}@${{ secrets.RSYNC_DEPLOY_HOST }}:"${{ secrets.RSYNC_DEPLOY_BASE_PATH }}${{steps.names.outputs.artifacts-path}}/"; + rm ./deploy_key; - name: 'Trigger update server reindex' if: ${{ !github.event.pull_request.head.repo.fork }} - uses: wei/curl@master - with: - args: -X POST -F 'key=${{ secrets.REINDEX_KEY }}' ${{ secrets.REINDEX_URL }} + run: curl -X POST -F 'key=${{ secrets.REINDEX_KEY }}' ${{ secrets.REINDEX_URL }} - name: 'Find Previous Comment' if: ${{ !github.event.pull_request.head.repo.fork && github.event.pull_request }} @@ -161,11 +146,8 @@ jobs: compact: if: ${{ !startsWith(github.ref, 'refs/tags') }} - runs-on: [self-hosted,FlipperZero] + runs-on: [self-hosted,FlipperZeroShell] steps: - - name: 'Cleanup workspace' - uses: AutoModality/action-clean@v1 - - name: 'Decontaminate previous build leftovers' run: | if [ -d .git ] @@ -181,9 +163,6 @@ jobs: submodules: true ref: ${{ github.event.pull_request.head.sha }} - - name: 'Build docker image' - uses: ./.github/actions/docker - - name: 'Generate suffix and folder name' id: names run: | @@ -203,12 +182,10 @@ jobs: echo "WORKFLOW_BRANCH_OR_TAG=${BRANCH_OR_TAG}" >> $GITHUB_ENV echo "DIST_SUFFIX=${SUFFIX}" >> $GITHUB_ENV - - name: 'Build the firmware in docker' - uses: ./.github/actions/docker - with: - run: | - set -e - for TARGET in ${TARGETS} - do - ./fbt TARGET_HW=`echo ${TARGET} | sed 's/f//'` --with-updater updater_package DEBUG=0 COMPACT=1 - done + - name: 'Build the firmware' + run: | + set -e + for TARGET in ${TARGETS} + do + FBT_TOOLCHAIN_PATH=/opt ./fbt TARGET_HW=`echo ${TARGET} | sed 's/f//'` updater_package DEBUG=0 COMPACT=1 + done diff --git a/.github/workflows/build_toolchain.yml b/.github/workflows/build_toolchain.yml deleted file mode 100644 index 452dfbee6..000000000 --- a/.github/workflows/build_toolchain.yml +++ /dev/null @@ -1,46 +0,0 @@ -name: 'Build toolchain Docker image' - -on: - push: - branches: - - dev - tags: - - '*' - -jobs: - build: - runs-on: ubuntu-latest - steps: - - name: Checkout code - uses: actions/checkout@v2 - with: - fetch-depth: 0 - - name: Docker meta - id: meta - uses: docker/metadata-action@v3 - with: - images: flipperdevices/flipperzero-toolchain - flavor: latest=${{ startsWith(github.ref, 'refs/tags/') && !endsWith(github.ref, 'rc')}} - tags: | - type=ref,event=branch - type=ref,event=tag - - name: Set up QEMU - uses: docker/setup-qemu-action@v1 - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v1 - - name: Login to DockerHub - uses: docker/login-action@v1 - with: - username: ${{ secrets.DOCKERHUB_USERNAME }} - password: ${{ secrets.DOCKERHUB_TOKEN }} - - name: Build and push - id: docker_build - uses: docker/build-push-action@v2 - with: - context: docker/ - push: true - tags: ${{ steps.meta.outputs.tags }} - labels: ${{ steps.meta.outputs.labels }} - platforms: linux/amd64,linux/arm64 - cache-from: type=registry,ref=flipperdevices/flipperzero-toolchain:buildcache - cache-to: type=registry,ref=flipperdevices/flipperzero-toolchain:buildcache,mode=max diff --git a/.github/workflows/check_submodules.yml b/.github/workflows/check_submodules.yml index b02f9a8d0..f9699be87 100644 --- a/.github/workflows/check_submodules.yml +++ b/.github/workflows/check_submodules.yml @@ -1,17 +1,47 @@ -name: 'Check submodules' +name: 'Check submodules branch' on: + push: + branches: + - dev + - "release*" + tags: + - '*' pull_request: jobs: - protobuf: - runs-on: ubuntu-latest + check_protobuf: + runs-on: [self-hosted, FlipperZeroShell] steps: - - name: 'Checkout code' - uses: actions/checkout@v2 - - name: 'Check submodule commit branch' - uses: jtmullen/submodule-branch-check-action@v1 - with: - path: assets/protobuf - branch: dev - fetch_depth: 50 + - name: 'Decontaminate previous build leftovers' + run: | + if [ -d .git ] + then + git submodule status \ + || git checkout `git rev-list --max-parents=0 HEAD | tail -n 1` + fi + + - name: 'Checkout code' + uses: actions/checkout@v2 + with: + fetch-depth: 0 + + - name: 'Check protobuf branch' + run: | + SUB_PATH="assets/protobuf"; + SUB_BRANCH="dev"; + SUB_COMMITS_MIN=40; + cd "$SUB_PATH"; + SUBMODULE_HASH="$(git rev-parse HEAD)"; + BRANCHES=$(git branch -r --contains "$SUBMODULE_HASH"); + COMMITS_IN_BRANCH="$(git rev-list --count dev)"; + if [ $COMMITS_IN_BRANCH -lt $SUB_COMMITS_MIN ]; then + echo "::set-output name=fails::error"; + echo "::error::Error: Too low commits in $SUB_BRANCH of submodule $SUB_PATH: $COMMITS_IN_BRANCH(expected $SUB_COMMITS_MIN+)"; + exit 1; + fi + if ! grep -q "/$SUB_BRANCH" <<< "$BRANCHES"; then + echo "::set-output name=fails::error"; + echo "::error::Error: Submodule $SUB_PATH is not on branch $SUB_BRANCH"; + exit 1; + fi diff --git a/.github/workflows/lint_c.yml b/.github/workflows/lint_c.yml index f834586d3..aaff396ec 100644 --- a/.github/workflows/lint_c.yml +++ b/.github/workflows/lint_c.yml @@ -1,6 +1,6 @@ name: 'Lint C/C++ with clang-format' -on: +on: push: branches: - dev @@ -14,11 +14,8 @@ env: jobs: lint_c_cpp: - runs-on: [self-hosted,FlipperZero] + runs-on: [self-hosted,FlipperZeroShell] steps: - - name: 'Cleanup workspace' - uses: AutoModality/action-clean@v1 - - name: 'Decontaminate previous build leftovers' run: | if [ -d .git ] @@ -31,23 +28,10 @@ jobs: uses: actions/checkout@v2 with: fetch-depth: 0 - submodules: true - - - name: 'Docker cache' - uses: satackey/action-docker-layer-caching@v0.0.11 - continue-on-error: true - with: - key: docker-cache-${{ hashFiles('docker/**') }}-{hash} - restore-keys: docker-cache-${{ hashFiles('docker/**') }}- - - - name: 'Build docker image' - uses: ./.github/actions/docker - name: 'Check code formatting' id: syntax_check - uses: ./.github/actions/docker - with: - run: SET_GH_OUTPUT=1 ./fbt lint + run: SET_GH_OUTPUT=1 FBT_TOOLCHAIN_PATH=/opt ./fbt lint - name: Report code formatting errors if: failure() && steps.syntax_check.outputs.errors && github.event.pull_request @@ -59,4 +43,4 @@ jobs: ``` ${{ steps.syntax_check.outputs.errors }} ``` - You might want to run `docker compose exec dev make format` for an auto-fix. + You might want to run `./fbt format` for an auto-fix. diff --git a/.github/workflows/lint_python.yml b/.github/workflows/lint_python.yml index e665378b1..c059f4348 100644 --- a/.github/workflows/lint_python.yml +++ b/.github/workflows/lint_python.yml @@ -1,6 +1,6 @@ name: 'Python Lint' -on: +on: push: branches: - dev @@ -11,11 +11,8 @@ on: jobs: lint_python: - runs-on: ubuntu-latest + runs-on: [self-hosted,FlipperZeroShell] steps: - - name: 'Cleanup workspace' - uses: AutoModality/action-clean@v1 - - name: 'Decontaminate previous build leftovers' run: | if [ -d .git ] @@ -29,8 +26,5 @@ jobs: with: fetch-depth: 0 - - name: 'Setup python' - uses: actions/setup-python@v2 - - - name: 'Check python code with black' - uses: psf/black@20.8b1 + - name: 'Check code formatting' + run: SET_GH_OUTPUT=1 FBT_TOOLCHAIN_PATH=/opt ./fbt lint_py diff --git a/.github/workflows/reindex.yml b/.github/workflows/reindex.yml index 78f9cbead..ea850e705 100644 --- a/.github/workflows/reindex.yml +++ b/.github/workflows/reindex.yml @@ -7,9 +7,8 @@ on: jobs: reindex: name: 'Reindex updates' - runs-on: [self-hosted,FlipperZero] + runs-on: [self-hosted,FlipperZeroShell] steps: - name: Trigger reindex - uses: wei/curl@master - with: - args: -X POST -F 'key=${{ secrets.REINDEX_KEY }}' ${{ secrets.REINDEX_URL }} + run: | + curl -X POST -F 'key=${{ secrets.REINDEX_KEY }}' ${{ secrets.REINDEX_URL }} diff --git a/.vscode/.gitignore b/.vscode/.gitignore new file mode 100644 index 000000000..670df7d48 --- /dev/null +++ b/.vscode/.gitignore @@ -0,0 +1,4 @@ +./c_cpp_properties.json +./launch.json +./settings.json +./tasks.json diff --git a/.vscode/ReadMe.md b/.vscode/ReadMe.md new file mode 100644 index 000000000..5aed0435c --- /dev/null +++ b/.vscode/ReadMe.md @@ -0,0 +1,17 @@ +# Visual Studio Code workspace for Flipper Zero + +## Setup + + * To start developing with VSCode, run `./fbt vscode_dist` in project root. _That should only be done once_ + * After that, open firmware folder in VSCode: "File" > "Open folder" + + For more details on fbt, see [fbt docs](../documentation/fbt.md). + + +## Workflow + +Commands for building firmware are invoked through Build menu: Ctrl+Shift+B. + +To attach a debugging session, first build and flash firmware, then choose your debug probe in Debug menu (Ctrl+Shift+D). + +Note that you have to detach debugging session before rebuilding and re-flashing firmware. diff --git a/.vscode/example/c_cpp_properties.json b/.vscode/example/c_cpp_properties.json new file mode 100644 index 000000000..db08320c9 --- /dev/null +++ b/.vscode/example/c_cpp_properties.json @@ -0,0 +1,32 @@ +{ + "configurations": [ + { + "name": "Win32", + "compilerPath": "${workspaceFolder}/toolchain/i686-windows/bin/arm-none-eabi-gcc.exe", + "intelliSenseMode": "gcc-arm", + "compileCommands": "${workspaceFolder}/build/latest/compile_commands.json", + "configurationProvider": "ms-vscode.cpptools", + "cStandard": "gnu17", + "cppStandard": "c++17" + }, + { + "name": "Linux", + "compilerPath": "${workspaceFolder}/toolchain/x86_64-linux/bin/arm-none-eabi-gcc", + "intelliSenseMode": "gcc-arm", + "compileCommands": "${workspaceFolder}/build/latest/compile_commands.json", + "configurationProvider": "ms-vscode.cpptools", + "cStandard": "gnu17", + "cppStandard": "c++17" + }, + { + "name": "Mac", + "compilerPath": "${workspaceFolder}/toolchain/x86_64-darwin/bin/arm-none-eabi-gcc", + "intelliSenseMode": "gcc-arm", + "compileCommands": "${workspaceFolder}/build/latest/compile_commands.json", + "configurationProvider": "ms-vscode.cpptools", + "cStandard": "gnu17", + "cppStandard": "c++17" + } + ], + "version": 4 +} \ No newline at end of file diff --git a/.vscode/example/launch.json b/.vscode/example/launch.json new file mode 100644 index 000000000..bdad04285 --- /dev/null +++ b/.vscode/example/launch.json @@ -0,0 +1,87 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "inputs": [ + { + "id": "BLACKMAGIC", + "type": "command", + "command": "shellCommand.execute", + "args": { + "command": "./fbt get_blackmagic", + "description": "Get Blackmagic device", + } + } + ], + "configurations": [ + { + "name": "Attach FW (ST-Link)", + "cwd": "${workspaceFolder}", + "executable": "./build/latest/firmware.elf", + "request": "attach", + "type": "cortex-debug", + "servertype": "openocd", + "device": "stlink", + "svdFile": "./debug/STM32WB55_CM4.svd", + "rtos": "FreeRTOS", + "configFiles": [ + "interface/stlink.cfg", + "./debug/stm32wbx.cfg", + ], + "postAttachCommands": [ + // "attach 1", + "compare-sections", + ] + // "showDevDebugOutput": "raw", + }, + { + "name": "Attach FW (blackmagic)", + "cwd": "${workspaceFolder}", + "executable": "./build/latest/firmware.elf", + "request": "attach", + "type": "cortex-debug", + "servertype": "external", + "gdbTarget": "${input:BLACKMAGIC}", + "svdFile": "./debug/STM32WB55_CM4.svd", + "rtos": "FreeRTOS", + "postAttachCommands": [ + "monitor swdp_scan", + "attach 1", + "set confirm off", + "set mem inaccessible-by-default off", + "compare-sections", + ] + // "showDevDebugOutput": "raw", + }, + { + "name": "Attach FW (JLink)", + "cwd": "${workspaceFolder}", + "executable": "./build/latest/firmware.elf", + "request": "attach", + "type": "cortex-debug", + "servertype": "jlink", + "interface": "swd", + "device": "STM32WB55RG", + "svdFile": "./debug/STM32WB55_CM4.svd", + "rtos": "FreeRTOS", + // "showDevDebugOutput": "raw", + }, + { + "name": "fbt debug", + "type": "python", + "request": "launch", + "program": "./lib/scons/scripts/scons.py", + "args": [ + "sdk" + ] + }, + { + "name": "python debug", + "type": "python", + "request": "launch", + "program": "${file}", + "args": [] + } + ] +} \ No newline at end of file diff --git a/.vscode/example/settings.json b/.vscode/example/settings.json new file mode 100644 index 000000000..925c2e076 --- /dev/null +++ b/.vscode/example/settings.json @@ -0,0 +1,22 @@ +{ + "C_Cpp.default.cStandard": "gnu17", + "C_Cpp.default.cppStandard": "c++17", + "python.formatting.provider": "black", + "workbench.tree.indent": 12, + "cortex-debug.enableTelemetry": false, + "cortex-debug.variableUseNaturalFormat": true, + "cortex-debug.showRTOS": true, + "cortex-debug.armToolchainPath.windows": "${workspaceFolder}/toolchain/i686-windows/bin", + "cortex-debug.armToolchainPath.linux": "${workspaceFolder}/toolchain/x86_64-linux/bin", + "cortex-debug.armToolchainPath.osx": "${workspaceFolder}/toolchain/x86_64-darwin/bin", + "cortex-debug.openocdPath.windows": "${workspaceFolder}/toolchain/i686-windows/openocd/bin/openocd.exe", + "cortex-debug.openocdPath.linux": "${workspaceFolder}/toolchain/x86_64-linux/openocd/bin/openocd", + "cortex-debug.openocdPath.osx": "${workspaceFolder}/toolchain/x86_64-darwin/openocd/bin/openocd", + "editor.formatOnSave": true, + "files.associations": { + "*.scons": "python", + "SConscript": "python", + "SConstruct": "python", + "*.fam": "python", + } +} \ No newline at end of file diff --git a/.vscode/example/tasks.json b/.vscode/example/tasks.json new file mode 100644 index 000000000..32efa0068 --- /dev/null +++ b/.vscode/example/tasks.json @@ -0,0 +1,103 @@ +{ + // See https://go.microsoft.com/fwlink/?LinkId=733558 + // for the documentation about the tasks.json format + "version": "2.0.0", + "tasks": [ + { + "label": "[Release] Build", + "group": "build", + "type": "shell", + "command": "./fbt COMPACT=1 DEBUG=0" + }, + { + "label": "[Debug] Build", + "group": "build", + "type": "shell", + "command": "./fbt" + }, + { + "label": "[Release] Flash (ST-Link)", + "group": "build", + "type": "shell", + "command": "./fbt COMPACT=1 DEBUG=0 FORCE=1 flash" + }, + { + "label": "[Debug] Flash (ST-Link)", + "group": "build", + "type": "shell", + "command": "./fbt FORCE=1 flash" + }, + { + "label": "[Release] Flash (blackmagic)", + "group": "build", + "type": "shell", + "command": "./fbt COMPACT=1 DEBUG=0 FORCE=1 flash_blackmagic" + }, + { + "label": "[Debug] Flash (blackmagic)", + "group": "build", + "type": "shell", + "command": "./fbt FORCE=1 flash_blackmagic" + }, + { + "label": "[Release] Flash (JLink)", + "group": "build", + "type": "shell", + "command": "./fbt COMPACT=1 DEBUG=0 FORCE=1 jflash" + }, + { + "label": "[Debug] Flash (JLink)", + "group": "build", + "type": "shell", + "command": "./fbt FORCE=1 jflash" + }, + { + "label": "[Release] Build update bundle", + "group": "build", + "type": "shell", + "command": "./fbt updater_package COMPACT=1 DEBUG=0" + }, + { + "label": "[Debug] Build update bundle", + "group": "build", + "type": "shell", + "command": "./fbt updater_package" + }, + { + "label": "[Release] Build updater", + "group": "build", + "type": "shell", + "command": "./fbt updater_all COMPACT=1 DEBUG=0" + }, + { + "label": "[Debug] Build updater", + "group": "build", + "type": "shell", + "command": "./fbt updater_all" + }, + { + "label": "[Debug] Flash (USB, w/o resources)", + "group": "build", + "type": "shell", + "command": "./fbt FORCE=1 flash_usb" + }, + { + "label": "[Release] Flash (USB, w/o resources)", + "group": "build", + "type": "shell", + "command": "./fbt COMPACT=1 DEBUG=0 FORCE=1 flash_usb" + }, + { + "label": "[Debug:unit_tests] Flash (USB)", + "group": "build", + "type": "shell", + "command": "./fbt FIRMWARE_APP_SET=unit_tests FORCE=1 flash_usb" + }, + { + "label": "[Release] Flash (USB, with resources)", + "group": "build", + "type": "shell", + "command": "./fbt COMPACT=1 DEBUG=0 FORCE=1 flash_usb_full" + }, + ] +} \ No newline at end of file diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 000000000..b53ffc24c --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,15 @@ +{ + // See https://go.microsoft.com/fwlink/?LinkId=827846 to learn about workspace recommendations. + // Extension identifier format: ${publisher}.${name}. Example: vscode.csharp + // List of extensions which should be recommended for users of this workspace. + "recommendations": [ + "ms-python.black-formatter", + "ms-vscode.cpptools", + "amiralizadeh9480.cpp-helper", + "marus25.cortex-debug", + "zxh404.vscode-proto3", + "augustocdias.tasks-shell-input" + ], + // List of extensions recommended by VS Code that should not be recommended for users of this workspace. + "unwantedRecommendations": [] +} \ No newline at end of file diff --git a/ReadMe.md b/ReadMe.md index f3a350564..36a887dc4 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -29,11 +29,11 @@ They both must be flashed in the order described. With Flipper attached over USB: -`./fbt --with-updater flash_usb` +`./fbt flash_usb` Just building the package: -`./fbt --with-updater updater_package` +`./fbt updater_package` To update, copy the resulting directory to Flipper's SD card and navigate to `update.fuf` file in Archive app. diff --git a/SConstruct b/SConstruct index 7cdf88fca..52fe75a6a 100644 --- a/SConstruct +++ b/SConstruct @@ -7,6 +7,7 @@ # construction of certain targets behind command-line options. import os +import subprocess EnsurePythonVersion(3, 8) @@ -33,8 +34,10 @@ coreenv["ROOT_DIR"] = Dir(".") # Create a separate "dist" environment and add construction envs to it distenv = coreenv.Clone( - tools=["fbt_dist", "openocd", "blackmagic"], - OPENOCD_GDB_PIPE=["|openocd -c 'gdb_port pipe; log_output debug/openocd.log' ${[SINGLEQUOTEFUNC(OPENOCD_OPTS)]}"], + tools=["fbt_dist", "openocd", "blackmagic", "jflash"], + OPENOCD_GDB_PIPE=[ + "|openocd -c 'gdb_port pipe; log_output debug/openocd.log' ${[SINGLEQUOTEFUNC(OPENOCD_OPTS)]}" + ], GDBOPTS_BASE=[ "-ex", "target extended-remote ${GDBREMOTE}", @@ -61,6 +64,7 @@ distenv = coreenv.Clone( "-ex", "compare-sections", ], + JFLASHPROJECT="${ROOT_DIR.abspath}/debug/fw.jflash", ENV=os.environ, ) @@ -71,7 +75,9 @@ firmware_env = distenv.AddFwProject( ) # If enabled, initialize updater-related targets -if GetOption("fullenv"): +if GetOption("fullenv") or any( + filter(lambda target: "updater" in target or "flash_usb" in target, BUILD_TARGETS) +): updater_env = distenv.AddFwProject( base_env=coreenv, fw_type="updater", @@ -79,11 +85,11 @@ if GetOption("fullenv"): ) # Target for self-update package - dist_arguments = [ - "-r", - '"${ROOT_DIR.abspath}/assets/resources"', + dist_basic_arguments = [ "--bundlever", '"${UPDATE_VERSION_STRING}"', + ] + dist_radio_arguments = [ "--radio", '"${ROOT_DIR.abspath}/${COPRO_STACK_BIN_DIR}/${COPRO_STACK_BIN}"', "--radiotype", @@ -92,16 +98,34 @@ if GetOption("fullenv"): "--obdata", '"${ROOT_DIR.abspath}/${COPRO_OB_DATA}"', ] - if distenv["UPDATE_SPLASH"]: - dist_arguments += [ + dist_resource_arguments = [ + "-r", + '"${ROOT_DIR.abspath}/assets/resources"', + ] + dist_splash_arguments = ( + [ "--splash", distenv.subst("assets/slideshow/$UPDATE_SPLASH"), ] + if distenv["UPDATE_SPLASH"] + else [] + ) selfupdate_dist = distenv.DistCommand( "updater_package", (distenv["DIST_DEPENDS"], firmware_env["FW_RESOURCES"]), - DIST_EXTRA=dist_arguments, + DIST_EXTRA=[ + *dist_basic_arguments, + *dist_radio_arguments, + *dist_resource_arguments, + *dist_splash_arguments, + ], + ) + + selfupdate_min_dist = distenv.DistCommand( + "updater_minpackage", + distenv["DIST_DEPENDS"], + DIST_EXTRA=dist_basic_arguments, ) # Updater debug @@ -121,18 +145,16 @@ if GetOption("fullenv"): ) # Installation over USB & CLI - usb_update_package = distenv.UsbInstall( - "#build/usbinstall.flag", - ( - distenv["DIST_DEPENDS"], - firmware_env["FW_RESOURCES"], - selfupdate_dist, - ), + usb_update_package = distenv.AddUsbFlashTarget( + "#build/usbinstall.flag", (firmware_env["FW_RESOURCES"], selfupdate_dist) + ) + distenv.Alias("flash_usb_full", usb_update_package) + + usb_minupdate_package = distenv.AddUsbFlashTarget( + "#build/minusbinstall.flag", (selfupdate_min_dist,) ) - if distenv["FORCE"]: - distenv.AlwaysBuild(usb_update_package) - distenv.Depends(usb_update_package, selfupdate_dist) - distenv.Alias("flash_usb", usb_update_package) + distenv.Alias("flash_usb", usb_minupdate_package) + # Target for copying & renaming binaries to dist folder basic_dist = distenv.DistCommand("fw_dist", distenv["DIST_DEPENDS"]) @@ -147,8 +169,9 @@ distenv.Alias("copro_dist", copro_dist) firmware_flash = distenv.AddOpenOCDFlashTarget(firmware_env) distenv.Alias("flash", firmware_flash) -if distenv["FORCE"]: - distenv.AlwaysBuild(firmware_flash) + +firmware_jflash = distenv.AddJFlashTarget(firmware_env) +distenv.Alias("jflash", firmware_jflash) firmware_bm_flash = distenv.PhonyTarget( "flash_blackmagic", @@ -209,10 +232,55 @@ distenv.PhonyTarget( LINT_SOURCES=firmware_env["LINT_SOURCES"], ) +# PY_LINT_SOURCES contains recursively-built modules' SConscript files + application manifests +# Here we add additional Python files residing in repo root +firmware_env.Append( + PY_LINT_SOURCES=[ + # Py code folders + "site_scons", + "scripts", + # Extra files + "applications/extapps.scons", + "SConstruct", + "firmware.scons", + "fbt_options.py", + ] +) -# Find blackmagic probe + +black_commandline = "@${PYTHON3} -m black ${PY_BLACK_ARGS} ${PY_LINT_SOURCES}" +black_base_args = ["--include", '"\\.scons|\\.py|SConscript|SConstruct"'] + +distenv.PhonyTarget( + "lint_py", + black_commandline, + PY_BLACK_ARGS=[ + "--check", + "--diff", + *black_base_args, + ], + PY_LINT_SOURCES=firmware_env["PY_LINT_SOURCES"], +) +distenv.PhonyTarget( + "format_py", + black_commandline, + PY_BLACK_ARGS=black_base_args, + PY_LINT_SOURCES=firmware_env["PY_LINT_SOURCES"], +) + +# Start Flipper CLI via PySerial's miniterm +distenv.PhonyTarget("cli", "${PYTHON3} scripts/serial_cli.py") + + +# Find blackmagic probe distenv.PhonyTarget( "get_blackmagic", "@echo $( ${BLACKMAGIC_ADDR} $)", ) + +# Prepare vscode environment +vscode_dist = distenv.Install("#.vscode", distenv.Glob("#.vscode/example/*")) +distenv.Precious(vscode_dist) +distenv.NoClean(vscode_dist) +distenv.Alias("vscode_dist", vscode_dist) diff --git a/applications/about/about.c b/applications/about/about.c index 4d12ed024..f785c2e40 100644 --- a/applications/about/about.c +++ b/applications/about/about.c @@ -45,7 +45,7 @@ static DialogMessageButton compliance_screen(DialogsApp* dialogs, DialogMessage* DialogMessageButton result; const char* screen_text = "For all compliance\n" - "certificates please visit\n" + "certificates please visit:\n" "www.flipp.dev/compliance"; dialog_message_set_text(message, screen_text, 0, 0, AlignLeft, AlignTop); @@ -91,13 +91,13 @@ static DialogMessageButton hw_version_screen(DialogsApp* dialogs, DialogMessage* furi_hal_version_get_hw_region_name(), my_name ? my_name : "Unknown"); - string_cat_printf(buffer, "Serial number:\n"); + string_cat_printf(buffer, "Serial Number:\n"); const uint8_t* uid = furi_hal_version_uid(); for(size_t i = 0; i < furi_hal_version_uid_size(); i++) { string_cat_printf(buffer, "%02X", uid[i]); } - dialog_message_set_header(message, "HW Version info:", 0, 0, AlignLeft, AlignTop); + dialog_message_set_header(message, "HW Version Info:", 0, 0, AlignLeft, AlignTop); dialog_message_set_text(message, string_get_cstr(buffer), 0, 13, AlignLeft, AlignTop); result = dialog_message_show(dialogs, message); dialog_message_set_text(message, NULL, 0, 0, AlignLeft, AlignTop); @@ -133,7 +133,7 @@ static DialogMessageButton fw_version_screen(DialogsApp* dialogs, DialogMessage* version_get_gitbranch(ver)); } - dialog_message_set_header(message, "FW Version info:", 0, 0, AlignLeft, AlignTop); + dialog_message_set_header(message, "FW Version Info:", 0, 0, AlignLeft, AlignTop); dialog_message_set_text(message, string_get_cstr(buffer), 0, 13, AlignLeft, AlignTop); result = dialog_message_show(dialogs, message); dialog_message_set_text(message, NULL, 0, 0, AlignLeft, AlignTop); @@ -156,10 +156,10 @@ const size_t about_screens_count = sizeof(about_screens) / sizeof(AboutDialogScr int32_t about_settings_app(void* p) { UNUSED(p); - DialogsApp* dialogs = furi_record_open("dialogs"); + DialogsApp* dialogs = furi_record_open(RECORD_DIALOGS); DialogMessage* message = dialog_message_alloc(); - Gui* gui = furi_record_open("gui"); + Gui* gui = furi_record_open(RECORD_GUI); ViewDispatcher* view_dispatcher = view_dispatcher_alloc(); EmptyScreen* empty_screen = empty_screen_alloc(); const uint32_t empty_screen_index = 0; @@ -198,12 +198,12 @@ int32_t about_settings_app(void* p) { } dialog_message_free(message); - furi_record_close("dialogs"); + furi_record_close(RECORD_DIALOGS); view_dispatcher_remove_view(view_dispatcher, empty_screen_index); view_dispatcher_free(view_dispatcher); empty_screen_free(empty_screen); - furi_record_close("gui"); + furi_record_close(RECORD_GUI); return 0; } \ No newline at end of file diff --git a/applications/archive/archive.c b/applications/archive/archive.c index 99b9bb02f..bbe532c8c 100644 --- a/applications/archive/archive.c +++ b/applications/archive/archive.c @@ -16,7 +16,7 @@ bool archive_back_event_callback(void* context) { ArchiveApp* archive_alloc() { ArchiveApp* archive = malloc(sizeof(ArchiveApp)); - archive->gui = furi_record_open("gui"); + archive->gui = furi_record_open(RECORD_GUI); archive->text_input = text_input_alloc(); string_init(archive->fav_move_str); @@ -62,7 +62,7 @@ void archive_free(ArchiveApp* archive) { text_input_free(archive->text_input); - furi_record_close("gui"); + furi_record_close(RECORD_GUI); archive->gui = NULL; free(archive); diff --git a/applications/archive/helpers/archive_apps.c b/applications/archive/helpers/archive_apps.c index 3393993d4..9a3f825f1 100644 --- a/applications/archive/helpers/archive_apps.c +++ b/applications/archive/helpers/archive_apps.c @@ -29,21 +29,22 @@ bool archive_app_is_available(void* context, const char* path) { if(app == ArchiveAppTypeU2f) { bool file_exists = false; - Storage* fs_api = furi_record_open("storage"); + Storage* fs_api = furi_record_open(RECORD_STORAGE); File* file = storage_file_alloc(fs_api); - file_exists = storage_file_open(file, "/any/u2f/key.u2f", FSAM_READ, FSOM_OPEN_EXISTING); + file_exists = + storage_file_open(file, ANY_PATH("u2f/key.u2f"), FSAM_READ, FSOM_OPEN_EXISTING); if(file_exists) { storage_file_close(file); file_exists = - storage_file_open(file, "/any/u2f/cnt.u2f", FSAM_READ, FSOM_OPEN_EXISTING); + storage_file_open(file, ANY_PATH("u2f/cnt.u2f"), FSAM_READ, FSOM_OPEN_EXISTING); if(file_exists) { storage_file_close(file); } } storage_file_free(file); - furi_record_close("storage"); + furi_record_close(RECORD_STORAGE); return file_exists; } else { @@ -77,10 +78,10 @@ void archive_app_delete_file(void* context, const char* path) { bool res = false; if(app == ArchiveAppTypeU2f) { - Storage* fs_api = furi_record_open("storage"); - res = (storage_common_remove(fs_api, "/any/u2f/key.u2f") == FSE_OK); - res |= (storage_common_remove(fs_api, "/any/u2f/cnt.u2f") == FSE_OK); - furi_record_close("storage"); + Storage* fs_api = furi_record_open(RECORD_STORAGE); + res = (storage_common_remove(fs_api, ANY_PATH("u2f/key.u2f")) == FSE_OK); + res |= (storage_common_remove(fs_api, ANY_PATH("u2f/cnt.u2f")) == FSE_OK); + furi_record_close(RECORD_STORAGE); if(archive_is_favorite("/app:u2f/U2F Token")) { archive_favorites_delete("/app:u2f/U2F Token"); diff --git a/applications/archive/helpers/archive_browser.c b/applications/archive/helpers/archive_browser.c index 282841365..54759dadc 100644 --- a/applications/archive/helpers/archive_browser.c +++ b/applications/archive/helpers/archive_browser.c @@ -391,18 +391,18 @@ void archive_favorites_move_mode(ArchiveBrowserView* browser, bool active) { } static bool archive_is_dir_exists(string_t path) { - if(string_equal_str_p(path, "/any")) { + if(string_equal_str_p(path, STORAGE_ANY_PATH_PREFIX)) { return true; } bool state = false; FileInfo file_info; - Storage* storage = furi_record_open("storage"); + Storage* storage = furi_record_open(RECORD_STORAGE); if(storage_common_stat(storage, string_get_cstr(path), &file_info) == FSE_OK) { if(file_info.flags & FSF_DIRECTORY) { state = true; } } - furi_record_close("storage"); + furi_record_close(RECORD_STORAGE); return state; } diff --git a/applications/archive/helpers/archive_browser.h b/applications/archive/helpers/archive_browser.h index c4283123a..d6c79817a 100644 --- a/applications/archive/helpers/archive_browser.h +++ b/applications/archive/helpers/archive_browser.h @@ -1,6 +1,7 @@ #pragma once #include "../archive_i.h" +#include #define TAB_RIGHT InputKeyRight // Default tab swith direction #define TAB_DEFAULT ArchiveTabFavorites // Start tab @@ -8,14 +9,14 @@ static const char* tab_default_paths[] = { [ArchiveTabFavorites] = "/app:favorites", - [ArchiveTabIButton] = "/any/ibutton", - [ArchiveTabNFC] = "/any/nfc", - [ArchiveTabSubGhz] = "/any/subghz", - [ArchiveTabLFRFID] = "/any/lfrfid", - [ArchiveTabInfrared] = "/any/infrared", - [ArchiveTabBadUsb] = "/any/badusb", + [ArchiveTabIButton] = ANY_PATH("ibutton"), + [ArchiveTabNFC] = ANY_PATH("nfc"), + [ArchiveTabSubGhz] = ANY_PATH("subghz"), + [ArchiveTabLFRFID] = ANY_PATH("lfrfid"), + [ArchiveTabInfrared] = ANY_PATH("infrared"), + [ArchiveTabBadUsb] = ANY_PATH("badusb"), [ArchiveTabU2f] = "/app:u2f", - [ArchiveTabBrowser] = "/any", + [ArchiveTabBrowser] = STORAGE_ANY_PATH_PREFIX, }; static const char* known_ext[] = { diff --git a/applications/archive/helpers/archive_favorites.c b/applications/archive/helpers/archive_favorites.c index af7927e91..fc0cad575 100644 --- a/applications/archive/helpers/archive_favorites.c +++ b/applications/archive/helpers/archive_favorites.c @@ -49,7 +49,7 @@ static bool archive_favorites_read_line(File* file, string_t str_result) { uint16_t archive_favorites_count(void* context) { furi_assert(context); - Storage* fs_api = furi_record_open("storage"); + Storage* fs_api = furi_record_open(RECORD_STORAGE); File* file = storage_file_alloc(fs_api); string_t buffer; @@ -74,7 +74,7 @@ uint16_t archive_favorites_count(void* context) { string_clear(buffer); storage_file_free(file); - furi_record_close("storage"); + furi_record_close(RECORD_STORAGE); return lines; } @@ -82,7 +82,7 @@ uint16_t archive_favorites_count(void* context) { static bool archive_favourites_rescan() { string_t buffer; string_init(buffer); - Storage* fs_api = furi_record_open("storage"); + Storage* fs_api = furi_record_open(RECORD_STORAGE); File* file = storage_file_alloc(fs_api); File* fav_item_file = storage_file_alloc(fs_api); @@ -122,7 +122,7 @@ static bool archive_favourites_rescan() { storage_file_free(file); storage_file_free(fav_item_file); - furi_record_close("storage"); + furi_record_close(RECORD_STORAGE); return result; } @@ -131,7 +131,7 @@ bool archive_favorites_read(void* context) { furi_assert(context); ArchiveBrowserView* browser = context; - Storage* fs_api = furi_record_open("storage"); + Storage* fs_api = furi_record_open(RECORD_STORAGE); File* file = storage_file_alloc(fs_api); File* fav_item_file = storage_file_alloc(fs_api); @@ -184,7 +184,7 @@ bool archive_favorites_read(void* context) { string_clear(buffer); storage_file_free(file); storage_file_free(fav_item_file); - furi_record_close("storage"); + furi_record_close(RECORD_STORAGE); archive_set_item_count(browser, file_count); @@ -204,7 +204,7 @@ bool archive_favorites_delete(const char* format, ...) { va_end(args); string_init(buffer); - Storage* fs_api = furi_record_open("storage"); + Storage* fs_api = furi_record_open(RECORD_STORAGE); File* file = storage_file_alloc(fs_api); bool result = storage_file_open(file, ARCHIVE_FAV_PATH, FSAM_READ, FSOM_OPEN_EXISTING); @@ -233,7 +233,7 @@ bool archive_favorites_delete(const char* format, ...) { storage_common_remove(fs_api, ARCHIVE_FAV_TEMP_PATH); storage_file_free(file); - furi_record_close("storage"); + furi_record_close(RECORD_STORAGE); return result; } @@ -247,7 +247,7 @@ bool archive_is_favorite(const char* format, ...) { va_end(args); string_init(buffer); - Storage* fs_api = furi_record_open("storage"); + Storage* fs_api = furi_record_open(RECORD_STORAGE); File* file = storage_file_alloc(fs_api); bool found = false; @@ -272,7 +272,7 @@ bool archive_is_favorite(const char* format, ...) { string_clear(buffer); string_clear(filename); storage_file_free(file); - furi_record_close("storage"); + furi_record_close(RECORD_STORAGE); return found; } @@ -281,7 +281,7 @@ bool archive_favorites_rename(const char* src, const char* dst) { furi_assert(src); furi_assert(dst); - Storage* fs_api = furi_record_open("storage"); + Storage* fs_api = furi_record_open(RECORD_STORAGE); File* file = storage_file_alloc(fs_api); string_t path; @@ -318,7 +318,7 @@ bool archive_favorites_rename(const char* src, const char* dst) { storage_common_remove(fs_api, ARCHIVE_FAV_TEMP_PATH); storage_file_free(file); - furi_record_close("storage"); + furi_record_close(RECORD_STORAGE); return result; } @@ -333,7 +333,7 @@ void archive_favorites_save(void* context) { furi_assert(context); ArchiveBrowserView* browser = context; - Storage* fs_api = furi_record_open("storage"); + Storage* fs_api = furi_record_open(RECORD_STORAGE); File* file = storage_file_alloc(fs_api); for(size_t i = 0; i < archive_file_get_array_size(browser); i++) { @@ -346,5 +346,5 @@ void archive_favorites_save(void* context) { storage_common_remove(fs_api, ARCHIVE_FAV_TEMP_PATH); storage_file_free(file); - furi_record_close("storage"); + furi_record_close(RECORD_STORAGE); } diff --git a/applications/archive/helpers/archive_favorites.h b/applications/archive/helpers/archive_favorites.h index 681d0ec69..29eedcdb6 100644 --- a/applications/archive/helpers/archive_favorites.h +++ b/applications/archive/helpers/archive_favorites.h @@ -2,8 +2,8 @@ #include -#define ARCHIVE_FAV_PATH "/any/favorites.txt" -#define ARCHIVE_FAV_TEMP_PATH "/any/favorites.tmp" +#define ARCHIVE_FAV_PATH ANY_PATH("favorites.txt") +#define ARCHIVE_FAV_TEMP_PATH ANY_PATH("favorites.tmp") uint16_t archive_favorites_count(void* context); bool archive_favorites_read(void* context); diff --git a/applications/archive/helpers/archive_files.c b/applications/archive/helpers/archive_files.c index 7ff54fedb..9f8b4ee1b 100644 --- a/applications/archive/helpers/archive_files.c +++ b/applications/archive/helpers/archive_files.c @@ -60,7 +60,7 @@ void archive_file_append(const char* path, const char* format, ...) { string_init_vprintf(string, format, args); va_end(args); - Storage* fs_api = furi_record_open("storage"); + Storage* fs_api = furi_record_open(RECORD_STORAGE); File* file = storage_file_alloc(fs_api); bool res = storage_file_open(file, path, FSAM_WRITE, FSOM_OPEN_APPEND); @@ -71,7 +71,7 @@ void archive_file_append(const char* path, const char* format, ...) { storage_file_close(file); storage_file_free(file); - furi_record_close("storage"); + furi_record_close(RECORD_STORAGE); } void archive_delete_file(void* context, const char* format, ...) { @@ -84,7 +84,7 @@ void archive_delete_file(void* context, const char* format, ...) { va_end(args); ArchiveBrowserView* browser = context; - Storage* fs_api = furi_record_open("storage"); + Storage* fs_api = furi_record_open(RECORD_STORAGE); FileInfo fileinfo; storage_common_stat(fs_api, string_get_cstr(filename), &fileinfo); @@ -97,7 +97,7 @@ void archive_delete_file(void* context, const char* format, ...) { res = (storage_common_remove(fs_api, string_get_cstr(filename)) == FSE_OK); } - furi_record_close("storage"); + furi_record_close(RECORD_STORAGE); if(archive_is_favorite("%s", string_get_cstr(filename))) { archive_favorites_delete("%s", string_get_cstr(filename)); diff --git a/applications/archive/scenes/archive_scene_browser.c b/applications/archive/scenes/archive_scene_browser.c index e3581538c..e22ac792f 100644 --- a/applications/archive/scenes/archive_scene_browser.c +++ b/applications/archive/scenes/archive_scene_browser.c @@ -36,7 +36,7 @@ static void archive_loader_callback(const void* message, void* context) { static void archive_run_in_app(ArchiveBrowserView* browser, ArchiveFile_t* selected) { UNUSED(browser); - Loader* loader = furi_record_open("loader"); + Loader* loader = furi_record_open(RECORD_LOADER); LoaderStatus status; if(selected->is_app) { @@ -54,7 +54,7 @@ static void archive_run_in_app(ArchiveBrowserView* browser, ArchiveFile_t* selec FURI_LOG_E(TAG, "loader_start failed: %d", status); } - furi_record_close("loader"); + furi_record_close(RECORD_LOADER); } void archive_scene_browser_callback(ArchiveBrowserEvent event, void* context) { @@ -71,10 +71,10 @@ void archive_scene_browser_on_enter(void* context) { archive_update_focus(browser, archive->text_store); view_dispatcher_switch_to_view(archive->view_dispatcher, ArchiveViewBrowser); - Loader* loader = furi_record_open("loader"); + Loader* loader = furi_record_open(RECORD_LOADER); archive->loader_stop_subscription = furi_pubsub_subscribe(loader_get_pubsub(loader), archive_loader_callback, archive); - furi_record_close("loader"); + furi_record_close(RECORD_LOADER); uint32_t state = scene_manager_get_scene_state(archive->scene_manager, ArchiveAppSceneBrowser); @@ -92,8 +92,6 @@ bool archive_scene_browser_on_event(void* context, SceneManagerEvent event) { ArchiveBrowserView* browser = archive->browser; ArchiveFile_t* selected = archive_get_current_file(browser); - const char* name = archive_get_name(browser); - bool known_app = archive_is_known_app(selected->type); bool favorites = archive_get_tab(browser) == ArchiveTabFavorites; bool consumed = false; @@ -108,18 +106,19 @@ bool archive_scene_browser_on_event(void* context, SceneManagerEvent event) { consumed = true; break; case ArchiveBrowserEventFileMenuRun: - if(known_app) { + if(archive_is_known_app(selected->type)) { archive_run_in_app(browser, selected); archive_show_file_menu(browser, false); } consumed = true; break; - case ArchiveBrowserEventFileMenuPin: + case ArchiveBrowserEventFileMenuPin: { + const char* name = archive_get_name(browser); if(favorites) { archive_favorites_delete(name); archive_file_array_rm_selected(browser); archive_show_file_menu(browser, false); - } else if(known_app) { + } else if(archive_is_known_app(selected->type)) { if(archive_is_favorite("%s", name)) { archive_favorites_delete("%s", name); } else { @@ -128,12 +127,12 @@ bool archive_scene_browser_on_event(void* context, SceneManagerEvent event) { archive_show_file_menu(browser, false); } consumed = true; - break; + } break; case ArchiveBrowserEventFileMenuRename: if(favorites) { browser->callback(ArchiveBrowserEventEnterFavMove, browser->context); - } else if((known_app) && (selected->is_app == false)) { + } else if((archive_is_known_app(selected->type)) && (selected->is_app == false)) { archive_show_file_menu(browser, false); scene_manager_set_scene_state( archive->scene_manager, ArchiveAppSceneBrowser, SCENE_STATE_NEED_REFRESH); @@ -196,10 +195,10 @@ bool archive_scene_browser_on_event(void* context, SceneManagerEvent event) { if(!archive_is_home(browser)) { archive_leave_dir(browser); } else { - Loader* loader = furi_record_open("loader"); + Loader* loader = furi_record_open(RECORD_LOADER); furi_pubsub_unsubscribe( loader_get_pubsub(loader), archive->loader_stop_subscription); - furi_record_close("loader"); + furi_record_close(RECORD_LOADER); view_dispatcher_stop(archive->view_dispatcher); } @@ -216,7 +215,7 @@ bool archive_scene_browser_on_event(void* context, SceneManagerEvent event) { void archive_scene_browser_on_exit(void* context) { ArchiveApp* archive = (ArchiveApp*)context; - Loader* loader = furi_record_open("loader"); + Loader* loader = furi_record_open(RECORD_LOADER); furi_pubsub_unsubscribe(loader_get_pubsub(loader), archive->loader_stop_subscription); - furi_record_close("loader"); + furi_record_close(RECORD_LOADER); } diff --git a/applications/archive/scenes/archive_scene_rename.c b/applications/archive/scenes/archive_scene_rename.c index d292dd60f..293fa89af 100644 --- a/applications/archive/scenes/archive_scene_rename.c +++ b/applications/archive/scenes/archive_scene_rename.c @@ -37,7 +37,7 @@ void archive_scene_rename_on_enter(void* context) { false); ValidatorIsFile* validator_is_file = validator_is_file_alloc_init( - string_get_cstr(archive->browser->path), archive->file_extension, NULL); + string_get_cstr(archive->browser->path), archive->file_extension, ""); text_input_set_validator(text_input, validator_is_file_callback, validator_is_file); string_clear(filename); @@ -51,7 +51,7 @@ bool archive_scene_rename_on_event(void* context, SceneManagerEvent event) { if(event.type == SceneManagerEventTypeCustom) { if(event.event == SCENE_RENAME_CUSTOM_EVENT) { - Storage* fs_api = furi_record_open("storage"); + Storage* fs_api = furi_record_open(RECORD_STORAGE); const char* path_src = archive_get_name(archive->browser); ArchiveFile_t* file = archive_get_current_file(archive->browser); @@ -62,7 +62,7 @@ bool archive_scene_rename_on_event(void* context, SceneManagerEvent event) { string_cat_printf(path_dst, "/%s%s", archive->text_store, known_ext[file->type]); storage_common_rename(fs_api, path_src, string_get_cstr(path_dst)); - furi_record_close("storage"); + furi_record_close(RECORD_STORAGE); if(file->fav) { archive_favorites_rename(path_src, string_get_cstr(path_dst)); diff --git a/applications/bad_usb/bad_usb_app.c b/applications/bad_usb/bad_usb_app.c index 65ccc575b..09d7d3468 100644 --- a/applications/bad_usb/bad_usb_app.c +++ b/applications/bad_usb/bad_usb_app.c @@ -28,13 +28,13 @@ BadUsbApp* bad_usb_app_alloc(char* arg) { string_init(app->file_path); - if(arg != NULL) { + if(arg && strlen(arg)) { string_set_str(app->file_path, arg); } - app->gui = furi_record_open("gui"); - app->notifications = furi_record_open("notification"); - app->dialogs = furi_record_open("dialogs"); + app->gui = furi_record_open(RECORD_GUI); + app->notifications = furi_record_open(RECORD_NOTIFICATION); + app->dialogs = furi_record_open(RECORD_DIALOGS); app->view_dispatcher = view_dispatcher_alloc(); view_dispatcher_enable_queue(app->view_dispatcher); @@ -79,7 +79,6 @@ void bad_usb_app_free(BadUsbApp* app) { furi_assert(app); // Views - view_dispatcher_remove_view(app->view_dispatcher, BadUsbAppViewFileSelect); view_dispatcher_remove_view(app->view_dispatcher, BadUsbAppViewWork); bad_usb_free(app->bad_usb_view); @@ -92,9 +91,9 @@ void bad_usb_app_free(BadUsbApp* app) { scene_manager_free(app->scene_manager); // Close records - furi_record_close("gui"); - furi_record_close("notification"); - furi_record_close("dialogs"); + furi_record_close(RECORD_GUI); + furi_record_close(RECORD_NOTIFICATION); + furi_record_close(RECORD_DIALOGS); string_clear(app->file_path); diff --git a/applications/bad_usb/bad_usb_app_i.h b/applications/bad_usb/bad_usb_app_i.h index c82419e0f..6378bddfb 100644 --- a/applications/bad_usb/bad_usb_app_i.h +++ b/applications/bad_usb/bad_usb_app_i.h @@ -14,7 +14,7 @@ #include #include "views/bad_usb_view.h" -#define BAD_USB_APP_PATH_FOLDER "/any/badusb" +#define BAD_USB_APP_PATH_FOLDER ANY_PATH("badusb") #define BAD_USB_APP_EXTENSION ".txt" typedef enum { @@ -38,6 +38,5 @@ struct BadUsbApp { typedef enum { BadUsbAppViewError, - BadUsbAppViewFileSelect, BadUsbAppViewWork, } BadUsbAppView; diff --git a/applications/bad_usb/bad_usb_script.c b/applications/bad_usb/bad_usb_script.c index a06f6d593..9d9d3e397 100644 --- a/applications/bad_usb/bad_usb_script.c +++ b/applications/bad_usb/bad_usb_script.c @@ -455,7 +455,7 @@ static int32_t bad_usb_worker(void* context) { FuriHalUsbInterface* usb_mode_prev = furi_hal_usb_get_config(); FURI_LOG_I(WORKER_TAG, "Init"); - File* script_file = storage_file_alloc(furi_record_open("storage")); + File* script_file = storage_file_alloc(furi_record_open(RECORD_STORAGE)); string_init(bad_usb->line); string_init(bad_usb->line_prev); diff --git a/applications/bad_usb/scenes/bad_usb_scene_error.c b/applications/bad_usb/scenes/bad_usb_scene_error.c index c8e1c361f..abd7b38b9 100644 --- a/applications/bad_usb/scenes/bad_usb_scene_error.c +++ b/applications/bad_usb/scenes/bad_usb_scene_error.c @@ -27,20 +27,22 @@ void bad_usb_scene_error_on_enter(void* context) { AlignTop, FontSecondary, "No SD card or\napp data found.\nThis app will not\nwork without\nrequired files."); + widget_add_button_element( + app->widget, GuiButtonTypeLeft, "Back", bad_usb_scene_error_event_callback, app); } else if(app->error == BadUsbAppErrorCloseRpc) { + widget_add_icon_element(app->widget, 78, 0, &I_ActiveConnection_50x64); + widget_add_string_multiline_element( + app->widget, 3, 2, AlignLeft, AlignTop, FontPrimary, "Connection\nis active!"); widget_add_string_multiline_element( app->widget, - 63, - 10, - AlignCenter, + 3, + 30, + AlignLeft, AlignTop, FontSecondary, - "Disconnect from\ncompanion app\nto use this function"); + "Disconnect from\nPC or phone to\nuse this function."); } - widget_add_button_element( - app->widget, GuiButtonTypeLeft, "Back", bad_usb_scene_error_event_callback, app); - view_dispatcher_switch_to_view(app->view_dispatcher, BadUsbAppViewError); } diff --git a/applications/bt/bt_cli.c b/applications/bt/bt_cli.c index d12724d2e..3aa1bc752 100644 --- a/applications/bt/bt_cli.c +++ b/applications/bt/bt_cli.c @@ -33,7 +33,7 @@ static void bt_cli_command_carrier_tx(Cli* cli, string_t args, void* context) { break; } - Bt* bt = furi_record_open("bt"); + Bt* bt = furi_record_open(RECORD_BT); bt_disconnect(bt); furi_hal_bt_reinit(); printf("Transmitting carrier at %d channel at %d dB power\r\n", channel, power); @@ -46,7 +46,7 @@ static void bt_cli_command_carrier_tx(Cli* cli, string_t args, void* context) { furi_hal_bt_stop_tone_tx(); bt_set_profile(bt, BtProfileSerial); - furi_record_close("bt"); + furi_record_close(RECORD_BT); } while(false); } @@ -60,7 +60,7 @@ static void bt_cli_command_carrier_rx(Cli* cli, string_t args, void* context) { break; } - Bt* bt = furi_record_open("bt"); + Bt* bt = furi_record_open(RECORD_BT); bt_disconnect(bt); furi_hal_bt_reinit(); printf("Receiving carrier at %d channel\r\n", channel); @@ -77,7 +77,7 @@ static void bt_cli_command_carrier_rx(Cli* cli, string_t args, void* context) { furi_hal_bt_stop_packet_test(); bt_set_profile(bt, BtProfileSerial); - furi_record_close("bt"); + furi_record_close(RECORD_BT); } while(false); } @@ -107,7 +107,7 @@ static void bt_cli_command_packet_tx(Cli* cli, string_t args, void* context) { break; } - Bt* bt = furi_record_open("bt"); + Bt* bt = furi_record_open(RECORD_BT); bt_disconnect(bt); furi_hal_bt_reinit(); printf( @@ -125,7 +125,7 @@ static void bt_cli_command_packet_tx(Cli* cli, string_t args, void* context) { printf("Transmitted %lu packets", furi_hal_bt_get_transmitted_packets()); bt_set_profile(bt, BtProfileSerial); - furi_record_close("bt"); + furi_record_close(RECORD_BT); } while(false); } @@ -144,7 +144,7 @@ static void bt_cli_command_packet_rx(Cli* cli, string_t args, void* context) { break; } - Bt* bt = furi_record_open("bt"); + Bt* bt = furi_record_open(RECORD_BT); bt_disconnect(bt); furi_hal_bt_reinit(); printf("Receiving packets at %d channel at %d M datarate\r\n", channel, datarate); @@ -160,7 +160,7 @@ static void bt_cli_command_packet_rx(Cli* cli, string_t args, void* context) { printf("Received %hu packets", packets_received); bt_set_profile(bt, BtProfileSerial); - furi_record_close("bt"); + furi_record_close(RECORD_BT); } while(false); } @@ -180,7 +180,7 @@ static void bt_cli_print_usage() { static void bt_cli(Cli* cli, string_t args, void* context) { UNUSED(context); - furi_record_open("bt"); + furi_record_open(RECORD_BT); string_t cmd; string_init(cmd); @@ -223,14 +223,14 @@ static void bt_cli(Cli* cli, string_t args, void* context) { } string_clear(cmd); - furi_record_close("bt"); + furi_record_close(RECORD_BT); } void bt_on_system_start() { #ifdef SRV_CLI - Cli* cli = furi_record_open("cli"); - cli_add_command(cli, "bt", CliCommandFlagDefault, bt_cli, NULL); - furi_record_close("cli"); + Cli* cli = furi_record_open(RECORD_CLI); + cli_add_command(cli, RECORD_BT, CliCommandFlagDefault, bt_cli, NULL); + furi_record_close(RECORD_CLI); #else UNUSED(bt_cli); #endif diff --git a/applications/bt/bt_debug_app/bt_debug_app.c b/applications/bt/bt_debug_app/bt_debug_app.c index 468f8a547..ac442de0a 100644 --- a/applications/bt/bt_debug_app/bt_debug_app.c +++ b/applications/bt/bt_debug_app/bt_debug_app.c @@ -35,7 +35,7 @@ BtDebugApp* bt_debug_app_alloc() { bt_settings_load(&app->settings); // Gui - app->gui = furi_record_open("gui"); + app->gui = furi_record_open(RECORD_GUI); // View dispatcher app->view_dispatcher = view_dispatcher_alloc(); @@ -88,7 +88,7 @@ void bt_debug_app_free(BtDebugApp* app) { view_dispatcher_free(app->view_dispatcher); // Close gui record - furi_record_close("gui"); + furi_record_close(RECORD_GUI); app->gui = NULL; // Free rest @@ -99,7 +99,7 @@ int32_t bt_debug_app(void* p) { UNUSED(p); if(!furi_hal_bt_is_testing_supported()) { FURI_LOG_E(TAG, "Incorrect radio stack: radio testing fetures are absent."); - DialogsApp* dialogs = furi_record_open("dialogs"); + DialogsApp* dialogs = furi_record_open(RECORD_DIALOGS); dialog_message_show_storage_error(dialogs, "Incorrect\nRadioStack"); return 255; } diff --git a/applications/bt/bt_hid_app/bt_hid.c b/applications/bt/bt_hid_app/bt_hid.c index 47ee2268d..b6f00e939 100755 --- a/applications/bt/bt_hid_app/bt_hid.c +++ b/applications/bt/bt_hid_app/bt_hid.c @@ -70,13 +70,13 @@ BtHid* bt_hid_app_alloc() { BtHid* app = malloc(sizeof(BtHid)); // Gui - app->gui = furi_record_open("gui"); + app->gui = furi_record_open(RECORD_GUI); // Bt - app->bt = furi_record_open("bt"); + app->bt = furi_record_open(RECORD_BT); // Notifications - app->notifications = furi_record_open("notification"); + app->notifications = furi_record_open(RECORD_NOTIFICATION); // View dispatcher app->view_dispatcher = view_dispatcher_alloc(); @@ -90,7 +90,7 @@ BtHid* bt_hid_app_alloc() { submenu_add_item( app->submenu, "Keyboard", BtHidSubmenuIndexKeyboard, bt_hid_submenu_callback, app); submenu_add_item( - app->submenu, "Media player", BtHidSubmenuIndexMedia, bt_hid_submenu_callback, app); + app->submenu, "Media Player", BtHidSubmenuIndexMedia, bt_hid_submenu_callback, app); submenu_add_item(app->submenu, "Mouse", BtHidSubmenuIndexMouse, bt_hid_submenu_callback, app); view_set_previous_callback(submenu_get_view(app->submenu), bt_hid_exit); view_dispatcher_add_view( @@ -103,7 +103,7 @@ BtHid* bt_hid_app_alloc() { dialog_ex_set_left_button_text(app->dialog, "Exit"); dialog_ex_set_right_button_text(app->dialog, "Stay"); dialog_ex_set_center_button_text(app->dialog, "Menu"); - dialog_ex_set_header(app->dialog, "Close current app?", 16, 12, AlignLeft, AlignTop); + dialog_ex_set_header(app->dialog, "Close Current App?", 16, 12, AlignLeft, AlignTop); view_dispatcher_add_view( app->view_dispatcher, BtHidViewExitConfirm, dialog_ex_get_view(app->dialog)); @@ -161,11 +161,11 @@ void bt_hid_app_free(BtHid* app) { view_dispatcher_free(app->view_dispatcher); // Close records - furi_record_close("gui"); + furi_record_close(RECORD_GUI); app->gui = NULL; - furi_record_close("notification"); + furi_record_close(RECORD_NOTIFICATION); app->notifications = NULL; - furi_record_close("bt"); + furi_record_close(RECORD_BT); app->bt = NULL; // Free rest diff --git a/applications/bt/bt_service/bt.c b/applications/bt/bt_service/bt.c index d122aa17b..bc80acc15 100644 --- a/applications/bt/bt_service/bt.c +++ b/applications/bt/bt_service/bt.c @@ -124,23 +124,23 @@ Bt* bt_alloc() { // Pin code view port bt->pin_code_view_port = bt_pin_code_view_port_alloc(bt); // Notification - bt->notification = furi_record_open("notification"); + bt->notification = furi_record_open(RECORD_NOTIFICATION); // Gui - bt->gui = furi_record_open("gui"); + bt->gui = furi_record_open(RECORD_GUI); gui_add_view_port(bt->gui, bt->statusbar_view_port, GuiLayerStatusBarLeft); gui_add_view_port(bt->gui, bt->pin_code_view_port, GuiLayerFullscreen); // Dialogs - bt->dialogs = furi_record_open("dialogs"); + bt->dialogs = furi_record_open(RECORD_DIALOGS); bt->dialog_message = dialog_message_alloc(); // Power - bt->power = furi_record_open("power"); + bt->power = furi_record_open(RECORD_POWER); FuriPubSub* power_pubsub = power_get_pubsub(bt->power); furi_pubsub_subscribe(power_pubsub, bt_battery_level_changed_callback, bt); // RPC - bt->rpc = furi_record_open("rpc"); + bt->rpc = furi_record_open(RECORD_RPC); bt->rpc_event = furi_event_flag_alloc(); // API evnent @@ -347,13 +347,14 @@ static void bt_close_connection(Bt* bt) { furi_event_flag_set(bt->api_event, BT_API_UNLOCK_EVENT); } -int32_t bt_srv() { +int32_t bt_srv(void* p) { + UNUSED(p); Bt* bt = bt_alloc(); if(furi_hal_rtc_get_boot_mode() != FuriHalRtcBootModeNormal) { FURI_LOG_W(TAG, "Skipped BT init: device in special startup mode"); ble_glue_wait_for_c2_start(FURI_HAL_BT_C2_START_TIMEOUT); - furi_record_create("bt", bt); + furi_record_create(RECORD_BT, bt); return 0; } @@ -381,7 +382,7 @@ int32_t bt_srv() { bt->status = BtStatusUnavailable; } - furi_record_create("bt", bt); + furi_record_create(RECORD_BT, bt); BtMessage message; while(1) { diff --git a/applications/bt/bt_service/bt.h b/applications/bt/bt_service/bt.h index 4ca6a32f1..6e4e1b824 100644 --- a/applications/bt/bt_service/bt.h +++ b/applications/bt/bt_service/bt.h @@ -7,6 +7,8 @@ extern "C" { #endif +#define RECORD_BT "bt" + typedef struct Bt Bt; typedef enum { diff --git a/applications/bt/bt_service/bt_keys_filename.h b/applications/bt/bt_service/bt_keys_filename.h new file mode 100644 index 000000000..da1d3f54e --- /dev/null +++ b/applications/bt/bt_service/bt_keys_filename.h @@ -0,0 +1,3 @@ +#pragma once + +#define BT_KEYS_STORAGE_FILE_NAME ".bt.keys" diff --git a/applications/bt/bt_service/bt_keys_storage.c b/applications/bt/bt_service/bt_keys_storage.c index e4f426c8c..91d97d67e 100644 --- a/applications/bt/bt_service/bt_keys_storage.c +++ b/applications/bt/bt_service/bt_keys_storage.c @@ -2,8 +2,9 @@ #include #include +#include -#define BT_KEYS_STORAGE_PATH "/int/bt.keys" +#define BT_KEYS_STORAGE_PATH INT_PATH(BT_KEYS_STORAGE_FILE_NAME) #define BT_KEYS_STORAGE_VERSION (0) #define BT_KEYS_STORAGE_MAGIC (0x18) diff --git a/applications/bt/bt_service/bt_keys_storage.h b/applications/bt/bt_service/bt_keys_storage.h index 68d4476aa..b82b1035f 100644 --- a/applications/bt/bt_service/bt_keys_storage.h +++ b/applications/bt/bt_service/bt_keys_storage.h @@ -1,6 +1,7 @@ #pragma once #include "bt_i.h" +#include "bt_keys_filename.h" bool bt_keys_storage_load(Bt* bt); diff --git a/applications/bt/bt_settings.c b/applications/bt/bt_settings.c index dbb2fd05d..1eaf6c7d7 100644 --- a/applications/bt/bt_settings.c +++ b/applications/bt/bt_settings.c @@ -2,8 +2,9 @@ #include #include +#include -#define BT_SETTINGS_PATH "/int/bt.settings" +#define BT_SETTINGS_PATH INT_PATH(BT_SETTINGS_FILE_NAME) #define BT_SETTINGS_VERSION (0) #define BT_SETTINGS_MAGIC (0x19) diff --git a/applications/bt/bt_settings.h b/applications/bt/bt_settings.h index 1a98668ac..260d9c0e0 100644 --- a/applications/bt/bt_settings.h +++ b/applications/bt/bt_settings.h @@ -1,5 +1,7 @@ #pragma once +#include "bt_settings_filename.h" + #include #include diff --git a/applications/bt/bt_settings_app/bt_settings_app.c b/applications/bt/bt_settings_app/bt_settings_app.c index cb55e9310..f211c7128 100755 --- a/applications/bt/bt_settings_app/bt_settings_app.c +++ b/applications/bt/bt_settings_app/bt_settings_app.c @@ -17,8 +17,8 @@ BtSettingsApp* bt_settings_app_alloc() { // Load settings bt_settings_load(&app->settings); - app->gui = furi_record_open("gui"); - app->bt = furi_record_open("bt"); + app->gui = furi_record_open(RECORD_GUI); + app->bt = furi_record_open(RECORD_BT); // View Dispatcher and Scene Manager app->view_dispatcher = view_dispatcher_alloc(); @@ -70,8 +70,8 @@ void bt_settings_app_free(BtSettingsApp* app) { scene_manager_free(app->scene_manager); // Records - furi_record_close("gui"); - furi_record_close("bt"); + furi_record_close(RECORD_GUI); + furi_record_close(RECORD_BT); free(app); } diff --git a/applications/bt/bt_settings_app/scenes/bt_settings_scene_forget_dev_confirm.c b/applications/bt/bt_settings_app/scenes/bt_settings_scene_forget_dev_confirm.c index be9a7196c..964736b66 100755 --- a/applications/bt/bt_settings_app/scenes/bt_settings_scene_forget_dev_confirm.c +++ b/applications/bt/bt_settings_app/scenes/bt_settings_scene_forget_dev_confirm.c @@ -10,9 +10,9 @@ void bt_settings_scene_forget_dev_confirm_dialog_callback(DialogExResult result, void bt_settings_scene_forget_dev_confirm_on_enter(void* context) { BtSettingsApp* app = context; DialogEx* dialog = app->dialog; - dialog_ex_set_header(dialog, "Unpair all devices?", 64, 3, AlignCenter, AlignTop); + dialog_ex_set_header(dialog, "Unpair All Devices?", 64, 3, AlignCenter, AlignTop); dialog_ex_set_text( - dialog, "All previous pairings\nwill be lost.", 64, 22, AlignCenter, AlignTop); + dialog, "All previous pairings\nwill be lost!", 64, 22, AlignCenter, AlignTop); dialog_ex_set_left_button_text(dialog, "Back"); dialog_ex_set_right_button_text(dialog, "Unpair"); dialog_ex_set_context(dialog, app); diff --git a/applications/bt/bt_settings_filename.h b/applications/bt/bt_settings_filename.h new file mode 100644 index 000000000..e5fb7ec48 --- /dev/null +++ b/applications/bt/bt_settings_filename.h @@ -0,0 +1,3 @@ +#pragma once + +#define BT_SETTINGS_FILE_NAME ".bt.settings" diff --git a/applications/cli/cli.c b/applications/cli/cli.c index 7d0f8dab9..e554ac898 100644 --- a/applications/cli/cli.c +++ b/applications/cli/cli.c @@ -185,7 +185,7 @@ static void cli_execute_command(Cli* cli, CliCommand* command, string_t args) { // Ensure that we running alone if(!(command->flags & CliCommandFlagParallelSafe)) { - Loader* loader = furi_record_open("loader"); + Loader* loader = furi_record_open(RECORD_LOADER); bool safety_lock = loader_lock(loader); if(safety_lock) { // Execute command @@ -194,7 +194,7 @@ static void cli_execute_command(Cli* cli, CliCommand* command, string_t args) { } else { printf("Other application is running, close it first"); } - furi_record_close("loader"); + furi_record_close(RECORD_LOADER); } else { // Execute command command->callback(cli, args, command->context); @@ -439,9 +439,9 @@ void cli_session_open(Cli* cli, void* session) { cli->session = session; if(cli->session != NULL) { cli->session->init(); - furi_stdglue_set_thread_stdout_callback(cli->session->tx_stdout); + furi_thread_set_stdout_callback(cli->session->tx_stdout); } else { - furi_stdglue_set_thread_stdout_callback(NULL); + furi_thread_set_stdout_callback(NULL); } furi_semaphore_release(cli->idle_sem); furi_check(furi_mutex_release(cli->mutex) == FuriStatusOk); @@ -455,7 +455,7 @@ void cli_session_close(Cli* cli) { cli->session->deinit(); } cli->session = NULL; - furi_stdglue_set_thread_stdout_callback(NULL); + furi_thread_set_stdout_callback(NULL); furi_check(furi_mutex_release(cli->mutex) == FuriStatusOk); } @@ -466,12 +466,12 @@ int32_t cli_srv(void* p) { // Init basic cli commands cli_commands_init(cli); - furi_record_create("cli", cli); + furi_record_create(RECORD_CLI, cli); if(cli->session != NULL) { - furi_stdglue_set_thread_stdout_callback(cli->session->tx_stdout); + furi_thread_set_stdout_callback(cli->session->tx_stdout); } else { - furi_stdglue_set_thread_stdout_callback(NULL); + furi_thread_set_stdout_callback(NULL); } if(furi_hal_rtc_get_boot_mode() == FuriHalRtcBootModeNormal) { diff --git a/applications/cli/cli.h b/applications/cli/cli.h index 29f27392c..549e72cc5 100644 --- a/applications/cli/cli.h +++ b/applications/cli/cli.h @@ -33,6 +33,8 @@ typedef enum { CliCommandFlagInsomniaSafe = (1 << 1), /**< Safe to run with insomnia mode on */ } CliCommandFlag; +#define RECORD_CLI "cli" + /** Cli type anonymous structure */ typedef struct Cli Cli; diff --git a/applications/cli/cli_commands.c b/applications/cli/cli_commands.c index c99f4edc7..198877962 100644 --- a/applications/cli/cli_commands.c +++ b/applications/cli/cli_commands.c @@ -166,13 +166,13 @@ void cli_command_vibro(Cli* cli, string_t args, void* context) { UNUSED(cli); UNUSED(context); if(!string_cmp(args, "0")) { - NotificationApp* notification = furi_record_open("notification"); + NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION); notification_message_block(notification, &sequence_reset_vibro); - furi_record_close("notification"); + furi_record_close(RECORD_NOTIFICATION); } else if(!string_cmp(args, "1")) { - NotificationApp* notification = furi_record_open("notification"); + NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION); notification_message_block(notification, &sequence_set_vibro_on); - furi_record_close("notification"); + furi_record_close(RECORD_NOTIFICATION); } else { cli_print_usage("vibro", "<1|0>", string_get_cstr(args)); } @@ -244,9 +244,9 @@ void cli_command_led(Cli* cli, string_t args, void* context) { }; // Send notification - NotificationApp* notification = furi_record_open("notification"); + NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION); notification_internal_message_block(notification, ¬ification_sequence); - furi_record_close("notification"); + furi_record_close(RECORD_NOTIFICATION); } void cli_command_ps(Cli* cli, string_t args, void* context) { diff --git a/applications/cli/cli_i.h b/applications/cli/cli_i.h index 076dd75ed..8f0bd85d5 100755 --- a/applications/cli/cli_i.h +++ b/applications/cli/cli_i.h @@ -25,7 +25,7 @@ struct CliSession { void (*deinit)(void); size_t (*rx)(uint8_t* buffer, size_t size, uint32_t timeout); void (*tx)(const uint8_t* buffer, size_t size); - void (*tx_stdout)(void* _cookie, const char* data, size_t size); + void (*tx_stdout)(const char* data, size_t size); bool (*is_connected)(void); }; diff --git a/applications/cli/cli_vcp.c b/applications/cli/cli_vcp.c index 5d66b8f8d..5a8b44dcf 100644 --- a/applications/cli/cli_vcp.c +++ b/applications/cli/cli_vcp.c @@ -277,8 +277,7 @@ static void cli_vcp_tx(const uint8_t* buffer, size_t size) { #endif } -static void cli_vcp_tx_stdout(void* _cookie, const char* data, size_t size) { - UNUSED(_cookie); +static void cli_vcp_tx_stdout(const char* data, size_t size) { cli_vcp_tx((const uint8_t*)data, size); } diff --git a/applications/crypto/crypto_cli.c b/applications/crypto/crypto_cli.c index a5ea80f8d..26e1fb9c0 100644 --- a/applications/crypto/crypto_cli.c +++ b/applications/crypto/crypto_cli.c @@ -317,9 +317,9 @@ static void crypto_cli(Cli* cli, string_t args, void* context) { void crypto_on_system_start() { #ifdef SRV_CLI - Cli* cli = furi_record_open("cli"); + Cli* cli = furi_record_open(RECORD_CLI); cli_add_command(cli, "crypto", CliCommandFlagDefault, crypto_cli, NULL); - furi_record_close("cli"); + furi_record_close(RECORD_CLI); #else UNUSED(crypto_cli); #endif diff --git a/applications/debug_tools/blink_test.c b/applications/debug_tools/blink_test.c index 3870c11e1..7dd2c9e96 100644 --- a/applications/debug_tools/blink_test.c +++ b/applications/debug_tools/blink_test.c @@ -88,10 +88,10 @@ int32_t blink_test_app(void* p) { furi_timer_start(timer, furi_kernel_get_tick_frequency()); // Register view port in GUI - Gui* gui = furi_record_open("gui"); + Gui* gui = furi_record_open(RECORD_GUI); gui_add_view_port(gui, view_port, GuiLayerFullscreen); - NotificationApp* notifications = furi_record_open("notification"); + NotificationApp* notifications = furi_record_open(RECORD_NOTIFICATION); uint8_t state = 0; BlinkEvent event; @@ -119,8 +119,8 @@ int32_t blink_test_app(void* p) { view_port_free(view_port); furi_message_queue_free(event_queue); - furi_record_close("notification"); - furi_record_close("gui"); + furi_record_close(RECORD_NOTIFICATION); + furi_record_close(RECORD_GUI); return 0; } diff --git a/applications/debug_tools/display_test/display_test.c b/applications/debug_tools/display_test/display_test.c index 95aea1deb..947e4dc8b 100644 --- a/applications/debug_tools/display_test/display_test.c +++ b/applications/debug_tools/display_test/display_test.c @@ -127,7 +127,7 @@ DisplayTest* display_test_alloc() { View* view = NULL; - instance->gui = furi_record_open("gui"); + instance->gui = furi_record_open(RECORD_GUI); instance->view_dispatcher = view_dispatcher_alloc(); view_dispatcher_enable_queue(instance->view_dispatcher); view_dispatcher_attach_to_gui( @@ -206,7 +206,7 @@ void display_test_free(DisplayTest* instance) { view_display_test_free(instance->view_display_test); view_dispatcher_free(instance->view_dispatcher); - furi_record_close("gui"); + furi_record_close(RECORD_GUI); free(instance); } diff --git a/applications/debug_tools/file_browser_test/file_browser_app.c b/applications/debug_tools/file_browser_test/file_browser_app.c index c9b63ecb0..c7f461d40 100644 --- a/applications/debug_tools/file_browser_test/file_browser_app.c +++ b/applications/debug_tools/file_browser_test/file_browser_app.c @@ -29,8 +29,8 @@ FileBrowserApp* file_browser_app_alloc(char* arg) { UNUSED(arg); FileBrowserApp* app = malloc(sizeof(FileBrowserApp)); - app->gui = furi_record_open("gui"); - app->dialogs = furi_record_open("dialogs"); + app->gui = furi_record_open(RECORD_GUI); + app->dialogs = furi_record_open(RECORD_DIALOGS); app->view_dispatcher = view_dispatcher_alloc(); view_dispatcher_enable_queue(app->view_dispatcher); @@ -80,9 +80,9 @@ void file_browser_app_free(FileBrowserApp* app) { scene_manager_free(app->scene_manager); // Close records - furi_record_close("gui"); - furi_record_close("notification"); - furi_record_close("dialogs"); + furi_record_close(RECORD_GUI); + furi_record_close(RECORD_NOTIFICATION); + furi_record_close(RECORD_DIALOGS); string_clear(app->file_path); diff --git a/applications/debug_tools/file_browser_test/scenes/file_browser_scene_start.c b/applications/debug_tools/file_browser_test/scenes/file_browser_scene_start.c index bb71e83df..b3381f0ad 100644 --- a/applications/debug_tools/file_browser_test/scenes/file_browser_scene_start.c +++ b/applications/debug_tools/file_browser_test/scenes/file_browser_scene_start.c @@ -1,6 +1,8 @@ #include "../file_browser_app_i.h" -#include "furi_hal.h" -#include "gui/modules/widget_elements/widget_element_i.h" + +#include +#include +#include static void file_browser_scene_start_ok_callback(GuiButtonType result, InputType type, void* context) { @@ -17,7 +19,7 @@ bool file_browser_scene_start_on_event(void* context, SceneManagerEvent event) { bool consumed = false; if(event.type == SceneManagerEventTypeCustom) { - string_set_str(app->file_path, "/any/badusb/demo_windows.txt"); + string_set_str(app->file_path, ANY_PATH("badusb/demo_windows.txt")); scene_manager_next_scene(app->scene_manager, FileBrowserSceneBrowser); consumed = true; } else if(event.type == SceneManagerEventTypeTick) { diff --git a/applications/debug_tools/keypad_test.c b/applications/debug_tools/keypad_test.c index 9a6e7f25a..2470baf8d 100644 --- a/applications/debug_tools/keypad_test.c +++ b/applications/debug_tools/keypad_test.c @@ -26,11 +26,11 @@ static void keypad_test_render_callback(Canvas* canvas, void* ctx) { canvas_clear(canvas); char strings[5][20]; - sprintf(strings[0], "Ok: %d", state->ok); - sprintf(strings[1], "L: %d", state->left); - sprintf(strings[2], "R: %d", state->right); - sprintf(strings[3], "U: %d", state->up); - sprintf(strings[4], "D: %d", state->down); + snprintf(strings[0], 20, "Ok: %d", state->ok); + snprintf(strings[1], 20, "L: %d", state->left); + snprintf(strings[2], 20, "R: %d", state->right); + snprintf(strings[3], 20, "U: %d", state->up); + snprintf(strings[4], 20, "D: %d", state->down); canvas_set_font(canvas, FontPrimary); canvas_draw_str(canvas, 0, 10, "Keypad test"); @@ -78,7 +78,7 @@ int32_t keypad_test_app(void* p) { view_port_input_callback_set(view_port, keypad_test_input_callback, event_queue); // Open GUI and register view_port - Gui* gui = furi_record_open("gui"); + Gui* gui = furi_record_open(RECORD_GUI); gui_add_view_port(gui, view_port, GuiLayerFullscreen); InputEvent event; @@ -149,7 +149,7 @@ int32_t keypad_test_app(void* p) { furi_message_queue_free(event_queue); delete_mutex(&state_mutex); - furi_record_close("gui"); + furi_record_close(RECORD_GUI); return 0; } diff --git a/applications/debug_tools/text_box_test.c b/applications/debug_tools/text_box_test.c index 837c34b6a..d7194ffee 100644 --- a/applications/debug_tools/text_box_test.c +++ b/applications/debug_tools/text_box_test.c @@ -88,7 +88,7 @@ int32_t text_box_test_app(void* p) { view_port_input_callback_set(view_port, text_box_test_input_callback, event_queue); // Open GUI and register view_port - Gui* gui = furi_record_open("gui"); + Gui* gui = furi_record_open(RECORD_GUI); gui_add_view_port(gui, view_port, GuiLayerFullscreen); uint32_t test_renders_num = COUNT_OF(text_box_test_render); @@ -121,7 +121,7 @@ int32_t text_box_test_app(void* p) { furi_message_queue_free(event_queue); delete_mutex(&state_mutex); - furi_record_close("gui"); + furi_record_close(RECORD_GUI); return 0; } diff --git a/applications/debug_tools/uart_echo.c b/applications/debug_tools/uart_echo.c index 8f795c56d..7a0f5c393 100644 --- a/applications/debug_tools/uart_echo.c +++ b/applications/debug_tools/uart_echo.c @@ -189,8 +189,8 @@ static UartEchoApp* uart_echo_app_alloc() { app->rx_stream = xStreamBufferCreate(2048, 1); // Gui - app->gui = furi_record_open("gui"); - app->notification = furi_record_open("notification"); + app->gui = furi_record_open(RECORD_GUI); + app->notification = furi_record_open(RECORD_NOTIFICATION); // View dispatcher app->view_dispatcher = view_dispatcher_alloc(); @@ -256,8 +256,8 @@ static void uart_echo_app_free(UartEchoApp* app) { view_dispatcher_free(app->view_dispatcher); // Close gui record - furi_record_close("gui"); - furi_record_close("notification"); + furi_record_close(RECORD_GUI); + furi_record_close(RECORD_NOTIFICATION); app->gui = NULL; vStreamBufferDelete(app->rx_stream); diff --git a/applications/debug_tools/usb_mouse.c b/applications/debug_tools/usb_mouse.c index 3174ccae3..6e716e69a 100644 --- a/applications/debug_tools/usb_mouse.c +++ b/applications/debug_tools/usb_mouse.c @@ -51,7 +51,7 @@ int32_t usb_mouse_app(void* p) { view_port_input_callback_set(view_port, usb_mouse_input_callback, event_queue); // Open GUI and register view_port - Gui* gui = furi_record_open("gui"); + Gui* gui = furi_record_open(RECORD_GUI); gui_add_view_port(gui, view_port, GuiLayerFullscreen); UsbMouseEvent event; diff --git a/applications/debug_tools/usb_test.c b/applications/debug_tools/usb_test.c index a4f42f470..ed86c37a8 100644 --- a/applications/debug_tools/usb_test.c +++ b/applications/debug_tools/usb_test.c @@ -59,7 +59,7 @@ UsbTestApp* usb_test_app_alloc() { UsbTestApp* app = malloc(sizeof(UsbTestApp)); // Gui - app->gui = furi_record_open("gui"); + app->gui = furi_record_open(RECORD_GUI); // View dispatcher app->view_dispatcher = view_dispatcher_alloc(); @@ -106,7 +106,7 @@ void usb_test_app_free(UsbTestApp* app) { view_dispatcher_free(app->view_dispatcher); // Close gui record - furi_record_close("gui"); + furi_record_close(RECORD_GUI); app->gui = NULL; // Free rest diff --git a/applications/debug_tools/vibro_test.c b/applications/debug_tools/vibro_test.c index dbd35d601..e6c45ef6d 100644 --- a/applications/debug_tools/vibro_test.c +++ b/applications/debug_tools/vibro_test.c @@ -32,10 +32,10 @@ int32_t vibro_test_app(void* p) { view_port_input_callback_set(view_port, vibro_test_input_callback, event_queue); // Register view port in GUI - Gui* gui = furi_record_open("gui"); + Gui* gui = furi_record_open(RECORD_GUI); gui_add_view_port(gui, view_port, GuiLayerFullscreen); - NotificationApp* notification = furi_record_open("notification"); + NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION); InputEvent event; @@ -60,8 +60,8 @@ int32_t vibro_test_app(void* p) { view_port_free(view_port); furi_message_queue_free(event_queue); - furi_record_close("notification"); - furi_record_close("gui"); + furi_record_close(RECORD_NOTIFICATION); + furi_record_close(RECORD_GUI); return 0; } diff --git a/applications/desktop/animations/animation_manager.c b/applications/desktop/animations/animation_manager.c index 9b702811c..d755be9c0 100644 --- a/applications/desktop/animations/animation_manager.c +++ b/applications/desktop/animations/animation_manager.c @@ -137,9 +137,9 @@ void animation_manager_check_blocking_process(AnimationManager* animation_manage bool blocked = animation_manager_check_blocking(animation_manager); if(!blocked) { - Dolphin* dolphin = furi_record_open("dolphin"); + Dolphin* dolphin = furi_record_open(RECORD_DOLPHIN); DolphinStats stats = dolphin_stats(dolphin); - furi_record_close("dolphin"); + furi_record_close(RECORD_DOLPHIN); const StorageAnimationManifestInfo* manifest_info = animation_storage_get_meta(animation_manager->current_animation); @@ -170,9 +170,9 @@ bool animation_manager_interact_process(AnimationManager* animation_manager) { animation_manager->levelup_pending = false; animation_manager->levelup_active = true; animation_manager_switch_to_one_shot_view(animation_manager); - Dolphin* dolphin = furi_record_open("dolphin"); + Dolphin* dolphin = furi_record_open(RECORD_DOLPHIN); dolphin_upgrade_level(dolphin); - furi_record_close("dolphin"); + furi_record_close(RECORD_DOLPHIN); } else if(animation_manager->levelup_active) { animation_manager->levelup_active = false; animation_manager_start_new_idle(animation_manager); @@ -205,7 +205,7 @@ static bool animation_manager_check_blocking(AnimationManager* animation_manager furi_assert(animation_manager); StorageAnimation* blocking_animation = NULL; - Storage* storage = furi_record_open("storage"); + Storage* storage = furi_record_open(RECORD_STORAGE); FS_Error sd_status = storage_sd_status(storage); if(sd_status == FSE_INTERNAL) { @@ -220,7 +220,7 @@ static bool animation_manager_check_blocking(AnimationManager* animation_manager furi_assert(blocking_animation); animation_manager->sd_shown_sd_ok = true; } else if(!animation_manager->sd_shown_no_db) { - bool db_exists = storage_common_stat(storage, "/ext/Manifest", NULL) == FSE_OK; + bool db_exists = storage_common_stat(storage, EXT_PATH("Manifest"), NULL) == FSE_OK; if(!db_exists) { blocking_animation = animation_storage_find_animation(NO_DB_ANIMATION_NAME); furi_assert(blocking_animation); @@ -234,9 +234,9 @@ static bool animation_manager_check_blocking(AnimationManager* animation_manager } } - Dolphin* dolphin = furi_record_open("dolphin"); + Dolphin* dolphin = furi_record_open(RECORD_DOLPHIN); DolphinStats stats = dolphin_stats(dolphin); - furi_record_close("dolphin"); + furi_record_close(RECORD_DOLPHIN); if(!blocking_animation && stats.level_up_is_pending) { blocking_animation = animation_storage_find_animation(NEW_MAIL_ANIMATION_NAME); furi_assert(blocking_animation); @@ -252,7 +252,7 @@ static bool animation_manager_check_blocking(AnimationManager* animation_manager animation_manager->state = AnimationManagerStateBlocked; } - furi_record_close("storage"); + furi_record_close(RECORD_STORAGE); return !!blocking_animation; } @@ -287,15 +287,15 @@ AnimationManager* animation_manager_alloc(void) { bubble_animation_view_set_interact_callback( animation_manager->animation_view, animation_manager_interact_callback, animation_manager); - Storage* storage = furi_record_open("storage"); + Storage* storage = furi_record_open(RECORD_STORAGE); animation_manager->pubsub_subscription_storage = furi_pubsub_subscribe( storage_get_pubsub(storage), animation_manager_check_blocking_callback, animation_manager); - furi_record_close("storage"); + furi_record_close(RECORD_STORAGE); - Dolphin* dolphin = furi_record_open("dolphin"); + Dolphin* dolphin = furi_record_open(RECORD_DOLPHIN); animation_manager->pubsub_subscription_dolphin = furi_pubsub_subscribe( dolphin_get_pubsub(dolphin), animation_manager_check_blocking_callback, animation_manager); - furi_record_close("dolphin"); + furi_record_close(RECORD_DOLPHIN); animation_manager->sd_shown_sd_ok = true; if(!animation_manager_check_blocking(animation_manager)) { @@ -308,15 +308,15 @@ AnimationManager* animation_manager_alloc(void) { void animation_manager_free(AnimationManager* animation_manager) { furi_assert(animation_manager); - Dolphin* dolphin = furi_record_open("dolphin"); + Dolphin* dolphin = furi_record_open(RECORD_DOLPHIN); furi_pubsub_unsubscribe( dolphin_get_pubsub(dolphin), animation_manager->pubsub_subscription_dolphin); - furi_record_close("dolphin"); + furi_record_close(RECORD_DOLPHIN); - Storage* storage = furi_record_open("storage"); + Storage* storage = furi_record_open(RECORD_STORAGE); furi_pubsub_unsubscribe( storage_get_pubsub(storage), animation_manager->pubsub_subscription_storage); - furi_record_close("storage"); + furi_record_close(RECORD_STORAGE); string_clear(animation_manager->freezed_animation_name); View* animation_view = bubble_animation_get_view(animation_manager->animation_view); @@ -340,16 +340,16 @@ static bool animation_manager_is_valid_idle_animation( bool result = true; if(!strcmp(info->name, BAD_BATTERY_ANIMATION_NAME)) { - Power* power = furi_record_open("power"); + Power* power = furi_record_open(RECORD_POWER); bool battery_is_well = power_is_battery_healthy(power); - furi_record_close("power"); + furi_record_close(RECORD_POWER); result = !battery_is_well; } if(!strcmp(info->name, NO_SD_ANIMATION_NAME)) { - Storage* storage = furi_record_open("storage"); + Storage* storage = furi_record_open(RECORD_STORAGE); FS_Error sd_status = storage_sd_status(storage); - furi_record_close("storage"); + furi_record_close(RECORD_STORAGE); result = (sd_status == FSE_NOT_READY); } @@ -370,9 +370,9 @@ static StorageAnimation* StorageAnimationList_init(animation_list); animation_storage_fill_animation_list(&animation_list); - Dolphin* dolphin = furi_record_open("dolphin"); + Dolphin* dolphin = furi_record_open(RECORD_DOLPHIN); DolphinStats stats = dolphin_stats(dolphin); - furi_record_close("dolphin"); + furi_record_close(RECORD_DOLPHIN); uint32_t whole_weight = 0; StorageAnimationList_it_t it; @@ -492,9 +492,9 @@ void animation_manager_load_and_continue_animation(AnimationManager* animation_m StorageAnimation* restore_animation = animation_storage_find_animation( string_get_cstr(animation_manager->freezed_animation_name)); if(restore_animation) { - Dolphin* dolphin = furi_record_open("dolphin"); + Dolphin* dolphin = furi_record_open(RECORD_DOLPHIN); DolphinStats stats = dolphin_stats(dolphin); - furi_record_close("dolphin"); + furi_record_close(RECORD_DOLPHIN); const StorageAnimationManifestInfo* manifest_info = animation_storage_get_meta(restore_animation); bool valid = animation_manager_is_valid_idle_animation(manifest_info, &stats); @@ -543,9 +543,9 @@ void animation_manager_load_and_continue_animation(AnimationManager* animation_m static void animation_manager_switch_to_one_shot_view(AnimationManager* animation_manager) { furi_assert(animation_manager); furi_assert(!animation_manager->one_shot_view); - Dolphin* dolphin = furi_record_open("dolphin"); + Dolphin* dolphin = furi_record_open(RECORD_DOLPHIN); DolphinStats stats = dolphin_stats(dolphin); - furi_record_close("dolphin"); + furi_record_close(RECORD_DOLPHIN); animation_manager->one_shot_view = one_shot_view_alloc(); one_shot_view_set_interact_callback( diff --git a/applications/desktop/animations/animation_storage.c b/applications/desktop/animations/animation_storage.c index 36b20bc87..e0c6bf411 100644 --- a/applications/desktop/animations/animation_storage.c +++ b/applications/desktop/animations/animation_storage.c @@ -14,7 +14,7 @@ #include #define ANIMATION_META_FILE "meta.txt" -#define ANIMATION_DIR "/ext/dolphin" +#define ANIMATION_DIR EXT_PATH("dolphin") #define ANIMATION_MANIFEST_FILE ANIMATION_DIR "/manifest.txt" #define TAG "AnimationStorage" @@ -29,7 +29,7 @@ static bool animation_storage_load_single_manifest_info( furi_assert(manifest_info); bool result = false; - Storage* storage = furi_record_open("storage"); + Storage* storage = furi_record_open(RECORD_STORAGE); FlipperFormat* file = flipper_format_file_alloc(storage); flipper_format_set_strict_mode(file, true); string_t read_string; @@ -75,7 +75,7 @@ static bool animation_storage_load_single_manifest_info( string_clear(read_string); flipper_format_free(file); - furi_record_close("storage"); + furi_record_close(RECORD_STORAGE); return result; } @@ -84,7 +84,7 @@ void animation_storage_fill_animation_list(StorageAnimationList_t* animation_lis furi_assert(sizeof(StorageAnimationList_t) == sizeof(void*)); furi_assert(!StorageAnimationList_size(*animation_list)); - Storage* storage = furi_record_open("storage"); + Storage* storage = furi_record_open(RECORD_STORAGE); FlipperFormat* file = flipper_format_file_alloc(storage); /* Forbid skipping fields */ flipper_format_set_strict_mode(file, true); @@ -134,7 +134,7 @@ void animation_storage_fill_animation_list(StorageAnimationList_t* animation_lis StorageAnimationList_push_back(*animation_list, (StorageAnimation*)&dolphin_internal[i]); } - furi_record_close("storage"); + furi_record_close(RECORD_STORAGE); } StorageAnimation* animation_storage_find_animation(const char* name) { @@ -434,7 +434,7 @@ static BubbleAnimation* animation_storage_load_animation(const char* name) { uint32_t height = 0; uint32_t width = 0; uint32_t* u32array = NULL; - Storage* storage = furi_record_open("storage"); + Storage* storage = furi_record_open(RECORD_STORAGE); FlipperFormat* ff = flipper_format_file_alloc(storage); /* Forbid skipping fields */ flipper_format_set_strict_mode(ff, true); diff --git a/applications/desktop/animations/views/bubble_animation_view.c b/applications/desktop/animations/views/bubble_animation_view.c index 54a686fb1..607862d11 100644 --- a/applications/desktop/animations/views/bubble_animation_view.c +++ b/applications/desktop/animations/views/bubble_animation_view.c @@ -143,7 +143,7 @@ static void bubble_animation_activate(BubbleAnimationView* view, bool force) { furi_assert(view); bool activate = true; BubbleAnimationViewModel* model = view_get_model(view->view); - if(!model->current) { + if(model->current == NULL) { activate = false; } else if(model->freeze_frame) { activate = false; @@ -151,14 +151,16 @@ static void bubble_animation_activate(BubbleAnimationView* view, bool force) { activate = false; } - if(!force) { - if((model->active_ended_at + model->current->active_cooldown * 1000) > - xTaskGetTickCount()) { - activate = false; - } else if(model->active_shift) { - activate = false; - } else if(model->current_frame >= model->current->passive_frames) { - activate = false; + if(model->current != NULL) { + if(!force) { + if((model->active_ended_at + model->current->active_cooldown * 1000) > + xTaskGetTickCount()) { + activate = false; + } else if(model->active_shift) { + activate = false; + } else if(model->current_frame >= model->current->passive_frames) { + activate = false; + } } } view_commit_model(view->view, false); @@ -288,7 +290,10 @@ static void bubble_animation_enter(void* context) { bubble_animation_activate(view, false); BubbleAnimationViewModel* model = view_get_model(view->view); - uint8_t frame_rate = model->current->icon_animation.frame_rate; + uint8_t frame_rate = 0; + if(model->current != NULL) { + frame_rate = model->current->icon_animation.frame_rate; + } view_commit_model(view->view, false); if(frame_rate) { diff --git a/applications/desktop/desktop.c b/applications/desktop/desktop.c index 83b4f0f3f..578066a6a 100644 --- a/applications/desktop/desktop.c +++ b/applications/desktop/desktop.c @@ -15,6 +15,7 @@ #include "desktop/views/desktop_view_pin_timeout.h" #include "desktop_i.h" #include "helpers/pin_lock.h" +#include "helpers/slideshow_filename.h" static void desktop_auto_lock_arm(Desktop*); static void desktop_auto_lock_inhibit(Desktop*); @@ -127,9 +128,9 @@ void desktop_lock(Desktop* desktop) { void desktop_unlock(Desktop* desktop) { view_port_enabled_set(desktop->lock_viewport, false); - Gui* gui = furi_record_open("gui"); + Gui* gui = furi_record_open(RECORD_GUI); gui_set_lockdown(gui, false); - furi_record_close("gui"); + furi_record_close(RECORD_GUI); desktop_view_locked_unlock(desktop->locked_view); scene_manager_search_and_switch_to_previous_scene(desktop->scene_manager, DesktopSceneMain); desktop_auto_lock_arm(desktop); @@ -139,7 +140,7 @@ Desktop* desktop_alloc() { Desktop* desktop = malloc(sizeof(Desktop)); desktop->animation_manager = animation_manager_alloc(); - desktop->gui = furi_record_open("gui"); + desktop->gui = furi_record_open(RECORD_GUI); desktop->scene_thread = furi_thread_alloc(); desktop->view_dispatcher = view_dispatcher_alloc(); desktop->scene_manager = scene_manager_alloc(&desktop_scene_handlers, desktop); @@ -218,17 +219,17 @@ Desktop* desktop_alloc() { gui_add_view_port(desktop->gui, desktop->lock_viewport, GuiLayerStatusBarLeft); // Special case: autostart application is already running - desktop->loader = furi_record_open("loader"); + desktop->loader = furi_record_open(RECORD_LOADER); if(loader_is_locked(desktop->loader) && animation_manager_is_animation_loaded(desktop->animation_manager)) { animation_manager_unload_and_stall_animation(desktop->animation_manager); } - desktop->notification = furi_record_open("notification"); + desktop->notification = furi_record_open(RECORD_NOTIFICATION); desktop->app_start_stop_subscription = furi_pubsub_subscribe( loader_get_pubsub(desktop->loader), desktop_loader_callback, desktop); - desktop->input_events_pubsub = furi_record_open("input_events"); + desktop->input_events_pubsub = furi_record_open(RECORD_INPUT_EVENTS); desktop->input_events_subscription = NULL; desktop->auto_lock_timer = @@ -250,9 +251,9 @@ void desktop_free(Desktop* desktop) { desktop->loader = NULL; desktop->input_events_pubsub = NULL; - furi_record_close("loader"); - furi_record_close("notification"); - furi_record_close("input_events"); + furi_record_close(RECORD_LOADER); + furi_record_close(RECORD_NOTIFICATION); + furi_record_close(RECORD_INPUT_EVENTS); view_dispatcher_remove_view(desktop->view_dispatcher, DesktopViewIdMain); view_dispatcher_remove_view(desktop->view_dispatcher, DesktopViewIdLockMenu); @@ -276,7 +277,7 @@ void desktop_free(Desktop* desktop) { popup_free(desktop->hw_mismatch_popup); desktop_view_pin_timeout_free(desktop->pin_timeout_view); - furi_record_close("gui"); + furi_record_close(RECORD_GUI); desktop->gui = NULL; furi_thread_free(desktop->scene_thread); @@ -289,9 +290,9 @@ void desktop_free(Desktop* desktop) { } static bool desktop_check_file_flag(const char* flag_path) { - Storage* storage = furi_record_open("storage"); + Storage* storage = furi_record_open(RECORD_STORAGE); bool exists = storage_common_stat(storage, flag_path, NULL) == FSE_OK; - furi_record_close("storage"); + furi_record_close(RECORD_STORAGE); return exists; } @@ -318,7 +319,7 @@ int32_t desktop_srv(void* p) { desktop_lock(desktop); } - if(desktop_check_file_flag("/int/slideshow")) { + if(desktop_check_file_flag(SLIDESHOW_FS_PATH)) { scene_manager_next_scene(desktop->scene_manager, DesktopSceneSlideshow); } diff --git a/applications/desktop/desktop_settings/desktop_settings.h b/applications/desktop/desktop_settings/desktop_settings.h index f20399246..800847d56 100644 --- a/applications/desktop/desktop_settings/desktop_settings.h +++ b/applications/desktop/desktop_settings/desktop_settings.h @@ -1,12 +1,16 @@ #pragma once +#include "desktop_settings_filename.h" + #include #include #include #include +#include #define DESKTOP_SETTINGS_VER (4) -#define DESKTOP_SETTINGS_PATH "/int/desktop.settings" + +#define DESKTOP_SETTINGS_PATH INT_PATH(DESKTOP_SETTINGS_FILE_NAME) #define DESKTOP_SETTINGS_MAGIC (0x17) #define PIN_MAX_LENGTH 12 diff --git a/applications/desktop/desktop_settings/desktop_settings_app.c b/applications/desktop/desktop_settings/desktop_settings_app.c index c52f1947c..89513a8b8 100644 --- a/applications/desktop/desktop_settings/desktop_settings_app.c +++ b/applications/desktop/desktop_settings/desktop_settings_app.c @@ -21,7 +21,7 @@ static bool desktop_settings_back_event_callback(void* context) { DesktopSettingsApp* desktop_settings_app_alloc() { DesktopSettingsApp* app = malloc(sizeof(DesktopSettingsApp)); - app->gui = furi_record_open("gui"); + app->gui = furi_record_open(RECORD_GUI); app->view_dispatcher = view_dispatcher_alloc(); app->scene_manager = scene_manager_alloc(&desktop_settings_scene_handlers, app); view_dispatcher_enable_queue(app->view_dispatcher); @@ -83,14 +83,14 @@ void desktop_settings_app_free(DesktopSettingsApp* app) { view_dispatcher_free(app->view_dispatcher); scene_manager_free(app->scene_manager); // Records - furi_record_close("gui"); + furi_record_close(RECORD_GUI); free(app); } extern int32_t desktop_settings_app(void* p) { DesktopSettingsApp* app = desktop_settings_app_alloc(); LOAD_DESKTOP_SETTINGS(&app->settings); - if(!strcmp(p, DESKTOP_SETTINGS_RUN_PIN_SETUP_ARG)) { + if(p && (strcmp(p, DESKTOP_SETTINGS_RUN_PIN_SETUP_ARG) == 0)) { scene_manager_next_scene(app->scene_manager, DesktopSettingsAppScenePinSetupHowto); } else { scene_manager_next_scene(app->scene_manager, DesktopSettingsAppSceneStart); diff --git a/applications/desktop/desktop_settings/desktop_settings_filename.h b/applications/desktop/desktop_settings/desktop_settings_filename.h new file mode 100644 index 000000000..b9140f24c --- /dev/null +++ b/applications/desktop/desktop_settings/desktop_settings_filename.h @@ -0,0 +1,3 @@ +#pragma once + +#define DESKTOP_SETTINGS_FILE_NAME ".desktop.settings" diff --git a/applications/desktop/desktop_settings/scenes/desktop_settings_scene_pin_setup_done.c b/applications/desktop/desktop_settings/scenes/desktop_settings_scene_pin_setup_done.c index 7be0e51c3..424084288 100644 --- a/applications/desktop/desktop_settings/scenes/desktop_settings_scene_pin_setup_done.c +++ b/applications/desktop/desktop_settings/scenes/desktop_settings_scene_pin_setup_done.c @@ -25,9 +25,9 @@ void desktop_settings_scene_pin_setup_done_on_enter(void* context) { app->settings.pin_code = app->pincode_buffer; SAVE_DESKTOP_SETTINGS(&app->settings); - NotificationApp* notification = furi_record_open("notification"); + NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION); notification_message(notification, &sequence_single_vibro); - furi_record_close("notification"); + furi_record_close(RECORD_NOTIFICATION); desktop_view_pin_input_set_context(app->pin_input_view, app); desktop_view_pin_input_set_back_callback(app->pin_input_view, NULL); diff --git a/applications/desktop/helpers/pin_lock.c b/applications/desktop/helpers/pin_lock.c index d63398d96..0495b675d 100644 --- a/applications/desktop/helpers/pin_lock.c +++ b/applications/desktop/helpers/pin_lock.c @@ -44,9 +44,9 @@ static const uint8_t desktop_helpers_fails_timeout[] = { }; void desktop_pin_lock_error_notify() { - NotificationApp* notification = furi_record_open("notification"); + NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION); notification_message(notification, &sequence_pin_fail); - furi_record_close("notification"); + furi_record_close(RECORD_NOTIFICATION); } uint32_t desktop_pin_lock_get_fail_timeout() { @@ -67,9 +67,9 @@ void desktop_pin_lock(DesktopSettings* settings) { furi_hal_rtc_set_pin_fails(0); furi_hal_rtc_set_flag(FuriHalRtcFlagLock); - Cli* cli = furi_record_open("cli"); + Cli* cli = furi_record_open(RECORD_CLI); cli_session_close(cli); - furi_record_close("cli"); + furi_record_close(RECORD_CLI); settings->is_locked = 1; SAVE_DESKTOP_SETTINGS(settings); } @@ -78,9 +78,9 @@ void desktop_pin_unlock(DesktopSettings* settings) { furi_assert(settings); furi_hal_rtc_reset_flag(FuriHalRtcFlagLock); - Cli* cli = furi_record_open("cli"); + Cli* cli = furi_record_open(RECORD_CLI); cli_session_open(cli, &cli_vcp); - furi_record_close("cli"); + furi_record_close(RECORD_CLI); settings->is_locked = 0; SAVE_DESKTOP_SETTINGS(settings); } @@ -103,9 +103,9 @@ void desktop_pin_lock_init(DesktopSettings* settings) { } if(desktop_pin_lock_is_locked()) { - Cli* cli = furi_record_open("cli"); + Cli* cli = furi_record_open(RECORD_CLI); cli_session_close(cli); - furi_record_close("cli"); + furi_record_close(RECORD_CLI); } } diff --git a/applications/desktop/helpers/slideshow.c b/applications/desktop/helpers/slideshow.c index 4ec55a5a5..63bd42b55 100644 --- a/applications/desktop/helpers/slideshow.c +++ b/applications/desktop/helpers/slideshow.c @@ -52,7 +52,7 @@ void slideshow_free(Slideshow* slideshow) { } bool slideshow_load(Slideshow* slideshow, const char* fspath) { - Storage* storage = furi_record_open("storage"); + Storage* storage = furi_record_open(RECORD_STORAGE); File* slideshow_file = storage_file_alloc(storage); slideshow->loaded = false; do { @@ -86,7 +86,7 @@ bool slideshow_load(Slideshow* slideshow, const char* fspath) { } } while(false); storage_file_free(slideshow_file); - furi_record_close("storage"); + furi_record_close(RECORD_STORAGE); return slideshow->loaded; } diff --git a/applications/desktop/helpers/slideshow_filename.h b/applications/desktop/helpers/slideshow_filename.h new file mode 100644 index 000000000..2250d91dd --- /dev/null +++ b/applications/desktop/helpers/slideshow_filename.h @@ -0,0 +1,3 @@ +#pragma once + +#define SLIDESHOW_FILE_NAME ".slideshow" diff --git a/applications/desktop/scenes/desktop_scene_debug.c b/applications/desktop/scenes/desktop_scene_debug.c index 4945f7ace..e79c56e11 100644 --- a/applications/desktop/scenes/desktop_scene_debug.c +++ b/applications/desktop/scenes/desktop_scene_debug.c @@ -22,7 +22,7 @@ void desktop_scene_debug_on_enter(void* context) { bool desktop_scene_debug_on_event(void* context, SceneManagerEvent event) { Desktop* desktop = (Desktop*)context; - Dolphin* dolphin = furi_record_open("dolphin"); + Dolphin* dolphin = furi_record_open(RECORD_DOLPHIN); bool consumed = false; if(event.type == SceneManagerEventTypeCustom) { @@ -55,7 +55,7 @@ bool desktop_scene_debug_on_event(void* context, SceneManagerEvent event) { } } - furi_record_close("dolphin"); + furi_record_close(RECORD_DOLPHIN); return consumed; } diff --git a/applications/desktop/scenes/desktop_scene_locked.c b/applications/desktop/scenes/desktop_scene_locked.c index f4c08d325..c377d40ab 100644 --- a/applications/desktop/scenes/desktop_scene_locked.c +++ b/applications/desktop/scenes/desktop_scene_locked.c @@ -47,9 +47,9 @@ void desktop_scene_locked_on_enter(void* context) { if(state == SCENE_LOCKED_FIRST_ENTER) { bool pin_locked = desktop_pin_lock_is_locked(); view_port_enabled_set(desktop->lock_viewport, true); - Gui* gui = furi_record_open("gui"); + Gui* gui = furi_record_open(RECORD_GUI); gui_set_lockdown(gui, true); - furi_record_close("gui"); + furi_record_close(RECORD_GUI); if(pin_locked) { LOAD_DESKTOP_SETTINGS(&desktop->settings); diff --git a/applications/desktop/scenes/desktop_scene_pin_input.c b/applications/desktop/scenes/desktop_scene_pin_input.c index 7d980a85e..9392309e6 100644 --- a/applications/desktop/scenes/desktop_scene_pin_input.c +++ b/applications/desktop/scenes/desktop_scene_pin_input.c @@ -24,13 +24,13 @@ typedef struct { } DesktopScenePinInputState; static void desktop_scene_locked_light_red(bool value) { - NotificationApp* app = furi_record_open("notification"); + NotificationApp* app = furi_record_open(RECORD_NOTIFICATION); if(value) { notification_message(app, &sequence_set_only_red_255); } else { notification_message(app, &sequence_reset_red); } - furi_record_close("notification"); + furi_record_close(RECORD_NOTIFICATION); } static void diff --git a/applications/desktop/scenes/desktop_scene_slideshow.c b/applications/desktop/scenes/desktop_scene_slideshow.c index 700801276..cab7bf62b 100644 --- a/applications/desktop/scenes/desktop_scene_slideshow.c +++ b/applications/desktop/scenes/desktop_scene_slideshow.c @@ -3,6 +3,7 @@ #include "../desktop_i.h" #include "../views/desktop_view_slideshow.h" #include "../views/desktop_events.h" +#include void desktop_scene_slideshow_callback(DesktopEvent event, void* context) { Desktop* desktop = (Desktop*)context; @@ -22,16 +23,23 @@ bool desktop_scene_slideshow_on_event(void* context, SceneManagerEvent event) { Desktop* desktop = (Desktop*)context; bool consumed = false; Storage* storage = NULL; + Power* power = NULL; if(event.type == SceneManagerEventTypeCustom) { switch(event.event) { case DesktopSlideshowCompleted: - storage = furi_record_open("storage"); - storage_common_remove(storage, "/int/slideshow"); - furi_record_close("storage"); + storage = furi_record_open(RECORD_STORAGE); + storage_common_remove(storage, SLIDESHOW_FS_PATH); + furi_record_close(RECORD_STORAGE); scene_manager_previous_scene(desktop->scene_manager); consumed = true; break; + case DesktopSlideshowPoweroff: + power = furi_record_open(RECORD_POWER); + power_off(power); + furi_record_close(RECORD_POWER); + consumed = true; + break; default: break; diff --git a/applications/desktop/views/desktop_events.h b/applications/desktop/views/desktop_events.h index 4ff5f7950..5d130be9b 100644 --- a/applications/desktop/views/desktop_events.h +++ b/applications/desktop/views/desktop_events.h @@ -35,6 +35,7 @@ typedef enum { DesktopAnimationEventInteractAnimation, DesktopSlideshowCompleted, + DesktopSlideshowPoweroff, // Global events DesktopGlobalBeforeAppStarted, diff --git a/applications/desktop/views/desktop_view_debug.c b/applications/desktop/views/desktop_view_debug.c index 3a6c87f13..e26411932 100644 --- a/applications/desktop/views/desktop_view_debug.c +++ b/applications/desktop/views/desktop_view_debug.c @@ -23,7 +23,7 @@ void desktop_debug_render(Canvas* canvas, void* model) { const Version* ver; char buffer[64]; - static const char* headers[] = {"FW Version info:", "Dolphin info:"}; + static const char* headers[] = {"FW Version Info:", "Dolphin Info:"}; canvas_set_color(canvas, ColorBlack); canvas_set_font(canvas, FontPrimary); @@ -78,27 +78,28 @@ void desktop_debug_render(Canvas* canvas, void* model) { canvas_draw_str(canvas, 5, 50 + STATUS_BAR_Y_SHIFT, buffer); } else { - char buffer[64]; - Dolphin* dolphin = furi_record_open("dolphin"); + Dolphin* dolphin = furi_record_open(RECORD_DOLPHIN); DolphinStats stats = dolphin_stats(dolphin); - furi_record_close("dolphin"); + furi_record_close(RECORD_DOLPHIN); uint32_t current_lvl = stats.level; uint32_t remaining = dolphin_state_xp_to_levelup(m->icounter); canvas_set_font(canvas, FontSecondary); - snprintf(buffer, 64, "Icounter: %ld Butthurt %ld", m->icounter, m->butthurt); + snprintf(buffer, sizeof(buffer), "Icounter: %ld Butthurt %ld", m->icounter, m->butthurt); canvas_draw_str(canvas, 5, 19 + STATUS_BAR_Y_SHIFT, buffer); snprintf( buffer, - 64, + sizeof(buffer), "Level: %ld To level up: %ld", current_lvl, (remaining == (uint32_t)(-1) ? remaining : 0)); canvas_draw_str(canvas, 5, 29 + STATUS_BAR_Y_SHIFT, buffer); - snprintf(buffer, 64, "%s", asctime(localtime((const time_t*)&m->timestamp))); + // even if timestamp is uint64_t, it's safe to cast it to uint32_t, because furi_hal_rtc_datetime_to_timestamp only returns uint32_t + snprintf(buffer, sizeof(buffer), "%ld", (uint32_t)m->timestamp); + canvas_draw_str(canvas, 5, 39 + STATUS_BAR_Y_SHIFT, buffer); canvas_draw_str(canvas, 0, 49 + STATUS_BAR_Y_SHIFT, "[< >] icounter value [ok] save"); } @@ -175,7 +176,7 @@ void desktop_debug_free(DesktopDebugView* debug_view) { } void desktop_debug_get_dolphin_data(DesktopDebugView* debug_view) { - Dolphin* dolphin = furi_record_open("dolphin"); + Dolphin* dolphin = furi_record_open(RECORD_DOLPHIN); DolphinStats stats = dolphin_stats(dolphin); with_view_model( debug_view->view, (DesktopDebugViewModel * model) { @@ -185,7 +186,7 @@ void desktop_debug_get_dolphin_data(DesktopDebugView* debug_view) { return true; }); - furi_record_close("dolphin"); + furi_record_close(RECORD_DOLPHIN); } void desktop_debug_reset_screen_idx(DesktopDebugView* debug_view) { diff --git a/applications/desktop/views/desktop_view_lock_menu.c b/applications/desktop/views/desktop_view_lock_menu.c index 1b2bd76b7..97d3c4898 100644 --- a/applications/desktop/views/desktop_view_lock_menu.c +++ b/applications/desktop/views/desktop_view_lock_menu.c @@ -67,7 +67,7 @@ void desktop_lock_menu_render(Canvas* canvas, void* model) { const char* str = Lockmenu_Items[i]; if(i == 1 && !m->pin_set) str = "Set PIN"; - if(m->hint_timeout && m->idx == 2 && m->idx == i) str = "Not implemented"; + if(m->hint_timeout && m->idx == 2 && m->idx == i) str = "Not Implemented"; if(str != NULL) canvas_draw_str_aligned( diff --git a/applications/desktop/views/desktop_view_slideshow.c b/applications/desktop/views/desktop_view_slideshow.c index cd22b39de..26ae95eae 100644 --- a/applications/desktop/views/desktop_view_slideshow.c +++ b/applications/desktop/views/desktop_view_slideshow.c @@ -2,14 +2,19 @@ #include #include -#include "../desktop_i.h" #include "desktop_view_slideshow.h" +#include "../desktop_i.h" #include "../helpers/slideshow.h" +#include "../helpers/slideshow_filename.h" + +#define DESKTOP_SLIDESHOW_POWEROFF_SHORT 5000 +#define DESKTOP_SLIDESHOW_POWEROFF_LONG (60 * 60 * 1000) struct DesktopSlideshowView { View* view; DesktopSlideshowViewCallback callback; void* context; + FuriTimer* timer; }; typedef struct { @@ -50,17 +55,35 @@ static bool desktop_view_slideshow_input(InputEvent* event, void* context) { instance->callback(DesktopSlideshowCompleted, instance->context); } view_commit_model(instance->view, true); + } else if(event->key == InputKeyOk) { + if(event->type == InputTypePress) { + furi_timer_start(instance->timer, DESKTOP_SLIDESHOW_POWEROFF_SHORT); + } else if(event->type == InputTypeRelease) { + furi_timer_stop(instance->timer); + furi_timer_start(instance->timer, DESKTOP_SLIDESHOW_POWEROFF_LONG); + } } return true; } +static void desktop_first_start_timer_callback(void* context) { + DesktopSlideshowView* instance = context; + instance->callback(DesktopSlideshowPoweroff, instance->context); +} + static void desktop_view_slideshow_enter(void* context) { DesktopSlideshowView* instance = context; + furi_assert(instance->timer == NULL); + instance->timer = + furi_timer_alloc(desktop_first_start_timer_callback, FuriTimerTypeOnce, instance); + + furi_timer_start(instance->timer, DESKTOP_SLIDESHOW_POWEROFF_LONG); + DesktopSlideshowViewModel* model = view_get_model(instance->view); model->slideshow = slideshow_alloc(); - if(!slideshow_load(model->slideshow, "/int/slideshow")) { + if(!slideshow_load(model->slideshow, SLIDESHOW_FS_PATH)) { instance->callback(DesktopSlideshowCompleted, instance->context); } view_commit_model(instance->view, false); @@ -69,6 +92,10 @@ static void desktop_view_slideshow_enter(void* context) { static void desktop_view_slideshow_exit(void* context) { DesktopSlideshowView* instance = context; + furi_timer_stop(instance->timer); + furi_timer_free(instance->timer); + instance->timer = NULL; + DesktopSlideshowViewModel* model = view_get_model(instance->view); slideshow_free(model->slideshow); view_commit_model(instance->view, false); diff --git a/applications/desktop/views/desktop_view_slideshow.h b/applications/desktop/views/desktop_view_slideshow.h index 5b45a6b70..624cbf007 100644 --- a/applications/desktop/views/desktop_view_slideshow.h +++ b/applications/desktop/views/desktop_view_slideshow.h @@ -3,6 +3,9 @@ #include #include "desktop_events.h" +#include "../helpers/slideshow_filename.h" + +#define SLIDESHOW_FS_PATH INT_PATH(SLIDESHOW_FILE_NAME) typedef struct DesktopSlideshowView DesktopSlideshowView; diff --git a/applications/dialogs/dialogs.c b/applications/dialogs/dialogs.c index da047d8a0..381da1635 100644 --- a/applications/dialogs/dialogs.c +++ b/applications/dialogs/dialogs.c @@ -29,7 +29,7 @@ static void dialogs_app_process_message(DialogsApp* app, DialogsAppMessage* mess int32_t dialogs_srv(void* p) { UNUSED(p); DialogsApp* app = dialogs_app_alloc(); - furi_record_create("dialogs", app); + furi_record_create(RECORD_DIALOGS, app); DialogsAppMessage message; while(1) { diff --git a/applications/dialogs/dialogs.h b/applications/dialogs/dialogs.h index 536060565..946ab6cf0 100644 --- a/applications/dialogs/dialogs.h +++ b/applications/dialogs/dialogs.h @@ -9,6 +9,8 @@ extern "C" { /****************** COMMON ******************/ +#define RECORD_DIALOGS "dialogs" + typedef struct DialogsApp DialogsApp; /****************** FILE BROWSER ******************/ diff --git a/applications/dialogs/dialogs_module_file_browser.c b/applications/dialogs/dialogs_module_file_browser.c index ecd0ca79b..f5355571a 100644 --- a/applications/dialogs/dialogs_module_file_browser.c +++ b/applications/dialogs/dialogs_module_file_browser.c @@ -23,7 +23,7 @@ static void dialogs_app_file_browser_callback(void* context) { bool dialogs_app_process_module_file_browser(const DialogsAppMessageDataFileBrowser* data) { bool ret = false; - Gui* gui = furi_record_open("gui"); + Gui* gui = furi_record_open(RECORD_GUI); DialogsAppFileBrowserContext* file_browser_context = malloc(sizeof(DialogsAppFileBrowserContext)); @@ -53,7 +53,7 @@ bool dialogs_app_process_module_file_browser(const DialogsAppMessageDataFileBrow file_browser_free(file_browser); API_LOCK_FREE(file_browser_context->lock); free(file_browser_context); - furi_record_close("gui"); + furi_record_close(RECORD_GUI); return ret; } diff --git a/applications/dialogs/dialogs_module_message.c b/applications/dialogs/dialogs_module_message.c index ec2efd2e3..8d1c3ba75 100644 --- a/applications/dialogs/dialogs_module_message.c +++ b/applications/dialogs/dialogs_module_message.c @@ -54,7 +54,7 @@ static void dialogs_app_message_callback(DialogExResult result, void* context) { DialogMessageButton dialogs_app_process_module_message(const DialogsAppMessageDataDialog* data) { DialogMessageButton ret = DialogMessageButtonBack; - Gui* gui = furi_record_open("gui"); + Gui* gui = furi_record_open(RECORD_GUI); const DialogMessage* message = data->message; DialogsAppMessageContext* message_context = malloc(sizeof(DialogsAppMessageContext)); message_context->lock = API_LOCK_INIT_LOCKED(); @@ -96,7 +96,7 @@ DialogMessageButton dialogs_app_process_module_message(const DialogsAppMessageDa dialog_ex_free(dialog_ex); API_LOCK_FREE(message_context->lock); free(message_context); - furi_record_close("gui"); + furi_record_close(RECORD_GUI); return ret; } diff --git a/applications/dolphin/dolphin.c b/applications/dolphin/dolphin.c index f495068b1..41eeef3b1 100644 --- a/applications/dolphin/dolphin.c +++ b/applications/dolphin/dolphin.c @@ -155,7 +155,7 @@ static void dolphin_update_clear_limits_timer_period(Dolphin* dolphin) { int32_t dolphin_srv(void* p) { UNUSED(p); Dolphin* dolphin = dolphin_alloc(); - furi_record_create("dolphin", dolphin); + furi_record_create(RECORD_DOLPHIN, dolphin); dolphin_state_load(dolphin->state); xTimerReset(dolphin->butthurt_timer, portMAX_DELAY); diff --git a/applications/dolphin/dolphin.h b/applications/dolphin/dolphin.h index 2abb166bf..41a6a6089 100644 --- a/applications/dolphin/dolphin.h +++ b/applications/dolphin/dolphin.h @@ -9,6 +9,8 @@ extern "C" { #endif +#define RECORD_DOLPHIN "dolphin" + typedef struct Dolphin Dolphin; typedef struct { diff --git a/applications/dolphin/helpers/dolphin_state.c b/applications/dolphin/helpers/dolphin_state.c index 8a569392a..76f38a5fd 100644 --- a/applications/dolphin/helpers/dolphin_state.c +++ b/applications/dolphin/helpers/dolphin_state.c @@ -1,5 +1,7 @@ #include "dolphin_state.h" #include "dolphin/helpers/dolphin_deed.h" +#include "dolphin_state_filename.h" + #include #include #include @@ -8,7 +10,8 @@ #include #define TAG "DolphinState" -#define DOLPHIN_STATE_PATH "/int/dolphin.state" + +#define DOLPHIN_STATE_PATH INT_PATH(DOLPHIN_STATE_FILE_NAME) #define DOLPHIN_STATE_HEADER_MAGIC 0xD0 #define DOLPHIN_STATE_HEADER_VERSION 0x01 #define LEVEL2_THRESHOLD 735 diff --git a/applications/dolphin/helpers/dolphin_state_filename.h b/applications/dolphin/helpers/dolphin_state_filename.h new file mode 100644 index 000000000..86822c0ac --- /dev/null +++ b/applications/dolphin/helpers/dolphin_state_filename.h @@ -0,0 +1,3 @@ +#pragma once + +#define DOLPHIN_STATE_FILE_NAME ".dolphin.state" diff --git a/applications/dolphin/passport/passport.c b/applications/dolphin/passport/passport.c index b9be2d228..d43f150c6 100644 --- a/applications/dolphin/passport/passport.c +++ b/applications/dolphin/passport/passport.c @@ -95,12 +95,12 @@ int32_t passport_app(void* p) { ViewPort* view_port = view_port_alloc(); - Dolphin* dolphin = furi_record_open("dolphin"); + Dolphin* dolphin = furi_record_open(RECORD_DOLPHIN); DolphinStats stats = dolphin_stats(dolphin); - furi_record_close("dolphin"); + furi_record_close(RECORD_DOLPHIN); view_port_draw_callback_set(view_port, render_callback, &stats); view_port_input_callback_set(view_port, input_callback, semaphore); - Gui* gui = furi_record_open("gui"); + Gui* gui = furi_record_open(RECORD_GUI); gui_add_view_port(gui, view_port, GuiLayerFullscreen); view_port_update(view_port); @@ -108,7 +108,7 @@ int32_t passport_app(void* p) { gui_remove_view_port(gui, view_port); view_port_free(view_port); - furi_record_close("gui"); + furi_record_close(RECORD_GUI); furi_semaphore_free(semaphore); return 0; diff --git a/applications/extapps.scons b/applications/extapps.scons index a53620b93..11b597576 100644 --- a/applications/extapps.scons +++ b/applications/extapps.scons @@ -38,6 +38,8 @@ appenv.AppendUnique( "-Wl,--no-export-dynamic", "-fvisibility=hidden", "-Wl,-e${APP_ENTRY}", + "-Xlinker", + "-Map=${TARGET}.map", ], ) diff --git a/applications/gpio/gpio_app.c b/applications/gpio/gpio_app.c index b5f5184a6..b8afdc8ea 100644 --- a/applications/gpio/gpio_app.c +++ b/applications/gpio/gpio_app.c @@ -24,7 +24,7 @@ static void gpio_app_tick_event_callback(void* context) { GpioApp* gpio_app_alloc() { GpioApp* app = malloc(sizeof(GpioApp)); - app->gui = furi_record_open("gui"); + app->gui = furi_record_open(RECORD_GUI); app->view_dispatcher = view_dispatcher_alloc(); app->scene_manager = scene_manager_alloc(&gpio_scene_handlers, app); @@ -40,7 +40,7 @@ GpioApp* gpio_app_alloc() { view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); - app->notifications = furi_record_open("notification"); + app->notifications = furi_record_open(RECORD_NOTIFICATION); app->var_item_list = variable_item_list_alloc(); view_dispatcher_add_view( @@ -88,8 +88,8 @@ void gpio_app_free(GpioApp* app) { scene_manager_free(app->scene_manager); // Close records - furi_record_close("gui"); - furi_record_close("notification"); + furi_record_close(RECORD_GUI); + furi_record_close(RECORD_NOTIFICATION); free(app); } diff --git a/applications/gpio/scenes/gpio_scene_usb_uart_close_rpc.c b/applications/gpio/scenes/gpio_scene_usb_uart_close_rpc.c index e09f2fd36..2cb53cab2 100644 --- a/applications/gpio/scenes/gpio_scene_usb_uart_close_rpc.c +++ b/applications/gpio/scenes/gpio_scene_usb_uart_close_rpc.c @@ -1,32 +1,20 @@ #include "../gpio_app_i.h" #include "../gpio_custom_event.h" -static void gpio_scene_usb_uart_close_rpc_event_callback( - GuiButtonType result, - InputType type, - void* context) { - furi_assert(context); - GpioApp* app = context; - - if((result == GuiButtonTypeLeft) && (type == InputTypeShort)) { - view_dispatcher_send_custom_event(app->view_dispatcher, GpioCustomEventErrorBack); - } -} - void gpio_scene_usb_uart_close_rpc_on_enter(void* context) { GpioApp* app = context; + widget_add_icon_element(app->widget, 78, 0, &I_ActiveConnection_50x64); + widget_add_string_multiline_element( + app->widget, 3, 2, AlignLeft, AlignTop, FontPrimary, "Connection\nis active!"); widget_add_string_multiline_element( app->widget, - 63, - 10, - AlignCenter, + 3, + 30, + AlignLeft, AlignTop, FontSecondary, - "Disconnect from\ncompanion app\nto use this function"); - - widget_add_button_element( - app->widget, GuiButtonTypeLeft, "Back", gpio_scene_usb_uart_close_rpc_event_callback, app); + "Disconnect from\nPC or phone to\nuse this function."); view_dispatcher_switch_to_view(app->view_dispatcher, GpioAppViewUsbUartCloseRpc); } diff --git a/applications/gpio/usb_uart_bridge.c b/applications/gpio/usb_uart_bridge.c index c51be513f..4623c4af1 100644 --- a/applications/gpio/usb_uart_bridge.c +++ b/applications/gpio/usb_uart_bridge.c @@ -86,15 +86,15 @@ static void usb_uart_on_irq_cb(UartIrqEvent ev, uint8_t data, void* context) { static void usb_uart_vcp_init(UsbUartBridge* usb_uart, uint8_t vcp_ch) { furi_hal_usb_unlock(); if(vcp_ch == 0) { - Cli* cli = furi_record_open("cli"); + Cli* cli = furi_record_open(RECORD_CLI); cli_session_close(cli); - furi_record_close("cli"); + furi_record_close(RECORD_CLI); furi_check(furi_hal_usb_set_config(&usb_cdc_single, NULL) == true); } else { furi_check(furi_hal_usb_set_config(&usb_cdc_dual, NULL) == true); - Cli* cli = furi_record_open("cli"); + Cli* cli = furi_record_open(RECORD_CLI); cli_session_open(cli, &cli_vcp); - furi_record_close("cli"); + furi_record_close(RECORD_CLI); } furi_hal_cdc_set_callbacks(vcp_ch, (CdcCallbacks*)&cdc_cb, usb_uart); } @@ -103,9 +103,9 @@ static void usb_uart_vcp_deinit(UsbUartBridge* usb_uart, uint8_t vcp_ch) { UNUSED(usb_uart); furi_hal_cdc_set_callbacks(vcp_ch, NULL, NULL); if(vcp_ch != 0) { - Cli* cli = furi_record_open("cli"); + Cli* cli = furi_record_open(RECORD_CLI); cli_session_close(cli); - furi_record_close("cli"); + furi_record_close(RECORD_CLI); } } @@ -276,9 +276,9 @@ static int32_t usb_uart_worker(void* context) { furi_hal_usb_unlock(); furi_check(furi_hal_usb_set_config(&usb_cdc_single, NULL) == true); - Cli* cli = furi_record_open("cli"); + Cli* cli = furi_record_open(RECORD_CLI); cli_session_open(cli, &cli_vcp); - furi_record_close("cli"); + furi_record_close(RECORD_CLI); return 0; } diff --git a/applications/gpio/views/gpio_test.c b/applications/gpio/views/gpio_test.c index 37c2f4083..89c09f864 100755 --- a/applications/gpio/views/gpio_test.c +++ b/applications/gpio/views/gpio_test.c @@ -20,7 +20,7 @@ static bool gpio_test_process_ok(GpioTest* gpio_test, InputEvent* event); static void gpio_test_draw_callback(Canvas* canvas, void* _model) { GpioTestModel* model = _model; canvas_set_font(canvas, FontPrimary); - elements_multiline_text_aligned(canvas, 64, 2, AlignCenter, AlignTop, "Gpio Output mode test"); + elements_multiline_text_aligned(canvas, 64, 2, AlignCenter, AlignTop, "GPIO Output Mode Test"); canvas_set_font(canvas, FontSecondary); elements_multiline_text_aligned( canvas, 64, 16, AlignCenter, AlignTop, "Press < or > to change pin"); diff --git a/applications/gui/gui.c b/applications/gui/gui.c index 50df399ad..6b4b9a0a7 100644 --- a/applications/gui/gui.c +++ b/applications/gui/gui.c @@ -485,7 +485,7 @@ Gui* gui_alloc() { // Input gui->input_queue = furi_message_queue_alloc(8, sizeof(InputEvent)); - gui->input_events = furi_record_open("input_events"); + gui->input_events = furi_record_open(RECORD_INPUT_EVENTS); furi_check(gui->input_events); furi_pubsub_subscribe(gui->input_events, gui_input_events_callback, gui); @@ -497,7 +497,7 @@ int32_t gui_srv(void* p) { UNUSED(p); Gui* gui = gui_alloc(); - furi_record_create("gui", gui); + furi_record_create(RECORD_GUI, gui); while(1) { uint32_t flags = diff --git a/applications/gui/gui.h b/applications/gui/gui.h index e32352426..f48867588 100644 --- a/applications/gui/gui.h +++ b/applications/gui/gui.h @@ -29,6 +29,8 @@ typedef enum { /** Gui Canvas Commit Callback */ typedef void (*GuiCanvasCommitCallback)(uint8_t* data, size_t size, void* context); +#define RECORD_GUI "gui" + typedef struct Gui Gui; /** Add view_port to view_port tree diff --git a/applications/gui/modules/button_menu.c b/applications/gui/modules/button_menu.c index 36fd6f3ab..84fea7888 100644 --- a/applications/gui/modules/button_menu.c +++ b/applications/gui/modules/button_menu.c @@ -185,17 +185,19 @@ static void button_menu_process_ok(ButtonMenu* button_menu, InputType type) { return false; }); - if(item->type == ButtonMenuItemTypeControl) { - if(type == InputTypeShort) { - if(item && item->callback) { - item->callback(item->callback_context, item->index, type); + if(item) { + if(item->type == ButtonMenuItemTypeControl) { + if(type == InputTypeShort) { + if(item && item->callback) { + item->callback(item->callback_context, item->index, type); + } } } - } - if(item->type == ButtonMenuItemTypeCommon) { - if((type == InputTypePress) || (type == InputTypeRelease)) { - if(item && item->callback) { - item->callback(item->callback_context, item->index, type); + if(item->type == ButtonMenuItemTypeCommon) { + if((type == InputTypePress) || (type == InputTypeRelease)) { + if(item && item->callback) { + item->callback(item->callback_context, item->index, type); + } } } } diff --git a/applications/gui/modules/file_browser_worker.c b/applications/gui/modules/file_browser_worker.c index ce3def41e..d705e5c3a 100644 --- a/applications/gui/modules/file_browser_worker.c +++ b/applications/gui/modules/file_browser_worker.c @@ -13,7 +13,7 @@ #define TAG "BrowserWorker" #define ASSETS_DIR "assets" -#define BROWSER_ROOT "/any" +#define BROWSER_ROOT STORAGE_ANY_PATH_PREFIX #define FILE_NAME_LEN_MAX 256 #define LONG_LOAD_THRESHOLD 100 @@ -53,13 +53,13 @@ struct BrowserWorker { static bool browser_path_is_file(string_t path) { bool state = false; FileInfo file_info; - Storage* storage = furi_record_open("storage"); + Storage* storage = furi_record_open(RECORD_STORAGE); if(storage_common_stat(storage, string_get_cstr(path), &file_info) == FSE_OK) { if((file_info.flags & FSF_DIRECTORY) == 0) { state = true; } } - furi_record_close("storage"); + furi_record_close(RECORD_STORAGE); return state; } @@ -97,7 +97,7 @@ static bool browser_filter_by_name(BrowserWorker* browser, string_t name, bool i static bool browser_folder_check_and_switch(string_t path) { FileInfo file_info; - Storage* storage = furi_record_open("storage"); + Storage* storage = furi_record_open(RECORD_STORAGE); bool is_root = false; while(1) { // Check if folder is existing and navigate back if not @@ -111,7 +111,7 @@ static bool browser_folder_check_and_switch(string_t path) { } is_root = browser_path_trim(path); } - furi_record_close("storage"); + furi_record_close(RECORD_STORAGE); return is_root; } @@ -125,7 +125,7 @@ static bool browser_folder_init( FileInfo file_info; uint32_t total_files_cnt = 0; - Storage* storage = furi_record_open("storage"); + Storage* storage = furi_record_open(RECORD_STORAGE); File* directory = storage_file_alloc(storage); char name_temp[FILE_NAME_LEN_MAX]; @@ -167,7 +167,7 @@ static bool browser_folder_init( storage_dir_close(directory); storage_file_free(directory); - furi_record_close("storage"); + furi_record_close(RECORD_STORAGE); return state; } @@ -176,7 +176,7 @@ static bool browser_folder_load(BrowserWorker* browser, string_t path, uint32_t offset, uint32_t count) { FileInfo file_info; - Storage* storage = furi_record_open("storage"); + Storage* storage = furi_record_open(RECORD_STORAGE); File* directory = storage_file_alloc(storage); char name_temp[FILE_NAME_LEN_MAX]; @@ -241,7 +241,7 @@ static bool storage_dir_close(directory); storage_file_free(directory); - furi_record_close("storage"); + furi_record_close(RECORD_STORAGE); return (items_cnt == count); } diff --git a/applications/gui/modules/text_input.c b/applications/gui/modules/text_input.c index 5aa101bb4..26f74e7ac 100644 --- a/applications/gui/modules/text_input.c +++ b/applications/gui/modules/text_input.c @@ -131,7 +131,9 @@ static bool char_is_lowercase(char letter) { } static char char_to_uppercase(const char letter) { - if(isalpha(letter)) { + if(letter == '_') { + return 0x20; + } else if(isalpha(letter)) { return (letter - 0x20); } else { return letter; @@ -147,7 +149,7 @@ static void text_input_backspace_cb(TextInputModel* model) { static void text_input_view_draw_callback(Canvas* canvas, void* _model) { TextInputModel* model = _model; - uint8_t text_length = strlen(model->text_buffer); + uint8_t text_length = model->text_buffer ? strlen(model->text_buffer) : 0; uint8_t needed_string_width = canvas_width(canvas) - 8; uint8_t start_pos = 4; diff --git a/applications/gui/modules/validators.c b/applications/gui/modules/validators.c index 546423d02..ac293f8cb 100644 --- a/applications/gui/modules/validators.c +++ b/applications/gui/modules/validators.c @@ -21,7 +21,7 @@ bool validator_is_file_callback(const char* text, string_t error, void* context) bool ret = true; string_t path; string_init_printf(path, "%s/%s%s", instance->app_path_folder, text, instance->app_extension); - Storage* storage = furi_record_open("storage"); + Storage* storage = furi_record_open(RECORD_STORAGE); if(storage_common_stat(storage, string_get_cstr(path), NULL) == FSE_OK) { ret = false; string_printf(error, "This name\nexists!\nChoose\nanother one."); @@ -29,7 +29,7 @@ bool validator_is_file_callback(const char* text, string_t error, void* context) ret = true; } string_clear(path); - furi_record_close("storage"); + furi_record_close(RECORD_STORAGE); return ret; } diff --git a/applications/gui/modules/validators.h b/applications/gui/modules/validators.h index 15dbe901f..c4c4ef54c 100644 --- a/applications/gui/modules/validators.h +++ b/applications/gui/modules/validators.h @@ -1,7 +1,7 @@ #pragma once -// #include #include +#include #ifdef __cplusplus extern "C" { diff --git a/applications/ibutton/ibutton.c b/applications/ibutton/ibutton.c index 0f54dc3ee..b30a4c61f 100644 --- a/applications/ibutton/ibutton.c +++ b/applications/ibutton/ibutton.c @@ -5,24 +5,10 @@ #include "m-string.h" #include #include -#include "rpc/rpc_app.h" +#include #define TAG "iButtonApp" -static const NotificationSequence sequence_blink_start_cyan = { - &message_blink_start_10, - &message_blink_set_color_cyan, - &message_do_not_reset, - NULL, -}; - -static const NotificationSequence sequence_blink_start_magenta = { - &message_blink_start_10, - &message_blink_set_color_magenta, - &message_do_not_reset, - NULL, -}; - static const NotificationSequence sequence_blink_set_yellow = { &message_blink_set_color_yellow, NULL, @@ -33,11 +19,6 @@ static const NotificationSequence sequence_blink_set_magenta = { NULL, }; -static const NotificationSequence sequence_blink_stop = { - &message_blink_stop, - NULL, -}; - static const NotificationSequence* ibutton_notification_sequences[] = { &sequence_error, &sequence_success, @@ -58,7 +39,7 @@ static void ibutton_make_app_folder(iButton* ibutton) { } } -static bool ibutton_load_key_data(iButton* ibutton, string_t key_path, bool show_dialog) { +bool ibutton_load_key_data(iButton* ibutton, string_t key_path, bool show_dialog) { FlipperFormat* file = flipper_format_file_alloc(ibutton->storage); bool result = false; string_t data; @@ -99,31 +80,20 @@ static bool ibutton_load_key_data(iButton* ibutton, string_t key_path, bool show return result; } -static bool ibutton_rpc_command_callback(RpcAppSystemEvent event, const char* arg, void* context) { +static void ibutton_rpc_command_callback(RpcAppSystemEvent event, void* context) { furi_assert(context); iButton* ibutton = context; - bool result = false; - if(event == RpcAppEventSessionClose) { - rpc_system_app_set_callback(ibutton->rpc_ctx, NULL, NULL); - ibutton->rpc_ctx = NULL; - view_dispatcher_send_custom_event(ibutton->view_dispatcher, iButtonCustomEventRpcExit); - result = true; + view_dispatcher_send_custom_event( + ibutton->view_dispatcher, iButtonCustomEventRpcSessionClose); } else if(event == RpcAppEventAppExit) { view_dispatcher_send_custom_event(ibutton->view_dispatcher, iButtonCustomEventRpcExit); - result = true; } else if(event == RpcAppEventLoadFile) { - if(arg) { - string_set_str(ibutton->file_path, arg); - if(ibutton_load_key_data(ibutton, ibutton->file_path, false)) { - ibutton_worker_emulate_start(ibutton->key_worker, ibutton->key); - result = true; - } - } + view_dispatcher_send_custom_event(ibutton->view_dispatcher, iButtonCustomEventRpcLoad); + } else { + rpc_system_app_confirm(ibutton->rpc_ctx, event, false); } - - return result; } bool ibutton_custom_event_callback(void* context, uint32_t event) { @@ -161,13 +131,11 @@ iButton* ibutton_alloc() { view_dispatcher_set_tick_event_callback( ibutton->view_dispatcher, ibutton_tick_event_callback, 100); - ibutton->gui = furi_record_open("gui"); - view_dispatcher_attach_to_gui( - ibutton->view_dispatcher, ibutton->gui, ViewDispatcherTypeFullscreen); + ibutton->gui = furi_record_open(RECORD_GUI); - ibutton->storage = furi_record_open("storage"); - ibutton->dialogs = furi_record_open("dialogs"); - ibutton->notifications = furi_record_open("notification"); + ibutton->storage = furi_record_open(RECORD_STORAGE); + ibutton->dialogs = furi_record_open(RECORD_DIALOGS); + ibutton->notifications = furi_record_open(RECORD_NOTIFICATION); ibutton->key = ibutton_key_alloc(); ibutton->key_worker = ibutton_worker_alloc(); @@ -224,16 +192,16 @@ void ibutton_free(iButton* ibutton) { view_dispatcher_free(ibutton->view_dispatcher); scene_manager_free(ibutton->scene_manager); - furi_record_close("storage"); + furi_record_close(RECORD_STORAGE); ibutton->storage = NULL; - furi_record_close("notification"); + furi_record_close(RECORD_NOTIFICATION); ibutton->notifications = NULL; - furi_record_close("dialogs"); + furi_record_close(RECORD_DIALOGS); ibutton->dialogs = NULL; - furi_record_close("gui"); + furi_record_close(RECORD_GUI); ibutton->gui = NULL; ibutton_worker_stop_thread(ibutton->key_worker); @@ -337,22 +305,6 @@ void ibutton_text_store_clear(iButton* ibutton) { memset(ibutton->text_store, 0, IBUTTON_TEXT_STORE_SIZE); } -void ibutton_switch_to_previous_scene_one_of( - iButton* ibutton, - const uint32_t* scene_ids, - size_t scene_ids_size) { - furi_assert(scene_ids_size); - SceneManager* scene_manager = ibutton->scene_manager; - - for(size_t i = 0; i < scene_ids_size; ++i) { - const uint32_t scene_id = scene_ids[i]; - if(scene_manager_has_previous_scene(scene_manager, scene_id)) { - scene_manager_search_and_switch_to_previous_scene(scene_manager, scene_id); - return; - } - } -} - void ibutton_notification_message(iButton* ibutton, uint32_t message) { furi_assert(message < sizeof(ibutton_notification_sequences) / sizeof(NotificationSequence*)); notification_message(ibutton->notifications, ibutton_notification_sequences[message]); @@ -366,13 +318,14 @@ int32_t ibutton_app(void* p) { bool key_loaded = false; bool rpc_mode = false; - if(p) { + if(p && strlen(p)) { uint32_t rpc_ctx = 0; if(sscanf(p, "RPC %lX", &rpc_ctx) == 1) { FURI_LOG_D(TAG, "Running in RPC mode"); ibutton->rpc_ctx = (void*)rpc_ctx; rpc_mode = true; rpc_system_app_set_callback(ibutton->rpc_ctx, ibutton_rpc_command_callback, ibutton); + rpc_system_app_send_started(ibutton->rpc_ctx); } else { string_set_str(ibutton->file_path, (const char*)p); if(ibutton_load_key_data(ibutton, ibutton->file_path, true)) { @@ -383,17 +336,24 @@ int32_t ibutton_app(void* p) { } if(rpc_mode) { + view_dispatcher_attach_to_gui( + ibutton->view_dispatcher, ibutton->gui, ViewDispatcherTypeDesktop); scene_manager_next_scene(ibutton->scene_manager, iButtonSceneRpc); - } else if(key_loaded) { - scene_manager_next_scene(ibutton->scene_manager, iButtonSceneEmulate); } else { - scene_manager_next_scene(ibutton->scene_manager, iButtonSceneStart); + view_dispatcher_attach_to_gui( + ibutton->view_dispatcher, ibutton->gui, ViewDispatcherTypeFullscreen); + if(key_loaded) { + scene_manager_next_scene(ibutton->scene_manager, iButtonSceneEmulate); + } else { + scene_manager_next_scene(ibutton->scene_manager, iButtonSceneStart); + } } view_dispatcher_run(ibutton->view_dispatcher); if(ibutton->rpc_ctx) { rpc_system_app_set_callback(ibutton->rpc_ctx, NULL, NULL); + rpc_system_app_send_exited(ibutton->rpc_ctx); } ibutton_free(ibutton); return 0; diff --git a/applications/ibutton/ibutton_cli.c b/applications/ibutton/ibutton_cli.c index 324c636de..d36d3dffd 100644 --- a/applications/ibutton/ibutton_cli.c +++ b/applications/ibutton/ibutton_cli.c @@ -12,10 +12,10 @@ static void onewire_cli(Cli* cli, string_t args, void* context); // app cli function void ibutton_on_system_start() { #ifdef SRV_CLI - Cli* cli = furi_record_open("cli"); + Cli* cli = furi_record_open(RECORD_CLI); cli_add_command(cli, "ikey", CliCommandFlagDefault, ibutton_cli, cli); cli_add_command(cli, "onewire", CliCommandFlagDefault, onewire_cli, cli); - furi_record_close("cli"); + furi_record_close(RECORD_CLI); #else UNUSED(ibutton_cli); UNUSED(onewire_cli); diff --git a/applications/ibutton/ibutton_custom_event.h b/applications/ibutton/ibutton_custom_event.h index 1706e00fa..1e2f0300d 100644 --- a/applications/ibutton/ibutton_custom_event.h +++ b/applications/ibutton/ibutton_custom_event.h @@ -10,5 +10,7 @@ enum iButtonCustomEvent { iButtonCustomEventWorkerEmulated, iButtonCustomEventWorkerRead, + iButtonCustomEventRpcLoad, iButtonCustomEventRpcExit, + iButtonCustomEventRpcSessionClose, }; diff --git a/applications/ibutton/ibutton_i.h b/applications/ibutton/ibutton_i.h index 889d5a67c..fd11b8c19 100644 --- a/applications/ibutton/ibutton_i.h +++ b/applications/ibutton/ibutton_i.h @@ -25,7 +25,7 @@ #define IBUTTON_FILE_NAME_SIZE 100 #define IBUTTON_TEXT_STORE_SIZE 128 -#define IBUTTON_APP_FOLDER "/any/ibutton" +#define IBUTTON_APP_FOLDER ANY_PATH("ibutton") #define IBUTTON_APP_EXTENSION ".ibtn" #define IBUTTON_APP_FILE_TYPE "Flipper iButton key" @@ -78,12 +78,9 @@ typedef enum { } iButtonNotificationMessage; bool ibutton_file_select(iButton* ibutton); +bool ibutton_load_key_data(iButton* ibutton, string_t key_path, bool show_dialog); bool ibutton_save_key(iButton* ibutton, const char* key_name); bool ibutton_delete_key(iButton* ibutton); void ibutton_text_store_set(iButton* ibutton, const char* text, ...); void ibutton_text_store_clear(iButton* ibutton); -void ibutton_switch_to_previous_scene_one_of( - iButton* ibutton, - const uint32_t* scene_ids, - size_t scene_ids_size); void ibutton_notification_message(iButton* ibutton, uint32_t message); diff --git a/applications/ibutton/scenes/ibutton_scene_exit_confirm.c b/applications/ibutton/scenes/ibutton_scene_exit_confirm.c index c4e90892e..2367e1217 100644 --- a/applications/ibutton/scenes/ibutton_scene_exit_confirm.c +++ b/applications/ibutton/scenes/ibutton_scene_exit_confirm.c @@ -21,7 +21,7 @@ void ibutton_scene_exit_confirm_on_enter(void* context) { widget_add_string_element( widget, 64, 19, AlignCenter, AlignBottom, FontPrimary, "Exit to iButton menu?"); widget_add_string_element( - widget, 64, 31, AlignCenter, AlignBottom, FontSecondary, "All unsaved data will be lost."); + widget, 64, 31, AlignCenter, AlignBottom, FontSecondary, "All unsaved data will be lost!"); view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewWidget); } diff --git a/applications/ibutton/scenes/ibutton_scene_read.c b/applications/ibutton/scenes/ibutton_scene_read.c index c4eb15d36..7af351f06 100644 --- a/applications/ibutton/scenes/ibutton_scene_read.c +++ b/applications/ibutton/scenes/ibutton_scene_read.c @@ -14,7 +14,7 @@ void ibutton_scene_read_on_enter(void* context) { DOLPHIN_DEED(DolphinDeedIbuttonRead); popup_set_header(popup, "iButton", 95, 26, AlignCenter, AlignBottom); - popup_set_text(popup, "waiting\nfor key ...", 95, 30, AlignCenter, AlignTop); + popup_set_text(popup, "Waiting\nfor key ...", 95, 30, AlignCenter, AlignTop); popup_set_icon(popup, 0, 5, &I_DolphinWait_61x59); view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewPopup); diff --git a/applications/ibutton/scenes/ibutton_scene_retry_confirm.c b/applications/ibutton/scenes/ibutton_scene_retry_confirm.c index d2778ac15..7f8c95b1e 100644 --- a/applications/ibutton/scenes/ibutton_scene_retry_confirm.c +++ b/applications/ibutton/scenes/ibutton_scene_retry_confirm.c @@ -21,7 +21,7 @@ void ibutton_scene_retry_confirm_on_enter(void* context) { widget_add_string_element( widget, 64, 19, AlignCenter, AlignBottom, FontPrimary, "Return to reading?"); widget_add_string_element( - widget, 64, 29, AlignCenter, AlignBottom, FontSecondary, "All unsaved data will be lost"); + widget, 64, 29, AlignCenter, AlignBottom, FontSecondary, "All unsaved data will be lost!"); view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewWidget); } diff --git a/applications/ibutton/scenes/ibutton_scene_rpc.c b/applications/ibutton/scenes/ibutton_scene_rpc.c index ceeca0179..a3f5eeee4 100644 --- a/applications/ibutton/scenes/ibutton_scene_rpc.c +++ b/applications/ibutton/scenes/ibutton_scene_rpc.c @@ -1,14 +1,17 @@ #include "../ibutton_i.h" #include +#include void ibutton_scene_rpc_on_enter(void* context) { iButton* ibutton = context; - Widget* widget = ibutton->widget; + Popup* popup = ibutton->popup; - widget_add_text_box_element( - widget, 0, 0, 128, 28, AlignCenter, AlignCenter, "RPC mode", false); + popup_set_header(popup, "iButton", 82, 28, AlignCenter, AlignBottom); + popup_set_text(popup, "RPC mode", 82, 32, AlignCenter, AlignTop); - view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewWidget); + popup_set_icon(popup, 2, 14, &I_iButtonKey_49x44); + + view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewPopup); notification_message(ibutton->notifications, &sequence_display_backlight_on); } @@ -17,12 +20,48 @@ bool ibutton_scene_rpc_on_event(void* context, SceneManagerEvent event) { UNUSED(context); UNUSED(event); iButton* ibutton = context; + Popup* popup = ibutton->popup; bool consumed = false; if(event.type == SceneManagerEventTypeCustom) { consumed = true; - if(event.event == iButtonCustomEventRpcExit) { + if(event.event == iButtonCustomEventRpcLoad) { + const char* arg = rpc_system_app_get_data(ibutton->rpc_ctx); + bool result = false; + if(arg) { + string_set_str(ibutton->file_path, arg); + if(ibutton_load_key_data(ibutton, ibutton->file_path, false)) { + ibutton_worker_emulate_start(ibutton->key_worker, ibutton->key); + string_t key_name; + string_init(key_name); + if(string_end_with_str_p(ibutton->file_path, IBUTTON_APP_EXTENSION)) { + path_extract_filename(ibutton->file_path, key_name, true); + } + + if(!string_empty_p(key_name)) { + ibutton_text_store_set( + ibutton, "emulating\n%s", string_get_cstr(key_name)); + } else { + ibutton_text_store_set(ibutton, "emulating"); + } + popup_set_text(popup, ibutton->text_store, 82, 32, AlignCenter, AlignTop); + + ibutton_notification_message(ibutton, iButtonNotificationMessageEmulateStart); + + string_clear(key_name); + result = true; + } + } + rpc_system_app_confirm(ibutton->rpc_ctx, RpcAppEventLoadFile, result); + } else if(event.event == iButtonCustomEventRpcExit) { + rpc_system_app_confirm(ibutton->rpc_ctx, RpcAppEventAppExit, true); + ibutton_notification_message(ibutton, iButtonNotificationMessageBlinkStop); + view_dispatcher_stop(ibutton->view_dispatcher); + } else if(event.event == iButtonCustomEventRpcSessionClose) { + rpc_system_app_set_callback(ibutton->rpc_ctx, NULL, NULL); + ibutton->rpc_ctx = NULL; + ibutton_notification_message(ibutton, iButtonNotificationMessageBlinkStop); view_dispatcher_stop(ibutton->view_dispatcher); } } @@ -32,5 +71,11 @@ bool ibutton_scene_rpc_on_event(void* context, SceneManagerEvent event) { void ibutton_scene_rpc_on_exit(void* context) { iButton* ibutton = context; - widget_reset(ibutton->widget); + Popup* popup = ibutton->popup; + + popup_set_header(popup, NULL, 0, 0, AlignCenter, AlignBottom); + popup_set_text(popup, NULL, 0, 0, AlignCenter, AlignTop); + popup_set_icon(popup, 0, 0, NULL); + + ibutton_notification_message(ibutton, iButtonNotificationMessageBlinkStop); } diff --git a/applications/ibutton/scenes/ibutton_scene_save_name.c b/applications/ibutton/scenes/ibutton_scene_save_name.c index 6caf5d2d5..be6403874 100644 --- a/applications/ibutton/scenes/ibutton_scene_save_name.c +++ b/applications/ibutton/scenes/ibutton_scene_save_name.c @@ -61,8 +61,8 @@ bool ibutton_scene_save_name_on_event(void* context, SceneManagerEvent event) { } else { const uint32_t possible_scenes[] = { iButtonSceneReadKeyMenu, iButtonSceneSavedKeyMenu, iButtonSceneAddType}; - ibutton_switch_to_previous_scene_one_of( - ibutton, possible_scenes, sizeof(possible_scenes) / sizeof(uint32_t)); + scene_manager_search_and_switch_to_previous_scene_one_of( + ibutton->scene_manager, possible_scenes, COUNT_OF(possible_scenes)); } } } diff --git a/applications/ibutton/scenes/ibutton_scene_save_success.c b/applications/ibutton/scenes/ibutton_scene_save_success.c index dffda6a0f..6c24a897c 100644 --- a/applications/ibutton/scenes/ibutton_scene_save_success.c +++ b/applications/ibutton/scenes/ibutton_scene_save_success.c @@ -31,8 +31,8 @@ bool ibutton_scene_save_success_on_event(void* context, SceneManagerEvent event) if(event.event == iButtonCustomEventBack) { const uint32_t possible_scenes[] = { iButtonSceneReadKeyMenu, iButtonSceneSavedKeyMenu, iButtonSceneAddType}; - ibutton_switch_to_previous_scene_one_of( - ibutton, possible_scenes, sizeof(possible_scenes) / sizeof(uint32_t)); + scene_manager_search_and_switch_to_previous_scene_one_of( + ibutton->scene_manager, possible_scenes, COUNT_OF(possible_scenes)); } } diff --git a/applications/ibutton/scenes/ibutton_scene_write_success.c b/applications/ibutton/scenes/ibutton_scene_write_success.c index 6abafbb37..3acb1dea0 100644 --- a/applications/ibutton/scenes/ibutton_scene_write_success.c +++ b/applications/ibutton/scenes/ibutton_scene_write_success.c @@ -31,8 +31,8 @@ bool ibutton_scene_write_success_on_event(void* context, SceneManagerEvent event consumed = true; if(event.event == iButtonCustomEventBack) { const uint32_t possible_scenes[] = {iButtonSceneReadKeyMenu, iButtonSceneStart}; - ibutton_switch_to_previous_scene_one_of( - ibutton, possible_scenes, sizeof(possible_scenes) / sizeof(uint32_t)); + scene_manager_search_and_switch_to_previous_scene_one_of( + ibutton->scene_manager, possible_scenes, COUNT_OF(possible_scenes)); } } diff --git a/applications/infrared/infrared.c b/applications/infrared/infrared.c index 622061165..aef14f9b8 100644 --- a/applications/infrared/infrared.c +++ b/applications/infrared/infrared.c @@ -7,10 +7,12 @@ static const NotificationSequence* infrared_notification_sequences[] = { &sequence_success, &sequence_set_only_green_255, &sequence_reset_green, - &sequence_blink_cyan_10, - &sequence_blink_magenta_10, &sequence_solid_yellow, - &sequence_reset_rgb}; + &sequence_reset_rgb, + &sequence_blink_start_cyan, + &sequence_blink_start_magenta, + &sequence_blink_stop, +}; static void infrared_make_app_folder(Infrared* infrared) { if(!storage_simply_mkdir(infrared->storage, INFRARED_APP_FOLDER)) { @@ -36,54 +38,33 @@ static void infrared_tick_event_callback(void* context) { scene_manager_handle_tick_event(infrared->scene_manager); } -static bool - infrared_rpc_command_callback(RpcAppSystemEvent event, const char* arg, void* context) { +static void infrared_rpc_command_callback(RpcAppSystemEvent event, void* context) { furi_assert(context); Infrared* infrared = context; - - if(!infrared->rpc_ctx) { - return false; - } - - bool result = false; + furi_assert(infrared->rpc_ctx); if(event == RpcAppEventSessionClose) { - rpc_system_app_set_callback(infrared->rpc_ctx, NULL, NULL); - infrared->rpc_ctx = NULL; view_dispatcher_send_custom_event( - infrared->view_dispatcher, InfraredCustomEventTypeBackPressed); - result = true; + infrared->view_dispatcher, InfraredCustomEventTypeRpcSessionClose); } else if(event == RpcAppEventAppExit) { view_dispatcher_send_custom_event( - infrared->view_dispatcher, InfraredCustomEventTypeBackPressed); - result = true; + infrared->view_dispatcher, InfraredCustomEventTypeRpcExit); } else if(event == RpcAppEventLoadFile) { - if(arg) { - string_set_str(infrared->file_path, arg); - result = infrared_remote_load(infrared->remote, infrared->file_path); - infrared_worker_tx_set_get_signal_callback( - infrared->worker, infrared_worker_tx_get_signal_steady_callback, infrared); - infrared_worker_tx_set_signal_sent_callback( - infrared->worker, infrared_signal_sent_callback, infrared); - } + view_dispatcher_send_custom_event( + infrared->view_dispatcher, InfraredCustomEventTypeRpcLoad); } else if(event == RpcAppEventButtonPress) { - if(arg) { - size_t button_index = 0; - if(infrared_remote_find_button_by_name(infrared->remote, arg, &button_index)) { - infrared_tx_start_button_index(infrared, button_index); - result = true; - } - } + view_dispatcher_send_custom_event( + infrared->view_dispatcher, InfraredCustomEventTypeRpcButtonPress); } else if(event == RpcAppEventButtonRelease) { - infrared_tx_stop(infrared); - result = true; + view_dispatcher_send_custom_event( + infrared->view_dispatcher, InfraredCustomEventTypeRpcButtonRelease); + } else { + rpc_system_app_confirm(infrared->rpc_ctx, event, false); } - - return result; } static void infrared_find_vacant_remote_name(string_t name, const char* path) { - Storage* storage = furi_record_open("storage"); + Storage* storage = furi_record_open(RECORD_STORAGE); string_t base_path; string_init_set_str(base_path, path); @@ -120,7 +101,7 @@ static void infrared_find_vacant_remote_name(string_t name, const char* path) { } string_clear(base_path); - furi_record_close("storage"); + furi_record_close(RECORD_STORAGE); } static Infrared* infrared_alloc() { @@ -138,19 +119,18 @@ static Infrared* infrared_alloc() { infrared->scene_manager = scene_manager_alloc(&infrared_scene_handlers, infrared); infrared->view_dispatcher = view_dispatcher_alloc(); - infrared->gui = furi_record_open("gui"); + infrared->gui = furi_record_open(RECORD_GUI); ViewDispatcher* view_dispatcher = infrared->view_dispatcher; - view_dispatcher_attach_to_gui(view_dispatcher, infrared->gui, ViewDispatcherTypeFullscreen); view_dispatcher_enable_queue(view_dispatcher); view_dispatcher_set_event_callback_context(view_dispatcher, infrared); view_dispatcher_set_custom_event_callback(view_dispatcher, infrared_custom_event_callback); view_dispatcher_set_navigation_event_callback(view_dispatcher, infrared_back_event_callback); view_dispatcher_set_tick_event_callback(view_dispatcher, infrared_tick_event_callback, 100); - infrared->storage = furi_record_open("storage"); - infrared->dialogs = furi_record_open("dialogs"); - infrared->notifications = furi_record_open("notification"); + infrared->storage = furi_record_open(RECORD_STORAGE); + infrared->dialogs = furi_record_open(RECORD_DIALOGS); + infrared->notifications = furi_record_open(RECORD_NOTIFICATION); infrared->worker = infrared_worker_alloc(); infrared->remote = infrared_remote_alloc(); @@ -202,6 +182,7 @@ static void infrared_free(Infrared* infrared) { if(infrared->rpc_ctx) { rpc_system_app_set_callback(infrared->rpc_ctx, NULL, NULL); + rpc_system_app_send_exited(infrared->rpc_ctx); infrared->rpc_ctx = NULL; } @@ -240,16 +221,13 @@ static void infrared_free(Infrared* infrared) { infrared_remote_free(infrared->remote); infrared_worker_free(infrared->worker); - furi_record_close("gui"); - infrared->gui = NULL; - - furi_record_close("notification"); + furi_record_close(RECORD_NOTIFICATION); infrared->notifications = NULL; - furi_record_close("dialogs"); + furi_record_close(RECORD_DIALOGS); infrared->dialogs = NULL; - furi_record_close("gui"); + furi_record_close(RECORD_GUI); infrared->gui = NULL; string_clear(infrared->file_path); @@ -300,16 +278,17 @@ bool infrared_rename_current_remote(Infrared* infrared, const char* name) { } string_cat_printf(new_path, "/%s%s", string_get_cstr(new_name), INFRARED_APP_EXTENSION); - Storage* storage = furi_record_open("storage"); + Storage* storage = furi_record_open(RECORD_STORAGE); FS_Error status = storage_common_rename( storage, infrared_remote_get_path(remote), string_get_cstr(new_path)); infrared_remote_set_name(remote, string_get_cstr(new_name)); + infrared_remote_set_path(remote, string_get_cstr(new_path)); string_clear(new_name); string_clear(new_path); - furi_record_close("storage"); + furi_record_close(RECORD_STORAGE); return (status == FSE_OK || status == FSE_EXIST); } @@ -324,6 +303,7 @@ void infrared_tx_start_signal(Infrared* infrared, InfraredSignal* signal) { DOLPHIN_DEED(DolphinDeedIrSend); infrared_worker_tx_start(infrared->worker); + infrared_play_notification_message(infrared, InfraredNotificationMessageBlinkStartSend); } void infrared_tx_start_button_index(Infrared* infrared, size_t button_index) { @@ -333,14 +313,17 @@ void infrared_tx_start_button_index(Infrared* infrared, size_t button_index) { InfraredSignal* signal = infrared_remote_button_get_signal(button); infrared_tx_start_signal(infrared, signal); + infrared_play_notification_message(infrared, InfraredNotificationMessageBlinkStartSend); } void infrared_tx_start_received(Infrared* infrared) { infrared_tx_start_signal(infrared, infrared->received_signal); + infrared_play_notification_message(infrared, InfraredNotificationMessageBlinkStartSend); } void infrared_tx_stop(Infrared* infrared) { infrared_worker_tx_stop(infrared->worker); + infrared_play_notification_message(infrared, InfraredNotificationMessageBlinkStop); } void infrared_text_store_set(Infrared* infrared, uint32_t bank, const char* text, ...) { @@ -377,12 +360,6 @@ void infrared_show_loading_popup(Infrared* infrared, bool show) { } } -void infrared_signal_sent_callback(void* context) { - furi_assert(context); - Infrared* infrared = context; - infrared_play_notification_message(infrared, InfraredNotificationMessageBlinkSend); -} - void infrared_signal_received_callback(void* context, InfraredWorkerSignal* received_signal) { furi_assert(context); Infrared* infrared = context; @@ -428,12 +405,13 @@ int32_t infrared_app(void* p) { bool is_remote_loaded = false; bool is_rpc_mode = false; - if(p) { + if(p && strlen(p)) { uint32_t rpc_ctx = 0; if(sscanf(p, "RPC %lX", &rpc_ctx) == 1) { infrared->rpc_ctx = (void*)rpc_ctx; rpc_system_app_set_callback( infrared->rpc_ctx, infrared_rpc_command_callback, infrared); + rpc_system_app_send_started(infrared->rpc_ctx); is_rpc_mode = true; } else { string_set_str(infrared->file_path, (const char*)p); @@ -447,11 +425,17 @@ int32_t infrared_app(void* p) { } if(is_rpc_mode) { + view_dispatcher_attach_to_gui( + infrared->view_dispatcher, infrared->gui, ViewDispatcherTypeDesktop); scene_manager_next_scene(infrared->scene_manager, InfraredSceneRpc); - } else if(is_remote_loaded) { - scene_manager_next_scene(infrared->scene_manager, InfraredSceneRemote); } else { - scene_manager_next_scene(infrared->scene_manager, InfraredSceneStart); + view_dispatcher_attach_to_gui( + infrared->view_dispatcher, infrared->gui, ViewDispatcherTypeFullscreen); + if(is_remote_loaded) { + scene_manager_next_scene(infrared->scene_manager, InfraredSceneRemote); + } else { + scene_manager_next_scene(infrared->scene_manager, InfraredSceneStart); + } } view_dispatcher_run(infrared->view_dispatcher); diff --git a/applications/infrared/infrared_brute_force.c b/applications/infrared/infrared_brute_force.c index 1e5f557a6..8dbc23012 100644 --- a/applications/infrared/infrared_brute_force.c +++ b/applications/infrared/infrared_brute_force.c @@ -50,7 +50,7 @@ bool infrared_brute_force_calculate_messages(InfraredBruteForce* brute_force) { furi_assert(brute_force->db_filename); bool success = false; - Storage* storage = furi_record_open("storage"); + Storage* storage = furi_record_open(RECORD_STORAGE); FlipperFormat* ff = flipper_format_buffered_file_alloc(storage); success = flipper_format_buffered_file_open_existing(ff, brute_force->db_filename); @@ -68,7 +68,7 @@ bool infrared_brute_force_calculate_messages(InfraredBruteForce* brute_force) { } flipper_format_free(ff); - furi_record_close("storage"); + furi_record_close(RECORD_STORAGE); return success; } @@ -94,14 +94,14 @@ bool infrared_brute_force_start( } if(*record_count) { - Storage* storage = furi_record_open("storage"); + Storage* storage = furi_record_open(RECORD_STORAGE); brute_force->ff = flipper_format_buffered_file_alloc(storage); success = flipper_format_buffered_file_open_existing(brute_force->ff, brute_force->db_filename); if(!success) { flipper_format_free(brute_force->ff); brute_force->ff = NULL; - furi_record_close("storage"); + furi_record_close(RECORD_STORAGE); } } return success; @@ -117,7 +117,7 @@ void infrared_brute_force_stop(InfraredBruteForce* brute_force) { string_reset(brute_force->current_record_name); flipper_format_free(brute_force->ff); - furi_record_close("storage"); + furi_record_close(RECORD_STORAGE); brute_force->ff = NULL; } diff --git a/applications/infrared/infrared_cli.c b/applications/infrared/infrared_cli.c index a2dfc2a30..aae02e8fd 100644 --- a/applications/infrared/infrared_cli.c +++ b/applications/infrared/infrared_cli.c @@ -27,7 +27,7 @@ static void signal_received_callback(void* context, InfraredWorkerSignal* receiv if(infrared_worker_signal_is_decoded(received_signal)) { const InfraredMessage* message = infrared_worker_get_decoded_signal(received_signal); - buf_cnt = sniprintf( + buf_cnt = snprintf( buf, sizeof(buf), "%s, A:0x%0*lX, C:0x%0*lX%s\r\n", @@ -43,13 +43,13 @@ static void signal_received_callback(void* context, InfraredWorkerSignal* receiv size_t timings_cnt; infrared_worker_get_raw_signal(received_signal, &timings, &timings_cnt); - buf_cnt = sniprintf(buf, sizeof(buf), "RAW, %d samples:\r\n", timings_cnt); + buf_cnt = snprintf(buf, sizeof(buf), "RAW, %d samples:\r\n", timings_cnt); cli_write(cli, (uint8_t*)buf, buf_cnt); for(size_t i = 0; i < timings_cnt; ++i) { - buf_cnt = sniprintf(buf, sizeof(buf), "%lu ", timings[i]); + buf_cnt = snprintf(buf, sizeof(buf), "%lu ", timings[i]); cli_write(cli, (uint8_t*)buf, buf_cnt); } - buf_cnt = sniprintf(buf, sizeof(buf), "\r\n"); + buf_cnt = snprintf(buf, sizeof(buf), "\r\n"); cli_write(cli, (uint8_t*)buf, buf_cnt); } } @@ -192,9 +192,9 @@ static void infrared_cli_start_ir(Cli* cli, string_t args, void* context) { } void infrared_on_system_start() { #ifdef SRV_CLI - Cli* cli = (Cli*)furi_record_open("cli"); + Cli* cli = (Cli*)furi_record_open(RECORD_CLI); cli_add_command(cli, "ir", CliCommandFlagDefault, infrared_cli_start_ir, NULL); - furi_record_close("cli"); + furi_record_close(RECORD_CLI); #else UNUSED(infrared_cli_start_ir); #endif diff --git a/applications/infrared/infrared_custom_event.h b/applications/infrared/infrared_custom_event.h index 46d75a9e9..09440ddec 100644 --- a/applications/infrared/infrared_custom_event.h +++ b/applications/infrared/infrared_custom_event.h @@ -14,6 +14,12 @@ enum InfraredCustomEventType { InfraredCustomEventTypePopupClosed, InfraredCustomEventTypeButtonSelected, InfraredCustomEventTypeBackPressed, + + InfraredCustomEventTypeRpcLoad, + InfraredCustomEventTypeRpcExit, + InfraredCustomEventTypeRpcButtonPress, + InfraredCustomEventTypeRpcButtonRelease, + InfraredCustomEventTypeRpcSessionClose, }; #pragma pack(push, 1) diff --git a/applications/infrared/infrared_i.h b/applications/infrared/infrared_i.h index c47753f82..4e9c79d51 100644 --- a/applications/infrared/infrared_i.h +++ b/applications/infrared/infrared_i.h @@ -39,7 +39,7 @@ #define INFRARED_MAX_BUTTON_NAME_LENGTH 22 #define INFRARED_MAX_REMOTE_NAME_LENGTH 22 -#define INFRARED_APP_FOLDER "/any/infrared" +#define INFRARED_APP_FOLDER ANY_PATH("infrared") #define INFRARED_APP_EXTENSION ".ir" #define INFRARED_DEFAULT_REMOTE_NAME "Remote" @@ -115,10 +115,11 @@ typedef enum { InfraredNotificationMessageSuccess, InfraredNotificationMessageGreenOn, InfraredNotificationMessageGreenOff, - InfraredNotificationMessageBlinkRead, - InfraredNotificationMessageBlinkSend, InfraredNotificationMessageYellowOn, InfraredNotificationMessageYellowOff, + InfraredNotificationMessageBlinkStartRead, + InfraredNotificationMessageBlinkStartSend, + InfraredNotificationMessageBlinkStop, } InfraredNotificationMessage; bool infrared_add_remote_with_button(Infrared* infrared, const char* name, InfraredSignal* signal); @@ -132,7 +133,6 @@ void infrared_text_store_clear(Infrared* infrared, uint32_t bank); void infrared_play_notification_message(Infrared* infrared, uint32_t message); void infrared_show_loading_popup(Infrared* infrared, bool show); -void infrared_signal_sent_callback(void* context); void infrared_signal_received_callback(void* context, InfraredWorkerSignal* received_signal); void infrared_text_input_callback(void* context); void infrared_popup_closed_callback(void* context); diff --git a/applications/infrared/infrared_remote.c b/applications/infrared/infrared_remote.c index 957e2457a..4417c3c73 100644 --- a/applications/infrared/infrared_remote.c +++ b/applications/infrared/infrared_remote.c @@ -110,7 +110,7 @@ bool infrared_remote_delete_button(InfraredRemote* remote, size_t index) { } bool infrared_remote_store(InfraredRemote* remote) { - Storage* storage = furi_record_open("storage"); + Storage* storage = furi_record_open(RECORD_STORAGE); FlipperFormat* ff = flipper_format_file_alloc(storage); const char* path = string_get_cstr(remote->path); @@ -134,12 +134,12 @@ bool infrared_remote_store(InfraredRemote* remote) { } flipper_format_free(ff); - furi_record_close("storage"); + furi_record_close(RECORD_STORAGE); return success; } bool infrared_remote_load(InfraredRemote* remote, string_t path) { - Storage* storage = furi_record_open("storage"); + Storage* storage = furi_record_open(RECORD_STORAGE); FlipperFormat* ff = flipper_format_buffered_file_alloc(storage); string_t buf; @@ -174,16 +174,16 @@ bool infrared_remote_load(InfraredRemote* remote, string_t path) { string_clear(buf); flipper_format_free(ff); - furi_record_close("storage"); + furi_record_close(RECORD_STORAGE); return success; } bool infrared_remote_remove(InfraredRemote* remote) { - Storage* storage = furi_record_open("storage"); + Storage* storage = furi_record_open(RECORD_STORAGE); FS_Error status = storage_common_remove(storage, string_get_cstr(remote->path)); infrared_remote_reset(remote); - furi_record_close("storage"); + furi_record_close(RECORD_STORAGE); return (status == FSE_OK || status == FSE_NOT_EXIST); } diff --git a/applications/infrared/scenes/common/infrared_scene_universal_common.c b/applications/infrared/scenes/common/infrared_scene_universal_common.c index 7eff81eec..57ac81168 100644 --- a/applications/infrared/scenes/common/infrared_scene_universal_common.c +++ b/applications/infrared/scenes/common/infrared_scene_universal_common.c @@ -21,12 +21,14 @@ static void infrared_scene_universal_common_show_popup(Infrared* infrared, uint3 infrared_progress_view_set_back_callback( progress, infrared_scene_universal_common_progress_back_callback, infrared); view_stack_add_view(view_stack, infrared_progress_view_get_view(progress)); + infrared_play_notification_message(infrared, InfraredNotificationMessageBlinkStartSend); } static void infrared_scene_universal_common_hide_popup(Infrared* infrared) { ViewStack* view_stack = infrared->view_stack; InfraredProgressView* progress = infrared->progress; view_stack_remove_view(view_stack, infrared_progress_view_get_view(progress)); + infrared_play_notification_message(infrared, InfraredNotificationMessageBlinkStop); } void infrared_scene_universal_common_on_enter(void* context) { @@ -42,7 +44,6 @@ bool infrared_scene_universal_common_on_event(void* context, SceneManagerEvent e if(infrared_brute_force_is_started(brute_force)) { if(event.type == SceneManagerEventTypeTick) { - infrared_play_notification_message(infrared, InfraredNotificationMessageBlinkSend); bool success = infrared_brute_force_send_next(brute_force); if(success) { success = infrared_progress_view_increase_progress(infrared->progress); @@ -71,8 +72,6 @@ bool infrared_scene_universal_common_on_event(void* context, SceneManagerEvent e brute_force, infrared_custom_event_get_value(event.event), &record_count)) { DOLPHIN_DEED(DolphinDeedIrBruteForce); infrared_scene_universal_common_show_popup(infrared, record_count); - infrared_play_notification_message( - infrared, InfraredNotificationMessageBlinkSend); } else { scene_manager_next_scene(scene_manager, InfraredSceneErrorDatabases); } diff --git a/applications/infrared/scenes/infrared_scene_ask_back.c b/applications/infrared/scenes/infrared_scene_ask_back.c index 11cfcb04d..493458ade 100644 --- a/applications/infrared/scenes/infrared_scene_ask_back.c +++ b/applications/infrared/scenes/infrared_scene_ask_back.c @@ -10,13 +10,13 @@ void infrared_scene_ask_back_on_enter(void* context) { DialogEx* dialog_ex = infrared->dialog_ex; if(infrared->app_state.is_learning_new_remote) { - dialog_ex_set_header(dialog_ex, "Exit to Infrared menu?", 64, 0, AlignCenter, AlignTop); + dialog_ex_set_header(dialog_ex, "Exit to Infrared Menu?", 64, 0, AlignCenter, AlignTop); } else { - dialog_ex_set_header(dialog_ex, "Exit to remote menu?", 64, 0, AlignCenter, AlignTop); + dialog_ex_set_header(dialog_ex, "Exit to Remote Menu?", 64, 0, AlignCenter, AlignTop); } dialog_ex_set_text( - dialog_ex, "All unsaved data\nwill be lost", 64, 31, AlignCenter, AlignCenter); + dialog_ex, "All unsaved data\nwill be lost!", 64, 31, AlignCenter, AlignCenter); dialog_ex_set_icon(dialog_ex, 0, 0, NULL); dialog_ex_set_left_button_text(dialog_ex, "Exit"); dialog_ex_set_center_button_text(dialog_ex, NULL); diff --git a/applications/infrared/scenes/infrared_scene_ask_retry.c b/applications/infrared/scenes/infrared_scene_ask_retry.c index 48a5cfcde..c87d9e6d3 100644 --- a/applications/infrared/scenes/infrared_scene_ask_retry.c +++ b/applications/infrared/scenes/infrared_scene_ask_retry.c @@ -9,9 +9,9 @@ void infrared_scene_ask_retry_on_enter(void* context) { Infrared* infrared = context; DialogEx* dialog_ex = infrared->dialog_ex; - dialog_ex_set_header(dialog_ex, "Return to reading?", 64, 0, AlignCenter, AlignTop); + dialog_ex_set_header(dialog_ex, "Return to Reading?", 64, 0, AlignCenter, AlignTop); dialog_ex_set_text( - dialog_ex, "All unsaved data\nwill be lost", 64, 31, AlignCenter, AlignCenter); + dialog_ex, "All unsaved data\nwill be lost!", 64, 31, AlignCenter, AlignCenter); dialog_ex_set_icon(dialog_ex, 0, 0, NULL); dialog_ex_set_left_button_text(dialog_ex, "Exit"); dialog_ex_set_center_button_text(dialog_ex, NULL); diff --git a/applications/infrared/scenes/infrared_scene_debug.c b/applications/infrared/scenes/infrared_scene_debug.c index ddb85644b..dd0609b56 100644 --- a/applications/infrared/scenes/infrared_scene_debug.c +++ b/applications/infrared/scenes/infrared_scene_debug.c @@ -65,4 +65,5 @@ void infrared_scene_debug_on_exit(void* context) { InfraredWorker* worker = infrared->worker; infrared_worker_rx_stop(worker); infrared_worker_rx_enable_blink_on_receiving(worker, false); + infrared_worker_rx_set_received_signal_callback(worker, NULL, NULL); } diff --git a/applications/infrared/scenes/infrared_scene_edit_delete.c b/applications/infrared/scenes/infrared_scene_edit_delete.c index 0842cd613..4dfc054fb 100644 --- a/applications/infrared/scenes/infrared_scene_edit_delete.c +++ b/applications/infrared/scenes/infrared_scene_edit_delete.c @@ -16,7 +16,7 @@ void infrared_scene_edit_delete_on_enter(void* context) { int32_t current_button_index = infrared->app_state.current_button_index; furi_assert(current_button_index != InfraredButtonIndexNone); - dialog_ex_set_header(dialog_ex, "Delete button?", 64, 0, AlignCenter, AlignTop); + dialog_ex_set_header(dialog_ex, "Delete Button?", 64, 0, AlignCenter, AlignTop); InfraredRemoteButton* current_button = infrared_remote_get_button(remote, current_button_index); InfraredSignal* signal = infrared_remote_button_get_signal(current_button); @@ -45,7 +45,7 @@ void infrared_scene_edit_delete_on_enter(void* context) { } } else if(edit_target == InfraredEditTargetRemote) { - dialog_ex_set_header(dialog_ex, "Delete remote?", 64, 0, AlignCenter, AlignTop); + dialog_ex_set_header(dialog_ex, "Delete Remote?", 64, 0, AlignCenter, AlignTop); infrared_text_store_set( infrared, 0, @@ -97,7 +97,7 @@ bool infrared_scene_edit_delete_on_event(void* context, SceneManagerEvent event) } else { const uint32_t possible_scenes[] = {InfraredSceneRemoteList, InfraredSceneStart}; scene_manager_search_and_switch_to_previous_scene_one_of( - scene_manager, possible_scenes, sizeof(possible_scenes) / sizeof(uint32_t)); + scene_manager, possible_scenes, COUNT_OF(possible_scenes)); } consumed = true; } diff --git a/applications/infrared/scenes/infrared_scene_edit_delete_done.c b/applications/infrared/scenes/infrared_scene_edit_delete_done.c index 4502abbdb..49a299d2a 100644 --- a/applications/infrared/scenes/infrared_scene_edit_delete_done.c +++ b/applications/infrared/scenes/infrared_scene_edit_delete_done.c @@ -28,8 +28,10 @@ bool infrared_scene_edit_delete_done_on_event(void* context, SceneManagerEvent e scene_manager, InfraredSceneRemote); } else if(edit_target == InfraredEditTargetRemote) { const uint32_t possible_scenes[] = {InfraredSceneStart, InfraredSceneRemoteList}; - scene_manager_search_and_switch_to_previous_scene_one_of( - scene_manager, possible_scenes, sizeof(possible_scenes) / sizeof(uint32_t)); + if(!scene_manager_search_and_switch_to_previous_scene_one_of( + scene_manager, possible_scenes, COUNT_OF(possible_scenes))) { + view_dispatcher_stop(infrared->view_dispatcher); + } } else { furi_assert(0); } diff --git a/applications/infrared/scenes/infrared_scene_edit_rename_done.c b/applications/infrared/scenes/infrared_scene_edit_rename_done.c index 54b18ab61..6c7096e17 100644 --- a/applications/infrared/scenes/infrared_scene_edit_rename_done.c +++ b/applications/infrared/scenes/infrared_scene_edit_rename_done.c @@ -21,7 +21,10 @@ bool infrared_scene_edit_rename_done_on_event(void* context, SceneManagerEvent e if(event.type == SceneManagerEventTypeCustom) { if(event.event == InfraredCustomEventTypePopupClosed) { - scene_manager_next_scene(infrared->scene_manager, InfraredSceneRemote); + if(!scene_manager_search_and_switch_to_previous_scene( + infrared->scene_manager, InfraredSceneRemote)) { + scene_manager_next_scene(infrared->scene_manager, InfraredSceneRemote); + } consumed = true; } } diff --git a/applications/infrared/scenes/infrared_scene_learn.c b/applications/infrared/scenes/infrared_scene_learn.c index d91b8676a..0edb74ca2 100644 --- a/applications/infrared/scenes/infrared_scene_learn.c +++ b/applications/infrared/scenes/infrared_scene_learn.c @@ -8,6 +8,7 @@ void infrared_scene_learn_on_enter(void* context) { infrared_worker_rx_set_received_signal_callback( worker, infrared_signal_received_callback, context); infrared_worker_rx_start(worker); + infrared_play_notification_message(infrared, InfraredNotificationMessageBlinkStartRead); popup_set_icon(popup, 0, 32, &I_InfraredLearnShort_128x31); popup_set_header(popup, NULL, 0, 0, AlignCenter, AlignCenter); @@ -22,10 +23,7 @@ bool infrared_scene_learn_on_event(void* context, SceneManagerEvent event) { Infrared* infrared = context; bool consumed = false; - if(event.type == SceneManagerEventTypeTick) { - infrared_play_notification_message(infrared, InfraredNotificationMessageBlinkRead); - consumed = true; - } else if(event.type == SceneManagerEventTypeCustom) { + if(event.type == SceneManagerEventTypeCustom) { if(event.event == InfraredCustomEventTypeSignalReceived) { infrared_worker_rx_set_received_signal_callback(infrared->worker, NULL, NULL); infrared_play_notification_message(infrared, InfraredNotificationMessageSuccess); @@ -41,6 +39,7 @@ void infrared_scene_learn_on_exit(void* context) { Infrared* infrared = context; Popup* popup = infrared->popup; infrared_worker_rx_stop(infrared->worker); + infrared_play_notification_message(infrared, InfraredNotificationMessageBlinkStop); popup_set_icon(popup, 0, 0, NULL); popup_set_text(popup, NULL, 0, 0, AlignCenter, AlignCenter); } diff --git a/applications/infrared/scenes/infrared_scene_learn_done.c b/applications/infrared/scenes/infrared_scene_learn_done.c index eb6cced84..7d3571715 100644 --- a/applications/infrared/scenes/infrared_scene_learn_done.c +++ b/applications/infrared/scenes/infrared_scene_learn_done.c @@ -29,7 +29,10 @@ bool infrared_scene_learn_done_on_event(void* context, SceneManagerEvent event) if(event.type == SceneManagerEventTypeCustom) { if(event.event == InfraredCustomEventTypePopupClosed) { - scene_manager_next_scene(infrared->scene_manager, InfraredSceneRemote); + if(!scene_manager_search_and_switch_to_previous_scene( + infrared->scene_manager, InfraredSceneRemote)) { + scene_manager_next_scene(infrared->scene_manager, InfraredSceneRemote); + } consumed = true; } } diff --git a/applications/infrared/scenes/infrared_scene_learn_success.c b/applications/infrared/scenes/infrared_scene_learn_success.c index 1297ebcf9..49e2f45f0 100644 --- a/applications/infrared/scenes/infrared_scene_learn_success.c +++ b/applications/infrared/scenes/infrared_scene_learn_success.c @@ -23,8 +23,6 @@ void infrared_scene_learn_success_on_enter(void* context) { infrared_worker_tx_set_get_signal_callback( infrared->worker, infrared_worker_tx_get_signal_steady_callback, context); - infrared_worker_tx_set_signal_sent_callback( - infrared->worker, infrared_signal_sent_callback, context); if(infrared_signal_is_raw(signal)) { InfraredRawSignal* raw = infrared_signal_get_raw_signal(signal); @@ -104,6 +102,8 @@ bool infrared_scene_learn_success_on_event(void* context, SceneManagerEvent even InfraredSceneLearnSuccess, InfraredSceneLearnSuccessStateSending); infrared_tx_start_received(infrared); + infrared_play_notification_message( + infrared, InfraredNotificationMessageBlinkStartSend); } consumed = true; } else if(event.event == DialogExReleaseCenter) { @@ -111,6 +111,7 @@ bool infrared_scene_learn_success_on_event(void* context, SceneManagerEvent even scene_manager_set_scene_state( scene_manager, InfraredSceneLearnSuccess, InfraredSceneLearnSuccessStateIdle); infrared_tx_stop(infrared); + infrared_play_notification_message(infrared, InfraredNotificationMessageBlinkStop); infrared_play_notification_message(infrared, InfraredNotificationMessageGreenOff); } consumed = true; @@ -124,7 +125,7 @@ void infrared_scene_learn_success_on_exit(void* context) { Infrared* infrared = context; InfraredWorker* worker = infrared->worker; dialog_ex_reset(infrared->dialog_ex); + infrared_play_notification_message(infrared, InfraredNotificationMessageBlinkStop); infrared_play_notification_message(infrared, InfraredNotificationMessageGreenOff); infrared_worker_tx_set_get_signal_callback(worker, NULL, NULL); - infrared_worker_tx_set_signal_sent_callback(worker, NULL, NULL); } diff --git a/applications/infrared/scenes/infrared_scene_remote.c b/applications/infrared/scenes/infrared_scene_remote.c index 50f714889..bcac58819 100644 --- a/applications/infrared/scenes/infrared_scene_remote.c +++ b/applications/infrared/scenes/infrared_scene_remote.c @@ -33,8 +33,6 @@ void infrared_scene_remote_on_enter(void* context) { infrared_worker_tx_set_get_signal_callback( infrared->worker, infrared_worker_tx_get_signal_steady_callback, infrared); - infrared_worker_tx_set_signal_sent_callback( - infrared->worker, infrared_signal_sent_callback, infrared); size_t button_count = infrared_remote_get_button_count(remote); for(size_t i = 0; i < button_count; ++i) { @@ -80,7 +78,7 @@ bool infrared_scene_remote_on_event(void* context, SceneManagerEvent event) { if(event.type == SceneManagerEventTypeBack) { const uint32_t possible_scenes[] = {InfraredSceneRemoteList, InfraredSceneStart}; consumed = scene_manager_search_and_switch_to_previous_scene_one_of( - scene_manager, possible_scenes, sizeof(possible_scenes) / sizeof(uint32_t)); + scene_manager, possible_scenes, COUNT_OF(possible_scenes)); } else if(event.type == SceneManagerEventTypeCustom) { const uint16_t custom_type = infrared_custom_event_get_type(event.event); const int16_t button_index = infrared_custom_event_get_value(event.event); @@ -112,7 +110,7 @@ bool infrared_scene_remote_on_event(void* context, SceneManagerEvent event) { void infrared_scene_remote_on_exit(void* context) { Infrared* infrared = context; + infrared_tx_stop(infrared); infrared_worker_tx_set_get_signal_callback(infrared->worker, NULL, NULL); - infrared_worker_tx_set_signal_sent_callback(infrared->worker, NULL, NULL); button_menu_reset(infrared->button_menu); } diff --git a/applications/infrared/scenes/infrared_scene_rpc.c b/applications/infrared/scenes/infrared_scene_rpc.c index 3cab9f80e..bc9c8652f 100644 --- a/applications/infrared/scenes/infrared_scene_rpc.c +++ b/applications/infrared/scenes/infrared_scene_rpc.c @@ -5,12 +5,14 @@ void infrared_scene_rpc_on_enter(void* context) { Infrared* infrared = context; Popup* popup = infrared->popup; - popup_set_text(popup, "Rpc mode", 64, 28, AlignCenter, AlignCenter); + popup_set_header(popup, "Infrared", 82, 28, AlignCenter, AlignBottom); + popup_set_text(popup, "RPC mode", 82, 32, AlignCenter, AlignTop); + + popup_set_icon(popup, 2, 14, &I_Warning_30x23); // TODO: icon popup_set_context(popup, context); popup_set_callback(popup, infrared_popup_closed_callback); - infrared_play_notification_message(infrared, InfraredNotificationMessageYellowOn); view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewPopup); notification_message(infrared->notifications, &sequence_display_backlight_on); @@ -26,6 +28,43 @@ bool infrared_scene_rpc_on_event(void* context, SceneManagerEvent event) { view_dispatcher_stop(infrared->view_dispatcher); } else if(event.event == InfraredCustomEventTypePopupClosed) { view_dispatcher_stop(infrared->view_dispatcher); + } else if(event.event == InfraredCustomEventTypeRpcLoad) { + bool result = false; + const char* arg = rpc_system_app_get_data(infrared->rpc_ctx); + if(arg) { + string_set_str(infrared->file_path, arg); + result = infrared_remote_load(infrared->remote, infrared->file_path); + infrared_worker_tx_set_get_signal_callback( + infrared->worker, infrared_worker_tx_get_signal_steady_callback, infrared); + } + const char* remote_name = infrared_remote_get_name(infrared->remote); + + infrared_text_store_set(infrared, 0, "loaded\n%s", remote_name); + popup_set_text( + infrared->popup, infrared->text_store[0], 82, 32, AlignCenter, AlignTop); + + rpc_system_app_confirm(infrared->rpc_ctx, RpcAppEventLoadFile, result); + } else if(event.event == InfraredCustomEventTypeRpcButtonPress) { + bool result = false; + const char* arg = rpc_system_app_get_data(infrared->rpc_ctx); + if(arg) { + size_t button_index = 0; + if(infrared_remote_find_button_by_name(infrared->remote, arg, &button_index)) { + infrared_tx_start_button_index(infrared, button_index); + result = true; + } + } + rpc_system_app_confirm(infrared->rpc_ctx, RpcAppEventButtonRelease, result); + } else if(event.event == InfraredCustomEventTypeRpcButtonRelease) { + infrared_tx_stop(infrared); + rpc_system_app_confirm(infrared->rpc_ctx, RpcAppEventButtonRelease, true); + } else if(event.event == InfraredCustomEventTypeRpcExit) { + view_dispatcher_stop(infrared->view_dispatcher); + rpc_system_app_confirm(infrared->rpc_ctx, RpcAppEventAppExit, true); + } else if(event.event == InfraredCustomEventTypeRpcSessionClose) { + rpc_system_app_set_callback(infrared->rpc_ctx, NULL, NULL); + infrared->rpc_ctx = NULL; + view_dispatcher_stop(infrared->view_dispatcher); } } return consumed; @@ -33,5 +72,6 @@ bool infrared_scene_rpc_on_event(void* context, SceneManagerEvent event) { void infrared_scene_rpc_on_exit(void* context) { Infrared* infrared = context; + infrared_tx_stop(infrared); popup_reset(infrared->popup); } diff --git a/applications/infrared/scenes/infrared_scene_universal_tv.c b/applications/infrared/scenes/infrared_scene_universal_tv.c index 83b084c37..583f21fa3 100644 --- a/applications/infrared/scenes/infrared_scene_universal_tv.c +++ b/applications/infrared/scenes/infrared_scene_universal_tv.c @@ -9,7 +9,7 @@ void infrared_scene_universal_tv_on_enter(void* context) { ButtonPanel* button_panel = infrared->button_panel; InfraredBruteForce* brute_force = infrared->brute_force; - infrared_brute_force_set_db_filename(brute_force, "/ext/infrared/assets/tv.ir"); + infrared_brute_force_set_db_filename(brute_force, EXT_PATH("infrared/assets/tv.ir")); button_panel_reserve(button_panel, 2, 3); uint32_t i = 0; diff --git a/applications/input/input.c b/applications/input/input.c index 7270a020a..7b8433aef 100644 --- a/applications/input/input.c +++ b/applications/input/input.c @@ -64,14 +64,15 @@ const char* input_get_type_name(InputType type) { return "Unknown"; } -int32_t input_srv() { +int32_t input_srv(void* p) { + UNUSED(p); input = malloc(sizeof(Input)); input->thread_id = furi_thread_get_current_id(); input->event_pubsub = furi_pubsub_alloc(); - furi_record_create("input_events", input->event_pubsub); + furi_record_create(RECORD_INPUT_EVENTS, input->event_pubsub); #ifdef SRV_CLI - input->cli = furi_record_open("cli"); + input->cli = furi_record_open(RECORD_CLI); if(input->cli) { cli_add_command(input->cli, "input", CliCommandFlagParallelSafe, input_cli, input); } diff --git a/applications/input/input.h b/applications/input/input.h index ad5263a8c..001ab1e2e 100644 --- a/applications/input/input.h +++ b/applications/input/input.h @@ -7,6 +7,8 @@ #include +#define RECORD_INPUT_EVENTS "input_events" + /** Input Types * Some of them are physical events and some logical */ diff --git a/applications/lfrfid/lfrfid_app.cpp b/applications/lfrfid/lfrfid_app.cpp index c491cf40b..5b762ae1d 100644 --- a/applications/lfrfid/lfrfid_app.cpp +++ b/applications/lfrfid/lfrfid_app.cpp @@ -25,9 +25,9 @@ #include #include -#include "rpc/rpc_app.h" +#include -const char* LfRfidApp::app_folder = "/any/lfrfid"; +const char* LfRfidApp::app_folder = ANY_PATH("lfrfid"); const char* LfRfidApp::app_extension = ".rfid"; const char* LfRfidApp::app_filetype = "Flipper RFID key"; @@ -44,41 +44,29 @@ LfRfidApp::~LfRfidApp() { string_clear(file_path); if(rpc_ctx) { rpc_system_app_set_callback(rpc_ctx, NULL, NULL); + rpc_system_app_send_exited(rpc_ctx); } } -static bool rpc_command_callback(RpcAppSystemEvent event, const char* arg, void* context) { +static void rpc_command_callback(RpcAppSystemEvent rpc_event, void* context) { furi_assert(context); LfRfidApp* app = static_cast(context); - bool result = false; - - if(event == RpcAppEventSessionClose) { - rpc_system_app_set_callback(app->rpc_ctx, NULL, NULL); - app->rpc_ctx = NULL; + if(rpc_event == RpcAppEventSessionClose) { LfRfidApp::Event event; - event.type = LfRfidApp::EventType::Exit; + event.type = LfRfidApp::EventType::RpcSessionClose; app->view_controller.send_event(&event); - result = true; - } else if(event == RpcAppEventAppExit) { + } else if(rpc_event == RpcAppEventAppExit) { LfRfidApp::Event event; event.type = LfRfidApp::EventType::Exit; app->view_controller.send_event(&event); - result = true; - } else if(event == RpcAppEventLoadFile) { - if(arg) { - string_set_str(app->file_path, arg); - if(app->load_key_data(app->file_path, &(app->worker.key), false)) { - LfRfidApp::Event event; - event.type = LfRfidApp::EventType::EmulateStart; - app->view_controller.send_event(&event); - app->worker.start_emulate(); - result = true; - } - } + } else if(rpc_event == RpcAppEventLoadFile) { + LfRfidApp::Event event; + event.type = LfRfidApp::EventType::RpcLoadFile; + app->view_controller.send_event(&event); + } else { + rpc_system_app_confirm(app->rpc_ctx, rpc_event, false); } - - return result; } void LfRfidApp::run(void* _args) { @@ -86,11 +74,12 @@ void LfRfidApp::run(void* _args) { make_app_folder(); - if(strlen(args)) { + if(args && strlen(args)) { uint32_t rpc_ctx_ptr = 0; if(sscanf(args, "RPC %lX", &rpc_ctx_ptr) == 1) { rpc_ctx = (RpcAppSystem*)rpc_ctx_ptr; rpc_system_app_set_callback(rpc_ctx, rpc_command_callback, this); + rpc_system_app_send_started(rpc_ctx); scene_controller.add_scene(SceneType::Rpc, new LfRfidAppSceneRpc()); scene_controller.process(100, SceneType::Rpc); } else { diff --git a/applications/lfrfid/lfrfid_app.h b/applications/lfrfid/lfrfid_app.h index 3372552fc..b0d4c5898 100644 --- a/applications/lfrfid/lfrfid_app.h +++ b/applications/lfrfid/lfrfid_app.h @@ -33,6 +33,8 @@ class LfRfidApp { Retry, Exit, EmulateStart, + RpcLoadFile, + RpcSessionClose, }; enum class SceneType : uint8_t { diff --git a/applications/lfrfid/lfrfid_cli.cpp b/applications/lfrfid/lfrfid_cli.cpp index 7d1bb6d4a..732197e95 100644 --- a/applications/lfrfid/lfrfid_cli.cpp +++ b/applications/lfrfid/lfrfid_cli.cpp @@ -107,7 +107,7 @@ static void lfrfid_cli_write(Cli* cli, string_t args) { UNUSED(cli); UNUSED(args); // TODO implement rfid write - printf("Not implemented :(\r\n"); + printf("Not Implemented :(\r\n"); } static void lfrfid_cli_emulate(Cli* cli, string_t args) { diff --git a/applications/lfrfid/scene/lfrfid_app_scene_emulate.cpp b/applications/lfrfid/scene/lfrfid_app_scene_emulate.cpp index 183361a0b..cad4f17c7 100644 --- a/applications/lfrfid/scene/lfrfid_app_scene_emulate.cpp +++ b/applications/lfrfid/scene/lfrfid_app_scene_emulate.cpp @@ -2,18 +2,6 @@ #include #include -static const NotificationSequence sequence_blink_start_magenta = { - &message_blink_start_10, - &message_blink_set_color_magenta, - &message_do_not_reset, - NULL, -}; - -static const NotificationSequence sequence_blink_stop = { - &message_blink_stop, - NULL, -}; - void LfRfidAppSceneEmulate::on_enter(LfRfidApp* app, bool /* need_restore */) { string_init(data_string); diff --git a/applications/lfrfid/scene/lfrfid_app_scene_exit_confirm.cpp b/applications/lfrfid/scene/lfrfid_app_scene_exit_confirm.cpp index bac0247d9..be070b40e 100644 --- a/applications/lfrfid/scene/lfrfid_app_scene_exit_confirm.cpp +++ b/applications/lfrfid/scene/lfrfid_app_scene_exit_confirm.cpp @@ -17,9 +17,9 @@ void LfRfidAppSceneExitConfirm::on_enter(LfRfidApp* app, bool /* need_restore */ auto line_1 = container->add(); auto line_2 = container->add(); - line_1->set_text("Exit to RFID menu?", 64, 19, 128 - 2, AlignCenter, AlignBottom, FontPrimary); + line_1->set_text("Exit to RFID Menu?", 64, 19, 128 - 2, AlignCenter, AlignBottom, FontPrimary); line_2->set_text( - "All unsaved data will be lost.", 64, 31, 0, AlignCenter, AlignBottom, FontSecondary); + "All unsaved data will be lost!", 64, 31, 0, AlignCenter, AlignBottom, FontSecondary); app->view_controller.switch_to(); } diff --git a/applications/lfrfid/scene/lfrfid_app_scene_retry_confirm.cpp b/applications/lfrfid/scene/lfrfid_app_scene_retry_confirm.cpp index e62b91bd4..c18122323 100644 --- a/applications/lfrfid/scene/lfrfid_app_scene_retry_confirm.cpp +++ b/applications/lfrfid/scene/lfrfid_app_scene_retry_confirm.cpp @@ -17,9 +17,9 @@ void LfRfidAppSceneRetryConfirm::on_enter(LfRfidApp* app, bool /* need_restore * auto line_1 = container->add(); auto line_2 = container->add(); - line_1->set_text("Return to reading?", 64, 19, 128 - 2, AlignCenter, AlignBottom, FontPrimary); + line_1->set_text("Return to Reading?", 64, 19, 128 - 2, AlignCenter, AlignBottom, FontPrimary); line_2->set_text( - "All unsaved data will be lost", 64, 29, 0, AlignCenter, AlignBottom, FontSecondary); + "All unsaved data will be lost!", 64, 29, 0, AlignCenter, AlignBottom, FontSecondary); app->view_controller.switch_to(); } diff --git a/applications/lfrfid/scene/lfrfid_app_scene_rpc.cpp b/applications/lfrfid/scene/lfrfid_app_scene_rpc.cpp index 80d479345..43b236286 100644 --- a/applications/lfrfid/scene/lfrfid_app_scene_rpc.cpp +++ b/applications/lfrfid/scene/lfrfid_app_scene_rpc.cpp @@ -1,11 +1,14 @@ #include "lfrfid_app_scene_rpc.h" #include #include +#include void LfRfidAppSceneRpc::on_enter(LfRfidApp* app, bool /* need_restore */) { auto popup = app->view_controller.get(); - popup->set_header("RPC Mode", 64, 30, AlignCenter, AlignTop); + popup->set_header("LF RFID", 89, 30, AlignCenter, AlignTop); + popup->set_text("RPC mode", 89, 43, AlignCenter, AlignTop); + popup->set_icon(0, 3, &I_RFIDDolphinSend_97x61); app->view_controller.switch_to(); @@ -22,16 +25,48 @@ bool LfRfidAppSceneRpc::on_event(LfRfidApp* app, LfRfidApp::Event* event) { LfRfidApp::Event view_event; view_event.type = LfRfidApp::EventType::Back; app->view_controller.send_event(&view_event); + rpc_system_app_confirm(app->rpc_ctx, RpcAppEventAppExit, true); + } else if(event->type == LfRfidApp::EventType::RpcSessionClose) { + // Detach RPC + rpc_system_app_set_callback(app->rpc_ctx, NULL, NULL); + app->rpc_ctx = NULL; + + consumed = true; + LfRfidApp::Event view_event; + view_event.type = LfRfidApp::EventType::Back; + app->view_controller.send_event(&view_event); } else if(event->type == LfRfidApp::EventType::EmulateStart) { + auto popup = app->view_controller.get(); consumed = true; emulating = true; + + app->text_store.set("emulating\n%s", app->worker.key.get_name()); + popup->set_text(app->text_store.text, 89, 43, AlignCenter, AlignTop); + + notification_message(app->notification, &sequence_blink_start_magenta); + } else if(event->type == LfRfidApp::EventType::RpcLoadFile) { + const char* arg = rpc_system_app_get_data(app->rpc_ctx); + bool result = false; + if(arg) { + string_set_str(app->file_path, arg); + if(app->load_key_data(app->file_path, &(app->worker.key), false)) { + LfRfidApp::Event event; + event.type = LfRfidApp::EventType::EmulateStart; + app->view_controller.send_event(&event); + app->worker.start_emulate(); + result = true; + } + } + rpc_system_app_confirm(app->rpc_ctx, RpcAppEventLoadFile, result); } + return consumed; } void LfRfidAppSceneRpc::on_exit(LfRfidApp* app) { if(emulating) { app->worker.stop_emulate(); + notification_message(app->notification, &sequence_blink_stop); } app->view_controller.get()->clean(); } diff --git a/applications/lfrfid/scene/lfrfid_app_scene_write.cpp b/applications/lfrfid/scene/lfrfid_app_scene_write.cpp index d6c9e9f4f..ff7b49a4d 100644 --- a/applications/lfrfid/scene/lfrfid_app_scene_write.cpp +++ b/applications/lfrfid/scene/lfrfid_app_scene_write.cpp @@ -41,13 +41,13 @@ bool LfRfidAppSceneWrite::on_event(LfRfidApp* app, LfRfidApp::Event* event) { case RfidWorker::WriteResult::NotWritable: if(!card_not_supported) { auto popup = app->view_controller.get(); - popup->set_icon(0, 0, NULL); - popup->set_header("Still trying to write", 64, 7, AlignCenter, AlignTop); + popup->set_icon(72, 14, &I_DolphinFirstStart8_56x51); + popup->set_header("Still trying to write...", 64, 3, AlignCenter, AlignTop); popup->set_text( - "This card may be protected\nor does not support this\ntype of writing", - 64, - 23, - AlignCenter, + "Make sure this\ncard is writable\nand not\nprotected.", + 3, + 17, + AlignLeft, AlignTop); card_not_supported = true; } diff --git a/applications/loader/loader.c b/applications/loader/loader.c index f5db5586d..3f4e876f3 100644 --- a/applications/loader/loader.c +++ b/applications/loader/loader.c @@ -290,8 +290,9 @@ static Loader* loader_alloc() { instance->pubsub = furi_pubsub_alloc(); #ifdef SRV_CLI - instance->cli = furi_record_open("cli"); - cli_add_command(instance->cli, "loader", CliCommandFlagParallelSafe, loader_cli, instance); + instance->cli = furi_record_open(RECORD_CLI); + cli_add_command( + instance->cli, RECORD_LOADER, CliCommandFlagParallelSafe, loader_cli, instance); #else UNUSED(loader_cli); #endif @@ -299,7 +300,7 @@ static Loader* loader_alloc() { instance->loader_thread = furi_thread_get_current_id(); // Gui - instance->gui = furi_record_open("gui"); + instance->gui = furi_record_open(RECORD_GUI); instance->view_dispatcher = view_dispatcher_alloc(); view_dispatcher_attach_to_gui( instance->view_dispatcher, instance->gui, ViewDispatcherTypeFullscreen); @@ -343,7 +344,7 @@ static void loader_free(Loader* instance) { furi_assert(instance); if(instance->cli) { - furi_record_close("cli"); + furi_record_close(RECORD_CLI); } furi_pubsub_free(instance->pubsub); @@ -360,7 +361,7 @@ static void loader_free(Loader* instance) { view_dispatcher_remove_view(loader_instance->view_dispatcher, LoaderMenuViewSettings); view_dispatcher_free(loader_instance->view_dispatcher); - furi_record_close("gui"); + furi_record_close(RECORD_GUI); free(instance); instance = NULL; @@ -463,7 +464,7 @@ int32_t loader_srv(void* p) { FURI_LOG_I(TAG, "Started"); - furi_record_create("loader", loader_instance); + furi_record_create(RECORD_LOADER, loader_instance); #ifdef LOADER_AUTOSTART loader_start(loader_instance, LOADER_AUTOSTART, NULL); @@ -480,7 +481,7 @@ int32_t loader_srv(void* p) { } } - furi_record_destroy("loader"); + furi_record_destroy(RECORD_LOADER); loader_free(loader_instance); return 0; diff --git a/applications/loader/loader.h b/applications/loader/loader.h index 4bf835b48..8f95d81b2 100644 --- a/applications/loader/loader.h +++ b/applications/loader/loader.h @@ -3,6 +3,8 @@ #include #include +#define RECORD_LOADER "loader" + typedef struct Loader Loader; typedef enum { diff --git a/applications/music_player/music_player.c b/applications/music_player/music_player.c index 26dfb8124..121efa0f9 100644 --- a/applications/music_player/music_player.c +++ b/applications/music_player/music_player.c @@ -1,15 +1,18 @@ -#include "assets_icons.h" -#include "m-string.h" +#include "music_player_worker.h" + #include #include +#include #include #include -#include "music_player_worker.h" +#include + +#include #define TAG "MusicPlayer" -#define MUSIC_PLAYER_APP_PATH_FOLDER "/any/music_player" +#define MUSIC_PLAYER_APP_PATH_FOLDER ANY_PATH("music_player") #define MUSIC_PLAYER_APP_EXTENSION "*" #define MUSIC_PLAYER_SEMITONE_HISTORY_SIZE 4 @@ -269,7 +272,7 @@ MusicPlayer* music_player_alloc() { view_port_input_callback_set(instance->view_port, input_callback, instance); // Open GUI and register view_port - instance->gui = furi_record_open("gui"); + instance->gui = furi_record_open(RECORD_GUI); gui_add_view_port(instance->gui, instance->view_port, GuiLayerFullscreen); return instance; @@ -277,7 +280,7 @@ MusicPlayer* music_player_alloc() { void music_player_free(MusicPlayer* instance) { gui_remove_view_port(instance->gui, instance->view_port); - furi_record_close("gui"); + furi_record_close(RECORD_GUI); view_port_free(instance->view_port); music_player_worker_free(instance->worker); @@ -297,12 +300,12 @@ int32_t music_player_app(void* p) { string_init(file_path); do { - if(p) { + if(p && strlen(p)) { string_cat_str(file_path, p); } else { string_set_str(file_path, MUSIC_PLAYER_APP_PATH_FOLDER); - DialogsApp* dialogs = furi_record_open("dialogs"); + DialogsApp* dialogs = furi_record_open(RECORD_DIALOGS); bool res = dialog_file_browser_show( dialogs, file_path, @@ -312,7 +315,7 @@ int32_t music_player_app(void* p) { &I_music_10px, false); - furi_record_close("dialogs"); + furi_record_close(RECORD_DIALOGS); if(!res) { FURI_LOG_E(TAG, "No file selected"); break; diff --git a/applications/music_player/music_player_cli.c b/applications/music_player/music_player_cli.c index 986c87816..782004439 100644 --- a/applications/music_player/music_player_cli.c +++ b/applications/music_player/music_player_cli.c @@ -6,7 +6,7 @@ static void music_player_cli(Cli* cli, string_t args, void* context) { UNUSED(context); MusicPlayerWorker* music_player_worker = music_player_worker_alloc(); - Storage* storage = furi_record_open("storage"); + Storage* storage = furi_record_open(RECORD_STORAGE); do { if(storage_common_stat(storage, string_get_cstr(args), NULL) == FSE_OK) { @@ -31,17 +31,17 @@ static void music_player_cli(Cli* cli, string_t args, void* context) { music_player_worker_stop(music_player_worker); } while(0); - furi_record_close("storage"); + furi_record_close(RECORD_STORAGE); music_player_worker_free(music_player_worker); } void music_player_on_system_start() { #ifdef SRV_CLI - Cli* cli = furi_record_open("cli"); + Cli* cli = furi_record_open(RECORD_CLI); cli_add_command(cli, "music_player", CliCommandFlagDefault, music_player_cli, NULL); - furi_record_close("cli"); + furi_record_close(RECORD_CLI); #else UNUSED(music_player_cli); #endif diff --git a/applications/music_player/music_player_worker.c b/applications/music_player/music_player_worker.c index 835745b70..ca4f1d8c9 100644 --- a/applications/music_player/music_player_worker.c +++ b/applications/music_player/music_player_worker.c @@ -329,7 +329,7 @@ bool music_player_worker_load_fmf_from_file(MusicPlayerWorker* instance, const c string_t temp_str; string_init(temp_str); - Storage* storage = furi_record_open("storage"); + Storage* storage = furi_record_open(RECORD_STORAGE); FlipperFormat* file = flipper_format_file_alloc(storage); do { @@ -367,7 +367,7 @@ bool music_player_worker_load_fmf_from_file(MusicPlayerWorker* instance, const c result = true; } while(false); - furi_record_close("storage"); + furi_record_close(RECORD_STORAGE); flipper_format_free(file); string_clear(temp_str); @@ -381,7 +381,7 @@ bool music_player_worker_load_rtttl_from_file(MusicPlayerWorker* instance, const bool result = false; string_t content; string_init(content); - Storage* storage = furi_record_open("storage"); + Storage* storage = furi_record_open(RECORD_STORAGE); File* file = storage_file_alloc(storage); do { @@ -414,7 +414,7 @@ bool music_player_worker_load_rtttl_from_file(MusicPlayerWorker* instance, const } while(0); storage_file_free(file); - furi_record_close("storage"); + furi_record_close(RECORD_STORAGE); string_clear(content); return result; diff --git a/applications/nfc/application.fam b/applications/nfc/application.fam index f43ed3144..ce21bfd27 100644 --- a/applications/nfc/application.fam +++ b/applications/nfc/application.fam @@ -10,7 +10,7 @@ App( ], provides=["nfc_start"], icon="A_NFC_14", - stack_size=4 * 1024, + stack_size=5 * 1024, order=30, ) diff --git a/applications/nfc/helpers/nfc_custom_event.h b/applications/nfc/helpers/nfc_custom_event.h index 5de440017..4227a5b14 100644 --- a/applications/nfc/helpers/nfc_custom_event.h +++ b/applications/nfc/helpers/nfc_custom_event.h @@ -9,4 +9,7 @@ enum NfcCustomEvent { NfcCustomEventByteInputDone, NfcCustomEventTextInputDone, NfcCustomEventDictAttackDone, + NfcCustomEventDictAttackSkip, + NfcCustomEventRpcLoad, + NfcCustomEventRpcSessionClose, }; diff --git a/applications/nfc/helpers/nfc_emv_parser.c b/applications/nfc/helpers/nfc_emv_parser.c index 2e0609994..0d7cb5a33 100755 --- a/applications/nfc/helpers/nfc_emv_parser.c +++ b/applications/nfc/helpers/nfc_emv_parser.c @@ -44,7 +44,7 @@ bool nfc_emv_parser_get_aid_name( for(uint8_t i = 0; i < aid_len; i++) { string_cat_printf(key, "%02X", aid[i]); } - if(nfc_emv_parser_search_data(storage, "/ext/nfc/assets/aid.nfc", key, aid_name)) { + if(nfc_emv_parser_search_data(storage, EXT_PATH("nfc/assets/aid.nfc"), key, aid_name)) { parsed = true; } string_clear(key); @@ -58,7 +58,8 @@ bool nfc_emv_parser_get_country_name( bool parsed = false; string_t key; string_init_printf(key, "%04X", country_code); - if(nfc_emv_parser_search_data(storage, "/ext/nfc/assets/country_code.nfc", key, country_name)) { + if(nfc_emv_parser_search_data( + storage, EXT_PATH("nfc/assets/country_code.nfc"), key, country_name)) { parsed = true; } string_clear(key); @@ -73,7 +74,7 @@ bool nfc_emv_parser_get_currency_name( string_t key; string_init_printf(key, "%04X", currency_code); if(nfc_emv_parser_search_data( - storage, "/ext/nfc/assets/currency_code.nfc", key, currency_name)) { + storage, EXT_PATH("nfc/assets/currency_code.nfc"), key, currency_name)) { parsed = true; } string_clear(key); diff --git a/applications/nfc/helpers/nfc_generators.c b/applications/nfc/helpers/nfc_generators.c index 67d9be7a0..3ec78a127 100644 --- a/applications/nfc/helpers/nfc_generators.c +++ b/applications/nfc/helpers/nfc_generators.c @@ -6,6 +6,8 @@ static const uint8_t version_bytes_mf0ulx1[] = {0x00, 0x04, 0x03, 0x00, 0x01, 0x00, 0x00, 0x03}; static const uint8_t version_bytes_ntag21x[] = {0x00, 0x04, 0x04, 0x02, 0x01, 0x00, 0x00, 0x03}; static const uint8_t version_bytes_ntag_i2c[] = {0x00, 0x04, 0x04, 0x05, 0x02, 0x00, 0x00, 0x03}; +static const uint8_t default_data_ntag203[] = + {0xE1, 0x10, 0x12, 0x00, 0x01, 0x03, 0xA0, 0x10, 0x44, 0x03, 0x00, 0xFE}; static const uint8_t default_data_ntag213[] = {0x01, 0x03, 0xA0, 0x0C, 0x34, 0x03, 0x00, 0xFE}; static const uint8_t default_data_ntag215_216[] = {0x03, 0x00, 0xFE}; static const uint8_t default_data_ntag_i2c[] = {0xE1, 0x10, 0x00, 0x00, 0x03, 0x00, 0xFE}; @@ -53,17 +55,32 @@ static void nfc_generate_mf_ul_orig(NfcDeviceData* data) { MfUltralightData* mful = &data->mf_ul_data; mful->type = MfUltralightTypeUnknown; mful->data_size = 16 * 4; + mful->data_read = mful->data_size; nfc_generate_mf_ul_copy_uid_with_bcc(data); // TODO: what's internal byte on page 2? memset(&mful->data[4 * 4], 0xFF, 4); } +static void nfc_generate_mf_ul_ntag203(NfcDeviceData* data) { + nfc_generate_common_start(data); + nfc_generate_mf_ul_common(data); + + MfUltralightData* mful = &data->mf_ul_data; + mful->type = MfUltralightTypeNTAG203; + mful->data_size = 42 * 4; + mful->data_read = mful->data_size; + nfc_generate_mf_ul_copy_uid_with_bcc(data); + mful->data[9] = 0x48; // Internal byte + memcpy(&mful->data[3 * 4], default_data_ntag203, sizeof(default_data_ntag203)); +} + static void nfc_generate_mf_ul_with_config_common(NfcDeviceData* data, uint8_t num_pages) { nfc_generate_common_start(data); nfc_generate_mf_ul_common(data); MfUltralightData* mful = &data->mf_ul_data; mful->data_size = num_pages * 4; + mful->data_read = mful->data_size; nfc_generate_mf_ul_copy_uid_with_bcc(data); uint16_t config_index = (num_pages - 4) * 4; mful->data[config_index] = 0x04; // STRG_MOD_EN @@ -166,6 +183,7 @@ static void mful->type = type; memcpy(&mful->version, version_bytes_ntag_i2c, sizeof(version_bytes_ntag_i2c)); mful->data_size = num_pages * 4; + mful->data_read = mful->data_size; memcpy(mful->data, data->nfc_data.uid, data->nfc_data.uid_len); mful->data[7] = data->nfc_data.sak; mful->data[8] = data->nfc_data.atqa[0]; @@ -253,62 +271,67 @@ static void nfc_generate_ntag_i2c_plus_2k(NfcDeviceData* data) { static const NfcGenerator mf_ul_generator = { .name = "Mifare Ultralight", .generator_func = nfc_generate_mf_ul_orig, - .next_scene = NfcSceneMifareUlMenu}; + .next_scene = NfcSceneMfUltralightMenu}; static const NfcGenerator mf_ul_11_generator = { .name = "Mifare Ultralight EV1 11", .generator_func = nfc_generate_mf_ul_11, - .next_scene = NfcSceneMifareUlMenu}; + .next_scene = NfcSceneMfUltralightMenu}; static const NfcGenerator mf_ul_h11_generator = { .name = "Mifare Ultralight EV1 H11", .generator_func = nfc_generate_mf_ul_h11, - .next_scene = NfcSceneMifareUlMenu}; + .next_scene = NfcSceneMfUltralightMenu}; static const NfcGenerator mf_ul_21_generator = { .name = "Mifare Ultralight EV1 21", .generator_func = nfc_generate_mf_ul_21, - .next_scene = NfcSceneMifareUlMenu}; + .next_scene = NfcSceneMfUltralightMenu}; static const NfcGenerator mf_ul_h21_generator = { .name = "Mifare Ultralight EV1 H21", .generator_func = nfc_generate_mf_ul_h21, - .next_scene = NfcSceneMifareUlMenu}; + .next_scene = NfcSceneMfUltralightMenu}; + +static const NfcGenerator ntag203_generator = { + .name = "NTAG203", + .generator_func = nfc_generate_mf_ul_ntag203, + .next_scene = NfcSceneMfUltralightMenu}; static const NfcGenerator ntag213_generator = { .name = "NTAG213", .generator_func = nfc_generate_ntag213, - .next_scene = NfcSceneMifareUlMenu}; + .next_scene = NfcSceneMfUltralightMenu}; static const NfcGenerator ntag215_generator = { .name = "NTAG215", .generator_func = nfc_generate_ntag215, - .next_scene = NfcSceneMifareUlMenu}; + .next_scene = NfcSceneMfUltralightMenu}; static const NfcGenerator ntag216_generator = { .name = "NTAG216", .generator_func = nfc_generate_ntag216, - .next_scene = NfcSceneMifareUlMenu}; + .next_scene = NfcSceneMfUltralightMenu}; static const NfcGenerator ntag_i2c_1k_generator = { .name = "NTAG I2C 1k", .generator_func = nfc_generate_ntag_i2c_1k, - .next_scene = NfcSceneMifareUlMenu}; + .next_scene = NfcSceneMfUltralightMenu}; static const NfcGenerator ntag_i2c_2k_generator = { .name = "NTAG I2C 2k", .generator_func = nfc_generate_ntag_i2c_2k, - .next_scene = NfcSceneMifareUlMenu}; + .next_scene = NfcSceneMfUltralightMenu}; static const NfcGenerator ntag_i2c_plus_1k_generator = { .name = "NTAG I2C Plus 1k", .generator_func = nfc_generate_ntag_i2c_plus_1k, - .next_scene = NfcSceneMifareUlMenu}; + .next_scene = NfcSceneMfUltralightMenu}; static const NfcGenerator ntag_i2c_plus_2k_generator = { .name = "NTAG I2C Plus 2k", .generator_func = nfc_generate_ntag_i2c_plus_2k, - .next_scene = NfcSceneMifareUlMenu}; + .next_scene = NfcSceneMfUltralightMenu}; const NfcGenerator* const nfc_generators[] = { &mf_ul_generator, @@ -316,6 +339,7 @@ const NfcGenerator* const nfc_generators[] = { &mf_ul_h11_generator, &mf_ul_21_generator, &mf_ul_h21_generator, + &ntag203_generator, &ntag213_generator, &ntag215_generator, &ntag216_generator, diff --git a/applications/nfc/helpers/nfc_mf_classic_dict.c b/applications/nfc/helpers/nfc_mf_classic_dict.c deleted file mode 100644 index e9cfff391..000000000 --- a/applications/nfc/helpers/nfc_mf_classic_dict.c +++ /dev/null @@ -1,53 +0,0 @@ -#include "nfc_mf_classic_dict.h" - -#include -#include - -#define NFC_MF_CLASSIC_DICT_PATH "/ext/nfc/assets/mf_classic_dict.nfc" - -#define NFC_MF_CLASSIC_KEY_LEN (13) - -bool nfc_mf_classic_dict_check_presence(Storage* storage) { - furi_assert(storage); - return storage_common_stat(storage, NFC_MF_CLASSIC_DICT_PATH, NULL) == FSE_OK; -} - -bool nfc_mf_classic_dict_open_file(Stream* stream) { - furi_assert(stream); - return file_stream_open(stream, NFC_MF_CLASSIC_DICT_PATH, FSAM_READ, FSOM_OPEN_EXISTING); -} - -void nfc_mf_classic_dict_close_file(Stream* stream) { - furi_assert(stream); - file_stream_close(stream); -} - -bool nfc_mf_classic_dict_get_next_key(Stream* stream, uint64_t* key) { - furi_assert(stream); - furi_assert(key); - uint8_t key_byte_tmp = 0; - string_t next_line; - string_init(next_line); - *key = 0; - - bool next_key_read = false; - while(!next_key_read) { - if(!stream_read_line(stream, next_line)) break; - if(string_get_char(next_line, 0) == '#') continue; - if(string_size(next_line) != NFC_MF_CLASSIC_KEY_LEN) continue; - for(uint8_t i = 0; i < 12; i += 2) { - args_char_to_hex( - string_get_char(next_line, i), string_get_char(next_line, i + 1), &key_byte_tmp); - *key |= (uint64_t)key_byte_tmp << 8 * (5 - i / 2); - } - next_key_read = true; - } - - string_clear(next_line); - return next_key_read; -} - -void nfc_mf_classic_dict_reset(Stream* stream) { - furi_assert(stream); - stream_rewind(stream); -} diff --git a/applications/nfc/helpers/nfc_mf_classic_dict.h b/applications/nfc/helpers/nfc_mf_classic_dict.h deleted file mode 100644 index 8d3a73728..000000000 --- a/applications/nfc/helpers/nfc_mf_classic_dict.h +++ /dev/null @@ -1,15 +0,0 @@ -#pragma once - -#include -#include -#include - -bool nfc_mf_classic_dict_check_presence(Storage* storage); - -bool nfc_mf_classic_dict_open_file(Stream* stream); - -void nfc_mf_classic_dict_close_file(Stream* stream); - -bool nfc_mf_classic_dict_get_next_key(Stream* stream, uint64_t* key); - -void nfc_mf_classic_dict_reset(Stream* stream); diff --git a/applications/nfc/nfc.c b/applications/nfc/nfc.c index 0dc4b3ae1..9d32e8713 100644 --- a/applications/nfc/nfc.c +++ b/applications/nfc/nfc.c @@ -13,81 +13,21 @@ bool nfc_back_event_callback(void* context) { return scene_manager_handle_back_event(nfc->scene_manager); } -void nfc_tick_event_callback(void* context) { +static void nfc_rpc_command_callback(RpcAppSystemEvent event, void* context) { furi_assert(context); Nfc* nfc = context; - scene_manager_handle_tick_event(nfc->scene_manager); -} -void nfc_rpc_exit_callback(Nfc* nfc) { - if(nfc->rpc_state == NfcRpcStateEmulating) { - // Stop worker - nfc_worker_stop(nfc->worker); - } else if(nfc->rpc_state == NfcRpcStateEmulated) { - // Stop worker - nfc_worker_stop(nfc->worker); - // Save data in shadow file - nfc_device_save_shadow(nfc->dev, nfc->dev->dev_name); - } - if(nfc->rpc_ctx) { - rpc_system_app_set_callback(nfc->rpc_ctx, NULL, NULL); - nfc->rpc_ctx = NULL; - } -} - -static void nfc_rpc_emulate_callback(NfcWorkerEvent event, void* context) { - UNUSED(event); - Nfc* nfc = context; - - nfc->rpc_state = NfcRpcStateEmulated; -} - -static bool nfc_rpc_command_callback(RpcAppSystemEvent event, const char* arg, void* context) { - furi_assert(context); - Nfc* nfc = context; - - if(!nfc->rpc_ctx) { - return false; - } - - bool result = false; + furi_assert(nfc->rpc_ctx); if(event == RpcAppEventSessionClose) { - rpc_system_app_set_callback(nfc->rpc_ctx, NULL, NULL); - nfc->rpc_ctx = NULL; - view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventViewExit); - result = true; + view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventRpcSessionClose); } else if(event == RpcAppEventAppExit) { view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventViewExit); - result = true; } else if(event == RpcAppEventLoadFile) { - if((arg) && (nfc->rpc_state == NfcRpcStateIdle)) { - if(nfc_device_load(nfc->dev, arg, false)) { - if(nfc->dev->format == NfcDeviceSaveFormatMifareUl) { - nfc_worker_start( - nfc->worker, - NfcWorkerStateEmulateMifareUltralight, - &nfc->dev->dev_data, - nfc_rpc_emulate_callback, - nfc); - } else if(nfc->dev->format == NfcDeviceSaveFormatMifareClassic) { - nfc_worker_start( - nfc->worker, - NfcWorkerStateEmulateMifareClassic, - &nfc->dev->dev_data, - nfc_rpc_emulate_callback, - nfc); - } else { - nfc_worker_start( - nfc->worker, NfcWorkerStateEmulate, &nfc->dev->dev_data, NULL, nfc); - } - nfc->rpc_state = NfcRpcStateEmulating; - result = true; - } - } + view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventRpcLoad); + } else { + rpc_system_app_confirm(nfc->rpc_ctx, event, false); } - - return result; } Nfc* nfc_alloc() { @@ -100,17 +40,15 @@ Nfc* nfc_alloc() { view_dispatcher_set_event_callback_context(nfc->view_dispatcher, nfc); view_dispatcher_set_custom_event_callback(nfc->view_dispatcher, nfc_custom_event_callback); view_dispatcher_set_navigation_event_callback(nfc->view_dispatcher, nfc_back_event_callback); - view_dispatcher_set_tick_event_callback(nfc->view_dispatcher, nfc_tick_event_callback, 100); // Nfc device nfc->dev = nfc_device_alloc(); // Open GUI record - nfc->gui = furi_record_open("gui"); - view_dispatcher_attach_to_gui(nfc->view_dispatcher, nfc->gui, ViewDispatcherTypeFullscreen); + nfc->gui = furi_record_open(RECORD_GUI); // Open Notification record - nfc->notifications = furi_record_open("notification"); + nfc->notifications = furi_record_open(RECORD_NOTIFICATION); // Submenu nfc->submenu = submenu_alloc(); @@ -154,7 +92,7 @@ Nfc* nfc_alloc() { view_dispatcher_add_view( nfc->view_dispatcher, NfcViewBankCard, bank_card_get_view(nfc->bank_card)); - // Dict Attack + // Mifare Classic Dict Attack nfc->dict_attack = dict_attack_alloc(); view_dispatcher_add_view( nfc->view_dispatcher, NfcViewDictAttack, dict_attack_get_view(nfc->dict_attack)); @@ -168,6 +106,21 @@ Nfc* nfc_alloc() { void nfc_free(Nfc* nfc) { furi_assert(nfc); + if(nfc->rpc_state == NfcRpcStateEmulating) { + // Stop worker + nfc_worker_stop(nfc->worker); + } else if(nfc->rpc_state == NfcRpcStateEmulated) { + // Stop worker + nfc_worker_stop(nfc->worker); + // Save data in shadow file + nfc_device_save_shadow(nfc->dev, nfc->dev->dev_name); + } + if(nfc->rpc_ctx) { + rpc_system_app_send_exited(nfc->rpc_ctx); + rpc_system_app_set_callback(nfc->rpc_ctx, NULL, NULL); + nfc->rpc_ctx = NULL; + } + // Nfc device nfc_device_free(nfc->dev); @@ -208,7 +161,7 @@ void nfc_free(Nfc* nfc) { view_dispatcher_remove_view(nfc->view_dispatcher, NfcViewBankCard); bank_card_free(nfc->bank_card); - // Dict Attack + // Mifare Classic Dict Attack view_dispatcher_remove_view(nfc->view_dispatcher, NfcViewDictAttack); dict_attack_free(nfc->dict_attack); @@ -223,11 +176,11 @@ void nfc_free(Nfc* nfc) { scene_manager_free(nfc->scene_manager); // GUI - furi_record_close("gui"); + furi_record_close(RECORD_GUI); nfc->gui = NULL; // Notifications - furi_record_close("notification"); + furi_record_close(RECORD_NOTIFICATION); nfc->notifications = NULL; free(nfc); @@ -246,18 +199,6 @@ void nfc_text_store_clear(Nfc* nfc) { memset(nfc->text_store, 0, sizeof(nfc->text_store)); } -static const NotificationSequence sequence_blink_start_blue = { - &message_blink_start_10, - &message_blink_set_color_blue, - &message_do_not_reset, - NULL, -}; - -static const NotificationSequence sequence_blink_stop = { - &message_blink_stop, - NULL, -}; - void nfc_blink_start(Nfc* nfc) { notification_message(nfc->notifications, &sequence_blink_start_blue); } @@ -285,27 +226,36 @@ int32_t nfc_app(void* p) { char* args = p; // Check argument and run corresponding scene - if((*args != '\0')) { + if(args && strlen(args)) { nfc_device_set_loading_callback(nfc->dev, nfc_show_loading_popup, nfc); uint32_t rpc_ctx = 0; if(sscanf(p, "RPC %lX", &rpc_ctx) == 1) { nfc->rpc_ctx = (void*)rpc_ctx; rpc_system_app_set_callback(nfc->rpc_ctx, nfc_rpc_command_callback, nfc); + rpc_system_app_send_started(nfc->rpc_ctx); + view_dispatcher_attach_to_gui( + nfc->view_dispatcher, nfc->gui, ViewDispatcherTypeDesktop); scene_manager_next_scene(nfc->scene_manager, NfcSceneRpc); - } else if(nfc_device_load(nfc->dev, p, true)) { - if(nfc->dev->format == NfcDeviceSaveFormatMifareUl) { - scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateMifareUl); - } else if(nfc->dev->format == NfcDeviceSaveFormatMifareClassic) { - scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateMifareClassic); + } else { + view_dispatcher_attach_to_gui( + nfc->view_dispatcher, nfc->gui, ViewDispatcherTypeFullscreen); + if(nfc_device_load(nfc->dev, p, true)) { + if(nfc->dev->format == NfcDeviceSaveFormatMifareUl) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightEmulate); + } else if(nfc->dev->format == NfcDeviceSaveFormatMifareClassic) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicEmulate); + } else { + scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateUid); + } } else { - scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateUid); + // Exit app + view_dispatcher_stop(nfc->view_dispatcher); } - } else { - // Exit app - view_dispatcher_stop(nfc->view_dispatcher); } nfc_device_set_loading_callback(nfc->dev, NULL, nfc); } else { + view_dispatcher_attach_to_gui( + nfc->view_dispatcher, nfc->gui, ViewDispatcherTypeFullscreen); scene_manager_next_scene(nfc->scene_manager, NfcSceneStart); } diff --git a/applications/nfc/nfc_cli.c b/applications/nfc/nfc_cli.c index bed007764..4ac95f043 100755 --- a/applications/nfc/nfc_cli.c +++ b/applications/nfc/nfc_cli.c @@ -1,9 +1,10 @@ #include #include #include -#include +#include -#include "nfc_types.h" +#include +#include static void nfc_cli_print_usage() { printf("Usage:\r\n"); @@ -131,9 +132,9 @@ static void nfc_cli(Cli* cli, string_t args, void* context) { void nfc_on_system_start() { #ifdef SRV_CLI - Cli* cli = furi_record_open("cli"); + Cli* cli = furi_record_open(RECORD_CLI); cli_add_command(cli, "nfc", CliCommandFlagDefault, nfc_cli, NULL); - furi_record_close("cli"); + furi_record_close(RECORD_CLI); #else UNUSED(nfc_cli); #endif diff --git a/applications/nfc/nfc_i.h b/applications/nfc/nfc_i.h old mode 100755 new mode 100644 index 4b806d408..5a916e803 --- a/applications/nfc/nfc_i.h +++ b/applications/nfc/nfc_i.h @@ -1,9 +1,6 @@ #pragma once #include "nfc.h" -#include "nfc_types.h" -#include "nfc_worker.h" -#include "nfc_device.h" #include #include @@ -24,6 +21,11 @@ #include #include +#include +#include +#include +#include + #include "views/bank_card.h" #include "views/dict_attack.h" @@ -32,8 +34,6 @@ #include "rpc/rpc_app.h" -#define NFC_SEND_NOTIFICATION_FALSE (0UL) -#define NFC_SEND_NOTIFICATION_TRUE (1UL) #define NFC_TEXT_STORE_SIZE 128 typedef enum { @@ -56,6 +56,7 @@ struct Nfc { char text_store[NFC_TEXT_STORE_SIZE + 1]; string_t text_box_store; + uint8_t byte_input_store[6]; void* rpc_ctx; NfcRpcState rpc_state; @@ -101,5 +102,3 @@ void nfc_blink_start(Nfc* nfc); void nfc_blink_stop(Nfc* nfc); void nfc_show_loading_popup(void* context, bool show); - -void nfc_rpc_exit_callback(Nfc* nfc); diff --git a/applications/nfc/nfc_worker.c b/applications/nfc/nfc_worker.c deleted file mode 100644 index 0ca267365..000000000 --- a/applications/nfc/nfc_worker.c +++ /dev/null @@ -1,708 +0,0 @@ -#include "nfc_worker_i.h" -#include - -#include - -#define TAG "NfcWorker" - -/***************************** NFC Worker API *******************************/ - -NfcWorker* nfc_worker_alloc() { - NfcWorker* nfc_worker = malloc(sizeof(NfcWorker)); - - // Worker thread attributes - nfc_worker->thread = furi_thread_alloc(); - furi_thread_set_name(nfc_worker->thread, "NfcWorker"); - furi_thread_set_stack_size(nfc_worker->thread, 8192); - furi_thread_set_callback(nfc_worker->thread, nfc_worker_task); - furi_thread_set_context(nfc_worker->thread, nfc_worker); - - nfc_worker->callback = NULL; - nfc_worker->context = NULL; - nfc_worker->storage = furi_record_open("storage"); - - // Initialize rfal - while(furi_hal_nfc_is_busy()) { - furi_delay_ms(10); - } - nfc_worker_change_state(nfc_worker, NfcWorkerStateReady); - - if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { - nfc_worker->debug_pcap_worker = nfc_debug_pcap_alloc(nfc_worker->storage); - } - - return nfc_worker; -} - -void nfc_worker_free(NfcWorker* nfc_worker) { - furi_assert(nfc_worker); - - furi_thread_free(nfc_worker->thread); - - furi_record_close("storage"); - - if(nfc_worker->debug_pcap_worker) nfc_debug_pcap_free(nfc_worker->debug_pcap_worker); - - free(nfc_worker); -} - -NfcWorkerState nfc_worker_get_state(NfcWorker* nfc_worker) { - return nfc_worker->state; -} - -void nfc_worker_start( - NfcWorker* nfc_worker, - NfcWorkerState state, - NfcDeviceData* dev_data, - NfcWorkerCallback callback, - void* context) { - furi_assert(nfc_worker); - furi_assert(dev_data); - while(furi_hal_nfc_is_busy()) { - furi_delay_ms(10); - } - - nfc_worker->callback = callback; - nfc_worker->context = context; - nfc_worker->dev_data = dev_data; - nfc_worker_change_state(nfc_worker, state); - furi_thread_start(nfc_worker->thread); -} - -void nfc_worker_stop(NfcWorker* nfc_worker) { - furi_assert(nfc_worker); - if(nfc_worker->state == NfcWorkerStateBroken || nfc_worker->state == NfcWorkerStateReady) { - return; - } - furi_hal_nfc_stop(); - nfc_worker_change_state(nfc_worker, NfcWorkerStateStop); - furi_thread_join(nfc_worker->thread); -} - -void nfc_worker_change_state(NfcWorker* nfc_worker, NfcWorkerState state) { - nfc_worker->state = state; -} - -/***************************** NFC Worker Thread *******************************/ - -int32_t nfc_worker_task(void* context) { - NfcWorker* nfc_worker = context; - - furi_hal_nfc_exit_sleep(); - - if(nfc_worker->state == NfcWorkerStateDetect) { - nfc_worker_detect(nfc_worker); - } else if(nfc_worker->state == NfcWorkerStateEmulate) { - nfc_worker_emulate(nfc_worker); - } else if(nfc_worker->state == NfcWorkerStateReadEMVApp) { - nfc_worker_read_emv_app(nfc_worker); - } else if(nfc_worker->state == NfcWorkerStateReadEMVData) { - nfc_worker_read_emv(nfc_worker); - } else if(nfc_worker->state == NfcWorkerStateEmulateApdu) { - nfc_worker_emulate_apdu(nfc_worker); - } else if(nfc_worker->state == NfcWorkerStateReadMifareUltralight) { - nfc_worker_read_mifare_ultralight(nfc_worker); - } else if(nfc_worker->state == NfcWorkerStateEmulateMifareUltralight) { - nfc_worker_emulate_mifare_ul(nfc_worker); - } else if(nfc_worker->state == NfcWorkerStateReadMifareClassic) { - nfc_worker_mifare_classic_dict_attack(nfc_worker); - } else if(nfc_worker->state == NfcWorkerStateEmulateMifareClassic) { - nfc_worker_emulate_mifare_classic(nfc_worker); - } else if(nfc_worker->state == NfcWorkerStateReadMifareDesfire) { - nfc_worker_read_mifare_desfire(nfc_worker); - } - furi_hal_nfc_sleep(); - nfc_worker_change_state(nfc_worker, NfcWorkerStateReady); - - return 0; -} - -void nfc_worker_detect(NfcWorker* nfc_worker) { - nfc_device_data_clear(nfc_worker->dev_data); - NfcDeviceData* dev_data = nfc_worker->dev_data; - FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data; - - while(nfc_worker->state == NfcWorkerStateDetect) { - if(furi_hal_nfc_detect(nfc_data, 1000)) { - // Process first found device - if(nfc_data->type == FuriHalNfcTypeA) { - if(mf_ul_check_card_type(nfc_data->atqa[0], nfc_data->atqa[1], nfc_data->sak)) { - dev_data->protocol = NfcDeviceProtocolMifareUl; - } else if(mf_classic_check_card_type( - nfc_data->atqa[0], nfc_data->atqa[1], nfc_data->sak)) { - dev_data->protocol = NfcDeviceProtocolMifareClassic; - } else if(mf_df_check_card_type( - nfc_data->atqa[0], nfc_data->atqa[1], nfc_data->sak)) { - dev_data->protocol = NfcDeviceProtocolMifareDesfire; - } else if(nfc_data->interface == FuriHalNfcInterfaceIsoDep) { - dev_data->protocol = NfcDeviceProtocolEMV; - } else { - dev_data->protocol = NfcDeviceProtocolUnknown; - } - } - - // Notify caller and exit - if(nfc_worker->callback) { - nfc_worker->callback(NfcWorkerEventSuccess, nfc_worker->context); - } - break; - } - furi_hal_nfc_sleep(); - furi_delay_ms(100); - } -} - -void nfc_worker_emulate(NfcWorker* nfc_worker) { - FuriHalNfcTxRxContext tx_rx = {}; - nfc_debug_pcap_prepare_tx_rx(nfc_worker->debug_pcap_worker, &tx_rx, true); - FuriHalNfcDevData* data = &nfc_worker->dev_data->nfc_data; - NfcReaderRequestData* reader_data = &nfc_worker->dev_data->reader_data; - - while(nfc_worker->state == NfcWorkerStateEmulate) { - if(furi_hal_nfc_listen(data->uid, data->uid_len, data->atqa, data->sak, true, 100)) { - if(furi_hal_nfc_tx_rx(&tx_rx, 100)) { - reader_data->size = tx_rx.rx_bits / 8; - if(reader_data->size > 0) { - memcpy(reader_data->data, tx_rx.rx_data, reader_data->size); - if(nfc_worker->callback) { - nfc_worker->callback(NfcWorkerEventSuccess, nfc_worker->context); - } - } - } else { - FURI_LOG_E(TAG, "Failed to get reader commands"); - } - } - } -} - -void nfc_worker_read_emv_app(NfcWorker* nfc_worker) { - FuriHalNfcTxRxContext tx_rx = {}; - nfc_debug_pcap_prepare_tx_rx(nfc_worker->debug_pcap_worker, &tx_rx, false); - EmvApplication emv_app = {}; - NfcDeviceData* result = nfc_worker->dev_data; - FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data; - nfc_device_data_clear(result); - - while(nfc_worker->state == NfcWorkerStateReadEMVApp) { - if(furi_hal_nfc_detect(nfc_data, 1000)) { - // Card was found. Check that it supports EMV - if(nfc_data->interface == FuriHalNfcInterfaceIsoDep) { - result->protocol = NfcDeviceProtocolEMV; - if(emv_search_application(&tx_rx, &emv_app)) { - // Notify caller and exit - result->emv_data.aid_len = emv_app.aid_len; - memcpy(result->emv_data.aid, emv_app.aid, emv_app.aid_len); - if(nfc_worker->callback) { - nfc_worker->callback(NfcWorkerEventSuccess, nfc_worker->context); - } - } - } else { - FURI_LOG_W(TAG, "Card doesn't support EMV"); - } - } else { - FURI_LOG_D(TAG, "Can't find any cards"); - } - furi_hal_nfc_sleep(); - furi_delay_ms(20); - } -} - -void nfc_worker_read_emv(NfcWorker* nfc_worker) { - FuriHalNfcTxRxContext tx_rx = {}; - nfc_debug_pcap_prepare_tx_rx(nfc_worker->debug_pcap_worker, &tx_rx, false); - EmvApplication emv_app = {}; - NfcDeviceData* result = nfc_worker->dev_data; - FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data; - nfc_device_data_clear(result); - - while(nfc_worker->state == NfcWorkerStateReadEMVData) { - if(furi_hal_nfc_detect(nfc_data, 1000)) { - // Card was found. Check that it supports EMV - if(nfc_data->interface == FuriHalNfcInterfaceIsoDep) { - result->protocol = NfcDeviceProtocolEMV; - if(emv_read_bank_card(&tx_rx, &emv_app)) { - result->emv_data.number_len = emv_app.card_number_len; - memcpy( - result->emv_data.number, emv_app.card_number, result->emv_data.number_len); - result->emv_data.aid_len = emv_app.aid_len; - memcpy(result->emv_data.aid, emv_app.aid, emv_app.aid_len); - if(emv_app.name_found) { - memcpy(result->emv_data.name, emv_app.name, sizeof(emv_app.name)); - } - if(emv_app.exp_month) { - result->emv_data.exp_mon = emv_app.exp_month; - result->emv_data.exp_year = emv_app.exp_year; - } - if(emv_app.country_code) { - result->emv_data.country_code = emv_app.country_code; - } - if(emv_app.currency_code) { - result->emv_data.currency_code = emv_app.currency_code; - } - // Notify caller and exit - if(nfc_worker->callback) { - nfc_worker->callback(NfcWorkerEventSuccess, nfc_worker->context); - } - break; - } - } else { - FURI_LOG_W(TAG, "Card doesn't support EMV"); - } - } else { - FURI_LOG_D(TAG, "Can't find any cards"); - } - furi_hal_nfc_sleep(); - furi_delay_ms(20); - } -} - -void nfc_worker_emulate_apdu(NfcWorker* nfc_worker) { - FuriHalNfcTxRxContext tx_rx = {}; - nfc_debug_pcap_prepare_tx_rx(nfc_worker->debug_pcap_worker, &tx_rx, true); - FuriHalNfcDevData params = { - .uid = {0xCF, 0x72, 0xd4, 0x40}, - .uid_len = 4, - .atqa = {0x00, 0x04}, - .sak = 0x20, - .type = FuriHalNfcTypeA, - }; - - while(nfc_worker->state == NfcWorkerStateEmulateApdu) { - if(furi_hal_nfc_listen(params.uid, params.uid_len, params.atqa, params.sak, false, 300)) { - FURI_LOG_D(TAG, "POS terminal detected"); - if(emv_card_emulation(&tx_rx)) { - FURI_LOG_D(TAG, "EMV card emulated"); - } - } else { - FURI_LOG_D(TAG, "Can't find reader"); - } - furi_hal_nfc_sleep(); - furi_delay_ms(20); - } -} - -void nfc_worker_read_mifare_ultralight(NfcWorker* nfc_worker) { - FuriHalNfcTxRxContext tx_rx = {}; - nfc_debug_pcap_prepare_tx_rx(nfc_worker->debug_pcap_worker, &tx_rx, false); - MfUltralightReader reader = {}; - MfUltralightData data = {}; - NfcDeviceData* result = nfc_worker->dev_data; - FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data; - - while(nfc_worker->state == NfcWorkerStateReadMifareUltralight) { - if(furi_hal_nfc_detect(nfc_data, 300)) { - if(nfc_data->type == FuriHalNfcTypeA && - mf_ul_check_card_type(nfc_data->atqa[0], nfc_data->atqa[1], nfc_data->sak)) { - FURI_LOG_D(TAG, "Found Mifare Ultralight tag. Start reading"); - if(mf_ul_read_card(&tx_rx, &reader, &data)) { - result->protocol = NfcDeviceProtocolMifareUl; - result->mf_ul_data = data; - // Notify caller and exit - if(nfc_worker->callback) { - nfc_worker->callback(NfcWorkerEventSuccess, nfc_worker->context); - } - break; - } else { - FURI_LOG_D(TAG, "Failed reading Mifare Ultralight"); - } - } else { - FURI_LOG_W(TAG, "Tag is not Mifare Ultralight"); - } - } else { - FURI_LOG_D(TAG, "Can't find any tags"); - } - furi_hal_nfc_sleep(); - furi_delay_ms(100); - } -} - -void nfc_worker_emulate_mifare_ul(NfcWorker* nfc_worker) { - FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data; - MfUltralightEmulator emulator = {}; - mf_ul_prepare_emulation(&emulator, &nfc_worker->dev_data->mf_ul_data); - while(nfc_worker->state == NfcWorkerStateEmulateMifareUltralight) { - mf_ul_reset_emulation(&emulator, true); - furi_hal_nfc_emulate_nfca( - nfc_data->uid, - nfc_data->uid_len, - nfc_data->atqa, - nfc_data->sak, - mf_ul_prepare_emulation_response, - &emulator, - 5000); - // Check if data was modified - if(emulator.data_changed) { - nfc_worker->dev_data->mf_ul_data = emulator.data; - if(nfc_worker->callback) { - nfc_worker->callback(NfcWorkerEventSuccess, nfc_worker->context); - } - emulator.data_changed = false; - } - } -} - -void nfc_worker_mifare_classic_dict_attack(NfcWorker* nfc_worker) { - furi_assert(nfc_worker->callback); - FuriHalNfcTxRxContext tx_rx_ctx = {}; - nfc_debug_pcap_prepare_tx_rx(nfc_worker->debug_pcap_worker, &tx_rx_ctx, false); - MfClassicAuthContext auth_ctx = {}; - MfClassicReader reader = {}; - uint64_t curr_key = 0; - uint16_t curr_sector = 0; - uint8_t total_sectors = 0; - NfcWorkerEvent event; - FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data; - - // Open dictionary - nfc_worker->dict_stream = file_stream_alloc(nfc_worker->storage); - if(!nfc_mf_classic_dict_open_file(nfc_worker->dict_stream)) { - event = NfcWorkerEventNoDictFound; - nfc_worker->callback(event, nfc_worker->context); - nfc_mf_classic_dict_close_file(nfc_worker->dict_stream); - stream_free(nfc_worker->dict_stream); - return; - } - - // Detect Mifare Classic card - while(nfc_worker->state == NfcWorkerStateReadMifareClassic) { - if(furi_hal_nfc_detect(nfc_data, 300)) { - if(mf_classic_get_type( - nfc_data->uid, - nfc_data->uid_len, - nfc_data->atqa[0], - nfc_data->atqa[1], - nfc_data->sak, - &reader)) { - total_sectors = mf_classic_get_total_sectors_num(&reader); - if(reader.type == MfClassicType1k) { - event = NfcWorkerEventDetectedClassic1k; - } else { - event = NfcWorkerEventDetectedClassic4k; - } - nfc_worker->callback(event, nfc_worker->context); - break; - } - } else { - event = NfcWorkerEventNoCardDetected; - nfc_worker->callback(event, nfc_worker->context); - } - } - - if(nfc_worker->state == NfcWorkerStateReadMifareClassic) { - bool card_removed_notified = false; - bool card_found_notified = false; - // Seek for mifare classic keys - for(curr_sector = 0; curr_sector < total_sectors; curr_sector++) { - FURI_LOG_I(TAG, "Sector: %d ...", curr_sector); - event = NfcWorkerEventNewSector; - nfc_worker->callback(event, nfc_worker->context); - mf_classic_auth_init_context(&auth_ctx, reader.cuid, curr_sector); - bool sector_key_found = false; - while(nfc_mf_classic_dict_get_next_key(nfc_worker->dict_stream, &curr_key)) { - furi_hal_nfc_sleep(); - if(furi_hal_nfc_activate_nfca(300, &reader.cuid)) { - if(!card_found_notified) { - if(reader.type == MfClassicType1k) { - event = NfcWorkerEventDetectedClassic1k; - } else { - event = NfcWorkerEventDetectedClassic4k; - } - nfc_worker->callback(event, nfc_worker->context); - card_found_notified = true; - card_removed_notified = false; - } - FURI_LOG_D( - TAG, - "Try to auth to sector %d with key %04lx%08lx", - curr_sector, - (uint32_t)(curr_key >> 32), - (uint32_t)curr_key); - if(mf_classic_auth_attempt(&tx_rx_ctx, &auth_ctx, curr_key)) { - sector_key_found = true; - if((auth_ctx.key_a != MF_CLASSIC_NO_KEY) && - (auth_ctx.key_b != MF_CLASSIC_NO_KEY)) - break; - } - } else { - // Notify that no tag is availalble - FURI_LOG_D(TAG, "Can't find tags"); - if(!card_removed_notified) { - event = NfcWorkerEventNoCardDetected; - nfc_worker->callback(event, nfc_worker->context); - card_removed_notified = true; - card_found_notified = false; - } - } - if(nfc_worker->state != NfcWorkerStateReadMifareClassic) break; - furi_delay_tick(1); - } - if(nfc_worker->state != NfcWorkerStateReadMifareClassic) break; - if(sector_key_found) { - // Notify that keys were found - if(auth_ctx.key_a != MF_CLASSIC_NO_KEY) { - FURI_LOG_I( - TAG, - "Sector %d key A: %04lx%08lx", - curr_sector, - (uint32_t)(auth_ctx.key_a >> 32), - (uint32_t)auth_ctx.key_a); - event = NfcWorkerEventFoundKeyA; - nfc_worker->callback(event, nfc_worker->context); - } - if(auth_ctx.key_b != MF_CLASSIC_NO_KEY) { - FURI_LOG_I( - TAG, - "Sector %d key B: %04lx%08lx", - curr_sector, - (uint32_t)(auth_ctx.key_b >> 32), - (uint32_t)auth_ctx.key_b); - event = NfcWorkerEventFoundKeyB; - nfc_worker->callback(event, nfc_worker->context); - } - // Add sectors to read sequence - mf_classic_reader_add_sector(&reader, curr_sector, auth_ctx.key_a, auth_ctx.key_b); - } - nfc_mf_classic_dict_reset(nfc_worker->dict_stream); - } - } - - if(nfc_worker->state == NfcWorkerStateReadMifareClassic) { - FURI_LOG_I(TAG, "Found keys to %d sectors. Start reading sectors", reader.sectors_to_read); - uint8_t sectors_read = - mf_classic_read_card(&tx_rx_ctx, &reader, &nfc_worker->dev_data->mf_classic_data); - if(sectors_read) { - event = NfcWorkerEventSuccess; - nfc_worker->dev_data->protocol = NfcDeviceProtocolMifareClassic; - FURI_LOG_I(TAG, "Successfully read %d sectors", sectors_read); - } else { - event = NfcWorkerEventFail; - FURI_LOG_W(TAG, "Failed to read any sector"); - } - nfc_worker->callback(event, nfc_worker->context); - } - - nfc_mf_classic_dict_close_file(nfc_worker->dict_stream); - stream_free(nfc_worker->dict_stream); -} - -void nfc_worker_emulate_mifare_classic(NfcWorker* nfc_worker) { - FuriHalNfcTxRxContext tx_rx = {}; - nfc_debug_pcap_prepare_tx_rx(nfc_worker->debug_pcap_worker, &tx_rx, true); - FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data; - MfClassicEmulator emulator = { - .cuid = nfc_util_bytes2num(&nfc_data->uid[nfc_data->uid_len - 4], 4), - .data = nfc_worker->dev_data->mf_classic_data, - .data_changed = false, - }; - NfcaSignal* nfca_signal = nfca_signal_alloc(); - tx_rx.nfca_signal = nfca_signal; - - rfal_platform_spi_acquire(); - - furi_hal_nfc_listen_start(nfc_data); - while(nfc_worker->state == NfcWorkerStateEmulateMifareClassic) { - if(furi_hal_nfc_listen_rx(&tx_rx, 300)) { - mf_classic_emulator(&emulator, &tx_rx); - } - } - if(emulator.data_changed) { - nfc_worker->dev_data->mf_classic_data = emulator.data; - if(nfc_worker->callback) { - nfc_worker->callback(NfcWorkerEventSuccess, nfc_worker->context); - } - emulator.data_changed = false; - } - - nfca_signal_free(nfca_signal); - - rfal_platform_spi_release(); -} - -void nfc_worker_read_mifare_desfire(NfcWorker* nfc_worker) { - FuriHalNfcTxRxContext tx_rx = {}; - nfc_debug_pcap_prepare_tx_rx(nfc_worker->debug_pcap_worker, &tx_rx, false); - NfcDeviceData* result = nfc_worker->dev_data; - nfc_device_data_clear(result); - MifareDesfireData* data = &result->mf_df_data; - FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data; - - while(nfc_worker->state == NfcWorkerStateReadMifareDesfire) { - furi_hal_nfc_sleep(); - if(!furi_hal_nfc_detect(nfc_data, 300)) { - furi_delay_ms(100); - continue; - } - memset(data, 0, sizeof(MifareDesfireData)); - if(nfc_data->type != FuriHalNfcTypeA || - !mf_df_check_card_type(nfc_data->atqa[0], nfc_data->atqa[1], nfc_data->sak)) { - FURI_LOG_D(TAG, "Tag is not DESFire"); - furi_delay_ms(100); - continue; - } - - FURI_LOG_D(TAG, "Found DESFire tag"); - - result->protocol = NfcDeviceProtocolMifareDesfire; - - // Get DESFire version - tx_rx.tx_bits = 8 * mf_df_prepare_get_version(tx_rx.tx_data); - if(!furi_hal_nfc_tx_rx_full(&tx_rx)) { - FURI_LOG_W(TAG, "Bad exchange getting version"); - continue; - } - if(!mf_df_parse_get_version_response(tx_rx.rx_data, tx_rx.rx_bits / 8, &data->version)) { - FURI_LOG_W(TAG, "Bad DESFire GET_VERSION response"); - continue; - } - - tx_rx.tx_bits = 8 * mf_df_prepare_get_free_memory(tx_rx.tx_data); - if(furi_hal_nfc_tx_rx_full(&tx_rx)) { - data->free_memory = malloc(sizeof(MifareDesfireFreeMemory)); - memset(data->free_memory, 0, sizeof(MifareDesfireFreeMemory)); - if(!mf_df_parse_get_free_memory_response( - tx_rx.rx_data, tx_rx.rx_bits / 8, data->free_memory)) { - FURI_LOG_D(TAG, "Bad DESFire GET_FREE_MEMORY response (normal for pre-EV1 cards)"); - free(data->free_memory); - data->free_memory = NULL; - } - } - - tx_rx.tx_bits = 8 * mf_df_prepare_get_key_settings(tx_rx.tx_data); - if(!furi_hal_nfc_tx_rx_full(&tx_rx)) { - FURI_LOG_D(TAG, "Bad exchange getting key settings"); - } else { - data->master_key_settings = malloc(sizeof(MifareDesfireKeySettings)); - memset(data->master_key_settings, 0, sizeof(MifareDesfireKeySettings)); - if(!mf_df_parse_get_key_settings_response( - tx_rx.rx_data, tx_rx.rx_bits / 8, data->master_key_settings)) { - FURI_LOG_W(TAG, "Bad DESFire GET_KEY_SETTINGS response"); - free(data->master_key_settings); - data->master_key_settings = NULL; - } else { - MifareDesfireKeyVersion** key_version_head = - &data->master_key_settings->key_version_head; - for(uint8_t key_id = 0; key_id < data->master_key_settings->max_keys; key_id++) { - tx_rx.tx_bits = 8 * mf_df_prepare_get_key_version(tx_rx.tx_data, key_id); - if(!furi_hal_nfc_tx_rx_full(&tx_rx)) { - FURI_LOG_W(TAG, "Bad exchange getting key version"); - continue; - } - MifareDesfireKeyVersion* key_version = malloc(sizeof(MifareDesfireKeyVersion)); - memset(key_version, 0, sizeof(MifareDesfireKeyVersion)); - key_version->id = key_id; - if(!mf_df_parse_get_key_version_response( - tx_rx.rx_data, tx_rx.rx_bits / 8, key_version)) { - FURI_LOG_W(TAG, "Bad DESFire GET_KEY_VERSION response"); - free(key_version); - continue; - } - *key_version_head = key_version; - key_version_head = &key_version->next; - } - } - } - - tx_rx.tx_bits = 8 * mf_df_prepare_get_application_ids(tx_rx.tx_data); - if(!furi_hal_nfc_tx_rx_full(&tx_rx)) { - FURI_LOG_W(TAG, "Bad exchange getting application IDs"); - } else { - if(!mf_df_parse_get_application_ids_response( - tx_rx.rx_data, tx_rx.rx_bits / 8, &data->app_head)) { - FURI_LOG_W(TAG, "Bad DESFire GET_APPLICATION_IDS response"); - } - } - - for(MifareDesfireApplication* app = data->app_head; app; app = app->next) { - tx_rx.tx_bits = 8 * mf_df_prepare_select_application(tx_rx.tx_data, app->id); - if(!furi_hal_nfc_tx_rx_full(&tx_rx) || - !mf_df_parse_select_application_response(tx_rx.rx_data, tx_rx.rx_bits / 8)) { - FURI_LOG_W(TAG, "Bad exchange selecting application"); - continue; - } - tx_rx.tx_bits = 8 * mf_df_prepare_get_key_settings(tx_rx.tx_data); - if(!furi_hal_nfc_tx_rx_full(&tx_rx)) { - FURI_LOG_W(TAG, "Bad exchange getting key settings"); - } else { - app->key_settings = malloc(sizeof(MifareDesfireKeySettings)); - memset(app->key_settings, 0, sizeof(MifareDesfireKeySettings)); - if(!mf_df_parse_get_key_settings_response( - tx_rx.rx_data, tx_rx.rx_bits / 8, app->key_settings)) { - FURI_LOG_W(TAG, "Bad DESFire GET_KEY_SETTINGS response"); - free(app->key_settings); - app->key_settings = NULL; - continue; - } - - MifareDesfireKeyVersion** key_version_head = &app->key_settings->key_version_head; - for(uint8_t key_id = 0; key_id < app->key_settings->max_keys; key_id++) { - tx_rx.tx_bits = 8 * mf_df_prepare_get_key_version(tx_rx.tx_data, key_id); - if(!furi_hal_nfc_tx_rx_full(&tx_rx)) { - FURI_LOG_W(TAG, "Bad exchange getting key version"); - continue; - } - MifareDesfireKeyVersion* key_version = malloc(sizeof(MifareDesfireKeyVersion)); - memset(key_version, 0, sizeof(MifareDesfireKeyVersion)); - key_version->id = key_id; - if(!mf_df_parse_get_key_version_response( - tx_rx.rx_data, tx_rx.rx_bits / 8, key_version)) { - FURI_LOG_W(TAG, "Bad DESFire GET_KEY_VERSION response"); - free(key_version); - continue; - } - *key_version_head = key_version; - key_version_head = &key_version->next; - } - } - - tx_rx.tx_bits = 8 * mf_df_prepare_get_file_ids(tx_rx.tx_data); - if(!furi_hal_nfc_tx_rx_full(&tx_rx)) { - FURI_LOG_W(TAG, "Bad exchange getting file IDs"); - } else { - if(!mf_df_parse_get_file_ids_response( - tx_rx.rx_data, tx_rx.rx_bits / 8, &app->file_head)) { - FURI_LOG_W(TAG, "Bad DESFire GET_FILE_IDS response"); - } - } - - for(MifareDesfireFile* file = app->file_head; file; file = file->next) { - tx_rx.tx_bits = 8 * mf_df_prepare_get_file_settings(tx_rx.tx_data, file->id); - if(!furi_hal_nfc_tx_rx_full(&tx_rx)) { - FURI_LOG_W(TAG, "Bad exchange getting file settings"); - continue; - } - if(!mf_df_parse_get_file_settings_response( - tx_rx.rx_data, tx_rx.rx_bits / 8, file)) { - FURI_LOG_W(TAG, "Bad DESFire GET_FILE_SETTINGS response"); - continue; - } - switch(file->type) { - case MifareDesfireFileTypeStandard: - case MifareDesfireFileTypeBackup: - tx_rx.tx_bits = 8 * mf_df_prepare_read_data(tx_rx.tx_data, file->id, 0, 0); - break; - case MifareDesfireFileTypeValue: - tx_rx.tx_bits = 8 * mf_df_prepare_get_value(tx_rx.tx_data, file->id); - break; - case MifareDesfireFileTypeLinearRecord: - case MifareDesfireFileTypeCyclicRecord: - tx_rx.tx_bits = 8 * mf_df_prepare_read_records(tx_rx.tx_data, file->id, 0, 0); - break; - } - if(!furi_hal_nfc_tx_rx_full(&tx_rx)) { - FURI_LOG_W(TAG, "Bad exchange reading file %d", file->id); - continue; - } - if(!mf_df_parse_read_data_response(tx_rx.rx_data, tx_rx.rx_bits / 8, file)) { - FURI_LOG_W(TAG, "Bad response reading file %d", file->id); - continue; - } - } - } - - // Notify caller and exit - if(nfc_worker->callback) { - nfc_worker->callback(NfcWorkerEventSuccess, nfc_worker->context); - } - break; - } -} diff --git a/applications/nfc/nfc_worker_i.h b/applications/nfc/nfc_worker_i.h deleted file mode 100644 index 18c495a46..000000000 --- a/applications/nfc/nfc_worker_i.h +++ /dev/null @@ -1,57 +0,0 @@ -#pragma once - -#include "nfc_worker.h" -#include "nfc_i.h" - -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#include "helpers/nfc_mf_classic_dict.h" -#include "helpers/nfc_debug_pcap.h" - -struct NfcWorker { - FuriThread* thread; - Storage* storage; - Stream* dict_stream; - - NfcDeviceData* dev_data; - - NfcWorkerCallback callback; - void* context; - - NfcWorkerState state; - - NfcDebugPcapWorker* debug_pcap_worker; -}; - -void nfc_worker_change_state(NfcWorker* nfc_worker, NfcWorkerState state); - -int32_t nfc_worker_task(void* context); - -void nfc_worker_read_emv_app(NfcWorker* nfc_worker); - -void nfc_worker_read_emv(NfcWorker* nfc_worker); - -void nfc_worker_emulate_apdu(NfcWorker* nfc_worker); - -void nfc_worker_detect(NfcWorker* nfc_worker); - -void nfc_worker_emulate(NfcWorker* nfc_worker); - -void nfc_worker_read_mifare_ultralight(NfcWorker* nfc_worker); - -void nfc_worker_mifare_classic_dict_attack(NfcWorker* nfc_worker); - -void nfc_worker_read_mifare_desfire(NfcWorker* nfc_worker); - -void nfc_worker_emulate_mifare_ul(NfcWorker* nfc_worker); - -void nfc_worker_emulate_mifare_classic(NfcWorker* nfc_worker); diff --git a/applications/nfc/scenes/nfc_scene_card_menu.c b/applications/nfc/scenes/nfc_scene_card_menu.c deleted file mode 100755 index b78624097..000000000 --- a/applications/nfc/scenes/nfc_scene_card_menu.c +++ /dev/null @@ -1,90 +0,0 @@ -#include "../nfc_i.h" - -enum SubmenuIndex { - SubmenuIndexRunApp, - SubmenuIndexChooseScript, - SubmenuIndexEmulate, - SubmenuIndexSave, -}; - -void nfc_scene_card_menu_submenu_callback(void* context, uint32_t index) { - Nfc* nfc = context; - - view_dispatcher_send_custom_event(nfc->view_dispatcher, index); -} - -void nfc_scene_card_menu_on_enter(void* context) { - Nfc* nfc = context; - Submenu* submenu = nfc->submenu; - - if(nfc->dev->dev_data.protocol > NfcDeviceProtocolUnknown) { - submenu_add_item( - submenu, - "Run Compatible App", - SubmenuIndexRunApp, - nfc_scene_card_menu_submenu_callback, - nfc); - } - submenu_add_item( - submenu, - "Additional reading scripts", - SubmenuIndexChooseScript, - nfc_scene_card_menu_submenu_callback, - nfc); - submenu_add_item( - submenu, "Emulate UID", SubmenuIndexEmulate, nfc_scene_card_menu_submenu_callback, nfc); - submenu_add_item( - submenu, "Save UID", SubmenuIndexSave, nfc_scene_card_menu_submenu_callback, nfc); - submenu_set_selected_item( - nfc->submenu, scene_manager_get_scene_state(nfc->scene_manager, NfcSceneCardMenu)); - - view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu); -} - -bool nfc_scene_card_menu_on_event(void* context, SceneManagerEvent event) { - Nfc* nfc = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == SubmenuIndexRunApp) { - scene_manager_set_scene_state( - nfc->scene_manager, NfcSceneCardMenu, SubmenuIndexRunApp); - if(nfc->dev->dev_data.protocol == NfcDeviceProtocolMifareUl) { - scene_manager_next_scene(nfc->scene_manager, NfcSceneReadMifareUl); - } else if(nfc->dev->dev_data.protocol == NfcDeviceProtocolMifareDesfire) { - scene_manager_next_scene(nfc->scene_manager, NfcSceneReadMifareDesfire); - } else if(nfc->dev->dev_data.protocol == NfcDeviceProtocolEMV) { - scene_manager_next_scene(nfc->scene_manager, NfcSceneReadEmvApp); - } else if(nfc->dev->dev_data.protocol == NfcDeviceProtocolMifareClassic) { - scene_manager_next_scene(nfc->scene_manager, NfcSceneReadMifareClassic); - } - consumed = true; - } else if(event.event == SubmenuIndexChooseScript) { - scene_manager_set_scene_state( - nfc->scene_manager, NfcSceneCardMenu, SubmenuIndexChooseScript); - scene_manager_next_scene(nfc->scene_manager, NfcSceneScriptsMenu); - consumed = true; - } else if(event.event == SubmenuIndexEmulate) { - scene_manager_set_scene_state( - nfc->scene_manager, NfcSceneCardMenu, SubmenuIndexEmulate); - scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateUid); - consumed = true; - } else if(event.event == SubmenuIndexSave) { - scene_manager_set_scene_state(nfc->scene_manager, NfcSceneCardMenu, SubmenuIndexSave); - nfc->dev->format = NfcDeviceSaveFormatUid; - scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveName); - consumed = true; - } - } else if(event.type == SceneManagerEventTypeBack) { - consumed = - scene_manager_search_and_switch_to_previous_scene(nfc->scene_manager, NfcSceneStart); - } - - return consumed; -} - -void nfc_scene_card_menu_on_exit(void* context) { - Nfc* nfc = context; - - submenu_reset(nfc->submenu); -} diff --git a/applications/nfc/scenes/nfc_scene_config.h b/applications/nfc/scenes/nfc_scene_config.h index ffd757de3..2b5cb5cf1 100755 --- a/applications/nfc/scenes/nfc_scene_config.h +++ b/applications/nfc/scenes/nfc_scene_config.h @@ -1,41 +1,47 @@ ADD_SCENE(nfc, start, Start) -ADD_SCENE(nfc, read_card, ReadCard) -ADD_SCENE(nfc, read_card_success, ReadCardSuccess) -ADD_SCENE(nfc, card_menu, CardMenu) -ADD_SCENE(nfc, emulate_uid, EmulateUid) -ADD_SCENE(nfc, save_name, SaveName) -ADD_SCENE(nfc, save_success, SaveSuccess) -ADD_SCENE(nfc, file_select, FileSelect) +ADD_SCENE(nfc, read, Read) ADD_SCENE(nfc, saved_menu, SavedMenu) +ADD_SCENE(nfc, extra_actions, ExtraActions) ADD_SCENE(nfc, set_type, SetType) ADD_SCENE(nfc, set_sak, SetSak) ADD_SCENE(nfc, set_atqa, SetAtqua) ADD_SCENE(nfc, set_uid, SetUid) -ADD_SCENE(nfc, scripts_menu, ScriptsMenu) -ADD_SCENE(nfc, read_mifare_ul, ReadMifareUl) -ADD_SCENE(nfc, read_mifare_ul_success, ReadMifareUlSuccess) -ADD_SCENE(nfc, mifare_ul_menu, MifareUlMenu) -ADD_SCENE(nfc, emulate_mifare_ul, EmulateMifareUl) -ADD_SCENE(nfc, read_emv_app, ReadEmvApp) -ADD_SCENE(nfc, read_emv_app_success, ReadEmvAppSuccess) -ADD_SCENE(nfc, read_mifare_desfire, ReadMifareDesfire) -ADD_SCENE(nfc, read_mifare_desfire_success, ReadMifareDesfireSuccess) -ADD_SCENE(nfc, mifare_desfire_menu, MifareDesfireMenu) -ADD_SCENE(nfc, mifare_desfire_data, MifareDesfireData) -ADD_SCENE(nfc, mifare_desfire_app, MifareDesfireApp) +ADD_SCENE(nfc, generate_info, GenerateInfo) +ADD_SCENE(nfc, read_card_success, ReadCardSuccess) +ADD_SCENE(nfc, save_name, SaveName) +ADD_SCENE(nfc, save_success, SaveSuccess) +ADD_SCENE(nfc, file_select, FileSelect) +ADD_SCENE(nfc, emulate_uid, EmulateUid) +ADD_SCENE(nfc, mf_ultralight_read_success, MfUltralightReadSuccess) +ADD_SCENE(nfc, mf_ultralight_menu, MfUltralightMenu) +ADD_SCENE(nfc, mf_ultralight_emulate, MfUltralightEmulate) +ADD_SCENE(nfc, mf_ultralight_read_auth, MfUltralightReadAuth) +ADD_SCENE(nfc, mf_ultralight_read_auth_result, MfUltralightReadAuthResult) +ADD_SCENE(nfc, mf_ultralight_key_input, MfUltralightKeyInput) +ADD_SCENE(nfc, mf_ultralight_unlock_menu, MfUltralightUnlockMenu) +ADD_SCENE(nfc, mf_ultralight_unlock_warn, MfUltralightUnlockWarn) +ADD_SCENE(nfc, mf_desfire_read_success, MfDesfireReadSuccess) +ADD_SCENE(nfc, mf_desfire_menu, MfDesfireMenu) +ADD_SCENE(nfc, mf_desfire_data, MfDesfireData) +ADD_SCENE(nfc, mf_desfire_app, MfDesfireApp) +ADD_SCENE(nfc, mf_classic_read_success, MfClassicReadSuccess) +ADD_SCENE(nfc, mf_classic_info, MfClassicInfo) +ADD_SCENE(nfc, mf_classic_menu, MfClassicMenu) +ADD_SCENE(nfc, mf_classic_emulate, MfClassicEmulate) +ADD_SCENE(nfc, mf_classic_keys, MfClassicKeys) +ADD_SCENE(nfc, mf_classic_keys_add, MfClassicKeysAdd) +ADD_SCENE(nfc, mf_classic_dict_attack, MfClassicDictAttack) +ADD_SCENE(nfc, emv_read_success, EmvReadSuccess) +ADD_SCENE(nfc, emulate_apdu_sequence, EmulateApduSequence) ADD_SCENE(nfc, device_info, DeviceInfo) ADD_SCENE(nfc, delete, Delete) ADD_SCENE(nfc, delete_success, DeleteSuccess) -ADD_SCENE(nfc, run_emv_app_confirm, RunEmvAppConfirm) -ADD_SCENE(nfc, read_emv_data, ReadEmvData) -ADD_SCENE(nfc, read_emv_data_success, ReadEmvDataSuccess) -ADD_SCENE(nfc, emulate_apdu_sequence, EmulateApduSequence) +ADD_SCENE(nfc, restore_original_confirm, RestoreOriginalConfirm) ADD_SCENE(nfc, restore_original, RestoreOriginal) ADD_SCENE(nfc, debug, Debug) ADD_SCENE(nfc, field, Field) -ADD_SCENE(nfc, read_mifare_classic, ReadMifareClassic) -ADD_SCENE(nfc, emulate_mifare_classic, EmulateMifareClassic) -ADD_SCENE(nfc, mifare_classic_menu, MifareClassicMenu) ADD_SCENE(nfc, dict_not_found, DictNotFound) ADD_SCENE(nfc, rpc, Rpc) -ADD_SCENE(nfc, generate_info, GenerateInfo) +ADD_SCENE(nfc, exit_confirm, ExitConfirm) +ADD_SCENE(nfc, retry_confirm, RetryConfirm) +ADD_SCENE(nfc, detect_reader, DetectReader) diff --git a/applications/nfc/scenes/nfc_scene_detect_reader.c b/applications/nfc/scenes/nfc_scene_detect_reader.c new file mode 100644 index 000000000..f734f04cb --- /dev/null +++ b/applications/nfc/scenes/nfc_scene_detect_reader.c @@ -0,0 +1,143 @@ +#include "../nfc_i.h" +#include + +#define NFC_SCENE_DETECT_READER_LOG_SIZE_MAX (200) + +enum { + NfcSceneDetectReaderStateWidget, + NfcSceneDetectReaderStateTextBox, +}; + +bool nfc_detect_reader_worker_callback(NfcWorkerEvent event, void* context) { + UNUSED(event); + furi_assert(context); + Nfc* nfc = context; + view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventWorkerExit); + return true; +} + +void nfc_scene_detect_reader_widget_callback(GuiButtonType result, InputType type, void* context) { + furi_assert(context); + Nfc* nfc = context; + if(type == InputTypeShort) { + view_dispatcher_send_custom_event(nfc->view_dispatcher, result); + } +} + +void nfc_detect_reader_textbox_callback(void* context) { + furi_assert(context); + Nfc* nfc = context; + view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventViewExit); +} + +// Add widget with device name or inform that data received +static void nfc_scene_detect_reader_widget_config(Nfc* nfc, bool data_received) { + Widget* widget = nfc->widget; + widget_reset(widget); + + widget_add_icon_element(widget, 0, 14, &I_Reader_detect); + widget_add_string_element( + widget, 64, 3, AlignCenter, AlignTop, FontSecondary, "Hold Near Reader"); + widget_add_string_element(widget, 55, 22, AlignLeft, AlignTop, FontPrimary, "Emulating..."); + + if(data_received) { + widget_add_button_element( + widget, GuiButtonTypeCenter, "Log", nfc_scene_detect_reader_widget_callback, nfc); + } +} + +void nfc_scene_detect_reader_on_enter(void* context) { + Nfc* nfc = context; + DOLPHIN_DEED(DolphinDeedNfcEmulate); + FuriHalNfcDevData nfc_params = { + .uid = {0x36, 0x9C, 0xe7, 0xb1, 0x0A, 0xC1, 0x34}, + .uid_len = 7, + .atqa = {0x44, 0x00}, + .sak = 0x08, + .type = FuriHalNfcTypeA, + }; + nfc->dev->dev_data.nfc_data = nfc_params; + + // Setup Widget + nfc_scene_detect_reader_widget_config(nfc, false); + // Setup TextBox + TextBox* text_box = nfc->text_box; + text_box_set_font(text_box, TextBoxFontHex); + text_box_set_focus(text_box, TextBoxFocusEnd); + string_reset(nfc->text_box_store); + + // Set Widget state and view + scene_manager_set_scene_state( + nfc->scene_manager, NfcSceneDetectReader, NfcSceneDetectReaderStateWidget); + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); + // Start worker + memset(&nfc->dev->dev_data.reader_data, 0, sizeof(NfcReaderRequestData)); + nfc_worker_start( + nfc->worker, + NfcWorkerStateUidEmulate, + &nfc->dev->dev_data, + nfc_detect_reader_worker_callback, + nfc); + + nfc_blink_start(nfc); +} + +bool nfc_scene_detect_reader_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + NfcReaderRequestData* reader_data = &nfc->dev->dev_data.reader_data; + uint32_t state = scene_manager_get_scene_state(nfc->scene_manager, NfcSceneDetectReader); + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == NfcCustomEventWorkerExit) { + // Add data button to widget if data is received for the first time + if(!string_size(nfc->text_box_store)) { + nfc_scene_detect_reader_widget_config(nfc, true); + } + // Update TextBox data + if(string_size(nfc->text_box_store) < NFC_SCENE_DETECT_READER_LOG_SIZE_MAX) { + string_cat_printf(nfc->text_box_store, "R:"); + for(uint16_t i = 0; i < reader_data->size; i++) { + string_cat_printf(nfc->text_box_store, " %02X", reader_data->data[i]); + } + string_push_back(nfc->text_box_store, '\n'); + text_box_set_text(nfc->text_box, string_get_cstr(nfc->text_box_store)); + } + memset(reader_data, 0, sizeof(NfcReaderRequestData)); + consumed = true; + } else if(event.event == GuiButtonTypeCenter && state == NfcSceneDetectReaderStateWidget) { + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewTextBox); + scene_manager_set_scene_state( + nfc->scene_manager, NfcSceneDetectReader, NfcSceneDetectReaderStateTextBox); + consumed = true; + } else if(event.event == NfcCustomEventViewExit && state == NfcSceneDetectReaderStateTextBox) { + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); + scene_manager_set_scene_state( + nfc->scene_manager, NfcSceneDetectReader, NfcSceneDetectReaderStateWidget); + consumed = true; + } + } else if(event.type == SceneManagerEventTypeBack) { + if(state == NfcSceneDetectReaderStateTextBox) { + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); + scene_manager_set_scene_state( + nfc->scene_manager, NfcSceneDetectReader, NfcSceneDetectReaderStateWidget); + consumed = true; + } + } + + return consumed; +} + +void nfc_scene_detect_reader_on_exit(void* context) { + Nfc* nfc = context; + + // Stop worker + nfc_worker_stop(nfc->worker); + + // Clear view + widget_reset(nfc->widget); + text_box_reset(nfc->text_box); + string_reset(nfc->text_box_store); + + nfc_blink_stop(nfc); +} diff --git a/applications/nfc/scenes/nfc_scene_device_info.c b/applications/nfc/scenes/nfc_scene_device_info.c index 03464c662..b79c51046 100644 --- a/applications/nfc/scenes/nfc_scene_device_info.c +++ b/applications/nfc/scenes/nfc_scene_device_info.c @@ -190,7 +190,7 @@ bool nfc_scene_device_info_on_event(void* context, SceneManagerEvent event) { view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewBankCard); consumed = true; } else if(nfc->dev->format == NfcDeviceSaveFormatMifareDesfire) { - scene_manager_next_scene(nfc->scene_manager, NfcSceneMifareDesfireData); + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfDesfireData); consumed = true; } } else if(state == NfcSceneDeviceInfoData && event.event == NfcCustomEventViewExit) { diff --git a/applications/nfc/scenes/nfc_scene_dict_not_found.c b/applications/nfc/scenes/nfc_scene_dict_not_found.c index 19db68d83..dc21b08b1 100644 --- a/applications/nfc/scenes/nfc_scene_dict_not_found.c +++ b/applications/nfc/scenes/nfc_scene_dict_not_found.c @@ -31,12 +31,9 @@ bool nfc_scene_dict_not_found_on_event(void* context, SceneManagerEvent event) { if(event.type == SceneManagerEventTypeCustom) { if(event.event == NfcCustomEventViewExit) { - if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneScriptsMenu)) { + if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneExtraActions)) { consumed = scene_manager_search_and_switch_to_previous_scene( - nfc->scene_manager, NfcSceneScriptsMenu); - } else if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneCardMenu)) { - consumed = scene_manager_search_and_switch_to_previous_scene( - nfc->scene_manager, NfcSceneCardMenu); + nfc->scene_manager, NfcSceneExtraActions); } else { consumed = scene_manager_search_and_switch_to_previous_scene( nfc->scene_manager, NfcSceneStart); diff --git a/applications/nfc/scenes/nfc_scene_emulate_uid.c b/applications/nfc/scenes/nfc_scene_emulate_uid.c index 3fabf4959..0d92c9f04 100755 --- a/applications/nfc/scenes/nfc_scene_emulate_uid.c +++ b/applications/nfc/scenes/nfc_scene_emulate_uid.c @@ -8,11 +8,12 @@ enum { NfcSceneEmulateUidStateTextBox, }; -void nfc_emulate_uid_worker_callback(NfcWorkerEvent event, void* context) { +bool nfc_emulate_uid_worker_callback(NfcWorkerEvent event, void* context) { UNUSED(event); furi_assert(context); Nfc* nfc = context; view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventWorkerExit); + return true; } void nfc_scene_emulate_uid_widget_callback(GuiButtonType result, InputType type, void* context) { @@ -76,7 +77,7 @@ void nfc_scene_emulate_uid_on_enter(void* context) { memset(&nfc->dev->dev_data.reader_data, 0, sizeof(NfcReaderRequestData)); nfc_worker_start( nfc->worker, - NfcWorkerStateEmulate, + NfcWorkerStateUidEmulate, &nfc->dev->dev_data, nfc_emulate_uid_worker_callback, nfc); @@ -90,9 +91,7 @@ bool nfc_scene_emulate_uid_on_event(void* context, SceneManagerEvent event) { uint32_t state = scene_manager_get_scene_state(nfc->scene_manager, NfcSceneEmulateUid); bool consumed = false; - if(event.type == SceneManagerEventTypeTick) { - consumed = true; - } else if(event.type == SceneManagerEventTypeCustom) { + if(event.type == SceneManagerEventTypeCustom) { if(event.event == NfcCustomEventWorkerExit) { // Add data button to widget if data is received for the first time if(!string_size(nfc->text_box_store)) { diff --git a/applications/nfc/scenes/nfc_scene_read_emv_data_success.c b/applications/nfc/scenes/nfc_scene_emv_read_success.c old mode 100755 new mode 100644 similarity index 78% rename from applications/nfc/scenes/nfc_scene_read_emv_data_success.c rename to applications/nfc/scenes/nfc_scene_emv_read_success.c index 4b3be9e18..eefe560e3 --- a/applications/nfc/scenes/nfc_scene_read_emv_data_success.c +++ b/applications/nfc/scenes/nfc_scene_emv_read_success.c @@ -2,7 +2,7 @@ #include "../helpers/nfc_emv_parser.h" #include -void nfc_scene_read_emv_data_success_widget_callback( +void nfc_scene_emv_read_success_widget_callback( GuiButtonType result, InputType type, void* context) { @@ -12,7 +12,7 @@ void nfc_scene_read_emv_data_success_widget_callback( } } -void nfc_scene_read_emv_data_success_on_enter(void* context) { +void nfc_scene_emv_read_success_on_enter(void* context) { Nfc* nfc = context; EmvData* emv_data = &nfc->dev->dev_data.emv_data; FuriHalNfcDevData* nfc_data = &nfc->dev->dev_data.nfc_data; @@ -23,17 +23,9 @@ void nfc_scene_read_emv_data_success_on_enter(void* context) { widget_add_frame_element(nfc->widget, 0, 0, 128, 64, 6); // Add buttons widget_add_button_element( - nfc->widget, - GuiButtonTypeLeft, - "Back", - nfc_scene_read_emv_data_success_widget_callback, - nfc); + nfc->widget, GuiButtonTypeLeft, "Retry", nfc_scene_emv_read_success_widget_callback, nfc); widget_add_button_element( - nfc->widget, - GuiButtonTypeRight, - "Save", - nfc_scene_read_emv_data_success_widget_callback, - nfc); + nfc->widget, GuiButtonTypeRight, "Save", nfc_scene_emv_read_success_widget_callback, nfc); // Add card name widget_add_string_element( nfc->widget, 64, 3, AlignCenter, AlignTop, FontSecondary, nfc->dev->dev_data.emv_data.name); @@ -103,25 +95,17 @@ void nfc_scene_read_emv_data_success_on_enter(void* context) { widget_add_string_element(nfc->widget, 7, 32, AlignLeft, AlignTop, FontSecondary, exp_str); } - // Send notification - if(scene_manager_get_scene_state(nfc->scene_manager, NfcSceneReadEmvDataSuccess) == - NFC_SEND_NOTIFICATION_TRUE) { - notification_message(nfc->notifications, &sequence_success); - scene_manager_set_scene_state( - nfc->scene_manager, NfcSceneReadEmvDataSuccess, NFC_SEND_NOTIFICATION_FALSE); - } - view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); } -bool nfc_scene_read_emv_data_success_on_event(void* context, SceneManagerEvent event) { +bool nfc_scene_emv_read_success_on_event(void* context, SceneManagerEvent event) { Nfc* nfc = context; bool consumed = false; if(event.type == SceneManagerEventTypeCustom) { if(event.event == GuiButtonTypeLeft) { - consumed = scene_manager_search_and_switch_to_previous_scene( - nfc->scene_manager, NfcSceneReadEmvAppSuccess); + scene_manager_next_scene(nfc->scene_manager, NfcSceneRetryConfirm); + consumed = true; } else if(event.event == GuiButtonTypeRight) { // Clear device name nfc_device_set_name(nfc->dev, ""); @@ -130,13 +114,13 @@ bool nfc_scene_read_emv_data_success_on_event(void* context, SceneManagerEvent e consumed = true; } } else if(event.type == SceneManagerEventTypeBack) { - consumed = scene_manager_search_and_switch_to_previous_scene( - nfc->scene_manager, NfcSceneReadEmvAppSuccess); + scene_manager_next_scene(nfc->scene_manager, NfcSceneExitConfirm); + consumed = true; } return consumed; } -void nfc_scene_read_emv_data_success_on_exit(void* context) { +void nfc_scene_emv_read_success_on_exit(void* context) { Nfc* nfc = context; // Clear view diff --git a/applications/nfc/scenes/nfc_scene_exit_confirm.c b/applications/nfc/scenes/nfc_scene_exit_confirm.c new file mode 100644 index 000000000..b22dd42a1 --- /dev/null +++ b/applications/nfc/scenes/nfc_scene_exit_confirm.c @@ -0,0 +1,47 @@ +#include "../nfc_i.h" + +void nfc_scene_exit_confirm_dialog_callback(DialogExResult result, void* context) { + Nfc* nfc = context; + + view_dispatcher_send_custom_event(nfc->view_dispatcher, result); +} + +void nfc_scene_exit_confirm_on_enter(void* context) { + Nfc* nfc = context; + DialogEx* dialog_ex = nfc->dialog_ex; + + dialog_ex_set_left_button_text(dialog_ex, "Exit"); + 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_text( + dialog_ex, "All unsaved data\nwill be lost!", 64, 25, AlignCenter, AlignTop); + dialog_ex_set_context(dialog_ex, nfc); + dialog_ex_set_result_callback(dialog_ex, nfc_scene_exit_confirm_dialog_callback); + + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewDialogEx); +} + +bool nfc_scene_exit_confirm_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == DialogExResultRight) { + consumed = scene_manager_previous_scene(nfc->scene_manager); + } else if(event.event == DialogExResultLeft) { + consumed = scene_manager_search_and_switch_to_previous_scene( + nfc->scene_manager, NfcSceneStart); + } + } else if(event.type == SceneManagerEventTypeBack) { + consumed = true; + } + + return consumed; +} + +void nfc_scene_exit_confirm_on_exit(void* context) { + Nfc* nfc = context; + + // Clean view + dialog_ex_reset(nfc->dialog_ex); +} diff --git a/applications/nfc/scenes/nfc_scene_extra_actions.c b/applications/nfc/scenes/nfc_scene_extra_actions.c new file mode 100644 index 000000000..43e49e5a0 --- /dev/null +++ b/applications/nfc/scenes/nfc_scene_extra_actions.c @@ -0,0 +1,57 @@ +#include "../nfc_i.h" + +enum SubmenuIndex { + SubmenuIndexMfClassicKeys, + SubmenuIndexMfUltralightUnlock, +}; + +void nfc_scene_extra_actions_submenu_callback(void* context, uint32_t index) { + Nfc* nfc = context; + + view_dispatcher_send_custom_event(nfc->view_dispatcher, index); +} + +void nfc_scene_extra_actions_on_enter(void* context) { + Nfc* nfc = context; + Submenu* submenu = nfc->submenu; + + submenu_add_item( + submenu, + "Mf Classic Keys", + SubmenuIndexMfClassicKeys, + nfc_scene_extra_actions_submenu_callback, + nfc); + submenu_add_item( + submenu, + "Unlock NTAG/Ultralight", + SubmenuIndexMfUltralightUnlock, + nfc_scene_extra_actions_submenu_callback, + nfc); + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu); +} + +bool nfc_scene_extra_actions_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == SubmenuIndexMfClassicKeys) { + if(mf_classic_dict_check_presence(MfClassicDictTypeFlipper)) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicKeys); + } else { + scene_manager_next_scene(nfc->scene_manager, NfcSceneDictNotFound); + } + consumed = true; + } else if(event.event == SubmenuIndexMfUltralightUnlock) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightUnlockMenu); + } + scene_manager_set_scene_state(nfc->scene_manager, NfcSceneExtraActions, event.event); + } + return consumed; +} + +void nfc_scene_extra_actions_on_exit(void* context) { + Nfc* nfc = context; + + submenu_reset(nfc->submenu); +} diff --git a/applications/nfc/scenes/nfc_scene_mf_classic_dict_attack.c b/applications/nfc/scenes/nfc_scene_mf_classic_dict_attack.c new file mode 100644 index 000000000..d821c182d --- /dev/null +++ b/applications/nfc/scenes/nfc_scene_mf_classic_dict_attack.c @@ -0,0 +1,170 @@ +#include "../nfc_i.h" + +#define TAG "NfcMfClassicDictAttack" + +typedef enum { + DictAttackStateIdle, + DictAttackStateUserDictInProgress, + DictAttackStateFlipperDictInProgress, +} DictAttackState; + +bool nfc_dict_attack_worker_callback(NfcWorkerEvent event, void* context) { + furi_assert(context); + Nfc* nfc = context; + view_dispatcher_send_custom_event(nfc->view_dispatcher, event); + return true; +} + +void nfc_dict_attack_dict_attack_result_callback(void* context) { + furi_assert(context); + Nfc* nfc = context; + view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventDictAttackSkip); +} + +static void nfc_scene_mf_classic_dict_attack_update_view(Nfc* nfc) { + MfClassicData* data = &nfc->dev->dev_data.mf_classic_data; + uint8_t sectors_read = 0; + uint8_t keys_found = 0; + + // Calculate found keys and read sectors + mf_classic_get_read_sectors_and_keys(data, §ors_read, &keys_found); + dict_attack_set_keys_found(nfc->dict_attack, keys_found); + dict_attack_set_sector_read(nfc->dict_attack, sectors_read); +} + +static void nfc_scene_mf_classic_dict_attack_prepare_view(Nfc* nfc, DictAttackState state) { + MfClassicData* data = &nfc->dev->dev_data.mf_classic_data; + NfcMfClassicDictAttackData* dict_attack_data = &nfc->dev->dev_data.mf_classic_dict_attack_data; + NfcWorkerState worker_state = NfcWorkerStateReady; + MfClassicDict* dict = NULL; + + // Identify scene state + if(state == DictAttackStateIdle) { + if(mf_classic_dict_check_presence(MfClassicDictTypeUser)) { + state = DictAttackStateUserDictInProgress; + } else { + state = DictAttackStateFlipperDictInProgress; + } + } else if(state == DictAttackStateUserDictInProgress) { + state = DictAttackStateFlipperDictInProgress; + } + + // Setup view + if(state == DictAttackStateUserDictInProgress) { + worker_state = NfcWorkerStateMfClassicDictAttack; + dict_attack_set_header(nfc->dict_attack, "Mf Classic User Dict."); + dict = mf_classic_dict_alloc(MfClassicDictTypeUser); + + // If failed to load user dictionary - try flipper dictionary + if(!dict) { + FURI_LOG_E(TAG, "User dictionary not found"); + state = DictAttackStateFlipperDictInProgress; + } + } + if(state == DictAttackStateFlipperDictInProgress) { + worker_state = NfcWorkerStateMfClassicDictAttack; + dict_attack_set_header(nfc->dict_attack, "Mf Classic Flipper Dict."); + dict = mf_classic_dict_alloc(MfClassicDictTypeFlipper); + if(!dict) { + FURI_LOG_E(TAG, "Flipper dictionary not found"); + // Pass through to let worker handle the failure + } + } + // Free previous dictionary + if(dict_attack_data->dict) { + mf_classic_dict_free(dict_attack_data->dict); + } + dict_attack_data->dict = dict; + scene_manager_set_scene_state(nfc->scene_manager, NfcSceneMfClassicDictAttack, state); + dict_attack_set_callback(nfc->dict_attack, nfc_dict_attack_dict_attack_result_callback, nfc); + dict_attack_set_current_sector(nfc->dict_attack, 0); + dict_attack_set_card_detected(nfc->dict_attack, data->type); + dict_attack_set_total_dict_keys( + nfc->dict_attack, dict ? mf_classic_dict_get_total_keys(dict) : 0); + nfc_scene_mf_classic_dict_attack_update_view(nfc); + nfc_worker_start( + nfc->worker, worker_state, &nfc->dev->dev_data, nfc_dict_attack_worker_callback, nfc); +} + +void nfc_scene_mf_classic_dict_attack_on_enter(void* context) { + Nfc* nfc = context; + nfc_scene_mf_classic_dict_attack_prepare_view(nfc, DictAttackStateIdle); + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewDictAttack); + nfc_blink_start(nfc); +} + +bool nfc_scene_mf_classic_dict_attack_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + MfClassicData* data = &nfc->dev->dev_data.mf_classic_data; + bool consumed = false; + + uint32_t state = + scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMfClassicDictAttack); + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == NfcWorkerEventSuccess) { + if(state == DictAttackStateUserDictInProgress) { + nfc_worker_stop(nfc->worker); + nfc_scene_mf_classic_dict_attack_prepare_view(nfc, state); + consumed = true; + } else { + notification_message(nfc->notifications, &sequence_success); + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicReadSuccess); + consumed = true; + } + } else if(event.event == NfcWorkerEventAborted) { + if(state == DictAttackStateUserDictInProgress) { + nfc_scene_mf_classic_dict_attack_prepare_view(nfc, state); + consumed = true; + } else { + notification_message(nfc->notifications, &sequence_success); + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicReadSuccess); + consumed = true; + } + } else if(event.event == NfcWorkerEventCardDetected) { + dict_attack_set_card_detected(nfc->dict_attack, data->type); + consumed = true; + } else if(event.event == NfcWorkerEventNoCardDetected) { + dict_attack_set_card_removed(nfc->dict_attack); + consumed = true; + } else if(event.event == NfcWorkerEventFoundKeyA) { + dict_attack_inc_keys_found(nfc->dict_attack); + consumed = true; + } else if(event.event == NfcWorkerEventFoundKeyB) { + dict_attack_inc_keys_found(nfc->dict_attack); + consumed = true; + } else if(event.event == NfcWorkerEventNewSector) { + nfc_scene_mf_classic_dict_attack_update_view(nfc); + dict_attack_inc_current_sector(nfc->dict_attack); + consumed = true; + } else if(event.event == NfcWorkerEventNewDictKeyBatch) { + nfc_scene_mf_classic_dict_attack_update_view(nfc); + dict_attack_inc_current_dict_key(nfc->dict_attack, NFC_DICT_KEY_BATCH_SIZE); + consumed = true; + } else if(event.event == NfcCustomEventDictAttackSkip) { + if(state == DictAttackStateUserDictInProgress) { + nfc_worker_stop(nfc->worker); + consumed = true; + } else if(state == DictAttackStateFlipperDictInProgress) { + nfc_worker_stop(nfc->worker); + consumed = true; + } + } + } else if(event.type == SceneManagerEventTypeBack) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneExitConfirm); + consumed = true; + } + return consumed; +} + +void nfc_scene_mf_classic_dict_attack_on_exit(void* context) { + Nfc* nfc = context; + NfcMfClassicDictAttackData* dict_attack_data = &nfc->dev->dev_data.mf_classic_dict_attack_data; + // Stop worker + nfc_worker_stop(nfc->worker); + if(dict_attack_data->dict) { + mf_classic_dict_free(dict_attack_data->dict); + dict_attack_data->dict = NULL; + } + dict_attack_reset(nfc->dict_attack); + nfc_blink_stop(nfc); +} diff --git a/applications/nfc/scenes/nfc_scene_emulate_mifare_classic.c b/applications/nfc/scenes/nfc_scene_mf_classic_emulate.c similarity index 57% rename from applications/nfc/scenes/nfc_scene_emulate_mifare_classic.c rename to applications/nfc/scenes/nfc_scene_mf_classic_emulate.c index 97e865454..044388b8b 100644 --- a/applications/nfc/scenes/nfc_scene_emulate_mifare_classic.c +++ b/applications/nfc/scenes/nfc_scene_mf_classic_emulate.c @@ -4,51 +4,52 @@ #define NFC_MF_CLASSIC_DATA_NOT_CHANGED (0UL) #define NFC_MF_CLASSIC_DATA_CHANGED (1UL) -void nfc_emulate_mifare_classic_worker_callback(NfcWorkerEvent event, void* context) { +bool nfc_mf_classic_emulate_worker_callback(NfcWorkerEvent event, void* context) { UNUSED(event); Nfc* nfc = context; scene_manager_set_scene_state( - nfc->scene_manager, NfcSceneEmulateMifareClassic, NFC_MF_CLASSIC_DATA_CHANGED); + nfc->scene_manager, NfcSceneMfClassicEmulate, NFC_MF_CLASSIC_DATA_CHANGED); + return true; } -void nfc_scene_emulate_mifare_classic_on_enter(void* context) { +void nfc_scene_mf_classic_emulate_on_enter(void* context) { Nfc* nfc = context; DOLPHIN_DEED(DolphinDeedNfcEmulate); // Setup view Popup* popup = nfc->popup; if(strcmp(nfc->dev->dev_name, "")) { - nfc_text_store_set(nfc, "%s", nfc->dev->dev_name); + nfc_text_store_set(nfc, "Emulating\n%s", nfc->dev->dev_name); + } else { + nfc_text_store_set(nfc, "Emulating\nMf Classic", nfc->dev->dev_name); } popup_set_icon(popup, 0, 3, &I_RFIDDolphinSend_97x61); - popup_set_header(popup, "Emulating\nMf Classic", 56, 31, AlignLeft, AlignTop); + popup_set_header(popup, nfc->text_store, 56, 31, AlignLeft, AlignTop); // Setup and start worker view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup); nfc_worker_start( nfc->worker, - NfcWorkerStateEmulateMifareClassic, + NfcWorkerStateMfClassicEmulate, &nfc->dev->dev_data, - nfc_emulate_mifare_classic_worker_callback, + nfc_mf_classic_emulate_worker_callback, nfc); nfc_blink_start(nfc); } -bool nfc_scene_emulate_mifare_classic_on_event(void* context, SceneManagerEvent event) { +bool nfc_scene_mf_classic_emulate_on_event(void* context, SceneManagerEvent event) { Nfc* nfc = context; bool consumed = false; - if(event.type == SceneManagerEventTypeTick) { - consumed = true; - } else if(event.type == SceneManagerEventTypeBack) { + if(event.type == SceneManagerEventTypeBack) { // Stop worker nfc_worker_stop(nfc->worker); // Check if data changed and save in shadow file - if(scene_manager_get_scene_state(nfc->scene_manager, NfcSceneEmulateMifareClassic) == + if(scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMfClassicEmulate) == NFC_MF_CLASSIC_DATA_CHANGED) { scene_manager_set_scene_state( - nfc->scene_manager, NfcSceneEmulateMifareClassic, NFC_MF_CLASSIC_DATA_NOT_CHANGED); + nfc->scene_manager, NfcSceneMfClassicEmulate, NFC_MF_CLASSIC_DATA_NOT_CHANGED); nfc_device_save_shadow(nfc->dev, nfc->dev->dev_name); } consumed = false; @@ -56,7 +57,7 @@ bool nfc_scene_emulate_mifare_classic_on_event(void* context, SceneManagerEvent return consumed; } -void nfc_scene_emulate_mifare_classic_on_exit(void* context) { +void nfc_scene_mf_classic_emulate_on_exit(void* context) { Nfc* nfc = context; // Clear view diff --git a/applications/nfc/scenes/nfc_scene_mf_classic_info.c b/applications/nfc/scenes/nfc_scene_mf_classic_info.c new file mode 100644 index 000000000..b658dfa48 --- /dev/null +++ b/applications/nfc/scenes/nfc_scene_mf_classic_info.c @@ -0,0 +1,72 @@ +#include "../nfc_i.h" + +void nfc_scene_mf_classic_info_widget_callback(GuiButtonType result, InputType type, void* context) { + furi_assert(context); + Nfc* nfc = context; + + if(type == InputTypeShort) { + view_dispatcher_send_custom_event(nfc->view_dispatcher, result); + } +} + +void nfc_scene_mf_classic_info_on_enter(void* context) { + Nfc* nfc = context; + NfcDeviceData* dev_data = &nfc->dev->dev_data; + MfClassicData* mf_data = &dev_data->mf_classic_data; + string_t str_tmp; + string_init(str_tmp); + + // Setup view + Widget* widget = nfc->widget; + + widget_add_string_element( + widget, 0, 0, AlignLeft, AlignTop, FontSecondary, mf_classic_get_type_str(mf_data->type)); + widget_add_string_element( + widget, 0, 11, AlignLeft, AlignTop, FontSecondary, "ISO 14443-3 (Type A)"); + string_printf(str_tmp, "UID:"); + for(size_t i = 0; i < dev_data->nfc_data.uid_len; i++) { + string_cat_printf(str_tmp, " %02X", dev_data->nfc_data.uid[i]); + } + widget_add_string_element( + widget, 0, 22, AlignLeft, AlignTop, FontSecondary, string_get_cstr(str_tmp)); + string_printf( + str_tmp, + "ATQA: %02X %02X SAK: %02X", + dev_data->nfc_data.atqa[0], + dev_data->nfc_data.atqa[1], + dev_data->nfc_data.sak); + widget_add_string_element( + widget, 0, 33, AlignLeft, AlignTop, FontSecondary, string_get_cstr(str_tmp)); + uint8_t sectors_total = mf_classic_get_total_sectors_num(mf_data->type); + uint8_t keys_total = sectors_total * 2; + uint8_t keys_found = 0; + uint8_t sectors_read = 0; + mf_classic_get_read_sectors_and_keys(mf_data, §ors_read, &keys_found); + string_printf(str_tmp, "Keys Found: %d/%d", keys_found, keys_total); + widget_add_string_element( + widget, 0, 44, AlignLeft, AlignTop, FontSecondary, string_get_cstr(str_tmp)); + string_printf(str_tmp, "Sectors Read: %d/%d", sectors_read, sectors_total); + widget_add_string_element( + widget, 0, 55, AlignLeft, AlignTop, FontSecondary, string_get_cstr(str_tmp)); + + string_clear(str_tmp); + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); +} + +bool nfc_scene_mf_classic_info_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeBack) { + consumed = scene_manager_previous_scene(nfc->scene_manager); + } + + return consumed; +} + +void nfc_scene_mf_classic_info_on_exit(void* context) { + Nfc* nfc = context; + + // Clear view + widget_reset(nfc->widget); +} diff --git a/applications/nfc/scenes/nfc_scene_mf_classic_keys.c b/applications/nfc/scenes/nfc_scene_mf_classic_keys.c new file mode 100644 index 000000000..0faa73673 --- /dev/null +++ b/applications/nfc/scenes/nfc_scene_mf_classic_keys.c @@ -0,0 +1,59 @@ +#include "../nfc_i.h" + +void nfc_scene_mf_classic_keys_widget_callback(GuiButtonType result, InputType type, void* context) { + Nfc* nfc = context; + if(type == InputTypeShort) { + view_dispatcher_send_custom_event(nfc->view_dispatcher, result); + } +} + +void nfc_scene_mf_classic_keys_on_enter(void* context) { + Nfc* nfc = context; + + // Load flipper dict keys total + uint32_t flipper_dict_keys_total = 0; + MfClassicDict* dict = mf_classic_dict_alloc(MfClassicDictTypeFlipper); + if(dict) { + flipper_dict_keys_total = mf_classic_dict_get_total_keys(dict); + mf_classic_dict_free(dict); + } + // Load user dict keys total + uint32_t user_dict_keys_total = 0; + dict = mf_classic_dict_alloc(MfClassicDictTypeUser); + if(dict) { + user_dict_keys_total = mf_classic_dict_get_total_keys(dict); + mf_classic_dict_free(dict); + } + + widget_add_string_element( + nfc->widget, 0, 0, AlignLeft, AlignTop, FontPrimary, "MF Classic Keys"); + char temp_str[32]; + snprintf(temp_str, sizeof(temp_str), "Flipper dict: %ld", flipper_dict_keys_total); + widget_add_string_element(nfc->widget, 0, 20, AlignLeft, AlignTop, FontSecondary, temp_str); + snprintf(temp_str, sizeof(temp_str), "User dict: %ld", user_dict_keys_total); + widget_add_string_element(nfc->widget, 0, 32, AlignLeft, AlignTop, FontSecondary, temp_str); + widget_add_button_element( + nfc->widget, GuiButtonTypeCenter, "Add", nfc_scene_mf_classic_keys_widget_callback, nfc); + + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); +} + +bool nfc_scene_mf_classic_keys_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == GuiButtonTypeCenter) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicKeysAdd); + consumed = true; + } + } + + return consumed; +} + +void nfc_scene_mf_classic_keys_on_exit(void* context) { + Nfc* nfc = context; + + widget_reset(nfc->widget); +} diff --git a/applications/nfc/scenes/nfc_scene_mf_classic_keys_add.c b/applications/nfc/scenes/nfc_scene_mf_classic_keys_add.c new file mode 100644 index 000000000..9f56b0f45 --- /dev/null +++ b/applications/nfc/scenes/nfc_scene_mf_classic_keys_add.c @@ -0,0 +1,57 @@ +#include "../nfc_i.h" + +void nfc_scene_mf_classic_keys_add_byte_input_callback(void* context) { + Nfc* nfc = context; + + view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventByteInputDone); +} + +void nfc_scene_mf_classic_keys_add_on_enter(void* context) { + Nfc* nfc = context; + + // Setup view + ByteInput* byte_input = nfc->byte_input; + byte_input_set_header_text(byte_input, "Enter the key in hex"); + byte_input_set_result_callback( + byte_input, + nfc_scene_mf_classic_keys_add_byte_input_callback, + NULL, + nfc, + nfc->byte_input_store, + 6); + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewByteInput); +} + +bool nfc_scene_mf_classic_keys_add_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == NfcCustomEventByteInputDone) { + // Add key to dict + bool key_added = false; + MfClassicDict* dict = mf_classic_dict_alloc(MfClassicDictTypeUser); + if(dict) { + if(mf_classic_dict_add_key(dict, nfc->byte_input_store)) { + key_added = true; + } + } + if(key_added) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveSuccess); + } else { + scene_manager_next_scene(nfc->scene_manager, NfcSceneDictNotFound); + } + mf_classic_dict_free(dict); + consumed = true; + } + } + return consumed; +} + +void nfc_scene_mf_classic_keys_add_on_exit(void* context) { + Nfc* nfc = context; + + // Clear view + byte_input_set_result_callback(nfc->byte_input, NULL, NULL, NULL, NULL, 0); + byte_input_set_header_text(nfc->byte_input, ""); +} diff --git a/applications/nfc/scenes/nfc_scene_mifare_classic_menu.c b/applications/nfc/scenes/nfc_scene_mf_classic_menu.c similarity index 52% rename from applications/nfc/scenes/nfc_scene_mifare_classic_menu.c rename to applications/nfc/scenes/nfc_scene_mf_classic_menu.c index 4611d16b7..6ee0ad868 100644 --- a/applications/nfc/scenes/nfc_scene_mifare_classic_menu.c +++ b/applications/nfc/scenes/nfc_scene_mf_classic_menu.c @@ -3,40 +3,40 @@ enum SubmenuIndex { SubmenuIndexSave, SubmenuIndexEmulate, + SubmenuIndexInfo, }; -void nfc_scene_mifare_classic_menu_submenu_callback(void* context, uint32_t index) { +void nfc_scene_mf_classic_menu_submenu_callback(void* context, uint32_t index) { Nfc* nfc = context; view_dispatcher_send_custom_event(nfc->view_dispatcher, index); } -void nfc_scene_mifare_classic_menu_on_enter(void* context) { +void nfc_scene_mf_classic_menu_on_enter(void* context) { Nfc* nfc = context; Submenu* submenu = nfc->submenu; submenu_add_item( - submenu, "Save", SubmenuIndexSave, nfc_scene_mifare_classic_menu_submenu_callback, nfc); + submenu, "Save", SubmenuIndexSave, nfc_scene_mf_classic_menu_submenu_callback, nfc); submenu_add_item( - submenu, - "Emulate", - SubmenuIndexEmulate, - nfc_scene_mifare_classic_menu_submenu_callback, - nfc); + submenu, "Emulate", SubmenuIndexEmulate, nfc_scene_mf_classic_menu_submenu_callback, nfc); + submenu_add_item( + submenu, "Info", SubmenuIndexInfo, nfc_scene_mf_classic_menu_submenu_callback, nfc); + submenu_set_selected_item( - nfc->submenu, scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMifareUlMenu)); + nfc->submenu, scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMfClassicMenu)); view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu); } -bool nfc_scene_mifare_classic_menu_on_event(void* context, SceneManagerEvent event) { +bool nfc_scene_mf_classic_menu_on_event(void* context, SceneManagerEvent event) { Nfc* nfc = context; bool consumed = false; if(event.type == SceneManagerEventTypeCustom) { if(event.event == SubmenuIndexSave) { scene_manager_set_scene_state( - nfc->scene_manager, NfcSceneMifareUlMenu, SubmenuIndexSave); + nfc->scene_manager, NfcSceneMfClassicMenu, SubmenuIndexSave); nfc->dev->format = NfcDeviceSaveFormatMifareClassic; // Clear device name nfc_device_set_name(nfc->dev, ""); @@ -44,19 +44,23 @@ bool nfc_scene_mifare_classic_menu_on_event(void* context, SceneManagerEvent eve consumed = true; } else if(event.event == SubmenuIndexEmulate) { scene_manager_set_scene_state( - nfc->scene_manager, NfcSceneMifareUlMenu, SubmenuIndexEmulate); - scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateMifareClassic); + nfc->scene_manager, NfcSceneMfClassicMenu, SubmenuIndexEmulate); + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicEmulate); + consumed = true; + } else if(event.event == SubmenuIndexInfo) { + scene_manager_set_scene_state( + nfc->scene_manager, NfcSceneMfClassicMenu, SubmenuIndexInfo); + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicInfo); consumed = true; } } else if(event.type == SceneManagerEventTypeBack) { - consumed = - scene_manager_search_and_switch_to_previous_scene(nfc->scene_manager, NfcSceneStart); + consumed = scene_manager_previous_scene(nfc->scene_manager); } return consumed; } -void nfc_scene_mifare_classic_menu_on_exit(void* context) { +void nfc_scene_mf_classic_menu_on_exit(void* context) { Nfc* nfc = context; // Clear view diff --git a/applications/nfc/scenes/nfc_scene_mf_classic_read_success.c b/applications/nfc/scenes/nfc_scene_mf_classic_read_success.c new file mode 100644 index 000000000..bd782305c --- /dev/null +++ b/applications/nfc/scenes/nfc_scene_mf_classic_read_success.c @@ -0,0 +1,104 @@ +#include "../nfc_i.h" +#include + +void nfc_scene_mf_classic_read_success_widget_callback( + GuiButtonType result, + InputType type, + void* context) { + furi_assert(context); + Nfc* nfc = context; + + if(type == InputTypeShort) { + view_dispatcher_send_custom_event(nfc->view_dispatcher, result); + } +} + +void nfc_scene_mf_classic_read_success_on_enter(void* context) { + Nfc* nfc = context; + NfcDeviceData* dev_data = &nfc->dev->dev_data; + MfClassicData* mf_data = &dev_data->mf_classic_data; + string_t str_tmp; + string_init(str_tmp); + + DOLPHIN_DEED(DolphinDeedNfcReadSuccess); + + // Setup view + Widget* widget = nfc->widget; + widget_add_button_element( + widget, GuiButtonTypeLeft, "Retry", nfc_scene_mf_classic_read_success_widget_callback, nfc); + widget_add_button_element( + widget, GuiButtonTypeRight, "More", nfc_scene_mf_classic_read_success_widget_callback, nfc); + + if(string_size(nfc->dev->dev_data.parsed_data)) { + widget_add_text_box_element( + nfc->widget, + 0, + 0, + 128, + 32, + AlignLeft, + AlignTop, + string_get_cstr(nfc->dev->dev_data.parsed_data), + true); + } else { + widget_add_string_element( + widget, + 0, + 0, + AlignLeft, + AlignTop, + FontSecondary, + mf_classic_get_type_str(mf_data->type)); + widget_add_string_element( + widget, 0, 11, AlignLeft, AlignTop, FontSecondary, "ISO 14443-3 (Type A)"); + string_printf(str_tmp, "UID:"); + for(size_t i = 0; i < dev_data->nfc_data.uid_len; i++) { + string_cat_printf(str_tmp, " %02X", dev_data->nfc_data.uid[i]); + } + widget_add_string_element( + widget, 0, 22, AlignLeft, AlignTop, FontSecondary, string_get_cstr(str_tmp)); + uint8_t sectors_total = mf_classic_get_total_sectors_num(mf_data->type); + uint8_t keys_total = sectors_total * 2; + uint8_t keys_found = 0; + uint8_t sectors_read = 0; + mf_classic_get_read_sectors_and_keys(mf_data, §ors_read, &keys_found); + string_printf(str_tmp, "Keys Found: %d/%d", keys_found, keys_total); + widget_add_string_element( + widget, 0, 33, AlignLeft, AlignTop, FontSecondary, string_get_cstr(str_tmp)); + string_printf(str_tmp, "Sectors Read: %d/%d", sectors_read, sectors_total); + widget_add_string_element( + widget, 0, 44, AlignLeft, AlignTop, FontSecondary, string_get_cstr(str_tmp)); + } + + string_clear(str_tmp); + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); +} + +bool nfc_scene_mf_classic_read_success_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == GuiButtonTypeLeft) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneRetryConfirm); + consumed = true; + } else if(event.event == GuiButtonTypeRight) { + // Clear device name + nfc_device_set_name(nfc->dev, ""); + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicMenu); + consumed = true; + } + } else if(event.type == SceneManagerEventTypeBack) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneExitConfirm); + consumed = true; + } + + return consumed; +} + +void nfc_scene_mf_classic_read_success_on_exit(void* context) { + Nfc* nfc = context; + + // Clear view + widget_reset(nfc->widget); +} diff --git a/applications/nfc/scenes/nfc_scene_mifare_desfire_app.c b/applications/nfc/scenes/nfc_scene_mf_desfire_app.c similarity index 73% rename from applications/nfc/scenes/nfc_scene_mifare_desfire_app.c rename to applications/nfc/scenes/nfc_scene_mf_desfire_app.c index fdfbd2ecb..7faafdcf0 100644 --- a/applications/nfc/scenes/nfc_scene_mifare_desfire_app.c +++ b/applications/nfc/scenes/nfc_scene_mf_desfire_app.c @@ -1,15 +1,15 @@ #include "../nfc_i.h" -#define TAG "NfcSceneMifareDesfireApp" +#define TAG "NfcSceneMfDesfireApp" enum SubmenuIndex { SubmenuIndexAppInfo, SubmenuIndexDynamic, // dynamic indexes start here }; -MifareDesfireApplication* nfc_scene_mifare_desfire_app_get_app(Nfc* nfc) { - uint32_t app_idx = - scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMifareDesfireApp) >> 1; +MifareDesfireApplication* nfc_scene_mf_desfire_app_get_app(Nfc* nfc) { + uint32_t app_idx = scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMfDesfireApp) >> + 1; MifareDesfireApplication* app = nfc->dev->dev_data.mf_df_data.app_head; for(uint32_t i = 0; i < app_idx && app; i++) { app = app->next; @@ -17,16 +17,16 @@ MifareDesfireApplication* nfc_scene_mifare_desfire_app_get_app(Nfc* nfc) { return app; } -void nfc_scene_mifare_desfire_app_submenu_callback(void* context, uint32_t index) { +void nfc_scene_mf_desfire_app_submenu_callback(void* context, uint32_t index) { Nfc* nfc = context; view_dispatcher_send_custom_event(nfc->view_dispatcher, index); } -void nfc_scene_mifare_desfire_app_on_enter(void* context) { +void nfc_scene_mf_desfire_app_on_enter(void* context) { Nfc* nfc = context; Submenu* submenu = nfc->submenu; - MifareDesfireApplication* app = nfc_scene_mifare_desfire_app_get_app(nfc); + MifareDesfireApplication* app = nfc_scene_mf_desfire_app_get_app(nfc); if(!app) { popup_set_icon(nfc->popup, 5, 5, &I_WarningDolphin_45x42); popup_set_header(nfc->popup, "Internal Error!", 55, 12, AlignLeft, AlignBottom); @@ -45,11 +45,7 @@ void nfc_scene_mifare_desfire_app_on_enter(void* context) { text_box_set_font(nfc->text_box, TextBoxFontHex); submenu_add_item( - submenu, - "App info", - SubmenuIndexAppInfo, - nfc_scene_mifare_desfire_app_submenu_callback, - nfc); + submenu, "App info", SubmenuIndexAppInfo, nfc_scene_mf_desfire_app_submenu_callback, nfc); uint16_t cap = NFC_TEXT_STORE_SIZE; char* buf = nfc->text_store; @@ -65,20 +61,19 @@ void nfc_scene_mifare_desfire_app_on_enter(void* context) { char* label = buf; cap -= size + 1; buf += size + 1; - submenu_add_item( - submenu, label, idx++, nfc_scene_mifare_desfire_app_submenu_callback, nfc); + submenu_add_item(submenu, label, idx++, nfc_scene_mf_desfire_app_submenu_callback, nfc); } view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu); } -bool nfc_scene_mifare_desfire_app_on_event(void* context, SceneManagerEvent event) { +bool nfc_scene_mf_desfire_app_on_event(void* context, SceneManagerEvent event) { Nfc* nfc = context; bool consumed = false; - uint32_t state = scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMifareDesfireApp); + uint32_t state = scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMfDesfireApp); if(event.type == SceneManagerEventTypeCustom) { - MifareDesfireApplication* app = nfc_scene_mifare_desfire_app_get_app(nfc); + MifareDesfireApplication* app = nfc_scene_mf_desfire_app_get_app(nfc); TextBox* text_box = nfc->text_box; string_reset(nfc->text_box_store); if(event.event == SubmenuIndexAppInfo) { @@ -95,14 +90,13 @@ bool nfc_scene_mifare_desfire_app_on_event(void* context, SceneManagerEvent even mf_df_cat_file(file, nfc->text_box_store); } text_box_set_text(text_box, string_get_cstr(nfc->text_box_store)); - scene_manager_set_scene_state(nfc->scene_manager, NfcSceneMifareDesfireApp, state | 1); + scene_manager_set_scene_state(nfc->scene_manager, NfcSceneMfDesfireApp, state | 1); view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewTextBox); consumed = true; } else if(event.type == SceneManagerEventTypeBack) { if(state & 1) { view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu); - scene_manager_set_scene_state( - nfc->scene_manager, NfcSceneMifareDesfireApp, state & ~1); + scene_manager_set_scene_state(nfc->scene_manager, NfcSceneMfDesfireApp, state & ~1); consumed = true; } } @@ -110,7 +104,7 @@ bool nfc_scene_mifare_desfire_app_on_event(void* context, SceneManagerEvent even return consumed; } -void nfc_scene_mifare_desfire_app_on_exit(void* context) { +void nfc_scene_mf_desfire_app_on_exit(void* context) { Nfc* nfc = context; // Clear views diff --git a/applications/nfc/scenes/nfc_scene_mifare_desfire_data.c b/applications/nfc/scenes/nfc_scene_mf_desfire_data.c similarity index 74% rename from applications/nfc/scenes/nfc_scene_mifare_desfire_data.c rename to applications/nfc/scenes/nfc_scene_mf_desfire_data.c index 3eb23fc27..0019a0846 100644 --- a/applications/nfc/scenes/nfc_scene_mifare_desfire_data.c +++ b/applications/nfc/scenes/nfc_scene_mf_desfire_data.c @@ -1,6 +1,6 @@ #include "../nfc_i.h" -#define TAG "NfcSceneMifareDesfireData" +#define TAG "NfcSceneMfDesfireData" enum { MifareDesfireDataStateMenu, @@ -12,16 +12,16 @@ enum SubmenuIndex { SubmenuIndexDynamic, // dynamic indexes start here }; -void nfc_scene_mifare_desfire_data_submenu_callback(void* context, uint32_t index) { +void nfc_scene_mf_desfire_data_submenu_callback(void* context, uint32_t index) { Nfc* nfc = (Nfc*)context; view_dispatcher_send_custom_event(nfc->view_dispatcher, index); } -void nfc_scene_mifare_desfire_data_on_enter(void* context) { +void nfc_scene_mf_desfire_data_on_enter(void* context) { Nfc* nfc = context; Submenu* submenu = nfc->submenu; - uint32_t state = scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMifareDesfireData); + uint32_t state = scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMfDesfireData); MifareDesfireData* data = &nfc->dev->dev_data.mf_df_data; text_box_set_font(nfc->text_box, TextBoxFontHex); @@ -30,7 +30,7 @@ void nfc_scene_mifare_desfire_data_on_enter(void* context) { submenu, "Card info", SubmenuIndexCardInfo, - nfc_scene_mifare_desfire_data_submenu_callback, + nfc_scene_mf_desfire_data_submenu_callback, nfc); uint16_t cap = NFC_TEXT_STORE_SIZE; @@ -46,24 +46,23 @@ void nfc_scene_mifare_desfire_data_on_enter(void* context) { char* label = buf; cap -= size + 1; buf += size + 1; - submenu_add_item( - submenu, label, idx++, nfc_scene_mifare_desfire_data_submenu_callback, nfc); + submenu_add_item(submenu, label, idx++, nfc_scene_mf_desfire_data_submenu_callback, nfc); } if(state >= MifareDesfireDataStateItem) { submenu_set_selected_item( nfc->submenu, state - MifareDesfireDataStateItem + SubmenuIndexDynamic); scene_manager_set_scene_state( - nfc->scene_manager, NfcSceneMifareDesfireData, MifareDesfireDataStateMenu); + nfc->scene_manager, NfcSceneMfDesfireData, MifareDesfireDataStateMenu); } view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu); } -bool nfc_scene_mifare_desfire_data_on_event(void* context, SceneManagerEvent event) { +bool nfc_scene_mf_desfire_data_on_event(void* context, SceneManagerEvent event) { Nfc* nfc = context; bool consumed = false; - uint32_t state = scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMifareDesfireData); + uint32_t state = scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMfDesfireData); MifareDesfireData* data = &nfc->dev->dev_data.mf_df_data; if(event.type == SceneManagerEventTypeCustom) { @@ -75,23 +74,22 @@ bool nfc_scene_mifare_desfire_data_on_event(void* context, SceneManagerEvent eve view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewTextBox); scene_manager_set_scene_state( nfc->scene_manager, - NfcSceneMifareDesfireData, + NfcSceneMfDesfireData, MifareDesfireDataStateItem + SubmenuIndexCardInfo); consumed = true; } else { uint16_t index = event.event - SubmenuIndexDynamic; scene_manager_set_scene_state( - nfc->scene_manager, NfcSceneMifareDesfireData, MifareDesfireDataStateItem + index); - scene_manager_set_scene_state( - nfc->scene_manager, NfcSceneMifareDesfireApp, index << 1); - scene_manager_next_scene(nfc->scene_manager, NfcSceneMifareDesfireApp); + nfc->scene_manager, NfcSceneMfDesfireData, MifareDesfireDataStateItem + index); + scene_manager_set_scene_state(nfc->scene_manager, NfcSceneMfDesfireApp, index << 1); + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfDesfireApp); consumed = true; } } else if(event.type == SceneManagerEventTypeBack) { if(state >= MifareDesfireDataStateItem) { view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu); scene_manager_set_scene_state( - nfc->scene_manager, NfcSceneMifareDesfireData, MifareDesfireDataStateMenu); + nfc->scene_manager, NfcSceneMfDesfireData, MifareDesfireDataStateMenu); consumed = true; } } @@ -99,7 +97,7 @@ bool nfc_scene_mifare_desfire_data_on_event(void* context, SceneManagerEvent eve return consumed; } -void nfc_scene_mifare_desfire_data_on_exit(void* context) { +void nfc_scene_mf_desfire_data_on_exit(void* context) { Nfc* nfc = context; // Clear views diff --git a/applications/nfc/scenes/nfc_scene_mifare_desfire_menu.c b/applications/nfc/scenes/nfc_scene_mf_desfire_menu.c similarity index 61% rename from applications/nfc/scenes/nfc_scene_mifare_desfire_menu.c rename to applications/nfc/scenes/nfc_scene_mf_desfire_menu.c index f6171c8b9..f15251143 100644 --- a/applications/nfc/scenes/nfc_scene_mifare_desfire_menu.c +++ b/applications/nfc/scenes/nfc_scene_mf_desfire_menu.c @@ -4,33 +4,32 @@ enum SubmenuIndex { SubmenuIndexSave, }; -void nfc_scene_mifare_desfire_menu_submenu_callback(void* context, uint32_t index) { +void nfc_scene_mf_desfire_menu_submenu_callback(void* context, uint32_t index) { Nfc* nfc = context; view_dispatcher_send_custom_event(nfc->view_dispatcher, index); } -void nfc_scene_mifare_desfire_menu_on_enter(void* context) { +void nfc_scene_mf_desfire_menu_on_enter(void* context) { Nfc* nfc = context; Submenu* submenu = nfc->submenu; submenu_add_item( - submenu, "Save", SubmenuIndexSave, nfc_scene_mifare_desfire_menu_submenu_callback, nfc); + submenu, "Save", SubmenuIndexSave, nfc_scene_mf_desfire_menu_submenu_callback, nfc); submenu_set_selected_item( - nfc->submenu, - scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMifareDesfireMenu)); + nfc->submenu, scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMfDesfireMenu)); view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu); } -bool nfc_scene_mifare_desfire_menu_on_event(void* context, SceneManagerEvent event) { +bool nfc_scene_mf_desfire_menu_on_event(void* context, SceneManagerEvent event) { Nfc* nfc = context; bool consumed = false; if(event.type == SceneManagerEventTypeCustom) { if(event.event == SubmenuIndexSave) { scene_manager_set_scene_state( - nfc->scene_manager, NfcSceneMifareDesfireMenu, SubmenuIndexSave); + nfc->scene_manager, NfcSceneMfDesfireMenu, SubmenuIndexSave); nfc->dev->format = NfcDeviceSaveFormatMifareDesfire; // Clear device name nfc_device_set_name(nfc->dev, ""); @@ -42,7 +41,7 @@ bool nfc_scene_mifare_desfire_menu_on_event(void* context, SceneManagerEvent eve return consumed; } -void nfc_scene_mifare_desfire_menu_on_exit(void* context) { +void nfc_scene_mf_desfire_menu_on_exit(void* context) { Nfc* nfc = context; // Clear view diff --git a/applications/nfc/scenes/nfc_scene_read_mifare_desfire_success.c b/applications/nfc/scenes/nfc_scene_mf_desfire_read_success.c similarity index 55% rename from applications/nfc/scenes/nfc_scene_read_mifare_desfire_success.c rename to applications/nfc/scenes/nfc_scene_mf_desfire_read_success.c index 17f62e411..a04f4e55c 100644 --- a/applications/nfc/scenes/nfc_scene_read_mifare_desfire_success.c +++ b/applications/nfc/scenes/nfc_scene_mf_desfire_read_success.c @@ -4,22 +4,22 @@ #define NFC_SCENE_READ_SUCCESS_SHIFT " " enum { - ReadMifareDesfireSuccessStateShowUID, - ReadMifareDesfireSuccessStateShowData, + MfDesfireReadSuccessStateShowUID, + MfDesfireReadSuccessStateShowData, }; -void nfc_scene_read_mifare_desfire_success_dialog_callback(DialogExResult result, void* context) { +void nfc_scene_mf_desfire_read_success_dialog_callback(DialogExResult result, void* context) { Nfc* nfc = context; view_dispatcher_send_custom_event(nfc->view_dispatcher, result); } -void nfc_scene_read_mifare_desfire_success_on_enter(void* context) { +void nfc_scene_mf_desfire_read_success_on_enter(void* context) { Nfc* nfc = context; MifareDesfireData* data = &nfc->dev->dev_data.mf_df_data; DialogEx* dialog_ex = nfc->dialog_ex; - dialog_ex_set_left_button_text(dialog_ex, "Back"); + dialog_ex_set_left_button_text(dialog_ex, "Retry"); dialog_ex_set_center_button_text(dialog_ex, "Data"); dialog_ex_set_right_button_text(dialog_ex, "More"); dialog_ex_set_icon(dialog_ex, 8, 16, &I_Medium_chip_22x21); @@ -34,18 +34,12 @@ void nfc_scene_read_mifare_desfire_success_on_enter(void* context) { } } + // TODO rework info view nfc_text_store_set( nfc, - "UID: %02X %02X %02X %02X %02X %02X %02X\n" NFC_SCENE_READ_SUCCESS_SHIFT - "%d%s bytes\n" NFC_SCENE_READ_SUCCESS_SHIFT "%d bytes free\n" - "%d application%s, %d file%s", - data->version.uid[0], - data->version.uid[1], - data->version.uid[2], - data->version.uid[3], - data->version.uid[4], - data->version.uid[5], - data->version.uid[6], + NFC_SCENE_READ_SUCCESS_SHIFT "Mifare DESFire\n" NFC_SCENE_READ_SUCCESS_SHIFT + "%d%s bytes\n" NFC_SCENE_READ_SUCCESS_SHIFT "%d bytes free\n" + "%d application%s, %d file%s", 1 << (data->version.sw_storage >> 1), (data->version.sw_storage & 1) ? "+" : "", data->free_memory ? data->free_memory->bytes : 0, @@ -55,41 +49,40 @@ void nfc_scene_read_mifare_desfire_success_on_enter(void* context) { n_files == 1 ? "" : "s"); dialog_ex_set_text(dialog_ex, nfc->text_store, 8, 6, AlignLeft, AlignTop); dialog_ex_set_context(dialog_ex, nfc); - dialog_ex_set_result_callback( - dialog_ex, nfc_scene_read_mifare_desfire_success_dialog_callback); + dialog_ex_set_result_callback(dialog_ex, nfc_scene_mf_desfire_read_success_dialog_callback); scene_manager_set_scene_state( - nfc->scene_manager, - NfcSceneReadMifareDesfireSuccess, - ReadMifareDesfireSuccessStateShowUID); + nfc->scene_manager, NfcSceneMfDesfireReadSuccess, MfDesfireReadSuccessStateShowUID); view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewDialogEx); } -bool nfc_scene_read_mifare_desfire_success_on_event(void* context, SceneManagerEvent event) { +bool nfc_scene_mf_desfire_read_success_on_event(void* context, SceneManagerEvent event) { Nfc* nfc = context; bool consumed = false; uint32_t state = - scene_manager_get_scene_state(nfc->scene_manager, NfcSceneReadMifareDesfireSuccess); + scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMfDesfireReadSuccess); if(event.type == SceneManagerEventTypeCustom) { - if(state == ReadMifareDesfireSuccessStateShowUID && event.event == DialogExResultLeft) { - scene_manager_previous_scene(nfc->scene_manager); + if(state == MfDesfireReadSuccessStateShowUID && event.event == DialogExResultLeft) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneRetryConfirm); consumed = true; - } else if( - state == ReadMifareDesfireSuccessStateShowUID && event.event == DialogExResultCenter) { - scene_manager_next_scene(nfc->scene_manager, NfcSceneMifareDesfireData); + } else if(state == MfDesfireReadSuccessStateShowUID && event.event == DialogExResultCenter) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfDesfireData); consumed = true; - } else if(state == ReadMifareDesfireSuccessStateShowUID && event.event == DialogExResultRight) { - scene_manager_next_scene(nfc->scene_manager, NfcSceneMifareDesfireMenu); + } else if(state == MfDesfireReadSuccessStateShowUID && event.event == DialogExResultRight) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfDesfireMenu); consumed = true; } } else if(event.type == SceneManagerEventTypeBack) { - if(state == ReadMifareDesfireSuccessStateShowData) { + if(state == MfDesfireReadSuccessStateShowData) { view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewDialogEx); scene_manager_set_scene_state( nfc->scene_manager, - NfcSceneReadMifareDesfireSuccess, - ReadMifareDesfireSuccessStateShowUID); + NfcSceneMfDesfireReadSuccess, + MfDesfireReadSuccessStateShowUID); + consumed = true; + } else { + scene_manager_next_scene(nfc->scene_manager, NfcSceneExitConfirm); consumed = true; } } @@ -97,7 +90,7 @@ bool nfc_scene_read_mifare_desfire_success_on_event(void* context, SceneManagerE return consumed; } -void nfc_scene_read_mifare_desfire_success_on_exit(void* context) { +void nfc_scene_mf_desfire_read_success_on_exit(void* context) { Nfc* nfc = context; // Clean dialog diff --git a/applications/nfc/scenes/nfc_scene_emulate_mifare_ul.c b/applications/nfc/scenes/nfc_scene_mf_ultralight_emulate.c similarity index 56% rename from applications/nfc/scenes/nfc_scene_emulate_mifare_ul.c rename to applications/nfc/scenes/nfc_scene_mf_ultralight_emulate.c index f23f554e6..ce3755541 100755 --- a/applications/nfc/scenes/nfc_scene_emulate_mifare_ul.c +++ b/applications/nfc/scenes/nfc_scene_mf_ultralight_emulate.c @@ -4,51 +4,52 @@ #define NFC_MF_UL_DATA_NOT_CHANGED (0UL) #define NFC_MF_UL_DATA_CHANGED (1UL) -void nfc_emulate_mifare_ul_worker_callback(NfcWorkerEvent event, void* context) { +bool nfc_mf_ultralight_emulate_worker_callback(NfcWorkerEvent event, void* context) { UNUSED(event); Nfc* nfc = context; scene_manager_set_scene_state( - nfc->scene_manager, NfcSceneEmulateMifareUl, NFC_MF_UL_DATA_CHANGED); + nfc->scene_manager, NfcSceneMfUltralightEmulate, NFC_MF_UL_DATA_CHANGED); + return true; } -void nfc_scene_emulate_mifare_ul_on_enter(void* context) { +void nfc_scene_mf_ultralight_emulate_on_enter(void* context) { Nfc* nfc = context; DOLPHIN_DEED(DolphinDeedNfcEmulate); // Setup view Popup* popup = nfc->popup; if(strcmp(nfc->dev->dev_name, "")) { - nfc_text_store_set(nfc, "%s", nfc->dev->dev_name); + nfc_text_store_set(nfc, "Emulating\n%s", nfc->dev->dev_name); + } else { + nfc_text_store_set(nfc, "Emulating\nMf Ultralight", nfc->dev->dev_name); } popup_set_icon(popup, 0, 3, &I_RFIDDolphinSend_97x61); - popup_set_header(popup, "Emulating\nMf Ultralight", 56, 31, AlignLeft, AlignTop); + popup_set_header(popup, nfc->text_store, 56, 31, AlignLeft, AlignTop); // Setup and start worker view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup); nfc_worker_start( nfc->worker, - NfcWorkerStateEmulateMifareUltralight, + NfcWorkerStateMfUltralightEmulate, &nfc->dev->dev_data, - nfc_emulate_mifare_ul_worker_callback, + nfc_mf_ultralight_emulate_worker_callback, nfc); nfc_blink_start(nfc); } -bool nfc_scene_emulate_mifare_ul_on_event(void* context, SceneManagerEvent event) { +bool nfc_scene_mf_ultralight_emulate_on_event(void* context, SceneManagerEvent event) { Nfc* nfc = context; bool consumed = false; - if(event.type == SceneManagerEventTypeTick) { - consumed = true; - } else if(event.type == SceneManagerEventTypeBack) { + if(event.type == SceneManagerEventTypeBack) { // Stop worker nfc_worker_stop(nfc->worker); // Check if data changed and save in shadow file - if(scene_manager_get_scene_state(nfc->scene_manager, NfcSceneEmulateMifareUl) == + if(scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMfUltralightEmulate) == NFC_MF_UL_DATA_CHANGED) { scene_manager_set_scene_state( - nfc->scene_manager, NfcSceneEmulateMifareUl, NFC_MF_UL_DATA_NOT_CHANGED); + nfc->scene_manager, NfcSceneMfUltralightEmulate, NFC_MF_UL_DATA_NOT_CHANGED); nfc_device_save_shadow(nfc->dev, nfc->dev->dev_name); } consumed = false; @@ -56,7 +57,7 @@ bool nfc_scene_emulate_mifare_ul_on_event(void* context, SceneManagerEvent event return consumed; } -void nfc_scene_emulate_mifare_ul_on_exit(void* context) { +void nfc_scene_mf_ultralight_emulate_on_exit(void* context) { Nfc* nfc = context; // Clear view diff --git a/applications/nfc/scenes/nfc_scene_mf_ultralight_key_input.c b/applications/nfc/scenes/nfc_scene_mf_ultralight_key_input.c new file mode 100644 index 000000000..089187d5b --- /dev/null +++ b/applications/nfc/scenes/nfc_scene_mf_ultralight_key_input.c @@ -0,0 +1,44 @@ +#include "../nfc_i.h" + +void nfc_scene_mf_ultralight_key_input_byte_input_callback(void* context) { + Nfc* nfc = context; + + view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventByteInputDone); +} + +void nfc_scene_mf_ultralight_key_input_on_enter(void* context) { + Nfc* nfc = context; + + // Setup view + ByteInput* byte_input = nfc->byte_input; + byte_input_set_header_text(byte_input, "Enter the password in hex"); + byte_input_set_result_callback( + byte_input, + nfc_scene_mf_ultralight_key_input_byte_input_callback, + NULL, + nfc, + nfc->byte_input_store, + 4); + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewByteInput); +} + +bool nfc_scene_mf_ultralight_key_input_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == NfcCustomEventByteInputDone) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightUnlockWarn); + consumed = true; + } + } + return consumed; +} + +void nfc_scene_mf_ultralight_key_input_on_exit(void* context) { + Nfc* nfc = context; + + // Clear view + byte_input_set_result_callback(nfc->byte_input, NULL, NULL, NULL, NULL, 0); + byte_input_set_header_text(nfc->byte_input, ""); +} diff --git a/applications/nfc/scenes/nfc_scene_mf_ultralight_menu.c b/applications/nfc/scenes/nfc_scene_mf_ultralight_menu.c new file mode 100644 index 000000000..9174a8b19 --- /dev/null +++ b/applications/nfc/scenes/nfc_scene_mf_ultralight_menu.c @@ -0,0 +1,74 @@ +#include "../nfc_i.h" + +enum SubmenuIndex { + SubmenuIndexUnlock, + SubmenuIndexSave, + SubmenuIndexEmulate, +}; + +void nfc_scene_mf_ultralight_menu_submenu_callback(void* context, uint32_t index) { + Nfc* nfc = context; + + view_dispatcher_send_custom_event(nfc->view_dispatcher, index); +} + +void nfc_scene_mf_ultralight_menu_on_enter(void* context) { + Nfc* nfc = context; + Submenu* submenu = nfc->submenu; + MfUltralightData* data = &nfc->dev->dev_data.mf_ul_data; + + if(data->data_read != data->data_size) { + submenu_add_item( + submenu, + "Unlock With Password", + SubmenuIndexUnlock, + nfc_scene_mf_ultralight_menu_submenu_callback, + nfc); + } + submenu_add_item( + submenu, "Save", SubmenuIndexSave, nfc_scene_mf_ultralight_menu_submenu_callback, nfc); + submenu_add_item( + submenu, + "Emulate", + SubmenuIndexEmulate, + nfc_scene_mf_ultralight_menu_submenu_callback, + nfc); + submenu_set_selected_item( + nfc->submenu, scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMfUltralightMenu)); + + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu); +} + +bool nfc_scene_mf_ultralight_menu_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == SubmenuIndexSave) { + nfc->dev->format = NfcDeviceSaveFormatMifareUl; + // Clear device name + nfc_device_set_name(nfc->dev, ""); + scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveName); + consumed = true; + } else if(event.event == SubmenuIndexEmulate) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightEmulate); + consumed = true; + } else if(event.event == SubmenuIndexUnlock) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightUnlockMenu); + consumed = true; + } + scene_manager_set_scene_state(nfc->scene_manager, NfcSceneMfUltralightMenu, event.event); + + } else if(event.type == SceneManagerEventTypeBack) { + consumed = scene_manager_previous_scene(nfc->scene_manager); + } + + return consumed; +} + +void nfc_scene_mf_ultralight_menu_on_exit(void* context) { + Nfc* nfc = context; + + // Clear view + submenu_reset(nfc->submenu); +} diff --git a/applications/nfc/scenes/nfc_scene_mf_ultralight_read_auth.c b/applications/nfc/scenes/nfc_scene_mf_ultralight_read_auth.c new file mode 100644 index 000000000..968157bdb --- /dev/null +++ b/applications/nfc/scenes/nfc_scene_mf_ultralight_read_auth.c @@ -0,0 +1,107 @@ +#include "../nfc_i.h" +#include + +typedef enum { + NfcSceneMfUlReadStateIdle, + NfcSceneMfUlReadStateDetecting, + NfcSceneMfUlReadStateReading, + NfcSceneMfUlReadStateNotSupportedCard, +} NfcSceneMfUlReadState; + +bool nfc_scene_mf_ultralight_read_auth_worker_callback(NfcWorkerEvent event, void* context) { + Nfc* nfc = context; + + if(event == NfcWorkerEventMfUltralightPassKey) { + memcpy(nfc->dev->dev_data.mf_ul_data.auth_key, nfc->byte_input_store, 4); + } else { + view_dispatcher_send_custom_event(nfc->view_dispatcher, event); + } + return true; +} + +void nfc_scene_mf_ultralight_read_auth_set_state(Nfc* nfc, NfcSceneMfUlReadState state) { + uint32_t curr_state = + scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMfUltralightReadAuth); + if(curr_state != state) { + if(state == NfcSceneMfUlReadStateDetecting) { + popup_reset(nfc->popup); + popup_set_text( + nfc->popup, "Apply card to\nFlipper's back", 97, 24, AlignCenter, AlignTop); + popup_set_icon(nfc->popup, 0, 8, &I_NFC_manual); + } else if(state == NfcSceneMfUlReadStateReading) { + popup_reset(nfc->popup); + popup_set_header( + nfc->popup, "Reading card\nDon't move...", 85, 24, AlignCenter, AlignTop); + popup_set_icon(nfc->popup, 12, 23, &A_Loading_24); + } else if(state == NfcSceneMfUlReadStateNotSupportedCard) { + popup_reset(nfc->popup); + popup_set_header(nfc->popup, "Wrong type of card!", 64, 3, AlignCenter, AlignTop); + popup_set_text( + nfc->popup, + "Only MIFARE\nUltralight & NTAG\n are supported", + 4, + 22, + AlignLeft, + AlignTop); + popup_set_icon(nfc->popup, 73, 17, &I_DolphinFirstStart8_56x51); + } + scene_manager_set_scene_state(nfc->scene_manager, NfcSceneMfUltralightReadAuth, state); + } +} + +void nfc_scene_mf_ultralight_read_auth_on_enter(void* context) { + Nfc* nfc = context; + DOLPHIN_DEED(DolphinDeedNfcRead); + + nfc_device_clear(nfc->dev); + // Setup view + nfc_scene_mf_ultralight_read_auth_set_state(nfc, NfcSceneMfUlReadStateDetecting); + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup); + // Start worker + nfc_worker_start( + nfc->worker, + NfcWorkerStateReadMfUltralightReadAuth, + &nfc->dev->dev_data, + nfc_scene_mf_ultralight_read_auth_worker_callback, + nfc); + + nfc_blink_start(nfc); +} + +bool nfc_scene_mf_ultralight_read_auth_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if((event.event == NfcWorkerEventSuccess) || (event.event == NfcWorkerEventFail)) { + notification_message(nfc->notifications, &sequence_success); + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightReadAuthResult); + consumed = true; + } else if(event.event == NfcWorkerEventCardDetected) { + nfc_scene_mf_ultralight_read_auth_set_state(nfc, NfcSceneMfUlReadStateReading); + consumed = true; + } else if(event.event == NfcWorkerEventNoCardDetected) { + nfc_scene_mf_ultralight_read_auth_set_state(nfc, NfcSceneMfUlReadStateDetecting); + consumed = true; + } else if(event.event == NfcWorkerEventWrongCardDetected) { + nfc_scene_mf_ultralight_read_auth_set_state( + nfc, NfcSceneMfUlReadStateNotSupportedCard); + } + } else if(event.type == SceneManagerEventTypeBack) { + consumed = scene_manager_search_and_switch_to_previous_scene( + nfc->scene_manager, NfcSceneMfUltralightUnlockMenu); + } + return consumed; +} + +void nfc_scene_mf_ultralight_read_auth_on_exit(void* context) { + Nfc* nfc = context; + + // Stop worker + nfc_worker_stop(nfc->worker); + // Clear view + popup_reset(nfc->popup); + nfc_blink_stop(nfc); + scene_manager_set_scene_state( + nfc->scene_manager, NfcSceneMfUltralightReadAuth, NfcSceneMfUlReadStateIdle); +} diff --git a/applications/nfc/scenes/nfc_scene_mf_ultralight_read_auth_result.c b/applications/nfc/scenes/nfc_scene_mf_ultralight_read_auth_result.c new file mode 100644 index 000000000..b94215455 --- /dev/null +++ b/applications/nfc/scenes/nfc_scene_mf_ultralight_read_auth_result.c @@ -0,0 +1,98 @@ +#include "../nfc_i.h" +#include + +void nfc_scene_mf_ultralight_read_auth_result_widget_callback( + GuiButtonType result, + InputType type, + void* context) { + Nfc* nfc = context; + + if(type == InputTypeShort) { + view_dispatcher_send_custom_event(nfc->view_dispatcher, result); + } +} + +void nfc_scene_mf_ultralight_read_auth_result_on_enter(void* context) { + Nfc* nfc = context; + DOLPHIN_DEED(DolphinDeedNfcReadSuccess); + + // Setup dialog view + FuriHalNfcDevData* nfc_data = &nfc->dev->dev_data.nfc_data; + MfUltralightData* mf_ul_data = &nfc->dev->dev_data.mf_ul_data; + MfUltralightConfigPages* config_pages = mf_ultralight_get_config_pages(mf_ul_data); + Widget* widget = nfc->widget; + string_t temp_str; + string_init(temp_str); + + if((mf_ul_data->data_read == mf_ul_data->data_size) && (mf_ul_data->data_read > 0)) { + widget_add_string_element( + widget, 64, 0, AlignCenter, AlignTop, FontPrimary, "All pages are unlocked!"); + } else { + widget_add_string_element( + widget, 64, 0, AlignCenter, AlignTop, FontPrimary, "Not all pages unlocked!"); + } + string_set_str(temp_str, "UID:"); + for(size_t i = 0; i < nfc_data->uid_len; i++) { + string_cat_printf(temp_str, " %02X", nfc_data->uid[i]); + } + widget_add_string_element( + widget, 0, 17, AlignLeft, AlignTop, FontSecondary, string_get_cstr(temp_str)); + if(mf_ul_data->auth_success) { + string_printf( + temp_str, + "Password: %02X %02X %02X %02X", + config_pages->auth_data.pwd.raw[0], + config_pages->auth_data.pwd.raw[1], + config_pages->auth_data.pwd.raw[2], + config_pages->auth_data.pwd.raw[3]); + widget_add_string_element( + widget, 0, 28, AlignLeft, AlignTop, FontSecondary, string_get_cstr(temp_str)); + string_printf( + temp_str, + "PACK: %02X %02X", + config_pages->auth_data.pack.raw[0], + config_pages->auth_data.pack.raw[1]); + widget_add_string_element( + widget, 0, 39, AlignLeft, AlignTop, FontSecondary, string_get_cstr(temp_str)); + } + string_printf( + temp_str, "Pages Read: %d/%d", mf_ul_data->data_read / 4, mf_ul_data->data_size / 4); + widget_add_string_element( + widget, 0, 50, AlignLeft, AlignTop, FontSecondary, string_get_cstr(temp_str)); + widget_add_button_element( + widget, + GuiButtonTypeRight, + "Save", + nfc_scene_mf_ultralight_read_auth_result_widget_callback, + nfc); + + string_clear(temp_str); + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); +} + +bool nfc_scene_mf_ultralight_read_auth_result_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == GuiButtonTypeRight) { + nfc->dev->format = NfcDeviceSaveFormatMifareUl; + // Clear device name + nfc_device_set_name(nfc->dev, ""); + scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveName); + consumed = true; + } + } else if(event.type == SceneManagerEventTypeBack) { + consumed = scene_manager_search_and_switch_to_previous_scene( + nfc->scene_manager, NfcSceneMfUltralightUnlockMenu); + } + + return consumed; +} + +void nfc_scene_mf_ultralight_read_auth_result_on_exit(void* context) { + Nfc* nfc = context; + + // Clean views + widget_reset(nfc->widget); +} diff --git a/applications/nfc/scenes/nfc_scene_mf_ultralight_read_success.c b/applications/nfc/scenes/nfc_scene_mf_ultralight_read_success.c new file mode 100644 index 000000000..65750b963 --- /dev/null +++ b/applications/nfc/scenes/nfc_scene_mf_ultralight_read_success.c @@ -0,0 +1,124 @@ +#include "../nfc_i.h" +#include + +enum { + ReadMifareUlStateShowInfo, + ReadMifareUlStateShowData, +}; + +void nfc_scene_mf_ultralight_read_success_widget_callback( + GuiButtonType result, + InputType type, + void* context) { + Nfc* nfc = context; + + if(type == InputTypeShort) { + view_dispatcher_send_custom_event(nfc->view_dispatcher, result); + } +} + +void nfc_scene_mf_ultralight_read_success_on_enter(void* context) { + Nfc* nfc = context; + DOLPHIN_DEED(DolphinDeedNfcReadSuccess); + + // Setup widget view + FuriHalNfcDevData* data = &nfc->dev->dev_data.nfc_data; + MfUltralightData* mf_ul_data = &nfc->dev->dev_data.mf_ul_data; + Widget* widget = nfc->widget; + widget_add_button_element( + widget, + GuiButtonTypeLeft, + "Retry", + nfc_scene_mf_ultralight_read_success_widget_callback, + nfc); + widget_add_button_element( + widget, + GuiButtonTypeCenter, + "Data", + nfc_scene_mf_ultralight_read_success_widget_callback, + nfc); + widget_add_button_element( + widget, + GuiButtonTypeRight, + "More", + nfc_scene_mf_ultralight_read_success_widget_callback, + nfc); + + widget_add_string_element( + widget, 0, 0, AlignLeft, AlignTop, FontSecondary, nfc_mf_ul_type(mf_ul_data->type, true)); + string_t data_str; + string_init_printf(data_str, "UID:"); + for(size_t i = 0; i < data->uid_len; i++) { + string_cat_printf(data_str, " %02X", data->uid[i]); + } + widget_add_string_element( + widget, 0, 13, AlignLeft, AlignTop, FontSecondary, string_get_cstr(data_str)); + string_printf( + data_str, "Pages Read: %d/%d", mf_ul_data->data_read / 4, mf_ul_data->data_size / 4); + widget_add_string_element( + widget, 0, 24, AlignLeft, AlignTop, FontSecondary, string_get_cstr(data_str)); + if(mf_ul_data->data_read != mf_ul_data->data_size) { + widget_add_string_element( + widget, 0, 35, AlignLeft, AlignTop, FontSecondary, "Password-protected pages!"); + } + string_clear(data_str); + + // Setup TextBox view + TextBox* text_box = nfc->text_box; + text_box_set_font(text_box, TextBoxFontHex); + for(uint16_t i = 0; i < mf_ul_data->data_size; i += 2) { + if(!(i % 8) && i) { + string_push_back(nfc->text_box_store, '\n'); + } + string_cat_printf( + nfc->text_box_store, "%02X%02X ", mf_ul_data->data[i], mf_ul_data->data[i + 1]); + } + text_box_set_text(text_box, string_get_cstr(nfc->text_box_store)); + + scene_manager_set_scene_state( + nfc->scene_manager, NfcSceneMfUltralightReadSuccess, ReadMifareUlStateShowInfo); + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); +} + +bool nfc_scene_mf_ultralight_read_success_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + bool consumed = false; + uint32_t state = + scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMfUltralightReadSuccess); + + if(event.type == SceneManagerEventTypeCustom) { + if(state == ReadMifareUlStateShowInfo && event.event == GuiButtonTypeLeft) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneRetryConfirm); + consumed = true; + } else if(state == ReadMifareUlStateShowInfo && event.event == GuiButtonTypeRight) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightMenu); + consumed = true; + } else if(state == ReadMifareUlStateShowInfo && event.event == GuiButtonTypeCenter) { + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewTextBox); + scene_manager_set_scene_state( + nfc->scene_manager, NfcSceneMfUltralightReadSuccess, ReadMifareUlStateShowData); + consumed = true; + } + } else if(event.type == SceneManagerEventTypeBack) { + if(state == ReadMifareUlStateShowData) { + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); + scene_manager_set_scene_state( + nfc->scene_manager, NfcSceneMfUltralightReadSuccess, ReadMifareUlStateShowInfo); + consumed = true; + } else { + scene_manager_next_scene(nfc->scene_manager, NfcSceneExitConfirm); + consumed = true; + } + } + + return consumed; +} + +void nfc_scene_mf_ultralight_read_success_on_exit(void* context) { + Nfc* nfc = context; + + // Clean views + widget_reset(nfc->widget); + text_box_reset(nfc->text_box); + string_reset(nfc->text_box_store); +} diff --git a/applications/nfc/scenes/nfc_scene_mf_ultralight_unlock_menu.c b/applications/nfc/scenes/nfc_scene_mf_ultralight_unlock_menu.c new file mode 100644 index 000000000..648aa31dc --- /dev/null +++ b/applications/nfc/scenes/nfc_scene_mf_ultralight_unlock_menu.c @@ -0,0 +1,70 @@ +#include "../nfc_i.h" + +enum SubmenuIndex { + SubmenuIndexMfUlUnlockMenuManual, + SubmenuIndexMfUlUnlockMenuAmeebo, + SubmenuIndexMfUlUnlockMenuXiaomi, +}; + +void nfc_scene_mf_ultralight_unlock_menu_submenu_callback(void* context, uint32_t index) { + Nfc* nfc = context; + + view_dispatcher_send_custom_event(nfc->view_dispatcher, index); +} + +void nfc_scene_mf_ultralight_unlock_menu_on_enter(void* context) { + Nfc* nfc = context; + Submenu* submenu = nfc->submenu; + + uint32_t state = + scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMfUltralightUnlockMenu); + submenu_add_item( + submenu, + "Enter Password Manually", + SubmenuIndexMfUlUnlockMenuManual, + nfc_scene_mf_ultralight_unlock_menu_submenu_callback, + nfc); + submenu_add_item( + submenu, + "Auth As Ameebo", + SubmenuIndexMfUlUnlockMenuAmeebo, + nfc_scene_mf_ultralight_unlock_menu_submenu_callback, + nfc); + submenu_add_item( + submenu, + "Auth As Xiaomi", + SubmenuIndexMfUlUnlockMenuXiaomi, + nfc_scene_mf_ultralight_unlock_menu_submenu_callback, + nfc); + submenu_set_selected_item(submenu, state); + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu); +} + +bool nfc_scene_mf_ultralight_unlock_menu_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == SubmenuIndexMfUlUnlockMenuManual) { + nfc->dev->dev_data.mf_ul_data.auth_method = MfUltralightAuthMethodManual; + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightKeyInput); + consumed = true; + } else if(event.event == SubmenuIndexMfUlUnlockMenuAmeebo) { + nfc->dev->dev_data.mf_ul_data.auth_method = MfUltralightAuthMethodAmeebo; + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightUnlockWarn); + consumed = true; + } else if(event.event == SubmenuIndexMfUlUnlockMenuXiaomi) { + nfc->dev->dev_data.mf_ul_data.auth_method = MfUltralightAuthMethodXiaomi; + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightUnlockWarn); + consumed = true; + } + scene_manager_set_scene_state(nfc->scene_manager, NfcSceneExtraActions, event.event); + } + return consumed; +} + +void nfc_scene_mf_ultralight_unlock_menu_on_exit(void* context) { + Nfc* nfc = context; + + submenu_reset(nfc->submenu); +} diff --git a/applications/nfc/scenes/nfc_scene_mf_ultralight_unlock_warn.c b/applications/nfc/scenes/nfc_scene_mf_ultralight_unlock_warn.c new file mode 100644 index 000000000..00df98e75 --- /dev/null +++ b/applications/nfc/scenes/nfc_scene_mf_ultralight_unlock_warn.c @@ -0,0 +1,45 @@ +#include "../nfc_i.h" + +void nfc_scene_mf_ultralight_unlock_warn_dialog_callback(DialogExResult result, void* context) { + Nfc* nfc = context; + + view_dispatcher_send_custom_event(nfc->view_dispatcher, result); +} + +void nfc_scene_mf_ultralight_unlock_warn_on_enter(void* context) { + Nfc* nfc = context; + DialogEx* dialog_ex = nfc->dialog_ex; + + dialog_ex_set_context(dialog_ex, nfc); + dialog_ex_set_result_callback(dialog_ex, nfc_scene_mf_ultralight_unlock_warn_dialog_callback); + + dialog_ex_set_header(dialog_ex, "Risky function!", 64, 4, AlignCenter, AlignTop); + dialog_ex_set_text( + dialog_ex, "Wrong password\ncan block your\ncard.", 4, 18, AlignLeft, AlignTop); + dialog_ex_set_icon(dialog_ex, 73, 17, &I_DolphinFirstStart8_56x51); + dialog_ex_set_center_button_text(dialog_ex, "OK"); + + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewDialogEx); +} + +bool nfc_scene_mf_ultralight_unlock_warn_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == DialogExResultCenter) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightReadAuth); + consumed = true; + } + } + + return consumed; +} + +void nfc_scene_mf_ultralight_unlock_warn_on_exit(void* context) { + Nfc* nfc = context; + + dialog_ex_reset(nfc->dialog_ex); + submenu_reset(nfc->submenu); +} diff --git a/applications/nfc/scenes/nfc_scene_mifare_ul_menu.c b/applications/nfc/scenes/nfc_scene_mifare_ul_menu.c deleted file mode 100755 index 0099f1a0b..000000000 --- a/applications/nfc/scenes/nfc_scene_mifare_ul_menu.c +++ /dev/null @@ -1,60 +0,0 @@ -#include "../nfc_i.h" - -enum SubmenuIndex { - SubmenuIndexSave, - SubmenuIndexEmulate, -}; - -void nfc_scene_mifare_ul_menu_submenu_callback(void* context, uint32_t index) { - Nfc* nfc = context; - - view_dispatcher_send_custom_event(nfc->view_dispatcher, index); -} - -void nfc_scene_mifare_ul_menu_on_enter(void* context) { - Nfc* nfc = context; - Submenu* submenu = nfc->submenu; - - submenu_add_item( - submenu, "Save", SubmenuIndexSave, nfc_scene_mifare_ul_menu_submenu_callback, nfc); - submenu_add_item( - submenu, "Emulate", SubmenuIndexEmulate, nfc_scene_mifare_ul_menu_submenu_callback, nfc); - submenu_set_selected_item( - nfc->submenu, scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMifareUlMenu)); - - view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu); -} - -bool nfc_scene_mifare_ul_menu_on_event(void* context, SceneManagerEvent event) { - Nfc* nfc = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == SubmenuIndexSave) { - scene_manager_set_scene_state( - nfc->scene_manager, NfcSceneMifareUlMenu, SubmenuIndexSave); - nfc->dev->format = NfcDeviceSaveFormatMifareUl; - // Clear device name - nfc_device_set_name(nfc->dev, ""); - scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveName); - consumed = true; - } else if(event.event == SubmenuIndexEmulate) { - scene_manager_set_scene_state( - nfc->scene_manager, NfcSceneMifareUlMenu, SubmenuIndexEmulate); - scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateMifareUl); - consumed = true; - } - } else if(event.type == SceneManagerEventTypeBack) { - consumed = - scene_manager_search_and_switch_to_previous_scene(nfc->scene_manager, NfcSceneStart); - } - - return consumed; -} - -void nfc_scene_mifare_ul_menu_on_exit(void* context) { - Nfc* nfc = context; - - // Clear view - submenu_reset(nfc->submenu); -} diff --git a/applications/nfc/scenes/nfc_scene_read.c b/applications/nfc/scenes/nfc_scene_read.c new file mode 100644 index 000000000..491b419ef --- /dev/null +++ b/applications/nfc/scenes/nfc_scene_read.c @@ -0,0 +1,111 @@ +#include "../nfc_i.h" +#include + +typedef enum { + NfcSceneReadStateIdle, + NfcSceneReadStateDetecting, + NfcSceneReadStateReading, +} NfcSceneReadState; + +bool nfc_scene_read_worker_callback(NfcWorkerEvent event, void* context) { + Nfc* nfc = context; + bool consumed = false; + if(event == NfcWorkerEventReadMfClassicLoadKeyCache) { + consumed = nfc_device_load_key_cache(nfc->dev); + } else { + view_dispatcher_send_custom_event(nfc->view_dispatcher, event); + consumed = true; + } + return consumed; +} + +void nfc_scene_read_set_state(Nfc* nfc, NfcSceneReadState state) { + uint32_t curr_state = scene_manager_get_scene_state(nfc->scene_manager, NfcSceneRead); + if(curr_state != state) { + if(state == NfcSceneReadStateDetecting) { + popup_reset(nfc->popup); + popup_set_text( + nfc->popup, "Apply card to\nFlipper's back", 97, 24, AlignCenter, AlignTop); + popup_set_icon(nfc->popup, 0, 8, &I_NFC_manual); + } else if(state == NfcSceneReadStateReading) { + popup_reset(nfc->popup); + popup_set_header( + nfc->popup, "Reading card\nDon't move...", 85, 24, AlignCenter, AlignTop); + popup_set_icon(nfc->popup, 12, 23, &A_Loading_24); + } + scene_manager_set_scene_state(nfc->scene_manager, NfcSceneRead, state); + } +} + +void nfc_scene_read_on_enter(void* context) { + Nfc* nfc = context; + DOLPHIN_DEED(DolphinDeedNfcRead); + + nfc_device_clear(nfc->dev); + // Setup view + nfc_scene_read_set_state(nfc, NfcSceneReadStateDetecting); + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup); + // Start worker + nfc_worker_start( + nfc->worker, NfcWorkerStateRead, &nfc->dev->dev_data, nfc_scene_read_worker_callback, nfc); + + nfc_blink_start(nfc); +} + +bool nfc_scene_read_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if((event.event == NfcWorkerEventReadUidNfcB) || + (event.event == NfcWorkerEventReadUidNfcF) || + (event.event == NfcWorkerEventReadUidNfcV) || + (event.event == NfcWorkerEventReadUidNfcA)) { + notification_message(nfc->notifications, &sequence_success); + scene_manager_next_scene(nfc->scene_manager, NfcSceneReadCardSuccess); + consumed = true; + } else if(event.event == NfcWorkerEventReadMfUltralight) { + notification_message(nfc->notifications, &sequence_success); + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightReadSuccess); + consumed = true; + } else if(event.event == NfcWorkerEventReadMfClassicDone) { + notification_message(nfc->notifications, &sequence_success); + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicReadSuccess); + consumed = true; + } else if(event.event == NfcWorkerEventReadMfDesfire) { + notification_message(nfc->notifications, &sequence_success); + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfDesfireReadSuccess); + consumed = true; + } else if(event.event == NfcWorkerEventReadBankCard) { + notification_message(nfc->notifications, &sequence_success); + scene_manager_next_scene(nfc->scene_manager, NfcSceneEmvReadSuccess); + consumed = true; + } else if(event.event == NfcWorkerEventReadMfClassicDictAttackRequired) { + if(mf_classic_dict_check_presence(MfClassicDictTypeFlipper)) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicDictAttack); + } else { + scene_manager_next_scene(nfc->scene_manager, NfcSceneDictNotFound); + } + consumed = true; + } else if(event.event == NfcWorkerEventCardDetected) { + nfc_scene_read_set_state(nfc, NfcSceneReadStateReading); + consumed = true; + } else if(event.event == NfcWorkerEventNoCardDetected) { + nfc_scene_read_set_state(nfc, NfcSceneReadStateDetecting); + consumed = true; + } + } + return consumed; +} + +void nfc_scene_read_on_exit(void* context) { + Nfc* nfc = context; + + // Stop worker + nfc_worker_stop(nfc->worker); + // Clear view + popup_reset(nfc->popup); + scene_manager_set_scene_state(nfc->scene_manager, NfcSceneRead, NfcSceneReadStateIdle); + + nfc_blink_stop(nfc); +} diff --git a/applications/nfc/scenes/nfc_scene_read_card.c b/applications/nfc/scenes/nfc_scene_read_card.c deleted file mode 100755 index 0cd0fc898..000000000 --- a/applications/nfc/scenes/nfc_scene_read_card.c +++ /dev/null @@ -1,51 +0,0 @@ -#include "../nfc_i.h" -#include - -void nfc_read_card_worker_callback(NfcWorkerEvent event, void* context) { - UNUSED(event); - Nfc* nfc = context; - view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventWorkerExit); -} - -void nfc_scene_read_card_on_enter(void* context) { - Nfc* nfc = context; - DOLPHIN_DEED(DolphinDeedNfcRead); - - // Setup view - Popup* popup = nfc->popup; - popup_set_header(popup, "Detecting\nNFC card", 70, 34, AlignLeft, AlignTop); - popup_set_icon(popup, 0, 3, &I_RFIDDolphinReceive_97x61); - - // Start worker - view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup); - nfc_worker_start( - nfc->worker, NfcWorkerStateDetect, &nfc->dev->dev_data, nfc_read_card_worker_callback, nfc); - - nfc_blink_start(nfc); -} - -bool nfc_scene_read_card_on_event(void* context, SceneManagerEvent event) { - Nfc* nfc = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == NfcCustomEventWorkerExit) { - scene_manager_next_scene(nfc->scene_manager, NfcSceneReadCardSuccess); - consumed = true; - } - } else if(event.type == SceneManagerEventTypeTick) { - consumed = true; - } - return consumed; -} - -void nfc_scene_read_card_on_exit(void* context) { - Nfc* nfc = context; - - // Stop worker - nfc_worker_stop(nfc->worker); - // Clear view - popup_reset(nfc->popup); - - nfc_blink_stop(nfc); -} diff --git a/applications/nfc/scenes/nfc_scene_read_card_success.c b/applications/nfc/scenes/nfc_scene_read_card_success.c index c0a865bca..b889ce08d 100755 --- a/applications/nfc/scenes/nfc_scene_read_card_success.c +++ b/applications/nfc/scenes/nfc_scene_read_card_success.c @@ -22,9 +22,6 @@ void nfc_scene_read_card_success_on_enter(void* context) { string_init(uid_str); DOLPHIN_DEED(DolphinDeedNfcReadSuccess); - // Send notification - notification_message(nfc->notifications, &sequence_success); - // Setup view FuriHalNfcDevData* data = &nfc->dev->dev_data.nfc_data; Widget* widget = nfc->widget; @@ -38,18 +35,12 @@ void nfc_scene_read_card_success_on_enter(void* context) { widget, GuiButtonTypeLeft, "Retry", nfc_scene_read_card_success_widget_callback, nfc); if(data->type == FuriHalNfcTypeA) { widget_add_button_element( - widget, GuiButtonTypeRight, "More", nfc_scene_read_card_success_widget_callback, nfc); + widget, GuiButtonTypeRight, "Save", nfc_scene_read_card_success_widget_callback, nfc); widget_add_icon_element(widget, 8, 13, &I_Medium_chip_22x21); - string_cat_printf(data_str, " may be:"); widget_add_string_element( widget, 37, 12, AlignLeft, AlignBottom, FontPrimary, string_get_cstr(data_str)); string_printf( - data_str, - "%s\nATQA: %02X%02X SAK: %02X", - nfc_guess_protocol(nfc->dev->dev_data.protocol), - data->atqa[0], - data->atqa[1], - data->sak); + data_str, "ATQA: %02X%02X\nSAK: %02X", data->atqa[0], data->atqa[1], data->sak); widget_add_string_multiline_element( widget, 37, 16, AlignLeft, AlignTop, FontSecondary, string_get_cstr(data_str)); widget_add_string_element( @@ -69,16 +60,15 @@ void nfc_scene_read_card_success_on_enter(void* context) { bool nfc_scene_read_card_success_on_event(void* context, SceneManagerEvent event) { Nfc* nfc = context; - FuriHalNfcDevData* data = &nfc->dev->dev_data.nfc_data; bool consumed = false; if(event.type == SceneManagerEventTypeCustom) { if(event.event == GuiButtonTypeLeft) { consumed = scene_manager_previous_scene(nfc->scene_manager); - } else if(data->type == FuriHalNfcTypeA && event.event == GuiButtonTypeRight) { - // Clear device name + } else if(event.event == GuiButtonTypeRight) { + nfc->dev->format = NfcDeviceSaveFormatUid; nfc_device_set_name(nfc->dev, ""); - scene_manager_next_scene(nfc->scene_manager, NfcSceneCardMenu); + scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveName); consumed = true; } } diff --git a/applications/nfc/scenes/nfc_scene_read_emv_app.c b/applications/nfc/scenes/nfc_scene_read_emv_app.c deleted file mode 100755 index 5bb8ac5fe..000000000 --- a/applications/nfc/scenes/nfc_scene_read_emv_app.c +++ /dev/null @@ -1,58 +0,0 @@ -#include "../nfc_i.h" -#include - -void nfc_read_emv_app_worker_callback(NfcWorkerEvent event, void* context) { - UNUSED(event); - Nfc* nfc = context; - view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventWorkerExit); -} - -void nfc_scene_read_emv_app_on_enter(void* context) { - Nfc* nfc = context; - DOLPHIN_DEED(DolphinDeedNfcRead); - - // Setup view - Popup* popup = nfc->popup; - popup_set_header(popup, "Reading\nbank card", 70, 34, AlignLeft, AlignTop); - popup_set_icon(popup, 0, 3, &I_RFIDDolphinReceive_97x61); - - view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup); - // Start worker - nfc_worker_start( - nfc->worker, - NfcWorkerStateReadEMVApp, - &nfc->dev->dev_data, - nfc_read_emv_app_worker_callback, - nfc); - nfc_blink_start(nfc); -} - -bool nfc_scene_read_emv_app_on_event(void* context, SceneManagerEvent event) { - Nfc* nfc = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == NfcCustomEventWorkerExit) { - scene_manager_set_scene_state( - nfc->scene_manager, NfcSceneReadEmvAppSuccess, NFC_SEND_NOTIFICATION_TRUE); - scene_manager_next_scene(nfc->scene_manager, NfcSceneReadEmvAppSuccess); - consumed = true; - } - } else if(event.type == SceneManagerEventTypeTick) { - consumed = true; - } - - return consumed; -} - -void nfc_scene_read_emv_app_on_exit(void* context) { - Nfc* nfc = context; - - // Stop worker - nfc_worker_stop(nfc->worker); - - // Clear view - popup_reset(nfc->popup); - - nfc_blink_stop(nfc); -} diff --git a/applications/nfc/scenes/nfc_scene_read_emv_app_success.c b/applications/nfc/scenes/nfc_scene_read_emv_app_success.c deleted file mode 100755 index b9bc5ba40..000000000 --- a/applications/nfc/scenes/nfc_scene_read_emv_app_success.c +++ /dev/null @@ -1,83 +0,0 @@ -#include "../nfc_i.h" -#include "../helpers/nfc_emv_parser.h" -#include - -void nfc_scene_read_emv_app_widget_callback(GuiButtonType result, InputType type, void* context) { - Nfc* nfc = context; - if(type == InputTypeShort) { - view_dispatcher_send_custom_event(nfc->view_dispatcher, result); - } -} - -void nfc_scene_read_emv_app_success_on_enter(void* context) { - Nfc* nfc = context; - DOLPHIN_DEED(DolphinDeedNfcReadSuccess); - - // Setup view - FuriHalNfcDevData* nfc_data = &nfc->dev->dev_data.nfc_data; - EmvData* emv_data = &nfc->dev->dev_data.emv_data; - Widget* widget = nfc->widget; - widget_add_button_element( - widget, GuiButtonTypeLeft, "Retry", nfc_scene_read_emv_app_widget_callback, nfc); - widget_add_button_element( - widget, GuiButtonTypeRight, "Run app", nfc_scene_read_emv_app_widget_callback, nfc); - widget_add_string_element(widget, 36, 5, AlignLeft, AlignTop, FontPrimary, "Found EMV App"); - widget_add_icon_element(widget, 8, 5, &I_Medium_chip_22x21); - // Display UID - string_t temp_str; - string_init_printf(temp_str, "UID:"); - for(size_t i = 0; i < nfc_data->uid_len; i++) { - string_cat_printf(temp_str, " %02X", nfc_data->uid[i]); - } - widget_add_string_element( - widget, 36, 18, AlignLeft, AlignTop, FontSecondary, string_get_cstr(temp_str)); - string_reset(temp_str); - // Display application - string_printf(temp_str, "App: "); - string_t aid; - string_init(aid); - bool aid_found = - nfc_emv_parser_get_aid_name(nfc->dev->storage, emv_data->aid, emv_data->aid_len, aid); - if(!aid_found) { - for(uint8_t i = 0; i < emv_data->aid_len; i++) { - string_cat_printf(aid, "%02X", emv_data->aid[i]); - } - } - string_cat(temp_str, aid); - widget_add_string_element( - widget, 7, 29, AlignLeft, AlignTop, FontSecondary, string_get_cstr(temp_str)); - string_clear(temp_str); - string_clear(aid); - - // Send notification - if(scene_manager_get_scene_state(nfc->scene_manager, NfcSceneReadEmvAppSuccess) == - NFC_SEND_NOTIFICATION_TRUE) { - notification_message(nfc->notifications, &sequence_success); - scene_manager_set_scene_state( - nfc->scene_manager, NfcSceneReadEmvAppSuccess, NFC_SEND_NOTIFICATION_FALSE); - } - - view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); -} - -bool nfc_scene_read_emv_app_success_on_event(void* context, SceneManagerEvent event) { - Nfc* nfc = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == GuiButtonTypeLeft) { - consumed = scene_manager_previous_scene(nfc->scene_manager); - } else if(event.event == GuiButtonTypeRight) { - scene_manager_next_scene(nfc->scene_manager, NfcSceneRunEmvAppConfirm); - consumed = true; - } - } - return consumed; -} - -void nfc_scene_read_emv_app_success_on_exit(void* context) { - Nfc* nfc = context; - - // Clear views - widget_reset(nfc->widget); -} diff --git a/applications/nfc/scenes/nfc_scene_read_emv_data.c b/applications/nfc/scenes/nfc_scene_read_emv_data.c deleted file mode 100755 index e1881cefb..000000000 --- a/applications/nfc/scenes/nfc_scene_read_emv_data.c +++ /dev/null @@ -1,59 +0,0 @@ -#include "../nfc_i.h" -#include - -void nfc_read_emv_data_worker_callback(NfcWorkerEvent event, void* context) { - UNUSED(event); - Nfc* nfc = context; - view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventWorkerExit); -} - -void nfc_scene_read_emv_data_on_enter(void* context) { - Nfc* nfc = context; - DOLPHIN_DEED(DolphinDeedNfcRead); - - // Setup view - Popup* popup = nfc->popup; - popup_set_header(popup, "Reading\nbank card", 70, 34, AlignLeft, AlignTop); - popup_set_icon(popup, 0, 3, &I_RFIDDolphinReceive_97x61); - - view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup); - // Clear emv data - memset(&nfc->dev->dev_data.emv_data, 0, sizeof(nfc->dev->dev_data.emv_data)); - // Start worker - nfc_worker_start( - nfc->worker, - NfcWorkerStateReadEMVData, - &nfc->dev->dev_data, - nfc_read_emv_data_worker_callback, - nfc); - - nfc_blink_start(nfc); -} - -bool nfc_scene_read_emv_data_on_event(void* context, SceneManagerEvent event) { - Nfc* nfc = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == NfcCustomEventWorkerExit) { - scene_manager_set_scene_state( - nfc->scene_manager, NfcSceneReadEmvDataSuccess, NFC_SEND_NOTIFICATION_TRUE); - scene_manager_next_scene(nfc->scene_manager, NfcSceneReadEmvDataSuccess); - consumed = true; - } - } else if(event.type == SceneManagerEventTypeTick) { - consumed = true; - } - return consumed; -} - -void nfc_scene_read_emv_data_on_exit(void* context) { - Nfc* nfc = context; - - // Stop worker - nfc_worker_stop(nfc->worker); - // Clear view - popup_reset(nfc->popup); - - nfc_blink_stop(nfc); -} diff --git a/applications/nfc/scenes/nfc_scene_read_mifare_classic.c b/applications/nfc/scenes/nfc_scene_read_mifare_classic.c deleted file mode 100644 index a901ecd48..000000000 --- a/applications/nfc/scenes/nfc_scene_read_mifare_classic.c +++ /dev/null @@ -1,96 +0,0 @@ -#include "../nfc_i.h" - -enum { - NfcSceneReadMifareClassicStateInProgress, - NfcSceneReadMifareClassicStateDone, -}; - -void nfc_read_mifare_classic_worker_callback(NfcWorkerEvent event, void* context) { - furi_assert(context); - Nfc* nfc = context; - view_dispatcher_send_custom_event(nfc->view_dispatcher, event); -} - -void nfc_read_mifare_classic_dict_attack_result_callback(void* context) { - furi_assert(context); - Nfc* nfc = context; - view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventDictAttackDone); -} - -void nfc_scene_read_mifare_classic_on_enter(void* context) { - Nfc* nfc = context; - - // Setup and start worker - memset(&nfc->dev->dev_data.mf_classic_data, 0, sizeof(MfClassicData)); - dict_attack_set_result_callback( - nfc->dict_attack, nfc_read_mifare_classic_dict_attack_result_callback, nfc); - scene_manager_set_scene_state( - nfc->scene_manager, NfcSceneReadMifareClassic, NfcSceneReadMifareClassicStateInProgress); - view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewDictAttack); - nfc_worker_start( - nfc->worker, - NfcWorkerStateReadMifareClassic, - &nfc->dev->dev_data, - nfc_read_mifare_classic_worker_callback, - nfc); - - nfc_blink_start(nfc); -} - -bool nfc_scene_read_mifare_classic_on_event(void* context, SceneManagerEvent event) { - Nfc* nfc = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeTick) { - consumed = true; - } else if(event.type == SceneManagerEventTypeCustom) { - if(event.event == NfcCustomEventDictAttackDone) { - scene_manager_next_scene(nfc->scene_manager, NfcSceneMifareClassicMenu); - consumed = true; - } else if(event.event == NfcWorkerEventDetectedClassic1k) { - dict_attack_card_detected(nfc->dict_attack, MfClassicType1k); - consumed = true; - } else if(event.event == NfcWorkerEventDetectedClassic4k) { - dict_attack_card_detected(nfc->dict_attack, MfClassicType4k); - consumed = true; - } else if(event.event == NfcWorkerEventNewSector) { - dict_attack_inc_curr_sector(nfc->dict_attack); - consumed = true; - } else if(event.event == NfcWorkerEventFoundKeyA) { - dict_attack_inc_found_key(nfc->dict_attack, MfClassicKeyA); - consumed = true; - } else if(event.event == NfcWorkerEventFoundKeyB) { - dict_attack_inc_found_key(nfc->dict_attack, MfClassicKeyB); - consumed = true; - } else if(event.event == NfcWorkerEventNoCardDetected) { - dict_attack_card_removed(nfc->dict_attack); - consumed = true; - } else if(event.event == NfcWorkerEventSuccess) { - scene_manager_set_scene_state( - nfc->scene_manager, NfcSceneReadMifareClassic, NfcSceneReadMifareClassicStateDone); - nfc_blink_stop(nfc); - notification_message(nfc->notifications, &sequence_success); - dict_attack_set_result(nfc->dict_attack, true); - consumed = true; - } else if(event.event == NfcWorkerEventFail) { - scene_manager_set_scene_state( - nfc->scene_manager, NfcSceneReadMifareClassic, NfcSceneReadMifareClassicStateDone); - nfc_blink_stop(nfc); - dict_attack_set_result(nfc->dict_attack, false); - consumed = true; - } else if(event.event == NfcWorkerEventNoDictFound) { - scene_manager_next_scene(nfc->scene_manager, NfcSceneDictNotFound); - consumed = true; - } - } - return consumed; -} - -void nfc_scene_read_mifare_classic_on_exit(void* context) { - Nfc* nfc = context; - // Stop worker - nfc_worker_stop(nfc->worker); - dict_attack_reset(nfc->dict_attack); - - nfc_blink_stop(nfc); -} diff --git a/applications/nfc/scenes/nfc_scene_read_mifare_desfire.c b/applications/nfc/scenes/nfc_scene_read_mifare_desfire.c deleted file mode 100644 index fc99b64c8..000000000 --- a/applications/nfc/scenes/nfc_scene_read_mifare_desfire.c +++ /dev/null @@ -1,56 +0,0 @@ -#include "../nfc_i.h" -#include - -void nfc_read_mifare_desfire_worker_callback(NfcWorkerEvent event, void* context) { - UNUSED(event); - Nfc* nfc = context; - view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventWorkerExit); -} - -void nfc_scene_read_mifare_desfire_on_enter(void* context) { - Nfc* nfc = context; - DOLPHIN_DEED(DolphinDeedNfcRead); - - // Setup view - Popup* popup = nfc->popup; - popup_set_header(popup, "Reading\nDESFire", 70, 34, AlignLeft, AlignTop); - popup_set_icon(popup, 0, 3, &I_RFIDDolphinReceive_97x61); - - view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup); - // Start worker - nfc_worker_start( - nfc->worker, - NfcWorkerStateReadMifareDesfire, - &nfc->dev->dev_data, - nfc_read_mifare_desfire_worker_callback, - nfc); - nfc_blink_start(nfc); -} - -bool nfc_scene_read_mifare_desfire_on_event(void* context, SceneManagerEvent event) { - Nfc* nfc = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == NfcCustomEventWorkerExit) { - notification_message(nfc->notifications, &sequence_success); - DOLPHIN_DEED(DolphinDeedNfcReadSuccess); - scene_manager_next_scene(nfc->scene_manager, NfcSceneReadMifareDesfireSuccess); - consumed = true; - } - } else if(event.type == SceneManagerEventTypeTick) { - consumed = true; - } - return consumed; -} - -void nfc_scene_read_mifare_desfire_on_exit(void* context) { - Nfc* nfc = context; - - // Stop worker - nfc_worker_stop(nfc->worker); - // Clear view - popup_reset(nfc->popup); - - nfc_blink_stop(nfc); -} diff --git a/applications/nfc/scenes/nfc_scene_read_mifare_ul.c b/applications/nfc/scenes/nfc_scene_read_mifare_ul.c deleted file mode 100755 index 444f62538..000000000 --- a/applications/nfc/scenes/nfc_scene_read_mifare_ul.c +++ /dev/null @@ -1,54 +0,0 @@ -#include "../nfc_i.h" -#include - -void nfc_read_mifare_ul_worker_callback(NfcWorkerEvent event, void* context) { - UNUSED(event); - Nfc* nfc = context; - view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventWorkerExit); -} - -void nfc_scene_read_mifare_ul_on_enter(void* context) { - Nfc* nfc = context; - DOLPHIN_DEED(DolphinDeedNfcRead); - - // Setup view - Popup* popup = nfc->popup; - popup_set_header(popup, "Detecting\nultralight", 70, 34, AlignLeft, AlignTop); - popup_set_icon(popup, 0, 3, &I_RFIDDolphinReceive_97x61); - - view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup); - // Start worker - nfc_worker_start( - nfc->worker, - NfcWorkerStateReadMifareUltralight, - &nfc->dev->dev_data, - nfc_read_mifare_ul_worker_callback, - nfc); - nfc_blink_start(nfc); -} - -bool nfc_scene_read_mifare_ul_on_event(void* context, SceneManagerEvent event) { - Nfc* nfc = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == NfcCustomEventWorkerExit) { - scene_manager_next_scene(nfc->scene_manager, NfcSceneReadMifareUlSuccess); - consumed = true; - } - } else if(event.type == SceneManagerEventTypeTick) { - consumed = true; - } - return consumed; -} - -void nfc_scene_read_mifare_ul_on_exit(void* context) { - Nfc* nfc = context; - - // Stop worker - nfc_worker_stop(nfc->worker); - // Clear view - popup_reset(nfc->popup); - - nfc_blink_stop(nfc); -} diff --git a/applications/nfc/scenes/nfc_scene_read_mifare_ul_success.c b/applications/nfc/scenes/nfc_scene_read_mifare_ul_success.c deleted file mode 100755 index 5bcf15894..000000000 --- a/applications/nfc/scenes/nfc_scene_read_mifare_ul_success.c +++ /dev/null @@ -1,108 +0,0 @@ -#include "../nfc_i.h" -#include - -#define NFC_SCENE_READ_SUCCESS_SHIFT " " - -enum { - ReadMifareUlStateShowUID, - ReadMifareUlStateShowData, -}; - -void nfc_scene_read_mifare_ul_success_dialog_callback(DialogExResult result, void* context) { - Nfc* nfc = context; - - view_dispatcher_send_custom_event(nfc->view_dispatcher, result); -} - -void nfc_scene_read_mifare_ul_success_on_enter(void* context) { - Nfc* nfc = context; - DOLPHIN_DEED(DolphinDeedNfcReadSuccess); - - // Send notification - notification_message(nfc->notifications, &sequence_success); - - // Setup dialog view - FuriHalNfcDevData* data = &nfc->dev->dev_data.nfc_data; - MfUltralightData* mf_ul_data = &nfc->dev->dev_data.mf_ul_data; - DialogEx* dialog_ex = nfc->dialog_ex; - dialog_ex_set_left_button_text(dialog_ex, "Retry"); - dialog_ex_set_right_button_text(dialog_ex, "More"); - dialog_ex_set_center_button_text(dialog_ex, "Data"); - dialog_ex_set_header( - dialog_ex, nfc_mf_ul_type(mf_ul_data->type, true), 64, 8, AlignCenter, AlignCenter); - dialog_ex_set_icon(dialog_ex, 8, 13, &I_Medium_chip_22x21); - // Display UID - nfc_text_store_set( - nfc, - NFC_SCENE_READ_SUCCESS_SHIFT "ATQA: %02X%02X\n" NFC_SCENE_READ_SUCCESS_SHIFT - "SAK: %02X\nUID: %02X %02X %02X %02X %02X %02X %02X", - data->atqa[0], - data->atqa[1], - data->sak, - data->uid[0], - data->uid[1], - data->uid[2], - data->uid[3], - data->uid[4], - data->uid[5], - data->uid[6]); - dialog_ex_set_text(dialog_ex, nfc->text_store, 8, 16, AlignLeft, AlignTop); - dialog_ex_set_context(dialog_ex, nfc); - dialog_ex_set_result_callback(dialog_ex, nfc_scene_read_mifare_ul_success_dialog_callback); - - // Setup TextBox view - TextBox* text_box = nfc->text_box; - text_box_set_font(text_box, TextBoxFontHex); - for(uint16_t i = 0; i < mf_ul_data->data_size; i += 2) { - if(!(i % 8) && i) { - string_push_back(nfc->text_box_store, '\n'); - } - string_cat_printf( - nfc->text_box_store, "%02X%02X ", mf_ul_data->data[i], mf_ul_data->data[i + 1]); - } - text_box_set_text(text_box, string_get_cstr(nfc->text_box_store)); - - scene_manager_set_scene_state( - nfc->scene_manager, NfcSceneReadMifareUlSuccess, ReadMifareUlStateShowUID); - view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewDialogEx); -} - -bool nfc_scene_read_mifare_ul_success_on_event(void* context, SceneManagerEvent event) { - Nfc* nfc = context; - bool consumed = false; - uint32_t state = - scene_manager_get_scene_state(nfc->scene_manager, NfcSceneReadMifareUlSuccess); - - if(event.type == SceneManagerEventTypeCustom) { - if(state == ReadMifareUlStateShowUID && event.event == DialogExResultLeft) { - scene_manager_previous_scene(nfc->scene_manager); - consumed = true; - } else if(state == ReadMifareUlStateShowUID && event.event == DialogExResultRight) { - scene_manager_next_scene(nfc->scene_manager, NfcSceneMifareUlMenu); - consumed = true; - } else if(state == ReadMifareUlStateShowUID && event.event == DialogExResultCenter) { - view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewTextBox); - scene_manager_set_scene_state( - nfc->scene_manager, NfcSceneReadMifareUlSuccess, ReadMifareUlStateShowData); - consumed = true; - } - } else if(event.type == SceneManagerEventTypeBack) { - if(state == ReadMifareUlStateShowData) { - view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewDialogEx); - scene_manager_set_scene_state( - nfc->scene_manager, NfcSceneReadMifareUlSuccess, ReadMifareUlStateShowUID); - consumed = true; - } - } - - return consumed; -} - -void nfc_scene_read_mifare_ul_success_on_exit(void* context) { - Nfc* nfc = context; - - // Clean views - dialog_ex_reset(nfc->dialog_ex); - text_box_reset(nfc->text_box); - string_reset(nfc->text_box_store); -} diff --git a/applications/nfc/scenes/nfc_scene_restore_original.c b/applications/nfc/scenes/nfc_scene_restore_original.c index 40b59b9eb..3ecf5c048 100644 --- a/applications/nfc/scenes/nfc_scene_restore_original.c +++ b/applications/nfc/scenes/nfc_scene_restore_original.c @@ -25,7 +25,13 @@ bool nfc_scene_restore_original_on_event(void* context, SceneManagerEvent event) if(event.type == SceneManagerEventTypeCustom) { if(event.event == NfcCustomEventViewExit) { - consumed = scene_manager_previous_scene(nfc->scene_manager); + if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSavedMenu)) { + consumed = scene_manager_search_and_switch_to_previous_scene( + nfc->scene_manager, NfcSceneSavedMenu); + } else { + consumed = scene_manager_search_and_switch_to_previous_scene( + nfc->scene_manager, NfcSceneStart); + } } } return consumed; diff --git a/applications/nfc/scenes/nfc_scene_restore_original_confirm.c b/applications/nfc/scenes/nfc_scene_restore_original_confirm.c new file mode 100644 index 000000000..2c12749df --- /dev/null +++ b/applications/nfc/scenes/nfc_scene_restore_original_confirm.c @@ -0,0 +1,53 @@ +#include "../nfc_i.h" + +void nfc_scene_restore_original_confirm_dialog_callback(DialogExResult result, void* context) { + Nfc* nfc = context; + + view_dispatcher_send_custom_event(nfc->view_dispatcher, result); +} + +void nfc_scene_restore_original_confirm_on_enter(void* context) { + Nfc* nfc = context; + DialogEx* dialog_ex = nfc->dialog_ex; + + dialog_ex_set_header(dialog_ex, "Restore Card Data?", 64, 0, AlignCenter, AlignTop); + dialog_ex_set_icon(dialog_ex, 5, 15, &I_Restoring); + dialog_ex_set_text( + dialog_ex, "It will be returned\nto its original state.", 47, 21, AlignLeft, AlignTop); + dialog_ex_set_left_button_text(dialog_ex, "Cancel"); + dialog_ex_set_right_button_text(dialog_ex, "Restore"); + dialog_ex_set_context(dialog_ex, nfc); + dialog_ex_set_result_callback(dialog_ex, nfc_scene_restore_original_confirm_dialog_callback); + + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewDialogEx); +} + +bool nfc_scene_restore_original_confirm_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == DialogExResultRight) { + if(!nfc_device_restore(nfc->dev, true)) { + scene_manager_search_and_switch_to_previous_scene( + nfc->scene_manager, NfcSceneStart); + } else { + scene_manager_next_scene(nfc->scene_manager, NfcSceneRestoreOriginal); + } + consumed = true; + } else if(event.event == DialogExResultLeft) { + consumed = scene_manager_previous_scene(nfc->scene_manager); + } + } else if(event.type == SceneManagerEventTypeBack) { + consumed = true; + } + + return consumed; +} + +void nfc_scene_restore_original_confirm_on_exit(void* context) { + Nfc* nfc = context; + + // Clean view + dialog_ex_reset(nfc->dialog_ex); +} diff --git a/applications/nfc/scenes/nfc_scene_retry_confirm.c b/applications/nfc/scenes/nfc_scene_retry_confirm.c new file mode 100644 index 000000000..366582ea8 --- /dev/null +++ b/applications/nfc/scenes/nfc_scene_retry_confirm.c @@ -0,0 +1,47 @@ +#include "../nfc_i.h" + +void nfc_scene_retry_confirm_dialog_callback(DialogExResult result, void* context) { + Nfc* nfc = context; + + view_dispatcher_send_custom_event(nfc->view_dispatcher, result); +} + +void nfc_scene_retry_confirm_on_enter(void* context) { + Nfc* nfc = context; + DialogEx* dialog_ex = nfc->dialog_ex; + + dialog_ex_set_left_button_text(dialog_ex, "Retry"); + dialog_ex_set_right_button_text(dialog_ex, "Stay"); + dialog_ex_set_header(dialog_ex, "Retry Reading?", 64, 11, AlignCenter, AlignTop); + dialog_ex_set_text( + dialog_ex, "All unsaved data will be\nlost!", 64, 25, AlignCenter, AlignTop); + dialog_ex_set_context(dialog_ex, nfc); + dialog_ex_set_result_callback(dialog_ex, nfc_scene_retry_confirm_dialog_callback); + + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewDialogEx); +} + +bool nfc_scene_retry_confirm_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == DialogExResultRight) { + consumed = scene_manager_previous_scene(nfc->scene_manager); + } else if(event.event == DialogExResultLeft) { + consumed = scene_manager_search_and_switch_to_previous_scene( + nfc->scene_manager, NfcSceneRead); + } + } else if(event.type == SceneManagerEventTypeBack) { + consumed = true; + } + + return consumed; +} + +void nfc_scene_retry_confirm_on_exit(void* context) { + Nfc* nfc = context; + + // Clean view + dialog_ex_reset(nfc->dialog_ex); +} diff --git a/applications/nfc/scenes/nfc_scene_rpc.c b/applications/nfc/scenes/nfc_scene_rpc.c index b94bf424e..94beccc6b 100644 --- a/applications/nfc/scenes/nfc_scene_rpc.c +++ b/applications/nfc/scenes/nfc_scene_rpc.c @@ -2,24 +2,75 @@ void nfc_scene_rpc_on_enter(void* context) { Nfc* nfc = context; - Widget* widget = nfc->widget; + Popup* popup = nfc->popup; - widget_add_text_box_element( - widget, 0, 0, 128, 28, AlignCenter, AlignCenter, "RPC mode", false); + popup_set_header(popup, "NFC", 82, 28, AlignCenter, AlignBottom); + popup_set_text(popup, "RPC mode", 82, 32, AlignCenter, AlignTop); + + popup_set_icon(popup, 2, 14, &I_Warning_30x23); // TODO: icon + + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup); notification_message(nfc->notifications, &sequence_display_backlight_on); +} + +static bool nfc_scene_rpc_emulate_callback(NfcWorkerEvent event, void* context) { + UNUSED(event); + Nfc* nfc = context; - view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); + nfc->rpc_state = NfcRpcStateEmulated; + return true; } bool nfc_scene_rpc_on_event(void* context, SceneManagerEvent event) { Nfc* nfc = context; + Popup* popup = nfc->popup; bool consumed = false; if(event.type == SceneManagerEventTypeCustom) { consumed = true; if(event.event == NfcCustomEventViewExit) { + rpc_system_app_confirm(nfc->rpc_ctx, RpcAppEventAppExit, true); + view_dispatcher_stop(nfc->view_dispatcher); + nfc_blink_stop(nfc); + } else if(event.event == NfcCustomEventRpcSessionClose) { + rpc_system_app_set_callback(nfc->rpc_ctx, NULL, NULL); + nfc->rpc_ctx = NULL; view_dispatcher_stop(nfc->view_dispatcher); + nfc_blink_stop(nfc); + } else if(event.event == NfcCustomEventRpcLoad) { + bool result = false; + const char* arg = rpc_system_app_get_data(nfc->rpc_ctx); + if((arg) && (nfc->rpc_state == NfcRpcStateIdle)) { + if(nfc_device_load(nfc->dev, arg, false)) { + if(nfc->dev->format == NfcDeviceSaveFormatMifareUl) { + nfc_worker_start( + nfc->worker, + NfcWorkerStateMfUltralightEmulate, + &nfc->dev->dev_data, + nfc_scene_rpc_emulate_callback, + nfc); + } else if(nfc->dev->format == NfcDeviceSaveFormatMifareClassic) { + nfc_worker_start( + nfc->worker, + NfcWorkerStateMfClassicEmulate, + &nfc->dev->dev_data, + nfc_scene_rpc_emulate_callback, + nfc); + } else { + nfc_worker_start( + nfc->worker, NfcWorkerStateUidEmulate, &nfc->dev->dev_data, NULL, nfc); + } + nfc->rpc_state = NfcRpcStateEmulating; + result = true; + + nfc_blink_start(nfc); + nfc_text_store_set(nfc, "emulating\n%s", nfc->dev->dev_name); + popup_set_text(popup, nfc->text_store, 82, 32, AlignCenter, AlignTop); + } + } + + rpc_system_app_confirm(nfc->rpc_ctx, RpcAppEventLoadFile, result); } } return consumed; @@ -27,8 +78,11 @@ bool nfc_scene_rpc_on_event(void* context, SceneManagerEvent event) { void nfc_scene_rpc_on_exit(void* context) { Nfc* nfc = context; + Popup* popup = nfc->popup; - nfc_rpc_exit_callback(nfc); + nfc_blink_stop(nfc); - widget_reset(nfc->widget); + popup_set_header(popup, NULL, 0, 0, AlignCenter, AlignBottom); + popup_set_text(popup, NULL, 0, 0, AlignCenter, AlignTop); + popup_set_icon(popup, 0, 0, NULL); } diff --git a/applications/nfc/scenes/nfc_scene_run_emv_app_confirm.c b/applications/nfc/scenes/nfc_scene_run_emv_app_confirm.c deleted file mode 100755 index 5ee9f442c..000000000 --- a/applications/nfc/scenes/nfc_scene_run_emv_app_confirm.c +++ /dev/null @@ -1,49 +0,0 @@ -#include "../nfc_i.h" - -void nfc_scene_run_emv_app_confirm_dialog_callback(DialogExResult result, void* context) { - Nfc* nfc = context; - - view_dispatcher_send_custom_event(nfc->view_dispatcher, result); -} - -void nfc_scene_run_emv_app_confirm_on_enter(void* context) { - Nfc* nfc = context; - - DialogEx* dialog_ex = nfc->dialog_ex; - dialog_ex_set_left_button_text(dialog_ex, "Back"); - dialog_ex_set_right_button_text(dialog_ex, "Run"); - dialog_ex_set_header(dialog_ex, "Run EMV app?", 64, 8, AlignCenter, AlignCenter); - dialog_ex_set_text( - dialog_ex, - "It will try to run card's app\nand detect unencrypted\ndata", - 64, - 18, - AlignCenter, - AlignTop); - dialog_ex_set_context(dialog_ex, nfc); - dialog_ex_set_result_callback(dialog_ex, nfc_scene_run_emv_app_confirm_dialog_callback); - - view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewDialogEx); -} - -bool nfc_scene_run_emv_app_confirm_on_event(void* context, SceneManagerEvent event) { - Nfc* nfc = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == DialogExResultLeft) { - consumed = scene_manager_previous_scene(nfc->scene_manager); - } else if(event.event == DialogExResultRight) { - scene_manager_next_scene(nfc->scene_manager, NfcSceneReadEmvData); - consumed = true; - } - } - return consumed; -} - -void nfc_scene_run_emv_app_confirm_on_exit(void* context) { - Nfc* nfc = context; - - // Clean view - dialog_ex_reset(nfc->dialog_ex); -} diff --git a/applications/nfc/scenes/nfc_scene_save_success.c b/applications/nfc/scenes/nfc_scene_save_success.c index 5c15a509a..a3b17451f 100644 --- a/applications/nfc/scenes/nfc_scene_save_success.c +++ b/applications/nfc/scenes/nfc_scene_save_success.c @@ -27,16 +27,12 @@ bool nfc_scene_save_success_on_event(void* context, SceneManagerEvent event) { if(event.type == SceneManagerEventTypeCustom) { if(event.event == NfcCustomEventViewExit) { - if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneCardMenu)) { + if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSavedMenu)) { consumed = scene_manager_search_and_switch_to_previous_scene( - nfc->scene_manager, NfcSceneCardMenu); - } else if(scene_manager_has_previous_scene( - nfc->scene_manager, NfcSceneMifareDesfireMenu)) { - consumed = scene_manager_search_and_switch_to_previous_scene( - nfc->scene_manager, NfcSceneMifareDesfireMenu); + nfc->scene_manager, NfcSceneSavedMenu); } else { - consumed = scene_manager_search_and_switch_to_previous_scene( - nfc->scene_manager, NfcSceneStart); + consumed = scene_manager_search_and_switch_to_another_scene( + nfc->scene_manager, NfcSceneFileSelect); } } } diff --git a/applications/nfc/scenes/nfc_scene_saved_menu.c b/applications/nfc/scenes/nfc_scene_saved_menu.c index f2b2dea37..e6b08e71b 100644 --- a/applications/nfc/scenes/nfc_scene_saved_menu.c +++ b/applications/nfc/scenes/nfc_scene_saved_menu.c @@ -2,7 +2,8 @@ enum SubmenuIndex { SubmenuIndexEmulate, - SubmenuIndexEdit, + SubmenuIndexEditUid, + SubmenuIndexRename, SubmenuIndexDelete, SubmenuIndexInfo, SubmenuIndexRestoreOriginal, @@ -27,16 +28,20 @@ void nfc_scene_saved_menu_on_enter(void* context) { SubmenuIndexEmulate, nfc_scene_saved_menu_submenu_callback, nfc); + if(nfc->dev->dev_data.protocol == NfcDeviceProtocolUnknown) { + submenu_add_item( + submenu, + "Edit UID", + SubmenuIndexEditUid, + nfc_scene_saved_menu_submenu_callback, + nfc); + } } else if( nfc->dev->format == NfcDeviceSaveFormatMifareUl || nfc->dev->format == NfcDeviceSaveFormatMifareClassic) { submenu_add_item( submenu, "Emulate", SubmenuIndexEmulate, nfc_scene_saved_menu_submenu_callback, nfc); } - submenu_add_item( - submenu, "Edit UID and Name", SubmenuIndexEdit, nfc_scene_saved_menu_submenu_callback, nfc); - submenu_add_item( - submenu, "Delete", SubmenuIndexDelete, nfc_scene_saved_menu_submenu_callback, nfc); submenu_add_item( submenu, "Info", SubmenuIndexInfo, nfc_scene_saved_menu_submenu_callback, nfc); submenu_set_selected_item( @@ -44,11 +49,15 @@ void nfc_scene_saved_menu_on_enter(void* context) { if(nfc->dev->shadow_file_exist) { submenu_add_item( submenu, - "Restore original", + "Restore to original", SubmenuIndexRestoreOriginal, nfc_scene_saved_menu_submenu_callback, nfc); } + submenu_add_item( + submenu, "Rename", SubmenuIndexRename, nfc_scene_saved_menu_submenu_callback, nfc); + submenu_add_item( + submenu, "Delete", SubmenuIndexDelete, nfc_scene_saved_menu_submenu_callback, nfc); view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu); } @@ -61,14 +70,17 @@ bool nfc_scene_saved_menu_on_event(void* context, SceneManagerEvent event) { scene_manager_set_scene_state(nfc->scene_manager, NfcSceneSavedMenu, event.event); if(event.event == SubmenuIndexEmulate) { if(nfc->dev->format == NfcDeviceSaveFormatMifareUl) { - scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateMifareUl); + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightEmulate); } else if(nfc->dev->format == NfcDeviceSaveFormatMifareClassic) { - scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateMifareClassic); + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicEmulate); } else { scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateUid); } consumed = true; - } else if(event.event == SubmenuIndexEdit) { + } else if(event.event == SubmenuIndexRename) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveName); + consumed = true; + } else if(event.event == SubmenuIndexEditUid) { scene_manager_next_scene(nfc->scene_manager, NfcSceneSetUid); consumed = true; } else if(event.event == SubmenuIndexDelete) { @@ -78,12 +90,7 @@ bool nfc_scene_saved_menu_on_event(void* context, SceneManagerEvent event) { scene_manager_next_scene(nfc->scene_manager, NfcSceneDeviceInfo); consumed = true; } else if(event.event == SubmenuIndexRestoreOriginal) { - if(!nfc_device_restore(nfc->dev, true)) { - scene_manager_search_and_switch_to_previous_scene( - nfc->scene_manager, NfcSceneStart); - } else { - scene_manager_next_scene(nfc->scene_manager, NfcSceneRestoreOriginal); - } + scene_manager_next_scene(nfc->scene_manager, NfcSceneRestoreOriginalConfirm); consumed = true; } } diff --git a/applications/nfc/scenes/nfc_scene_scripts_menu.c b/applications/nfc/scenes/nfc_scene_scripts_menu.c deleted file mode 100755 index 446b9d820..000000000 --- a/applications/nfc/scenes/nfc_scene_scripts_menu.c +++ /dev/null @@ -1,82 +0,0 @@ -#include "../nfc_i.h" - -enum SubmenuIndex { - SubmenuIndexBankCard, - SubmenuIndexMifareUltralight, - SubmenuIdexReadMfClassic, - SubmenuIndexMifareDesfire, -}; - -void nfc_scene_scripts_menu_submenu_callback(void* context, uint32_t index) { - Nfc* nfc = context; - view_dispatcher_send_custom_event(nfc->view_dispatcher, index); -} - -void nfc_scene_scripts_menu_on_enter(void* context) { - Nfc* nfc = context; - Submenu* submenu = nfc->submenu; - - submenu_add_item( - submenu, - "Read Bank Card", - SubmenuIndexBankCard, - nfc_scene_scripts_menu_submenu_callback, - nfc); - submenu_add_item( - submenu, - "Read Mifare Ultral/Ntag", - SubmenuIndexMifareUltralight, - nfc_scene_scripts_menu_submenu_callback, - nfc); - submenu_add_item( - submenu, - "Read Mifare Classic", - SubmenuIdexReadMfClassic, - nfc_scene_scripts_menu_submenu_callback, - nfc); - submenu_add_item( - submenu, - "Read Mifare DESFire", - SubmenuIndexMifareDesfire, - nfc_scene_scripts_menu_submenu_callback, - nfc); - submenu_set_selected_item( - nfc->submenu, scene_manager_get_scene_state(nfc->scene_manager, NfcSceneScriptsMenu)); - view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu); -} - -bool nfc_scene_scripts_menu_on_event(void* context, SceneManagerEvent event) { - Nfc* nfc = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == SubmenuIndexBankCard) { - scene_manager_set_scene_state( - nfc->scene_manager, NfcSceneScriptsMenu, SubmenuIndexBankCard); - scene_manager_next_scene(nfc->scene_manager, NfcSceneReadEmvApp); - consumed = true; - } else if(event.event == SubmenuIndexMifareUltralight) { - scene_manager_set_scene_state( - nfc->scene_manager, NfcSceneScriptsMenu, SubmenuIndexMifareUltralight); - scene_manager_next_scene(nfc->scene_manager, NfcSceneReadMifareUl); - consumed = true; - } else if(event.event == SubmenuIdexReadMfClassic) { - scene_manager_set_scene_state( - nfc->scene_manager, NfcSceneScriptsMenu, SubmenuIdexReadMfClassic); - scene_manager_next_scene(nfc->scene_manager, NfcSceneReadMifareClassic); - consumed = true; - } else if(event.event == SubmenuIndexMifareDesfire) { - scene_manager_set_scene_state( - nfc->scene_manager, NfcSceneScriptsMenu, SubmenuIndexMifareDesfire); - scene_manager_next_scene(nfc->scene_manager, NfcSceneReadMifareDesfire); - consumed = true; - } - } - - return consumed; -} - -void nfc_scene_scripts_menu_on_exit(void* context) { - Nfc* nfc = context; - submenu_reset(nfc->submenu); -} diff --git a/applications/nfc/scenes/nfc_scene_set_uid.c b/applications/nfc/scenes/nfc_scene_set_uid.c index 6fe807ced..0ff289710 100755 --- a/applications/nfc/scenes/nfc_scene_set_uid.c +++ b/applications/nfc/scenes/nfc_scene_set_uid.c @@ -31,8 +31,16 @@ bool nfc_scene_set_uid_on_event(void* context, SceneManagerEvent event) { if(event.type == SceneManagerEventTypeCustom) { if(event.event == NfcCustomEventByteInputDone) { DOLPHIN_DEED(DolphinDeedNfcAdd); - scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveName); - consumed = true; + if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSavedMenu)) { + nfc->dev->dev_data.nfc_data = nfc->dev_edit_data; + if(nfc_device_save(nfc->dev, nfc->dev->dev_name)) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveSuccess); + consumed = true; + } + } else { + scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveName); + consumed = true; + } } } return consumed; diff --git a/applications/nfc/scenes/nfc_scene_start.c b/applications/nfc/scenes/nfc_scene_start.c index d64aa76e3..01ffb46b8 100644 --- a/applications/nfc/scenes/nfc_scene_start.c +++ b/applications/nfc/scenes/nfc_scene_start.c @@ -2,8 +2,9 @@ enum SubmenuIndex { SubmenuIndexRead, - SubmenuIndexRunScript, + SubmenuIndexDetectReader, SubmenuIndexSaved, + SubmenuIndexExtraAction, SubmenuIndexAddManualy, SubmenuIndexDebug, }; @@ -18,15 +19,12 @@ void nfc_scene_start_on_enter(void* context) { Nfc* nfc = context; Submenu* submenu = nfc->submenu; + submenu_add_item(submenu, "Read", SubmenuIndexRead, nfc_scene_start_submenu_callback, nfc); submenu_add_item( - submenu, "Read Card", SubmenuIndexRead, nfc_scene_start_submenu_callback, nfc); - submenu_add_item( - submenu, - "Run Special Action", - SubmenuIndexRunScript, - nfc_scene_start_submenu_callback, - nfc); + submenu, "Detect Reader", SubmenuIndexDetectReader, nfc_scene_start_submenu_callback, nfc); submenu_add_item(submenu, "Saved", SubmenuIndexSaved, nfc_scene_start_submenu_callback, nfc); + submenu_add_item( + submenu, "Extra Actions", SubmenuIndexExtraAction, nfc_scene_start_submenu_callback, nfc); submenu_add_item( submenu, "Add Manually", SubmenuIndexAddManualy, nfc_scene_start_submenu_callback, nfc); @@ -48,14 +46,17 @@ bool nfc_scene_start_on_event(void* context, SceneManagerEvent event) { if(event.type == SceneManagerEventTypeCustom) { if(event.event == SubmenuIndexRead) { - scene_manager_next_scene(nfc->scene_manager, NfcSceneReadCard); + scene_manager_next_scene(nfc->scene_manager, NfcSceneRead); consumed = true; - } else if(event.event == SubmenuIndexRunScript) { - scene_manager_next_scene(nfc->scene_manager, NfcSceneScriptsMenu); + } else if(event.event == SubmenuIndexDetectReader) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneDetectReader); consumed = true; } else if(event.event == SubmenuIndexSaved) { scene_manager_next_scene(nfc->scene_manager, NfcSceneFileSelect); consumed = true; + } else if(event.event == SubmenuIndexExtraAction) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneExtraActions); + consumed = true; } else if(event.event == SubmenuIndexAddManualy) { scene_manager_next_scene(nfc->scene_manager, NfcSceneSetType); consumed = true; diff --git a/applications/nfc/views/dict_attack.c b/applications/nfc/views/dict_attack.c index 0f9da494a..b4674fd31 100644 --- a/applications/nfc/views/dict_attack.c +++ b/applications/nfc/views/dict_attack.c @@ -1,83 +1,69 @@ #include "dict_attack.h" -#include +#include #include typedef enum { - DictAttackStateSearchCard, - DictAttackStateSearchKeys, + DictAttackStateRead, DictAttackStateCardRemoved, - DictAttackStateSuccess, - DictAttackStateFail, } DictAttackState; struct DictAttack { View* view; - DictAttackResultCallback callback; + DictAttackCallback callback; void* context; }; typedef struct { DictAttackState state; MfClassicType type; - uint8_t current_sector; - uint8_t total_sectors; - uint8_t keys_a_found; - uint8_t keys_a_total; - uint8_t keys_b_found; - uint8_t keys_b_total; + string_t header; + uint8_t sectors_total; + uint8_t sectors_read; + uint8_t sector_current; + uint8_t keys_total; + uint8_t keys_found; + uint16_t dict_keys_total; + uint16_t dict_keys_current; } DictAttackViewModel; static void dict_attack_draw_callback(Canvas* canvas, void* model) { DictAttackViewModel* m = model; - if(m->state == DictAttackStateSearchCard) { + if(m->state == DictAttackStateCardRemoved) { canvas_set_font(canvas, FontPrimary); - canvas_draw_str_aligned( - canvas, 64, 32, AlignCenter, AlignCenter, "Detecting Mifare Classic"); - } else if(m->state == DictAttackStateCardRemoved) { + canvas_draw_str_aligned(canvas, 64, 4, AlignCenter, AlignTop, "Lost the tag!"); + canvas_set_font(canvas, FontSecondary); + elements_multiline_text_aligned( + canvas, 64, 23, AlignCenter, AlignTop, "Make sure the tag is\npositioned correctly."); + } else if(m->state == DictAttackStateRead) { + char draw_str[32] = {}; canvas_set_font(canvas, FontPrimary); - canvas_draw_str_aligned( - canvas, 64, 32, AlignCenter, AlignTop, "Place card back to flipper"); - } else { - char draw_str[32]; - if(m->state == DictAttackStateSearchKeys) { - snprintf( - draw_str, sizeof(draw_str), "Searching keys for sector %d", m->current_sector); - canvas_draw_str_aligned(canvas, 64, 2, AlignCenter, AlignTop, draw_str); - } else if(m->state == DictAttackStateSuccess) { - canvas_draw_str_aligned(canvas, 64, 2, AlignCenter, AlignTop, "Complete!"); - elements_button_right(canvas, "More"); - } else if(m->state == DictAttackStateFail) { - canvas_draw_str_aligned( - canvas, 64, 2, AlignCenter, AlignTop, "Failed to read any sector"); + canvas_draw_str_aligned(canvas, 64, 2, AlignCenter, AlignTop, string_get_cstr(m->header)); + canvas_set_font(canvas, FontSecondary); + float dict_progress = m->dict_keys_total == 0 ? + 0 : + (float)(m->dict_keys_current) / (float)(m->dict_keys_total); + float progress = m->sectors_total == 0 ? 0 : + ((float)(m->sector_current) + dict_progress) / + (float)(m->sectors_total); + if(progress > 1.0) { + progress = 1.0; } - uint16_t keys_found = m->keys_a_found + m->keys_b_found; - uint16_t keys_total = m->keys_a_total + m->keys_b_total; - float progress = (float)(m->current_sector) / (float)(m->total_sectors); - elements_progress_bar(canvas, 5, 12, 120, progress); + elements_progress_bar(canvas, 5, 15, 120, progress); canvas_set_font(canvas, FontSecondary); - snprintf(draw_str, sizeof(draw_str), "Total keys found: %d/%d", keys_found, keys_total); - canvas_draw_str_aligned(canvas, 1, 23, AlignLeft, AlignTop, draw_str); + snprintf(draw_str, sizeof(draw_str), "Keys found: %d/%d", m->keys_found, m->keys_total); + canvas_draw_str_aligned(canvas, 1, 28, AlignLeft, AlignTop, draw_str); snprintf( - draw_str, sizeof(draw_str), "A keys found: %d/%d", m->keys_a_found, m->keys_a_total); - canvas_draw_str_aligned(canvas, 1, 34, AlignLeft, AlignTop, draw_str); - snprintf( - draw_str, sizeof(draw_str), "B keys found: %d/%d", m->keys_b_found, m->keys_b_total); - canvas_draw_str_aligned(canvas, 1, 45, AlignLeft, AlignTop, draw_str); + draw_str, sizeof(draw_str), "Sectors Read: %d/%d", m->sectors_read, m->sectors_total); + canvas_draw_str_aligned(canvas, 1, 40, AlignLeft, AlignTop, draw_str); } + elements_button_center(canvas, "Skip"); } static bool dict_attack_input_callback(InputEvent* event, void* context) { DictAttack* dict_attack = context; bool consumed = false; - DictAttackState state; - with_view_model( - dict_attack->view, (DictAttackViewModel * model) { - state = model->state; - return false; - }); - if(state == DictAttackStateSuccess && event->type == InputTypeShort && - event->key == InputKeyRight) { + if(event->type == InputTypeShort && event->key == InputKeyOk) { if(dict_attack->callback) { dict_attack->callback(dict_attack->context); } @@ -93,11 +79,21 @@ DictAttack* dict_attack_alloc() { view_set_draw_callback(dict_attack->view, dict_attack_draw_callback); view_set_input_callback(dict_attack->view, dict_attack_input_callback); view_set_context(dict_attack->view, dict_attack); + with_view_model( + dict_attack->view, (DictAttackViewModel * model) { + string_init(model->header); + return false; + }); return dict_attack; } void dict_attack_free(DictAttack* dict_attack) { furi_assert(dict_attack); + with_view_model( + dict_attack->view, (DictAttackViewModel * model) { + string_clear(model->header); + return false; + }); view_free(dict_attack->view); free(dict_attack); } @@ -106,8 +102,17 @@ void dict_attack_reset(DictAttack* dict_attack) { furi_assert(dict_attack); with_view_model( dict_attack->view, (DictAttackViewModel * model) { - memset(model, 0, sizeof(DictAttackViewModel)); - return true; + model->state = DictAttackStateRead; + model->type = MfClassicType1k; + model->sectors_total = 0; + model->sectors_read = 0; + model->sector_current = 0; + model->keys_total = 0; + model->keys_found = 0; + model->dict_keys_total = 0; + model->dict_keys_current = 0; + string_reset(model->header); + return false; }); } @@ -116,78 +121,110 @@ View* dict_attack_get_view(DictAttack* dict_attack) { return dict_attack->view; } -void dict_attack_set_result_callback( - DictAttack* dict_attack, - DictAttackResultCallback callback, - void* context) { +void dict_attack_set_callback(DictAttack* dict_attack, DictAttackCallback callback, void* context) { furi_assert(dict_attack); furi_assert(callback); dict_attack->callback = callback; dict_attack->context = context; } -void dict_attack_card_detected(DictAttack* dict_attack, MfClassicType type) { +void dict_attack_set_header(DictAttack* dict_attack, const char* header) { furi_assert(dict_attack); + furi_assert(header); + with_view_model( dict_attack->view, (DictAttackViewModel * model) { - model->state = DictAttackStateSearchKeys; - if(type == MfClassicType1k) { - model->total_sectors = 16; - model->keys_a_total = 16; - model->keys_b_total = 16; - } else if(type == MfClassicType4k) { - model->total_sectors = 40; - model->keys_a_total = 40; - model->keys_b_total = 40; - } + string_set_str(model->header, header); return true; }); } -void dict_attack_card_removed(DictAttack* dict_attack) { +void dict_attack_set_card_detected(DictAttack* dict_attack, MfClassicType type) { furi_assert(dict_attack); with_view_model( dict_attack->view, (DictAttackViewModel * model) { - if(model->state == DictAttackStateSearchKeys) { - model->state = DictAttackStateCardRemoved; - } else { - model->state = DictAttackStateSearchCard; - } + model->state = DictAttackStateRead; + model->sectors_total = mf_classic_get_total_sectors_num(type); + model->keys_total = model->sectors_total * 2; + return true; + }); +} + +void dict_attack_set_card_removed(DictAttack* dict_attack) { + furi_assert(dict_attack); + with_view_model( + dict_attack->view, (DictAttackViewModel * model) { + model->state = DictAttackStateCardRemoved; return true; }); } -void dict_attack_inc_curr_sector(DictAttack* dict_attack) { +void dict_attack_set_sector_read(DictAttack* dict_attack, uint8_t sec_read) { furi_assert(dict_attack); with_view_model( dict_attack->view, (DictAttackViewModel * model) { - model->current_sector++; + model->sectors_read = sec_read; return true; }); } -void dict_attack_inc_found_key(DictAttack* dict_attack, MfClassicKey key) { +void dict_attack_set_keys_found(DictAttack* dict_attack, uint8_t keys_found) { furi_assert(dict_attack); with_view_model( dict_attack->view, (DictAttackViewModel * model) { - model->state = DictAttackStateSearchKeys; - if(key == MfClassicKeyA) { - model->keys_a_found++; - } else if(key == MfClassicKeyB) { - model->keys_b_found++; + model->keys_found = keys_found; + return true; + }); +} + +void dict_attack_set_current_sector(DictAttack* dict_attack, uint8_t curr_sec) { + furi_assert(dict_attack); + with_view_model( + dict_attack->view, (DictAttackViewModel * model) { + model->sector_current = curr_sec; + model->dict_keys_current = 0; + return true; + }); +} + +void dict_attack_inc_current_sector(DictAttack* dict_attack) { + furi_assert(dict_attack); + with_view_model( + dict_attack->view, (DictAttackViewModel * model) { + if(model->sector_current < model->sectors_total) { + model->sector_current++; + model->dict_keys_current = 0; } return true; }); } -void dict_attack_set_result(DictAttack* dict_attack, bool success) { +void dict_attack_inc_keys_found(DictAttack* dict_attack) { + furi_assert(dict_attack); + with_view_model( + dict_attack->view, (DictAttackViewModel * model) { + if(model->keys_found < model->keys_total) { + model->keys_found++; + } + return true; + }); +} + +void dict_attack_set_total_dict_keys(DictAttack* dict_attack, uint16_t dict_keys_total) { + furi_assert(dict_attack); + with_view_model( + dict_attack->view, (DictAttackViewModel * model) { + model->dict_keys_total = dict_keys_total; + return true; + }); +} + +void dict_attack_inc_current_dict_key(DictAttack* dict_attack, uint16_t keys_tried) { furi_assert(dict_attack); with_view_model( dict_attack->view, (DictAttackViewModel * model) { - if(success) { - model->state = DictAttackStateSuccess; - } else { - model->state = DictAttackStateFail; + if(model->dict_keys_current + keys_tried < model->dict_keys_total) { + model->dict_keys_current += keys_tried; } return true; }); diff --git a/applications/nfc/views/dict_attack.h b/applications/nfc/views/dict_attack.h index f8d5afca5..684f17f06 100644 --- a/applications/nfc/views/dict_attack.h +++ b/applications/nfc/views/dict_attack.h @@ -3,11 +3,11 @@ #include #include -#include +#include typedef struct DictAttack DictAttack; -typedef void (*DictAttackResultCallback)(void* context); +typedef void (*DictAttackCallback)(void* context); DictAttack* dict_attack_alloc(); @@ -17,17 +17,24 @@ void dict_attack_reset(DictAttack* dict_attack); View* dict_attack_get_view(DictAttack* dict_attack); -void dict_attack_set_result_callback( - DictAttack* dict_attack, - DictAttackResultCallback callback, - void* context); +void dict_attack_set_callback(DictAttack* dict_attack, DictAttackCallback callback, void* context); -void dict_attack_card_detected(DictAttack* dict_attack, MfClassicType type); +void dict_attack_set_header(DictAttack* dict_attack, const char* header); -void dict_attack_card_removed(DictAttack* dict_attack); +void dict_attack_set_card_detected(DictAttack* dict_attack, MfClassicType type); -void dict_attack_inc_curr_sector(DictAttack* dict_attack); +void dict_attack_set_card_removed(DictAttack* dict_attack); -void dict_attack_inc_found_key(DictAttack* dict_attack, MfClassicKey key); +void dict_attack_set_sector_read(DictAttack* dict_attack, uint8_t sec_read); -void dict_attack_set_result(DictAttack* dict_attack, bool success); +void dict_attack_set_keys_found(DictAttack* dict_attack, uint8_t keys_found); + +void dict_attack_set_current_sector(DictAttack* dict_attack, uint8_t curr_sec); + +void dict_attack_inc_current_sector(DictAttack* dict_attack); + +void dict_attack_inc_keys_found(DictAttack* dict_attack); + +void dict_attack_set_total_dict_keys(DictAttack* dict_attack, uint16_t dict_keys_total); + +void dict_attack_inc_current_dict_key(DictAttack* dict_attack, uint16_t keys_tried); diff --git a/applications/notification/notification.h b/applications/notification/notification.h index 14ca0ac2b..b38620f0f 100644 --- a/applications/notification/notification.h +++ b/applications/notification/notification.h @@ -7,6 +7,8 @@ extern "C" { #endif +#define RECORD_NOTIFICATION "notification" + typedef struct NotificationApp NotificationApp; typedef struct { float frequency; diff --git a/applications/notification/notification_app.c b/applications/notification/notification_app.c index 437d20ab8..640bd7d71 100644 --- a/applications/notification/notification_app.c +++ b/applications/notification/notification_app.c @@ -398,7 +398,7 @@ void notification_process_internal_message(NotificationApp* app, NotificationApp static bool notification_load_settings(NotificationApp* app) { NotificationSettings settings; - File* file = storage_file_alloc(furi_record_open("storage")); + File* file = storage_file_alloc(furi_record_open(RECORD_STORAGE)); const size_t settings_size = sizeof(NotificationSettings); FURI_LOG_I(TAG, "loading settings from \"%s\"", NOTIFICATION_SETTINGS_PATH); @@ -430,14 +430,14 @@ static bool notification_load_settings(NotificationApp* app) { storage_file_close(file); storage_file_free(file); - furi_record_close("storage"); + furi_record_close(RECORD_STORAGE); return fs_result; }; static bool notification_save_settings(NotificationApp* app) { NotificationSettings settings; - File* file = storage_file_alloc(furi_record_open("storage")); + File* file = storage_file_alloc(furi_record_open(RECORD_STORAGE)); const size_t settings_size = sizeof(NotificationSettings); FURI_LOG_I(TAG, "saving settings to \"%s\"", NOTIFICATION_SETTINGS_PATH); @@ -465,7 +465,7 @@ static bool notification_save_settings(NotificationApp* app) { storage_file_close(file); storage_file_free(file); - furi_record_close("storage"); + furi_record_close(RECORD_STORAGE); return fs_result; }; @@ -512,7 +512,7 @@ static NotificationApp* notification_app_alloc() { app->settings.version = NOTIFICATION_SETTINGS_VERSION; // display backlight control - app->event_record = furi_record_open("input_events"); + app->event_record = furi_record_open(RECORD_INPUT_EVENTS); furi_pubsub_subscribe(app->event_record, input_event_callback, app); notification_message(app, &sequence_display_backlight_on); @@ -535,7 +535,7 @@ int32_t notification_srv(void* p) { notification_apply_internal_led_layer(&app->led[1], 0x00); notification_apply_internal_led_layer(&app->led[2], 0x00); - furi_record_create("notification", app); + furi_record_create(RECORD_NOTIFICATION, app); NotificationAppMessage message; while(1) { diff --git a/applications/notification/notification_app.h b/applications/notification/notification_app.h index f5c7cc46e..88194bfbd 100644 --- a/applications/notification/notification_app.h +++ b/applications/notification/notification_app.h @@ -2,6 +2,7 @@ #include #include "notification.h" #include "notification_messages.h" +#include "notification_settings_filename.h" #define NOTIFICATION_LED_COUNT 3 #define NOTIFICATION_EVENT_COMPLETE 0x00000001U @@ -32,7 +33,7 @@ typedef struct { } NotificationLedLayer; #define NOTIFICATION_SETTINGS_VERSION 0x01 -#define NOTIFICATION_SETTINGS_PATH "/int/notification.settings" +#define NOTIFICATION_SETTINGS_PATH INT_PATH(NOTIFICATION_SETTINGS_FILE_NAME) typedef struct { uint8_t version; diff --git a/applications/notification/notification_messages.c b/applications/notification/notification_messages.c index b469cb5f1..d795c55d9 100644 --- a/applications/notification/notification_messages.c +++ b/applications/notification/notification_messages.c @@ -431,6 +431,54 @@ const NotificationSequence sequence_blink_white_100 = { NULL, }; +// Hardware blink +const NotificationSequence sequence_blink_start_blue = { + &message_blink_start_10, + &message_blink_set_color_blue, + &message_do_not_reset, + NULL, +}; + +const NotificationSequence sequence_blink_start_red = { + &message_blink_start_10, + &message_blink_set_color_red, + &message_do_not_reset, + NULL, +}; + +const NotificationSequence sequence_blink_start_green = { + &message_blink_start_10, + &message_blink_set_color_green, + &message_do_not_reset, + NULL, +}; + +const NotificationSequence sequence_blink_start_yellow = { + &message_blink_start_10, + &message_blink_set_color_yellow, + &message_do_not_reset, + NULL, +}; + +const NotificationSequence sequence_blink_start_cyan = { + &message_blink_start_10, + &message_blink_set_color_cyan, + &message_do_not_reset, + NULL, +}; + +const NotificationSequence sequence_blink_start_magenta = { + &message_blink_start_10, + &message_blink_set_color_magenta, + &message_do_not_reset, + NULL, +}; + +const NotificationSequence sequence_blink_stop = { + &message_blink_stop, + NULL, +}; + //General const NotificationSequence sequence_single_vibro = { &message_vibro_on, diff --git a/applications/notification/notification_messages.h b/applications/notification/notification_messages.h index 92a105f6a..100796917 100644 --- a/applications/notification/notification_messages.h +++ b/applications/notification/notification_messages.h @@ -122,6 +122,15 @@ extern const NotificationSequence sequence_blink_cyan_100; extern const NotificationSequence sequence_blink_magenta_100; extern const NotificationSequence sequence_blink_white_100; +// Hardware blink +extern const NotificationSequence sequence_blink_start_blue; +extern const NotificationSequence sequence_blink_start_red; +extern const NotificationSequence sequence_blink_start_green; +extern const NotificationSequence sequence_blink_start_yellow; +extern const NotificationSequence sequence_blink_start_cyan; +extern const NotificationSequence sequence_blink_start_magenta; +extern const NotificationSequence sequence_blink_stop; + // General extern const NotificationSequence sequence_single_vibro; extern const NotificationSequence sequence_double_vibro; diff --git a/applications/notification/notification_settings_app.c b/applications/notification/notification_settings_app.c index bcb1b6a27..894938f4c 100644 --- a/applications/notification/notification_settings_app.c +++ b/applications/notification/notification_settings_app.c @@ -126,8 +126,8 @@ static uint32_t notification_app_settings_exit(void* context) { static NotificationAppSettings* alloc_settings() { NotificationAppSettings* app = malloc(sizeof(NotificationAppSettings)); - app->notification = furi_record_open("notification"); - app->gui = furi_record_open("gui"); + app->notification = furi_record_open(RECORD_NOTIFICATION); + app->gui = furi_record_open(RECORD_GUI); app->variable_item_list = variable_item_list_alloc(); View* view = variable_item_list_get_view(app->variable_item_list); @@ -184,8 +184,8 @@ static void free_settings(NotificationAppSettings* app) { variable_item_list_free(app->variable_item_list); view_dispatcher_free(app->view_dispatcher); - furi_record_close("gui"); - furi_record_close("notification"); + furi_record_close(RECORD_GUI); + furi_record_close(RECORD_NOTIFICATION); free(app); } diff --git a/applications/notification/notification_settings_filename.h b/applications/notification/notification_settings_filename.h new file mode 100644 index 000000000..d9ed596ec --- /dev/null +++ b/applications/notification/notification_settings_filename.h @@ -0,0 +1,3 @@ +#pragma once + +#define NOTIFICATION_SETTINGS_FILE_NAME ".notification.settings" diff --git a/applications/picopass/picopass.c b/applications/picopass/picopass.c index 191895482..8c0db4e2a 100644 --- a/applications/picopass/picopass.c +++ b/applications/picopass/picopass.c @@ -39,12 +39,12 @@ Picopass* picopass_alloc() { picopass->dev = picopass_device_alloc(); // Open GUI record - picopass->gui = furi_record_open("gui"); + picopass->gui = furi_record_open(RECORD_GUI); view_dispatcher_attach_to_gui( picopass->view_dispatcher, picopass->gui, ViewDispatcherTypeFullscreen); // Open Notification record - picopass->notifications = furi_record_open("notification"); + picopass->notifications = furi_record_open(RECORD_NOTIFICATION); // Submenu picopass->submenu = submenu_alloc(); @@ -105,11 +105,11 @@ void picopass_free(Picopass* picopass) { scene_manager_free(picopass->scene_manager); // GUI - furi_record_close("gui"); + furi_record_close(RECORD_GUI); picopass->gui = NULL; // Notifications - furi_record_close("notification"); + furi_record_close(RECORD_NOTIFICATION); picopass->notifications = NULL; free(picopass); diff --git a/applications/picopass/picopass_device.c b/applications/picopass/picopass_device.c index 8cce52880..9b422edd3 100644 --- a/applications/picopass/picopass_device.c +++ b/applications/picopass/picopass_device.c @@ -10,8 +10,11 @@ static const uint32_t picopass_file_version = 1; PicopassDevice* picopass_device_alloc() { PicopassDevice* picopass_dev = malloc(sizeof(PicopassDevice)); - picopass_dev->storage = furi_record_open("storage"); - picopass_dev->dialogs = furi_record_open("dialogs"); + picopass_dev->dev_data.pacs.legacy = false; + picopass_dev->dev_data.pacs.se_enabled = false; + picopass_dev->dev_data.pacs.pin_length = 0; + picopass_dev->storage = furi_record_open(RECORD_STORAGE); + picopass_dev->dialogs = furi_record_open(RECORD_DIALOGS); return picopass_dev; } @@ -32,7 +35,7 @@ static bool picopass_device_save_file( bool saved = false; FlipperFormat* file = flipper_format_file_alloc(dev->storage); PicopassPacs* pacs = &dev->dev_data.pacs; - ApplicationArea* AA1 = &dev->dev_data.AA1; + PicopassBlock* AA1 = dev->dev_data.AA1; string_t temp_str; string_init(temp_str); @@ -54,40 +57,40 @@ static bool picopass_device_save_file( if(!flipper_format_file_open_always(file, string_get_cstr(temp_str))) break; if(dev->format == PicopassDeviceSaveFormatHF) { + uint32_t fc = pacs->record.FacilityCode; + uint32_t cn = pacs->record.CardNumber; // Write header if(!flipper_format_write_header_cstr(file, picopass_file_header, picopass_file_version)) break; if(pacs->record.valid) { - if(!flipper_format_write_uint32( - file, "Facility Code", (uint32_t*)&pacs->record.FacilityCode, 1)) - break; - if(!flipper_format_write_uint32( - file, "Card Number", (uint32_t*)&pacs->record.CardNumber, 1)) - break; + if(!flipper_format_write_uint32(file, "Facility Code", &fc, 1)) break; + if(!flipper_format_write_uint32(file, "Card Number", &cn, 1)) break; if(!flipper_format_write_hex( file, "Credential", pacs->credential, PICOPASS_BLOCK_LEN)) break; - if(!flipper_format_write_hex(file, "PIN", pacs->pin0, PICOPASS_BLOCK_LEN)) break; - if(!flipper_format_write_hex(file, "PIN(cont.)", pacs->pin1, PICOPASS_BLOCK_LEN)) - break; - - if(!flipper_format_write_comment_cstr(file, "Picopass blocks")) break; - // TODO: Save CSN, CFG, AA1, etc - bool block_saved = true; - for(size_t i = 0; i < 4; i++) { - string_printf(temp_str, "Block %d", i + 6); + if(pacs->pin_length > 0) { + if(!flipper_format_write_hex(file, "PIN\t\t", pacs->pin0, PICOPASS_BLOCK_LEN)) + break; if(!flipper_format_write_hex( - file, - string_get_cstr(temp_str), - AA1->block[i].data, - PICOPASS_BLOCK_LEN)) { - block_saved = false; + file, "PIN(cont.)\t", pacs->pin1, PICOPASS_BLOCK_LEN)) break; - } } - if(!block_saved) break; - if(!flipper_format_write_comment_cstr(file, "This is currently incomplete")) break; } + if(!flipper_format_write_comment_cstr(file, "Picopass blocks")) break; + bool block_saved = true; + + size_t app_limit = AA1[PICOPASS_CONFIG_BLOCK_INDEX].data[0] < PICOPASS_MAX_APP_LIMIT ? + AA1[PICOPASS_CONFIG_BLOCK_INDEX].data[0] : + PICOPASS_MAX_APP_LIMIT; + for(size_t i = 0; i < app_limit; i++) { + string_printf(temp_str, "Block %d", i); + if(!flipper_format_write_hex( + file, string_get_cstr(temp_str), AA1[i].data, PICOPASS_BLOCK_LEN)) { + block_saved = false; + break; + } + } + if(!block_saved) break; } else if(dev->format == PicopassDeviceSaveFormatLF) { const char* lf_header = "Flipper RFID key"; // Write header @@ -120,7 +123,7 @@ bool picopass_device_save(PicopassDevice* dev, const char* dev_name) { return picopass_device_save_file( dev, dev_name, PICOPASS_APP_FOLDER, PICOPASS_APP_EXTENSION, true); } else if(dev->format == PicopassDeviceSaveFormatLF) { - return picopass_device_save_file(dev, dev_name, "/any/lfrfid", ".rfid", true); + return picopass_device_save_file(dev, dev_name, ANY_PATH("lfrfid"), ".rfid", true); } return false; } @@ -135,12 +138,17 @@ void picopass_device_clear(PicopassDevice* dev) { void picopass_device_free(PicopassDevice* picopass_dev) { furi_assert(picopass_dev); picopass_device_clear(picopass_dev); - furi_record_close("storage"); - furi_record_close("dialogs"); + furi_record_close(RECORD_STORAGE); + furi_record_close(RECORD_DIALOGS); string_clear(picopass_dev->load_path); free(picopass_dev); } void picopass_device_data_clear(PicopassDeviceData* dev_data) { - memset(&dev_data->AA1, 0, sizeof(ApplicationArea)); + for(size_t i = 0; i < PICOPASS_MAX_APP_LIMIT; i++) { + memset(dev_data->AA1[i].data, 0, sizeof(dev_data->AA1[i].data)); + } + dev_data->pacs.legacy = false; + dev_data->pacs.se_enabled = false; + dev_data->pacs.pin_length = 0; } diff --git a/applications/picopass/picopass_device.h b/applications/picopass/picopass_device.h index a0f7a667e..89e031ca7 100644 --- a/applications/picopass/picopass_device.h +++ b/applications/picopass/picopass_device.h @@ -10,8 +10,13 @@ #define PICOPASS_DEV_NAME_MAX_LEN 22 #define PICOPASS_READER_DATA_MAX_SIZE 64 #define PICOPASS_BLOCK_LEN 8 +#define PICOPASS_MAX_APP_LIMIT 32 -#define PICOPASS_APP_FOLDER "/any/picopass" +#define PICOPASS_CSN_BLOCK_INDEX 0 +#define PICOPASS_CONFIG_BLOCK_INDEX 1 +#define PICOPASS_AIA_BLOCK_INDEX 5 + +#define PICOPASS_APP_FOLDER ANY_PATH("picopass") #define PICOPASS_APP_EXTENSION ".picopass" #define PICOPASS_APP_SHADOW_EXTENSION ".pas" @@ -35,7 +40,10 @@ typedef struct { } PicopassWiegandRecord; typedef struct { + bool legacy; + bool se_enabled; bool biometrics; + uint8_t pin_length; PicopassEncryption encryption; uint8_t credential[8]; uint8_t pin0[8]; @@ -44,7 +52,11 @@ typedef struct { } PicopassPacs; typedef struct { - ApplicationArea AA1; + uint8_t data[PICOPASS_BLOCK_LEN]; +} PicopassBlock; + +typedef struct { + PicopassBlock AA1[PICOPASS_MAX_APP_LIMIT]; PicopassPacs pacs; } PicopassDeviceData; diff --git a/applications/picopass/picopass_worker.c b/applications/picopass/picopass_worker.c index 645a1bce9..3079a98c4 100644 --- a/applications/picopass/picopass_worker.c +++ b/applications/picopass/picopass_worker.c @@ -55,12 +55,11 @@ static ReturnCode picopass_worker_parse_wiegand(uint8_t* data, PicopassWiegandRe if(record->bitLength == 26) { uint8_t* v4 = data + 4; - v4[0] = 0; - uint32_t bot = v4[3] | (v4[2] << 8) | (v4[1] << 16) | (v4[0] << 24); record->CardNumber = (bot >> 1) & 0xFFFF; record->FacilityCode = (bot >> 17) & 0xFF; + FURI_LOG_D(TAG, "FC:%u CN: %u\n", record->FacilityCode, record->CardNumber); record->valid = true; } else { record->CardNumber = 0; @@ -84,7 +83,7 @@ PicopassWorker* picopass_worker_alloc() { picopass_worker->callback = NULL; picopass_worker->context = NULL; - picopass_worker->storage = furi_record_open("storage"); + picopass_worker->storage = furi_record_open(RECORD_STORAGE); picopass_worker_change_state(picopass_worker, PicopassWorkerStateReady); @@ -96,7 +95,7 @@ void picopass_worker_free(PicopassWorker* picopass_worker) { furi_thread_free(picopass_worker->thread); - furi_record_close("storage"); + furi_record_close(RECORD_STORAGE); free(picopass_worker); } @@ -165,7 +164,7 @@ ReturnCode picopass_detect_card(int timeout) { return ERR_NONE; } -ReturnCode picopass_read_card(ApplicationArea* AA1) { +ReturnCode picopass_read_card(PicopassBlock* AA1) { rfalPicoPassIdentifyRes idRes; rfalPicoPassSelectRes selRes; rfalPicoPassReadCheckRes rcRes; @@ -205,10 +204,20 @@ ReturnCode picopass_read_card(ApplicationArea* AA1) { return err; } - for(size_t i = 0; i < 4; i++) { - FURI_LOG_D(TAG, "rfalPicoPassPollerReadBlock block %d", i + 6); + rfalPicoPassReadBlockRes csn; + err = rfalPicoPassPollerReadBlock(PICOPASS_CSN_BLOCK_INDEX, &csn); + memcpy(AA1[PICOPASS_CSN_BLOCK_INDEX].data, csn.data, sizeof(csn.data)); + + rfalPicoPassReadBlockRes cfg; + err = rfalPicoPassPollerReadBlock(PICOPASS_CONFIG_BLOCK_INDEX, &cfg); + memcpy(AA1[PICOPASS_CONFIG_BLOCK_INDEX].data, cfg.data, sizeof(cfg.data)); + + size_t app_limit = cfg.data[0] < PICOPASS_MAX_APP_LIMIT ? cfg.data[0] : PICOPASS_MAX_APP_LIMIT; + + for(size_t i = 2; i < app_limit; i++) { + FURI_LOG_D(TAG, "rfalPicoPassPollerReadBlock block %d", i); rfalPicoPassReadBlockRes block; - err = rfalPicoPassPollerReadBlock(i + 6, &block); + err = rfalPicoPassPollerReadBlock(i, &block); if(err != ERR_NONE) { FURI_LOG_E(TAG, "rfalPicoPassPollerReadBlock error %d", err); return err; @@ -217,7 +226,7 @@ ReturnCode picopass_read_card(ApplicationArea* AA1) { FURI_LOG_D( TAG, "rfalPicoPassPollerReadBlock %d %02x%02x%02x%02x%02x%02x%02x%02x", - i + 6, + i, block.data[0], block.data[1], block.data[2], @@ -227,7 +236,7 @@ ReturnCode picopass_read_card(ApplicationArea* AA1) { block.data[6], block.data[7]); - memcpy(&(AA1->block[i]), &block, sizeof(block)); + memcpy(AA1[i].data, block.data, sizeof(block.data)); } return ERR_NONE; @@ -251,7 +260,7 @@ void picopass_worker_detect(PicopassWorker* picopass_worker) { picopass_device_data_clear(picopass_worker->dev_data); PicopassDeviceData* dev_data = picopass_worker->dev_data; - ApplicationArea* AA1 = &dev_data->AA1; + PicopassBlock* AA1 = dev_data->AA1; PicopassPacs* pacs = &dev_data->pacs; ReturnCode err; @@ -263,34 +272,39 @@ void picopass_worker_detect(PicopassWorker* picopass_worker) { FURI_LOG_E(TAG, "picopass_read_card error %d", err); } - pacs->biometrics = AA1->block[0].data[4]; - pacs->encryption = AA1->block[0].data[7]; + // Thank you proxmark! + pacs->legacy = (memcmp(AA1[5].data, "\xff\xff\xff\xff\xff\xff\xff\xff", 8) == 0); + pacs->se_enabled = (memcmp(AA1[5].data, "\xff\xff\xff\x00\x06\xff\xff\xff", 8) == 0); + + pacs->biometrics = AA1[6].data[4]; + pacs->pin_length = AA1[6].data[6] & 0x0F; + pacs->encryption = AA1[6].data[7]; - if(pacs->encryption == 0x17) { + if(pacs->encryption == PicopassDeviceEncryption3DES) { FURI_LOG_D(TAG, "3DES Encrypted"); - err = picopass_worker_decrypt(AA1->block[1].data, pacs->credential); + err = picopass_worker_decrypt(AA1[7].data, pacs->credential); if(err != ERR_NONE) { FURI_LOG_E(TAG, "decrypt error %d", err); break; } - err = picopass_worker_decrypt(AA1->block[2].data, pacs->pin0); + err = picopass_worker_decrypt(AA1[8].data, pacs->pin0); if(err != ERR_NONE) { FURI_LOG_E(TAG, "decrypt error %d", err); break; } - err = picopass_worker_decrypt(AA1->block[3].data, pacs->pin1); + err = picopass_worker_decrypt(AA1[9].data, pacs->pin1); if(err != ERR_NONE) { FURI_LOG_E(TAG, "decrypt error %d", err); break; } - } else if(pacs->encryption == 0x14) { + } else if(pacs->encryption == PicopassDeviceEncryptionNone) { FURI_LOG_D(TAG, "No Encryption"); - memcpy(pacs->credential, AA1->block[1].data, RFAL_PICOPASS_MAX_BLOCK_LEN); - memcpy(pacs->pin0, AA1->block[2].data, RFAL_PICOPASS_MAX_BLOCK_LEN); - memcpy(pacs->pin1, AA1->block[3].data, RFAL_PICOPASS_MAX_BLOCK_LEN); - } else if(pacs->encryption == 0x15) { + memcpy(pacs->credential, AA1[7].data, PICOPASS_BLOCK_LEN); + memcpy(pacs->pin0, AA1[8].data, PICOPASS_BLOCK_LEN); + memcpy(pacs->pin1, AA1[9].data, PICOPASS_BLOCK_LEN); + } else if(pacs->encryption == PicopassDeviceEncryptionDES) { FURI_LOG_D(TAG, "DES Encrypted"); } else { FURI_LOG_D(TAG, "Unknown encryption"); diff --git a/applications/picopass/scenes/picopass_scene_read_card.c b/applications/picopass/scenes/picopass_scene_read_card.c index add05e479..0867898a5 100644 --- a/applications/picopass/scenes/picopass_scene_read_card.c +++ b/applications/picopass/scenes/picopass_scene_read_card.c @@ -13,7 +13,7 @@ void picopass_scene_read_card_on_enter(void* context) { // Setup view Popup* popup = picopass->popup; - popup_set_header(popup, "Detecting\npicopass card", 70, 34, AlignLeft, AlignTop); + popup_set_header(popup, "Detecting\npicopass\ncard", 68, 30, AlignLeft, AlignTop); popup_set_icon(popup, 0, 3, &I_RFIDDolphinReceive_97x61); // Start worker diff --git a/applications/picopass/scenes/picopass_scene_read_card_success.c b/applications/picopass/scenes/picopass_scene_read_card_success.c index 96a080310..3866d201c 100644 --- a/applications/picopass/scenes/picopass_scene_read_card_success.c +++ b/applications/picopass/scenes/picopass_scene_read_card_success.c @@ -29,14 +29,17 @@ void picopass_scene_read_card_success_on_enter(void* context) { PicopassPacs* pacs = &picopass->dev->dev_data.pacs; Widget* widget = picopass->widget; + size_t bytesLength = 1 + pacs->record.bitLength / 8; string_set_str(credential_str, ""); - for(uint8_t i = 0; i < RFAL_PICOPASS_MAX_BLOCK_LEN; i++) { + for(uint8_t i = PICOPASS_BLOCK_LEN - bytesLength; i < PICOPASS_BLOCK_LEN; i++) { string_cat_printf(credential_str, " %02X", pacs->credential[i]); } if(pacs->record.valid) { string_cat_printf( - wiegand_str, "FC: %03u CN: %05u", pacs->record.FacilityCode, pacs->record.CardNumber); + wiegand_str, "FC: %u CN: %u", pacs->record.FacilityCode, pacs->record.CardNumber); + } else { + string_cat_printf(wiegand_str, "%d bits", pacs->record.bitLength); } widget_add_button_element( @@ -53,10 +56,8 @@ void picopass_scene_read_card_success_on_enter(void* context) { picopass_scene_read_card_success_widget_callback, picopass); - if(pacs->record.valid) { - widget_add_string_element( - widget, 64, 12, AlignCenter, AlignCenter, FontPrimary, string_get_cstr(wiegand_str)); - } + widget_add_string_element( + widget, 64, 12, AlignCenter, AlignCenter, FontPrimary, string_get_cstr(wiegand_str)); widget_add_string_element( widget, 64, 32, AlignCenter, AlignCenter, FontSecondary, string_get_cstr(credential_str)); diff --git a/applications/power/battery_test_app/battery_test_app.c b/applications/power/battery_test_app/battery_test_app.c index f24de32ba..ab6889dc8 100755 --- a/applications/power/battery_test_app/battery_test_app.c +++ b/applications/power/battery_test_app/battery_test_app.c @@ -35,9 +35,9 @@ BatteryTestApp* battery_test_alloc() { BatteryTestApp* app = malloc(sizeof(BatteryTestApp)); // Records - app->gui = furi_record_open("gui"); - app->power = furi_record_open("power"); - app->notifications = furi_record_open("notification"); + app->gui = furi_record_open(RECORD_GUI); + app->power = furi_record_open(RECORD_POWER); + app->notifications = furi_record_open(RECORD_NOTIFICATION); // View dispatcher app->view_dispatcher = view_dispatcher_alloc(); @@ -57,7 +57,7 @@ BatteryTestApp* battery_test_alloc() { battery_info_get_view(app->batery_info)); app->dialog = dialog_ex_alloc(); - dialog_ex_set_header(app->dialog, "Close battery test?", 64, 12, AlignCenter, AlignTop); + dialog_ex_set_header(app->dialog, "Close Battery Test?", 64, 12, AlignCenter, AlignTop); dialog_ex_set_left_button_text(app->dialog, "Exit"); dialog_ex_set_right_button_text(app->dialog, "Stay"); dialog_ex_set_result_callback(app->dialog, battery_test_dialog_callback); @@ -82,9 +82,9 @@ void battery_test_free(BatteryTestApp* app) { // View dispatcher view_dispatcher_free(app->view_dispatcher); // Records - furi_record_close("power"); - furi_record_close("gui"); - furi_record_close("notification"); + furi_record_close(RECORD_POWER); + furi_record_close(RECORD_GUI); + furi_record_close(RECORD_NOTIFICATION); free(app); } diff --git a/applications/power/power_cli.c b/applications/power/power_cli.c index 8c6a986d2..6af396318 100644 --- a/applications/power/power_cli.c +++ b/applications/power/power_cli.c @@ -8,7 +8,7 @@ void power_cli_off(Cli* cli, string_t args) { UNUSED(cli); UNUSED(args); - Power* power = furi_record_open("power"); + Power* power = furi_record_open(RECORD_POWER); printf("It's now safe to disconnect USB from your flipper\r\n"); furi_delay_ms(666); power_off(power); @@ -138,11 +138,11 @@ void power_cli(Cli* cli, string_t args, void* context) { void power_on_system_start() { #ifdef SRV_CLI - Cli* cli = furi_record_open("cli"); + Cli* cli = furi_record_open(RECORD_CLI); cli_add_command(cli, "power", CliCommandFlagParallelSafe, power_cli, NULL); - furi_record_close("cli"); + furi_record_close(RECORD_CLI); #else UNUSED(power_cli); #endif diff --git a/applications/power/power_service/power.c b/applications/power/power_service/power.c index 991c2a81d..9036ae1ce 100644 --- a/applications/power/power_service/power.c +++ b/applications/power/power_service/power.c @@ -41,8 +41,8 @@ Power* power_alloc() { Power* power = malloc(sizeof(Power)); // Records - power->notification = furi_record_open("notification"); - power->gui = furi_record_open("gui"); + power->notification = furi_record_open(RECORD_NOTIFICATION); + power->gui = furi_record_open(RECORD_GUI); // Pubsub power->event_pubsub = furi_pubsub_alloc(); @@ -89,8 +89,8 @@ void power_free(Power* power) { furi_pubsub_free(power->event_pubsub); // Records - furi_record_close("notification"); - furi_record_close("gui"); + furi_record_close(RECORD_NOTIFICATION); + furi_record_close(RECORD_GUI); free(power); } @@ -200,10 +200,10 @@ static void power_check_battery_level_change(Power* power) { } int32_t power_srv(void* p) { - (void)p; + UNUSED(p); Power* power = power_alloc(); power_update_info(power); - furi_record_create("power", power); + furi_record_create(RECORD_POWER, power); while(1) { // Update data from gauge and charger diff --git a/applications/power/power_service/power.h b/applications/power/power_service/power.h index cea1663f4..c516f28f6 100644 --- a/applications/power/power_service/power.h +++ b/applications/power/power_service/power.h @@ -4,6 +4,8 @@ #include #include +#define RECORD_POWER "power" + typedef struct Power Power; typedef enum { diff --git a/applications/power/power_settings_app/power_settings_app.c b/applications/power/power_settings_app/power_settings_app.c index 3edf38371..b01f32f75 100644 --- a/applications/power/power_settings_app/power_settings_app.c +++ b/applications/power/power_settings_app/power_settings_app.c @@ -22,8 +22,8 @@ PowerSettingsApp* power_settings_app_alloc(uint32_t first_scene) { PowerSettingsApp* app = malloc(sizeof(PowerSettingsApp)); // Records - app->gui = furi_record_open("gui"); - app->power = furi_record_open("power"); + app->gui = furi_record_open(RECORD_GUI); + app->power = furi_record_open(RECORD_POWER); // View dispatcher app->view_dispatcher = view_dispatcher_alloc(); @@ -69,14 +69,14 @@ void power_settings_app_free(PowerSettingsApp* app) { view_dispatcher_free(app->view_dispatcher); scene_manager_free(app->scene_manager); // Records - furi_record_close("power"); - furi_record_close("gui"); + furi_record_close(RECORD_POWER); + furi_record_close(RECORD_GUI); free(app); } int32_t power_settings_app(void* p) { uint32_t first_scene = PowerSettingsAppSceneStart; - if(p && !strcmp(p, "off")) { + if(p && strlen(p) && !strcmp(p, "off")) { first_scene = PowerSettingsAppScenePowerOff; } PowerSettingsApp* app = power_settings_app_alloc(first_scene); diff --git a/applications/power/power_settings_app/scenes/power_settings_scene_power_off.c b/applications/power/power_settings_app/scenes/power_settings_scene_power_off.c index 83046743b..923ec250e 100644 --- a/applications/power/power_settings_app/scenes/power_settings_scene_power_off.c +++ b/applications/power/power_settings_app/scenes/power_settings_scene_power_off.c @@ -10,7 +10,7 @@ void power_settings_scene_power_off_on_enter(void* context) { PowerSettingsApp* app = context; DialogEx* dialog = app->dialog; - dialog_ex_set_header(dialog, "Turn off Device?", 64, 2, AlignCenter, AlignTop); + dialog_ex_set_header(dialog, "Turn Off Device?", 64, 2, AlignCenter, AlignTop); dialog_ex_set_text( dialog, " I will be\nwaiting for\n you here...", 78, 16, AlignLeft, AlignTop); dialog_ex_set_icon(dialog, 21, 13, &I_Cry_dolph_55x52); diff --git a/applications/rpc/rpc.c b/applications/rpc/rpc.c index a85d0a420..f241a6786 100644 --- a/applications/rpc/rpc.c +++ b/applications/rpc/rpc.c @@ -655,11 +655,11 @@ int32_t rpc_srv(void* p) { rpc->busy_mutex = furi_mutex_alloc(FuriMutexTypeNormal); - Cli* cli = furi_record_open("cli"); + Cli* cli = furi_record_open(RECORD_CLI); cli_add_command( cli, "start_rpc_session", CliCommandFlagParallelSafe, rpc_cli_command_start_session, rpc); - furi_record_create("rpc", rpc); + furi_record_create(RECORD_RPC, rpc); return 0; } diff --git a/applications/rpc/rpc.h b/applications/rpc/rpc.h index 38dd9af33..dea8b749f 100644 --- a/applications/rpc/rpc.h +++ b/applications/rpc/rpc.h @@ -8,6 +8,8 @@ #define RPC_BUFFER_SIZE (1024) #define RPC_MAX_MESSAGE_SIZE (1536) +#define RECORD_RPC "rpc" + /** Rpc interface. Used for opening session only. */ typedef struct Rpc Rpc; /** Rpc session interface */ diff --git a/applications/rpc/rpc_app.c b/applications/rpc/rpc_app.c index 84cb54107..555cec8cf 100644 --- a/applications/rpc/rpc_app.c +++ b/applications/rpc/rpc_app.c @@ -6,23 +6,18 @@ #include "rpc_app.h" #define TAG "RpcSystemApp" -#define APP_BUTTON_TIMEOUT 1000 struct RpcAppSystem { RpcSession* session; RpcAppSystemCallback app_callback; void* app_context; - FuriTimer* timer; -}; + PB_Main* state_msg; -static void rpc_system_app_timer_callback(void* context) { - furi_assert(context); - RpcAppSystem* rpc_app = context; + uint32_t last_id; + char* last_data; +}; - if(rpc_app->app_callback) { - rpc_app->app_callback(RpcAppEventButtonRelease, NULL, rpc_app->app_context); - } -} +#define RPC_SYSTEM_APP_TEMP_ARGS_SIZE 16 static void rpc_system_app_start_process(const PB_Main* request, void* context) { furi_assert(request); @@ -32,19 +27,22 @@ static void rpc_system_app_start_process(const PB_Main* request, void* context) RpcAppSystem* rpc_app = context; RpcSession* session = rpc_app->session; furi_assert(session); - char args_temp[16]; + char args_temp[RPC_SYSTEM_APP_TEMP_ARGS_SIZE]; + + furi_assert(!rpc_app->last_id); + furi_assert(!rpc_app->last_data); - FURI_LOG_D(TAG, "Start"); + FURI_LOG_D(TAG, "StartProcess: id %d", request->command_id); PB_CommandStatus result = PB_CommandStatus_ERROR_APP_CANT_START; - Loader* loader = furi_record_open("loader"); + Loader* loader = furi_record_open(RECORD_LOADER); const char* app_name = request->content.app_start_request.name; if(app_name) { const char* app_args = request->content.app_start_request.args; - if(strcmp(app_args, "RPC") == 0) { + if(app_args && strcmp(app_args, "RPC") == 0) { // If app is being started in RPC mode - pass RPC context via args string - snprintf(args_temp, 16, "RPC %08lX", (uint32_t)rpc_app); + snprintf(args_temp, RPC_SYSTEM_APP_TEMP_ARGS_SIZE, "RPC %08lX", (uint32_t)rpc_app); app_args = args_temp; } LoaderStatus status = loader_start(loader, app_name, app_args); @@ -57,14 +55,15 @@ static void rpc_system_app_start_process(const PB_Main* request, void* context) } else if(status == LoaderStatusOk) { result = PB_CommandStatus_OK; } else { - furi_assert(0); + furi_crash("Programming Error"); } } else { result = PB_CommandStatus_ERROR_INVALID_PARAMETERS; } - furi_record_close("loader"); + furi_record_close(RECORD_LOADER); + FURI_LOG_D(TAG, "StartProcess: response id %d, result %d", request->command_id, result); rpc_send_and_release_empty(session, request->command_id, result); } @@ -79,7 +78,7 @@ static void rpc_system_app_lock_status_process(const PB_Main* request, void* con FURI_LOG_D(TAG, "LockStatus"); - Loader* loader = furi_record_open("loader"); + Loader* loader = furi_record_open(RECORD_LOADER); PB_Main response = { .has_next = false, @@ -90,13 +89,14 @@ static void rpc_system_app_lock_status_process(const PB_Main* request, void* con response.content.app_lock_status_response.locked = loader_is_locked(loader); - furi_record_close("loader"); + furi_record_close(RECORD_LOADER); + FURI_LOG_D(TAG, "LockStatus: response"); rpc_send_and_release(session, &response); pb_release(&PB_Main_msg, &response); } -static void rpc_system_app_exit(const PB_Main* request, void* context) { +static void rpc_system_app_exit_request(const PB_Main* request, void* context) { furi_assert(request); furi_assert(context); @@ -108,17 +108,17 @@ static void rpc_system_app_exit(const PB_Main* request, void* context) { PB_CommandStatus status; if(rpc_app->app_callback) { - if(rpc_app->app_callback(RpcAppEventAppExit, NULL, rpc_app->app_context)) { - status = PB_CommandStatus_OK; - furi_timer_stop(rpc_app->timer); - } else { - status = PB_CommandStatus_ERROR_APP_CMD_ERROR; - } + FURI_LOG_D(TAG, "ExitRequest: id %d", request->command_id); + furi_assert(!rpc_app->last_id); + furi_assert(!rpc_app->last_data); + rpc_app->last_id = request->command_id; + rpc_app->app_callback(RpcAppEventAppExit, rpc_app->app_context); } else { status = PB_CommandStatus_ERROR_APP_NOT_RUNNING; + FURI_LOG_E( + TAG, "ExitRequest: APP_NOT_RUNNING, id %d, status: %d", request->command_id, status); + rpc_send_and_release_empty(session, request->command_id, status); } - - rpc_send_and_release_empty(session, request->command_id, status); } static void rpc_system_app_load_file(const PB_Main* request, void* context) { @@ -132,17 +132,18 @@ static void rpc_system_app_load_file(const PB_Main* request, void* context) { PB_CommandStatus status; if(rpc_app->app_callback) { - const char* file_path = request->content.app_load_file_request.path; - if(rpc_app->app_callback(RpcAppEventLoadFile, file_path, rpc_app->app_context)) { - status = PB_CommandStatus_OK; - } else { - status = PB_CommandStatus_ERROR_APP_CMD_ERROR; - } + FURI_LOG_D(TAG, "LoadFile: id %d", request->command_id); + furi_assert(!rpc_app->last_id); + furi_assert(!rpc_app->last_data); + rpc_app->last_id = request->command_id; + rpc_app->last_data = strdup(request->content.app_load_file_request.path); + rpc_app->app_callback(RpcAppEventLoadFile, rpc_app->app_context); } else { status = PB_CommandStatus_ERROR_APP_NOT_RUNNING; + FURI_LOG_E( + TAG, "LoadFile: APP_NOT_RUNNING, id %d, status: %d", request->command_id, status); + rpc_send_and_release_empty(session, request->command_id, status); } - - rpc_send_and_release_empty(session, request->command_id, status); } static void rpc_system_app_button_press(const PB_Main* request, void* context) { @@ -156,18 +157,18 @@ static void rpc_system_app_button_press(const PB_Main* request, void* context) { PB_CommandStatus status; if(rpc_app->app_callback) { - const char* args = request->content.app_button_press_request.args; - if(rpc_app->app_callback(RpcAppEventButtonPress, args, rpc_app->app_context)) { - status = PB_CommandStatus_OK; - furi_timer_start(rpc_app->timer, APP_BUTTON_TIMEOUT); - } else { - status = PB_CommandStatus_ERROR_APP_CMD_ERROR; - } + FURI_LOG_D(TAG, "ButtonPress"); + furi_assert(!rpc_app->last_id); + furi_assert(!rpc_app->last_data); + rpc_app->last_id = request->command_id; + rpc_app->last_data = strdup(request->content.app_button_press_request.args); + rpc_app->app_callback(RpcAppEventButtonPress, rpc_app->app_context); } else { status = PB_CommandStatus_ERROR_APP_NOT_RUNNING; + FURI_LOG_E( + TAG, "ButtonPress: APP_NOT_RUNNING, id %d, status: %d", request->command_id, status); + rpc_send_and_release_empty(session, request->command_id, status); } - - rpc_send_and_release_empty(session, request->command_id, status); } static void rpc_system_app_button_release(const PB_Main* request, void* context) { @@ -181,17 +182,74 @@ static void rpc_system_app_button_release(const PB_Main* request, void* context) PB_CommandStatus status; if(rpc_app->app_callback) { - if(rpc_app->app_callback(RpcAppEventButtonRelease, NULL, rpc_app->app_context)) { - status = PB_CommandStatus_OK; - furi_timer_stop(rpc_app->timer); - } else { - status = PB_CommandStatus_ERROR_APP_CMD_ERROR; - } + FURI_LOG_D(TAG, "ButtonRelease"); + furi_assert(!rpc_app->last_id); + furi_assert(!rpc_app->last_data); + rpc_app->last_id = request->command_id; + rpc_app->app_callback(RpcAppEventButtonRelease, rpc_app->app_context); } else { status = PB_CommandStatus_ERROR_APP_NOT_RUNNING; + FURI_LOG_E( + TAG, "ButtonRelease: APP_NOT_RUNNING, id %d, status: %d", request->command_id, status); + rpc_send_and_release_empty(session, request->command_id, status); } +} + +void rpc_system_app_send_started(RpcAppSystem* rpc_app) { + furi_assert(rpc_app); + RpcSession* session = rpc_app->session; + furi_assert(session); + + rpc_app->state_msg->content.app_state_response.state = PB_App_AppState_APP_STARTED; + + FURI_LOG_D(TAG, "SendStarted"); + rpc_send(session, rpc_app->state_msg); +} + +void rpc_system_app_send_exited(RpcAppSystem* rpc_app) { + furi_assert(rpc_app); + RpcSession* session = rpc_app->session; + furi_assert(session); + + rpc_app->state_msg->content.app_state_response.state = PB_App_AppState_APP_CLOSED; - rpc_send_and_release_empty(session, request->command_id, status); + FURI_LOG_D(TAG, "SendExit"); + rpc_send(session, rpc_app->state_msg); +} + +const char* rpc_system_app_get_data(RpcAppSystem* rpc_app) { + furi_assert(rpc_app); + furi_assert(rpc_app->last_data); + return rpc_app->last_data; +} + +void rpc_system_app_confirm(RpcAppSystem* rpc_app, RpcAppSystemEvent event, bool result) { + furi_assert(rpc_app); + RpcSession* session = rpc_app->session; + furi_assert(session); + furi_assert(rpc_app->last_id); + + PB_CommandStatus status = result ? PB_CommandStatus_OK : PB_CommandStatus_ERROR_APP_CMD_ERROR; + + uint32_t last_id = 0; + switch(event) { + case RpcAppEventAppExit: + case RpcAppEventLoadFile: + case RpcAppEventButtonPress: + case RpcAppEventButtonRelease: + last_id = rpc_app->last_id; + rpc_app->last_id = 0; + if(rpc_app->last_data) { + free(rpc_app->last_data); + rpc_app->last_data = NULL; + } + FURI_LOG_D(TAG, "AppConfirm: event %d last_id %d status %d", event, last_id, status); + rpc_send_and_release_empty(session, last_id, status); + break; + default: + furi_crash("RPC App state programming Error"); + break; + } } void rpc_system_app_set_callback(RpcAppSystem* rpc_app, RpcAppSystemCallback callback, void* ctx) { @@ -207,7 +265,10 @@ void* rpc_system_app_alloc(RpcSession* session) { RpcAppSystem* rpc_app = malloc(sizeof(RpcAppSystem)); rpc_app->session = session; - rpc_app->timer = furi_timer_alloc(rpc_system_app_timer_callback, FuriTimerTypeOnce, rpc_app); + // App exit message + rpc_app->state_msg = malloc(sizeof(PB_Main)); + rpc_app->state_msg->which_content = PB_Main_app_state_response_tag; + rpc_app->state_msg->command_status = PB_CommandStatus_OK; RpcHandler rpc_handler = { .message_handler = NULL, @@ -221,7 +282,7 @@ void* rpc_system_app_alloc(RpcSession* session) { rpc_handler.message_handler = rpc_system_app_lock_status_process; rpc_add_handler(session, PB_Main_app_lock_status_request_tag, &rpc_handler); - rpc_handler.message_handler = rpc_system_app_exit; + rpc_handler.message_handler = rpc_system_app_exit_request; rpc_add_handler(session, PB_Main_app_exit_request_tag, &rpc_handler); rpc_handler.message_handler = rpc_system_app_load_file; @@ -241,11 +302,16 @@ void rpc_system_app_free(void* context) { RpcSession* session = rpc_app->session; furi_assert(session); - furi_timer_free(rpc_app->timer); - if(rpc_app->app_callback) { - rpc_app->app_callback(RpcAppEventSessionClose, NULL, rpc_app->app_context); + rpc_app->app_callback(RpcAppEventSessionClose, rpc_app->app_context); } + while(rpc_app->app_callback) { + furi_delay_tick(1); + } + + if(rpc_app->last_data) free(rpc_app->last_data); + + free(rpc_app->state_msg); free(rpc_app); } diff --git a/applications/rpc/rpc_app.h b/applications/rpc/rpc_app.h index 396eef1a3..635c9f8c6 100644 --- a/applications/rpc/rpc_app.h +++ b/applications/rpc/rpc_app.h @@ -13,12 +13,20 @@ typedef enum { RpcAppEventButtonRelease, } RpcAppSystemEvent; -typedef bool (*RpcAppSystemCallback)(RpcAppSystemEvent event, const char* arg, void* context); +typedef void (*RpcAppSystemCallback)(RpcAppSystemEvent event, void* context); typedef struct RpcAppSystem RpcAppSystem; void rpc_system_app_set_callback(RpcAppSystem* rpc_app, RpcAppSystemCallback callback, void* ctx); +void rpc_system_app_send_started(RpcAppSystem* rpc_app); + +void rpc_system_app_send_exited(RpcAppSystem* rpc_app); + +const char* rpc_system_app_get_data(RpcAppSystem* rpc_app); + +void rpc_system_app_confirm(RpcAppSystem* rpc_app, RpcAppSystemEvent event, bool result); + #ifdef __cplusplus } #endif diff --git a/applications/rpc/rpc_gui.c b/applications/rpc/rpc_gui.c index 62a232d8f..029ed0106 100644 --- a/applications/rpc/rpc_gui.c +++ b/applications/rpc/rpc_gui.c @@ -198,10 +198,10 @@ static void return; } - FuriPubSub* input_events = furi_record_open("input_events"); + FuriPubSub* input_events = furi_record_open(RECORD_INPUT_EVENTS); furi_check(input_events); furi_pubsub_publish(input_events, &event); - furi_record_close("input_events"); + furi_record_close(RECORD_INPUT_EVENTS); rpc_send_and_release_empty(session, request->command_id, PB_CommandStatus_OK); } @@ -317,7 +317,7 @@ void* rpc_system_gui_alloc(RpcSession* session) { furi_assert(session); RpcGuiSystem* rpc_gui = malloc(sizeof(RpcGuiSystem)); - rpc_gui->gui = furi_record_open("gui"); + rpc_gui->gui = furi_record_open(RECORD_GUI); rpc_gui->session = session; RpcHandler rpc_handler = { @@ -374,6 +374,6 @@ void rpc_system_gui_free(void* context) { free(rpc_gui->transmit_frame); rpc_gui->transmit_frame = NULL; } - furi_record_close("gui"); + furi_record_close(RECORD_GUI); free(rpc_gui); } diff --git a/applications/rpc/rpc_storage.c b/applications/rpc/rpc_storage.c index 4ab681ae9..48ea9e998 100644 --- a/applications/rpc/rpc_storage.c +++ b/applications/rpc/rpc_storage.c @@ -49,7 +49,7 @@ static void rpc_system_storage_reset_state( if(rpc_storage->state == RpcStorageStateWriting) { storage_file_close(rpc_storage->file); storage_file_free(rpc_storage->file); - furi_record_close("storage"); + furi_record_close(RECORD_STORAGE); } rpc_storage->state = RpcStorageStateIdle; @@ -117,7 +117,7 @@ static void rpc_system_storage_info_process(const PB_Main* request, void* contex PB_Main* response = malloc(sizeof(PB_Main)); response->command_id = request->command_id; - Storage* fs_api = furi_record_open("storage"); + Storage* fs_api = furi_record_open(RECORD_STORAGE); FS_Error error = storage_common_fs_info( fs_api, @@ -134,7 +134,7 @@ static void rpc_system_storage_info_process(const PB_Main* request, void* contex rpc_send_and_release(session, response); free(response); - furi_record_close("storage"); + furi_record_close(RECORD_STORAGE); } static void rpc_system_storage_stat_process(const PB_Main* request, void* context) { @@ -153,7 +153,7 @@ static void rpc_system_storage_stat_process(const PB_Main* request, void* contex PB_Main* response = malloc(sizeof(PB_Main)); response->command_id = request->command_id; - Storage* fs_api = furi_record_open("storage"); + Storage* fs_api = furi_record_open(RECORD_STORAGE); const char* path = request->content.storage_stat_request.path; FileInfo fileinfo; @@ -173,7 +173,7 @@ static void rpc_system_storage_stat_process(const PB_Main* request, void* contex rpc_send_and_release(session, response); free(response); - furi_record_close("storage"); + furi_record_close(RECORD_STORAGE); } static void rpc_system_storage_list_root(const PB_Main* request, void* context) { @@ -222,7 +222,7 @@ static void rpc_system_storage_list_process(const PB_Main* request, void* contex return; } - Storage* fs_api = furi_record_open("storage"); + Storage* fs_api = furi_record_open(RECORD_STORAGE); File* dir = storage_file_alloc(fs_api); PB_Main response = { @@ -276,7 +276,7 @@ static void rpc_system_storage_list_process(const PB_Main* request, void* contex storage_dir_close(dir); storage_file_free(dir); - furi_record_close("storage"); + furi_record_close(RECORD_STORAGE); } static void rpc_system_storage_read_process(const PB_Main* request, void* context) { @@ -295,7 +295,7 @@ static void rpc_system_storage_read_process(const PB_Main* request, void* contex /* use same message memory to send reponse */ PB_Main* response = malloc(sizeof(PB_Main)); const char* path = request->content.storage_read_request.path; - Storage* fs_api = furi_record_open("storage"); + Storage* fs_api = furi_record_open(RECORD_STORAGE); File* file = storage_file_alloc(fs_api); bool result = false; @@ -305,21 +305,27 @@ static void rpc_system_storage_read_process(const PB_Main* request, void* contex response->command_id = request->command_id; response->which_content = PB_Main_storage_read_response_tag; response->command_status = PB_CommandStatus_OK; - response->content.storage_read_response.has_file = true; - response->content.storage_read_response.file.data = - malloc(PB_BYTES_ARRAY_T_ALLOCSIZE(MIN(size_left, MAX_DATA_SIZE))); - uint8_t* buffer = response->content.storage_read_response.file.data->bytes; - uint16_t* read_size_msg = &response->content.storage_read_response.file.data->size; size_t read_size = MIN(size_left, MAX_DATA_SIZE); - *read_size_msg = storage_file_read(file, buffer, read_size); - size_left -= read_size; - result = (*read_size_msg == read_size); - - if(result) { - response->has_next = (size_left > 0); - rpc_send_and_release(session, response); + if(read_size) { + response->content.storage_read_response.has_file = true; + response->content.storage_read_response.file.data = + malloc(PB_BYTES_ARRAY_T_ALLOCSIZE()); + uint8_t* buffer = response->content.storage_read_response.file.data->bytes; + uint16_t* read_size_msg = &response->content.storage_read_response.file.data->size; + + *read_size_msg = storage_file_read(file, buffer, read_size); + size_left -= read_size; + result = (*read_size_msg == read_size); + + response->has_next = result && (size_left > 0); + } else { + response->content.storage_read_response.has_file = false; + response->has_next = false; + result = true; } + + rpc_send_and_release(session, response); } while((size_left != 0) && result); if(!result) { @@ -335,7 +341,7 @@ static void rpc_system_storage_read_process(const PB_Main* request, void* contex storage_file_close(file); storage_file_free(file); - furi_record_close("storage"); + furi_record_close(RECORD_STORAGE); } static void rpc_system_storage_write_process(const PB_Main* request, void* context) { @@ -349,7 +355,7 @@ static void rpc_system_storage_write_process(const PB_Main* request, void* conte RpcSession* session = rpc_storage->session; furi_assert(session); - bool result = true; + bool fs_operation_success = true; if(!path_contains_only_ascii(request->content.storage_write_request.path)) { rpc_storage->current_command_id = request->command_id; @@ -365,33 +371,37 @@ static void rpc_system_storage_write_process(const PB_Main* request, void* conte } if(rpc_storage->state != RpcStorageStateWriting) { - rpc_storage->api = furi_record_open("storage"); + rpc_storage->api = furi_record_open(RECORD_STORAGE); rpc_storage->file = storage_file_alloc(rpc_storage->api); rpc_storage->current_command_id = request->command_id; rpc_storage->state = RpcStorageStateWriting; const char* path = request->content.storage_write_request.path; - result = storage_file_open(rpc_storage->file, path, FSAM_WRITE, FSOM_CREATE_ALWAYS); + fs_operation_success = + storage_file_open(rpc_storage->file, path, FSAM_WRITE, FSOM_CREATE_ALWAYS); } File* file = rpc_storage->file; + bool send_response = false; + + if(fs_operation_success) { + if(request->content.storage_write_request.has_file) { + uint8_t* buffer = request->content.storage_write_request.file.data->bytes; + size_t buffer_size = request->content.storage_write_request.file.data->size; + uint16_t written_size = storage_file_write(file, buffer, buffer_size); + fs_operation_success = (written_size == buffer_size); + } - if(result) { - uint8_t* buffer = request->content.storage_write_request.file.data->bytes; - size_t buffer_size = request->content.storage_write_request.file.data->size; - - uint16_t written_size = storage_file_write(file, buffer, buffer_size); - result = (written_size == buffer_size); + send_response = !request->has_next; + } - if(result && !request->has_next) { - rpc_send_and_release_empty( - session, rpc_storage->current_command_id, PB_CommandStatus_OK); - rpc_system_storage_reset_state(rpc_storage, session, false); - } + PB_CommandStatus command_status = PB_CommandStatus_OK; + if(!fs_operation_success) { + send_response = true; + command_status = rpc_system_storage_get_file_error(file); } - if(!result) { - rpc_send_and_release_empty( - session, rpc_storage->current_command_id, rpc_system_storage_get_file_error(file)); + if(send_response) { + rpc_send_and_release_empty(session, rpc_storage->current_command_id, command_status); rpc_system_storage_reset_state(rpc_storage, session, false); } } @@ -433,7 +443,7 @@ static void rpc_system_storage_delete_process(const PB_Main* request, void* cont PB_CommandStatus status = PB_CommandStatus_ERROR; rpc_system_storage_reset_state(rpc_storage, session, true); - Storage* fs_api = furi_record_open("storage"); + Storage* fs_api = furi_record_open(RECORD_STORAGE); char* path = request->content.storage_delete_request.path; if(!path) { @@ -456,7 +466,7 @@ static void rpc_system_storage_delete_process(const PB_Main* request, void* cont } } - furi_record_close("storage"); + furi_record_close(RECORD_STORAGE); rpc_send_and_release_empty(session, request->command_id, status); } @@ -474,7 +484,7 @@ static void rpc_system_storage_mkdir_process(const PB_Main* request, void* conte PB_CommandStatus status; rpc_system_storage_reset_state(rpc_storage, session, true); - Storage* fs_api = furi_record_open("storage"); + Storage* fs_api = furi_record_open(RECORD_STORAGE); char* path = request->content.storage_mkdir_request.path; if(path) { if(path_contains_only_ascii(path)) { @@ -486,7 +496,7 @@ static void rpc_system_storage_mkdir_process(const PB_Main* request, void* conte } else { status = PB_CommandStatus_ERROR_INVALID_PARAMETERS; } - furi_record_close("storage"); + furi_record_close(RECORD_STORAGE); rpc_send_and_release_empty(session, request->command_id, status); } @@ -510,7 +520,7 @@ static void rpc_system_storage_md5sum_process(const PB_Main* request, void* cont return; } - Storage* fs_api = furi_record_open("storage"); + Storage* fs_api = furi_record_open(RECORD_STORAGE); File* file = storage_file_alloc(fs_api); if(storage_file_open(file, filename, FSAM_READ, FSOM_OPEN_EXISTING)) { @@ -541,7 +551,7 @@ static void rpc_system_storage_md5sum_process(const PB_Main* request, void* cont (void)md5sum_size; furi_assert(hash_size <= ((md5sum_size - 1) / 2)); for(uint8_t i = 0; i < hash_size; i++) { - md5sum += sprintf(md5sum, "%02x", hash[i]); + md5sum += snprintf(md5sum, md5sum_size, "%02x", hash[i]); } free(hash); @@ -555,7 +565,7 @@ static void rpc_system_storage_md5sum_process(const PB_Main* request, void* cont storage_file_free(file); - furi_record_close("storage"); + furi_record_close(RECORD_STORAGE); } static void rpc_system_storage_rename_process(const PB_Main* request, void* context) { @@ -572,7 +582,7 @@ static void rpc_system_storage_rename_process(const PB_Main* request, void* cont PB_CommandStatus status; rpc_system_storage_reset_state(rpc_storage, session, true); - Storage* fs_api = furi_record_open("storage"); + Storage* fs_api = furi_record_open(RECORD_STORAGE); if(path_contains_only_ascii(request->content.storage_rename_request.new_path)) { FS_Error error = storage_common_rename( @@ -584,7 +594,7 @@ static void rpc_system_storage_rename_process(const PB_Main* request, void* cont status = PB_CommandStatus_ERROR_STORAGE_INVALID_NAME; } - furi_record_close("storage"); + furi_record_close(RECORD_STORAGE); rpc_send_and_release_empty(session, request->command_id, status); } @@ -594,23 +604,19 @@ static void rpc_system_storage_backup_create_process(const PB_Main* request, voi FURI_LOG_D(TAG, "BackupCreate"); - RpcSession* session = (RpcSession*)context; + RpcStorageSystem* rpc_storage = context; + RpcSession* session = rpc_storage->session; furi_assert(session); - PB_Main* response = malloc(sizeof(PB_Main)); - response->command_id = request->command_id; - response->has_next = false; - - Storage* fs_api = furi_record_open("storage"); + Storage* fs_api = furi_record_open(RECORD_STORAGE); bool backup_ok = lfs_backup_create(fs_api, request->content.storage_backup_create_request.archive_path); - response->command_status = backup_ok ? PB_CommandStatus_OK : PB_CommandStatus_ERROR; - furi_record_close("storage"); + furi_record_close(RECORD_STORAGE); - rpc_send_and_release(session, response); - free(response); + rpc_send_and_release_empty( + session, request->command_id, backup_ok ? PB_CommandStatus_OK : PB_CommandStatus_ERROR); } static void rpc_system_storage_backup_restore_process(const PB_Main* request, void* context) { @@ -619,31 +625,26 @@ static void rpc_system_storage_backup_restore_process(const PB_Main* request, vo FURI_LOG_D(TAG, "BackupRestore"); - RpcSession* session = (RpcSession*)context; + RpcStorageSystem* rpc_storage = context; + RpcSession* session = rpc_storage->session; furi_assert(session); - PB_Main* response = malloc(sizeof(PB_Main)); - response->command_id = request->command_id; - response->has_next = false; - response->command_status = PB_CommandStatus_OK; - - Storage* fs_api = furi_record_open("storage"); + Storage* fs_api = furi_record_open(RECORD_STORAGE); bool backup_ok = lfs_backup_unpack(fs_api, request->content.storage_backup_restore_request.archive_path); - response->command_status = backup_ok ? PB_CommandStatus_OK : PB_CommandStatus_ERROR; - furi_record_close("storage"); + furi_record_close(RECORD_STORAGE); - rpc_send_and_release(session, response); - free(response); + rpc_send_and_release_empty( + session, request->command_id, backup_ok ? PB_CommandStatus_OK : PB_CommandStatus_ERROR); } void* rpc_system_storage_alloc(RpcSession* session) { furi_assert(session); RpcStorageSystem* rpc_storage = malloc(sizeof(RpcStorageSystem)); - rpc_storage->api = furi_record_open("storage"); + rpc_storage->api = furi_record_open(RECORD_STORAGE); rpc_storage->session = session; rpc_storage->state = RpcStorageStateIdle; diff --git a/applications/rpc/rpc_system.c b/applications/rpc/rpc_system.c index 350602fd8..0538aa64d 100644 --- a/applications/rpc/rpc_system.c +++ b/applications/rpc/rpc_system.c @@ -78,6 +78,8 @@ static void rpc_system_system_device_info_callback( furi_assert(value); RpcSystemContext* ctx = context; + furi_assert(key); + furi_assert(value); char* str_key = strdup(key); char* str_value = strdup(value); @@ -193,9 +195,9 @@ static void RpcSession* session = (RpcSession*)context; furi_assert(session); - NotificationApp* notification = furi_record_open("notification"); + NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION); notification_message(notification, &sequence_audiovisual_alert); - furi_record_close("notification"); + furi_record_close(RECORD_NOTIFICATION); rpc_send_and_release_empty(session, request->command_id, PB_CommandStatus_OK); } @@ -232,6 +234,8 @@ static void rpc_system_system_power_info_callback( furi_assert(value); RpcSystemContext* ctx = context; + furi_assert(key); + furi_assert(value); char* str_key = strdup(key); char* str_value = strdup(value); diff --git a/applications/snake_game/snake_game.c b/applications/snake_game/snake_game.c index bfd31ced0..b7aabb17c 100644 --- a/applications/snake_game/snake_game.c +++ b/applications/snake_game/snake_game.c @@ -304,7 +304,7 @@ int32_t snake_game_app(void* p) { furi_timer_start(timer, furi_kernel_get_tick_frequency() / 4); // Open GUI and register view_port - Gui* gui = furi_record_open("gui"); + Gui* gui = furi_record_open(RECORD_GUI); gui_add_view_port(gui, view_port, GuiLayerFullscreen); SnakeEvent event; @@ -354,7 +354,7 @@ int32_t snake_game_app(void* p) { furi_timer_free(timer); view_port_enabled_set(view_port, false); gui_remove_view_port(gui, view_port); - furi_record_close("gui"); + furi_record_close(RECORD_GUI); view_port_free(view_port); furi_message_queue_free(event_queue); delete_mutex(&state_mutex); diff --git a/applications/storage/storage.c b/applications/storage/storage.c index 90a191a2e..9079a95ed 100644 --- a/applications/storage/storage.c +++ b/applications/storage/storage.c @@ -11,7 +11,7 @@ #define ICON_SD_MOUNTED &I_SDcardMounted_11x8 #define ICON_SD_ERROR &I_SDcardFail_11x8 -#define TAG "Storage" +#define TAG RECORD_STORAGE static void storage_app_sd_icon_draw_callback(Canvas* canvas, void* context) { furi_assert(canvas); @@ -52,9 +52,9 @@ Storage* storage_app_alloc() { view_port_draw_callback_set(app->sd_gui.view_port, storage_app_sd_icon_draw_callback, app); view_port_enabled_set(app->sd_gui.view_port, false); - Gui* gui = furi_record_open("gui"); + Gui* gui = furi_record_open(RECORD_GUI); gui_add_view_port(gui, app->sd_gui.view_port, GuiLayerStatusBarLeft); - furi_record_close("gui"); + furi_record_close(RECORD_GUI); return app; } @@ -102,7 +102,7 @@ void storage_tick(Storage* app) { int32_t storage_srv(void* p) { UNUSED(p); Storage* app = storage_app_alloc(); - furi_record_create("storage", app); + furi_record_create(RECORD_STORAGE, app); StorageMessage message; while(1) { diff --git a/applications/storage/storage.h b/applications/storage/storage.h index dcb8deee8..55a951d12 100644 --- a/applications/storage/storage.h +++ b/applications/storage/storage.h @@ -8,6 +8,16 @@ extern "C" { #endif +#define STORAGE_INT_PATH_PREFIX "/int" +#define STORAGE_EXT_PATH_PREFIX "/ext" +#define STORAGE_ANY_PATH_PREFIX "/any" + +#define INT_PATH(path) STORAGE_INT_PATH_PREFIX "/" path +#define EXT_PATH(path) STORAGE_EXT_PATH_PREFIX "/" path +#define ANY_PATH(path) STORAGE_ANY_PATH_PREFIX "/" path + +#define RECORD_STORAGE "storage" + typedef struct Storage Storage; /** Allocates and initializes a file descriptor @@ -273,6 +283,8 @@ FS_Error storage_sd_status(Storage* api); /******************* Internal LFS Functions *******************/ +typedef void (*Storage_name_converter)(string_t); + /** Backs up internal storage to a tar archive * @param api pointer to the api * @param dstmane destination archive path @@ -283,9 +295,10 @@ FS_Error storage_int_backup(Storage* api, const char* dstname); /** Restores internal storage from a tar archive * @param api pointer to the api * @param dstmane archive path + * @param converter pointer to filename conversion function, may be NULL * @return FS_Error operation result */ -FS_Error storage_int_restore(Storage* api, const char* dstname); +FS_Error storage_int_restore(Storage* api, const char* dstname, Storage_name_converter converter); /***************** Simplified Functions ******************/ diff --git a/applications/storage/storage_cli.c b/applications/storage/storage_cli.c index dd423cc61..802ebd548 100644 --- a/applications/storage/storage_cli.c +++ b/applications/storage/storage_cli.c @@ -40,12 +40,13 @@ static void storage_cli_print_error(FS_Error error) { static void storage_cli_info(Cli* cli, string_t path) { UNUSED(cli); - Storage* api = furi_record_open("storage"); + Storage* api = furi_record_open(RECORD_STORAGE); - if(string_cmp_str(path, "/int") == 0) { + if(string_cmp_str(path, STORAGE_INT_PATH_PREFIX) == 0) { uint64_t total_space; uint64_t free_space; - FS_Error error = storage_common_fs_info(api, "/int", &total_space, &free_space); + FS_Error error = + storage_common_fs_info(api, STORAGE_INT_PATH_PREFIX, &total_space, &free_space); if(error != FSE_OK) { storage_cli_print_error(error); @@ -56,7 +57,7 @@ static void storage_cli_info(Cli* cli, string_t path) { (uint32_t)(total_space / 1024), (uint32_t)(free_space / 1024)); } - } else if(string_cmp_str(path, "/ext") == 0) { + } else if(string_cmp_str(path, STORAGE_EXT_PATH_PREFIX) == 0) { SDInfo sd_info; FS_Error error = storage_sd_info(api, &sd_info); @@ -74,17 +75,17 @@ static void storage_cli_info(Cli* cli, string_t path) { storage_cli_print_usage(); } - furi_record_close("storage"); + furi_record_close(RECORD_STORAGE); }; static void storage_cli_format(Cli* cli, string_t path) { - if(string_cmp_str(path, "/int") == 0) { + if(string_cmp_str(path, STORAGE_INT_PATH_PREFIX) == 0) { storage_cli_print_error(FSE_NOT_IMPLEMENTED); - } else if(string_cmp_str(path, "/ext") == 0) { - printf("Formatting SD card, all data will be lost. Are you sure (y/n)?\r\n"); + } else if(string_cmp_str(path, STORAGE_EXT_PATH_PREFIX) == 0) { + printf("Formatting SD card, All data will be lost! Are you sure (y/n)?\r\n"); char answer = cli_getc(cli); if(answer == 'y' || answer == 'Y') { - Storage* api = furi_record_open("storage"); + Storage* api = furi_record_open(RECORD_STORAGE); printf("Formatting, please wait...\r\n"); FS_Error error = storage_sd_format(api); @@ -94,7 +95,7 @@ static void storage_cli_format(Cli* cli, string_t path) { } else { printf("SD card was successfully formatted.\r\n"); } - furi_record_close("storage"); + furi_record_close(RECORD_STORAGE); } else { printf("Cancelled.\r\n"); } @@ -110,7 +111,7 @@ static void storage_cli_list(Cli* cli, string_t path) { printf("\t[D] ext\r\n"); printf("\t[D] any\r\n"); } else { - Storage* api = furi_record_open("storage"); + Storage* api = furi_record_open(RECORD_STORAGE); File* file = storage_file_alloc(api); if(storage_dir_open(file, string_get_cstr(path))) { @@ -136,18 +137,18 @@ static void storage_cli_list(Cli* cli, string_t path) { storage_dir_close(file); storage_file_free(file); - furi_record_close("storage"); + furi_record_close(RECORD_STORAGE); } } static void storage_cli_tree(Cli* cli, string_t path) { if(string_cmp_str(path, "/") == 0) { - string_set(path, "/int"); + string_set(path, STORAGE_INT_PATH_PREFIX); storage_cli_tree(cli, path); - string_set(path, "/ext"); + string_set(path, STORAGE_EXT_PATH_PREFIX); storage_cli_tree(cli, path); } else { - Storage* api = furi_record_open("storage"); + Storage* api = furi_record_open(RECORD_STORAGE); DirWalk* dir_walk = dir_walk_alloc(api); string_t name; string_init(name); @@ -174,13 +175,13 @@ static void storage_cli_tree(Cli* cli, string_t path) { string_clear(name); dir_walk_free(dir_walk); - furi_record_close("storage"); + furi_record_close(RECORD_STORAGE); } } static void storage_cli_read(Cli* cli, string_t path) { UNUSED(cli); - Storage* api = furi_record_open("storage"); + Storage* api = furi_record_open(RECORD_STORAGE); File* file = storage_file_alloc(api); if(storage_file_open(file, string_get_cstr(path), FSAM_READ, FSOM_OPEN_EXISTING)) { @@ -206,11 +207,11 @@ static void storage_cli_read(Cli* cli, string_t path) { storage_file_close(file); storage_file_free(file); - furi_record_close("storage"); + furi_record_close(RECORD_STORAGE); } static void storage_cli_write(Cli* cli, string_t path) { - Storage* api = furi_record_open("storage"); + Storage* api = furi_record_open(RECORD_STORAGE); File* file = storage_file_alloc(api); const uint16_t buffer_size = 512; @@ -260,11 +261,11 @@ static void storage_cli_write(Cli* cli, string_t path) { free(buffer); storage_file_free(file); - furi_record_close("storage"); + furi_record_close(RECORD_STORAGE); } static void storage_cli_read_chunks(Cli* cli, string_t path, string_t args) { - Storage* api = furi_record_open("storage"); + Storage* api = furi_record_open(RECORD_STORAGE); File* file = storage_file_alloc(api); uint32_t buffer_size; @@ -273,24 +274,26 @@ static void storage_cli_read_chunks(Cli* cli, string_t path, string_t args) { if(parsed_count == EOF || parsed_count != 1) { storage_cli_print_usage(); } else if(storage_file_open(file, string_get_cstr(path), FSAM_READ, FSOM_OPEN_EXISTING)) { - uint8_t* data = malloc(buffer_size); uint64_t file_size = storage_file_size(file); printf("Size: %lu\r\n", (uint32_t)file_size); - while(file_size > 0) { - printf("\r\nReady?\r\n"); - cli_getc(cli); + if(buffer_size) { + uint8_t* data = malloc(buffer_size); + while(file_size > 0) { + printf("\r\nReady?\r\n"); + cli_getc(cli); - uint16_t read_size = storage_file_read(file, data, buffer_size); - for(uint16_t i = 0; i < read_size; i++) { - putchar(data[i]); + uint16_t read_size = storage_file_read(file, data, buffer_size); + for(uint16_t i = 0; i < read_size; i++) { + putchar(data[i]); + } + file_size -= read_size; } - file_size -= read_size; + free(data); } printf("\r\n"); - free(data); } else { storage_cli_print_error(storage_file_get_error(file)); } @@ -298,11 +301,11 @@ static void storage_cli_read_chunks(Cli* cli, string_t path, string_t args) { storage_file_close(file); storage_file_free(file); - furi_record_close("storage"); + furi_record_close(RECORD_STORAGE); } static void storage_cli_write_chunk(Cli* cli, string_t path, string_t args) { - Storage* api = furi_record_open("storage"); + Storage* api = furi_record_open(RECORD_STORAGE); File* file = storage_file_alloc(api); uint32_t buffer_size; @@ -314,19 +317,21 @@ static void storage_cli_write_chunk(Cli* cli, string_t path, string_t args) { if(storage_file_open(file, string_get_cstr(path), FSAM_WRITE, FSOM_OPEN_APPEND)) { printf("Ready\r\n"); - uint8_t* buffer = malloc(buffer_size); + if(buffer_size) { + uint8_t* buffer = malloc(buffer_size); - for(uint32_t i = 0; i < buffer_size; i++) { - buffer[i] = cli_getc(cli); - } + for(uint32_t i = 0; i < buffer_size; i++) { + buffer[i] = cli_getc(cli); + } - uint16_t written_size = storage_file_write(file, buffer, buffer_size); + uint16_t written_size = storage_file_write(file, buffer, buffer_size); - if(written_size != buffer_size) { - storage_cli_print_error(storage_file_get_error(file)); - } + if(written_size != buffer_size) { + storage_cli_print_error(storage_file_get_error(file)); + } - free(buffer); + free(buffer); + } } else { storage_cli_print_error(storage_file_get_error(file)); } @@ -334,18 +339,19 @@ static void storage_cli_write_chunk(Cli* cli, string_t path, string_t args) { } storage_file_free(file); - furi_record_close("storage"); + furi_record_close(RECORD_STORAGE); } static void storage_cli_stat(Cli* cli, string_t path) { UNUSED(cli); - Storage* api = furi_record_open("storage"); + Storage* api = furi_record_open(RECORD_STORAGE); if(string_cmp_str(path, "/") == 0) { printf("Storage\r\n"); } else if( - string_cmp_str(path, "/ext") == 0 || string_cmp_str(path, "/int") == 0 || - string_cmp_str(path, "/any") == 0) { + string_cmp_str(path, STORAGE_EXT_PATH_PREFIX) == 0 || + string_cmp_str(path, STORAGE_INT_PATH_PREFIX) == 0 || + string_cmp_str(path, STORAGE_ANY_PATH_PREFIX) == 0) { uint64_t total_space; uint64_t free_space; FS_Error error = @@ -374,12 +380,12 @@ static void storage_cli_stat(Cli* cli, string_t path) { } } - furi_record_close("storage"); + furi_record_close(RECORD_STORAGE); } static void storage_cli_copy(Cli* cli, string_t old_path, string_t args) { UNUSED(cli); - Storage* api = furi_record_open("storage"); + Storage* api = furi_record_open(RECORD_STORAGE); string_t new_path; string_init(new_path); @@ -395,24 +401,24 @@ static void storage_cli_copy(Cli* cli, string_t old_path, string_t args) { } string_clear(new_path); - furi_record_close("storage"); + furi_record_close(RECORD_STORAGE); } static void storage_cli_remove(Cli* cli, string_t path) { UNUSED(cli); - Storage* api = furi_record_open("storage"); + Storage* api = furi_record_open(RECORD_STORAGE); FS_Error error = storage_common_remove(api, string_get_cstr(path)); if(error != FSE_OK) { storage_cli_print_error(error); } - furi_record_close("storage"); + furi_record_close(RECORD_STORAGE); } static void storage_cli_rename(Cli* cli, string_t old_path, string_t args) { UNUSED(cli); - Storage* api = furi_record_open("storage"); + Storage* api = furi_record_open(RECORD_STORAGE); string_t new_path; string_init(new_path); @@ -428,24 +434,24 @@ static void storage_cli_rename(Cli* cli, string_t old_path, string_t args) { } string_clear(new_path); - furi_record_close("storage"); + furi_record_close(RECORD_STORAGE); } static void storage_cli_mkdir(Cli* cli, string_t path) { UNUSED(cli); - Storage* api = furi_record_open("storage"); + Storage* api = furi_record_open(RECORD_STORAGE); FS_Error error = storage_common_mkdir(api, string_get_cstr(path)); if(error != FSE_OK) { storage_cli_print_error(error); } - furi_record_close("storage"); + furi_record_close(RECORD_STORAGE); } static void storage_cli_md5(Cli* cli, string_t path) { UNUSED(cli); - Storage* api = furi_record_open("storage"); + Storage* api = furi_record_open(RECORD_STORAGE); File* file = storage_file_alloc(api); if(storage_file_open(file, string_get_cstr(path), FSAM_READ, FSOM_OPEN_EXISTING)) { @@ -478,7 +484,7 @@ static void storage_cli_md5(Cli* cli, string_t path) { storage_file_close(file); storage_file_free(file); - furi_record_close("storage"); + furi_record_close(RECORD_STORAGE); } void storage_cli(Cli* cli, string_t args, void* context) { @@ -579,7 +585,7 @@ void storage_cli(Cli* cli, string_t args, void* context) { static void storage_cli_factory_reset(Cli* cli, string_t args, void* context) { UNUSED(args); UNUSED(context); - printf("All data will be lost. Are you sure (y/n)?\r\n"); + printf("All data will be lost! Are you sure (y/n)?\r\n"); char c = cli_getc(cli); if(c == 'y' || c == 'Y') { printf("Data will be wiped after reboot.\r\n"); @@ -592,11 +598,11 @@ static void storage_cli_factory_reset(Cli* cli, string_t args, void* context) { void storage_on_system_start() { #ifdef SRV_CLI - Cli* cli = furi_record_open("cli"); - cli_add_command(cli, "storage", CliCommandFlagParallelSafe, storage_cli, NULL); + Cli* cli = furi_record_open(RECORD_CLI); + cli_add_command(cli, RECORD_STORAGE, CliCommandFlagParallelSafe, storage_cli, NULL); cli_add_command( cli, "factory_reset", CliCommandFlagParallelSafe, storage_cli_factory_reset, NULL); - furi_record_close("cli"); + furi_record_close(RECORD_CLI); #else UNUSED(storage_cli_factory_reset); #endif diff --git a/applications/storage/storage_external_api.c b/applications/storage/storage_external_api.c index 426fac9a4..b32080dfc 100644 --- a/applications/storage/storage_external_api.c +++ b/applications/storage/storage_external_api.c @@ -447,17 +447,16 @@ static FS_Error storage_merge_recursive(Storage* storage, const char* old_path, const char* new_path) { FS_Error error = storage_common_mkdir(storage, new_path); DirWalk* dir_walk = dir_walk_alloc(storage); - string_t path; - string_t tmp_new_path; - string_t tmp_old_path; + string_t path, file_basename, tmp_new_path; FileInfo fileinfo; string_init(path); + string_init(file_basename); string_init(tmp_new_path); - string_init(tmp_old_path); do { if((error != FSE_OK) && (error != FSE_EXIST)) break; + dir_walk_set_recursive(dir_walk, false); if(!dir_walk_open(dir_walk, old_path)) { error = dir_walk_get_error(dir_walk); break; @@ -472,30 +471,33 @@ static FS_Error } else if(res == DirWalkLast) { break; } else { - string_set(tmp_old_path, path); - string_right(path, strlen(old_path)); - string_printf(tmp_new_path, "%s%s", new_path, string_get_cstr(path)); + path_extract_basename(string_get_cstr(path), file_basename); + path_concat(new_path, string_get_cstr(file_basename), tmp_new_path); if(fileinfo.flags & FSF_DIRECTORY) { if(storage_common_stat(storage, string_get_cstr(tmp_new_path), &fileinfo) == FSE_OK) { if(fileinfo.flags & FSF_DIRECTORY) { error = storage_common_mkdir(storage, string_get_cstr(tmp_new_path)); + if(error != FSE_OK) { + break; + } } } - } else { - error = storage_common_merge( - storage, string_get_cstr(tmp_old_path), string_get_cstr(tmp_new_path)); } + error = storage_common_merge( + storage, string_get_cstr(path), string_get_cstr(tmp_new_path)); - if(error != FSE_OK) break; + if(error != FSE_OK) { + break; + } } } } while(false); string_clear(tmp_new_path); - string_clear(tmp_old_path); + string_clear(file_basename); string_clear(path); dir_walk_free(dir_walk); return error; diff --git a/applications/storage/storage_internal_api.c b/applications/storage/storage_internal_api.c index 093140435..620eae367 100644 --- a/applications/storage/storage_internal_api.c +++ b/applications/storage/storage_internal_api.c @@ -3,20 +3,19 @@ #include "storage.h" #include -#define INT_PATH "/int" - FS_Error storage_int_backup(Storage* api, const char* dstname) { TarArchive* archive = tar_archive_alloc(api); bool success = tar_archive_open(archive, dstname, TAR_OPEN_MODE_WRITE) && - tar_archive_add_dir(archive, INT_PATH, "") && tar_archive_finalize(archive); + tar_archive_add_dir(archive, STORAGE_INT_PATH_PREFIX, "") && + tar_archive_finalize(archive); tar_archive_free(archive); return success ? FSE_OK : FSE_INTERNAL; } -FS_Error storage_int_restore(Storage* api, const char* srcname) { +FS_Error storage_int_restore(Storage* api, const char* srcname, Storage_name_converter converter) { TarArchive* archive = tar_archive_alloc(api); bool success = tar_archive_open(archive, srcname, TAR_OPEN_MODE_READ) && - tar_archive_unpack_to(archive, INT_PATH); + tar_archive_unpack_to(archive, STORAGE_INT_PATH_PREFIX, converter); tar_archive_free(archive); return success ? FSE_OK : FSE_INTERNAL; } diff --git a/applications/storage/storage_processing.c b/applications/storage/storage_processing.c index 0eb8a32cb..46ca4e165 100644 --- a/applications/storage/storage_processing.c +++ b/applications/storage/storage_processing.c @@ -43,17 +43,18 @@ static const char* remove_vfs(const char* path) { return path + MIN(4u, strlen(path)); } -static const char* ext_path = "/ext"; -static const char* int_path = "/int"; -static const char* any_path = "/any"; - static StorageType storage_get_type_by_path(Storage* app, const char* path) { StorageType type = ST_ERROR; - if(strlen(path) >= strlen(ext_path) && memcmp(path, ext_path, strlen(ext_path)) == 0) { + if(strlen(path) >= strlen(STORAGE_EXT_PATH_PREFIX) && + memcmp(path, STORAGE_EXT_PATH_PREFIX, strlen(STORAGE_EXT_PATH_PREFIX)) == 0) { type = ST_EXT; - } else if(strlen(path) >= strlen(int_path) && memcmp(path, int_path, strlen(int_path)) == 0) { + } else if( + strlen(path) >= strlen(STORAGE_INT_PATH_PREFIX) && + memcmp(path, STORAGE_INT_PATH_PREFIX, strlen(STORAGE_INT_PATH_PREFIX)) == 0) { type = ST_INT; - } else if(strlen(path) >= strlen(any_path) && memcmp(path, any_path, strlen(any_path)) == 0) { + } else if( + strlen(path) >= strlen(STORAGE_ANY_PATH_PREFIX) && + memcmp(path, STORAGE_ANY_PATH_PREFIX, strlen(STORAGE_ANY_PATH_PREFIX)) == 0) { type = ST_ANY; } @@ -68,19 +69,20 @@ static StorageType storage_get_type_by_path(Storage* app, const char* path) { } static void storage_path_change_to_real_storage(string_t path, StorageType real_storage) { - if(memcmp(string_get_cstr(path), any_path, strlen(any_path)) == 0) { + if(memcmp(string_get_cstr(path), STORAGE_ANY_PATH_PREFIX, strlen(STORAGE_ANY_PATH_PREFIX)) == + 0) { switch(real_storage) { case ST_EXT: - string_set_char(path, 0, ext_path[0]); - string_set_char(path, 1, ext_path[1]); - string_set_char(path, 2, ext_path[2]); - string_set_char(path, 3, ext_path[3]); + string_set_char(path, 0, STORAGE_EXT_PATH_PREFIX[0]); + string_set_char(path, 1, STORAGE_EXT_PATH_PREFIX[1]); + string_set_char(path, 2, STORAGE_EXT_PATH_PREFIX[2]); + string_set_char(path, 3, STORAGE_EXT_PATH_PREFIX[3]); break; case ST_INT: - string_set_char(path, 0, int_path[0]); - string_set_char(path, 1, int_path[1]); - string_set_char(path, 2, int_path[2]); - string_set_char(path, 3, int_path[3]); + string_set_char(path, 0, STORAGE_INT_PATH_PREFIX[0]); + string_set_char(path, 1, STORAGE_INT_PATH_PREFIX[1]); + string_set_char(path, 2, STORAGE_INT_PATH_PREFIX[2]); + string_set_char(path, 3, STORAGE_INT_PATH_PREFIX[3]); break; default: break; diff --git a/applications/storage/storage_test_app.c b/applications/storage/storage_test_app.c index 226024b30..8bfa9826c 100644 --- a/applications/storage/storage_test_app.c +++ b/applications/storage/storage_test_app.c @@ -317,22 +317,22 @@ static void do_test_end(Storage* api, const char* path) { int32_t storage_test_app(void* p) { UNUSED(p); - Storage* api = furi_record_open("storage"); - do_test_start(api, "/int"); - do_test_start(api, "/any"); - do_test_start(api, "/ext"); - - do_file_test(api, "/int/test.txt"); - do_file_test(api, "/any/test.txt"); - do_file_test(api, "/ext/test.txt"); - - do_dir_test(api, "/int"); - do_dir_test(api, "/any"); - do_dir_test(api, "/ext"); - - do_test_end(api, "/int"); - do_test_end(api, "/any"); - do_test_end(api, "/ext"); + Storage* api = furi_record_open(RECORD_STORAGE); + do_test_start(api, STORAGE_INT_PATH_PREFIX); + do_test_start(api, STORAGE_ANY_PATH_PREFIX); + do_test_start(api, STORAGE_EXT_PATH_PREFIX); + + do_file_test(api, INT_PATH("test.txt")); + do_file_test(api, ANY_PATH("test.txt")); + do_file_test(api, EXT_PATH("test.txt")); + + do_dir_test(api, STORAGE_INT_PATH_PREFIX); + do_dir_test(api, STORAGE_ANY_PATH_PREFIX); + do_dir_test(api, STORAGE_EXT_PATH_PREFIX); + + do_test_end(api, STORAGE_INT_PATH_PREFIX); + do_test_end(api, STORAGE_ANY_PATH_PREFIX); + do_test_end(api, STORAGE_EXT_PATH_PREFIX); while(true) { furi_delay_ms(1000); diff --git a/applications/storage/storages/storage_ext.c b/applications/storage/storages/storage_ext.c index abfcb0e72..7341a6ec8 100644 --- a/applications/storage/storages/storage_ext.c +++ b/applications/storage/storages/storage_ext.c @@ -11,7 +11,7 @@ typedef FILINFO SDFileInfo; typedef FRESULT SDError; #define TAG "StorageExt" -#define STORAGE_PATH "/ext" + /********************* Definitions ********************/ typedef struct { @@ -26,8 +26,7 @@ static FS_Error storage_ext_parse_error(SDError error); static bool sd_mount_card(StorageData* storage, bool notify) { bool result = false; - const uint8_t max_init_counts = 10; - uint8_t counter = max_init_counts; + uint8_t counter = BSP_SD_MaxMountRetryCount(); uint8_t bsp_result; SDData* sd_data = storage->data; @@ -35,9 +34,9 @@ static bool sd_mount_card(StorageData* storage, bool notify) { while(result == false && counter > 0 && hal_sd_detect()) { if(notify) { - NotificationApp* notification = furi_record_open("notification"); + NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION); sd_notify_wait(notification); - furi_record_close("notification"); + furi_record_close(RECORD_NOTIFICATION); } if((counter % 2) == 0) { @@ -78,9 +77,9 @@ static bool sd_mount_card(StorageData* storage, bool notify) { } if(notify) { - NotificationApp* notification = furi_record_open("notification"); + NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION); sd_notify_wait_off(notification); - furi_record_close("notification"); + furi_record_close(RECORD_NOTIFICATION); } if(!result) { @@ -224,16 +223,16 @@ static void storage_ext_tick_internal(StorageData* storage, bool notify) { if(storage->status != StorageStatusOK) { FURI_LOG_E(TAG, "sd init error: %s", storage_data_status_text(storage)); if(notify) { - NotificationApp* notification = furi_record_open("notification"); + NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION); sd_notify_error(notification); - furi_record_close("notification"); + furi_record_close(RECORD_NOTIFICATION); } } else { FURI_LOG_I(TAG, "card mounted"); if(notify) { - NotificationApp* notification = furi_record_open("notification"); + NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION); sd_notify_success(notification); - furi_record_close("notification"); + furi_record_close(RECORD_NOTIFICATION); } } @@ -252,9 +251,9 @@ static void storage_ext_tick_internal(StorageData* storage, bool notify) { sd_unmount_card(storage); if(notify) { - NotificationApp* notification = furi_record_open("notification"); + NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION); sd_notify_eject(notification); - furi_record_close("notification"); + furi_record_close(RECORD_NOTIFICATION); } } } diff --git a/applications/storage/storages/storage_int.c b/applications/storage/storages/storage_int.c index bdd78172e..0765a92dc 100644 --- a/applications/storage/storages/storage_int.c +++ b/applications/storage/storages/storage_int.c @@ -1,11 +1,16 @@ #include "storage_int.h" #include #include +#include #define TAG "StorageInt" -#define STORAGE_PATH "/int" +#define STORAGE_PATH STORAGE_INT_PATH_PREFIX #define LFS_CLEAN_FINGERPRINT 0 +/* When less than LFS_RESERVED_PAGES_COUNT are left free, creation & + * modification of non-dot files is restricted */ +#define LFS_RESERVED_PAGES_COUNT 5 + typedef struct { const size_t start_address; const size_t start_page; @@ -297,6 +302,20 @@ static FS_Error storage_int_parse_error(int error) { return result; } +/* Returns false if less than reserved space is left free */ +static bool storage_int_check_for_free_space(StorageData* storage) { + LFSData* lfs_data = lfs_data_get_from_storage(storage); + + lfs_ssize_t result = lfs_fs_size(lfs_get_from_storage(storage)); + if(result >= 0) { + lfs_size_t free_space = + (lfs_data->config.block_count - result) * lfs_data->config.block_size; + + return (free_space > LFS_RESERVED_PAGES_COUNT * furi_hal_flash_get_page_size()); + } + + return false; +} /******************* File Functions *******************/ static bool storage_int_file_open( @@ -308,6 +327,8 @@ static bool storage_int_file_open( StorageData* storage = ctx; lfs_t* lfs = lfs_get_from_storage(storage); + bool enough_free_space = storage_int_check_for_free_space(storage); + int flags = 0; if(access_mode & FSAM_READ) flags |= LFS_O_RDONLY; @@ -321,6 +342,23 @@ static bool storage_int_file_open( LFSHandle* handle = lfs_handle_alloc_file(); storage_set_storage_file_data(file, handle, storage); + + if(!enough_free_space) { + string_t filename; + string_init(filename); + path_extract_basename(path, filename); + bool is_dot_file = (!string_empty_p(filename) && (string_get_char(filename, 0) == '.')); + string_clear(filename); + + /* Restrict write & creation access to all non-dot files */ + if(!is_dot_file && (flags & (LFS_O_CREAT | LFS_O_WRONLY))) { + file->internal_error_id = LFS_ERR_NOSPC; + file->error_id = FSE_DENIED; + FURI_LOG_W(TAG, "Denied access to '%s': no free space", path); + return false; + } + } + file->internal_error_id = lfs_file_open(lfs, lfs_handle_get_file(handle), path, flags); if(file->internal_error_id >= LFS_ERR_OK) { @@ -328,6 +366,7 @@ static bool storage_int_file_open( } file->error_id = storage_int_parse_error(file->internal_error_id); + return (file->error_id == FSE_OK); } diff --git a/applications/storage_move_to_sd/application.fam b/applications/storage_move_to_sd/application.fam index 60a6d9876..de47de055 100644 --- a/applications/storage_move_to_sd/application.fam +++ b/applications/storage_move_to_sd/application.fam @@ -3,7 +3,7 @@ App( name="StorageMoveToSd", apptype=FlipperAppType.SYSTEM, entry_point="storage_move_to_sd_app", - requires=["gui","storage"], + requires=["gui", "storage"], provides=["storage_move_to_sd_start"], stack_size=2 * 1024, order=30, @@ -16,4 +16,3 @@ App( requires=["storage"], order=120, ) - diff --git a/applications/storage_move_to_sd/storage_move_to_sd.c b/applications/storage_move_to_sd/storage_move_to_sd.c index fe5807d19..e5b195d55 100644 --- a/applications/storage_move_to_sd/storage_move_to_sd.c +++ b/applications/storage_move_to_sd/storage_move_to_sd.c @@ -4,66 +4,78 @@ #include "loader/loader.h" #include "m-string.h" #include +#include +#include #define TAG "MoveToSd" -#define MOVE_SRC "/int" -#define MOVE_DST "/ext" +#define MOVE_SRC STORAGE_INT_PATH_PREFIX +#define MOVE_DST STORAGE_EXT_PATH_PREFIX -static const char* app_dirs[] = { - "subghz", - "lfrfid", - "nfc", - "infrared", - "ibutton", - "badusb", -}; +static bool storage_move_to_sd_check_entry(const char* name, FileInfo* fileinfo, void* ctx) { + UNUSED(ctx); + if((fileinfo->flags & FSF_DIRECTORY) != 0) { + return true; + } + + return (name && (*name != '.')); +} bool storage_move_to_sd_perform(void) { - Storage* storage = furi_record_open("storage"); - string_t path_src; - string_t path_dst; - string_init(path_src); + Storage* storage = furi_record_open(RECORD_STORAGE); + + DirWalk* dir_walk = dir_walk_alloc(storage); + dir_walk_set_recursive(dir_walk, false); + dir_walk_set_filter_cb(dir_walk, storage_move_to_sd_check_entry, NULL); + + string_t path_src, path_dst; + string_init(path_dst); + string_init(path_src); + + if(dir_walk_open(dir_walk, STORAGE_INT_PATH_PREFIX)) { + while(dir_walk_read(dir_walk, path_src, NULL) == DirWalkOK) { + string_set(path_dst, path_src); + string_replace_at( + path_dst, 0, strlen(STORAGE_INT_PATH_PREFIX), STORAGE_EXT_PATH_PREFIX); - for(uint32_t i = 0; i < COUNT_OF(app_dirs); i++) { - string_printf(path_src, "%s/%s", MOVE_SRC, app_dirs[i]); - string_printf(path_dst, "%s/%s", MOVE_DST, app_dirs[i]); - storage_common_merge(storage, string_get_cstr(path_src), string_get_cstr(path_dst)); - storage_simply_remove_recursive(storage, string_get_cstr(path_src)); + storage_common_merge(storage, string_get_cstr(path_src), string_get_cstr(path_dst)); + storage_simply_remove_recursive(storage, string_get_cstr(path_src)); + } } - string_clear(path_src); + dir_walk_free(dir_walk); string_clear(path_dst); + string_clear(path_src); - furi_record_close("storage"); + furi_record_close(RECORD_STORAGE); return false; } static bool storage_move_to_sd_check(void) { - Storage* storage = furi_record_open("storage"); - - FileInfo file_info; - bool state = false; - string_t path; - string_init(path); - - for(uint32_t i = 0; i < COUNT_OF(app_dirs); i++) { - string_printf(path, "%s/%s", MOVE_SRC, app_dirs[i]); - if(storage_common_stat(storage, string_get_cstr(path), &file_info) == FSE_OK) { - if((file_info.flags & FSF_DIRECTORY) != 0) { - state = true; - break; - } - } + Storage* storage = furi_record_open(RECORD_STORAGE); + + bool should_migrate = false; + + DirWalk* dir_walk = dir_walk_alloc(storage); + dir_walk_set_recursive(dir_walk, false); + dir_walk_set_filter_cb(dir_walk, storage_move_to_sd_check_entry, NULL); + + string_t name; + string_init(name); + + if(dir_walk_open(dir_walk, STORAGE_INT_PATH_PREFIX)) { + // if at least 1 entry is present, we should migrate + should_migrate = (dir_walk_read(dir_walk, name, NULL) == DirWalkOK); } - string_clear(path); + dir_walk_free(dir_walk); + string_clear(name); - furi_record_close("storage"); + furi_record_close(RECORD_STORAGE); - return state; + return should_migrate; } static bool storage_move_to_sd_custom_event_callback(void* context, uint32_t event) { @@ -92,8 +104,8 @@ static void storage_move_to_sd_unmount_callback(const void* message, void* conte static StorageMoveToSd* storage_move_to_sd_alloc() { StorageMoveToSd* app = malloc(sizeof(StorageMoveToSd)); - app->gui = furi_record_open("gui"); - app->notifications = furi_record_open("notification"); + app->gui = furi_record_open(RECORD_GUI); + app->notifications = furi_record_open(RECORD_NOTIFICATION); app->view_dispatcher = view_dispatcher_alloc(); app->scene_manager = scene_manager_alloc(&storage_move_to_sd_scene_handlers, app); @@ -114,26 +126,26 @@ static StorageMoveToSd* storage_move_to_sd_alloc() { scene_manager_next_scene(app->scene_manager, StorageMoveToSdConfirm); - Storage* storage = furi_record_open("storage"); + Storage* storage = furi_record_open(RECORD_STORAGE); app->sub = furi_pubsub_subscribe( storage_get_pubsub(storage), storage_move_to_sd_unmount_callback, app); - furi_record_close("storage"); + furi_record_close(RECORD_STORAGE); return app; } static void storage_move_to_sd_free(StorageMoveToSd* app) { - Storage* storage = furi_record_open("storage"); + Storage* storage = furi_record_open(RECORD_STORAGE); furi_pubsub_unsubscribe(storage_get_pubsub(storage), app->sub); - furi_record_close("storage"); - furi_record_close("notification"); + furi_record_close(RECORD_STORAGE); + furi_record_close(RECORD_NOTIFICATION); view_dispatcher_remove_view(app->view_dispatcher, StorageMoveToSdViewWidget); widget_free(app->widget); view_dispatcher_free(app->view_dispatcher); scene_manager_free(app->scene_manager); - furi_record_close("gui"); + furi_record_close(RECORD_GUI); free(app); } @@ -159,18 +171,18 @@ static void storage_move_to_sd_mount_callback(const void* message, void* context const StorageEvent* storage_event = message; if(storage_event->type == StorageEventTypeCardMount) { - Loader* loader = furi_record_open("loader"); + Loader* loader = furi_record_open(RECORD_LOADER); loader_start(loader, "StorageMoveToSd", NULL); - furi_record_close("loader"); + furi_record_close(RECORD_LOADER); } } int32_t storage_move_to_sd_start(void* p) { UNUSED(p); - Storage* storage = furi_record_open("storage"); + Storage* storage = furi_record_open(RECORD_STORAGE); furi_pubsub_subscribe(storage_get_pubsub(storage), storage_move_to_sd_mount_callback, NULL); - furi_record_close("storage"); + furi_record_close(RECORD_STORAGE); return 0; } diff --git a/applications/storage_settings/scenes/storage_settings_scene_benchmark.c b/applications/storage_settings/scenes/storage_settings_scene_benchmark.c index e7c55ad17..615e07f8b 100644 --- a/applications/storage_settings/scenes/storage_settings_scene_benchmark.c +++ b/applications/storage_settings/scenes/storage_settings_scene_benchmark.c @@ -4,7 +4,7 @@ #define BENCH_DATA_SIZE 4096 #define BENCH_COUNT 6 #define BENCH_REPEATS 4 -#define BENCH_FILE "/ext/rwfiletest.bin" +#define BENCH_FILE EXT_PATH("rwfiletest.bin") static void storage_settings_scene_benchmark_dialog_callback(DialogExResult result, void* context) { @@ -75,7 +75,7 @@ static bool static void storage_settings_scene_benchmark(StorageSettings* app) { DialogEx* dialog_ex = app->dialog_ex; uint8_t* bench_data; - dialog_ex_set_header(dialog_ex, "Preparing data...", 64, 32, AlignCenter, AlignCenter); + dialog_ex_set_header(dialog_ex, "Preparing Data...", 64, 32, AlignCenter, AlignCenter); bench_data = malloc(BENCH_DATA_SIZE); for(size_t i = 0; i < BENCH_DATA_SIZE; i++) { @@ -122,14 +122,10 @@ void storage_settings_scene_benchmark_on_enter(void* context) { view_dispatcher_switch_to_view(app->view_dispatcher, StorageSettingsViewDialogEx); if(sd_status != FSE_OK) { - dialog_ex_set_header(dialog_ex, "SD card not mounted", 64, 10, AlignCenter, AlignCenter); + dialog_ex_set_icon(dialog_ex, 72, 14, &I_DolphinFirstStart8_56x51); + dialog_ex_set_header(dialog_ex, "SD Card Not Mounted", 64, 3, AlignCenter, AlignTop); dialog_ex_set_text( - dialog_ex, - "If an SD card is inserted,\r\npull it out and reinsert it", - 64, - 32, - AlignCenter, - AlignCenter); + dialog_ex, "Try to reinsert\nor format SD\ncard.", 3, 19, AlignLeft, AlignTop); dialog_ex_set_center_button_text(dialog_ex, "Ok"); } else { storage_settings_scene_benchmark(app); diff --git a/applications/storage_settings/scenes/storage_settings_scene_factory_reset.c b/applications/storage_settings/scenes/storage_settings_scene_factory_reset.c index 84119422a..a69479681 100644 --- a/applications/storage_settings/scenes/storage_settings_scene_factory_reset.c +++ b/applications/storage_settings/scenes/storage_settings_scene_factory_reset.c @@ -24,7 +24,7 @@ void storage_settings_scene_factory_reset_on_enter(void* context) { dialog_ex_set_header(dialog_ex, "Confirm Factory Reset", 64, 10, AlignCenter, AlignCenter); dialog_ex_set_text( dialog_ex, - "Internal storage will be erased\r\nData and setting will be lost", + "Internal storage will be erased\r\nData and setting will be lost!", 64, 32, AlignCenter, diff --git a/applications/storage_settings/scenes/storage_settings_scene_format_confirm.c b/applications/storage_settings/scenes/storage_settings_scene_format_confirm.c index 1ebe8dfbd..ebf7dece4 100644 --- a/applications/storage_settings/scenes/storage_settings_scene_format_confirm.c +++ b/applications/storage_settings/scenes/storage_settings_scene_format_confirm.c @@ -14,18 +14,14 @@ void storage_settings_scene_format_confirm_on_enter(void* context) { FS_Error sd_status = storage_sd_status(app->fs_api); if(sd_status == FSE_NOT_READY) { - dialog_ex_set_header(dialog_ex, "SD card not mounted", 64, 10, AlignCenter, AlignCenter); + dialog_ex_set_icon(dialog_ex, 72, 14, &I_DolphinFirstStart8_56x51); + dialog_ex_set_header(dialog_ex, "SD Card Not Mounted", 64, 3, AlignCenter, AlignTop); dialog_ex_set_text( - dialog_ex, - "If an SD card is inserted,\r\npull it out and reinsert it", - 64, - 32, - AlignCenter, - AlignCenter); + dialog_ex, "Try to reinsert\nor format SD\ncard.", 3, 19, AlignLeft, AlignTop); dialog_ex_set_center_button_text(dialog_ex, "Ok"); } else { - dialog_ex_set_header(dialog_ex, "Format SD card?", 64, 10, AlignCenter, AlignCenter); - dialog_ex_set_text(dialog_ex, "All data will be lost", 64, 32, AlignCenter, AlignCenter); + dialog_ex_set_header(dialog_ex, "Format SD Card?", 64, 10, AlignCenter, AlignCenter); + dialog_ex_set_text(dialog_ex, "All data will be lost!", 64, 32, AlignCenter, AlignCenter); dialog_ex_set_left_button_text(dialog_ex, "Cancel"); dialog_ex_set_right_button_text(dialog_ex, "Format"); } diff --git a/applications/storage_settings/scenes/storage_settings_scene_formatting.c b/applications/storage_settings/scenes/storage_settings_scene_formatting.c index 2cbf97eea..e0d8dfca8 100755 --- a/applications/storage_settings/scenes/storage_settings_scene_formatting.c +++ b/applications/storage_settings/scenes/storage_settings_scene_formatting.c @@ -43,11 +43,12 @@ void storage_settings_scene_formatting_on_enter(void* context) { dialog_ex_set_result_callback(dialog_ex, storage_settings_scene_formatting_dialog_callback); if(error != FSE_OK) { - dialog_ex_set_header(dialog_ex, "Cannot format SD Card", 64, 10, AlignCenter, AlignCenter); + dialog_ex_set_header(dialog_ex, "Cannot Format SD Card", 64, 10, AlignCenter, AlignCenter); dialog_ex_set_text( dialog_ex, storage_error_get_desc(error), 64, 32, AlignCenter, AlignCenter); } else { - dialog_ex_set_header(dialog_ex, "Format complete!", 64, 32, AlignCenter, AlignCenter); + dialog_ex_set_icon(dialog_ex, 72, 14, &I_DolphinFirstStart8_56x51); + dialog_ex_set_header(dialog_ex, "Format\ncomplete!", 14, 15, AlignLeft, AlignTop); } dialog_ex_set_center_button_text(dialog_ex, "OK"); } diff --git a/applications/storage_settings/scenes/storage_settings_scene_internal_info.c b/applications/storage_settings/scenes/storage_settings_scene_internal_info.c index 53f791bd8..76c7fd0ec 100644 --- a/applications/storage_settings/scenes/storage_settings_scene_internal_info.c +++ b/applications/storage_settings/scenes/storage_settings_scene_internal_info.c @@ -12,7 +12,8 @@ void storage_settings_scene_internal_info_on_enter(void* context) { StorageSettings* app = context; uint64_t total_space; uint64_t free_space; - FS_Error error = storage_common_fs_info(app->fs_api, "/int", &total_space, &free_space); + FS_Error error = + storage_common_fs_info(app->fs_api, STORAGE_INT_PATH_PREFIX, &total_space, &free_space); DialogEx* dialog_ex = app->dialog_ex; dialog_ex_set_context(dialog_ex, app); @@ -20,7 +21,7 @@ void storage_settings_scene_internal_info_on_enter(void* context) { if(error != FSE_OK) { dialog_ex_set_header( - dialog_ex, "Internal storage error", 64, 10, AlignCenter, AlignCenter); + dialog_ex, "Internal Storage Error", 64, 10, AlignCenter, AlignCenter); dialog_ex_set_text( dialog_ex, storage_error_get_desc(error), 64, 32, AlignCenter, AlignCenter); } else { diff --git a/applications/storage_settings/scenes/storage_settings_scene_sd_info.c b/applications/storage_settings/scenes/storage_settings_scene_sd_info.c index 69ad1ea57..485368c55 100644 --- a/applications/storage_settings/scenes/storage_settings_scene_sd_info.c +++ b/applications/storage_settings/scenes/storage_settings_scene_sd_info.c @@ -18,14 +18,10 @@ void storage_settings_scene_sd_info_on_enter(void* context) { dialog_ex_set_result_callback(dialog_ex, storage_settings_scene_sd_info_dialog_callback); if(sd_status != FSE_OK) { - dialog_ex_set_header(dialog_ex, "SD card not mounted", 64, 10, AlignCenter, AlignCenter); + dialog_ex_set_icon(dialog_ex, 72, 14, &I_DolphinFirstStart8_56x51); + dialog_ex_set_header(dialog_ex, "SD Card Not Mounted", 64, 3, AlignCenter, AlignTop); dialog_ex_set_text( - dialog_ex, - "If an SD card is inserted,\r\npull it out and reinsert it", - 64, - 32, - AlignCenter, - AlignCenter); + dialog_ex, "Try to reinsert\nor format SD\ncard.", 3, 19, AlignLeft, AlignTop); dialog_ex_set_center_button_text(dialog_ex, "Ok"); } else { string_printf( diff --git a/applications/storage_settings/scenes/storage_settings_scene_unmount_confirm.c b/applications/storage_settings/scenes/storage_settings_scene_unmount_confirm.c index 001ce7684..971870715 100644 --- a/applications/storage_settings/scenes/storage_settings_scene_unmount_confirm.c +++ b/applications/storage_settings/scenes/storage_settings_scene_unmount_confirm.c @@ -14,17 +14,13 @@ void storage_settings_scene_unmount_confirm_on_enter(void* context) { FS_Error sd_status = storage_sd_status(app->fs_api); if(sd_status == FSE_NOT_READY) { - dialog_ex_set_header(dialog_ex, "SD card not mounted", 64, 10, AlignCenter, AlignCenter); + dialog_ex_set_icon(dialog_ex, 72, 14, &I_DolphinFirstStart8_56x51); + dialog_ex_set_header(dialog_ex, "SD Card Not Mounted", 64, 3, AlignCenter, AlignTop); dialog_ex_set_text( - dialog_ex, - "If an SD card is inserted,\r\npull it out and reinsert it", - 64, - 32, - AlignCenter, - AlignCenter); - dialog_ex_set_center_button_text(dialog_ex, "OK"); + dialog_ex, "Try to reinsert\nor format SD\ncard.", 3, 19, AlignLeft, AlignTop); + dialog_ex_set_center_button_text(dialog_ex, "Ok"); } else { - dialog_ex_set_header(dialog_ex, "Unmount SD card?", 64, 10, AlignCenter, AlignCenter); + dialog_ex_set_header(dialog_ex, "Unmount SD Card?", 64, 10, AlignCenter, AlignCenter); dialog_ex_set_text( dialog_ex, "SD card will be\nunavailable", 64, 32, AlignCenter, AlignCenter); dialog_ex_set_left_button_text(dialog_ex, "Cancel"); diff --git a/applications/storage_settings/scenes/storage_settings_scene_unmounted.c b/applications/storage_settings/scenes/storage_settings_scene_unmounted.c old mode 100755 new mode 100644 index ddd70d055..43f44583d --- a/applications/storage_settings/scenes/storage_settings_scene_unmounted.c +++ b/applications/storage_settings/scenes/storage_settings_scene_unmounted.c @@ -13,17 +13,15 @@ void storage_settings_scene_unmounted_on_enter(void* context) { DialogEx* dialog_ex = app->dialog_ex; dialog_ex_set_center_button_text(dialog_ex, "OK"); + dialog_ex_set_icon(dialog_ex, 72, 14, &I_DolphinFirstStart8_56x51); if(error == FSE_OK) { - dialog_ex_set_header(dialog_ex, "SD card unmounted", 64, 10, AlignCenter, AlignCenter); - dialog_ex_set_text( - dialog_ex, "Now the SD card\ncan be removed.", 64, 32, AlignCenter, AlignCenter); + dialog_ex_set_header(dialog_ex, "SD Card Unmounted", 64, 3, AlignCenter, AlignTop); + dialog_ex_set_text(dialog_ex, "You can remove\nSD card now.", 3, 22, AlignLeft, AlignTop); notification_message(app->notification, &sequence_blink_green_100); } else { - dialog_ex_set_header( - dialog_ex, "Cannot unmount SD Card", 64, 10, AlignCenter, AlignCenter); - dialog_ex_set_text( - dialog_ex, storage_error_get_desc(error), 64, 32, AlignCenter, AlignCenter); + dialog_ex_set_header(dialog_ex, "Cannot Unmount SD Card", 64, 3, AlignCenter, AlignTop); + dialog_ex_set_text(dialog_ex, storage_error_get_desc(error), 3, 22, AlignLeft, AlignTop); notification_message(app->notification, &sequence_blink_red_100); } diff --git a/applications/storage_settings/storage_settings.c b/applications/storage_settings/storage_settings.c index b89fdf060..f580e6369 100644 --- a/applications/storage_settings/storage_settings.c +++ b/applications/storage_settings/storage_settings.c @@ -15,9 +15,9 @@ static bool storage_settings_back_event_callback(void* context) { static StorageSettings* storage_settings_alloc() { StorageSettings* app = malloc(sizeof(StorageSettings)); - app->gui = furi_record_open("gui"); - app->fs_api = furi_record_open("storage"); - app->notification = furi_record_open("notification"); + app->gui = furi_record_open(RECORD_GUI); + app->fs_api = furi_record_open(RECORD_STORAGE); + app->notification = furi_record_open(RECORD_NOTIFICATION); app->view_dispatcher = view_dispatcher_alloc(); app->scene_manager = scene_manager_alloc(&storage_settings_scene_handlers, app); @@ -56,9 +56,9 @@ static void storage_settings_free(StorageSettings* app) { view_dispatcher_free(app->view_dispatcher); scene_manager_free(app->scene_manager); - furi_record_close("gui"); - furi_record_close("storage"); - furi_record_close("notification"); + furi_record_close(RECORD_GUI); + furi_record_close(RECORD_STORAGE); + furi_record_close(RECORD_NOTIFICATION); string_clear(app->text_string); diff --git a/applications/subghz/helpers/subghz_custom_event.h b/applications/subghz/helpers/subghz_custom_event.h index 05f0f4924..765c9e251 100644 --- a/applications/subghz/helpers/subghz_custom_event.h +++ b/applications/subghz/helpers/subghz_custom_event.h @@ -46,6 +46,11 @@ typedef enum { SubGhzCustomEventSceneExit, SubGhzCustomEventSceneStay, + SubGhzCustomEventSceneRpcLoad, + SubGhzCustomEventSceneRpcButtonPress, + SubGhzCustomEventSceneRpcButtonRelease, + SubGhzCustomEventSceneRpcSessionClose, + SubGhzCustomEventViewReceiverOK, SubGhzCustomEventViewReceiverConfig, SubGhzCustomEventViewReceiverBack, diff --git a/applications/subghz/helpers/subghz_types.h b/applications/subghz/helpers/subghz_types.h index 8d2dcf17c..8da4c8f53 100644 --- a/applications/subghz/helpers/subghz_types.h +++ b/applications/subghz/helpers/subghz_types.h @@ -1,5 +1,9 @@ #pragma once +#include "m-string.h" +#include +#include + /** SubGhzNotification state */ typedef enum { SubGhzNotificationStateStarting, @@ -67,3 +71,12 @@ typedef enum { SubGhzViewIdTestCarrier, SubGhzViewIdTestPacket, } SubGhzViewId; + +struct SubGhzPresetDefinition { + string_t name; + uint32_t frequency; + uint8_t* data; + size_t data_size; +}; + +typedef struct SubGhzPresetDefinition SubGhzPresetDefinition; diff --git a/applications/subghz/scenes/subghz_scene_need_saving.c b/applications/subghz/scenes/subghz_scene_need_saving.c index ae76fbb17..eb70223a7 100644 --- a/applications/subghz/scenes/subghz_scene_need_saving.c +++ b/applications/subghz/scenes/subghz_scene_need_saving.c @@ -48,8 +48,12 @@ bool subghz_scene_need_saving_on_event(void* context, SceneManagerEvent event) { } else if(event.event == SubGhzCustomEventSceneExit) { if(subghz->txrx->rx_key_state == SubGhzRxKeyStateExit) { subghz->txrx->rx_key_state = SubGhzRxKeyStateIDLE; - subghz->txrx->frequency = subghz_setting_get_default_frequency(subghz->setting); - subghz->txrx->preset = FuriHalSubGhzPresetOok650Async; + subghz_preset_init( + subghz, + "AM650", + subghz_setting_get_default_frequency(subghz->setting), + NULL, + 0); scene_manager_search_and_switch_to_previous_scene( subghz->scene_manager, SubGhzSceneStart); } else { diff --git a/applications/subghz/scenes/subghz_scene_read_raw.c b/applications/subghz/scenes/subghz_scene_read_raw.c index 598f235e3..38b73e07d 100644 --- a/applications/subghz/scenes/subghz_scene_read_raw.c +++ b/applications/subghz/scenes/subghz_scene_read_raw.c @@ -131,8 +131,12 @@ bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) { scene_manager_next_scene(subghz->scene_manager, SubGhzSceneNeedSaving); } else { //Restore default setting - subghz->txrx->frequency = subghz_setting_get_default_frequency(subghz->setting); - subghz->txrx->preset = FuriHalSubGhzPresetOok650Async; + subghz_preset_init( + subghz, + "AM650", + subghz_setting_get_default_frequency(subghz->setting), + NULL, + 0); if(!scene_manager_search_and_switch_to_previous_scene( subghz->scene_manager, SubGhzSceneSaved)) { if(!scene_manager_search_and_switch_to_previous_scene( @@ -202,6 +206,7 @@ bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) { if((subghz->txrx->txrx_state == SubGhzTxRxStateIDLE) || (subghz->txrx->txrx_state == SubGhzTxRxStateSleep)) { if(!subghz_tx_start(subghz, subghz->txrx->fff_data)) { + subghz->txrx->rx_key_state = SubGhzRxKeyStateBack; scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowOnlyRx); } else { DOLPHIN_DEED(DolphinDeedSubGhzSend); @@ -267,13 +272,15 @@ bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) { if(subghz_protocol_raw_save_to_file_init( (SubGhzProtocolDecoderRAW*)subghz->txrx->decoder_result, RAW_FILE_NAME, - subghz->txrx->frequency, subghz->txrx->preset)) { DOLPHIN_DEED(DolphinDeedSubGhzRawRec); if((subghz->txrx->txrx_state == SubGhzTxRxStateIDLE) || (subghz->txrx->txrx_state == SubGhzTxRxStateSleep)) { - subghz_begin(subghz, subghz->txrx->preset); - subghz_rx(subghz, subghz->txrx->frequency); + subghz_begin( + subghz, + subghz_setting_get_preset_data_by_name( + subghz->setting, string_get_cstr(subghz->txrx->preset->name))); + subghz_rx(subghz, subghz->txrx->preset->frequency); } subghz->state_notifications = SubGhzNotificationStateRx; subghz->txrx->rx_key_state = SubGhzRxKeyStateAddKey; diff --git a/applications/subghz/scenes/subghz_scene_receiver.c b/applications/subghz/scenes/subghz_scene_receiver.c index 55302b01f..7c1f016d0 100644 --- a/applications/subghz/scenes/subghz_scene_receiver.c +++ b/applications/subghz/scenes/subghz_scene_receiver.c @@ -75,8 +75,7 @@ static void subghz_scene_add_to_history_callback( string_t str_buff; string_init(str_buff); - if(subghz_history_add_to_history( - subghz->txrx->history, decoder_base, subghz->txrx->frequency, subghz->txrx->preset)) { + if(subghz_history_add_to_history(subghz->txrx->history, decoder_base, subghz->txrx->preset)) { string_reset(str_buff); subghz->state_notifications = SubGhzNotificationStateRxDone; @@ -103,8 +102,8 @@ void subghz_scene_receiver_on_enter(void* context) { string_init(str_buff); if(subghz->txrx->rx_key_state == SubGhzRxKeyStateIDLE) { - subghz->txrx->frequency = subghz_setting_get_default_frequency(subghz->setting); - subghz->txrx->preset = FuriHalSubGhzPresetOok650Async; + subghz_preset_init( + subghz, "AM650", subghz_setting_get_default_frequency(subghz->setting), NULL, 0); subghz_history_reset(subghz->txrx->history); subghz->txrx->rx_key_state = SubGhzRxKeyStateStart; } @@ -135,8 +134,11 @@ void subghz_scene_receiver_on_enter(void* context) { }; if((subghz->txrx->txrx_state == SubGhzTxRxStateIDLE) || (subghz->txrx->txrx_state == SubGhzTxRxStateSleep)) { - subghz_begin(subghz, subghz->txrx->preset); - subghz_rx(subghz, subghz->txrx->frequency); + subghz_begin( + subghz, + subghz_setting_get_preset_data_by_name( + subghz->setting, string_get_cstr(subghz->txrx->preset->name))); + subghz_rx(subghz, subghz->txrx->preset->frequency); } subghz_view_receiver_set_idx_menu(subghz->subghz_receiver, subghz->txrx->idx_menu_chosen); @@ -164,8 +166,12 @@ bool subghz_scene_receiver_on_event(void* context, SceneManagerEvent event) { scene_manager_next_scene(subghz->scene_manager, SubGhzSceneNeedSaving); } else { subghz->txrx->rx_key_state = SubGhzRxKeyStateIDLE; - subghz->txrx->frequency = subghz_setting_get_default_frequency(subghz->setting); - subghz->txrx->preset = FuriHalSubGhzPresetOok650Async; + subghz_preset_init( + subghz, + "AM650", + subghz_setting_get_default_frequency(subghz->setting), + NULL, + 0); scene_manager_search_and_switch_to_previous_scene( subghz->scene_manager, SubGhzSceneStart); } diff --git a/applications/subghz/scenes/subghz_scene_receiver_config.c b/applications/subghz/scenes/subghz_scene_receiver_config.c index cf31c1e92..bf2f0cdba 100644 --- a/applications/subghz/scenes/subghz_scene_receiver_config.c +++ b/applications/subghz/scenes/subghz_scene_receiver_config.c @@ -7,20 +7,6 @@ enum SubGhzSettingIndex { SubGhzSettingIndexLock, }; -#define PRESET_COUNT 4 -const char* const preset_text[PRESET_COUNT] = { - "AM270", - "AM650", - "FM238", - "FM476", -}; -const uint32_t preset_value[PRESET_COUNT] = { - FuriHalSubGhzPresetOok270Async, /** OOK, bandwidth 270kHz, asynchronous */ - FuriHalSubGhzPresetOok650Async, /** OOK, bandwidth 650kHz, asynchronous */ - FuriHalSubGhzPreset2FSKDev238Async, /** FM, deviation 2.380371 kHz, asynchronous */ - FuriHalSubGhzPreset2FSKDev476Async, /** FM, deviation 4.760742 kHz, asynchronous */ -}; - #define HOPPING_COUNT 2 const char* const hopping_text[HOPPING_COUNT] = { "OFF", @@ -31,32 +17,31 @@ const uint32_t hopping_value[HOPPING_COUNT] = { SubGhzHopperStateRunnig, }; -uint8_t subghz_scene_receiver_config_uint32_value_index( - const uint32_t value, - const uint32_t values[], - uint8_t values_count) { - int64_t last_value = INT64_MIN; +uint8_t subghz_scene_receiver_config_next_frequency(const uint32_t value, void* context) { + furi_assert(context); + SubGhz* subghz = context; uint8_t index = 0; - for(uint8_t i = 0; i < values_count; i++) { - if((value >= last_value) && (value <= values[i])) { + for(uint8_t i = 0; i < subghz_setting_get_frequency_count(subghz->setting); i++) { + if(value == subghz_setting_get_frequency(subghz->setting, i)) { index = i; break; + } else { + index = subghz_setting_get_frequency_default_index(subghz->setting); } - last_value = values[i]; } return index; } -uint8_t subghz_scene_receiver_config_next_frequency(const uint32_t value, void* context) { +uint8_t subghz_scene_receiver_config_next_preset(const char* preset_name, void* context) { furi_assert(context); SubGhz* subghz = context; uint8_t index = 0; - for(uint8_t i = 0; i < subghz_setting_get_frequency_count(subghz->setting); i++) { - if(value == subghz_setting_get_frequency(subghz->setting, i)) { + for(uint8_t i = 0; i < subghz_setting_get_preset_count(subghz->setting); i++) { + if(!strcmp(subghz_setting_get_preset_name(subghz->setting, i), preset_name)) { index = i; break; } else { - index = subghz_setting_get_frequency_default_index(subghz->setting); + // index = subghz_setting_get_frequency_default_index(subghz->setting); } } return index; @@ -88,13 +73,14 @@ static void subghz_scene_receiver_config_set_frequency(VariableItem* item) { if(subghz->txrx->hopper_state == SubGhzHopperStateOFF) { char text_buf[10] = {0}; - sprintf( + snprintf( text_buf, + sizeof(text_buf), "%lu.%02lu", subghz_setting_get_frequency(subghz->setting, index) / 1000000, (subghz_setting_get_frequency(subghz->setting, index) % 1000000) / 10000); variable_item_set_current_value_text(item, text_buf); - subghz->txrx->frequency = subghz_setting_get_frequency(subghz->setting, index); + subghz->txrx->preset->frequency = subghz_setting_get_frequency(subghz->setting, index); } else { variable_item_set_current_value_index( item, subghz_setting_get_frequency_default_index(subghz->setting)); @@ -104,9 +90,14 @@ static void subghz_scene_receiver_config_set_frequency(VariableItem* item) { static void subghz_scene_receiver_config_set_preset(VariableItem* item) { SubGhz* subghz = variable_item_get_context(item); uint8_t index = variable_item_get_current_value_index(item); - - variable_item_set_current_value_text(item, preset_text[index]); - subghz->txrx->preset = preset_value[index]; + variable_item_set_current_value_text( + item, subghz_setting_get_preset_name(subghz->setting, index)); + subghz_preset_init( + subghz, + subghz_setting_get_preset_name(subghz->setting, index), + subghz->txrx->preset->frequency, + subghz_setting_get_preset_data(subghz->setting, index), + subghz_setting_get_preset_data_size(subghz->setting, index)); } static void subghz_scene_receiver_config_set_hopping_runing(VariableItem* item) { @@ -116,8 +107,9 @@ static void subghz_scene_receiver_config_set_hopping_runing(VariableItem* item) variable_item_set_current_value_text(item, hopping_text[index]); if(hopping_value[index] == SubGhzHopperStateOFF) { char text_buf[10] = {0}; - sprintf( + snprintf( text_buf, + sizeof(text_buf), "%lu.%02lu", subghz_setting_get_default_frequency(subghz->setting) / 1000000, (subghz_setting_get_default_frequency(subghz->setting) % 1000000) / 10000); @@ -125,7 +117,7 @@ static void subghz_scene_receiver_config_set_hopping_runing(VariableItem* item) (VariableItem*)scene_manager_get_scene_state( subghz->scene_manager, SubGhzSceneReceiverConfig), text_buf); - subghz->txrx->frequency = subghz_setting_get_default_frequency(subghz->setting); + subghz->txrx->preset->frequency = subghz_setting_get_default_frequency(subghz->setting); variable_item_set_current_value_index( (VariableItem*)scene_manager_get_scene_state( subghz->scene_manager, SubGhzSceneReceiverConfig), @@ -164,13 +156,15 @@ void subghz_scene_receiver_config_on_enter(void* context) { subghz_setting_get_frequency_count(subghz->setting), subghz_scene_receiver_config_set_frequency, subghz); - value_index = subghz_scene_receiver_config_next_frequency(subghz->txrx->frequency, subghz); + value_index = + subghz_scene_receiver_config_next_frequency(subghz->txrx->preset->frequency, subghz); scene_manager_set_scene_state( subghz->scene_manager, SubGhzSceneReceiverConfig, (uint32_t)item); variable_item_set_current_value_index(item, value_index); char text_buf[10] = {0}; - sprintf( + snprintf( text_buf, + sizeof(text_buf), "%lu.%02lu", subghz_setting_get_frequency(subghz->setting, value_index) / 1000000, (subghz_setting_get_frequency(subghz->setting, value_index) % 1000000) / 10000); @@ -193,13 +187,14 @@ void subghz_scene_receiver_config_on_enter(void* context) { item = variable_item_list_add( subghz->variable_item_list, "Modulation:", - PRESET_COUNT, + subghz_setting_get_preset_count(subghz->setting), subghz_scene_receiver_config_set_preset, subghz); - value_index = subghz_scene_receiver_config_uint32_value_index( - subghz->txrx->preset, preset_value, PRESET_COUNT); + value_index = subghz_scene_receiver_config_next_preset( + string_get_cstr(subghz->txrx->preset->name), subghz); variable_item_set_current_value_index(item, value_index); - variable_item_set_current_value_text(item, preset_text[value_index]); + variable_item_set_current_value_text( + item, subghz_setting_get_preset_name(subghz->setting, value_index)); if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneReadRAW) != SubGhzCustomEventManagerSet) { diff --git a/applications/subghz/scenes/subghz_scene_receiver_info.c b/applications/subghz/scenes/subghz_scene_receiver_info.c index 31471184a..cbda1bc7b 100644 --- a/applications/subghz/scenes/subghz_scene_receiver_info.c +++ b/applications/subghz/scenes/subghz_scene_receiver_info.c @@ -27,10 +27,16 @@ static bool subghz_scene_receiver_info_update_parser(void* context) { subghz_protocol_decoder_base_deserialize( subghz->txrx->decoder_result, subghz_history_get_raw_data(subghz->txrx->history, subghz->txrx->idx_menu_chosen)); - subghz->txrx->frequency = - subghz_history_get_frequency(subghz->txrx->history, subghz->txrx->idx_menu_chosen); - subghz->txrx->preset = - subghz_history_get_preset(subghz->txrx->history, subghz->txrx->idx_menu_chosen); + + SubGhzPresetDefinition* preset = + subghz_history_get_preset_def(subghz->txrx->history, subghz->txrx->idx_menu_chosen); + subghz_preset_init( + subghz, + string_get_cstr(preset->name), + preset->frequency, + preset->data, + preset->data_size); + return true; } return false; @@ -137,8 +143,11 @@ bool subghz_scene_receiver_info_on_event(void* context, SceneManagerEvent event) subghz_tx_stop(subghz); } if(subghz->txrx->txrx_state == SubGhzTxRxStateIDLE) { - subghz_begin(subghz, subghz->txrx->preset); - subghz_rx(subghz, subghz->txrx->frequency); + subghz_begin( + subghz, + subghz_setting_get_preset_data_by_name( + subghz->setting, string_get_cstr(subghz->txrx->preset->name))); + subghz_rx(subghz, subghz->txrx->preset->frequency); } if(subghz->txrx->hopper_state == SubGhzHopperStatePause) { subghz->txrx->hopper_state = SubGhzHopperStateRunnig; diff --git a/applications/subghz/scenes/subghz_scene_rpc.c b/applications/subghz/scenes/subghz_scene_rpc.c index c7573fda1..c6f7df268 100644 --- a/applications/subghz/scenes/subghz_scene_rpc.c +++ b/applications/subghz/scenes/subghz_scene_rpc.c @@ -2,24 +2,80 @@ void subghz_scene_rpc_on_enter(void* context) { SubGhz* subghz = context; - Widget* widget = subghz->widget; + Popup* popup = subghz->popup; - widget_add_text_box_element( - widget, 0, 0, 128, 28, AlignCenter, AlignCenter, "RPC mode", false); + popup_set_header(popup, "Sub-GHz", 82, 28, AlignCenter, AlignBottom); + popup_set_text(popup, "RPC mode", 82, 32, AlignCenter, AlignTop); - notification_message(subghz->notifications, &sequence_display_backlight_on); + popup_set_icon(popup, 2, 14, &I_Warning_30x23); // TODO: icon + + view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdPopup); - view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdWidget); + notification_message(subghz->notifications, &sequence_display_backlight_on); } bool subghz_scene_rpc_on_event(void* context, SceneManagerEvent event) { SubGhz* subghz = context; + Popup* popup = subghz->popup; bool consumed = false; if(event.type == SceneManagerEventTypeCustom) { consumed = true; if(event.event == SubGhzCustomEventSceneExit) { + if(subghz->txrx->txrx_state == SubGhzTxRxStateTx) { + subghz_tx_stop(subghz); + subghz_sleep(subghz); + } view_dispatcher_stop(subghz->view_dispatcher); + rpc_system_app_confirm(subghz->rpc_ctx, RpcAppEventAppExit, true); + } else if(event.event == SubGhzCustomEventSceneRpcSessionClose) { + rpc_system_app_set_callback(subghz->rpc_ctx, NULL, NULL); + subghz->rpc_ctx = NULL; + subghz_blink_stop(subghz); + if(subghz->txrx->txrx_state == SubGhzTxRxStateTx) { + subghz_tx_stop(subghz); + subghz_sleep(subghz); + } + view_dispatcher_send_custom_event(subghz->view_dispatcher, SubGhzCustomEventSceneExit); + } else if(event.event == SubGhzCustomEventSceneRpcButtonPress) { + bool result = false; + if(subghz->txrx->txrx_state == SubGhzTxRxStateSleep) { + subghz_blink_start(subghz); + result = subghz_tx_start(subghz, subghz->txrx->fff_data); + result = true; + } + rpc_system_app_confirm(subghz->rpc_ctx, RpcAppEventButtonPress, result); + } else if(event.event == SubGhzCustomEventSceneRpcButtonRelease) { + bool result = false; + if(subghz->txrx->txrx_state == SubGhzTxRxStateTx) { + subghz_blink_stop(subghz); + subghz_tx_stop(subghz); + subghz_sleep(subghz); + result = true; + } + rpc_system_app_confirm(subghz->rpc_ctx, RpcAppEventButtonRelease, result); + } else if(event.event == SubGhzCustomEventSceneRpcLoad) { + bool result = false; + const char* arg = rpc_system_app_get_data(subghz->rpc_ctx); + if(arg) { + if(subghz_key_load(subghz, arg, false)) { + string_set_str(subghz->file_path, arg); + result = true; + string_t file_name; + string_init(file_name); + path_extract_filename(subghz->file_path, file_name, true); + + snprintf( + subghz->file_name_tmp, + SUBGHZ_MAX_LEN_NAME, + "loaded\n%s", + string_get_cstr(file_name)); + popup_set_text(popup, subghz->file_name_tmp, 82, 32, AlignCenter, AlignTop); + + string_clear(file_name); + } + } + rpc_system_app_confirm(subghz->rpc_ctx, RpcAppEventLoadFile, result); } } return consumed; @@ -27,8 +83,9 @@ bool subghz_scene_rpc_on_event(void* context, SceneManagerEvent event) { void subghz_scene_rpc_on_exit(void* context) { SubGhz* subghz = context; + Popup* popup = subghz->popup; - //subghz_rpc_exit_callback(subghz); - - widget_reset(subghz->widget); + popup_set_header(popup, NULL, 0, 0, AlignCenter, AlignBottom); + popup_set_text(popup, NULL, 0, 0, AlignCenter, AlignTop); + popup_set_icon(popup, 0, 0, NULL); } diff --git a/applications/subghz/scenes/subghz_scene_save_name.c b/applications/subghz/scenes/subghz_scene_save_name.c index 272cb6811..12ec98681 100644 --- a/applications/subghz/scenes/subghz_scene_save_name.c +++ b/applications/subghz/scenes/subghz_scene_save_name.c @@ -59,8 +59,8 @@ void subghz_scene_save_name_on_enter(void* context) { MAX_TEXT_INPUT_LEN, // buffer size dev_name_empty); - ValidatorIsFile* validator_is_file = validator_is_file_alloc_init( - string_get_cstr(subghz->file_path), SUBGHZ_APP_EXTENSION, NULL); + ValidatorIsFile* validator_is_file = + validator_is_file_alloc_init(string_get_cstr(subghz->file_path), SUBGHZ_APP_EXTENSION, ""); text_input_set_validator(text_input, validator_is_file_callback, validator_is_file); string_clear(file_name); diff --git a/applications/subghz/scenes/subghz_scene_set_type.c b/applications/subghz/scenes/subghz_scene_set_type.c index a7a4491e1..1a359542e 100644 --- a/applications/subghz/scenes/subghz_scene_set_type.c +++ b/applications/subghz/scenes/subghz_scene_set_type.c @@ -16,17 +16,18 @@ bool subghz_scene_set_type_submenu_gen_data_protocol( uint64_t key, uint32_t bit, uint32_t frequency, - FuriHalSubGhzPreset preset) { + const char* preset_name) { furi_assert(context); SubGhz* subghz = context; bool res = false; + subghz_preset_init(subghz, preset_name, frequency, NULL, 0); subghz->txrx->decoder_result = subghz_receiver_search_decoder_base_by_name(subghz->txrx->receiver, protocol_name); if(subghz->txrx->decoder_result == NULL) { - string_set_str(subghz->error_str, "Protocol not found"); + string_set_str(subghz->error_str, "Protocol not\nfound!"); scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowErrorSub); return false; } @@ -35,7 +36,7 @@ bool subghz_scene_set_type_submenu_gen_data_protocol( Stream* fff_data_stream = flipper_format_get_raw_stream(subghz->txrx->fff_data); stream_clean(fff_data_stream); if(!subghz_protocol_decoder_base_serialize( - subghz->txrx->decoder_result, subghz->txrx->fff_data, frequency, preset)) { + subghz->txrx->decoder_result, subghz->txrx->fff_data, subghz->txrx->preset)) { FURI_LOG_E(TAG, "Unable to serialize"); break; } @@ -177,12 +178,7 @@ bool subghz_scene_set_type_on_event(void* context, SceneManagerEvent event) { case SubmenuIndexPricenton: key = (key & 0x00FFFFF0) | 0x4; //btn 0x1, 0x2, 0x4, 0x8 if(subghz_scene_set_type_submenu_gen_data_protocol( - subghz, - SUBGHZ_PROTOCOL_PRINCETON_NAME, - key, - 24, - 433920000, - FuriHalSubGhzPresetOok650Async)) { + subghz, SUBGHZ_PROTOCOL_PRINCETON_NAME, key, 24, 433920000, "AM650")) { uint32_t te = 400; flipper_format_update_uint32(subghz->txrx->fff_data, "TE", (uint32_t*)&te, 1); generated_protocol = true; @@ -191,60 +187,35 @@ bool subghz_scene_set_type_on_event(void* context, SceneManagerEvent event) { case SubmenuIndexNiceFlo12bit: key = (key & 0x0000FFF0) | 0x1; //btn 0x1, 0x2, 0x4 if(subghz_scene_set_type_submenu_gen_data_protocol( - subghz, - SUBGHZ_PROTOCOL_NICE_FLO_NAME, - key, - 12, - 433920000, - FuriHalSubGhzPresetOok650Async)) { + subghz, SUBGHZ_PROTOCOL_NICE_FLO_NAME, key, 12, 433920000, "AM650")) { generated_protocol = true; } break; case SubmenuIndexNiceFlo24bit: key = (key & 0x00FFFFF0) | 0x4; //btn 0x1, 0x2, 0x4, 0x8 if(subghz_scene_set_type_submenu_gen_data_protocol( - subghz, - SUBGHZ_PROTOCOL_NICE_FLO_NAME, - key, - 24, - 433920000, - FuriHalSubGhzPresetOok650Async)) { + subghz, SUBGHZ_PROTOCOL_NICE_FLO_NAME, key, 24, 433920000, "AM650")) { generated_protocol = true; } break; case SubmenuIndexCAME12bit: key = (key & 0x0000FFF0) | 0x1; //btn 0x1, 0x2, 0x4 if(subghz_scene_set_type_submenu_gen_data_protocol( - subghz, - SUBGHZ_PROTOCOL_CAME_NAME, - key, - 12, - 433920000, - FuriHalSubGhzPresetOok650Async)) { + subghz, SUBGHZ_PROTOCOL_CAME_NAME, key, 12, 433920000, "AM650")) { generated_protocol = true; } break; case SubmenuIndexCAME24bit: key = (key & 0x00FFFFF0) | 0x4; //btn 0x1, 0x2, 0x4, 0x8 if(subghz_scene_set_type_submenu_gen_data_protocol( - subghz, - SUBGHZ_PROTOCOL_CAME_NAME, - key, - 24, - 433920000, - FuriHalSubGhzPresetOok650Async)) { + subghz, SUBGHZ_PROTOCOL_CAME_NAME, key, 24, 433920000, "AM650")) { generated_protocol = true; } break; case SubmenuIndexLinear_300_00: key = (key & 0x3FF); if(subghz_scene_set_type_submenu_gen_data_protocol( - subghz, - SUBGHZ_PROTOCOL_LINEAR_NAME, - key, - 10, - 300000000, - FuriHalSubGhzPresetOok650Async)) { + subghz, SUBGHZ_PROTOCOL_LINEAR_NAME, key, 10, 300000000, "AM650")) { generated_protocol = true; } break; @@ -252,12 +223,7 @@ bool subghz_scene_set_type_on_event(void* context, SceneManagerEvent event) { key = (key & 0x0FFFFFF0); key = 0x003FFF7200000000 | (key ^ 0xE0E0E0EE); if(subghz_scene_set_type_submenu_gen_data_protocol( - subghz, - SUBGHZ_PROTOCOL_CAME_TWEE_NAME, - key, - 54, - 433920000, - FuriHalSubGhzPresetOok650Async)) { + subghz, SUBGHZ_PROTOCOL_CAME_TWEE_NAME, key, 54, 433920000, "AM650")) { generated_protocol = true; } break; @@ -271,18 +237,15 @@ bool subghz_scene_set_type_on_event(void* context, SceneManagerEvent event) { key = (key & 0x00F0FF00) | 0xF << 16 | 0x40; //btn 0xF, 0xC, 0xA, 0x6 (?) uint64_t rev_key = subghz_protocol_blocks_reverse_key(key, 24); if(subghz_scene_set_type_submenu_gen_data_protocol( - subghz, - SUBGHZ_PROTOCOL_GATE_TX_NAME, - rev_key, - 24, - 433920000, - FuriHalSubGhzPresetOok650Async)) { + subghz, SUBGHZ_PROTOCOL_GATE_TX_NAME, rev_key, 24, 433920000, "AM650")) { generated_protocol = true; } break; case SubmenuIndexDoorHan_433_92: subghz->txrx->transmitter = subghz_transmitter_alloc_init( subghz->txrx->environment, SUBGHZ_PROTOCOL_KEELOQ_NAME); + subghz_preset_init( + subghz, "AM650", subghz_setting_get_default_frequency(subghz->setting), NULL, 0); if(subghz->txrx->transmitter) { subghz_protocol_keeloq_create_data( subghz_transmitter_get_protocol_instance(subghz->txrx->transmitter), @@ -291,8 +254,7 @@ bool subghz_scene_set_type_on_event(void* context, SceneManagerEvent event) { 0x2, 0x0003, "DoorHan", - subghz_setting_get_default_frequency(subghz->setting), - FuriHalSubGhzPresetOok650Async); + subghz->txrx->preset); generated_protocol = true; } else { generated_protocol = false; @@ -307,6 +269,7 @@ bool subghz_scene_set_type_on_event(void* context, SceneManagerEvent event) { case SubmenuIndexDoorHan_315_00: subghz->txrx->transmitter = subghz_transmitter_alloc_init( subghz->txrx->environment, SUBGHZ_PROTOCOL_KEELOQ_NAME); + subghz_preset_init(subghz, "AM650", 315000000, NULL, 0); if(subghz->txrx->transmitter) { subghz_protocol_keeloq_create_data( subghz_transmitter_get_protocol_instance(subghz->txrx->transmitter), @@ -315,8 +278,7 @@ bool subghz_scene_set_type_on_event(void* context, SceneManagerEvent event) { 0x2, 0x0003, "DoorHan", - 315000000, - FuriHalSubGhzPresetOok650Async); + subghz->txrx->preset); generated_protocol = true; } else { generated_protocol = false; @@ -338,7 +300,7 @@ bool subghz_scene_set_type_on_event(void* context, SceneManagerEvent event) { (uint64_t)key << 32 | 0xE6000000, 42, 315000000, - FuriHalSubGhzPresetOok650Async)) { + "AM650")) { generated_protocol = true; } break; @@ -352,13 +314,14 @@ bool subghz_scene_set_type_on_event(void* context, SceneManagerEvent event) { (uint64_t)key << 32 | 0xE6000000, 42, 390000000, - FuriHalSubGhzPresetOok650Async)) { + "AM650")) { generated_protocol = true; } break; case SubmenuIndexSecPlus_v2_310_00: subghz->txrx->transmitter = subghz_transmitter_alloc_init( subghz->txrx->environment, SUBGHZ_PROTOCOL_SECPLUS_V2_NAME); + subghz_preset_init(subghz, "AM650", 310000000, NULL, 0); if(subghz->txrx->transmitter) { subghz_protocol_secplus_v2_create_data( subghz_transmitter_get_protocol_instance(subghz->txrx->transmitter), @@ -366,8 +329,7 @@ bool subghz_scene_set_type_on_event(void* context, SceneManagerEvent event) { key, 0x68, 0xE500000, - 310000000, - FuriHalSubGhzPresetOok650Async); + subghz->txrx->preset); generated_protocol = true; } else { generated_protocol = false; @@ -377,6 +339,7 @@ bool subghz_scene_set_type_on_event(void* context, SceneManagerEvent event) { case SubmenuIndexSecPlus_v2_315_00: subghz->txrx->transmitter = subghz_transmitter_alloc_init( subghz->txrx->environment, SUBGHZ_PROTOCOL_SECPLUS_V2_NAME); + subghz_preset_init(subghz, "AM650", 315000000, NULL, 0); if(subghz->txrx->transmitter) { subghz_protocol_secplus_v2_create_data( subghz_transmitter_get_protocol_instance(subghz->txrx->transmitter), @@ -384,8 +347,7 @@ bool subghz_scene_set_type_on_event(void* context, SceneManagerEvent event) { key, 0x68, 0xE500000, - 315000000, - FuriHalSubGhzPresetOok650Async); + subghz->txrx->preset); generated_protocol = true; } else { generated_protocol = false; @@ -395,6 +357,7 @@ bool subghz_scene_set_type_on_event(void* context, SceneManagerEvent event) { case SubmenuIndexSecPlus_v2_390_00: subghz->txrx->transmitter = subghz_transmitter_alloc_init( subghz->txrx->environment, SUBGHZ_PROTOCOL_SECPLUS_V2_NAME); + subghz_preset_init(subghz, "AM650", 390000000, NULL, 0); if(subghz->txrx->transmitter) { subghz_protocol_secplus_v2_create_data( subghz_transmitter_get_protocol_instance(subghz->txrx->transmitter), @@ -402,8 +365,7 @@ bool subghz_scene_set_type_on_event(void* context, SceneManagerEvent event) { key, 0x68, 0xE500000, - 390000000, - FuriHalSubGhzPresetOok650Async); + subghz->txrx->preset); generated_protocol = true; } else { generated_protocol = false; diff --git a/applications/subghz/scenes/subghz_scene_show_error_sub.c b/applications/subghz/scenes/subghz_scene_show_error_sub.c index 64cd5b5a6..697588d0e 100644 --- a/applications/subghz/scenes/subghz_scene_show_error_sub.c +++ b/applications/subghz/scenes/subghz_scene_show_error_sub.c @@ -11,8 +11,8 @@ void subghz_scene_show_error_sub_on_enter(void* context) { // Setup view Popup* popup = subghz->popup; - popup_set_icon(popup, 32, 12, &I_DolphinFirstStart7_61x51); - popup_set_header(popup, string_get_cstr(subghz->error_str), 64, 8, AlignCenter, AlignBottom); + popup_set_icon(popup, 72, 14, &I_DolphinFirstStart8_56x51); + popup_set_header(popup, string_get_cstr(subghz->error_str), 14, 15, AlignLeft, AlignTop); popup_set_timeout(popup, 1500); popup_set_context(popup, subghz); popup_set_callback(popup, subghz_scene_show_error_sub_popup_callback); diff --git a/applications/subghz/scenes/subghz_scene_transmitter.c b/applications/subghz/scenes/subghz_scene_transmitter.c index b8b22749c..b3f8d0799 100644 --- a/applications/subghz/scenes/subghz_scene_transmitter.c +++ b/applications/subghz/scenes/subghz_scene_transmitter.c @@ -94,7 +94,7 @@ bool subghz_scene_transmitter_on_event(void* context, SceneManagerEvent event) { subghz->scene_manager, SubGhzSceneStart); return true; } else if(event.event == SubGhzCustomEventViewTransmitterError) { - string_set_str(subghz->error_str, "Protocol not found"); + string_set_str(subghz->error_str, "Protocol not\nfound!"); scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowErrorSub); } } else if(event.type == SceneManagerEventTypeTick) { diff --git a/applications/subghz/subghz.c b/applications/subghz/subghz.c index 984ce4728..3770d52c1 100644 --- a/applications/subghz/subghz.c +++ b/applications/subghz/subghz.c @@ -23,52 +23,38 @@ void subghz_tick_event_callback(void* context) { scene_manager_handle_tick_event(subghz->scene_manager); } -static bool subghz_rpc_command_callback(RpcAppSystemEvent event, const char* arg, void* context) { +static void subghz_rpc_command_callback(RpcAppSystemEvent event, void* context) { furi_assert(context); SubGhz* subghz = context; - if(!subghz->rpc_ctx) { - return false; - } - - bool result = false; + furi_assert(subghz->rpc_ctx); if(event == RpcAppEventSessionClose) { - rpc_system_app_set_callback(subghz->rpc_ctx, NULL, NULL); - subghz->rpc_ctx = NULL; - view_dispatcher_send_custom_event(subghz->view_dispatcher, SubGhzCustomEventSceneExit); - if(subghz->txrx->txrx_state == SubGhzTxRxStateTx) { - subghz_tx_stop(subghz); - subghz_sleep(subghz); - } - result = true; + view_dispatcher_send_custom_event( + subghz->view_dispatcher, SubGhzCustomEventSceneRpcSessionClose); } else if(event == RpcAppEventAppExit) { view_dispatcher_send_custom_event(subghz->view_dispatcher, SubGhzCustomEventSceneExit); - if(subghz->txrx->txrx_state == SubGhzTxRxStateTx) { - subghz_tx_stop(subghz); - subghz_sleep(subghz); - } - result = true; } else if(event == RpcAppEventLoadFile) { - if(arg) { - if(subghz_key_load(subghz, arg, false)) { - string_set_str(subghz->file_path, arg); - result = true; - } - } + view_dispatcher_send_custom_event(subghz->view_dispatcher, SubGhzCustomEventSceneRpcLoad); } else if(event == RpcAppEventButtonPress) { - if(subghz->txrx->txrx_state == SubGhzTxRxStateSleep) { - result = subghz_tx_start(subghz, subghz->txrx->fff_data); - } + view_dispatcher_send_custom_event( + subghz->view_dispatcher, SubGhzCustomEventSceneRpcButtonPress); } else if(event == RpcAppEventButtonRelease) { - if(subghz->txrx->txrx_state == SubGhzTxRxStateTx) { - subghz_tx_stop(subghz); - subghz_sleep(subghz); - result = true; - } + view_dispatcher_send_custom_event( + subghz->view_dispatcher, SubGhzCustomEventSceneRpcButtonRelease); + } else { + rpc_system_app_confirm(subghz->rpc_ctx, event, false); } +} + +void subghz_blink_start(SubGhz* instance) { + furi_assert(instance); + notification_message(instance->notifications, &sequence_blink_start_magenta); +} - return result; +void subghz_blink_stop(SubGhz* instance) { + furi_assert(instance); + notification_message(instance->notifications, &sequence_blink_stop); } SubGhz* subghz_alloc() { @@ -78,13 +64,11 @@ SubGhz* subghz_alloc() { string_init(subghz->file_path_tmp); // GUI - subghz->gui = furi_record_open("gui"); + subghz->gui = furi_record_open(RECORD_GUI); // View Dispatcher subghz->view_dispatcher = view_dispatcher_alloc(); view_dispatcher_enable_queue(subghz->view_dispatcher); - view_dispatcher_attach_to_gui( - subghz->view_dispatcher, subghz->gui, ViewDispatcherTypeFullscreen); subghz->scene_manager = scene_manager_alloc(&subghz_scene_handlers, subghz); view_dispatcher_set_event_callback_context(subghz->view_dispatcher, subghz); @@ -96,7 +80,7 @@ SubGhz* subghz_alloc() { subghz->view_dispatcher, subghz_tick_event_callback, 100); // Open Notification record - subghz->notifications = furi_record_open("notification"); + subghz->notifications = furi_record_open(RECORD_NOTIFICATION); // SubMenu subghz->submenu = submenu_alloc(); @@ -126,7 +110,7 @@ SubGhz* subghz_alloc() { subghz->view_dispatcher, SubGhzViewIdWidget, widget_get_view(subghz->widget)); //Dialog - subghz->dialogs = furi_record_open("dialogs"); + subghz->dialogs = furi_record_open(RECORD_DIALOGS); // Transmitter subghz->subghz_transmitter = subghz_view_transmitter_alloc(); @@ -179,13 +163,16 @@ SubGhz* subghz_alloc() { //init setting subghz->setting = subghz_setting_alloc(); - subghz_setting_load(subghz->setting, "/ext/subghz/assets/setting_user"); + subghz_setting_load(subghz->setting, EXT_PATH("subghz/assets/setting_user")); //init Worker & Protocol & History & KeyBoard subghz->lock = SubGhzLockOff; subghz->txrx = malloc(sizeof(SubGhzTxRx)); - subghz->txrx->frequency = subghz_setting_get_default_frequency(subghz->setting); - subghz->txrx->preset = FuriHalSubGhzPresetOok650Async; + subghz->txrx->preset = malloc(sizeof(SubGhzPresetDefinition)); + string_init(subghz->txrx->preset->name); + subghz_preset_init( + subghz, "AM650", subghz_setting_get_default_frequency(subghz->setting), NULL, 0); + subghz->txrx->txrx_state = SubGhzTxRxStateSleep; subghz->txrx->hopper_state = SubGhzHopperStateOFF; subghz->txrx->rx_key_state = SubGhzRxKeyStateIDLE; @@ -195,9 +182,9 @@ SubGhz* subghz_alloc() { subghz->txrx->environment = subghz_environment_alloc(); subghz_environment_set_came_atomo_rainbow_table_file_name( - subghz->txrx->environment, "/ext/subghz/assets/came_atomo"); + subghz->txrx->environment, EXT_PATH("subghz/assets/came_atomo")); subghz_environment_set_nice_flor_s_rainbow_table_file_name( - subghz->txrx->environment, "/ext/subghz/assets/nice_flor_s"); + subghz->txrx->environment, EXT_PATH("subghz/assets/nice_flor_s")); subghz->txrx->receiver = subghz_receiver_alloc_init(subghz->txrx->environment); subghz_receiver_set_filter(subghz->txrx->receiver, SubGhzProtocolFlag_Decodable); @@ -218,6 +205,8 @@ void subghz_free(SubGhz* subghz) { if(subghz->rpc_ctx) { rpc_system_app_set_callback(subghz->rpc_ctx, NULL, NULL); + rpc_system_app_send_exited(subghz->rpc_ctx); + subghz_blink_stop(subghz); subghz->rpc_ctx = NULL; } @@ -246,7 +235,7 @@ void subghz_free(SubGhz* subghz) { widget_free(subghz->widget); //Dialog - furi_record_close("dialogs"); + furi_record_close(RECORD_DIALOGS); // Transmitter view_dispatcher_remove_view(subghz->view_dispatcher, SubGhzViewIdTransmitter); @@ -279,7 +268,7 @@ void subghz_free(SubGhz* subghz) { view_dispatcher_free(subghz->view_dispatcher); // GUI - furi_record_close("gui"); + furi_record_close(RECORD_GUI); subghz->gui = NULL; //setting @@ -291,13 +280,15 @@ void subghz_free(SubGhz* subghz) { subghz_worker_free(subghz->txrx->worker); flipper_format_free(subghz->txrx->fff_data); subghz_history_free(subghz->txrx->history); + string_clear(subghz->txrx->preset->name); + free(subghz->txrx->preset); free(subghz->txrx); //Error string string_clear(subghz->error_str); // Notifications - furi_record_close("notification"); + furi_record_close(RECORD_NOTIFICATION); subghz->notifications = NULL; // Path strings @@ -313,33 +304,42 @@ int32_t subghz_app(void* p) { //Load database bool load_database = subghz_environment_load_keystore( - subghz->txrx->environment, "/ext/subghz/assets/keeloq_mfcodes"); + subghz->txrx->environment, EXT_PATH("subghz/assets/keeloq_mfcodes")); subghz_environment_load_keystore( - subghz->txrx->environment, "/ext/subghz/assets/keeloq_mfcodes_user"); + subghz->txrx->environment, EXT_PATH("subghz/assets/keeloq_mfcodes_user")); // Check argument and run corresponding scene - if(p) { + if(p && strlen(p)) { uint32_t rpc_ctx = 0; if(sscanf(p, "RPC %lX", &rpc_ctx) == 1) { subghz->rpc_ctx = (void*)rpc_ctx; rpc_system_app_set_callback(subghz->rpc_ctx, subghz_rpc_command_callback, subghz); + rpc_system_app_send_started(subghz->rpc_ctx); + view_dispatcher_attach_to_gui( + subghz->view_dispatcher, subghz->gui, ViewDispatcherTypeDesktop); scene_manager_next_scene(subghz->scene_manager, SubGhzSceneRpc); - } else if(subghz_key_load(subghz, p, true)) { - string_set_str(subghz->file_path, p); - - if((!strcmp(subghz->txrx->decoder_result->protocol->name, "RAW"))) { - //Load Raw TX - subghz->txrx->rx_key_state = SubGhzRxKeyStateRAWLoad; - scene_manager_next_scene(subghz->scene_manager, SubGhzSceneReadRAW); + } else { + view_dispatcher_attach_to_gui( + subghz->view_dispatcher, subghz->gui, ViewDispatcherTypeFullscreen); + if(subghz_key_load(subghz, p, true)) { + string_set_str(subghz->file_path, p); + + if((!strcmp(subghz->txrx->decoder_result->protocol->name, "RAW"))) { + //Load Raw TX + subghz->txrx->rx_key_state = SubGhzRxKeyStateRAWLoad; + scene_manager_next_scene(subghz->scene_manager, SubGhzSceneReadRAW); + } else { + //Load transmitter TX + scene_manager_next_scene(subghz->scene_manager, SubGhzSceneTransmitter); + } } else { - //Load transmitter TX - scene_manager_next_scene(subghz->scene_manager, SubGhzSceneTransmitter); + //exit app + scene_manager_stop(subghz->scene_manager); + view_dispatcher_stop(subghz->view_dispatcher); } - } else { - //exit app - scene_manager_stop(subghz->scene_manager); - view_dispatcher_stop(subghz->view_dispatcher); } } else { + view_dispatcher_attach_to_gui( + subghz->view_dispatcher, subghz->gui, ViewDispatcherTypeFullscreen); string_set_str(subghz->file_path, SUBGHZ_APP_FOLDER); if(load_database) { scene_manager_next_scene(subghz->scene_manager, SubGhzSceneStart); diff --git a/applications/subghz/subghz_cli.c b/applications/subghz/subghz_cli.c index 3944fc5b5..2b6fdd841 100644 --- a/applications/subghz/subghz_cli.c +++ b/applications/subghz/subghz_cli.c @@ -245,12 +245,12 @@ void subghz_cli_command_rx(Cli* cli, string_t args, void* context) { furi_check(instance->stream); SubGhzEnvironment* environment = subghz_environment_alloc(); - subghz_environment_load_keystore(environment, "/ext/subghz/assets/keeloq_mfcodes"); - subghz_environment_load_keystore(environment, "/ext/subghz/assets/keeloq_mfcodes_user"); + subghz_environment_load_keystore(environment, EXT_PATH("subghz/assets/keeloq_mfcodes")); + subghz_environment_load_keystore(environment, EXT_PATH("subghz/assets/keeloq_mfcodes_user")); subghz_environment_set_came_atomo_rainbow_table_file_name( - environment, "/ext/subghz/assets/came_atomo"); + environment, EXT_PATH("subghz/assets/came_atomo")); subghz_environment_set_nice_flor_s_rainbow_table_file_name( - environment, "/ext/subghz/assets/nice_flor_s"); + environment, EXT_PATH("subghz/assets/nice_flor_s")); SubGhzReceiver* receiver = subghz_receiver_alloc_init(environment); subghz_receiver_set_filter(receiver, SubGhzProtocolFlag_Decodable); @@ -304,9 +304,9 @@ void subghz_cli_command_decode_raw(Cli* cli, string_t args, void* context) { UNUSED(context); string_t file_name; string_init(file_name); - string_set_str(file_name, "/any/subghz/test.sub"); + string_set_str(file_name, ANY_PATH("subghz/test.sub")); - Storage* storage = furi_record_open("storage"); + Storage* storage = furi_record_open(RECORD_STORAGE); FlipperFormat* fff_data_file = flipper_format_file_alloc(storage); string_t temp_str; string_init(temp_str); @@ -346,29 +346,30 @@ void subghz_cli_command_decode_raw(Cli* cli, string_t args, void* context) { string_clear(temp_str); flipper_format_free(fff_data_file); - furi_record_close("storage"); + furi_record_close(RECORD_STORAGE); if(check_file) { // Allocate context SubGhzCliCommandRx* instance = malloc(sizeof(SubGhzCliCommandRx)); SubGhzEnvironment* environment = subghz_environment_alloc(); - if(subghz_environment_load_keystore(environment, "/ext/subghz/assets/keeloq_mfcodes")) { + if(subghz_environment_load_keystore( + environment, EXT_PATH("subghz/assets/keeloq_mfcodes"))) { printf("SubGhz decode_raw: Load_keystore keeloq_mfcodes \033[0;32mOK\033[0m\r\n"); } else { printf("SubGhz decode_raw: Load_keystore keeloq_mfcodes \033[0;31mERROR\033[0m\r\n"); } if(subghz_environment_load_keystore( - environment, "/ext/subghz/assets/keeloq_mfcodes_user")) { + environment, EXT_PATH("subghz/assets/keeloq_mfcodes_user"))) { printf("SubGhz decode_raw: Load_keystore keeloq_mfcodes_user \033[0;32mOK\033[0m\r\n"); } else { printf( "SubGhz decode_raw: Load_keystore keeloq_mfcodes_user \033[0;31mERROR\033[0m\r\n"); } subghz_environment_set_came_atomo_rainbow_table_file_name( - environment, "/ext/subghz/assets/came_atomo"); + environment, EXT_PATH("subghz/assets/came_atomo")); subghz_environment_set_nice_flor_s_rainbow_table_file_name( - environment, "/ext/subghz/assets/nice_flor_s"); + environment, EXT_PATH("subghz/assets/nice_flor_s")); SubGhzReceiver* receiver = subghz_receiver_alloc_init(environment); subghz_receiver_set_filter(receiver, SubGhzProtocolFlag_Decodable); @@ -569,7 +570,7 @@ static void subghz_cli_command_chat(Cli* cli, string_t args) { bool exit = false; SubGhzChatEvent chat_event; - NotificationApp* notification = furi_record_open("notification"); + NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION); string_printf(name, "\033[0;33m%s\033[0m: ", furi_hal_version_get_name_ptr()); string_set(input, name); @@ -676,7 +677,7 @@ static void subghz_cli_command_chat(Cli* cli, string_t args) { break; } } - if(cli_is_connected(cli)) { + if(!cli_is_connected(cli)) { printf("\r\n"); chat_event.event = SubGhzChatEventUserExit; subghz_chat_worker_put_event_chat(subghz_chat, &chat_event); @@ -688,7 +689,7 @@ static void subghz_cli_command_chat(Cli* cli, string_t args) { string_clear(output); string_clear(sysmsg); furi_hal_power_suppress_charge_exit(); - furi_record_close("notification"); + furi_record_close(RECORD_NOTIFICATION); if(subghz_chat_worker_is_running(subghz_chat)) { subghz_chat_worker_stop(subghz_chat); @@ -757,11 +758,11 @@ static void subghz_cli_command(Cli* cli, string_t args, void* context) { void subghz_on_system_start() { #ifdef SRV_CLI - Cli* cli = furi_record_open("cli"); + Cli* cli = furi_record_open(RECORD_CLI); cli_add_command(cli, "subghz", CliCommandFlagDefault, subghz_cli_command, NULL); - furi_record_close("cli"); + furi_record_close(RECORD_CLI); #else UNUSED(subghz_cli_command); #endif diff --git a/applications/subghz/subghz_history.c b/applications/subghz/subghz_history.c index 85578b997..820a13d14 100644 --- a/applications/subghz/subghz_history.c +++ b/applications/subghz/subghz_history.c @@ -12,8 +12,7 @@ typedef struct { string_t item_str; FlipperFormat* flipper_string; uint8_t type; - FuriHalSubGhzPreset preset; - uint32_t frequency; + SubGhzPresetDefinition* preset; } SubGhzHistoryItem; ARRAY_DEF(SubGhzHistoryItemArray, SubGhzHistoryItem, M_POD_OPLIST) @@ -46,6 +45,8 @@ void subghz_history_free(SubGhzHistory* instance) { for M_EACH(item, instance->history->data, SubGhzHistoryItemArray_t) { string_clear(item->item_str); + string_clear(item->preset->name); + free(item->preset); flipper_format_free(item->flipper_string); item->type = 0; } @@ -57,21 +58,29 @@ void subghz_history_free(SubGhzHistory* instance) { uint32_t subghz_history_get_frequency(SubGhzHistory* instance, uint16_t idx) { furi_assert(instance); SubGhzHistoryItem* item = SubGhzHistoryItemArray_get(instance->history->data, idx); - return item->frequency; + return item->preset->frequency; } -FuriHalSubGhzPreset subghz_history_get_preset(SubGhzHistory* instance, uint16_t idx) { +SubGhzPresetDefinition* subghz_history_get_preset_def(SubGhzHistory* instance, uint16_t idx) { furi_assert(instance); SubGhzHistoryItem* item = SubGhzHistoryItemArray_get(instance->history->data, idx); return item->preset; } +const char* subghz_history_get_preset(SubGhzHistory* instance, uint16_t idx) { + furi_assert(instance); + SubGhzHistoryItem* item = SubGhzHistoryItemArray_get(instance->history->data, idx); + return string_get_cstr(item->preset->name); +} + void subghz_history_reset(SubGhzHistory* instance) { furi_assert(instance); string_reset(instance->tmp_string); for M_EACH(item, instance->history->data, SubGhzHistoryItemArray_t) { string_clear(item->item_str); + string_clear(item->preset->name); + free(item->preset); flipper_format_free(item->flipper_string); item->type = 0; } @@ -130,8 +139,7 @@ void subghz_history_get_text_item_menu(SubGhzHistory* instance, string_t output, bool subghz_history_add_to_history( SubGhzHistory* instance, void* context, - uint32_t frequency, - FuriHalSubGhzPreset preset) { + SubGhzPresetDefinition* preset) { furi_assert(instance); furi_assert(context); @@ -151,13 +159,17 @@ bool subghz_history_add_to_history( string_t text; string_init(text); SubGhzHistoryItem* item = SubGhzHistoryItemArray_push_raw(instance->history->data); + item->preset = malloc(sizeof(SubGhzPresetDefinition)); item->type = decoder_base->protocol->type; - item->frequency = frequency; - item->preset = preset; + item->preset->frequency = preset->frequency; + string_init(item->preset->name); + string_set(item->preset->name, preset->name); + item->preset->data = preset->data; + item->preset->data_size = preset->data_size; string_init(item->item_str); item->flipper_string = flipper_format_string_alloc(); - subghz_protocol_decoder_base_serialize(decoder_base, item->flipper_string, frequency, preset); + subghz_protocol_decoder_base_serialize(decoder_base, item->flipper_string, preset); do { if(!flipper_format_rewind(item->flipper_string)) { diff --git a/applications/subghz/subghz_history.h b/applications/subghz/subghz_history.h index f90b01d99..adbcfc18a 100644 --- a/applications/subghz/subghz_history.h +++ b/applications/subghz/subghz_history.h @@ -5,6 +5,7 @@ #include #include #include +#include "helpers/subghz_types.h" typedef struct SubGhzHistory SubGhzHistory; @@ -34,13 +35,15 @@ void subghz_history_reset(SubGhzHistory* instance); */ uint32_t subghz_history_get_frequency(SubGhzHistory* instance, uint16_t idx); +SubGhzPresetDefinition* subghz_history_get_preset_def(SubGhzHistory* instance, uint16_t idx); + /** Get preset to history[idx] * * @param instance - SubGhzHistory instance * @param idx - record index - * @return preset - FuriHalSubGhzPreset preset + * @return preset - preset name */ -FuriHalSubGhzPreset subghz_history_get_preset(SubGhzHistory* instance, uint16_t idx); +const char* subghz_history_get_preset(SubGhzHistory* instance, uint16_t idx); /** Get history index write * @@ -85,15 +88,13 @@ bool subghz_history_get_text_space_left(SubGhzHistory* instance, string_t output * * @param instance - SubGhzHistory instance * @param context - SubGhzProtocolCommon context - * @param frequency - frequency Hz - * @param preset - FuriHalSubGhzPreset preset + * @param preset - SubGhzPresetDefinition preset * @return bool; */ bool subghz_history_add_to_history( SubGhzHistory* instance, void* context, - uint32_t frequency, - FuriHalSubGhzPreset preset); + SubGhzPresetDefinition* preset); /** Get SubGhzProtocolCommonLoad to load into the protocol decoder bin data * diff --git a/applications/subghz/subghz_i.c b/applications/subghz/subghz_i.c index 0593cc6f7..00cc922c7 100644 --- a/applications/subghz/subghz_i.c +++ b/applications/subghz/subghz_i.c @@ -1,7 +1,6 @@ #include "subghz_i.h" #include "assets_icons.h" -#include "m-string.h" #include "subghz/types.h" #include #include @@ -19,15 +18,31 @@ #define TAG "SubGhz" +void subghz_preset_init( + void* context, + const char* preset_name, + uint32_t frequency, + uint8_t* preset_data, + size_t preset_data_size) { + furi_assert(context); + SubGhz* subghz = context; + string_set(subghz->txrx->preset->name, preset_name); + subghz->txrx->preset->frequency = frequency; + subghz->txrx->preset->data = preset_data; + subghz->txrx->preset->data_size = preset_data_size; +} + bool subghz_set_preset(SubGhz* subghz, const char* preset) { if(!strcmp(preset, "FuriHalSubGhzPresetOok270Async")) { - subghz->txrx->preset = FuriHalSubGhzPresetOok270Async; + string_set(subghz->txrx->preset->name, "AM270"); } else if(!strcmp(preset, "FuriHalSubGhzPresetOok650Async")) { - subghz->txrx->preset = FuriHalSubGhzPresetOok650Async; + string_set(subghz->txrx->preset->name, "AM650"); } else if(!strcmp(preset, "FuriHalSubGhzPreset2FSKDev238Async")) { - subghz->txrx->preset = FuriHalSubGhzPreset2FSKDev238Async; + string_set(subghz->txrx->preset->name, "FM238"); } else if(!strcmp(preset, "FuriHalSubGhzPreset2FSKDev476Async")) { - subghz->txrx->preset = FuriHalSubGhzPreset2FSKDev476Async; + string_set(subghz->txrx->preset->name, "FM476"); + } else if(!strcmp(preset, "FuriHalSubGhzPresetCustom")) { + string_set(subghz->txrx->preset->name, "CUSTOM"); } else { FURI_LOG_E(TAG, "Unknown preset"); return false; @@ -41,29 +56,19 @@ void subghz_get_frequency_modulation(SubGhz* subghz, string_t frequency, string_ string_printf( frequency, "%03ld.%02ld", - subghz->txrx->frequency / 1000000 % 1000, - subghz->txrx->frequency / 10000 % 100); + subghz->txrx->preset->frequency / 1000000 % 1000, + subghz->txrx->preset->frequency / 10000 % 100); } - if(modulation != NULL) { - if(subghz->txrx->preset == FuriHalSubGhzPresetOok650Async || - subghz->txrx->preset == FuriHalSubGhzPresetOok270Async) { - string_set_str(modulation, "AM"); - } else if( - subghz->txrx->preset == FuriHalSubGhzPreset2FSKDev238Async || - subghz->txrx->preset == FuriHalSubGhzPreset2FSKDev476Async) { - string_set_str(modulation, "FM"); - } else { - furi_crash("SubGhz: Modulation is incorrect."); - } + string_printf(modulation, "%0.2s", string_get_cstr(subghz->txrx->preset->name)); } } -void subghz_begin(SubGhz* subghz, FuriHalSubGhzPreset preset) { +void subghz_begin(SubGhz* subghz, uint8_t* preset_data) { furi_assert(subghz); furi_hal_subghz_reset(); furi_hal_subghz_idle(); - furi_hal_subghz_load_preset(preset); + furi_hal_subghz_load_custom_preset(preset_data); furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeInput, GpioPullNo, GpioSpeedLow); subghz->txrx->txrx_state = SubGhzTxRxStateIDLE; } @@ -155,13 +160,21 @@ bool subghz_tx_start(SubGhz* subghz, FlipperFormat* flipper_format) { if(subghz->txrx->transmitter) { if(subghz_transmitter_deserialize(subghz->txrx->transmitter, flipper_format)) { - if(subghz->txrx->preset) { - subghz_begin(subghz, subghz->txrx->preset); + if(strcmp(string_get_cstr(subghz->txrx->preset->name), "")) { + subghz_begin( + subghz, + subghz_setting_get_preset_data_by_name( + subghz->setting, string_get_cstr(subghz->txrx->preset->name))); } else { - subghz_begin(subghz, FuriHalSubGhzPresetOok270Async); + FURI_LOG_E( + TAG, + "Unknown name preset \" %s \"", + string_get_cstr(subghz->txrx->preset->name)); + subghz_begin( + subghz, subghz_setting_get_preset_data_by_name(subghz->setting, "AM650")); } - if(subghz->txrx->frequency) { - ret = subghz_tx(subghz, subghz->txrx->frequency); + if(subghz->txrx->preset->frequency) { + ret = subghz_tx(subghz, subghz->txrx->preset->frequency); } else { ret = subghz_tx(subghz, 433920000); } @@ -205,15 +218,19 @@ void subghz_tx_stop(SubGhz* subghz) { void subghz_dialog_message_show_only_rx(SubGhz* subghz) { DialogsApp* dialogs = subghz->dialogs; DialogMessage* message = dialog_message_alloc(); + + dialog_message_set_header(message, "Transmission is blocked", 63, 3, AlignCenter, AlignTop); + dialog_message_set_text( message, - "This frequency can\nonly be used for RX\nin your region", - 38, - 23, - AlignCenter, - AlignCenter); - dialog_message_set_icon(message, &I_DolphinFirstStart7_61x51, 67, 12); - dialog_message_set_buttons(message, "Back", NULL, NULL); + "This frequency\nis restricted to\nreceiving only\nin your region.", + 3, + 17, + AlignLeft, + AlignTop); + + dialog_message_set_icon(message, &I_DolphinFirstStart8_56x51, 72, 14); + dialog_message_show(dialogs, message); dialog_message_free(message); } @@ -222,7 +239,7 @@ bool subghz_key_load(SubGhz* subghz, const char* file_path, bool show_dialog) { furi_assert(subghz); furi_assert(file_path); - Storage* storage = furi_record_open("storage"); + Storage* storage = furi_record_open(RECORD_STORAGE); FlipperFormat* fff_data_file = flipper_format_file_alloc(storage); Stream* fff_data_stream = flipper_format_get_raw_stream(subghz->txrx->fff_data); @@ -266,16 +283,38 @@ bool subghz_key_load(SubGhz* subghz, const char* file_path, bool show_dialog) { load_key_state = SubGhzLoadKeyStateOnlyRx; break; } - subghz->txrx->frequency = temp_data32; + subghz->txrx->preset->frequency = temp_data32; if(!flipper_format_read_string(fff_data_file, "Preset", temp_str)) { FURI_LOG_E(TAG, "Missing Preset"); break; } + if(!subghz_set_preset(subghz, string_get_cstr(temp_str))) { break; } + if(!strcmp(string_get_cstr(temp_str), "FuriHalSubGhzPresetCustom")) { + //Todo add Custom_preset_module + //delete peset if it already exists + subghz_setting_delete_custom_preset( + subghz->setting, string_get_cstr(subghz->txrx->preset->name)); + //load custom preset from file + if(!subghz_setting_load_custom_preset( + subghz->setting, string_get_cstr(subghz->txrx->preset->name), fff_data_file)) { + FURI_LOG_E(TAG, "Missing Custom preset"); + break; + } + } + size_t preset_index = subghz_setting_get_inx_preset_by_name( + subghz->setting, string_get_cstr(subghz->txrx->preset->name)); + subghz_preset_init( + subghz, + string_get_cstr(subghz->txrx->preset->name), + subghz->txrx->preset->frequency, + subghz_setting_get_preset_data(subghz->setting, preset_index), + subghz_setting_get_preset_data_size(subghz->setting, preset_index)); + if(!flipper_format_read_string(fff_data_file, "Protocol", temp_str)) { FURI_LOG_E(TAG, "Missing Protocol"); break; @@ -292,8 +331,10 @@ bool subghz_key_load(SubGhz* subghz, const char* file_path, bool show_dialog) { subghz->txrx->decoder_result = subghz_receiver_search_decoder_base_by_name( subghz->txrx->receiver, string_get_cstr(temp_str)); if(subghz->txrx->decoder_result) { - subghz_protocol_decoder_base_deserialize( - subghz->txrx->decoder_result, subghz->txrx->fff_data); + if(!subghz_protocol_decoder_base_deserialize( + subghz->txrx->decoder_result, subghz->txrx->fff_data)) { + break; + } } else { FURI_LOG_E(TAG, "Protocol not found"); break; @@ -304,7 +345,7 @@ bool subghz_key_load(SubGhz* subghz, const char* file_path, bool show_dialog) { string_clear(temp_str); flipper_format_free(fff_data_file); - furi_record_close("storage"); + furi_record_close(RECORD_STORAGE); switch(load_key_state) { case SubGhzLoadKeyStateParseErr: @@ -331,7 +372,7 @@ bool subghz_key_load(SubGhz* subghz, const char* file_path, bool show_dialog) { bool subghz_get_next_name_file(SubGhz* subghz, uint8_t max_len) { furi_assert(subghz); - Storage* storage = furi_record_open("storage"); + Storage* storage = furi_record_open(RECORD_STORAGE); string_t temp_str; string_t file_name; string_t file_path; @@ -368,7 +409,7 @@ bool subghz_get_next_name_file(SubGhz* subghz, uint8_t max_len) { string_clear(temp_str); string_clear(file_path); string_clear(file_name); - furi_record_close("storage"); + furi_record_close(RECORD_STORAGE); return res; } @@ -381,7 +422,7 @@ bool subghz_save_protocol_to_file( furi_assert(flipper_format); furi_assert(dev_file_name); - Storage* storage = furi_record_open("storage"); + Storage* storage = furi_record_open(RECORD_STORAGE); Stream* flipper_format_stream = flipper_format_get_raw_stream(flipper_format); bool saved = false; @@ -410,7 +451,7 @@ bool subghz_save_protocol_to_file( saved = true; } while(0); string_clear(file_dir); - furi_record_close("storage"); + furi_record_close(RECORD_STORAGE); return saved; } @@ -443,7 +484,7 @@ bool subghz_rename_file(SubGhz* subghz) { furi_assert(subghz); bool ret = true; - Storage* storage = furi_record_open("storage"); + Storage* storage = furi_record_open(RECORD_STORAGE); if(string_cmp(subghz->file_path_tmp, subghz->file_path)) { FS_Error fs_result = storage_common_rename( @@ -454,7 +495,7 @@ bool subghz_rename_file(SubGhz* subghz) { ret = false; } } - furi_record_close("storage"); + furi_record_close(RECORD_STORAGE); return ret; } @@ -462,9 +503,9 @@ bool subghz_rename_file(SubGhz* subghz) { bool subghz_delete_file(SubGhz* subghz) { furi_assert(subghz); - Storage* storage = furi_record_open("storage"); + Storage* storage = furi_record_open(RECORD_STORAGE); bool result = storage_simply_remove(storage, string_get_cstr(subghz->file_path_tmp)); - furi_record_close("storage"); + furi_record_close(RECORD_STORAGE); subghz_file_name_clear(subghz); @@ -537,8 +578,8 @@ void subghz_hopper_update(SubGhz* subghz) { }; if(subghz->txrx->txrx_state == SubGhzTxRxStateIDLE) { subghz_receiver_reset(subghz->txrx->receiver); - subghz->txrx->frequency = subghz_setting_get_hopper_frequency( + subghz->txrx->preset->frequency = subghz_setting_get_hopper_frequency( subghz->setting, subghz->txrx->hopper_idx_frequency); - subghz_rx(subghz, subghz->txrx->frequency); + subghz_rx(subghz, subghz->txrx->preset->frequency); } } diff --git a/applications/subghz/subghz_i.h b/applications/subghz/subghz_i.h index 0a4bcf0b4..99a0f8a28 100644 --- a/applications/subghz/subghz_i.h +++ b/applications/subghz/subghz_i.h @@ -11,8 +11,8 @@ #include "views/subghz_test_carrier.h" #include "views/subghz_test_packet.h" -#include -#include +// #include +// #include #include #include #include @@ -49,8 +49,7 @@ struct SubGhzTxRx { SubGhzProtocolDecoderBase* decoder_result; FlipperFormat* fff_data; - uint32_t frequency; - FuriHalSubGhzPreset preset; + SubGhzPresetDefinition* preset; SubGhzHistory* history; uint16_t idx_menu_chosen; SubGhzTxRxState txrx_state; @@ -97,12 +96,22 @@ struct SubGhz { void* rpc_ctx; }; +void subghz_preset_init( + void* context, + const char* preset_name, + uint32_t frequency, + uint8_t* preset_data, + size_t preset_data_size); bool subghz_set_preset(SubGhz* subghz, const char* preset); void subghz_get_frequency_modulation(SubGhz* subghz, string_t frequency, string_t modulation); -void subghz_begin(SubGhz* subghz, FuriHalSubGhzPreset preset); +void subghz_begin(SubGhz* subghz, uint8_t* preset_data); uint32_t subghz_rx(SubGhz* subghz, uint32_t frequency); void subghz_rx_end(SubGhz* subghz); void subghz_sleep(SubGhz* subghz); + +void subghz_blink_start(SubGhz* instance); +void subghz_blink_stop(SubGhz* instance); + bool subghz_tx_start(SubGhz* subghz, FlipperFormat* flipper_format); void subghz_tx_stop(SubGhz* subghz); void subghz_dialog_message_show_only_rx(SubGhz* subghz); diff --git a/applications/subghz/subghz_setting.c b/applications/subghz/subghz_setting.c index 9dcfb291c..0a662f589 100644 --- a/applications/subghz/subghz_setting.c +++ b/applications/subghz/subghz_setting.c @@ -3,7 +3,7 @@ #include #include -#include +#include "furi_hal_subghz_configs.h" #define TAG "SubGhzSetting" @@ -157,29 +157,89 @@ static const uint32_t subghz_hopper_frequency_list_region_jp[] = { 0, }; +typedef struct { + string_t custom_preset_name; + uint8_t* custom_preset_data; + size_t custom_preset_data_size; +} SubGhzSettingCustomPresetItem; + +ARRAY_DEF(SubGhzSettingCustomPresetItemArray, SubGhzSettingCustomPresetItem, M_POD_OPLIST) + +#define M_OPL_SubGhzSettingCustomPresetItemArray_t() \ + ARRAY_OPLIST(SubGhzSettingCustomPresetItemArray, M_POD_OPLIST) + LIST_DEF(FrequencyList, uint32_t) #define M_OPL_FrequencyList_t() LIST_OPLIST(FrequencyList) +typedef struct { + SubGhzSettingCustomPresetItemArray_t data; +} SubGhzSettingCustomPresetStruct; + struct SubGhzSetting { FrequencyList_t frequencies; FrequencyList_t hopper_frequencies; + SubGhzSettingCustomPresetStruct* preset; }; SubGhzSetting* subghz_setting_alloc(void) { SubGhzSetting* instance = malloc(sizeof(SubGhzSetting)); FrequencyList_init(instance->frequencies); FrequencyList_init(instance->hopper_frequencies); + instance->preset = malloc(sizeof(SubGhzSettingCustomPresetStruct)); + SubGhzSettingCustomPresetItemArray_init(instance->preset->data); return instance; } +static void subghz_setting_preset_reset(SubGhzSetting* instance) { + for + M_EACH(item, instance->preset->data, SubGhzSettingCustomPresetItemArray_t) { + string_clear(item->custom_preset_name); + free(item->custom_preset_data); + } + SubGhzSettingCustomPresetItemArray_reset(instance->preset->data); +} + void subghz_setting_free(SubGhzSetting* instance) { furi_assert(instance); FrequencyList_clear(instance->frequencies); FrequencyList_clear(instance->hopper_frequencies); + for + M_EACH(item, instance->preset->data, SubGhzSettingCustomPresetItemArray_t) { + string_clear(item->custom_preset_name); + free(item->custom_preset_data); + } + SubGhzSettingCustomPresetItemArray_clear(instance->preset->data); + free(instance->preset); free(instance); } +static void subghz_setting_load_default_preset( + SubGhzSetting* instance, + const char* preset_name, + const uint8_t* preset_data, + const uint8_t preset_pa_table[8]) { + furi_assert(instance); + furi_assert(preset_data); + uint32_t preset_data_count = 0; + SubGhzSettingCustomPresetItem* item = + SubGhzSettingCustomPresetItemArray_push_raw(instance->preset->data); + + string_init(item->custom_preset_name); + string_set(item->custom_preset_name, preset_name); + + while(preset_data[preset_data_count]) { + preset_data_count += 2; + } + preset_data_count += 2; + item->custom_preset_data_size = sizeof(uint8_t) * preset_data_count + sizeof(uint8_t) * 8; + item->custom_preset_data = malloc(item->custom_preset_data_size); + //load preset register + memcpy(&item->custom_preset_data[0], &preset_data[0], preset_data_count); + //load pa table + memcpy(&item->custom_preset_data[preset_data_count], &preset_pa_table[0], 8); +} + static void subghz_setting_load_default_region( SubGhzSetting* instance, const uint32_t frequencies[], @@ -188,6 +248,7 @@ static void subghz_setting_load_default_region( FrequencyList_reset(instance->frequencies); FrequencyList_reset(instance->hopper_frequencies); + subghz_setting_preset_reset(instance); while(*frequencies) { FrequencyList_push_back(instance->frequencies, *frequencies); @@ -198,6 +259,27 @@ static void subghz_setting_load_default_region( FrequencyList_push_back(instance->hopper_frequencies, *hopper_frequencies); hopper_frequencies++; } + + subghz_setting_load_default_preset( + instance, + "AM270", + (uint8_t*)furi_hal_subghz_preset_ook_270khz_async_regs, + furi_hal_subghz_preset_ook_async_patable); + subghz_setting_load_default_preset( + instance, + "AM650", + (uint8_t*)furi_hal_subghz_preset_ook_650khz_async_regs, + furi_hal_subghz_preset_ook_async_patable); + subghz_setting_load_default_preset( + instance, + "FM238", + (uint8_t*)furi_hal_subghz_preset_2fsk_dev2_38khz_async_regs, + furi_hal_subghz_preset_2fsk_async_patable); + subghz_setting_load_default_preset( + instance, + "FM476", + (uint8_t*)furi_hal_subghz_preset_2fsk_dev47_6khz_async_regs, + furi_hal_subghz_preset_2fsk_async_patable); } void subghz_setting_load_default(SubGhzSetting* instance) { @@ -229,7 +311,7 @@ void subghz_setting_load_default(SubGhzSetting* instance) { void subghz_setting_load(SubGhzSetting* instance, const char* file_path) { furi_assert(instance); - Storage* storage = furi_record_open("storage"); + Storage* storage = furi_record_open(RECORD_STORAGE); FlipperFormat* fff_data_file = flipper_format_file_alloc(storage); string_t temp_str; @@ -260,7 +342,7 @@ void subghz_setting_load(SubGhzSetting* instance, const char* file_path) { // Standard frequencies (optional) temp_bool = true; - flipper_format_read_bool(fff_data_file, "add_standard_frequencies", &temp_bool, 1); + flipper_format_read_bool(fff_data_file, "Add_standard_frequencies", &temp_bool, 1); if(!temp_bool) { FURI_LOG_I(TAG, "Removing standard frequencies"); FrequencyList_reset(instance->frequencies); @@ -275,7 +357,7 @@ void subghz_setting_load(SubGhzSetting* instance, const char* file_path) { break; } while(flipper_format_read_uint32( - fff_data_file, "frequency", (uint32_t*)&temp_data32, 1)) { + fff_data_file, "Frequency", (uint32_t*)&temp_data32, 1)) { if(furi_hal_subghz_is_frequency_valid(temp_data32)) { FURI_LOG_I(TAG, "Frequency loaded %lu", temp_data32); FrequencyList_push_back(instance->frequencies, temp_data32); @@ -290,7 +372,7 @@ void subghz_setting_load(SubGhzSetting* instance, const char* file_path) { break; } while(flipper_format_read_uint32( - fff_data_file, "hopper_frequency", (uint32_t*)&temp_data32, 1)) { + fff_data_file, "Hopper_frequency", (uint32_t*)&temp_data32, 1)) { if(furi_hal_subghz_is_frequency_valid(temp_data32)) { FURI_LOG_I(TAG, "Hopper frequency loaded %lu", temp_data32); FrequencyList_push_back(instance->hopper_frequencies, temp_data32); @@ -304,7 +386,7 @@ void subghz_setting_load(SubGhzSetting* instance, const char* file_path) { FURI_LOG_E(TAG, "Rewind error"); break; } - if(flipper_format_read_uint32(fff_data_file, "default_frequency", &temp_data32, 1)) { + if(flipper_format_read_uint32(fff_data_file, "Default_frequency", &temp_data32, 1)) { for M_EACH(frequency, instance->frequencies, FrequencyList_t) { *frequency &= FREQUENCY_MASK; @@ -313,12 +395,24 @@ void subghz_setting_load(SubGhzSetting* instance, const char* file_path) { } } } + + // custom preset (optional) + if(!flipper_format_rewind(fff_data_file)) { + FURI_LOG_E(TAG, "Rewind error"); + break; + } + while(flipper_format_read_string(fff_data_file, "Custom_preset_name", temp_str)) { + FURI_LOG_I(TAG, "Custom preset loaded %s", string_get_cstr(temp_str)); + subghz_setting_load_custom_preset( + instance, string_get_cstr(temp_str), fff_data_file); + } + } while(false); } string_clear(temp_str); flipper_format_free(fff_data_file); - furi_record_close("storage"); + furi_record_close(RECORD_STORAGE); if(!FrequencyList_size(instance->frequencies) || !FrequencyList_size(instance->hopper_frequencies)) { @@ -337,6 +431,104 @@ size_t subghz_setting_get_hopper_frequency_count(SubGhzSetting* instance) { return FrequencyList_size(instance->hopper_frequencies); } +size_t subghz_setting_get_preset_count(SubGhzSetting* instance) { + furi_assert(instance); + return SubGhzSettingCustomPresetItemArray_size(instance->preset->data); +} + +const char* subghz_setting_get_preset_name(SubGhzSetting* instance, size_t idx) { + furi_assert(instance); + SubGhzSettingCustomPresetItem* item = + SubGhzSettingCustomPresetItemArray_get(instance->preset->data, idx); + return string_get_cstr(item->custom_preset_name); +} + +int subghz_setting_get_inx_preset_by_name(SubGhzSetting* instance, const char* preset_name) { + furi_assert(instance); + size_t idx = 0; + for + M_EACH(item, instance->preset->data, SubGhzSettingCustomPresetItemArray_t) { + if(strcmp(string_get_cstr(item->custom_preset_name), preset_name) == 0) { + return idx; + } + idx++; + } + furi_crash("SubGhz: No name preset."); + return -1; +} + +bool subghz_setting_load_custom_preset( + SubGhzSetting* instance, + const char* preset_name, + FlipperFormat* fff_data_file) { + furi_assert(instance); + furi_assert(preset_name); + uint32_t temp_data32; + SubGhzSettingCustomPresetItem* item = + SubGhzSettingCustomPresetItemArray_push_raw(instance->preset->data); + string_init(item->custom_preset_name); + string_set(item->custom_preset_name, preset_name); + do { + if(!flipper_format_get_value_count(fff_data_file, "Custom_preset_data", &temp_data32)) + break; + if(!temp_data32 || (temp_data32 % 2)) { + FURI_LOG_E(TAG, "Integrity error Custom_preset_data"); + break; + } + item->custom_preset_data_size = sizeof(uint8_t) * temp_data32; + item->custom_preset_data = malloc(item->custom_preset_data_size); + if(!flipper_format_read_hex( + fff_data_file, + "Custom_preset_data", + item->custom_preset_data, + item->custom_preset_data_size)) { + FURI_LOG_E(TAG, "Missing Custom_preset_data"); + break; + } + return true; + } while(true); + return false; +} + +bool subghz_setting_delete_custom_preset(SubGhzSetting* instance, const char* preset_name) { + furi_assert(instance); + furi_assert(preset_name); + SubGhzSettingCustomPresetItemArray_it_t it; + SubGhzSettingCustomPresetItemArray_it_last(it, instance->preset->data); + while(!SubGhzSettingCustomPresetItemArray_end_p(it)) { + SubGhzSettingCustomPresetItem* item = SubGhzSettingCustomPresetItemArray_ref(it); + if(strcmp(string_get_cstr(item->custom_preset_name), preset_name) == 0) { + string_clear(item->custom_preset_name); + free(item->custom_preset_data); + SubGhzSettingCustomPresetItemArray_remove(instance->preset->data, it); + return true; + } + SubGhzSettingCustomPresetItemArray_previous(it); + } + return false; +} + +uint8_t* subghz_setting_get_preset_data(SubGhzSetting* instance, size_t idx) { + furi_assert(instance); + SubGhzSettingCustomPresetItem* item = + SubGhzSettingCustomPresetItemArray_get(instance->preset->data, idx); + return item->custom_preset_data; +} + +size_t subghz_setting_get_preset_data_size(SubGhzSetting* instance, size_t idx) { + furi_assert(instance); + SubGhzSettingCustomPresetItem* item = + SubGhzSettingCustomPresetItemArray_get(instance->preset->data, idx); + return item->custom_preset_data_size; +} + +uint8_t* subghz_setting_get_preset_data_by_name(SubGhzSetting* instance, const char* preset_name) { + furi_assert(instance); + SubGhzSettingCustomPresetItem* item = SubGhzSettingCustomPresetItemArray_get( + instance->preset->data, subghz_setting_get_inx_preset_by_name(instance, preset_name)); + return item->custom_preset_data; +} + uint32_t subghz_setting_get_frequency(SubGhzSetting* instance, size_t idx) { furi_assert(instance); uint32_t* ret = FrequencyList_get(instance->frequencies, idx); diff --git a/applications/subghz/subghz_setting.h b/applications/subghz/subghz_setting.h index 58a898c56..0590cf499 100644 --- a/applications/subghz/subghz_setting.h +++ b/applications/subghz/subghz_setting.h @@ -4,6 +4,9 @@ #include #include #include +#include + +#define SUBGHZ_SETTING_DEFAULT_PRESET_COUNT 4 typedef struct SubGhzSetting SubGhzSetting; @@ -17,6 +20,25 @@ size_t subghz_setting_get_frequency_count(SubGhzSetting* instance); size_t subghz_setting_get_hopper_frequency_count(SubGhzSetting* instance); +size_t subghz_setting_get_preset_count(SubGhzSetting* instance); + +const char* subghz_setting_get_preset_name(SubGhzSetting* instance, size_t idx); + +int subghz_setting_get_inx_preset_by_name(SubGhzSetting* instance, const char* preset_name); + +uint8_t* subghz_setting_get_preset_data(SubGhzSetting* instance, size_t idx); + +size_t subghz_setting_get_preset_data_size(SubGhzSetting* instance, size_t idx); + +uint8_t* subghz_setting_get_preset_data_by_name(SubGhzSetting* instance, const char* preset_name); + +bool subghz_setting_load_custom_preset( + SubGhzSetting* instance, + const char* preset_name, + FlipperFormat* fff_data_file); + +bool subghz_setting_delete_custom_preset(SubGhzSetting* instance, const char* preset_name); + uint32_t subghz_setting_get_frequency(SubGhzSetting* instance, size_t idx); uint32_t subghz_setting_get_hopper_frequency(SubGhzSetting* instance, size_t idx); diff --git a/applications/subghz/views/subghz_read_raw.c b/applications/subghz/views/subghz_read_raw.c index a4807d775..ccffaf42f 100644 --- a/applications/subghz/views/subghz_read_raw.c +++ b/applications/subghz/views/subghz_read_raw.c @@ -26,7 +26,7 @@ typedef struct { bool rssi_history_end; uint8_t ind_write; uint8_t ind_sin; - SubGhzReadRAWStatus satus; + SubGhzReadRAWStatus status; } SubGhzReadRAWModel; void subghz_read_raw_set_callback( @@ -88,21 +88,21 @@ void subghz_read_raw_stop_send(SubGhzReadRAW* instance) { with_view_model( instance->view, (SubGhzReadRAWModel * model) { - switch(model->satus) { + switch(model->status) { case SubGhzReadRAWStatusTXRepeat: case SubGhzReadRAWStatusLoadKeyTXRepeat: instance->callback(SubGhzCustomEventViewReadRAWSendStart, instance->context); break; case SubGhzReadRAWStatusTX: - model->satus = SubGhzReadRAWStatusIDLE; + model->status = SubGhzReadRAWStatusIDLE; break; case SubGhzReadRAWStatusLoadKeyTX: - model->satus = SubGhzReadRAWStatusLoadKeyIDLE; + model->status = SubGhzReadRAWStatusLoadKeyIDLE; break; default: FURI_LOG_W(TAG, "unknown status"); - model->satus = SubGhzReadRAWStatusIDLE; + model->status = SubGhzReadRAWStatusIDLE; break; } return true; @@ -216,8 +216,8 @@ void subghz_read_raw_draw(Canvas* canvas, SubGhzReadRAWModel* model) { uint8_t graphics_mode = 1; canvas_set_color(canvas, ColorBlack); canvas_set_font(canvas, FontSecondary); - canvas_draw_str(canvas, 5, 8, string_get_cstr(model->frequency_str)); - canvas_draw_str(canvas, 40, 8, string_get_cstr(model->preset_str)); + canvas_draw_str(canvas, 5, 7, string_get_cstr(model->frequency_str)); + canvas_draw_str(canvas, 40, 7, string_get_cstr(model->preset_str)); canvas_draw_str_aligned( canvas, 126, 0, AlignRight, AlignTop, string_get_cstr(model->sample_write)); @@ -225,7 +225,7 @@ void subghz_read_raw_draw(Canvas* canvas, SubGhzReadRAWModel* model) { canvas_draw_line(canvas, 0, 48, 115, 48); canvas_draw_line(canvas, 115, 14, 115, 48); - switch(model->satus) { + switch(model->status) { case SubGhzReadRAWStatusIDLE: elements_button_left(canvas, "Erase"); elements_button_center(canvas, "Send"); @@ -289,26 +289,26 @@ bool subghz_read_raw_input(InputEvent* event, void* context) { with_view_model( instance->view, (SubGhzReadRAWModel * model) { uint8_t ret = false; - switch(model->satus) { + switch(model->status) { case SubGhzReadRAWStatusIDLE: // Start TX instance->callback(SubGhzCustomEventViewReadRAWSendStart, instance->context); - model->satus = SubGhzReadRAWStatusTXRepeat; + model->status = SubGhzReadRAWStatusTXRepeat; ret = true; break; case SubGhzReadRAWStatusTX: // Start TXRepeat - model->satus = SubGhzReadRAWStatusTXRepeat; + model->status = SubGhzReadRAWStatusTXRepeat; break; case SubGhzReadRAWStatusLoadKeyIDLE: // Start Load Key TX instance->callback(SubGhzCustomEventViewReadRAWSendStart, instance->context); - model->satus = SubGhzReadRAWStatusLoadKeyTXRepeat; + model->status = SubGhzReadRAWStatusLoadKeyTXRepeat; ret = true; break; case SubGhzReadRAWStatusLoadKeyTX: // Start Load Key TXRepeat - model->satus = SubGhzReadRAWStatusLoadKeyTXRepeat; + model->status = SubGhzReadRAWStatusLoadKeyTXRepeat; break; default: @@ -319,33 +319,33 @@ bool subghz_read_raw_input(InputEvent* event, void* context) { } else if(event->key == InputKeyOk && event->type == InputTypeRelease) { with_view_model( instance->view, (SubGhzReadRAWModel * model) { - if(model->satus == SubGhzReadRAWStatusTXRepeat) { + if(model->status == SubGhzReadRAWStatusTXRepeat) { // Stop repeat TX - model->satus = SubGhzReadRAWStatusTX; - } else if(model->satus == SubGhzReadRAWStatusLoadKeyTXRepeat) { + model->status = SubGhzReadRAWStatusTX; + } else if(model->status == SubGhzReadRAWStatusLoadKeyTXRepeat) { // Stop repeat TX - model->satus = SubGhzReadRAWStatusLoadKeyTX; + model->status = SubGhzReadRAWStatusLoadKeyTX; } return false; }); } else if(event->key == InputKeyBack && event->type == InputTypeShort) { with_view_model( instance->view, (SubGhzReadRAWModel * model) { - switch(model->satus) { + switch(model->status) { case SubGhzReadRAWStatusREC: //Stop REC instance->callback(SubGhzCustomEventViewReadRAWIDLE, instance->context); - model->satus = SubGhzReadRAWStatusIDLE; + model->status = SubGhzReadRAWStatusIDLE; break; case SubGhzReadRAWStatusLoadKeyTX: //Stop TxRx instance->callback(SubGhzCustomEventViewReadRAWTXRXStop, instance->context); - model->satus = SubGhzReadRAWStatusLoadKeyIDLE; + model->status = SubGhzReadRAWStatusLoadKeyIDLE; break; case SubGhzReadRAWStatusTX: //Stop TxRx instance->callback(SubGhzCustomEventViewReadRAWTXRXStop, instance->context); - model->satus = SubGhzReadRAWStatusIDLE; + model->status = SubGhzReadRAWStatusIDLE; break; case SubGhzReadRAWStatusLoadKeyIDLE: //Exit @@ -362,14 +362,14 @@ bool subghz_read_raw_input(InputEvent* event, void* context) { } else if(event->key == InputKeyLeft && event->type == InputTypeShort) { with_view_model( instance->view, (SubGhzReadRAWModel * model) { - if(model->satus == SubGhzReadRAWStatusStart) { + if(model->status == SubGhzReadRAWStatusStart) { //Config instance->callback(SubGhzCustomEventViewReadRAWConfig, instance->context); } else if( - (model->satus == SubGhzReadRAWStatusIDLE) || - (model->satus == SubGhzReadRAWStatusLoadKeyIDLE)) { + (model->status == SubGhzReadRAWStatusIDLE) || + (model->status == SubGhzReadRAWStatusLoadKeyIDLE)) { //Erase - model->satus = SubGhzReadRAWStatusStart; + model->status = SubGhzReadRAWStatusStart; model->rssi_history_end = false; model->ind_write = 0; string_set_str(model->sample_write, "0 spl."); @@ -381,10 +381,10 @@ bool subghz_read_raw_input(InputEvent* event, void* context) { } else if(event->key == InputKeyRight && event->type == InputTypeShort) { with_view_model( instance->view, (SubGhzReadRAWModel * model) { - if(model->satus == SubGhzReadRAWStatusIDLE) { + if(model->status == SubGhzReadRAWStatusIDLE) { //Save instance->callback(SubGhzCustomEventViewReadRAWSave, instance->context); - } else if(model->satus == SubGhzReadRAWStatusLoadKeyIDLE) { + } else if(model->status == SubGhzReadRAWStatusLoadKeyIDLE) { //More instance->callback(SubGhzCustomEventViewReadRAWMore, instance->context); } @@ -393,16 +393,16 @@ bool subghz_read_raw_input(InputEvent* event, void* context) { } else if(event->key == InputKeyOk && event->type == InputTypeShort) { with_view_model( instance->view, (SubGhzReadRAWModel * model) { - if(model->satus == SubGhzReadRAWStatusStart) { + if(model->status == SubGhzReadRAWStatusStart) { //Record instance->callback(SubGhzCustomEventViewReadRAWREC, instance->context); - model->satus = SubGhzReadRAWStatusREC; + model->status = SubGhzReadRAWStatusREC; model->ind_write = 0; model->rssi_history_end = false; - } else if(model->satus == SubGhzReadRAWStatusREC) { + } else if(model->status == SubGhzReadRAWStatusREC) { //Stop instance->callback(SubGhzCustomEventViewReadRAWIDLE, instance->context); - model->satus = SubGhzReadRAWStatusIDLE; + model->status = SubGhzReadRAWStatusIDLE; } return true; }); @@ -412,15 +412,15 @@ bool subghz_read_raw_input(InputEvent* event, void* context) { void subghz_read_raw_set_status( SubGhzReadRAW* instance, - SubGhzReadRAWStatus satus, + SubGhzReadRAWStatus status, const char* file_name) { furi_assert(instance); - switch(satus) { + switch(status) { case SubGhzReadRAWStatusStart: with_view_model( instance->view, (SubGhzReadRAWModel * model) { - model->satus = SubGhzReadRAWStatusStart; + model->status = SubGhzReadRAWStatusStart; model->rssi_history_end = false; model->ind_write = 0; string_reset(model->file_name); @@ -431,14 +431,14 @@ void subghz_read_raw_set_status( case SubGhzReadRAWStatusIDLE: with_view_model( instance->view, (SubGhzReadRAWModel * model) { - model->satus = SubGhzReadRAWStatusIDLE; + model->status = SubGhzReadRAWStatusIDLE; return true; }); break; case SubGhzReadRAWStatusLoadKeyTX: with_view_model( instance->view, (SubGhzReadRAWModel * model) { - model->satus = SubGhzReadRAWStatusLoadKeyIDLE; + model->status = SubGhzReadRAWStatusLoadKeyIDLE; model->rssi_history_end = false; model->ind_write = 0; string_set_str(model->file_name, file_name); @@ -449,7 +449,7 @@ void subghz_read_raw_set_status( case SubGhzReadRAWStatusSaveKey: with_view_model( instance->view, (SubGhzReadRAWModel * model) { - model->satus = SubGhzReadRAWStatusLoadKeyIDLE; + model->status = SubGhzReadRAWStatusLoadKeyIDLE; if(!model->ind_write) { string_set_str(model->file_name, file_name); string_set_str(model->sample_write, "RAW"); @@ -477,11 +477,11 @@ void subghz_read_raw_exit(void* context) { with_view_model( instance->view, (SubGhzReadRAWModel * model) { - if(model->satus != SubGhzReadRAWStatusIDLE && - model->satus != SubGhzReadRAWStatusStart && - model->satus != SubGhzReadRAWStatusLoadKeyIDLE) { + if(model->status != SubGhzReadRAWStatusIDLE && + model->status != SubGhzReadRAWStatusStart && + model->status != SubGhzReadRAWStatusLoadKeyIDLE) { instance->callback(SubGhzCustomEventViewReadRAWIDLE, instance->context); - model->satus = SubGhzReadRAWStatusStart; + model->status = SubGhzReadRAWStatusStart; } return true; }); diff --git a/applications/subghz/views/subghz_read_raw.h b/applications/subghz/views/subghz_read_raw.h index db75f1b48..1d4bb7dc0 100644 --- a/applications/subghz/views/subghz_read_raw.h +++ b/applications/subghz/views/subghz_read_raw.h @@ -44,7 +44,7 @@ void subghz_read_raw_add_data_rssi(SubGhzReadRAW* instance, float rssi); void subghz_read_raw_set_status( SubGhzReadRAW* instance, - SubGhzReadRAWStatus satus, + SubGhzReadRAWStatus status, const char* file_name); View* subghz_read_raw_get_view(SubGhzReadRAW* subghz_static); diff --git a/applications/subghz/views/subghz_test_static.c b/applications/subghz/views/subghz_test_static.c index 26cf9b484..7af54c3c0 100644 --- a/applications/subghz/views/subghz_test_static.c +++ b/applications/subghz/views/subghz_test_static.c @@ -25,7 +25,7 @@ static const uint32_t subghz_test_static_keys[] = { struct SubGhzTestStatic { View* view; - SubGhzTestStaticStatus satus_tx; + SubGhzTestStaticStatus status_tx; SubGhzEncoderPrinceton* encoder; SubGhzTestStaticCallback callback; void* context; @@ -93,7 +93,7 @@ bool subghz_test_static_input(InputEvent* event, void* context) { model->real_frequency = subghz_frequencies_testing[model->frequency]; if(event->key == InputKeyOk) { - NotificationApp* notification = furi_record_open("notification"); + NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION); if(event->type == InputTypePress) { furi_hal_subghz_idle(); furi_hal_subghz_set_frequency_and_path( @@ -113,10 +113,10 @@ bool subghz_test_static_input(InputEvent* event, void* context) { furi_hal_subghz_start_async_tx( subghz_encoder_princeton_for_testing_yield, instance->encoder); - instance->satus_tx = SubGhzTestStaticStatusTX; + instance->status_tx = SubGhzTestStaticStatusTX; } } else if(event->type == InputTypeRelease) { - if(instance->satus_tx == SubGhzTestStaticStatusTX) { + if(instance->status_tx == SubGhzTestStaticStatusTX) { FURI_LOG_I(TAG, "TX Stop"); subghz_encoder_princeton_for_testing_stop( instance->encoder, furi_get_tick()); @@ -124,9 +124,9 @@ bool subghz_test_static_input(InputEvent* event, void* context) { furi_hal_subghz_stop_async_tx(); notification_message(notification, &sequence_reset_red); } - instance->satus_tx = SubGhzTestStaticStatusIDLE; + instance->status_tx = SubGhzTestStaticStatusIDLE; } - furi_record_close("notification"); + furi_record_close(RECORD_NOTIFICATION); } return true; @@ -144,7 +144,7 @@ void subghz_test_static_enter(void* context) { furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); furi_hal_gpio_write(&gpio_cc1101_g0, false); - instance->satus_tx = SubGhzTestStaticStatusIDLE; + instance->status_tx = SubGhzTestStaticStatusIDLE; with_view_model( instance->view, (SubGhzTestStaticModel * model) { diff --git a/applications/system/system_settings.c b/applications/system/system_settings.c index 7bbcdd7bb..7661413d7 100644 --- a/applications/system/system_settings.c +++ b/applications/system/system_settings.c @@ -54,7 +54,7 @@ SystemSettings* system_settings_alloc() { SystemSettings* app = malloc(sizeof(SystemSettings)); // Load settings - app->gui = furi_record_open("gui"); + app->gui = furi_record_open(RECORD_GUI); app->view_dispatcher = view_dispatcher_alloc(); view_dispatcher_enable_queue(app->view_dispatcher); @@ -99,7 +99,7 @@ void system_settings_free(SystemSettings* app) { // View dispatcher view_dispatcher_free(app->view_dispatcher); // Records - furi_record_close("gui"); + furi_record_close(RECORD_GUI); free(app); } diff --git a/applications/u2f/scenes/u2f_scene_error.c b/applications/u2f/scenes/u2f_scene_error.c index d40183678..e10e9c098 100644 --- a/applications/u2f/scenes/u2f_scene_error.c +++ b/applications/u2f/scenes/u2f_scene_error.c @@ -22,20 +22,22 @@ void u2f_scene_error_on_enter(void* context) { AlignTop, FontSecondary, "No SD card or\napp data found.\nThis app will not\nwork without\nrequired files."); + widget_add_button_element( + app->widget, GuiButtonTypeLeft, "Back", u2f_scene_error_event_callback, app); } else if(app->error == U2fAppErrorCloseRpc) { + widget_add_icon_element(app->widget, 78, 0, &I_ActiveConnection_50x64); + widget_add_string_multiline_element( + app->widget, 3, 2, AlignLeft, AlignTop, FontPrimary, "Connection\nis active!"); widget_add_string_multiline_element( app->widget, - 63, - 10, - AlignCenter, + 3, + 30, + AlignLeft, AlignTop, FontSecondary, - "Disconnect from\ncompanion app\nto use this function"); + "Disconnect from\nPC or phone to\nuse this function."); } - widget_add_button_element( - app->widget, GuiButtonTypeLeft, "Back", u2f_scene_error_event_callback, app); - view_dispatcher_switch_to_view(app->view_dispatcher, U2fAppViewError); } diff --git a/applications/u2f/u2f_app.c b/applications/u2f/u2f_app.c index 2c3ff562a..216481976 100644 --- a/applications/u2f/u2f_app.c +++ b/applications/u2f/u2f_app.c @@ -24,8 +24,8 @@ static void u2f_app_tick_event_callback(void* context) { U2fApp* u2f_app_alloc() { U2fApp* app = malloc(sizeof(U2fApp)); - app->gui = furi_record_open("gui"); - app->notifications = furi_record_open("notification"); + app->gui = furi_record_open(RECORD_GUI); + app->notifications = furi_record_open(RECORD_NOTIFICATION); app->view_dispatcher = view_dispatcher_alloc(); app->scene_manager = scene_manager_alloc(&u2f_scene_handlers, app); @@ -79,8 +79,8 @@ void u2f_app_free(U2fApp* app) { scene_manager_free(app->scene_manager); // Close records - furi_record_close("gui"); - furi_record_close("notification"); + furi_record_close(RECORD_GUI); + furi_record_close(RECORD_NOTIFICATION); free(app); } diff --git a/applications/u2f/u2f_data.c b/applications/u2f/u2f_data.c index 5143b27b9..0419fc7e1 100644 --- a/applications/u2f/u2f_data.c +++ b/applications/u2f/u2f_data.c @@ -7,7 +7,7 @@ #define TAG "U2F" -#define U2F_DATA_FOLDER "/any/u2f/" +#define U2F_DATA_FOLDER ANY_PATH("u2f/") #define U2F_CERT_FILE U2F_DATA_FOLDER "assets/cert.der" #define U2F_CERT_KEY_FILE U2F_DATA_FOLDER "assets/cert_key.u2f" #define U2F_KEY_FILE U2F_DATA_FOLDER "key.u2f" @@ -40,7 +40,7 @@ typedef struct { bool u2f_data_check(bool cert_only) { bool state = false; - Storage* fs_api = furi_record_open("storage"); + Storage* fs_api = furi_record_open(RECORD_STORAGE); File* file = storage_file_alloc(fs_api); do { @@ -61,14 +61,14 @@ bool u2f_data_check(bool cert_only) { storage_file_close(file); storage_file_free(file); - furi_record_close("storage"); + furi_record_close(RECORD_STORAGE); return state; } bool u2f_data_cert_check() { bool state = false; - Storage* fs_api = furi_record_open("storage"); + Storage* fs_api = furi_record_open(RECORD_STORAGE); File* file = storage_file_alloc(fs_api); uint8_t file_buf[8]; @@ -96,7 +96,7 @@ bool u2f_data_cert_check() { storage_file_close(file); storage_file_free(file); - furi_record_close("storage"); + furi_record_close(RECORD_STORAGE); return state; } @@ -104,7 +104,7 @@ bool u2f_data_cert_check() { uint32_t u2f_data_cert_load(uint8_t* cert) { furi_assert(cert); - Storage* fs_api = furi_record_open("storage"); + Storage* fs_api = furi_record_open(RECORD_STORAGE); File* file = storage_file_alloc(fs_api); uint32_t file_size = 0; uint32_t len_cur = 0; @@ -117,7 +117,7 @@ uint32_t u2f_data_cert_load(uint8_t* cert) { storage_file_close(file); storage_file_free(file); - furi_record_close("storage"); + furi_record_close(RECORD_STORAGE); return len_cur; } @@ -146,7 +146,7 @@ static bool u2f_data_cert_key_encrypt(uint8_t* cert_key) { } furi_hal_crypto_store_unload_key(U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_UNIQUE); - Storage* storage = furi_record_open("storage"); + Storage* storage = furi_record_open(RECORD_STORAGE); FlipperFormat* flipper_format = flipper_format_file_alloc(storage); if(flipper_format_file_open_always(flipper_format, U2F_CERT_KEY_FILE)) { @@ -162,7 +162,7 @@ static bool u2f_data_cert_key_encrypt(uint8_t* cert_key) { } flipper_format_free(flipper_format); - furi_record_close("storage"); + furi_record_close(RECORD_STORAGE); return state; } @@ -183,7 +183,7 @@ bool u2f_data_cert_key_load(uint8_t* cert_key) { string_t filetype; string_init(filetype); - Storage* storage = furi_record_open("storage"); + Storage* storage = furi_record_open(RECORD_STORAGE); FlipperFormat* flipper_format = flipper_format_file_alloc(storage); if(flipper_format_file_open_existing(flipper_format, U2F_CERT_KEY_FILE)) { @@ -248,7 +248,7 @@ bool u2f_data_cert_key_load(uint8_t* cert_key) { } flipper_format_free(flipper_format); - furi_record_close("storage"); + furi_record_close(RECORD_STORAGE); string_clear(filetype); if(cert_type == U2F_CERT_USER_UNENCRYPTED) { @@ -269,7 +269,7 @@ bool u2f_data_key_load(uint8_t* device_key) { string_t filetype; string_init(filetype); - Storage* storage = furi_record_open("storage"); + Storage* storage = furi_record_open(RECORD_STORAGE); FlipperFormat* flipper_format = flipper_format_file_alloc(storage); if(flipper_format_file_open_existing(flipper_format, U2F_KEY_FILE)) { @@ -306,7 +306,7 @@ bool u2f_data_key_load(uint8_t* device_key) { } while(0); } flipper_format_free(flipper_format); - furi_record_close("storage"); + furi_record_close(RECORD_STORAGE); string_clear(filetype); return state; } @@ -334,7 +334,7 @@ bool u2f_data_key_generate(uint8_t* device_key) { } furi_hal_crypto_store_unload_key(U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_UNIQUE); - Storage* storage = furi_record_open("storage"); + Storage* storage = furi_record_open(RECORD_STORAGE); FlipperFormat* flipper_format = flipper_format_file_alloc(storage); if(flipper_format_file_open_always(flipper_format, U2F_KEY_FILE)) { @@ -350,7 +350,7 @@ bool u2f_data_key_generate(uint8_t* device_key) { } flipper_format_free(flipper_format); - furi_record_close("storage"); + furi_record_close(RECORD_STORAGE); return state; } @@ -367,7 +367,7 @@ bool u2f_data_cnt_read(uint32_t* cnt_val) { string_t filetype; string_init(filetype); - Storage* storage = furi_record_open("storage"); + Storage* storage = furi_record_open(RECORD_STORAGE); FlipperFormat* flipper_format = flipper_format_file_alloc(storage); if(flipper_format_file_open_existing(flipper_format, U2F_CNT_FILE)) { @@ -407,7 +407,7 @@ bool u2f_data_cnt_read(uint32_t* cnt_val) { } while(0); } flipper_format_free(flipper_format); - furi_record_close("storage"); + furi_record_close(RECORD_STORAGE); string_clear(filetype); return state; } @@ -435,7 +435,7 @@ bool u2f_data_cnt_write(uint32_t cnt_val) { } furi_hal_crypto_store_unload_key(U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_UNIQUE); - Storage* storage = furi_record_open("storage"); + Storage* storage = furi_record_open(RECORD_STORAGE); FlipperFormat* flipper_format = flipper_format_file_alloc(storage); if(flipper_format_file_open_always(flipper_format, U2F_CNT_FILE)) { @@ -450,7 +450,7 @@ bool u2f_data_cnt_write(uint32_t cnt_val) { } flipper_format_free(flipper_format); - furi_record_close("storage"); + furi_record_close(RECORD_STORAGE); return state; } diff --git a/applications/unit_tests/flipper_format/flipper_format_string_test.c b/applications/unit_tests/flipper_format/flipper_format_string_test.c index 7158ffd82..b22b333a3 100644 --- a/applications/unit_tests/flipper_format/flipper_format_string_test.c +++ b/applications/unit_tests/flipper_format/flipper_format_string_test.c @@ -296,9 +296,9 @@ MU_TEST(flipper_format_string_test) { } MU_TEST(flipper_format_file_test) { - Storage* storage = furi_record_open("storage"); + Storage* storage = furi_record_open(RECORD_STORAGE); FlipperFormat* flipper_format = flipper_format_file_alloc(storage); - mu_check(flipper_format_file_open_always(flipper_format, "/ext/flipper.fff")); + mu_check(flipper_format_file_open_always(flipper_format, EXT_PATH("flipper.fff"))); Stream* stream = flipper_format_get_raw_stream(flipper_format); mu_check(flipper_format_write_header_cstr(flipper_format, test_filetype, test_version)); @@ -323,7 +323,7 @@ MU_TEST(flipper_format_file_test) { MU_RUN_TEST_1(flipper_format_read_and_update_test, flipper_format); flipper_format_free(flipper_format); - furi_record_close("storage"); + furi_record_close(RECORD_STORAGE); } MU_TEST_SUITE(flipper_format_string_suite) { diff --git a/applications/unit_tests/flipper_format/flipper_format_test.c b/applications/unit_tests/flipper_format/flipper_format_test.c index f83360b43..86e2df21e 100644 --- a/applications/unit_tests/flipper_format/flipper_format_test.c +++ b/applications/unit_tests/flipper_format/flipper_format_test.c @@ -5,7 +5,7 @@ #include "../minunit.h" #define TEST_DIR TEST_DIR_NAME "/" -#define TEST_DIR_NAME "/ext/unit_tests_tmp" +#define TEST_DIR_NAME EXT_PATH("unit_tests_tmp") static const char* test_filetype = "Flipper File test"; static const uint32_t test_version = 666; @@ -66,7 +66,7 @@ static const char* test_file_windows = TEST_DIR READ_TEST_WIN; static const char* test_file_flipper = TEST_DIR READ_TEST_FLP; static bool storage_write_string(const char* path, const char* data) { - Storage* storage = furi_record_open("storage"); + Storage* storage = furi_record_open(RECORD_STORAGE); File* file = storage_file_alloc(storage); bool result = false; @@ -79,26 +79,26 @@ static bool storage_write_string(const char* path, const char* data) { storage_file_close(file); storage_file_free(file); - furi_record_close("storage"); + furi_record_close(RECORD_STORAGE); return result; } static void tests_setup() { - Storage* storage = furi_record_open("storage"); + Storage* storage = furi_record_open(RECORD_STORAGE); mu_assert(storage_simply_remove_recursive(storage, TEST_DIR_NAME), "Cannot clean data"); mu_assert(storage_simply_mkdir(storage, TEST_DIR_NAME), "Cannot create dir"); - furi_record_close("storage"); + furi_record_close(RECORD_STORAGE); } static void tests_teardown() { - Storage* storage = furi_record_open("storage"); + Storage* storage = furi_record_open(RECORD_STORAGE); mu_assert(storage_simply_remove_recursive(storage, TEST_DIR_NAME), "Cannot clean data"); - furi_record_close("storage"); + furi_record_close(RECORD_STORAGE); } static bool test_read(const char* file_name) { - Storage* storage = furi_record_open("storage"); + Storage* storage = furi_record_open(RECORD_STORAGE); bool result = false; FlipperFormat* file = flipper_format_file_alloc(storage); @@ -154,13 +154,13 @@ static bool test_read(const char* file_name) { flipper_format_free(file); - furi_record_close("storage"); + furi_record_close(RECORD_STORAGE); return result; } static bool test_read_updated(const char* file_name) { - Storage* storage = furi_record_open("storage"); + Storage* storage = furi_record_open(RECORD_STORAGE); bool result = false; FlipperFormat* file = flipper_format_file_alloc(storage); @@ -232,13 +232,13 @@ static bool test_read_updated(const char* file_name) { flipper_format_free(file); - furi_record_close("storage"); + furi_record_close(RECORD_STORAGE); return result; } static bool test_write(const char* file_name) { - Storage* storage = furi_record_open("storage"); + Storage* storage = furi_record_open(RECORD_STORAGE); bool result = false; FlipperFormat* file = flipper_format_file_alloc(storage); @@ -264,13 +264,13 @@ static bool test_write(const char* file_name) { } while(false); flipper_format_free(file); - furi_record_close("storage"); + furi_record_close(RECORD_STORAGE); return result; } static bool test_delete_last_key(const char* file_name) { - Storage* storage = furi_record_open("storage"); + Storage* storage = furi_record_open(RECORD_STORAGE); bool result = false; FlipperFormat* file = flipper_format_file_alloc(storage); @@ -281,13 +281,13 @@ static bool test_delete_last_key(const char* file_name) { } while(false); flipper_format_free(file); - furi_record_close("storage"); + furi_record_close(RECORD_STORAGE); return result; } static bool test_append_key(const char* file_name) { - Storage* storage = furi_record_open("storage"); + Storage* storage = furi_record_open(RECORD_STORAGE); bool result = false; FlipperFormat* file = flipper_format_file_alloc(storage); @@ -299,13 +299,13 @@ static bool test_append_key(const char* file_name) { } while(false); flipper_format_free(file); - furi_record_close("storage"); + furi_record_close(RECORD_STORAGE); return result; } static bool test_update(const char* file_name) { - Storage* storage = furi_record_open("storage"); + Storage* storage = furi_record_open(RECORD_STORAGE); bool result = false; FlipperFormat* file = flipper_format_file_alloc(storage); @@ -333,13 +333,13 @@ static bool test_update(const char* file_name) { } while(false); flipper_format_free(file); - furi_record_close("storage"); + furi_record_close(RECORD_STORAGE); return result; } static bool test_update_backward(const char* file_name) { - Storage* storage = furi_record_open("storage"); + Storage* storage = furi_record_open(RECORD_STORAGE); bool result = false; FlipperFormat* file = flipper_format_file_alloc(storage); @@ -364,13 +364,13 @@ static bool test_update_backward(const char* file_name) { } while(false); flipper_format_free(file); - furi_record_close("storage"); + furi_record_close(RECORD_STORAGE); return result; } static bool test_write_multikey(const char* file_name) { - Storage* storage = furi_record_open("storage"); + Storage* storage = furi_record_open(RECORD_STORAGE); bool result = false; FlipperFormat* file = flipper_format_file_alloc(storage); @@ -391,13 +391,13 @@ static bool test_write_multikey(const char* file_name) { } while(false); flipper_format_free(file); - furi_record_close("storage"); + furi_record_close(RECORD_STORAGE); return result; } static bool test_read_multikey(const char* file_name) { - Storage* storage = furi_record_open("storage"); + Storage* storage = furi_record_open(RECORD_STORAGE); bool result = false; FlipperFormat* file = flipper_format_file_alloc(storage); @@ -432,7 +432,7 @@ static bool test_read_multikey(const char* file_name) { string_clear(string_value); flipper_format_free(file); - furi_record_close("storage"); + furi_record_close(RECORD_STORAGE); return result; } diff --git a/applications/unit_tests/infrared/infrared_test.c b/applications/unit_tests/infrared/infrared_test.c index cdae742fc..32266c48e 100644 --- a/applications/unit_tests/infrared/infrared_test.c +++ b/applications/unit_tests/infrared/infrared_test.c @@ -4,7 +4,7 @@ #include #include "../minunit.h" -#define IR_TEST_FILES_DIR "/ext/unit_tests/infrared/" +#define IR_TEST_FILES_DIR EXT_PATH("unit_tests/infrared/") #define IR_TEST_FILE_PREFIX "test_" #define IR_TEST_FILE_SUFFIX ".irtest" @@ -18,7 +18,7 @@ typedef struct { static InfraredTest* test; static void infrared_test_alloc() { - Storage* storage = furi_record_open("storage"); + Storage* storage = furi_record_open(RECORD_STORAGE); test = malloc(sizeof(InfraredTest)); test->decoder_handler = infrared_alloc_decoder(); test->encoder_handler = infrared_alloc_encoder(); @@ -32,7 +32,7 @@ static void infrared_test_free() { infrared_free_encoder(test->encoder_handler); flipper_format_free(test->ff); string_clear(test->file_path); - furi_record_close("storage"); + furi_record_close(RECORD_STORAGE); free(test); test = NULL; } diff --git a/applications/unit_tests/nfc/nfc_test.c b/applications/unit_tests/nfc/nfc_test.c index 4e1b9a646..dcd162d13 100644 --- a/applications/unit_tests/nfc/nfc_test.c +++ b/applications/unit_tests/nfc/nfc_test.c @@ -2,7 +2,7 @@ #include #include #include -#include +#include #include #include @@ -12,7 +12,7 @@ #define TAG "NfcTest" -#define NFC_TEST_RESOURCES_DIR "/ext/unit_tests/nfc/" +#define NFC_TEST_RESOURCES_DIR EXT_PATH("unit_tests/nfc/") #define NFC_TEST_SIGNAL_SHORT_FILE "nfc_nfca_signal_short.nfc" #define NFC_TEST_SIGNAL_LONG_FILE "nfc_nfca_signal_long.nfc" @@ -36,13 +36,13 @@ static NfcTest* nfc_test = NULL; static void nfc_test_alloc() { nfc_test = malloc(sizeof(NfcTest)); nfc_test->signal = nfca_signal_alloc(); - nfc_test->storage = furi_record_open("storage"); + nfc_test->storage = furi_record_open(RECORD_STORAGE); } static void nfc_test_free() { furi_assert(nfc_test); - furi_record_close("storage"); + furi_record_close(RECORD_STORAGE); nfca_signal_free(nfc_test->signal); free(nfc_test); nfc_test = NULL; diff --git a/applications/unit_tests/rpc/rpc_test.c b/applications/unit_tests/rpc/rpc_test.c index c5336385b..d31311af6 100644 --- a/applications/unit_tests/rpc/rpc_test.c +++ b/applications/unit_tests/rpc/rpc_test.c @@ -47,7 +47,7 @@ static RpcSessionContext rpc_session[TEST_RPC_SESSIONS]; #define MAX_NAME_LENGTH 255 #define MAX_DATA_SIZE 512u // have to be exact as in rpc_storage.c #define TEST_DIR TEST_DIR_NAME "/" -#define TEST_DIR_NAME "/ext/unit_tests_tmp" +#define TEST_DIR_NAME EXT_PATH("unit_tests_tmp") #define MD5SUM_SIZE 16 #define PING_REQUEST 0 @@ -83,7 +83,7 @@ static void test_rpc_setup(void) { furi_check(!rpc); furi_check(!(rpc_session[0].session)); - rpc = furi_record_open("rpc"); + rpc = furi_record_open(RECORD_RPC); for(int i = 0; !(rpc_session[0].session) && (i < 10000); ++i) { rpc_session[0].session = rpc_session_open(rpc); furi_delay_tick(1); @@ -125,7 +125,7 @@ static void test_rpc_teardown(void) { xSemaphoreTake(rpc_session[0].terminate_semaphore, 0); rpc_session_close(rpc_session[0].session); furi_check(xSemaphoreTake(rpc_session[0].terminate_semaphore, portMAX_DELAY)); - furi_record_close("rpc"); + furi_record_close(RECORD_RPC); vStreamBufferDelete(rpc_session[0].output_stream); vSemaphoreDelete(rpc_session[0].close_session_semaphore); vSemaphoreDelete(rpc_session[0].terminate_semaphore); @@ -153,17 +153,17 @@ static void test_rpc_teardown_second_session(void) { static void test_rpc_storage_setup(void) { test_rpc_setup(); - Storage* fs_api = furi_record_open("storage"); + Storage* fs_api = furi_record_open(RECORD_STORAGE); clean_directory(fs_api, TEST_DIR_NAME); - furi_record_close("storage"); + furi_record_close(RECORD_STORAGE); } static void test_rpc_storage_teardown(void) { test_rpc_teardown(); - Storage* fs_api = furi_record_open("storage"); + Storage* fs_api = furi_record_open(RECORD_STORAGE); clean_directory(fs_api, TEST_DIR_NAME); - furi_record_close("storage"); + furi_record_close(RECORD_STORAGE); } static void test_rpc_session_close_callback(void* context) { @@ -189,8 +189,9 @@ static void clean_directory(Storage* fs_api, const char* clean_dir) { FileInfo fileinfo; char* name = malloc(MAX_NAME_LENGTH + 1); while(storage_dir_read(dir, &fileinfo, name, MAX_NAME_LENGTH)) { - char* fullname = malloc(strlen(clean_dir) + strlen(name) + 1 + 1); - sprintf(fullname, "%s/%s", clean_dir, name); + size_t size = strlen(clean_dir) + strlen(name) + 1 + 1; + char* fullname = malloc(size); + snprintf(fullname, size, "%s/%s", clean_dir, name); if(fileinfo.flags & FSF_DIRECTORY) { clean_directory(fs_api, fullname); } @@ -420,10 +421,12 @@ static void mu_check(result_msg_file->size == expected_msg_file->size); mu_check(result_msg_file->type == expected_msg_file->type); - mu_check(!result_msg_file->data == !expected_msg_file->data); - mu_check(result_msg_file->data->size == expected_msg_file->data->size); - for(int i = 0; i < result_msg_file->data->size; ++i) { - mu_check(result_msg_file->data->bytes[i] == expected_msg_file->data->bytes[i]); + if(result_msg_file->data && result_msg_file->type != PB_Storage_File_FileType_DIR) { + mu_check(!result_msg_file->data == !expected_msg_file->data); // Zlo: WTF??? + mu_check(result_msg_file->data->size == expected_msg_file->data->size); + for(int i = 0; i < result_msg_file->data->size; ++i) { + mu_check(result_msg_file->data->bytes[i] == expected_msg_file->data->bytes[i]); + } } } @@ -571,7 +574,7 @@ static void test_rpc_storage_list_create_expected_list( MsgList_t msg_list, const char* path, uint32_t command_id) { - Storage* fs_api = furi_record_open("storage"); + Storage* fs_api = furi_record_open(RECORD_STORAGE); File* dir = storage_file_alloc(fs_api); PB_Main response = { @@ -627,7 +630,7 @@ static void test_rpc_storage_list_create_expected_list( storage_dir_close(dir); storage_file_free(dir); - furi_record_close("storage"); + furi_record_close(RECORD_STORAGE); } static void test_rpc_decode_and_compare(MsgList_t expected_msg_list, uint8_t session) { @@ -693,13 +696,13 @@ static void test_rpc_storage_list_run(const char* path, uint32_t command_id) { MU_TEST(test_storage_list) { test_rpc_storage_list_run("/", ++command_id); - test_rpc_storage_list_run("/ext/nfc", ++command_id); + test_rpc_storage_list_run(EXT_PATH("nfc"), ++command_id); - test_rpc_storage_list_run("/int", ++command_id); - test_rpc_storage_list_run("/ext", ++command_id); - test_rpc_storage_list_run("/ext/infrared", ++command_id); - test_rpc_storage_list_run("/ext/ibutton", ++command_id); - test_rpc_storage_list_run("/ext/lfrfid", ++command_id); + test_rpc_storage_list_run(STORAGE_INT_PATH_PREFIX, ++command_id); + test_rpc_storage_list_run(STORAGE_EXT_PATH_PREFIX, ++command_id); + test_rpc_storage_list_run(EXT_PATH("infrared"), ++command_id); + test_rpc_storage_list_run(EXT_PATH("ibutton"), ++command_id); + test_rpc_storage_list_run(EXT_PATH("lfrfid"), ++command_id); test_rpc_storage_list_run("error_path", ++command_id); } @@ -718,7 +721,7 @@ static void test_rpc_add_read_to_list_by_reading_real_file( const char* path, uint32_t command_id) { furi_check(MsgList_empty_p(msg_list)); - Storage* fs_api = furi_record_open("storage"); + Storage* fs_api = furi_record_open(RECORD_STORAGE); File* file = storage_file_alloc(fs_api); bool result = false; @@ -759,7 +762,7 @@ static void test_rpc_add_read_to_list_by_reading_real_file( storage_file_close(file); storage_file_free(file); - furi_record_close("storage"); + furi_record_close(RECORD_STORAGE); } static void test_storage_read_run(const char* path, uint32_t command_id) { @@ -777,25 +780,25 @@ static void test_storage_read_run(const char* path, uint32_t command_id) { } static bool test_is_exists(const char* path) { - Storage* fs_api = furi_record_open("storage"); + Storage* fs_api = furi_record_open(RECORD_STORAGE); FileInfo fileinfo; FS_Error result = storage_common_stat(fs_api, path, &fileinfo); furi_check((result == FSE_OK) || (result == FSE_NOT_EXIST)); - furi_record_close("storage"); + furi_record_close(RECORD_STORAGE); return result == FSE_OK; } static void test_create_dir(const char* path) { - Storage* fs_api = furi_record_open("storage"); + Storage* fs_api = furi_record_open(RECORD_STORAGE); FS_Error error = storage_common_mkdir(fs_api, path); (void)error; furi_check((error == FSE_OK) || (error == FSE_EXIST)); - furi_record_close("storage"); + furi_record_close(RECORD_STORAGE); furi_check(test_is_exists(path)); } static void test_create_file(const char* path, size_t size) { - Storage* fs_api = furi_record_open("storage"); + Storage* fs_api = furi_record_open(RECORD_STORAGE); File* file = storage_file_alloc(fs_api); if(storage_file_open(file, path, FSAM_WRITE, FSOM_CREATE_ALWAYS)) { @@ -813,7 +816,7 @@ static void test_create_file(const char* path, size_t size) { storage_file_close(file); storage_file_free(file); - furi_record_close("storage"); + furi_record_close(RECORD_STORAGE); furi_check(test_is_exists(path)); } @@ -827,7 +830,7 @@ static void test_rpc_storage_info_run(const char* path, uint32_t command_id) { PB_Main* response = MsgList_push_new(expected_msg_list); response->command_id = command_id; - Storage* fs_api = furi_record_open("storage"); + Storage* fs_api = furi_record_open(RECORD_STORAGE); FS_Error error = storage_common_fs_info( fs_api, @@ -856,10 +859,10 @@ static void test_rpc_storage_stat_run(const char* path, uint32_t command_id) { test_rpc_create_simple_message(&request, PB_Main_storage_stat_request_tag, path, command_id); - Storage* fs_api = furi_record_open("storage"); + Storage* fs_api = furi_record_open(RECORD_STORAGE); FileInfo fileinfo; FS_Error error = storage_common_stat(fs_api, path, &fileinfo); - furi_record_close("storage"); + furi_record_close(RECORD_STORAGE); PB_Main* response = MsgList_push_new(expected_msg_list); response->command_id = command_id; @@ -884,9 +887,9 @@ static void test_rpc_storage_stat_run(const char* path, uint32_t command_id) { } MU_TEST(test_storage_info) { - test_rpc_storage_info_run("/any", ++command_id); - test_rpc_storage_info_run("/int", ++command_id); - test_rpc_storage_info_run("/ext", ++command_id); + test_rpc_storage_info_run(STORAGE_ANY_PATH_PREFIX, ++command_id); + test_rpc_storage_info_run(STORAGE_INT_PATH_PREFIX, ++command_id); + test_rpc_storage_info_run(STORAGE_EXT_PATH_PREFIX, ++command_id); } #define TEST_DIR_STAT_NAME TEST_DIR "stat_dir" @@ -897,8 +900,8 @@ MU_TEST(test_storage_stat) { test_create_file(TEST_DIR_STAT "l33t.txt", 1337); test_rpc_storage_stat_run("/", ++command_id); - test_rpc_storage_stat_run("/int", ++command_id); - test_rpc_storage_stat_run("/ext", ++command_id); + test_rpc_storage_stat_run(STORAGE_INT_PATH_PREFIX, ++command_id); + test_rpc_storage_stat_run(STORAGE_EXT_PATH_PREFIX, ++command_id); test_rpc_storage_stat_run(TEST_DIR_STAT "empty.txt", ++command_id); test_rpc_storage_stat_run(TEST_DIR_STAT "l33t.txt", ++command_id); @@ -1224,8 +1227,8 @@ MU_TEST(test_storage_mkdir) { mu_check(test_is_exists(TEST_DIR "dir2")); } -static void test_storage_calculate_md5sum(const char* path, char* md5sum) { - Storage* api = furi_record_open("storage"); +static void test_storage_calculate_md5sum(const char* path, char* md5sum, size_t md5sum_size) { + Storage* api = furi_record_open(RECORD_STORAGE); File* file = storage_file_alloc(api); if(storage_file_open(file, path, FSAM_READ, FSOM_OPEN_EXISTING)) { @@ -1245,7 +1248,7 @@ static void test_storage_calculate_md5sum(const char* path, char* md5sum) { free(md5_ctx); for(uint8_t i = 0; i < hash_size; i++) { - md5sum += sprintf(md5sum, "%02x", hash[i]); + md5sum += snprintf(md5sum, md5sum_size, "%02x", hash[i]); } free(hash); @@ -1257,7 +1260,7 @@ static void test_storage_calculate_md5sum(const char* path, char* md5sum) { storage_file_close(file); storage_file_free(file); - furi_record_close("storage"); + furi_record_close(RECORD_STORAGE); } static void test_storage_md5sum_run( @@ -1297,9 +1300,9 @@ MU_TEST(test_storage_md5sum) { test_create_file(TEST_DIR "file1.txt", 0); test_create_file(TEST_DIR "file2.txt", 1); test_create_file(TEST_DIR "file3.txt", 512); - test_storage_calculate_md5sum(TEST_DIR "file1.txt", md5sum1); - test_storage_calculate_md5sum(TEST_DIR "file2.txt", md5sum2); - test_storage_calculate_md5sum(TEST_DIR "file3.txt", md5sum3); + test_storage_calculate_md5sum(TEST_DIR "file1.txt", md5sum1, MD5SUM_SIZE * 2 + 1); + test_storage_calculate_md5sum(TEST_DIR "file2.txt", md5sum2, MD5SUM_SIZE * 2 + 1); + test_storage_calculate_md5sum(TEST_DIR "file3.txt", md5sum3, MD5SUM_SIZE * 2 + 1); test_storage_md5sum_run(TEST_DIR "file1.txt", ++command_id, md5sum1, PB_CommandStatus_OK); test_storage_md5sum_run(TEST_DIR "file1.txt", ++command_id, md5sum1, PB_CommandStatus_OK); @@ -1346,8 +1349,7 @@ static void test_rpc_storage_rename_run( } MU_TEST(test_storage_rename) { - test_rpc_storage_rename_run( - NULL, NULL, ++command_id, PB_CommandStatus_ERROR_STORAGE_INVALID_NAME); + test_rpc_storage_rename_run("", "", ++command_id, PB_CommandStatus_ERROR_STORAGE_INVALID_NAME); furi_check(!test_is_exists(TEST_DIR "empty.txt")); test_create_file(TEST_DIR "empty.txt", 0); @@ -1516,7 +1518,8 @@ static void test_app_get_status_lock_run(bool locked_expected, uint32_t command_ MU_TEST(test_app_start_and_lock_status) { test_app_get_status_lock_run(false, ++command_id); - test_app_start_run(NULL, "/ext/file", PB_CommandStatus_ERROR_INVALID_PARAMETERS, ++command_id); + test_app_start_run( + NULL, EXT_PATH("file"), PB_CommandStatus_ERROR_INVALID_PARAMETERS, ++command_id); test_app_start_run(NULL, NULL, PB_CommandStatus_ERROR_INVALID_PARAMETERS, ++command_id); test_app_get_status_lock_run(false, ++command_id); test_app_start_run( @@ -1765,23 +1768,23 @@ MU_TEST_SUITE(test_rpc_session) { MU_RUN_TEST(test_rpc_feed_rubbish); MU_RUN_TEST(test_rpc_multisession_ping); - Storage* storage = furi_record_open("storage"); + Storage* storage = furi_record_open(RECORD_STORAGE); if(storage_sd_status(storage) != FSE_OK) { FURI_LOG_E(TAG, "SD card not mounted - skip storage tests"); } else { MU_RUN_TEST(test_rpc_multisession_storage); } - furi_record_close("storage"); + furi_record_close(RECORD_STORAGE); } int run_minunit_test_rpc() { - Storage* storage = furi_record_open("storage"); + Storage* storage = furi_record_open(RECORD_STORAGE); if(storage_sd_status(storage) != FSE_OK) { FURI_LOG_E(TAG, "SD card not mounted - skip storage tests"); } else { MU_RUN_SUITE(test_rpc_storage); } - furi_record_close("storage"); + furi_record_close(RECORD_STORAGE); MU_RUN_SUITE(test_rpc_system); MU_RUN_SUITE(test_rpc_app); MU_RUN_SUITE(test_rpc_session); diff --git a/applications/unit_tests/storage/dirwalk_test.c b/applications/unit_tests/storage/dirwalk_test.c index 25e122596..db3d91a96 100644 --- a/applications/unit_tests/storage/dirwalk_test.c +++ b/applications/unit_tests/storage/dirwalk_test.c @@ -175,10 +175,10 @@ MU_TEST_1(test_dirwalk_full, Storage* storage) { storage_test_paths_alloc(storage_test_dirwalk_full, COUNT_OF(storage_test_dirwalk_full)); DirWalk* dir_walk = dir_walk_alloc(storage); - mu_check(dir_walk_open(dir_walk, "/ext/dirwalk")); + mu_check(dir_walk_open(dir_walk, EXT_PATH("dirwalk"))); while(dir_walk_read(dir_walk, path, &fileinfo) == DirWalkOK) { - string_right(path, strlen("/ext/dirwalk/")); + string_right(path, strlen(EXT_PATH("dirwalk/"))); mu_check(storage_test_paths_mark(paths, path, (fileinfo.flags & FSF_DIRECTORY))); } @@ -200,10 +200,10 @@ MU_TEST_1(test_dirwalk_no_recursive, Storage* storage) { DirWalk* dir_walk = dir_walk_alloc(storage); dir_walk_set_recursive(dir_walk, false); - mu_check(dir_walk_open(dir_walk, "/ext/dirwalk")); + mu_check(dir_walk_open(dir_walk, EXT_PATH("dirwalk"))); while(dir_walk_read(dir_walk, path, &fileinfo) == DirWalkOK) { - string_right(path, strlen("/ext/dirwalk/")); + string_right(path, strlen(EXT_PATH("dirwalk/"))); mu_check(storage_test_paths_mark(paths, path, (fileinfo.flags & FSF_DIRECTORY))); } @@ -239,10 +239,10 @@ MU_TEST_1(test_dirwalk_filter, Storage* storage) { DirWalk* dir_walk = dir_walk_alloc(storage); dir_walk_set_filter_cb(dir_walk, test_dirwalk_filter_no_folder_ext, NULL); - mu_check(dir_walk_open(dir_walk, "/ext/dirwalk")); + mu_check(dir_walk_open(dir_walk, EXT_PATH("dirwalk"))); while(dir_walk_read(dir_walk, path, &fileinfo) == DirWalkOK) { - string_right(path, strlen("/ext/dirwalk/")); + string_right(path, strlen(EXT_PATH("dirwalk/"))); mu_check(storage_test_paths_mark(paths, path, (fileinfo.flags & FSF_DIRECTORY))); } @@ -255,15 +255,15 @@ MU_TEST_1(test_dirwalk_filter, Storage* storage) { } MU_TEST_SUITE(test_dirwalk_suite) { - Storage* storage = furi_record_open("storage"); - storage_dirs_create(storage, "/ext/dirwalk"); + Storage* storage = furi_record_open(RECORD_STORAGE); + storage_dirs_create(storage, EXT_PATH("dirwalk")); MU_RUN_TEST_1(test_dirwalk_full, storage); MU_RUN_TEST_1(test_dirwalk_no_recursive, storage); MU_RUN_TEST_1(test_dirwalk_filter, storage); - storage_simply_remove_recursive(storage, "/ext/dirwalk"); - furi_record_close("storage"); + storage_simply_remove_recursive(storage, EXT_PATH("dirwalk")); + furi_record_close(RECORD_STORAGE); } int run_minunit_test_dirwalk() { diff --git a/applications/unit_tests/storage/storage_test.c b/applications/unit_tests/storage/storage_test.c index c21abecca..c3628a4f9 100644 --- a/applications/unit_tests/storage/storage_test.c +++ b/applications/unit_tests/storage/storage_test.c @@ -2,28 +2,28 @@ #include #include -#define STORAGE_LOCKED_FILE "/ext/locked_file.test" -#define STORAGE_LOCKED_DIR "/int" +#define STORAGE_LOCKED_FILE EXT_PATH("locked_file.test") +#define STORAGE_LOCKED_DIR STORAGE_INT_PATH_PREFIX static void storage_file_open_lock_setup() { - Storage* storage = furi_record_open("storage"); + Storage* storage = furi_record_open(RECORD_STORAGE); File* file = storage_file_alloc(storage); storage_simply_remove(storage, STORAGE_LOCKED_FILE); mu_check(storage_file_open(file, STORAGE_LOCKED_FILE, FSAM_WRITE, FSOM_CREATE_NEW)); mu_check(storage_file_write(file, "0123", 4) == 4); mu_check(storage_file_close(file)); storage_file_free(file); - furi_record_close("storage"); + furi_record_close(RECORD_STORAGE); } static void storage_file_open_lock_teardown() { - Storage* storage = furi_record_open("storage"); + Storage* storage = furi_record_open(RECORD_STORAGE); mu_check(storage_simply_remove(storage, STORAGE_LOCKED_FILE)); - furi_record_close("storage"); + furi_record_close(RECORD_STORAGE); } static int32_t storage_file_locker(void* ctx) { - Storage* storage = furi_record_open("storage"); + Storage* storage = furi_record_open(RECORD_STORAGE); FuriSemaphore* semaphore = ctx; File* file = storage_file_alloc(storage); furi_check(storage_file_open(file, STORAGE_LOCKED_FILE, FSAM_READ_WRITE, FSOM_OPEN_EXISTING)); @@ -31,13 +31,13 @@ static int32_t storage_file_locker(void* ctx) { furi_delay_ms(1000); furi_check(storage_file_close(file)); - furi_record_close("storage"); + furi_record_close(RECORD_STORAGE); storage_file_free(file); return 0; } MU_TEST(storage_file_open_lock) { - Storage* storage = furi_record_open("storage"); + Storage* storage = furi_record_open(RECORD_STORAGE); bool result = false; FuriSemaphore* semaphore = furi_semaphore_alloc(1, 0); File* file = storage_file_alloc(storage); @@ -63,13 +63,13 @@ MU_TEST(storage_file_open_lock) { // clean data storage_file_free(file); - furi_record_close("storage"); + furi_record_close(RECORD_STORAGE); mu_assert(result, "cannot open locked file"); } MU_TEST(storage_file_open_close) { - Storage* storage = furi_record_open("storage"); + Storage* storage = furi_record_open(RECORD_STORAGE); File* file; file = storage_file_alloc(storage); @@ -84,7 +84,7 @@ MU_TEST(storage_file_open_close) { storage_file_free(file); } - furi_record_close("storage"); + furi_record_close(RECORD_STORAGE); } MU_TEST_SUITE(storage_file) { @@ -95,7 +95,7 @@ MU_TEST_SUITE(storage_file) { } MU_TEST(storage_dir_open_close) { - Storage* storage = furi_record_open("storage"); + Storage* storage = furi_record_open(RECORD_STORAGE); File* file; file = storage_file_alloc(storage); @@ -109,11 +109,11 @@ MU_TEST(storage_dir_open_close) { storage_file_free(file); } - furi_record_close("storage"); + furi_record_close(RECORD_STORAGE); } static int32_t storage_dir_locker(void* ctx) { - Storage* storage = furi_record_open("storage"); + Storage* storage = furi_record_open(RECORD_STORAGE); FuriSemaphore* semaphore = ctx; File* file = storage_file_alloc(storage); furi_check(storage_dir_open(file, STORAGE_LOCKED_DIR)); @@ -121,13 +121,13 @@ static int32_t storage_dir_locker(void* ctx) { furi_delay_ms(1000); furi_check(storage_dir_close(file)); - furi_record_close("storage"); + furi_record_close(RECORD_STORAGE); storage_file_free(file); return 0; } MU_TEST(storage_dir_open_lock) { - Storage* storage = furi_record_open("storage"); + Storage* storage = furi_record_open(RECORD_STORAGE); bool result = false; FuriSemaphore* semaphore = furi_semaphore_alloc(1, 0); File* file = storage_file_alloc(storage); @@ -153,7 +153,7 @@ MU_TEST(storage_dir_open_lock) { // clean data storage_file_free(file); - furi_record_close("storage"); + furi_record_close(RECORD_STORAGE); mu_assert(result, "cannot open locked dir"); } @@ -265,46 +265,48 @@ static bool storage_dir_rename_check(Storage* storage, const char* base) { } MU_TEST(storage_file_rename) { - Storage* storage = furi_record_open("storage"); + Storage* storage = furi_record_open(RECORD_STORAGE); File* file = storage_file_alloc(storage); - mu_check(write_file_13DA(storage, "/ext/file.old")); - mu_check(check_file_13DA(storage, "/ext/file.old")); - mu_assert_int_eq(FSE_OK, storage_common_rename(storage, "/ext/file.old", "/ext/file.new")); - mu_assert_int_eq(FSE_NOT_EXIST, storage_common_stat(storage, "/ext/file.old", NULL)); - mu_assert_int_eq(FSE_OK, storage_common_stat(storage, "/ext/file.new", NULL)); - mu_check(check_file_13DA(storage, "/ext/file.new")); - mu_assert_int_eq(FSE_OK, storage_common_remove(storage, "/ext/file.new")); + mu_check(write_file_13DA(storage, EXT_PATH("file.old"))); + mu_check(check_file_13DA(storage, EXT_PATH("file.old"))); + mu_assert_int_eq( + FSE_OK, storage_common_rename(storage, EXT_PATH("file.old"), EXT_PATH("file.new"))); + mu_assert_int_eq(FSE_NOT_EXIST, storage_common_stat(storage, EXT_PATH("file.old"), NULL)); + mu_assert_int_eq(FSE_OK, storage_common_stat(storage, EXT_PATH("file.new"), NULL)); + mu_check(check_file_13DA(storage, EXT_PATH("file.new"))); + mu_assert_int_eq(FSE_OK, storage_common_remove(storage, EXT_PATH("file.new"))); storage_file_free(file); - furi_record_close("storage"); + furi_record_close(RECORD_STORAGE); } MU_TEST(storage_dir_rename) { - Storage* storage = furi_record_open("storage"); + Storage* storage = furi_record_open(RECORD_STORAGE); - storage_dir_create(storage, "/ext/dir.old"); + storage_dir_create(storage, EXT_PATH("dir.old")); - mu_check(storage_dir_rename_check(storage, "/ext/dir.old")); + mu_check(storage_dir_rename_check(storage, EXT_PATH("dir.old"))); - mu_assert_int_eq(FSE_OK, storage_common_rename(storage, "/ext/dir.old", "/ext/dir.new")); - mu_assert_int_eq(FSE_NOT_EXIST, storage_common_stat(storage, "/ext/dir.old", NULL)); - mu_check(storage_dir_rename_check(storage, "/ext/dir.new")); + mu_assert_int_eq( + FSE_OK, storage_common_rename(storage, EXT_PATH("dir.old"), EXT_PATH("dir.new"))); + mu_assert_int_eq(FSE_NOT_EXIST, storage_common_stat(storage, EXT_PATH("dir.old"), NULL)); + mu_check(storage_dir_rename_check(storage, EXT_PATH("dir.new"))); - storage_dir_remove(storage, "/ext/dir.new"); - mu_assert_int_eq(FSE_NOT_EXIST, storage_common_stat(storage, "/ext/dir.new", NULL)); + storage_dir_remove(storage, EXT_PATH("dir.new")); + mu_assert_int_eq(FSE_NOT_EXIST, storage_common_stat(storage, EXT_PATH("dir.new"), NULL)); - furi_record_close("storage"); + furi_record_close(RECORD_STORAGE); } MU_TEST_SUITE(storage_rename) { MU_RUN_TEST(storage_file_rename); MU_RUN_TEST(storage_dir_rename); - Storage* storage = furi_record_open("storage"); - storage_dir_remove(storage, "/ext/dir.old"); - storage_dir_remove(storage, "/ext/dir.new"); - furi_record_close("storage"); + Storage* storage = furi_record_open(RECORD_STORAGE); + storage_dir_remove(storage, EXT_PATH("dir.old")); + storage_dir_remove(storage, EXT_PATH("dir.new")); + furi_record_close(RECORD_STORAGE); } int run_minunit_test_storage() { diff --git a/applications/unit_tests/stream/stream_test.c b/applications/unit_tests/stream/stream_test.c index 65f1409ac..b5a2d3980 100644 --- a/applications/unit_tests/stream/stream_test.c +++ b/applications/unit_tests/stream/stream_test.c @@ -278,19 +278,20 @@ MU_TEST(stream_composite_test) { stream_free(stream); // test file stream - Storage* storage = furi_record_open("storage"); + Storage* storage = furi_record_open(RECORD_STORAGE); stream = file_stream_alloc(storage); - mu_check(file_stream_open(stream, "/ext/filestream.str", FSAM_READ_WRITE, FSOM_CREATE_ALWAYS)); + mu_check( + file_stream_open(stream, EXT_PATH("filestream.str"), FSAM_READ_WRITE, FSOM_CREATE_ALWAYS)); MU_RUN_TEST_1(stream_composite_subtest, stream); stream_free(stream); // test buffered file stream stream = buffered_file_stream_alloc(storage); mu_check(buffered_file_stream_open( - stream, "/ext/filestream.str", FSAM_READ_WRITE, FSOM_CREATE_ALWAYS)); + stream, EXT_PATH("filestream.str"), FSAM_READ_WRITE, FSOM_CREATE_ALWAYS)); MU_RUN_TEST_1(stream_composite_subtest, stream); stream_free(stream); - furi_record_close("storage"); + furi_record_close(RECORD_STORAGE); } MU_TEST_1(stream_write_subtest, Stream* stream) { @@ -307,7 +308,7 @@ MU_TEST_1(stream_read_subtest, Stream* stream) { MU_TEST(stream_write_read_save_load_test) { Stream* stream_orig = string_stream_alloc(); Stream* stream_copy = string_stream_alloc(); - Storage* storage = furi_record_open("storage"); + Storage* storage = furi_record_open(RECORD_STORAGE); // write, read MU_RUN_TEST_1(stream_write_subtest, stream_orig); @@ -321,7 +322,7 @@ MU_TEST(stream_write_read_save_load_test) { mu_check(stream_seek(stream_orig, 0, StreamOffsetFromStart)); mu_assert_int_eq( strlen(stream_test_data), - stream_save_to_file(stream_orig, storage, "/ext/filestream.str", FSOM_CREATE_ALWAYS)); + stream_save_to_file(stream_orig, storage, EXT_PATH("filestream.str"), FSOM_CREATE_ALWAYS)); stream_free(stream_copy); stream_free(stream_orig); @@ -330,11 +331,11 @@ MU_TEST(stream_write_read_save_load_test) { Stream* stream_new = string_stream_alloc(); mu_assert_int_eq( strlen(stream_test_data), - stream_load_from_file(stream_new, storage, "/ext/filestream.str")); + stream_load_from_file(stream_new, storage, EXT_PATH("filestream.str"))); MU_RUN_TEST_1(stream_read_subtest, stream_new); stream_free(stream_new); - furi_record_close("storage"); + furi_record_close(RECORD_STORAGE); } MU_TEST_1(stream_split_subtest, Stream* stream) { @@ -369,20 +370,49 @@ MU_TEST(stream_split_test) { stream_free(stream); // test file stream - Storage* storage = furi_record_open("storage"); + Storage* storage = furi_record_open(RECORD_STORAGE); stream = file_stream_alloc(storage); - mu_check(file_stream_open(stream, "/ext/filestream.str", FSAM_READ_WRITE, FSOM_CREATE_ALWAYS)); + mu_check( + file_stream_open(stream, EXT_PATH("filestream.str"), FSAM_READ_WRITE, FSOM_CREATE_ALWAYS)); MU_RUN_TEST_1(stream_split_subtest, stream); stream_free(stream); // test buffered stream stream = buffered_file_stream_alloc(storage); mu_check(buffered_file_stream_open( - stream, "/ext/filestream.str", FSAM_READ_WRITE, FSOM_CREATE_ALWAYS)); + stream, EXT_PATH("filestream.str"), FSAM_READ_WRITE, FSOM_CREATE_ALWAYS)); MU_RUN_TEST_1(stream_split_subtest, stream); stream_free(stream); - furi_record_close("storage"); + furi_record_close(RECORD_STORAGE); +} + +MU_TEST(stream_buffered_write_after_read_test) { + const char* prefix = "I write "; + const char* substr = "Hello there"; + + const size_t substr_len = strlen(substr); + const size_t prefix_len = strlen(prefix); + const size_t buf_size = substr_len + 1; + + char buf[buf_size]; + memset(buf, 0, buf_size); + + Storage* storage = furi_record_open(RECORD_STORAGE); + Stream* stream = buffered_file_stream_alloc(storage); + mu_check(buffered_file_stream_open( + stream, EXT_PATH("filestream.str"), FSAM_READ_WRITE, FSOM_CREATE_ALWAYS)); + mu_assert_int_eq(strlen(stream_test_data), stream_write_cstring(stream, stream_test_data)); + mu_check(stream_rewind(stream)); + mu_assert_int_eq(prefix_len, stream_read(stream, (uint8_t*)buf, prefix_len)); + mu_assert_string_eq(prefix, buf); + mu_assert_int_eq(substr_len, stream_write(stream, (uint8_t*)substr, substr_len)); + mu_check(stream_seek(stream, prefix_len, StreamOffsetFromStart)); + mu_assert_int_eq(substr_len, stream_read(stream, (uint8_t*)buf, substr_len)); + mu_assert_string_eq(substr, buf); + + stream_free(stream); + furi_record_close(RECORD_STORAGE); } MU_TEST(stream_buffered_large_file_test) { @@ -391,7 +421,7 @@ MU_TEST(stream_buffered_large_file_test) { string_init(input_data); string_init(output_data); - Storage* storage = furi_record_open("storage"); + Storage* storage = furi_record_open(RECORD_STORAGE); // generate test data consisting of several identical lines const size_t data_size = 4096; @@ -405,7 +435,7 @@ MU_TEST(stream_buffered_large_file_test) { // write test data to file Stream* stream = buffered_file_stream_alloc(storage); mu_check(buffered_file_stream_open( - stream, "/ext/filestream.str", FSAM_READ_WRITE, FSOM_CREATE_ALWAYS)); + stream, EXT_PATH("filestream.str"), FSAM_READ_WRITE, FSOM_CREATE_ALWAYS)); mu_assert_int_eq(0, stream_size(stream)); mu_assert_int_eq(string_size(input_data), stream_write_string(stream, input_data)); mu_assert_int_eq(string_size(input_data), stream_size(stream)); @@ -459,7 +489,7 @@ MU_TEST(stream_buffered_large_file_test) { stream_free(stream); - furi_record_close("storage"); + furi_record_close(RECORD_STORAGE); string_clear(input_data); string_clear(output_data); } @@ -468,6 +498,7 @@ MU_TEST_SUITE(stream_suite) { MU_RUN_TEST(stream_write_read_save_load_test); MU_RUN_TEST(stream_composite_test); MU_RUN_TEST(stream_split_test); + MU_RUN_TEST(stream_buffered_write_after_read_test); MU_RUN_TEST(stream_buffered_large_file_test); } diff --git a/applications/unit_tests/subghz/subghz_test.c b/applications/unit_tests/subghz/subghz_test.c index 23c6117b8..fb3c7a4eb 100644 --- a/applications/unit_tests/subghz/subghz_test.c +++ b/applications/unit_tests/subghz/subghz_test.c @@ -9,11 +9,11 @@ #include #define TAG "SubGhz TEST" -#define KEYSTORE_DIR_NAME "/ext/subghz/assets/keeloq_mfcodes" -#define CAME_ATOMO_DIR_NAME "/ext/subghz/assets/came_atomo" -#define NICE_FLOR_S_DIR_NAME "/ext/subghz/assets/nice_flor_s" -#define TEST_RANDOM_DIR_NAME "/ext/unit_tests/subghz/test_random_raw.sub" -#define TEST_RANDOM_COUNT_PARSE 119 +#define KEYSTORE_DIR_NAME EXT_PATH("subghz/assets/keeloq_mfcodes") +#define CAME_ATOMO_DIR_NAME EXT_PATH("subghz/assets/came_atomo") +#define NICE_FLOR_S_DIR_NAME EXT_PATH("subghz/assets/nice_flor_s") +#define TEST_RANDOM_DIR_NAME EXT_PATH("unit_tests/subghz/test_random_raw.sub") +#define TEST_RANDOM_COUNT_PARSE 158 #define TEST_TIMEOUT 10000 static SubGhzEnvironment* environment_handler; @@ -145,7 +145,7 @@ static bool subghz_encoder_test(const char* path) { string_init(temp_str); bool file_load = false; - Storage* storage = furi_record_open("storage"); + Storage* storage = furi_record_open(RECORD_STORAGE); FlipperFormat* fff_data_file = flipper_format_file_alloc(storage); do { @@ -210,237 +210,297 @@ MU_TEST(subghz_keystore_test) { MU_TEST(subghz_decoder_came_atomo_test) { mu_assert( subghz_decoder_test( - "/ext/unit_tests/subghz/came_atomo_raw.sub", SUBGHZ_PROTOCOL_CAME_ATOMO_NAME), + EXT_PATH("unit_tests/subghz/came_atomo_raw.sub"), SUBGHZ_PROTOCOL_CAME_ATOMO_NAME), "Test decoder " SUBGHZ_PROTOCOL_CAME_ATOMO_NAME " error\r\n"); } MU_TEST(subghz_decoder_came_test) { mu_assert( - subghz_decoder_test("/ext/unit_tests/subghz/came_raw.sub", SUBGHZ_PROTOCOL_CAME_NAME), + subghz_decoder_test(EXT_PATH("unit_tests/subghz/came_raw.sub"), SUBGHZ_PROTOCOL_CAME_NAME), "Test decoder " SUBGHZ_PROTOCOL_CAME_NAME " error\r\n"); } MU_TEST(subghz_decoder_came_twee_test) { mu_assert( subghz_decoder_test( - "/ext/unit_tests/subghz/came_twee_raw.sub", SUBGHZ_PROTOCOL_CAME_TWEE_NAME), + EXT_PATH("unit_tests/subghz/came_twee_raw.sub"), SUBGHZ_PROTOCOL_CAME_TWEE_NAME), "Test decoder " SUBGHZ_PROTOCOL_CAME_TWEE_NAME " error\r\n"); } MU_TEST(subghz_decoder_faac_slh_test) { mu_assert( subghz_decoder_test( - "/ext/unit_tests/subghz/faac_slh_raw.sub", SUBGHZ_PROTOCOL_FAAC_SLH_NAME), + EXT_PATH("unit_tests/subghz/faac_slh_raw.sub"), SUBGHZ_PROTOCOL_FAAC_SLH_NAME), "Test decoder " SUBGHZ_PROTOCOL_FAAC_SLH_NAME " error\r\n"); } MU_TEST(subghz_decoder_gate_tx_test) { mu_assert( - subghz_decoder_test("/ext/unit_tests/subghz/gate_tx_raw.sub", SUBGHZ_PROTOCOL_GATE_TX_NAME), + subghz_decoder_test( + EXT_PATH("unit_tests/subghz/gate_tx_raw.sub"), SUBGHZ_PROTOCOL_GATE_TX_NAME), "Test decoder " SUBGHZ_PROTOCOL_GATE_TX_NAME " error\r\n"); } MU_TEST(subghz_decoder_hormann_hsm_test) { mu_assert( subghz_decoder_test( - "/ext/unit_tests/subghz/hormann_hsm_raw.sub", SUBGHZ_PROTOCOL_HORMANN_HSM_NAME), + EXT_PATH("unit_tests/subghz/hormann_hsm_raw.sub"), SUBGHZ_PROTOCOL_HORMANN_HSM_NAME), "Test decoder " SUBGHZ_PROTOCOL_HORMANN_HSM_NAME " error\r\n"); } MU_TEST(subghz_decoder_ido_test) { mu_assert( - subghz_decoder_test("/ext/unit_tests/subghz/ido_117_111_raw.sub", SUBGHZ_PROTOCOL_IDO_NAME), + subghz_decoder_test( + EXT_PATH("unit_tests/subghz/ido_117_111_raw.sub"), SUBGHZ_PROTOCOL_IDO_NAME), "Test decoder " SUBGHZ_PROTOCOL_IDO_NAME " error\r\n"); } MU_TEST(subghz_decoder_keelog_test) { mu_assert( - subghz_decoder_test("/ext/unit_tests/subghz/doorhan_raw.sub", SUBGHZ_PROTOCOL_KEELOQ_NAME), + subghz_decoder_test( + EXT_PATH("unit_tests/subghz/doorhan_raw.sub"), SUBGHZ_PROTOCOL_KEELOQ_NAME), "Test decoder " SUBGHZ_PROTOCOL_KEELOQ_NAME " error\r\n"); } MU_TEST(subghz_decoder_kia_seed_test) { mu_assert( - subghz_decoder_test("/ext/unit_tests/subghz/kia_seed_raw.sub", SUBGHZ_PROTOCOL_KIA_NAME), + subghz_decoder_test( + EXT_PATH("unit_tests/subghz/kia_seed_raw.sub"), SUBGHZ_PROTOCOL_KIA_NAME), "Test decoder " SUBGHZ_PROTOCOL_KIA_NAME " error\r\n"); } MU_TEST(subghz_decoder_nero_radio_test) { mu_assert( subghz_decoder_test( - "/ext/unit_tests/subghz/nero_radio_raw.sub", SUBGHZ_PROTOCOL_NERO_RADIO_NAME), + EXT_PATH("unit_tests/subghz/nero_radio_raw.sub"), SUBGHZ_PROTOCOL_NERO_RADIO_NAME), "Test decoder " SUBGHZ_PROTOCOL_NERO_RADIO_NAME " error\r\n"); } MU_TEST(subghz_decoder_nero_sketch_test) { mu_assert( subghz_decoder_test( - "/ext/unit_tests/subghz/nero_sketch_raw.sub", SUBGHZ_PROTOCOL_NERO_SKETCH_NAME), + EXT_PATH("unit_tests/subghz/nero_sketch_raw.sub"), SUBGHZ_PROTOCOL_NERO_SKETCH_NAME), "Test decoder " SUBGHZ_PROTOCOL_NERO_SKETCH_NAME " error\r\n"); } MU_TEST(subghz_decoder_nice_flo_test) { mu_assert( subghz_decoder_test( - "/ext/unit_tests/subghz/nice_flo_raw.sub", SUBGHZ_PROTOCOL_NICE_FLO_NAME), + EXT_PATH("unit_tests/subghz/nice_flo_raw.sub"), SUBGHZ_PROTOCOL_NICE_FLO_NAME), "Test decoder " SUBGHZ_PROTOCOL_NICE_FLO_NAME " error\r\n"); } MU_TEST(subghz_decoder_nice_flor_s_test) { mu_assert( subghz_decoder_test( - "/ext/unit_tests/subghz/nice_flor_s_raw.sub", SUBGHZ_PROTOCOL_NICE_FLOR_S_NAME), + EXT_PATH("unit_tests/subghz/nice_flor_s_raw.sub"), SUBGHZ_PROTOCOL_NICE_FLOR_S_NAME), "Test decoder " SUBGHZ_PROTOCOL_NICE_FLOR_S_NAME " error\r\n"); } MU_TEST(subghz_decoder_princeton_test) { mu_assert( subghz_decoder_test( - "/ext/unit_tests/subghz/Princeton_raw.sub", SUBGHZ_PROTOCOL_PRINCETON_NAME), + EXT_PATH("unit_tests/subghz/Princeton_raw.sub"), SUBGHZ_PROTOCOL_PRINCETON_NAME), "Test decoder " SUBGHZ_PROTOCOL_PRINCETON_NAME " error\r\n"); } MU_TEST(subghz_decoder_scher_khan_magic_code_test) { mu_assert( subghz_decoder_test( - "/ext/unit_tests/subghz/scher_khan_magic_code.sub", SUBGHZ_PROTOCOL_SCHER_KHAN_NAME), + EXT_PATH("unit_tests/subghz/scher_khan_magic_code.sub"), + SUBGHZ_PROTOCOL_SCHER_KHAN_NAME), "Test decoder " SUBGHZ_PROTOCOL_SCHER_KHAN_NAME " error\r\n"); } MU_TEST(subghz_decoder_somfy_keytis_test) { mu_assert( subghz_decoder_test( - "/ext/unit_tests/subghz/Somfy_keytis_raw.sub", SUBGHZ_PROTOCOL_SOMFY_KEYTIS_NAME), + EXT_PATH("unit_tests/subghz/Somfy_keytis_raw.sub"), SUBGHZ_PROTOCOL_SOMFY_KEYTIS_NAME), "Test decoder " SUBGHZ_PROTOCOL_SOMFY_KEYTIS_NAME " error\r\n"); } MU_TEST(subghz_decoder_somfy_telis_test) { mu_assert( subghz_decoder_test( - "/ext/unit_tests/subghz/somfy_telis_raw.sub", SUBGHZ_PROTOCOL_SOMFY_TELIS_NAME), + EXT_PATH("unit_tests/subghz/somfy_telis_raw.sub"), SUBGHZ_PROTOCOL_SOMFY_TELIS_NAME), "Test decoder " SUBGHZ_PROTOCOL_SOMFY_TELIS_NAME " error\r\n"); } MU_TEST(subghz_decoder_star_line_test) { mu_assert( subghz_decoder_test( - "/ext/unit_tests/subghz/cenmax_raw.sub", SUBGHZ_PROTOCOL_STAR_LINE_NAME), + EXT_PATH("unit_tests/subghz/cenmax_raw.sub"), SUBGHZ_PROTOCOL_STAR_LINE_NAME), "Test decoder " SUBGHZ_PROTOCOL_STAR_LINE_NAME " error\r\n"); } MU_TEST(subghz_decoder_linear_test) { mu_assert( - subghz_decoder_test("/ext/unit_tests/subghz/linear_raw.sub", SUBGHZ_PROTOCOL_LINEAR_NAME), + subghz_decoder_test( + EXT_PATH("unit_tests/subghz/linear_raw.sub"), SUBGHZ_PROTOCOL_LINEAR_NAME), "Test decoder " SUBGHZ_PROTOCOL_LINEAR_NAME " error\r\n"); } MU_TEST(subghz_decoder_megacode_test) { mu_assert( subghz_decoder_test( - "/ext/unit_tests/subghz/megacode_raw.sub", SUBGHZ_PROTOCOL_MEGACODE_NAME), + EXT_PATH("unit_tests/subghz/megacode_raw.sub"), SUBGHZ_PROTOCOL_MEGACODE_NAME), "Test decoder " SUBGHZ_PROTOCOL_MEGACODE_NAME " error\r\n"); } MU_TEST(subghz_decoder_secplus_v1_test) { mu_assert( subghz_decoder_test( - "/ext/unit_tests/subghz/security_pls_1_0_raw.sub", SUBGHZ_PROTOCOL_SECPLUS_V1_NAME), + EXT_PATH("unit_tests/subghz/security_pls_1_0_raw.sub"), + SUBGHZ_PROTOCOL_SECPLUS_V1_NAME), "Test decoder " SUBGHZ_PROTOCOL_SECPLUS_V1_NAME " error\r\n"); } MU_TEST(subghz_decoder_secplus_v2_test) { mu_assert( subghz_decoder_test( - "/ext/unit_tests/subghz/security_pls_2_0_raw.sub", SUBGHZ_PROTOCOL_SECPLUS_V2_NAME), + EXT_PATH("unit_tests/subghz/security_pls_2_0_raw.sub"), + SUBGHZ_PROTOCOL_SECPLUS_V2_NAME), "Test decoder " SUBGHZ_PROTOCOL_SECPLUS_V2_NAME " error\r\n"); } MU_TEST(subghz_decoder_holtek_test) { mu_assert( - subghz_decoder_test("/ext/unit_tests/subghz/holtek_raw.sub", SUBGHZ_PROTOCOL_HOLTEK_NAME), + subghz_decoder_test( + EXT_PATH("unit_tests/subghz/holtek_raw.sub"), SUBGHZ_PROTOCOL_HOLTEK_NAME), "Test decoder " SUBGHZ_PROTOCOL_HOLTEK_NAME " error\r\n"); } MU_TEST(subghz_decoder_power_smart_test) { mu_assert( subghz_decoder_test( - "/ext/unit_tests/subghz/power_smart_raw.sub", SUBGHZ_PROTOCOL_POWER_SMART_NAME), + EXT_PATH("unit_tests/subghz/power_smart_raw.sub"), SUBGHZ_PROTOCOL_POWER_SMART_NAME), "Test decoder " SUBGHZ_PROTOCOL_POWER_SMART_NAME " error\r\n"); } +MU_TEST(subghz_decoder_marantec_test) { + mu_assert( + subghz_decoder_test( + EXT_PATH("unit_tests/subghz/marantec_raw.sub"), SUBGHZ_PROTOCOL_MARANTEC_NAME), + "Test decoder " SUBGHZ_PROTOCOL_MARANTEC_NAME " error\r\n"); +} + +MU_TEST(subghz_decoder_bett_test) { + mu_assert( + subghz_decoder_test(EXT_PATH("unit_tests/subghz/bett_raw.sub"), SUBGHZ_PROTOCOL_BETT_NAME), + "Test decoder " SUBGHZ_PROTOCOL_BETT_NAME " error\r\n"); +} + +MU_TEST(subghz_decoder_doitrand_test) { + mu_assert( + subghz_decoder_test( + EXT_PATH("unit_tests/subghz/doitrand_raw.sub"), SUBGHZ_PROTOCOL_DOITRAND_NAME), + "Test decoder " SUBGHZ_PROTOCOL_DOITRAND_NAME " error\r\n"); +} + +MU_TEST(subghz_decoder_phoenix_v2_test) { + mu_assert( + subghz_decoder_test( + EXT_PATH("unit_tests/subghz/phoenix_v2_raw.sub"), SUBGHZ_PROTOCOL_PHOENIX_V2_NAME), + "Test decoder " SUBGHZ_PROTOCOL_PHOENIX_V2_NAME " error\r\n"); +} + //test encoders MU_TEST(subghz_encoder_princeton_test) { mu_assert( - subghz_encoder_test("/ext/unit_tests/subghz/princeton.sub"), + subghz_encoder_test(EXT_PATH("unit_tests/subghz/princeton.sub")), "Test encoder " SUBGHZ_PROTOCOL_PRINCETON_NAME " error\r\n"); } MU_TEST(subghz_encoder_came_test) { mu_assert( - subghz_encoder_test("/ext/unit_tests/subghz/came.sub"), + subghz_encoder_test(EXT_PATH("unit_tests/subghz/came.sub")), "Test encoder " SUBGHZ_PROTOCOL_CAME_NAME " error\r\n"); } MU_TEST(subghz_encoder_came_twee_test) { mu_assert( - subghz_encoder_test("/ext/unit_tests/subghz/came_twee.sub"), + subghz_encoder_test(EXT_PATH("unit_tests/subghz/came_twee.sub")), "Test encoder " SUBGHZ_PROTOCOL_CAME_TWEE_NAME " error\r\n"); } MU_TEST(subghz_encoder_gate_tx_test) { mu_assert( - subghz_encoder_test("/ext/unit_tests/subghz/gate_tx.sub"), + subghz_encoder_test(EXT_PATH("unit_tests/subghz/gate_tx.sub")), "Test encoder " SUBGHZ_PROTOCOL_GATE_TX_NAME " error\r\n"); } MU_TEST(subghz_encoder_nice_flo_test) { mu_assert( - subghz_encoder_test("/ext/unit_tests/subghz/nice_flo.sub"), + subghz_encoder_test(EXT_PATH("unit_tests/subghz/nice_flo.sub")), "Test encoder " SUBGHZ_PROTOCOL_NICE_FLO_NAME " error\r\n"); } MU_TEST(subghz_encoder_keelog_test) { mu_assert( - subghz_encoder_test("/ext/unit_tests/subghz/doorhan.sub"), + subghz_encoder_test(EXT_PATH("unit_tests/subghz/doorhan.sub")), "Test encoder " SUBGHZ_PROTOCOL_KEELOQ_NAME " error\r\n"); } MU_TEST(subghz_encoder_linear_test) { mu_assert( - subghz_encoder_test("/ext/unit_tests/subghz/linear.sub"), + subghz_encoder_test(EXT_PATH("unit_tests/subghz/linear.sub")), "Test encoder " SUBGHZ_PROTOCOL_LINEAR_NAME " error\r\n"); } MU_TEST(subghz_encoder_megacode_test) { mu_assert( - subghz_encoder_test("/ext/unit_tests/subghz/megacode.sub"), + subghz_encoder_test(EXT_PATH("unit_tests/subghz/megacode.sub")), "Test encoder " SUBGHZ_PROTOCOL_MEGACODE_NAME " error\r\n"); } MU_TEST(subghz_encoder_holtek_test) { mu_assert( - subghz_encoder_test("/ext/unit_tests/subghz/holtek.sub"), + subghz_encoder_test(EXT_PATH("unit_tests/subghz/holtek.sub")), "Test encoder " SUBGHZ_PROTOCOL_HOLTEK_NAME " error\r\n"); } MU_TEST(subghz_encoder_secplus_v1_test) { mu_assert( - subghz_encoder_test("/ext/unit_tests/subghz/security_pls_1_0.sub"), + subghz_encoder_test(EXT_PATH("unit_tests/subghz/security_pls_1_0.sub")), "Test encoder " SUBGHZ_PROTOCOL_SECPLUS_V1_NAME " error\r\n"); } MU_TEST(subghz_encoder_secplus_v2_test) { mu_assert( - subghz_encoder_test("/ext/unit_tests/subghz/security_pls_2_0.sub"), + subghz_encoder_test(EXT_PATH("unit_tests/subghz/security_pls_2_0.sub")), "Test encoder " SUBGHZ_PROTOCOL_SECPLUS_V2_NAME " error\r\n"); } MU_TEST(subghz_encoder_power_smart_test) { mu_assert( - subghz_encoder_test("/ext/unit_tests/subghz/power_smart.sub"), + subghz_encoder_test(EXT_PATH("unit_tests/subghz/power_smart.sub")), "Test encoder " SUBGHZ_PROTOCOL_POWER_SMART_NAME " error\r\n"); } +MU_TEST(subghz_encoder_marantec_test) { + mu_assert( + subghz_encoder_test(EXT_PATH("unit_tests/subghz/marantec.sub")), + "Test encoder " SUBGHZ_PROTOCOL_MARANTEC_NAME " error\r\n"); +} + +MU_TEST(subghz_encoder_bett_test) { + mu_assert( + subghz_encoder_test(EXT_PATH("unit_tests/subghz/bett.sub")), + "Test encoder " SUBGHZ_PROTOCOL_BETT_NAME " error\r\n"); +} + +MU_TEST(subghz_encoder_doitrand_test) { + mu_assert( + subghz_encoder_test(EXT_PATH("unit_tests/subghz/doitrand.sub")), + "Test encoder " SUBGHZ_PROTOCOL_DOITRAND_NAME " error\r\n"); +} + +MU_TEST(subghz_encoder_phoenix_v2_test) { + mu_assert( + subghz_encoder_test(EXT_PATH("unit_tests/subghz/phoenix_v2.sub")), + "Test encoder " SUBGHZ_PROTOCOL_PHOENIX_V2_NAME " error\r\n"); +} + MU_TEST(subghz_random_test) { mu_assert(subghz_decode_random_test(TEST_RANDOM_DIR_NAME), "Random test error\r\n"); } @@ -473,6 +533,10 @@ MU_TEST_SUITE(subghz) { MU_RUN_TEST(subghz_decoder_secplus_v2_test); MU_RUN_TEST(subghz_decoder_holtek_test); MU_RUN_TEST(subghz_decoder_power_smart_test); + MU_RUN_TEST(subghz_decoder_marantec_test); + MU_RUN_TEST(subghz_decoder_bett_test); + MU_RUN_TEST(subghz_decoder_doitrand_test); + MU_RUN_TEST(subghz_decoder_phoenix_v2_test); MU_RUN_TEST(subghz_encoder_princeton_test); MU_RUN_TEST(subghz_encoder_came_test); @@ -486,6 +550,10 @@ MU_TEST_SUITE(subghz) { MU_RUN_TEST(subghz_encoder_secplus_v1_test); MU_RUN_TEST(subghz_encoder_secplus_v2_test); MU_RUN_TEST(subghz_encoder_power_smart_test); + MU_RUN_TEST(subghz_encoder_marantec_test); + MU_RUN_TEST(subghz_encoder_bett_test); + MU_RUN_TEST(subghz_encoder_doitrand_test); + MU_RUN_TEST(subghz_encoder_phoenix_v2_test); MU_RUN_TEST(subghz_random_test); subghz_test_deinit(); diff --git a/applications/unit_tests/test_index.c b/applications/unit_tests/test_index.c index ca7641b1c..e52822465 100644 --- a/applications/unit_tests/test_index.c +++ b/applications/unit_tests/test_index.c @@ -67,8 +67,8 @@ void unit_tests_cli(Cli* cli, string_t args, void* context) { minunit_fail = 0; minunit_status = 0; - Loader* loader = furi_record_open("loader"); - NotificationApp* notification = furi_record_open("notification"); + Loader* loader = furi_record_open(RECORD_LOADER); + NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION); // TODO: lock device while test running if(loader_is_locked(loader)) { @@ -116,16 +116,16 @@ void unit_tests_cli(Cli* cli, string_t args, void* context) { } } - furi_record_close("notification"); - furi_record_close("loader"); + furi_record_close(RECORD_NOTIFICATION); + furi_record_close(RECORD_LOADER); } void unit_tests_on_system_start() { #ifdef SRV_CLI - Cli* cli = furi_record_open("cli"); + Cli* cli = furi_record_open(RECORD_CLI); // We need to launch apps from tests, so we cannot lock loader cli_add_command(cli, "unit_tests", CliCommandFlagParallelSafe, unit_tests_cli, NULL); - furi_record_close("cli"); + furi_record_close(RECORD_CLI); #endif } diff --git a/applications/updater/cli/updater_cli.c b/applications/updater/cli/updater_cli.c index 3dfd145ce..ec209bd1d 100644 --- a/applications/updater/cli/updater_cli.c +++ b/applications/updater/cli/updater_cli.c @@ -35,17 +35,17 @@ static void updater_cli_install(string_t manifest_path) { static void updater_cli_backup(string_t args) { printf("Backup /int to '%s'\r\n", string_get_cstr(args)); - Storage* storage = furi_record_open("storage"); + Storage* storage = furi_record_open(RECORD_STORAGE); bool success = lfs_backup_create(storage, string_get_cstr(args)); - furi_record_close("storage"); + furi_record_close(RECORD_STORAGE); printf("Result: %s\r\n", success ? "OK" : "FAIL"); } static void updater_cli_restore(string_t args) { printf("Restore /int from '%s'\r\n", string_get_cstr(args)); - Storage* storage = furi_record_open("storage"); + Storage* storage = furi_record_open(RECORD_STORAGE); bool success = lfs_backup_unpack(storage, string_get_cstr(args)); - furi_record_close("storage"); + furi_record_close(RECORD_STORAGE); printf("Result: %s\r\n", success ? "OK" : "FAIL"); } @@ -88,9 +88,9 @@ static void updater_cli_ep(Cli* cli, string_t args, void* context) { static int32_t updater_spawner_thread_worker(void* arg) { UNUSED(arg); - Loader* loader = furi_record_open("loader"); + Loader* loader = furi_record_open(RECORD_LOADER); loader_start(loader, "UpdaterApp", NULL); - furi_record_close("loader"); + furi_record_close(RECORD_LOADER); return 0; } @@ -123,9 +123,9 @@ static void updater_start_app() { void updater_on_system_start() { #ifdef SRV_CLI - Cli* cli = (Cli*)furi_record_open("cli"); + Cli* cli = (Cli*)furi_record_open(RECORD_CLI); cli_add_command(cli, "update", CliCommandFlagDefault, updater_cli_ep, NULL); - furi_record_close("cli"); + furi_record_close(RECORD_CLI); #else UNUSED(updater_cli_ep); #endif diff --git a/applications/updater/updater.c b/applications/updater/updater.c index 4c9fe41f9..e9bedc72e 100644 --- a/applications/updater/updater.c +++ b/applications/updater/updater.c @@ -34,17 +34,17 @@ static void Updater* updater_alloc(const char* arg) { Updater* updater = malloc(sizeof(Updater)); - if(arg) { + if(arg && strlen(arg)) { string_init_set_str(updater->startup_arg, arg); - string_replace_str(updater->startup_arg, "/any/", "/ext/"); + string_replace_str(updater->startup_arg, ANY_PATH(""), EXT_PATH("")); } else { string_init(updater->startup_arg); } - updater->storage = furi_record_open("storage"); - updater->notification = furi_record_open("notification"); + updater->storage = furi_record_open(RECORD_STORAGE); + updater->notification = furi_record_open(RECORD_NOTIFICATION); - updater->gui = furi_record_open("gui"); + updater->gui = furi_record_open(RECORD_GUI); updater->view_dispatcher = view_dispatcher_alloc(); updater->scene_manager = scene_manager_alloc(&updater_scene_handlers, updater); @@ -111,9 +111,9 @@ void updater_free(Updater* updater) { view_dispatcher_free(updater->view_dispatcher); scene_manager_free(updater->scene_manager); - furi_record_close("gui"); - furi_record_close("storage"); - furi_record_close("notification"); + furi_record_close(RECORD_GUI); + furi_record_close(RECORD_STORAGE); + furi_record_close(RECORD_NOTIFICATION); free(updater); } diff --git a/applications/updater/util/update_task.c b/applications/updater/util/update_task.c index f7b9f8128..6864076d6 100644 --- a/applications/updater/util/update_task.c +++ b/applications/updater/util/update_task.c @@ -211,7 +211,7 @@ UpdateTask* update_task_alloc() { string_init(update_task->state.status); update_task->manifest = update_manifest_alloc(); - update_task->storage = furi_record_open("storage"); + update_task->storage = furi_record_open(RECORD_STORAGE); update_task->file = storage_file_alloc(update_task->storage); update_task->status_change_cb = NULL; update_task->boot_mode = furi_hal_rtc_get_boot_mode(); @@ -246,7 +246,7 @@ void update_task_free(UpdateTask* update_task) { storage_file_free(update_task->file); update_manifest_free(update_task->manifest); - furi_record_close("storage"); + furi_record_close(RECORD_STORAGE); string_clear(update_task->update_path); free(update_task); diff --git a/applications/updater/util/update_task_worker_backup.c b/applications/updater/util/update_task_worker_backup.c index 1e8c2f09c..09e459533 100644 --- a/applications/updater/util/update_task_worker_backup.c +++ b/applications/updater/util/update_task_worker_backup.c @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -18,8 +19,6 @@ break; \ } -#define EXT_PATH "/ext" - static bool update_task_pre_update(UpdateTask* update_task) { bool success = false; string_t backup_file_path; @@ -89,7 +88,7 @@ static bool update_task_post_update(UpdateTask* update_task) { progress.total_files = tar_archive_get_entries_count(archive); if(progress.total_files > 0) { - CHECK_RESULT(tar_archive_unpack_to(archive, EXT_PATH)); + CHECK_RESULT(tar_archive_unpack_to(archive, STORAGE_EXT_PATH_PREFIX, NULL)); } } @@ -99,7 +98,9 @@ static bool update_task_post_update(UpdateTask* update_task) { string_init_set(tmp_path, update_task->update_path); path_append(tmp_path, string_get_cstr(update_task->manifest->splash_file)); if(storage_common_copy( - update_task->storage, string_get_cstr(tmp_path), "/int/slideshow") != FSE_OK) { + update_task->storage, + string_get_cstr(tmp_path), + INT_PATH(SLIDESHOW_FILE_NAME)) != FSE_OK) { // actually, not critical } string_clear(tmp_path); @@ -129,10 +130,6 @@ int32_t update_task_worker_backup_restore(void* context) { break; } - /* Waiting for BT service to 'start', so we don't race for boot mode flag */ - furi_record_open("bt"); - furi_record_close("bt"); - if(boot_mode == FuriHalRtcBootModePreUpdate) { success = update_task_pre_update(update_task); } else if(boot_mode == FuriHalRtcBootModePostUpdate) { diff --git a/assets/ReadMe.md b/assets/ReadMe.md index 09ec4cff5..2cd99d56b 100644 --- a/assets/ReadMe.md +++ b/assets/ReadMe.md @@ -38,10 +38,10 @@ Good starting point: https://docs.unrealengine.com/4.27/en-US/ProductionPipeline Don't include assets that you are not using, compiler is not going to strip unused assets. # Structure - -- `compiled` - Output folder for compiled assets. -- `dolphin` - Dolphin game assets sources. Goes to `compiled` and `resources` folders. -- `icons` - Icons sources. Goes to `compiled` folder. -- `protobuf` - Protobuf sources. Goes to `compiled` folder. +- `compiled` - Output folder made for compiled assets, after building project, in `build` directory. +- `dolphin` - Dolphin game assets sources. Goes to `compiled` and `resources` folders in `build` directory. +- `icons` - Icons sources. Goes to `compiled` folder in `build` directory. +- `protobuf` - Protobuf sources. Goes to `compiled` folder in `build` directory. - `resources` - Assets that is going to be provisioned to SD card. -- `slideshow` - One-time slideshows for desktop +- `slideshow` - One-time slideshows for desktop +- `unit_tests` - Some pre-defined signals for testing purposes. diff --git a/assets/SConscript b/assets/SConscript index d5465534d..ecb109567 100644 --- a/assets/SConscript +++ b/assets/SConscript @@ -97,14 +97,13 @@ if assetsenv["IS_BASE_FIRMWARE"]: "#/assets/resources/Manifest", assetsenv.GlobRecursive("*", "resources", exclude="Manifest"), action=Action( - '${PYTHON3} ${ASSETS_COMPILER} manifest "${TARGET.dir.posix}"', + '${PYTHON3} "${ASSETS_COMPILER}" manifest "${TARGET.dir.posix}"', "${RESMANIFESTCOMSTR}", ), ) assetsenv.Precious(resources) assetsenv.NoClean(resources) - if assetsenv["FORCE"]: - assetsenv.AlwaysBuild(resources) + assetsenv.AlwaysBuild(resources) # Exporting resources node to external environment env["FW_RESOURCES"] = resources diff --git a/assets/dolphin/ReadMe.md b/assets/dolphin/ReadMe.md index f69592550..6b59d231f 100644 --- a/assets/dolphin/ReadMe.md +++ b/assets/dolphin/ReadMe.md @@ -2,7 +2,7 @@ Dolphin assets are split into 3 parts: -- essential - Essential animations that are used for blocking system notifications. They are packed to `assets_dolphin_essential.[h,c]`. +- blocking - Essential animations that are used for blocking system notifications. They are packed to `assets_dolphin_blocking.[h,c]`. - internal - Internal animations that are used for idle dolphin animation. Converted to `assets_dolphin_internal.[h,c]`. - external - External animations that are used for idle dolphin animation. Packed to resource folder and placed on SD card. @@ -10,7 +10,7 @@ Dolphin assets are split into 3 parts: - `manifest.txt` - contains animations enumeration that is used for random animation selection. Starting point for Dolphin. - `meta.txt` - contains data that describes how animation is drawn. -- `frame_X.bm` - Flipper Compressed Bitmap. +- `frame_X.png` - animation frame. ## File manifest.txt diff --git a/assets/icons/Archive/ble_10px.png b/assets/icons/Archive/ble_10px.png deleted file mode 100644 index d7ebdcf8a84e6c7870c858689609c36028f725e9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 301 zcmeAS@N?(olHy`uVBq!ia0vp^AT}2xkYHHq`AGmsv7|ftIx;Y9?C1WI$O_~uBzpw; zGB8xBF)%c=FfjZA3N^f7U???UV0e|lz+g3lfkC`r&aOZkpafHrx4R1i<>&pI=m5)c>{NDU7b)b-fr;B3<$MxhL^$id2Ft!!2 zx2@#md47Q71Vd3{(|3LbhMnO&{!uGi{{hvimbgZgq$HN4S|t~y0x1R~10zFS15;f? zlMq8AD`NvIV+(Bq11kfA^%ccmP&DM`r(~v8LNyoz^@JFhSeaT{nVLW}IBimk0BT_H MboFyt=akR{016vUy#N3J diff --git a/assets/icons/BLE/BLE_HID/Ble_connected_38x34.png b/assets/icons/BLE/BLE_HID/Ble_connected_38x34.png deleted file mode 100644 index ed5514df827fb0e917789ed422ff57839899d0ed..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3694 zcmaJ^XH*kwyB&&3k=_IZLJ*Eh2?<4sgkFMDLkl8e2mwM!F$s_$Ac`o8G-*<$ifF)w zf=E+QK$@T+Qlv-`0Rz%RiZ`C`c)t7NdS|Vfx9z>3{l3pLYt3ACw6_)#kP`p^K*$DZ zf#S4koEyx?!+G*jJ+AODT=rsUyK~&u2WlXGqo1~Oj4$`&he6Ij9H+DRKf3?5qmDuBPt|tzE zRT?UN6cNUrpWk@DQ9GS8z1hj^5{VKMS4*BM3 z_S}ERO8{8lVKO4=_l!5W?vep+fYPhAPrP~TXLOCDnmYhW21dc+(}G+{D4<2lr9~2G z*#q2je&{Ozqyj(?Gu#Xe+!g}nmTio70R3etLoz^r;odSnAf5}z+;6>)+vF*5%Jm#d zgS+uLkk2v!YwS;CW?6ljxB}?dj$jq=T&_Q>a;(_8U555Bag;K zu5wnl2g4SC(^)2&<&8zrTc_Z%qOU13+2|daq*e6utlZxE&h~HA%Jz!q0#>Fu19S_jgeouxZ{M;Jt2TeUG!!-4IT5v2pkPnBsjN#Qyr1{cJs!Dyv8*xC%4g8SLerz-C(g}M zWOkm~llOd2?L~fG(-uAnj50h{%qO8A+{q`CnZOIiAa$=r6@zACC7r-8Td2j{WoCiN zUPzU1np8=H_!A~Sk_KBquq&HaYOSHE=N>rmyyFfwd2&lh?#iotS~p!+#49SjkkBnx zsfdVrTv*zD>~k<3h`5q~jp~L9`(6EjIdS!p<*CbPIkjeK>?B&xEm04vfIbu=CEu8b zVe{?dj*F6*<2!jl^m)+vq$F#!E!zB+>YPfYcnzOBfAj_D^S{6;w;U=}!^GE{7(Vd( zNoXsU+E-;o*FF!+#jv{SiLsPl{_kEXHa{l36*zw*fPLjz$0 z^#eZh)OqRo*#UMiCD$ZZBX+zHEZ9CHK($`qm-HhCrZYyr@SvGwqW;JGseeOYy z^6uHYv*Fook8hP{mp6rE6GtS+B@++y99VHC4M68M=LF`m=622-tYTJGSBa}#3hjHv z75a|QkAzd{!R;YIl-k+iG2ItsO=T};-2LVK3uw7ZdNMbUCAEoXgk;D#6HBj@l5T{x zrA^9xt?7C6cBLr$Y4%Y02jaWocErnwI~#Ud+48MkFA!|g<%Ko$`04gfxuw~q&zF*> zAtysldYs&-Q*?Nkqn6jFO>7x#@*J&eJ97$dguwmioZ&HgWyF0W?8c?~oAuxkM7>I6 zUu0(FuFbrSBR|@<62CW$F~3PK+V42U^^9whtL~iKxraOi9wpvP-jZEgi4yPB1@jjHT2N)ND4n3b6q^+f$?NC$jlM4$6>ORWTznU?Lu~pgJpeTe4*3*s_LVL*QRpa z(pC=-ABiyk5pdDLD)^NSzXM90nJjzliOQ0ln?>J8R@aBqA2bb8b5e7PP(t|hO7-{J zRr{+P{M6M`geI6%#MkmEmtL<;sI1eL2Bm^xY`cppQ5BBD(zcLW>I~04o{VZ_9rE}) zoe{t7FY79T=cU8zlI}TH-+S0!&G1+p1vgkel~##!mI>8@dZB838Tf^sh_-;X2kBdm z<7kxYw?O95w**vlPf3Zn`gwQVw6fIJHd4j%@h4a*Oq#DNJ20cy)9vg*nKW+}g>|dq z`#r1bsd0CE@80kgCUIck-ZvK)gv#X?<0s|4iAO)u z3&H|d5;`h7UU%^5Vf5xb+aI$AzZK{gt%l{E^=fn5D!)@z|3Ph_uVFa!%#v`lg4B_Z z?9{p1N9qiBLY-yA3aRJo`Qb!4ANiVwsmS4K>rjW6d5LFA&g@#XWX}fD$w5PR8yX() z6H)r{ibyzRGCPVqkUgIj`iOG;pzw3k$!C9!+zdbS$?^R0vmg2@4vs-^18vSW51e{uOg`p@2ys?S@|BaIxiC{ z@YQz6D=d+5ce8$A>&vbmB45r{Zr{f>5=XzEYCP5T!b5~D%zr6wJN4(=OJhCO?bf|2 zRx0s{xk^g%LUMKAd^=OdN*|O;biIyr`^lW8-I*TQ8s9k9h2HF65L}PzYN%kAEm7Hb z%I?CORhpG*JDG^Zo15jGgAwd%cFjn0ol@rhO_OcMrS2ct=F&F~Yev)I+nbQJ3ribO z2C8D5uS!|EC7SL`_M?Yls5rpXm+XTB*$^@QI1~=!%RJqRGXwx0WxNZTj<&OfW64A< z%rA@HP#TOY1t0Q%`F$_%-P35o%miDhL{RZ0E{r^xR@n1BJj>7%d z@Bb-Gb74|&5EPC^4yIx`iStwW6^a5kr{XYlGS!7l4*GqHjsavknHE5%fH;2;``z&* zUvenzz~68?JGc#rM#qq_I2#KDm?NNt$NR!{55W%WnweVand_KAp_b-mW>A==j@2Pc zPHO?x)&9-3AY+4xI1>Fg*Z03%t3PsoRRWR1iEM$R;zMx0R#Y+(^!JY8_&<9w|D)bN zT;D%?vG^kw!Z8E+mDm53=igJD;`!D6w`w_qf9oDc;uJfTQ`MdM`PVpa#7;+B7fVk2 z^XJc!4>yK6z4JB}rY?*D);m+-C~ZCqr>X6X5R6dC)m4HoZn-4Qj6XhB?17ln@H2Gs zB~0q+F_+Nmb~kd4C~94VvGCE zLzV{`dCoNp#;x35zL%^xAg*sv2mATa8NT=Pt%7KR@lFx0oq#Bf+p{$g`;_Abu(7na Ks4zQq{(k^B*?y@2 diff --git a/assets/icons/BLE/BLE_HID/Ble_disconnected_24x34.png b/assets/icons/BLE/BLE_HID/Ble_disconnected_24x34.png deleted file mode 100644 index 6f135c1162b8fdd943e46c617e16e6e855349b16..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3656 zcmaJ^XH*kiw;q}TN|6=;kq`tMkc3Vov`~VEVrW4{4Iw~)6q5i6DxwG$q)C%1O%V;) zP!MS<3P=+aL_jH0L`osb_85$Vz9u46Bc%8fMKTU<0sLb@OS6t&41SIWSx!os*hdTvmb$ky3 ztNja)0^P2FZ{L!KPLD|T5z%)cOHpjYRkNL0Iga(lN;^ciY?)N^%&CNZjWXHL8KCw@ zg)Z=R*Ly;yfm3N_sYNw;;cKVxl%Qg4%xI&a1)Q=ts=8hmac(VUZ`+{m!($q(xJxw>E{KTAj}(K0IYYkmS$Ub)fh`!Y#IvNA)2#2*<9Ya2GJ$>ph<+z@Du=dr}s}(lz#Pg313w+=YG<$YOS>fuNY(^VXSHdGKIoH2U zp=2jA>}hU6+o5lPOyKO*7(!SZT-^KGC;YK%m#j`+!6~TM$q|Nedakj01SOn7kgQ@& z7M?4-i$5w%dP4a4Mah=|7vkb z!#JEZM}10Vr%IOE@!jImClb)R#2c;;*^8>E#cA7avGqsypf&TI@=e+$Nf{XR@i=nB zmm%RtVH|Kud}*9SoWu2%hF3$SGKklg2QebKyRi9*QU~>jro)1_ZfaYkX5T&BT$uGi z`0i!I%_Fwt>>x*|)1^Xb-M}=p=5#q!W4>ly$GbF=4{6aCI>sy)`5eB}D&<{DyZ^V% zW20l?KZ3W4MT&*jp*{D=`^%q}FW6ox4^73~7J8qOTJH?|sI8dlb3@_=)djb2Vc!|q z__tC?h$Ppf(4^73&MpiW(+>9CEzb8`B<>w|J$u{Q<+0<}LXi)0_c{udT%H%|7q?)8 zv9XS=hfm~HomjiDOb~k%8DX6riOzE-l)jOVR*`$7O`O45bkbb~#x?wJ*m)j(v^0SU z5?WCVf}X}Wp%{0)Io|Q!nIor~`NZEm?4LIJlyR^{rA4gew5EWjspi8@zasCV&?1BG zknXB(&q?~E+~jySH;|TTmZ_0Bk02u!14YX9%MX`Fl=lR-em8kf7-x@(%)gx%AN$;9 zGf)1JHxK&G?c}y88^Cmh_2c9N;D&_hrZ~bk{~M&Tw&{xlceXY9mdDrn=C#@8?JiTF zy5#d!j|DRE=1_Nda&*5Cd;N{^o+Ry_{M!82Lsfg56aCY?%iBucGn<;~HF`N+oQ8=Q zqmB&!EPU0cik{Wzh+9?}uMbA)N7W^FC!c8)O14X0&CyME-@b{-qo7$3a?O8CFX9GIHGR!2NH2If z7dHw$9(3IO_)6tYhsPP}S)DL)eNXMt{>sMFClMw{;&RKFfXN#Z{+l5;FIC;Hg7hJ) zR6{#MQ$sheX004pZd{A~Sv|m>m7B2_I?4BfZnoFVrWpURk-*urbi4)pjvr3d+N z`~t86go*UBu(G>l=UJD2TFs=E)?#e_?57l%}r7;`C)9q&)eH$Mo zuQ?9lur9NH?B3ZJYDyj-|`DIG^$&+P*xo#4Lur3=JbhAIvab})P?(Y6_u=MTZIC?yPoWfajufBh<($Bo(UF5V_k>X7Bu;K~w z!7t355Wo4D=91>O%>sIOy~(3ZPdPoaIr@1EA(`hq8eP|l?v+-3Qt$4p{v3RIPCQ&m z_P`fz;zY%h{VX^CN~^Q;l=i6$pJNp~70atfLO++;1UtOWiamYn^yURC?zkV58qj;c zy84j_8LJxi9rx>Adhb zn!UdrQ60$9%p+$bb1J#tuH|Ja9aplcD%}X>!oLy%>-1vXva&PxAZM0_E+l*{@G8j4 zv^Pb5NbO}WVpdcAJq~+R`k-2~OhL>kOzDZmhPf}Ju^!VEv>%W~&mOhXLq*79q)jMo zZ2*1$D0Q5;*f{BCT+IAoH-4av`~KXrUrh(&y#^6*vOgNt3@q6XSrqE^1ixC<yl;1HnQ<{?SG7>|e`J2>2fe(;o@>Z%{ZpM=**? zCxZ2~^fd`^U2U*|krrGZW~8mF0oI1W4WKXsC|pMq21mg35jsZTe=i8n8{Nwrfwi>$ z*B5Vwg!nRV}o{~iZ`EI}Xb2RYh-Xsi&Qxl^?^AU2r821uUn&jE`-9ly zMxuC8gBg4Of!o<3Y$*&Tokrou_E+w&P9W2Go-K)VQV`M0nocEy|H&9Z`g<&v zf7Sb!>-E=I46Odjh4O-d{;KQ$s`Kw9Ui17i|8y;H@~7{K6kfB_d0l<1Gs}(lMr?A# zI9qLOZ17B%m+wP)!!cV+b7xj}MuNE%02byS*tj``P0+{Y*mQdC5%NAIM<=V~^n%i8 zS)k?XGJD8uK1+;LJxKT?@`YT3=>yId36a$u#WuSyt+?8YZ?q~hZrM0XJUJfjcPbJP Z0CWd{XVb+|td> z!r9Q!)!EU_*wMws(bdJo!o+|9>De^w8xx1_nlc zPZ!6Kid%Q4If@-t;BcAwJAT*tP9aX7E?={yv$s97ULYXgXuh%S#y;)bk3Z4`I(gEL zDo#Ac6X;VL8)jK!-?*~N?xEnBkYl;$);9R4a>?)V@zdd$zjDh7ZjU1UBO9N5^ZXIn zcd_1?&;E~=;OQG24lZ}{n;KY@59`S(9uv$v#AT_tjn{sS-Qv%4+~)22wCUVusac=? zehW9P>P))3@>^6+N~5OW+2u*clDb#?-YQsocKh0D$9mP1r@!qwFn@)@R^EJvf2Yda zd^Y4h+W8`@T0%6Rd(Yt9dcwwCr9 z>7|ZE$NV*)UU9X(8>lZhy%OK4DfU3<&xrJU|`_&^l%9R(ICvh3?yAmMf^4@vTLwc)7$}%q>ZPV6 z8YLO0B_``A7#SED>Kg))xvrtPm65TPq2*?OZe>PB!^y=w8VaVyhGu4#MkeNtPOgTA zuFg)5CN7qa7Ot+YM$Q&yj*gRe^T@yync`IRoyQ=?(a_S=+0DQbsK^DINlFUEB|)ia zRw?<(rManjC7v#}N)@>UdMTMHR#3f;xb#kL=5=z}qC71C=v}q|pAgsobwCyayQ9xq wASLGM;uunKD=EQ+ji-l)!{9{42?m7&41&iP468#YZv(0HboFyt=akR{0A27>pa1{> diff --git a/assets/icons/Dolphin/DolphinFirstStart0_70x53.png b/assets/icons/Dolphin/DolphinFirstStart0_70x53.png deleted file mode 100644 index 7255f679cf518db01604520b574b6c32e20c1df7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 624 zcmeAS@N?(olHy`uVBq!ia0vp^Za{3x2qYM;JYE3eu%tWsIx;Y9?C1WI$O_~uBzpw; zGB8xBF)%c=FfjZA3N^f7U???UV0e|lz+g3lfkC`r&aOZkpafHrx4R1i<>&pI=QI^kG&ph|=GzJF7WKS2z5RLPp7x#M~4iI4f zpv}0sBUW~m#c&J!2W393cCw(b*UGri@WyPQ%`__?F#X=k0&mf-C&6>TeCdEPD!-m+JJalzz8 zucHMcEtclEmU~QDzKWOSn({nNZT6bkb3<2o&1Byy5g>NTlF_SnS7Y9+w{~w&)qA%6 zTUi{q=S7Uj_sw(Wg^Ji6_d1qzMC<4zRbSJ)$L*&kZInFif9z9Q(bu~F3@&N8voB~g zo&?5#YKdz^NlIc#s#S7PDv)9@GB7gKH89jQvJ5dau`)HZGPKY(Ft9Q(_*pI|hN2-i oKP5A*5~RUE*U$*00i@2{%Gd~^VcyJwV4wyDPgg&ebxsLQ05M(W(EtDd diff --git a/assets/icons/Dolphin/DolphinFirstStart1_59x53.png b/assets/icons/Dolphin/DolphinFirstStart1_59x53.png deleted file mode 100644 index 85bdb5d147b4c6cca6bc93f0b71fdb7b88edbf87..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 558 zcmeAS@N?(olHy`uVBq!ia0vp^)O>_%)r1c1j3A$?$-SQ3bL1Y`ns||Wt8PJ)-%sNJdJ^YQPI=IF+}3B>BYTX%#I?g z4;ZTMct9< zeb3+d8-FfLOL(_;al--z3q9!4Eqx$M60|IJ(VIbF+Ax6;dC^&BnM$Cp+* zeK0CM#Wl%$`68=}elrsq%FD9MeCDyNZxKD?+pLi*ygO@h60fAr{2Mdau5R<)GyPD< z1xED=fx_A+S~@G+-M7U(-g$V6@rsM?e+&O}oX_!|@O`s8F#J?YTq8l#m4U&}ayc;+4Y~O#nQ4_E4FnpU4KuEA1EY(9!PC{xWt~$(69CeFzBK>< diff --git a/assets/icons/Dolphin/DolphinFirstStart2_59x51.png b/assets/icons/Dolphin/DolphinFirstStart2_59x51.png deleted file mode 100644 index e1fd177e8647ada52b75709fb5916d99745353ea..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 574 zcmeAS@N?(olHy`uVBq!ia0vp^)dAqwXFy8GFUIOHC z7I;JgP2?5=Va7{$>;3=**-JcqUD=;9%JLcOndcs!2DI1E)5S4F;1$298DUXOPkgx3Cvz>k zxp-lcl&kKfD;ErGkLqrV;hvzM9AJ@n(sf5*oBGc(@>h}>8@?Fp;dM%BvJn_p%yJ>lJE?kuHn-Q6l( zgd--i+yicW|Zt*ZsfCUs!~(&zY9V-x$AwBAG{LZ<>WZ~&qMo}+kY^6G5UC#Kh_Hc zhNo(YYeY#(Vo9o1a#1RfVlXl=GSoFN)HSjUF*LCYfAvZrI oGp!P&!9ds02&BQl+{)O{%ESPo;gwHCEl>l4r>mdKI;Vst0CeWcXaE2J diff --git a/assets/icons/Dolphin/DolphinFirstStart3_57x48.png b/assets/icons/Dolphin/DolphinFirstStart3_57x48.png deleted file mode 100644 index 04466fd83537721b9aa583d61402ec2863bf519c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 527 zcmeAS@N?(olHy`uVBq!ia0vp^mOyO42qYMqbKXAyQY`6?zK#qG8~eHcB(ehe3dtTp zz6=aiY77hwEes65fIO>_%)r1c1j3A$?$-SQ3bL1Y`ns||Wt8PJ)-%sNJdJ^Yk;&7=F+}2W>IFmZBMt(r z53U}IPna83-=e9zwrT0!sdHN^gtu(ivdgM9R_C3%4hMT#`GUV%?Jl}8fhoe1Za=>r z+udMr^muhJ&tis(sS86~+C!b16%R59EwmIoG@;@Yr}qr&w!RC@7uj3pvh-fHI`qIO zVP^FF%A~pnQJfX~Uq(ATuW|X7zDseQulE!D?r&lA!sNh2`~JQ36=l&SHrfY zAiqG+cXGn)Q|f!X(rw>w6ypuldL5uzvR37X?w^AnxQ&{e|M5G$Ftjo>z40R0*b&V`T3{9*|4Xq3zU^sp2onyDDUaw7@~1L^x}TM!v+Fu z1;H%>!oot*b1ON&^9Hn^dpr3^PERe%4TsoY9DF`~>)zjtXERq^=-8?>{_@@h8Of&x3up{C``l@=#`q|s1+SXSALr=BDHKHUXu_VKa*w7@Am_8d@1zXd4(<85sO5mlH$Lkei>9nN|tXV4!Pg1kzw&Ze?s_ YWoiP^U^(ykXP^cKPgg&ebxsLQ00T(O82|tP diff --git a/assets/icons/Dolphin/DolphinFirstStart5_54x49.png b/assets/icons/Dolphin/DolphinFirstStart5_54x49.png deleted file mode 100644 index 28ee477f5202f69e9cbcf8a7a62ee412b8165bfd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1721 zcmbVNZEO=|9KU5RnS;$2f-c5n$FVSQ+-tjbJ6mVBj^CfwB?3}_ zAojnp;5I17az&&o+A%XTgJ1i;Ji7@W_ZgvQEP`0D$azYL_jc71L^-WS`|WqIF9tT zFaqz5$-uLc6gYurISj3T#g z6%vM1$7X=R@(kz1mIEqs5oZjUshkIj%)%7Zpl(|j%NMam64}T~qJN<-{(Of4oMTBU z+L+fDO)HwP?2cg^Mj-4DM$RR2TplVRMU9Wl3&n-@+yTYC zqW~M+6|Bf&9GM!x0jnBFK-SO`6j@B8*p5=j#D=kVuACQ=B!+d%2D%KxK|h6KW>i%X zmAKpOiMap`+(4!kuiHxlmnTkx=4MYk&U<9e>z=(IMDmbR+1dNb|GdB2RPl^~)IIa$ zCczmCU#>I6kZR$YjTF^|{_cev=co$GluG6rLU126uRzBu-SThhy`D<7(<4@6^CkF@c%n~(N{ zBb8-=*OvrtHb%|q+ zZu!a`)6D62+OJ>kA62(b&y1hH_4w}HH4kSG?;m}8{CLf}504)md+k(e+oksheyLrz z=!L^4ukZBN{q;FHoH#hq5LU_`q*&4&eH|GXHuiJ>Nn{1`6_P!I zd>I(3)EF2VS{N990fib~Fff!FFfhDI0P3H?z#v{QXIG#NP=YDR+uenM@oty!5+IMW zz$3Dlfq`2Hgc&d0t@{HMWH0gbb!C6bD9dN8XP$d_8Uq8PjHioZh{Wa43mg3o8;G!8 z&@N4QCTF1(b zVLCNJVPaP{Pv8#M&r1AhbM7R6_q!s)Q2o$%R~K(QwX^CCZ+Q;8Ppg@W3im!;C9}45 zTg3hcSbk&Ax%t!h$FX}GtvPF@>lxZ#MfgMttXu>PI@J=_h?11Vl2ohYqEsNoU}Ruq zsB2)TYh)Q>Xkuk*Xk}=jZD3$!VDPhCP7FmuZhlH;S|wbAfklXcrInGnm5~WVgKN0n Qa-aqVPgg&ebxsLQ0C@+)SO5S3 diff --git a/assets/icons/Dolphin/DolphinOkay_41x43.png b/assets/icons/Dolphin/DolphinOkay_41x43.png deleted file mode 100644 index 52cc98b1dca4bf82dc8ac46dc94ff2e98a0cabb1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 423 zcmeAS@N?(olHy`uVBq!ia0vp^nn0}02qYNJUY-*Gq*&4&eH|GXHuiJ>Nn{1`6_P!I zd>I(3)EF2VS{N990fib~Fff!FFfhDIU|_JC!N4G1FlSew4N!t9$=lt9f$?sa@Dd=8 zy~NYkmHjER7_Yc77mLb8pwN0x7sn8Z%dQs``3@O~usskzBgB(cC3jemZTXCr2*)FVYAVZ_P96h zw>Y{>TaHE@a+~_7_^Bk*?ixXje5v#V1F8BKo2vZSCYtQjt=YY*|y3XlPR; zTe4=0BBAUgyGXKq)BC=?-}lFRedoH)InQ~X`*+{J`}f@Uxt=Sy6Q;sK@kP*em2xo3&1fnx&ZWM1a00j1BSqC^Iu1f0;u98LV40{(I zr+P;KU}d&IlbGWju^Ry4l9;%DePpzdout(6Of%cM3*ACyS*q+v& zQyj>Dc0Oo+c6R;4dc|bsH%PZhZPvge5AL*^^gW|1N3h7{o;+o+ZW4ms`daV0T=>{PwwQ3#{*63HcgU1 zlPvJa`YA~WNCE(FR)`@OC=>=}md$nffZl?{0U4n8&Yl7R;5rwOcEEH$xBg4O#Wn)3 z#$8hjby0@T;Yz-5vJVXm3)AItV6u0z6%hZW~kN*fV8;*qkR%>JRl3699UQN zP{CPxWSJQkP}7p!mU_HPL$WOSt$e`J!qAA)NUir{LxRjfF*m_6aAs((e`m{Ufd&3h@I$|?>jp_-orz0+ z?Nx9J=(7Vn2G^W5`UP{Ggx&_N#jvzD^!H`o#x?2e-6OPf=d6nB?Mm2ajPAC^Fl{I% z^gCyDt3PM~IG3)UR#cOFaq}F($G;dKJK4f(2=`f?RNiWg4BL!7*g2y4__UhT)kTLZ zWjL^9BkKh|!C0v|c|?3F46&$~vMHDtAPlghtZGg?=YQ7XT@yOT#T_H!(B%*4hkk=x zli!&Dh`DPbSgQb#SiKgff0zqsOU)YvfQm1&r|&1pSTqO#fN@U5p+^S%yIx7MYeX_% z?X9Sh;U?@b*59?iMn}wW$5{9tq1&=4268qvm|oszk9g#BcBc=EmAry$3r`M-pN^QO z%j|HG&90TLh!W&AXcCYhC}E;^yT}d7!Ci$&=vvCPp52fuU!D zvzhV|gdvH(ep>%$e18)NadqQrg(*BK;>k&#_uPK^FYYVKUv0?o?PO|-I)|s+@#>T> z*@q2(ekZ^4_!mDWaQ6fn3mLYA_k_XA{ew$8lr>uHFrJ#=z}wM=kFc0eTo|`5PUEFpu1qN?aKmGaXUT$ zABXEWc{aD|?B<1^MByh1qJ+i9NrN15u} zW%p5Jpp32?o3_W5t@)We;H5)wr6BtA>hkKxst&d#d8NCRcb2?kwzf8@4X}IJ%`>s+ z?o6+2LeE7XsXg}I zI0gHRT^X2+mgo12GK%W_GpK`-Ba(3kyAG~c`}DzQHfDro(r0$eqQ4Wqt9++^@7UYA zM|^M35$2H)2Gg(A-c}J$tv3oAEMZpy(s@{ijxJ{rLy$mg*S_&Cai}^9IWcs}3XQT1T_;GxBS{ z#ZN-d_@8k+vtGH+>S?BGb`Ol&)L(aMsIn#41*3~4|7@S)(QVM>z7=%qa@D;m@F2EI zIkYDBSR#E?lp;# zbAjj~Q}5_9MT%US%-VU$gNG1>>R(TmSToNtSJishV~*?Ym9dL)1=n62>Gyoj@ONi> zdgHx`GZ|&!WrbxCftP=nET)PxCH4!>UKy+Ft;1d!_2r)QPS?mBOSeg35{t(N?!1th zm}+my4$yH#I>v`+N8S`qeWEvh5oCyi%36)*`&jOkSh|!EX=PG%=lY$Kk4wvr>D-ve zvP=H1{prYgqn{p8R!99BGz6{ihgk`7H(n@zv$Qkr`ONP4cuq?8=~ z04qIEYUOtLaH8-SYl8YlA@TB?)iLEY-*5d=-jNoaxh42woQSjq^#0+%Q?jQ5%gidx zoZf2;x@~=3Q{H(&I;1lG5w7gf)84W`$HgIVwaH6qw>W zeZTm}qoZXLBlaiGc89F6sC|8(kCnceoi>}!oAzO^I#xeCR_STb{WfYrxJY5~`nbYb z>aowv+d-Zyv27)7Z`yc}1mx_g*5~a0rQ4|7??G8%&MkJEMGs1=KC1ThRDTKx{w8v9 zuhfyx^GP!m&khILdsUj8U-9XhyYMMa-c_NzdLr~wnQ4GkeRf>%-C+LjCiBytOq%z= z!|LiMf>b=}`aUxZeLN$a)|WAx9`KCrbV#JuVEok|gZDy$M{!?nx4#~E-8PsOR=OfE zZN@s>g{k&qLvpD(*xQx!U#{h5?LD*Cw5oJFbRIrR^lL=M*=Ofu9l$QGja^6_%_rq& zXPwZ)zE2xqty*lPc{!hSu5>r*$kvl#&RLY6U2XpMc{0v|A9-g92lUG}2?RAQo%a+qeI_KYo{q(Hq_WkhVgSzuAR^@!dj6$i8>Zd(rD&;Fb z{J+o21PIMp3^)hH1wP!U>f8Lv|5Nm9SjpC7at(Fphf9r1$7@H?c@e?O*;`55?=BB_ znYNmCD<4(99+#!4s30s~xkPGD9L|4|FVXQP)NY$K?fYPIaC2n+c!$GA@50Wtn2zdV zR>3#M{DXprm`3GB#fo+ocJbauQG5US`Lg-)!Ny9(v;!OZTexqXKZ%X`Z>?5!CquS2 zpsSa@t%svkc5_aZa!eBkrZvrt89-o=0Rs}vl?*bc65PpnGJ(W8*G$#{0GIGG&jbA zIVj$XW;z97RN;h}!Vqe*O)QHZn#TAJsw_ zqK+cra1%oVgt3v4iG~RZhSWAjX&U^-n)~=N2|h&f@7NSh?7y+_|BJ;KF~|fajbTHh zdH=2goCl3b^Yx(7K?shZpab?49}+FV_u#KR`YUB)GK1nzCLLwasGvVZ#!&u+fVMUf zu0_^BK(xp(O$d^tNrs?FFfs&*(u8Tj2qXm36%GCyPx>G6{wh+=3?~WtZ!Q=VlBh{Q z!dxM6vKtYC)Y2qE2ymn;gh(c%wFzz{1R90{Lpcge|rr#OEN|LF?O$3J~V z_Te-WgVQx<4RoQLj@yB=urb-*-sTK#Yu|+e0N0ASv4Krs-^|UJCl^7xjD5GZ^J)14 z`;}gGHTq>L`Ln?{$bybIQvY*meV{|7=JxW1sr6xO`6tH}yx~BDJ77pd1KKXf(b$fa zLcE_%ux>(VuGgN;IRIwM<0NETUzvmXe4Ow4d+e8oy{QzOr!8a01Q2w5JcyUR+{OI} z62@J_f4nZ77a&DO$n$W8qiQSDLt(~O^59}TKh`v>bvkBjUe3C8-AvV{x*l?0<<23C zCU+bwB;v}AAW(!3Pj%slo7P3>uiW| zNn*5ocutk^p6i)ea(rzc95!VWc4YC~4qZD^OD>NV1k6R$s_OkD z%gBz;&4`SWxnRT2GfsN%tz-ve0olA~FthP|!#|6jA-NF$n!9x0yfSjzybiiwdfXfZq0$hySqo}Gu zzkRz|3}!Cf5GKnb#y>2^!xC!%WH&B9&7rC9|KoFrang{rV-sM65?BIuho=<2rvUNd zF1|N1_>EMTfgpEmxQ>*D%ryzfTSdJ=$y`bOq&T_v@g3tg?d_wU%D!CP;i=p!fVvQ% i6;=Ha^tp43R~E2RBrB%!7mLJ25h!7fFbR1r`>12&Y1 zG!X<1MNkj{rAQG010o<&eDPkr@BO&z%~~^a=A8ZQ{q67UJ?orz*>5i?p(+6YfTRP) z2FriP@o%u05dWEPsJR6IB&>t2tz8_ftwD4KEg+ao0)VK#T(?ME%95hl;1WsN)2es& zUIzID0M=rQwa9oE$Xo&Nr)6X%`Sx2qFz%5B&`kRB92+&)aGN1(X7HlaM11<{ynLF&a30gb>{5(!z zbp`7ifPA(ESWmdVTp-T-k-oiPT#X12k$_8*w(tYuGOcu50g$mEV7*nYO$g*Dln?8w z8!A0akLO?nfO>9PTgJXF19?u`E7i!avqK}ABMszxR(PeIG6CYF;GB$XEji4DkYoEM z08m}-e7bIIo-{E&Iy@dQLFRpHDp(JlrGd>f=LXlSF9`|*GW$DteTT=!dW7iB0(Zmf zLQ4+-eV#zj;Igklzj(fn#H;A#i!9?+i>-OrU0O`kw@7TW0!wAP#BkFO={>>M$?cTiX1!W`~A z#|7-v%zBJXw$Xf%HX=KDTz^g@eN8+iQW9XFajxJ0Nc16>Tp#;IK=7gzt}6nth@FRA zQr(yg$OIbdvz7oLrEWRVVz&U$mQnN(0IEMJA1p~#a(pfZ05%LgrfB=xs|^e?PlsJ4fvp1AKS^hPk;;&F+F>V@a|VVz7vY2Uc?g3wOY zimj$`j|z%A_k9Xy0>>^S5aK%FQURCV;SXLqYwLF&r>fJeNEpK*JQEQFb(~Rzf?9nZ zo+q|V@M4_YwAi83G7aRDNlErNN1Szu_Ow=oY?YXo_^H$IhV9^#66Xr-XxZh)D1Y?5 zB%B>v=Zof6%{=Wx+oj|Vr(m{8y|^;EUqVwmN#9|kLnx*n11WMVGV78F&T-V4N}{~@ zFe-LGjt$C4u1u0na=x|O-Pd`$SpHGQkh{9CSVrFn0} zZkpqY+0MP6rQwz$LI>Z@f2UX{K|^T6#|+;vnT z`K4?cI?XdJHf`dTyGNLZc{l6UM)%tu(zg$J9=mDh@xbL{srVbk+ufz=9#2Y<*2!M!)qsh5G3!PiMPu?gDG?;R6bX^tf}~1pEQTF>S1^aP3eYSb@D%Oe|g$5 zLS#iP0vhA!W*v4bfE|z=kUM^)O+f1Ty`fpuyPUo4n(dP9F%Th$IpkhXNLfHxY?)DC zbYE?s{|sYBab~KI7f#Q$$kokVKvU35;o?=uD%Yy`s{Zhf&t|U)Q>;nxg_jFblkYq2 z7bxEf7nDBpdU%~0MleG$WReOQZcLbMO(J{>y&BBvoIO2UvY8uHmE0Iq*y&KXoul*R zY~V*kl#*Eho3_QDZTNvS5Nd+=57zH5YAk9Rt=-v{8k!YQ)mibH+1lEoJHYN`znD%y zb?1cU;cMSj_pe@#zhRrxwD%(NVsm<5`cbY(x>NcR`>LmzC(Coe^DWz<=irp?6s5AN*ha`O?m~Y1zr!!oku9$+?T|a&4|cQa-$SqD5hSE z=BACQeyQqu_1C_E@O@^=v-~!@gc~{1`Z2dSySQOK zX##pE;*j^Dm71;24{~(!dSH~6{>B4CHQX3Kw3#XCNBg9Z*>f|&Yth%v*50fI51Q6$ z#rDKz#)_`yt?c>1T}%94H_ZB~IJaNKPvEJ*m_W@5)e{edLWMMhGlg%6B1A5WJlO!< zWTs%XNnFxfgONh3SNf{9OfVP-)XnNtcqx{v;G^~f)~e&UwP$m}nGUf*nONG*Wyi_Z zM`GuO?8)ai8o?@=O3TOOcka@^rT5vj!i{-^xun~}9`#-ctVx(lEWPx2Pk+cGMnoVx zgp4HoF8rMZ8SaqF-&lRoMn(Rn&qC(B$SU06g*ZM zpKRymMVfdcypxU^$6uArxNkmo3S{L1RdyaLraGz1eLbBW?`&IJaJj(sUL|L*$(8Y3 z&$LD3_j`_6{|GwcY#09AK-?L-o0Y6`<+0Yhlc!D32X@E1Bkv%n)ST3uLabzPdd2Ry zu*w~k&H=l3r$~;n#wjn=QqI0y8r513o9|a3$2)cwR$$9rq!b;YCA*^zC?AO8U}`Wv zZwv+l);}+6Z9J)Xv?l4U3+L{GUQU$v+z`0V_KBj_MK`5LJ-9EniWn6<+jWc^#J!)s z<}!lAdVCFG4SY?&p6a@B!+Q5gFT=Fb)E8W8`8S`(1OzP=B8D__n`VWx_vsO%;0x;uMoLM*#qotc#^Q=7XyrgoUJ z_XG2KbjU(NTSeQ;HX#HaF>|2x5xf8Eb!6dUbnbCquIF0W?aJDBI(2t_lc_hYE^aPvF|zdkFy&Qz1+mdEic6 z-Fn0dT%L2L@c z+TDe&3ui+LDfy*`w*Xx))#uEhM}Sb2Eq#r%hfMBmwJ`Zw^Bf4_F+zB1EtQ~Ox@ z+A!wM0ooL4i96$EQqKJ5G_|Lb_xkvckoxYhx8L9D`Ceny@SabrRZ?}{Z{cWHEqsqy zoKcFD_~JO=8=V++XSKF(?X&0)>CeY2*6)$(DMR1=>is&Nc}w%8#Lwofr~Z6>cDT#F z)xKNHPU~`Fu7-x1q-xDqVtdMP@%>`Cj+e2XKUq^@wrm&_)PuCGFu zPS3Bzp|m&iPn9Z6TO8Ak7Ql?eGf03Xk>*bVIZ*I{BrFL}WF2`yG64Xg6T$8{CeFzb zO`uWq@V{*Iq9}Ae8URepqv&`-D2WO3Cj|ymO~Je;bzo31(G={3aDq9}tw}+_m}myc zEqcE@Av%PzvQ&UVmAKF|nlo z()d^FFn1Q61jUlVXyFV3e?J1W{($+~{r^C}4Eb!(&J6zPfr=;F&`4FBWj-|#S77|Z~Uva~|l zA>nXaD@z-=p{*6l7>P8nMHwLN{$L%bVN5)gK>8Cqm>>H$7WIFzXln)u&!jQjX*BYm zDsTy+F==5zG&)G1FDPh-S1^@GiwxWOYmfd;*@najjvx{37&Hp#FOku~|3bjnm;g5- z8R$cdNH9YPf@nyBpolOM1c5Y!8Nu*GeS|*>{2NdFAMyS!QvM7-3Hl!{Xe5GQh)2Nu zA#hRv0fI0xBtY-ZhF!Nt+t_UF%^{Gms~>sSB~@O7}UbdTyA&FGYB0By33S^rt=^oBAM zwA1B{sCW7&n`q8KA04nG^4TIzW3#c9=wunG9+|PFv(8f@%=0j<(KiDvfqK`VoQ;LkP~pt+$%)8>6kbX zX<-M~8@p~sJoOcwUY(QMZ0Sm!;`(j<^!=4nQ)BVTfXGM#%>=1{bxU|bj4qy|IJyGE zEz8t%sm$BMBgvOKQ?83`6TJcf2F(@RMDkzmlN>6E6nVO1w~KIz3_04sXM68L#6h2U o&9w)I*cG{extCw5sjxCoeOv^&cwnyc*Wb>;cE3%zmEXz#06+BqIsgCw diff --git a/assets/icons/Infrared/InfraredSend_128x64.png b/assets/icons/Infrared/InfraredSend_128x64.png deleted file mode 100644 index 33e2cecb5feec9c19ee486d9ba949eb4abd96cdd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4219 zcmaJ^c{r478-J}KWEU!9#7T@<7|hsrIz)DrGG<{gi!qjwG)Sv8Th?qP8Cs4a*%BdJ zh-As0gpiOWebYIo^L>Au>wB;3ec$_eo_qP-zvsT+>v^x*AGZ_|kQD#`K*-w4%z^!8 zvTq|kF7|VVFToZ71WbvhruNpRrXVVfLLmC%0U&fJ$1%h$c|*!zYy-c~#bj{#VY+`L z09Iu2wTieGh}-}$7e&O3nq#gAxJXEfW?MNoMfD3<&~ZjKFg03 zKRzF}w79tacDrUadv>Rb(Y=o;DyE$Dp}-eFImGCp2jvs&teVG%*6F+H;RE70fQ%!ShdGU&0iMp04r-hY zjX)mD2&~TCP|m@0e}b^&WY+Qk!SQa1`;0sRW`>Dc2LRIH1WXT!cXEMjxbk4V_2VUH zsWDYn96&>RYG?ZIy&4i#sjp>2zATSVDoi%|KQzHetB4Tzr@`6jS&HITvs^Y^f&d`9 z+4Fo?$1rjB!}P=l!mR(&*XDv66OXbkh`ofLQ-wt1bQvtx89H{ZTh@8AJ}y?Ymxyz*;dQ`ZfCXiQMXuScx2 zS9gcQmVtmwql~hK!szb-7;Ya$pM0FYe2nP8;K0d%GpO#wj7$Ni`8rbyeo;WaZ7JlvosV!}JkJRp$2Kh0RisR2F6{rCZwY@TT5gh<76xQ@{5xY+5)d8#z8 z=l5O=8Fvd#r2D1n>cSYg^xVe>9#93nEo8)1o z_yt0fK*gOl+DaU51tG5OT&uB!r$jzF!S#kS$mr=^dD&|(^8$P5n)^JMX$5{gvK5E) znNJFedw%~IL_Io}U`e@#T+9;R zVb1GJ@dds!7eyNVqY@J>-E7=U?MPQGhbp%f;z*%a!9ewgTH z!BYOLa7ZCn>CA7!;%Aes4hy&4m_9C`pp=NP-fQh=HEabbax60F6(MHZD9%nlk$S?p^leJ#5IF5l{&MeOxuefh5fHHiQdEwALtytntwFPNsN&r3^H~<_=F^dPb`g>@7kr!~5pGn-piaqw7b*C6iu> zrJ_?^QX^7l?>RXKIve&g?(KCdb>3Hc#^wAS3+F%Vr%L$WNR{@N9CUtGqE+7I5aN(v z-+kh2Vcps9QCnD{N0(wO@-A5wI$T;KI}YWj(_ddn(*9ckL4Qa zYvG#or=@|V9(iq;CZATKj6YllyWEk8{pfd-Sk<$9apJCMyH9melTTrfb>VMS%AYTL zPa#944G1jC0WX&3d;Ex>9>$A^7%plmYM!oB=}hsb6k zT(bnT&{^$I?v`bhH3esp#v~>s5>$Fs)*Q)0@P(ZPfrZQk-bK_pW?gBWwBCKNN)j^`OKRM0@u zM37&|U7nUqX^{RRw~5sl@m9<1k$lCMBY9G83)Z1*b7(*`{!%yJm`DWW&Zf;=#}mFQ z8Pc2QB~*?g?x}w|QQ=4rpl_%Ru%_L&y=&uF5=vs9>JIxpp#^)heEl8# zu?tyM%&L1;k)fBrnXjaa(Zvt*FJ7H)8f?z}ESJ)P;4M#q-Vj1)YT{xH|o zo*Sa)j&x59*NM3)mj1|aCK_a758ZD!Q%tr!DE{SQR*ao_T|r#IiHDU{$MkM|$Z<(s z*BRA4Z@T4k$<89^r3Sy9gDN9Q=EhUSRa+Ocf%mNLcLDDpsFduK>_P`2Vp@gjTUh0h zN;`t8YO>HY;{)lHT=L~t8`FxLfvdxk{xLQ^g%u9v_QFy&(7URkr}m!;t+J}MI{8Lp zjIjG@Q*m#URCsOT1N*86e+*WIy046b>&>4@DPDJ!4pE1DI8@_8iOap`+kM&}rG2-b zbaQb2;>#HM67LY*d;7MjYLu&HYDr34JGuPp$){LJOe)TK$v3_Kl*@&q(y3=zRMy?{ zZx1Z0K1{kEKieO^#vlz1y+2m@YH{9bzG$Az+HkMGf2`Kmxc~K~WuY>;mADzXv!rA1 z>G@&4Yw?{Gov%8%kQn6Rsg5VC;V=1Gh3jEC7d+ZszL%9&*1c068mJ!)IkzeteNa;O z{Zh(8&12P2SHD{G^K0bZ&rzcZvR-o4^&cWet1Lt8nsXD*-9EQx-F#`@mrn5?xnE!Z zh@a%36?e!AO`XYNQiie?GeaIzPaYL+G@g0>+t{7(b5r&o^SfS*yyzUuxKO#qH*dvI z?M2rIu^@${JpKIIrH`?NIS0=iw5+TA5wQfH!UnY<6I^ria*pV)Y)(fdPZi^eb90Ux z>c7btVQkp!WcYcU@Tm1R?anonrhm4nJiF1h`hGUSW4VU<6};g!tW^1XnSQyxWd!y6 zgw_31lzIF{`=YB}IsL2cyl&4@--RvThW@~}-`=Ww++x%&ylplSUCweVA zEc+EL6yp+d$+s@$f-p%XX_2rC` zRoYT%$$fN-VvBrD7ejyL&Q4j^@cE^xrRuSkTKSA4J4UupAl#(d>R4GF@ccVK}2=ovP4G$ROC|-DwH3{R5cfezCjDR-09sqDf5}n-WZnidP zEQO?w`KhBGN}{r@0YJ|%l#0Rn;preRyf=}o4_VyCEi)PC|r=eibpDJ`eeehqP+-&VZ zrW6_;gjCmpVBwkwkTyykt_4FOG}S-|7+f0)(}uz|ATT%@riIo(f&RY0>}WI`0qtOB z@pmltNFVG&r&G~TXh=widWeQPh2{-~qfjU)3;{(TAZ!gtAcIWDghI%HioZOV;RCTW zB9%_0kU>8^V!S9pbbT-z>0c?3sJ6EMI3@@FO%$6lXefpXg{#A$B+}2k{?rbnJK+Bd z<6pG{ofuR+)Bztz38G=y`$16rWz6R8{|EZ1$VP*Z*KgEB#f<5@Bukd7c zGtt;xb0I%Om)&u^_BKxDKYsjRi_VRG5dgpmwKg+$3LRR=xO!s>Bxts_`=i)f8*9WF zfGzfYRW+%Ty?7_dNR-i+WpMR^ie4tgK&k0;{bSNxdN7b8sq%Rr@Xe0v3uda|FaUd6 zec&s0>-?x!bH2*B?5D6^o{J_y;&5lBHPF3D9WDcAlacJUs}vW|=$FHr_}B*yas;Ay z!+{Lm$q^Y*3t9p1OCZ(@_vE>?BmAvO&A^(iolf~P3m-y2i&z%Gd}wOPh$9G-CL{L5 zvJ^F>B0k-I{J1-?g2+1)dx#@(^AZ%We_Cm4yzCWRDqfVdefskm-p-iy2E>9+-?mUs zR^7yDLT@zf9IkYu1!!ozi0FC1#l-WP{FqQ9t0#&A?OQcn=+V7JJf5S*H&yP&Ey5b| zR}=w_J5D;Sibw8AZ#ig6h6}acuy`!RWhoMH62GpLqpy__yS}<1*R_#ALvp$P98nh#r64=5z3QN#DwVn&8S< mY4b5O|M z>l2X0SQ6wH%;50sMjD8d21sKV=r?HDYU?bwC{`6z%Ea7{YPA zw%3ra!GMQZcI8d$DgRp*Rtb* W8AOASiQHtM1_n=8KbLh*2~7Zp&vAYL diff --git a/assets/icons/Interface/PassportBottom_128x17.png b/assets/icons/Interface/PassportBottom_128x17.png deleted file mode 100644 index e4caa754f337785486825791569055924f1eaea8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 364 zcmeAS@N?(olHy`uVBq!ia0vp^4L~f&2qYM6>t}*EEa{HEjtmSN`?>!lvI6-E$sR$z z3=CCjK(#Fl48MRv4KElNN(~qoUL`OvSj}Ky5HFasE6@fg!Ib3f?!v%$w@Y{lkjGx) z>Fdh=lv$8VP2}V09VS4b5>FS$5Q)pl7Jub=>B1oktyHq2GDHxyxW z`q$d_C*8neGZ$Ofzw1#y=O_NNY5Dh`_soAuz6DXg5stX6&uUg_7QIe8a zl4_M)lnSI6j0}tnbq$Pl4b4Lg4XljKtxPPm4GgRd3?^%4+o5R4%}>cptHiD0QOV^d Ppaup{S3j3^P6n!epx1JuCa>FVdQ&MBb@0B*5S AEdT%j diff --git a/assets/icons/MainMenu/Bluetooth_14/frame_01.png b/assets/icons/MainMenu/Bluetooth_14/frame_01.png deleted file mode 100644 index 3b1073358ec1a8a9b2951127b6d2921365186b00..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 100 zcmeAS@N?(olHy`uVBq!ia0vp^d?3sSBpA$%1!{nlwx^3@2*>s0AOG4PewJ^$WX^MY xpFzy-gCD-I9)8Xvz+}`Avp^w$YsG`#@(gc(iMI<^9AyS+_jL7hS?83{1OO$qA8`Nx diff --git a/assets/icons/MainMenu/Bluetooth_14/frame_02.png b/assets/icons/MainMenu/Bluetooth_14/frame_02.png deleted file mode 100644 index 748a0d9dbeec511660ff3e6f089e8a8774e96a78..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 99 zcmeAS@N?(olHy`uVBq!ia0vp^d?3sSBpA$%1!{nlmZytj2*>s0AOG4PewJ^$WX^MY wpFzy-gCD-I9!@u4GHQrfpb)^d;=ylu2BuDl_U@@aV?nw-UHx3vIVCg!0Qj^X%m4rY diff --git a/assets/icons/MainMenu/Bluetooth_14/frame_03.png b/assets/icons/MainMenu/Bluetooth_14/frame_03.png deleted file mode 100644 index 7a90fea5ca6d61607462af399dff4e67b1fd7e43..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 99 zcmeAS@N?(olHy`uVBq!ia0vp^d?3sSBpA$%1!{nlmZytj2*>s0AOG4PewJ^$WX^MY wpFzy;gCD-IA5J%5GHQrfpb)^d;=ylu2A-3W>>FljdxLa)y85}Sb4q9e0QSWmBLDyZ diff --git a/assets/icons/MainMenu/Bluetooth_14/frame_04.png b/assets/icons/MainMenu/Bluetooth_14/frame_04.png deleted file mode 100644 index 8baed331116a74a24af45f29ba2443e91f7957d2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 96 zcmeAS@N?(olHy`uVBq!ia0vp^d?3sSBpA$%1!{nlx~Gd{2*>s0AOG4PewJ^$WY2T^ tpFzy;!yiP>{N+iDVdkFJwDbOd1_q&CN&lojX)i!pJzf1=);T3K0RXzR9s0AOG4PewJ^$WY2T^ upFzy;!yiOWtYuudJmH<$29bb+-|ZRx{*&-uXS%-uq}S8c&t;ucLK6T0OCWjx diff --git a/assets/icons/MainMenu/Bluetooth_14/frame_06.png b/assets/icons/MainMenu/Bluetooth_14/frame_06.png deleted file mode 100644 index 06013e7e95c3ea6a776c6b311caf2c85c17a6e0d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 99 zcmeAS@N?(olHy`uVBq!ia0vp^d?3sSBpA$%1!{nlmZytj2*>s0AOG4PewJ^$WY2T^ wpFzy-!ymrz9)2z(z+}{LCpSTJLDTzshR^%N&ll80Sc7zXy85}Sb4q9e04s|iS^xk5 diff --git a/assets/icons/MainMenu/Bluetooth_14/frame_rate b/assets/icons/MainMenu/Bluetooth_14/frame_rate deleted file mode 100644 index e440e5c84..000000000 --- a/assets/icons/MainMenu/Bluetooth_14/frame_rate +++ /dev/null @@ -1 +0,0 @@ -3 \ No newline at end of file diff --git a/assets/icons/MainMenu/Games_14/frame_01.png b/assets/icons/MainMenu/Games_14/frame_01.png deleted file mode 100644 index f471e0593b5767184ed9adf4f255c4dd5d648bc4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 96 zcmeAS@N?(olHy`uVBq!ia0vp^d?3sSBpA$%1!{nlx~Gd{2*>s0AOG7K`{tcbv^a2h t!+&n)f4pqpMHMzJYFb#s-eLHkli}J%kx;8SGb=$_Jzf1=);T3K0RVTV9MJ#( diff --git a/assets/icons/MainMenu/Games_14/frame_02.png b/assets/icons/MainMenu/Games_14/frame_02.png deleted file mode 100644 index 7f06f6c604f66be2283dd1abbf8675143711bae1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 99 zcmeAS@N?(olHy`uVBq!ia0vp^d?3sSBpA$%1!{nlmZytj2*>s0AOG7Ko93NQv^a2h w!+&n)f4pqpMHMzJYFe1X-eFkF>HL?UVV{R^hve21mq5BbUHx3vIVCg!0LO71wg3PC diff --git a/assets/icons/MainMenu/Games_14/frame_03.png b/assets/icons/MainMenu/Games_14/frame_03.png deleted file mode 100644 index 15c412be5e31d6fb3b313179c0bed66d4814abe6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 105 zcmeAS@N?(olHy`uVBq!ia0vp^d?3sSBpA$%1!{nlfv1aO2*>s0AOG7KJLa8Fv^a2h z!+&n)f4pqpMHMzJYFe1X?tG88?WMQGsd9rW{}~t-R|xHvIk_36m%-E3&t;ucLK6V8 C+#zcK diff --git a/assets/icons/MainMenu/Games_14/frame_04.png b/assets/icons/MainMenu/Games_14/frame_04.png deleted file mode 100644 index 6d539b5c97ba7b2390594afd042ffb715b397a54..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 104 zcmeAS@N?(olHy`uVBq!ia0vp^d?3sSBpA$%1!{nlzNd?02*>s0AOG7Q{*-BBw&D4` z-{8spgo1cRC2cKs03cL1)4E%@x>GS+F zm$(z&aH5N=ZIdidrMbj1{X~uX2Lt{$Km0GRz{HSSA+&o_w!=!08J@0wF6*2UngBC0 B9?1Xz diff --git a/assets/icons/MainMenu/Games_14/frame_06.png b/assets/icons/MainMenu/Games_14/frame_06.png deleted file mode 100644 index 6753b7a24e9b61da795907dc823a44dfb9bac575..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 102 zcmeAS@N?(olHy`uVBq!ia0vp^d?3sSBpA$%1!{nluBVG*2*>s09pBp?{*+;3=WF{L yFYzhgV8?ofiK3!Bsn!lXFTEvR)f@cz&+Nib>?(Bmr*hV2kO7{qelF{r5}E+(+8x{g diff --git a/assets/icons/MainMenu/Games_14/frame_07.png b/assets/icons/MainMenu/Games_14/frame_07.png deleted file mode 100644 index 597451e87d9dae74250a26a5215eaaff94a3882e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 101 zcmeAS@N?(olHy`uVBq!ia0vp^d?3sSBpA$%1!{nlj;D)b2*>s09pBp+J1(UgSQHq% y`G06aee=Ou){ZqR5;X2JCY`B2^x;3h03$=iDWTWCYr0rL`aNC!T-G@yGywo2iywUe diff --git a/assets/icons/MainMenu/Games_14/frame_08.png b/assets/icons/MainMenu/Games_14/frame_08.png deleted file mode 100644 index 2109b6b0fa8e6443e2146ee834fb1d5d6b2fbead..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 102 zcmeAS@N?(olHy`uVBq!ia0vp^d?3sSBpA$%1!{nluBVG*2*>s09pBp+J08UuEIFT0 z@F3yF|5nBSY|X#91R?_u2JB-#GUI>C!~gOOm6<|jYM(6cfei3;^>bP0l+XkKWjG-6 diff --git a/assets/icons/MainMenu/Games_14/frame_09.png b/assets/icons/MainMenu/Games_14/frame_09.png deleted file mode 100644 index 65f1af5d480744099e9b3d58cd34be8cce56a85a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 100 zcmeAS@N?(olHy`uVBq!ia0vp^d?3sSBpA$%1!{nlwx^3@2*>s0AOG7KJKpVOT=+$j whfjv*_kM#X_Y(@@8I`oPBuzopr0K8xwY5)KL diff --git a/assets/icons/MainMenu/Games_14/frame_rate b/assets/icons/MainMenu/Games_14/frame_rate deleted file mode 100644 index e440e5c84..000000000 --- a/assets/icons/MainMenu/Games_14/frame_rate +++ /dev/null @@ -1 +0,0 @@ -3 \ No newline at end of file diff --git a/assets/icons/MainMenu/Passport_14/frame_01.png b/assets/icons/MainMenu/Passport_14/frame_01.png deleted file mode 100644 index 97da34659190f683f367b0734f9b7fb01a720a0e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 98 zcmeAS@N?(olHy`uVBq!ia0vp^d?3sSBpA$%1!{nlrl*Tz2*>s0AD`PFmh*C$ZZNp= uKf&W!f(P5=rw&2Qir?6qlUY3WG_o?pF68?r!4;$r((LK#=d#Wzp$PzYqZ>B> diff --git a/assets/icons/MainMenu/Passport_14/frame_02.png b/assets/icons/MainMenu/Passport_14/frame_02.png deleted file mode 100644 index ecb66c5c926811117412ea961b603504425c9b0e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 81 zcmeAS@N?(olHy`uVBq!ia0vp^d?3sSBpA$%1!{nlgr|#R2*>s0AD`PFmh*C$ZZNp= d|3Ify1jC}s!gB9dKY9dG>*?y}vd$@?2>{O+7Uci{ diff --git a/assets/icons/MainMenu/Passport_14/frame_03.png b/assets/icons/MainMenu/Passport_14/frame_03.png deleted file mode 100644 index 7164af227fa57d5ff3ca01a5744826ea1c428e3c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 86 zcmeAS@N?(olHy`uVBq!ia0vp^d?3sSBpA$%1!{nltfz}(2*>s0AD`PFmh*C$ZZNp= ipJ8E*!76uGULA%7%0mC%ZAw)GY4CLQb6Mw<&;$S?uNLJ1 diff --git a/assets/icons/MainMenu/Passport_14/frame_04.png b/assets/icons/MainMenu/Passport_14/frame_04.png deleted file mode 100644 index f28f77e257261f427ffc55ce5258df66bc59a094..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 89 zcmeAS@N?(olHy`uVBq!ia0vp^d?3sSBpA$%1!{nlf~SjP2*>s0AD`PFmh*C$ZZNp= lKf&YQ!3q3sUstSXWM!D^E%eCr*nfVI9#2<4mvv4FO#rE28Y%z) diff --git a/assets/icons/MainMenu/Passport_14/frame_05.png b/assets/icons/MainMenu/Passport_14/frame_05.png deleted file mode 100644 index 7d01eb34597f089bd05a20d2a630ce2e3ad10efe..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 89 zcmeAS@N?(olHy`uVBq!ia0vp^d?3sSBpA$%1!{nlf~SjP2*>s0AD`PFmh*C$ZZNp= lKf$AI;U{?oJsHyt3=FgO3mtmq$*=>Y$J5o%Wt~$(699X$7;yjq diff --git a/assets/icons/MainMenu/Passport_14/frame_06.png b/assets/icons/MainMenu/Passport_14/frame_06.png deleted file mode 100644 index c445fae0ab567958b02cc113694ed458134b735a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 94 zcmeAS@N?(olHy`uVBq!ia0vp^d?3sSBpA$%1!{nls;7%%2*>s0AD`PFmh*C$ZZNp= qKf$AI;U{^XpY{@u4svMRJHW&+ys0AD`PFmh*C$ZZNp= tKf$AI;U{^XpY{@u>J2RZF?5=5U|^WmFBD=K&#)P!*VEO{Wt~$(698(<8-D-* diff --git a/assets/icons/MainMenu/Passport_14/frame_08.png b/assets/icons/MainMenu/Passport_14/frame_08.png deleted file mode 100644 index a16cee9640723949b0dde2a79cec4373e27e7fbe..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 97 zcmeAS@N?(olHy`uVBq!ia0vp^d?3sSBpA$%1!{nlhNp{T2*>s0AD`PFmh*C$ZZNp= tKfz;Lg2z8br>6-<-`JZU^G~Q?=4NOX6l`m}Bzy;?*VEO{Wt~$(698?f8|eT5 diff --git a/assets/icons/MainMenu/Passport_14/frame_09.png b/assets/icons/MainMenu/Passport_14/frame_09.png deleted file mode 100644 index c581a9e8ff7917d7cc110d0701c10d94a0ad0404..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 99 zcmeAS@N?(olHy`uVBq!ia0vp^d?3sSBpA$%1!{nlmZytj2*>s0AD`PFmh*C$ZZNp= vKf&W!f(P3qF`lRP4m~&IdHU=f9`Wih6etQz(>m%E2GZ^6>gTe~DWM4feHR-E diff --git a/assets/icons/MainMenu/Passport_14/frame_10.png b/assets/icons/MainMenu/Passport_14/frame_10.png deleted file mode 100644 index f217e9bbc991bf6190db1a489b7a06d52e14d652..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 98 zcmeAS@N?(olHy`uVBq!ia0vp^d?3sSBpA$%1!{nlrl*Tz2*>s0AD`PFmh*C$ZZNp= uKf&W!f(P5=rw*5z6u+@IKjxoM!OYE&bdi5q diff --git a/assets/icons/MainMenu/Passport_14/frame_rate b/assets/icons/MainMenu/Passport_14/frame_rate deleted file mode 100644 index e440e5c84..000000000 --- a/assets/icons/MainMenu/Passport_14/frame_rate +++ /dev/null @@ -1 +0,0 @@ -3 \ No newline at end of file diff --git a/assets/icons/MainMenu/Power_14/frame_01.png b/assets/icons/MainMenu/Power_14/frame_01.png deleted file mode 100644 index 382d7132f4efabf1fad9a595915447dd1e6d0248..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 326 zcmeAS@N?(olHy`uVBq!ia0vp^d?3uk3?!4(jyeG;mUKs7M+SzC{oH>NS%G|oWRD45bDP46hOx7_4S6Fo+k-*%fF5l;8~T332`ZAIM?=k_T^V zeFCx=OM?7@862M7NCR<_yxm77bm)M>fkPasG7%Co7dRQ&Cg^k|q%vrwag-)7Ff$~+WHyLs<-P*cs9NG0 zQIe8al4_M)lnSI6j0}tnbq!2(jm$y}4XjMftW1ox4GgRd3>2r{e~zLdH$NpatrAm% Xu?0j!1eetgpaup{S3j3^P6s09|?y))bSnuYpk$E zu{H6lP+JLe+a6vXR#u*`@)FzB4BpfqD%jWlu$hOUmR)eldwX#YkRhI~elF{r5}E*K Ciyo2y diff --git a/assets/icons/MainMenu/Tamagotchi_14/frame_02.png b/assets/icons/MainMenu/Tamagotchi_14/frame_02.png deleted file mode 100644 index 4de357a597fa7b70d8d684bf3b863b472c0674c8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 106 zcmeAS@N?(olHy`uVBq!ia0vp^d?3sSBpA$%1!{nlp{I*u2*>s09|?y))bSnuYpk$E zu{Cigm-98Ywi4#HCZ@Ju{5;XZ65s3-E$$!w;K;^M7$L~BwY8rYWQeD$pUXO@geCxE C`yNUF diff --git a/assets/icons/MainMenu/Tamagotchi_14/frame_03.png b/assets/icons/MainMenu/Tamagotchi_14/frame_03.png deleted file mode 100644 index c2c11f6105069c79b42a257876db16687e280fa5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 106 zcmeAS@N?(olHy`uVBq!ia0vp^d?3sSBpA$%1!{nlp{I*u2*>s09|?y))bSnuYpk$E zu{H6oP+JLe+Z$dUR#u*`@)FzB4BpfqD%jWlu$hOUR#I?^-%6kNAVWM|{an^LB{Ts5 DbI2cI diff --git a/assets/icons/MainMenu/Tamagotchi_14/frame_04.png b/assets/icons/MainMenu/Tamagotchi_14/frame_04.png deleted file mode 100644 index 43645f94e331ac1c1b468b6cf2a4057bf4abddd2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 106 zcmeAS@N?(olHy`uVBq!ia0vp^d?3sSBpA$%1!{nlp{I*u2*>s09|?y))bSnuYpk$E zu{Cimm-98Ywky0mtgJj=bP0l+XkK Da+)8F diff --git a/assets/icons/MainMenu/Tamagotchi_14/frame_05.png b/assets/icons/MainMenu/Tamagotchi_14/frame_05.png deleted file mode 100644 index a0040ebd1272d4a2c609c28a86a2c7c292fabae0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 106 zcmeAS@N?(olHy`uVBq!ia0vp^d?3sSBpA$%1!{nlp{I*u2*>s09|?y))bSnuYpk$E zu{H6kP}?0|o=9m4E-r~z_69k=2{-s09|?y))bSnuYpk$E zu{H6fP}>?_o}FwwtgJj=Gh>M%jEuAmX2zs3V@!>uWXYBy$=*ndeW@t2 zWz8Bw_N_uv;mZActbzR&&K*Zuq5>vLUC)Cn7NA$}Qt004w6El~FC z)qwqJ@p7{N`l=S10KktXBatU8kw_4YP9>5r5&*z=nB_piI?PHUR>zl3ts;Z&T2bvK zctQ52(Lv&I%4+g_qQ@iU9}G#@)$Ku}xnx^1A~|DXf^JIKsSDoVALN;me;5<`DDpeN7EU`+ucxrhC6D_pubb|zQO%LpOAKKj5^kE8Y9L%po14MaC z+~s{X6*+*lKm&s#3bj1101n??0bZaMlUA#_KVnP1pwz;6cv4e>nVV^ z*`kxd_ajB3GivNgr4$>KE5XpgF1#AvJWfvF1FD^tQb)w~@VoG-#^8Ft6ltws9g+7- zZvY@8PJ*57(xz{xa8YNcUQDU*IgKwh+}jGSu9I8SUHLR)0QkTN?A}s`l*j}f;|`*1 zJv=ne<#ARZ8Yu~Z4My)|p^)uC@2|Z@uu>7lF={`q0>EM= zweFoNFK3WP=!Y)m_JYx-dB!0ih-i7o8vxFtl)%`w5~F5b06=8~t35T5U9Q`wUdz3| zZue-Nz{YvK>!wPL^`@ex{O&>f>E{m@gqW&^cRZC-I}dqhET>az=Mf%H69(5iz7$5# zM1JCV)9X~Lg88^iT6p*3<%c6VTyNkMV|b-f!q(*LEV#s?l|ZeL;&uvFak>^z`x{u0 zqlMfeg1!qDaoVgR?pO<;6|xatWe&X?Tx^GUC-?$co}({w-Rz;jTXzODHC8es?JfPe z4C1EVgPFJa9wNiBhR9~k+RyuVv>PvKf}0vlpB+`_i+5{(rcfZ5-z4+&WC3So)QVfz zGbWc-G#v{W#rW1?ch6!T z*j;tdk(RJ2)>Olk_LS_D{Gtm#%hlNX@tVU&Rr|IJ$EBx5r*)>e3CUU}j*n99$8sKE z_vpr+GA(>iYX8J8B4@A8rBql)sHCM;X5qtxUKtN5k5%%M&y0#aV+jXrlHNM?w9lG< zPWsHb%oG#~mk4c+B&kZL?c>=;l4kCEl5CwN-5V|4jMdbKeodZ95lNvs;?zpju1LhS z@h2QlP)?9lgJ5&>vhv3B1RR$f+p)2^XC1BX9m-iIP55E+w+o=4kW9Z6dwaVm8 zxyoonUhV@JQv0~JQ;Gf3U7``sWU}|#J%$b6jB0k$Qs9ko@rA=556fohSeHWyr#OVH)9FF(k)l#c=~X<* zRf<&hx~O43zB>MD#noGz2p*w`A>n+vQ*wbm&*|dulkoA>&U^DlS6?qD&O%7IF43+* z?a9);?S~u5EQhpSbCMLP+$VG?GCImCq#c}O2u_o28f&SZI?h<}KJ&r9XN8qkl2$*L zGxB6!Z=O6KF?#=v&i%vb&e}e28(NU>?WVhp1nwtjdQKDs+9GX(NiSv;A#RX3r^11! zWtq&pRs4dK;SWRl{Yk?~1O0KWap!Yy^lQsn%GzxksOjgzCXm+@x81k>x4VJtphFxa z&ZuCMV3%F%YyMZ{YhsMxBZMEtLvtoKGs;aQOkzU{L#FErm&<@ zoe2Eg|CR^;2_M}MD5w$^5#|(b6hn)|$#g@LbeY|wNS_JRPgEjmJdFgkg+0+YuB&F4 z2fko1tY4v1VblaBI=|_|v2d0bt@gvfYDIcp7hg?m%q>NHWPKEv43J8Ow49;&J?N}o z4$GFz1&gV}6OFASZI0gk!$edqNAl*O#l6f!G5mh@a`hwyNVi^hu2ow(cHrg`$1_)^jr(kJ5O z_5wm!@z!gv=rYKG1fEvUlG_Eloi+GNO|w2@PpJ;5@f4E?PQ;pys5V$)e)^G)xi=+k zBe(VME!^Lp6RQ{daHljg+{#Hq4)>|L-~z1Jz}s(xe^O%ik?@n;1qLr~l&VqsZ1d-w zl8OSWmHjcE!Ds8*Lh4>{czzXdmttMsk?(^LI#&Y*AVh?fl)3`>ui*RCI(x)V0FQK8~=Ry-FpUm{p3MNxUPYl-WWGle!3@405q9?nf3Md8wc@^^i5JqWCQZ2yt3 z=EBVfUv04#m>NQQLXNlYHGNd1q5P(1SNSGZ4+z1BFW(F(_`uV9@Uk394syXXburZ} z%^`K&#nq+4_Kjh8|Ce$94fBzMBKLF*oc)e3VOz<=vmw3lq{XhAtOVB8K=7ZV=SLov z2F$p1PFxV7E>wszKJ=isqi2p)9qT;3_>!?$JTkr4>7`TZ6ZkpG7seNZt@vKs=E{4O zsYT_dJI&$Z>bA$9&sH4XX0OLf$H#ATaV9TqEa=`1 zVc#pI8E72Cfl6dB@pJ-U;!brXfGjC^62YE;clYydC9tocoT_9jj)B8i!`-M9Fn-4d z>`S4s(d-+lkuMGJ=1E|HTnQwy7eZm7vPJ0&f7G$g@;Y~fEQIQZLO-TXb> zVD1V=h9Co9IGcb%VBkT%l#5~DAM z9YVo_!Jxq*5GIoeW@>|}bP@y#gTWx0S`aNQ4Yq}bkDnI<@2lbEqxg#fMeuQ>lW7bx z)eE%4hgD{57v)HfY=j!sF&z&?A{R-cU;lnNIC(}pwh8a>cwA$JmEoQP<=e8G?11y7z$Fw z;N8exJDS6PK`Hnw@Va)7vmS!{XbaPZ?QWAL7}ldqX=~JWrDjIok{`yl{K9F`&jgT z%l9|d{r9ox{}u~j2LsvZ?SJ+9mx?_=JK{gX%ijDm{sb@f%+uM!eS@HLpM5a6PgrBo z+uPf0(XqZakiE=UqD-*9!`~9@gd0J;sFd|{{_$Su6OTiQ@qy}4Oz+SADC0eD@2z)5 z*UNjyq}l<%}`s$yMBoGI9%t(0vZw{+5Rq z$a_i(1-~%{1;=b5oYdU~+8-!0ng8VOVr^*?x?(Qh0upr# zk%V_*qRS%kE5$XlZchN~R+pT^YwQE(4=(Tz+VvKe9OU2z+B$ZHW*CaUXQvEUqHRz` IrsqTc1+$%)k^lez literal 0 HcmV?d00001 diff --git a/assets/icons/NFC/Reader_detect.png b/assets/icons/NFC/Reader_detect.png new file mode 100644 index 0000000000000000000000000000000000000000..56d3663eaa2666f68493fa3088f9beab1bcf0717 GIT binary patch literal 3771 zcmaJ@c{r4N`+r3CEm@Lu#*i&$jI|kKn;E+yjEtpaFvg@=jH$7dsBA4H*&AuGFO@Z+ zY@vjZvP(#E@E%K?#JuCYr}O^)`1L;5^E}`C{$BUzb1&b|b6rpTQAcYr;r+q@0EpSz zU|hKCVeX?Y$jf~nm0R%!0AWiq8hz9jjRw&fG=K6LA^?O8vt3Cz*Ev~}_3>4deO%;i zdzy=kAjk!EEmAs3RZ~ew(k@Qv-jIxQ(;iDpK822)INpSW$m=rBTB28l2L+~;?k8M+ z5O?+U=4AMT!1wj*pS!lJKA6sytgtJ31R4cJQ*HE|4I)JgQ3v;YyxiW}HaQJ3j}lg) z1CoLjeoDbx$~?eE1QK}|+$iuI;0fs$5d<2sS&cdeVmT6DQ5oJmkym&cnMp_N1tX;a z^RNU|9$;?86Uk0jbp>+xfK#2mK2yL^ZQzvJ#n1gfBzq$xoCol_tt7*flLUbF65}vH zfH6?gbL^@mfY$+}{q0-fz@RpuW9#m313az)8oMP$$^k)9K*u>QS_9w<2TpY;D~AAA zG5~3-Wq0Hshsz{qHMx_@C`Z<+z%8S^1hs<%+}$Au)w^xw4oe#nb`dhoA2 z?ptU6j6nWaU;Fs{lS-8C@hEG)jgQT5RvmH=^xP7<;xqa=(fUV?2@JFlz_>+zM`88jz`_nczLLg!T@-D+LR|eNxR^kF8E|>Bhs9Yk+p^9%1VsAV-UxTDc-xdc*3@4hR+jSbh>=V#yJ);9@Ye79Zq9v5<+xY}sB3j(G|M9UB-n#fT z-<~HdKB*|nMj=B!779c-XkmkQz&j4w6o3w>=fd$?!OItC5OSJX%++_U10zCl7IbQhio^R2Strt->sQoBhhkuZH~s z%LU-KwLphEqNmx@Ez^3_3e!rsq)y$aEPi zxQE+gkm!jjy;tq>$T_xPum0Igc-LiFTl?J7`zihV1LOkM$&bD;pY0h-Ip~=7*1fND z&iYH*7v5Ets9225<@Pv|x_s6?fqf)-*1kvS4tetZ*W}xAI!3Vh^w|uF44NBWvs&}K zOww!N6uT7F6a`HvuCOStXuL?oZN;quSY?MK(>XWXa^XD|{ z(zg`ru9*}S-h-p{?DX6#Tvl%Tn+Z&%2W{M8IttCI=clbiTpgEQ()gsIkf-lZ3nO|5 z7Nw@8;)`vHor|sK8w|@9^4WO}mUSMRQQ{Wuc52>g)}+Ml-RxP{A=j~d;e1)F31Y1b zQWTi0aIX%-dO^)!OwWFPA~M^7mOmcaF#G+$1?U!Ti}F2x%~}-CUm>c(KO)+_5Bbs{ zh=x>zwvYVbj6U9Cj?N{d5Yi^EB-QpJ9}HOCu#&h=Zu6c}nWD5&+8(I#t17A5W4~e- zGhMK?c^Y}f4#p1cU2nSXcfHRr$TER_(4Ebi&H9ivQFF>O#8a!fp!zA%wtU(%&ttK= zo*L~5_sGS&c^rfz zNIr6;nwMoQi^&bi7oJ_Xv1KA47CR(ftmvs25nRcTMjS(oXLjFNG`sL}HFXY3!uLq_ z4C+l)@9orRC9_?#`mITFq}t%iD{mZVSQ`ttctAW zg@Tbsi-wEj#n{oiZ}jhKWP9iZzisJ2e{`PdH50j;q7yh#65)h)iXv6bDnostN$#Z| z9@pfuiq;8Ba;D!*8F!~lr;tYDy5k-v-xxL4bkuY@wa~IpMo*wGDs%ieVVqtdKk^U{ zi8nsVBFbym=0@0gr56cih!Ntq@}UKb>p8U+G8T!KN-xbcl_~{v{C=}kX?-@mJE@y2 z9(^?8%lo$igwKR)EfJz>(Xr9)F=h$$IQ;%zSegN=cN85wf%%R}#f)c>U{3Hy+I9w- zrbUp`$YG;r`p?YTApRb22JmdW?HT(6aENA^A@$7ji609qv7gwVM5e;!(xDy4>7n@YK1rJ9mE{QvJly_ndd+hT@yqD?DDOon?a}|C2Vpv=_~28 z|GCZ*Vs!cTOX{^6=)n|$SAi8bOLlu`3sf9K6cWp)d=Z2Eg@N#5O=!o!?%sYPj@)5NJ_L&KF~6UQnS*1lPe zK-0R^N9rzDqiY|$Kt8P}=D+IhGB(3K#I54K;|v%3#;OP(HmJRe3gPwN^nV{1@v2|n zGJW-Y#Er)dJK#S++_c@SST`$+FPhs(-J~2Zb)*C>wDb1yZV4!hSAVE|H=x*Of^$iH ze|X+ny#2T5+w3KlwwR%q+l@PIpOopC(CN8#p^5c_Vu;~$*TYLD1^RzGr$|zSosW^Q`v-4?>yh6i?&u>uLhV*?ugJr5m->@Q%RSfGDeMaokUvLH zjohyepY`pYI4wb2dM3%7?6c%tn>DUC?)F}O%3LlVnc3_9)IIYN;w0rO^DA;Yx!?q?P6F%Rk;89oIbwBD)y5GUyo|G-1hzL&N4Y!XvD_A{Dt(7 z89`v$zB)QOAR{=0=tt1?(eo$xYJqfhbYV~!_cqdo86kA_5HL9C?+eUL$nf(=xL~aQ zPRI33zyVAq9RY>1SS$!j4?<&*pfETb4%N|x>gsB9Bea7re9Ltzk|ovwZcVzK`_ltTF@I+*D~ z{BOPgFL5v~lum@Y5QAxF8NS@J^H<(UMMt0+L;{n>z|m-Deh%_z0F6lt4xrINx_S^8 zNF7V?B~y2HH2#8Mu?SmgFq1&_CE8+4z+4gtne2zKFwn-<1Ycc#K7RoIKx^uSw=d6EjaRszv{v%$l^N;+ARIcV3TydkQgM-`;ga4=l z&XT)wIGo&}yJK8y7t_Vjl>^Mw=-6?+r?waiT*z?cK(Evlkf3>QCx^32O*o`}v$7O= zLoc5{N^(!zEvAy^^v2;N?D_L4B*T~5p4me}6@#favJF&P$70~$N+Z4>sq{OSKzO+{ zC^Io*QIEVcQfxSwTXHA&OH~GWUV$l>_^fcccLE1n)z($>w47$&FfNX(QvUvSI5`@2 z1n(7YC?Nn>F3l?3owvs((0fU7yZ^;*ukRD6Iy3uQlkMBTPo-serVnhg%x9bMR_dbE$YUqoR+cft54KK*>TSh_gWV?1@hyZ7r=c^I+{@c1a4r%UasV0jBL_zrTg0u83=4T@*N4|%@mre0KSXo39)@{kJQAa&*-(!rB+ zYfP?JIkIl-M7xypXukromd9*1DM!7*WZ$nI9bK58A2Djtwa0hb+&&;SU2Cw}_xLkV zHxq(CH*=hMbM$KszpzOLPLqNPj{uL+2Z^qKRI9kK(4ghS_kQ+b9 zurda@hRpQ&9Ik8a>t~$my{9YL(xl6)%kCU>hUy?&d{`!VPY+e$9=f@O;O! zW;V*y2D35gw6mVVi;YDvI7ZxZDEyf%6rrI$urrp57CV_s%qETAV;u`g`h2VPuSI z_R{+zSDvvrO;np=!{^g1N-Z9W;MQi{7Z>E&5}dkTh!=AfwF;MADrT&S+;-}F;lp$? z7}LO}%H*R9!k^VKz?tC8Mhx;nXC#$RjIpV)G2XLik`_ZryXI?aGZg> z#K)6yry#rm5vUe5$&-;Nm~{31V}>uDVFykQ=nZ&UN-WD4q$?W;OC)rRIlGs$z#qzk z&bNooXUsRxE6t0{i*4AmXEyF#=&$KruCKJz^CBL^B=vvnQocx(_ z%ZHOIj6b9;f+!=DewyVpQOM`?^AwX@p}}aOHmsr=bR}gel_!;KjgzaCyTu>h$)0GG zD3vH82f-E;<`zyBa#(L#cVXiSu3FtL)w5Qznk!)YkW^${m~nB%O2mp-pq?LINX#c= zwVnmq?ng4)Hk&k?qn=r0y|^}4+X~`v5~}c(7jx$-3cC@k(jxVuXY|%hxtf%H(VA#v zVL>(=rDUXJQ(?LJ&_#=7F2!s25zUNkNhU9OGcan3Z(Vj)RwP&1q#8=N>|U6ZoP;Yf zD6%NhU#U|qUCqnLt;5vV?gew}v>8cXmewc6^&ZbyvKqCT%wx|JFhwG^OTmTiIU?CL zXrFq|ytS0fw^xHQO~`puesxRV&)kOWWA{nl^S1Rlam*E*lFZ|ry{9$asd$k!L?LbC zUoB8qnzd0m_(Xj2%)R*PevPL?dcW)O#JIIyo|Bv-wUSz&N-;}`Ng11dFRn~fj+QDse zaMpHLD)dS+O3r<(DXGajkymY&U{;j*k=R`JwX&nKph~E0VT5_Sw31YL8&7l;Bv!pE zZC*>LOSt{!_V4>h7OwD7?jlh;(LnE)R6fKd#8g`EqcyGD@3xYbAw*msZ{LX0T;-`Q z*%r49tMoyAq9C!_J7hB=I@0)V7dTlHoG#Kj*W;*r^P&G?Kadx6j)BM+8LSg* ze{65p|CU&NtQKON@U47wRVOB^T8CdJ?rzE5g~k#w*Y-c|mx%2wrS!)4x^ahI4E+4@ zJqvyjAKe_tDIFRfY7?dvONqb<_d}CaeEKI)-qYys=p^)1IuU9Pf39GpBBxmhzOFH* z_D1=QRx8-WwtEPdfiv_lJ_85Km8yDryq*5Bx*0y3G0QO*AeaJaze4fL?rqu%%@Zg9 zpOi-=X`4itU3mB}9bUP7ftYg}r+m)EvimiOHW9@k{i^*DBdE)AXU#SY23(dGrxY^nxswW$7n8X?xkkrca!p@)xw`!gGY(1akr}TE zsYF#jt=D*6OUUc!?NQglKErNdzhhO`1}0zOhj%^u*F*wpzbXGQG;UwJv#;6lcHEl5 z+H}Zeh_Gk4SFxj28d4qcpfQh*!mf5VmsW%mhTQG5I6c_G7>Xx2ZH~ca2S758L;Hk zJAIvpy#9ulHZy=Zj9yZ&RqwsL@tU?#KE80u=Cw`QbHp{$7upw%gM>lzwwgyZX{FVd z-K*F9>s|%8>@169s`XB8)%krDIQ%%22}e%WZTgdU-tBBp3rq%5rT2TgYDRse*Gg*5 zYp-o-uj-7VCc}rc><=bJ)+g>Pfe9_P;c&2t6NfZE8LH zw!>EmdUfm4-fE-IgpcU@(`g>_`CFhnGKa2zzSy>UpSPDFl#p=9#F(=AV_oIpUHX5e z5DkN)S&?06K6okt&~YX^5hIR6HcY-^M zYiWWsd=Yd45`l&X`I0<5y%D}h@xOQxbos~(5eNO{LUTuo|0gM&=|vEVOeKKSz)%Gz zMMXuB8XQb;#={(yT<}ivAebTy3W3sZH3g^|0;Yt3!a;vu;`D%2XBUJeTJP_0bPXx) zN~2K_5QvYD57RJWT1XFV#J1p2FLgxfE3!RY;x z?{BR0uX9oSB^E+Y268mp|1;~KCi(&$iT^AwUHNDI37+(wr_z`Ew~Fsd^bdpWq6rpr zcz8$`>1W`2oH<=$q*Tsm~M%Mh)D^&N|OXu-`S#)@)Vzw_Dlc%^zw!%*SOjtlidA)aJ-e2z3&p@g@UHx3vIVCg!0Md~alK=n! diff --git a/assets/icons/StatusBar/Battery_19x8.png b/assets/icons/StatusBar/Battery_19x8.png deleted file mode 100644 index 1265d7c184a40bfdcd4500b388c3fcf0eaa905b7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 81 zcmeAS@N?(olHy`uVBq!ia0vp^!a&Ty2qYMct$tbpDG5&(#}JO|$r%YA2`m2ZXXuh% eAaRKO6eGi9DL&IxFNBLgYCT>3T-G@yGywpi5)>r> diff --git a/assets/icons/StatusBar/PlaceholderL_11x13.png b/assets/icons/StatusBar/PlaceholderL_11x13.png deleted file mode 100644 index a6a5612798eccf8cd845fa0e2c5e7ab0e7e0dcd5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 312 zcmeAS@N?(olHy`uVBq!ia0vp^+#og+GmvDOU9$p6v7|ftIx;Y9?C1WI$O_~uBzpw; zGB8xBF)%c=FfjZA3N^f7U???UV0e|lz+g3lfkC`r&aOZkpaf@tPl)UP|3DT4kUV%> z>l2X0SQ6wH%;50sMjD8d21sKV=r?WYe$ZW=;SK$$PpuhHzX@ zPT(y~5MXd(XA|3F(csvkXkyafXmW*-At##Uab@vA4WLrh64!{5l*E!$tK_0oAjM#0 zU}UIkV61Cs8Dd~zWnyM!Y_4r!U}a$7cX`EO6b-rgDVb@Nm>LYB8fsUIGXXU)c)I$z JtaD0e0s!sVP1pbc diff --git a/assets/icons/StatusBar/PlaceholderR_30x13.png b/assets/icons/StatusBar/PlaceholderR_30x13.png deleted file mode 100644 index bc1c9367cf87414f9af08a2d9cdab6c50bbe1580..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 318 zcmeAS@N?(olHy`uVBq!ia0vp^av(MnGmtziW5xocSkfJR9T^xl_H+M9WCijSl0AZa z85pY67#JE_7#My5g&JNkFq9fFFuY1&V6d9Oz#v{QXIG#NP=YhSC&cyte;|tiNFKbc z^$EygED7=pW^j0RBMrn!@^*J&=wOxg0CLz%Jbhi+pE3(`vZ@^X(y0v;QuTCk4B@z* zoWRS<%*MvZW>CQ3WFW9$iXcNGi$RCenu3Ieo2(4C12|7vf1Yas(oo_WQIe8al4_M) zlnSI6j0}tnbq$Pl4J|_qEUgR-tqe@G4GgRd4C>Tb4x?zu%}>cpt3=meU}b6w(ZHvF RX(CVqgQu&X%Q~loCIEz}PecF! diff --git a/assets/protobuf b/assets/protobuf index d9e343661..cc5918dc4 160000 --- a/assets/protobuf +++ b/assets/protobuf @@ -1 +1 @@ -Subproject commit d9e343661dd36cfab792b78be1dea4e5950cb4dd +Subproject commit cc5918dc488ac3617012ce5377114e086b447324 diff --git a/assets/resources/Manifest b/assets/resources/Manifest index a0aeb67b9..7dbd077be 100644 --- a/assets/resources/Manifest +++ b/assets/resources/Manifest @@ -1,5 +1,5 @@ V:0 -T:1655152832 +T:1659888302 D:badusb D:dolphin D:infrared @@ -223,7 +223,7 @@ F:33b8fde22f34ef556b64b77164bc19b0:578:dolphin/L3_Lab_research_128x54/frame_8.bm F:f267f0654781049ca323b11bb4375519:581:dolphin/L3_Lab_research_128x54/frame_9.bm F:41106c0cbc5144f151b2b2d3daaa0527:727:dolphin/L3_Lab_research_128x54/meta.txt D:infrared/assets -F:d895fda2f48c6cc4c55e8a398ff52e43:74300:infrared/assets/tv.ir +F:a565c3a381695a5f2ba7a0698460238c:74833:infrared/assets/tv.ir F:a157a80f5a668700403d870c23b9567d:470:music_player/Marble_Machine.fmf D:nfc/assets F:81dc04c7b181f94b644079a71476dff4:4742:nfc/assets/aid.nfc @@ -232,10 +232,10 @@ F:41b4f08774249014cb8d3dffa5f5c07d:1757:nfc/assets/currency_code.nfc F:c60e862919731b0bd538a1001bbc1098:17453:nfc/assets/mf_classic_dict.nfc D:subghz/assets F:dda1ef895b8a25fde57c874feaaef997:650:subghz/assets/came_atomo -F:610a0ffa2479a874f2060eb2348104c5:2712:subghz/assets/keeloq_mfcodes +F:788eef2cc74e29f3388463d6607dab0d:3264:subghz/assets/keeloq_mfcodes F:9214f9c10463b746a27e82ce0b96e040:465:subghz/assets/keeloq_mfcodes_user F:653bd8d349055a41e1152e557d4a52d3:202:subghz/assets/nice_flor_s -F:c6ec4374275cd20f482ecd46de9f53e3:528:subghz/assets/setting_user +F:c1c63fbd5f5aa3ea504027014652191f:1150:subghz/assets/setting_user D:u2f/assets F:7e11e688e39034bbb9d88410044795e1:365:u2f/assets/cert.der F:f60b88c20ed479ed9684e249f7134618:264:u2f/assets/cert_key.u2f diff --git a/assets/resources/infrared/assets/tv.ir b/assets/resources/infrared/assets/tv.ir index 98292a8ec..79478e7eb 100755 --- a/assets/resources/infrared/assets/tv.ir +++ b/assets/resources/infrared/assets/tv.ir @@ -135,6 +135,42 @@ command: 02 00 00 00 # name: POWER type: parsed +protocol: Samsung32 +address: 07 00 00 00 +command: E6 00 00 00 +# +name: VOL+ +type: parsed +protocol: Samsung32 +address: 07 00 00 00 +command: 07 00 00 00 +# +name: VOL- +type: parsed +protocol: Samsung32 +address: 07 00 00 00 +command: 0B 00 00 00 +# +name: CH+ +type: parsed +protocol: Samsung32 +address: 07 00 00 00 +command: 12 00 00 00 +# +name: CH- +type: parsed +protocol: Samsung32 +address: 07 00 00 00 +command: 10 00 00 00 +# +name: MUTE +type: parsed +protocol: Samsung32 +address: 07 00 00 00 +command: 0F 00 00 00 +# +name: POWER +type: parsed protocol: NEC address: 50 00 00 00 command: 17 00 00 00 diff --git a/assets/resources/subghz/assets/setting_user b/assets/resources/subghz/assets/setting_user index 413dbf31f..1f37a2eb5 100644 --- a/assets/resources/subghz/assets/setting_user +++ b/assets/resources/subghz/assets/setting_user @@ -2,17 +2,28 @@ Filetype: Flipper SubGhz Setting File Version: 1 # Add Standard frequencies for your region -#add_standard_frequencies: true +#Add_standard_frequencies: true # Default Frequency: used as default for "Read" and "Read Raw" -#default_frequency: 433920000 +#Default_frequency: 433920000 # Frequencies used for "Read", "Read Raw" and "Frequency Analyzer" -#frequency: 300000000 -#frequency: 310000000 -#frequency: 320000000 +#Frequency: 300000000 +#Frequency: 310000000 +#Frequency: 320000000 # Frequencies used for hopping mode (keep this list small or flipper will miss signal) -#hopper_frequency: 300000000 -#hopper_frequency: 310000000 -#hopper_frequency: 310000000 +#Hopper_frequency: 300000000 +#Hopper_frequency: 310000000 +#Hopper_frequency: 310000000 + +# Custom preset +# format for CC1101 "Custom_preset_data:" XX YY XX YY .. 00 00 ZZ ZZ ZZ ZZ ZZ ZZ ZZ ZZ, where: XX-register, YY - register data, 00 00 - end load register, ZZ - 8 byte Pa table register + +#Custom_preset_name: AM_1 +#Custom_preset_module: CC1101 +#Custom_preset_data: 02 0D 03 07 08 32 0B 06 14 00 13 00 12 30 11 32 10 17 18 18 19 18 1D 91 1C 00 1B 07 20 FB 22 11 21 B6 00 00 00 C0 00 00 00 00 00 00 + +#Custom_preset_name: AM_2 +#Custom_preset_module: CC1101 +#Custom_preset_data: 02 0D 03 07 08 32 0B 06 14 00 13 00 12 30 11 32 10 17 18 18 19 18 1D 91 1C 00 1B 07 20 FB 22 11 21 B6 00 00 00 C0 00 00 00 00 00 00 diff --git a/assets/slideshow/first_start/frame_00.png b/assets/slideshow/first_start/frame_00.png new file mode 100644 index 0000000000000000000000000000000000000000..67f23bd31b596fdf57d3b8bff62b2e6d2a03070a GIT binary patch literal 602 zcmV-g0;TPx#1ZP1_K>z@;j|==^1poj522e~?MgRZ*00010!qa{L000SaNLh0L01FcU01FcV z0GgZ_0005qNklf1jQIb5Ot-FLBM) o8C$9X3)L09%Gjx{=!^ik1;ah0>A+VwJ^%m!07*qoM6N<$f@V(tCIA2c literal 0 HcmV?d00001 diff --git a/assets/slideshow/first_start/frame_01.png b/assets/slideshow/first_start/frame_01.png new file mode 100644 index 0000000000000000000000000000000000000000..5ac995c3976340eeb6607218c872e43363879ba0 GIT binary patch literal 558 zcmV+}0@3}6P)Px#1ZP1_K>z@;j|==^1poj522e~?MgRZ*00010!qa{L000SaNLh0L01FcU01FcV z0GgZ_00058Nkl(1eeS1-S74`v$I40wx#0;{eCP> z<93}+(EH{s4^7IT*9HP26;TpTXT>QbF<_C-XbLGabjDXl4FLyGCc3z-9}YA_7Jy#| za4p~h90LRS1k-t)&iFj+3wW53u;akRe?12dR{a5O0dEzLp;Ek5AafmPNEWQ)yWVl@ zzSqwhsNzLOED8p1>}rb&0yA!#kumNWLMb9+KKE6-GQG`6rwn*`t_Oxmq(P20$Ob=`ev!Mjsj-(RhXPaI zv!aW1UQ;HcqFGJq9ir2wl)wzqpIXXDn$lZQ`Px#1ZP1_K>z@;j|==^1poj522e~?MgRZ*00010!qa{L000SaNLh0L01FcU01FcV z0GgZ_0005CNklDWTK(>sam%ZW+D@kb~X0S@E4iT=oV&Qy% zcmc7yDTKpH#XNu(EBFSMQ(E~93pSQ1E!4gLH#-~GJi|?z>@VMb^XHoh{x4aotd<4h zvLLj=4+hxc2hagBpg=5O(fjT}Swz-D!g9hg*%AI8B|kPF2<_@ceLDKQpRe<4mreQ} zlKuRN&{qkP8nnrfGC@~*jM(FzkTJohH=m=G!e$Z;9aRH+a7(nKMpA}CvIv<#iX`0$ zUFw@0A)0j+;Us?wFeuXaMJjX+&UK!lf8E_r zHau(ty%~40evSm?_!hK64V=&)*Bmm3!P96!3e_pCzB17q+G=LComUua4q)}|&%^QM zS0~^~7ZLU@hSOXsFyPi`0@v~)tU}-H9S$dJHAr;$^Qj>A!FVJ!lppQ~w^@XqhlrUy z#AO`dO9i|SuY|{yZ)58r

(>4?X|CwYLt^;zmGmgJ85iec5G6@WdeT{P5*zV;dlWT<|4Zrq0bj12oc{9Px#1ZP1_K>z@;j|==^1poj522e~?MgRZ*00010!qa{L000SaNLh0L01FcU01FcV z0GgZ_0005jNklljA6bJBkI5dtF66X}rAQhaUkrJ^Orb-~$Y8k8wsa4sL zC*}@FFwv8`q*6B+*$_KB%MFN-g}I}}(juzL1414muK#B{<=n4;6`hmcy}WyVcc%aB za!gu>d-RxZ2d20*(LOa9R5?8)q4`6iiVw)am;k&btQ4aKZHxnk+5W4CF#tcv1@tEI zyKM%9bCl?{Di~tE;aL$0(yk<&BYyed6Cfg?997i83o%gAEDJ+9fDGSm*p(jpMt6+` zhZ}x6@D)SsksFZ8ufyx`^dwZ`*2%@Mw}Wqh(3~F_C3>Ur=97837Ke`3^1*>5{FWm1&Q|i?`{d2)aqXnaz1I4 zfahJsR?4HFWkh}jtuhDBXmlTcX;0@>KNuEP9SqO%I(+$1j}V`?e%GM0efWo{d^-%E huPx#1ZP1_K>z@;j|==^1poj522e~?MgRZ*00010!qa{L000SaNLh0L01FcU01FcV z0GgZ_0004}NklB4*$GWC(}0K=P`9o z@IeQ(Hz5HTfB@pa8_*1hU@<|yC!fL=!Mbq z2y6x#!_&O`?pn%cph^In7uBY##5im+#HL_yFhi9lIanb`a7ZYibd8^o5Hy-v60Ksr z@M_%b4=5kR&_HXqw!3Gv-4)DB#Ge(MGQ1<}Bb`^Uy;p0y0?R$5iw2s7$HME*VPl}+ zGlp}{t$shppyP5@SOD@5;6T$9*oc43z!Atk48=%C^JGw7Eq82z1=`sQoeR6`yLnT< zG->Y`>I4;A_!-)21+{$NwH&-MKK7>~sWtD>w1O;8^L2j6fnjMij&- m3WgS`1HQUa;bryT0NNKHvxH{lzhQO&0000Px#1ZP1_K>z@;j|==^1poj522e~?MgRZ*00010!qa{L000SaNLh0L01FcU01FcV z0GgZ_0005TNklYE7y$6^(psZLjkRC|#Z(ftTe>+2mZA>DLBv@y`2<46 z#ZwR(2(fAxp`e3(2PX-Z79F&&5W&IG(Lspc-6b~l6+B4p_}$%iANL3Td07iZ`HZ0y z)iV2q0RW9a!EczZ0@Nt)RvE0q@E`<0d^nrQd%I63_A`}K%)>4vK%4go21ZyXIs+Ncit=+QaTp)NCT^P7Eq2=XJ}4{uI1#}qR5tRa z4vL*iLI8@-K#l=pP_~1(N6uoglQ%F19l=%X#Oc$~37DV>xXl5-HGMEd;}`0WqH)d- z1EEJmAGH Open Folder" menu. + + * On first start, you'll be prompted to install recommended plug-ins. Please install them for best development experience. _You can find a list of them in `.vscode/extensions.json`._ + * Basic build tasks are invoked in Ctrl+Shift+B menu. + * Debugging requires a supported probe. That includes: + * Wi-Fi devboard with stock firmware (blackmagic), + * ST-Link and compatible devices, + * J-Link for flashing and debugging (in VSCode only). _Note that J-Link tools are not included with our toolchain and you have to [download](https://www.segger.com/downloads/jlink/) them yourself and put on your system's PATH._ + * Without a supported probe, you can install firmware on Flipper using USB installation method. + + ## FBT targets FBT keeps track of internal dependencies, so you only need to build the highest-level target you need, and FBT will make sure everything they depend on is up-to-date. ### High-level (what you most likely need) - + - `fw_dist` - build & publish firmware to `dist` folder. This is a default target, when no other are specified -- `updater_package` - build self-update package. _Requires `--with-updater` option_ +- `updater_package`, `updater_minpackage` - build self-update package. Minimal version only inclues firmware's DFU file; full version also includes radio stack & resources for SD card - `copro_dist` - bundle Core2 FUS+stack binaries for qFlipper - `flash` - flash attached device with OpenOCD over ST-Link -- `flash_usb` - build, upload and install update package to device over USB. _Requires `--with-updater` option_ +- `flash_usb`, `flash_usb_full` - build, upload and install update package to device over USB. See details on `updater_package`, `updater_minpackage` - `debug` - build and flash firmware, then attach with gdb with firmware's .elf loaded -- `debug_updater` - attach gdb with updater's .elf loaded. _Requires `--with-updater` option_ -- `debug_other` - attach gdb without loading any .elf. Allows to manually add external elf files with `add-symbol-file` in gdb. +- `debug_other` - attach gdb without loading any .elf. Allows to manually add external elf files with `add-symbol-file` in gdb +- `updater_debug` - attach gdb with updater's .elf loaded - `blackmagic` - debug firmware with Blackmagic probe (WiFi dev board) - `openocd` - just start OpenOCD - `get_blackmagic` - output blackmagic address in gdb remote format. Useful for IDE integration +- `lint`, `format` - run clang-tidy on C source code to check and reformat it according to `.clang-format` specs +- `lint_py`, `format_py` - run [black](https://black.readthedocs.io/en/stable/index.html) on Python source code, build system files & application manifests ### Firmware targets @@ -49,9 +64,11 @@ FBT keeps track of internal dependencies, so you only need to build the highest- - Check out `--extra-ext-apps` for force adding extra apps to external build - `firmware_snake_game_list`, etc - generate source + assembler listing for app's .elf - `flash`, `firmware_flash` - flash current version to attached device with OpenOCD over ST-Link +- `jflash` - flash current version to attached device with JFlash using J-Link probe. JFlash executable must be on your $PATH - `flash_blackmagic` - flash current version to attached device with Blackmagic probe - `firmware_all`, `updater_all` - build basic set of binaries - `firmware_list`, `updater_list` - generate source + assembler listing +- `firmware_cdb`, `updater_cdb` - generate `compilation_database.json` file for external tools and IDEs. It can be created without actually building the firmware. ### Assets @@ -66,21 +83,21 @@ FBT keeps track of internal dependencies, so you only need to build the highest- ## Command-line parameters - `--options optionfile.py` (default value `fbt_options.py`) - load file with multiple configuration values -- `--with-updater` - enables updater-related targets and dependency tracking. Enabling this options introduces extra startup time costs, so use it when bundling update packages. Or if you have a fast computer and don't care about a few extra seconds of startup time +- `--with-updater` - enables updater-related targets and dependency tracking. Enabling this option introduces extra startup time costs, so use it when bundling update packages. _Explicily enabling this should no longer be required, fbt now has specific handling for updater-related targets_ - `--extra-int-apps=app1,app2,appN` - forces listed apps to be built as internal with `firmware` target - `--extra-ext-apps=app1,app2,appN` - forces listed apps to be built as external with `firmware_extapps` target ## Configuration -Default configuration variables are set in configuration file `fbt_options.py`. +Default configuration variables are set in the configuration file `fbt_options.py`. Values set on command-line have higher precedence over configuration file. You can find out available options with `./fbt -h`. ### Firmware application set -You can create customized firmware builds by modifying application list to be included in the build. Application presets are configured with `FIRMWARE_APPS` option, which is a map(configuration_name:str -> application_list:tuple(str)). To specify application set to use in a build, set `FIRMWARE_APP_SET` to its name. -For example, to build firmware image with unit tests, run `./fbt FIRMWARE_APP_SET=unit_tests`. +You can create customized firmware builds by modifying the application list to be included in the build. Application presets are configured with the `FIRMWARE_APPS` option, which is a map(configuration_name:str -> application_list:tuple(str)). To specify application set to use in a build, set `FIRMWARE_APP_SET` to its name. +For example, to build a firmware image with unit tests, run `./fbt FIRMWARE_APP_SET=unit_tests`. Check out `fbt_options.py` for details. diff --git a/fbt b/fbt index 0ea572b12..981489dd1 100755 --- a/fbt +++ b/fbt @@ -4,14 +4,20 @@ # unofficial strict mode set -eu; -SCONS_DEFAULT_FLAGS="-Q --warn=target-not-built"; +# private variables SCRIPT_PATH="$(cd "$(dirname "$0")" && pwd -P)"; +SCONS_DEFAULT_FLAGS="-Q --warn=target-not-built"; + +# public variables +FBT_NOENV="${FBT_NOENV:-""}"; +FBT_NO_SYNC="${FBT_NO_SYNC:-""}"; +FBT_TOOLCHAIN_PATH="${FBT_TOOLCHAIN_PATH:-$SCRIPT_PATH}"; -if [ -z "${FBT_NOENV:-}" ]; then +if [ -z "$FBT_NOENV" ]; then . "$SCRIPT_PATH/scripts/toolchain/fbtenv.sh"; fi -if [ -z "${FBT_NO_SYNC:-}" ]; then +if [ -z "$FBT_NO_SYNC" ]; then if [ ! -d "$SCRIPT_PATH/.git" ]; then echo "\".git\" directory not found, please clone repo via \"git clone --recursive\""; exit 1; @@ -19,4 +25,4 @@ if [ -z "${FBT_NO_SYNC:-}" ]; then git submodule update --init; fi -python3 "$SCRIPT_PATH/lib/scons/scripts/scons.py" $SCONS_DEFAULT_FLAGS "$@" \ No newline at end of file +python3 "$SCRIPT_PATH/lib/scons/scripts/scons.py" $SCONS_DEFAULT_FLAGS "$@" diff --git a/fbt.cmd b/fbt.cmd index 2339eaaa1..f09b98382 100644 --- a/fbt.cmd +++ b/fbt.cmd @@ -1,5 +1,5 @@ @echo off -call %~dp0scripts\toolchain\fbtenv.cmd env +call "%~dp0scripts\toolchain\fbtenv.cmd" env set SCONS_EP=%~dp0\lib\scons\scripts\scons.py @@ -11,7 +11,6 @@ if [%FBT_NO_SYNC%] == [] ( exit /b 1 ) ) -git submodule update --init set "SCONS_DEFAULT_FLAGS=-Q --warn=target-not-built" -python lib\scons\scripts\scons.py %SCONS_DEFAULT_FLAGS% %* \ No newline at end of file +python lib\scons\scripts\scons.py %SCONS_DEFAULT_FLAGS% %* diff --git a/fbt_options.py b/fbt_options.py index fb2a0d366..7276f579e 100644 --- a/fbt_options.py +++ b/fbt_options.py @@ -80,6 +80,7 @@ ], "unit_tests": [ "basic_services", + "updater_app", "unit_tests", ], } diff --git a/firmware.scons b/firmware.scons index 0d1205926..064e0ddab 100644 --- a/firmware.scons +++ b/firmware.scons @@ -9,7 +9,11 @@ from fbt.util import ( # Building initial C environment for libs env = ENV.Clone( - tools=["compilation_db", "fwbin", "fbt_apps"], + tools=[ + ("compilation_db", {"COMPILATIONDB_COMSTR": "\tCDB\t${TARGET}"}), + "fwbin", + "fbt_apps", + ], COMPILATIONDB_USE_ABSPATH=False, BUILD_DIR=fw_build_meta["build_dir"], IS_BASE_FIRMWARE=fw_build_meta["type"] == "firmware", @@ -76,7 +80,6 @@ if not env["VERBOSE"]: HEXCOMSTR="\tHEX\t${TARGET}", BINCOMSTR="\tBIN\t${TARGET}", DFUCOMSTR="\tDFU\t${TARGET}", - OPENOCDCOMSTR="\tFLASH\t${SOURCE}", ) @@ -94,7 +97,7 @@ else: ], ) -# Invoke child SCopscripts to populate global `env` + build their own part of the code +# Invoke child SConscripts to populate global `env` + build their own part of the code lib_targets = env.BuildModules( [ "lib", @@ -139,7 +142,7 @@ apps_c = fwenv.ApplicationsC( Value(fwenv["APPS"]), ) # Adding dependency on manifest files so apps.c is rebuilt when any manifest is changed -fwenv.Depends(apps_c, fwenv.GlobRecursive("*.fam", "applications")) +fwenv.Depends(apps_c, fwenv.GlobRecursive("*.fam", "#/applications")) sources = [apps_c] # Gather sources only from app folders in current configuration @@ -161,9 +164,9 @@ fwenv.AppendUnique( "-Wl,--wrap,_free_r", "-Wl,--wrap,_calloc_r", "-Wl,--wrap,_realloc_r", - "-u", - "_printf_float", "-n", + "-Xlinker", + "-Map=${TARGET}.map", ], ) @@ -176,6 +179,7 @@ fwelf = fwenv["FW_ELF"] = fwenv.Program( "${FIRMWARE_BUILD_CFG}", sources, LIBS=[ + "print", "flipper${TARGET_HW}", "furi", "freertos", @@ -186,6 +190,7 @@ fwelf = fwenv["FW_ELF"] = fwenv.Program( "subghz", "flipperformat", "toolbox", + "nfc", "microtar", "usb_stm32", "st25rfal002", @@ -201,7 +206,6 @@ fwelf = fwenv["FW_ELF"] = fwenv.Program( ], ) -# Make it depend on everything child builders returned # Firmware depends on everything child builders returned Depends(fwelf, lib_targets) # Output extra details after building firmware @@ -231,7 +235,8 @@ if should_gen_cdb_and_link_dir(fwenv, BUILD_TARGETS): fwcdb = fwenv.CompilationDatabase() # without filtering, both updater & firmware commands would be generated fwenv.Replace(COMPILATIONDB_PATH_FILTER=fwenv.subst("*${FW_FLAVOR}*")) - Depends(fwcdb, fwelf) + AlwaysBuild(fwcdb) + Alias(fwenv["FIRMWARE_BUILD_CFG"] + "_cdb", fwcdb) fw_artifacts.append(fwcdb) # Adding as a phony target, so folder link is updated even if elf didn't change diff --git a/firmware/targets/f7/Inc/FreeRTOSConfig.h b/firmware/targets/f7/Inc/FreeRTOSConfig.h index 4f9d1fcf9..f54d774ca 100644 --- a/firmware/targets/f7/Inc/FreeRTOSConfig.h +++ b/firmware/targets/f7/Inc/FreeRTOSConfig.h @@ -32,7 +32,7 @@ extern uint32_t SystemCoreClock; #define configUSE_16_BIT_TICKS 0 #define configUSE_MUTEXES 1 #define configQUEUE_REGISTRY_SIZE 0 -#define configCHECK_FOR_STACK_OVERFLOW 2 +#define configCHECK_FOR_STACK_OVERFLOW 0 #define configUSE_RECURSIVE_MUTEXES 1 #define configUSE_COUNTING_SEMAPHORES 1 #define configENABLE_BACKWARD_COMPATIBILITY 0 @@ -145,3 +145,7 @@ standard names. */ #define USE_CUSTOM_SYSTICK_HANDLER_IMPLEMENTATION 1 #define configOVERRIDE_DEFAULT_TICK_CONFIGURATION \ 1 /* required only for Keil but does not hurt otherwise */ + +#define traceTASK_SWITCHED_IN() \ + extern void furi_hal_mpu_set_stack_protection(uint32_t* stack); \ + furi_hal_mpu_set_stack_protection((uint32_t*)pxCurrentTCB->pxStack) diff --git a/firmware/targets/f7/Src/dfu.c b/firmware/targets/f7/Src/dfu.c index 889c3c1ec..f32ac2ac4 100644 --- a/firmware/targets/f7/Src/dfu.c +++ b/firmware/targets/f7/Src/dfu.c @@ -19,7 +19,7 @@ void flipper_boot_dfu_show_splash() { u8g2_DrawXBM(fb, 0, 64 - 50, 128, 50, splash_data); u8g2_SetFont(fb, u8g2_font_helvB08_tr); u8g2_DrawStr(fb, 2, 8, "Update & Recovery Mode"); - u8g2_DrawStr(fb, 2, 21, "DFU started"); + u8g2_DrawStr(fb, 2, 21, "DFU Started"); u8g2_SetPowerSave(fb, 0); u8g2_SendBuffer(fb); } diff --git a/firmware/targets/f7/Src/update.c b/firmware/targets/f7/Src/update.c index bab3b9aad..36204829e 100644 --- a/firmware/targets/f7/Src/update.c +++ b/firmware/targets/f7/Src/update.c @@ -11,8 +11,7 @@ #include #include -#define FS_ROOT_PATH "/" -#define UPDATE_POINTER_FILE_PATH FS_ROOT_PATH UPDATE_MANIFEST_POINTER_FILE_NAME +#define UPDATE_POINTER_FILE_PATH "/" UPDATE_MANIFEST_POINTER_FILE_NAME static FATFS* pfs = NULL; @@ -23,6 +22,21 @@ static FATFS* pfs = NULL; } \ } +static bool flipper_update_mount_sd() { + for(int i = 0; i < BSP_SD_MaxMountRetryCount(); ++i) { + if(BSP_SD_Init((i % 2) == 0) != MSD_OK) { + /* Next attempt will be without card reset, let it settle */ + furi_delay_ms(1000); + continue; + } + + if(f_mount(pfs, "/", 1) == FR_OK) { + return true; + } + } + return false; +} + static bool flipper_update_init() { furi_hal_clock_init(); furi_hal_rtc_init(); @@ -35,13 +49,9 @@ static bool flipper_update_init() { return false; } - if(BSP_SD_Init(true)) { - return false; - } - pfs = malloc(sizeof(FATFS)); - CHECK_FRESULT(f_mount(pfs, FS_ROOT_PATH, 1)); - return true; + + return flipper_update_mount_sd(); } static bool flipper_update_load_stage(const string_t work_dir, UpdateManifest* manifest) { @@ -119,7 +129,7 @@ static bool flipper_update_get_manifest_path(string_t out_path) { break; } string_set_str(out_path, manifest_name_buf); - string_right(out_path, strlen("/ext")); + string_right(out_path, strlen(STORAGE_EXT_PATH_PREFIX)); } while(0); f_close(&file); return !string_empty_p(out_path); diff --git a/firmware/targets/f7/fatfs/stm32_adafruit_sd.c b/firmware/targets/f7/fatfs/stm32_adafruit_sd.c index 1ca38abe4..6db430a53 100644 --- a/firmware/targets/f7/fatfs/stm32_adafruit_sd.c +++ b/firmware/targets/f7/fatfs/stm32_adafruit_sd.c @@ -331,6 +331,10 @@ void SD_SPI_Bus_To_Normal_State() { * @{ */ +uint8_t BSP_SD_MaxMountRetryCount() { + return 10; +} + /** * @brief Initializes the SD/SD communication. * @param None diff --git a/firmware/targets/f7/fatfs/stm32_adafruit_sd.h b/firmware/targets/f7/fatfs/stm32_adafruit_sd.h index 74a0e0c22..e0c5e3bef 100644 --- a/firmware/targets/f7/fatfs/stm32_adafruit_sd.h +++ b/firmware/targets/f7/fatfs/stm32_adafruit_sd.h @@ -198,6 +198,7 @@ typedef struct { /** @defgroup STM32_ADAFRUIT_SD_Exported_Functions * @{ */ +uint8_t BSP_SD_MaxMountRetryCount(); uint8_t BSP_SD_Init(bool reset_card); uint8_t BSP_SD_ReadBlocks(uint32_t* pData, uint32_t ReadAddr, uint32_t NumOfBlocks, uint32_t Timeout); diff --git a/firmware/targets/f7/furi_hal/furi_hal.c b/firmware/targets/f7/furi_hal/furi_hal.c index 0cef33ddf..23f409736 100644 --- a/firmware/targets/f7/furi_hal/furi_hal.c +++ b/firmware/targets/f7/furi_hal/furi_hal.c @@ -1,4 +1,5 @@ #include +#include #include @@ -35,6 +36,7 @@ void furi_hal_deinit_early() { } void furi_hal_init() { + furi_hal_mpu_init(); furi_hal_clock_init(); furi_hal_console_init(); furi_hal_rtc_init(); @@ -80,17 +82,6 @@ void furi_hal_init() { // FatFS driver initialization MX_FATFS_Init(); FURI_LOG_I(TAG, "FATFS OK"); - - // Partial null pointer dereference protection - LL_MPU_Disable(); - LL_MPU_ConfigRegion( - LL_MPU_REGION_NUMBER0, - 0x00, - 0x0, - LL_MPU_REGION_SIZE_1MB | LL_MPU_REGION_PRIV_RO_URO | LL_MPU_ACCESS_BUFFERABLE | - LL_MPU_ACCESS_CACHEABLE | LL_MPU_ACCESS_SHAREABLE | LL_MPU_TEX_LEVEL1 | - LL_MPU_INSTRUCTION_ACCESS_ENABLE); - LL_MPU_Enable(LL_MPU_CTRL_PRIVILEGED_DEFAULT); } void furi_hal_switch(void* address) { diff --git a/firmware/targets/f7/furi_hal/furi_hal_interrupt.c b/firmware/targets/f7/furi_hal/furi_hal_interrupt.c index fa595921a..038ae9489 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_interrupt.c +++ b/firmware/targets/f7/furi_hal/furi_hal_interrupt.c @@ -6,6 +6,7 @@ #include #include #include +#include #define TAG "FuriHalInterrupt" @@ -95,6 +96,10 @@ void furi_hal_interrupt_init() { LL_SYSCFG_DisableIT_FPU_IDC(); LL_SYSCFG_DisableIT_FPU_IXC(); + LL_HANDLER_EnableFault(LL_HANDLER_FAULT_USG); + LL_HANDLER_EnableFault(LL_HANDLER_FAULT_BUS); + LL_HANDLER_EnableFault(LL_HANDLER_FAULT_MEM); + FURI_LOG_I(TAG, "Init OK"); } @@ -241,6 +246,20 @@ void HardFault_Handler() { } void MemManage_Handler() { + if(FURI_BIT(SCB->CFSR, SCB_CFSR_MMARVALID_Pos)) { + uint32_t memfault_address = SCB->MMFAR; + if(memfault_address < (1024 * 1024)) { + // from 0x00 to 1MB, see FuriHalMpuRegionNULL + furi_crash("NULL pointer dereference"); + } else { + // write or read of MPU region 1 (FuriHalMpuRegionStack) + furi_crash("MPU fault, possibly stack overflow"); + } + } else if(FURI_BIT(SCB->CFSR, SCB_CFSR_MSTKERR_Pos)) { + // push to stack on MPU region 1 (FuriHalMpuRegionStack) + furi_crash("MemManage fault, possibly stack overflow"); + } + furi_crash("MemManage"); } diff --git a/firmware/targets/f7/furi_hal/furi_hal_mpu.c b/firmware/targets/f7/furi_hal/furi_hal_mpu.c new file mode 100644 index 000000000..ea6cd55be --- /dev/null +++ b/firmware/targets/f7/furi_hal/furi_hal_mpu.c @@ -0,0 +1,66 @@ +#include +#include + +#define FURI_HAL_MPU_ATTRIBUTES \ + (LL_MPU_ACCESS_BUFFERABLE | LL_MPU_ACCESS_CACHEABLE | LL_MPU_ACCESS_SHAREABLE | \ + LL_MPU_TEX_LEVEL1 | LL_MPU_INSTRUCTION_ACCESS_ENABLE) + +#define FURI_HAL_MPU_STACK_PROTECT_REGION FuriHalMPURegionSize32B + +void furi_hal_mpu_init() { + furi_hal_mpu_enable(); + + // NULL pointer dereference protection + furi_hal_mpu_protect_no_access(FuriHalMpuRegionNULL, 0x00, FuriHalMPURegionSize1MB); +} + +void furi_hal_mpu_enable() { + LL_MPU_Enable(LL_MPU_CTRL_PRIVILEGED_DEFAULT); +} + +void furi_hal_mpu_disable() { + LL_MPU_Disable(); +} + +void furi_hal_mpu_protect_no_access( + FuriHalMpuRegion region, + uint32_t address, + FuriHalMPURegionSize size) { + uint32_t size_ll = size; + size_ll = size_ll << MPU_RASR_SIZE_Pos; + + furi_hal_mpu_disable(); + LL_MPU_ConfigRegion( + region, 0x00, address, FURI_HAL_MPU_ATTRIBUTES | LL_MPU_REGION_NO_ACCESS | size_ll); + furi_hal_mpu_enable(); +} + +void furi_hal_mpu_protect_read_only( + FuriHalMpuRegion region, + uint32_t address, + FuriHalMPURegionSize size) { + uint32_t size_ll = size; + size_ll = size_ll << MPU_RASR_SIZE_Pos; + + furi_hal_mpu_disable(); + LL_MPU_ConfigRegion( + region, 0x00, address, FURI_HAL_MPU_ATTRIBUTES | LL_MPU_REGION_PRIV_RO_URO | size_ll); + furi_hal_mpu_enable(); +} + +void furi_hal_mpu_protect_disable(FuriHalMpuRegion region) { + furi_hal_mpu_disable(); + LL_MPU_DisableRegion(region); + furi_hal_mpu_enable(); +} + +void furi_hal_mpu_set_stack_protection(uint32_t* stack) { + // Protection area address must be aligned to region size + uint32_t stack_ptr = (uint32_t)stack; + uint32_t mask = ((1 << (FURI_HAL_MPU_STACK_PROTECT_REGION + 2)) - 1); + stack_ptr &= ~mask; + if(stack_ptr < (uint32_t)stack) stack_ptr += (mask + 1); + + furi_hal_mpu_protect_read_only( + FuriHalMpuRegionStack, stack_ptr, FURI_HAL_MPU_STACK_PROTECT_REGION); +} \ No newline at end of file diff --git a/firmware/targets/f7/furi_hal/furi_hal_nfc.c b/firmware/targets/f7/furi_hal/furi_hal_nfc.c index 7b7d5f275..2d6db8fbf 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_nfc.c +++ b/firmware/targets/f7/furi_hal/furi_hal_nfc.c @@ -341,13 +341,13 @@ void furi_hal_nfc_listen_start(FuriHalNfcDevData* nfc_data) { if(nfc_data->uid_len == 4) { pt_memory[12] = nfc_data->sak & ~FURI_HAL_NFC_UID_INCOMPLETE; } else { - pt_memory[12] = nfc_data->sak | FURI_HAL_NFC_UID_INCOMPLETE; + pt_memory[12] = FURI_HAL_NFC_UID_INCOMPLETE; } pt_memory[13] = nfc_data->sak & ~FURI_HAL_NFC_UID_INCOMPLETE; pt_memory[14] = nfc_data->sak & ~FURI_HAL_NFC_UID_INCOMPLETE; st25r3916WritePTMem(pt_memory, sizeof(pt_memory)); - // Go to sence + // Go to sense st25r3916ExecuteCommand(ST25R3916_CMD_GOTO_SENSE); } @@ -507,6 +507,9 @@ static bool furi_hal_nfc_transparent_tx_rx(FuriHalNfcTxRxContext* tx_rx, uint16_ uint8_t rxe = 0; uint32_t start = DWT->CYCCNT; while(true) { + if(!rfalIsExtFieldOn()) { + return false; + } if(furi_hal_gpio_read(&gpio_nfc_irq_rfid_pull) == true) { st25r3916ReadRegister(ST25R3916_REG_IRQ_MAIN, &rxe); if(rxe & (1 << 4)) { diff --git a/firmware/targets/f7/furi_hal/furi_hal_subghz.c b/firmware/targets/f7/furi_hal/furi_hal_subghz.c index 5ad0d5785..650f8ac33 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_subghz.c +++ b/firmware/targets/f7/furi_hal/furi_hal_subghz.c @@ -100,22 +100,24 @@ void furi_hal_subghz_dump_state() { void furi_hal_subghz_load_preset(FuriHalSubGhzPreset preset) { if(preset == FuriHalSubGhzPresetOok650Async) { - furi_hal_subghz_load_registers(furi_hal_subghz_preset_ook_650khz_async_regs); + furi_hal_subghz_load_registers((uint8_t*)furi_hal_subghz_preset_ook_650khz_async_regs); furi_hal_subghz_load_patable(furi_hal_subghz_preset_ook_async_patable); } else if(preset == FuriHalSubGhzPresetOok270Async) { - furi_hal_subghz_load_registers(furi_hal_subghz_preset_ook_270khz_async_regs); + furi_hal_subghz_load_registers((uint8_t*)furi_hal_subghz_preset_ook_270khz_async_regs); furi_hal_subghz_load_patable(furi_hal_subghz_preset_ook_async_patable); } else if(preset == FuriHalSubGhzPreset2FSKDev238Async) { - furi_hal_subghz_load_registers(furi_hal_subghz_preset_2fsk_dev2_38khz_async_regs); + furi_hal_subghz_load_registers( + (uint8_t*)furi_hal_subghz_preset_2fsk_dev2_38khz_async_regs); furi_hal_subghz_load_patable(furi_hal_subghz_preset_2fsk_async_patable); } else if(preset == FuriHalSubGhzPreset2FSKDev476Async) { - furi_hal_subghz_load_registers(furi_hal_subghz_preset_2fsk_dev47_6khz_async_regs); + furi_hal_subghz_load_registers( + (uint8_t*)furi_hal_subghz_preset_2fsk_dev47_6khz_async_regs); furi_hal_subghz_load_patable(furi_hal_subghz_preset_2fsk_async_patable); } else if(preset == FuriHalSubGhzPresetMSK99_97KbAsync) { - furi_hal_subghz_load_registers(furi_hal_subghz_preset_msk_99_97kb_async_regs); + furi_hal_subghz_load_registers((uint8_t*)furi_hal_subghz_preset_msk_99_97kb_async_regs); furi_hal_subghz_load_patable(furi_hal_subghz_preset_msk_async_patable); } else if(preset == FuriHalSubGhzPresetGFSK9_99KbAsync) { - furi_hal_subghz_load_registers(furi_hal_subghz_preset_gfsk_9_99kb_async_regs); + furi_hal_subghz_load_registers((uint8_t*)furi_hal_subghz_preset_gfsk_9_99kb_async_regs); furi_hal_subghz_load_patable(furi_hal_subghz_preset_gfsk_async_patable); } else { furi_crash("SubGhz: Missing config."); @@ -123,13 +125,44 @@ void furi_hal_subghz_load_preset(FuriHalSubGhzPreset preset) { furi_hal_subghz.preset = preset; } -void furi_hal_subghz_load_registers(const uint8_t data[][2]) { +void furi_hal_subghz_load_custom_preset(uint8_t* preset_data) { + //load config furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz); cc1101_reset(&furi_hal_spi_bus_handle_subghz); uint32_t i = 0; - while(data[i][0]) { - cc1101_write_reg(&furi_hal_spi_bus_handle_subghz, data[i][0], data[i][1]); - i++; + uint8_t pa[8] = {0}; + while(preset_data[i]) { + cc1101_write_reg(&furi_hal_spi_bus_handle_subghz, preset_data[i], preset_data[i + 1]); + i += 2; + } + furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz); + + //load pa table + memcpy(&pa[0], &preset_data[i + 2], 8); + furi_hal_subghz_load_patable(pa); + furi_hal_subghz.preset = FuriHalSubGhzPresetCustom; + + //show debug + if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { + i = 0; + FURI_LOG_D(TAG, "Loading custom preset"); + while(preset_data[i]) { + FURI_LOG_D(TAG, "Reg[%lu]: %02X=%02X", i, preset_data[i], preset_data[i + 1]); + i += 2; + } + for(uint8_t y = i; y < i + 10; y++) { + FURI_LOG_D(TAG, "PA[%lu]: %02X", y, preset_data[y]); + } + } +} + +void furi_hal_subghz_load_registers(uint8_t* data) { + furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz); + cc1101_reset(&furi_hal_spi_bus_handle_subghz); + uint32_t i = 0; + while(data[i]) { + cc1101_write_reg(&furi_hal_spi_bus_handle_subghz, data[i], data[i + 1]); + i += 2; } furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz); } diff --git a/firmware/targets/f7/stm32wb55xx_flash.ld b/firmware/targets/f7/stm32wb55xx_flash.ld index 1d0e916b0..20314ba3c 100644 --- a/firmware/targets/f7/stm32wb55xx_flash.ld +++ b/firmware/targets/f7/stm32wb55xx_flash.ld @@ -75,6 +75,7 @@ SECTIONS .text : { . = ALIGN(4); + *lib*.a:*(.text .text.*) /* code from libraries before apps */ *(.text) /* .text sections (code) */ *(.text*) /* .text* sections (code) */ *(.glue_7) /* glue arm to thumb code */ diff --git a/firmware/targets/furi_hal_include/furi_hal_mpu.h b/firmware/targets/furi_hal_include/furi_hal_mpu.h new file mode 100644 index 000000000..5dddadeb6 --- /dev/null +++ b/firmware/targets/furi_hal_include/furi_hal_mpu.h @@ -0,0 +1,86 @@ +/** + * @file furi_hal_light.h + * Light control HAL API + */ + +#pragma once + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + FuriHalMpuRegionNULL = 0x00, // region 0 used to protect null pointer dereference + FuriHalMpuRegionStack = 0x01, // region 1 used to protect stack + FuriHalMpuRegion2 = 0x02, + FuriHalMpuRegion3 = 0x03, + FuriHalMpuRegion4 = 0x04, + FuriHalMpuRegion5 = 0x05, + FuriHalMpuRegion6 = 0x06, + FuriHalMpuRegion7 = 0x07, +} FuriHalMpuRegion; + +typedef enum { + FuriHalMPURegionSize32B = 0x04U, + FuriHalMPURegionSize64B = 0x05U, + FuriHalMPURegionSize128B = 0x06U, + FuriHalMPURegionSize256B = 0x07U, + FuriHalMPURegionSize512B = 0x08U, + FuriHalMPURegionSize1KB = 0x09U, + FuriHalMPURegionSize2KB = 0x0AU, + FuriHalMPURegionSize4KB = 0x0BU, + FuriHalMPURegionSize8KB = 0x0CU, + FuriHalMPURegionSize16KB = 0x0DU, + FuriHalMPURegionSize32KB = 0x0EU, + FuriHalMPURegionSize64KB = 0x0FU, + FuriHalMPURegionSize128KB = 0x10U, + FuriHalMPURegionSize256KB = 0x11U, + FuriHalMPURegionSize512KB = 0x12U, + FuriHalMPURegionSize1MB = 0x13U, + FuriHalMPURegionSize2MB = 0x14U, + FuriHalMPURegionSize4MB = 0x15U, + FuriHalMPURegionSize8MB = 0x16U, + FuriHalMPURegionSize16MB = 0x17U, + FuriHalMPURegionSize32MB = 0x18U, + FuriHalMPURegionSize64MB = 0x19U, + FuriHalMPURegionSize128MB = 0x1AU, + FuriHalMPURegionSize256MB = 0x1BU, + FuriHalMPURegionSize512MB = 0x1CU, + FuriHalMPURegionSize1GB = 0x1DU, + FuriHalMPURegionSize2GB = 0x1EU, + FuriHalMPURegionSize4GB = 0x1FU, +} FuriHalMPURegionSize; + +/** + * @brief Initialize memory protection unit + */ +void furi_hal_mpu_init(); + +/** +* @brief Enable memory protection unit +*/ +void furi_hal_mpu_enable(); + +/** +* @brief Disable memory protection unit +*/ +void furi_hal_mpu_disable(); + +void furi_hal_mpu_protect_no_access( + FuriHalMpuRegion region, + uint32_t address, + FuriHalMPURegionSize size); + +void furi_hal_mpu_protect_read_only( + FuriHalMpuRegion region, + uint32_t address, + FuriHalMPURegionSize size); + +void furi_hal_mpu_protect_disable(FuriHalMpuRegion region); + +#ifdef __cplusplus +} +#endif diff --git a/firmware/targets/furi_hal_include/furi_hal_nfc.h b/firmware/targets/furi_hal_include/furi_hal_nfc.h index a25e7c6d3..71186076f 100644 --- a/firmware/targets/furi_hal_include/furi_hal_nfc.h +++ b/firmware/targets/furi_hal_include/furi_hal_nfc.h @@ -10,7 +10,7 @@ #include #include -#include +#include #ifdef __cplusplus extern "C" { diff --git a/firmware/targets/furi_hal_include/furi_hal_subghz.h b/firmware/targets/furi_hal_include/furi_hal_subghz.h index 4c5ad07f0..b6d132ac3 100644 --- a/firmware/targets/furi_hal_include/furi_hal_subghz.h +++ b/firmware/targets/furi_hal_include/furi_hal_subghz.h @@ -22,7 +22,8 @@ typedef enum { FuriHalSubGhzPreset2FSKDev238Async, /**< FM, deviation 2.380371 kHz, asynchronous */ FuriHalSubGhzPreset2FSKDev476Async, /**< FM, deviation 47.60742 kHz, asynchronous */ FuriHalSubGhzPresetMSK99_97KbAsync, /**< MSK, deviation 47.60742 kHz, 99.97Kb/s, asynchronous */ - FuriHalSubGhzPresetGFSK9_99KbAsync /**< GFSK, deviation 19.042969 kHz, 9.996Kb/s, asynchronous */ + FuriHalSubGhzPresetGFSK9_99KbAsync, /**< GFSK, deviation 19.042969 kHz, 9.996Kb/s, asynchronous */ + FuriHalSubGhzPresetCustom, /**Custom Preset*/ } FuriHalSubGhzPreset; /** Switchable Radio Paths */ @@ -74,11 +75,17 @@ void furi_hal_subghz_dump_state(); */ void furi_hal_subghz_load_preset(FuriHalSubGhzPreset preset); +/** Load custom registers from preset + * + * @param preset_data registers to load + */ +void furi_hal_subghz_load_custom_preset(uint8_t* preset_data); + /** Load registers * * @param data Registers data */ -void furi_hal_subghz_load_registers(const uint8_t data[][2]); +void furi_hal_subghz_load_registers(uint8_t* data); /** Load PATABLE * diff --git a/furi/core/common_defines.h b/furi/core/common_defines.h index d75f7592d..c211ad7ee 100644 --- a/furi/core/common_defines.h +++ b/furi/core/common_defines.h @@ -88,6 +88,14 @@ extern "C" { #define FURI_BIT(x, n) (((x) >> (n)) & 1) #endif +#ifndef FURI_BIT_SET +#define FURI_BIT_SET(x, n) ((x) |= (1 << (n))) +#endif + +#ifndef FURI_BIT_CLEAR +#define FURI_BIT_CLEAR(x, n) ((x) &= ~(1 << (n))) +#endif + #ifndef FURI_IS_IRQ_MASKED #define FURI_IS_IRQ_MASKED() (__get_PRIMASK() != 0U) #endif diff --git a/furi/core/log.c b/furi/core/log.c index 8cf904855..8a36a930b 100644 --- a/furi/core/log.c +++ b/furi/core/log.c @@ -22,24 +22,60 @@ void furi_log_init() { furi_log.mutex = furi_mutex_alloc(FuriMutexTypeNormal); } -void furi_log_print(FuriLogLevel level, const char* format, ...) { +void furi_log_print_format(FuriLogLevel level, const char* tag, const char* format, ...) { if(level <= furi_log.log_level && furi_mutex_acquire(furi_log.mutex, FuriWaitForever) == FuriStatusOk) { string_t string; + string_init(string); + + const char* color = FURI_LOG_CLR_RESET; + const char* log_letter = " "; + switch(level) { + case FuriLogLevelError: + color = FURI_LOG_CLR_E; + log_letter = "E"; + break; + case FuriLogLevelWarn: + color = FURI_LOG_CLR_W; + log_letter = "W"; + break; + case FuriLogLevelInfo: + color = FURI_LOG_CLR_I; + log_letter = "I"; + break; + case FuriLogLevelDebug: + color = FURI_LOG_CLR_D; + log_letter = "D"; + break; + case FuriLogLevelTrace: + color = FURI_LOG_CLR_T; + log_letter = "T"; + break; + default: + break; + } // Timestamp - string_init_printf(string, "%lu ", furi_log.timetamp()); + string_printf( + string, + "%lu %s[%s][%s] " FURI_LOG_CLR_RESET, + furi_log.timetamp(), + color, + log_letter, + tag); furi_log.puts(string_get_cstr(string)); - string_clear(string); + string_reset(string); va_list args; va_start(args, format); - string_init_vprintf(string, format, args); + string_vprintf(string, format, args); va_end(args); furi_log.puts(string_get_cstr(string)); string_clear(string); + furi_log.puts("\r\n"); + furi_mutex_release(furi_log.mutex); } } diff --git a/furi/core/log.h b/furi/core/log.h index 6d23a9c29..30026fc44 100644 --- a/furi/core/log.h +++ b/furi/core/log.h @@ -44,13 +44,14 @@ typedef uint32_t (*FuriLogTimestamp)(void); /** Initialize logging */ void furi_log_init(); -/** Log record - * - * @param[in] level The level - * @param[in] format The format - * @param[in] VA args +/** Print log record + * + * @param level + * @param tag + * @param format + * @param ... */ -void furi_log_print(FuriLogLevel level, const char* format, ...); +void furi_log_print_format(FuriLogLevel level, const char* tag, const char* format, ...); /** Set log level * @@ -76,11 +77,6 @@ void furi_log_set_puts(FuriLogPuts puts); */ void furi_log_set_timestamp(FuriLogTimestamp timestamp); -#define FURI_LOG_FORMAT(log_letter, tag, format) \ - FURI_LOG_CLR_##log_letter "[" #log_letter "][" tag "]: " FURI_LOG_CLR_RESET format "\r\n" -#define FURI_LOG_SHOW(tag, format, log_level, log_letter, ...) \ - furi_log_print(log_level, FURI_LOG_FORMAT(log_letter, tag, format), ##__VA_ARGS__) - /** Log methods * * @param tag The application tag @@ -88,13 +84,15 @@ void furi_log_set_timestamp(FuriLogTimestamp timestamp); * @param ... VA Args */ #define FURI_LOG_E(tag, format, ...) \ - FURI_LOG_SHOW(tag, format, FuriLogLevelError, E, ##__VA_ARGS__) -#define FURI_LOG_W(tag, format, ...) FURI_LOG_SHOW(tag, format, FuriLogLevelWarn, W, ##__VA_ARGS__) -#define FURI_LOG_I(tag, format, ...) FURI_LOG_SHOW(tag, format, FuriLogLevelInfo, I, ##__VA_ARGS__) + furi_log_print_format(FuriLogLevelError, tag, format, ##__VA_ARGS__) +#define FURI_LOG_W(tag, format, ...) \ + furi_log_print_format(FuriLogLevelWarn, tag, format, ##__VA_ARGS__) +#define FURI_LOG_I(tag, format, ...) \ + furi_log_print_format(FuriLogLevelInfo, tag, format, ##__VA_ARGS__) #define FURI_LOG_D(tag, format, ...) \ - FURI_LOG_SHOW(tag, format, FuriLogLevelDebug, D, ##__VA_ARGS__) + furi_log_print_format(FuriLogLevelDebug, tag, format, ##__VA_ARGS__) #define FURI_LOG_T(tag, format, ...) \ - FURI_LOG_SHOW(tag, format, FuriLogLevelTrace, T, ##__VA_ARGS__) + furi_log_print_format(FuriLogLevelTrace, tag, format, ##__VA_ARGS__) #ifdef __cplusplus } diff --git a/furi/core/memmgr.c b/furi/core/memmgr.c index 01cf573eb..80f87b930 100644 --- a/furi/core/memmgr.c +++ b/furi/core/memmgr.c @@ -36,10 +36,8 @@ void* calloc(size_t count, size_t size) { } char* strdup(const char* s) { - const char* s_null = s; - if(s_null == NULL) { - return NULL; - } + // arg s marked as non-null, so we need hack to check for NULL + furi_check(((uint32_t)s << 2) != 0); size_t siz = strlen(s) + 1; char* y = pvPortMalloc(siz); diff --git a/furi/core/stdglue.c b/furi/core/stdglue.c deleted file mode 100644 index 573277aa5..000000000 --- a/furi/core/stdglue.c +++ /dev/null @@ -1,102 +0,0 @@ -#include "stdglue.h" -#include "check.h" -#include "memmgr.h" - -#include -#include - -#include -#include - -DICT_DEF2( - FuriStdglueCallbackDict, - uint32_t, - M_DEFAULT_OPLIST, - FuriStdglueWriteCallback, - M_PTR_OPLIST) - -typedef struct { - FuriMutex* mutex; - FuriStdglueCallbackDict_t thread_outputs; -} FuriStdglue; - -static FuriStdglue* furi_stdglue = NULL; - -static ssize_t stdout_write(void* _cookie, const char* data, size_t size) { - furi_assert(furi_stdglue); - bool consumed = false; - FuriThreadId task_id = furi_thread_get_current_id(); - if(xTaskGetSchedulerState() == taskSCHEDULER_RUNNING && task_id && - furi_mutex_acquire(furi_stdglue->mutex, FuriWaitForever) == FuriStatusOk) { - // We are in the thread context - // Handle thread callbacks - FuriStdglueWriteCallback* callback_ptr = - FuriStdglueCallbackDict_get(furi_stdglue->thread_outputs, (uint32_t)task_id); - if(callback_ptr) { - (*callback_ptr)(_cookie, data, size); - consumed = true; - } - furi_check(furi_mutex_release(furi_stdglue->mutex) == FuriStatusOk); - } - // Flush - if(data == 0) { - /* - * This means that we should flush internal buffers. Since we - * don't we just return. (Remember, "handle" == -1 means that all - * handles should be flushed.) - */ - return 0; - } - // Debug uart - if(!consumed) furi_hal_console_tx((const uint8_t*)data, size); - // All data consumed - return size; -} - -void furi_stdglue_init() { - furi_stdglue = malloc(sizeof(FuriStdglue)); - // Init outputs structures - furi_stdglue->mutex = furi_mutex_alloc(FuriMutexTypeNormal); - furi_check(furi_stdglue->mutex); - FuriStdglueCallbackDict_init(furi_stdglue->thread_outputs); - // Prepare and set stdout descriptor - FILE* fp = fopencookie( - NULL, - "w", - (cookie_io_functions_t){ - .read = NULL, - .write = stdout_write, - .seek = NULL, - .close = NULL, - }); - setvbuf(fp, NULL, _IOLBF, 0); - stdout = fp; -} - -bool furi_stdglue_set_thread_stdout_callback(FuriStdglueWriteCallback callback) { - furi_assert(furi_stdglue); - FuriThreadId task_id = furi_thread_get_current_id(); - if(task_id) { - furi_check(furi_mutex_acquire(furi_stdglue->mutex, FuriWaitForever) == FuriStatusOk); - if(callback) { - FuriStdglueCallbackDict_set_at( - furi_stdglue->thread_outputs, (uint32_t)task_id, callback); - } else { - FuriStdglueCallbackDict_erase(furi_stdglue->thread_outputs, (uint32_t)task_id); - } - furi_check(furi_mutex_release(furi_stdglue->mutex) == FuriStatusOk); - return true; - } else { - return false; - } -} - -void __malloc_lock(struct _reent* REENT) { - UNUSED(REENT); - vTaskSuspendAll(); -} - -void __malloc_unlock(struct _reent* REENT) { - UNUSED(REENT); - xTaskResumeAll(); -} diff --git a/furi/core/stdglue.h b/furi/core/stdglue.h deleted file mode 100644 index 800fcf928..000000000 --- a/furi/core/stdglue.h +++ /dev/null @@ -1,36 +0,0 @@ -/** - * @file stdglue.h - * Furi: stdlibc glue - */ - -#pragma once - -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -/** Write callback - * @param _cookie pointer to cookie (see stdio gnu extension) - * @param data pointer to data - * @param size data size @warnign your handler must consume everything - */ -typedef void (*FuriStdglueWriteCallback)(void* _cookie, const char* data, size_t size); - -/** Initialized std library glue code */ -void furi_stdglue_init(); - -/** Set STDOUT callback for your thread - * - * @param callback callback or NULL to clear - * - * @return true on success, otherwise fail - * @warning function is thread aware, use this API from the same thread - */ -bool furi_stdglue_set_thread_stdout_callback(FuriStdglueWriteCallback callback); - -#ifdef __cplusplus -} -#endif diff --git a/furi/core/thread.c b/furi/core/thread.c index 097cedef2..044f83711 100644 --- a/furi/core/thread.c +++ b/furi/core/thread.c @@ -4,12 +4,21 @@ #include "memmgr_heap.h" #include "check.h" #include "common_defines.h" +#include "mutex.h" #include #include +#include #define THREAD_NOTIFY_INDEX 1 // Index 0 is used for stream buffers +typedef struct FuriThreadStdout FuriThreadStdout; + +struct FuriThreadStdout { + FuriThreadStdoutWriteCallback write_callback; + string_t buffer; +}; + struct FuriThread { FuriThreadState state; int32_t ret; @@ -27,8 +36,13 @@ struct FuriThread { TaskHandle_t task_handle; bool heap_trace_enabled; size_t heap_size; + + FuriThreadStdout output; }; +static size_t __furi_thread_stdout_write(FuriThread* thread, const char* data, size_t size); +static int32_t __furi_thread_stdout_flush(FuriThread* thread); + /** Catch threads that are trying to exit wrong way */ __attribute__((__noreturn__)) void furi_thread_catch() { asm volatile("nop"); // extra magic @@ -47,6 +61,10 @@ static void furi_thread_body(void* context) { furi_assert(context); FuriThread* thread = context; + // store thread instance to thread local storage + furi_assert(pvTaskGetThreadLocalStoragePointer(NULL, 0) == NULL); + vTaskSetThreadLocalStoragePointer(NULL, 0, thread); + furi_assert(thread->state == FuriThreadStateStarting); furi_thread_set_state(thread, FuriThreadStateRunning); @@ -66,12 +84,18 @@ static void furi_thread_body(void* context) { furi_assert(thread->state == FuriThreadStateRunning); furi_thread_set_state(thread, FuriThreadStateStopped); + // clear thread local storage + __furi_thread_stdout_flush(thread); + furi_assert(pvTaskGetThreadLocalStoragePointer(NULL, 0) != NULL); + vTaskSetThreadLocalStoragePointer(NULL, 0, NULL); + vTaskDelete(thread->task_handle); furi_thread_catch(); } FuriThread* furi_thread_alloc() { FuriThread* thread = malloc(sizeof(FuriThread)); + string_init(thread->output.buffer); return thread; } @@ -81,6 +105,8 @@ void furi_thread_free(FuriThread* thread) { furi_assert(thread->state == FuriThreadStateStopped); if(thread->name) free((void*)thread->name); + string_clear(thread->output.buffer); + free(thread); } @@ -88,7 +114,7 @@ void furi_thread_set_name(FuriThread* thread, const char* name) { furi_assert(thread); furi_assert(thread->state == FuriThreadStateStopped); if(thread->name) free((void*)thread->name); - thread->name = strdup(name); + thread->name = name ? strdup(name) : NULL; } void furi_thread_set_stack_size(FuriThread* thread, size_t stack_size) { @@ -199,6 +225,12 @@ FuriThreadId furi_thread_get_current_id() { return xTaskGetCurrentTaskHandle(); } +FuriThread* furi_thread_get_current() { + FuriThread* thread = pvTaskGetThreadLocalStoragePointer(NULL, 0); + furi_assert(thread != NULL); + return thread; +} + void furi_thread_yield() { furi_assert(!FURI_IS_IRQ_MODE()); taskYIELD(); @@ -408,3 +440,59 @@ uint32_t furi_thread_get_stack_space(FuriThreadId thread_id) { return (sz); } + +static size_t __furi_thread_stdout_write(FuriThread* thread, const char* data, size_t size) { + if(thread->output.write_callback != NULL) { + thread->output.write_callback(data, size); + } else { + furi_hal_console_tx((const uint8_t*)data, size); + } + return size; +} + +static int32_t __furi_thread_stdout_flush(FuriThread* thread) { + string_ptr buffer = thread->output.buffer; + size_t size = string_size(buffer); + if(size > 0) { + __furi_thread_stdout_write(thread, string_get_cstr(buffer), size); + string_reset(buffer); + } + return 0; +} + +bool furi_thread_set_stdout_callback(FuriThreadStdoutWriteCallback callback) { + FuriThread* thread = furi_thread_get_current(); + + __furi_thread_stdout_flush(thread); + thread->output.write_callback = callback; + + return true; +} + +size_t furi_thread_stdout_write(const char* data, size_t size) { + FuriThread* thread = furi_thread_get_current(); + + if(size == 0 || data == NULL) { + return __furi_thread_stdout_flush(thread); + } else { + if(data[size - 1] == '\n') { + // if the last character is a newline, we can flush buffer and write data as is, wo buffers + __furi_thread_stdout_flush(thread); + __furi_thread_stdout_write(thread, data, size); + } else { + // string_cat doesn't work here because we need to write the exact size data + for(size_t i = 0; i < size; i++) { + string_push_back(thread->output.buffer, data[i]); + if(data[i] == '\n') { + __furi_thread_stdout_flush(thread); + } + } + } + } + + return size; +} + +int32_t furi_thread_stdout_flush() { + return __furi_thread_stdout_flush(furi_thread_get_current()); +} \ No newline at end of file diff --git a/furi/core/thread.h b/furi/core/thread.h index 34eb39f03..7f746f03f 100644 --- a/furi/core/thread.h +++ b/furi/core/thread.h @@ -42,6 +42,12 @@ typedef void* FuriThreadId; */ typedef int32_t (*FuriThreadCallback)(void* context); +/** Write to stdout callback + * @param data pointer to data + * @param size data size @warning your handler must consume everything + */ +typedef void (*FuriThreadStdoutWriteCallback)(const char* data, size_t size); + /** FuriThread state change calback called upon thread state change * @param state new thread state * @param context callback context @@ -177,6 +183,12 @@ int32_t furi_thread_get_return_code(FuriThread* thread); */ FuriThreadId furi_thread_get_current_id(); +/** Get FuriThread instance for current thread + * + * @return FuriThread* + */ +FuriThread* furi_thread_get_current(); + /** Return control to scheduler */ void furi_thread_yield(); @@ -194,6 +206,29 @@ const char* furi_thread_get_name(FuriThreadId thread_id); uint32_t furi_thread_get_stack_space(FuriThreadId thread_id); +/** Set STDOUT callback for thread + * + * @param callback callback or NULL to clear + * + * @return true on success, otherwise fail + */ +bool furi_thread_set_stdout_callback(FuriThreadStdoutWriteCallback callback); + +/** Write data to buffered STDOUT + * + * @param data input data + * @param size input data size + * + * @return size_t written data size + */ +size_t furi_thread_stdout_write(const char* data, size_t size); + +/** Flush data to STDOUT + * + * @return int32_t error code + */ +int32_t furi_thread_stdout_flush(); + #ifdef __cplusplus } #endif diff --git a/furi/furi.c b/furi/furi.c index e6848624b..76aed024f 100644 --- a/furi/furi.c +++ b/furi/furi.c @@ -8,7 +8,6 @@ void furi_init() { furi_log_init(); furi_record_init(); - furi_stdglue_init(); } void furi_run() { diff --git a/furi/furi.h b/furi/furi.h index d78129a82..68914b502 100644 --- a/furi/furi.h +++ b/furi/furi.h @@ -14,7 +14,6 @@ #include #include #include -#include #include #include #include diff --git a/lib/ReadMe.md b/lib/ReadMe.md index 0bdf3d63a..9cd846a0a 100644 --- a/lib/ReadMe.md +++ b/lib/ReadMe.md @@ -15,7 +15,7 @@ - `microtar` - TAR archive support library - `mlib` - Algorithms and containers - `nanopb` - Nano Protobuf library -- `nfc_protocols` - Nfc protocols library +- `nfc` - Nfc library - `one_wire` - One wire library - `qrcode` - Qr code generator library - `ST25RFAL002` - ST253916 driver and NFC hal diff --git a/lib/SConscript b/lib/SConscript index a3617c5d1..5e5bb2eaa 100644 --- a/lib/SConscript +++ b/lib/SConscript @@ -7,13 +7,14 @@ env.Append( "lib/drivers", "lib/flipper_format", "lib/infrared", - "lib/nfc_protocols", + "lib/nfc", "lib/one_wire", "lib/ST25RFAL002", "lib/subghz", "lib/toolbox", "lib/u8g2", "lib/update_util", + "lib/print", ] ) @@ -43,7 +44,7 @@ env.Append( # fnv1a-hash # micro-ecc # microtar -# nfc_protocols +# nfc # one_wire # qrcode # u8g2 @@ -60,6 +61,7 @@ libs = env.BuildModules( [ "STM32CubeWB", "freertos", + "print", "microtar", "toolbox", "ST25RFAL002", @@ -69,10 +71,11 @@ libs = env.BuildModules( "flipper_format", "infrared", "littlefs", + "mbedtls", "subghz", + "nfc", "appframe", "misc", - "mbedtls", "loclass", ], ) diff --git a/lib/ST25RFAL002/include/rfal_picopass.h b/lib/ST25RFAL002/include/rfal_picopass.h index baa8ea6f1..5b8150251 100644 --- a/lib/ST25RFAL002/include/rfal_picopass.h +++ b/lib/ST25RFAL002/include/rfal_picopass.h @@ -51,10 +51,6 @@ typedef struct { uint8_t crc[2]; } rfalPicoPassReadBlockRes; -typedef struct { - rfalPicoPassReadBlockRes block[4]; -} ApplicationArea; - ReturnCode rfalPicoPassPollerInitialize(void); ReturnCode rfalPicoPassPollerCheckPresence(void); ReturnCode rfalPicoPassPollerIdentify(rfalPicoPassIdentifyRes* idRes); diff --git a/lib/flipper_format/flipper_format.h b/lib/flipper_format/flipper_format.h index 3f7b71af8..6163dee0e 100644 --- a/lib/flipper_format/flipper_format.h +++ b/lib/flipper_format/flipper_format.h @@ -50,7 +50,7 @@ * const uint16_t array_size = 4; * const uint8_t* array[array_size] = {0x00, 0x01, 0xFF, 0xA3}; * - * if(!flipper_format_file_open_new(format, "/ext/flipper_format_test")) break; + * if(!flipper_format_file_open_new(format, EXT_PATH("flipper_format_test"))) break; * if(!flipper_format_write_header_cstr(format, "Flipper Test File", version)) break; * if(!flipper_format_write_comment_cstr(format, "Just test file")) break; * if(!flipper_format_write_string_cstr(format, "String", string_value)) break; @@ -78,7 +78,7 @@ * string_init(file_type); * string_init(string_value); * - * if(!flipper_format_file_open_existing(file, "/ext/flipper_format_test")) break; + * if(!flipper_format_file_open_existing(file, EXT_PATH("flipper_format_test"))) break; * if(!flipper_format_read_header(file, file_type, &version)) break; * if(!flipper_format_read_string(file, "String", string_value)) break; * if(!flipper_format_read_uint32(file, "UINT", &uint32_value, 1)) break; @@ -116,7 +116,7 @@ FlipperFormat* flipper_format_string_alloc(); FlipperFormat* flipper_format_file_alloc(Storage* storage); /** - * Allocate FlipperFormat as file, buffered read-only mode. + * Allocate FlipperFormat as file, buffered mode. * @return FlipperFormat* pointer to a FlipperFormat instance */ FlipperFormat* flipper_format_buffered_file_alloc(Storage* storage); @@ -131,7 +131,7 @@ FlipperFormat* flipper_format_buffered_file_alloc(Storage* storage); bool flipper_format_file_open_existing(FlipperFormat* flipper_format, const char* path); /** - * Open existing file, read-only with buffered read operations. + * Open existing file, buffered mode. * Use only if FlipperFormat allocated as a file. * @param flipper_format Pointer to a FlipperFormat instance * @param path File path diff --git a/lib/infrared/worker/infrared_worker.c b/lib/infrared/worker/infrared_worker.c index becd8d882..2b4e3cdbb 100644 --- a/lib/infrared/worker/infrared_worker.c +++ b/lib/infrared/worker/infrared_worker.c @@ -236,7 +236,7 @@ InfraredWorker* infrared_worker_alloc() { instance->infrared_decoder = infrared_alloc_decoder(); instance->infrared_encoder = infrared_alloc_encoder(); instance->blink_enable = false; - instance->notification = furi_record_open("notification"); + instance->notification = furi_record_open(RECORD_NOTIFICATION); instance->state = InfraredWorkerStateIdle; return instance; @@ -246,7 +246,7 @@ void infrared_worker_free(InfraredWorker* instance) { furi_assert(instance); furi_assert(instance->state == InfraredWorkerStateIdle); - furi_record_close("notification"); + furi_record_close(RECORD_NOTIFICATION); infrared_free_decoder(instance->infrared_decoder); infrared_free_encoder(instance->infrared_encoder); vStreamBufferDelete(instance->stream); diff --git a/lib/loclass.scons b/lib/loclass.scons index ba43dd6e7..a657f0424 100644 --- a/lib/loclass.scons +++ b/lib/loclass.scons @@ -4,8 +4,6 @@ env.Append( CPPPATH=[ "#/lib/loclass", ], - CPPDEFINES=[ - ], ) diff --git a/lib/mbedtls.scons b/lib/mbedtls.scons index 35de7e6fc..e39d9dae8 100644 --- a/lib/mbedtls.scons +++ b/lib/mbedtls.scons @@ -5,15 +5,17 @@ env.Append( "#/lib/mbedtls", "#/lib/mbedtls/include", ], - CPPDEFINES=[ - ], ) libenv = env.Clone(FW_LIB_NAME="mbedtls") libenv.ApplyLibFlags() -sources = ["mbedtls/library/des.c", "mbedtls/library/platform_util.c"] +sources = [ + "mbedtls/library/des.c", + "mbedtls/library/sha1.c", + "mbedtls/library/platform_util.c", +] lib = libenv.StaticLibrary("${FW_LIB_NAME}", sources) libenv.Install("${LIB_DIST_DIR}", lib) diff --git a/lib/misc.scons b/lib/misc.scons index 91a11ff64..5a826b18d 100644 --- a/lib/misc.scons +++ b/lib/misc.scons @@ -7,7 +7,6 @@ env.Append( "#/lib/heatshrink", "#/lib/micro-ecc", "#/lib/nanopb", - "#/lib/nfc_protocols", "#/lib/u8g2", ], CPPDEFINES=[ @@ -24,7 +23,6 @@ sources = [] libs_recurse = [ "digital_signal", "micro-ecc", - "nfc_protocols", "one_wire", "u8g2", "update_util", diff --git a/lib/nfc/SConscript b/lib/nfc/SConscript new file mode 100644 index 000000000..657f3a9e5 --- /dev/null +++ b/lib/nfc/SConscript @@ -0,0 +1,16 @@ +Import("env") + +env.Append( + CPPPATH=[ + "#/lib/nfc", + ], +) + +libenv = env.Clone(FW_LIB_NAME="nfc") +libenv.ApplyLibFlags() + +sources = libenv.GlobRecursive("*.c*") + +lib = libenv.StaticLibrary("${FW_LIB_NAME}", sources) +libenv.Install("${LIB_DIST_DIR}", lib) +Return("lib") diff --git a/lib/nfc/helpers/mf_classic_dict.c b/lib/nfc/helpers/mf_classic_dict.c new file mode 100644 index 000000000..410ddbd8b --- /dev/null +++ b/lib/nfc/helpers/mf_classic_dict.c @@ -0,0 +1,148 @@ +#include "mf_classic_dict.h" + +#include +#include + +#define MF_CLASSIC_DICT_FLIPPER_PATH EXT_PATH("nfc/assets/mf_classic_dict.nfc") +#define MF_CLASSIC_DICT_USER_PATH EXT_PATH("nfc/assets/mf_classic_dict_user.nfc") + +#define TAG "MfClassicDict" + +#define NFC_MF_CLASSIC_KEY_LEN (13) + +struct MfClassicDict { + Stream* stream; + uint32_t total_keys; +}; + +bool mf_classic_dict_check_presence(MfClassicDictType dict_type) { + Storage* storage = furi_record_open(RECORD_STORAGE); + + bool dict_present = false; + if(dict_type == MfClassicDictTypeFlipper) { + dict_present = storage_common_stat(storage, MF_CLASSIC_DICT_FLIPPER_PATH, NULL) == FSE_OK; + } else if(dict_type == MfClassicDictTypeUser) { + dict_present = storage_common_stat(storage, MF_CLASSIC_DICT_USER_PATH, NULL) == FSE_OK; + } + + furi_record_close(RECORD_STORAGE); + + return dict_present; +} + +MfClassicDict* mf_classic_dict_alloc(MfClassicDictType dict_type) { + MfClassicDict* dict = malloc(sizeof(MfClassicDict)); + Storage* storage = furi_record_open(RECORD_STORAGE); + dict->stream = buffered_file_stream_alloc(storage); + furi_record_close(RECORD_STORAGE); + + bool dict_loaded = false; + do { + if(dict_type == MfClassicDictTypeFlipper) { + if(!buffered_file_stream_open( + dict->stream, MF_CLASSIC_DICT_FLIPPER_PATH, FSAM_READ, FSOM_OPEN_EXISTING)) { + buffered_file_stream_close(dict->stream); + break; + } + } else if(dict_type == MfClassicDictTypeUser) { + if(!buffered_file_stream_open( + dict->stream, MF_CLASSIC_DICT_USER_PATH, FSAM_READ_WRITE, FSOM_OPEN_ALWAYS)) { + buffered_file_stream_close(dict->stream); + break; + } + } + + // Read total amount of keys + string_t next_line; + string_init(next_line); + while(true) { + if(!stream_read_line(dict->stream, next_line)) break; + if(string_get_char(next_line, 0) == '#') continue; + if(string_size(next_line) != NFC_MF_CLASSIC_KEY_LEN) continue; + dict->total_keys++; + } + string_clear(next_line); + stream_rewind(dict->stream); + + dict_loaded = true; + FURI_LOG_I(TAG, "Loaded dictionary with %d keys", dict->total_keys); + } while(false); + + if(!dict_loaded) { + buffered_file_stream_close(dict->stream); + free(dict); + dict = NULL; + } + + return dict; +} + +void mf_classic_dict_free(MfClassicDict* dict) { + furi_assert(dict); + furi_assert(dict->stream); + + buffered_file_stream_close(dict->stream); + stream_free(dict->stream); + free(dict); +} + +uint32_t mf_classic_dict_get_total_keys(MfClassicDict* dict) { + furi_assert(dict); + + return dict->total_keys; +} + +bool mf_classic_dict_get_next_key(MfClassicDict* dict, uint64_t* key) { + furi_assert(dict); + furi_assert(dict->stream); + + uint8_t key_byte_tmp = 0; + string_t next_line; + string_init(next_line); + + bool key_read = false; + *key = 0ULL; + while(!key_read) { + if(!stream_read_line(dict->stream, next_line)) break; + if(string_get_char(next_line, 0) == '#') continue; + if(string_size(next_line) != NFC_MF_CLASSIC_KEY_LEN) continue; + for(uint8_t i = 0; i < 12; i += 2) { + args_char_to_hex( + string_get_char(next_line, i), string_get_char(next_line, i + 1), &key_byte_tmp); + *key |= (uint64_t)key_byte_tmp << 8 * (5 - i / 2); + } + key_read = true; + } + + string_clear(next_line); + return key_read; +} + +bool mf_classic_dict_rewind(MfClassicDict* dict) { + furi_assert(dict); + furi_assert(dict->stream); + + return stream_rewind(dict->stream); +} + +bool mf_classic_dict_add_key(MfClassicDict* dict, uint8_t* key) { + furi_assert(dict); + furi_assert(dict->stream); + + string_t key_str; + string_init(key_str); + for(size_t i = 0; i < 6; i++) { + string_cat_printf(key_str, "%02X", key[i]); + } + string_cat_printf(key_str, "\n"); + + bool key_added = false; + do { + if(!stream_seek(dict->stream, 0, StreamOffsetFromEnd)) break; + if(!stream_insert_string(dict->stream, key_str)) break; + key_added = true; + } while(false); + + string_clear(key_str); + return key_added; +} diff --git a/lib/nfc/helpers/mf_classic_dict.h b/lib/nfc/helpers/mf_classic_dict.h new file mode 100644 index 000000000..2654e668c --- /dev/null +++ b/lib/nfc/helpers/mf_classic_dict.h @@ -0,0 +1,28 @@ +#pragma once + +#include +#include +#include +#include +#include + +typedef enum { + MfClassicDictTypeUser, + MfClassicDictTypeFlipper, +} MfClassicDictType; + +typedef struct MfClassicDict MfClassicDict; + +bool mf_classic_dict_check_presence(MfClassicDictType dict_type); + +MfClassicDict* mf_classic_dict_alloc(MfClassicDictType dict_type); + +void mf_classic_dict_free(MfClassicDict* dict); + +uint32_t mf_classic_dict_get_total_keys(MfClassicDict* dict); + +bool mf_classic_dict_get_next_key(MfClassicDict* dict, uint64_t* key); + +bool mf_classic_dict_rewind(MfClassicDict* dict); + +bool mf_classic_dict_add_key(MfClassicDict* dict, uint8_t* key); diff --git a/applications/nfc/helpers/nfc_debug_pcap.c b/lib/nfc/helpers/nfc_debug_pcap.c similarity index 98% rename from applications/nfc/helpers/nfc_debug_pcap.c rename to lib/nfc/helpers/nfc_debug_pcap.c index d340791b2..48d72bfbf 100644 --- a/applications/nfc/helpers/nfc_debug_pcap.c +++ b/lib/nfc/helpers/nfc_debug_pcap.c @@ -15,7 +15,7 @@ #define DATA_PICC_TO_PCD_CRC_DROPPED 0xFB #define DATA_PCD_TO_PICC_CRC_DROPPED 0xFA -#define NFC_DEBUG_PCAP_FILENAME "/ext/nfc/debug.pcap" +#define NFC_DEBUG_PCAP_FILENAME EXT_PATH("nfc/debug.pcap") #define NFC_DEBUG_PCAP_BUFFER_SIZE 64 struct NfcDebugPcapWorker { diff --git a/applications/nfc/helpers/nfc_debug_pcap.h b/lib/nfc/helpers/nfc_debug_pcap.h similarity index 100% rename from applications/nfc/helpers/nfc_debug_pcap.h rename to lib/nfc/helpers/nfc_debug_pcap.h diff --git a/applications/nfc/nfc_device.c b/lib/nfc/nfc_device.c similarity index 77% rename from applications/nfc/nfc_device.c rename to lib/nfc/nfc_device.c index e4f9ac565..9da891327 100644 --- a/applications/nfc/nfc_device.c +++ b/lib/nfc/nfc_device.c @@ -3,29 +3,40 @@ #include "m-string.h" #include "nfc_types.h" -#include +#include +#include +#include #include +#define NFC_DEVICE_KEYS_FOLDER EXT_PATH("nfc/cache") +#define NFC_DEVICE_KEYS_EXTENSION ".keys" + static const char* nfc_file_header = "Flipper NFC device"; static const uint32_t nfc_file_version = 2; +static const char* nfc_keys_file_header = "Flipper NFC keys"; +static const uint32_t nfc_keys_file_version = 1; + // Protocols format versions -static const uint32_t nfc_mifare_classic_data_format_version = 1; +static const uint32_t nfc_mifare_classic_data_format_version = 2; +static const uint32_t nfc_mifare_ultralight_data_format_version = 1; NfcDevice* nfc_device_alloc() { NfcDevice* nfc_dev = malloc(sizeof(NfcDevice)); - nfc_dev->storage = furi_record_open("storage"); - nfc_dev->dialogs = furi_record_open("dialogs"); + nfc_dev->storage = furi_record_open(RECORD_STORAGE); + nfc_dev->dialogs = furi_record_open(RECORD_DIALOGS); string_init(nfc_dev->load_path); + string_init(nfc_dev->dev_data.parsed_data); return nfc_dev; } void nfc_device_free(NfcDevice* nfc_dev) { furi_assert(nfc_dev); nfc_device_clear(nfc_dev); - furi_record_close("storage"); - furi_record_close("dialogs"); + furi_record_close(RECORD_STORAGE); + furi_record_close(RECORD_DIALOGS); string_clear(nfc_dev->load_path); + string_clear(nfc_dev->dev_data.parsed_data); free(nfc_dev); } @@ -87,6 +98,9 @@ static bool nfc_device_save_mifare_ul_data(FlipperFormat* file, NfcDevice* dev) // Save Mifare Ultralight specific data do { if(!flipper_format_write_comment_cstr(file, "Mifare Ultralight specific data")) break; + if(!flipper_format_write_uint32( + file, "Data format version", &nfc_mifare_ultralight_data_format_version, 1)) + break; if(!flipper_format_write_hex(file, "Signature", data->signature, sizeof(data->signature))) break; if(!flipper_format_write_hex( @@ -111,6 +125,8 @@ static bool nfc_device_save_mifare_ul_data(FlipperFormat* file, NfcDevice* dev) // Write pages data uint32_t pages_total = data->data_size / 4; if(!flipper_format_write_uint32(file, "Pages total", &pages_total, 1)) break; + uint32_t pages_read = data->data_read / 4; + if(!flipper_format_write_uint32(file, "Pages read", &pages_read, 1)) break; bool pages_saved = true; for(uint16_t i = 0; i < data->data_size; i += 4) { string_printf(temp_str, "Page %d", i / 4); @@ -138,8 +154,14 @@ bool nfc_device_load_mifare_ul_data(FlipperFormat* file, NfcDevice* dev) { MfUltralightData* data = &dev->dev_data.mf_ul_data; string_t temp_str; string_init(temp_str); + uint32_t data_format_version = 0; do { + // Read Mifare Ultralight format version + if(!flipper_format_read_uint32(file, "Data format version", &data_format_version, 1)) { + if(!flipper_format_rewind(file)) break; + } + // Read signature if(!flipper_format_read_hex(file, "Signature", data->signature, sizeof(data->signature))) break; @@ -163,11 +185,18 @@ bool nfc_device_load_mifare_ul_data(FlipperFormat* file, NfcDevice* dev) { } if(!counters_parsed) break; // Read pages - uint32_t pages = 0; - if(!flipper_format_read_uint32(file, "Pages total", &pages, 1)) break; - data->data_size = pages * 4; + uint32_t pages_total = 0; + if(!flipper_format_read_uint32(file, "Pages total", &pages_total, 1)) break; + uint32_t pages_read = 0; + if(data_format_version < nfc_mifare_ultralight_data_format_version) { + pages_read = pages_total; + } else { + if(!flipper_format_read_uint32(file, "Pages read", &pages_read, 1)) break; + } + data->data_size = pages_total * 4; + data->data_read = pages_read * 4; bool pages_parsed = true; - for(uint16_t i = 0; i < pages; i++) { + for(uint16_t i = 0; i < pages_total; i++) { string_printf(temp_str, "Page %d", i); if(!flipper_format_read_hex(file, string_get_cstr(temp_str), &data->data[i * 4], 4)) { pages_parsed = false; @@ -648,6 +677,52 @@ bool nfc_device_load_bank_card_data(FlipperFormat* file, NfcDevice* dev) { return parsed; } +static void nfc_device_write_mifare_classic_block( + string_t block_str, + MfClassicData* data, + uint8_t block_num) { + string_reset(block_str); + bool is_sec_trailer = mf_classic_is_sector_trailer(block_num); + if(is_sec_trailer) { + uint8_t sector_num = mf_classic_get_sector_by_block(block_num); + MfClassicSectorTrailer* sec_tr = mf_classic_get_sector_trailer_by_sector(data, sector_num); + // Write key A + for(size_t i = 0; i < sizeof(sec_tr->key_a); i++) { + if(mf_classic_is_key_found(data, sector_num, MfClassicKeyA)) { + string_cat_printf(block_str, "%02X ", sec_tr->key_a[i]); + } else { + string_cat_printf(block_str, "?? "); + } + } + // Write Access bytes + for(size_t i = 0; i < MF_CLASSIC_ACCESS_BYTES_SIZE; i++) { + if(mf_classic_is_block_read(data, block_num)) { + string_cat_printf(block_str, "%02X ", sec_tr->access_bits[i]); + } else { + string_cat_printf(block_str, "?? "); + } + } + // Write key B + for(size_t i = 0; i < sizeof(sec_tr->key_b); i++) { + if(mf_classic_is_key_found(data, sector_num, MfClassicKeyB)) { + string_cat_printf(block_str, "%02X ", sec_tr->key_b[i]); + } else { + string_cat_printf(block_str, "?? "); + } + } + } else { + // Write data block + for(size_t i = 0; i < MF_CLASSIC_BLOCK_SIZE; i++) { + if(mf_classic_is_block_read(data, block_num)) { + string_cat_printf(block_str, "%02X ", data->block[block_num].value[i]); + } else { + string_cat_printf(block_str, "?? "); + } + } + } + string_strim(block_str); +} + static bool nfc_device_save_mifare_classic_data(FlipperFormat* file, NfcDevice* dev) { bool saved = false; MfClassicData* data = &dev->dev_data.mf_classic_data; @@ -669,23 +744,21 @@ static bool nfc_device_save_mifare_classic_data(FlipperFormat* file, NfcDevice* if(!flipper_format_write_uint32( file, "Data format version", &nfc_mifare_classic_data_format_version, 1)) break; - if(!flipper_format_write_comment_cstr( - file, "Key map is the bit mask indicating valid key in each sector")) + file, "Mifare Classic blocks, \'??\' means unknown data")) break; - if(!flipper_format_write_hex_uint64(file, "Key A map", &data->key_a_mask, 1)) break; - if(!flipper_format_write_hex_uint64(file, "Key B map", &data->key_b_mask, 1)) break; - - if(!flipper_format_write_comment_cstr(file, "Mifare Classic blocks")) break; bool block_saved = true; + string_t block_str; + string_init(block_str); for(size_t i = 0; i < blocks; i++) { string_printf(temp_str, "Block %d", i); - if(!flipper_format_write_hex( - file, string_get_cstr(temp_str), data->block[i].value, 16)) { + nfc_device_write_mifare_classic_block(block_str, data, i); + if(!flipper_format_write_string(file, string_get_cstr(temp_str), block_str)) { block_saved = false; break; } } + string_clear(block_str); if(!block_saved) break; saved = true; } while(false); @@ -694,6 +767,59 @@ static bool nfc_device_save_mifare_classic_data(FlipperFormat* file, NfcDevice* return saved; } +static void nfc_device_load_mifare_classic_block( + string_t block_str, + MfClassicData* data, + uint8_t block_num) { + string_strim(block_str); + MfClassicBlock block_tmp = {}; + bool is_sector_trailer = mf_classic_is_sector_trailer(block_num); + uint8_t sector_num = mf_classic_get_sector_by_block(block_num); + uint16_t block_unknown_bytes_mask = 0; + + string_strim(block_str); + for(size_t i = 0; i < MF_CLASSIC_BLOCK_SIZE; i++) { + char hi = string_get_char(block_str, 3 * i); + char low = string_get_char(block_str, 3 * i + 1); + uint8_t byte = 0; + if(hex_chars_to_uint8(hi, low, &byte)) { + block_tmp.value[i] = byte; + } else { + FURI_BIT_SET(block_unknown_bytes_mask, i); + } + } + + if(block_unknown_bytes_mask == 0xffff) { + // All data is unknown, exit + return; + } + + if(is_sector_trailer) { + MfClassicSectorTrailer* sec_tr_tmp = (MfClassicSectorTrailer*)&block_tmp; + // Load Key A + // Key A mask 0b0000000000111111 = 0x003f + if((block_unknown_bytes_mask & 0x003f) == 0) { + uint64_t key = nfc_util_bytes2num(sec_tr_tmp->key_a, sizeof(sec_tr_tmp->key_a)); + mf_classic_set_key_found(data, sector_num, MfClassicKeyA, key); + } + // Load Access Bits + // Access bits mask 0b0000001111000000 = 0x03c0 + if((block_unknown_bytes_mask & 0x03c0) == 0) { + mf_classic_set_block_read(data, block_num, &block_tmp); + } + // Load Key B + // Key B mask 0b1111110000000000 = 0xfc00 + if((block_unknown_bytes_mask & 0xfc00) == 0) { + uint64_t key = nfc_util_bytes2num(sec_tr_tmp->key_b, sizeof(sec_tr_tmp->key_b)); + mf_classic_set_key_found(data, sector_num, MfClassicKeyB, key); + } + } else { + if(block_unknown_bytes_mask == 0) { + mf_classic_set_block_read(data, block_num, &block_tmp); + } + } +} + static bool nfc_device_load_mifare_classic_data(FlipperFormat* file, NfcDevice* dev) { bool parsed = false; MfClassicData* data = &dev->dev_data.mf_classic_data; @@ -701,6 +827,7 @@ static bool nfc_device_load_mifare_classic_data(FlipperFormat* file, NfcDevice* uint32_t data_format_version = 0; string_init(temp_str); uint16_t data_blocks = 0; + memset(data, 0, sizeof(MfClassicData)); do { // Read Mifare Classic type @@ -715,29 +842,40 @@ static bool nfc_device_load_mifare_classic_data(FlipperFormat* file, NfcDevice* break; } + bool old_format = false; // Read Mifare Classic format version if(!flipper_format_read_uint32(file, "Data format version", &data_format_version, 1)) { // Load unread sectors with zero keys access for backward compatability if(!flipper_format_rewind(file)) break; - data->key_a_mask = 0xffffffffffffffff; - data->key_b_mask = 0xffffffffffffffff; + old_format = true; } else { - if(data_format_version != nfc_mifare_classic_data_format_version) break; - if(!flipper_format_read_hex_uint64(file, "Key A map", &data->key_a_mask, 1)) break; - if(!flipper_format_read_hex_uint64(file, "Key B map", &data->key_b_mask, 1)) break; + if(data_format_version < nfc_mifare_classic_data_format_version) { + old_format = true; + } } // Read Mifare Classic blocks bool block_read = true; + string_t block_str; + string_init(block_str); for(size_t i = 0; i < data_blocks; i++) { string_printf(temp_str, "Block %d", i); - if(!flipper_format_read_hex( - file, string_get_cstr(temp_str), data->block[i].value, 16)) { + if(!flipper_format_read_string(file, string_get_cstr(temp_str), block_str)) { block_read = false; break; } + nfc_device_load_mifare_classic_block(block_str, data, i); } + string_clear(block_str); if(!block_read) break; + + // Set keys and blocks as unknown for backward compatibility + if(old_format) { + data->key_a_mask = 0ULL; + data->key_b_mask = 0ULL; + memset(data->block_read_mask, 0, sizeof(data->block_read_mask)); + } + parsed = true; } while(false); @@ -745,6 +883,113 @@ static bool nfc_device_load_mifare_classic_data(FlipperFormat* file, NfcDevice* return parsed; } +static void nfc_device_get_key_cache_file_path(NfcDevice* dev, string_t file_path) { + uint8_t* uid = dev->dev_data.nfc_data.uid; + uint8_t uid_len = dev->dev_data.nfc_data.uid_len; + string_set_str(file_path, NFC_DEVICE_KEYS_FOLDER "/"); + for(size_t i = 0; i < uid_len; i++) { + string_cat_printf(file_path, "%02X", uid[i]); + } + string_cat_printf(file_path, NFC_DEVICE_KEYS_EXTENSION); +} + +static bool nfc_device_save_mifare_classic_keys(NfcDevice* dev) { + FlipperFormat* file = flipper_format_file_alloc(dev->storage); + MfClassicData* data = &dev->dev_data.mf_classic_data; + string_t temp_str; + string_init(temp_str); + + nfc_device_get_key_cache_file_path(dev, temp_str); + bool save_success = false; + do { + if(!storage_simply_mkdir(dev->storage, NFC_DEVICE_KEYS_FOLDER)) break; + if(!storage_simply_remove(dev->storage, string_get_cstr(temp_str))) break; + if(!flipper_format_file_open_always(file, string_get_cstr(temp_str))) break; + if(!flipper_format_write_header_cstr(file, nfc_keys_file_header, nfc_keys_file_version)) + break; + if(data->type == MfClassicType1k) { + if(!flipper_format_write_string_cstr(file, "Mifare Classic type", "1K")) break; + } else if(data->type == MfClassicType4k) { + if(!flipper_format_write_string_cstr(file, "Mifare Classic type", "4K")) break; + } + if(!flipper_format_write_hex_uint64(file, "Key A map", &data->key_a_mask, 1)) break; + if(!flipper_format_write_hex_uint64(file, "Key B map", &data->key_b_mask, 1)) break; + uint8_t sector_num = mf_classic_get_total_sectors_num(data->type); + bool key_save_success = true; + for(size_t i = 0; (i < sector_num) && (key_save_success); i++) { + MfClassicSectorTrailer* sec_tr = mf_classic_get_sector_trailer_by_sector(data, i); + if(FURI_BIT(data->key_a_mask, i)) { + string_printf(temp_str, "Key A sector %d", i); + key_save_success = + flipper_format_write_hex(file, string_get_cstr(temp_str), sec_tr->key_a, 6); + } + if(!key_save_success) break; + if(FURI_BIT(data->key_a_mask, i)) { + string_printf(temp_str, "Key B sector %d", i); + key_save_success = + flipper_format_write_hex(file, string_get_cstr(temp_str), sec_tr->key_b, 6); + } + } + save_success = key_save_success; + } while(false); + + flipper_format_free(file); + string_clear(temp_str); + return save_success; +} + +bool nfc_device_load_key_cache(NfcDevice* dev) { + furi_assert(dev); + string_t temp_str; + string_init(temp_str); + + MfClassicData* data = &dev->dev_data.mf_classic_data; + nfc_device_get_key_cache_file_path(dev, temp_str); + FlipperFormat* file = flipper_format_file_alloc(dev->storage); + + bool load_success = false; + do { + if(storage_common_stat(dev->storage, string_get_cstr(temp_str), NULL) != FSE_OK) break; + if(!flipper_format_file_open_existing(file, string_get_cstr(temp_str))) break; + uint32_t version = 0; + if(!flipper_format_read_header(file, temp_str, &version)) break; + if(string_cmp_str(temp_str, nfc_keys_file_header)) break; + if(version != nfc_keys_file_version) break; + if(!flipper_format_read_string(file, "Mifare Classic type", temp_str)) break; + if(!string_cmp_str(temp_str, "1K")) { + data->type = MfClassicType1k; + } else if(!string_cmp_str(temp_str, "4K")) { + data->type = MfClassicType4k; + } else { + break; + } + if(!flipper_format_read_hex_uint64(file, "Key A map", &data->key_a_mask, 1)) break; + if(!flipper_format_read_hex_uint64(file, "Key B map", &data->key_b_mask, 1)) break; + uint8_t sectors = mf_classic_get_total_sectors_num(data->type); + bool key_read_success = true; + for(size_t i = 0; (i < sectors) && (key_read_success); i++) { + MfClassicSectorTrailer* sec_tr = mf_classic_get_sector_trailer_by_sector(data, i); + if(FURI_BIT(data->key_a_mask, i)) { + string_printf(temp_str, "Key A sector %d", i); + key_read_success = + flipper_format_read_hex(file, string_get_cstr(temp_str), sec_tr->key_a, 6); + } + if(!key_read_success) break; + if(FURI_BIT(data->key_b_mask, i)) { + string_printf(temp_str, "Key B sector %d", i); + key_read_success = + flipper_format_read_hex(file, string_get_cstr(temp_str), sec_tr->key_b, 6); + } + } + load_success = key_read_success; + } while(false); + + string_clear(temp_str); + flipper_format_free(file); + + return load_success; +} + void nfc_device_set_name(NfcDevice* dev, const char* name) { furi_assert(dev); @@ -815,7 +1060,10 @@ static bool nfc_device_save_file( } else if(dev->format == NfcDeviceSaveFormatBankCard) { if(!nfc_device_save_bank_card_data(file, dev)) break; } else if(dev->format == NfcDeviceSaveFormatMifareClassic) { + // Save data if(!nfc_device_save_mifare_classic_data(file, dev)) break; + // Save keys cache + if(!nfc_device_save_mifare_classic_keys(dev)) break; } saved = true; } while(0); @@ -954,14 +1202,22 @@ bool nfc_file_select(NfcDevice* dev) { void nfc_device_data_clear(NfcDeviceData* dev_data) { if(dev_data->protocol == NfcDeviceProtocolMifareDesfire) { mf_df_clear(&dev_data->mf_df_data); + } else if(dev_data->protocol == NfcDeviceProtocolMifareClassic) { + memset(&dev_data->mf_classic_data, 0, sizeof(MfClassicData)); + } else if(dev_data->protocol == NfcDeviceProtocolMifareUl) { + mf_ul_reset(&dev_data->mf_ul_data); + } else if(dev_data->protocol == NfcDeviceProtocolEMV) { + memset(&dev_data->emv_data, 0, sizeof(EmvData)); } + memset(&dev_data->nfc_data, 0, sizeof(FuriHalNfcDevData)); + dev_data->protocol = NfcDeviceProtocolUnknown; + string_reset(dev_data->parsed_data); } void nfc_device_clear(NfcDevice* dev) { furi_assert(dev); nfc_device_data_clear(&dev->dev_data); - memset(&dev->dev_data, 0, sizeof(dev->dev_data)); dev->format = NfcDeviceSaveFormatUid; string_reset(dev->load_path); } diff --git a/applications/nfc/nfc_device.h b/lib/nfc/nfc_device.h similarity index 78% rename from applications/nfc/nfc_device.h rename to lib/nfc/nfc_device.h index fee9b07e1..f9a0b24ba 100644 --- a/applications/nfc/nfc_device.h +++ b/lib/nfc/nfc_device.h @@ -6,15 +6,17 @@ #include #include -#include -#include -#include -#include +#include +#include +#include +#include +#include #define NFC_DEV_NAME_MAX_LEN 22 #define NFC_READER_DATA_MAX_SIZE 64 +#define NFC_DICT_KEY_BATCH_SIZE 50 -#define NFC_APP_FOLDER "/any/nfc" +#define NFC_APP_FOLDER ANY_PATH("nfc") #define NFC_APP_EXTENSION ".nfc" #define NFC_APP_SHADOW_EXTENSION ".shd" @@ -41,16 +43,24 @@ typedef struct { uint16_t size; } NfcReaderRequestData; +typedef struct { + MfClassicDict* dict; +} NfcMfClassicDictAttackData; + typedef struct { FuriHalNfcDevData nfc_data; NfcProtocol protocol; - NfcReaderRequestData reader_data; + union { + NfcReaderRequestData reader_data; + NfcMfClassicDictAttackData mf_classic_dict_attack_data; + }; union { EmvData emv_data; MfUltralightData mf_ul_data; MfClassicData mf_classic_data; MifareDesfireData mf_df_data; }; + string_t parsed_data; } NfcDeviceData; typedef struct { @@ -78,6 +88,8 @@ bool nfc_device_save_shadow(NfcDevice* dev, const char* dev_name); bool nfc_device_load(NfcDevice* dev, const char* file_path, bool show_dialog); +bool nfc_device_load_key_cache(NfcDevice* dev); + bool nfc_file_select(NfcDevice* dev); void nfc_device_data_clear(NfcDeviceData* dev); diff --git a/applications/nfc/nfc_types.c b/lib/nfc/nfc_types.c similarity index 96% rename from applications/nfc/nfc_types.c rename to lib/nfc/nfc_types.c index 2d11c3397..427628769 100644 --- a/applications/nfc/nfc_types.c +++ b/lib/nfc/nfc_types.c @@ -43,6 +43,8 @@ const char* nfc_mf_ul_type(MfUltralightType type, bool full_name) { return "NTAG I2C Plus 1K"; } else if(type == MfUltralightTypeNTAGI2CPlus2K) { return "NTAG I2C Plus 2K"; + } else if(type == MfUltralightTypeNTAG203) { + return "NTAG203"; } else if(type == MfUltralightTypeUL11 && full_name) { return "Mifare Ultralight 11"; } else if(type == MfUltralightTypeUL21 && full_name) { diff --git a/applications/nfc/nfc_types.h b/lib/nfc/nfc_types.h similarity index 100% rename from applications/nfc/nfc_types.h rename to lib/nfc/nfc_types.h diff --git a/lib/nfc/nfc_worker.c b/lib/nfc/nfc_worker.c new file mode 100644 index 000000000..a92f148a2 --- /dev/null +++ b/lib/nfc/nfc_worker.c @@ -0,0 +1,570 @@ +#include "nfc_worker_i.h" +#include + +#include +#include "parsers/nfc_supported_card.h" + +#define TAG "NfcWorker" + +/***************************** NFC Worker API *******************************/ + +NfcWorker* nfc_worker_alloc() { + NfcWorker* nfc_worker = malloc(sizeof(NfcWorker)); + + // Worker thread attributes + nfc_worker->thread = furi_thread_alloc(); + furi_thread_set_name(nfc_worker->thread, "NfcWorker"); + furi_thread_set_stack_size(nfc_worker->thread, 8192); + furi_thread_set_callback(nfc_worker->thread, nfc_worker_task); + furi_thread_set_context(nfc_worker->thread, nfc_worker); + + nfc_worker->callback = NULL; + nfc_worker->context = NULL; + nfc_worker->storage = furi_record_open(RECORD_STORAGE); + + // Initialize rfal + while(furi_hal_nfc_is_busy()) { + furi_delay_ms(10); + } + nfc_worker_change_state(nfc_worker, NfcWorkerStateReady); + + if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { + nfc_worker->debug_pcap_worker = nfc_debug_pcap_alloc(nfc_worker->storage); + } + + return nfc_worker; +} + +void nfc_worker_free(NfcWorker* nfc_worker) { + furi_assert(nfc_worker); + + furi_thread_free(nfc_worker->thread); + + furi_record_close(RECORD_STORAGE); + + if(nfc_worker->debug_pcap_worker) nfc_debug_pcap_free(nfc_worker->debug_pcap_worker); + + free(nfc_worker); +} + +NfcWorkerState nfc_worker_get_state(NfcWorker* nfc_worker) { + return nfc_worker->state; +} + +void nfc_worker_start( + NfcWorker* nfc_worker, + NfcWorkerState state, + NfcDeviceData* dev_data, + NfcWorkerCallback callback, + void* context) { + furi_assert(nfc_worker); + furi_assert(dev_data); + while(furi_hal_nfc_is_busy()) { + furi_delay_ms(10); + } + + nfc_worker->callback = callback; + nfc_worker->context = context; + nfc_worker->dev_data = dev_data; + nfc_worker_change_state(nfc_worker, state); + furi_thread_start(nfc_worker->thread); +} + +void nfc_worker_stop(NfcWorker* nfc_worker) { + furi_assert(nfc_worker); + if(nfc_worker->state == NfcWorkerStateBroken || nfc_worker->state == NfcWorkerStateReady) { + return; + } + furi_hal_nfc_stop(); + nfc_worker_change_state(nfc_worker, NfcWorkerStateStop); + furi_thread_join(nfc_worker->thread); +} + +void nfc_worker_change_state(NfcWorker* nfc_worker, NfcWorkerState state) { + nfc_worker->state = state; +} + +/***************************** NFC Worker Thread *******************************/ + +int32_t nfc_worker_task(void* context) { + NfcWorker* nfc_worker = context; + + furi_hal_nfc_exit_sleep(); + + if(nfc_worker->state == NfcWorkerStateRead) { + nfc_worker_read(nfc_worker); + } else if(nfc_worker->state == NfcWorkerStateUidEmulate) { + nfc_worker_emulate_uid(nfc_worker); + } else if(nfc_worker->state == NfcWorkerStateEmulateApdu) { + nfc_worker_emulate_apdu(nfc_worker); + } else if(nfc_worker->state == NfcWorkerStateMfUltralightEmulate) { + nfc_worker_emulate_mf_ultralight(nfc_worker); + } else if(nfc_worker->state == NfcWorkerStateMfClassicEmulate) { + nfc_worker_emulate_mf_classic(nfc_worker); + } else if(nfc_worker->state == NfcWorkerStateReadMfUltralightReadAuth) { + nfc_worker_mf_ultralight_read_auth(nfc_worker); + } else if(nfc_worker->state == NfcWorkerStateMfClassicDictAttack) { + nfc_worker_mf_classic_dict_attack(nfc_worker); + } + furi_hal_nfc_sleep(); + nfc_worker_change_state(nfc_worker, NfcWorkerStateReady); + + return 0; +} + +static bool nfc_worker_read_mf_ultralight(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) { + bool read_success = false; + MfUltralightReader reader = {}; + MfUltralightData data = {}; + + nfc_debug_pcap_prepare_tx_rx(nfc_worker->debug_pcap_worker, tx_rx, false); + do { + // Read card + if(!furi_hal_nfc_detect(&nfc_worker->dev_data->nfc_data, 200)) break; + if(!mf_ul_read_card(tx_rx, &reader, &data)) break; + // Copy data + nfc_worker->dev_data->mf_ul_data = data; + read_success = true; + } while(false); + + return read_success; +} + +static bool nfc_worker_read_mf_classic(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) { + furi_assert(nfc_worker->callback); + bool read_success = false; + + nfc_debug_pcap_prepare_tx_rx(nfc_worker->debug_pcap_worker, tx_rx, false); + do { + // Try to read supported card + FURI_LOG_I(TAG, "Try read supported card ..."); + for(size_t i = 0; i < NfcSupportedCardTypeEnd; i++) { + if(nfc_supported_card[i].protocol == NfcDeviceProtocolMifareClassic) { + if(nfc_supported_card[i].verify(nfc_worker, tx_rx)) { + if(nfc_supported_card[i].read(nfc_worker, tx_rx)) { + read_success = true; + nfc_supported_card[i].parse(nfc_worker); + } + } + } + } + if(read_success) break; + // Try to read card with key cache + FURI_LOG_I(TAG, "Search for key cache ..."); + if(nfc_worker->callback(NfcWorkerEventReadMfClassicLoadKeyCache, nfc_worker->context)) { + FURI_LOG_I(TAG, "Load keys cache success. Start reading"); + uint8_t sectors_read = + mf_classic_update_card(tx_rx, &nfc_worker->dev_data->mf_classic_data); + uint8_t sectors_total = + mf_classic_get_total_sectors_num(nfc_worker->dev_data->mf_classic_data.type); + FURI_LOG_I(TAG, "Read %d sectors out of %d total", sectors_read, sectors_total); + read_success = (sectors_read == sectors_total); + } + } while(false); + + return read_success; +} + +static bool nfc_worker_read_mf_desfire(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) { + bool read_success = false; + MifareDesfireData* data = &nfc_worker->dev_data->mf_df_data; + + nfc_debug_pcap_prepare_tx_rx(nfc_worker->debug_pcap_worker, tx_rx, false); + do { + if(!furi_hal_nfc_detect(&nfc_worker->dev_data->nfc_data, 300)) break; + if(!mf_df_read_card(tx_rx, data)) break; + read_success = true; + } while(false); + + return read_success; +} + +static bool nfc_worker_read_bank_card(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) { + bool read_success = false; + EmvApplication emv_app = {}; + EmvData* result = &nfc_worker->dev_data->emv_data; + + nfc_debug_pcap_prepare_tx_rx(nfc_worker->debug_pcap_worker, tx_rx, false); + do { + // Read card + if(!furi_hal_nfc_detect(&nfc_worker->dev_data->nfc_data, 300)) break; + if(!emv_read_bank_card(tx_rx, &emv_app)) break; + // Copy data + // TODO Set EmvData to reader or like in mifare ultralight! + result->number_len = emv_app.card_number_len; + memcpy(result->number, emv_app.card_number, result->number_len); + result->aid_len = emv_app.aid_len; + memcpy(result->aid, emv_app.aid, result->aid_len); + if(emv_app.name_found) { + memcpy(result->name, emv_app.name, sizeof(emv_app.name)); + } + if(emv_app.exp_month) { + result->exp_mon = emv_app.exp_month; + result->exp_year = emv_app.exp_year; + } + if(emv_app.country_code) { + result->country_code = emv_app.country_code; + } + if(emv_app.currency_code) { + result->currency_code = emv_app.currency_code; + } + read_success = true; + } while(false); + + return read_success; +} + +static bool nfc_worker_read_nfca(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) { + FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data; + + bool card_read = false; + furi_hal_nfc_sleep(); + if(mf_ul_check_card_type(nfc_data->atqa[0], nfc_data->atqa[1], nfc_data->sak)) { + FURI_LOG_I(TAG, "Mifare Ultralight / NTAG detected"); + nfc_worker->dev_data->protocol = NfcDeviceProtocolMifareUl; + card_read = nfc_worker_read_mf_ultralight(nfc_worker, tx_rx); + } else if(mf_classic_check_card_type(nfc_data->atqa[0], nfc_data->atqa[1], nfc_data->sak)) { + FURI_LOG_I(TAG, "Mifare Classic detected"); + nfc_worker->dev_data->protocol = NfcDeviceProtocolMifareClassic; + nfc_worker->dev_data->mf_classic_data.type = + mf_classic_get_classic_type(nfc_data->atqa[0], nfc_data->atqa[1], nfc_data->sak); + card_read = nfc_worker_read_mf_classic(nfc_worker, tx_rx); + } else if(mf_df_check_card_type(nfc_data->atqa[0], nfc_data->atqa[1], nfc_data->sak)) { + FURI_LOG_I(TAG, "Mifare DESFire detected"); + nfc_worker->dev_data->protocol = NfcDeviceProtocolMifareDesfire; + if(!nfc_worker_read_mf_desfire(nfc_worker, tx_rx)) { + FURI_LOG_I(TAG, "Unknown card. Save UID"); + nfc_worker->dev_data->protocol = NfcDeviceProtocolUnknown; + } + card_read = true; + } else if(nfc_data->interface == FuriHalNfcInterfaceIsoDep) { + FURI_LOG_I(TAG, "ISO14443-4 card detected"); + nfc_worker->dev_data->protocol = NfcDeviceProtocolEMV; + if(!nfc_worker_read_bank_card(nfc_worker, tx_rx)) { + FURI_LOG_I(TAG, "Unknown card. Save UID"); + nfc_worker->dev_data->protocol = NfcDeviceProtocolUnknown; + } + card_read = true; + } else { + nfc_worker->dev_data->protocol = NfcDeviceProtocolUnknown; + card_read = true; + } + + return card_read; +} + +void nfc_worker_read(NfcWorker* nfc_worker) { + furi_assert(nfc_worker); + furi_assert(nfc_worker->callback); + + nfc_device_data_clear(nfc_worker->dev_data); + NfcDeviceData* dev_data = nfc_worker->dev_data; + FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data; + FuriHalNfcTxRxContext tx_rx = {}; + NfcWorkerEvent event = 0; + bool card_not_detected_notified = false; + + while(nfc_worker->state == NfcWorkerStateRead) { + if(furi_hal_nfc_detect(nfc_data, 300)) { + // Process first found device + nfc_worker->callback(NfcWorkerEventCardDetected, nfc_worker->context); + card_not_detected_notified = false; + if(nfc_data->type == FuriHalNfcTypeA) { + if(nfc_worker_read_nfca(nfc_worker, &tx_rx)) { + if(dev_data->protocol == NfcDeviceProtocolMifareUl) { + event = NfcWorkerEventReadMfUltralight; + break; + } else if(dev_data->protocol == NfcDeviceProtocolMifareClassic) { + event = NfcWorkerEventReadMfClassicDone; + break; + } else if(dev_data->protocol == NfcDeviceProtocolMifareDesfire) { + event = NfcWorkerEventReadMfDesfire; + break; + } else if(dev_data->protocol == NfcDeviceProtocolEMV) { + event = NfcWorkerEventReadBankCard; + break; + } else if(dev_data->protocol == NfcDeviceProtocolUnknown) { + event = NfcWorkerEventReadUidNfcA; + break; + } + } else { + if(dev_data->protocol == NfcDeviceProtocolMifareClassic) { + event = NfcWorkerEventReadMfClassicDictAttackRequired; + break; + } + } + } else if(nfc_data->type == FuriHalNfcTypeB) { + event = NfcWorkerEventReadUidNfcB; + break; + } else if(nfc_data->type == FuriHalNfcTypeF) { + event = NfcWorkerEventReadUidNfcF; + break; + } else if(nfc_data->type == FuriHalNfcTypeV) { + event = NfcWorkerEventReadUidNfcV; + break; + } + } else { + if(!card_not_detected_notified) { + nfc_worker->callback(NfcWorkerEventNoCardDetected, nfc_worker->context); + card_not_detected_notified = true; + } + } + furi_hal_nfc_sleep(); + furi_delay_ms(100); + } + // Notify caller and exit + if(event > NfcWorkerEventReserved) { + nfc_worker->callback(event, nfc_worker->context); + } +} + +void nfc_worker_emulate_uid(NfcWorker* nfc_worker) { + FuriHalNfcTxRxContext tx_rx = {}; + nfc_debug_pcap_prepare_tx_rx(nfc_worker->debug_pcap_worker, &tx_rx, true); + FuriHalNfcDevData* data = &nfc_worker->dev_data->nfc_data; + NfcReaderRequestData* reader_data = &nfc_worker->dev_data->reader_data; + + // TODO add support for RATS + // Now remove bit 6 in SAK to support ISO-14443A-3 emulation + // Need to save ATS to support ISO-14443A-4 emulation + uint8_t sak = data->sak; + FURI_BIT_CLEAR(sak, 5); + + while(nfc_worker->state == NfcWorkerStateUidEmulate) { + if(furi_hal_nfc_listen(data->uid, data->uid_len, data->atqa, sak, true, 100)) { + if(furi_hal_nfc_tx_rx(&tx_rx, 100)) { + reader_data->size = tx_rx.rx_bits / 8; + if(reader_data->size > 0) { + memcpy(reader_data->data, tx_rx.rx_data, reader_data->size); + if(nfc_worker->callback) { + nfc_worker->callback(NfcWorkerEventSuccess, nfc_worker->context); + } + } + } else { + FURI_LOG_E(TAG, "Failed to get reader commands"); + } + } + } +} + +void nfc_worker_emulate_apdu(NfcWorker* nfc_worker) { + FuriHalNfcTxRxContext tx_rx = {}; + nfc_debug_pcap_prepare_tx_rx(nfc_worker->debug_pcap_worker, &tx_rx, true); + FuriHalNfcDevData params = { + .uid = {0xCF, 0x72, 0xd4, 0x40}, + .uid_len = 4, + .atqa = {0x00, 0x04}, + .sak = 0x20, + .type = FuriHalNfcTypeA, + }; + + while(nfc_worker->state == NfcWorkerStateEmulateApdu) { + if(furi_hal_nfc_listen(params.uid, params.uid_len, params.atqa, params.sak, false, 300)) { + FURI_LOG_D(TAG, "POS terminal detected"); + if(emv_card_emulation(&tx_rx)) { + FURI_LOG_D(TAG, "EMV card emulated"); + } + } else { + FURI_LOG_D(TAG, "Can't find reader"); + } + furi_hal_nfc_sleep(); + furi_delay_ms(20); + } +} + +void nfc_worker_emulate_mf_ultralight(NfcWorker* nfc_worker) { + FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data; + MfUltralightEmulator emulator = {}; + mf_ul_prepare_emulation(&emulator, &nfc_worker->dev_data->mf_ul_data); + while(nfc_worker->state == NfcWorkerStateMfUltralightEmulate) { + mf_ul_reset_emulation(&emulator, true); + furi_hal_nfc_emulate_nfca( + nfc_data->uid, + nfc_data->uid_len, + nfc_data->atqa, + nfc_data->sak, + mf_ul_prepare_emulation_response, + &emulator, + 5000); + // Check if data was modified + if(emulator.data_changed) { + nfc_worker->dev_data->mf_ul_data = emulator.data; + if(nfc_worker->callback) { + nfc_worker->callback(NfcWorkerEventSuccess, nfc_worker->context); + } + emulator.data_changed = false; + } + } +} + +void nfc_worker_mf_classic_dict_attack(NfcWorker* nfc_worker) { + furi_assert(nfc_worker); + furi_assert(nfc_worker->callback); + + MfClassicData* data = &nfc_worker->dev_data->mf_classic_data; + NfcMfClassicDictAttackData* dict_attack_data = + &nfc_worker->dev_data->mf_classic_dict_attack_data; + uint32_t total_sectors = mf_classic_get_total_sectors_num(data->type); + uint64_t key = 0; + FuriHalNfcTxRxContext tx_rx = {}; + bool card_found_notified = true; + bool card_removed_notified = false; + + // Load dictionary + MfClassicDict* dict = dict_attack_data->dict; + if(!dict) { + FURI_LOG_E(TAG, "Dictionary not found"); + nfc_worker->callback(NfcWorkerEventNoDictFound, nfc_worker->context); + return; + } + + FURI_LOG_D(TAG, "Start Dictionary attack, Key Count %d", mf_classic_dict_get_total_keys(dict)); + for(size_t i = 0; i < total_sectors; i++) { + FURI_LOG_I(TAG, "Sector %d", i); + nfc_worker->callback(NfcWorkerEventNewSector, nfc_worker->context); + uint8_t block_num = mf_classic_get_sector_trailer_block_num_by_sector(i); + if(mf_classic_is_sector_read(data, i)) continue; + bool is_key_a_found = mf_classic_is_key_found(data, i, MfClassicKeyA); + bool is_key_b_found = mf_classic_is_key_found(data, i, MfClassicKeyB); + uint16_t key_index = 0; + while(mf_classic_dict_get_next_key(dict, &key)) { + if(++key_index % NFC_DICT_KEY_BATCH_SIZE == 0) { + nfc_worker->callback(NfcWorkerEventNewDictKeyBatch, nfc_worker->context); + } + furi_hal_nfc_sleep(); + if(furi_hal_nfc_activate_nfca(200, NULL)) { + furi_hal_nfc_sleep(); + if(!card_found_notified) { + nfc_worker->callback(NfcWorkerEventCardDetected, nfc_worker->context); + card_found_notified = true; + card_removed_notified = false; + } + FURI_LOG_D( + TAG, + "Try to auth to sector %d with key %04lx%08lx", + i, + (uint32_t)(key >> 32), + (uint32_t)key); + if(!is_key_a_found) { + is_key_a_found = mf_classic_is_key_found(data, i, MfClassicKeyA); + if(mf_classic_authenticate(&tx_rx, block_num, key, MfClassicKeyA)) { + mf_classic_set_key_found(data, i, MfClassicKeyA, key); + nfc_worker->callback(NfcWorkerEventFoundKeyA, nfc_worker->context); + } + furi_hal_nfc_sleep(); + } + if(!is_key_b_found) { + is_key_b_found = mf_classic_is_key_found(data, i, MfClassicKeyB); + if(mf_classic_authenticate(&tx_rx, block_num, key, MfClassicKeyB)) { + mf_classic_set_key_found(data, i, MfClassicKeyB, key); + nfc_worker->callback(NfcWorkerEventFoundKeyB, nfc_worker->context); + } + } + if(is_key_a_found && is_key_b_found) break; + if(nfc_worker->state != NfcWorkerStateMfClassicDictAttack) break; + } else { + if(!card_removed_notified) { + nfc_worker->callback(NfcWorkerEventNoCardDetected, nfc_worker->context); + card_removed_notified = true; + card_found_notified = false; + } + if(nfc_worker->state != NfcWorkerStateMfClassicDictAttack) break; + } + } + if(nfc_worker->state != NfcWorkerStateMfClassicDictAttack) break; + mf_classic_read_sector(&tx_rx, data, i); + mf_classic_dict_rewind(dict); + } + if(nfc_worker->state == NfcWorkerStateMfClassicDictAttack) { + nfc_worker->callback(NfcWorkerEventSuccess, nfc_worker->context); + } else { + nfc_worker->callback(NfcWorkerEventAborted, nfc_worker->context); + } +} + +void nfc_worker_emulate_mf_classic(NfcWorker* nfc_worker) { + FuriHalNfcTxRxContext tx_rx = {}; + nfc_debug_pcap_prepare_tx_rx(nfc_worker->debug_pcap_worker, &tx_rx, true); + FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data; + MfClassicEmulator emulator = { + .cuid = nfc_util_bytes2num(&nfc_data->uid[nfc_data->uid_len - 4], 4), + .data = nfc_worker->dev_data->mf_classic_data, + .data_changed = false, + }; + NfcaSignal* nfca_signal = nfca_signal_alloc(); + tx_rx.nfca_signal = nfca_signal; + + rfal_platform_spi_acquire(); + + furi_hal_nfc_listen_start(nfc_data); + while(nfc_worker->state == NfcWorkerStateMfClassicEmulate) { + if(furi_hal_nfc_listen_rx(&tx_rx, 300)) { + mf_classic_emulator(&emulator, &tx_rx); + } + } + if(emulator.data_changed) { + nfc_worker->dev_data->mf_classic_data = emulator.data; + if(nfc_worker->callback) { + nfc_worker->callback(NfcWorkerEventSuccess, nfc_worker->context); + } + emulator.data_changed = false; + } + + nfca_signal_free(nfca_signal); + + rfal_platform_spi_release(); +} + +void nfc_worker_mf_ultralight_read_auth(NfcWorker* nfc_worker) { + furi_assert(nfc_worker); + furi_assert(nfc_worker->callback); + + MfUltralightData* data = &nfc_worker->dev_data->mf_ul_data; + FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data; + FuriHalNfcTxRxContext tx_rx = {}; + MfUltralightReader reader = {}; + mf_ul_reset(data); + + uint32_t key = 0; + uint16_t pack = 0; + while(nfc_worker->state == NfcWorkerStateReadMfUltralightReadAuth) { + furi_hal_nfc_sleep(); + if(furi_hal_nfc_detect(nfc_data, 300) && nfc_data->type == FuriHalNfcTypeA) { + if(mf_ul_check_card_type(nfc_data->atqa[0], nfc_data->atqa[1], nfc_data->sak)) { + nfc_worker->callback(NfcWorkerEventCardDetected, nfc_worker->context); + if(data->auth_method == MfUltralightAuthMethodManual) { + nfc_worker->callback(NfcWorkerEventMfUltralightPassKey, nfc_worker->context); + key = nfc_util_bytes2num(data->auth_key, 4); + } else if(data->auth_method == MfUltralightAuthMethodAmeebo) { + key = mf_ul_pwdgen_amiibo(nfc_data); + } else if(data->auth_method == MfUltralightAuthMethodXiaomi) { + key = mf_ul_pwdgen_xiaomi(nfc_data); + } else { + FURI_LOG_E(TAG, "Incorrect auth method"); + break; + } + + data->auth_success = mf_ultralight_authenticate(&tx_rx, key, &pack); + mf_ul_read_card(&tx_rx, &reader, data); + if(data->auth_success) { + MfUltralightConfigPages* config_pages = mf_ultralight_get_config_pages(data); + if(config_pages != NULL) { + config_pages->auth_data.pwd.value = REVERSE_BYTES_U32(key); + config_pages->auth_data.pack.value = pack; + } + nfc_worker->callback(NfcWorkerEventSuccess, nfc_worker->context); + break; + } else { + nfc_worker->callback(NfcWorkerEventFail, nfc_worker->context); + break; + } + } else { + nfc_worker->callback(NfcWorkerEventWrongCardDetected, nfc_worker->context); + furi_delay_ms(10); + } + } else { + nfc_worker->callback(NfcWorkerEventNoCardDetected, nfc_worker->context); + furi_delay_ms(10); + } + } +} diff --git a/applications/nfc/nfc_worker.h b/lib/nfc/nfc_worker.h similarity index 54% rename from applications/nfc/nfc_worker.h rename to lib/nfc/nfc_worker.h index a68f42d72..22cbc3dcc 100755 --- a/applications/nfc/nfc_worker.h +++ b/lib/nfc/nfc_worker.h @@ -10,17 +10,15 @@ typedef enum { NfcWorkerStateBroken, NfcWorkerStateReady, // Main worker states - NfcWorkerStateDetect, - NfcWorkerStateEmulate, - NfcWorkerStateReadEMVApp, - NfcWorkerStateReadEMVData, + NfcWorkerStateRead, + NfcWorkerStateUidEmulate, + NfcWorkerStateMfUltralightEmulate, + NfcWorkerStateMfClassicEmulate, + NfcWorkerStateReadMfUltralightReadAuth, + NfcWorkerStateMfClassicDictAttack, + // Debug NfcWorkerStateEmulateApdu, NfcWorkerStateField, - NfcWorkerStateReadMifareUltralight, - NfcWorkerStateEmulateMifareUltralight, - NfcWorkerStateReadMifareClassic, - NfcWorkerStateEmulateMifareClassic, - NfcWorkerStateReadMifareDesfire, // Transition NfcWorkerStateStop, } NfcWorkerState; @@ -29,21 +27,38 @@ typedef enum { // Reserve first 50 events for application events NfcWorkerEventReserved = 50, + // Nfc read events + NfcWorkerEventReadUidNfcB, + NfcWorkerEventReadUidNfcV, + NfcWorkerEventReadUidNfcF, + NfcWorkerEventReadUidNfcA, + NfcWorkerEventReadMfUltralight, + NfcWorkerEventReadMfDesfire, + NfcWorkerEventReadMfClassicDone, + NfcWorkerEventReadMfClassicLoadKeyCache, + NfcWorkerEventReadMfClassicDictAttackRequired, + NfcWorkerEventReadBankCard, + // Nfc worker common events NfcWorkerEventSuccess, NfcWorkerEventFail, + NfcWorkerEventAborted, + NfcWorkerEventCardDetected, NfcWorkerEventNoCardDetected, + NfcWorkerEventWrongCardDetected, + // Mifare Classic events NfcWorkerEventNoDictFound, - NfcWorkerEventDetectedClassic1k, - NfcWorkerEventDetectedClassic4k, NfcWorkerEventNewSector, + NfcWorkerEventNewDictKeyBatch, NfcWorkerEventFoundKeyA, NfcWorkerEventFoundKeyB, - NfcWorkerEventStartReading, + + // Mifare Ultralight events + NfcWorkerEventMfUltralightPassKey, } NfcWorkerEvent; -typedef void (*NfcWorkerCallback)(NfcWorkerEvent event, void* context); +typedef bool (*NfcWorkerCallback)(NfcWorkerEvent event, void* context); NfcWorker* nfc_worker_alloc(); diff --git a/lib/nfc/nfc_worker_i.h b/lib/nfc/nfc_worker_i.h new file mode 100644 index 000000000..1e98879a7 --- /dev/null +++ b/lib/nfc/nfc_worker_i.h @@ -0,0 +1,51 @@ +#pragma once + +#include "nfc_worker.h" + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "helpers/nfc_debug_pcap.h" + +struct NfcWorker { + FuriThread* thread; + Storage* storage; + Stream* dict_stream; + + NfcDeviceData* dev_data; + + NfcWorkerCallback callback; + void* context; + + NfcWorkerState state; + + NfcDebugPcapWorker* debug_pcap_worker; +}; + +void nfc_worker_change_state(NfcWorker* nfc_worker, NfcWorkerState state); + +int32_t nfc_worker_task(void* context); + +void nfc_worker_read(NfcWorker* nfc_worker); + +void nfc_worker_emulate_uid(NfcWorker* nfc_worker); + +void nfc_worker_emulate_mf_ultralight(NfcWorker* nfc_worker); + +void nfc_worker_emulate_mf_classic(NfcWorker* nfc_worker); + +void nfc_worker_mf_classic_dict_attack(NfcWorker* nfc_worker); + +void nfc_worker_mf_ultralight_read_auth(NfcWorker* nfc_worker); + +void nfc_worker_mf_ul_auth_attack(NfcWorker* nfc_worker); + +void nfc_worker_emulate_apdu(NfcWorker* nfc_worker); diff --git a/lib/nfc/parsers/nfc_supported_card.c b/lib/nfc/parsers/nfc_supported_card.c new file mode 100644 index 000000000..59482a123 --- /dev/null +++ b/lib/nfc/parsers/nfc_supported_card.c @@ -0,0 +1,13 @@ +#include "nfc_supported_card.h" + +#include "troyka_parser.h" + +NfcSupportedCard nfc_supported_card[NfcSupportedCardTypeEnd] = { + [NfcSupportedCardTypeTroyka] = + { + .protocol = NfcDeviceProtocolMifareClassic, + .verify = troyka_parser_verify, + .read = troyka_parser_read, + .parse = troyka_parser_parse, + }, +}; diff --git a/lib/nfc/parsers/nfc_supported_card.h b/lib/nfc/parsers/nfc_supported_card.h new file mode 100644 index 000000000..5c94c78cd --- /dev/null +++ b/lib/nfc/parsers/nfc_supported_card.h @@ -0,0 +1,27 @@ +#pragma once + +#include +#include "../nfc_worker.h" + +#include + +typedef enum { + NfcSupportedCardTypeTroyka, + + NfcSupportedCardTypeEnd, +} NfcSupportedCardType; + +typedef bool (*NfcSupportedCardVerify)(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx); + +typedef bool (*NfcSupportedCardRead)(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx); + +typedef bool (*NfcSupportedCardParse)(NfcWorker* nfc_worker); + +typedef struct { + NfcProtocol protocol; + NfcSupportedCardVerify verify; + NfcSupportedCardRead read; + NfcSupportedCardParse parse; +} NfcSupportedCard; + +extern NfcSupportedCard nfc_supported_card[NfcSupportedCardTypeEnd]; diff --git a/lib/nfc/parsers/troyka_parser.c b/lib/nfc/parsers/troyka_parser.c new file mode 100644 index 000000000..3167b5181 --- /dev/null +++ b/lib/nfc/parsers/troyka_parser.c @@ -0,0 +1,71 @@ +#include "nfc_supported_card.h" + +#include +#include + +static const MfClassicAuthContext troyka_keys[] = { + {.sector = 0, .key_a = 0xa0a1a2a3a4a5, .key_b = 0xfbf225dc5d58}, + {.sector = 1, .key_a = 0xa82607b01c0d, .key_b = 0x2910989b6880}, + {.sector = 2, .key_a = 0x2aa05ed1856f, .key_b = 0xeaac88e5dc99}, + {.sector = 3, .key_a = 0x2aa05ed1856f, .key_b = 0xeaac88e5dc99}, + {.sector = 4, .key_a = 0x73068f118c13, .key_b = 0x2b7f3253fac5}, + {.sector = 5, .key_a = 0xfbc2793d540b, .key_b = 0xd3a297dc2698}, + {.sector = 6, .key_a = 0x2aa05ed1856f, .key_b = 0xeaac88e5dc99}, + {.sector = 7, .key_a = 0xae3d65a3dad4, .key_b = 0x0f1c63013dba}, + {.sector = 8, .key_a = 0xa73f5dc1d333, .key_b = 0xe35173494a81}, + {.sector = 9, .key_a = 0x69a32f1c2f19, .key_b = 0x6b8bd9860763}, + {.sector = 10, .key_a = 0x9becdf3d9273, .key_b = 0xf8493407799d}, + {.sector = 11, .key_a = 0x08b386463229, .key_b = 0x5efbaecef46b}, + {.sector = 12, .key_a = 0xcd4c61c26e3d, .key_b = 0x31c7610de3b0}, + {.sector = 13, .key_a = 0xa82607b01c0d, .key_b = 0x2910989b6880}, + {.sector = 14, .key_a = 0x0e8f64340ba4, .key_b = 0x4acec1205d75}, + {.sector = 15, .key_a = 0x2aa05ed1856f, .key_b = 0xeaac88e5dc99}, +}; + +bool troyka_parser_verify(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) { + furi_assert(nfc_worker); + UNUSED(nfc_worker); + + MfClassicAuthContext auth_ctx = { + .key_a = MF_CLASSIC_NO_KEY, + .key_b = MF_CLASSIC_NO_KEY, + .sector = 8, + }; + return mf_classic_auth_attempt(tx_rx, &auth_ctx, 0xa73f5dc1d333); +} + +bool troyka_parser_read(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) { + furi_assert(nfc_worker); + + MfClassicReader reader = {}; + FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data; + reader.type = mf_classic_get_classic_type(nfc_data->atqa[0], nfc_data->atqa[1], nfc_data->sak); + + for(size_t i = 0; i < COUNT_OF(troyka_keys); i++) { + mf_classic_reader_add_sector( + &reader, troyka_keys[i].sector, troyka_keys[i].key_a, troyka_keys[i].key_b); + } + + return mf_classic_read_card(tx_rx, &reader, &nfc_worker->dev_data->mf_classic_data) == 16; +} + +bool troyka_parser_parse(NfcWorker* nfc_worker) { + MfClassicData* data = &nfc_worker->dev_data->mf_classic_data; + uint8_t* temp_ptr = &data->block[8 * 4 + 1].value[5]; + uint16_t balance = ((temp_ptr[0] << 8) | temp_ptr[1]) / 25; + temp_ptr = &data->block[8 * 4].value[3]; + uint32_t number = 0; + for(size_t i = 0; i < 4; i++) { + number <<= 8; + number |= temp_ptr[i]; + } + number >>= 4; + + string_printf( + nfc_worker->dev_data->parsed_data, + "Troyka Transport card\nNumber: %ld\nBalance: %d rub", + number, + balance); + + return true; +} diff --git a/lib/nfc/parsers/troyka_parser.h b/lib/nfc/parsers/troyka_parser.h new file mode 100644 index 000000000..0d5cee233 --- /dev/null +++ b/lib/nfc/parsers/troyka_parser.h @@ -0,0 +1,9 @@ +#pragma once + +#include "nfc_supported_card.h" + +bool troyka_parser_verify(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx); + +bool troyka_parser_read(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx); + +bool troyka_parser_parse(NfcWorker* nfc_worker); diff --git a/lib/nfc_protocols/crypto1.c b/lib/nfc/protocols/crypto1.c similarity index 100% rename from lib/nfc_protocols/crypto1.c rename to lib/nfc/protocols/crypto1.c diff --git a/lib/nfc_protocols/crypto1.h b/lib/nfc/protocols/crypto1.h similarity index 100% rename from lib/nfc_protocols/crypto1.h rename to lib/nfc/protocols/crypto1.h diff --git a/lib/nfc_protocols/emv.c b/lib/nfc/protocols/emv.c similarity index 100% rename from lib/nfc_protocols/emv.c rename to lib/nfc/protocols/emv.c diff --git a/lib/nfc_protocols/emv.h b/lib/nfc/protocols/emv.h similarity index 100% rename from lib/nfc_protocols/emv.h rename to lib/nfc/protocols/emv.h diff --git a/lib/nfc_protocols/mifare_classic.c b/lib/nfc/protocols/mifare_classic.c similarity index 69% rename from lib/nfc_protocols/mifare_classic.c rename to lib/nfc/protocols/mifare_classic.c index e35a1d6c2..3b5343932 100644 --- a/lib/nfc_protocols/mifare_classic.c +++ b/lib/nfc/protocols/mifare_classic.c @@ -25,6 +25,16 @@ typedef enum { MfClassicActionACWrite, } MfClassicAction; +const char* mf_classic_get_type_str(MfClassicType type) { + if(type == MfClassicType1k) { + return "MIFARE Classic 1K"; + } else if(type == MfClassicType4k) { + return "MIFARE Classic 4K"; + } else { + return "Unknown"; + } +} + static uint8_t mf_classic_get_first_block_num_of_sector(uint8_t sector) { furi_assert(sector < 40); if(sector < 32) { @@ -34,7 +44,16 @@ static uint8_t mf_classic_get_first_block_num_of_sector(uint8_t sector) { } } -static uint8_t mf_classic_get_sector_by_block(uint8_t block) { +uint8_t mf_classic_get_sector_trailer_block_num_by_sector(uint8_t sector) { + furi_assert(sector < 40); + if(sector < 32) { + return sector * 4 + 3; + } else { + return 32 * 4 + (sector - 32) * 16 + 15; + } +} + +uint8_t mf_classic_get_sector_by_block(uint8_t block) { if(block < 128) { return (block | 0x03) / 4; } else { @@ -47,7 +66,7 @@ static uint8_t mf_classic_get_blocks_num_in_sector(uint8_t sector) { return sector < 32 ? 4 : 16; } -static uint8_t mf_classic_get_sector_trailer(uint8_t block) { +uint8_t mf_classic_get_sector_trailer_num_by_block(uint8_t block) { if(block < 128) { return block | 0x03; } else { @@ -55,15 +74,21 @@ static uint8_t mf_classic_get_sector_trailer(uint8_t block) { } } -static bool mf_classic_is_sector_trailer(uint8_t block) { - return block == mf_classic_get_sector_trailer(block); +bool mf_classic_is_sector_trailer(uint8_t block) { + return block == mf_classic_get_sector_trailer_num_by_block(block); } -uint8_t mf_classic_get_total_sectors_num(MfClassicReader* reader) { - furi_assert(reader); - if(reader->type == MfClassicType1k) { +MfClassicSectorTrailer* + mf_classic_get_sector_trailer_by_sector(MfClassicData* data, uint8_t sector) { + furi_assert(data); + uint8_t sec_tr_block_num = mf_classic_get_sector_trailer_block_num_by_sector(sector); + return (MfClassicSectorTrailer*)data->block[sec_tr_block_num].value; +} + +uint8_t mf_classic_get_total_sectors_num(MfClassicType type) { + if(type == MfClassicType1k) { return MF_CLASSIC_1K_TOTAL_SECTORS_NUM; - } else if(reader->type == MfClassicType4k) { + } else if(type == MfClassicType4k) { return MF_CLASSIC_4K_TOTAL_SECTORS_NUM; } else { return 0; @@ -80,6 +105,104 @@ static uint16_t mf_classic_get_total_block_num(MfClassicType type) { } } +bool mf_classic_is_block_read(MfClassicData* data, uint8_t block_num) { + furi_assert(data); + + return (FURI_BIT(data->block_read_mask[block_num / 32], block_num % 32) == 1); +} + +void mf_classic_set_block_read(MfClassicData* data, uint8_t block_num, MfClassicBlock* block_data) { + furi_assert(data); + + if(mf_classic_is_sector_trailer(block_num)) { + memcpy(&data->block[block_num].value[6], &block_data->value[6], 4); + } else { + memcpy(data->block[block_num].value, block_data->value, MF_CLASSIC_BLOCK_SIZE); + } + FURI_BIT_SET(data->block_read_mask[block_num / 32], block_num % 32); +} + +bool mf_classic_is_key_found(MfClassicData* data, uint8_t sector_num, MfClassicKey key_type) { + furi_assert(data); + + bool key_found = false; + if(key_type == MfClassicKeyA) { + key_found = (FURI_BIT(data->key_a_mask, sector_num) == 1); + } else if(key_type == MfClassicKeyB) { + key_found = (FURI_BIT(data->key_b_mask, sector_num) == 1); + } + + return key_found; +} + +void mf_classic_set_key_found( + MfClassicData* data, + uint8_t sector_num, + MfClassicKey key_type, + uint64_t key) { + furi_assert(data); + + uint8_t key_arr[6] = {}; + MfClassicSectorTrailer* sec_trailer = + mf_classic_get_sector_trailer_by_sector(data, sector_num); + nfc_util_num2bytes(key, 6, key_arr); + if(key_type == MfClassicKeyA) { + memcpy(sec_trailer->key_a, key_arr, sizeof(sec_trailer->key_a)); + FURI_BIT_SET(data->key_a_mask, sector_num); + } else if(key_type == MfClassicKeyB) { + memcpy(sec_trailer->key_b, key_arr, sizeof(sec_trailer->key_b)); + FURI_BIT_SET(data->key_b_mask, sector_num); + } +} + +bool mf_classic_is_sector_read(MfClassicData* data, uint8_t sector_num) { + furi_assert(data); + + bool sector_read = false; + do { + if(!mf_classic_is_key_found(data, sector_num, MfClassicKeyA)) break; + if(!mf_classic_is_key_found(data, sector_num, MfClassicKeyB)) break; + uint8_t start_block = mf_classic_get_first_block_num_of_sector(sector_num); + uint8_t total_blocks = mf_classic_get_blocks_num_in_sector(sector_num); + uint8_t block_read = true; + for(size_t i = start_block; i < start_block + total_blocks; i++) { + block_read = mf_classic_is_block_read(data, i); + if(!block_read) break; + } + sector_read = block_read; + } while(false); + + return sector_read; +} + +void mf_classic_get_read_sectors_and_keys( + MfClassicData* data, + uint8_t* sectors_read, + uint8_t* keys_found) { + furi_assert(data); + *sectors_read = 0; + *keys_found = 0; + uint8_t sectors_total = mf_classic_get_total_sectors_num(data->type); + for(size_t i = 0; i < sectors_total; i++) { + if(mf_classic_is_key_found(data, i, MfClassicKeyA)) { + *keys_found += 1; + } + if(mf_classic_is_key_found(data, i, MfClassicKeyB)) { + *keys_found += 1; + } + uint8_t first_block = mf_classic_get_first_block_num_of_sector(i); + uint8_t total_blocks_in_sec = mf_classic_get_blocks_num_in_sector(i); + bool blocks_read = true; + for(size_t i = first_block; i < first_block + total_blocks_in_sec; i++) { + blocks_read = mf_classic_is_block_read(data, i); + if(!blocks_read) break; + } + if(blocks_read) { + *sectors_read += 1; + } + } +} + static bool mf_classic_is_allowed_access_sector_trailer( MfClassicEmulator* emulator, uint8_t block_num, @@ -126,7 +249,8 @@ static bool mf_classic_is_allowed_access_data_block( uint8_t block_num, MfClassicKey key, MfClassicAction action) { - uint8_t* sector_trailer = emulator->data.block[mf_classic_get_sector_trailer(block_num)].value; + uint8_t* sector_trailer = + emulator->data.block[mf_classic_get_sector_trailer_num_by_block(block_num)].value; uint8_t sector_block; if(block_num <= 128) { @@ -200,6 +324,9 @@ bool mf_classic_check_card_type(uint8_t ATQA0, uint8_t ATQA1, uint8_t SAK) { UNUSED(ATQA1); if((ATQA0 == 0x44 || ATQA0 == 0x04) && (SAK == 0x08 || SAK == 0x88 || SAK == 0x09)) { return true; + } else if((ATQA0 == 0x01) && (ATQA1 == 0x0F) && (SAK == 0x01)) { + //skylanders support + return true; } else if((ATQA0 == 0x42 || ATQA0 == 0x02) && (SAK == 0x18)) { return true; } else { @@ -207,34 +334,17 @@ bool mf_classic_check_card_type(uint8_t ATQA0, uint8_t ATQA1, uint8_t SAK) { } } -bool mf_classic_get_type( - uint8_t* uid, - uint8_t uid_len, - uint8_t ATQA0, - uint8_t ATQA1, - uint8_t SAK, - MfClassicReader* reader) { +MfClassicType mf_classic_get_classic_type(int8_t ATQA0, uint8_t ATQA1, uint8_t SAK) { UNUSED(ATQA1); - furi_assert(uid); - furi_assert(reader); - memset(reader, 0, sizeof(MfClassicReader)); - if((ATQA0 == 0x44 || ATQA0 == 0x04) && (SAK == 0x08 || SAK == 0x88 || SAK == 0x09)) { - reader->type = MfClassicType1k; + return MfClassicType1k; + } else if((ATQA0 == 0x01) && (ATQA1 == 0x0F) && (SAK == 0x01)) { + //skylanders support + return MfClassicType1k; } else if((ATQA0 == 0x42 || ATQA0 == 0x02) && (SAK == 0x18)) { - reader->type = MfClassicType4k; - } else { - return false; - } - - uint8_t* cuid_start = uid; - if(uid_len == 7) { - cuid_start = &uid[3]; + return MfClassicType4k; } - reader->cuid = (cuid_start[0] << 24) | (cuid_start[1] << 16) | (cuid_start[2] << 8) | - (cuid_start[3]); - - return true; + return MfClassicType1k; } void mf_classic_reader_add_sector( @@ -246,7 +356,7 @@ void mf_classic_reader_add_sector( furi_assert(sector < MF_CLASSIC_SECTORS_MAX); furi_assert((key_a != MF_CLASSIC_NO_KEY) || (key_b != MF_CLASSIC_NO_KEY)); - if(reader->sectors_to_read < MF_CLASSIC_SECTORS_MAX - 1) { + if(reader->sectors_to_read < MF_CLASSIC_SECTORS_MAX) { reader->sector_reader[reader->sectors_to_read].key_a = key_a; reader->sector_reader[reader->sectors_to_read].key_b = key_b; reader->sector_reader[reader->sectors_to_read].sector_num = sector; @@ -254,9 +364,8 @@ void mf_classic_reader_add_sector( } } -void mf_classic_auth_init_context(MfClassicAuthContext* auth_ctx, uint32_t cuid, uint8_t sector) { +void mf_classic_auth_init_context(MfClassicAuthContext* auth_ctx, uint8_t sector) { furi_assert(auth_ctx); - auth_ctx->cuid = cuid; auth_ctx->sector = sector; auth_ctx->key_a = MF_CLASSIC_NO_KEY; auth_ctx->key_b = MF_CLASSIC_NO_KEY; @@ -264,17 +373,18 @@ void mf_classic_auth_init_context(MfClassicAuthContext* auth_ctx, uint32_t cuid, static bool mf_classic_auth( FuriHalNfcTxRxContext* tx_rx, - uint32_t cuid, uint32_t block, uint64_t key, MfClassicKey key_type, Crypto1* crypto) { bool auth_success = false; + uint32_t cuid = 0; memset(tx_rx->tx_data, 0, sizeof(tx_rx->tx_data)); memset(tx_rx->tx_parity, 0, sizeof(tx_rx->tx_parity)); tx_rx->tx_rx_type = FuriHalNfcTxRxTypeDefault; do { + if(!furi_hal_nfc_activate_nfca(200, &cuid)) break; if(key_type == MfClassicKeyA) { tx_rx->tx_data[0] = MF_CLASSIC_AUTH_KEY_A_CMD; } else { @@ -315,6 +425,19 @@ static bool mf_classic_auth( return auth_success; } +bool mf_classic_authenticate( + FuriHalNfcTxRxContext* tx_rx, + uint8_t block_num, + uint64_t key, + MfClassicKey key_type) { + furi_assert(tx_rx); + + Crypto1 crypto = {}; + bool key_found = mf_classic_auth(tx_rx, block_num, key, key_type, &crypto); + furi_hal_nfc_sleep(); + return key_found; +} + bool mf_classic_auth_attempt( FuriHalNfcTxRxContext* tx_rx, MfClassicAuthContext* auth_ctx, @@ -330,7 +453,6 @@ bool mf_classic_auth_attempt( // Try AUTH with key A if(mf_classic_auth( tx_rx, - auth_ctx->cuid, mf_classic_get_first_block_num_of_sector(auth_ctx->sector), key, MfClassicKeyA, @@ -342,14 +464,12 @@ bool mf_classic_auth_attempt( if(need_halt) { furi_hal_nfc_sleep(); - furi_hal_nfc_activate_nfca(300, &auth_ctx->cuid); } if(auth_ctx->key_b == MF_CLASSIC_NO_KEY) { // Try AUTH with key B if(mf_classic_auth( tx_rx, - auth_ctx->cuid, mf_classic_get_first_block_num_of_sector(auth_ctx->sector), key, MfClassicKeyB, @@ -410,7 +530,60 @@ bool mf_classic_read_block( return read_block_success; } -bool mf_classic_read_sector( +void mf_classic_read_sector(FuriHalNfcTxRxContext* tx_rx, MfClassicData* data, uint8_t sec_num) { + furi_assert(tx_rx); + furi_assert(data); + + furi_hal_nfc_sleep(); + bool key_a_found = mf_classic_is_key_found(data, sec_num, MfClassicKeyA); + bool key_b_found = mf_classic_is_key_found(data, sec_num, MfClassicKeyB); + uint8_t start_block = mf_classic_get_first_block_num_of_sector(sec_num); + uint8_t total_blocks = mf_classic_get_blocks_num_in_sector(sec_num); + MfClassicBlock block_tmp = {}; + uint64_t key = 0; + MfClassicSectorTrailer* sec_tr = mf_classic_get_sector_trailer_by_sector(data, sec_num); + Crypto1 crypto = {}; + + uint8_t blocks_read = 0; + do { + if(!key_a_found) break; + FURI_LOG_D(TAG, "Try to read blocks with key A"); + key = nfc_util_bytes2num(sec_tr->key_a, sizeof(sec_tr->key_a)); + if(!mf_classic_auth(tx_rx, start_block, key, MfClassicKeyA, &crypto)) break; + for(size_t i = start_block; i < start_block + total_blocks; i++) { + if(!mf_classic_is_block_read(data, i)) { + if(mf_classic_read_block(tx_rx, &crypto, i, &block_tmp)) { + mf_classic_set_block_read(data, i, &block_tmp); + blocks_read++; + } + } else { + blocks_read++; + } + } + FURI_LOG_D(TAG, "Read %d blocks out of %d", blocks_read, total_blocks); + } while(false); + do { + if(blocks_read == total_blocks) break; + if(!key_b_found) break; + FURI_LOG_D(TAG, "Try to read blocks with key B"); + key = nfc_util_bytes2num(sec_tr->key_b, sizeof(sec_tr->key_b)); + furi_hal_nfc_sleep(); + if(!mf_classic_auth(tx_rx, start_block, key, MfClassicKeyB, &crypto)) break; + for(size_t i = start_block; i < start_block + total_blocks; i++) { + if(!mf_classic_is_block_read(data, i)) { + if(mf_classic_read_block(tx_rx, &crypto, i, &block_tmp)) { + mf_classic_set_block_read(data, i, &block_tmp); + blocks_read++; + } + } else { + blocks_read++; + } + } + FURI_LOG_D(TAG, "Read %d blocks out of %d", blocks_read, total_blocks); + } while(false); +} + +static bool mf_classic_read_sector_with_reader( FuriHalNfcTxRxContext* tx_rx, Crypto1* crypto, MfClassicSectorReader* sector_reader, @@ -419,7 +592,6 @@ bool mf_classic_read_sector( furi_assert(sector_reader); furi_assert(sector); - uint32_t cuid = 0; uint64_t key; MfClassicKey key_type; uint8_t first_block; @@ -428,7 +600,6 @@ bool mf_classic_read_sector( furi_hal_nfc_sleep(); do { // Activate card - if(!furi_hal_nfc_activate_nfca(200, &cuid)) break; first_block = mf_classic_get_first_block_num_of_sector(sector_reader->sector_num); if(sector_reader->key_a != MF_CLASSIC_NO_KEY) { key = sector_reader->key_a; @@ -441,7 +612,7 @@ bool mf_classic_read_sector( } // Auth to first block in sector - if(!mf_classic_auth(tx_rx, cuid, first_block, key, key_type, crypto)) break; + if(!mf_classic_auth(tx_rx, first_block, key, key_type, crypto)) break; sector->total_blocks = mf_classic_get_blocks_num_in_sector(sector_reader->sector_num); // Read blocks @@ -478,18 +649,26 @@ uint8_t mf_classic_read_card( data->key_b_mask = 0; MfClassicSector temp_sector = {}; for(uint8_t i = 0; i < reader->sectors_to_read; i++) { - if(mf_classic_read_sector( + if(mf_classic_read_sector_with_reader( tx_rx, &reader->crypto, &reader->sector_reader[i], &temp_sector)) { uint8_t first_block = mf_classic_get_first_block_num_of_sector(reader->sector_reader[i].sector_num); for(uint8_t j = 0; j < temp_sector.total_blocks; j++) { - data->block[first_block + j] = temp_sector.block[j]; + mf_classic_set_block_read(data, first_block + j, &temp_sector.block[j]); } if(reader->sector_reader[i].key_a != MF_CLASSIC_NO_KEY) { - data->key_a_mask |= 1 << reader->sector_reader[i].sector_num; + mf_classic_set_key_found( + data, + reader->sector_reader[i].sector_num, + MfClassicKeyA, + reader->sector_reader[i].key_a); } if(reader->sector_reader[i].key_b != MF_CLASSIC_NO_KEY) { - data->key_b_mask |= 1 << reader->sector_reader[i].sector_num; + mf_classic_set_key_found( + data, + reader->sector_reader[i].sector_num, + MfClassicKeyB, + reader->sector_reader[i].key_b); } sectors_read++; } @@ -498,6 +677,46 @@ uint8_t mf_classic_read_card( return sectors_read; } +uint8_t mf_classic_update_card(FuriHalNfcTxRxContext* tx_rx, MfClassicData* data) { + furi_assert(tx_rx); + furi_assert(data); + + uint8_t sectors_read = 0; + Crypto1 crypto = {}; + uint8_t total_sectors = mf_classic_get_total_sectors_num(data->type); + uint64_t key_a = 0; + uint64_t key_b = 0; + MfClassicSectorReader sec_reader = {}; + MfClassicSector temp_sector = {}; + + for(size_t i = 0; i < total_sectors; i++) { + MfClassicSectorTrailer* sec_tr = mf_classic_get_sector_trailer_by_sector(data, i); + // Load key A + if(mf_classic_is_key_found(data, i, MfClassicKeyA)) { + sec_reader.key_a = nfc_util_bytes2num(sec_tr->key_a, 6); + } else { + sec_reader.key_a = MF_CLASSIC_NO_KEY; + } + // Load key B + if(mf_classic_is_key_found(data, i, MfClassicKeyB)) { + sec_reader.key_b = nfc_util_bytes2num(sec_tr->key_b, 6); + } else { + sec_reader.key_b = MF_CLASSIC_NO_KEY; + } + if((key_a != MF_CLASSIC_NO_KEY) || (key_b != MF_CLASSIC_NO_KEY)) { + sec_reader.sector_num = i; + if(mf_classic_read_sector_with_reader(tx_rx, &crypto, &sec_reader, &temp_sector)) { + uint8_t first_block = mf_classic_get_first_block_num_of_sector(i); + for(uint8_t j = 0; j < temp_sector.total_blocks; j++) { + mf_classic_set_block_read(data, first_block + j, &temp_sector.block[j]); + } + sectors_read++; + } + } + } + return sectors_read; +} + void mf_crypto1_decrypt( Crypto1* crypto, uint8_t* encrypted_data, @@ -552,6 +771,7 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_ // Read command while(!command_processed) { if(!is_encrypted) { + crypto1_reset(&emulator->crypto); memcpy(plain_data, tx_rx->rx_data, tx_rx->rx_bits / 8); } else { if(!furi_hal_nfc_tx_rx(tx_rx, 300)) { @@ -573,7 +793,7 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_ } else if(plain_data[0] == 0x60 || plain_data[0] == 0x61) { uint8_t block = plain_data[1]; uint64_t key = 0; - uint8_t sector_trailer_block = mf_classic_get_sector_trailer(block); + uint8_t sector_trailer_block = mf_classic_get_sector_trailer_num_by_block(block); MfClassicSectorTrailer* sector_trailer = (MfClassicSectorTrailer*)emulator->data.block[sector_trailer_block].value; if(plain_data[0] == 0x60) { @@ -584,7 +804,7 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_ access_key = MfClassicKeyB; } - uint32_t nonce = prng_successor(DWT->CYCCNT, 32); + uint32_t nonce = prng_successor(DWT->CYCCNT, 32) ^ 0xAA; uint8_t nt[4]; uint8_t nt_keystream[4]; nfc_util_num2bytes(nonce, 4, nt); @@ -635,26 +855,11 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_ nr, ar); - // Check if we store valid key - if(access_key == MfClassicKeyA) { - if(FURI_BIT(emulator->data.key_a_mask, mf_classic_get_sector_by_block(block)) == - 0) { - FURI_LOG_D(TAG, "Unsupported sector key A for block %d", sector_trailer_block); - break; - } - } else if(access_key == MfClassicKeyB) { - if(FURI_BIT(emulator->data.key_b_mask, mf_classic_get_sector_by_block(block)) == - 0) { - FURI_LOG_D(TAG, "Unsupported sector key B for block %d", sector_trailer_block); - break; - } - } - crypto1_word(&emulator->crypto, nr, 1); uint32_t cardRr = ar ^ crypto1_word(&emulator->crypto, 0, 0); if(cardRr != prng_successor(nonce, 64)) { FURI_LOG_T(TAG, "Wrong AUTH! %08X != %08X", cardRr, prng_successor(nonce, 64)); - // Don't send NACK, as tag don't send it + // Don't send NACK, as the tag doesn't send it command_processed = true; break; } @@ -693,7 +898,18 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_ } else { if(!mf_classic_is_allowed_access( emulator, block, access_key, MfClassicActionDataRead)) { - memset(block_data, 0, 16); + // Send NACK + uint8_t nack = 0x04; + if(is_encrypted) { + mf_crypto1_encrypt( + &emulator->crypto, NULL, &nack, 4, tx_rx->tx_data, tx_rx->tx_parity); + } else { + tx_rx->tx_data[0] = nack; + } + tx_rx->tx_rx_type = FuriHalNfcTxRxTransparent; + tx_rx->tx_bits = 4; + furi_hal_nfc_tx_rx(tx_rx, 300); + break; } } nfca_append_crc16(block_data, 16); diff --git a/lib/nfc_protocols/mifare_classic.h b/lib/nfc/protocols/mifare_classic.h similarity index 55% rename from lib/nfc_protocols/mifare_classic.h rename to lib/nfc/protocols/mifare_classic.h index bbf34b2dc..b9921fb1c 100644 --- a/lib/nfc_protocols/mifare_classic.h +++ b/lib/nfc/protocols/mifare_classic.h @@ -14,6 +14,8 @@ #define MF_CLASSIC_NO_KEY (0xFFFFFFFFFFFFFFFF) #define MF_CLASSIC_MAX_DATA_SIZE (16) +#define MF_CLASSIC_KEY_SIZE (6) +#define MF_CLASSIC_ACCESS_BYTES_SIZE (4) typedef enum { MfClassicType1k, @@ -30,9 +32,9 @@ typedef struct { } MfClassicBlock; typedef struct { - uint8_t key_a[6]; - uint8_t access_bits[4]; - uint8_t key_b[6]; + uint8_t key_a[MF_CLASSIC_KEY_SIZE]; + uint8_t access_bits[MF_CLASSIC_ACCESS_BYTES_SIZE]; + uint8_t key_b[MF_CLASSIC_KEY_SIZE]; } MfClassicSectorTrailer; typedef struct { @@ -42,13 +44,13 @@ typedef struct { typedef struct { MfClassicType type; + uint32_t block_read_mask[MF_CLASSIC_TOTAL_BLOCKS_MAX / 32]; uint64_t key_a_mask; uint64_t key_b_mask; MfClassicBlock block[MF_CLASSIC_TOTAL_BLOCKS_MAX]; } MfClassicData; typedef struct { - uint32_t cuid; uint8_t sector; uint64_t key_a; uint64_t key_b; @@ -62,9 +64,8 @@ typedef struct { typedef struct { MfClassicType type; - uint32_t cuid; - uint8_t sectors_to_read; Crypto1 crypto; + uint8_t sectors_to_read; MfClassicSectorReader sector_reader[MF_CLASSIC_SECTORS_MAX]; } MfClassicReader; @@ -75,19 +76,49 @@ typedef struct { bool data_changed; } MfClassicEmulator; +const char* mf_classic_get_type_str(MfClassicType type); + bool mf_classic_check_card_type(uint8_t ATQA0, uint8_t ATQA1, uint8_t SAK); -bool mf_classic_get_type( - uint8_t* uid, - uint8_t uid_len, - uint8_t ATQA0, - uint8_t ATQA1, - uint8_t SAK, - MfClassicReader* reader); +MfClassicType mf_classic_get_classic_type(int8_t ATQA0, uint8_t ATQA1, uint8_t SAK); + +uint8_t mf_classic_get_total_sectors_num(MfClassicType type); + +uint8_t mf_classic_get_sector_trailer_block_num_by_sector(uint8_t sector); + +bool mf_classic_is_sector_trailer(uint8_t block); + +uint8_t mf_classic_get_sector_by_block(uint8_t block); + +bool mf_classic_is_key_found(MfClassicData* data, uint8_t sector_num, MfClassicKey key_type); -uint8_t mf_classic_get_total_sectors_num(MfClassicReader* reader); +void mf_classic_set_key_found( + MfClassicData* data, + uint8_t sector_num, + MfClassicKey key_type, + uint64_t key); + +bool mf_classic_is_block_read(MfClassicData* data, uint8_t block_num); + +void mf_classic_set_block_read(MfClassicData* data, uint8_t block_num, MfClassicBlock* block_data); + +bool mf_classic_is_sector_read(MfClassicData* data, uint8_t sector_num); + +void mf_classic_get_read_sectors_and_keys( + MfClassicData* data, + uint8_t* sectors_read, + uint8_t* keys_found); -void mf_classic_auth_init_context(MfClassicAuthContext* auth_ctx, uint32_t cuid, uint8_t sector); +MfClassicSectorTrailer* + mf_classic_get_sector_trailer_by_sector(MfClassicData* data, uint8_t sector); + +void mf_classic_auth_init_context(MfClassicAuthContext* auth_ctx, uint8_t sector); + +bool mf_classic_authenticate( + FuriHalNfcTxRxContext* tx_rx, + uint8_t block_num, + uint64_t key, + MfClassicKey key_type); bool mf_classic_auth_attempt( FuriHalNfcTxRxContext* tx_rx, @@ -100,15 +131,13 @@ void mf_classic_reader_add_sector( uint64_t key_a, uint64_t key_b); -bool mf_classic_read_sector( - FuriHalNfcTxRxContext* tx_rx, - Crypto1* crypto, - MfClassicSectorReader* sector_reader, - MfClassicSector* sector); +void mf_classic_read_sector(FuriHalNfcTxRxContext* tx_rx, MfClassicData* data, uint8_t sec_num); uint8_t mf_classic_read_card( FuriHalNfcTxRxContext* tx_rx, MfClassicReader* reader, MfClassicData* data); +uint8_t mf_classic_update_card(FuriHalNfcTxRxContext* tx_rx, MfClassicData* data); + bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_rx); diff --git a/lib/nfc_protocols/mifare_common.c b/lib/nfc/protocols/mifare_common.c similarity index 80% rename from lib/nfc_protocols/mifare_common.c rename to lib/nfc/protocols/mifare_common.c index fd622765e..90b57e1f0 100644 --- a/lib/nfc_protocols/mifare_common.c +++ b/lib/nfc/protocols/mifare_common.c @@ -7,7 +7,8 @@ MifareType mifare_common_get_type(uint8_t ATQA0, uint8_t ATQA1, uint8_t SAK) { type = MifareTypeUltralight; } else if( ((ATQA0 == 0x44 || ATQA0 == 0x04) && (SAK == 0x08 || SAK == 0x88 || SAK == 0x09)) || - ((ATQA0 == 0x42 || ATQA0 == 0x02) && (SAK == 0x18))) { + ((ATQA0 == 0x42 || ATQA0 == 0x02) && (SAK == 0x18)) || + ((ATQA0 == 0x01) && (ATQA1 == 0x0F) && (SAK == 0x01))) { type = MifareTypeClassic; } else if(ATQA0 == 0x44 && ATQA1 == 0x03 && SAK == 0x20) { type = MifareTypeDesfire; diff --git a/lib/nfc_protocols/mifare_common.h b/lib/nfc/protocols/mifare_common.h similarity index 100% rename from lib/nfc_protocols/mifare_common.h rename to lib/nfc/protocols/mifare_common.h diff --git a/lib/nfc_protocols/mifare_desfire.c b/lib/nfc/protocols/mifare_desfire.c similarity index 62% rename from lib/nfc_protocols/mifare_desfire.c rename to lib/nfc/protocols/mifare_desfire.c index 6f28dc5da..1822d5c1a 100644 --- a/lib/nfc_protocols/mifare_desfire.c +++ b/lib/nfc/protocols/mifare_desfire.c @@ -2,6 +2,8 @@ #include #include +#define TAG "MifareDESFire" + void mf_df_clear(MifareDesfireData* data) { free(data->free_memory); if(data->master_key_settings) { @@ -449,3 +451,173 @@ bool mf_df_parse_read_data_response(uint8_t* buf, uint16_t len, MifareDesfireFil memcpy(out->contents, buf, len); return true; } + +bool mf_df_read_card(FuriHalNfcTxRxContext* tx_rx, MifareDesfireData* data) { + furi_assert(tx_rx); + furi_assert(data); + + bool card_read = false; + do { + // Get version + tx_rx->tx_bits = 8 * mf_df_prepare_get_version(tx_rx->tx_data); + if(!furi_hal_nfc_tx_rx_full(tx_rx)) { + FURI_LOG_W(TAG, "Bad exchange getting version"); + break; + } + if(!mf_df_parse_get_version_response(tx_rx->rx_data, tx_rx->rx_bits / 8, &data->version)) { + FURI_LOG_W(TAG, "Bad DESFire GET_VERSION responce"); + } + + // Get free memory + tx_rx->tx_bits = 8 * mf_df_prepare_get_free_memory(tx_rx->tx_data); + if(furi_hal_nfc_tx_rx_full(tx_rx)) { + data->free_memory = malloc(sizeof(MifareDesfireFreeMemory)); + if(!mf_df_parse_get_free_memory_response( + tx_rx->rx_data, tx_rx->rx_bits / 8, data->free_memory)) { + FURI_LOG_D(TAG, "Bad DESFire GET_FREE_MEMORY response (normal for pre-EV1 cards)"); + free(data->free_memory); + data->free_memory = NULL; + } + } + + // Get key settings + tx_rx->tx_bits = 8 * mf_df_prepare_get_key_settings(tx_rx->tx_data); + if(!furi_hal_nfc_tx_rx_full(tx_rx)) { + FURI_LOG_D(TAG, "Bad exchange getting key settings"); + } else { + data->master_key_settings = malloc(sizeof(MifareDesfireKeySettings)); + if(!mf_df_parse_get_key_settings_response( + tx_rx->rx_data, tx_rx->rx_bits / 8, data->master_key_settings)) { + FURI_LOG_W(TAG, "Bad DESFire GET_KEY_SETTINGS response"); + free(data->master_key_settings); + data->master_key_settings = NULL; + } else { + MifareDesfireKeyVersion** key_version_head = + &data->master_key_settings->key_version_head; + for(uint8_t key_id = 0; key_id < data->master_key_settings->max_keys; key_id++) { + tx_rx->tx_bits = 8 * mf_df_prepare_get_key_version(tx_rx->tx_data, key_id); + if(!furi_hal_nfc_tx_rx_full(tx_rx)) { + FURI_LOG_W(TAG, "Bad exchange getting key version"); + continue; + } + MifareDesfireKeyVersion* key_version = malloc(sizeof(MifareDesfireKeyVersion)); + memset(key_version, 0, sizeof(MifareDesfireKeyVersion)); + key_version->id = key_id; + if(!mf_df_parse_get_key_version_response( + tx_rx->rx_data, tx_rx->rx_bits / 8, key_version)) { + FURI_LOG_W(TAG, "Bad DESFire GET_KEY_VERSION response"); + free(key_version); + continue; + } + *key_version_head = key_version; + key_version_head = &key_version->next; + } + } + } + + // Get application IDs + tx_rx->tx_bits = 8 * mf_df_prepare_get_application_ids(tx_rx->tx_data); + if(!furi_hal_nfc_tx_rx_full(tx_rx)) { + FURI_LOG_W(TAG, "Bad exchange getting application IDs"); + break; + } else { + if(!mf_df_parse_get_application_ids_response( + tx_rx->rx_data, tx_rx->rx_bits / 8, &data->app_head)) { + FURI_LOG_W(TAG, "Bad DESFire GET_APPLICATION_IDS response"); + break; + } + } + + for(MifareDesfireApplication* app = data->app_head; app; app = app->next) { + tx_rx->tx_bits = 8 * mf_df_prepare_select_application(tx_rx->tx_data, app->id); + if(!furi_hal_nfc_tx_rx_full(tx_rx) || + !mf_df_parse_select_application_response(tx_rx->rx_data, tx_rx->rx_bits / 8)) { + FURI_LOG_W(TAG, "Bad exchange selecting application"); + continue; + } + tx_rx->tx_bits = 8 * mf_df_prepare_get_key_settings(tx_rx->tx_data); + if(!furi_hal_nfc_tx_rx_full(tx_rx)) { + FURI_LOG_W(TAG, "Bad exchange getting key settings"); + } else { + app->key_settings = malloc(sizeof(MifareDesfireKeySettings)); + memset(app->key_settings, 0, sizeof(MifareDesfireKeySettings)); + if(!mf_df_parse_get_key_settings_response( + tx_rx->rx_data, tx_rx->rx_bits / 8, app->key_settings)) { + FURI_LOG_W(TAG, "Bad DESFire GET_KEY_SETTINGS response"); + free(app->key_settings); + app->key_settings = NULL; + continue; + } + + MifareDesfireKeyVersion** key_version_head = &app->key_settings->key_version_head; + for(uint8_t key_id = 0; key_id < app->key_settings->max_keys; key_id++) { + tx_rx->tx_bits = 8 * mf_df_prepare_get_key_version(tx_rx->tx_data, key_id); + if(!furi_hal_nfc_tx_rx_full(tx_rx)) { + FURI_LOG_W(TAG, "Bad exchange getting key version"); + continue; + } + MifareDesfireKeyVersion* key_version = malloc(sizeof(MifareDesfireKeyVersion)); + memset(key_version, 0, sizeof(MifareDesfireKeyVersion)); + key_version->id = key_id; + if(!mf_df_parse_get_key_version_response( + tx_rx->rx_data, tx_rx->rx_bits / 8, key_version)) { + FURI_LOG_W(TAG, "Bad DESFire GET_KEY_VERSION response"); + free(key_version); + continue; + } + *key_version_head = key_version; + key_version_head = &key_version->next; + } + } + + tx_rx->tx_bits = 8 * mf_df_prepare_get_file_ids(tx_rx->tx_data); + if(!furi_hal_nfc_tx_rx_full(tx_rx)) { + FURI_LOG_W(TAG, "Bad exchange getting file IDs"); + } else { + if(!mf_df_parse_get_file_ids_response( + tx_rx->rx_data, tx_rx->rx_bits / 8, &app->file_head)) { + FURI_LOG_W(TAG, "Bad DESFire GET_FILE_IDS response"); + } + } + + for(MifareDesfireFile* file = app->file_head; file; file = file->next) { + tx_rx->tx_bits = 8 * mf_df_prepare_get_file_settings(tx_rx->tx_data, file->id); + if(!furi_hal_nfc_tx_rx_full(tx_rx)) { + FURI_LOG_W(TAG, "Bad exchange getting file settings"); + continue; + } + if(!mf_df_parse_get_file_settings_response( + tx_rx->rx_data, tx_rx->rx_bits / 8, file)) { + FURI_LOG_W(TAG, "Bad DESFire GET_FILE_SETTINGS response"); + continue; + } + switch(file->type) { + case MifareDesfireFileTypeStandard: + case MifareDesfireFileTypeBackup: + tx_rx->tx_bits = 8 * mf_df_prepare_read_data(tx_rx->tx_data, file->id, 0, 0); + break; + case MifareDesfireFileTypeValue: + tx_rx->tx_bits = 8 * mf_df_prepare_get_value(tx_rx->tx_data, file->id); + break; + case MifareDesfireFileTypeLinearRecord: + case MifareDesfireFileTypeCyclicRecord: + tx_rx->tx_bits = + 8 * mf_df_prepare_read_records(tx_rx->tx_data, file->id, 0, 0); + break; + } + if(!furi_hal_nfc_tx_rx_full(tx_rx)) { + FURI_LOG_W(TAG, "Bad exchange reading file %d", file->id); + continue; + } + if(!mf_df_parse_read_data_response(tx_rx->rx_data, tx_rx->rx_bits / 8, file)) { + FURI_LOG_W(TAG, "Bad response reading file %d", file->id); + continue; + } + } + } + + card_read = true; + } while(false); + + return card_read; +} diff --git a/lib/nfc_protocols/mifare_desfire.h b/lib/nfc/protocols/mifare_desfire.h similarity index 98% rename from lib/nfc_protocols/mifare_desfire.h rename to lib/nfc/protocols/mifare_desfire.h index dbe0802eb..e59743a2f 100644 --- a/lib/nfc_protocols/mifare_desfire.h +++ b/lib/nfc/protocols/mifare_desfire.h @@ -4,6 +4,8 @@ #include #include +#include + #define MF_DF_GET_VERSION (0x60) #define MF_DF_GET_FREE_MEMORY (0x6E) #define MF_DF_GET_KEY_SETTINGS (0x45) @@ -163,3 +165,5 @@ uint16_t mf_df_prepare_read_data(uint8_t* dest, uint8_t file_id, uint32_t offset uint16_t mf_df_prepare_get_value(uint8_t* dest, uint8_t file_id); uint16_t mf_df_prepare_read_records(uint8_t* dest, uint8_t file_id, uint32_t offset, uint32_t len); bool mf_df_parse_read_data_response(uint8_t* buf, uint16_t len, MifareDesfireFile* out); + +bool mf_df_read_card(FuriHalNfcTxRxContext* tx_rx, MifareDesfireData* data); diff --git a/lib/nfc_protocols/mifare_ultralight.c b/lib/nfc/protocols/mifare_ultralight.c similarity index 84% rename from lib/nfc_protocols/mifare_ultralight.c rename to lib/nfc/protocols/mifare_ultralight.c index 21dbd9c4c..c043f2069 100644 --- a/lib/nfc_protocols/mifare_ultralight.c +++ b/lib/nfc/protocols/mifare_ultralight.c @@ -1,10 +1,39 @@ #include +#include #include "mifare_ultralight.h" +#include "nfc_util.h" #include +#include "furi_hal_nfc.h" #include #define TAG "MfUltralight" +// Algorithms from: https://github.com/RfidResearchGroup/proxmark3/blob/0f6061c16f072372b7d4d381911f1542afbc3a69/common/generator.c#L110 +uint32_t mf_ul_pwdgen_xiaomi(FuriHalNfcDevData* data) { + uint8_t hash[20]; + mbedtls_sha1(data->uid, data->uid_len, hash); + + uint32_t pwd = 0; + pwd |= (hash[hash[0] % 20]) << 24; + pwd |= (hash[(hash[0] + 5) % 20]) << 16; + pwd |= (hash[(hash[0] + 13) % 20]) << 8; + pwd |= (hash[(hash[0] + 17) % 20]); + + return pwd; +} + +uint32_t mf_ul_pwdgen_amiibo(FuriHalNfcDevData* data) { + uint8_t* uid = data->uid; + + uint32_t pwd = 0; + pwd |= (uid[1] ^ uid[3] ^ 0xAA) << 24; + pwd |= (uid[2] ^ uid[4] ^ 0x55) << 16; + pwd |= (uid[3] ^ uid[5] ^ 0xAA) << 8; + pwd |= uid[4] ^ uid[6] ^ 0x55; + + return pwd; +} + bool mf_ul_check_card_type(uint8_t ATQA0, uint8_t ATQA1, uint8_t SAK) { if((ATQA0 == 0x44) && (ATQA1 == 0x00) && (SAK == 0x00)) { return true; @@ -12,6 +41,20 @@ bool mf_ul_check_card_type(uint8_t ATQA0, uint8_t ATQA1, uint8_t SAK) { return false; } +void mf_ul_reset(MfUltralightData* data) { + furi_assert(data); + data->type = MfUltralightTypeUnknown; + memset(&data->version, 0, sizeof(MfUltralightVersion)); + memset(data->signature, 0, sizeof(data->signature)); + memset(data->counter, 0, sizeof(data->counter)); + memset(data->tearing, 0, sizeof(data->tearing)); + memset(data->data, 0, sizeof(data->data)); + data->data_size = 0; + data->data_read = 0; + data->curr_authlim = 0; + data->has_auth = false; +} + static MfUltralightFeatures mf_ul_get_features(MfUltralightType type) { switch(type) { case MfUltralightTypeUL11: @@ -35,9 +78,11 @@ static MfUltralightFeatures mf_ul_get_features(MfUltralightType type) { return MfUltralightSupportFastRead | MfUltralightSupportAuth | MfUltralightSupportFastWrite | MfUltralightSupportSignature | MfUltralightSupportSectorSelect; + case MfUltralightTypeNTAG203: + return MfUltralightSupportCompatWrite | MfUltralightSupportCounterInMemory; default: // Assumed original MFUL 512-bit - return MfUltralightSupportNone; + return MfUltralightSupportCompatWrite; } } @@ -46,6 +91,11 @@ static void mf_ul_set_default_version(MfUltralightReader* reader, MfUltralightDa reader->pages_to_read = 16; } +static void mf_ul_set_version_ntag203(MfUltralightReader* reader, MfUltralightData* data) { + data->type = MfUltralightTypeNTAG203; + reader->pages_to_read = 42; +} + bool mf_ultralight_read_version( FuriHalNfcTxRxContext* tx_rx, MfUltralightReader* reader, @@ -57,7 +107,7 @@ bool mf_ultralight_read_version( tx_rx->tx_data[0] = MF_UL_GET_VERSION_CMD; tx_rx->tx_bits = 8; tx_rx->tx_rx_type = FuriHalNfcTxRxTypeDefault; - if(!furi_hal_nfc_tx_rx(tx_rx, 50)) { + if(!furi_hal_nfc_tx_rx(tx_rx, 50) || tx_rx->rx_bits != 64) { FURI_LOG_D(TAG, "Failed reading version"); mf_ul_set_default_version(reader, data); furi_hal_nfc_sleep(); @@ -120,6 +170,37 @@ bool mf_ultralight_read_version( return version_read; } +bool mf_ultralight_authenticate(FuriHalNfcTxRxContext* tx_rx, uint32_t key, uint16_t* pack) { + bool authenticated = false; + + do { + FURI_LOG_D(TAG, "Authenticating"); + tx_rx->tx_data[0] = MF_UL_AUTH; + nfc_util_num2bytes(key, 4, &tx_rx->tx_data[1]); + tx_rx->tx_bits = 40; + tx_rx->tx_rx_type = FuriHalNfcTxRxTypeDefault; + if(!furi_hal_nfc_tx_rx(tx_rx, 50)) { + FURI_LOG_D(TAG, "Tag did not respond to authentication"); + break; + } + + // PACK + if(tx_rx->rx_bits < 2 * 8) { + FURI_LOG_D(TAG, "Authentication failed"); + break; + } + + if(pack != NULL) { + *pack = (tx_rx->rx_data[0] << 8) | tx_rx->rx_data[1]; + } + + FURI_LOG_I(TAG, "Auth success. Password: %08X. PACK: %04X", key, *pack); + authenticated = true; + } while(false); + + return authenticated; +} + static int16_t mf_ultralight_page_addr_to_tag_addr(uint8_t sector, uint8_t page) { return sector * 256 + page; } @@ -406,7 +487,7 @@ static int16_t mf_ultralight_ntag_i2c_addr_tag_to_lin( } } -static MfUltralightConfigPages* mf_ultralight_get_config_pages(MfUltralightData* data) { +MfUltralightConfigPages* mf_ultralight_get_config_pages(MfUltralightData* data) { if(data->type >= MfUltralightTypeUL11 && data->type <= MfUltralightTypeNTAG216) { return (MfUltralightConfigPages*)&data->data[data->data_size - 4 * 4]; } else if( @@ -468,6 +549,23 @@ static bool mf_ultralight_sector_select(FuriHalNfcTxRxContext* tx_rx, uint8_t se return true; } +bool mf_ultralight_read_pages_direct( + FuriHalNfcTxRxContext* tx_rx, + uint8_t start_index, + uint8_t* data) { + FURI_LOG_D(TAG, "Reading pages %d - %d", start_index, start_index + 3); + tx_rx->tx_data[0] = MF_UL_READ_CMD; + tx_rx->tx_data[1] = start_index; + tx_rx->tx_bits = 16; + tx_rx->tx_rx_type = FuriHalNfcTxRxTypeDefault; + if(!furi_hal_nfc_tx_rx(tx_rx, 50) || tx_rx->rx_bits < 16 * 8) { + FURI_LOG_D(TAG, "Failed to read pages %d - %d", start_index, start_index + 3); + return false; + } + memcpy(data, tx_rx->rx_data, 16); + return true; +} + bool mf_ultralight_read_pages( FuriHalNfcTxRxContext* tx_rx, MfUltralightReader* reader, @@ -492,6 +590,7 @@ bool mf_ultralight_read_pages( tx_rx->tx_data[1] = tag_page; tx_rx->tx_bits = 16; tx_rx->tx_rx_type = FuriHalNfcTxRxTypeDefault; + if(!furi_hal_nfc_tx_rx(tx_rx, 50) || tx_rx->rx_bits < 16 * 8) { FURI_LOG_D( TAG, @@ -500,17 +599,19 @@ bool mf_ultralight_read_pages( i + (valid_pages > 4 ? 4 : valid_pages) - 1); break; } + if(valid_pages > 4) { pages_read_cnt = 4; } else { pages_read_cnt = valid_pages; } reader->pages_read += pages_read_cnt; - data->data_size = reader->pages_read * 4; memcpy(&data->data[i * 4], tx_rx->rx_data, pages_read_cnt * 4); } + data->data_size = reader->pages_to_read * 4; + data->data_read = reader->pages_read * 4; - return reader->pages_read == reader->pages_to_read; + return reader->pages_read > 0; } bool mf_ultralight_fast_read_pages( @@ -596,6 +697,48 @@ bool mf_ultralight_read_counters(FuriHalNfcTxRxContext* tx_rx, MfUltralightData* return counter_read == (is_single_counter ? 1 : 3); } +int16_t mf_ultralight_get_authlim( + FuriHalNfcTxRxContext* tx_rx, + MfUltralightReader* reader, + MfUltralightData* data) { + mf_ultralight_read_version(tx_rx, reader, data); + if(!(reader->supported_features & MfUltralightSupportAuth)) { + // No authentication + return -2; + } + + uint8_t config_pages_index; + if(data->type >= MfUltralightTypeUL11 && data->type <= MfUltralightTypeNTAG216) { + config_pages_index = reader->pages_to_read - 4; + } else if( + data->type >= MfUltralightTypeNTAGI2CPlus1K && + data->type <= MfUltralightTypeNTAGI2CPlus1K) { + config_pages_index = 0xe3; + } else { + // No config pages + return -2; + } + + if(!mf_ultralight_read_pages_direct(tx_rx, config_pages_index, data->data)) { + // Config pages are not readable due to protection + return -1; + } + + MfUltralightConfigPages* config_pages = (MfUltralightConfigPages*)&data->data; + if(config_pages->auth0 >= reader->pages_to_read) { + // Authentication is not configured + return -2; + } + + int16_t authlim = config_pages->access.authlim; + if(authlim > 0 && data->type >= MfUltralightTypeNTAGI2CPlus1K && + data->type <= MfUltralightTypeNTAGI2CPlus2K) { + authlim = 1 << authlim; + } + + return authlim; +} + bool mf_ultralight_read_tearing_flags(FuriHalNfcTxRxContext* tx_rx, MfUltralightData* data) { uint8_t flag_read = 0; @@ -632,6 +775,17 @@ bool mf_ul_read_card( // Read Signature mf_ultralight_read_signature(tx_rx, data); } + } else { + // No GET_VERSION command, check for NTAG203 by reading last page (41) + uint8_t dummy[16]; + if(mf_ultralight_read_pages_direct(tx_rx, 41, dummy)) { + mf_ul_set_version_ntag203(reader, data); + reader->supported_features = mf_ul_get_features(data->type); + } else { + // We're really an original Mifare Ultralight, reset tag for safety + furi_hal_nfc_sleep(); + furi_hal_nfc_activate_nfca(300, NULL); + } } card_read = mf_ultralight_read_pages(tx_rx, reader, data); @@ -772,6 +926,8 @@ static bool mf_ul_ntag_i2c_plus_check_auth( static int16_t mf_ul_get_dynamic_lock_page_addr(MfUltralightData* data) { switch(data->type) { + case MfUltralightTypeNTAG203: + return 0x28; case MfUltralightTypeUL21: case MfUltralightTypeNTAG213: case MfUltralightTypeNTAG215: @@ -804,6 +960,10 @@ static bool mf_ul_check_lock(MfUltralightEmulator* emulator, int16_t write_page) // Check max page switch(emulator->data.type) { + case MfUltralightTypeNTAG203: + // Counter page can be locked and is after dynamic locks + if(write_page == 40) return true; + break; case MfUltralightTypeUL21: case MfUltralightTypeNTAG213: case MfUltralightTypeNTAG215: @@ -841,6 +1001,19 @@ static bool mf_ul_check_lock(MfUltralightEmulator* emulator, int16_t write_page) switch(emulator->data.type) { // low byte LSB range, MSB range + case MfUltralightTypeNTAG203: + if(write_page >= 16 && write_page <= 27) + shift = (write_page - 16) / 4 + 1; + else if(write_page >= 28 && write_page <= 39) + shift = (write_page - 28) / 4 + 5; + else if(write_page == 41) + shift = 12; + else { + furi_assert(false); + shift = 0; + } + + break; case MfUltralightTypeUL21: case MfUltralightTypeNTAG213: // 16-17, 30-31 @@ -937,6 +1110,42 @@ static void mf_ul_increment_single_counter(MfUltralightEmulator* emulator) { } } +static bool + mf_ul_emulate_ntag203_counter_write(MfUltralightEmulator* emulator, uint8_t* page_buff) { + // We'll reuse the existing counters for other NTAGs as staging + // Counter 0 stores original value, data is new value + uint32_t counter_value; + if(emulator->data.tearing[0] == MF_UL_TEARING_FLAG_DEFAULT) { + counter_value = emulator->data.data[MF_UL_NTAG203_COUNTER_PAGE * 4] | + (emulator->data.data[MF_UL_NTAG203_COUNTER_PAGE * 4 + 1] << 8); + } else { + // We've had a reset here, so load from original value + counter_value = emulator->data.counter[0]; + } + // Although the datasheet says increment by 0 is always possible, this is not the case on + // an actual tag. If the counter is at 0xFFFF, any writes are locked out. + if(counter_value == 0xFFFF) return false; + uint32_t increment = page_buff[0] | (page_buff[1] << 8); + if(counter_value == 0) { + counter_value = increment; + } else { + // Per datasheet specifying > 0x000F is supposed to NAK, but actual tag doesn't + increment &= 0x000F; + if(counter_value + increment > 0xFFFF) return false; + counter_value += increment; + } + // Commit to new value counter + emulator->data.data[MF_UL_NTAG203_COUNTER_PAGE * 4] = (uint8_t)counter_value; + emulator->data.data[MF_UL_NTAG203_COUNTER_PAGE * 4 + 1] = (uint8_t)(counter_value >> 8); + emulator->data.tearing[0] = MF_UL_TEARING_FLAG_DEFAULT; + if(counter_value == 0xFFFF) { + // Tag will lock out counter if final number is 0xFFFF, even if you try to roll it back + emulator->data.counter[1] = 0xFFFF; + } + emulator->data_changed = true; + return true; +} + static void mf_ul_emulate_write( MfUltralightEmulator* emulator, int16_t tag_addr, @@ -962,53 +1171,75 @@ static void mf_ul_emulate_write( *(uint32_t*)page_buff |= *(uint32_t*)&emulator->data.data[write_page * 4]; } else if(tag_addr == mf_ul_get_dynamic_lock_page_addr(&emulator->data)) { // Handle dynamic locks - uint16_t orig_locks = emulator->data.data[write_page * 4] | - (emulator->data.data[write_page * 4 + 1] << 8); - uint8_t orig_block_locks = emulator->data.data[write_page * 4 + 2]; - uint16_t new_locks = page_buff[0] | (page_buff[1] << 8); - uint8_t new_block_locks = page_buff[2]; - - int block_lock_count; - switch(emulator->data.type) { - case MfUltralightTypeUL21: - block_lock_count = 5; - break; - case MfUltralightTypeNTAG213: - block_lock_count = 6; - break; - case MfUltralightTypeNTAG215: - block_lock_count = 4; - break; - case MfUltralightTypeNTAG216: - case MfUltralightTypeNTAGI2C1K: - case MfUltralightTypeNTAGI2CPlus1K: - block_lock_count = 7; - break; - case MfUltralightTypeNTAGI2C2K: - case MfUltralightTypeNTAGI2CPlus2K: - block_lock_count = 8; - break; - default: - furi_assert(false); - block_lock_count = 0; - break; - } + if(emulator->data.type == MfUltralightTypeNTAG203) { + // NTAG203 lock bytes are a bit different from the others + uint8_t orig_page_lock_byte = emulator->data.data[write_page * 4]; + uint8_t orig_cnt_lock_byte = emulator->data.data[write_page * 4 + 1]; + uint8_t new_page_lock_byte = page_buff[0]; + uint8_t new_cnt_lock_byte = page_buff[1]; + + if(orig_page_lock_byte & 0x01) // Block lock bits 1-3 + new_page_lock_byte &= ~0x0E; + if(orig_page_lock_byte & 0x10) // Block lock bits 5-7 + new_page_lock_byte &= ~0xE0; + for(uint8_t i = 0; i < 4; ++i) { + if(orig_cnt_lock_byte & (1 << i)) // Block lock counter bit + new_cnt_lock_byte &= ~(1 << (4 + i)); + } - for(int i = 0; i < block_lock_count; ++i) { - if(orig_block_locks & (1 << i)) new_locks &= ~(3 << (2 * i)); - } + new_page_lock_byte |= orig_page_lock_byte; + new_cnt_lock_byte |= orig_cnt_lock_byte; + page_buff[0] = new_page_lock_byte; + page_buff[1] = new_cnt_lock_byte; + } else { + uint16_t orig_locks = emulator->data.data[write_page * 4] | + (emulator->data.data[write_page * 4 + 1] << 8); + uint8_t orig_block_locks = emulator->data.data[write_page * 4 + 2]; + uint16_t new_locks = page_buff[0] | (page_buff[1] << 8); + uint8_t new_block_locks = page_buff[2]; + + int block_lock_count; + switch(emulator->data.type) { + case MfUltralightTypeUL21: + block_lock_count = 5; + break; + case MfUltralightTypeNTAG213: + block_lock_count = 6; + break; + case MfUltralightTypeNTAG215: + block_lock_count = 4; + break; + case MfUltralightTypeNTAG216: + case MfUltralightTypeNTAGI2C1K: + case MfUltralightTypeNTAGI2CPlus1K: + block_lock_count = 7; + break; + case MfUltralightTypeNTAGI2C2K: + case MfUltralightTypeNTAGI2CPlus2K: + block_lock_count = 8; + break; + default: + furi_assert(false); + block_lock_count = 0; + break; + } - new_locks |= orig_locks; - new_block_locks |= orig_block_locks; + for(int i = 0; i < block_lock_count; ++i) { + if(orig_block_locks & (1 << i)) new_locks &= ~(3 << (2 * i)); + } - page_buff[0] = new_locks & 0xff; - page_buff[1] = new_locks >> 8; - page_buff[2] = new_block_locks; - if(emulator->data.type >= MfUltralightTypeUL21 && - emulator->data.type <= MfUltralightTypeNTAG216) - page_buff[3] = MF_UL_TEARING_FLAG_DEFAULT; - else - page_buff[3] = 0; + new_locks |= orig_locks; + new_block_locks |= orig_block_locks; + + page_buff[0] = new_locks & 0xff; + page_buff[1] = new_locks >> 8; + page_buff[2] = new_block_locks; + if(emulator->data.type >= MfUltralightTypeUL21 && + emulator->data.type <= MfUltralightTypeNTAG216) + page_buff[3] = MF_UL_TEARING_FLAG_DEFAULT; + else + page_buff[3] = 0; + } } memcpy(&emulator->data.data[write_page * 4], page_buff, 4); @@ -1025,6 +1256,18 @@ void mf_ul_reset_emulation(MfUltralightEmulator* emulator, bool is_power_cycle) if(emulator->supported_features & MfUltralightSupportSingleCounter) { emulator->read_counter_incremented = false; } + + if(emulator->data.type == MfUltralightTypeNTAG203) { + // Apply lockout if counter ever reached 0xFFFF + if(emulator->data.counter[1] == 0xFFFF) { + emulator->data.data[MF_UL_NTAG203_COUNTER_PAGE * 4] = 0xFF; + emulator->data.data[MF_UL_NTAG203_COUNTER_PAGE * 4 + 1] = 0xFF; + } + // Copy original counter value from data + emulator->data.counter[0] = + emulator->data.data[MF_UL_NTAG203_COUNTER_PAGE * 4] | + (emulator->data.data[MF_UL_NTAG203_COUNTER_PAGE * 4 + 1] << 8); + } } else { if(emulator->config != NULL) { // ACCESS (less CFGLCK) and AUTH0 are updated when reactivated @@ -1034,6 +1277,10 @@ void mf_ul_reset_emulation(MfUltralightEmulator* emulator, bool is_power_cycle) emulator->config_cache.auth0 = emulator->config->auth0; } } + if(emulator->data.type == MfUltralightTypeNTAG203) { + // Mark counter as dirty + emulator->data.tearing[0] = 0; + } } void mf_ul_prepare_emulation(MfUltralightEmulator* emulator, MfUltralightData* data) { @@ -1077,12 +1324,20 @@ bool mf_ul_prepare_emulation_response( // Check composite commands if(emulator->comp_write_cmd_started) { - // Compatibility write is the only one composit command if(buff_rx_len == 16 * 8) { - mf_ul_emulate_write( - emulator, emulator->comp_write_page_addr, emulator->comp_write_page_addr, buff_rx); - send_ack = true; - command_parsed = true; + if(emulator->data.type == MfUltralightTypeNTAG203 && + emulator->comp_write_page_addr == MF_UL_NTAG203_COUNTER_PAGE) { + send_ack = mf_ul_emulate_ntag203_counter_write(emulator, buff_rx); + command_parsed = send_ack; + } else { + mf_ul_emulate_write( + emulator, + emulator->comp_write_page_addr, + emulator->comp_write_page_addr, + buff_rx); + send_ack = true; + command_parsed = true; + } } emulator->comp_write_cmd_started = false; } else if(emulator->sector_select_cmd_started) { @@ -1099,7 +1354,7 @@ bool mf_ul_prepare_emulation_response( } else if(buff_rx_len >= 8) { uint8_t cmd = buff_rx[0]; if(cmd == MF_UL_GET_VERSION_CMD) { - if(emulator->data.type != MfUltralightTypeUnknown) { + if(emulator->data.type >= MfUltralightTypeUL11) { if(buff_rx_len == 1 * 8) { tx_bytes = sizeof(emulator->data.version); memcpy(buff_tx, &emulator->data.version, tx_bytes); @@ -1409,9 +1664,15 @@ bool mf_ul_prepare_emulation_response( int16_t tag_addr = mf_ultralight_page_addr_to_tag_addr( emulator->curr_sector, orig_write_page); if(!mf_ul_check_lock(emulator, tag_addr)) break; - mf_ul_emulate_write(emulator, tag_addr, write_page, &buff_rx[2]); - send_ack = true; - command_parsed = true; + if(emulator->data.type == MfUltralightTypeNTAG203 && + orig_write_page == MF_UL_NTAG203_COUNTER_PAGE) { + send_ack = mf_ul_emulate_ntag203_counter_write(emulator, &buff_rx[2]); + command_parsed = send_ack; + } else { + mf_ul_emulate_write(emulator, tag_addr, write_page, &buff_rx[2]); + send_ack = true; + command_parsed = true; + } } while(false); } } else if(cmd == MF_UL_FAST_WRITE) { @@ -1590,7 +1851,8 @@ bool mf_ul_prepare_emulation_response( } } } else { - reset_idle = true; + // NTAG203 appears to NAK instead of just falling off on invalid commands + if(emulator->data.type != MfUltralightTypeNTAG203) reset_idle = true; FURI_LOG_D(TAG, "Received invalid command"); } } else { diff --git a/lib/nfc_protocols/mifare_ultralight.h b/lib/nfc/protocols/mifare_ultralight.h similarity index 78% rename from lib/nfc_protocols/mifare_ultralight.h rename to lib/nfc/protocols/mifare_ultralight.h index 36b81fdf1..727bffab2 100644 --- a/lib/nfc_protocols/mifare_ultralight.h +++ b/lib/nfc/protocols/mifare_ultralight.h @@ -26,15 +26,29 @@ #define MF_UL_NAK_INVALID_ARGUMENT (0x0) #define MF_UL_NAK_AUTHLIM_REACHED (0x4) +#define MF_UL_NTAG203_COUNTER_PAGE (41) + +typedef enum { + MfUltralightAuthMethodManual, + MfUltralightAuthMethodAmeebo, + MfUltralightAuthMethodXiaomi, +} MfUltralightAuthMethod; + +// Important: order matters; some features are based on positioning in this enum typedef enum { MfUltralightTypeUnknown, + MfUltralightTypeNTAG203, + // Below have config pages and GET_VERSION support MfUltralightTypeUL11, MfUltralightTypeUL21, MfUltralightTypeNTAG213, MfUltralightTypeNTAG215, MfUltralightTypeNTAG216, + // Below also have sector select + // NTAG I2C's *does not* have regular config pages, so it's a bit of an odd duck MfUltralightTypeNTAGI2C1K, MfUltralightTypeNTAGI2C2K, + // NTAG I2C Plus has stucture expected from NTAG21x MfUltralightTypeNTAGI2CPlus1K, MfUltralightTypeNTAGI2CPlus2K, @@ -42,6 +56,13 @@ typedef enum { MfUltralightTypeNum, } MfUltralightType; +typedef enum { + MfUltralightAuthLimitUnknown, + MfUltralightAuthLimitNotSupported, + MfUltralightAuthLimitConfigured, + MfUltralightAuthLimitNotConfigured, +} MfUltralightAuthLimit; + typedef enum { MfUltralightSupportNone = 0, MfUltralightSupportFastRead = 1 << 0, @@ -58,6 +79,8 @@ typedef enum { MfUltralightSupportSingleCounter = 1 << 10, // ASCII mirror is not a command, but handy to have as a flag MfUltralightSupportAsciiMirror = 1 << 11, + // NTAG203 counter that's in memory rather than through a command + MfUltralightSupportCounterInMemory = 1 << 12, } MfUltralightFeatures; typedef enum { @@ -94,9 +117,14 @@ typedef struct { uint8_t signature[32]; uint32_t counter[3]; uint8_t tearing[3]; + bool has_auth; + MfUltralightAuthMethod auth_method; + uint8_t auth_key[4]; + bool auth_success; uint16_t curr_authlim; uint16_t data_size; uint8_t data[MF_UL_MAX_DUMP_SIZE]; + uint16_t data_read; } MfUltralightData; typedef struct __attribute__((packed)) { @@ -166,6 +194,8 @@ typedef struct { bool read_counter_incremented; } MfUltralightEmulator; +void mf_ul_reset(MfUltralightData* data); + bool mf_ul_check_card_type(uint8_t ATQA0, uint8_t ATQA1, uint8_t SAK); bool mf_ultralight_read_version( @@ -173,6 +203,11 @@ bool mf_ultralight_read_version( MfUltralightReader* reader, MfUltralightData* data); +bool mf_ultralight_read_pages_direct( + FuriHalNfcTxRxContext* tx_rx, + uint8_t start_index, + uint8_t* data); + bool mf_ultralight_read_pages( FuriHalNfcTxRxContext* tx_rx, MfUltralightReader* reader, @@ -189,6 +224,10 @@ bool mf_ultralight_read_counters(FuriHalNfcTxRxContext* tx_rx, MfUltralightData* bool mf_ultralight_read_tearing_flags(FuriHalNfcTxRxContext* tx_rx, MfUltralightData* data); +bool mf_ultralight_authenticate(FuriHalNfcTxRxContext* tx_rx, uint32_t key, uint16_t* pack); + +MfUltralightConfigPages* mf_ultralight_get_config_pages(MfUltralightData* data); + bool mf_ul_read_card( FuriHalNfcTxRxContext* tx_rx, MfUltralightReader* reader, @@ -205,3 +244,12 @@ bool mf_ul_prepare_emulation_response( uint16_t* buff_tx_len, uint32_t* data_type, void* context); + +int16_t mf_ultralight_get_authlim( + FuriHalNfcTxRxContext* tx_rx, + MfUltralightReader* reader, + MfUltralightData* data); + +uint32_t mf_ul_pwdgen_amiibo(FuriHalNfcDevData* data); + +uint32_t mf_ul_pwdgen_xiaomi(FuriHalNfcDevData* data); diff --git a/lib/nfc_protocols/nfc_util.c b/lib/nfc/protocols/nfc_util.c similarity index 100% rename from lib/nfc_protocols/nfc_util.c rename to lib/nfc/protocols/nfc_util.c diff --git a/lib/nfc_protocols/nfc_util.h b/lib/nfc/protocols/nfc_util.h similarity index 100% rename from lib/nfc_protocols/nfc_util.h rename to lib/nfc/protocols/nfc_util.h diff --git a/lib/nfc_protocols/nfca.c b/lib/nfc/protocols/nfca.c similarity index 100% rename from lib/nfc_protocols/nfca.c rename to lib/nfc/protocols/nfca.c diff --git a/lib/nfc_protocols/nfca.h b/lib/nfc/protocols/nfca.h similarity index 100% rename from lib/nfc_protocols/nfca.h rename to lib/nfc/protocols/nfca.h diff --git a/lib/print/SConscript b/lib/print/SConscript new file mode 100644 index 000000000..412d17a66 --- /dev/null +++ b/lib/print/SConscript @@ -0,0 +1,107 @@ +Import("env") + +wrapped_fn_list = [ + # + # used by our firmware, so we provide their realizations + # + "fflush", + "printf", + "putc", # fallback from printf, thanks gcc + "putchar", # storage cli + "puts", # fallback from printf, thanks gcc + "snprintf", + "vsnprintf", # m-string + "__assert", # ??? + "__assert_func", # ??? + # + # wrap other functions to make sure they are not called + # realization is not provided + # + "setbuf", + "setvbuf", + "fprintf", + "vfprintf", + "vprintf", + "fputc", + "fputs", + "sprintf", # specially, because this function is dangerous + "asprintf", + "vasprintf", + "asiprintf", + "asniprintf", + "asnprintf", + "diprintf", + "fiprintf", + "iprintf", + "siprintf", + "sniprintf", + "vasiprintf", + "vasniprintf", + "vasnprintf", + "vdiprintf", + "vfiprintf", + "viprintf", + "vsiprintf", + "vsniprintf", + # + # Scanf is not implemented 4 now + # + # "fscanf", + # "scanf", + # "sscanf", + # "vsprintf", + # "fgetc", + # "fgets", + # "getc", + # "getchar", + # "gets", + # "ungetc", + # "vfscanf", + # "vscanf", + # "vsscanf", + # "fiscanf", + # "iscanf", + # "siscanf", + # "vfiscanf", + # "viscanf", + # "vsiscanf", + # + # File management + # + # "fclose", + # "freopen", + # "fread", + # "fwrite", + # "fgetpos", + # "fseek", + # "fsetpos", + # "ftell", + # "rewind", + # "feof", + # "ferror", + # "fopen", + # "remove", + # "rename", + # "fseeko", + # "ftello", +] + +for wrapped_fn in wrapped_fn_list: + env.Append( + LINKFLAGS=[ + "-Wl,--wrap," + wrapped_fn, + "-Wl,--wrap," + wrapped_fn + "_unlocked", + "-Wl,--wrap,_" + wrapped_fn + "_r", + "-Wl,--wrap,_" + wrapped_fn + "_unlocked_r", + ] + ) + +libenv = env.Clone(FW_LIB_NAME="print") +libenv.ApplyLibFlags() +libenv.Append(CCFLAGS=["-Wno-double-promotion"]) + +sources = libenv.GlobRecursive("*.c*", ".") + +lib = libenv.StaticLibrary("${FW_LIB_NAME}", sources) +libenv.Install("${LIB_DIST_DIR}", lib) +Return("lib") diff --git a/lib/print/printf_tiny.c b/lib/print/printf_tiny.c new file mode 100644 index 000000000..0db11922d --- /dev/null +++ b/lib/print/printf_tiny.c @@ -0,0 +1,1037 @@ +/////////////////////////////////////////////////////////////////////////////// +// \author (c) Marco Paland (info@paland.com) +// 2014-2019, PALANDesign Hannover, Germany +// +// \license The MIT License (MIT) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +// \brief Tiny printf, sprintf and (v)snprintf implementation, optimized for speed on +// embedded systems with a very limited resources. These routines are thread +// safe and reentrant! +// Use this instead of the bloated standard/newlib printf cause these use +// malloc for printf (and may not be thread safe). +// +/////////////////////////////////////////////////////////////////////////////// + +#include +#include + +#include "printf_tiny.h" + +// define this globally (e.g. gcc -DPRINTF_INCLUDE_CONFIG_H ...) to include the +// printf_config.h header file +// default: undefined +#ifdef PRINTF_INCLUDE_CONFIG_H +#include "printf_config.h" +#endif + +// 'ntoa' conversion buffer size, this must be big enough to hold one converted +// numeric number including padded zeros (dynamically created on stack) +// default: 32 byte +#ifndef PRINTF_NTOA_BUFFER_SIZE +#define PRINTF_NTOA_BUFFER_SIZE 32U +#endif + +// 'ftoa' conversion buffer size, this must be big enough to hold one converted +// float number including padded zeros (dynamically created on stack) +// default: 32 byte +#ifndef PRINTF_FTOA_BUFFER_SIZE +#define PRINTF_FTOA_BUFFER_SIZE 32U +#endif + +// support for the floating point type (%f) +// default: activated +#ifndef PRINTF_DISABLE_SUPPORT_FLOAT +#define PRINTF_SUPPORT_FLOAT +#endif + +// support for exponential floating point notation (%e/%g) +// default: activated +#ifndef PRINTF_DISABLE_SUPPORT_EXPONENTIAL +#define PRINTF_SUPPORT_EXPONENTIAL +#endif + +// define the default floating point precision +// default: 6 digits +#ifndef PRINTF_DEFAULT_FLOAT_PRECISION +#define PRINTF_DEFAULT_FLOAT_PRECISION 6U +#endif + +// define the largest float suitable to print with %f +// default: 1e9 +#ifndef PRINTF_MAX_FLOAT +#define PRINTF_MAX_FLOAT 1e9 +#endif + +// support for the long long types (%llu or %p) +// default: activated +#ifndef PRINTF_DISABLE_SUPPORT_LONG_LONG +#define PRINTF_SUPPORT_LONG_LONG +#endif + +// support for the ptrdiff_t type (%t) +// ptrdiff_t is normally defined in as long or long long type +// default: activated +#ifndef PRINTF_DISABLE_SUPPORT_PTRDIFF_T +#define PRINTF_SUPPORT_PTRDIFF_T +#endif + +/////////////////////////////////////////////////////////////////////////////// + +// internal flag definitions +#define FLAGS_ZEROPAD (1U << 0U) +#define FLAGS_LEFT (1U << 1U) +#define FLAGS_PLUS (1U << 2U) +#define FLAGS_SPACE (1U << 3U) +#define FLAGS_HASH (1U << 4U) +#define FLAGS_UPPERCASE (1U << 5U) +#define FLAGS_CHAR (1U << 6U) +#define FLAGS_SHORT (1U << 7U) +#define FLAGS_LONG (1U << 8U) +#define FLAGS_LONG_LONG (1U << 9U) +#define FLAGS_PRECISION (1U << 10U) +#define FLAGS_ADAPT_EXP (1U << 11U) + +// import float.h for DBL_MAX +#if defined(PRINTF_SUPPORT_FLOAT) +#include +#endif + +// output function type +typedef void (*out_fct_type)(char character, void* buffer, size_t idx, size_t maxlen); + +// wrapper (used as buffer) for output function type +typedef struct { + void (*fct)(char character, void* arg); + void* arg; +} out_fct_wrap_type; + +// internal buffer output +static inline void _out_buffer(char character, void* buffer, size_t idx, size_t maxlen) { + if(idx < maxlen) { + ((char*)buffer)[idx] = character; + } +} + +// internal null output +static inline void _out_null(char character, void* buffer, size_t idx, size_t maxlen) { + (void)character; + (void)buffer; + (void)idx; + (void)maxlen; +} + +// internal _putchar wrapper +static inline void _out_char(char character, void* buffer, size_t idx, size_t maxlen) { + (void)buffer; + (void)idx; + (void)maxlen; + if(character) { + _putchar(character); + } +} + +// internal output function wrapper +static inline void _out_fct(char character, void* buffer, size_t idx, size_t maxlen) { + (void)idx; + (void)maxlen; + if(character) { + // buffer is the output fct pointer + ((out_fct_wrap_type*)buffer)->fct(character, ((out_fct_wrap_type*)buffer)->arg); + } +} + +// internal secure strlen +// \return The length of the string (excluding the terminating 0) limited by 'maxsize' +static inline unsigned int _strnlen_s(const char* str, size_t maxsize) { + const char* s; + for(s = str; *s && maxsize--; ++s) + ; + return (unsigned int)(s - str); +} + +// internal test if char is a digit (0-9) +// \return true if char is a digit +static inline bool _is_digit(char ch) { + return (ch >= '0') && (ch <= '9'); +} + +// internal ASCII string to unsigned int conversion +static unsigned int _atoi(const char** str) { + unsigned int i = 0U; + while(_is_digit(**str)) { + i = i * 10U + (unsigned int)(*((*str)++) - '0'); + } + return i; +} + +// output the specified string in reverse, taking care of any zero-padding +static size_t _out_rev( + out_fct_type out, + char* buffer, + size_t idx, + size_t maxlen, + const char* buf, + size_t len, + unsigned int width, + unsigned int flags) { + const size_t start_idx = idx; + + // pad spaces up to given width + if(!(flags & FLAGS_LEFT) && !(flags & FLAGS_ZEROPAD)) { + for(size_t i = len; i < width; i++) { + out(' ', buffer, idx++, maxlen); + } + } + + // reverse string + while(len) { + out(buf[--len], buffer, idx++, maxlen); + } + + // append pad spaces up to given width + if(flags & FLAGS_LEFT) { + while(idx - start_idx < width) { + out(' ', buffer, idx++, maxlen); + } + } + + return idx; +} + +// internal itoa format +static size_t _ntoa_format( + out_fct_type out, + char* buffer, + size_t idx, + size_t maxlen, + char* buf, + size_t len, + bool negative, + unsigned int base, + unsigned int prec, + unsigned int width, + unsigned int flags) { + // pad leading zeros + if(!(flags & FLAGS_LEFT)) { + if(width && (flags & FLAGS_ZEROPAD) && + (negative || (flags & (FLAGS_PLUS | FLAGS_SPACE)))) { + width--; + } + while((len < prec) && (len < PRINTF_NTOA_BUFFER_SIZE)) { + buf[len++] = '0'; + } + while((flags & FLAGS_ZEROPAD) && (len < width) && (len < PRINTF_NTOA_BUFFER_SIZE)) { + buf[len++] = '0'; + } + } + + // handle hash + if(flags & FLAGS_HASH) { + if(!(flags & FLAGS_PRECISION) && len && ((len == prec) || (len == width))) { + len--; + if(len && (base == 16U)) { + len--; + } + } + if((base == 16U) && !(flags & FLAGS_UPPERCASE) && (len < PRINTF_NTOA_BUFFER_SIZE)) { + buf[len++] = 'x'; + } else if((base == 16U) && (flags & FLAGS_UPPERCASE) && (len < PRINTF_NTOA_BUFFER_SIZE)) { + buf[len++] = 'X'; + } else if((base == 2U) && (len < PRINTF_NTOA_BUFFER_SIZE)) { + buf[len++] = 'b'; + } + if(len < PRINTF_NTOA_BUFFER_SIZE) { + buf[len++] = '0'; + } + } + + if(len < PRINTF_NTOA_BUFFER_SIZE) { + if(negative) { + buf[len++] = '-'; + } else if(flags & FLAGS_PLUS) { + buf[len++] = '+'; // ignore the space if the '+' exists + } else if(flags & FLAGS_SPACE) { + buf[len++] = ' '; + } + } + + return _out_rev(out, buffer, idx, maxlen, buf, len, width, flags); +} + +// internal itoa for 'long' type +static size_t _ntoa_long( + out_fct_type out, + char* buffer, + size_t idx, + size_t maxlen, + unsigned long value, + bool negative, + unsigned long base, + unsigned int prec, + unsigned int width, + unsigned int flags) { + char buf[PRINTF_NTOA_BUFFER_SIZE]; + size_t len = 0U; + + // no hash for 0 values + if(!value) { + flags &= ~FLAGS_HASH; + } + + // write if precision != 0 and value is != 0 + if(!(flags & FLAGS_PRECISION) || value) { + do { + const char digit = (char)(value % base); + buf[len++] = digit < 10 ? '0' + digit : + (flags & FLAGS_UPPERCASE ? 'A' : 'a') + digit - 10; + value /= base; + } while(value && (len < PRINTF_NTOA_BUFFER_SIZE)); + } + + return _ntoa_format( + out, buffer, idx, maxlen, buf, len, negative, (unsigned int)base, prec, width, flags); +} + +// internal itoa for 'long long' type +#if defined(PRINTF_SUPPORT_LONG_LONG) +static size_t _ntoa_long_long( + out_fct_type out, + char* buffer, + size_t idx, + size_t maxlen, + unsigned long long value, + bool negative, + unsigned long long base, + unsigned int prec, + unsigned int width, + unsigned int flags) { + char buf[PRINTF_NTOA_BUFFER_SIZE]; + size_t len = 0U; + + // no hash for 0 values + if(!value) { + flags &= ~FLAGS_HASH; + } + + // write if precision != 0 and value is != 0 + if(!(flags & FLAGS_PRECISION) || value) { + do { + const char digit = (char)(value % base); + buf[len++] = digit < 10 ? '0' + digit : + (flags & FLAGS_UPPERCASE ? 'A' : 'a') + digit - 10; + value /= base; + } while(value && (len < PRINTF_NTOA_BUFFER_SIZE)); + } + + return _ntoa_format( + out, buffer, idx, maxlen, buf, len, negative, (unsigned int)base, prec, width, flags); +} +#endif // PRINTF_SUPPORT_LONG_LONG + +#if defined(PRINTF_SUPPORT_FLOAT) + +#if defined(PRINTF_SUPPORT_EXPONENTIAL) +// forward declaration so that _ftoa can switch to exp notation for values > PRINTF_MAX_FLOAT +static size_t _etoa( + out_fct_type out, + char* buffer, + size_t idx, + size_t maxlen, + double value, + unsigned int prec, + unsigned int width, + unsigned int flags); +#endif + +// internal ftoa for fixed decimal floating point +static size_t _ftoa( + out_fct_type out, + char* buffer, + size_t idx, + size_t maxlen, + double value, + unsigned int prec, + unsigned int width, + unsigned int flags) { + char buf[PRINTF_FTOA_BUFFER_SIZE]; + size_t len = 0U; + double diff = 0.0; + + // powers of 10 + static const double pow10[] = { + 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000}; + + // test for special values + if(value != value) return _out_rev(out, buffer, idx, maxlen, "nan", 3, width, flags); + if(value < -DBL_MAX) return _out_rev(out, buffer, idx, maxlen, "fni-", 4, width, flags); + if(value > DBL_MAX) + return _out_rev( + out, + buffer, + idx, + maxlen, + (flags & FLAGS_PLUS) ? "fni+" : "fni", + (flags & FLAGS_PLUS) ? 4U : 3U, + width, + flags); + + // test for very large values + // standard printf behavior is to print EVERY whole number digit -- which could be 100s of characters overflowing your buffers == bad + if((value > PRINTF_MAX_FLOAT) || (value < -PRINTF_MAX_FLOAT)) { +#if defined(PRINTF_SUPPORT_EXPONENTIAL) + return _etoa(out, buffer, idx, maxlen, value, prec, width, flags); +#else + return 0U; +#endif + } + + // test for negative + bool negative = false; + if(value < 0) { + negative = true; + value = 0 - value; + } + + // set default precision, if not set explicitly + if(!(flags & FLAGS_PRECISION)) { + prec = PRINTF_DEFAULT_FLOAT_PRECISION; + } + // limit precision to 9, cause a prec >= 10 can lead to overflow errors + while((len < PRINTF_FTOA_BUFFER_SIZE) && (prec > 9U)) { + buf[len++] = '0'; + prec--; + } + + int whole = (int)value; + double tmp = (value - whole) * pow10[prec]; + unsigned long frac = (unsigned long)tmp; + diff = tmp - frac; + + if(diff > 0.5) { + ++frac; + // handle rollover, e.g. case 0.99 with prec 1 is 1.0 + if(frac >= pow10[prec]) { + frac = 0; + ++whole; + } + } else if(diff < 0.5) { + } else if((frac == 0U) || (frac & 1U)) { + // if halfway, round up if odd OR if last digit is 0 + ++frac; + } + + if(prec == 0U) { + diff = value - (double)whole; + if((!(diff < 0.5) || (diff > 0.5)) && (whole & 1)) { + // exactly 0.5 and ODD, then round up + // 1.5 -> 2, but 2.5 -> 2 + ++whole; + } + } else { + unsigned int count = prec; + // now do fractional part, as an unsigned number + while(len < PRINTF_FTOA_BUFFER_SIZE) { + --count; + buf[len++] = (char)(48U + (frac % 10U)); + if(!(frac /= 10U)) { + break; + } + } + // add extra 0s + while((len < PRINTF_FTOA_BUFFER_SIZE) && (count-- > 0U)) { + buf[len++] = '0'; + } + if(len < PRINTF_FTOA_BUFFER_SIZE) { + // add decimal + buf[len++] = '.'; + } + } + + // do whole part, number is reversed + while(len < PRINTF_FTOA_BUFFER_SIZE) { + buf[len++] = (char)(48 + (whole % 10)); + if(!(whole /= 10)) { + break; + } + } + + // pad leading zeros + if(!(flags & FLAGS_LEFT) && (flags & FLAGS_ZEROPAD)) { + if(width && (negative || (flags & (FLAGS_PLUS | FLAGS_SPACE)))) { + width--; + } + while((len < width) && (len < PRINTF_FTOA_BUFFER_SIZE)) { + buf[len++] = '0'; + } + } + + if(len < PRINTF_FTOA_BUFFER_SIZE) { + if(negative) { + buf[len++] = '-'; + } else if(flags & FLAGS_PLUS) { + buf[len++] = '+'; // ignore the space if the '+' exists + } else if(flags & FLAGS_SPACE) { + buf[len++] = ' '; + } + } + + return _out_rev(out, buffer, idx, maxlen, buf, len, width, flags); +} + +#if defined(PRINTF_SUPPORT_EXPONENTIAL) +// internal ftoa variant for exponential floating-point type, contributed by Martijn Jasperse +static size_t _etoa( + out_fct_type out, + char* buffer, + size_t idx, + size_t maxlen, + double value, + unsigned int prec, + unsigned int width, + unsigned int flags) { + // check for NaN and special values + if((value != value) || (value > DBL_MAX) || (value < -DBL_MAX)) { + return _ftoa(out, buffer, idx, maxlen, value, prec, width, flags); + } + + // determine the sign + const bool negative = value < 0; + if(negative) { + value = -value; + } + + // default precision + if(!(flags & FLAGS_PRECISION)) { + prec = PRINTF_DEFAULT_FLOAT_PRECISION; + } + + // determine the decimal exponent + // based on the algorithm by David Gay (https://www.ampl.com/netlib/fp/dtoa.c) + union { + uint64_t U; + double F; + } conv; + + conv.F = value; + int exp2 = (int)((conv.U >> 52U) & 0x07FFU) - 1023; // effectively log2 + conv.U = (conv.U & ((1ULL << 52U) - 1U)) | + (1023ULL << 52U); // drop the exponent so conv.F is now in [1,2) + // now approximate log10 from the log2 integer part and an expansion of ln around 1.5 + int expval = + (int)(0.1760912590558 + exp2 * 0.301029995663981 + (conv.F - 1.5) * 0.289529654602168); + // now we want to compute 10^expval but we want to be sure it won't overflow + exp2 = (int)(expval * 3.321928094887362 + 0.5); + const double z = expval * 2.302585092994046 - exp2 * 0.6931471805599453; + const double z2 = z * z; + conv.U = (uint64_t)(exp2 + 1023) << 52U; + // compute exp(z) using continued fractions, see https://en.wikipedia.org/wiki/Exponential_function#Continued_fractions_for_ex + conv.F *= 1 + 2 * z / (2 - z + (z2 / (6 + (z2 / (10 + z2 / 14))))); + // correct for rounding errors + if(value < conv.F) { + expval--; + conv.F /= 10; + } + + // the exponent format is "%+03d" and largest value is "307", so set aside 4-5 characters + unsigned int minwidth = ((expval < 100) && (expval > -100)) ? 4U : 5U; + + // in "%g" mode, "prec" is the number of *significant figures* not decimals + if(flags & FLAGS_ADAPT_EXP) { + // do we want to fall-back to "%f" mode? + if((value >= 1e-4) && (value < 1e6)) { + if((int)prec > expval) { + prec = (unsigned)((int)prec - expval - 1); + } else { + prec = 0; + } + flags |= FLAGS_PRECISION; // make sure _ftoa respects precision + // no characters in exponent + minwidth = 0U; + expval = 0; + } else { + // we use one sigfig for the whole part + if((prec > 0) && (flags & FLAGS_PRECISION)) { + --prec; + } + } + } + + // will everything fit? + unsigned int fwidth = width; + if(width > minwidth) { + // we didn't fall-back so subtract the characters required for the exponent + fwidth -= minwidth; + } else { + // not enough characters, so go back to default sizing + fwidth = 0U; + } + if((flags & FLAGS_LEFT) && minwidth) { + // if we're padding on the right, DON'T pad the floating part + fwidth = 0U; + } + + // rescale the float value + if(expval) { + value /= conv.F; + } + + // output the floating part + const size_t start_idx = idx; + idx = _ftoa( + out, buffer, idx, maxlen, negative ? -value : value, prec, fwidth, flags & ~FLAGS_ADAPT_EXP); + + // output the exponent part + if(minwidth) { + // output the exponential symbol + out((flags & FLAGS_UPPERCASE) ? 'E' : 'e', buffer, idx++, maxlen); + // output the exponent value + idx = _ntoa_long( + out, + buffer, + idx, + maxlen, + (expval < 0) ? -expval : expval, + expval < 0, + 10, + 0, + minwidth - 1, + FLAGS_ZEROPAD | FLAGS_PLUS); + // might need to right-pad spaces + if(flags & FLAGS_LEFT) { + while(idx - start_idx < width) out(' ', buffer, idx++, maxlen); + } + } + return idx; +} +#endif // PRINTF_SUPPORT_EXPONENTIAL +#endif // PRINTF_SUPPORT_FLOAT + +// internal vsnprintf +static int + _vsnprintf(out_fct_type out, char* buffer, const size_t maxlen, const char* format, va_list va) { + unsigned int flags, width, precision, n; + size_t idx = 0U; + + if(!buffer) { + // use null output function + out = _out_null; + } + + while(*format) { + // format specifier? %[flags][width][.precision][length] + if(*format != '%') { + // no + out(*format, buffer, idx++, maxlen); + format++; + continue; + } else { + // yes, evaluate it + format++; + } + + // evaluate flags + flags = 0U; + do { + switch(*format) { + case '0': + flags |= FLAGS_ZEROPAD; + format++; + n = 1U; + break; + case '-': + flags |= FLAGS_LEFT; + format++; + n = 1U; + break; + case '+': + flags |= FLAGS_PLUS; + format++; + n = 1U; + break; + case ' ': + flags |= FLAGS_SPACE; + format++; + n = 1U; + break; + case '#': + flags |= FLAGS_HASH; + format++; + n = 1U; + break; + default: + n = 0U; + break; + } + } while(n); + + // evaluate width field + width = 0U; + if(_is_digit(*format)) { + width = _atoi(&format); + } else if(*format == '*') { + const int w = va_arg(va, int); + if(w < 0) { + flags |= FLAGS_LEFT; // reverse padding + width = (unsigned int)-w; + } else { + width = (unsigned int)w; + } + format++; + } + + // evaluate precision field + precision = 0U; + if(*format == '.') { + flags |= FLAGS_PRECISION; + format++; + if(_is_digit(*format)) { + precision = _atoi(&format); + } else if(*format == '*') { + const int prec = (int)va_arg(va, int); + precision = prec > 0 ? (unsigned int)prec : 0U; + format++; + } + } + + // evaluate length field + switch(*format) { + case 'l': + flags |= FLAGS_LONG; + format++; + if(*format == 'l') { + flags |= FLAGS_LONG_LONG; + format++; + } + break; + case 'h': + flags |= FLAGS_SHORT; + format++; + if(*format == 'h') { + flags |= FLAGS_CHAR; + format++; + } + break; +#if defined(PRINTF_SUPPORT_PTRDIFF_T) + case 't': + flags |= (sizeof(ptrdiff_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG); + format++; + break; +#endif + case 'j': + flags |= (sizeof(intmax_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG); + format++; + break; + case 'z': + flags |= (sizeof(size_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG); + format++; + break; + default: + break; + } + + // evaluate specifier + switch(*format) { + case 'd': + case 'i': + case 'u': + case 'x': + case 'X': + case 'o': + case 'b': { + // set the base + unsigned int base; + if(*format == 'x' || *format == 'X') { + base = 16U; + } else if(*format == 'o') { + base = 8U; + } else if(*format == 'b') { + base = 2U; + } else { + base = 10U; + flags &= ~FLAGS_HASH; // no hash for dec format + } + // uppercase + if(*format == 'X') { + flags |= FLAGS_UPPERCASE; + } + + // no plus or space flag for u, x, X, o, b + if((*format != 'i') && (*format != 'd')) { + flags &= ~(FLAGS_PLUS | FLAGS_SPACE); + } + + // ignore '0' flag when precision is given + if(flags & FLAGS_PRECISION) { + flags &= ~FLAGS_ZEROPAD; + } + + // convert the integer + if((*format == 'i') || (*format == 'd')) { + // signed + if(flags & FLAGS_LONG_LONG) { +#if defined(PRINTF_SUPPORT_LONG_LONG) + const long long value = va_arg(va, long long); + idx = _ntoa_long_long( + out, + buffer, + idx, + maxlen, + (unsigned long long)(value > 0 ? value : 0 - value), + value < 0, + base, + precision, + width, + flags); +#endif + } else if(flags & FLAGS_LONG) { + const long value = va_arg(va, long); + idx = _ntoa_long( + out, + buffer, + idx, + maxlen, + (unsigned long)(value > 0 ? value : 0 - value), + value < 0, + base, + precision, + width, + flags); + } else { + const int value = (flags & FLAGS_CHAR) ? (char)va_arg(va, int) : + (flags & FLAGS_SHORT) ? (short int)va_arg(va, int) : + va_arg(va, int); + idx = _ntoa_long( + out, + buffer, + idx, + maxlen, + (unsigned int)(value > 0 ? value : 0 - value), + value < 0, + base, + precision, + width, + flags); + } + } else { + // unsigned + if(flags & FLAGS_LONG_LONG) { +#if defined(PRINTF_SUPPORT_LONG_LONG) + idx = _ntoa_long_long( + out, + buffer, + idx, + maxlen, + va_arg(va, unsigned long long), + false, + base, + precision, + width, + flags); +#endif + } else if(flags & FLAGS_LONG) { + idx = _ntoa_long( + out, + buffer, + idx, + maxlen, + va_arg(va, unsigned long), + false, + base, + precision, + width, + flags); + } else { + const unsigned int value = + (flags & FLAGS_CHAR) ? (unsigned char)va_arg(va, unsigned int) : + (flags & FLAGS_SHORT) ? (unsigned short int)va_arg(va, unsigned int) : + va_arg(va, unsigned int); + idx = _ntoa_long( + out, buffer, idx, maxlen, value, false, base, precision, width, flags); + } + } + format++; + break; + } +#if defined(PRINTF_SUPPORT_FLOAT) + case 'f': + case 'F': + if(*format == 'F') flags |= FLAGS_UPPERCASE; + idx = _ftoa(out, buffer, idx, maxlen, va_arg(va, double), precision, width, flags); + format++; + break; +#if defined(PRINTF_SUPPORT_EXPONENTIAL) + case 'e': + case 'E': + case 'g': + case 'G': + if((*format == 'g') || (*format == 'G')) flags |= FLAGS_ADAPT_EXP; + if((*format == 'E') || (*format == 'G')) flags |= FLAGS_UPPERCASE; + idx = _etoa(out, buffer, idx, maxlen, va_arg(va, double), precision, width, flags); + format++; + break; +#endif // PRINTF_SUPPORT_EXPONENTIAL +#endif // PRINTF_SUPPORT_FLOAT + case 'c': { + unsigned int l = 1U; + // pre padding + if(!(flags & FLAGS_LEFT)) { + while(l++ < width) { + out(' ', buffer, idx++, maxlen); + } + } + // char output + out((char)va_arg(va, int), buffer, idx++, maxlen); + // post padding + if(flags & FLAGS_LEFT) { + while(l++ < width) { + out(' ', buffer, idx++, maxlen); + } + } + format++; + break; + } + + case 's': { + const char* p = va_arg(va, char*); + unsigned int l = _strnlen_s(p, precision ? precision : (size_t)-1); + // pre padding + if(flags & FLAGS_PRECISION) { + l = (l < precision ? l : precision); + } + if(!(flags & FLAGS_LEFT)) { + while(l++ < width) { + out(' ', buffer, idx++, maxlen); + } + } + // string output + while((*p != 0) && (!(flags & FLAGS_PRECISION) || precision--)) { + out(*(p++), buffer, idx++, maxlen); + } + // post padding + if(flags & FLAGS_LEFT) { + while(l++ < width) { + out(' ', buffer, idx++, maxlen); + } + } + format++; + break; + } + + case 'p': { + width = sizeof(void*) * 2U; + flags |= FLAGS_ZEROPAD | FLAGS_UPPERCASE; +#if defined(PRINTF_SUPPORT_LONG_LONG) + const bool is_ll = sizeof(uintptr_t) == sizeof(long long); + if(is_ll) { + idx = _ntoa_long_long( + out, + buffer, + idx, + maxlen, + (uintptr_t)va_arg(va, void*), + false, + 16U, + precision, + width, + flags); + } else { +#endif + idx = _ntoa_long( + out, + buffer, + idx, + maxlen, + (unsigned long)((uintptr_t)va_arg(va, void*)), + false, + 16U, + precision, + width, + flags); +#if defined(PRINTF_SUPPORT_LONG_LONG) + } +#endif + format++; + break; + } + + case '%': + out('%', buffer, idx++, maxlen); + format++; + break; + + default: + out(*format, buffer, idx++, maxlen); + format++; + break; + } + } + + // termination + out((char)0, buffer, idx < maxlen ? idx : maxlen - 1U, maxlen); + + // return written chars without terminating \0 + return (int)idx; +} + +/////////////////////////////////////////////////////////////////////////////// + +int printf_(const char* format, ...) { + va_list va; + va_start(va, format); + char buffer[1]; + const int ret = _vsnprintf(_out_char, buffer, (size_t)-1, format, va); + va_end(va); + return ret; +} + +int sprintf_(char* buffer, const char* format, ...) { + va_list va; + va_start(va, format); + const int ret = _vsnprintf(_out_buffer, buffer, (size_t)-1, format, va); + va_end(va); + return ret; +} + +int snprintf_(char* buffer, size_t count, const char* format, ...) { + va_list va; + va_start(va, format); + const int ret = _vsnprintf(_out_buffer, buffer, count, format, va); + va_end(va); + return ret; +} + +int vprintf_(const char* format, va_list va) { + char buffer[1]; + return _vsnprintf(_out_char, buffer, (size_t)-1, format, va); +} + +int vsnprintf_(char* buffer, size_t count, const char* format, va_list va) { + return _vsnprintf(_out_buffer, buffer, count, format, va); +} + +int fctprintf(void (*out)(char character, void* arg), void* arg, const char* format, ...) { + va_list va; + va_start(va, format); + const out_fct_wrap_type out_fct_wrap = {out, arg}; + const int ret = _vsnprintf(_out_fct, (char*)(uintptr_t)&out_fct_wrap, (size_t)-1, format, va); + va_end(va); + return ret; +} diff --git a/lib/print/printf_tiny.h b/lib/print/printf_tiny.h new file mode 100644 index 000000000..8f292819e --- /dev/null +++ b/lib/print/printf_tiny.h @@ -0,0 +1,103 @@ +/////////////////////////////////////////////////////////////////////////////// +// \author (c) Marco Paland (info@paland.com) +// 2014-2019, PALANDesign Hannover, Germany +// +// \license The MIT License (MIT) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +// \brief Tiny printf, sprintf and snprintf implementation, optimized for speed on +// embedded systems with a very limited resources. +// Use this instead of bloated standard/newlib printf. +// These routines are thread safe and reentrant. +// +/////////////////////////////////////////////////////////////////////////////// + +#ifndef _PRINTF_H_ +#define _PRINTF_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Output a character to a custom device like UART, used by the printf() function + * This function is declared here only. You have to write your custom implementation somewhere + * \param character Character to output + */ +void _putchar(char character); + +/** + * Tiny printf implementation + * You have to implement _putchar if you use printf() + * To avoid conflicts with the regular printf() API it is overridden by macro defines + * and internal underscore-appended functions like printf_() are used + * \param format A string that specifies the format of the output + * \return The number of characters that are written into the array, not counting the terminating null character + */ +int printf_(const char* format, ...); + +/** + * Tiny sprintf implementation + * Due to security reasons (buffer overflow) YOU SHOULD CONSIDER USING (V)SNPRINTF INSTEAD! + * \param buffer A pointer to the buffer where to store the formatted string. MUST be big enough to store the output! + * \param format A string that specifies the format of the output + * \return The number of characters that are WRITTEN into the buffer, not counting the terminating null character + */ +int sprintf_(char* buffer, const char* format, ...); + +/** + * Tiny snprintf/vsnprintf implementation + * \param buffer A pointer to the buffer where to store the formatted string + * \param count The maximum number of characters to store in the buffer, including a terminating null character + * \param format A string that specifies the format of the output + * \param va A value identifying a variable arguments list + * \return The number of characters that COULD have been written into the buffer, not counting the terminating + * null character. A value equal or larger than count indicates truncation. Only when the returned value + * is non-negative and less than count, the string has been completely written. + */ +int snprintf_(char* buffer, size_t count, const char* format, ...); +int vsnprintf_(char* buffer, size_t count, const char* format, va_list va); + +/** + * Tiny vprintf implementation + * \param format A string that specifies the format of the output + * \param va A value identifying a variable arguments list + * \return The number of characters that are WRITTEN into the buffer, not counting the terminating null character + */ +int vprintf_(const char* format, va_list va); + +/** + * printf with output function + * You may use this as dynamic alternative to printf() with its fixed _putchar() output + * \param out An output function which takes one character and an argument pointer + * \param arg An argument pointer for user data passed to output function + * \param format A string that specifies the format of the output + * \return The number of characters that are sent to the output function, not counting the terminating null character + */ +int fctprintf(void (*out)(char character, void* arg), void* arg, const char* format, ...); + +#ifdef __cplusplus +} +#endif + +#endif // _PRINTF_H_ diff --git a/lib/print/wrappers.c b/lib/print/wrappers.c new file mode 100644 index 000000000..3fe446657 --- /dev/null +++ b/lib/print/wrappers.c @@ -0,0 +1,74 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include "printf_tiny.h" + +void _putchar(char character) { + furi_thread_stdout_write(&character, 1); +} + +int __wrap_printf(const char* format, ...) { + va_list args; + va_start(args, format); + int ret = vprintf_(format, args); + va_end(args); + + return ret; +} + +int __wrap_vsnprintf(char* str, size_t size, const char* format, va_list args) { + return vsnprintf_(str, size, format, args); +} + +int __wrap_puts(const char* str) { + size_t size = furi_thread_stdout_write(str, strlen(str)); + size += furi_thread_stdout_write("\n", 1); + return size; +} + +int __wrap_putchar(int ch) { + size_t size = furi_thread_stdout_write((char*)&ch, 1); + return size; +} + +int __wrap_putc(int ch, FILE* stream) { + UNUSED(stream); + size_t size = furi_thread_stdout_write((char*)&ch, 1); + return size; +} + +int __wrap_snprintf(char* str, size_t size, const char* format, ...) { + va_list args; + va_start(args, format); + int ret = __wrap_vsnprintf(str, size, format, args); + va_end(args); + + return ret; +} + +int __wrap_fflush(FILE* stream) { + UNUSED(stream); + furi_thread_stdout_flush(); + return 0; +} + +__attribute__((__noreturn__)) void __wrap___assert(const char* file, int line, const char* e) { + UNUSED(file); + UNUSED(line); + // TODO: message file and line number + furi_crash(e); +} + +__attribute__((__noreturn__)) void + __wrap___assert_func(const char* file, int line, const char* func, const char* e) { + UNUSED(file); + UNUSED(line); + UNUSED(func); + // TODO: message file and line number + furi_crash(e); +} \ No newline at end of file diff --git a/lib/subghz/blocks/generic.c b/lib/subghz/blocks/generic.c index 811a5fa73..353ff18bf 100644 --- a/lib/subghz/blocks/generic.c +++ b/lib/subghz/blocks/generic.c @@ -1,39 +1,29 @@ #include "generic.h" -#include "../types.h" #include #include #define TAG "SubGhzBlockGeneric" -bool subghz_block_generic_get_preset_name(FuriHalSubGhzPreset preset, string_t preset_str) { - const char* preset_name; - switch(preset) { - case FuriHalSubGhzPresetOok270Async: - preset_name = "FuriHalSubGhzPresetOok270Async"; - break; - case FuriHalSubGhzPresetOok650Async: - preset_name = "FuriHalSubGhzPresetOok650Async"; - break; - case FuriHalSubGhzPreset2FSKDev238Async: - preset_name = "FuriHalSubGhzPreset2FSKDev238Async"; - break; - case FuriHalSubGhzPreset2FSKDev476Async: - preset_name = "FuriHalSubGhzPreset2FSKDev476Async"; - break; - default: - FURI_LOG_E(TAG, "Unknown preset"); - return false; - break; +void subghz_block_generic_get_preset_name(const char* preset_name, string_t preset_str) { + const char* preset_name_temp; + if(!strcmp(preset_name, "AM270")) { + preset_name_temp = "FuriHalSubGhzPresetOok270Async"; + } else if(!strcmp(preset_name, "AM650")) { + preset_name_temp = "FuriHalSubGhzPresetOok650Async"; + } else if(!strcmp(preset_name, "FM238")) { + preset_name_temp = "FuriHalSubGhzPreset2FSKDev238Async"; + } else if(!strcmp(preset_name, "FM476")) { + preset_name_temp = "FuriHalSubGhzPreset2FSKDev476Async"; + } else { + preset_name_temp = "FuriHalSubGhzPresetCustom"; } - string_set(preset_str, preset_name); - return true; + string_set(preset_str, preset_name_temp); } bool subghz_block_generic_serialize( SubGhzBlockGeneric* instance, FlipperFormat* flipper_format, - uint32_t frequency, - FuriHalSubGhzPreset preset) { + SubGhzPresetDefinition* preset) { furi_assert(instance); bool res = false; string_t temp_str; @@ -46,17 +36,28 @@ bool subghz_block_generic_serialize( break; } - if(!flipper_format_write_uint32(flipper_format, "Frequency", &frequency, 1)) { + if(!flipper_format_write_uint32(flipper_format, "Frequency", &preset->frequency, 1)) { FURI_LOG_E(TAG, "Unable to add Frequency"); break; } - if(!subghz_block_generic_get_preset_name(preset, temp_str)) { - break; - } + + subghz_block_generic_get_preset_name(string_get_cstr(preset->name), temp_str); if(!flipper_format_write_string_cstr(flipper_format, "Preset", string_get_cstr(temp_str))) { FURI_LOG_E(TAG, "Unable to add Preset"); break; } + if(!strcmp(string_get_cstr(temp_str), "FuriHalSubGhzPresetCustom")) { + if(!flipper_format_write_string_cstr( + flipper_format, "Custom_preset_module", "CC1101")) { + FURI_LOG_E(TAG, "Unable to add Custom_preset_module"); + break; + } + if(!flipper_format_write_hex( + flipper_format, "Custom_preset_data", preset->data, preset->data_size)) { + FURI_LOG_E(TAG, "Unable to add Custom_preset_data"); + break; + } + } if(!flipper_format_write_string_cstr(flipper_format, "Protocol", instance->protocol_name)) { FURI_LOG_E(TAG, "Unable to add Protocol"); break; diff --git a/lib/subghz/blocks/generic.h b/lib/subghz/blocks/generic.h index 1a7bca83b..56a7fc2d3 100644 --- a/lib/subghz/blocks/generic.h +++ b/lib/subghz/blocks/generic.h @@ -7,6 +7,7 @@ #include #include "furi.h" #include "furi_hal.h" +#include "../types.h" typedef struct SubGhzBlockGeneric SubGhzBlockGeneric; @@ -20,26 +21,23 @@ struct SubGhzBlockGeneric { }; /** - * Get modulation name. - * @param preset modulation,FuriHalSubGhzPreset - * @param preset_str Output modulation name - * @return true On success + * Get name preset. + * @param preset_name name preset + * @param preset_str Output name preset */ -bool subghz_block_generic_get_preset_name(FuriHalSubGhzPreset preset, string_t preset_str); +void subghz_block_generic_get_preset_name(const char* preset_name, string_t preset_str); /** * Serialize data SubGhzBlockGeneric. * @param instance Pointer to a SubGhzBlockGeneric instance * @param flipper_format Pointer to a FlipperFormat instance - * @param frequency The frequency at which the signal was received, Hz - * @param preset The modulation on which the signal was received, FuriHalSubGhzPreset + * @param preset The modulation on which the signal was received, SubGhzPresetDefinition * @return true On success */ bool subghz_block_generic_serialize( SubGhzBlockGeneric* instance, FlipperFormat* flipper_format, - uint32_t frequency, - FuriHalSubGhzPreset preset); + SubGhzPresetDefinition* preset); /** * Deserialize data SubGhzBlockGeneric. diff --git a/lib/subghz/protocols/base.c b/lib/subghz/protocols/base.c index 90096c2b9..06542f5eb 100644 --- a/lib/subghz/protocols/base.c +++ b/lib/subghz/protocols/base.c @@ -26,14 +26,12 @@ bool subghz_protocol_decoder_base_get_string( bool subghz_protocol_decoder_base_serialize( SubGhzProtocolDecoderBase* decoder_base, FlipperFormat* flipper_format, - uint32_t frequency, - FuriHalSubGhzPreset preset) { + SubGhzPresetDefinition* preset) { bool status = false; if(decoder_base->protocol && decoder_base->protocol->decoder && decoder_base->protocol->decoder->serialize) { - status = decoder_base->protocol->decoder->serialize( - decoder_base, flipper_format, frequency, preset); + status = decoder_base->protocol->decoder->serialize(decoder_base, flipper_format, preset); } return status; diff --git a/lib/subghz/protocols/base.h b/lib/subghz/protocols/base.h index 19d9c4143..a1a7478d9 100644 --- a/lib/subghz/protocols/base.h +++ b/lib/subghz/protocols/base.h @@ -43,15 +43,13 @@ bool subghz_protocol_decoder_base_get_string( * Serialize data SubGhzProtocolDecoderBase. * @param decoder_base Pointer to a SubGhzProtocolDecoderBase instance * @param flipper_format Pointer to a FlipperFormat instance - * @param frequency The frequency at which the signal was received, Hz - * @param preset The modulation on which the signal was received, FuriHalSubGhzPreset + * @param preset The modulation on which the signal was received, SubGhzPresetDefinition * @return true On success */ bool subghz_protocol_decoder_base_serialize( SubGhzProtocolDecoderBase* decoder_base, FlipperFormat* flipper_format, - uint32_t frequency, - FuriHalSubGhzPreset preset); + SubGhzPresetDefinition* preset); /** * Deserialize data SubGhzProtocolDecoderBase. diff --git a/lib/subghz/protocols/bett.c b/lib/subghz/protocols/bett.c new file mode 100644 index 000000000..e32679d27 --- /dev/null +++ b/lib/subghz/protocols/bett.c @@ -0,0 +1,357 @@ +#include "bett.h" + +#include "../blocks/const.h" +#include "../blocks/decoder.h" +#include "../blocks/encoder.h" +#include "../blocks/generic.h" +#include "../blocks/math.h" + +// protocol BERNER / ELKA / TEDSEN / TELETASTER +#define TAG "SubGhzProtocolBETT" + +#define DIP_P 0b11 //(+) +#define DIP_O 0b10 //(0) +#define DIP_N 0b00 //(-) + +#define DIP_PATTERN "%c%c%c%c%c%c%c%c%c" +#define SHOW_DIP_P(dip, check_dip) \ + ((((dip >> 0x8) >> 0x8) == check_dip) ? '*' : '_'), \ + ((((dip >> 0xE) & 0x3) == check_dip) ? '*' : '_'), \ + ((((dip >> 0xC) & 0x3) == check_dip) ? '*' : '_'), \ + ((((dip >> 0xA) & 0x3) == check_dip) ? '*' : '_'), \ + ((((dip >> 0x8) & 0x3) == check_dip) ? '*' : '_'), \ + ((((dip >> 0x6) & 0x3) == check_dip) ? '*' : '_'), \ + ((((dip >> 0x4) & 0x3) == check_dip) ? '*' : '_'), \ + ((((dip >> 0x2) & 0x3) == check_dip) ? '*' : '_'), \ + ((((dip >> 0x0) & 0x3) == check_dip) ? '*' : '_') + +static const SubGhzBlockConst subghz_protocol_bett_const = { + .te_short = 340, + .te_long = 2000, + .te_delta = 150, + .min_count_bit_for_found = 18, +}; + +struct SubGhzProtocolDecoderBETT { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + SubGhzBlockGeneric generic; +}; + +struct SubGhzProtocolEncoderBETT { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolBlockEncoder encoder; + SubGhzBlockGeneric generic; +}; + +typedef enum { + BETTDecoderStepReset = 0, + BETTDecoderStepSaveDuration, + BETTDecoderStepCheckDuration, +} BETTDecoderStep; + +const SubGhzProtocolDecoder subghz_protocol_bett_decoder = { + .alloc = subghz_protocol_decoder_bett_alloc, + .free = subghz_protocol_decoder_bett_free, + + .feed = subghz_protocol_decoder_bett_feed, + .reset = subghz_protocol_decoder_bett_reset, + + .get_hash_data = subghz_protocol_decoder_bett_get_hash_data, + .serialize = subghz_protocol_decoder_bett_serialize, + .deserialize = subghz_protocol_decoder_bett_deserialize, + .get_string = subghz_protocol_decoder_bett_get_string, +}; + +const SubGhzProtocolEncoder subghz_protocol_bett_encoder = { + .alloc = subghz_protocol_encoder_bett_alloc, + .free = subghz_protocol_encoder_bett_free, + + .deserialize = subghz_protocol_encoder_bett_deserialize, + .stop = subghz_protocol_encoder_bett_stop, + .yield = subghz_protocol_encoder_bett_yield, +}; + +const SubGhzProtocol subghz_protocol_bett = { + .name = SUBGHZ_PROTOCOL_BETT_NAME, + .type = SubGhzProtocolTypeStatic, + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable | + SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send, + + .decoder = &subghz_protocol_bett_decoder, + .encoder = &subghz_protocol_bett_encoder, +}; + +void* subghz_protocol_encoder_bett_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolEncoderBETT* instance = malloc(sizeof(SubGhzProtocolEncoderBETT)); + + instance->base.protocol = &subghz_protocol_bett; + instance->generic.protocol_name = instance->base.protocol->name; + + instance->encoder.repeat = 10; + instance->encoder.size_upload = 52; //max 24bit*2 + 2 (start, stop) + instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); + instance->encoder.is_runing = false; + return instance; +} + +void subghz_protocol_encoder_bett_free(void* context) { + furi_assert(context); + SubGhzProtocolEncoderBETT* instance = context; + free(instance->encoder.upload); + free(instance); +} + +/** + * Generating an upload from data. + * @param instance Pointer to a SubGhzProtocolEncoderBETT instance + * @return true On success + */ +static bool subghz_protocol_encoder_bett_get_upload(SubGhzProtocolEncoderBETT* instance) { + furi_assert(instance); + size_t index = 0; + size_t size_upload = (instance->generic.data_count_bit * 2); + if(size_upload > instance->encoder.size_upload) { + FURI_LOG_E(TAG, "Size upload exceeds allocated encoder buffer."); + return false; + } else { + instance->encoder.size_upload = size_upload; + } + + for(uint8_t i = instance->generic.data_count_bit; i > 1; i--) { + if(bit_read(instance->generic.data, i - 1)) { + //send bit 1 + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_bett_const.te_long); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_bett_const.te_short); + } else { + //send bit 0 + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_bett_const.te_short); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_bett_const.te_long); + } + } + if(bit_read(instance->generic.data, 0)) { + //send bit 1 + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_bett_const.te_long); + instance->encoder.upload[index++] = level_duration_make( + false, + (uint32_t)subghz_protocol_bett_const.te_short + + subghz_protocol_bett_const.te_long * 7); + } else { + //send bit 0 + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_bett_const.te_short); + instance->encoder.upload[index++] = level_duration_make( + false, + (uint32_t)subghz_protocol_bett_const.te_long + subghz_protocol_bett_const.te_long * 7); + } + return true; +} + +bool subghz_protocol_encoder_bett_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolEncoderBETT* instance = context; + bool res = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + FURI_LOG_E(TAG, "Deserialize error"); + break; + } + if(instance->generic.data_count_bit != + subghz_protocol_bett_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } + //optional parameter parameter + flipper_format_read_uint32( + flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1); + + subghz_protocol_encoder_bett_get_upload(instance); + instance->encoder.is_runing = true; + + res = true; + } while(false); + + return res; +} + +void subghz_protocol_encoder_bett_stop(void* context) { + SubGhzProtocolEncoderBETT* instance = context; + instance->encoder.is_runing = false; +} + +LevelDuration subghz_protocol_encoder_bett_yield(void* context) { + SubGhzProtocolEncoderBETT* instance = context; + + if(instance->encoder.repeat == 0 || !instance->encoder.is_runing) { + instance->encoder.is_runing = false; + return level_duration_reset(); + } + + LevelDuration ret = instance->encoder.upload[instance->encoder.front]; + + if(++instance->encoder.front == instance->encoder.size_upload) { + instance->encoder.repeat--; + instance->encoder.front = 0; + } + + return ret; +} + +void* subghz_protocol_decoder_bett_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolDecoderBETT* instance = malloc(sizeof(SubGhzProtocolDecoderBETT)); + instance->base.protocol = &subghz_protocol_bett; + instance->generic.protocol_name = instance->base.protocol->name; + return instance; +} + +void subghz_protocol_decoder_bett_free(void* context) { + furi_assert(context); + SubGhzProtocolDecoderBETT* instance = context; + free(instance); +} + +void subghz_protocol_decoder_bett_reset(void* context) { + furi_assert(context); + SubGhzProtocolDecoderBETT* instance = context; + instance->decoder.parser_step = BETTDecoderStepReset; +} + +void subghz_protocol_decoder_bett_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + SubGhzProtocolDecoderBETT* instance = context; + + switch(instance->decoder.parser_step) { + case BETTDecoderStepReset: + if((!level) && (DURATION_DIFF(duration, subghz_protocol_bett_const.te_short * 42) < + subghz_protocol_bett_const.te_delta * 21)) { + //Found Preambula + instance->decoder.parser_step = BETTDecoderStepCheckDuration; + } + break; + case BETTDecoderStepSaveDuration: + if(!level) { + if(duration >= ((uint32_t)subghz_protocol_bett_const.te_short * 10 + + subghz_protocol_bett_const.te_delta)) { + instance->decoder.parser_step = BETTDecoderStepSaveDuration; + if(instance->decoder.decode_count_bit == + subghz_protocol_bett_const.min_count_bit_for_found) { + instance->generic.data = instance->decoder.decode_data; + instance->generic.data_count_bit = instance->decoder.decode_count_bit; + + if(instance->base.callback) + instance->base.callback(&instance->base, instance->base.context); + } else { + instance->decoder.parser_step = BETTDecoderStepReset; + } + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + break; + } else { + if((DURATION_DIFF(duration, subghz_protocol_bett_const.te_short) < + subghz_protocol_bett_const.te_delta) || + (DURATION_DIFF(duration, subghz_protocol_bett_const.te_long) < + subghz_protocol_bett_const.te_delta * 3)) { + instance->decoder.parser_step = BETTDecoderStepCheckDuration; + } else { + instance->decoder.parser_step = BETTDecoderStepReset; + } + } + } + break; + case BETTDecoderStepCheckDuration: + if(level) { + if(DURATION_DIFF(duration, subghz_protocol_bett_const.te_long) < + subghz_protocol_bett_const.te_delta * 3) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + instance->decoder.parser_step = BETTDecoderStepSaveDuration; + } else if( + DURATION_DIFF(duration, subghz_protocol_bett_const.te_short) < + subghz_protocol_bett_const.te_delta) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + instance->decoder.parser_step = BETTDecoderStepSaveDuration; + } else { + instance->decoder.parser_step = BETTDecoderStepReset; + } + } else { + instance->decoder.parser_step = BETTDecoderStepReset; + } + break; + } +} + +/** + * Analysis of received data + * @param instance Pointer to a SubGhzBlockGeneric* instance + */ +static void subghz_protocol_bett_check_remote_controller(SubGhzBlockGeneric* instance) { + uint32_t code_found_reverse = + subghz_protocol_blocks_reverse_key(instance->data, instance->data_count_bit); + + instance->serial = (code_found_reverse & 0xFF) << 12 | + ((code_found_reverse >> 8) & 0xFF) << 4 | + ((code_found_reverse >> 20) & 0x0F); + instance->btn = ((code_found_reverse >> 16) & 0x0F); +} + +uint8_t subghz_protocol_decoder_bett_get_hash_data(void* context) { + furi_assert(context); + SubGhzProtocolDecoderBETT* instance = context; + return subghz_protocol_blocks_get_hash_data( + &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); +} + +bool subghz_protocol_decoder_bett_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzPresetDefinition* preset) { + furi_assert(context); + SubGhzProtocolDecoderBETT* instance = context; + return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); +} + +bool subghz_protocol_decoder_bett_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolDecoderBETT* instance = context; + bool ret = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + break; + } + if(instance->generic.data_count_bit != + subghz_protocol_bett_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } + ret = true; + } while(false); + return ret; +} + +void subghz_protocol_decoder_bett_get_string(void* context, string_t output) { + furi_assert(context); + SubGhzProtocolDecoderBETT* instance = context; + subghz_protocol_bett_check_remote_controller(&instance->generic); + uint32_t data = (uint32_t)(instance->generic.data & 0xFFFFFF); + string_cat_printf( + output, + "%s %dbit\r\n" + "Key:%05lX\r\n" + " +: " DIP_PATTERN "\r\n" + " o: " DIP_PATTERN "\r\n" + " -: " DIP_PATTERN "\r\n", + instance->generic.protocol_name, + instance->generic.data_count_bit, + (uint32_t)(instance->generic.data & 0xFFFFFF), + SHOW_DIP_P(data, DIP_P), + SHOW_DIP_P(data, DIP_O), + SHOW_DIP_P(data, DIP_N)); +} diff --git a/lib/subghz/protocols/bett.h b/lib/subghz/protocols/bett.h new file mode 100644 index 000000000..48f32b3e0 --- /dev/null +++ b/lib/subghz/protocols/bett.h @@ -0,0 +1,107 @@ +#pragma once + +#include "base.h" + +#define SUBGHZ_PROTOCOL_BETT_NAME "BETT" + +typedef struct SubGhzProtocolDecoderBETT SubGhzProtocolDecoderBETT; +typedef struct SubGhzProtocolEncoderBETT SubGhzProtocolEncoderBETT; + +extern const SubGhzProtocolDecoder subghz_protocol_bett_decoder; +extern const SubGhzProtocolEncoder subghz_protocol_bett_encoder; +extern const SubGhzProtocol subghz_protocol_bett; + +/** + * Allocate SubGhzProtocolEncoderBETT. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolEncoderBETT* pointer to a SubGhzProtocolEncoderBETT instance + */ +void* subghz_protocol_encoder_bett_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolEncoderBETT. + * @param context Pointer to a SubGhzProtocolEncoderBETT instance + */ +void subghz_protocol_encoder_bett_free(void* context); + +/** + * Deserialize and generating an upload to send. + * @param context Pointer to a SubGhzProtocolEncoderBETT instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_encoder_bett_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Forced transmission stop. + * @param context Pointer to a SubGhzProtocolEncoderBETT instance + */ +void subghz_protocol_encoder_bett_stop(void* context); + +/** + * Getting the level and duration of the upload to be loaded into DMA. + * @param context Pointer to a SubGhzProtocolEncoderBETT instance + * @return LevelDuration + */ +LevelDuration subghz_protocol_encoder_bett_yield(void* context); + +/** + * Allocate SubGhzProtocolDecoderBETT. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolDecoderBETT* pointer to a SubGhzProtocolDecoderBETT instance + */ +void* subghz_protocol_decoder_bett_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolDecoderBETT. + * @param context Pointer to a SubGhzProtocolDecoderBETT instance + */ +void subghz_protocol_decoder_bett_free(void* context); + +/** + * Reset decoder SubGhzProtocolDecoderBETT. + * @param context Pointer to a SubGhzProtocolDecoderBETT instance + */ +void subghz_protocol_decoder_bett_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a SubGhzProtocolDecoderBETT instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void subghz_protocol_decoder_bett_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a SubGhzProtocolDecoderBETT instance + * @return hash Hash sum + */ +uint8_t subghz_protocol_decoder_bett_get_hash_data(void* context); + +/** + * Serialize data SubGhzProtocolDecoderBETT. + * @param context Pointer to a SubGhzProtocolDecoderBETT instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param preset The modulation on which the signal was received, SubGhzPresetDefinition + * @return true On success + */ +bool subghz_protocol_decoder_bett_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzPresetDefinition* preset); + +/** + * Deserialize data SubGhzProtocolDecoderBETT. + * @param context Pointer to a SubGhzProtocolDecoderBETT instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_decoder_bett_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a SubGhzProtocolDecoderBETT instance + * @param output Resulting text + */ +void subghz_protocol_decoder_bett_get_string(void* context, string_t output); diff --git a/lib/subghz/protocols/came.c b/lib/subghz/protocols/came.c index 8817b3803..d28b735c5 100644 --- a/lib/subghz/protocols/came.c +++ b/lib/subghz/protocols/came.c @@ -145,7 +145,13 @@ bool subghz_protocol_encoder_came_deserialize(void* context, FlipperFormat* flip FURI_LOG_E(TAG, "Deserialize error"); break; } - + if((instance->generic.data_count_bit != + subghz_protocol_came_const.min_count_bit_for_found) && + (instance->generic.data_count_bit != + 2 * subghz_protocol_came_const.min_count_bit_for_found)) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } //optional parameter parameter flipper_format_read_uint32( flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1); @@ -284,17 +290,30 @@ uint8_t subghz_protocol_decoder_came_get_hash_data(void* context) { bool subghz_protocol_decoder_came_serialize( void* context, FlipperFormat* flipper_format, - uint32_t frequency, - FuriHalSubGhzPreset preset) { + SubGhzPresetDefinition* preset) { furi_assert(context); SubGhzProtocolDecoderCame* instance = context; - return subghz_block_generic_serialize(&instance->generic, flipper_format, frequency, preset); + return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); } bool subghz_protocol_decoder_came_deserialize(void* context, FlipperFormat* flipper_format) { furi_assert(context); SubGhzProtocolDecoderCame* instance = context; - return subghz_block_generic_deserialize(&instance->generic, flipper_format); + bool ret = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + break; + } + if((instance->generic.data_count_bit != + subghz_protocol_came_const.min_count_bit_for_found) && + (instance->generic.data_count_bit != + 2 * subghz_protocol_came_const.min_count_bit_for_found)) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } + ret = true; + } while(false); + return ret; } void subghz_protocol_decoder_came_get_string(void* context, string_t output) { diff --git a/lib/subghz/protocols/came.h b/lib/subghz/protocols/came.h index 2fd1b7183..c2648c057 100644 --- a/lib/subghz/protocols/came.h +++ b/lib/subghz/protocols/came.h @@ -83,15 +83,13 @@ uint8_t subghz_protocol_decoder_came_get_hash_data(void* context); * Serialize data SubGhzProtocolDecoderCame. * @param context Pointer to a SubGhzProtocolDecoderCame instance * @param flipper_format Pointer to a FlipperFormat instance - * @param frequency The frequency at which the signal was received, Hz - * @param preset The modulation on which the signal was received, FuriHalSubGhzPreset + * @param preset The modulation on which the signal was received, SubGhzPresetDefinition * @return true On success */ bool subghz_protocol_decoder_came_serialize( void* context, FlipperFormat* flipper_format, - uint32_t frequency, - FuriHalSubGhzPreset preset); + SubGhzPresetDefinition* preset); /** * Deserialize data SubGhzProtocolDecoderCame. diff --git a/lib/subghz/protocols/came_atomo.c b/lib/subghz/protocols/came_atomo.c index ad98f83f1..0c3cdd8ad 100644 --- a/lib/subghz/protocols/came_atomo.c +++ b/lib/subghz/protocols/came_atomo.c @@ -301,17 +301,28 @@ uint8_t subghz_protocol_decoder_came_atomo_get_hash_data(void* context) { bool subghz_protocol_decoder_came_atomo_serialize( void* context, FlipperFormat* flipper_format, - uint32_t frequency, - FuriHalSubGhzPreset preset) { + SubGhzPresetDefinition* preset) { furi_assert(context); SubGhzProtocolDecoderCameAtomo* instance = context; - return subghz_block_generic_serialize(&instance->generic, flipper_format, frequency, preset); + return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); } bool subghz_protocol_decoder_came_atomo_deserialize(void* context, FlipperFormat* flipper_format) { furi_assert(context); SubGhzProtocolDecoderCameAtomo* instance = context; - return subghz_block_generic_deserialize(&instance->generic, flipper_format); + bool ret = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + break; + } + if(instance->generic.data_count_bit != + subghz_protocol_came_atomo_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } + ret = true; + } while(false); + return ret; } void subghz_protocol_decoder_came_atomo_get_string(void* context, string_t output) { diff --git a/lib/subghz/protocols/came_atomo.h b/lib/subghz/protocols/came_atomo.h index d1c856e16..70a79eca1 100644 --- a/lib/subghz/protocols/came_atomo.h +++ b/lib/subghz/protocols/came_atomo.h @@ -48,15 +48,13 @@ uint8_t subghz_protocol_decoder_came_atomo_get_hash_data(void* context); * Serialize data SubGhzProtocolDecoderCameAtomo. * @param context Pointer to a SubGhzProtocolDecoderCameAtomo instance * @param flipper_format Pointer to a FlipperFormat instance - * @param frequency The frequency at which the signal was received, Hz - * @param preset The modulation on which the signal was received, FuriHalSubGhzPreset + * @param preset The modulation on which the signal was received, SubGhzPresetDefinition * @return true On success */ bool subghz_protocol_decoder_came_atomo_serialize( void* context, FlipperFormat* flipper_format, - uint32_t frequency, - FuriHalSubGhzPreset preset); + SubGhzPresetDefinition* preset); /** * Deserialize data SubGhzProtocolDecoderCameAtomo. diff --git a/lib/subghz/protocols/came_twee.c b/lib/subghz/protocols/came_twee.c index 1b60e1119..ef352bf60 100644 --- a/lib/subghz/protocols/came_twee.c +++ b/lib/subghz/protocols/came_twee.c @@ -250,7 +250,11 @@ bool subghz_protocol_encoder_came_twee_deserialize(void* context, FlipperFormat* FURI_LOG_E(TAG, "Deserialize error"); break; } - + if(instance->generic.data_count_bit != + subghz_protocol_came_twee_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } //optional parameter parameter flipper_format_read_uint32( flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1); @@ -354,7 +358,7 @@ void subghz_protocol_decoder_came_twee_feed(void* context, bool level, uint32_t } else if( duration >= ((uint32_t)subghz_protocol_came_twee_const.te_long * 2 + subghz_protocol_came_twee_const.te_delta)) { - if(instance->decoder.decode_count_bit >= + if(instance->decoder.decode_count_bit == subghz_protocol_came_twee_const.min_count_bit_for_found) { instance->generic.data = instance->decoder.decode_data; instance->generic.data_count_bit = instance->decoder.decode_count_bit; @@ -418,17 +422,28 @@ uint8_t subghz_protocol_decoder_came_twee_get_hash_data(void* context) { bool subghz_protocol_decoder_came_twee_serialize( void* context, FlipperFormat* flipper_format, - uint32_t frequency, - FuriHalSubGhzPreset preset) { + SubGhzPresetDefinition* preset) { furi_assert(context); SubGhzProtocolDecoderCameTwee* instance = context; - return subghz_block_generic_serialize(&instance->generic, flipper_format, frequency, preset); + return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); } bool subghz_protocol_decoder_came_twee_deserialize(void* context, FlipperFormat* flipper_format) { furi_assert(context); SubGhzProtocolDecoderCameTwee* instance = context; - return subghz_block_generic_deserialize(&instance->generic, flipper_format); + bool ret = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + break; + } + if(instance->generic.data_count_bit != + subghz_protocol_came_twee_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } + ret = true; + } while(false); + return ret; } void subghz_protocol_decoder_came_twee_get_string(void* context, string_t output) { diff --git a/lib/subghz/protocols/came_twee.h b/lib/subghz/protocols/came_twee.h index 225f3c047..42e6ddaf3 100644 --- a/lib/subghz/protocols/came_twee.h +++ b/lib/subghz/protocols/came_twee.h @@ -83,15 +83,13 @@ uint8_t subghz_protocol_decoder_came_twee_get_hash_data(void* context); * Serialize data SubGhzProtocolDecoderCameTwee. * @param context Pointer to a SubGhzProtocolDecoderCameTwee instance * @param flipper_format Pointer to a FlipperFormat instance - * @param frequency The frequency at which the signal was received, Hz - * @param preset The modulation on which the signal was received, FuriHalSubGhzPreset + * @param preset The modulation on which the signal was received, SubGhzPresetDefinition * @return true On success */ bool subghz_protocol_decoder_came_twee_serialize( void* context, FlipperFormat* flipper_format, - uint32_t frequency, - FuriHalSubGhzPreset preset); + SubGhzPresetDefinition* preset); /** * Deserialize data SubGhzProtocolDecoderCameTwee. diff --git a/lib/subghz/protocols/chamberlain_code.c b/lib/subghz/protocols/chamberlain_code.c index 23ac8cc89..3128b71ec 100644 --- a/lib/subghz/protocols/chamberlain_code.c +++ b/lib/subghz/protocols/chamberlain_code.c @@ -215,7 +215,11 @@ bool subghz_protocol_encoder_chamb_code_deserialize(void* context, FlipperFormat FURI_LOG_E(TAG, "Deserialize error"); break; } - + if(instance->generic.data_count_bit < + subghz_protocol_chamb_code_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } //optional parameter parameter flipper_format_read_uint32( flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1); @@ -423,17 +427,28 @@ uint8_t subghz_protocol_decoder_chamb_code_get_hash_data(void* context) { bool subghz_protocol_decoder_chamb_code_serialize( void* context, FlipperFormat* flipper_format, - uint32_t frequency, - FuriHalSubGhzPreset preset) { + SubGhzPresetDefinition* preset) { furi_assert(context); SubGhzProtocolDecoderChamb_Code* instance = context; - return subghz_block_generic_serialize(&instance->generic, flipper_format, frequency, preset); + return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); } bool subghz_protocol_decoder_chamb_code_deserialize(void* context, FlipperFormat* flipper_format) { furi_assert(context); SubGhzProtocolDecoderChamb_Code* instance = context; - return subghz_block_generic_deserialize(&instance->generic, flipper_format); + bool ret = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + break; + } + if(instance->generic.data_count_bit < + subghz_protocol_chamb_code_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } + ret = true; + } while(false); + return ret; } void subghz_protocol_decoder_chamb_code_get_string(void* context, string_t output) { diff --git a/lib/subghz/protocols/chamberlain_code.h b/lib/subghz/protocols/chamberlain_code.h index f6ef48fdb..1ac2f9f96 100644 --- a/lib/subghz/protocols/chamberlain_code.h +++ b/lib/subghz/protocols/chamberlain_code.h @@ -83,15 +83,13 @@ uint8_t subghz_protocol_decoder_chamb_code_get_hash_data(void* context); * Serialize data SubGhzProtocolDecoderChamb_Code. * @param context Pointer to a SubGhzProtocolDecoderChamb_Code instance * @param flipper_format Pointer to a FlipperFormat instance - * @param frequency The frequency at which the signal was received, Hz - * @param preset The modulation on which the signal was received, FuriHalSubGhzPreset + * @param preset The modulation on which the signal was received, SubGhzPresetDefinition * @return true On success */ bool subghz_protocol_decoder_chamb_code_serialize( void* context, FlipperFormat* flipper_format, - uint32_t frequency, - FuriHalSubGhzPreset preset); + SubGhzPresetDefinition* preset); /** * Deserialize data SubGhzProtocolDecoderChamb_Code. diff --git a/lib/subghz/protocols/doitrand.c b/lib/subghz/protocols/doitrand.c new file mode 100644 index 000000000..c26cbc5b6 --- /dev/null +++ b/lib/subghz/protocols/doitrand.c @@ -0,0 +1,356 @@ +#include "doitrand.h" + +#include "../blocks/const.h" +#include "../blocks/decoder.h" +#include "../blocks/encoder.h" +#include "../blocks/generic.h" +#include "../blocks/math.h" + +#define TAG "SubGhzProtocolDoitrand" + +#define DIP_PATTERN "%c%c%c%c%c%c%c%c%c%c" +#define CNT_TO_DIP(dip) \ + (dip & 0x0001 ? '1' : '0'), (dip & 0x0100 ? '1' : '0'), (dip & 0x0080 ? '1' : '0'), \ + (dip & 0x0040 ? '1' : '0'), (dip & 0x0020 ? '1' : '0'), (dip & 0x1000 ? '1' : '0'), \ + (dip & 0x0800 ? '1' : '0'), (dip & 0x0400 ? '1' : '0'), (dip & 0x0200 ? '1' : '0'), \ + (dip & 0x0002 ? '1' : '0') + +static const SubGhzBlockConst subghz_protocol_doitrand_const = { + .te_short = 400, + .te_long = 1100, + .te_delta = 150, + .min_count_bit_for_found = 37, +}; + +struct SubGhzProtocolDecoderDoitrand { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + SubGhzBlockGeneric generic; +}; + +struct SubGhzProtocolEncoderDoitrand { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolBlockEncoder encoder; + SubGhzBlockGeneric generic; +}; + +typedef enum { + DoitrandDecoderStepReset = 0, + DoitrandDecoderStepFoundStartBit, + DoitrandDecoderStepSaveDuration, + DoitrandDecoderStepCheckDuration, +} DoitrandDecoderStep; + +const SubGhzProtocolDecoder subghz_protocol_doitrand_decoder = { + .alloc = subghz_protocol_decoder_doitrand_alloc, + .free = subghz_protocol_decoder_doitrand_free, + + .feed = subghz_protocol_decoder_doitrand_feed, + .reset = subghz_protocol_decoder_doitrand_reset, + + .get_hash_data = subghz_protocol_decoder_doitrand_get_hash_data, + .serialize = subghz_protocol_decoder_doitrand_serialize, + .deserialize = subghz_protocol_decoder_doitrand_deserialize, + .get_string = subghz_protocol_decoder_doitrand_get_string, +}; + +const SubGhzProtocolEncoder subghz_protocol_doitrand_encoder = { + .alloc = subghz_protocol_encoder_doitrand_alloc, + .free = subghz_protocol_encoder_doitrand_free, + + .deserialize = subghz_protocol_encoder_doitrand_deserialize, + .stop = subghz_protocol_encoder_doitrand_stop, + .yield = subghz_protocol_encoder_doitrand_yield, +}; + +const SubGhzProtocol subghz_protocol_doitrand = { + .name = SUBGHZ_PROTOCOL_DOITRAND_NAME, + .type = SubGhzProtocolTypeStatic, + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable | + SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send, + + .decoder = &subghz_protocol_doitrand_decoder, + .encoder = &subghz_protocol_doitrand_encoder, +}; + +void* subghz_protocol_encoder_doitrand_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolEncoderDoitrand* instance = malloc(sizeof(SubGhzProtocolEncoderDoitrand)); + + instance->base.protocol = &subghz_protocol_doitrand; + instance->generic.protocol_name = instance->base.protocol->name; + + instance->encoder.repeat = 10; + instance->encoder.size_upload = 128; + instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); + instance->encoder.is_runing = false; + return instance; +} + +void subghz_protocol_encoder_doitrand_free(void* context) { + furi_assert(context); + SubGhzProtocolEncoderDoitrand* instance = context; + free(instance->encoder.upload); + free(instance); +} + +/** + * Generating an upload from data. + * @param instance Pointer to a SubGhzProtocolEncoderDoitrand instance + * @return true On success + */ +static bool subghz_protocol_encoder_doitrand_get_upload(SubGhzProtocolEncoderDoitrand* instance) { + furi_assert(instance); + size_t index = 0; + size_t size_upload = (instance->generic.data_count_bit * 2) + 2; + if(size_upload > instance->encoder.size_upload) { + FURI_LOG_E(TAG, "Size upload exceeds allocated encoder buffer."); + return false; + } else { + instance->encoder.size_upload = size_upload; + } + //Send header + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_doitrand_const.te_short * 62); + //Send start bit + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_doitrand_const.te_short * 2 - 100); + //Send key data + for(uint8_t i = instance->generic.data_count_bit; i > 0; i--) { + if(bit_read(instance->generic.data, i - 1)) { + //send bit 1 + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_doitrand_const.te_long); + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_doitrand_const.te_short); + } else { + //send bit 0 + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_doitrand_const.te_short); + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_doitrand_const.te_long); + } + } + return true; +} + +bool subghz_protocol_encoder_doitrand_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolEncoderDoitrand* instance = context; + bool res = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + FURI_LOG_E(TAG, "Deserialize error"); + break; + } + if(instance->generic.data_count_bit != + subghz_protocol_doitrand_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } + //optional parameter parameter + flipper_format_read_uint32( + flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1); + + subghz_protocol_encoder_doitrand_get_upload(instance); + instance->encoder.is_runing = true; + + res = true; + } while(false); + + return res; +} + +void subghz_protocol_encoder_doitrand_stop(void* context) { + SubGhzProtocolEncoderDoitrand* instance = context; + instance->encoder.is_runing = false; +} + +LevelDuration subghz_protocol_encoder_doitrand_yield(void* context) { + SubGhzProtocolEncoderDoitrand* instance = context; + + if(instance->encoder.repeat == 0 || !instance->encoder.is_runing) { + instance->encoder.is_runing = false; + return level_duration_reset(); + } + + LevelDuration ret = instance->encoder.upload[instance->encoder.front]; + + if(++instance->encoder.front == instance->encoder.size_upload) { + instance->encoder.repeat--; + instance->encoder.front = 0; + } + + return ret; +} + +void* subghz_protocol_decoder_doitrand_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolDecoderDoitrand* instance = malloc(sizeof(SubGhzProtocolDecoderDoitrand)); + instance->base.protocol = &subghz_protocol_doitrand; + instance->generic.protocol_name = instance->base.protocol->name; + return instance; +} + +void subghz_protocol_decoder_doitrand_free(void* context) { + furi_assert(context); + SubGhzProtocolDecoderDoitrand* instance = context; + free(instance); +} + +void subghz_protocol_decoder_doitrand_reset(void* context) { + furi_assert(context); + SubGhzProtocolDecoderDoitrand* instance = context; + instance->decoder.parser_step = DoitrandDecoderStepReset; +} + +void subghz_protocol_decoder_doitrand_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + SubGhzProtocolDecoderDoitrand* instance = context; + + switch(instance->decoder.parser_step) { + case DoitrandDecoderStepReset: + if((!level) && (DURATION_DIFF(duration, subghz_protocol_doitrand_const.te_short * 62) < + subghz_protocol_doitrand_const.te_delta * 30)) { + //Found Preambula + instance->decoder.parser_step = DoitrandDecoderStepFoundStartBit; + } + break; + case DoitrandDecoderStepFoundStartBit: + if(level && ((DURATION_DIFF(duration, (subghz_protocol_doitrand_const.te_short * 2)) < + subghz_protocol_doitrand_const.te_delta * 3))) { + //Found start bit + instance->decoder.parser_step = DoitrandDecoderStepSaveDuration; + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + } else { + instance->decoder.parser_step = DoitrandDecoderStepReset; + } + break; + case DoitrandDecoderStepSaveDuration: + if(!level) { + if(duration >= ((uint32_t)subghz_protocol_doitrand_const.te_short * 10 + + subghz_protocol_doitrand_const.te_delta)) { + instance->decoder.parser_step = DoitrandDecoderStepFoundStartBit; + if(instance->decoder.decode_count_bit == + subghz_protocol_doitrand_const.min_count_bit_for_found) { + instance->generic.data = instance->decoder.decode_data; + instance->generic.data_count_bit = instance->decoder.decode_count_bit; + + if(instance->base.callback) + instance->base.callback(&instance->base, instance->base.context); + } + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + break; + } else { + instance->decoder.te_last = duration; + instance->decoder.parser_step = DoitrandDecoderStepCheckDuration; + } + } + break; + case DoitrandDecoderStepCheckDuration: + if(level) { + if((DURATION_DIFF(instance->decoder.te_last, subghz_protocol_doitrand_const.te_short) < + subghz_protocol_doitrand_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_doitrand_const.te_long) < + subghz_protocol_doitrand_const.te_delta * 3)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + instance->decoder.parser_step = DoitrandDecoderStepSaveDuration; + } else if( + (DURATION_DIFF(instance->decoder.te_last, subghz_protocol_doitrand_const.te_long) < + subghz_protocol_doitrand_const.te_delta * 3) && + (DURATION_DIFF(duration, subghz_protocol_doitrand_const.te_short) < + subghz_protocol_doitrand_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + instance->decoder.parser_step = DoitrandDecoderStepSaveDuration; + } else { + instance->decoder.parser_step = DoitrandDecoderStepReset; + } + } else { + instance->decoder.parser_step = DoitrandDecoderStepReset; + } + break; + } +} + +/** + * Analysis of received data + * @param instance Pointer to a SubGhzBlockGeneric* instance + */ +static void subghz_protocol_doitrand_check_remote_controller(SubGhzBlockGeneric* instance) { + /* +* 67892345 0 k 1 +* 0000082F5F => 00000000000000000 10 000010111101011111 +* 0002082F5F => 00000000000100000 10 000010111101011111 +* 0200082F5F => 00010000000000000 10 000010111101011111 +* 0400082F5F => 00100000000000000 10 000010111101011111 +* 0800082F5F => 01000000000000000 10 000010111101011111 +* 1000082F5F => 10000000000000000 10 000010111101011111 +* 0020082F5F => 00000001000000000 10 000010111101011111 +* 0040082F5F => 00000010000000000 10 000010111101011111 +* 0080082F5F => 00000100000000000 10 000010111101011111 +* 0100082F5F => 00001000000000000 10 000010111101011111 +* 000008AF5F => 00000000000000000 10 001010111101011111 +* 1FE208AF5F => 11111111000100000 10 001010111101011111 +* +* 0...9 - DIP +* k- KEY +*/ + instance->cnt = (instance->data >> 24) | ((instance->data >> 15) & 0x1); + instance->btn = ((instance->data >> 18) & 0x3); +} + +uint8_t subghz_protocol_decoder_doitrand_get_hash_data(void* context) { + furi_assert(context); + SubGhzProtocolDecoderDoitrand* instance = context; + return subghz_protocol_blocks_get_hash_data( + &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); +} + +bool subghz_protocol_decoder_doitrand_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzPresetDefinition* preset) { + furi_assert(context); + SubGhzProtocolDecoderDoitrand* instance = context; + return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); +} + +bool subghz_protocol_decoder_doitrand_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolDecoderDoitrand* instance = context; + bool ret = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + break; + } + if(instance->generic.data_count_bit != + subghz_protocol_doitrand_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } + ret = true; + } while(false); + return ret; +} + +void subghz_protocol_decoder_doitrand_get_string(void* context, string_t output) { + furi_assert(context); + SubGhzProtocolDecoderDoitrand* instance = context; + subghz_protocol_doitrand_check_remote_controller(&instance->generic); + string_cat_printf( + output, + "%s %dbit\r\n" + "Key:%02lX%08lX\r\n" + "Btn:%lX\r\n" + "DIP:" DIP_PATTERN "\r\n", + instance->generic.protocol_name, + instance->generic.data_count_bit, + (uint32_t)(instance->generic.data >> 32) & 0xFFFFFFFF, + (uint32_t)(instance->generic.data & 0xFFFFFFFF), + instance->generic.btn, + CNT_TO_DIP(instance->generic.cnt)); +} diff --git a/lib/subghz/protocols/doitrand.h b/lib/subghz/protocols/doitrand.h new file mode 100644 index 000000000..f94d73899 --- /dev/null +++ b/lib/subghz/protocols/doitrand.h @@ -0,0 +1,107 @@ +#pragma once + +#include "base.h" + +#define SUBGHZ_PROTOCOL_DOITRAND_NAME "Doitrand" + +typedef struct SubGhzProtocolDecoderDoitrand SubGhzProtocolDecoderDoitrand; +typedef struct SubGhzProtocolEncoderDoitrand SubGhzProtocolEncoderDoitrand; + +extern const SubGhzProtocolDecoder subghz_protocol_doitrand_decoder; +extern const SubGhzProtocolEncoder subghz_protocol_doitrand_encoder; +extern const SubGhzProtocol subghz_protocol_doitrand; + +/** + * Allocate SubGhzProtocolEncoderDoitrand. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolEncoderDoitrand* pointer to a SubGhzProtocolEncoderDoitrand instance + */ +void* subghz_protocol_encoder_doitrand_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolEncoderDoitrand. + * @param context Pointer to a SubGhzProtocolEncoderDoitrand instance + */ +void subghz_protocol_encoder_doitrand_free(void* context); + +/** + * Deserialize and generating an upload to send. + * @param context Pointer to a SubGhzProtocolEncoderDoitrand instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_encoder_doitrand_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Forced transmission stop. + * @param context Pointer to a SubGhzProtocolEncoderDoitrand instance + */ +void subghz_protocol_encoder_doitrand_stop(void* context); + +/** + * Getting the level and duration of the upload to be loaded into DMA. + * @param context Pointer to a SubGhzProtocolEncoderDoitrand instance + * @return LevelDuration + */ +LevelDuration subghz_protocol_encoder_doitrand_yield(void* context); + +/** + * Allocate SubGhzProtocolDecoderDoitrand. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolDecoderDoitrand* pointer to a SubGhzProtocolDecoderDoitrand instance + */ +void* subghz_protocol_decoder_doitrand_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolDecoderDoitrand. + * @param context Pointer to a SubGhzProtocolDecoderDoitrand instance + */ +void subghz_protocol_decoder_doitrand_free(void* context); + +/** + * Reset decoder SubGhzProtocolDecoderDoitrand. + * @param context Pointer to a SubGhzProtocolDecoderDoitrand instance + */ +void subghz_protocol_decoder_doitrand_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a SubGhzProtocolDecoderDoitrand instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void subghz_protocol_decoder_doitrand_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a SubGhzProtocolDecoderDoitrand instance + * @return hash Hash sum + */ +uint8_t subghz_protocol_decoder_doitrand_get_hash_data(void* context); + +/** + * Serialize data SubGhzProtocolDecoderDoitrand. + * @param context Pointer to a SubGhzProtocolDecoderDoitrand instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param preset The modulation on which the signal was received, SubGhzPresetDefinition + * @return true On success + */ +bool subghz_protocol_decoder_doitrand_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzPresetDefinition* preset); + +/** + * Deserialize data SubGhzProtocolDecoderDoitrand. + * @param context Pointer to a SubGhzProtocolDecoderDoitrand instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_decoder_doitrand_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a SubGhzProtocolDecoderDoitrand instance + * @param output Resulting text + */ +void subghz_protocol_decoder_doitrand_get_string(void* context, string_t output); diff --git a/lib/subghz/protocols/faac_slh.c b/lib/subghz/protocols/faac_slh.c index d5761831c..8b90f471c 100644 --- a/lib/subghz/protocols/faac_slh.c +++ b/lib/subghz/protocols/faac_slh.c @@ -115,7 +115,7 @@ void subghz_protocol_decoder_faac_slh_feed(void* context, bool level, uint32_t d if(duration >= ((uint32_t)subghz_protocol_faac_slh_const.te_short * 3 + subghz_protocol_faac_slh_const.te_delta)) { instance->decoder.parser_step = FaacSLHDecoderStepFoundPreambula; - if(instance->decoder.decode_count_bit >= + if(instance->decoder.decode_count_bit == subghz_protocol_faac_slh_const.min_count_bit_for_found) { instance->generic.data = instance->decoder.decode_data; instance->generic.data_count_bit = instance->decoder.decode_count_bit; @@ -183,17 +183,28 @@ uint8_t subghz_protocol_decoder_faac_slh_get_hash_data(void* context) { bool subghz_protocol_decoder_faac_slh_serialize( void* context, FlipperFormat* flipper_format, - uint32_t frequency, - FuriHalSubGhzPreset preset) { + SubGhzPresetDefinition* preset) { furi_assert(context); SubGhzProtocolDecoderFaacSLH* instance = context; - return subghz_block_generic_serialize(&instance->generic, flipper_format, frequency, preset); + return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); } bool subghz_protocol_decoder_faac_slh_deserialize(void* context, FlipperFormat* flipper_format) { furi_assert(context); SubGhzProtocolDecoderFaacSLH* instance = context; - return subghz_block_generic_deserialize(&instance->generic, flipper_format); + bool ret = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + break; + } + if(instance->generic.data_count_bit != + subghz_protocol_faac_slh_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } + ret = true; + } while(false); + return ret; } void subghz_protocol_decoder_faac_slh_get_string(void* context, string_t output) { diff --git a/lib/subghz/protocols/faac_slh.h b/lib/subghz/protocols/faac_slh.h index 2575a5ff5..fe7a79265 100644 --- a/lib/subghz/protocols/faac_slh.h +++ b/lib/subghz/protocols/faac_slh.h @@ -49,15 +49,13 @@ uint8_t subghz_protocol_decoder_faac_slh_get_hash_data(void* context); * Serialize data SubGhzProtocolDecoderFaacSLH. * @param context Pointer to a SubGhzProtocolDecoderFaacSLH instance * @param flipper_format Pointer to a FlipperFormat instance - * @param frequency The frequency at which the signal was received, Hz - * @param preset The modulation on which the signal was received, FuriHalSubGhzPreset + * @param preset The modulation on which the signal was received, SubGhzPresetDefinition * @return true On success */ bool subghz_protocol_decoder_faac_slh_serialize( void* context, FlipperFormat* flipper_format, - uint32_t frequency, - FuriHalSubGhzPreset preset); + SubGhzPresetDefinition* preset); /** * Deserialize data SubGhzProtocolDecoderFaacSLH. diff --git a/lib/subghz/protocols/gate_tx.c b/lib/subghz/protocols/gate_tx.c index 5f124c376..66174d011 100644 --- a/lib/subghz/protocols/gate_tx.c +++ b/lib/subghz/protocols/gate_tx.c @@ -138,7 +138,11 @@ bool subghz_protocol_encoder_gate_tx_deserialize(void* context, FlipperFormat* f FURI_LOG_E(TAG, "Deserialize error"); break; } - + if(instance->generic.data_count_bit != + subghz_protocol_gate_tx_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } //optional parameter parameter flipper_format_read_uint32( flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1); @@ -289,17 +293,28 @@ uint8_t subghz_protocol_decoder_gate_tx_get_hash_data(void* context) { bool subghz_protocol_decoder_gate_tx_serialize( void* context, FlipperFormat* flipper_format, - uint32_t frequency, - FuriHalSubGhzPreset preset) { + SubGhzPresetDefinition* preset) { furi_assert(context); SubGhzProtocolDecoderGateTx* instance = context; - return subghz_block_generic_serialize(&instance->generic, flipper_format, frequency, preset); + return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); } bool subghz_protocol_decoder_gate_tx_deserialize(void* context, FlipperFormat* flipper_format) { furi_assert(context); SubGhzProtocolDecoderGateTx* instance = context; - return subghz_block_generic_deserialize(&instance->generic, flipper_format); + bool ret = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + break; + } + if(instance->generic.data_count_bit != + subghz_protocol_gate_tx_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } + ret = true; + } while(false); + return ret; } void subghz_protocol_decoder_gate_tx_get_string(void* context, string_t output) { diff --git a/lib/subghz/protocols/gate_tx.h b/lib/subghz/protocols/gate_tx.h index 4b5f4d00c..171576816 100644 --- a/lib/subghz/protocols/gate_tx.h +++ b/lib/subghz/protocols/gate_tx.h @@ -83,15 +83,13 @@ uint8_t subghz_protocol_decoder_gate_tx_get_hash_data(void* context); * Serialize data SubGhzProtocolDecoderGateTx. * @param context Pointer to a SubGhzProtocolDecoderGateTx instance * @param flipper_format Pointer to a FlipperFormat instance - * @param frequency The frequency at which the signal was received, Hz - * @param preset The modulation on which the signal was received, FuriHalSubGhzPreset + * @param preset The modulation on which the signal was received, SubGhzPresetDefinition * @return true On success */ bool subghz_protocol_decoder_gate_tx_serialize( void* context, FlipperFormat* flipper_format, - uint32_t frequency, - FuriHalSubGhzPreset preset); + SubGhzPresetDefinition* preset); /** * Deserialize data SubGhzProtocolDecoderGateTx. diff --git a/lib/subghz/protocols/holtek.c b/lib/subghz/protocols/holtek.c index 174d023e5..ed5e4fb54 100644 --- a/lib/subghz/protocols/holtek.c +++ b/lib/subghz/protocols/holtek.c @@ -151,7 +151,11 @@ bool subghz_protocol_encoder_holtek_deserialize(void* context, FlipperFormat* fl FURI_LOG_E(TAG, "Deserialize error"); break; } - + if(instance->generic.data_count_bit != + subghz_protocol_holtek_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } //optional parameter parameter flipper_format_read_uint32( flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1); @@ -322,17 +326,28 @@ uint8_t subghz_protocol_decoder_holtek_get_hash_data(void* context) { bool subghz_protocol_decoder_holtek_serialize( void* context, FlipperFormat* flipper_format, - uint32_t frequency, - FuriHalSubGhzPreset preset) { + SubGhzPresetDefinition* preset) { furi_assert(context); SubGhzProtocolDecoderHoltek* instance = context; - return subghz_block_generic_serialize(&instance->generic, flipper_format, frequency, preset); + return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); } bool subghz_protocol_decoder_holtek_deserialize(void* context, FlipperFormat* flipper_format) { furi_assert(context); SubGhzProtocolDecoderHoltek* instance = context; - return subghz_block_generic_deserialize(&instance->generic, flipper_format); + bool ret = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + break; + } + if(instance->generic.data_count_bit != + subghz_protocol_holtek_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } + ret = true; + } while(false); + return ret; } void subghz_protocol_decoder_holtek_get_string(void* context, string_t output) { @@ -344,7 +359,7 @@ void subghz_protocol_decoder_holtek_get_string(void* context, string_t output) { output, "%s %dbit\r\n" "Key:0x%lX%08lX\r\n" - "Sn:0x%05lX BTN:%X ", + "Sn:0x%05lX Btn:%X ", instance->generic.protocol_name, instance->generic.data_count_bit, (uint32_t)((instance->generic.data >> 32) & 0xFFFFFFFF), diff --git a/lib/subghz/protocols/holtek.h b/lib/subghz/protocols/holtek.h index 0110fdd32..df7dd6448 100644 --- a/lib/subghz/protocols/holtek.h +++ b/lib/subghz/protocols/holtek.h @@ -83,15 +83,13 @@ uint8_t subghz_protocol_decoder_holtek_get_hash_data(void* context); * Serialize data SubGhzProtocolDecoderHoltek. * @param context Pointer to a SubGhzProtocolDecoderHoltek instance * @param flipper_format Pointer to a FlipperFormat instance - * @param frequency The frequency at which the signal was received, Hz - * @param preset The modulation on which the signal was received, FuriHalSubGhzPreset + * @param preset The modulation on which the signal was received, SubGhzPresetDefinition * @return true On success */ bool subghz_protocol_decoder_holtek_serialize( void* context, FlipperFormat* flipper_format, - uint32_t frequency, - FuriHalSubGhzPreset preset); + SubGhzPresetDefinition* preset); /** * Deserialize data SubGhzProtocolDecoderHoltek. diff --git a/lib/subghz/protocols/hormann.c b/lib/subghz/protocols/hormann.c index 20ff50fc0..ac6312511 100644 --- a/lib/subghz/protocols/hormann.c +++ b/lib/subghz/protocols/hormann.c @@ -154,7 +154,11 @@ bool subghz_protocol_encoder_hormann_deserialize(void* context, FlipperFormat* f FURI_LOG_E(TAG, "Deserialize error"); break; } - + if(instance->generic.data_count_bit != + subghz_protocol_hormann_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } //optional parameter parameter flipper_format_read_uint32( flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1); @@ -310,17 +314,28 @@ uint8_t subghz_protocol_decoder_hormann_get_hash_data(void* context) { bool subghz_protocol_decoder_hormann_serialize( void* context, FlipperFormat* flipper_format, - uint32_t frequency, - FuriHalSubGhzPreset preset) { + SubGhzPresetDefinition* preset) { furi_assert(context); SubGhzProtocolDecoderHormann* instance = context; - return subghz_block_generic_serialize(&instance->generic, flipper_format, frequency, preset); + return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); } bool subghz_protocol_decoder_hormann_deserialize(void* context, FlipperFormat* flipper_format) { furi_assert(context); SubGhzProtocolDecoderHormann* instance = context; - return subghz_block_generic_deserialize(&instance->generic, flipper_format); + bool ret = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + break; + } + if(instance->generic.data_count_bit != + subghz_protocol_hormann_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } + ret = true; + } while(false); + return ret; } void subghz_protocol_decoder_hormann_get_string(void* context, string_t output) { diff --git a/lib/subghz/protocols/hormann.h b/lib/subghz/protocols/hormann.h index 2b09a065e..45d6eb223 100644 --- a/lib/subghz/protocols/hormann.h +++ b/lib/subghz/protocols/hormann.h @@ -83,15 +83,13 @@ uint8_t subghz_protocol_decoder_hormann_get_hash_data(void* context); * Serialize data SubGhzProtocolDecoderHormann. * @param context Pointer to a SubGhzProtocolDecoderHormann instance * @param flipper_format Pointer to a FlipperFormat instance - * @param frequency The frequency at which the signal was received, Hz - * @param preset The modulation on which the signal was received, FuriHalSubGhzPreset + * @param preset The modulation on which the signal was received, SubGhzPresetDefinition * @return true On success */ bool subghz_protocol_decoder_hormann_serialize( void* context, FlipperFormat* flipper_format, - uint32_t frequency, - FuriHalSubGhzPreset preset); + SubGhzPresetDefinition* preset); /** * Deserialize data SubGhzProtocolDecoderHormann. diff --git a/lib/subghz/protocols/ido.c b/lib/subghz/protocols/ido.c index 93693b68f..914468445 100644 --- a/lib/subghz/protocols/ido.c +++ b/lib/subghz/protocols/ido.c @@ -182,17 +182,27 @@ uint8_t subghz_protocol_decoder_ido_get_hash_data(void* context) { bool subghz_protocol_decoder_ido_serialize( void* context, FlipperFormat* flipper_format, - uint32_t frequency, - FuriHalSubGhzPreset preset) { + SubGhzPresetDefinition* preset) { furi_assert(context); SubGhzProtocolDecoderIDo* instance = context; - return subghz_block_generic_serialize(&instance->generic, flipper_format, frequency, preset); + return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); } bool subghz_protocol_decoder_ido_deserialize(void* context, FlipperFormat* flipper_format) { furi_assert(context); SubGhzProtocolDecoderIDo* instance = context; - return subghz_block_generic_deserialize(&instance->generic, flipper_format); + bool ret = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + break; + } + if(instance->generic.data_count_bit != subghz_protocol_ido_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } + ret = true; + } while(false); + return ret; } void subghz_protocol_decoder_ido_get_string(void* context, string_t output) { diff --git a/lib/subghz/protocols/ido.h b/lib/subghz/protocols/ido.h index 266529e1f..4fe4e740a 100644 --- a/lib/subghz/protocols/ido.h +++ b/lib/subghz/protocols/ido.h @@ -49,15 +49,13 @@ uint8_t subghz_protocol_decoder_ido_get_hash_data(void* context); * Serialize data SubGhzProtocolDecoderIDo. * @param context Pointer to a SubGhzProtocolDecoderIDo instance * @param flipper_format Pointer to a FlipperFormat instance - * @param frequency The frequency at which the signal was received, Hz - * @param preset The modulation on which the signal was received, FuriHalSubGhzPreset + * @param preset The modulation on which the signal was received, SubGhzPresetDefinition * @return true On success */ bool subghz_protocol_decoder_ido_serialize( void* context, FlipperFormat* flipper_format, - uint32_t frequency, - FuriHalSubGhzPreset preset); + SubGhzPresetDefinition* preset); /** * Deserialize data SubGhzProtocolDecoderIDo. diff --git a/lib/subghz/protocols/keeloq.c b/lib/subghz/protocols/keeloq.c index 566ae5764..526a6b34e 100644 --- a/lib/subghz/protocols/keeloq.c +++ b/lib/subghz/protocols/keeloq.c @@ -174,8 +174,7 @@ bool subghz_protocol_keeloq_create_data( uint8_t btn, uint16_t cnt, const char* manufacture_name, - uint32_t frequency, - FuriHalSubGhzPreset preset) { + SubGhzPresetDefinition* preset) { furi_assert(context); SubGhzProtocolEncoderKeeloq* instance = context; instance->generic.serial = serial; @@ -184,8 +183,7 @@ bool subghz_protocol_keeloq_create_data( instance->generic.data_count_bit = 64; bool res = subghz_protocol_keeloq_gen_data(instance, btn); if(res) { - res = - subghz_block_generic_serialize(&instance->generic, flipper_format, frequency, preset); + res = subghz_block_generic_serialize(&instance->generic, flipper_format, preset); } return res; } @@ -266,7 +264,11 @@ bool subghz_protocol_encoder_keeloq_deserialize(void* context, FlipperFormat* fl FURI_LOG_E(TAG, "Deserialize error"); break; } - + if(instance->generic.data_count_bit != + subghz_protocol_keeloq_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } subghz_protocol_keeloq_check_remote_controller( &instance->generic, instance->keystore, &instance->manufacture_name); @@ -633,15 +635,13 @@ uint8_t subghz_protocol_decoder_keeloq_get_hash_data(void* context) { bool subghz_protocol_decoder_keeloq_serialize( void* context, FlipperFormat* flipper_format, - uint32_t frequency, - FuriHalSubGhzPreset preset) { + SubGhzPresetDefinition* preset) { furi_assert(context); SubGhzProtocolDecoderKeeloq* instance = context; subghz_protocol_keeloq_check_remote_controller( &instance->generic, instance->keystore, &instance->manufacture_name); - bool res = - subghz_block_generic_serialize(&instance->generic, flipper_format, frequency, preset); + bool res = subghz_block_generic_serialize(&instance->generic, flipper_format, preset); if(res && !flipper_format_write_string_cstr( flipper_format, "Manufacture", instance->manufacture_name)) { @@ -660,6 +660,11 @@ bool subghz_protocol_decoder_keeloq_deserialize(void* context, FlipperFormat* fl FURI_LOG_E(TAG, "Deserialize error"); break; } + if(instance->generic.data_count_bit != + subghz_protocol_keeloq_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } res = true; } while(false); diff --git a/lib/subghz/protocols/keeloq.h b/lib/subghz/protocols/keeloq.h index 72961ceb6..e1485e5ef 100644 --- a/lib/subghz/protocols/keeloq.h +++ b/lib/subghz/protocols/keeloq.h @@ -32,8 +32,7 @@ void subghz_protocol_encoder_keeloq_free(void* context); * @param btn Button number, 4 bit * @param cnt Container value, 16 bit * @param manufacture_name Name of manufacturer's key - * @param frequency Transmission frequency, Hz - * @param preset Modulation, FuriHalSubGhzPreset + * @param preset Modulation, SubGhzPresetDefinition * @return true On success */ bool subghz_protocol_keeloq_create_data( @@ -43,8 +42,7 @@ bool subghz_protocol_keeloq_create_data( uint8_t btn, uint16_t cnt, const char* manufacture_name, - uint32_t frequency, - FuriHalSubGhzPreset preset); + SubGhzPresetDefinition* preset); /** * Deserialize and generating an upload to send. @@ -105,15 +103,13 @@ uint8_t subghz_protocol_decoder_keeloq_get_hash_data(void* context); * Serialize data SubGhzProtocolDecoderKeeloq. * @param context Pointer to a SubGhzProtocolDecoderKeeloq instance * @param flipper_format Pointer to a FlipperFormat instance - * @param frequency The frequency at which the signal was received, Hz - * @param preset The modulation on which the signal was received, FuriHalSubGhzPreset + * @param preset The modulation on which the signal was received, SubGhzPresetDefinition * @return true On success */ bool subghz_protocol_decoder_keeloq_serialize( void* context, FlipperFormat* flipper_format, - uint32_t frequency, - FuriHalSubGhzPreset preset); + SubGhzPresetDefinition* preset); /** * Deserialize data SubGhzProtocolDecoderKeeloq. diff --git a/lib/subghz/protocols/kia.c b/lib/subghz/protocols/kia.c index 5a89934bd..6fc106170 100644 --- a/lib/subghz/protocols/kia.c +++ b/lib/subghz/protocols/kia.c @@ -12,7 +12,7 @@ static const SubGhzBlockConst subghz_protocol_kia_const = { .te_short = 250, .te_long = 500, .te_delta = 100, - .min_count_bit_for_found = 60, + .min_count_bit_for_found = 61, }; struct SubGhzProtocolDecoderKIA { @@ -145,7 +145,7 @@ void subghz_protocol_decoder_kia_feed(void* context, bool level, uint32_t durati (uint32_t)(subghz_protocol_kia_const.te_long + subghz_protocol_kia_const.te_delta * 2)) { //Found stop bit instance->decoder.parser_step = KIADecoderStepReset; - if(instance->decoder.decode_count_bit >= + if(instance->decoder.decode_count_bit == subghz_protocol_kia_const.min_count_bit_for_found) { instance->generic.data = instance->decoder.decode_data; instance->generic.data_count_bit = instance->decoder.decode_count_bit; @@ -233,17 +233,27 @@ uint8_t subghz_protocol_decoder_kia_get_hash_data(void* context) { bool subghz_protocol_decoder_kia_serialize( void* context, FlipperFormat* flipper_format, - uint32_t frequency, - FuriHalSubGhzPreset preset) { + SubGhzPresetDefinition* preset) { furi_assert(context); SubGhzProtocolDecoderKIA* instance = context; - return subghz_block_generic_serialize(&instance->generic, flipper_format, frequency, preset); + return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); } bool subghz_protocol_decoder_kia_deserialize(void* context, FlipperFormat* flipper_format) { furi_assert(context); SubGhzProtocolDecoderKIA* instance = context; - return subghz_block_generic_deserialize(&instance->generic, flipper_format); + bool ret = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + break; + } + if(instance->generic.data_count_bit != subghz_protocol_kia_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } + ret = true; + } while(false); + return ret; } void subghz_protocol_decoder_kia_get_string(void* context, string_t output) { diff --git a/lib/subghz/protocols/kia.h b/lib/subghz/protocols/kia.h index 40e496830..743ab7cbd 100644 --- a/lib/subghz/protocols/kia.h +++ b/lib/subghz/protocols/kia.h @@ -49,15 +49,13 @@ uint8_t subghz_protocol_decoder_kia_get_hash_data(void* context); * Serialize data SubGhzProtocolDecoderKIA. * @param context Pointer to a SubGhzProtocolDecoderKIA instance * @param flipper_format Pointer to a FlipperFormat instance - * @param frequency The frequency at which the signal was received, Hz - * @param preset The modulation on which the signal was received, FuriHalSubGhzPreset + * @param preset The modulation on which the signal was received, SubGhzPresetDefinition * @return true On success */ bool subghz_protocol_decoder_kia_serialize( void* context, FlipperFormat* flipper_format, - uint32_t frequency, - FuriHalSubGhzPreset preset); + SubGhzPresetDefinition* preset); /** * Deserialize data SubGhzProtocolDecoderKIA. diff --git a/lib/subghz/protocols/linear.c b/lib/subghz/protocols/linear.c index 6b0132788..c989a6183 100644 --- a/lib/subghz/protocols/linear.c +++ b/lib/subghz/protocols/linear.c @@ -156,7 +156,11 @@ bool subghz_protocol_encoder_linear_deserialize(void* context, FlipperFormat* fl FURI_LOG_E(TAG, "Deserialize error"); break; } - + if(instance->generic.data_count_bit != + subghz_protocol_linear_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } //optional parameter parameter flipper_format_read_uint32( flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1); @@ -299,17 +303,28 @@ uint8_t subghz_protocol_decoder_linear_get_hash_data(void* context) { bool subghz_protocol_decoder_linear_serialize( void* context, FlipperFormat* flipper_format, - uint32_t frequency, - FuriHalSubGhzPreset preset) { + SubGhzPresetDefinition* preset) { furi_assert(context); SubGhzProtocolDecoderLinear* instance = context; - return subghz_block_generic_serialize(&instance->generic, flipper_format, frequency, preset); + return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); } bool subghz_protocol_decoder_linear_deserialize(void* context, FlipperFormat* flipper_format) { furi_assert(context); SubGhzProtocolDecoderLinear* instance = context; - return subghz_block_generic_deserialize(&instance->generic, flipper_format); + bool ret = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + break; + } + if(instance->generic.data_count_bit != + subghz_protocol_linear_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } + ret = true; + } while(false); + return ret; } void subghz_protocol_decoder_linear_get_string(void* context, string_t output) { diff --git a/lib/subghz/protocols/linear.h b/lib/subghz/protocols/linear.h index 44f4e7ddc..035b130c4 100644 --- a/lib/subghz/protocols/linear.h +++ b/lib/subghz/protocols/linear.h @@ -83,15 +83,13 @@ uint8_t subghz_protocol_decoder_linear_get_hash_data(void* context); * Serialize data SubGhzProtocolDecoderLinear. * @param context Pointer to a SubGhzProtocolDecoderLinear instance * @param flipper_format Pointer to a FlipperFormat instance - * @param frequency The frequency at which the signal was received, Hz - * @param preset The modulation on which the signal was received, FuriHalSubGhzPreset + * @param preset The modulation on which the signal was received, SubGhzPresetDefinition * @return true On success */ bool subghz_protocol_decoder_linear_serialize( void* context, FlipperFormat* flipper_format, - uint32_t frequency, - FuriHalSubGhzPreset preset); + SubGhzPresetDefinition* preset); /** * Deserialize data SubGhzProtocolDecoderLinear. diff --git a/lib/subghz/protocols/marantec.c b/lib/subghz/protocols/marantec.c new file mode 100644 index 000000000..d46927246 --- /dev/null +++ b/lib/subghz/protocols/marantec.c @@ -0,0 +1,393 @@ +#include "marantec.h" +#include +#include +#include "../blocks/const.h" +#include "../blocks/decoder.h" +#include "../blocks/encoder.h" +#include "../blocks/generic.h" +#include "../blocks/math.h" + +#define TAG "SubGhzProtocolMarantec" + +static const SubGhzBlockConst subghz_protocol_marantec_const = { + .te_short = 1000, + .te_long = 2000, + .te_delta = 200, + .min_count_bit_for_found = 49, +}; + +struct SubGhzProtocolDecoderMarantec { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + SubGhzBlockGeneric generic; + ManchesterState manchester_saved_state; + uint16_t header_count; +}; + +struct SubGhzProtocolEncoderMarantec { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolBlockEncoder encoder; + SubGhzBlockGeneric generic; +}; + +typedef enum { + MarantecDecoderStepReset = 0, + MarantecDecoderFoundHeader, + MarantecDecoderStepDecoderData, +} MarantecDecoderStep; + +const SubGhzProtocolDecoder subghz_protocol_marantec_decoder = { + .alloc = subghz_protocol_decoder_marantec_alloc, + .free = subghz_protocol_decoder_marantec_free, + + .feed = subghz_protocol_decoder_marantec_feed, + .reset = subghz_protocol_decoder_marantec_reset, + + .get_hash_data = subghz_protocol_decoder_marantec_get_hash_data, + .serialize = subghz_protocol_decoder_marantec_serialize, + .deserialize = subghz_protocol_decoder_marantec_deserialize, + .get_string = subghz_protocol_decoder_marantec_get_string, +}; + +const SubGhzProtocolEncoder subghz_protocol_marantec_encoder = { + .alloc = subghz_protocol_encoder_marantec_alloc, + .free = subghz_protocol_encoder_marantec_free, + + .deserialize = subghz_protocol_encoder_marantec_deserialize, + .stop = subghz_protocol_encoder_marantec_stop, + .yield = subghz_protocol_encoder_marantec_yield, +}; + +const SubGhzProtocol subghz_protocol_marantec = { + .name = SUBGHZ_PROTOCOL_MARANTEC_NAME, + .type = SubGhzProtocolTypeStatic, + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable | + SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send, + + .decoder = &subghz_protocol_marantec_decoder, + .encoder = &subghz_protocol_marantec_encoder, +}; + +void* subghz_protocol_encoder_marantec_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolEncoderMarantec* instance = malloc(sizeof(SubGhzProtocolEncoderMarantec)); + + instance->base.protocol = &subghz_protocol_marantec; + instance->generic.protocol_name = instance->base.protocol->name; + + instance->encoder.repeat = 10; + instance->encoder.size_upload = 256; + instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); + instance->encoder.is_runing = false; + return instance; +} + +void subghz_protocol_encoder_marantec_free(void* context) { + furi_assert(context); + SubGhzProtocolEncoderMarantec* instance = context; + free(instance->encoder.upload); + free(instance); +} + +static LevelDuration + subghz_protocol_encoder_marantec_add_duration_to_upload(ManchesterEncoderResult result) { + LevelDuration data = {.duration = 0, .level = 0}; + switch(result) { + case ManchesterEncoderResultShortLow: + data.duration = subghz_protocol_marantec_const.te_short; + data.level = false; + break; + case ManchesterEncoderResultLongLow: + data.duration = subghz_protocol_marantec_const.te_long; + data.level = false; + break; + case ManchesterEncoderResultLongHigh: + data.duration = subghz_protocol_marantec_const.te_long; + data.level = true; + break; + case ManchesterEncoderResultShortHigh: + data.duration = subghz_protocol_marantec_const.te_short; + data.level = true; + break; + + default: + furi_crash("SubGhz: ManchesterEncoderResult is incorrect."); + break; + } + return level_duration_make(data.level, data.duration); +} + +/** + * Generating an upload from data. + * @param instance Pointer to a SubGhzProtocolEncoderMarantec instance + */ +static void subghz_protocol_encoder_marantec_get_upload(SubGhzProtocolEncoderMarantec* instance) { + furi_assert(instance); + size_t index = 0; + + ManchesterEncoderState enc_state; + manchester_encoder_reset(&enc_state); + ManchesterEncoderResult result; + + if(!manchester_encoder_advance( + &enc_state, + bit_read(instance->generic.data, instance->generic.data_count_bit - 1), + &result)) { + instance->encoder.upload[index++] = + subghz_protocol_encoder_marantec_add_duration_to_upload(result); + manchester_encoder_advance( + &enc_state, + bit_read(instance->generic.data, instance->generic.data_count_bit - 1), + &result); + } + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_marantec_const.te_long * 5); + + for(uint8_t i = instance->generic.data_count_bit - 1; i > 0; i--) { + if(!manchester_encoder_advance( + &enc_state, bit_read(instance->generic.data, i - 1), &result)) { + instance->encoder.upload[index++] = + subghz_protocol_encoder_marantec_add_duration_to_upload(result); + manchester_encoder_advance( + &enc_state, bit_read(instance->generic.data, i - 1), &result); + } + instance->encoder.upload[index++] = + subghz_protocol_encoder_marantec_add_duration_to_upload(result); + } + instance->encoder.upload[index] = subghz_protocol_encoder_marantec_add_duration_to_upload( + manchester_encoder_finish(&enc_state)); + if(level_duration_get_level(instance->encoder.upload[index])) { + index++; + } + instance->encoder.size_upload = index; +} + +uint8_t subghz_protocol_marantec_crc8(uint8_t* data, size_t len) { + uint8_t crc = 0x08; + size_t i, j; + for(i = 0; i < len; i++) { + crc ^= data[i]; + for(j = 0; j < 8; j++) { + if((crc & 0x80) != 0) + crc = (uint8_t)((crc << 1) ^ 0x1D); + else + crc <<= 1; + } + } + return crc; +} + +/** + * Analysis of received data + * @param instance Pointer to a SubGhzBlockGeneric* instance + */ +static void subghz_protocol_marantec_remote_controller(SubGhzBlockGeneric* instance) { + instance->btn = (instance->data >> 16) & 0xF; + instance->serial = ((instance->data >> 12) & 0xFFFFFF00) | ((instance->data >> 8) & 0xFF); +} + +bool subghz_protocol_encoder_marantec_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolEncoderMarantec* instance = context; + bool res = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + FURI_LOG_E(TAG, "Deserialize error"); + break; + } + if(instance->generic.data_count_bit != + subghz_protocol_marantec_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } + //optional parameter parameter + flipper_format_read_uint32( + flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1); + + subghz_protocol_marantec_remote_controller(&instance->generic); + subghz_protocol_encoder_marantec_get_upload(instance); + instance->encoder.is_runing = true; + + res = true; + } while(false); + + return res; +} + +void subghz_protocol_encoder_marantec_stop(void* context) { + SubGhzProtocolEncoderMarantec* instance = context; + instance->encoder.is_runing = false; +} + +LevelDuration subghz_protocol_encoder_marantec_yield(void* context) { + SubGhzProtocolEncoderMarantec* instance = context; + + if(instance->encoder.repeat == 0 || !instance->encoder.is_runing) { + instance->encoder.is_runing = false; + return level_duration_reset(); + } + + LevelDuration ret = instance->encoder.upload[instance->encoder.front]; + + if(++instance->encoder.front == instance->encoder.size_upload) { + instance->encoder.repeat--; + instance->encoder.front = 0; + } + + return ret; +} + +void* subghz_protocol_decoder_marantec_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolDecoderMarantec* instance = malloc(sizeof(SubGhzProtocolDecoderMarantec)); + instance->base.protocol = &subghz_protocol_marantec; + instance->generic.protocol_name = instance->base.protocol->name; + return instance; +} + +void subghz_protocol_decoder_marantec_free(void* context) { + furi_assert(context); + SubGhzProtocolDecoderMarantec* instance = context; + free(instance); +} + +void subghz_protocol_decoder_marantec_reset(void* context) { + furi_assert(context); + SubGhzProtocolDecoderMarantec* instance = context; + manchester_advance( + instance->manchester_saved_state, + ManchesterEventReset, + &instance->manchester_saved_state, + NULL); +} + +void subghz_protocol_decoder_marantec_feed(void* context, bool level, volatile uint32_t duration) { + furi_assert(context); + SubGhzProtocolDecoderMarantec* instance = context; + ManchesterEvent event = ManchesterEventReset; + + switch(instance->decoder.parser_step) { + case MarantecDecoderStepReset: + if((!level) && (DURATION_DIFF(duration, subghz_protocol_marantec_const.te_long * 5) < + subghz_protocol_marantec_const.te_delta * 8)) { + //Found header marantec + instance->decoder.parser_step = MarantecDecoderStepDecoderData; + instance->decoder.decode_data = 1; + instance->decoder.decode_count_bit = 1; + manchester_advance( + instance->manchester_saved_state, + ManchesterEventReset, + &instance->manchester_saved_state, + NULL); + } + break; + case MarantecDecoderStepDecoderData: + if(!level) { + if(DURATION_DIFF(duration, subghz_protocol_marantec_const.te_short) < + subghz_protocol_marantec_const.te_delta) { + event = ManchesterEventShortLow; + } else if( + DURATION_DIFF(duration, subghz_protocol_marantec_const.te_long) < + subghz_protocol_marantec_const.te_delta) { + event = ManchesterEventLongLow; + } else if( + duration >= ((uint32_t)subghz_protocol_marantec_const.te_long * 2 + + subghz_protocol_marantec_const.te_delta)) { + if(instance->decoder.decode_count_bit == + subghz_protocol_marantec_const.min_count_bit_for_found) { + instance->generic.data = instance->decoder.decode_data; + instance->generic.data_count_bit = instance->decoder.decode_count_bit; + + if(instance->base.callback) + instance->base.callback(&instance->base, instance->base.context); + } + instance->decoder.decode_data = 1; + instance->decoder.decode_count_bit = 1; + manchester_advance( + instance->manchester_saved_state, + ManchesterEventReset, + &instance->manchester_saved_state, + NULL); + } else { + instance->decoder.parser_step = MarantecDecoderStepReset; + } + } else { + if(DURATION_DIFF(duration, subghz_protocol_marantec_const.te_short) < + subghz_protocol_marantec_const.te_delta) { + event = ManchesterEventShortHigh; + } else if( + DURATION_DIFF(duration, subghz_protocol_marantec_const.te_long) < + subghz_protocol_marantec_const.te_delta) { + event = ManchesterEventLongHigh; + } else { + instance->decoder.parser_step = MarantecDecoderStepReset; + } + } + if(event != ManchesterEventReset) { + bool data; + bool data_ok = manchester_advance( + instance->manchester_saved_state, event, &instance->manchester_saved_state, &data); + + if(data_ok) { + instance->decoder.decode_data = (instance->decoder.decode_data << 1) | data; + instance->decoder.decode_count_bit++; + } + } + break; + } +} + +uint8_t subghz_protocol_decoder_marantec_get_hash_data(void* context) { + furi_assert(context); + SubGhzProtocolDecoderMarantec* instance = context; + return subghz_protocol_blocks_get_hash_data( + &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); +} + +bool subghz_protocol_decoder_marantec_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzPresetDefinition* preset) { + furi_assert(context); + SubGhzProtocolDecoderMarantec* instance = context; + return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); +} + +bool subghz_protocol_decoder_marantec_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolDecoderMarantec* instance = context; + bool ret = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + break; + } + if(instance->generic.data_count_bit != + subghz_protocol_marantec_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } + ret = true; + } while(false); + return ret; +} + +void subghz_protocol_decoder_marantec_get_string(void* context, string_t output) { + furi_assert(context); + SubGhzProtocolDecoderMarantec* instance = context; + subghz_protocol_marantec_remote_controller(&instance->generic); + + string_cat_printf( + output, + "%s %db\r\n" + "Key:0x%lX%08lX\r\n" + "Sn:0x%07lX \r\n" + "Btn:%lX\r\n", + instance->generic.protocol_name, + instance->generic.data_count_bit, + (uint32_t)(instance->generic.data >> 32), + (uint32_t)(instance->generic.data & 0xFFFFFFFF), + instance->generic.serial, + instance->generic.btn); +} diff --git a/lib/subghz/protocols/marantec.h b/lib/subghz/protocols/marantec.h new file mode 100644 index 000000000..2da3c88a5 --- /dev/null +++ b/lib/subghz/protocols/marantec.h @@ -0,0 +1,107 @@ +#pragma once + +#include "base.h" + +#define SUBGHZ_PROTOCOL_MARANTEC_NAME "Marantec" + +typedef struct SubGhzProtocolDecoderMarantec SubGhzProtocolDecoderMarantec; +typedef struct SubGhzProtocolEncoderMarantec SubGhzProtocolEncoderMarantec; + +extern const SubGhzProtocolDecoder subghz_protocol_marantec_decoder; +extern const SubGhzProtocolEncoder subghz_protocol_marantec_encoder; +extern const SubGhzProtocol subghz_protocol_marantec; + +/** + * Allocate SubGhzProtocolEncoderMarantec. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolEncoderMarantec* pointer to a SubGhzProtocolEncoderMarantec instance + */ +void* subghz_protocol_encoder_marantec_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolEncoderMarantec. + * @param context Pointer to a SubGhzProtocolEncoderMarantec instance + */ +void subghz_protocol_encoder_marantec_free(void* context); + +/** + * Deserialize and generating an upload to send. + * @param context Pointer to a SubGhzProtocolEncoderMarantec instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_encoder_marantec_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Forced transmission stop. + * @param context Pointer to a SubGhzProtocolEncoderMarantec instance + */ +void subghz_protocol_encoder_marantec_stop(void* context); + +/** + * Getting the level and duration of the upload to be loaded into DMA. + * @param context Pointer to a SubGhzProtocolEncoderMarantec instance + * @return LevelDuration + */ +LevelDuration subghz_protocol_encoder_marantec_yield(void* context); + +/** + * Allocate SubGhzProtocolDecoderMarantec. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolDecoderMarantec* pointer to a SubGhzProtocolDecoderMarantec instance + */ +void* subghz_protocol_decoder_marantec_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolDecoderMarantec. + * @param context Pointer to a SubGhzProtocolDecoderMarantec instance + */ +void subghz_protocol_decoder_marantec_free(void* context); + +/** + * Reset decoder SubGhzProtocolDecoderMarantec. + * @param context Pointer to a SubGhzProtocolDecoderMarantec instance + */ +void subghz_protocol_decoder_marantec_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a SubGhzProtocolDecoderMarantec instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void subghz_protocol_decoder_marantec_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a SubGhzProtocolDecoderMarantec instance + * @return hash Hash sum + */ +uint8_t subghz_protocol_decoder_marantec_get_hash_data(void* context); + +/** + * Serialize data SubGhzProtocolDecoderMarantec. + * @param context Pointer to a SubGhzProtocolDecoderMarantec instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param preset The modulation on which the signal was received, SubGhzPresetDefinition + * @return true On success + */ +bool subghz_protocol_decoder_marantec_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzPresetDefinition* preset); + +/** + * Deserialize data SubGhzProtocolDecoderMarantec. + * @param context Pointer to a SubGhzProtocolDecoderMarantec instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_decoder_marantec_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a SubGhzProtocolDecoderMarantec instance + * @param output Resulting text + */ +void subghz_protocol_decoder_marantec_get_string(void* context, string_t output); diff --git a/lib/subghz/protocols/megacode.c b/lib/subghz/protocols/megacode.c index f8d42b29e..bfe1a76b9 100644 --- a/lib/subghz/protocols/megacode.c +++ b/lib/subghz/protocols/megacode.c @@ -184,7 +184,11 @@ bool subghz_protocol_encoder_megacode_deserialize(void* context, FlipperFormat* FURI_LOG_E(TAG, "Deserialize error"); break; } - + if(instance->generic.data_count_bit != + subghz_protocol_megacode_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } //optional parameter parameter flipper_format_read_uint32( flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1); @@ -270,7 +274,7 @@ void subghz_protocol_decoder_megacode_feed(void* context, bool level, uint32_t d if(!level) { //save interval if(duration >= (subghz_protocol_megacode_const.te_short * 10)) { instance->decoder.parser_step = MegaCodeDecoderStepReset; - if(instance->decoder.decode_count_bit >= + if(instance->decoder.decode_count_bit == subghz_protocol_megacode_const.min_count_bit_for_found) { instance->generic.data = instance->decoder.decode_data; instance->generic.data_count_bit = instance->decoder.decode_count_bit; @@ -380,17 +384,28 @@ uint8_t subghz_protocol_decoder_megacode_get_hash_data(void* context) { bool subghz_protocol_decoder_megacode_serialize( void* context, FlipperFormat* flipper_format, - uint32_t frequency, - FuriHalSubGhzPreset preset) { + SubGhzPresetDefinition* preset) { furi_assert(context); SubGhzProtocolDecoderMegaCode* instance = context; - return subghz_block_generic_serialize(&instance->generic, flipper_format, frequency, preset); + return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); } bool subghz_protocol_decoder_megacode_deserialize(void* context, FlipperFormat* flipper_format) { furi_assert(context); SubGhzProtocolDecoderMegaCode* instance = context; - return subghz_block_generic_deserialize(&instance->generic, flipper_format); + bool ret = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + break; + } + if(instance->generic.data_count_bit != + subghz_protocol_megacode_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } + ret = true; + } while(false); + return ret; } void subghz_protocol_decoder_megacode_get_string(void* context, string_t output) { diff --git a/lib/subghz/protocols/megacode.h b/lib/subghz/protocols/megacode.h index 1a3cfd5d3..c8010665b 100644 --- a/lib/subghz/protocols/megacode.h +++ b/lib/subghz/protocols/megacode.h @@ -83,15 +83,13 @@ uint8_t subghz_protocol_decoder_megacode_get_hash_data(void* context); * Serialize data SubGhzProtocolDecoderMegaCode. * @param context Pointer to a SubGhzProtocolDecoderMegaCode instance * @param flipper_format Pointer to a FlipperFormat instance - * @param frequency The frequency at which the signal was received, Hz - * @param preset The modulation on which the signal was received, FuriHalSubGhzPreset + * @param preset The modulation on which the signal was received, SubGhzPresetDefinition * @return true On success */ bool subghz_protocol_decoder_megacode_serialize( void* context, FlipperFormat* flipper_format, - uint32_t frequency, - FuriHalSubGhzPreset preset); + SubGhzPresetDefinition* preset); /** * Deserialize data SubGhzProtocolDecoderMegaCode. diff --git a/lib/subghz/protocols/nero_radio.c b/lib/subghz/protocols/nero_radio.c index 6797843fd..b6b1587ff 100644 --- a/lib/subghz/protocols/nero_radio.c +++ b/lib/subghz/protocols/nero_radio.c @@ -163,7 +163,11 @@ bool subghz_protocol_encoder_nero_radio_deserialize(void* context, FlipperFormat FURI_LOG_E(TAG, "Deserialize error"); break; } - + if(instance->generic.data_count_bit != + subghz_protocol_nero_radio_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } //optional parameter parameter flipper_format_read_uint32( flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1); @@ -294,7 +298,7 @@ void subghz_protocol_decoder_nero_radio_feed(void* context, bool level, uint32_t subghz_protocol_blocks_add_bit(&instance->decoder, 1); } instance->decoder.parser_step = NeroRadioDecoderStepReset; - if(instance->decoder.decode_count_bit >= + if(instance->decoder.decode_count_bit == subghz_protocol_nero_radio_const.min_count_bit_for_found) { instance->generic.data = instance->decoder.decode_data; instance->generic.data_count_bit = instance->decoder.decode_count_bit; @@ -342,17 +346,28 @@ uint8_t subghz_protocol_decoder_nero_radio_get_hash_data(void* context) { bool subghz_protocol_decoder_nero_radio_serialize( void* context, FlipperFormat* flipper_format, - uint32_t frequency, - FuriHalSubGhzPreset preset) { + SubGhzPresetDefinition* preset) { furi_assert(context); SubGhzProtocolDecoderNeroRadio* instance = context; - return subghz_block_generic_serialize(&instance->generic, flipper_format, frequency, preset); + return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); } bool subghz_protocol_decoder_nero_radio_deserialize(void* context, FlipperFormat* flipper_format) { furi_assert(context); SubGhzProtocolDecoderNeroRadio* instance = context; - return subghz_block_generic_deserialize(&instance->generic, flipper_format); + bool ret = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + break; + } + if(instance->generic.data_count_bit != + subghz_protocol_nero_radio_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } + ret = true; + } while(false); + return ret; } void subghz_protocol_decoder_nero_radio_get_string(void* context, string_t output) { diff --git a/lib/subghz/protocols/nero_radio.h b/lib/subghz/protocols/nero_radio.h index 46c80738d..f04dc2b3c 100644 --- a/lib/subghz/protocols/nero_radio.h +++ b/lib/subghz/protocols/nero_radio.h @@ -83,15 +83,13 @@ uint8_t subghz_protocol_decoder_nero_radio_get_hash_data(void* context); * Serialize data SubGhzProtocolDecoderNeroRadio. * @param context Pointer to a SubGhzProtocolDecoderNeroRadio instance * @param flipper_format Pointer to a FlipperFormat instance - * @param frequency The frequency at which the signal was received, Hz - * @param preset The modulation on which the signal was received, FuriHalSubGhzPreset + * @param preset The modulation on which the signal was received, SubGhzPresetDefinition * @return true On success */ bool subghz_protocol_decoder_nero_radio_serialize( void* context, FlipperFormat* flipper_format, - uint32_t frequency, - FuriHalSubGhzPreset preset); + SubGhzPresetDefinition* preset); /** * Deserialize data SubGhzProtocolDecoderNeroRadio. diff --git a/lib/subghz/protocols/nero_sketch.c b/lib/subghz/protocols/nero_sketch.c index 1ba0cf860..0b87ec11b 100644 --- a/lib/subghz/protocols/nero_sketch.c +++ b/lib/subghz/protocols/nero_sketch.c @@ -157,7 +157,11 @@ bool subghz_protocol_encoder_nero_sketch_deserialize(void* context, FlipperForma FURI_LOG_E(TAG, "Deserialize error"); break; } - + if(instance->generic.data_count_bit != + subghz_protocol_nero_sketch_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } //optional parameter parameter flipper_format_read_uint32( flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1); @@ -271,7 +275,7 @@ void subghz_protocol_decoder_nero_sketch_feed(void* context, bool level, uint32_ subghz_protocol_nero_sketch_const.te_delta * 2)) { //Found stop bit instance->decoder.parser_step = NeroSketchDecoderStepReset; - if(instance->decoder.decode_count_bit >= + if(instance->decoder.decode_count_bit == subghz_protocol_nero_sketch_const.min_count_bit_for_found) { instance->generic.data = instance->decoder.decode_data; instance->generic.data_count_bit = instance->decoder.decode_count_bit; @@ -327,17 +331,28 @@ uint8_t subghz_protocol_decoder_nero_sketch_get_hash_data(void* context) { bool subghz_protocol_decoder_nero_sketch_serialize( void* context, FlipperFormat* flipper_format, - uint32_t frequency, - FuriHalSubGhzPreset preset) { + SubGhzPresetDefinition* preset) { furi_assert(context); SubGhzProtocolDecoderNeroSketch* instance = context; - return subghz_block_generic_serialize(&instance->generic, flipper_format, frequency, preset); + return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); } bool subghz_protocol_decoder_nero_sketch_deserialize(void* context, FlipperFormat* flipper_format) { furi_assert(context); SubGhzProtocolDecoderNeroSketch* instance = context; - return subghz_block_generic_deserialize(&instance->generic, flipper_format); + bool ret = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + break; + } + if(instance->generic.data_count_bit != + subghz_protocol_nero_sketch_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } + ret = true; + } while(false); + return ret; } void subghz_protocol_decoder_nero_sketch_get_string(void* context, string_t output) { diff --git a/lib/subghz/protocols/nero_sketch.h b/lib/subghz/protocols/nero_sketch.h index e2b12f649..ab592b48e 100644 --- a/lib/subghz/protocols/nero_sketch.h +++ b/lib/subghz/protocols/nero_sketch.h @@ -83,15 +83,13 @@ uint8_t subghz_protocol_decoder_nero_sketch_get_hash_data(void* context); * Serialize data SubGhzProtocolDecoderNeroSketch. * @param context Pointer to a SubGhzProtocolDecoderNeroSketch instance * @param flipper_format Pointer to a FlipperFormat instance - * @param frequency The frequency at which the signal was received, Hz - * @param preset The modulation on which the signal was received, FuriHalSubGhzPreset + * @param preset The modulation on which the signal was received, SubGhzPresetDefinition * @return true On success */ bool subghz_protocol_decoder_nero_sketch_serialize( void* context, FlipperFormat* flipper_format, - uint32_t frequency, - FuriHalSubGhzPreset preset); + SubGhzPresetDefinition* preset); /** * Deserialize data SubGhzProtocolDecoderNeroSketch. diff --git a/lib/subghz/protocols/nice_flo.c b/lib/subghz/protocols/nice_flo.c index d2844dcd6..236b42223 100644 --- a/lib/subghz/protocols/nice_flo.c +++ b/lib/subghz/protocols/nice_flo.c @@ -138,7 +138,13 @@ bool subghz_protocol_encoder_nice_flo_deserialize(void* context, FlipperFormat* FURI_LOG_E(TAG, "Deserialize error"); break; } - + if((instance->generic.data_count_bit != + subghz_protocol_nice_flo_const.min_count_bit_for_found) && + (instance->generic.data_count_bit != + 2 * subghz_protocol_nice_flo_const.min_count_bit_for_found)) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } //optional parameter parameter flipper_format_read_uint32( flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1); @@ -277,17 +283,30 @@ uint8_t subghz_protocol_decoder_nice_flo_get_hash_data(void* context) { bool subghz_protocol_decoder_nice_flo_serialize( void* context, FlipperFormat* flipper_format, - uint32_t frequency, - FuriHalSubGhzPreset preset) { + SubGhzPresetDefinition* preset) { furi_assert(context); SubGhzProtocolDecoderNiceFlo* instance = context; - return subghz_block_generic_serialize(&instance->generic, flipper_format, frequency, preset); + return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); } bool subghz_protocol_decoder_nice_flo_deserialize(void* context, FlipperFormat* flipper_format) { furi_assert(context); SubGhzProtocolDecoderNiceFlo* instance = context; - return subghz_block_generic_deserialize(&instance->generic, flipper_format); + bool ret = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + break; + } + if((instance->generic.data_count_bit != + subghz_protocol_nice_flo_const.min_count_bit_for_found) && + (instance->generic.data_count_bit != + 2 * subghz_protocol_nice_flo_const.min_count_bit_for_found)) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } + ret = true; + } while(false); + return ret; } void subghz_protocol_decoder_nice_flo_get_string(void* context, string_t output) { diff --git a/lib/subghz/protocols/nice_flo.h b/lib/subghz/protocols/nice_flo.h index 430b111df..0873e81b9 100644 --- a/lib/subghz/protocols/nice_flo.h +++ b/lib/subghz/protocols/nice_flo.h @@ -83,15 +83,13 @@ uint8_t subghz_protocol_decoder_nice_flo_get_hash_data(void* context); * Serialize data SubGhzProtocolDecoderNiceFlo. * @param context Pointer to a SubGhzProtocolDecoderNiceFlo instance * @param flipper_format Pointer to a FlipperFormat instance - * @param frequency The frequency at which the signal was received, Hz - * @param preset The modulation on which the signal was received, FuriHalSubGhzPreset + * @param preset The modulation on which the signal was received, SubGhzPresetDefinition * @return true On success */ bool subghz_protocol_decoder_nice_flo_serialize( void* context, FlipperFormat* flipper_format, - uint32_t frequency, - FuriHalSubGhzPreset preset); + SubGhzPresetDefinition* preset); /** * Deserialize data SubGhzProtocolDecoderNiceFlo. diff --git a/lib/subghz/protocols/nice_flor_s.c b/lib/subghz/protocols/nice_flor_s.c index 1fe5876da..1d370e85a 100644 --- a/lib/subghz/protocols/nice_flor_s.c +++ b/lib/subghz/protocols/nice_flor_s.c @@ -237,7 +237,7 @@ void subghz_protocol_decoder_nice_flor_s_feed(void* context, bool level, uint32_ subghz_protocol_nice_flor_s_const.te_delta) { //Found STOP bit instance->decoder.parser_step = NiceFlorSDecoderStepReset; - if(instance->decoder.decode_count_bit >= + if(instance->decoder.decode_count_bit == subghz_protocol_nice_flor_s_const.min_count_bit_for_found) { instance->generic.data = instance->decoder.decode_data; instance->generic.data_count_bit = instance->decoder.decode_count_bit; @@ -330,17 +330,28 @@ uint8_t subghz_protocol_decoder_nice_flor_s_get_hash_data(void* context) { bool subghz_protocol_decoder_nice_flor_s_serialize( void* context, FlipperFormat* flipper_format, - uint32_t frequency, - FuriHalSubGhzPreset preset) { + SubGhzPresetDefinition* preset) { furi_assert(context); SubGhzProtocolDecoderNiceFlorS* instance = context; - return subghz_block_generic_serialize(&instance->generic, flipper_format, frequency, preset); + return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); } bool subghz_protocol_decoder_nice_flor_s_deserialize(void* context, FlipperFormat* flipper_format) { furi_assert(context); SubGhzProtocolDecoderNiceFlorS* instance = context; - return subghz_block_generic_deserialize(&instance->generic, flipper_format); + bool ret = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + break; + } + if(instance->generic.data_count_bit != + subghz_protocol_nice_flor_s_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } + ret = true; + } while(false); + return ret; } void subghz_protocol_decoder_nice_flor_s_get_string(void* context, string_t output) { diff --git a/lib/subghz/protocols/nice_flor_s.h b/lib/subghz/protocols/nice_flor_s.h index d11264bc6..7d98876f6 100644 --- a/lib/subghz/protocols/nice_flor_s.h +++ b/lib/subghz/protocols/nice_flor_s.h @@ -49,15 +49,13 @@ uint8_t subghz_protocol_decoder_nice_flor_s_get_hash_data(void* context); * Serialize data SubGhzProtocolDecoderNiceFlorS. * @param context Pointer to a SubGhzProtocolDecoderNiceFlorS instance * @param flipper_format Pointer to a FlipperFormat instance - * @param frequency The frequency at which the signal was received, Hz - * @param preset The modulation on which the signal was received, FuriHalSubGhzPreset + * @param preset The modulation on which the signal was received, SubGhzPresetDefinition * @return true On success */ bool subghz_protocol_decoder_nice_flor_s_serialize( void* context, FlipperFormat* flipper_format, - uint32_t frequency, - FuriHalSubGhzPreset preset); + SubGhzPresetDefinition* preset); /** * Deserialize data SubGhzProtocolDecoderNiceFlorS. diff --git a/lib/subghz/protocols/phoenix_v2.c b/lib/subghz/protocols/phoenix_v2.c new file mode 100644 index 000000000..e71e28349 --- /dev/null +++ b/lib/subghz/protocols/phoenix_v2.c @@ -0,0 +1,339 @@ +#include "phoenix_v2.h" + +#include "../blocks/const.h" +#include "../blocks/decoder.h" +#include "../blocks/encoder.h" +#include "../blocks/generic.h" +#include "../blocks/math.h" + +#define TAG "SubGhzProtocolPhoenix_V2" + +//transmission only static mode + +static const SubGhzBlockConst subghz_protocol_phoenix_v2_const = { + .te_short = 427, + .te_long = 853, + .te_delta = 100, + .min_count_bit_for_found = 52, +}; + +struct SubGhzProtocolDecoderPhoenix_V2 { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + SubGhzBlockGeneric generic; +}; + +struct SubGhzProtocolEncoderPhoenix_V2 { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolBlockEncoder encoder; + SubGhzBlockGeneric generic; +}; + +typedef enum { + Phoenix_V2DecoderStepReset = 0, + Phoenix_V2DecoderStepFoundStartBit, + Phoenix_V2DecoderStepSaveDuration, + Phoenix_V2DecoderStepCheckDuration, +} Phoenix_V2DecoderStep; + +const SubGhzProtocolDecoder subghz_protocol_phoenix_v2_decoder = { + .alloc = subghz_protocol_decoder_phoenix_v2_alloc, + .free = subghz_protocol_decoder_phoenix_v2_free, + + .feed = subghz_protocol_decoder_phoenix_v2_feed, + .reset = subghz_protocol_decoder_phoenix_v2_reset, + + .get_hash_data = subghz_protocol_decoder_phoenix_v2_get_hash_data, + .serialize = subghz_protocol_decoder_phoenix_v2_serialize, + .deserialize = subghz_protocol_decoder_phoenix_v2_deserialize, + .get_string = subghz_protocol_decoder_phoenix_v2_get_string, +}; + +const SubGhzProtocolEncoder subghz_protocol_phoenix_v2_encoder = { + .alloc = subghz_protocol_encoder_phoenix_v2_alloc, + .free = subghz_protocol_encoder_phoenix_v2_free, + + .deserialize = subghz_protocol_encoder_phoenix_v2_deserialize, + .stop = subghz_protocol_encoder_phoenix_v2_stop, + .yield = subghz_protocol_encoder_phoenix_v2_yield, +}; + +const SubGhzProtocol subghz_protocol_phoenix_v2 = { + .name = SUBGHZ_PROTOCOL_PHOENIX_V2_NAME, + .type = SubGhzProtocolTypeStatic, + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable | + SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send, + + .decoder = &subghz_protocol_phoenix_v2_decoder, + .encoder = &subghz_protocol_phoenix_v2_encoder, +}; + +void* subghz_protocol_encoder_phoenix_v2_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolEncoderPhoenix_V2* instance = malloc(sizeof(SubGhzProtocolEncoderPhoenix_V2)); + + instance->base.protocol = &subghz_protocol_phoenix_v2; + instance->generic.protocol_name = instance->base.protocol->name; + + instance->encoder.repeat = 10; + instance->encoder.size_upload = 128; + instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); + instance->encoder.is_runing = false; + return instance; +} + +void subghz_protocol_encoder_phoenix_v2_free(void* context) { + furi_assert(context); + SubGhzProtocolEncoderPhoenix_V2* instance = context; + free(instance->encoder.upload); + free(instance); +} + +/** + * Generating an upload from data. + * @param instance Pointer to a SubGhzProtocolEncoderPhoenix_V2 instance + * @return true On success + */ +static bool + subghz_protocol_encoder_phoenix_v2_get_upload(SubGhzProtocolEncoderPhoenix_V2* instance) { + furi_assert(instance); + size_t index = 0; + size_t size_upload = (instance->generic.data_count_bit * 2) + 2; + if(size_upload > instance->encoder.size_upload) { + FURI_LOG_E(TAG, "Size upload exceeds allocated encoder buffer."); + return false; + } else { + instance->encoder.size_upload = size_upload; + } + //Send header + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_phoenix_v2_const.te_short * 60); + //Send start bit + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_phoenix_v2_const.te_short * 6); + //Send key data + for(uint8_t i = instance->generic.data_count_bit; i > 0; i--) { + if(!bit_read(instance->generic.data, i - 1)) { + //send bit 1 + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_phoenix_v2_const.te_long); + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_phoenix_v2_const.te_short); + } else { + //send bit 0 + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_phoenix_v2_const.te_short); + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_phoenix_v2_const.te_long); + } + } + return true; +} + +bool subghz_protocol_encoder_phoenix_v2_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolEncoderPhoenix_V2* instance = context; + bool res = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + FURI_LOG_E(TAG, "Deserialize error"); + break; + } + if(instance->generic.data_count_bit != + subghz_protocol_phoenix_v2_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } + //optional parameter parameter + flipper_format_read_uint32( + flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1); + + subghz_protocol_encoder_phoenix_v2_get_upload(instance); + instance->encoder.is_runing = true; + + res = true; + } while(false); + + return res; +} + +void subghz_protocol_encoder_phoenix_v2_stop(void* context) { + SubGhzProtocolEncoderPhoenix_V2* instance = context; + instance->encoder.is_runing = false; +} + +LevelDuration subghz_protocol_encoder_phoenix_v2_yield(void* context) { + SubGhzProtocolEncoderPhoenix_V2* instance = context; + + if(instance->encoder.repeat == 0 || !instance->encoder.is_runing) { + instance->encoder.is_runing = false; + return level_duration_reset(); + } + + LevelDuration ret = instance->encoder.upload[instance->encoder.front]; + + if(++instance->encoder.front == instance->encoder.size_upload) { + instance->encoder.repeat--; + instance->encoder.front = 0; + } + + return ret; +} + +void* subghz_protocol_decoder_phoenix_v2_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolDecoderPhoenix_V2* instance = malloc(sizeof(SubGhzProtocolDecoderPhoenix_V2)); + instance->base.protocol = &subghz_protocol_phoenix_v2; + instance->generic.protocol_name = instance->base.protocol->name; + return instance; +} + +void subghz_protocol_decoder_phoenix_v2_free(void* context) { + furi_assert(context); + SubGhzProtocolDecoderPhoenix_V2* instance = context; + free(instance); +} + +void subghz_protocol_decoder_phoenix_v2_reset(void* context) { + furi_assert(context); + SubGhzProtocolDecoderPhoenix_V2* instance = context; + instance->decoder.parser_step = Phoenix_V2DecoderStepReset; +} + +void subghz_protocol_decoder_phoenix_v2_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + SubGhzProtocolDecoderPhoenix_V2* instance = context; + + switch(instance->decoder.parser_step) { + case Phoenix_V2DecoderStepReset: + if((!level) && (DURATION_DIFF(duration, subghz_protocol_phoenix_v2_const.te_short * 60) < + subghz_protocol_phoenix_v2_const.te_delta * 30)) { + //Found Preambula + instance->decoder.parser_step = Phoenix_V2DecoderStepFoundStartBit; + } + break; + case Phoenix_V2DecoderStepFoundStartBit: + if(level && ((DURATION_DIFF(duration, (subghz_protocol_phoenix_v2_const.te_short * 6)) < + subghz_protocol_phoenix_v2_const.te_delta * 4))) { + //Found start bit + instance->decoder.parser_step = Phoenix_V2DecoderStepSaveDuration; + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + } else { + instance->decoder.parser_step = Phoenix_V2DecoderStepReset; + } + break; + case Phoenix_V2DecoderStepSaveDuration: + if(!level) { + if(duration >= ((uint32_t)subghz_protocol_phoenix_v2_const.te_short * 10 + + subghz_protocol_phoenix_v2_const.te_delta)) { + instance->decoder.parser_step = Phoenix_V2DecoderStepFoundStartBit; + if(instance->decoder.decode_count_bit == + subghz_protocol_phoenix_v2_const.min_count_bit_for_found) { + instance->generic.data = instance->decoder.decode_data; + instance->generic.data_count_bit = instance->decoder.decode_count_bit; + + if(instance->base.callback) + instance->base.callback(&instance->base, instance->base.context); + } + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + break; + } else { + instance->decoder.te_last = duration; + instance->decoder.parser_step = Phoenix_V2DecoderStepCheckDuration; + } + } + break; + case Phoenix_V2DecoderStepCheckDuration: + if(level) { + if((DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_phoenix_v2_const.te_short) < + subghz_protocol_phoenix_v2_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_phoenix_v2_const.te_long) < + subghz_protocol_phoenix_v2_const.te_delta * 3)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + instance->decoder.parser_step = Phoenix_V2DecoderStepSaveDuration; + } else if( + (DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_phoenix_v2_const.te_long) < + subghz_protocol_phoenix_v2_const.te_delta * 3) && + (DURATION_DIFF(duration, subghz_protocol_phoenix_v2_const.te_short) < + subghz_protocol_phoenix_v2_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + instance->decoder.parser_step = Phoenix_V2DecoderStepSaveDuration; + } else { + instance->decoder.parser_step = Phoenix_V2DecoderStepReset; + } + } else { + instance->decoder.parser_step = Phoenix_V2DecoderStepReset; + } + break; + } +} + +/** + * Analysis of received data + * @param instance Pointer to a SubGhzBlockGeneric* instance + */ +static void subghz_protocol_phoenix_v2_check_remote_controller(SubGhzBlockGeneric* instance) { + uint64_t data_rev = + subghz_protocol_blocks_reverse_key(instance->data, instance->data_count_bit + 4); + instance->serial = data_rev & 0xFFFFFFFF; + instance->cnt = (data_rev >> 40) & 0xFFFF; + instance->btn = (data_rev >> 32) & 0xF; +} + +uint8_t subghz_protocol_decoder_phoenix_v2_get_hash_data(void* context) { + furi_assert(context); + SubGhzProtocolDecoderPhoenix_V2* instance = context; + return subghz_protocol_blocks_get_hash_data( + &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); +} + +bool subghz_protocol_decoder_phoenix_v2_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzPresetDefinition* preset) { + furi_assert(context); + SubGhzProtocolDecoderPhoenix_V2* instance = context; + return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); +} + +bool subghz_protocol_decoder_phoenix_v2_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolDecoderPhoenix_V2* instance = context; + bool ret = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + break; + } + if(instance->generic.data_count_bit != + subghz_protocol_phoenix_v2_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } + ret = true; + } while(false); + return ret; +} + +void subghz_protocol_decoder_phoenix_v2_get_string(void* context, string_t output) { + furi_assert(context); + SubGhzProtocolDecoderPhoenix_V2* instance = context; + subghz_protocol_phoenix_v2_check_remote_controller(&instance->generic); + string_cat_printf( + output, + "%s %dbit\r\n" + "Key:%02lX%08lX\r\n" + "Sn:0x%07lX \r\n" + "Btn:%lX\r\n", + instance->generic.protocol_name, + instance->generic.data_count_bit, + (uint32_t)(instance->generic.data >> 32) & 0xFFFFFFFF, + (uint32_t)(instance->generic.data & 0xFFFFFFFF), + instance->generic.serial, + instance->generic.btn); +} diff --git a/lib/subghz/protocols/phoenix_v2.h b/lib/subghz/protocols/phoenix_v2.h new file mode 100644 index 000000000..38cadc1a2 --- /dev/null +++ b/lib/subghz/protocols/phoenix_v2.h @@ -0,0 +1,107 @@ +#pragma once + +#include "base.h" + +#define SUBGHZ_PROTOCOL_PHOENIX_V2_NAME "Phoenix_V2" + +typedef struct SubGhzProtocolDecoderPhoenix_V2 SubGhzProtocolDecoderPhoenix_V2; +typedef struct SubGhzProtocolEncoderPhoenix_V2 SubGhzProtocolEncoderPhoenix_V2; + +extern const SubGhzProtocolDecoder subghz_protocol_phoenix_v2_decoder; +extern const SubGhzProtocolEncoder subghz_protocol_phoenix_v2_encoder; +extern const SubGhzProtocol subghz_protocol_phoenix_v2; + +/** + * Allocate SubGhzProtocolEncoderPhoenix_V2. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolEncoderPhoenix_V2* pointer to a SubGhzProtocolEncoderPhoenix_V2 instance + */ +void* subghz_protocol_encoder_phoenix_v2_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolEncoderPhoenix_V2. + * @param context Pointer to a SubGhzProtocolEncoderPhoenix_V2 instance + */ +void subghz_protocol_encoder_phoenix_v2_free(void* context); + +/** + * Deserialize and generating an upload to send. + * @param context Pointer to a SubGhzProtocolEncoderPhoenix_V2 instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_encoder_phoenix_v2_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Forced transmission stop. + * @param context Pointer to a SubGhzProtocolEncoderPhoenix_V2 instance + */ +void subghz_protocol_encoder_phoenix_v2_stop(void* context); + +/** + * Getting the level and duration of the upload to be loaded into DMA. + * @param context Pointer to a SubGhzProtocolEncoderPhoenix_V2 instance + * @return LevelDuration + */ +LevelDuration subghz_protocol_encoder_phoenix_v2_yield(void* context); + +/** + * Allocate SubGhzProtocolDecoderPhoenix_V2. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolDecoderPhoenix_V2* pointer to a SubGhzProtocolDecoderPhoenix_V2 instance + */ +void* subghz_protocol_decoder_phoenix_v2_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolDecoderPhoenix_V2. + * @param context Pointer to a SubGhzProtocolDecoderPhoenix_V2 instance + */ +void subghz_protocol_decoder_phoenix_v2_free(void* context); + +/** + * Reset decoder SubGhzProtocolDecoderPhoenix_V2. + * @param context Pointer to a SubGhzProtocolDecoderPhoenix_V2 instance + */ +void subghz_protocol_decoder_phoenix_v2_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a SubGhzProtocolDecoderPhoenix_V2 instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void subghz_protocol_decoder_phoenix_v2_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a SubGhzProtocolDecoderPhoenix_V2 instance + * @return hash Hash sum + */ +uint8_t subghz_protocol_decoder_phoenix_v2_get_hash_data(void* context); + +/** + * Serialize data SubGhzProtocolDecoderPhoenix_V2. + * @param context Pointer to a SubGhzProtocolDecoderPhoenix_V2 instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param preset The modulation on which the signal was received, SubGhzPresetDefinition + * @return true On success + */ +bool subghz_protocol_decoder_phoenix_v2_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzPresetDefinition* preset); + +/** + * Deserialize data SubGhzProtocolDecoderPhoenix_V2. + * @param context Pointer to a SubGhzProtocolDecoderPhoenix_V2 instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_decoder_phoenix_v2_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a SubGhzProtocolDecoderPhoenix_V2 instance + * @param output Resulting text + */ +void subghz_protocol_decoder_phoenix_v2_get_string(void* context, string_t output); diff --git a/lib/subghz/protocols/power_smart.c b/lib/subghz/protocols/power_smart.c index 0dc81f2a3..53e9f3380 100644 --- a/lib/subghz/protocols/power_smart.c +++ b/lib/subghz/protocols/power_smart.c @@ -201,7 +201,11 @@ bool subghz_protocol_encoder_power_smart_deserialize(void* context, FlipperForma FURI_LOG_E(TAG, "Deserialize error"); break; } - + if(instance->generic.data_count_bit != + subghz_protocol_power_smart_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } //optional parameter parameter flipper_format_read_uint32( flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1); @@ -345,17 +349,28 @@ uint8_t subghz_protocol_decoder_power_smart_get_hash_data(void* context) { bool subghz_protocol_decoder_power_smart_serialize( void* context, FlipperFormat* flipper_format, - uint32_t frequency, - FuriHalSubGhzPreset preset) { + SubGhzPresetDefinition* preset) { furi_assert(context); SubGhzProtocolDecoderPowerSmart* instance = context; - return subghz_block_generic_serialize(&instance->generic, flipper_format, frequency, preset); + return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); } bool subghz_protocol_decoder_power_smart_deserialize(void* context, FlipperFormat* flipper_format) { furi_assert(context); SubGhzProtocolDecoderPowerSmart* instance = context; - return subghz_block_generic_deserialize(&instance->generic, flipper_format); + bool ret = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + break; + } + if(instance->generic.data_count_bit != + subghz_protocol_power_smart_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } + ret = true; + } while(false); + return ret; } void subghz_protocol_decoder_power_smart_get_string(void* context, string_t output) { diff --git a/lib/subghz/protocols/power_smart.h b/lib/subghz/protocols/power_smart.h index 346fe5370..f6e9571b1 100644 --- a/lib/subghz/protocols/power_smart.h +++ b/lib/subghz/protocols/power_smart.h @@ -83,15 +83,13 @@ uint8_t subghz_protocol_decoder_power_smart_get_hash_data(void* context); * Serialize data SubGhzProtocolDecoderPowerSmart. * @param context Pointer to a SubGhzProtocolDecoderPowerSmart instance * @param flipper_format Pointer to a FlipperFormat instance - * @param frequency The frequency at which the signal was received, Hz - * @param preset The modulation on which the signal was received, FuriHalSubGhzPreset + * @param preset The modulation on which the signal was received, SubGhzPresetDefinition * @return true On success */ bool subghz_protocol_decoder_power_smart_serialize( void* context, FlipperFormat* flipper_format, - uint32_t frequency, - FuriHalSubGhzPreset preset); + SubGhzPresetDefinition* preset); /** * Deserialize data SubGhzProtocolDecoderPowerSmart. diff --git a/lib/subghz/protocols/princeton.c b/lib/subghz/protocols/princeton.c index 046484c75..3fdeaae94 100644 --- a/lib/subghz/protocols/princeton.c +++ b/lib/subghz/protocols/princeton.c @@ -158,6 +158,11 @@ bool subghz_protocol_encoder_princeton_deserialize(void* context, FlipperFormat* FURI_LOG_E(TAG, "Missing TE"); break; } + if(instance->generic.data_count_bit != + subghz_protocol_princeton_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } //optional parameter parameter flipper_format_read_uint32( flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1); @@ -251,8 +256,6 @@ void subghz_protocol_decoder_princeton_feed(void* context, bool level, uint32_t instance->generic.data = instance->decoder.decode_data; instance->generic.data_count_bit = instance->decoder.decode_count_bit; - instance->generic.serial = instance->decoder.decode_data >> 4; - instance->generic.btn = (uint8_t)instance->decoder.decode_data & 0x00000F; if(instance->base.callback) instance->base.callback(&instance->base, instance->base.context); @@ -290,6 +293,15 @@ void subghz_protocol_decoder_princeton_feed(void* context, bool level, uint32_t } } +/** + * Analysis of received data + * @param instance Pointer to a SubGhzBlockGeneric* instance + */ +static void subghz_protocol_princeton_check_remote_controller(SubGhzBlockGeneric* instance) { + instance->serial = instance->data >> 4; + instance->btn = instance->data & 0xF; +} + uint8_t subghz_protocol_decoder_princeton_get_hash_data(void* context) { furi_assert(context); SubGhzProtocolDecoderPrinceton* instance = context; @@ -300,12 +312,10 @@ uint8_t subghz_protocol_decoder_princeton_get_hash_data(void* context) { bool subghz_protocol_decoder_princeton_serialize( void* context, FlipperFormat* flipper_format, - uint32_t frequency, - FuriHalSubGhzPreset preset) { + SubGhzPresetDefinition* preset) { furi_assert(context); SubGhzProtocolDecoderPrinceton* instance = context; - bool res = - subghz_block_generic_serialize(&instance->generic, flipper_format, frequency, preset); + bool res = subghz_block_generic_serialize(&instance->generic, flipper_format, preset); if(res && !flipper_format_write_uint32(flipper_format, "TE", &instance->te, 1)) { FURI_LOG_E(TAG, "Unable to add TE"); res = false; @@ -322,6 +332,11 @@ bool subghz_protocol_decoder_princeton_deserialize(void* context, FlipperFormat* FURI_LOG_E(TAG, "Deserialize error"); break; } + if(instance->generic.data_count_bit != + subghz_protocol_princeton_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } if(!flipper_format_rewind(flipper_format)) { FURI_LOG_E(TAG, "Rewind error"); break; @@ -339,25 +354,21 @@ bool subghz_protocol_decoder_princeton_deserialize(void* context, FlipperFormat* void subghz_protocol_decoder_princeton_get_string(void* context, string_t output) { furi_assert(context); SubGhzProtocolDecoderPrinceton* instance = context; - - uint32_t code_found_lo = instance->generic.data & 0x00000000ffffffff; - - uint64_t code_found_reverse = subghz_protocol_blocks_reverse_key( + subghz_protocol_princeton_check_remote_controller(&instance->generic); + uint32_t data_rev = subghz_protocol_blocks_reverse_key( instance->generic.data, instance->generic.data_count_bit); - uint32_t code_found_reverse_lo = code_found_reverse & 0x00000000ffffffff; - string_cat_printf( output, "%s %dbit\r\n" "Key:0x%08lX\r\n" "Yek:0x%08lX\r\n" - "Sn:0x%05lX BTN:%02X\r\n" + "Sn:0x%05lX Btn:%01X\r\n" "Te:%dus\r\n", instance->generic.protocol_name, instance->generic.data_count_bit, - code_found_lo, - code_found_reverse_lo, + (uint32_t)(instance->generic.data & 0xFFFFFF), + data_rev, instance->generic.serial, instance->generic.btn, instance->te); diff --git a/lib/subghz/protocols/princeton.h b/lib/subghz/protocols/princeton.h index 129819c6f..9c296c4a1 100644 --- a/lib/subghz/protocols/princeton.h +++ b/lib/subghz/protocols/princeton.h @@ -83,15 +83,13 @@ uint8_t subghz_protocol_decoder_princeton_get_hash_data(void* context); * Serialize data SubGhzProtocolDecoderPrinceton. * @param context Pointer to a SubGhzProtocolDecoderPrinceton instance * @param flipper_format Pointer to a FlipperFormat instance - * @param frequency The frequency at which the signal was received, Hz - * @param preset The modulation on which the signal was received, FuriHalSubGhzPreset + * @param preset The modulation on which the signal was received, SubGhzPresetDefinition * @return true On success */ bool subghz_protocol_decoder_princeton_serialize( void* context, FlipperFormat* flipper_format, - uint32_t frequency, - FuriHalSubGhzPreset preset); + SubGhzPresetDefinition* preset); /** * Deserialize data SubGhzProtocolDecoderPrinceton. diff --git a/lib/subghz/protocols/raw.c b/lib/subghz/protocols/raw.c index ebc84c110..9ab649a7b 100644 --- a/lib/subghz/protocols/raw.c +++ b/lib/subghz/protocols/raw.c @@ -57,6 +57,7 @@ const SubGhzProtocolDecoder subghz_protocol_raw_decoder = { .get_hash_data = NULL, .serialize = NULL, + .deserialize = subghz_protocol_decoder_raw_deserialize, .get_string = subghz_protocol_decoder_raw_get_string, }; @@ -83,11 +84,10 @@ const SubGhzProtocol subghz_protocol_raw = { bool subghz_protocol_raw_save_to_file_init( SubGhzProtocolDecoderRAW* instance, const char* dev_name, - uint32_t frequency, - FuriHalSubGhzPreset preset) { + SubGhzPresetDefinition* preset) { furi_assert(instance); - instance->storage = furi_record_open("storage"); + instance->storage = furi_record_open(RECORD_STORAGE); instance->flipper_file = flipper_format_file_alloc(instance->storage); string_t temp_str; @@ -124,19 +124,30 @@ bool subghz_protocol_raw_save_to_file_init( break; } - if(!flipper_format_write_uint32(instance->flipper_file, "Frequency", &frequency, 1)) { + if(!flipper_format_write_uint32( + instance->flipper_file, "Frequency", &preset->frequency, 1)) { FURI_LOG_E(TAG, "Unable to add Frequency"); break; } - if(!subghz_block_generic_get_preset_name(preset, temp_str)) { - break; - } + + subghz_block_generic_get_preset_name(string_get_cstr(preset->name), temp_str); if(!flipper_format_write_string_cstr( instance->flipper_file, "Preset", string_get_cstr(temp_str))) { FURI_LOG_E(TAG, "Unable to add Preset"); break; } - + if(!strcmp(string_get_cstr(temp_str), "FuriHalSubGhzPresetCustom")) { + if(!flipper_format_write_string_cstr( + instance->flipper_file, "Custom_preset_module", "CC1101")) { + FURI_LOG_E(TAG, "Unable to add Custom_preset_module"); + break; + } + if(!flipper_format_write_hex( + instance->flipper_file, "Custom_preset_data", preset->data, preset->data_size)) { + FURI_LOG_E(TAG, "Unable to add Custom_preset_data"); + break; + } + } if(!flipper_format_write_string_cstr( instance->flipper_file, "Protocol", instance->base.protocol->name)) { FURI_LOG_E(TAG, "Unable to add Protocol"); @@ -181,7 +192,7 @@ void subghz_protocol_raw_save_to_file_stop(SubGhzProtocolDecoderRAW* instance) { instance->upload_raw = NULL; flipper_format_file_close(instance->flipper_file); flipper_format_free(instance->flipper_file); - furi_record_close("storage"); + furi_record_close(RECORD_STORAGE); } instance->file_is_open = RAWFileIsOpenClose; @@ -236,9 +247,18 @@ void subghz_protocol_decoder_raw_feed(void* context, bool level, uint32_t durati } } +bool subghz_protocol_decoder_raw_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + UNUSED(context); + UNUSED(flipper_format); + //ToDo stub, for backwards compatibility + return true; +} + void subghz_protocol_decoder_raw_get_string(void* context, string_t output) { furi_assert(context); //SubGhzProtocolDecoderRAW* instance = context; + UNUSED(context); //ToDo no use string_cat_printf(output, "RAW Date"); } diff --git a/lib/subghz/protocols/raw.h b/lib/subghz/protocols/raw.h index 9e4931f9a..00654ad28 100644 --- a/lib/subghz/protocols/raw.h +++ b/lib/subghz/protocols/raw.h @@ -17,15 +17,13 @@ extern const SubGhzProtocol subghz_protocol_raw; * Open file for writing * @param instance Pointer to a SubGhzProtocolDecoderRAW instance * @param dev_name File name - * @param frequency The frequency at which the signal was received, Hz - * @param preset The modulation on which the signal was received, FuriHalSubGhzPreset + * @param preset The modulation on which the signal was received, SubGhzPresetDefinition * @return true On success */ bool subghz_protocol_raw_save_to_file_init( SubGhzProtocolDecoderRAW* instance, const char* dev_name, - uint32_t frequency, - FuriHalSubGhzPreset preset); + SubGhzPresetDefinition* preset); /** * Stop writing file to flash @@ -67,6 +65,14 @@ void subghz_protocol_decoder_raw_reset(void* context); */ void subghz_protocol_decoder_raw_feed(void* context, bool level, uint32_t duration); +/** + * Deserialize data SubGhzProtocolDecoderRAW. + * @param context Pointer to a SubGhzProtocolDecoderRAW instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_decoder_raw_deserialize(void* context, FlipperFormat* flipper_format); + /** * Getting a textual representation of the received data. * @param context Pointer to a SubGhzProtocolDecoderRAW instance diff --git a/lib/subghz/protocols/registry.c b/lib/subghz/protocols/registry.c index 5a09ee8c2..893947304 100644 --- a/lib/subghz/protocols/registry.c +++ b/lib/subghz/protocols/registry.c @@ -9,7 +9,8 @@ const SubGhzProtocol* subghz_protocol_registry[] = { &subghz_protocol_somfy_keytis, &subghz_protocol_scher_khan, &subghz_protocol_princeton, &subghz_protocol_raw, &subghz_protocol_linear, &subghz_protocol_secplus_v2, &subghz_protocol_secplus_v1, &subghz_protocol_megacode, &subghz_protocol_holtek, - &subghz_protocol_chamb_code, &subghz_protocol_power_smart, + &subghz_protocol_chamb_code, &subghz_protocol_power_smart, &subghz_protocol_marantec, + &subghz_protocol_bett, &subghz_protocol_doitrand, &subghz_protocol_phoenix_v2, }; diff --git a/lib/subghz/protocols/registry.h b/lib/subghz/protocols/registry.h index d64b0cddc..97a1376d6 100644 --- a/lib/subghz/protocols/registry.h +++ b/lib/subghz/protocols/registry.h @@ -28,6 +28,10 @@ #include "holtek.h" #include "chamberlain_code.h" #include "power_smart.h" +#include "marantec.h" +#include "bett.h" +#include "doitrand.h" +#include "phoenix_v2.h" /** * Registration by name SubGhzProtocol. diff --git a/lib/subghz/protocols/scher_khan.c b/lib/subghz/protocols/scher_khan.c index 233b95646..dd8d4c8fa 100644 --- a/lib/subghz/protocols/scher_khan.c +++ b/lib/subghz/protocols/scher_khan.c @@ -251,11 +251,10 @@ uint8_t subghz_protocol_decoder_scher_khan_get_hash_data(void* context) { bool subghz_protocol_decoder_scher_khan_serialize( void* context, FlipperFormat* flipper_format, - uint32_t frequency, - FuriHalSubGhzPreset preset) { + SubGhzPresetDefinition* preset) { furi_assert(context); SubGhzProtocolDecoderScherKhan* instance = context; - return subghz_block_generic_serialize(&instance->generic, flipper_format, frequency, preset); + return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); } bool subghz_protocol_decoder_scher_khan_deserialize(void* context, FlipperFormat* flipper_format) { diff --git a/lib/subghz/protocols/scher_khan.h b/lib/subghz/protocols/scher_khan.h index fa3f5147a..391ccc414 100644 --- a/lib/subghz/protocols/scher_khan.h +++ b/lib/subghz/protocols/scher_khan.h @@ -49,15 +49,13 @@ uint8_t subghz_protocol_decoder_scher_khan_get_hash_data(void* context); * Serialize data SubGhzProtocolDecoderScherKhan. * @param context Pointer to a SubGhzProtocolDecoderScherKhan instance * @param flipper_format Pointer to a FlipperFormat instance - * @param frequency The frequency at which the signal was received, Hz - * @param preset The modulation on which the signal was received, FuriHalSubGhzPreset + * @param preset The modulation on which the signal was received, SubGhzPresetDefinition * @return true On success */ bool subghz_protocol_decoder_scher_khan_serialize( void* context, FlipperFormat* flipper_format, - uint32_t frequency, - FuriHalSubGhzPreset preset); + SubGhzPresetDefinition* preset); /** * Deserialize data SubGhzProtocolDecoderScherKhan. diff --git a/lib/subghz/protocols/secplus_v1.c b/lib/subghz/protocols/secplus_v1.c index b51179f49..25c35ce55 100644 --- a/lib/subghz/protocols/secplus_v1.c +++ b/lib/subghz/protocols/secplus_v1.c @@ -273,7 +273,11 @@ bool subghz_protocol_encoder_secplus_v1_deserialize(void* context, FlipperFormat FURI_LOG_E(TAG, "Deserialize error"); break; } - + if(instance->generic.data_count_bit != + 2 * subghz_protocol_secplus_v1_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } //optional parameter parameter flipper_format_read_uint32( flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1); @@ -515,17 +519,28 @@ uint8_t subghz_protocol_decoder_secplus_v1_get_hash_data(void* context) { bool subghz_protocol_decoder_secplus_v1_serialize( void* context, FlipperFormat* flipper_format, - uint32_t frequency, - FuriHalSubGhzPreset preset) { + SubGhzPresetDefinition* preset) { furi_assert(context); SubGhzProtocolDecoderSecPlus_v1* instance = context; - return subghz_block_generic_serialize(&instance->generic, flipper_format, frequency, preset); + return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); } bool subghz_protocol_decoder_secplus_v1_deserialize(void* context, FlipperFormat* flipper_format) { furi_assert(context); SubGhzProtocolDecoderSecPlus_v1* instance = context; - return subghz_block_generic_deserialize(&instance->generic, flipper_format); + bool ret = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + break; + } + if(instance->generic.data_count_bit != + 2 * subghz_protocol_secplus_v1_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } + ret = true; + } while(false); + return ret; } bool subghz_protocol_secplus_v1_check_fixed(uint32_t fixed) { diff --git a/lib/subghz/protocols/secplus_v1.h b/lib/subghz/protocols/secplus_v1.h index 1c752df76..8ae1c0cb2 100644 --- a/lib/subghz/protocols/secplus_v1.h +++ b/lib/subghz/protocols/secplus_v1.h @@ -82,15 +82,13 @@ uint8_t subghz_protocol_decoder_secplus_v1_get_hash_data(void* context); * Serialize data SubGhzProtocolDecoderSecPlus_v1. * @param context Pointer to a SubGhzProtocolDecoderSecPlus_v1 instance * @param flipper_format Pointer to a FlipperFormat instance - * @param frequency The frequency at which the signal was received, Hz - * @param preset The modulation on which the signal was received, FuriHalSubGhzPreset + * @param preset The modulation on which the signal was received, SubGhzPresetDefinition * @return true On success */ bool subghz_protocol_decoder_secplus_v1_serialize( void* context, FlipperFormat* flipper_format, - uint32_t frequency, - FuriHalSubGhzPreset preset); + SubGhzPresetDefinition* preset); /** * Deserialize data SubGhzProtocolDecoderSecPlus_v1. diff --git a/lib/subghz/protocols/secplus_v2.c b/lib/subghz/protocols/secplus_v2.c index 70ff19e48..37dc1c823 100644 --- a/lib/subghz/protocols/secplus_v2.c +++ b/lib/subghz/protocols/secplus_v2.c @@ -514,7 +514,11 @@ bool subghz_protocol_encoder_secplus_v2_deserialize(void* context, FlipperFormat FURI_LOG_E(TAG, "Deserialize error"); break; } - + if(instance->generic.data_count_bit != + subghz_protocol_secplus_v2_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } uint8_t key_data[sizeof(uint64_t)] = {0}; if(!flipper_format_read_hex( flipper_format, "Secplus_packet_1", key_data, sizeof(uint64_t))) { @@ -588,8 +592,7 @@ bool subghz_protocol_secplus_v2_create_data( uint32_t serial, uint8_t btn, uint32_t cnt, - uint32_t frequency, - FuriHalSubGhzPreset preset) { + SubGhzPresetDefinition* preset) { furi_assert(context); SubGhzProtocolEncoderSecPlus_v2* instance = context; instance->generic.serial = serial; @@ -598,8 +601,7 @@ bool subghz_protocol_secplus_v2_create_data( instance->generic.data_count_bit = (uint8_t)subghz_protocol_secplus_v2_const.min_count_bit_for_found; subghz_protocol_secplus_v2_encode(instance); - bool res = - subghz_block_generic_serialize(&instance->generic, flipper_format, frequency, preset); + bool res = subghz_block_generic_serialize(&instance->generic, flipper_format, preset); uint8_t key_data[sizeof(uint64_t)] = {0}; for(size_t i = 0; i < sizeof(uint64_t); i++) { @@ -691,7 +693,7 @@ void subghz_protocol_decoder_secplus_v2_feed(void* context, bool level, uint32_t } else if( duration >= (uint32_t)(subghz_protocol_secplus_v2_const.te_long * 2 + subghz_protocol_secplus_v2_const.te_delta)) { - if(instance->decoder.decode_count_bit >= + if(instance->decoder.decode_count_bit == subghz_protocol_secplus_v2_const.min_count_bit_for_found) { instance->generic.data = instance->decoder.decode_data; instance->generic.data_count_bit = instance->decoder.decode_count_bit; @@ -757,12 +759,10 @@ uint8_t subghz_protocol_decoder_secplus_v2_get_hash_data(void* context) { bool subghz_protocol_decoder_secplus_v2_serialize( void* context, FlipperFormat* flipper_format, - uint32_t frequency, - FuriHalSubGhzPreset preset) { + SubGhzPresetDefinition* preset) { furi_assert(context); SubGhzProtocolDecoderSecPlus_v2* instance = context; - bool res = - subghz_block_generic_serialize(&instance->generic, flipper_format, frequency, preset); + bool res = subghz_block_generic_serialize(&instance->generic, flipper_format, preset); uint8_t key_data[sizeof(uint64_t)] = {0}; for(size_t i = 0; i < sizeof(uint64_t); i++) { @@ -786,6 +786,11 @@ bool subghz_protocol_decoder_secplus_v2_deserialize(void* context, FlipperFormat FURI_LOG_E(TAG, "Deserialize error"); break; } + if(instance->generic.data_count_bit != + subghz_protocol_secplus_v2_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } if(!flipper_format_rewind(flipper_format)) { FURI_LOG_E(TAG, "Rewind error"); break; diff --git a/lib/subghz/protocols/secplus_v2.h b/lib/subghz/protocols/secplus_v2.h index 3d6951482..9e9ae2a7d 100644 --- a/lib/subghz/protocols/secplus_v2.h +++ b/lib/subghz/protocols/secplus_v2.h @@ -52,8 +52,7 @@ LevelDuration subghz_protocol_encoder_secplus_v2_yield(void* context); * @param btn Button number, 8 bit * @param cnt Container value, 28 bit * @param manufacture_name Name of manufacturer's key - * @param frequency Transmission frequency, Hz - * @param preset Modulation, FuriHalSubGhzPreset + * @param preset Modulation, SubGhzPresetDefinition * @return true On success */ bool subghz_protocol_secplus_v2_create_data( @@ -62,8 +61,7 @@ bool subghz_protocol_secplus_v2_create_data( uint32_t serial, uint8_t btn, uint32_t cnt, - uint32_t frequency, - FuriHalSubGhzPreset preset); + SubGhzPresetDefinition* preset); /** * Allocate SubGhzProtocolDecoderSecPlus_v2. @@ -103,15 +101,13 @@ uint8_t subghz_protocol_decoder_secplus_v2_get_hash_data(void* context); * Serialize data SubGhzProtocolDecoderSecPlus_v2. * @param context Pointer to a SubGhzProtocolDecoderSecPlus_v2 instance * @param flipper_format Pointer to a FlipperFormat instance - * @param frequency The frequency at which the signal was received, Hz - * @param preset The modulation on which the signal was received, FuriHalSubGhzPreset + * @param preset The modulation on which the signal was received, SubGhzPresetDefinition * @return true On success */ bool subghz_protocol_decoder_secplus_v2_serialize( void* context, FlipperFormat* flipper_format, - uint32_t frequency, - FuriHalSubGhzPreset preset); + SubGhzPresetDefinition* preset); /** * Deserialize data SubGhzProtocolDecoderSecPlus_v2. diff --git a/lib/subghz/protocols/somfy_keytis.c b/lib/subghz/protocols/somfy_keytis.c index 764e81478..7a3b2186f 100644 --- a/lib/subghz/protocols/somfy_keytis.c +++ b/lib/subghz/protocols/somfy_keytis.c @@ -382,12 +382,10 @@ uint8_t subghz_protocol_decoder_somfy_keytis_get_hash_data(void* context) { bool subghz_protocol_decoder_somfy_keytis_serialize( void* context, FlipperFormat* flipper_format, - uint32_t frequency, - FuriHalSubGhzPreset preset) { + SubGhzPresetDefinition* preset) { furi_assert(context); SubGhzProtocolDecoderSomfyKeytis* instance = context; - bool res = - subghz_block_generic_serialize(&instance->generic, flipper_format, frequency, preset); + bool res = subghz_block_generic_serialize(&instance->generic, flipper_format, preset); if(res && !flipper_format_write_uint32( flipper_format, "Duration_Counter", &instance->press_duration_counter, 1)) { FURI_LOG_E(TAG, "Unable to add Duration_Counter"); @@ -405,6 +403,11 @@ bool subghz_protocol_decoder_somfy_keytis_deserialize(void* context, FlipperForm FURI_LOG_E(TAG, "Deserialize error"); break; } + if(instance->generic.data_count_bit != + subghz_protocol_somfy_keytis_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } if(!flipper_format_rewind(flipper_format)) { FURI_LOG_E(TAG, "Rewind error"); break; diff --git a/lib/subghz/protocols/somfy_keytis.h b/lib/subghz/protocols/somfy_keytis.h index eea12f362..29f96cd6a 100644 --- a/lib/subghz/protocols/somfy_keytis.h +++ b/lib/subghz/protocols/somfy_keytis.h @@ -49,15 +49,13 @@ uint8_t subghz_protocol_decoder_somfy_keytis_get_hash_data(void* context); * Serialize data SubGhzProtocolDecoderSomfyKeytis. * @param context Pointer to a SubGhzProtocolDecoderSomfyKeytis instance * @param flipper_format Pointer to a FlipperFormat instance - * @param frequency The frequency at which the signal was received, Hz - * @param preset The modulation on which the signal was received, FuriHalSubGhzPreset + * @param preset The modulation on which the signal was received, SubGhzPresetDefinition * @return true On success */ bool subghz_protocol_decoder_somfy_keytis_serialize( void* context, FlipperFormat* flipper_format, - uint32_t frequency, - FuriHalSubGhzPreset preset); + SubGhzPresetDefinition* preset); /** * Deserialize data SubGhzProtocolDecoderSomfyKeytis. diff --git a/lib/subghz/protocols/somfy_telis.c b/lib/subghz/protocols/somfy_telis.c index 634074273..b9aac5777 100644 --- a/lib/subghz/protocols/somfy_telis.c +++ b/lib/subghz/protocols/somfy_telis.c @@ -339,17 +339,28 @@ uint8_t subghz_protocol_decoder_somfy_telis_get_hash_data(void* context) { bool subghz_protocol_decoder_somfy_telis_serialize( void* context, FlipperFormat* flipper_format, - uint32_t frequency, - FuriHalSubGhzPreset preset) { + SubGhzPresetDefinition* preset) { furi_assert(context); SubGhzProtocolDecoderSomfyTelis* instance = context; - return subghz_block_generic_serialize(&instance->generic, flipper_format, frequency, preset); + return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); } bool subghz_protocol_decoder_somfy_telis_deserialize(void* context, FlipperFormat* flipper_format) { furi_assert(context); SubGhzProtocolDecoderSomfyTelis* instance = context; - return subghz_block_generic_deserialize(&instance->generic, flipper_format); + bool ret = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + break; + } + if(instance->generic.data_count_bit != + subghz_protocol_somfy_telis_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } + ret = true; + } while(false); + return ret; } void subghz_protocol_decoder_somfy_telis_get_string(void* context, string_t output) { diff --git a/lib/subghz/protocols/somfy_telis.h b/lib/subghz/protocols/somfy_telis.h index 3414de6f5..ff5b45e23 100644 --- a/lib/subghz/protocols/somfy_telis.h +++ b/lib/subghz/protocols/somfy_telis.h @@ -49,15 +49,13 @@ uint8_t subghz_protocol_decoder_somfy_telis_get_hash_data(void* context); * Serialize data SubGhzProtocolDecoderSomfyTelis. * @param context Pointer to a SubGhzProtocolDecoderSomfyTelis instance * @param flipper_format Pointer to a FlipperFormat instance - * @param frequency The frequency at which the signal was received, Hz - * @param preset The modulation on which the signal was received, FuriHalSubGhzPreset + * @param preset The modulation on which the signal was received, SubGhzPresetDefinition * @return true On success */ bool subghz_protocol_decoder_somfy_telis_serialize( void* context, FlipperFormat* flipper_format, - uint32_t frequency, - FuriHalSubGhzPreset preset); + SubGhzPresetDefinition* preset); /** * Deserialize data SubGhzProtocolDecoderSomfyTelis. diff --git a/lib/subghz/protocols/star_line.c b/lib/subghz/protocols/star_line.c index 7db33570c..757b56622 100644 --- a/lib/subghz/protocols/star_line.c +++ b/lib/subghz/protocols/star_line.c @@ -320,20 +320,23 @@ uint8_t subghz_protocol_decoder_star_line_get_hash_data(void* context) { bool subghz_protocol_decoder_star_line_serialize( void* context, FlipperFormat* flipper_format, - uint32_t frequency, - FuriHalSubGhzPreset preset) { + SubGhzPresetDefinition* preset) { furi_assert(context); SubGhzProtocolDecoderStarLine* instance = context; subghz_protocol_star_line_check_remote_controller( &instance->generic, instance->keystore, &instance->manufacture_name); - bool res = - subghz_block_generic_serialize(&instance->generic, flipper_format, frequency, preset); + bool res = subghz_block_generic_serialize(&instance->generic, flipper_format, preset); if(res && !flipper_format_write_string_cstr( flipper_format, "Manufacture", instance->manufacture_name)) { FURI_LOG_E(TAG, "Unable to add manufacture name"); res = false; } + if(res && instance->generic.data_count_bit != + subghz_protocol_star_line_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + res = false; + } return res; } diff --git a/lib/subghz/protocols/star_line.h b/lib/subghz/protocols/star_line.h index 9253ff523..e240954b1 100644 --- a/lib/subghz/protocols/star_line.h +++ b/lib/subghz/protocols/star_line.h @@ -49,15 +49,13 @@ uint8_t subghz_protocol_decoder_star_line_get_hash_data(void* context); * Serialize data SubGhzProtocolDecoderStarLine. * @param context Pointer to a SubGhzProtocolDecoderStarLine instance * @param flipper_format Pointer to a FlipperFormat instance - * @param frequency The frequency at which the signal was received, Hz - * @param preset The modulation on which the signal was received, FuriHalSubGhzPreset + * @param preset The modulation on which the signal was received, SubGhzPresetDefinition * @return true On success */ bool subghz_protocol_decoder_star_line_serialize( void* context, FlipperFormat* flipper_format, - uint32_t frequency, - FuriHalSubGhzPreset preset); + SubGhzPresetDefinition* preset); /** * Deserialize data SubGhzProtocolDecoderStarLine. diff --git a/lib/subghz/subghz_file_encoder_worker.c b/lib/subghz/subghz_file_encoder_worker.c index 457c8b02b..8f65ea004 100644 --- a/lib/subghz/subghz_file_encoder_worker.c +++ b/lib/subghz/subghz_file_encoder_worker.c @@ -183,7 +183,7 @@ SubGhzFileEncoderWorker* subghz_file_encoder_worker_alloc() { furi_thread_set_callback(instance->thread, subghz_file_encoder_worker_thread); instance->stream = xStreamBufferCreate(sizeof(int32_t) * 2048, sizeof(int32_t)); - instance->storage = furi_record_open("storage"); + instance->storage = furi_record_open(RECORD_STORAGE); instance->flipper_format = flipper_format_file_alloc(instance->storage); string_init(instance->str_data); @@ -204,7 +204,7 @@ void subghz_file_encoder_worker_free(SubGhzFileEncoderWorker* instance) { string_clear(instance->file_path); flipper_format_free(instance->flipper_format); - furi_record_close("storage"); + furi_record_close(RECORD_STORAGE); free(instance); } diff --git a/lib/subghz/subghz_keystore.c b/lib/subghz/subghz_keystore.c index d3903bc51..0abd2d5e6 100644 --- a/lib/subghz/subghz_keystore.c +++ b/lib/subghz/subghz_keystore.c @@ -189,7 +189,7 @@ bool subghz_keystore_load(SubGhzKeystore* instance, const char* file_name) { FURI_LOG_I(TAG, "Loading keystore %s", file_name); - Storage* storage = furi_record_open("storage"); + Storage* storage = furi_record_open(RECORD_STORAGE); FlipperFormat* flipper_format = flipper_format_file_alloc(storage); do { @@ -229,7 +229,7 @@ bool subghz_keystore_load(SubGhzKeystore* instance, const char* file_name) { } while(0); flipper_format_free(flipper_format); - furi_record_close("storage"); + furi_record_close(RECORD_STORAGE); string_clear(filetype); @@ -240,7 +240,7 @@ bool subghz_keystore_save(SubGhzKeystore* instance, const char* file_name, uint8 furi_assert(instance); bool result = false; - Storage* storage = furi_record_open("storage"); + Storage* storage = furi_record_open(RECORD_STORAGE); char* decrypted_line = malloc(SUBGHZ_KEYSTORE_FILE_DECRYPTED_LINE_SIZE); char* encrypted_line = malloc(SUBGHZ_KEYSTORE_FILE_ENCRYPTED_LINE_SIZE); @@ -326,7 +326,7 @@ bool subghz_keystore_save(SubGhzKeystore* instance, const char* file_name, uint8 free(encrypted_line); free(decrypted_line); - furi_record_close("storage"); + furi_record_close(RECORD_STORAGE); return result; } @@ -346,7 +346,7 @@ bool subghz_keystore_raw_encrypted_save( string_init(filetype); SubGhzKeystoreEncryption encryption; - Storage* storage = furi_record_open("storage"); + Storage* storage = furi_record_open(RECORD_STORAGE); char* encrypted_line = malloc(SUBGHZ_KEYSTORE_FILE_ENCRYPTED_LINE_SIZE); @@ -470,7 +470,7 @@ bool subghz_keystore_raw_encrypted_save( free(encrypted_line); - furi_record_close("storage"); + furi_record_close(RECORD_STORAGE); return encrypted; } @@ -484,7 +484,7 @@ bool subghz_keystore_raw_get_data(const char* file_name, size_t offset, uint8_t* string_t str_temp; string_init(str_temp); - Storage* storage = furi_record_open("storage"); + Storage* storage = furi_record_open(RECORD_STORAGE); char* decrypted_line = malloc(SUBGHZ_KEYSTORE_FILE_DECRYPTED_LINE_SIZE); FlipperFormat* flipper_format = flipper_format_file_alloc(storage); @@ -594,7 +594,7 @@ bool subghz_keystore_raw_get_data(const char* file_name, size_t offset, uint8_t* } while(0); flipper_format_free(flipper_format); - furi_record_close("storage"); + furi_record_close(RECORD_STORAGE); free(decrypted_line); diff --git a/lib/subghz/subghz_worker.c b/lib/subghz/subghz_worker.c index dcebdfffd..58db8ea5d 100644 --- a/lib/subghz/subghz_worker.c +++ b/lib/subghz/subghz_worker.c @@ -57,7 +57,6 @@ static int32_t subghz_worker_thread_callback(void* context) { if(ret == sizeof(LevelDuration)) { if(level_duration_is_reset(level_duration)) { FURI_LOG_E(TAG, "Overrun buffer"); - ; if(instance->overrun_callback) instance->overrun_callback(instance->context); } else { bool level = level_duration_get_level(level_duration); @@ -98,11 +97,11 @@ SubGhzWorker* subghz_worker_alloc() { furi_thread_set_context(instance->thread, instance); furi_thread_set_callback(instance->thread, subghz_worker_thread_callback); - instance->stream = xStreamBufferCreate(sizeof(LevelDuration) * 2048, sizeof(LevelDuration)); + instance->stream = xStreamBufferCreate(sizeof(LevelDuration) * 4096, sizeof(LevelDuration)); //setting filter instance->filter_running = true; - instance->filter_duration = 20; + instance->filter_duration = 30; return instance; } diff --git a/lib/subghz/types.h b/lib/subghz/types.h index 49f971f04..32b70b651 100644 --- a/lib/subghz/types.h +++ b/lib/subghz/types.h @@ -10,9 +10,10 @@ #include "environment.h" #include #include +#include -#define SUBGHZ_APP_FOLDER "/any/subghz" -#define SUBGHZ_RAW_FOLDER "/ext/subghz" +#define SUBGHZ_APP_FOLDER ANY_PATH("subghz") +#define SUBGHZ_RAW_FOLDER EXT_PATH("subghz") #define SUBGHZ_APP_EXTENSION ".sub" #define SUBGHZ_KEY_FILE_VERSION 1 @@ -33,8 +34,7 @@ typedef void (*SubGhzFree)(void* context); typedef bool (*SubGhzSerialize)( void* context, FlipperFormat* flipper_format, - uint32_t frequency, - FuriHalSubGhzPreset preset); + SubGhzPresetDefinition* preset); typedef bool (*SubGhzDeserialize)(void* context, FlipperFormat* flipper_format); // Decoder specific diff --git a/lib/toolbox/dir_walk.c b/lib/toolbox/dir_walk.c index 2efbc0f1a..348bd5442 100644 --- a/lib/toolbox/dir_walk.c +++ b/lib/toolbox/dir_walk.c @@ -55,7 +55,7 @@ static bool dir_walk_filter(DirWalk* dir_walk, const char* name, FileInfo* filei static DirWalkResult dir_walk_iter(DirWalk* dir_walk, string_t return_path, FileInfo* fileinfo) { DirWalkResult result = DirWalkError; - char* name = malloc(256); + char* name = malloc(256); // FIXME: remove magic number FileInfo info; bool end = false; diff --git a/lib/toolbox/path.c b/lib/toolbox/path.c index 607671fb9..1262362ef 100644 --- a/lib/toolbox/path.c +++ b/lib/toolbox/path.c @@ -56,7 +56,7 @@ void path_extract_basename(const char* path, string_t basename) { path_cleanup(basename); size_t pos = string_search_rchar(basename, '/'); if(pos != STRING_FAILURE) { - string_right(basename, pos); + string_right(basename, pos + 1); } } @@ -85,6 +85,10 @@ void path_concat(const char* path, const char* suffix, string_t out_path) { } bool path_contains_only_ascii(const char* path) { + if(!path) { + return false; + } + const char* name_pos = strrchr(path, '/'); if(name_pos == NULL) { name_pos = path; @@ -102,7 +106,7 @@ bool path_contains_only_ascii(const char* path) { } else if((*name_pos >= 'a') && (*name_pos <= 'z')) { name_pos++; continue; - } else if(strchr(".!#\\$%&'()-@^_`{}~", *name_pos) != NULL) { + } else if(strchr(" .!#\\$%&'()-@^_`{}~", *name_pos) != NULL) { name_pos++; continue; } @@ -111,4 +115,4 @@ bool path_contains_only_ascii(const char* path) { } return true; -} \ No newline at end of file +} diff --git a/lib/toolbox/random_name.c b/lib/toolbox/random_name.c index b581bb979..5a5374398 100644 --- a/lib/toolbox/random_name.c +++ b/lib/toolbox/random_name.c @@ -12,15 +12,10 @@ void set_random_name(char* name, uint8_t max_name_size) { rand_generator_inited = true; } const char* prefix[] = { - "ancient", - "hollow", - "strange", - "disappeared", - "unknown", - "unthinkable", - "unnamable", - "nameless", - "my", + "ancient", "hollow", "strange", "disappeared", "unknown", + "unthinkable", "unnamable", "nameless", "my", "concealed", + "forgotten", "hidden", "mysterious", "obscure", "random", + "remote", "uncharted", "undefined", "untravelled", "untold", }; const char* suffix[] = { @@ -32,11 +27,15 @@ void set_random_name(char* name, uint8_t max_name_size) { "entree", "opening", "crack", + "access", + "corridor", + "passage", + "port", }; uint8_t prefix_i = rand() % COUNT_OF(prefix); uint8_t suffix_i = rand() % COUNT_OF(suffix); - sniprintf(name, max_name_size, "%s_%s", prefix[prefix_i], suffix[suffix_i]); + snprintf(name, max_name_size, "%s_%s", prefix[prefix_i], suffix[suffix_i]); // Set first symbol to upper case name[0] = name[0] - 0x20; } diff --git a/lib/toolbox/saved_struct.c b/lib/toolbox/saved_struct.c index ef7dbcf74..65b761f80 100644 --- a/lib/toolbox/saved_struct.c +++ b/lib/toolbox/saved_struct.c @@ -22,7 +22,7 @@ bool saved_struct_save(const char* path, void* data, size_t size, uint8_t magic, FURI_LOG_I(TAG, "Saving \"%s\"", path); // Store - Storage* storage = furi_record_open("storage"); + Storage* storage = furi_record_open(RECORD_STORAGE); File* file = storage_file_alloc(storage); bool result = true; bool saved = storage_file_open(file, path, FSAM_WRITE, FSOM_CREATE_ALWAYS); @@ -58,7 +58,7 @@ bool saved_struct_save(const char* path, void* data, size_t size, uint8_t magic, storage_file_close(file); storage_file_free(file); - furi_record_close("storage"); + furi_record_close(RECORD_STORAGE); return result; } @@ -68,7 +68,7 @@ bool saved_struct_load(const char* path, void* data, size_t size, uint8_t magic, SavedStructHeader header; uint8_t* data_read = malloc(size); - Storage* storage = furi_record_open("storage"); + Storage* storage = furi_record_open(RECORD_STORAGE); File* file = storage_file_alloc(storage); bool result = true; bool loaded = storage_file_open(file, path, FSAM_READ, FSOM_OPEN_EXISTING); @@ -120,7 +120,7 @@ bool saved_struct_load(const char* path, void* data, size_t size, uint8_t magic, storage_file_close(file); storage_file_free(file); - furi_record_close("storage"); + furi_record_close(RECORD_STORAGE); free(data_read); return result; diff --git a/lib/toolbox/stream/buffered_file_stream.c b/lib/toolbox/stream/buffered_file_stream.c index 5db276d3f..3b20a391c 100644 --- a/lib/toolbox/stream/buffered_file_stream.c +++ b/lib/toolbox/stream/buffered_file_stream.c @@ -8,6 +8,7 @@ typedef struct { Stream stream_base; Stream* file_stream; StreamCache* cache; + bool sync_pending; } BufferedFileStream; static void buffered_file_stream_free(BufferedFileStream* stream); @@ -26,6 +27,9 @@ static bool buffered_file_stream_delete_and_insert( StreamWriteCB write_callback, const void* ctx); +static bool buffered_file_stream_flush(BufferedFileStream* stream); +static bool buffered_file_stream_unread(BufferedFileStream* stream); + const StreamVTable buffered_file_stream_vtable = { .free = (StreamFreeFn)buffered_file_stream_free, .eof = (StreamEOFFn)buffered_file_stream_eof, @@ -43,6 +47,7 @@ Stream* buffered_file_stream_alloc(Storage* storage) { stream->file_stream = file_stream_alloc(storage); stream->cache = stream_cache_alloc(); + stream->sync_pending = false; stream->stream_base.vtable = &buffered_file_stream_vtable; return (Stream*)stream; @@ -55,7 +60,6 @@ bool buffered_file_stream_open( FS_OpenMode open_mode) { furi_assert(_stream); BufferedFileStream* stream = (BufferedFileStream*)_stream; - stream_cache_drop(stream->cache); furi_check(stream->stream_base.vtable == &buffered_file_stream_vtable); return file_stream_open(stream->file_stream, path, access_mode, open_mode); } @@ -64,7 +68,22 @@ bool buffered_file_stream_close(Stream* _stream) { furi_assert(_stream); BufferedFileStream* stream = (BufferedFileStream*)_stream; furi_check(stream->stream_base.vtable == &buffered_file_stream_vtable); - return file_stream_close(stream->file_stream); + bool success = false; + do { + if(!(stream->sync_pending ? buffered_file_stream_flush(stream) : + buffered_file_stream_unread(stream))) + break; + if(!file_stream_close(stream->file_stream)) break; + success = true; + } while(false); + return success; +} + +bool buffered_file_stream_sync(Stream* _stream) { + furi_assert(_stream); + BufferedFileStream* stream = (BufferedFileStream*)_stream; + furi_check(stream->stream_base.vtable == &buffered_file_stream_vtable); + return stream->sync_pending ? buffered_file_stream_flush(stream) : true; } FS_Error buffered_file_stream_get_error(Stream* _stream) { @@ -82,10 +101,23 @@ static void buffered_file_stream_free(BufferedFileStream* stream) { } static bool buffered_file_stream_eof(BufferedFileStream* stream) { - return stream_cache_at_end(stream->cache) && stream_eof(stream->file_stream); + bool ret; + const bool file_stream_eof = stream_eof(stream->file_stream); + const bool cache_at_end = stream_cache_at_end(stream->cache); + if(!stream->sync_pending) { + ret = file_stream_eof && cache_at_end; + } else { + const size_t remaining_size = + stream_size(stream->file_stream) - stream_tell(stream->file_stream); + ret = stream_cache_size(stream->cache) >= + (remaining_size ? cache_at_end : file_stream_eof); + } + return ret; } static void buffered_file_stream_clean(BufferedFileStream* stream) { + // Not syncing because data will be deleted anyway + stream->sync_pending = false; stream_cache_drop(stream->cache); stream_clean(stream->file_stream); } @@ -94,7 +126,7 @@ static bool buffered_file_stream_seek( BufferedFileStream* stream, int32_t offset, StreamOffset offset_type) { - bool success = false; + bool success = true; int32_t new_offset = offset; if(offset_type == StreamOffsetFromCurrent) { @@ -105,43 +137,71 @@ static bool buffered_file_stream_seek( } if((new_offset != 0) || (offset_type != StreamOffsetFromCurrent)) { - stream_cache_drop(stream->cache); - success = stream_seek(stream->file_stream, new_offset, offset_type); - } else { - success = true; + if(stream->sync_pending) { + success = buffered_file_stream_sync((Stream*)stream); + } else { + stream_cache_drop(stream->cache); + } + if(success) { + success = stream_seek(stream->file_stream, new_offset, offset_type); + } } return success; } static size_t buffered_file_stream_tell(BufferedFileStream* stream) { - return stream_tell(stream->file_stream) + stream_cache_pos(stream->cache) - - stream_cache_size(stream->cache); + size_t pos = stream_tell(stream->file_stream) + stream_cache_pos(stream->cache); + if(!stream->sync_pending) { + pos -= stream_cache_size(stream->cache); + } + return pos; } static size_t buffered_file_stream_size(BufferedFileStream* stream) { - return stream_cache_size(stream->cache) + stream_size(stream->file_stream); + size_t size = stream_size(stream->file_stream); + if(stream->sync_pending) { + const size_t remaining_size = size - stream_tell(stream->file_stream); + const size_t cache_size = stream_cache_size(stream->cache); + if(cache_size > remaining_size) { + size += (cache_size - remaining_size); + } + } + return size; } static size_t buffered_file_stream_write(BufferedFileStream* stream, const uint8_t* data, size_t size) { - stream_cache_drop(stream->cache); - return stream_write(stream->file_stream, data, size); + size_t need_to_write = size; + do { + if(!stream->sync_pending) { + if(!buffered_file_stream_unread(stream)) break; + } + while(need_to_write) { + stream->sync_pending = true; + need_to_write -= + stream_cache_write(stream->cache, data + (size - need_to_write), need_to_write); + if(need_to_write) { + stream->sync_pending = false; + if(!stream_cache_flush(stream->cache, stream->file_stream)) break; + } + } + } while(false); + return size - need_to_write; } static size_t buffered_file_stream_read(BufferedFileStream* stream, uint8_t* data, size_t size) { size_t need_to_read = size; - while(need_to_read) { need_to_read -= stream_cache_read(stream->cache, data + (size - need_to_read), need_to_read); if(need_to_read) { - if(!stream_cache_fill(stream->cache, stream->file_stream)) { - break; + if(stream->sync_pending) { + if(!buffered_file_stream_flush(stream)) break; } + if(!stream_cache_fill(stream->cache, stream->file_stream)) break; } } - return size - need_to_read; } @@ -150,6 +210,43 @@ static bool buffered_file_stream_delete_and_insert( size_t delete_size, StreamWriteCB write_callback, const void* ctx) { - stream_cache_drop(stream->cache); - return stream_delete_and_insert(stream->file_stream, delete_size, write_callback, ctx); + bool success = false; + do { + if(!(stream->sync_pending ? buffered_file_stream_flush(stream) : + buffered_file_stream_unread(stream))) + break; + if(!stream_delete_and_insert(stream->file_stream, delete_size, write_callback, ctx)) break; + success = true; + } while(false); + return success; +} + +// Write the cache into the underlying stream and adjust seek position +static bool buffered_file_stream_flush(BufferedFileStream* stream) { + bool success = false; + do { + const int32_t offset = stream_cache_size(stream->cache) - stream_cache_pos(stream->cache); + if(!stream_cache_flush(stream->cache, stream->file_stream)) break; + if(offset > 0) { + if(!stream_seek(stream->file_stream, -offset, StreamOffsetFromCurrent)) break; + } + success = true; + } while(false); + stream->sync_pending = false; + return success; +} + +// Drop read cache and adjust the underlying stream seek position +static bool buffered_file_stream_unread(BufferedFileStream* stream) { + bool success = true; + const size_t cache_size = stream_cache_size(stream->cache); + if(cache_size > 0) { + const size_t cache_pos = stream_cache_pos(stream->cache); + if(cache_pos < cache_size) { + const int32_t offset = cache_size - cache_pos; + success = stream_seek(stream->file_stream, -offset, StreamOffsetFromCurrent); + } + stream_cache_drop(stream->cache); + } + return success; } diff --git a/lib/toolbox/stream/buffered_file_stream.h b/lib/toolbox/stream/buffered_file_stream.h index e6ad72091..54917b6a4 100644 --- a/lib/toolbox/stream/buffered_file_stream.h +++ b/lib/toolbox/stream/buffered_file_stream.h @@ -19,7 +19,7 @@ Stream* buffered_file_stream_alloc(Storage* storage); * @param path path to file * @param access_mode access mode from FS_AccessMode * @param open_mode open mode from FS_OpenMode - * @return success flag. You need to close the file even if the open operation failed. + * @return True on success, False on failure. You need to close the file even if the open operation failed. */ bool buffered_file_stream_open( Stream* stream, @@ -29,12 +29,18 @@ bool buffered_file_stream_open( /** * Closes the file. - * @param stream - * @return true - * @return false + * @param stream pointer to file stream object. + * @return True on success, False on failure. */ bool buffered_file_stream_close(Stream* stream); +/** + * Forces write from cache to the underlying file. + * @param stream pointer to file stream object. + * @return True on success, False on failure. + */ +bool buffered_file_stream_sync(Stream* stream); + /** * Retrieves the error id from the file object * @param stream pointer to stream object. diff --git a/lib/toolbox/stream/file_stream.c b/lib/toolbox/stream/file_stream.c index 70db8af9d..f7363c6be 100644 --- a/lib/toolbox/stream/file_stream.c +++ b/lib/toolbox/stream/file_stream.c @@ -176,8 +176,9 @@ static bool file_stream_delete_and_insert( string_t scratch_name; string_t tmp_name; string_init(tmp_name); - storage_get_next_filename(_stream->storage, "/any", ".scratch", ".pad", tmp_name, 255); - string_init_printf(scratch_name, "/any/%s.pad", string_get_cstr(tmp_name)); + storage_get_next_filename( + _stream->storage, STORAGE_ANY_PATH_PREFIX, ".scratch", ".pad", tmp_name, 255); + string_init_printf(scratch_name, ANY_PATH("%s.pad"), string_get_cstr(tmp_name)); string_clear(tmp_name); do { diff --git a/lib/toolbox/stream/stream_cache.c b/lib/toolbox/stream/stream_cache.c index 164ac466c..f5e147dfe 100644 --- a/lib/toolbox/stream/stream_cache.c +++ b/lib/toolbox/stream/stream_cache.c @@ -46,6 +46,14 @@ size_t stream_cache_fill(StreamCache* cache, Stream* stream) { return size_read; } +bool stream_cache_flush(StreamCache* cache, Stream* stream) { + const size_t size_written = stream_write(stream, cache->data, cache->data_size); + const bool success = (size_written == cache->data_size); + cache->data_size = 0; + cache->position = 0; + return success; +} + size_t stream_cache_read(StreamCache* cache, uint8_t* data, size_t size) { furi_assert(cache->data_size >= cache->position); const size_t size_read = MIN(size, cache->data_size - cache->position); @@ -56,6 +64,19 @@ size_t stream_cache_read(StreamCache* cache, uint8_t* data, size_t size) { return size_read; } +size_t stream_cache_write(StreamCache* cache, const uint8_t* data, size_t size) { + furi_assert(cache->data_size >= cache->position); + const size_t size_written = MIN(size, STREAM_CACHE_MAX_SIZE - cache->position); + if(size_written > 0) { + memcpy(cache->data + cache->position, data, size_written); + cache->position += size_written; + if(cache->position > cache->data_size) { + cache->data_size = cache->position; + } + } + return size_written; +} + int32_t stream_cache_seek(StreamCache* cache, int32_t offset) { furi_assert(cache->data_size >= cache->position); int32_t actual_offset = 0; @@ -63,7 +84,7 @@ int32_t stream_cache_seek(StreamCache* cache, int32_t offset) { if(offset > 0) { actual_offset = MIN(cache->data_size - cache->position, (size_t)offset); } else if(offset < 0) { - actual_offset = -MIN(cache->position, (size_t)abs(offset)); + actual_offset = MAX(-((int32_t)cache->position), offset); } cache->position += actual_offset; diff --git a/lib/toolbox/stream/stream_cache.h b/lib/toolbox/stream/stream_cache.h index 20c18d802..f61e5e8f2 100644 --- a/lib/toolbox/stream/stream_cache.h +++ b/lib/toolbox/stream/stream_cache.h @@ -55,6 +55,14 @@ size_t stream_cache_pos(StreamCache* cache); */ size_t stream_cache_fill(StreamCache* cache, Stream* stream); +/** + * Write as much cached data as possible to a stream. + * @param cache Pointer to a StreamCache instance + * @param stream Pointer to a Stream instance + * @return True on success, False on failure. + */ +bool stream_cache_flush(StreamCache* cache, Stream* stream); + /** * Read cached data and advance the internal cursor. * @param cache Pointer to a StreamCache instance. @@ -64,6 +72,15 @@ size_t stream_cache_fill(StreamCache* cache, Stream* stream); */ size_t stream_cache_read(StreamCache* cache, uint8_t* data, size_t size); +/** + * Write to cached data and advance the internal cursor. + * @param cache Pointer to a StreamCache instance. + * @param data Pointer to a data buffer. + * @param size Maximum size in bytes to write to the cache. + * @return Actual size that was written. + */ +size_t stream_cache_write(StreamCache* cache, const uint8_t* data, size_t size); + /** * Move the internal cursor relatively to its current position. * @param cache Pointer to a StreamCache instance. diff --git a/lib/toolbox/tar/tar_archive.c b/lib/toolbox/tar/tar_archive.c index cc107530c..5ac89a0fd 100644 --- a/lib/toolbox/tar/tar_archive.c +++ b/lib/toolbox/tar/tar_archive.c @@ -165,12 +165,12 @@ bool tar_archive_file_finalize(TarArchive* archive) { typedef struct { TarArchive* archive; const char* work_dir; + Storage_name_converter converter; } TarArchiveDirectoryOpParams; static int archive_extract_foreach_cb(mtar_t* tar, const mtar_header_t* header, void* param) { TarArchiveDirectoryOpParams* op_params = param; TarArchive* archive = op_params->archive; - string_t fname; bool skip_entry = false; if(archive->unpack_cb) { @@ -183,12 +183,14 @@ static int archive_extract_foreach_cb(mtar_t* tar, const mtar_header_t* header, return 0; } + string_t full_extracted_fname; if(header->type == MTAR_TDIR) { - string_init(fname); - path_concat(op_params->work_dir, header->name, fname); + string_init(full_extracted_fname); + path_concat(op_params->work_dir, header->name, full_extracted_fname); - bool create_res = storage_simply_mkdir(archive->storage, string_get_cstr(fname)); - string_clear(fname); + bool create_res = + storage_simply_mkdir(archive->storage, string_get_cstr(full_extracted_fname)); + string_clear(full_extracted_fname); return create_res ? 0 : -1; } @@ -197,8 +199,16 @@ static int archive_extract_foreach_cb(mtar_t* tar, const mtar_header_t* header, return 0; } - string_init(fname); - path_concat(op_params->work_dir, header->name, fname); + string_init(full_extracted_fname); + + string_t converted_fname; + string_init_set(converted_fname, header->name); + if(op_params->converter) { + op_params->converter(converted_fname); + } + path_concat(op_params->work_dir, string_get_cstr(converted_fname), full_extracted_fname); + string_clear(converted_fname); + FURI_LOG_I(TAG, "Extracting %d bytes to '%s'", header->size, header->name); File* out_file = storage_file_alloc(archive->storage); uint8_t* readbuf = malloc(FILE_BLOCK_SIZE); @@ -208,10 +218,17 @@ static int archive_extract_foreach_cb(mtar_t* tar, const mtar_header_t* header, do { while(n_tries-- > 0) { if(storage_file_open( - out_file, string_get_cstr(fname), FSAM_WRITE, FSOM_CREATE_ALWAYS)) { + out_file, + string_get_cstr(full_extracted_fname), + FSAM_WRITE, + FSOM_CREATE_ALWAYS)) { break; } - FURI_LOG_W(TAG, "Failed to open '%s', reties: %d", string_get_cstr(fname), n_tries); + FURI_LOG_W( + TAG, + "Failed to open '%s', reties: %d", + string_get_cstr(full_extracted_fname), + n_tries); storage_file_close(out_file); furi_delay_ms(FILE_OPEN_RETRY_DELAY); } @@ -232,15 +249,19 @@ static int archive_extract_foreach_cb(mtar_t* tar, const mtar_header_t* header, storage_file_free(out_file); free(readbuf); - string_clear(fname); + string_clear(full_extracted_fname); return failed ? -1 : 0; } -bool tar_archive_unpack_to(TarArchive* archive, const char* destination) { +bool tar_archive_unpack_to( + TarArchive* archive, + const char* destination, + Storage_name_converter converter) { furi_assert(archive); TarArchiveDirectoryOpParams param = { .archive = archive, .work_dir = destination, + .converter = converter, }; FURI_LOG_I(TAG, "Restoring '%s'", destination); @@ -313,6 +334,7 @@ bool tar_archive_add_dir(TarArchive* archive, const char* fs_full_path, const ch string_t element_name, element_fs_abs_path; string_init(element_name); string_init(element_fs_abs_path); + path_concat(fs_full_path, name, element_fs_abs_path); if(strlen(path_prefix)) { path_concat(path_prefix, name, element_name); diff --git a/lib/toolbox/tar/tar_archive.h b/lib/toolbox/tar/tar_archive.h index d9b0f3215..88cb3dd4d 100644 --- a/lib/toolbox/tar/tar_archive.h +++ b/lib/toolbox/tar/tar_archive.h @@ -2,6 +2,8 @@ #include #include +#include +#include #ifdef __cplusplus extern "C" { @@ -24,7 +26,10 @@ bool tar_archive_open(TarArchive* archive, const char* path, TarOpenMode mode); void tar_archive_free(TarArchive* archive); /* High-level API - assumes archive is open */ -bool tar_archive_unpack_to(TarArchive* archive, const char* destination); +bool tar_archive_unpack_to( + TarArchive* archive, + const char* destination, + Storage_name_converter converter); bool tar_archive_add_file( TarArchive* archive, diff --git a/lib/update_util/lfs_backup.c b/lib/update_util/lfs_backup.c index fa9141df6..724da3657 100644 --- a/lib/update_util/lfs_backup.c +++ b/lib/update_util/lfs_backup.c @@ -2,7 +2,37 @@ #include -#define LFS_BACKUP_DEFAULT_LOCATION "/ext/" LFS_BACKUP_DEFAULT_FILENAME +#include +#include +#include +#include +#include +#include + +#define LFS_BACKUP_DEFAULT_LOCATION EXT_PATH(LFS_BACKUP_DEFAULT_FILENAME) + +static void backup_name_converter(string_t filename) { + if(string_empty_p(filename) || (string_get_char(filename, 0) == '.')) { + return; + } + + /* Filenames are already prefixed with '.' */ + const char* const names[] = { + BT_SETTINGS_FILE_NAME, + BT_KEYS_STORAGE_FILE_NAME, + DESKTOP_SETTINGS_FILE_NAME, + NOTIFICATION_SETTINGS_FILE_NAME, + SLIDESHOW_FILE_NAME, + DOLPHIN_STATE_FILE_NAME, + }; + + for(size_t i = 0; i < COUNT_OF(names); i++) { + if(string_equal_str_p(filename, &names[i][1])) { + string_set_str(filename, names[i]); + return; + } + } +} bool lfs_backup_create(Storage* storage, const char* destination) { const char* final_destination = @@ -18,5 +48,5 @@ bool lfs_backup_exists(Storage* storage, const char* source) { bool lfs_backup_unpack(Storage* storage, const char* source) { const char* final_source = source && strlen(source) ? source : LFS_BACKUP_DEFAULT_LOCATION; - return storage_int_restore(storage, final_source) == FSE_OK; + return storage_int_restore(storage, final_source, backup_name_converter) == FSE_OK; } diff --git a/lib/update_util/update_manifest.c b/lib/update_util/update_manifest.c index d1ac0d7d0..1b205d9ce 100644 --- a/lib/update_util/update_manifest.c +++ b/lib/update_util/update_manifest.c @@ -157,14 +157,14 @@ bool update_manifest_has_obdata(UpdateManifest* update_manifest) { } bool update_manifest_init(UpdateManifest* update_manifest, const char* manifest_filename) { - Storage* storage = furi_record_open("storage"); + Storage* storage = furi_record_open(RECORD_STORAGE); FlipperFormat* flipper_file = flipper_format_file_alloc(storage); if(flipper_format_file_open_existing(flipper_file, manifest_filename)) { update_manifest_init_from_ff(update_manifest, flipper_file); } flipper_format_free(flipper_file); - furi_record_close("storage"); + furi_record_close(RECORD_STORAGE); return update_manifest->valid; } diff --git a/lib/update_util/update_manifest.h b/lib/update_util/update_manifest.h index 2b0c91794..8f3859471 100644 --- a/lib/update_util/update_manifest.h +++ b/lib/update_util/update_manifest.h @@ -9,8 +9,7 @@ extern "C" { #include #include -/* Paths don't include /ext -- because at startup SD card is mounted as root */ -#define UPDATE_DIR_DEFAULT_REL_PATH "/update" +/* Paths don't include /ext -- because at startup SD card is mounted as FS root */ #define UPDATE_MANIFEST_DEFAULT_NAME "update.fuf" #define UPDATE_MANIFEST_POINTER_FILE_NAME ".fupdate" diff --git a/lib/update_util/update_operation.c b/lib/update_util/update_operation.c index 9082d2625..138828ff0 100644 --- a/lib/update_util/update_operation.c +++ b/lib/update_util/update_operation.c @@ -9,9 +9,8 @@ #include #include -#define UPDATE_ROOT_DIR "/ext" UPDATE_DIR_DEFAULT_REL_PATH -#define UPDATE_PREFIX "/ext" UPDATE_DIR_DEFAULT_REL_PATH "/" -#define UPDATE_SUFFIX "/" UPDATE_MANIFEST_DEFAULT_NAME +#define UPDATE_ROOT_DIR EXT_PATH("update") + /* Need at least 4 free LFS pages before update */ #define UPDATE_MIN_INT_FREE_SPACE 4 * 4 * 1024 @@ -70,7 +69,7 @@ static bool update_operation_get_current_package_path_rtc(Storage* storage, stri return found; } -#define UPDATE_FILE_POINTER_FN "/ext/" UPDATE_MANIFEST_POINTER_FILE_NAME +#define UPDATE_FILE_POINTER_FN EXT_PATH(UPDATE_MANIFEST_POINTER_FILE_NAME) #define UPDATE_MANIFEST_MAX_PATH_LEN 256u bool update_operation_get_current_package_manifest_path(Storage* storage, string_t out_path) { @@ -137,7 +136,7 @@ static bool update_operation_persist_manifest_path(Storage* storage, const char* UpdatePrepareResult update_operation_prepare(const char* manifest_file_path) { UpdatePrepareResult result = UpdatePrepareResultIntFull; - Storage* storage = furi_record_open("storage"); + Storage* storage = furi_record_open(RECORD_STORAGE); UpdateManifest* manifest = update_manifest_alloc(); File* file = storage_file_alloc(storage); @@ -145,7 +144,8 @@ UpdatePrepareResult update_operation_prepare(const char* manifest_file_path) { string_t stage_path; string_init(stage_path); do { - if((storage_common_fs_info(storage, "/int", NULL, &free_int_space) != FSE_OK) || + if((storage_common_fs_info(storage, STORAGE_INT_PATH_PREFIX, NULL, &free_int_space) != + FSE_OK) || (free_int_space < UPDATE_MIN_INT_FREE_SPACE)) { break; } @@ -197,7 +197,7 @@ UpdatePrepareResult update_operation_prepare(const char* manifest_file_path) { storage_file_free(file); update_manifest_free(manifest); - furi_record_close("storage"); + furi_record_close(RECORD_STORAGE); return result; } @@ -206,10 +206,10 @@ bool update_operation_is_armed() { FuriHalRtcBootMode boot_mode = furi_hal_rtc_get_boot_mode(); const uint32_t rtc_upd_index = furi_hal_rtc_get_register(FuriHalRtcRegisterUpdateFolderFSIndex); - Storage* storage = furi_record_open("storage"); + Storage* storage = furi_record_open(RECORD_STORAGE); const bool upd_fn_ptr_exists = (storage_common_stat(storage, UPDATE_FILE_POINTER_FN, NULL) == FSE_OK); - furi_record_close("storage"); + furi_record_close(RECORD_STORAGE); return (boot_mode >= FuriHalRtcBootModePreUpdate) && (boot_mode <= FuriHalRtcBootModePostUpdate) && ((rtc_upd_index != INT_MAX) || upd_fn_ptr_exists); @@ -218,7 +218,7 @@ bool update_operation_is_armed() { void update_operation_disarm() { furi_hal_rtc_set_boot_mode(FuriHalRtcBootModeNormal); furi_hal_rtc_set_register(FuriHalRtcRegisterUpdateFolderFSIndex, INT_MAX); - Storage* storage = furi_record_open("storage"); + Storage* storage = furi_record_open(RECORD_STORAGE); storage_simply_remove(storage, UPDATE_FILE_POINTER_FN); - furi_record_close("storage"); -} \ No newline at end of file + furi_record_close(RECORD_STORAGE); +} diff --git a/scripts/ReadMe.md b/scripts/ReadMe.md index 86b5b8afe..d06303957 100644 --- a/scripts/ReadMe.md +++ b/scripts/ReadMe.md @@ -58,3 +58,16 @@ Run in the root folder of the repo: ```bash python scripts/storage.py -p send assets/resources /ext ``` + + +# Slideshow creation + +Put fullscreen slideshow frames in .png format into `assets/slideshow/my_show` folder, named frame_xx.png, where xx is zero-padded frame number, starting with #0. + +Then run + +```bash +python scripts/slideshow.py -i assets/slideshow/my_show/ -o assets/slideshow/my_show/.slideshow +``` + +Upload generated .slideshow file to Flipper's internal storage and restart it. \ No newline at end of file diff --git a/scripts/flipper/storage.py b/scripts/flipper/storage.py index 3d1b46b94..5fa8a2c81 100644 --- a/scripts/flipper/storage.py +++ b/scripts/flipper/storage.py @@ -53,13 +53,14 @@ class FlipperStorage: CLI_PROMPT = ">: " CLI_EOL = "\r\n" - def __init__(self, portname: str, portbaud: int = 115200): + def __init__(self, portname: str, chunk_size: int = 8192): self.port = serial.Serial() self.port.port = portname self.port.timeout = 2 - self.port.baudrate = portbaud + self.port.baudrate = 115200 # Doesn't matter for VCP self.read = BufferedRead(self.port) self.last_error = "" + self.chunk_size = chunk_size def start(self): self.port.open() @@ -192,7 +193,7 @@ def send_file(self, filename_from, filename_to): with open(filename_from, "rb") as file: filesize = os.fstat(file.fileno()).st_size - buffer_size = 512 + buffer_size = self.chunk_size while True: filedata = file.read(buffer_size) size = len(filedata) @@ -221,7 +222,7 @@ def send_file(self, filename_from, filename_to): def read_file(self, filename): """Receive file from Flipper, and get filedata (bytes)""" - buffer_size = 512 + buffer_size = self.chunk_size self.send_and_wait_eol( 'storage read_chunks "' + filename + '" ' + str(buffer_size) + "\r" ) @@ -355,7 +356,7 @@ def hash_local(self, filename): """Hash of local file""" hash_md5 = hashlib.md5() with open(filename, "rb") as f: - for chunk in iter(lambda: f.read(4096), b""): + for chunk in iter(lambda: f.read(self.chunk_size), b""): hash_md5.update(chunk) return hash_md5.hexdigest() diff --git a/scripts/selfupdate.py b/scripts/selfupdate.py index 6d7057474..1c16c5ca6 100644 --- a/scripts/selfupdate.py +++ b/scripts/selfupdate.py @@ -14,14 +14,6 @@ class Main(App): def init(self): self.parser.add_argument("-p", "--port", help="CDC Port", default="auto") - self.parser.add_argument( - "-b", - "--baud", - help="Port Baud rate", - required=False, - default=115200 * 4, - type=int, - ) self.parser.add_argument("manifest_path", help="Manifest path") self.parser.add_argument( @@ -64,7 +56,7 @@ def install(self): if not (port := resolve_port(self.logger, self.args.port)): return 1 - storage = FlipperStorage(port, self.args.baud) + storage = FlipperStorage(port) storage.start() try: @@ -99,6 +91,7 @@ def install(self): self.logger.error(f"Error: {storage.last_error}") return -3 + # return -11 storage.send_and_wait_eol( f"update install {flipper_update_path}/{manifest_name}\r" ) diff --git a/scripts/serial_cli.py b/scripts/serial_cli.py new file mode 100644 index 000000000..e07e6bfb4 --- /dev/null +++ b/scripts/serial_cli.py @@ -0,0 +1,14 @@ +import logging +import subprocess +from flipper.utils.cdc import resolve_port + + +def main(): + logger = logging.getLogger() + if not (port := resolve_port(logger, "auto")): + return 1 + subprocess.call(["python3", "-m", "serial.tools.miniterm", "--raw", port, "230400"]) + + +if __name__ == "__main__": + main() diff --git a/scripts/storage.py b/scripts/storage.py index 0ddc2fc0c..167ba88eb 100755 --- a/scripts/storage.py +++ b/scripts/storage.py @@ -14,14 +14,7 @@ class Main(App): def init(self): self.parser.add_argument("-p", "--port", help="CDC Port", default="auto") - self.parser.add_argument( - "-b", - "--baud", - help="Port Baud rate", - required=False, - default=115200 * 4, - type=int, - ) + self.subparsers = self.parser.add_subparsers(help="sub-command help") self.parser_mkdir = self.subparsers.add_parser("mkdir", help="Create directory") @@ -77,7 +70,7 @@ def _get_storage(self): if not (port := resolve_port(self.logger, self.args.port)): return None - storage = FlipperStorage(port, self.args.baud) + storage = FlipperStorage(port) storage.start() return storage diff --git a/scripts/toolchain/fbtenv.cmd b/scripts/toolchain/fbtenv.cmd index 34adb12c1..471ce835c 100644 --- a/scripts/toolchain/fbtenv.cmd +++ b/scripts/toolchain/fbtenv.cmd @@ -13,19 +13,19 @@ if not [%FBT_NOENV%] == [] ( exit /b 0 ) -set "FLIPPER_TOOLCHAIN_VERSION=3" +set "FLIPPER_TOOLCHAIN_VERSION=8" set "FBT_TOOLCHAIN_ROOT=%FBT_ROOT%\toolchain\i686-windows" if not exist "%FBT_TOOLCHAIN_ROOT%" ( - powershell -ExecutionPolicy Bypass -File %FBT_ROOT%\scripts\toolchain\windows-toolchain-download.ps1 "%flipper_toolchain_version%" + powershell -ExecutionPolicy Bypass -File "%FBT_ROOT%\scripts\toolchain\windows-toolchain-download.ps1" "%flipper_toolchain_version%" ) if not exist "%FBT_TOOLCHAIN_ROOT%\VERSION" ( - powershell -ExecutionPolicy Bypass -File %FBT_ROOT%\scripts\toolchain\windows-toolchain-download.ps1 "%flipper_toolchain_version%" + powershell -ExecutionPolicy Bypass -File "%FBT_ROOT%\scripts\toolchain\windows-toolchain-download.ps1" "%flipper_toolchain_version%" ) -set /p REAL_TOOLCHAIN_VERSION=<%FBT_TOOLCHAIN_ROOT%\VERSION +set /p REAL_TOOLCHAIN_VERSION=<"%FBT_TOOLCHAIN_ROOT%\VERSION" if not "%REAL_TOOLCHAIN_VERSION%" == "%FLIPPER_TOOLCHAIN_VERSION%" ( - powershell -ExecutionPolicy Bypass -File %FBT_ROOT%\scripts\toolchain\windows-toolchain-download.ps1 "%flipper_toolchain_version%" + powershell -ExecutionPolicy Bypass -File "%FBT_ROOT%\scripts\toolchain\windows-toolchain-download.ps1" "%flipper_toolchain_version%" ) @@ -40,6 +40,6 @@ if not "%1" == "env" ( echo ********************************* echo * fbt build environment * echo ********************************* - cd %FBT_ROOT% + cd "%FBT_ROOT%" cmd /k ) diff --git a/scripts/toolchain/fbtenv.sh b/scripts/toolchain/fbtenv.sh index 55bc89b07..654b1fe0d 100755 --- a/scripts/toolchain/fbtenv.sh +++ b/scripts/toolchain/fbtenv.sh @@ -1,54 +1,211 @@ #!/bin/sh -# unofficial strict mode -set -eu; +# shellcheck disable=SC2034,SC2016,SC2086 -FLIPPER_TOOLCHAIN_VERSION="3"; +# public variables +DEFAULT_SCRIPT_PATH="$(pwd -P)"; +SCRIPT_PATH="${SCRIPT_PATH:-$DEFAULT_SCRIPT_PATH}"; +FBT_TOOLCHAIN_VERSION="${FBT_TOOLCHAIN_VERSION:-"8"}"; +FBT_TOOLCHAIN_PATH="${FBT_TOOLCHAIN_PATH:-$SCRIPT_PATH}"; -get_kernel_type() +fbtenv_check_sourced() { - SYS_TYPE="$(uname -s)" + case "${ZSH_EVAL_CONTEXT:-""}" in *:file:*) + return 0;; + esac + case ${0##*/} in dash|-dash|bash|-bash|ksh|-ksh|sh|-sh) + return 0;; + esac + if [ "$(basename $0)" = "fbt" ]; then + return 0; + fi + echo "Running this script manually is wrong, please source it"; + echo "Example:"; + printf "\tsource scripts/toolchain/fbtenv.sh\n"; + return 1; +} + +fbtenv_check_script_path() +{ + if [ ! -x "$SCRIPT_PATH/fbt" ]; then + echo "Please source this script being into flipperzero-firmware root directory, or specify 'SCRIPT_PATH' manually"; + echo "Example:"; + printf "\tSCRIPT_PATH=lang/c/flipperzero-firmware source lang/c/flipperzero-firmware/scripts/fbtenv.sh\n"; + echo "If current directory is right, type 'unset SCRIPT_PATH' and try again" + return 1; + fi + return 0; +} + +fbtenv_get_kernel_type() +{ + SYS_TYPE="$(uname -s)"; + ARCH_TYPE="$(uname -m)"; + if [ "$ARCH_TYPE" != "x86_64" ] && [ "$SYS_TYPE" != "Darwin" ]; then + echo "Now we provide toolchain only for x86_64 arhitecture, sorry.."; + return 1; + fi if [ "$SYS_TYPE" = "Darwin" ]; then - TOOLCHAIN_PATH="toolchain/x86_64-darwin"; + fbtenv_check_rosetta || return 1; + TOOLCHAIN_ARCH_DIR="$FBT_TOOLCHAIN_PATH/toolchain/x86_64-darwin"; + TOOLCHAIN_URL="https://update.flipperzero.one/builds/toolchain/gcc-arm-none-eabi-10.3-x86_64-darwin-flipper-$FBT_TOOLCHAIN_VERSION.tar.gz"; elif [ "$SYS_TYPE" = "Linux" ]; then - TOOLCHAIN_PATH="toolchain/x86_64-linux"; + TOOLCHAIN_ARCH_DIR="$FBT_TOOLCHAIN_PATH/toolchain/x86_64-linux"; + TOOLCHAIN_URL="https://update.flipperzero.one/builds/toolchain/gcc-arm-none-eabi-10.3-x86_64-linux-flipper-$FBT_TOOLCHAIN_VERSION.tar.gz"; elif echo "$SYS_TYPE" | grep -q "MINGW"; then echo "In MinGW shell use \"fbt.cmd\" instead of \"fbt\""; - exit 1; + return 1; else - echo "Sorry, your system is not supported. Please report your configuration to us."; - exit 1; + echo "Your system is not recognized. Sorry.. Please report us your configuration."; + return 1; fi + return 0; } -check_download_toolchain() +fbtenv_check_rosetta() { - if [ ! -d "$SCRIPT_PATH/$TOOLCHAIN_PATH" ]; then - download_toolchain; - elif [ ! -f "$SCRIPT_PATH/$TOOLCHAIN_PATH/VERSION" ]; then - download_toolchain; - elif [ "$(cat "$SCRIPT_PATH/$TOOLCHAIN_PATH/VERSION")" -ne "$FLIPPER_TOOLCHAIN_VERSION" ]; then - download_toolchain; + if [ "$ARCH_TYPE" = "arm64" ]; then + if ! /usr/bin/pgrep -q oahd; then + echo "Flipper Zero Toolchain needs Rosetta2 to run under Apple Silicon"; + echo "Please instal it by typing 'softwareupdate --install-rosetta --agree-to-license'"; + return 1; + fi fi + return 0; } -download_toolchain() +fbtenv_check_tar() { - chmod 755 "$SCRIPT_PATH/scripts/toolchain/unix-toolchain-download.sh"; - "$SCRIPT_PATH/scripts/toolchain/unix-toolchain-download.sh" "$FLIPPER_TOOLCHAIN_VERSION" || exit 1; + printf "Checking tar.."; + if ! tar --version > /dev/null 2>&1; then + echo "no"; + return 1; + fi + echo "yes"; + return 0; } -main() +fbtenv_check_downloaded_toolchain() { - if [ -z "${SCRIPT_PATH:-}" ]; then - echo "Manual running of this script is not allowed."; - exit 1; + printf "Checking downloaded toolchain tgz.."; + if [ ! -f "$FBT_TOOLCHAIN_PATH/toolchain/$TOOLCHAIN_TAR" ]; then + echo "no"; + return 1; fi - get_kernel_type; # sets TOOLCHAIN_PATH - check_download_toolchain; - PATH="$SCRIPT_PATH/$TOOLCHAIN_PATH/python/bin:$PATH"; - PATH="$SCRIPT_PATH/$TOOLCHAIN_PATH/bin:$PATH"; - PATH="$SCRIPT_PATH/$TOOLCHAIN_PATH/protobuf/bin:$PATH"; - PATH="$SCRIPT_PATH/$TOOLCHAIN_PATH/openocd/bin:$PATH"; + echo "yes"; + return 0; } -main; + +fbtenv_download_toolchain_tar() +{ + echo "Downloading toolchain:"; + mkdir -p "$FBT_TOOLCHAIN_PATH/toolchain" || return 1; + "$DOWNLOADER" $DOWNLOADER_ARGS "$FBT_TOOLCHAIN_PATH/toolchain/$TOOLCHAIN_TAR" "$TOOLCHAIN_URL" || return 1; + echo "done"; + return 0; +} + +fbtenv_remove_old_tooclhain() +{ + printf "Removing old toolchain (if exist).."; + rm -rf "${TOOLCHAIN_ARCH_DIR}"; + echo "done"; +} + +fbtenv_show_unpack_percentage() +{ + LINE=0; + while read -r line; do + LINE=$(( LINE + 1 )); + if [ $(( LINE % 300 )) -eq 0 ]; then + printf "#"; + fi + done + echo " 100.0%"; +} + +fbtenv_unpack_toolchain() +{ + echo "Unpacking toolchain:"; + tar -xvf "$FBT_TOOLCHAIN_PATH/toolchain/$TOOLCHAIN_TAR" -C "$FBT_TOOLCHAIN_PATH/toolchain" 2>&1 | fbtenv_show_unpack_percentage; + mkdir -p "$FBT_TOOLCHAIN_PATH/toolchain" || return 1; + mv "$FBT_TOOLCHAIN_PATH/toolchain/$TOOLCHAIN_DIR" "$TOOLCHAIN_ARCH_DIR" || return 1; + echo "done"; + return 0; +} + +fbtenv_clearing() +{ + printf "Clearing.."; + rm -rf "${FBT_TOOLCHAIN_PATH:?}/toolchain/$TOOLCHAIN_TAR"; + echo "done"; + return 0; +} + +fbtenv_curl_wget_check() +{ + printf "Checking curl.."; + if ! curl --version > /dev/null 2>&1; then + echo "no"; + printf "Checking wget.."; + if ! wget --version > /dev/null 2>&1; then + echo "no"; + echo "No curl or wget found in your PATH"; + echo "Please provide it or download this file:"; + echo; + echo "$TOOLCHAIN_URL"; + echo; + echo "And place in $FBT_TOOLCHAIN_PATH/toolchain/ dir mannualy"; + return 1; + fi + echo "yes" + DOWNLOADER="wget"; + DOWNLOADER_ARGS="--show-progress --progress=bar:force -qO"; + return 0; + fi + echo "yes" + DOWNLOADER="curl"; + DOWNLOADER_ARGS="--progress-bar -SLo"; + return 0; +} + +fbtenv_check_download_toolchain() +{ + if [ ! -d "$TOOLCHAIN_ARCH_DIR" ]; then + fbtenv_download_toolchain || return 1; + elif [ ! -f "$TOOLCHAIN_ARCH_DIR/VERSION" ]; then + fbtenv_download_toolchain || return 1; + elif [ "$(cat "$TOOLCHAIN_ARCH_DIR/VERSION")" -ne "$FBT_TOOLCHAIN_VERSION" ]; then + fbtenv_download_toolchain || return 1; + fi + return 0; +} + +fbtenv_download_toolchain() +{ + fbtenv_check_tar || return 1; + TOOLCHAIN_TAR="$(basename "$TOOLCHAIN_URL")"; + TOOLCHAIN_DIR="$(echo "$TOOLCHAIN_TAR" | sed "s/-$FBT_TOOLCHAIN_VERSION.tar.gz//g")"; + if ! fbtenv_check_downloaded_toolchain; then + fbtenv_curl_wget_check || return 1; + fbtenv_download_toolchain_tar; + fi + fbtenv_remove_old_tooclhain; + fbtenv_unpack_toolchain || { fbtenv_clearing && return 1; }; + fbtenv_clearing; + return 0; +} + +fbtenv_main() +{ + fbtenv_check_sourced || return 1; + fbtenv_check_script_path || return 1; + fbtenv_get_kernel_type || return 1; + fbtenv_check_download_toolchain || return 1; + PATH="$TOOLCHAIN_ARCH_DIR/python/bin:$PATH"; + PATH="$TOOLCHAIN_ARCH_DIR/bin:$PATH"; + PATH="$TOOLCHAIN_ARCH_DIR/protobuf/bin:$PATH"; + PATH="$TOOLCHAIN_ARCH_DIR/openocd/bin:$PATH"; +} + +fbtenv_main; diff --git a/scripts/toolchain/unix-toolchain-download.sh b/scripts/toolchain/unix-toolchain-download.sh deleted file mode 100755 index 386be2a3c..000000000 --- a/scripts/toolchain/unix-toolchain-download.sh +++ /dev/null @@ -1,135 +0,0 @@ -#!/bin/sh -# shellcheck disable=SC2086,SC2034 - -# unofficial strict mode -set -eu; - -check_system() -{ - VER="$1"; # toolchain version - printf "Checking kernel type.."; - SYS_TYPE="$(uname -s)" - if [ "$SYS_TYPE" = "Darwin" ]; then - echo "darwin"; - TOOLCHAIN_URL="https://update.flipperzero.one/builds/toolchain/gcc-arm-none-eabi-10.3-x86_64-darwin-flipper-$VER.tar.gz"; - TOOLCHAIN_PATH="toolchain/x86_64-darwin"; - elif [ "$SYS_TYPE" = "Linux" ]; then - echo "linux"; - TOOLCHAIN_URL="https://update.flipperzero.one/builds/toolchain/gcc-arm-none-eabi-10.3-x86_64-linux-flipper-$VER.tar.gz"; - TOOLCHAIN_PATH="toolchain/x86_64-linux"; - else - echo "unsupported."; - echo "Your system is unsupported.. sorry.."; - exit 1; - fi -} - -check_tar() -{ - printf "Checking tar.."; - if ! tar --version > /dev/null 2>&1; then - echo "no"; - exit 1; - fi - echo "yes"; -} - - -curl_wget_check() -{ - printf "Checking curl.."; - if ! curl --version > /dev/null 2>&1; then - echo "no"; - printf "Checking wget.."; - if ! wget --version > /dev/null 2>&1; then - echo "no"; - echo "No curl or wget found in your PATH."; - echo "Please provide it or download this file:"; - echo; - echo "$TOOLCHAIN_URL"; - echo; - echo "And place in repo root dir mannualy."; - exit 1; - fi - echo "yes" - DOWNLOADER="wget"; - DOWNLOADER_ARGS="--show-progress --progress=bar:force -qO"; - return; - fi - echo "yes" - DOWNLOADER="curl"; - DOWNLOADER_ARGS="--progress-bar -SLo"; -} - -check_downloaded_toolchain() -{ - printf "Checking downloaded toolchain tgz.."; - if [ -f "$REPO_ROOT/$TOOLCHAIN_TAR" ]; then - echo "yes"; - return 0; - fi - echo "no"; - return 1; -} - -download_toolchain() -{ - echo "Downloading toolchain:"; - "$DOWNLOADER" $DOWNLOADER_ARGS "$REPO_ROOT/$TOOLCHAIN_TAR" "$TOOLCHAIN_URL"; - echo "done"; -} - -remove_old_tooclhain() -{ - printf "Removing old toolchain (if exist).."; - rm -rf "${REPO_ROOT:?}/$TOOLCHAIN_PATH"; - echo "done"; -} - -show_unpack_percentage() -{ - LINE=0; - while read -r line; do - LINE=$(( LINE + 1 )); - if [ $(( LINE % 300 )) -eq 0 ]; then - printf "#"; - fi - done - echo " 100.0%"; -} - -unpack_toolchain() -{ - echo "Unpacking toolchain:"; - tar -xvf "$REPO_ROOT/$TOOLCHAIN_TAR" -C "$REPO_ROOT/" 2>&1 | show_unpack_percentage; - mkdir -p "$REPO_ROOT/toolchain"; - mv "$REPO_ROOT/$TOOLCHAIN_DIR" "$REPO_ROOT/$TOOLCHAIN_PATH/"; - echo "done"; -} - -clearing() -{ - printf "Clearing.."; - rm -rf "${REPO_ROOT:?}/$TOOLCHAIN_TAR"; - echo "done"; -} - -main() -{ - SCRIPT_PATH="$(cd "$(dirname "$0")" && pwd -P)" - REPO_ROOT="$(cd "$SCRIPT_PATH/../../" && pwd)"; - check_system "$1"; # recives TOOLCHAIN_VERSION, defines TOOLCHAIN_URL and TOOLCHAIN_PATH - check_tar; - TOOLCHAIN_TAR="$(basename "$TOOLCHAIN_URL")"; - TOOLCHAIN_DIR="$(echo "$TOOLCHAIN_TAR" | sed "s/-$VER.tar.gz//g")"; - if ! check_downloaded_toolchain; then - curl_wget_check; - download_toolchain; - fi - remove_old_tooclhain; - unpack_toolchain; -} - -trap clearing EXIT; -trap clearing 2; # SIGINT not coverable by EXIT -main "$1"; # toochain version diff --git a/site_scons/site_tools/fbt_apps.py b/site_scons/site_tools/fbt_apps.py index 1c2e0167a..2cd63b70b 100644 --- a/site_scons/site_tools/fbt_apps.py +++ b/site_scons/site_tools/fbt_apps.py @@ -1,8 +1,9 @@ from SCons.Builder import Builder from SCons.Action import Action -from SCons.Errors import UserError - +from SCons.Warnings import warn, WarningOnByDefault import SCons +import os.path + from fbt.appmanifest import ( FlipperAppType, AppManager, @@ -17,12 +18,14 @@ def LoadApplicationManifests(env): appmgr = env["APPMGR"] = AppManager() - for entry in env.Glob("#/applications/*"): + for entry in env.Glob("#/applications/*", ondisk=True, source=True): if isinstance(entry, SCons.Node.FS.Dir) and not str(entry).startswith("."): try: - appmgr.load_manifest(entry.File("application.fam").abspath, entry.name) + app_manifest_file_path = os.path.join(entry.abspath, "application.fam") + appmgr.load_manifest(app_manifest_file_path, entry.name) + env.Append(PY_LINT_SOURCES=[app_manifest_file_path]) except FlipperManifestException as e: - raise UserError(e) + warn(WarningOnByDefault, str(e)) def PrepareApplicationsBuild(env): @@ -64,6 +67,7 @@ def generate(env): build_apps_c, "${APPSCOMSTR}", ), + suffix=".c", ), } ) diff --git a/site_scons/site_tools/fbt_assets.py b/site_scons/site_tools/fbt_assets.py index 0c2ce6f79..c844db36b 100644 --- a/site_scons/site_tools/fbt_assets.py +++ b/site_scons/site_tools/fbt_assets.py @@ -109,14 +109,14 @@ def generate(env): BUILDERS={ "IconBuilder": Builder( action=Action( - "${PYTHON3} ${ASSETS_COMPILER} icons ${SOURCE.posix} ${TARGET.dir.posix}", + '${PYTHON3} "${ASSETS_COMPILER}" icons ${SOURCE.posix} ${TARGET.dir.posix}', "${ICONSCOMSTR}", ), emitter=icons_emitter, ), "ProtoBuilder": Builder( action=Action( - "${PYTHON3} ${NANOPB_COMPILER} -q -I${SOURCE.dir.posix} -D${TARGET.dir.posix} ${SOURCES.posix}", + '${PYTHON3} "${NANOPB_COMPILER}" -q -I${SOURCE.dir.posix} -D${TARGET.dir.posix} ${SOURCES.posix}', "${PROTOCOMSTR}", ), emitter=proto_emitter, @@ -125,14 +125,14 @@ def generate(env): ), "DolphinSymBuilder": Builder( action=Action( - '${PYTHON3} ${ASSETS_COMPILER} dolphin -s dolphin_${DOLPHIN_RES_TYPE} "${SOURCE}" "${_DOLPHIN_OUT_DIR}"', + '${PYTHON3} "${ASSETS_COMPILER}" dolphin -s dolphin_${DOLPHIN_RES_TYPE} "${SOURCE}" "${_DOLPHIN_OUT_DIR}"', "${DOLPHINCOMSTR}", ), emitter=dolphin_emitter, ), "DolphinExtBuilder": Builder( action=Action( - '${PYTHON3} ${ASSETS_COMPILER} dolphin "${SOURCE}" "${_DOLPHIN_OUT_DIR}"', + '${PYTHON3} "${ASSETS_COMPILER}" dolphin "${SOURCE}" "${_DOLPHIN_OUT_DIR}"', "${DOLPHINCOMSTR}", ), emitter=dolphin_emitter, diff --git a/site_scons/site_tools/fbt_dist.py b/site_scons/site_tools/fbt_dist.py index 15a653a60..399b7ecdd 100644 --- a/site_scons/site_tools/fbt_dist.py +++ b/site_scons/site_tools/fbt_dist.py @@ -66,15 +66,44 @@ def AddOpenOCDFlashTarget(env, targetenv, **kw): **kw, ) env.Alias(targetenv.subst("${FIRMWARE_BUILD_CFG}_flash"), openocd_target) + if env["FORCE"]: + env.AlwaysBuild(openocd_target) return openocd_target +def AddJFlashTarget(env, targetenv, **kw): + jflash_target = env.JFlash( + "#build/jflash-${BUILD_CFG}-flash.flag", + targetenv["FW_BIN"], + JFLASHADDR=targetenv.subst("$IMAGE_BASE_ADDRESS"), + BUILD_CFG=targetenv.subst("${FIRMWARE_BUILD_CFG}"), + **kw, + ) + env.Alias(targetenv.subst("${FIRMWARE_BUILD_CFG}_jflash"), jflash_target) + if env["FORCE"]: + env.AlwaysBuild(jflash_target) + return jflash_target + + +def AddUsbFlashTarget(env, file_flag, extra_deps, **kw): + usb_update = env.UsbInstall( + file_flag, + ( + env["DIST_DEPENDS"], + *extra_deps, + ), + ) + if env["FORCE"]: + env.AlwaysBuild(usb_update) + return usb_update + + def DistCommand(env, name, source, **kw): target = f"dist_{name}" command = env.Command( target, source, - '@${PYTHON3} ${ROOT_DIR.abspath}/scripts/sconsdist.py copy -p ${DIST_PROJECTS} -s "${DIST_SUFFIX}" ${DIST_EXTRA}', + '@${PYTHON3} "${ROOT_DIR.abspath}/scripts/sconsdist.py" copy -p ${DIST_PROJECTS} -s "${DIST_SUFFIX}" ${DIST_EXTRA}', **kw, ) env.Pseudo(target) @@ -86,6 +115,8 @@ def generate(env): env.AddMethod(AddFwProject) env.AddMethod(DistCommand) env.AddMethod(AddOpenOCDFlashTarget) + env.AddMethod(AddJFlashTarget) + env.AddMethod(AddUsbFlashTarget) env.SetDefault( COPRO_MCU_FAMILY="STM32WB5x", @@ -96,7 +127,7 @@ def generate(env): "UsbInstall": Builder( action=[ Action( - "${PYTHON3} ${ROOT_DIR.abspath}/scripts/selfupdate.py dist/${DIST_DIR}/f${TARGET_HW}-update-${DIST_SUFFIX}/update.fuf" + '${PYTHON3} "${ROOT_DIR.abspath}/scripts/selfupdate.py" dist/${DIST_DIR}/f${TARGET_HW}-update-${DIST_SUFFIX}/update.fuf' ), Touch("${TARGET}"), ] @@ -105,7 +136,7 @@ def generate(env): action=Action( [ Mkdir("$TARGET"), - "${PYTHON3} ${ROOT_DIR.abspath}/scripts/assets.py " + '${PYTHON3} "${ROOT_DIR.abspath}/scripts/assets.py" ' "copro ${COPRO_CUBE_DIR} " "${TARGET} ${COPRO_MCU_FAMILY} " "--cube_ver=${COPRO_CUBE_VERSION} " diff --git a/site_scons/site_tools/fbt_version.py b/site_scons/site_tools/fbt_version.py index 5073c55d9..909eea4f3 100644 --- a/site_scons/site_tools/fbt_version.py +++ b/site_scons/site_tools/fbt_version.py @@ -16,7 +16,7 @@ def generate(env): BUILDERS={ "VersionBuilder": Builder( action=Action( - "${PYTHON3} ${ROOT_DIR.abspath}/scripts/version.py generate -t ${TARGET_HW} -o ${TARGET.dir.posix} --dir ${ROOT_DIR}", + '${PYTHON3} "${ROOT_DIR.abspath}/scripts/version.py" generate -t ${TARGET_HW} -o ${TARGET.dir.posix} --dir "${ROOT_DIR}"', "${VERSIONCOMSTR}", ), emitter=version_emitter, diff --git a/site_scons/site_tools/fwbin.py b/site_scons/site_tools/fwbin.py index 848571e34..37e64e56d 100644 --- a/site_scons/site_tools/fwbin.py +++ b/site_scons/site_tools/fwbin.py @@ -30,7 +30,7 @@ def generate(env): ), "DFUBuilder": Builder( action=Action( - '${PYTHON3} ${BIN2DFU} -i "${SOURCE}" -o "${TARGET}" -a ${IMAGE_BASE_ADDRESS} -l "Flipper Zero F${TARGET_HW}"', + '${PYTHON3} "${BIN2DFU}" -i "${SOURCE}" -o "${TARGET}" -a ${IMAGE_BASE_ADDRESS} -l "Flipper Zero F${TARGET_HW}"', "${DFUCOMSTR}", ), suffix=".dfu", diff --git a/site_scons/site_tools/jflash.py b/site_scons/site_tools/jflash.py new file mode 100644 index 000000000..aea7279b6 --- /dev/null +++ b/site_scons/site_tools/jflash.py @@ -0,0 +1,27 @@ +from SCons.Builder import Builder +from SCons.Defaults import Touch + + +def generate(env): + env.SetDefault( + JFLASH="JFlash" if env.subst("$PLATFORM") == "win32" else "JFlashExe", + JFLASHFLAGS=[ + "-auto", + "-exit", + ], + JFLASHCOM="${JFLASH} -openprj${JFLASHPROJECT} -open${SOURCE},${JFLASHADDR} ${JFLASHFLAGS}", + ) + env.Append( + BUILDERS={ + "JFlash": Builder( + action=[ + "${JFLASHCOM}", + Touch("${TARGET}"), + ], + ), + } + ) + + +def exists(env): + return True diff --git a/site_scons/site_tools/sconsmodular.py b/site_scons/site_tools/sconsmodular.py index db0cb8f35..778c664e4 100644 --- a/site_scons/site_tools/sconsmodular.py +++ b/site_scons/site_tools/sconsmodular.py @@ -1,5 +1,6 @@ import posixpath import os +from SCons.Errors import UserError def BuildModule(env, module): @@ -8,9 +9,9 @@ def BuildModule(env, module): if not os.path.exists(module_sconscript): module_sconscript = posixpath.join(src_dir, f"{module}.scons") if not os.path.exists(module_sconscript): - print(f"Cannot build module {module}: scons file not found") - Exit(2) + raise UserError(f"Cannot build module {module}: scons file not found") + env.Append(PY_LINT_SOURCES=[module_sconscript]) return env.SConscript( module_sconscript, variant_dir=posixpath.join(env.subst("$BUILD_DIR"), module),