From 0adad32418decc8d19c39e80ce5c42795d141b1e Mon Sep 17 00:00:00 2001 From: hedger Date: Wed, 26 Oct 2022 02:15:02 +0400 Subject: [PATCH 01/31] fbt fixes pt4 (#1899) * fbt: fixed py scripts for gdb * fbt: removed compiled dolphin assets from tracked files; resolved cached dependency issues by globally disabling deps cache; changed dependency tracking for dolphin assets * fbt: fix for "resources" node lookup * toolchain: bump to v.16 with scons + x64 win binaries * fbt: using scons from toolchain * vscode: fixed paths for 64-bit Windows toolchain * fbt: added colors! * fbt: moved import validator to ansi lib coloring * fbt: moved COMSTR vars to tools * fbt: custom action for fap dist * fbt: added OPENOCD_ADAPTER_SERIAL configuration var for openocd operations * fbt: added get_stlink target * docs: details on libs for faps * vscode: added DAP config for using Flipper as a debugger for a 2nd Flipper * fbt: blind deps fix for sdk_origin * fbt: sdk: moved deployment actions to pure python * Github: disable disableLicenseExpirationCheck option for pvs Co-authored-by: Aleksandr Kutuzov --- .github/workflows/pvs_studio.yml | 1 - .gitmodules | 3 - .vscode/example/c_cpp_properties.json | 2 +- .vscode/example/launch.json | 19 +++ .vscode/example/settings.json | 6 +- SConstruct | 16 ++- assets/.gitignore | 3 +- assets/SConscript | 6 +- .../dolphin/L1_Boxing_128x64/frame_0.bm | Bin 481 -> 0 bytes .../dolphin/L1_Boxing_128x64/frame_1.bm | Bin 461 -> 0 bytes .../dolphin/L1_Boxing_128x64/frame_2.bm | Bin 531 -> 0 bytes .../dolphin/L1_Boxing_128x64/frame_3.bm | Bin 437 -> 0 bytes .../dolphin/L1_Boxing_128x64/frame_4.bm | Bin 459 -> 0 bytes .../dolphin/L1_Boxing_128x64/frame_5.bm | Bin 450 -> 0 bytes .../dolphin/L1_Boxing_128x64/frame_6.bm | Bin 442 -> 0 bytes .../dolphin/L1_Boxing_128x64/meta.txt | 32 ----- .../dolphin/L1_Cry_128x64/frame_0.bm | Bin 889 -> 0 bytes .../dolphin/L1_Cry_128x64/frame_1.bm | Bin 911 -> 0 bytes .../dolphin/L1_Cry_128x64/frame_2.bm | Bin 910 -> 0 bytes .../dolphin/L1_Cry_128x64/frame_3.bm | Bin 923 -> 0 bytes .../dolphin/L1_Cry_128x64/frame_4.bm | Bin 894 -> 0 bytes .../dolphin/L1_Cry_128x64/frame_5.bm | Bin 940 -> 0 bytes .../dolphin/L1_Cry_128x64/frame_6.bm | Bin 915 -> 0 bytes .../dolphin/L1_Cry_128x64/frame_7.bm | Bin 934 -> 0 bytes .../resources/dolphin/L1_Cry_128x64/meta.txt | 41 ------ .../dolphin/L1_Furippa1_128x64/frame_0.bm | Bin 294 -> 0 bytes .../dolphin/L1_Furippa1_128x64/frame_1.bm | Bin 325 -> 0 bytes .../dolphin/L1_Furippa1_128x64/frame_10.bm | Bin 465 -> 0 bytes .../dolphin/L1_Furippa1_128x64/frame_11.bm | Bin 698 -> 0 bytes .../dolphin/L1_Furippa1_128x64/frame_12.bm | Bin 541 -> 0 bytes .../dolphin/L1_Furippa1_128x64/frame_13.bm | Bin 584 -> 0 bytes .../dolphin/L1_Furippa1_128x64/frame_14.bm | Bin 610 -> 0 bytes .../dolphin/L1_Furippa1_128x64/frame_15.bm | Bin 719 -> 0 bytes .../dolphin/L1_Furippa1_128x64/frame_16.bm | Bin 458 -> 0 bytes .../dolphin/L1_Furippa1_128x64/frame_17.bm | Bin 400 -> 0 bytes .../dolphin/L1_Furippa1_128x64/frame_18.bm | Bin 333 -> 0 bytes .../dolphin/L1_Furippa1_128x64/frame_2.bm | Bin 351 -> 0 bytes .../dolphin/L1_Furippa1_128x64/frame_3.bm | Bin 324 -> 0 bytes .../dolphin/L1_Furippa1_128x64/frame_4.bm | Bin 387 -> 0 bytes .../dolphin/L1_Furippa1_128x64/frame_5.bm | Bin 390 -> 0 bytes .../dolphin/L1_Furippa1_128x64/frame_6.bm | Bin 407 -> 0 bytes .../dolphin/L1_Furippa1_128x64/frame_7.bm | Bin 294 -> 0 bytes .../dolphin/L1_Furippa1_128x64/frame_8.bm | Bin 283 -> 0 bytes .../dolphin/L1_Furippa1_128x64/frame_9.bm | Bin 312 -> 0 bytes .../dolphin/L1_Furippa1_128x64/meta.txt | 14 -- .../dolphin/L1_Laptop_128x51/frame_0.bm | Bin 555 -> 0 bytes .../dolphin/L1_Laptop_128x51/frame_1.bm | Bin 557 -> 0 bytes .../dolphin/L1_Laptop_128x51/frame_2.bm | Bin 560 -> 0 bytes .../dolphin/L1_Laptop_128x51/frame_3.bm | Bin 556 -> 0 bytes .../dolphin/L1_Laptop_128x51/frame_4.bm | Bin 560 -> 0 bytes .../dolphin/L1_Laptop_128x51/frame_5.bm | Bin 554 -> 0 bytes .../dolphin/L1_Laptop_128x51/frame_6.bm | Bin 553 -> 0 bytes .../dolphin/L1_Laptop_128x51/frame_7.bm | Bin 560 -> 0 bytes .../dolphin/L1_Laptop_128x51/meta.txt | 32 ----- .../dolphin/L1_Leaving_sad_128x64/frame_0.bm | Bin 514 -> 0 bytes .../dolphin/L1_Leaving_sad_128x64/frame_1.bm | Bin 526 -> 0 bytes .../dolphin/L1_Leaving_sad_128x64/frame_10.bm | Bin 316 -> 0 bytes .../dolphin/L1_Leaving_sad_128x64/frame_11.bm | Bin 294 -> 0 bytes .../dolphin/L1_Leaving_sad_128x64/frame_12.bm | Bin 322 -> 0 bytes .../dolphin/L1_Leaving_sad_128x64/frame_2.bm | Bin 542 -> 0 bytes .../dolphin/L1_Leaving_sad_128x64/frame_3.bm | Bin 557 -> 0 bytes .../dolphin/L1_Leaving_sad_128x64/frame_4.bm | Bin 488 -> 0 bytes .../dolphin/L1_Leaving_sad_128x64/frame_5.bm | Bin 469 -> 0 bytes .../dolphin/L1_Leaving_sad_128x64/frame_6.bm | Bin 499 -> 0 bytes .../dolphin/L1_Leaving_sad_128x64/frame_7.bm | Bin 486 -> 0 bytes .../dolphin/L1_Leaving_sad_128x64/frame_8.bm | Bin 403 -> 0 bytes .../dolphin/L1_Leaving_sad_128x64/frame_9.bm | Bin 317 -> 0 bytes .../dolphin/L1_Leaving_sad_128x64/meta.txt | 32 ----- .../dolphin/L1_Mad_fist_128x64/frame_0.bm | Bin 520 -> 0 bytes .../dolphin/L1_Mad_fist_128x64/frame_1.bm | Bin 540 -> 0 bytes .../dolphin/L1_Mad_fist_128x64/frame_10.bm | Bin 542 -> 0 bytes .../dolphin/L1_Mad_fist_128x64/frame_11.bm | Bin 505 -> 0 bytes .../dolphin/L1_Mad_fist_128x64/frame_12.bm | Bin 501 -> 0 bytes .../dolphin/L1_Mad_fist_128x64/frame_13.bm | Bin 500 -> 0 bytes .../dolphin/L1_Mad_fist_128x64/frame_2.bm | Bin 515 -> 0 bytes .../dolphin/L1_Mad_fist_128x64/frame_3.bm | Bin 538 -> 0 bytes .../dolphin/L1_Mad_fist_128x64/frame_4.bm | Bin 512 -> 0 bytes .../dolphin/L1_Mad_fist_128x64/frame_5.bm | Bin 519 -> 0 bytes .../dolphin/L1_Mad_fist_128x64/frame_6.bm | Bin 524 -> 0 bytes .../dolphin/L1_Mad_fist_128x64/frame_7.bm | Bin 515 -> 0 bytes .../dolphin/L1_Mad_fist_128x64/frame_8.bm | Bin 517 -> 0 bytes .../dolphin/L1_Mad_fist_128x64/frame_9.bm | Bin 526 -> 0 bytes .../dolphin/L1_Mad_fist_128x64/meta.txt | 23 ---- .../dolphin/L1_Painting_128x64/frame_0.bm | Bin 763 -> 0 bytes .../dolphin/L1_Painting_128x64/frame_1.bm | Bin 764 -> 0 bytes .../dolphin/L1_Painting_128x64/frame_10.bm | Bin 772 -> 0 bytes .../dolphin/L1_Painting_128x64/frame_11.bm | Bin 767 -> 0 bytes .../dolphin/L1_Painting_128x64/frame_2.bm | Bin 762 -> 0 bytes .../dolphin/L1_Painting_128x64/frame_3.bm | Bin 759 -> 0 bytes .../dolphin/L1_Painting_128x64/frame_4.bm | Bin 759 -> 0 bytes .../dolphin/L1_Painting_128x64/frame_5.bm | Bin 757 -> 0 bytes .../dolphin/L1_Painting_128x64/frame_6.bm | Bin 785 -> 0 bytes .../dolphin/L1_Painting_128x64/frame_7.bm | Bin 803 -> 0 bytes .../dolphin/L1_Painting_128x64/frame_8.bm | Bin 797 -> 0 bytes .../dolphin/L1_Painting_128x64/frame_9.bm | Bin 777 -> 0 bytes .../dolphin/L1_Painting_128x64/meta.txt | 32 ----- .../dolphin/L1_Read_books_128x64/frame_0.bm | Bin 653 -> 0 bytes .../dolphin/L1_Read_books_128x64/frame_1.bm | Bin 653 -> 0 bytes .../dolphin/L1_Read_books_128x64/frame_2.bm | Bin 650 -> 0 bytes .../dolphin/L1_Read_books_128x64/frame_3.bm | Bin 646 -> 0 bytes .../dolphin/L1_Read_books_128x64/frame_4.bm | Bin 650 -> 0 bytes .../dolphin/L1_Read_books_128x64/frame_5.bm | Bin 652 -> 0 bytes .../dolphin/L1_Read_books_128x64/frame_6.bm | Bin 646 -> 0 bytes .../dolphin/L1_Read_books_128x64/frame_7.bm | Bin 647 -> 0 bytes .../dolphin/L1_Read_books_128x64/frame_8.bm | Bin 643 -> 0 bytes .../dolphin/L1_Read_books_128x64/meta.txt | 23 ---- .../dolphin/L1_Recording_128x51/frame_0.bm | Bin 663 -> 0 bytes .../dolphin/L1_Recording_128x51/frame_1.bm | Bin 657 -> 0 bytes .../dolphin/L1_Recording_128x51/frame_10.bm | Bin 629 -> 0 bytes .../dolphin/L1_Recording_128x51/frame_11.bm | Bin 659 -> 0 bytes .../dolphin/L1_Recording_128x51/frame_2.bm | Bin 628 -> 0 bytes .../dolphin/L1_Recording_128x51/frame_3.bm | Bin 654 -> 0 bytes .../dolphin/L1_Recording_128x51/frame_4.bm | Bin 662 -> 0 bytes .../dolphin/L1_Recording_128x51/frame_5.bm | Bin 622 -> 0 bytes .../dolphin/L1_Recording_128x51/frame_6.bm | Bin 664 -> 0 bytes .../dolphin/L1_Recording_128x51/frame_7.bm | Bin 626 -> 0 bytes .../dolphin/L1_Recording_128x51/frame_8.bm | Bin 663 -> 0 bytes .../dolphin/L1_Recording_128x51/frame_9.bm | Bin 661 -> 0 bytes .../dolphin/L1_Recording_128x51/meta.txt | 14 -- .../dolphin/L1_Sleep_128x64/frame_0.bm | Bin 580 -> 0 bytes .../dolphin/L1_Sleep_128x64/frame_1.bm | Bin 589 -> 0 bytes .../dolphin/L1_Sleep_128x64/frame_2.bm | Bin 582 -> 0 bytes .../dolphin/L1_Sleep_128x64/frame_3.bm | Bin 597 -> 0 bytes .../dolphin/L1_Sleep_128x64/meta.txt | 41 ------ .../dolphin/L1_Waves_128x50/frame_0.bm | Bin 443 -> 0 bytes .../dolphin/L1_Waves_128x50/frame_1.bm | Bin 448 -> 0 bytes .../dolphin/L1_Waves_128x50/frame_2.bm | Bin 463 -> 0 bytes .../dolphin/L1_Waves_128x50/frame_3.bm | Bin 472 -> 0 bytes .../dolphin/L1_Waves_128x50/meta.txt | 50 -------- .../dolphin/L2_Furippa2_128x64/frame_0.bm | Bin 350 -> 0 bytes .../dolphin/L2_Furippa2_128x64/frame_1.bm | Bin 385 -> 0 bytes .../dolphin/L2_Furippa2_128x64/frame_10.bm | Bin 465 -> 0 bytes .../dolphin/L2_Furippa2_128x64/frame_11.bm | Bin 698 -> 0 bytes .../dolphin/L2_Furippa2_128x64/frame_12.bm | Bin 541 -> 0 bytes .../dolphin/L2_Furippa2_128x64/frame_13.bm | Bin 584 -> 0 bytes .../dolphin/L2_Furippa2_128x64/frame_14.bm | Bin 610 -> 0 bytes .../dolphin/L2_Furippa2_128x64/frame_15.bm | Bin 740 -> 0 bytes .../dolphin/L2_Furippa2_128x64/frame_16.bm | Bin 533 -> 0 bytes .../dolphin/L2_Furippa2_128x64/frame_17.bm | Bin 451 -> 0 bytes .../dolphin/L2_Furippa2_128x64/frame_18.bm | Bin 397 -> 0 bytes .../dolphin/L2_Furippa2_128x64/frame_2.bm | Bin 402 -> 0 bytes .../dolphin/L2_Furippa2_128x64/frame_3.bm | Bin 374 -> 0 bytes .../dolphin/L2_Furippa2_128x64/frame_4.bm | Bin 440 -> 0 bytes .../dolphin/L2_Furippa2_128x64/frame_5.bm | Bin 449 -> 0 bytes .../dolphin/L2_Furippa2_128x64/frame_6.bm | Bin 466 -> 0 bytes .../dolphin/L2_Furippa2_128x64/frame_7.bm | Bin 350 -> 0 bytes .../dolphin/L2_Furippa2_128x64/frame_8.bm | Bin 319 -> 0 bytes .../dolphin/L2_Furippa2_128x64/frame_9.bm | Bin 317 -> 0 bytes .../dolphin/L2_Furippa2_128x64/meta.txt | 14 -- .../dolphin/L2_Hacking_pc_128x64/frame_0.bm | Bin 543 -> 0 bytes .../dolphin/L2_Hacking_pc_128x64/frame_1.bm | Bin 545 -> 0 bytes .../dolphin/L2_Hacking_pc_128x64/frame_2.bm | Bin 548 -> 0 bytes .../dolphin/L2_Hacking_pc_128x64/frame_3.bm | Bin 608 -> 0 bytes .../dolphin/L2_Hacking_pc_128x64/frame_4.bm | Bin 609 -> 0 bytes .../dolphin/L2_Hacking_pc_128x64/meta.txt | 32 ----- .../dolphin/L2_Soldering_128x64/frame_0.bm | Bin 699 -> 0 bytes .../dolphin/L2_Soldering_128x64/frame_1.bm | Bin 688 -> 0 bytes .../dolphin/L2_Soldering_128x64/frame_10.bm | Bin 699 -> 0 bytes .../dolphin/L2_Soldering_128x64/frame_2.bm | Bin 689 -> 0 bytes .../dolphin/L2_Soldering_128x64/frame_3.bm | Bin 689 -> 0 bytes .../dolphin/L2_Soldering_128x64/frame_4.bm | Bin 693 -> 0 bytes .../dolphin/L2_Soldering_128x64/frame_5.bm | Bin 696 -> 0 bytes .../dolphin/L2_Soldering_128x64/frame_6.bm | Bin 712 -> 0 bytes .../dolphin/L2_Soldering_128x64/frame_7.bm | Bin 732 -> 0 bytes .../dolphin/L2_Soldering_128x64/frame_8.bm | Bin 705 -> 0 bytes .../dolphin/L2_Soldering_128x64/frame_9.bm | Bin 698 -> 0 bytes .../dolphin/L2_Soldering_128x64/meta.txt | 23 ---- .../dolphin/L3_Furippa3_128x64/frame_0.bm | Bin 398 -> 0 bytes .../dolphin/L3_Furippa3_128x64/frame_1.bm | Bin 438 -> 0 bytes .../dolphin/L3_Furippa3_128x64/frame_10.bm | Bin 559 -> 0 bytes .../dolphin/L3_Furippa3_128x64/frame_11.bm | Bin 728 -> 0 bytes .../dolphin/L3_Furippa3_128x64/frame_12.bm | Bin 541 -> 0 bytes .../dolphin/L3_Furippa3_128x64/frame_13.bm | Bin 584 -> 0 bytes .../dolphin/L3_Furippa3_128x64/frame_14.bm | Bin 610 -> 0 bytes .../dolphin/L3_Furippa3_128x64/frame_15.bm | Bin 741 -> 0 bytes .../dolphin/L3_Furippa3_128x64/frame_16.bm | Bin 559 -> 0 bytes .../dolphin/L3_Furippa3_128x64/frame_17.bm | Bin 492 -> 0 bytes .../dolphin/L3_Furippa3_128x64/frame_18.bm | Bin 445 -> 0 bytes .../dolphin/L3_Furippa3_128x64/frame_2.bm | Bin 463 -> 0 bytes .../dolphin/L3_Furippa3_128x64/frame_3.bm | Bin 424 -> 0 bytes .../dolphin/L3_Furippa3_128x64/frame_4.bm | Bin 499 -> 0 bytes .../dolphin/L3_Furippa3_128x64/frame_5.bm | Bin 504 -> 0 bytes .../dolphin/L3_Furippa3_128x64/frame_6.bm | Bin 521 -> 0 bytes .../dolphin/L3_Furippa3_128x64/frame_7.bm | Bin 398 -> 0 bytes .../dolphin/L3_Furippa3_128x64/frame_8.bm | Bin 419 -> 0 bytes .../dolphin/L3_Furippa3_128x64/frame_9.bm | Bin 435 -> 0 bytes .../dolphin/L3_Furippa3_128x64/meta.txt | 14 -- .../dolphin/L3_Hijack_radio_128x64/frame_0.bm | Bin 524 -> 0 bytes .../dolphin/L3_Hijack_radio_128x64/frame_1.bm | Bin 527 -> 0 bytes .../L3_Hijack_radio_128x64/frame_10.bm | Bin 550 -> 0 bytes .../L3_Hijack_radio_128x64/frame_11.bm | Bin 572 -> 0 bytes .../L3_Hijack_radio_128x64/frame_12.bm | Bin 539 -> 0 bytes .../L3_Hijack_radio_128x64/frame_13.bm | Bin 579 -> 0 bytes .../dolphin/L3_Hijack_radio_128x64/frame_2.bm | Bin 526 -> 0 bytes .../dolphin/L3_Hijack_radio_128x64/frame_3.bm | Bin 529 -> 0 bytes .../dolphin/L3_Hijack_radio_128x64/frame_4.bm | Bin 571 -> 0 bytes .../dolphin/L3_Hijack_radio_128x64/frame_5.bm | Bin 574 -> 0 bytes .../dolphin/L3_Hijack_radio_128x64/frame_6.bm | Bin 524 -> 0 bytes .../dolphin/L3_Hijack_radio_128x64/frame_7.bm | Bin 655 -> 0 bytes .../dolphin/L3_Hijack_radio_128x64/frame_8.bm | Bin 645 -> 0 bytes .../dolphin/L3_Hijack_radio_128x64/frame_9.bm | Bin 611 -> 0 bytes .../dolphin/L3_Hijack_radio_128x64/meta.txt | 14 -- .../dolphin/L3_Lab_research_128x54/frame_0.bm | Bin 611 -> 0 bytes .../dolphin/L3_Lab_research_128x54/frame_1.bm | Bin 614 -> 0 bytes .../L3_Lab_research_128x54/frame_10.bm | Bin 576 -> 0 bytes .../L3_Lab_research_128x54/frame_11.bm | Bin 585 -> 0 bytes .../L3_Lab_research_128x54/frame_12.bm | Bin 571 -> 0 bytes .../L3_Lab_research_128x54/frame_13.bm | Bin 615 -> 0 bytes .../dolphin/L3_Lab_research_128x54/frame_2.bm | Bin 618 -> 0 bytes .../dolphin/L3_Lab_research_128x54/frame_3.bm | Bin 608 -> 0 bytes .../dolphin/L3_Lab_research_128x54/frame_4.bm | Bin 615 -> 0 bytes .../dolphin/L3_Lab_research_128x54/frame_5.bm | Bin 618 -> 0 bytes .../dolphin/L3_Lab_research_128x54/frame_6.bm | Bin 615 -> 0 bytes .../dolphin/L3_Lab_research_128x54/frame_7.bm | Bin 585 -> 0 bytes .../dolphin/L3_Lab_research_128x54/frame_8.bm | Bin 578 -> 0 bytes .../dolphin/L3_Lab_research_128x54/frame_9.bm | Bin 581 -> 0 bytes .../dolphin/L3_Lab_research_128x54/meta.txt | 59 --------- assets/resources/dolphin/manifest.txt | 121 ------------------ documentation/AppManifests.md | 4 +- documentation/fbt.md | 1 + fbt | 3 +- fbt.cmd | 4 +- fbt_options.py | 2 - firmware.scons | 38 +----- lib/scons | 1 - scripts/fbt/sdk.py | 25 ++-- scripts/fbt_tools/fbt_assets.py | 30 +++-- scripts/fbt_tools/fbt_debugopts.py | 35 +++++ scripts/fbt_tools/fbt_extapps.py | 44 ++++++- scripts/fbt_tools/fbt_sdk.py | 72 +++++++---- scripts/fbt_tools/fbt_tweaks.py | 43 +++++++ scripts/fbt_tools/fwbin.py | 8 ++ scripts/fbt_tools/gdb.py | 2 - scripts/fwsize.py | 5 +- scripts/sconsdist.py | 7 +- scripts/toolchain/fbtenv.cmd | 4 +- scripts/toolchain/fbtenv.sh | 2 +- .../toolchain/windows-toolchain-download.ps1 | 12 +- site_scons/commandline.scons | 5 + site_scons/environ.scons | 4 +- site_scons/extapps.scons | 3 + site_scons/fbt_extra/util.py | 5 +- site_scons/site_init.py | 16 +-- 243 files changed, 302 insertions(+), 740 deletions(-) delete mode 100644 assets/resources/dolphin/L1_Boxing_128x64/frame_0.bm delete mode 100644 assets/resources/dolphin/L1_Boxing_128x64/frame_1.bm delete mode 100644 assets/resources/dolphin/L1_Boxing_128x64/frame_2.bm delete mode 100644 assets/resources/dolphin/L1_Boxing_128x64/frame_3.bm delete mode 100644 assets/resources/dolphin/L1_Boxing_128x64/frame_4.bm delete mode 100644 assets/resources/dolphin/L1_Boxing_128x64/frame_5.bm delete mode 100644 assets/resources/dolphin/L1_Boxing_128x64/frame_6.bm delete mode 100644 assets/resources/dolphin/L1_Boxing_128x64/meta.txt delete mode 100644 assets/resources/dolphin/L1_Cry_128x64/frame_0.bm delete mode 100644 assets/resources/dolphin/L1_Cry_128x64/frame_1.bm delete mode 100644 assets/resources/dolphin/L1_Cry_128x64/frame_2.bm delete mode 100644 assets/resources/dolphin/L1_Cry_128x64/frame_3.bm delete mode 100644 assets/resources/dolphin/L1_Cry_128x64/frame_4.bm delete mode 100644 assets/resources/dolphin/L1_Cry_128x64/frame_5.bm delete mode 100644 assets/resources/dolphin/L1_Cry_128x64/frame_6.bm delete mode 100644 assets/resources/dolphin/L1_Cry_128x64/frame_7.bm delete mode 100644 assets/resources/dolphin/L1_Cry_128x64/meta.txt delete mode 100644 assets/resources/dolphin/L1_Furippa1_128x64/frame_0.bm delete mode 100644 assets/resources/dolphin/L1_Furippa1_128x64/frame_1.bm delete mode 100644 assets/resources/dolphin/L1_Furippa1_128x64/frame_10.bm delete mode 100644 assets/resources/dolphin/L1_Furippa1_128x64/frame_11.bm delete mode 100644 assets/resources/dolphin/L1_Furippa1_128x64/frame_12.bm delete mode 100644 assets/resources/dolphin/L1_Furippa1_128x64/frame_13.bm delete mode 100644 assets/resources/dolphin/L1_Furippa1_128x64/frame_14.bm delete mode 100644 assets/resources/dolphin/L1_Furippa1_128x64/frame_15.bm delete mode 100644 assets/resources/dolphin/L1_Furippa1_128x64/frame_16.bm delete mode 100644 assets/resources/dolphin/L1_Furippa1_128x64/frame_17.bm delete mode 100644 assets/resources/dolphin/L1_Furippa1_128x64/frame_18.bm delete mode 100644 assets/resources/dolphin/L1_Furippa1_128x64/frame_2.bm delete mode 100644 assets/resources/dolphin/L1_Furippa1_128x64/frame_3.bm delete mode 100644 assets/resources/dolphin/L1_Furippa1_128x64/frame_4.bm delete mode 100644 assets/resources/dolphin/L1_Furippa1_128x64/frame_5.bm delete mode 100644 assets/resources/dolphin/L1_Furippa1_128x64/frame_6.bm delete mode 100644 assets/resources/dolphin/L1_Furippa1_128x64/frame_7.bm delete mode 100644 assets/resources/dolphin/L1_Furippa1_128x64/frame_8.bm delete mode 100644 assets/resources/dolphin/L1_Furippa1_128x64/frame_9.bm delete mode 100644 assets/resources/dolphin/L1_Furippa1_128x64/meta.txt delete mode 100644 assets/resources/dolphin/L1_Laptop_128x51/frame_0.bm delete mode 100644 assets/resources/dolphin/L1_Laptop_128x51/frame_1.bm delete mode 100644 assets/resources/dolphin/L1_Laptop_128x51/frame_2.bm delete mode 100644 assets/resources/dolphin/L1_Laptop_128x51/frame_3.bm delete mode 100644 assets/resources/dolphin/L1_Laptop_128x51/frame_4.bm delete mode 100644 assets/resources/dolphin/L1_Laptop_128x51/frame_5.bm delete mode 100644 assets/resources/dolphin/L1_Laptop_128x51/frame_6.bm delete mode 100644 assets/resources/dolphin/L1_Laptop_128x51/frame_7.bm delete mode 100644 assets/resources/dolphin/L1_Laptop_128x51/meta.txt delete mode 100644 assets/resources/dolphin/L1_Leaving_sad_128x64/frame_0.bm delete mode 100644 assets/resources/dolphin/L1_Leaving_sad_128x64/frame_1.bm delete mode 100644 assets/resources/dolphin/L1_Leaving_sad_128x64/frame_10.bm delete mode 100644 assets/resources/dolphin/L1_Leaving_sad_128x64/frame_11.bm delete mode 100644 assets/resources/dolphin/L1_Leaving_sad_128x64/frame_12.bm delete mode 100644 assets/resources/dolphin/L1_Leaving_sad_128x64/frame_2.bm delete mode 100644 assets/resources/dolphin/L1_Leaving_sad_128x64/frame_3.bm delete mode 100644 assets/resources/dolphin/L1_Leaving_sad_128x64/frame_4.bm delete mode 100644 assets/resources/dolphin/L1_Leaving_sad_128x64/frame_5.bm delete mode 100644 assets/resources/dolphin/L1_Leaving_sad_128x64/frame_6.bm delete mode 100644 assets/resources/dolphin/L1_Leaving_sad_128x64/frame_7.bm delete mode 100644 assets/resources/dolphin/L1_Leaving_sad_128x64/frame_8.bm delete mode 100644 assets/resources/dolphin/L1_Leaving_sad_128x64/frame_9.bm delete mode 100644 assets/resources/dolphin/L1_Leaving_sad_128x64/meta.txt delete mode 100644 assets/resources/dolphin/L1_Mad_fist_128x64/frame_0.bm delete mode 100644 assets/resources/dolphin/L1_Mad_fist_128x64/frame_1.bm delete mode 100644 assets/resources/dolphin/L1_Mad_fist_128x64/frame_10.bm delete mode 100644 assets/resources/dolphin/L1_Mad_fist_128x64/frame_11.bm delete mode 100644 assets/resources/dolphin/L1_Mad_fist_128x64/frame_12.bm delete mode 100644 assets/resources/dolphin/L1_Mad_fist_128x64/frame_13.bm delete mode 100644 assets/resources/dolphin/L1_Mad_fist_128x64/frame_2.bm delete mode 100644 assets/resources/dolphin/L1_Mad_fist_128x64/frame_3.bm delete mode 100644 assets/resources/dolphin/L1_Mad_fist_128x64/frame_4.bm delete mode 100644 assets/resources/dolphin/L1_Mad_fist_128x64/frame_5.bm delete mode 100644 assets/resources/dolphin/L1_Mad_fist_128x64/frame_6.bm delete mode 100644 assets/resources/dolphin/L1_Mad_fist_128x64/frame_7.bm delete mode 100644 assets/resources/dolphin/L1_Mad_fist_128x64/frame_8.bm delete mode 100644 assets/resources/dolphin/L1_Mad_fist_128x64/frame_9.bm delete mode 100644 assets/resources/dolphin/L1_Mad_fist_128x64/meta.txt delete mode 100644 assets/resources/dolphin/L1_Painting_128x64/frame_0.bm delete mode 100644 assets/resources/dolphin/L1_Painting_128x64/frame_1.bm delete mode 100644 assets/resources/dolphin/L1_Painting_128x64/frame_10.bm delete mode 100644 assets/resources/dolphin/L1_Painting_128x64/frame_11.bm delete mode 100644 assets/resources/dolphin/L1_Painting_128x64/frame_2.bm delete mode 100644 assets/resources/dolphin/L1_Painting_128x64/frame_3.bm delete mode 100644 assets/resources/dolphin/L1_Painting_128x64/frame_4.bm delete mode 100644 assets/resources/dolphin/L1_Painting_128x64/frame_5.bm delete mode 100644 assets/resources/dolphin/L1_Painting_128x64/frame_6.bm delete mode 100644 assets/resources/dolphin/L1_Painting_128x64/frame_7.bm delete mode 100644 assets/resources/dolphin/L1_Painting_128x64/frame_8.bm delete mode 100644 assets/resources/dolphin/L1_Painting_128x64/frame_9.bm delete mode 100644 assets/resources/dolphin/L1_Painting_128x64/meta.txt delete mode 100644 assets/resources/dolphin/L1_Read_books_128x64/frame_0.bm delete mode 100644 assets/resources/dolphin/L1_Read_books_128x64/frame_1.bm delete mode 100644 assets/resources/dolphin/L1_Read_books_128x64/frame_2.bm delete mode 100644 assets/resources/dolphin/L1_Read_books_128x64/frame_3.bm delete mode 100644 assets/resources/dolphin/L1_Read_books_128x64/frame_4.bm delete mode 100644 assets/resources/dolphin/L1_Read_books_128x64/frame_5.bm delete mode 100644 assets/resources/dolphin/L1_Read_books_128x64/frame_6.bm delete mode 100644 assets/resources/dolphin/L1_Read_books_128x64/frame_7.bm delete mode 100644 assets/resources/dolphin/L1_Read_books_128x64/frame_8.bm delete mode 100644 assets/resources/dolphin/L1_Read_books_128x64/meta.txt delete mode 100644 assets/resources/dolphin/L1_Recording_128x51/frame_0.bm delete mode 100644 assets/resources/dolphin/L1_Recording_128x51/frame_1.bm delete mode 100644 assets/resources/dolphin/L1_Recording_128x51/frame_10.bm delete mode 100644 assets/resources/dolphin/L1_Recording_128x51/frame_11.bm delete mode 100644 assets/resources/dolphin/L1_Recording_128x51/frame_2.bm delete mode 100644 assets/resources/dolphin/L1_Recording_128x51/frame_3.bm delete mode 100644 assets/resources/dolphin/L1_Recording_128x51/frame_4.bm delete mode 100644 assets/resources/dolphin/L1_Recording_128x51/frame_5.bm delete mode 100644 assets/resources/dolphin/L1_Recording_128x51/frame_6.bm delete mode 100644 assets/resources/dolphin/L1_Recording_128x51/frame_7.bm delete mode 100644 assets/resources/dolphin/L1_Recording_128x51/frame_8.bm delete mode 100644 assets/resources/dolphin/L1_Recording_128x51/frame_9.bm delete mode 100644 assets/resources/dolphin/L1_Recording_128x51/meta.txt delete mode 100644 assets/resources/dolphin/L1_Sleep_128x64/frame_0.bm delete mode 100644 assets/resources/dolphin/L1_Sleep_128x64/frame_1.bm delete mode 100644 assets/resources/dolphin/L1_Sleep_128x64/frame_2.bm delete mode 100644 assets/resources/dolphin/L1_Sleep_128x64/frame_3.bm delete mode 100644 assets/resources/dolphin/L1_Sleep_128x64/meta.txt delete mode 100644 assets/resources/dolphin/L1_Waves_128x50/frame_0.bm delete mode 100644 assets/resources/dolphin/L1_Waves_128x50/frame_1.bm delete mode 100644 assets/resources/dolphin/L1_Waves_128x50/frame_2.bm delete mode 100644 assets/resources/dolphin/L1_Waves_128x50/frame_3.bm delete mode 100644 assets/resources/dolphin/L1_Waves_128x50/meta.txt delete mode 100644 assets/resources/dolphin/L2_Furippa2_128x64/frame_0.bm delete mode 100644 assets/resources/dolphin/L2_Furippa2_128x64/frame_1.bm delete mode 100644 assets/resources/dolphin/L2_Furippa2_128x64/frame_10.bm delete mode 100644 assets/resources/dolphin/L2_Furippa2_128x64/frame_11.bm delete mode 100644 assets/resources/dolphin/L2_Furippa2_128x64/frame_12.bm delete mode 100644 assets/resources/dolphin/L2_Furippa2_128x64/frame_13.bm delete mode 100644 assets/resources/dolphin/L2_Furippa2_128x64/frame_14.bm delete mode 100644 assets/resources/dolphin/L2_Furippa2_128x64/frame_15.bm delete mode 100644 assets/resources/dolphin/L2_Furippa2_128x64/frame_16.bm delete mode 100644 assets/resources/dolphin/L2_Furippa2_128x64/frame_17.bm delete mode 100644 assets/resources/dolphin/L2_Furippa2_128x64/frame_18.bm delete mode 100644 assets/resources/dolphin/L2_Furippa2_128x64/frame_2.bm delete mode 100644 assets/resources/dolphin/L2_Furippa2_128x64/frame_3.bm delete mode 100644 assets/resources/dolphin/L2_Furippa2_128x64/frame_4.bm delete mode 100644 assets/resources/dolphin/L2_Furippa2_128x64/frame_5.bm delete mode 100644 assets/resources/dolphin/L2_Furippa2_128x64/frame_6.bm delete mode 100644 assets/resources/dolphin/L2_Furippa2_128x64/frame_7.bm delete mode 100644 assets/resources/dolphin/L2_Furippa2_128x64/frame_8.bm delete mode 100644 assets/resources/dolphin/L2_Furippa2_128x64/frame_9.bm delete mode 100644 assets/resources/dolphin/L2_Furippa2_128x64/meta.txt delete mode 100644 assets/resources/dolphin/L2_Hacking_pc_128x64/frame_0.bm delete mode 100644 assets/resources/dolphin/L2_Hacking_pc_128x64/frame_1.bm delete mode 100644 assets/resources/dolphin/L2_Hacking_pc_128x64/frame_2.bm delete mode 100644 assets/resources/dolphin/L2_Hacking_pc_128x64/frame_3.bm delete mode 100644 assets/resources/dolphin/L2_Hacking_pc_128x64/frame_4.bm delete mode 100644 assets/resources/dolphin/L2_Hacking_pc_128x64/meta.txt delete mode 100644 assets/resources/dolphin/L2_Soldering_128x64/frame_0.bm delete mode 100644 assets/resources/dolphin/L2_Soldering_128x64/frame_1.bm delete mode 100644 assets/resources/dolphin/L2_Soldering_128x64/frame_10.bm delete mode 100644 assets/resources/dolphin/L2_Soldering_128x64/frame_2.bm delete mode 100644 assets/resources/dolphin/L2_Soldering_128x64/frame_3.bm delete mode 100644 assets/resources/dolphin/L2_Soldering_128x64/frame_4.bm delete mode 100644 assets/resources/dolphin/L2_Soldering_128x64/frame_5.bm delete mode 100644 assets/resources/dolphin/L2_Soldering_128x64/frame_6.bm delete mode 100644 assets/resources/dolphin/L2_Soldering_128x64/frame_7.bm delete mode 100644 assets/resources/dolphin/L2_Soldering_128x64/frame_8.bm delete mode 100644 assets/resources/dolphin/L2_Soldering_128x64/frame_9.bm delete mode 100644 assets/resources/dolphin/L2_Soldering_128x64/meta.txt delete mode 100644 assets/resources/dolphin/L3_Furippa3_128x64/frame_0.bm delete mode 100644 assets/resources/dolphin/L3_Furippa3_128x64/frame_1.bm delete mode 100644 assets/resources/dolphin/L3_Furippa3_128x64/frame_10.bm delete mode 100644 assets/resources/dolphin/L3_Furippa3_128x64/frame_11.bm delete mode 100644 assets/resources/dolphin/L3_Furippa3_128x64/frame_12.bm delete mode 100644 assets/resources/dolphin/L3_Furippa3_128x64/frame_13.bm delete mode 100644 assets/resources/dolphin/L3_Furippa3_128x64/frame_14.bm delete mode 100644 assets/resources/dolphin/L3_Furippa3_128x64/frame_15.bm delete mode 100644 assets/resources/dolphin/L3_Furippa3_128x64/frame_16.bm delete mode 100644 assets/resources/dolphin/L3_Furippa3_128x64/frame_17.bm delete mode 100644 assets/resources/dolphin/L3_Furippa3_128x64/frame_18.bm delete mode 100644 assets/resources/dolphin/L3_Furippa3_128x64/frame_2.bm delete mode 100644 assets/resources/dolphin/L3_Furippa3_128x64/frame_3.bm delete mode 100644 assets/resources/dolphin/L3_Furippa3_128x64/frame_4.bm delete mode 100644 assets/resources/dolphin/L3_Furippa3_128x64/frame_5.bm delete mode 100644 assets/resources/dolphin/L3_Furippa3_128x64/frame_6.bm delete mode 100644 assets/resources/dolphin/L3_Furippa3_128x64/frame_7.bm delete mode 100644 assets/resources/dolphin/L3_Furippa3_128x64/frame_8.bm delete mode 100644 assets/resources/dolphin/L3_Furippa3_128x64/frame_9.bm delete mode 100644 assets/resources/dolphin/L3_Furippa3_128x64/meta.txt delete mode 100644 assets/resources/dolphin/L3_Hijack_radio_128x64/frame_0.bm delete mode 100644 assets/resources/dolphin/L3_Hijack_radio_128x64/frame_1.bm delete mode 100644 assets/resources/dolphin/L3_Hijack_radio_128x64/frame_10.bm delete mode 100644 assets/resources/dolphin/L3_Hijack_radio_128x64/frame_11.bm delete mode 100644 assets/resources/dolphin/L3_Hijack_radio_128x64/frame_12.bm delete mode 100644 assets/resources/dolphin/L3_Hijack_radio_128x64/frame_13.bm delete mode 100644 assets/resources/dolphin/L3_Hijack_radio_128x64/frame_2.bm delete mode 100644 assets/resources/dolphin/L3_Hijack_radio_128x64/frame_3.bm delete mode 100644 assets/resources/dolphin/L3_Hijack_radio_128x64/frame_4.bm delete mode 100644 assets/resources/dolphin/L3_Hijack_radio_128x64/frame_5.bm delete mode 100644 assets/resources/dolphin/L3_Hijack_radio_128x64/frame_6.bm delete mode 100644 assets/resources/dolphin/L3_Hijack_radio_128x64/frame_7.bm delete mode 100644 assets/resources/dolphin/L3_Hijack_radio_128x64/frame_8.bm delete mode 100644 assets/resources/dolphin/L3_Hijack_radio_128x64/frame_9.bm delete mode 100644 assets/resources/dolphin/L3_Hijack_radio_128x64/meta.txt delete mode 100644 assets/resources/dolphin/L3_Lab_research_128x54/frame_0.bm delete mode 100644 assets/resources/dolphin/L3_Lab_research_128x54/frame_1.bm delete mode 100644 assets/resources/dolphin/L3_Lab_research_128x54/frame_10.bm delete mode 100644 assets/resources/dolphin/L3_Lab_research_128x54/frame_11.bm delete mode 100644 assets/resources/dolphin/L3_Lab_research_128x54/frame_12.bm delete mode 100644 assets/resources/dolphin/L3_Lab_research_128x54/frame_13.bm delete mode 100644 assets/resources/dolphin/L3_Lab_research_128x54/frame_2.bm delete mode 100644 assets/resources/dolphin/L3_Lab_research_128x54/frame_3.bm delete mode 100644 assets/resources/dolphin/L3_Lab_research_128x54/frame_4.bm delete mode 100644 assets/resources/dolphin/L3_Lab_research_128x54/frame_5.bm delete mode 100644 assets/resources/dolphin/L3_Lab_research_128x54/frame_6.bm delete mode 100644 assets/resources/dolphin/L3_Lab_research_128x54/frame_7.bm delete mode 100644 assets/resources/dolphin/L3_Lab_research_128x54/frame_8.bm delete mode 100644 assets/resources/dolphin/L3_Lab_research_128x54/frame_9.bm delete mode 100644 assets/resources/dolphin/L3_Lab_research_128x54/meta.txt delete mode 100644 assets/resources/dolphin/manifest.txt delete mode 160000 lib/scons create mode 100644 scripts/fbt_tools/fbt_tweaks.py diff --git a/.github/workflows/pvs_studio.yml b/.github/workflows/pvs_studio.yml index e3d5fc132..f28fad20d 100644 --- a/.github/workflows/pvs_studio.yml +++ b/.github/workflows/pvs_studio.yml @@ -65,7 +65,6 @@ jobs: pvs-studio-analyzer credentials ${{ secrets.PVS_STUDIO_CREDENTIALS }} pvs-studio-analyzer analyze \ @.pvsoptions \ - --disableLicenseExpirationCheck \ -j$(grep -c processor /proc/cpuinfo) \ -f build/f7-firmware-DC/compile_commands.json \ -o PVS-Studio.log diff --git a/.gitmodules b/.gitmodules index 308d60fdc..a97e0933a 100644 --- a/.gitmodules +++ b/.gitmodules @@ -22,9 +22,6 @@ [submodule "lib/microtar"] path = lib/microtar url = https://github.com/amachronic/microtar.git -[submodule "lib/scons"] - path = lib/scons - url = https://github.com/SCons/scons.git [submodule "lib/mbedtls"] path = lib/mbedtls url = https://github.com/Mbed-TLS/mbedtls.git diff --git a/.vscode/example/c_cpp_properties.json b/.vscode/example/c_cpp_properties.json index db08320c9..d1cac63e9 100644 --- a/.vscode/example/c_cpp_properties.json +++ b/.vscode/example/c_cpp_properties.json @@ -2,7 +2,7 @@ "configurations": [ { "name": "Win32", - "compilerPath": "${workspaceFolder}/toolchain/i686-windows/bin/arm-none-eabi-gcc.exe", + "compilerPath": "${workspaceFolder}/toolchain/x86_64-windows/bin/arm-none-eabi-gcc.exe", "intelliSenseMode": "gcc-arm", "compileCommands": "${workspaceFolder}/build/latest/compile_commands.json", "configurationProvider": "ms-vscode.cpptools", diff --git a/.vscode/example/launch.json b/.vscode/example/launch.json index f4df96592..c8b0c601d 100644 --- a/.vscode/example/launch.json +++ b/.vscode/example/launch.json @@ -79,6 +79,25 @@ ] // "showDevDebugOutput": "raw", }, + { + "name": "Attach FW (DAP)", + "cwd": "${workspaceFolder}", + "executable": "./build/latest/firmware.elf", + "request": "attach", + "type": "cortex-debug", + "servertype": "openocd", + "device": "cmsis-dap", + "svdFile": "./debug/STM32WB55_CM4.svd", + "rtos": "FreeRTOS", + "configFiles": [ + "interface/cmsis-dap.cfg", + "./debug/stm32wbx.cfg", + ], + "postAttachCommands": [ + "source debug/flipperapps.py", + ], + // "showDevDebugOutput": "raw", + }, { "name": "fbt debug", "type": "python", diff --git a/.vscode/example/settings.json b/.vscode/example/settings.json index d84707e07..19a03b69d 100644 --- a/.vscode/example/settings.json +++ b/.vscode/example/settings.json @@ -6,13 +6,13 @@ "cortex-debug.enableTelemetry": false, "cortex-debug.variableUseNaturalFormat": true, "cortex-debug.showRTOS": true, - "cortex-debug.armToolchainPath.windows": "${workspaceFolder}/toolchain/i686-windows/bin", + "cortex-debug.armToolchainPath.windows": "${workspaceFolder}/toolchain/x86_64-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.windows": "${workspaceFolder}/toolchain/x86_64-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", - "cortex-debug.gdbPath.windows": "${workspaceFolder}/toolchain/i686-windows/bin/arm-none-eabi-gdb-py.bat", + "cortex-debug.gdbPath.windows": "${workspaceFolder}/toolchain/x86_64-windows/bin/arm-none-eabi-gdb-py.bat", "cortex-debug.gdbPath.linux": "${workspaceFolder}/toolchain/x86_64-linux/bin/arm-none-eabi-gdb-py", "cortex-debug.gdbPath.osx": "${workspaceFolder}/toolchain/x86_64-darwin/bin/arm-none-eabi-gdb-py", "editor.formatOnSave": true, diff --git a/SConstruct b/SConstruct index 74fa5667b..448df9715 100644 --- a/SConstruct +++ b/SConstruct @@ -156,11 +156,9 @@ Depends(fap_dist, firmware_env["FW_EXTAPPS"]["validators"].values()) Alias("fap_dist", fap_dist) # distenv.Default(fap_dist) -plugin_resources_dist = list( - distenv.Install(f"#/assets/resources/apps/{dist_entry[0]}", dist_entry[1]) - for dist_entry in firmware_env["FW_EXTAPPS"]["dist"].values() +distenv.Depends( + firmware_env["FW_RESOURCES"], firmware_env["FW_EXTAPPS"]["resources_dist"] ) -distenv.Depends(firmware_env["FW_RESOURCES"], plugin_resources_dist) # Target for bundling core2 package for qFlipper @@ -291,6 +289,16 @@ distenv.PhonyTarget( "@echo $( ${BLACKMAGIC_ADDR} $)", ) + +# Find STLink probe ids +distenv.PhonyTarget( + "get_stlink", + distenv.Action( + lambda **kw: distenv.GetDevices(), + None, + ), +) + # Prepare vscode environment vscode_dist = distenv.Install("#.vscode", distenv.Glob("#.vscode/example/*")) distenv.Precious(vscode_dist) diff --git a/assets/.gitignore b/assets/.gitignore index 9bc0bdc0c..269577047 100644 --- a/assets/.gitignore +++ b/assets/.gitignore @@ -1,3 +1,4 @@ /core2_firmware /resources/Manifest -/resources/apps/* \ No newline at end of file +/resources/apps/* +/resources/dolphin/* diff --git a/assets/SConscript b/assets/SConscript index e1bf546cc..63141829e 100644 --- a/assets/SConscript +++ b/assets/SConscript @@ -68,15 +68,17 @@ if assetsenv["IS_BASE_FIRMWARE"]: assetsenv.Dir("#/assets/dolphin"), DOLPHIN_RES_TYPE="external", ) - assetsenv.NoClean(dolphin_external) if assetsenv["FORCE"]: assetsenv.AlwaysBuild(dolphin_external) assetsenv.Alias("dolphin_ext", dolphin_external) + assetsenv.Clean(dolphin_external, assetsenv.Dir("#/assets/resources/dolphin")) # Resources manifest resources = assetsenv.Command( "#/assets/resources/Manifest", - assetsenv.GlobRecursive("*", "resources", exclude="Manifest"), + assetsenv.GlobRecursive( + "*", assetsenv.Dir("resources").srcnode(), exclude="Manifest" + ), action=Action( '${PYTHON3} "${ASSETS_COMPILER}" manifest "${TARGET.dir.posix}" --timestamp=${GIT_UNIX_TIMESTAMP}', "${RESMANIFESTCOMSTR}", diff --git a/assets/resources/dolphin/L1_Boxing_128x64/frame_0.bm b/assets/resources/dolphin/L1_Boxing_128x64/frame_0.bm deleted file mode 100644 index 46079c3728bbc787dac3b6bd912534c3cb26c2dc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 481 zcmV<70UrJV0Nnuq41gct{-6W=AJ_nYgZv-l{|EQLAJWJOKV)zscEkbuK!`yZ+7IZ2 zA`k^H{0tv403h%n5x{%GBY^UdKr8^Whl&a-KGuK#@pCZ$fTREYfXmDOv4I8_7$4|8 zWM9wy{zW2%1H=e2Efj7e5vqyGsa1l5#-&xN^>_NMRwDg?{NgbPBEkVj5nrNFDx-1l zhs9(-K@X&ej6;zJl%Rf5@%aPDK9qp^!U4m790)wJ1NH}yWI^RnAHn>AY(wU59C%i$P0j1>yyZb2eSZ&<<EXw3P3+Y`9bg> zGw`qs{?L2IGCmF}0sTSa4~A5K;qqV!{Q=`2l1RPCF<=n=r}ZkY=Heb41yFw9{7xhI zFDwB4@9UBO1B(J5LHS?bB2PRR{qOIQ2c8r@kbrq&0D=!9D38oEA@{_G&~zj7CzJxB z|4{mWm<1(&sQl034`;q07 diff --git a/assets/resources/dolphin/L1_Boxing_128x64/frame_1.bm b/assets/resources/dolphin/L1_Boxing_128x64/frame_1.bm deleted file mode 100644 index e12d70796299cef4674cbc2e84e90f9f89cdf652..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 461 zcmV;;0W$sp0LcLW41gct{|ER#!Tt~HfIq?gs0Z|t0uSgIKV(oKe#l^e?7&DN0RIQ& z0znW0m%@IofB=C?8Ndg{z#eBYLYzDVKl;Doq5uEy!2|jNkN@@qEZ_g1{{Pp(1NLHZ5`cK(00m#7Q7XS*{roa9 zP>mvRO#zP{B1I8dfDER%u^N0_S{9+ME27rFS^biz)e|IKu$OrfJzlYp@m=A^i z13({@Y<|qu!2#onAFWhAQ3xC$9wqgWREOoTfP>0NKU-)#WTfGU z2i*Rg2mm~v^OfMhED%ZsA%8#U20{r$lt1U=0Kh=#f%SiFb)nte>GVQX8}N{kLpDxphzMk`jYS_5(oPa(oq11 z=rM!(lE5Gm0saHX2qW|VhsXpn@ObC|;s8K;MZx|L9)LMu1M+~tA^`CL5CH-H5Ac5g D_X*BS diff --git a/assets/resources/dolphin/L1_Boxing_128x64/frame_2.bm b/assets/resources/dolphin/L1_Boxing_128x64/frame_2.bm deleted file mode 100644 index b416740f2772d3a057f2fd7817d1b16adc40af47..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 531 zcmV+u0_^<(01pBH41gct{-6W=AJ_nYgZv-l{|EQLAJWJOKV)zscEkbuK!`yZ+7IZ2 zA`k^H^c){C0wF+S0QZDP0pybz04(9)A^*i^+Ry*~UJ(!=fCu&iFE9Sa1Q@u)f57#T ze?Ry66pAD$83+_~QMimosuwDyRuT^yr~rDq{Z^|He!zZl7=w{wfTM`7(I}N)@Bh9Z z7$`hyLG_Ulh;k|-k`Ky0KOlJr#iT$%52PR*H}SxO$b3We2asfNK=G&#;Qm@60K^CI zA1_o5fFt{N6@Y$KvHOF|KwuI3io_mN1^}7x9D%Rn9wc*+m!`v1q^8X)k4#Xu+Y4~RT;0Q1NX)2sad=j0k7 zaDWCve^7ZuGEfLWJcsCiJcrH!4+o6|{`>pHGlCB@`lHGpQCE}$15`htJfxB+0svwd z5&O^ABUOPy0pcG)`Qk?-1cXR1Blo|)MymygNFZzV2b72aGx@y&3n3r1{5(Bg1R{Y6 zU#L8X11Nw*Xb}=HkIJeaNU6AJ8B+R4hv^D{NTeDU`iIgI4;;aiNAlho790vA_Wy=N zK0XWpePmzYc#p;<0FqDuqCY?QJb*(NfJdkTf&dsjCHM#kKP&VI06m}p@_;}v5)gQS VL;#<_c)&s99)L&z_y-VwgV=lG-g*E4 diff --git a/assets/resources/dolphin/L1_Boxing_128x64/frame_3.bm b/assets/resources/dolphin/L1_Boxing_128x64/frame_3.bm deleted file mode 100644 index b7e15ddeec980a2775b4df50901a2996a7991609..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 437 zcmV;m0ZRS>0I>l841gct{|ER#!Tt~HfIq?gs0Z|t0uSgIKV(oKe#l^e?7&DN0RIQ& z0znW0m%=`-fB=C?8DIy+z#ecc0u1NLHZ5`cK(00m#7Q7XSuV1`CY z4;p}dWM9E}$YfC2*hpfPa4=c@OXF0}r_UegN^% z56ZSbf5?6Z=>q-_)qejQ`Mi1+LHxrffbqZ|-TF0H6yP2(0sBV>RDt862aJFoGC}&=L+8+l2i*Q}m=6*VKNtN# zd{AKc(iigvh_l84D1Xf4Vj}Sms{J?+L-`NuDi0_HKz}+YI5=bx0sQ4bu z1P4KoAI(-jfESHKe=;lefF3as{MB|b@OZ!2ev*QC=rM!(lAnlwav*r0gnpm!`2dDq f4<3LVKnM@;dPTwU03VaEVh!?X5CFmc5Ac5gJVnKO diff --git a/assets/resources/dolphin/L1_Boxing_128x64/frame_4.bm b/assets/resources/dolphin/L1_Boxing_128x64/frame_4.bm deleted file mode 100644 index 202ad6e37bf56c5959bb6ca06bfe101de96248f1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 459 zcmV;+0W|&r0LK9U41gct{|ER#!Tt~HfIq?gs0Z|t0uSgIKV(oKe#l^e?7&DN0RIQ& z0znW0m-wj?004;!0}Kbnz#dcx1i%(>@DTs%|B8qI|1SuL5I_U_0hVw7&;Nho;}HS~ zJtSYC8Azf)@d6BsMH`63YQNBU)oQSjz<~y<)!*v1SdHWl*@?s;hzKA7uhA%#U$6fD z85k%$YC-jpe+AokJ_ZaE5rzZL-w|V$E$z-5DKJ! zoPXj14;BGG-Ua9f3$0RibCfPNrA{TK*8!OC5Y B#Yg}E diff --git a/assets/resources/dolphin/L1_Boxing_128x64/frame_5.bm b/assets/resources/dolphin/L1_Boxing_128x64/frame_5.bm deleted file mode 100644 index cdc0a2a343182740de054af8ad6e8a42848e616b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 450 zcmV;z0X_Z!0KNeL41gct{|ER#!Tt~HfIq?gs0Z|t0uSgIKV(oKe#l^e?7&DN0RIQ& z0znX0XaGQi!2g5h;14N;0O*kL5dZ4`iiiLI3xIt9f&d@b46}d!fBXLhfO-M#BK-i# zBvAk$$h1+oj7F>d2aQ&$4;p|FYQ0_lt5t~JK>e7QLr8QOfB{$NluEDH|9=dKJY)g( zk$(l_A<-p>esS^pWnd5*0R6+|3BXV)Gy(gE%ZraLjs~bdnU163^}>(p6OjCW@Zt6! zoEJb3A^FzF>jDRu{(^{EsSrG-1O1RM=zd`FU&wr5PzwNkhw2X% skiXadA0QBe#zDjX0t5hH_RvoO$`6hhIbZ|wE{qWVE+GIYKf(SF;Hdb~#sB~S diff --git a/assets/resources/dolphin/L1_Boxing_128x64/frame_6.bm b/assets/resources/dolphin/L1_Boxing_128x64/frame_6.bm deleted file mode 100644 index e8ea3aa683605683a11be4c74d8946e2c7f720d3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 442 zcmV;r0Y&}+0JZ@D41gct{|ER#!Tt~HfIq?gs0Z|t0uSgIKV(oKe#l^e?7&DN0RIQ& z0znX0XaGQi!2g5h;14N;0O*kL5dZ4`iiiLI3xIt9f&d@b46}d!fBXLhfO-M#BK-i# zBvAk$$h1+oj7F>d2aQ&$4;p|FYQ0_lt5t~JK>e7QLr8QOfB{$NluEDH|9=dKJY)g( zk$(l_A<kmk_ZF^z(e$o2g*laz(8aN z_Oye?{SlyG8UjCRz&v2k{s#g9fFHF~9zU3V0>V%c`&5DB@du6k9x@U7+d<={hrlQ1 z0jVG2QwV@Zpb$J${v@;nc|k+!zd8+o9#D7u)oK9pfUoBhNC%b$e&O<~gNFe_f6RwK z4=LdOvY_&)kLwT*DZu`N=kOoah#oWvfc~nm5O}~B^NEB5&3_^LAEb%}06`1=f8p{0 k2s~sQKky(x1`lln@EoA{;e(a{KO$(s3FH7^e}nuVz*X;M1& diff --git a/assets/resources/dolphin/L1_Boxing_128x64/meta.txt b/assets/resources/dolphin/L1_Boxing_128x64/meta.txt deleted file mode 100644 index c66998e7d..000000000 --- a/assets/resources/dolphin/L1_Boxing_128x64/meta.txt +++ /dev/null @@ -1,32 +0,0 @@ -Filetype: Flipper Animation -Version: 1 - -Width: 128 -Height: 64 -Passive frames: 9 -Active frames: 7 -Frames order: 0 1 2 1 3 1 2 3 1 4 5 6 5 6 5 4 -Active cycles: 1 -Frame rate: 2 -Duration: 3600 -Active cooldown: 7 - -Bubble slots: 2 - -Slot: 0 -X: 78 -Y: 16 -Text: F*&K!!! -AlignH: Left -AlignV: Bottom -StartFrame: 2 -EndFrame: 4 - -Slot: 1 -X: 78 -Y: 16 -Text: What ya\nlookin at?! -AlignH: Left -AlignV: Center -StartFrame: 10 -EndFrame: 15 diff --git a/assets/resources/dolphin/L1_Cry_128x64/frame_0.bm b/assets/resources/dolphin/L1_Cry_128x64/frame_0.bm deleted file mode 100644 index 9147714c1e291e52f40294d265c696fc6ae5c708..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 889 zcmV-<1BUzo0CfYDU~v!(1pp2(7#2ba0isYMQizTMiKK)^720#ulnk15?AeiDP5lAF}9xx0=5)*)eL;*vIpiChW z2vinH0iZ(RGDw921%bdwu@VA^!67(^gd$0#2aG2o4-<&cAdtL-LlH72#WvB=6^$ zQ4eDvkYafN;s=b1k(9{N4`V=qLP6;57zC8|VIja=LFk?n*oQ|+K#)c(0rfqO2M~K0 zLcBrgfS|x-GYE)0a1;oDJz%jH=$^&_5eJOHQYR5Y3=H-;3J z;z8oY7x91w7JEuWz-l1((jLbHfPrEcLJv$`nD!u1$iSfx51=%mkPI2@tdRit)S(|j z=))uM2pR0k!v={`0p0qmg=NQEQeV1R?l2Z%kE5OOgnjNpVn!Fb7!WeAc$ z1f!Ci1qa|V88Pf!BLt8DgdQNl02G+^G6UF@MnQu3AJPpFJ)KZ6R{(lZk7QmNq$-fb z5c;0Q7EYlDjAR%wz!1ZaX#~JRBoNTZ*umpY&^ko+GK>x)fGHaQC}0`G*q8?;2qK8& zqX6?gh@e5^EWpA6;sF>G18D?3jFJVBghWB(z(^j_0Z;-Ah@3DOh1zfsJ0dJZgCxkx zBv>4DAMhV5A-G`&u@;PjAq;>|!R5p~i~txiC=7!EIPg0}9$AS2XlM{9j0y$u|HHxM zgo#2j7?=dAD@2h4#s&{F*vT*;@sUtu7a#e&;DGUjA4EDN0kK~j=L9v1ET|GFO%lZK PKzQIK5;%cCMPd(Nc|%A2 diff --git a/assets/resources/dolphin/L1_Cry_128x64/frame_1.bm b/assets/resources/dolphin/L1_Cry_128x64/frame_1.bm deleted file mode 100644 index 789273d9da32277651ffe8631e3a657068af1b5d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 911 zcmV;A191ES0E+{N=4y}%1Og=ih+t@t4n&eg0Fhv70YQRPq!LI-7;z9u1TsYts7N6& zNR z$o4WBCPEN{DF6geBP212?4l5f4nX!&3>F~_FbN^-b_y{ZiR@&UCTm-JP96%9yJ9qKr|GH z6vS{)br8r8V*q^)hY(Aq0kl${A!ui1e~BN=8CK2M3m7kc%A! zN|87@PGy}NWfDhP$O924kJkioWNEfk>LZ!5Ro1U96{xXJ()mZ83-379(Dvl z9?48N5cq?L9xxmLSadvsXyjl+Q2-YLLnMO4G#QEnN+Sih1JV+EAn3s&QYadmV_R1v{}fpA@PAsAdmpy;2~$R%tRnq7ckPkiSeHU z#tT3_o+gt3p-ctm0E5y8r2>IMXcZuOz}4dt*p5b%K*~-7>U$ZKjtdf`VltnAX+t0w zv)W9uSO9pJMj0O$j6(o>7zBeQ0NQ*3-lnVhs$3g!A^0FS%5hBEJ4 l45L(JRvY7d;D@rr(jpWI1Om$MMTnlp5kSylWRMF8JwP+AOc?+G diff --git a/assets/resources/dolphin/L1_Cry_128x64/frame_2.bm b/assets/resources/dolphin/L1_Cry_128x64/frame_2.bm deleted file mode 100644 index a3c87e0a6de7d598766d94945ad530339de2d96f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 910 zcmWO3QA`tO007{-UTg2VlWR{l4p!RhabVLjJ?1UZrRd-G=&p`Qkv=S$$tsjd+zY5# zOlnxK6n0muwjFDVS+F2;WG|z-B|aEaLWKikfIckx))b6;=n~i*F-tb*>-YM>Py$hf z1*6D16uKlZ+#2hY7#aLxoMIV_jt6P(W}6dc<>Hu^O~T6vgj_qoJB^|`!{gT(Hyndk zu*s5}hDPLaKNSO&Z~#Bm2MZ1R4LUI<*f|E27LK91wpMox%#&iA1w6Q{y0@V5O`R8& z;bj;oo*zuc-=`nzpv28JiEd7M`G{R>fK3WhD$^tBuvEQBrG|!s-N1Yypcw5J^aUBT znp&PcazMLt!icV#>h91NNZHjtnVZwZK5N2Ld%@`C4g5Crfg*&U-5lsV@$c37;rHJl zmr~feKIp&K&tHnRT7losYBnNNauj~}iBh{;e~o2Qge6`F@7R=Lh;ry|9}rgsm(W}B zxT}$N9bLzrkE<3AZKfYM&&nR#QB>#Q2|I7_L#d3<1gn;1Ghq+m-!7$9bizL~rdi`k zX1EsS(5w%m52LR2K|I~;^ZGZDC*r|->HaY3sMBbO_u>f&?@Y!JTN(_5zjnm?`n!HS z+hwXE2{jYv`uW}ZLm1RW9;@Q z2Q4h62Pot!f1d1FeDD*zD}zBJ+Qqm*>WuU}745z8#Xd@B*!Y;)%V)E)l}Z{$lTE==&kxd&ed~{#*&6c=+@X{`Af>@ zOGD93Ti-4170XD=v*KJ(Y*>0t=(8kfrf{D!TLrGqUr}$Cp@ePuExPdalNZ`dsD-r8 in?r|oA0o2lG%nSK^SznFf@KnUs?wiQ+WepN-~R)iPDSnj diff --git a/assets/resources/dolphin/L1_Cry_128x64/frame_3.bm b/assets/resources/dolphin/L1_Cry_128x64/frame_3.bm deleted file mode 100644 index ba3012b7e5a476c4d58f100bc9a8ff16e28a31ec..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 923 zcmWN~-%lEM008j22iDH-bR?NuJW+c(CdW#W3JB1r=X;5%peN8vT34J+c05U-O*d;@yWx$5HozO?*(cecOtOb9P@p&`EO#ew@na zA3d|`+nnDFC=`LL@xGU#Q-aOc{WA1oLf2WZ0v+KD>BoGBmG*$@B-G^>o+sRlhn%c{ zB^Meq;rJcjFILI+SDWBn^`8+ebdPSo+!hr*%m5LwJ)tE#`=PJXX$c48%ScpWPsb@f zQVOwuJ!D@Cj&!+_o`4ceeu1?yH)k!-89iw`q$E4l-1|DzJP2Nt^&32BeE%E#Oxm$s z2ieoNC7@>%U1)NK2wxW(F;-!UygG|yqe6E=PHvkjw^S!ms{tzvl`b#BH>=s;DvesW z{{MF@+^631wT~~yA0y#YoZ0b*qoA)z2q9qZ0aqRHZuk_+>-$d*SBIEt3sFJQGYJFC zWNhCR8}l*DsiLtQeQtQ{C|7UnYb)}6(q+_1^G3L(P2WWTeSIFt>8UO995IS0!gRcK z11cncz;~e&6BZKJnWu9|^$5=M^u$mWD!a{DofF%g8yP?NIZlzXTkCs6_Ky+biB99| z>Mu_IIu|64`K>-zp*U7DU3^I?_GJVOb{mU~0g5BVauYn)C2d1Sm1#R(F|j!|x)0L} z$d#~l7V9&{xNnK5Me$?o9W7D3_R(tZ;}CfaMXJm6jE^03Ve)Rgh1WLt7(e$Gx#lqr zH9pCYq##jW<02w^PfxVq_dp}hKLDj-3H0=|&O+C^3_&TkO u!B>y&mLZ0pg#?B-%Q>X*d-qc`wX#V$vsWyO$N#~c`oU33;av~mpZ^Cg14kzS diff --git a/assets/resources/dolphin/L1_Cry_128x64/frame_4.bm b/assets/resources/dolphin/L1_Cry_128x64/frame_4.bm deleted file mode 100644 index 1ce28c7adb733388b90d31ce7bc1d5e8a5998ebd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 894 zcmV-^1A+Vj0D1#}QXrB{1tLKaqDU~1NDLxH1(HIc0P%!K!Q(>2$RZR721bbxq)8@` z08rs5fbq~&q!=1RRsjSSh}dBuA);uaPzV$z3jm-|P9nz~L_$Pm91uKUFjy2IL^!V}{~{_Hpxj^)EDQpmbsSgo z|BsPCQDe+Vl!7HlfWf2i4fGlXM37=i0zDXoCjrEPz&;RIhcNa~DO46E(13XeL%^cM zp)^PbAoe2xfMO}W=V}K8gLXqMJ5Il1yu>guJWSJm(@FFL)gveqLix52pBLIaS%2ATcSpn!e6j;Fr zqbQJMECBPsNKs=7J(Vs2kPoH;14w|L%4bqzjL>o?5f6?e_Hrr|l1KSZjQAc|0*Hvk z{xAs?+g=G8r&DRN@aDL`WVW!2lE(9w)Mhf(H@!KcpD8G72RMBpWM;Jt)BP zhEX6=7$iz!A7m)vL4ye*3}hHFz!1Z*IKx;|QifF+lO)Bo63%I7J2t2!!3I0Ryrg$^jCf zz#&webR9w>p|T#v0!0w`(IF=BpNq?gXc$rKau|pt26^C*gUd*kc%7yW>EmNf6W9Z91RfmQV4{nAz{7?M0O{Eq`@%&afrxd UkMj?d2=*|_Op+oi5P6M82x}2S2LJ#7 diff --git a/assets/resources/dolphin/L1_Cry_128x64/frame_5.bm b/assets/resources/dolphin/L1_Cry_128x64/frame_5.bm deleted file mode 100644 index 4cbc4968951c8b4391c7da7a5a90cd8bacf4b245..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 940 zcmV~$T}&DW007|Q?l?|B?+4tlrFdKqi%ys0T07Q2oE;wbWtuP&H<;dtx?j^-axY%(8X+J`L~)QX_U8Z9ud{ z$>+pFOveYBQn$Ug3U1KaRUQgZ(uYiM9tjX|W|ir+Fo9Y)A60z&614IFo~{;ZsB}8G z>lV(_4p<4=ne-g*jG*{J0+%vKmTWQl?=Wl73gna`S8yez4&Y5;6QHw*o+PKQCFA&v z>Pn8iUM(WsXR1McYhBpj5T?Ofj+i(w^$Gb{Ekwr=Is_ETi|tEL&i{bU`kj`qnA@X* zX=_^{16!p&47ymo~9}?nvBU)V5th52&`>CH?B68DV_ue1Z0Y|}sd=m`$>P;`B9+>0-W?$EqiBJTcng%6I^rVw7^)5R$I52k`b+v zsW|0TBE|14D?*WL1Uy4#3HSV72k*TeC^mH(2tjQ@ZSG*QJXb`2{^qI`xVFX0<8b;% zmP)O-#cf^Acbh{U8iCEDUN^R0h|ETfH%SLxgf?)!MN4N%HAqu`sbLpJEQee!=Y(jx z!pn)ifOOH=!N*2@#*^ZFd65lMo;VrOJ-3~%ToWkP`jF1zqr$1gpYtjm$<0xHG;zZM z41JE?rLW?8qYcC^yFGDEsz&U3u`j)=J7EwK?pjGbD?WV#l@A+cr3z)HQeU^<#l+Y? z83vi*2yFRRvr{knUi6z4wRpkx{Bko}zj<9_C`Bl<74CpCUyj%z`yJJ;uFFy3Sx93n z!EI6w?8KE2M@^r|^$bJ!ZueAwv+R3o43$Q@c59@yIB<}S(QgCS`Ah+`4pKN*C3`>G z0X8>G&wvTT=3%QIDWs|SP*p$BhI*1@^CH-6} z8sRvjnX=J@Zeneb(+ySPWJmCLTJss|d8A#+48_@t=YLJ&c;0kzj4P5zIS<^Ydb;0CV@8DJs>ST3f;!oz4Qq#njP8&mm z4|j#EhrP~gN6cL2($%IhK9#u#qYoY{?dYOrGj2g+A}O6SW-+iX4O`;o_kqBt3O?#Z zy{|zO%#%O^lPe|j2@k}h@-3jD)Eb66(Rs>kxy;_2fAg?}U|V56H2x`~b>r`_QP_da$rUj{+@SF>ERH?Ar$xD$G(q!Iu3& z=AhSa?sDtyU3N5nnF?^=HMz#d(oZh_^?y;GZ^^YCx=uUhb z*rJtC_V9yegYZ@iTzB(ns?pDdzih%yBL3cf8-4BqxkJ_++E*{}i9*;dVIpiD1mvdE z7c&Gv=0mgY!jG2}o8T!fF{&x$yNAduRwusRC`s&?xnPMca21^r2;Bl6PuVQ>DsI!# znEg$zz1fjM`hf1?8vwSUP?n3h=8o#pPa$u3W)0G_ZgYb!n{ZWFLE1pI%<$1kyQGe%XP&cIJ^jLqS-rA;12@yr8SeWpszx|iGk7$a@z0+K>dJOmfhpA^% zp`zMER+wYtqd7%o@3dATQi(Z<_=sxZB7EJih4VkW(?%qFfF-MZ1NfD~P3uoAnWu1Q=4FIwVrhUItE9sJ{Sg>VWYLoBNZiP&N0vi{VmU zlo6L93Ggo*x&0_h+v`^S;Zz!yC`rNTB(T^K%_YPMq-) zLHj8>d8J~H_}h~|L5QoC+jNmv$*&xR0`YM68=;J;7$Ad_T{PAmg|^g1?JA<;o3aAwlK%8M1$Hce;#> n8<&_P_`=$s9S@Xh?bL)CyiFcD^mvAzRDm-T{+8Av+L!(ZB@#`e diff --git a/assets/resources/dolphin/L1_Cry_128x64/frame_7.bm b/assets/resources/dolphin/L1_Cry_128x64/frame_7.bm deleted file mode 100644 index da28419fd279dac8ad18145dc9a1385df41ce7c0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 934 zcmV;X16lk50HOnsz-kZ(M1mp+97qUo5J?0QMUkjTDKZEx3I&0PKp`+d(7=O31fa+R zBLalLU{HW~;-FFtKon{cAQ;53p}~MeF;oZ^A}JDzNH~-MaL6i9ff$3v6Cr_7BpBKt z;9!voNZe?YD9})NluS(mB`Si%xCF7F4h91dz|kNai6n@6FalXYDpCm~B=#VYkV6c@ z1rwOWs1giFWN;_4kYJz^oJ@jCN(Kq-AyP~fLQw#qz-U4tg1{pOvVuV(GMJSH2|}bq zFbA=KKygr+Bt|k1875I^FcaA=&O*l;4ImyzvaDb{pZE-lg#gGJ#(^Z2Kuu!+{(tH6 zD1?(B#Suuo#z27>`~v+Vkw_vCi4H*aoQNC);R4V=HWdg16i;X16ORH4C=-~%G(eF( zl)wrw2pp1;B!IMqqBy8Tk}?k{H4qLl3LJuxe-qflLFJ63LM)mXlRQWx37|p2D$)hA@jDQ|FpU6CaMgfJ$u_5tN3?zy_5P3yl5jYkGK>(#mBQ7Dh z1IFYZ7?^x%FbWg`Bn%(KA_3%xz#`Bn5Ugrc2o#0@k1xQeV$eol&@zyK_`xOuAr_1R zO$CWkF#z$ALZCk-aYG;wWw_+90Pz$FFjwXe7zBeQ0NN=9sxtt3z`^ARh{-8f2*5od z$FVFH0*M604#WU->J5l@XG35XnzfWlGC6#_vJpg<@v2vclh5FmMFi(|1VjRFZo z2Z)3~12i>^kU%mF4k8H$;0uTzSc*WHWEltq67M+R1Io!1h|)9=C_n^YnCL&?9&&(q z*su_c6e3V>8TdT7hq1&_Gy(C5$|oKN=!4DxtPqgNA`%~`{vHo3B?>_iqF6G3L6G=A z@GyCz#vz2H90ns0uz$_x1O*HR6F|sHBvmT~@qTba*ubz92!sX&5VJGV2zeP6BY^^- Iq{he(P&v{|ZU6uP diff --git a/assets/resources/dolphin/L1_Cry_128x64/meta.txt b/assets/resources/dolphin/L1_Cry_128x64/meta.txt deleted file mode 100644 index 1b7d13dd8..000000000 --- a/assets/resources/dolphin/L1_Cry_128x64/meta.txt +++ /dev/null @@ -1,41 +0,0 @@ -Filetype: Flipper Animation -Version: 1 - -Width: 128 -Height: 64 -Passive frames: 8 -Active frames: 4 -Frames order: 0 1 2 3 4 2 3 4 5 6 7 6 -Active cycles: 2 -Frame rate: 2 -Duration: 3600 -Active cooldown: 7 - -Bubble slots: 2 - -Slot: 0 -X: 22 -Y: 40 -Text: I miss you -AlignH: Right -AlignV: Bottom -StartFrame: 9 -EndFrame: 11 - -Slot: 0 -X: 17 -Y: 40 -Text: my friend... -AlignH: Right -AlignV: Bottom -StartFrame: 12 -EndFrame: 15 - -Slot: 1 -X: 2 -Y: 29 -Text: Why are you\nalways away? -AlignH: Right -AlignV: Bottom -StartFrame: 9 -EndFrame: 15 diff --git a/assets/resources/dolphin/L1_Furippa1_128x64/frame_0.bm b/assets/resources/dolphin/L1_Furippa1_128x64/frame_0.bm deleted file mode 100644 index 8558f0f5bd29c5b76d6c222d7c85d52ab284e154..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 294 zcmZQ%P-0}LU_Kzn@PWO-hVcPEGKca1{|5=LKR6@^KBzs%;QWF8js=SaV=L1IH3#+w zjC=Wa$Lk9CK2LLbCB`HWC7-b4J-I(2#5*^aR#jFO37OA^}lG0 z+|rGb0(J=^42cT^7&dV~QQ$D>eXOc_{ROn zF@fr@3~l#CYqA>JnHt;}--j?xV@OjHWH4R8=5nZ+`OXCc$HW5%-!TYew6i2RaBQex qY<|G*k(2y_nIWe@{^$!y!>R_h^8##l82@^(f12R{$K;eQ(miEr(rzy}T-4@vmuCID3D#ndPOCK}D<0GEJ3z=sR83SEjo^TZX!IK-3> ziNINn5C^BCGGK6e{VeAfmnYNr-w(X}2yp-9oF4V?A^-pJuzS}*>FzTkF3V*x;2tB9J0MLWRxxj&o+Jp}~{F*?nWid2Jyde1>pZ*FDh=qV2UziX+ zED^wYUSK5g2etzS1TFe?zy&~B9{?XR02xcc=YWHh6b4apN=5XjI9KX?h!Y8XA|2#F`05eA_3Di@J)Nb`;Ym%t`9dA(X8 z2xA!h!RrWO7=TX<4>Yid1Rr#W1wpVVVUYvoKo7<-s>ojD@ogA9WF`fO0g{*xBzr)@ zLIoR`JYp*P;9np(2!R2B2Oz(L#EyUig8+xIdw|dZ_vE;P#t1?X1b+8pA;ANu0sP;Y H1;juD{u;iw diff --git a/assets/resources/dolphin/L1_Furippa1_128x64/frame_11.bm b/assets/resources/dolphin/L1_Furippa1_128x64/frame_11.bm deleted file mode 100644 index c91ed2fd23efc317a9fec7f9244f5ecf26bfdaf6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 698 zcmV;r0!94+0JZ}EfFeKmfrIjX59|Np9uLp{LL3k1U_W#*kbcNuF#vwR<^cS;@vIO) zd(Z#CF}ve9B@qX-JRV&5#tfn=$M69PXF`dC-_IZZ0Sk>X2aoI?k-rCw2tNFDh(7RG z1aJsF{OCR-a1Q{$x3&;_+0i069sK5s4--KE3qB7Yqr`7u@&FI;)>rrp1JI!=ED16{ zi|2eOP$&mL2sL^*JbV9r{(!)9X&8`x@E~~0{Q&eFj|1S45O~kx-2i$rMgb51`=XFY z{7@c~_&7X%AB}K5FZveQ1B3uPee_r!fBx`1X5jOjOTpql6S(+u|M*Be_!7x95WXpK zPLKW!4>$%49$ov|$bZxR2|R!U30bH0tOFAfIQ$(2*5D`?RtQVL>}ORfY^cfK@wo4#6MrnKs|tmU<0WTLH;XP z27sI(^o9V%@FvkNl$ajh@M$c>{x_Jwy*v;P#4Z6m2#gaC7YC$sfA@mGI8b;bBL5Hv zl>>p}ulg3lJ^Tm<|BIl5$ALfoSM3lU2l@jagnjXN6H#zK_4KgJKlR@P9)Acv7K8uq zuhJkK&-_qkJ@}dkx*z}k{!f7M-}}L#m&pO;={q_P6AHB35AhFN`%)TJ2HpaDrD0~`qk(=2CEZ z0f)!)C5Sy^VDfDd6n=P6c!CNF4@>zJeV5sIJm5g`(0}*gLcxQ{tS}II8iWXe@eB|E zXaM&oh#-OHDi9%W;4^FixNpGtzyG;6_}9XI3LFReE(ABI%&-txX8^(=_6Q6zF#`F| z%pwXHSUm6OQH)?em-v`H|E9r!(1@c42fM^ULUj;%Vgc))|K1Fy>Kb?a9=!ZL{EAT6 z1w0;~ugGj@5C#axa1?NQe1YPw7#HjRdiZJo@l^SIcmMtl`T!3dm;X)wiUZI8-@skq zAn}sVy5IRje7^$Wz<=Pt@uKiS==&f4;{o%GgT|Nt_kZdD@qLIqwsLTI)$jj*{(v4o zPrv8!!7vCsb@%_@{(}R60SDkA5P1JeqyPQ^#q2GC2cG?kAOw2~a6#jN_us+G|A+7a f<$u2eTmSC}k$89X5PN_N?%5stv_aF0B#1shQS}MQ diff --git a/assets/resources/dolphin/L1_Furippa1_128x64/frame_13.bm b/assets/resources/dolphin/L1_Furippa1_128x64/frame_13.bm deleted file mode 100644 index aa5353e988d02785f45759a024931abe7d580086..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 584 zcmV-O0=NAE07L@+fII=<4*+-rz#ahb2Y@^IANT|F;Rk?x!S}#`z&(Kbz(C*~0DZs# zKZbz<@kP z-_ZGk-W5OxmVe$YUN}5vRseX=FaHn!_#S((c=Kc+^8fh5iJ!HD#(O>ojswBL|KI=9 zyXAl$Ey0L`%fG>Z-}v0Uoqw17zfWUvCftXMrUwwc* zFdOIv=nzr}54)K^iv!95wSa!G^7S~tJCHd1P#$^^c)jswmy;2(?+ml*DY%6Tpaje?1R4~z?+C+R@JB71OJ%^j$i~g#Q}fdP!0e4@$U+bI{^s# z3(Nlv;0TA07lYt1>XG_?f57p%{4ekc#4b7D#lRpxNFUrDAR+mp#tHC`{fFbU28aM| z{|{g!U}Yi1z%rjedF|ox@OZ&E5DPI#e_na`JbB{LzW^w(J#=94pcIrmEe`+y diff --git a/assets/resources/dolphin/L1_Furippa1_128x64/frame_14.bm b/assets/resources/dolphin/L1_Furippa1_128x64/frame_14.bm deleted file mode 100644 index 837c6c71defd8be280776e7881817fc68276b5d0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 610 zcmV-o0-gN<0A2$B|KVUk{Q*P)Fi?2?0rvt2kDwoLFnIg{_W=j^KOjHgdBMT)0R98a zDj@jq1I#KQ_}~M|;Q;Id;{Xp%fIlVx`hWxc9=`B>kL(_GhKz#+o><3>gVG5B{Tt>8 z4;U5;!}CwXJVamY_&z)lfe}FLAMe0Dt_H!Nasoqr`-9r~&_@VJU>l>0Krj6W95O)w zgJlpe;FN#haR>y*&>wsRUrhppDF-nZ{sYsG|A@(;VDX@2Js+@oPGA%6%nc+tE0kSi zuxJH>Lj(tm3qcDYD|B!X2tYh=AoBP#)&UWmL9h@2c-lSS_KA8RbO;895vb?`+9^1J zKqL|g{UbmRX+$ywAO;Ttt3U_0LJkiYg@OTE2b}yKQ3wz`aF!;BgU_B1DFg(5A@ZVV zdEoJzgVn$w5#o2S;PHin%RrqGp+G;L5P3mC<-mYJ58@9VCwyLBV0pmdiSdjD2SN@Q z8NBEa$N2-wgZ=&g0qGDH4O%!v8W;Zm|NDc@SRg@@1ImB)|6{@DKq!m?&N3Yo|NZ%| zgUlq?4N0o0xY&yEj|qzoQt^FED>06iEa;GRJCuV4t{^!eXG#1C2(0)+vGdoW=5 z-AFJnC>}#T4`yhz^{KA$Q(_^4>({vevs<0996^* wNHeH?5Gg=DU|{j6KxP1O`UB1b0N_Ay5Dfv)f$A(Dg8+f^cz{o!766F@+?Nvri2wiq diff --git a/assets/resources/dolphin/L1_Furippa1_128x64/frame_15.bm b/assets/resources/dolphin/L1_Furippa1_128x64/frame_15.bm deleted file mode 100644 index 4cb6e53325ca82bc1ba52fafee1ac42a1860a50b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 719 zcmV;=0x2edpMFaQvP#()oKH^LrM60rl^I503^{{iLsHy|)^ z0P@TO$c~T*6avV`F91BQ2NQrnAQ7N>U;*j-2b3ZK1#_7gbPDB+U69S%_nXL) zC-VRZJnv)jfk2!FPbpLAzn>Mrz?udRAJnuSUypGxMd<{tLA* zrXFq&{(Uec2t8nMZU^Um&;LikL=Pb#gU11WpyB_e!8i^9U?f3-fro8?!+z!9@{kxt z1Au`*;1$q#@xkK^fn+M+^1?tt<^J!N#5gpGLDXAf4;s)scmMbcfMkji;6cIZe>Vs` zWDyAj0>L;3j_JTF@t8;f0&ac3Iqm7QxJHZ1_AZpe1D;lp}-`8@&O7E zf4~2Td|(6-v^gM1u%*MM5rYZhlmIgItVWU zM*v6&Tz~M)@I2@cw)t=ZxC7z zA2bOE00<&62h@N_MMD3>!RtVd0SE*!_y@oqya2gi1HuX-s!%Wq5(kL{%Bg_BK>)`F z0hGWVuplXB7K?0mW9)pqY1KH}Ok%5B<9%eKGis(y(512t1 zV1o5Im^_lA7$AJa0isACd4S9wN1+GifZ8p>2hD&2D1rsA;Y>{dhy%&&jsbR%2w?#C za0n!fqF@vd!9nLh_()r-01$a420;v=I8aJZka|FG^IL|R3GfZ_(Gk1Pbd4kST{ zMsfi11Bg7X2ZO{(1R}5@neYJv=m)TLP6L3zL7)(Ddpk-XLm%(`ia3bCJkmrDD-itP z9{_m_j2T6gp61a(gZ{ZM1zPgjQn6h8mzDgPw@^3 zz}F@lG7%UECQ4EVmjV2M^?I5VRtF3sqyij97+5^uQ9ycvN8SY{0Kl$5kq4*%+69tk z2m(_@KO~R>1LeVjgXn4{VF%kx+rka7e&8S}(IJomV8H&oCqy1_6AdOzA27gr0I%$+ AWB>pF diff --git a/assets/resources/dolphin/L1_Furippa1_128x64/frame_17.bm b/assets/resources/dolphin/L1_Furippa1_128x64/frame_17.bm deleted file mode 100644 index 02af0bcf325201270bdfadedbec283ef88d23ca3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 400 zcmV;B0dM{R0E__u6hI%*fPX>)+z)6lKcGPUz(7I#PwD_arU3pz{4gKTMmK;k_{2d3 zDe-;_GJ(K9|L_=t*8Igk0EA=6#lS<%$2b$10XzUQ!9pRyf*1$@!z2$7S_H`=&>jM< z!s7x5oq?9d6#|P{0)!VvvyBn0Ld9!N0es2xXIKD?hHNkcf>bJ^nHs1)f5le^scc3> z4<_k=4fG(wLZAU;7sx;mVIRmiJ>nnuh!AlQc;G$qANZIMe+_j29wYxF0t`96e+YOy z`5!qud`_ZP5CH#(h7U6`01AE&KlnW>;6u0r4{JaeiG>Chtc7q; zL13H+9^e56h;T+EH$XAiJOEMf2b8WMQ;(nw0EJZm6mSf&0DG5>a9{?&7mP;0180j7 u0|XCSfDp`V06IWm_y=K9vXGzv!6FDmf$)kW1cS%`P~bR%6du3>^KMRlm@CSfA0hhpbq?iBy zfrHw>1U~>!W5JH5eJR~-y#2rfd}y7oJamf1Q>IQc^^4Dd`_ZWV0xQm9$)Z!SHb(hAEy8x69Iam zOBw%{f(jT9T;K?EQ}fZl!RiVqqNK+He{2K-RC^G)$P4e(0vmi)@u~tG3IP}gy-;dn zF%XCWtpGA66d7V;Ljw#DSa2}=fCL&L!5EMNEFJ)0_yN)z82IHt7aRxi1V9;pFHiv4 f;sD!#KdAuzRg>(&`v8E*0s8e2eMA64*bo4KNFID{ diff --git a/assets/resources/dolphin/L1_Furippa1_128x64/frame_2.bm b/assets/resources/dolphin/L1_Furippa1_128x64/frame_2.bm deleted file mode 100644 index 94357802fc84c61ff65389b3e8e592c882f7dbf8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 351 zcmV-l0igZ?09ye7cmu#50PqKZJOSVj0C)qy9suwMfII=<0ssHtVC(QeU=iSb&HzL3 z2i!OWI01zM&?Z0!-~kRW51RlG<^cHxAW;AcDnX(E78q1Oe8BN!R}iTkwCZr_kd6iL=zu`d>--(%?dhyEu35Rdu>4}3@dMx-zKoNNH~<27iUx!LC*lA+s1!%z z06$O$N8q_n<%^kOb)bFk~5wa1*0=zys#M xtsVp)fDe)2|J%Yg5C*>#0W3lQWN;tNQvg>1!5{>@3%Md74DbuO_)Y8hDgXv`km3LU diff --git a/assets/resources/dolphin/L1_Furippa1_128x64/frame_3.bm b/assets/resources/dolphin/L1_Furippa1_128x64/frame_3.bm deleted file mode 100644 index b0d0e691407afc3067f1adb44437407fb75e695b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 324 zcmV-K0lWSI06+l%cmu#50PqKZJOSVj0C)qy9suwMfII=<0ssHtVC(QeU=iSb&HzL3 z2i!OWI01zM&?Z0!-~kRW51RlG<^cHxAW;AcDnX(E78q1Oe8BCSxu9Tz!-0d`03gv02*iL9VDJM6zz&eu$Iu1<*x)~l&@y86 W01ciX4Y&jPkPqb`gZv-h{{T3j6M%aF diff --git a/assets/resources/dolphin/L1_Furippa1_128x64/frame_4.bm b/assets/resources/dolphin/L1_Furippa1_128x64/frame_4.bm deleted file mode 100644 index 3413e507294a89aad208259baae4b7e14c2fed36..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 387 zcmV-}0et=e0Dl1hcmu#50PqKZJOSVj0C)qy9suwMfII=<0ssHtVC(QeU=iSb&HzL3 z2i!OWI01zM&?Z0!-~kRW51RlG<^cHxAW;AcDnX(E78q1O89{;T1P7!C`45Bx)GV+V zJmMe^SP&IBa2WtSlS^`dND1uC5CO)4_5N5S{2t}8859eE4|oLt2{}X@+NBsi_q~snrkNk~DU-LNF0ppx47bE8< zU>E)9K=FoP%)$>&1^fm#3k0eihX3V`WjVd+2l)@g z1Jtar7(C)2rw{?afT_cP$N}t{TakIiA_Yj$KEKNZpM%`CBO-xt0q%z*kc7wK9|ydG z`3MP@;vNi1zzAX=_=^y~kbedK%>c|wLF17B#NYxE|3Ja%IR}m-|07Zt{LVH2c;^d6 z$oa|G1^;>wJYkqKFoV;9e*ulc!77Jg!R7x4mw^0Ji-f8S-5`1&jy*V#LDb?8Q}K8q z@#usO7z!L`{$2zV@Poz&4u%8IH9tm)_?6E9iUx!LC*lA+s1!%z06$O$N8q_n<%^kOb)bFk~5wa1*0=zys#MtsVp)fDe)2|J%Yg k5C+%*{KOyzM*;lRFa>ZN5&%oUyOJUR&j7oxgxI3xgWa$Lk9CK2LLbCB`HWC7-b4J-I(2#5*^aR#jFO37OA^}lG0 z+|rGb0(J=^42cT^7&dV~QQ$D>eXOc_{ROn zF@fr@3~l#CYqA>JnHt;}--j?xV@OjHWH4R8=5nZ+`OXCc$HW5%-!TYew6i2RaBQex qY<|G*k(2y_nIWe@{^$!y!>R_h^8##l82@^(f12R85=KBE9Kzz`}J1UMKzV8BQKC}{+U0724`4BWs}hy&t)1xbKDEC5uH1K<*& zRTR5KMyg4E8C8uffcQXg0QiI$i}?cw%4DP;=sp7je+RS>9*_@-1=%GK{KMc8_mjYZ z>eUyM1JC{s5HN$-3_$!F5IoR=C?I?Pf%_N-od6!d0-8@2g)j%qq7d{bKt5`UGE0a) zbq1q=o7g^JqF)NVgXRh;0{y@RP&u1@&}{^;1L=Bm0DZv)qLLs4#wQR6U_Sg1ba;d9 hyh!N)A-cc^a0l^w00j;Q@jIEc5A$+^{2$%`d;q4lY*+vQ diff --git a/assets/resources/dolphin/L1_Furippa1_128x64/frame_9.bm b/assets/resources/dolphin/L1_Furippa1_128x64/frame_9.bm deleted file mode 100644 index 114b26391f8643130725f03b5209d97ec8e791d7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 312 zcmV-80muFU05kyrcmu#50PqKZJOSVj0C)qy9{}J$VSx>WL;?FSkOOc32Y^r@AJ6zg zZx9F%f(HPHgW~}KfkyzNa2Lb^4S){_RDtggh*$vU0tdhTK^Oy^5FZeDFdX3k_{WZp z;FNgzz<>q#kb~H^GXOFDBxnd6r_zQGf^u~z?r{gO9X^zK;9Vc&1y3g)261O<@i6EY zJS-vXAoYj=mJsj|Ldq8l&;blEKCOi!lqlUm^1wnt34j}{f)QXGEdl`)1Ej(@IC8r1 zAYk=-xgo*uNEd996aa-)W57N~3{_FYl~hqM61xf{{eTGI0o&ki(y)of90xeSK4K_) z2dQviVEsC|Jjdk(up4#=_JONI0s9aqI03v1&Y%PP0L{2Vx5I>AjsawYgnx+xAb><* KfO)#k4i8}640;Rz diff --git a/assets/resources/dolphin/L1_Furippa1_128x64/meta.txt b/assets/resources/dolphin/L1_Furippa1_128x64/meta.txt deleted file mode 100644 index c21027e49..000000000 --- a/assets/resources/dolphin/L1_Furippa1_128x64/meta.txt +++ /dev/null @@ -1,14 +0,0 @@ -Filetype: Flipper Animation -Version: 1 - -Width: 128 -Height: 64 -Passive frames: 8 -Active frames: 11 -Frames order: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 -Active cycles: 1 -Frame rate: 2 -Duration: 3600 -Active cooldown: 7 - -Bubble slots: 0 diff --git a/assets/resources/dolphin/L1_Laptop_128x51/frame_0.bm b/assets/resources/dolphin/L1_Laptop_128x51/frame_0.bm deleted file mode 100644 index 5eb2bdd05d20de1a4abcae2b7f79aa7c3277ec5d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 555 zcmV+`0@VEh04D+fYy=2+2pBoRC4hr~h6E3)J|YABhlAdM1b|v86Q~@(9`Xo81L05S zKsW&Rh(sVCdVIJJB?p8^AQxN2cn%|B5sV&r0RDgf_xrsr92X!E*dY-xvCu9Qh%nrZ zMUV;)&^|Tr_@77~Pzs1WL*fr2Fn|W6EfD&w$R2e-xP#8?NC%Yw9$tVrhtrV&z5~y} zc4QCIKky%jJa9ns%zJJi@$>V6=K+9&$|3vSKz^US^?f2Z3xo>*QY*ZA2lW2mtKzZ1 zdI9G^w`0ZQ{{L5l<@|y0A?fk_Q^DvTAb89a1^1i4E}#GPb_Nd{cE zZ!j1bJbWJ*_&qK_@&9-h1%UUE#2(1;fKQZz{#T9C|NO4}s3`KlNMIa}KRyR^-~NN+ zLS>bM_{kgn-VJo`{ek0p2b5v`@_c_Y z>ix=8J|d8T2jPDQi+q9S7zd>N{($os2n@d+_jt$t0P&3i%YY9*`8-aeU;nLyfM5as ze}&=?C|X8AGkb1r2w8lz+(spoKN5nRZ&C%<-m;OAi(pI1I7&?Bph>K1tKx1 tM}R3V1b?6kAON-q4wL@`ygXn6KZ$@qLm-enL;$`3`V9~P;12+J0DzyW_2~cr diff --git a/assets/resources/dolphin/L1_Laptop_128x51/frame_1.bm b/assets/resources/dolphin/L1_Laptop_128x51/frame_1.bm deleted file mode 100644 index 210f0c918cea285c191659adaf8d55e740f72e12..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 557 zcmV+|0@D2f04V|hYy=2+2pBoRC4hr~h6E3)J|YABhlAdM1b|v86Q~@(9`Xo81L05S zKsW&Rh(sVCdVIJJB?p8^AQxN2cn%|B5sV&r0RDgf_xrsr92X!E*dY-xvCu9Qh%nrZ zMUV;)&^|Tr_@77~Pzs1WL*fr2Fn|W6EfD&w$R2e-xP#8?NC%Yw9$tVrhtrV&z5~y} zc4QCIKky%jJa9ns%zJJi@$>V6=K+9&$|3vSKz^US^?f2Z3xo>*QY*ZA2lW2mtKzZ1 zdI9G^w`0ZQ{{L5l<@|y0A?fk_Q^DvTAb89a1^1i4E}#GPb_Nd{cE zZ!j1bJbWJ*_&qK_@&9-h1%UUE#2(1;fKQZz{#T9C|NO4}s3`KlNMIa}KRyR^-~NN+ zLS>bM_{kgn-VJo`{ek0p2b5v`@_c_Y z>ix=8J|d8T2jPDQi+q9S7zd>N{($os2n@d+w|K|?0P&3i%YY9*={!!OU;nLyfM5as ze?{UCCL3O12hu;lFCYWJ9su|N)o=Bl diff --git a/assets/resources/dolphin/L1_Laptop_128x51/frame_2.bm b/assets/resources/dolphin/L1_Laptop_128x51/frame_2.bm deleted file mode 100644 index ff2851c280846f4470944424c7acfaee9acc2d6f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 560 zcmV-00?+*c04xFkYy=2+2pBoRC4hr~h6E3)J|YABhlAdM1b|v86Q~@(9`Xo81L05S zKsW&Rh(sVCdVIJJB?p8^AQxN2cn%|B5sV&r0RDgf_xrsr92X!E*dY-xvCu9Qh%nrZ zMUV;)&^|Tr_@77~Pzs1WL*fr2Fn|W6EfD&w$R2e-xP#8?NC%Yw9$tVrhtrV&z5~y} zc4QCIKky%jJa9ns%zJJi@$>V6=K+9&$|3vSKz^US^?f2Z3xo>*QY*ZA2lW2mtKzZ1 zdI9G^w`0ZQ{{L5l<@|y0A?fk_Q^DvTAb89a1^1i4E}#GPb_Nd{cE zZ!j1bJbWJ*_&qK_@&9-h1%UUE#2(1;fKQZz{#T9C|NO4}s3`KlNMIa}KRyR^-~NN+ zLS>bM_{kgn-VJo`{ek0p2b5v`@_c_Y z>ix=8J|d8T2jPDQi+q9S7zd)fU*-=nfPl;K(|3%2;13wkEVuyk_>;uyHUIk9NCp5O z=ipu-@_;&s_8>C8PXX!wV-5M7KmosB0qMYmU@$NcdNc$catJ8!U_&$f|DbDAoxwqa z1P7RGVC_?qK;aMwKnPGn_XmhnpcBX#3}OKDiTnZTswn_GxDlL$7#?y!c*7_JgN|$f yq((pd{{Sg21b?6kAON-q4wL@`ygXn6m;i)QQV9dpKnvgxqV6=K+9&$|3vSKz^US^?f2Z3xo>*QY*ZA2lW2mtKzZ1 zdI9G^w`0ZQ{{L5l<@|y0A?fk_Q^DvTAb89a1^1i4E}#GPb_Nd{cE zZ!j1bJbWJ*_&qK_@&9-h1%UUE#2(1;fKQZz{#T9C|NO4}s3`KlNMIa}KRyR^-~NN+ zLS>bM_{kgn-VJo`{ek0p2b5v`@_c_Y z>ix=8J|d8T2jP7Oi+q9S7zd=~{($os2n@d+mw3ni0P&3i%YY9*!8}f*U;nLyfM5as ze+A+XC&1OC5*(}4&^j2IrD@%l(0sL_NB&-4F*txk6Z9t=Pp zVX=d?PDuk641i`RA^U^GDo_dJ3`jupiTnZTsw@CJxDlL$7#?y!c+4OWQ5i=z1mYtY u08SzwDE~kdV6kin9Vh+?czD1BE*Jo$q!I_HfEU0YNdEx5fDZt81KV6=K+9&$|3vSKz^US^?f2Z2m}ZOB_X@Vpnp&8`o1e1 z1%MuO3wAtSKkxN;K3|9z0v?|q#XKH?^8P^P3IhAh;1^H-`nv-Mjq(KyNDl-3I|rrs zwl~-e3?4oYjC>xKAb9`03j)A<$YKv3{xLepD2BU?eaOM<1U9x^MqM z@u4!x!Te;6{_h65cmBZfy#vZH`xCg|;J;6Iy1jCx1Ii5h69C_rj@!Ir{{VQ#fn~r4pZuODQLq2j!#^Mp z08m0N5P3i%902I7#2!Im52QX|O%`x~2mOBsrvecOfIxxhKUoA7Rxn6_JikBq8r0`- zP$0qq<{KD0ROFB_P{am8f*-g%LZtwn1I7&?2b@pf4^>dYC;;-{Msg5fdC39eFpv@s yIj{ne7{CN6E(Cv|3P!;J(tqHWhl~J8@jwU&WD*CcfEU0YNdEx5fDZt81KV6=K+9&$|3vSKz^US^?f2Z2m}ZOB_X@Vpnp&8`o1e1 z1%MuO3wAtSKkxN;K3|9z0v?|q#XKH?^8P^P3IhAh;1^H-`nv-Mjq(KyNDl-3I|rrs zwl~-e3?4oYjC>xKAb9`03j)A<$YKv3{xLepD2BU?eaOM<1U9x^MqM z@u4!x!Te;6{_h65cmBZfy#vZH`xCg|;J;6Iy1jCx1Ii5h69C_rj{Cf0{{VQ#fn~r4pZuODQLq2j!ay(p zpoCr^@_;&s_8{{gkbKy}2o`XF2mD_kd2k^d0qBF!ezFKGz}h1-{QuxDKqrte7{USP6ZiwwR8atVa3eVgFg)ad@qV6=Q)6b$|3vSKz^US^?f2Z3*-v`QY*ZA2lW2mtKzZ1 zdx7Ubw`0ZQ{{L5l<^F+iA?fk_Q^DvTAb89a1^1i4E}#GPb_Nd-_`?zd!2eFc>3%JV zd|+Vl@O)$7^tl7a{oq&@1KvXrdn3jHK2i_)UN=ks^1Je&z#uRj1cm{~oxMe%;`DU?3p#-zUfO zKCj%RL*gk25Plc%c(=$Nae#VH-{=o9fPl;K-*=3E;13wkEVuyk|C7Y(HUIk9NCp5D z5R1efPzO;S#2#bv51SZ40?rTs|BK`gE(9ZhJrH^i)V6=Q)6b$|3vSKz^US^?f2Z3*-v`QY*ZA2lW2mtKzZ1 zdx7Hd8U?!^FCX{%ydN+03c!#Y2jZR&K=}j4Fn}+--T`#~|EsVtc#p;m4;}~lb`MMO zY-cza2t0fr82CLdK=J=rAQSQU`^sVuWO(ocV|@QB#_50lSAJ9&1O@}}4UZ#_&w<@H z|DgELnPvQB3EXb*Yo~wg4;#=tzwlQXU*Nw_ce=fDr2>b6|9y#oZ_DxfcY*2fzk|V^EI(Qd|iCKomd$Y!Dq1gD(#l0FUBe5D>^D2SWfKfIfpn0C)qy9snRwx%a&Q diff --git a/assets/resources/dolphin/L1_Laptop_128x51/meta.txt b/assets/resources/dolphin/L1_Laptop_128x51/meta.txt deleted file mode 100644 index 90cdc5ce9..000000000 --- a/assets/resources/dolphin/L1_Laptop_128x51/meta.txt +++ /dev/null @@ -1,32 +0,0 @@ -Filetype: Flipper Animation -Version: 1 - -Width: 128 -Height: 51 -Passive frames: 6 -Active frames: 2 -Frames order: 0 1 2 3 4 5 6 7 -Active cycles: 4 -Frame rate: 2 -Duration: 3600 -Active cooldown: 7 - -Bubble slots: 1 - -Slot: 0 -X: 60 -Y: 23 -Text: I have to rest -AlignH: Left -AlignV: Bottom -StartFrame: 7 -EndFrame: 10 - -Slot: 0 -X: 60 -Y: 23 -Text: but not today -AlignH: Left -AlignV: Bottom -StartFrame: 11 -EndFrame: 13 diff --git a/assets/resources/dolphin/L1_Leaving_sad_128x64/frame_0.bm b/assets/resources/dolphin/L1_Leaving_sad_128x64/frame_0.bm deleted file mode 100644 index d4cf85bada7670d1b6d1fccf281fcfbea3533e1e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 514 zcmV+d0{#5~0R900HUJ;s{|ER#!Tt~MGXFo|{elPhAK?E7_&#_a|NJ~3bO0E_0r~&_ zLc#fgf+_1S0_hxEt6Wya)gUQR*)_1V+FO z7z#dS009CF48J-Eq+kdzV0?#$7!SetgUT2Wj0DDM1_$24#ZZHT@8NXYz`)}eH^;O4 zln0Ff9!T>Cm&*HI2dBgT|Br*uiEkH{03I;_V1S4oIgOnh9#a7PNI!D&c}#EsF#s4m zXdk)wKRg0)2jqdcKp~j}{2rI#`GeAE4@?L>Fc9#1l1u)|5J30??Qjs2sGv81IbH$f z7z30R03sOR9J>JWrT~WkKM=5p$R2=lpaaN!a14QHU=%PA$N}YmfP>co!TVQZpz$$? zJmMHIIHpPw{5g^&!Q(`-KaPz=2dsk)nViA^!vf9bU>pE(LIL9Kf8&^R9y0LUa6k1=GY_X{KMX`JK=Ot&20jlPVfbvp^#_wQv1j4L E03aFJ-~a#s diff --git a/assets/resources/dolphin/L1_Leaving_sad_128x64/frame_1.bm b/assets/resources/dolphin/L1_Leaving_sad_128x64/frame_1.bm deleted file mode 100644 index f1f0e89f0bf92ab9618b0b7416579212bc000e05..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 526 zcmV+p0`dI;015&CHUJ;s{|ER#!Tt~MGXFo|{elPhAK?E7_&#_a|NJ~3bO0E_0r~&_ zLc#fgffO%jd@DIoS1`k?5Jg5g@jzA#h^#`;X0Srf|yyOuW z1P%chN6f-ufGCIzOYxwJ1N1&T03g@_@;?xMU?ww55FB^__}Eyg7f^ltE}MHq0vrz` zdwV~*Nz?)(fWV{B9$zc%dTI|phyVW{2cHt(x13-k0P%=1v0-uyo>ePBWB90!&h3?!2O!Q0{p7_dCC zpkcTON(=%R2tDHf@`AtwMH2^>WEM2Q5a0*m79IdSg=IhomxF&H2aW?^2pSj&WB~FK z2t9BdAGLNm4-+9s0px5X$wEJX;sgu4ycA0#_~_I?d*){_fOx>OWpW>gJhl=j(JZhs zIS_oPAn~AVIgrQ@WOy9TXNTbjqrf6G4CF=*s2T-=A_*D<3CuAb20zRwDgDCDW&;)c z9#}s>cwiRH#v>4rD*!yO6Tj~B_v4;S=3w&RKp|EP2fGDB;&>?ZN+2|6;Pj2xeUtqL zWB?^WkORgEVDNb)aR_9BL;w?@$HC%4AAww;egp9H90pn$gh3`M(U>2GBG52-z$4)C QwjYMfA8E#S?_px7MM3xQx^3kK`}Tiwm85(F%jJEqWm+YG_<#TL@Okkq`+Gn<;s7xL5Il1m zIy_WAau3|UL_ahkgU*5bpV)!-;1i7eA94kW1N?u6bO-o8K?J8De83Tc1R#9>;NT$- zD*zy&ZL*Z{%g2S5Vf!RJH(Fg^1i1{$C69#S9!3=ifHE?@z?KL|d% zm=7q30jLxR6_aoeD=-0!D4-k$AOOf@fdj^70APEc-T}a0h&<2nK#&8_{@{_1gV6rt O_dmJ4&+Z;K;UD0o(vidf diff --git a/assets/resources/dolphin/L1_Leaving_sad_128x64/frame_11.bm b/assets/resources/dolphin/L1_Leaving_sad_128x64/frame_11.bm deleted file mode 100644 index fe93787f2dbaa45e6546910e547ae553947e3827..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 294 zcmV+>0oncm03rbZHUJ;s{|ER#!Tt~MGXFo|{elPhAK?E7_&#_a|NJ~3bO0E_0r~&_ zLc#fgf2W^ zzlsC&KlAbX1LA1{SLOPl6%XYHZzwnC|36wsz&$_tR*7If0CA6l+TIU1fDAxH4@mfb zwd3Hq#h!Rdepj07Hd7$hhjgMtU=fF3X~en5Nx{D^;}2uMGSqz}6Q z6oL3I-A zU;%;e5dbi}_5kv;02n@jLBL`F3=cTO2p%&q0|VUt@Hjv{Pw)>IKs?Xz!Vr2N;1(eC UKf$bFAA@-x-vpF diff --git a/assets/resources/dolphin/L1_Leaving_sad_128x64/frame_2.bm b/assets/resources/dolphin/L1_Leaving_sad_128x64/frame_2.bm deleted file mode 100644 index 3050ba38f22ac4c8f1ccac17830bfd3455abaa1f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 542 zcmV+(0^$7u02%@SHUJ;s{|ER#!Tt~MGXFo|{elPhAK?E7_&#_a|NJ~3bO0E_0r~&_ zLc#fgfqEK2tVN7p!VtSN3MM3xQx^49f z64)Ex+5O6Pf#{Dgd3>+6?yMXhUl0HOJ`X-6zi()A7Q!F{5D^2%F|(u1K?EO(2ku`Q z510&q1P>Yq?tcyh7GNI!0XUP0A2I+StBHOf`cD)CI1M}S{K4z82c85Tm;~`aJU}5N zktnbL_27Vp;07X5KzIOhzySsw7z30R03r~1zyO0grUi(8FbKdr;vNhIgjQ4ld3Z17 zQuuH994Z(rWB~GjSO8_<&)6`WAGLNm4-*)N#sm`_1pknkC`a(-Q}Ym$hXWkAKaPz= z2a*U&1BZmnApl{4X7eCHm@J9`{)fg1Wr2~%gXB0U2jV{rB8pUboX-!!Za5?fLI|Tk zutZ`s3Ixb-K=SwsO5tX+0Z1N^i9if(nT$pu9#Ft}&p4aU-;Q}RnS;_`1s{JBhs5wv z=#)fI-{HV9dJJyI?4Rf}AOR{5f*u_i_v6Vwh(jb2AOM{QKN~+1+`sTY0r+`^qX(2< g0F@XN!2B@~xkuJylMpDtt%u>W2h<)+(#4;L5FzQ{VgLXD diff --git a/assets/resources/dolphin/L1_Leaving_sad_128x64/frame_3.bm b/assets/resources/dolphin/L1_Leaving_sad_128x64/frame_3.bm deleted file mode 100644 index 0c0c832385caef6fc7bcbcfb485699100bc5df3e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 557 zcmV+|0@D2f04V|hHUJ;s{|ER#!Tt~MGXFo|{elPhAK?E7_&#_a|NJ~3bO0E_0r~&_ zLc#DX932A#1cd|R;s3{j^YQ=BgYt0z&jaQHJ{~{+LJv#8Jg^aQaR3H_4>~|Rs0U$k zhyZEk^9Q&19#!;ti_Sq2fCEG%H}reVCIIn|kcR$WeFRV*Fm#Cn+&*7890R})f%xD) zC?W^G0%J76d|D@A(0t2hK6TgA2?39|xQ;9$pFj8IWHH9v~i2 zM@g=T0Ca$QtxgaRI$@B%fYJw%0N@m0c~Pj8KzWQ3h7T;(XEF&a8l;b7BL|d-03q_> zhmXM`LKi!k%<%jm{xEso1LY8j#9}lG1PkU5Kk%TZ_Iov$3_$bXd&q?>#NraA0PhEm zcm3Xe{Btk>g3Fk$E&sa(L*jTSr|}ew|9~zm3~syZpXd+5e;NdU9)1rbjv)*`njmA~ v^Y|&xz}PCWh|3 diff --git a/assets/resources/dolphin/L1_Leaving_sad_128x64/frame_4.bm b/assets/resources/dolphin/L1_Leaving_sad_128x64/frame_4.bm deleted file mode 100644 index 5e74ea12ae20161d809bc90a813fe9be78929a4a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 488 zcmV<%yeeI8=-kVJ3*^S`6sVK7#T|)H4`lBX0Km`YpD=k) eBhms0%n#(BICwm~5%75159Hf0d4Y-ri$9QvY}0N4 diff --git a/assets/resources/dolphin/L1_Leaving_sad_128x64/frame_5.bm b/assets/resources/dolphin/L1_Leaving_sad_128x64/frame_5.bm deleted file mode 100644 index 5c556cfdfd3f46173b2f6eb279797637000bb792..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 469 zcmV;`0V@6h0MP*eHUJ;s{|ER#!Tt~MGXFo|{elPhAK?E7&*B~r-}nFi5g>enh5-l% z@AeiCt^hn>@SuH&L?4q6{r(6Kvhhd3?Exn!fI;JU2bKaZ4=(^bzu@tt1ImDQAbOj` zA44Jsh`i(x5P8qaM2}g7z@BmN82K!NwL zu~Z_U`}kcp@_>0y3GeLwR;45T*k7|G~gQ4?ejxO?ej~aA`OqCi4>)!L2OvL?oMYhjxF|&aL-?6Ppb`W58`K_LC<{bSXD1+1Da2WmI9#{ytJ%6C_|I$I_K!h9E093Gir^bE) z8~8k=5fFP5NBsu}l)_*SVz~bS=b(xY*a$y}7=BCQ1RsP1e>4gR5Ml`Oz)WV4AbspC zRS2j(EFiH5pTg<4lmpU!Ul4iwKe$!mPh|GQy|NKM%GztU?$+!oVnoUsPU@Z1)F@+QZ zfW%rd0CC0QF$n~L1IA}_h&D!LGns?j@-TpS(SV}>;4j1;d78*Pvq$ovNCD|0@eYbc pg)9Vo9*2KBl!o7_$TtjQ9y%<0*Hd~*c<== diff --git a/assets/resources/dolphin/L1_Leaving_sad_128x64/frame_7.bm b/assets/resources/dolphin/L1_Leaving_sad_128x64/frame_7.bm deleted file mode 100644 index e992d75c7a000c28168c9a683b2cd8a00694f195..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 486 zcmVMe+nUkfkr_Q z54bo%=P;NL-~7T&1W+FkKg4`LkPiSnhwcv>)B}Jo90bN`0tdAy)+X`TSgH|Fef%z) zc|twG=j{IFD@gXFKtbj5zSpv?62N>9gMSa765qG91I{3MC=fie8#+8xKGX2|$Uk!Z z5d6SoA`p2{KXdyKKKuf62h@RLK=zC)#2=7|CIEgya|g5tAF#*{3?3kYgag|h0KrE< zqKpurgV!_(8N37k2LT8@xq=M)6-f(h@=Z)VyHzy^BjPDE}MBlzG09LXZI;uN5DM3 zSK9VfqF4`y|NkEclnaS(+u8x=5CMn?ppXp4&W{xj%o0H${mb-2^FkN^aFQAa?tfwj z%@RT26O8;HGC_E(NFOsme@{P%ax;)U?2$lz(IS9?Q;@3zR|h04abzmBIiZc>&M>KfoDZ!RJH(ClbI9e8>TG15^G3%0vJ^7ruTp zKbSnZfCH9ZJRTo}A6?7`ltch?8H9p?K!I5|0P?c{5&>{BqJVH1fC2J9`1CSBf#Wj( xFg?%e02l`WejxKdwq^l9kOR>EfC2=Jd>)7N01*Ap=l~;opVb0D0)7$wKm>gitj+)c diff --git a/assets/resources/dolphin/L1_Leaving_sad_128x64/frame_9.bm b/assets/resources/dolphin/L1_Leaving_sad_128x64/frame_9.bm deleted file mode 100644 index 6ebc55c16edc741797d5b69ba3482afcaa5896cc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 317 zcmV-D0mA+P0678wfENA-U;~5vAK?E7_&>qz12^~VKfwP7_&>q(KV=WVAA9f^{lWPF zf&Bsbe*OJH{txhfgV+2IoH#tM{(yY#!R5a@5xv3ok1=`o{mA|2^UM2R^@H?3|t=KG1jt$I{^1IinM(At_Me}}*z9sqgH@)V4?JpJG?0T4WF z^v%bpKYjQoLb^XRA%o9<1mI^u_a76F03Utzi39w9hI9w`K0yShAbkJPf&?IZz&`*X z59gkL|NaB+K8_!lKEMUfAJ_oF#^A1P~1p07L#i-~^w8-Y_m%fIZ~^^^^nOkS|#{89)P1Etm*= zm0x{IV1C`3Kw{ zUl05g9z*sAmGglI59B@p9uM9E2LPN0@K^H&_&`JApQtn<4FLW@)AI)6Pr>6456ln9z!-F3K7E?FeBggb ze*yzA52n1nVDe{=z`kMfUz88=7g!G5KH&0zLLd|k#UrhCo{!8Pa7LKyDITrVxIW+k zh6Bg#eV>Qa`YiIyQWir${QLjq^5KJrfdKqd!ROciuf!HB;WvyhKB@AsKpy%7)(4cp9x(y|1Jm3;5P4vawqu603MGK z|6k#NL4jZmA`fqbd>-Io2bch0!1@uK93Gem2tBa^^AIC!Y KBj9xacmu%J8sK#R diff --git a/assets/resources/dolphin/L1_Mad_fist_128x64/frame_1.bm b/assets/resources/dolphin/L1_Mad_fist_128x64/frame_1.bm deleted file mode 100644 index 0ec761cbfd5c88d482ea64236928813bae064c6c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 540 zcmV+%0^|Jw02l&*&>#^A1P~1p07L#i-~^w8-Y_m%fIZ~^^^^nOkS|#{89)P1Etm*= zm0x{IV1C`3Kw{ zUl05g9z*sAmGglI59B@p9uM9E2LPN0@K^H&_&`JApQtn<4FLW@)AI)6Pr>6456ln9z!-F3K7E?FeBggb ze*yzA52n1nVDe{=z`kMfUz88=7g!G5KH&0zLLd|k#UrhCo{!8Pa7LKyDITrVxIW+k zh6Bg#eV>Qa`YiIyQWir${QLjq^5KJrfdKqd!ROciuf!HB;WvyhKB@AsKpy%7)(4cp9x(y|1Jm3;5P4vaHi0=0J@+7-~-X(KkNSj8vub|4I&R`2-so3^#zDL#v`MJgXBhjU_gV*0RacJA{Y-v e_?bi=kq_a4U|?mBlmq~f598r{5%4;IJOSX1q3;R+ diff --git a/assets/resources/dolphin/L1_Mad_fist_128x64/frame_10.bm b/assets/resources/dolphin/L1_Mad_fist_128x64/frame_10.bm deleted file mode 100644 index d4207c95d87c1178737332cfb846c4f3ef65a217..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 542 zcmV+(0^$7u02%^-P#_Tq1QG!O12ljT;Q#!8zzIJGykL5>0DH;->nI1mAYQU?GJpo2 z94H6~e3%FXf&mDCK(G{mddUIt{wOd$EyYNc9`7=z}mRhrgny-@$j3-BLbA^g9e z_$g>Z?hh#c0uB!$`vb>;1As?^_uxW|;6HN!c-RB-Ss3^{cm?@Cj4&QhH2p&$&hZ0)zhG90flh@xLfXo*%pP z1HsS_r)I7nJPi5)5by`rUSBu(m&Jf3^AD2zNrC_v9f1C!@&}j*LFLxE&qwADGWg5^ z3T8KacwU zA0+t`2Luloa6EI@dEMu%4@eXS0SyO~{2o5x_`qWn2owVVFD3}bY5JCa~EC@Xy7wBi=0q_SD{2suugQ`W&Fc|@q zLFh0X3=SL*pa9rlkZ^uPV36>6nFJoth$r~Id;omRAP_7B03I-aVG2YZQGkDc0p_m| gdqg0f2sk{I;tL>pL4jfbI2aWG5da#91Hc{tz=$g6mH+?% diff --git a/assets/resources/dolphin/L1_Mad_fist_128x64/frame_11.bm b/assets/resources/dolphin/L1_Mad_fist_128x64/frame_11.bm deleted file mode 100644 index 35955bc2021298f14e78677f1134681ff57ed248..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 505 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 JKpp__1AyX#(Xjvk diff --git a/assets/resources/dolphin/L1_Mad_fist_128x64/frame_6.bm b/assets/resources/dolphin/L1_Mad_fist_128x64/frame_6.bm deleted file mode 100644 index 887a4b866414750fa353f434e7890755aabf34e8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 524 zcmV+n0`vU=00;tr&>#^A1P~1p07L#i-~^w8-Y_m%fIZ~^^^^nOkS|#{89)P1Etm*= zm0x{Js-G)4;UJLV1F1nd|=7=;6Q&cJAsfx@DDg}JcH%gtB2G4 zKOY1%KAQ6RgGLDeh<}UbA0_zk4Wj}V4lq98^AL;-9zAQU^nPITfPNqtYDcSeE)Tx~ zeh)9T_I@8(%$N z{(tG<^C^n_VUh)K%psTxV%;DJ@q>641suR-f$Jh%ns01ONh O00d);od6yH@B{!JY}+dU diff --git a/assets/resources/dolphin/L1_Mad_fist_128x64/frame_7.bm b/assets/resources/dolphin/L1_Mad_fist_128x64/frame_7.bm deleted file mode 100644 index 8e9a34e971bd15694ea1fbbb480a712b73950009..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 515 zcmV+e0{r~}0RI7i&>#^A1P~1p07L#i-~^w8-Y_m%fIZ~^^^^nOkS|#{89)P1Etm*= zm0x{Js-G)4;UJLV1F1nd|=7=;6Q&cJAsfx@DDg}JcH%gtB2G5 z30Mp?KAQ6RlST;uh!_U0A4T}^5u`30XW%v=^aq$kv5?N?)_OlMdVC-lXgA|^E)Tu} zeh)8&_I@8(;Ko{FBlFX2vacnC(6M9!7GNr z0paF6^PA6D9#a5#lty6z!{@=}?jMLeph^*rU~f6V`$i}bC^P~efq?%9>;H#?#sR^Q@$WSZ*-3{j$2f^%sWabcl!NKfz5P88k5HNc$#L6J@{1=!2FfdF25&`^kpaZ}j F0Dyyp+Bg6J diff --git a/assets/resources/dolphin/L1_Mad_fist_128x64/frame_8.bm b/assets/resources/dolphin/L1_Mad_fist_128x64/frame_8.bm deleted file mode 100644 index a430e480a6fff0f3e6a619747de0e6cb40819722..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 517 zcmV+g0{Z;{009Dk&>#^A1P~1p07L#i-~^w8-Y_m%fIZ~^^^^nOkS|#{89)P1Etm*= zm0x{Js; zhw2Q#90dJ;P&@Pj;qYJ75As4E0)DW6$OI37pPmK>{lGX32k;+$1qS^vfOFtK>bQJ+ zHE{WZ#a=xK3?Eh>EqQ$KGw1|CfG`pH2g!aT!H<{6fq;PP1L_YYaaaN4+`7+4<_|OI zgUi{vmj~Z50GK#O`F)><)_DM;1Iiix|IhzE|M`5la0wIx@&90mJi7n&_>0Da5`acH zG7$PF%E17kLk@|F2ed#QqOkxRowgwLLkIyS z0QCWgJ*FS{2t8Ni089uynj!#6_&w=>GJ+UJKo5E_J-8rp2b~NbxC#h8zu@$Qcni({ zQ;0np10YHi3jntudS`+11`q)9FyQ_EU_tKp5P8o50uN>QnM6KC=Ku^06(9o*3!y+B H0Pq8VP0`#< diff --git a/assets/resources/dolphin/L1_Mad_fist_128x64/frame_9.bm b/assets/resources/dolphin/L1_Mad_fist_128x64/frame_9.bm deleted file mode 100644 index 487f50b354b087e2a65878b92ca3e653f1b244da..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 526 zcmV+p0`dI;015(t&>#^A1P~1p07L#i-~^w8-Y_m%fIZ~^^^^nOkS|#{89)P1Etm*= zm0x{Jss;s6yGFo;0{j~F+A3NQ#jJYazLhy&9B85Rftr0uZ> ztOLv)AOOk%>H{XZ019FMfP>k7AOygJ-M%0LpM&Bs0~jHMWB~Yv1J{BJFd3c5A7*n0 zpZp$&@h><0>>%~LNC1Tbz#)hpU;&SV;Z{h}gM;!3{D1@70Rab}^L`I?_?bi=Mb-cm Q3>6>)4GDPw9suwJ09n4>asU7T diff --git a/assets/resources/dolphin/L1_Mad_fist_128x64/meta.txt b/assets/resources/dolphin/L1_Mad_fist_128x64/meta.txt deleted file mode 100644 index 93e59e49b..000000000 --- a/assets/resources/dolphin/L1_Mad_fist_128x64/meta.txt +++ /dev/null @@ -1,23 +0,0 @@ -Filetype: Flipper Animation -Version: 1 - -Width: 128 -Height: 64 -Passive frames: 7 -Active frames: 13 -Frames order: 0 1 0 1 2 3 4 5 6 7 8 9 10 11 12 13 12 13 12 13 -Active cycles: 1 -Frame rate: 2 -Duration: 3600 -Active cooldown: 7 - -Bubble slots: 1 - -Slot: 0 -X: 67 -Y: 24 -Text: Am I a joke\nto you?!?! -AlignH: Left -AlignV: Center -StartFrame: 15 -EndFrame: 19 diff --git a/assets/resources/dolphin/L1_Painting_128x64/frame_0.bm b/assets/resources/dolphin/L1_Painting_128x64/frame_0.bm deleted file mode 100644 index 2694219ef9eefda1c8d52e3ce99cd1284d0d8d31..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 763 zcmVrBnnlSRlM?kSPVi2@upQgaA>f5hQr9 zSqB0O5bPdE05MQ_z-+K%nDOE|fOpudH`hRdR4*Wh)9!NbZ zIh_adpMUy@#sNqNo&#Y~)W$gb{ZIfw<%qwoMxiOILU335r~rTTb_l@W@(qL~OfVgI zeCQ0(z(yk#h%A5}HsJXK%7K8eLA8=uh6f3v53GX$#DLYLaheS-${$vcoX1}goXBPx zfcn5D6RZVhBl(EHZ3Xi-3W>5=-f&+BIKrB^N0FX!kSqp_gX>^~C7P% z5DX>*Z>qx=#&i!H0)Su~h%)?Z6x76{hvh|tRbb%t t!9;K;@{=FN1`k*l`~Wxltd|IW5PF(K20Q@x(4WHt2Oa_t4=MnBbU+(OFu?!- diff --git a/assets/resources/dolphin/L1_Painting_128x64/frame_1.bm b/assets/resources/dolphin/L1_Painting_128x64/frame_1.bm deleted file mode 100644 index 3c9623d4c79a19e74bfe403f8819221e10322d62..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 764 zcmVrBnnlSRlM?kSPVi2@upQgaA>f5hQr9 zSqB0O5bPdE05MQ_z-+K%nDOE|fOpudH`hRdR4*Wh)9!NbZ zIhhCZpMUy@#sNqNo&#Y~l*Tyw{ZIfw<%qwoMxiOILNHhQr~rTTb_l@W@(qM0fI1y` ze8A)2z(yk#h$uX*!SV-{0|8)zYb3Y~1`|XdSq1}%0jo&xGzwjmKCK}+j=m#7kjym! z^?*z#SPINX!x4bm3+8MX2Lcb^*g$j`sD{KoTB}mbpce}S2VsPOSgNW4AdmpFoCpp= zlK{|ap->H1s2+Iy1O_n)fLJQO(D?O1#el=WAajU_e4xSg_)+H&Ys7>;P*?zcKhxSI zGx!D&`XEGHG4|NPF3j?jV2LDx(;Sa(OSRfd<0r8I}kAEZeiLFwq0vN0iUOd2f&LEKuLdZY`8i5i< z8v%oO0pr*_kN{$!@qoc#5PSy~Bd7)KEAWA;h5;di+#nj8@I05|qab+9VBk@$&;A4gii65A0QiH;RX?~G2Yw$Y3?v?W z3f^Wx{O8~PqA`FZL>^KNbw*Pda1O3<3vNf`iK3A0T-E2Y~?9$#579CWt@GV27^@! zWC(ox191n5U`V6=53lfSAn|+%d|<)!;BUj?(;)dlU;*>^LG^3#6A*py6WYC2(gqmt zec&E3_~dXreF&6b*bP+Z9#PN#RslkkP%eS-yj5nP^0+}Ya0+Ci0k98@5I&p;7yJJ+ zm`5rB@qtxms`G%uiK7^ihsp!0mn+Tzb2prd zf#MJ4CW20Z<$#35A@S%p^W2$+0l7vTBA0pp;6LMRac`2Roj0Qq47 z<52KG_y_TUf4}oE`B;O4D2PEY3-N=OM*bfd6h;v+jzS^$500-KFCX}Pagq23LI;E~ z41T|V`~$|r75G5M!vNw(I1n9W_#R8~QII@@g8;5i{saPDhyZQ?^wdx81_9rP$^$6} zrGGOZ{&VmDQ5e7_0Q10VDl(YIAAhO<2t2tH2nB+%DXT&-SNf;`fAV%{i-kTgngHl^ z;q#CvL>@58t@94-g323+F!{kVJJ*7zQdKu@9E2)DJtwR311;1&XSm8m-hGd*Jy}AQ}x+DgYl$ z1zbKzVX#$yq46+?0Dc<1R1HQU@`DH0{vQ}Qpo8NDfCtZkAJ%H2p<)lj0=ZT6j0QX( zZ~*=_|NQSCLPZ!h16Aq=ox}O~xXLIOK=`5ust-7XBjNazz%~K#f(O$94}^aw^El-| zJ}@N^tBwhb@xbHKF!?}rTcCMEpkK&@5(xkglm@Gv7Z?5m3h==2m<_(G3||~@JY(<( z4-^U^@vK#Fw+gNtJT?pj0&))+H2{7zXhy3BFDeEByfFI^{HU;wt^}S`6M*0$0f7&U xnhEL^hyY~V355d@|Nr0WvRoz9R8R#_@F*NdIS0mtItQFUqBt8E7-#|U(EtcKLwx`M diff --git a/assets/resources/dolphin/L1_Painting_128x64/frame_2.bm b/assets/resources/dolphin/L1_Painting_128x64/frame_2.bm deleted file mode 100644 index 13916806fd62d4aed731cb58265eab34adb4b755..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 762 zcmVrBnnlSRlM?kSPVi2@upQgaA>f5hQr9 zSqB0O5bPdE05MQ_z-+K%nDOE|fOpudH`hRdR4*Wh)9!NbZ zIhhCZpMUy@#sNqNo&#Y~l*Tyw{ZIfw<%qwoMxiOILNHhQr~rTTb_l@W@(qM0fI1y` ze8A)2z(yk#h$uX*!SV-{0|8)zYb3Y~1`|XdSq1}%0jo&xGzwjmKCK}+j=m!Skjym! z^?*z#SPIBIKqF``nXq6S2tR{i0f1tn8xZ+wtxGb1Tr2?b!ayulRREAk09noeJW~Up z)k2^euTVVk_yBmqAQlR*^gcaMabPg;2pX6}3?1VOfZOV@#qpg3 z%71{MVLr?8tW|Kg3aoSm5U~%+iwLU0$L9u+&*K;q s`ALuC0|%TV!$ALk|M&W=mk53o`bKaNGbsngg#Hjg6b26fD;RuqKoWI0G5`Po diff --git a/assets/resources/dolphin/L1_Painting_128x64/frame_3.bm b/assets/resources/dolphin/L1_Painting_128x64/frame_3.bm deleted file mode 100644 index 751fbc3efb282916cc38d5999018e804cd4c0a37..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 759 zcmVrBnnlSRlM?kSPVi2@upQgaA>f5hQr9 zSqB0O5bPdE05MQ_z-+K%nDOE|fOpudH`hRdR4*Wh)9!NbZ zIhhCZpMUy@#sNqNo&#Y~l*Tyw{ZIfw<%qwoMxiOILNHhQr~rTTb_l@W@(qM0fI1y` ze8A)2z(yk#h$uX*!SV-{0|8)zYb3Y~1`|XdSq1}%0jo&xGzwjmKCK}+j=m!Skjym! z^?*z#SPIBIKqF``nXq6S2tR{i0f1tn8xZ+wtxGb1Tr2?b!ayulRREAk09noeJW~Up z)k2^euTVVk_yBmqAQlR*^gcaMabPg;2pX6}ciuJ510UA7%7K_Pe8f{#_%5zxU5Qv;P^?P8vyvh z1L6-k1mfXnnnx-D@qq`{!2pj*Ba$%rKu_WiItKznA~lG7pfy{lJfqwRg#v?zz-{$d zV))L1PFMTAvg;PWVy pM*@E-G5la)^`3vg1AnT?aEIXstOyKT0Qk_K!vhDPSm*)q(Ew_YJf;8u diff --git a/assets/resources/dolphin/L1_Painting_128x64/frame_4.bm b/assets/resources/dolphin/L1_Painting_128x64/frame_4.bm deleted file mode 100644 index c1135b467cc5fe2e313984e30a04ded3dd814e7d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 759 zcmVrBnnlSRlM?kSPVi2@upQgaA>f5hQr9 zSqB0O5bPdE05MQ_z-+K%nDOE|fOpudH`hRdR4*Wh)9!NbZ zIhhCZpMUy@#sNqNo&#Y~l*Tyw{ZIfw<%qwoMxiOILNHhQr~rTTb_l@W@(qM0fI1y` ze8A)2z(yk#h$uX*!SV-{0|8)zYb3Y~1`|XdSq1}%0jo&xGzwjmKCK}+j=m!Skjym! z^?*z#SPIBIKqF``nXq6S2tR{i0f1tn8xZ+wtxGb1Tr2?b!ayulRREAk09noi2jNM8 zXf;r%2CLK$JbnTL_=UhM6<_FldZ6OKVc-xyq)a|gVEX(h^N2T!P9G>N06w4T?GhRB z4TyaZA}$#FY;hNeBzQj%2*wJrDB@^!Ve!BR%m6VA6!J+bpj`vwcn^vsRwYE?)1`0? zfP7$q@duoPae(;DW0e5-z=P{xYK{gmBM+1W{vh(cP$mHJf8L-2#t(j-z3jS2iPFmd1^0P>&*$3*xWIm`e6 diff --git a/assets/resources/dolphin/L1_Painting_128x64/frame_5.bm b/assets/resources/dolphin/L1_Painting_128x64/frame_5.bm deleted file mode 100644 index a4681af98e85a780faca27f632565d56a90abee8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 757 zcmVB0PzBVP#_wh1V*4h^Q%=r4;6re#y}sidI#q!KOp_)f#@Ib|JA;~|NVSF zaiA#b1NRjIjbK0F{}1|oq7Vqjg1#Sx_{cBtNru2bFs=pzg&nX(e-Qe|)~{6o3|1Ks zpV(mf2-Q+6sFiCqKp~AmBoql03&9`$N9z+>rBnnlSRlM?kSPVi2@upQgaA>f5hQr9 zSqB0O5bPdE05MQ_z-+K%nDOE|fONp1LOSv&;#X!fI=t` z5Wt_t1^)lc!{uTJj)MUKz%RxQT^sm(U{M%EBB20>U_3g!aJ+xv@y18T<2XbV?!ABf z1IEJ@_(0&F2sk{U`pfV8P)bMOCA7{DnI3j{I&fY?-JF^)d}Q~(fpOcVuBl+~daEB#aeKl(capz;lbCV)B} zcznR(Ef~aNu>}W}xIRGiutBwwTm}OPq7T0k16GmXXcW6BeW3?2*TiTt8HS)f(+SoB zvk~ybU^asJ-@)U+gZMTO9R?~Pu@9E2)UzlB!odO9VIUSNs(?r&04(PM1CXS^G#aQ> z16Aq=o<9Krj6zofRsM&^stzm$9svWKL__5U53jfcU^cr6ZnJ5c|hPO1_=NUlm@GH2bT|nFn~aCm<_(G z3||@0yz$V81RMl4imn#nRgQztV0airGJroCG$U0G2b;Kj5epFfsIZEx93Ef+$V6}_ n@{=FN1`k+A`~Wxltd|IW5PHD?#lR1Z3H&fHdP0HaKo5?HvfnzS diff --git a/assets/resources/dolphin/L1_Painting_128x64/frame_6.bm b/assets/resources/dolphin/L1_Painting_128x64/frame_6.bm deleted file mode 100644 index 36f2d084fe3277d555dda68b3a9675916ce237c3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 785 zcmV+s1Md6*01X3xP#_wh1V*4h^Q%=r4;6re#y}sidI#q!KOp_)f#@Ib|JA;~|NVSF zaiA#b1NRjIjbK0F{}1|oq7Vr#7-N!1m0(_h@t{H>K=F(N$51I5i%Xy{O8~P zqA?GT_#QAVR;r^ZjB)qiVbxF|ANYBQYV}!7S`mW3)j$J(fPY{Z2nMfJ2@DJeULQue zU;^U>2bH)!Kw*qG$AA|ZE&~CC(Fe&^Fo*;~z!w=F27ybmhsbsy@q}yw;}L+!W*UI` z1H^7HYQS7U;sF~#e81rF!(erRae%-vQ4NTEy+HB2U@F0J2aXZ}VydVIst*_k0PsNZ zq(C$ps8j$DK=H4J2(Ct9{)faKRsi_Nuo3uxe4xSfoL9cEIP}6mA12U;&V96I0|$?S0TQeMhJ65_C&nR!3=9In;187I{2w?*5(45cgiJm^&;0;CSWF}W@c@W^ z1Lgkz%){kk#y~K_LH;ks4qY4gd|*a_fIwhSPxE|n!twuy#~B~EKrb*MSii>q_y>)K zEAWBQFdxVy0{$Nx@I05|TN(xf%#jdy)+hf00YyRUfS-rPe{e7k{61712lyT#p|9p- zAI^RM>IfPH1OE@;YxP>HjHWTi-+_lz4JrmYCUU%fg3b0>gwY4f01qf9^08Hs;Aj-PD13q902~~DQL2c* zWHSILht2`PPZd-iAQ7|&%l;1&0Khexs0;%Y5ZH&y)Gr_a)vBQJ!ayulRRHxt0stNc ztUgqT27^@!fNHQH^1Kbi9wu?HRez!J2cGzDAn}L?$_yVK2qb(9@#>YvKp!Y91FOM< zz(37_pWpz(A82F)@qg-r2KfMw2hOWOXO07ZKAe0w9DNCtVAu^+4mcYh1P+Y|z{+SB zK=_(R2ObBF0yrfY46XsN4~!F3%oKS@r``wz$~jOEj0>v+pM%E$BPaxFMjt2+st0Tb zmjVU^64-~z1J!|Y0p&4*cpx}j2H#bNz-P#_wh1V*4h^Q%=r4;6re#y}sidI#q!KOp_)f#@Ib|JA;~|NVSF zaiA#b1NRjIjbK0F{}0FF1|O98#2^spFn(9#Aiu!jf%=7TI}jK^e18!7$JVb^0Ss0d z5cl9e`uNpSE2x!gH9#SaK_nN81P}2Q{zvN*TBTG3F<2)8VbFkJBXCHDp=2NejXI17Q$+fWtn3P!r=2!UhHbV7^4d<2b(u&Jl!wgU5l6hzG~{|DXrU350-N0pkNN zj0^q$nTN{7jDTUFJO?1wAm!1&hsFgFtUw?zDFgxqsK*P({vRAVa1LJ-NlKe|!K)`vDBM5<3C;tKgMM3QZ4~+icU>*2;s5lSsJWYdN z%*a2S`~TENGzbU&9%3C{s-r24argS50E5T-0Kh;h^;u0?5rV(fKm&hco&Y{Dkifuo z;q;6*$AA|YC_Jsf@>Pr?0TA#7#!G;}VKhPV9f&+(7XY}(@H7ftls-W56O0>h7Z{8N zLon0_(ZE%}xP!z3Hh}qm!Q*&5czt6;s6@>TtVZ6fLN-k0qTMV0l+*E z7--To62B0rGm~YybuYLa-bg=7gh&92aW(nPzcnFK2RN04%iPa1Pllz zu@967s{-T$%3}oZKybJXzN-y@^+B=?5`@Ac7$x}DD!5yR*NqVslrCWq;X^0`@jDHw z4I)wu0u?5RSZqV`qQWk)N+Q6a(jNrD&>``YK_^g3MED**LxBE2|NH$`ON5@mG8_n=;>2_ diff --git a/assets/resources/dolphin/L1_Painting_128x64/frame_9.bm b/assets/resources/dolphin/L1_Painting_128x64/frame_9.bm deleted file mode 100644 index 99ed507179a3dc7a64a99f871d18c55796a97cb1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 777 zcmV+k1NQs@00jepP#_wh1V*4h^Q%=r4;6re#y}sidI#q!KOp_)f#@Ib|JA;~|NVSF zaiA#b1NRjIjbK0F{}0djKPmBu1I7ne55oLp7ysl0KTxg)0_;C8#6Ge0tJOe56^2A7 z@;C?jH&lx1C0fl;2xCx51p-X~fkpq3`oz|$RRIiE2rnAoJY$eZhM{C20*ye4BWHj> zZUFJ@9!LN&P$bQ$M&E2Yw$Y3=|%4 ziq2+1{O8~PqA`F$0pkY(4!)x)jB)q+pa6n~0WlJc3acrrLNHhQr~rTA!G}OF7#&@`ss42EH- z53BqiZEy(eDD+a z33bwjuI>)nnlDkWeER*d`kRx77g0 zRTmbF4nZJ-SYVLI1!Aj(xOHM{frdmRAB+SJ5kMb}8WHMI|3CNotd|LO2b2N}C@_p71Q!ta(5F)Xc|fHc0C>Pq H!XF(FZihX= diff --git a/assets/resources/dolphin/L1_Painting_128x64/meta.txt b/assets/resources/dolphin/L1_Painting_128x64/meta.txt deleted file mode 100644 index e5f5fc0a6..000000000 --- a/assets/resources/dolphin/L1_Painting_128x64/meta.txt +++ /dev/null @@ -1,32 +0,0 @@ -Filetype: Flipper Animation -Version: 1 - -Width: 128 -Height: 64 -Passive frames: 9 -Active frames: 13 -Frames order: 0 1 2 3 4 5 2 3 4 10 6 7 8 7 8 7 8 7 8 9 10 11 -Active cycles: 1 -Frame rate: 2 -Duration: 3600 -Active cooldown: 7 - -Bubble slots: 1 - -Slot: 0 -X: 57 -Y: 24 -Text: No mistakes, -AlignH: Left -AlignV: Center -StartFrame: 11 -EndFrame: 14 - -Slot: 0 -X: 57 -Y: 21 -Text: only happy\n accidents -AlignH: Left -AlignV: Center -StartFrame: 15 -EndFrame: 18 diff --git a/assets/resources/dolphin/L1_Read_books_128x64/frame_0.bm b/assets/resources/dolphin/L1_Read_books_128x64/frame_0.bm deleted file mode 100644 index 1169e42d690a46a34ea8a8e1910843a4d46556f7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 653 zcmV;80&@KU0Eq&sfICzG;QxpGKjHrm_%c+< zAOOz11N86I1Z!1;zySlg*6zML59jDUkQfHDRqy)qd>#DcmaFw26aoQQs>}Vn|91a5 zC)UQbdaF=8b{eU7*OTh_XC#$aZCa}}2aSL}nPB-sheIj{j{rV?41j#^6Xgq`B4-ooVpgRau0{9hgxfsM+m>fXw~f#LNB$6cx5CFg?0rHaBWj;#3R%jnca6D@B-!7-gtkq8g=fEHq zx5|D+_=m=;Kj0WfvJYVSz<+`P)d%Yb{}+u*N)JgmeE-+*c^7!R>aI*NP#nfa zF(wC$mqm)zTV-@8L^=f!%SJo^@q+8H!=d$4Cm0AF!a)K5K>PvoMU6v|e}P~mei{5T n@fo3FwjuGSOfUt3o`2wf3;BI$91Hjmd=v5T$N$Im3ZMYM>|;S8 diff --git a/assets/resources/dolphin/L1_Read_books_128x64/frame_1.bm b/assets/resources/dolphin/L1_Read_books_128x64/frame_1.bm deleted file mode 100644 index 80e2f39bb69255c5c17ab52708c536680e2ad7dd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 653 zcmV;80&@KU0Eq&sfICzG;QxpGKjHrm_%c+< zAOOz11N86I1Z!1;zySlg*6zML59jDUkQfHDRqy)qd>#DcmaFw26aoQQs>}Vn|91a5 zC)UQbdaF=8b{eU7*OTh_XC#$aZCa}}2aSL}nPB-sheIj{j{rV?41j#^6Xgq`B4-ooVpgRau0{9hgxfsM+m>fXw~f#LNB$6cx5CFg?0rHaBWj;#3R%jnca6D@B-!7-gtkq8g=fEHq zx5|D+_=m=;Kj0WfvJYVSz<+`P)d%Yb{}+u*N)JgmeE-+*c^7!R>aI*NP#nfa zF(wC$mqm)zTV-@8L^=f!%SJo^@q+8H!=d$4Cm0AF!a)K5K>PvoMU6v|e}P~mei{5T n@fo3FwjuGSOfUt3o`2wf3;BI$91Hjmd=v5T$N$Im3ZMYMr(i() diff --git a/assets/resources/dolphin/L1_Read_books_128x64/frame_2.bm b/assets/resources/dolphin/L1_Read_books_128x64/frame_2.bm deleted file mode 100644 index 959b02556d64ec11a58378b58d291bb1843394bc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 650 zcmV;50(JcX0EPmpfICzG;QxpGKjHrm_%c+< zAOOz11N86I1Z!1;zySlg*6zML59jDUkQfHDRqy)qd>#DcmaFw26aoQQs>}Vn|91a5 zC)UQbdaF=8b{eU7*OTh_XC#$aZCa}}2aSL}nPB-sheIj{j{rV?41j#^6Xgq`B4-ooVpgRau0{9I8tTOt4|-EOM`51*@hD*gwD)E^yosFHfQ?UsAw z4vf@3HD0M)>au+-zsMgMczi$u0GJ2LOJ$V#D*aiYeIdZ{tIvG8pC+?aJP)4$fLh-v z`4{3J8m#|-VH(IigXaPM2nSRjtRMVdH7zJTB;oV_U%}&~-m{WSA1mxP{|A6sG@$hl zmm3cQk3cyM<&%fUs~@v?Bk&4D9cpq9z zTd5g>ScBRwA1rvhLGeEilzS)mg@eSLU;*HN|70=9Mk3&WjMK2%nMbO*FvUP~85qQv z9x`1PD^+ck(4i3M6hkc-@B_vRuEP$8)l8gVAa@A_2mJ%^2hA2W4n_V2fRXrT@Xy3% kg^JjR#+@+076y6$f&4G!^`LMs;6dfevkzfcae_rL=k*0*)>*ndAk^n*Y(dar-io8a%~B(-0te4sE2rB+|<=li$$$v(C; z8XZ&u0jSkWzPz7T!#O0X!)n3t2aP~JnPB-swVJ6=Jahr`@MHt$c&g1o<3Os*zr2w7 zNeAPE)y9G2zz>g1u%NAwcpEpQt{2SOsDm0KNy*9$Ma116V&C z5`ahV{eV@YqVa$S%pQ8(Fla;L|A6ZM|KIok>b2YrR6aNO3XE8S06k{QgR1R7FQNIb z*k8ggDz2&WEs_G3%T#|NAiasQKmo1R>abJzm&et;m45@n>JN^)RRRy3_RBr;2S#ci z5FnOLt0&US{DJY8hr|FTbJfXgvY#bit27UnRsk>vjb3}@)cG}p3LB^1iZ04*;@hLFykaHXa8a zfN~qlCl8c<&ESu~DG+rj$Ugz{@x~*`0mw-N2QP%x1LFjKjldo(&ipZfTGpwzy^3J*XBuiQR1t92tW1Dpfe zE*~DQI9?^O2ekbDQT(6bUNi!6fCqv9{gB5Y7>9xeGfu;5Wge>J!xaI{WMdOxc*%5F ztyQ*HLWD!0Q4F+Wzz-NMy9_!XRWfmaf!rhzAM_8vA2e9hIT!dA0!QJW!#@$47As;O g8g#<|SQ+R32k^g_)`7skfd|1q9}Iu|e^9Cb3>pbCT>t<8 diff --git a/assets/resources/dolphin/L1_Read_books_128x64/frame_4.bm b/assets/resources/dolphin/L1_Read_books_128x64/frame_4.bm deleted file mode 100644 index 389d2a8ef52e63d603a3d5c6357d14886acd4518..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 650 zcmV;50(JcX0EPmpfICzG;QxpGKjHrm_fevkzfcae_rL=k*0*)>*ndAk^n*Y(dar-io8a%~B(-0te4sE2rB+|<=li$$$v(C; z8XZ&u0jSkWzPz7T!#O0X!)n3t2aP~JnPB-swVJ6=Jahr`@MHt$c&g1o<3Os*zr2w7 zNeAPE)y9G2zz>g1u%NAwcpEpQt{2SOsDm0KNy*9$Ma116V&C z5`ahV{eV@Yun7DFK4A0K>4QQa8Tw_B>gPvZcp`nR&L;COvO@z-jgL~_;7Y_s1W zbY`LP0tsa5vVAPS$R8Pad_V$cJzSQ{De_hNvq1TEU=sj%)#tulPm@`yo(Iw#4;^om z{EP7qmjHlOkb4Ktf53sjH9`3=kMKY>Ehs%C;q$?N#p9&jvyx07E9)d+@CzoC9-;E% zVc>D-2O+$&aQR2<-U$2xkq1(ogYX|O9AZ3>9E6ZSa`;V9J}^)G2H+1FJCr~JDEx=U zRS1$yJ_3=Fhtje?8Lgs8a6k-ze!GDAVJRPv6b?XMPyKv8P-WpHj6=Z#8K+^jGLKbqVTyp}GBJs; zJY>2oR;t@8p+X_hD27@w;0KHsU4|VGs+l;zK<*L<5Bdk-51K4$9Ep%c+< zAOOz11N86I1Z!1;zySlg*6zML59jDUkQfHDRqy)qd>#DcmaFw26aoQQs>}Vn|91a5 zC)UQbdaF=8b{eU7*OTh_XC#$aZCa}}2aSL}nPB-sheIj{j{rV?41j#^6Xgq`B4-ooVpgRau0{9hgxfsM+m>fXw~f#LNB$6cx5CFg?0rHaBWj;#3R%jnca6D@B-!7-gtkq8g=fEHq zx5|D+_=m>7Kj0WfvJYVSXkb6V0P2JB)$m~d;_<0zLFp$CmV^I)2ac0^&PgzQr;zXb z9sy+1gVa7;d@K$<0OU88PG2bfyTKoTQXuP7kbVQ@;*3X>1CWvk4qpkXH;exPxC6#d z1rL;?@*gNPl41N5jFdi=k@;Czb>0qge< zm8y}LC5Szu;qu0d#2*vz`9`vTh*&&H#sD4%|Mo*1gkmlT7|lBksg!!FlMGY`A(4zp zf#W67VzpM;T?!EnfkZOVj{rPiy6iCMeN@TD0taxAK!4Cb0DSdQs)r)~0>DW8Gx%rX mGeX5|L*q`EU<(60|G@qi^7_y?7w{nXC*$Fd|BvbwKmmYdgFQk3 diff --git a/assets/resources/dolphin/L1_Read_books_128x64/frame_6.bm b/assets/resources/dolphin/L1_Read_books_128x64/frame_6.bm deleted file mode 100644 index 451c80a26af36c0bfe1cdbfa307f6e6cbb98b292..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 646 zcmV;10(t!b0D=OlfICzG;QxpGKjHrm_%c+< zAOOz11N86I1Z!1;zySlg*6zML59jDUkQfHDRqy)qd>#DcmaFw26aoQQs>}Vn|91a5 zC)UQbdaF=8b{eU7*OTh_XC#$aZCa}}2aSL}nPB-sheIj{j{rV?41j#^6Xgq`B4-ooVpgRau0{9hgxfsM+m>fXw~f#LNB$6cx5CFg?0rHaBWj;#3R%jnca6D@B-!7-gtkq8g=fEHq zx5|D+_=m=;Kj0WfvJYVSz<+`P)d%Yat{4SNN)JgmeE-lec?1niA#9RT5*kRE6sgsNZ4&fkx|Db*V`Rb)r4n_V2fRXrT@Xy3%g^JjR g#+@+076y6$f&4G!^`LMs;6d%c+< zAOOz11N86I1Z!1;zySlg*6zML59jDUkQfHDRqy)qd>#DcmaFw26aoQQs>}Vn|91a5 zC)UQbdaF=8b{eU7*OTh_XC#$aZCa}}2aSL}nPB-sheIj{j{rV?41j#^6Xgq`B4-ooVpgRaqeZ4{R+MdKdPyqS^%UjA#h*vf#{r`Xtt6jj=d$0~X2CD#S2mtk)E(`czAMpIw>?$FE ze~06$e2Zj20tFqGsto|X5e4iuR>*(~w_B>f%jfFe%D;i(^#{jYswAGSdu5*a1EVz$ zjaRByI;@{dFY*V*ULOzuz$O9mlG$ZGO21ZUA4qUKYV+SNr^&3j(cAjY~=oNjQA}*YJ4hH>~872g>^m|H0rEO(;D><;KIn zc{Nf2>b$(2U?ti@E;pjk})1o4njyEIeaFmTbjZ@@Ed?UWbjb=N5PVO=IkGrcvsyOfgU##zrwF2aK0R ziq%_XbSOkR1rW#)P2^;0Jp2pz&f0slb!0rN$TLy>=hU?hGS{4?%c+< zAOOz11N86I1Z!1;zySlg*6zML59jDUkQfHDRqy)qd>#DcmaFw26aoQQs>}Vn|91a5 zC)UQbdaF=8b{eU7*OTh_XC#$aZCa}}2aSL}nPB-sheIj{j{rV?41j#^6Xgq`B4-ooVpgRaqeZ4{R+MdKdPyqS^%UjA#h*vf#{r`Xtt6ji|i;n@Sz#4)8J!Z>;s*k`w;rXxFTgCn# zj;Zo3kpKu3c1_~G5e4iuR>*(~w_6AKa@I`*nv(c0QLKy%H2rJ62u|N9}1LNONv3}&5%)XF_o$%ZNen8?N?!10pkv0AHa zu7wDPK%yCF$ABI%U3M6BKB{En0Ry;5AV26IfIeuksB$mxECi3kKZbrIG%QxcJ~Ziu d0I)O9{14%OFRcTCe*zDJem)rg`2L|(02rJOIJW=* diff --git a/assets/resources/dolphin/L1_Read_books_128x64/meta.txt b/assets/resources/dolphin/L1_Read_books_128x64/meta.txt deleted file mode 100644 index 7432507ce..000000000 --- a/assets/resources/dolphin/L1_Read_books_128x64/meta.txt +++ /dev/null @@ -1,23 +0,0 @@ -Filetype: Flipper Animation -Version: 1 - -Width: 128 -Height: 64 -Passive frames: 13 -Active frames: 2 -Frames order: 0 1 0 2 3 3 4 0 1 5 6 1 1 7 8 -Active cycles: 2 -Frame rate: 2 -Duration: 3600 -Active cooldown: 5 - -Bubble slots: 1 - -Slot: 0 -X: 5 -Y: 28 -Text: Predictable twist -AlignH: Right -AlignV: Bottom -StartFrame: 14 -EndFrame: 16 diff --git a/assets/resources/dolphin/L1_Recording_128x51/frame_0.bm b/assets/resources/dolphin/L1_Recording_128x51/frame_0.bm deleted file mode 100644 index a278e3a9d693124ef13784e913883e49b5c1e994..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 663 zcmV;I0%-jK0FwfMU_apf@PFW7LAU`#0MHM{0uB!zWPn=m3FHDD0I*P`Fc0vUzYy?v z!@tn@2Lgj3z(8Su@q!62UqK0wYO^1xgqQlG|x3FPo#@?i5q4L?O>f=ppCD2)?I zb_bj?!J_&=uR!sIN;H{fDcfQ?PoxI;4;Veb9^d`{(&7&;J;WYggowR_9yJhoQNiP$ zfO#L|{twQ60rGc@3IIQh{2!zbGWfvn!10GpG?9q-e7;Z}2-Iib@sCdd<30zJI|U&0 zkcJ5){zvjr%fLKbV?>a0ary9g&LP0@xOk!Shy@}+lE>5FKv98WVcrkWykHoK1dAUh z2c#Rp1cNXJA19(?A}}0;hlPaWcpy+n1;Pl!~zK*X^uP|KZtldZUK>S3@C`x)A3%I)5G({}5=Ei{mcTwbXka~Ifb;MZ z#xoJXMnUFK5GnuT@R*hY5(zE8k$iMl zV0ptV8ZV>|{0|s(<3*NYowg&S`ao}h@q_#{AoU&((&GdNj~*kS2an+*FJT9bL>^Rd zc;}!5k#&fEbMOz7ykJlP{Ab|(AbFR@1Yamod}9&t`Fx-{8zd0^Q4{#aC&2QDV5AeD z!hC$51o+1<0P%5+5OfU!WpgkJ2< zfSxg!jsh|dGJt_k{~v_Jun>?*Z~^K77%;q1A_gD8JW`|pKzZOl;PHchzzmrr0KrmY z0uQh}?+``^!{iX7b rwNoj#XR&PAKo{o(Rt~#VA7FaQ2ksAPBsjjn_K_#v0SE`41HzwhY5ozm diff --git a/assets/resources/dolphin/L1_Recording_128x51/frame_10.bm b/assets/resources/dolphin/L1_Recording_128x51/frame_10.bm deleted file mode 100644 index 7684384fdfb09f1dc5e2dde7b898fa11630b8d79..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 629 zcmV-*0*d_s0C56nmLGlK12bKciBJ}_K0P+4{@?i5q4L?QbgUn$vDD@Lb zb_bj?!J_;???CZ~N;H{h2f!dU*p8Fo0lq?bPDR|M&0-*KRNgZ&|WYo0RA)Zev}*@Mfi+I{!sV_$^)T4ykdR&2aG!fAWyt< z@dk!C#>pUGymH}ifKDJ3i35G%-UwpEu@VRS!Mp{*U<^J_ME*fwF#rhRINkv9d?6Ez zL+&#AMkjO1OU)L$$zM!`3Ifihz}TagTdp+1$^CSV+fPOF#cm;xmA%K5^#rTJV#(4mS zKsXc`4gvxO7z7$%4;Xj)J|!9)hQR=s0fqy>2ag^Da0CpxLlFN^;1mIP;BXT#v0x{X z7(@pN0FXf<7%T)dY*YxuhY9?Iz+wRL!f+r!@q|z!5*Qc$4+4Y(#}UAQgT^61hDhK> z@duBh7l`m73PC`@$f7^W3=#?OgX9e202&4Y;Ubj&G!RcGg9nobqynF!vOy*=n3P6| zq_Zs!8DP0>@-h%iQ7?0o{KFBCMi{k^o1I8UV(q_Wk})3# zm&*en^pJ)LB>qS8QHl5Z7l4P*q#T@nd>%81c1k&TgF`M53M7LikEg(?5(pM1IdF3c zz;O}@7Cuf7NOyuTfG9+siHwNAauOaE6OG`4FbW?y#6j`!2?2a?Qg0csSVIVqL>{C8 z7zPF{4+8-f^l%}*2sk`JV~qdL;L{8Qd-1^dXW%f&1Oxq#2an<( zfH#i<04o@fJYX2F{r&*~2c&WVAqSQbVmttO42A+w5PA9o<3ND}#Bxv$1q2>j0Ql*l zm+lNhU-HKY)0T1OU)L zc;G+uaQuVF;51Np!=xS$9zP$)&%lA@c#;FgBmemQ`~ClZ{sJjDrUOB)kX)|)zQwzD t06(PzL8{eErrn;!vtw9d@Wb!1W)%J!A#=2f6*=5P*5a6Y#Yx0w4eY diff --git a/assets/resources/dolphin/L1_Recording_128x51/frame_2.bm b/assets/resources/dolphin/L1_Recording_128x51/frame_2.bm deleted file mode 100644 index f5dbbc71c2ddd18c7644ca48385fa4066c1f6daa..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 628 zcmV-)0*n0t0B{0;U_apf@PFW7LAU`#0MHM{0uB!zWPn=m3FHDD0I*P`Fc0upzYy?v z!@tn@2Lgj3z(8Su@qnmLGlK12bKciBJ}_K0P+4{@?i5q4L?QbgUn$vDD@Lb zb_bj?!J_;???CZ~N;H{fDcfQ?PlN{f3E@HR0QUdy;1>{hz_^zXc?w1V_7Hf~LFGgT zj(P#)bPxDHIrs<3-Y_Tt{xk4?kUY!r7>|d`oO7;G?R0K??;PvjN@5CD!7jo^VnAQuQk;}HSEKqL@o zh6+vNUz9o#Ac%jca0& z5C|lJra16;i^Lu`0LZuo1P>SnEB}9hKtbX*AMh+-Ao9XJ1yCUIh!@L%Y7ij$1LHt} zU?V`1Ef5s80rAsA0qYD0pMah*n2rK64>Ev(PyZi;#IO*MNpJz;g&+Yzh2n`2FujBl zFff8BgdSK2P@u>8{{SjvkOKu+i;zR=4?DyWVlenegU64@@$>4F4h$Os5&!&t{r>;I zeyKPHG0-~bMau8n>|3{Il)6AP8m&~yZQ1NwHc$omlLuk0)d$!fvVr@9+DQ&Cusx(_ O!62UqK0wYO^1xgqQlG|x3FPo#@?i5q4L?O>f=ppCD2)?I zb_bj?!J_&=uR!sIN;H{h2f)HM*p8Fw0lovq4{!&RG7JwN{{QK52bUh=41ZZ#DDn*fPA1jA0!@r4;b|D9y9Os zJ^>EFNIfJWf=NG-{FHL>28KAsi6G?T^WgEELxJOPYYgH6NRVW)^!N}IU|5)QfZ!O3 z1dAUh2c#GtcnEx+iHwNAauOaE6OG_*i~`^?97G=<0FW2Q1t#%>${h%hL?9FZ2|zG0 zZzKW&Ul4fMAQJ(wf=2-!{6HYn$N)S+V~qdL;L{8N{9p?Lf=C);j|Y$99uFIXK>_0c z#eeVPp$CZAKm`E@mJwnkfd`3TKm))v2oQY%@t{Dk5uiyHhzeT(`01g5^@an_z)u*= zM*$fJoE#o-5Bz=;62L-1CBO%$0AE9ZM2HxF0P#wQ0YT@0|AWR2{{S*%lS2hoj0ir! z^SnVBqYsdHJb3(nA3muy@WwC^AOFYi-|zeP>XSc=AAzorT(14T#k+QCPox7us?|)U o-JZp>WdL8C5m-9yP$qK1KLENcmyCGcn=DFz&rO3ng9R* diff --git a/assets/resources/dolphin/L1_Recording_128x51/frame_4.bm b/assets/resources/dolphin/L1_Recording_128x51/frame_4.bm deleted file mode 100644 index e0db66ffe303381a4cee87351807509ea9dfe0b2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 662 zcmV;H0%`pL0FnZLU_apf@PFW7LAU`#0MHM{0uB!zWPn=m3FHDD0I*P`Fc0upzYy?v z!@tn@2Lgj3z(8Su@q8k$iMl zV0ptV8ZV>|{0|s(<3*N$d<-LPi0M9%8{j-({|yK{VUS>Wj|b^-f&<5o5zqs~08e2D zjYJ+)aCql|=l&1Qd>(Kp0RA)Zf5QYp;$IjMe4$10j7R^Fcn8V@p|U{_iwf&=_tj1%Dx2aUo72%JD0CH`-WBhdrI!<+{Ie2feJ z{}@CQ0f>VG&jAmU!9TGW3_=M&!g0I}(STeALkNK5AQA{9!v!YsgUTHUkVHS!I0Xao z55UE|kO&EULE~V6Oa{UU90Ysu0E15;0PzKmGygw>OfU!WfGi3LAZd;~9wPAvjlrOR z@ql8#_wmq!#B3k}fP>2j^b$aW#vl$Z0jNNO=nsto1%QnLNVGsw*ayc=3<>G{5-|XW!aN>4em{?&RG4sJ*a(mRnmLGlK12bKciBJ}_K0P+4{@?i5q4L?QbgUn$vDD@Lb zb_bj?!J_;???CZ~N;H{fDcfQ?PlN{f3E@HR0QUdy;1>{hz_^zXc?w1V_7Hf~LFGgT zj(P#)bPxDHIrs<3-Y_Tt{xk4?kUY!r7>|d`oO7;G?R0K??;PvjN@5CD!7jo^VnAQuQk;}HSEKqL@o zh6+vNUz9o#Ac%jca0& z5C|lJra16;i^Lu`0LZuo1P>SnEB}9hKtbX*AMh+-Ao9XJ1yCUIh!@L%Y7ij$1LHt} zU?V`1Ef5s80rAsA0qYD0pMah*n2rK64>Ev(PyZi;#IO*MNpJz`02na5Q6dHxuz-Xf zilPUW0n{Op|9}}XNCASR#snW=dEOw55r@b;9z1?OkDpYU_+uCdkN@NM@Av)t^+}(` z55U(*E?0ivV%@tmr_uqS)oP|wZqH)bvVbqn2&^4;s6N2;ln>k<(nxWAf$btsyaEsp IJO_n7;4e4@;Q#;t diff --git a/assets/resources/dolphin/L1_Recording_128x51/frame_6.bm b/assets/resources/dolphin/L1_Recording_128x51/frame_6.bm deleted file mode 100644 index b2676b7ce2fc232e1fcfb5069feb32a5271de2bd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 664 zcmV;J0%!dJ0F(lNU_apf@PFW7LAU`#0MHM{0uB!zWPn=m3FHDD0I*P`Fc0upzYy?v z!@tn@2Lgj3z(8Su@q8k$iMl zV0ptV8ZV>|{0|s(<3*N$d;$Y)i0M9%8{j-({|yK{U^Qt zJZd2Fql3phHb^1-q9^f; zPruN(1Um&FocQ4dLVe1cY+wP zC&<9R@9~5|Fc^q8fVeCHhsofd*o+1t1fXF!-T?7@Arpv12N?j6K_(a}H;f)r_(Xys z{-MAqABcViE#%-p2tp4V1Oi|-7!Lyh9{g}1amW}vL1T>n&*0Mx0sMF$&Kv?6AZd;~ z9wPAvjlmisk%1$^CSV+fPOF#cm;xmA%K5?!V4I9JZF#y zWCMXgkl-L7VSquV0P%-^qvBDa$ZQY^fEZvr0C@4>HvmA(q%jZm4go+Hjt2oV3l;)- zA;ds%kO>4DA%eg|M#VskNNAtPECwI}947(<4;V!PAt8}};P5O&0wIXtKtbaWphF~i zBrw3K9td6|z=SCU0|z4H|KKo5C*lu~Gl&3a7z>1p)Bo@T$N7WFgVF&{(Rv{B7)**i zMABK7hYYZ2zYsgnJYmv}CD^Q?d4LEWVfN3x1_uC9i2ecN?1F>HzZr->`VtQ<3H{>{@OgZ&G6eg^C*SB^ z0v|$2HjY!(p0BoPnw4emdPeg-Y%z$eCh4;usmU?vz30|6HFa3Q`3I6OgPjQ`Kz z(+mWA@xb_J;4sMq1O1N&h`ewH@!&uOV-g390~PjzAq@Vps@BB;erm01K!= z>A=I_0qV&l$bkp&4-w#i8VC;@2mX#9ka-*iiVqlcgTdp+_2g)d$!fqxc7`fWH9u MKfD4E4>*E;0O;od%>V!Z diff --git a/assets/resources/dolphin/L1_Recording_128x51/frame_8.bm b/assets/resources/dolphin/L1_Recording_128x51/frame_8.bm deleted file mode 100644 index 05f98d639bbddc8653ccabef2808aeb876a4b0c3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 663 zcmV;I0%-jK0FwfMU_apf@PFW7LAU`#0MHM{0uB!zWPn=m3FHDD0I*P`Fc0vUzYy?v z!@tn@2Lgj3z(8Su@q!62UqK0wYO^1xgqQlG|x3FPo#@?i5q4L?O>f=ppCD2)?I zb_bj?!J_&=uR!sIN;H{h2f!dU*p8Fw0lovq4{!&R0RlGP{{QK52bUh=4>%+N>>%-| zgUXH%9P^N2F0l{JegW(kj0ylhjQk({1qYFQV0Ykn!>1ZZ#DDxRACw0}%81a6E1o2n6B*NRVW)^!N}IU|5)U zf*7$ZM1n<+lY`O?;4TXQVe)z=G9v-VNO)LIH-J1}2t?x%2gkr91@Xa2ykPR5!Xyz0 z1poq23=CVzz<>~h9ySOBz-%xc1_C|!;6US$FnEH;8ULTbrWgbG@IRb51TsL=9C$o` z5b${15uzy=kUU@*ul@c30SAbPU}zDLgUbl95YU6fus`S@03LpT_|PC&2+$;pLqP(T zz&<)?U_D`g^Y9bKGZDZ>LFWg+flvP*gv78AkV$X>>Hrte;1MDQAI2X54_3(o%K`rf zi10uS1Pqt@iXV`9-Y9_ahe$jgJbpiqpMeHWNDml~|Ks=X_x=0$2t4AL4Ft9+V9Rt5q_ac6%1hlmUJq0fGa^U8)bTJxA~lSpj|l?tgd$ARci9`~W@N5<~z1 diff --git a/assets/resources/dolphin/L1_Recording_128x51/frame_9.bm b/assets/resources/dolphin/L1_Recording_128x51/frame_9.bm deleted file mode 100644 index 65b723203c3cba419975bb816eceab751c8021f4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 661 zcmV;G0&4vM0FeR!d;1$^CSV+fPOF#cm;xmA%K5?!V4I9JZF#y zWCMXgkl-L7VSquV0P%-^qvBDa$ZQY^fEZvr0C@4>HvmA(q%jBu00K}94gzKtECljH zh=b$c5(xD31%QT)ih&rA(J_%23_?Rf!f+r!@q|zTBO=Gi;8=+U3lYG8gT^61hDh>Q z`g{tJAc10h2ttrhFmf(VKR*PLP9fPNIDiI$fVfD#KmGVTf06Kdz$yAKBp-hWi5JI3 zW?CFF!J_&=|G@EwPBd3wdD|j7PoxI;4;VkgLzaMi3?pqG57OfV2ag^jpa+aH4FG!x zJZd2Fql3%d7R?fd}vp5#WFt z2oD?w{*E7zc^n3c4;XZV!Q;o{`1$w{Jg*W!c*KAIAHRRU@87^hCltVFHPQ=}-?!Me vZr}&>plCH(sg&EZ*tTq-5de8X0pqUK2iP8?_y??jzX10?yaEspID&ovpRo_7 diff --git a/assets/resources/dolphin/L1_Recording_128x51/meta.txt b/assets/resources/dolphin/L1_Recording_128x51/meta.txt deleted file mode 100644 index de37d5b2e..000000000 --- a/assets/resources/dolphin/L1_Recording_128x51/meta.txt +++ /dev/null @@ -1,14 +0,0 @@ -Filetype: Flipper Animation -Version: 1 - -Width: 128 -Height: 51 -Passive frames: 6 -Active frames: 6 -Frames order: 0 1 2 3 4 5 6 7 8 9 10 11 -Active cycles: 1 -Frame rate: 2 -Duration: 3600 -Active cooldown: 5 - -Bubble slots: 0 diff --git a/assets/resources/dolphin/L1_Sleep_128x64/frame_0.bm b/assets/resources/dolphin/L1_Sleep_128x64/frame_0.bm deleted file mode 100644 index 9560e1f41551cbb914ece9b331178782165c8e38..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 580 zcmV-K0=xYI06+r&fII=<4*+-rz#ahb2Y@^Q;12+J1Hcat2lW5^46XPGZ~wslgXVt# z6&OALfBrxJ@(+LLMDH*UIX%JY4uEG3?SJ?I4hI0Z5}ZDJ_zVMpTX1>z;1CP}P(TxJ z2aE&k52?63xq$kMgUWEUU;)O#<=~D$3PT?UtTY~24p^N(^}X!1MtZ;9#?Gc=!Q>KmiVO=5A1U+yR3i0S%9w4@g)%OM?KI z9ESlpKPWt7!10I-OUyt>Fg!p77#NmdfRG@1LIflJ|MSzw0|f{Gp^y%Q2Z20K90VSM zL7Wsv!ayzfsDK2-5(vOB-?ecTh_+BD1@MSoV0hQhq971>z~C4_5X?J^;($nClnVp_ zoG?O=&?W%!m*6N6Dg=W74;T`NyeBaSjsgZ3902fOTnq>Q2mNmdC}1)Oz&!95@|c(g zE)EzBVmSUJaGb|6_Xm~)_%P@3KuqNz_dkXKf+h?cz%UOC1fVny!}wqnD*;4XgMf}9 zO98Nd3<8N5@DxN8DB(I4hyMi!j1u6P5Miwgh5nAfF%U`f45$UsdKMG=H-Ka?aQ5L) z8{`!m$L)SH5Af($WMU<-<^JyRpm+fz4g!e)s2Ax6+kXMTOhMu?69|w8;Q^p~FYs_- zZ~%f25&*-)VvrF~!h-@2I|t#WT@U>30X=|!!Qz1tAweJ;AOF4q>i?KX)-&P&v_{4M S-@)pYUZ^~NANU9~LW4vded_Z7 diff --git a/assets/resources/dolphin/L1_Sleep_128x64/frame_1.bm b/assets/resources/dolphin/L1_Sleep_128x64/frame_1.bm deleted file mode 100644 index 238b50a2e7b89ec4d08249e43b1fc9ed16fff178..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 589 zcmV-T0fII=<4*+-rz#ahb2Y@^Q;12+J1Hcat244IGGym{Dqu?RG{{#9D zf7qm80;31N$N$Iw{z3E~8_WaF2O@fZLMIM@XAST86dVo#a4Wcc_wX180M~u^1Oos> z5Cq%7;{f{u>TVA%U_PSY@|*+eHV-cZasW~lp#TmV4=e|d|HB5a-v0&%lmdJK6Ps_~ z;PZ|^`~o3AED;dI3AF& zc$WqNFgXqaa(+;F$ARMz7?+rUkYIR#3NSG&!2uvZ^n?gR)I9KUU*KS&02DF-(17qK ziQ|BS&`2|ai1IQh5E5L4gOIfNm`@=zr&M3G4&@4-^QA3JCz%|NZa}SO5HB b6IjoP0?`{6|9=OnReGTD_TVA% zU_PSY@|-Og0CBhk`T^jMKng=22A|*m4S-Ne1LObj!|UU}!LS$sfQW{Cb6>vx4?ut* zMmYoU2!sGjLGQsR5x{u-07D*XExaC_0g(O%1|J!?JqBXbADOv9;y)<>JZya6dP3lZ z^Ozim0XYYxHVA(x1VCb5V~Rn6;(QncU}9N<0ziS{04P9&OGD2%oD38o0){|35FP~a zsBjQ^2?lUc9|-`r;-UZ)7=lm?H|!og^3fK`1pvMg3(OB1`E*1A4;UN+2m%?0a2_`J z1cY`r1qne4LqM1V#$SM-3dTWz2aE|sUK5yu#{mP7Oco^q;9x)aKkIlwLjjCN0q20f zl*GU>aB#q2k;m~PgyuPqxIC~W!G}MJ0%s`)x%@B;5U^n80f2a5B>|vzAHx8lSPCRt z7~&ML8wc>fD3K2Y1RhA?Iu(ci1qX~G;F%C%tqX={O$ogfPcZ_fe|4=AR8b5z5(k0m`K($;sCTp U#sA;I>XlxoJboYe2sA>2M3T4c#sB~S diff --git a/assets/resources/dolphin/L1_Sleep_128x64/frame_3.bm b/assets/resources/dolphin/L1_Sleep_128x64/frame_3.bm deleted file mode 100644 index 72fedc5e13cf89e0b10e68e27ddbb31f86f17de5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 597 zcmV-b0;>H108s+}fII=<4*+-rz#ahb2Y@^Q;12+J1Hcat244IGGym{Dqu?RG{{#9D zf7qm80;31N$N$Iw{z32k51r-#=LHcx0ptJ}bOShVYyZFqa5x3PuHp0Fz+fB$TZ7NP z0Dxc!f&iO%JYXMSeNDmT%m>t59#e%Q01h_*pFlhj$N@-Lga9~f0)kKA)Ee;9#?GdJM&=KQnTJ z#C}o$c-Z;C^o79-=P)@A0&;#>A^e~b0f~8t2?hs=@L&;viDn210tbiyp#l*#4?G-K z_!uZa1q^_6AUp};c;F!P5)9y?J`w?M#Y6xoF$AC(Z`!zvL|Z5n0{BEPFg$DJ(GUnc zU~mi|2xc9?c-!C-5!l!iB?KuA0$>jregc3i83q6zFeMRqPGS!n1P(zkSdFa>1~C{1o&x?;69C1*!vTgzAHtXS|eir@8I=HuT&mC5BvlgAwi-5Z;a;$ diff --git a/assets/resources/dolphin/L1_Sleep_128x64/meta.txt b/assets/resources/dolphin/L1_Sleep_128x64/meta.txt deleted file mode 100644 index ffd845e8c..000000000 --- a/assets/resources/dolphin/L1_Sleep_128x64/meta.txt +++ /dev/null @@ -1,41 +0,0 @@ -Filetype: Flipper Animation -Version: 1 - -Width: 128 -Height: 64 -Passive frames: 2 -Active frames: 4 -Frames order: 0 1 2 3 2 3 -Active cycles: 2 -Frame rate: 2 -Duration: 3600 -Active cooldown: 5 - -Bubble slots: 2 - -Slot: 0 -X: 53 -Y: 20 -Text: In a lucid dream,\nI could walk... -AlignH: Left -AlignV: Bottom -StartFrame: 3 -EndFrame: 9 - -Slot: 1 -X: 53 -Y: 20 -Text: OH MY GOD! -AlignH: Left -AlignV: Bottom -StartFrame: 3 -EndFrame: 5 - -Slot: 1 -X: 53 -Y: 31 -Text: Just a dream... -AlignH: Left -AlignV: Bottom -StartFrame: 6 -EndFrame: 9 diff --git a/assets/resources/dolphin/L1_Waves_128x50/frame_0.bm b/assets/resources/dolphin/L1_Waves_128x50/frame_0.bm deleted file mode 100644 index aa7454666240b57190099b0e7bee4a1c218982cc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 443 zcmV;s0Yv@*0Ji}EKZDN$0uSIHI1l_hC_yp+Kl(TXAbno|W$=T8=l=m8m;8S@@YzQ{ z;|Rb;N#%r(j3a>=gaHbAFNy?V2y=iTh|nIN3xE)^1%dz&JnkF-$%E8XcZ>i(fI>Vd z5Iw(>@OfT==l=hI7xD}qsDNN!`ltZ@uzv!$wNvmY>VOIQ1`hro;Hv-Y;lrv`YALe5 zeQW-p>Eg9gpdi1?-~|!V01Nw9XO{x6RUN1Rf7eYZ_2fsd!ydOzcpaw8M zz&!VwudVz(omdZnYYP17oUI#WkCQB3=l9tQ9sTB z_zw-&fPEOiJl|;86}W>&zzHHE01m(n8~{AW<_|#0-wKKkNCXH7Kj4_H2mqb}@t+C| z4`RT=0)!nHa6O3wFai)fVSx4=4FDcUK?j$D{tq~DfWhe;z(>L593>i%&_u;71nvM( zAbH#ah5Uiu0K!lrc+Lr{{DYtcwm1k-J=-Y*pv6RnfN>y)$a-J}C5!@q9}s+;1b%-2 le1E_Khb)7A|KLM6j6fQf$OQZjhS3ME06J$wkB{VRvG5NIyQu&G diff --git a/assets/resources/dolphin/L1_Waves_128x50/frame_1.bm b/assets/resources/dolphin/L1_Waves_128x50/frame_1.bm deleted file mode 100644 index a23d250b969fd3914a84d4ad0b4e0594ed714ae5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 448 zcmV;x0YCl$0K5SJKZDN$0uSIHI1l_hC_yp+Kl(TXAbno|W$=T8=l=m8m;8S@@YzQ{ z;|Rb;N#%r(j3a>=gaHbAFNy?V2y=iTh|nIN3xE)^1%dz&JnkF-$%E8XcZ>i&|Iz!v z7)ONy2d@MF{|A-m9)I9afC>lj{euUpAQ%_^ssKCdjDh2-pZGuepaMsMgT9COD!=;p zaO#y>ioCBMTK}jzc&${c{i`gOgIB5@AOOGpcF}Mu^-NYQ0RcN*?e&e_#Fnt92cSJnt0{@k4=QueBotUVi}i(R!tVJP0&c$6z0m1cU?c zK8!F9UIF`e qCxBGs1LOJs|C)vY2p>QFFjxrqJ-!eh;n9Qb{#z^p|A-&J*>B)^bG`)t diff --git a/assets/resources/dolphin/L1_Waves_128x50/frame_2.bm b/assets/resources/dolphin/L1_Waves_128x50/frame_2.bm deleted file mode 100644 index cd39b17ec8969063183447fd5463a0ae27cf0fc4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 463 zcmV;=0Wkgn0LuXYKZDN$0uSIHI1l_hC_yp+Kl(TXAbno|W$=T8=l=m8m;8S@@YzQ{ z;|Rb;N#%r(j3a>=gaHbAFNy?V2y=iTh|nIN3xE)^1%dz&JnkF-$%E8XcZ>i&|B(B@ z7)ONy2de~c;PSl#&;9=Z91r371`kv~FfaX70C(3J1IJZA@PG9{0s;(p7(3{Hf~)_p zhYzX4sjJHI^{@Jar;62jpc>-IcmYB>Kmj_%v|IoF&)nla2hZT(ZsAHG0OJS|F_1J; zh7Uq0g9qRZ5PQr90ty}gKCBu9An*V~1_!l+0YQh8L>^oMIRFUIIB4)Z;gg5~;6Mau z7{d^H0P-5700b~U1^NM?CX8_waDRXhVaL&cR1QK9Fo2ST5MK-qf&_;ZgWLc^eyBlU z1mOTTfCfSG2gd=DQ~>z?0q%Ps0rCOvUJwBY#t;sF@r;Th4`h(Kh-h#y`yfmJf5=Gw F2F$Z&xby%3 diff --git a/assets/resources/dolphin/L1_Waves_128x50/frame_3.bm b/assets/resources/dolphin/L1_Waves_128x50/frame_3.bm deleted file mode 100644 index 2d5452d7c931fbcc2cb933335456f4f647e40ed1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 472 zcmV;}0Vn=gaHbAFNy?V2y=iTh|nHi$bZ7%2`qtNfCU&xjsD@l446GH(}dvs zFE{{vaOZb}7~jC+JSY*k;4|fUUw}h;2cP@?m>s}Yx~e#<`w ztE$Z+sDKB0%MeL$Dzl)ehzbsX7zFqpAaQmb@~Ryl@OVN)fU1LxEGW@{e*xqIAJ{zr z2l!u)zyvQs2e{;1MAR?={4f6ic>sj^AO-9Ygj6^He8M1l2>3kVfcKtI0prjIzxw|O z;AqjKpeBrP{E!>~aDRy*s2{{0lfV$p5@mtVK%fzTNnj3zU<(!cp#=tsOomDXAo+d< zq5x2_&;#T6S2+xT2gnD%fM95H2f0we_W*|j_u>Bl2*`o&{%(o@fsFSbfb|Gv1PCYK OI$#3+K!3#e8!QB;G|RjDQco7?di^51Rl;u)Gj_%m726q2vr=0C>O%Y7!X&24R8n zh)|%w_<{$@5kvxz0fQxIgkl7w0B8|NsF;XAJ_#fLl;I)@34o*qEd+qX!Sf*hMj;#& zDFfXWF*roRK%kmL0!a{t1L{e9q$h?!{DXtuDgUUHhd~E0#!LT`0uSM?UKtQA-7L0R1hYVN712hFV6ap|Gd#F&xVj&O%T7YCsC@?+hkO&~K){|ERc09Xc%RjDQco7?di^51Rl;u)Gj_%m726q2vr=0C>O%Y7!X&24R8n zh)|%w_<{$@5kvxz0fQxIgkl7w0B8|NsF;XAJ_#fLl;I)@34o*qEd+qX!Sf*hMj;#- zhvFU&bXdgU6A1)8g%pSck|7N6Kvv`cStat2o)xIj0q+$5)Jj5s8V7>@4_uf3CUraT zJ@H@t8^mB33H#*02g>>B-i6?ggY0}i1Nhtl?f=u@2gCe>zz{4CAjyA%LmmgftZ*Ol z2&`Kd|CfOWfNUQCgg^&?$xq9oP8t32KJijWfCur9ql23PCV+uU{r_LbGzf9{;63me zpdoND@G$&cKxN>#t6Ts!pc!5v&W~N`v`S)hAOpq;!UQk`SUd+J7>EKe0pf8G f13UojemDXL)Ive`i@Ey%2ET*(n*auOz(3#s%S5EX diff --git a/assets/resources/dolphin/L2_Furippa2_128x64/frame_10.bm b/assets/resources/dolphin/L2_Furippa2_128x64/frame_10.bm deleted file mode 100644 index c5312e5e5a820aa4379d24bfda9c4e3a6c296f8b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 465 zcmV;?0WSUl0L=ma|KJ!1JOBb92k?KvV|ai<1QYz9hz1J~2hIcX0~p1CgIV|t0%5=@ zSun5w`-VUukO3HgE-(-8gboCi*f7LHlA#oMki-Dc9wdEcFgU`3ATxj=M-Bu3hlAJe z1_Bj>;eQ(miEr(rzy}T-4@vmuCID3D#ndPOCK}D<0GEJ3z=sR83SEjo^TZX!IK-3> ziNINn5C^BCGGK6e{VeAfmnYNr-w(X}2yp-9oF4V?A^-pJuzS}*>FzTkF3V*x;2tB9J0MLWRxxj&o+Jp}~{F*?nWid2Jyde1>pZ*FDh=qV2UziX+ zED^wYUSK5g2etzS1TFe?zy&~B9{?XR02xcc=YWHh6b4apN=5XjI9KX?h!Y8XA|2#F`05eA_3Di@J)Nb`;Ym%t`9dA(X8 z2xA!h!RrWO7=TX<4>Yid1Rr#W1wpVVVUYvoKo7<-s>ojD@ogA9WF`fO0g{*xBzr)@ zLIoR`JYp*P;9np(2!R2B2Oz(L#EyUig8+xIdw|dZ_vE;P#t1?X1b+8pA;ANu0sP;Y H1;juD{u;iw diff --git a/assets/resources/dolphin/L2_Furippa2_128x64/frame_11.bm b/assets/resources/dolphin/L2_Furippa2_128x64/frame_11.bm deleted file mode 100644 index c91ed2fd23efc317a9fec7f9244f5ecf26bfdaf6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 698 zcmV;r0!94+0JZ}EfFeKmfrIjX59|Np9uLp{LL3k1U_W#*kbcNuF#vwR<^cS;@vIO) zd(Z#CF}ve9B@qX-JRV&5#tfn=$M69PXF`dC-_IZZ0Sk>X2aoI?k-rCw2tNFDh(7RG z1aJsF{OCR-a1Q{$x3&;_+0i069sK5s4--KE3qB7Yqr`7u@&FI;)>rrp1JI!=ED16{ zi|2eOP$&mL2sL^*JbV9r{(!)9X&8`x@E~~0{Q&eFj|1S45O~kx-2i$rMgb51`=XFY z{7@c~_&7X%AB}K5FZveQ1B3uPee_r!fBx`1X5jOjOTpql6S(+u|M*Be_!7x95WXpK zPLKW!4>$%49$ov|$bZxR2|R!U30bH0tOFAfIQ$(2*5D`?RtQVL>}ORfY^cfK@wo4#6MrnKs|tmU<0WTLH;XP z27sI(^o9V%@FvkNl$ajh@M$c>{x_Jwy*v;P#4Z6m2#gaC7YC$sfA@mGI8b;bBL5Hv zl>>p}ulg3lJ^Tm<|BIl5$ALfoSM3lU2l@jagnjXN6H#zK_4KgJKlR@P9)Acv7K8uq zuhJkK&-_qkJ@}dkx*z}k{!f7M-}}L#m&pO;={q_P6AHB35AhFN`%)TJ2HpaDrD0~`qk(=2CEZ z0f)!)C5Sy^VDfDd6n=P6c!CNF4@>zJeV5sIJm5g`(0}*gLcxQ{tS}II8iWXe@eB|E zXaM&oh#-OHDi9%W;4^FixNpGtzyG;6_}9XI3LFReE(ABI%&-txX8^(=_6Q6zF#`F| z%pwXHSUm6OQH)?em-v`H|E9r!(1@c42fM^ULUj;%Vgc))|K1Fy>Kb?a9=!ZL{EAT6 z1w0;~ugGj@5C#axa1?NQe1YPw7#HjRdiZJo@l^SIcmMtl`T!3dm;X)wiUZI8-@skq zAn}sVy5IRje7^$Wz<=Pt@uKiS==&f4;{o%GgT|Nt_kZdD@qLIqwsLTI)$jj*{(v4o zPrv8!!7vCsb@%_@{(}R60SDkA5P1JeqyPQ^#q2GC2cG?kAOw2~a6#jN_us+G|A+7a f<$u2eTmSC}k$89X5PN_N?%5stv_aF0B#1shQS}MQ diff --git a/assets/resources/dolphin/L2_Furippa2_128x64/frame_13.bm b/assets/resources/dolphin/L2_Furippa2_128x64/frame_13.bm deleted file mode 100644 index aa5353e988d02785f45759a024931abe7d580086..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 584 zcmV-O0=NAE07L@+fII=<4*+-rz#ahb2Y@^IANT|F;Rk?x!S}#`z&(Kbz(C*~0DZs# zKZbz<@kP z-_ZGk-W5OxmVe$YUN}5vRseX=FaHn!_#S((c=Kc+^8fh5iJ!HD#(O>ojswBL|KI=9 zyXAl$Ey0L`%fG>Z-}v0Uoqw17zfWUvCftXMrUwwc* zFdOIv=nzr}54)K^iv!95wSa!G^7S~tJCHd1P#$^^c)jswmy;2(?+ml*DY%6Tpaje?1R4~z?+C+R@JB71OJ%^j$i~g#Q}fdP!0e4@$U+bI{^s# z3(Nlv;0TA07lYt1>XG_?f57p%{4ekc#4b7D#lRpxNFUrDAR+mp#tHC`{fFbU28aM| z{|{g!U}Yi1z%rjedF|ox@OZ&E5DPI#e_na`JbB{LzW^w(J#=94pcIrmEe`+y diff --git a/assets/resources/dolphin/L2_Furippa2_128x64/frame_14.bm b/assets/resources/dolphin/L2_Furippa2_128x64/frame_14.bm deleted file mode 100644 index 837c6c71defd8be280776e7881817fc68276b5d0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 610 zcmV-o0-gN<0A2$B|KVUk{Q*P)Fi?2?0rvt2kDwoLFnIg{_W=j^KOjHgdBMT)0R98a zDj@jq1I#KQ_}~M|;Q;Id;{Xp%fIlVx`hWxc9=`B>kL(_GhKz#+o><3>gVG5B{Tt>8 z4;U5;!}CwXJVamY_&z)lfe}FLAMe0Dt_H!Nasoqr`-9r~&_@VJU>l>0Krj6W95O)w zgJlpe;FN#haR>y*&>wsRUrhppDF-nZ{sYsG|A@(;VDX@2Js+@oPGA%6%nc+tE0kSi zuxJH>Lj(tm3qcDYD|B!X2tYh=AoBP#)&UWmL9h@2c-lSS_KA8RbO;895vb?`+9^1J zKqL|g{UbmRX+$ywAO;Ttt3U_0LJkiYg@OTE2b}yKQ3wz`aF!;BgU_B1DFg(5A@ZVV zdEoJzgVn$w5#o2S;PHin%RrqGp+G;L5P3mC<-mYJ58@9VCwyLBV0pmdiSdjD2SN@Q z8NBEa$N2-wgZ=&g0qGDH4O%!v8W;Zm|NDc@SRg@@1ImB)|6{@DKq!m?&N3Yo|NZ%| zgUlq?4N0o0xY&yEj|qzoQt^FED>06iEa;GRJCuV4t{^!eXG#1C2(0)+vGdoW=5 z-AFJnC>}#T4`yhz^{KA$Q(_^4>({vevs<0996^* wNHeH?5Gg=DU|{j6KxP1O`UB1b0N_Ay5Dfv)f$A(Dg8+f^cz{o!766F@+?Nvri2wiq diff --git a/assets/resources/dolphin/L2_Furippa2_128x64/frame_15.bm b/assets/resources/dolphin/L2_Furippa2_128x64/frame_15.bm deleted file mode 100644 index 0000a886313b8c265743efe166ccfd156492d830..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 740 zcmV2edpMFaQvP#()oKH^LrM60rl^I503^{{iLsHy|)^ z0P@TO$c~T*6avV`F91BQ2NQrnAQ7N>U;*j-2b3ZK1#_7gbPDB+UsoNaWG5dPb%aa%Ma{d z|DEDsz@&x(4~zyiF!OMK^XY*gSOo$Hj1CRJ{O`H{==f-1o&bS^#{qtz;s2(wI1VBV zR2T#eE<0=n8}}~2CKq!|)qA>9R0!Kjw;7H&PBItkNnc#WQA#nkC@B-!W2amiSaeM(VZUFM|55zur z3vdiz5rDaP0mKg|26UD%2yi^UB;p606xKKhI6S}qaC+Xa&=0^Es2%ZpVYol}I2f=O W5qn`E9|>4I?g03JC zA2bOE00<&62h@N_MMD3>!RtVd0SE*!_y@oqya2gi1HuI$kb}V0E5g61P?e66wYH9giI33j52{(AdrB`gVf+cal;Nwlp<4z0LlXagupw1_IjTN zArTdbFoEVNECQfnMiC(pJ>mfYBMcB;p<UWSb^&U9gP8?HUvQ}Q-S!Aj0Rwr5P2)idN68af)NZK2tD!;9ESo92@?RI zh6)clN5Vq^MF;@_GKB&s2`okA)ew0}AXG>YEJP^P6xl@}IEWEAK;n-a1iZRX z2$W|b1X>6jLFI4qM!wUzT z3Mdau03?0jVqgpk5>2tL|o-Vkkr_W=P; Xi41@f1_$-wIw13am}x#>`Gy0~KbO0F diff --git a/assets/resources/dolphin/L2_Furippa2_128x64/frame_17.bm b/assets/resources/dolphin/L2_Furippa2_128x64/frame_17.bm deleted file mode 100644 index edccc7396ec68d5dd2a861017a681b2763c1a4c1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 451 zcmV;!0X+Tz0KWkM6hI%*fPX>)+z)6lKcGPUz(7I#PwD_arU3pz{4gKTMmK;k_{2d3 zDe-;_GJ(K9|L_=t*8Igk0EA=6#lS<%$2b#|96X)?8DOCw>-+=J90#Cef#ZOI;2`rr zNu*W-zzdWr%n2f(z=7vr0gnR~(2t^UO zA(FI0F%QWy6vBy!gahH8Qk*0~K(B;I3|a{RiG$`r{)|F6C{hQyEMjnpgn_!C1EfGC zkqBTsfDa_TQWL`<{z1X-6#vvpL!g70V9{3ED{Pb`z z{6$QLp%MGwB+w8k2!*2@&>_PW@qo<%P6Ysrd;zFN8>kq@Vj&QFT7bk%C@{HXFbbps z2rLtU1Ka>0(GCd2hUf-62Y?DM5-X@w>^KMRlm@CSfA0hhpbq?iBy zfrHw>1U~>!W5l;g-C!L zl0W%Q5+I;30>DxO7J@)xVEK@LqY#b?6oKxG7@Q(uAZRcEsSpVyLKqJ)14%EGgz(5e zkZ^m&KlKuj=pg18$$#=-LHsq$0JyLIj0iCIxqugy^V7gQ1=t6u@PYIH)8HS)UkBB| z3OoV&8U!AM(Ju(SP^F9i%fST<2d;7ffXPqKM*|0_D59#990~of7zs24E{z!Gfesk2 zj0R{5a3};|KKD?ejKo492DJdlm{4VjjSi^*f(s2~KnDhha7H8miwA%hFA@Mo0zNrV r1;+vWK@bLD3)BEMcz`zG59&Zam1IO9e?TBI0DiqhA5j1hwgdnmDfpDF diff --git a/assets/resources/dolphin/L2_Furippa2_128x64/frame_2.bm b/assets/resources/dolphin/L2_Furippa2_128x64/frame_2.bm deleted file mode 100644 index ade67d1016ab61cb52596c9cc607f61b30ea100a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 402 zcmV;D0d4*P0FD6wcmu#50PqKZJOSVj0C)qy9suwMfII=<0ssHtVC(QeU=iSb&HzL3 zgM;h+e}H-;fa$RjDQco7?di^51Rl;u)Gj_%m726q2vr=0C>O%Y7!X&24R8n zh)|%w_<{$@5k!K2A>j55m7)=dhC$9x0x1;}5y*hZ0qjX1{HF;JzCZ!*0Z0s52?33R z=0X09LO3Q-pM%{NF*roR1;7Wq0+9fcL@|&bgnSO2M@{~-Pg z`;~HE{G0+20RKS2?~4EE)P?_Wjes7!ub!O9I1l(BLGqFw1^f^<3jjTT`f_mi55+jR z1Km0&ija90cgzFaY_m wD@TC`afymcg5DYcK17NS<{sClcm>^jCiVOk07yZoxc~qF diff --git a/assets/resources/dolphin/L2_Furippa2_128x64/frame_3.bm b/assets/resources/dolphin/L2_Furippa2_128x64/frame_3.bm deleted file mode 100644 index d05e8ae2b4715a5e6ec362ef63a96ac6b1ce0895..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 374 zcmV-+0g3(r0CE8Ucmu#50PqKZJOSVj0C)qy9suwMfII=<0ssHtVC(QeU=iSb&HzL3 zgM;h+e}H-;fa$RjDQco7?di^51Rl;u)Gj_%m726q2vr=0C>O%Y7!X&24R8n zh)|%w_<{$@5kvxz0fQxIgkl7w0B8|NsF;XAJ_#fLl;I)@34o*qEd+qX!Sf*hMj;#& zDFfXWF*roRK%kmL0!a{t1L{e9q$h?!{DXtuDgUUHhd~F9crWf%$$#=-LHr>E9{8{R zj0iCIxmW?~%K7Qw9sn@V0rs9Cc-#a+4^RG|0RAfc0&#E$xFEOz!H-S>?oAT#&^!?M z@I5hq`FIqefboIDfyaQ!PuYh9Cx`)}s*_NOaYN-MfPqLvEg0s34j2(2`(}Wr0)R#X z?{x|o%tRspYfub{g$4(`QUL@O8pwbS4G`drNC6fP05D!80E`5E0ALM{1NglICNEF` U+2R1(fIq1K{-6W=AK?E4z^3n-*Z=?k diff --git a/assets/resources/dolphin/L2_Furippa2_128x64/frame_4.bm b/assets/resources/dolphin/L2_Furippa2_128x64/frame_4.bm deleted file mode 100644 index 5aef127622c6a77c35d1a33e111aca2f49d2cd7a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 440 zcmV;p0Z0A;0JH%Bcmu#50PqKZJOSVj0C)qy9suwMfII=<0ssHtVC(QeU=iSb&HzL3 zgM;h+e}H-;fa$RjDQco7?di^51Rl;u)Gj_%m726q2vr=0C>O%Y7!X&24R8n zh)|%wQ2`JD;DPdlQ6Qhld>|g7Vzfdr5Xd~@AP-m(NT`^OL^FU;NF#G0!h2Z~y z1R(cN8C*Xf{P;cXKr)^X2ObZ4qyh;5PLIO|QJBX8IyZ~}K5Pon;6dDCVv^vuhJX)| iB7^yeKn#up`Kn+F;5Z-vw}E#gL;;=wcV7v;e+58OX1XQ- diff --git a/assets/resources/dolphin/L2_Furippa2_128x64/frame_5.bm b/assets/resources/dolphin/L2_Furippa2_128x64/frame_5.bm deleted file mode 100644 index 3be1790d74ca3b2fce974f633aaa7e01de7b81dc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 449 zcmV;y0Y3f#0KEYKcmu#50PqKZJOSVj0C)qy9suwMfII=<0ssHtVC(QeU=iSb&HzL3 zgM;h+e}H-;fa$RjDQco7?di^2q-W;Yyl#|@IeI_2duyZItm^@#xMy0JwnA$ zkjM}vKnJWaK2Zt~@UX!3f(ObGM1p@I_<(wbO3?_!Lm=~rfee5J0x1;}5y*hZ0qjX1 z{HF;Jz89VW0Z0s52?33R=0X09LO3Q-pM%{NF*roR1;7Wq0+9fcL@|&bgnSA&^yK02ABu5sl|h@N4$1{oLzFaIwB3HU+d1BXKa<$%dg$HIOkbHE~@p#TZ^fDM3?0*L%z2j+mt z{9*Ua0Sm$Z00=?upfb3AKl$)`+JI#|APzhp^+*H~0G%I(45KlQ0(5T}0DRaLqrii> r#Kk4SZw&w+B1H%D5P%sR2lG_G6~J&n0dE5CNQeVG0`9&Odj1N48I-`J diff --git a/assets/resources/dolphin/L2_Furippa2_128x64/frame_6.bm b/assets/resources/dolphin/L2_Furippa2_128x64/frame_6.bm deleted file mode 100644 index c457e78771463e34c9357d1c8dd27ddc2984d33c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 466 zcmV;@0WJOk0L}pbcmu#50PqKZJOSVj0C)qy9suwMfII=<0ssHtVC(QeU=iRsfe-*w zfDrs3;Bv47+x>rldLw{xzzM_vcnBT>4>S>w0AN7$;0#I?W&{)(2oFKAa4@_OK}G@V zFaZvNhmakVf)Ec#zzAv*83F{T0QH6k${|970Y?oG2cXod5Q-!d`47Yc)G}6xMj{yp zoJ1lZ^g5KniHPJtWB~RgkN#7Hh+hlBu4oECV$etoY#%ZY^kNagGK~Bl=&^~yCJ-(F zJ>V3G1d<_)fczui_mW>J5(*px@ec+gprW7pj{%53$UlPqrmA>d!Z<8ZJ_q1bSFfAr+x@E?kC zaFs!uqz@$MpN>5^kU`GLIxQa1~30F0txs*;{%670p)fI+{{RRR diff --git a/assets/resources/dolphin/L2_Furippa2_128x64/frame_7.bm b/assets/resources/dolphin/L2_Furippa2_128x64/frame_7.bm deleted file mode 100644 index 7e83e14a5ae80fed0dfe4163d65bc5bb431613e6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 350 zcmV-k0ipf@09pY6cmu#50PqKZJOSVj0C)qy9suwMfII=<0ssHtVC(QeU=iSb&HzL3 zgM;h+e}H-;fa$RjDQco7?di^51Rl;u)Gj_%m726q2vr=0C>O%Y7!X&24R8n zh)|%w_<{$@5kvxz0fQxIgkl7w0B8|NsF;XAJ_#fLl;I)@34o*qEd+qX!Sf*hMj;#& zDFfXWF*roRK%kmL0!a{t1L{e9q$h?!{DXtuDgUUHhd~E0#!LT`0uSM?UKtQA-7L0R1hYVN712hFV6ap|Gd#F&xVj&O%T7YCsC@?+hkO&~K){|ERc09Xc%0uQ)60v!(r zR{%IO3BW$C0V41N>M#Q=83LhzGJ-(C^9BKwAP8Uv4IDx~4vYZk^aVmUI1ua{fP7E@ zhakcO;=ltb^g{%2i2(?>Fo=jgCIcd#L*yt3hzN^^2B5%|)sXl>LGuU!4I}vrfdk=u zQjhdr1%S2iy=^F@Sx>ClCl=KKu}Lc!TT!I0STn5Zzz{xC8jTfC7gD R_?^t!2l=@{{txc}J^=C5d%XYv diff --git a/assets/resources/dolphin/L2_Furippa2_128x64/frame_9.bm b/assets/resources/dolphin/L2_Furippa2_128x64/frame_9.bm deleted file mode 100644 index 269e5b1d860d851c52cc46fe88b260d4ba0f9a9b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 317 zcmV-D0mA+P0675wcmu#50PqKZJOSVj0C)qy9{}J$VSx>WL;?FSkOOc32Y^r@AJ6zg zZx9F%f(HPHgW~}KfkyzNa2Lb^4S){_RDtggh*$vU1_!_XK^QuqJ|N+vyhMTVj~yS< zRu5?Tz<>q%l!Mr|GXOFDX=n(91Jb4sgGzO3?r{gO9X`5w;sv+>RPy2AV{`xuwRpaA zI)DZs1xQicLF*6$EGgh1g_J=6$94!I?6c8+M zaHrubuL1@SQ}U7|9|VDR$ss@pRaQI$8=1_lq)tCP%rP)h-~V2@}Tv@jpB0&{>Hz`W`JKd=njggbmVMfl(rNH|CM PkTM7aMhFL+tl;4G_Y8T; diff --git a/assets/resources/dolphin/L2_Furippa2_128x64/meta.txt b/assets/resources/dolphin/L2_Furippa2_128x64/meta.txt deleted file mode 100644 index c21027e49..000000000 --- a/assets/resources/dolphin/L2_Furippa2_128x64/meta.txt +++ /dev/null @@ -1,14 +0,0 @@ -Filetype: Flipper Animation -Version: 1 - -Width: 128 -Height: 64 -Passive frames: 8 -Active frames: 11 -Frames order: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 -Active cycles: 1 -Frame rate: 2 -Duration: 3600 -Active cooldown: 7 - -Bubble slots: 0 diff --git a/assets/resources/dolphin/L2_Hacking_pc_128x64/frame_0.bm b/assets/resources/dolphin/L2_Hacking_pc_128x64/frame_0.bm deleted file mode 100644 index 3ff70a91699daba73b1e15ad953597404f21fd73..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 543 zcmV+)0^t1t02>1TfII=<4*+-rz#ahb2Y@^Q;12+J0B`Jbzys%g6hLdw|K0cqe5e5T z@Be+^{ICCh;P->@0qgMt6&wa%nfN_FVDqCCi~Yd<1JYxS;(zF%kh%paaHZP5cASF948q4a5Hr#4N}<5C6Ua`VWl;A`dt!&`zW8hy%nv!0~`!f5G&n zWP#@b2aE++H9-6W$_4okq)X%vEBp)|gZKxW6XYLBC-@&f|JZ%E;Prrm#$y!ZgW3_| z2bg_v;Pdkb*T@eqyaXQM_5Fj?o!c9`#aq|Apyq2d!Ju$TE2Y z(jhqNLO%fV_)uUL0C{jf^9sN`RpSeXBSap5&;5e&4ERJ~5FvPj&j3dr2R;wr4~z_6 zzW{iR4gwb-0E5U}Ao2K5@DLxxRKm7gw!T;ge!Qv$-0kBDfIJLkx z_$%1iFc1$N{sDOZ4TcAPFBp8ImR@_1@VA>6y~llIsgt14`B1)1DF5+ diff --git a/assets/resources/dolphin/L2_Hacking_pc_128x64/frame_1.bm b/assets/resources/dolphin/L2_Hacking_pc_128x64/frame_1.bm deleted file mode 100644 index ed11583f8f8149118f44b8da3aa791c579bffa72..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 545 zcmV++0^a=r038DVfII=<4*+-rz#ahb2Y@^Q;12+J0B`Jbzys%g6hLdw|K0cqe5e5T z@Be+^{ICCh;P->@0qgMt6&wa%nfN_Fa~y9I|3)bn`+@uipnT|{AI4rM3b1N{@`-$b?E?IV=Kli+p#0>Y;Cn$nLH!4p|Mnjkrz9R*@Or>O;vZ0X!aP9p z508)@SnzrIgY)-*gWNtpka>Q=>Q5m0&q3u#2Z_9g=S2sIIA9u%H@{W-{fEwt|BnO8 zQOom(#2$k2jRVv69}>a9A0rk5lw+0afPK945PDOA>D$$MzhLo|KnLSK2b4ppyMTJV zV)ZYWJcHxgClhpk_ptl!|MU0<`#=A?gU_)$fcmdt^WTHss!t#Ay)EGNt9n@mPat|k zCml#f;2vKJ3250rBO1N>HKun6_R8bk9J3=_fg1CN3p4_|}^4G7=@#fE+W jd6S9=SST1i2jn~unFHUzUl<4D{~rgExRvGrM(KmhGyew= diff --git a/assets/resources/dolphin/L2_Hacking_pc_128x64/frame_2.bm b/assets/resources/dolphin/L2_Hacking_pc_128x64/frame_2.bm deleted file mode 100644 index 41850505b54db86c1a371b051d9d4d1a5eee03da..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 548 zcmV+<0^9uo03ZVYfII=<4*+-rz#ahb2Y@^Q;12+J0B`Jbzys%g6hLdw|K0cqe5e5T z@Be+^{ICCh;P->@0qgMt6&wa%nfN_FVDqCCi~Yd<1JYwn{D0`6kh%paaHZL5PFSF948q4a5Hr#4N}<5C6Ua`VWmOBo8<$&`zW8hy%nv!0~`!f5G&L ze1Ycz2aE++H9-6W$_4okq?7y)EBp)|gZKxW6XYLArz9Ug|JZ%E;Prrm#6CVidqO-w z^AD~(9)4i@{z2vUfP>sVzp#3f$bPfXc~Sx5Zy@>6LE;V=2BVGd)qcNw5P4C*@!)w% zIeu{X{>SSY2dC^lC4+!IMl1y=$1B$W`+4Rd^rr*Ux2pAi!Q(1`55{~CD2G#b0QGvs z>R&K<2gkNfCg}g~VfWqt=kO2qfB$y}pJI0b^09*q9fII=<4*+-rz#ahb2Y@^Q;12+J0B`Jbzys%g6hLdw|K0cqe5e5T z@Be+^{ICCh;Q#o6ijF!s2lyU;uzAsdWyk-5NFGxhZxjDT2bAUk&Ih0LUhr?^{AM%& zc+6?PfO+5WFg^kBf8qGw&+%42{=M-4c!%2G5C@OYK9sDGJo@mE zc=|o{K>R}C!13q@(k1c-oEH%f2ax_`w+Vy;Kp#mW+(G3Mh*81i|NV#C2NLAw0p$S) zjK(R+2bK3FD9}BG9%1#tVEmF82t5Pi9${8@q z2*e&H@(-OCe-PjU%K`7ze!pPzqi@@zuiwBh9&q@B(4I6H2k;(Wu=xGpEO9&y{{sNv zSHa`!fPK945PARLL1B}F$G58We!=4^fKi*n0)R(>Kwd=sMpFN#Ee^9sN`RpSeXQ-Omt0T1*4V7x;<5f}m}XaK=BksNp&_&&aA;IDQrw8%>hrtZOCMWe~5Kj4f5%sw&91P}4qAq=a*2S$ke#lr;fe8!`HkEck! ua2PZrfe#7~;P5^Gd6S6{KljlC++%42{=M-4c!%2G5C@OYK9sDGJo@mE zc=|o{K>R}C!13q@(k1c-oEH%f2ax_`w+Vy;Kp#mW+(G3Mh*81i|NV#C2NLAw0p$S) zjK(R+2bK3FD9}BG9%1#tVEmF82t5Pi|pA|Cm+*;;$H7I-Cp{;0S-8`vu||@QA=5Lh%L(w20%t=fV5| z@qvrR;14f=JV<50dWnFs;zAb)Jbn}W1PAd|tJPtUFnFE{0S*rj|3Clyf8p>$Fo}u) z1rWH-HGU0LVlXyL1Ovx^fL=erp^x|@0P_!wa{&YVc1S}i@Ild{KQVB@JRdQr-{a}h vFPsJq2;f7)gZMlTfF5MxL=XLTK=&V%0zC)^&w#!n!JmW8TuSo*9{?WUP=735 diff --git a/assets/resources/dolphin/L2_Hacking_pc_128x64/meta.txt b/assets/resources/dolphin/L2_Hacking_pc_128x64/meta.txt deleted file mode 100644 index 8ad8d42a3..000000000 --- a/assets/resources/dolphin/L2_Hacking_pc_128x64/meta.txt +++ /dev/null @@ -1,32 +0,0 @@ -Filetype: Flipper Animation -Version: 1 - -Width: 128 -Height: 64 -Passive frames: 3 -Active frames: 2 -Frames order: 0 1 2 3 4 -Active cycles: 4 -Frame rate: 2 -Duration: 3600 -Active cooldown: 7 - -Bubble slots: 1 - -Slot: 0 -X: 22 -Y: 25 -Text: Mess with\nthe best, -AlignH: Right -AlignV: Center -StartFrame: 4 -EndFrame: 7 - -Slot: 0 -X: 31 -Y: 25 -Text: die like\nthe rest. -AlignH: Right -AlignV: Center -StartFrame: 8 -EndFrame: 10 diff --git a/assets/resources/dolphin/L2_Soldering_128x64/frame_0.bm b/assets/resources/dolphin/L2_Soldering_128x64/frame_0.bm deleted file mode 100644 index 3fc3644065c7118acc02181bf59501908732b125..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 699 zcmV;s0z~}*0Jj4FfCfGf$p7FUupbCNU>qQN{0AfeDf|P74_<&hL4%kFv=0CeUw}U$ z;QxSl0sBA)zwdqz*j|DT1s}vdgXE6`-|ikC&OYPf7r=cdfcGbeJmc}D`*b3CAG{16 zU9fxV^-+412Js*Nhw|~C|NKYyIQ@ghI{@Hc+wuSXugYP76pcVikN|*`h5-zKLxb^u z2b2l{6bGRIV1M}j;2tm-1dtw{{rZrHD$M^G($N}S! z2Z?aK!1Dk7!RHWwhui=$_=f@>42~UJqSM`&-CydE|Od3pbU@dk(pe)D*Gq;Medn)&zO^^3>E140LmD=|NxemDa1 zh0p8|37|YdBfpq3igEt3^j~*Kuh<)aUfs$XY5Gs&7yaCY%odWRyQSg+4w=1yg|qkID<<)vNDd9{|YR;1AKy{v_k^4~~`r2*5zqBLet&|I!k9 z(O?iy_x^nVfW$YDaCp!G<3@l$;Lzy50s#>GK>R=Z_~>-x(YAXca{ze!ATtx0KqHa( h24lnIhl9ivFpznG2ski+AcM*d4`jHA#Oh?hfB?*oKqQN{0AfeDf|P74_<&hL4%kFv=0CeUw}U$ z;QxSl0sBA)zwdqz*j|DT1s}vdgXE6`-|ikC&OYPf7r=cdfcGbeJmc}D`*b3CAG{16 zU9fxV^-+412Js*Nhw|~C|NKYyIQ@ghI{@Hc+wuSXugYP76pcVikN|*`h5-zKLxb^u z2b2l{6bGRIV1M}j;2tm-1dtw{{rZrHD$M^G($N}S! z2Z?aK!1Dk7!RHWwhui=$_=f@>42~UJqS^Z)ja(BB|=SRLi({sY7sAR+tD#AATKgT`y;--Fga9uN%(9yqMT{(bo13(6lp zus|k&@dS?keQ@9k$K?;d1I8HuN0fSavJiPf|K0$3bOGa^2b5;V@`C?7FbR-6U^oc) zJhZt_loRpefN+812U3sVh9L9!ao{j0A3{I)v&_rlFB_B!G0cF#VT<#hm&_h8d|*6g z4)c%~&Sm0&XCQDI0DS@C1R1zUW!UwIhvH}g8ZG*9fd`*}Iw14FQXm>g1x5fbBKY@Y z`$z=?;8YNJ@F21IL410(edw6yVbP!G{}OTdhsR3*h+rUb>;wbh=l@7a=S6@*Ki~L! zh+-SaI6P&}?)rI1eAh55xbzj)zVo8)x(90|$@712H+20yvc%#pVbH7=Q?2 WAoBnez=R<5gM--MG4LS#6Ndmx6F-&! diff --git a/assets/resources/dolphin/L2_Soldering_128x64/frame_10.bm b/assets/resources/dolphin/L2_Soldering_128x64/frame_10.bm deleted file mode 100644 index f808193580805a0d405d7316ec33cc3708428222..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 699 zcmV;s0z~}*0Jj4FfCfGf$p7FUupbCNU>qQN{0AfeDf|P74_<&hL4%kFv=0CeUw}U$ z;QxSl0sBA)zwdqz*j|DT1s}vdgXE6`-|ikC&OYPf7r=cdfcGbeJmc}D`Fbr z;9&=kY#vwm;5@Z@sJ%)9c#r?V6@05Tu}|7a%==MeNb|NeYDLMlT4_&@L(06a;9 zz~}F(?t#L8)58DL5rOJGRqKzH{e7q3%Ty&^-wM;?FZLh`erCEF2UbFva=L z%ja^jU?BkUm^;l1Ao7_K1`-M$0DT=L|C_*Q5#ZTWL+>;PjN#%iDv&(90nrDXg5dz< zKx&Rb7m0j(vVEiiA>e>{AQeso7C$I2k5;d}M~Gx@@CWGU{}OTdhsR3*f?yzOk%4?X z|LF-l=%Athf9KE$3`2Pb2aNz8G;9O~+A($j#2CZj|KG<$r!I}N!4q%{L*W6KoXPqQN{0AfeDf|P74_<&hL4%kFv=0CeUw}U$ z;QxSl0sBA)zwdqz*j|DT1s}vdgXE6`-|ikC&OYPf7r=cdfcGbeJmc}D`*b3CAG{16 zU9fxV^-+412Js*Nhw|~C|NKYyIQ@ghI{@Hc+wuSXugYP76pcVikN|*`h5-zKLxb^u z2b2l{6bGRIV1M}j;2tm-1dtw{{rZrHD$M^G($N}S! z2Z?aK!1Dk7!RHWwhui=$_=f@>42~UJqS^Z)ja(BC0=z$^~(^Zxje?GWy2V?S!{{iC+fFsI0C=hwX|DF(ebOGa^2b5;V@`V08a0!q+U^oc) zJhZt_lq2EifN+812U3sVh9L5ScscMG6c3>v{8{E@@fVHC2{`OPU@*n`&&%g>fkCqr z7!1MQasv6xxKJI%4g)|Bpgcf>Hwg_hp0N=8O#nlR|4uL<^Y8~m9+@8ok^xbG3&_4b z**?%#J`0V=9y|yveo$W?tzUXXIoNb(`TxY66e01_k&15~z(5qE{*aT-ivWm!zwq}E z#5a&|c+dgkMs5rnc?*sM$MFO3|L^0W)4*VM&&SRN4qQN{0AfeDf|P74_<&hL4%kFv=0CeUw}U$ z;QxSl0sBA)zwdqz*j|DT1s}vdgXE6`-|ikC&OYPf7r=cdfcGbeJmc}D`*b3CAG{16 zU9fxV^-+412Js*Nhw|~C|NKYyIQ@ghI{@Hc+wuSXugYP76pcVikN|*`h5-zKLxb^u z2b2l{6bGRIV1M}j;2tm-1dtw{{rZrHD$M^G($N}S! z2Z?aK!1Dk7!RHWwhui=$_=f@>42~UJqS^Z)ja(BB|=SRLi({sY7sAR+tD#AATKgT`y;--Fga9)Ar89yqMT{(bo13(6lp zutFw)@dS?keQ@9k$K?;dSRs(e0z9MB#gK!_7yj@A%b*V&_&lRFKa>~w;f73s;{m`& z!R4jOe4w9?95aLu7&?@H1ThDn!;c+-K>88?#hzwg5qR97QI2GG0}NlB{JvoEgX1qS z8H2p!1@oDBpc%^?27n(xc!36P5*c^BVj=jN0EUZxoM1uc;0}m9@Dzy#k^xbG3&_4b z**?+%K=>5|9y|yveo$W?tzUX3IoNb(`TxY6ej)MFyGH>7k6<7l4?p@sPdY3D5&r+f z>;X~ULBZod2aOs527_avalm-~AbubH{B$~T7~4OeI2b&B5E+Tgpb^BV<}WZnIK%)$ X2?v+}rUW4eq#Pc{0gr(P;G8%BxBEH0 diff --git a/assets/resources/dolphin/L2_Soldering_128x64/frame_4.bm b/assets/resources/dolphin/L2_Soldering_128x64/frame_4.bm deleted file mode 100644 index a961f4c0a17681a1c74e2d84e79a148aad724c00..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 693 zcmV;m0!sY>0I>r9fCfGf$p7FUupbCNU>qQN{0AfeDf|P74_<&hL4%kFv=0CeUw}U$ z;QxSl0sBA)zwdqz*j|DT1s}vdgXE6`-|ikC&OYPf7r=cdfcGbeJmc}D`*b3CAG{16 zU9fxV^-+412Js*Nhw|~C|NKYyIQ@ghI{@Hc+wuSXugYP76pcVikN|*`h5-zKLxb^u z2b2l{6bGRIV1M}j;2tm-1dtw{{rZrHD$M^G($N}S! z2Z?aK!1Dk7!RHWwhui=$_=f@>42~UJqS2Y|N1+?Jbr-k z{z(Djpa0MQ+B-vhh2sFQJIl}f2Z%I4L-)VHpdSnfJZ8Rq_&sOy$OPhq4;)rve?I(h z1?4ZkSRoTYc!EcNKDclPWAcmtED%U!0UlA|K!eUF{P2Uzpbs4QJfk*0lqd1yf=q$q z0l-JW<)zAep&t)CBZLnaKY}0lA&5Mno(_Bl1q0|u{}y?fd`07Of=)XS7z{CfbMpDz zpipeUJZ28_kQdHn!hr4|a2f!80pbK1xJYT3_yzz!6F?B+uhWbOJp2LC2c|{=q<~al z0`f19c2BewkAeeo2af^^ACwozt5@EU4t5HgXpn2anqQN{0AfeDf|P74_<&hL4%kFv=0CeUw}U$ z;QxSl0sBA)zwdqz*j|DT1s}vdgXE6`-|ikC&OYPf7r=cdfcGbeJmc}D`*b3CAG{16 zU9fxV^-+412Js*Nhw|~C|NKYyIQ@ghI{@Hc+wuSXugYP76pcVikN|*`h5-zKLxb^u z2b2nd6bGRIV1M}j;2tm=1dtw{{rZrHP-5s;UI*I%px2mpVO z`b791w0H%49$)BuqF)3Dk01ZfhloW;KoQhW0pv^`2S0sRfWR<-@$>+M9*gn^j}!j` z^iSsi@oz8YPvr-l0s{YU&0HQZ77*Z5=pSegf*%P7uaEFQA1C}uIC(%k@BjTB;2u9f zc}FCG@z4L~|Lq;2zCiM@JIl}f2Z%I4L-(JE#{q!{jMvY<2dsWPAQ})naaoD{`|-dR zlsy+^C*#Ke;RD7Fr60izLFe$}z+g~5gn#j8nU};~Hz*WinE`;q7w11Om^@(k zzvVEiif$)HNAQeso7C$I2k5;d}6CK_F{T%<|PCpR%>0l8I1PxL!FNdG~At#*{ z0SNzp=gBM7f{zT>g@%TVyCo+IX5~G;> e`61x(1q>t}U;+*dAP6AxgM--MG4LS#6Ndn7H99^3 diff --git a/assets/resources/dolphin/L2_Soldering_128x64/frame_6.bm b/assets/resources/dolphin/L2_Soldering_128x64/frame_6.bm deleted file mode 100644 index 2f030833a27a95c21247e158074d5fe721ef4e51..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 712 zcmV;(0yq5u0K@|SfCfGf$p7FUupbCNU>qQN{0AfeDf|P74_<&hL4%kFv=0CeUw}U$ z;QxSl0sBA)zwdqz*j|DT1s}vdgXE6`-|ikC&OYPf50nsIf%KjO&KOTXdB@{R^9P(y zBlm%W%eD_Vz&y2jsJ%)9c#r?V=Qt0&<3IoSgUaAPdB^M?G1vzJ{@;)P?S4}afTU^& zRDc8oq$T0FLxb^u2b2oo6bGRIV1M}j;2tm;gpeMd{rZrHD$M^G($RH4mfILfu?gy9u?hhygLINLf0LS7S2zWe@LZBde$RZGk5IpJl586z} zqb~=pzgPqi0RJKMiGV$5@Cx`mztH(af(Q>DKmVT(5Q>n1Bj^X2FnApO^0En z0)um)Fc@O|=jHRcc%V4oc+4H=ATONA1UYCN2Y??yc!36P4;dajHZ>6Y%?$%2zg!?y zAbEHLq7OO+;sMBj)gS`$FOPOlv=a(^e2@yK0t+9M7ssnt-oQQqk-NYjqo4dq$KoFy zECLaLfvQFY@bmwqB=e%cAfNC2`T+rmZy@0DpaaH@0D-}w(S8I1A^3s#fA{gw>B*yP u_C)3Y@%TVyCo+IXBk&Bzhsh5Ih$vwo^8gTVVE{n~lpG$(aS@5s$%6o#P)TY4 diff --git a/assets/resources/dolphin/L2_Soldering_128x64/frame_7.bm b/assets/resources/dolphin/L2_Soldering_128x64/frame_7.bm deleted file mode 100644 index 4519819ea5c7e9d0127fbaf538f5aa276c881d4c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 732 zcmV<20weta0N4WmfCfGV7(hN_|A2W$0rLUygZ2T!2b6#WKLN=A3V#6N1J|GrP+;Z( z<=H`&OaarjzAtI!uJEq|Mv%+LINLf0LS7S z2zWe@LZBde$RZGk5IpJl586!O83}kjb^5>{fCu>xq)Y+pM}Sws<^G4tCGbFa@&Ekz zc!X4h0Ubo}9z?<5bNAJCP~idN=m7{l7vv8fC;kWMpUwf|-e1h0$`3jO1^(ZfxIADi zA;725KF}WoJ`xXKAK-pIPxzE@@_>2Y|N1+?Jbsz*c*i7w@z4L~|Lq;2zFzQn*d68P z{sY7sAR+tB;p&mVgT`y;--FgK9}o=)9yqMT{(bo13(6Nius|k&@dS?keQ@9t$K?aR zgT@&EN0fSavJiPe|K0$3bOGa^2b5;V@`C?7FbR-6U^oc)JhZt_loRpefN+812U3sV zh9L5RPB_pQ6c3>v{8{E@@fVHC1qSCpU@*n`&&%g>@j!9F@t8Z#KwmkK2y)Ok4*))Z z@d6Co9x^<5Y-%C*ni>X4ez-uYK=SYhL>_bt!~>B5sz3$gUmonAXeJc+`5+Ze1QtIi zFOOEQy?}fJBX@v5M?d(JkHkJYSOg;h15}I);phKIN#{j?K|kO5^a2AA-a*0RKnIN) z0Rw|WqWlO1L-7Of|L^0W)00No?1{_)Pl0{-8R|LuNL3;?8R z0#twm1f(zsWC9!?i}*aCPzazs2m=HE$M*p7fWRbx^!M-7gg+br*l7Utm+#?!{1`lF z0P(~J*d9N>oPIzL9DqDah3*HJ|LzYsgakg|0guEu5b${*g+M^{kVGL6AbHd9AGDdm zG7|85>-B&^01xsXNSFiGj{vWO%l!|OOW=U<G{Jc)zA=kKcOp~3^l z&;k&8FUTG|Py7$jKb!-_yuX=0lpb^l3;n+}aCpF2LxE4AeV{%Fd?X&eKfwHapYbT+ zMw89FhaaKmVWqw04I13&sIpcbA{|4-ja8hwp!ZDn|kj8Lyvy4_W;1 zU^F0j;e*`fHloP?vfWV-A2>;^GGcSm|Zcs_b zVgmt&FV22nJCq6ym3igEt8cGaefnh<)aOA;n*=5Gs&7 zyaCY%rbYqCfYl%Z@-L5ePqc22f&=7$RX7k>{Gh%)TE6s%cX$KzbN`7rC`03=BNQD{ zFeNDeq$KmAz#<>-{Q3a_h;Ja^@t_07jNBMBaxcINkKza6|KG<$r+~oipNO2m9zO^S n#O6>4=lTJd`1v8=@dXSd9$*3v3?K*~@`HofE-~;R{1b-&_Jm6b diff --git a/assets/resources/dolphin/L2_Soldering_128x64/frame_9.bm b/assets/resources/dolphin/L2_Soldering_128x64/frame_9.bm deleted file mode 100644 index 1339c607e63f0d52de7d04b4076e94a820e1a774..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 698 zcmV;r0!94+0JZ}EfCfGf$p7FUupbCNU>qQN{0AfeDf|P74_<&hL4%kFv=0CeUw}U$ z;QxSl0sBA)zwdqz*j|DT1s}vdgXE6`-|ikC&OYPf7r=cdfcGbeJmc}D`Fbr z;9&=kY#vwm;5@Z@sJ%)9c#r?V6@05Tu}|7a%==MeNb|NeYDLMlT4_&@L(06a;9 zz~}F(?t#L8)58D(8>|NlpL2anUflp_fAGynPj`$uSRm%Pj-aR-l|_zw_hfQRq*hrm7pVdzbK`|x`8 z*MguBc;d4Y`S;_1FDzXBgdp++j{bdcG>ik~2Tl#*1dllM@bWl001;PbV2bX|4AoGx1ARLGd zQHUb(FOPOlw17lB5Dz2*slbBA0l5{1PxL!FNdG~ zAt#*_6hH6$`T+rmZy@0DpaaH@fPlfE5q1E?7{lTJ-^W9zE{(In6L1Ve;Q^SO$^jgQ gz%w2nAP`~zp;M3n1A@UIc|pPKml*gEehI^X01SRc?f?J) diff --git a/assets/resources/dolphin/L2_Soldering_128x64/meta.txt b/assets/resources/dolphin/L2_Soldering_128x64/meta.txt deleted file mode 100644 index b705bf623..000000000 --- a/assets/resources/dolphin/L2_Soldering_128x64/meta.txt +++ /dev/null @@ -1,23 +0,0 @@ -Filetype: Flipper Animation -Version: 1 - -Width: 128 -Height: 64 -Passive frames: 9 -Active frames: 5 -Frames order: 0 1 2 3 4 5 6 7 8 9 10 9 10 9 -Active cycles: 1 -Frame rate: 2 -Duration: 3600 -Active cooldown: 7 - -Bubble slots: 1 - -Slot: 0 -X: 71 -Y: 28 -Text: I am busy rn -AlignH: Left -AlignV: Center -StartFrame: 10 -EndFrame: 13 diff --git a/assets/resources/dolphin/L3_Furippa3_128x64/frame_0.bm b/assets/resources/dolphin/L3_Furippa3_128x64/frame_0.bm deleted file mode 100644 index 07a63d6424edfe232cc55faf2756cca9485384c3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 398 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 K0l^!}zz-0n&QtRM diff --git a/assets/resources/dolphin/L3_Furippa3_128x64/frame_12.bm b/assets/resources/dolphin/L3_Furippa3_128x64/frame_12.bm deleted file mode 100644 index 392905a559e718552e9db176de5b7cc96aee167a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 541 zcmV+&0^2HpaDrD0~`qk(=2CEZ z0f)!)C5Sy^VDfDd6n=P6c!CNF4@>zJeV5sIJm5g`(0}*gLcxQ{tS}II8iWXe@eB|E zXaM&oh#-OHDi9%W;4^FixNpGtzyG;6_}9XI3LFReE(ABI%&-txX8^(=_6Q6zF#`F| z%pwXHSUm6OQH)?em-v`H|E9r!(1@c42fM^ULUj;%Vgc))|K1Fy>Kb?a9=!ZL{EAT6 z1w0;~ugGj@5C#axa1?NQe1YPw7#HjRdiZJo@l^SIcmMtl`T!3dm;X)wiUZI8-@skq zAn}sVy5IRje7^$Wz<=Pt@uKiS==&f4;{o%GgT|Nt_kZdD@qLIqwsLTI)$jj*{(v4o zPrv8!!7vCsb@%_@{(}R60SDkA5P1JeqyPQ^#q2GC2cG?kAOw2~a6#jN_us+G|A+7a f<$u2eTmSC}k$89X5PN_N?%5stv_aF0B#1shQS}MQ diff --git a/assets/resources/dolphin/L3_Furippa3_128x64/frame_13.bm b/assets/resources/dolphin/L3_Furippa3_128x64/frame_13.bm deleted file mode 100644 index aa5353e988d02785f45759a024931abe7d580086..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 584 zcmV-O0=NAE07L@+fII=<4*+-rz#ahb2Y@^IANT|F;Rk?x!S}#`z&(Kbz(C*~0DZs# zKZbz<@kP z-_ZGk-W5OxmVe$YUN}5vRseX=FaHn!_#S((c=Kc+^8fh5iJ!HD#(O>ojswBL|KI=9 zyXAl$Ey0L`%fG>Z-}v0Uoqw17zfWUvCftXMrUwwc* zFdOIv=nzr}54)K^iv!95wSa!G^7S~tJCHd1P#$^^c)jswmy;2(?+ml*DY%6Tpaje?1R4~z?+C+R@JB71OJ%^j$i~g#Q}fdP!0e4@$U+bI{^s# z3(Nlv;0TA07lYt1>XG_?f57p%{4ekc#4b7D#lRpxNFUrDAR+mp#tHC`{fFbU28aM| z{|{g!U}Yi1z%rjedF|ox@OZ&E5DPI#e_na`JbB{LzW^w(J#=94pcIrmEe`+y diff --git a/assets/resources/dolphin/L3_Furippa3_128x64/frame_14.bm b/assets/resources/dolphin/L3_Furippa3_128x64/frame_14.bm deleted file mode 100644 index 837c6c71defd8be280776e7881817fc68276b5d0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 610 zcmV-o0-gN<0A2$B|KVUk{Q*P)Fi?2?0rvt2kDwoLFnIg{_W=j^KOjHgdBMT)0R98a zDj@jq1I#KQ_}~M|;Q;Id;{Xp%fIlVx`hWxc9=`B>kL(_GhKz#+o><3>gVG5B{Tt>8 z4;U5;!}CwXJVamY_&z)lfe}FLAMe0Dt_H!Nasoqr`-9r~&_@VJU>l>0Krj6W95O)w zgJlpe;FN#haR>y*&>wsRUrhppDF-nZ{sYsG|A@(;VDX@2Js+@oPGA%6%nc+tE0kSi zuxJH>Lj(tm3qcDYD|B!X2tYh=AoBP#)&UWmL9h@2c-lSS_KA8RbO;895vb?`+9^1J zKqL|g{UbmRX+$ywAO;Ttt3U_0LJkiYg@OTE2b}yKQ3wz`aF!;BgU_B1DFg(5A@ZVV zdEoJzgVn$w5#o2S;PHin%RrqGp+G;L5P3mC<-mYJ58@9VCwyLBV0pmdiSdjD2SN@Q z8NBEa$N2-wgZ=&g0qGDH4O%!v8W;Zm|NDc@SRg@@1ImB)|6{@DKq!m?&N3Yo|NZ%| zgUlq?4N0o0xY&yEj|qzoQt^FED>06iEa;GRJCuV4t{^!eXG#1C2(0)+vGdoW=5 z-AFJnC>}#T4`yhz^{KA$Q(_^4>({vevs<0996^* wNHeH?5Gg=DU|{j6KxP1O`UB1b0N_Ay5Dfv)f$A(Dg8+f^cz{o!766F@+?Nvri2wiq diff --git a/assets/resources/dolphin/L3_Furippa3_128x64/frame_15.bm b/assets/resources/dolphin/L3_Furippa3_128x64/frame_15.bm deleted file mode 100644 index 9ff56a5b6bd83d39c6585dafb084113387bf309d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 741 zcmV2edpMFaQvP#()oKH^LrM60rl^I503^{{iLsHy|)^ z0P@TO$c~T*6avV`F91BQ2NQrnAQ7N>U;*j-2b3ZK1#_7gbPDB+U69Vy z2d})|M3D~)2*KxjACw9NtPlv3%2fI<=fqYpC`JMNAOZbLLFM@O69xpR5TL;;kZ&wM zv48$|iGu=y7zjQv7}Uef!T-;u1c9OhgeEXZ92#R14<0x?VUR4PTpn0R2t2>t^7yL9kti^WY(e8% z2af;$0dNeKDo`E-93GeRaKs{72$D!_6M%T`oC3cYgo1+sguF|DVj0x`W)B_(0t2Li zLmEV1AFl)B{S1u`0c1CcR7V2<{{Q|V@qlR{6sUUupb^dk^nd z`{45EK&FvLi4;m6ARtKSAiN120pwi|{4+ccIs`5tFCG9~z98}UgU&C2CJn$IUIF-r z&jD@$i~=whF90}!d04o9jcmu#50PqKZJOMC(e@*~H;Gcu)Kp-FI{sIrE!~!3HfrIK{fDVi> zA2bOE00<&6fJcGxAQBVFfe%V z2?Po(1P}^v0C>zG2xA9~2ufh`6Nn_h0*o^N!C(S$6qr3?0P+rtY{w!sFnNW<3O&Gk zC9wT8f5ItaOfd_~*OhgYk2s}k& z0iYO!9@H2I{!R!IaNrw+4_FxNU{(lyfXFOW;C>`v@t9B|0P2CSOq13hXmkeVx$Jlghm1hk|1&b^56rE9-u*b zk`z`43?iih97Y&eJm674dC)Kbk@t^KHL{N x0tncHk_Vv>zzCfd@UEO1VEw>AQ=&RRdj|v`*MLBS&H_(>K!T0}e_#N^0q7Yl$LIh6 diff --git a/assets/resources/dolphin/L3_Furippa3_128x64/frame_17.bm b/assets/resources/dolphin/L3_Furippa3_128x64/frame_17.bm deleted file mode 100644 index 80cb06fd3cbd753e5bf6f00f85a67bee5a0590ad..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 492 zcmV)+z)6lKcGPUz(7I#PwD_arU3pz{4gKTMmK;S{sc4Q z5e5Umrk^9=vnU*SF-SfRq7N02#{ta&$w0Dd;runk9{8*#h#oL? zpn`|t4+otp@uYxyFYqOpfCv0UAb8RN;~c<20p|dpgVzBAfI@5F^SA(p1TP>la);yq zKzQLakl--Rh3F4FUyEM>0}MDG_)L^!Tu=xYJ>Zd5NxcMq_(?FdlcdpjPc#Z}#e8CO zMO1)BJ^*vTP$E}QF^t4QAoY-1fW%BFFu7zj3ZaNaf^Z;vQUGEN5a5hRZjc-Rw7dYp z@GDp(AySW^3;=~uQ?zglu?Pg@5T)Z>8J(~N;}N1AS>nV%!5)YJPa`p_olv4@E&zNWkwRctC_R7$4g-w>^KMRlm@CSfA05|Yd-vDL6 zrh}m*mk*cyGWGx=$00Bbd_3^slEc9uABn*Va1<7}%{%~tpfJ~gKoN{-w5JrATcm{vup{D1Q;0w-&W_W-d nPOl>XE1giJZ<8y6l#+*4fXD!a1qaL2M1)X4c|-t0*bo4Ks86b< diff --git a/assets/resources/dolphin/L3_Furippa3_128x64/frame_2.bm b/assets/resources/dolphin/L3_Furippa3_128x64/frame_2.bm deleted file mode 100644 index c014858502e8d153d61a39604e4128198513b5df..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 463 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 diff --git a/assets/resources/dolphin/L3_Furippa3_128x64/frame_6.bm b/assets/resources/dolphin/L3_Furippa3_128x64/frame_6.bm deleted file mode 100644 index f46aabadf6b1ae21e3d61fe50b3691c7d6c063cd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 521 zcmV+k0`~m@00ja7cmu#50PqKZJOSVj0C)qy9suwMfCl~!-UT!tw-1;80uQ+4CIOF! zfyz7&Y&;SH_?!XCzz$pm1_c0`cmW1AUIhRSF`!xn!~k{xc|%|jFhIeE;s7}SJa7s*c2Y|^a2<$=eApaMJ1^j@| z!R>*_Kw@C=fpCHE0HnYqkPIjP=^qEYn)yh8Q6L}Kcrhml0>RW^6G#X858%Jqpc#ot zXmU_2n)tuaFnUfwJ=1rhkb4GHFf2>f6M#O8p7;QxRG9?>hP z5UwAO{(K(N3s4ZJgaOBc+JRIt3;{Yn3>ZDB03nQU6Qg)(hyg(5U{;R;55Nb=@PF;$ zpby**&oKZjYfqw_UW%0@E diff --git a/assets/resources/dolphin/L3_Furippa3_128x64/frame_7.bm b/assets/resources/dolphin/L3_Furippa3_128x64/frame_7.bm deleted file mode 100644 index 07a63d6424edfe232cc55faf2756cca9485384c3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 398 zcmV;90df8T0Ez(scmu#50PqKZJOSVj0C)qy9suwMfCl~!-UT!tw-1;80uQ+4CIOF! zgX<3jfPNA+B6Pzk4iom2tn#;^n#umj2)0D*(zKo1-Q8UO(h1Iz*AKn{saKo1xP zj)D&Wh99IJJP5=B5D$o;JZusHD1+iHh=9~65D0)iBuBB7U;*_bz<=hjC=9?K6dnWu zJph1^L?03l@i1UO`M~5LF))3=DKH5lctQ0hzET0@{85TQ@O2P)tcExaXbwsRlUEPn zt|0ftVKhMTgRKM}^sB~_0qDQLmSPWjKs;lZ2p~T25I6)Tz7J>+ysiLY@6g zO6mkG0L(-p09gg72t>kz1KNR9F$l2MLI7Go2tlG85s3g704*;7Fnj>!2c7~xfHC`l smN*Z2$Qet{2y6j)(Cp6;1F6;IUcmu#50PqKZJOSVj0C)qy9suwMfCmBpkAw5U0GI>vco7%~KIHHT z1UwyF2?#uA0Q$H{0pdas1jYg55Woy789)b&0yYnrFg&mmpnSl=;|QQxQ?mgFj3+_D zH%LT7<$*}RbV22y8i+mb03m?St`HayJ>`HQ!0=-bC@?+bz#*YeA@E2bJ>)?_LF)kn zj#5D@pd-aX!1M3}$K?YDxMXsX30DFi3Q$SHKg+Wz(67Z8-P9y0uM6tm<%3p;CwCweq`t<5GWL?94Aeas{f2wnmn4S)O| z0YKn1MNcxw0z(|^5sxL?E_DWKtbc>!Jr~=4-}B)a6tGph%)1~B0Iz@!4=gh~K|3LmQUL&XqM4gg-Ku?-MBWDtu45FU!Lnhgj>GYI4e z{Gs!Z0Y`xY2dQ*Ny* dI7Rs27DoVFD1bfW41$AzguwvwjseIfKm^${pj`j} diff --git a/assets/resources/dolphin/L3_Furippa3_128x64/meta.txt b/assets/resources/dolphin/L3_Furippa3_128x64/meta.txt deleted file mode 100644 index c21027e49..000000000 --- a/assets/resources/dolphin/L3_Furippa3_128x64/meta.txt +++ /dev/null @@ -1,14 +0,0 @@ -Filetype: Flipper Animation -Version: 1 - -Width: 128 -Height: 64 -Passive frames: 8 -Active frames: 11 -Frames order: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 -Active cycles: 1 -Frame rate: 2 -Duration: 3600 -Active cooldown: 7 - -Bubble slots: 0 diff --git a/assets/resources/dolphin/L3_Hijack_radio_128x64/frame_0.bm b/assets/resources/dolphin/L3_Hijack_radio_128x64/frame_0.bm deleted file mode 100644 index cf2120ff470ed140a0d47f8adcfedd64a8d20cd8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 524 zcmV+n0`vU=00;vAfII=<4*+-rz#ahb2Y@^Q;12+J1Hc^RUH~ZK7yu!Ef~mg&l>gs< z0+a|ongB_2=NJzFKIZ&r9DHANybK@1{0G+80S$qCz@u&L?SMnp2bF384}p6D^MCHa z;m|ICK6moyY2a$|_{Be1B z_1@fzk3(N>7S?zlxT`9#A0ogVTq9g@ew41d(XP=VSg~;Hj|y3A{t&2LqOX zBrqU(%pwvm*nHFC4;V-A;4y&t56?soFT@N!Fvf^Kd8K*sdIr~590(U zs62`y4-ou9;1~gjhQ)p~0s7tW2a<#X=NDfII=<4*+-rz#ahb2Y@^Q;12+J1Hc^RUH~ZK7yu!Ef~mg&l>gs< z0+a|ongB_2=NJzFKIZ&r9DHANybK@1{0G+80S$qCz@u&L?SMnp2bF384}p6D^MCHa z;m|ICK6moydAvUe z4CbAH(1Y5=_NBlv@yzD~LFxymuRHCPz&0?<#{z&4SzY_JCBSe2<6sB2t&TES06g#j z_w~xK7yu)fFdrfL=z-<Ggk>LU;sg2?ePEr diff --git a/assets/resources/dolphin/L3_Hijack_radio_128x64/frame_10.bm b/assets/resources/dolphin/L3_Hijack_radio_128x64/frame_10.bm deleted file mode 100644 index 1354c78f2ce9d20f8e2b08bad75be024c8b4018e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 550 zcmV+>0@?im03rhafIt8H0hjbC;12+J1Hc{t@CSfA0pJe+cmu!?uZ39z`kHnw)XbGA?pLmwE%~} zy@2_@_h9ho7eF67`E+st0xO6;`d`8WiKJjOAoj6;sc;N`6C=RTdV%Td&i`d_4UCf|z@P)xSAP9Ta2yHddcY5D zU;pbRfN%rjV88?4*DAnZ2jegY$6OvT0{Fay9`gBW6i?!y5PR3_um}%|P7f3M=-eY# z4~*V0FA#gz^x&a{#3T>}`sy9SPT;0k$7k=NpgZVC3@91cn6=9t;9u5Rrbv=ARIF!Y9FT7>Rtoa!<%S{|EAhPy^%- zU-Ud~P-+4FLFENYoC^Sd7$H4DLu*4rY^Z^6M{0Si0fBpab3Vf�Q4{f5&!@I diff --git a/assets/resources/dolphin/L3_Hijack_radio_128x64/frame_11.bm b/assets/resources/dolphin/L3_Hijack_radio_128x64/frame_11.bm deleted file mode 100644 index c15289b5ec4e1bf2622a9d4e63dabf35ff48e539..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 572 zcmV-C0>k|Q05}5wfII=<4*+-rz#ahb2Y@^Q;12+J1Hc^RUH~ZK7yu!Ef~mg&l>gs< z0+a|ongB_2=NJzFKIZ&r9DHANybK@1{0G+80S$qCz@u&L?SMnp2bF384}p6D^MCHa z;m|ICK6moyM;G#;+THpei>dh@^8Tmxey$#5tZuD#Z~-@3~0->EJGfjrMx z0ps8L-+TXkU;pbRfN%rjV89E+9y5$^JbiMk1^|9D0C@Gmn5fFIDpmcnf zp7Y|Lf8wRFCXWIH9}s$Q|M0MQ#tGvDk!Z!|WBz~Osj-6yNIo!fd1wMd0*DU=0WgS2 zzhU!Fh&*8v;JA!LzF#>fgs< z0+a|ongB_2=NJzFKIZ&r9DHANybK@1{0G+80S$qCz@u&L?SMnp2bF384}p6D^MCHa z;m|ICK6moyM;G#;ROdh@^8Tmxey$#5tD^_AbhQd|cDd7iKX+gJbk$zU7+ z_}DN2_w~xK7yDIo{EcjsC^iG+9t zG5_(!<>TT1B|@gs< z0+a|ongB_2=NJzFKIZ&r9DHANybK@1{0G+80S$qCz@u&L?SMnp2bF384}p6D^MCHa z;m|ICK6moyM;G#;+k_jh%>`t!fpTmxey$#5tZuYH%D-P_9V->EJGfjrMx z0poe!-}is*U;pbRfN%rjV8D8}`g!-iRu3Oss{w!?jKCf~ukD||x(AP39xwv#QwTB2-kz-2Z#&A9`*hBC}S|m1Oa}! zhj8DxG5-V(!2oZpv;9-P&)-ErfIrv4{kCslF9AsiJ>$PR(fmQ=V;}zDAj zh=ayO`{U>Qm!A~-{}nBWM0gM&`2){~|Am9Z`ZNe2e1+v>{(s=9v4%hm;vX0}9JB!; zfkZG6F$lyYP(C7k0pke21;#KRDn5EoNznt#@P8;}0!8BoxB4D8C^Z27qVj^J&IN!U zi9LbjVGwwS;uf9}h`|@-K*>)zAo9E54gs< z0+a|ongB_2=NJzFKIZ&r9DHANybK@1{0G+80S$qCz@u&L?SMnp2bF384}p6D^MCHa z;m|ICK6moydAvUe z3?`9)(1Y5={-wY%@JxpTLFxymuRHCPz&0TApaa%de*H;s8~}LO0qv_}jFtcoJODkj z{<&5I01p{}J@5Tsc)$zd@)3K><)~3Vihx1yTdu$$De(u1{d8^-uLs5t5EqC&>HDxy z#$l2O0{wLk;lFTW{s`lo%LzKVeWf3Ji4Y~H|L0+JAW$9{FA_=CvCKmItp zynH?6s8%5JkwEDADJ3VxKL5o_Vh<<~d_n2M|H8rNK!QlLV)L>8Kk!u8fCSzl@q>ZO zKoS@bJmwJz7wkT1@du0}_;47&e23?v2bbao9~fl-J|Oo0L&fC=pdZv;P*l0Vum|yi z6Vx6>5eJBVA#e-;#6x1g8UX!n_yfs80rQUm34lT8z!$CjJ|q-(p*+9)`S=`IVh^MN Q1IPRcAXxy-TqLo801PJV%>V!Z diff --git a/assets/resources/dolphin/L3_Hijack_radio_128x64/frame_3.bm b/assets/resources/dolphin/L3_Hijack_radio_128x64/frame_3.bm deleted file mode 100644 index dc0fb9b79cc78fac3cda74455bf92526b8504378..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 529 zcmV+s0`C0*01X2FfII=<4*+-rz#ahb2Y@^Q;12+J1Hc^RUH~ZK7yu!Ef~mg&l>gs< z0+a|ongB_2=NJzFKIZ&r9DHANybK@1{0G+80S$qCz@u&L?SMnp2bF384}p6D^MCHa z;m|ICK6moyx?2gVN&7l=LU z`|wc4VUh>}{dErEzi?yz2pxg|-&tq+r+uHkih%%suY>z+-oRc0k`Q~xes!bxgUH4| z{y4n6d_Ux zgMrIH5*QFX<`D@O>^^Dn2aF^5a2UXRhv%XPm*NH=7-axHAol-5#pMQ|AJkq@RJp*g z2l0Xv)E-3<2Z(+la0~#%Lt?)g0R3+G1Ia=G^N#@ufI;WL7p?q0Boud{Jiq(-_#9Yb T52OJD$NUK(SpdykB(Z=1M>Oxj diff --git a/assets/resources/dolphin/L3_Hijack_radio_128x64/frame_4.bm b/assets/resources/dolphin/L3_Hijack_radio_128x64/frame_4.bm deleted file mode 100644 index 025477a7a943d7eac253ddddafe16f07fea0a0a4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 571 zcmV-B0>u3R05<~vfII=<4*+-rz#ahb2Y@^Q;12+J1Hc^RUH~ZK7yu!Ef~mg&l>gs< z0+a|ongB_2=NJzFKIZ&r9DHANybK@1{0G+80S$qCz@u&L?SMnp2bF384}p6D^MCHa z;m|ICK6moy#QwTB2-kz-2Z#&A9`*hBC}S|m1Oa}!hj8Dx zG5-V(!2oZpv;9-P&)-ErfIrv4{kCslF9AsiJ>$PR(fmQ=V;}zDAjdC0$f zeE*X3;-7!wrLhN;2tGjb;s4=a^PoWk9JtssDFT@N!Fv JGgk>LU;r>`30MFC diff --git a/assets/resources/dolphin/L3_Hijack_radio_128x64/frame_5.bm b/assets/resources/dolphin/L3_Hijack_radio_128x64/frame_5.bm deleted file mode 100644 index 89a4cd6acf85afc12c608940bc379d5b1464e61c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 574 zcmV-E0>S+O06GHyfII=<4*+-rz#ahb2Y@^Q;12+J1Hc^RUH~ZK7yu!Ef~mg&l>gs< z0+a|ongB_2=NJzFKIZ&r9DHANybK@1{0G+80S$qCz@u&L?SMnp2bF384}p6D^MCHa z;m|ICK6moyEJGfDao0 zJbV7%r~kLB|NUgJ0D0goL&5v*|KRcU%CHy#_{;(0wcGc8b?|ug!Q%igi^zRn|F`}> z_Xmu=T7?t%s0Y>D?$P#p(0JGDum}o#LE?X18-#1Y@q@$#;tzWM{1h>mWP$*{T|>BU z+!+6Y2Vj6V)>;0k-)HZlpg@lx1>$^;)8I6U}&_*gvX5J?w|UUom{{tBBA0Gq@?gHR9Z4=5^J;8+P4?4H2#D2O~m z@e6=p1|tMtl>;T`r09X;cfcM>5D%Pq2uuPGJ^;OM;qf4&y$R+2-_O9}!w`KS2p&J+ MNdm|QYT+e}07|gs< z0+a|ongB_2=NJzFKIZ&r9DHANybK@1{0G+80S$qCz@u&L?SMnp2bF384}p6D^MCHa z;m|ICK6moygT??~7m$PAUoAq3{8R!Ddi{0*0Z)iLPwS&_jd(sVc!0b??_b}7 zhBFM1Ko{$%cMbc4AMilz5C;0oKh-TT1B|@ZO zKoS@bJmwJz6c31>KzPDGhXITS$bNbtd43>a@rF<&UNC!qq2ls`P!H-aC@NgwSONHx z*d9d@2Z(+la0~#%V2kpg57zI1Jd_|GIPegd1Ri_=df&t1K}UKM%m2ThfyIU(`alpo Of54IjkPOwrOBeu5#pKBV diff --git a/assets/resources/dolphin/L3_Hijack_radio_128x64/frame_7.bm b/assets/resources/dolphin/L3_Hijack_radio_128x64/frame_7.bm deleted file mode 100644 index fb6d9bd292c739951e00a03612b86833b5222191..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 655 zcmV;A0&x8S0E+_ufIt852lspgKhS;iz(e>4-y8%#zgs<0+a|ong+SfFdhJW`Tz5N zG!8y5l&I41Fni(o&u{_Gwd0_X$Y z;r`#tqmT#@TtV!i9sF8g4;lbH%EMoU*a72!{{z^>Ao1V>%lHpousn6}Tm%I;Keha8 z!Gqy}z&`8v2b5|V0QG~`KqeZz1_1_$1IIKkPhxmM<>HaRIxwL6N<3fuygmU@qTo6p zfdCwjJl-FK2XjuqXhH2_|5D%>|E6Dop!IgYySuI5*PZ^#;2RlcYk@$ud+faK?%r2^ z{Yh{f38q@W4;#+@|GWQc|NmJm1AreJ1_RZ<)6c#7xnKX+D!^a|<1h!0tNUl~?xlau zxIAD5@p%ucy{-Q-23Po(%TS_!6#)9KcYfWnpR4>=>#zt4d_m%WT^w(JUJr~OATJPm zcdN6rHKZgN~2g{H1l72zw_<@JU89)z^J%7;gc|oWL^#_y{ zE^sUX{9uIj2a!ZU;vZQboc=Hj0K`LLzZwR5d3lj=zxR9rof1`a$*CIJVZ0A6_y p{rn^(6nCV8VdMYr=iqV&z2mSC6afRr{4F2=@9*sa->Za{FaVQOM?U}n diff --git a/assets/resources/dolphin/L3_Hijack_radio_128x64/frame_8.bm b/assets/resources/dolphin/L3_Hijack_radio_128x64/frame_8.bm deleted file mode 100644 index a0377f635fe25d6ae38494e2e28ec168af6f1f3d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 645 zcmV;00($)c0D%Jk|NZ~_|M&_Z3G;);_z%CBJbnQF`UCIo0uXceiGk!D-3L^J| z&NuYD3?IUn5PMnjAN{TZ8v=R)3!o2ykjtZx z2oan?^A?x`#()o+{4T%`90&LxTmX500q^tI!Eg{1;QrU~uLci>2LSu4*dQSCjYA+F zuzJV@!&iX7AkhGL=7s6({)5lOBY<>aLG+Y(zxjB40;5O3bU^|DIUaleOZY%_G`t3c z9@a1QE&-43Wq2A7R`StyE@tgPm*Gn$0|I`PJ1Jg=*53Be4-PiB> zxcs78g&U0(KsK)bH`m+QhsJ?LfIw5?4-@+6|M(h^d|>ebc!S=rxCIPm86bc!*HG>o z_Xa=Uf!H7o^_G9CciH>ss1OJG_&>JI>;>Q{AqTv7=UP9AJd9)i1Ro%I@c;0zc%Mdr1P_qBtbfn^6*e%)0lY)w2P2k)zyEwE%~}y@2@7+&i#%bPJ#liT>hU9DqQI;tz5V#iju9pabT=3$O#n z0saTq03Kh!d~4vi2nuk2XZY6x2g3t^ebw==1dM7K0QG~`KqeZz1_1_$1IIKkPiOQV zekmLSqY4kCqs9Nr!@+|^z;rScHu4_9LwW0+&T zdEe}=0kM{5xD*RlUh7@&-DP+0)RzIko@cB8@$da_z5l+i|Milhm&hJ9dh7xMpAdMT*GK=s z(}Uv&hzrCX^l|X`zcBd(0e-rNaNIaC_yj`10B@|b{Zqcr-$g)xKi9$iwr^lB0Z9lw zuABcR@;tv=__%1Pk`El}cen>q32l9qc zBw8?gf1%@YgHR9Z4=5^J;8+P4?4G{xc^E_jf@01)tm2VkEkh=8RH9=ZSk diff --git a/assets/resources/dolphin/L3_Hijack_radio_128x64/meta.txt b/assets/resources/dolphin/L3_Hijack_radio_128x64/meta.txt deleted file mode 100644 index 1d415b4b8..000000000 --- a/assets/resources/dolphin/L3_Hijack_radio_128x64/meta.txt +++ /dev/null @@ -1,14 +0,0 @@ -Filetype: Flipper Animation -Version: 1 - -Width: 128 -Height: 64 -Passive frames: 8 -Active frames: 8 -Frames order: 0 1 2 3 4 5 4 6 7 8 9 10 11 12 11 13 -Active cycles: 1 -Frame rate: 2 -Duration: 3600 -Active cooldown: 7 - -Bubble slots: 0 diff --git a/assets/resources/dolphin/L3_Lab_research_128x54/frame_0.bm b/assets/resources/dolphin/L3_Lab_research_128x54/frame_0.bm deleted file mode 100644 index db283e81fbb46514ec3a63dedb87bba98daeb086..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 611 zcmV-p0-XH;0AB(C9v}oX06F|W|M~y;20#3`JpT|Kgajs8?>{Feln0y|4Y0(-C|H1b ze#7Yjgbl!hQGMmP^FfkZ{(4=*JIk|}|Q#BCT6`h2W`agbO8 zUe7-Od}#U4@H~1P4h3P}!192K27NKCgT@U8C!TZU4=M~g;13_bAXDh;6iC5=_{D9tfb60|+AxcxrGc1{wqGfrH#I zFh$UV$NuRUAk1~a@(CBh2dw^-#2!@PD1**04;T;f05=#8I0hgCg9FQe4>)8L9C!=} z{9rZ!dP0r`0JvZvKI!m1W`WXx!-Zq+;mlAa0)}!0#r}Z!AaVFSz#fwWuz&~_F!*F( z&`?Nc0vHcJ<_;Ga2dn}n4>*6q3viYd#bPmN1h5=j4j=vtFyjpbZXoffD~B~v1BMS0!Vg3qc>D(j1F-*q3?7~ge*j`T1L%MXMh`v)KESLHh=bRFH!b|) xaRBTC(n22@;1qe0==1{c4`bMf2Qi0(z%7CJ5D(+ROd0S!garY-UjV~52cXVC@<9Lq diff --git a/assets/resources/dolphin/L3_Lab_research_128x54/frame_1.bm b/assets/resources/dolphin/L3_Lab_research_128x54/frame_1.bm deleted file mode 100644 index c600743700f179ba45f3713af0d427024c0b683c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 614 zcmV-s0-5~*0Ad0F9v}oX06F|W|M~y;20#3`JpT|Kgajs8FFofaln0y|4Y0(-C|H1b zeuL=&gblVi*Vs{7vFNSUds*4@7v6$R3bLgaSUV zF#4v(5Rn;sJp2Lj&VPa9(BN<@4)+I)1W+^Sjbt7$XfQo8K=Po&t^o1;0t9}2Cs^b# z5MX%#2x1@s2bDts1^HjW=ji{x1RkOF03G-r)({Yz#6QwFEXW@hfDq_6kWYm?3^Cvk z;1_}qJp>E*UmSqstPzL>unH*g!9eZ-fnbXWT4E0>2l(zh5kV*h5JnpC)ZkDIGzZxO z2e@Ehi=hXP{n9W&nCpY&5-)@gS^X)9JgLG_2b^FYFdyUqZZI5h3_u442bTaIaL6b) z@E8&Jz-$2Yg&Yb2aKJ!))8Kl|1Em3n3dh~Un4n4p4CD)o{Q>YmT9()Xafmk6C2d@Bb zTlvJ|0oVtmgg!ICDDxxH=mp>&$FUF&V-E*_TLbVQAIF54GvIp&3Ilk)0fuf5K-I|j AUjP6A diff --git a/assets/resources/dolphin/L3_Lab_research_128x54/frame_10.bm b/assets/resources/dolphin/L3_Lab_research_128x54/frame_10.bm deleted file mode 100644 index 694302a9de7918f0009e738f417aee84929cc94a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 576 zcmV-G0>AwM06YQ!9v}oX06F|W|M~y;20#3`JpT|Kgajs8FFofaln0y|4Y0(-C|H1a z{(s=~fWiji4`F%WAQ1rjUtk&Vc?bqT4?=ju_#g0jNIn4(1Ld6mKx2@~WCOWRcd$IXkvtp&@n2DjkU{6tfMm3Sv7B52LevrY zgU1zU6^t^#@(=)wL;xW1;?zOo0|%$0|NozZ#!FIw90UR&2ERZjwde=N0uPG_eBrP! z@P0?-|HA*l?pz#HpjbYjc}IXlfP;g}B_y0cKUeTrjz$4sj6g3C1o(wV{Xc*(NrWvi ztUxmYvj0ch#Gn{Mlud(R1c^Xl;P>4E2^v4~ki;MmC;`4`{>`4n793gXT2=jDH}1^mv2X86<#s1EcsD2sA<>MHmz^P);C_ z@!(+hKM_1<_3cxEV0<=bAA8&^YMpFP7%pt(_jsnU6)DW1% zh3SCDumk|1^+<{YKnn{Z68!)T3&0ix064&W*c=DE2gD~q4FZ>7A20}z6te*NkV^oi O7ze{pY5-D3m;pc_5AB-( diff --git a/assets/resources/dolphin/L3_Lab_research_128x54/frame_11.bm b/assets/resources/dolphin/L3_Lab_research_128x54/frame_11.bm deleted file mode 100644 index 246b955cf93c8b6abccaef6f44dd1b8fcf01e296..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 585 zcmV-P0=E4D07U`-9v}oX06F|W|M~y;20#3`JpT|Kgajs8?Qy>+`d;`bH6T!eM0P%w<1Ri}T21`kR2cO}nAoays z1!4~%0SrU{Ao1eVLF4$pgU`|b|IfkWC8+{O@m~j+0PoNV?RY*h%fLPCA@jh%>;d^7 zmBs*~5Cazn6)3=ef#)6p4gwAjER>Uh0fGMkVmTNEf-wLj3VeT`_-76P!4P=FBngp# zWDVEGEIQ0!9He2gc|SKmmpW#vuTKKp=xL2Z$g*0)HTTR-hpA4v0Ki zg9Hea2Rs810L%a+4`{>`4p8U;;6d*i1d2RL0uOj(kXAFnh{1uR5fHe*p^}1VqsIZm zfrH%qa1Q_^jv}!rAU$s%2pB!Z;s7b|2t*?wR*1|)>42l~90EraAeP1)4?l(-AV2|! zgvuN*KlUF0V-Ny|&w>9BkK_ITSXmIDAIxBNfCCG_76bqw`mi_;cn^q9f*J)bz&>CR XASq@6^B|T1N&FxOY7IaNNYel)t_A1i diff --git a/assets/resources/dolphin/L3_Lab_research_128x54/frame_12.bm b/assets/resources/dolphin/L3_Lab_research_128x54/frame_12.bm deleted file mode 100644 index b6fb1130bb895889494bb5ade77a02bde7d64d3f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 571 zcmV-B0>u3R05<{v9v}oX06F|W|M~y;20#3`JpT|Kgajs8?>{Feln0y|4Y0(-C|H1b zf5GVigbl{$PW`(gUcXM5qN{j$Y?L&5Mnlr2>m`&KzT+3=VWyFD^hl;o$Q4Kxh#Hc>DwBo`b-F<2Zqz;2w~8Fg*W?^nruNfdGyx%sy0bd3hl7 z2n4a7egOE<^PlJpav4m3cPZ}m2cMEBgMfZ3>M>FYJhPuD21`gQ8O6XPEkRGD0~Kf$ zj55IT5CDus03h+=)Is9|2dAU||DS`#OHzOw1Ogxizd$Fo=m*9E4~qzV;jl08en;j1 z!vDeUTpU!OSU#Y6M}R|sgM-T@B%DA$SMXSlMgd@qKravk_=QLPKW_?DSVGep!~-xZ zFZ6x9N&$p9MA$X}NR$R14}H)ek)!_^3_<|{fI$Xg4-i1WJ|FlPJ?l^qd4E6RvIdAm zpgG_ekBFo|_KZP5KREVHN0I;$lFVFzcya8Z91B?gFfxvsfd_r^(&?$BS^8kqfOE3?a1h5KGfP6Iu JpamppfE4a4{Feln0y|4Y0(-C|H1b zf5GVigblZNCreNFnGtz;#ePmJcH%p#C)WX z@VLR?5QvR~01sUxE&&9G1Ir*$5qN{k$Y&TQ9>x3~evkkDfAWB6JP|>8fE)Y|7=%=KG%$PcLE|5b#6J}9{zM^_zb^tK2MT9Le2aW^z$AG~B z;`StforZh`8I0jlDDeliXbLdlK-vT25P(3S5J8ycfTRx?z&;~TP&onw9xTBNh$LV! z=+Hdia{znBB#A=cKmqeuI0yhC~6P5ffOzqZ9xjyjs2bT_F4@nFI7?mIP z{sA9@mq;F$2y_GS!!3+4h#>XC3JV8-g~kEz0H-gD;RU!A$R3bNfD~Zg$SlGP4?xk! B`X>MY diff --git a/assets/resources/dolphin/L3_Lab_research_128x54/frame_2.bm b/assets/resources/dolphin/L3_Lab_research_128x54/frame_2.bm deleted file mode 100644 index 1025137e462b44c2341e193471c8b457d4451c1e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 618 zcmV-w0+sy%0A>OJ9v}oX06F|W|M~y;20#3`JpT|Kgajs8?>u;K^nyrYW-I1e0-Lm*#YJs^FyKMwk_VLr9dHMa;1D45={mx!Dc}Cya0zmyn=iw;9-vd zhXDr%rh*0hFOL9nRtUrb*aZ}L;GlO12ZI0!7=(ZxbPw^|cp&kBs0I*58t~NMPz*E& zVFL%dXkd$>2ao;IFhQ8>gX9t~gb#84DTq9&!chmDU>-1URk2L^%fAlv|X z!yusJz+hlI$PIuVkfVV>E*J<7+I|mNpmd-x;aKgt^AKo+Mj6N#7y1L>f!yPh0D4Rg z!T=yx&gKz=K|vv$2w*(_m^o`OLFoXAgU%oDi|{3daafF60W1d>1Bd^Ma5%$38;Cq= z3gdudj6(*8h6o-aQh;wT6fk&}5PBf<$KWU`;s1aP9-a(;0^tMTA4C9BFnRDX_5~QI zgdU@F-_AS&K?kISJ~O~5^CQ=Y12{qIdl3NUAo1rIhu}azj0_$DI0vwRpf`_!a7M%c EAd|oRBLDyZ diff --git a/assets/resources/dolphin/L3_Lab_research_128x54/frame_3.bm b/assets/resources/dolphin/L3_Lab_research_128x54/frame_3.bm deleted file mode 100644 index e623a1c0fa5cba36a8a2f364e19c96d49a644e13..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 608 zcmV-m0-yZ>09*n99v}oX06F|W|M~y;20#3`JpT|Kgajs8?>{Feln0y|4Y0(-C|H1b ze#7Yjgbl!hQGMmP^FfkZ{(4=*JIk|}|Q#BCT6`h2W`agbO8 zUe7-Od}#U4@H~1P4h3P}!192K27NKCgT@U8C!TZU4=M~g;13_bAXDh;6iC5=tAQr$VqsIjUxHpA@EFo!#Jg6VzxbR>SMhK(?j5Xn@z@Qjt54r{qaKOPA zLje*Cq+o+F*9XWXUkDuRra#0%CzE6f=-6FZ2h%Ul;_y9+LyGfCv^pvlyBR z2@K#v0q6X|@!{b0fJDLP4@eEpSXUK@#h?7QS@!$>1e>g3O uJ!B#Ao&iUh9*jUOfc8CzfO8OdbOOvDfdKw6Fn9#m9>M~E-aG}tnS;Vi*Vs{7vFNSUds*4@7v6$R3bLgaSUV zF#4v(5Rn;sJp2Lj&VPa9(BN<@4)+I)1W+^Sjbt7$XfQo8K=Po&t^o1;0t9}2Cs^b# z5MX%#2x1@s2bDts1^HjW=ji{x1RkOF03G-r)({Yz#6QwFEXW@hfDq_6kWYm?3^Cvk z;1_}qJp>E*UmO7BtPzL>unH*g!9eZ>QUr`arXcd5e~#n9fKC`9kPsM1NMJPVh<{CltJehf$JbY$N=16IN%t74h#=206gK4 zP;uZeBk_RP0qF`j6awLZfZM0Q^_mAt0}d6ByMZx4lnNQh7Z>^i;DP(#a{ziw4#EH+ zSjXV;gF!(doCsh%|Cl&@Fdnc7m^|VC2wmb>R~3lGpc24wa5#VXEW?a65x9fKpsqLu zI36%)cwm9zB`5~-0mCl|VF#iQJbnX%1K5AS1`kgLKY{Ro><^*OJ9v}oX06F|W|M~y;20#3`JpT|Kgajs8?>u;K^nyrYW-I1e0-Lm*#YJs^FyKMwk_VLr9dHMa;1D45={mEOrk1|zUOhybKu^WbCb3c(17 zJ$M6h-_9ox4!}JmA@QC8N0}avKraCHJ&1sF7{Feln0y|4Y0(-C|H1b zf5GVigblZNCreNFnGtz;#ePmJcH%p#C)WX z@VLR?5QvR~01sUxE&&9G1Ir*$5qN{k$Y&TQ9>x3~evkkDfAWB6JP|>8fE)Y|7=%=KG%$PcLE|5b#6J}9{zM^_zb^tK2MT9Le2aW^z$AG~B z;`StforZh`8I0jlDDeliXbLdlK-vT25P(3S5J8ycfTRx?z&;~TP&onw9xTBNh$LV! z=+Hdia{znBB#A=cKmqeuI0yhC~6P5ffOzqZ9xjyjs2bT_F4@nFI7?mIP z{sA9@mq;F$2y_GS!!3+4h#>XC3JV8-g~kEz0H-gD;RU!A$R3bNfD~Zg$SlGP4?xk! B`X>MY diff --git a/assets/resources/dolphin/L3_Lab_research_128x54/frame_7.bm b/assets/resources/dolphin/L3_Lab_research_128x54/frame_7.bm deleted file mode 100644 index c9b99a0144864d530c76fb9864ad23ebd5351253..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 585 zcmV-P0=E4D07U`-9v}oX06F|W|M~y;20#3`JpT|Kgajs8FFofaln0y|4Y0(-C|H1a z{(s=~fWiji4`F%WAQ1rjUtk&Vc?bqT4?=ju_#g0jNIn4(1Ld6mKx2@~WCFPFaCvzmcsK=M9x!DfgU_V^$!P^*4==+|N9GS4 zRiIWN@&FLTKmrdQEkqtai}*bKAOHOP9x_^xB!3m~d4LZ60G`)_;~cyL-ohU|3=Y5_ zk@;L;3LyY7aB)(M2lyUw;1J*-;PS~yI1m^g@D?MHfLJ3C0zjw7`TvG+;0zLB3ruSe z49ETl0O^aw6e02zP;f$bQAfyx~KJP1AGK#@m@Ktb;ek_yInF&Hp3f+80f6f#gv6nNk` za4>tHjsf5Vk;GOd1P86-;R6S_yg&s$0SJU-3eg#eeJ~V$1As{4gc8`pf#>kUqzC{o z@R>t}=l;Xs3}OIK`S3sC@%(?l3kxC?1Nn>&umE9r0>FR-A65qe?*Z`%&_h6_*ayr4 XBn2$MK4cQWDL;e&?LnvkNg7}UlQy?A6d%eNqrfDR ze?Q`~28cwUIp7$Nh@?RFj6p!-;A;pzV^9dk@(2G%h&`c_NC$vAKY@UQL?R?nfkPz( z;t3BP1`l)b6UKhh5m>|)B?JeeM4({z7mxs~0{Feln0y|4Y0(-C|H1b zf5GVigbl{$PW`(gUcXM5qN{j$Y?L&5Mnlr2>m`&KzT+3=VWyFD^hl;o$Q4Kxh#Hc>DwBo`b-F<2Zqz;2w~8Fg*W?^nruNfdGyx%sy0bd3hl7 z2n4a7egOE<^PlJpav4m3R~_sRKO|2F0IUPX45SiyXFgC2mXKB;^87UgK9CGmpjIIA z01(7L0uLT7L>@nj_&og||NQ(OGFp%%e--d~fDZitp4Wrp9J~YG!XG>g4!|Fg`CMQM zApkLOaZ-#2_#ScK5a1x-^2td!5Eviu79){>SR)VuK&Qv~|Auhe!j%>fw8pUj%zxl; z4w$?_LZBEzSWSaq1dIY`4~@_ufCCH%j6wkdfI$Xg4-i0r1pYwwtw2HK9T0f41_%)- z4tNG20hj9HGzyz=Pg22^4sg1Rn6oAgpJD5rYFrA|Y{sLnQ>!M~(xB0|&YI z;2r=;97SSKKziOj5HNd-!~j#^5Qs)Vtr3`q(*Z}|I0TL;K`o3p9)AovK!5`e36wZq zf9yU0#vla`p9B6MAIJOvu(BaQKbXMl00tL;EC>KW^4} flash pages (last page {last_page_state:.02f}% full)" + fg.yellow( + f"{os.path.basename(self.args.binname):<11}: {pages:>4} flash pages (last page {last_page_state:.02f}% full)" + ) ) return 0 diff --git a/scripts/sconsdist.py b/scripts/sconsdist.py index cb4f8f5a5..4c0427894 100644 --- a/scripts/sconsdist.py +++ b/scripts/sconsdist.py @@ -7,6 +7,7 @@ from update import Main as UpdateMain import shutil import zipfile import tarfile +from ansi.color import fg class ProjectDir: @@ -126,7 +127,7 @@ class Main(App): self.copy_single_project(project) self.logger.info( - f"Firmware binaries can be found at:\n\t{self.output_dir_path}" + fg.green(f"Firmware binaries can be found at:\n\t{self.output_dir_path}") ) if self.args.version: @@ -162,7 +163,9 @@ class Main(App): if (bundle_result := UpdateMain(no_exit=True)(bundle_args)) == 0: self.logger.info( - f"Use this directory to self-update your Flipper:\n\t{bundle_dir}" + fg.green( + f"Use this directory to self-update your Flipper:\n\t{bundle_dir}" + ) ) # Create tgz archive diff --git a/scripts/toolchain/fbtenv.cmd b/scripts/toolchain/fbtenv.cmd index 5eaf16f94..a11a3ccd5 100644 --- a/scripts/toolchain/fbtenv.cmd +++ b/scripts/toolchain/fbtenv.cmd @@ -13,8 +13,8 @@ if not [%FBT_NOENV%] == [] ( exit /b 0 ) -set "FLIPPER_TOOLCHAIN_VERSION=15" -set "FBT_TOOLCHAIN_ROOT=%FBT_ROOT%\toolchain\i686-windows" +set "FLIPPER_TOOLCHAIN_VERSION=16" +set "FBT_TOOLCHAIN_ROOT=%FBT_ROOT%\toolchain\x86_64-windows" if not exist "%FBT_TOOLCHAIN_ROOT%" ( diff --git a/scripts/toolchain/fbtenv.sh b/scripts/toolchain/fbtenv.sh index 7c501803f..15f29e4dc 100755 --- a/scripts/toolchain/fbtenv.sh +++ b/scripts/toolchain/fbtenv.sh @@ -5,7 +5,7 @@ # public variables DEFAULT_SCRIPT_PATH="$(pwd -P)"; SCRIPT_PATH="${SCRIPT_PATH:-$DEFAULT_SCRIPT_PATH}"; -FBT_TOOLCHAIN_VERSION="${FBT_TOOLCHAIN_VERSION:-"15"}"; +FBT_TOOLCHAIN_VERSION="${FBT_TOOLCHAIN_VERSION:-"16"}"; FBT_TOOLCHAIN_PATH="${FBT_TOOLCHAIN_PATH:-$SCRIPT_PATH}"; fbtenv_show_usage() diff --git a/scripts/toolchain/windows-toolchain-download.ps1 b/scripts/toolchain/windows-toolchain-download.ps1 index bcb8f998f..370f1a14a 100644 --- a/scripts/toolchain/windows-toolchain-download.ps1 +++ b/scripts/toolchain/windows-toolchain-download.ps1 @@ -3,13 +3,13 @@ $ErrorActionPreference = "Stop" [Net.ServicePointManager]::SecurityProtocol = "tls12, tls11, tls" $repo_root = (Get-Item "$PSScriptRoot\..\..").FullName $toolchain_version = $args[0] -$toolchain_url = "https://update.flipperzero.one/builds/toolchain/gcc-arm-none-eabi-10.3-i686-windows-flipper-$toolchain_version.zip" -$toolchain_zip = "gcc-arm-none-eabi-10.3-i686-windows-flipper-$toolchain_version.zip" -$toolchain_dir = "gcc-arm-none-eabi-10.3-i686-windows-flipper" +$toolchain_url = "https://update.flipperzero.one/builds/toolchain/gcc-arm-none-eabi-10.3-x86_64-windows-flipper-$toolchain_version.zip" +$toolchain_zip = "gcc-arm-none-eabi-10.3-x86_64-windows-flipper-$toolchain_version.zip" +$toolchain_dir = "gcc-arm-none-eabi-10.3-x86_64-windows-flipper" -if (Test-Path -LiteralPath "$repo_root\toolchain\i686-windows") { +if (Test-Path -LiteralPath "$repo_root\toolchain\x86_64-windows") { Write-Host -NoNewline "Removing old Windows toolchain.." - Remove-Item -LiteralPath "$repo_root\toolchain\i686-windows" -Force -Recurse + Remove-Item -LiteralPath "$repo_root\toolchain\x86_64-windows" -Force -Recurse Write-Host "done!" } if (!(Test-Path -Path "$repo_root\$toolchain_zip" -PathType Leaf)) { @@ -26,7 +26,7 @@ if (!(Test-Path -LiteralPath "$repo_root\toolchain")) { Write-Host -NoNewline "Unziping Windows toolchain.." Add-Type -Assembly "System.IO.Compression.Filesystem" [System.IO.Compression.ZipFile]::ExtractToDirectory("$toolchain_zip", "$repo_root\") -Move-Item -Path "$repo_root\$toolchain_dir" -Destination "$repo_root\toolchain\i686-windows" +Move-Item -Path "$repo_root\$toolchain_dir" -Destination "$repo_root\toolchain\x86_64-windows" Write-Host "done!" Write-Host -NoNewline "Clearing temporary files.." diff --git a/site_scons/commandline.scons b/site_scons/commandline.scons index 324dfe332..cadb417f8 100644 --- a/site_scons/commandline.scons +++ b/site_scons/commandline.scons @@ -171,6 +171,11 @@ vars.AddVariables( "Blackmagic probe location", "auto", ), + ( + "OPENOCD_ADAPTER_SERIAL", + "OpenOCD adapter serial number", + "auto", + ), ( "UPDATE_SPLASH", "Directory name with slideshow frames to render after installing update package", diff --git a/site_scons/environ.scons b/site_scons/environ.scons index 2d082838a..94705dada 100644 --- a/site_scons/environ.scons +++ b/site_scons/environ.scons @@ -35,6 +35,7 @@ for env_value_name in variables_to_forward: coreenv = VAR_ENV.Clone( tools=[ + "fbt_tweaks", ( "crosscc", { @@ -80,8 +81,9 @@ if not coreenv["VERBOSE"]: # Default value for commandline options SetOption("num_jobs", multiprocessing.cpu_count()) +## NB - disabled both caches since they seem to do more harm then good in our case # Avoiding re-scan of all sources on every startup -SetOption("implicit_cache", True) +# SetOption("implicit_cache", True) # SetOption("implicit_deps_unchanged", True) # More aggressive caching SetOption("max_drift", 1) diff --git a/site_scons/extapps.scons b/site_scons/extapps.scons index 9edc6b5c1..ee317be3b 100644 --- a/site_scons/extapps.scons +++ b/site_scons/extapps.scons @@ -65,6 +65,7 @@ extapps = appenv["_extapps"] = { "debug": {}, "validators": {}, "dist": {}, + "resources_dist": None, } @@ -108,6 +109,8 @@ appenv.PhonyTarget("firmware_extapps", appenv.Action(legacy_app_build_stub, None Alias("faps", extapps["compact"].values()) Alias("faps", extapps["validators"].values()) +extapps["resources_dist"] = appenv.FapDist(appenv.Dir("#/assets/resources/apps"), []) + if appsrc := appenv.subst("$APPSRC"): app_manifest, fap_file, app_validator = appenv.GetExtAppFromPath(appsrc) appenv.PhonyTarget( diff --git a/site_scons/fbt_extra/util.py b/site_scons/fbt_extra/util.py index aa3d50b64..c670c01d4 100644 --- a/site_scons/fbt_extra/util.py +++ b/site_scons/fbt_extra/util.py @@ -1,10 +1,11 @@ from fbt.util import link_dir +from ansi.color import fg def link_elf_dir_as_latest(env, elf_node): elf_dir = elf_node.Dir(".") latest_dir = env.Dir("#build/latest") - print(f"Setting {elf_dir} as latest built dir (./build/latest/)") + print(fg.green(f"Linking {elf_dir} as latest built dir (./build/latest/)")) return link_dir(latest_dir.abspath, elf_dir.abspath, env["PLATFORM"] == "win32") @@ -12,7 +13,7 @@ def should_gen_cdb_and_link_dir(env, requested_targets): explicitly_building_updater = False # Hacky way to check if updater-related targets were requested for build_target in requested_targets: - if "updater" in str(build_target): + if "updater" in str(build_target) and "package" not in str(build_target): explicitly_building_updater = True is_updater = not env["IS_BASE_FIRMWARE"] diff --git a/site_scons/site_init.py b/site_scons/site_init.py index 2d83d8816..b763a3a7c 100644 --- a/site_scons/site_init.py +++ b/site_scons/site_init.py @@ -3,6 +3,7 @@ from SCons.Script import GetBuildFailures import sys import os import atexit +from ansi.color import fg, fx sys.path.insert(0, os.path.join(os.getcwd(), "scripts")) sys.path.insert(0, os.path.join(os.getcwd(), "lib/cxxheaderparser")) @@ -16,12 +17,12 @@ def bf_to_str(bf): if bf is None: # unknown targets product None in list return "(unknown tgt)" elif isinstance(bf, SCons.Errors.StopError): - return str(bf) + return fg.yellow(str(bf)) elif bf.node: - return str(bf.node) + ": " + bf.errstr + return fg.yellow(str(bf.node)) + ": " + bf.errstr elif bf.filename: - return bf.filename + ": " + bf.errstr - return "unknown failure: " + bf.errstr + return fg.yellow(bf.filename) + ": " + bf.errstr + return fg.yellow("unknown failure: ") + bf.errstr def display_build_status(): @@ -31,10 +32,9 @@ def display_build_status(): if bf: # bf is normally a list of build failures; if an element is None, # it's because of a target that scons doesn't know anything about. - failures_message = "\n".join( - ["Failed building %s" % bf_to_str(x) for x in bf if x is not None] - ) - print("*" * 10, "ERRORS", "*" * 10) + failures_message = "\n".join([bf_to_str(x) for x in bf if x is not None]) + print() + print(fg.brightred(fx.bold("*" * 10 + " FBT ERRORS " + "*" * 10))) print(failures_message) From 30f10dce80300a2a308e323e70cb1961f86b60cc Mon Sep 17 00:00:00 2001 From: Astra <93453568+Astrrra@users.noreply.github.com> Date: Wed, 26 Oct 2022 01:44:27 +0300 Subject: [PATCH 02/31] Fix a typo in the factory reset screen "setting" -> "settings" #1917 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: あく --- .../scenes/storage_settings_scene_factory_reset.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applications/settings/storage_settings/scenes/storage_settings_scene_factory_reset.c b/applications/settings/storage_settings/scenes/storage_settings_scene_factory_reset.c index 64d2b96b1..865ee48d4 100644 --- a/applications/settings/storage_settings/scenes/storage_settings_scene_factory_reset.c +++ b/applications/settings/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 settings will be lost!", 64, 32, AlignCenter, From d530238faeae6cf0b9ac2229ed7bead090ee1d09 Mon Sep 17 00:00:00 2001 From: Anna Prosvetova Date: Wed, 26 Oct 2022 00:48:33 +0200 Subject: [PATCH 03/31] CI: Update web updater domain (#1919) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: あく --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 7d6162954..ed5ee6bd7 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -127,7 +127,7 @@ jobs: **Compiled firmware for commit `${{steps.names.outputs.commit_sha}}`:** - [📦 Update package](https://update.flipperzero.one/builds/firmware/${{steps.names.outputs.branch_name}}/flipper-z-${{steps.names.outputs.default_target}}-update-${{steps.names.outputs.suffix}}.tgz) - [📥 DFU file](https://update.flipperzero.one/builds/firmware/${{steps.names.outputs.branch_name}}/flipper-z-${{steps.names.outputs.default_target}}-full-${{steps.names.outputs.suffix}}.dfu) - - [☁️ Web/App updater](https://my.flipp.dev/?url=https://update.flipperzero.one/builds/firmware/${{steps.names.outputs.branch_name}}/flipper-z-${{steps.names.outputs.default_target}}-update-${{steps.names.outputs.suffix}}.tgz&channel=${{steps.names.outputs.branch_name}}&version=${{steps.names.outputs.commit_sha}}) + - [☁️ Web/App updater](https://lab.flipper.net/?url=https://update.flipperzero.one/builds/firmware/${{steps.names.outputs.branch_name}}/flipper-z-${{steps.names.outputs.default_target}}-update-${{steps.names.outputs.suffix}}.tgz&channel=${{steps.names.outputs.branch_name}}&version=${{steps.names.outputs.commit_sha}}) edit-mode: replace compact: From 7d2d2b3dd9e04ca61a88781a5dd229974f1de58d Mon Sep 17 00:00:00 2001 From: gornekich Date: Wed, 26 Oct 2022 02:57:06 +0400 Subject: [PATCH 04/31] [FL-2932] TikTok: reset cursor after enter and reconnect #1921 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: あく --- .../plugins/bt_hid_app/views/bt_hid_tiktok.c | 179 ++++++++++-------- 1 file changed, 101 insertions(+), 78 deletions(-) diff --git a/applications/plugins/bt_hid_app/views/bt_hid_tiktok.c b/applications/plugins/bt_hid_app/views/bt_hid_tiktok.c index 9af00157d..fe1005b2d 100644 --- a/applications/plugins/bt_hid_app/views/bt_hid_tiktok.c +++ b/applications/plugins/bt_hid_app/views/bt_hid_tiktok.c @@ -17,6 +17,7 @@ typedef struct { bool down_pressed; bool ok_pressed; bool connected; + bool is_cursor_set; } BtHidTikTokModel; static void bt_hid_tiktok_draw_callback(Canvas* canvas, void* context) { @@ -88,48 +89,48 @@ static void bt_hid_tiktok_draw_callback(Canvas* canvas, void* context) { elements_multiline_text_aligned(canvas, 13, 62, AlignLeft, AlignBottom, "Hold to exit"); } -static void bt_hid_tiktok_process_press(BtHidTikTok* bt_hid_tiktok, InputEvent* event) { - with_view_model( - bt_hid_tiktok->view, - BtHidTikTokModel * model, - { - if(event->key == InputKeyUp) { - model->up_pressed = true; - } else if(event->key == InputKeyDown) { - model->down_pressed = true; - } else if(event->key == InputKeyLeft) { - model->left_pressed = true; - furi_hal_bt_hid_consumer_key_press(HID_CONSUMER_VOLUME_DECREMENT); - } else if(event->key == InputKeyRight) { - model->right_pressed = true; - furi_hal_bt_hid_consumer_key_press(HID_CONSUMER_VOLUME_INCREMENT); - } else if(event->key == InputKeyOk) { - model->ok_pressed = true; - } - }, - true); +static void bt_hid_tiktok_reset_cursor() { + // Set cursor to the phone's left up corner + // Delays to guarantee one packet per connection interval + for(size_t i = 0; i < 8; i++) { + furi_hal_bt_hid_mouse_move(-127, -127); + furi_delay_ms(50); + } + // Move cursor from the corner + furi_hal_bt_hid_mouse_move(20, 120); + furi_delay_ms(50); } -static void bt_hid_tiktok_process_release(BtHidTikTok* bt_hid_tiktok, InputEvent* event) { - with_view_model( - bt_hid_tiktok->view, - BtHidTikTokModel * model, - { - if(event->key == InputKeyUp) { - model->up_pressed = false; - } else if(event->key == InputKeyDown) { - model->down_pressed = false; - } else if(event->key == InputKeyLeft) { - model->left_pressed = false; - furi_hal_bt_hid_consumer_key_release(HID_CONSUMER_VOLUME_DECREMENT); - } else if(event->key == InputKeyRight) { - model->right_pressed = false; - furi_hal_bt_hid_consumer_key_release(HID_CONSUMER_VOLUME_INCREMENT); - } else if(event->key == InputKeyOk) { - model->ok_pressed = false; - } - }, - true); +static void bt_hid_tiktok_process_press(BtHidTikTokModel* model, InputEvent* event) { + if(event->key == InputKeyUp) { + model->up_pressed = true; + } else if(event->key == InputKeyDown) { + model->down_pressed = true; + } else if(event->key == InputKeyLeft) { + model->left_pressed = true; + furi_hal_bt_hid_consumer_key_press(HID_CONSUMER_VOLUME_DECREMENT); + } else if(event->key == InputKeyRight) { + model->right_pressed = true; + furi_hal_bt_hid_consumer_key_press(HID_CONSUMER_VOLUME_INCREMENT); + } else if(event->key == InputKeyOk) { + model->ok_pressed = true; + } +} + +static void bt_hid_tiktok_process_release(BtHidTikTokModel* model, InputEvent* event) { + if(event->key == InputKeyUp) { + model->up_pressed = false; + } else if(event->key == InputKeyDown) { + model->down_pressed = false; + } else if(event->key == InputKeyLeft) { + model->left_pressed = false; + furi_hal_bt_hid_consumer_key_release(HID_CONSUMER_VOLUME_DECREMENT); + } else if(event->key == InputKeyRight) { + model->right_pressed = false; + furi_hal_bt_hid_consumer_key_release(HID_CONSUMER_VOLUME_INCREMENT); + } else if(event->key == InputKeyOk) { + model->ok_pressed = false; + } } static bool bt_hid_tiktok_input_callback(InputEvent* event, void* context) { @@ -137,43 +138,59 @@ static bool bt_hid_tiktok_input_callback(InputEvent* event, void* context) { BtHidTikTok* bt_hid_tiktok = context; bool consumed = false; - if(event->type == InputTypePress) { - bt_hid_tiktok_process_press(bt_hid_tiktok, event); - consumed = true; - } else if(event->type == InputTypeRelease) { - bt_hid_tiktok_process_release(bt_hid_tiktok, event); - consumed = true; - } else if(event->type == InputTypeShort) { - if(event->key == InputKeyOk) { - furi_hal_bt_hid_mouse_press(HID_MOUSE_BTN_LEFT); - furi_delay_ms(50); - furi_hal_bt_hid_mouse_release(HID_MOUSE_BTN_LEFT); - furi_delay_ms(50); - furi_hal_bt_hid_mouse_press(HID_MOUSE_BTN_LEFT); - furi_delay_ms(50); - furi_hal_bt_hid_mouse_release(HID_MOUSE_BTN_LEFT); - consumed = true; - } else if(event->key == InputKeyUp) { - // Emulate up swipe - furi_hal_bt_hid_mouse_scroll(-6); - furi_hal_bt_hid_mouse_scroll(-12); - furi_hal_bt_hid_mouse_scroll(-19); - furi_hal_bt_hid_mouse_scroll(-12); - furi_hal_bt_hid_mouse_scroll(-6); - consumed = true; - } else if(event->key == InputKeyDown) { - // Emulate down swipe - furi_hal_bt_hid_mouse_scroll(6); - furi_hal_bt_hid_mouse_scroll(12); - furi_hal_bt_hid_mouse_scroll(19); - furi_hal_bt_hid_mouse_scroll(12); - furi_hal_bt_hid_mouse_scroll(6); - consumed = true; - } else if(event->key == InputKeyBack) { - furi_hal_bt_hid_consumer_key_release_all(); - consumed = true; - } - } + with_view_model( + bt_hid_tiktok->view, + BtHidTikTokModel * model, + { + if(event->type == InputTypePress) { + bt_hid_tiktok_process_press(model, event); + if(model->connected && !model->is_cursor_set) { + bt_hid_tiktok_reset_cursor(); + model->is_cursor_set = true; + } + consumed = true; + } else if(event->type == InputTypeRelease) { + bt_hid_tiktok_process_release(model, event); + consumed = true; + } else if(event->type == InputTypeShort) { + if(event->key == InputKeyOk) { + furi_hal_bt_hid_mouse_press(HID_MOUSE_BTN_LEFT); + furi_delay_ms(50); + furi_hal_bt_hid_mouse_release(HID_MOUSE_BTN_LEFT); + furi_delay_ms(50); + furi_hal_bt_hid_mouse_press(HID_MOUSE_BTN_LEFT); + furi_delay_ms(50); + furi_hal_bt_hid_mouse_release(HID_MOUSE_BTN_LEFT); + consumed = true; + } else if(event->key == InputKeyUp) { + // Emulate up swipe + furi_hal_bt_hid_mouse_scroll(-6); + furi_hal_bt_hid_mouse_scroll(-12); + furi_hal_bt_hid_mouse_scroll(-19); + furi_hal_bt_hid_mouse_scroll(-12); + furi_hal_bt_hid_mouse_scroll(-6); + consumed = true; + } else if(event->key == InputKeyDown) { + // Emulate down swipe + furi_hal_bt_hid_mouse_scroll(6); + furi_hal_bt_hid_mouse_scroll(12); + furi_hal_bt_hid_mouse_scroll(19); + furi_hal_bt_hid_mouse_scroll(12); + furi_hal_bt_hid_mouse_scroll(6); + consumed = true; + } else if(event->key == InputKeyBack) { + furi_hal_bt_hid_consumer_key_release_all(); + consumed = true; + } + } else if(event->type == InputTypeLong) { + if(event->key == InputKeyBack) { + furi_hal_bt_hid_consumer_key_release_all(); + model->is_cursor_set = false; + consumed = false; + } + } + }, + true); return consumed; } @@ -203,5 +220,11 @@ View* bt_hid_tiktok_get_view(BtHidTikTok* bt_hid_tiktok) { void bt_hid_tiktok_set_connected_status(BtHidTikTok* bt_hid_tiktok, bool connected) { furi_assert(bt_hid_tiktok); with_view_model( - bt_hid_tiktok->view, BtHidTikTokModel * model, { model->connected = connected; }, true); + bt_hid_tiktok->view, + BtHidTikTokModel * model, + { + model->connected = connected; + model->is_cursor_set = false; + }, + true); } From 406d830fb652bb0a8791efd28357bb50e1f9640a Mon Sep 17 00:00:00 2001 From: Max Lapan Date: Wed, 26 Oct 2022 15:37:58 +0200 Subject: [PATCH 05/31] Oregon2 extra (#1924) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add support for temp-humid sensor 0x1D20 * Fix protocol type and flags, humidity decoding Co-authored-by: あく --- .../weather_station/protocols/oregon2.c | 42 ++++++++++++------- 1 file changed, 27 insertions(+), 15 deletions(-) diff --git a/applications/plugins/weather_station/protocols/oregon2.c b/applications/plugins/weather_station/protocols/oregon2.c index c4e4471a0..76bc3f0a1 100644 --- a/applications/plugins/weather_station/protocols/oregon2.c +++ b/applications/plugins/weather_station/protocols/oregon2.c @@ -19,7 +19,7 @@ static const SubGhzBlockConst ws_oregon2_const = { }; #define OREGON2_PREAMBLE_BITS 19 -#define OREGON2_PREAMBLE_MASK ((1 << (OREGON2_PREAMBLE_BITS + 1)) - 1) +#define OREGON2_PREAMBLE_MASK 0b1111111111111111111 #define OREGON2_SENSOR_ID(d) (((d) >> 16) & 0xFFFF) #define OREGON2_CHECKSUM_BITS 8 @@ -103,6 +103,7 @@ static ManchesterEvent level_and_duration_to_event(bool level, uint32_t duration // From sensor id code return amount of bits in variable section static uint8_t oregon2_sensor_id_var_bits(uint16_t sensor_id) { if(sensor_id == 0xEC40) return 16; + if(sensor_id == 0x1D20) return 24; return 0; } @@ -119,18 +120,30 @@ static void ws_oregon2_decode_const_data(WSBlockGeneric* ws_block) { ws_block->battery_low = (ws_block->data & OREGON2_FLAG_BAT_LOW) ? 1 : 0; } -static void - ws_oregon2_decode_var_data(WSBlockGeneric* ws_block, uint16_t sensor_id, uint32_t var_data) { - int16_t temp_val; - if(sensor_id == 0xEC40) { - temp_val = ((var_data >> 4) & 0xF) * 10 + ((var_data >> 8) & 0xF); - temp_val *= 10; - temp_val += (var_data >> 12) & 0xF; - if(var_data & 0xF) temp_val = -temp_val; - } else - return; +uint16_t bcd_decode_short(uint32_t data) { + return (data & 0xF) * 10 + ((data >> 4) & 0xF); +} - ws_block->temp = (float)temp_val / 10.0; +static float ws_oregon2_decode_temp(uint32_t data) { + int32_t temp_val; + temp_val = bcd_decode_short(data >> 4); + temp_val *= 10; + temp_val += (data >> 12) & 0xF; + if(data & 0xF) temp_val = -temp_val; + return (float)temp_val / 10.0; +} + +static void ws_oregon2_decode_var_data(WSBlockGeneric* ws_b, uint16_t sensor_id, uint32_t data) { + switch(sensor_id) { + case 0xEC40: + ws_b->temp = ws_oregon2_decode_temp(data); + ws_b->humidity = WS_NO_HUMIDITY; + break; + case 0x1D20: + ws_b->humidity = bcd_decode_short(data); + ws_b->temp = ws_oregon2_decode_temp(data >> 8); + break; + } } void ws_protocol_decoder_oregon2_feed(void* context, bool level, uint32_t duration) { @@ -346,9 +359,8 @@ const SubGhzProtocolDecoder ws_protocol_oregon2_decoder = { const SubGhzProtocol ws_protocol_oregon2 = { .name = WS_PROTOCOL_OREGON2_NAME, - .type = SubGhzProtocolTypeStatic, - .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable | - SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save, + .type = SubGhzProtocolWeatherStation, + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable, .decoder = &ws_protocol_oregon2_decoder, }; From 378bf050684b9e56d71c84fd2e44a57caa507ee2 Mon Sep 17 00:00:00 2001 From: Skorpionm <85568270+Skorpionm@users.noreply.github.com> Date: Wed, 26 Oct 2022 18:56:54 +0400 Subject: [PATCH 06/31] [FL-2934] WS: add protocol Acurite 592TXR (#1916) * WS: add protocol Acurite 592TXR * WS: fix decoder Acurite_592TXR * SubGhz: proper types in math Co-authored-by: Aleksandr Kutuzov --- .../helpers/weather_station_types.h | 2 +- .../protocols/acurite_592txr.c | 307 ++++++++++++++++++ .../protocols/acurite_592txr.h | 79 +++++ .../protocols/protocol_items.c | 1 + .../protocols/protocol_items.h | 1 + firmware/targets/f7/api_symbols.csv | 6 +- lib/subghz/blocks/math.c | 30 ++ lib/subghz/blocks/math.h | 31 ++ 8 files changed, 455 insertions(+), 2 deletions(-) create mode 100644 applications/plugins/weather_station/protocols/acurite_592txr.c create mode 100644 applications/plugins/weather_station/protocols/acurite_592txr.h diff --git a/applications/plugins/weather_station/helpers/weather_station_types.h b/applications/plugins/weather_station/helpers/weather_station_types.h index 275d23329..479638b98 100644 --- a/applications/plugins/weather_station/helpers/weather_station_types.h +++ b/applications/plugins/weather_station/helpers/weather_station_types.h @@ -3,7 +3,7 @@ #include #include -#define WS_VERSION_APP "0.2" +#define WS_VERSION_APP "0.3" #define WS_DEVELOPED "SkorP" #define WS_GITHUB "https://github.com/flipperdevices/flipperzero-firmware" diff --git a/applications/plugins/weather_station/protocols/acurite_592txr.c b/applications/plugins/weather_station/protocols/acurite_592txr.c new file mode 100644 index 000000000..db05af095 --- /dev/null +++ b/applications/plugins/weather_station/protocols/acurite_592txr.c @@ -0,0 +1,307 @@ +#include "acurite_592txr.h" + +#define TAG "WSProtocolAcurite_592TXR" + +/* + * Help + * https://github.com/merbanan/rtl_433/blob/5bef4e43133ac4c0e2d18d36f87c52b4f9458453/src/devices/acurite.c + * + * Acurite 592TXR Temperature Humidity sensor decoder + * Message Type 0x04, 7 bytes + * | Byte 0 | Byte 1 | Byte 2 | Byte 3 | Byte 4 | Byte 5 | Byte 6 | + * | --------- | --------- | --------- | --------- | --------- | --------- | --------- | + * | CCII IIII | IIII IIII | pB00 0100 | pHHH HHHH | p??T TTTT | pTTT TTTT | KKKK KKKK | + * - C: Channel 00: C, 10: B, 11: A, (01 is invalid) + * - I: Device ID (14 bits) + * - B: Battery, 1 is battery OK, 0 is battery low + * - M: Message type (6 bits), 0x04 + * - T: Temperature Celsius (11 - 14 bits?), + 1000 * 10 + * - H: Relative Humidity (%) (7 bits) + * - K: Checksum (8 bits) + * - p: Parity bit + * Notes: + * - Temperature + * - Encoded as Celsius + 1000 * 10 + * - only 11 bits needed for specified range -40 C to 70 C (-40 F - 158 F) + * - However 14 bits available for temperature, giving possible range of -100 C to 1538.4 C + * - @todo - check if high 3 bits ever used for anything else + * + */ + +static const SubGhzBlockConst ws_protocol_acurite_592txr_const = { + .te_short = 200, + .te_long = 400, + .te_delta = 90, + .min_count_bit_for_found = 56, +}; + +struct WSProtocolDecoderAcurite_592TXR { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + WSBlockGeneric generic; + + uint16_t header_count; +}; + +struct WSProtocolEncoderAcurite_592TXR { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolBlockEncoder encoder; + WSBlockGeneric generic; +}; + +typedef enum { + Acurite_592TXRDecoderStepReset = 0, + Acurite_592TXRDecoderStepCheckPreambule, + Acurite_592TXRDecoderStepSaveDuration, + Acurite_592TXRDecoderStepCheckDuration, +} Acurite_592TXRDecoderStep; + +const SubGhzProtocolDecoder ws_protocol_acurite_592txr_decoder = { + .alloc = ws_protocol_decoder_acurite_592txr_alloc, + .free = ws_protocol_decoder_acurite_592txr_free, + + .feed = ws_protocol_decoder_acurite_592txr_feed, + .reset = ws_protocol_decoder_acurite_592txr_reset, + + .get_hash_data = ws_protocol_decoder_acurite_592txr_get_hash_data, + .serialize = ws_protocol_decoder_acurite_592txr_serialize, + .deserialize = ws_protocol_decoder_acurite_592txr_deserialize, + .get_string = ws_protocol_decoder_acurite_592txr_get_string, +}; + +const SubGhzProtocolEncoder ws_protocol_acurite_592txr_encoder = { + .alloc = NULL, + .free = NULL, + + .deserialize = NULL, + .stop = NULL, + .yield = NULL, +}; + +const SubGhzProtocol ws_protocol_acurite_592txr = { + .name = WS_PROTOCOL_ACURITE_592TXR_NAME, + .type = SubGhzProtocolWeatherStation, + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_315 | SubGhzProtocolFlag_868 | + SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable, + + .decoder = &ws_protocol_acurite_592txr_decoder, + .encoder = &ws_protocol_acurite_592txr_encoder, +}; + +void* ws_protocol_decoder_acurite_592txr_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + WSProtocolDecoderAcurite_592TXR* instance = malloc(sizeof(WSProtocolDecoderAcurite_592TXR)); + instance->base.protocol = &ws_protocol_acurite_592txr; + instance->generic.protocol_name = instance->base.protocol->name; + return instance; +} + +void ws_protocol_decoder_acurite_592txr_free(void* context) { + furi_assert(context); + WSProtocolDecoderAcurite_592TXR* instance = context; + free(instance); +} + +void ws_protocol_decoder_acurite_592txr_reset(void* context) { + furi_assert(context); + WSProtocolDecoderAcurite_592TXR* instance = context; + instance->decoder.parser_step = Acurite_592TXRDecoderStepReset; +} + +static bool ws_protocol_acurite_592txr_check_crc(WSProtocolDecoderAcurite_592TXR* instance) { + uint8_t msg[] = { + instance->decoder.decode_data >> 48, + instance->decoder.decode_data >> 40, + instance->decoder.decode_data >> 32, + instance->decoder.decode_data >> 24, + instance->decoder.decode_data >> 16, + instance->decoder.decode_data >> 8}; + + if((subghz_protocol_blocks_add_bytes(msg, 6) == + (uint8_t)(instance->decoder.decode_data & 0xFF)) && + (!subghz_protocol_blocks_parity_bytes(&msg[2], 4))) { + return true; + } else { + return false; + } +} + +/** + * Analysis of received data + * @param instance Pointer to a WSBlockGeneric* instance + */ +static void ws_protocol_acurite_592txr_remote_controller(WSBlockGeneric* instance) { + uint8_t channel[] = {3, 0, 2, 1}; + uint8_t channel_raw = ((instance->data >> 54) & 0x03); + instance->channel = channel[channel_raw]; + instance->id = (instance->data >> 40) & 0x3FFF; + instance->battery_low = !((instance->data >> 38) & 1); + instance->humidity = (instance->data >> 24) & 0x7F; + + uint16_t temp_raw = ((instance->data >> 9) & 0xF80) | ((instance->data >> 8) & 0x7F); + instance->temp = ((float)(temp_raw)-1000) / 10.0f; + + instance->btn = WS_NO_BTN; +} + +void ws_protocol_decoder_acurite_592txr_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + WSProtocolDecoderAcurite_592TXR* instance = context; + + switch(instance->decoder.parser_step) { + case Acurite_592TXRDecoderStepReset: + if((level) && (DURATION_DIFF(duration, ws_protocol_acurite_592txr_const.te_short * 3) < + ws_protocol_acurite_592txr_const.te_delta * 2)) { + instance->decoder.parser_step = Acurite_592TXRDecoderStepCheckPreambule; + instance->decoder.te_last = duration; + instance->header_count = 0; + } + break; + + case Acurite_592TXRDecoderStepCheckPreambule: + if(level) { + instance->decoder.te_last = duration; + } else { + if((DURATION_DIFF( + instance->decoder.te_last, ws_protocol_acurite_592txr_const.te_short * 3) < + ws_protocol_acurite_592txr_const.te_delta * 2) && + (DURATION_DIFF(duration, ws_protocol_acurite_592txr_const.te_short * 3) < + ws_protocol_acurite_592txr_const.te_delta * 2)) { + //Found preambule + instance->header_count++; + } else if((instance->header_count > 2) && (instance->header_count < 5)) { + if((DURATION_DIFF( + instance->decoder.te_last, ws_protocol_acurite_592txr_const.te_short) < + ws_protocol_acurite_592txr_const.te_delta) && + (DURATION_DIFF(duration, ws_protocol_acurite_592txr_const.te_long) < + ws_protocol_acurite_592txr_const.te_delta)) { + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + instance->decoder.parser_step = Acurite_592TXRDecoderStepSaveDuration; + } else if( + (DURATION_DIFF( + instance->decoder.te_last, ws_protocol_acurite_592txr_const.te_long) < + ws_protocol_acurite_592txr_const.te_delta) && + (DURATION_DIFF(duration, ws_protocol_acurite_592txr_const.te_short) < + ws_protocol_acurite_592txr_const.te_delta)) { + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + instance->decoder.parser_step = Acurite_592TXRDecoderStepSaveDuration; + } else { + instance->decoder.parser_step = Acurite_592TXRDecoderStepReset; + } + } else { + instance->decoder.parser_step = Acurite_592TXRDecoderStepReset; + } + } + break; + + case Acurite_592TXRDecoderStepSaveDuration: + if(level) { + instance->decoder.te_last = duration; + instance->decoder.parser_step = Acurite_592TXRDecoderStepCheckDuration; + } else { + instance->decoder.parser_step = Acurite_592TXRDecoderStepReset; + } + break; + + case Acurite_592TXRDecoderStepCheckDuration: + if(!level) { + if(duration >= ((uint32_t)ws_protocol_acurite_592txr_const.te_short * 5)) { + if((instance->decoder.decode_count_bit == + ws_protocol_acurite_592txr_const.min_count_bit_for_found) && + ws_protocol_acurite_592txr_check_crc(instance)) { + instance->generic.data = instance->decoder.decode_data; + instance->generic.data_count_bit = instance->decoder.decode_count_bit; + ws_protocol_acurite_592txr_remote_controller(&instance->generic); + if(instance->base.callback) + instance->base.callback(&instance->base, instance->base.context); + } + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + instance->decoder.parser_step = Acurite_592TXRDecoderStepReset; + break; + } else if( + (DURATION_DIFF( + instance->decoder.te_last, ws_protocol_acurite_592txr_const.te_short) < + ws_protocol_acurite_592txr_const.te_delta) && + (DURATION_DIFF(duration, ws_protocol_acurite_592txr_const.te_long) < + ws_protocol_acurite_592txr_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + instance->decoder.parser_step = Acurite_592TXRDecoderStepSaveDuration; + } else if( + (DURATION_DIFF( + instance->decoder.te_last, ws_protocol_acurite_592txr_const.te_long) < + ws_protocol_acurite_592txr_const.te_delta) && + (DURATION_DIFF(duration, ws_protocol_acurite_592txr_const.te_short) < + ws_protocol_acurite_592txr_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + instance->decoder.parser_step = Acurite_592TXRDecoderStepSaveDuration; + } else { + instance->decoder.parser_step = Acurite_592TXRDecoderStepReset; + } + } else { + instance->decoder.parser_step = Acurite_592TXRDecoderStepReset; + } + break; + } +} + +uint8_t ws_protocol_decoder_acurite_592txr_get_hash_data(void* context) { + furi_assert(context); + WSProtocolDecoderAcurite_592TXR* instance = context; + return subghz_protocol_blocks_get_hash_data( + &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); +} + +bool ws_protocol_decoder_acurite_592txr_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(context); + WSProtocolDecoderAcurite_592TXR* instance = context; + return ws_block_generic_serialize(&instance->generic, flipper_format, preset); +} + +bool ws_protocol_decoder_acurite_592txr_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + WSProtocolDecoderAcurite_592TXR* instance = context; + bool ret = false; + do { + if(!ws_block_generic_deserialize(&instance->generic, flipper_format)) { + break; + } + if(instance->generic.data_count_bit != + ws_protocol_acurite_592txr_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } + ret = true; + } while(false); + return ret; +} + +void ws_protocol_decoder_acurite_592txr_get_string(void* context, FuriString* output) { + furi_assert(context); + WSProtocolDecoderAcurite_592TXR* instance = context; + furi_string_printf( + output, + "%s %dbit\r\n" + "Key:0x%lX%08lX\r\n" + "Sn:0x%lX Ch:%d Bat:%d\r\n" + "Temp:%d.%d C Hum:%d%%", + instance->generic.protocol_name, + instance->generic.data_count_bit, + (uint32_t)(instance->generic.data >> 32), + (uint32_t)(instance->generic.data), + instance->generic.id, + instance->generic.channel, + instance->generic.battery_low, + (int16_t)instance->generic.temp, + abs(((int16_t)(instance->generic.temp * 10) - (((int16_t)instance->generic.temp) * 10))), + instance->generic.humidity); +} diff --git a/applications/plugins/weather_station/protocols/acurite_592txr.h b/applications/plugins/weather_station/protocols/acurite_592txr.h new file mode 100644 index 000000000..ac0371d89 --- /dev/null +++ b/applications/plugins/weather_station/protocols/acurite_592txr.h @@ -0,0 +1,79 @@ +#pragma once + +#include + +#include +#include +#include +#include "ws_generic.h" +#include + +#define WS_PROTOCOL_ACURITE_592TXR_NAME "Acurite 592TXR" + +typedef struct WSProtocolDecoderAcurite_592TXR WSProtocolDecoderAcurite_592TXR; +typedef struct WSProtocolEncoderAcurite_592TXR WSProtocolEncoderAcurite_592TXR; + +extern const SubGhzProtocolDecoder ws_protocol_acurite_592txr_decoder; +extern const SubGhzProtocolEncoder ws_protocol_acurite_592txr_encoder; +extern const SubGhzProtocol ws_protocol_acurite_592txr; + +/** + * Allocate WSProtocolDecoderAcurite_592TXR. + * @param environment Pointer to a SubGhzEnvironment instance + * @return WSProtocolDecoderAcurite_592TXR* pointer to a WSProtocolDecoderAcurite_592TXR instance + */ +void* ws_protocol_decoder_acurite_592txr_alloc(SubGhzEnvironment* environment); + +/** + * Free WSProtocolDecoderAcurite_592TXR. + * @param context Pointer to a WSProtocolDecoderAcurite_592TXR instance + */ +void ws_protocol_decoder_acurite_592txr_free(void* context); + +/** + * Reset decoder WSProtocolDecoderAcurite_592TXR. + * @param context Pointer to a WSProtocolDecoderAcurite_592TXR instance + */ +void ws_protocol_decoder_acurite_592txr_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a WSProtocolDecoderAcurite_592TXR instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void ws_protocol_decoder_acurite_592txr_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a WSProtocolDecoderAcurite_592TXR instance + * @return hash Hash sum + */ +uint8_t ws_protocol_decoder_acurite_592txr_get_hash_data(void* context); + +/** + * Serialize data WSProtocolDecoderAcurite_592TXR. + * @param context Pointer to a WSProtocolDecoderAcurite_592TXR instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return true On success + */ +bool ws_protocol_decoder_acurite_592txr_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data WSProtocolDecoderAcurite_592TXR. + * @param context Pointer to a WSProtocolDecoderAcurite_592TXR instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool ws_protocol_decoder_acurite_592txr_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a WSProtocolDecoderAcurite_592TXR instance + * @param output Resulting text + */ +void ws_protocol_decoder_acurite_592txr_get_string(void* context, FuriString* output); diff --git a/applications/plugins/weather_station/protocols/protocol_items.c b/applications/plugins/weather_station/protocols/protocol_items.c index 0740691a2..3ec9e995a 100644 --- a/applications/plugins/weather_station/protocols/protocol_items.c +++ b/applications/plugins/weather_station/protocols/protocol_items.c @@ -8,6 +8,7 @@ const SubGhzProtocol* weather_station_protocol_registry_items[] = { &ws_protocol_acurite_606tx, &ws_protocol_lacrosse_tx141thbv2, &ws_protocol_oregon2, + &ws_protocol_acurite_592txr, }; const SubGhzProtocolRegistry weather_station_protocol_registry = { diff --git a/applications/plugins/weather_station/protocols/protocol_items.h b/applications/plugins/weather_station/protocols/protocol_items.h index edc078cd6..8f3eb53d7 100644 --- a/applications/plugins/weather_station/protocols/protocol_items.h +++ b/applications/plugins/weather_station/protocols/protocol_items.h @@ -8,5 +8,6 @@ #include "acurite_606tx.h" #include "lacrosse_tx141thbv2.h" #include "oregon2.h" +#include "acurite_592txr.h" extern const SubGhzProtocolRegistry weather_station_protocol_registry; diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index 4c48d9abd..17a8a675a 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,5.0,, +Version,+,5.1,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -2271,6 +2271,7 @@ Function,-,subghz_keystore_raw_encrypted_save,_Bool,"const char*, const char*, u Function,-,subghz_keystore_raw_get_data,_Bool,"const char*, size_t, uint8_t*, size_t" Function,-,subghz_keystore_save,_Bool,"SubGhzKeystore*, const char*, uint8_t*" Function,+,subghz_protocol_blocks_add_bit,void,"SubGhzBlockDecoder*, uint8_t" +Function,+,subghz_protocol_blocks_add_bytes,uint8_t,"const uint8_t[], size_t" Function,+,subghz_protocol_blocks_crc16,uint16_t,"const uint8_t[], unsigned, uint16_t, uint16_t" Function,+,subghz_protocol_blocks_crc16lsb,uint16_t,"const uint8_t[], unsigned, uint16_t, uint16_t" Function,+,subghz_protocol_blocks_crc4,uint8_t,"const uint8_t[], unsigned, uint8_t, uint8_t" @@ -2284,8 +2285,11 @@ Function,+,subghz_protocol_blocks_get_upload,size_t,"uint8_t[], size_t, LevelDur Function,+,subghz_protocol_blocks_lfsr_digest16,uint16_t,"const uint8_t[], unsigned, uint16_t, uint16_t" Function,+,subghz_protocol_blocks_lfsr_digest8,uint8_t,"const uint8_t[], unsigned, uint8_t, uint8_t" Function,+,subghz_protocol_blocks_lfsr_digest8_reflect,uint8_t,"const uint8_t[], int, uint8_t, uint8_t" +Function,+,subghz_protocol_blocks_parity8,int,uint8_t +Function,+,subghz_protocol_blocks_parity_bytes,int,"const uint8_t[], size_t" Function,+,subghz_protocol_blocks_reverse_key,uint64_t,"uint64_t, uint8_t" Function,+,subghz_protocol_blocks_set_bit_array,void,"_Bool, uint8_t[], size_t, size_t" +Function,+,subghz_protocol_blocks_xor_bytes,uint8_t,"const uint8_t[], size_t" Function,-,subghz_protocol_decoder_base_deserialize,_Bool,"SubGhzProtocolDecoderBase*, FlipperFormat*" Function,+,subghz_protocol_decoder_base_get_hash_data,uint8_t,SubGhzProtocolDecoderBase* Function,+,subghz_protocol_decoder_base_get_string,_Bool,"SubGhzProtocolDecoderBase*, FuriString*" diff --git a/lib/subghz/blocks/math.c b/lib/subghz/blocks/math.c index 588d4effc..d2b8e3d11 100644 --- a/lib/subghz/blocks/math.c +++ b/lib/subghz/blocks/math.c @@ -216,4 +216,34 @@ uint16_t subghz_protocol_blocks_lfsr_digest16( } } return sum; +} + +uint8_t subghz_protocol_blocks_add_bytes(uint8_t const message[], size_t num_bytes) { + int result = 0; + for(size_t i = 0; i < num_bytes; ++i) { + result += message[i]; + } + return (uint8_t)result; +} + +int subghz_protocol_blocks_parity8(uint8_t byte) { + byte ^= byte >> 4; + byte &= 0xf; + return (0x6996 >> byte) & 1; +} + +int subghz_protocol_blocks_parity_bytes(uint8_t const message[], size_t num_bytes) { + int result = 0; + for(size_t i = 0; i < num_bytes; ++i) { + result ^= subghz_protocol_blocks_parity8(message[i]); + } + return result; +} + +uint8_t subghz_protocol_blocks_xor_bytes(uint8_t const message[], size_t num_bytes) { + uint8_t result = 0; + for(size_t i = 0; i < num_bytes; ++i) { + result ^= message[i]; + } + return result; } \ No newline at end of file diff --git a/lib/subghz/blocks/math.h b/lib/subghz/blocks/math.h index 275889484..8cddf4c0b 100644 --- a/lib/subghz/blocks/math.h +++ b/lib/subghz/blocks/math.h @@ -161,6 +161,37 @@ uint16_t subghz_protocol_blocks_lfsr_digest16( uint16_t gen, uint16_t key); +/** + * Compute Addition of a number of bytes. + * @param message bytes of message data + * @param num_bytes number of bytes to sum + * @return summation value + **/ +uint8_t subghz_protocol_blocks_add_bytes(uint8_t const message[], size_t num_bytes); + +/** + * Compute bit parity of a single byte (8 bits). + * @param byte single byte to check + * @return 1 odd parity, 0 even parity + **/ +int subghz_protocol_blocks_parity8(uint8_t byte); + +/** + * Compute bit parity of a number of bytes. + * @param message bytes of message data + * @param num_bytes number of bytes to sum + * @return 1 odd parity, 0 even parity + **/ +int subghz_protocol_blocks_parity_bytes(uint8_t const message[], size_t num_bytes); + +/** + * Compute XOR (byte-wide parity) of a number of bytes. + * @param message bytes of message data + * @param num_bytes number of bytes to sum + * @return summation value, per bit-position 1 odd parity, 0 even parity + **/ +uint8_t subghz_protocol_blocks_xor_bytes(uint8_t const message[], size_t num_bytes); + #ifdef __cplusplus } #endif From 5c8df66b7c2d10c4e17c831ebf9852d5690205fc Mon Sep 17 00:00:00 2001 From: Skorpionm <85568270+Skorpionm@users.noreply.github.com> Date: Wed, 26 Oct 2022 19:13:00 +0400 Subject: [PATCH 07/31] [FL-2927] SubGhz: add RAW Read threshold rssi (#1911) * SubGhz: add RAW Read threshold rssi * SubGhz: update indicator RSSI * SubGhz: fix record file Co-authored-by: Aleksandr Kutuzov --- .../subghz/scenes/subghz_scene_read_raw.c | 50 ++++++++++-- .../scenes/subghz_scene_receiver_config.c | 52 +++++++++++++ applications/main/subghz/subghz.c | 1 + applications/main/subghz/subghz_i.h | 3 + .../main/subghz/views/subghz_read_raw.c | 77 ++++++++++++++++--- .../main/subghz/views/subghz_read_raw.h | 7 +- firmware/targets/f7/api_symbols.csv | 1 + lib/subghz/protocols/raw.c | 12 ++- lib/subghz/protocols/raw.h | 7 ++ 9 files changed, 192 insertions(+), 18 deletions(-) diff --git a/applications/main/subghz/scenes/subghz_scene_read_raw.c b/applications/main/subghz/scenes/subghz_scene_read_raw.c index 5dacefd66..8ac9bf5ba 100644 --- a/applications/main/subghz/scenes/subghz_scene_read_raw.c +++ b/applications/main/subghz/scenes/subghz_scene_read_raw.c @@ -6,6 +6,7 @@ #define RAW_FILE_NAME "Raw_signal_" #define TAG "SubGhzSceneReadRAW" +#define RAW_THRESHOLD_RSSI_LOW_COUNT 10 bool subghz_scene_read_raw_update_filename(SubGhz* subghz) { bool ret = false; @@ -72,24 +73,33 @@ void subghz_scene_read_raw_on_enter(void* context) { switch(subghz->txrx->rx_key_state) { case SubGhzRxKeyStateBack: - subghz_read_raw_set_status(subghz->subghz_read_raw, SubGhzReadRAWStatusIDLE, ""); + subghz_read_raw_set_status( + subghz->subghz_read_raw, SubGhzReadRAWStatusIDLE, "", subghz->txrx->raw_threshold_rssi); break; case SubGhzRxKeyStateRAWLoad: path_extract_filename(subghz->file_path, file_name, true); subghz_read_raw_set_status( subghz->subghz_read_raw, SubGhzReadRAWStatusLoadKeyTX, - furi_string_get_cstr(file_name)); + furi_string_get_cstr(file_name), + subghz->txrx->raw_threshold_rssi); subghz->txrx->rx_key_state = SubGhzRxKeyStateIDLE; break; case SubGhzRxKeyStateRAWSave: path_extract_filename(subghz->file_path, file_name, true); subghz_read_raw_set_status( - subghz->subghz_read_raw, SubGhzReadRAWStatusSaveKey, furi_string_get_cstr(file_name)); + subghz->subghz_read_raw, + SubGhzReadRAWStatusSaveKey, + furi_string_get_cstr(file_name), + subghz->txrx->raw_threshold_rssi); subghz->txrx->rx_key_state = SubGhzRxKeyStateIDLE; break; default: - subghz_read_raw_set_status(subghz->subghz_read_raw, SubGhzReadRAWStatusStart, ""); + subghz_read_raw_set_status( + subghz->subghz_read_raw, + SubGhzReadRAWStatusStart, + "", + subghz->txrx->raw_threshold_rssi); subghz->txrx->rx_key_state = SubGhzRxKeyStateIDLE; break; } @@ -273,7 +283,7 @@ bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) { if(subghz->txrx->rx_key_state != SubGhzRxKeyStateIDLE) { scene_manager_next_scene(subghz->scene_manager, SubGhzSceneNeedSaving); } else { - //subghz_get_preset_name(subghz, subghz->error_str); + subghz->txrx->raw_threshold_rssi_low_count = RAW_THRESHOLD_RSSI_LOW_COUNT; if(subghz_protocol_raw_save_to_file_init( (SubGhzProtocolDecoderRAW*)subghz->txrx->decoder_result, RAW_FILE_NAME, @@ -319,7 +329,35 @@ bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) { subghz->subghz_read_raw, subghz_protocol_raw_get_sample_write( (SubGhzProtocolDecoderRAW*)subghz->txrx->decoder_result)); - subghz_read_raw_add_data_rssi(subghz->subghz_read_raw, furi_hal_subghz_get_rssi()); + + float rssi = furi_hal_subghz_get_rssi(); + + if(subghz->txrx->raw_threshold_rssi == SUBGHZ_RAW_TRESHOLD_MIN) { + subghz_read_raw_add_data_rssi(subghz->subghz_read_raw, rssi, true); + subghz_protocol_raw_save_to_file_pause( + (SubGhzProtocolDecoderRAW*)subghz->txrx->decoder_result, false); + } else { + if(rssi < subghz->txrx->raw_threshold_rssi) { + subghz->txrx->raw_threshold_rssi_low_count++; + if(subghz->txrx->raw_threshold_rssi_low_count > RAW_THRESHOLD_RSSI_LOW_COUNT) { + subghz->txrx->raw_threshold_rssi_low_count = RAW_THRESHOLD_RSSI_LOW_COUNT; + } + subghz_read_raw_add_data_rssi(subghz->subghz_read_raw, rssi, false); + } else { + subghz->txrx->raw_threshold_rssi_low_count = 0; + } + + if(subghz->txrx->raw_threshold_rssi_low_count == RAW_THRESHOLD_RSSI_LOW_COUNT) { + subghz_read_raw_add_data_rssi(subghz->subghz_read_raw, rssi, false); + subghz_protocol_raw_save_to_file_pause( + (SubGhzProtocolDecoderRAW*)subghz->txrx->decoder_result, true); + } else { + subghz_read_raw_add_data_rssi(subghz->subghz_read_raw, rssi, true); + subghz_protocol_raw_save_to_file_pause( + (SubGhzProtocolDecoderRAW*)subghz->txrx->decoder_result, false); + } + } + break; case SubGhzNotificationStateTx: notification_message(subghz->notifications, &sequence_blink_magenta_10); diff --git a/applications/main/subghz/scenes/subghz_scene_receiver_config.c b/applications/main/subghz/scenes/subghz_scene_receiver_config.c index 5f022d6e1..fd42829b5 100644 --- a/applications/main/subghz/scenes/subghz_scene_receiver_config.c +++ b/applications/main/subghz/scenes/subghz_scene_receiver_config.c @@ -1,10 +1,41 @@ #include "../subghz_i.h" +#include enum SubGhzSettingIndex { SubGhzSettingIndexFrequency, SubGhzSettingIndexHopping, SubGhzSettingIndexModulation, SubGhzSettingIndexLock, + SubGhzSettingIndexRAWThesholdRSSI, +}; + +#define RAW_THRESHOLD_RSSI_COUNT 11 +const char* const raw_theshold_rssi_text[RAW_THRESHOLD_RSSI_COUNT] = { + "-----", + "-85.0", + "-80.0", + "-75.0", + "-70.0", + "-65.0", + "-60.0", + "-55.0", + "-50.0", + "-45.0", + "-40.0", + +}; +const float raw_theshold_rssi_value[RAW_THRESHOLD_RSSI_COUNT] = { + -90.0f, + -85.0f, + -80.0f, + -75.0f, + -70.0f, + -65.0f, + -60.0f, + -55.0f, + -50.0f, + -45.0f, + -40.0f, }; #define HOPPING_COUNT 2 @@ -136,6 +167,14 @@ static void subghz_scene_receiver_config_set_hopping_running(VariableItem* item) subghz->txrx->hopper_state = hopping_value[index]; } +static void subghz_scene_receiver_config_set_raw_threshold_rssi(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, raw_theshold_rssi_text[index]); + subghz->txrx->raw_threshold_rssi = raw_theshold_rssi_value[index]; +} + static void subghz_scene_receiver_config_var_list_enter_callback(void* context, uint32_t index) { furi_assert(context); SubGhz* subghz = context; @@ -204,6 +243,19 @@ void subghz_scene_receiver_config_on_enter(void* context) { subghz_scene_receiver_config_var_list_enter_callback, subghz); } + if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneReadRAW) == + SubGhzCustomEventManagerSet) { + item = variable_item_list_add( + subghz->variable_item_list, + "RSSI Threshold:", + RAW_THRESHOLD_RSSI_COUNT, + subghz_scene_receiver_config_set_raw_threshold_rssi, + subghz); + value_index = value_index_float( + subghz->txrx->raw_threshold_rssi, raw_theshold_rssi_value, RAW_THRESHOLD_RSSI_COUNT); + variable_item_set_current_value_index(item, value_index); + variable_item_set_current_value_text(item, raw_theshold_rssi_text[value_index]); + } view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdVariableItemList); } diff --git a/applications/main/subghz/subghz.c b/applications/main/subghz/subghz.c index ba70f36d1..df5a76525 100644 --- a/applications/main/subghz/subghz.c +++ b/applications/main/subghz/subghz.c @@ -178,6 +178,7 @@ SubGhz* subghz_alloc() { subghz->txrx->txrx_state = SubGhzTxRxStateSleep; subghz->txrx->hopper_state = SubGhzHopperStateOFF; subghz->txrx->rx_key_state = SubGhzRxKeyStateIDLE; + subghz->txrx->raw_threshold_rssi = SUBGHZ_RAW_TRESHOLD_MIN; subghz->txrx->history = subghz_history_alloc(); subghz->txrx->worker = subghz_worker_alloc(); subghz->txrx->fff_data = flipper_format_string_alloc(); diff --git a/applications/main/subghz/subghz_i.h b/applications/main/subghz/subghz_i.h index 284f7ccf5..09830ba05 100644 --- a/applications/main/subghz/subghz_i.h +++ b/applications/main/subghz/subghz_i.h @@ -54,6 +54,9 @@ struct SubGhzTxRx { uint8_t hopper_timeout; uint8_t hopper_idx_frequency; SubGhzRxKeyState rx_key_state; + + float raw_threshold_rssi; + uint8_t raw_threshold_rssi_low_count; }; typedef struct SubGhzTxRx SubGhzTxRx; diff --git a/applications/main/subghz/views/subghz_read_raw.c b/applications/main/subghz/views/subghz_read_raw.c index 2d951b11a..6120a210b 100644 --- a/applications/main/subghz/views/subghz_read_raw.c +++ b/applications/main/subghz/views/subghz_read_raw.c @@ -23,10 +23,12 @@ typedef struct { FuriString* sample_write; FuriString* file_name; uint8_t* rssi_history; + uint8_t rssi_curret; bool rssi_history_end; uint8_t ind_write; uint8_t ind_sin; SubGhzReadRAWStatus status; + float raw_threshold_rssi; } SubGhzReadRAWModel; void subghz_read_raw_set_callback( @@ -54,21 +56,27 @@ void subghz_read_raw_add_data_statusbar( true); } -void subghz_read_raw_add_data_rssi(SubGhzReadRAW* instance, float rssi) { +void subghz_read_raw_add_data_rssi(SubGhzReadRAW* instance, float rssi, bool trace) { furi_assert(instance); uint8_t u_rssi = 0; - if(rssi < -90) { + if(rssi < SUBGHZ_RAW_TRESHOLD_MIN) { u_rssi = 0; } else { - u_rssi = (uint8_t)((rssi + 90) / 2.7); + u_rssi = (uint8_t)((rssi - SUBGHZ_RAW_TRESHOLD_MIN) / 2.7); } with_view_model( instance->view, SubGhzReadRAWModel * model, { - model->rssi_history[model->ind_write++] = u_rssi; + model->rssi_curret = u_rssi; + if(trace) { + model->rssi_history[model->ind_write++] = u_rssi; + } else { + model->rssi_history[model->ind_write] = u_rssi; + } + if(model->ind_write > SUBGHZ_READ_RAW_RSSI_HISTORY_SIZE) { model->rssi_history_end = true; model->ind_write = 0; @@ -187,24 +195,53 @@ void subghz_read_raw_draw_scale(Canvas* canvas, SubGhzReadRAWModel* model) { void subghz_read_raw_draw_rssi(Canvas* canvas, SubGhzReadRAWModel* model) { int ind = 0; int base = 0; + uint8_t width = 2; if(model->rssi_history_end == false) { for(int i = model->ind_write; i >= 0; i--) { canvas_draw_line(canvas, i, 47, i, 47 - model->rssi_history[i]); } + canvas_draw_line( + canvas, model->ind_write + 1, 47, model->ind_write + 1, 47 - model->rssi_curret); if(model->ind_write > 3) { - canvas_draw_line(canvas, model->ind_write, 47, model->ind_write, 13); + canvas_draw_line( + canvas, model->ind_write - 1, 47, model->ind_write - 1, 47 - model->rssi_curret); + + for(uint8_t i = 13; i < 47; i += width * 2) { + canvas_draw_line(canvas, model->ind_write, i, model->ind_write, i + width); + } canvas_draw_line(canvas, model->ind_write - 2, 12, model->ind_write + 2, 12); canvas_draw_line(canvas, model->ind_write - 1, 13, model->ind_write + 1, 13); } } else { + int i = 0; base = SUBGHZ_READ_RAW_RSSI_HISTORY_SIZE - model->ind_write; - for(int i = SUBGHZ_READ_RAW_RSSI_HISTORY_SIZE; i >= 0; i--) { + for(i = SUBGHZ_READ_RAW_RSSI_HISTORY_SIZE; i > 0; i--) { ind = i - base; if(ind < 0) ind += SUBGHZ_READ_RAW_RSSI_HISTORY_SIZE; canvas_draw_line(canvas, i, 47, i, 47 - model->rssi_history[ind]); } + canvas_draw_line( - canvas, SUBGHZ_READ_RAW_RSSI_HISTORY_SIZE, 47, SUBGHZ_READ_RAW_RSSI_HISTORY_SIZE, 13); + canvas, + SUBGHZ_READ_RAW_RSSI_HISTORY_SIZE - 1, + 47, + SUBGHZ_READ_RAW_RSSI_HISTORY_SIZE - 1, + 47 - model->rssi_curret); + canvas_draw_line( + canvas, + SUBGHZ_READ_RAW_RSSI_HISTORY_SIZE + 1, + 47, + SUBGHZ_READ_RAW_RSSI_HISTORY_SIZE + 1, + 47 - model->rssi_curret); + + for(uint8_t i = 13; i < 47; i += width * 2) { + canvas_draw_line( + canvas, + SUBGHZ_READ_RAW_RSSI_HISTORY_SIZE, + i, + SUBGHZ_READ_RAW_RSSI_HISTORY_SIZE, + i + width); + } canvas_draw_line( canvas, SUBGHZ_READ_RAW_RSSI_HISTORY_SIZE - 2, @@ -220,6 +257,24 @@ void subghz_read_raw_draw_rssi(Canvas* canvas, SubGhzReadRAWModel* model) { } } +void subghz_read_raw_draw_threshold_rssi(Canvas* canvas, SubGhzReadRAWModel* model) { + uint8_t x = 118; + uint8_t y = 48; + + if(model->raw_threshold_rssi > SUBGHZ_RAW_TRESHOLD_MIN) { + uint8_t x = 118; + y -= (uint8_t)((model->raw_threshold_rssi - SUBGHZ_RAW_TRESHOLD_MIN) / 2.7); + + uint8_t width = 3; + for(uint8_t i = 0; i < x; i += width * 2) { + canvas_draw_line(canvas, i, y, i + width, y); + } + } + canvas_draw_line(canvas, x, y - 2, x, y + 2); + canvas_draw_line(canvas, x - 1, y - 1, x - 1, y + 1); + canvas_draw_dot(canvas, x - 2, y); +} + void subghz_read_raw_draw(Canvas* canvas, SubGhzReadRAWModel* model) { uint8_t graphics_mode = 1; canvas_set_color(canvas, ColorBlack); @@ -278,8 +333,9 @@ void subghz_read_raw_draw(Canvas* canvas, SubGhzReadRAWModel* model) { } else { subghz_read_raw_draw_rssi(canvas, model); subghz_read_raw_draw_scale(canvas, model); + subghz_read_raw_draw_threshold_rssi(canvas, model); canvas_set_font_direction(canvas, CanvasDirectionBottomToTop); - canvas_draw_str(canvas, 126, 40, "RSSI"); + canvas_draw_str(canvas, 128, 40, "RSSI"); canvas_set_font_direction(canvas, CanvasDirectionLeftToRight); } } @@ -433,7 +489,8 @@ bool subghz_read_raw_input(InputEvent* event, void* context) { void subghz_read_raw_set_status( SubGhzReadRAW* instance, SubGhzReadRAWStatus status, - const char* file_name) { + const char* file_name, + float raw_threshold_rssi) { furi_assert(instance); switch(status) { @@ -447,6 +504,7 @@ void subghz_read_raw_set_status( model->ind_write = 0; furi_string_reset(model->file_name); furi_string_set(model->sample_write, "0 spl."); + model->raw_threshold_rssi = raw_threshold_rssi; }, true); break; @@ -536,6 +594,7 @@ SubGhzReadRAW* subghz_read_raw_alloc() { model->sample_write = furi_string_alloc(); model->file_name = furi_string_alloc(); model->rssi_history = malloc(SUBGHZ_READ_RAW_RSSI_HISTORY_SIZE * sizeof(uint8_t)); + model->raw_threshold_rssi = -127.0f; }, true); diff --git a/applications/main/subghz/views/subghz_read_raw.h b/applications/main/subghz/views/subghz_read_raw.h index 1d4bb7dc0..bc8711923 100644 --- a/applications/main/subghz/views/subghz_read_raw.h +++ b/applications/main/subghz/views/subghz_read_raw.h @@ -3,6 +3,8 @@ #include #include "../helpers/subghz_custom_event.h" +#define SUBGHZ_RAW_TRESHOLD_MIN -90.0f + typedef struct SubGhzReadRAW SubGhzReadRAW; typedef void (*SubGhzReadRAWCallback)(SubGhzCustomEvent event, void* context); @@ -40,11 +42,12 @@ void subghz_read_raw_stop_send(SubGhzReadRAW* instance); void subghz_read_raw_update_sin(SubGhzReadRAW* instance); -void subghz_read_raw_add_data_rssi(SubGhzReadRAW* instance, float rssi); +void subghz_read_raw_add_data_rssi(SubGhzReadRAW* instance, float rssi, bool trace); void subghz_read_raw_set_status( SubGhzReadRAW* instance, SubGhzReadRAWStatus status, - const char* file_name); + const char* file_name, + float raw_threshold_rssi); View* subghz_read_raw_get_view(SubGhzReadRAW* subghz_static); diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index 17a8a675a..8bad0b830 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -2310,6 +2310,7 @@ Function,+,subghz_protocol_raw_file_encoder_worker_set_callback_end,void,"SubGhz Function,+,subghz_protocol_raw_gen_fff_data,void,"FlipperFormat*, const char*" Function,+,subghz_protocol_raw_get_sample_write,size_t,SubGhzProtocolDecoderRAW* Function,+,subghz_protocol_raw_save_to_file_init,_Bool,"SubGhzProtocolDecoderRAW*, const char*, SubGhzRadioPreset*" +Function,+,subghz_protocol_raw_save_to_file_pause,void,"SubGhzProtocolDecoderRAW*, _Bool" Function,+,subghz_protocol_raw_save_to_file_stop,void,SubGhzProtocolDecoderRAW* Function,+,subghz_receiver_alloc_init,SubGhzReceiver*,SubGhzEnvironment* Function,+,subghz_receiver_decode,void,"SubGhzReceiver*, _Bool, uint32_t" diff --git a/lib/subghz/protocols/raw.c b/lib/subghz/protocols/raw.c index b8e93c3d5..b639c93b9 100644 --- a/lib/subghz/protocols/raw.c +++ b/lib/subghz/protocols/raw.c @@ -32,6 +32,7 @@ struct SubGhzProtocolDecoderRAW { FuriString* file_name; size_t sample_write; bool last_level; + bool pause; }; struct SubGhzProtocolEncoderRAW { @@ -158,6 +159,7 @@ bool subghz_protocol_raw_save_to_file_init( instance->upload_raw = malloc(SUBGHZ_DOWNLOAD_MAX_SIZE * sizeof(int32_t)); instance->file_is_open = RAWFileIsOpenWrite; instance->sample_write = 0; + instance->pause = false; init = true; } while(0); @@ -199,6 +201,14 @@ void subghz_protocol_raw_save_to_file_stop(SubGhzProtocolDecoderRAW* instance) { instance->file_is_open = RAWFileIsOpenClose; } +void subghz_protocol_raw_save_to_file_pause(SubGhzProtocolDecoderRAW* instance, bool pause) { + furi_assert(instance); + + if(instance->pause != pause) { + instance->pause = pause; + } +} + size_t subghz_protocol_raw_get_sample_write(SubGhzProtocolDecoderRAW* instance) { return instance->sample_write + instance->ind_write; } @@ -234,7 +244,7 @@ void subghz_protocol_decoder_raw_feed(void* context, bool level, uint32_t durati furi_assert(context); SubGhzProtocolDecoderRAW* instance = context; - if(instance->upload_raw != NULL) { + if(!instance->pause && (instance->upload_raw != NULL)) { if(duration > subghz_protocol_raw_const.te_short) { if(instance->last_level != level) { instance->last_level = (level ? true : false); diff --git a/lib/subghz/protocols/raw.h b/lib/subghz/protocols/raw.h index 96c77553a..44c7faec5 100644 --- a/lib/subghz/protocols/raw.h +++ b/lib/subghz/protocols/raw.h @@ -103,6 +103,13 @@ void subghz_protocol_encoder_raw_free(void* context); */ void subghz_protocol_encoder_raw_stop(void* context); +/** + * pause writing to flash. + * @param context Pointer to a SubGhzProtocolEncoderRAW instance + * @param pause pause writing + */ +void subghz_protocol_raw_save_to_file_pause(SubGhzProtocolDecoderRAW* instance, bool pause); + /** * Set callback on completion of file transfer. * @param instance Pointer to a SubGhzProtocolEncoderRAW instance From a8edb41eae75c7796e4f16f31b0fa8c5d6440e01 Mon Sep 17 00:00:00 2001 From: hedger Date: Wed, 26 Oct 2022 19:18:06 +0400 Subject: [PATCH 08/31] fbt, docs: typo fixes; vscode: fixed deprecated target names (#1926) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: あく --- .vscode/example/tasks.json | 4 ++-- documentation/fbt.md | 2 +- firmware.scons | 3 ++- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/.vscode/example/tasks.json b/.vscode/example/tasks.json index 3946d05cc..9baaf97b4 100644 --- a/.vscode/example/tasks.json +++ b/.vscode/example/tasks.json @@ -109,13 +109,13 @@ "label": "[Debug] Build FAPs", "group": "build", "type": "shell", - "command": "./fbt plugin_dist" + "command": "./fbt fap_dist" }, { "label": "[Release] Build FAPs", "group": "build", "type": "shell", - "command": "./fbt COMPACT=1 DEBUG=0 plugin_dist" + "command": "./fbt COMPACT=1 DEBUG=0 fap_dist" }, { "label": "[Debug] Launch App on Flipper", diff --git a/documentation/fbt.md b/documentation/fbt.md index 659170519..2bf9ea28e 100644 --- a/documentation/fbt.md +++ b/documentation/fbt.md @@ -54,7 +54,7 @@ To run cleanup (think of `make clean`) for specified targets, add `-c` option. - `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 -- `get_stlink` - output serial numbers for attached STLink probes. Ued for `OPENOCD_ADAPTER_SERIAL=...`. +- `get_stlink` - output serial numbers for attached STLink probes. Used for specifying an adapter with `OPENOCD_ADAPTER_SERIAL=...`. - `lint`, `format` - run clang-format 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 - `cli` - start Flipper CLI session over USB diff --git a/firmware.scons b/firmware.scons index d0b89a763..d501996b3 100644 --- a/firmware.scons +++ b/firmware.scons @@ -292,9 +292,10 @@ Alias(fwenv["FIRMWARE_BUILD_CFG"] + "_all", fw_artifacts) if fwenv["IS_BASE_FIRMWARE"]: sdk_source = fwenv.SDKPrebuilder( "sdk_origin", + # Deps on root SDK headers and generated files (fwenv["SDK_HEADERS"], fwenv["FW_ASSETS_HEADERS"]), ) - # Extra deps for root headers and generated files + # Extra deps on headers included in deeper levels Depends(sdk_source, fwenv.ProcessSdkDepends("sdk_origin.d")) fwenv["SDK_DIR"] = fwenv.Dir("sdk") From ebaa84b0c27548dd4df1a9584fe22ee797a68331 Mon Sep 17 00:00:00 2001 From: "Vasyl \"vk\" Kaigorodov" Date: Wed, 26 Oct 2022 20:35:49 +0200 Subject: [PATCH 09/31] Support for setting all screen orientations (#1928) * Support for setting all screen orientations * Gui: add flipped orientation to view * Gui: correct assert conditions in gui_add_view_port Co-authored-by: Aleksandr Kutuzov --- applications/services/gui/canvas.c | 35 ++++++++-- applications/services/gui/canvas.h | 2 + applications/services/gui/gui.c | 4 +- applications/services/gui/view.h | 2 + applications/services/gui/view_dispatcher.c | 10 ++- applications/services/gui/view_port.c | 74 ++++++++++++++++++--- applications/services/gui/view_port.h | 2 + 7 files changed, 112 insertions(+), 17 deletions(-) diff --git a/applications/services/gui/canvas.c b/applications/services/gui/canvas.c index 1e275aafb..a2979d56b 100644 --- a/applications/services/gui/canvas.c +++ b/applications/services/gui/canvas.c @@ -371,16 +371,39 @@ void canvas_set_bitmap_mode(Canvas* canvas, bool alpha) { void canvas_set_orientation(Canvas* canvas, CanvasOrientation orientation) { furi_assert(canvas); if(canvas->orientation != orientation) { - canvas->orientation = orientation; - if(canvas->orientation == CanvasOrientationHorizontal) { - FURI_SWAP(canvas->width, canvas->height); + switch(orientation) { + case CanvasOrientationHorizontal: + if(canvas->orientation == CanvasOrientationVertical || + canvas->orientation == CanvasOrientationVerticalFlip) { + FURI_SWAP(canvas->width, canvas->height); + } u8g2_SetDisplayRotation(&canvas->fb, U8G2_R0); - } else if(canvas->orientation == CanvasOrientationVertical) { - FURI_SWAP(canvas->width, canvas->height); + break; + case CanvasOrientationHorizontalFlip: + if(canvas->orientation == CanvasOrientationVertical || + canvas->orientation == CanvasOrientationVerticalFlip) { + FURI_SWAP(canvas->width, canvas->height); + } + u8g2_SetDisplayRotation(&canvas->fb, U8G2_R2); + break; + case CanvasOrientationVertical: + if(canvas->orientation == CanvasOrientationHorizontal || + canvas->orientation == CanvasOrientationHorizontalFlip) { + FURI_SWAP(canvas->width, canvas->height); + }; u8g2_SetDisplayRotation(&canvas->fb, U8G2_R3); - } else { + break; + case CanvasOrientationVerticalFlip: + if(canvas->orientation == CanvasOrientationHorizontal || + canvas->orientation == CanvasOrientationHorizontalFlip) { + FURI_SWAP(canvas->width, canvas->height); + } + u8g2_SetDisplayRotation(&canvas->fb, U8G2_R1); + break; + default: furi_assert(0); } + canvas->orientation = orientation; } } diff --git a/applications/services/gui/canvas.h b/applications/services/gui/canvas.h index 49bbd7d68..a67e58494 100644 --- a/applications/services/gui/canvas.h +++ b/applications/services/gui/canvas.h @@ -42,7 +42,9 @@ typedef enum { /** Canvas Orientation */ typedef enum { CanvasOrientationHorizontal, + CanvasOrientationHorizontalFlip, CanvasOrientationVertical, + CanvasOrientationVerticalFlip, } CanvasOrientation; /** Font Direction */ diff --git a/applications/services/gui/gui.c b/applications/services/gui/gui.c index 8c5ed91a9..42712ed90 100644 --- a/applications/services/gui/gui.c +++ b/applications/services/gui/gui.c @@ -322,7 +322,9 @@ void gui_add_view_port(Gui* gui, ViewPort* view_port, GuiLayer layer) { furi_check(layer < GuiLayerMAX); // Only fullscreen supports Vertical orientation for now furi_assert( - (layer == GuiLayerFullscreen) || (view_port->orientation != ViewPortOrientationVertical)); + (layer == GuiLayerFullscreen) || + ((view_port->orientation != ViewPortOrientationVertical) && + (view_port->orientation != ViewPortOrientationVerticalFlip))); gui_lock(gui); // Verify that view port is not yet added diff --git a/applications/services/gui/view.h b/applications/services/gui/view.h index b40f8ded5..7a2003a63 100644 --- a/applications/services/gui/view.h +++ b/applications/services/gui/view.h @@ -25,7 +25,9 @@ extern "C" { typedef enum { ViewOrientationHorizontal, + ViewOrientationHorizontalFlip, ViewOrientationVertical, + ViewOrientationVerticalFlip, } ViewOrientation; /** View, anonymous type */ diff --git a/applications/services/gui/view_dispatcher.c b/applications/services/gui/view_dispatcher.c index 6e4ce8360..1736558cb 100644 --- a/applications/services/gui/view_dispatcher.c +++ b/applications/services/gui/view_dispatcher.c @@ -331,10 +331,16 @@ void view_dispatcher_set_current_view(ViewDispatcher* view_dispatcher, View* vie view_dispatcher->current_view = view; // Dispatch view enter event if(view_dispatcher->current_view) { - if(view->orientation == ViewOrientationVertical) + if(view->orientation == ViewOrientationVertical) { view_port_set_orientation(view_dispatcher->view_port, ViewPortOrientationVertical); - else if(view->orientation == ViewOrientationHorizontal) + } else if(view->orientation == ViewOrientationVerticalFlip) { + view_port_set_orientation(view_dispatcher->view_port, ViewPortOrientationVerticalFlip); + } else if(view->orientation == ViewOrientationHorizontal) { view_port_set_orientation(view_dispatcher->view_port, ViewPortOrientationHorizontal); + } else if(view->orientation == ViewOrientationHorizontalFlip) { + view_port_set_orientation( + view_dispatcher->view_port, ViewPortOrientationHorizontalFlip); + } view_enter(view_dispatcher->current_view); view_port_enabled_set(view_dispatcher->view_port, true); view_port_update(view_dispatcher->view_port); diff --git a/applications/services/gui/view_port.c b/applications/services/gui/view_port.c index 8069a02e5..baa8f7bd2 100644 --- a/applications/services/gui/view_port.c +++ b/applications/services/gui/view_port.c @@ -7,7 +7,7 @@ // TODO add mutex to view_port ops -static void view_port_rotate_buttons(InputEvent* event) { +static void view_port_remap_buttons_vertical(InputEvent* event) { switch(event->key) { case InputKeyUp: event->key = InputKeyRight; @@ -26,14 +26,61 @@ static void view_port_rotate_buttons(InputEvent* event) { } } -static void view_port_setup_canvas_orientation(const ViewPort* view_port, Canvas* canvas) { - if(view_port->orientation == ViewPortOrientationHorizontal) { - canvas_set_orientation(canvas, CanvasOrientationHorizontal); - } else if(view_port->orientation == ViewPortOrientationVertical) { - canvas_set_orientation(canvas, CanvasOrientationVertical); +static void view_port_remap_buttons_vertical_flip(InputEvent* event) { + switch(event->key) { + case InputKeyUp: + event->key = InputKeyLeft; + break; + case InputKeyDown: + event->key = InputKeyRight; + break; + case InputKeyRight: + event->key = InputKeyUp; + break; + case InputKeyLeft: + event->key = InputKeyDown; + break; + default: + break; } } +static void view_port_remap_buttons_horizontal_flip(InputEvent* event) { + switch(event->key) { + case InputKeyUp: + event->key = InputKeyDown; + break; + case InputKeyDown: + event->key = InputKeyUp; + break; + case InputKeyRight: + event->key = InputKeyLeft; + break; + case InputKeyLeft: + event->key = InputKeyRight; + break; + default: + break; + } +} + +static void view_port_setup_canvas_orientation(const ViewPort* view_port, Canvas* canvas) { + switch(view_port->orientation) { + case ViewPortOrientationHorizontalFlip: + canvas_set_orientation(canvas, CanvasOrientationHorizontalFlip); + break; + case ViewPortOrientationVertical: + canvas_set_orientation(canvas, CanvasOrientationVertical); + break; + case ViewPortOrientationVerticalFlip: + canvas_set_orientation(canvas, CanvasOrientationVerticalFlip); + break; + default: + canvas_set_orientation(canvas, CanvasOrientationHorizontal); + break; + }; +} + ViewPort* view_port_alloc() { ViewPort* view_port = malloc(sizeof(ViewPort)); view_port->orientation = ViewPortOrientationHorizontal; @@ -122,8 +169,19 @@ void view_port_input(ViewPort* view_port, InputEvent* event) { furi_check(view_port->gui); if(view_port->input_callback) { - if(view_port_get_orientation(view_port) == ViewPortOrientationVertical) { - view_port_rotate_buttons(event); + ViewPortOrientation orientation = view_port_get_orientation(view_port); + switch(orientation) { + case ViewPortOrientationHorizontalFlip: + view_port_remap_buttons_horizontal_flip(event); + break; + case ViewPortOrientationVertical: + view_port_remap_buttons_vertical(event); + break; + case ViewPortOrientationVerticalFlip: + view_port_remap_buttons_vertical_flip(event); + break; + default: + break; } view_port->input_callback(event, view_port->input_callback_context); } diff --git a/applications/services/gui/view_port.h b/applications/services/gui/view_port.h index 96f2798e2..169681ac0 100644 --- a/applications/services/gui/view_port.h +++ b/applications/services/gui/view_port.h @@ -16,7 +16,9 @@ typedef struct ViewPort ViewPort; typedef enum { ViewPortOrientationHorizontal, + ViewPortOrientationHorizontalFlip, ViewPortOrientationVertical, + ViewPortOrientationVerticalFlip, } ViewPortOrientation; /** ViewPort Draw callback From aff99a72e84aac45b7bc6281d3072218b83f5498 Mon Sep 17 00:00:00 2001 From: Skorpionm <85568270+Skorpionm@users.noreply.github.com> Date: Wed, 26 Oct 2022 23:40:13 +0400 Subject: [PATCH 10/31] SubGhz: fix variable types and CC1101 GPIO initialization optimization (#1931) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * SubGhz: fix variable types * SubGhz: CC1101 GPIO initialization optimization * SubGhz: return back gpio init * SubGhz: cleanup grammar in math and format doxygen comments Co-authored-by: あく --- applications/main/subghz/subghz_i.c | 2 +- firmware/targets/f7/api_symbols.csv | 25 +-- lib/subghz/blocks/decoder.c | 10 + lib/subghz/blocks/decoder.h | 11 ++ lib/subghz/blocks/math.c | 115 ++++++------ lib/subghz/blocks/math.h | 272 +++++++++++++++------------- 6 files changed, 235 insertions(+), 200 deletions(-) diff --git a/applications/main/subghz/subghz_i.c b/applications/main/subghz/subghz_i.c index ac1036d05..beefd8024 100644 --- a/applications/main/subghz/subghz_i.c +++ b/applications/main/subghz/subghz_i.c @@ -102,8 +102,8 @@ static bool subghz_tx(SubGhz* subghz, uint32_t frequency) { furi_assert(subghz->txrx->txrx_state != SubGhzTxRxStateSleep); furi_hal_subghz_idle(); furi_hal_subghz_set_frequency_and_path(frequency); + furi_hal_gpio_write(&gpio_cc1101_g0, false); furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); - furi_hal_gpio_write(&gpio_cc1101_g0, true); bool ret = furi_hal_subghz_tx(); subghz->txrx->txrx_state = SubGhzTxRxStateTx; return ret; diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index 8bad0b830..fd11dffe0 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,5.1,, +Version,+,6.0,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -2272,21 +2272,22 @@ Function,-,subghz_keystore_raw_get_data,_Bool,"const char*, size_t, uint8_t*, si Function,-,subghz_keystore_save,_Bool,"SubGhzKeystore*, const char*, uint8_t*" Function,+,subghz_protocol_blocks_add_bit,void,"SubGhzBlockDecoder*, uint8_t" Function,+,subghz_protocol_blocks_add_bytes,uint8_t,"const uint8_t[], size_t" -Function,+,subghz_protocol_blocks_crc16,uint16_t,"const uint8_t[], unsigned, uint16_t, uint16_t" -Function,+,subghz_protocol_blocks_crc16lsb,uint16_t,"const uint8_t[], unsigned, uint16_t, uint16_t" -Function,+,subghz_protocol_blocks_crc4,uint8_t,"const uint8_t[], unsigned, uint8_t, uint8_t" -Function,+,subghz_protocol_blocks_crc7,uint8_t,"const uint8_t[], unsigned, uint8_t, uint8_t" -Function,+,subghz_protocol_blocks_crc8,uint8_t,"const uint8_t[], unsigned, uint8_t, uint8_t" -Function,+,subghz_protocol_blocks_crc8le,uint8_t,"const uint8_t[], unsigned, uint8_t, uint8_t" +Function,+,subghz_protocol_blocks_add_to_128_bit,void,"SubGhzBlockDecoder*, uint8_t, uint64_t*" +Function,+,subghz_protocol_blocks_crc16,uint16_t,"const uint8_t[], size_t, uint16_t, uint16_t" +Function,+,subghz_protocol_blocks_crc16lsb,uint16_t,"const uint8_t[], size_t, uint16_t, uint16_t" +Function,+,subghz_protocol_blocks_crc4,uint8_t,"const uint8_t[], size_t, uint8_t, uint8_t" +Function,+,subghz_protocol_blocks_crc7,uint8_t,"const uint8_t[], size_t, uint8_t, uint8_t" +Function,+,subghz_protocol_blocks_crc8,uint8_t,"const uint8_t[], size_t, uint8_t, uint8_t" +Function,+,subghz_protocol_blocks_crc8le,uint8_t,"const uint8_t[], size_t, uint8_t, uint8_t" Function,+,subghz_protocol_blocks_get_bit_array,_Bool,"uint8_t[], size_t" Function,+,subghz_protocol_blocks_get_hash_data,uint8_t,"SubGhzBlockDecoder*, size_t" Function,+,subghz_protocol_blocks_get_parity,uint8_t,"uint64_t, uint8_t" Function,+,subghz_protocol_blocks_get_upload,size_t,"uint8_t[], size_t, LevelDuration*, size_t, uint32_t" -Function,+,subghz_protocol_blocks_lfsr_digest16,uint16_t,"const uint8_t[], unsigned, uint16_t, uint16_t" -Function,+,subghz_protocol_blocks_lfsr_digest8,uint8_t,"const uint8_t[], unsigned, uint8_t, uint8_t" -Function,+,subghz_protocol_blocks_lfsr_digest8_reflect,uint8_t,"const uint8_t[], int, uint8_t, uint8_t" -Function,+,subghz_protocol_blocks_parity8,int,uint8_t -Function,+,subghz_protocol_blocks_parity_bytes,int,"const uint8_t[], size_t" +Function,+,subghz_protocol_blocks_lfsr_digest16,uint16_t,"const uint8_t[], size_t, uint16_t, uint16_t" +Function,+,subghz_protocol_blocks_lfsr_digest8,uint8_t,"const uint8_t[], size_t, uint8_t, uint8_t" +Function,+,subghz_protocol_blocks_lfsr_digest8_reflect,uint8_t,"const uint8_t[], size_t, uint8_t, uint8_t" +Function,+,subghz_protocol_blocks_parity8,uint8_t,uint8_t +Function,+,subghz_protocol_blocks_parity_bytes,uint8_t,"const uint8_t[], size_t" Function,+,subghz_protocol_blocks_reverse_key,uint64_t,"uint64_t, uint8_t" Function,+,subghz_protocol_blocks_set_bit_array,void,"_Bool, uint8_t[], size_t, size_t" Function,+,subghz_protocol_blocks_xor_bytes,uint8_t,"const uint8_t[], size_t" diff --git a/lib/subghz/blocks/decoder.c b/lib/subghz/blocks/decoder.c index d2237f6c4..f491c87bf 100644 --- a/lib/subghz/blocks/decoder.c +++ b/lib/subghz/blocks/decoder.c @@ -7,6 +7,16 @@ void subghz_protocol_blocks_add_bit(SubGhzBlockDecoder* decoder, uint8_t bit) { decoder->decode_count_bit++; } +void subghz_protocol_blocks_add_to_128_bit( + SubGhzBlockDecoder* decoder, + uint8_t bit, + uint64_t* head_64_bit) { + if(++decoder->decode_count_bit > 64) { + (*head_64_bit) = ((*head_64_bit) << 1) | (decoder->decode_data >> 63); + } + decoder->decode_data = decoder->decode_data << 1 | bit; +} + uint8_t subghz_protocol_blocks_get_hash_data(SubGhzBlockDecoder* decoder, size_t len) { uint8_t hash = 0; uint8_t* p = (uint8_t*)&decoder->decode_data; diff --git a/lib/subghz/blocks/decoder.h b/lib/subghz/blocks/decoder.h index 25549fab3..a5e561e35 100644 --- a/lib/subghz/blocks/decoder.h +++ b/lib/subghz/blocks/decoder.h @@ -24,6 +24,17 @@ struct SubGhzBlockDecoder { */ void subghz_protocol_blocks_add_bit(SubGhzBlockDecoder* decoder, uint8_t bit); +/** + * Add data to_128 bit when decoding. + * @param decoder Pointer to a SubGhzBlockDecoder instance + * @param head_64_bit Pointer to a head_64_bit + * @param bit data, 1bit + */ +void subghz_protocol_blocks_add_to_128_bit( + SubGhzBlockDecoder* decoder, + uint8_t bit, + uint64_t* head_64_bit); + /** * Getting the hash sum of the last randomly received parcel. * @param decoder Pointer to a SubGhzBlockDecoder instance diff --git a/lib/subghz/blocks/math.c b/lib/subghz/blocks/math.c index d2b8e3d11..24202ad1c 100644 --- a/lib/subghz/blocks/math.c +++ b/lib/subghz/blocks/math.c @@ -1,16 +1,16 @@ #include "math.h" -uint64_t subghz_protocol_blocks_reverse_key(uint64_t key, uint8_t count_bit) { - uint64_t key_reverse = 0; - for(uint8_t i = 0; i < count_bit; i++) { - key_reverse = key_reverse << 1 | bit_read(key, i); +uint64_t subghz_protocol_blocks_reverse_key(uint64_t key, uint8_t bit_count) { + uint64_t reverse_key = 0; + for(uint8_t i = 0; i < bit_count; i++) { + reverse_key = reverse_key << 1 | bit_read(key, i); } - return key_reverse; + return reverse_key; } -uint8_t subghz_protocol_blocks_get_parity(uint64_t key, uint8_t count_bit) { +uint8_t subghz_protocol_blocks_get_parity(uint64_t key, uint8_t bit_count) { uint8_t parity = 0; - for(uint8_t i = 0; i < count_bit; i++) { + for(uint8_t i = 0; i < bit_count; i++) { parity += bit_read(key, i); } return parity & 0x01; @@ -18,14 +18,14 @@ uint8_t subghz_protocol_blocks_get_parity(uint64_t key, uint8_t count_bit) { uint8_t subghz_protocol_blocks_crc4( uint8_t const message[], - unsigned nBytes, + size_t size, uint8_t polynomial, uint8_t init) { - unsigned remainder = init << 4; // LSBs are unused - unsigned poly = polynomial << 4; - unsigned bit; + uint8_t remainder = init << 4; // LSBs are unused + uint8_t poly = polynomial << 4; + uint8_t bit; - while(nBytes--) { + while(size--) { remainder ^= *message++; for(bit = 0; bit < 8; bit++) { if(remainder & 0x80) { @@ -40,16 +40,15 @@ uint8_t subghz_protocol_blocks_crc4( uint8_t subghz_protocol_blocks_crc7( uint8_t const message[], - unsigned nBytes, + size_t size, uint8_t polynomial, uint8_t init) { - unsigned remainder = init << 1; // LSB is unused - unsigned poly = polynomial << 1; - unsigned byte, bit; + uint8_t remainder = init << 1; // LSB is unused + uint8_t poly = polynomial << 1; - for(byte = 0; byte < nBytes; ++byte) { + for(size_t byte = 0; byte < size; ++byte) { remainder ^= message[byte]; - for(bit = 0; bit < 8; ++bit) { + for(uint8_t bit = 0; bit < 8; ++bit) { if(remainder & 0x80) { remainder = (remainder << 1) ^ poly; } else { @@ -62,15 +61,14 @@ uint8_t subghz_protocol_blocks_crc7( uint8_t subghz_protocol_blocks_crc8( uint8_t const message[], - unsigned nBytes, + size_t size, uint8_t polynomial, uint8_t init) { uint8_t remainder = init; - unsigned byte, bit; - for(byte = 0; byte < nBytes; ++byte) { + for(size_t byte = 0; byte < size; ++byte) { remainder ^= message[byte]; - for(bit = 0; bit < 8; ++bit) { + for(uint8_t bit = 0; bit < 8; ++bit) { if(remainder & 0x80) { remainder = (remainder << 1) ^ polynomial; } else { @@ -83,16 +81,15 @@ uint8_t subghz_protocol_blocks_crc8( uint8_t subghz_protocol_blocks_crc8le( uint8_t const message[], - unsigned nBytes, + size_t size, uint8_t polynomial, uint8_t init) { uint8_t remainder = subghz_protocol_blocks_reverse_key(init, 8); - unsigned byte, bit; polynomial = subghz_protocol_blocks_reverse_key(polynomial, 8); - for(byte = 0; byte < nBytes; ++byte) { + for(size_t byte = 0; byte < size; ++byte) { remainder ^= message[byte]; - for(bit = 0; bit < 8; ++bit) { + for(uint8_t bit = 0; bit < 8; ++bit) { if(remainder & 1) { remainder = (remainder >> 1) ^ polynomial; } else { @@ -105,15 +102,14 @@ uint8_t subghz_protocol_blocks_crc8le( uint16_t subghz_protocol_blocks_crc16lsb( uint8_t const message[], - unsigned nBytes, + size_t size, uint16_t polynomial, uint16_t init) { uint16_t remainder = init; - unsigned byte, bit; - for(byte = 0; byte < nBytes; ++byte) { + for(size_t byte = 0; byte < size; ++byte) { remainder ^= message[byte]; - for(bit = 0; bit < 8; ++bit) { + for(uint8_t bit = 0; bit < 8; ++bit) { if(remainder & 1) { remainder = (remainder >> 1) ^ polynomial; } else { @@ -126,15 +122,14 @@ uint16_t subghz_protocol_blocks_crc16lsb( uint16_t subghz_protocol_blocks_crc16( uint8_t const message[], - unsigned nBytes, + size_t size, uint16_t polynomial, uint16_t init) { uint16_t remainder = init; - unsigned byte, bit; - for(byte = 0; byte < nBytes; ++byte) { + for(size_t byte = 0; byte < size; ++byte) { remainder ^= message[byte] << 8; - for(bit = 0; bit < 8; ++bit) { + for(uint8_t bit = 0; bit < 8; ++bit) { if(remainder & 0x8000) { remainder = (remainder << 1) ^ polynomial; } else { @@ -147,18 +142,18 @@ uint16_t subghz_protocol_blocks_crc16( uint8_t subghz_protocol_blocks_lfsr_digest8( uint8_t const message[], - unsigned bytes, + size_t size, uint8_t gen, uint8_t key) { uint8_t sum = 0; - for(unsigned k = 0; k < bytes; ++k) { - uint8_t data = message[k]; + for(size_t byte = 0; byte < size; ++byte) { + uint8_t data = message[byte]; for(int i = 7; i >= 0; --i) { // XOR key into sum if data bit is set if((data >> i) & 1) sum ^= key; - // roll the key right (actually the lsb is dropped here) - // and apply the gen (needs to include the dropped lsb as msb) + // roll the key right (actually the LSB is dropped here) + // and apply the gen (needs to include the dropped LSB as MSB) if(key & 1) key = (key >> 1) ^ gen; else @@ -170,22 +165,22 @@ uint8_t subghz_protocol_blocks_lfsr_digest8( uint8_t subghz_protocol_blocks_lfsr_digest8_reflect( uint8_t const message[], - int bytes, + size_t size, uint8_t gen, uint8_t key) { uint8_t sum = 0; // Process message from last byte to first byte (reflected) - for(int k = bytes - 1; k >= 0; --k) { - uint8_t data = message[k]; + for(int byte = size - 1; byte >= 0; --byte) { + uint8_t data = message[byte]; // Process individual bits of each byte (reflected) - for(int i = 0; i < 8; ++i) { + for(uint8_t i = 0; i < 8; ++i) { // XOR key into sum if data bit is set if((data >> i) & 1) { sum ^= key; } - // roll the key left (actually the lsb is dropped here) - // and apply the gen (needs to include the dropped lsb as msb) + // roll the key left (actually the LSB is dropped here) + // and apply the gen (needs to include the dropped lsb as MSB) if(key & 0x80) key = (key << 1) ^ gen; else @@ -197,18 +192,18 @@ uint8_t subghz_protocol_blocks_lfsr_digest8_reflect( uint16_t subghz_protocol_blocks_lfsr_digest16( uint8_t const message[], - unsigned bytes, + size_t size, uint16_t gen, uint16_t key) { uint16_t sum = 0; - for(unsigned k = 0; k < bytes; ++k) { - uint8_t data = message[k]; - for(int i = 7; i >= 0; --i) { + for(size_t byte = 0; byte < size; ++byte) { + uint8_t data = message[byte]; + for(int8_t i = 7; i >= 0; --i) { // if data bit is set then xor with key if((data >> i) & 1) sum ^= key; - // roll the key right (actually the lsb is dropped here) - // and apply the gen (needs to include the dropped lsb as msb) + // roll the key right (actually the LSB is dropped here) + // and apply the gen (needs to include the dropped LSB as MSB) if(key & 1) key = (key >> 1) ^ gen; else @@ -218,31 +213,31 @@ uint16_t subghz_protocol_blocks_lfsr_digest16( return sum; } -uint8_t subghz_protocol_blocks_add_bytes(uint8_t const message[], size_t num_bytes) { - int result = 0; - for(size_t i = 0; i < num_bytes; ++i) { +uint8_t subghz_protocol_blocks_add_bytes(uint8_t const message[], size_t size) { + uint32_t result = 0; + for(size_t i = 0; i < size; ++i) { result += message[i]; } return (uint8_t)result; } -int subghz_protocol_blocks_parity8(uint8_t byte) { +uint8_t subghz_protocol_blocks_parity8(uint8_t byte) { byte ^= byte >> 4; byte &= 0xf; return (0x6996 >> byte) & 1; } -int subghz_protocol_blocks_parity_bytes(uint8_t const message[], size_t num_bytes) { - int result = 0; - for(size_t i = 0; i < num_bytes; ++i) { +uint8_t subghz_protocol_blocks_parity_bytes(uint8_t const message[], size_t size) { + uint8_t result = 0; + for(size_t i = 0; i < size; ++i) { result ^= subghz_protocol_blocks_parity8(message[i]); } return result; } -uint8_t subghz_protocol_blocks_xor_bytes(uint8_t const message[], size_t num_bytes) { +uint8_t subghz_protocol_blocks_xor_bytes(uint8_t const message[], size_t size) { uint8_t result = 0; - for(size_t i = 0; i < num_bytes; ++i) { + for(size_t i = 0; i < size; ++i) { result ^= message[i]; } return result; diff --git a/lib/subghz/blocks/math.h b/lib/subghz/blocks/math.h index 8cddf4c0b..a4f04271a 100644 --- a/lib/subghz/blocks/math.h +++ b/lib/subghz/blocks/math.h @@ -14,183 +14,201 @@ #ifdef __cplusplus extern "C" { #endif -/** - * Flip the data bitwise. - * @param key In data - * @param count_bit number of data bits - * @return Reverse data - **/ -uint64_t subghz_protocol_blocks_reverse_key(uint64_t key, uint8_t count_bit); -/** - * Get parity the data bitwise. - * @param key In data - * @param count_bit number of data bits - * @return parity - **/ -uint8_t subghz_protocol_blocks_get_parity(uint64_t key, uint8_t count_bit); +/** Flip the data bitwise + * + * @param key In data + * @param bit_count number of data bits + * + * @return Reverse data + */ +uint64_t subghz_protocol_blocks_reverse_key(uint64_t key, uint8_t bit_count); -/** - * CRC-4. - * @param message array of bytes to check - * @param nBytes number of bytes in message - * @param polynomial CRC polynomial - * @param init starting crc value - * @return CRC value - **/ +/** Get parity the data bitwise + * + * @param key In data + * @param bit_count number of data bits + * + * @return parity + */ +uint8_t subghz_protocol_blocks_get_parity(uint64_t key, uint8_t bit_count); + +/** CRC-4 + * + * @param message array of bytes to check + * @param size number of bytes in message + * @param polynomial CRC polynomial + * @param init starting crc value + * + * @return CRC value + */ uint8_t subghz_protocol_blocks_crc4( uint8_t const message[], - unsigned nBytes, + size_t size, uint8_t polynomial, uint8_t init); -/** - * CRC-7. - * @param message array of bytes to check - * @param nBytes number of bytes in message - * @param polynomial CRC polynomial - * @param init starting crc value - * @return CRC value - **/ +/** CRC-7 + * + * @param message array of bytes to check + * @param size number of bytes in message + * @param polynomial CRC polynomial + * @param init starting crc value + * + * @return CRC value + */ uint8_t subghz_protocol_blocks_crc7( uint8_t const message[], - unsigned nBytes, + size_t size, uint8_t polynomial, uint8_t init); -/** - * Generic Cyclic Redundancy Check CRC-8. - * Example polynomial: 0x31 = x8 + x5 + x4 + 1 (x8 is implicit) - * Example polynomial: 0x80 = x8 + x7 (a normal bit-by-bit parity XOR) - * @param message array of bytes to check - * @param nBytes number of bytes in message - * @param polynomial byte is from x^7 to x^0 (x^8 is implicitly one) - * @param init starting crc value - * @return CRC value - **/ +/** Generic Cyclic Redundancy Check CRC-8. Example polynomial: 0x31 = x8 + x5 + + * x4 + 1 (x8 is implicit) Example polynomial: 0x80 = x8 + x7 (a normal + * bit-by-bit parity XOR) + * + * @param message array of bytes to check + * @param size number of bytes in message + * @param polynomial byte is from x^7 to x^0 (x^8 is implicitly one) + * @param init starting crc value + * + * @return CRC value + */ uint8_t subghz_protocol_blocks_crc8( uint8_t const message[], - unsigned nBytes, + size_t size, uint8_t polynomial, uint8_t init); -/** - * "Little-endian" Cyclic Redundancy Check CRC-8 LE - * Input and output are reflected, i.e. least significant bit is shifted in first. - * @param message array of bytes to check - * @param nBytes number of bytes in message - * @param polynomial CRC polynomial - * @param init starting crc value - * @return CRC value - **/ +/** "Little-endian" Cyclic Redundancy Check CRC-8 LE Input and output are + * reflected, i.e. least significant bit is shifted in first + * + * @param message array of bytes to check + * @param size number of bytes in message + * @param polynomial CRC polynomial + * @param init starting crc value + * + * @return CRC value + */ uint8_t subghz_protocol_blocks_crc8le( uint8_t const message[], - unsigned nBytes, + size_t size, uint8_t polynomial, uint8_t init); -/** - * CRC-16 LSB. - * Input and output are reflected, i.e. least significant bit is shifted in first. - * Note that poly and init already need to be reflected. - * @param message array of bytes to check - * @param nBytes number of bytes in message - * @param polynomial CRC polynomial - * @param init starting crc value - * @return CRC value - **/ +/** CRC-16 LSB. Input and output are reflected, i.e. least significant bit is + * shifted in first. Note that poly and init already need to be reflected + * + * @param message array of bytes to check + * @param size number of bytes in message + * @param polynomial CRC polynomial + * @param init starting crc value + * + * @return CRC value + */ uint16_t subghz_protocol_blocks_crc16lsb( uint8_t const message[], - unsigned nBytes, + size_t size, uint16_t polynomial, uint16_t init); -/** - * CRC-16. - * @param message array of bytes to check - * @param nBytes number of bytes in message - * @param polynomial CRC polynomial - * @param init starting crc value - * @return CRC value - **/ +/** CRC-16 + * + * @param message array of bytes to check + * @param size number of bytes in message + * @param polynomial CRC polynomial + * @param init starting crc value + * + * @return CRC value + */ uint16_t subghz_protocol_blocks_crc16( uint8_t const message[], - unsigned nBytes, + size_t size, uint16_t polynomial, uint16_t init); -/** - * Digest-8 by "LFSR-based Toeplitz hash". - * @param message bytes of message data - * @param bytes number of bytes to digest - * @param gen key stream generator, needs to includes the MSB if the LFSR is rolling - * @param key initial key - * @return digest value - **/ +/** Digest-8 by "LFSR-based Toeplitz hash" + * + * @param message bytes of message data + * @param size number of bytes to digest + * @param gen key stream generator, needs to includes the MSB if the + * LFSR is rolling + * @param key initial key + * + * @return digest value + */ uint8_t subghz_protocol_blocks_lfsr_digest8( uint8_t const message[], - unsigned bytes, + size_t size, uint8_t gen, uint8_t key); -/** - * Digest-8 by "LFSR-based Toeplitz hash", byte reflect, bit reflect. - * @param message bytes of message data - * @param bytes number of bytes to digest - * @param gen key stream generator, needs to includes the MSB if the LFSR is rolling - * @param key initial key - * @return digest value - **/ +/** Digest-8 by "LFSR-based Toeplitz hash", byte reflect, bit reflect + * + * @param message bytes of message data + * @param size number of bytes to digest + * @param gen key stream generator, needs to includes the MSB if the + * LFSR is rolling + * @param key initial key + * + * @return digest value + */ uint8_t subghz_protocol_blocks_lfsr_digest8_reflect( uint8_t const message[], - int bytes, + size_t size, uint8_t gen, uint8_t key); -/** - * Digest-16 by "LFSR-based Toeplitz hash". - * @param message bytes of message data - * @param bytes number of bytes to digest - * @param gen key stream generator, needs to includes the MSB if the LFSR is rolling - * @param key initial key - * @return digest value - **/ +/** Digest-16 by "LFSR-based Toeplitz hash" + * + * @param message bytes of message data + * @param size number of bytes to digest + * @param gen key stream generator, needs to includes the MSB if the + * LFSR is rolling + * @param key initial key + * + * @return digest value + */ uint16_t subghz_protocol_blocks_lfsr_digest16( uint8_t const message[], - unsigned bytes, + size_t size, uint16_t gen, uint16_t key); -/** - * Compute Addition of a number of bytes. - * @param message bytes of message data - * @param num_bytes number of bytes to sum - * @return summation value - **/ -uint8_t subghz_protocol_blocks_add_bytes(uint8_t const message[], size_t num_bytes); +/** Compute Addition of a number of bytes + * + * @param message bytes of message data + * @param size number of bytes to sum + * + * @return summation value + */ +uint8_t subghz_protocol_blocks_add_bytes(uint8_t const message[], size_t size); -/** - * Compute bit parity of a single byte (8 bits). - * @param byte single byte to check - * @return 1 odd parity, 0 even parity - **/ -int subghz_protocol_blocks_parity8(uint8_t byte); +/** Compute bit parity of a single byte (8 bits) + * + * @param byte single byte to check + * + * @return 1 odd parity, 0 even parity + */ +uint8_t subghz_protocol_blocks_parity8(uint8_t byte); -/** - * Compute bit parity of a number of bytes. - * @param message bytes of message data - * @param num_bytes number of bytes to sum - * @return 1 odd parity, 0 even parity - **/ -int subghz_protocol_blocks_parity_bytes(uint8_t const message[], size_t num_bytes); +/** Compute bit parity of a number of bytes + * + * @param message bytes of message data + * @param size number of bytes to sum + * + * @return 1 odd parity, 0 even parity + */ +uint8_t subghz_protocol_blocks_parity_bytes(uint8_t const message[], size_t size); -/** - * Compute XOR (byte-wide parity) of a number of bytes. - * @param message bytes of message data - * @param num_bytes number of bytes to sum - * @return summation value, per bit-position 1 odd parity, 0 even parity - **/ -uint8_t subghz_protocol_blocks_xor_bytes(uint8_t const message[], size_t num_bytes); +/** Compute XOR (byte-wide parity) of a number of bytes + * + * @param message bytes of message data + * @param size number of bytes to sum + * + * @return summation value, per bit-position 1 odd parity, 0 even parity + */ +uint8_t subghz_protocol_blocks_xor_bytes(uint8_t const message[], size_t size); #ifdef __cplusplus } From 8b7a52b97b6b537b02608517ad3d12c6c170ece5 Mon Sep 17 00:00:00 2001 From: hedger Date: Thu, 27 Oct 2022 00:25:31 +0400 Subject: [PATCH 11/31] fbt: fixed linter paths (#1930) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fbt: fixed linter paths * lint: changed file permissions Co-authored-by: あく --- .../targets/f7/ble_glue/dev_info_service.c | 0 .../targets/f7/furi_hal/furi_hal_cortex.c | 3 +- .../targets/furi_hal_include/furi_hal_nfc.h | 125 +++++++++++------- furi/SConscript | 7 +- furi/core/timer.c | 2 +- furi/flipper.c | 0 lib/lfrfid/SConscript | 2 +- lib/lfrfid/protocols/protocol_awid.c | 2 +- 8 files changed, 85 insertions(+), 56 deletions(-) mode change 100755 => 100644 firmware/targets/f7/ble_glue/dev_info_service.c mode change 100755 => 100644 furi/flipper.c diff --git a/firmware/targets/f7/ble_glue/dev_info_service.c b/firmware/targets/f7/ble_glue/dev_info_service.c old mode 100755 new mode 100644 diff --git a/firmware/targets/f7/furi_hal/furi_hal_cortex.c b/firmware/targets/f7/furi_hal/furi_hal_cortex.c index c2abd1b85..192b83ee4 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_cortex.c +++ b/firmware/targets/f7/furi_hal/furi_hal_cortex.c @@ -36,5 +36,6 @@ bool furi_hal_cortex_timer_is_expired(FuriHalCortexTimer cortex_timer) { } void furi_hal_cortex_timer_wait(FuriHalCortexTimer cortex_timer) { - while(!furi_hal_cortex_timer_is_expired(cortex_timer)); + while(!furi_hal_cortex_timer_is_expired(cortex_timer)) + ; } \ No newline at end of file diff --git a/firmware/targets/furi_hal_include/furi_hal_nfc.h b/firmware/targets/furi_hal_include/furi_hal_nfc.h index 537e0abf0..90d968fea 100644 --- a/firmware/targets/furi_hal_include/furi_hal_nfc.h +++ b/firmware/targets/furi_hal_include/furi_hal_nfc.h @@ -111,7 +111,7 @@ bool furi_hal_nfc_is_busy(); * * @return true if initialized */ - bool furi_hal_nfc_is_init(); +bool furi_hal_nfc_is_init(); /** NFC field on */ @@ -234,7 +234,6 @@ void furi_hal_nfc_sleep(); void furi_hal_nfc_stop(); - /* Low level transport API, use it to implement your own transport layers */ #define furi_hal_nfc_ll_ms2fc rfalConvMsTo1fc @@ -250,7 +249,8 @@ typedef enum { FuriHalNfcReturnBusy = 2, /*!< device or resource busy */ FuriHalNfcReturnIo = 3, /*!< generic IO error */ FuriHalNfcReturnTimeout = 4, /*!< error due to timeout */ - FuriHalNfcReturnRequest = 5, /*!< invalid request or requested function can't be executed at the moment */ + FuriHalNfcReturnRequest = + 5, /*!< invalid request or requested function can't be executed at the moment */ FuriHalNfcReturnNomsg = 6, /*!< No message of desired type */ FuriHalNfcReturnParam = 7, /*!< Parameter error */ FuriHalNfcReturnSystem = 8, /*!< System error */ @@ -261,20 +261,23 @@ typedef enum { FuriHalNfcReturnAgain = 13, /*!< Call again */ FuriHalNfcReturnMemCorrupt = 14, /*!< memory corruption */ FuriHalNfcReturnNotImplemented = 15, /*!< not implemented */ - FuriHalNfcReturnPcCorrupt = 16, /*!< Program Counter has been manipulated or spike/noise trigger illegal operation */ + FuriHalNfcReturnPcCorrupt = + 16, /*!< Program Counter has been manipulated or spike/noise trigger illegal operation */ FuriHalNfcReturnSend = 17, /*!< error sending*/ FuriHalNfcReturnIgnore = 18, /*!< indicates error detected but to be ignored */ FuriHalNfcReturnSemantic = 19, /*!< indicates error in state machine (unexpected cmd) */ FuriHalNfcReturnSyntax = 20, /*!< indicates error in state machine (unknown cmd) */ FuriHalNfcReturnCrc = 21, /*!< crc error */ FuriHalNfcReturnNotfound = 22, /*!< transponder not found */ - FuriHalNfcReturnNotunique = 23, /*!< transponder not unique - more than one transponder in field */ + FuriHalNfcReturnNotunique = + 23, /*!< transponder not unique - more than one transponder in field */ FuriHalNfcReturnNotsupp = 24, /*!< requested operation not supported */ FuriHalNfcReturnWrite = 25, /*!< write error */ FuriHalNfcReturnFifo = 26, /*!< fifo over or underflow error */ FuriHalNfcReturnPar = 27, /*!< parity error */ FuriHalNfcReturnDone = 28, /*!< transfer has already finished */ - FuriHalNfcReturnRfCollision = 29, /*!< collision error (Bit Collision or during RF Collision avoidance ) */ + FuriHalNfcReturnRfCollision = + 29, /*!< collision error (Bit Collision or during RF Collision avoidance ) */ FuriHalNfcReturnHwOverrun = 30, /*!< lost one or more received bytes */ FuriHalNfcReturnReleaseReq = 31, /*!< device requested release */ FuriHalNfcReturnSleepReq = 32, /*!< device requested sleep */ @@ -282,7 +285,8 @@ typedef enum { FuriHalNfcReturnMaxReruns = 34, /*!< blocking procedure reached maximum runs */ FuriHalNfcReturnDisabled = 35, /*!< operation aborted due to disabled configuration */ FuriHalNfcReturnHwMismatch = 36, /*!< expected hw do not match */ - FuriHalNfcReturnLinkLoss = 37, /*!< Other device's field didn't behave as expected: turned off by Initiator in Passive mode, or AP2P did not turn on field */ + FuriHalNfcReturnLinkLoss = + 37, /*!< Other device's field didn't behave as expected: turned off by Initiator in Passive mode, or AP2P did not turn on field */ FuriHalNfcReturnInvalidHandle = 38, /*!< invalid or not initalized device handle */ FuriHalNfcReturnIncompleteByte = 40, /*!< Incomplete byte rcvd */ FuriHalNfcReturnIncompleteByte01 = 41, /*!< Incomplete byte rcvd - 1 bit */ @@ -295,38 +299,40 @@ typedef enum { } FuriHalNfcReturn; typedef enum { - FuriHalNfcModeNone = 0, /*!< No mode selected/defined */ - FuriHalNfcModePollNfca = 1, /*!< Mode to perform as NFCA (ISO14443A) Poller (PCD) */ - FuriHalNfcModePollNfcaT1t = 2, /*!< Mode to perform as NFCA T1T (Topaz) Poller (PCD) */ - FuriHalNfcModePollNfcb = 3, /*!< Mode to perform as NFCB (ISO14443B) Poller (PCD) */ - FuriHalNfcModePollBPrime = 4, /*!< Mode to perform as B' Calypso (Innovatron) (PCD) */ - FuriHalNfcModePollBCts = 5, /*!< Mode to perform as CTS Poller (PCD) */ - FuriHalNfcModePollNfcf = 6, /*!< Mode to perform as NFCF (FeliCa) Poller (PCD) */ - FuriHalNfcModePollNfcv = 7, /*!< Mode to perform as NFCV (ISO15963) Poller (PCD) */ - FuriHalNfcModePollPicopass = 8, /*!< Mode to perform as PicoPass / iClass Poller (PCD) */ - FuriHalNfcModePollActiveP2p = 9, /*!< Mode to perform as Active P2P (ISO18092) Initiator */ - FuriHalNfcModeListenNfca = 10, /*!< Mode to perform as NFCA (ISO14443A) Listener (PICC) */ - FuriHalNfcModeListenNfcb = 11, /*!< Mode to perform as NFCA (ISO14443B) Listener (PICC) */ - FuriHalNfcModeListenNfcf = 12, /*!< Mode to perform as NFCA (ISO15963) Listener (PICC) */ - FuriHalNfcModeListenActiveP2p = 13 /*!< Mode to perform as Active P2P (ISO18092) Target */ + FuriHalNfcModeNone = 0, /*!< No mode selected/defined */ + FuriHalNfcModePollNfca = 1, /*!< Mode to perform as NFCA (ISO14443A) Poller (PCD) */ + FuriHalNfcModePollNfcaT1t = 2, /*!< Mode to perform as NFCA T1T (Topaz) Poller (PCD) */ + FuriHalNfcModePollNfcb = 3, /*!< Mode to perform as NFCB (ISO14443B) Poller (PCD) */ + FuriHalNfcModePollBPrime = 4, /*!< Mode to perform as B' Calypso (Innovatron) (PCD) */ + FuriHalNfcModePollBCts = 5, /*!< Mode to perform as CTS Poller (PCD) */ + FuriHalNfcModePollNfcf = 6, /*!< Mode to perform as NFCF (FeliCa) Poller (PCD) */ + FuriHalNfcModePollNfcv = 7, /*!< Mode to perform as NFCV (ISO15963) Poller (PCD) */ + FuriHalNfcModePollPicopass = 8, /*!< Mode to perform as PicoPass / iClass Poller (PCD) */ + FuriHalNfcModePollActiveP2p = 9, /*!< Mode to perform as Active P2P (ISO18092) Initiator */ + FuriHalNfcModeListenNfca = 10, /*!< Mode to perform as NFCA (ISO14443A) Listener (PICC) */ + FuriHalNfcModeListenNfcb = 11, /*!< Mode to perform as NFCA (ISO14443B) Listener (PICC) */ + FuriHalNfcModeListenNfcf = 12, /*!< Mode to perform as NFCA (ISO15963) Listener (PICC) */ + FuriHalNfcModeListenActiveP2p = 13 /*!< Mode to perform as Active P2P (ISO18092) Target */ } FuriHalNfcMode; typedef enum { - FuriHalNfcBitrate106 = 0, /*!< Bit Rate 106 kbit/s (fc/128) */ - FuriHalNfcBitrate212 = 1, /*!< Bit Rate 212 kbit/s (fc/64) */ - FuriHalNfcBitrate424 = 2, /*!< Bit Rate 424 kbit/s (fc/32) */ - FuriHalNfcBitrate848 = 3, /*!< Bit Rate 848 kbit/s (fc/16) */ - FuriHalNfcBitrate1695 = 4, /*!< Bit Rate 1695 kbit/s (fc/8) */ - FuriHalNfcBitrate3390 = 5, /*!< Bit Rate 3390 kbit/s (fc/4) */ - FuriHalNfcBitrate6780 = 6, /*!< Bit Rate 6780 kbit/s (fc/2) */ - FuriHalNfcBitrate13560 = 7, /*!< Bit Rate 13560 kbit/s (fc) */ - FuriHalNfcBitrate52p97 = 0xEB, /*!< Bit Rate 52.97 kbit/s (fc/256) Fast Mode VICC->VCD */ - FuriHalNfcBitrate26p48 = 0xEC, /*!< Bit Rate 26,48 kbit/s (fc/512) NFCV VICC->VCD & VCD->VICC 1of4 */ - FuriHalNfcBitrate1p66 = 0xED, /*!< Bit Rate 1,66 kbit/s (fc/8192) NFCV VCD->VICC 1of256 */ - FuriHalNfcBitrateKeep = 0xFF /*!< Value indicating to keep the same previous bit rate */ + FuriHalNfcBitrate106 = 0, /*!< Bit Rate 106 kbit/s (fc/128) */ + FuriHalNfcBitrate212 = 1, /*!< Bit Rate 212 kbit/s (fc/64) */ + FuriHalNfcBitrate424 = 2, /*!< Bit Rate 424 kbit/s (fc/32) */ + FuriHalNfcBitrate848 = 3, /*!< Bit Rate 848 kbit/s (fc/16) */ + FuriHalNfcBitrate1695 = 4, /*!< Bit Rate 1695 kbit/s (fc/8) */ + FuriHalNfcBitrate3390 = 5, /*!< Bit Rate 3390 kbit/s (fc/4) */ + FuriHalNfcBitrate6780 = 6, /*!< Bit Rate 6780 kbit/s (fc/2) */ + FuriHalNfcBitrate13560 = 7, /*!< Bit Rate 13560 kbit/s (fc) */ + FuriHalNfcBitrate52p97 = 0xEB, /*!< Bit Rate 52.97 kbit/s (fc/256) Fast Mode VICC->VCD */ + FuriHalNfcBitrate26p48 = + 0xEC, /*!< Bit Rate 26,48 kbit/s (fc/512) NFCV VICC->VCD & VCD->VICC 1of4 */ + FuriHalNfcBitrate1p66 = 0xED, /*!< Bit Rate 1,66 kbit/s (fc/8192) NFCV VCD->VICC 1of256 */ + FuriHalNfcBitrateKeep = 0xFF /*!< Value indicating to keep the same previous bit rate */ } FuriHalNfcBitrate; -FuriHalNfcReturn furi_hal_nfc_ll_set_mode(FuriHalNfcMode mode, FuriHalNfcBitrate txBR, FuriHalNfcBitrate rxBR); +FuriHalNfcReturn + furi_hal_nfc_ll_set_mode(FuriHalNfcMode mode, FuriHalNfcBitrate txBR, FuriHalNfcBitrate rxBR); #define FURI_HAL_NFC_LL_GT_NFCA furi_hal_nfc_ll_ms2fc(5U) /*!< GTA Digital 2.0 6.10.4.1 & B.2 */ #define FURI_HAL_NFC_LL_GT_NFCB furi_hal_nfc_ll_ms2fc(5U) /*!< GTB Digital 2.0 7.9.4.1 & B.3 */ @@ -334,40 +340,57 @@ FuriHalNfcReturn furi_hal_nfc_ll_set_mode(FuriHalNfcMode mode, FuriHalNfcBitrate #define FURI_HAL_NFC_LL_GT_NFCV furi_hal_nfc_ll_ms2fc(5U) /*!< GTV Digital 2.0 9.7.5.1 & B.5 */ #define FURI_HAL_NFC_LL_GT_PICOPASS furi_hal_nfc_ll_ms2fc(1U) /*!< GT Picopass */ #define FURI_HAL_NFC_LL_GT_AP2P furi_hal_nfc_ll_ms2fc(5U) /*!< TIRFG Ecma 340 11.1.1 */ -#define FURI_HAL_NFC_LL_GT_AP2P_ADJUSTED furi_hal_nfc_ll_ms2fc(5U + 25U) /*!< Adjusted GT for greater interoperability (Sony XPERIA P, Nokia N9, Huawei P2) */ +#define FURI_HAL_NFC_LL_GT_AP2P_ADJUSTED \ + furi_hal_nfc_ll_ms2fc( \ + 5U + \ + 25U) /*!< Adjusted GT for greater interoperability (Sony XPERIA P, Nokia N9, Huawei P2) */ void furi_hal_nfc_ll_set_guard_time(uint32_t cycles); typedef enum { - FuriHalNfcErrorHandlingNone = 0, /*!< No special error handling will be performed */ - FuriHalNfcErrorHandlingNfc = 1, /*!< Error handling set to perform as NFC compliant device */ - FuriHalNfcErrorHandlingEmvco = 2 /*!< Error handling set to perform as EMVCo compliant device */ + FuriHalNfcErrorHandlingNone = 0, /*!< No special error handling will be performed */ + FuriHalNfcErrorHandlingNfc = 1, /*!< Error handling set to perform as NFC compliant device */ + FuriHalNfcErrorHandlingEmvco = + 2 /*!< Error handling set to perform as EMVCo compliant device */ } FuriHalNfcErrorHandling; void furi_hal_nfc_ll_set_error_handling(FuriHalNfcErrorHandling eHandling); /* RFAL Frame Delay Time (FDT) Listen default values */ -#define FURI_HAL_NFC_LL_FDT_LISTEN_NFCA_POLLER 1172U /*!< FDTA,LISTEN,MIN (n=9) Last bit: Logic "1" - tnn,min/2 Digital 1.1 6.10 ; EMV CCP Spec Book D v2.01 4.8.1.3 */ -#define FURI_HAL_NFC_LL_FDT_LISTEN_NFCB_POLLER 1008U /*!< TR0B,MIN Digital 1.1 7.1.3 & A.3 ; EMV CCP Spec Book D v2.01 4.8.1.3 & Table A.5 */ -#define FURI_HAL_NFC_LL_FDT_LISTEN_NFCF_POLLER 2672U /*!< TR0F,LISTEN,MIN Digital 1.1 8.7.1.1 & A.4 */ -#define FURI_HAL_NFC_LL_FDT_LISTEN_NFCV_POLLER 4310U /*!< FDTV,LISTEN,MIN t1 min Digital 2.1 B.5 ; ISO15693-3 2009 9.1 */ -#define FURI_HAL_NFC_LL_FDT_LISTEN_PICOPASS_POLLER 3400U /*!< ISO15693 t1 min - observed adjustment */ -#define FURI_HAL_NFC_LL_FDT_LISTEN_AP2P_POLLER 64U /*!< FDT AP2P No actual FDTListen is required as fields switch and collision avoidance */ +#define FURI_HAL_NFC_LL_FDT_LISTEN_NFCA_POLLER \ + 1172U /*!< FDTA,LISTEN,MIN (n=9) Last bit: Logic "1" - tnn,min/2 Digital 1.1 6.10 ; EMV CCP Spec Book D v2.01 4.8.1.3 */ +#define FURI_HAL_NFC_LL_FDT_LISTEN_NFCB_POLLER \ + 1008U /*!< TR0B,MIN Digital 1.1 7.1.3 & A.3 ; EMV CCP Spec Book D v2.01 4.8.1.3 & Table A.5 */ +#define FURI_HAL_NFC_LL_FDT_LISTEN_NFCF_POLLER \ + 2672U /*!< TR0F,LISTEN,MIN Digital 1.1 8.7.1.1 & A.4 */ +#define FURI_HAL_NFC_LL_FDT_LISTEN_NFCV_POLLER \ + 4310U /*!< FDTV,LISTEN,MIN t1 min Digital 2.1 B.5 ; ISO15693-3 2009 9.1 */ +#define FURI_HAL_NFC_LL_FDT_LISTEN_PICOPASS_POLLER \ + 3400U /*!< ISO15693 t1 min - observed adjustment */ +#define FURI_HAL_NFC_LL_FDT_LISTEN_AP2P_POLLER \ + 64U /*!< FDT AP2P No actual FDTListen is required as fields switch and collision avoidance */ #define FURI_HAL_NFC_LL_FDT_LISTEN_NFCA_LISTENER 1172U /*!< FDTA,LISTEN,MIN Digital 1.1 6.10 */ -#define FURI_HAL_NFC_LL_FDT_LISTEN_NFCB_LISTENER 1024U /*!< TR0B,MIN Digital 1.1 7.1.3 & A.3 ; EMV CCP Spec Book D v2.01 4.8.1.3 & Table A.5 */ -#define FURI_HAL_NFC_LL_FDT_LISTEN_NFCF_LISTENER 2688U /*!< TR0F,LISTEN,MIN Digital 2.1 8.7.1.1 & B.4 */ -#define FURI_HAL_NFC_LL_FDT_LISTEN_AP2P_LISTENER 64U /*!< FDT AP2P No actual FDTListen exists as fields switch and collision avoidance */ +#define FURI_HAL_NFC_LL_FDT_LISTEN_NFCB_LISTENER \ + 1024U /*!< TR0B,MIN Digital 1.1 7.1.3 & A.3 ; EMV CCP Spec Book D v2.01 4.8.1.3 & Table A.5 */ +#define FURI_HAL_NFC_LL_FDT_LISTEN_NFCF_LISTENER \ + 2688U /*!< TR0F,LISTEN,MIN Digital 2.1 8.7.1.1 & B.4 */ +#define FURI_HAL_NFC_LL_FDT_LISTEN_AP2P_LISTENER \ + 64U /*!< FDT AP2P No actual FDTListen exists as fields switch and collision avoidance */ void furi_hal_nfc_ll_set_fdt_listen(uint32_t cycles); /* RFAL Frame Delay Time (FDT) Poll default values */ -#define FURI_HAL_NFC_LL_FDT_POLL_NFCA_POLLER 6780U /*!< FDTA,POLL,MIN Digital 1.1 6.10.3.1 & A.2 */ -#define FURI_HAL_NFC_LL_FDT_POLL_NFCA_T1T_POLLER 384U /*!< RRDDT1T,MIN,B1 Digital 1.1 10.7.1 & A.5 */ -#define FURI_HAL_NFC_LL_FDT_POLL_NFCB_POLLER 6780U /*!< FDTB,POLL,MIN = TR2B,MIN,DEFAULT Digital 1.1 7.9.3 & A.3 ; EMVCo 3.0 FDTB,PCD,MIN Table A.5 */ +#define FURI_HAL_NFC_LL_FDT_POLL_NFCA_POLLER \ + 6780U /*!< FDTA,POLL,MIN Digital 1.1 6.10.3.1 & A.2 */ +#define FURI_HAL_NFC_LL_FDT_POLL_NFCA_T1T_POLLER \ + 384U /*!< RRDDT1T,MIN,B1 Digital 1.1 10.7.1 & A.5 */ +#define FURI_HAL_NFC_LL_FDT_POLL_NFCB_POLLER \ + 6780U /*!< FDTB,POLL,MIN = TR2B,MIN,DEFAULT Digital 1.1 7.9.3 & A.3 ; EMVCo 3.0 FDTB,PCD,MIN Table A.5 */ #define FURI_HAL_NFC_LL_FDT_POLL_NFCF_POLLER 6800U /*!< FDTF,POLL,MIN Digital 2.1 8.7.3 & B.4 */ #define FURI_HAL_NFC_LL_FDT_POLL_NFCV_POLLER 4192U /*!< FDTV,POLL Digital 2.1 9.7.3.1 & B.5 */ #define FURI_HAL_NFC_LL_FDT_POLL_PICOPASS_POLLER 1790U /*!< FDT Max */ -#define FURI_HAL_NFC_LL_FDT_POLL_AP2P_POLLER 0U /*!< FDT AP2P No actual FDTPoll exists as fields switch and collision avoidance */ +#define FURI_HAL_NFC_LL_FDT_POLL_AP2P_POLLER \ + 0U /*!< FDT AP2P No actual FDTPoll exists as fields switch and collision avoidance */ void furi_hal_nfc_ll_set_fdt_poll(uint32_t FDTPoll); diff --git a/furi/SConscript b/furi/SConscript index a751eb6e1..f95ef13f9 100644 --- a/furi/SConscript +++ b/furi/SConscript @@ -1,6 +1,11 @@ Import("env") -env.Append(LINT_SOURCES=["furi"]) +env.Append( + LINT_SOURCES=[ + "furi", + "furi/core", + ] +) libenv = env.Clone(FW_LIB_NAME="furi") diff --git a/furi/core/timer.c b/furi/core/timer.c index 462a2e89e..c42b0c2ac 100644 --- a/furi/core/timer.c +++ b/furi/core/timer.c @@ -86,7 +86,7 @@ void furi_timer_free(FuriTimer* instance) { furi_check(xTimerDelete(hTimer, portMAX_DELAY) == pdPASS); - while (furi_timer_is_running(instance)) furi_delay_tick(2); + while(furi_timer_is_running(instance)) furi_delay_tick(2); if((uint32_t)callb & 1U) { /* Callback memory was allocated from dynamic pool, clear flag */ diff --git a/furi/flipper.c b/furi/flipper.c old mode 100755 new mode 100644 diff --git a/lib/lfrfid/SConscript b/lib/lfrfid/SConscript index fd29ca2ef..6177a9a50 100644 --- a/lib/lfrfid/SConscript +++ b/lib/lfrfid/SConscript @@ -2,7 +2,7 @@ Import("env") env.Append( LINT_SOURCES=[ - "#/lib/lfrfid", + "lib/lfrfid", ], CPPPATH=[ "#/lib/lfrfid", diff --git a/lib/lfrfid/protocols/protocol_awid.c b/lib/lfrfid/protocols/protocol_awid.c index 38c7793b8..939627723 100644 --- a/lib/lfrfid/protocols/protocol_awid.c +++ b/lib/lfrfid/protocols/protocol_awid.c @@ -207,7 +207,7 @@ bool protocol_awid_write_data(ProtocolAwid* protocol, void* data) { // Fix incorrect length byte if(protocol->data[0] != 26 && protocol->data[0] != 50 && protocol->data[0] != 37 && - protocol->data[0] != 34 && protocol->data[0] != 36 ) { + protocol->data[0] != 34 && protocol->data[0] != 36) { protocol->data[0] = 26; } From f11df494683ecadd20fe760ec27d2e77115aee90 Mon Sep 17 00:00:00 2001 From: Georgii Surkov <37121527+gsurkov@users.noreply.github.com> Date: Fri, 28 Oct 2022 10:18:41 +0300 Subject: [PATCH 12/31] [FL-2828] Dolphin score update take 2 (#1929) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Move DolphinDeedNfcRead * Move DolphinDeedNfcReadSuccess * Move DolphinDeedNfcSave * Move DolphinDeedNfcDetectReader * Move DolphinDeedNfcEmulate * Count DolphinDeedNfcEmulate when launched from file browser * Implement most of the score accounting for NFC * Fully update Nfc icounter handling * Move DolphinDeedSubGhzFrequencyAnalyzer * Update the rest of icounter in SubGHz * Adjust SubGHz icounter handling * Adjust LFRFID icounter handling * Adjust Infrared icounter handling * Don't count renaming RFID tags as saving * Don't count renaming SubGHz signals as saving * Don't count renaming NFC tags as saving * Adjust iButton icounter handling * Minor code refactoring * Correct formatting * Account for emulating iButton keys from file manager/rpc Co-authored-by: あく --- applications/main/ibutton/ibutton.c | 3 +++ .../main/ibutton/scenes/ibutton_scene_add_value.c | 3 --- .../main/ibutton/scenes/ibutton_scene_emulate.c | 3 --- .../main/ibutton/scenes/ibutton_scene_read.c | 3 +-- .../ibutton/scenes/ibutton_scene_read_key_menu.c | 2 ++ .../main/ibutton/scenes/ibutton_scene_save_name.c | 10 ++++++++++ .../main/ibutton/scenes/ibutton_scene_save_success.c | 2 -- .../ibutton/scenes/ibutton_scene_saved_key_menu.c | 2 ++ .../main/ibutton/scenes/ibutton_scene_start.c | 2 ++ .../main/infrared/scenes/infrared_scene_learn.c | 2 ++ .../main/infrared/scenes/infrared_scene_learn_done.c | 3 --- .../scenes/infrared_scene_learn_enter_name.c | 2 ++ .../infrared/scenes/infrared_scene_learn_success.c | 3 --- applications/main/lfrfid/lfrfid.c | 5 ++++- .../main/lfrfid/scenes/lfrfid_scene_emulate.c | 3 --- .../main/lfrfid/scenes/lfrfid_scene_extra_actions.c | 3 +++ applications/main/lfrfid/scenes/lfrfid_scene_read.c | 3 +-- .../main/lfrfid/scenes/lfrfid_scene_read_key_menu.c | 2 ++ .../main/lfrfid/scenes/lfrfid_scene_save_data.c | 2 -- .../main/lfrfid/scenes/lfrfid_scene_save_name.c | 8 ++++++++ .../main/lfrfid/scenes/lfrfid_scene_save_success.c | 2 -- .../main/lfrfid/scenes/lfrfid_scene_saved_key_menu.c | 2 ++ applications/main/lfrfid/scenes/lfrfid_scene_start.c | 2 ++ applications/main/nfc/nfc.c | 4 ++++ .../main/nfc/scenes/nfc_scene_detect_reader.c | 2 -- applications/main/nfc/scenes/nfc_scene_emulate_uid.c | 2 -- .../main/nfc/scenes/nfc_scene_emv_read_success.c | 2 -- .../nfc/scenes/nfc_scene_mf_classic_dict_attack.c | 4 ++++ .../main/nfc/scenes/nfc_scene_mf_classic_emulate.c | 2 -- .../main/nfc/scenes/nfc_scene_mf_classic_keys_add.c | 2 ++ .../main/nfc/scenes/nfc_scene_mf_classic_menu.c | 7 +++++-- .../nfc/scenes/nfc_scene_mf_classic_read_success.c | 3 --- .../main/nfc/scenes/nfc_scene_mf_desfire_menu.c | 6 ++++++ .../nfc/scenes/nfc_scene_mf_ultralight_emulate.c | 2 -- .../main/nfc/scenes/nfc_scene_mf_ultralight_menu.c | 6 ++++++ .../nfc/scenes/nfc_scene_mf_ultralight_read_auth.c | 2 -- .../nfc_scene_mf_ultralight_read_auth_result.c | 4 ---- .../scenes/nfc_scene_mf_ultralight_read_success.c | 2 -- .../nfc/scenes/nfc_scene_mf_ultralight_unlock_warn.c | 2 ++ applications/main/nfc/scenes/nfc_scene_nfca_menu.c | 6 ++++++ .../main/nfc/scenes/nfc_scene_nfca_read_success.c | 3 --- applications/main/nfc/scenes/nfc_scene_read.c | 7 ++++++- .../main/nfc/scenes/nfc_scene_read_card_success.c | 2 -- applications/main/nfc/scenes/nfc_scene_save_name.c | 8 ++++++++ .../main/nfc/scenes/nfc_scene_save_success.c | 2 -- applications/main/nfc/scenes/nfc_scene_saved_menu.c | 2 ++ applications/main/nfc/scenes/nfc_scene_set_uid.c | 2 -- applications/main/nfc/scenes/nfc_scene_start.c | 3 +++ .../subghz/scenes/subghz_scene_frequency_analyzer.c | 2 -- .../main/subghz/scenes/subghz_scene_read_raw.c | 7 ++++++- .../main/subghz/scenes/subghz_scene_receiver.c | 2 ++ .../main/subghz/scenes/subghz_scene_receiver_info.c | 2 -- .../main/subghz/scenes/subghz_scene_save_name.c | 12 ++++++++++++ .../main/subghz/scenes/subghz_scene_save_success.c | 3 --- .../main/subghz/scenes/subghz_scene_set_type.c | 2 -- applications/main/subghz/scenes/subghz_scene_start.c | 2 ++ .../main/subghz/scenes/subghz_scene_transmitter.c | 2 +- applications/services/dolphin/helpers/dolphin_deed.c | 2 +- applications/services/dolphin/helpers/dolphin_deed.h | 2 +- 59 files changed, 125 insertions(+), 72 deletions(-) diff --git a/applications/main/ibutton/ibutton.c b/applications/main/ibutton/ibutton.c index 887fb3e75..b6d8361b3 100644 --- a/applications/main/ibutton/ibutton.c +++ b/applications/main/ibutton/ibutton.c @@ -5,6 +5,7 @@ #include #include #include +#include #define TAG "iButtonApp" @@ -337,11 +338,13 @@ int32_t ibutton_app(void* p) { view_dispatcher_attach_to_gui( ibutton->view_dispatcher, ibutton->gui, ViewDispatcherTypeDesktop); scene_manager_next_scene(ibutton->scene_manager, iButtonSceneRpc); + DOLPHIN_DEED(DolphinDeedIbuttonEmulate); } else { view_dispatcher_attach_to_gui( ibutton->view_dispatcher, ibutton->gui, ViewDispatcherTypeFullscreen); if(key_loaded) { scene_manager_next_scene(ibutton->scene_manager, iButtonSceneEmulate); + DOLPHIN_DEED(DolphinDeedIbuttonEmulate); } else { scene_manager_next_scene(ibutton->scene_manager, iButtonSceneStart); } diff --git a/applications/main/ibutton/scenes/ibutton_scene_add_value.c b/applications/main/ibutton/scenes/ibutton_scene_add_value.c index b3ec11a50..ccac76121 100644 --- a/applications/main/ibutton/scenes/ibutton_scene_add_value.c +++ b/applications/main/ibutton/scenes/ibutton_scene_add_value.c @@ -1,7 +1,5 @@ #include "../ibutton_i.h" -#include - void ibutton_scene_add_type_byte_input_callback(void* context) { iButton* ibutton = context; view_dispatcher_send_custom_event(ibutton->view_dispatcher, iButtonCustomEventByteEditResult); @@ -38,7 +36,6 @@ bool ibutton_scene_add_value_on_event(void* context, SceneManagerEvent event) { consumed = true; if(event.event == iButtonCustomEventByteEditResult) { ibutton_key_set_data(ibutton->key, new_key_data, IBUTTON_KEY_DATA_SIZE); - DOLPHIN_DEED(DolphinDeedIbuttonAdd); scene_manager_next_scene(ibutton->scene_manager, iButtonSceneSaveName); } } diff --git a/applications/main/ibutton/scenes/ibutton_scene_emulate.c b/applications/main/ibutton/scenes/ibutton_scene_emulate.c index b3bc38ead..6f6ffcf57 100644 --- a/applications/main/ibutton/scenes/ibutton_scene_emulate.c +++ b/applications/main/ibutton/scenes/ibutton_scene_emulate.c @@ -1,6 +1,5 @@ #include "../ibutton_i.h" #include -#include #include #define EMULATE_TIMEOUT_TICKS 10 @@ -26,8 +25,6 @@ void ibutton_scene_emulate_on_enter(void* context) { path_extract_filename(ibutton->file_path, key_name, true); } - DOLPHIN_DEED(DolphinDeedIbuttonEmulate); - // check that stored key has name if(!furi_string_empty(key_name)) { ibutton_text_store_set(ibutton, "%s", furi_string_get_cstr(key_name)); diff --git a/applications/main/ibutton/scenes/ibutton_scene_read.c b/applications/main/ibutton/scenes/ibutton_scene_read.c index 05920a0ad..1fe75e45a 100644 --- a/applications/main/ibutton/scenes/ibutton_scene_read.c +++ b/applications/main/ibutton/scenes/ibutton_scene_read.c @@ -11,7 +11,6 @@ void ibutton_scene_read_on_enter(void* context) { Popup* popup = ibutton->popup; iButtonKey* key = ibutton->key; iButtonWorker* worker = ibutton->key_worker; - DOLPHIN_DEED(DolphinDeedIbuttonRead); popup_set_header(popup, "iButton", 95, 26, AlignCenter, AlignBottom); popup_set_text(popup, "Waiting\nfor key ...", 95, 30, AlignCenter, AlignTop); @@ -54,8 +53,8 @@ bool ibutton_scene_read_on_event(void* context, SceneManagerEvent event) { if(success) { ibutton_notification_message(ibutton, iButtonNotificationMessageSuccess); ibutton_notification_message(ibutton, iButtonNotificationMessageGreenOn); - DOLPHIN_DEED(DolphinDeedIbuttonReadSuccess); scene_manager_next_scene(scene_manager, iButtonSceneReadSuccess); + DOLPHIN_DEED(DolphinDeedIbuttonReadSuccess); } } } diff --git a/applications/main/ibutton/scenes/ibutton_scene_read_key_menu.c b/applications/main/ibutton/scenes/ibutton_scene_read_key_menu.c index 921b24fc1..0a8ecfa55 100644 --- a/applications/main/ibutton/scenes/ibutton_scene_read_key_menu.c +++ b/applications/main/ibutton/scenes/ibutton_scene_read_key_menu.c @@ -1,4 +1,5 @@ #include "../ibutton_i.h" +#include typedef enum { SubmenuIndexSave, @@ -49,6 +50,7 @@ bool ibutton_scene_read_key_menu_on_event(void* context, SceneManagerEvent event scene_manager_next_scene(ibutton->scene_manager, iButtonSceneSaveName); } else if(event.event == SubmenuIndexEmulate) { scene_manager_next_scene(ibutton->scene_manager, iButtonSceneEmulate); + DOLPHIN_DEED(DolphinDeedIbuttonEmulate); } else if(event.event == SubmenuIndexWrite) { scene_manager_next_scene(ibutton->scene_manager, iButtonSceneWrite); } diff --git a/applications/main/ibutton/scenes/ibutton_scene_save_name.c b/applications/main/ibutton/scenes/ibutton_scene_save_name.c index 773b93e0c..5f25a0002 100644 --- a/applications/main/ibutton/scenes/ibutton_scene_save_name.c +++ b/applications/main/ibutton/scenes/ibutton_scene_save_name.c @@ -1,6 +1,7 @@ #include "../ibutton_i.h" #include #include +#include static void ibutton_scene_save_name_text_input_callback(void* context) { iButton* ibutton = context; @@ -57,6 +58,15 @@ bool ibutton_scene_save_name_on_event(void* context, SceneManagerEvent event) { if(event.event == iButtonCustomEventTextEditResult) { if(ibutton_save_key(ibutton, ibutton->text_store)) { scene_manager_next_scene(ibutton->scene_manager, iButtonSceneSaveSuccess); + if(scene_manager_has_previous_scene( + ibutton->scene_manager, iButtonSceneSavedKeyMenu)) { + // Nothing, do not count editing as saving + } else if(scene_manager_has_previous_scene( + ibutton->scene_manager, iButtonSceneAddType)) { + DOLPHIN_DEED(DolphinDeedIbuttonAdd); + } else { + DOLPHIN_DEED(DolphinDeedIbuttonSave); + } } else { const uint32_t possible_scenes[] = { iButtonSceneReadKeyMenu, iButtonSceneSavedKeyMenu, iButtonSceneAddType}; diff --git a/applications/main/ibutton/scenes/ibutton_scene_save_success.c b/applications/main/ibutton/scenes/ibutton_scene_save_success.c index 43237f429..e0b9b3c47 100644 --- a/applications/main/ibutton/scenes/ibutton_scene_save_success.c +++ b/applications/main/ibutton/scenes/ibutton_scene_save_success.c @@ -1,5 +1,4 @@ #include "../ibutton_i.h" -#include static void ibutton_scene_save_success_popup_callback(void* context) { iButton* ibutton = context; @@ -9,7 +8,6 @@ static void ibutton_scene_save_success_popup_callback(void* context) { void ibutton_scene_save_success_on_enter(void* context) { iButton* ibutton = context; Popup* popup = ibutton->popup; - DOLPHIN_DEED(DolphinDeedIbuttonSave); popup_set_icon(popup, 32, 5, &I_DolphinNice_96x59); popup_set_header(popup, "Saved!", 5, 7, AlignLeft, AlignTop); diff --git a/applications/main/ibutton/scenes/ibutton_scene_saved_key_menu.c b/applications/main/ibutton/scenes/ibutton_scene_saved_key_menu.c index 3d588dd02..e4c9c350a 100644 --- a/applications/main/ibutton/scenes/ibutton_scene_saved_key_menu.c +++ b/applications/main/ibutton/scenes/ibutton_scene_saved_key_menu.c @@ -1,4 +1,5 @@ #include "../ibutton_i.h" +#include enum SubmenuIndex { SubmenuIndexEmulate, @@ -58,6 +59,7 @@ bool ibutton_scene_saved_key_menu_on_event(void* context, SceneManagerEvent even consumed = true; if(event.event == SubmenuIndexEmulate) { scene_manager_next_scene(ibutton->scene_manager, iButtonSceneEmulate); + DOLPHIN_DEED(DolphinDeedIbuttonEmulate); } else if(event.event == SubmenuIndexWrite) { scene_manager_next_scene(ibutton->scene_manager, iButtonSceneWrite); } else if(event.event == SubmenuIndexEdit) { diff --git a/applications/main/ibutton/scenes/ibutton_scene_start.c b/applications/main/ibutton/scenes/ibutton_scene_start.c index dde224e15..b8f6b07d6 100644 --- a/applications/main/ibutton/scenes/ibutton_scene_start.c +++ b/applications/main/ibutton/scenes/ibutton_scene_start.c @@ -1,5 +1,6 @@ #include "../ibutton_i.h" #include "ibutton/scenes/ibutton_scene.h" +#include enum SubmenuIndex { SubmenuIndexRead, @@ -38,6 +39,7 @@ bool ibutton_scene_start_on_event(void* context, SceneManagerEvent event) { consumed = true; if(event.event == SubmenuIndexRead) { scene_manager_next_scene(ibutton->scene_manager, iButtonSceneRead); + DOLPHIN_DEED(DolphinDeedIbuttonRead); } else if(event.event == SubmenuIndexSaved) { furi_string_set(ibutton->file_path, IBUTTON_APP_FOLDER); scene_manager_next_scene(ibutton->scene_manager, iButtonSceneSelectKey); diff --git a/applications/main/infrared/scenes/infrared_scene_learn.c b/applications/main/infrared/scenes/infrared_scene_learn.c index 37f9b3e05..48699a71f 100644 --- a/applications/main/infrared/scenes/infrared_scene_learn.c +++ b/applications/main/infrared/scenes/infrared_scene_learn.c @@ -1,4 +1,5 @@ #include "../infrared_i.h" +#include void infrared_scene_learn_on_enter(void* context) { Infrared* infrared = context; @@ -27,6 +28,7 @@ bool infrared_scene_learn_on_event(void* context, SceneManagerEvent event) { if(event.event == InfraredCustomEventTypeSignalReceived) { infrared_play_notification_message(infrared, InfraredNotificationMessageSuccess); scene_manager_next_scene(infrared->scene_manager, InfraredSceneLearnSuccess); + DOLPHIN_DEED(DolphinDeedIrLearnSuccess); consumed = true; } } diff --git a/applications/main/infrared/scenes/infrared_scene_learn_done.c b/applications/main/infrared/scenes/infrared_scene_learn_done.c index 7d3571715..54b7da724 100644 --- a/applications/main/infrared/scenes/infrared_scene_learn_done.c +++ b/applications/main/infrared/scenes/infrared_scene_learn_done.c @@ -1,13 +1,10 @@ #include "../infrared_i.h" -#include - void infrared_scene_learn_done_on_enter(void* context) { Infrared* infrared = context; Popup* popup = infrared->popup; popup_set_icon(popup, 32, 5, &I_DolphinNice_96x59); - DOLPHIN_DEED(DolphinDeedIrSave); if(infrared->app_state.is_learning_new_remote) { popup_set_header(popup, "New remote\ncreated!", 0, 0, AlignLeft, AlignTop); diff --git a/applications/main/infrared/scenes/infrared_scene_learn_enter_name.c b/applications/main/infrared/scenes/infrared_scene_learn_enter_name.c index b6a7eac0d..a8772a985 100644 --- a/applications/main/infrared/scenes/infrared_scene_learn_enter_name.c +++ b/applications/main/infrared/scenes/infrared_scene_learn_enter_name.c @@ -1,4 +1,5 @@ #include "../infrared_i.h" +#include void infrared_scene_learn_enter_name_on_enter(void* context) { Infrared* infrared = context; @@ -49,6 +50,7 @@ bool infrared_scene_learn_enter_name_on_event(void* context, SceneManagerEvent e if(success) { scene_manager_next_scene(scene_manager, InfraredSceneLearnDone); + DOLPHIN_DEED(DolphinDeedIrSave); } else { dialog_message_show_storage_error(infrared->dialogs, "Failed to save file"); const uint32_t possible_scenes[] = {InfraredSceneRemoteList, InfraredSceneStart}; diff --git a/applications/main/infrared/scenes/infrared_scene_learn_success.c b/applications/main/infrared/scenes/infrared_scene_learn_success.c index 466627144..469d4de9e 100644 --- a/applications/main/infrared/scenes/infrared_scene_learn_success.c +++ b/applications/main/infrared/scenes/infrared_scene_learn_success.c @@ -1,7 +1,5 @@ #include "../infrared_i.h" -#include - static void infrared_scene_learn_success_dialog_result_callback(DialogExResult result, void* context) { Infrared* infrared = context; @@ -13,7 +11,6 @@ void infrared_scene_learn_success_on_enter(void* context) { DialogEx* dialog_ex = infrared->dialog_ex; InfraredSignal* signal = infrared->received_signal; - DOLPHIN_DEED(DolphinDeedIrLearnSuccess); infrared_play_notification_message(infrared, InfraredNotificationMessageGreenOn); if(infrared_signal_is_raw(signal)) { diff --git a/applications/main/lfrfid/lfrfid.c b/applications/main/lfrfid/lfrfid.c index b0f989374..513227306 100644 --- a/applications/main/lfrfid/lfrfid.c +++ b/applications/main/lfrfid/lfrfid.c @@ -1,4 +1,5 @@ #include "lfrfid_i.h" +#include static bool lfrfid_debug_custom_event_callback(void* context, uint32_t event) { furi_assert(context); @@ -182,12 +183,14 @@ int32_t lfrfid_app(void* p) { view_dispatcher_attach_to_gui( app->view_dispatcher, app->gui, ViewDispatcherTypeDesktop); scene_manager_next_scene(app->scene_manager, LfRfidSceneRpc); + DOLPHIN_DEED(DolphinDeedRfidEmulate); } else { furi_string_set(app->file_path, args); lfrfid_load_key_data(app, app->file_path, true); view_dispatcher_attach_to_gui( app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); scene_manager_next_scene(app->scene_manager, LfRfidSceneEmulate); + DOLPHIN_DEED(DolphinDeedRfidEmulate); } } else { @@ -311,4 +314,4 @@ void lfrfid_widget_callback(GuiButtonType result, InputType type, void* context) void lfrfid_text_input_callback(void* context) { LfRfid* app = context; view_dispatcher_send_custom_event(app->view_dispatcher, LfRfidEventNext); -} \ No newline at end of file +} diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_emulate.c b/applications/main/lfrfid/scenes/lfrfid_scene_emulate.c index 2725982f0..dc3918994 100644 --- a/applications/main/lfrfid/scenes/lfrfid_scene_emulate.c +++ b/applications/main/lfrfid/scenes/lfrfid_scene_emulate.c @@ -1,12 +1,9 @@ #include "../lfrfid_i.h" -#include void lfrfid_scene_emulate_on_enter(void* context) { LfRfid* app = context; Popup* popup = app->popup; - DOLPHIN_DEED(DolphinDeedRfidEmulate); - popup_set_header(popup, "Emulating", 89, 30, AlignCenter, AlignTop); if(!furi_string_empty(app->file_name)) { popup_set_text(popup, furi_string_get_cstr(app->file_name), 89, 43, AlignCenter, AlignTop); diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_extra_actions.c b/applications/main/lfrfid/scenes/lfrfid_scene_extra_actions.c index d7fd93e19..fac2ebcec 100644 --- a/applications/main/lfrfid/scenes/lfrfid_scene_extra_actions.c +++ b/applications/main/lfrfid/scenes/lfrfid_scene_extra_actions.c @@ -1,4 +1,5 @@ #include "../lfrfid_i.h" +#include typedef enum { SubmenuIndexASK, @@ -57,10 +58,12 @@ bool lfrfid_scene_extra_actions_on_event(void* context, SceneManagerEvent event) if(event.event == SubmenuIndexASK) { app->read_type = LFRFIDWorkerReadTypeASKOnly; scene_manager_next_scene(app->scene_manager, LfRfidSceneRead); + DOLPHIN_DEED(DolphinDeedRfidRead); consumed = true; } else if(event.event == SubmenuIndexPSK) { app->read_type = LFRFIDWorkerReadTypePSKOnly; scene_manager_next_scene(app->scene_manager, LfRfidSceneRead); + DOLPHIN_DEED(DolphinDeedRfidRead); consumed = true; } else if(event.event == SubmenuIndexRAW) { scene_manager_next_scene(app->scene_manager, LfRfidSceneRawName); diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_read.c b/applications/main/lfrfid/scenes/lfrfid_scene_read.c index 4bdb215d1..5f1959728 100644 --- a/applications/main/lfrfid/scenes/lfrfid_scene_read.c +++ b/applications/main/lfrfid/scenes/lfrfid_scene_read.c @@ -46,7 +46,6 @@ static void void lfrfid_scene_read_on_enter(void* context) { LfRfid* app = context; - DOLPHIN_DEED(DolphinDeedRfidRead); if(app->read_type == LFRFIDWorkerReadTypePSKOnly) { lfrfid_view_read_set_read_mode(app->read_view, LfRfidReadPskOnly); } else if(app->read_type == LFRFIDWorkerReadTypeASKOnly) { @@ -79,10 +78,10 @@ bool lfrfid_scene_read_on_event(void* context, SceneManagerEvent event) { consumed = true; } else if(event.event == LfRfidEventReadDone) { app->protocol_id = app->protocol_id_next; - DOLPHIN_DEED(DolphinDeedRfidReadSuccess); notification_message(app->notifications, &sequence_success); furi_string_reset(app->file_name); scene_manager_next_scene(app->scene_manager, LfRfidSceneReadSuccess); + DOLPHIN_DEED(DolphinDeedRfidReadSuccess); consumed = true; } else if(event.event == LfRfidEventReadStartPSK) { if(app->read_type == LFRFIDWorkerReadTypeAuto) { diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_read_key_menu.c b/applications/main/lfrfid/scenes/lfrfid_scene_read_key_menu.c index 7480304b6..081c47912 100644 --- a/applications/main/lfrfid/scenes/lfrfid_scene_read_key_menu.c +++ b/applications/main/lfrfid/scenes/lfrfid_scene_read_key_menu.c @@ -1,4 +1,5 @@ #include "../lfrfid_i.h" +#include typedef enum { SubmenuIndexSave, @@ -43,6 +44,7 @@ bool lfrfid_scene_read_key_menu_on_event(void* context, SceneManagerEvent event) consumed = true; } else if(event.event == SubmenuIndexEmulate) { scene_manager_next_scene(app->scene_manager, LfRfidSceneEmulate); + DOLPHIN_DEED(DolphinDeedRfidEmulate); consumed = true; } scene_manager_set_scene_state(app->scene_manager, LfRfidSceneReadKeyMenu, event.event); diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_save_data.c b/applications/main/lfrfid/scenes/lfrfid_scene_save_data.c index 6c5ea2f2d..11a687bdd 100644 --- a/applications/main/lfrfid/scenes/lfrfid_scene_save_data.c +++ b/applications/main/lfrfid/scenes/lfrfid_scene_save_data.c @@ -1,5 +1,4 @@ #include "../lfrfid_i.h" -#include void lfrfid_scene_save_data_on_enter(void* context) { LfRfid* app = context; @@ -32,7 +31,6 @@ bool lfrfid_scene_save_data_on_event(void* context, SceneManagerEvent event) { consumed = true; size_t size = protocol_dict_get_data_size(app->dict, app->protocol_id); protocol_dict_set_data(app->dict, app->protocol_id, app->new_key_data, size); - DOLPHIN_DEED(DolphinDeedRfidAdd); scene_manager_next_scene(scene_manager, LfRfidSceneSaveName); scene_manager_set_scene_state(scene_manager, LfRfidSceneSaveData, 1); } diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_save_name.c b/applications/main/lfrfid/scenes/lfrfid_scene_save_name.c index ca9a52de0..87e110f18 100644 --- a/applications/main/lfrfid/scenes/lfrfid_scene_save_name.c +++ b/applications/main/lfrfid/scenes/lfrfid_scene_save_name.c @@ -1,5 +1,6 @@ #include #include "../lfrfid_i.h" +#include void lfrfid_scene_save_name_on_enter(void* context) { LfRfid* app = context; @@ -55,6 +56,13 @@ bool lfrfid_scene_save_name_on_event(void* context, SceneManagerEvent event) { if(lfrfid_save_key(app)) { scene_manager_next_scene(scene_manager, LfRfidSceneSaveSuccess); + if(scene_manager_has_previous_scene(scene_manager, LfRfidSceneSavedKeyMenu)) { + // Nothing, do not count editing as saving + } else if(scene_manager_has_previous_scene(scene_manager, LfRfidSceneSaveType)) { + DOLPHIN_DEED(DolphinDeedRfidAdd); + } else { + DOLPHIN_DEED(DolphinDeedRfidSave); + } } else { scene_manager_search_and_switch_to_previous_scene( scene_manager, LfRfidSceneReadKeyMenu); diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_save_success.c b/applications/main/lfrfid/scenes/lfrfid_scene_save_success.c index e91ad04e3..52aefa848 100644 --- a/applications/main/lfrfid/scenes/lfrfid_scene_save_success.c +++ b/applications/main/lfrfid/scenes/lfrfid_scene_save_success.c @@ -1,5 +1,4 @@ #include "../lfrfid_i.h" -#include void lfrfid_scene_save_success_on_enter(void* context) { LfRfid* app = context; @@ -8,7 +7,6 @@ void lfrfid_scene_save_success_on_enter(void* context) { // Clear state of data enter scene scene_manager_set_scene_state(app->scene_manager, LfRfidSceneSaveData, 0); - DOLPHIN_DEED(DolphinDeedRfidSave); popup_set_icon(popup, 32, 5, &I_DolphinNice_96x59); popup_set_header(popup, "Saved!", 5, 7, AlignLeft, AlignTop); popup_set_context(popup, app); diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_saved_key_menu.c b/applications/main/lfrfid/scenes/lfrfid_scene_saved_key_menu.c index 040b31f10..d3c3d389a 100644 --- a/applications/main/lfrfid/scenes/lfrfid_scene_saved_key_menu.c +++ b/applications/main/lfrfid/scenes/lfrfid_scene_saved_key_menu.c @@ -1,4 +1,5 @@ #include "../lfrfid_i.h" +#include typedef enum { SubmenuIndexEmulate, @@ -42,6 +43,7 @@ bool lfrfid_scene_saved_key_menu_on_event(void* context, SceneManagerEvent event if(event.type == SceneManagerEventTypeCustom) { if(event.event == SubmenuIndexEmulate) { scene_manager_next_scene(app->scene_manager, LfRfidSceneEmulate); + DOLPHIN_DEED(DolphinDeedRfidEmulate); consumed = true; } else if(event.event == SubmenuIndexWrite) { scene_manager_next_scene(app->scene_manager, LfRfidSceneWrite); diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_start.c b/applications/main/lfrfid/scenes/lfrfid_scene_start.c index d6d87f441..8e1c92dbb 100644 --- a/applications/main/lfrfid/scenes/lfrfid_scene_start.c +++ b/applications/main/lfrfid/scenes/lfrfid_scene_start.c @@ -1,4 +1,5 @@ #include "../lfrfid_i.h" +#include typedef enum { SubmenuIndexRead, @@ -47,6 +48,7 @@ bool lfrfid_scene_start_on_event(void* context, SceneManagerEvent event) { if(event.type == SceneManagerEventTypeCustom) { if(event.event == SubmenuIndexRead) { scene_manager_next_scene(app->scene_manager, LfRfidSceneRead); + DOLPHIN_DEED(DolphinDeedRfidRead); consumed = true; } else if(event.event == SubmenuIndexSaved) { furi_string_set(app->file_path, LFRFID_APP_FOLDER); diff --git a/applications/main/nfc/nfc.c b/applications/main/nfc/nfc.c index 0b685f545..55c68a450 100644 --- a/applications/main/nfc/nfc.c +++ b/applications/main/nfc/nfc.c @@ -1,5 +1,6 @@ #include "nfc_i.h" #include "furi_hal_nfc.h" +#include bool nfc_custom_event_callback(void* context, uint32_t event) { furi_assert(context); @@ -275,12 +276,15 @@ int32_t nfc_app(void* p) { if(nfc_device_load(nfc->dev, p, true)) { if(nfc->dev->format == NfcDeviceSaveFormatMifareUl) { scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightEmulate); + DOLPHIN_DEED(DolphinDeedNfcEmulate); } else if(nfc->dev->format == NfcDeviceSaveFormatMifareClassic) { scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicEmulate); + DOLPHIN_DEED(DolphinDeedNfcEmulate); } else if(nfc->dev->format == NfcDeviceSaveFormatBankCard) { scene_manager_next_scene(nfc->scene_manager, NfcSceneDeviceInfo); } else { scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateUid); + DOLPHIN_DEED(DolphinDeedNfcEmulate); } } else { // Exit app diff --git a/applications/main/nfc/scenes/nfc_scene_detect_reader.c b/applications/main/nfc/scenes/nfc_scene_detect_reader.c index f0177f9c1..abf1437d2 100644 --- a/applications/main/nfc/scenes/nfc_scene_detect_reader.c +++ b/applications/main/nfc/scenes/nfc_scene_detect_reader.c @@ -1,5 +1,4 @@ #include "../nfc_i.h" -#include #define NFC_SCENE_DETECT_READER_PAIR_NONCES_MAX (10U) @@ -26,7 +25,6 @@ void nfc_scene_detect_reader_callback(void* context) { void nfc_scene_detect_reader_on_enter(void* context) { Nfc* nfc = context; - DOLPHIN_DEED(DolphinDeedNfcDetectReader); detect_reader_set_callback(nfc->detect_reader, nfc_scene_detect_reader_callback, nfc); detect_reader_set_nonces_max(nfc->detect_reader, NFC_SCENE_DETECT_READER_PAIR_NONCES_MAX); diff --git a/applications/main/nfc/scenes/nfc_scene_emulate_uid.c b/applications/main/nfc/scenes/nfc_scene_emulate_uid.c index 8bb207960..5ddb60992 100644 --- a/applications/main/nfc/scenes/nfc_scene_emulate_uid.c +++ b/applications/main/nfc/scenes/nfc_scene_emulate_uid.c @@ -1,5 +1,4 @@ #include "../nfc_i.h" -#include #define NFC_SCENE_EMULATE_UID_LOG_SIZE_MAX (200) @@ -59,7 +58,6 @@ static void nfc_scene_emulate_uid_widget_config(Nfc* nfc, bool data_received) { void nfc_scene_emulate_uid_on_enter(void* context) { Nfc* nfc = context; - DOLPHIN_DEED(DolphinDeedNfcEmulate); // Setup Widget nfc_scene_emulate_uid_widget_config(nfc, false); diff --git a/applications/main/nfc/scenes/nfc_scene_emv_read_success.c b/applications/main/nfc/scenes/nfc_scene_emv_read_success.c index 6a0b32fad..005b76cb2 100644 --- a/applications/main/nfc/scenes/nfc_scene_emv_read_success.c +++ b/applications/main/nfc/scenes/nfc_scene_emv_read_success.c @@ -1,6 +1,5 @@ #include "../nfc_i.h" #include "../helpers/nfc_emv_parser.h" -#include void nfc_scene_emv_read_success_widget_callback( GuiButtonType result, @@ -15,7 +14,6 @@ void nfc_scene_emv_read_success_widget_callback( void nfc_scene_emv_read_success_on_enter(void* context) { Nfc* nfc = context; EmvData* emv_data = &nfc->dev->dev_data.emv_data; - DOLPHIN_DEED(DolphinDeedNfcReadSuccess); // Setup Custom Widget view widget_add_button_element( diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_dict_attack.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_dict_attack.c index 813546905..acb5b783c 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_classic_dict_attack.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_dict_attack.c @@ -1,4 +1,5 @@ #include "../nfc_i.h" +#include #define TAG "NfcMfClassicDictAttack" @@ -110,6 +111,7 @@ bool nfc_scene_mf_classic_dict_attack_on_event(void* context, SceneManagerEvent } else { notification_message(nfc->notifications, &sequence_success); scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicReadSuccess); + DOLPHIN_DEED(DolphinDeedNfcReadSuccess); consumed = true; } } else if(event.event == NfcWorkerEventAborted) { @@ -119,6 +121,8 @@ bool nfc_scene_mf_classic_dict_attack_on_event(void* context, SceneManagerEvent } else { notification_message(nfc->notifications, &sequence_success); scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicReadSuccess); + // Counting failed attempts too + DOLPHIN_DEED(DolphinDeedNfcReadSuccess); consumed = true; } } else if(event.event == NfcWorkerEventCardDetected) { diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_emulate.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_emulate.c index 65639b2b4..e514fa728 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_classic_emulate.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_emulate.c @@ -1,5 +1,4 @@ #include "../nfc_i.h" -#include #define NFC_MF_CLASSIC_DATA_NOT_CHANGED (0UL) #define NFC_MF_CLASSIC_DATA_CHANGED (1UL) @@ -15,7 +14,6 @@ bool nfc_mf_classic_emulate_worker_callback(NfcWorkerEvent event, void* context) void nfc_scene_mf_classic_emulate_on_enter(void* context) { Nfc* nfc = context; - DOLPHIN_DEED(DolphinDeedNfcEmulate); // Setup view Popup* popup = nfc->popup; diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_keys_add.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_keys_add.c index 2921d21c9..b122aa225 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_classic_keys_add.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_keys_add.c @@ -1,4 +1,5 @@ #include "../nfc_i.h" +#include void nfc_scene_mf_classic_keys_add_byte_input_callback(void* context) { Nfc* nfc = context; @@ -36,6 +37,7 @@ bool nfc_scene_mf_classic_keys_add_on_event(void* context, SceneManagerEvent eve nfc->scene_manager, NfcSceneMfClassicKeysWarnDuplicate); } else if(mf_classic_dict_add_key(dict, nfc->byte_input_store)) { scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveSuccess); + DOLPHIN_DEED(DolphinDeedNfcMfcAdd); } else { scene_manager_next_scene(nfc->scene_manager, NfcSceneDictNotFound); } diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_menu.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_menu.c index 2cba04337..a5bb10ddf 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_classic_menu.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_menu.c @@ -36,8 +36,6 @@ bool nfc_scene_mf_classic_menu_on_event(void* context, SceneManagerEvent event) if(event.type == SceneManagerEventTypeCustom) { if(event.event == SubmenuIndexSave) { - DOLPHIN_DEED(DolphinDeedNfcMfcAdd); - scene_manager_set_scene_state( nfc->scene_manager, NfcSceneMfClassicMenu, SubmenuIndexSave); nfc->dev->format = NfcDeviceSaveFormatMifareClassic; @@ -49,6 +47,11 @@ bool nfc_scene_mf_classic_menu_on_event(void* context, SceneManagerEvent event) scene_manager_set_scene_state( nfc->scene_manager, NfcSceneMfClassicMenu, SubmenuIndexEmulate); scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicEmulate); + if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSetType)) { + DOLPHIN_DEED(DolphinDeedNfcAddEmulate); + } else { + DOLPHIN_DEED(DolphinDeedNfcEmulate); + } consumed = true; } else if(event.event == SubmenuIndexInfo) { scene_manager_set_scene_state( diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_read_success.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_read_success.c index 0cdb86464..ae31e92cc 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_classic_read_success.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_read_success.c @@ -1,5 +1,4 @@ #include "../nfc_i.h" -#include void nfc_scene_mf_classic_read_success_widget_callback( GuiButtonType result, @@ -18,8 +17,6 @@ void nfc_scene_mf_classic_read_success_on_enter(void* context) { NfcDeviceData* dev_data = &nfc->dev->dev_data; MfClassicData* mf_data = &dev_data->mf_classic_data; - DOLPHIN_DEED(DolphinDeedNfcReadSuccess); - // Setup view Widget* widget = nfc->widget; widget_add_button_element( diff --git a/applications/main/nfc/scenes/nfc_scene_mf_desfire_menu.c b/applications/main/nfc/scenes/nfc_scene_mf_desfire_menu.c index 1e2f2d2f2..bee63d775 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_desfire_menu.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_desfire_menu.c @@ -1,4 +1,5 @@ #include "../nfc_i.h" +#include enum SubmenuIndex { SubmenuIndexSave, @@ -48,6 +49,11 @@ bool nfc_scene_mf_desfire_menu_on_event(void* context, SceneManagerEvent event) consumed = true; } else if(event.event == SubmenuIndexEmulateUid) { scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateUid); + if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSetType)) { + DOLPHIN_DEED(DolphinDeedNfcAddEmulate); + } else { + DOLPHIN_DEED(DolphinDeedNfcEmulate); + } consumed = true; } else if(event.event == SubmenuIndexInfo) { scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcDataInfo); diff --git a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_emulate.c b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_emulate.c index 712ddc077..e84fb3927 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_emulate.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_emulate.c @@ -1,5 +1,4 @@ #include "../nfc_i.h" -#include #define NFC_MF_UL_DATA_NOT_CHANGED (0UL) #define NFC_MF_UL_DATA_CHANGED (1UL) @@ -15,7 +14,6 @@ bool nfc_mf_ultralight_emulate_worker_callback(NfcWorkerEvent event, void* conte void nfc_scene_mf_ultralight_emulate_on_enter(void* context) { Nfc* nfc = context; - DOLPHIN_DEED(DolphinDeedNfcEmulate); // Setup view Popup* popup = nfc->popup; diff --git a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_menu.c b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_menu.c index ba9f22338..ab4d37b09 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_menu.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_menu.c @@ -1,4 +1,5 @@ #include "../nfc_i.h" +#include enum SubmenuIndex { SubmenuIndexUnlock, @@ -56,6 +57,11 @@ bool nfc_scene_mf_ultralight_menu_on_event(void* context, SceneManagerEvent even consumed = true; } else if(event.event == SubmenuIndexEmulate) { scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightEmulate); + if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSetType)) { + DOLPHIN_DEED(DolphinDeedNfcAddEmulate); + } else { + DOLPHIN_DEED(DolphinDeedNfcEmulate); + } consumed = true; } else if(event.event == SubmenuIndexUnlock) { scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightUnlockMenu); diff --git a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_read_auth.c b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_read_auth.c index 25008004b..5dbb0c18a 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_read_auth.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_read_auth.c @@ -1,5 +1,4 @@ #include "../nfc_i.h" -#include typedef enum { NfcSceneMfUlReadStateIdle, @@ -51,7 +50,6 @@ void nfc_scene_mf_ultralight_read_auth_set_state(Nfc* nfc, NfcSceneMfUlReadState void nfc_scene_mf_ultralight_read_auth_on_enter(void* context) { Nfc* nfc = context; - DOLPHIN_DEED(DolphinDeedNfcRead); nfc_device_clear(nfc->dev); // Setup view diff --git a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_read_auth_result.c b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_read_auth_result.c index 5a690a213..178d03351 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_read_auth_result.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_read_auth_result.c @@ -1,5 +1,4 @@ #include "../nfc_i.h" -#include void nfc_scene_mf_ultralight_read_auth_result_widget_callback( GuiButtonType result, @@ -37,7 +36,6 @@ void nfc_scene_mf_ultralight_read_auth_result_on_enter(void* context) { widget_add_string_element( widget, 0, 17, AlignLeft, AlignTop, FontSecondary, furi_string_get_cstr(temp_str)); if(mf_ul_data->auth_success) { - DOLPHIN_DEED(DolphinDeedNfcReadSuccess); furi_string_printf( temp_str, "Password: %02X %02X %02X %02X", @@ -54,8 +52,6 @@ void nfc_scene_mf_ultralight_read_auth_result_on_enter(void* context) { config_pages->auth_data.pack.raw[1]); widget_add_string_element( widget, 0, 39, AlignLeft, AlignTop, FontSecondary, furi_string_get_cstr(temp_str)); - } else { - DOLPHIN_DEED(DolphinDeedNfcMfulError); } furi_string_printf( temp_str, "Pages Read: %d/%d", mf_ul_data->data_read / 4, mf_ul_data->data_size / 4); diff --git a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_read_success.c b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_read_success.c index 77034ea80..63bffbf36 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_read_success.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_read_success.c @@ -1,5 +1,4 @@ #include "../nfc_i.h" -#include void nfc_scene_mf_ultralight_read_success_widget_callback( GuiButtonType result, @@ -14,7 +13,6 @@ void nfc_scene_mf_ultralight_read_success_widget_callback( 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; diff --git a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_unlock_warn.c b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_unlock_warn.c index 58e081db9..514cd4e98 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_unlock_warn.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_unlock_warn.c @@ -1,4 +1,5 @@ #include "../nfc_i.h" +#include void nfc_scene_mf_ultralight_unlock_warn_dialog_callback(DialogExResult result, void* context) { Nfc* nfc = context; @@ -30,6 +31,7 @@ bool nfc_scene_mf_ultralight_unlock_warn_on_event(void* context, SceneManagerEve if(event.type == SceneManagerEventTypeCustom) { if(event.event == DialogExResultCenter) { scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightReadAuth); + DOLPHIN_DEED(DolphinDeedNfcRead); consumed = true; } } diff --git a/applications/main/nfc/scenes/nfc_scene_nfca_menu.c b/applications/main/nfc/scenes/nfc_scene_nfca_menu.c index 00d0d943d..30f63945c 100644 --- a/applications/main/nfc/scenes/nfc_scene_nfca_menu.c +++ b/applications/main/nfc/scenes/nfc_scene_nfca_menu.c @@ -1,4 +1,5 @@ #include "../nfc_i.h" +#include enum SubmenuIndex { SubmenuIndexSaveUid, @@ -41,6 +42,11 @@ bool nfc_scene_nfca_menu_on_event(void* context, SceneManagerEvent event) { consumed = true; } else if(event.event == SubmenuIndexEmulateUid) { scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateUid); + if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSetType)) { + DOLPHIN_DEED(DolphinDeedNfcAddEmulate); + } else { + DOLPHIN_DEED(DolphinDeedNfcEmulate); + } consumed = true; } else if(event.event == SubmenuIndexInfo) { scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcDataInfo); diff --git a/applications/main/nfc/scenes/nfc_scene_nfca_read_success.c b/applications/main/nfc/scenes/nfc_scene_nfca_read_success.c index 2ea7c9921..a38f31a98 100644 --- a/applications/main/nfc/scenes/nfc_scene_nfca_read_success.c +++ b/applications/main/nfc/scenes/nfc_scene_nfca_read_success.c @@ -1,5 +1,4 @@ #include "../nfc_i.h" -#include void nfc_scene_nfca_read_success_widget_callback( GuiButtonType result, @@ -16,8 +15,6 @@ void nfc_scene_nfca_read_success_widget_callback( void nfc_scene_nfca_read_success_on_enter(void* context) { Nfc* nfc = context; - DOLPHIN_DEED(DolphinDeedNfcReadSuccess); - // Setup view FuriHalNfcDevData* data = &nfc->dev->dev_data.nfc_data; Widget* widget = nfc->widget; diff --git a/applications/main/nfc/scenes/nfc_scene_read.c b/applications/main/nfc/scenes/nfc_scene_read.c index e6df476f0..1f82aef08 100644 --- a/applications/main/nfc/scenes/nfc_scene_read.c +++ b/applications/main/nfc/scenes/nfc_scene_read.c @@ -39,7 +39,6 @@ void nfc_scene_read_set_state(Nfc* nfc, NfcSceneReadState state) { void nfc_scene_read_on_enter(void* context) { Nfc* nfc = context; - DOLPHIN_DEED(DolphinDeedNfcRead); nfc_device_clear(nfc->dev); // Setup view @@ -62,26 +61,32 @@ bool nfc_scene_read_on_event(void* context, SceneManagerEvent event) { (event.event == NfcWorkerEventReadUidNfcV)) { notification_message(nfc->notifications, &sequence_success); scene_manager_next_scene(nfc->scene_manager, NfcSceneReadCardSuccess); + DOLPHIN_DEED(DolphinDeedNfcReadSuccess); consumed = true; } else if(event.event == NfcWorkerEventReadUidNfcA) { notification_message(nfc->notifications, &sequence_success); scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcaReadSuccess); + DOLPHIN_DEED(DolphinDeedNfcReadSuccess); consumed = true; } else if(event.event == NfcWorkerEventReadMfUltralight) { notification_message(nfc->notifications, &sequence_success); scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightReadSuccess); + DOLPHIN_DEED(DolphinDeedNfcReadSuccess); consumed = true; } else if(event.event == NfcWorkerEventReadMfClassicDone) { notification_message(nfc->notifications, &sequence_success); scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicReadSuccess); + DOLPHIN_DEED(DolphinDeedNfcReadSuccess); consumed = true; } else if(event.event == NfcWorkerEventReadMfDesfire) { notification_message(nfc->notifications, &sequence_success); scene_manager_next_scene(nfc->scene_manager, NfcSceneMfDesfireReadSuccess); + DOLPHIN_DEED(DolphinDeedNfcReadSuccess); consumed = true; } else if(event.event == NfcWorkerEventReadBankCard) { notification_message(nfc->notifications, &sequence_success); scene_manager_next_scene(nfc->scene_manager, NfcSceneEmvReadSuccess); + DOLPHIN_DEED(DolphinDeedNfcReadSuccess); consumed = true; } else if(event.event == NfcWorkerEventReadMfClassicDictAttackRequired) { if(mf_classic_dict_check_presence(MfClassicDictTypeFlipper)) { diff --git a/applications/main/nfc/scenes/nfc_scene_read_card_success.c b/applications/main/nfc/scenes/nfc_scene_read_card_success.c index 352cb4a7e..9b2a2188e 100644 --- a/applications/main/nfc/scenes/nfc_scene_read_card_success.c +++ b/applications/main/nfc/scenes/nfc_scene_read_card_success.c @@ -1,5 +1,4 @@ #include "../nfc_i.h" -#include void nfc_scene_read_card_success_widget_callback( GuiButtonType result, @@ -18,7 +17,6 @@ void nfc_scene_read_card_success_on_enter(void* context) { FuriString* temp_str; temp_str = furi_string_alloc(); - DOLPHIN_DEED(DolphinDeedNfcReadSuccess); // Setup view FuriHalNfcDevData* data = &nfc->dev->dev_data.nfc_data; diff --git a/applications/main/nfc/scenes/nfc_scene_save_name.c b/applications/main/nfc/scenes/nfc_scene_save_name.c index 736eab7de..791f8d99b 100644 --- a/applications/main/nfc/scenes/nfc_scene_save_name.c +++ b/applications/main/nfc/scenes/nfc_scene_save_name.c @@ -2,6 +2,7 @@ #include #include #include +#include void nfc_scene_save_name_text_input_callback(void* context) { Nfc* nfc = context; @@ -63,6 +64,13 @@ bool nfc_scene_save_name_on_event(void* context, SceneManagerEvent event) { strlcpy(nfc->dev->dev_name, nfc->text_store, strlen(nfc->text_store) + 1); if(nfc_device_save(nfc->dev, nfc->text_store)) { scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveSuccess); + if(!scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSavedMenu)) { + // Nothing, do not count editing as saving + } else if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSetType)) { + DOLPHIN_DEED(DolphinDeedNfcAddSave); + } else { + DOLPHIN_DEED(DolphinDeedNfcSave); + } consumed = true; } else { consumed = scene_manager_search_and_switch_to_previous_scene( diff --git a/applications/main/nfc/scenes/nfc_scene_save_success.c b/applications/main/nfc/scenes/nfc_scene_save_success.c index dcd2519f1..34919cbd8 100644 --- a/applications/main/nfc/scenes/nfc_scene_save_success.c +++ b/applications/main/nfc/scenes/nfc_scene_save_success.c @@ -1,5 +1,4 @@ #include "../nfc_i.h" -#include void nfc_scene_save_success_popup_callback(void* context) { Nfc* nfc = context; @@ -8,7 +7,6 @@ void nfc_scene_save_success_popup_callback(void* context) { void nfc_scene_save_success_on_enter(void* context) { Nfc* nfc = context; - DOLPHIN_DEED(DolphinDeedNfcSave); // Setup view Popup* popup = nfc->popup; diff --git a/applications/main/nfc/scenes/nfc_scene_saved_menu.c b/applications/main/nfc/scenes/nfc_scene_saved_menu.c index fe65b5b8a..09d2c2d61 100644 --- a/applications/main/nfc/scenes/nfc_scene_saved_menu.c +++ b/applications/main/nfc/scenes/nfc_scene_saved_menu.c @@ -1,4 +1,5 @@ #include "../nfc_i.h" +#include enum SubmenuIndex { SubmenuIndexEmulate, @@ -76,6 +77,7 @@ bool nfc_scene_saved_menu_on_event(void* context, SceneManagerEvent event) { } else { scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateUid); } + DOLPHIN_DEED(DolphinDeedNfcEmulate); consumed = true; } else if(event.event == SubmenuIndexRename) { scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveName); diff --git a/applications/main/nfc/scenes/nfc_scene_set_uid.c b/applications/main/nfc/scenes/nfc_scene_set_uid.c index 9622ba213..1d3fb5eb9 100644 --- a/applications/main/nfc/scenes/nfc_scene_set_uid.c +++ b/applications/main/nfc/scenes/nfc_scene_set_uid.c @@ -1,5 +1,4 @@ #include "../nfc_i.h" -#include void nfc_scene_set_uid_byte_input_callback(void* context) { Nfc* nfc = context; @@ -30,7 +29,6 @@ bool nfc_scene_set_uid_on_event(void* context, SceneManagerEvent event) { if(event.type == SceneManagerEventTypeCustom) { if(event.event == NfcCustomEventByteInputDone) { - DOLPHIN_DEED(DolphinDeedNfcAddSave); 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)) { diff --git a/applications/main/nfc/scenes/nfc_scene_start.c b/applications/main/nfc/scenes/nfc_scene_start.c index 1a9051dfd..0c4ec1cf9 100644 --- a/applications/main/nfc/scenes/nfc_scene_start.c +++ b/applications/main/nfc/scenes/nfc_scene_start.c @@ -1,4 +1,5 @@ #include "../nfc_i.h" +#include enum SubmenuIndex { SubmenuIndexRead, @@ -47,11 +48,13 @@ 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, NfcSceneRead); + DOLPHIN_DEED(DolphinDeedNfcRead); consumed = true; } else if(event.event == SubmenuIndexDetectReader) { bool sd_exist = storage_sd_status(nfc->dev->storage) == FSE_OK; if(sd_exist) { scene_manager_next_scene(nfc->scene_manager, NfcSceneDetectReader); + DOLPHIN_DEED(DolphinDeedNfcDetectReader); } else { scene_manager_next_scene(nfc->scene_manager, NfcSceneDictNotFound); } diff --git a/applications/main/subghz/scenes/subghz_scene_frequency_analyzer.c b/applications/main/subghz/scenes/subghz_scene_frequency_analyzer.c index 9595c61be..b48a821da 100644 --- a/applications/main/subghz/scenes/subghz_scene_frequency_analyzer.c +++ b/applications/main/subghz/scenes/subghz_scene_frequency_analyzer.c @@ -1,6 +1,5 @@ #include "../subghz_i.h" #include "../views/subghz_frequency_analyzer.h" -#include void subghz_scene_frequency_analyzer_callback(SubGhzCustomEvent event, void* context) { furi_assert(context); @@ -10,7 +9,6 @@ void subghz_scene_frequency_analyzer_callback(SubGhzCustomEvent event, void* con void subghz_scene_frequency_analyzer_on_enter(void* context) { SubGhz* subghz = context; - DOLPHIN_DEED(DolphinDeedSubGhzFrequencyAnalyzer); subghz_frequency_analyzer_set_callback( subghz->subghz_frequency_analyzer, subghz_scene_frequency_analyzer_callback, subghz); view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdFrequencyAnalyzer); diff --git a/applications/main/subghz/scenes/subghz_scene_read_raw.c b/applications/main/subghz/scenes/subghz_scene_read_raw.c index 8ac9bf5ba..ba9ce803b 100644 --- a/applications/main/subghz/scenes/subghz_scene_read_raw.c +++ b/applications/main/subghz/scenes/subghz_scene_read_raw.c @@ -223,7 +223,12 @@ bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) { subghz->txrx->rx_key_state = SubGhzRxKeyStateBack; scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowOnlyRx); } else { - DOLPHIN_DEED(DolphinDeedSubGhzSend); + if(scene_manager_has_previous_scene( + subghz->scene_manager, SubGhzSceneSaved) || + !scene_manager_has_previous_scene( + subghz->scene_manager, SubGhzSceneStart)) { + DOLPHIN_DEED(DolphinDeedSubGhzSend); + } // set callback end tx subghz_protocol_raw_file_encoder_worker_set_callback_end( (SubGhzProtocolEncoderRAW*)subghz_transmitter_get_protocol_instance( diff --git a/applications/main/subghz/scenes/subghz_scene_receiver.c b/applications/main/subghz/scenes/subghz_scene_receiver.c index 77a921457..2b01e2975 100644 --- a/applications/main/subghz/scenes/subghz_scene_receiver.c +++ b/applications/main/subghz/scenes/subghz_scene_receiver.c @@ -1,5 +1,6 @@ #include "../subghz_i.h" #include "../views/receiver.h" +#include static const NotificationSequence subghs_sequence_rx = { &message_green_255, @@ -181,6 +182,7 @@ bool subghz_scene_receiver_on_event(void* context, SceneManagerEvent event) { subghz->txrx->idx_menu_chosen = subghz_view_receiver_get_idx_menu(subghz->subghz_receiver); scene_manager_next_scene(subghz->scene_manager, SubGhzSceneReceiverInfo); + DOLPHIN_DEED(DolphinDeedSubGhzReceiverInfo); consumed = true; break; case SubGhzCustomEventViewReceiverConfig: diff --git a/applications/main/subghz/scenes/subghz_scene_receiver_info.c b/applications/main/subghz/scenes/subghz_scene_receiver_info.c index 03a8ebbcb..bc6c8f112 100644 --- a/applications/main/subghz/scenes/subghz_scene_receiver_info.c +++ b/applications/main/subghz/scenes/subghz_scene_receiver_info.c @@ -1,6 +1,5 @@ #include "../subghz_i.h" #include "../helpers/subghz_custom_event.h" -#include void subghz_scene_receiver_info_callback(GuiButtonType result, InputType type, void* context) { furi_assert(context); @@ -45,7 +44,6 @@ static bool subghz_scene_receiver_info_update_parser(void* context) { void subghz_scene_receiver_info_on_enter(void* context) { SubGhz* subghz = context; - DOLPHIN_DEED(DolphinDeedSubGhzReceiverInfo); if(subghz_scene_receiver_info_update_parser(subghz)) { FuriString* frequency_str; FuriString* modulation_str; diff --git a/applications/main/subghz/scenes/subghz_scene_save_name.c b/applications/main/subghz/scenes/subghz_scene_save_name.c index dfcb65865..33846c283 100644 --- a/applications/main/subghz/scenes/subghz_scene_save_name.c +++ b/applications/main/subghz/scenes/subghz_scene_save_name.c @@ -4,6 +4,7 @@ #include "../helpers/subghz_custom_event.h" #include #include +#include #define MAX_TEXT_INPUT_LEN 22 @@ -131,6 +132,17 @@ bool subghz_scene_save_name_on_event(void* context, SceneManagerEvent event) { } scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSaveSuccess); + if(scene_manager_has_previous_scene(subghz->scene_manager, SubGhzSceneSavedMenu)) { + // Nothing, do not count editing as saving + } else if(scene_manager_has_previous_scene( + subghz->scene_manager, SubGhzSceneMoreRAW)) { + // Ditto, for RAW signals + } else if(scene_manager_has_previous_scene( + subghz->scene_manager, SubGhzSceneSetType)) { + DOLPHIN_DEED(DolphinDeedSubGhzAddManually); + } else { + DOLPHIN_DEED(DolphinDeedSubGhzSave); + } return true; } else { furi_string_set(subghz->error_str, "No name file"); diff --git a/applications/main/subghz/scenes/subghz_scene_save_success.c b/applications/main/subghz/scenes/subghz_scene_save_success.c index 3d5c16ca3..d32c9271a 100644 --- a/applications/main/subghz/scenes/subghz_scene_save_success.c +++ b/applications/main/subghz/scenes/subghz_scene_save_success.c @@ -1,7 +1,5 @@ #include "../subghz_i.h" #include "../helpers/subghz_custom_event.h" -#include -#include void subghz_scene_save_success_popup_callback(void* context) { SubGhz* subghz = context; @@ -10,7 +8,6 @@ void subghz_scene_save_success_popup_callback(void* context) { void subghz_scene_save_success_on_enter(void* context) { SubGhz* subghz = context; - DOLPHIN_DEED(DolphinDeedSubGhzSave); // Setup view Popup* popup = subghz->popup; diff --git a/applications/main/subghz/scenes/subghz_scene_set_type.c b/applications/main/subghz/scenes/subghz_scene_set_type.c index 44fe2fc76..2ed537193 100644 --- a/applications/main/subghz/scenes/subghz_scene_set_type.c +++ b/applications/main/subghz/scenes/subghz_scene_set_type.c @@ -3,7 +3,6 @@ #include #include #include -#include #include #include #include @@ -381,7 +380,6 @@ bool subghz_scene_set_type_on_event(void* context, SceneManagerEvent event) { if(generated_protocol) { subghz_file_name_clear(subghz); - DOLPHIN_DEED(DolphinDeedSubGhzAddManually); scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSaveName); return true; } diff --git a/applications/main/subghz/scenes/subghz_scene_start.c b/applications/main/subghz/scenes/subghz_scene_start.c index f37ccae9e..0b1c3c159 100644 --- a/applications/main/subghz/scenes/subghz_scene_start.c +++ b/applications/main/subghz/scenes/subghz_scene_start.c @@ -1,4 +1,5 @@ #include "../subghz_i.h" +#include enum SubmenuIndex { SubmenuIndexRead = 10, @@ -84,6 +85,7 @@ bool subghz_scene_start_on_event(void* context, SceneManagerEvent event) { scene_manager_set_scene_state( subghz->scene_manager, SubGhzSceneStart, SubmenuIndexFrequencyAnalyzer); scene_manager_next_scene(subghz->scene_manager, SubGhzSceneFrequencyAnalyzer); + DOLPHIN_DEED(DolphinDeedSubGhzFrequencyAnalyzer); return true; } else if(event.event == SubmenuIndexTest) { scene_manager_set_scene_state( diff --git a/applications/main/subghz/scenes/subghz_scene_transmitter.c b/applications/main/subghz/scenes/subghz_scene_transmitter.c index 5da6f4300..fbe954f0c 100644 --- a/applications/main/subghz/scenes/subghz_scene_transmitter.c +++ b/applications/main/subghz/scenes/subghz_scene_transmitter.c @@ -50,7 +50,6 @@ bool subghz_scene_transmitter_update_data_show(void* context) { void subghz_scene_transmitter_on_enter(void* context) { SubGhz* subghz = context; - DOLPHIN_DEED(DolphinDeedSubGhzSend); if(!subghz_scene_transmitter_update_data_show(subghz)) { view_dispatcher_send_custom_event( subghz->view_dispatcher, SubGhzCustomEventViewTransmitterError); @@ -78,6 +77,7 @@ bool subghz_scene_transmitter_on_event(void* context, SceneManagerEvent event) { } else { subghz->state_notifications = SubGhzNotificationStateTx; subghz_scene_transmitter_update_data_show(subghz); + DOLPHIN_DEED(DolphinDeedSubGhzSend); } } return true; diff --git a/applications/services/dolphin/helpers/dolphin_deed.c b/applications/services/dolphin/helpers/dolphin_deed.c index ce3e058b5..51db56fdf 100644 --- a/applications/services/dolphin/helpers/dolphin_deed.c +++ b/applications/services/dolphin/helpers/dolphin_deed.c @@ -21,8 +21,8 @@ static const DolphinDeedWeight dolphin_deed_weights[] = { {1, DolphinAppNfc}, // DolphinDeedNfcDetectReader {2, DolphinAppNfc}, // DolphinDeedNfcEmulate {2, DolphinAppNfc}, // DolphinDeedNfcMfcAdd - {1, DolphinAppNfc}, // DolphinDeedNfcMfulError {1, DolphinAppNfc}, // DolphinDeedNfcAddSave + {1, DolphinAppNfc}, // DolphinDeedNfcAddEmulate {1, DolphinAppIr}, // DolphinDeedIrSend {3, DolphinAppIr}, // DolphinDeedIrLearnSuccess diff --git a/applications/services/dolphin/helpers/dolphin_deed.h b/applications/services/dolphin/helpers/dolphin_deed.h index abe027d79..c9cd18f31 100644 --- a/applications/services/dolphin/helpers/dolphin_deed.h +++ b/applications/services/dolphin/helpers/dolphin_deed.h @@ -37,8 +37,8 @@ typedef enum { DolphinDeedNfcDetectReader, DolphinDeedNfcEmulate, DolphinDeedNfcMfcAdd, - DolphinDeedNfcMfulError, DolphinDeedNfcAddSave, + DolphinDeedNfcAddEmulate, DolphinDeedIrSend, DolphinDeedIrLearnSuccess, From 26f852839a93f82a3d863fc6a6e64c9ebd7e6f0a Mon Sep 17 00:00:00 2001 From: Skorpionm <85568270+Skorpionm@users.noreply.github.com> Date: Fri, 28 Oct 2022 15:16:54 +0400 Subject: [PATCH 13/31] WS: fix Acurite-606TX protocol (#1938) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * WS: fix acurite_606tx protocol * WS: update version * WeatherStation: remove break from invalid place Co-authored-by: あく --- .../helpers/weather_station_types.h | 2 +- .../weather_station/protocols/acurite_606tx.c | 64 +++++++++---------- 2 files changed, 31 insertions(+), 35 deletions(-) diff --git a/applications/plugins/weather_station/helpers/weather_station_types.h b/applications/plugins/weather_station/helpers/weather_station_types.h index 479638b98..2976cbce8 100644 --- a/applications/plugins/weather_station/helpers/weather_station_types.h +++ b/applications/plugins/weather_station/helpers/weather_station_types.h @@ -3,7 +3,7 @@ #include #include -#define WS_VERSION_APP "0.3" +#define WS_VERSION_APP "0.3.1" #define WS_DEVELOPED "SkorP" #define WS_GITHUB "https://github.com/flipperdevices/flipperzero-firmware" diff --git a/applications/plugins/weather_station/protocols/acurite_606tx.c b/applications/plugins/weather_station/protocols/acurite_606tx.c index a92ec243c..3c9144057 100644 --- a/applications/plugins/weather_station/protocols/acurite_606tx.c +++ b/applications/plugins/weather_station/protocols/acurite_606tx.c @@ -151,41 +151,37 @@ void ws_protocol_decoder_acurite_606tx_feed(void* context, bool level, uint32_t case Acurite_606TXDecoderStepCheckDuration: if(!level) { - if((DURATION_DIFF(instance->decoder.te_last, ws_protocol_acurite_606tx_const.te_short) < - ws_protocol_acurite_606tx_const.te_delta) && - (DURATION_DIFF(duration, ws_protocol_acurite_606tx_const.te_short) < - ws_protocol_acurite_606tx_const.te_delta)) { - //Found syncPostfix - instance->decoder.parser_step = Acurite_606TXDecoderStepReset; - if((instance->decoder.decode_count_bit == - ws_protocol_acurite_606tx_const.min_count_bit_for_found) && - ws_protocol_acurite_606tx_check(instance)) { - instance->generic.data = instance->decoder.decode_data; - instance->generic.data_count_bit = instance->decoder.decode_count_bit; - ws_protocol_acurite_606tx_remote_controller(&instance->generic); - if(instance->base.callback) - instance->base.callback(&instance->base, instance->base.context); + if(DURATION_DIFF(instance->decoder.te_last, ws_protocol_acurite_606tx_const.te_short) < + ws_protocol_acurite_606tx_const.te_delta) { + if((DURATION_DIFF(duration, ws_protocol_acurite_606tx_const.te_short) < + ws_protocol_acurite_606tx_const.te_delta) || + (duration > ws_protocol_acurite_606tx_const.te_long * 3)) { + //Found syncPostfix + instance->decoder.parser_step = Acurite_606TXDecoderStepReset; + if((instance->decoder.decode_count_bit == + ws_protocol_acurite_606tx_const.min_count_bit_for_found) && + ws_protocol_acurite_606tx_check(instance)) { + instance->generic.data = instance->decoder.decode_data; + instance->generic.data_count_bit = instance->decoder.decode_count_bit; + ws_protocol_acurite_606tx_remote_controller(&instance->generic); + if(instance->base.callback) + instance->base.callback(&instance->base, instance->base.context); + } + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + } else if( + DURATION_DIFF(duration, ws_protocol_acurite_606tx_const.te_long) < + ws_protocol_acurite_606tx_const.te_delta * 2) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + instance->decoder.parser_step = Acurite_606TXDecoderStepSaveDuration; + } else if( + DURATION_DIFF(duration, ws_protocol_acurite_606tx_const.te_long * 2) < + ws_protocol_acurite_606tx_const.te_delta * 4) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + instance->decoder.parser_step = Acurite_606TXDecoderStepSaveDuration; + } else { + instance->decoder.parser_step = Acurite_606TXDecoderStepReset; } - instance->decoder.decode_data = 0; - instance->decoder.decode_count_bit = 0; - - break; - } else if( - (DURATION_DIFF( - instance->decoder.te_last, ws_protocol_acurite_606tx_const.te_short) < - ws_protocol_acurite_606tx_const.te_delta) && - (DURATION_DIFF(duration, ws_protocol_acurite_606tx_const.te_long) < - ws_protocol_acurite_606tx_const.te_delta * 2)) { - subghz_protocol_blocks_add_bit(&instance->decoder, 0); - instance->decoder.parser_step = Acurite_606TXDecoderStepSaveDuration; - } else if( - (DURATION_DIFF( - instance->decoder.te_last, ws_protocol_acurite_606tx_const.te_short) < - ws_protocol_acurite_606tx_const.te_delta) && - (DURATION_DIFF(duration, ws_protocol_acurite_606tx_const.te_long * 2) < - ws_protocol_acurite_606tx_const.te_delta * 4)) { - subghz_protocol_blocks_add_bit(&instance->decoder, 1); - instance->decoder.parser_step = Acurite_606TXDecoderStepSaveDuration; } else { instance->decoder.parser_step = Acurite_606TXDecoderStepReset; } From be3ee9f2fe7a06fc2cd8e959c3dcb3e7fde374a7 Mon Sep 17 00:00:00 2001 From: Oleg Moiseenko <807634+merlokk@users.noreply.github.com> Date: Fri, 28 Oct 2022 16:42:59 +0300 Subject: [PATCH 14/31] Oregon2 additional sensors defines (#1933) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * added a list of sensors and added several additional temperature and temperature/humidity sensor id's * now here are only sensors that have test data Co-authored-by: あく --- .../weather_station/protocols/oregon2.c | 52 ++++++++++++++++--- 1 file changed, 45 insertions(+), 7 deletions(-) diff --git a/applications/plugins/weather_station/protocols/oregon2.c b/applications/plugins/weather_station/protocols/oregon2.c index 76bc3f0a1..d294548e6 100644 --- a/applications/plugins/weather_station/protocols/oregon2.c +++ b/applications/plugins/weather_station/protocols/oregon2.c @@ -29,6 +29,40 @@ static const SubGhzBlockConst ws_oregon2_const = { // bit indicating the low battery #define OREGON2_FLAG_BAT_LOW 0x4 +/// Documentation for Oregon Scientific protocols can be found here: +/// http://wmrx00.sourceforge.net/Arduino/OregonScientific-RF-Protocols.pdf +// Sensors ID +#define ID_THGR122N 0x1d20 +#define ID_THGR968 0x1d30 +#define ID_BTHR918 0x5d50 +#define ID_BHTR968 0x5d60 +#define ID_RGR968 0x2d10 +#define ID_THR228N 0xec40 +#define ID_THN132N 0xec40 // same as THR228N but different packet size +#define ID_RTGN318 0x0cc3 // warning: id is from 0x0cc3 and 0xfcc3 +#define ID_RTGN129 0x0cc3 // same as RTGN318 but different packet size +#define ID_THGR810 0xf824 // This might be ID_THGR81, but what's true is lost in (git) history +#define ID_THGR810a 0xf8b4 // unconfirmed version +#define ID_THN802 0xc844 +#define ID_PCR800 0x2914 +#define ID_PCR800a 0x2d14 // Different PCR800 ID - AU version I think +#define ID_WGR800 0x1984 +#define ID_WGR800a 0x1994 // unconfirmed version +#define ID_WGR968 0x3d00 +#define ID_UV800 0xd874 +#define ID_THN129 0xcc43 // THN129 Temp only +#define ID_RTHN129 0x0cd3 // RTHN129 Temp, clock sensors +#define ID_BTHGN129 0x5d53 // Baro, Temp, Hygro sensor +#define ID_UVR128 0xec70 +#define ID_THGR328N 0xcc23 // Temp & Hygro sensor similar to THR228N with 5 channel instead of 3 +#define ID_RTGR328N_1 0xdcc3 // RTGR328N_[1-5] RFclock(date &time)&Temp&Hygro sensor +#define ID_RTGR328N_2 0xccc3 +#define ID_RTGR328N_3 0xbcc3 +#define ID_RTGR328N_4 0xacc3 +#define ID_RTGR328N_5 0x9cc3 +#define ID_RTGR328N_6 0x8ce3 // RTGR328N_6&7 RFclock(date &time)&Temp&Hygro sensor like THGR328N +#define ID_RTGR328N_7 0x8ae3 + struct WSProtocolDecoderOregon2 { SubGhzProtocolDecoderBase base; @@ -101,9 +135,12 @@ static ManchesterEvent level_and_duration_to_event(bool level, uint32_t duration } // From sensor id code return amount of bits in variable section +// https://temofeev.ru/info/articles/o-dekodirovanii-protokola-pogodnykh-datchikov-oregon-scientific static uint8_t oregon2_sensor_id_var_bits(uint16_t sensor_id) { - if(sensor_id == 0xEC40) return 16; - if(sensor_id == 0x1D20) return 24; + if(sensor_id == ID_THR228N) return 16; + + if(sensor_id == ID_THGR122N) return 24; + return 0; } @@ -134,15 +171,16 @@ static float ws_oregon2_decode_temp(uint32_t data) { } static void ws_oregon2_decode_var_data(WSBlockGeneric* ws_b, uint16_t sensor_id, uint32_t data) { - switch(sensor_id) { - case 0xEC40: + if(sensor_id == ID_THR228N) { ws_b->temp = ws_oregon2_decode_temp(data); ws_b->humidity = WS_NO_HUMIDITY; - break; - case 0x1D20: + return; + } + + if(sensor_id == ID_THGR122N) { ws_b->humidity = bcd_decode_short(data); ws_b->temp = ws_oregon2_decode_temp(data >> 8); - break; + return; } } From 492f147568a6f04896dfea6fba06a9494dfaa65d Mon Sep 17 00:00:00 2001 From: Konstantin Volkov <72250702+doomwastaken@users.noreply.github.com> Date: Fri, 28 Oct 2022 16:59:09 +0300 Subject: [PATCH 15/31] [FL-2887] actions unit tests runner (#1920) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Konstantin Volkov Co-authored-by: あく --- .github/workflows/unit_tests.yml | 56 ++++++++++++++++++++++ scripts/flipper/storage.py | 13 ++++++ scripts/storage.py | 16 +++++++ scripts/testing/await_flipper.py | 48 +++++++++++++++++++ scripts/testing/units.py | 79 ++++++++++++++++++++++++++++++++ 5 files changed, 212 insertions(+) create mode 100644 .github/workflows/unit_tests.yml create mode 100644 scripts/testing/await_flipper.py create mode 100644 scripts/testing/units.py diff --git a/.github/workflows/unit_tests.yml b/.github/workflows/unit_tests.yml new file mode 100644 index 000000000..8c5bac2a2 --- /dev/null +++ b/.github/workflows/unit_tests.yml @@ -0,0 +1,56 @@ +name: 'Unit tests' + +on: + pull_request: + +env: + TARGETS: f7 + DEFAULT_TARGET: f7 + +jobs: + main: + runs-on: [self-hosted, FlipperZeroTest] + steps: + - name: Checkout code + uses: actions/checkout@v2 + with: + fetch-depth: 0 + ref: ${{ github.event.pull_request.head.sha }} + + - name: 'Get flipper from device manager (mock)' + id: device + run: | + echo "flipper=/dev/ttyACM0" >> $GITHUB_OUTPUT + + - name: 'Compile unit tests firmware' + id: compile + continue-on-error: true + run: | + FBT_TOOLCHAIN_PATH=/opt ./fbt flash OPENOCD_ADAPTER_SERIAL=2A0906016415303030303032 FIRMWARE_APP_SET=unit_tests FORCE=1 + + - name: 'Wait for flipper to finish updating' + id: connect + if: steps.compile.outcome == 'success' + continue-on-error: true + run: | + python3 ./scripts/testing/await_flipper.py ${{steps.device.outputs.flipper}} + + - name: 'Format flipper SD card' + id: format + if: steps.connect.outcome == 'success' + continue-on-error: true + run: | + ./scripts/storage.py -p ${{steps.device.outputs.flipper}} format_ext + + - name: 'Copy unit tests to flipper' + id: copy + if: steps.format.outcome == 'success' + continue-on-error: true + run: | + ./scripts/storage.py -p ${{steps.device.outputs.flipper}} send assets/unit_tests /ext/unit_tests + + - name: 'Run units and validate results' + if: steps.copy.outcome == 'success' + continue-on-error: true + run: | + python3 ./scripts/testing/units.py ${{steps.device.outputs.flipper}} diff --git a/scripts/flipper/storage.py b/scripts/flipper/storage.py index 5fa8a2c81..6b53f7477 100644 --- a/scripts/flipper/storage.py +++ b/scripts/flipper/storage.py @@ -340,6 +340,19 @@ class FlipperStorage: else: return True + def format_ext(self): + """Create a directory on Flipper""" + self.send_and_wait_eol("storage format /ext\r") + self.send_and_wait_eol("y\r") + answer = self.read.until(self.CLI_EOL) + self.read.until(self.CLI_PROMPT) + + if self.has_error(answer): + self.last_error = self.get_error(answer) + return False + else: + return True + def remove(self, path): """Remove file or directory on Flipper""" self.send_and_wait_eol('storage remove "' + path + '"\r') diff --git a/scripts/storage.py b/scripts/storage.py index 167ba88eb..ee5dabd43 100755 --- a/scripts/storage.py +++ b/scripts/storage.py @@ -21,6 +21,11 @@ class Main(App): self.parser_mkdir.add_argument("flipper_path", help="Flipper path") self.parser_mkdir.set_defaults(func=self.mkdir) + self.parser_format = self.subparsers.add_parser( + "format_ext", help="Format flash card" + ) + self.parser_format.set_defaults(func=self.format_ext) + self.parser_remove = self.subparsers.add_parser( "remove", help="Remove file/directory" ) @@ -275,6 +280,17 @@ class Main(App): storage.stop() return 0 + def format_ext(self): + if not (storage := self._get_storage()): + return 1 + + self.logger.debug("Formatting /ext SD card") + + if not storage.format_ext(): + self.logger.error(f"Error: {storage.last_error}") + storage.stop() + return 0 + def stress(self): self.logger.error("This test is wearing out flash memory.") self.logger.error("Never use it with internal storage(/int)") diff --git a/scripts/testing/await_flipper.py b/scripts/testing/await_flipper.py new file mode 100644 index 000000000..1f0d16194 --- /dev/null +++ b/scripts/testing/await_flipper.py @@ -0,0 +1,48 @@ +#!/usr/bin/env python3 + +import sys, os, time + + +def flp_serial_by_name(flp_name): + if sys.platform == "darwin": # MacOS + flp_serial = "/dev/cu.usbmodemflip_" + flp_name + "1" + elif sys.platform == "linux": # Linux + flp_serial = ( + "/dev/serial/by-id/usb-Flipper_Devices_Inc._Flipper_" + + flp_name + + "_flip_" + + flp_name + + "-if00" + ) + + if os.path.exists(flp_serial): + return flp_serial + else: + if os.path.exists(flp_name): + return flp_name + else: + return "" + + +UPDATE_TIMEOUT = 30 + + +def main(): + flipper_name = sys.argv[1] + elapsed = 0 + flipper = flp_serial_by_name(flipper_name) + + while flipper == "" and elapsed < UPDATE_TIMEOUT: + elapsed += 1 + time.sleep(1) + flipper = flp_serial_by_name(flipper_name) + + if flipper == "": + print(f"Cannot find {flipper_name} flipper. Guess your flipper swam away") + sys.exit(1) + + sys.exit(0) + + +if __name__ == "__main__": + main() diff --git a/scripts/testing/units.py b/scripts/testing/units.py new file mode 100644 index 000000000..83b07899a --- /dev/null +++ b/scripts/testing/units.py @@ -0,0 +1,79 @@ +#!/usr/bin/env python3 + +import sys, os +import serial +import re + +from await_flipper import flp_serial_by_name + + +LEAK_THRESHOLD = 3000 # added until units are fixed + + +def main(): + flp_serial = flp_serial_by_name(sys.argv[1]) + + if flp_serial == "": + print("Name or serial port is invalid") + sys.exit(1) + + with serial.Serial(flp_serial, timeout=1) as flipper: + flipper.baudrate = 230400 + flipper.flushOutput() + flipper.flushInput() + + flipper.timeout = 300 + + flipper.read_until(b">: ").decode("utf-8") + flipper.write(b"unit_tests\r") + data = flipper.read_until(b">: ").decode("utf-8") + + lines = data.split("\r\n") + + tests_re = r"Failed tests: \d{0,}" + time_re = r"Consumed: \d{0,}" + leak_re = r"Leaked: \d{0,}" + status_re = r"Status: \w{3,}" + + tests_pattern = re.compile(tests_re) + time_pattern = re.compile(time_re) + leak_pattern = re.compile(leak_re) + status_pattern = re.compile(status_re) + + tests, time, leak, status = None, None, None, None + + for line in lines: + print(line) + if not tests: + tests = re.match(tests_pattern, line) + if not time: + time = re.match(time_pattern, line) + if not leak: + leak = re.match(leak_pattern, line) + if not status: + status = re.match(status_pattern, line) + + if leak is None or time is None or leak is None or status is None: + print("Failed to get data. Or output is corrupt") + sys.exit(1) + + leak = int(re.findall(r"[- ]\d+", leak.group(0))[0]) + status = re.findall(r"\w+", status.group(0))[1] + tests = int(re.findall(r"\d+", tests.group(0))[0]) + time = int(re.findall(r"\d+", time.group(0))[0]) + + if tests > 0 or leak > LEAK_THRESHOLD or status != "PASSED": + print(f"Got {tests} failed tests.") + print(f"Leaked {leak} bytes.") + print(f"Status by flipper: {status}") + print(f"Time elapsed {time/1000} seconds.") + sys.exit(1) + + print( + f"Tests ran successfully! Time elapsed {time/1000} seconds. Passed {tests} tests." + ) + sys.exit(0) + + +if __name__ == "__main__": + main() From 3434305630b4e76f20d703e119c9f3ad1df9cf4b Mon Sep 17 00:00:00 2001 From: Sergey Gavrilov Date: Sat, 29 Oct 2022 00:08:50 +1000 Subject: [PATCH 16/31] [FL-2937] Remove resources from API to prevent frequent API version increase (#1935) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Remove all icons from API * Music player: icons * Signal generator: icons * Bt hid: icons * Weather station: icons * Picopass: icons * File browser test: icons * Example images: documentation * Remove global assets header * Fix picopass Co-authored-by: あく --- .../debug/file_browser_test/application.fam | 1 + .../file_browser_test/file_browser_app.c | 2 +- .../file_browser_test/icons/badusb_10px.png | Bin 0 -> 576 bytes .../examples/example_images/ReadMe.md | 24 +++ applications/main/bad_usb/bad_usb_app_i.h | 1 + .../main/bad_usb/views/bad_usb_view.c | 1 + applications/main/fap_loader/fap_loader_app.c | 1 + applications/main/gpio/gpio_app_i.h | 1 + applications/main/ibutton/ibutton_i.h | 1 + applications/main/infrared/infrared_i.h | 1 + applications/main/lfrfid/lfrfid_i.h | 1 + .../main/lfrfid/views/lfrfid_view_read.c | 1 + applications/main/nfc/nfc_i.h | 1 + applications/main/nfc/views/detect_reader.c | 2 +- applications/main/subghz/subghz_i.h | 1 + applications/main/u2f/u2f_app_i.h | 1 + applications/main/u2f/views/u2f_view.c | 1 + .../bt_hid_app/assets/ButtonDown_7x4.png | Bin 0 -> 102 bytes .../bt_hid_app/assets/ButtonLeft_4x7.png | Bin 0 -> 1415 bytes .../bt_hid_app/assets/ButtonRight_4x7.png | Bin 0 -> 1839 bytes .../bt_hid_app/assets/ButtonUp_7x4.png | Bin 0 -> 102 bytes .../plugins/bt_hid_app/assets/Ok_btn_9x9.png | Bin 0 -> 3605 bytes .../bt_hid_app/assets/Pin_arrow_down_7x9.png | Bin 0 -> 3607 bytes .../bt_hid_app/assets/Pin_arrow_left_9x7.png | Bin 0 -> 3603 bytes .../bt_hid_app/assets/Pin_arrow_right_9x7.png | Bin 0 -> 3602 bytes .../bt_hid_app/assets/Pin_arrow_up_7x9.png | Bin 0 -> 3603 bytes .../bt_hid_app/assets/Pin_back_arrow_10x8.png | Bin 0 -> 3606 bytes .../plugins/music_player/application.fam | 3 +- .../plugins/music_player/icons/music_10px.png | Bin 0 -> 142 bytes .../plugins/music_player/music_player.c | 2 +- applications/plugins/picopass/application.fam | 1 + .../picopass/icons/DolphinMafia_115x62.png | Bin 0 -> 2504 bytes .../picopass/icons/DolphinNice_96x59.png | Bin 0 -> 2459 bytes .../plugins/picopass/icons/Nfc_10px.png | Bin 0 -> 304 bytes .../icons/RFIDDolphinReceive_97x61.png | Bin 0 -> 1421 bytes .../picopass/icons/RFIDDolphinSend_97x61.png | Bin 0 -> 1418 bytes .../plugins/picopass/picopass_device.c | 1 + applications/plugins/picopass/picopass_i.h | 1 + .../plugins/signal_generator/application.fam | 1 + .../icons/SmallArrowDown_3x5.png | Bin 0 -> 3592 bytes .../icons/SmallArrowUp_3x5.png | Bin 0 -> 7976 bytes .../signal_generator/views/signal_gen_pwm.c | 1 + .../weather_station/images/Lock_7x8.png | Bin 0 -> 3597 bytes .../images/Pin_back_arrow_10x8.png | Bin 0 -> 3606 bytes .../weather_station/images/Quest_7x8.png | Bin 0 -> 3675 bytes .../images/Scanning_123x52.png | Bin 0 -> 1690 bytes .../weather_station/images/Unlock_7x8.png | Bin 0 -> 3598 bytes .../images/WarningDolphin_45x42.png | Bin 0 -> 1139 bytes .../views/weather_station_receiver.c | 3 +- applications/services/bt/bt_service/bt.c | 1 + .../desktop/views/desktop_view_lock_menu.c | 1 + .../desktop/views/desktop_view_locked.c | 1 + .../desktop/views/desktop_view_pin_input.c | 1 + applications/services/dialogs/dialogs_api.c | 1 + applications/services/gui/canvas.h | 2 +- applications/services/gui/gui.c | 1 + applications/services/gui/icon_animation.h | 2 +- .../services/gui/modules/button_menu.c | 1 + .../services/gui/modules/byte_input.c | 5 +- applications/services/gui/modules/menu.c | 1 + .../services/gui/modules/text_input.c | 1 + .../services/power/power_service/power_i.h | 1 + .../power/power_service/views/power_off.c | 1 + .../power_service/views/power_unplug_usb.c | 1 + applications/services/storage/storage.c | 1 + applications/settings/about/about.c | 1 + .../bt_settings_app/bt_settings_app.h | 1 + .../desktop_settings/desktop_settings_app.h | 1 + .../power_settings_app/power_settings_app.h | 1 + .../power_settings_app/views/battery_info.c | 1 + .../storage_settings/storage_settings.h | 1 + .../system/updater/views/updater_main.c | 1 + firmware/targets/f7/api_symbols.csv | 186 +----------------- 73 files changed, 74 insertions(+), 195 deletions(-) create mode 100644 applications/debug/file_browser_test/icons/badusb_10px.png create mode 100644 applications/examples/example_images/ReadMe.md create mode 100644 applications/plugins/bt_hid_app/assets/ButtonDown_7x4.png create mode 100644 applications/plugins/bt_hid_app/assets/ButtonLeft_4x7.png create mode 100644 applications/plugins/bt_hid_app/assets/ButtonRight_4x7.png create mode 100644 applications/plugins/bt_hid_app/assets/ButtonUp_7x4.png create mode 100644 applications/plugins/bt_hid_app/assets/Ok_btn_9x9.png create mode 100644 applications/plugins/bt_hid_app/assets/Pin_arrow_down_7x9.png create mode 100644 applications/plugins/bt_hid_app/assets/Pin_arrow_left_9x7.png create mode 100644 applications/plugins/bt_hid_app/assets/Pin_arrow_right_9x7.png create mode 100644 applications/plugins/bt_hid_app/assets/Pin_arrow_up_7x9.png create mode 100644 applications/plugins/bt_hid_app/assets/Pin_back_arrow_10x8.png create mode 100644 applications/plugins/music_player/icons/music_10px.png create mode 100644 applications/plugins/picopass/icons/DolphinMafia_115x62.png create mode 100644 applications/plugins/picopass/icons/DolphinNice_96x59.png create mode 100644 applications/plugins/picopass/icons/Nfc_10px.png create mode 100644 applications/plugins/picopass/icons/RFIDDolphinReceive_97x61.png create mode 100644 applications/plugins/picopass/icons/RFIDDolphinSend_97x61.png create mode 100644 applications/plugins/signal_generator/icons/SmallArrowDown_3x5.png create mode 100644 applications/plugins/signal_generator/icons/SmallArrowUp_3x5.png create mode 100644 applications/plugins/weather_station/images/Lock_7x8.png create mode 100644 applications/plugins/weather_station/images/Pin_back_arrow_10x8.png create mode 100644 applications/plugins/weather_station/images/Quest_7x8.png create mode 100644 applications/plugins/weather_station/images/Scanning_123x52.png create mode 100644 applications/plugins/weather_station/images/Unlock_7x8.png create mode 100644 applications/plugins/weather_station/images/WarningDolphin_45x42.png diff --git a/applications/debug/file_browser_test/application.fam b/applications/debug/file_browser_test/application.fam index 5e4c7f467..4a401a649 100644 --- a/applications/debug/file_browser_test/application.fam +++ b/applications/debug/file_browser_test/application.fam @@ -8,4 +8,5 @@ App( stack_size=2 * 1024, order=150, fap_category="Debug", + fap_icon_assets="icons", ) diff --git a/applications/debug/file_browser_test/file_browser_app.c b/applications/debug/file_browser_test/file_browser_app.c index 5c7b93bcb..6cb50d385 100644 --- a/applications/debug/file_browser_test/file_browser_app.c +++ b/applications/debug/file_browser_test/file_browser_app.c @@ -1,4 +1,4 @@ -#include "assets_icons.h" +#include #include "file_browser_app_i.h" #include "gui/modules/file_browser.h" #include diff --git a/applications/debug/file_browser_test/icons/badusb_10px.png b/applications/debug/file_browser_test/icons/badusb_10px.png new file mode 100644 index 0000000000000000000000000000000000000000..037474aa3bc9c2e1aca79a68483e69980432bcf5 GIT binary patch literal 576 zcmV-G0>AxEX>4Tx04R}tkv&MmKpe$i(`rSk4t5Z6$WWau6cusQDionYs1;guFuC*#nlvOW zE{=k0!NHHks)LKOt`4q(Aou~|=;Wm6A|?JWDYS_3;J6>}?mh0_0Yan9G%FATG`(u3 z5^*t;T@{0`5D-8=V(6BcWz0!Z5}xDh9zMR_MR}I@xj#prnzI<-6NzV;VOEJZh^IHJ z2Iqa^Fe}O`@j3ChNf#u3C`-Nm{=@yu+qV-Xlle$#1U1~DPPFA zta9Gstd(o5bx;1nP)=W2<~q$0B(R7jND!f*h7!uCB1)@HiiH&I$36VRj$a~|Laq`R zITlcX2HEk0|H1EWt^DMKn-q!zT`#u%F$x5Cfo9#dzmILZc>?&Kfh)c3uQY&}Ptxmc zEph}5Yy%h9ZB5w&E_Z;TCqp)6NAlAY@_FF>jJ_!g4Bi60Yi@6?eVjf3Y3eF@0~{Oz zV+G1y_jq?tXK(+WY4!I5C=YUpXXIhH00006VoOIv0RI600RN!9r;`8x010qNS#tmY z3ljhU3ljkVnw%H_000McNliru<^lu`HWXkp{t5s906j@WK~xyijZi@f05Awj>HlAL zr$MwDdI>{Qf+U53tOUR#xOeyy)jcQo#JNRv)7r6DVVK|+*(cmT+R+EbO(O#X#REG4 O0000 +#include #include #include #include diff --git a/applications/main/bad_usb/views/bad_usb_view.c b/applications/main/bad_usb/views/bad_usb_view.c index 6c6a15847..e5c5d92a3 100644 --- a/applications/main/bad_usb/views/bad_usb_view.c +++ b/applications/main/bad_usb/views/bad_usb_view.c @@ -1,6 +1,7 @@ #include "bad_usb_view.h" #include "../bad_usb_script.h" #include +#include #define MAX_NAME_LEN 64 diff --git a/applications/main/fap_loader/fap_loader_app.c b/applications/main/fap_loader/fap_loader_app.c index faf8eefc8..9b4c92335 100644 --- a/applications/main/fap_loader/fap_loader_app.c +++ b/applications/main/fap_loader/fap_loader_app.c @@ -1,5 +1,6 @@ #include #include +#include #include #include #include diff --git a/applications/main/gpio/gpio_app_i.h b/applications/main/gpio/gpio_app_i.h index fff35c95a..85c5c332e 100644 --- a/applications/main/gpio/gpio_app_i.h +++ b/applications/main/gpio/gpio_app_i.h @@ -15,6 +15,7 @@ #include #include "views/gpio_test.h" #include "views/gpio_usb_uart.h" +#include struct GpioApp { Gui* gui; diff --git a/applications/main/ibutton/ibutton_i.h b/applications/main/ibutton/ibutton_i.h index a9515195e..0a8099351 100644 --- a/applications/main/ibutton/ibutton_i.h +++ b/applications/main/ibutton/ibutton_i.h @@ -4,6 +4,7 @@ #include #include +#include #include #include #include diff --git a/applications/main/infrared/infrared_i.h b/applications/main/infrared/infrared_i.h index 6d25d1609..5b555e4bb 100644 --- a/applications/main/infrared/infrared_i.h +++ b/applications/main/infrared/infrared_i.h @@ -2,6 +2,7 @@ #include #include +#include #include #include #include diff --git a/applications/main/lfrfid/lfrfid_i.h b/applications/main/lfrfid/lfrfid_i.h index 71917c0c2..72b061930 100644 --- a/applications/main/lfrfid/lfrfid_i.h +++ b/applications/main/lfrfid/lfrfid_i.h @@ -5,6 +5,7 @@ #include #include +#include #include #include #include diff --git a/applications/main/lfrfid/views/lfrfid_view_read.c b/applications/main/lfrfid/views/lfrfid_view_read.c index 0d4db6178..094afb617 100644 --- a/applications/main/lfrfid/views/lfrfid_view_read.c +++ b/applications/main/lfrfid/views/lfrfid_view_read.c @@ -1,5 +1,6 @@ #include "lfrfid_view_read.h" #include +#include #define TEMP_STR_LEN 128 diff --git a/applications/main/nfc/nfc_i.h b/applications/main/nfc/nfc_i.h index fa5b54edc..e9b36a3e9 100644 --- a/applications/main/nfc/nfc_i.h +++ b/applications/main/nfc/nfc_i.h @@ -7,6 +7,7 @@ #include #include +#include #include #include #include diff --git a/applications/main/nfc/views/detect_reader.c b/applications/main/nfc/views/detect_reader.c index 2dbb4338b..91537868b 100644 --- a/applications/main/nfc/views/detect_reader.c +++ b/applications/main/nfc/views/detect_reader.c @@ -1,5 +1,5 @@ #include "detect_reader.h" - +#include #include struct DetectReader { diff --git a/applications/main/subghz/subghz_i.h b/applications/main/subghz/subghz_i.h index 09830ba05..46768cf02 100644 --- a/applications/main/subghz/subghz_i.h +++ b/applications/main/subghz/subghz_i.h @@ -13,6 +13,7 @@ #include "views/subghz_test_packet.h" #include +#include #include #include #include diff --git a/applications/main/u2f/u2f_app_i.h b/applications/main/u2f/u2f_app_i.h index 53647859a..2896684c3 100644 --- a/applications/main/u2f/u2f_app_i.h +++ b/applications/main/u2f/u2f_app_i.h @@ -4,6 +4,7 @@ #include "scenes/u2f_scene.h" #include +#include #include #include #include diff --git a/applications/main/u2f/views/u2f_view.c b/applications/main/u2f/views/u2f_view.c index fa3d6cc24..181c495d0 100644 --- a/applications/main/u2f/views/u2f_view.c +++ b/applications/main/u2f/views/u2f_view.c @@ -1,5 +1,6 @@ #include "u2f_view.h" #include +#include struct U2fView { View* view; diff --git a/applications/plugins/bt_hid_app/assets/ButtonDown_7x4.png b/applications/plugins/bt_hid_app/assets/ButtonDown_7x4.png new file mode 100644 index 0000000000000000000000000000000000000000..2954bb6a67d1c23c0bb5d765e8d2aa04b9b5adec GIT binary patch literal 102 zcmeAS@N?(olHy`uVBq!ia0vp^>_E)I!3HFqj;YoHDIHH2#}J9|(o>FH3<^BV2haYO z-y5_sM4;GPjq%Ck6>60csmUj6EiNa>ORduPH4*)h!w|e3sE@(Z)z4*}Q$iC10Gods AV*mgE literal 0 HcmV?d00001 diff --git a/applications/plugins/bt_hid_app/assets/ButtonLeft_4x7.png b/applications/plugins/bt_hid_app/assets/ButtonLeft_4x7.png new file mode 100644 index 0000000000000000000000000000000000000000..0b4655d43247083aa705620e9836ac415b42ca46 GIT binary patch literal 1415 zcmbVM+iKK67*5rq)>aU2M7$VM1Vxif;vTv~W2u`S7ED{V3s&&L*<`XiG|9wd+THd> z5CnY!sdyuJtrvQyAo>KpiLcV|{Tkc)riAbluXfwSZCApL`ztB&p zx6LGKvks4K_4~)qD&oGa-YdJlW)hAKMNJd7<=t?6c^RI1>c$ifyjaM>^|&8!ey zB4!nh9u>5uen6Ve@<H5rru6h<2Ef#GQdQ*CmZOlQi~N!?9H`Rp;C% zU}CB21#?;r`&0|6C0}b-=jODa5|nEJ#ntxQ&{~jpgtwDta4hftr~G=#p@V36e4Zjh zq%J~{y26Jjn=1Nw-l*3%QW5YFE*v4z3gt0$&(*xf2en34c?JpH8+FYldo+Alvg8af-pG4(=!fyUi-Wsg z`g#n9VUcf(DFr{poMSNzw-lz>w+HV+n1ELr&SLA#LHUb0p(xWQ(1*vJ-i+1!`swxZ Z!O7;c$;lT_->m1Ovaz)0yuI`A$q$F8u*d)a literal 0 HcmV?d00001 diff --git a/applications/plugins/bt_hid_app/assets/ButtonRight_4x7.png b/applications/plugins/bt_hid_app/assets/ButtonRight_4x7.png new file mode 100644 index 0000000000000000000000000000000000000000..8e1c74c1c0038ea55172f19ac875003fc80c2d06 GIT binary patch literal 1839 zcmcIlO>f*p7#Yw)M6zw!O+@VZ{?d|D~WYi~8rHRY?X-&T}Yen`g$^+EJ;z+|RV zE@PoDvZ9%#+_}3bC_5Cj8jDGq541mi{7F+&KF}W65sr$Xn5H|YrMQ2(J7%Yc%;(zO z57ax000=TsQ+1Ke@+w#iw3au3cGGQWY740k2ijH>P(6tD)S)be>gX6Tj7`<`b>di- zgWp$8Y+?i31~CzF0&E4uRlA=C(Mp~K`{74jEchB|)4DDK!ZVhSwdFyw0YIZ1cDh0S{OvfO-U_~ zvmRF*m9sWDXNH)GOyqS1Skhxbr6}s*7t&@~kFM(NW5}qh?Lu@lJ}HE;FDiLdGO>LO z5pS*%E2grR)l^;|?O5b_?u0me&c1U}%jrk8*%=Wk%i)8yp2P|kuxmKg<=(u_`oQRI_0 zS`-DNysBx=#3&qSkgA@hJP>~D+ZM(s5jI6Owp`?yE=3e`YGUqkVOp#Cp=3wR3O4hX zX6BLsN3UBzV(vI5;|SZHgOb=HD0VFjpTyfFW}GnQuh>2*Q`k>*cAmA#iUT7EXSpo# zkPm5~#I-o^cpgfe#P$=4-Pi*SpT!-@nJgp8L347xe>5EKl`=_ZFc8XGy+_j=_R_7! z@vZZMowS1GJ?Zw)eetks%~G{BTR>T}9|jt0j3Btyb*C3-`C?fwY3EY`q*oYZ39DpM z&uJ;PCZPLs4QO1Jd_|A1PF)azZJ)RZ`^-VMWr6e#XUOA%3eLG_Ch@BDOHzMk*MF0G zCo7xMd?Mg*HMIXw%nNz?%60fZiZPlqb?GqUpXO`F&Yi!okZl(n>P@r1P2i)yk3DgRwbHeNn6e|;J^SK4TM LH~i+q&mR8;k>NTA literal 0 HcmV?d00001 diff --git a/applications/plugins/bt_hid_app/assets/ButtonUp_7x4.png b/applications/plugins/bt_hid_app/assets/ButtonUp_7x4.png new file mode 100644 index 0000000000000000000000000000000000000000..1be79328b40a93297a5609756328406565c437c0 GIT binary patch literal 102 zcmeAS@N?(olHy`uVBq!ia0vp^>_E)I!3HFqj;YoHDIHH2#}J8d-yTOk1_O>mFaFD) zeWb+ZHz{mGZZ1QpXe09^4tcYT#4oe=UbmGC^A-KE*|F&zP#=S*tDnm{r-UX30HgpM AM*si- literal 0 HcmV?d00001 diff --git a/applications/plugins/bt_hid_app/assets/Ok_btn_9x9.png b/applications/plugins/bt_hid_app/assets/Ok_btn_9x9.png new file mode 100644 index 0000000000000000000000000000000000000000..9a1539da2049f12f7b25f96b11a9c40cd8227302 GIT binary patch literal 3605 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 QX&eA7&1_6djNPvM5BL~PlmGw# literal 0 HcmV?d00001 diff --git a/applications/plugins/bt_hid_app/assets/Pin_arrow_left_9x7.png b/applications/plugins/bt_hid_app/assets/Pin_arrow_left_9x7.png new file mode 100644 index 0000000000000000000000000000000000000000..fb4ded78fde8d1bf4f053ba0b353e7acf14f031a GIT binary patch literal 3603 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 literal 0 HcmV?d00001 diff --git a/applications/plugins/bt_hid_app/assets/Pin_arrow_up_7x9.png b/applications/plugins/bt_hid_app/assets/Pin_arrow_up_7x9.png new file mode 100644 index 0000000000000000000000000000000000000000..a91a6fd5e99a72112e28865cd8a004c7d1933fff GIT binary patch literal 3603 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 TFC+3}9cE|kU{!4CefEC<&8td2 literal 0 HcmV?d00001 diff --git a/applications/plugins/music_player/application.fam b/applications/plugins/music_player/application.fam index 76787e097..a36988983 100644 --- a/applications/plugins/music_player/application.fam +++ b/applications/plugins/music_player/application.fam @@ -11,8 +11,9 @@ App( provides=["music_player_start"], stack_size=2 * 1024, order=20, - fap_icon="../../../assets/icons/Archive/music_10px.png", + fap_icon="icons/music_10px.png", fap_category="Misc", + fap_icon_assets="icons", ) App( diff --git a/applications/plugins/music_player/icons/music_10px.png b/applications/plugins/music_player/icons/music_10px.png new file mode 100644 index 0000000000000000000000000000000000000000..d41eb0db8c822c60be6c097393b3682680b81a6c GIT binary patch literal 142 zcmeAS@N?(olHy`uVBq!ia0vp^AT}2xGmzZ=C-xtZVhivIasA%~WHIdey2}7aaTa() z7Bet#3xhBt!>l_x9Asc&(0a_=e)W&n8K5!-Pgg&ebxsLQ0Ao%f>i_@% literal 0 HcmV?d00001 diff --git a/applications/plugins/music_player/music_player.c b/applications/plugins/music_player/music_player.c index 192500c2e..28872284b 100644 --- a/applications/plugins/music_player/music_player.c +++ b/applications/plugins/music_player/music_player.c @@ -3,7 +3,7 @@ #include #include -#include +#include #include #include #include diff --git a/applications/plugins/picopass/application.fam b/applications/plugins/picopass/application.fam index bbe37e064..88460b4ff 100644 --- a/applications/plugins/picopass/application.fam +++ b/applications/plugins/picopass/application.fam @@ -17,4 +17,5 @@ App( name="loclass", ), ], + fap_icon_assets="icons", ) diff --git a/applications/plugins/picopass/icons/DolphinMafia_115x62.png b/applications/plugins/picopass/icons/DolphinMafia_115x62.png new file mode 100644 index 0000000000000000000000000000000000000000..66fdb40ff2651916faed4a2ae1d564cafdbf7bcb GIT binary patch literal 2504 zcmbVO3se(V8ji5Kg5ZmV3I#ewZHbsZB8b0=g#+k z_y7La$vcsnwa$(njyxXES*27&ad(Ehf@a%szx(??vFC0MMrAy=Img9%&ES885R5X2P@K{dB9p<$p?SQ()g~i~r4cNkC3JdH&i}sg6d%yza(--p8dMv@ zh!njtmnNcfH8EIj8V2M1)j>d@3E>C~1d9SDLpsSICOLnC7va{{Z80C1fUs$Deu(uz zAWj_#gi$mBNJXF!13?Io!6J#&-(L!@03Z+o#bAI~0tqEj1oTHFGGOY%=T4*XWF$)Q z`>C_ICpkZbWsQhfoSmI5%Jvgcv`#F6VOR`8Vh9p)2qBY0vZzT&GE1fz6a<6OdLyf+ zNWjX7YNwhuAF&nut zlTM%T7{|m!I$L;81?0JCCML&7h@%LG%A_%3 zO%`|J5~~^`5=Ij!OVKeDl|G%Q$Z2^1BoRS?PpqEAscc5@GXp|_vV@$^WlbUkA?_Ok zK?n#V5acTX5fGe&s<}GAQ5OB*z!a`e&iO^CEx1S+l}^!W3g`Ur;{!N`BvZ5jW@%VH?>OF2Tk@O zPGOv@&rGEfX|lv0Cxk2gKu)ie6Af#Vr9x}>!CI+Aiv@szVry$~6u{(al2-hTBEgTzn_D^}jklllIvu1V{Q`ig6OgP|0jI zN)sVEE|=@hm?j7H6PqgYzU5==|fB0<6@J90B?N8); z?B48M`Q6&q<>QYftD|a*tJ$!0YduA;TS}(23t@i9jJ}9E&d>+O-{j}lDtd6mP7wiU?pLh0* zla-TQ!!6f>9b(>jct-Z*@vzVmEjaUp9adYyRH)W#u&{1)0G7#K8z}OOe9Z4J`?k~5 z;u#n4^?R%GdBZDjly!H8xtVMF9ud_Q|CsUp%X4BI?jMd19&&9{QqgG_a)Rz9J*BH| z$zM9cbZYA6R(n(=QYD(cO(#Aoy6CQh;hG<}_gRz&>ZIovmNuT&Z9VwM8m5pu&$kG$ zvTJ!+pA|E6E-UBtJJrv;*XaRo7|Z#x4L(qON`UQa?6`jZqnkg3XliTEuJKo%PCa~M z@WlnE3u1ZRT?c;b@m&$07PGImr1km-TQZ8*DS|rZudw{x4R!5F9=$VOt{XWj(Y>BT zd-yG`a(KJ-o0Dfs8h&U=J*C(_ z=8hNq6aC?^r7wqGy5!v`zvX@KNEDDEpXqBVXiB`Z=eNZRgGG2tG`F;x~xDn9)G1Y@4Fl28Px*E!|ivy@~-8Lx%@`DyQ}?V z4f!BGF*jl}N~1D%!=YeZY6W)9lyDw_Uq#NDJx^=CJZDD2|CF# zA7Ixt{Z7BT8@4fZgFkI{D9fJxang<$JS``+d(*81cbB@prG*c!rZ)8U4y-<__Pt)Z zZ3lJfK;Y5eZHd?A3O-!mWX3$UChhmy)r@4iKkvyz(mdTtF7?TWn4`7t4=} zZ`OLe!fHzEo3eUH7jwVD-n?Xnx$AC<-H6`;RB2iYH9UO}ROfZkPOl32mRZ%`xW#FL zD@GqK${E&#=gzidc(qkxLZ^tk7u}u0Uu|;00}}A@rq4$9xE75>Hwj!4$Nk!`)YmDg{{4HeKCy?7Z85xPzg%Peucca}QJ6#D*z!+`G0ZOj literal 0 HcmV?d00001 diff --git a/applications/plugins/picopass/icons/DolphinNice_96x59.png b/applications/plugins/picopass/icons/DolphinNice_96x59.png new file mode 100644 index 0000000000000000000000000000000000000000..a299d3630239b4486e249cc501872bed5996df3b GIT binary patch literal 2459 zcmbVO3s4i+8V(M(gEFORwSrA`4O0uPn|M|5y* zB*aMDxC&7(gP9JN;POOi-9khrC>Z9YJs2U!LnVcQEEC0fDtKo&ILlzb30%M}3J^;~ zv7RzcsilOs4Mq@tD*&R;!LMSk2A~{(`HK9|hQBqEX)3sQr9Je6SZU*F-^fD-p+~Hs; zHLkO%v?>ZoxEv+F#whudr%615FkA0DYR0tMEo}3OOY#xecLWe>xV?u5KtSmC^ z7)Fmj6gjfKstiEV-*Cxbbb+&rRWuI_rBJ)ybs_f1Rn&f2>q3pYwI^|J(hdn{j{0EZIm_F zpIyIWLsRUgOItR-dUbVd|6Zo=_BU_Tj4|{{jxO#=JH4o8er(5{!nZD_j4}MH&zh~9 zVLC~y(0-D6GO0ghZD8BYzP?o{>22~lT6^d@X{SwQ8vrNY-PPIMajIwC)`s14Ep72@ zeq7YOzM`?U{+W)ocXBr`eSOcpk?Rxc=ou5&)fWW|pD};-Z0mvk9}=&`Rb&y<77W~a z(>6YM;6Y5aIU~JKZ}mQZynKHiSTQ#Bczn@&jTiN^?vPJ(jhm7cXLx0oum5P$`TceG zU+wR;OO^)8CVlnM)5p$CO&e94KJt>HccCaHGusmW_b`T6m| z-R6V6Db1pErTot?^d22ojm+2>_)FbD`_+WbDGMx9f@hO27maS2`csiV(D&Fs`PS2& zvrq18du_&zXID(!KIxsU$)iuTYuZ?zmYiP&n&i@Be{IdbS-jA2c0QAlu5NXQv_0K< z3Hvs4eeu6B7yD&CNT~gIkMV&UkRU=V!iQ(+_(O&u^ah$+s{_yn(yBYeD40HeU{xGsIT6W Zfq!wOp!Q<>&pI=m5)b(dHL6nbwD9yPZ!4!j_b)k${QZ;XFmLn zjqNsD+YPq1J8W%_*aXBie!OR3*tC!PwU_7Q9H4U564!{5l*E!$tK_0oAjM#0U}UIk zV5)0q5@Kj%Wo%$&Y@uynU}a#izM}XGiiX_$l+3hBs0L%8o)7~QD^p9LQiz6svOM}g O4Gf;HelF{r5}E+GUQp8j literal 0 HcmV?d00001 diff --git a/applications/plugins/picopass/icons/RFIDDolphinReceive_97x61.png b/applications/plugins/picopass/icons/RFIDDolphinReceive_97x61.png new file mode 100644 index 0000000000000000000000000000000000000000..e1f5f9f8017b49ab1ea78a9394ed8ea7684e3083 GIT binary patch literal 1421 zcmaJ>eQXnD7{Agv#{x2p_yIvKhl^r%z3;AfTbW%yMuEcUiZn{X?&IxtS=&4BZnPU= zlx>i)WI>$aQvw7<5XB`bIuRj@1WD9@Lr2D(5=YPwGc*zsSTf&kEAj{7lDqeL-}m`F zpTFm}Rj;U;Sva>4L6DijCB86RMfkc4?C{(9_MQ1~dCu}jtr{(6r9=ZD9z~M?8cc|F zAPhvM>5U7Z96{_EH4?R=q2+?CB^+W_$B|Cx5RD+^6=_|R8-RsMpiWJ?vC&g!FjQ6C z*cvWGhIB8eSC=#!pr(06L~d@7c?GLjjFzVbXdnSB5ltuJNmEF>u?f2Zl(WYKhEAwh z4Q^~QsA#Af^=bw{oemP0Nz#dy@(x9mL|KwbP@1GEf@BGb#Ys|Nc!6cnsRx7Z3?(Ln zeSs-waOcMAElU>&B9%%xQj9}0>IjPGd4i+~n#Q39ZZ;(?F^wn9g*gj8V9JK7TdI~s zvlc~3YqZ=L40SSxgdPgrH=H!5Dg|psq(z;e93+uQWD}dvHmxxDKa7WJn~^3R5Mf|y zjfM;x5?h!9!{R;KQC1N~Bdj!3*cCDE)8xhkNLoRk8-q6vMO6faWjLq8D>(0>TsY@s zOL2+gT{ut5lFP+&G;q>6I}gJ}uK`3$Ga{N6&(WZ|Ub8f_Uei&p7kz1snpCuuxhUJA$%K8tP}c(` zU}y<+qQrvwF!u}>V`HR<(=n36VBt1B^`_fx&WPzU_AMY;oo7=&mIg2tu^u1f5 z)@y#lGg2HF{icooYxXeey6HJl+%===Q-Yg*f$J(< z+gbGCvVprluc__jmS6m=F>l7JjJ;Cb^sMdho~B4w{1|(u#k_H5R;4;`zs)u0gC*%S zI_>C5rsHbY>U}-r=8b&^Mh7zat>Eaqs$E;p%^t}^&M*C`d_!V*2g<#^ZLQq9;N6x= zv^)OzpYh#+OwHKfQ+kHHZreNi()*6Nw&PX5?kxF@U2EB*+}LH?toC1`{oRjksXb78 zx8u;V!Qv~6!ySjp4u16f-y8F;3}d=*b!=ao^)Gw)nS({6qa!CbyuwrWMvi?_zz4rL rb-KI#{JuTj%qEZPotyLfwj*}ruaRky;O7Gyvp>k7e}(TvWo_$!Vg&g_ literal 0 HcmV?d00001 diff --git a/applications/plugins/picopass/icons/RFIDDolphinSend_97x61.png b/applications/plugins/picopass/icons/RFIDDolphinSend_97x61.png new file mode 100644 index 0000000000000000000000000000000000000000..380a970d9004cba5520560fd9aa24aa42924e2a1 GIT binary patch literal 1418 zcmaJ>eQXnD7{6`EHeeAZ8ii;s2g4C}y=!}S>mBQswzrLLl$EYRfoyOe?`_T2-g$Sk z9pb2CPQ(a0XM$+dKhO~O0nx;1L}kUOGdh8Ve-^?LG)CD7lOfXp&bQl&{IPJ!-TS=n z`~05I-*YefH&^B@S+xW~kUZ~3J^)t%zRsL1_&wM?{Wx46Gs{C}t*V$YK?jISRz-k% zBSHfR06}hjW(brZNLC^o44EO{CQec#79pi$iAOYuMv#)SxF$$Vz(hsR5RN*rYhQeg zp<&sHZKHjpPxFAr@WwqlsNJ(UDD7#ISQ#rTMN8rwG!Ox%fW{-uQG<&+v01wulvBq9 zhR&*(O-^hssF2T(dQ=^tjD^G{l4Q_g)*=g{AcBJ%MzrGu-R~^fg7z+Q;6eHV@=uu4-82U zYi3xDqA81lsJ56+42C+FLqzlW?i!97^Ob@%BjSQaSS=(GiKG&n)i%rk_&3wK+`#f1_%uMx&~s9uHc$EgY5An6W<9p}B;4 zpogCYa)qu&(Ag4m;RW0?c3PnnQowBrN#lw@*>TY-L0(ZbMKQG9>QDeSkC*Q$-5f{Z z2~0stN5WYZfssX-#AS)L;{r{IxG2~K90(F6+7!i3SxJn5ArdLp+{2>u5u|2HygL+d zb9byj6wZ} zqrIB@aESUiV~B&zwY0sUci%;mf;cmkA+7cD0^$ih9{f{w;v_DJ`sY;R`f3( z?7BXf_vMbW zuU1_w753GAG_~{axB58aI?KM!#N|b)zyZV)ZU9QaOj9KuN$fX{&>fy=f`f8Io+CbZIMpovDCx1HL z?$&C^=R1DyispWLc%|FSKGs*ccUMOLz=7=zt7r7(!|y7;X08;c-@aJ>V5pwIR`S;) wTk7+73`}?J{<7dJ@~ literal 0 HcmV?d00001 diff --git a/applications/plugins/picopass/picopass_device.c b/applications/plugins/picopass/picopass_device.c index b6e69cc21..199b79e97 100644 --- a/applications/plugins/picopass/picopass_device.c +++ b/applications/plugins/picopass/picopass_device.c @@ -2,6 +2,7 @@ #include #include +#include #define TAG "PicopassDevice" diff --git a/applications/plugins/picopass/picopass_i.h b/applications/plugins/picopass/picopass_i.h index 8e011f222..469a672b7 100644 --- a/applications/plugins/picopass/picopass_i.h +++ b/applications/plugins/picopass/picopass_i.h @@ -24,6 +24,7 @@ #include #include +#include #define PICOPASS_TEXT_STORE_SIZE 128 diff --git a/applications/plugins/signal_generator/application.fam b/applications/plugins/signal_generator/application.fam index 7794ee492..de915733c 100644 --- a/applications/plugins/signal_generator/application.fam +++ b/applications/plugins/signal_generator/application.fam @@ -9,4 +9,5 @@ App( order=50, fap_icon="signal_gen_10px.png", fap_category="Tools", + fap_icon_assets="icons", ) diff --git a/applications/plugins/signal_generator/icons/SmallArrowDown_3x5.png b/applications/plugins/signal_generator/icons/SmallArrowDown_3x5.png new file mode 100644 index 0000000000000000000000000000000000000000..1912e5d246268d75a20984bdc8b996d503f3d166 GIT binary patch literal 3592 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 BDRuw= literal 0 HcmV?d00001 diff --git a/applications/plugins/signal_generator/icons/SmallArrowUp_3x5.png b/applications/plugins/signal_generator/icons/SmallArrowUp_3x5.png new file mode 100644 index 0000000000000000000000000000000000000000..9c6242078d3bcc9a93eb55b6b5f358720ee6662c GIT binary patch literal 7976 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 +#include typedef enum { LineIndexChannel, diff --git a/applications/plugins/weather_station/images/Lock_7x8.png b/applications/plugins/weather_station/images/Lock_7x8.png new file mode 100644 index 0000000000000000000000000000000000000000..f7c9ca2c702f1b93d7d06bd12ae708655c79d7c8 GIT binary patch literal 3597 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 a9|QyVQ~@!E@+ey_4zMz}H7hmoyzn0`d`!~- literal 0 HcmV?d00001 diff --git a/applications/plugins/weather_station/images/Pin_back_arrow_10x8.png b/applications/plugins/weather_station/images/Pin_back_arrow_10x8.png new file mode 100644 index 0000000000000000000000000000000000000000..3bafabd144864b575144c75b592e5eaf53974566 GIT binary patch literal 3606 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 TFC+3}9cE|kU{!4CefEC<&8td2 literal 0 HcmV?d00001 diff --git a/applications/plugins/weather_station/images/Quest_7x8.png b/applications/plugins/weather_station/images/Quest_7x8.png new file mode 100644 index 0000000000000000000000000000000000000000..6825247fbeaf98b4d0d9f8aa15d4f2c5f45dcf3c GIT binary patch literal 3675 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 N!3~Mzlm%~0{|8*@s)qmo literal 0 HcmV?d00001 diff --git a/applications/plugins/weather_station/images/Unlock_7x8.png b/applications/plugins/weather_station/images/Unlock_7x8.png new file mode 100644 index 0000000000000000000000000000000000000000..9d82b4daf3c722a5244f537f18b147833c2fb4ee GIT binary patch literal 3598 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 MrJY5ondim-0h+K&wEzGB literal 0 HcmV?d00001 diff --git a/applications/plugins/weather_station/images/WarningDolphin_45x42.png b/applications/plugins/weather_station/images/WarningDolphin_45x42.png new file mode 100644 index 0000000000000000000000000000000000000000..d766ffbb444db1739f2ccd030e506e8bada11ee8 GIT binary patch literal 1139 zcmaJ=TWAz#6rQ4%BpO*okt7zz3Bkm=bK9L=XV}qhW_HceY#KHzP4Z$UGyf(-b}pUy z<8ESW=_MtCk6tj|`k)VrCbo(SMH0n`eW+KIU`wG5O`$IeMg)Vzf7adDhv+af=lqBB zo%5Z`zqhqzdu2s+1%_dji6%LPq#u2o%9fzN?;7Dlq6)^^VVjkKImH23RI|DPo-mXi zkOGP}@Wrnnf?-SQ^>jOIPc{pxWsr*JL*@+|p)oA7EpIDoAAoo_=+RA)c=F3Qf$N$` ze9k55q%DD7y=l+^ZG$aob+Aw6HDcRVJdzhs00Te;&l_3O74jlch$|r7GgAa!aDjay z@rG1;vK5ys2jF3n@vAgV<6)izn!k6Qhd>D(EhD7l zcrhJ1i9|1iwm?z2T#n2INXzM=7@p@Tnx$CQk39VDfC-hn-*jtB5oF-1j&4KUGI1}W z(rxuakw9eMRAJc3%8 zlBq3$QTyJX$a6$&1ldyi4Pe5AEE32Sg@vDzY~7qR?1u@oXh zd9(fBtV<@eK%Tm=yy&p7{=h^#@1W)WFFSe!U5pP~o71uR`FW)7xc*=d5_b}EG@XBZ z@<7MRp-;+|o}SzJvMyfx>37Y4etBizf%0H*zaIF?2ObZt?$D;{o|esJ z*PgAOIO^*8tL%z2)|}k$DP5kN5}8qo*wNa8y?R2=%wO{gCD(`sHt}TzWQuK zMDIJap(nb2=7*ns*oDcRBbC23yy`kvKYV`ovU`a=EmxNv-90;;5r6+l8hQTp^u`Hn XX6*+%8gHC`h)Tl}u@-r>vFqE{F~5F& literal 0 HcmV?d00001 diff --git a/applications/plugins/weather_station/views/weather_station_receiver.c b/applications/plugins/weather_station/views/weather_station_receiver.c index d30b7926b..61b152602 100644 --- a/applications/plugins/weather_station/views/weather_station_receiver.c +++ b/applications/plugins/weather_station/views/weather_station_receiver.c @@ -1,11 +1,10 @@ #include "weather_station_receiver.h" #include "../weather_station_app_i.h" -#include "weather_station_icons.h" +#include #include #include #include -#include #include #define FRAME_HEIGHT 12 diff --git a/applications/services/bt/bt_service/bt.c b/applications/services/bt/bt_service/bt.c index c003013e4..62b5ab109 100644 --- a/applications/services/bt/bt_service/bt.c +++ b/applications/services/bt/bt_service/bt.c @@ -4,6 +4,7 @@ #include #include +#include #define TAG "BtSrv" diff --git a/applications/services/desktop/views/desktop_view_lock_menu.c b/applications/services/desktop/views/desktop_view_lock_menu.c index 8cb8a7a12..486be23b5 100644 --- a/applications/services/desktop/views/desktop_view_lock_menu.c +++ b/applications/services/desktop/views/desktop_view_lock_menu.c @@ -1,5 +1,6 @@ #include #include +#include #include "../desktop_i.h" #include "desktop_view_lock_menu.h" diff --git a/applications/services/desktop/views/desktop_view_locked.c b/applications/services/desktop/views/desktop_view_locked.c index 915b26103..d18ed6c98 100644 --- a/applications/services/desktop/views/desktop_view_locked.c +++ b/applications/services/desktop/views/desktop_view_locked.c @@ -4,6 +4,7 @@ #include #include #include +#include #include #include diff --git a/applications/services/desktop/views/desktop_view_pin_input.c b/applications/services/desktop/views/desktop_view_pin_input.c index bf05f06b9..b86bf2929 100644 --- a/applications/services/desktop/views/desktop_view_pin_input.c +++ b/applications/services/desktop/views/desktop_view_pin_input.c @@ -2,6 +2,7 @@ #include #include #include +#include #include #include diff --git a/applications/services/dialogs/dialogs_api.c b/applications/services/dialogs/dialogs_api.c index 6fd51782d..ee959a33c 100644 --- a/applications/services/dialogs/dialogs_api.c +++ b/applications/services/dialogs/dialogs_api.c @@ -1,6 +1,7 @@ #include "dialogs/dialogs_message.h" #include "dialogs_i.h" #include "dialogs_api_lock.h" +#include /****************** File browser ******************/ diff --git a/applications/services/gui/canvas.h b/applications/services/gui/canvas.h index a67e58494..a3df5adc7 100644 --- a/applications/services/gui/canvas.h +++ b/applications/services/gui/canvas.h @@ -7,7 +7,7 @@ #include #include -#include +#include #ifdef __cplusplus extern "C" { diff --git a/applications/services/gui/gui.c b/applications/services/gui/gui.c index 42712ed90..2d06d70c7 100644 --- a/applications/services/gui/gui.c +++ b/applications/services/gui/gui.c @@ -1,5 +1,6 @@ #include "gui/canvas.h" #include "gui_i.h" +#include #define TAG "GuiSrv" diff --git a/applications/services/gui/icon_animation.h b/applications/services/gui/icon_animation.h index dab9d996d..684790353 100644 --- a/applications/services/gui/icon_animation.h +++ b/applications/services/gui/icon_animation.h @@ -7,7 +7,7 @@ #include #include -#include +#include #ifdef __cplusplus extern "C" { diff --git a/applications/services/gui/modules/button_menu.c b/applications/services/gui/modules/button_menu.c index 37a04326a..ff12a9311 100644 --- a/applications/services/gui/modules/button_menu.c +++ b/applications/services/gui/modules/button_menu.c @@ -5,6 +5,7 @@ #include #include #include +#include #define ITEM_FIRST_OFFSET 17 #define ITEM_NEXT_OFFSET 4 diff --git a/applications/services/gui/modules/byte_input.c b/applications/services/gui/modules/byte_input.c index 8d7e7fd4f..bc19f0eee 100644 --- a/applications/services/gui/modules/byte_input.c +++ b/applications/services/gui/modules/byte_input.c @@ -1,6 +1,7 @@ -#include "byte_input.h" -#include #include +#include +#include +#include "byte_input.h" struct ByteInput { View* view; diff --git a/applications/services/gui/modules/menu.c b/applications/services/gui/modules/menu.c index db0717f77..6983e0108 100644 --- a/applications/services/gui/modules/menu.c +++ b/applications/services/gui/modules/menu.c @@ -2,6 +2,7 @@ #include #include +#include #include struct Menu { diff --git a/applications/services/gui/modules/text_input.c b/applications/services/gui/modules/text_input.c index 79fa87728..540e4b7c4 100644 --- a/applications/services/gui/modules/text_input.c +++ b/applications/services/gui/modules/text_input.c @@ -1,5 +1,6 @@ #include "text_input.h" #include +#include #include struct TextInput { diff --git a/applications/services/power/power_service/power_i.h b/applications/services/power/power_service/power_i.h index 66ced885b..8cb5140d7 100644 --- a/applications/services/power/power_service/power_i.h +++ b/applications/services/power/power_service/power_i.h @@ -5,6 +5,7 @@ #include #include #include +#include #include #include "views/power_off.h" diff --git a/applications/services/power/power_service/views/power_off.c b/applications/services/power/power_service/views/power_off.c index b0046325c..f14a18d7e 100644 --- a/applications/services/power/power_service/views/power_off.c +++ b/applications/services/power/power_service/views/power_off.c @@ -1,6 +1,7 @@ #include "power_off.h" #include #include +#include struct PowerOff { View* view; diff --git a/applications/services/power/power_service/views/power_unplug_usb.c b/applications/services/power/power_service/views/power_unplug_usb.c index 5632cd8b0..c2d61139e 100644 --- a/applications/services/power/power_service/views/power_unplug_usb.c +++ b/applications/services/power/power_service/views/power_unplug_usb.c @@ -1,6 +1,7 @@ #include "power_unplug_usb.h" #include #include +#include struct PowerUnplugUsb { View* view; diff --git a/applications/services/storage/storage.c b/applications/services/storage/storage.c index 9079a95ed..700408c9d 100644 --- a/applications/services/storage/storage.c +++ b/applications/services/storage/storage.c @@ -5,6 +5,7 @@ #include "storage/storage_glue.h" #include "storages/storage_int.h" #include "storages/storage_ext.h" +#include #define STORAGE_TICK 1000 diff --git a/applications/settings/about/about.c b/applications/settings/about/about.c index a42969b2b..1719e188d 100644 --- a/applications/settings/about/about.c +++ b/applications/settings/about/about.c @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include diff --git a/applications/settings/bt_settings_app/bt_settings_app.h b/applications/settings/bt_settings_app/bt_settings_app.h index c45ff3db0..b79e36951 100644 --- a/applications/settings/bt_settings_app/bt_settings_app.h +++ b/applications/settings/bt_settings_app/bt_settings_app.h @@ -6,6 +6,7 @@ #include #include #include +#include #include #include diff --git a/applications/settings/desktop_settings/desktop_settings_app.h b/applications/settings/desktop_settings/desktop_settings_app.h index fc56c3253..6f97564c9 100644 --- a/applications/settings/desktop_settings/desktop_settings_app.h +++ b/applications/settings/desktop_settings/desktop_settings_app.h @@ -7,6 +7,7 @@ #include #include #include +#include #include #include diff --git a/applications/settings/power_settings_app/power_settings_app.h b/applications/settings/power_settings_app/power_settings_app.h index 8429b54b4..cd05846c0 100644 --- a/applications/settings/power_settings_app/power_settings_app.h +++ b/applications/settings/power_settings_app/power_settings_app.h @@ -6,6 +6,7 @@ #include #include #include +#include #include "views/battery_info.h" #include diff --git a/applications/settings/power_settings_app/views/battery_info.c b/applications/settings/power_settings_app/views/battery_info.c index 1a8bc71ec..e1b7adb4b 100644 --- a/applications/settings/power_settings_app/views/battery_info.c +++ b/applications/settings/power_settings_app/views/battery_info.c @@ -1,6 +1,7 @@ #include "battery_info.h" #include #include +#include struct BatteryInfo { View* view; diff --git a/applications/settings/storage_settings/storage_settings.h b/applications/settings/storage_settings/storage_settings.h index 4cf185e0c..664e74c84 100644 --- a/applications/settings/storage_settings/storage_settings.h +++ b/applications/settings/storage_settings/storage_settings.h @@ -4,6 +4,7 @@ #include #include #include +#include #include #include diff --git a/applications/system/updater/views/updater_main.c b/applications/system/updater/views/updater_main.c index 5ed3c70aa..1199cc882 100644 --- a/applications/system/updater/views/updater_main.c +++ b/applications/system/updater/views/updater_main.c @@ -2,6 +2,7 @@ #include #include #include +#include #include #include diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index fd11dffe0..00ba30c24 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,6.0,, +Version,+,7.0,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -2661,191 +2661,7 @@ Function,-,yn,double,"int, double" Function,-,ynf,float,"int, float" Variable,-,AHBPrescTable,const uint32_t[16], Variable,-,APBPrescTable,const uint32_t[8], -Variable,+,A_125khz_14,const Icon, -Variable,+,A_BadUsb_14,const Icon, -Variable,+,A_Debug_14,const Icon, -Variable,+,A_FileManager_14,const Icon, -Variable,+,A_GPIO_14,const Icon, -Variable,+,A_Infrared_14,const Icon, -Variable,+,A_Levelup1_128x64,const Icon, -Variable,+,A_Levelup2_128x64,const Icon, -Variable,+,A_Loading_24,const Icon, -Variable,+,A_NFC_14,const Icon, -Variable,+,A_Plugins_14,const Icon, -Variable,+,A_Round_loader_8x8,const Icon, -Variable,+,A_Settings_14,const Icon, -Variable,+,A_Sub1ghz_14,const Icon, -Variable,+,A_U2F_14,const Icon, -Variable,+,A_iButton_14,const Icon, Variable,-,ITM_RxBuffer,volatile int32_t, -Variable,+,I_125_10px,const Icon, -Variable,+,I_ActiveConnection_50x64,const Icon, -Variable,+,I_Alert_9x8,const Icon, -Variable,+,I_ArrowC_1_36x36,const Icon, -Variable,+,I_ArrowDownEmpty_14x15,const Icon, -Variable,+,I_ArrowDownFilled_14x15,const Icon, -Variable,+,I_ArrowUpEmpty_14x15,const Icon, -Variable,+,I_ArrowUpFilled_14x15,const Icon, -Variable,+,I_Attention_5x8,const Icon, -Variable,+,I_Auth_62x31,const Icon, -Variable,+,I_BLE_Pairing_128x64,const Icon, -Variable,+,I_Background_128x11,const Icon, -Variable,+,I_BatteryBody_52x28,const Icon, -Variable,+,I_Battery_16x16,const Icon, -Variable,+,I_Battery_26x8,const Icon, -Variable,+,I_Ble_connected_15x15,const Icon, -Variable,+,I_Ble_disconnected_15x15,const Icon, -Variable,+,I_Bluetooth_Connected_16x8,const Icon, -Variable,+,I_Bluetooth_Idle_5x8,const Icon, -Variable,+,I_ButtonCenter_7x7,const Icon, -Variable,+,I_ButtonDown_7x4,const Icon, -Variable,+,I_ButtonLeftSmall_3x5,const Icon, -Variable,+,I_ButtonLeft_4x7,const Icon, -Variable,+,I_ButtonRightSmall_3x5,const Icon, -Variable,+,I_ButtonRight_4x7,const Icon, -Variable,+,I_ButtonUp_7x4,const Icon, -Variable,+,I_Button_18x18,const Icon, -Variable,+,I_Certification1_103x56,const Icon, -Variable,+,I_Certification2_98x33,const Icon, -Variable,+,I_Charging_lightning_9x10,const Icon, -Variable,+,I_Charging_lightning_mask_9x10,const Icon, -Variable,+,I_Circles_47x47,const Icon, -Variable,+,I_Clock_18x18,const Icon, -Variable,+,I_Connect_me_62x31,const Icon, -Variable,+,I_Connected_62x31,const Icon, -Variable,+,I_CoolHi_25x27,const Icon, -Variable,+,I_CoolHi_hvr_25x27,const Icon, -Variable,+,I_CoolLo_25x27,const Icon, -Variable,+,I_CoolLo_hvr_25x27,const Icon, -Variable,+,I_Cry_dolph_55x52,const Icon, -Variable,+,I_DFU_128x50,const Icon, -Variable,+,I_Dehumidify_25x27,const Icon, -Variable,+,I_Dehumidify_hvr_25x27,const Icon, -Variable,+,I_Detailed_chip_17x13,const Icon, -Variable,+,I_DolphinCommon_56x48,const Icon, -Variable,+,I_DolphinMafia_115x62,const Icon, -Variable,+,I_DolphinNice_96x59,const Icon, -Variable,+,I_DolphinReadingSuccess_59x63,const Icon, -Variable,+,I_DolphinWait_61x59,const Icon, -Variable,+,I_DoorLeft_70x55,const Icon, -Variable,+,I_DoorRight_70x55,const Icon, -Variable,+,I_Down_25x27,const Icon, -Variable,+,I_Down_hvr_25x27,const Icon, -Variable,+,I_Drive_112x35,const Icon, -Variable,+,I_Error_18x18,const Icon, -Variable,+,I_Error_62x31,const Icon, -Variable,+,I_EviSmile1_18x21,const Icon, -Variable,+,I_EviSmile2_18x21,const Icon, -Variable,+,I_EviWaiting1_18x21,const Icon, -Variable,+,I_EviWaiting2_18x21,const Icon, -Variable,+,I_FaceCharging_29x14,const Icon, -Variable,+,I_FaceConfused_29x14,const Icon, -Variable,+,I_FaceNopower_29x14,const Icon, -Variable,+,I_FaceNormal_29x14,const Icon, -Variable,+,I_GameMode_11x8,const Icon, -Variable,+,I_Health_16x16,const Icon, -Variable,+,I_HeatHi_25x27,const Icon, -Variable,+,I_HeatHi_hvr_25x27,const Icon, -Variable,+,I_HeatLo_25x27,const Icon, -Variable,+,I_HeatLo_hvr_25x27,const Icon, -Variable,+,I_Hidden_window_9x8,const Icon, -Variable,+,I_InfraredArrowDown_4x8,const Icon, -Variable,+,I_InfraredArrowUp_4x8,const Icon, -Variable,+,I_InfraredLearnShort_128x31,const Icon, -Variable,+,I_KeyBackspaceSelected_16x9,const Icon, -Variable,+,I_KeyBackspace_16x9,const Icon, -Variable,+,I_KeySaveSelected_24x11,const Icon, -Variable,+,I_KeySave_24x11,const Icon, -Variable,+,I_Keychain_39x36,const Icon, -Variable,+,I_Left_mouse_icon_9x9,const Icon, -Variable,+,I_Lock_7x8,const Icon, -Variable,+,I_Lock_8x8,const Icon, -Variable,+,I_MHz_25x11,const Icon, -Variable,+,I_Medium_chip_22x21,const Icon, -Variable,+,I_Modern_reader_18x34,const Icon, -Variable,+,I_Move_flipper_26x39,const Icon, -Variable,+,I_Mute_25x27,const Icon, -Variable,+,I_Mute_hvr_25x27,const Icon, -Variable,+,I_NFC_manual_60x50,const Icon, -Variable,+,I_Nfc_10px,const Icon, -Variable,+,I_Off_25x27,const Icon, -Variable,+,I_Off_hvr_25x27,const Icon, -Variable,+,I_Ok_btn_9x9,const Icon, -Variable,+,I_Ok_btn_pressed_13x13,const Icon, -Variable,+,I_Percent_10x14,const Icon, -Variable,+,I_Pin_arrow_down_7x9,const Icon, -Variable,+,I_Pin_arrow_left_9x7,const Icon, -Variable,+,I_Pin_arrow_right_9x7,const Icon, -Variable,+,I_Pin_arrow_up_7x9,const Icon, -Variable,+,I_Pin_attention_dpad_29x29,const Icon, -Variable,+,I_Pin_back_arrow_10x8,const Icon, -Variable,+,I_Pin_back_full_40x8,const Icon, -Variable,+,I_Pin_pointer_5x3,const Icon, -Variable,+,I_Pin_star_7x7,const Icon, -Variable,+,I_Power_25x27,const Icon, -Variable,+,I_Power_hvr_25x27,const Icon, -Variable,+,I_Pressed_Button_13x13,const Icon, -Variable,+,I_Quest_7x8,const Icon, -Variable,+,I_RFIDBigChip_37x36,const Icon, -Variable,+,I_RFIDDolphinReceive_97x61,const Icon, -Variable,+,I_RFIDDolphinSend_97x61,const Icon, -Variable,+,I_RFIDDolphinSuccess_108x57,const Icon, -Variable,+,I_Reader_detect_43x40,const Icon, -Variable,+,I_Release_arrow_18x15,const Icon, -Variable,+,I_Restoring_38x32,const Icon, -Variable,+,I_Right_mouse_icon_9x9,const Icon, -Variable,+,I_SDQuestion_35x43,const Icon, -Variable,+,I_SDcardFail_11x8,const Icon, -Variable,+,I_SDcardMounted_11x8,const Icon, -Variable,+,I_Scanning_123x52,const Icon, -Variable,+,I_SmallArrowDown_3x5,const Icon, -Variable,+,I_SmallArrowDown_4x7,const Icon, -Variable,+,I_SmallArrowUp_3x5,const Icon, -Variable,+,I_SmallArrowUp_4x7,const Icon, -Variable,+,I_Smile_18x18,const Icon, -Variable,+,I_Space_65x18,const Icon, -Variable,+,I_Tap_reader_36x38,const Icon, -Variable,+,I_Temperature_16x16,const Icon, -Variable,+,I_Unlock_7x8,const Icon, -Variable,+,I_Unplug_bg_bottom_128x10,const Icon, -Variable,+,I_Unplug_bg_top_128x14,const Icon, -Variable,+,I_Up_25x27,const Icon, -Variable,+,I_Up_hvr_25x27,const Icon, -Variable,+,I_Updating_32x40,const Icon, -Variable,+,I_UsbTree_48x22,const Icon, -Variable,+,I_Vol_down_25x27,const Icon, -Variable,+,I_Vol_down_hvr_25x27,const Icon, -Variable,+,I_Vol_up_25x27,const Icon, -Variable,+,I_Vol_up_hvr_25x27,const Icon, -Variable,+,I_Voldwn_6x6,const Icon, -Variable,+,I_Voltage_16x16,const Icon, -Variable,+,I_Volup_8x6,const Icon, -Variable,+,I_WarningDolphin_45x42,const Icon, -Variable,+,I_Warning_30x23,const Icon, -Variable,+,I_back_10px,const Icon, -Variable,+,I_badusb_10px,const Icon, -Variable,+,I_dir_10px,const Icon, -Variable,+,I_iButtonDolphinVerySuccess_108x52,const Icon, -Variable,+,I_iButtonKey_49x44,const Icon, -Variable,+,I_ibutt_10px,const Icon, -Variable,+,I_ir_10px,const Icon, -Variable,+,I_loading_10px,const Icon, -Variable,+,I_music_10px,const Icon, -Variable,+,I_passport_bad1_46x49,const Icon, -Variable,+,I_passport_bad2_46x49,const Icon, -Variable,+,I_passport_bad3_46x49,const Icon, -Variable,+,I_passport_bottom_128x18,const Icon, -Variable,+,I_passport_happy1_46x49,const Icon, -Variable,+,I_passport_happy2_46x49,const Icon, -Variable,+,I_passport_happy3_46x49,const Icon, -Variable,+,I_passport_left_6x46,const Icon, -Variable,+,I_passport_okay1_46x49,const Icon, -Variable,+,I_passport_okay2_46x49,const Icon, -Variable,+,I_passport_okay3_46x49,const Icon, -Variable,+,I_sub1_10px,const Icon, -Variable,+,I_u2f_10px,const Icon, -Variable,+,I_unknown_10px,const Icon, -Variable,+,I_update_10px,const Icon, Variable,-,MSIRangeTable,const uint32_t[16], Variable,-,SmpsPrescalerTable,const uint32_t[4][6], Variable,+,SystemCoreClock,uint32_t, From 9cd0592aafb826ea08d180c857d399acdf607706 Mon Sep 17 00:00:00 2001 From: Skorpionm <85568270+Skorpionm@users.noreply.github.com> Date: Fri, 28 Oct 2022 18:31:41 +0400 Subject: [PATCH 17/31] SubGhz: add keeloq potocol JCM_Tech (#1939) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * SubGhz: add keeloq potocol JCM_Tech * SubGhz: add new metod decoder Co-authored-by: あく --- assets/resources/subghz/assets/keeloq_mfcodes | 99 ++++++++++--------- lib/subghz/protocols/keeloq.c | 18 ++++ lib/subghz/protocols/keeloq_common.c | 30 +++++- lib/subghz/protocols/keeloq_common.h | 18 ++++ 4 files changed, 117 insertions(+), 48 deletions(-) diff --git a/assets/resources/subghz/assets/keeloq_mfcodes b/assets/resources/subghz/assets/keeloq_mfcodes index b8fc36903..1b27bfb01 100644 --- a/assets/resources/subghz/assets/keeloq_mfcodes +++ b/assets/resources/subghz/assets/keeloq_mfcodes @@ -1,50 +1,55 @@ Filetype: Flipper SubGhz Keystore File Version: 0 Encryption: 1 -IV: 2A 34 F1 5A AF 6F F5 1A 83 A6 1E DA DE B7 3D F1 -06B63DF24AE073A2F2B19C55CA9E8364FBECD26E49C551990153F6513BDE5267 -6139C78C74C341EB7474085CF1D047BD6FB005F80A72AF3EF3F89D58EF5DF500 -D85F11689020ECA47FBE9C2B67EE41A81E1F06DE2A35AF958965E3ECE29EA701 -1AE9073A42FE0E439544FE6945F6B33CF15A7A4A279020B5E0B3BE33FD189A7E -E161F007854BB33E0056FA09A2E2DEE66789B5C87C8D6D3DE2C8C1BD2B48983EB9D1C5697CA6E95996918F7C47B761B0 -59AE4644DCB3D720C38B5115F230DA58E7BE0A697907F6174BB05AB7886ACDB1 -634DF0BCC185C4C1F7E1B1594B4438D051ABAE092433078963063B51D961D08C -1EBEBCB49E498B9BE977D53EC21B9A546155B627737BD0AA832D496035729346 -4DFA93E639197772D57E8ACE04512CEFC045B8CC965C175A25ED525B630CBB63 -C2D5235D1014A319B249EAE8A5EE350F18D5AB8A498EF222704BD4EB1435F388 -F66D1937160E1392197F463A52E87FCE938A92070892113443C348D7553327A5715CF615CE2F2C96284F47759E043419 -841D29E7CBE040188E2283BFBA9F26EF2F65CCB085B56C3515E8C46C3F20BD75BAA963550869435FDAF509CEEE66A2C4 -7D87E24487D307635E7A17B989B8547EE11F3BF3468D055F0B44633B631BA42C -B4916043973501B95A82B329196D6EBA69FBBC3AF8FD914583104E0E18CE82F6 -E4649F9C2A5465D2EA6F3E9724DD06CD6962FE2BAEB14F1453C14D1559232AE1 -96E15D890DF7FD348441F5E429A875754C6BF0520A787F8E9D8C5415674783CC -CB52005EDED47B57F795BC92FB0522EAB18D23EE028B8D10ED57828C250EB285BFEC6E4A4BE8DABCE0D57ECAA20D90C3 -8E5A50C7D5C374445E88752301D20F0B3D6E4988B61D90FD63779B0EDEF9C60D -49D6CB276A0E5FF134A38062503F01351F44CD6455708B50B5F07D03FC477C33 -CB45B56613DF208E79E4E10A6510F07DC1AA49210C7B94E8BBAECD2C35EC6ABC99FB10FD7C96DD6BB6A6685E9FAD93FB -0743F3CC51200F763C242F1956B4D775C092ADF1A5C19ACAE96EB60C2990CF214F8FEA8FC6749286F6BDAB67657C479A -E5608B28A058787D64A145F0362DEFD98CAE0B5A0F22C6DA7C6D278C7B5F95E3 -D4C113D43E7FB6D2EFA9E87471AA76A61B26872607B4AF5B87F9D72113835CE6 -2DC502800BFD21B76126390CA64A08C5432A2254E822F214CDE1EA11430084C5 -CA22C73010B0F1CB8009601BE2AF0B3674D83D5880E4A26C2A3FF0EA0A098CEA -E53B2B102FDB000E9BB747F957156976E5A0C0E3898AA844C13AE8A9CEE7013B -95CF1A46FFC252BE92919531C92BF6A3AA1B16C170DF4461EC54BE07A55C2387 -2EC7E24090F6DFFF6F2F2D8874D2F36AA769995F31F29FBE3B0EA6A16C3EE833 -C1145B1D9AC70761EA902B86455C1BE1BB1153552A1F7327411DECABE538827B -18D596CADD2EE544200A58716C7A4690B658E58CC2B97334740F70894A6C90FA -6A2F8859DFF01E13AC6C5300AD4A2218810FC91A6FB64A560E99FE6C99226AD2 -48D2EB5A08E35AF89A3B7A1CFDEE829FC0C2DDD2E965F4E3D043B0B14CB7825E -91039325D53CDD0236D1CD13047973A013C14B45A32DE0784A73BFABCEAFBCD1 -51B4EAC87C4DC49B007F40D38B8166C388A1AF25E8D2FF6598E8EDE8726E6E14AD88443114D2A0F5E7721E304F3870DA -3A179DDF65B9868CD84C7C04931F40D5D204C97B20DCBF1A70C241E59BFD7F14 -AF538FD16104DCAF03F4DDF05026D6741898DFC247E48A8F72E652DDF2DFD289 -E67F16AEC9D84B6C06F77B806CA6FBC7618BFBECD0D7A04EC3AE1D1DD06BEC5B -FA4D9F8920EBF2F4293C6D4E99083AA4A71A9DDFFDB07EEBDC552DACEC4DA24A -5BF23E630AC81E2CD533803E225BCB3C481B8D650A9858CF2B5219BAE1CDA01A -17B57E8C1032481E69247EA9A0C9EA41F6C0EA9B3F11170CA69C0842423F0455 -96EA848B8527A647DC9DACDB16C5D92B0081EB1CD77B99B47F56C2E249190BD3BE4306333F37487133DD3AD8E57F3092 -B0E9411274D799BE5989D52E74E00DE310CCA2BD47D7A8FA554D66BB04CD787A -D0D28476E3D8832975653D93F545C35278EC1F0B7AD70CA2F36EB476CC207937 -933195E37014619F997B73F5CF4C0110865A822CA8CB0ED1D977D49A1B06A37F -E790CAC2A26452BF941A9E1BABF0A85598EA1CC8F8CFED637C9B40D5E027B518 -49C1F179ABA5BD4F2C45257A33701730E9CC4728677EFF07808ABE31D3CE6FD5C805F43EA5ABB7261B220C82F0794092 +IV: AA FF DE 54 A1 BB F1 21 83 46 FE 2A 1E B7 3D 33 +95B8CD65BBAC95EACE67CA94F679B82877A921396D461ECB479722F8A369454A +61065C41297B9FF8F8168814F49A03D1FE7B4CB79DFFCBBF0402AAA6A2211E84 +A1557AC139188FF105D1081A4B688C5CA440FB5DA7F40901B541120AD08A544F +AF0A6056D7F0D97DAD6C16C4E63204E4B3B1C5A20AC82B983B516F4F718EE29F +6861BFAE46A1AADB1DB2D6DFAA7E39D21D5B3E46A41BD50F4F2828879EB328EF0A406F2B9C79A031AB361257E6D69756 +0DDB3DAC53678541981CC46C22CED245CBA314C9BBE1BA9383B8505B75AC5E40 +99AB5D9404934F2D257ED04D9F8CCEE06D00F38157B121AFD63101E4E5C08268 +5114A6C42B342C7D933A76F9052FF963C2047E85EA524497C21B4C35C38EF6E7 +88CA2A1907D94B972FF93DBB9B88CB576F3E1BB0FE8F85A5B2CCA7D44B00374D +349C4153FE7CA8AE044E9F75F77D9694304474CE3F127CF968662B5F78A7F421 +62AA02E20CA7E691EFC0B55CA41C9BDF889FB23868289284241CD31AA1A0E499AE2A770B6B5AB3170CDCCDB8A246D36C +97901B5EB76228ADF8E5073F1BAB1502878DEFF1C4EBF12A43D105556CB7E80F947A8BD7831666BD838C57CDF64A6F3F +B05959D210B500943A93BDFAF783D9DB215FC84503B152EAFBCFB5B6237E3888 +B393DE4489BCAFD5DB80592A12E329E18913E185D2042580048029A8C4C3A257 +B4B30492A5F0C3C763E2F43C02D1451A5B9CFB468CFE62BE85B1F56FF49DAB9A +CE5D57C0EE3D717FC717EB725970A9F25D211546EE7AC5C237950CEA323D85D4 +4E9028944813FD40A17AF6DF5A97E76179B48EE79265BBD38B07E3A270587A813DADB51B3367479AC5644F754B5613F8 +3B3C3000B9D1361711ECE3DB77C90A059576F738CB167679DA36DD3D128B27A1 +997023148148DE7B9CBA47D3FD48DEF73AA1715FF4BC1E7A1DBA6D52A0DCB2C0 +C8428D18E69FB92486434FCE470F1FF37D40507F27D824679C132A70D516530367277F02DDB5C464D03450FF6B425A24 +3701200DF5DA7235971FD95844056E74C7D61A8EB12A8772E04F52037C63D50B6229A7F905F3E6F84C565FCC7632870C +BB392A464CDC0D5D923AA9EF8ECC3C6F020D0AD82165462DF0DE7C5025AAAAAC +999C82209B30638506E5D708471676D2CBB4A432E5AF86ABD61179111EDAE636 +FDE2A452A6B47261338117EC20FC57731DA492562ECD21BBC61F098A5442CF20 +D923BABB5C4DFB48E3F763898B2796C7830D3EE9A91DF904AC2223A0F4736507 +0987DDAC695DD5E4607048DF1D4EF96599E17ED52F41785E676AA048AB7213FE +26CB3E6CFA10338A8DDD99BFF6957C53DEF435CB0FF977B71B5164ADFE11292A +097908FD07A0A093CA80E6FF59524707C1A11169D0CB6F8E4967D8DAA725FE7A +8C629E70A5CC6FCB039DFA1A6AC58CB7B7E92C85BDA66266AB49E6B1285FC7A6 +39A2052350CD446EDC1B9AD0C2DD51C78B2E5F3A76AAD0EC200F74B40ACD4AC5 +A1685CF8C4A5401F2CA0C8172CB5B4B5726C61CE68A72AE834B0A472CEB2F3DE +1F5ED5793DB381D1B501BA8A4DF3E74FB11FC1A922DDC8AE62E5BA8934C37EA8 +D80EF661BF36E2F6C179E253CE5BC3732684ACBC7C65E526A628442A2EBF8FAA +7785BF721F21E19A8CFBFBBB56BD76B96A4E8EF9F8A2344009B14AB385909598F834A5533B648DA7D62BD6D4314A43A5 +C8F6F943DE615B5827569B283577344C0455B3279C73634FC4E0E9A8088DF633 +FB4F4C786FC51BBDA679A212B4A05EF120AC62F7EBFFE8263BD50A4D9BC9C6E0 +16EEC35CF69BA86DB3BE999CDF9B39F5736F3727B2AA2C5AB9141A48F176D831 +AD1AD6DE813E7710DA3AF546D4F9EA085831E6B3FA17B64F1B8765F48134EA54 +345D743BC35B4A8614632ADD11E809C0D1E6C78F9469256B9A738DA0B648B2B8 +7C876CECAC839EBB4609C3996966C3EC454F51C8ABCC51097E405370C4B6F086 +0F857C031FD3047607647148C534F969567F207FF1691D8D06DCDF4C2514695D +EC0630EDC82241C1952F49B6B1B0C1A954A7DDD6BDB1326ACC54AD449D1BF985 +286EF9F7FD0D09F2604CCE867C52144CD0C4773A3D8183066C61B8BF9860AE7C +EA55424097A08722A66966E3177E09DE91AC65175E5C68CB47B6153E6585DF85 +D54FCDF9EA4BD1FE4F316DB6D5CE4A2675F2D0144772865EDC781FBA7DFD23E4 +7A2F5C5CA9F97FE9527BAA760E64B930C407A27DE036476737E6BDD9422F4056A5F1F414F12F0982109FD7C30E8CC1CB +06BAD9B4EEEEB1BCF8C97672D271534FE84D772282EE9642698788D3842D7641 +101C1B2DBD963E23777294C22E553D145D5B40838F91355CA86D571A0CEFF68F +1B148C2B502B3E0A5BD40858E019C513DD4CCAF2A114CBB29C59BFB018079285 +8DF4D07EC20FF873EA989ACEF4AF96E9787FE6E0F71965858B4186C3AF302A31 +2317DC8C098CD60F3467B3644A19CCE887339708820CD37F6F5277D6648F837512F70CE90E23D7339CDDE002BD8D83DB diff --git a/lib/subghz/protocols/keeloq.c b/lib/subghz/protocols/keeloq.c index ae6588e7a..eef1d0937 100644 --- a/lib/subghz/protocols/keeloq.c +++ b/lib/subghz/protocols/keeloq.c @@ -529,6 +529,24 @@ static uint8_t subghz_protocol_keeloq_check_remote_controller_selector( return 1; } break; + case KEELOQ_LEARNING_MAGIC_SERIAL_TYPE_2: + man = subghz_protocol_keeloq_common_magic_serial_type2_learning( + fix, manufacture_code->key); + decrypt = subghz_protocol_keeloq_common_decrypt(hop, man); + if(subghz_protocol_keeloq_check_decrypt(instance, decrypt, btn, end_serial)) { + *manufacture_name = furi_string_get_cstr(manufacture_code->name); + return 1; + } + break; + case KEELOQ_LEARNING_MAGIC_SERIAL_TYPE_3: + man = subghz_protocol_keeloq_common_magic_serial_type3_learning( + fix, manufacture_code->key); + decrypt = subghz_protocol_keeloq_common_decrypt(hop, man); + if(subghz_protocol_keeloq_check_decrypt(instance, decrypt, btn, end_serial)) { + *manufacture_name = furi_string_get_cstr(manufacture_code->name); + return 1; + } + break; case KEELOQ_LEARNING_UNKNOWN: // Simple Learning decrypt = subghz_protocol_keeloq_common_decrypt(hop, manufacture_code->key); diff --git a/lib/subghz/protocols/keeloq_common.c b/lib/subghz/protocols/keeloq_common.c index 6c9bc461e..ddbf1c917 100644 --- a/lib/subghz/protocols/keeloq_common.c +++ b/lib/subghz/protocols/keeloq_common.c @@ -94,6 +94,34 @@ inline uint64_t inline uint64_t subghz_protocol_keeloq_common_magic_serial_type1_learning(uint32_t data, uint64_t man) { - return man | ((uint64_t)data << 40) | + return (man & 0xFFFFFFFF) | ((uint64_t)data << 40) | ((uint64_t)(((data & 0xff) + ((data >> 8) & 0xFF)) & 0xFF) << 32); } + +/** Magic_serial_type2 Learning + * @param data - btn+serial number (32bit) + * @param man - magic man (64bit) + * @return manufacture for this serial number (64bit) + */ + +inline uint64_t + subghz_protocol_keeloq_common_magic_serial_type2_learning(uint32_t data, uint64_t man) { + uint8_t* p = (uint8_t*)&data; + uint8_t* m = (uint8_t*)&man; + m[7] = p[0]; + m[6] = p[1]; + m[5] = p[2]; + m[4] = p[3]; + return man; +} + +/** Magic_serial_type3 Learning + * @param data - serial number (24bit) + * @param man - magic man (64bit) + * @return manufacture for this serial number (64bit) + */ + +inline uint64_t + subghz_protocol_keeloq_common_magic_serial_type3_learning(uint32_t data, uint64_t man) { + return (man & 0xFFFFFFFFFF000000) | (data & 0xFFFFFF); +} diff --git a/lib/subghz/protocols/keeloq_common.h b/lib/subghz/protocols/keeloq_common.h index 448388f0a..df3d0dbf3 100644 --- a/lib/subghz/protocols/keeloq_common.h +++ b/lib/subghz/protocols/keeloq_common.h @@ -22,6 +22,8 @@ #define KEELOQ_LEARNING_SECURE 3u #define KEELOQ_LEARNING_MAGIC_XOR_TYPE_1 4u #define KEELOQ_LEARNING_MAGIC_SERIAL_TYPE_1 5u +#define KEELOQ_LEARNING_MAGIC_SERIAL_TYPE_2 6u +#define KEELOQ_LEARNING_MAGIC_SERIAL_TYPE_3 7u /** * Simple Learning Encrypt @@ -72,3 +74,19 @@ uint64_t subghz_protocol_keeloq_common_magic_xor_type1_learning(uint32_t data, u */ uint64_t subghz_protocol_keeloq_common_magic_serial_type1_learning(uint32_t data, uint64_t man); + +/** Magic_serial_type2 Learning + * @param data - btn+serial number (32bit) + * @param man - magic man (64bit) + * @return manufacture for this serial number (64bit) + */ + +uint64_t subghz_protocol_keeloq_common_magic_serial_type2_learning(uint32_t data, uint64_t man); + +/** Magic_serial_type3 Learning + * @param data - btn+serial number (32bit) + * @param man - magic man (64bit) + * @return manufacture for this serial number (64bit) + */ + +uint64_t subghz_protocol_keeloq_common_magic_serial_type3_learning(uint32_t data, uint64_t man); From 4b921803cbad19829d0b08e92bab89f29080ffd7 Mon Sep 17 00:00:00 2001 From: hedger Date: Fri, 28 Oct 2022 19:32:06 +0400 Subject: [PATCH 18/31] fbt: fixes for ufbt compat (#1940) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fbt: split sdk management code * scripts: fixed import handling * fbt: sdk: reformatted paths * scrips: dist: bundling libs as a build artifact * fbt: sdk: better path management * typo fix * fbt: sdk: minor path handling fixes * toolchain: fixed windows toolchain download Co-authored-by: あく --- firmware.scons | 17 -- scripts/fbt/sdk/__init__.py | 44 +++ scripts/fbt/{sdk.py => sdk/cache.py} | 280 +----------------- scripts/fbt/sdk/collector.py | 238 +++++++++++++++ scripts/fbt_tools/fbt_extapps.py | 2 +- scripts/fbt_tools/fbt_sdk.py | 49 ++- scripts/sconsdist.py | 66 +++-- .../toolchain/windows-toolchain-download.ps1 | 6 +- site_scons/extapps.scons | 2 +- site_scons/firmwareopts.scons | 20 +- 10 files changed, 383 insertions(+), 341 deletions(-) create mode 100644 scripts/fbt/sdk/__init__.py rename scripts/fbt/{sdk.py => sdk/cache.py} (52%) create mode 100644 scripts/fbt/sdk/collector.py diff --git a/firmware.scons b/firmware.scons index d501996b3..63a1aa3f7 100644 --- a/firmware.scons +++ b/firmware.scons @@ -178,23 +178,6 @@ sources.extend( ) ) - -fwenv.AppendUnique( - LINKFLAGS=[ - "-specs=nano.specs", - "-specs=nosys.specs", - "-Wl,--gc-sections", - "-Wl,--undefined=uxTopUsedPriority", - "-Wl,--wrap,_malloc_r", - "-Wl,--wrap,_free_r", - "-Wl,--wrap,_calloc_r", - "-Wl,--wrap,_realloc_r", - "-n", - "-Xlinker", - "-Map=${TARGET}.map", - ], -) - # Debug # print(fwenv.Dump()) diff --git a/scripts/fbt/sdk/__init__.py b/scripts/fbt/sdk/__init__.py new file mode 100644 index 000000000..27da5f7c8 --- /dev/null +++ b/scripts/fbt/sdk/__init__.py @@ -0,0 +1,44 @@ +from typing import Set, ClassVar +from dataclasses import dataclass, field + + +@dataclass(frozen=True) +class ApiEntryFunction: + name: str + returns: str + params: str + + csv_type: ClassVar[str] = "Function" + + def dictify(self): + return dict(name=self.name, type=self.returns, params=self.params) + + +@dataclass(frozen=True) +class ApiEntryVariable: + name: str + var_type: str + + csv_type: ClassVar[str] = "Variable" + + def dictify(self): + return dict(name=self.name, type=self.var_type, params=None) + + +@dataclass(frozen=True) +class ApiHeader: + name: str + + csv_type: ClassVar[str] = "Header" + + def dictify(self): + return dict(name=self.name, type=None, params=None) + + +@dataclass +class ApiEntries: + # These are sets, to avoid creating duplicates when we have multiple + # declarations with same signature + functions: Set[ApiEntryFunction] = field(default_factory=set) + variables: Set[ApiEntryVariable] = field(default_factory=set) + headers: Set[ApiHeader] = field(default_factory=set) diff --git a/scripts/fbt/sdk.py b/scripts/fbt/sdk/cache.py similarity index 52% rename from scripts/fbt/sdk.py rename to scripts/fbt/sdk/cache.py index 48f935de3..62d42798c 100644 --- a/scripts/fbt/sdk.py +++ b/scripts/fbt/sdk/cache.py @@ -4,284 +4,18 @@ import csv import operator from enum import Enum, auto -from typing import List, Set, ClassVar, Any -from dataclasses import dataclass, field +from typing import Set, ClassVar, Any +from dataclasses import dataclass from ansi.color import fg -from cxxheaderparser.parser import CxxParser - - -# 'Fixing' complaints about typedefs -CxxParser._fundamentals.discard("wchar_t") - -from cxxheaderparser.types import ( - EnumDecl, - Field, - ForwardDecl, - FriendDecl, - Function, - Method, - Typedef, - UsingAlias, - UsingDecl, - Variable, - Pointer, - Type, - PQName, - NameSpecifier, - FundamentalSpecifier, - Parameter, - Array, - Value, - Token, - FunctionType, +from . import ( + ApiEntries, + ApiEntryFunction, + ApiEntryVariable, + ApiHeader, ) -from cxxheaderparser.parserstate import ( - State, - EmptyBlockState, - ClassBlockState, - ExternBlockState, - NamespaceBlockState, -) - - -@dataclass(frozen=True) -class ApiEntryFunction: - name: str - returns: str - params: str - - csv_type: ClassVar[str] = "Function" - - def dictify(self): - return dict(name=self.name, type=self.returns, params=self.params) - - -@dataclass(frozen=True) -class ApiEntryVariable: - name: str - var_type: str - - csv_type: ClassVar[str] = "Variable" - - def dictify(self): - return dict(name=self.name, type=self.var_type, params=None) - - -@dataclass(frozen=True) -class ApiHeader: - name: str - - csv_type: ClassVar[str] = "Header" - - def dictify(self): - return dict(name=self.name, type=None, params=None) - - -@dataclass -class ApiEntries: - # These are sets, to avoid creating duplicates when we have multiple - # declarations with same signature - functions: Set[ApiEntryFunction] = field(default_factory=set) - variables: Set[ApiEntryVariable] = field(default_factory=set) - headers: Set[ApiHeader] = field(default_factory=set) - - -class SymbolManager: - def __init__(self): - self.api = ApiEntries() - self.name_hashes = set() - - # Calculate hash of name and raise exception if it already is in the set - def _name_check(self, name: str): - name_hash = gnu_sym_hash(name) - if name_hash in self.name_hashes: - raise Exception(f"Hash collision on {name}") - self.name_hashes.add(name_hash) - - def add_function(self, function_def: ApiEntryFunction): - if function_def in self.api.functions: - return - self._name_check(function_def.name) - self.api.functions.add(function_def) - - def add_variable(self, variable_def: ApiEntryVariable): - if variable_def in self.api.variables: - return - self._name_check(variable_def.name) - self.api.variables.add(variable_def) - - def add_header(self, header: str): - self.api.headers.add(ApiHeader(header)) - - -def gnu_sym_hash(name: str): - h = 0x1505 - for c in name: - h = (h << 5) + h + ord(c) - return str(hex(h))[-8:] - - -class SdkCollector: - def __init__(self): - self.symbol_manager = SymbolManager() - - def add_header_to_sdk(self, header: str): - self.symbol_manager.add_header(header) - - def process_source_file_for_sdk(self, file_path: str): - visitor = SdkCxxVisitor(self.symbol_manager) - with open(file_path, "rt") as f: - content = f.read() - parser = CxxParser(file_path, content, visitor, None) - parser.parse() - - def get_api(self): - return self.symbol_manager.api - - -def stringify_array_dimension(size_descr): - if not size_descr: - return "" - return stringify_descr(size_descr) - - -def stringify_array_descr(type_descr): - assert isinstance(type_descr, Array) - return ( - stringify_descr(type_descr.array_of), - stringify_array_dimension(type_descr.size), - ) - - -def stringify_descr(type_descr): - if isinstance(type_descr, (NameSpecifier, FundamentalSpecifier)): - return type_descr.name - elif isinstance(type_descr, PQName): - return "::".join(map(stringify_descr, type_descr.segments)) - elif isinstance(type_descr, Pointer): - # Hack - if isinstance(type_descr.ptr_to, FunctionType): - return stringify_descr(type_descr.ptr_to) - return f"{stringify_descr(type_descr.ptr_to)}*" - elif isinstance(type_descr, Type): - return ( - f"{'const ' if type_descr.const else ''}" - f"{'volatile ' if type_descr.volatile else ''}" - f"{stringify_descr(type_descr.typename)}" - ) - elif isinstance(type_descr, Parameter): - return stringify_descr(type_descr.type) - elif isinstance(type_descr, Array): - # Hack for 2d arrays - if isinstance(type_descr.array_of, Array): - argtype, dimension = stringify_array_descr(type_descr.array_of) - return ( - f"{argtype}[{stringify_array_dimension(type_descr.size)}][{dimension}]" - ) - return f"{stringify_descr(type_descr.array_of)}[{stringify_array_dimension(type_descr.size)}]" - elif isinstance(type_descr, Value): - return " ".join(map(stringify_descr, type_descr.tokens)) - elif isinstance(type_descr, FunctionType): - return f"{stringify_descr(type_descr.return_type)} (*)({', '.join(map(stringify_descr, type_descr.parameters))})" - elif isinstance(type_descr, Token): - return type_descr.value - elif type_descr is None: - return "" - else: - raise Exception("unsupported type_descr: %s" % type_descr) - - -class SdkCxxVisitor: - def __init__(self, symbol_manager: SymbolManager): - self.api = symbol_manager - - def on_variable(self, state: State, v: Variable) -> None: - if not v.extern: - return - - self.api.add_variable( - ApiEntryVariable( - stringify_descr(v.name), - stringify_descr(v.type), - ) - ) - - def on_function(self, state: State, fn: Function) -> None: - if fn.inline or fn.has_body: - return - - self.api.add_function( - ApiEntryFunction( - stringify_descr(fn.name), - stringify_descr(fn.return_type), - ", ".join(map(stringify_descr, fn.parameters)) - + (", ..." if fn.vararg else ""), - ) - ) - - def on_define(self, state: State, content: str) -> None: - pass - - def on_pragma(self, state: State, content: str) -> None: - pass - - def on_include(self, state: State, filename: str) -> None: - pass - - def on_empty_block_start(self, state: EmptyBlockState) -> None: - pass - - def on_empty_block_end(self, state: EmptyBlockState) -> None: - pass - - def on_extern_block_start(self, state: ExternBlockState) -> None: - pass - - def on_extern_block_end(self, state: ExternBlockState) -> None: - pass - - def on_namespace_start(self, state: NamespaceBlockState) -> None: - pass - - def on_namespace_end(self, state: NamespaceBlockState) -> None: - pass - - def on_forward_decl(self, state: State, fdecl: ForwardDecl) -> None: - pass - - def on_typedef(self, state: State, typedef: Typedef) -> None: - pass - - def on_using_namespace(self, state: State, namespace: List[str]) -> None: - pass - - def on_using_alias(self, state: State, using: UsingAlias) -> None: - pass - - def on_using_declaration(self, state: State, using: UsingDecl) -> None: - pass - - def on_enum(self, state: State, enum: EnumDecl) -> None: - pass - - def on_class_start(self, state: ClassBlockState) -> None: - pass - - def on_class_field(self, state: State, f: Field) -> None: - pass - - def on_class_method(self, state: ClassBlockState, method: Method) -> None: - pass - - def on_class_friend(self, state: ClassBlockState, friend: FriendDecl) -> None: - pass - - def on_class_end(self, state: ClassBlockState) -> None: - pass - @dataclass(frozen=True) class SdkVersion: diff --git a/scripts/fbt/sdk/collector.py b/scripts/fbt/sdk/collector.py new file mode 100644 index 000000000..578a8c7a6 --- /dev/null +++ b/scripts/fbt/sdk/collector.py @@ -0,0 +1,238 @@ +from typing import List + +from cxxheaderparser.parser import CxxParser +from . import ( + ApiEntries, + ApiEntryFunction, + ApiEntryVariable, + ApiHeader, +) + + +# 'Fixing' complaints about typedefs +CxxParser._fundamentals.discard("wchar_t") + +from cxxheaderparser.types import ( + EnumDecl, + Field, + ForwardDecl, + FriendDecl, + Function, + Method, + Typedef, + UsingAlias, + UsingDecl, + Variable, + Pointer, + Type, + PQName, + NameSpecifier, + FundamentalSpecifier, + Parameter, + Array, + Value, + Token, + FunctionType, +) + +from cxxheaderparser.parserstate import ( + State, + EmptyBlockState, + ClassBlockState, + ExternBlockState, + NamespaceBlockState, +) + + +class SymbolManager: + def __init__(self): + self.api = ApiEntries() + self.name_hashes = set() + + # Calculate hash of name and raise exception if it already is in the set + def _name_check(self, name: str): + name_hash = gnu_sym_hash(name) + if name_hash in self.name_hashes: + raise Exception(f"Hash collision on {name}") + self.name_hashes.add(name_hash) + + def add_function(self, function_def: ApiEntryFunction): + if function_def in self.api.functions: + return + self._name_check(function_def.name) + self.api.functions.add(function_def) + + def add_variable(self, variable_def: ApiEntryVariable): + if variable_def in self.api.variables: + return + self._name_check(variable_def.name) + self.api.variables.add(variable_def) + + def add_header(self, header: str): + self.api.headers.add(ApiHeader(header)) + + +def gnu_sym_hash(name: str): + h = 0x1505 + for c in name: + h = (h << 5) + h + ord(c) + return str(hex(h))[-8:] + + +class SdkCollector: + def __init__(self): + self.symbol_manager = SymbolManager() + + def add_header_to_sdk(self, header: str): + self.symbol_manager.add_header(header) + + def process_source_file_for_sdk(self, file_path: str): + visitor = SdkCxxVisitor(self.symbol_manager) + with open(file_path, "rt") as f: + content = f.read() + parser = CxxParser(file_path, content, visitor, None) + parser.parse() + + def get_api(self): + return self.symbol_manager.api + + +def stringify_array_dimension(size_descr): + if not size_descr: + return "" + return stringify_descr(size_descr) + + +def stringify_array_descr(type_descr): + assert isinstance(type_descr, Array) + return ( + stringify_descr(type_descr.array_of), + stringify_array_dimension(type_descr.size), + ) + + +def stringify_descr(type_descr): + if isinstance(type_descr, (NameSpecifier, FundamentalSpecifier)): + return type_descr.name + elif isinstance(type_descr, PQName): + return "::".join(map(stringify_descr, type_descr.segments)) + elif isinstance(type_descr, Pointer): + # Hack + if isinstance(type_descr.ptr_to, FunctionType): + return stringify_descr(type_descr.ptr_to) + return f"{stringify_descr(type_descr.ptr_to)}*" + elif isinstance(type_descr, Type): + return ( + f"{'const ' if type_descr.const else ''}" + f"{'volatile ' if type_descr.volatile else ''}" + f"{stringify_descr(type_descr.typename)}" + ) + elif isinstance(type_descr, Parameter): + return stringify_descr(type_descr.type) + elif isinstance(type_descr, Array): + # Hack for 2d arrays + if isinstance(type_descr.array_of, Array): + argtype, dimension = stringify_array_descr(type_descr.array_of) + return ( + f"{argtype}[{stringify_array_dimension(type_descr.size)}][{dimension}]" + ) + return f"{stringify_descr(type_descr.array_of)}[{stringify_array_dimension(type_descr.size)}]" + elif isinstance(type_descr, Value): + return " ".join(map(stringify_descr, type_descr.tokens)) + elif isinstance(type_descr, FunctionType): + return f"{stringify_descr(type_descr.return_type)} (*)({', '.join(map(stringify_descr, type_descr.parameters))})" + elif isinstance(type_descr, Token): + return type_descr.value + elif type_descr is None: + return "" + else: + raise Exception("unsupported type_descr: %s" % type_descr) + + +class SdkCxxVisitor: + def __init__(self, symbol_manager: SymbolManager): + self.api = symbol_manager + + def on_variable(self, state: State, v: Variable) -> None: + if not v.extern: + return + + self.api.add_variable( + ApiEntryVariable( + stringify_descr(v.name), + stringify_descr(v.type), + ) + ) + + def on_function(self, state: State, fn: Function) -> None: + if fn.inline or fn.has_body: + return + + self.api.add_function( + ApiEntryFunction( + stringify_descr(fn.name), + stringify_descr(fn.return_type), + ", ".join(map(stringify_descr, fn.parameters)) + + (", ..." if fn.vararg else ""), + ) + ) + + def on_define(self, state: State, content: str) -> None: + pass + + def on_pragma(self, state: State, content: str) -> None: + pass + + def on_include(self, state: State, filename: str) -> None: + pass + + def on_empty_block_start(self, state: EmptyBlockState) -> None: + pass + + def on_empty_block_end(self, state: EmptyBlockState) -> None: + pass + + def on_extern_block_start(self, state: ExternBlockState) -> None: + pass + + def on_extern_block_end(self, state: ExternBlockState) -> None: + pass + + def on_namespace_start(self, state: NamespaceBlockState) -> None: + pass + + def on_namespace_end(self, state: NamespaceBlockState) -> None: + pass + + def on_forward_decl(self, state: State, fdecl: ForwardDecl) -> None: + pass + + def on_typedef(self, state: State, typedef: Typedef) -> None: + pass + + def on_using_namespace(self, state: State, namespace: List[str]) -> None: + pass + + def on_using_alias(self, state: State, using: UsingAlias) -> None: + pass + + def on_using_declaration(self, state: State, using: UsingDecl) -> None: + pass + + def on_enum(self, state: State, enum: EnumDecl) -> None: + pass + + def on_class_start(self, state: ClassBlockState) -> None: + pass + + def on_class_field(self, state: State, f: Field) -> None: + pass + + def on_class_method(self, state: ClassBlockState, method: Method) -> None: + pass + + def on_class_friend(self, state: ClassBlockState, friend: FriendDecl) -> None: + pass + + def on_class_end(self, state: ClassBlockState) -> None: + pass diff --git a/scripts/fbt_tools/fbt_extapps.py b/scripts/fbt_tools/fbt_extapps.py index 5a5dab572..38c943cc5 100644 --- a/scripts/fbt_tools/fbt_extapps.py +++ b/scripts/fbt_tools/fbt_extapps.py @@ -8,7 +8,7 @@ import os import pathlib from fbt.elfmanifest import assemble_manifest_data from fbt.appmanifest import FlipperApplication, FlipperManifestException -from fbt.sdk import SdkCache +from fbt.sdk.cache import SdkCache import itertools from ansi.color import fg diff --git a/scripts/fbt_tools/fbt_sdk.py b/scripts/fbt_tools/fbt_sdk.py index 0b6e22de5..f1f55bdb8 100644 --- a/scripts/fbt_tools/fbt_sdk.py +++ b/scripts/fbt_tools/fbt_sdk.py @@ -4,7 +4,7 @@ from SCons.Action import Action from SCons.Errors import UserError # from SCons.Scanner import C -from SCons.Script import Mkdir, Copy, Delete, Entry +from SCons.Script import Entry from SCons.Util import LogicalLines import os.path @@ -12,7 +12,8 @@ import posixpath import pathlib import json -from fbt.sdk import SdkCollector, SdkCache +from fbt.sdk.collector import SdkCollector +from fbt.sdk.cache import SdkCache def ProcessSdkDepends(env, filename): @@ -49,15 +50,19 @@ def prebuild_sdk_create_origin_file(target, source, env): class SdkMeta: - def __init__(self, env): + def __init__(self, env, tree_builder: "SdkTreeBuilder"): self.env = env + self.treebuilder = tree_builder def save_to(self, json_manifest_path: str): meta_contents = { - "sdk_symbols": self.env["SDK_DEFINITION"].name, + "sdk_symbols": self.treebuilder.build_sdk_file_path( + self.env["SDK_DEFINITION"].path + ), "cc_args": self._wrap_scons_vars("$CCFLAGS $_CCCOMCOM"), "cpp_args": self._wrap_scons_vars("$CXXFLAGS $CCFLAGS $_CCCOMCOM"), "linker_args": self._wrap_scons_vars("$LINKFLAGS"), + "linker_script": self.env.subst("${LINKER_SCRIPT_PATH}"), } with open(json_manifest_path, "wt") as f: json.dump(meta_contents, f, indent=4) @@ -68,6 +73,8 @@ class SdkMeta: class SdkTreeBuilder: + SDK_DIR_SUBST = "SDK_ROOT_DIR" + def __init__(self, env, target, source) -> None: self.env = env self.target = target @@ -88,6 +95,8 @@ class SdkTreeBuilder: self.header_depends = list( filter(lambda fname: fname.endswith(".h"), depends.split()), ) + self.header_depends.append(self.env.subst("${LINKER_SCRIPT_PATH}")) + self.header_depends.append(self.env.subst("${SDK_DEFINITION}")) self.header_dirs = sorted( set(map(os.path.normpath, map(os.path.dirname, self.header_depends))) ) @@ -102,17 +111,33 @@ class SdkTreeBuilder: ) sdk_dirs = ", ".join(f"'{dir}'" for dir in self.header_dirs) - for dir in full_fw_paths: - if dir in sdk_dirs: - filtered_paths.append( - posixpath.normpath(posixpath.join(self.target_sdk_dir_name, dir)) - ) + filtered_paths.extend( + map( + self.build_sdk_file_path, + filter(lambda path: path in sdk_dirs, full_fw_paths), + ) + ) sdk_env = self.env.Clone() - sdk_env.Replace(CPPPATH=filtered_paths) - meta = SdkMeta(sdk_env) + sdk_env.Replace( + CPPPATH=filtered_paths, + LINKER_SCRIPT=self.env.subst("${APP_LINKER_SCRIPT}"), + ORIG_LINKER_SCRIPT_PATH=self.env["LINKER_SCRIPT_PATH"], + LINKER_SCRIPT_PATH=self.build_sdk_file_path("${ORIG_LINKER_SCRIPT_PATH}"), + ) + + meta = SdkMeta(sdk_env, self) meta.save_to(self.target[0].path) + def build_sdk_file_path(self, orig_path: str) -> str: + return posixpath.normpath( + posixpath.join( + self.SDK_DIR_SUBST, + self.target_sdk_dir_name, + orig_path, + ) + ).replace("\\", "/") + def emitter(self, target, source, env): target_folder = target[0] target = [target_folder.File("sdk.opts")] @@ -128,8 +153,6 @@ class SdkTreeBuilder: for sdkdir in dirs_to_create: os.makedirs(sdkdir, exist_ok=True) - shutil.copy2(self.env["SDK_DEFINITION"].path, self.sdk_root_dir.path) - for header in self.header_depends: shutil.copy2(header, self.sdk_deploy_dir.File(header).path) diff --git a/scripts/sconsdist.py b/scripts/sconsdist.py index 4c0427894..7636c87bb 100644 --- a/scripts/sconsdist.py +++ b/scripts/sconsdist.py @@ -48,48 +48,52 @@ class Main(App): ) self.parser_copy.set_defaults(func=self.copy) - def get_project_filename(self, project, filetype): + def get_project_file_name(self, project: ProjectDir, filetype: str) -> str: # Temporary fix project_name = project.project - if project_name == "firmware": - if filetype == "zip": - project_name = "sdk" - elif filetype != "elf": - project_name = "full" + if project_name == "firmware" and filetype != "elf": + project_name = "full" - return f"{self.DIST_FILE_PREFIX}{self.target}-{project_name}-{self.args.suffix}.{filetype}" + return self.get_dist_file_name(project_name, filetype) - def get_dist_filepath(self, filename): + def get_dist_file_name(self, dist_artifact_type: str, filetype: str) -> str: + return f"{self.DIST_FILE_PREFIX}{self.target}-{dist_artifact_type}-{self.args.suffix}.{filetype}" + + def get_dist_file_path(self, filename: str) -> str: return join(self.output_dir_path, filename) - def copy_single_project(self, project): + def copy_single_project(self, project: ProjectDir) -> None: obj_directory = join("build", project.dir) for filetype in ("elf", "bin", "dfu", "json"): if exists(src_file := join(obj_directory, f"{project.project}.{filetype}")): shutil.copyfile( src_file, - self.get_dist_filepath( - self.get_project_filename(project, filetype) + self.get_dist_file_path( + self.get_project_file_name(project, filetype) ), ) - if exists(sdk_folder := join(obj_directory, "sdk")): - with zipfile.ZipFile( - self.get_dist_filepath(self.get_project_filename(project, "zip")), - "w", - zipfile.ZIP_DEFLATED, - ) as zf: - for root, dirs, files in walk(sdk_folder): - for file in files: - zf.write( - join(root, file), - relpath( - join(root, file), - sdk_folder, - ), - ) + for foldertype in ("sdk", "lib"): + if exists(sdk_folder := join(obj_directory, foldertype)): + self.package_zip(foldertype, sdk_folder) - def copy(self): + def package_zip(self, foldertype, sdk_folder): + with zipfile.ZipFile( + self.get_dist_file_path(self.get_dist_file_name(foldertype, "zip")), + "w", + zipfile.ZIP_DEFLATED, + ) as zf: + for root, _, files in walk(sdk_folder): + for file in files: + zf.write( + join(root, file), + relpath( + join(root, file), + sdk_folder, + ), + ) + + def copy(self) -> int: self.projects = dict( map( lambda pd: (pd.project, pd), @@ -144,12 +148,12 @@ class Main(App): "-t", self.target, "--dfu", - self.get_dist_filepath( - self.get_project_filename(self.projects["firmware"], "dfu") + self.get_dist_file_path( + self.get_project_file_name(self.projects["firmware"], "dfu") ), "--stage", - self.get_dist_filepath( - self.get_project_filename(self.projects["updater"], "bin") + self.get_dist_file_path( + self.get_project_file_name(self.projects["updater"], "bin") ), ] if self.args.resources: diff --git a/scripts/toolchain/windows-toolchain-download.ps1 b/scripts/toolchain/windows-toolchain-download.ps1 index 370f1a14a..aaed89856 100644 --- a/scripts/toolchain/windows-toolchain-download.ps1 +++ b/scripts/toolchain/windows-toolchain-download.ps1 @@ -23,12 +23,12 @@ if (!(Test-Path -LiteralPath "$repo_root\toolchain")) { New-Item "$repo_root\toolchain" -ItemType Directory } -Write-Host -NoNewline "Unziping Windows toolchain.." +Write-Host -NoNewline "Extracting Windows toolchain.." Add-Type -Assembly "System.IO.Compression.Filesystem" -[System.IO.Compression.ZipFile]::ExtractToDirectory("$toolchain_zip", "$repo_root\") +[System.IO.Compression.ZipFile]::ExtractToDirectory("$repo_root\$toolchain_zip", "$repo_root\") Move-Item -Path "$repo_root\$toolchain_dir" -Destination "$repo_root\toolchain\x86_64-windows" Write-Host "done!" -Write-Host -NoNewline "Clearing temporary files.." +Write-Host -NoNewline "Cleaning up temporary files.." Remove-Item -LiteralPath "$repo_root\$toolchain_zip" -Force Write-Host "done!" diff --git a/site_scons/extapps.scons b/site_scons/extapps.scons index ee317be3b..90d228e58 100644 --- a/site_scons/extapps.scons +++ b/site_scons/extapps.scons @@ -21,7 +21,7 @@ appenv = ENV.Clone( ) appenv.Replace( - LINKER_SCRIPT="application_ext", + LINKER_SCRIPT=appenv.subst("$APP_LINKER_SCRIPT"), ) appenv.AppendUnique( diff --git a/site_scons/firmwareopts.scons b/site_scons/firmwareopts.scons index f04b55cdd..9f707b4d8 100644 --- a/site_scons/firmwareopts.scons +++ b/site_scons/firmwareopts.scons @@ -32,12 +32,27 @@ else: ], ) -ENV.Append( +ENV.AppendUnique( LINKFLAGS=[ - "-Tfirmware/targets/f${TARGET_HW}/${LINKER_SCRIPT}.ld", + "-specs=nano.specs", + "-specs=nosys.specs", + "-Wl,--gc-sections", + "-Wl,--undefined=uxTopUsedPriority", + "-Wl,--wrap,_malloc_r", + "-Wl,--wrap,_free_r", + "-Wl,--wrap,_calloc_r", + "-Wl,--wrap,_realloc_r", + "-n", + "-Xlinker", + "-Map=${TARGET}.map", + "-T${LINKER_SCRIPT_PATH}", ], ) +ENV.SetDefault( + LINKER_SCRIPT_PATH="firmware/targets/f${TARGET_HW}/${LINKER_SCRIPT}.ld", +) + if ENV["FIRMWARE_BUILD_CFG"] == "updater": ENV.Append( IMAGE_BASE_ADDRESS="0x20000000", @@ -47,4 +62,5 @@ else: ENV.Append( IMAGE_BASE_ADDRESS="0x8000000", LINKER_SCRIPT="stm32wb55xx_flash", + APP_LINKER_SCRIPT="application_ext", ) From 09b622d4ae01ef6d3ffcfd0eefabfa5cf890ffdd Mon Sep 17 00:00:00 2001 From: Konstantin Volkov <72250702+doomwastaken@users.noreply.github.com> Date: Fri, 28 Oct 2022 18:45:22 +0300 Subject: [PATCH 19/31] UnitTests: removed all continue-on-error lines (#1946) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * removed all continue-on-error lines * Github: add assets deployment after format Co-authored-by: Konstantin Volkov Co-authored-by: あく --- .github/workflows/unit_tests.yml | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/.github/workflows/unit_tests.yml b/.github/workflows/unit_tests.yml index 8c5bac2a2..b5bf10004 100644 --- a/.github/workflows/unit_tests.yml +++ b/.github/workflows/unit_tests.yml @@ -24,33 +24,29 @@ jobs: - name: 'Compile unit tests firmware' id: compile - continue-on-error: true run: | FBT_TOOLCHAIN_PATH=/opt ./fbt flash OPENOCD_ADAPTER_SERIAL=2A0906016415303030303032 FIRMWARE_APP_SET=unit_tests FORCE=1 - name: 'Wait for flipper to finish updating' id: connect if: steps.compile.outcome == 'success' - continue-on-error: true run: | python3 ./scripts/testing/await_flipper.py ${{steps.device.outputs.flipper}} - name: 'Format flipper SD card' id: format if: steps.connect.outcome == 'success' - continue-on-error: true run: | ./scripts/storage.py -p ${{steps.device.outputs.flipper}} format_ext - - name: 'Copy unit tests to flipper' + - name: 'Copy assets and unit tests data to flipper' id: copy if: steps.format.outcome == 'success' - continue-on-error: true run: | + ./scripts/storage.py -p ${{steps.device.outputs.flipper}} send assets/resources /ext ./scripts/storage.py -p ${{steps.device.outputs.flipper}} send assets/unit_tests /ext/unit_tests - name: 'Run units and validate results' if: steps.copy.outcome == 'success' - continue-on-error: true run: | python3 ./scripts/testing/units.py ${{steps.device.outputs.flipper}} From 93a6e17ce57222fa7b92a930dd4f9aaee3a146bf Mon Sep 17 00:00:00 2001 From: gornekich Date: Fri, 28 Oct 2022 20:10:16 +0400 Subject: [PATCH 20/31] [FL-2933] Mf Classic initial write, update, detect reader (#1941) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * nfc: introduce nfc write * nfc: add write logic * nfc worker: add write state * nfc: add mfc update logic * nfc: add update success logic * nfc: add custom card for detect reader * nfc: update write logic * nfc: add halt command, add notifications * nfc: add write fail scene * nfc: fixes and clean up * nfc: fix navigation ad notifications * nfc: fix detect reader nfc data setter Co-authored-by: あく --- .../main/nfc/scenes/nfc_scene_config.h | 6 + .../main/nfc/scenes/nfc_scene_detect_reader.c | 5 + .../nfc/scenes/nfc_scene_mf_classic_update.c | 98 +++++++ .../nfc_scene_mf_classic_update_success.c | 44 +++ .../nfc/scenes/nfc_scene_mf_classic_write.c | 92 ++++++ .../scenes/nfc_scene_mf_classic_write_fail.c | 58 ++++ .../nfc_scene_mf_classic_write_success.c | 44 +++ .../scenes/nfc_scene_mf_classic_wrong_card.c | 53 ++++ .../main/nfc/scenes/nfc_scene_saved_menu.c | 34 +++ .../main/nfc/scenes/nfc_scene_start.c | 1 + applications/main/nfc/views/detect_reader.c | 36 +++ applications/main/nfc/views/detect_reader.h | 2 + firmware/targets/f7/furi_hal/furi_hal_nfc.c | 10 +- lib/nfc/helpers/reader_analyzer.c | 9 +- lib/nfc/helpers/reader_analyzer.h | 2 + lib/nfc/nfc_device.c | 7 + lib/nfc/nfc_worker.c | 150 +++++++++- lib/nfc/nfc_worker.h | 7 +- lib/nfc/nfc_worker_i.h | 4 + lib/nfc/protocols/crypto1.c | 52 ++++ lib/nfc/protocols/crypto1.h | 14 + lib/nfc/protocols/mifare_classic.c | 271 ++++++++++++------ lib/nfc/protocols/mifare_classic.h | 43 +++ 23 files changed, 949 insertions(+), 93 deletions(-) create mode 100644 applications/main/nfc/scenes/nfc_scene_mf_classic_update.c create mode 100644 applications/main/nfc/scenes/nfc_scene_mf_classic_update_success.c create mode 100644 applications/main/nfc/scenes/nfc_scene_mf_classic_write.c create mode 100644 applications/main/nfc/scenes/nfc_scene_mf_classic_write_fail.c create mode 100644 applications/main/nfc/scenes/nfc_scene_mf_classic_write_success.c create mode 100644 applications/main/nfc/scenes/nfc_scene_mf_classic_wrong_card.c diff --git a/applications/main/nfc/scenes/nfc_scene_config.h b/applications/main/nfc/scenes/nfc_scene_config.h index a25850c84..9b922add2 100644 --- a/applications/main/nfc/scenes/nfc_scene_config.h +++ b/applications/main/nfc/scenes/nfc_scene_config.h @@ -36,6 +36,12 @@ ADD_SCENE(nfc, mf_classic_keys_list, MfClassicKeysList) ADD_SCENE(nfc, mf_classic_keys_delete, MfClassicKeysDelete) ADD_SCENE(nfc, mf_classic_keys_warn_duplicate, MfClassicKeysWarnDuplicate) ADD_SCENE(nfc, mf_classic_dict_attack, MfClassicDictAttack) +ADD_SCENE(nfc, mf_classic_write, MfClassicWrite) +ADD_SCENE(nfc, mf_classic_write_success, MfClassicWriteSuccess) +ADD_SCENE(nfc, mf_classic_write_fail, MfClassicWriteFail) +ADD_SCENE(nfc, mf_classic_update, MfClassicUpdate) +ADD_SCENE(nfc, mf_classic_update_success, MfClassicUpdateSuccess) +ADD_SCENE(nfc, mf_classic_wrong_card, MfClassicWrongCard) ADD_SCENE(nfc, emv_read_success, EmvReadSuccess) ADD_SCENE(nfc, emv_menu, EmvMenu) ADD_SCENE(nfc, emulate_apdu_sequence, EmulateApduSequence) diff --git a/applications/main/nfc/scenes/nfc_scene_detect_reader.c b/applications/main/nfc/scenes/nfc_scene_detect_reader.c index abf1437d2..745946157 100644 --- a/applications/main/nfc/scenes/nfc_scene_detect_reader.c +++ b/applications/main/nfc/scenes/nfc_scene_detect_reader.c @@ -28,6 +28,11 @@ void nfc_scene_detect_reader_on_enter(void* context) { detect_reader_set_callback(nfc->detect_reader, nfc_scene_detect_reader_callback, nfc); detect_reader_set_nonces_max(nfc->detect_reader, NFC_SCENE_DETECT_READER_PAIR_NONCES_MAX); + NfcDeviceData* dev_data = &nfc->dev->dev_data; + if(dev_data->nfc_data.uid_len) { + detect_reader_set_uid( + nfc->detect_reader, dev_data->nfc_data.uid, dev_data->nfc_data.uid_len); + } // Store number of collected nonces in scene state scene_manager_set_scene_state(nfc->scene_manager, NfcSceneDetectReader, 0); diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_update.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_update.c new file mode 100644 index 000000000..dd3a6f7d5 --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_update.c @@ -0,0 +1,98 @@ +#include "../nfc_i.h" +#include + +enum { + NfcSceneMfClassicUpdateStateCardSearch, + NfcSceneMfClassicUpdateStateCardFound, +}; + +bool nfc_mf_classic_update_worker_callback(NfcWorkerEvent event, void* context) { + furi_assert(context); + + Nfc* nfc = context; + view_dispatcher_send_custom_event(nfc->view_dispatcher, event); + + return true; +} + +static void nfc_scene_mf_classic_update_setup_view(Nfc* nfc) { + Popup* popup = nfc->popup; + popup_reset(popup); + uint32_t state = scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMfClassicUpdate); + + if(state == NfcSceneMfClassicUpdateStateCardSearch) { + popup_set_text( + nfc->popup, "Apply the initial\ncard only", 128, 32, AlignRight, AlignCenter); + popup_set_icon(nfc->popup, 0, 8, &I_NFC_manual_60x50); + } else { + popup_set_header(popup, "Updating\nDon't move...", 52, 32, AlignLeft, AlignCenter); + popup_set_icon(popup, 12, 23, &A_Loading_24); + } + + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup); +} + +void nfc_scene_mf_classic_update_on_enter(void* context) { + Nfc* nfc = context; + DOLPHIN_DEED(DolphinDeedNfcEmulate); + + scene_manager_set_scene_state( + nfc->scene_manager, NfcSceneMfClassicUpdate, NfcSceneMfClassicUpdateStateCardSearch); + nfc_scene_mf_classic_update_setup_view(nfc); + + // Setup and start worker + nfc_worker_start( + nfc->worker, + NfcWorkerStateMfClassicUpdate, + &nfc->dev->dev_data, + nfc_mf_classic_update_worker_callback, + nfc); + nfc_blink_emulate_start(nfc); +} + +bool nfc_scene_mf_classic_update_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == NfcWorkerEventSuccess) { + nfc_worker_stop(nfc->worker); + if(nfc_device_save_shadow(nfc->dev, nfc->dev->dev_name)) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicUpdateSuccess); + } else { + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicWrongCard); + } + consumed = true; + } else if(event.event == NfcWorkerEventWrongCard) { + nfc_worker_stop(nfc->worker); + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicWrongCard); + consumed = true; + } else if(event.event == NfcWorkerEventCardDetected) { + scene_manager_set_scene_state( + nfc->scene_manager, + NfcSceneMfClassicUpdate, + NfcSceneMfClassicUpdateStateCardFound); + nfc_scene_mf_classic_update_setup_view(nfc); + consumed = true; + } else if(event.event == NfcWorkerEventNoCardDetected) { + scene_manager_set_scene_state( + nfc->scene_manager, + NfcSceneMfClassicUpdate, + NfcSceneMfClassicUpdateStateCardSearch); + nfc_scene_mf_classic_update_setup_view(nfc); + consumed = true; + } + } + return consumed; +} + +void nfc_scene_mf_classic_update_on_exit(void* context) { + Nfc* nfc = context; + nfc_worker_stop(nfc->worker); + scene_manager_set_scene_state( + nfc->scene_manager, NfcSceneMfClassicUpdate, NfcSceneMfClassicUpdateStateCardSearch); + // Clear view + popup_reset(nfc->popup); + + nfc_blink_stop(nfc); +} diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_update_success.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_update_success.c new file mode 100644 index 000000000..fef8fd5e9 --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_update_success.c @@ -0,0 +1,44 @@ +#include "../nfc_i.h" +#include + +void nfc_scene_mf_classic_update_success_popup_callback(void* context) { + Nfc* nfc = context; + view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventViewExit); +} + +void nfc_scene_mf_classic_update_success_on_enter(void* context) { + Nfc* nfc = context; + DOLPHIN_DEED(DolphinDeedNfcSave); + + notification_message(nfc->notifications, &sequence_success); + + Popup* popup = nfc->popup; + popup_set_icon(popup, 32, 5, &I_DolphinNice_96x59); + popup_set_header(popup, "Updated!", 11, 20, AlignLeft, AlignBottom); + popup_set_timeout(popup, 1500); + popup_set_context(popup, nfc); + popup_set_callback(popup, nfc_scene_mf_classic_update_success_popup_callback); + popup_enable_timeout(popup); + + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup); +} + +bool nfc_scene_mf_classic_update_success_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == NfcCustomEventViewExit) { + consumed = scene_manager_search_and_switch_to_previous_scene( + nfc->scene_manager, NfcSceneFileSelect); + } + } + return consumed; +} + +void nfc_scene_mf_classic_update_success_on_exit(void* context) { + Nfc* nfc = context; + + // Clear view + popup_reset(nfc->popup); +} diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_write.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_write.c new file mode 100644 index 000000000..3543cbc58 --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_write.c @@ -0,0 +1,92 @@ +#include "../nfc_i.h" +#include + +enum { + NfcSceneMfClassicWriteStateCardSearch, + NfcSceneMfClassicWriteStateCardFound, +}; + +bool nfc_mf_classic_write_worker_callback(NfcWorkerEvent event, void* context) { + furi_assert(context); + + Nfc* nfc = context; + view_dispatcher_send_custom_event(nfc->view_dispatcher, event); + + return true; +} + +static void nfc_scene_mf_classic_write_setup_view(Nfc* nfc) { + Popup* popup = nfc->popup; + popup_reset(popup); + uint32_t state = scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMfClassicWrite); + + if(state == NfcSceneMfClassicWriteStateCardSearch) { + popup_set_text( + nfc->popup, "Apply the initial\ncard only", 128, 32, AlignRight, AlignCenter); + popup_set_icon(nfc->popup, 0, 8, &I_NFC_manual_60x50); + } else { + popup_set_header(popup, "Writing\nDon't move...", 52, 32, AlignLeft, AlignCenter); + popup_set_icon(popup, 12, 23, &A_Loading_24); + } + + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup); +} + +void nfc_scene_mf_classic_write_on_enter(void* context) { + Nfc* nfc = context; + DOLPHIN_DEED(DolphinDeedNfcEmulate); + + scene_manager_set_scene_state( + nfc->scene_manager, NfcSceneMfClassicWrite, NfcSceneMfClassicWriteStateCardSearch); + nfc_scene_mf_classic_write_setup_view(nfc); + + // Setup and start worker + nfc_worker_start( + nfc->worker, + NfcWorkerStateMfClassicWrite, + &nfc->dev->dev_data, + nfc_mf_classic_write_worker_callback, + nfc); + nfc_blink_emulate_start(nfc); +} + +bool nfc_scene_mf_classic_write_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == NfcWorkerEventSuccess) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicWriteSuccess); + consumed = true; + } else if(event.event == NfcWorkerEventFail) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicWriteFail); + consumed = true; + } else if(event.event == NfcWorkerEventWrongCard) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicWrongCard); + consumed = true; + } else if(event.event == NfcWorkerEventCardDetected) { + scene_manager_set_scene_state( + nfc->scene_manager, NfcSceneMfClassicWrite, NfcSceneMfClassicWriteStateCardFound); + nfc_scene_mf_classic_write_setup_view(nfc); + consumed = true; + } else if(event.event == NfcWorkerEventNoCardDetected) { + scene_manager_set_scene_state( + nfc->scene_manager, NfcSceneMfClassicWrite, NfcSceneMfClassicWriteStateCardSearch); + nfc_scene_mf_classic_write_setup_view(nfc); + consumed = true; + } + } + return consumed; +} + +void nfc_scene_mf_classic_write_on_exit(void* context) { + Nfc* nfc = context; + + nfc_worker_stop(nfc->worker); + scene_manager_set_scene_state( + nfc->scene_manager, NfcSceneMfClassicWrite, NfcSceneMfClassicWriteStateCardSearch); + // Clear view + popup_reset(nfc->popup); + + nfc_blink_stop(nfc); +} diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_write_fail.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_write_fail.c new file mode 100644 index 000000000..aeea6eef0 --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_write_fail.c @@ -0,0 +1,58 @@ +#include "../nfc_i.h" + +void nfc_scene_mf_classic_write_fail_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_write_fail_on_enter(void* context) { + Nfc* nfc = context; + Widget* widget = nfc->widget; + + notification_message(nfc->notifications, &sequence_error); + + widget_add_icon_element(widget, 72, 17, &I_DolphinCommon_56x48); + widget_add_string_element( + widget, 7, 4, AlignLeft, AlignTop, FontPrimary, "Writing gone wrong!"); + widget_add_string_multiline_element( + widget, + 7, + 17, + AlignLeft, + AlignTop, + FontSecondary, + "Not all sectors\nwere written\ncorrectly."); + + widget_add_button_element( + widget, GuiButtonTypeLeft, "Finish", nfc_scene_mf_classic_write_fail_widget_callback, nfc); + + // Setup and start worker + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); +} + +bool nfc_scene_mf_classic_write_fail_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, NfcSceneFileSelect); + } + } else if(event.type == SceneManagerEventTypeBack) { + consumed = scene_manager_search_and_switch_to_previous_scene( + nfc->scene_manager, NfcSceneSavedMenu); + } + return consumed; +} + +void nfc_scene_mf_classic_write_fail_on_exit(void* context) { + Nfc* nfc = context; + + widget_reset(nfc->widget); +} diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_write_success.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_write_success.c new file mode 100644 index 000000000..2f2a3beb1 --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_write_success.c @@ -0,0 +1,44 @@ +#include "../nfc_i.h" +#include + +void nfc_scene_mf_classic_write_success_popup_callback(void* context) { + Nfc* nfc = context; + view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventViewExit); +} + +void nfc_scene_mf_classic_write_success_on_enter(void* context) { + Nfc* nfc = context; + DOLPHIN_DEED(DolphinDeedNfcSave); + + notification_message(nfc->notifications, &sequence_success); + + Popup* popup = nfc->popup; + popup_set_icon(popup, 32, 5, &I_DolphinNice_96x59); + popup_set_header(popup, "Successfully\nwritten", 13, 22, AlignLeft, AlignBottom); + popup_set_timeout(popup, 1500); + popup_set_context(popup, nfc); + popup_set_callback(popup, nfc_scene_mf_classic_write_success_popup_callback); + popup_enable_timeout(popup); + + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup); +} + +bool nfc_scene_mf_classic_write_success_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == NfcCustomEventViewExit) { + consumed = scene_manager_search_and_switch_to_previous_scene( + nfc->scene_manager, NfcSceneFileSelect); + } + } + return consumed; +} + +void nfc_scene_mf_classic_write_success_on_exit(void* context) { + Nfc* nfc = context; + + // Clear view + popup_reset(nfc->popup); +} diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_wrong_card.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_wrong_card.c new file mode 100644 index 000000000..2c56270e3 --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_wrong_card.c @@ -0,0 +1,53 @@ +#include "../nfc_i.h" + +void nfc_scene_mf_classic_wrong_card_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_wrong_card_on_enter(void* context) { + Nfc* nfc = context; + Widget* widget = nfc->widget; + + notification_message(nfc->notifications, &sequence_error); + + widget_add_icon_element(widget, 73, 17, &I_DolphinCommon_56x48); + widget_add_string_element( + widget, 3, 4, AlignLeft, AlignTop, FontPrimary, "This is wrong card"); + widget_add_string_multiline_element( + widget, + 4, + 17, + AlignLeft, + AlignTop, + FontSecondary, + "Data management\nis only possible\nwith initial card"); + widget_add_button_element( + widget, GuiButtonTypeLeft, "Retry", nfc_scene_mf_classic_wrong_card_widget_callback, nfc); + + // Setup and start worker + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); +} + +bool nfc_scene_mf_classic_wrong_card_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); + } + } + return consumed; +} + +void nfc_scene_mf_classic_wrong_card_on_exit(void* context) { + Nfc* nfc = context; + + widget_reset(nfc->widget); +} \ No newline at end of file diff --git a/applications/main/nfc/scenes/nfc_scene_saved_menu.c b/applications/main/nfc/scenes/nfc_scene_saved_menu.c index 09d2c2d61..231f12089 100644 --- a/applications/main/nfc/scenes/nfc_scene_saved_menu.c +++ b/applications/main/nfc/scenes/nfc_scene_saved_menu.c @@ -4,6 +4,9 @@ enum SubmenuIndex { SubmenuIndexEmulate, SubmenuIndexEditUid, + SubmenuIndexDetectReader, + SubmenuIndexWrite, + SubmenuIndexUpdate, SubmenuIndexRename, SubmenuIndexDelete, SubmenuIndexInfo, @@ -42,6 +45,28 @@ void nfc_scene_saved_menu_on_enter(void* context) { submenu_add_item( submenu, "Emulate", SubmenuIndexEmulate, nfc_scene_saved_menu_submenu_callback, nfc); } + if(nfc->dev->format == NfcDeviceSaveFormatMifareClassic) { + if(!mf_classic_is_card_read(&nfc->dev->dev_data.mf_classic_data)) { + submenu_add_item( + submenu, + "Detect reader", + SubmenuIndexDetectReader, + nfc_scene_saved_menu_submenu_callback, + nfc); + } + submenu_add_item( + submenu, + "Write To Initial Card", + SubmenuIndexWrite, + nfc_scene_saved_menu_submenu_callback, + nfc); + submenu_add_item( + submenu, + "Update From Initial Card", + SubmenuIndexUpdate, + nfc_scene_saved_menu_submenu_callback, + nfc); + } submenu_add_item( submenu, "Info", SubmenuIndexInfo, nfc_scene_saved_menu_submenu_callback, nfc); if(nfc->dev->shadow_file_exist) { @@ -79,6 +104,15 @@ bool nfc_scene_saved_menu_on_event(void* context, SceneManagerEvent event) { } DOLPHIN_DEED(DolphinDeedNfcEmulate); consumed = true; + } else if(event.event == SubmenuIndexDetectReader) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneDetectReader); + consumed = true; + } else if(event.event == SubmenuIndexWrite) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicWrite); + consumed = true; + } else if(event.event == SubmenuIndexUpdate) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicUpdate); + consumed = true; } else if(event.event == SubmenuIndexRename) { scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveName); consumed = true; diff --git a/applications/main/nfc/scenes/nfc_scene_start.c b/applications/main/nfc/scenes/nfc_scene_start.c index 0c4ec1cf9..028f85ae0 100644 --- a/applications/main/nfc/scenes/nfc_scene_start.c +++ b/applications/main/nfc/scenes/nfc_scene_start.c @@ -53,6 +53,7 @@ bool nfc_scene_start_on_event(void* context, SceneManagerEvent event) { } else if(event.event == SubmenuIndexDetectReader) { bool sd_exist = storage_sd_status(nfc->dev->storage) == FSE_OK; if(sd_exist) { + nfc_device_data_clear(&nfc->dev->dev_data); scene_manager_next_scene(nfc->scene_manager, NfcSceneDetectReader); DOLPHIN_DEED(DolphinDeedNfcDetectReader); } else { diff --git a/applications/main/nfc/views/detect_reader.c b/applications/main/nfc/views/detect_reader.c index 91537868b..e5951beb2 100644 --- a/applications/main/nfc/views/detect_reader.c +++ b/applications/main/nfc/views/detect_reader.c @@ -2,6 +2,8 @@ #include #include +#define DETECT_READER_UID_MAX_LEN (10) + struct DetectReader { View* view; DetectReaderDoneCallback callback; @@ -12,6 +14,7 @@ typedef struct { uint16_t nonces; uint16_t nonces_max; DetectReaderState state; + FuriString* uid_str; } DetectReaderViewModel; static void detect_reader_draw_callback(Canvas* canvas, void* model) { @@ -23,6 +26,10 @@ static void detect_reader_draw_callback(Canvas* canvas, void* model) { if(m->state == DetectReaderStateStart) { snprintf(text, sizeof(text), "Touch the reader"); canvas_draw_icon(canvas, 21, 13, &I_Move_flipper_26x39); + if(furi_string_size(m->uid_str)) { + elements_multiline_text_aligned( + canvas, 64, 64, AlignCenter, AlignBottom, furi_string_get_cstr(m->uid_str)); + } } else if(m->state == DetectReaderStateReaderDetected) { snprintf(text, sizeof(text), "Move the Flipper away"); canvas_draw_icon(canvas, 24, 25, &I_Release_arrow_18x15); @@ -86,12 +93,24 @@ DetectReader* detect_reader_alloc() { view_set_input_callback(detect_reader->view, detect_reader_input_callback); view_set_context(detect_reader->view, detect_reader); + with_view_model( + detect_reader->view, + DetectReaderViewModel * model, + { model->uid_str = furi_string_alloc(); }, + false); + return detect_reader; } void detect_reader_free(DetectReader* detect_reader) { furi_assert(detect_reader); + with_view_model( + detect_reader->view, + DetectReaderViewModel * model, + { furi_string_free(model->uid_str); }, + false); + view_free(detect_reader->view); free(detect_reader); } @@ -106,6 +125,7 @@ void detect_reader_reset(DetectReader* detect_reader) { model->nonces = 0; model->nonces_max = 0; model->state = DetectReaderStateStart; + furi_string_reset(model->uid_str); }, false); } @@ -152,3 +172,19 @@ void detect_reader_set_state(DetectReader* detect_reader, DetectReaderState stat with_view_model( detect_reader->view, DetectReaderViewModel * model, { model->state = state; }, true); } + +void detect_reader_set_uid(DetectReader* detect_reader, uint8_t* uid, uint8_t uid_len) { + furi_assert(detect_reader); + furi_assert(uid); + furi_assert(uid_len < DETECT_READER_UID_MAX_LEN); + with_view_model( + detect_reader->view, + DetectReaderViewModel * model, + { + furi_string_set_str(model->uid_str, "UID:"); + for(size_t i = 0; i < uid_len; i++) { + furi_string_cat_printf(model->uid_str, " %02X", uid[i]); + } + }, + true); +} diff --git a/applications/main/nfc/views/detect_reader.h b/applications/main/nfc/views/detect_reader.h index aabdd7c87..6481216b4 100644 --- a/applications/main/nfc/views/detect_reader.h +++ b/applications/main/nfc/views/detect_reader.h @@ -32,3 +32,5 @@ void detect_reader_set_nonces_max(DetectReader* detect_reader, uint16_t nonces_m void detect_reader_set_nonces_collected(DetectReader* detect_reader, uint16_t nonces_collected); void detect_reader_set_state(DetectReader* detect_reader, DetectReaderState state); + +void detect_reader_set_uid(DetectReader* detect_reader, uint8_t* uid, uint8_t uid_len); diff --git a/firmware/targets/f7/furi_hal/furi_hal_nfc.c b/firmware/targets/f7/furi_hal/furi_hal_nfc.c index 069ac4ea4..3ebf4f82b 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_nfc.c +++ b/firmware/targets/f7/furi_hal/furi_hal_nfc.c @@ -620,6 +620,10 @@ uint16_t furi_hal_nfc_bitstream_to_data_and_parity( uint16_t in_buff_bits, uint8_t* out_data, uint8_t* out_parity) { + if(in_buff_bits < 8) { + out_data[0] = in_buff[0]; + return in_buff_bits; + } if(in_buff_bits % 9 != 0) { return 0; } @@ -635,7 +639,7 @@ uint16_t furi_hal_nfc_bitstream_to_data_and_parity( bit_processed += 9; curr_byte++; } - return curr_byte; + return curr_byte * 8; } bool furi_hal_nfc_tx_rx(FuriHalNfcTxRxContext* tx_rx, uint16_t timeout_ms) { @@ -692,8 +696,8 @@ bool furi_hal_nfc_tx_rx(FuriHalNfcTxRxContext* tx_rx, uint16_t timeout_ms) { if(tx_rx->tx_rx_type == FuriHalNfcTxRxTypeRaw || tx_rx->tx_rx_type == FuriHalNfcTxRxTypeRxRaw) { - tx_rx->rx_bits = 8 * furi_hal_nfc_bitstream_to_data_and_parity( - temp_rx_buff, *temp_rx_bits, tx_rx->rx_data, tx_rx->rx_parity); + tx_rx->rx_bits = furi_hal_nfc_bitstream_to_data_and_parity( + temp_rx_buff, *temp_rx_bits, tx_rx->rx_data, tx_rx->rx_parity); } else { memcpy(tx_rx->rx_data, temp_rx_buff, MIN(*temp_rx_bits / 8, FURI_HAL_NFC_DATA_BUFF_SIZE)); tx_rx->rx_bits = *temp_rx_bits; diff --git a/lib/nfc/helpers/reader_analyzer.c b/lib/nfc/helpers/reader_analyzer.c index 0ba657a2e..7fed9c6f6 100644 --- a/lib/nfc/helpers/reader_analyzer.c +++ b/lib/nfc/helpers/reader_analyzer.c @@ -201,10 +201,17 @@ NfcProtocol FuriHalNfcDevData* reader_analyzer_get_nfc_data(ReaderAnalyzer* instance) { furi_assert(instance); - + instance->nfc_data = reader_analyzer_nfc_data[ReaderAnalyzerNfcDataMfClassic]; return &instance->nfc_data; } +void reader_analyzer_set_nfc_data(ReaderAnalyzer* instance, FuriHalNfcDevData* nfc_data) { + furi_assert(instance); + furi_assert(nfc_data); + + memcpy(&instance->nfc_data, nfc_data, sizeof(FuriHalNfcDevData)); +} + static void reader_analyzer_write( ReaderAnalyzer* instance, uint8_t* data, diff --git a/lib/nfc/helpers/reader_analyzer.h b/lib/nfc/helpers/reader_analyzer.h index cc501f5a6..13bf4d77c 100644 --- a/lib/nfc/helpers/reader_analyzer.h +++ b/lib/nfc/helpers/reader_analyzer.h @@ -35,6 +35,8 @@ NfcProtocol FuriHalNfcDevData* reader_analyzer_get_nfc_data(ReaderAnalyzer* instance); +void reader_analyzer_set_nfc_data(ReaderAnalyzer* instance, FuriHalNfcDevData* nfc_data); + void reader_analyzer_prepare_tx_rx( ReaderAnalyzer* instance, FuriHalNfcTxRxContext* tx_rx, diff --git a/lib/nfc/nfc_device.c b/lib/nfc/nfc_device.c index 740cfae5e..a5e3fc14f 100644 --- a/lib/nfc/nfc_device.c +++ b/lib/nfc/nfc_device.c @@ -1122,6 +1122,13 @@ static bool nfc_device_load_data(NfcDevice* dev, FuriString* path, bool show_dia if(!flipper_format_read_hex(file, "UID", data->uid, data->uid_len)) break; if(!flipper_format_read_hex(file, "ATQA", data->atqa, 2)) break; if(!flipper_format_read_hex(file, "SAK", &data->sak, 1)) break; + // Load CUID + uint8_t* cuid_start = data->uid; + if(data->uid_len == 7) { + cuid_start = &data->uid[3]; + } + data->cuid = (cuid_start[0] << 24) | (cuid_start[1] << 16) | (cuid_start[2] << 8) | + (cuid_start[3]); // Parse other data if(dev->format == NfcDeviceSaveFormatMifareUl) { if(!nfc_device_load_mifare_ul_data(file, dev)) break; diff --git a/lib/nfc/nfc_worker.c b/lib/nfc/nfc_worker.c index ebe203905..e1e379a06 100644 --- a/lib/nfc/nfc_worker.c +++ b/lib/nfc/nfc_worker.c @@ -99,6 +99,10 @@ int32_t nfc_worker_task(void* context) { 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 == NfcWorkerStateMfClassicWrite) { + nfc_worker_write_mf_classic(nfc_worker); + } else if(nfc_worker->state == NfcWorkerStateMfClassicUpdate) { + nfc_worker_update_mf_classic(nfc_worker); } else if(nfc_worker->state == NfcWorkerStateReadMfUltralightReadAuth) { nfc_worker_mf_ultralight_read_auth(nfc_worker); } else if(nfc_worker->state == NfcWorkerStateMfClassicDictAttack) { @@ -666,6 +670,144 @@ void nfc_worker_emulate_mf_classic(NfcWorker* nfc_worker) { rfal_platform_spi_release(); } +void nfc_worker_write_mf_classic(NfcWorker* nfc_worker) { + FuriHalNfcTxRxContext tx_rx = {}; + bool card_found_notified = false; + FuriHalNfcDevData nfc_data = {}; + MfClassicData* src_data = &nfc_worker->dev_data->mf_classic_data; + MfClassicData dest_data = *src_data; + + while(nfc_worker->state == NfcWorkerStateMfClassicWrite) { + if(furi_hal_nfc_detect(&nfc_data, 200)) { + if(!card_found_notified) { + nfc_worker->callback(NfcWorkerEventCardDetected, nfc_worker->context); + card_found_notified = true; + } + furi_hal_nfc_sleep(); + + FURI_LOG_I(TAG, "Check low level nfc data"); + if(memcmp(&nfc_data, &nfc_worker->dev_data->nfc_data, sizeof(FuriHalNfcDevData))) { + FURI_LOG_E(TAG, "Wrong card"); + nfc_worker->callback(NfcWorkerEventWrongCard, nfc_worker->context); + break; + } + + FURI_LOG_I(TAG, "Check mf classic type"); + MfClassicType type = + mf_classic_get_classic_type(nfc_data.atqa[0], nfc_data.atqa[1], nfc_data.sak); + if(type != nfc_worker->dev_data->mf_classic_data.type) { + FURI_LOG_E(TAG, "Wrong mf classic type"); + nfc_worker->callback(NfcWorkerEventWrongCard, nfc_worker->context); + break; + } + + // Set blocks not read + mf_classic_set_sector_data_not_read(&dest_data); + FURI_LOG_I(TAG, "Updating card sectors"); + uint8_t total_sectors = mf_classic_get_total_sectors_num(type); + bool write_success = true; + for(uint8_t i = 0; i < total_sectors; i++) { + FURI_LOG_I(TAG, "Reading sector %d", i); + mf_classic_read_sector(&tx_rx, &dest_data, i); + bool old_data_read = mf_classic_is_sector_data_read(src_data, i); + bool new_data_read = mf_classic_is_sector_data_read(&dest_data, i); + if(old_data_read != new_data_read) { + FURI_LOG_E(TAG, "Failed to update sector %d", i); + write_success = false; + break; + } + if(nfc_worker->state != NfcWorkerStateMfClassicWrite) break; + if(!mf_classic_write_sector(&tx_rx, &dest_data, src_data, i)) { + FURI_LOG_E(TAG, "Failed to write %d sector", i); + write_success = false; + break; + } + } + if(nfc_worker->state != NfcWorkerStateMfClassicWrite) break; + if(write_success) { + nfc_worker->callback(NfcWorkerEventSuccess, nfc_worker->context); + break; + } else { + nfc_worker->callback(NfcWorkerEventFail, nfc_worker->context); + break; + } + + } else { + if(card_found_notified) { + nfc_worker->callback(NfcWorkerEventNoCardDetected, nfc_worker->context); + card_found_notified = false; + } + } + furi_delay_ms(300); + } +} + +void nfc_worker_update_mf_classic(NfcWorker* nfc_worker) { + FuriHalNfcTxRxContext tx_rx = {}; + bool card_found_notified = false; + FuriHalNfcDevData nfc_data = {}; + MfClassicData* old_data = &nfc_worker->dev_data->mf_classic_data; + MfClassicData new_data = *old_data; + + while(nfc_worker->state == NfcWorkerStateMfClassicUpdate) { + if(furi_hal_nfc_detect(&nfc_data, 200)) { + if(!card_found_notified) { + nfc_worker->callback(NfcWorkerEventCardDetected, nfc_worker->context); + card_found_notified = true; + } + furi_hal_nfc_sleep(); + + FURI_LOG_I(TAG, "Check low level nfc data"); + if(memcmp(&nfc_data, &nfc_worker->dev_data->nfc_data, sizeof(FuriHalNfcDevData))) { + FURI_LOG_E(TAG, "Low level nfc data mismatch"); + nfc_worker->callback(NfcWorkerEventWrongCard, nfc_worker->context); + break; + } + + FURI_LOG_I(TAG, "Check MF classic type"); + MfClassicType type = + mf_classic_get_classic_type(nfc_data.atqa[0], nfc_data.atqa[1], nfc_data.sak); + if(type != nfc_worker->dev_data->mf_classic_data.type) { + FURI_LOG_E(TAG, "MF classic type mismatch"); + nfc_worker->callback(NfcWorkerEventWrongCard, nfc_worker->context); + break; + } + + // Set blocks not read + mf_classic_set_sector_data_not_read(&new_data); + FURI_LOG_I(TAG, "Updating card sectors"); + uint8_t total_sectors = mf_classic_get_total_sectors_num(type); + bool update_success = true; + for(uint8_t i = 0; i < total_sectors; i++) { + FURI_LOG_I(TAG, "Reading sector %d", i); + mf_classic_read_sector(&tx_rx, &new_data, i); + bool old_data_read = mf_classic_is_sector_data_read(old_data, i); + bool new_data_read = mf_classic_is_sector_data_read(&new_data, i); + if(old_data_read != new_data_read) { + FURI_LOG_E(TAG, "Failed to update sector %d", i); + update_success = false; + break; + } + if(nfc_worker->state != NfcWorkerStateMfClassicUpdate) break; + } + if(nfc_worker->state != NfcWorkerStateMfClassicUpdate) break; + + // Check updated data + if(update_success) { + *old_data = new_data; + nfc_worker->callback(NfcWorkerEventSuccess, nfc_worker->context); + break; + } + } else { + if(card_found_notified) { + nfc_worker->callback(NfcWorkerEventNoCardDetected, nfc_worker->context); + card_found_notified = false; + } + } + furi_delay_ms(300); + } +} + void nfc_worker_mf_ultralight_read_auth(NfcWorker* nfc_worker) { furi_assert(nfc_worker); furi_assert(nfc_worker->callback); @@ -758,7 +900,13 @@ void nfc_worker_analyze_reader(NfcWorker* nfc_worker) { FuriHalNfcTxRxContext tx_rx = {}; ReaderAnalyzer* reader_analyzer = nfc_worker->reader_analyzer; - FuriHalNfcDevData* nfc_data = reader_analyzer_get_nfc_data(reader_analyzer); + FuriHalNfcDevData* nfc_data = NULL; + if(nfc_worker->dev_data->protocol == NfcDeviceProtocolMifareClassic) { + nfc_data = &nfc_worker->dev_data->nfc_data; + reader_analyzer_set_nfc_data(reader_analyzer, nfc_data); + } else { + nfc_data = reader_analyzer_get_nfc_data(reader_analyzer); + } MfClassicEmulator emulator = { .cuid = nfc_util_bytes2num(&nfc_data->uid[nfc_data->uid_len - 4], 4), .data = nfc_worker->dev_data->mf_classic_data, diff --git a/lib/nfc/nfc_worker.h b/lib/nfc/nfc_worker.h index 84615f5d8..ce3a18241 100644 --- a/lib/nfc/nfc_worker.h +++ b/lib/nfc/nfc_worker.h @@ -14,6 +14,8 @@ typedef enum { NfcWorkerStateUidEmulate, NfcWorkerStateMfUltralightEmulate, NfcWorkerStateMfClassicEmulate, + NfcWorkerStateMfClassicWrite, + NfcWorkerStateMfClassicUpdate, NfcWorkerStateReadMfUltralightReadAuth, NfcWorkerStateMfClassicDictAttack, NfcWorkerStateAnalyzeReader, @@ -48,13 +50,16 @@ typedef enum { NfcWorkerEventNoCardDetected, NfcWorkerEventWrongCardDetected, - // Mifare Classic events + // Read Mifare Classic events NfcWorkerEventNoDictFound, NfcWorkerEventNewSector, NfcWorkerEventNewDictKeyBatch, NfcWorkerEventFoundKeyA, NfcWorkerEventFoundKeyB, + // Write Mifare Classic events + NfcWorkerEventWrongCard, + // Detect Reader events NfcWorkerEventDetectReaderDetected, NfcWorkerEventDetectReaderLost, diff --git a/lib/nfc/nfc_worker_i.h b/lib/nfc/nfc_worker_i.h index 526182f9a..b9f69e620 100644 --- a/lib/nfc/nfc_worker_i.h +++ b/lib/nfc/nfc_worker_i.h @@ -41,6 +41,10 @@ void nfc_worker_emulate_mf_ultralight(NfcWorker* nfc_worker); void nfc_worker_emulate_mf_classic(NfcWorker* nfc_worker); +void nfc_worker_write_mf_classic(NfcWorker* nfc_worker); + +void nfc_worker_update_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); diff --git a/lib/nfc/protocols/crypto1.c b/lib/nfc/protocols/crypto1.c index f08164ba9..2ac0ff081 100644 --- a/lib/nfc/protocols/crypto1.c +++ b/lib/nfc/protocols/crypto1.c @@ -73,3 +73,55 @@ uint32_t prng_successor(uint32_t x, uint32_t n) { return SWAPENDIAN(x); } + +void crypto1_decrypt( + Crypto1* crypto, + uint8_t* encrypted_data, + uint16_t encrypted_data_bits, + uint8_t* decrypted_data) { + furi_assert(crypto); + furi_assert(encrypted_data); + furi_assert(decrypted_data); + + if(encrypted_data_bits < 8) { + uint8_t decrypted_byte = 0; + decrypted_byte |= (crypto1_bit(crypto, 0, 0) ^ FURI_BIT(encrypted_data[0], 0)) << 0; + decrypted_byte |= (crypto1_bit(crypto, 0, 0) ^ FURI_BIT(encrypted_data[0], 1)) << 1; + decrypted_byte |= (crypto1_bit(crypto, 0, 0) ^ FURI_BIT(encrypted_data[0], 2)) << 2; + decrypted_byte |= (crypto1_bit(crypto, 0, 0) ^ FURI_BIT(encrypted_data[0], 3)) << 3; + decrypted_data[0] = decrypted_byte; + } else { + for(size_t i = 0; i < encrypted_data_bits / 8; i++) { + decrypted_data[i] = crypto1_byte(crypto, 0, 0) ^ encrypted_data[i]; + } + } +} + +void crypto1_encrypt( + Crypto1* crypto, + uint8_t* keystream, + uint8_t* plain_data, + uint16_t plain_data_bits, + uint8_t* encrypted_data, + uint8_t* encrypted_parity) { + furi_assert(crypto); + furi_assert(plain_data); + furi_assert(encrypted_data); + furi_assert(encrypted_parity); + + if(plain_data_bits < 8) { + encrypted_data[0] = 0; + for(size_t i = 0; i < plain_data_bits; i++) { + encrypted_data[0] |= (crypto1_bit(crypto, 0, 0) ^ FURI_BIT(plain_data[0], i)) << i; + } + } else { + memset(encrypted_parity, 0, plain_data_bits / 8 + 1); + for(uint8_t i = 0; i < plain_data_bits / 8; i++) { + encrypted_data[i] = crypto1_byte(crypto, keystream ? keystream[i] : 0, 0) ^ + plain_data[i]; + encrypted_parity[i / 8] |= + (((crypto1_filter(crypto->odd) ^ nfc_util_odd_parity8(plain_data[i])) & 0x01) + << (7 - (i & 0x0007))); + } + } +} diff --git a/lib/nfc/protocols/crypto1.h b/lib/nfc/protocols/crypto1.h index 07b39c22c..450d1534e 100644 --- a/lib/nfc/protocols/crypto1.h +++ b/lib/nfc/protocols/crypto1.h @@ -21,3 +21,17 @@ uint32_t crypto1_word(Crypto1* crypto1, uint32_t in, int is_encrypted); uint32_t crypto1_filter(uint32_t in); uint32_t prng_successor(uint32_t x, uint32_t n); + +void crypto1_decrypt( + Crypto1* crypto, + uint8_t* encrypted_data, + uint16_t encrypted_data_bits, + uint8_t* decrypted_data); + +void crypto1_encrypt( + Crypto1* crypto, + uint8_t* keystream, + uint8_t* plain_data, + uint16_t plain_data_bits, + uint8_t* encrypted_data, + uint8_t* encrypted_parity); diff --git a/lib/nfc/protocols/mifare_classic.c b/lib/nfc/protocols/mifare_classic.c index e879ff4ef..7b0e17975 100644 --- a/lib/nfc/protocols/mifare_classic.c +++ b/lib/nfc/protocols/mifare_classic.c @@ -9,21 +9,8 @@ #define MF_CLASSIC_AUTH_KEY_A_CMD (0x60U) #define MF_CLASSIC_AUTH_KEY_B_CMD (0x61U) -#define MF_CLASSIC_READ_SECT_CMD (0x30) - -typedef enum { - MfClassicActionDataRead, - MfClassicActionDataWrite, - MfClassicActionDataInc, - MfClassicActionDataDec, - - MfClassicActionKeyARead, - MfClassicActionKeyAWrite, - MfClassicActionKeyBRead, - MfClassicActionKeyBWrite, - MfClassicActionACRead, - MfClassicActionACWrite, -} MfClassicAction; +#define MF_CLASSIC_READ_BLOCK_CMD (0x30) +#define MF_CLASSIC_WRITE_BLOCK_CMD (0xA0) const char* mf_classic_get_type_str(MfClassicType type) { if(type == MfClassicType1k) { @@ -122,6 +109,24 @@ void mf_classic_set_block_read(MfClassicData* data, uint8_t block_num, MfClassic FURI_BIT_SET(data->block_read_mask[block_num / 32], block_num % 32); } +bool mf_classic_is_sector_data_read(MfClassicData* data, uint8_t sector_num) { + furi_assert(data); + + uint8_t first_block = mf_classic_get_first_block_num_of_sector(sector_num); + uint8_t total_blocks = mf_classic_get_blocks_num_in_sector(sector_num); + bool data_read = true; + for(size_t i = first_block; i < first_block + total_blocks; i++) { + data_read &= mf_classic_is_block_read(data, i); + } + + return data_read; +} + +void mf_classic_set_sector_data_not_read(MfClassicData* data) { + furi_assert(data); + memset(data->block_read_mask, 0, sizeof(data->block_read_mask)); +} + bool mf_classic_is_key_found(MfClassicData* data, uint8_t sector_num, MfClassicKey key_type) { furi_assert(data); @@ -190,6 +195,9 @@ void mf_classic_get_read_sectors_and_keys( uint8_t* sectors_read, uint8_t* keys_found) { furi_assert(data); + furi_assert(sectors_read); + furi_assert(keys_found); + *sectors_read = 0; *keys_found = 0; uint8_t sectors_total = mf_classic_get_total_sectors_num(data->type); @@ -225,12 +233,12 @@ bool mf_classic_is_card_read(MfClassicData* data) { return card_read; } -static bool mf_classic_is_allowed_access_sector_trailer( - MfClassicEmulator* emulator, +bool mf_classic_is_allowed_access_sector_trailer( + MfClassicData* data, uint8_t block_num, MfClassicKey key, MfClassicAction action) { - uint8_t* sector_trailer = emulator->data.block[block_num].value; + uint8_t* sector_trailer = data->block[block_num].value; uint8_t AC = ((sector_trailer[7] >> 5) & 0x04) | ((sector_trailer[8] >> 2) & 0x02) | ((sector_trailer[8] >> 7) & 0x01); switch(action) { @@ -266,13 +274,13 @@ static bool mf_classic_is_allowed_access_sector_trailer( return true; } -static bool mf_classic_is_allowed_access_data_block( - MfClassicEmulator* emulator, +bool mf_classic_is_allowed_access_data_block( + MfClassicData* data, uint8_t block_num, MfClassicKey key, MfClassicAction action) { uint8_t* sector_trailer = - emulator->data.block[mf_classic_get_sector_trailer_num_by_block(block_num)].value; + data->block[mf_classic_get_sector_trailer_num_by_block(block_num)].value; uint8_t sector_block; if(block_num <= 128) { @@ -336,9 +344,10 @@ static bool mf_classic_is_allowed_access( MfClassicKey key, MfClassicAction action) { if(mf_classic_is_sector_trailer(block_num)) { - return mf_classic_is_allowed_access_sector_trailer(emulator, block_num, key, action); + return mf_classic_is_allowed_access_sector_trailer( + &emulator->data, block_num, key, action); } else { - return mf_classic_is_allowed_access_data_block(emulator, block_num, key, action); + return mf_classic_is_allowed_access_data_block(&emulator->data, block_num, key, action); } } @@ -514,25 +523,17 @@ bool mf_classic_read_block( furi_assert(block); bool read_block_success = false; - uint8_t plain_cmd[4] = {MF_CLASSIC_READ_SECT_CMD, block_num, 0x00, 0x00}; + uint8_t plain_cmd[4] = {MF_CLASSIC_READ_BLOCK_CMD, block_num, 0x00, 0x00}; nfca_append_crc16(plain_cmd, 2); - memset(tx_rx->tx_data, 0, sizeof(tx_rx->tx_data)); - memset(tx_rx->tx_parity, 0, sizeof(tx_rx->tx_parity)); - for(uint8_t i = 0; i < 4; i++) { - tx_rx->tx_data[i] = crypto1_byte(crypto, 0x00, 0) ^ plain_cmd[i]; - tx_rx->tx_parity[0] |= - ((crypto1_filter(crypto->odd) ^ nfc_util_odd_parity8(plain_cmd[i])) & 0x01) << (7 - i); - } + crypto1_encrypt(crypto, NULL, plain_cmd, 4 * 8, tx_rx->tx_data, tx_rx->tx_parity); tx_rx->tx_bits = 4 * 9; tx_rx->tx_rx_type = FuriHalNfcTxRxTypeRaw; if(furi_hal_nfc_tx_rx(tx_rx, 50)) { if(tx_rx->rx_bits == 8 * (MF_CLASSIC_BLOCK_SIZE + 2)) { uint8_t block_received[MF_CLASSIC_BLOCK_SIZE + 2]; - for(uint8_t i = 0; i < MF_CLASSIC_BLOCK_SIZE + 2; i++) { - block_received[i] = crypto1_byte(crypto, 0, 0) ^ tx_rx->rx_data[i]; - } + crypto1_decrypt(crypto, tx_rx->rx_data, tx_rx->rx_bits, block_received); uint16_t crc_calc = nfca_get_crc16(block_received, MF_CLASSIC_BLOCK_SIZE); uint16_t crc_received = (block_received[MF_CLASSIC_BLOCK_SIZE + 1] << 8) | block_received[MF_CLASSIC_BLOCK_SIZE]; @@ -754,49 +755,6 @@ uint8_t mf_classic_update_card(FuriHalNfcTxRxContext* tx_rx, MfClassicData* data return sectors_read; } -void mf_crypto1_decrypt( - Crypto1* crypto, - uint8_t* encrypted_data, - uint16_t encrypted_data_bits, - uint8_t* decrypted_data) { - if(encrypted_data_bits < 8) { - uint8_t decrypted_byte = 0; - decrypted_byte |= (crypto1_bit(crypto, 0, 0) ^ FURI_BIT(encrypted_data[0], 0)) << 0; - decrypted_byte |= (crypto1_bit(crypto, 0, 0) ^ FURI_BIT(encrypted_data[0], 1)) << 1; - decrypted_byte |= (crypto1_bit(crypto, 0, 0) ^ FURI_BIT(encrypted_data[0], 2)) << 2; - decrypted_byte |= (crypto1_bit(crypto, 0, 0) ^ FURI_BIT(encrypted_data[0], 3)) << 3; - decrypted_data[0] = decrypted_byte; - } else { - for(size_t i = 0; i < encrypted_data_bits / 8; i++) { - decrypted_data[i] = crypto1_byte(crypto, 0, 0) ^ encrypted_data[i]; - } - } -} - -void mf_crypto1_encrypt( - Crypto1* crypto, - uint8_t* keystream, - uint8_t* plain_data, - uint16_t plain_data_bits, - uint8_t* encrypted_data, - uint8_t* encrypted_parity) { - if(plain_data_bits < 8) { - encrypted_data[0] = 0; - for(size_t i = 0; i < plain_data_bits; i++) { - encrypted_data[0] |= (crypto1_bit(crypto, 0, 0) ^ FURI_BIT(plain_data[0], i)) << i; - } - } else { - memset(encrypted_parity, 0, plain_data_bits / 8 + 1); - for(uint8_t i = 0; i < plain_data_bits / 8; i++) { - encrypted_data[i] = crypto1_byte(crypto, keystream ? keystream[i] : 0, 0) ^ - plain_data[i]; - encrypted_parity[i / 8] |= - (((crypto1_filter(crypto->odd) ^ nfc_util_odd_parity8(plain_data[i])) & 0x01) - << (7 - (i & 0x0007))); - } - } -} - bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_rx) { furi_assert(emulator); furi_assert(tx_rx); @@ -819,7 +777,7 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_ tx_rx->rx_bits); break; } - mf_crypto1_decrypt(&emulator->crypto, tx_rx->rx_data, tx_rx->rx_bits, plain_data); + crypto1_decrypt(&emulator->crypto, tx_rx->rx_data, tx_rx->rx_bits, plain_data); } if(plain_data[0] == 0x50 && plain_data[1] == 0x00) { @@ -857,7 +815,7 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_ tx_rx->tx_bits = sizeof(nt) * 8; tx_rx->tx_rx_type = FuriHalNfcTxRxTransparent; } else { - mf_crypto1_encrypt( + crypto1_encrypt( &emulator->crypto, nt_keystream, nt, @@ -904,7 +862,7 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_ uint32_t ans = prng_successor(nonce, 96); uint8_t responce[4] = {}; nfc_util_num2bytes(ans, 4, responce); - mf_crypto1_encrypt( + crypto1_encrypt( &emulator->crypto, NULL, responce, @@ -938,7 +896,7 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_ // Send NACK uint8_t nack = 0x04; if(is_encrypted) { - mf_crypto1_encrypt( + crypto1_encrypt( &emulator->crypto, NULL, &nack, 4, tx_rx->tx_data, tx_rx->tx_parity); } else { tx_rx->tx_data[0] = nack; @@ -951,7 +909,7 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_ } nfca_append_crc16(block_data, 16); - mf_crypto1_encrypt( + crypto1_encrypt( &emulator->crypto, NULL, block_data, @@ -967,14 +925,14 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_ } // Send ACK uint8_t ack = 0x0A; - mf_crypto1_encrypt(&emulator->crypto, NULL, &ack, 4, tx_rx->tx_data, tx_rx->tx_parity); + crypto1_encrypt(&emulator->crypto, NULL, &ack, 4, tx_rx->tx_data, tx_rx->tx_parity); tx_rx->tx_rx_type = FuriHalNfcTxRxTransparent; tx_rx->tx_bits = 4; if(!furi_hal_nfc_tx_rx(tx_rx, 300)) break; if(tx_rx->rx_bits != 18 * 8) break; - mf_crypto1_decrypt(&emulator->crypto, tx_rx->rx_data, tx_rx->rx_bits, plain_data); + crypto1_decrypt(&emulator->crypto, tx_rx->rx_data, tx_rx->rx_bits, plain_data); uint8_t block_data[16] = {}; memcpy(block_data, emulator->data.block[block].value, MF_CLASSIC_BLOCK_SIZE); if(mf_classic_is_sector_trailer(block)) { @@ -1002,7 +960,7 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_ } // Send ACK ack = 0x0A; - mf_crypto1_encrypt(&emulator->crypto, NULL, &ack, 4, tx_rx->tx_data, tx_rx->tx_parity); + crypto1_encrypt(&emulator->crypto, NULL, &ack, 4, tx_rx->tx_data, tx_rx->tx_parity); tx_rx->tx_rx_type = FuriHalNfcTxRxTransparent; tx_rx->tx_bits = 4; } else { @@ -1015,8 +973,7 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_ // 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); + crypto1_encrypt(&emulator->crypto, NULL, &nack, 4, tx_rx->tx_data, tx_rx->tx_parity); } else { tx_rx->tx_data[0] = nack; } @@ -1027,3 +984,143 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_ return true; } + +bool mf_classic_write_block( + FuriHalNfcTxRxContext* tx_rx, + MfClassicBlock* src_block, + uint8_t block_num, + MfClassicKey key_type, + uint64_t key) { + furi_assert(tx_rx); + furi_assert(src_block); + + Crypto1 crypto = {}; + uint8_t plain_data[18] = {}; + uint8_t resp = 0; + bool write_success = false; + + do { + furi_hal_nfc_sleep(); + if(!mf_classic_auth(tx_rx, block_num, key, key_type, &crypto)) { + FURI_LOG_D(TAG, "Auth fail"); + break; + } + // Send write command + plain_data[0] = MF_CLASSIC_WRITE_BLOCK_CMD; + plain_data[1] = block_num; + nfca_append_crc16(plain_data, 2); + crypto1_encrypt(&crypto, NULL, plain_data, 4 * 8, tx_rx->tx_data, tx_rx->tx_parity); + tx_rx->tx_bits = 4 * 8; + tx_rx->tx_rx_type = FuriHalNfcTxRxTypeRaw; + + if(furi_hal_nfc_tx_rx(tx_rx, 50)) { + if(tx_rx->rx_bits == 4) { + crypto1_decrypt(&crypto, tx_rx->rx_data, 4, &resp); + if(resp != 0x0A) { + FURI_LOG_D(TAG, "NACK received on write cmd: %02X", resp); + break; + } + } else { + FURI_LOG_D(TAG, "Not ACK received"); + break; + } + } else { + FURI_LOG_D(TAG, "Failed to send write cmd"); + break; + } + + // Send data + memcpy(plain_data, src_block->value, MF_CLASSIC_BLOCK_SIZE); + nfca_append_crc16(plain_data, MF_CLASSIC_BLOCK_SIZE); + crypto1_encrypt( + &crypto, + NULL, + plain_data, + (MF_CLASSIC_BLOCK_SIZE + 2) * 8, + tx_rx->tx_data, + tx_rx->tx_parity); + tx_rx->tx_bits = (MF_CLASSIC_BLOCK_SIZE + 2) * 8; + tx_rx->tx_rx_type = FuriHalNfcTxRxTypeRaw; + if(furi_hal_nfc_tx_rx(tx_rx, 50)) { + if(tx_rx->rx_bits == 4) { + crypto1_decrypt(&crypto, tx_rx->rx_data, 4, &resp); + if(resp != 0x0A) { + FURI_LOG_D(TAG, "NACK received on sending data"); + break; + } + } else { + FURI_LOG_D(TAG, "Not ACK received"); + break; + } + } else { + FURI_LOG_D(TAG, "Failed to send data"); + break; + } + write_success = true; + + // Send Halt + plain_data[0] = 0x50; + plain_data[1] = 0x00; + nfca_append_crc16(plain_data, 2); + crypto1_encrypt(&crypto, NULL, plain_data, 2 * 8, tx_rx->tx_data, tx_rx->tx_parity); + tx_rx->tx_bits = 2 * 8; + tx_rx->tx_rx_type = FuriHalNfcTxRxTypeRaw; + // No response is expected + furi_hal_nfc_tx_rx(tx_rx, 50); + } while(false); + + return write_success; +} + +bool mf_classic_write_sector( + FuriHalNfcTxRxContext* tx_rx, + MfClassicData* dest_data, + MfClassicData* src_data, + uint8_t sec_num) { + furi_assert(tx_rx); + furi_assert(dest_data); + furi_assert(src_data); + + uint8_t first_block = mf_classic_get_first_block_num_of_sector(sec_num); + uint8_t total_blocks = mf_classic_get_blocks_num_in_sector(sec_num); + MfClassicSectorTrailer* sec_tr = mf_classic_get_sector_trailer_by_sector(dest_data, sec_num); + bool key_a_found = mf_classic_is_key_found(dest_data, sec_num, MfClassicKeyA); + bool key_b_found = mf_classic_is_key_found(dest_data, sec_num, MfClassicKeyB); + + bool write_success = true; + for(size_t i = first_block; i < first_block + total_blocks; i++) { + // Compare blocks + if(memcmp(dest_data->block[i].value, src_data->block[i].value, MF_CLASSIC_BLOCK_SIZE)) { + bool key_a_write_allowed = mf_classic_is_allowed_access_data_block( + dest_data, i, MfClassicKeyA, MfClassicActionDataWrite); + bool key_b_write_allowed = mf_classic_is_allowed_access_data_block( + dest_data, i, MfClassicKeyB, MfClassicActionDataWrite); + + if(key_a_found && key_a_write_allowed) { + FURI_LOG_I(TAG, "Writing block %d with key A", i); + uint64_t key = nfc_util_bytes2num(sec_tr->key_a, 6); + if(!mf_classic_write_block(tx_rx, &src_data->block[i], i, MfClassicKeyA, key)) { + FURI_LOG_E(TAG, "Failed to write block %d", i); + write_success = false; + break; + } + } else if(key_b_found && key_b_write_allowed) { + FURI_LOG_I(TAG, "Writing block %d with key A", i); + uint64_t key = nfc_util_bytes2num(sec_tr->key_b, 6); + if(!mf_classic_write_block(tx_rx, &src_data->block[i], i, MfClassicKeyB, key)) { + FURI_LOG_E(TAG, "Failed to write block %d", i); + write_success = false; + break; + } + } else { + FURI_LOG_E(TAG, "Failed to find key with write access"); + write_success = false; + break; + } + } else { + FURI_LOG_D(TAG, "Blocks %d are equal", i); + } + } + + return write_success; +} diff --git a/lib/nfc/protocols/mifare_classic.h b/lib/nfc/protocols/mifare_classic.h index ead846e42..d5467b100 100644 --- a/lib/nfc/protocols/mifare_classic.h +++ b/lib/nfc/protocols/mifare_classic.h @@ -27,6 +27,20 @@ typedef enum { MfClassicKeyB, } MfClassicKey; +typedef enum { + MfClassicActionDataRead, + MfClassicActionDataWrite, + MfClassicActionDataInc, + MfClassicActionDataDec, + + MfClassicActionKeyARead, + MfClassicActionKeyAWrite, + MfClassicActionKeyBRead, + MfClassicActionKeyBWrite, + MfClassicActionACRead, + MfClassicActionACWrite, +} MfClassicAction; + typedef struct { uint8_t value[MF_CLASSIC_BLOCK_SIZE]; } MfClassicBlock; @@ -90,6 +104,18 @@ bool mf_classic_is_sector_trailer(uint8_t block); uint8_t mf_classic_get_sector_by_block(uint8_t block); +bool mf_classic_is_allowed_access_sector_trailer( + MfClassicData* data, + uint8_t block_num, + MfClassicKey key, + MfClassicAction action); + +bool mf_classic_is_allowed_access_data_block( + MfClassicData* data, + uint8_t block_num, + MfClassicKey key, + MfClassicAction action); + bool mf_classic_is_key_found(MfClassicData* data, uint8_t sector_num, MfClassicKey key_type); void mf_classic_set_key_found( @@ -104,6 +130,10 @@ 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_data_read(MfClassicData* data, uint8_t sector_num); + +void mf_classic_set_sector_data_not_read(MfClassicData* data); + bool mf_classic_is_sector_read(MfClassicData* data, uint8_t sector_num); bool mf_classic_is_card_read(MfClassicData* data); @@ -145,3 +175,16 @@ uint8_t mf_classic_read_card( uint8_t mf_classic_update_card(FuriHalNfcTxRxContext* tx_rx, MfClassicData* data); bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_rx); + +bool mf_classic_write_block( + FuriHalNfcTxRxContext* tx_rx, + MfClassicBlock* src_block, + uint8_t block_num, + MfClassicKey key_type, + uint64_t key); + +bool mf_classic_write_sector( + FuriHalNfcTxRxContext* tx_rx, + MfClassicData* dest_data, + MfClassicData* src_data, + uint8_t sec_num); From d5f791b1fa8ee34428b92f6c0c2ddac9d6fcbe0a Mon Sep 17 00:00:00 2001 From: Georgii Surkov <37121527+gsurkov@users.noreply.github.com> Date: Fri, 28 Oct 2022 19:43:54 +0300 Subject: [PATCH 21/31] [FL-2911] IR Universal Audio Remote (#1942) * Add Audio universal remote * Add signal library for Audio Universal Remote * Update UniversalRemotes.md * Added IR profile for Samsung K450 soundbar (#1892) * Add symbols to API file * Rearrange Audio remote buttons * Add new icons, remove old ones * Remove old signals, add new ones * Add universal audio remote to CLI, refactor code * Improve help text * Correct formatting * Update UniversalRemotes.md * Furi: restore correct api_symbols.csv version Co-authored-by: Alexei Humeniy Co-authored-by: Aleksandr Kutuzov --- applications/main/infrared/infrared_cli.c | 211 ++++++--------- .../infrared/scenes/infrared_scene_config.h | 1 + .../scenes/infrared_scene_universal.c | 8 +- .../scenes/infrared_scene_universal_audio.c | 133 ++++++++++ assets/icons/Infrared/Pause_25x27.png | Bin 0 -> 3634 bytes assets/icons/Infrared/Pause_hvr_25x27.png | Bin 0 -> 3623 bytes assets/icons/Infrared/Play_25x27.png | Bin 0 -> 3653 bytes assets/icons/Infrared/Play_hvr_25x27.png | Bin 0 -> 3643 bytes assets/icons/Infrared/TrackNext_25x27.png | Bin 0 -> 3651 bytes assets/icons/Infrared/TrackNext_hvr_25x27.png | Bin 0 -> 3639 bytes assets/icons/Infrared/TrackPrev_25x27.png | Bin 0 -> 3657 bytes assets/icons/Infrared/TrackPrev_hvr_25x27.png | Bin 0 -> 3644 bytes assets/resources/infrared/assets/audio.ir | 244 ++++++++++++++++++ documentation/UniversalRemotes.md | 16 +- 14 files changed, 486 insertions(+), 127 deletions(-) create mode 100644 applications/main/infrared/scenes/infrared_scene_universal_audio.c create mode 100644 assets/icons/Infrared/Pause_25x27.png create mode 100644 assets/icons/Infrared/Pause_hvr_25x27.png create mode 100644 assets/icons/Infrared/Play_25x27.png create mode 100644 assets/icons/Infrared/Play_hvr_25x27.png create mode 100644 assets/icons/Infrared/TrackNext_25x27.png create mode 100644 assets/icons/Infrared/TrackNext_hvr_25x27.png create mode 100644 assets/icons/Infrared/TrackPrev_25x27.png create mode 100644 assets/icons/Infrared/TrackPrev_hvr_25x27.png create mode 100644 assets/resources/infrared/assets/audio.ir diff --git a/applications/main/infrared/infrared_cli.c b/applications/main/infrared/infrared_cli.c index 5a04f7495..8f35a8fd1 100644 --- a/applications/main/infrared/infrared_cli.c +++ b/applications/main/infrared/infrared_cli.c @@ -5,25 +5,21 @@ #include #include #include +#include #include "infrared_signal.h" #include "infrared_brute_force.h" -#include - #define INFRARED_CLI_BUF_SIZE 10 +#define INFRARED_ASSETS_FOLDER "infrared/assets" +#define INFRARED_BRUTE_FORCE_DUMMY_INDEX 0 DICT_DEF2(dict_signals, FuriString*, FURI_STRING_OPLIST, int, M_DEFAULT_OPLIST) -enum RemoteTypes { TV = 0, AC = 1 }; - static void infrared_cli_start_ir_rx(Cli* cli, FuriString* args); static void infrared_cli_start_ir_tx(Cli* cli, FuriString* args); static void infrared_cli_process_decode(Cli* cli, FuriString* args); static void infrared_cli_process_universal(Cli* cli, FuriString* args); -static void infrared_cli_list_remote_signals(enum RemoteTypes remote_type); -static void - infrared_cli_brute_force_signals(Cli* cli, enum RemoteTypes remote_type, FuriString* signal); static const struct { const char* cmd; @@ -87,8 +83,10 @@ static void infrared_cli_print_usage(void) { INFRARED_MIN_FREQUENCY, INFRARED_MAX_FREQUENCY); printf("\tir decode []\r\n"); - printf("\tir universal \r\n"); - printf("\tir universal list \r\n"); + printf("\tir universal \r\n"); + printf("\tir universal list \r\n"); + // TODO: Do not hardcode universal remote names + printf("\tAvailable universal remotes: tv audio ac\r\n"); } static void infrared_cli_start_ir_rx(Cli* cli, FuriString* args) { @@ -356,89 +354,31 @@ static void infrared_cli_process_decode(Cli* cli, FuriString* args) { furi_record_close(RECORD_STORAGE); } -static void infrared_cli_process_universal(Cli* cli, FuriString* args) { - enum RemoteTypes Remote; - - FuriString* command; - FuriString* remote; - FuriString* signal; - command = furi_string_alloc(); - remote = furi_string_alloc(); - signal = furi_string_alloc(); - - do { - if(!args_read_string_and_trim(args, command)) { - infrared_cli_print_usage(); - break; - } - - if(furi_string_cmp_str(command, "list") == 0) { - args_read_string_and_trim(args, remote); - if(furi_string_cmp_str(remote, "tv") == 0) { - Remote = TV; - } else if(furi_string_cmp_str(remote, "ac") == 0) { - Remote = AC; - } else { - printf("Invalid remote type.\r\n"); - break; - } - infrared_cli_list_remote_signals(Remote); - break; - } - - if(furi_string_cmp_str(command, "tv") == 0) { - Remote = TV; - } else if(furi_string_cmp_str(command, "ac") == 0) { - Remote = AC; - } else { - printf("Invalid remote type.\r\n"); - break; - } - - args_read_string_and_trim(args, signal); - if(furi_string_empty(signal)) { - printf("Must supply a valid signal for type of remote selected.\r\n"); - break; - } - - infrared_cli_brute_force_signals(cli, Remote, signal); - break; - - } while(false); - - furi_string_free(command); - furi_string_free(remote); - furi_string_free(signal); -} - -static void infrared_cli_list_remote_signals(enum RemoteTypes remote_type) { - Storage* storage = furi_record_open(RECORD_STORAGE); - FlipperFormat* ff = flipper_format_buffered_file_alloc(storage); - dict_signals_t signals_dict; - FuriString* key; - const char* remote_file = NULL; - bool success = false; - int max = 1; - - switch(remote_type) { - case TV: - remote_file = EXT_PATH("infrared/assets/tv.ir"); - break; - case AC: - remote_file = EXT_PATH("infrared/assets/ac.ir"); - break; - default: - break; +static void infrared_cli_list_remote_signals(FuriString* remote_name) { + if(furi_string_empty(remote_name)) { + printf("Missing remote name.\r\n"); + return; } - dict_signals_init(signals_dict); - key = furi_string_alloc(); + Storage* storage = furi_record_open(RECORD_STORAGE); + FlipperFormat* ff = flipper_format_buffered_file_alloc(storage); + FuriString* remote_path = furi_string_alloc_printf( + "%s/%s.ir", EXT_PATH(INFRARED_ASSETS_FOLDER), furi_string_get_cstr(remote_name)); + + do { + if(!flipper_format_buffered_file_open_existing(ff, furi_string_get_cstr(remote_path))) { + printf("Invalid remote name.\r\n"); + break; + } + + dict_signals_t signals_dict; + dict_signals_init(signals_dict); + + FuriString* key = furi_string_alloc(); + FuriString* signal_name = furi_string_alloc(); - success = flipper_format_buffered_file_open_existing(ff, remote_file); - if(success) { - FuriString* signal_name; - signal_name = furi_string_alloc(); printf("Valid signals:\r\n"); + int max = 1; while(flipper_format_read_string(ff, "name", signal_name)) { furi_string_set_str(key, furi_string_get_cstr(signal_name)); int* v = dict_signals_get(signals_dict, key); @@ -449,57 +389,57 @@ static void infrared_cli_list_remote_signals(enum RemoteTypes remote_type) { dict_signals_set_at(signals_dict, key, 1); } } + dict_signals_it_t it; for(dict_signals_it(it, signals_dict); !dict_signals_end_p(it); dict_signals_next(it)) { const struct dict_signals_pair_s* pair = dict_signals_cref(it); printf("\t%s\r\n", furi_string_get_cstr(pair->key)); } - furi_string_free(signal_name); - } - furi_string_free(key); - dict_signals_clear(signals_dict); + furi_string_free(key); + furi_string_free(signal_name); + dict_signals_clear(signals_dict); + + } while(false); + flipper_format_free(ff); + furi_string_free(remote_path); furi_record_close(RECORD_STORAGE); } static void - infrared_cli_brute_force_signals(Cli* cli, enum RemoteTypes remote_type, FuriString* signal) { + infrared_cli_brute_force_signals(Cli* cli, FuriString* remote_name, FuriString* signal_name) { InfraredBruteForce* brute_force = infrared_brute_force_alloc(); - const char* remote_file = NULL; - uint32_t i = 0; - bool success = false; + FuriString* remote_path = furi_string_alloc_printf( + "%s/%s.ir", EXT_PATH(INFRARED_ASSETS_FOLDER), furi_string_get_cstr(remote_name)); - switch(remote_type) { - case TV: - remote_file = EXT_PATH("infrared/assets/tv.ir"); - break; - case AC: - remote_file = EXT_PATH("infrared/assets/ac.ir"); - break; - default: - break; - } + infrared_brute_force_set_db_filename(brute_force, furi_string_get_cstr(remote_path)); + infrared_brute_force_add_record( + brute_force, INFRARED_BRUTE_FORCE_DUMMY_INDEX, furi_string_get_cstr(signal_name)); - infrared_brute_force_set_db_filename(brute_force, remote_file); - infrared_brute_force_add_record(brute_force, i++, furi_string_get_cstr(signal)); - - success = infrared_brute_force_calculate_messages(brute_force); - if(success) { - uint32_t record_count; - uint32_t index = 0; - int records_sent = 0; - bool running = false; - - running = infrared_brute_force_start(brute_force, index, &record_count); - if(record_count <= 0) { - printf("Invalid signal.\n"); - infrared_brute_force_reset(brute_force); - return; + do { + if(furi_string_empty(signal_name)) { + printf("Missing signal name.\r\n"); + break; + } + if(!infrared_brute_force_calculate_messages(brute_force)) { + printf("Invalid remote name.\r\n"); + break; } - printf("Sending %ld codes to the tv.\r\n", record_count); + uint32_t record_count; + bool running = infrared_brute_force_start( + brute_force, INFRARED_BRUTE_FORCE_DUMMY_INDEX, &record_count); + + if(record_count <= 0) { + printf("Invalid signal name.\r\n"); + break; + } + + printf("Sending %ld signal(s)...\r\n", record_count); printf("Press Ctrl-C to stop.\r\n"); + + int records_sent = 0; while(running) { running = infrared_brute_force_send_next(brute_force); @@ -510,14 +450,35 @@ static void } infrared_brute_force_stop(brute_force); - } else { - printf("Invalid signal.\r\n"); - } + } while(false); + furi_string_free(remote_path); infrared_brute_force_reset(brute_force); infrared_brute_force_free(brute_force); } +static void infrared_cli_process_universal(Cli* cli, FuriString* args) { + FuriString* arg1 = furi_string_alloc(); + FuriString* arg2 = furi_string_alloc(); + + do { + if(!args_read_string_and_trim(args, arg1)) break; + if(!args_read_string_and_trim(args, arg2)) break; + } while(false); + + if(furi_string_empty(arg1)) { + printf("Wrong arguments.\r\n"); + infrared_cli_print_usage(); + } else if(furi_string_equal_str(arg1, "list")) { + infrared_cli_list_remote_signals(arg2); + } else { + infrared_cli_brute_force_signals(cli, arg1, arg2); + } + + furi_string_free(arg1); + furi_string_free(arg2); +} + static void infrared_cli_start_ir(Cli* cli, FuriString* args, void* context) { UNUSED(context); if(furi_hal_infrared_is_busy()) { diff --git a/applications/main/infrared/scenes/infrared_scene_config.h b/applications/main/infrared/scenes/infrared_scene_config.h index 22125fb79..111fd2d31 100644 --- a/applications/main/infrared/scenes/infrared_scene_config.h +++ b/applications/main/infrared/scenes/infrared_scene_config.h @@ -16,6 +16,7 @@ ADD_SCENE(infrared, remote_list, RemoteList) ADD_SCENE(infrared, universal, Universal) ADD_SCENE(infrared, universal_tv, UniversalTV) ADD_SCENE(infrared, universal_ac, UniversalAC) +ADD_SCENE(infrared, universal_audio, UniversalAudio) ADD_SCENE(infrared, debug, Debug) ADD_SCENE(infrared, error_databases, ErrorDatabases) ADD_SCENE(infrared, rpc, Rpc) diff --git a/applications/main/infrared/scenes/infrared_scene_universal.c b/applications/main/infrared/scenes/infrared_scene_universal.c index 2bd7082c4..914360d78 100644 --- a/applications/main/infrared/scenes/infrared_scene_universal.c +++ b/applications/main/infrared/scenes/infrared_scene_universal.c @@ -21,6 +21,12 @@ void infrared_scene_universal_on_enter(void* context) { SubmenuIndexUniversalTV, infrared_scene_universal_submenu_callback, context); + submenu_add_item( + submenu, + "Audio Players", + SubmenuIndexUniversalAudio, + infrared_scene_universal_submenu_callback, + context); submenu_add_item( submenu, "Air Conditioners", @@ -45,7 +51,7 @@ bool infrared_scene_universal_on_event(void* context, SceneManagerEvent event) { scene_manager_next_scene(scene_manager, InfraredSceneUniversalAC); consumed = true; } else if(event.event == SubmenuIndexUniversalAudio) { - //TODO Implement Audio universal remote + scene_manager_next_scene(scene_manager, InfraredSceneUniversalAudio); consumed = true; } } diff --git a/applications/main/infrared/scenes/infrared_scene_universal_audio.c b/applications/main/infrared/scenes/infrared_scene_universal_audio.c new file mode 100644 index 000000000..00c86fff4 --- /dev/null +++ b/applications/main/infrared/scenes/infrared_scene_universal_audio.c @@ -0,0 +1,133 @@ +#include "../infrared_i.h" + +#include "common/infrared_scene_universal_common.h" + +void infrared_scene_universal_audio_on_enter(void* context) { + infrared_scene_universal_common_on_enter(context); + + Infrared* infrared = context; + ButtonPanel* button_panel = infrared->button_panel; + InfraredBruteForce* brute_force = infrared->brute_force; + + infrared_brute_force_set_db_filename(brute_force, EXT_PATH("infrared/assets/audio.ir")); + + button_panel_reserve(button_panel, 2, 4); + uint32_t i = 0; + button_panel_add_item( + button_panel, + i, + 0, + 0, + 3, + 11, + &I_Power_25x27, + &I_Power_hvr_25x27, + infrared_scene_universal_common_item_callback, + context); + infrared_brute_force_add_record(brute_force, i++, "Power"); + button_panel_add_item( + button_panel, + i, + 1, + 0, + 36, + 11, + &I_Mute_25x27, + &I_Mute_hvr_25x27, + infrared_scene_universal_common_item_callback, + context); + infrared_brute_force_add_record(brute_force, i++, "Mute"); + button_panel_add_item( + button_panel, + i, + 0, + 1, + 3, + 41, + &I_Play_25x27, + &I_Play_hvr_25x27, + infrared_scene_universal_common_item_callback, + context); + infrared_brute_force_add_record(brute_force, i++, "Play"); + button_panel_add_item( + button_panel, + i, + 1, + 1, + 36, + 41, + &I_Pause_25x27, + &I_Pause_hvr_25x27, + infrared_scene_universal_common_item_callback, + context); + infrared_brute_force_add_record(brute_force, i++, "Pause"); + button_panel_add_item( + button_panel, + i, + 0, + 2, + 3, + 71, + &I_TrackPrev_25x27, + &I_TrackPrev_hvr_25x27, + infrared_scene_universal_common_item_callback, + context); + infrared_brute_force_add_record(brute_force, i++, "Prev"); + button_panel_add_item( + button_panel, + i, + 1, + 2, + 36, + 71, + &I_TrackNext_25x27, + &I_TrackNext_hvr_25x27, + infrared_scene_universal_common_item_callback, + context); + infrared_brute_force_add_record(brute_force, i++, "Next"); + button_panel_add_item( + button_panel, + i, + 0, + 3, + 3, + 101, + &I_Vol_down_25x27, + &I_Vol_down_hvr_25x27, + infrared_scene_universal_common_item_callback, + context); + infrared_brute_force_add_record(brute_force, i++, "Vol_dn"); + button_panel_add_item( + button_panel, + i, + 1, + 3, + 36, + 101, + &I_Vol_up_25x27, + &I_Vol_up_hvr_25x27, + infrared_scene_universal_common_item_callback, + context); + infrared_brute_force_add_record(brute_force, i++, "Vol_up"); + + button_panel_add_label(button_panel, 1, 8, FontPrimary, "Mus. remote"); + + view_set_orientation(view_stack_get_view(infrared->view_stack), ViewOrientationVertical); + view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewStack); + + infrared_show_loading_popup(infrared, true); + bool success = infrared_brute_force_calculate_messages(brute_force); + infrared_show_loading_popup(infrared, false); + + if(!success) { + scene_manager_next_scene(infrared->scene_manager, InfraredSceneErrorDatabases); + } +} + +bool infrared_scene_universal_audio_on_event(void* context, SceneManagerEvent event) { + return infrared_scene_universal_common_on_event(context, event); +} + +void infrared_scene_universal_audio_on_exit(void* context) { + infrared_scene_universal_common_on_exit(context); +} diff --git a/assets/icons/Infrared/Pause_25x27.png b/assets/icons/Infrared/Pause_25x27.png new file mode 100644 index 0000000000000000000000000000000000000000..a371ba81718e864efe908e61f34bc842079cc20d GIT binary patch literal 3634 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 literal 0 HcmV?d00001 diff --git a/assets/icons/Infrared/Pause_hvr_25x27.png b/assets/icons/Infrared/Pause_hvr_25x27.png new file mode 100644 index 0000000000000000000000000000000000000000..472d583db0ff9adc5ef810675c8b915c6e5a899c GIT binary patch literal 3623 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 HpN{x9W3^Ap literal 0 HcmV?d00001 diff --git a/assets/icons/Infrared/Play_hvr_25x27.png b/assets/icons/Infrared/Play_hvr_25x27.png new file mode 100644 index 0000000000000000000000000000000000000000..6708dcdbf2c7ac0c656bbb1c2441fb0f4d6fc9c5 GIT binary patch literal 3643 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 SxG~3f0odCdwl1|i74;vAA8iN# literal 0 HcmV?d00001 diff --git a/assets/icons/Infrared/TrackNext_hvr_25x27.png b/assets/icons/Infrared/TrackNext_hvr_25x27.png new file mode 100644 index 0000000000000000000000000000000000000000..a4de4fc3cbe582349b8c79bbb7ec6e931e8bc792 GIT binary patch literal 3639 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 W@<`O{ko&lf02|BW7A0mL7ybo9V{1nM literal 0 HcmV?d00001 diff --git a/assets/icons/Infrared/TrackPrev_hvr_25x27.png b/assets/icons/Infrared/TrackPrev_hvr_25x27.png new file mode 100644 index 0000000000000000000000000000000000000000..838055341e568fed60b3e28a280fe839d2c9db9e GIT binary patch literal 3644 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 in order to keep the library organised. + +When done, open a pull request containing the changed file. + ## Air Conditioners ### Recording signals Air conditioners differ from most other infrared-controlled devices because their state is tracked by the remote. @@ -31,7 +45,7 @@ Finally, record the `Off` signal: 4. Save the resulting signal under the name `Off`. The resulting remote file should now contain 6 signals. Any of them can be omitted, but that will mean that this functionality will not be used. -Test the file against the actual device. Every signal must do what it's supposed to. +Test the file against the actual device. Make sure that every signal does what it's supposed to. If everything checks out, append these signals **to the end** of the [A/C universal remote file](/assets/resources/infrared/assets/ac.ir). From 85d341104f32b509c70ed8808655697d25963df3 Mon Sep 17 00:00:00 2001 From: Vladimir <74153654+touchscan@users.noreply.github.com> Date: Fri, 28 Oct 2022 19:50:07 +0300 Subject: [PATCH 22/31] Update ac.ir (#1945) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Added conditioner Saturn CS-TL09CHR ir signals (Dh, Cool_hi, Cool_lo, Heat_hi, Heat_lo, Off) Co-authored-by: あく --- assets/resources/infrared/assets/ac.ir | 37 ++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/assets/resources/infrared/assets/ac.ir b/assets/resources/infrared/assets/ac.ir index 97c384591..7ef953059 100644 --- a/assets/resources/infrared/assets/ac.ir +++ b/assets/resources/infrared/assets/ac.ir @@ -111,4 +111,41 @@ type: raw frequency: 38000 duty_cycle: 0.330000 data: 9106 4398 731 499 706 500 705 502 702 504 701 505 701 505 701 1606 701 505 701 1607 701 505 701 506 700 1607 700 506 700 506 700 505 700 505 701 506 700 506 700 506 699 506 700 506 700 1607 700 506 700 506 700 506 700 505 701 506 700 506 700 1608 699 506 700 1608 699 506 700 506 700 1608 700 506 700 19941 701 1606 700 505 701 505 701 506 700 505 700 506 700 506 700 506 700 506 700 506 700 506 700 506 700 506 701 506 700 506 700 506 700 506 700 506 700 506 700 506 700 506 700 506 700 506 700 506 700 506 700 506 699 506 700 506 700 1608 700 1607 700 506 700 506 700 +# +# Model: Saturn CS-TL09CHR +name: Dh +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3014 1708 488 1059 464 1085 461 362 461 363 460 387 436 1085 462 362 461 402 436 1084 463 1083 463 364 459 1083 463 363 460 386 437 1082 518 1044 464 386 437 1108 439 1108 438 386 464 360 463 1082 464 360 490 352 459 1084 462 362 460 363 460 363 460 364 459 364 459 364 459 380 459 364 459 364 459 364 460 364 459 364 459 364 459 364 459 380 459 364 459 365 458 1088 459 364 459 365 458 1088 458 365 458 380 459 365 458 1088 459 365 458 364 459 365 459 365 458 365 458 380 458 1088 459 1088 459 1088 458 365 458 365 458 365 458 365 458 381 458 365 458 365 458 365 458 365 458 365 459 365 458 365 458 381 458 366 457 366 457 366 457 366 457 366 458 365 458 366 458 381 457 366 457 366 457 366 457 366 457 366 457 366 457 366 458 382 457 366 457 366 457 367 456 367 457 367 456 367 456 390 433 406 433 367 456 367 456 390 433 391 433 390 433 390 433 390 433 406 433 391 432 1114 432 391 432 391 432 391 432 391 432 1114 433 396 433 +# +name: Cool_hi +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3011 1710 462 1084 462 1085 486 356 467 356 444 364 484 1060 462 364 483 357 458 1085 461 1085 461 388 435 1085 461 388 435 388 435 1084 462 1099 463 387 436 1084 462 1085 461 388 461 362 461 1084 462 362 461 378 460 1087 459 365 458 365 458 366 457 366 457 366 457 366 457 382 457 366 457 366 457 366 457 367 456 367 456 367 456 367 456 382 457 367 456 367 456 1090 457 367 456 367 456 1090 457 367 456 382 457 1090 456 1090 457 367 456 367 456 367 456 367 456 367 456 382 457 1091 456 1091 456 1091 456 1090 456 367 456 367 456 367 457 382 457 367 456 367 456 367 456 367 456 368 456 367 456 368 455 383 456 367 456 367 456 368 455 368 455 368 455 368 456 368 455 383 456 368 455 368 455 368 455 368 455 368 455 368 455 368 455 383 456 368 455 368 455 368 455 368 455 368 455 368 455 368 455 384 455 368 455 368 455 368 455 368 455 368 455 368 455 368 455 383 456 1091 456 1091 456 368 456 1091 455 368 455 368 455 1091 456 373 456 +# +name: Cool_lo +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3012 1709 462 1083 463 1084 463 361 462 362 484 355 469 1059 463 387 436 402 437 1083 464 1083 464 362 462 1080 520 354 469 354 469 1026 521 1068 518 354 390 1108 439 1108 438 386 463 360 464 1081 465 359 464 375 463 1083 463 361 462 362 461 363 460 363 460 364 459 364 459 380 459 364 459 364 459 365 458 365 458 365 458 365 458 365 458 380 459 365 458 365 458 1089 458 365 459 365 458 1089 458 366 457 382 456 1090 457 1113 433 390 433 390 433 390 433 390 433 390 433 405 434 390 433 390 433 390 433 1113 434 390 433 390 433 390 433 405 434 390 433 390 433 390 434 390 433 390 433 390 433 390 433 405 434 390 433 390 433 390 433 390 433 390 433 390 433 390 434 405 433 390 433 390 433 390 433 390 433 390 433 390 433 390 433 406 433 390 433 390 433 390 433 390 433 390 433 391 432 391 432 406 433 390 433 391 432 391 432 391 433 390 433 390 433 391 432 406 433 391 432 391 432 1114 433 391 432 391 432 391 432 1114 433 396 433 +# +name: Heat_hi +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3011 1712 461 1087 459 1088 459 364 459 389 434 366 458 1086 461 388 435 404 435 1086 460 1085 461 388 435 1085 461 365 458 388 435 1084 462 1098 464 387 436 1084 462 1084 462 388 461 362 461 1084 462 362 461 378 460 1086 460 364 459 365 458 365 458 365 458 366 457 365 458 381 458 366 457 366 458 366 457 366 457 366 457 366 457 365 458 381 458 366 458 366 457 1089 458 366 457 366 457 1089 458 366 457 381 458 1089 458 366 457 366 457 366 457 366 458 366 457 366 457 381 458 366 457 366 457 366 457 366 457 366 457 366 457 366 457 381 458 366 457 366 457 366 458 366 457 366 457 366 457 366 457 382 457 366 457 366 457 366 457 366 457 366 457 366 457 366 457 382 457 366 457 366 457 367 456 367 456 367 457 366 457 366 457 382 457 367 457 366 457 367 457 366 457 367 457 366 457 366 457 382 457 367 456 367 456 367 456 367 456 367 456 367 456 367 456 382 457 367 456 1090 457 367 456 1091 456 1090 457 1090 456 367 456 373 456 +# +name: Heat_lo +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3045 1677 493 1023 524 1024 522 354 469 354 469 354 469 1026 576 353 470 350 487 1001 492 1054 492 354 469 1054 492 355 468 354 469 1054 492 1070 491 354 468 1056 438 1109 438 386 437 385 438 1107 439 385 438 400 464 1081 465 359 464 360 463 361 462 362 461 388 435 388 435 404 434 389 434 389 434 389 434 389 434 389 434 389 434 389 434 405 434 389 434 389 434 1112 434 389 434 389 434 1113 434 389 434 405 434 1112 435 389 434 366 458 365 458 365 458 365 458 365 458 381 458 365 458 366 457 365 458 1089 457 366 457 366 457 366 457 382 456 367 433 390 433 391 432 391 432 391 432 391 432 391 432 406 433 391 432 391 432 390 433 391 432 391 432 391 432 391 432 406 433 391 432 391 433 391 432 391 432 391 432 391 432 391 432 407 432 391 432 391 432 391 432 392 431 391 433 391 432 391 432 430 409 393 430 392 431 392 431 392 432 391 457 367 432 392 431 408 431 392 431 1138 433 391 408 392 431 392 456 367 456 1113 408 421 433 +# +name: Off +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3013 1709 463 1085 462 1084 463 387 461 355 469 355 444 1084 463 387 461 378 436 1084 462 1084 487 355 469 1059 463 387 460 355 444 1083 463 1098 464 386 437 1083 463 1083 489 361 463 360 463 1082 464 361 462 376 462 1085 461 363 460 364 459 364 459 365 458 365 459 364 459 380 459 365 458 365 458 365 458 365 458 365 458 365 458 365 459 380 458 365 459 365 458 365 459 365 458 365 458 1089 458 365 458 380 459 1088 459 365 458 365 458 365 458 365 459 365 458 365 458 381 458 365 458 365 458 365 458 1089 458 365 458 365 458 365 458 381 458 365 458 365 458 365 458 365 458 365 458 365 458 365 458 381 457 366 457 366 457 366 457 366 457 366 458 365 458 366 457 381 458 366 458 366 457 366 457 366 457 366 457 366 457 366 457 381 458 366 457 366 457 366 457 366 457 366 457 366 457 366 457 382 457 366 457 366 457 366 457 366 457 366 457 367 457 366 457 382 457 367 456 1090 457 1090 456 1090 457 1090 457 1090 456 367 457 372 457 From 104a1998a5e2f77cea99c31311541864fb98d4c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=82=E3=81=8F?= Date: Tue, 1 Nov 2022 19:27:25 +0900 Subject: [PATCH 23/31] Furi: raise bkpt only if debug session initiated, add debug support for release builds (#1957) * Fix hard crash on some custom firmwares in RELEASE mode * Furi: anya wa erai, anya wa eleganto, raise bkpt only if debug session initiated, add debug support for release builds Co-authored-by: DerSkythe --- furi/core/check.c | 59 ++++++++++++++++++++++++++++++++--------------- 1 file changed, 41 insertions(+), 18 deletions(-) diff --git a/furi/core/check.c b/furi/core/check.c index 00c20575a..1c2a005f3 100644 --- a/furi/core/check.c +++ b/furi/core/check.c @@ -1,6 +1,7 @@ #include "check.h" #include "common_defines.h" +#include #include #include #include @@ -24,16 +25,30 @@ PLACE_IN_SECTION("MB_MEM2") uint32_t __furi_check_registers[12] = {0}; : \ : "memory"); -// Restore registers and halt MCU -#define RESTORE_REGISTERS_AND_HALT_MCU() \ - asm volatile("ldr r12, =__furi_check_registers \n" \ - "ldm r12, {r0-r11} \n" \ +/** Restore registers and halt MCU + * + * - Always use it with GET_MESSAGE_AND_STORE_REGISTERS + * - If debugger is(was) connected this routine will raise bkpt + * - If debugger is not connected then endless loop + * + */ +#define RESTORE_REGISTERS_AND_HALT_MCU(debug) \ + register const bool r0 asm("r0") = debug; \ + asm volatile("cbnz r0, with_debugger%= \n" \ + "ldr r12, =__furi_check_registers\n" \ + "ldm r12, {r0-r11} \n" \ "loop%=: \n" \ - "bkpt 0x00 \n" \ "wfi \n" \ - "b loop%= \n" \ - : \ + "b loop%= \n" \ + "with_debugger%=: \n" \ + "ldr r12, =__furi_check_registers\n" \ + "ldm r12, {r0-r11} \n" \ + "debug_loop%=: \n" \ + "bkpt 0x00 \n" \ + "wfi \n" \ + "b debug_loop%= \n" \ : \ + : "r"(r0) \ : "memory"); extern size_t xPortGetTotalHeapSize(void); @@ -96,16 +111,19 @@ FURI_NORETURN void __furi_crash() { } __furi_print_heap_info(); -#ifdef FURI_DEBUG - furi_hal_console_puts("\r\nSystem halted. Connect debugger for more info\r\n"); - furi_hal_console_puts("\033[0m\r\n"); - RESTORE_REGISTERS_AND_HALT_MCU(); -#else - furi_hal_rtc_set_fault_data((uint32_t)__furi_check_message); - furi_hal_console_puts("\r\nRebooting system.\r\n"); - furi_hal_console_puts("\033[0m\r\n"); - furi_hal_power_reset(); -#endif + // Check if debug enabled by DAP + // https://developer.arm.com/documentation/ddi0403/d/Debug-Architecture/ARMv7-M-Debug/Debug-register-support-in-the-SCS/Debug-Halting-Control-and-Status-Register--DHCSR?lang=en + bool debug = CoreDebug->DHCSR & CoreDebug_DHCSR_C_DEBUGEN_Msk; + if(debug) { + furi_hal_console_puts("\r\nSystem halted. Connect debugger for more info\r\n"); + furi_hal_console_puts("\033[0m\r\n"); + RESTORE_REGISTERS_AND_HALT_MCU(debug); + } else { + furi_hal_rtc_set_fault_data((uint32_t)__furi_check_message); + furi_hal_console_puts("\r\nRebooting system.\r\n"); + furi_hal_console_puts("\033[0m\r\n"); + furi_hal_power_reset(); + } __builtin_unreachable(); } @@ -124,6 +142,11 @@ FURI_NORETURN void __furi_halt() { furi_hal_console_puts(__furi_check_message); furi_hal_console_puts("\r\nSystem halted. Bye-bye!\r\n"); furi_hal_console_puts("\033[0m\r\n"); - RESTORE_REGISTERS_AND_HALT_MCU(); + + // Check if debug enabled by DAP + // https://developer.arm.com/documentation/ddi0403/d/Debug-Architecture/ARMv7-M-Debug/Debug-register-support-in-the-SCS/Debug-Halting-Control-and-Status-Register--DHCSR?lang=en + bool debug = CoreDebug->DHCSR & CoreDebug_DHCSR_C_DEBUGEN_Msk; + RESTORE_REGISTERS_AND_HALT_MCU(debug); + __builtin_unreachable(); } From abfa804ae0bf8c126b5dd78a92cc59c9cec14103 Mon Sep 17 00:00:00 2001 From: "Vasyl \"vk\" Kaigorodov" Date: Wed, 2 Nov 2022 15:36:17 +0100 Subject: [PATCH 24/31] Gui: refactor buttons remapping (#1949) * Gui: refactor buttons remapping Instead of calling 3 separate functions with a ton of switch/case statements, use a 2-dimensional lookup table to remap buttons based on the orientation. * Gui: cleanup input mapping and fix incorrect asserts * SnakeGame: handle input special case Co-authored-by: Aleksandr Kutuzov --- applications/plugins/snake_game/snake_game.c | 2 + applications/services/gui/view_port.c | 110 +++++++----------- applications/services/gui/view_port.h | 1 + applications/services/input/input.c | 3 +- applications/services/input/input.h | 1 + .../targets/f7/furi_hal/furi_hal_resources.h | 1 + 6 files changed, 51 insertions(+), 67 deletions(-) diff --git a/applications/plugins/snake_game/snake_game.c b/applications/plugins/snake_game/snake_game.c index 283d017ed..f9309c280 100644 --- a/applications/plugins/snake_game/snake_game.c +++ b/applications/plugins/snake_game/snake_game.c @@ -380,6 +380,8 @@ int32_t snake_game_app(void* p) { case InputKeyBack: processing = false; break; + default: + break; } } } else if(event.type == EventTypeTick) { diff --git a/applications/services/gui/view_port.c b/applications/services/gui/view_port.c index baa8f7bd2..ffd01450b 100644 --- a/applications/services/gui/view_port.c +++ b/applications/services/gui/view_port.c @@ -7,61 +7,51 @@ // TODO add mutex to view_port ops -static void view_port_remap_buttons_vertical(InputEvent* event) { - switch(event->key) { - case InputKeyUp: - event->key = InputKeyRight; - break; - case InputKeyDown: - event->key = InputKeyLeft; - break; - case InputKeyRight: - event->key = InputKeyDown; - break; - case InputKeyLeft: - event->key = InputKeyUp; - break; - default: - break; - } -} +_Static_assert(ViewPortOrientationMAX == 4, "Incorrect ViewPortOrientation count"); +_Static_assert( + (ViewPortOrientationHorizontal == 0 && ViewPortOrientationHorizontalFlip == 1 && + ViewPortOrientationVertical == 2 && ViewPortOrientationVerticalFlip == 3), + "Incorrect ViewPortOrientation order"); +_Static_assert(InputKeyMAX == 6, "Incorrect InputKey count"); +_Static_assert( + (InputKeyUp == 0 && InputKeyDown == 1 && InputKeyRight == 2 && InputKeyLeft == 3 && + InputKeyOk == 4 && InputKeyBack == 5), + "Incorrect InputKey order"); -static void view_port_remap_buttons_vertical_flip(InputEvent* event) { - switch(event->key) { - case InputKeyUp: - event->key = InputKeyLeft; - break; - case InputKeyDown: - event->key = InputKeyRight; - break; - case InputKeyRight: - event->key = InputKeyUp; - break; - case InputKeyLeft: - event->key = InputKeyDown; - break; - default: - break; - } -} +/** InputKey directional keys mappings for different screen orientations +* +*/ +static const InputKey view_port_input_mapping[ViewPortOrientationMAX][InputKeyMAX] = { + {InputKeyUp, + InputKeyDown, + InputKeyRight, + InputKeyLeft, + InputKeyOk, + InputKeyBack}, //ViewPortOrientationHorizontal + {InputKeyDown, + InputKeyUp, + InputKeyLeft, + InputKeyRight, + InputKeyOk, + InputKeyBack}, //ViewPortOrientationHorizontalFlip + {InputKeyRight, + InputKeyLeft, + InputKeyDown, + InputKeyUp, + InputKeyOk, + InputKeyBack}, //ViewPortOrientationVertical + {InputKeyLeft, + InputKeyRight, + InputKeyUp, + InputKeyDown, + InputKeyOk, + InputKeyBack}, //ViewPortOrientationVerticalFlip +}; -static void view_port_remap_buttons_horizontal_flip(InputEvent* event) { - switch(event->key) { - case InputKeyUp: - event->key = InputKeyDown; - break; - case InputKeyDown: - event->key = InputKeyUp; - break; - case InputKeyRight: - event->key = InputKeyLeft; - break; - case InputKeyLeft: - event->key = InputKeyRight; - break; - default: - break; - } +// Remaps directional pad buttons on Flipper based on ViewPort orientation +static void view_port_map_input(InputEvent* event, ViewPortOrientation orientation) { + furi_assert(orientation < ViewPortOrientationMAX && event->key < InputKeyMAX); + event->key = view_port_input_mapping[orientation][event->key]; } static void view_port_setup_canvas_orientation(const ViewPort* view_port, Canvas* canvas) { @@ -170,19 +160,7 @@ void view_port_input(ViewPort* view_port, InputEvent* event) { if(view_port->input_callback) { ViewPortOrientation orientation = view_port_get_orientation(view_port); - switch(orientation) { - case ViewPortOrientationHorizontalFlip: - view_port_remap_buttons_horizontal_flip(event); - break; - case ViewPortOrientationVertical: - view_port_remap_buttons_vertical(event); - break; - case ViewPortOrientationVerticalFlip: - view_port_remap_buttons_vertical_flip(event); - break; - default: - break; - } + view_port_map_input(event, orientation); view_port->input_callback(event, view_port->input_callback_context); } } diff --git a/applications/services/gui/view_port.h b/applications/services/gui/view_port.h index 169681ac0..703e99248 100644 --- a/applications/services/gui/view_port.h +++ b/applications/services/gui/view_port.h @@ -19,6 +19,7 @@ typedef enum { ViewPortOrientationHorizontalFlip, ViewPortOrientationVertical, ViewPortOrientationVerticalFlip, + ViewPortOrientationMAX, /**< Special value, don't use it */ } ViewPortOrientation; /** ViewPort Draw callback diff --git a/applications/services/input/input.c b/applications/services/input/input.c index 7b8433aef..d1aef9e8a 100644 --- a/applications/services/input/input.c +++ b/applications/services/input/input.c @@ -60,8 +60,9 @@ const char* input_get_type_name(InputType type) { return "Long"; case InputTypeRepeat: return "Repeat"; + default: + return "Unknown"; } - return "Unknown"; } int32_t input_srv(void* p) { diff --git a/applications/services/input/input.h b/applications/services/input/input.h index bd0ba3902..172b16361 100644 --- a/applications/services/input/input.h +++ b/applications/services/input/input.h @@ -22,6 +22,7 @@ typedef enum { InputTypeShort, /**< Short event, emitted after InputTypeRelease done withing INPUT_LONG_PRESS interval */ InputTypeLong, /**< Long event, emmited after INPUT_LONG_PRESS interval, asynchronouse to InputTypeRelease */ InputTypeRepeat, /**< Repeat event, emmited with INPUT_REPEATE_PRESS period after InputTypeLong event */ + InputTypeMAX, /**< Special value for exceptional */ } InputType; /** Input Event, dispatches with FuriPubSub */ diff --git a/firmware/targets/f7/furi_hal/furi_hal_resources.h b/firmware/targets/f7/furi_hal/furi_hal_resources.h index d16c567e6..8d53ec48a 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_resources.h +++ b/firmware/targets/f7/furi_hal/furi_hal_resources.h @@ -20,6 +20,7 @@ typedef enum { InputKeyLeft, InputKeyOk, InputKeyBack, + InputKeyMAX, /**< Special value */ } InputKey; /* Light */ From ebc2b66372091d811278bcb6180bfa16b62cd46f Mon Sep 17 00:00:00 2001 From: hedger Date: Wed, 2 Nov 2022 19:15:40 +0400 Subject: [PATCH 25/31] fbt fixes for mfbt pt2 (#1951) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fbt: split sdk management code * scripts: fixed import handling * fbt: sdk: reformatted paths * scrips: dist: bundling libs as a build artifact * fbt: sdk: better path management * typo fix * fbt: sdk: minor path handling fixes * toolchain: fixed windows toolchain download * fbt: minor refactorin * fbt: moved sdk management code to extapps.scons * fbt: fixed sdk symbols header path; disabled -fstack-usage * fbt: changed pathing for .py scripts * fbt: changed SDK_HEADERS pathing; added libusb to SDK; added icon_i.h to SDK; added hw target to SDK meta * fbt: added libusb headers to SDK * picopass: include cleanup; api: added subghz/registry.h; api: added mbedtls to exported headers * picopass: fixed formatting * fbt: fixed COPRO_ASSETS_SCRIPT * sdk: added basic infrared apis * toolchain: added ufbt to list of legal fbtenv callers; updated error messages * fbt: changed manifest collection & icon processing code * fbt: simpler srcdir lookup * toolchain: path management fixes; fbt: fixes for fap private libs paths * scripts: toolchain: reworked download on Windows * toolchain: v17 * scripts: added colorlog for logging * Github: fix unit tests Co-authored-by: あく --- .github/workflows/unit_tests.yml | 8 +- SConstruct | 10 +- applications/plugins/picopass/125_10px.png | Bin 0 -> 308 bytes applications/plugins/picopass/application.fam | 2 +- .../plugins/picopass/picopass_worker_i.h | 2 - applications/plugins/picopass/rfal_picopass.c | 7 +- applications/services/gui/application.fam | 1 + firmware.scons | 66 ++++------ firmware/SConscript | 7 +- firmware/targets/f7/api_symbols.csv | 124 +++++++++++++++++- lib/SConscript | 12 +- lib/STM32CubeWB.scons | 2 +- lib/flipper_application/SConscript | 2 +- lib/flipper_format/SConscript | 4 +- lib/infrared/SConscript | 5 + lib/lfrfid/SConscript | 12 +- lib/libusb_stm32.scons | 4 + lib/mbedtls.scons | 4 + lib/misc.scons | 2 +- lib/print/SConscript | 2 +- lib/subghz/SConscript | 25 ++-- lib/subghz/registry.h | 8 ++ lib/toolbox/SConscript | 34 ++--- scripts/fbt/util.py | 15 ++- scripts/fbt_tools/crosscc.py | 15 +++ scripts/fbt_tools/fbt_apps.py | 38 +++--- scripts/fbt_tools/fbt_assets.py | 20 ++- scripts/fbt_tools/fbt_dist.py | 9 +- scripts/fbt_tools/fbt_extapps.py | 24 +++- scripts/fbt_tools/fbt_sdk.py | 46 ++++--- scripts/fbt_tools/fbt_version.py | 5 +- scripts/fbt_tools/fwbin.py | 3 +- scripts/flipper/app.py | 16 ++- scripts/sconsdist.py | 6 +- scripts/testing/await_flipper.py | 0 scripts/testing/units.py | 0 scripts/toolchain/fbtenv.cmd | 13 +- scripts/toolchain/fbtenv.sh | 24 ++-- .../toolchain/windows-toolchain-download.ps1 | 38 ++++-- site_scons/cc.scons | 5 +- site_scons/environ.scons | 36 ++--- site_scons/extapps.scons | 38 +++++- 42 files changed, 459 insertions(+), 235 deletions(-) create mode 100644 applications/plugins/picopass/125_10px.png mode change 100644 => 100755 scripts/testing/await_flipper.py mode change 100644 => 100755 scripts/testing/units.py diff --git a/.github/workflows/unit_tests.yml b/.github/workflows/unit_tests.yml index b5bf10004..a7671f0f9 100644 --- a/.github/workflows/unit_tests.yml +++ b/.github/workflows/unit_tests.yml @@ -31,22 +31,26 @@ jobs: id: connect if: steps.compile.outcome == 'success' run: | - python3 ./scripts/testing/await_flipper.py ${{steps.device.outputs.flipper}} + . scripts/toolchain/fbtenv.sh + ./scripts/testing/await_flipper.py ${{steps.device.outputs.flipper}} - name: 'Format flipper SD card' id: format if: steps.connect.outcome == 'success' run: | + . scripts/toolchain/fbtenv.sh ./scripts/storage.py -p ${{steps.device.outputs.flipper}} format_ext - name: 'Copy assets and unit tests data to flipper' id: copy if: steps.format.outcome == 'success' run: | + . scripts/toolchain/fbtenv.sh ./scripts/storage.py -p ${{steps.device.outputs.flipper}} send assets/resources /ext ./scripts/storage.py -p ${{steps.device.outputs.flipper}} send assets/unit_tests /ext/unit_tests - name: 'Run units and validate results' if: steps.copy.outcome == 'success' run: | - python3 ./scripts/testing/units.py ${{steps.device.outputs.flipper}} + . scripts/toolchain/fbtenv.sh + ./scripts/testing/units.py ${{steps.device.outputs.flipper}} diff --git a/SConstruct b/SConstruct index 448df9715..13a698c81 100644 --- a/SConstruct +++ b/SConstruct @@ -33,10 +33,6 @@ coreenv = SConscript( ) SConscript("site_scons/cc.scons", exports={"ENV": coreenv}) -# Store root dir in environment for certain tools -coreenv["ROOT_DIR"] = Dir(".") - - # Create a separate "dist" environment and add construction envs to it distenv = coreenv.Clone( tools=[ @@ -233,13 +229,13 @@ distenv.PhonyTarget( # Linter distenv.PhonyTarget( "lint", - "${PYTHON3} scripts/lint.py check ${LINT_SOURCES}", + "${PYTHON3} ${FBT_SCRIPT_DIR}/lint.py check ${LINT_SOURCES}", LINT_SOURCES=firmware_env["LINT_SOURCES"], ) distenv.PhonyTarget( "format", - "${PYTHON3} scripts/lint.py format ${LINT_SOURCES}", + "${PYTHON3} ${FBT_SCRIPT_DIR}/lint.py format ${LINT_SOURCES}", LINT_SOURCES=firmware_env["LINT_SOURCES"], ) @@ -280,7 +276,7 @@ distenv.PhonyTarget( ) # Start Flipper CLI via PySerial's miniterm -distenv.PhonyTarget("cli", "${PYTHON3} scripts/serial_cli.py") +distenv.PhonyTarget("cli", "${PYTHON3} ${FBT_SCRIPT_DIR}/serial_cli.py") # Find blackmagic probe diff --git a/applications/plugins/picopass/125_10px.png b/applications/plugins/picopass/125_10px.png new file mode 100644 index 0000000000000000000000000000000000000000..ce01284a2c1f3eb413f581b84f1fb3f3a2a7223b GIT binary patch literal 308 zcmeAS@N?(olHy`uVBq!ia0vp^AT}2xkYHHq`AGmsv7|ftIx;Y9?C1WI$O_~uBzpw; zGB8xBF)%c=FfjZA3N^f7U???UV0e|lz+g3lfkC`r&aOZkpafHrx4R1i<>&pI=m5)bWZjP>yH&963)5S4_<9hOs!iI #include -#include -#include #include #include diff --git a/applications/plugins/picopass/rfal_picopass.c b/applications/plugins/picopass/rfal_picopass.c index 50cd4e95d..ac66cb92d 100644 --- a/applications/plugins/picopass/rfal_picopass.c +++ b/applications/plugins/picopass/rfal_picopass.c @@ -1,5 +1,4 @@ #include "rfal_picopass.h" -#include "utils.h" #define RFAL_PICOPASS_TXRX_FLAGS \ (FURI_HAL_NFC_LL_TXRX_FLAGS_CRC_TX_MANUAL | FURI_HAL_NFC_LL_TXRX_FLAGS_AGC_ON | \ @@ -97,7 +96,7 @@ FuriHalNfcReturn rfalPicoPassPollerSelect(uint8_t* csn, rfalPicoPassSelectRes* s rfalPicoPassSelectReq selReq; selReq.CMD = RFAL_PICOPASS_CMD_SELECT; - ST_MEMCPY(selReq.CSN, csn, RFAL_PICOPASS_UID_LEN); + memcpy(selReq.CSN, csn, RFAL_PICOPASS_UID_LEN); uint16_t recvLen = 0; uint32_t flags = RFAL_PICOPASS_TXRX_FLAGS; uint32_t fwt = furi_hal_nfc_ll_ms2fc(20); @@ -146,8 +145,8 @@ FuriHalNfcReturn rfalPicoPassPollerCheck(uint8_t* mac, rfalPicoPassCheckRes* chk FuriHalNfcReturn ret; rfalPicoPassCheckReq chkReq; chkReq.CMD = RFAL_PICOPASS_CMD_CHECK; - ST_MEMCPY(chkReq.mac, mac, 4); - ST_MEMSET(chkReq.null, 0, 4); + memcpy(chkReq.mac, mac, 4); + memset(chkReq.null, 0, 4); uint16_t recvLen = 0; uint32_t flags = RFAL_PICOPASS_TXRX_FLAGS; uint32_t fwt = furi_hal_nfc_ll_ms2fc(20); diff --git a/applications/services/gui/application.fam b/applications/services/gui/application.fam index 7fad7b4ea..869d964dd 100644 --- a/applications/services/gui/application.fam +++ b/applications/services/gui/application.fam @@ -12,6 +12,7 @@ App( order=70, sdk_headers=[ "gui.h", + "icon_i.h", "elements.h", "view_dispatcher.h", "view_stack.h", diff --git a/firmware.scons b/firmware.scons index 63a1aa3f7..a0c1ab339 100644 --- a/firmware.scons +++ b/firmware.scons @@ -1,6 +1,7 @@ Import("ENV", "fw_build_meta") from SCons.Errors import UserError +from SCons.Node import FS import itertools from fbt_extra.util import ( @@ -14,7 +15,6 @@ env = ENV.Clone( ("compilation_db", {"COMPILATIONDB_COMSTR": "\tCDB\t${TARGET}"}), "fwbin", "fbt_apps", - "fbt_sdk", ], COMPILATIONDB_USE_ABSPATH=False, BUILD_DIR=fw_build_meta["build_dir"], @@ -112,7 +112,9 @@ lib_targets = env.BuildModules( # Now, env is fully set up with everything to build apps -fwenv = env.Clone() +fwenv = env.Clone(FW_ARTIFACTS=[]) + +fw_artifacts = fwenv["FW_ARTIFACTS"] # Set up additional app-specific build flags SConscript("site_scons/firmwareopts.scons", exports={"ENV": fwenv}) @@ -130,7 +132,14 @@ if extra_int_apps := GetOption("extra_int_apps"): if fwenv["FAP_EXAMPLES"]: fwenv.Append(APPDIRS=[("applications/examples", False)]) -fwenv.LoadApplicationManifests() +for app_dir, _ in env["APPDIRS"]: + app_dir_node = env.Dir("#").Dir(app_dir) + + for entry in app_dir_node.glob("*"): + if isinstance(entry, FS.Dir) and not str(entry).startswith("."): + fwenv.LoadAppManifest(entry) + + fwenv.PrepareApplicationsBuild() # Build external apps @@ -138,6 +147,7 @@ if env["IS_BASE_FIRMWARE"]: extapps = fwenv["FW_EXTAPPS"] = SConscript( "site_scons/extapps.scons", exports={"ENV": fwenv} ) + fw_artifacts.append(extapps["sdk_tree"]) # Add preprocessor definitions for current set of apps @@ -220,7 +230,10 @@ Depends(fwelf, lib_targets) AddPostAction(fwelf, fwenv["APPBUILD_DUMP"]) AddPostAction( fwelf, - Action('${PYTHON3} "${ROOT_DIR}/scripts/fwsize.py" elf ${TARGET}', "Firmware size"), + Action( + '${PYTHON3} "${BIN_SIZE_SCRIPT}" elf ${TARGET}', + "Firmware size", + ), ) # Produce extra firmware files @@ -228,7 +241,7 @@ fwhex = fwenv["FW_HEX"] = fwenv.HEXBuilder("${FIRMWARE_BUILD_CFG}") fwbin = fwenv["FW_BIN"] = fwenv.BINBuilder("${FIRMWARE_BUILD_CFG}") AddPostAction( fwbin, - Action('@${PYTHON3} "${ROOT_DIR}/scripts/fwsize.py" bin ${TARGET}'), + Action('@${PYTHON3} "${BIN_SIZE_SCRIPT}" bin ${TARGET}'), ) fwdfu = fwenv["FW_DFU"] = fwenv.DFUBuilder("${FIRMWARE_BUILD_CFG}") @@ -238,12 +251,14 @@ fwdump = fwenv.ObjDump("${FIRMWARE_BUILD_CFG}") Alias(fwenv["FIRMWARE_BUILD_CFG"] + "_list", fwdump) -fw_artifacts = fwenv["FW_ARTIFACTS"] = [ - fwhex, - fwbin, - fwdfu, - fwenv["FW_VERSION_JSON"], -] +fw_artifacts.extend( + [ + fwhex, + fwbin, + fwdfu, + fwenv["FW_VERSION_JSON"], + ] +) fwcdb = fwenv.CompilationDatabase() @@ -272,34 +287,5 @@ if should_gen_cdb_and_link_dir(fwenv, BUILD_TARGETS): Alias(fwenv["FIRMWARE_BUILD_CFG"] + "_all", fw_artifacts) -if fwenv["IS_BASE_FIRMWARE"]: - sdk_source = fwenv.SDKPrebuilder( - "sdk_origin", - # Deps on root SDK headers and generated files - (fwenv["SDK_HEADERS"], fwenv["FW_ASSETS_HEADERS"]), - ) - # Extra deps on headers included in deeper levels - Depends(sdk_source, fwenv.ProcessSdkDepends("sdk_origin.d")) - - fwenv["SDK_DIR"] = fwenv.Dir("sdk") - sdk_tree = fwenv.SDKTree(fwenv["SDK_DIR"], "sdk_origin") - fw_artifacts.append(sdk_tree) - # AlwaysBuild(sdk_tree) - Alias("sdk_tree", sdk_tree) - - sdk_apicheck = fwenv.SDKSymUpdater(fwenv["SDK_DEFINITION"], "sdk_origin") - Precious(sdk_apicheck) - NoClean(sdk_apicheck) - AlwaysBuild(sdk_apicheck) - Alias("sdk_check", sdk_apicheck) - - sdk_apisyms = fwenv.SDKSymGenerator( - "assets/compiled/symbols.h", fwenv["SDK_DEFINITION"] - ) - Alias("api_syms", sdk_apisyms) - - if fwenv["FORCE"]: - fwenv.AlwaysBuild(sdk_source, sdk_tree, sdk_apicheck, sdk_apisyms) - Return("fwenv") diff --git a/firmware/SConscript b/firmware/SConscript index 2285a6f2c..19dde2e4c 100644 --- a/firmware/SConscript +++ b/firmware/SConscript @@ -2,11 +2,10 @@ Import("env") env.Append( LINT_SOURCES=["firmware"], - # SDK_HEADERS=[env.File("#/firmware/targets/furi_hal_include/furi_hal.h")], SDK_HEADERS=[ - *env.GlobRecursive("*.h", "#/firmware/targets/furi_hal_include", "*_i.h"), - *env.GlobRecursive("*.h", "#/firmware/targets/f${TARGET_HW}/furi_hal", "*_i.h"), - File("#/firmware/targets/f7/platform_specific/intrinsic_export.h"), + *env.GlobRecursive("*.h", "targets/furi_hal_include", "*_i.h"), + *env.GlobRecursive("*.h", "targets/f${TARGET_HW}/furi_hal", "*_i.h"), + File("targets/f7/platform_specific/intrinsic_export.h"), ], ) diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index 00ba30c24..0d9a5b561 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,7.0,, +Version,+,7.2,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -7,6 +7,7 @@ Header,+,applications/services/dialogs/dialogs.h,, Header,+,applications/services/dolphin/dolphin.h,, Header,+,applications/services/gui/elements.h,, Header,+,applications/services/gui/gui.h,, +Header,+,applications/services/gui/icon_i.h,, Header,+,applications/services/gui/modules/button_menu.h,, Header,+,applications/services/gui/modules/button_panel.h,, Header,+,applications/services/gui/modules/byte_input.h,, @@ -110,12 +111,42 @@ Header,+,lib/STM32CubeWB/Drivers/STM32WBxx_HAL_Driver/Inc/stm32wbxx_ll_wwdg.h,, Header,+,lib/flipper_application/flipper_application.h,, Header,+,lib/flipper_format/flipper_format.h,, Header,+,lib/flipper_format/flipper_format_i.h,, +Header,+,lib/infrared/encoder_decoder/infrared.h,, +Header,+,lib/infrared/worker/infrared_transmit.h,, +Header,+,lib/infrared/worker/infrared_worker.h,, Header,+,lib/lfrfid/lfrfid_dict_file.h,, Header,+,lib/lfrfid/lfrfid_raw_file.h,, Header,+,lib/lfrfid/lfrfid_raw_worker.h,, Header,+,lib/lfrfid/lfrfid_worker.h,, Header,+,lib/lfrfid/protocols/lfrfid_protocols.h,, Header,+,lib/lfrfid/tools/bit_lib.h,, +Header,+,lib/libusb_stm32/inc/hid_usage_button.h,, +Header,+,lib/libusb_stm32/inc/hid_usage_consumer.h,, +Header,+,lib/libusb_stm32/inc/hid_usage_desktop.h,, +Header,+,lib/libusb_stm32/inc/hid_usage_device.h,, +Header,+,lib/libusb_stm32/inc/hid_usage_game.h,, +Header,+,lib/libusb_stm32/inc/hid_usage_keyboard.h,, +Header,+,lib/libusb_stm32/inc/hid_usage_led.h,, +Header,+,lib/libusb_stm32/inc/hid_usage_ordinal.h,, +Header,+,lib/libusb_stm32/inc/hid_usage_power.h,, +Header,+,lib/libusb_stm32/inc/hid_usage_simulation.h,, +Header,+,lib/libusb_stm32/inc/hid_usage_sport.h,, +Header,+,lib/libusb_stm32/inc/hid_usage_telephony.h,, +Header,+,lib/libusb_stm32/inc/hid_usage_vr.h,, +Header,+,lib/libusb_stm32/inc/usb.h,, +Header,+,lib/libusb_stm32/inc/usb_cdc.h,, +Header,+,lib/libusb_stm32/inc/usb_cdca.h,, +Header,+,lib/libusb_stm32/inc/usb_cdce.h,, +Header,+,lib/libusb_stm32/inc/usb_cdci.h,, +Header,+,lib/libusb_stm32/inc/usb_cdcp.h,, +Header,+,lib/libusb_stm32/inc/usb_cdcw.h,, +Header,+,lib/libusb_stm32/inc/usb_dfu.h,, +Header,+,lib/libusb_stm32/inc/usb_hid.h,, +Header,+,lib/libusb_stm32/inc/usb_std.h,, +Header,+,lib/libusb_stm32/inc/usb_tmc.h,, +Header,+,lib/libusb_stm32/inc/usbd_core.h,, +Header,+,lib/mbedtls/include/mbedtls/des.h,, +Header,+,lib/mbedtls/include/mbedtls/sha1.h,, Header,+,lib/micro-ecc/uECC.h,, Header,+,lib/one_wire/ibutton/ibutton_worker.h,, Header,+,lib/one_wire/maxim_crc.h,, @@ -132,6 +163,7 @@ Header,+,lib/subghz/blocks/math.h,, Header,+,lib/subghz/environment.h,, Header,+,lib/subghz/protocols/raw.h,, Header,+,lib/subghz/receiver.h,, +Header,+,lib/subghz/registry.h,, Header,+,lib/subghz/subghz_setting.h,, Header,+,lib/subghz/subghz_tx_rx_worker.h,, Header,+,lib/subghz/subghz_worker.h,, @@ -407,6 +439,7 @@ Function,-,_system_r,int,"_reent*, const char*" Function,-,_tempnam_r,char*,"_reent*, const char*, const char*" Function,-,_tmpfile_r,FILE*,_reent* Function,-,_tmpnam_r,char*,"_reent*, char*" +Function,-,_tzset_r,void,_reent* Function,-,_ungetc_r,int,"_reent*, int, FILE*" Function,-,_unsetenv_r,int,"_reent*, const char*" Function,-,_vasiprintf_r,int,"_reent*, char**, const char*, __gnuc_va_list" @@ -454,6 +487,8 @@ Function,+,args_read_hex_bytes,_Bool,"FuriString*, uint8_t*, size_t" Function,+,args_read_int_and_trim,_Bool,"FuriString*, int*" Function,+,args_read_probably_quoted_string_and_trim,_Bool,"FuriString*, FuriString*" Function,+,args_read_string_and_trim,_Bool,"FuriString*, FuriString*" +Function,-,asctime,char*,const tm* +Function,-,asctime_r,char*,"const tm*, char*" Function,-,asin,double,double Function,-,asinf,float,float Function,-,asinh,double,double @@ -621,6 +656,7 @@ Function,+,cli_read_timeout,size_t,"Cli*, uint8_t*, size_t, uint32_t" Function,+,cli_session_close,void,Cli* Function,+,cli_session_open,void,"Cli*, void*" Function,+,cli_write,void,"Cli*, const uint8_t*, size_t" +Function,-,clock,clock_t, Function,-,copysign,double,"double, double" Function,-,copysignf,float,"float, float" Function,-,copysignl,long double,"long double, long double" @@ -633,6 +669,8 @@ Function,-,cosl,long double,long double Function,+,crc32_calc_buffer,uint32_t,"uint32_t, const void*, size_t" Function,+,crc32_calc_file,uint32_t,"File*, const FileCrcProgressCb, void*" Function,-,ctermid,char*,char* +Function,-,ctime,char*,const time_t* +Function,-,ctime_r,char*,"const time_t*, char*" Function,-,cuserid,char*,char* Function,+,delete_mutex,_Bool,ValueMutex* Function,+,dialog_ex_alloc,DialogEx*, @@ -659,6 +697,7 @@ Function,+,dialog_message_set_icon,void,"DialogMessage*, const Icon*, uint8_t, u Function,+,dialog_message_set_text,void,"DialogMessage*, const char*, uint8_t, uint8_t, Align, Align" Function,+,dialog_message_show,DialogMessageButton,"DialogsApp*, const DialogMessage*" Function,+,dialog_message_show_storage_error,void,"DialogsApp*, const char*" +Function,-,difftime,double,"time_t, time_t" Function,-,digital_signal_alloc,DigitalSignal*,uint32_t Function,-,digital_signal_append,_Bool,"DigitalSignal*, DigitalSignal*" Function,-,digital_signal_free,void,DigitalSignal* @@ -1475,6 +1514,8 @@ Function,-,getenv,char*,const char* Function,-,gets,char*,char* Function,-,getsubopt,int,"char**, char**, char**" Function,-,getw,int,FILE* +Function,-,gmtime,tm*,const time_t* +Function,-,gmtime_r,tm*,"const time_t*, tm*" Function,+,gui_add_framebuffer_callback,void,"Gui*, GuiCanvasCommitCallback, void*" Function,+,gui_add_view_port,void,"Gui*, ViewPort*, GuiLayer" Function,+,gui_get_framebuffer_size,size_t,Gui* @@ -1535,6 +1576,42 @@ Function,-,ilogbl,int,long double Function,-,index,char*,"const char*, int" Function,-,infinity,double, Function,-,infinityf,float, +Function,+,infrared_alloc_decoder,InfraredDecoderHandler*, +Function,+,infrared_alloc_encoder,InfraredEncoderHandler*, +Function,+,infrared_check_decoder_ready,const InfraredMessage*,InfraredDecoderHandler* +Function,+,infrared_decode,const InfraredMessage*,"InfraredDecoderHandler*, _Bool, uint32_t" +Function,+,infrared_encode,InfraredStatus,"InfraredEncoderHandler*, uint32_t*, _Bool*" +Function,+,infrared_free_decoder,void,InfraredDecoderHandler* +Function,+,infrared_free_encoder,void,InfraredEncoderHandler* +Function,+,infrared_get_protocol_address_length,uint8_t,InfraredProtocol +Function,+,infrared_get_protocol_by_name,InfraredProtocol,const char* +Function,+,infrared_get_protocol_command_length,uint8_t,InfraredProtocol +Function,+,infrared_get_protocol_duty_cycle,float,InfraredProtocol +Function,+,infrared_get_protocol_frequency,uint32_t,InfraredProtocol +Function,+,infrared_get_protocol_name,const char*,InfraredProtocol +Function,+,infrared_is_protocol_valid,_Bool,InfraredProtocol +Function,+,infrared_reset_decoder,void,InfraredDecoderHandler* +Function,+,infrared_reset_encoder,void,"InfraredEncoderHandler*, const InfraredMessage*" +Function,+,infrared_send,void,"const InfraredMessage*, int" +Function,+,infrared_send_raw,void,"const uint32_t[], uint32_t, _Bool" +Function,+,infrared_send_raw_ext,void,"const uint32_t[], uint32_t, _Bool, uint32_t, float" +Function,+,infrared_worker_alloc,InfraredWorker*, +Function,+,infrared_worker_free,void,InfraredWorker* +Function,+,infrared_worker_get_decoded_signal,const InfraredMessage*,const InfraredWorkerSignal* +Function,+,infrared_worker_get_raw_signal,void,"const InfraredWorkerSignal*, const uint32_t**, size_t*" +Function,+,infrared_worker_rx_enable_blink_on_receiving,void,"InfraredWorker*, _Bool" +Function,+,infrared_worker_rx_enable_signal_decoding,void,"InfraredWorker*, _Bool" +Function,+,infrared_worker_rx_set_received_signal_callback,void,"InfraredWorker*, InfraredWorkerReceivedSignalCallback, void*" +Function,+,infrared_worker_rx_start,void,InfraredWorker* +Function,+,infrared_worker_rx_stop,void,InfraredWorker* +Function,+,infrared_worker_set_decoded_signal,void,"InfraredWorker*, const InfraredMessage*" +Function,+,infrared_worker_set_raw_signal,void,"InfraredWorker*, const uint32_t*, size_t" +Function,+,infrared_worker_signal_is_decoded,_Bool,const InfraredWorkerSignal* +Function,+,infrared_worker_tx_get_signal_steady_callback,InfraredWorkerGetSignalResponse,"void*, InfraredWorker*" +Function,+,infrared_worker_tx_set_get_signal_callback,void,"InfraredWorker*, InfraredWorkerGetSignalCallback, void*" +Function,+,infrared_worker_tx_set_signal_sent_callback,void,"InfraredWorker*, InfraredWorkerMessageSentCallback, void*" +Function,+,infrared_worker_tx_start,void,InfraredWorker* +Function,+,infrared_worker_tx_stop,void,InfraredWorker* Function,+,init_mutex,_Bool,"ValueMutex*, void*, size_t" Function,-,initstate,char*,"unsigned, char*, size_t" Function,+,input_get_key_name,const char*,InputKey @@ -1634,6 +1711,8 @@ Function,+,loader_update_menu,void, Function,+,loading_alloc,Loading*, Function,+,loading_free,void,Loading* Function,+,loading_get_view,View*,Loading* +Function,-,localtime,tm*,const time_t* +Function,-,localtime_r,tm*,"const time_t*, tm*" Function,-,log,double,double Function,-,log10,double,double Function,-,log10f,float,float @@ -1662,6 +1741,36 @@ Function,+,manchester_encoder_advance,_Bool,"ManchesterEncoderState*, const _Boo Function,+,manchester_encoder_finish,ManchesterEncoderResult,ManchesterEncoderState* Function,+,manchester_encoder_reset,void,ManchesterEncoderState* Function,+,maxim_crc8,uint8_t,"const uint8_t*, const uint8_t, const uint8_t" +Function,-,mbedtls_des3_crypt_cbc,int,"mbedtls_des3_context*, int, size_t, unsigned char[8], const unsigned char*, unsigned char*" +Function,-,mbedtls_des3_crypt_ecb,int,"mbedtls_des3_context*, const unsigned char[8], unsigned char[8]" +Function,-,mbedtls_des3_free,void,mbedtls_des3_context* +Function,-,mbedtls_des3_init,void,mbedtls_des3_context* +Function,-,mbedtls_des3_set2key_dec,int,"mbedtls_des3_context*, const unsigned char[8 * 2]" +Function,-,mbedtls_des3_set2key_enc,int,"mbedtls_des3_context*, const unsigned char[8 * 2]" +Function,-,mbedtls_des3_set3key_dec,int,"mbedtls_des3_context*, const unsigned char[8 * 3]" +Function,-,mbedtls_des3_set3key_enc,int,"mbedtls_des3_context*, const unsigned char[8 * 3]" +Function,-,mbedtls_des_crypt_cbc,int,"mbedtls_des_context*, int, size_t, unsigned char[8], const unsigned char*, unsigned char*" +Function,-,mbedtls_des_crypt_ecb,int,"mbedtls_des_context*, const unsigned char[8], unsigned char[8]" +Function,-,mbedtls_des_free,void,mbedtls_des_context* +Function,-,mbedtls_des_init,void,mbedtls_des_context* +Function,-,mbedtls_des_key_check_key_parity,int,const unsigned char[8] +Function,-,mbedtls_des_key_check_weak,int,const unsigned char[8] +Function,-,mbedtls_des_key_set_parity,void,unsigned char[8] +Function,-,mbedtls_des_self_test,int,int +Function,-,mbedtls_des_setkey,void,"uint32_t[32], const unsigned char[8]" +Function,-,mbedtls_des_setkey_dec,int,"mbedtls_des_context*, const unsigned char[8]" +Function,-,mbedtls_des_setkey_enc,int,"mbedtls_des_context*, const unsigned char[8]" +Function,-,mbedtls_internal_sha1_process,int,"mbedtls_sha1_context*, const unsigned char[64]" +Function,-,mbedtls_platform_gmtime_r,tm*,"const mbedtls_time_t*, tm*" +Function,-,mbedtls_platform_zeroize,void,"void*, size_t" +Function,-,mbedtls_sha1,int,"const unsigned char*, size_t, unsigned char[20]" +Function,-,mbedtls_sha1_clone,void,"mbedtls_sha1_context*, const mbedtls_sha1_context*" +Function,-,mbedtls_sha1_finish,int,"mbedtls_sha1_context*, unsigned char[20]" +Function,-,mbedtls_sha1_free,void,mbedtls_sha1_context* +Function,-,mbedtls_sha1_init,void,mbedtls_sha1_context* +Function,-,mbedtls_sha1_self_test,int,int +Function,-,mbedtls_sha1_starts,int,mbedtls_sha1_context* +Function,-,mbedtls_sha1_update,int,"mbedtls_sha1_context*, const unsigned char*, size_t" Function,-,mblen,int,"const char*, size_t" Function,-,mbstowcs,size_t,"wchar_t*, const char*, size_t" Function,-,mbtowc,int,"wchar_t*, const char*, size_t" @@ -1702,6 +1811,7 @@ Function,-,mkostemps,int,"char*, int, int" Function,-,mkstemp,int,char* Function,-,mkstemps,int,"char*, int" Function,-,mktemp,char*,char* +Function,-,mktime,time_t,tm* Function,-,modf,double,"double, double*" Function,-,modff,float,"float, float*" Function,-,modfl,long double,"long double, long double*" @@ -2210,6 +2320,8 @@ Function,+,stream_write_vaformat,size_t,"Stream*, const char*, va_list" Function,-,strerror,char*,int Function,-,strerror_l,char*,"int, locale_t" Function,-,strerror_r,char*,"int, char*, size_t" +Function,-,strftime,size_t,"char*, size_t, const char*, const tm*" +Function,-,strftime_l,size_t,"char*, size_t, const char*, const tm*, locale_t" Function,+,string_stream_alloc,Stream*, Function,-,strlcat,size_t,"char*, const char*, size_t" Function,+,strlcpy,size_t,"char*, const char*, size_t" @@ -2224,6 +2336,8 @@ Function,-,strndup,char*,"const char*, size_t" Function,-,strnlen,size_t,"const char*, size_t" Function,-,strnstr,char*,"const char*, const char*, size_t" Function,-,strpbrk,char*,"const char*, const char*" +Function,-,strptime,char*,"const char*, const char*, tm*" +Function,-,strptime_l,char*,"const char*, const char*, tm*, locale_t" Function,+,strrchr,char*,"const char*, int" Function,-,strsep,char*,"char**, const char*" Function,-,strsignal,char*,int @@ -2313,6 +2427,9 @@ Function,+,subghz_protocol_raw_get_sample_write,size_t,SubGhzProtocolDecoderRAW* Function,+,subghz_protocol_raw_save_to_file_init,_Bool,"SubGhzProtocolDecoderRAW*, const char*, SubGhzRadioPreset*" Function,+,subghz_protocol_raw_save_to_file_pause,void,"SubGhzProtocolDecoderRAW*, _Bool" Function,+,subghz_protocol_raw_save_to_file_stop,void,SubGhzProtocolDecoderRAW* +Function,+,subghz_protocol_registry_count,size_t,const SubGhzProtocolRegistry* +Function,+,subghz_protocol_registry_get_by_index,const SubGhzProtocol*,"const SubGhzProtocolRegistry*, size_t" +Function,+,subghz_protocol_registry_get_by_name,const SubGhzProtocol*,"const SubGhzProtocolRegistry*, const char*" Function,+,subghz_receiver_alloc_init,SubGhzReceiver*,SubGhzEnvironment* Function,+,subghz_receiver_decode,void,"SubGhzReceiver*, _Bool, uint32_t" Function,+,subghz_receiver_free,void,SubGhzReceiver* @@ -2411,6 +2528,7 @@ Function,+,text_input_set_validator,void,"TextInput*, TextInputValidatorCallback Function,-,tgamma,double,double Function,-,tgammaf,float,float Function,-,tgammal,long double,long double +Function,-,time,time_t,time_t* Function,+,timerCalculateTimer,uint32_t,uint16_t Function,-,timerDelay,void,uint16_t Function,+,timerIsExpired,_Bool,uint32_t @@ -2429,6 +2547,7 @@ Function,-,toupper_l,int,"int, locale_t" Function,-,trunc,double,double Function,-,truncf,float,float Function,-,truncl,long double,long double +Function,-,tzset,void, Function,-,uECC_compress,void,"const uint8_t*, uint8_t*, uECC_Curve" Function,+,uECC_compute_public_key,int,"const uint8_t*, uint8_t*, uECC_Curve" Function,-,uECC_curve_private_key_size,int,uECC_Curve @@ -2666,10 +2785,13 @@ Variable,-,MSIRangeTable,const uint32_t[16], Variable,-,SmpsPrescalerTable,const uint32_t[4][6], Variable,+,SystemCoreClock,uint32_t, Variable,+,_ctype_,const char[], +Variable,-,_daylight,int, Variable,+,_global_impure_ptr,_reent*, Variable,+,_impure_ptr,_reent*, Variable,-,_sys_errlist,const char*[], Variable,-,_sys_nerr,int, +Variable,-,_timezone,long, +Variable,-,_tzname,char*[2], Variable,+,cli_vcp,CliSession, Variable,+,furi_hal_i2c_bus_external,FuriHalI2cBus, Variable,+,furi_hal_i2c_bus_power,FuriHalI2cBus, diff --git a/lib/SConscript b/lib/SConscript index 2822684bc..60ffabfa9 100644 --- a/lib/SConscript +++ b/lib/SConscript @@ -17,12 +17,12 @@ env.Append( "lib/print", ], SDK_HEADERS=[ - File("#/lib/one_wire/one_wire_host_timing.h"), - File("#/lib/one_wire/one_wire_host.h"), - File("#/lib/one_wire/one_wire_slave.h"), - File("#/lib/one_wire/one_wire_device.h"), - File("#/lib/one_wire/ibutton/ibutton_worker.h"), - File("#/lib/one_wire/maxim_crc.h"), + File("one_wire/one_wire_host_timing.h"), + File("one_wire/one_wire_host.h"), + File("one_wire/one_wire_slave.h"), + File("one_wire/one_wire_device.h"), + File("one_wire/ibutton/ibutton_worker.h"), + File("one_wire/maxim_crc.h"), ], ) diff --git a/lib/STM32CubeWB.scons b/lib/STM32CubeWB.scons index e8350ea99..b0e55f82e 100644 --- a/lib/STM32CubeWB.scons +++ b/lib/STM32CubeWB.scons @@ -15,7 +15,7 @@ env.Append( ], SDK_HEADERS=env.GlobRecursive( "*_ll_*.h", - "#/lib/STM32CubeWB/Drivers/STM32WBxx_HAL_Driver/Inc/", + Dir("STM32CubeWB/Drivers/STM32WBxx_HAL_Driver/Inc/"), exclude="*usb.h", ), ) diff --git a/lib/flipper_application/SConscript b/lib/flipper_application/SConscript index 3a5e7f4db..9fbbf95d1 100644 --- a/lib/flipper_application/SConscript +++ b/lib/flipper_application/SConscript @@ -5,7 +5,7 @@ env.Append( "#/lib/flipper_application", ], SDK_HEADERS=[ - File("#/lib/flipper_application/flipper_application.h"), + File("flipper_application.h"), ], ) diff --git a/lib/flipper_format/SConscript b/lib/flipper_format/SConscript index 5e185678b..353da8035 100644 --- a/lib/flipper_format/SConscript +++ b/lib/flipper_format/SConscript @@ -5,8 +5,8 @@ env.Append( "#/lib/flipper_format", ], SDK_HEADERS=[ - File("#/lib/flipper_format/flipper_format.h"), - File("#/lib/flipper_format/flipper_format_i.h"), + File("flipper_format.h"), + File("flipper_format_i.h"), ], ) diff --git a/lib/infrared/SConscript b/lib/infrared/SConscript index 35db75f87..9a1543f00 100644 --- a/lib/infrared/SConscript +++ b/lib/infrared/SConscript @@ -5,6 +5,11 @@ env.Append( "#/lib/infrared/encoder_decoder", "#/lib/infrared/worker", ], + SDK_HEADERS=[ + File("encoder_decoder/infrared.h"), + File("worker/infrared_worker.h"), + File("worker/infrared_transmit.h"), + ], ) diff --git a/lib/lfrfid/SConscript b/lib/lfrfid/SConscript index 6177a9a50..69ea9d3c1 100644 --- a/lib/lfrfid/SConscript +++ b/lib/lfrfid/SConscript @@ -8,12 +8,12 @@ env.Append( "#/lib/lfrfid", ], SDK_HEADERS=[ - File("#/lib/lfrfid/lfrfid_worker.h"), - File("#/lib/lfrfid/lfrfid_raw_worker.h"), - File("#/lib/lfrfid/lfrfid_raw_file.h"), - File("#/lib/lfrfid/lfrfid_dict_file.h"), - File("#/lib/lfrfid/tools/bit_lib.h"), - File("#/lib/lfrfid/protocols/lfrfid_protocols.h"), + File("lfrfid_worker.h"), + File("lfrfid_raw_worker.h"), + File("lfrfid_raw_file.h"), + File("lfrfid_dict_file.h"), + File("tools/bit_lib.h"), + File("protocols/lfrfid_protocols.h"), ], ) diff --git a/lib/libusb_stm32.scons b/lib/libusb_stm32.scons index cb867fdbc..4838b7c50 100644 --- a/lib/libusb_stm32.scons +++ b/lib/libusb_stm32.scons @@ -7,6 +7,10 @@ env.Append( CPPDEFINES=[ ("USB_PMASIZE", "0x400"), ], + SDK_HEADERS=env.GlobRecursive( + "*.h", + Dir("libusb_stm32/inc"), + ), ) diff --git a/lib/mbedtls.scons b/lib/mbedtls.scons index b57221a49..79a4a2520 100644 --- a/lib/mbedtls.scons +++ b/lib/mbedtls.scons @@ -5,6 +5,10 @@ env.Append( "#/lib/mbedtls", "#/lib/mbedtls/include", ], + SDK_HEADERS=[ + File("mbedtls/include/mbedtls/des.h"), + File("mbedtls/include/mbedtls/sha1.h"), + ], ) diff --git a/lib/misc.scons b/lib/misc.scons index b7d8554b5..91ad276a0 100644 --- a/lib/misc.scons +++ b/lib/misc.scons @@ -13,7 +13,7 @@ env.Append( "PB_ENABLE_MALLOC", ], SDK_HEADERS=[ - File("#/lib/micro-ecc/uECC.h"), + File("micro-ecc/uECC.h"), ], ) diff --git a/lib/print/SConscript b/lib/print/SConscript index d4a55ab84..f34c8152f 100644 --- a/lib/print/SConscript +++ b/lib/print/SConscript @@ -98,7 +98,7 @@ for wrapped_fn in wrapped_fn_list: env.Append( SDK_HEADERS=[ - File("#/lib/print/wrappers.h"), + File("wrappers.h"), ], ) diff --git a/lib/subghz/SConscript b/lib/subghz/SConscript index e25d122c8..6d9c0cd06 100644 --- a/lib/subghz/SConscript +++ b/lib/subghz/SConscript @@ -5,18 +5,19 @@ env.Append( "#/lib/subghz", ], SDK_HEADERS=[ - File("#/lib/subghz/environment.h"), - File("#/lib/subghz/receiver.h"), - File("#/lib/subghz/subghz_worker.h"), - File("#/lib/subghz/subghz_tx_rx_worker.h"), - File("#/lib/subghz/transmitter.h"), - File("#/lib/subghz/protocols/raw.h"), - File("#/lib/subghz/blocks/const.h"), - File("#/lib/subghz/blocks/decoder.h"), - File("#/lib/subghz/blocks/encoder.h"), - File("#/lib/subghz/blocks/generic.h"), - File("#/lib/subghz/blocks/math.h"), - File("#/lib/subghz/subghz_setting.h"), + File("environment.h"), + File("receiver.h"), + File("registry.h"), + File("subghz_worker.h"), + File("subghz_tx_rx_worker.h"), + File("transmitter.h"), + File("protocols/raw.h"), + File("blocks/const.h"), + File("blocks/decoder.h"), + File("blocks/encoder.h"), + File("blocks/generic.h"), + File("blocks/math.h"), + File("subghz_setting.h"), ], ) diff --git a/lib/subghz/registry.h b/lib/subghz/registry.h index 062cdf68f..91027807e 100644 --- a/lib/subghz/registry.h +++ b/lib/subghz/registry.h @@ -2,6 +2,10 @@ #include "types.h" +#ifdef __cplusplus +extern "C" { +#endif + typedef struct SubGhzEnvironment SubGhzEnvironment; typedef struct SubGhzProtocolRegistry SubGhzProtocolRegistry; @@ -37,3 +41,7 @@ const SubGhzProtocol* subghz_protocol_registry_get_by_index( * @return Number of protocols */ size_t subghz_protocol_registry_count(const SubGhzProtocolRegistry* protocol_registry); + +#ifdef __cplusplus +} +#endif diff --git a/lib/toolbox/SConscript b/lib/toolbox/SConscript index d631431ee..015a8ed18 100644 --- a/lib/toolbox/SConscript +++ b/lib/toolbox/SConscript @@ -8,23 +8,23 @@ env.Append( "#/lib/toolbox", ], SDK_HEADERS=[ - File("#/lib/toolbox/manchester_decoder.h"), - File("#/lib/toolbox/manchester_encoder.h"), - File("#/lib/toolbox/path.h"), - File("#/lib/toolbox/random_name.h"), - File("#/lib/toolbox/hmac_sha256.h"), - File("#/lib/toolbox/crc32_calc.h"), - File("#/lib/toolbox/dir_walk.h"), - File("#/lib/toolbox/md5.h"), - File("#/lib/toolbox/args.h"), - File("#/lib/toolbox/saved_struct.h"), - File("#/lib/toolbox/version.h"), - File("#/lib/toolbox/tar/tar_archive.h"), - File("#/lib/toolbox/stream/stream.h"), - File("#/lib/toolbox/stream/file_stream.h"), - File("#/lib/toolbox/stream/string_stream.h"), - File("#/lib/toolbox/stream/buffered_file_stream.h"), - File("#/lib/toolbox/protocols/protocol_dict.h"), + File("manchester_decoder.h"), + File("manchester_encoder.h"), + File("path.h"), + File("random_name.h"), + File("hmac_sha256.h"), + File("crc32_calc.h"), + File("dir_walk.h"), + File("md5.h"), + File("args.h"), + File("saved_struct.h"), + File("version.h"), + File("tar/tar_archive.h"), + File("stream/stream.h"), + File("stream/file_stream.h"), + File("stream/string_stream.h"), + File("stream/buffered_file_stream.h"), + File("protocols/protocol_dict.h"), ], ) diff --git a/scripts/fbt/util.py b/scripts/fbt/util.py index baa4ddfee..f5404458e 100644 --- a/scripts/fbt/util.py +++ b/scripts/fbt/util.py @@ -1,11 +1,11 @@ import SCons from SCons.Subst import quote_spaces from SCons.Errors import StopError +from SCons.Node.FS import _my_normcase import re import os -import random -import string + WINPATHSEP_RE = re.compile(r"\\([^\"'\\]|$)") @@ -41,3 +41,14 @@ def link_dir(target_path, source_path, is_windows): def single_quote(arg_list): return " ".join(f"'{arg}'" if " " in arg else str(arg) for arg in arg_list) + + +def extract_abs_dir_path(node): + if isinstance(node, SCons.Node.FS.EntryProxy): + node = node.get() + + for repo_dir in node.get_all_rdirs(): + if os.path.exists(repo_dir.abspath): + return repo_dir.abspath + + raise StopError(f"Can't find absolute path for {node.name}") diff --git a/scripts/fbt_tools/crosscc.py b/scripts/fbt_tools/crosscc.py index aacda58c6..dd5cd5319 100644 --- a/scripts/fbt_tools/crosscc.py +++ b/scripts/fbt_tools/crosscc.py @@ -37,6 +37,21 @@ def _get_tool_version(env, tool): def generate(env, **kw): + if not env.get("VERBOSE", False): + env.SetDefault( + CCCOMSTR="\tCC\t${SOURCE}", + CXXCOMSTR="\tCPP\t${SOURCE}", + ASCOMSTR="\tASM\t${SOURCE}", + ARCOMSTR="\tAR\t${TARGET}", + RANLIBCOMSTR="\tRANLIB\t${TARGET}", + LINKCOMSTR="\tLINK\t${TARGET}", + INSTALLSTR="\tINSTALL\t${TARGET}", + APPSCOMSTR="\tAPPS\t${TARGET}", + VERSIONCOMSTR="\tVERSION\t${TARGET}", + STRIPCOMSTR="\tSTRIP\t${TARGET}", + OBJDUMPCOMSTR="\tOBJDUMP\t${TARGET}", + ) + for orig_tool in (asm, gcc, gxx, ar, gnulink, strip, gdb, objdump): orig_tool.generate(env) env.SetDefault( diff --git a/scripts/fbt_tools/fbt_apps.py b/scripts/fbt_tools/fbt_apps.py index ef5e9b9d9..55e282017 100644 --- a/scripts/fbt_tools/fbt_apps.py +++ b/scripts/fbt_tools/fbt_apps.py @@ -2,7 +2,7 @@ from SCons.Builder import Builder from SCons.Action import Action from SCons.Warnings import warn, WarningOnByDefault import SCons -import os.path +from ansi.color import fg from fbt.appmanifest import ( FlipperAppType, @@ -16,21 +16,20 @@ from fbt.appmanifest import ( # AppBuildset env["APPBUILD"] - contains subset of apps, filtered for current config -def LoadApplicationManifests(env): - appmgr = env["APPMGR"] = AppManager() - for app_dir, _ in env["APPDIRS"]: - app_dir_node = env.Dir("#").Dir(app_dir) +def LoadAppManifest(env, entry): + try: + APP_MANIFEST_NAME = "application.fam" + manifest_glob = entry.glob(APP_MANIFEST_NAME) + if len(manifest_glob) == 0: + raise FlipperManifestException( + f"Folder {entry}: manifest {APP_MANIFEST_NAME} is missing" + ) - for entry in app_dir_node.glob("*", ondisk=True, source=True): - if isinstance(entry, SCons.Node.FS.Dir) and not str(entry).startswith("."): - try: - app_manifest_file_path = os.path.join( - entry.abspath, "application.fam" - ) - appmgr.load_manifest(app_manifest_file_path, entry) - env.Append(PY_LINT_SOURCES=[app_manifest_file_path]) - except FlipperManifestException as e: - warn(WarningOnByDefault, str(e)) + app_manifest_file_path = manifest_glob[0].rfile().abspath + env["APPMGR"].load_manifest(app_manifest_file_path, entry) + env.Append(PY_LINT_SOURCES=[app_manifest_file_path]) + except FlipperManifestException as e: + warn(WarningOnByDefault, str(e)) def PrepareApplicationsBuild(env): @@ -46,12 +45,12 @@ def PrepareApplicationsBuild(env): def DumpApplicationConfig(target, source, env): print(f"Loaded {len(env['APPMGR'].known_apps)} app definitions.") - print("Firmware modules configuration:") + print(fg.boldgreen("Firmware modules configuration:")) for apptype in FlipperAppType: app_sublist = env["APPBUILD"].get_apps_of_type(apptype) if app_sublist: print( - f"{apptype.value}:\n\t", + fg.green(f"{apptype.value}:\n\t"), ", ".join(app.appid for app in app_sublist), ) @@ -65,8 +64,11 @@ def build_apps_c(target, source, env): def generate(env): - env.AddMethod(LoadApplicationManifests) + env.AddMethod(LoadAppManifest) env.AddMethod(PrepareApplicationsBuild) + env.SetDefault( + APPMGR=AppManager(), + ) env.Append( BUILDERS={ diff --git a/scripts/fbt_tools/fbt_assets.py b/scripts/fbt_tools/fbt_assets.py index 4fa5353d0..521c37e90 100644 --- a/scripts/fbt_tools/fbt_assets.py +++ b/scripts/fbt_tools/fbt_assets.py @@ -1,11 +1,10 @@ -import SCons - from SCons.Builder import Builder from SCons.Action import Action -from SCons.Node.FS import File +from SCons.Errors import SConsEnvironmentError import os import subprocess +from ansi.color import fg def icons_emitter(target, source, env): @@ -13,7 +12,6 @@ def icons_emitter(target, source, env): target[0].File(env.subst("${ICON_FILE_NAME}.c")), target[0].File(env.subst("${ICON_FILE_NAME}.h")), ] - source = env.GlobRecursive("*.*", env["ICON_SRC_DIR"]) return target, source @@ -86,7 +84,7 @@ def proto_ver_generator(target, source, env): ) except (subprocess.CalledProcessError, EnvironmentError) as e: # Not great, not terrible - print("Git: fetch failed") + print(fg.boldred("Git: fetch failed")) try: git_describe = _invoke_git( @@ -94,10 +92,8 @@ def proto_ver_generator(target, source, env): source_dir=src_dir, ) except (subprocess.CalledProcessError, EnvironmentError) as e: - print("Git: describe failed") - Exit("git error") + raise SConsEnvironmentError("Git: describe failed") - # print("describe=", git_describe) git_major, git_minor = git_describe.split(".") version_file_data = ( "#pragma once", @@ -116,7 +112,7 @@ def CompileIcons(env, target_dir, source_dir, *, icon_bundle_name="assets_icons" icons = env.IconBuilder( target_dir, - ICON_SRC_DIR=source_dir, + source_dir, ICON_FILE_NAME=icon_bundle_name, ) env.Depends(icons, icons_src) @@ -125,8 +121,8 @@ def CompileIcons(env, target_dir, source_dir, *, icon_bundle_name="assets_icons" def generate(env): env.SetDefault( - ASSETS_COMPILER="${ROOT_DIR.abspath}/scripts/assets.py", - NANOPB_COMPILER="${ROOT_DIR.abspath}/lib/nanopb/generator/nanopb_generator.py", + ASSETS_COMPILER="${FBT_SCRIPT_DIR}/assets.py", + NANOPB_COMPILER="${ROOT_DIR}/lib/nanopb/generator/nanopb_generator.py", ) env.AddMethod(CompileIcons) @@ -143,7 +139,7 @@ def generate(env): BUILDERS={ "IconBuilder": Builder( action=Action( - '${PYTHON3} "${ASSETS_COMPILER}" icons ${ICON_SRC_DIR} ${TARGET.dir} --filename ${ICON_FILE_NAME}', + '${PYTHON3} "${ASSETS_COMPILER}" icons ${ABSPATHGETTERFUNC(SOURCE)} ${TARGET.dir} --filename ${ICON_FILE_NAME}', "${ICONSCOMSTR}", ), emitter=icons_emitter, diff --git a/scripts/fbt_tools/fbt_dist.py b/scripts/fbt_tools/fbt_dist.py index 853013e9f..fb59e5b95 100644 --- a/scripts/fbt_tools/fbt_dist.py +++ b/scripts/fbt_tools/fbt_dist.py @@ -103,7 +103,7 @@ def DistCommand(env, name, source, **kw): command = env.Command( target, source, - '@${PYTHON3} "${ROOT_DIR.abspath}/scripts/sconsdist.py" copy -p ${DIST_PROJECTS} -s "${DIST_SUFFIX}" ${DIST_EXTRA}', + '@${PYTHON3} "${DIST_SCRIPT}" copy -p ${DIST_PROJECTS} -s "${DIST_SUFFIX}" ${DIST_EXTRA}', **kw, ) env.Pseudo(target) @@ -121,6 +121,9 @@ def generate(env): env.SetDefault( COPRO_MCU_FAMILY="STM32WB5x", + SELFUPDATE_SCRIPT="${FBT_SCRIPT_DIR}/selfupdate.py", + DIST_SCRIPT="${FBT_SCRIPT_DIR}/sconsdist.py", + COPRO_ASSETS_SCRIPT="${FBT_SCRIPT_DIR}/assets.py", ) env.Append( @@ -128,7 +131,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} "${SELFUPDATE_SCRIPT}" dist/${DIST_DIR}/f${TARGET_HW}-update-${DIST_SUFFIX}/update.fuf' ), Touch("${TARGET}"), ] @@ -136,7 +139,7 @@ def generate(env): "CoproBuilder": Builder( action=Action( [ - '${PYTHON3} "${ROOT_DIR.abspath}/scripts/assets.py" ' + '${PYTHON3} "${COPRO_ASSETS_SCRIPT}" ' "copro ${COPRO_CUBE_DIR} " "${TARGET} ${COPRO_MCU_FAMILY} " "--cube_ver=${COPRO_CUBE_VERSION} " diff --git a/scripts/fbt_tools/fbt_extapps.py b/scripts/fbt_tools/fbt_extapps.py index 38c943cc5..fb4dc2f16 100644 --- a/scripts/fbt_tools/fbt_extapps.py +++ b/scripts/fbt_tools/fbt_extapps.py @@ -1,15 +1,18 @@ -import shutil from SCons.Builder import Builder from SCons.Action import Action from SCons.Errors import UserError import SCons.Warnings -import os -import pathlib from fbt.elfmanifest import assemble_manifest_data from fbt.appmanifest import FlipperApplication, FlipperManifestException from fbt.sdk.cache import SdkCache +from fbt.util import extract_abs_dir_path + +import os +import pathlib import itertools +import shutil + from ansi.color import fg @@ -62,7 +65,7 @@ def BuildAppElf(env, app): lib_src_root_path = os.path.join(app_work_dir, "lib", lib_def.name) app_env.AppendUnique( CPPPATH=list( - app_env.Dir(lib_src_root_path).Dir(incpath).srcnode() + app_env.Dir(lib_src_root_path).Dir(incpath).srcnode().rfile().abspath for incpath in lib_def.fap_include_paths ), ) @@ -82,7 +85,12 @@ def BuildAppElf(env, app): *lib_def.cflags, ], CPPDEFINES=lib_def.cdefines, - CPPPATH=list(map(app._appdir.Dir, lib_def.cincludes)), + CPPPATH=list( + map( + lambda cpath: extract_abs_dir_path(app._appdir.Dir(cpath)), + lib_def.cincludes, + ) + ), ) lib = private_lib_env.StaticLibrary( @@ -157,7 +165,6 @@ def prepare_app_metadata(target, source, env): app = env["APP"] meta_file_name = source[0].path + ".meta" with open(meta_file_name, "wb") as f: - # f.write(f"hello this is {app}") f.write( assemble_manifest_data( app_manifest=app, @@ -236,7 +243,10 @@ def fap_dist_action(target, source, env): def generate(env, **kw): - env.SetDefault(EXT_APPS_WORK_DIR=kw.get("EXT_APPS_WORK_DIR")) + env.SetDefault( + EXT_APPS_WORK_DIR=kw.get("EXT_APPS_WORK_DIR"), + APP_RUN_SCRIPT="${FBT_SCRIPT_DIR}/runfap.py", + ) if not env["VERBOSE"]: env.SetDefault( diff --git a/scripts/fbt_tools/fbt_sdk.py b/scripts/fbt_tools/fbt_sdk.py index f1f55bdb8..c46346b65 100644 --- a/scripts/fbt_tools/fbt_sdk.py +++ b/scripts/fbt_tools/fbt_sdk.py @@ -46,7 +46,9 @@ def prebuild_sdk_emitter(target, source, env): def prebuild_sdk_create_origin_file(target, source, env): mega_file = env.subst("${TARGET}.c", target=target[0]) with open(mega_file, "wt") as sdk_c: - sdk_c.write("\n".join(f"#include <{h.path}>" for h in env["SDK_HEADERS"])) + sdk_c.write( + "\n".join(f"#include <{h.srcnode().path}>" for h in env["SDK_HEADERS"]) + ) class SdkMeta: @@ -62,18 +64,25 @@ class SdkMeta: "cc_args": self._wrap_scons_vars("$CCFLAGS $_CCCOMCOM"), "cpp_args": self._wrap_scons_vars("$CXXFLAGS $CCFLAGS $_CCCOMCOM"), "linker_args": self._wrap_scons_vars("$LINKFLAGS"), - "linker_script": self.env.subst("${LINKER_SCRIPT_PATH}"), + "linker_libs": self.env.subst("${LIBS}"), + "app_ep_subst": self.env.subst("${APP_ENTRY}"), + "sdk_path_subst": self.env.subst("${SDK_DIR_SUBST}"), + "hardware": self.env.subst("${TARGET_HW}"), } with open(json_manifest_path, "wt") as f: json.dump(meta_contents, f, indent=4) def _wrap_scons_vars(self, vars: str): - expanded_vars = self.env.subst(vars, target=Entry("dummy")) + expanded_vars = self.env.subst( + vars, + target=Entry("dummy"), + ) return expanded_vars.replace("\\", "/") class SdkTreeBuilder: SDK_DIR_SUBST = "SDK_ROOT_DIR" + SDK_APP_EP_SUBST = "SDK_APP_EP_SUBST" def __init__(self, env, target, source) -> None: self.env = env @@ -87,6 +96,11 @@ class SdkTreeBuilder: self.sdk_root_dir = target[0].Dir(".") self.sdk_deploy_dir = self.sdk_root_dir.Dir(self.target_sdk_dir_name) + self.sdk_env = self.env.Clone( + APP_ENTRY=self.SDK_APP_EP_SUBST, + SDK_DIR_SUBST=self.SDK_DIR_SUBST, + ) + def _parse_sdk_depends(self): deps_file = self.source[0] with open(deps_file.path, "rt") as deps_f: @@ -95,38 +109,36 @@ class SdkTreeBuilder: self.header_depends = list( filter(lambda fname: fname.endswith(".h"), depends.split()), ) - self.header_depends.append(self.env.subst("${LINKER_SCRIPT_PATH}")) - self.header_depends.append(self.env.subst("${SDK_DEFINITION}")) + self.header_depends.append(self.sdk_env.subst("${LINKER_SCRIPT_PATH}")) + self.header_depends.append(self.sdk_env.subst("${SDK_DEFINITION}")) self.header_dirs = sorted( set(map(os.path.normpath, map(os.path.dirname, self.header_depends))) ) def _generate_sdk_meta(self): - filtered_paths = [self.target_sdk_dir_name] + filtered_paths = ["."] full_fw_paths = list( map( os.path.normpath, - (self.env.Dir(inc_dir).relpath for inc_dir in self.env["CPPPATH"]), + ( + self.sdk_env.Dir(inc_dir).relpath + for inc_dir in self.sdk_env["CPPPATH"] + ), ) ) sdk_dirs = ", ".join(f"'{dir}'" for dir in self.header_dirs) filtered_paths.extend( - map( - self.build_sdk_file_path, - filter(lambda path: path in sdk_dirs, full_fw_paths), - ) + filter(lambda path: path in sdk_dirs, full_fw_paths), ) + filtered_paths = list(map(self.build_sdk_file_path, filtered_paths)) - sdk_env = self.env.Clone() - sdk_env.Replace( + self.sdk_env.Replace( CPPPATH=filtered_paths, - LINKER_SCRIPT=self.env.subst("${APP_LINKER_SCRIPT}"), ORIG_LINKER_SCRIPT_PATH=self.env["LINKER_SCRIPT_PATH"], LINKER_SCRIPT_PATH=self.build_sdk_file_path("${ORIG_LINKER_SCRIPT_PATH}"), ) - - meta = SdkMeta(sdk_env, self) + meta = SdkMeta(self.sdk_env, self) meta.save_to(self.target[0].path) def build_sdk_file_path(self, orig_path: str) -> str: @@ -211,7 +223,7 @@ def validate_sdk_cache(source, target, env): current_sdk = SdkCollector() current_sdk.process_source_file_for_sdk(source[0].path) for h in env["SDK_HEADERS"]: - current_sdk.add_header_to_sdk(pathlib.Path(h.path).as_posix()) + current_sdk.add_header_to_sdk(pathlib.Path(h.srcnode().path).as_posix()) sdk_cache = SdkCache(target[0].path) sdk_cache.validate_api(current_sdk.get_api()) diff --git a/scripts/fbt_tools/fbt_version.py b/scripts/fbt_tools/fbt_version.py index 909eea4f3..87497ca5f 100644 --- a/scripts/fbt_tools/fbt_version.py +++ b/scripts/fbt_tools/fbt_version.py @@ -12,11 +12,14 @@ def version_emitter(target, source, env): def generate(env): + env.SetDefault( + VERSION_SCRIPT="${FBT_SCRIPT_DIR}/version.py", + ) env.Append( BUILDERS={ "VersionBuilder": Builder( action=Action( - '${PYTHON3} "${ROOT_DIR.abspath}/scripts/version.py" generate -t ${TARGET_HW} -o ${TARGET.dir.posix} --dir "${ROOT_DIR}"', + '${PYTHON3} "${VERSION_SCRIPT}" generate -t ${TARGET_HW} -o ${TARGET.dir.posix} --dir "${ROOT_DIR}"', "${VERSIONCOMSTR}", ), emitter=version_emitter, diff --git a/scripts/fbt_tools/fwbin.py b/scripts/fbt_tools/fwbin.py index 67e0d6450..f510c2a60 100644 --- a/scripts/fbt_tools/fwbin.py +++ b/scripts/fbt_tools/fwbin.py @@ -8,7 +8,8 @@ __NM_ARM_BIN = "arm-none-eabi-nm" def generate(env): env.SetDefault( - BIN2DFU="${ROOT_DIR.abspath}/scripts/bin2dfu.py", + BIN2DFU="${FBT_SCRIPT_DIR}/bin2dfu.py", + BIN_SIZE_SCRIPT="${FBT_SCRIPT_DIR}/fwsize.py", OBJCOPY=__OBJCOPY_ARM_BIN, # FIXME NM=__NM_ARM_BIN, # FIXME ) diff --git a/scripts/flipper/app.py b/scripts/flipper/app.py index 958356021..30630a5f9 100644 --- a/scripts/flipper/app.py +++ b/scripts/flipper/app.py @@ -1,6 +1,7 @@ import logging import argparse import sys +import colorlog class App: @@ -10,7 +11,7 @@ class App: self.parser = argparse.ArgumentParser() self.parser.add_argument("-d", "--debug", action="store_true", help="Debug") # Logging - self.logger = logging.getLogger() + self.logger = colorlog.getLogger() # Application specific initialization self.init() @@ -21,10 +22,17 @@ class App: self.log_level = logging.DEBUG if self.args.debug else logging.INFO self.logger.setLevel(self.log_level) if not self.logger.hasHandlers(): - self.handler = logging.StreamHandler(sys.stdout) + self.handler = colorlog.StreamHandler(sys.stdout) self.handler.setLevel(self.log_level) - self.formatter = logging.Formatter( - "%(asctime)s [%(levelname)s] %(message)s" + self.formatter = colorlog.ColoredFormatter( + "%(log_color)s%(asctime)s [%(levelname)s] %(message)s", + log_colors={ + "DEBUG": "cyan", + # "INFO": "white", + "WARNING": "yellow", + "ERROR": "red", + "CRITICAL": "red,bg_white", + }, ) self.handler.setFormatter(self.formatter) self.logger.addHandler(self.handler) diff --git a/scripts/sconsdist.py b/scripts/sconsdist.py index 7636c87bb..b8f1d72b2 100644 --- a/scripts/sconsdist.py +++ b/scripts/sconsdist.py @@ -131,7 +131,9 @@ class Main(App): self.copy_single_project(project) self.logger.info( - fg.green(f"Firmware binaries can be found at:\n\t{self.output_dir_path}") + fg.boldgreen( + f"Firmware binaries can be found at:\n\t{self.output_dir_path}" + ) ) if self.args.version: @@ -167,7 +169,7 @@ class Main(App): if (bundle_result := UpdateMain(no_exit=True)(bundle_args)) == 0: self.logger.info( - fg.green( + fg.boldgreen( f"Use this directory to self-update your Flipper:\n\t{bundle_dir}" ) ) diff --git a/scripts/testing/await_flipper.py b/scripts/testing/await_flipper.py old mode 100644 new mode 100755 diff --git a/scripts/testing/units.py b/scripts/testing/units.py old mode 100644 new mode 100755 diff --git a/scripts/toolchain/fbtenv.cmd b/scripts/toolchain/fbtenv.cmd index a11a3ccd5..6e87bf95a 100644 --- a/scripts/toolchain/fbtenv.cmd +++ b/scripts/toolchain/fbtenv.cmd @@ -13,19 +13,22 @@ if not [%FBT_NOENV%] == [] ( exit /b 0 ) -set "FLIPPER_TOOLCHAIN_VERSION=16" -set "FBT_TOOLCHAIN_ROOT=%FBT_ROOT%\toolchain\x86_64-windows" +set "FLIPPER_TOOLCHAIN_VERSION=17" +if [%FBT_TOOLCHAIN_ROOT%] == [] ( + set "FBT_TOOLCHAIN_ROOT=%FBT_ROOT%\toolchain\x86_64-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%" "%FBT_TOOLCHAIN_ROOT%" ) 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%" "%FBT_TOOLCHAIN_ROOT%" ) + 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%" "%FBT_TOOLCHAIN_ROOT%" ) diff --git a/scripts/toolchain/fbtenv.sh b/scripts/toolchain/fbtenv.sh index 15f29e4dc..d3fdb8cea 100755 --- a/scripts/toolchain/fbtenv.sh +++ b/scripts/toolchain/fbtenv.sh @@ -5,7 +5,7 @@ # public variables DEFAULT_SCRIPT_PATH="$(pwd -P)"; SCRIPT_PATH="${SCRIPT_PATH:-$DEFAULT_SCRIPT_PATH}"; -FBT_TOOLCHAIN_VERSION="${FBT_TOOLCHAIN_VERSION:-"16"}"; +FBT_TOOLCHAIN_VERSION="${FBT_TOOLCHAIN_VERSION:-"17"}"; FBT_TOOLCHAIN_PATH="${FBT_TOOLCHAIN_PATH:-$SCRIPT_PATH}"; fbtenv_show_usage() @@ -62,7 +62,7 @@ fbtenv_check_sourced() fbtenv_show_usage; return 1; fi - case ${0##*/} in dash|-dash|bash|-bash|ksh|-ksh|sh|-sh|*.sh|fbt) + case ${0##*/} in dash|-dash|bash|-bash|ksh|-ksh|sh|-sh|*.sh|fbt|ufbt) return 0;; esac fbtenv_show_usage; @@ -76,8 +76,8 @@ fbtenv_chck_many_source() return 0; fi fi - echo "Warning! FBT environment script sourced more than once!"; - echo "This may signal that you are making mistakes, please open a new shell!"; + echo "Warning! FBT environment script was sourced more than once!"; + echo "You might be doing things wrong, please open a new shell!"; return 1; } @@ -93,8 +93,8 @@ fbtenv_set_shell_prompt() 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"; + if [ ! -x "$SCRIPT_PATH/fbt" ] && [ ! -x "$SCRIPT_PATH/ufbt" ] ; then + echo "Please source this script from [u]fbt root directory, or specify 'SCRIPT_PATH' variable 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" @@ -108,7 +108,7 @@ 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.."; + echo "We only provide toolchain for x86_64 CPUs, sorry.."; return 1; fi if [ "$SYS_TYPE" = "Darwin" ]; then @@ -119,10 +119,10 @@ fbtenv_get_kernel_type() 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\""; + echo "In MinGW shell use \"[u]fbt.cmd\" instead of \"[u]fbt\""; return 1; else - echo "Your system is not recognized. Sorry.. Please report us your configuration."; + echo "Your system configuration is not supported. Sorry.. Please report us your configuration."; return 1; fi return 0; @@ -142,7 +142,7 @@ fbtenv_check_rosetta() fbtenv_check_tar() { - printf "Checking tar.."; + printf "Checking for tar.."; if ! tar --version > /dev/null 2>&1; then echo "no"; return 1; @@ -153,7 +153,7 @@ fbtenv_check_tar() fbtenv_check_downloaded_toolchain() { - printf "Checking downloaded toolchain tgz.."; + printf "Checking if downloaded toolchain tgz exists.."; if [ ! -f "$FBT_TOOLCHAIN_PATH/toolchain/$TOOLCHAIN_TAR" ]; then echo "no"; return 1; @@ -204,7 +204,7 @@ fbtenv_unpack_toolchain() fbtenv_clearing() { - printf "Clearing.."; + printf "Cleaning up.."; if [ -n "${FBT_TOOLCHAIN_PATH:-""}" ]; then rm -rf "${FBT_TOOLCHAIN_PATH:?}/toolchain/"*.tar.gz; rm -rf "${FBT_TOOLCHAIN_PATH:?}/toolchain/"*.part; diff --git a/scripts/toolchain/windows-toolchain-download.ps1 b/scripts/toolchain/windows-toolchain-download.ps1 index aaed89856..c96bb119c 100644 --- a/scripts/toolchain/windows-toolchain-download.ps1 +++ b/scripts/toolchain/windows-toolchain-download.ps1 @@ -1,34 +1,46 @@ Set-StrictMode -Version 2.0 $ErrorActionPreference = "Stop" [Net.ServicePointManager]::SecurityProtocol = "tls12, tls11, tls" -$repo_root = (Get-Item "$PSScriptRoot\..\..").FullName +# TODO: fix +$download_dir = (Get-Item "$PSScriptRoot\..\..").FullName $toolchain_version = $args[0] -$toolchain_url = "https://update.flipperzero.one/builds/toolchain/gcc-arm-none-eabi-10.3-x86_64-windows-flipper-$toolchain_version.zip" -$toolchain_zip = "gcc-arm-none-eabi-10.3-x86_64-windows-flipper-$toolchain_version.zip" -$toolchain_dir = "gcc-arm-none-eabi-10.3-x86_64-windows-flipper" +$toolchain_target_path = $args[1] -if (Test-Path -LiteralPath "$repo_root\toolchain\x86_64-windows") { +$toolchain_url = "https://update.flipperzero.one/builds/toolchain/gcc-arm-none-eabi-10.3-x86_64-windows-flipper-$toolchain_version.zip" +$toolchain_dist_folder = "gcc-arm-none-eabi-10.3-x86_64-windows-flipper" +$toolchain_zip = "$toolchain_dist_folder-$toolchain_version.zip" + +$toolchain_zip_temp_path = "$download_dir\$toolchain_zip" +$toolchain_dist_temp_path = "$download_dir\$toolchain_dist_folder" + +if (Test-Path -LiteralPath "$toolchain_target_path") { Write-Host -NoNewline "Removing old Windows toolchain.." - Remove-Item -LiteralPath "$repo_root\toolchain\x86_64-windows" -Force -Recurse + Remove-Item -LiteralPath "$toolchain_target_path" -Force -Recurse Write-Host "done!" } -if (!(Test-Path -Path "$repo_root\$toolchain_zip" -PathType Leaf)) { +if (!(Test-Path -Path "$toolchain_zip_temp_path" -PathType Leaf)) { Write-Host -NoNewline "Downloading Windows toolchain.." $wc = New-Object net.webclient - $wc.Downloadfile("$toolchain_url", "$repo_root\$toolchain_zip") + $wc.Downloadfile("$toolchain_url", "$toolchain_zip_temp_path") Write-Host "done!" } -if (!(Test-Path -LiteralPath "$repo_root\toolchain")) { - New-Item "$repo_root\toolchain" -ItemType Directory +if (!(Test-Path -LiteralPath "$toolchain_target_path\..")) { + New-Item "$toolchain_target_path\.." -ItemType Directory -Force } Write-Host -NoNewline "Extracting Windows toolchain.." +# This is faster than Expand-Archive Add-Type -Assembly "System.IO.Compression.Filesystem" -[System.IO.Compression.ZipFile]::ExtractToDirectory("$repo_root\$toolchain_zip", "$repo_root\") -Move-Item -Path "$repo_root\$toolchain_dir" -Destination "$repo_root\toolchain\x86_64-windows" +[System.IO.Compression.ZipFile]::ExtractToDirectory("$toolchain_zip_temp_path", "$download_dir") +# Expand-Archive -LiteralPath "$toolchain_zip_temp_path" -DestinationPath "$download_dir" + +Write-Host -NoNewline "moving.." +Move-Item -LiteralPath "$toolchain_dist_temp_path" -Destination "$toolchain_target_path" Write-Host "done!" Write-Host -NoNewline "Cleaning up temporary files.." -Remove-Item -LiteralPath "$repo_root\$toolchain_zip" -Force +Remove-Item -LiteralPath "$toolchain_zip_temp_path" -Force Write-Host "done!" + +# dasdasd \ No newline at end of file diff --git a/site_scons/cc.scons b/site_scons/cc.scons index c923b3872..1eb6a3376 100644 --- a/site_scons/cc.scons +++ b/site_scons/cc.scons @@ -30,10 +30,9 @@ ENV.AppendUnique( "-ffunction-sections", "-fsingle-precision-constant", "-fno-math-errno", - "-fstack-usage", + # Generates .su files with stack usage information + # "-fstack-usage", "-g", - # "-Wno-stringop-overread", - # "-Wno-stringop-overflow", ], CPPDEFINES=[ "_GNU_SOURCE", diff --git a/site_scons/environ.scons b/site_scons/environ.scons index 94705dada..96424caad 100644 --- a/site_scons/environ.scons +++ b/site_scons/environ.scons @@ -1,5 +1,10 @@ from SCons.Platform import TempFileMunge -from fbt.util import tempfile_arg_esc_func, single_quote, wrap_tempfile +from fbt.util import ( + tempfile_arg_esc_func, + single_quote, + wrap_tempfile, + extract_abs_dir_path, +) import os import multiprocessing @@ -52,6 +57,12 @@ coreenv = VAR_ENV.Clone( MAXLINELENGTH=2048, PROGSUFFIX=".elf", ENV=forward_os_env, + SINGLEQUOTEFUNC=single_quote, + ABSPATHGETTERFUNC=extract_abs_dir_path, + # Setting up temp file parameters - to overcome command line length limits + TEMPFILEARGESCFUNC=tempfile_arg_esc_func, + FBT_SCRIPT_DIR=Dir("#/scripts"), + ROOT_DIR=Dir("#"), ) # If DIST_SUFFIX is set in environment, is has precedence (set by CI) @@ -60,24 +71,6 @@ if os_suffix := os.environ.get("DIST_SUFFIX", None): DIST_SUFFIX=os_suffix, ) -# print(coreenv.Dump()) -if not coreenv["VERBOSE"]: - coreenv.SetDefault( - CCCOMSTR="\tCC\t${SOURCE}", - CXXCOMSTR="\tCPP\t${SOURCE}", - ASCOMSTR="\tASM\t${SOURCE}", - ARCOMSTR="\tAR\t${TARGET}", - RANLIBCOMSTR="\tRANLIB\t${TARGET}", - LINKCOMSTR="\tLINK\t${TARGET}", - INSTALLSTR="\tINSTALL\t${TARGET}", - APPSCOMSTR="\tAPPS\t${TARGET}", - VERSIONCOMSTR="\tVERSION\t${TARGET}", - STRIPCOMSTR="\tSTRIP\t${TARGET}", - OBJDUMPCOMSTR="\tOBJDUMP\t${TARGET}", - # GDBCOMSTR="\tGDB\t${SOURCE}", - # GDBPYCOMSTR="\tGDB-PY\t${SOURCE}", - ) - # Default value for commandline options SetOption("num_jobs", multiprocessing.cpu_count()) @@ -90,12 +83,7 @@ SetOption("max_drift", 1) # Random task queue - to discover isses with build logic faster # SetOption("random", 1) - -# Setting up temp file parameters - to overcome command line length limits -coreenv["TEMPFILEARGESCFUNC"] = tempfile_arg_esc_func wrap_tempfile(coreenv, "LINKCOM") wrap_tempfile(coreenv, "ARCOM") -coreenv["SINGLEQUOTEFUNC"] = single_quote - Return("coreenv") diff --git a/site_scons/extapps.scons b/site_scons/extapps.scons index 90d228e58..bb1d65ffc 100644 --- a/site_scons/extapps.scons +++ b/site_scons/extapps.scons @@ -3,10 +3,9 @@ from SCons.Errors import UserError Import("ENV") - from fbt.appmanifest import FlipperAppType -appenv = ENV.Clone( +appenv = ENV["APPENV"] = ENV.Clone( tools=[ ( "fbt_extapps", @@ -17,6 +16,7 @@ appenv = ENV.Clone( }, ), "fbt_assets", + "fbt_sdk", ] ) @@ -66,6 +66,7 @@ extapps = appenv["_extapps"] = { "validators": {}, "dist": {}, "resources_dist": None, + "sdk_tree": None, } @@ -115,10 +116,41 @@ if appsrc := appenv.subst("$APPSRC"): app_manifest, fap_file, app_validator = appenv.GetExtAppFromPath(appsrc) appenv.PhonyTarget( "launch_app", - '${PYTHON3} scripts/runfap.py ${SOURCE} --fap_dst_dir "/ext/apps/${FAP_CATEGORY}"', + '${PYTHON3} "${APP_RUN_SCRIPT}" ${SOURCE} --fap_dst_dir "/ext/apps/${FAP_CATEGORY}"', source=fap_file, FAP_CATEGORY=app_manifest.fap_category, ) appenv.Alias("launch_app", app_validator) +# SDK management + +sdk_origin_path = "${BUILD_DIR}/sdk_origin" +sdk_source = appenv.SDKPrebuilder( + sdk_origin_path, + # Deps on root SDK headers and generated files + (appenv["SDK_HEADERS"], appenv["FW_ASSETS_HEADERS"]), +) +# Extra deps on headers included in deeper levels +Depends(sdk_source, appenv.ProcessSdkDepends(f"{sdk_origin_path}.d")) + +appenv["SDK_DIR"] = appenv.Dir("${BUILD_DIR}/sdk") +sdk_tree = extapps["sdk_tree"] = appenv.SDKTree(appenv["SDK_DIR"], sdk_origin_path) +# AlwaysBuild(sdk_tree) +Alias("sdk_tree", sdk_tree) + +sdk_apicheck = appenv.SDKSymUpdater(appenv["SDK_DEFINITION"], sdk_origin_path) +Precious(sdk_apicheck) +NoClean(sdk_apicheck) +AlwaysBuild(sdk_apicheck) +Alias("sdk_check", sdk_apicheck) + +sdk_apisyms = appenv.SDKSymGenerator( + "${BUILD_DIR}/assets/compiled/symbols.h", appenv["SDK_DEFINITION"] +) +Alias("api_syms", sdk_apisyms) + +if appenv["FORCE"]: + appenv.AlwaysBuild(sdk_source, sdk_tree, sdk_apicheck, sdk_apisyms) + + Return("extapps") From a09d0a8bd42d727fe4b686b761cb8012dc99703a Mon Sep 17 00:00:00 2001 From: Konstantin Volkov <72250702+doomwastaken@users.noreply.github.com> Date: Wed, 2 Nov 2022 18:21:43 +0300 Subject: [PATCH 26/31] fixed job name, renamed compile step id (#1952) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: あく --- .github/workflows/unit_tests.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/unit_tests.yml b/.github/workflows/unit_tests.yml index a7671f0f9..1ca4a9c03 100644 --- a/.github/workflows/unit_tests.yml +++ b/.github/workflows/unit_tests.yml @@ -8,7 +8,7 @@ env: DEFAULT_TARGET: f7 jobs: - main: + run_units_on_test_bench: runs-on: [self-hosted, FlipperZeroTest] steps: - name: Checkout code @@ -22,14 +22,14 @@ jobs: run: | echo "flipper=/dev/ttyACM0" >> $GITHUB_OUTPUT - - name: 'Compile unit tests firmware' - id: compile + - name: 'Flash unit tests firmware' + id: flashing run: | FBT_TOOLCHAIN_PATH=/opt ./fbt flash OPENOCD_ADAPTER_SERIAL=2A0906016415303030303032 FIRMWARE_APP_SET=unit_tests FORCE=1 - name: 'Wait for flipper to finish updating' id: connect - if: steps.compile.outcome == 'success' + if: steps.flashing.outcome == 'success' run: | . scripts/toolchain/fbtenv.sh ./scripts/testing/await_flipper.py ${{steps.device.outputs.flipper}} From c417d467f72b63b25f7dce0eee6b3554fd2d8d0d Mon Sep 17 00:00:00 2001 From: Georgii Surkov <37121527+gsurkov@users.noreply.github.com> Date: Wed, 2 Nov 2022 19:13:06 +0300 Subject: [PATCH 27/31] Handle storage full error (#1958) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: あく --- applications/services/rpc/rpc_storage.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/applications/services/rpc/rpc_storage.c b/applications/services/rpc/rpc_storage.c index 1b545b414..e28998e13 100644 --- a/applications/services/rpc/rpc_storage.c +++ b/applications/services/rpc/rpc_storage.c @@ -405,6 +405,10 @@ static void rpc_system_storage_write_process(const PB_Main* request, void* conte if(!fs_operation_success) { send_response = true; command_status = rpc_system_storage_get_file_error(file); + if(command_status == PB_CommandStatus_OK) { + // Report errors not handled by underlying APIs + command_status = PB_CommandStatus_ERROR_STORAGE_INTERNAL; + } } if(send_response) { From 0652830c51cee5fced1d4651c538d24160599dfa Mon Sep 17 00:00:00 2001 From: Skorpionm <85568270+Skorpionm@users.noreply.github.com> Date: Wed, 2 Nov 2022 20:24:07 +0400 Subject: [PATCH 28/31] [FL-2940] WS: add protocol Ambient_Weather (#1960) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * WS: add protocol Ambient_Weather * WS: fix link * WS: removing unused code Co-authored-by: あく --- .../helpers/weather_station_types.h | 2 +- .../protocols/acurite_592txr.c | 2 +- .../protocols/ambient_weather.c | 278 ++++++++++++++++++ .../protocols/ambient_weather.h | 79 +++++ .../weather_station/protocols/gt_wt_03.c | 2 +- .../protocols/lacrosse_tx141thbv2.c | 2 +- .../weather_station/protocols/nexus_th.c | 2 +- .../protocols/protocol_items.c | 1 + .../protocols/protocol_items.h | 1 + 9 files changed, 364 insertions(+), 5 deletions(-) create mode 100644 applications/plugins/weather_station/protocols/ambient_weather.c create mode 100644 applications/plugins/weather_station/protocols/ambient_weather.h diff --git a/applications/plugins/weather_station/helpers/weather_station_types.h b/applications/plugins/weather_station/helpers/weather_station_types.h index 2976cbce8..5a66dd0ce 100644 --- a/applications/plugins/weather_station/helpers/weather_station_types.h +++ b/applications/plugins/weather_station/helpers/weather_station_types.h @@ -3,7 +3,7 @@ #include #include -#define WS_VERSION_APP "0.3.1" +#define WS_VERSION_APP "0.4" #define WS_DEVELOPED "SkorP" #define WS_GITHUB "https://github.com/flipperdevices/flipperzero-firmware" diff --git a/applications/plugins/weather_station/protocols/acurite_592txr.c b/applications/plugins/weather_station/protocols/acurite_592txr.c index db05af095..4d7f59544 100644 --- a/applications/plugins/weather_station/protocols/acurite_592txr.c +++ b/applications/plugins/weather_station/protocols/acurite_592txr.c @@ -4,7 +4,7 @@ /* * Help - * https://github.com/merbanan/rtl_433/blob/5bef4e43133ac4c0e2d18d36f87c52b4f9458453/src/devices/acurite.c + * https://github.com/merbanan/rtl_433/blob/master/src/devices/acurite.c * * Acurite 592TXR Temperature Humidity sensor decoder * Message Type 0x04, 7 bytes diff --git a/applications/plugins/weather_station/protocols/ambient_weather.c b/applications/plugins/weather_station/protocols/ambient_weather.c new file mode 100644 index 000000000..07f5330fc --- /dev/null +++ b/applications/plugins/weather_station/protocols/ambient_weather.c @@ -0,0 +1,278 @@ +#include "ambient_weather.h" +#include + +#define TAG "WSProtocolAmbient_Weather" + +/* + * Help + * https://github.com/merbanan/rtl_433/blob/master/src/devices/ambient_weather.c + * + * Decode Ambient Weather F007TH, F012TH, TF 30.3208.02, SwitchDoc F016TH. + * Devices supported: + * - Ambient Weather F007TH Thermo-Hygrometer. + * - Ambient Weather F012TH Indoor/Display Thermo-Hygrometer. + * - TFA senders 30.3208.02 from the TFA "Klima-Monitor" 30.3054, + * - SwitchDoc Labs F016TH. + * This decoder handles the 433mhz/868mhz thermo-hygrometers. + * The 915mhz (WH*) family of devices use different modulation/encoding. + * Byte 0 Byte 1 Byte 2 Byte 3 Byte 4 Byte 5 + * xxxxMMMM IIIIIIII BCCCTTTT TTTTTTTT HHHHHHHH MMMMMMMM + * - x: Unknown 0x04 on F007TH/F012TH + * - M: Model Number?, 0x05 on F007TH/F012TH/SwitchDocLabs F016TH + * - I: ID byte (8 bits), volatie, changes at power up, + * - B: Battery Low + * - C: Channel (3 bits 1-8) - F007TH set by Dip switch, F012TH soft setting + * - T: Temperature 12 bits - Fahrenheit * 10 + 400 + * - H: Humidity (8 bits) + * - M: Message integrity check LFSR Digest-8, gen 0x98, key 0x3e, init 0x64 + * + * three repeats without gap + * full preamble is 0x00145 (the last bits might not be fixed, e.g. 0x00146) + * and on decoding also 0xffd45 + */ + +#define AMBIENT_WEATHER_PACKET_HEADER_1 0xFFD440000000000 //0xffd45 .. 0xffd46 +#define AMBIENT_WEATHER_PACKET_HEADER_2 0x001440000000000 //0x00145 .. 0x00146 +#define AMBIENT_WEATHER_PACKET_HEADER_MASK 0xFFFFC0000000000 + +static const SubGhzBlockConst ws_protocol_ambient_weather_const = { + .te_short = 500, + .te_long = 1000, + .te_delta = 120, + .min_count_bit_for_found = 48, +}; + +struct WSProtocolDecoderAmbient_Weather { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + WSBlockGeneric generic; + ManchesterState manchester_saved_state; + uint16_t header_count; +}; + +struct WSProtocolEncoderAmbient_Weather { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolBlockEncoder encoder; + WSBlockGeneric generic; +}; + +const SubGhzProtocolDecoder ws_protocol_ambient_weather_decoder = { + .alloc = ws_protocol_decoder_ambient_weather_alloc, + .free = ws_protocol_decoder_ambient_weather_free, + + .feed = ws_protocol_decoder_ambient_weather_feed, + .reset = ws_protocol_decoder_ambient_weather_reset, + + .get_hash_data = ws_protocol_decoder_ambient_weather_get_hash_data, + .serialize = ws_protocol_decoder_ambient_weather_serialize, + .deserialize = ws_protocol_decoder_ambient_weather_deserialize, + .get_string = ws_protocol_decoder_ambient_weather_get_string, +}; + +const SubGhzProtocolEncoder ws_protocol_ambient_weather_encoder = { + .alloc = NULL, + .free = NULL, + + .deserialize = NULL, + .stop = NULL, + .yield = NULL, +}; + +const SubGhzProtocol ws_protocol_ambient_weather = { + .name = WS_PROTOCOL_AMBIENT_WEATHER_NAME, + .type = SubGhzProtocolWeatherStation, + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_315 | SubGhzProtocolFlag_868 | + SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable, + + .decoder = &ws_protocol_ambient_weather_decoder, + .encoder = &ws_protocol_ambient_weather_encoder, +}; + +void* ws_protocol_decoder_ambient_weather_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + WSProtocolDecoderAmbient_Weather* instance = malloc(sizeof(WSProtocolDecoderAmbient_Weather)); + instance->base.protocol = &ws_protocol_ambient_weather; + instance->generic.protocol_name = instance->base.protocol->name; + return instance; +} + +void ws_protocol_decoder_ambient_weather_free(void* context) { + furi_assert(context); + WSProtocolDecoderAmbient_Weather* instance = context; + free(instance); +} + +void ws_protocol_decoder_ambient_weather_reset(void* context) { + furi_assert(context); + WSProtocolDecoderAmbient_Weather* instance = context; + manchester_advance( + instance->manchester_saved_state, + ManchesterEventReset, + &instance->manchester_saved_state, + NULL); +} + +static bool ws_protocol_ambient_weather_check_crc(WSProtocolDecoderAmbient_Weather* instance) { + uint8_t msg[] = { + instance->decoder.decode_data >> 40, + instance->decoder.decode_data >> 32, + instance->decoder.decode_data >> 24, + instance->decoder.decode_data >> 16, + instance->decoder.decode_data >> 8}; + + uint8_t crc = subghz_protocol_blocks_lfsr_digest8(msg, 5, 0x98, 0x3e) ^ 0x64; + return (crc == (uint8_t)(instance->decoder.decode_data & 0xFF)); +} + +/** + * Analysis of received data + * @param instance Pointer to a WSBlockGeneric* instance + */ +static void ws_protocol_ambient_weather_remote_controller(WSBlockGeneric* instance) { + instance->id = (instance->data >> 32) & 0xFF; + instance->battery_low = (instance->data >> 31) & 1; + instance->channel = ((instance->data >> 28) & 0x07) + 1; + instance->temp = ws_block_generic_fahrenheit_to_celsius( + ((float)((instance->data >> 16) & 0x0FFF) - 400.0f) / 10.0f); + instance->humidity = (instance->data >> 8) & 0xFF; + instance->btn = WS_NO_BTN; + + // ToDo maybe it won't be needed + /* + Sanity checks to reduce false positives and other bad data + Packets with Bad data often pass the MIC check. + - humidity > 100 (such as 255) and + - temperatures > 140 F (such as 369.5 F and 348.8 F + Specs in the F007TH and F012TH manuals state the range is: + - Temperature: -40 to 140 F + - Humidity: 10 to 99% + @todo - sanity check b[0] "model number" + - 0x45 - F007TH and F012TH + - 0x?5 - SwitchDocLabs F016TH temperature sensor (based on comment b[0] & 0x0f == 5) + - ? - TFA 30.3208.02 + if (instance->humidity < 0 || instance->humidity > 100) { + ERROR; + } + + if (instance->temp < -40.0 || instance->temp > 140.0) { + ERROR; + } + */ +} + +void ws_protocol_decoder_ambient_weather_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + WSProtocolDecoderAmbient_Weather* instance = context; + + ManchesterEvent event = ManchesterEventReset; + if(!level) { + if(DURATION_DIFF(duration, ws_protocol_ambient_weather_const.te_short) < + ws_protocol_ambient_weather_const.te_delta) { + event = ManchesterEventShortLow; + } else if( + DURATION_DIFF(duration, ws_protocol_ambient_weather_const.te_long) < + ws_protocol_ambient_weather_const.te_delta * 2) { + event = ManchesterEventLongLow; + } + } else { + if(DURATION_DIFF(duration, ws_protocol_ambient_weather_const.te_short) < + ws_protocol_ambient_weather_const.te_delta) { + event = ManchesterEventShortHigh; + } else if( + DURATION_DIFF(duration, ws_protocol_ambient_weather_const.te_long) < + ws_protocol_ambient_weather_const.te_delta * 2) { + event = ManchesterEventLongHigh; + } + } + 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; + } + + if(((instance->decoder.decode_data & AMBIENT_WEATHER_PACKET_HEADER_MASK) == + AMBIENT_WEATHER_PACKET_HEADER_1) || + ((instance->decoder.decode_data & AMBIENT_WEATHER_PACKET_HEADER_MASK) == + AMBIENT_WEATHER_PACKET_HEADER_2)) { + if(ws_protocol_ambient_weather_check_crc(instance)) { + instance->decoder.decode_data = instance->decoder.decode_data; + instance->generic.data = instance->decoder.decode_data; + instance->generic.data_count_bit = + ws_protocol_ambient_weather_const.min_count_bit_for_found; + ws_protocol_ambient_weather_remote_controller(&instance->generic); + if(instance->base.callback) + instance->base.callback(&instance->base, instance->base.context); + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + } + } + } else { + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + manchester_advance( + instance->manchester_saved_state, + ManchesterEventReset, + &instance->manchester_saved_state, + NULL); + } +} + +uint8_t ws_protocol_decoder_ambient_weather_get_hash_data(void* context) { + furi_assert(context); + WSProtocolDecoderAmbient_Weather* instance = context; + return subghz_protocol_blocks_get_hash_data( + &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); +} + +bool ws_protocol_decoder_ambient_weather_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(context); + WSProtocolDecoderAmbient_Weather* instance = context; + return ws_block_generic_serialize(&instance->generic, flipper_format, preset); +} + +bool ws_protocol_decoder_ambient_weather_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + WSProtocolDecoderAmbient_Weather* instance = context; + bool ret = false; + do { + if(!ws_block_generic_deserialize(&instance->generic, flipper_format)) { + break; + } + if(instance->generic.data_count_bit != + ws_protocol_ambient_weather_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } + ret = true; + } while(false); + return ret; +} + +void ws_protocol_decoder_ambient_weather_get_string(void* context, FuriString* output) { + furi_assert(context); + WSProtocolDecoderAmbient_Weather* instance = context; + furi_string_printf( + output, + "%s %dbit\r\n" + "Key:0x%lX%08lX\r\n" + "Sn:0x%lX Ch:%d Bat:%d\r\n" + "Temp:%d.%d C Hum:%d%%", + instance->generic.protocol_name, + instance->generic.data_count_bit, + (uint32_t)(instance->generic.data >> 32), + (uint32_t)(instance->generic.data), + instance->generic.id, + instance->generic.channel, + instance->generic.battery_low, + (int16_t)instance->generic.temp, + abs(((int16_t)(instance->generic.temp * 10) - (((int16_t)instance->generic.temp) * 10))), + instance->generic.humidity); +} diff --git a/applications/plugins/weather_station/protocols/ambient_weather.h b/applications/plugins/weather_station/protocols/ambient_weather.h new file mode 100644 index 000000000..04cc5819c --- /dev/null +++ b/applications/plugins/weather_station/protocols/ambient_weather.h @@ -0,0 +1,79 @@ +#pragma once + +#include + +#include +#include +#include +#include "ws_generic.h" +#include + +#define WS_PROTOCOL_AMBIENT_WEATHER_NAME "Ambient_Weather" + +typedef struct WSProtocolDecoderAmbient_Weather WSProtocolDecoderAmbient_Weather; +typedef struct WSProtocolEncoderAmbient_Weather WSProtocolEncoderAmbient_Weather; + +extern const SubGhzProtocolDecoder ws_protocol_ambient_weather_decoder; +extern const SubGhzProtocolEncoder ws_protocol_ambient_weather_encoder; +extern const SubGhzProtocol ws_protocol_ambient_weather; + +/** + * Allocate WSProtocolDecoderAmbient_Weather. + * @param environment Pointer to a SubGhzEnvironment instance + * @return WSProtocolDecoderAmbient_Weather* pointer to a WSProtocolDecoderAmbient_Weather instance + */ +void* ws_protocol_decoder_ambient_weather_alloc(SubGhzEnvironment* environment); + +/** + * Free WSProtocolDecoderAmbient_Weather. + * @param context Pointer to a WSProtocolDecoderAmbient_Weather instance + */ +void ws_protocol_decoder_ambient_weather_free(void* context); + +/** + * Reset decoder WSProtocolDecoderAmbient_Weather. + * @param context Pointer to a WSProtocolDecoderAmbient_Weather instance + */ +void ws_protocol_decoder_ambient_weather_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a WSProtocolDecoderAmbient_Weather instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void ws_protocol_decoder_ambient_weather_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a WSProtocolDecoderAmbient_Weather instance + * @return hash Hash sum + */ +uint8_t ws_protocol_decoder_ambient_weather_get_hash_data(void* context); + +/** + * Serialize data WSProtocolDecoderAmbient_Weather. + * @param context Pointer to a WSProtocolDecoderAmbient_Weather instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return true On success + */ +bool ws_protocol_decoder_ambient_weather_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data WSProtocolDecoderAmbient_Weather. + * @param context Pointer to a WSProtocolDecoderAmbient_Weather instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool ws_protocol_decoder_ambient_weather_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a WSProtocolDecoderAmbient_Weather instance + * @param output Resulting text + */ +void ws_protocol_decoder_ambient_weather_get_string(void* context, FuriString* output); diff --git a/applications/plugins/weather_station/protocols/gt_wt_03.c b/applications/plugins/weather_station/protocols/gt_wt_03.c index 1492374c9..04bca9ac1 100644 --- a/applications/plugins/weather_station/protocols/gt_wt_03.c +++ b/applications/plugins/weather_station/protocols/gt_wt_03.c @@ -4,7 +4,7 @@ /* * Help - * https://github.com/merbanan/rtl_433/blob/5f0ff6db624270a4598958ab9dd79bb385ced3ef/src/devices/gt_wt_03.c + * https://github.com/merbanan/rtl_433/blob/master/src/devices/gt_wt_03.c * * * Globaltronics GT-WT-03 sensor on 433.92MHz. diff --git a/applications/plugins/weather_station/protocols/lacrosse_tx141thbv2.c b/applications/plugins/weather_station/protocols/lacrosse_tx141thbv2.c index 828d49be7..d4b89be87 100644 --- a/applications/plugins/weather_station/protocols/lacrosse_tx141thbv2.c +++ b/applications/plugins/weather_station/protocols/lacrosse_tx141thbv2.c @@ -4,7 +4,7 @@ /* * Help - * https://github.com/merbanan/rtl_433/blob/7e83cfd27d14247b6c3c81732bfe4a4f9a974d30/src/devices/lacrosse_tx141x.c + * https://github.com/merbanan/rtl_433/blob/master/src/devices/lacrosse_tx141x.c * * iiii iiii | bkcc tttt | tttt tttt | hhhh hhhh | cccc cccc | u * - i: identification; changes on battery switch diff --git a/applications/plugins/weather_station/protocols/nexus_th.c b/applications/plugins/weather_station/protocols/nexus_th.c index a2ab0f412..c3d823eda 100644 --- a/applications/plugins/weather_station/protocols/nexus_th.c +++ b/applications/plugins/weather_station/protocols/nexus_th.c @@ -4,7 +4,7 @@ /* * Help - * https://github.com/merbanan/rtl_433/blob/ef2d37cf51e3264d11cde9149ef87de2f0a4d37a/src/devices/nexus.c + * https://github.com/merbanan/rtl_433/blob/master/src/devices/nexus.c * * Nexus sensor protocol with ID, temperature and optional humidity * also FreeTec (Pearl) NC-7345 sensors for FreeTec Weatherstation NC-7344, diff --git a/applications/plugins/weather_station/protocols/protocol_items.c b/applications/plugins/weather_station/protocols/protocol_items.c index 3ec9e995a..d7f6458ab 100644 --- a/applications/plugins/weather_station/protocols/protocol_items.c +++ b/applications/plugins/weather_station/protocols/protocol_items.c @@ -9,6 +9,7 @@ const SubGhzProtocol* weather_station_protocol_registry_items[] = { &ws_protocol_lacrosse_tx141thbv2, &ws_protocol_oregon2, &ws_protocol_acurite_592txr, + &ws_protocol_ambient_weather, }; const SubGhzProtocolRegistry weather_station_protocol_registry = { diff --git a/applications/plugins/weather_station/protocols/protocol_items.h b/applications/plugins/weather_station/protocols/protocol_items.h index 8f3eb53d7..76c085ab4 100644 --- a/applications/plugins/weather_station/protocols/protocol_items.h +++ b/applications/plugins/weather_station/protocols/protocol_items.h @@ -9,5 +9,6 @@ #include "lacrosse_tx141thbv2.h" #include "oregon2.h" #include "acurite_592txr.h" +#include "ambient_weather.h" extern const SubGhzProtocolRegistry weather_station_protocol_registry; From 95182b266cfd6fc1d81ec1ff4019bfcca930e1cb Mon Sep 17 00:00:00 2001 From: Nikolay Minaylov Date: Thu, 3 Nov 2022 07:42:54 +0300 Subject: [PATCH 29/31] BadUSB scrolllock typo fix (#1968) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: あく --- applications/main/bad_usb/bad_usb_script.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applications/main/bad_usb/bad_usb_script.c b/applications/main/bad_usb/bad_usb_script.c index 8ff38ef66..b90218f8d 100644 --- a/applications/main/bad_usb/bad_usb_script.c +++ b/applications/main/bad_usb/bad_usb_script.c @@ -82,7 +82,7 @@ static const DuckyKey ducky_keys[] = { {"PAGEUP", HID_KEYBOARD_PAGE_UP}, {"PAGEDOWN", HID_KEYBOARD_PAGE_DOWN}, {"PRINTSCREEN", HID_KEYBOARD_PRINT_SCREEN}, - {"SCROLLOCK", HID_KEYBOARD_SCROLL_LOCK}, + {"SCROLLLOCK", HID_KEYBOARD_SCROLL_LOCK}, {"SPACE", HID_KEYBOARD_SPACEBAR}, {"TAB", HID_KEYBOARD_TAB}, {"MENU", HID_KEYBOARD_APPLICATION}, From eee90c6c406fef114be9b2250936c5e36f0a46cd Mon Sep 17 00:00:00 2001 From: head47 <63517545+head47@users.noreply.github.com> Date: Thu, 3 Nov 2022 08:21:44 +0300 Subject: [PATCH 30/31] Run Bad USB immediately after connection (#1955) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: あく --- applications/main/bad_usb/bad_usb_script.c | 31 ++++++++++++++++++- applications/main/bad_usb/bad_usb_script.h | 1 + .../main/bad_usb/views/bad_usb_view.c | 10 +++++- 3 files changed, 40 insertions(+), 2 deletions(-) diff --git a/applications/main/bad_usb/bad_usb_script.c b/applications/main/bad_usb/bad_usb_script.c index b90218f8d..33b3f5030 100644 --- a/applications/main/bad_usb/bad_usb_script.c +++ b/applications/main/bad_usb/bad_usb_script.c @@ -524,12 +524,16 @@ static int32_t bad_usb_worker(void* context) { } else if(worker_state == BadUsbStateNotConnected) { // State: USB not connected uint32_t flags = furi_thread_flags_wait( - WorkerEvtEnd | WorkerEvtConnect, FuriFlagWaitAny, FuriWaitForever); + WorkerEvtEnd | WorkerEvtConnect | WorkerEvtToggle, + FuriFlagWaitAny, + FuriWaitForever); furi_check((flags & FuriFlagError) == 0); if(flags & WorkerEvtEnd) { break; } else if(flags & WorkerEvtConnect) { worker_state = BadUsbStateIdle; // Ready to run + } else if(flags & WorkerEvtToggle) { + worker_state = BadUsbStateWillRun; // Will run when USB is connected } bad_usb->st.state = worker_state; @@ -556,6 +560,31 @@ static int32_t bad_usb_worker(void* context) { } bad_usb->st.state = worker_state; + } else if(worker_state == BadUsbStateWillRun) { // State: start on connection + uint32_t flags = furi_thread_flags_wait( + WorkerEvtEnd | WorkerEvtConnect | WorkerEvtToggle, + FuriFlagWaitAny, + FuriWaitForever); + furi_check((flags & FuriFlagError) == 0); + if(flags & WorkerEvtEnd) { + break; + } else if(flags & WorkerEvtConnect) { // Start executing script + DOLPHIN_DEED(DolphinDeedBadUsbPlayScript); + delay_val = 0; + bad_usb->buf_len = 0; + bad_usb->st.line_cur = 0; + bad_usb->defdelay = 0; + bad_usb->repeat_cnt = 0; + bad_usb->file_end = false; + storage_file_seek(script_file, 0, true); + // extra time for PC to recognize Flipper as keyboard + furi_thread_flags_wait(0, FuriFlagWaitAny, 1500); + worker_state = BadUsbStateRunning; + } else if(flags & WorkerEvtToggle) { // Cancel scheduled execution + worker_state = BadUsbStateNotConnected; + } + bad_usb->st.state = worker_state; + } else if(worker_state == BadUsbStateRunning) { // State: running uint16_t delay_cur = (delay_val > 1000) ? (1000) : (delay_val); uint32_t flags = furi_thread_flags_wait( diff --git a/applications/main/bad_usb/bad_usb_script.h b/applications/main/bad_usb/bad_usb_script.h index f24372fab..188142db8 100644 --- a/applications/main/bad_usb/bad_usb_script.h +++ b/applications/main/bad_usb/bad_usb_script.h @@ -12,6 +12,7 @@ typedef enum { BadUsbStateInit, BadUsbStateNotConnected, BadUsbStateIdle, + BadUsbStateWillRun, BadUsbStateRunning, BadUsbStateDelay, BadUsbStateDone, diff --git a/applications/main/bad_usb/views/bad_usb_view.c b/applications/main/bad_usb/views/bad_usb_view.c index e5c5d92a3..b3eb9bb56 100644 --- a/applications/main/bad_usb/views/bad_usb_view.c +++ b/applications/main/bad_usb/views/bad_usb_view.c @@ -29,10 +29,13 @@ static void bad_usb_draw_callback(Canvas* canvas, void* _model) { canvas_draw_icon(canvas, 22, 20, &I_UsbTree_48x22); - if((model->state.state == BadUsbStateIdle) || (model->state.state == BadUsbStateDone)) { + if((model->state.state == BadUsbStateIdle) || (model->state.state == BadUsbStateDone) || + (model->state.state == BadUsbStateNotConnected)) { elements_button_center(canvas, "Run"); } else if((model->state.state == BadUsbStateRunning) || (model->state.state == BadUsbStateDelay)) { elements_button_center(canvas, "Stop"); + } else if(model->state.state == BadUsbStateWillRun) { + elements_button_center(canvas, "Cancel"); } if(model->state.state == BadUsbStateNotConnected) { @@ -40,6 +43,11 @@ static void bad_usb_draw_callback(Canvas* canvas, void* _model) { canvas_set_font(canvas, FontPrimary); canvas_draw_str_aligned(canvas, 127, 27, AlignRight, AlignBottom, "Connect"); canvas_draw_str_aligned(canvas, 127, 39, AlignRight, AlignBottom, "to USB"); + } else if(model->state.state == BadUsbStateWillRun) { + canvas_draw_icon(canvas, 4, 22, &I_Clock_18x18); + canvas_set_font(canvas, FontPrimary); + canvas_draw_str_aligned(canvas, 127, 27, AlignRight, AlignBottom, "Will run"); + canvas_draw_str_aligned(canvas, 127, 39, AlignRight, AlignBottom, "on connect"); } else if(model->state.state == BadUsbStateFileError) { canvas_draw_icon(canvas, 4, 22, &I_Error_18x18); canvas_set_font(canvas, FontPrimary); From 60d125e72a832d300a487b3668187f7a0fe6e996 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexandre=20D=C3=ADaz?= Date: Thu, 3 Nov 2022 08:57:56 +0100 Subject: [PATCH 31/31] subghz: add analyzer frequency logs (#1914) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * subghz: add analyzer frequency logs * SubGhz: switch to change on short press * SubGhz: use full RSSI bar for history view Co-authored-by: あく --- ...subghz_frequency_analyzer_log_item_array.c | 27 ++ ...subghz_frequency_analyzer_log_item_array.h | 73 +++++ .../subghz/views/subghz_frequency_analyzer.c | 280 ++++++++++++++++-- 3 files changed, 355 insertions(+), 25 deletions(-) create mode 100644 applications/main/subghz/helpers/subghz_frequency_analyzer_log_item_array.c create mode 100644 applications/main/subghz/helpers/subghz_frequency_analyzer_log_item_array.h diff --git a/applications/main/subghz/helpers/subghz_frequency_analyzer_log_item_array.c b/applications/main/subghz/helpers/subghz_frequency_analyzer_log_item_array.c new file mode 100644 index 000000000..9b33e92d1 --- /dev/null +++ b/applications/main/subghz/helpers/subghz_frequency_analyzer_log_item_array.c @@ -0,0 +1,27 @@ +#include "subghz_frequency_analyzer_log_item_array.h" + +const char* + subghz_frequency_analyzer_log_get_order_name(SubGhzFrequencyAnalyzerLogOrderBy order_by) { + if(order_by == SubGhzFrequencyAnalyzerLogOrderBySeqAsc) { + return "Seq. A"; + } + if(order_by == SubGhzFrequencyAnalyzerLogOrderByCountDesc) { + return "Count D"; + } + if(order_by == SubGhzFrequencyAnalyzerLogOrderByCountAsc) { + return "Count A"; + } + if(order_by == SubGhzFrequencyAnalyzerLogOrderByRSSIDesc) { + return "RSSI D"; + } + if(order_by == SubGhzFrequencyAnalyzerLogOrderByRSSIAsc) { + return "RSSI A"; + } + if(order_by == SubGhzFrequencyAnalyzerLogOrderByFrequencyDesc) { + return "Freq. D"; + } + if(order_by == SubGhzFrequencyAnalyzerLogOrderByFrequencyAsc) { + return "Freq. A"; + } + return "Seq. D"; +} diff --git a/applications/main/subghz/helpers/subghz_frequency_analyzer_log_item_array.h b/applications/main/subghz/helpers/subghz_frequency_analyzer_log_item_array.h new file mode 100644 index 000000000..eaf53b663 --- /dev/null +++ b/applications/main/subghz/helpers/subghz_frequency_analyzer_log_item_array.h @@ -0,0 +1,73 @@ +#pragma once + +#include +#include +#include +#include + +typedef enum { + SubGhzFrequencyAnalyzerLogOrderBySeqDesc, + SubGhzFrequencyAnalyzerLogOrderBySeqAsc, + SubGhzFrequencyAnalyzerLogOrderByCountDesc, + SubGhzFrequencyAnalyzerLogOrderByCountAsc, + SubGhzFrequencyAnalyzerLogOrderByRSSIDesc, + SubGhzFrequencyAnalyzerLogOrderByRSSIAsc, + SubGhzFrequencyAnalyzerLogOrderByFrequencyDesc, + SubGhzFrequencyAnalyzerLogOrderByFrequencyAsc, +} SubGhzFrequencyAnalyzerLogOrderBy; + +const char* + subghz_frequency_analyzer_log_get_order_name(SubGhzFrequencyAnalyzerLogOrderBy order_by); + +TUPLE_DEF2( + SubGhzFrequencyAnalyzerLogItem, + (seq, uint8_t), + (frequency, uint32_t), + (count, uint8_t), + (rssi_max, uint8_t)) +/* Register globaly the oplist */ +#define M_OPL_SubGhzFrequencyAnalyzerLogItem_t() \ + TUPLE_OPLIST(SubGhzFrequencyAnalyzerLogItem, M_POD_OPLIST, M_DEFAULT_OPLIST, M_DEFAULT_OPLIST) + +/* Define the array, register the oplist and define further algorithms on it */ +ARRAY_DEF(SubGhzFrequencyAnalyzerLogItemArray, SubGhzFrequencyAnalyzerLogItem_t) +#define M_OPL_SubGhzFrequencyAnalyzerLogItemArray_t() \ + ARRAY_OPLIST(SubGhzFrequencyAnalyzerLogItemArray, M_OPL_SubGhzFrequencyAnalyzerLogItem_t()) +ALGO_DEF(SubGhzFrequencyAnalyzerLogItemArray, SubGhzFrequencyAnalyzerLogItemArray_t) + +FUNC_OBJ_INS_DEF( + SubGhzFrequencyAnalyzerLogItemArray_compare_by /* name of the instance */, + SubGhzFrequencyAnalyzerLogItemArray_cmp_obj /* name of the interface */, + (a, + b) /* name of the input parameters of the function like object. The type are inherited from the interface. */ + , + { + /* code of the function object */ + if(self->order_by == SubGhzFrequencyAnalyzerLogOrderByFrequencyAsc) { + return a->frequency < b->frequency ? -1 : a->frequency > b->frequency; + } + if(self->order_by == SubGhzFrequencyAnalyzerLogOrderByFrequencyDesc) { + return a->frequency > b->frequency ? -1 : a->frequency < b->frequency; + } + if(self->order_by == SubGhzFrequencyAnalyzerLogOrderByRSSIAsc) { + return a->rssi_max < b->rssi_max ? -1 : a->rssi_max > b->rssi_max; + } + if(self->order_by == SubGhzFrequencyAnalyzerLogOrderByRSSIDesc) { + return a->rssi_max > b->rssi_max ? -1 : a->rssi_max < b->rssi_max; + } + if(self->order_by == SubGhzFrequencyAnalyzerLogOrderByCountAsc) { + return a->count < b->count ? -1 : a->count > b->count; + } + if(self->order_by == SubGhzFrequencyAnalyzerLogOrderByCountDesc) { + return a->count > b->count ? -1 : a->count < b->count; + } + if(self->order_by == SubGhzFrequencyAnalyzerLogOrderBySeqAsc) { + return a->seq < b->seq ? -1 : a->seq > b->seq; + } + + return a->seq > b->seq ? -1 : a->seq < b->seq; + }, + /* Additional fields stored in the function object */ + (order_by, SubGhzFrequencyAnalyzerLogOrderBy)) +#define M_OPL_SubGhzFrequencyAnalyzerLogItemArray_compare_by_t() \ + FUNC_OBJ_INS_OPLIST(SubGhzFrequencyAnalyzerLogItemArray_compare_by, M_DEFAULT_OPLIST) diff --git a/applications/main/subghz/views/subghz_frequency_analyzer.c b/applications/main/subghz/views/subghz_frequency_analyzer.c index c169f3611..e980bd970 100644 --- a/applications/main/subghz/views/subghz_frequency_analyzer.c +++ b/applications/main/subghz/views/subghz_frequency_analyzer.c @@ -5,30 +5,53 @@ #include #include #include +#include #include #include "../helpers/subghz_frequency_analyzer_worker.h" +#include "../helpers/subghz_frequency_analyzer_log_item_array.h" #include +#define LOG_FREQUENCY_MAX_ITEMS 60 // uint8_t (limited by 'seq' of SubGhzFrequencyAnalyzerLogItem) +#define RSSI_OFFSET 74 +#define RSSI_MAX 53 // 127 - RSSI_OFFSET + +#define SNPRINTF_FREQUENCY(buff, freq) \ + snprintf(buff, sizeof(buff), "%03ld.%03ld", freq / 1000000 % 1000, freq / 1000 % 1000); + typedef enum { SubGhzFrequencyAnalyzerStatusIDLE, } SubGhzFrequencyAnalyzerStatus; +typedef enum { + SubGhzFrequencyAnalyzerFragmentBottomTypeMain, + SubGhzFrequencyAnalyzerFragmentBottomTypeLog, +} SubGhzFrequencyAnalyzerFragmentBottomType; + struct SubGhzFrequencyAnalyzer { View* view; SubGhzFrequencyAnalyzerWorker* worker; SubGhzFrequencyAnalyzerCallback callback; void* context; bool locked; + uint32_t last_frequency; }; typedef struct { uint32_t frequency; - float rssi; + uint8_t rssi; uint32_t history_frequency[3]; bool signal; + SubGhzFrequencyAnalyzerLogItemArray_t log_frequency; + SubGhzFrequencyAnalyzerFragmentBottomType fragment_bottom_type; + SubGhzFrequencyAnalyzerLogOrderBy log_frequency_order_by; + uint8_t log_frequency_scroll_offset; } SubGhzFrequencyAnalyzerModel; +static inline uint8_t rssi_sanitize(float rssi) { + return (rssi * -1.0f) - RSSI_OFFSET; +} + void subghz_frequency_analyzer_set_callback( SubGhzFrequencyAnalyzer* subghz_frequency_analyzer, SubGhzFrequencyAnalyzerCallback callback, @@ -39,13 +62,11 @@ void subghz_frequency_analyzer_set_callback( subghz_frequency_analyzer->context = context; } -void subghz_frequency_analyzer_draw_rssi(Canvas* canvas, float rssi) { - uint8_t x = 20; - uint8_t y = 64; +void subghz_frequency_analyzer_draw_rssi(Canvas* canvas, uint8_t rssi, uint8_t x, uint8_t y) { uint8_t column_number = 0; if(rssi) { - rssi = (rssi + 90) / 3; - for(size_t i = 1; i < (uint8_t)rssi; i++) { + rssi = rssi / 3; + for(uint8_t i = 1; i < rssi; i++) { if(i > 20) break; if(i % 4) { column_number++; @@ -55,6 +76,54 @@ void subghz_frequency_analyzer_draw_rssi(Canvas* canvas, float rssi) { } } +static void subghz_frequency_analyzer_log_frequency_draw( + Canvas* canvas, + SubGhzFrequencyAnalyzerModel* model) { + char buffer[64]; + const uint8_t offset_x = 0; + const uint8_t offset_y = 43; + canvas_set_font(canvas, FontKeyboard); + + const size_t items_count = SubGhzFrequencyAnalyzerLogItemArray_size(model->log_frequency); + if(items_count == 0) { + canvas_draw_rframe(canvas, offset_x + 27u, offset_y - 3u, 73u, 16u, 5u); + canvas_draw_str_aligned( + canvas, offset_x + 64u, offset_y + 8u, AlignCenter, AlignBottom, "No records"); + return; + } else if(items_count > 3) { + elements_scrollbar_pos( + canvas, + offset_x + 127, + offset_y - 8, + 29, + model->log_frequency_scroll_offset, + items_count - 2); + } + + SubGhzFrequencyAnalyzerLogItem_t* log_frequency_item; + for(uint8_t i = 0; i < 3; ++i) { + const uint8_t item_pos = model->log_frequency_scroll_offset + i; + if(item_pos >= items_count) { + break; + } + log_frequency_item = + SubGhzFrequencyAnalyzerLogItemArray_get(model->log_frequency, item_pos); + // Frequency + SNPRINTF_FREQUENCY(buffer, (*log_frequency_item)->frequency) + canvas_draw_str(canvas, offset_x, offset_y + i * 10, buffer); + + // Count + snprintf(buffer, sizeof(buffer), "%3d", (*log_frequency_item)->count); + canvas_draw_str(canvas, offset_x + 48, offset_y + i * 10, buffer); + + // Max RSSI + subghz_frequency_analyzer_draw_rssi( + canvas, (*log_frequency_item)->rssi_max, offset_x + 69, (offset_y + i * 10)); + } + + canvas_set_font(canvas, FontSecondary); +} + static void subghz_frequency_analyzer_history_frequency_draw( Canvas* canvas, SubGhzFrequencyAnalyzerModel* model) { @@ -65,12 +134,7 @@ static void subghz_frequency_analyzer_history_frequency_draw( canvas_set_font(canvas, FontKeyboard); for(uint8_t i = 0; i < 3; i++) { if(model->history_frequency[i]) { - snprintf( - buffer, - sizeof(buffer), - "%03ld.%03ld", - model->history_frequency[i] / 1000000 % 1000, - model->history_frequency[i] / 1000 % 1000); + SNPRINTF_FREQUENCY(buffer, model->history_frequency[i]) canvas_draw_str(canvas, x, y + i * 10, buffer); } else { canvas_draw_str(canvas, x, y + i * 10, "---.---"); @@ -81,18 +145,34 @@ static void subghz_frequency_analyzer_history_frequency_draw( } void subghz_frequency_analyzer_draw(Canvas* canvas, SubGhzFrequencyAnalyzerModel* model) { + furi_assert(canvas); + furi_assert(model); char buffer[64]; canvas_set_color(canvas, ColorBlack); canvas_set_font(canvas, FontSecondary); - canvas_draw_str(canvas, 20, 8, "Frequency Analyzer"); - canvas_draw_str(canvas, 0, 64, "RSSI"); - subghz_frequency_analyzer_draw_rssi(canvas, model->rssi); + if(model->fragment_bottom_type == SubGhzFrequencyAnalyzerFragmentBottomTypeLog) { + const size_t items_count = SubGhzFrequencyAnalyzerLogItemArray_size(model->log_frequency); + const char* log_order_by_name = + subghz_frequency_analyzer_log_get_order_name(model->log_frequency_order_by); + if(items_count < LOG_FREQUENCY_MAX_ITEMS) { + snprintf(buffer, sizeof(buffer), "Frequency Analyzer [%s]", log_order_by_name); + canvas_draw_str_aligned(canvas, 64, 8, AlignCenter, AlignBottom, buffer); + } else { + snprintf(buffer, sizeof(buffer), "The log is full! [%s]", log_order_by_name); + canvas_draw_str(canvas, 2, 8, buffer); + } + subghz_frequency_analyzer_log_frequency_draw(canvas, model); + } else { + canvas_draw_str(canvas, 20, 8, "Frequency Analyzer"); + canvas_draw_str(canvas, 0, 64, "RSSI"); + subghz_frequency_analyzer_draw_rssi(canvas, model->rssi, 20u, 64u); - subghz_frequency_analyzer_history_frequency_draw(canvas, model); + subghz_frequency_analyzer_history_frequency_draw(canvas, model); + } - //Frequency + // Frequency canvas_set_font(canvas, FontBigNumbers); snprintf( buffer, @@ -103,23 +183,151 @@ void subghz_frequency_analyzer_draw(Canvas* canvas, SubGhzFrequencyAnalyzerModel if(model->signal) { canvas_draw_box(canvas, 4, 12, 121, 22); canvas_set_color(canvas, ColorWhite); - } else { } - canvas_draw_str(canvas, 8, 30, buffer); canvas_draw_icon(canvas, 96, 19, &I_MHz_25x11); } +static void subghz_frequency_analyzer_log_frequency_sort(SubGhzFrequencyAnalyzerModel* model) { + furi_assert(model); + M_LET((cmp, model->log_frequency_order_by), SubGhzFrequencyAnalyzerLogItemArray_compare_by_t) + SubGhzFrequencyAnalyzerLogItemArray_sort_fo( + model->log_frequency, SubGhzFrequencyAnalyzerLogItemArray_compare_by_as_interface(cmp)); +} + bool subghz_frequency_analyzer_input(InputEvent* event, void* context) { furi_assert(context); + SubGhzFrequencyAnalyzer* instance = context; if(event->key == InputKeyBack) { return false; } + if((event->type == InputTypeShort) && + ((event->key == InputKeyLeft) || (event->key == InputKeyRight))) { + with_view_model( + instance->view, + SubGhzFrequencyAnalyzerModel * model, + { + if(event->key == InputKeyLeft) { + if(model->fragment_bottom_type == 0) { + model->fragment_bottom_type = SubGhzFrequencyAnalyzerFragmentBottomTypeLog; + } else { + --model->fragment_bottom_type; + } + } else if(event->key == InputKeyRight) { + if(model->fragment_bottom_type == + SubGhzFrequencyAnalyzerFragmentBottomTypeLog) { + model->fragment_bottom_type = 0; + } else { + ++model->fragment_bottom_type; + } + } + }, + true); + } else if((event->type == InputTypeShort) && (event->key == InputKeyOk)) { + with_view_model( + instance->view, + SubGhzFrequencyAnalyzerModel * model, + { + if(model->fragment_bottom_type == SubGhzFrequencyAnalyzerFragmentBottomTypeLog) { + ++model->log_frequency_order_by; + if(model->log_frequency_order_by > + SubGhzFrequencyAnalyzerLogOrderByFrequencyAsc) { + model->log_frequency_order_by = 0; + } + subghz_frequency_analyzer_log_frequency_sort(model); + } + }, + true); + } else if((event->type == InputTypeShort) || (event->type == InputTypeRepeat)) { + with_view_model( + instance->view, + SubGhzFrequencyAnalyzerModel * model, + { + if(model->fragment_bottom_type == SubGhzFrequencyAnalyzerFragmentBottomTypeLog) { + if(event->key == InputKeyUp) { + if(model->log_frequency_scroll_offset > 0) { + --model->log_frequency_scroll_offset; + } + } else if(event->key == InputKeyDown) { + const size_t items_count = + SubGhzFrequencyAnalyzerLogItemArray_size(model->log_frequency); + if((model->log_frequency_scroll_offset + 3u) < items_count) { + ++model->log_frequency_scroll_offset; + } + } + } + }, + true); + } + return true; } +static void subghz_frequency_analyzer_log_frequency_search_it( + SubGhzFrequencyAnalyzerLogItemArray_it_t* itref, + SubGhzFrequencyAnalyzerLogItemArray_t* log_frequency, + uint32_t frequency) { + furi_assert(log_frequency); + + SubGhzFrequencyAnalyzerLogItemArray_it(*itref, *log_frequency); + SubGhzFrequencyAnalyzerLogItem_t* item; + while(!SubGhzFrequencyAnalyzerLogItemArray_end_p(*itref)) { + item = SubGhzFrequencyAnalyzerLogItemArray_ref(*itref); + if((*item)->frequency == frequency) { + break; + } + SubGhzFrequencyAnalyzerLogItemArray_next(*itref); + } +} + +static bool subghz_frequency_analyzer_log_frequency_insert(SubGhzFrequencyAnalyzerModel* model) { + furi_assert(model); + const size_t items_count = SubGhzFrequencyAnalyzerLogItemArray_size(model->log_frequency); + if(items_count < LOG_FREQUENCY_MAX_ITEMS) { + SubGhzFrequencyAnalyzerLogItem_t* item = + SubGhzFrequencyAnalyzerLogItemArray_push_new(model->log_frequency); + if(item == NULL) { + return false; + } + (*item)->frequency = model->frequency; + (*item)->count = 1u; + (*item)->rssi_max = model->rssi; + (*item)->seq = items_count; + return true; + } + return false; +} + +static void subghz_frequency_analyzer_log_frequency_update( + SubGhzFrequencyAnalyzerModel* model, + bool need_insert) { + furi_assert(model); + if(!model->frequency) { + return; + } + + SubGhzFrequencyAnalyzerLogItemArray_it_t it; + subghz_frequency_analyzer_log_frequency_search_it( + &it, &model->log_frequency, model->frequency); + if(!SubGhzFrequencyAnalyzerLogItemArray_end_p(it)) { + SubGhzFrequencyAnalyzerLogItem_t* item = SubGhzFrequencyAnalyzerLogItemArray_ref(it); + if((*item)->rssi_max < model->rssi) { + (*item)->rssi_max = model->rssi; + } + + if(need_insert && (*item)->count < UINT8_MAX) { + ++(*item)->count; + subghz_frequency_analyzer_log_frequency_sort(model); + } + } else if(need_insert) { + if(subghz_frequency_analyzer_log_frequency_insert(model)) { + subghz_frequency_analyzer_log_frequency_sort(model); + } + } +} + void subghz_frequency_analyzer_pair_callback( void* context, uint32_t frequency, @@ -130,6 +338,7 @@ void subghz_frequency_analyzer_pair_callback( if(instance->callback) { instance->callback(SubGhzCustomEventSceneAnalyzerUnlock, instance->context); } + instance->last_frequency = 0; //update history with_view_model( instance->view, @@ -151,9 +360,14 @@ void subghz_frequency_analyzer_pair_callback( instance->view, SubGhzFrequencyAnalyzerModel * model, { - model->rssi = rssi; + model->rssi = rssi_sanitize(rssi); model->frequency = frequency; model->signal = signal; + if(frequency) { + subghz_frequency_analyzer_log_frequency_update( + model, frequency != instance->last_frequency); + instance->last_frequency = frequency; + } }, true); } @@ -176,11 +390,14 @@ void subghz_frequency_analyzer_enter(void* context) { instance->view, SubGhzFrequencyAnalyzerModel * model, { - model->rssi = 0; + model->rssi = 0u; model->frequency = 0; - model->history_frequency[2] = 0; - model->history_frequency[1] = 0; - model->history_frequency[0] = 0; + model->fragment_bottom_type = SubGhzFrequencyAnalyzerFragmentBottomTypeMain; + model->log_frequency_order_by = SubGhzFrequencyAnalyzerLogOrderBySeqDesc; + model->log_frequency_scroll_offset = 0u; + model->history_frequency[0] = model->history_frequency[1] = + model->history_frequency[2] = 0u; + SubGhzFrequencyAnalyzerLogItemArray_init(model->log_frequency); }, true); } @@ -196,13 +413,26 @@ void subghz_frequency_analyzer_exit(void* context) { subghz_frequency_analyzer_worker_free(instance->worker); with_view_model( - instance->view, SubGhzFrequencyAnalyzerModel * model, { model->rssi = 0; }, true); + instance->view, + SubGhzFrequencyAnalyzerModel * model, + { + model->rssi = 0u; + model->frequency = 0; + model->fragment_bottom_type = SubGhzFrequencyAnalyzerFragmentBottomTypeMain; + model->log_frequency_order_by = SubGhzFrequencyAnalyzerLogOrderBySeqDesc; + model->log_frequency_scroll_offset = 0u; + model->history_frequency[0] = model->history_frequency[1] = + model->history_frequency[2] = 0u; + SubGhzFrequencyAnalyzerLogItemArray_clear(model->log_frequency); + }, + true); } SubGhzFrequencyAnalyzer* subghz_frequency_analyzer_alloc() { SubGhzFrequencyAnalyzer* instance = malloc(sizeof(SubGhzFrequencyAnalyzer)); // View allocation and configuration + instance->last_frequency = 0; instance->view = view_alloc(); view_allocate_model( instance->view, ViewModelTypeLocking, sizeof(SubGhzFrequencyAnalyzerModel));